git: omitiendo confirmaciones específicas al fusionar

200

He estado usando Git durante aproximadamente un año y creo que es fantástico, pero acabo de comenzar con una segunda versión del proyecto y comencé una nueva rama para él. Estoy luchando un poco con la mejor manera de manejar las cosas en el futuro.

Tengo dos ramas llamadas say master10 (para v1) y master20 (para v2). He estado haciendo correcciones de errores en v1 en la rama master10, y desarrollando nuevas cosas de master20. Cada vez que soluciono un error, lo combino en la v2 revisando master20 y haciendo git merge master10. Hasta aquí todo bien.

Ahora, sin embargo, he realizado un cambio en v1 que no quiero en v2, pero quiero continuar fusionando otras correcciones de errores. ¿Cómo le digo a Git que omita ese commit en particular (o un rango de commits), pero que en el futuro todavía quiero fusionar otras correcciones de errores?

Pensé que git rebasepodría ser lo que necesitaba, pero leí el documento y mi cabeza casi explotó.

Creo que lo que quiero es algo así como un comando "git sync" que le dice a git que dos ramas ahora están sincronizadas y en el futuro solo fusionan los commits desde este punto de sincronización en adelante.

Cualquier ayuda apreciada.

Brad Robinson
fuente

Respuestas:

289

Si desea fusionar la mayoría pero no todos los commits en la rama "maint" a "master", por ejemplo, puede hacerlo. Requiere algo de trabajo, como se mencionó anteriormente, el caso de uso habitual es fusionar todo desde una rama, pero a veces sucede que realizó un cambio a una versión de lanzamiento que no debería integrarse (tal vez ese código ya ha sido reemplazado en master), entonces, ¿cómo representas eso? Aquí va...

Entonces, supongamos que a maint se le han aplicado 5 cambios, y uno de ellos (maint ~ 3) no debe volver a fusionarse en master, aunque todos los demás deberían serlo. Haces esto en tres etapas: fusionar todo antes de eso, decirle a git que marque maint ~ 3 como fusionado incluso cuando no lo es, y luego fusionar el resto. La magia es:

bash <master>$ git merge maint~4
bash <master>$ git merge -s ours maint~3
bash <master>$ git merge maint

El primer comando fusiona todo antes de que su problemático mantenimiento se confirme en el maestro. El mensaje de registro de fusión predeterminado explicará que está fusionando "branch 'maint' (primera parte)".

El segundo comando combina el problemático maint ~ 3 commit, pero la opción "-s our" le dice a git que use una "estrategia de fusión" especial que, de hecho, funciona simplemente manteniendo el árbol en el que se está fusionando e ignorando el commit (s ) te estás fusionando por completo. Pero aún así se realiza una nueva confirmación de fusión con HEAD y maint ~ 3 como padres, por lo que el gráfico de revisión ahora dice que maint ~ 3 se fusionó. Entonces, de hecho, probablemente desee usar la opción -m para 'git merge' también, para explicar que ese maint ~ 3 commit realmente se está ignorando.

El comando final simplemente combina el resto de maint (maint ~ 2..maint) en master para que todos estén sincronizados nuevamente.

araqnid
fuente
55
Creo que si necesita posponer la fusión de esa confirmación, no tiene más remedio que omitirla (fusionar -s la nuestra) y luego aplicarla utilizando el comando cherry-pick. Una vez que se puede acceder a la confirmación desde el maestro, no se puede fusionar nuevamente; evitar fusionar el mismo cambio dos veces es uno de los principales objetivos de git.
araqnid
3
Con respecto a su ejemplo de sub-sucursal: sospecho que una vez que haya fusionado la sub-sucursal, se encuentra exactamente en la misma situación que después del segundo paso de mi publicación. Crear ramas adicionales no hace ninguna diferencia en las relaciones de compromiso.
araqnid
55
Solo por interés, si solo desea omitir un cambio, ¿por qué no haría una sola fusión y luego revertir ese conjunto de cambios, en lugar de realizar tres fusiones? Resultaría en menos conjuntos de cambios comprometidos con el repositorio y un historial más explícito.
Mark Booth
3
A menudo es más fácil nombrar explícitamente los commits. Por lo tanto, solo necesita ver el registro de git de la rama que se va a fusionar y observar los hashes del commit que no deberían fusionarse y el anterior, en lugar de contar commits ...
zwirbeltier
14
@ MarkBooth: el compromiso que desea omitir podría estar en un gran conflicto con la rama en la que desea fusionarse. Es más fácil omitirlo en lugar de solucionar los conflictos y luego revertir ese cambio y solucionar los conflictos nuevamente
SztupY
39

En mi humilde opinión, lo más lógico es fusionar todo y luego usar git revert (commit_you_dont_want) para eliminarlo .

