Reordenación de confirmaciones

104

Actualmente estoy trabajando en una rama y quiero que algunas confirmaciones se fusionen con otras ramas:

    a-b-c-d-e-f-g (branchA)
   /
--o-x-x-x-x-x-x-x-x-x-x (master)
   \
    x-x-x-x-x (branchB)

(Las letras denotan confirmaciones y la "x" son confirmaciones irrelevantes).

Sin embargo, noté que sería una buena idea agrupar algunas confirmaciones. Quiero "concatenar" las confirmaciones a, d, e y g en un parche y enviarlas a master. Las confirmaciones byf deberían ir como una confirmación para branchB. ¿Existe una buena forma 'git'-ish de lograrlo?

Pedro
fuente
4
Las dos respuestas ya dadas mencionan el comando correcto, pero creo que con una tarea tan básica como esta, vale la pena tener instrucciones reales aquí en StackOverflow, así que he publicado algunas. (Me sorprende que no pueda encontrar una buena pregunta anterior para enlazar).
Cascabel

Respuestas:

125

El comando que está buscando es git rebase, específicamente la -i/--interactiveopción.

Voy a suponer que desea dejar la confirmación c en la rama A, y que realmente quiere decir que desea mover las otras confirmaciones a las otras ramas, en lugar de fusionar, ya que las fusiones son sencillas. Comencemos por manipular la rama A.

git rebase -i <SHA1 of commit a>^ branchA

Los ^medios de la confirmación anterior, por lo que este comando dice que rebase la rama A usando el commit antes "a" como la base. Git te presentará una lista de las confirmaciones en este rango. Reordenarlos y decirle a git que aplaste los apropiados:

pick c ...
pick a ...
squash d ...
squash e ...
squash g ...
pick b
squash f

Ahora, el historial debería verse así:

    c - [a+d+e+g] - [b+f] (branchA)
   /
--o-x-x-x-x-x-x-x-x-x-x (master)

Ahora, tomemos la confirmación b + f recién aplastada para branchB.

git checkout branchB
git cherry-pick branchA  # cherry-pick one commit, the tip of branchA

Y lo mismo para a + d + e + g para master:

git checkout master
git cherry-pick branchA^

Finalmente, actualice branchA para que apunte ac:

git branch -f branchA branchA^^

Ahora deberíamos tener:

    c (branch A) - [a+d+e+g] - [b+f] (dangling commits)
   /
--o-x-x-x-x-x-x-x-x-x-x-[a+d+e+g] (master)
   \
    x-x-x-x-x-[b+f] (branchB)

Tenga en cuenta que si tiene varias confirmaciones que desea mover entre ramas, puede usar rebase nuevamente (no interactivamente):

# create a temporary branch
git branch fromAtoB branchA
# move branchA back two commits
git branch -f branchA branchA~2
# rebase those two commits onto branchB
git rebase --onto branchB branchA fromAtoB
# merge (fast-forward) these into branchB
git checkout branchB
git merge fromAtoB
# clean up
git branch -d fromAtoB

Finalmente, un descargo de responsabilidad: es muy posible reordenar las confirmaciones de tal manera que algunas ya no se apliquen limpiamente. Esto podría deberse a que eligió un orden incorrecto (colocando un parche antes de la confirmación introduciendo la característica que parcheó); en ese caso, querrá abortar el rebase ( git rebase --abort). De lo contrario, tendrá que solucionar los conflictos de manera inteligente (tal como lo hace con los conflictos de fusión), agregar las correcciones y luego ejecutar git rebase --continuepara continuar . Estas instrucciones también las proporciona el mensaje de error que se imprime cuando se produce el conflicto.

