从中央仓库拉取时出现git-subtree冲突

15

我有几个项目依赖于同一个库,我想为每个项目维护一个单独的git仓库,并使用git-subtree管理。因此,例如,在每个项目中,我可以执行以下操作:

project1$  git subtree add --prefix=lib1 /path/to/lib1.git master
project2$  git subtree add --prefix=lib1 /path/to/lib1.git master

现在我正在项目1上工作,在这个过程中,我对lib1进行了一些更改,比如说lib1/file1.c,并将其推送回中央仓库:

project1$  git add lib1/file1.c
project1$  git commit -m "updates to lib1"
project1$  git subtree push --prefix=lib1 /path/to/lib1.git master

目前为止,一切都很好。但现在我想更新project2中lib1的副本。所以我尝试:

project2$  git subtree pull --prefix=lib1 /path/to/lib1.git master
Auto-merging lib1/file1.c
CONFLICT (content): Merge conflict in lib1/file1.c
Automatic merge failed; fix conflicts and then commit the result.

发生了什么?我可以肯定地说,在项目2下的任何lib1文件中都没有进行任何更改,那么为什么会出现冲突呢?

冲突是半空的,就像这个问题中报告的那样。一切都在一个系统(OS X)内进行拉取/推送,因此我知道行结尾不是问题。

毫无疑问,这对于git-subtree来说是一个常见的用例,并且有一个简单的答案,但我看不到。请帮我!

编辑:我找到了一个不太令人满意的解决方法:在将更改推送到子树后,我需要立即重新运行子树拉取:

project1$  git subtree push --prefix=lib1 /path/to/lib1.git master
project1$  git subtree pull --prefix=lib1 /path/to/lib1.git master

虽然没有进行更改,但git-subtree在合并提交时会找到一些内容。然后,在其他地方进行了一些更改之后,下一次从中央仓库pull时就不会发生冲突。但是,如果我忘记立即运行pull,那么下一次pull将会出现冲突。

因此,我的问题是,这是为什么?是git-subtree跟踪推送的方式存在错误,还是我漏掉了什么?


不过,我仍然会查看受影响行的十六进制转储。可能某个 CR 字符已经悄悄地混入其中,例如从其他来源粘贴文本时。 - chirlu
我在每个步骤检查了文件的十六进制转储,没有任何CR(0d),只有LF(0a)。所以这不是问题,尽管检查一下还是很好的。 - user2509951
当然,这可能是某种错误。毕竟,Git子树相对较新。如果您能提供可重现的测试用例,可以写信给Git邮件列表。 - chirlu
有人找到了更好的解决方法吗?还是新版本的Git已经修复了这个问题? - dreid
4个回答

8
“我通过一些试错找到了正确的方法来做这件事。”
“在执行这个命令之后:”
project1$  git subtree push --prefix=lib1 /path/to/lib1.git master

执行获取命令:
project2$  git fetch /path/to/lib1.git master

然后进行拉动:
project2$  git subtree pull --prefix=lib1 /path/to/lib1.git master

2
这必须是被接受的答案。正是我所需要的。 - hidefromkgb
谢谢!这个有效,让我走上了正确的轨道。 - Dato
根据您的工作流程或确切需求,您可以放弃推送/获取/拉取,并使用常规的合并+子树分割:git merge $(git subtree -P lib1 split) - Dato

5
很遗憾,没有好的方法可以将提交分开而不给新分支的提交分配完全不同的提交ID。这是因为它们毕竟是不同的提交:它们不包含子树中不存在的部分。这意味着当您将它们拉回时,Git 将看到它们作为全新的提交并生成冲突。
有两件事情可以做。其中另一个答案建议在推送后立即进行 git subtree pull。这将起作用,但你最终会有每个提交的两个副本,因为实际上存在两个更改集合:上游的更改(由git-subtree push/split自动生成)和您合并的项目中的更改,您正在将它们合并在一起。这种方法的一种快捷方式是split/push的--rejoin选项,它立即添加了额外的合并提交。
第二个选择是使用--squash。这也创建新的合并提交,但由于您将来自上游存储库的所有更改合并为单个提交而不是每个原始提交一个,因此在历史记录中会减少混乱。大多数人似乎更喜欢使用--squash

3

嗯,看起来这是git-subtree中的一个bug。我为了满足我的需求而对其进行了评估,但最终放弃了。基本上,git-subtree push会更改提交消息,因此提交的SHA1也会发生变化。您必须拉取以合并引入完全相同更改但仅由于更改的提交消息具有不同SHA1哈希而产生的其他提交。GIT正确处理双重合并(再次合并相同更改),因此它会悄悄记录合并。

有人必须修复它!


3
明白了,谢谢。很遗憾的是,子树无法自动处理这么简单的情况。有没有其他方法可以避免改变SHA1的推送方式,或者我们需要重新使用子模块? - user2509951
看起来还没有修复。我现在也遇到了同样的问题。 - zhekaus

1
在您的主项目中,尝试始终使用压缩命令进行拉取和推送:
步骤1:使用压缩子树拉取
 git subtree pull --prefix=mainProjectFolder/subtreeFolder http://bitbucket.org/repo.git master --squash

步骤2:使用squash进行子树推送。
 git subtree push --prefix=mainProjectFolder/subtreeFolder http://bitbucket.org/repo.git master --squash

squash标志将避免在不同的存储库中为相同提交创建新的SHA1 id。

解释: 为了克服这个问题,您可以约定在推送和拉取子树时使用squash标志。@Borg描述的关于SHA1提交id的问题是正确的,但是子树并不是真正为此而建立的。您应该避免在父项目和子树项目中都保留子树(库)存储库的提交Id。如果您确实要从父存储库向子树存储库推送更改,请遵循文档(line 58)中的以下语句: 也就是说,如果您进行的更改影响到库和主应用程序,则将其分为两部分进行提交。

还有这个视频 解释了何时不使用子树。 跳转到11:00分钟以查找如果:

如果你的代码库经常有更新,

或者你有许多依赖关系,

或者团队中的每个人都需要学习子树技术。


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