* [PATCH v3] worktree: add 'list' command @ 2015-08-10 20:53 Michael Rappazzo 2015-08-10 20:53 ` Michael Rappazzo 0 siblings, 1 reply; 8+ messages in thread From: Michael Rappazzo @ 2015-08-10 20:53 UTC (permalink / raw) To: gitster, sunshine; +Cc: git, Michael Rappazzo Differences from [v2](http://www.mail-archive.com/git@vger.kernel.org/msg75467.html) - removed unintended whitespace changes - cleanup based on comments from v2 Michael Rappazzo (1): worktree: add 'list' command Documentation/git-worktree.txt | 6 +++- builtin/worktree.c | 67 ++++++++++++++++++++++++++++++++++++++++++ t/t2027-worktree-list.sh | 30 +++++++++++++++++++ 3 files changed, 102 insertions(+), 1 deletion(-) create mode 100755 t/t2027-worktree-list.sh -- 2.5.0 ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v3] worktree: add 'list' command 2015-08-10 20:53 [PATCH v3] worktree: add 'list' command Michael Rappazzo @ 2015-08-10 20:53 ` Michael Rappazzo 2015-08-10 22:10 ` Junio C Hamano 2015-08-11 2:55 ` David Turner 0 siblings, 2 replies; 8+ messages in thread From: Michael Rappazzo @ 2015-08-10 20:53 UTC (permalink / raw) To: gitster, sunshine; +Cc: git, Michael Rappazzo 'git worktree list' will list the main worktree followed by any linked worktrees which were created using 'git worktree add'. Signed-off-by: Michael Rappazzo <rappazzo@gmail.com> --- Documentation/git-worktree.txt | 6 +++- builtin/worktree.c | 67 ++++++++++++++++++++++++++++++++++++++++++ t/t2027-worktree-list.sh | 30 +++++++++++++++++++ 3 files changed, 102 insertions(+), 1 deletion(-) create mode 100755 t/t2027-worktree-list.sh diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt index 3387e2f..efb8e9d 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' DESCRIPTION ----------- @@ -59,6 +60,10 @@ prune:: Prune working tree information in $GIT_DIR/worktrees. +list:: + +List the main worktree followed by all of the linked worktrees. + OPTIONS ------- @@ -167,7 +172,6 @@ performed manually, such as: - `remove` to remove a linked worktree and its administrative files (and warn if the worktree is dirty) - `mv` to move or rename a worktree and update its administrative files -- `list` to list linked worktrees - `lock` to prevent automatic pruning of administrative files (for instance, for a worktree on a portable device) diff --git a/builtin/worktree.c b/builtin/worktree.c index 6a264ee..d90bdee 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -10,6 +10,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 }; @@ -316,6 +317,70 @@ static int add(int ac, const char **av, const char *prefix) return add_worktree(path, cmd.argv); } +static int list(int ac, const char **av, const char *prefix) +{ + int main_only = 0; + struct option options[] = { + OPT_BOOL(0, "main-only", &main_only, N_("only list the main worktree")), + OPT_END() + }; + + ac = parse_options(ac, av, prefix, options, worktree_usage, 0); + if (ac) + usage_with_options(worktree_usage, options); + + struct strbuf main_path = STRBUF_INIT; + const char* common_dir = get_git_common_dir(); + int is_bare = is_bare_repository(); + if (is_bare) { + strbuf_addstr(&main_path, absolute_path(common_dir)); + strbuf_strip_suffix(&main_path, "/."); + } else if (!strcmp(common_dir, ".git")) { + /* within a worktree, the common dir only returns ".git" */ + strbuf_addstr(&main_path, get_git_work_tree()); + } else { + strbuf_addstr(&main_path, common_dir); + strbuf_strip_suffix(&main_path, "/.git"); + } + + printf("%s\n", main_path.buf); + + if (!is_bare) { + strbuf_addstr(&main_path, "/.git"); + } + strbuf_addstr(&main_path, "/worktrees"); + + if (is_directory(main_path.buf)) { + DIR *dir = opendir(main_path.buf); + if (dir) { + struct dirent *d; + struct stat st; + struct strbuf path = STRBUF_INIT; + struct strbuf other_worktree_path = STRBUF_INIT; + while ((d = readdir(dir)) != NULL) { + if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) + continue; + strbuf_reset(&other_worktree_path); + strbuf_addf(&other_worktree_path, "%s/%s/gitdir", main_path.buf, d->d_name); + + if (stat(other_worktree_path.buf, &st)) + continue; + + strbuf_reset(&path); + strbuf_read_file(&path, other_worktree_path.buf, st.st_size); + strbuf_strip_suffix(&path, "/.git\n"); + + printf("%s\n", path.buf); + } + strbuf_release(&other_worktree_path); + strbuf_release(&path); + } + closedir(dir); + } + strbuf_release(&main_path); + return 0; +} + int cmd_worktree(int ac, const char **av, const char *prefix) { struct option options[] = { @@ -328,5 +393,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..8e3dbbc --- /dev/null +++ b/t/t2027-worktree-list.sh @@ -0,0 +1,30 @@ +#!/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' ' + git rev-parse --show-toplevel >expect && + git worktree add --detach here master && + git -C here rev-parse --show-toplevel >>expect && + git worktree list >actual && + test_cmp expect actual && + rm -rf here && + git worktree prune +' +test_expect_success '"list" all worktrees from linked' ' + 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 >actual && + test_cmp expect actual && + rm -rf here && + git worktree prune +' + +test_done -- 2.5.0 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v3] worktree: add 'list' command 2015-08-10 20:53 ` Michael Rappazzo @ 2015-08-10 22:10 ` Junio C Hamano 2015-08-11 11:41 ` Mike Rappazzo 2015-08-11 2:55 ` David Turner 1 sibling, 1 reply; 8+ messages in thread From: Junio C Hamano @ 2015-08-10 22:10 UTC (permalink / raw) To: Michael Rappazzo; +Cc: sunshine, git, Nguyễn Thái Ngọc Duy Michael Rappazzo <rappazzo@gmail.com> writes: > +static int list(int ac, const char **av, const char *prefix) > +{ > + int main_only = 0; > + struct option options[] = { > + OPT_BOOL(0, "main-only", &main_only, N_("only list the main worktree")), > + OPT_END() > + }; Hmm, main-only is still there? > + > + ac = parse_options(ac, av, prefix, options, worktree_usage, 0); > + if (ac) > + usage_with_options(worktree_usage, options); > + > + struct strbuf main_path = STRBUF_INIT; > + const char* common_dir = get_git_common_dir(); Asterisks stick to the variable names, not types. > + int is_bare = is_bare_repository(); Please do not introduce decl-after-stmt. > + if (is_bare) { > + strbuf_addstr(&main_path, absolute_path(common_dir)); Hmm, interesting. Because .git/config is shared, core.bare read from that tells us if the "main" one is bare, even if you start this command from one of its linked worktrees. So in that sense, this test of is_bare correctly tells if "main" one is a bare repository. But that by itself feels wrong. Doesn't the presense of a working tree mean that you should not get "is_bare==true" in such a case (i.e. your "main" one is bare, you are in a linked worktree of it that has the index and the working tree)? Duy? Eric? What do you guys think? There are many codepaths that change their behaviour (e.g. if we create reflogs by default) based on the return value of is_bare_repository(). If I am reading this correctly, I _think_ a new working area that was prepared with "git worktree add" out of a bare repository would not work well, as these operations behave as if we do not have a working tree. Perhaps is_bare_repository() in such a working area _should_ say "No", even if core.bare in the shared bare one is set to true. > + strbuf_strip_suffix(&main_path, "/."); In any case, what is that stripping of "/." about? Who is adding that extra trailing string? What I am getting at is (1) perhaps it shouldn't be adding that in the first place, and (2) if some other code is randomly adding "/." at the end, what guarantees you that you would need to strip it only once here---if the other code added that twice, don't you have to repeatedly remove "/." from the end? ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v3] worktree: add 'list' command 2015-08-10 22:10 ` Junio C Hamano @ 2015-08-11 11:41 ` Mike Rappazzo 2015-08-11 15:46 ` Junio C Hamano 0 siblings, 1 reply; 8+ messages in thread From: Mike Rappazzo @ 2015-08-11 11:41 UTC (permalink / raw) To: Junio C Hamano; +Cc: Eric Sunshine, Git List, Nguyễn Thái Ngọc On Mon, Aug 10, 2015 at 6:10 PM, Junio C Hamano <gitster@pobox.com> wrote: > Michael Rappazzo <rappazzo@gmail.com> writes: > >> +static int list(int ac, const char **av, const char *prefix) >> +{ >> + int main_only = 0; >> + struct option options[] = { >> + OPT_BOOL(0, "main-only", &main_only, N_("only list the main worktree")), >> + OPT_END() >> + }; > > Hmm, main-only is still there? Sorry, I missed that. > >> + int is_bare = is_bare_repository(); > > Please do not introduce decl-after-stmt. Since I reused this value below, I thought it would be acceptable. >> + if (is_bare) { >> + strbuf_addstr(&main_path, absolute_path(common_dir)); > > Hmm, interesting. > > Because .git/config is shared, core.bare read from that tells us if > the "main" one is bare, even if you start this command from one of > its linked worktrees. So in that sense, this test of is_bare > correctly tells if "main" one is a bare repository. > > But that by itself feels wrong. Doesn't the presense of a working > tree mean that you should not get "is_bare==true" in such a case > (i.e. your "main" one is bare, you are in a linked worktree of it > that has the index and the working tree)? Is is even correct for a bare repo to be included in the list? I think that is part of what you are asking here. > >> + strbuf_strip_suffix(&main_path, "/."); > > In any case, what is that stripping of "/." about? Who is adding > that extra trailing string? > > What I am getting at is (1) perhaps it shouldn't be adding that in > the first place, and (2) if some other code is randomly adding "/." > at the end, what guarantees you that you would need to strip it only > once here---if the other code added that twice, don't you have to > repeatedly remove "/." from the end? In the case of a common dir, the value returned is just '.'. I wanted to resolve that to the full path, so I called `absolute_path(common_dir)`. Hence the trailing "/.". Similarly, in the main repo, the common dir is just ".git", and I handled that by using `get_git_work_tree()`. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v3] worktree: add 'list' command 2015-08-11 11:41 ` Mike Rappazzo @ 2015-08-11 15:46 ` Junio C Hamano 0 siblings, 0 replies; 8+ messages in thread From: Junio C Hamano @ 2015-08-11 15:46 UTC (permalink / raw) To: Mike Rappazzo; +Cc: Eric Sunshine, Git List, Nguyễn Thái Ngọc Mike Rappazzo <rappazzo@gmail.com> writes: >>> + int is_bare = is_bare_repository(); >> >> Please do not introduce decl-after-stmt. > > Since I reused this value below, I thought it would be acceptable. Use of a new variable is fine. "Do not declare one in a block after you already wrote statement" is what "decl-after-stmt not allowed" means. In your patch: +static int list(int ac, const char **av, const char *prefix) +{ + int main_only = 0; + struct option options[] = { + OPT_BOOL(0, "main-only", &main_only, N_("only list the main worktree")), + OPT_END() + }; + + ac = parse_options(ac, av, prefix, options, worktree_usage, 0); + if (ac) + usage_with_options(worktree_usage, options); + + struct strbuf main_path = STRBUF_INIT; + const char* common_dir = get_git_common_dir(); + int is_bare = is_bare_repository(); Three variables, main_path, common_dir and is_bare are declared here after statements such as a call to parse_options(). Don't. +static int list(int ac, const char **av, const char *prefix) +{ + int main_only = 0; + struct strbuf main_path = STRBUF_INIT; + const char *common_dir; + int is_bare; + struct option options[] = { + OPT_BOOL(0, "main-only", &main_only, N_("only list the main worktree")), + OPT_END() + }; + + ac = parse_options(ac, av, prefix, options, worktree_usage, 0); + if (ac) + usage_with_options(worktree_usage, options); + + common_dir = get_git_common_dir(); + int is_bare = is_bare_repository(); ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v3] worktree: add 'list' command 2015-08-10 20:53 ` Michael Rappazzo 2015-08-10 22:10 ` Junio C Hamano @ 2015-08-11 2:55 ` David Turner 2015-08-11 11:42 ` Mike Rappazzo 2015-08-11 15:46 ` Junio C Hamano 1 sibling, 2 replies; 8+ messages in thread From: David Turner @ 2015-08-11 2:55 UTC (permalink / raw) To: Michael Rappazzo; +Cc: gitster, sunshine, git On Mon, 2015-08-10 at 16:53 -0400, Michael Rappazzo wrote: > + while ((d = readdir(dir)) != NULL) { I think it would be useful to break this loop out into a for_each_worktree function. While looking into per-worktree ref stuff, I have just noticed that git prune will delete objects that are only referenced in a different worktree's detached HEAD. To fix this, git prune will need to walk over each worktree, looking at that worktree's HEAD (and other per-worktree refs). It would be useful to be able to reuse some of this code for that task. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v3] worktree: add 'list' command 2015-08-11 2:55 ` David Turner @ 2015-08-11 11:42 ` Mike Rappazzo 2015-08-11 15:46 ` Junio C Hamano 1 sibling, 0 replies; 8+ messages in thread From: Mike Rappazzo @ 2015-08-11 11:42 UTC (permalink / raw) To: David Turner; +Cc: Junio C Hamano, Eric Sunshine, Git List On Mon, Aug 10, 2015 at 10:55 PM, David Turner <dturner@twopensource.com> wrote: > On Mon, 2015-08-10 at 16:53 -0400, Michael Rappazzo wrote: >> + while ((d = readdir(dir)) != NULL) { > > I think it would be useful to break this loop out into a > for_each_worktree function. > > While looking into per-worktree ref stuff, I have just noticed that git > prune will delete objects that are only referenced in a different > worktree's detached HEAD. To fix this, git prune will need to walk over > each worktree, looking at that worktree's HEAD (and other per-worktree > refs). It would be useful to be able to reuse some of this code for > that task. > I agree, but I will save that for another round. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v3] worktree: add 'list' command 2015-08-11 2:55 ` David Turner 2015-08-11 11:42 ` Mike Rappazzo @ 2015-08-11 15:46 ` Junio C Hamano 1 sibling, 0 replies; 8+ messages in thread From: Junio C Hamano @ 2015-08-11 15:46 UTC (permalink / raw) To: David Turner; +Cc: Michael Rappazzo, sunshine, git David Turner <dturner@twopensource.com> writes: > On Mon, 2015-08-10 at 16:53 -0400, Michael Rappazzo wrote: >> + while ((d = readdir(dir)) != NULL) { > > I think it would be useful to break this loop out into a > for_each_worktree function. Very good point. ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2015-08-11 15:47 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2015-08-10 20:53 [PATCH v3] worktree: add 'list' command Michael Rappazzo 2015-08-10 20:53 ` Michael Rappazzo 2015-08-10 22:10 ` Junio C Hamano 2015-08-11 11:41 ` Mike Rappazzo 2015-08-11 15:46 ` Junio C Hamano 2015-08-11 2:55 ` David Turner 2015-08-11 11:42 ` Mike Rappazzo 2015-08-11 15:46 ` Junio C Hamano
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).