How to make an existing directory within a git repository a git submodule
I’m very confused about git-submodules.
Basically my problem is that I can’t make git understand that
~/main-project/submodule is a submodule.
I have good experience with git submodules:
in my dotfiles repository I created the .gitmodules file in
~/dotfiles-repo and I added there paths and urls. Since then, If I make changes to the files within the submodules and run
git status, I’d get something like like:
.vim/bundle/auto-complete (new commits) # in red
I created the
.gitmodules file in
- If I make changes to
~/main-project/submoduleand even push the changes, I don’t get a similar response like
<submodule> (new commits) # in redwhen running
~/main-project. I just get the changes that were made in those directories
When I hit the folders’ links at
githubfor these directories it’s not directing me to the repositories themselves but I stay in the same repository.
- Maybe I’m missing the whole point. What are the main features of submodules?
- Why does git understands the submodules in the dotfiles repo but not in my other repo?
- Is it because I have already told git to add the files inside
~/main-project/submoduleto the index?
I’ve read this question which led me to this answer But I’m not sure I need
git-subtree. I don’t want to do things that might do changes hard to be revert.
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
3 Solutions collect form web for “How to make an existing directory within a git repository a git submodule”
There is basically no better way than pretending to start over:
- ensure that everything is committed everywhere
- move your sub-repository out of the way
git submodule addfrom the sub-repository’s remote
git fetch ../wherever/you/stashed/the/sub-repository/in/step-1
git merge FETCH_HEAD
To explain why this is so, it seems to me than a deeper understanding of what submodules are is needed, than what one can glean from the
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, but since that post is a bit lengthy I am taking the liberty summarize them here.
At a low level, a git submodule consists of the following elements,
- A commit object at the top of the submodule tree,
- (In recent versions of Git) A subdirectory in
.git/modulesto host the Git objects for the submodule,
- An entry in the
The commit object is contained (or more precisely, referenced by SHA1) in the parent tree object. This is unusual, as things 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 with
git ls-tree to observe this commit object in more detail.
The subdirectory in
.git/modules stands in for a
.git subdirectory in the submodule; and in fact, there is a
.git file in the submodule that points to the former using a
gitdir: line. This is the default behavior since version 1.7.8 of Git. Not sure why everything wouldn’t Just Work if you just kept on having a separate
.git directory, except as noted in the release notes you would probably run into trouble when switching between a branch that has the submodule and another that doesn’t.
.gitmodules file provides the URL that
git submodule update --remote and friends should pull from; which obviously is distinct from the main repository’s set of remotes. Note also that
.gitmodules is copied in part into
.git/config by the
git submodule sync command and other commands that invoke it behind the scenes.
While it is fairly easy to do the necessary changes by hand for
.git/config, and also for
mysubmodule/.git (and in fact, there is even
git submodule absorbgitdirs for the latter), there isn’t really a porcelain to create only the in-tree commit object. Hence the proposed solution by moving + redoing changes presented above.
The Solution is quite simple. It was extracted from here.
git rm submodule-dir
This will delete all the files that git was tracking after in
rm -rf submoduledir
This will delete all the other files that might have been left in
submodule-dirbecause git ignored them.
- Now, we have to commit in order to remove the files from the index:
After the commit, we cleaned the files that git followed and didn’t followed in
Now it’s time to do:
git submodule add <remote-path-to-submodule>
This will re-add the submodule but as a true submodule.
- At this point it might be a good idea to check
.gitmodulesand see if the submodules have been added successfully. In my case I already had an
.gitmodulesfile so I had to modify it.
Since v2.12.0-rc0, and after this commit, We received
git submodule absorbgitdirs and It’s exactly what I needed at the time I posted this question.
This is what the docs state this command does:
If a git directory of a submodule is inside the submodule,
move the git directory of the submodule into its superprojects
$GIT_DIR/modulespath and then connect the git directory and
its working directory by setting the
a .git file pointing to the git directory embedded in the
superprojects git directory.
So instead of starting all over as suggested in the previous answers by @DomQ and myself, one can just add run the following:
- Without removing from the index the submodule, Add the submodule’s url to
git submodule add <url> <path>
- Move the submodule’s
.gitin regular repositories) to
git absorbgitdirs <path>
To answer your questions in order:
- The purpose of submodules according to GitHub. Feature wise, it has been designed to be conceptualized a child repository (which can almost be treated like any other file), that is version controlled by a parent repository, where the parent tracks the current commit ID of the submodule (child repository) rather than it’s content.
- It’s most likely because you’ve already added the files to the index of the repository. In which case, the solution is to
git rm --cached submodule-name/. Then create an intermediate commit, followed by the adding of the folder as a repository:
git add submodule-name(notice that there is no trailing slash after submodule-name in the case of submodules).
- Yes 🙂
The answer you mentioned may correct the history of your commits as well:
That folder will be treated as a submodule in all of your commit history, not just all future commits. This avoids any complications if you checkout to a previous version where it was treated like a folder. This is a complication because when you return to the tip of your branch, you may have to enter your submodule also and checkout to the latest commit to restore all the files (which may be deleted from your working directory). This could be avoided by doing some kind of a recursive checkout to your latest commit.
If the commit history is modified, all other contributors would also have to re-clone the project since they will get merge conflicts or worse; reintroduce the problem commits back into the project.