git: fusiona dos twigs: ¿en qué dirección?

Tenemos la siguiente situación:

A --- B --- C --- ... --- iphone / ... --- last-working --- ... --- master 

Entre el último trabajo y el iPhone, se realizaron 32 confirmaciones. Entre el último trabajo y el maestro, se hicieron muchos commits.

Lo que quiero ahora es una nueva twig en la que tengo iphone y master actual fusionados. Y en algún momento posterior, esto debería fusionarse en el maestro.

Primero, planeé hacer:

 git checkout iphone -b iphone31 git merge master 

Pero luego pensé, si sería mejor hacerlo:

 git checkout master -b iphone31 git merge iphone 

Ahora me pregunto. ¿Cuál sería la diferencia en el resultado? ¿La combinación se comportaría diferente?

Ya probé las dos cosas y, como esperaba, tuve muchos conflictos porque el iPhone es realmente viejo en comparación con el maestro. Ahora me pregunto cuál es la forma más fácil de fusionarlos.

¿Tal vez incluso comenzar con master y fusionar cada commit de iphone en él sería más fácil? Me gusta hacer esto:

 git checkout master -b iphone31 git merge A git merge B git merge C ... git merge iphone 

Al final, cuando se realiza esta fusión (es decir, todos los conflictos se resuelven y está funcionando), quiero hacer esto:

 git checkout master git merge iphone31 

En cuanto a las alternativas

 git checkout iphone -b iphone31 git merge master 

y

 git checkout master -b iphone31 git merge iphone 

tendrán idéntica facilidad o dificultad, es como discutir si un vaso está medio lleno o medio vacío.

Percepción del tree de versión

La forma en que vemos los treees de versión son, de alguna manera, solo nuestra percepción arbitraria. Digamos que tenemos un tree de versiones como el siguiente:

  A----------+ | | | | \_/ \_/ BX | | | | \_/ \_/ CY | | \_/ D | | \_/ E 

Y digamos que queremos hacer una nueva versión Z desprotegida de Y en base a los cambios de C a E, pero sin include los cambios realizados de A a C.

"Oh, eso será difícil porque no hay un punto de partida común". Bueno en realidad no. Si simplemente colocamos los objects un poco diferente en el layout gráfico como este

  / C+---------+ | \ | | | \_/ | DB | / \ | | \_/ | EA | | \_/ X | | \_/ Y 

ahora las cosas están empezando a parecer prometedoras. Tenga en count que no he cambiado ninguna relación de barcos aquí, todas las flechas apuntan de la misma manera que en la image anterior y la versión A sigue siendo la base común. Solo el layout se cambia.

Pero ahora es trivial imaginar un tree diferente

  C'---------+ | | | | \_/ \_/ DB' | | | | \_/ \_/ EA | | \_/ X | | \_/ Y 

donde la tarea sería fusionar la versión E normalmente.

Para que pueda fusionar cualquier cosa que desee, lo único que influye en la facilidad o dificultad es el set de cambios realizados entre el lugar que selecciona como punto de partida o base común y donde se fusiona. No está limitado a utilizar el punto de partida natural que sugiere su herramienta de control de versiones.

Esto puede no ser simple con algunos sistemas / herramientas de control de versiones, pero si todo lo demás falla, no hay nada que le impida hacerlo manualmente al verificar la versión C y save el file como file1, verificando la versión E y guardando el file como file2 , revisando la versión Y y kdiff3 -o merge_result file1 file2 file3 el file como file3, y ejecutando kdiff3 -o merge_result file1 file2 file3 .

Responder

Ahora, para su situación específica, es difícil decir exactamente qué estrategia generará la menor cantidad de problemas, pero si hay muchos cambios que crean algún tipo de conflicto, probablemente sea más fácil dividir y fusionar partes más pequeñas.

Mi sugerencia sería que, dado que hay 32 confirmaciones entre el último trabajo y el iPhone, podría, por ejemplo, comenzar por la bifurcación del maestro y luego combinar en los primeros 16 commits. Si eso resulta ser demasiado problema, invierta e intente combinar los 8 primeros commits. Y así. En el peor de los casos, terminas fusionando cada una de las 32 confirmaciones una por una, pero probablemente sería más fácil que tener que manejar todos los conflictos acumulados en una sola operación de fusión (y en ese caso estás trabajando con una base de código realmente divergente) .

Consejos:

Dibuje en papel un tree de versiones y anote con flechas lo que desea fusionar. Tache las cosas tal como están hechas si divide el process en varios pasos. Esto le dará una idea más clara de lo que quiere lograr, lo que ha hecho hasta ahora y lo que queda.

Realmente puedo recomendar KDiff3 , es una excelente herramienta de diff / merge.

El mejor enfoque realmente depende de si otras personas tienen copys remotas de tu código. Si la twig maestra está solo en su máquina local, puede usar el command rebase para aplicar de manera interactiva las confirmaciones desde la twig de entidades al maestro:

 git checkout master -b iphone-merge-branch git rebase -i iphone 

