Confundido por git diff

Quiero generar un file de parche de las diferencias entre mi twig y el maestro. Pero la twig es bastante longeva, así que simplemente hice una fusión de master para actualizarla. Puedo ver las diferencias bien si comienzo a crear una request de extracción en Bitbucket. Pero cuando hago git diff master.. en mi twig veo diferencias que no están ahí. ¿Son resultado de la fusión? ¿Cómo puedo get una list de diferencias igual que Bitbucket, solo las diferencias entre mi twig y mi maestro en este momento?

TL; DR

No está claro para mí dónde comienza la confusión, pero vale la pena señalar que usar git diff es bastante diferente de generar una request de extracción. Eventualmente, se networkingucirá a ejecutar git diff en las confirmaciones específicas correctas. El truco está en encontrar los compromisos correctos.

Largo

Qué hay en un repository

Primero, restring qué es lo que guarda Git. En una especie de nivel fundamental, lo que le importa a Git son las instantáneas de origen , guardadas en forma de commits . Una confirmación contiene una instantánea completa de algún tree fuente. Una confirmación también contiene algunos metadatos: nombre y dirección de correo electrónico de la persona, o a veces dos personas, que hicieron la confirmación ( autor y autor : pueden ser iguales o separados) y las marcas de time para cuando lo hicieron; un padre debe confirmar la identificación, de modo que Git pueda presentar la serie de confirmaciones como un historial de quién (autor) hizo qué (ver a continuación) y cuándo (timestamp); y un post de logging , para proporcionar la descripción del autor de por qué hicieron lo que hicieron.

Como cada confirmación es una instantánea completa, para ver quién hizo qué, debemos usar un command como git diff . Supongamos que tenemos dos commits hechos en sucesión, en branch master , así:

 (parent) (child) df731ac <- 049a12b <-- master 

Un nombre de twig como master nos permite encontrar la confirmación más reciente 049a12b . Usamos el padre padre ID almacenado del niño df731ac para encontrar el padre, y luego podemos ejecutar git diff df731ac 049a12b -o mucho más simplemente, git show master -para comparar df731ac a 049a12b .

Lo que sea que aparezca diferente aquí, el autor de 049a12b debe haberlo cambiado. Pero df731ac (el pnetworkingecesor o la confirmación primaria) es una instantánea completa, y 049a12b (la 049a12b del sucesor o hijo que es la punta del master de la sucursal) también es una instantánea completa. Saber esto es útil para entender la siguiente parte.

Tenga en count que, como en el dibujo anterior, un nombre de twig como master o develop o feature/tall simplemente contiene el ID de una confirmación específica. Llamamos a esto cometer el compromiso de sugerencia de la sucursal. Cuando agrega nuevas confirmaciones a una sucursal, lo que hace Git es crear la nueva confirmación, que le da una ID, y luego escribir la nueva ID de confirmación de la propina en el nombre de la sucursal . Por lo tanto, los nombres de las twigs se "mueven" a lo largo del time: siempre apuntan a la última confirmación (la más infantil). Cada nuevo commit tiene, como su padre, el ID que era la punta de la twig anterior, lo que permite a Git seguir estos pointers hacia atrás a través del repository.

Si las ID de hash de Git commit fueran solo una letra, podríamos dibujar un repository simple de tres confirmaciones como:

 A <-B <-C <-- master 

y agregar un nuevo compromiso consistiría simplemente en escribir el compromiso D con C como su padre, y hacer que el master apunte a D :

 A--B--C--D <-- master 

El nombre especial HEAD normalmente contiene el nombre de una twig . Entonces, si HEAD contiene master , Git puede usar HEAD para seleccionar branch master , y master para encontrar D En otras palabras, Git generalmente comienza usando un nombre de twig para get una ID de confirmación de sugerencia. Luego observa esa confirmación para get su ID padre, luego mira la confirmación principal para otro padre, y así sucesivamente. Para esto sirven los nombres de las sucursales y lo que hacen: encuentran sugerencias de sugerencias .

Usando git diff

Todos los git diff does (la mayoría de las veces 1 ) es tomar dos confirmaciones individuales como esta y compararlas. Para hacer esto, necesita resolver sus dos inputs a las ID de hash. Esas ID de hash son las dos confirmaciones; luego compara las dos instantáneas.

