Jak zmienić nazwisko autora i autora danych oraz adres e-mail wielu zatwierdzeń w Git?

2391

Pisałem prosty skrypt na szkolnym komputerze i zatwierdzałem zmiany w Git (w repozytorium, które było w moim pendrive, sklonowane z mojego komputera w domu). Po kilku zatwierdzeniach zdałem sobie sprawę, że popełniam rzeczy jako użytkownik root.

Czy jest jakiś sposób na zmianę autora tych zatwierdzeń na moje imię?

Flávio Amieiro
źródło
13
Pytanie: czy użycie git filter-branch chroni SHA1 dla poprzednich tagów, wersji i obiektów? A może zmiana nazwy autora wymusi również zmianę powiązanych SHA1?
AndyL
36
Hashe się zmienią tak
Niedostępne
3
Stycznie stworzyłem mały skrypt, który ostatecznie ustalił dla mnie główną przyczynę. gist.github.com/tripleee/16767aa4137706fd896c
tripleee
2
@impinball Wiek pytania jest mało istotny. Tworzenie nowego duplikatu pytania jest wykluczone. Przypuszczam, że mógłbym stworzyć pytanie, które prosi o tę konkretną odpowiedź, ale nie jestem całkowicie przekonany, że dostanie tyle widoczności. To nie tak, że brakuje tutaj pytań Gita ... Cieszę się, że i tak mogłem pomóc.
tripleee
8
GitHub ma do tego specjalny skrypt: help.github.com/articles/changing-author-info
Timur Bernikovich

Odpowiedzi:

1210

Używa tej odpowiedzi git-filter-branch, w przypadku której dokumenty dają teraz to ostrzeżenie:

git filter-branch ma mnóstwo pułapek, które mogą produkować nieoczywiste zmiany w planowanej historii (i mogą pozostawić ci niewiele czasu na badanie takich problemów, ponieważ mają tak fatalną wydajność). Tych problemów związanych z bezpieczeństwem i wydajnością nie można naprawić wstecz, dlatego też nie jest zalecane ich stosowanie. Użyj alternatywnego narzędzia do filtrowania historii, takiego jak git filter-repo . Jeśli nadal musisz używać gałęzi filtrującej git, przeczytaj uważnie BEZPIECZEŃSTWO (i WYDAJNOŚĆ ), aby dowiedzieć się o minach lądowych gałęzi filtrujących, a następnie czujnie unikaj jak największej liczby wymienionych tam zagrożeń.

Zmiana autora (lub autora) wymagałaby ponownego napisania całej historii. Jeśli nie masz nic przeciwko temu i uważasz, że warto, powinieneś sprawdzić git filter-branch . Strona podręcznika zawiera kilka przykładów na początek. Zauważ też, że możesz użyć zmiennych środowiskowych, aby zmienić nazwisko autora, autora, daty itp. - patrz sekcja „Zmienne środowiskowe” na stronie man git .

W szczególności za pomocą tego polecenia możesz naprawić wszystkie niepoprawne nazwy autorów i adresy e-mail dla wszystkich gałęzi i tagów (źródło: pomoc GitHub ):

#!/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
źródło
610
Github ma publiczny skrypt do tego help.github.com/articles/changing-author-info i działa świetnie!
defvol
34
Po uruchomieniu skryptu możesz usunąć gałąź kopii zapasowej, wykonując polecenie „git update-ref -d refs / original / refs / heads / master”.
DR
7
@rodowi, powiela wszystkie moje zobowiązania.
Rafael Barros,
6
@RafaelBarros informacje o autorze (podobnie jak wszystko inne w historii) są częścią klucza sha zatwierdzenia. Każda zmiana w historii jest przepisywaniem prowadzącym do nowego identyfikatora dla wszystkich zatwierdzeń. Więc nie przepisuj na udostępnionym repozytorium lub upewnij się, że wszyscy użytkownicy są tego świadomi ...
johannes
20
Rozwiązany przy użyciugit push --force --tags origin HEAD:master
mcont
1577

UWAGA: Ta odpowiedź zmienia SHA1, więc uważaj, aby użyć jej na gałęzi, która została już wypchnięta. Jeśli chcesz tylko poprawić pisownię nazwy lub zaktualizować stary e-mail, git pozwala to zrobić bez przepisywania historii przy użyciu .mailmap. Zobacz moją drugą odpowiedź .

