Encontrar el compromiso culpado de una mala combinación de git

Me encontré con un problema extraño con mi script de fusión. El script en general hace los siguientes pasos:

  1. git checkout $ source_branch
  2. git checkout -f $ dest_branch
  3. git merge -Xtheirs –squash $ source_branch -m "$ commit_message"
  4. git commit -m $ commit_message
  5. git push origen –todo

Hace otras cosas como tag en master y crea una nueva versión, etc.

Ahora, al acertijo, descubrí que en la última fusión entre desarrollar y liberar candidato, se insertó un fragment de código en una location aleatoria y se rompió el producto. Miré todas las confirmaciones de desarrollo y no pude encontrar ninguna con este cambio.

Entonces, ¿hay alguna manera de encontrar el compromiso culpable incluso si los aplasté durante la fusión?

… entonces, ¿hay alguna forma de encontrar el compromiso culpable incluso si los aplasté durante la fusión?

La respuesta corta es no, pero puede haber una manera de recuperarse.

Qué combinación de squash es y hace

Este es uno de los peligros de squash "merge" (que no es una fusión en absoluto: utiliza la maquinaria de fusión , es decir, "fusiona como un verbo", pero produce una única confirmación ordinaria, que no es una combinación , es decir, no "fusionar como un sustantivo"). Por ejemplo, supongamos que tienes esto para empezar:

 ...--o--Q--R <-- main \ A--B--C <-- side 

es decir, hay tres confirmaciones en el lado de la twig side que no están en la twig main , y luego ejecuta el command:

 git checkout main && git merge --squash side 

Lo que obtienes es esto:

 ...--o--Q--R--S <-- main \ A--B--C <-- side 

donde S es un nuevo compromiso que contiene el resultado del tree de trabajo al realizar la combinación. Este resultado del tree de trabajo es el resultado de combinar lo que cambiaste (en main , in commits Q y R ) con lo que cambiaron (en side , in commits A , B y C ). Esa es la misma combinación que haría una combinación regular. Pero S es un compromiso ordinario, no un compromiso de fusión. La nueva confirmación S no almacena la identificación hash de la confirmación C Git no puede encontrar el compromiso C través del compromiso S

Si hubiera hecho una fusión "real", S tendría un segundo enlace hacia atrás, de modo que apuntaría no solo para confirmar R sino también para confirmar C Git podría usar esto para localizar cualquier problema en la cadena ABC . Pero le dijiste a Git: No, no quiero el viejo ABC, márcalo, solo mantén a S. Esto no está mal: mantiene despejada la twig principal, por ejemplo. Pero limita su capacidad más adelante, para volver atrás y ver ABC en su lugar: eso es a la vez su gran ventaja y su gran desventaja. Y eso es todo una fusión de squash.

Encontrar un error con git bisect

Ahora, solo porque sacaste ABC al hacer la fusión, no significa que hayas descartado el ABC completo. Si todavía tiene el side del nombre apuntando a cometer C , puede usarlo para encontrar la confirmación C Esto te permitirá usar git bisect en la cadena ABC .

Sin embargo, dado que esta cadena no está conectada a la twig principal (no hay ningún enlace entre S y ella), debe ejecutar la operación de bisección en la cadena original, no en la cadena de fusión aplastada. Si el error se encuentra en la confirmación B , deberá arreglarlo por separado en una confirmación después de la confirmación S , en la twig main (a less que sea correcto eliminar S completamente de la main , y usted elige hacerlo en su lugar).

Si no tiene las confirmaciones originales, es decir, ha eliminado el lado del nombre de la sucursal side entonces tiene que tratar la confirmación S como lo que es: una gran comisión que cambia mucho. Encuentra el error.

La resolución automática es peligrosa

Cualquier fusión de Git puede ir mal, pero agregar -X theirs (o -X ours , para el caso) es especialmente peligrosa, porque le dice a Git-que no es inteligente, para nada-que cualquier conflicto de fusión puede resolverse eligiendo ciegamente una de las dos alternativas.

Mencioné la maquinaria de fusión anterior y hablé de combinar "sus cambios" (confirma Q y R ) con "sus cambios" (confirma A , B y C ). Supongamos que decidieron que toda la fruta es anaranjada, porque de vuelta en el compromiso o el único tipo de fruta utilizada fueron las naranjas:

  We have some recipes for you. -Fruits grow on fruit trees. +Oranges grow on orange trees. Blah blah ... 

Mientras tanto, usted , al cometer Q , agregó algunas instrucciones para convertir manzanas en puré de manzana:

  We have some recipes for you. Fruits grow on fruit trees. +To turn apples into applesauce, see recipe 3. Blah blah ... 

Aquí, sus froutes que limitan el cambio a las naranjas solo chocan con su cambio. Una fusión ordinaria le diría que no estaba seguro de qué hacer aquí -tut corrió git merge -X theirs , diciéndole a Git: "siempre que haya un problema , deshazte de mi cambio, y toma solo el de ellos". Por lo tanto, su reference adicional a puré de manzana simplemente se descarta . Eso podría estar bien, pero podría no ser así. Git no sabe, solo sigue tus instrucciones.

Cualquier fusión requiere algún tipo de escrutinio (como las testings), pero una fusión realizada con "cerrar los ojos, elegir una y esperar lo mejor" requiere un escrutinio adicional . Esto es cierto independientemente de si va a cometer el resultado de fusión como un verbo como fusión-como-un-nombre, o como una confirmación ordinaria que olvida las dos fonts del tree de trabajo fusionado. Si mantiene la bifurcación lateral, ya sea por fusión real, o simplemente manteniéndola, y no publica el resultado de la fusión de inmediato, tiene la oportunidad de hacer algunas testings buenas, o al less cerrar la corrección, del resultado de la fusión.

Si no, te preparas para este mismo problema.

Puede especificar el hash de confirmación de origen en el post de confirmación. Entonces no tienes que searchlo.

También puede dejar de usar --squash .