2013-03-06 13 views
33
git filter-branch --env-filter ' 
export GIT_AUTHOR_EMAIL="[email protected]" 
export GIT_AUTHOR_NAME="foo"' -- commita..commitb 

Risultati in Which ref do you want to rewrite?Esecuzione di filtro-ramo su una gamma di commit

Così sembra che filter-branch non permette gamma di utilizzare la notazione utilizzare un intervallo tra due arbitri arbitrari.

Qual è il modo più diretto per eseguire un filtro su un intervallo di commit consecutivi (da qualche parte all'interno della cronologia di un ramo) se questo approccio non è possibile.

+10

Seriamente, chi ha inventato un messaggio di errore inutile come quello? L'unico uso per quel messaggio sembra essere quello di inserirlo in Google ... Qualcosa come "la fine dell'intervallo deve essere un riferimento, non l'ID di un commit" (da thx a @qqx) sembrerebbe essere più utile . – oliver

risposta

22

la soluzione più pulita che ho trovato è stato quello di effettuare le seguenti operazioni:

  1. Creare un ramo temporaneo refb.
  2. Applicare il filtro di diramazione su refa..temp.
  3. Rebase sul ramo temporaneo ed eliminarlo.

ie.

git branch temp refb 

git filter-branch --env-filter ' 
export GIT_AUTHOR_EMAIL="[email protected]"' refa..temp 

git rebase temp 
git branch --delete temp 
+1

+1 - bel trucco, ma penso che il ramo del filtro debba essere eseguito su refa..temp' e il comando rebase dovrebbe essere 'git rebase --onto temp refa refb' – Chronial

8

git filter-branchfa accettare gamma notazione, ma alla fine della serie ha bisogno di essere un riferimento, non l'ID di un commit.

git checkout -b tofilter commitb 
git filter-branch .... commita..tofilter 

Se dato solo commit, non saprebbe quale aggiornamento aggiornare con il ramo filtrato.

+0

In questo caso, come dovrei interrompere il mio script di filtro da applicare a commit prima di commita e commit dopo commitb? – Acorn

2

Non puoi semplicemente eseguire l'override dei commit nel mezzo di una cronologia, perché sha1 di un commit dipende da quello di un genitore. Quindi, il git non sa dove vuoi indicare il tuo riferimento HEAD dopo la filtrazione. Quindi, dovresti riscrivere tutto fino al TESTO.

Esempio:

A---B---C---D---E---F master 
      \ 
      \--G---H branch 

se si desidera che il filtro impegna B e C si dovrebbe anche filtrare tutti i commit dopo: D, E, F, G, H. Quindi, ecco perché git ti dice di utilizzare un ref alla fine della gamma, in modo che non finisca con una testa staccata.

Dopo aver modificato B e C si impegna e smettere sarà simile a questa:

A---B---C---D---E---F master 
\   \ 
\   \--G---H branch 
    \-B'--C'  (HEAD or a temporary TAG?..)  

Così, il master e branch sarà intatta. Non penso che questo sia ciò che vuoi. Significa che è necessario eseguire l'override di tutti i commit. La storia sarà quindi:

A---B---C---D---E---F (loose end, will be garbage collected one day) 
\   \ 
\   \--G---H (loose end, will be garbage collected one day) 
    \-B'--C'--D'--E'--F' master 
      \ 
      \--G'--H' branch 
+0

Quindi, secondo te, l'approccio migliore è se non voglio applicare il mio filtro ai commit dopo 'C'? Potrei creare un TAG temporaneo forse? – Acorn

+0

@Acorn Se vuoi mantenere i commit dopo aver filtrato C (io lo chiamo 'C'') devi filtrarli tutti almeno per cambiare il loro antenato da' C' a 'C'', altrimenti, lasciali spostando il ramo principale al 'C''. Non c'è opzione. Un commit ha tutta la cronologia precedente a quella crittografata nel proprio ID SHA1, quindi ogni commit è protetto da crittografia, non è possibile modificare la cronologia senza essere notato. – kan

+0

Giusto, quindi ho l'impressione che dovrei decidere se applicare la modifica dell'autore/email nel mio script di bash. Come posso sapere se sono nel raggio di azione da lì? – Acorn

5

Racchiudere filtrare i comandi in una dichiarazione, se-che verifica la presenza di tale intervallo. È possibile verificare se un commit è all'interno di un determinato intervallo con questo comando:

git rev-list start..end | grep **fullsha** 

L'attuale commit verranno memorizzati nel $GIT_COMMIT nel filtro.Quindi, il filtro diventa:

git filter-branch --env-filter ' 
    if git rev-list commita..commitb | grep $GIT_COMMIT; then 
    export GIT_AUTHOR_EMAIL="[email protected]" 
    export GIT_AUTHOR_NAME="foo" 
    fi' -- ^commita --all 

Se si vuole riscrivere solo il tuo ramo corrente, sostituire --all con HEAD

16

Non è possibile applicare il filtro-ramo nel mezzo della storia, come detto da @kan. È necessario applicare dal commit fatto fino alla fine della storia

git filter-branch --env-filter '...' SHA1..HEAD 

filtro-ramo può controllare per l'autore commit o altre informazioni, a scegliere di cambiare o meno il commit, quindi non ci sono modi per ottenere ciò che si vogliono, vedere http://git-scm.com/book/ch6-4.html, cercare "Modifica Indirizzi di posta elettronica a livello globale"

Ricorda: se si hanno spinto il commit a un repository pubblico non si deve utente filtro-ramo

+0

* PSA: * Non può riscrivere il centro della cronologia, perché invaliderebbe il campo genitore della coda collegata al centro. – cdosborn

4

lo faccio in questo modo.

Supponiamo di voler filtrare il contenuto di un ramo chiamato branch-you-are-filtering.

Supponiamo che ci sia un antenato impegnato su quel ramo, con un riferimento chiamato ref-for-commit-to-stop-at.

git filter-branch --commit-filter 'YOUR_FILTER_COMMANDS' branch-you-are-filtering...ref-for-commit-to-stop-at 

Dopo l'esecuzione, il commit su ref-for-commit-to-stop-at non verrà modificato. Tutti i commit \ change filtrati nel branch branch-you-are-filtering saranno basati su ref-for-commit-to-stop-at.

Se si sta utilizzando o meno --commit-filter o qualcos'altro dipende da voi.

-1

La soluzione di @Acron mi sembra sbagliata. Vorrei suggerire di seguito per passare da REFA e REFB inclusi sia gli hash:

  1. git tag master.bak
  2. git reset --hard refa
  3. git filter-branch --env-filter ' export GIT_AUTHOR_EMAIL="[email protected]"' refa^..master
  4. git cherry-pick refb..master.bak
  5. git tag -d master.bak
-1

Uso del filtro-ramo --setup parm e qualche potere di shell:

git filter-branch --setup ' 
for id in `git rev-list commitA..commitB`; do 
     eval filterfor_$id=rewrite 
done 
rewrite() { 
     GIT_AUTHOR_NAME="Frederick. O. Oosball" 
     [email protected] 
} 
' --env-filter 'eval \$filterfor_$GIT_COMMIT'