Wie ändere ich den Namen des Autors und des Committers sowie die E-Mail-Adresse mehrerer Commits in Git?

2392

Ich habe ein einfaches Skript auf dem Schulcomputer geschrieben und die Änderungen an Git vorgenommen (in einem Repo, das sich in meinem Pendrive befand und von meinem Computer zu Hause geklont wurde). Nach mehreren Commits wurde mir klar, dass ich als Root-Benutzer etwas festschrieb.

Gibt es eine Möglichkeit, den Autor dieser Commits in meinen Namen zu ändern?

Flávio Amieiro
quelle
13
Frage: Bewahrt die Verwendung von git filter-branch die SHA1s für frühere Tags, Versionen und Objekte? Oder werden durch Ändern des Autorennamens auch die zugehörigen SHA1 geändert?
AndyL
36
Hashes ändern sich ja
Nicht verfügbar
3
Tangential habe ich ein kleines Skript erstellt, das schließlich die Grundursache für mich behoben hat. gist.github.com/tripleee/16767aa4137706fd896c
Tripleee
2
@impinball Das Alter der Frage ist kaum relevant. Das Erstellen einer neuen doppelten Frage kommt nicht in Frage. Ich nehme an, ich könnte eine Frage erstellen, die diese spezielle Antwort erfordert, aber ich bin nicht ganz davon überzeugt, dass sie so viel Sichtbarkeit bekommen würde. Es ist nicht so, dass es hier an Git-Fragen mangelt ... Ich bin jedenfalls froh, dass ich helfen konnte.
Tripleee
8
GitHub hat ein spezielles Skript dafür: help.github.com/articles/changing-author-info
Timur Bernikovich

Antworten:

1213

Diese Antwort verwendet git-filter-branch, für die die Dokumente jetzt diese Warnung geben:

Git Filter-Branch hat eine Vielzahl von Fallstricken, die zu nicht offensichtlichen Mangeln des beabsichtigten Umschreibens der Geschichte führen können (und Ihnen wenig Zeit lassen können, solche Probleme zu untersuchen, da es eine solch miserable Leistung aufweist). Diese Sicherheits- und Leistungsprobleme können nicht abwärtskompatibel behoben werden, weshalb ihre Verwendung nicht empfohlen wird. Bitte verwenden Sie ein alternatives Tool zum Filtern von Verlaufsdaten wie git filter-repo . Wenn Sie immer noch Git-Filterzweige verwenden müssen, lesen Sie bitte SICHERHEIT (und LEISTUNG ) sorgfältig durch , um mehr über die Landminen von Filterzweigen zu erfahren, und vermeiden Sie dann sorgfältig so viele der dort aufgeführten Gefahren wie möglich.

Das Ändern des Autors (oder Committers) würde ein erneutes Schreiben des gesamten Verlaufs erfordern. Wenn Sie damit einverstanden sind und denken, dass es sich lohnt, sollten Sie sich den Git-Filter-Zweig ansehen . Die Manpage enthält mehrere Beispiele, um Ihnen den Einstieg zu erleichtern. Beachten Sie auch, dass Sie Umgebungsvariablen verwenden können, um den Namen des Autors, des Committers, der Daten usw. zu ändern - siehe Abschnitt "Umgebungsvariablen" auf der Git-Manpage .

Insbesondere können Sie mit diesem Befehl alle falschen Autorennamen und E-Mails für alle Zweige und Tags korrigieren (Quelle: GitHub-Hilfe ):

#!/bin/sh

git filter-branch --env-filter '
OLD_EMAIL="[email protected]"
CORRECT_NAME="Your Correct Name"
CORRECT_EMAIL="[email protected]"
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
Pat Notz
quelle
612
Github hat ein öffentliches Skript für diese Hilfe.github.com/articles/changing-author-info und es funktioniert großartig!
defvol
34
Nach dem Ausführen des Skripts können Sie den Sicherungszweig entfernen, indem Sie "git update-ref -d refs / original / refs / Heads / Master" ausführen.
DR
7
@rodowi, es dupliziert alle meine Commits.
Rafael Barros
6
@RafaelBarros Die Autoreninformationen (genau wie alles andere in der Geschichte) sind Teil des sha-Schlüssels des Commits. Jede Änderung des Verlaufs ist eine Neufassung, die zu neuen IDs für alle Commits führt. Schreiben Sie also nicht auf ein freigegebenes Repo um oder stellen Sie sicher, dass alle Benutzer davon Kenntnis haben ...
Johannes
20
Gelöst mitgit push --force --tags origin HEAD:master
mcont
1577

HINWEIS: Diese Antwort ändert SHA1s. Achten Sie daher darauf, sie in einem Zweig zu verwenden, der bereits verschoben wurde. Wenn Sie nur die Schreibweise eines Namens korrigieren oder eine alte E-Mail aktualisieren möchten, können Sie dies mit git tun, ohne den Verlauf mit neu zu schreiben .mailmap. Siehe meine andere Antwort .

