git - español - ¿Cómo elegir una serie de compromisos y fusionarlos en otra rama?
git push (7)
¿Estás seguro de que no quieres fusionar las ramas? Si la rama de trabajo tiene algunas confirmaciones recientes que no desea, puede crear una nueva rama con una CABEZA en el punto que desee.
Ahora, si realmente desea elegir una serie de compromisos, por cualquier razón, una forma elegante de hacerlo es simplemente tirar de 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 está haciendo git-rebase de todos modos, pero sin la necesidad de jugar juegos. Puede agregar --3way
a git-am
si necesita fusionar. Asegúrese de que no haya otros archivos * .patch en el directorio donde hace esto, si sigue las instrucciones literalmente ...
Tengo el siguiente diseño de repositorio:
- rama maestra (producción)
- integración
- trabajando
Lo que quiero lograr es elegir una serie de confirmaciones de la rama de trabajo y fusionarla en la rama de integración. Soy bastante nuevo en git y no puedo encontrar la manera exacta de hacer esto (la selección exacta de los rangos de confirmación en una operación, no la fusión) sin desordenar el repositorio. ¿Alguna sugerencia o pensamiento sobre esto? ¡Gracias!
A partir de git v1.7.2, cherry pick puede aceptar un rango de confirmaciones:
git cherry-pick
aprendió a elegir un rango de confirmaciones (por ejemplo,cherry-pick A..B
ycherry-pick --stdin
), así tambiéngit revert
; Sin embargo, estos no admiten el mejor control de secuenciación que harebase [-i]
.
Cuando se trata de una gama de compromisos, la selección de cereza es no era practico
Como mencionó Keith Kim a continuación , Git 1.7.2+ introdujo la capacidad de seleccionar una serie de confirmaciones (pero aún debe ser consciente de las consecuencias de la selección de cerebros para una combinación futura )
git cherry-pick "aprendió a elegir una serie de compromisos
(por ejemplo, "cherry-pick A..B
" y "cherry-pick --stdin
"), también lo hizo "git revert
"; Sin embargo, estos no admiten el mejor control de secuenciación que tiene "rebase [-i]
".
damian comments y nos advierte:
En la forma "
cherry-pick A..B
",A
debe ser mayor queB
Si son una orden incorrecta, el comando fallará silenciosamente .
Si desea seleccionar el rango de B
a D
(inclusive) , sería B^..D
Consulte " Git crear rama del rango de confirmaciones anteriores? " Como ilustración.
Como Jubobs menciona en los comentarios :
Esto supone que
B
no es una confirmación de raíz; obtendrá un error de "unknown revision
" de lo contrario.
Nota: a partir de Git 2.9.x / 2.10 (Q3 2016), puede seleccionar un rango de confirmación 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 2010)
Una rebase --onto
sería mejor, donde rebase --onto
el rango dado de confirmación en la parte superior de su rama de integración, como Charles Bailey describió aquí .
(También, busque "Aquí está cómo trasplantar una rama temática de una rama a otra" en la página de manual de git rebase , para ver un ejemplo práctico de git rebase --onto
)
Si su rama 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 lo volverá a 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 la "
integration
" (que apunta a la última confirmación que desea reproducir, desde la rama deworking
)
a " tmp
" (que apunta a donde apuntaba la integration
anteriormente)
Si hay algún conflicto cuando se repite una de esas confirmaciones:
-
git rebase --continue
y ejecuta "git rebase --continue
". - o salta este parche, y ejecuta "
git rebase --skip
" - o cancele todo con un "
git rebase --abort
" (y vuelva a colocar la rama deintegration
en la ramatmp
)
Después de esa rebase --onto
, la integration
volverá a la última confirmación de la rama de integración (es decir, la rama " tmp
" + todas las confirmaciones repetidas)
Con cherry picking o rebase --onto
, no olvide que tiene consecuencias en las siguientes combinaciones, como se describe aquí .
Una solución pura de " cherry-pick
" se discute aquí , e involucraría algo como:
Si desea utilizar un enfoque de parche, "git format-patch | git am" y "git cherry" son sus opciones.
Actualmente,git cherry-pick
solo acepta un solo compromiso, pero si desea seleccionar el rangoB
aD
seríaB^..D
en git lingo, entonces
git rev-list --reverse --topo-order B^..D | while read rev
do
git cherry-pick $rev || break
done
Pero de todos modos, cuando necesite "repetir" un rango de confirmaciones, la palabra "repetición" debería empujarlo a usar la función " rebase
" de Git.
Otra opción podría ser fusionarse con la estrategia nuestra para el compromiso antes del rango y luego una combinación ''normal'' con el último compromiso de ese rango (o rama cuando sea el último). Así que supongamos que solo 2345 y 3456 confirmaciones del maestro se fusionen en la rama de características:
master: 1234 2345 3456 4567
en la rama característica:
git merge -s ours 4567 git merge 2345
Supongamos que tienes 2 ramas,
"branchA": incluye las confirmaciones que desea copiar (de "commitA" a "commitB"
"branchB": la sucursal a la que desea que se transfieran las confirmaciones desde "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 tenga un conflicto, resuélvalo y escriba
git cherry-pick --continue
para continuar con el proceso de selección de cerezas.
Todas las opciones anteriores le pedirán que resuelva los conflictos de combinación. Si está fusionando los cambios confirmados para un equipo, es difícil resolver los conflictos de combinación de los desarrolladores y continuar. Sin embargo, "git merge" hará la fusión en un solo disparo, pero no puede pasar un rango de revisiones como argumento. tenemos que usar los comandos "git diff" y "git apply" para hacer el rango de combinación de revoluciones. He observado que "git apply" fallará si el archivo de parche tiene diff para demasiados archivos, así que tenemos que crear un parche por archivo y luego aplicar. Tenga en cuenta que la secuencia de comandos no podrá eliminar los archivos que se eliminan en la rama de origen. Este es un caso raro, puede eliminar manualmente dichos archivos de la rama de destino. El estado de salida de "git apply" no es cero si no puede aplicar el parche, sin embargo, si usa la opción -3way, volverá a la combinación de 3 vías y no tendrá que preocuparse por este fallo.
A continuación se muestra el guión.
enter code here
#!/bin/bash
# This script will merge the diff between two git revisions to checked out branch
# Make sure to cd to git source area and checkout the target branch
# Make sure that checked out branch is clean run "git reset --hard HEAD"
START=$1
END=$2
echo Start version: $START
echo End version: $END
mkdir -p ~/temp
echo > /tmp/status
#get files
git --no-pager diff --name-only ${START}..${END} > ~/temp/files
echo > ~/temp/error.log
# merge every file
for file in `cat ~/temp/files`
do
git --no-pager diff --binary ${START}..${END} $file > ~/temp/git-diff
if [ $? -ne 0 ]
then
# Diff usually fail if the file got deleted
echo Skipping the merge: git diff command failed for $file >> ~/temp/error.log
echo Skipping the merge: git diff command failed for $file
echo "STATUS: FAILED $file" >> /tmp/status
echo "STATUS: FAILED $file"
# skip the merge for this file and continue the merge for others
rm -f ~/temp/git-diff
continue
fi
git apply --ignore-space-change --ignore-whitespace --3way --allow-binary-replacement ~/temp/git-diff
if [ $? -ne 0 ]
then
# apply failed, but it will fall back to 3-way merge, you can ignore this failure
echo "git apply command filed for $file"
fi
echo
STATUS=`git status -s $file`
if [ ! "$STATUS" ]
then
# status is null if the merged diffs are already present in the target file
echo "STATUS:NOT_MERGED $file"
echo "STATUS: NOT_MERGED $file$" >> /tmp/status
else
# 3 way merge is successful
echo STATUS: $STATUS
echo "STATUS: $STATUS" >> /tmp/status
fi
done
echo GIT merge failed for below listed files
cat ~/temp/error.log
echo "Git merge status per file is available in /tmp/status"
Envolví el código de VonC en un breve script de bash, git-multi-cherry-pick
, para facilitar la 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 el historial de un proyecto que tenía tanto el código de terceros como las personalizaciones combinadas en el mismo tronco svn. Ahora estoy dividiendo el código principal de terceros, los módulos de terceros y las personalizaciones en sus propias sucursales de git para una mejor comprensión de las personalizaciones en el futuro. git-cherry-pick
es útil en esta situación ya que tengo dos árboles en el mismo repositorio, pero sin un antepasado compartido.