Skip to content

Commit b701634

Browse files
calebdwgitster
authored andcommitted
worktree: add relative cli/config options to add command
This introduces the `--[no-]relative-paths` CLI option and `worktree.useRelativePaths` configuration setting to the `worktree add` command. When enabled these options allow worktrees to be linked using relative paths, enhancing portability across environments where absolute paths may differ (e.g., containerized setups, shared network drives). Git still creates absolute paths by default, but these options allow users to opt-in to relative paths if desired. The t2408 test file is removed and more comprehensive tests are written for the various worktree operations in their own files. Signed-off-by: Caleb White <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 4dac9e3 commit b701634

File tree

7 files changed

+95
-49
lines changed

7 files changed

+95
-49
lines changed

Documentation/config/worktree.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,13 @@ worktree.guessRemote::
77
such a branch exists, it is checked out and set as "upstream"
88
for the new branch. If no such match can be found, it falls
99
back to creating a new branch from the current HEAD.
10+
11+
worktree.useRelativePaths::
12+
Link worktrees using relative paths (when "true") or absolute
13+
paths (when "false"). This is particularly useful for setups
14+
where the repository and worktrees may be moved between
15+
different locations or environments. Defaults to "false".
16+
+
17+
Note that setting `worktree.useRelativePaths` to "true" implies enabling the
18+
`extension.relativeWorktrees` config (see linkgit:git-config[1]),
19+
thus making it incompatible with older versions of Git.

Documentation/git-worktree.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,11 @@ To remove a locked worktree, specify `--force` twice.
216216
This can also be set up as the default behaviour by using the
217217
`worktree.guessRemote` config option.
218218

219+
--[no-]relative-paths::
220+
Link worktrees using relative paths or absolute paths (default).
221+
Overrides the `worktree.useRelativePaths` config option, see
222+
linkgit:git-config[1].
223+
219224
--[no-]track::
220225
When creating a new branch, if `<commit-ish>` is a branch,
221226
mark it as "upstream" from the new branch. This is the

builtin/worktree.c

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -120,12 +120,14 @@ struct add_opts {
120120
int quiet;
121121
int checkout;
122122
int orphan;
123+
int relative_paths;
123124
const char *keep_locked;
124125
};
125126

126127
static int show_only;
127128
static int verbose;
128129
static int guess_remote;
130+
static int use_relative_paths;
129131
static timestamp_t expire;
130132

