Search code examples
bashgitgit-submodulesfish

howto find out which git submodule current directory belongs to


setup

i have a git repo located in /home/v/git_repo, in which i have a submodule localted in subdirectory ./a/b/c.

$ cat /home/v/git_repo/.gitmodules
[submodule "foo/bar"]
    path = a/b/c
    url = git@github.com:username/repo.git

having the full path or only the in-repository subpath (that i have implemented in helper script git-where-in-repo-am-i-currently)

$ pwd
/home/v/git_repo/a/b/c/something

$ git where-in-repo-am-i-currently
a/b/c/something

question

i want to find out (preferably in fish) which submodule this path belongs to: e.g

$ git which-submodule (pwd)
foo/bar

to later use it to query that submodules status like

$ git -C (git rev-parse --git-dir)/modules/(git which-submodule) status
on branch master
Your branch is up to date with 'origin/master'

and ultimately display this information in my prompt (that part is already implemented)

what i tried

parsing the output of

$ git -C (git rev-parse --show-toplevel) config --file=.gitmodules --get-regexp "path"`
submodule.foo/bar.path a/b/c

and comparing my sub-directory path to that of a submodule, but it was rather a mess, with splitting pathes into arrays and all kinds of hacks


Solution

  • For the usual setup you've described here, with the worktree nesting matching the submodule nesting, you can

    mytoplevel=`git rev-parse --show-toplevel`
    abovethat=`git -C "$mytoplevel"/.. rev-parse --show-toplevel`
    

    Then,

    echo ${mytoplevel#$abovethat/}
    

    will get you the submodule path in the superproject, or you can

    echo ${PWD#$abovethat/}
    

    to get your current directory's path relative to the superproject.

    So:

    me=`git             rev-parse --show-toplevel`
    up=`git -C "$me"/.. rev-parse --show-toplevel`
    subpath=${me#$up/}
    git -C "$up" config -f .gitmodules --get-regexp '^submodule\..*\.path$' ^$subpath$
    

    gets you the current repo's submodule name and path from the config entry in its superproject.


    Git can be useful in any imaginable build system, though; it doesn't impose restrictions on how things outside its remit are set up. So short of an exhaustive search of the filesystem namespace you can't be sure you've found everybody using any worktree as a submodule checkout, there's just no reason for Git to care how a repository is used.

    For instance, if multiple projects all need to run off the same submodule rev, you can have a single repo and worktree serve as a shared submodule for them all: rather than have to go through every single one of them and do synchronized checkouts, and then trusting that you haven't missed one, just use one repo, with one worktree, and point everybody using it at that.

    For workflows with that need, this can be compellingly better than the usual setup, all users by definition see a synchronized, current submodule revision and any client who needs to know "what's new" with an update can e.g. git -C utils diff `git rev-parse :utils` HEAD, every submodule user effectively has their own tracking branch and can use all of Git's tools to help stay current or resolve conflicts.


    So, to recreate your setup, I do:

    git init git_repo; cd $_
    mkdir a/b; git init a/b/c; cd $_
    mkdir something; touch something/somefile;
    git add .; git commit -m-
    cd `git -C .. rev-parse --show-toplevel`
    git submodule add --name foo/bar ./a/b/c -- a/b/c
    git add .; git commit -m-
    

    Then I get this when I try it:

    $ find -print -name .git -prune
    .
    ./a
    ./a/b
    ./a/b/c
    ./a/b/c/something
    ./a/b/c/something/somefile
    ./a/b/c/.git
    ./.gitmodules
    ./.git
    $ git grl
    core.repositoryformatversion 0
    core.filemode true
    core.bare false
    core.logallrefupdates true
    submodule.foo/bar.url /home/jthill/src/snips/git_repo/a/b/c
    submodule.foo/bar.active true
    $ cd a/b/c/something
    
    $ me=`git             rev-parse --show-toplevel`
    $ up=`git -C "$me"/.. rev-parse --show-toplevel`
    $ subpath=${me#$up/}
    $ git -C "$up" config -f .gitmodules --get-regexp '^submodule\..*\.path$' ^$subpath$
    submodule.foo/bar.path a/b/c
    
    $ echo $me $up $subpath
    /home/jthill/src/snips/git_repo/a/b/c /home/jthill/src/snips/git_repo a/b/c
    

    If there's a difference between this setup and what you've described, I'm missing it, I've got the directory structure, the submodule name, the start directory... if you'll step through that and find where the setup or results diverge from yours I think that'd help.