Git moviendo una twig a otra con una historia de compromiso

Estoy desarrollando en mi twig de características. Después de completar el desarrollo, moveré todo el código comprometido a la twig principal.

Pero el punto especificado es después de que esta twig maestra de operación tenga solo un historial de compromiso.

Trataré de explicar con treees.

--- BRANCH TREES WITHOUT MERGE OPERATION --- A--B--C (master-branch) \ ^--- After the merge operation commit will come after this point. \ D--E--F--G (feature-branch) --- AFTER THE MERGE OPERATION EXPECTED BRANCH TREES --- A--B--C--(H) (master-branch) \ ^--- 'H' is the commit of all feature branch merge. \ Include commits D, E, F, G. \ This should have all source but, will shown one commit message. \ D--E--F--G (feature-branch) 

¿Cómo puedo fusionar estas twigs de esta manera?

Git llama a esto una "fusión de squash", y lo git merge --squash ejecutando git merge --squash . Al igual que con todas las confirmaciones nuevas en Git, la nueva confirmación se agregará a la sucursal actual, por lo que en este caso se obtendría primero la master-branch luego se ejecutaría la git merge --squash feature-branch .

Advertencias

Hay algunas cosas importantes que debes saber sobre esto. En particular, aunque el tree (fuente o tree de trabajo) asociado con la confirmación resultante se obtiene mediante la fusión (el verbo: la acción, "fusionar"), no es una combinación de combinación (adjetivo o sustantivo).

Recuerde que en Git, commits tiene dos funciones:

  • Ellos son la historia.

  • Contienen instantáneas del tree de trabajo (más algunos metadatos: quién realizó la confirmación y cuándo y un post de logging).

Una git merge other normal git merge other encuentra una confirmación de base de fusión ejecutando git merge-base --all HEAD other , 1 luego:

  • Combina dos sets de diferencias: diffs de la base a HEAD , que equivalen a "lo que ha cambiado", más diferencias de la base a other , que equivalen a "lo que cambiaron". Si cambiaste el file one.txt y cambiaron el file two.txt , Git toma ambos cambios, es decir, tu one.txt y sus two.txt . Si ambos cambiaron el file three.txt , Git intenta ver si los cambios y los de ellos se superponen, o son duplicates exactos, o son completamente independientes. Si se superponen, Git te da un conflicto de fusión. Si son independientes, Git acepta ambos cambios. Si son duplicates exactos, Git toma una copy del cambio. El three.txt resultante es el resultado de una combinación de tres vías .

  • Realiza un nuevo compromiso cuya historia dice: "este compromiso es un compromiso de fusión, combinando el trabajo del primer padre HEAD y el segundo padre other ".

Una git merge --squash other aún hace la acción de fusión, incluidas las fusiones de tres vías requeridas. Entonces, sin ninguna razón obvia 2, comporta el paso de git commit y te hace ejecutar git commit , y cuando ejecutas git commit , el resultado no es un commit de fusión: su historia dice "este commit es un commit ordinario, y su el único padre es HEAD ". Su historia de compromiso es, en otras palabras, como si hubiera realizado todos los cambios de other , a exception de los duplicates, usted mismo.

Esto es lo que estás pidiendo, pero también es una especie de trampa. Si decide, más adelante, que necesita desarrollar más en feature-branch y fusionar nuevamente, Git no tendrá el historial que muestra una fusión existente, y no sabrá que podría comenzar el nuevo process de fusión usando el resultado de la fusión anterior.

Lo que esto significa al final es que las fusiones squash funcionan mejor cuando intenta eliminar la twig de características inmediatamente (o al less muy pronto) después de la fusión de squash:

 A---B----C <-- main \ D--E--F--G <-- feature 

(ahora git checkout main && git merge --squash feature && git commit && git branch -D feature )

 A---B----C--H <-- main \ D--E--F--G [abandoned] 

De esta forma, no puedes volver atrás y agregar más commits después de G que usan la cadena DEFG que luego requerirán fusionarse nuevamente. Si retrocedió y realizó tales confirmaciones, y se fusionó de nuevo, la confirmación de la combinación de fusión sería A , no G , y es más probable que Git se fusione incorrectamente, duplicando accidentalmente algunos de los cambios DEFG . 3


1 Si git merge-base --all encuentra más de una base de combinación, el comportamiento depende de la estrategia de combinación. Si no encuentra una base de combinación, el comportamiento en las versiones anteriores de Git y en las versiones nuevas de Git es diferente: los Gits más nuevos se detienen con un error al rechazar fusionar historias no relacionadas.

2 Una fusión regular también se detendrá, pero solo si se solicita específicamente con --no-commit . Un git commit posterior de git commit hará un commit de fusión, es decir, un commit de dos padres. Así que no hay ninguna razón para que git merge --squash actúe como si también especificaras --no-commit : si realmente quisieras que se detuviera, podrías ejecutar git merge --squash --no-commit . (Es probable que esto sea un remanente de los primeros guiones de Git).

El algorithm de fusión de 3 Git, que trata de tomar solo una copy de cada cambio, a menudo logrará tomar solo una copy del cambio. Es decir, la diferencia de A a lo que venga después de G incluye todos los cambios de DEFG , y la diferencia de A a lo que viene después de H también incluye todos los cambios de DEFG , pero eso es lo que significa "tomar una copy de los cambios" manejar.

El problema es que cuando enfatizas demasiado este algorithm, falla. Por lo general, el efecto es un conflicto de fusión gigantesco que debes resolver a mano.

Creo que lo que quieres hacer es la function 'squash commits'.

Solo haría una copy de la twig original, y luego la rebase de manera interactiva, para poder aplastar las confirmaciones.

Uno de los enfoques:

 git checkout -b tmp feature-branch git reset A --soft git commit -m "feature" #a new commit X is created. It's a squash of D, E, F, G git checkout master-branch git cherry-pick X git branch -D tmp