First, let's try to make some things here clear. It's hard because when you work with Git on your own, it's complicated. When you add a second Git repository–to which you git push
your own commits, and from which you git fetch
other people's new commits—that is complicated too. A submodule is nothing more than a third Git repository, and that third Git repository has a fourth Git repository to which you might perhaps git push
your own commits, and from which you might git fetch
other people's new commits. So we're leaping right into a situation in which there are at least four Git repositories, all of which are somewhat independent of each other.
We could try to draw pictures but even pictures will be a bit muddy. How do we keep four repositories straight? Git has names for two of them: the two that you work with directly, yourself. One is the superproject, which is where you are running git diff
and seeing:
diff --git a/submodule-path b/submodule-path
index 5a8162f..2ff89a2 160000
--- a/submodule-path
+++ b/submodule-path
@@ -1 +1 @@
-Subproject commit 5a8162ff9a602deb96956854346988e1ee45672e
+Subproject commit 2ff89a2bfcaa014885a70b0da86e997ecd8d0688
The other is the submodule itself: if you cd submodule-path
and run various Git commands, you'll see that it is an ordinary Git repository. The only thing unusual about it is that it is almost always in "detached HEAD" mode.
Your superproject Git probably has an origin
. That's a repository—well, technically, a short name you can use in this Git to refer to another repository—to which you can git push
commits you make in the superproject. You will need to make a commit in the superproject and then git push
this new commit to origin
. What precisely goes into this superproject commit? We'll see in a moment.
Your submodule Git also has an origin
. That's a different other Git repository: the fourth Git in this not-very-well-drawn-at-all picture. It's not clear to me whether you want to send commits to that fourth repository. You do presumably want to get a commit from that repository. There are multiple ways to go about this, including using git submodule update --remote
, perhaps with additional options. I prefer to just cd submodule-path
and start working directly in the submodule Git as this reduces the problem to things you already know how to do: manipulate one local Git repository based on new commits that have appeared in its origin
.
Supposing you just want to pick up some new commit(s) in the submodule
If this is the case, you can:
cd submodule-path # begin working in your submodule
git fetch # update origin/*
git checkout origin/master # get a detached HEAD on the desired commit
# (this assumes `origin/master` is the
# desired commit; it's impossible for me
# to know which commit you desire)
and now the submodule has the desired commit as its detached HEAD. There is nothing to git push
from here: all the commits that reside in this repository are commits obtained from this submodule's origin
's Git.
(Using git submodule update --remote
may do the cd submodue-path
and git fetch
and git checkout origin/master
for you, all without changing your own working directory. The whole operation runs in its own sub-shell so that none of these cd
operations affect where you are. That seems to be what your git submodule update --remote --merge
did: there was no need to make a new merge commit, so it just switched to the commit identified by some branch name on origin
.)
But if you need to make new commits in the submodule ...
In this case, you may want to force the submodule to be on a branch, so that you're working in a more normal work-flow. Then you'd probably want to git checkout master
for instance, and then do various commands. Eventually you might end up with a new commit made in your submodule repository, which you will need to git push
to the origin
repository of the submodule, so that other people will be able to obtain this commit too.
You can leave the submodule on its branch. The submodule's branch is irrelevant to the superproject Git: the superproject Git cares only which commit is checked out in the submodule. (That's why in the earlier case above, we can just switch the detached HEAD around.)
Now that the submodule Git is on the right commit, you must make a new superproject commit
At this point, you can cd
back out of the submodule into the superproject. You'll see in git diff
output something exactly like you quoted above, and git status
will say:
modified: submodule-path (new commits)
This doesn't necessarily mean there are any new commits in the submodule repository that aren't in the submodule repository's origin
. It just means that the submodule repository is on (as its HEAD, detached or not) a commit that is not the commit that the current superproject status says it should be on.
The problem here is that the current superproject commit is, in a way, defective. It was correct, but it isn't any more, just as the current superproject commit would be defective if you had, say, edited the file README.md
locally. This means that you need to make, in the superproject, a new, corrected commit. The superproject Git will make the new commit from whatever is in the index of the superproject repository, so you now need to update the index.
If you'd changed a README.md
file, the way you'd update the index is:
git add README.md
But what you changed was not a README.md
file. It was, instead, the submodule hash ID. So you need to record the new hash ID in the index. The way you do that is:
git add submodule-path
This takes the hash ID from the submodule by running cd submodule-path; git rev-parse HEAD
to gets the raw hash ID—the one that showed up in the git diff
—and stuff that hash ID into the index. Now git diff
—which compares the index to your work-tree—won't show these Subproject commit
lines any more, but git diff --cached
—which compares the current (superproject) commit to the index—will show them. Now git status
will say that these "new commits" are ready to be committed, rather than not-yet-staged-for-commit.
You can git add
any other superproject files (if there are any that need to be updated in the index) at this time. Then:
git commit
in the superproject will make a new commit that will record the hash ID you put into the superproject's index when you ran git add
on the submodule path.
You're (locally) done with the update, but here's some things to think about
Note that every commit in the superproject records the hash ID in the submodule. Every time you git checkout
a different superproject commit, that not only extracts the right superproject files into the (superproject) index and your (superproject) work-tree, it also extracts the recorded submodule hash ID into the (superproject) index. It does not, by default, cd
into the submodule and git checkout
that particular commit by its hash ID. You can change this with a setting, or add --recursive
to git checkout
; or you can just run git submodule update
, which tells your Git to cd
into each submodule, one at a time, and git checkout
the hash ID currently recorded in the (superproject) index.
You will, at some point, need to git push
the new commit you made in your superproject, to the origin
(of the superproject), so that the new commit with its newly recorded hash ID appears in the Git over at the origin
of your superproject. You can do this whenever you like—but suppose you did make new commits in your submodule, and you have not yet used git push
in the submodule itself to send those new commits to the origin
of the submodule. In this case, the new commit you made in the superproject records the hash ID of a commit that only exists in your local submodule repository. If someone runs git fetch
to the superproject Git's origin
, they'll get this new hash ID from the new commit you sent, but not be able to find that commit in their clone of the submodule. So if you did make new submodule commits, it's usually best to git push
them first, then git push
the new superproject commits.
(If you did not make any new submodule commits, there's no problem here.)
git submodule update -f --init
, but it resets to first commit, but I need the last. – Poynterupdate
subcommand just forces the submodule back to the commit recorded in the superproject. But there isn't necessarily a single "last" commit in any Git repository. I'll write something up, but remember, submodules are inherently complicated. – Embryologist