Firstly, you need to know what a commit is and how it looks like. From Wikipedia:
A commit object links tree objects together into a history. It contains the name of a tree object (of the top-level source directory), a time stamp, a log message, and the names of zero or more parent commit objects.
Every commit in the repository is different, has a unique identification (or simply a hash). You can get this id with command
git log. However, every commit has a long id or short (
git log --pretty=format:%h), on the remote server (Bitbucket, Github) you will see rather short than long id. Both of them are unique, so you can use them interchangeably.
So with every command
git commit you create a unique snapshot of the current code in your repository. And this is important. You probably know that you can do
git checkout to another branch, but also you can check out to a specific commit. To do that, simply run
git checkout commit_hash. And from this point, you can create new branches, do commits and other commands which you normally do when you are on a branch.
What is a git submodule?
Git submodule is a normal repository with all the features that comes with git. And it’s very important to remember this.
How to add git submodule?
With a simple command
git submodule add [<options>] [<repository>]. It’s very similar to command
git clone so you can add the repository by ssh or https, define specific folder etc. That command will create an empty directory and add proper options to file
.gitmodules. Why is the directory empty? Because submodule wasn’t initialized.
To get the code from the submodule you need to run
git submodule update. It will download all files from the repository. Which branch? As it was said, It’s similar to the clone command, so you will have HEAD from the master branch. When you will push changes to the remote server, you will have a directory with submodule and hash of the last commit fro the master. When you will click on it, it will redirect you to the submodule repository. What is most important, the parent repository is storing only the hash commit of the submodule repository.
How to make changes to the submodule?
If you will change some files on the submodule and you will run
git status, you will see “modified content”, but you cannot add this to the stage. Why? Because parent repository sees, that something has changed, but the hash of the submodule hasn’t changed. You need to go to the submodule directory and create commit. It is important to push this commit to the server, without it, you will be the only person that have this commit on the local machine. After this push nothing will change in the parent repository. Go back to it and run
git status. You will see, that submodule has
new commits. At this moment you can add submodule to the stage and create new commit.
What is important?
You need to remember that changes made on the submodule don’t trigger changes on the parent repository. You need to push separately commits from both repositories. There is also possibility to have submodule in the submodule. In that case you can use
--recursive flag for
git submodule update command. Also, after someone will clone your repository with submodules, he/she should use
git submodule update --init to initialize and download submodule code.
To get familiar with git submodules, I’ve prepared some exercises, which can be done in 10 minutes:
- Create repository (hereinafter referred to as REPO1), add some files, push them to the remote server (github/bitbucket).
- Create another repository (hereinafter referred to as REPO2), add some files, push them to the remote server (github/bitbucket).
- Add REPO2 as a submodule in REPO1 in src/common directory (all files from the REPO2 should be in src/common directory, not src/REPO2), push changes to the server.
- Clone REPO1 to another directory (hereinafter referred to as REPO1Copy), initialize submodules (hereinafter referred to as REPO2Copy).
- Modify file in REPO2 on the new branch (repo2-test-branch), push changes to the server, create new branch in REPO1 (repo1-test-branch), push changes to the server, create Pull Requests for both branches, check how they look like.
- Checkout to repo1-test-branch branch in REPO1Copy, pull changes from submodules.
- Modify file in REPO1 in branch repo1-test-branch, push changes to the server.
- Checkout to the first commit in REPO2, commit changes in REPO1 and push changes to the server.
- In REPO1Copy pull changes from the server.
- Checkout to master branch in REPO1, update submodules.
- Merge repo1-test-branch to master branch (through Pull Request), pull changes from remote master in REPO1 and REPO1Copy (observe, that you don’t need to merge repo2-test-branch to master branch, to have actual changes in the parent repository).
That’s it! Have fun with submodules.