Search code examples
gitgit-submodules

How does one git submodule add a specific commit and have it be recorded in the .modules files?


Ideally I'd like the .modules file to have the exact commit I want to use and not change it (unless I tell it to, e.g. with git submodule --init <path2submodule_repo> --remote). But the git submodule add comment doesn't seem to provide an option (so I assume the .gitmodules can't do it?) e.g. see man page:

NAME
       git-submodule - Initialize, update or inspect submodules

SYNOPSIS
       git submodule [--quiet] [--cached]
       git submodule [--quiet] add [<options>] [--] <repository> [<path>]
       git submodule [--quiet] status [--cached] [--recursive] [--] [<path>...]
       git submodule [--quiet] init [--] [<path>...]
       git submodule [--quiet] deinit [-f|--force] (--all|[--] <path>...)
       git submodule [--quiet] update [<options>] [--] [<path>...]
       git submodule [--quiet] set-branch [<options>] [--] <path>
       git submodule [--quiet] set-url [--] <path> <newurl>
       git submodule [--quiet] summary [<options>] [--] [<path>...]
       git submodule [--quiet] foreach [--recursive] <command>
       git submodule [--quiet] sync [--recursive] [--] [<path>...]
       git submodule [--quiet] absorbgitdirs [--] [<path>...]

DESCRIPTION
       Inspects, updates and manages submodules.

       For more information about submodules, see gitsubmodules(7).

COMMANDS
       With no arguments, shows the status of existing submodules. Several subcommands are available to perform operations on the submodules.

       add [-b <branch>] [-f|--force] [--name <name>] [--reference <repository>] [--depth <depth>] [--] <repository> [<path>]
           Add the given repository as a submodule at the given path to the changeset to be committed next to the current project: the current
           project is termed the "superproject".

           <repository> is the URL of the new submodule’s origin repository. This may be either an absolute URL, or (if it begins with ./ or ../),
           the location relative to the superproject’s default remote repository (Please note that to specify a repository foo.git which is located
           right next to a superproject bar.git, you’ll have to use ../foo.git instead of ./foo.git - as one might expect when following the rules
           for relative URLs - because the evaluation of relative URLs in Git is identical to that of relative directories).

           The default remote is the remote of the remote-tracking branch of the current branch. If no such remote-tracking branch exists or the
           HEAD is detached, "origin" is assumed to be the default remote. If the superproject doesn’t have a default remote configured the
           superproject is its own authoritative upstream and the current working directory is used instead.

           The optional argument <path> is the relative location for the cloned submodule to exist in the superproject. If <path> is not given, the
           canonical part of the source repository is used ("repo" for "/path/to/repo.git" and "foo" for "host.xz:foo/.git"). If <path> exists and
           is already a valid Git repository, then it is staged for commit without cloning. The <path> is also used as the submodule’s logical name
           in its configuration entries unless --name is used to specify a logical name.

           The given URL is recorded into .gitmodules for use by subsequent users cloning the superproject. If the URL is given relative to the
           superproject’s repository, the presumption is the superproject and submodule repositories will be kept together in the same relative
           location, and only the superproject’s URL needs to be provided. git-submodule will correctly locate the submodule using the relative URL
           in .gitmodules.

So is the only way to record the commit manually in a bash script like this:

git submodule add -f --name coq-projects/metalib https://github.com/plclub/metalib.git coq-projects/metalib
git submodule foreach -q --recursive 'git switch $(git config -f $toplevel/.gitmodules submodule.$name.branch || echo master || echo main )'

but obviously edited so it works with commits? e.g.

git submodule add -f --name coq-projects/metalib https://github.com/plclub/metalib.git coq-projects/metalib
Run git submodule foreach git checkout <commit-hash> to change the checked-out commit in each submodule to the desired commit. Replace <commit-hash> with the hash of the desired commit.

Specifying the commit in the URL doesn't for git submodule add

But it does work if you git add it (either way I suppose, with our without the commit in the url since it will fetch the wrong one anyway) cd and the fetch the right commit:

(iit_synthesis) brando9~/proverbot9001 $ git submodule add -f --name coq-projects/metalib git+https://github.com/plclub/metalib.git#104fd9efbfd048b7df25dbac7b971f41e8e67897 coq-projects/metalib
Reactivating local git directory for submodule 'coq-projects/metalib'.

...

(iit_synthesis) brando9~/proverbot9001/coq-projects/metalib $ cd coq-projects/metalib
-bash: cd: coq-projects/metalib: No such file or directory
(iit_synthesis) brando9~/proverbot9001/coq-projects/metalib $ git checkout 104fd9efbfd048b7df25dbac7b971f41e8e67897
Note: switching to '104fd9efbfd048b7df25dbac7b971f41e8e67897'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:

  git switch -c <new-branch-name>

Or undo this operation with:

  git switch -

Turn off this advice by setting config variable advice.detachedHead to false

HEAD is now at 104fd9e Sync Makefile coq version with README/Docker
(iit_synthesis) brando9~/proverbot9001/coq-projects/metalib $ git status
HEAD detached at 104fd9e
nothing to commit, working tree clean

Solution

  • The Git Submodule is tracked under a Git repo, so it consists of handling its Git Subproject Commit in it. Just enter the Submodule directory, checkout to whatever detached commit, revision or tag you want, and go to the main repo root again and do a Git diff, it will show you a commit hash diff for the Submodule; commit that out with a description like: "Update Subproject Commit for X to tag Y".

    After that, when you want to clone your repo, you will clone recursively with the recurse-submodules flag.

    cd <Submodule path>
    git checkout <hash/tag/branch/etc>
    cd - 
    
    git status
    git add <Submodule>
    git commit "Update Subproject Commit for X to tag Y"
    git push
    

    Then, when cloning, clone this way:

    git clone --recurse-submodules <my repo with Submodules>
    

    Hope it helps.