¿Por qué git le da prioridad a un compromiso de selección de cereza sobre el compromiso de revertir?

Supongamos que tengo un siguiente tree:

$ git log --graph --oneline --decorate --all

 * 7b261e3 (HEAD, master) Revert "A" * 32f08ae A | * f0b008f (b) A |/ * 83c0052 init 

$ git log -p --graph --decorate --all

 * commit 7b261e3534e12446b75d286ef94556d077a9ee87 (HEAD, master) | Author: Shin Kojima <shin@kojima.org> | Date: Tue Jun 10 03:13:29 2014 +0900 | | Revert "A" | | This reverts commit 32f08ae6e6a8036a7ed0a72568ac41b3e0fe806a. | | diff --git a/test b/test | index f16344d..26604dc 100644 | --- a/test | +++ b/test | @@ -1,3 +1,3 @@ | foo | -hoge | +bar | buz | * commit 32f08ae6e6a8036a7ed0a72568ac41b3e0fe806a | Author: Shin Kojima <shin@kojima.org> | Date: Tue Jun 10 03:12:18 2014 +0900 | | A | | diff --git a/test b/test | index 26604dc..f16344d 100644 | --- a/test | +++ b/test | @@ -1,3 +1,3 @@ | foo | -bar | +hoge | buz | | * commit f0b008f3da2426611b40560ce4b64be6e32707e5 (b) |/ Author: Shin Kojima <shin@kojima.org> | Date: Tue Jun 10 03:12:18 2014 +0900 | | A | | diff --git a/test b/test | index 26604dc..f16344d 100644 | --- a/test | +++ b/test | @@ -1,3 +1,3 @@ | foo | -bar | +hoge | buz | * commit 83c00525a1d8168ae251cf33c00178d398ef4b54 Author: Shin Kojima <shin@kojima.org> Date: Tue Jun 10 03:11:38 2014 +0900 init diff --git a/test b/test new file mode 100644 index 0000000..26604dc --- /dev/null +++ b/test @@ -0,0 +1,3 @@ +foo +bar +buz 

7b261e3 es un compromiso de revertir de 32f08ae y f0b008f es una selección de cereza de 32f08ae .

Cuando fusiono la twig b en el master , descubrí que git ignora la confirmación de revertir ( 7b261e3 ) en silencio y el resultado es un resultado diferente del método de rebase. Parece que todavía tengo que evaluar las degradaciones y ver por mí mismo en este caso.

 Merge made by the 'recursive' strategy. test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 

$ cat test

 foo hoge buz 

¿Hay alguna forma de detectar conflictos? ¿Qué he hecho mal?

git versión 2.0.0

Esto es una consecuencia de cómo git define la acción de fusión. Déjame elaborar:

Estás en el master twigs (es decir, HEAD es ref: refs/heads/master ), con el master haciendo reference a commit 7b261e3 b haciendo reference a commit f0b008f . El gráfico de confirmación se ve así:

 I - A - R <-- HEAD=master \ A' <-- b 

donde I es la confirmación inicial ( 83c0052 ), A es su confirmación con el post "A", R es la confirmación de reversión de A , y A' ( f0b008f ) es la copy de A que seleccionó para la twig b .

Ahora corres:

 $ git merge b 

Git encuentra la "base de fusión" de HEAD b , que es commit I

Git ahora tiene, en efecto, dos diferencias:

 git diff 83c0052 HEAD # or: git diff 83c0052 7b261e3 git diff 83c0052 b # or: git diff 83c0052 f0b008f 

(Pruébelo usted mismo como commands reales, usando los SHA-1 sin procesar si no ha deshecho la fusión).

Los cambios de I a R son, bueno, ninguno: hiciste el cambio en A , luego hiciste un cambio de marcha atrás para volver al estado que tenías en I Entonces la primera diferencia está vacía.

Los cambios de I a A' son los cambios que hizo en A

Le dijiste a git que combinara esos dos (no tres o cuatro, solo dos) cambios. La combinación de "no cambiar" y "cambiar de bar a hoge " es cambiar de bar a hoge , así que eso es lo que obtienes:

 I - A - R - M <-- HEAD=master \ ____/ A' <-- b 

donde commit M 's tree mantiene los cambios desde A' . Por lo tanto:

¿Hay alguna forma de detectar conflictos? ¿Qué he hecho mal?

no hay conflicto; lo único que podrías decir que hiciste mal es esperar más de Git. 🙂

En serio, este tipo de cosas es una de las razones, solo una de muchas, por la que siempre debes inspeccionar el resultado de una combinación de alguna manera, ya sea mirando los loggings de los elementos que se fusionan o ejecutando testings automatizadas, o simplemente observando el resultados.

… un resultado diferente del método de rebase.

La operación "rebase" es fundamentalmente diferente de fusionar. Merge toma dos (o más) historias de desarrollo y las combina, comparando la base de fusión con cada punta de historial y combinando todos los cambios. Rebase toma un set de confirmaciones (por lo general, un único historial de desarrollo) e intenta "reproducir" cada una (comenzando por un punto nuevo). Si todo tiene éxito, la label se moverá a la punta de la serie de compromisos resultante.

En este caso, sin embargo, si tuviera que reproducir "twig b " en "twig master ":

 I - A - R <-- master \ A' <-- b 

eso le diría a git que eligiera a A' encima de R , dando:

 I - A - R <-- master \ ` A'' [proposed new b] A' <-- b 

que da como resultado el mismo tree que fusiona b en el master . (Y luego, una vez que la selección de cereza funcionó, git movería la twig b al nuevo lugar propuesto).

Por otro lado, la reproducción de master encima de b (al retirar b y hacer una serie de selects de cereza) es diferente: esto significa seleccionar (copyr) A , luego R Como A ya está copydo, el bash de traer A de nuevo se omite como un duplicado:

 I - A - R <-- master \ A' - A''? [proposed, but we discover A'' is duplicate, discard] I - A - R <-- old master \ A' - R' <-- new master, after proposed cherry pick sequence accepted 

Es decir, el "cherry picking" (y por lo tanto también el rebase) funciona uno por vez; pero fusiona intenta tomar el resultado como un todo, sin considerar todos los pasos intermedios que puede haber tomado para llegar allí.

(Para tomar medidas intermedias en consideración, realice una " combinación incremental ". Tenga en count que esto no está integrado en git).