如何使 git 存储库中的现有目录成为 git 子模块
How to make an existing directory within a git repository a git submodule
我对 git-子模块很困惑。
基本上我的问题是我无法让 git 理解 ~/main-project/submodule
是一个子模块。
我对 git 子模块有很好的经验:
在我的 dotfiles repository 中,我在 ~/dotfiles-repo
中创建了 .gitmodules 文件,并在其中添加了路径和 url。从那时起,如果我对子模块和 运行 git status
中的文件进行更改,我会得到类似这样的内容:.vim/bundle/auto-complete (new commits) # in red
我在 ~/main-project
中创建了 .gitmodules
文件,但是:
- 如果我对
~/main-project/submodule
进行更改甚至推送更改,当 运行ning git status
在 ~/main-project
。 我刚得到在这些目录中所做的更改
当我点击这些目录的 github
的文件夹链接时,它并没有将我定向到存储库本身,而是我留在同一个存储库中。
- 也许我漏掉了重点。子模块的主要特点是什么?
- 为什么git理解the dotfiles repo but not in my other repo中的子模块?
- 难道我已经告诉git将
~/main-project/submodule
里面的文件加入索引了?
我读过 this question which led me to this answer 但我不确定我是否需要 git-subtree
。我不想做那些可能会造成难以恢复的变化的事情。
Edit: This suggested duplicate-solution didn't work either, I recieved an error that Updates were rejected because the remote contains work that you do not have locally
. It seems that @GabLeRoux practically told me to push <repo-A>
to the url of <repo-B>
.
使用git submodule absorbgitdirs
这就是 docs 声明此命令的作用:
If a git directory of a submodule is inside the submodule,
move the git directory of the submodule into its superprojects
$GIT_DIR/modules
path and then connect the git directory and
its working directory by setting the core.worktree
and adding
a .git file pointing to the git directory embedded in the
superprojects git directory.
因此,不必像@DomQ 和我自己在之前的回答中所建议的那样从头开始,只需添加 运行 以下内容即可:
- 不从索引中删除子模块,将子模块的 url 添加到
.gitmodules
并添加到 .git/config
和
git submodule add <url> <path>
- 将子模块的
$GIT_DIR
目录(常规存储库中的 .git
)移动到 .git/modules/<path>
和
git submodule absorbgitdirs <path>
原始答案 - v2.12.0 之前
git submodule absorbgitdirs
仅在 v2.12.0-rc0 (see commit) 中引入。
解决方法很简单。它是从 here.
中提取的
git rm submodule-dir
这将删除 git 在 submodule-dir
中跟踪的所有文件
rm -rf submoduledir
这将删除可能留在 submodule-dir
中的所有其他文件,因为 git 忽略了它们。
- 现在,我们必须提交才能从索引中删除文件:
git commit
提交后,我们清理了 git 跟进和 submodul-dir
没有跟进的文件。
现在是时候了:
git submodule add <remote-path-to-submodule>
这将重新添加子模块,但作为真正的子模块。
- 此时检查
.gitmodules
并查看子模块是否已成功添加可能是个好主意。在我的例子中,我已经有一个 .gitmodules
文件,所以我必须修改它。
基本上没有比假装重新开始更好的方法了:
- 确保所有地方都提交
- 将您的子存储库移开
git submodule add
来自子存储库的远程
cd mysubmodule
git fetch ../wherever/you/stashed/the/sub-repository/in/step-1
git merge FETCH_HEAD
要解释为什么会这样,在我看来,需要更深入地了解 是什么子模块 ,而不是从 git-submodule(1)
manual page (or even the relevant chapter from the Git book). I found some more in-depth explanations on this blog post 中收集到什么,但由于 post 有点长,我冒昧地在这里总结一下。
在较低级别,git 子模块由以下元素组成,
- 子模块树顶部的commit object,
- (在 Git 的最新版本中)
.git/modules
中的一个子目录,用于托管子模块的 Git 对象,
.gitmodules
配置文件中的条目。
提交对象包含(或更准确地说,由 SHA1 引用)在父树对象中。这是不寻常的,因为 usually happen the other way round, but this explains why you see a directory appear in the main repository's git status
after you have performed a commit in the submodule. You can also make some experiments 和 git ls-tree
可以更详细地观察这个提交对象。
.git/modules
中的子目录代表子模块中的.git
子目录;事实上,子模块中有一个 .git
file ,它使用 gitdir:
行指向前者。这是默认行为 since version 1.7.8 of Git。不知道为什么如果你只是继续拥有一个单独的 .git
目录,为什么一切都不会正常工作,除非发行说明中指出你可能 运行 在具有子模块的分支之间切换时遇到麻烦还有一个没有。
.gitmodules
文件提供 git submodule update --remote
和朋友应该从中提取的 URL;这显然不同于主存储库的一组遥控器。另请注意,.gitmodules
被 git submodule sync
命令和在幕后调用它的其他命令部分复制到 .git/config
。
虽然手动为 .gitmodules
+ .git/config
和 .git/modules
+ mysubmodule/.git
进行必要的更改相当容易(事实上,有甚至 git submodule absorbgitdirs
对于后者), 并没有真正的瓷器来创建树内提交对象 。因此,通过移动 + 重做上面提出的更改提出的解决方案。
按顺序回答您的问题:
- 根据GitHub的子模块purpose。在功能方面,它被设计为概念化的子存储库(几乎可以像任何其他文件一样对待),即版本由父存储库控制,其中 父跟踪子模块的当前提交 ID (子存储库)而不是它的内容.
- 这很可能是因为您已经将文件添加到存储库的索引中。在这种情况下,解决方案是
git rm --cached submodule-name/
。然后创建一个中间提交,然后添加文件夹 作为存储库 :git add submodule-name
(注意没有尾部斜杠 在子模块的子模块名称之后)。
- 是:)
您提到的 answer 也可能更正您的提交历史记录:
- 优点:
该文件夹将被视为所有提交历史记录中的子模块,而不仅仅是所有未来的提交。如果您检出到将其视为文件夹的先前版本,这可以避免任何并发症。这是一个复杂的问题,因为当你 return 到分支的顶端时,你可能还必须输入你的子模块并检出到最新的提交以恢复所有文件(可能从你的工作目录中删除)。这可以通过对您的最新提交进行某种递归检查来避免。
- 缺点:
如果提交历史被修改,所有其他贡献者也必须重新克隆项目,因为他们会遇到合并冲突或更糟;将问题提交重新引入项目。
None 这些解决方案似乎对我有用,所以我想出我自己的解决方案:
确保一个新的 git 存储库已经存在,它将保存内容
例如,我们将使用“git@github.com:/newemptyrepo”
导航到您正在模块化的目录:
cd myproject/submodule-dir
- 从父索引中删除 to-be 子模块:
git rm -r --cached .
- 在 to-be 子模块中初始化一个新的 git 仓库:
git init
- 为 to-be 子模块设置源并进行第一次提交:
git remote add origin git@github.com:/newemptyrepo
git add . && git commit && git push --set-upstream origin master
- 现在您必须导航到父存储库的 top-level 路径:
cd .. && cd `git rev-parse --show-toplevel`
- 最后,像往常一样添加子模块:
git submodule add git@github.com:/newemptyrepo ./myproject/submodule-dir
- 现在提交并推送上述命令所做的更改,一切就绪!
我对 git-子模块很困惑。
基本上我的问题是我无法让 git 理解 ~/main-project/submodule
是一个子模块。
我对 git 子模块有很好的经验:
在我的 dotfiles repository 中,我在 ~/dotfiles-repo
中创建了 .gitmodules 文件,并在其中添加了路径和 url。从那时起,如果我对子模块和 运行 git status
中的文件进行更改,我会得到类似这样的内容:.vim/bundle/auto-complete (new commits) # in red
我在 ~/main-project
中创建了 .gitmodules
文件,但是:
- 如果我对
~/main-project/submodule
进行更改甚至推送更改,当 运行ninggit status
在~/main-project
。 我刚得到在这些目录中所做的更改 当我点击这些目录的
github
的文件夹链接时,它并没有将我定向到存储库本身,而是我留在同一个存储库中。- 也许我漏掉了重点。子模块的主要特点是什么?
- 为什么git理解the dotfiles repo but not in my other repo中的子模块?
- 难道我已经告诉git将
~/main-project/submodule
里面的文件加入索引了?
我读过 this question which led me to this answer 但我不确定我是否需要 git-subtree
。我不想做那些可能会造成难以恢复的变化的事情。
Edit: This suggested duplicate-solution didn't work either, I recieved an error that
Updates were rejected because the remote contains work that you do not have locally
. It seems that @GabLeRoux practically told me to push<repo-A>
to the url of<repo-B>
.
使用git submodule absorbgitdirs
这就是 docs 声明此命令的作用:
If a git directory of a submodule is inside the submodule, move the git directory of the submodule into its superprojects
$GIT_DIR/modules
path and then connect the git directory and its working directory by setting thecore.worktree
and adding a .git file pointing to the git directory embedded in the superprojects git directory.
因此,不必像@DomQ 和我自己在之前的回答中所建议的那样从头开始,只需添加 运行 以下内容即可:
- 不从索引中删除子模块,将子模块的 url 添加到
.gitmodules
并添加到.git/config
和
git submodule add <url> <path>
- 将子模块的
$GIT_DIR
目录(常规存储库中的.git
)移动到.git/modules/<path>
和
git submodule absorbgitdirs <path>
原始答案 - v2.12.0 之前
git submodule absorbgitdirs
仅在 v2.12.0-rc0 (see commit) 中引入。
解决方法很简单。它是从 here.
中提取的git rm submodule-dir
这将删除 git 在submodule-dir
中跟踪的所有文件
rm -rf submoduledir
这将删除可能留在submodule-dir
中的所有其他文件,因为 git 忽略了它们。- 现在,我们必须提交才能从索引中删除文件:
git commit
提交后,我们清理了 git 跟进和submodul-dir
没有跟进的文件。 现在是时候了: git submodule add <remote-path-to-submodule>
这将重新添加子模块,但作为真正的子模块。- 此时检查
.gitmodules
并查看子模块是否已成功添加可能是个好主意。在我的例子中,我已经有一个.gitmodules
文件,所以我必须修改它。
基本上没有比假装重新开始更好的方法了:
- 确保所有地方都提交
- 将您的子存储库移开
git submodule add
来自子存储库的远程cd mysubmodule
git fetch ../wherever/you/stashed/the/sub-repository/in/step-1
git merge FETCH_HEAD
要解释为什么会这样,在我看来,需要更深入地了解 是什么子模块 ,而不是从 git-submodule(1)
manual page (or even the relevant chapter from the Git book). I found some more in-depth explanations on this blog post 中收集到什么,但由于 post 有点长,我冒昧地在这里总结一下。
在较低级别,git 子模块由以下元素组成,
- 子模块树顶部的commit object,
- (在 Git 的最新版本中)
.git/modules
中的一个子目录,用于托管子模块的 Git 对象, .gitmodules
配置文件中的条目。
提交对象包含(或更准确地说,由 SHA1 引用)在父树对象中。这是不寻常的,因为 usually happen the other way round, but this explains why you see a directory appear in the main repository's git status
after you have performed a commit in the submodule. You can also make some experiments 和 git ls-tree
可以更详细地观察这个提交对象。
.git/modules
中的子目录代表子模块中的.git
子目录;事实上,子模块中有一个 .git
file ,它使用 gitdir:
行指向前者。这是默认行为 since version 1.7.8 of Git。不知道为什么如果你只是继续拥有一个单独的 .git
目录,为什么一切都不会正常工作,除非发行说明中指出你可能 运行 在具有子模块的分支之间切换时遇到麻烦还有一个没有。
.gitmodules
文件提供 git submodule update --remote
和朋友应该从中提取的 URL;这显然不同于主存储库的一组遥控器。另请注意,.gitmodules
被 git submodule sync
命令和在幕后调用它的其他命令部分复制到 .git/config
。
虽然手动为 .gitmodules
+ .git/config
和 .git/modules
+ mysubmodule/.git
进行必要的更改相当容易(事实上,有甚至 git submodule absorbgitdirs
对于后者), 并没有真正的瓷器来创建树内提交对象 。因此,通过移动 + 重做上面提出的更改提出的解决方案。
按顺序回答您的问题:
- 根据GitHub的子模块purpose。在功能方面,它被设计为概念化的子存储库(几乎可以像任何其他文件一样对待),即版本由父存储库控制,其中 父跟踪子模块的当前提交 ID (子存储库)而不是它的内容.
- 这很可能是因为您已经将文件添加到存储库的索引中。在这种情况下,解决方案是
git rm --cached submodule-name/
。然后创建一个中间提交,然后添加文件夹 作为存储库 :git add submodule-name
(注意没有尾部斜杠 在子模块的子模块名称之后)。 - 是:)
您提到的 answer 也可能更正您的提交历史记录:
- 优点:
该文件夹将被视为所有提交历史记录中的子模块,而不仅仅是所有未来的提交。如果您检出到将其视为文件夹的先前版本,这可以避免任何并发症。这是一个复杂的问题,因为当你 return 到分支的顶端时,你可能还必须输入你的子模块并检出到最新的提交以恢复所有文件(可能从你的工作目录中删除)。这可以通过对您的最新提交进行某种递归检查来避免。
- 缺点:
如果提交历史被修改,所有其他贡献者也必须重新克隆项目,因为他们会遇到合并冲突或更糟;将问题提交重新引入项目。
None 这些解决方案似乎对我有用,所以我想出我自己的解决方案:
确保一个新的 git 存储库已经存在,它将保存内容 例如,我们将使用“git@github.com:/newemptyrepo”
导航到您正在模块化的目录:
cd myproject/submodule-dir
- 从父索引中删除 to-be 子模块:
git rm -r --cached .
- 在 to-be 子模块中初始化一个新的 git 仓库:
git init
- 为 to-be 子模块设置源并进行第一次提交:
git remote add origin git@github.com:/newemptyrepo
git add . && git commit && git push --set-upstream origin master
- 现在您必须导航到父存储库的 top-level 路径:
cd .. && cd `git rev-parse --show-toplevel`
- 最后,像往常一样添加子模块:
git submodule add git@github.com:/newemptyrepo ./myproject/submodule-dir
- 现在提交并推送上述命令所做的更改,一切就绪!