Tenga en count que esto altera el historial de confirmaciones de su nueva sucursal de iphone-merge-branch , lo que puede causar problemas para cualquier otra persona que intente incorporar los cambios en el process de pago más adelante. Por el contrario, el command de fusión aplica los cambios como una nueva confirmación, que es más segura cuando se queuebora porque no afecta el historial de la sucursal. Consulte este artículo para get algunos consejos útiles sobre el uso de rebase.

Si necesita mantener el historial de confirmaciones sincronizado, es mejor que realice una fusión. Puede usar git mergetool para resolver conflictos interactivamente uno a uno utilizando una herramienta de diferencia visual (aquí puede encontrar un tutorial sobre esto):

 git checkout master -b iphone-merge-branch git merge iphone git mergetool -t kdiff3 

Una tercera opción, si desea un control absoluto sobre el process, sería usar git cherry-pick . Puede usar gitk (o su visualizador de historial favorito) para ver los hash de confirmación en la ramificación de iphone, anotarlos y seleccionarlos individualmente en la twig de fusión, solucionando los conflictos a medida que avanza. Una explicación de este process se puede encontrar aquí . Este process será el más lento, pero podría ser la mejor opción alternativa si los otros methods no funcionan:

 gitk iphone <note down the 35 commit SHA hashes in this branch> git checkout master -b iphone-merge-branch git cherry-pick b50788b git cherry-pick g614590 ... 

Tu dices:

iphone twig es muy viejo en comparación con el maestro

¿Realmente quieres unirlos formando una nueva twig?

El propósito de las twigs maestra y iphone ahora habría sido muy diferente (porque el iphone es muy antiguo). ¿Una nueva fusión de iphone con un ancestro de maestro sería mejor? Piénsalo.

Recomiendo mucho que leas Diversión con fusiones y propósitos de twigs .

Después de leer ese artículo, si todavía siente que desea fusionar iphone y master, @seanhodges explica cómo manejar los conflictos realmente bien.

Es posible que desee hacer una copy de security local de su trabajo antes de experimentar, para que pueda reiniciar, si deja de entender lo que sucedió.

Haga una copy de respaldo de su trabajo para no perderlo durante la rebase, si desea conservarlo como reference.

 git checkout iphone -b iphone_backup 

Crear una nueva twig desde el maestro

 git checkout master -b iphone31 

luego rebase encima de eso.

 git rebase -i --onto iphone31 iphone 

Sean arriba es correcto sobre rebase aplicando un compromiso a la vez. Pero no se preocupe por las confirmaciones existentes en la twig en la que se basa: no se modificarán. Rebase pone nuevos commit (adaptados) encima de ellos. Mientras se hace uno se compromete a la vez, usted tiene un mejor control sobre los conflictos. Sin embargo, lo que es importante, debe volver a establecer su trabajo en la parte superior del maestro y no al revés, por lo que el historial del maestro no cambiará.

Alternativamente, puedes hacer solo

 git rebase -i master iphone 

si no te importa respaldar la twig de iphone. Consulte la documentation de rebase para get detalles y alternativas http://git-scm.com/docs/git-rebase .

Hay una diferencia.


Siempre revisa la twig objective durante una combinación (es decir, si fusionas A en B, finaliza B).


La gente a menudo dice que la dirección de fusión no importa, pero esto es incorrecto. Si bien el contenido resultante será el mismo independientemente de la dirección de fusión, hay varios aspectos que son diferentes:

  1. Los diffs enumerados en la combinación de fusión resultante serán diferentes según la dirección.
  2. La mayoría de los visualizadores de twig decidirán qué twig es "primaria" utilizando la dirección de fusión.

Para elaborar, imagina este ejemplo exagerado:

  • Te has separado de MASTER en 1000 commits behind, y lo has llamado DEVELOP (o en un escenario de tracking-branch, no llegaste bastante time).
  • Usted agrega un compromiso en DESARROLLO. Sabes que no hay conflictos por este cambio.
  • Desea llevar sus cambios a MASTER.
  • Incorrectamente combina MASTER en DESARROLLO (es decir, DESARROLLAR está desprotegido durante la fusión). Luego, presionas DESARROLLAR como el nuevo MAESTRO.
  • Los diffs en el merge-commit resultante mostrarán los 1000 commits que ocurrieron en MASTER, porque DEVELOP es el punto de reference.

No solo es inútil este dato, es difícil leer lo que está pasando. La mayoría de los visualizadores harán que parezca que tu línea DEVELOP fue la principal todo el time, con 1000 commits incorporados.

Mi sugerencia es esta: siempre verifique la twig de destino durante una combinación (es decir, si fusiona A en B, checkout B).

  • Si está trabajando en una sucursal paralela y desea realizar cambios periódicamente desde una sucursal principal, realice la compra en su sucursal paralela. Los diffs tendrán sentido: verás los cambios realizados en la twig principal de tu twig.
  • Cuando haya terminado de trabajar en paralelo y desee fusionar sus cambios en la twig principal, busque en la twig principal y fusione con su twig paralela. La diferencia nuevamente tendrá sentido: mostrará cuáles son tus cambios paralelos a la twig principal.

La legibilidad del logging, en mi opinión, importa.