当子项目是子树合并时,如何还原来自子树远程的提交?

时间:2011-04-28 18:12:22

标签: git

我有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 更好吗?

2 个答案:

答案 0 :(得分:13)

git revert v3中的最终superproject失败,因为它没有说明v3是“后面”子树合并的事实(因此记录的路径名在Nv3)。

恢复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(例如因为v3subproject的上下文中没有问题,但是不适合用于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的第二个版本中。生成的历史记录与第一个变体具有相同的结构和内容。

如果您无法在superprojectrevert-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'历史记录,Rsubproject/的版本,只有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子模块会有所帮助。或者,只需使用一个存储库,并将子项目的更改保存在单独的分支或一组分支中。

希望这有帮助。