2009-03-13 2 views
44

Ho apportato una modifica a uno script e l'ho eseguito. Poi ho apportato alcune altre modifiche e le ho trasferite su un repository remoto e così via.Annulla la modifica di git (non riscrive la cronologia)

Poi ho capito che il primo cambiamento che ho menzionato era stupido, e voglio annullarlo .. Posso "unpply" che esegue il commit, senza copiare/incollare manualmente il diff?

Per fare un esempio: ho due file, a.py e b.py:

Commit 1: 
I delete a function in a.py 

Commit 2: 
I change a few lines in b.py 

Commit 3: 
I change the docstring in a.py 

Posso annullare quella funzione cancellazione, e farla apparire come "commit 4" (piuttosto che l'eliminazione di commettere 1)

+13

Oi, è "realizzato", non "realizzato"! (Io non sono americano ..) – dbr

+3

La tua ortografia è stata corretta da un popolo che usa metri e piedi per misurare le cose ... ah ah – FaddishWorm

risposta

57

Sì, è possibile utilizzare git revert per questo. Vedere lo git manual section on this per ulteriori informazioni.

Il succo è che si può dire:

git revert 4f4k2a 

Dove 4f4k2a è l'id del impegnano che desideri annullare, e cercherà di annullarla.

+0

Aha, ovviamente, grazie! – dbr

+0

BTW! Cancellerà quell'oggetto commit 1 – aatifh

+0

Hm, non sembra cancellare il commit annullato .. – dbr

31

Solo un commento:

git revert aCommit 

vuol riportare il tutti commit (come in "tutti i file parte del commit"):
si calcola una patch contrario, si applica sulla testa e impegnarsi.

Così due problemi qui (il primo è facilmente risolto):

  • lo fa sempre commettere, quindi si consiglia di aggiungere -no-commit opzione: "git revert --no-commit aCommit": questo è utile quando un ritorno più di uno commette l'effetto sul tuo indice di fila.
  • non si applica per un file specifico (cosa succede se a.py faceva parte di un commit con 1000 altre modifiche che potresti non voler annullare)?
    Per questo, se si desidera estrarre i file specifici come erano in un altro commit, si dovrebbe vedere git-checkout, in particolare la sintassi git checkout <commit> <filename> (che non è esattamente quello che ti serve in questo caso, però)

Easy Git (Elijah Newren) ha cercato di portare un "ripristino completo" più al Git Mailing list; ma senza molto successo:

Le persone di tanto in tanto desiderano "annullare le modifiche".

Ora, questo può essere:

  • i cambiamenti tra 32 e 29 revisioni fa,
  • potrebbe essere tutte le modifiche dall'ultimo commit,
  • potrebbe essere i cambiamenti dal 3 commette fa oppure
  • potrebbe essere solo uno specifico commit.
  • L'utente potrebbe voler sottoinsieme di tali ritorni al solo file specifici,

(eg revert è documented here, ma io non sono sicuro che è parte della corrente di distribuzione, ad esempio, però)

ma tutto si riduce a "ripristinare le modifiche" alla fine.

eg revert --since HEAD~3 # Undo all changes since HEAD~3 
eg revert --in HEAD~8  # much like git revert HEAD~8, but nocommit by default 
eg revert --since HEAD foo.py # Undo changes to foo.py since last commit 
eg revert foo.py    # Same as above 
eg revert --in trial~7 bar.c baz. # Undo changes made in trial~7 to bar.[ch] 

Sono questi tipi di "ripristino di dati" in realtà in modo diverso che ci dovrebbe essere bisogno di diversi comandi, o che alcune di queste operazioni non dovrebbero essere sostenuti con il semplice comando Versione?
Certo, la maggior parte degli utenti la maggior parte delle volte userà probabilmente il modulo "eg revert FILE1 FILE2..." , ma non ho visto il danno nel supportare le funzionalità extra.

Inoltre ... c'è qualcosa di fondamentale che impedirebbe al core git di adottare un simile comportamento?

Elia

Nota: si impegna per default non hanno senso per il comando generalizzata revert, e "git revert REVISION" sarebbe errore fuori con le istruzioni (dicendo all'utente di aggiungere il flag --in).


Diciamo che avere, di 50 impegnati, 20 file ci si rende conto che il vecchio commit X ha introdotto modifiche che non avrebbe dovuto aver luogo.
Un po 'di impianto idraulico è in ordine.
Quello che vi serve è un modo per elencare tutti i file specifici necessari per tornare
(come in "per annullare le modifiche apportate in commettere X, mantenendo tutte le successive modifiche"),
e poi, per ogni di loro:

git-merge-file -p a.py X X^ 

il problema qui è quello di recuperare la funzione perduta senza cancellando tutte le modifiche successive a.py si potrebbe desiderare di mantenere.
Questa tecnica è talvolta denominata "unione negativa".

Dal git merge-file <current-file> <base-file> <other-file>means:
incorpora tutte le modifiche che portano dal <base-file>-<other-file> in <current-file>, è possibile ristabilire la funzione cancellato dallo dicendo che si desidera incorporare tutte le modifiche)

    .
  • da: X (dove la funzione è stata cancellata)
  • a: X^(il commit precedente prima di X, in cui la funzione era ancora lì)

Nota: il '-p' argomento che consente di rivedere prima le modifiche senza fare nulla sul file corrente. Quando sei sicuro, rimuovi l'opzione.

Nota: il git merge-file è not that simple: non è possibile fare riferimento le versioni precedenti del file proprio così.
(si avrebbe più e più volte il messaggio frustrante: error: Could not stat X)
Dovete:

git cat-file blob a.py > tmp/ori # current file before any modification 
git cat-file blob HEAD~2:a.py > tmp/X # file with the function deleted 
git cat-file blob HEAD~3:a.py > tmp/F # file with the function which was still there 

git merge-file a.py tmp/X tmp/F # basically a RCS-style merge 
           # note the inversed commit order: X as based, then F 
           # that is why is is a "negative merge" 
diff -u a.py tmp/ori # eyeball the merge result 
git add a.py 
git commit -m "function restored" # and any other changes made from X are preserved! 

Se questo deve essere fatto per un gran numero di file all'interno di un precedente impegno ... un po ' scripting è in ordine;)

