¿Cómo elegir un rango de confirmaciones y fusionarse en otra rama?


640

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 con la rama de integración. Soy bastante nuevo en git y no puedo entender cómo hacer exactamente esto (la elección de los rangos de confirmación en una operación, no la fusión) sin alterar el repositorio. ¿Alguna sugerencia o pensamiento sobre esto? ¡Gracias!


Respuestas:


808

Cuando se trata de una variedad de compromisos, selección de cerezas es No fue práctico.

Como se menciona a continuación por Keith Kim , Git 1.7.2+ introdujo la capacidad de seleccionar una variedad de confirmaciones (pero aún debe ser consciente de las consecuencias de la selección de cerezas para una futura fusión )

git cherry-pick "aprendió a elegir un rango de confirmaciones
(por ejemplo," cherry-pick A..B"y" cherry-pick --stdin"), también lo hizo" git revert"; sin embargo , estas no admiten el mejor control de secuencia" rebase [-i]".

Damian comenta y nos advierte:

En la forma " cherry-pick A..B", Adebe ser anterior aB .
Si están en el orden incorrecto, el comando fallará en silencio .

Si desea elegir el rango a Btravés D(inclusive) , sería B^..D.
Consulte " Git create branch from range of previous commits " como ilustración.

Como Jubobs menciona en los comentarios :

Esto supone que Bno es una confirmación de raíz; obtendrá un " unknown revision" error de lo contrario.

Nota: a partir de Git 2.9.x / 2.10 (Q3 2016), puede elegir 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 de 2010)

