¿Cómo eliminar las entradas de registro de confirmación seleccionadas de un repositorio de Git manteniendo sus cambios?

241

Me gustaría eliminar las entradas de registro de confirmación seleccionadas de un árbol de confirmación lineal, para que las entradas no se muestren en el registro de confirmación.

Mi árbol de confirmación se parece a:

R--A--B--C--D--E--HEAD

Me gustaría eliminar las entradas B y C para que no se muestren en el registro de confirmación, pero los cambios de A a D deben conservarse. Tal vez introduciendo un solo commit, para que B y C se conviertan en BC y el árbol se vea como.

R--A--BC--D--E--HEAD

O, idealmente, después de A viene D directamente. D 'representa los cambios de A a B, B a C y C a D.

R--A--D'--E--HEAD

es posible? Si es así, ¿cómo?

Este es un proyecto bastante nuevo, por lo que no tiene sucursales a partir de ahora, por lo tanto, tampoco hay fusiones.

xk0der
fuente
@ xk0der: "commits" es el término correcto aquí. rebasepuede eliminar viejas / crear nuevas confirmaciones. No sé qué significa "confirmar entradas de registro".
jfs
@JFSebastian No veo un problema con el "registro de confirmación": registro de todas las confirmaciones. Y quería eliminar algunas entradas del registro, manteniendo los cambios reales (los commits).
xk0der
@ xk0der: las confirmaciones de git son direccionables por contenido, es decir, si cambia algo en una confirmación, por ejemplo, su mensaje de registro; creas una nueva confirmación. Podrías leer git's commit sin git y verlo por ti mismo .
jfs
@JFSebastian - Gracias por los enlaces - Lo sé - ¿Pero ese tecnicismo realmente cambia el problema que estaba enfrentando y cómo lo planteé? Supongo que no. Al final: quería eliminar "los mensajes de registro de confirmación", sin eliminar los "cambios de confirmación". Vuelva a leer mi pregunta, especialmente el segundo párrafo. Para agregar más git logmuestra el "registro de confirmación" git-scm.com/docs/git-log . Y quería deshacerme de dos entradas de ese registro, no los cambios.
xk0der

Respuestas:

273

git-rebase (1) hace exactamente eso.

$ git rebase -i HEAD~5

git awsome-ness [git rebase --interactive] contiene un ejemplo.

  1. No lo use git-rebaseen confirmaciones públicas (remotas).
  2. Asegúrese de que su directorio de trabajo esté limpio ( commito stashsus cambios actuales).
  3. Ejecute el comando anterior. Lanza tu $EDITOR.
  4. Reemplazar pickantes Cy Dpor squash. Fusionará C y D en B. Si desea eliminar una confirmación, simplemente elimine su línea.

Si está perdido, escriba:

$ git rebase --abort  
jfs
fuente
Gracias por la rápida respuesta. Entonces, ¿pago A y hago una nueva versión, algo así git rebase -i D [A]?
xk0der
3
¿Cómo podemos hacerlo en repositorios remotos?
Eray
66
@Eray: solo push -ftus cambios. No lo hagas si no estás trabajando solo.
jfs
2
@ ripper234: arreglé los enlaces al git-rebasemanual de puntos y la máquina de retroceso para la publicación del blog.
jfs
75
# detach head and move to D commit
git checkout <SHA1-for-D>

# move HEAD to A, but leave the index and working tree as for D
git reset --soft <SHA1-for-A>

# Redo the D commit re-using the commit message, but now on top of A
git commit -C <SHA1-for-D>

# Re-apply everything from the old D onwards onto this new place 
git rebase --onto HEAD <SHA1-for-D> master
CB Bailey
fuente
Esto también funciona y me ayudó a comprender qué es un restablecimiento parcial. De acuerdo, la respuesta "superior" también es correcta y más corta, pero gracias por esta respuesta también.
cgp
41

Aquí hay una manera de eliminar una identificación de confirmación específica conociendo solo la identificación de confirmación que desea eliminar.

git rebase --onto commit-id^ commit-id

Tenga en cuenta que esto realmente elimina el cambio que introdujo el commit.

rado
fuente
77
El HEAD extra en este comando hará que el rebase termine con un 'HEAD separado' que no es deseable. Debe ser omitido.
Frosty
3
Esto revierte los cambios introducidos en mi commit-id, el OP quiere retener los cambios, solo aplastar los commits.
CB Bailey
1
-1 porque no hace lo que le pidió el OP (más bien destruye algo que quería conservar explícitamente).
Emil Styrke
1
Aunque no hace lo que el OP solicitó, era exactamente lo que necesitaba, así que +1 para una respuesta útil.
Edvins
20

Para ampliar la respuesta de JF Sebastian:

Puede usar git-rebase para realizar fácilmente todo tipo de cambios en su historial de confirmaciones.

