En git, ¿cuál es la diferencia entre merge --squash y rebase?

363

Soy nuevo en git y estoy tratando de entender la diferencia entre un squash y un rebase. Según tengo entendido, realizas un squash cuando haces un rebase.

GiH
fuente

Respuestas:

360

Ambos git merge --squashy git rebase --interactivepueden producir un compromiso "aplastado".
Pero sirven para diferentes propósitos.

producirá una confirmación aplastada en la rama de destino, sin marcar ninguna relación de fusión.
(Nota: no produce una confirmación de inmediato: necesita una adicional git commit -m "squash branch")
Esto es útil si desea descartar por completo la rama de origen, pasando de (esquema tomado de la pregunta SO ):

 git checkout stable

      X                   stable
     /                   
a---b---c---d---e---f---g tmp

a:

git merge --squash tmp
git commit -m "squash tmp"

      X-------------------G stable
     /                   
a---b---c---d---e---f---g tmp

y luego eliminando la tmprama.


Nota: git mergetiene una --commitopción , pero no se puede usar con --squash. Fue nunca se pueden utilizar --commity --squashjuntos.
Desde Git 2.22.1 (Q3 2019), esta incompatibilidad se hace explícita:

Ver commit 1d14d0c (24 de mayo de 2019) por Vishal Verma ( reloadbrain) .
(Fusionada por Junio ​​C Hamano - gitster- en commit 33f2790 , 25 jul 2019)

merge: rechazar --commitcon--squash

Anteriormente, cuando --squashse suministraba ' option_commit', se soltó en silencio. Esto podría haber sido sorprendente para un usuario que intentó anular el comportamiento sin compromiso de squash usando --commitexplícitamente.

git/git builtin/merge.c#cmd_merge() ahora incluye:

if (option_commit > 0)
    die(_("You cannot combine --squash with --commit."));

reproduce algunas o todas sus confirmaciones en una nueva base, lo que le permite aplastar (o más recientemente "arreglar", vea esta pregunta SO ), yendo directamente a:

git checkout tmp
git rebase -i stable

      stable
      X-------------------G tmp
     /                     
a---b

Si elige aplastar todas las confirmaciones de tmp(pero, al contrario merge --squash, puede elegir reproducir algunas y aplastar otras).

Entonces las diferencias son:

  • squashno toca tu rama de origen ( tmpaquí) y crea una única confirmación donde quieras.
  • rebasele permite continuar en la misma rama fuente (aún tmp) con:
    • una nueva base
    • una historia más limpia
VonC
fuente
11
Ges c--d--e--f--gaplastado juntos?
Wayne Conrad
8
@Wayne: sí, G en esos ejemplos representa los tmpcommits aplastados juntos.
VonC
3
@ Th4wn: Dado que Git razona con instantáneas de un proyecto completo, Gno representará el mismo contenido que g, debido a los cambios introducidos por X.
VonC
1
@VonC: no estoy seguro sobre ese último comentario. Si tienes un en git merge --no-ff templugar de git merge --squash temp, obtienes un historial desordenado, pero también puedes hacer cosas como git revert e, mucho más fácilmente. Es una historia desordenada, pero honesta y pragmática, y la rama principal sigue siendo bastante limpia.
nada101
2
@ naught101 Estoy de acuerdo. Como se explica en stackoverflow.com/a/7425751/6309 sin embargo, también se trata de no romper git bisecto git blamecuando se utiliza con demasiada frecuencia (como en git pull --no-ff: stackoverflow.com/questions/12798767/... ). De todos modos, no hay un enfoque, por eso este artículo describe tres ( stackoverflow.com/questions/9107861/… )
VonC
183

Fusionar confirmaciones: retiene todas las confirmaciones en su rama y las intercala con confirmaciones en la rama baseingrese la descripción de la imagen aquí

Combinar Squash: retiene los cambios pero omite las confirmaciones individuales del historial ingrese la descripción de la imagen aquí

Rebase: Esto mueve toda la rama de características para comenzar en la punta de la rama maestra, incorporando efectivamente todos los nuevos commits en master

ingrese la descripción de la imagen aquí

Más aquí

Md Ayub Ali Sarker
fuente
81

Combinar squash combina un árbol (una secuencia de confirmaciones) en una única confirmación. Es decir, aplasta todos los cambios realizados en n commits en un solo commit.

Rebasar es volver a basar, es decir, elegir una nueva base (confirmación principal) para un árbol. Quizás el término mercurial para esto sea más claro: lo llaman trasplante porque es solo eso: elegir un nuevo terreno (compromiso de los padres, raíz) para un árbol.

Al hacer un rebase interactivo, se le da la opción de aplastar, elegir, editar u omitir los commits que va a rebase.

¡Espero que haya quedado claro!

Mauricio Scheffer
fuente
77
¿Cuándo debería rebasear y cuándo aplastar?
Martin Thoma
31

Comencemos por el siguiente ejemplo:

ingrese la descripción de la imagen aquí

Ahora tenemos 3 opciones para fusionar los cambios de la rama de características en la rama maestra :

  1. Combinar confirmaciones
    Mantendrá el historial de todas las confirmaciones de la rama característica y las moverá a la rama maestra.
    Agregará una confirmación ficticia adicional.

  2. Rebase y fusión Agregará
    todo el historial de confirmaciones de la rama de características en el frente de la rama maestra
    NO agregará una confirmación ficticia adicional.

  3. Aplastar y fusionar
    Agrupará todas las confirmaciones de la rama de características en una confirmación y luego la agregará al frente de la rama maestra.
    Agregará una confirmación ficticia adicional.

A continuación puede encontrar cómo se verá la rama maestra después de cada una de ellas.

ingrese la descripción de la imagen aquí

En todos los casos:
podemos ELIMINAR de manera segura la rama de características .

ahmednabil88
fuente
1
¿Puedes explicar qué es el commit ficticio en la segunda imagen? Soy un principiante en git.
Yusuf
1
@Yusuf, es solo una confirmación adicional que contiene actualizaciones de ambas ramas, es el mensaje de confirmación predeterminado = "Megre branch XYZ into master"
ahmednabil88