Search code examples
gitgit-submodules

Easiest way to get `svncopy --pin-externals`-like behaviour with Git submodules


Currently my project uses Git submodules in a detached HEAD way. In context of a regular development workflow, it would much more convenient to point submodules to the corresponding remote branches' tips (git submodule add -b master ...) so the freshest changes in submodules are taken into account automatically. But when it comes to creating tags, which must remain constant over time, submodule links must be fixed on those specific commits that are used when a tag is being created.

In SVN, this fixation can be achieved just by adding the --pin-externals argument to a tag creation command, but it looks like Git has no direct equivalent for it. What is my best bet here?


Solution

  • TL;DR: there's nothing to do here. All superproject commits always direct everything to detached HEADs. The -bs are not used when committing, nor when git adding, only when git submodule update --remoteing, and even then you're still on a detached HEAD, just a different one.


    In Git, submodules are just separate Git repositories. A superproject commit always records the one specific hash to which each submodule will be forced. So if you've made a superproject commit with hash a123456, for instance, the superproject's a123456 already pins each submodule: submodule sub/A is fixed forever (for this commit) onto, say, aaaaaaaa and submodule moduleB is fixed forever (for this commit) onto bbbbbbb. When you go back to commit a123456 in the superproject and run git submodule update, sub/A will be detached to sub/A's aaaaaaaa and moduleB will be detached to bbbbbbb.

    To un-detach the submodules, you must cd sub/A; git checkout master and cd moduleB; git checkout master. You can semi-automate this using git submodule update --remote, which uses the recorded branch, but what that actually does is not switch sub/A to (its) master but rather to find out what sub/A's remote's origin/master is, by hash ID, and switch sub/A to that commit as a detached HEAD. So sub/A will continue to be in detached HEAD mode.

    Using git add in the superproject just takes the current HEAD (usually detached, but even if not, the actual hash ID as reported by git rev-parse) from each submodule and records it in the superproject's index, so that the one hash ID will be in the next superproject commit. The branch name in the submodule is not really important here. The only use of those -b masters in git submodule add is for git submodule update --remote, and even then it's not really to make the submodule be on a branch, it's just to automatically update the submodule's detached-HEAD.