https://git-scm.com/book/en/v2/Git-Tools-Submodules
Submodules allow you to keep a Git repository as a subdirectory of another Git repository. This lets you clone another repository into your project and keep your commits separate.
Let's start by adding an existing Git repository as a submodule of the repository that we're working on.
$ git submodule add https://github.com/chaconinc/DbConnector DbConnector
$ git status
$ git diff --cached DbConnector
// If you want a little nicer diff output,
$ git diff --cached --submodule
// When you commit,
$ git commit -am 'added DbConnector module'
// Lastly, push these changes:
$ git push origin mastersubmodules will add the subproject into a directory named the same as the repository.
Issue with adding common code as git submodule: “already exists in the index”
// to unstage all of those files: $ git rm -r --cached <subproject-folder> // and then add the submodule with: git submodule add <url_to_repo> <subproject-folder>
clone a project with a submodule in it. When you clone such a project, by default you get the directories that contain submodules, but none of the files within them yet:
$ git clone https://github.com/chaconinc/MainProjectThe DbConnector directory is there, but empty. You must run two commands: git submodule init to initialize your local configuration file, and git submodule update to fetch all the data from that project and check out the appropriate commit listed in your superproject:
$ git submodule init
$ git submodule updateIf you pass --recursive to the git clone command, it will automatically initialize and update each submodule in the repository.
$ git clone --recursive https://github.com/chaconinc/MainProjectIf you want to check for new work in a submodule, you can go into the directory and run git fetch and git merge the upstream branch to update the local code.
$ git fetch
$ git merge origin/masterNow if you go back into the main project and run git diff --submodule you can see that the submodule was updated and get a list of commits that were added to it.
If you run git submodule update --remote, Git will go into your submodules and fetch and update for you.
$ git submodule update --remote DbConnector
$ git statusGit will by default try to update all of your submodules when you run git submodule update --remote so if you have a lot of them, you may want to pass the name of just the submodule you want to try to update.
let's go through an example of making changes to the submodule at the same time as the main project and committing and publishing those changes at the same time.
let's go into our submodule directory and check out a branch.
$ git checkout stable
Switched to branch 'stable'Here we'll see that there was a change on the server for this submodule and it gets merged in.
$ git submodule update --remote --mergeask Git to check that all your submodules have been pushed properly before pushing the main project. The git push command takes the --recurse-submodules argument which can be set to either “check” or “on-demand”.
If
checkis used, it will be checked that all submodule commits that changed in the revisions to be pushed are available on a remote. Otherwise the push will be aborted and exit with non-zero status.If
on-demandis used, all submodules that changed in the revisions to be pushed will be pushed. Ifon-demandwas not able to push all necessary revisions it will also be aborted and exit with non-zero status.
$ git push --recurse-submodules=on-demandgo into each subgitmodule and push to the remotes to make sure they're externally available and then try this push again for you.
$ git pull
$ git diff
diff --cc DbConnector
index eb41d76,c771610..0000000So, in this case,
eb41d76is the commit in our submodule that we had andc771610is the commit that upstream had.
You can either just try the merge with the SHA-1 directly, or you can create a branch for it and then try to merge that in. We would suggest the latter, even if only to make a nicer merge commit message.
So, we will go into our submodule directory, create a branch based on that second SHA-1 from git diff and manually merge.
$ cd DbConnector
$ git rev-parse HEAD
eb41d764bccf88be77aced643c13a7fa86714135
$ git branch try-merge c771610
(DbConnector) $ git merge try-merge
Automatic merge failed; fix conflicts and then commit the result.We got an actual merge conflict here, so if we resolve that and commit it, then we can simply update the main project with the result.
$ vim src/main.c (1)
$ git add src/main.c
$ git commit -am 'merged our changes'
$ cd .. (2)
$ git diff (3)
- Subproject commit eb41d764bccf88be77aced643c13a7fa86714135
-Subproject commit c77161012afbbe1f58b5053316ead08f4b7e6d1d
++Subproject commit 9fd905e5d7f45a0d4cbc43d1ee550f16a30e825a
$ git add DbConnector (4)
$ git commit -m "Merge Tom's Changes" (5)(1) First we resolve the conflict (2) Then we go back to the main project directory (3) We can check the SHA-1s again (4) Resolve the conflicted submodule entry (5) Commit our merge
There are a few things you can do to make working with submodules a little easier.
There is a foreach submodule command to run some arbitrary command in each submodule.
// stash all the work in all our submodules:
$ git submodule foreach 'git stash'
// create a new branch and switch to it in all our submodules:
$ git submodule foreach 'git checkout -b featureA'
// produce a nice unified diff of what is changed in your main project and all your subprojects as well:
$ git diff; git submodule foreach 'git diff'$ git config alias.sdiff '!'"git diff && git submodule foreach 'git diff'"
$ git config alias.spush 'push --recurse-submodules=on-demand'
$ git config alias.supdate 'submodule update --remote --merge'This way you can simply run
git supdatewhen you want to update your submodules, orgit spushto push with submodule dependency checking.
Assume that you have files in a subdirectory of your project, and you want to switch it to a submodule. If you delete the subdirectory and then run submodule add, Git yells at you:
$ rm -Rf CryptoLibrary/
$ git submodule add https://github.com/chaconinc/CryptoLibrary
'CryptoLibrary' already exists in the indexYou have to unstage the
CryptoLibrarydirectory first. Then you can add the submodule:
$ git rm -r CryptoLibrary
$ git submodule add https://github.com/chaconinc/CryptoLibraryNow suppose you did that in a branch. If you try to switch back to a branch where those files are still in the actual tree rather than a submodule – you get this error:
$ git checkout master
error: The following untracked working tree files would be overwritten by checkout:
CryptoLibrary/Makefile
CryptoLibrary/includes/crypto.h
...
Please move or remove them before you can switch branches.
AbortingYou can force it to switch with checkout -f, but be careful that you don't have the unsaved changes in there as they could be overwritten with that command.
$ git checkout -f master
warning: unable to rmdir CryptoLibrary: Directory not empty
Switched to branch 'master'Then, when you switch back, you get an empty
CryptoLibrarydirectory for some reason andgit submodule updatemay not fix it either. You may need to go into yoursubmoduledirectory and run agit checkout .to get all your files back. You could run this in asubmodule foreachscript to run it for multiple submodules.