Después de ejecutar git rebase --interactive, obtienes lo siguiente en tu $ EDITOR:

pick 366eca1 This has a huge file
pick d975b30 delete foo
pick 121802a delete bar
# Rebase 57d0b28..121802a onto 57d0b28
#
# 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

Puede mover líneas para cambiar el orden de las confirmaciones y eliminar líneas para eliminar esa confirmación. O bien, puede agregar un comando para combinar (aplastar) dos confirmaciones en una única confirmación (la confirmación anterior es la confirmación anterior), editar confirmaciones (lo que se modificó) o redactar mensajes de confirmación.

Creo que elegir solo significa que quieres dejar ese compromiso solo.

(El ejemplo es de aquí )

revs idbrii
fuente
14

Puede eliminar de forma no interactiva B y C en su ejemplo con:

git rebase --onto HEAD~5 HEAD~3 HEAD

o simbólicamente

git rebase --onto A C HEAD

Tenga en cuenta que los cambios en B y C no serán en D; Se habrán ido .

Cabeza
fuente
Consulte aquí para obtener más información: sethrobertson.github.io/GitFixUm/fixup.html#remove_deep
Max
3

Una forma más

git rebase -i ad0389efc1a79b1f9c4dd6061dca6edc1d5bb78a (C's hash)
and
git push origin master  -f

elija el hash que desea usar como base, y el comando anterior debe hacerlo interactivo para que pueda aplastar todos los mensajes principales (debe dejar el más antiguo)

resultados
fuente
2

Encuentro este proceso mucho más seguro y fácil de entender al crear otra rama del SHA1 de A y seleccionar los cambios deseados para asegurarme de que estoy satisfecho con el aspecto de esta nueva rama. Después de eso, es fácil eliminar la rama anterior y cambiar el nombre de la nueva.

git checkout <SHA1 of A>
git log #verify looks good
git checkout -b rework
git cherry-pick <SHA1 of D>
....
git log #verify looks good
git branch -D <oldbranch>
git branch -m rework <oldbranch>
Eric Woodruff
fuente
si haces esto, también perderás el compromiso E, ¿no? Como entendí, eliminas el maestro y cambias el nombre del retrabajo como maestro (considerando que el flujo ABCDE es la rama maestra).
Renan Bandeira
1

Acabo de recopilar las respuestas de todas las personas: (soy nuevo en git, por favor úselo solo como referencia)

git rebase para eliminar cualquier confirmación

registro de git

-first check from which commit you want to rebase

git rebase -i CABEZA ~ 1

-Here i want to rebase on the second last commit- commit count starts from '1')
-this will open the command line editor (called vim editor i guess)

Entonces la pantalla se verá así:

pick 0c2236d Se agregó una nueva línea.

Rebase 2a1cd65..0c2236d en 2a1cd65 (1 comando)

# #

Comandos:

p, pick = usar commit

r, reword = use commit, pero edite el mensaje de commit

e, edit = use commit, pero deténgase para enmendar

s, squash = usar commit, pero fusionarse con el commit anterior

f, arreglo = como "squash", pero descarta el mensaje de registro de este commit

x, exec = ejecutar comando (el resto de la línea) usando shell

d, soltar = eliminar confirmación

# #

Estas líneas se pueden reordenar; Se ejecutan de arriba a abajo.

# #

Si elimina una línea aquí, ESTE COMITÉ SE PERDERÁ.

# #

Sin embargo, si elimina todo, se cancelará el rebase.

# #

Tenga en cuenta que las confirmaciones vacías se comentan ~ ~

~
~
~
~
~
~
~
~
~

Aquí cambie la primera línea según sus necesidades (utilizando los comandos enumerados anteriormente, es decir, 'soltar' para eliminar commit, etc.) Una vez realizada la edición, presione ': x' para guardar y salir del editor (esto es solo para el editor vim)

Y entonces

git push

Si muestra un problema, entonces necesita forzar los cambios a control remoto (ES MUY CRÍTICO: no fuerce el empuje si está trabajando en equipo)

git push -f origen

Shrinath Kopare
fuente
-1

Puedes usar git cherry-pick para esto. 'cherry-pick' aplicará un commit en la rama en la que estás ahora.

entonces hazlo

git rebase --hard <SHA1 of A>

luego aplique las confirmaciones D y E.

git cherry-pick <SHA1 of D>
git cherry-pick <SHA1 of E>

Esto omitirá la confirmación B y C. Dicho esto, podría ser imposible aplicar la confirmación D a la rama sin B, por lo que YMMV.

Rory
fuente
2
El OP quiere combinar las confirmaciones B, C, D, no eliminar sus cambios.
jfs
3
Creo que quisiste decir reset --hard, no rebase --hard(que no existe)
Mauricio Scheffer