Git子树: 将子树移动到不同目录并拉取它

6
假设我创建了一个如下所示的子树:
git subtree --add --prefix=subdir <path_to_remote> <remote_branch> --squash

我想要移动/重命名子目录,所以我执行了以下操作:

git mv subdir dir2/subdir

现在当我尝试将该子树拉取到一个新的前缀时:

git subtree --pull --prefix=dir2/subdir <path_to_remote> <remote_branch> --squash

Git 说:

Can't squash-merge: 'dir2/subdir' was never added.

我该如何正确地做到这一点?

2个回答

11
git subtree 命令知道你的子树,因为它在你 add 一个子树时将其名称存储在第一次提交中。
Add 'subdir/' from commit 'c7fbc973614eced220dcef1663d43dad90676e00'

git-subtree-dir: subdir
git-subtree-mainline: c166dc69165f6678d3c409df344c4ed9577a2e11
git-subtree-split: c7fbc973614eced220dcef1663d43dad90676e00

git subtree pull 带上 --squash 选项会寻找一个包含 git-subtree-dir 的提交,以找到来自远程仓库的最新提交,因此确定了应用和合并所有提交的起点。

在许多情况下,git subtree split --rejoin 操作将会成功:

$ git subtree split --rejoin --prefix=dir2/subdir HEAD
Merge made by the 'ours' strategy.
25070c483647f8136655d0e0c6c3d62f469177aa

生成的提交看起来像这样:
Split 'dir2/subdir/' into commit '25070c483647f8136655d0e0c6c3d62f469177aa'

git-subtree-dir: dir2/subdir
git-subtree-mainline: 59cc3c770e78dbc30bdfe36a6b4e14ce83b38f6c
git-subtree-split: 25070c483647f8136655d0e0c6c3d62f469177aa

此提交将被找到,下一个`git subtree pull --squash`在大多数情况下将成功。请注意,有时子树操作会失败,并且会在您的工作副本中留下子树的分支。确保删除任何残留的临时分支以从干净的状态开始。
有时上述操作无法成功,但我从未找到原因。在这些情况下,您可以基于添加子树的提交进行变基,并通过修改提交消息手动更改目录名称。然而,此操作将损坏其他人的整个历史记录。

1
谢谢,对我有用!既然你说git subtree只是查找提交信息中的文本,我尝试使用重命名将必要的文本添加到提交中,但就像你说的一样,最终在我的工作副本中得到子树的一个分支。 - Amomum

2
正如其他答案所指出的那样,这里的问题在于git-subtree将文件夹名称嵌入到提交中,并拒绝承认您已经重命名了它。该答案建议进行分离/合并,我认为这可能会使您的提交历史变得有些臃肿和复杂。
一个更简单的方法是进行简单的提交,以教会git-subtree做正确的事情。
  1. 查找 git-subtree 执行压缩合并的最后一次提交。我认为 git-subtree 没有提供原生工具来执行此操作,但是一种方法是手动搜索(假设您的文件夹名为“basedir”):

    git log -1 --grep "git-subtree-dir: basedir"
    

    最终提交中可能看起来像这样:

    git-subtree-dir: basedir
    git-subtree-split: 607efa887537d5544b3a99e2a0a2dd64abcc72ee
    

    您需要注意提交消息中 git-subtree-split: 的值。假设它是 607efa887537d5544b3a99e2a0a2dd64abcc72ee

  2. 像这样重命名文件夹:git mv basedir basedir2 && git commit

  3. 现在,通过执行以下操作进行空提交(假设您想将其重命名为“basedir”):

    git commit --allow-empty
    

    使用以下提交消息(当然可以自定义):

    从 basedir 重新分配子树目录到 basedir2
    
    git-subtree-dir: basedir2
    git-subtree-split: 607efa887537d5544b3a99e2a0a2dd64abcc72ee
    

    请注意,哈希值 607efa887537d5544b3a99e2a0a2dd64abcc72ee 与之前的相同。

你现在准备就绪了!除了创建一个空的提交消息并手动将git-subtree-dir从"basedir"重命名为"basedir2"之外,你基本上没有做任何事情。Git-subtree在某种程度上是一种简单的工具,它只是用它来记住子目录是什么。我觉得它应该真正地作为工具的一部分包含在内,而不是用户需要手动构建这样一个提交,但无论如何它都能正常工作(至少在我测试时是这样的)。


感谢您的确认,git-subtree-* 元数据可以安全地应用于以后的提交。当我们在 GitLab 中使用合并请求时,如果忘记从相关提交中传输元数据,这种情况经常发生。 - björnen

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