Today I Learned

tags


2023/07/30

Getting the top-level directory of the relevant git worktree

#!/usr/bin/env bash
# set up a dummy repo
set -euo pipefail
experiment_dir="$(mktemp -d --tmpdir test_repo.XXX)"
git clone --recurse-submodules git@github.com:SKalt/dummy_repo.git "$experiment_dir"
cd "$experiment_dir"
git pull --recurse-submodules=yes

git checkout -b brnch && git checkout - # set up a dummy branch
git worktree add wrktr brnch # add a wortree
cd wrktr &&  git submodule update --init --recursive # init the recursive submodules in the worktree
tree .
.
├── LICENSE
├── nested
│   └── file.txt
├── README.md
├── submod
│   ├── LICENSE
│   ├── nested
│   │   └── file.txt
│   ├── README.md
│   └── submod
│       ├── LICENSE
│       ├── nested
│       │   └── file.txt
│       └── README.md
└── wrktr
    ├── LICENSE
    ├── nested
    │   └── file.txt
    ├── README.md
    └── submod
        ├── LICENSE
        ├── nested
        │   └── file.txt
        ├── README.md
        └── submod
            ├── LICENSE
            ├── nested
            │   └── file.txt
            └── README.md

11 directories, 18 files
tree .git
./.git
├── branches
├── config
├── description
├── HEAD
├── hooks/...
├── index
├── info/...
├── logs/...
├── modules
│   └── submod
│       ├── branches
│       ├── config
│       ├── description
│       ├── HEAD
│       ├── hooks/...
│       ├── index
│       ├── info/...
│       ├── logs/...
│       ├── modules
│       │   └── submod
│       │       ├── branches
│       │       ├── config # <- contains relative path in core.worktree
│       │       ├── description
│       │       ├── HEAD
│       │       ├── hooks/...
│       │       ├── index
│       │       ├── info/...
│       │       ├── logs/...
│       │       ├── objects/...
│       │       ├── packed-refs
│       │       └── refs/...
│       ├── objects/...
│       ├── packed-refs
│       └── refs/...
├── objects/...
├── packed-refs
├── refs
│   ├── heads
│   │   ├── brnch
│   │   └── main
│   ├── remotes
│   │   └── origin
│   │       └── HEAD
│   └── tags
└── worktrees
    └── wrktr
        ├── commondir
        ├── gitdir
        ├── HEAD
        ├── index
        ├── logs
        │   └── HEAD
        ├── modules
        │   └── submod
        │       ├── branches
        │       ├── config
        │       ├── description
        │       ├── HEAD
        │       ├── hooks/...
        │       ├── index
        │       ├── info/...
        │       ├── logs/...
        │       ├── modules
        │       │   └── submod
        │       │       ├── branches
        │       │       ├── config
        │       │       ├── description
        │       │       ├── HEAD
        │       │       ├── hooks/...
        │       │       ├── index
        │       │       ├── info/...
        │       │       ├── logs/...
        │       │       ├── objects/...
        │       │       ├── packed-refs
        │       │       └── refs/...
        │       ├── objects/...
        │       ├── packed-refs
        │       └── refs/...
        └── ORIG_HEAD

91 directories, 133 files
args=(
    --git-common-dir
    --git-dir
    --is-inside-git-dir
    --is-inside-work-tree
    --show-toplevel
);