Cascabel
fuente
¿No es el diagrama después del git branch -f branchA branchA^^error? Es decir, ¿no debería branchA apuntar ac en este punto, de modo que la primera línea del diagrama sea c (branchA)y no c - [a+d+e+g] - [b+f] (branchA)?
ntc2
@enoksrd: Sí, hay un error, pero su edición tenía errores. Lo arreglaré cuando tenga la oportunidad aquí.
Cascabel
En lugar de hacerlo, git branch -f branchA branchA^^¿por qué no puedes simplemente hacer una git reset --hard <sha1 of c>en la rama A?
Mikhail Vasilyev
17
git rebase -i HEAD~3

¿Dónde 3está la cantidad de confirmaciones que necesitan reordenarse ( fuente )?

Esto abrirá vi , enumerando las confirmaciones de la más antigua (arriba) a la más nueva (abajo).
Ahora reordene las líneas, guarde y salga del editor.

ddpmoverá la línea actual hacia abajo
ddkP moverá la línea actual hacia arriba ( fuente )

Nelu
fuente
9

git rebase --interactive es el comando que desea.

Ejemplo:

El estado actual es este:

bernt@le3180:~/src/stackoverflow/reordering_of_commits
$ git status
On branch master
nothing to commit, working tree clean
bernt@le3180:~/src/stackoverflow/reordering_of_commits
$ git log --oneline
a6e3c6a (HEAD -> master) Fourth commit
9a24b81 Third commit
7bdfb68 Second commit
186d1e0 First commit

Quiero reordenar las confirmaciones 9a24b81(Tercera confirmación) y 7bdfb68(Segunda confirmación). Para hacer esto, primero encuentro el compromiso antes del primer compromiso que queremos cambiar . Este es el compromiso 186d1e0(primer compromiso).

El comando a ejecutar es git rebase --interactive COMMIT-BEFORE-FIRST-COMMIT-WE-WANT-TO-CHANGE, en este caso:

bernt@le3180:~/src/stackoverflow/reordering_of_commits
$ git rebase --interactive 186d1e0

Esto abre un archivo en el editor predeterminado con el siguiente contenido:

pick 7bdfb68 Second commit
pick 9a24b81 Third commit
pick a6e3c6a Fourth commit

# Rebase 186d1e0..a6e3c6a onto 186d1e0 (3 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#

Tenga en cuenta que el orden de las confirmaciones en el archivo es opuesto al que se usa para git log. En git log, la confirmación más reciente está en la parte superior. En este archivo, la confirmación más reciente está al final.

Como explica el comentario en la parte inferior del archivo, hay varias cosas que puedo hacer, como aplastar, eliminar y reordenar las confirmaciones. Para reordenar las confirmaciones, edito el archivo para que se vea así (los comentarios inferiores no se muestran):

pick 9a24b81 Third commit
pick 7bdfb68 Second commit
pick a6e3c6a Fourth commit

El pickcomando al comienzo de cada línea significa "usar (es decir, incluir) esta confirmación" y cuando guardo el archivo temporal con los comandos de rebase y salgo del editor, git ejecutará los comandos y actualizará el repositorio y el directorio de trabajo:

$ git rebase --interactive 186d1e0
Successfully rebased and updated refs/heads/master.
bernt@le3180:~/src/stackoverflow/reordering_of_commits
$ git log --oneline
ba48fc9 (HEAD -> master) Fourth commit
119639c Second commit
9716581 Third commit
186d1e0 First commit

Tenga en cuenta el historial de confirmaciones reescrito.

Enlaces:

codeape
fuente
1
Agregue detalles relevantes de su enlace para proporcionar una respuesta completa e independiente. SO frunce el ceño ante las "respuestas de solo enlace". Específicamente, ¿cómo se usaría git rebase --interactivepara lograr el objetivo de OP?
SherylHohman
El segundo enlace no existe ahora.
devsaw
Contenido agregado para que la respuesta sea completa y autónoma. Se eliminó el segundo enlace.
Codeape
-1

git rebase es lo que quieres. Consulte la opción --interactive.


fuente
4
Agregue detalles relevantes de su enlace para proporcionar una respuesta completa e independiente. SO frunce el ceño ante las "respuestas de solo enlace".
SherylHohman