¿Cómo seleccionar una variedad de confirmaciones y fusionarlas en otra rama? [duplicar]

Resuelto crazybyte asked hace 14 años • 12 respuestas

Tengo el siguiente diseño de repositorio:

  • rama maestra (producción)
  • integración
  • laboral

Lo que quiero lograr es seleccionar una variedad de confirmaciones de la rama de trabajo y fusionarlas en la rama de integración. Soy bastante nuevo en git y no puedo entender cómo hacer esto exactamente (la selección de rangos de confirmación en una operación, no la fusión) sin estropear el repositorio. ¿Algún consejo o idea sobre esto? ¡Gracias!

crazybyte avatar Jan 03 '10 16:01 crazybyte
Aceptado

Cuando se trata de una variedad de confirmaciones, seleccionar cuidadosamentees no era práctico.

Como lo menciona Keith Kim a continuación , Git 1.7.2+ introdujo la capacidad de seleccionar una variedad de confirmaciones (pero aún debe tener en cuenta las consecuencias de la selección para futuras fusiones ).

git cherry-pick" aprendió a elegir una variedad de confirmaciones
(por ejemplo, " cherry-pick A..B" y " cherry-pick --stdin"), y " git revert" también lo hizo; sin embargo, estas no admiten el control de secuenciación más agradable que " rebase [-i]" tiene.

damian comenta y nos advierte:

En el cherry-pick A..Bformulario " ", Adebe ser anterior aB .
Si están en el orden incorrecto, el comando fallará silenciosamente .

Si desea elegir el rango Bhasta D(incluido B) , sería B~..D(en lugar de B..D).
Consulte "¿ Git crea una rama a partir de una variedad de confirmaciones anteriores? " para ver una ilustración.

Como menciona Jubobs en los comentarios :

Esto supone que Bno es una confirmación raíz; De lo contrario , obtendrá un unknown revisionerror " ".

Nota: a partir de Git 2.9.x/2.10 (tercer trimestre de 2016), puede seleccionar un rango de confirmaciones directamente en una rama huérfana (cabeza vacía): consulte " Cómo hacer que una rama existente sea huérfana en Git ".


Respuesta original (enero de 2010)

Sería mejor A rebase --onto, donde reproduces el rango dado de confirmaciones en la parte superior de tu rama de integración, como lo describió Charles Bailey aquí .
(Además, busque "Así es como trasplantaría una rama temática basada en una rama a otra" en la página de manual de git rebase , para ver un ejemplo práctico de git rebase --onto)

Si tu sucursal actual es integración:

# Checkout a new temporary branch at the current location
git checkout -b tmp

# Move the integration branch to the head of the new patchset
git branch -f integration last_SHA-1_of_working_branch_range

# Rebase the patchset onto tmp, the old location of integration
git rebase --onto tmp first_SHA-1_of_working_branch_range~1 integration

Eso reproducirá todo entre:

  • después del padre de first_SHA-1_of_working_branch_range(de ahí el ~1): la primera confirmación que desea reproducir
  • hasta " integration" (que apunta a la última confirmación que desea reproducir, desde la workingrama)

a " tmp" (que apunta a donde integrationapuntaba antes)

Si hay algún conflicto cuando se reproduce una de esas confirmaciones:

  • Resuélvelo y ejecuta " git rebase --continue".
  • u omitir este parche y en su lugar ejecutar " git rebase --skip"
  • o cancelar todo con un " git rebase --abort" (y volver a colocar la integrationrama en la tmprama)

Después de eso rebase --onto, integrationregresaremos a la última confirmación de la rama de integración (es decir, " tmp" rama + todas las confirmaciones reproducidas)

Con la selección selectiva o rebase --onto, no olvide que tiene consecuencias en fusiones posteriores, como se describe aquí .


Aquí se analiza una " cherry-pick" solución pura que implicaría algo como:

Si desea utilizar un método de parche, sus opciones son "git format-patch|git am" y "git cherry".
Actualmente, git cherry-picksolo acepta una única confirmación, pero si desea elegir el rango, Bese Dsería B^..D(en realidad B~..D, consulte a continuación) en la jerga de Git, entonces:

git rev-list --reverse --topo-order B~..D | while read rev 
do 
  git cherry-pick $rev || break 
done 

Pero de todos modos, cuando necesites "reproducir" una serie de confirmaciones, la palabra "reproducir" debería impulsarte a utilizar la rebasefunción " " de Git.


objetos pridmorej en los comentarios :

ADVERTENCIA: ¡no se deje engañar por la sugerencia anterior de usar quilates ( ^) para que el rango sea inclusivo!

Esto no incluye CommitId1si, por ejemplo, el padre de CommitId1es una confirmación de fusión: git cherry-pick CommitId1^..CommitId99.
En ese caso, cherry-picktodavía comienza desde CommitId2, no tengo idea de por qué, pero ese es el comportamiento que he experimentado.

Sin embargo, descubrí que usar tilde ( ~) funciona como se esperaba, incluso cuando el padre de CommitId1es una confirmación de fusión: git cherry-pick CommitId1~..CommitId99.

Es cierto: eso resalta un matiz importante en el uso del comando de selección de Git con rangos de confirmación, particularmente cuando se trata de confirmaciones de fusión.

En Git, ^y ~tienen significados específicos cuando se usan con referencias de confirmación:

  • ^se refiere al padre de una confirmación y, cuando se usa en un rango, puede generar resultados inesperados, especialmente con confirmaciones de fusión.
  • ~, por otro lado, se utiliza para indicar el primer padre de una confirmación en un historial lineal, lo que lo hace más predecible en el contexto de la selección de un rango.