Cuando ejecuta git diff master.. , el diff de Git traduce master.. en master y HEAD (el pnetworkingeterminado para completar una position vacía alnetworkingedor de .. es HEAD ), y luego traduce master en un ID de tip de branch. Si la confirmación de punta de 049a12b master es 049a12b como en el dibujo anterior, la identificación de almohadilla para la mitad izquierda de la comparación será 049a12b . Para la mitad derecha, git diff debe leer HEAD para get su nombre de twig, como develop o feature/tall o lo que sea. El nombre de la sucursal se correlaciona con su propio compromiso de propina. Digamos que su ID abreviada es 6bc9702 . Luego, este command git diff finalmente le dice a Git que extraiga la instantánea de origen para 049a12b , la de 6bc9702 , y compare esas dos.

Sin embargo, puede proporcionar dos hash para cada dos confirmaciones que tenga:

 git diff 0123456 fedcba9 

por ejemplo. Pero tienes que encontrar esos commits, o algún nombre que Git se convierta en esos commits.

(No importa si dices git diff AB o git diff A..B ; estos significan exactamente lo mismo . Esto es diferente de git log y la mayoría de los otros commands de Git: solo git diff tiene este manejo especial para los dos: sin embargo, la regla que completa HEAD si falta uno de los nombres, es común a git diff y otros commands de Git).


1 El git diff Git puede producir algo llamado diff combinado, pero estos son bastante complicados y no son relevantes aquí.


Breve aparte: git show y git log -p

Mencioné el git show arriba. Lo git show hace el git show es encontrar el documento padre automáticamente para usted y luego mostrarle primero los metadatos: el autor (nombre, correo electrónico, timestamp) y el post de logging, y luego un post de padre a hijo.

Cuando ejecutas git log -p , esto es similar a ejecutar git show en cada commit, comenzando desde child-most y trabajando hacia atrás (ten en count que git log defecto en HEAD ). Es decir, el primer git log muestra la confirmación de sugerencia de la twig actual como si git show HEAD , luego muestra el padre de commit como si fuera un git show , luego muestra el padre de parent como si fuera un git show , y así sucesivamente.

Hay una diferencia bastante grande: el git show invocará la maquinaria especial de diferencias combinadas en cualquier commit de fusión , mientras que git log solo mostrará el post de logging por defecto, omitiendo cualquier bash de diferir la fusión. (Hay indicadores que puede usar para cambiar este comportamiento).

Solicitudes de extracción

Las requestes de extracción son más complicadas, porque para realizar una request de extracción, debes abrir tu repository a otra persona que pueda ejecutar git pull 2, de ahí viene el término, y es el significado original de la request de extracción, o bien encuentre o cree un repository compartido, envíe algunas de sus confirmaciones a esta location compartida y luego solicite a la otra persona que obtenga sus confirmaciones de la location compartida. Ignoraré el significado original de "request de extracción" -principalmente solo un post de correo electrónico pidiéndole a otra persona que ejecute git fetch y saltaré a la forma en que estos sitios lo manejan.

Con services como GitHub y Bitbucket, ahora hay al less otros dos repositorys involucrados. Incluso ejecutan una fusión de testing (aunque esto no es tan importante, salvo para verificar que la request de extracción tenga sentido). Estoy más familiarizado con GitHub que con Bitbucket (yo uso GitHub), pero ambos funcionan de la misma manera aquí, al less desde una vista de nivel suficientemente alto.

Antes de que pueda siquiera pensar en requestes de extracción, debe "bifurcar" un repository. Un tenedor es un clon, pero con algo de memory extra sobre el repository desde el que se clonó . 3 Detrás de escena, de una manera que normalmente no tiene por qué preocuparse, 4 el proveedor comparte mucho el almacenamiento, de modo que cada horquilla ocupa muy poco espacio en los serveres del proveedor.

Sin embargo, esta bifurcación es la razón por la cual hay dos repositorys adicionales involucrados. Esto nos da tres repositorys de los que debemos hacer un seguimiento:

  • Tu propio repository de Git, en tu máquina. Esto es tuyo , para hacer con lo que quieras. Aquí es donde ejecutas git diff también.
  • Tu tenedor del repository original. Su máquina y su Git se referirán a esto como el control remoto de origin .
  • El repository original. Esto no tiene necesariamente ningún nombre en su repository. Puede, y tal vez debería, agregar otro control remoto , que en otros ejemplos se denomina en upstream . No siempre se requiere que agregue esto, pero supongamos que lo hizo. Si no lo has hecho, ejecuta:

     git remote add upstream <url> 

    dónde está la URL del repository del que ha bifurcado su depósito de origin .

