我有testcase shell script。我想降级子项目,但无法弄清楚如何。这是故事:
我创建了两个回购,“子项目”和“超级项目”。我将一些文件的几个版本提交到子项目中,给它们标签“v0”,“v1”,“v2”......等。在超级项目中,我将子项目添加为repo并将其合并为子树:
git remote add subproject ../subproject
git fetch --tags subproject
git merge -s ours --no-commit v1
git read-tree --prefix=subproject/ -u v1
git commit -m "added subproject at v1"
接下来,我将子项目更新为v3,这很顺利:
git merge -s subtree -X subtree=subproject v3
然后我意识到我真的想要v2。我认为我需要的是一个篮板,但我不确定。这是我试过的:
git checkout -b downgrade-subproject-to-v2
git rebase -s subtree -X subtree=subproject --onto v2 downgrade-subproject-to-v2
但是我看到子项目的内容在我的超级项目的主目录中被spoojed了!
所以我做了一些阅读并找到了其他一些想法。这个不起作用:
git revert --no-edit v3
它还在我的超级项目中spoojed文件,并抱怨冲突。这也行不通:
git merge -s subtree -X subtree=subproject v2
显然,v3已经合并,因此合并v2(v3的祖先)将不起作用。
我发现导致降级的唯一原因是:
git diff v3 v2 --src-prefix=subproject/ --dst-prefix=subproject/ | patch -p0
但我不喜欢它,因为它实际上并没有在提交DAG中显示正确的祖先。它只是对树的蛮力重写。此外,它依赖于补丁程序进行“合并”,这并不像git合并那样聪明。如果superproject在降级之前对子项目进行了任何更改,则补丁将不太可能像git merge那样解决合并错误。
是否有 规范 方法使用子树合并还原子项目版本?如果没有,至少有一些 比diff | patch 更好吗?
答案 0 :(得分:13)
git revert v3
中的最终superproject
失败,因为它没有说明v3
是“后面”子树合并的事实(因此记录的路径名在N
和v3
)。
superproject
历史记录使用Git 1.7.5,您可以使用
git revert --strategy subtree -X subtree=subproject v3
代替您的git diff | patch; commit
方法。它可能会更好地利用合并机制。但是,无论使用哪种方法,新提交的父级都将建立在superproject
的历史记录上(可能不是您真正想要的)。
您的subproject
历史记录如下:
(v1) (v2) (v3)
/ / /
----o----o----o
您的superproject
历史记录显示了这一点:
(v1) (v2) (v3)
/ / /
----o----o----o
\ \
----M---------N
两种方法都使用此历史记录创建提交O
:
(v1) (v2) (v3)
/ / /
----o----o----o
\ \
----M---------N----O
subproject
历史记录您可能更乐意直接在v3
之上构建提交。您可以将此类提交合并到superproject
历史记录中,就像使用任何新的subproject
提交一样。
在subproject
:结帐v3
,还原它(R
),并标记结果(revert-v3
)
git checkout v3
git revert HEAD
git tag revert-v3
git checkout - # go back to wherever you originally were
结果历史:
(v1) (v2) (v3) (revert-v3)
/ / / /
----o----o----o----R
在superproject
中:获取结果,并使用子树合并(P
)将其合并
git fetch --tags subproject
git merge -s subtree -X subtree=subproject revert-v3
结果历史:
(v1) (v2) (v3) (revert-v3)
/ / / /
----o----o----o----R
\ \ \
----M---------N----P
如果您不想在revert-v3
本身拥有subproject
(例如因为v3
在subproject
的上下文中没有问题,但是不适合用于superproject
superproject
),然后您可以完全在superproject
中完成工作,代价是搅拌工作树(在“无关”提交之间来回切换将有效删除和恢复所有工作树文件,所以mtimes,inode等会发生变化):
在v3
:checkout git status # make sure to start with a clean index and working tree
git checkout v3
git revert HEAD
git tag revert-v3
git checkout -
git merge -s subtree -X subtree=subproject revert-v3
中,还原它,标记它,返回上一个结帐,子树合并新提交
revert-v3
主要区别在于,最终superproject
标记仅出现在subproject
的第二个版本中。生成的历史记录与第一个变体具有相同的结构和内容。
如果您无法在superproject
或revert-v3
中生成工作树,而您不希望subproject
中有subproject
,那么您可以使用临时克隆superproject
(在克隆中:恢复提交,标记它;在git subtree
中:从克隆中获取标记,子树合并它;然后删除临时克隆)。
git subtree pull -P prefix repository refspec
您可能想要调查apenwarr的git subtree
命令。它对子树合并的支持更加简化(例如git subtree split
)。此外,它的git diff new old | patch
命令可能特别有用,因为它可以让您将从git revert --strategy
(或git subtree split --prefix=subproject/ --onto v1
)的结果提交的提交转换为似乎已经提交的提交直接在子树的历史上。
superproject
1 将“恢复 (v1) (v2) (v3)
/ / /
----o----o----o
\ \
----M---------N----R
”历史记录:
subproject
并提取 (v1) (v2) (v3)
/ / /
----o----o----o-----R'
- 只有这样的历史:
R'
其中subproject
之前的所有内容都是原始R'
历史记录,R
是subproject/
的版本,只有superproject
的内容。
这种“事后提取”的能力意味着你可以在subproject/
中处理内容本身,而不必担心触及subproject
的提交是否可能成为发送“上游”的候选者到--onto v1
存储库。
1
如果您使用git subtree add
初始添加子树 2 ,则可以不使用git subtree
。它将特殊文本放在它生成的提交消息中,以便它可以识别历史记录中的“子树”位。
2 或者您可以使用类似
的内容“转换为git rm -rf subproject &&
git commit -m 'converting to subproject/ to "git subtree"' &&
git subtree add --prefix=subproject most-recent-subproject-commit
”
{{1}}
答案 1 :(得分:1)
也许在git slave的帮助下使用git子模块会有所帮助。或者,只需使用一个存储库,并将子项目的更改保存在单独的分支或一组分支中。
希望这有帮助。