Verwenden von Interactive Rebase

Du könntest es tun

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

Markieren Sie dann alle Ihre fehlerhaften Commits in der Rebase-Datei als "Bearbeiten". Wenn Sie auch Ihr erstes Commit ändern möchten, müssen Sie es manuell als erste Zeile in die Rebase-Datei einfügen (folgen Sie dem Format der anderen Zeilen). Wenn git Sie dann auffordert, jedes Commit zu ändern, tun Sie dies

 git commit --amend --author "New Author Name <[email protected]>" 

Bearbeiten oder schließen Sie einfach den geöffneten Editor und tun Sie es dann

git rebase --continue

um die Rebase fortzusetzen.

Sie können das Öffnen des Editors hier ganz überspringen, indem Sie anhängen, --no-edit sodass der Befehl wie folgt lautet:

git commit --amend --author "New Author Name <[email protected]>" --no-edit && \
git rebase --continue

Single Commit

Wie einige Kommentatoren bemerkt haben, ist der Befehl rebase nicht erforderlich, wenn Sie nur das letzte Commit ändern möchten. Mach einfach

 git commit --amend --author "New Author Name <[email protected]>"

Dadurch wird den Autor zu dem angegebenen Namen ändern, aber die Committer werden in Ihren konfigurierten Benutzer eingestellt werden git config user.nameund git config user.email. Wenn Sie den Committer auf einen von Ihnen angegebenen Wert festlegen möchten, werden sowohl der Autor als auch der Committer festgelegt:

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

Hinweis zu Merge Commits

In meiner ursprünglichen Antwort gab es einen kleinen Fehler. Wenn es Zusammenführungs-Commits zwischen dem aktuellen HEADund Ihrem gibt <some HEAD before all your bad commits>, git rebasewerden diese abgeflacht (und wenn Sie GitHub-Pull-Anforderungen verwenden, wird es übrigens eine Menge Zusammenführungs-Commits in Ihrem Verlauf geben). Dies kann sehr oft zu einem sehr unterschiedlichen Verlauf führen (da doppelte Änderungen möglicherweise "umbasiert" werden) und im schlimmsten Fall dazu führen, git rebasedass Sie aufgefordert werden, schwierige Zusammenführungskonflikte zu lösen (die wahrscheinlich bereits in den Zusammenführungs-Commits gelöst wurden). Die Lösung besteht darin, das -pFlag to zu verwenden git rebase, um die Zusammenführungsstruktur Ihres Verlaufs beizubehalten. Die Manpage für git rebasewarnt davor, dass die Verwendung -pund -izu Problemen führen kann, aber in derBUGS In diesem Abschnitt heißt es: "Das Bearbeiten von Commits und das Umformulieren ihrer Commit-Nachrichten sollte einwandfrei funktionieren."

Ich habe -pden obigen Befehl hinzugefügt . Für den Fall, dass Sie nur das letzte Commit ändern, ist dies kein Problem.

asmeurer
quelle
27
Großartig für das eine oder andere Commit - nützlich, wenn Sie ein
Paar bilden
32
+1 für die Erwähnung des Anwendungsfalls für die typische Fehlerbehebung: git commit --amend --author = Benutzername
Nathan Kidd
12
Dies ist perfekt. Mein häufigster Anwendungsfall ist, dass ich mich an einen anderen Computer setze und vergesse, den Autor einzurichten, und daher normalerweise <5 Commits oder so zu beheben habe.
Zitrax
57
git commit --amend --reset-authorfunktioniert auch einmal user.nameund user.emailist korrekt konfiguriert.
Punkte
14
Schreiben Sie die Autoreninformationen zu allen Commits nach der <commit>Verwendung von user.nameund user.emailvon neu ~/.gitconfig: Ausführen git rebase -i <commit> --exec 'git commit --amend --reset-author --no-edit', Speichern, Beenden. Keine Notwendigkeit zu bearbeiten!
ntc2
588

Sie können auch tun:

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

Beachten Sie , wenn Sie diesen Befehl in der Windows - Eingabeaufforderung verwenden, dann müssen Sie verwenden "statt ':

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
Rognon
quelle
4
Ist die Verwendung von env-filter nicht die einfachere Lösung? Ich bin mir nicht sicher, warum dies dann mehr Stimmen bekommt.
Stigkj
3
Dann ist die Verbindung unterbrochen. Wie übertragen wir diese Änderungen in ein anderes Repository?
Russell
28
env-filter ändert alle Commits. Diese Lösung ermöglicht eine Bedingung.
user208769
5
"A previous backup already exists in refs/original/ Force overwriting the backup with -f"Entschuldigung, aber wo wird die -f-flag sein, wenn wir dieses Skript zweimal ausführen. Eigentlich ist das in Brians Antwort, entschuldigen Sie die Störung kurz nachdem der Filterzweig die Lösung ist.
hhh
2
@ user208769 env-filter erlaubt auch eine Bedingung; schau dir meine Antwort an :-)
stigkj
559

