¿Cómo seleccionar una variedad de confirmaciones y fusionarlas en otra rama? [duplicar]
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!
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..B
formulario " ",A
debe ser anterior aB
.
Si están en el orden incorrecto, el comando fallará silenciosamente .
Si desea elegir el rango B
hasta 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
B
no es una confirmación raíz; De lo contrario , obtendrá ununknown revision
error " ".
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 laworking
rama)
a " tmp
" (que apunta a donde integration
apuntaba 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 laintegration
rama en latmp
rama)
Después de eso rebase --onto
, integration
regresaremos 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-pick
solo acepta una única confirmación, pero si desea elegir el rango,B
eseD
seríaB^..D
(en realidadB~..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 rebase
funció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
CommitId1
si, por ejemplo, el padre deCommitId1
es una confirmación de fusión:git cherry-pick CommitId1^..CommitId99
.
En ese caso,cherry-pick
todavía comienza desdeCommitId2
, 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 deCommitId1
es 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 CommitId1
e incluir confirmaciones hasta CommitId99
".
- Si
CommitId1
se trata de una confirmación normal, su único padre (digamosCommitId0
) se convierte en el inicio del rango, excluyéndoseCommitId1
a sí mismo de manera efectiva. - Si
CommitId1
se 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 CommitId1
y retroceder una confirmación desde allí", que en la mayoría de los casos se incluirá CommitId1
en el rango, según lo previsto.
Considere el siguiente historial de confirmaciones, donde M
representa 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
,E
son confirmaciones en lamaster
rama.X
,Y
son confirmaciones en unafeature
rama.M
es una confirmación de fusión en lafeature
rama, fusionando cambios demaster
dentro defeature
. AquíM
estáCommitId1
.
Cuando ejecutas el comando:
git cherry-pick CommitId1^..CommitId99
CommitId1
esM
.CommitId1^
se refiere al primer padre deM
.- En este caso, el primer padre de
M
seD
debe a que las confirmaciones de fusión enumeran sus padres en el orden en que se fusionaron.
Entonces, el rango CommitId1^..CommitId99
se 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 CommitId1
e incluir hasta CommitId99
".
Entonces CommitId1~
se refiere a la confirmación justo antes M
en la feature
rama, que es Y
(ya que M
se creó en la feature
rama, mediante una fusión de master
a feature
).
El rango CommitId1~..CommitId99
se traduce en Y..CommitId99
.
El uso ~
en el rango con git cherry-pick
cambia efectivamente el inicio del rango al compromiso justo antes CommitId1
, incluyéndolo así CommitId1
en el rango seleccionado. Ese comportamiento es particularmente útil cuando desea incluir confirmaciones de fusión en su operación de selección.
A partir de git v1.7.2, cherry pick puede aceptar una variedad de confirmaciones:
git cherry-pick
aprendió a elegir una variedad de confirmaciones (por ejemplo,cherry-pick A..B
ycherry-pick --stdin
), y también lo hizogit revert
; Sin embargo , estos no respaldan el mejor control de secuenciaciónrebase [-i]
que tiene.
Como señala Gabe Moothart , cherry-pick A..B
no 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).
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.
¿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 --3way
si git-am
necesita 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...
git cherry-pick start_commit_sha_id ^.. end_commit_sha_id
p.ejgit cherry-pick 3a7322ac^..7d7c123c
Suponiendo que se encuentra en branchA
el 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.