修复GIT存储库的历史记录

时间:2015-01-27 18:28:24

标签: git git-branch git-merge master git-revert

我的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-------

AFTER

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

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

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)的最后一次提交不会进行任何更改。它只会“切换”主人。有没有办法让这些提交消失?

2 个答案:

答案 0 :(得分:2)

这确实有意义:他所做的是违反了#34;主要发展方向是第一父母&#34;规则。

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

无论何时运行git merge(或者在这种情况下&#34;他&#34;运行它),您选择当前分支作为开发的主线,以及您作为备用项合并的任何内容正在合并的行。因此,如果你这样做:

$ 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默认执行的操作。反过来,这意味着&#34;主线是第一父母&#34;除非你非常严格/小心,否则很容易受到违反,并且不能依赖。


  

有没有办法让那些[merge]提交消失?

是的,但只能写一个新的提交行(&#34;重写历史记录&#34;)。

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

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

提交BD是&#34;在错误的行&#34;此时,因为合并提交M2的第一个父项是x,其第二个父项是D。同时,提交AM1的第一个父级,xM1的第二个父级。

如果你真的非常关心第一父规则,你可以在提交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'的第一个且唯一的父级是提交x,这是master在事情第一次出错时提示#34;&#34;,原样。然后B'的第一个也是唯一的父母是A&#39;,依此类推。

如果您拥有此图表,则从白板提交AM2,并master指向提交D',您将拥有该图表这样:

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

现在你可以&#34;理顺&#34;从xA'的链接,它看起来像一个很好的线性历史。

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

您可以确定B'C'D'所需的树与B所用的树相同,分别为CD。但是,您想要用于新提交A'的树可能是当前正在合并M1的树。此可能与提交A下的相同,但可能不是。这实际上取决于AM1的比较。

在没有大量手工工作的情况下,有许多相对棘手的方法来构建新提交,但它们很难在文本中描述。此外,这种&#34;历史记录重写&#34; - 当您强行使旧的master标签指向new-master提交D'时发生的部分 - 对所有开发人员施加压力,这些开发人员正在进行以M2作为父提交的提交。他们必须将这些提交复制到新的提交中,并将新的D'作为他们的父母。

由于这种痛苦是否值得,这取决于你和他们。

答案 1 :(得分:0)

git分支只是一个指向单个提交的标签。提交不知道当前指向哪个分支;它也不知道以前哪些分支指向它的历史。因此,唯一真正重要的事情(并且改变是非常重要的)是提交历史本身。

清除它的最简单方法可能如下:找到您认为代表代码库的合理状态的最新提交,并运行以下命令(假设该提交的哈希是123abc):

git checkout -B master 123abc
git push -f origin master

这将使master在本地(在运行这些命令的机器上)和服务器上指向123abc。当其他开发人员运行git fetch时,他们的origin/master将移至123abc,他们可以将其检出并使用master移动自己的git checkout -B master origin/master(我但是,我不完全确定这个命令的语法,而且我手头没有git存储库。)

警告:除非你有一个分支指向比123abc更新的提交,否则这些提交似乎会消失。如果你想稍后查看它们的内容以便清理它并重新提交它,你应该首先为那些提交创建分支,例如: git branch tempbranch 567def