Ein Liner, aber seien Sie vorsichtig, wenn Sie ein Repository für mehrere Benutzer haben. Dadurch werden alle Commits so geändert , dass sie denselben (neuen) Autor und Committer haben.

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

Mit Zeilenumbrüchen in der Zeichenfolge (was in Bash möglich ist):

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
Brian Gianforcaro
quelle
Kleiner Punkt, der Export ist eigentlich überflüssig, obwohl er keinen Schaden anrichtet. zB git-filter-branch --env-filter "GIT_AUTHOR_NAME = 'Neuer Name'; GIT_AUTHOR_EMAIL = 'Neue E-Mail'" HEAD.
Alec der Geek
4
Warum werden alle Commits neu geschrieben, wenn Sie HEADam Ende des Befehls angeben ?
Nick Volynkin
1
Dies funktioniert nicht für mein Bitbucket-Repository, eine Idee? Ich mache ein git push --force --tags origin 'refs/heads/*'nach dem empfohlenen Befehl
Olorin
1
Der Push-Befehl dafür lautet:$git push --force --tags origin 'refs/heads/master'
HARSH NILESH PATHAK
1
Ordentlich; Dadurch bleiben auch die alten Zeitstempel erhalten.
DharmaTurtle
221

Dies passiert, wenn Sie kein $ HOME / .gitconfig initialisiert haben. Sie können dies wie folgt beheben:

git config --global user.name "you name"
git config --global user.email [email protected]
git commit --amend --reset-author

getestet mit git version 1.7.5.4

lrkwz
quelle
9
Das funktioniert beim letzten Commit sehr gut. Schön und einfach. Nicht haben , um eine globale Veränderung, mit --localWerken zu
Ben
Dieser war der große Gewinner für mich! Der git commit --amend --reset-author --no-editBefehl ist besonders nützlich, wenn Sie Commits mit den falschen Autoreninformationen erstellt und dann den richtigen Autor nachträglich über festgelegt haben git config. Ich habe gerade mein Geld gespart, als ich meine E-Mail aktualisieren musste.
Ecbrodie
187

Für ein einzelnes Commit:

git commit --amend --author="Author Name <[email protected]>"

(Auszug aus der Antwort von Asmeurer)

blau gefärbt
quelle
14
aber das ist nur, wenn es das letzte Commit ist
Richard
4
Entsprechend git help commit, git commit --amendändert sich die an der „Spitze des Stromzweig“ commit (der Kopf). Dies ist normalerweise der letzte begehen, aber man kann es machen jede begehen wollen , indem zuerst Überprüfung heraus , die mit begehen git checkout <branch-name>oder git checkout <commit-SHA>.
Rory O'Kane
12
Wenn Sie dies tun, zeigen alle Commits, die dieses Commit bereits als übergeordnetes Commit haben, auf das falsche Commit. Verwenden Sie an dieser Stelle besser den Filterzweig.
John Gietzen
3
@JohnGietzen: Sie können die Commits wieder auf das zurücksetzen, das geändert wurde, um das zu beheben. Wenn Sie jedoch> 1 Commit ausführen, wird der Filterzweig, wie bereits erwähnt, wahrscheinlich viel einfacher.
Thanatos
5
Beachten Sie, dass diese Änderungen nur authorcommitter
festschreiben
179

In dem Fall, in dem nur die obersten Commits schlechte Autoren haben, können Sie dies alles innerhalb git rebase -ides execBefehls und des --amendCommits wie folgt tun :

git rebase -i HEAD~6 # as required

Hier finden Sie die bearbeitbare Liste der Commits:

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

Fügen Sie dann exec ... --author="..."nach allen Zeilen mit schlechten Autoren Zeilen hinzu:

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

Speichern und Beenden des Editors (zum Ausführen).

Diese Lösung ist möglicherweise länger zu tippen als einige andere, aber sie ist in hohem Maße kontrollierbar - ich weiß genau, welche Commits sie trifft.

Vielen Dank an @asmeurer für die Inspiration.

Alex Brown
quelle
26
Auf jeden Fall super. Können Sie es verkürzen, indem Sie user.name und user.email in der lokalen Konfiguration des Repos festlegen, und dann ist jede Zeile nur noch exec git commit --amend --reset-author -C HEAD?
Andrew
1
Die kanonische Antwort, um Filter-Branch zu verwenden, hat gerade Refs / Heads / Master für mich gelöscht. Also +1 zu Ihrer steuerbaren, bearbeitbaren Lösung. Vielen Dank!
jmtd
Warum fängst du mit Someone else's commitstatt an my bad commit 1? Ich habe gerade versucht HEAD^^, die letzten beiden Commits zu ändern, und es hat einwandfrei funktioniert.
Fredoverflow
3
Anstelle von können git rebase -i HEAD^^^^^^Sie auch schreibengit rebase -i HEAD~6
Patrick Schlüter
1
Bitte beachten Sie, dass dies den Zeitstempel der Commits ändert. Siehe stackoverflow.com/a/11179245/1353267 für das Zurücksetzen auf die richtigen Zeitstempel
Samveen
111

