I'm interested in the specific case where you manually place one Git repository inside another Git repository (either by using git clone
or by copy-pasting in the filesystem). I've noticed the following things happen when you do this:
- Git will save a pointer to a specific commit, like with a submodule.
- Git will not allow you to modify the contents of the subproject from within the superproject, like with a submodule.
-
git clone
on the superproject will not copy the subproject contents, like with a submodule. -
git submodule
will not work. It complains about .gitmodules not being there.
I would like to know:
- What does .gitmodules do? It's absence is the only obvious difference between a manual copy like this and a true submodule.
- Is there any other difference I don't know about?
- Does this manually-copied state have a special name?
- Is th开发者_StackOverflow中文版ere any way to override and/or work around effects 3 and 4? (IE: I want to render the resulting repository fully usable)
I think the only difference is a submodule has a relation with its parent. For instance, you can use --recursive
option when cloning to fetch all the submodules at a time. Otherwise, you'd have to fetch them all one by one.
I think (I am not sure) this is an implementation detail of submodules. If a repository in a repository is added (using git add), not the content of the directory is stored, but instead the hash of the commit of the HEAD is stored (as type commit and not as type tree or blob). This commit hash contains all information you need if you have the sub repository. But if you have only the super repository and the commit hash of the HEAD of the sub repository, you still need to get the sub repository. This information is stored in the .gitmodules file. If you don't use remotes at all, if you never clone, push or pull your super repository, you can actually use submodules without .gitmodules. Simply glone the sub repositories (e.g. 3rd party libraries) in your repository. This allows to keep track which versions of the 3rd party libraries where used for each commit in the super repository. But it does not allow to clone everything at once. The remotes of the sub repositories are stored only in the clones in the working tree. If you push the super repository (to a bare repository), the information where to get the sub modules from is lost.
To understand how this works cat-file
is a very helpful tool to take a look at the single git objects. A commit contains a message, author, comitter and usually a link to tree object:
$ git cat-file -p 218f61f0ae29941edcc594bde692145f7f1177cc
tree 916a626892af1cd59ff84c3b7d351fca12712806
author lumbric <not-real-adress@gmail.com> 1513291656 +0100
committer lumbric <not-real-adress@gmail.com> 1513291690 +0100
Commit message in the super repository
The tree object contains a list of other git objects:
$ git cat-file -p 916a626892af1cd59ff84c3b7d351fca12712806
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 a
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 b
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 c
040000 tree 765b32c65d38f04c4f287abda055818ec0f26912 folder_a
160000 commit 77e2f79e6401f28cab5cf4ca62bb0d58c945f823 sub_repo1
Folders are stored as tree objects (containing again a lits of git objects) and files as blob. A submodule is stored as type commit in a tree object. The contents can be found in the given hash in the sub repository.
From man git-submodule
:
Submodules are composed from a so-called gitlink tree entry in the main repository that refers to a particular commit object within the inner repository that is completely separate. A record in the .gitmodules (see gitmodules(5)) file at the root of the source tree assigns a logical name to the submodule and describes the default URL the submodule shall be cloned from. The logical name can be used for overriding this URL within your local repository configuration (see submodule init).
精彩评论