tag - git rebase
Cómo dividir el último commit en dos en Git (9)
Tengo dos ramas de trabajo, maestro y foro, y acabo de hacer algunas modificaciones en la rama del foro , que me gustaría seleccionar en maestro . Pero, desafortunadamente, la confirmación que quiero seleccionar también contiene algunas modificaciones que no deseo.
La solución probablemente sería eliminar de alguna manera la confirmación incorrecta y reemplazarla con dos confirmaciones separadas, una con los cambios que quiero seleccionar en master y otras que no pertenecen a ella.
He tratado de hacer
git reset --hard HEAD^
que eliminó todos los cambios, así que tuve que volver con
git reset ORIG_HEAD
Entonces, mi pregunta es, ¿cuál es la mejor manera de dividir el último compromiso en dos confirmaciones separadas?
El método de doble revertir-squash.
- Haz otra confirmación que elimine los cambios no deseados. (Si es por archivo, esto es realmente sencillo:
git checkout HEAD~1 -- files with unwanted changes
ygit commit
. De lo contrario, los archivos con cambios mixtos se pueden configurar parcialmente.git reset file
ygit add -p file
como paso intermedio .) Llama a este revertir . -
git revert HEAD
: realiza otra confirmación, que agrega los cambios no deseados. Este es el doble revertir. - De los 2 envíos que hiciste, aplasta el primero sobre el compromiso de dividir (
git rebase -i HEAD~3
). Este compromiso ahora se libera de los cambios no deseados, ya que están en el segundo compromiso.
Beneficios
- Conserva el mensaje de confirmación.
- Funciona incluso si el compromiso de dividir no es el último. Solo requiere que los cambios no deseados no entren en conflicto con las confirmaciones posteriores.
Metas:
- Quiero dividir un commit anterior (
splitme
) en dos. - Quiero mantener el mensaje de confirmación .
Plan:
- Rebase interactivo desde uno antes de
splitme
. - editar
splitme
. - Restablecer los archivos para dividir en una segunda confirmación.
- Modificar la confirmación, mantener el mensaje, modificar según sea necesario.
- Vuelva a agregar los archivos divididos a partir de la primera confirmación.
- Comprometete con un nuevo mensaje.
- Continuar rebase.
Los pasos de rebase (1 y 7) se pueden omitir si el splitme
es la confirmación más reciente.
git rebase -i splitme^
# mark splitme commit with ''e''
git reset HEAD^ -- $files
git commit --amend
git add $files
git commit -m "commit with just some files"
git rebase --continue
Si quisiera que los archivos divididos se confirmaran primero, entonces volvería a cambiar -i y cambiaría el orden
git rebase -i splitme^
# swap order of splitme and ''just some files''
Deberías usar el índice. Después de hacer un reinicio mixto (" git reset HEAD ^"), agregue el primer conjunto de cambios en el índice, luego confírmelos. Entonces cometer el resto.
Puede usar " git add " para colocar todos los cambios realizados en un archivo en el índice. Si no desea realizar todas las modificaciones realizadas en un archivo, solo algunas de ellas, puede usar "git add -p".
Veamos un ejemplo. Supongamos que tengo un archivo llamado myfile, que contiene el siguiente texto:
something
something else
something again
Lo modifiqué en mi última confirmación para que ahora se vea así:
1
something
something else
something again
2
Ahora decido que quiero dividirlo en dos, y quiero que la inserción de la primera línea esté en la primera confirmación, y que la inserción de la última línea esté en la segunda confirmación.
Primero vuelvo con el padre de HEAD, pero quiero mantener las modificaciones en el sistema de archivos, así que uso "git reset" sin argumentos (lo que hará un llamado reinicio "mixto"):
$ git reset HEAD^
myfile: locally modified
$ cat myfile
1
something
something else
something again
2
Ahora uso "git add -p" para agregar los cambios que quiero confirmar en el índice (= los escalé). "git add -p" es una herramienta interactiva que le pregunta qué cambios en el archivo deben agregarse al índice.
$ git add -p myfile
diff --git a/myfile b/myfile
index 93db4cb..2f113ce 100644
--- a/myfile
+++ b/myfile
@@ -1,3 +1,5 @@
+1
something
something else
something again
+2
Stage this hunk [y,n,a,d,/,s,e,?]? s # split this section into two!
Split into 2 hunks.
@@ -1,3 +1,4 @@
+1
something
something else
something again
Stage this hunk [y,n,a,d,/,j,J,g,e,?]? y # yes, I want to stage this
@@ -1,3 +2,4 @@
something
something else
something again
+2
Stage this hunk [y,n,a,d,/,K,g,e,?]? n # no, I don''t want to stage this
Entonces me comprometo este primer cambio:
$ git commit -m "Added first line"
[master cef3d4e] Added first line
1 files changed, 1 insertions(+), 0 deletions(-)
Ahora puedo confirmar todos los demás cambios (a saber, el número "2" en la última línea):
$ git commit -am "Added last line"
[master 5e284e6] Added last line
1 files changed, 1 insertions(+), 0 deletions(-)
Vamos a revisar el log para ver qué compromisos tenemos:
$ git log -p -n2 | cat
Commit 5e284e652f5e05a47ad8883d9f59ed9817be59d8
Author: ...
Date: ...
Added last line
Diff --git a/myfile b/myfile
Index f9e1a67..2f113ce 100644
--- a/myfile
+++ b/myfile
@@ -2,3 +2,4 @@
something
something else
something again
+2
Commit cef3d4e0298dd5d279a911440bb72d39410e7898
Author: ...
Date: ...
Added first line
Diff --git a/myfile b/myfile
Index 93db4cb..f9e1a67 100644
--- a/myfile
+++ b/myfile
@@ -1,3 +1,4 @@
+1
something
something else
something again
Ejecute git gui
, seleccione el botón de opción "Modificar último compromiso", y deshaga los cambios (Confirmar> Desactivar etapa de confirmación o Ctrl - U ) que no desea que se realicen las primeras confirmaciones. Creo que esa es la forma más fácil de hacerlo.
Otra cosa que podrías hacer es seleccionar el cambio sin cometer cambios ( git cherry-pick -n
) y luego, manualmente o con git gui
seleccionar los cambios deseados antes de cometer.
Esta podría ser otra solución dirigida a los casos en los que hay un gran compromiso y una pequeña cantidad de archivos deben trasladarse a un nuevo compromiso. Esto funcionará si se va a extraer un conjunto de <path>
archivos de la última confirmación en HEAD y todos se moverán a una nueva confirmación. Si se necesitan múltiples confirmaciones, se pueden usar las otras soluciones.
Primero haga los parches en las áreas organizadas y no escalonadas que contendrían los cambios para revertir el código antes de la modificación y después de la modificación, respectivamente:
git reset HEAD^ <path> $ git status On branch <your-branch> Changes to be committed: (use "git reset HEAD <file>..." to unstage) modified: <path> Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: <path>
Para entender lo que va a pasar (la flecha y los comentarios no son parte del comando):
git diff --cached -> show staged changes to revert <path> to before HEAD git diff -> show unstaged changes to add current <path> changes
Revertir <path>
cambios en la última confirmación:
git commit --amend -> reverts changes on HEAD by amending with staged changes
Crear nuevo commit con <path>
cambios:
git commit -a -m "New Commit" -> adds new commit with unstaged changes
Esto tiene el efecto de crear una nueva confirmación que contiene los cambios extraídos de la última confirmación.
Me sorprende que nadie haya sugerido git cherry-pick -n forum
. Esto preparará los cambios del último compromiso del forum
pero no los confirmará; luego puede reset
los cambios que no necesita y confirmar lo que desea mantener.
Para cambiar la confirmación actual en dos confirmaciones, puede hacer algo como lo siguiente.
Ya sea:
git reset --soft HEAD^
Esto deshace el último commit pero deja todo en escena. A continuación, puede desestabilizar ciertos archivos:
git reset -- file.file
Opcionalmente restaurar partes de esos archivos:
git add -p file.file
Haz un nuevo primer commit:
git commit
La etapa y cometer el resto de los cambios en una segunda confirmación:
git commit -a
O:
Deshacer y eliminar todas las modificaciones de la última confirmación:
git reset HEAD^
Escenario selectivo de la primera ronda de cambios:
git add -p
Cometer:
git commit
Comete el resto de los cambios:
git commit -a
(En cualquiera de los pasos, si deshizo una confirmación que agregó un nuevo archivo y desea agregarla a la segunda confirmación, tendrá que agregarla manualmente como commit -a
solo etapas escalonadas a archivos ya rastreados).
Ya que estás recogiendo cerezas, puedes:
-
cherry-pick
con la--no-commit
agregada. -
reset
y useadd --patch
,add --edit
o simplementeadd
a la etapa lo que desea conservar. -
commit
los cambios por etapas.- Para reutilizar el mensaje de confirmación original, puede agregar las
--reuse-message=<old-commit-ref>
o--reedit-message=<old-commit-ref>
al comandocommit
.
- Para reutilizar el mensaje de confirmación original, puede agregar las
- Elimina los cambios sin
reset --hard
conreset --hard
.
Otra forma, conservando o editando el mensaje de confirmación original:
-
cherry-pick
el compromiso original como normal. - Invierta los cambios que no desea y use
add
para organizar la reversión.- Este paso sería fácil si está eliminando lo que agregó, pero un poco complicado si está agregando lo que eliminó o anulando un cambio.
-
commit --amend
para efectuar la reversión en el commit de cherry-recogido.- Recibirá nuevamente el mismo mensaje de confirmación, que puede conservar o revisar según sea necesario.
git reset HEAD^
el - duro es lo que está matando tus cambios.