在主存储库中的子目录与子模块化分支之间切换

发布于 2024-11-18 00:53:17 字数 245 浏览 3 评论 0原文

我们已将子目录 S 放入单独的存储库中,并将其作为子模块重新添加到分支 B 中。现在,当我想切换回原始分支 A(该子目录已签入原始存储库中)时,git 会抱怨子模块中的内容未被跟踪,必须首先移走。我们是否可以切换回 A,而无需手动将 S 拖走,然后将其移回 B?

更新:下面的评论表明这可能是旧分支的缺陷,并且 git 不愿意在这里做一些聪明的事情。是这样吗?对主存储库中带有 S 的原始分支进行什么样的变基(如果有)可以让它与子模块化版本共存并可以随时检出?

We've made a subdirectory S into a separate repo and re-added it as a submodule in a branch B. Now when I want to switch back to the original branch A, where the subdirectory was checked into the original repo, git complains that the stuff from the submodule is untracked and has to be moved away first. Can we switch back to A at all without manually dragging the S away and they moving it back in for B?

Update: the comments below indicate it may be a deficiency of the old branch and git not willing to do a smart thing here. Is this so, and what kind of rebasing, if any, of the original branch with S in the main repo can let it coexist and be checkout-able at any time vs. the submodularized version?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

落在眉间の轻吻 2024-11-25 00:53:17

您的历史记录看起来像这样:

---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 项目,旨在解决子模块的某些领域支持,但我不清楚这个确切的问题是否会得到解决。


您可以按照您的建议重写您的历史记录,以便 S 始终显示为子模块。这肯定会为未来的提交切换提供最平滑的表面,但由于您需要确保子模块的原始存储库中的提交反映了 S 目录的每个历史状态,因此情况变得复杂在原始提交中。如果您有许多不同的 S 树(即,在将其转换为子模块之前,您在 S 下进行了一些本地更改),那么这可能是一个复杂的过程/脚本。

一个更简单的解决方法可能是在切换到将其作为目录的提交之前暂时签出子模块中的“空分支”。

  • 在子模块中创建“空分支”。

    git checkout --orphan 空
    git rm -r --cached 。
    git commit --allow-empty -mempty
    

    您可以在子模块的原始存储库中发布此“分支”,这样其他人就不需要自己重新创建它。

  • 当您需要从 S 是子模块的提交切换到 S 是目录的提交时,请首先签出子模块中的“空分支”:

    (cd S && git checkout 空)
    git 结账 A
    

    您将看到此警告,因为 Git 会留下 S/.git

    警告:无法 rmdir S:目录不为空
    

    由于 S/.git 仍然存在,因此在处理具有 S 的提交时,应小心仅在 S 外部发出 Git 命令code> 作为目录;在 S 下发出的 Git 命令将在 S/.git 上运行(在这种状态下,它只是一个“子存储库”,而不是完整的子模块)而不是顶层.git 存储库。

  • 当您需要从 S 是目录的提交切换到 S 是子模块的提交时,您需要签出相应的分支/提交切换超级项目的提交后在子模块中。
    您可以使用 git submodule update 来恢复超级项目中记录的提交。或者,如果您正在处理子模块中的分支,只需将其签出即可。

    git checkout B
    
    # 然后
    
    git 子模块更新
    
    # 或者签出特定分支
    
    (cd S && git checkout master)
    
    # 或者签出上一个分支
    
    (cd S && git checkout -)
    

Your history looks something like this:

---X     A
    \
     Y   B

git ls-tree A shows (e.g.):

040000 tree 48770cdc854cc14fecc71029180be7a979f4baa1    S
100644 blob beac6e189b7c69b249271b52cd2db5e418c05a50    file

git ls-tree A:S shows (e.g.):

100644 blob 6357df9903460c9e8b43311aff3c7a6fd7fe6aa1    somefile

git ls-tree B shows (e.g.):

100644 blob 1f8556335163a2bcbcc366a17d08d1f8e0540e6f    .gitmodules
160000 commit 234871cd6f0c1f9109e483383d7712dd8a1986e5  S
100644 blob beac6e189b7c69b249271b52cd2db5e418c05a50    file

(cd S; git ls-tree HEAD) shows (e.g.):

100644 blob abccc3958be33be4b93f56efae1b60820545aad2    somefile

You want to move from commit Y (or later) to commit X (or earlier) or vice versa.

If your active branch is B, then git checkout A says (e.g.):

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 tries very hard to never lose data unless you tell it to do so (e.g. with “force” options). The problem Git finds and reports here is that branch A has different content for S/somefile then the working tree. Because S/somefile is not tracked (from the perspective of the superproject), Git refuses to replace the file and thus refuses to switch branches/commits.

Git could arguably be smarter about this (by noticing that the file is tracked in the submodule, so it should not really be considered untracked when switching branches in the superproject), but it is a limitation of the current implementation. There is a Google Summer of Code 2011 project for Git that aims to address some areas of submodule support, but it is not clear to me whether this exact problem will be covered.


You could, as you suggest, rewrite your history so that S always appeared to have been a submodule. This would certainly present the smoothest surface for future commit switches, but it is complicated by the fact that you would need to make sure you have commits in the submodule’s origin repository that reflect each historical state of the S directory in the original commits. If you have many different S trees (i.e. you had made some local changes under S before converting it to a submodule), then this may be a complicated process/script.

A simpler workaround might be to temporarily checkout an “empty branch” in the submodule before switching to a commit that has it as a directory.

  • Create the “empty branch” in the submodule.

    git checkout --orphan empty
    git rm -r --cached .
    git commit --allow-empty -mempty
    

    You could publish this “branch” in the submodule’s origin repository so that no one else would need to recreate it form themselves.

  • When you need to switch from a commit where S is a submodule to a commit where S is a directory, checkout the “empty branch” in the submodule first:

    (cd S && git checkout empty)
    git checkout A
    

    You will see this warning because Git will leave behind S/.git:

    warning: unable to rmdir S: Directory not empty
    

    Because S/.git is still present, you should be careful to issue Git commands only outside S when working on a commit that has S as a directory; Git commands issued under S would operate on the S/.git (in this state, it is just a “subrepository”, not a full submodule) instead of the top level .git repository.

  • When you need to switch from a commit where S is a directory to a commit where S is a submodule, you will need to check out the appropriate branch/commit in the submodule after switching the superproject’s commit.
    You can use git submodule update to restore the commit that is recorded in the superproject. Or, if you were working on a branch in the submodule, just check it back out.

    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 -)
    
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文