Come modificare il nome dell'autore e del committer e l'e-mail di più commit in Git?


2392

Stavo scrivendo una semplice sceneggiatura nel computer della scuola e stavo commettendo le modifiche a Git (in un repository che era nel mio pendrive, clonato dal mio computer a casa). Dopo diversi commit mi sono reso conto che stavo commettendo cose come utente root.

C'è un modo per cambiare l'autore di questi commit con il mio nome?


13
Domanda: l'uso di git filter-branch conserva gli SHA1 per tag, versioni e oggetti precedenti? O cambiando la forza del nome dell'autore cambierà anche gli SHA1 associati?
AndyL,

36
Gli hash cambieranno sì
Non disponibile

3
Tangenzialmente, ho creato un piccolo script che finalmente risolto la causa principale per me. gist.github.com/tripleee/16767aa4137706fd896c
tripleee

2
@impinball L'età della domanda è poco rilevante. La creazione di una nuova domanda duplicata è fuori discussione. Suppongo di poter creare una domanda che implora questa risposta particolare, ma non sono del tutto convinto che otterrebbe tanta visibilità. Non è che ci sia una carenza di domande Git qui ... Sono contento di poterti aiutare, comunque.
Tripleee

8
GitHub ha uno script speciale per questo: help.github.com/articles/changing-author-info
Timur Bernikovich,

Risposte:


1214

Questa risposta utilizza git-filter-branch, per la quale i documenti ora danno questo avviso:

git filter-branch ha una miriade di insidie ​​che possono produrre mangling non ovvi della riscrittura della storia prevista (e possono lasciarti con poco tempo per indagare su tali problemi poiché ha prestazioni così spaventose). Questi problemi di sicurezza e prestazioni non possono essere risolti in modo compatibile all'indietro e, come tale, non è raccomandato il suo utilizzo. Utilizzare uno strumento di filtro cronologico alternativo come git filter-repo . Se hai ancora bisogno di usare git filtro-ramo, leggi attentamente SICUREZZA (e PRESTAZIONI ) per conoscere le miniere di terra di filtro-ramo, quindi evita con cautela quanti più pericoli elencati lì ragionevolmente possibile.

Cambiare l'autore (o il committer) richiederebbe riscrivere tutta la storia. Se stai bene e pensi che ne valga la pena, allora dai un'occhiata a git filter-branch . La pagina man include diversi esempi per iniziare. Nota anche che puoi usare le variabili d'ambiente per cambiare il nome dell'autore, il committer, le date, ecc. - vedi la sezione "Variabili d'ambiente" della pagina man di git .

In particolare, puoi correggere tutti i nomi e le e-mail sbagliate dell'autore per tutti i rami e tag con questo comando (fonte: guida di GitHub ):

#!/bin/sh

git filter-branch --env-filter '
OLD_EMAIL="your-old-email@example.com"
CORRECT_NAME="Your Correct Name"
CORRECT_EMAIL="your-correct-email@example.com"
if [ "$GIT_COMMITTER_EMAIL" = "$OLD_EMAIL" ]
then
    export GIT_COMMITTER_NAME="$CORRECT_NAME"
    export GIT_COMMITTER_EMAIL="$CORRECT_EMAIL"
fi
if [ "$GIT_AUTHOR_EMAIL" = "$OLD_EMAIL" ]
then
    export GIT_AUTHOR_NAME="$CORRECT_NAME"
    export GIT_AUTHOR_EMAIL="$CORRECT_EMAIL"
fi
' --tag-name-filter cat -- --branches --tags

612
Github ha una sceneggiatura pubblica per help.github.com/articles/changing-author-info e funziona benissimo!
defvol

34
Dopo aver eseguito lo script è possibile rimuovere il ramo di backup eseguendo "git update-ref -d refs / original / refs / heads / master".
DR

7
@rodowi, duplica tutti i miei impegni.
Rafael Barros,

6
@RafaelBarros le informazioni sull'autore (proprio come qualsiasi altra cosa nella storia) fanno parte della chiave sha del commit. Qualsiasi modifica alla cronologia è una riscrittura che porta a nuovi ID per tutti i commit. Quindi non riscrivere su un repository condiviso o assicurarsi che tutti gli utenti ne siano a conoscenza ...
johannes,

20
Risolto usandogit push --force --tags origin HEAD:master
mcont

1577

NOTA: questa risposta modifica gli SHA1, quindi fai attenzione a usarlo su un ramo che è già stato premuto. Se vuoi solo correggere l'ortografia di un nome o aggiornare una vecchia e-mail, git ti consente di farlo senza riscrivere la cronologia usando .mailmap. Vedi la mia altra risposta .

Utilizzo di Interactive Rebase

Si potrebbe fare

git rebase -i -p <some HEAD before all of your bad commits>

Quindi contrassegna tutti i tuoi commit errati come "modifica" nel file rebase. Se si desidera modificare anche il primo commit, è necessario aggiungerlo manualmente come prima riga nel file rebase (seguire il formato delle altre righe). Quindi, quando git ti chiede di modificare ogni commit, fallo

 git commit --amend --author "New Author Name <email@address.com>" 