Ejemplo:

git merge master
git revert 12345678

Si tiene varias confirmaciones de "ignorar" o desea editar el mensaje de reversión:

git merge master
git revert -n 123456
git revert -n abcdef
git commit -m "... Except commits 123456 and abcdef"

Entonces su historial puede verse así:

| ... Except 123456 and abcdef
|\ Merge branch 'master' into 'your_branch'

Si tiene conflictos que involucran SOLO estas confirmaciones de "ignorar", puede usar:

git merge master -X ours

Entonces su versión persistirá sobre la otra. Incluso sin mensajes de error, aún puede "revertir" esas confirmaciones no deseadas, ya que pueden tener otros cambios que no entraron en conflicto, y todavía no los quiere.

Si tiene conflictos que involucran NO SOLO las confirmaciones de "ignorar", debe resolverlas manualmente, y probablemente tendrá que resolverlas nuevamente durante la reversión.

Alexandre T.
fuente
2
Si luego quieres fusionar esos commits revertidos en la rama en cuestión, ¿Git los ignorará?
Anriëtte Myburgh
@ AnriëtteMyburgh Puede revertir los compromisos de reversión para luego fusionarlos en algo que en realidad no puede hacer tan fácilmente con la estrategia "fusionar-nuestra".
Lily Chung
Gracias, esto es exactamente lo que necesitaba, tenía algunos commits más antiguos en una rama de características que no quería en master y un montón que sí quería en master. Bonito y limpio.
Vale Trujillo
17

Los commits incluyen ascendencia. No puede fusionar una confirmación sin fusionar confirmaciones anteriores.

Puedes elegirlos, por supuesto. Eso es un buen flujo cuando tienes una rama que está en modo de mantenimiento.

Dustin
fuente
1
Gracias, Cherry Pick hará el trabajo. No es tan bueno como esperaba, pero servirá.
Brad Robinson
3

Una especie de anuncio para mi proyecto que básicamente envuelve el proceso descrito por @araqnid.

Es una especie de ayuda que introduce el siguiente flujo de GIT:

  • hay una notificación diaria / semanal sobre fusiones pendientes de las sucursales de mantenimiento en la sucursal de desarrollo / maestra
  • el encargado de la sucursal verifica el estado y decide si se requieren todas las confirmaciones y bloquea algunas o solicita a los desarrolladores que se bloqueen. Al final, la rama de mantenimiento se fusiona en el flujo ascendente.

Una cita de la página del proyecto:

Dependiendo del flujo de trabajo, es posible tener sucursales de mantenimiento o específicas del cliente junto con la sucursal maestra. Estas ramas también se llaman ramas LTS.

A menudo, las correcciones urgentes van a las ramas donde se informó el error y luego la confirmación se fusiona nuevamente en la rama maestra.

La práctica general es tener todas las ramas perfectamente sincronizadas con el maestro, es decir, desea ver un delta claro entre una rama particular y un maestro para comprender si el maestro contiene todas las características y correcciones de errores.

Sin embargo, a veces no desea confirmaciones particulares porque son específicas del cliente y no serán visibles para otros usuarios. O su rama maestra divergió tanto que requiere un enfoque completamente diferente para solucionar el problema, o mejor aún, el problema ya no está presente allí.

También en caso de selección de cereza de maestro a la rama de mantenimiento, el compromiso resultante se bloqueará en maestro.

Dmytro
fuente
0

Cree una tercera rama para los cambios que desea en master10 pero no en master20. Siempre considere master10 como su "maestro", la rama más estable de todas. La rama con la que todas las demás ramas quieren estar sincronizadas en todo momento.

Wilhelmtell
fuente
Supongo que eso podría funcionar, pero ya me he metido en este estado, y una tercera rama probablemente me confundiría aún más. :)
Brad Robinson
0

En lugar de reverto cherry-pickpara este caso, necesita que git considere que los cambios que omite son más antiguos que los que realizó.

Entonces:

  1. combine la última confirmación antes de las confirmaciones que desea omitir. Esto, por supuesto, fusionará todas las confirmaciones anteriores. git merge ccc
  2. fusionar las confirmaciones que desea omitir. git merge fff --no-commit
  3. realice cualquier fusión, elimine todos los cambios, deshaga todos los cambios. (Tal vez hay comandos infalibles para esto, pero solo haría esta parte en una interfaz de usuario, sin embargo, ya sabes cómo)
  4. completar la fusión vacía git merge --continue
  5. fusionar las confirmaciones DESPUÉS de la que desea omitir. git merge source-branch-head

Después del Paso 4, git considerará su rama más reciente que esa confirmación, ya que ya la trató (al elegir mantener SUS versiones de las cosas).

Jason Kleban
fuente