修复GIT存储库的历史记录

我的GIT存储库中存在以下情况.在做出改变之前,有人忘记了拉大师,然后他的当地大师承诺.在那之后,由于某种原因,他将原点/主人合并到他的本地主人,然后推了它.结果是原点/主人有点“切换位置”与他当地的主人.我有意义吗?

这是一个例子:

在推前

x----x-----x----x----x----x----x-----x----x (MASTER)

推后

 ---------------------------------------------x---x (MASTER)
|                                                 |
x----x-----x----x----x----x----x-----x----x-------

这有点搞乱了存储库,因为现在所有的历史似乎都在分支上.

在那之后,有一些新的提交被推送到新的主人,然后由于一个现在不重要的原因,我们决定我们不想要那些,所以我们设法放弃了我们不想要的提交,并且同时将de MASTER恢复到原来的位置.像这样:

之前

 ---------------------------------------------x---x---x---x---x (MASTER)
|                                                 |
x----x-----x----x----x----x----x-----x----x-------

                                             (2)
 ---------------------------------------------x---x---x---x---x-- 
|                                               |                |
x----x-----x----x----x----x----x-----x----x-----x----------------x (MASTER)
                                         (1)                    (3)

正如你所看到的那样,现在那个忘记了tu pull的人所提交的提交已被合并到最初的主人那里.这是通过以下方式实现的:

git checkout <HASH OF COMMIT MARKED AS (1) >
git checkout -b refactor_master
git merge --no-ff <HASH OF COMMIT MARKED AS (2) >
git push origin refactor_master
git merge --strategy=ours mastergit checkout master
git merge refactor_master
git push origin master

这有效地使那些承诺所包含的变化从主人那里消失了,并且使主人变成了过去的样子.但是,我现在有一个不应该存在的“分支”.实际上,标记为(3)的最后一次提交不会进行任何更改.它只会“切换”主人.有没有办法让这些提交消失?

最佳答案 这是有道理的:他所做的是违反“主线发展是第一父母”的规则.

请注意,git本身没有任何内容可以强制执行此规则.这是不可能的,原因很简单:谁定义哪条线是“主线”?这个问题唯一可能的答案是“你”,其中“你”的意思是“运行git来操纵提交图的人”.所以这不是一个真正的git规则,它是一个“使用git”规则的人.

每当你运行git merge时(或者在这种情况下“he”运行它),你选择当前的分支作为开发的主线,以及你正在合并的任何合并的替代线.因此,如果你这样做:

$git checkout master
$make-some-change; git add ...; git commit -m message

$git fetch origin # and let's assume this brings in a new commit
$git merge origin/master

你告诉git将你的主人作为主线,并将上游变化合并为分支线.

请注意,最后两个命令-git fetch后跟git merge-是git pull默认执行的操作.反过来,这意味着“主线是第一父母”经常被违反,除非你非常严格/小心,否则不能依赖它.

Is there any way to make those [merge] commits disappear?

是的,但只能写一个新的提交行(“重写历史”).

让我把你的最终图表(不用担心你是如何到达那里)并对绘图进行一些小的改动以获得更紧凑的表示:

  ------------------------A---M1--B--C--D
 /                           /           \
o--o--o--o--o--o--o--o--o---x-------------M2   <-- master

提交B到D此时“在错误的行上”,因为合并提交M2的第一个父项是x,其第二个父项是D.同时提交A是M1的第一个父项,x是M1的第二个父项.

如果你真的非常关心第一父规则,你可以使用提交x来提交一个新的提交行:

  ------------------------A---M1--B--C--D
 /                           /           \
o--o--o--o--o--o--o--o--o---x-------------M2   <-- master
                             \
                              A'--B'--C'--D'   <-- new-master

这里A’的第一个也是唯一的一个父是commit x,这是当事情第一次“出错”时的主要提示,就像它一样.然后B’的第一个也是唯一的父母是A’,依此类推.

如果你有这个图表,你从白板上删除提交A到M2并让主要点提交D’,你将拥有:

o--o--o--o--o--o--o--o--o---x
                             \
                              A'--B'--C'--D'   <-- master

现在你可以“理顺”从x到A’的链接,它看起来像一个很好的线性历史.

这是一个棘手的部分:这只是你想要的图形.对于图中的每个提交,git都会保留一个树:当你git checkout提交时,要放在工作目录中的一组文件.每个提交A’到D’所需的树可能与A到D上的原始树不完全相同.

可以肯定的是,B’,C’和D’所需的树将分别与B,C和D相同.但是,您想要用于新提交A’的树可能是当前在合并M1下的树.这可能与提交A下的那个相同,但可能不是.这实际上取决于A与M1的比较.

在没有大量手工工作的情况下,有许多相对棘手的方法来构建新提交,但它们很难在文本中描述.此外,这种“历史重写” – 当您强行使旧的主标签指向新主人的提交D’时发生的部分 – 对所有开发人员施加了痛苦,他们正在进行将M2作为其父提交的提交.他们必须将这些提交复制到新提交的新D’作为他们的父母.

这取决于你是否值得,这取决于你和他们.

点赞