Git cherry pick vs rebase

120

Recientemente comencé a trabajar con Git.

Al revisar el libro de Git en línea, encontré lo siguiente en la sección "Git Rebase":

Con el comando rebase, puede tomar todos los cambios que se confirmaron en una rama y reproducirlos en otra.

(Citado de: http://git-scm.com/book/en/Git-Branching-Rebasing )

Pensé que esta es la definición exacta de git cherry-pick (volver a aplicar una confirmación o un conjunto de objetos de confirmación en la rama actualmente verificada).

Cuál es la diferencia entre los dos ?

ácido lisérgico
fuente

Respuestas:

166

Desde el tiempo que git cherry-pickaprendí para poder aplicar múltiples confirmaciones, la distinción se volvió algo discutible, pero esto es algo que se puede llamar evolución convergente ;-)

La verdadera distinción radica en la intención original de crear ambas herramientas:

  • git rebaseLa tarea es reenviar una serie de cambios que un desarrollador tiene en su repositorio privado, creado contra la versión X de alguna rama ascendente, a la versión Y de esa misma rama (Y> X). Esto cambia efectivamente la base de esa serie de confirmaciones, por lo tanto, "rebase".

    (También permite al desarrollador trasplantar una serie de confirmaciones a cualquier confirmación arbitraria, pero esto tiene un uso menos obvio).

  • git cherry-pickes para llevar un compromiso interesante de una línea de desarrollo a otra. Un ejemplo clásico es el backporting de una corrección de seguridad realizada en una rama de desarrollo inestable a una rama estable (mantenimiento), donde mergeno tiene sentido, ya que traería muchos cambios no deseados.

    Desde su primera aparición, git cherry-pickha podido elegir varias confirmaciones a la vez, una por una.

Por lo tanto, posiblemente la diferencia más llamativa entre estos dos comandos es cómo tratan la rama en la que trabajan: git cherry-pickgeneralmente trae una confirmación de otro lugar y la aplica en la parte superior de su rama actual, registrando una nueva confirmación, mientras git rebasetoma su rama actual y reescribe una serie de su propia sugerencia se compromete de una forma u otra. Sí, esta es una descripción muy simplificada de lo que se git rebasepuede hacer, pero es intencional, para tratar de asimilar la idea general.

Actualice para explicar mejor un ejemplo de uso que se git rebaseestá discutiendo.

Ante esta situación,
un estado del repositorio antes de reajustar
The Book afirma:

Sin embargo, hay otra forma: puede tomar el parche del cambio que se introdujo en C3 y volver a aplicarlo sobre C4. En Git, esto se llama rebase. Con el comando rebase, puede tomar todos los cambios que se confirmaron en una rama y aplicarlos en otra.

En este ejemplo, ejecutaría lo siguiente:

$ git checkout experiment
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: added staged command

El "problema" aquí es que, en este ejemplo, la rama "experimento" (el tema de la reorganización) se bifurcó originalmente de la rama "maestra" y, por lo tanto, comparte las confirmaciones C0 a C2 con ella; de hecho, "experimento" es " master "hasta, e incluido, C2 más el compromiso C3 encima. (Este es el caso más simple posible; por supuesto, "experimento" podría contener varias docenas de confirmaciones además de su base original).

