参见英文答案 >
How to change the author and committer name and e-mail of multiple commits in Git? 32个
我使用错误的邮件地址使用源树做了几次提交(未推送).
为了纠正这个错误,我做了一些研究,发现这个脚本用一个好的邮件来编辑提交.
问题是,当我第一次从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
编辑1:
我在其他邮件地址上使用过该脚本
它纠正了以前的所有内容但是sourcetree告诉我主人是248up / 248down:
我怎么能得到这种状态?
EDIT2:
正如我所建议的那样
git branch -f master origin/master
它纠正了回购的状态(248up / 248down消失了)
但是我仍然有2倍的sourcetree历史,在紫色我们可以看到最后一次提交在遥远的回购(我修改了错误的邮件),从蓝色部分开始有正确的历史记录,最后我的本地提交(开发)分支和功能与良好的邮件):
最佳答案 由于你只有一些提交要做,而不是整个历史,我会手工使用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我们都知道我们有相同的历史.
例如,假设您有一个包含五个提交的简单存储库.主人和起源/主人都指向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是最简单的.
更新这涵盖了您的新问题,其中您的开发分支悬挂在已过滤的分支上.让我们从头开始.你有这种情况……
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>
现在使用git branch -f devel master将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到FF的所有内容.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的一个优点是它将为您移动所有标记和分支.对于像你这样的小变化,对于新用户,我更喜欢小步骤.更容易理解发生了什么.