我们已经将一个子目录S变成了一个单独的仓库并将其重新添加为分支B中的子模块。现在,当我想切换回原始分支A时,子目录被检入原始仓库, git抱怨子模块中的东西没有跟踪,必须先将其移走。我们可以切换回A,而无需手动拖动S并将它移回B进行B?
更新:下面的评论表明它可能是旧分支的缺陷,而git不愿意在这里做一件聪明的事情。是这样的,在主回购中使用S的原始分支有什么样的变基(如果有的话)可以让它在任何时候与子模块化版本共存并且可以结账吗?
答案 0 :(得分:11)
您的历史记录如下:
---X A
\
Y B
git ls-tree A
显示(例如):
040000 tree 48770cdc854cc14fecc71029180be7a979f4baa1 S
100644 blob beac6e189b7c69b249271b52cd2db5e418c05a50 file
git ls-tree A:S
显示(例如):
100644 blob 6357df9903460c9e8b43311aff3c7a6fd7fe6aa1 somefile
git ls-tree B
显示(例如):
100644 blob 1f8556335163a2bcbcc366a17d08d1f8e0540e6f .gitmodules
160000 commit 234871cd6f0c1f9109e483383d7712dd8a1986e5 S
100644 blob beac6e189b7c69b249271b52cd2db5e418c05a50 file
(cd S; git ls-tree HEAD)
显示(例如):
100644 blob abccc3958be33be4b93f56efae1b60820545aad2 somefile
您希望从提交Y(或更高版本)移动到提交X(或更早),反之亦然。
如果你的活动分支是B,那么git checkout A
说(例如):
error: The following untracked working tree files would be overwritten by checkout:
S/somefile
Please move or remove them before you can switch branches.
Aborting
Git非常努力地永远不会丢失数据,除非你告诉它这样做(例如使用“强制”选项)。 Git在此处发现并报告的问题是,分支A具有与S/somefile
不同的内容,然后是工作树。因为没有跟踪S/somefile
(从超级项目的角度来看),Git拒绝替换文件,因此拒绝切换分支/提交。
Git可以说是更聪明一点(通过注意文件在子模块中被跟踪,因此在超级项目中切换分支时不应该被认为是未跟踪的),但这是当前实现的限制。有一个针对Git的Google Summer of Code 2011项目aims to address some areas of submodule support,但我不清楚是否会涵盖这个确切的问题。
您可以按照建议重写您的历史记录,以便S
似乎始终是一个子模块。这肯定会为将来的提交开关提供最平滑的表面,但是由于您需要确保在子模块的原始存储库中提交了反映原始S
目录的每个历史状态的事实,这很复杂。提交。如果您有许多不同的S
树(即在将其转换为子模块之前在S
下进行了一些本地更改),那么这可能是一个复杂的过程/脚本。
一个更简单的解决方法可能是在切换到将其作为目录的提交之前临时签出子模块中的“空分支”。
在子模块中创建“空分支”。
git checkout --orphan empty
git rm -r --cached .
git commit --allow-empty -mempty
您可以在子模块的原始存储库中发布此“分支”,以便其他任何人都不需要自己重新创建它。
当您需要从S
是子模块的提交切换到S
是目录的提交时,首先检查子模块中的“空分支”:
(cd S && git checkout empty)
git checkout A
您会看到此警告,因为Git会留下S/.git
:
warning: unable to rmdir S: Directory not empty
由于S/.git
仍然存在,因此在处理S
作为目录的提交时,应该小心在S
之外发出Git命令;在S
下发出的Git命令将在S/.git
上运行(在此状态下,它只是一个“子库”,而不是完整的子模块),而不是顶级.git
存储库。
当您需要从S
是目录的提交切换到S
是子模块的提交时,您需要检查子模块中的相应分支/提交切换超级项目的提交后
您可以使用git submodule update
来恢复超级项目中记录的提交。或者,如果您正在处理子模块中的分支,请将其检出。
git checkout B
# THEN
git submodule update
# OR checkout a specific branch
(cd S && git checkout master)
# OR checkout previous branch
(cd S && git checkout -)