Carlo Wood
Carlo Wood

Reputation: 6791

How to add and commit a submodule in the parent project without resetting the state of other submodules?

I am trying to write a script that checks out 'master' in all submodules.

Normally, after a clone, all submodules are in a detached state. I want them to be on a branch however. So my script visits each submodule and under certain conditions checks out a branch.

Roughly speaking, that comes down to say

git submodule foreach --recursive 'git checkout master'

After such a checkout I also try to fast-forward them. The submodule's HEAD (master) might no longer be equal to the recorded sha1 in the parent project. If that is the case, the script also does

git add path/to/submodule
git commit -m 'Automatic update of submodule'

in the parent (immediately following the 'git checkout master; git merge --ff-only' in the submodule).

That is... that would also commit anything that was added to the index already and I don't want that: I ONLY want to commit this 'add' of path/to/submodule.

I tried to solve this by wrapping the above in a git stash push/pop:

git stash save Automatic stash of parent project by update_submodules.sh
git add "$path"
git commit -m "Updating submodule reference to current HEAD of branch $submodule_branch of $name"
git stash pop

However, the git stash puts all submodules in a detached state again! And the git stash pop does NOT restore that :/

How can I achieve what I want?

EDIT

The answer of torek was used in the final update_submodules.sh script at line 47. It should be called like this.

Upvotes: 1

Views: 201

Answers (1)

torek
torek

Reputation: 488183

Use the otherwise rather specialized git commit --only.

What git commit --only does is:

  1. create a temporary index1 and read the existing HEAD commit into that temporary index, so that HEAD and this temporary index match;
  2. git add the specified arguments to the temporary index;
  3. make a commit from the result;
  4. arrange that the regular index also has those git add-ed by the time git commit --only finishes.

So, the new commit—which is now HEAD; the commit that was HEAD is now HEAD~1 or HEAD^ (whichever syntax you prefer: both mean the same thing)—is exactly the same as the old one except for the files listed after --only. If some stuff was staged, that same stuff is still staged. Meanwhile the (main) index is also now updated to account for the fact that the HEAD commit has new versions of the specified files.

(In this case the "files" are just the gitlinks, or the one gitlink for the one submodule.)


1It actually creates two temporary index files. For the complete and gory details, see my answer to Calling git in pre-commit hook.

Upvotes: 2

Related Questions