git:为什么替换需要合并提交?

时间:2017-07-11 14:02:18

标签: git

我们的git repo是从不同的VCS(Perforce)导入的,但标准导入无法恢复分支关系(即分支1.1是从分支1.0派生的)。为了解决这个问题,我们添加了移植:git replace --graft <commit> <parent>来记录从每个分支的创建到其父提交的缺失关系。

所以我们有一个git存储库,在.git / refs / replace中有一些替换。 (这些应该可能已经通过某种方式进行了重新定位,但现在可能为时已晚)

必须使用以下方式手动拉取这些替换:

git pull origin 'refs/replace/*:refs/replace/*'

一位同事没有意识到这一点,但却以某种方式推动了对回购的改变:

git pull origin 'refs/replace/*:refs/replace/*'

导致合并冲突,如:

Simple merge did not work, trying automatic merge.
ERROR: /some/file: Not handling case c6aa12b3f446c57921a68c5fc73dae9e086c2bdb ->  -> bb909246180daf894ccdb59cc4a4ff398ac62bad
fatal: merge program failed
Automated merge did not work.
Should not be doing an octopus.
Merge with strategy octopus failed

我不明白这里合并了什么。 如果我使用:

git pull origin 'refs/replace/*:refs/replace/*' -s ours

合并成功,我最终(提交后)使用合并消息,如:

commit 67d7f7cc40826e8a84beed3af9999d39e411e65d
Merge: 718dbe3 c9333d1 b870b22 d3f10f7 77d0835 de79e03 d7f0e7c 97ca1f8 Author: xxxxx
Date:   Tue Jul 11 11:33:55 2017 +0100
Merge commits 'refs/replace/00029d7b3e531215f6ce5afb32862b49d652e896', 'refs/replace/03d715c9890e5cec95ac62d1c9ecc54cb78b9f62', '

git声称有几个文件被更改,尽管这些文件最近没有更改。我怀疑它们是两个旧分支之间的区别。

合并/拉取后,我们在.git / refs / replace。

中进行了替换

我的同事的报告声称所推动的变化包括复制他需要的两个失踪分支关系的移植物。这些都不会出现在.git / refs / replace中。

如果它们与早先推出的那些混合在一起,还有其他一些问题:

他如何在没有首先解决合并冲突的情况下推进?

此外,如果您从存储库中克隆而未拉动替换项,则仍会拉出他添加的关系。有两个提交消息的提示消息,如

Former-commit-id: bc735afc1d8bb842733cb94767afb8b42599eb6a

但没有.git / refs / replace目录描述替换。

我的同事怎么能够将他的移植物作为永久性更改自动拉回来的时候将其移植到早期不是? 并且根本没有.git / refs / replace目录?

有人可以告诉我这里可能会发生什么吗?

我还能做些什么来使其他分支关系永久化而不重写历史记录?我的同事似乎已经这样做但我无法理解。

解决

总结答案:

在git中,推送的反面是获取而不是拉

我的同事推动没有合并问题,因为没有合并问题。这是我使用&#39; pull&#39;而不是&#39; fetch&#39;。

感谢您的帮助。

2 个答案:

答案 0 :(得分:1)

我认为99%的混淆来自于您使用pull而不是fetch复制替换代表的事实。我希望这是尝试章鱼将所有替换refs合并到你当前的分支中,这当然是荒谬的。

对提交谱系进行永久性更改的方法是进行历史记录重写,从长远来看,你应该继续这样做,而不是过分依赖于替换机制。显然,这需要团队的协调,但这是一次性成本,然后回购将从那时起更加无缝地工作。

答案 1 :(得分:1)

标题问题的答案(&#34;为什么替换需要合并提交?&#34;)是: 它不会。

以下是使用Git的重要规则:永远不要使用git pull。 : - )

(一旦你精通Git,你就可以放松这条规则,只有当你确切地知道将要发生什么时才使用git pull 。 )

在这种特殊情况下,您应该运行:

git fetch origin 'refs/replace/*:refs/replace/*'

并在此时停止。

如果您打算继续使用替换,您可能希望添加到每个遥控器的fetch =行集。 (注意,一旦你开始使用替换,你往往会陷入困境,所以这可能是一个合理的事情。另一个选项,如Mark Adelsberger's answer,是连接替换,例如,使用no-op git filter-branch。)这个&#34;添加fetch设置&#34;必须在每个git clone之后手动完成,因为普通克隆不会获取替换名称空间名称。 (这也与之前的镜像克隆问题有关:镜像克隆盲目地提取所有 refs/*引用,其中包括替换名称空间。)

描述

git pull命令是一种方便的捷径。它首先运行git fetch,这是从其他存储库获取提交和其他项目的实际操作。然后它运行另一个Git命令。

大多数案例中,在从另一个Git存储库获取项目(例如提交)之后,您必须自己采取一些操作使用这些项目。获取它们只会将它们放入您的存储库,为它们提供某种引用名称 - 通常是远程跟踪分支名称,例如refs/remotes/origin/somebranch

要采取的行动通常为git rebase,有时为git mergegit pull命令将为您运行git merge,即采取错误操作,除非您告诉它为您运行git rebase。当然,这可能仍然是错误的行为 - 在这种情况下,它

您带来的引用位于refs/replace/名称空间,而不是refs/heads/名称空间(包含分支名称)而不是refs/tags/名称空间(持有标签名称)。 refs/replace/名称空间包含替换项

与正常使用分支名称相比,替换和标记共享一个奇怪的功能:没有操作是必需的。使用分支名称,当您从Monique的存储库中获取refs/heads/master时,您将其重命名为refs/remotes/monique/master,以便您可以 master - refs/heads/中的分支 - 与 master分开,您只能将其作为远程跟踪分支。然后,您将采取一些行动 - 合并或变基 - 将她的工作纳入您的工作中。

但是,对于代码名称,您可以将Monique&#39; refs/tags/v2.3称为refs/tags/v2.3。现在,您共享代码v2.3,并且无需执行第二项操作:您的Git会在您自己的v2.3名称空间中查找代码名称refs/tags/

替换也是如此。 Git中的替换对象由具有非常特殊模式的refs/replace/名称空间名称表示:我们找到refs/replace/master之类的名称,而不是refs/replace/b06d3643105c8758ed019125a4399cb7efdcce2c或其他类似名称。该名称映射到替换对象本身。也就是说,那个大长毛的名字映射到另一个很长的丑陋的Git哈希ID,Git可以使用它来访问不同的 Git对象。

对于可记忆性,我将使用blah而不是b06d3643105c8758ed019125a4399cb7efdcce2c。因此,refs/replace/blah会映射到另一个ID,我们可能会将其称为bazinga

当您的Git即将对哈希ID为blah的内部对象执行某些操作时,它会注意到refs/replace/blah。然后,它会查找blah对象,而不是使用普通的bazinga对象,而是使用该对象。 (使用--no-replace-objects Git跳过这个&#34;检查refs/replace/&#34;步骤。)

那是替换的工作原理,因此,当你使用它们时,你应该只是获取它们并停止。

如果您查看.git/config文件,您会看到表格的某些行:

[remote "origin"]
    url = ...
    fetch = +refs/heads/*:refs/remotes/origin/*

添加以下格式的行(保持原始fetch =):

    fetch = +refs/replace/*:refs/replace/*

将使每个git fetch origin自动获取任何新的替换对象。有关此内容的更多信息,请参阅我对What is the difference between these `git fetch` syntaxes?

的回答