git cherry-pick dice "... 38c74d es una fusión pero no se le dio la opción -m"

519

Hice algunos cambios en mi rama maestra y quiero llevarlos hacia arriba. cuando selecciono las siguientes confirmaciones, sin embargo, me atoro en fd9f578 donde git dice:

$ git cherry-pick fd9f578
fatal: Commit fd9f57850f6b94b7906e5bbe51a0d75bf638c74d is a merge but no -m option was given.

¿Qué está tratando de decirme git? ¿Es la elección correcta para usar aquí? La rama maestra incluye cambios en los archivos que se han modificado en la rama ascendente, por lo que estoy seguro de que habrá algunos conflictos de fusión, pero esos no son demasiado malos para solucionarlos. Sé qué cambios son necesarios y dónde.

Estas son las confirmaciones que quiero presentar.

e7d4cff added some comments...
23e6d2a moved static strings...
44cc65a incorporated test ...
40b83d5 whoops delete whitspace...
24f8a50 implemented global.c...
43651c3 cleaned up ...
068b2fe cleaned up version.c ...
fd9f578 Merge branch 'master' of ssh://extgit/git/sessions_common
4172caa cleaned up comments in sessions.c ...
wufoo
fuente

Respuestas:

607

La forma en que funciona una selección de cereza es tomando el diff que representa un conjunto de cambios (la diferencia entre el árbol de trabajo en ese punto y el árbol de trabajo de su padre) y aplicándolo a su rama actual.

Entonces, si un commit tiene dos o más padres, también representa dos o más diferencias: ¿cuál debe aplicarse?

Estás tratando de elegir fd9f578, lo que fue una fusión con dos padres. Por lo tanto, debe decirle al comando cherry-pick cuál contra cuál debe calcularse la diferencia, utilizando la -mopción Por ejemplo, git cherry-pick -m 1 fd9f578para usar el padre 1 como base.

No puedo decir con certeza su situación particular, pero generalmente es aconsejable usar en git mergelugar de git cherry-pick. Cuando selecciona una confirmación de fusión, contrae todos los cambios realizados en el elemento primario que no especificó -men esa confirmación . Pierdes toda su historia y engullen todas sus diferencias. Tu llamada.

Borealid
fuente
3
@wufoo Probablemente también deberías aprender git rebase: es como una fusión, pero en lugar de integrar dos ramas, trasplanta una para sentarse encima de la otra.
Borealid
92
¿Cómo sabes el número de padre?
Anentropic
66
@Anentropic 1 es el "primer padre", 2 es el "segundo padre", y así sucesivamente. El orden es aquel en el que se enumeran en la confirmación (según lo visto git showy similares).
Borealid
2
@lkraav También podría haber hecho una git reset --hard HEAD@{1}para recuperar su confirmación faltante. git resetno se limita a moverse "hacia atrás" en la historia. git checkout -b mybranch HEAD@{1}También funcionaría.
Borealid
44
ADVERTENCIA: git mergepuede tener consecuencias no deseadas. Ese comando agregará todas las otras confirmaciones (anteriores) que existen en la rama principal. Por lo general, las personas optan por elegir porque no quieren los otros compromisos. ¡Asegúrese de verificar que solo está implementando los cambios que desea!
Kay V
52

-m significa el número padre.

Del git doc:

Por lo general, no puede elegir una fusión porque no sabe qué lado de la fusión debe considerarse la línea principal. Esta opción especifica el número principal (a partir de 1) de la línea principal y permite que cherry-pick reproduzca el cambio relativo al elemento primario especificado.

Por ejemplo, si su árbol de confirmación es el siguiente:

- A - D - E - F -   master
   \     /
    B - C           branch one

entonces git cherry-pick Eproducirá el problema que enfrentó.

git cherry-pick E -m 1significa usar D-E, mientras que git cherry-pick E -m 2significa usar B-C-E.

gavincook
fuente
32

La respuesta de @ Borealid es correcta, pero suponga que no le importa preservar el historial exacto de fusión de una rama y solo quiere elegir una versión linealizada. Aquí hay una manera fácil y segura de hacerlo:

Estado de inicio: está en la sucursal Xy desea seleccionar los commits Y..Z.

  1. git checkout -b tempZ Z
  2. git rebase Y
  3. git checkout -b newX X
  4. git cherry-pick Y..tempZ
  5. (Opcional) git branch -D tempZ

