worktree list: quote paths
If a worktree path contains newlines or other control characters it messes up the output of "git worktree list". Fix this by using quote_path() to display the worktree path. The output of "git worktree list" is designed for human consumption, scripts should be using the "--porcelain" option so this change should not break them. Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Phillip Wood committed
Nov 18, 2025 at 16:07 UTC
08dfa5983572645ae7cc51b49cadfdf216ecfec6
2 files changed
+22
-3
builtin/worktree.c
+8
-2
@@ -980,6 +980,7 @@ static void show_worktree_porcelain(struct worktree *wt, int line_terminator)
980
}
981
982
struct worktree_display {
983
+ char *path;
984
int width;
985
};
986
@@ -989,7 +990,7 @@ static void show_worktree(struct worktree *wt, struct worktree_display *display,
990
struct strbuf sb = STRBUF_INIT;
991
const char *reason;
992
992
- strbuf_addf(&sb, "%s%*s", wt->path, 1 + path_maxwidth - display->width, "");
993
+ strbuf_addf(&sb, "%s%*s", display->path, 1 + path_maxwidth - display->width, "");
994
if (wt->is_bare)
995
strbuf_addstr(&sb, "(bare)");
996
else {
@@ -1028,11 +1029,14 @@ static void measure_widths(struct worktree **wt, int *abbrev,
1029
{
1030
int i, display_alloc = 0;
1031
struct worktree_display *display = NULL;
1032
+ struct strbuf buf = STRBUF_INIT;
1033
1034
for (i = 0; wt[i]; i++) {
1035
int sha1_len;
1036
ALLOC_GROW(display, i + 1, display_alloc);
1035
- display[i].width = utf8_strwidth(wt[i]->path);
1037
+ quote_path(wt[i]->path, NULL, &buf, 0);
1038
+ display[i].width = utf8_strwidth(buf.buf);
1039
+ display[i].path = strbuf_detach(&buf, NULL);
1040
1041
if (display[i].width > *maxwidth)
1042
*maxwidth = display[i].width;
@@ -1104,6 +1108,8 @@ static int list(int ac, const char **av, const char *prefix,
1108
show_worktree(worktrees[i],
1109
&display[i], path_maxwidth, abbrev);
1110
}
1111
+ for (i = 0; display && worktrees[i]; i++)
1112
+ free(display[i].path);
1113
free(display);
1114
free_worktrees(worktrees);
1115
}
t/t2402-worktree-list.sh
+14
-1
@@ -29,7 +29,8 @@ test_expect_success 'rev-parse --git-path objects linked worktree' '
29
test_cmp expect actual
30
'
31
32
-test_expect_success '"list" all worktrees from main' '
32
+test_expect_success '"list" all worktrees from main core.quotepath=false' '
33
+ test_config core.quotepath false &&
34
echo "$(git rev-parse --show-toplevel) $(git rev-parse --short HEAD) [$(git symbolic-ref --short HEAD)]" >expect &&
35
test_when_finished "rm -rf áááá out actual expect && git worktree prune" &&
36
git worktree add --detach áááá main &&
@@ -38,7 +39,19 @@ test_expect_success '"list" all worktrees from main' '
39
test_cmp expect actual
40
'
41
42
+test_expect_success '"list" all worktrees from main core.quotepath=true' '
43
+ test_config core.quotepath true &&
44
+ echo "$(git rev-parse --show-toplevel) $(git rev-parse --short HEAD) [$(git symbolic-ref --short HEAD)]" >expect &&
45
+ test_when_finished "rm -rf á out actual expect && git worktree prune" &&
46
+ git worktree add --detach á main &&
47
+ echo "\"$(git -C á rev-parse --show-toplevel)\" $(git rev-parse --short HEAD) (detached HEAD)" |
48
+ sed s/á/\\\\303\\\\241/g >>expect &&
49
+ git worktree list >actual &&
50
+ test_cmp expect actual
51
+'
52
+
53
test_expect_success '"list" all worktrees from linked' '
54
+ test_config core.quotepath false &&
55
echo "$(git rev-parse --show-toplevel) $(git rev-parse --short HEAD) [$(git symbolic-ref --short HEAD)]" >expect &&
56
test_when_finished "rm -rf áááá out actual expect && git worktree prune" &&
57
git worktree add --detach áááá main &&