如何在Git中更改多次提交的作者和提交者名称以及电子邮件?


2390

我当时正在学校计算机上编写一个简单的脚本,然后将更改提交到Git(在笔驱动器中的一个回购中,该回购是从家里的计算机中克隆的)。经过几次提交后,我意识到我正在以root用户身份提交东西。

有什么办法可以将这些提交的作者更改为我的名字?


13
问题:使用git filter-branch是否可以为以前的标签,版本和对象保留SHA1?还是更改作者姓名会强制更改关联的SHA1?
AndyL 2010年

36
哈希将更改为“是”
不可用于2010年

3
切线地,我创建了一个小脚本,最终为我解决了根本原因。gist.github.com/tripleee/16767aa4137706fd896c
Tripleee 2014年

2
@impinball问题的年龄与年龄无关。创建新的重复问题是不可能的。我想我可以提出一个问题来求这个特定的答案,但我并不完全相信它会获得如此多的知名度。好像这里不存在Git问题的短缺……很高兴无论如何我都可以帮助。
2014年

Answers:


1210

此答案使用git-filter-branch文档现在为此警告:

git filter-branch有很多陷阱,它们可能会对预期的历史记录重写产生明显的干扰(并且由于性能如此糟糕,因此您几乎没有时间去研究此类问题)。这些安全和性能问题无法向后兼容解决,因此,建议不要使用它。请使用其他历史记录过滤工具,例如git filter-repo。如果仍然需要使用git filter-branch,请仔细阅读SAFETY(和PERFORMANCE)以了解filter-branch的地雷,然后警惕地尽可能避免此处列出的许多危害。

更改作者(或提交者)将需要重写所有历史记录。如果您对此表示满意,并认为值得这样做,则应查看git filter-branch。手册页包含一些示例,可以帮助您入门。还要注意,您可以使用环境变量来更改作者,提交者,日期等的名称。请参见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

610
Github有一个用于该帮助的公共脚本。github.com/ articles / changing-author-info,它的效果很好!
defvol 2012年

34
执行脚本后,您可以通过执行“ git update-ref -d refs / original / refs / heads / master”来删除备份分支。
DR

7
@rodowi,它复制了我的所有提交。
拉斐尔·巴罗斯

6
@RafaelBarros作者信息(就像历史上的其他信息一样)是提交的sha键的一部分。对历史记录的任何更改都会被重写,从而导致所有提交都具有新的ID。因此,请勿在共享存储库上重写或确保所有用户都知道它……
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>

然后,将所有错误提交标记为rebase文件中的“ edit”。如果还想更改您的第一次提交,则必须手动将其作为重新添加到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

单次提交

正如一些评论者所指出的那样,如果您只想更改最近的提交,则不需要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

关于合并提交的注意事项

我的原始回复有轻微缺陷。如果current HEAD和your 之间存在任何合并提交<some HEAD before all your bad commits>git rebase则将它们放平(顺便说一句,如果您使用GitHub pull request,那么您的历史记录中将有大量合并提交)。这通常会导致非常不同的历史记录(因为重复的更改可能会“重新建立”),并且在最坏的情况下,它可能会导致git rebase要求您解决困难的合并冲突(在合并提交中可能已经解决了)。解决方案是使用-p标志来git rebase保留历史记录的合并结构。的帮助手册git rebase警告说,使用-p-i可能会导致问题,但在BUGS 它说:“编辑提交并用其提交消息改写应该可以正常工作。”

我已经添加-p到上面的命令。对于仅更改最新提交的情况,这不是问题。


27
但是,对于奇数提交非常有用-如果您配对并且忘记更改作者
很有用

32
+1提到典型的一个错误修正的用例:git commit --amend --author = username
Nathan Kidd 2010年

12
这是完美的,我最常见的用例是我坐在另一台计算机上,忘记了设置作者,因此通常需要提交少于5次。
Zitrax

