关于git:在每次提交中更改邮件地址

Change mail address in each commit

本问题已经有最佳答案,请猛点这里访问。

我使用带有错误邮件地址的源代码树进行了几次提交(未推送)。

为了纠正这个错误,我做了一些研究,找到了这个脚本来用一封好邮件编辑提交。问题是,当我第一次从Git中提取项目时,已经有超过200个用户提交了该项目。

当我使用该脚本时,它正确地恢复了我的邮件地址,但其他地址却被销毁了:

1
ex : a.my@mail.com became a.my@5030863e-2e11-0d4c-b7c1-a084646f5798

你知道我怎么处理这个问题吗?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/bin/sh

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

OLD_EMAIL="a.bbbb@5030863e-2e11-0d4c-b7c1-a084646f5798"
CORRECT_NAME="a.bbbb"
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

编辑1:

我用了其他邮件地址上的脚本

它纠正了以前的所有错误,但sourcetree告诉我master是248up/248down:

enter image description here

我怎样才能得到这种状态?

编辑2:

按照我的建议

1
git branch -f master origin/master

它修正了回购的状态(248向上/248向下消失)

但我在sourcetree中仍然有2倍的历史记录,在紫色中,我们可以看到远程repo中的最后一次提交(我修改了错误的邮件),从蓝色部分开始,在结束时有正确的历史记录,我的本地提交(使用好邮件开发分支和功能):

氧化镁


因为你只有一些承诺要做,而不是整个历史,所以我会用git rebase -i -pgit commit --amend --author"a.jard"手工做。好的。

这个答案包含了这一点,它不是公认的答案,但拥有双倍的投票权。好的。

至于您为什么得到脚本的结果,这是由于Git的性质和Rebase的工作方式。Rebase不会重写历史,它不能。Git中的提交是不可变的。提交的ID与提交本身的内容相关联,包括日期、日志消息、作者和提交者等元数据。Rebase写入新的历史记录。好的。

这个难题的另一个关键是使用提交的父级ID计算提交的ID。如果不同时更改子级,则无法更改父级。这使得git推和拉非常有效,如果我告诉你我已经提交了abc123,如果你已经提交了abc123,我们都知道我们有相同的历史。好的。

例如,假设您有一个简单的存储库,其中包含五个这样的提交。主控点和原点/主控点都在E。好的。

1
A - B - C - D - E [master] [origin/master]

B的电子邮件地址错误。A、C、D和E都很好。运行过滤器分支命令。它会看着A,看到没有变化,就让它单独存在。它将查看b,更改提交者,并以a作为父级编写新的提交。我们称它为b1。好的。

1
2
3
A - B - C - D - E [master] [origin/master]
 \
  B1

现在它看c。没有什么可以改变的,但它需要它的父级到b1。因为ID包含父ID,所以它必须进行新的提交。好的。

1
2
3
A - B - C - D - E [master] [origin/master]
 \
  B1 - C1

D和E也是一样。好的。

1
2
3
A - B - C - D - E [master] [origin/master]
 \
  B1 - C1 - D1 - E1

完成后,过滤分支将[master]移动到e1。好的。

1
2
3
A - B - C - D - E [origin/master]
 \
  B1 - C1 - D1 - E1 [master]

这就是为什么在过去改变一个承诺会导致它之后的一切发生分歧的原因。好的。

因为该脚本的作者没有指示您限制哪些修订git filter分支应该进行筛选,所以它完成了当前分支的整个历史记录。好的。

幸运的是,您可以通过将主控形状移回原点/主控形状来撤消此操作。有几种方法可以做到这一点。git branch -f master origin/master是最简单的。好的。

更新这涵盖了您的新问题,在这个问题中,您的开发分支挂起了过滤后的分支。让我们从头开始。你遇到了这样的情况…好的。

1
A - B - C - D - E [master] [origin/master]

你跑了git author-rewrite然后就这样结束了。好的。

1
2
3
A - B - C - D - E [origin/master]
 \
  B1 - C1 - D1 - E1 [master]

你脱离了大师,开始做出新的承诺。好的。

1
2
3
A - B - C - D - E [origin/master]
 \
  B1 - C1 - D1 - E1 [master] - F - G - H [devel]

你运行git branch -f master origin/master来撤销你的过滤器。Git中的分支只是指向提交的标签,因此只移动了主标签。您的devel分支仍挂起已筛选的提交。好的。

1
2
3
A - B - C - D - E [origin/master] [master]
 \
  B1 - C1 - D1 - E1 - F - G - H [devel]

现在你需要让德维尔和F,G和H离开大师。业务的第一步是将Devel转移到Master。如果我们这样做,就很难再找到f,g和h。你可以把身份证写下来,或者用标签买些保险。git tag tmp devel。好的。

1
2
3
A - B - C - D - E [origin/master] [master]
 \
  B1 - C1 - D1 - E1 - F - G - H [devel] <tmp>

现在用git branch -f devel master把devel移动到master。tmp标记使过滤后的分支可以访问。好的。

1
2
3
A - B - C - D - E [origin/master] [master] [devel]
 \
  B1 - C1 - D1 - E1 - F - G - H <tmp>

现在您可以使用git cherry-pick将每个单独的更改复制到devel。提交的内容不会更改,但父级是,因此必须复制它们。好的。

1
2
git checkout devel
git cherry-pick F^..tmp

为了解释F^..tmp部分,我们需要从h到f的所有内容。F..H说包括h的父母,但不包括f的父母,这只是h和g。因为我们想在列表中包括f,所以我们使用F^排除f的父母。好的。

你最终会这样做的。好的。

1
2
3
A - B - C - D - E [origin/master] [master] - F1 - G1 - H1 [devel]
 \
  B1 - C1 - D1 - E1 - F - G - H <tmp>

一旦你检查了它是否正常,删除带有git tag -d tmp的tmp标签。好的。

1
A - B - C - D - E [origin/master] [master] - F1 - G1 - H1 [devel]

别担心,如果您弄乱了提交,那么在它们被垃圾收集之前,它们仍然会存在数周。好的。

现在,您可以使用上面提到的REBASE技术检查devel并修复提交。你最终会明白的。好的。

1
2
3
A - B - C - D - E [origin/master] [master]
 \
  B2 - C2 - D2 - E2 - F2 - G2 - H2 [devel]

使用git branch -f master E2手动将master移动到e2。好的。

1
2
3
A - B - C - D - E [origin/master]
 \
  B2 - C2 - D2 - E2 [master] - F2 - G2 - H2 [devel]

你还是会有分歧的。推动你的改变仍然需要被强制,而其他人都必须强制拉。这一部分是不可避免的。历史被推后的变化总是一团糟。好的。

完成这一切还有很多其他的方法。使用git filter-branch的一个优点是,它可以为您移动沿途更改的所有标签和分支。对于像您这样的小改动,对于新用户,我更喜欢小步进行。更容易理解发生了什么。好的。好啊.