modifica o chiudi semplicemente l'editor che si apre e poi fai

git rebase --continue

per continuare il rebase.

Potresti saltare l'apertura totale dell'editor qui aggiungendo in --no-edit modo che il comando sarà:

git commit --amend --author "New Author Name <email@address.com>" --no-edit && \
git rebase --continue

Impegno singolo

Come alcuni commentatori hanno notato, se si desidera solo modificare il commit più recente, il comando rebase non è necessario. Basta fare

 git commit --amend --author "New Author Name <email@address.com>"

Ciò cambierà l'autore con il nome specificato, ma il committer verrà impostato come utente configurato in git config user.namee git config user.email. Se vuoi impostare il committer su qualcosa che specifichi, questo imposterà sia l'autore che il committer:

 git -c user.name="New Author Name" -c user.email=email@address.com commit --amend --reset-author

Nota su Unisci commit

C'è stato un piccolo difetto nella mia risposta originale. Se ci sono dei commit di fusione tra la corrente HEADe la tua <some HEAD before all your bad commits>, git rebaseli appiattirai (e comunque, se usi le richieste pull di GitHub, ci saranno un sacco di commit di fusione nella tua cronologia). Questo può portare molto spesso a una storia molto diversa (poiché le modifiche duplicate possono essere "riequilibrate") e, nel peggiore dei casi, può portare a git rebasechiederti di risolvere conflitti di unione difficili (che probabilmente erano già stati risolti nelle commit di unione). La soluzione è utilizzare la -pbandiera a git rebase, che conserverà la struttura di unione della tua cronologia. La manpage per git rebaseavverte che l'utilizzo -pe -ipuò portare a problemi, ma nelBUGS sezione dice "La modifica dei commit e la riformulazione dei loro messaggi di commit dovrebbero funzionare correttamente."

Ho aggiunto -pal comando sopra. Nel caso in cui stai cambiando il commit più recente, questo non è un problema.


27
Ottimo per l'impegno dispari però - utile se stai accoppiando e dimentica di cambiare l'autore
mloughran

32
+1 per menzionare il caso d'uso per la tipica correzione di un errore: git commit --amend --author = username
Nathan Kidd

12
Questo è perfetto, il mio caso d'uso più comune è che mi siedo su un altro computer e mi dimentichi di impostare l'autore e quindi di solito ho circa 5 commit per risolvere.
Zitrax,

57
git commit --amend --reset-authorfunziona anche una volta user.namee user.emailsono configurati correttamente.
punti

14
Riscrivi le informazioni sull'autore su tutti i commit dopo l' <commit>uso user.namee user.emailda ~/.gitconfig: esegui git rebase -i <commit> --exec 'git commit --amend --reset-author --no-edit', salva, esci. Non c'è bisogno di modificare!
ntc2

588

Puoi anche fare:

git filter-branch --commit-filter '
        if [ "$GIT_COMMITTER_NAME" = "<Old Name>" ];
        then
                GIT_COMMITTER_NAME="<New Name>";
                GIT_AUTHOR_NAME="<New Name>";
                GIT_COMMITTER_EMAIL="<New Email>";
                GIT_AUTHOR_EMAIL="<New Email>";
                git commit-tree "$@";
        else
                git commit-tree "$@";
        fi' HEAD

Nota, se si utilizza questo comando nel prompt dei comandi di Windows, è necessario utilizzare "invece di ':

git filter-branch --commit-filter "
        if [ "$GIT_COMMITTER_NAME" = "<Old Name>" ];
        then
                GIT_COMMITTER_NAME="<New Name>";
                GIT_AUTHOR_NAME="<New Name>";
                GIT_COMMITTER_EMAIL="<New Email>";
                GIT_AUTHOR_EMAIL="<New Email>";
                git commit-tree "$@";
        else
                git commit-tree "$@";
        fi" HEAD

4
L'uso di env-filter non è la soluzione più semplice? Non so perché questo ottenga più voti, quindi.
Stigkj,

3
Quindi il collegamento è interrotto. Come possiamo spingere queste modifiche in un altro repository?
Russell,

28
env-filter modificherà tutti i commit. Questa soluzione consente un condizionale.
user208769

5
"A previous backup already exists in refs/original/ Force overwriting the backup with -f"scusate ma dove sarà la -fbandiera quando eseguirà questo script due volte. In realtà questo è nella risposta di Brian, scusate il disturbo subito dopo che il ramo del filtro è la soluzione.
hhh,

2
@ user208769 env-filter consente anche un condizionale; guarda la mia risposta :-)
stigkj

559

Un aspetto, ma fai attenzione se hai un repository multiutente - questo cambierà tutti gli commit per avere lo stesso (nuovo) autore e committer.

git filter-branch -f --env-filter "GIT_AUTHOR_NAME='Newname'; GIT_AUTHOR_EMAIL='new@email'; GIT_COMMITTER_NAME='Newname'; GIT_COMMITTER_EMAIL='new@email';" HEAD

Con le interruzioni di riga nella stringa (che è possibile in bash):

