Search code examples
gitgit-submodules

Why is git submodule not updated automatically on git checkout?


When switching branches with git checkout I would assume that most of the time you would want to update your submodules.

  • In what situation do you not want to update submodules after switching?
  • What would break if this was done automatically by git checkout?

Updated with example:

  • Branch A has submodule S at 3852f1
  • Branch B has submodule S at fd72d7

On branch A, git checkout B will result in a working copy of branch B with submodule S at 3852f1 (with a modified S). git submodule update will checkout S at fd72d7.


Solution

  • git checkout --recurse-submodules was added to git 2.13

    This is mentioned on the release notes at: https://github.com/git/git/commit/e1104a5ee539408b81566066aaa6963cb87d5cd6#diff-c24776ff22455a30fbb78e378b7df0b0R139

    submodule.recurse option was added to git 2.14

    Set as:

    git config --global submodule.recurse true
    

    man git-config says:

    Specifies if commands recurse into submodules by default. This applies to all commands that have a --recurse-submodules option. Defaults to false.

    I feel that not updating modules by default is a bad Git default behavior that goes against most user's expectations and limits the adoption of submodules, I really wish the devs would change it.

    submodule.recurse makes git fetch fetch all submodules every time

    This makes the option basically unusably slow, because the fetch happens even when the submodules are up to date, as it tries to fetch an branch updates from those submodules.

    I do a lot of fetching to see my co-workers commits so I have to fetch often. And submodules tend do move much more slowly compared to the toplevel repo, so fetching them all the time is not necessary.

    So for now I stick to the Bash function workaround:

    gco() { git checkout --recurse-submodules "$@" }
    

    This workaround is not perfect as there are other commands that can change commit, e.g. git rebase, so you end up having to define multiple aliases. I don't have a better solution for this right now:

    gsuu() {
      git submodule sync --recursive &>/dev/null
      git submodule update --init --recursive --progress "$@"
    }
    grb() {
      git rebase "$@"
      gsuu
    }