Al seleccionar un rango de confirmaciones, especialmente en escenarios que involucran confirmaciones de fusión, prefiera usar ~para asegurarse de que el rango incluya las confirmaciones deseadas.

En cuanto a git cherry-pick CommitId1^..CommitId99: al especificar un rango CommitId1^..CommitId99, Git interpreta esto como "comenzar desde el padre CommitId1e incluir confirmaciones hasta CommitId99".

  • Si CommitId1se trata de una confirmación normal, su único padre (digamos CommitId0) se convierte en el inicio del rango, excluyéndose CommitId1a sí mismo de manera efectiva.
  • Si CommitId1se trata de una confirmación de fusión, CommitId1^todavía apunta a su primer padre. Esto puede ser particularmente confuso porque las confirmaciones de fusión, por su naturaleza, fusionan dos líneas de desarrollo, y el primer padre podría no ser intuitivamente la confirmación "anterior" en un sentido lineal.

En historiales no lineales que involucran confirmaciones de fusión, es posible que el primer padre de una confirmación de fusión no sea el predecesor directo en la misma rama.

La notación tilde ( ~), cuando se usa como en CommitId1~..CommitId99, significa efectivamente "incluir CommitId1y retroceder una confirmación desde allí", que en la mayoría de los casos se incluirá CommitId1en el rango, según lo previsto.

Considere el siguiente historial de confirmaciones, donde Mrepresenta una confirmación de fusión y cada letra representa una confirmación diferente:

A---B---C-------D---E--CommitId99   <- master
     \         /
      X---Y---M                     <- feature (CommitId1 is M)
  • A, B, C, D, Eson confirmaciones en la masterrama.
  • X, Yson confirmaciones en una featurerama.
  • Mes una confirmación de fusión en la featurerama, fusionando cambios de masterdentro de feature. Aquí Mestá CommitId1.

Cuando ejecutas el comando:

git cherry-pick CommitId1^..CommitId99
  • CommitId1es M.
  • CommitId1^se refiere al primer padre de M.
  • En este caso, el primer padre de Mse Ddebe a que las confirmaciones de fusión enumeran sus padres en el orden en que se fusionaron.

Entonces, el rango CommitId1^..CommitId99se traduce en D..CommitId99. ¡ M ( CommitId1) está excluido!

Pero si usa git cherry-pick CommitId1~..CommitId99, cuando se usa en el contexto de un rango, como CommitId1~..CommitId99, la interpretación es "comenzar desde la confirmación justo antes CommitId1e incluir hasta CommitId99".

Entonces CommitId1~se refiere a la confirmación justo antes Men la featurerama, que es Y(ya que Mse creó en la featurerama, mediante una fusión de mastera feature).
El rango CommitId1~..CommitId99se traduce en Y..CommitId99.

El uso ~en el rango con git cherry-pickcambia efectivamente el inicio del rango al compromiso justo antes CommitId1, incluyéndolo así CommitId1en el rango seleccionado. Ese comportamiento es particularmente útil cuando desea incluir confirmaciones de fusión en su operación de selección.

VonC avatar Jan 03 '2010 10:01 VonC

A partir de git v1.7.2, cherry pick puede aceptar una variedad de confirmaciones:

git cherry-pickaprendió a elegir una variedad de confirmaciones (por ejemplo, cherry-pick A..By cherry-pick --stdin), y también lo hizo git revert; Sin embargo , estos no respaldan el mejor control de secuenciación rebase [-i]que tiene.

Como señala Gabe Moothart , cherry-pick A..Bno se comprometerá A(lo necesitaría A~1..B) y, si hay algún conflicto, git no continuará automáticamente como lo hace rebase (al menos a partir de 1.7.3.1).

Keith Kim avatar Sep 08 '2010 03:09 Keith Kim

Supongamos que tiene 2 sucursales,

"branchA": incluye las confirmaciones que desea copiar (de "commitA" a "commitB"

"branchB": la rama desde la que desea que se transfieran las confirmaciones "branchA"

1)

 git checkout <branchA>

2) obtener los ID de "commitA" y "commitB"

3)

git checkout <branchB>

4)

git cherry-pick <commitA>^..<commitB>

5) En caso de que tengas algún conflicto, resuélvelo y escribe

git cherry-pick --continue

para continuar con el proceso de selección.

KostasA avatar Aug 01 '2017 14:08 KostasA

¿Estás seguro de que no deseas fusionar las ramas? Si la rama de trabajo tiene algunas confirmaciones recientes que no desea, puede simplemente crear una nueva rama con un HEAD en el punto que desee.

Ahora, si realmente desea seleccionar una variedad de confirmaciones, por cualquier motivo, una forma elegante de hacerlo es simplemente extraer un conjunto de parches y aplicarlo a su nueva rama de integración:

git format-patch A..B
git checkout integration
git am *.patch

Esto es esencialmente lo que hace git-rebase de todos modos, pero sin la necesidad de jugar. Puede agregar --3waysi git-amnecesita fusionar. Asegúrese de que no haya otros archivos *.patch en el directorio donde hace esto, si sigue las instrucciones al pie de la letra...

djs avatar Jan 04 '2010 09:01 djs

git cherry-pick start_commit_sha_id ^.. end_commit_sha_id

p.ejgit cherry-pick 3a7322ac^..7d7c123c

Suponiendo que se encuentra en branchAel lugar donde desea elegir confirmaciones ( se proporciona el SHA de inicio y finalización para el rango y el SHA de confirmación izquierda es anterior ) branchB. Toda la gama de confirmaciones (ambas incluidas) se seleccionarán en branchA.

Los ejemplos dados en la documentación oficial son bastante útiles.

Saikat avatar Jul 17 '2020 12:07 Saikat