Acabo de leer la modificación de un solo archivo en una confirmación anterior en git, pero desafortunadamente la solución aceptada 'reordena' las confirmaciones, que no es lo que quiero. Entonces esta es mi pregunta:
De vez en cuando, noto un error en mi código mientras trabajo en una función (no relacionada). git blame
Luego, un rápido revela que el error se introdujo hace algunas confirmaciones (yo comprometo bastante, por lo que generalmente no es la confirmación más reciente la que introdujo el error). En este punto, suelo hacer esto:
git stash # temporarily put my work aside
git rebase -i <bad_commit>~1 # rebase one step before the bad commit
# mark broken commit for editing
vim <affected_sources> # fix the bug
git add <affected_sources> # stage fixes
git commit -C <bad_commit> # commit fixes using same log message as before
git rebase --continue # base all later changes onto this
Sin embargo, esto sucede con tanta frecuencia que la secuencia anterior se vuelve molesta. Especialmente la 'rebase interactiva' es aburrida. ¿Hay algún atajo a la secuencia anterior, que me permita modificar una confirmación arbitraria en el pasado con los cambios por etapas? Soy perfectamente consciente de que esto cambia la historia, pero cometo errores con tanta frecuencia que me encantaría tener algo como
vim <affected_sources> # fix bug
git add -p <affected_sources> # Mark my 'fixup' hungs for staging
git fixup <bad_commit> # amend the specified commit with staged changes,
# rebase any successors of bad commit on rewritten
# commit.
¿Quizás un script inteligente que pueda reescribir confirmaciones usando herramientas de plomería o algo así?
rebase -i
?rebase --onto tmp bad-commit master
. Como está escrito, intentará aplicar el compromiso incorrecto al estado de compromiso fijo.Respuestas:
RESPUESTA ACTUALIZADA
Hace un tiempo,
--fixup
se agregó un nuevo argumento algit commit
que se puede usar para construir una confirmación con un mensaje de registro adecuado paragit rebase --interactive --autosquash
. Entonces, la forma más sencilla de arreglar una confirmación anterior es ahora:RESPUESTA ORIGINAL
Aquí hay un pequeño script de Python que escribí hace un tiempo que implementa esta
git fixup
lógica que esperaba en mi pregunta original. La secuencia de comandos asume que realizó algunos cambios y luego aplica esos cambios a la confirmación dada.NOTA : Este script es específico de Windows; busca
git.exe
y establece laGIT_EDITOR
variable de entorno usandoset
. Ajústelo según sea necesario para otros sistemas operativos.Usando este script puedo implementar precisamente el flujo de trabajo 'arreglar fuentes rotas, arreglar arreglos, ejecutar git fixup' que pedí:
fuente
git stash
ygit stash pop
alrededor de su rebase para que ya no requiera un directorio de trabajo limpiogit stash
ygit stash pop
: tienes razón, pero desafortunadamentegit stash
es mucho más lento en Windows que en Linux o OS / X. Dado que mi directorio de trabajo suele estar limpio, omití este paso para no ralentizar el comando.git rebase -i --fixup
, y se modificó a partir de la confirmación arreglada como punto de partida, por lo que el argumento sha no era necesario en mi caso.git config --global rebase.autosquash true
Lo que hago es:
Su editor se abrirá con una lista de las últimas 5 confirmaciones, listas para ser entrometidas. Cambio:
...a:
Guarde y salga de su editor, y la corrección se volverá a colocar en la confirmación a la que pertenece.
Después de haberlo hecho varias veces, lo hará en segundos mientras duerme. El rebase interactivo es la característica que realmente me vendió en git. Es increíblemente útil para esto y más ...
fuente
--autosquash
cambiogit rebase
, que reordena automáticamente los pasos en el editor. Vea mi respuesta para un script que aprovecha esto para implementar ungit fixup
comando.Un poco tarde para la fiesta, pero aquí hay una solución que funciona como imaginó el autor.
Agregue esto a su .gitconfig:
Uso de ejemplo:
Sin embargo, si tiene cambios sin etapas, debe guardarlos antes de la reorganización.
Puede modificar el alias para ocultarlo automáticamente, en lugar de dar una advertencia. Sin embargo, si la reparación no se aplica correctamente, necesitará sacar el alijo manualmente después de solucionar los conflictos. Hacer tanto guardar como hacer estallar manualmente parece más consistente y menos confuso.
fuente
git fixup HEAD
es para lo que creé un alias. También me vendría bien enmendar eso, supongo.amend = commit --amend --reuse-message=HEAD
Luego puede simplemente escribirgit amend
ogit amend -a
y omitir el editor para el mensaje de confirmación.Para arreglar una confirmación:
donde a0b1c2d3 es el compromiso que desea corregir y donde 2 es el número de confirmaciones +1 pegadas que desea cambiar.
Nota: git rebase --autosquash sin -i no funcionó pero con -i funcionó, lo cual es extraño.
fuente
--autosquash
sin-i
todavía no funciona.EDITOR=true git rebase --autosquash -i
ACTUALIZACIÓN: ahora se puede encontrar una versión más limpia del script aquí: https://github.com/deiwin/git-dotfiles/blob/docs/bin/git-fixup .
He estado buscando algo similar. Sin embargo, este script de Python parece demasiado complicado, por lo tanto, he elaborado mi propia solución:
Primero, mis alias de git se ven así (tomados de aquí ):
Ahora la función bash se vuelve bastante simple:
Este código primero prepara todos los cambios actuales (puede eliminar esta parte, si desea preparar los archivos usted mismo). Luego crea la reparación (también se puede usar squash, si eso es lo que necesita) commit. Después de eso, comienza una rebase interactiva con la
--autosquash
bandera en el padre de la confirmación que le da como argumento. Eso abrirá tu editor de texto configurado, para que puedas verificar que todo es como esperas y simplemente cerrar el editor finalizará el proceso.La
if [[ "$1" == HEAD* ]]
parte (tomada de aquí ) se usa, porque si usa, por ejemplo, HEAD ~ 2 como su referencia de compromiso (el compromiso con el que desea corregir los cambios actuales), entonces el HEAD se desplazará después de que se haya creado el compromiso de reparación y necesitaría usar HEAD ~ 3 para referirse a la misma confirmación.fuente
Puede evitar la etapa interactiva utilizando un editor "nulo":
Esto se utilizará
/bin/true
como editor, en lugar de/usr/bin/vim
. Siempre acepta cualquier sugerencia de git, sin preguntar.fuente
call(["set", "GIT_EDITOR=true", "&&", git, "rebase", "-i" ...
).Lo que realmente me molestó del flujo de trabajo de reparación fue que tenía que averiguar yo mismo en qué compromiso quería aplastar el cambio cada vez. Creé un comando "git fixup" que ayuda con esto.
Este comando crea confirmaciones de reparación, con la magia añadida de que utiliza git-deps para encontrar automáticamente la confirmación relevante, por lo que el flujo de trabajo a menudo se reduce a:
Esto solo funciona si los cambios por etapas se pueden atribuir sin ambigüedades a una confirmación en particular en el árbol de trabajo (entre el maestro y HEAD). Encuentro que ese es el caso muy a menudo para el tipo de pequeños cambios para los que uso esto, por ejemplo, errores tipográficos en comentarios o nombres de métodos recién introducidos (o renombrados). Si este no es el caso, al menos mostrará una lista de confirmaciones candidatas.
Utilizo mucho esto en mi flujo de trabajo diario, para integrar rápidamente pequeños cambios en líneas previamente cambiadas en confirmaciones en mi rama de trabajo. El guión no es tan hermoso como podría ser, y está escrito en zsh, pero me ha estado haciendo el trabajo lo suficientemente bien durante un buen tiempo, ahora que nunca sentí la necesidad de reescribirlo:
https://github.com/Valodim/git-fixup
fuente
Puede crear una reparación para un archivo en particular utilizando este alias.
Si ha realizado algunos cambios
myfile.txt
pero no desea ponerlos en una nueva confirmación,git fixup-file myfile.txt
creará unfixup!
para la confirmación dondemyfile.txt
se modificó por última vez, y luego lo harárebase --autosquash
.fuente
git rebase
no se llamara automáticamente.commit --fixup
yrebase --autosquash
son geniales, pero no hacen lo suficiente. Cuando tengo una secuencia de confirmacionesA-B-C
y escribo algunos cambios más en mi árbol de trabajo que pertenecen a una o más de esas confirmaciones existentes, tengo que mirar manualmente el historial, decidir qué cambios pertenecen a qué confirmaciones, organizarlos y crear elfixup!
se compromete. Pero git ya tiene acceso a suficiente información para poder hacer todo eso por mí, así que escribí un script en Perl que hace precisamente eso.Por cada trozo en
git diff
el script, se usagit blame
para encontrar la confirmación que tocó por última vez las líneas relevantes y las llamadasgit commit --fixup
para escribir lasfixup!
confirmaciones apropiadas , esencialmente haciendo lo mismo que estaba haciendo manualmente antes.Si lo encuentra útil, siéntase libre de mejorarlo e iterarlo y tal vez algún día tengamos dicha función en forma
git
adecuada. Me encantaría ver una herramienta que pueda comprender cómo se debe resolver un conflicto de fusión cuando ha sido introducido por una rebase interactiva.fuente
Escribí una pequeña función de shell llamada
gcf
para realizar la confirmación de reparación y la rebase automáticamente:Por ejemplo, puede parchear la segunda confirmación antes de la última con:
gcf HEAD~~
Aquí está la función . Puedes pegarlo en tu
~/.bashrc
Se utiliza
--autostash
para guardar y hacer estallar cualquier cambio no confirmado si es necesario.--autosquash
requiere un--interactive
rebase, pero evitamos la interacción usando un maniquíEDITOR
.--no-fork-point
protege las confirmaciones de ser eliminadas silenciosamente en situaciones raras (cuando ha bifurcado una nueva rama y alguien ya ha vuelto a basar las confirmaciones pasadas).fuente
No conozco una forma automatizada, pero aquí hay una solución que podría ser más fácil de manipular por humanos:
fuente
git fixup
comando.--autosquash
Recomendaría https://github.com/tummychow/git-absorb :
fuente