Quello che vuoi fare è altamente distruttivo se hai pubblicato la cronologia per altri sviluppatori. Consulta la sezione "Ripristino dal rebase upstream" nella git rebasedocumentazione per i passaggi necessari dopo aver riparato la cronologia.
Hai almeno due opzioni: git filter-branche 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-statusswitch, 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-emptyrimuove 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.
-dnomina 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/shmcomporterà un'esecuzione più rapida .
--index-filterè l'evento principale e viene eseguito contro l'indice in ogni passaggio della cronologia. Si desidera rimuovere oops.isoovunque si trovi, ma non è presente in tutti i commit. Il comando git rm --cached -f --ignore-unmatch oops.isocancella il DVD-rip quando è presente e non fallisce altrimenti.
--tag-name-filterdescrive 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
--alldi 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.htmle che il commit "Remove DVD-rip" non si trova più nel ramo master. La filiale etichettata refs/original/refs/heads/mastercontiene 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.isoe "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.isoda “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 5af4522avvia 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 editpiuttosto 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 HEADindica a git di riutilizzare il vecchio messaggio di commit. Infine, git rebase --continueprocede 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