Cómo seleccionar múltiples confirmaciones

875

Tengo dos ramas Commit aes la cabeza de uno, mientras que el otro tiene b, c, d, ey fen la parte superior de a. Quiero mover c, d, ey fhasta la primera rama sin comprometerse b. Usar Cherry Pick es fácil: verifique la primera rama Cherry-Pick una por una y vuelva ca fcolocar la segunda rama en la primera. Pero, ¿hay alguna manera de elegir todo c, fcon un solo comando?

Aquí hay una descripción visual del escenario (gracias JJD ):

ingrese la descripción de la imagen aquí

tig
fuente
3
el rebase que mencionas no es realmente relevante para la pregunta, ¿verdad? (Entiendo que es posible que desee bbasarse fmás adelante, pero eso no tiene nada que ver con la recolección de cerezas).
Superole

Respuestas:

1282

Git 1.7.2 introdujo la capacidad de elegir una variedad de confirmaciones. De las notas de la versión :

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 .

Para seleccionar todos los commits de commit Aa commit B(donde Aes anterior a B), ejecute:

git cherry-pick A^..B

Si quieres ignorar A, ejecuta:

git cherry-pick A..B

(El crédito es para Damian, JB Rainsberger y sschaef en los comentarios)

Eric Darchis
fuente
249
En la forma "cherry-pick A..B", A debería ser anterior a B. Si el orden es incorrecto, el comando fallará en silencio.
damian
294
Además, esto no cereza-Pick A, sino más bien todo después de que A hasta e incluyendo B.
JB Rainsberger
455
Para incluir un tipo A justogit cherry-pick A^..B
kiritsuku
17
Si tiene git 1.7.1 o anterior y no puede actualizar, puede seleccionarlos rápidamente en orden ejecutando git cherry-pick f~3, git cherry-pick f~2etc., hasta git cherry-pick f(presionar la flecha hacia arriba obtiene el comando anterior para que pueda cambiar rápidamente el número y ejecutar debería ser similar en la mayoría de las consolas).
David Mason
19
Puede ser bueno saber que esta sintaxis también funciona con nombres de rama. git cherry-pick master..somebranchelegirá todos los commits en somebranch desde master (suponiendo que ya se haya basado en master), y los aplicará a su rama actual.
Tor Klingberg
103

La forma más sencilla de hacerlo es con la ontoopción de rebase. Supongamos que la rama que acaba actuales en aque se llama MyBranch y esta es la rama que desea mover c- fa.

# checkout mybranch
git checkout mybranch

# reset it to f (currently includes a)
git reset --hard f

# rebase every commit after b and transplant it onto a
git rebase --onto a b
CB Bailey
fuente
1
¡Gracias! ¿Podría agregar también git checkout secondbranch && git rebase mybranchpara una respuesta completa
Tig
1
Esta respuesta me ayudó mucho a entender qué compromiso es cuál en este escenario. Y: también puede usar rebaseel modo interactivo. Gracias, @Charles!
Oliver
1
La belleza de este enfoque es que puede usar --interactivepara eliminar algunos commits de la secuencia o reordenarlos antes de la "selección de cereza". +1
Michael Merickel
Este es un comando genial, un poco complicado de entender, pero funciona de maravilla.
Valerio
Es una locura que tengas que dar el compromiso de que no quieres rebase como uno de los argumentos ( ben este ejemplo) pero sí, esto me ha funcionado.
asontu
85

O el one-liner solicitado:

git rebase --onto a b f
Wolfc
fuente
55
Aunque solo sea por su brevedad, esta es la mejor respuesta.
Nate Chandler
99
Votó a favor pero lo dejará en estado HEAD separado si f es una confirmación (en lugar de una rama): debe editar para agregar que se debe pagar una rama como en la respuesta a continuación
Mr_and_Mrs_D
68

Puede usar una combinación en serie de git rebasey git branchpara aplicar un grupo de confirmaciones en otra rama. Como ya lo publicó wolfc, el primer comando copia los commits. Sin embargo, el cambio no es visible hasta que agregue un nombre de rama a la parte superior del grupo.

Abra la imagen en una nueva pestaña ...

Flujo de trabajo

Para resumir los comandos en forma de texto:

  1. Abrir gitk como un proceso independiente con el comando: gitk --all &.
  2. Ejecutar git rebase --onto a b f.
  3. Presione F5en gitk . Nada cambia. Pero no HEADestá marcado.
  4. correr git branch selection
  5. Presione F5en gitk . Aparece la nueva rama con sus commits.

Esto debería aclarar las cosas:

  • Commit aes el nuevo destino raíz del grupo.
  • Commit bes el commit antes del primer commit del grupo (exclusivo).
  • Commit fes el último commit del grupo (inclusive).

Luego, puede usar git checkout feature && git reset --hard bpara eliminar las confirmaciones chasta fde la featurerama.

Además de esta respuesta, escribí una publicación de blog que describe los comandos en otro escenario que debería ayudar a usarlo en general.

JJD
fuente
2
Si ya no se necesita mybranch (a..f commits), esto se puede simplificar a: git rebase --onto a b mybranchy por cierto, ¿qué programa hacen esas ingeniosas imágenes git?
Mr_and_Mrs_D
2
@ Mr_and_Mrs_D Gracias por tu comentario. Creo que usé cacoo.com para dibujar las imágenes.
JJD
48

