'git worktree list' uses the for_each_worktree function to iterate, and outputs in the format: '<worktree> (<short-ref>)' Signed-off-by: Michael Rappazzo <rappazzo@xxxxxxxxx> --- Documentation/git-worktree.txt | 11 ++++++++- builtin/worktree.c | 55 ++++++++++++++++++++++++++++++++++++++++++ t/t2027-worktree-list.sh | 51 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 116 insertions(+), 1 deletion(-) create mode 100755 t/t2027-worktree-list.sh diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt index fb68156..e953b4e 100644 --- a/Documentation/git-worktree.txt +++ b/Documentation/git-worktree.txt @@ -11,6 +11,7 @@ SYNOPSIS [verse] 'git worktree add' [-f] [--detach] [-b <new-branch>] <path> [<branch>] 'git worktree prune' [-n] [-v] [--expire <expire>] +'git worktree list' [--path-only] DESCRIPTION ----------- @@ -59,6 +60,12 @@ prune:: Prune working tree information in $GIT_DIR/worktrees. +list:: + +List the main worktree followed by all of the linked worktrees. The default +format of the list includes the full path to the worktree and the branch or +revision that the head of that worktree is currently pointing to. + OPTIONS ------- @@ -93,6 +100,9 @@ OPTIONS --expire <time>:: With `prune`, only expire unused working trees older than <time>. +--path-only + With `list`, only show the worktree path + DETAILS ------- Each linked working tree has a private sub-directory in the repository's @@ -167,7 +177,6 @@ performed manually, such as: - `remove` to remove a linked working tree and its administrative files (and warn if the working tree is dirty) - `mv` to move or rename a working tree and update its administrative files -- `list` to list linked working trees - `lock` to prevent automatic pruning of administrative files (for instance, for a working tree on a portable device) diff --git a/builtin/worktree.c b/builtin/worktree.c index a43e360..b39ecbd 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -12,6 +12,7 @@ static const char * const worktree_usage[] = { N_("git worktree add [<options>] <path> <branch>"), N_("git worktree prune [<options>]"), + N_("git worktree list [<options>]"), NULL }; @@ -443,6 +444,58 @@ done: return ret; } +/* list callback data */ +struct list_opts { + int path_only; +}; + +static int print_worktree_details(const char *path, const char *git_dir, void *cb_data) +{ + struct strbuf head_file = STRBUF_INIT; + struct strbuf head_ref = STRBUF_INIT; + struct stat st; + struct list_opts *opts = cb_data; + const char *ref_prefix = "ref: refs/heads/"; + + strbuf_addf(&head_file, "%s/HEAD", git_dir); + if (!opts->path_only && !stat(head_file.buf, &st)) { + strbuf_read_file(&head_ref, head_file.buf, st.st_size); + strbuf_strip_suffix(&head_ref, "\n"); + + if (starts_with(head_ref.buf, ref_prefix)) { + /* branch checked out */ + strbuf_remove(&head_ref, 0, strlen(ref_prefix)); + /* } else { + * headless -- no-op + */ + } + printf("%s (%s)\n", path, head_ref.buf); + } else { + printf("%s\n", path); + } + + strbuf_release(&head_ref); + strbuf_release(&head_file); + return 0; +} + +static int list(int ac, const char **av, const char *prefix) +{ + struct list_opts opts; + struct option options[] = { + OPT_BOOL(0, "path-only", &opts.path_only, N_("only show the path of the worktree")), + OPT_END() + }; + + opts.path_only = 0; + + ac = parse_options(ac, av, prefix, options, worktree_usage, 0); + if (ac) + usage_with_options(worktree_usage, options); + + return for_each_worktree(&print_worktree_details, &opts); +} + int cmd_worktree(int ac, const char **av, const char *prefix) { struct option options[] = { @@ -455,5 +508,7 @@ int cmd_worktree(int ac, const char **av, const char *prefix) return add(ac - 1, av + 1, prefix); if (!strcmp(av[1], "prune")) return prune(ac - 1, av + 1, prefix); + if (!strcmp(av[1], "list")) + return list(ac - 1, av + 1, prefix); usage_with_options(worktree_usage, options); } diff --git a/t/t2027-worktree-list.sh b/t/t2027-worktree-list.sh new file mode 100755 index 0000000..220f98e --- /dev/null +++ b/t/t2027-worktree-list.sh @@ -0,0 +1,51 @@ +#!/bin/sh + +test_description='test git worktree list' + +. ./test-lib.sh + +test_expect_success 'setup' ' + test_commit init +' + +test_expect_success '"list" all worktrees from main' ' + echo "$(git rev-parse --show-toplevel) ($(git symbolic-ref --short HEAD))" >expect && + git worktree add --detach here master && + echo "$(git -C here rev-parse --show-toplevel) ($(git -C here rev-parse HEAD))" >>expect && + git worktree list >actual && + test_cmp expect actual && + rm -rf here && + git worktree prune +' + +test_expect_success '"list" all worktrees from linked' ' + echo "$(git rev-parse --show-toplevel) ($(git symbolic-ref --short HEAD))" >expect && + git worktree add --detach here master && + echo "$(git -C here rev-parse --show-toplevel) ($(git -C here rev-parse HEAD))" >>expect && + git -C here worktree list >actual && + test_cmp expect actual && + rm -rf here && + git worktree prune +' + +test_expect_success '"list" all worktrees from main --path-only' ' + git rev-parse --show-toplevel >expect && + git worktree add --detach here master && + git -C here rev-parse --show-toplevel >>expect && + git worktree list --path-only >actual && + test_cmp expect actual && + rm -rf here && + git worktree prune +' + +test_expect_success '"list" all worktrees from linked --path-only' ' + git rev-parse --show-toplevel >expect && + git worktree add --detach here master && + git -C here rev-parse --show-toplevel >>expect && + git -C here worktree list --path-only >actual && + test_cmp expect actual && + rm -rf here && + git worktree prune +' + +test_done -- 2.5.0 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html