Как изменить автора, имя коммиттера и адрес электронной почты нескольких коммитов в Git?

2394

Я писал простой сценарий на школьном компьютере и вносил изменения в Git (в репозитории, который был в моем pendrive, клонирован с моего компьютера дома). После нескольких коммитов я понял, что коммитирую как пользователь root.

Есть ли способ изменить автора этих коммитов на мое имя?

Flávio Amieiro
источник
13
Вопрос: сохраняет ли ветвь фильтра git SHA1 для предыдущих тегов, версий и объектов? Или изменение имени автора также изменит связанные SHA1?
AndyL
36
Хэши поменяются да
не доступно
3
Тангенциально я создал небольшой скрипт, который, наконец, устранил основную причину для меня. gist.github.com/tripleee/16767aa4137706fd896c
tripleee
2
@impinball Возраст вопроса вряд ли актуален. Создание нового дублирующего вопроса исключено. Я полагаю, что мог бы создать вопрос, который напрашивается именно на этот конкретный ответ, но я не совсем уверен, что он получит столько внимания. Не то, чтобы здесь не хватало вопросов Git ... Рад, что я все равно смог помочь.
трипл
8
У GitHub есть специальный скрипт для этого: help.github.com/articles/changing-author-info
Тимур Берникович

Ответы:

1214

Этот ответ использует git-filter-branch, для которого документы теперь дают это предупреждение:

В git filter-branch есть множество ловушек, которые могут привести к неочевидным искажениям запланированного переписывания истории (и могут оставить вам немного времени для исследования таких проблем, поскольку у него такая ужасная производительность). Эти проблемы безопасности и производительности не могут быть исправлены путем обратной совместимости, поэтому их использование не рекомендуется. Пожалуйста, используйте альтернативный инструмент фильтрации истории, такой как git filter-repo . Если вам все еще нужно использовать git filter-branch, пожалуйста, внимательно прочитайте БЕЗОПАСНОСТЬPERFORMANCE ), чтобы узнать о наземных минах filter-branch, и затем бдительно избегайте как можно большего числа перечисленных опасностей, насколько это возможно.

Смена автора (или коммиттера) потребует переписывания всей истории. Если вы согласны с этим и считаете, что оно того стоит, вам следует проверить git filter-branch . Страница man содержит несколько примеров, с которых можно начать. Также обратите внимание, что вы можете использовать переменные окружения, чтобы изменить имя автора, коммиттера, даты и т. Д. - см. Раздел «Переменные среды» на странице руководства git .

В частности, вы можете исправить все неправильные имена авторов и электронные адреса для всех веток и тегов с помощью этой команды (источник: GitHub help ):

#!/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
Пэт Нотц
источник
613
У Github есть публичный скрипт для этого help.github.com/articles/changing-author-info, и он отлично работает!
defvol
34
После выполнения скрипта вы можете удалить резервную ветку, выполнив «git update-ref -d refs / original / refs /head / master».
DR
7
@rodowi, он дублирует все мои коммиты.
Рафаэль Баррос
6
@RafaelBarros информация об авторе (как и все остальное в истории) является частью ключа sha коммита. Любое изменение в истории - это переписывание, приводящее к новым идентификаторам для всех коммитов. Так что не переписывайте общий репозиторий и не убедитесь, что все пользователи знают об этом ...
johannes
20
Решено с помощьюgit push --force --tags origin HEAD:master
mcont
1577

ПРИМЕЧАНИЕ. Этот ответ изменяет SHA1, поэтому позаботьтесь об его использовании в уже перемещенной ветви. Если вы хотите исправить только написание имени или обновить старую электронную почту, git позволяет вам делать это без переписывания истории с помощью .mailmap. Смотрите мой другой ответ .

Использование интерактивной ребазы

Вы могли бы сделать

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

Затем пометьте все ваши плохие коммиты как "edit" в файле rebase. Если вы также хотите изменить свой первый коммит, вы должны вручную добавить его в качестве первой строки в файле rebase (следуйте формату других строк). Затем, когда git просит вас внести изменения в каждый коммит, выполните

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

отредактируйте или просто закройте открывшийся редактор, а затем выполните

git rebase --continue

продолжить ребаз.