Nos referiremos, a continuación, a su repository, su origin y su upstream . Recuerde que estos nombres remotos son solo nombres cortos en su propio repository que hacen reference a otro Git en alguna URL. Eso es lo que es un control remoto : un nombre corto para una URL donde hay un repository de Git en esa URL. Usaremos el término proveedor para referirnos a GitHub o Bitbucket.


2 El command git pull es un atajo para hacer git fetch seguido de un segundo command de Git, todo con un command. Resulta que, a menudo, es importante usar los dos commands por separado, no siempre, pero con la suficiente frecuencia como para combinarlos así probablemente sea un error. Probablemente, el command ahora llamado git fetch debería haber sido llamado git pull , y el ahora llamado git pull podría ser opciones que pasas a git fetch , o un par de commands de acceso directo: git fm para fetch-and-merge, y git fr para fetch-and-rebase. Recomiendo que los nuevos usuarios de Git eviten el git pull en favor de los commands por separado, al less hasta que estén bastante familiarizados con los commands por separado. No obstante, este pequeño error histórico está totalmente integrado en Git hoy, no solo en términos de git pull siendo el opuesto (pero incorrecto) opuesto a git push , sino también en el mismo nombre "request de extracción".

3 Esto está por encima, o quizás "al lado" es una mejor descripción: la forma en que los clones restringn su origen a través del nombre remoto del origin . En cualquier caso, las bifurcaciones se parecen más a los clones de duplicación inicialmente, pero no están vinculadas al depósito desde el que están bifurcadas, como lo serían los clones de duplicación. Por lo tanto, son una especie de híbrido, con características adicionales, que incluyen, específicamente, que puede hacer la versión del service de una request de extracción.

4 GitHub ocasionalmente lo menciona si y cuando elimina las bifurcaciones y elimina los repositorys no editados, ya que (a) tienen que deshacer el uso compartido especial de la bifurcación, y (b) eliminar las bifurcaciones es más seguro porque el repository original (del cual bifurcó) todavía cerca. Me imagino que Bitbucket es similar.


Una request de extracción de proveedor comienza con git push

Lo principal que git push saber sobre git push es que empuja los commits , no los files. Lo hace llamando a algún otro repository de Git. Luego descubre qué commits tienes que no hacen, les da sus commits, y les pide que establezcan algún nombre (s), generalmente nombres de twig, para recordar commits específicos.

Ahora, su tenedor en el origin pertenece a usted , por lo que puede git push hacia él sin embargo y siempre que lo desee. Es un repository de Git real (o algo así como uno), almacenado en las máquinas del proveedor en lugar del suyo, pero es como su propio repository Git en el sentido de que tiene confirmaciones y nombres de twigs, y esos nombres de twigs apuntan volcar devuelve ese punto a las confirmaciones anteriores.

Cuando ejecuta git push , su request para establecer un nombre de twig, como master o develop o feature/tall , viene con una identificación hash de confirmación. Si su Git no tiene ese compromiso, tu Git le da a su Git que se comprometa. Si su Git no tiene el padre de ese compromiso, tu Git le da a su Git el padre también. Esto continúa hasta que llegas a algún compromiso que su Git tiene. Esos son lo que ambos compartieron antes de comenzar el git push .

La ID del hash de confirmación que les das es normalmente la que está en la punta de tu sucursal. Entonces si tienes:

  ...--H--I--J <-- master 

y usted git push origin master , está haciendo que su Git llame a su Git y diga "Me gustaría que configure su master para que emita J ". Si su Git tiene su master apuntando a cometer H , y le falta I y J , su Git les da I y J también.

Es posible que su Git tenga su nombre de sucursal apuntando a algún compromiso que no tiene, o que no está en la cadena formada al comenzar desde su sucursal. Tal vez su Git tiene:

 ...--H--K <-- master 

Si es así, su request, que agreguen I y J y hagan que su maestro recuerde J , será denegada por defecto, porque esto daría como resultado:

  K [abandoned] / ...--H--I--J <-- master 

después de lo cual "perderán" el compromiso K , posiblemente real y para siempre. Sin embargo, dado que el origin Git te pertenece, normalmente puedes usar un empuje de fuerza ( git push --force ) para convertir tu request educada en un command: sí, establece tu master en J , ¡aunque pierda K ! (Por lo general, esta es una mala idea y no deberías hacerlo. En su lugar, deberías git fetch origin para traer K a tu propio repository, y luego fusionar o rebase para incorporar K junto con tu propia I--J Esto da usted tiene un compromiso nuevo y diferente, o un set de compromisos, que puede presionar cortésmente, que no perderá K En cambio, serán adiciones puras de nuevos compromisos.)

