Tengo el siguiente diseño de repositorio:
Lo que quiero conseguir es seleccionar un rango de commits de la rama de trabajo y fusionarlo con la rama de integración. Soy bastante nuevo en git y no puedo averiguar cómo hacer esto exactamente (la selección de rangos de commits en una operación, no la fusión) sin desordenar el repositorio. ¿Algún consejo o idea sobre esto? Gracias.
Cuando se trata de un rango de commits, el cherry-picking es *no es práctico.
Como se menciona a continuación por Keith Kim, Git 1.7.2+ introdujo la capacidad de hacer cherry-pick de un rango de commits (pero todavía hay que ser consciente de la consecuencia de hacer cherry-pick para futuras fusiones)
git cherry-pick" aprendió a escoger un rango de commits (p.ej. "Cherry-pick A..B
" y "Cherry-pick --stdin
"), también lo hizo "git revert
"; aunque estos no soportan el control de secuenciación más agradable que tiene "Rebase [-i]`".
damian comentarios y nos advierte:
En la forma "A..B
", **
Adebe ser mayor que
B`.
Si están en el orden incorrecto el comando fallará silenciosamente**.
Si quieres escoger el rango B
hasta D
(inclusive) sería B^..D
.
Ver "¿Git crear rama a partir de un rango de commits anteriores?" como ilustración.
Como menciona Jubobs en los comentarios:
Esto asume que
B
no es un commit raíz; usted' obtendrá un "revisión desconocida`" error de lo contrario.
Nota: a partir de Git 2.9.x/2.10 (Q3 2016), se puede hacer cherry-pick de un rango de commit directamente en una rama huérfana (cabeza vacía): ver "Cómo hacer que una rama existente sea huérfana en git".
Respuesta original (enero de 2010)
Una rebase --onto
sería mejor, donde se reproduce el rango de commit dado sobre su rama de integración, como Charles Bailey describió aquí.
(también, busca "Así es como se trasplanta una rama temática basada en una rama a otra" en la página man de git rebase, para ver un ejemplo práctico de git rebase --onto
)
Si tu rama actual es la de integración:
`lang-all: lang-sh -->
# 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 lo que hay entre ellos:
first_SHA-1_of_working_branch_range
(de ahí el ~1
): la primera confirmación que quieres reproducir" (que apunta a la última confirmación que desea reproducir, de la rama
working`)hasta "Tmp" (que apunta a donde apuntaba antes
integración`)
Si hay algún conflicto cuando se reproduce uno de esos commits
" (y volver a poner la rama
integraciónen la rama
tmp`)Después de ese rebase --onto
, integration
volverá a estar en el último commit de la rama de integración (es decir "tmp
" rama + todos los commits reproducidos)
Con cherry-picking o rebase --onto
, no olvide que tiene consecuencias en las fusiones posteriores, como se describe aquí.
Una solución pura de cherry-pick
" es discutida aquí, e implicaría algo como:
Si desea utilizar un enfoque de parches entonces "git format-patch|git am" y "git cherry" son sus opciones.
Actualmente,git cherry-pick
sólo acepta un único commit, pero si quieres escoger el rango deB
aD
eso seríaB^..D
en la jerga de git, así que
git rev-list --reverse --topo-order B^..D | while read rev
do
git cherry-pick $rev || break
done
Pero de todos modos, cuando necesites "repetir" un rango de confirmaciones, la palabra "repetir" debería empujarte a usar la función "rebase" de Git.
¿Estás seguro de que no quieres fusionar las ramas? Si la rama de trabajo tiene algunos commits recientes que no quieres, puedes simplemente crear una nueva rama con un HEAD en el punto que quieras.
Ahora bien, si realmente quieres seleccionar un rango de commits, por la razón que sea, una forma elegante de hacerlo es simplemente tirar de un conjunto de parches y aplicarlo a tu nueva rama de integración:
git format-patch A..B
git checkout integration
git am *.patch
Esto es esencialmente lo que git-rebase hace de todos modos, pero sin la necesidad de jugar. Puedes añadir --3way
a git-am
si necesitas fusionar. Asegúrate de que no hay otros archivos *.patch ya en el directorio donde haces esto, si sigues las instrucciones al pie de la letra...
He envuelto el código de VonC's en un corto script bash, git-multi-cherry-pick
, para facilitar su ejecución:
#!/bin/bash
if [ -z $1 ]; then
echo "Equivalent to running git-cherry-pick on each of the commits in the range specified.";
echo "";
echo "Usage: $0 start^..end";
echo "";
exit 1;
fi
git rev-list --reverse --topo-order $1 | while read rev
do
git cherry-pick $rev || break
done
Actualmente estoy usando esto mientras reconstruyo la historia de un proyecto que tenía código de terceros y personalizaciones mezcladas en el mismo trunk de svn. Ahora estoy separando el código de terceros, los módulos de terceros y las personalizaciones en sus propias ramas git para una mejor comprensión de las personalizaciones en el futuro. El git-cherry-pick
es útil en esta situación ya que tengo dos árboles en el mismo repositorio, pero sin un ancestro compartido.