大量git历史记录重写后如何同步本地历史记录?

san*_*one 2 git git-rewrite-history

这个问题似乎很奇怪,但是重写100次以上的提交后,我在同步git历史记录时遇到了问题。

在我确实重写过的机器上,一个简单的git fetch同步了所有内容。

在另一台Mac机器上,git sync没有帮助,但是在随机删除本地.git/日志和refs文件然后发出后git pull,历史记录得到了刷新。

但是,无论我在Windows计算机上做什么,都无法刷新项目历史记录。尝试了所有:

  • git reset --hard HEADgit fetch
  • git fetch --all
  • git pull
  • 等等

每次在Windows计算机上,我都会获得与另一作者相同的提交的重复条目(更改了Author字段)。

我使用本教程重写了大量历史记录:

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
Run Code Online (Sandbox Code Playgroud)

有没有人经历过大量的git历史重写?如果是,其他团队成员刷新其git历史记录的步骤是什么?

tor*_*rek 7

理解这里问题的关键是在Git中:

  • 承诺就是历史。
  • 任何提交的“真实名称”是其哈希ID。
  • 没有承诺可以永远改变。
  • 每个提交都通过哈希ID 记住其先前的(直接祖先,也称为parent)提交。
  • 名称(包括分支名称和标记名称)主要仅存储一(1)个哈希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
Run Code Online (Sandbox Code Playgroud)

从字面上看,提交是提交。任何仍在使用旧提交的人实际上仍在使用旧提交。他们必须停止使用这些提交并开始使用新的提交。

在某些情况下,您git filter-branch最终指定的过滤器在原始提交中根本不会更改任何内容。在这种情况下,如果写入的提交filter-branch与原始提交逐位相同,则只有在那时,新提交才与旧提交实际上相同。如果我们查看相同的三提交原始存储库,但选择仅修改第二次B提交的内容或元数据的过滤器,则会得到:

A--B--C
 \
  B'-C'  <-- master
Run Code Online (Sandbox Code Playgroud)

作为最终结果。

请注意,即使C过滤未更改原始内容,也会发生这种情况。这是因为有关原始的某些内容B 更改,导致提交了新的和不同的提交B'。因此,在git filter-branch复制时C,它必须进行一项更改:副本的父项C'是新的,B'而不是原始的B

也就是说,已git filter-branch复制A到新的提交,但根本没有任何更改(甚至没有更改任何父信息),因此,新的提交原来是对original的重用A。然后将其复制B到新提交,并进行更改,因此新提交现在为B'。然后,它进行了复制C而不进行更改,将父级更改为B',并编写了新的commit C'

如果您的过滤器所做的更改只是C,该git filter-branch命令将复制A到自身,B对自身,CC',赠送:

A--B--C
    \
     C'  <-- master
Run Code Online (Sandbox Code Playgroud)

处理上游重写

通常,人们处理大量上游origin重写的最简单方法是让他们完全丢弃其现有存储库。也就是说,我们希望共享的原始提交不超过几个:在大规模重写的某个早期阶段,我们更改提交A或在其附近进行一次提交,因此每个后续提交都必须复制到一个新提交中。因此,创建克隆可能比更新现有克隆昂贵得多。这当然更容易!

严格来讲,这不是必需的。作为“下游”使用者,我们可以运行git fetch并获取具有其更新的分支名称以及更新的标记的所有新提交(此处特别小心,因为标记默认情况下不会更新)。但是由于我们有自己的分支名称,指向原始提交而不是新复制的提交,因此我们现在必须使我们的每个分支名称都引用新复制的提交,也许还复制上游拥有的所有提交没有(因此尚未复制)。

换句话说,对于每个分支,我们都可以运行:

git checkout <branch>
git reset --hard origin/<branch>
Run Code Online (Sandbox Code Playgroud)

使我们的branch名称(作为其最先提交)与该名称相同。(请记住,强制更新我们的所有名称,以匹配指向的哈希ID 。)origin/branchgit fetch origin/branchbranchorigin

这等效于删除我们的每个分支并使用git checkout它们来重新创建它们。换句话说,它不会继承我们所做的任何改写的origin人都不会复制的承诺(因为他们不能这样做是因为他们没有它们)。为了落实我们的承诺,我们必须做与上游上游基地相同的工作。内置的分叉点代码是否会为您正确地做到这一点(如果您的Git至少为2.0,那么通常会做到)确实是针对一个单独的问题(并且已经在其他地方得到了回答)。 请注意,您必须对您希望继续提交的每个分支执行此操作。