git filter-branch -f --env-filter "
    GIT_AUTHOR_NAME='Newname'
    GIT_AUTHOR_EMAIL='new@email'
    GIT_COMMITTER_NAME='Newname'
    GIT_COMMITTER_EMAIL='new@email'
  " HEAD

Punto minore, l'esportazione è in realtà superflua sebbene non danneggi. ad es. git-filter-branch --env-filter "GIT_AUTHOR_NAME = 'Nuovo nome'; GIT_AUTHOR_EMAIL = 'Nuova email'" HEAD.
Alec the Geek,

4
Perché riscrive tutti i commit se si specifica HEADalla fine del comando?
Nick Volynkin,

1
Questo non funziona per il mio repository bitbucket, qualche idea? Faccio un git push --force --tags origin 'refs/heads/*'dopo il comando consigliato
Olórin

1
Il comando push per questo è:$git push --force --tags origin 'refs/heads/master'
HARSH NILESH PATHAK,

1
Neat; questo mantiene anche i vecchi timestamp.
DharmaTurtle,

221

Succede quando non hai inizializzato $ HOME / .gitconfig. È possibile risolvere questo problema come:

git config --global user.name "you name"
git config --global user.email you@domain.com
git commit --amend --reset-author

testato con git versione 1.7.5.4


9
Funziona davvero bene sull'ultimo commit. Bello e semplice. Non deve essere un cambiamento globale, usando --localanche le opere
Ben

Questo è stato il grande vincitore per me! Il git commit --amend --reset-author --no-editcomando è particolarmente utile se hai creato commit con le informazioni sbagliate sull'autore, quindi imposta l'autore corretto dopo il fatto via git config. Ho salvato un mio $$ proprio ora quando ho dovuto aggiornare la mia e-mail.
ecbrodie,

187

Per un singolo commit:

git commit --amend --author="Author Name <email@address.com>"

(estratto dalla risposta di asmeurer)


14
ma questo è solo se è il commit più recente
Richard

4
Secondo git help commit, git commit --amendcambia il commit sulla "punta del ramo corrente" (che è HEAD). Questo è normalmente il commit più recente, ma è possibile effettuare qualsiasi commit desiderato verificando prima il commit con git checkout <branch-name>o git checkout <commit-SHA>.
Rory O'Kane,

12
Ma se lo fai, tutti i commit che hanno già quel commit come genitore indicheranno il commit sbagliato. Meglio usare il filtro-ramo a quel punto.
John Gietzen,

3
@JohnGietzen: puoi rifare il commit su quello che è stato modificato per risolvere il problema. Tuttavia, se stai eseguendo> 1 commit, allora come detto, probabilmente il filtro-ramo sarà molto più semplice.
Thanatos,

5
Nota che questo cambiamento commette solo authore non ilcommitter
Nick Volynkin il

179

Nel caso in cui solo alcuni dei primi commit abbiano autori non validi, puoi farlo tutto all'interno git rebase -iusando il execcomando e il --amendcommit, come segue:

git rebase -i HEAD~6 # as required

che ti presenta un elenco modificabile di commit:

pick abcd Someone else's commit
pick defg my bad commit 1
pick 1234 my bad commit 2

Quindi aggiungere le exec ... --author="..."righe dopo tutte le righe con autori non validi:

pick abcd Someone else's commit
pick defg my bad commit 1
exec git commit --amend --author="New Author Name <email@address.com>" -C HEAD
pick 1234 my bad commit 2
exec git commit --amend --author="New Author Name <email@address.com>" -C HEAD

salva ed esci dall'editor (per eseguirlo).

Questa soluzione potrebbe essere più lunga da digitare rispetto ad alcune altre, ma è altamente controllabile: so esattamente cosa commette.

Grazie a @asmeurer per l'ispirazione.


26
Decisamente fantastico. Puoi accorciarlo impostando user.name e user.email nella configurazione locale del repository, e quindi ogni riga è solo exec git commit --amend --reset-author -C HEAD?
Andrew,

1
La risposta canonica, per usare il filtro-ramo, mi ha appena eliminato refs / heads / master. Quindi +1 alla tua soluzione controllabile e modificabile. Grazie!
jmtd,

Perché inizi con Someone else's commitinvece di my bad commit 1? Ho appena provato HEAD^^a modificare gli ultimi 2 commit e ha funzionato perfettamente.
Fredoverflow

3
Al posto di git rebase -i HEAD^^^^^^te puoi anche scriveregit rebase -i HEAD~6
Patrick Schlüter il

1
Si noti che ciò modifica il timestamp degli commit. Vedi stackoverflow.com/a/11179245/1353267 per il ripristino dei timestamp corretti
Samveen,

111

Github ha una bella soluzione , che è il seguente script di shell:

#!/bin/sh

git filter-branch --env-filter '

an="$GIT_AUTHOR_NAME"
am="$GIT_AUTHOR_EMAIL"
cn="$GIT_COMMITTER_NAME"
cm="$GIT_COMMITTER_EMAIL"

if [ "$GIT_COMMITTER_EMAIL" = "your@email.to.match" ]
then
    cn="Your New Committer Name"
    cm="Your New Committer Email"
fi
if [ "$GIT_AUTHOR_EMAIL" = "your@email.to.match" ]
then
    an="Your New Author Name"
    am="Your New Author Email"
