我使用错误邮件地址的源树做了几次提交(未推送)。
为了纠正这个错误,我做了一些研究,发现这个脚本用好的邮件编辑提交。 问题是,当我第一次从git中提取项目时,已有来自多个用户的200多个提交。
当我使用该脚本时,它正确地还原了我的邮件地址,但其他邮件已被销毁:
ex : a.my@mail.com became a.my@5030863e-2e11-0d4c-b7c1-a084646f5798
你知道我怎么能解决这个问题吗?
#!/bin/sh
git filter-branch -f --env-filter '
OLD_EMAIL="a.bbbb@5030863e-2e11-0d4c-b7c1-a084646f5798"
CORRECT_NAME="a.bbbb"
CORRECT_EMAIL="a.bbbb@mail.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
EDIT1:
我在其他邮件地址上使用了该脚本
它纠正了以前的所有内容但是sourcetree告诉我主人是248up / 248down:
我怎么能获得这种状态?
EDIT2:
正如我所建议的
git branch -f master origin/master
它纠正了回购的状态(248up / 248down消失了)
但我仍然有2倍的sourcetree历史,紫色我们可以看到最后一次提交在遥远的回购(我修改了错误的邮件),从蓝色部分开始有正确的历史,最后我的本地提交(开发分支和具有良好邮件的功能):
答案 0 :(得分:2)
由于您只有一些提交要做,而不是整个历史记录,我会使用git rebase -i -p
和git commit --amend --author "a.jard <a.jard@mail.com>"
手动完成。
It's covered in this answer这不是公认的答案,但是票数增加了一倍。
至于为什么你会得到你的脚本所带来的结果,这是由于git的性质以及rebase的工作方式。 rebase不会重写历史,它不能。 git中的提交是不可变的。提交的ID与提交本身的内容相关联,包括日期,日志消息,作者和提交者等元数据。 rebase 写新历史。
谜题的另一个关键是提交的ID是使用其父项的ID计算的。如果不更改子项,则无法更改父项。这使得git push和pull非常有效,如果我告诉你我已经提交ABC123并且如果你有提交ABC123我们都知道我们有相同的历史。
例如,假设您有一个包含五个提交的简单存储库。 master和origin / master都指向E。
A - B - C - D - E [master] [origin/master]
B的电子邮件地址错误。 A,C,D和E都很好。您运行filter-branch命令。它会看A,看看没有变化,不管它。它将查看B,更改提交者,并编写一个以A作为父项的新提交。我们称之为B1。
A - B - C - D - E [master] [origin/master]
\
B1
现在它看着C.没有什么可以改变,但它需要它的父母B1。由于ID包含父ID,因此必须进行新的提交。
A - B - C - D - E [master] [origin/master]
\
B1 - C1
和D和E一样。
A - B - C - D - E [master] [origin/master]
\
B1 - C1 - D1 - E1
完成,filter-branch将[master]移动到E1。
A - B - C - D - E [origin/master]
\
B1 - C1 - D1 - E1 [master]
这就是为什么在过去更改一个提交会导致其后的所有内容发生分歧。
因为该脚本的作者没有指示您限制git-filter-branch应该过滤哪些修订版,所以它完成了当前分支的整个历史记录。
幸运的是,您可以通过将master移回origin / master来撤消此操作。有几种方法可以做到这一点。 git branch -f master origin/master
最简单。
UPDATE 这涵盖了您的新问题,其中您的开发分支悬挂在已过滤的分支上。让我们从头开始吧。你有这样的情况......
A - B - C - D - E [master] [origin/master]
你跑了git author-rewrite
并结束了这个。
A - B - C - D - E [origin/master]
\
B1 - C1 - D1 - E1 [master]
您分离了master并开始进行新的提交。
A - B - C - D - E [origin/master]
\
B1 - C1 - D1 - E1 [master] - F - G - H [devel]
您运行了git branch -f master origin/master
来撤消过滤器。 git中的分支只是指向提交的标签,因此只移动了主标签。你的devel分支仍在挂起过滤的提交。
A - B - C - D - E [origin/master] [master]
\
B1 - C1 - D1 - E1 - F - G - H [devel]
现在你需要让devel和F,G和H挂掉主人。第一项业务是将devel转移到掌握。如果我们这样做,将很难再找到F,G和H.你可以写下ID,或者你可以用标签取出一些保险。 git tag tmp devel
。
A - B - C - D - E [origin/master] [master]
\
B1 - C1 - D1 - E1 - F - G - H [devel] <tmp>
现在将devel移动到使用git branch -f devel master
的主人。 tmp标记可以访问过滤的分支。
A - B - C - D - E [origin/master] [master] [devel]
\
B1 - C1 - D1 - E1 - F - G - H <tmp>
现在,您可以使用git cherry-pick
将每个更改复制到devel。提交的内容不会改变,但父母是,所以必须复制。
git checkout devel
git cherry-pick F^..tmp
为了解释F^..tmp
部分,我们想要从H到F的所有内容。F..H
说包括H的父母,但排除F的父母,只有H和G由于我们希望在列表中包含F,我们使用F^
来排除F的父母。
你结束了。
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标记。
A - B - C - D - E [origin/master] [master] - F1 - G1 - H1 [devel]
不用担心,如果你搞砸了提交,那么在他们被垃圾收集之前,它们仍会存在数周。
现在您可以使用上面提到的rebase技术检查devel并修复您的提交。你最终会这样做。
A - B - C - D - E [origin/master] [master]
\
B2 - C2 - D2 - E2 - F2 - G2 - H2 [devel]
使用git branch -f master E2
手动将master移至E2。
A - B - C - D - E [origin/master]
\
B2 - C2 - D2 - E2 [master] - F2 - G2 - H2 [devel]
你仍然会分歧。仍然需要强制推动您的更改,其他所有人都必须强制推动。那部分是无法避免的。在被推动之后改变历史总是很麻烦。
还有很多其他方法可以实现这一切。使用git filter-branch
的一个优点是它会为您移动所有标记和分支。对于像你这样的小变化,对于新用户,我更喜欢小步骤。它更容易理解发生了什么。