A rebase --ontosería mejor, donde reproduzca el rango de compromiso dado en la parte superior de su rama de integración, como Charles Bailey describió aquí .
(también, busque "Aquí es cómo trasplantaría una rama de tema 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 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 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 señala hacia dónde integrationapuntaba antes)

Si hay algún conflicto cuando se reproduce uno de esos commits:

  • resuélvelo y ejecuta " git rebase --continue".
  • u omita este parche y, en su lugar, ejecute " git rebase --skip"
  • o cancele todo con un " git rebase --abort" (y vuelva a colocar la integrationrama en la tmprama)

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

Con la selección de cerezas o rebase --onto, no olvides que tiene consecuencias en las fusiones posteriores, como se describe aquí .


Aquícherry-pick se discute una " " solución pura , e implicaría algo como:

Si desea utilizar un enfoque de parche, entonces "git format-patch | git am" y "git cherry" son sus opciones.
Actualmente, git cherry-picksólo acepta un único cometido, pero si usted quiere recoger el rango Ba través de Deso sería B^..Den la jerga de Git, por lo

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

Pero de todos modos, cuando necesite "reproducir" una serie de confirmaciones, la palabra "reproducir" debería empujarlo a usar la función " rebase" de Git.


1
Si tiene compromisos que tienen padres que requieren la -mopción, ¿cómo maneja esos compromisos? ¿O hay una manera de filtrar estos commits?
agosto

-mSe supone que @aug los manejará por usted, seleccionando la línea principal a la que hace referencia el -mparámetro que ha elegido para esta selección de cerezas.
VonC

La cuestión es que si está seleccionando un rango de confirmaciones, seleccionará correctamente las confirmaciones principales, pero luego, cuando alcanza una confirmación normal, falla y dice que la confirmación no es una fusión. Supongo que mi pregunta está mejor formulada: ¿cómo hacer que pase la -mopción solo cuando llegue a una confirmación principal cuando se selecciona una variedad de confirmaciones? En este momento, si paso -mcomo git cherry-pick a87afaaf..asfa789 -m 1se aplica a todos los commits dentro del rango.
agosto

@aug Strange, no reproduje el problema. ¿Cuál es su versión de git y cuál es el mensaje de error exacto que ve?
VonC

Ah, estoy ejecutando git versión 2.6.4 (Apple Git-63). El error que veo sería algo así como error: Commit 8fcaf3b61823c14674c841ea88c6067dfda3af48 is a merge but no -m option was given.que en realidad se dio cuenta de que sólo pudiera git cherry-pick --continuey que estaría bien (pero no incluiría los padres se comprometan)
agosto

136

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

git cherry-pickaprendió a elegir una serie de confirmaciones (por ejemplo, cherry-pick A..By cherry-pick --stdin), también lo hizo git revert; sin rebase [-i]embargo, estos no son compatibles con el mejor control de secuenciación .


103
Tenga en cuenta que cherry-pick A..Bno obtendrá el commit A (lo necesitaría A~1..Bpara eso), y si hay algún conflicto, git no continuará automáticamente como lo hace rebase (al menos a partir del 1.7.3.1)
Gabe Moothart

3
También es bueno tener en cuenta que git cherry-pick A..B Cno funciona como cabría esperar, ingenuamente. ¡No elegirá todo en el rango A..By se comprometerá C! Para hacer esto, debe dividirse en dos líneas, primero git cherry-pick A..By luego git cherry-pick C. Por lo tanto, siempre que tenga un rango, debe ejecutarlo por separado.
MicroVirus

42

Suponga que tiene 2 ramas,

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

"branchB": la rama a la que desea que se transfieran los commits 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 el proceso de selección de cerezas.


Funcionó como encanto! ¡Muchas gracias!
snukone

28

¿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 un HEAD en el punto que desee.

Ahora, si realmente desea elegir una variedad de confirmaciones, por cualquier razón, una forma elegante de hacerlo es simplemente extraer un parche 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 git-rebase está haciendo de todos modos, pero sin la necesidad de jugar. Puede agregar --3waya git-amsi necesita fusionar. Asegúrese de que no haya otros archivos * .patch ya en el directorio donde hace esto, si sigue las instrucciones al pie de la letra ...


1
Tenga en cuenta que, al igual que con otros rangos de revisión, debe ser A^incluido A.
Gino Mempin

9

Envolví código de VonC en una escritura del golpe corto, git-multi-cherry-pickpara facilidad de movimiento:

#!/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 mezcladas en el mismo tronco svn. Ahora estoy dividiendo 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. git-cherry-pickEs útil en esta situación ya que tengo dos árboles en el mismo repositorio, pero sin un antepasado compartido.


3

Todas las opciones anteriores le pedirán que resuelva los 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 sola vez, pero no puede pasar un rango de revisiones como argumento. tenemos que usar los comandos "git diff" y "git apply" para hacer la combinación de rango de revoluciones. He observado que "git apply" fallará si el archivo de parche tiene diferencias para demasiados archivos, por lo que tenemos que crear un parche por archivo y luego aplicarlo. Tenga en cuenta que el script 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,

Debajo está 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"

2

Lo probé hace unos días, después de leer la explicación muy clara de Vonc.

Mis pasos

comienzo

  • Rama dev : ABCDEFGHIJ
  • Rama target : ABCD
  • No quiero EniH

Pasos para copiar características sin los pasos E y H en la rama dev_feature_wo_E_H

  • git checkout dev
  • git checkout -b dev_feature_wo_E_H
  • git rebase --interactive --rebase-merges --no-ff Ddonde puse dropdelante EyH en el editor de rebase
  • resolver conflictos, continuar y commit

Pasos para copiar la rama dev_feature_wo_E_Hen el destino.

  • git checkout target
  • git merge --no-ff --no-commit dev_feature_wo_E_H
  • resolver conflictos, continuar y commit

Algunas observaciones

  • Lo he hecho por mucho cherry-picken los días anteriores
  • git cherry-pick es potente y simple pero

    • crea confirmaciones duplicadas
    • y cuando quiero mergetengo que resolver los conflictos de las confirmaciones iniciales y las confirmaciones duplicadas, por lo que para una o dos cherry-pick, está bien "elegir", pero para más es demasiado detallado y la rama se volverá demasiado compleja
  • En mi opinión, los pasos que he hecho son más claros que git rebase --onto

1
Buen post. Votado Me recuerda a stackoverflow.com/a/38418941/6309 . Recapté los inconvenientes de la recolección de cerezas en 2012: stackoverflow.com/a/13524494/6309 .
VonC

1

Otra opción podría ser fusionar con la estrategia nuestra con el commit antes del rango y luego una fusión 'normal' con el último commit de ese rango (o bifurcar cuando es el último). Supongamos que solo 2345 y 3456 confirmaciones de master se fusionan en la rama de características:

Maestro:
1234
2345
3456
4567

en rama de características:

git merge -s our 4567
git merge 2345
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.