How do we check out a git commit including submodules as they were at that time?
One reason why we might want this is to look at a previous version of the main program for which we need to rebuild it with the submodules in the version that was used at the time of the commit.
Given this, we could even use this in regular workflow:
git submodule update --remote --merge
, then try to build to see if the program can work with the newest version of all submodules. We can kind-of do it by manually looking at each submodule: which commit had the appropriate timestamp (and hope that the program used the then-most-uptodate version). It would be much better if we could see commit X of the program used submodule commit Y. And check those out for each submodule.
Pulling with submodules. Once you have set up the submodules you can update the repository with fetch/pull like you would normally do. To pull everything including the submodules, use the --recurse-submodules and the --remote parameter in the git pull command .
When it points to a branch, Git doesn't complain, but when you check out a commit, it switches into a “detached HEAD” state. The point is, your development should always take place on a branch—never on a detached HEAD . This makes sure you always have a reference to your new commits.
A git submodule is a record within a host git repository that points to a specific commit in another external repository. Submodules are very static and only track specific commits. Submodules do not track git refs or branches and are not automatically updated when the host repository is updated.
git submodule foreach git pull origin master or git pull origin master --recurse-submodules is what you want if you intend to update each submodule to the latest from their origin repositories. Only then will you get pending changes in the parent repo with updated revision hashes for submodules.
Using the --recurse-submodules flag of git checkout can also be useful when you work on several branches in the superproject, each having your submodule pointing at different commits.
By default, the git pull command recursively fetches submodules changes, as we can see in the output of the first command above. However, it does not update the submodules. This is shown by the output of the git status command, which shows the submodule is “modified”, and has “new commits”.
In that case, it is possible for git pull --recurse-submodules, or git submodule update, to fail if the superproject references a submodule commit that is not found in the submodule remote locally configured in your repository. In order to remedy this situation, the git submodule sync command is required:
Step 1: Clone the repository or fetch all the latest changes and commits. Step 2: Get the commit ID (SHA) that you want to checkout. From your local repository, you can get the commit SHA from... Step 3: Copy the commit (SHA) id and checkout using the following command. git checkout... Step 4: If ...
In this case, you just need to run git submodule update --checkout
(no --merge
, no --remote
) after checking out a previous commit.
There is a lot of confusion around submodules. The basics are actually fairly simple though:
git clone
—for each of its submodules in the .gitmodules
file.This has the effect of "freezing" the appropriate submodule commit into each superproject commit. It is—or was originally—intended to manage third party code, where the submodule itself changes rarely compared to the superproject.
This model is not at all flexible, and is not suitable for the way many people want to use submodules, which is to keep them at the tip of some branch. So submodules grew the ability to update to branch names, or to be worked-in and have the work rebased and/or merged. These new abilities spawned the submodule.name.update
configuration entries and git submodule update --remote
options.
If you have not configured any of these items, git submodule update
alone will check out the desired (recorded) submodule commits for each submodule recorded in the current, i.e., HEAD
, commit of the superproject. If you have configured some of these, you can use git submodule update --checkout
to override the configuration and cause a git checkout hash-id
in each submodule. Note that adding --force
makes Git do this submodule checkout even if the HEAD
is already at that entry. But since each submodule is its own Git repository, the submodule's checkout has its own interaction with its own (per-repository / per-work-tree) index and work-tree.2
Again, every submodule is its own Git repository, which means a submodule of the current superproject may have submodules of its own. If so, this makes the submodule a superproject as well, and this is where the --recursive
flag comes in. If you are not nesting submodules, none of this complexity will affect you.
1In other words, the index for the superproject has an entry for each submodule. The type of this index entry is "gitlink", which stores the SHA-1 read from HEAD
in the submodule. These gitlink entries are treated as sort of a weird cross between a symlink and a directory.
2In other words, if you have manually entered one of the submodules and modified the index and/or work-tree, the git checkout
run inside that submodule, if any, may still carry your modifications into the new checked-out commit.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With