Quello che vuoi fare è altamente distruttivo se hai pubblicato la cronologia per altri sviluppatori. Consulta la sezione "Ripristino dal rebase upstream" nella git rebase
documentazione per i passaggi necessari dopo aver riparato la cronologia.
Hai almeno due opzioni: git filter-branch
e un rebase interattivo, entrambi spiegati di seguito.
utilizzando git filter-branch
Ho avuto un problema simile con ingombranti dati di test binari da un'importazione Subversion e ho scritto sulla rimozione di dati da un repository git .
Dì che la tua cronologia git è:
$ git lola --name-status
* f772d66 (HEAD, master) Login page
| A login.html
* cb14efd Remove DVD-rip
| D oops.iso
* ce36c98 Careless
| A oops.iso
| A other.html
* 5af4522 Admin page
| A admin.html
* e738b63 Index
A index.html
Si noti che git lola
è un alias non standard ma molto utile. Con lo --name-status
switch, possiamo vedere le modifiche dell'albero associate ad ogni commit.
Nel commit "Careless" (il cui nome dell'oggetto SHA1 è ce36c98) il file oops.iso
è il DVD-rip aggiunto per errore e rimosso nel commit successivo, cb14efd. Utilizzando la tecnica descritta nel suddetto post di blog, il comando da eseguire è:
git filter-branch --prune-empty -d /dev/shm/scratch \
--index-filter "git rm --cached -f --ignore-unmatch oops.iso" \
--tag-name-filter cat -- --all
Opzioni:
--prune-empty
rimuove i commit che diventano vuoti ( cioè non cambiano l'albero) come risultato dell'operazione di filtro. Nel caso tipico, questa opzione produce una cronologia più pulita.
-d
nomina una directory temporanea che non esiste ancora da utilizzare per creare la cronologia filtrata. Se si esegue su una moderna distribuzione Linux, la specifica di un albero /dev/shm
comporterà un'esecuzione più rapida .
--index-filter
è l'evento principale e viene eseguito contro l'indice in ogni passaggio della cronologia. Si desidera rimuovere oops.iso
ovunque si trovi, ma non è presente in tutti i commit. Il comando git rm --cached -f --ignore-unmatch oops.iso
cancella il DVD-rip quando è presente e non fallisce altrimenti.
--tag-name-filter
descrive come riscrivere i nomi dei tag. Un filtro di cat
è l'operazione di identità. Il tuo repository, come nell'esempio sopra, potrebbe non avere alcun tag, ma ho incluso questa opzione per la massima generalità.
--
specifica la fine delle opzioni a git filter-branch
--all
di seguito --
è una scorciatoia per tutti i riferimenti. Il tuo repository, come l'esempio sopra, può avere solo un ref (master), ma ho incluso questa opzione per la generalità completa.
Dopo un po 'di agitazione, la storia è ora:
$ git lola --name-status
* 8e0a11c (HEAD, master) Login page
| A login.html
* e45ac59 Careless
| A other.html
|
| * f772d66 (refs/original/refs/heads/master) Login page
| | A login.html
| * cb14efd Remove DVD-rip
| | D oops.iso
| * ce36c98 Careless
|/ A oops.iso
| A other.html
|
* 5af4522 Admin page
| A admin.html
* e738b63 Index
A index.html
Si noti che il nuovo commit "Careless" aggiunge solo other.html
e che il commit "Remove DVD-rip" non si trova più nel ramo master. La filiale etichettata refs/original/refs/heads/master
contiene i tuoi commit originali nel caso in cui tu abbia commesso un errore. Per rimuoverlo, seguire i passaggi in "Elenco di controllo per la riduzione di un repository".
$ git update-ref -d refs/original/refs/heads/master
$ git reflog expire --expire=now --all
$ git gc --prune=now
Per un'alternativa più semplice, clonare il repository per scartare i bit indesiderati.
$ cd ~/src
$ mv repo repo.old
$ git clone file:///home/user/src/repo.old repo
L'uso di un file:///...
URL clone copia gli oggetti anziché solo la creazione di hardlink.
Ora la tua storia è:
$ git lola --name-status
* 8e0a11c (HEAD, master) Login page
| A login.html
* e45ac59 Careless
| A other.html
* 5af4522 Admin page
| A admin.html
* e738b63 Index
A index.html
I nomi degli oggetti SHA1 per i primi due commit ("Indice" e "Pagina di amministrazione") sono rimasti invariati perché l'operazione di filtro non ha modificato tali commit. "Careless" ha perso oops.iso
e "Login page" ha ottenuto un nuovo genitore, quindi i suoi SHA1 sono cambiati.
Rebase interattivo
Con una storia di:
$ git lola --name-status
* f772d66 (HEAD, master) Login page
| A login.html
* cb14efd Remove DVD-rip
| D oops.iso
* ce36c98 Careless
| A oops.iso
| A other.html
* 5af4522 Admin page
| A admin.html
* e738b63 Index
A index.html
vuoi rimuovere oops.iso
da “Careless” come se non lo avessi mai aggiunto, e quindi “Remove DVD-rip” è inutile per te. Pertanto, il nostro piano per entrare in un rebase interattivo è quello di mantenere la "Pagina di amministrazione", modificare "Careless" e scartare "Rimuovi DVD-rip".
L'esecuzione $ git rebase -i 5af4522
avvia un editor con i seguenti contenuti.
pick ce36c98 Careless
pick cb14efd Remove DVD-rip
pick f772d66 Login page
# Rebase 5af4522..f772d66 onto 5af4522
#
# 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
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#
In esecuzione del nostro piano, lo modifichiamo in
edit ce36c98 Careless
pick f772d66 Login page
# Rebase 5af4522..f772d66 onto 5af4522
# ...
Cioè, eliminiamo la riga con "Rimuovi DVD-rip" e cambiamo l'operazione su "Careless" per essere edit
piuttosto che pick
.
Salvare l'uscita dall'editor ci lascia al prompt dei comandi con il seguente messaggio.
Stopped at ce36c98... Careless
You can amend the commit now, with
git commit --amend
Once you are satisfied with your changes, run
git rebase --continue
Come ci dice il messaggio, siamo impegnati nel commit "Careless" che vogliamo modificare, quindi eseguiamo due comandi.
$ git rm --cached oops.iso
$ git commit --amend -C HEAD
$ git rebase --continue
Il primo rimuove il file offensivo dall'indice. Il secondo modifica o modifica "Careless" come indice aggiornato e -C HEAD
indica a git di riutilizzare il vecchio messaggio di commit. Infine, git rebase --continue
procede con il resto dell'operazione rebase.
Questo dà una storia di:
$ git lola --name-status
* 93174be (HEAD, master) Login page
| A login.html
* a570198 Careless
| A other.html
* 5af4522 Admin page
| A admin.html
* e738b63 Index
A index.html
che è quello che vuoi