Lo que esto hace es crear una rama tempZbasada en Z, pero con el historial Ylinealizado en adelante, y luego seleccionarlo en una copia de Xllamado newX. (Es más seguro hacer esto en una nueva rama en lugar de mutar X). Por supuesto, puede haber conflictos en el paso 4, que tendrá que resolver de la manera habitual ( cherry-pickfunciona de manera muy similar rebasea ese respecto). Finalmente borra lo temporaltempZ rama .

Si el paso 2 muestra el mensaje "La rama actual tempZ está actualizada", entonces Y..Z ya era lineal, así que simplemente ignore ese mensaje y continúe con los pasos 3 en adelante.

Luego revise newX y vea si eso hizo lo que quería.

(Nota: esto no es lo mismo que un simple git rebase Xcuando está en la rama Z, porque no depende de ninguna manera de la relación entre Xy Y; puede haber compromisos entre el antepasado común y el Yque no desea).

Daira Hopwood
fuente
1
git rebase YdiceCurrent branch tempZ is up to date
Basilevs
Creo que eso significa que Y..Zya era lineal. Entonces puede ignorar ese mensaje y continuar con los pasos 3 y 4.
Daira Hopwood
1
Idea interesante, tuve que dibujarlo en papel para apreciar completamente lo que estaba pasando = D
Chris
2
Brillante. git cherry-pick para un rango completo se quejó de que faltaba la opción -m o de que se había proporcionado. Tu solución fue dorada. (Una sugerencia: eliminar tempZ branch después)
Otheus
1
¡esto es increíble! He estado luchando con la selección de cereza y solo esto tenía sentido. Tuve algunos problemas con las letras elegidas (algunas ramas y commits son mayúsculas y algunas ramas son minúsculas)
pcarvalho
19

Simplificar. Cherry-recoger los commits. No escojas la combinación.

Aquí hay una reescritura de la respuesta aceptada que idealmente aclara las ventajas / riesgos de los posibles enfoques:

Estás tratando de elegir fd9f578, que fue una fusión con dos padres.

En lugar de elegir una fusión, lo más simple es elegir las confirmaciones que realmente desea de cada rama de la fusión.

Como ya se ha fusionado, es probable que todas sus confirmaciones deseadas estén en su lista. Escoge cereza directamente y no necesitas meterte con la confirmación de fusión.

explicación

La forma en que funciona una selección de cereza es tomando la diferencia que representa un conjunto de cambios (la diferencia entre el árbol de trabajo en ese punto y el árbol de trabajo de su padre) y aplicando el conjunto de cambios a su rama actual.

Si un commit tiene dos o más padres, como es el caso de una fusión, ese commit también representa dos o más diferencias. El error ocurre debido a la incertidumbre sobre la cual debería aplicarse diff.

alternativas

Si determina que necesita incluir la combinación vs selección de cereza de las confirmaciones relacionadas, tiene dos opciones:

  1. (Más complicado y oscuro; también descarta el historial) puede indicar qué padre debe aplicar.

    • Use la -mopción para hacerlo. Por ejemplo, git cherry-pick -m 1 fd9f578usará el primer padre que aparece en la fusión como base.

    • También tenga en cuenta que cuando selecciona una confirmación de fusión, contrae todos los cambios realizados en el elemento primario que no especificó -men esa confirmación . Pierdes toda su historia y engullen todas sus diferencias. Tu llamada.

  2. (Más simple y más familiar; conserva el historial) que puede usar en git mergelugar de git cherry-pick.

    • Como es habitual git merge, intentará aplicar todas las confirmaciones que existen en la rama que está fusionando, y enumerarlas individualmente en su registro de git.
Kay V
fuente
2

Simplificación del método @Daira Hopwood bueno para elegir una sola confirmación. No necesita ramas temporales.

En el caso del autor:

  • Se quiere confirmar Z (fd9f578)
  • Y es comprometerse antes
  • X rama de trabajo actual

entonces hazlo:

git checkout Z   # move HEAD to wanted commit
git reset Y      # have Z as changes in working tree
git stash        # save Z in stash
git checkout X   # return to working branch
git stash pop    # apply Z to current branch
git commit -a    # do commit
efemérides
fuente
2
Por supuesto, esto pierde los metadatos asociados con la confirmación original. Supongo que es una cuestión de opinión si es más simple. Lo uso a veces cuando quiero perder los metadatos y solo mantener los cambios generales del código. Tenga en cuenta que funciona incluso si Y no es un padre inmediato de Z (en cuyo caso los cambios serán aplastados).
Daira Hopwood el