131133
static int git_worktree_config(const char *var, const char *value,
@@ -134,6 +136,9 @@ static int git_worktree_config(const char *var, const char *value,
134136
if (!strcmp(var, "worktree.guessremote")) {
135137
guess_remote = git_config_bool(var, value);
136138
return 0;
139+
} else if (!strcmp(var, "worktree.userelativepaths")) {
140+
use_relative_paths = git_config_bool(var, value);
141+
return 0;
137142
}
138143

139144
return git_default_config(var, value, ctx, cb);
@@ -414,8 +419,7 @@ static int add_worktree(const char *path, const char *refname,
414419
const struct add_opts *opts)
415420
{
416421
struct strbuf sb_git = STRBUF_INIT, sb_repo = STRBUF_INIT;
417-
struct strbuf sb = STRBUF_INIT, sb_tmp = STRBUF_INIT;
418-
struct strbuf sb_path_realpath = STRBUF_INIT, sb_repo_realpath = STRBUF_INIT;
422+
struct strbuf sb = STRBUF_INIT;
419423
const char *name;
420424
struct strvec child_env = STRVEC_INIT;
421425
unsigned int counter = 0;
@@ -491,10 +495,7 @@ static int add_worktree(const char *path, const char *refname,
491495

492496
strbuf_reset(&sb);
493497
strbuf_addf(&sb, "%s/gitdir", sb_repo.buf);
494-
strbuf_realpath(&sb_path_realpath, path, 1);
495-
strbuf_realpath(&sb_repo_realpath, sb_repo.buf, 1);
496-
write_file(sb.buf, "%s/.git", relative_path(sb_path_realpath.buf, sb_repo_realpath.buf, &sb_tmp));
497-
write_file(sb_git.buf, "gitdir: %s", relative_path(sb_repo_realpath.buf, sb_path_realpath.buf, &sb_tmp));
498+
write_worktree_linking_files(sb_git, sb, opts->relative_paths);
498499
strbuf_reset(&sb);
499500
strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
500501
write_file(sb.buf, "../..");
@@ -578,12 +579,9 @@ static int add_worktree(const char *path, const char *refname,
578579

579580
strvec_clear(&child_env);
580581
strbuf_release(&sb);
581-
strbuf_release(&sb_tmp);
582582
strbuf_release(&symref);
583583
strbuf_release(&sb_repo);
584-
strbuf_release(&sb_repo_realpath);
585584
strbuf_release(&sb_git);
586-
strbuf_release(&sb_path_realpath);
587585
strbuf_release(&sb_name);
588586
free_worktree(wt);
589587
return ret;
@@ -796,12 +794,15 @@ static int add(int ac, const char **av, const char *prefix)
796794
PARSE_OPT_NOARG | PARSE_OPT_OPTARG),
797795
OPT_BOOL(0, "guess-remote", &guess_remote,
798796
N_("try to match the new branch name with a remote-tracking branch")),
797+
OPT_BOOL(0, "relative-paths", &opts.relative_paths,
798+
N_("use relative paths for worktrees")),
799799
OPT_END()
800800
};
801801
int ret;
802802

803803
memset(&opts, 0, sizeof(opts));
804804
opts.checkout = 1;
805+
opts.relative_paths = use_relative_paths;
805806
ac = parse_options(ac, av, prefix, options, git_worktree_add_usage, 0);
806807
if (!!opts.detach + !!new_branch + !!new_branch_force > 1)
807808
die(_("options '%s', '%s', and '%s' cannot be used together"), "-b", "-B", "--detach");

t/t2400-worktree-add.sh

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1207,4 +1207,50 @@ test_expect_success '"add" with initialized submodule, with submodule.recurse se
12071207
git -C project-clone -c submodule.recurse worktree add ../project-5
12081208
'
12091209

1210+
test_expect_success 'can create worktrees with relative paths' '
1211+
test_when_finished "git worktree remove relative" &&
1212+
test_config worktree.useRelativePaths false &&
1213+
git worktree add --relative-paths ./relative &&
1214+
echo "gitdir: ../.git/worktrees/relative" >expect &&
1215+
test_cmp expect relative/.git &&
1216+
echo "../../../relative/.git" >expect &&
1217+
test_cmp expect .git/worktrees/relative/gitdir
1218+
'
1219+
1220+
test_expect_success 'can create worktrees with absolute paths' '
1221+
test_config worktree.useRelativePaths true &&
1222+
git worktree add ./relative &&
1223+
echo "gitdir: ../.git/worktrees/relative" >expect &&
1224+
test_cmp expect relative/.git &&
1225+
git worktree add --no-relative-paths ./absolute &&
1226+
echo "gitdir: $(pwd)/.git/worktrees/absolute" >expect &&
1227+
test_cmp expect absolute/.git &&
1228+
echo "$(pwd)/absolute/.git" >expect &&
1229+
test_cmp expect .git/worktrees/absolute/gitdir
1230+
'
1231+
1232+
test_expect_success 'move repo without breaking relative internal links' '
1233+
test_when_finished rm -rf repo moved &&
1234+
git init repo &&
1235+
(
1236+
cd repo &&
1237+
test_commit initial &&
1238+
git worktree add --relative-paths wt1 &&
1239+
cd .. &&
1240+
mv repo moved &&
1241+
cd moved/wt1 &&
1242+
git worktree list >out 2>err &&
1243+
test_must_be_empty err
1244+
)
1245+
'
1246+
1247+
test_expect_success 'relative worktree sets extension config' '
1248+
test_when_finished "rm -rf repo" &&
1249+
git init repo &&
1250+
git -C repo commit --allow-empty -m base &&
1251+
git -C repo worktree add --relative-paths ./foo &&
1252+
test_cmp_config -C repo 1 core.repositoryformatversion &&
1253+
test_cmp_config -C repo true extensions.relativeworktrees
1254+
'
1255+
12101256
test_done

t/t2401-worktree-prune.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,11 +120,12 @@ test_expect_success 'prune duplicate (main/linked)' '
120120
! test -d .git/worktrees/wt
121121
'
122122

123-
test_expect_success 'not prune proper worktrees when run inside linked worktree' '
123+
test_expect_success 'not prune proper worktrees inside linked worktree with relative paths' '
124124
test_when_finished rm -rf repo wt_ext &&
125125
git init repo &&
126126
(
127127
cd repo &&
128+
git config worktree.useRelativePaths true &&
128129
echo content >file &&
129130
git add file &&
130131
git commit -m msg &&

t/t2402-worktree-list.sh

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ test_expect_success 'broken main worktree still at the top' '
261261
'
262262

263263
test_expect_success 'linked worktrees are sorted' '
264+
test_when_finished "rm -rf sorted" &&
264265
mkdir sorted &&
265266
git init sorted/main &&
266267
(
@@ -280,6 +281,27 @@ test_expect_success 'linked worktrees are sorted' '
280281
test_cmp expected sorted/main/actual
281282
'
282283

284+
test_expect_success 'linked worktrees with relative paths are shown with absolute paths' '
285+
test_when_finished "rm -rf sorted" &&
286+
mkdir sorted &&
287+
git init sorted/main &&
288+
(
289+
cd sorted/main &&
290+
test_tick &&
291+
test_commit new &&
292+
git worktree add --relative-paths ../first &&
293+
git worktree add ../second &&
294+
git worktree list --porcelain >out &&
295+
grep ^worktree out >actual
296+
) &&
297+
cat >expected <<-EOF &&
298+
worktree $(pwd)/sorted/main
299+
worktree $(pwd)/sorted/first
300+
worktree $(pwd)/sorted/second
301+
EOF
302+
test_cmp expected sorted/main/actual
303+
'
304+
283305
test_expect_success 'worktree path when called in .git directory' '
284306
git worktree list >list1 &&
285307
git -C .git worktree list >list2 &&

t/t2408-worktree-relative.sh

Lines changed: 0 additions & 39 deletions
This file was deleted.

0 commit comments

Comments
 (0)