Come rimuovere le voci del registro di commit selezionate da un repository Git mantenendo le loro modifiche?


241

Vorrei rimuovere le voci del registro di commit selezionate da un albero di commit lineare, in modo che le voci non vengano visualizzate nel registro di commit.

Il mio albero di commit è simile a:

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

Vorrei rimuovere le voci B e C in modo che non vengano visualizzate nel registro di commit, ma le modifiche da A a D dovrebbero essere preservate. Forse introducendo un singolo commit, in modo che B e C diventino BC e l'albero assomigli.

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

O, idealmente, dopo A arriva D direttamente. D 'rappresenta le modifiche da A a B, da B a C e da C a D.

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

È possibile? se si, come?

Questo è un progetto abbastanza nuovo, quindi non ci sono filiali al momento, quindi non ci sono nemmeno fusioni.


@ xk0der: "commit" è il termine giusto qui. rebasepuò rimuovere vecchi / creare nuovi commit. Non so che cosa significhi "commit delle voci di registro".
jfs,

@JFSebastian Non vedo un problema con "commit commit" - Registro di tutti i commit. E volevo eliminare alcune voci dal registro, mantenendo le modifiche effettive (i commit).
xk0der,

@ xk0der: i commit di git sono indirizzabili al contenuto, ad esempio se si cambia qualcosa in un commit, ad esempio, il suo messaggio di registro; crei un nuovo commit. Puoi leggere il commit di git senza git e vedere di persona .
jfs,

@JFSebastian - Grazie per i collegamenti - Lo so - Ma questo tecnicismo cambia davvero il problema che stavo affrontando e come l'ho presentato? Non credo. Alla fine: volevo rimuovere "i messaggi di registro di commit" senza rimuovere le "modifiche di commit" - Per favore rileggi la mia domanda - specialmente il secondo paragrafo. Per aggiungere altro git logmostra il "commit log" git-scm.com/docs/git-log . E volevo eliminare due voci da quel registro, non le modifiche.
xk0der,

Risposte:


273

git-rebase (1) fa esattamente questo.

$ git rebase -i HEAD~5

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

  1. Non utilizzare git-rebasesu commit pubblici (remoti).
  2. Assicurarsi che la directory di lavoro sia pulita ( commito stashle modifiche correnti).
  3. Esegui il comando sopra. Lancia il tuo $EDITOR.
  4. Sostituisci pickprima Ce Dentro squash. Combinerà C e D in B. Se si desidera eliminare un commit, è sufficiente eliminare la sua riga.

Se ti sei perso, digita:

$ git rebase --abort  

Grazie per la risposta rapida. Quindi controllo A e faccio un rebase, qualcosa del genere git rebase -i D [A]?
xk0der


3
Come possiamo farlo su repository remoti?
Eray,

6
@Eray: solo le push -ftue modifiche. Non farlo se non lavori da solo.
jfs,

2
@ ripper234: ho corretto i collegamenti per indicare git-rebasela macchina manuale e di ritorno per il post sul 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

Anche questo funziona e mi ha aiutato a capire cos'è un soft reset. Certo, anche la risposta "migliore" è giusta e più breve, ma grazie anche per questa risposta.
CG

41

Ecco un modo per rimuovere un ID di commit specifico conoscendo solo l'id di commit che desideri rimuovere.

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

Si noti che ciò rimuove effettivamente la modifica introdotta dal commit.


7
La TESTA aggiuntiva in questo comando farà terminare il rebase con una "TESTA staccata" che è indesiderabile. Dovrebbe essere omesso.
Frosty,

3
Questo ripristina le modifiche introdotte dal mio commit-id, l'OP vuole conservare le modifiche, semplicemente schiacciare i commit.
CB Bailey,

1
-1 perché non fa ciò che l'OP ha chiesto (piuttosto distrugge qualcosa che voleva esplicitamente conservare).
Emil Styrke,

1
Anche se non fa ciò che l'OP ha richiesto, era esattamente quello di cui avevo bisogno, quindi +1 per una risposta utile.
Edvins,

20

Per espandere la risposta di JF Sebastian:

È possibile utilizzare git-rebase per apportare facilmente tutti i tipi di modifiche alla cronologia di commit.

Dopo aver eseguito git rebase --interactive ottieni quanto segue nel tuo $ 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

È possibile spostare le righe per modificare l'ordine delle commit ed eliminare le righe per rimuovere tale commit. Oppure puoi aggiungere un comando per combinare (schiacciare) due commit in un singolo commit (il commit precedente è il commit precedente), modificare i commit (ciò che è stato modificato) o riformulare i messaggi di commit.

Penso che scegliere significhi solo che vuoi lasciare quel commit da solo.

(L'esempio è da qui )



3

Un altro modo,

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

scegli l'hash che vuoi usare come base e il comando sopra dovrebbe renderlo interattivo in modo da poter schiacciare tutti i messaggi principali (devi lasciare il più vecchio)


2

Trovo questo processo molto più sicuro e più facile da capire creando un altro ramo dalla SHA1 di A e selezionando le modifiche desiderate in modo da poter essere soddisfatto dell'aspetto di questo nuovo ramo. Successivamente, è facile rimuovere il vecchio ramo e rinominare quello nuovo.

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>

se lo fai perderai anche l'impegno E, vero? Come ho capito, si elimina il master e si rinomina la rilavorazione come master (considerando che il flusso ABCDE è il ramo master).
Renan Bandeira,

1

Ho appena raccolto le risposte di tutte le persone: (m new to git plz usarlo solo come riferimento)

git rebase per eliminare eventuali commit

log git

-first check from which commit you want to rebase

git rebase -i HEAD ~ 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)

Quindi lo schermo sarà simile a questo:

pick 0c2236d Aggiunta nuova riga.

Sostituisci 2a1cd65..0c2236d su 2a1cd65 (1 comando)

#

comandi:

p, pick = usa commit

r, reword = usa commit, ma modifica il messaggio di commit

e, edit = usa commit, ma ferma per la modifica

s, squash = usa il commit, ma si fonde nel commit precedente

f, fixup = like "squash", ma scarta il messaggio di log di questo commit

x, comando exec = run (il resto della riga) usando shell

d, drop = rimuovi commit

#

Queste linee possono essere riordinate; vengono eseguiti dall'alto verso il basso.

#

Se rimuovi una riga qui, QUESTO IMPEGNO SARÀ PERSO.

#

Tuttavia, se rimuovi tutto, il rebase verrà interrotto.

#

Nota che i commit vuoti sono commentati ~ ~

~
~
~
~
~
~
~
~
~

Qui cambia la prima riga secondo le tue necessità (usando i comandi sopra elencati, ad esempio 'drop' per rimuovere il commit ecc.) Una volta terminata la modifica, premi ': x' per salvare ed uscire dall'editor (questo è solo per l'editor vim)

E poi

git push

Se il problema si presenta, è necessario inviare forzatamente le modifiche al telecomando (È MOLTO CRITICO: non forzare la spinta se si lavora in gruppo)

git push -f origine


-1

Puoi usare git cherry-pick per questo. 'cherry-pick' applicherà un commit sul ramo in cui ci si trova ora.

Quindi fa

git rebase --hard <SHA1 of A>

quindi applicare gli commit D ed E.

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

Ciò salterà il commit B e C. Detto questo, potrebbe essere impossibile applicare il commit D al ramo senza B, quindi YMMV.


2
L'OP vuole combinare i commit B, C, D, per non cancellare le loro modifiche.
jfs,

3
Penso che volevi dire reset --hard, non rebase --hard(cosa che non esiste)
Mauricio Scheffer,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.