fi

export GIT_AUTHOR_NAME="$an"
export GIT_AUTHOR_EMAIL="$am"
export GIT_COMMITTER_NAME="$cn"
export GIT_COMMITTER_EMAIL="$cm"
'

5
Ha funzionato perfettamente. Ho dovuto solo git reset --hard HEAD^un paio di volte sugli altri repository locali per portarli a una versione precedente, git pull-ed la versione modificata, ed eccomi qui senza righe contenenti unknown <stupid-windows-user@.StupidWindowsDomain.local>( devo amare il default di Git).
Alan Plum,

1
Non posso spingere dopo questo. Devo usare "-f"?
Fish Monitor

9
L'ho fatto git push -f. Inoltre, i repository locali devono essere ritirati dopo questo.
Fish Monitor

Se è necessario eseguire lo script della shell su un ramo specifico, è possibile modificare l'ultima riga in: "'master..your-branch-name" (presupponendo che si sia ramificato di master).
Robert Kajic,

Fai clic sul link <bella soluzione> poiché lo script è stato aggiornato
gxpr,

82

Come menzionato nel documento, la riscrittura della storia è pericolosa e spezzerà i repository di altre persone.

Ma se vuoi davvero farlo e ti trovi in ​​un ambiente bash (nessun problema in Linux, su Windows, puoi usare git bash, fornito con l'installazione di git), usa git filter-branch :

git filter-branch --env-filter '
  if [ $GIT_AUTHOR_EMAIL = bad@email ];
    then GIT_AUTHOR_EMAIL=correct@email;
  fi;
export GIT_AUTHOR_EMAIL'

Per velocizzare le cose, puoi specificare una serie di revisioni che vuoi riscrivere:

git filter-branch --env-filter '
  if [ $GIT_AUTHOR_EMAIL = bad@email ];
    then GIT_AUTHOR_EMAIL=correct@email;
  fi;
export GIT_AUTHOR_EMAIL' HEAD~20..HEAD

2
Nota che questo lascerà qualsiasi tag che punta ai vecchi commit. --tag-name-filter catè l'opzione "fallo funzionare".
Roman Starkov,

@romkyns qualche idea su come cambiare anche i tag?
Nick Volynkin,

@NickVolynkin Sì, si specifica --tag-name-filter cat. Questo avrebbe dovuto essere il comportamento predefinito.
Roman Starkov,

48

Quando si assume un commit non unito da un altro autore, esiste un modo semplice per gestirlo.

git commit --amend --reset-author


1
Per un singolo commit e se vuoi inserire il tuo nome utente, questo è il modo più semplice.
Pedro Benevides,

7
Puoi aggiungerlo --no-editper renderlo ancora più semplice, poiché in genere la maggior parte delle persone vorrà aggiornare solo l'indirizzo e-mail e non il messaggio di commit
PlagueHammer,

Ragazzi, per favore, condividete il comando git solo per aggiornare l'e-mail / nome utente dell'ultimo commit con quello nuovo
adi,

Hai provato questo? Questo dovrebbe essere un effetto collaterale di questo, se non stackoverflow.com/a/2717477/654245 sembra un buon percorso.
Ryanmt,

47

Puoi usarlo come alias in modo da poter fare:

git change-commits GIT_AUTHOR_NAME "old name" "new name"

o per gli ultimi 10 commit:

git change-commits GIT_AUTHOR_EMAIL "old@email.com" "new@email.com" HEAD~10..HEAD

Aggiungi a ~ / .gitconfig:

[alias]
    change-commits = "!f() { VAR=$1; OLD=$2; NEW=$3; shift 3; git filter-branch --env-filter \"if [[ \\\"$`echo $VAR`\\\" = '$OLD' ]]; then export $VAR='$NEW'; fi\" $@; }; f "

Fonte: https://github.com/brauliobo/gitconfig/blob/master/configs/.gitconfig

Spero sia utile


"git: 'change-commit' non è un comando git. Vedi 'git --help'."
Native_Mobile_Arch_Dev

Dopo questo comando e sincronizzalo con il master tutti i commit nella cronologia vengono duplicati! Anche di altri utenti :(
Vladimir il

@Vladimir che è previsto, per favore studia come cambiare la storia in git
brauliobo,

Per me sembra funzionare in / bin / sh, quindi ho dovuto sostituire il test specifico per bash [[ ]]con test sh-compatibile [ ](parentesi singole). Inoltre funziona molto bene, grazie!
Steffen Schwigon

39

Questa è una versione più elaborata della versione di @ Brian:

Per modificare l'autore e il committer, puoi farlo (con interruzioni di riga nella stringa che è possibile in bash):

git filter-branch --env-filter '
    if [ "$GIT_COMMITTER_NAME" = "<Old name>" ];
    then
        GIT_COMMITTER_NAME="<New name>";
        GIT_COMMITTER_EMAIL="<New email>";
        GIT_AUTHOR_NAME="<New name>";
        GIT_AUTHOR_EMAIL="<New email>";
    fi' -- --all

È possibile che venga visualizzato uno di questi errori:

  1. La directory temporanea esiste già
  2. I riferimenti che iniziano con ref / originale esistono già
    (ciò significa che un altro filtro-ramo è stato precedentemente eseguito sul repository e il riferimento al ramo originale di cui è stato eseguito il backup su ref / originale )

Se vuoi forzare la corsa nonostante questi errori, aggiungi il --forceflag:

git filter-branch --force --env-filter '
    if [ "$GIT_COMMITTER_NAME" = "<Old name>" ];
    then
        GIT_COMMITTER_NAME="<New name>";
        GIT_COMMITTER_EMAIL="<New email>";
        GIT_AUTHOR_NAME="<New name>";
        GIT_AUTHOR_EMAIL="<New email>";
    fi' -- --all

-- --allPotrebbe essere necessaria una piccola spiegazione dell'opzione: fa funzionare il ramo del filtro su tutte le revisioni su tutti i riferimenti (che include tutti i rami). Ciò significa, ad esempio, che anche i tag vengono riscritti ed è visibile sui rami riscritti.

Un "errore" comune è HEADinvece quello di utilizzare , il che significa filtrare tutte le revisioni solo sul ramo corrente . E quindi non esisterebbero tag (o altri riferimenti) nel ramo riscritto.


Complimenti per aver fornito una procedura che cambia si impegna su tutti i ref / filiali.
Johnny Utahh,

25

Un singolo comando per modificare l'autore degli ultimi N commit:

git rebase -i HEAD~4 -x "git commit --amend --author 'Author Name <author.name@mail.com>' --no-edit"

APPUNTI

  • la --no-editbandiera si assicura git commit --amendche non richieda una conferma aggiuntiva
  • quando lo usi git rebase -i, puoi selezionare manualmente i commit dove cambiare l'autore,

il file modificato sarà simile al seguente:

pick 897fe9e simplify code a little
exec git commit --amend --author 'Author Name <author.name@mail.com>' --no-edit
pick abb60f9 add new feature
exec git commit --amend --author 'Author Name <author.name@mail.com>' --no-edit
pick dc18f70 bugfix
exec git commit --amend --author 'Author Name <author.name@mail.com>' --no-edit

È quindi possibile modificare alcune righe per vedere dove si desidera modificare l'autore. Questo ti offre una buona via di mezzo tra automazione e controllo: vedi i passaggi che verranno eseguiti e una volta salvato tutto verrà applicato in una sola volta.


Eccezionale! Grazie!
Pablo Lalloni,

Ho usato HEAD ~ 8 e mostra molto più degli ultimi 8 commit.
Bryan Bryce il

1
@BryanBryce se ci sono commit di merge coinvolti, le cose si complicano :)
Chris Maes

@ChrisMaes Ah, vedo cosa sta succedendo. Non voglio scherzare con quelli, solo sul ramo in cui mi trovo.
Bryan Bryce,

In quel caso, supponendo che tu ti sia ramificato dal maestro, potresti:git rebase -i master -x ...
Chris Maes il

23
  1. correre git rebase -i <sha1 or ref of starting point>
  2. contrassegnare tutti i commit con cui si desidera modificare edit(o e)
  3. ripetere i due comandi seguenti fino a quando non sono stati elaborati tutti i commit:

    git commit --amend --reuse-message=HEAD --author="New Author <new@author.email>" ; git rebase --continue

Ciò manterrà tutte le altre informazioni di commit (comprese le date). L' --reuse-message=HEADopzione impedisce l'avvio dell'editor dei messaggi.


23

Uso quanto segue per riscrivere l'autore per un intero repository, inclusi i tag e tutti i rami:

git filter-branch --tag-name-filter cat --env-filter "
  export GIT_AUTHOR_NAME='New name';
  export GIT_AUTHOR_EMAIL='New email'
" -- --all

Quindi, come descritto nella pagina MAN del filtro-ramo , rimuovere tutti i riferimenti originali di cui è stato eseguito il backup filter-branch(questo è distruttivo, prima il backup):

git for-each-ref --format="%(refname)" refs/original/ | \
xargs -n 1 git update-ref -d

2
È molto importante da usare --tag-name-filter cat. In caso contrario, i tag rimarranno sulla catena di commit originale. Le altre risposte non menzionano questo.
jeberle

21

Ho adattato questa soluzione che funziona inserendo un semplice author-conv-file(il formato è lo stesso di quello per git-cvsimport ). Funziona modificando tutti gli utenti come definito in author-conv-filetutti i rami.

Abbiamo usato questo insieme a cvs2gitper migrare il nostro repository da cvs a git.

cioè campione author-conv-file

john=John Doe <john.doe@hotmail.com>
jill=Jill Doe <jill.doe@hotmail.com>

Il copione:

 #!/bin/bash

 export $authors_file=author-conv-file

 git filter-branch -f --env-filter '

 get_name () {
     grep "^$1=" "$authors_file" |
     sed "s/^.*=\(.*\) <.*>$/\1/"
 }

 get_email () {
     grep "^$1=" "$authors_file" |
     sed "s/^.*=.* <\(.*\)>$/\1/"
 }

 GIT_AUTHOR_NAME=$(get_name $GIT_COMMITTER_NAME) &&
     GIT_AUTHOR_EMAIL=$(get_email $GIT_COMMITTER_NAME) &&
     GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME &&
     GIT_COMMITTER_EMAIL=$GIT_AUTHOR_EMAIL &&
     export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL &&
     export GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL
 ' -- --all

Grazie, mi chiedo perché questa non sia la funzionalità core git (o git-svn). Questo può essere fatto con un flag per git svn clone, ma non in git filter-branch ...
Daniel Hershcovich,

20

Devo sottolineare che se l'unico problema è che l'autore / e-mail è diverso dal solito, questo non è un problema. La correzione corretta è quella di creare un file chiamato .mailmapalla base della directory con linee simili

Name you want <email you want> Name you don't want <email you don't want>

E da quel momento in poi, i comandi simili git shortlogconsidereranno quei due nomi uguali (a meno che tu non gli dica espressamente di non farlo). Vedi http://schacon.github.com/git/git-shortlog.html per ulteriori informazioni.

Questo ha il vantaggio di tutte le altre soluzioni qui in quanto non è necessario riscrivere la cronologia, il che può causare problemi se si dispone di un upstream ed è sempre un buon modo per perdere accidentalmente i dati.

Certo, se hai commesso qualcosa come te stesso e dovrebbe davvero essere qualcun altro, e non ti dispiace riscrivere la storia a questo punto, cambiare l'autore del commit è probabilmente una buona idea per scopi di attribuzione (nel qual caso ti indirizzo al mio altra risposta qui).


18

Ho trovato le versioni presentate troppo aggressive, specialmente se si commettono patch da altri sviluppatori, questo essenzialmente ruberà il loro codice.

La versione seguente funziona su tutti i rami e modifica l'autore e la comitter separatamente per evitarlo.

Complimenti a leif81 per l'opzione all.

#!/bin/bash

git filter-branch --env-filter '
if [ "$GIT_AUTHOR_NAME" = "<old author>" ];
then
    GIT_AUTHOR_NAME="<new author>";
    GIT_AUTHOR_EMAIL="<youmail@somehost.ext>";
fi
if [ "$GIT_COMMITTER_NAME" = "<old committer>" ];
then
    GIT_COMMITTER_NAME="<new commiter>";
    GIT_COMMITTER_EMAIL="<youmail@somehost.ext>";
fi
' -- --all

18
  1. Cambia commit author name & emailcon Amend, quindi sostituendo old-commit with new-one:

    $ git checkout <commit-hash>                            # checkout to the commit need to modify  
    $ git commit --amend --author "name <author@email.com>" # change the author name and email
    
    $ git replace <old-commit-hash> <new-commit-hash>      # replace the old commit by new one
    $ git filter-branch -- --all                           # rewrite all futures commits based on the replacement                   
    
    $ git replace -d <old-commit-hash>     # remove the replacement for cleanliness 
    $ git push -f origin HEAD              # force push 
    
  2. Un altro modo Rebasing:

    $ git rebase -i <good-commit-hash>      # back to last good commit
    
    # Editor would open, replace 'pick' with 'edit' before the commit want to change author
    
    $ git commit --amend --author="author name <author@email.com>"  # change the author name & email
    
    # Save changes and exit the editor
    
    $ git rebase --continue                # finish the rebase
    

2
Risposta molto bella. Mi piace il fatto che le modifiche siano state completate dall'aggiornamento fino a ripulire persino il git commit
Aleks

12

Il modo più rapido e semplice per farlo è usare l'argomento --exec di git rebase:

git rebase -i -p --exec 'git commit --amend --reset-author --no-edit'

Questo creerà una lista di cose da fare simile a questa:

pick ef11092 Blah blah blah
exec git commit --amend --reset-author --no-edit
pick 52d6391 Blah bloh bloo
exec git commit --amend --reset-author --no-edit
pick 30ebbfe Blah bluh bleh
exec git commit --amend --reset-author --no-edit
...

e questo funzionerà tutto automaticamente, il che funziona quando hai centinaia di commit.


9

Se sei l'unico utente di questo repository, puoi riscrivere la cronologia usando git filter-branch(come svick ha scritto ) o git fast-export/ git fast-importpiù lo script di filtro (come descritto nell'articolo a cui si fa riferimento nella risposta del docgnome ) o il rebase interattivo . Ma uno di questi cambierebbe le revisioni dal primo commit in poi; questo significa problemi per chiunque abbia basato le proprie modifiche sulla pre-riscrittura del proprio ramo.

