如何在Git历史大规模重写后同步本地历史记录?

5
这个问题可能看起来很奇怪,但是我在重写了100多个提交记录之后同步git历史方面遇到了问题。
在我进行重写的机器上,简单的 git fetch 就能同步所有内容。
在另一台mac机器上, git sync 没有帮助,但是在随机删除本地的 .git / 日志和引用文件后,并发出 git pull ,历史得到了刷新。
然而,在Windows机器上无论我做什么,都无法刷新项目历史记录。 尝试了以下所有方法:
  • git reset --hard HEAD git fetch
  • git fetch --all
  • git pull
  • 等等
每次在Windows机器上,我都会得到相同提交的作者不同的重复条目(我更改了作者字段)。
我按照这个教程进行了大量的历史重写: https://help.github.com/articles/changing-author-info/
Open Terminal.

Create a fresh, bare clone of your repository:

git clone --bare https://github.com/user/repo.git
cd repo.git
Copy and paste the script, replacing the following variables based on the information you gathered:

OLD_EMAIL
CORRECT_NAME
CORRECT_EMAIL

#!/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
view rawgit-author-rewrite.sh hosted with ❤ by GitHub
Press Enter to run the script.
Review the new Git history for errors.
Push the corrected history to GitHub:

git push --force --tags origin 'refs/heads/*'
Clean up the temporary clone:

cd ..
rm -rf repo.git

有没有人有大规模的Git历史重写的经验?如果有,其他团队成员需要采取哪些步骤来刷新他们的Git历史记录?


git reset --hard HEAD 只会从您当前的提交中刷新文件,如果这是重写期间丢失的提交之一,则无法帮助。相反,请执行 git reset --hard origin/branchname,将其替换为您所在的分支名称(如果需要,还要替换为远程名称)。此外,请确保您有本地存储库的备份,如果您丢失文件,我不负责任。 - Lasse V. Karlsen
@LasseVågsætherKarlsen 我需要逐个分支重置(或通过循环脚本)吗?没有像 fetch 这样的 Git 命令可以刷新所有分支的完整历史记录吗? - sandalone
请参考此问题:https://dev59.com/jHI-5IYBdhLWcg3w99kH - Lasse V. Karlsen
2个回答

11
理解这里的问题的关键(或关键)在于,在Git中:
  • 提交是历史记录。
  • 任何提交的“真实名称”都是其哈希ID。
  • 没有任何提交可以被更改。
  • 每个提交都通过哈希ID记住其前一个(直接祖先,也称为父项)提交。
  • 名称(包括分支和标签名称)主要只存储一个(1)哈希ID。
  • 分支名称的特殊属性是它会更改存储的哈希ID,随着分支的增长,通常以“良好”的方式进行,以便无论今天分支名称指向哪个提交(通过哈希ID),该提交(通过哈希ID)最终都会返回到昨天标识的提交(通过哈希ID)。

当你“重写历史记录”时,你不会 - 你不能 - 更改任何现有的提交。相反,你复制每个现有的提交。 git filter-branch所做的就是按照“最古老的”(最祖先的)到“最新的”(最少祖先/顶部)的顺序复制您请求的所有提交,并在执行过程中应用筛选器:

  • 提取原始提交;
  • 应用筛选器;
  • 从结果中创建新的提交,并根据任何之前的副本来更改父哈希ID。
最终,对于大规模重写来说,这意味着你实际上有两个不同的仓库并排放置:旧的那个,带有它的旧提交,和新的那个,带有它的新提交。在过滤过程结束时,git filter-branch 会更改名称以指向新的副本。
如果你有一个只有三个提交的小仓库——我们称之为提交 A 到 C——和一个 master 分支,并且这三个提交都需要进行一些更改,你将会得到这样的结果:
A--B--C   [was the original master]

A'-B'-C'  <-- master

新提交是真正的“新”提交。仍在使用旧提交的人实际上仍在使用旧提交。他们必须停止使用那些提交,并开始使用新提交。
在某些情况下,您使用git filter-branch指定的过滤器在原始提交中可能不会更改任何内容。 在这种情况下 - 如果filter-branch编写的新提交与原始提交逐位相同 - 那么只有在新提交实际上与旧提交相同时,才是相同的。 如果我们查看相同的三个提交原始存储库,但选择修改仅第二个B提交的内容或元数据的过滤器,则会得到:
A--B--C
 \
  B'-C'  <-- master

作为最终结果。

请注意,即使筛选没有改变原始的C,这也会发生。这是因为原始的B发生了一些变化,导致新的不同的提交B'。因此,当git filter-branch复制C时,它必须进行一个更改:副本C'的父项是新的B'而不是原始的B

也就是说,git filter-branchA复制到一个新的提交中,但没有进行任何更改(甚至没有更改任何父项信息),因此新的提交结果是重用原始的A。然后它将B复制到一个新的提交中,并进行了更改,所以新的提交现在是B'。然后它复制C而不进行更改,将父项更改为B',并写入新的提交C'

如果您的筛选器仅更改了C,则git filter-branch命令将把A复制到自身,将B复制到自身,并将C复制到C',结果如下:

A--B--C
    \
     C'  <-- master

处理上游重写

一般来说,处理大规模的上游origin重写最简单的方法是完全放弃现有的存储库。也就是说,在重写的早期阶段,我们更改提交A或附近的一个提交,以便每个后续提交都必须复制到一个新的提交。因此,创建一个新的克隆可能与更新现有克隆一样甚至更加省时。这当然也更容易!

严格来说,这并非必要。作为“下游”用户,我们可以运行git fetch并获取所有包含更新分支名称和更新标签(在此要特别小心,因为默认情况下标签不会更新)的新提交。但由于我们拥有自己的分支名称,指向原始提交而不是新复制的提交,因此现在我们必须使每个我们的分支名称都指向新复制的提交,还需要复制任何我们拥有但上游没有的提交(因此上游没有复制)。

换句话说,我们可以为我们的每个分支运行:

git checkout <branch>
git reset --hard origin/<branch>

为了使我们的分支名称与其顶端提交相同,需要将其命名为origin/branch所指向的哈希ID。记住,git fetch会强制更新我们所有的origin/branch名称以匹配originbranch指向的哈希ID。这相当于删除我们的每个分支并使用git checkout重新创建它们。换句话说,它不会继承任何我们的提交,因为重写origin的人没有复制它们(因为他们没有这些提交)。要继承我们的提交,我们必须做与处理上游变基相同的事情。内置的fork-point代码是否能正确地执行此操作取决于你的Git版本是否至少为2.0,这是一个单独的问题(已在其他地方回答过)。请注意,您需要对每个包含您想要继承的提交的分支执行此操作。

我以为我对Git有所了解。感谢您通过ID和父级内容提升了我的Git知识。点赞! - sandalone
对于最后一句感到遗憾。我希望有一个像fetch这样的Git命令,可以一次重置所有分支。 - sandalone

0
在第二台机器上,首先运行git fetch,而不是git pull。然后对于每个历史记录被重写的分支,您需要执行git reset --hard HEAD。请注意,此命令仅适用于当前分支。因此,如果有多个分支受到历史记录重写的影响,您需要检出和重置每个分支。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接