-1

I am running git on windows 8.1, and pulling from upstream using the following batch script:

git pull --recurse-submodules
git submodule update --recursive --remote --init --merge
git submodule foreach "git pull || true"

I know that it is redundant, but trial and error showed that sometimes any one of the above commands would not fetch something that the others would, so after battling with it for a while, and reading different answers on SO, I gave up and went with the "belt and suspenders" approach.

This worked reasonably well for about a year, until a week ago I updated to git version 2.18.0.windows.1

Since then, almost every invocation results in a detached head in some of the submodules. I go into the problematic ones, git checkout the correct branch, do a git pull from inside the submodule and everything looks fine, but if I try to run the script again for in main repo, BAM! same submodules get into a detached head state even if nothing changed upstream and nothing was pulled.

The only thing I am aware of that changed is the git version.

What am I doing wrong and what is the right way?

Edit:

The project is set up, submodules are used for files that are shared between multiples repos, and those files are changed regularly by developers (no, I don't have a say in it).

What I am trying to accomplish is to get the latest commits for the repositories on their checkoed-out branches, and then update the superproject pointer and push to origin.

Edit 2:

Will the following do what I intend above?

git pull
git submodule foreach "git pull || true"
Alex O
  • 1,429
  • 2
  • 13
  • 20

1 Answers1

2

Submodules are supposed to be detached, pretty much at all times (though there are a few specific exceptions). If they weren't before, that was what was wrong.

Note that git submodule update --remote --merge is essentially equivalent to:

(cd $submodule; git checkout $superproject_hash; git fetch ${remote};
 target_hash=$(git rev-parse $remote/$branch; git merge $target_hash)

That is:

  1. Make sure the submodule is in detached HEAD mode, at the commit commanded by the superproject.
  2. Fetch from the submodule's remote (usually its origin), so that we obtain new commits and update $remote/$branch, where $branch is the branch recorded in the superproject.
  3. Discover the hash ID of the updated remote-tracking name $remote/$branch (updated by the fetch we just ran).
  4. Run git merge, on the current detached HEAD, to either fast-forward the current HEAD to the target commit, or make a true merge with the target commit, resulting in a new commit. Either way HEAD remains detached, pointing to the commit—the new one just made, if we made a new one, or the target commit if we were able to fast-forward.

Edit: you can also use git submodule update --remote without --merge, which is essentially equivalent to:

(cd $submodule; git fetch $remote; git checkout $remote/$branch)

i.e., switch the detached HEAD of the submodule to the current remote-tracking name after updating the remote-tracking name using git fetch. This may be closer to your intended work-flow, though if git merge uses fast-forwarding, it comes out exactly the same.


The general idea is that the submodule's commit hash is fully determined by the superproject and the superproject alone. This calls for them being in detached-HEAD mode. At some specific times, you might want to update a submodule to some other commit; once you do, that commit, and that commit alone, is the correct commit, so you will record any and all new hash IDs in the superproject, and the submodules will again be in detached HEAD mode on these specific commits, and only these specific commits—which continues to call for them to be in detached-HEAD mode.

(There's a moment when you are in the submodule, updating away, that you might want to be, temporarily while no one is watching, secretly, briefly, on a branch ... but, whew, let's get back into detached-HEAD mode quick before anyone sees! That's Git's attitude towards submodules. If you want to develop in that submodule, well, Git won't stop you, but that's only for consenting adults behind closed doors!)

torek
  • 448,244
  • 59
  • 642
  • 775
  • Thank you for the informative answer, but this is not what I am trying to accomplish. I apologize for being unclear, please see the update to the question. – Alex O Sep 10 '18 at 15:04
  • Unfortunately, it's likely that what happened is that your previous Git had a bug that left the submodules on a branch (which you wanted, but Git isn't supposed to be doing) and the bug has been fixed. Note that if you want sub-repositories that *always* stay on branches, the way to do that is by *not* using submodules, and this is not something Git supports directly. – torek Sep 10 '18 at 15:21
  • Nonetheless, using `git submodule update --remote` (with or without `--init` and with or without `--merge`—I'd recommend the *without* mode, at least initially) should bring the submodule to a detached-HEAD *at the current remote-tracking name head after fetching*. You can then test the superproject and if all is well, `git add -A` and `git commit` to make a new commit that freezes everything at the given detached HEADs. That's the intended way of working with submodules. – torek Sep 10 '18 at 15:24