Normalmente presento una lista de confirmaciones para su revisión. Si tengo los siguientes commits:
HEAD
Commit3
Commit2
Commit1
... Sé que puedo modificar head commit con git commit --amend
. Pero, ¿cómo puedo modificar Commit1
, dado que no es el HEAD
commit?
git
git-rewrite-history
Sam Liao
fuente
fuente
Respuestas:
Puedes usar git rebase . Por ejemplo, si desea modificar commit
bbc643cd
, ejecuteTenga en cuenta el símbolo
^
de intercalación al final del comando, ya que en realidad debe volver a redactar la confirmación antes de la que desea modificar .En el editor predeterminado, modifique
pick
aedit
en la línea que menciona 'bbc643cd'.Guarde el archivo y salga: git interpretará y ejecutará automáticamente los comandos en el archivo. Te encontrarás en la situación anterior en la que acabas de crear commit
bbc643cd
.En este punto,
bbc643cd
es su última confirmación y puede modificarla fácilmente : realice sus cambios y luego confirme con el comando:Después de eso, escriba:
para volver a la confirmación HEAD anterior.
ADVERTENCIA : Tenga en cuenta que esto cambiará el SHA-1 de ese commit , así como todos los hijos ; en otras palabras, esto reescribe el historial a partir de ese momento. Puede romper repos haciendo esto si presiona usando el comando
git push --force
fuente
reword
acción engit rebase -i
lugar deedit
(abre automáticamente el editor y continúa con el resto de los pasos de rebase; esto evita el uso degit commit --ammend
ygit rebase --continue
cuando solo necesita cambiar el mensaje de confirmación y no el contenido )git stash
antesgit rebase
ygit stash pop
después, si tiene cambios pendientes.git commit --all --amend --no-edit
aquí. Todo lo que tenía que hacer despuésgit rebase -i ...
era hacerlogit commit --amend
normalmentegit rebase --continue
.Utilice la impresionante rebase interactiva:
Encuentre la confirmación que desea, cambie
pick
ae
(edit
) y guarde y cierre el archivo. Git se rebobinará con esa confirmación, permitiéndole:git commit --amend
para hacer cambios, ogit reset @~
para descartar la última confirmación, pero no los cambios en los archivos (es decir, llevarlo al punto en el que estaba cuando editó los archivos, pero aún no se comprometió).Este último es útil para hacer cosas más complejas como dividir en múltiples confirmaciones.
Luego, ejecute
git rebase --continue
, y Git reproducirá los cambios posteriores además de su confirmación modificada. Es posible que se le solicite que solucione algunos conflictos de fusión.Nota:
@
es una abreviatura deHEAD
, y~
es la confirmación antes de la confirmación especificada.Lea más sobre la reescritura del historial en los documentos de Git.
No tengas miedo de rebase
ProTip ™: no tenga miedo de experimentar con comandos "peligrosos" que reescriben el historial *: Git no elimina sus confirmaciones durante 90 días de forma predeterminada; puedes encontrarlos en el reflog:
* Tenga cuidado con las opciones como
--hard
y--force
aunque pueden descartar datos.* Además, no reescriba el historial en ninguna rama en la que esté colaborando.
En muchos sistemas,
git rebase -i
se abrirá Vim por defecto. Vim no funciona como la mayoría de los editores de texto modernos, así que eche un vistazo a cómo volver a crear una base usando Vim . Si prefiere usar un editor diferente, cámbielo congit config --global core.editor your-favorite-text-editor
.fuente
@
como taquigrafíaHEAD
. Gracias por publicar esto.git reset @~
exactamente lo que quería hacer después de elegir commit withgit rebase ...
. Eres mi héroe)El rebase interactivo con
--autosquash
es algo que uso con frecuencia cuando necesito arreglar compromisos previos más profundos en la historia. Básicamente, acelera el proceso que ilustra la respuesta de ZelluX, y es especialmente útil cuando tiene más de una confirmación que necesita editar.De la documentación:
Supongamos que tiene un historial que se ve así:
y tiene cambios que desea modificar a Commit2 y luego confirme sus cambios usando
alternativamente, puede usar el commit-sha en lugar del mensaje de confirmación,
"fixup! e8adec4
o incluso un prefijo del mensaje de confirmación.Luego inicie un rebase interactivo en el commit antes
su editor se abrirá con los commits ya ordenados correctamente
todo lo que necesitas hacer es guardar y salir
fuente
git commit --fixup=@~
lugar degit commit -m "fixup! Commit2"
. Esto es especialmente útil cuando los mensajes de confirmación son más largos y sería difícil escribir todo.Correr:
$ git rebase --interactive commit_hash^
cada uno
^
indica cuántas confirmaciones desea volver a editar, si es solo una (el hash de confirmación que especificó), entonces solo agrega una^
.Usando Vim, cambia las palabras
pick
areword
las confirmaciones que desea cambiar, guardar y salir (:wq
). Luego, git le indicará con cada confirmación que marcó como cambio de palabra para que pueda cambiar el mensaje de confirmación.Cada mensaje de confirmación que tiene que guardar y salir (
:wq
) para ir al siguiente mensaje de confirmaciónSi desea salir sin aplicar los cambios, presione
:q!
EDITAR : para navegar
vim
, usaj
para subir,k
para bajar,h
para ir a la izquierda yl
para ir a la derecha (todo esto enNORMAL
modo, presioneESC
para ir alNORMAL
modo). Para editar un texto, presionei
para ingresar alINSERT
modo donde inserta el texto. PresioneESC
para volver alNORMAL
modo :)ACTUALIZACIÓN : Aquí hay un gran enlace de la lista de github Cómo deshacer (casi) cualquier cosa con git
fuente
git push --force
?git push --force
hace es sobrescribir las confirmaciones remotas con sus confirmaciones locales. Ese no es el caso de este tema :)Si por alguna razón no le gustan los editores interactivos, puede usarlos
git rebase --onto
.Digamos que quieres modificar
Commit1
. Primero, bifurca desde antesCommit1
:Segundo, agarra
Commit1
concherry-pick
:Ahora, modifique sus cambios, creando
Commit1'
:Y finalmente, después de haber ocultado cualquier otro cambio, trasplante el resto de sus confirmaciones hasta la
master
parte superior de su nueva confirmación:Lea: "rebase, en la rama
amending
, todos los compromisos entreCommit1
(no inclusivo) emaster
(inclusivo)". Es decir, Commit2 y Commit3, eliminando por completo el viejo Commit1. Podrías simplemente elegirlos, pero de esta manera es más fácil.¡Recuerda limpiar tus ramas!
fuente
git checkout -b amending Commit1~1
para obtener el compromiso previogit checkout -b amending Commit1
?Basado en documentación
Modificación del mensaje de mensajes de confirmación anteriores o múltiples
Lo anterior muestra una lista de los últimos 3 commits en la rama actual, cambie 3 a otra cosa si desea más. La lista será similar a la siguiente:
Reemplace pick con reword antes de cada mensaje de confirmación que desee cambiar. Digamos que cambia la segunda confirmación en la lista, su archivo tendrá el siguiente aspecto:
Guarde y cierre el archivo de la lista de confirmación, aparecerá un nuevo editor para que pueda cambiar su mensaje de confirmación, cambiar el mensaje de confirmación y guardar.
Finalmente, presione forzosamente los commits enmendados.
fuente
Comando completamente no interactivo (1)
Solo pensé en compartir un alias que estoy usando para esto. Se basa en un rebase interactivo no interactivo. Para agregarlo a su git, ejecute este comando (la explicación se proporciona a continuación):
La mayor ventaja de este comando es el hecho de que es no-vim .
(1) dado que no hay conflictos durante el rebase, por supuesto
Uso
El nombre
amend-to
parece apropiado en mi humilde opinión. Compare el flujo con--amend
:Explicación
git config --global alias.<NAME> '!<COMMAND>'
- crea un alias global de git llamado<NAME>
que ejecutará un comando que no sea git<COMMAND>
f() { <BODY> }; f
- una función bash "anónima".SHA=`git rev-parse "$1"`;
- convierte el argumento a git revision, y asigna el resultado a variableSHA
git commit --fixup "$SHA"
- Fixup-commit paraSHA
. Vergit-commit
documentosGIT_SEQUENCE_EDITOR=true git rebase --interactive --autosquash "$SHA^"
git rebase --interactive "$SHA^"
parte ha sido cubierta por otras respuestas.--autosquash
es lo que se usa junto congit commit --fixup
, vergit-rebase
documentos para más informaciónGIT_SEQUENCE_EDITOR=true
es lo que hace que todo sea no interactivo. Este truco lo aprendí de esta publicación de blog .fuente
amend-to
manejen los archivos sin clasificar:git config --global alias.amend-to '!f() { SHA=
git rev-parse "$ 1"; git stash -k && git commit --fixup "$SHA" && GIT_SEQUENCE_EDITOR=true git rebase --interactive --autosquash "$SHA^" && git stash pop; }; f'
Edición de rebase interactiva automatizada seguida de revertir confirmación listo para una rehacer
Me encontré arreglando un commit del pasado con tanta frecuencia que escribí un script para él.
Aquí está el flujo de trabajo:
Esto lo colocará en la confirmación que desea editar.
Arregle y organice la confirmación como desearía que hubiera sido en primer lugar.
(Es posible que desee utilizar
git stash save
para mantener los archivos que no está confirmando)Rehaga el compromiso con
--amend
, por ejemplo:Completa el rebase:
Para que funcione lo anterior, coloque el siguiente script en un archivo ejecutable llamado
git-commit-edit
en algún lugar de su$PATH
:fuente
Llegué a este enfoque (y probablemente sea exactamente lo mismo que usar rebase interactivo), pero para mí es algo sencillo.
Nota: presento este enfoque para ilustrar lo que puede hacer en lugar de una alternativa diaria. Dado que tiene muchos pasos (y posiblemente algunas advertencias).
Digamos que quieres cambiar commit
0
y actualmente estás enfeature-branch
Pague este compromiso y cree un
quick-branch
. También puede clonar su rama de características como un punto de recuperación (antes de comenzar).Ahora tendrá algo como esto:
Cambios de escenario, esconde todo lo demás.
Confirmar cambios y finalizar la compra en
feature-branch
Ahora tendrá algo como esto:
Rebase
feature-branch
enquick-branch
(resolver cualquier conflicto en el camino). Aplicar alijo y eliminarquick-branch
.Y terminas con:
Git no duplicará (aunque realmente no puedo decir hasta qué punto) el 0 se compromete al rebasar.
Nota: todos los hash de confirmación se cambian a partir de la confirmación que originalmente teníamos la intención de cambiar.
fuente
Para obtener un comando no interactivo, coloque un script con este contenido en su RUTA:
Úselo organizando sus cambios (con
git add
) y luego ejecutegit fixup <commit-to-modify>
. Por supuesto, seguirá siendo interactivo si tienes conflictos.fuente
git stash
+rebase
automatizaciónPara cuando necesito modificar un antiguo commit muchas veces para las revisiones de Gerrit, he estado haciendo:
GitHub aguas arriba .
Uso:
git add
si ya está en repositoriogit-amend-old $old_sha
Me gusta esto
--autosquash
ya que no aplasta otras reparaciones no relacionadas.fuente
git amend
para aplicar cambios a una confirmación específica con el uso del alijo actual, ¡muy inteligente!Resolví esto
1) creando una nueva confirmación con los cambios que quiero ...
2) Sé qué compromiso necesito fusionar con él. que es commit 3.
entonces, el
git rebase -i HEAD~4
# 4 representa el commit 4 reciente (aquí commit 3 está en el 4to lugar)3) en el rebase interactivo la confirmación reciente se ubicará en la parte inferior. se verá igual
4) aquí necesitamos reorganizar commit si quieres fusionar con uno específico. debería ser como,
después de reorganizar, debe reemplazarlo
p
pick
conf
(la reparación se fusionará sin el mensaje de confirmación) os
(la combinación de squash con el mensaje de confirmación puede cambiar en el tiempo de ejecución)y luego salva tu árbol.
ahora fusionar hecho con commit existente.
fuente
La mejor opción es utilizar el "Comando de rebase interactivo" .
¿Ahora cómo usar este comando?
-i
significa "interactivo" . Tenga en cuenta que puede realizar un rebase en modo no interactivo. ex:HEAD
indica su ubicación actual (también puede ser el nombre de la sucursal o confirmar SHA). El~n
medio "n beforeé, por lo queHEAD~n
será la lista de 'n' commit antes del que se encuentra actualmente.git rebase
tiene un comando diferente como:p
opick
para mantener el compromiso tal como es.r
oreword
: para mantener el contenido de la confirmación pero alterar el mensaje de confirmación.s
osquash
: para combinar los cambios de este commit en el commit anterior (el commit arriba en la lista).... etc.
Nota: Es mejor hacer que Git funcione con su editor de código para simplificar las cosas. Por ejemplo, si usa código visual, puede agregarlo así
git config --global core.editor "code --wait"
. O puede buscar en Google cómo asociar que prefiere su editor de código con GIT.Ejemplo de
git rebase
Quería cambiar las últimas 2 confirmaciones que hice, así que proceso así:
Ahora uso
git rebase
para cambiar los 2 últimos mensajes de confirmación:$git rebase -i HEAD~2
Abre el editor de código y muestra esto:Como quiero cambiar el mensaje de confirmación para estas 2 confirmaciones. Entonces escribiré
r
oreword
en lugar depick
. Luego guarde el archivo y cierre la pestaña. Tenga en cuenta querebase
se ejecuta en un proceso de varios pasos, por lo que el siguiente paso es actualizar los mensajes. Tenga en cuenta también que las confirmaciones se muestran en orden cronológico inverso, por lo que la última confirmación se muestra en esa y la primera confirmación en la primera línea y así sucesivamente.Actualice los mensajes: Actualice el primer mensaje:
guardar y cerrar Editar el segundo mensaje
guardar y cerrar.
Recibirá un mensaje como este al final del rebase: lo
Successfully rebased and updated refs/heads/documentation
que significa que tiene éxito. Puede mostrar los cambios:Deseo que pueda ayudar a los nuevos usuarios :).
fuente
Para mí fue para eliminar algunas credenciales de un repositorio. Traté de rebase y me encontré con una tonelada de conflictos aparentemente no relacionados en el camino al intentar rebase - continuar. No te molestes en intentar cambiar tu base, usa la herramienta llamada BFG (brew install bfg) en mac.
fuente
Si aún no ha enviado las confirmaciones, puede volver a una confirmación anterior utilizando
git reset HEAD^[1,2,3,4...]
Por ejemplo
¡Vaya! Olvidé agregar file2 al primer commit ...
Esto agregará file2 a la primera confirmación.
fuente
Bueno, esta solución puede sonar muy tonta, pero puede salvarte en ciertas condiciones.
Un amigo mío se topó accidentalmente con algunos archivos enormes (cuatro archivos generados automáticamente que van desde 3 GB hasta 5 GB cada uno) y luego realizó algunos compromisos de código adicionales además de eso antes de darse cuenta del problema que
git push
ya no funcionaba.Los archivos se habían incluido en la lista,
.gitignore
pero después de cambiar el nombre de la carpeta del contenedor, ¡quedaron expuestos y comprometidos! Y ahora había algunas confirmaciones más del código además de eso, pero sepush
estaba ejecutando para siempre (¡tratando de cargar GB de datos!) Y finalmente fallaría debido a los límites de tamaño de archivo de Github .El problema con el rebase interactivo o algo similar era que tratarían de hurgar en estos enormes archivos y les tomaría una eternidad hacer cualquier cosa. Sin embargo, después de pasar casi una hora en la CLI, no estábamos seguros de si los archivos (y deltas) realmente se eliminan del historial o simplemente no se incluyen en las confirmaciones actuales. El empuje tampoco estaba funcionando y mi amigo estaba realmente atrapado.
Entonces, la solución que se me ocurrió fue:
~/Project-old
.~/Project
).cp -r
los archivos de la~/Project-old
carpeta a~/Project
.mv
editados e incluidos.gitignore
correctamente..git
carpeta en el clonado recientemente~/Project
por la anterior. ¡Ahí es donde viven los registros de la problemática historia!push
'ed.El mayor problema con esta solución es que trata con la copia manual de algunos archivos, y también combina todos los commits recientes en uno (obviamente con un nuevo commit-hash). B
Los grandes beneficios son que, es muy claro en cada paso, funciona muy bien para archivos grandes (así como para archivos confidenciales) , ¡y no deja ningún rastro en la historia!
fuente