From b7dfe398a355ff6c6e3d2c06031041fbe3b9cb46 Mon Sep 17 00:00:00 2001 From: "Travis A. Everett" Date: Fri, 23 Dec 2022 19:21:00 -0600 Subject: [PATCH] find git-dir directly and look up work-tree This enables mgitstatus to handle repositories with non-standard work tree locations (such as those used by yadm). It can discover these, or the user can explicitly list the path to the git-dir. Two broad strokes to this: 1. Instead of checking every sub-directory for a .git directory in the standard location, search directly for directories matching "*.git". - Since *standard* .git directories will be one level deeper in the tree, this adds 1 to DEPTH. - Since we're looking for the .git directory itself, I think we can use -prune to skip searching within those matching directories. 2. Use `git rev-parse --show-toplevel` to look up the work tree. As far as I can tell, this will report the path of a non-standard work tree. This will error (and print nothing) on a standard repo. If that fails to produce output, fall back on the existing guess that the work tree will be the parent directory of the git-dir. Most of the diff is from computing this once (as PROJ_DIR) and replacing instances of `$(dirname "$GIT_DIR")` with it. --- mgitstatus | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/mgitstatus b/mgitstatus index 2275ef0..ab53286 100755 --- a/mgitstatus +++ b/mgitstatus @@ -156,18 +156,35 @@ ID="$(id -n -u)" # infinitely deep FIND_OPTS="" if [ "$DEPTH" -ne 0 ]; then + # 1 lvl deeper since we're searching for the .git directory inside the project + ((DEPTH=$DEPTH+1)) FIND_OPTS="$FIND_OPTS -maxdepth $DEPTH" fi -# Go through positional arguments (DIRs) or '.' if no argumnets are given +find_git_work_tree()( + cd "$1" + likely_gitdir="$PWD" + worktree="$(git rev-parse --show-toplevel 2>/dev/null)" + if [ -z "$worktree" ]; then + # GIT_DIR doesn't know; guess: + cd .. + echo "$PWD" + else + # git told us, so we'll trust it + echo "$worktree" + return 0 + fi +) + +# Go through positional arguments (DIRs) or '.' if no arguments are given for DIR in "${@:-"."}"; do # We *want* to expand parameters, so disable shellcheck for this error: # shellcheck disable=SC2086 - find -L "$DIR" $FIND_OPTS -type d | while read -r PROJ_DIR + find -L "$DIR" $FIND_OPTS -type d -name "*.git" -prune | while read -r GIT_DIR do - GIT_DIR="$PROJ_DIR/.git" - GIT_CONF="$PROJ_DIR/.git/config" - + PROJ_DIR="$(find_git_work_tree "$GIT_DIR")" + GIT_CONF="$GIT_DIR/config" + # Check if the repo is safe (https://github.blog/2022-04-12-git-security-vulnerability-announced/) if [ -d "$GIT_DIR" ]; then GIT_DIR_OWNER="$(ls -ld "$GIT_DIR" | awk 'NR==1 {print $3}')" @@ -201,11 +218,11 @@ for DIR in "${@:-"."}"; do # Do a 'git fetch' if requested if [ "$DO_FETCH" -eq 1 ]; then - git --work-tree "$(dirname "$GIT_DIR")" --git-dir "$GIT_DIR" fetch -q >/dev/null + git --work-tree "$PROJ_DIR" --git-dir "$GIT_DIR" fetch -q >/dev/null fi # Refresh the index, or we might get wrong results. - git --work-tree "$(dirname "$GIT_DIR")" --git-dir "$GIT_DIR" update-index -q --refresh >/dev/null 2>&1 + git --work-tree "$PROJ_DIR" --git-dir "$GIT_DIR" update-index -q --refresh >/dev/null 2>&1 # Find all remote branches that have been checked out and figure out if # they need a push or pull. We do this with various tests and put the name @@ -269,12 +286,10 @@ for DIR in "${@:-"."}"; do NEEDS_UPSTREAM_BRANCHES=$(printf "$NEEDS_UPSTREAM_BRANCHES" | sort | uniq | tr '\n' ',' | sed "s/^,\(.*\),$/\1/") # Find out if there are unstaged, uncommitted or untracked changes - UNSTAGED=$(git --work-tree "$(dirname "$GIT_DIR")" --git-dir "$GIT_DIR" diff-index --quiet HEAD -- 2>/dev/null; echo $?) - UNCOMMITTED=$(git --work-tree "$(dirname "$GIT_DIR")" --git-dir "$GIT_DIR" diff-files --quiet --ignore-submodules --; echo $?) - UNTRACKED=$(git --work-tree "$(dirname "$GIT_DIR")" --git-dir "$GIT_DIR" ls-files --exclude-standard --others) - cd "$(dirname "$GIT_DIR")" || exit - STASHES=$(git stash list | wc -l) - cd "$OLDPWD" || exit + UNSTAGED=$(git --work-tree "$PROJ_DIR" --git-dir "$GIT_DIR" diff-index --quiet HEAD -- 2>/dev/null; echo $?) + UNCOMMITTED=$(git --work-tree "$PROJ_DIR" --git-dir "$GIT_DIR" diff-files --quiet --ignore-submodules --; echo $?) + UNTRACKED=$(git --work-tree "$PROJ_DIR" --git-dir "$GIT_DIR" ls-files --exclude-standard --others) + STASHES=$(git --work-tree "$PROJ_DIR" --git-dir "$GIT_DIR" stash list | wc -l) # Build up the status string if not flattening. Otherwise, print # results immediately.