Para aplicar los comentarios de JB Rainsberger y sschaef para responder específicamente a la pregunta ... Para usar un rango de selección de cereza en este ejemplo:

git checkout a
git cherry-pick b..f

o

git checkout a
git cherry-pick c^..f
Andy
fuente
3
Lo uso git 2.7.0.windows.1y noté que cuando trato de elegir el rango de confirmaciones, todo está bien, pero git no te dice en ningún lado lo que tienes que hacer git cherry-pick --continue | --abort | --quitantes de intentar comprometer / seleccionar de nuevo. Entonces, si elige un rango de confirmaciones, deberá ejecutar git cherry-pick --continuecada vez que esté listo (resolviendo conflictos o similares) con una confirmación del rango dado.
kuskmen
Hice exactamente lo mismo, pero fatal: no puedo encontrar 'a..b'
Amit Karnik
No sé dónde me equivoco, pero cuando hago 'git cherry-pick c ^ .. f' de mi lado, esto incluye el commit f pero no el commit c. Pero como leo en todas partes, se supone que define a c y f como inclusivas. ¿O estoy equivocado?
Samuel
@ Samuel, sí, eso es correcto. El ^después de la c en realidad significa "el commit antes de c" que es b en este caso. Por eso c^..fes sinónimo de b..f. Intenta hacerlo git log c^..fy deberías ver los commits de c a f, exactamente igual que si lo hicierasgit log b..f
Andy
41

Si tiene revisiones selectivas para fusionar, diga A, C, F, J de A, B, C, D, E, F, G, H, I, J commits, simplemente use el siguiente comando:

git cherry-pick ACFJ

Shrikant Wandhare
fuente
1
agradable y simple
spinup
21
git rev-list --reverse b..f | xargs -n 1 git cherry-pick
Dustin
fuente
2
Funciona perfecto si no hay conflictos, de lo contrario "rebase" podría ser más fácil ya que no tendrá que averiguar dónde se ha detenido y volver a aplicar el resto de los parches.
Ruslan Kabalin
66
Agregue comentarios que expliquen lo que está haciendo esto
Mr_and_Mrs_D
77
dado que nadie explicó ... git rev-list imprime todas las revisiones de la rama b a f (invertida) de modo que cuando se pasa cada línea (el hash de confirmación) en orden, seleccionará cada una de ellas en el git HEAD actual. es decirgit cherry-pick {hash of c}; git cherry-pick {hash of d}; ...
coderatchet
8

Para elegir desde una identificación de confirmación hasta la punta de la rama, puede usar:

git cherry-pick commit_id^..branch_name

ut9081
fuente
Esto ya es parte de la respuesta stackoverflow.com/a/31640427/96823
tig
1
Esta respuesta es realmente diferente y fue útil para mí. Especifica el nombre de la rama, no el SHA de confirmación final.
Subtletree
7

Otra variante que vale la pena mencionar es que si desea las últimas nconfirmaciones de una rama, la ~sintaxis puede ser útil:

git cherry-pick some-branch~4..some-branch

En este caso, el comando anterior seleccionaría los últimos 4 commits de una rama llamada some-branch(aunque también podría usar un hash de commit en lugar del nombre de una rama)

Nick F
fuente
1
Muy útil respuesta, gracias! :)
Jacek Dziurdzikowski
3

En realidad, la forma más sencilla de hacerlo podría ser:

  1. registre la base de fusión entre las dos ramas: MERGE_BASE=$(git merge-base branch-a branch-b)
  2. adelantar o cambiar la base de la rama anterior a la nueva
  3. vuelva a basar la rama resultante en sí misma, comenzando en la base de fusión del paso 1, y elimine manualmente las confirmaciones que no desea:

    git rebase ${SAVED_MERGE_BASE} -i
    

    Alternativamente, si solo hay unas pocas confirmaciones nuevas, omita el paso 1 y simplemente use

    git rebase HEAD^^^^^^^ -i
    

    en el primer paso, usar lo suficiente ^para pasar la base de fusión.

Verá algo como esto en el rebase interactivo:

pick 3139276 commit a
pick c1b421d commit b
pick 7204ee5 commit c
pick 6ae9419 commit d
pick 0152077 commit e
pick 2656623 commit f

Luego elimine las líneas b (y cualquier otra que desee)

ealfonso
fuente
1
git format-patch --full-index --binary --stdout range... | git am -3
Roger Wang
fuente
42
Agregue comentarios que expliquen lo que está haciendo esto
Mr_and_Mrs_D
0

Aquí hay una secuencia de comandos que le permitirá seleccionar varias confirmaciones en una fila simplemente diciéndole a la secuencia de comandos qué ramas de origen y destino para las selecciones de cereza y el número de confirmaciones:

https://gist.github.com/nickboldt/99ac1dc4eb4c9ff003a1effef2eb2d81

Para seleccionar de la rama a la maestra (usa la rama actual como fuente):

./gcpl.sh -m

Para elegir los últimos 5 commits de tu rama 6.19.x para masterizar:

./gcpl.sh -c 5 -s 6.19.x -t master
Nickboldt
fuente
¿Por qué se necesita este script cuando se puede hacer eso con Git directamente ...?
code_dredd
Menos tipeo, y mi script selecciona automáticamente los commits para usted, por lo que no tiene que elegir cada uno por su SHA. De todos modos, YMMV.
nickboldt