我一直在使用git-subtree扩展(https://github.com/apenwarr/git-subtree)来管理我们主项目中的子项目.当我尝试从主项目中拆分对子项目所做的更改时,它正在完成我想要的事情.
比如早些时候我做过
git subtree add -P Some/Sub/Dir --squash git@gitserver:lib.git master
Run Code Online (Sandbox Code Playgroud)
将库代码引入我们主项目中的Some/Sub/Dir.这里的一切都很棒,所以我把我的更改推到了我们的中央主要项目裸git repo.然后我决定在Some/Sub/Dir中更改我的本地版本的lib,提交它,然后将其拆分以将其推回到lib.git repo
git subtree split -P Some/Sub/Dir -b some_branch
Run Code Online (Sandbox Code Playgroud)
一切都按预期工作.不再需要repo的本地副本我删除了它.
从我们的中央仓库克隆了一个新的repo副本后,我对Some/Sub/Dir中的lib进行了一些更改,并决定将这些更改拆分出来并将它们推回到lib.git存储库.我尝试使用与以前相同的子树分割命令,但这次我最终得到以下输出:
1/ 3 (0)
2/ 3 (1)
3/ 3 (1)
fatal: bad object d76a03f0ec7e20724bcfa253e6a03683211a7bb1
Run Code Online (Sandbox Code Playgroud)
d76a03f0ec7e20724bcfa253e6a03683211a7bb1来自我添加子树时:
commit 43b3eb7d69d5eb64241eddb12e5bd74fd0215083
Author: Ian Bond <ibond@onezero.com>
Date: Fri Apr 22 15:06:50 2011 -0400
Squashed 'Subtree/librepoLib/' content from commit d76a03f
git-subtree-dir: Subtree/librepoLib
git-subtree-split: d76a03f0ec7e20724bcfa253e6a03683211a7bb1
Run Code Online (Sandbox Code Playgroud)
实际上是指lib.git repo中的提交.
我能够拼凑起来(我是一个git noob所以我可能错了,忽略了一些东西,或者在这里使用了不正确的术语),'git subtree add --squash'将带来整个历史远程lib.git repo进入当前仓库,将其压缩到单独的提交中,然后将该提交添加到工作分支中.lib.git提交历史记录保留在当前的repo中,但是它们是悬空提交,因为除了通过压缩提交的文本之外它们实际上没有被引用.只要那些悬空提交仍然存在,git-subtree就可以使用它们来执行拆分,但是由于推或拉不包含悬空对象(或者如果我运行gc并完全修剪悬空对象),那些悬空提交将丢失并且git-subtree不再具有执行拆分所需的信息.
我添加了一个脚本,可以完全重现我一直存在的问题.
我的问题是:
1)我可以做些什么来处理我现在有子树的现有情况,我想要合并回原始仓库,但不再有任何将它们链接在一起的历史记录.我目前的想法是做一些像:
git subtree split -P Some/Sub/Dir 43b3eb7^.. --ignore-joins -b splitBranch
Run Code Online (Sandbox Code Playgroud)
分离自'git子树添加'以来的所有历史记录并将其合并回原始仓库(幸运的是,自添加以来没有任何变化).这是最好的方式吗?有关如何执行合并的任何建议?
2)我能做些什么来使git-subtree按预期工作?我相信如果我省略'git subtree add'上的--squash参数,那么一切都会起作用,但这会导致一堆无关的历史注入我的仓库.有没有办法保持所需的提交(最好不保持库的整个历史)?
Chr*_*sen 14
目的git subtree split是在子树的原始历史记录之上创建一些新的提交(表示最初在子树的本地目录中进行的"本地"更改).由于它直接涉及子树的原始历史(作为触及子树的第一个重写的本地提交的父提交),因此如果不存在子树的原始历史本身,则不能进行拆分操作.
想想你将对所git subtree split产生的历史做些什么.您可能希望将其推送到存储库,您可以将其合并到"上游"历史记录的其余部分.为了使此合并操作有意义,拆分历史记录需要基于原始历史本身1.
安排用户拥有子树原始历史记录的最可靠方法可能是在文档中发布子树上游存储库的URL,并让它们为它定义一个遥控器(在一个单独的"无关"遥控器中完全没问题.库).例如
如果您需要使用"上游"
Some/Sub/Dir(以引入外部更改或推出本地更改),请在使用之前为库的存储库定义和更新远程git subtree:Run Code Online (Sandbox Code Playgroud)git remote add lib git@host:the-lib-repository && git fetch lib
即使您没有使用,也需要执行类似的操作,--squash因为用户需要知道从哪里获得新的上游提交(以及(最终)推送新的拆分生成的提交).
使用--squash在主项目中为您提供"干净"的历史记录,这意味着只有那些需要处理子树的"上游"的用户实际上必须在其存储库中拥有其对象.
看起来你对对象模型有很好的理解.你是正确的,git subtree add --squash拉入的历史将变成悬空2,但git subtree split仍然可以使用它直到它被修剪掉.
(参考你的复制脚本)
你能够成功分裂你的repoMainClone唯一因为本地克隆自动硬链接(或复制)所有文件.git/objects/(从而获得repoMain对悬挂(或接近悬挂2)对象的副本repoLib)而不是使用通常的"包协议"传输(它将传输的对象限制为仅传输的refs所需的那些;即省略任何东西repoLib).您repoMainPull实际上是等效克隆file://"$(pwd)"/repoMain repoMainCloneFile(file://URL强制本地克隆使用基于包的传输而不是仅链接/复制所有内容).
1 实际上,您可以直接合并不相关的历史记录,但是您失去了进行三向合并的能力(因为没有共同的祖先).这将是一个相当大的牺牲.
你提议的git subtree split -P Some/Sub/Dir 43b3eb7^.. --ignore-joins …(其中43b3eb7是由此产生的综合提交git subtree add --squash …)将产生一个不相关的历史(除了它需要是43b3eb7..因为43b3eb7^意味着"43b3eb7的第一个父"和43b3eb7没有父母).我不确定git subtree split是否设计了这样的范围.git subtree split刚才说的文件<commit>,但从未真正提到它的目的.读取代码显示它默认为HEAD,这可能表明它是一个单一的提交,指定应该处理以进行拆分的历史记录的"提示".此外,打开调试输出会显示一条消息incorrect order:,该消息可能表明使用范围参数将拆分操作置于意外情况(它希望在处理提交本身之前处理了提交的所有父项,但是范围确保从不处理43b3eb7(它是子树合并提交的父级).--ignore-splits如果你想生成"不相关的"历史并尝试以某种方式使用它,我认为你可以使用并离开范围:git subtree split -P Some/Sub/Dir --ignore-joins ….
2
它们实际上并没有立即悬空,git subtree add --squash因为它们仍被FETCH_HEAD引用.然而,一旦完成无关的提取,它们将变得真正悬空.