¿Combinar las dos primeras confirmaciones de un repositorio Git?

197

Supongamos que tiene un historial que contiene las tres confirmaciones A, B y C :

A-B-C

Me gustaría combinar los dos commits A y B en un commit AB :

AB-C

Lo intenté

git rebase -i A

que abre mi editor con los siguientes contenidos:

pick e97a17b B
pick asd314f C

Cambio esto a

squash e97a17b B
pick asd314f C

Entonces Git 1.6.0.4 dice:

Cannot 'squash' without a previous commit

¿Hay alguna manera o es simplemente imposible?

cristiano
fuente

Respuestas:

169

Usar a git rebase -i --root partir de Git versión 1.7.12 .

En el archivo de rebase interactivo, cambie la segunda línea de commit B a squash y deje las otras líneas en pick :

pick f4202da A
squash bea708e B
pick a8c6abc C

Esto combinará los dos commits A y B en un commit AB .

Encontrado en esta respuesta .

kostmo
fuente
126

Intentaste:

git rebase -i A

Es posible comenzar así si continúa con, en editlugar de squash:

edit e97a17b B
pick asd314f C

entonces corre

git reset --soft HEAD^
git commit --amend
git rebase --continue

Hecho.

David Lichteblau
fuente
44
Si está haciendo esto para arreglar silenciosamente un github gist, deberá agregar -m "initial" al commit. ;-)
Bruno Bronosky
1
git rebase --abortcomenzar de nuevo y hacerlo de la manera correcta (sin aplastar el primer commit en el editor)
oma
66

A fue la confirmación inicial, pero ahora quieres B ser la confirmación inicial. los commits de git son árboles enteros, no diffs, incluso si normalmente se describen y ven en términos del diff que introducen.

Esta receta funciona incluso si hay múltiples confirmaciones entre A y B, y B y C.

# Go back to the last commit that we want
# to form the initial commit (detach HEAD)
git checkout <sha1_for_B>

# reset the branch pointer to the initial commit,
# but leaving the index and working tree intact.
git reset --soft <sha1_for_A>

# amend the initial tree using the tree from 'B'
git commit --amend

# temporarily tag this new initial commit
# (or you could remember the new commit sha1 manually)
git tag tmp

# go back to the original branch (assume master for this example)
git checkout master

# Replay all the commits after B onto the new initial commit
git rebase --onto tmp <sha1_for_B>

# remove the temporary tag
git tag -d tmp
CB Bailey
fuente
1
esto desencadena un rebase interactivo masivo cuando hago elgit rebase --onto tmp <sha1_for_B>
Alex
Teniendo en cuenta que tenía un nuevo repositorio con solo dos commits (que quería incluir en uno), esto funcionó perfectamente para mí. Gracias @CB Bailey
RominRonin
10

En el caso de un rebase interactivo, debe hacerlo antes de A para que la lista sea:

pick A
pick B
pick C

convertirse:

pick A
squash B
pick C

Si A es la confirmación inicial, debe tener una confirmación inicial diferente antes de A. Git piensa en las diferencias, funcionará en la diferencia entre (A y B) y (B y C). Por lo tanto, la calabaza no funciona en su ejemplo.

Loki
fuente
9

En el caso de que tenga cientos o miles de confirmaciones, utilice la respuesta de kostmo de

git rebase -i --root

puede ser poco práctico y lento, solo debido a la gran cantidad de confirmaciones que el script de rebase tiene que procesar dos veces , una para generar la lista interactiva del editor de rebase (donde selecciona qué acción tomar para cada commit), y una vez para ejecutar realmente nueva aplicación de commits.

Aquí hay una solución alternativa que evitará el costo de tiempo de generar la lista del editor de rebase interactivo al no usar un rebase interactivo en primer lugar. De esta manera, es similar a la solución de Charles Bailey . Simplemente cree una rama huérfana a partir de la segunda confirmación, y luego vuelva a basar todas las confirmaciones descendientes encima de ella:

git checkout --orphan orphan <second-commit-sha>
git commit -m "Enter a commit message for the new root commit"
git rebase --onto orphan <second-commit-sha> master

Documentación

Comunidad
fuente
0

Comando git para escuadrón: git rebase -i HEAD ~ [número de confirmaciones]

Digamos que tienes el siguiente historial de git commit:


pick 5152061 feat: Se agregó soporte para guardar la imagen. (A)
elija 39c5a04 Corrección: correcciones de errores. (B)
elija la corrección 839c6b3: conflicto resuelto. (C)

Ahora desea aplastar A y B a AB, realice los siguientes pasos:


pick 5152061 feat: Se agregó soporte para guardar la imagen. (A)
s 39c5a04 Corrección: corrección de errores. (B)
elija la corrección 839c6b3: conflicto resuelto. (C)

Nota: para el squash commit podemos usar squash o s. El resultado final será:
seleccione 5152061 feat: Se agregó soporte para guardar la imagen. (AB)
pick 839c6b3 fix: conflicto resuelto. (C)

Sumit
fuente
-1

Tienes que realizar un poco de magia de línea de comandos.

git checkout -b a A
git checkout B <files>
git commit --amend
git checkout master
git rebase a

Eso debería dejarte con una rama que tiene AB y C como commits.

Bombe
fuente
Debido a que las confirmaciones iniciales antiguas y nuevas no tienen un antepasado común, es posible que tenga algunos conflictos innecesarios a medida que git intenta aplicar toda la historia de master en un, a pesar de que tienen un árbol en común. Al usar la opción --onto para git rebase, puede decirle a git el lugar correcto para comenzar a aplicar.
CB Bailey