Compare files en git remoto vs local

Soy nuevo en git y he intentado jugar con algunas características.

Que hace

 git diff HEAD...origin master 

vs

 git diff origin master 

Parecen darme resultados completamente diferentes.

Quizás es bueno tener en count que tengo un origin/master que es diferente del origin master .

¿No debería significar todo lo mismo?

Estás mezclando varios conceptos diferentes de Git. Es cierto que estos conceptos de Git tienen nombres muy similares: mandos a distancia, twigs y twigs de seguimiento remoto. (La nomenclatura de Git empeora aún más, ya que existe el concepto de seguimiento , que no es lo mismo que una twig de seguimiento remoto , y cuando una twig rastrea a otra, la twig rastreada se denomina ascendente , que no es lo mismo que usar un control remoto como un flujo ascendente, o usando un control remoto denominado upstream . Si no está confundido, lo está haciendo mucho mejor que yo, o la mayoría de la gente. :-))

Entonces, retrocedamos un poco y definamos todo esto.

Definición: twig

Primero, tenemos twigs . La palabra "twig" en Git es en realidad ambigua: puede referirse a un nombre de twig, como master , o puede referirse a una serie de confirmaciones , comenzando por la confirmación de punta en una twig y retrocediendo en el time. En otras palabras, si dices casualmente "blah mumble branch master yada yada", no está claro de inmediato si te refieres al nombre master twig, o la serie de confirmaciones formadas comenzando con el commit que los nombres master y trabajando hacia atrás en la historia.

Sin embargo, generalmente está claro desde el context, y si no, puede usar "nombre de sucursal" y "estructura de sucursal" para distinguirlos. Un nombre de twig es solo una palabra como master , excepto que para ser un nombre de twig actual y válido, tiene que ser un nombre que conozca Git, que Git mostrará si ejecuta git branch . Ver también ¿Qué queremos decir con "twig"?

Tenga en count que un nombre de sucursal puede traducirse directamente en la confirmación más importante de la estructura de sucursal. El command git diff usa esto bastante, como veremos en un momento. Para ver cómo un nombre de sucursal se convierte en un ID de confirmación, use git rev-parse . Este command simplemente mira las cosas, por lo que es seguro usarlo en cualquier momento. Pruébalo ahora:

 $ git rev-parse master 

y:

 $ git rev-parse HEAD 

