2010-01-03 10 views
457

Tengo el siguiente esquema de repositorio:Cómo cereza recoger una serie de confirmaciones y se funden en otra rama

  • rama principal (producción)
  • integración
  • trabajo

Lo que Lo que quiero lograr es seleccionar una serie de compromisos de la rama de trabajo y fusionarla en la rama de integración. Soy bastante nuevo en git y no puedo encontrar la manera de hacerlo exactamente (la selección de cerezas de los rangos de compromiso en una operación, no la fusión) sin ensuciar el repositorio. ¿Alguna sugerencia o pensamiento sobre esto? ¡Gracias!

+0

http://www.draconianoverlord.com/2013/09/07/no-cherry-picking.html (no es mi blog) –

Respuesta

598

Cuando se trata de una serie de confirmaciones, cherry-picking esfue no es práctico.

Como mentioned below por Keith Kim, Git 1.7.2+ introdujo la posibilidad de cereza recoger una serie de confirmaciones (pero que todavía tienen que ser conscientes de la consequence of cherry-picking for future merge)

git cherry-pick" learned to pick a range of commits
(e.g. " cherry-pick A..B " and " cherry-pick --stdin "), so did " git revert "; these do not support the nicer sequencing control " rebase [-i] " has, though.

damiancomments y nos advierte:

In the " cherry-pick A..B " form, A should be older than B.
If they're the wrong order the command will silently fail.

Si usted quiere recoger el rango Btravés D (inclusive) eso sería B^..D.
Consulte "Git create branch from range of previous commits?" como una ilustración.

Como Jubobs menciones in the comments:

This assumes that B is not a root commit; you'll get an " unknown revision " error otherwise.

Nota: a partir de Git 2.9.x/2,10 (Q3 2016), se pueden seleccionar los diversos comprometerse directamente en una rama huérfano (cabeza vacía): ver "How to make existing branch an orphan in git".


Respuesta original (enero de 2010)

Un rebase --onto sería mejor, en el que reproducir la gama dada de comprometerse en la parte superior de la rama de la integración, como Charles Bailey described here.
(también, busca "Así es como se le trasplantar una rama tema sobre la base de una rama a otra" en el git rebase man page, para ver un ejemplo práctico de git rebase --onto)

Si su rama actual es la 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 se volverá a reproducir todo lo que entre:

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

a "tmp" (que apunta hacia donde integration señalaba antes)

Si existe algún conflicto cuando una de esas confirmaciones se repite:

  • o bien resolverlo y ejecutar "git rebase --continue".
  • o saltarse este parche, y en lugar de correr "git rebase --skip"
  • o cancelar el todas las cosas con un "git rebase --abort" (y volver a poner la rama integration en la rama tmp)

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

Con cherry-picking o rebase --onto, no olvide que tiene consecuencias en la subsecuencia nt se fusiona, como described here.


Una solución pura "cherry-pick" es discussed here, e implicaría algo así como:

If you want to use a patch approach then "git format-patch|git am" and "git cherry" are your options.
Currently, git cherry-pick accepts only a single commit, but if you want to pick the range B through D that would be B^..D in git lingo, so

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

Pero de todos modos, cuando se necesita "repetición" una serie de confirmaciones, la palabra " la repetición "debería empujarte a utilizar la función" rebase "de Git.

+0

Si tiene commits que tienen padres que requieren la opción '-m', ¿cómo usted maneja esos compromisos? ¿O hay una forma de filtrar estos commits? – aug

+0

@aug '-m' se supone que los maneja por usted, seleccionando la línea principal a la que hace referencia el parámetro' -m' que ha elegido para este "cherry picking". – VonC

+0

El problema es que si seleccionas un rango de confirmaciones, seleccionará las confirmaciones principales correctamente pero luego cuando haga una confirmación normal, falla y dice que la confirmación no es una fusión. Supongo que mi pregunta tiene una mejor redacción sobre cómo hacer que pase la opción '-m' solo cuando acciona una confirmación principal cuando selecciona el rango de commits. En este momento si paso '-m' como' git cherry-pick a87afaaf..asfa789 -m 1' se aplica a todas las confirmaciones dentro del rango. – aug