Github hat eine nette Lösung , nämlich das folgende Shell-Skript:

#!/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" = "[email protected]" ]
then
    cn="Your New Committer Name"
    cm="Your New Committer Email"
fi
if [ "$GIT_AUTHOR_EMAIL" = "[email protected]" ]
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"
'
Olivier Verdier
quelle
5
Hat perfekt funktioniert. Musste nur git reset --hard HEAD^ein paar Mal auf den anderen lokalen Repositories, um sie auf eine frühere Version zu bringen, git pull-ed die geänderte Version, und hier bin ich ohne Zeilen, die enthalten unknown <[email protected]>(ich muss die Standardeinstellung von git lieben).
Alan Plum
1
Ich kann danach nicht mehr pushen. Muss ich "-f" verwenden?
Fisch - Monitor
9
Ich habe es getan git push -f. Außerdem müssen lokale Repos danach neu geklont werden.
Fisch - Monitor
Wenn Sie das Shell-Skript für einen bestimmten Zweig ausführen müssen, können Sie die letzte Zeile in "'master..your-branch-name" ändern (vorausgesetzt, Sie haben vom Master verzweigt).
Robert Kajic
Klicken Sie auf den Link <nette Lösung>, da das Skript aktualisiert wurde
gxpr
82

Wie bereits erwähnt, ist das Umschreiben des Verlaufs gefährlich und führt dazu, dass die Repositories anderer Personen beschädigt werden.

Aber wenn Sie das wirklich wollen und sich in einer Bash-Umgebung befinden (kein Problem unter Linux, unter Windows können Sie Git Bash verwenden, das mit der Installation von Git geliefert wird), verwenden Sie 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'

Um die Arbeit zu beschleunigen, können Sie eine Reihe von Revisionen angeben, die Sie neu schreiben möchten:

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
svick
quelle
2
Beachten Sie, dass dadurch alle Tags auf die alten Commits verweisen. --tag-name-filter catist die Option "Lass es funktionieren".
Roman Starkov
@romkyns eine Idee, wie man auch Tags ändert?
Nick Volynkin
@NickVolynkin Ja, Sie geben an --tag-name-filter cat. Dies sollte wirklich das Standardverhalten gewesen sein.
Roman Starkov
48

Wenn Sie ein nicht zusammengeführtes Commit von einem anderen Autor übernehmen, gibt es eine einfache Möglichkeit, damit umzugehen.

git commit --amend --reset-author

Ryanmt
quelle
1
Für ein einzelnes Commit und wenn Sie Ihren Benutzernamen eingeben möchten, ist dies der einfachste Weg.
Pedro Benevides
7
Sie können hinzufügen --no-edit, um dies noch einfacher zu machen, da im Allgemeinen die meisten Leute nur die E-Mail-Adresse und nicht die Commit-Nachricht aktualisieren möchten
PlagueHammer
Könnt ihr bitte den Befehl git teilen, um nur die E-Mail / den Benutzernamen des letzten Commits mit dem neuen zu aktualisieren
adi
Hast du das versucht? Das sollte ein Nebeneffekt sein, wenn nicht stackoverflow.com/a/2717477/654245 wie ein guter Weg aussieht.
Ryanmt
46

Sie können dies als Alias ​​verwenden, um Folgendes zu tun:

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

oder für die letzten 10 Commits:

git change-commits GIT_AUTHOR_EMAIL "[email protected]" "[email protected]" HEAD~10..HEAD

Zu ~ / .gitconfig hinzufügen:

[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 "

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

Hoffe es ist nützlich.

Brauliobo
quelle
"git: 'change-commits' ist kein git-Befehl. Siehe 'git --help'."
Native_Mobile_Arch_Dev
Nach diesem Befehl und der Synchronisierung mit dem Master werden alle Commits im Verlauf dupliziert! Auch von anderen Benutzern :(
Vladimir
@ Vladimir das erwartet wird, bitte studieren Sie über die Veränderung der Geschichte in Git
Brauliobo
39

Dies ist eine ausführlichere Version von @ Brians Version:

Um den Autor und den Committer zu ändern, können Sie dies tun (mit Zeilenumbrüchen in der Zeichenfolge, die in bash möglich sind):

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

Möglicherweise wird einer der folgenden Fehler angezeigt:

  1. Das temporäre Verzeichnis existiert bereits
  2. Refs, die mit refs / original beginnen , sind bereits vorhanden
    (dies bedeutet, dass zuvor ein anderer Filterzweig im Repository ausgeführt wurde und die ursprüngliche Originalverzweigungsreferenz bei refs / original gesichert wird ).

Wenn Sie den Lauf trotz dieser Fehler erzwingen möchten, fügen Sie das --forceFlag hinzu:

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

-- --allMöglicherweise ist eine kleine Erläuterung der Option erforderlich: Dadurch funktioniert der Filterzweig bei allen Revisionen aller Refs (einschließlich aller Zweige). Dies bedeutet zum Beispiel, dass Tags ebenfalls neu geschrieben werden und in den neu geschriebenen Zweigen sichtbar sind.

Ein häufiger "Fehler" ist die Verwendung, HEADdh das Filtern aller Revisionen nur für den aktuellen Zweig . Und dann würden keine Tags (oder andere Verweise) in dem umgeschriebenen Zweig existieren.

stigkj
quelle
Ein großes Lob für die Bereitstellung eines Verfahrens, das Commits für alle Refs / Zweige ändert .
Johnny Utahh
25

Ein einzelner Befehl zum Ändern des Autors für die letzten N Commits:

git rebase -i HEAD~4 -x "git commit --amend --author 'Author Name <[email protected]>' --no-edit"

ANMERKUNGEN

  • Die --no-editFlagge stellt sicher, git commit --amenddass keine zusätzliche Bestätigung angefordert wird
  • Wenn Sie verwenden git rebase -i, können Sie manuell festlegen, wo der Autor geändert werden soll.

Die von Ihnen bearbeitete Datei sieht folgendermaßen aus:

pick 897fe9e simplify code a little
exec git commit --amend --author 'Author Name <[email protected]>' --no-edit
pick abb60f9 add new feature
exec git commit --amend --author 'Author Name <[email protected]>' --no-edit
pick dc18f70 bugfix
exec git commit --amend --author 'Author Name <[email protected]>' --no-edit

Sie können dann noch einige Zeilen ändern, um zu sehen, wo Sie den Autor ändern möchten. Dies gibt Ihnen einen guten Mittelweg zwischen Automatisierung und Steuerung: Sie sehen die Schritte, die ausgeführt werden, und sobald Sie alles gespeichert haben, wird alles auf einmal angewendet.

Chris Maes
quelle
Genial! Vielen Dank!
Pablo Lalloni
Ich habe HEAD ~ 8 verwendet und es zeigt weit mehr als die letzten 8 Commits.
Bryan Bryce
1
@BryanBryce Wenn es sich um Merge-Commits handelt, werden die Dinge kompliziert :)
Chris Maes
@ ChrisMaes Ah, ich sehe, was los ist. Ich möchte mich nicht mit denen anlegen, nur auf dem Ast, auf dem ich bin.
Bryan Bryce
In diesem Fall könnten Sie, vorausgesetzt, Sie haben sich vom Meister getrennt, Folgendes tun:git rebase -i master -x ...
Chris Maes
23
  1. Lauf git rebase -i <sha1 or ref of starting point>
  2. Markieren Sie alle Commits, die Sie ändern möchten, mit edit(oder e)
  3. Führen Sie die folgenden zwei Befehle durch, bis Sie alle Commits verarbeitet haben:

    git commit --amend --reuse-message=HEAD --author="New Author <[email protected]>" ;; git rebase --continue

Dadurch bleiben alle anderen Festschreibungsinformationen (einschließlich der Daten) erhalten. Die --reuse-message=HEADOption verhindert, dass der Nachrichteneditor gestartet wird.

sporsh
quelle
23

Ich verwende Folgendes, um den Autor für ein gesamtes Repository einschließlich Tags und aller Zweige neu zu schreiben:

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

Entfernen Sie dann, wie auf der MAN-Seite von filter-branch beschrieben , alle ursprünglichen Refs, die von gesichert wurden filter-branch(dies ist destruktiv, sichern Sie zuerst):

git for-each-ref --format="%(refname)" refs/original/ | \
xargs -n 1 git update-ref -d
Ton van den Heuvel
quelle
2
Es ist sehr wichtig zu verwenden --tag-name-filter cat. Andernfalls bleiben Ihre Tags in der ursprünglichen Commit-Kette. Die anderen Antworten erwähnen dies nicht.
Jeberle
21

Ich habe diese Lösung angepasst, indem ich eine einfache Lösung einnehmeauthor-conv-file (das Format ist das gleiche wie bei git-cvsimport ). Es funktioniert, indem alle Benutzer geändert werden, wie in author-conv-fileallen Zweigen definiert.

Wir haben dies in Verbindung mit verwendet cvs2git, um unser Repository von cvs auf git zu migrieren.

dh Probe author-conv-file

john=John Doe <[email protected]>
jill=Jill Doe <[email protected]>

Das Skript:

 #!/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
Leif Gruenwoldt
quelle
Vielen Dank, ich frage mich, warum dies nicht die Kernfunktion von git (oder git-svn) ist. Dies kann mit einem Flag für Git-SVN-Klon erfolgen, jedoch nicht in Git-Filter-Zweig ...
Daniel Hershcovich
20

Ich sollte darauf hinweisen, dass wenn das einzige Problem darin besteht, dass der Autor / die E-Mail von Ihrem üblichen abweicht, dies kein Problem ist. Die richtige Lösung besteht darin, eine Datei zu erstellen, die .mailmapan der Basis des Verzeichnisses mit Zeilen wie aufgerufen wird

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

Und von da an betrachten Befehle wie git shortlogdiese beiden Namen als gleich (es sei denn, Sie weisen sie ausdrücklich an, dies nicht zu tun). Weitere Informationen finden Sie unter http://schacon.github.com/git/git-shortlog.html .

Dies hat den Vorteil aller anderen Lösungen hier, dass Sie den Verlauf nicht neu schreiben müssen, was bei einem Upstream zu Problemen führen kann und immer eine gute Möglichkeit ist, versehentlich Daten zu verlieren.

Wenn Sie etwas als sich selbst begangen haben und es eigentlich jemand anderes sein sollte und es Ihnen nichts ausmacht, den Verlauf an dieser Stelle neu zu schreiben, ist es wahrscheinlich eine gute Idee, den Commit-Autor zu ändern, um ihn zuzuordnen (in diesem Fall leite ich Sie zu meinem andere Antwort hier).

asmeurer
quelle
18

Ich fand die vorgestellten Versionen viel zu aggressiv, besonders wenn Sie Patches von anderen Entwicklern festschreiben, wird dies im Wesentlichen deren Code stehlen.

Die folgende Version funktioniert in allen Zweigen und ändert den Autor und den Comitter separat, um dies zu verhindern.

Ein großes Lob an leif81 für die Option all.

#!/bin/bash

git filter-branch --env-filter '
if [ "$GIT_AUTHOR_NAME" = "<old author>" ];
then
    GIT_AUTHOR_NAME="<new author>";
    GIT_AUTHOR_EMAIL="<[email protected]>";
fi
if [ "$GIT_COMMITTER_NAME" = "<old committer>" ];
then
    GIT_COMMITTER_NAME="<new commiter>";
    GIT_COMMITTER_EMAIL="<[email protected]>";
fi
' -- --all
drahnr
quelle
18
  1. Ändern Sie das Commit author name & emaildurch Amendund ersetzen Sie dann old-commit with new-one:

    $ git checkout <commit-hash>                            # checkout to the commit need to modify  
    $ git commit --amend --author "name <[email protected]>" # 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. Ein anderer Weg 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 <[email protected]>"  # change the author name & email
    
    # Save changes and exit the editor
    
    $ git rebase --continue                # finish the rebase
    
Sajib Khan
quelle
2
Sehr schöne Antwort. Ich mag es, dass die Änderungen vom eigentlichen Update bis zur Bereinigung der Git-Commits abgeschlossen sind
Aleks
12

Der schnellste und einfachste Weg, dies zu tun, ist das Argument --exec von git rebase:

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

Dadurch wird eine Aufgabenliste erstellt, die folgendermaßen aussieht:

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
...

und dies funktioniert alles automatisch, was funktioniert, wenn Sie Hunderte von Commits haben.

Lie Ryan
quelle
9

Wenn Sie der einzige Benutzer dieses Repositorys sind, können Sie den Verlauf entweder mit git filter-branch(wie von svick geschrieben ) oder mit dem Filterskriptgit fast-export / git fast-importplus (wie in dem Artikel beschrieben, auf den in der Antwort von docgnome verwiesen wird ) oder mit interaktiver Basis neu schreiben . Beides würde jedoch die Revisionen ab dem ersten geänderten Commit ändern. Dies bedeutet Ärger für jeden, der seine Änderungen auf Ihrem Zweig vor dem Umschreiben basiert.

WIEDERHERSTELLUNG

Wenn andere Entwickler ihre Arbeit nicht auf der Version vor dem Umschreiben basieren würden, wäre die einfachste Lösung das erneute Klonen (erneutes Klonen).

Alternativ können sie versuchen git rebase --pull, einen schnellen Vorlauf durchzuführen, wenn keine Änderungen in ihrem Repository vorgenommen wurden, oder ihren Zweig zusätzlich zu neu geschriebenen Commits neu zu gründen (wir möchten das Zusammenführen vermeiden, da dadurch vorab neu geschriebene Commits für immer erhalten bleiben). All dies unter der Annahme, dass sie keine Arbeit geleistet haben; Verwenden Sie git stashdiese Option, um Änderungen anderweitig aufzubewahren.

Wenn andere Entwickler Feature-Zweige verwenden und / oder git pull --rebasenicht funktionieren, z. B. weil Upstream nicht eingerichtet ist, müssen sie ihre Arbeit zusätzlich zu den Commits nach dem Umschreiben neu aufbauen . Zum Beispiel muss unmittelbar nach dem Abrufen neuer Änderungen ( git fetch) für einen masterZweig, der auf / forked from basiert origin/master, ausgeführt werden

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

Hier origin/master@{1}ist der Zustand vor dem Umschreiben (vor dem Abrufen), siehe Gitrevisions .


Eine alternative Lösung wäre die Verwendung von Refs / Replace / Mechanism, die in Git seit Version 1.6.5 verfügbar sind. In dieser Lösung ersetzen Sie Commits mit falschen E-Mails. dann würde jeder, der Refs zum Ersetzen abruft (so etwas wie fetch = +refs/replace/*:refs/replace/*Refspec an geeigneter Stelle in seinem .git/config ), transparent ersetzt werden, und diejenigen, die diese Refs nicht abrufen, würden alte Commits sehen.

Das Verfahren sieht ungefähr so ​​aus:

  1. Finden Sie alle Commits mit falscher E-Mail, zum Beispiel mit

    $ git log [email protected] --all
    
  2. Erstellen Sie für jedes falsche Commit ein Ersatz-Commit und fügen Sie es der Objektdatenbank hinzu

    $ git cat-file -p <ID of wrong commit> | 
      sed -e 's/user@wrong\.email/[email protected]/g' > tmp.txt
    $ git hash-object -t commit -w tmp.txt
    <ID of corrected commit>
    
  3. Nachdem Sie das Commit in der Objektdatenbank korrigiert haben, müssen Sie git anweisen, das falsche Commit automatisch und transparent durch das korrigierte Commit mit dem folgenden git replaceBefehl zu ersetzen :

    $ git replace <ID of wrong commit> <ID of corrected commit>
    
  4. Listen Sie abschließend alle Ersetzungen auf, um zu überprüfen, ob dieser Vorgang erfolgreich war

    $ git replace -l
    

    und prüfen Sie, ob ein Austausch erfolgt

    $ git log [email protected] --all
    

Sie können diesen Vorgang natürlich automatisieren ... nun, alle außer der Verwendung, git replacedie (noch) keinen Batch-Modus hat. Sie müssten also dafür eine Shell-Schleife verwenden oder "von Hand" ersetzen.

NICHT GETESTET! YMMV.

Beachten Sie, dass bei der Verwendung des refs/replace/Mechanismus möglicherweise einige Ecken und Kanten auftreten: Er ist neu und noch nicht sehr gut getestet .

Jakub Narębski
quelle
6

Wenn die Commits, die Sie korrigieren möchten, die neuesten sind und nur einige davon, können Sie eine Kombination aus git resetund verwendengit stash , um sie erneut zu bestätigen, nachdem Sie den richtigen Namen und die richtige E-Mail-Adresse konfiguriert haben.

Die Sequenz sieht ungefähr so ​​aus (für 2 falsche Commits, keine ausstehenden Änderungen):

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
Djromero
quelle
5

Wenn Sie Eclipse mit EGit verwenden, gibt es eine recht einfache Lösung.
Annahme: Sie haben Commits in einem lokalen Zweig 'local_master_user_x', die aufgrund des ungültigen Benutzers nicht an einen 'Zweig' der entfernten Zweigstelle übertragen werden können.

  1. Kasse des Remote-Zweigs 'Master'
  2. Wählen Sie die Projekte / Ordner / Dateien aus, für die 'local_master_user_x' Änderungen enthält
  3. Rechtsklick - Ersetzen durch - Zweig - 'local_master_user_x'
  4. Übernehmen Sie diese Änderungen erneut, diesmal als korrekter Benutzer und in den lokalen Zweig 'master'.
  5. Zum Remote-Master drücken
paphko
quelle
5

Mit der interaktiven Rebase können Sie nach jedem Commit, den Sie ändern möchten, einen Änderungsbefehl platzieren. Zum Beispiel:

pick a07cb86 Project tile template with full details and styling
x git commit --amend --reset-author -Chead
j16r
quelle
3
Das Problem dabei ist, dass auch andere Commit-Metadaten (z. B. Datum und Uhrzeit) geändert werden. Ich habe das nur auf die harte Tour herausgefunden ;-).
Halfer
5

Beachten Sie, dass git zwei verschiedene E-Mail-Adressen speichert , eine für den Committer (die Person, die die Änderung übernommen hat) und eine für den Autor (die Person, die die Änderung geschrieben hat).

Die Committer-Informationen werden an den meisten Stellen nicht angezeigt, aber Sie können sie mit anzeigen git log -1 --format=%cn,%ce(oder verwenden, showanstatt logein bestimmtes Commit anzugeben).

Während das Ändern des Autors Ihres letzten Commits so einfach wie möglich ist git commit --amend --author "Author Name <[email protected]>", gibt es keinen Einzeiler oder kein Argument, um dasselbe mit den Committer-Informationen zu tun.

Die Lösung besteht darin, Ihre Benutzerinformationen (vorübergehend oder nicht) zu ändern und dann das Commit zu ändern, wodurch der Committer auf Ihre aktuellen Informationen aktualisiert wird:

git config user.email [email protected] 
git commit --amend
Sir Athos
quelle
Beachten Sie, dass der alte Wert noch an einigen Stellen in ist path\to\repo\.git. Ich bin mir noch nicht sicher, was Sie tun müssen, um es vollständig zu löschen. Änderungen scheinen leider (?) Nicht zu löschen.
Ruffin
5

Wir haben heute ein Problem festgestellt, bei dem ein UTF8-Zeichen in einem Autorennamen Probleme auf dem Build-Server verursachte. Daher mussten wir den Verlauf neu schreiben, um dies zu korrigieren. Die unternommenen Schritte waren:

Schritt 1: Ändern Sie Ihren Benutzernamen in git für alle zukünftigen Commits gemäß den Anweisungen hier: https://help.github.com/articles/setting-your-username-in-git/

Schritt 2: Führen Sie das folgende Bash-Skript aus:

#!/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="[email protected]"
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

Kurzübersicht: Checken Sie Ihr Repository in eine temporäre Datei aus, checken Sie alle Remote-Zweige aus, führen Sie das Skript aus, mit dem der Verlauf neu geschrieben wird, erzwingen Sie einen Push des neuen Status und weisen Sie alle Kollegen an, einen Rebase-Pull durchzuführen, um die Änderungen zu erhalten.

Wir hatten Probleme damit, dies unter OS X auszuführen, da es die Zeilenenden in Festschreibungsnachrichten irgendwie durcheinander brachte, sodass wir es anschließend auf einem Linux-Computer erneut ausführen mussten.

Miloš Ranđelović
quelle
5

Ihr Problem ist wirklich häufig. Siehe " Verwenden von Mailmap zum Korrigieren der Autorenliste in Git "

Der Einfachheit halber habe ich ein Skript erstellt, um den Prozess zu vereinfachen: git-changemail

Nachdem Sie dieses Skript in Ihren Pfad eingefügt haben, können Sie folgende Befehle ausführen:

  • Ändern Sie die Autorenübereinstimmungen im aktuellen Zweig

    $ git changemail -a [email protected] -n newname -m [email protected]
    
  • Ändern Sie die Autoren- und Committer-Übereinstimmungen auf <branch> und <branch2>. Übergeben Sie -fan Filter-Branch, um das Umschreiben von Backups zu ermöglichen

    $ git changemail -b [email protected] -n newname -m [email protected] -- -f &lt;branch> &lt;branch2>
    
  • Vorhandene Benutzer auf Repo anzeigen

    $ git changemail --show-both
    

Übrigens, nachdem Sie Ihre Änderungen vorgenommen haben, bereinigen Sie das Backup aus dem Filterzweig mit: git-backup-clean

Albfan
quelle
1
Wenn ich Ihren Befehl ausführe, heißt es "fatal: git-changemail 'kann nicht ausgeführt werden: Berechtigung verweigert"
Govind
3

Ich möchte auch mein Beispiel hinzufügen. Ich möchte eine bash_function mit einem bestimmten Parameter erstellen .

Dies funktioniert 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:-"[email protected]"}
 NEW_NAME=${2:-"your name"}
 NEW_EMAIL=${3:-"[email protected]"}

 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;
}
stephanfriedrich
quelle
2

Wenn Sie der einzige Benutzer dieses Repos sind oder es Ihnen nicht wichtig ist, das Repo möglicherweise für andere Benutzer zu brechen, dann ja. Wenn Sie diese Commits gepusht haben und sie dort existieren, wo irgendwo anders auf sie zugegriffen werden kann, nein, es sei denn, Sie kümmern sich nicht darum, die Repos anderer Leute zu brechen. Das Problem besteht darin, dass durch Ändern dieser Commits neue SHAs generiert werden, die dazu führen, dass sie als unterschiedliche Commits behandelt werden. Wenn jemand anderes versucht, diese geänderten Commits einzuziehen, ist die Historie anders und kaboom.

Diese Seite http://inputvalidation.blogspot.com/2008/08/how-to-change-git-commit-author.html beschreibt, wie es geht. (Ich habe das nicht so YMMV versucht)

Baudtack
quelle
Es gibt also keine sichere Möglichkeit, die user.email neu zu schreiben. Ohne alle anderen in die Luft zu jagen. Ich wusste, dass das Umschreiben der Geschichte eine schlechte Idee war. Ich dachte nur, dass es einen sauberen Weg geben könnte, dies sicher zu tun. Vielen Dank.
Manumoomoo
@mediaslave: refs/replace/Mechanismus ausprobieren .
Jakub Narębski
meta.stackexchange.com/a/8259/184684 - aka, summiere Links, um sie zu Antworten zu machen.
Ruffin