y si tiene otras twigs, intente pasar sus nombres a git rev-parse . (Luego testing git branch -vv and compare the abbreviated commit IDs you see in its output, to what you got from git rev-parse`.)

Definición: remoto

Un control remoto es solo un nombre, como el origin . En este sentido, se parece mucho a un nombre de sucursal. La diferencia es que un nombre remoto se almacena en un lugar diferente de los nombres de las sucursales, y Git le mostrará sus nombres remotos si ejecuta git remote . Además de esto, un nombre remoto te da dos cosas: la capacidad de ejecutar git fetch y git push sin escribir una gran URL larga. Git mantiene la gran URL larga bajo el nombre del control remoto y la posibilidad de tener sucursales de seguimiento remoto. .

Definición: twig de seguimiento remoto

Una twig de seguimiento remoto es (¡una vez más!) Solo un nombre, pero comienza con el nombre de un control remoto, como origin , luego tiene una barra inclinada, y luego tiene el nombre de una twig "como se ve en la TV el control remoto" . 1 Por lo tanto, verá nombres como origin/master , que son nombres típicos de las sucursales de rastreo remoto.

Hay una diferencia key entre sus nombres de sucursales (regulares, locales) y las sucursales de seguimiento remoto: Su Git actualiza sus sucursales a medida que trabaja con ellos: los revisa, usa git commit para agregarles commits, usa git merge para agregar merge se compromete con ellos, y así sucesivamente. Puedes git checkout una twig y luego el git status te dirá que estás "conectado" a la twig, por ejemplo, on branch master .

Su Git no actualiza sus twigs de seguimiento remoto de esta manera. De hecho, no se puede "encender" en absoluto. En cambio, cuando ejecutas el git fetch origin aquí es donde utilizas el nombre remoto, origin , tu Git busca la URL del control remoto, llama a otro Git que usa esa URL y mantiene una pequeña conversación con él. Tu Git obtiene, de su Git, una list de todas las twigs, nombres de twigs, debo decir. Entonces tu Git obtiene de su Git cualquiera de las confirmaciones que tienen, que tú no haces: la estructura de su twig.

Una vez que su Git tiene la estructura de sucursal, configura su twig de seguimiento remoto (un nombre) para que apunte a la confirmación de la punta, la misma que hace su nombre de sucursal en su Git. Tu Git hace esto para cada una de sus twigs. De esta forma, después git fetch origin , las sucursales de seguimiento remoto ahora siguen la pista de dónde estuvieron sus twigs la última vez que su Git alcanzó a su Git.

Su Git construye los nombres de las sucursales de seguimiento remoto colocando su nombre remoto ( origin ) delante de sus nombres de sucursal ( master ). Es por eso que su twig de seguimiento remoto es origin/master : su nombre de sucursal es master .

Definición: HEAD

El nombre HEAD , en Git, es bastante especial. (De hecho, es tan especial que si logras eliminar el file .git/HEAD alguna manera, ¡Git dejará de creer que tu repository de Git es un repository de Git!) Sin embargo, normalmente HEAD realmente solo contiene el nombre de una twig . Por ejemplo, si está en Branch master , el file HEAD especial solo contiene la cadena: ref: refs/heads/master . (El string refs/heads/master es de hecho el nombre completo del nombre de la twig master , aunque normalmente no necesitas preocuparte por esto: Git oculta el prefijo refs/heads/ , al igual que oculta los refs/remotes/ prefijo cuando está utilizando el origin/master twig de rastreo remoto).

Cuando HEAD contiene un nombre de sucursal, que, como acabamos de decir, es el caso habitual, el nombre HEAD es principalmente una abreviatura para escribir el nombre de la sucursal actual. Entonces, si eres master , HEAD es solo otra forma de decir master . No es realmente tan corto, pero tiene la ventaja de que funciona incluso si estás en la sucursal llanfairpwllgwyngyll . Más importante aún, significa que los progtwigs como git log no necesitan saber en qué twig estás, o de forma equivalente, progtwigs como el git status pueden averiguar en qué twig estás. De hecho, así es como se entera el git status .

Revisión rápida

  • git branch enumera los nombres de sus twigs, como el master .
  • git remote list sus nombres remotos, como el origin .
  • git branch -r enumera las twigs de seguimiento remoto, como origin/master .

¿Qué significa todo esto para los diversos commands de git diff

El command git diff es en sí mismo bastante inusual. La mayoría de los commands de Git tratan el nombre de la sucursal y los arguments de la list de revisión de la forma descrita en la documentation de gitrevisions . En git diff , sin embargo, tanto las notaciones de dos puntos como de tres puntos de branch1..branch2 y branch1...branch2 tienen significados nuevos y diferentes.

(Además de esto, git diff tiene un montón de submodos, que puedes invocar con git diff-index , git diff-files y git diff-tree . Pero no nos preocupemos por eso aquí).

Tu corres:

git diff HEAD...origin master

Aquí hay dos dificultades adicionales, e ignoraré por completo a una de ellas por un time. El otro problema es que esto usa la notación de tres puntos, con la interpretación especial de git diff , que requiere la comprensión del command git merge-base .

Simplifiquemos este segundo problema por un momento fingiendo que, en cambio, usted escribió:

 git diff HEAD..origin master 

La interpretación especial de git diff de la syntax de dos puntos es mucho más simple: git diff pretende que no usaste los dos puntos en absoluto, y en su lugar simplemente escribió los dos nombres como dos arguments separados. Entonces, esta forma particular significa exactamente lo mismo que:

 git diff HEAD origin master 

Aquí hay un pequeño problema, porque acabamos de nombrar tres cosas: el nombre HEAD especial, un nombre que parece (y de hecho es) una twig remota, en lugar de una twig o de seguimiento remoto, y finalmente una twig nombre. El command git diff quiere dos cosas aquí: quiere dos nombres de twig, o al less, dos arguments que puede resolver a commits específicos. 2

Por supuesto, HEAD funciona de maravilla: nombra la twig actual, que nombra la confirmación de punta en la twig. Si la twig actual es la master y la resolución master para confirmar 24377c8... , HEAD también resuelve a 24377c8... y Git usará 24377c8... como la primera confirmación en la diferencia.

Pero, ¿y el origin ? Aquí es donde gitrevisions documentation de gitrevisions . Es difícil de ver al principio, pero de hecho, lo que sucede es que el origin se trata como si dijera origin/HEAD , y origin/HEAD generalmente 3 maps para origin/master , por lo que usualmente este significa "lo que sea que haya cometido el git rev-parse origin/master ". Definitivamente siempre significa "lo que se le git rev-parse origin creación del git rev-parse origin ".

Solo por concreción, digamos que su HEAD es su master que es cometer 24377c8... , y ese origin es su master que es cometer b240a77... Entonces podrías haber escrito esto:

 git diff 24377c8 b240a77 master 

Es decir, los dos commits que comparará git diff son estos dos hashes, estamos usando estos acortados aquí porque los 40 caracteres completos son demasiado, pero ¿qué hay de ese extra master ?

Esto nos lleva a la otra dificultad adicional que mencioné anteriormente: git diff puede tomar más de dos commits, y si recibe tres o más commits, a menudo 4 producirá un "diff combinado". Si la palabra master no fuera un nombre de twig, por lo que git rev-parse quejó de ello, git diff lo habría tratado como un nombre de ruta , que restringiría la salida de diff a routes particulares. Pero, por supuesto, el master es un nombre de sucursal válido, por lo que puede analizarse como una revisión y puede llevar a un comportamiento difícil de describir. (En la versión 2.8.1 de Git, donde lo probé, actúa particularmente raro).

En pocas palabras: no hagas eso

Si desea utilizar la forma de tres puntos, quédese con un solo argumento de tres puntos con dos nombres de twig. En este caso, Git usará git merge-base para encontrar la base de combinación de las dos revisiones. (Para más detalles, ver la respuesta mucho más corta de Drew Beres a esta pregunta . 5 )

En ausencia de forms particularmente complicadas, simplemente puede ejecutar git rev-parse en los nombres que va a enviar a git diff para ver qué commits usará:

 $ git rev-parse HEAD origin 

Esto le mostrará dos identificaciones de compromiso, y esas son las dos confirmaciones que git diff HEAD origin git diff HEAD..origin git diff HEAD origin o git diff HEAD..origin . Al usar la syntax de tres puntos, puede ejecutar git merge-base --all para ver qué compromiso elegirá Git para compararlo con el lado derecho de la versión de tres puntos. Si eso imprime solo una revisión, esa es la revisión del git diff se comparará con el lado derecho.

(Y recuerde que todo este comportamiento es específico de git diff : otros commands como git log tratan las syntax de dos puntos y tres puntos de forma diferente).


1 Puede hacer nombres de twigs de rastreo remoto que no comiencen con nombres remotos. También puede hacer nombres de sucursales locales que comiencen con nombres remotos. Hacer cualquiera de estos es una mala idea ya que confundirá a los humanos. Git los mantendrá en línea recta, Git usa los refs/heads y refs/remotes/ prefixes para saber que son sucursales locales y de seguimiento remoto, pero es imposible trabajar con ellos; no lo hagas

2 Más precisamente, git diff quiere resolver los dos arguments a dos treees . Sin embargo, una ID de confirmación siempre funciona, y los nombres de las sucursales resuelven confirmar las ID, por lo que probablemente tenga más sentido, al less inicialmente, preocuparse por encontrar las confirmaciones.

3 Cuando se git clone una URL por primera vez, Git configura el origin remoto para contener la URL, y también averigua, si puede, qué nombres de HEAD twig en el otro repository de Git. A continuación, configura el origin/HEAD de su nombre de seguimiento remoto para asignarlo a su nombre de seguimiento remoto para esa twig. Como esa twig, en ese otro repository de Git, suele ser su master , tu origin/HEAD suele ser una reference simbólica a tu origin/master .

Sin embargo, si su repository de Git tiene una ramificación diferente, su origin/HEAD apuntará a algún otro origin/ whatever nombre. (Y, en lo que probablemente sea un error en Git, el git fetch origin nunca actualiza su origin/HEAD , aunque probablemente debería actualizarlo si cambian su twig actual).

4 Se pone muy mal desde aquí debido a la forma en que git diff maneja la notación de tres puntos. Sin embargo, esto depende de su versión específica de Git: las versiones anteriores de Git detectaron esto con testings de cadena literales en los arguments que pasó, y los más nuevos lo detectan al observar las banderas dejadas atrás por el código de revisión y análisis. Sin build versiones anteriores de Git, todo lo que puedo decir es que estoy bastante seguro de que se comportan de manera diferente a la versión que probé.

5 Comencé esta respuesta hace horas, fui interrumpido varias veces, y no pude explicar por qué algunos commands de git diff con tres o más nombres de twig y syntax de dos y tres puntos producían diffs combinados y algunos producían diffs ordinarios, así que tenía que ir a search en la fuente builtin/diff.c Básicamente es algo problemático. Después de hurgar en él durante unas horas, creo que tengo una solución, aunque dado que los mantenedores de Git han ignorado por completo mi corrección de git stash , no estoy esperando mucho aquí.

Según la documentation de git-diff:

Comparando twigs

 $ git diff topic master (1) $ git diff topic..master (2) $ git diff topic...master (3) 
  1. Cambios entre las sugerencias del tema y las twigs principales.

  2. Lo mismo que arriba.

  3. Cambios que ocurrieron en la twig maestra desde que se inició la twig de tema.

En el período triple de <commit>...<commit> git-diff forma específicamente:

Esta forma es para ver los cambios en la twig que contiene y hasta la segunda, comenzando en un ancestro común de ambos. "git diff A … B" es equivalente a "git diff $ (git-merge-base AB) B". Puede omitir cualquiera de, que tiene el mismo efecto que usar HEAD en su lugar.