]> git.ipfire.org Git - thirdparty/git.git/commitdiff
worktree list: quote paths
authorPhillip Wood <phillip.wood@dunelm.org.uk>
Tue, 18 Nov 2025 16:07:33 +0000 (16:07 +0000)
committerJunio C Hamano <gitster@pobox.com>
Tue, 18 Nov 2025 18:11:29 +0000 (10:11 -0800)
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>
builtin/worktree.c
t/t2402-worktree-list.sh

index 0643a22ee58b89c63fdf22cc34823e0851b6e2c2..303cc3b2d64b6457b50ddd4944f7a2fdbb193775 100644 (file)
@@ -980,6 +980,7 @@ static void show_worktree_porcelain(struct worktree *wt, int line_terminator)
 }
 
 struct worktree_display {
+       char *path;
        int width;
 };
 
@@ -989,7 +990,7 @@ static void show_worktree(struct worktree *wt, struct worktree_display *display,
        struct strbuf sb = STRBUF_INIT;
        const char *reason;
 
-       strbuf_addf(&sb, "%s%*s", wt->path, 1 + path_maxwidth - display->width, "");
+       strbuf_addf(&sb, "%s%*s", display->path, 1 + path_maxwidth - display->width, "");
        if (wt->is_bare)
                strbuf_addstr(&sb, "(bare)");
        else {
@@ -1028,11 +1029,14 @@ static void measure_widths(struct worktree **wt, int *abbrev,
 {
        int i, display_alloc = 0;
        struct worktree_display *display = NULL;
+       struct strbuf buf = STRBUF_INIT;
 
        for (i = 0; wt[i]; i++) {
                int sha1_len;
                ALLOC_GROW(display, i + 1, display_alloc);
-               display[i].width = utf8_strwidth(wt[i]->path);
+               quote_path(wt[i]->path, NULL, &buf, 0);
+               display[i].width = utf8_strwidth(buf.buf);
+               display[i].path = strbuf_detach(&buf, NULL);
 
                if (display[i].width > *maxwidth)
                        *maxwidth = display[i].width;
@@ -1104,6 +1108,8 @@ static int list(int ac, const char **av, const char *prefix,
                                show_worktree(worktrees[i],
                                              &display[i], path_maxwidth, abbrev);
                }
+               for (i = 0; display && worktrees[i]; i++)
+                       free(display[i].path);
                free(display);
                free_worktrees(worktrees);
        }
index a494df6d6126680af12c10d2d39b809f7d37aaa7..e0c6abd2f58e206efaa4d58c075d584feb064fda 100755 (executable)
@@ -29,7 +29,8 @@ test_expect_success 'rev-parse --git-path objects linked worktree' '
        test_cmp expect actual
 '
 
-test_expect_success '"list" all worktrees from main' '
+test_expect_success '"list" all worktrees from main core.quotepath=false' '
+       test_config core.quotepath false &&
        echo "$(git rev-parse --show-toplevel)      $(git rev-parse --short HEAD) [$(git symbolic-ref --short HEAD)]" >expect &&
        test_when_finished "rm -rf áááá out actual expect && git worktree prune" &&
        git worktree add --detach áááá main &&
@@ -38,7 +39,19 @@ test_expect_success '"list" all worktrees from main' '
        test_cmp expect actual
 '
 
+test_expect_success '"list" all worktrees from main core.quotepath=true' '
+       test_config core.quotepath true &&
+       echo "$(git rev-parse --show-toplevel)            $(git rev-parse --short HEAD) [$(git symbolic-ref --short HEAD)]" >expect &&
+       test_when_finished "rm -rf á out actual expect && git worktree prune" &&
+       git worktree add --detach á main &&
+       echo "\"$(git -C á rev-parse --show-toplevel)\" $(git rev-parse --short HEAD) (detached HEAD)" |
+               sed s/á/\\\\303\\\\241/g >>expect &&
+       git worktree list >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success '"list" all worktrees from linked' '
+       test_config core.quotepath false &&
        echo "$(git rev-parse --show-toplevel)      $(git rev-parse --short HEAD) [$(git symbolic-ref --short HEAD)]" >expect &&
        test_when_finished "rm -rf áááá out actual expect && git worktree prune" &&
        git worktree add --detach áááá main &&