Вы можете вообще пропустить открытие редактора, добавив --no-edit команду так:

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

Single Commit

Как отмечают некоторые из комментаторов, если вы просто хотите изменить самый последний коммит, команда rebase не нужна. Просто делать

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

Это изменит автора на указанное имя, но для коммиттера будет настроен ваш настроенный пользователь в git config user.nameи git config user.email. Если вы хотите установить для коммиттера что-то, что вы укажете, это установит и автора, и коммиттера:

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

Примечание о слиянии коммитов

В моем первоначальном ответе был небольшой недостаток. Если есть какие-либо коммиты слияния между текущим HEADи вашим <some HEAD before all your bad commits>, тогда они git rebaseбудут сглажены (и, между прочим, если вы используете GitHub pull-запросы, в вашей истории будет тонна коммитов слияния). Это может очень часто приводить к очень разной истории (поскольку повторяющиеся изменения могут быть «перебазированы»), и в худшем случае это может привести к тому, что git rebaseвас попросят разрешить сложные конфликты слияния (которые, вероятно, уже были разрешены в коммитах слияния). Решением является использование -pфлага to git rebase, который сохранит структуру слияния вашей истории. Страница man для git rebaseпредупреждений о том, что использование -pи -iможет привести к проблемам, но вBUGS в разделе написано «Редактирование коммитов и переписывание их коммитов должны работать нормально».

Я добавил -pк вышеупомянутой команде. Для случая, когда вы просто изменяете самый последний коммит, это не проблема.

asmeurer
источник
27
Хотя
отлично подходит
32
+1 за упоминание варианта использования для типичного исправления с одной ошибкой: git commit --amend --author = username
Nathan Kidd
12
Это прекрасно, мой самый распространенный вариант использования - я сажусь за другой компьютер и забываю настроить автора и, следовательно, обычно имею <5 коммитов или около того, чтобы их исправить.
Цитракс
57
git commit --amend --reset-authorтакже работает один раз user.nameи user.emailнастроен правильно.
Очки
14
Переписать информацию об авторе для всех коммитов после <commit>использования user.nameи user.emailиз ~/.gitconfig: запустить git rebase -i <commit> --exec 'git commit --amend --reset-author --no-edit', сохранить, выйти. Не нужно редактировать!
ntc2
588

Вы также можете сделать:

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

Обратите внимание: если вы используете эту команду в командной строке Windows, вам необходимо использовать "вместо ':

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
источник
4
Разве использование env-фильтра не является более простым решением? Не уверен, почему это получает больше голосов, тогда.
Stigkj
3
Тогда ссылка не работает. Как мы перенесем эти изменения в другой репозиторий?
Рассел
28
ENV-фильтр изменит все коммиты. Такое решение позволяет условно.
user208769
5
"A previous backup already exists in refs/original/ Force overwriting the backup with -f"извините, но где -fбудет флаг, когда вы выполняете этот скрипт два раза. На самом деле это ответ Брайана, извините за беспокойство сразу после того, как ответвление фильтра является решением.
HHH
2
@ user208769 env-filter также позволяет использовать условные выражения; посмотрите на мой ответ :-)
stigkj
559

Один вкладыш, но будьте осторожны, если у вас есть многопользовательский репозиторий - это изменит все коммиты на одного и того же (нового) автора и коммиттера.

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

