feat: resolve a relative FORGIT_WORKTREE_ADD_DIR per repository

A relative FORGIT_WORKTREE_ADD_DIR is resolved against the root of the
main worktree on every invocation, so a single setting such as
`.worktrees` is valid in every repository. Absolute paths keep their
meaning.

The variable previously only took a full path, so users resolved the
repository root in their shell startup files, which runs git outside a
repository on every new shell and freezes the path for the whole
session.
This commit is contained in:
carlfriedrich 2026-09-07 14:12:12 +02:00
parent d7d53288f9
commit 4f884bd9bd
3 changed files with 66 additions and 3 deletions

View file

@ -381,7 +381,7 @@ export FORGIT_LOG_FZF_OPTS='
| `FORGIT_FULLSCREEN_CONTEXT` | lines of diff context in full-screen mode | 10 |
| `FORGIT_DIR_VIEW` | command used to preview directories | `tree` if available, otherwise `find` |
| `FORGIT_CLEAN_LIST_FILES_OPTS` | arguments passed to `git ls-files` together with `--others` to determine which files are shown when invoking `forgit clean` | |
| `FORGIT_WORKTREE_ADD_DIR` | directory where new worktrees are created | `<repo-root>/.wt` |
| `FORGIT_WORKTREE_ADD_DIR` | directory where new worktrees are created (a relative path is resolved against the main worktree root) | `.wt` |
| `FORGIT_CHECKOUT_BRANCH_AUTO_CREATE_BRANCH` | prevents forgit from auto creating a new branch with `gcb <BRANCH_NAME>` when `<BRANCH_NAME>` does not exist when set to `false` | |
| `FORGIT_SWITCH_AUTO_CREATE_BRANCH` | prevents forgit from auto creating a new branch with `gsw <BRANCH_NAME>` when `<BRANCH_NAME>` does not exist when set to `false` | |

View file

@ -1583,6 +1583,20 @@ _forgit_main_worktree_root() {
git worktree list --porcelain | sed -n 's/^worktree //p;q'
}
# Get the directory in which new worktrees are created
# A relative FORGIT_WORKTREE_ADD_DIR is resolved against the root of the main
# worktree, so that a single setting is valid in every repository. Anchoring it
# to the main worktree rather than the current one also keeps adding a worktree
# from a linked worktree from nesting the directory any deeper.
_forgit_worktree_add_dir() {
local dir="${FORGIT_WORKTREE_ADD_DIR:-.wt}"
if [[ $dir == /* ]]; then
echo "$dir"
else
echo "$(_forgit_main_worktree_root)/$dir"
fi
}
# Parse git worktree list --porcelain output and format it for display
# Output format: [XY] /path/to/worktree (branch) 3 hours ago
# X: '*' = current worktree, ' ' = other
@ -1740,8 +1754,8 @@ _forgit_worktree_delete() {
_forgit_worktree_add() {
_forgit_inside_git_repo || return 1
# Default to main worktree root, not current worktree's root
local wt_dir="${FORGIT_WORKTREE_ADD_DIR:-$(_forgit_main_worktree_root)/.wt}"
local wt_dir
wt_dir=$(_forgit_worktree_add_dir)
# Two or more arguments: pass through to git directly
if [[ $# -ge 2 ]]; then

49
tests/worktree.test.sh Normal file
View file

@ -0,0 +1,49 @@
#!/usr/bin/env bash
function set_up_before_script() {
source bin/git-forgit
# Ignore global git config files
export GIT_CONFIG_SYSTEM=/dev/null
export GIT_CONFIG_GLOBAL=/dev/null
unset FORGIT_WORKTREE_ADD_DIR
# Create a temporary git repository for testing
MAIN_WORKTREE_ROOT="$(bashunit::temp_dir)/main"
git init -q "$MAIN_WORKTREE_ROOT"
cd "$MAIN_WORKTREE_ROOT" || return 1
git config user.email "test@example.com"
git config user.name "Test User"
echo "initial" >README.md
git add README.md
git commit -q -m "Initial commit"
}
function test_worktree_add_dir_defaults_to_dot_wt_in_main_worktree_root() {
assert_same "$MAIN_WORKTREE_ROOT/.wt" "$(_forgit_worktree_add_dir)"
}
function test_worktree_add_dir_resolves_a_relative_path_against_main_worktree_root() {
assert_same "$MAIN_WORKTREE_ROOT/.worktrees" \
"$(FORGIT_WORKTREE_ADD_DIR=.worktrees _forgit_worktree_add_dir)"
}
function test_worktree_add_dir_resolves_a_nested_relative_path() {
assert_same "$MAIN_WORKTREE_ROOT/build/wt" \
"$(FORGIT_WORKTREE_ADD_DIR=build/wt _forgit_worktree_add_dir)"
}
function test_worktree_add_dir_uses_an_absolute_path_as_is() {
assert_same "/somewhere/else" \
"$(FORGIT_WORKTREE_ADD_DIR=/somewhere/else _forgit_worktree_add_dir)"
}
function test_worktree_add_dir_resolves_a_relative_path_from_a_linked_worktree() {
local linked_worktree="$MAIN_WORKTREE_ROOT/.worktrees/linked"
git worktree add -q -b linked "$linked_worktree" >/dev/null 2>&1
cd "$linked_worktree" || return 1
assert_same "$MAIN_WORKTREE_ROOT/.worktrees" \
"$(FORGIT_WORKTREE_ADD_DIR=.worktrees _forgit_worktree_add_dir)"
}