57
git commit --amend --reset-author也可以运行一次user.nameuser.email正确配置。
pts 2014年

14
<commit>使用user.nameand user.emailfrom 之后重写所有提交的作者信息~/.gitconfig:run git rebase -i <commit> --exec 'git commit --amend --reset-author --no-edit',save,quit。无需编辑!
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

4
使用env-filter不是更简单的解决方案吗?不知道为什么会因此获得更多选票。
stigkj 2011年

3
然后链接断开。我们如何将这些更改推送到另一个存储库?
罗素

28
env-filter将更改所有提交。该解决方案允许有条件的。
user208769'4

5
"A previous backup already exists in refs/original/ Force overwriting the backup with -f"抱歉,-f两次执行此脚本时-flag将在哪里。实际上,这就是Brian的回答,对于滤波器分支之后的干扰,我们深表歉意。
hhh 2012年

2
@ user208769 env-filter也允许有条件的;看看我的回答:-)
stigkj

558

一个划线员,但是如果您有多用户存储库,请小心-这将更改所有提交以具有相同的(新的)作者和提交者。

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 ='新电子邮件'” HEAD。
极客亚历克(Alec),2010年

4
如果HEAD在命令末尾指定,为什么它会重写所有提交?
Nick Volynkin 2015年

1
这对我的bitbucket存储库不起作用吗?我git push --force --tags origin 'refs/heads/*'按照建议的命令执行了操作
Olorin '16

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测试


9
在最后一次提交时,效果很好。漂亮又简单。不具备成为一个全球性的变化,使用--local的作品太

这是我最大的赢家!git commit --amend --reset-author --no-edit如果您使用错误的作者信息创建提交,然后通过设置事后正确的作者,则该命令特别有用git config。刚才我必须更新电子邮件时,保存了我的a $$。
ecbrodie

186

对于一次提交:

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

(摘自asmeurer的回答)


14
但这仅是最近一次提交
理查德(Richard)

4
根据git help commitgit commit --amend在“当前分支的尖端”(即HEAD)处更改提交。通常,这是最新的提交,但是您可以通过使用或首先检出该提交来使其成为所需的任何提交。git checkout <branch-name>git checkout <commit-SHA>
罗里·奥肯

12
但是,如果您这样做,那么所有已经具有该提交作为父项的提交都将指向错误的提交。最好在那时使用过滤器分支。
John Gietzen

3
@JohnGietzen:您可以将提交重新设置为已更改的提交以修复该提交。但是,如果您要进行> 1次提交,则如上所述,filter-branch可能会容易得多。
Thanatos

5
请注意,此更改只会提交,author而不是committer
Nick Volynkin

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吗?
Andrew

1
可以使用过滤器分支的规范答案,只是为我删除了refs / heads / master。因此,+ 1为您的可控,可编辑解决方案。谢谢!
2014年

为什么从开始Someone else's commit而不是my bad commit 1?我只是HEAD^^想修改最后2次提交,所以效果很好。
fredoverflow

3
代替git rebase -i HEAD^^^^^^您也可以写git rebase -i HEAD~6
PatrickSchlüter

1
请注意,这会更改提交的时间戳。有关恢复到正确时间戳的信息,请参见stackoverflow.com/a/11179245/1353267
Samveen

111

Github有一个很好的解决方案,它是以下shell脚本:

#!/bin/sh

git filter-branch --env-filter '

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

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

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

5
工作完美。只需git reset --hard HEAD^在其他本地存储库中进行几次操作,即可将它们升级到较早的版本,即git pull-ed的修订版,这里我没有任何内容unknown <stupid-windows-user@.StupidWindowsDomain.local>(喜欢git的默认设置)。
艾伦·梅尔

1
此后我再也推不动了。我必须使用“ -f”吗?
Fish Monitor

9
我做到了git push -f。此外,此后必须重新克隆本地存储库。
Fish Monitor