С переносами строк в строке (что возможно в 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
Брайан Джанфоркаро
источник
Минорным моментом является то, что экспорт на самом деле является излишним, хотя и не приносит вреда. например, git-filter-branch --env-filter "GIT_AUTHOR_NAME = 'Новое имя'; GIT_AUTHOR_EMAIL = 'New email' 'HEAD.
Алек Компьютерщик
4
Почему он переписывает все коммиты, если вы указали HEADв конце команды?
Ник Волынкин
1
Это не работает для моего репозитория bitbucket, любая идея? Я делаю git push --force --tags origin 'refs/heads/*'после рекомендованной команды
Олорин
1
Команда толчка для этого:$git push --force --tags origin 'refs/heads/master'
HARSH NILESH PATHAK
1
Аккуратный; это сохраняет старые временные метки тоже.
DharmaTurtle
221

Это происходит, когда у вас нет инициализированного $ HOME / .gitconfig. Вы можете исправить это как:

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

протестировано с версией git 1.7.5.4

lrkwz
источник
9
Это действительно хорошо работает на последнем коммите. Красиво и просто. Не должно быть глобальное изменение, использование --localработ тоже
Бен
Это был большой победитель для меня! Эта git commit --amend --reset-author --no-editкоманда особенно полезна, если вы создали коммиты с неверной информацией об авторе, а затем установите корректного автора через факт git config. Спасли мой $$ только сейчас, когда мне пришлось обновить свою электронную почту.
Ecbrodie
187

Для одного коммита:

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

(извлечено из ответа asmeurer)

blueyed
источник
14
но это только если это самый последний коммит
Ричард
4
В соответствии с этим git help commit, git commit --amendизменения фиксируются на «вершине текущей ветви» (то есть HEAD). Как правило , это самый последний коммит, но вы можете сделать это любой фиксации вы хотите, сначала проверяя , что совершаем git checkout <branch-name>или git checkout <commit-SHA>.
Рори О'Кейн
12
Но если вы сделаете это, все коммиты, которые уже имеют этот коммит в качестве родителя, будут указывать на неправильный коммит. В этот момент лучше использовать фильтр-ветку.
Джон Гитцен
3
@JohnGietzen: Вы можете перенести коммиты обратно на тот, который был изменен, чтобы это исправить. Однако, если вы делаете> 1 коммит, то, как уже упоминалось, фильтр-ветвь, вероятно, будет намного проще.
Танатос,
5
Обратите внимание, что это изменение только коммит, authorа неcommitter
Ник Волынкин
179

В случае, если только у нескольких верхних коммитов есть плохие авторы, вы можете сделать все это внутри, git rebase -iиспользуя execкоманду и --amendкоммит, следующим образом:

git rebase -i HEAD~6 # as required

который представляет вам редактируемый список коммитов:

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

Затем добавьте exec ... --author="..."строки после всех строк с плохими авторами:

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

сохранить и выйти из редактора (для запуска).

Это решение может быть длиннее, чем некоторые другие, но оно очень управляемо - я точно знаю, что оно совершает.

Спасибо @asmeurer за вдохновение.

Алекс Браун
источник
26
Определенно потрясающе. Можете ли вы сократить его, установив user.name и user.email в локальной конфигурации репо, и тогда каждая строка будет только exec git commit --amend --reset-author -C HEAD?
Андрей
1
Канонический ответ, если использовать фильтр-ветку, только что удалил для меня ссылки / заголовки / мастера. +1 к вашему контролируемому, редактируемому решению. Спасибо!
JMTD
Почему вы начинаете с Someone else's commitвместо my bad commit 1? Я просто попытался HEAD^^изменить последние 2 коммита, и это сработало идеально.
fredoverflow
3
Вместо git rebase -i HEAD^^^^^^вас также можно написатьgit rebase -i HEAD~6
Патрик Шлютер
1
Обратите внимание, что это меняет отметку времени коммитов. См. Stackoverflow.com/a/11179245/1353267 для возврата к правильным временным меткам
Самвин
111

У Github есть отличное решение : скрипт оболочки:

#!/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
Работал отлично. Просто пришлось git reset --hard HEAD^пару раз в других локальных репозиториях перенести их в более раннюю версию, git pull- исправленную версию, и здесь я без каких-либо строк, содержащих unknown <stupid-windows-user@.StupidWindowsDomain.local>(получил любовь к дефолту git).
Алан Плам
1
Я не могу толкать после этого. Должен ли я использовать "-f"?
Fish Monitor
9
Я сделал git push -f. Кроме того, местные репо должны быть перенесены после этого.
Fish Monitor
Если вам нужно запустить скрипт оболочки в определенной ветке, вы можете изменить последнюю строку на: «'master..your-branch-name" (при условии, что вы ветвились от master).
Роберт Кайч
Нажмите на ссылку <хорошее решение>, так как скрипт был обновлен
gxpr
82

Как уже упоминалось в docgnome, переписывание истории опасно и разрушит хранилища других людей.

Но если вы действительно хотите это сделать и находитесь в среде bash (без проблем в Linux, в Windows вы можете использовать git bash, который поставляется вместе с git), используйте 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'

Чтобы ускорить процесс, вы можете указать диапазон ревизий, которые вы хотите переписать:

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
источник
2
Обратите внимание, что это оставит любые теги, указывающие на старые коммиты. --tag-name-filter catэто опция "заставь это работать".
Роман Старков
@romkyns есть идеи, как изменить теги?
Ник Волынкин
@NickVolynkin Да, вы указываете --tag-name-filter cat. Это действительно должно было быть поведением по умолчанию.
Роман Старков
48

Принимая нефиксированный коммит от другого автора, есть простой способ справиться с этим.

git commit --amend --reset-author

Ryanmt
источник
1
Для одного коммита, и если вы хотите указать свое имя пользователя, это самый простой способ.
Педро Беневидес
7
Вы можете добавить это, --no-editчтобы сделать это еще проще, так как обычно большинство людей хотят обновить только адрес электронной почты, а не сообщение о
коммите
Ребята, пожалуйста, поделитесь командой git, чтобы просто обновить адрес электронной почты / имя пользователя последнего коммита
adi
Ты пробовал это? Это должно быть побочным эффектом этого, если не stackoverflow.com/a/2717477/654245 выглядит как хороший путь.
Ryanmt
47

Вы можете использовать это как псевдоним, так что вы можете сделать:

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

или за последние 10 коммитов:

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

Добавьте в ~ / .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 "

Источник: https://github.com/brauliobo/gitconfig/blob/master/configs/.gitconfig

Надеюсь, это полезно.

brauliobo
источник
"git: 'change-commits' не является командой git. Смотрите 'git --help'."
Native_Mobile_Arch_Dev
После этой команды и синхронизации с мастером все коммиты в истории дублируются! Даже других пользователей :(
Владимир
@ Владимир, что ожидается, пожалуйста, изучите об изменении истории в git
brauliobo
Мне кажется, что он запускается в / bin / sh, поэтому мне пришлось заменить специфичный для bash тест [[ ]]на sh-совместимый тест [ ](одиночные скобки). Кроме того, это работает очень хорошо, спасибо!
Штеффен Швигон
39

Это более сложная версия версии @ Brian:

Чтобы изменить автора и коммиттера, вы можете сделать это (с переносом строки в строке, что возможно в 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

Вы можете получить одну из этих ошибок:

  1. Временный каталог уже существует
  2. Ссылки, начинающиеся с refs / original, уже существуют
    (это означает, что другая ветвь фильтра была запущена ранее в хранилище, а затем ссылка на оригинальную ветку резервируется в refs / original )

Если вы хотите форсировать запуск, несмотря на эти ошибки, добавьте --forceфлаг:

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

Небольшое объяснение -- --allопции может потребоваться: она заставляет ветку фильтра работать на всех ревизиях на всех ссылках (которая включает в себя все ветки). Это означает, например, что теги также переписаны и видны в переписанных ветвях.

Распространенной «ошибкой» является использование HEADвместо этого, что означает фильтрацию всех ревизий только в текущей ветви . И тогда в переписанной ветке не будет никаких тегов (или других ссылок).

stigkj
источник
Престижность за предоставление процедуры, которая изменяет фиксации на всех ссылках / ветках.
Джонни Юта
25

Одной команды , чтобы изменить автор за последние N фиксаций:

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

НОТЫ

  • то --no-editфлаг убеждается git commit --amendне требует дополнительного подтверждения
  • когда вы используете git rebase -i, вы можете вручную выбрать коммиты, где сменить автора,

файл, который вы редактируете, будет выглядеть так:

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

Затем вы можете изменить некоторые строки, чтобы увидеть, где вы хотите изменить автора. Это дает вам хорошее промежуточное звено между автоматизацией и управлением: вы видите шаги, которые будут выполняться, и после сохранения все будет применено сразу.

Крис Мэйс
источник
Потрясающие! Спасибо!
Пабло Лаллони
Я использовал HEAD ~ 8, и он показывает больше, чем последние 8 коммитов.
Брайан Брайс
1
@BryanBryce, если есть коммиты слияния, все усложняется :)
Крис Мэйс
@ ChrisMaes Ах, я вижу, что происходит. Я не хочу связываться с ними, просто на ветке, на которой я работаю.
Брайан Брайс
В этом случае, если бы вы ответили от мастера, вы могли бы:git rebase -i master -x ...
Крис Мэйс
23
  1. запустить git rebase -i <sha1 or ref of starting point>
  2. пометить все коммиты, которые вы хотите изменить с edit(или e)
  3. выполните следующие две команды, пока не обработаете все коммиты:

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

Это сохранит всю остальную информацию о коммите (включая даты). Эта --reuse-message=HEADопция запрещает запуск редактора сообщений.

sporsh
источник
23

Я использую следующее, чтобы переписать автора для всего хранилища, включая теги и все ветви:

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

Затем, как описано на странице MAN в ответвлении фильтра , удалите все исходные ссылки, скопированные с помощью filter-branch(это деструктивно, сначала сделайте резервную копию):

git for-each-ref --format="%(refname)" refs/original/ | \
xargs -n 1 git update-ref -d
Тон ван ден Хеувел
источник
2
Это очень важно использовать --tag-name-filter cat. В противном случае ваши теги останутся в исходной цепочке коммитов. Другие ответы не упоминают об этом.
Jeberle
21

Я адаптировал это решение, которое работает путем приема простого author-conv-file(формат такой же, как и для git-cvsimport ). Он работает путем изменения всех пользователей, как определено во author-conv-fileвсех ветвях.

Мы использовали это в сочетании с cvs2gitмиграцией нашего репозитория из cvs в git.

т.е. образец author-conv-file

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

Сценарий:

 #!/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
Лейф Грюнвольдт
источник
Спасибо, мне интересно, почему это не основная функциональность git (или git-svn). Это можно сделать с помощью флага для git svn clone, но не в git filter-branch ...
Даниэль Гершович
20

Я должен отметить, что если единственная проблема заключается в том, что автор / электронная почта отличается от вашего обычного, это не проблема. Правильное исправление заключается в создании файла с именем .mailmapв основании каталога с такими строками, как

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

И с тех пор команды like git shortlogбудут считать эти два имени одинаковыми (если вы специально не скажете им не делать этого). См. Http://schacon.github.com/git/git-shortlog.html. для получения дополнительной информации.

Это дает преимущество перед всеми остальными решениями, заключающимися в том, что вам не нужно переписывать историю, что может вызвать проблемы, если у вас есть апстрим, и это всегда хороший способ случайно потерять данные.

Конечно, если вы передали что-то как себя, и это действительно должен быть кто-то другой, и вы не против переписать историю на этом этапе, изменение автора коммита, вероятно, является хорошей идеей для целей атрибуции (в этом случае я направляю вас к своему другой ответ тут).

asmeurer
источник
18

Я обнаружил, что представленные версии являются агрессивными, особенно если вы делаете патчи от других разработчиков, это по существу украдет их код.

Версия ниже работает на всех ветках и меняет автора и комитера отдельно, чтобы предотвратить это.

Престижность к leif81 для всего варианта.

#!/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
drahnr
источник
18
  1. Изменение фиксации author name & emailпутем Amend, то замена 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. Другой способ 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
Очень хороший ответ. Мне нравится, что изменения с самого обновления обернуты, чтобы даже очистить коммиты git
Aleks
12

Самый быстрый и простой способ сделать это - использовать аргумент --exec в git rebase:

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

Это создаст список задач, который выглядит следующим образом:

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

и это будет работать все автоматически, что работает, когда у вас есть сотни коммитов.

Ли Райан
источник
9

Если вы являетесь единственным пользователем этого репозитория, вы можете переписать историю, используя git filter-branch(как писал svick ), или git fast-export/ или git fast-importсценарий фильтра (как описано в статье, на который есть ссылка в ответе docgnome ), или интерактивную перебазировку . Но любой из них изменил бы ревизии с первого измененного коммита и далее; это означает проблемы для любого, кто основывал свои изменения на предварительной ветке вашей ветки.

ВОССТАНОВЛЕНИЕ

Если бы другие разработчики не основывали свою работу на версии перед перезаписью, самым простым решением было бы переклонировать (снова клонировать).

В качестве альтернативы они могут попытаться git rebase --pullвыполнить ускоренную перемотку, если в их хранилище не было никаких изменений, или перебазировать свою ветку поверх переписанных коммитов (мы хотим избежать слияния, так как это сохранит пре-переписанные комиты навсегда). Все это при условии, что они не имеют работы; используйте, git stashчтобы скрыть изменения в противном случае.

Если другие разработчики используют ветви функций и / или git pull --rebaseне работают, например, из-за того, что апстрим не настроен, они должны перебазировать свою работу поверх коммитов после перезаписи. Например, сразу после получения новых изменений ( git fetch), для masterветви, основанной на / разветвленной origin/master, нужно запустить

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

Вот состояние origin/master@{1}предварительной перезаписи (перед извлечением), см. Gitrevisions .


Альтернативным решением было бы использовать refs / replace / механизм, доступный в Git начиная с версии 1.6.5. В этом решении вы предоставляете замену для коммитов, которые имеют неправильный адрес электронной почты; тогда любой, кто выбирает «заменить» ссылки (что-то вроде fetch = +refs/replace/*:refs/replace/*refspec в соответствующем месте в их .git/config ), получит замены прозрачно, а те, кто не получит эти ссылки, увидят старые коммиты.

Процедура идет примерно так:

  1. Найти все коммиты с неправильным адресом электронной почты, например, используя

    $ git log --author=user@wrong.email --all
    
  2. Для каждого неправильного коммита создайте коммит замены и добавьте его в базу данных объектов.

    $ 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. Теперь, когда вы исправили коммит в объектной базе данных, вы должны указать git автоматически и прозрачно заменить неправильный коммит на исправленный с помощью git replaceкоманды:

    $ git replace <ID of wrong commit> <ID of corrected commit>
    
  4. Наконец, перечислите все замены, чтобы проверить, успешно ли прошла эта процедура.

    $ git replace -l
    

    и проверьте, есть ли замены

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

Конечно, вы можете автоматизировать эту процедуру ... ну, все, кроме использования, git replaceкоторое не имеет (пока) пакетного режима, так что вам придется использовать цикл оболочки для этого или заменить «вручную».

НЕ ПРОВЕРЕНО! YMMV.

Обратите внимание, что при использовании refs/replace/механизма вы можете столкнуться с некоторыми грубыми углами : он новый и еще не очень хорошо протестирован .

Якуб Наребски
источник
6

Если коммиты, которые вы хотите исправить, являются самыми последними, и только несколько из них, вы можете использовать комбинацию git resetи, git stashчтобы вернуться к фиксации их снова после настройки правильного имени и адреса электронной почты.

Последовательность будет примерно такой (для 2 неправильных коммитов, без ожидающих изменений):

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
источник
5

Если вы используете Eclipse с EGit, то есть довольно простое решение.
Предположение: у вас есть коммиты в локальной ветке 'local_master_user_x', которую нельзя отправить в удаленную ветку 'master' из-за недопустимого пользователя.

  1. Оформить заказ на удаленную ветку "Мастер"
  2. Выберите проекты / папки / файлы, для которых «local_master_user_x» содержит изменения
  3. Щелкните правой кнопкой мыши - Замените на - Ветвь - 'local_master_user_x'
  4. Подтвердите эти изменения снова, на этот раз как правильный пользователь и в локальную ветку 'master'
  5. Нажмите на удаленный «мастер»
paphko
источник
5

Используя интерактивную перебазировку, вы можете поместить команду исправления после каждого коммита, который вы хотите изменить. Например:

pick a07cb86 Project tile template with full details and styling
x git commit --amend --reset-author -Chead
j16r
источник
3
Проблема в том, что другие метаданные фиксации (например, дата и время) также изменяются. Я только что выяснил это трудным путем ;-).
Половник
5

Обратите внимание, что git хранит два разных адреса электронной почты: один для коммиттера (человека, который внес изменение), а другой для автора. (человека, который написал изменение).

Информация о коммитере не отображается в большинстве мест, но вы можете увидеть ее с помощью git log -1 --format=%cn,%ce(или использовать showвместо logуказания конкретного коммита).

Менять автора вашего последнего коммита так же просто, как git commit --amend --author "Author Name <email@example.com>" и отсутствие единой строки или аргумента, чтобы сделать то же самое с информацией о коммитере.

Решение состоит в том, чтобы (временно или нет) изменить вашу пользовательскую информацию, а затем изменить коммит, который обновит коммиттера до вашей текущей информации:

git config user.email my_other_email@example.com 
git commit --amend
Сэр афон
источник
Обратите внимание, что старое значение все еще находится в нескольких местах path\to\repo\.git. Я еще не уверен, что вам нужно сделать, чтобы полностью исключить это. К сожалению, исправления (?) Не стираются.
Рябин
5

Сегодня мы столкнулись с проблемой, когда символ UTF8 в имени автора вызывал проблемы на сервере сборки, поэтому нам пришлось переписать историю, чтобы исправить это. Предпринятые шаги:

Шаг 1: Измените свое имя пользователя в git для всех будущих коммитов, следуя инструкциям здесь: https://help.github.com/articles/setting-your-username-in-git/

Шаг 2: Запустите следующий скрипт 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

Краткий обзор: извлеките свой репозиторий во временный файл, извлеките все удаленные ветви, запустите сценарий, который перезапишет историю, сделайте принудительное выдвижение нового состояния и попросите всех своих коллег выполнить перебазирование для получения изменений.

У нас были проблемы с запуском этого на OS X, потому что он как-то испортил окончания строк в сообщениях коммита, поэтому нам пришлось потом перезапускать его на машине Linux.

Милош Ранжелович
источник
5

Ваша проблема действительно распространена. Смотрите " Использование Mailmap для исправления списка авторов в Git "

Для простоты я создал скрипт для облегчения процесса: git-changemail

Поместив этот скрипт в свой путь, вы можете выполнить такие команды:

  • Изменить совпадения авторов в текущей ветке

    $ git changemail -a old@email.com -n newname -m new@email.com
    
  • Измените совпадения авторов и коммиттеров для <branch> и <branch2>. Перейдите -fв filter-branch, чтобы разрешить перезапись резервных копий

    $ git changemail -b old@email.com -n newname -m new@email.com -- -f &lt;branch> &lt;branch2>
    
  • Показать существующих пользователей в репо

    $ git changemail --show-both
    

Кстати, после внесения изменений очистите резервную копию из ветви фильтра с помощью: git-backup-clean

albfan
источник
1
когда я запускаю вашу команду, она говорит: «Неустранимый: невозможно выполнить git-changemail: разрешение запрещено»
Govind
4

Попробуйте это. Это будет сделано так же, как указано выше, но в интерактивном режиме.

bash <(curl -s  https://raw.githubusercontent.com/majdarbash/git-author-change-script/master/run.sh)

Ссылка: https://github.com/majdarbash/git-author-change-script

Маджд Арбаш
источник
3

Я тоже хочу добавить свой пример. Я хочу создать bash_function с заданным параметром.

это работает в 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;
}
stephanfriedrich
источник
2

Если вы являетесь единственным пользователем этого репо или вас не волнует возможное нарушение репо для других пользователей, тогда да. Если вы выдвинули эти коммиты, и они существуют там, где кто-то другой может получить к ним доступ, то нет, если вы не заботитесь о нарушении репо других людей. Проблема заключается в том, что, изменяя эти коммиты, вы будете генерировать новые SHA, в результате чего они будут рассматриваться как разные коммиты. Когда кто-то еще пытается втянуть эти измененные коммиты, история становится другой и kaboom.

На этой странице http://inputvalidation.blogspot.com/2008/08/how-to-change-git-commit-author.html описано, как это сделать. (Я не пробовал это так, YMMV)

baudtack
источник
Таким образом, не существует безопасного способа переписать user.email. Не взрывая всех остальных. Я знал, что переписывать историю было плохой идеей, я просто думал, что может быть чистый способ сделать это безопасно. Спасибо.
manumoomoo
@mediaslave: попробуй refs/replace/механизм.
Якуб Наребски
meta.stackexchange.com/a/8259/184684 - иначе, суммируйте ссылки, чтобы превратить их в ответы.
Ерф