Tenga en count que estos cambios (generalmente adiciones puras de nuevos commits seguidos de mover un nombre de twig "forward") entran en su fork. Afectan su origin , pero no afectan su upstream . ¡Ese no es tu repository después de todo! No puedes presionar directamente hacia arriba.

Finalmente, la request de extracción

En cambio, lo que puede hacer, ahora que sus nuevos commits están en su origin que es una bifurcación de su upstream , es hacer una request de extracción, generalmente usando algún button clicky de la interfaz web. El server del proveedor sabrá, usted le dirá, si y según sea necesario, qué nombre de sucursal desea usar en su origin y qué nombre de sucursal desea usar en su upstream .

El proveedor luego notificará a quien sea que realmente controle el flujo ascendente que ha realizado esta request de extracción. Dado que el proveedor tiene su fork, su origin especialmente compartido con su repository que es su upstream , tendrá acceso directo a las confirmaciones que envió a su sucursal, que ahora están en la punta de sucursal de su origin .

Al ver la diferencia

Ahora tenemos todas las herramientas que necesitamos para encontrar la diferencia correcta. Queremos comparar su confirmación de sugerencia de bifurcación, desde el nombre de la sucursal que seleccionó cuando realizó la request de extracción, hasta la confirmación de sugerencia en la sucursal upstream que estableció cuando ejecutó git push . Si tiene esas dos ID de hash delante de usted, puede ejecutar git diff <their-upstream-tip-hash> <your-origin-tip-hash> .

Pero las ID de hash son terriblemente feas. Sería bueno si conseguimos que Git traduzca para nosotros, y nosotros podemos. Pasé por alto cómo funciona el git fetch arriba, pero vamos a profundizar un momento.

Usando git fetch

Si ejecutas git fetch upstream , eso le dice a tu Git que invoque el Git que responde en la URL que almacenaste en la upstream . Ese es el Git para el repository en sentido ascendente de su proveedor, el que ha bifurcado. Su Git llamará a ese Git, obtendrá los nuevos commit que tengan y no los tendrá en su repository. Entonces, aquí está el truco key: su Git configurará los nombres de las sucursal de seguimiento remoto para registrar las ID de hash para cada una de las sugerencias de sucursal, por lo que tengan en este momento.

Su master convierte en tu upstream/master . Su feature/tall convierte en su upstream/feature/tall . Tu Git lo restring por ti, junto con los nuevos commits que tienen.

Lo mismo ocurre cuando ejecutas git fetch origin : tu Git llama al otro Git en el origin -este es tu tenedor con el proveedor- y carga cualquier origin commit que no tengas. Luego, su Git establece su origin/master para recordar el master en su origin , y así sucesivamente. Tenga en count que cuando git push al origin y les da actualizaciones, su Git sabe si toman las actualizaciones. Si aceptan sus actualizaciones, su Git registra las nuevas ID de hash en origin/master , origin/develop , y así sucesivamente.

Por lo tanto, siempre y cuando su Git esté sincronizado con los dos Gits en upstream y origin y si no lo es, puede ejecutar git fetch upstream y hasta su origin para actualizarlo, ahora tiene en su propio repository los commits correctos , nombrado a través de upstream/theirbranch y origin/yourbranch . Entonces, en lugar de git diff <magic hash 1> <magic hash 2> , si has enviado una request de extracción para que tu upstream incorpore tu feature/tall en su develop , puedes hacer git diff upstream/develop origin/feature/tall .

Resumen

Los dos commits que necesita para diferenciar son los de otros dos repositorys. Si esos dos repositorys están configurados como controles remotos en upstream y origin en su propio repository, y su repository está actualizado con respecto a esos dos repositorys, puede hacer que git log o git show los commits en cuestión, y usar sus nombres de rastreo remoto en upstream/* y origin/* para localizar sugerencias de bifurcación específicas.

Puede tener confirmaciones que no se encuentran en ninguno de estos repositorys, y puede ver qué sucedería si inserta estos nuevos compromisos en su propio origin . Esto le permite ver qué pasaría si los empujó y luego realizó una request de extracción: simplemente compare su punta de nombre de rastreo remoto upstream/* las confirmaciones de punta de sucursal.