如果需要在特定分支上运行shell脚本,则可以将最后一行更改为:“'master..your-branch-name”(假设您是master的分支)。
罗伯特·凯奇

单击链接<nice solution>,因为脚本已更新
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

2
请注意,这将保留所有指向旧提交的标记。--tag-name-filter cat是“使其正常运行”选项。
罗曼·斯塔科夫

@romkyns关于如何更改标签的任何想法吗?
Nick Volynkin

@NickVolynkin是的,您指定--tag-name-filter cat。这确实应该是默认行为。
罗曼·斯塔科夫

48

当接管另一位作者的未合并提交时,有一种简单的方法来处理此问题。

git commit --amend --reset-author


1
对于一次提交,如果您想输入用户名,这是最简单的方法。
Pedro Benevides

7
您可以添加它--no-edit,使其变得更加容易,因为通常大多数人只希望更新电子邮件地址而不是提交消息
PlagueHammer

你们能分享git命令,用新的命令更新上一次提交的电子邮件/用户名吗
-adi

你有尝试过吗?如果不是,那应该是它的副作用,stackoverflow.com/ a/ 2717477/654245看起来是个不错的路径。
Ryanmt '16

46

您可以将其用作别名,以便执行以下操作:

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

希望它是有用的。


“ git:'change-commits'不是git命令。请参阅'git --help'。”
Native_Mobile_Arch_Dev

