Fusionar una rama de una rama después de que la primera rama se aplasta cuando se fusiona con la maestra

81

Aquí hay un flujo de trabajo con el que me ocupo habitualmente en el trabajo.

git checkout -b feature_branch
# Do some development
git add .
git commit
git push origin feature_branch

En este punto, mis colegas deben revisar la rama de funciones, pero quiero seguir desarrollando otras funciones de las que dependen feature_branch. Entonces, mientras feature_branchestá en revisión ...

git checkout feature_branch
git checkout -b dependent_branch
# Do some more development
git add .
git commit

Ahora hago algunos cambios en respuesta a la revisión del código en feature_branch

git checkout feature_branch
# Do review fixes
git add .
git commit
git checkout dependent_branch
git merge feature_branch

Ahora aquí es donde tenemos problemas. Tenemos una política de aplastamiento en el maestro, lo que significa que las ramas de características que se fusionan en el maestro deben comprimirse en una única confirmación.

git checkout feature_branch
git log # Look for hash at beginning of branch
git rebase -i  first_hash_of_branch # Squash feature_branch into a single commit
git merge master

Todo está bien, excepto con dependent_branch. Cuando trato de cambiar la base de la rama dependiente al maestro o intento fusionar el maestro en él, git se confunde con el historial reescrito / aplastado y básicamente marca cada cambio depedendent_branchcomo un conflicto. Es un PITA para pasar y básicamente rehacer o eliminar los conflictos de todos los cambios dependent_branch. ¿Hay alguna solución para esto? A veces, creo manualmente un parche y lo aplico desde una nueva rama del maestro, pero si hay algún conflicto real con eso, es aún peor arreglarlo.

git checkout dependent_branch
git diff > ~/Desktop/dependent_branch.diff
git checkout master
git checkout -b new_dependent_branch
patch -p1 < ~/Desktop/dependent_branch.diff
# Pray for a clean apply.

¿Algunas ideas? Sé que esto sucede debido al historial reescrito durante el squash, pero ese es un requisito que no puedo cambiar. ¿Cuál es la mejor solución / alternativa? ¿Hay algo de magia que pueda hacer? ¿O hay una forma más rápida de realizar todos los pasos relacionados con la creación manual de la diferencia?

Miguel
fuente
Aparte, git add .es completamente malvado y eventualmente te morderá cuando termines cometiendo cosas que no pretendías. Debería usar git add -po al menosgit add -u .
meagar
1
Una vez que haya realizado los cambios en la revisión de código, debería volver a colocar la rama dependiente en la rama de funciones. Luego, cuando esté listo para ir al maestro, rebase la rama de la característica en el maestro, aplaste las confirmaciones y luego combine la rama de la característica en el maestro.
Tim

Respuestas:

108

Un poco sobre por qué sucede esto:

Dejaré que Osea ​​"maestro original" y FB"nuevo maestro", después de que una rama de función se haya fusionado en:

Decir se feature_branchparece a:

O - A - B - C 

dependent_feature tiene algunas confirmaciones adicionales además de eso:

O - A - B - C - D - E - F

Fusiona su rama de características original en master y la aplasta, lo que le brinda:

O - FB

Ahora, cuando intente cambiar la base de la rama dependiente, git intentará descubrir el ancestro común entre esas ramas. Si bien originalmente lo habría sido C, si no hubiera reducido las confirmaciones, git encuentra Ocomo el ancestro común. Como resultado, git está tratando de reproducir A, By Clos que están ya contenía en FB, y usted va a obtener un montón de conflictos.

Por esta razón, realmente no puede confiar en un comando de rebase típico, y debe ser más explícito al proporcionar el --ontoparámetro:

git rebase --onto master HEAD~3  # instruct git to replay only the last
                                 # 3 commits, D E and F, onto master.

Modifique el HEAD~3parámetro según sea necesario para sus sucursales y no debería tener que lidiar con ninguna resolución de conflicto redundante.

Alguna sintaxis alternativa, si no le gusta especificar rangos y aún no ha eliminado su rama de función original:

git rebase --onto master feature_branch dependent_feature

                                 # replay all commits, starting at feature_branch
                                 # exclusive, through dependent_feature inclusive 
                                 # onto master
Joshtkling
fuente
Esperaba ver cómo el árbol de git para master es diferente cuando se usa --onto. O quizás alguna otra forma de entender un poco más en qué se diferencia --onto.
Abhishek Anand
3
--onto simplemente te permite ser más selectivo sobre qué confirmaciones te gustaría reproducir en una rama. De forma predeterminada, git intenta "averiguar" qué confirmaciones deben reproducirse. Cuando usa --onto, le indica explícitamente a git que reproduzca un cierto rango de confirmaciones . En mi ejemplo, si no usó --onto, terminaría con O - FB - A - B - C - D - E - F y numerosos conflictos de fusión ya que FB y A - B - C contienen los mismos cambios. Al usar --onto y especificar las últimas 3 confirmaciones, terminas con O - FB - D - E - F, como estaba buscando el autor de la pregunta original.
joshtkling
2

En este caso particular, parece que "sabe" que sólo se ha puesto en master el trabajo aplastado de la rama en la que trabajó originalmente.

Para que pueda fusionarse felizmente manteniendo sus cambios cada vez que haya un conflicto. Hay una opción para eso:

git merge -Xours master

Consulte https://git-scm.com/docs/git-rebase para obtener más detalles.

nha
fuente
1
Desafortunadamente, puede que ese no sea el caso. Si hay tiempo entre las revisiones de código para feature_branch ypendent_branch, es posible que feature_branch se fusione y empuje y que alguien más agregue algo conflictivo antes de que se aterrice dependiente_branch.
Mike
0

Estoy totalmente en desacuerdo con la política de "combinar cada desarrollo de funciones en una única confirmación", pero es su decisión ...

Mantendría las ramas tal como están y crearía una confirmación combinada solo para su publicación, en una rama especial. Ser capaz de seguir el desarrollo paso a paso es valioso, incluso si la dirección no cree en él. Marque los lugares de los squashes con etiquetas en las ramas "reales", también puede agregar etiquetas entre squash con mensajes que apunten a las confirmaciones reales.

vonbrand
fuente
De hecho, me gusta ese aplastamiento, pero eso se debe a que en realidad lo hace arccanist, y todo el desarrollo / confirmaciones / revisión de código para una rama de características se almacena en phabricator ... pero veo su punto con seguridad.
Mike