+0

Non credo che sia necessario il trattino: 'git checkout ' – Paul

5

per ripristinare le modifiche solo un file all'interno di un commit, come VonC sottolineato, avrei checkout il ramo (master o del tronco o altro) e quindi checkout la versione del file che volevo ripristina e t reat come un nuovo impegno:

$ git checkout trunk 
$ git checkout 4f4k2a^ a.py 
$ git add a.py 
$ git diff    #verify I'm only changing what I want; edit as needed 
$ git commit -m 'recover function deleted from a.py in 4f4k2a' 

C'è probabilmente un comando idraulico che farlo direttamente, ma io non lo uso se lo sapevo. Non è che non mi fido di Git, ma non mi fido di me stesso - non mi fiderei di averlo saputo senza guardare ciò che è stato cambiato in quel file in quel commit e da allora. E una volta che guardo, è più semplice creare un nuovo commit modificando lo diff. Forse è solo uno stile di lavoro personale.

+1

Due commenti: 1/se la funzione è stato eliminato nel commit 4f4k2a per a.py, non dovresti pagare a.py dal commit * precedente *? (uno * prima di * 4f4k2a, come in 4f4k2a ^?) 2/Non si cancellerebbe completamente la versione corrente di a.py? Cosa succede se voglio mantenere le successive modifiche? – VonC

+0

Preferisco creare una "unione negativa" con git-merge-file: vedere la mia risposta completata – VonC

+0

1) Sì. Grazie per aver corretto l'errore di battitura. 2) Sì - Ecco perché dovrei vedere il diff. In realtà utilizzerei una diffrazione hackerata in modalità unione a 3 vie, che mi mostra una differenza più utile con la quale sono abituato a lavorare, ma git diff è utilizzabile. Il punto è: sono fiducioso solo se ho visto il codice. – Paul

1

Dai un'occhiata allo this git revert question. Sembra esserci un problema durante il ripristino dei commit precedenti se non in una sequenza consecutiva incluso il commit più recente.

+0

Fare l'upvoting perché era utile, ma in realtà dovrebbe essere un commento piuttosto che una risposta. – tripleee