执行此命令并与master同步后,历史记录中的所有提交都将重复!甚至其他用户:(
弗拉基米尔

@弗拉基米尔(Vladimir),请期待有关git历史的更改
brauliobo

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已经存在
    (这意味着先前已在存储库上运行了另一个筛选器分支,然后将原始分支引用备份到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可能需要对该选项进行一些解释:它使筛选器分支在所有ref(包括所有分支)的所有修订版上起作用。例如,这意味着标签也被重写并且在重写的分支上可见。

通常使用“错误” HEAD代替,这意味着仅过滤当前分支上的所有修订。然后在重写的分支中将不存在任何标签(或其他引用)。


提供更改过程的荣誉在所有引用/分支上提交。
约翰尼·犹他

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

然后,您仍然可以修改一些行,以查看要在哪里更改作者。这为您提供了介于自动化和控制之间的良好中间立场:您将看到将要运行的步骤,一旦保存,所有内容将立即应用。


太棒了!谢谢!
巴勃罗Lalloni

我使用HEAD〜8,它比最后8次提交显示的方式更多。
布赖恩·布莱斯

1
@BryanBryce如果涉及合并提交,事情就会变得复杂:)
Chris Maes

@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选项可阻止启动消息编辑器。


23

我使用以下内容重写整个存储库的作者,包括标记和所有分支:

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

然后,如filter-branchMAN页中所述,删除备份的所有原始引用filter-branch(这是破坏性的,请先备份):

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

2
使用非常重要--tag-name-filter cat。否则,您的标签将保留在原始提交链中。其他答案没有提及这一点。
jeberle 2014年

21

我通过引入一个简单的方法(该格式与git-cvsimport的格式相同)调整了该解决方案。它通过更改所有分支中定义的所有用户来工作。author-conv-fileauthor-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中完成……
Daniel Hershcovich 2012年

20

我应该指出,如果唯一的问题是作者/电子邮件与您的平常情况不同,那么这不是问题。正确的解决方法是.mailmap在目录的底部创建一个名为的文件

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

从那时起,诸如此类的命令git shortlog将认为这两个名称是相同的(除非您明确告诉他们不要这样做)。有关更多信息,请参见http://schacon.github.com/git/git-shortlog.html

这具有此处所有其他解决方案的优点,因为您不必重写历史记录,如果您拥有上游记录,则可能会引起问题,并且始终是意外丢失数据的好方法。

当然,如果您自己做某件事,并且确实应该是其他人,并且此时您不介意重写历史记录,那么出于作者目的,更改提交作者可能是一个好主意(在这种情况下,我会将您定向到我的其他答案在这里)。


18

我发现所提供的版本具有攻击性,特别是如果您从其他开发人员提交补丁程序时,这实际上会窃取其代码。

下面的版本在所有分支上均有效,并分别更改了作者和提交者以防止这种情况。

感谢leif81的all选项。

#!/bin/bash

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

18
  1. 更改承诺author name & emailAmend,然后替换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 commits
Aleks

12

最快,最简单的方法是使用git rebase的--exec参数:

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

如果您是该存储库的唯一用户,则可以使用(如svick所写的)或/ 和过滤器脚本(如docgnome答案中引用的文章中所述)或交互式rebase 重写历史记录。但是,其中任何一个都会从第一次更改的提交开始就更改修订;这对于任何将您的更改基于分支预重写的人来说都是麻烦。git filter-branchgit fast-exportgit fast-import

复苏

如果其他开发人员未将其工作基于预重写版本,则最简单的解决方案是重新克隆(再次克隆)。

或者,他们可以尝试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 /机制,该机制自1.6.5版起在Git中可用。在此解决方案中,您将替换电子邮件错误的提交;那么任何人谁取“取代”裁判(类似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(还没有批处理模式)外,其他所有操作都必须使用shell循环或手动替换。

未经测试!YMMV。

请注意,使用refs/replace/机制时可能会遇到一些困难:它是新的,尚未经过很好的测试


6

如果您要修复的提交是最新的,并且只是其中的几个,则可以在配置正确的名称和电子邮件后使用的组合git resetgit 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

5

如果您将Eclipse与EGit结合使用,那么有一个非常简单的解决方案。
假设:您在本地分支“ local_master_user_x”中提交,由于用户无效,该分支无法推送到远程分支“ master”。

  1. 检出远程分支“ master”
  2. 选择“ local_master_user_x”包含更改的项目/文件夹/文件
  3. 右键单击-替换为-分支-'local_master_user_x'
  4. 再次提交这些更改,这次以正确的用户身份并进入本地分支“ master”
  5. 推送到远程“主”

5

使用交互式变基,可以在每个要更改的提交之后放置一个amend命令。例如:

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

3
问题在于其他提交元数据(例如日期和时间)也被修改。我只是觉得很难;-)
2013年

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。我不确定您要完全清除它需要做什么。不幸的是,修改(?)似乎没有删除。
鲁芬2014年

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到筛选器分支以允许重写备份

    $ git changemail -b old@email.com -n newname -m new@email.com -- -f &lt;branch> &lt;branch2>
    
  • 显示回购中的现有用户

    $ git changemail --show-both
    

顺便说一句,进行更改后,请使用以下命令从筛选器分支中清除备份:git-backup-clean


1
当我运行您的命令时,它说“严重:无法执行'git-changemail':权限被拒绝”
Govind 2015年


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;
}

2

如果您是该存储库的唯一用户,或者您不关心是否有可能破坏其他用户的存储库,那么可以。如果您已推送这些提交,并且它们存在于其他地方可以访问它们的位置,则不会,除非您不在乎破坏其他人的回购协议。问题是通过更改这些提交,您将生成新的SHA,这将导致将它们视为不同的提交。当其他人尝试引入这些更改的提交时,历史和kaboom都不一样。

此页面http://inputvalidation.blogspot.com/2008/08/how-to-change-git-commit-author.html描述了如何执行此操作。(我还没尝试过,所以YMMV)


因此,没有安全的方法来重写user.email。没有炸毁其他所有人。我知道重写历史记录不是一个好主意,我只是想可能会有一种安全的干净方法来重写历史记录。谢谢。
manumoomoo

@mediaslave:尝试refs/replace/机制。
JakubNarębski10年

meta.stackexchange.com/a/8259/184684-又名,汇总链接以使其成为答案。
鲁芬2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.