Korzystanie z Interactive Rebase

Mógłbyś

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

Następnie zaznacz wszystkie swoje złe zmiany jako „edytuj” w pliku bazy danych. Jeśli chcesz również zmienić swoje pierwsze zatwierdzenie, musisz ręcznie dodać je jako pierwszą linię w pliku rebase (postępuj zgodnie z formatem pozostałych linii). Następnie, gdy git poprosi cię o zmianę każdego zatwierdzenia, zrób to

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

edytuj lub po prostu zamknij edytor, który się otworzy, a następnie zrób

git rebase --continue

aby kontynuować rebase.

Możesz pominąć otwarcie edytora tutaj, dodając, --no-edit aby polecenie było:

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

Single Commit

Jak zauważyli niektórzy komentatorzy, jeśli chcesz tylko zmienić ostatnie zatwierdzenie, polecenie rebase nie jest konieczne. Po prostu zrób

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

Spowoduje to zmianę autora na podaną nazwę, ale podmiot ustawiający zostanie ustawiony na skonfigurowanego użytkownika w git config user.namei git config user.email. Jeśli chcesz ustawić moduł ustalający na określony przez siebie element, spowoduje to ustawienie zarówno autora, jak i modułu zmieniającego:

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

Uwaga dotycząca zatwierdzeń scalania

W mojej pierwotnej odpowiedzi była niewielka wada. Jeśli są jakieś zatwierdzenia scalania między bieżącym HEADa twoim <some HEAD before all your bad commits>, wówczas git rebaseje spłaszczysz (a przy okazji, jeśli użyjesz żądań ściągania GitHub, w twojej historii będzie mnóstwo zatwierdzeń scalania). Może to często prowadzić do bardzo odmiennej historii (ponieważ duplikaty zmian mogą zostać „wycofane”), aw najgorszym przypadku mogą prowadzić do git rebaseprośby o rozwiązanie trudnych konfliktów scalania (które prawdopodobnie zostały już rozwiązane w zatwierdzeniach scalania). Rozwiązaniem jest użycie -pflagi do git rebase, która zachowa strukturę scalania twojej historii. Strona podręcznika dla git rebaseostrzega, że ​​używanie -pi -imoże prowadzić do problemów, ale wBUGS w sekcji napisano: „Edytowanie zatwierdzeń i przeredagowanie ich komunikatów zatwierdzeń powinno działać dobrze”.

Dodałem -pdo powyższego polecenia. W przypadku, gdy zmieniasz tylko najnowsze zatwierdzenie, nie stanowi to problemu.

asmeurer
źródło
27
Świetny do dziwnego zatwierdzenia - przydatny, gdy
parujesz
32
+1 za wzmiankę o przypadku użycia typowej poprawki jednopoprawnej: git commit --amend --author = nazwa użytkownika
Nathan Kidd
12
Jest to idealne, moim najczęstszym przypadkiem jest to, że siadam przy innym komputerze i zapominam o ustawieniu autora, a zatem zwykle mam mniej niż 5 zmian do zatwierdzenia.
Zitrax,
57
git commit --amend --reset-authordziała również raz user.namei user.emailsą poprawnie skonfigurowane.
pkt
14
Przepisz informacje o autorze na temat wszystkich zatwierdzeń po <commit>użyciu user.namei user.emailod ~/.gitconfig: uruchom git rebase -i <commit> --exec 'git commit --amend --reset-author --no-edit', zapisz, wyjdź. Nie trzeba edytować!
ntc2
588

Możesz także:

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

Uwaga: jeśli używasz tego polecenia w wierszu polecenia systemu Windows, musisz użyć "zamiast ':

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
źródło
4
Czy stosowanie filtru env nie jest łatwiejszym rozwiązaniem? Nie jestem pewien, dlaczego dostaje to więcej głosów.
stigkj
3
Następnie link jest zepsuty. Jak wypychamy te zmiany do innego repozytorium?
Russell
28
filtr env zmieni wszystkie zatwierdzenia. To rozwiązanie pozwala na warunkowe.
user208769,
5
"A previous backup already exists in refs/original/ Force overwriting the backup with -f"przepraszam, ale gdzie -f-flag będzie musiał wykonać ten skrypt dwa razy. Właściwie to jest odpowiedź Briana, przepraszam za zakłócenia zaraz po tym, jak odgałęzienie filtra jest rozwiązaniem.
hhh
2
@ user208769 env-filter pozwala również na warunkowe; spójrz na moją odpowiedź :-)
stigkj
558

