在每次提交中更改邮件地址 [重复]

Posted

技术标签:

【中文标题】在每次提交中更改邮件地址 [重复]【英文标题】:Change mail address in each commit [duplicate] 【发布时间】:2015-02-25 21:07:53 【问题描述】:

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

为了纠正这个错误,我做了一些研究,发现这个脚本可以用一个好的邮件来编辑提交。 问题是,当我第一次从 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 告诉我 master 是 248up/248down :

我怎样才能摆脱这种状态?

EDIT2:

按照建议使用

git branch -f master origin/master

它更正了 repo 的状态(248up/248down 消失了)

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

【问题讨论】:

上面的脚本改变了每次提交的作者信息?您可以使用修订选择器限制filter-branch 操作的引用。也许你应该这样做? @Schwern 是的,我在这篇文章中找到了这个脚本,但是你可以看到它引发了一个问题。我的问题是从现在开始我能做什么(现在只有我的电子邮件地址是好的) @EtanReisner 是的,我不明白为什么。我不太擅长 sbash ^^ 所有之前的提交还是你之前的所有提交?前者没有意义。后者确实如此(并且应该可以通过在 filter-branch 调用上更具体地指定要重写的修订来修复)。 @EtanReisner 是的,好的解决方案是只编辑我的提交。但正如我所说,我不擅长使用 bash(并且使用 git,我是初学者)。现在我必须通过邮件地址重新编辑提交以恢复好的提交。但我担心脚本会再次破坏提交。有什么想法吗? 【参考方案1】:

由于您只有几个提交要做,而不是整个历史,我会使用 git rebase -i -pgit 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 最简单。

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

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 挂在 master 上。首要任务是将开发转移到掌握。如果我们这样做,将很难再次找到 F、G 和 H。你可以只写下身份证,或者你可以用标签购买一些保险。 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 将开发者转移到大师。 tmp 标签使过滤后的分支保持可访问性。

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

现在您可以使用git cherry-pick 将每个单独的更改复制到开发。提交的内容不会改变,但父母会改变,所以必须复制它们。

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 技术检查并修复您的提交。你会得到这个。

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 的优点之一是它将为您移动沿途更改的所有标签和分支。对于像你这样的小改动,对于新用户,我更喜欢分小步进行。更容易理解发生了什么。

【讨论】:

如果我使用最后一个命令。我的本地仓库中不会有很大一部分垃圾历史吗? (你的解释很清楚,谢谢) @JulienM。是的。这不是问题。 Git 的存储效率极高。 Git 会在需要时垃圾收集任何未引用的提交。这是好事。您可以使用 git reflog 查看 HEAD 先前指向的提交,并在错误后使用它来恢复旧提交。 感谢您的精彩回答,git 非常强大,但一开始确实很复杂。只是为了确保问题的某些部分是好的:1-我的原始/主服务器下的历史没有好的邮件,开发有 2-现在我只从服务器中拉出项目,我没有推送我的功能 3- 我至少有 5 封邮件要更改,所以我运行了 5 次脚本;/ @JulienM。 Git 界面复杂而难。 Git 内部结构很简单(从几部分的意义上说)。 1和2是正确的。至于 3...您运行了 5 次哪个脚本以及使用什么参数? 我在我的帖子中多次运行脚本 qith 不同的邮件地址(你的回答非常好,所以我会接受它:)

以上是关于在每次提交中更改邮件地址 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

更改以前提交的 git 电子邮件

更改所有提交的提交元数据[重复]

Kotlin / Android Studio - 当电子邮件地址格式错误时,应用程序在注册时崩溃[重复]

如何从电子邮件地址 ArrayList 中删除重复的域电子邮件地址

在 PHP 中验证电子邮件地址 [重复]

在 Magento1.8 中更改现有客户电子邮件地址