Ahora git rebasese le dice que vuelva a basar "experimento" en la punta actual de "maestro", y git rebasedice así:

  1. Se ejecuta git merge-basepara ver cuál es la última confirmación compartida por el "experimento" y el "maestro" (en otras palabras, cuál es el punto de desviación). Este es C2.
  2. Guarda todas las confirmaciones realizadas desde el punto de desvío; en nuestro ejemplo de juguete, es solo C3.
  3. Rebobina HEAD (que apunta a la confirmación de punta de "experimento" antes de que la operación comience a ejecutarse) para apuntar a la punta de "maestro"; estamos reajustando sobre ella.
  4. Intenta aplicar cada una de las confirmaciones guardadas (como si fuera con git apply) en orden. En nuestro ejemplo de juguete es solo una confirmación, C3. Digamos que su aplicación producirá un commit C3 '.
  5. Si todo salió bien, la referencia del "experimento" se actualiza para señalar la confirmación resultante de aplicar la última confirmación guardada (C3 'en nuestro caso).

Ahora volvamos a tu pregunta. Como puede ver, aquí técnicamente de git rebase hecho trasplanta una serie de confirmaciones de "experimento" a la punta de "maestro", por lo que puede decir con razón que hay "otra rama" en el proceso. Pero la esencia es que la confirmación de sugerencias de "experimento" terminó siendo la nueva confirmación de sugerencias en "experimento", simplemente cambió su base:
estado después de fusionarse

Una vez más, técnicamente se puede decir que git rebaseaquí se incorporaron ciertas confirmaciones del "maestro", y esto es absolutamente correcto.

kostix
fuente
2
Gracias. Todavía no entendí completamente lo que quieres decir aquí. En el libro, se da el ejemplo de que rebase aplica una serie de confirmaciones de sugerencias de otra rama, mientras que usted dice que es de "la misma rama". ¿O tal vez hay algunos casos de cómo funciona?
ácido lisérgico
1
Traté de explicar el asunto actualizando mi respuesta.
kostix
98

Con cherry-pick, las confirmaciones / ramas originales se mantienen y se crean nuevas confirmaciones. Con rebase, toda la rama se mueve con la rama apuntando a las confirmaciones reproducidas.

Digamos que comenzó con:

      A---B---C topic
     /
D---E---F---G master

Rebase:

$ git rebase master topic

Usted obtiene:

              A'--B'--C' topic
             /
D---E---F---G master

Selección de cereza:

$ git checkout master -b topic_new
$ git cherry-pick A^..C

Usted obtiene:

      A---B---C topic
     /
D---E---F---G master
             \
              A'--B'--C' topic_new

para obtener más información sobre git, este libro tiene la mayor parte (http://git-scm.com/book)

Kenny Ho
fuente
3
Bien respondido. También es común que desee seleccionar solo las confirmaciones A y B, pero dejar C colgando en esos casos, es posible que desee mantener la rama y solo seleccionar los cambios que los colegas pueden necesitar ver. Git está hecho para trabajar con personas, por lo que si no ve los beneficios de algo cuando trabaja solo, a menudo se usa más comúnmente cuando se trabaja en grupos más grandes.
Pablo Jomer
Si se hiciera una rebase interactiva en su lugar, dejando fuera una o más confirmaciones, ¿qué ramas tendrías al final? si solo se topicbasara en la parte superior master, no contiene las confirmaciones omitidas, entonces, ¿de qué rama serán parte?
Anthony
Solo una cosa más que quiero agregar: si usted git checkout topicy luego de git reset --hard C'la recolección de cerezas, entonces tiene el mismo resultado que después de rebase. Me salvé de muchos conflictos de fusión utilizando la selección selectiva sobre el rebase, porque el antepasado común estaba muy atrás.
sorrymissjackson
@anthony - stackoverflow.com/questions/11835948/… : por lo que tengo entendido, están perdidos. No soy git-guru pero esto rebase/ cherry-pickIS en la de todos los detalles con gitque tenía una comprensión del problema.
thoni56
1
Sus gráficos hacen más daño que bien, porque son funcionalmente idénticos. La única diferencia es la rama creada por git checkout -b, que no tiene nada que ver con git cherry-pick. Una mejor manera de explicar lo que está tratando de decir sería “corre git rebaseen la topicrama y la pasa master; corres git cherry-picken la masterrama y la pasas (se confirma desde) topic".
Rory O'Kane
14

La selección selectiva funciona para confirmaciones individuales .

Cuando realizas un cambio de base, se aplican todas las confirmaciones del historial al HEAD de la rama que faltan allí.

iltempo
fuente
Gracias. ¿Sabes si estos funcionan igual bajo las mantas? (almacena sus salidas intermedias en archivos de "parche", etc.).
ácido lisérgico
Afaik sí. Aplica todos los parches uno por uno. Esa es la razón por la que a veces tienes que resolver conflictos de fusión en medio de un rebase antes de continuar.
iltempo
6
@iltempo, funcionó para confirmaciones individuales solo en versiones anteriores de Git; en la actualidad, puede hacer algo como git cherry-pick foo~3..fooy obtener las confirmaciones superiores del árbol de "foo" elegidas una por una.
kostix
1
git-rebase usa la misma api que la selección selectiva en el código base, iirc
alternativa
No creo que en realidad funcionen igual bajo las sábanas. Intenté reajustar miles de confirmaciones y creo que git crea un archivo de buzón enorme y luego se ejecuta git amen él. Mientras que una selección selectiva aplica confirmación por confirmación (posiblemente creando un buzón de un solo mensaje para cada parche). Mi rebase estaba fallando porque el archivo de buzón que estaba creando se quedó sin espacio en la unidad, pero la selección con el mismo rango de revisión tuvo éxito (y parece ejecutarse más rápido).
ninguno
11

Una respuesta corta:

  • git cherry-pick es más "de bajo nivel"
  • Como tal, puede emular git rebase

Las respuestas dadas anteriormente son buenas, solo quería dar un ejemplo en un intento de demostrar su interrelación.

No se recomienda reemplazar "git rebase" con esta secuencia de acciones, es solo "una prueba de concepto" que, espero, ayude a comprender cómo funcionan las cosas.

Dado el siguiente repositorio de juguetes:

$ git log --graph --decorate --all --oneline
* 558be99 (test_branch_1) Test commit #7
* 21883bb Test commit #6
| * 7254931 (HEAD -> master) Test commit #5
| * 79fd6cb Test commit #4
| * 48c9b78 Test commit #3
| * da8a50f Test commit #2
|/
* f2fa606 Test commit #1

Digamos, tenemos algunos cambios muy importantes (confirmaciones # 2 a # 5) en master que queremos incluir en nuestro test_branch_1. Por lo general, simplemente cambiamos a una rama y hacemos "git rebase master". Pero como pretendemos que solo estamos equipados con "git cherry-pick", lo hacemos:

$ git checkout 7254931                # Switch to master (7254931 <-- master <-- HEAD)
$ git cherry-pick 21883bb^..558be99   # Apply a range of commits (first commit is included, hence "^")    

Después de todas estas operaciones, nuestro gráfico de confirmación se verá así:

* dd0d3b4 (HEAD) Test commit #7
* 8ccc132 Test commit #6
* 7254931 (master) Test commit #5
* 79fd6cb Test commit #4
* 48c9b78 Test commit #3
* da8a50f Test commit #2
| * 558be99 (test_branch_1) Test commit #7
| * 21883bb Test commit #6
|/
* f2fa606 Test commit #1

Como podemos ver, las confirmaciones # 6 y # 7 se aplicaron contra 7254931 (una confirmación de sugerencia del maestro). HEAD se movió y señala una confirmación que es, esencialmente, la punta de una rama rebasada. Ahora todo lo que tenemos que hacer es eliminar un puntero de rama antiguo y crear uno nuevo:

$ git branch -D test_branch_1
$ git checkout -b test_branch_1 dd0d3b4

test_branch_1 ahora está enraizado desde la última posición maestra. ¡Hecho!

raiks
fuente
¿Pero rebase también puede simular git cherry-pick?
Number945
Dado que cherry-pickes capaz de aplicar una variedad de confirmaciones, creo que sí. Aunque esta es una forma un poco extraña de hacer las cosas, nada le impide seleccionar todas las confirmaciones en su rama de características además de master, luego elimine la rama de características y vuelva a crearla de manera que apunte a la punta de master. Se puede pensar en git rebasecomo una secuencia de git cherry-pick feature_branch, git branch -d feature_branchy git branch feature_branch master.
raiks
7

Ambos son comandos para reescribir las confirmaciones de una rama encima de otra: la diferencia está en qué rama - "la tuya" (la actualmente verificada HEAD) o "suya" (la rama pasada como argumento al comando) - es la base de esta reescritura.

git rebasetoma una confirmación inicial y reproduce tus confirmaciones como si fueran después de las suyas (la confirmación inicial).

git cherry-picktoma un conjunto de confirmaciones y reproduce sus confirmaciones como si fueran posteriores a las tuyas (tu HEAD).

En otras palabras, los dos comandos son, en su comportamiento núcleo (ignorando sus características de rendimiento divergentes, convenciones de llamada y opciones de mejora), simétrica : retirar la rama bary en funcionamiento git rebase fooconjuntos de la barrama a la misma historia como el registro de salida rama fooy funcionando git cherry-pick ..barfijaría fooa (los cambios de foo, seguidos de los cambios de bar).

En cuanto al nombre, la diferencia entre los dos comandos se puede recordar en que cada uno describe lo que hace con la rama actual : rebasehace que el otro encabece la nueva base para sus cambios, mientras que cherry-pickselecciona los cambios de la otra rama y los coloca encima de tuHEAD (como cerezas encima de un helado).

Stuart P. Bentley
fuente
1
¡No pude entender ninguna de las respuestas excepto la tuya! Es conciso y tiene mucho sentido sin palabras superfluas.
Neóxico
4

Ambos hacen cosas muy similares; la principal diferencia conceptual es (en términos simplificados) que:

  • rebase mueve confirmaciones de la rama actual a otra rama .

  • Cherry-pick copia confirmaciones de otra rama a la rama actual .

Usando diagramas similares a la respuesta de @Kenny Ho :

Dado este estado inicial:

A---B---C---D master
     \
      E---F---G topic

... y suponiendo que desea que las confirmaciones de la topicrama se reproduzcan en la parte superior de la masterrama actual , tiene dos opciones:

  1. Usando rebase: primero iría a topichaciendo git checkout topicy luego movería la rama ejecutando git rebase master, produciendo:

    A---B---C---D master
                 \
                  E'---F'---G' topic
    

    Resultado: su rama actual topicfue reorganizada (movida) a master.
    La topicsucursal se actualizó, mientras que la mastersucursal permaneció en su lugar.

  2. Usando cherry-pick : primero iría a masterhaciendo git checkout mastery luego copiaría la rama ejecutando git cherry-pick topic~3..topic(o, de manera equivalente, git cherry-pick B..G), produciendo:

    A---B---C---D---E'---F'---G' master
         \
          E---F---G topic
    

    Resultado: las confirmaciones de topicse copiaron en master.
    La mastersucursal se actualizó, mientras que la topicsucursal permaneció en su lugar.


Por supuesto, aquí tenía que decirle explícitamente a cherry-pick que eligiera una secuencia de confirmaciones , usando la notación de rango foo..bar . Si simplemente hubiera pasado el nombre de la rama, como en git cherry-pick topic, habría recogido solo la confirmación en la punta de la rama, dando como resultado:

A---B---C---D---G' master
     \
      E---F---G topic
waldyrious
fuente