padding() {
    local delimiter="$1" # must be 1ch
    local message="$2"
    local width="${3:-78}"
    printf -- "${delimiter}%.0s" $(seq 0 $(($width - ${#message})))
}

header() {
    message="$(printf "%s " "$1")"
    echo
    printf "%s" "$message"; padding "#" "$message"; echo
    echo
}
indent() {
    padding " " "$1" 22;
    printf "%s = " "$1";
}

explore() {
    local to_explore="$1"
    {
        cd "$to_explore"
        header "$to_explore"
        for arg in "${args[@]}"; do
            indent "$arg";
            git --no-pager rev-parse "$arg" 2>&1 || true;
        done

        indent "config#core.worktree"
        git --no-pager config core.worktree || echo "<missing>"

        indent "./config#core.worktree" 22
        if [ -f ./config ]; then
            git --no-pager config --file ${PWD}/config core.worktree || echo "<missing>"
        else
            echo
        fi
        indent "./gitdir"
        if [ -f ./gitdir ]; then
            cat ./gitdir;
        else
            echo
        fi
    } | sed 's/^/# /g' | sed "s#${PWD}#\${PWD}#g"
}

paths_to_explore=(
    "${PWD}"
    "${PWD}/nested"
    "${PWD}/.git"
    "${PWD}/.git/worktrees"
    "${PWD}/.git/worktrees/wrktr"
    "${PWD}/.git/worktrees/wrktr/modules/submod"
    "${PWD}/wrktr"
    "${PWD}/wrktr/submod"
    "${PWD}/submod"
    "${PWD}/.git/modules/submod"
)

for p in "${paths_to_explore[@]}"; do explore "$p"; done
#
# ${PWD} ############################################################
#
#        --git-common-dir = .git
#               --git-dir = .git
#     --is-inside-git-dir = false
#   --is-inside-work-tree = true
#         --show-toplevel = ${PWD}
#    config#core.worktree = <missing>
#  ./config#core.worktree =
#                ./gitdir =
#
# ${PWD}/nested #####################################################
#
#        --git-common-dir = ../.git
#               --git-dir = ${PWD}/.git
#     --is-inside-git-dir = false
#   --is-inside-work-tree = true
#         --show-toplevel = ${PWD}
#    config#core.worktree = <missing>
#  ./config#core.worktree =
#                ./gitdir =
#
# ${PWD}/.git #######################################################
#
#        --git-common-dir = .
#               --git-dir = .
#     --is-inside-git-dir = true
#   --is-inside-work-tree = false
#         --show-toplevel = fatal: this operation must be run in a work tree
#    config#core.worktree = <missing>
#  ./config#core.worktree = <missing>
#                ./gitdir =
#
# ${PWD}/.git/worktrees #############################################
#
#        --git-common-dir = ${PWD}/.git
#               --git-dir = ${PWD}/.git
#     --is-inside-git-dir = true
#   --is-inside-work-tree = false
#         --show-toplevel = fatal: this operation must be run in a work tree
#    config#core.worktree = <missing>
#  ./config#core.worktree =
#                ./gitdir =
#
# ${PWD}/.git/worktrees/wrktr #######################################
#
#        --git-common-dir = ${PWD}/.git
#               --git-dir = .
#     --is-inside-git-dir = true
#   --is-inside-work-tree = false
#         --show-toplevel = fatal: this operation must be run in a work tree
#    config#core.worktree = <missing>
#  ./config#core.worktree =
#                ./gitdir = ${PWD}/wrktr/.git
#
# ${PWD}/.git/worktrees/wrktr/modules/submod ########################
#
#        --git-common-dir = .
#               --git-dir = .
#     --is-inside-git-dir = false
#   --is-inside-work-tree = false
#         --show-toplevel = ${PWD}/wrktr/submod
#    config#core.worktree = ../../../../../wrktr/submod
#  ./config#core.worktree = ../../../../../wrktr/submod
#                ./gitdir =
#
# ${PWD}/wrktr ######################################################
#
#        --git-common-dir = ${PWD}/.git
#               --git-dir = ${PWD}/.git/worktrees/wrktr
#     --is-inside-git-dir = false
#   --is-inside-work-tree = true
#         --show-toplevel = ${PWD}/wrktr
#    config#core.worktree = <missing>
#  ./config#core.worktree =
#                ./gitdir =
#
# ${PWD}/wrktr/submod ###############################################
#
#        --git-common-dir = ${PWD}/.git/worktrees/wrktr/modules/submod
#               --git-dir = ${PWD}/.git/worktrees/wrktr/modules/submod
#     --is-inside-git-dir = false
#   --is-inside-work-tree = true
#         --show-toplevel = ${PWD}/wrktr/submod
#    config#core.worktree = ../../../../../wrktr/submod
#  ./config#core.worktree =
#                ./gitdir =
#
# ${PWD}/submod #####################################################
#
#        --git-common-dir = ${PWD}/.git/modules/submod
#               --git-dir = ${PWD}/.git/modules/submod
#     --is-inside-git-dir = false
#   --is-inside-work-tree = true
#         --show-toplevel = ${PWD}/submod
#    config#core.worktree = ../../../submod
#  ./config#core.worktree =
#                ./gitdir =
#
# ${PWD}/.git/modules/submod ########################################
#
#        --git-common-dir = .
#               --git-dir = .
#     --is-inside-git-dir = false
#   --is-inside-work-tree = false
#         --show-toplevel = ${PWD}/submod
#    config#core.worktree = ../../../submod
#  ./config#core.worktree = ../../../submod
#                ./gitdir =
explore_tab() {
    local to_explore="$1"
    {
        cd "$to_explore"

        printf '|`%s`' "$to_explore"
        for arg in "${args[@]}"; do
            _result="$(git --no-pager rev-parse "$arg" 2>&1 || true)"
            printf '|`%s`' "$_result"
        done
        # config#core.worktree
        printf '|`%s`' "$(git --no-pager config core.worktree || echo "<missing>")"

        # ./config#core.worktree
        printf '|'
        if [ -f ./config ]; then
            printf '`%s`' "$(
                git --no-pager config --file ${PWD}/config core.worktree || echo "<missing>"
            )"
        fi
        # ./gitdir
        printf "|"
        if [ -f ./gitdir ]; then
            printf '`%s`' "$(cat ./gitdir)";
        fi
        echo "|"
    }
}
_headers=(
    "pwd"
    "${args[@]}"
    "config#core.worktree"
    ./config#core.worktree
    ./gitdir
)
for i in "${_headers[@]}"; do printf '|`%s`' "$i"; done
echo "|"
for i in "${_headers[@]}"; do printf "|-"; done
echo "|"
for p in "${paths_to_explore[@]}"; do
  explore_tab "$p" | sed "s#${PWD}#\${PWD}#g";
done
pwd--git-common-dir--git-dir--is-inside-git-dir--is-inside-work-tree--show-toplevelconfig#core.worktree./config#core.worktree./gitdir
${PWD}.git.gitfalsetrue${PWD}<missing>
${PWD}/nested../.git${PWD}/.gitfalsetrue${PWD}<missing>
${PWD}/.git..truefalsefatal: this operation must be run in a work tree<missing><missing>
${PWD}/.git/worktrees${PWD}/.git${PWD}/.gittruefalsefatal: this operation must be run in a work tree<missing>
${PWD}/.git/worktrees/wrktr${PWD}/.git.truefalsefatal: this operation must be run in a work tree<missing>${PWD}/wrktr/.git
${PWD}/.git/worktrees/wrktr/modules/submod..falsefalse${PWD}/wrktr/submod../../../../../wrktr/submod../../../../../wrktr/submod
${PWD}/wrktr${PWD}/.git${PWD}/.git/worktrees/wrktrfalsetrue${PWD}/wrktr<missing>
${PWD}/wrktr/submod${PWD}/.git/worktrees/wrktr/modules/submod${PWD}/.git/worktrees/wrktr/modules/submodfalsetrue${PWD}/wrktr/submod../../../../../wrktr/submod
${PWD}/submod${PWD}/.git/modules/submod${PWD}/.git/modules/submodfalsetrue${PWD}/submod../../../submod
${PWD}/.git/modules/submod..falsefalse${PWD}/submod../../../submod../../../submod
pwd--git-common-dir--git-dir--is-inside-git-dir--is-inside-work-tree--show-toplevelconfig#core.worktree./config#core.worktree./gitdir
${PWD}/.git..truefalsefatal: this operation must be run in a work tree<missing><missing>
${PWD}/.git/worktrees${PWD}/.git${PWD}/.gittruefalsefatal: this operation must be run in a work tree<missing>
${PWD}/.git/worktrees/wrktr${PWD}/.git.truefalsefatal: this operation must be run in a work tree<missing>${PWD}/wrktr/.git
${PWD}/.git/worktrees/wrktr/modules/submod..falsefalse${PWD}/wrktr/submod../../../../../wrktr/submod../../../../../wrktr/submod
${PWD}/.git/modules/submod..falsefalse${PWD}/submod../../../submod../../../submod