22

¿Está seguro de que no quiere realmente fusionar las sucursales? Si la rama de trabajo tiene algunas confirmaciones recientes que no desea, puede simplemente crear una nueva rama con una CABEZA en el punto que desee.

Ahora, si realmente quiere cereza recoger una serie de confirmaciones, por cualquier razón, una manera elegante de hacer esto es simplemente tirar de un conjunto de parches y aplicarlo a su nueva rama de la integración:

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

Esto es esencialmente lo que git-rebase está haciendo de todos modos, pero sin la necesidad de jugar juegos. Puede agregar --3way al git-am si necesita fusionarse. Asegúrese de que no haya otros archivos * .patch en el directorio donde lo hace, si sigue las instrucciones textualmente ...

7

envolví VonC's code en una escritura del golpe corto, 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 como reconstruyo la historia de un proyecto que tenían tanto el código 3 ª parte y personalizaciones mezclan juntos en el mismo tronco svn. Ahora estoy dividiendo el código central de terceros, los módulos de terceros y las personalizaciones en sus propias sucursales de git para comprender mejor 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 antecesor compartido.

112

A partir de git recogida de cereza v1.7.2 puede aceptar una serie de confirmaciones:

git cherry-pick learned to pick a range of commits (e.g. cherry-pick A..B and cherry-pick --stdin), so did git revert ; these do not support the nicer sequencing control rebase [-i] has, though.

+78

Tenga en cuenta que 'cherry-pick A..B' no obtendrá el commit A (necesitaría' A ~ 1..B' para eso), y si hay algún conflicto, git no continuará automáticamente como rebase does (al menos a partir de 1.7.3.1) –

+2

También es bueno tener en cuenta que 'git cherry-pick A..BC' no funciona como cabría esperar, ingenuamente. No recogerá todo en el rango 'A..B' y ¡cometará' C'! Para hacer esto, necesitas dividirlo en dos líneas, primero 'git cherry-pick A..B' y luego' git cherry-pick C'. Entonces, cada vez que tenga un rango, necesita ejecutarlo por separado. – MicroVirus

0

Otra opción podría ser para combinar con la nuestra estrategia para la confirmación antes de que el rango y luego un 'normal' fundirse con el último commit de ese rango (o branch cuando es el último). Así que supongamos que sólo 2.345 y 3.456 confirmaciones de maestras que se fusionaron en rama de la característica:

 
master: 
1234 
2345 
3456 
4567 

en rama de la característica:

 
git merge -s ours 4567 
git merge 2345 
2

Todas las opciones anteriores le sugerirán para resolver conflictos de fusión. Si está fusionando los cambios comprometidos para un equipo, es difícil resolver los conflictos de fusión de los desarrolladores y continuar. Sin embargo, "git merge" hará la fusión de una vez, pero no se puede pasar un rango de revisiones como argumento. tenemos que usar los comandos "git diff" y "git apply" para hacer el rango de fusión de las revoluciones. He observado que "git apply" fallará si el archivo de parche tiene diferencias para demasiados archivos, por lo que debemos crear un parche por archivo y luego aplicarlo. Tenga en cuenta que la secuencia de comandos no podrá eliminar los archivos que se eliminan en la rama fuente. 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 fusionarse en 3 vías y no tendrá que preocuparse por este error.

A continuación se muestra el guion.

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" 
1

Suponga que tiene 2 ramas,

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

"branchB": la rama desea que el se compromete a ser transferido de "branchA"

1)

git checkout <branchA> 

2) obtener los identificadores de "comm ITA" y "commitB"

3)

git checkout <branchB> 

4)

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

5) En caso de que tenga un conflicto, resolverlo y escriba

git cherry-pick --continue 

para continuar el proceso de selección de cereza.

Cuestiones relacionadas