RECUPERO

Se altri sviluppatori non basassero il loro lavoro sulla versione di riscrittura, la soluzione più semplice sarebbe quella di ri-clonare (clonare di nuovo).

In alternativa, possono provare git rebase --pull, il che farebbe avanzare rapidamente se non ci fossero cambiamenti nel loro repository, o rifare il loro ramo in cima ai commit riscritti (vogliamo evitare l'unione, poiché manterrebbe i commit pre-riscrittura per sempre). Tutto questo supponendo che non abbiano commesso un lavoro; usogit stash per nascondere le modifiche altrimenti.

Se altri sviluppatori usano questi rami, e / o git pull --rebasenon funziona per esempio perché a monte non è configurato, devono rebase il loro lavoro sulla parte superiore del post-commit di riscrittura. Ad esempio, subito dopo aver recuperato nuove modifiche ( git fetch), per un masterramo basato su / biforcato origin/master, è necessario eseguire

$ git rebase --onto origin/master origin/master@{1} master

Ecco lo origin/master@{1}stato di pre-riscrittura (prima del recupero), vedere gitrevisions .


Una soluzione alternativa sarebbe quella di utilizzare refs / replace / meccanismo, disponibile in Git dalla versione 1.6.5. In questa soluzione fornite sostituzioni per commit che hanno email errate; quindi chiunque fetch = +refs/replace/*:refs/replace/*recuperi i ref di "sostituzione" (qualcosa come refspec nel loro posto appropriato .git/config) otterrebbe sostituzioni in modo trasparente, e quelli che non recuperano questi ref vedrebbero i vecchi commit.

La procedura è simile a questa:

  1. Trova tutti i commit con email errate, ad esempio utilizzando

    $ git log --author=user@wrong.email --all
    
  2. Per ogni commit errato, creare un commit sostitutivo e aggiungerlo al database degli oggetti

    $ git cat-file -p <ID of wrong commit> | 
      sed -e 's/user@wrong\.email/user@example.com/g' > tmp.txt
    $ git hash-object -t commit -w tmp.txt
    <ID of corrected commit>
    
  3. Ora che hai corretto il commit nel database degli oggetti, devi dire a git di sostituire automaticamente e in modo trasparente il commit errato con uno corretto usando il git replacecomando:

    $ git replace <ID of wrong commit> <ID of corrected commit>
    
  4. Infine, elenca tutte le sostituzioni per verificare se questa procedura ha avuto esito positivo

    $ git replace -l
    

    e verificare se si verificano sostituzioni

    $ git log --author=user@wrong.email --all
    

Ovviamente puoi automatizzare questa procedura ... beh, tutto tranne l'uso git replaceche non ha (ancora) la modalità batch, quindi dovresti usare il loop shell per quello, o sostituire "a mano".

NON TESTATO! YMMV.

Si noti che è possibile incontrare alcuni angoli irregolari quando si utilizza il refs/replace/meccanismo: è nuovo e non ancora ben testato .


6

Se i commit che vuoi correggere sono gli ultimi, e solo un paio di essi, puoi usare una combinazione di git resete git stashtornare indietro per impegnarli nuovamente dopo aver configurato il nome e l'e-mail corretti.

La sequenza sarà simile a questa (per 2 commit errati, nessuna modifica in sospeso):

git config user.name <good name>
git config user.email <good email>
git reset HEAD^
git stash
git reset HEAD^
git commit -a
git stash pop
git commit -a

5

Se si utilizza Eclipse con EGit, esiste una soluzione abbastanza semplice.
Presupposto: è stato eseguito il commit in una filiale locale 'local_master_user_x' che non può essere trasferita a una filiale remota 'master' a causa dell'utente non valido.

  1. Verifica il ramo principale "master"
  2. Seleziona i progetti / cartelle / file per i quali 'local_master_user_x' contiene le modifiche
  3. Tasto destro del mouse - Sostituisci con - Branch - 'local_master_user_x'
  4. Effettuare nuovamente il commit di queste modifiche, questa volta come utente corretto e nel ramo "master" locale
  5. Spingere in remoto "master"

5

Utilizzando rebase interattivo, è possibile inserire un comando di modifica dopo ogni commit che si desidera modificare. Per esempio:

pick a07cb86 Project tile template with full details and styling
x git commit --amend --reset-author -Chead

3
Il problema è che anche altri metadati di commit (ad es. Data e ora) vengono modificati. L'ho appena scoperto nel modo più duro ;-).
Halfer

5

Nota che git memorizza due diversi indirizzi e-mail, uno per il committer (la persona che ha commesso la modifica) e un altro per l' autore (la persona che ha scritto la modifica).

Le informazioni sul committer non vengono visualizzate nella maggior parte dei luoghi, ma è possibile visualizzarle con git log -1 --format=%cn,%ce(o utilizzare showinvece di logspecificare un commit specifico).

Cambiare l'autore del tuo ultimo commit è semplice come git commit --amend --author "Author Name <email@example.com>" , non esiste una riga o argomento per fare lo stesso con le informazioni del committer.

La soluzione è quella di modificare (temporaneamente o meno) le informazioni dell'utente, quindi modificare il commit, che aggiornerà il committer con le informazioni correnti:

git config user.email my_other_email@example.com 
git commit --amend

Si noti che il vecchio valore è ancora in alcuni punti in path\to\repo\.git. Non sono ancora sicuro di cosa dovresti fare per eliminarlo totalmente. Le modifiche purtroppo (?) Non sembrano cancellare.
ruffin,

5

Oggi abbiamo riscontrato un problema in cui un carattere UTF8 nel nome di un autore stava causando problemi sul server di compilazione, quindi abbiamo dovuto riscrivere la cronologia per correggere questo. I passi fatti sono stati:

Passaggio 1: modifica il tuo nome utente in git per tutti i commit futuri, come da istruzioni qui: https://help.github.com/articles/setting-your-username-in-git/

Passaggio 2: eseguire il seguente script bash:

#!/bin/sh

REPO_URL=ssh://path/to/your.git
REPO_DIR=rewrite.tmp

# Clone the repository
git clone ${REPO_URL} ${REPO_DIR}

# Change to the cloned repository
cd ${REPO_DIR}

# Checkout all the remote branches as local tracking branches
git branch --list -r origin/* | cut -c10- | xargs -n1 git checkout

# Rewrite the history, use a system that will preseve the eol (or lack of in commit messages) - preferably Linux not OSX
git filter-branch --env-filter '
OLD_EMAIL="me@something.com"
CORRECT_NAME="New Me"

if [ "$GIT_COMMITTER_EMAIL" = "$OLD_EMAIL" ]
then
    export GIT_COMMITTER_NAME="$CORRECT_NAME"
fi
if [ "$GIT_AUTHOR_EMAIL" = "$OLD_EMAIL" ]
then
    export GIT_AUTHOR_NAME="$CORRECT_NAME"
fi
' --tag-name-filter cat -- --branches --tags

# Force push the rewritten branches + tags to the remote
git push -f

# Remove all knowledge that we did something
rm -rf ${REPO_DIR}

# Tell your colleagues to `git pull --rebase` on all their local remote tracking branches

Panoramica rapida: controlla il tuo repository in un file temporaneo, controlla tutti i rami remoti, esegui lo script che riscriverà la cronologia, eseguirà una spinta forzata del nuovo stato e dirà a tutti i tuoi colleghi di fare un pull di rebase per ottenere le modifiche.

Abbiamo avuto problemi con l'esecuzione di questo su OS X perché in qualche modo ha incasinato le terminazioni di linea nei messaggi di commit, quindi in seguito abbiamo dovuto eseguirlo nuovamente su una macchina Linux.


5

Il tuo problema è molto comune Vedere " Uso di Mailmap per correggere l'elenco degli autori in Git "

Per semplicità, ho creato uno script per facilitare il processo: git-changemail

Dopo aver inserito quello script nel tuo percorso, puoi eseguire comandi come:

  • Modifica le corrispondenze dell'autore nel ramo corrente

    $ git changemail -a old@email.com -n newname -m new@email.com
    
  • Cambia le corrispondenze di autore e committer su <branch> e <branch2>. Passa -fal ramo filtro per consentire la riscrittura dei backup

    $ git changemail -b old@email.com -n newname -m new@email.com -- -f &lt;branch> &lt;branch2>
    
  • Mostra utenti esistenti su repository

    $ git changemail --show-both
    

A proposito, dopo aver apportato le modifiche, pulire il backup dal ramo filtro con: git-backup-clean


1
quando eseguo il tuo comando, dice "fatale: impossibile eseguire" git-changemail ": autorizzazione negata"
Govind


3

Voglio aggiungere anche il mio esempio. Voglio creare una funzione bash con un determinato parametro.

funziona in mint-linux-17.3

# $1 => email to change, $2 => new_name, $3 => new E-Mail

function git_change_user_config_for_commit {

 # defaults
 WRONG_EMAIL=${1:-"you_wrong_mail@hello.world"}
 NEW_NAME=${2:-"your name"}
 NEW_EMAIL=${3:-"new_mail@hello.world"}

 git filter-branch -f --env-filter "
  if [ \$GIT_COMMITTER_EMAIL = '$WRONG_EMAIL' ]; then
    export GIT_COMMITTER_NAME='$NEW_NAME'
    export GIT_COMMITTER_EMAIL='$NEW_EMAIL'
  fi
  if [ \$GIT_AUTHOR_EMAIL = '$WRONG_EMAIL' ]; then
    export GIT_AUTHOR_NAME='$NEW_NAME'
    export GIT_AUTHOR_EMAIL='$NEW_EMAIL'
  fi
 " --tag-name-filter cat -- --branches --tags;
}

2

Se sei l'unico utente di questo repository o non ti interessa possibilmente interrompere il repository per altri utenti, quindi sì. Se hai spinto questi commit ed esistono dove altrove possono accedervi, allora no, a meno che non ti interessi a interrompere i repository di altre persone. Il problema è che cambiando questi commit genererai nuovi SHA che li faranno trattare come commit diversi. Quando qualcun altro tenta di attirare questi impegni modificati, la storia è diversa e kaboom.

Questa pagina http://inputvalidation.blogspot.com/2008/08/how-to-change-git-commit-author.html descrive come farlo. (Non l'ho provato, quindi YMMV)


Pertanto, non esiste un modo sicuro per riscrivere user.email. Senza far saltare in aria tutti gli altri. Sapevo che riscrivere la storia era una cattiva idea, pensavo solo che potesse esserci un modo pulito per farlo in sicurezza. Grazie.
manumoomoo,

@mediaslave: prova il refs/replace/meccanismo.
Jakub Narębski,

meta.stackexchange.com/a/8259/184684 - aka, sommare i collegamenti per trasformarli in risposte.
ruffin,
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.