Jedna linijka, ale uważaj, jeśli masz repozytorium dla wielu użytkowników - spowoduje to zmianę wszystkich zatwierdzeń na tego samego (nowego) autora i osobę odpowiedzialną.

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

Z łamaniem wiersza w ciągu (co jest możliwe w 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
Brian Gianforcearo
źródło
Drobny punkt, eksport jest w rzeczywistości zbędny, chociaż nie szkodzi. np. git-filter-branch --env-filter "GIT_AUTHOR_NAME =„ Nowa nazwa ”; GIT_AUTHOR_EMAIL =„ Nowy e-mail ”„ HEAD.
Alec the Geek
4
Dlaczego przepisuje wszystkie zatwierdzenia, jeśli podasz je HEADna końcu polecenia?
Nick Volynkin,
1
To nie działa w moim repozytorium bitbucket, jakiś pomysł? Robię git push --force --tags origin 'refs/heads/*'po doradzał polecenia
Olorin
1
Polecenie push dla tego jest:$git push --force --tags origin 'refs/heads/master'
HARSH NILESH PATHAK
1
Schludny; zachowuje to również stare znaczniki czasu.
DharmaTurtle
221

Dzieje się tak, gdy nie masz zainicjowanego $ HOME / .gitconfig. Możesz to naprawić jako:

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

testowany z wersją git 1.7.5.4

lrkwz
źródło
9
To działa naprawdę dobrze przy ostatnim zatwierdzeniu. Ładne i proste. Nie musi to być globalna zmiana, także korzystanie z --localutworów
Ben
Ten był dla mnie wielkim zwycięzcą! git commit --amend --reset-author --no-editPolecenia jest szczególnie przydatna, gdy tworzony zobowiązuje ze złym informacje o autorze, a następnie ustawić właściwy autor po-fakcie poprzez git config. Zapisałem moje $$ właśnie teraz, kiedy musiałem zaktualizować swój e-mail.
ecbrodie
187

Dla pojedynczego zatwierdzenia:

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

(wyodrębniony z odpowiedzi pośrednika)

niebieskawy
źródło
14
ale to tylko wtedy, gdy jest to najnowsze zatwierdzenie
Richard,
4
Zgodnie z git help commit, git commit --amendzmienia zatwierdzenie na „wierzchołku bieżącej gałęzi” (czyli HEAD). Zwykle jest to ostatni zatwierdzenie, ale możesz dokonać dowolnego zatwierdzenia, najpierw sprawdzając ten zatwierdzenie za pomocą git checkout <branch-name>lub git checkout <commit-SHA>.
Rory O'Kane,
12
Ale jeśli to zrobisz, wszystkie zatwierdzenia, które już mają to zatwierdzenie jako rodzic, będą wskazywać na niewłaściwe zatwierdzenie. W tym momencie lepiej użyć gałęzi filter.
John Gietzen,
3
@JohnGietzen: Możesz zmienić bazę zatwierdzeń z powrotem na zmienioną, aby to naprawić. Jednakże, jeśli wykonujesz> 1 zatwierdzenie, to jak wspomniano, odgałęzienie filtra prawdopodobnie będzie o wiele łatwiejsze.
Thanatos
5
Zauważ, że zmiany te tylko zatwierdzają, authora niecommitter
Nick Volynkin,
179

W przypadku, gdy tylko kilka pierwszych zatwierdzeń ma złych autorów, możesz to wszystko zrobić git rebase -iza pomocą execpolecenia i --amendzatwierdzenia, w następujący sposób:

git rebase -i HEAD~6 # as required

która przedstawia edytowalną listę zatwierdzeń:

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

Następnie dodaj exec ... --author="..."wiersze po wszystkich wierszach ze złymi autorami:

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

zapisz i zamknij edytor (aby uruchomić).

To rozwiązanie może być dłuższe niż niektóre inne, ale można je w pełni kontrolować - wiem dokładnie, do czego się zobowiązuje.

Dzięki @asmeurer za inspirację.

Alex Brown
źródło
26
Zdecydowanie niesamowite. Czy możesz to skrócić, ustawiając user.name i user.email w lokalnej konfiguracji repo, a wtedy każda linia jest tylko exec git commit --amend --reset-author -C HEAD?
Andrew
1
Odpowiedź kanoniczna, żeby użyć gałęzi filter, właśnie dla mnie usunęła refs / heads / master. Więc +1 do kontrolowanego, edytowalnego rozwiązania. Dzięki!
jmtd,
Dlaczego zaczynasz Someone else's commitzamiast my bad commit 1? Właśnie próbowałem HEAD^^zmienić ostatnie 2 zatwierdzenia i wszystko działało idealnie.
fredoverflow
3
Na miejscu git rebase -i HEAD^^^^^^możesz także napisaćgit rebase -i HEAD~6
Patrick Schlüter
1
Pamiętaj, że zmienia to znacznik czasu zatwierdzeń. Zobacz stackoverflow.com/a/11179245/1353267, aby przywrócić prawidłowe znaczniki czasu
Samveen,
111

Github ma fajne rozwiązanie , którym jest następujący skrypt powłoki:

#!/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
źródło
5
Działał idealnie. Musiałem git reset --hard HEAD^tylko kilka razy w innych lokalnych repozytoriach, aby przenieść je do wcześniejszej wersji, git pull-ed poprawiona wersja, a tutaj nie mam żadnych wierszy zawierających unknown <[email protected]>(uwielbiam domyślną wersję gita).
Alan Plum
1
Nie mogę dalej naciskać. Czy muszę używać „-f”?
Fish Monitor
9
Zrobiłem git push -f. Ponadto lokalne repozytoria muszą zostać ponownie sklonowane.
Fish Monitor
Jeśli chcesz uruchomić skrypt powłoki na określonej gałęzi, możesz zmienić ostatni wiersz na: „'master..your-branch-name” (zakładając, że masz rozgałęzienie master).
Robert Kajic
Kliknij link <ładne rozwiązanie> po zaktualizowaniu skryptu
gxpr
82

Jak wspomniano docgnome, przepisywanie historii jest niebezpieczne i spowoduje uszkodzenie repozytoriów innych ludzi.

Ale jeśli naprawdę chcesz to zrobić i jesteś w środowisku bash (nie ma problemu w Linuksie, w systemie Windows, możesz użyć git bash, który jest dostarczany z instalacją git), użyj 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'

Aby przyspieszyć, możesz określić zakres poprawek, które chcesz przepisać:

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
źródło
2
Pamiętaj, że spowoduje to pozostawienie tagów wskazujących na stare zatwierdzenia. --tag-name-filter catto opcja „spraw, by działała”.
Roman Starkov
@romkyns również pomysł na zmianę tagów?
Nick Volynkin
@NickVolynkin Tak, określasz --tag-name-filter cat. To naprawdę powinno być zachowanie domyślne.
Roman Starkov,
48

W przypadku przejęcia niezałączonego zatwierdzenia od innego autora istnieje prosty sposób na poradzenie sobie z tym.

git commit --amend --reset-author

Ryanmt
źródło
1
W przypadku pojedynczego zatwierdzenia i jeśli chcesz podać swoją nazwę użytkownika, jest to najłatwiejszy sposób.
Pedro Benevides,
7
Możesz dodać, --no-editaby było to jeszcze łatwiejsze, ponieważ generalnie większość ludzi będzie chciała zaktualizować tylko adres e-mail, a nie wiadomość zatwierdzenia
PlagueHammer
Czy możecie, proszę, udostępnić polecenie git, aby tylko zaktualizować adres e-mail / nazwę użytkownika ostatniego zatwierdzenia nowym
adi
Próbowałeś tego? To powinien być efekt uboczny tego, jeśli nie stackoverflow.com/a/2717477/654245 wygląda na dobrą ścieżkę.
Ryanmt,
46

Możesz użyć tego jako aliasu, dzięki czemu możesz:

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

lub dla ostatnich 10 zatwierdzeń:

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

Dodaj do ~ / .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 "

Źródło: https://github.com/brauliobo/gitconfig/blob/master/configs/.gitconfig

Mam nadzieję, że jest to przydatne.

brauliobo
źródło
„git: 'change-commits' nie jest poleceniem git. Zobacz„ git --help ”.”
Native_Mobile_Arch_Dev
Po tym poleceniu i synchronizacji z master wszystkie zatwierdzenia w historii są duplikowane! Nawet innych użytkowników :(
Vladimir
@Wladimir, który jest oczekiwany, proszę przestudiować historię zmian w git
brauliobo
39

To jest bardziej rozbudowana wersja wersji @ Briana:

Aby zmienić autora i committera, możesz to zrobić (z łamaniem linii w ciągu, co jest możliwe w 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

Może pojawić się jeden z następujących błędów:

  1. Katalog tymczasowy już istnieje
  2. Refs zaczynające się od refs / original już istnieje
    (oznacza to, że poprzednio uruchomiono inną gałąź filtrów w repozytorium, a następnie tworzona jest kopia zapasowa referencji oddziału w refs / original )

Jeśli chcesz wymusić uruchomienie pomimo tych błędów, dodaj --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

-- --allMoże być potrzebne małe wyjaśnienie tej opcji: sprawia, że ​​gałąź filtra działa we wszystkich wersjach we wszystkich referencjach (która obejmuje wszystkie gałęzie). Oznacza to na przykład, że tagi są również przepisywane i są widoczne na przepisanych gałęziach.

Często stosuje się HEADzamiast tego „błąd” , co oznacza filtrowanie wszystkich poprawek tylko w bieżącej gałęzi . I wtedy w przepisanej gałęzi nie istniałyby żadne tagi (ani inne referencje).

stigkj
źródło
Wyrazy uznania za dostarczenie procedury, która zmienia zatwierdzenia dla wszystkich referencji / oddziałów.
Johnny Utahh
25

Jedno polecenie , aby zmienić autora z ostatnich N zobowiązuje:

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

UWAGI

  • te --no-editodbierają fladze, czy git commit --amendnie poprosić o dodatkowe potwierdzenie
  • kiedy używasz git rebase -i, możesz ręcznie wybrać zatwierdzenia, gdzie zmienić autora,

plik, który edytujesz, będzie wyglądał tak:

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

Następnie możesz nadal modyfikować niektóre wiersze, aby zobaczyć, gdzie chcesz zmienić autora. Daje to przyjemny pośrednik między automatyzacją a kontrolą: widzisz kroki, które zostaną wykonane, a po zapisaniu wszystko zostanie zastosowane na raz.

Chris Maes
źródło
Niesamowite! Dziękuję Ci!
Pablo Lalloni,
Użyłem HEAD ~ 8 i pokazuje on znacznie więcej niż 8 ostatnich zatwierdzeń.
Bryan Bryce
1
@BryanBryce, jeśli zaangażowane są zobowiązania do scalania, sprawy się komplikują :)
Chris Maes
@ChrisMaes Ach, widzę, co się dzieje. Nie chcę z nimi zadzierać, tylko na gałęzi, na której jestem.
Bryan Bryce
W takim przypadku, zakładając, że rozgałęziłeś się od mistrza, możesz:git rebase -i master -x ...
Chris Maes
23
  1. biegać git rebase -i <sha1 or ref of starting point>
  2. zaznacz wszystkie zatwierdzenia, które chcesz zmienić za pomocą edit(lub e)
  3. zapętlaj następujące dwa polecenia, aż przetworzysz wszystkie zatwierdzenia:

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

Pozwoli to zachować wszystkie inne informacje dotyczące zatwierdzeń (w tym daty). Ta --reuse-message=HEADopcja zapobiega uruchomieniu edytora wiadomości.

sporsh
źródło
23

Używam następujących poleceń do przepisania autora dla całego repozytorium, w tym znaczników i wszystkich gałęzi:

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

Następnie, jak opisano na stronie MAN gałęzi filter- , usuń wszystkie oryginalne referencje, których kopie zapasowe utworzono filter-branch(destrukcyjne, najpierw wykonaj kopię zapasową):

git for-each-ref --format="%(refname)" refs/original/ | \
xargs -n 1 git update-ref -d
Ton van den Heuvel
źródło
2
Jest to bardzo ważne w użyciu --tag-name-filter cat. W przeciwnym razie tagi pozostaną w oryginalnym łańcuchu zatwierdzeń. Inne odpowiedzi nie wspominają o tym.
jeberle
21

Zaadaptowałem to rozwiązanie, które działa poprzez przyjęcie prostego author-conv-file(format jest taki sam jak dla git-cvsimport ). Działa poprzez zmianę wszystkich użytkowników zgodnie z definicją wauthor-conv-file we wszystkich gałęziach.

Użyliśmy tego w połączeniu z cvs2gitdo migracji naszego repozytorium z cvs do git.

tj. próbka author-conv-file

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

Scenariusz:

 #!/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
źródło
Dzięki, zastanawiam się, dlaczego nie jest to podstawowa funkcjonalność git (lub git-svn). Można to zrobić za pomocą flagi dla klonu git svn, ale nie w gałęzi filter git ...
Daniel Hershcovich
20

Powinienem zauważyć, że jeśli jedynym problemem jest to, że autor / adres e-mail różni się od zwykłego, nie stanowi to problemu. Poprawną poprawką jest utworzenie pliku o nazwie .mailmapu podstawy katalogu z liniami podobnymi

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

I odtąd polecenia takie git shortloguznają te dwie nazwy za takie same (chyba, że ​​wyraźnie im to powiesz). Więcej informacji można znaleźć na stronie http://schacon.github.com/git/git-shortlog.html .

Ma to tę zaletę, że nie wymaga przepisywania historii, co może powodować problemy w przypadku przesyłania danych i zawsze jest dobrym sposobem na przypadkową utratę danych.

Oczywiście, jeśli popełniłeś coś jak ty i naprawdę powinien to być ktoś inny, i nie masz nic przeciwko przepisywaniu historii w tym momencie, zmiana autora zatwierdzenia jest prawdopodobnie dobrym pomysłem do celów przypisania (w takim przypadku kieruję cię do mojego inna odpowiedź tutaj).

asmeurer
źródło
18

Znalazłem prezentowane wersje jako zbyt agresywne, szczególnie jeśli popełnisz łatki od innych programistów, to w zasadzie wykradnie ich kod.

Poniższa wersja działa we wszystkich gałęziach i zmienia osobno autora i autora, aby temu zapobiec.

Wyrazy uznania dla leif81 dla wszystkich opcji.

#!/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
źródło
18
  1. Zmień zatwierdzenie author name & emailprzez Amend, a następnie zastępując 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. Inny sposób 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
źródło
2
Bardzo miła odpowiedź. Podoba mi się, że zmiany są podsumowywane od samego uaktualnienia, aż nawet do czyszczenia zatwierdzeń git
Aleks
12

Najszybszym i najłatwiejszym sposobem na to jest użycie argumentu --exec git rebase:

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

Spowoduje to utworzenie listy rzeczy do zrobienia, która wygląda następująco:

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

i to zadziała automatycznie, co działa, gdy masz setki zatwierdzeń.

Lie Ryan
źródło
9

Jeśli jesteś jedynym użytkownikiem tego repozytorium, możesz przepisać historię za pomocą git filter-branch(jak napisał svick ) lub git fast-export/ git fast-importi skryptu filtrującego (zgodnie z opisem w artykule, do którego odwołuje się odpowiedź docgnome ) lub interaktywnego rebase . Ale którykolwiek z nich zmieniłby poprawki od pierwszej zmiany zatwierdzenia; oznacza to kłopoty dla każdego, kto oparł swoje zmiany na przepisaniu twojego oddziału.

POPRAWA

Jeśli inni programiści nie oparli swojej pracy na wersji przed przepisaniem, najprostszym rozwiązaniem byłoby ponowne sklonowanie (ponowne sklonowanie).

Ewentualnie mogą spróbować git rebase --pull, który przewinąłby do przodu, gdyby nie było żadnych zmian w ich repozytorium, lub zmienił ich oddział na podstawie ponownie zapisanych zatwierdzeń (chcemy uniknąć scalania, ponieważ zachowałoby wcześniejsze zapisywanie zmian na zawsze). Wszystko to przy założeniu, że nie zobowiązali się do pracy; posługiwać sięgit stash aby w inny sposób ukryć zmiany.

Jeśli inni programiści używają gałęzi funkcji i / lub git pull --rebasenie działają, np. Ponieważ upstream nie jest skonfigurowany, muszą bazować swoją pracę na zatwierdzonych zmianach. Na przykład tuż po pobraniu nowych zmian ( git fetch), dla mastergałęzi opartej na / forked from origin/master, należy uruchomić

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

Oto origin/master@{1}stan przed przepisaniem (przed pobraniem ), zobacz gitrevisions .


Alternatywnym rozwiązaniem byłoby użycie referencji / zamiany / mechanizmu, dostępnych w Git od wersji 1.6.5. W tym rozwiązaniu zapewniasz zamienniki dla zatwierdzeń, które mają błędny adres e-mail; wtedy każdy, kto pobierze „zamień” referencje (coś w rodzajufetch = +refs/replace/*:refs/replace/* referencje refspec w odpowiednim miejscu w ich miejscu .git/config ) otrzyma zamiany w sposób przejrzysty, a ci, którzy nie zobaczą stare zatwierdzenia.

Procedura przebiega mniej więcej tak:

  1. Znajdź wszystkie zatwierdzenia z niewłaściwym adresem e-mail, na przykład używając

    $ git log [email protected] --all
    
  2. Dla każdego niepoprawnego zatwierdzenia utwórz zatwierdzenie zastępcze i dodaj je do bazy danych obiektów

    $ 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. Teraz, gdy poprawiłeś zatwierdzenie w bazie danych obiektów, musisz powiedzieć gitowi, aby automatycznie i przejrzyście zamieniał niepoprawne zatwierdzenie przez poprawienie jednego za pomocą git replacepolecenia:

    $ git replace <ID of wrong commit> <ID of corrected commit>
    
  4. Na koniec wypisz listę wszystkich zamienników, aby sprawdzić, czy procedura się powiodła

    $ git replace -l
    

    i sprawdź, czy nastąpi wymiana

    $ git log [email protected] --all
    

Możesz oczywiście zautomatyzować tę procedurę ... cóż, wszystko oprócz użycia git replace który nie ma (jeszcze) trybu wsadowego, więc musiałbyś do tego użyć pętli powłoki lub zastąpić ją „ręcznie”.

NIE TESTOWANY!YMMV.

Pamiętaj, że podczas korzystania z refs/replace/mechanizmu możesz napotkać pewne nierówne kąty : jest nowy i jeszcze nie bardzo dobrze przetestowany .

Jakub Narębski
źródło
6

Jeśli zatwierdzenia, które chcesz naprawić, są najnowsze, a tylko kilka z nich, możesz użyć kombinacji git resetigit stash aby wrócić i zatwierdzić je ponownie po skonfigurowaniu prawidłowej nazwy i adresu e-mail.

Sekwencja będzie mniej więcej taka (dla 2 błędnych zatwierdzeń, bez oczekujących zmian):

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
źródło
5

Jeśli używasz Eclipse z EGit, istnieje całkiem proste rozwiązanie.
Założenie: masz zatwierdzenia w lokalnym oddziale „local_master_user_x”, którego nie można przekazać do zdalnego oddziału „master” z powodu nieprawidłowego użytkownika.

  1. Kasa „głównego” zdalnego oddziału
  2. Wybierz projekty / foldery / pliki, dla których „local_master_user_x” zawiera zmiany
  3. Kliknij prawym przyciskiem myszy - Zamień na - Oddział - „local_master_user_x”
  4. Zatwierdź te zmiany ponownie, tym razem jako właściwy użytkownik i w lokalnym oddziale „master”
  5. Wciśnij do zdalnego „master”
Pafko
źródło
5

Korzystając z interaktywnej bazy danych, możesz umieścić polecenie zmiany po każdym zatwierdzeniu, które chcesz zmienić. Na przykład:

pick a07cb86 Project tile template with full details and styling
x git commit --amend --reset-author -Chead
j16r
źródło
3
Problem polega na tym, że inne metadane zatwierdzenia (np. Data i godzina) również są zmieniane. Właśnie to odkryłem na własnej skórze ;-).
halfer
5

Należy pamiętać, że sklepy git dwa różne adresy e-mail, jeden dla committer (osoba, która dopuściła się zmiany) i drugi dla autora (osoby, która napisała zmiany).

Informacje o committerze nie są wyświetlane w większości miejsc, ale można je zobaczyć za pomocą git log -1 --format=%cn,%ce(lub użyć showzamiast, logaby określić konkretne zatwierdzenie).

Chociaż zmiana autora ostatniego zatwierdzenia jest tak prosta git commit --amend --author "Author Name <[email protected]>", że nie ma jednego wiersza ani argumentu, który mógłby zrobić to samo z informacjami o komisarzu.

Rozwiązaniem jest (tymczasowo lub nie) zmiana informacji o użytkowniku, a następnie zmiana zatwierdzenia, która zaktualizuje podmiot zatwierdzający do bieżących informacji:

git config user.email [email protected] 
git commit --amend
Sir Athos
źródło
Zauważ, że stara wartość wciąż znajduje się w kilku miejscach w path\to\repo\.git. Nie jestem jeszcze pewien, co musisz zrobić, aby całkowicie to zniszczyć. Poprawki niestety (?) Nie usuwają się.
ruffin
5

Wystąpił dzisiaj problem, w wyniku którego znak UTF8 w nazwie autora powodował problemy na serwerze kompilacji, więc musieliśmy przepisać historię, aby to naprawić. Podjęte kroki to:

Krok 1: Zmień swoją nazwę użytkownika w git dla wszystkich przyszłych zatwierdzeń, zgodnie z instrukcjami tutaj: https://help.github.com/articles/setting-your-username-in-git/

Krok 2: Uruchom następujący skrypt 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="[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

Szybki przegląd: Pobierz repozytorium do pliku tymczasowego, sprawdź wszystkie zdalne gałęzie, uruchom skrypt, który przepisuje historię, wymuś nowy stan i powiedz wszystkim swoim kolegom, aby wykonali rebase, aby uzyskać zmiany.

Mieliśmy problem z uruchomieniem tego w systemie OS X, ponieważ w jakiś sposób pomieszało się ono z zakończeniami linii w komunikatach zatwierdzania, dlatego musieliśmy ponownie uruchomić go na komputerze z systemem Linux.

Miloš Ranđelović
źródło
5

Twój problem jest naprawdę powszechny. Zobacz „ Korzystanie z Mailmap do naprawy listy autorów w Git

Dla uproszczenia stworzyłem skrypt ułatwiający proces: git-changemail

Po umieszczeniu tego skryptu na swojej ścieżce możesz wydawać polecenia takie jak:

  • Zmień dopasowania autora w bieżącym oddziale

    $ git changemail -a [email protected] -n newname -m [email protected]
    
  • Zmień dopasowania autora i committera w <branch> i <branch2>. Przekaż -fdo gałęzi filter, aby umożliwić przepisywanie kopii zapasowych

    $ git changemail -b [email protected] -n newname -m [email protected] -- -f &lt;branch> &lt;branch2>
    
  • Pokaż istniejących użytkowników na repo

    $ git changemail --show-both
    

Nawiasem mówiąc, po dokonaniu zmian wyczyść kopię zapasową z gałęzi filtru za pomocą: git-backup-clean

albfan
źródło
1
po uruchomieniu polecenia pojawia się komunikat „fatal: nie można wykonać polecenia„ git-changemail ”: odmowa zezwolenia”
Govind
3

Chcę też dodać mój przykład. Chcę utworzyć funkcję bash z podanym parametrem.

działa to w wersji 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
źródło
2

Jeśli jesteś jedynym użytkownikiem tego repozytorium lub nie obchodzi Cię ewentualne uszkodzenie repozytorium dla innych użytkowników, to tak. Jeśli wypchnąłeś te zatwierdzenia i istnieją one tam, gdzie gdzie indziej mają do nich dostęp, to nie, chyba że nie przejmujesz się łamaniem repozytoriów innych ludzi. Problem polega na tym, że zmieniając te zatwierdzenia, będziesz generować nowe SHA, co spowoduje, że będą traktowane jak różne zatwierdzenia. Kiedy ktoś próbuje wciągnąć te zmienione zobowiązania, historia jest inna i kaboom.

Ta strona http://inputvalidation.blogspot.com/2008/08/how-to-change-git-commit-author.html opisuje, jak to zrobić. (Nie próbowałem tego, więc YMMV)

baudtack
źródło
Tak więc nie ma bezpiecznego sposobu na przepisanie poczty elektronicznej user.email. Bez wysadzenia wszystkich innych. Wiedziałem, że przepisywanie historii było złym pomysłem, po prostu pomyślałem, że może istnieć czysty sposób, aby zrobić to bezpiecznie. Dzięki.
manumoomoo,
@mediaslave: Wypróbuj refs/replace/mechanizm.
Jakub Narębski,
meta.stackexchange.com/a/8259/184684 - aka, sumuj linki, aby zamienić je w odpowiedzi.
ruffin