* [PATCH] branch: colorize branches checked out in a linked working tree the same way as the current branch is colorized @ 2018-09-27 15:13 ` Nickolai Belakovski 2018-09-27 15:33 ` Ævar Arnfjörð Bjarmason ` (11 more replies) 0 siblings, 12 replies; 136+ messages in thread From: Nickolai Belakovski @ 2018-09-27 15:13 UTC (permalink / raw) To: git In order to more clearly display which branches are active, the output of git branch is modified to colorize branches checked out in any linked worktrees with the same color as the current branch. This is meant to simplify workflows related to worktree, particularly due to the limitations of not being able to check out the same branch in two worktrees and the inability to delete a branch checked out in a worktree. When performing branch operations like checkout and delete, it would be useful to know more readily if the branches in which the user is interested are already checked out in a worktree. The git worktree list command contains the relevant information, however this is a much less frquently used command than git branch. Signed-off-by: Nickolai Belakovski <nbelakovski@gmail.com> --- Notes: Travis CI results: https://travis-ci.org/nbelakovski/git/builds/432320949 builtin/branch.c | 35 ++++++++++++++++++++++++++++++----- t/t3203-branch-output.sh | 21 +++++++++++++++++++++ 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/builtin/branch.c b/builtin/branch.c index 4fc55c350..65b58ff7c 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -334,11 +334,36 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r struct strbuf local = STRBUF_INIT; struct strbuf remote = STRBUF_INIT; - strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else) %s%%(end)", - branch_get_color(BRANCH_COLOR_CURRENT), - branch_get_color(BRANCH_COLOR_LOCAL)); - strbuf_addf(&remote, " %s", - branch_get_color(BRANCH_COLOR_REMOTE)); + // Prepend the current branch of this worktree with "* " and all other branches with " " + strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %%(else) %%(end)"); + // Prepend remote branches with two spaces + strbuf_addstr(&remote, " "); + if(want_color(branch_use_color)) { + // Create a nested if statement to evaluate if the current ref is equal to a HEAD ref from either + // the main or any linked worktrees. If so, color it CURRENT, otherwise color it LOCAL + struct strbuf color = STRBUF_INIT; + struct worktree **worktrees = get_worktrees(0); + int i; + for (i = 0; worktrees[i]; ++i) { + strbuf_addf(&color, "%%(if:equals=%s)%%(refname)%%(then)%s%%(else)", + worktrees[i]->head_ref, + branch_get_color(BRANCH_COLOR_CURRENT)); + } + // add one more check in the nested if-else to cover the detached HEAD state + strbuf_addf(&color, "%%(if)%%(HEAD)%%(then)%s%%(else)%s%%(end)", + branch_get_color(BRANCH_COLOR_CURRENT), + branch_get_color(BRANCH_COLOR_LOCAL)); + // close up the nested if-else + for (; i > 0; --i) { + strbuf_addf(&color, "%%(end)"); + } + free_worktrees(worktrees); + strbuf_addbuf(&local, &color); + strbuf_release(&color); + + strbuf_addf(&remote, "%s", + branch_get_color(BRANCH_COLOR_REMOTE)); + } if (filter->verbose) { struct strbuf obname = STRBUF_INIT; diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh index ee6787614..369a156c0 100755 --- a/t/t3203-branch-output.sh +++ b/t/t3203-branch-output.sh @@ -240,6 +240,27 @@ test_expect_success 'git branch --format option' ' test_i18ncmp expect actual ' +test_expect_success '"add" a worktree' ' + mkdir worktree_dir && + git worktree add -b master_worktree worktree_dir master +' + +cat >expect <<'EOF' +* <GREEN>(HEAD detached from fromtag)<RESET> + ambiguous<RESET> + branch-one<RESET> + branch-two<RESET> + master<RESET> + <GREEN>master_worktree<RESET> + ref-to-branch<RESET> -> branch-one + ref-to-remote<RESET> -> origin/branch-one +EOF +test_expect_success TTY 'worktree colors correct' ' + test_terminal git branch >actual.raw && + test_decode_color <actual.raw >actual && + test_cmp expect actual +' + test_expect_success "set up color tests" ' echo "<RED>master<RESET>" >expect.color && echo "master" >expect.bare && -- 2.14.2 ^ permalink raw reply related [flat|nested] 136+ messages in thread
* Re: [PATCH] branch: colorize branches checked out in a linked working tree the same way as the current branch is colorized 2018-09-27 15:13 ` [PATCH] branch: colorize branches checked out in a linked working tree the same way as the current branch is colorized Nickolai Belakovski @ 2018-09-27 15:33 ` Ævar Arnfjörð Bjarmason 2018-09-27 17:46 ` Nickolai Belakovski [not found] ` <CAC05387S9P+w8yqqcjkQDnURYSgQmqtukxS4KvqJu-kDA+_o0g@mail.gmail.com> 2018-09-27 17:58 ` Duy Nguyen ` (10 subsequent siblings) 11 siblings, 2 replies; 136+ messages in thread From: Ævar Arnfjörð Bjarmason @ 2018-09-27 15:33 UTC (permalink / raw) To: Nickolai Belakovski; +Cc: git On Thu, Sep 27 2018, Nickolai Belakovski wrote: > In order to more clearly display which branches are active, the output > of git branch is modified to colorize branches checked out in any linked > worktrees with the same color as the current branch. > > This is meant to simplify workflows related to worktree, particularly > due to the limitations of not being able to check out the same branch in > two worktrees and the inability to delete a branch checked out in a > worktree. When performing branch operations like checkout and delete, it > would be useful to know more readily if the branches in which the user > is interested are already checked out in a worktree. > > The git worktree list command contains the relevant information, however > this is a much less frquently used command than git branch. > > Signed-off-by: Nickolai Belakovski <nbelakovski@gmail.com> Sounds cool, b.t.w. would be neat-o to have some screenshot uploaded to imgur or whatever just to skim what it looks like before/after. > diff --git a/builtin/branch.c b/builtin/branch.c > index 4fc55c350..65b58ff7c 100644 > --- a/builtin/branch.c > +++ b/builtin/branch.c > @@ -334,11 +334,36 @@ static char *build_format(struct ref_filter > *filter, int maxwidth, const char *r > struct strbuf local = STRBUF_INIT; > struct strbuf remote = STRBUF_INIT; > > - strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else) %s%%(end)", > - branch_get_color(BRANCH_COLOR_CURRENT), > - branch_get_color(BRANCH_COLOR_LOCAL)); > - strbuf_addf(&remote, " %s", > - branch_get_color(BRANCH_COLOR_REMOTE)); > + // Prepend the current branch of this worktree with "* " and > all other branches with " " We use /* ... */ C comments, not C++-style // (well, it's in C now, but not the ancient versions we need to support). It also seems all of this patch was copy/pasted into GMail or something, it has wrapping and doesn't apply with "git am". Also most/all of these comments I'd say we could better do without, i.e. the ones explaining basic code flow that's easy to see from the code itself. ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH] branch: colorize branches checked out in a linked working tree the same way as the current branch is colorized 2018-09-27 15:33 ` Ævar Arnfjörð Bjarmason @ 2018-09-27 17:46 ` Nickolai Belakovski [not found] ` <CAC05387S9P+w8yqqcjkQDnURYSgQmqtukxS4KvqJu-kDA+_o0g@mail.gmail.com> 1 sibling, 0 replies; 136+ messages in thread From: Nickolai Belakovski @ 2018-09-27 17:46 UTC (permalink / raw) To: avarab; +Cc: git Will do re: screenshot when I get home, although it's pretty easy to imagine, the git branch output will have one other branch colored in green, bit without the asterisk (for one linked worktree) :) Also will do re: changing comments to /**/ (didn't know // was from C++, TIL) and I'll clean up the comments to remove some of the more obvious ones, but I'll try to keep a comment explaining the basic flow of creating a nest if statement to evaluate worktree refs for color. And yes, I copy/pasted into gmail. I was having trouble setting up send-email, but I think I may have it figured out now. Should I create a new thread with send-email? Or maybe reply to this one (I can do that by specifying the Message-ID to reply to right? This is my first time using this workflow, so I appreciate your patience :) )? Thanks for the feedback! On Thu, Sep 27, 2018 at 8:33 AM Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote: > > > On Thu, Sep 27 2018, Nickolai Belakovski wrote: > > > In order to more clearly display which branches are active, the output > > of git branch is modified to colorize branches checked out in any linked > > worktrees with the same color as the current branch. > > > > This is meant to simplify workflows related to worktree, particularly > > due to the limitations of not being able to check out the same branch in > > two worktrees and the inability to delete a branch checked out in a > > worktree. When performing branch operations like checkout and delete, it > > would be useful to know more readily if the branches in which the user > > is interested are already checked out in a worktree. > > > > The git worktree list command contains the relevant information, however > > this is a much less frquently used command than git branch. > > > > Signed-off-by: Nickolai Belakovski <nbelakovski@gmail.com> > > Sounds cool, b.t.w. would be neat-o to have some screenshot uploaded to > imgur or whatever just to skim what it looks like before/after. > > > diff --git a/builtin/branch.c b/builtin/branch.c > > index 4fc55c350..65b58ff7c 100644 > > --- a/builtin/branch.c > > +++ b/builtin/branch.c > > @@ -334,11 +334,36 @@ static char *build_format(struct ref_filter > > *filter, int maxwidth, const char *r > > struct strbuf local = STRBUF_INIT; > > struct strbuf remote = STRBUF_INIT; > > > > - strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else) %s%%(end)", > > - branch_get_color(BRANCH_COLOR_CURRENT), > > - branch_get_color(BRANCH_COLOR_LOCAL)); > > - strbuf_addf(&remote, " %s", > > - branch_get_color(BRANCH_COLOR_REMOTE)); > > + // Prepend the current branch of this worktree with "* " and > > all other branches with " " > > > We use /* ... */ C comments, not C++-style // (well, it's in C now, but > not the ancient versions we need to support). > > It also seems all of this patch was copy/pasted into GMail or something, > it has wrapping and doesn't apply with "git am". > > Also most/all of these comments I'd say we could better do without, > i.e. the ones explaining basic code flow that's easy to see from the > code itself. ^ permalink raw reply [flat|nested] 136+ messages in thread
[parent not found: <CAC05387S9P+w8yqqcjkQDnURYSgQmqtukxS4KvqJu-kDA+_o0g@mail.gmail.com>]
* Re: [PATCH] branch: colorize branches checked out in a linked working tree the same way as the current branch is colorized [not found] ` <CAC05387S9P+w8yqqcjkQDnURYSgQmqtukxS4KvqJu-kDA+_o0g@mail.gmail.com> @ 2018-09-27 17:59 ` Ævar Arnfjörð Bjarmason 2018-10-02 20:41 ` Johannes Schindelin 0 siblings, 1 reply; 136+ messages in thread From: Ævar Arnfjörð Bjarmason @ 2018-09-27 17:59 UTC (permalink / raw) To: Nickolai Belakovski; +Cc: git, Johannes Schindelin, Derrick Stolee On Thu, Sep 27 2018, Nickolai Belakovski wrote: > Will do re: screenshot when I get home, although it's pretty easy to > imagine, the git branch output will have one other branch colored in green, > bit without the asterisk (for one linked worktree) :) > > Also will do re: changing comments to /**/ (didn't know // was from C++, > TIL) and I'll clean up the comments to remove some of the more obvious > ones, but I'll try to keep a comment explaining the basic flow of creating > a nest if statement to evaluate worktree refs for color. > > And yes, I copy/pasted into gmail. I was having trouble setting up > send-email, but I think I may have it figured out now. Should I create a > new thread with send-email? Or maybe reply to this one (I can do that by > specifying the Message-ID to reply to right? You'd run git format-patch master..your-topic with --subject-prefix="PATCH v2" and --in-reply-to="<CAC05386q2iGoiJ_fRgwoOTF23exEN2D1+oh4VjajEvYQ58O1TQ@mail.gmail.com>". Then it'll show up in reply to your v1. You can also for an easier experience do this via GitGitGadget, see https://github.com/gitgitgadget/gitgitgadget looking at its code it seems to have some way to reference a Message-ID, but I don't know how to trigger that. > This is my first time using this workflow, so I appreciate your > patience :) )? No worries, happy to help. > On Thu, Sep 27, 2018 at 8:33 AM Ævar Arnfjörð Bjarmason <avarab@gmail.com> > wrote: > >> >> On Thu, Sep 27 2018, Nickolai Belakovski wrote: >> >> > In order to more clearly display which branches are active, the output >> > of git branch is modified to colorize branches checked out in any linked >> > worktrees with the same color as the current branch. >> > >> > This is meant to simplify workflows related to worktree, particularly >> > due to the limitations of not being able to check out the same branch in >> > two worktrees and the inability to delete a branch checked out in a >> > worktree. When performing branch operations like checkout and delete, it >> > would be useful to know more readily if the branches in which the user >> > is interested are already checked out in a worktree. >> > >> > The git worktree list command contains the relevant information, however >> > this is a much less frquently used command than git branch. >> > >> > Signed-off-by: Nickolai Belakovski <nbelakovski@gmail.com> >> >> Sounds cool, b.t.w. would be neat-o to have some screenshot uploaded to >> imgur or whatever just to skim what it looks like before/after. >> >> > diff --git a/builtin/branch.c b/builtin/branch.c >> > index 4fc55c350..65b58ff7c 100644 >> > --- a/builtin/branch.c >> > +++ b/builtin/branch.c >> > @@ -334,11 +334,36 @@ static char *build_format(struct ref_filter >> > *filter, int maxwidth, const char *r >> > struct strbuf local = STRBUF_INIT; >> > struct strbuf remote = STRBUF_INIT; >> > >> > - strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else) >> %s%%(end)", >> > - branch_get_color(BRANCH_COLOR_CURRENT), >> > - branch_get_color(BRANCH_COLOR_LOCAL)); >> > - strbuf_addf(&remote, " %s", >> > - branch_get_color(BRANCH_COLOR_REMOTE)); >> > + // Prepend the current branch of this worktree with "* " and >> > all other branches with " " >> >> >> We use /* ... */ C comments, not C++-style // (well, it's in C now, but >> not the ancient versions we need to support). >> >> It also seems all of this patch was copy/pasted into GMail or something, >> it has wrapping and doesn't apply with "git am". >> >> Also most/all of these comments I'd say we could better do without, >> i.e. the ones explaining basic code flow that's easy to see from the >> code itself. >> ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH] branch: colorize branches checked out in a linked working tree the same way as the current branch is colorized 2018-09-27 17:59 ` Ævar Arnfjörð Bjarmason @ 2018-10-02 20:41 ` Johannes Schindelin 0 siblings, 0 replies; 136+ messages in thread From: Johannes Schindelin @ 2018-10-02 20:41 UTC (permalink / raw) To: Ævar Arnfjörð Bjarmason Cc: Nickolai Belakovski, git, Derrick Stolee [-- Attachment #1: Type: text/plain, Size: 4674 bytes --] Hi Ævar, On Thu, 27 Sep 2018, Ævar Arnfjörð Bjarmason wrote: > On Thu, Sep 27 2018, Nickolai Belakovski wrote: > > > Will do re: screenshot when I get home, although it's pretty easy to > > imagine, the git branch output will have one other branch colored in green, > > bit without the asterisk (for one linked worktree) :) > > > > Also will do re: changing comments to /**/ (didn't know // was from C++, > > TIL) and I'll clean up the comments to remove some of the more obvious > > ones, but I'll try to keep a comment explaining the basic flow of creating > > a nest if statement to evaluate worktree refs for color. > > > > And yes, I copy/pasted into gmail. I was having trouble setting up > > send-email, but I think I may have it figured out now. Should I create a > > new thread with send-email? Or maybe reply to this one (I can do that by > > specifying the Message-ID to reply to right? > > You'd run git format-patch master..your-topic with > --subject-prefix="PATCH v2" and > --in-reply-to="<CAC05386q2iGoiJ_fRgwoOTF23exEN2D1+oh4VjajEvYQ58O1TQ@mail.gmail.com>". Then > it'll show up in reply to your v1. There is also a nice tutorial in https://github.com/git-for-windows/git/blob/master/CONTRIBUTING.md#submit-your-patch (which, contrary to the location, is useful for non-Windows developers, too.) > You can also for an easier experience do this via GitGitGadget, see > https://github.com/gitgitgadget/gitgitgadget looking at its code it > seems to have some way to reference a Message-ID, but I don't know how > to trigger that. IIRC GitGitGadget has no facility yet to reply to any mail it did not generate itself (i.e. if you did not generate v1 using GitGitGadget, then it cannot generate a v2 that replies to the previous iteration). This might change at some stage, but I have other priorities for now. (Which should not stop any contributor from opening a PR to scratch their own favorite itch.) Ciao, Johannes > > > This is my first time using this workflow, so I appreciate your > > patience :) )? > > No worries, happy to help. > > > On Thu, Sep 27, 2018 at 8:33 AM Ævar Arnfjörð Bjarmason <avarab@gmail.com> > > wrote: > > > >> > >> On Thu, Sep 27 2018, Nickolai Belakovski wrote: > >> > >> > In order to more clearly display which branches are active, the output > >> > of git branch is modified to colorize branches checked out in any linked > >> > worktrees with the same color as the current branch. > >> > > >> > This is meant to simplify workflows related to worktree, particularly > >> > due to the limitations of not being able to check out the same branch in > >> > two worktrees and the inability to delete a branch checked out in a > >> > worktree. When performing branch operations like checkout and delete, it > >> > would be useful to know more readily if the branches in which the user > >> > is interested are already checked out in a worktree. > >> > > >> > The git worktree list command contains the relevant information, however > >> > this is a much less frquently used command than git branch. > >> > > >> > Signed-off-by: Nickolai Belakovski <nbelakovski@gmail.com> > >> > >> Sounds cool, b.t.w. would be neat-o to have some screenshot uploaded to > >> imgur or whatever just to skim what it looks like before/after. > >> > >> > diff --git a/builtin/branch.c b/builtin/branch.c > >> > index 4fc55c350..65b58ff7c 100644 > >> > --- a/builtin/branch.c > >> > +++ b/builtin/branch.c > >> > @@ -334,11 +334,36 @@ static char *build_format(struct ref_filter > >> > *filter, int maxwidth, const char *r > >> > struct strbuf local = STRBUF_INIT; > >> > struct strbuf remote = STRBUF_INIT; > >> > > >> > - strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else) > >> %s%%(end)", > >> > - branch_get_color(BRANCH_COLOR_CURRENT), > >> > - branch_get_color(BRANCH_COLOR_LOCAL)); > >> > - strbuf_addf(&remote, " %s", > >> > - branch_get_color(BRANCH_COLOR_REMOTE)); > >> > + // Prepend the current branch of this worktree with "* " and > >> > all other branches with " " > >> > >> > >> We use /* ... */ C comments, not C++-style // (well, it's in C now, but > >> not the ancient versions we need to support). > >> > >> It also seems all of this patch was copy/pasted into GMail or something, > >> it has wrapping and doesn't apply with "git am". > >> > >> Also most/all of these comments I'd say we could better do without, > >> i.e. the ones explaining basic code flow that's easy to see from the > >> code itself. > >> > ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH] branch: colorize branches checked out in a linked working tree the same way as the current branch is colorized 2018-09-27 15:13 ` [PATCH] branch: colorize branches checked out in a linked working tree the same way as the current branch is colorized Nickolai Belakovski 2018-09-27 15:33 ` Ævar Arnfjörð Bjarmason @ 2018-09-27 17:58 ` Duy Nguyen 2018-09-27 18:17 ` Jeff King ` (9 subsequent siblings) 11 siblings, 0 replies; 136+ messages in thread From: Duy Nguyen @ 2018-09-27 17:58 UTC (permalink / raw) To: nbelakovski; +Cc: Git Mailing List On Thu, Sep 27, 2018 at 5:15 PM Nickolai Belakovski <nbelakovski@gmail.com> wrote: > > In order to more clearly display which branches are active, the output > of git branch is modified to colorize branches checked out in any linked > worktrees with the same color as the current branch. My first thought was "how do I know which branch I'm on then if they are all green?" but then the current worktree's branch would have a "*" in front while other worktree's do not. Perhaps worth mentioning in the commit message. It may be better though to have a different color code for other worktree's branch, we can still default the color to green, but people who rely on colors rather than "*" can choose a different color. -- Duy ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH] branch: colorize branches checked out in a linked working tree the same way as the current branch is colorized 2018-09-27 15:13 ` [PATCH] branch: colorize branches checked out in a linked working tree the same way as the current branch is colorized Nickolai Belakovski 2018-09-27 15:33 ` Ævar Arnfjörð Bjarmason 2018-09-27 17:58 ` Duy Nguyen @ 2018-09-27 18:17 ` Jeff King 2018-09-27 18:39 ` Nickolai Belakovski 2018-09-27 19:28 ` Rafael Ascensão 2019-01-06 0:26 ` [PATCH v5 0/3] nbelakovski ` (8 subsequent siblings) 11 siblings, 2 replies; 136+ messages in thread From: Jeff King @ 2018-09-27 18:17 UTC (permalink / raw) To: Nickolai Belakovski; +Cc: git On Thu, Sep 27, 2018 at 08:13:13AM -0700, Nickolai Belakovski wrote: > In order to more clearly display which branches are active, the output > of git branch is modified to colorize branches checked out in any linked > worktrees with the same color as the current branch. I think the goal makes sense. Do we want to limit this to git-branch, though? Ideally any output you get from git-branch could be replicated with for-each-ref (or with a custom "branch --format"). I.e., could we have a format in ref-filter that matches HEAD, but returns a distinct symbol for a worktree HEAD? That would allow a few things: - custom --formats for for-each-ref and branch could reuse the logic - we could show the symbol (in place of "*") even when color is not enabled - it should be much faster if there are a lot of worktrees; your patch does a linear if/else chain to look at each worktree, and it does it in the format-language, which is much slower than actual C. :) Something like the patch below. I just picked "+" arbitrarily, but any character would do (I avoided "*" just to make it visually distinct from the current-worktree HEAD). I've left plugging this into git-branch's default format as an exercise for the reader. ;) --- diff --git a/ref-filter.c b/ref-filter.c index e1bcb4ca8a..b17eefed0d 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -20,6 +20,7 @@ #include "commit-slab.h" #include "commit-graph.h" #include "commit-reach.h" +#include "worktree.h" static struct ref_msg { const char *gone; @@ -114,6 +115,7 @@ static struct used_atom { } objectname; struct refname_atom refname; char *head; + struct string_list worktree_heads; } u; } *used_atom; static int used_atom_cnt, need_tagged, need_symref; @@ -420,6 +422,29 @@ static int head_atom_parser(const struct ref_format *format, struct used_atom *a return 0; } +static int worktree_head_atom_parser(const struct ref_format *format, + struct used_atom *atom, + const char *arg, + struct strbuf *unused_err) +{ + struct worktree **worktrees = get_worktrees(0); + int i; + + string_list_init(&atom->u.worktree_heads, 1); + + for (i = 0; worktrees[i]; i++) { + if (worktrees[i]->head_ref) + string_list_append(&atom->u.worktree_heads, + worktrees[i]->head_ref); + } + + string_list_sort(&atom->u.worktree_heads); + + free_worktrees(worktrees); + return 0; + +} + static struct { const char *name; info_source source; @@ -460,6 +485,7 @@ static struct { { "symref", SOURCE_NONE, FIELD_STR, refname_atom_parser }, { "flag", SOURCE_NONE }, { "HEAD", SOURCE_NONE, FIELD_STR, head_atom_parser }, + { "worktree", SOURCE_NONE, FIELD_STR, worktree_head_atom_parser }, { "color", SOURCE_NONE, FIELD_STR, color_atom_parser }, { "align", SOURCE_NONE, FIELD_STR, align_atom_parser }, { "end", SOURCE_NONE }, @@ -1588,6 +1614,13 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err) else v->s = " "; continue; + } else if (!strcmp(name, "worktree")) { + if (string_list_has_string(&atom->u.worktree_heads, + ref->refname)) + v->s = "+"; + else + v->s = " "; + continue; } else if (starts_with(name, "align")) { v->handler = align_atom_handler; v->s = ""; ^ permalink raw reply related [flat|nested] 136+ messages in thread
* Re: [PATCH] branch: colorize branches checked out in a linked working tree the same way as the current branch is colorized 2018-09-27 18:17 ` Jeff King @ 2018-09-27 18:39 ` Nickolai Belakovski 2018-09-27 18:51 ` Jeff King 2018-09-27 19:28 ` Rafael Ascensão 1 sibling, 1 reply; 136+ messages in thread From: Nickolai Belakovski @ 2018-09-27 18:39 UTC (permalink / raw) To: Jeff King; +Cc: git Thanks for the feedback Peff. I actually agree with all your points. I'd considered an approach like what you proposed, but rejected it for the first iteration in an effort to keep scope limited and see what kind of feedback I'd get overall (like would people even want this?). This is a much better approach, and also gives a path for listing the worktree path in the verbose output. @Duy yea we can use a different color, maybe a darker shade of green. I saw plenty to choose from in the color list so I'll play around with it. It would definitely make it easier to distinguish at a glance which branch is checked out in the current worktree vs others. On Thu, Sep 27, 2018 at 11:17 AM Jeff King <peff@peff.net> wrote: > > On Thu, Sep 27, 2018 at 08:13:13AM -0700, Nickolai Belakovski wrote: > > > In order to more clearly display which branches are active, the output > > of git branch is modified to colorize branches checked out in any linked > > worktrees with the same color as the current branch. > > I think the goal makes sense. > > Do we want to limit this to git-branch, though? Ideally any output you > get from git-branch could be replicated with for-each-ref (or with > a custom "branch --format"). > > I.e., could we have a format in ref-filter that matches HEAD, but > returns a distinct symbol for a worktree HEAD? That would allow a few > things: > > - custom --formats for for-each-ref and branch could reuse the logic > > - we could show the symbol (in place of "*") even when color is not > enabled > > - it should be much faster if there are a lot of worktrees; your patch > does a linear if/else chain to look at each worktree, and it does it > in the format-language, which is much slower than actual C. :) > > Something like the patch below. I just picked "+" arbitrarily, but any > character would do (I avoided "*" just to make it visually distinct from > the current-worktree HEAD). I've left plugging this into git-branch's > default format as an exercise for the reader. ;) > > --- > diff --git a/ref-filter.c b/ref-filter.c > index e1bcb4ca8a..b17eefed0d 100644 > --- a/ref-filter.c > +++ b/ref-filter.c > @@ -20,6 +20,7 @@ > #include "commit-slab.h" > #include "commit-graph.h" > #include "commit-reach.h" > +#include "worktree.h" > > static struct ref_msg { > const char *gone; > @@ -114,6 +115,7 @@ static struct used_atom { > } objectname; > struct refname_atom refname; > char *head; > + struct string_list worktree_heads; > } u; > } *used_atom; > static int used_atom_cnt, need_tagged, need_symref; > @@ -420,6 +422,29 @@ static int head_atom_parser(const struct ref_format *format, struct used_atom *a > return 0; > } > > +static int worktree_head_atom_parser(const struct ref_format *format, > + struct used_atom *atom, > + const char *arg, > + struct strbuf *unused_err) > +{ > + struct worktree **worktrees = get_worktrees(0); > + int i; > + > + string_list_init(&atom->u.worktree_heads, 1); > + > + for (i = 0; worktrees[i]; i++) { > + if (worktrees[i]->head_ref) > + string_list_append(&atom->u.worktree_heads, > + worktrees[i]->head_ref); > + } > + > + string_list_sort(&atom->u.worktree_heads); > + > + free_worktrees(worktrees); > + return 0; > + > +} > + > static struct { > const char *name; > info_source source; > @@ -460,6 +485,7 @@ static struct { > { "symref", SOURCE_NONE, FIELD_STR, refname_atom_parser }, > { "flag", SOURCE_NONE }, > { "HEAD", SOURCE_NONE, FIELD_STR, head_atom_parser }, > + { "worktree", SOURCE_NONE, FIELD_STR, worktree_head_atom_parser }, > { "color", SOURCE_NONE, FIELD_STR, color_atom_parser }, > { "align", SOURCE_NONE, FIELD_STR, align_atom_parser }, > { "end", SOURCE_NONE }, > @@ -1588,6 +1614,13 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err) > else > v->s = " "; > continue; > + } else if (!strcmp(name, "worktree")) { > + if (string_list_has_string(&atom->u.worktree_heads, > + ref->refname)) > + v->s = "+"; > + else > + v->s = " "; > + continue; > } else if (starts_with(name, "align")) { > v->handler = align_atom_handler; > v->s = ""; ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH] branch: colorize branches checked out in a linked working tree the same way as the current branch is colorized 2018-09-27 18:39 ` Nickolai Belakovski @ 2018-09-27 18:51 ` Jeff King 0 siblings, 0 replies; 136+ messages in thread From: Jeff King @ 2018-09-27 18:51 UTC (permalink / raw) To: Nickolai Belakovski; +Cc: git On Thu, Sep 27, 2018 at 11:39:26AM -0700, Nickolai Belakovski wrote: > Thanks for the feedback Peff. I actually agree with all your points. > I'd considered an approach like what you proposed, but rejected it for > the first iteration in an effort to keep scope limited and see what > kind of feedback I'd get overall (like would people even want this?). > This is a much better approach, and also gives a path for listing the > worktree path in the verbose output. Great. If you go that route, feel free to use whatever bits of my patch are useful. I tested it only by running "for-each-ref" once, so it might need some more help. Definitely tests and documentation at the least. :) -Peff ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH] branch: colorize branches checked out in a linked working tree the same way as the current branch is colorized 2018-09-27 18:17 ` Jeff King 2018-09-27 18:39 ` Nickolai Belakovski @ 2018-09-27 19:28 ` Rafael Ascensão 2018-09-27 19:35 ` Jeff King 2018-09-27 20:02 ` Ævar Arnfjörð Bjarmason 1 sibling, 2 replies; 136+ messages in thread From: Rafael Ascensão @ 2018-09-27 19:28 UTC (permalink / raw) To: Jeff King; +Cc: Nickolai Belakovski, git On Thu, Sep 27, 2018 at 02:17:08PM -0400, Jeff King wrote: > Do we want to limit this to git-branch, though? Ideally any output you > get from git-branch could be replicated with for-each-ref (or with > a custom "branch --format"). > > I.e., could we have a format in ref-filter that matches HEAD, but > returns a distinct symbol for a worktree HEAD? That would allow a few > things: I was going to suggest using dim green and green for elsewhere and here respectively, in a similar way how range-diff uses it to show different versions of the same diff. But if we're open to change how branches are displayed maybe a config option like branch.format (probably not the best name choice) that can be set to the 'for-each-ref --format' syntax would be way more flexible. e.g. branch.format='%(if:equals=+)...' I think the different symbol and dimmed color would be a nice addition, but I am leaning towards giving the user the ultimate choice on how they want to format their output. (Maybe with dimmed plus symbol as default). -- Cheers, Rafael Ascensão ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH] branch: colorize branches checked out in a linked working tree the same way as the current branch is colorized 2018-09-27 19:28 ` Rafael Ascensão @ 2018-09-27 19:35 ` Jeff King 2018-09-27 19:41 ` Jeff King 2018-09-27 21:35 ` Rafael Ascensão 2018-09-27 20:02 ` Ævar Arnfjörð Bjarmason 1 sibling, 2 replies; 136+ messages in thread From: Jeff King @ 2018-09-27 19:35 UTC (permalink / raw) To: Rafael Ascensão; +Cc: Nickolai Belakovski, git On Thu, Sep 27, 2018 at 08:28:04PM +0100, Rafael Ascensão wrote: > On Thu, Sep 27, 2018 at 02:17:08PM -0400, Jeff King wrote: > > Do we want to limit this to git-branch, though? Ideally any output you > > get from git-branch could be replicated with for-each-ref (or with > > a custom "branch --format"). > > > > I.e., could we have a format in ref-filter that matches HEAD, but > > returns a distinct symbol for a worktree HEAD? That would allow a few > > things: > > I was going to suggest using dim green and green for elsewhere and here > respectively, in a similar way how range-diff uses it to show different > versions of the same diff. Yeah, I think that's reasonable, and would be enabled by the %(worktree) placeholder I showed. Just like we do something like: %(if)%(HEAD)%(then)* %(color:green)%(else) %(end) now, we could do: %(if)%(HEAD)%(then)* %(color:bold green) %(else)%(if)%(worktree)%(then)+ %(color:green) %(else) %(end)%(end) (respecting the user's color config, of course, rather than hard-coded colors). Trying that out, though, I'm not sure if we properly support nested if's. That might be a bug we have to fix first. > But if we're open to change how branches are displayed maybe a config > option like branch.format (probably not the best name choice) that can > be set to the 'for-each-ref --format' syntax would be way more flexible. We have that already, don't we? > I think the different symbol and dimmed color would be a nice addition, > but I am leaning towards giving the user the ultimate choice on how they > want to format their output. (Maybe with dimmed plus symbol as default). Definitely. -Peff ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH] branch: colorize branches checked out in a linked working tree the same way as the current branch is colorized 2018-09-27 19:35 ` Jeff King @ 2018-09-27 19:41 ` Jeff King 2018-09-27 21:22 ` Junio C Hamano 2018-09-27 21:35 ` Rafael Ascensão 1 sibling, 1 reply; 136+ messages in thread From: Jeff King @ 2018-09-27 19:41 UTC (permalink / raw) To: Rafael Ascensão; +Cc: Nickolai Belakovski, git On Thu, Sep 27, 2018 at 03:35:59PM -0400, Jeff King wrote: > now, we could do: > > %(if)%(HEAD)%(then)* %(color:bold green) > %(else)%(if)%(worktree)%(then)+ %(color:green) > %(else) %(end)%(end) > > (respecting the user's color config, of course, rather than hard-coded > colors). > > Trying that out, though, I'm not sure if we properly support nested > if's. That might be a bug we have to fix first. Sorry, false alarm. I just had a typo in my format. This seems to work with the patch I posted earlier: git for-each-ref \ --format='%(if)%(HEAD)%(then)* %(color:bold green)%(else)%(if)%(worktree)%(then)+ %(color:green)%(else) %(end)%(end)%(refname)' \ refs/heads It sure would be nice if there was a way to insert line breaks without impacting the output. ;) -Peff ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH] branch: colorize branches checked out in a linked working tree the same way as the current branch is colorized 2018-09-27 19:41 ` Jeff King @ 2018-09-27 21:22 ` Junio C Hamano 2018-09-28 1:05 ` Jeff King 0 siblings, 1 reply; 136+ messages in thread From: Junio C Hamano @ 2018-09-27 21:22 UTC (permalink / raw) To: Jeff King; +Cc: Rafael Ascensão, Nickolai Belakovski, git Jeff King <peff@peff.net> writes: > On Thu, Sep 27, 2018 at 03:35:59PM -0400, Jeff King wrote: > >> now, we could do: >> >> %(if)%(HEAD)%(then)* %(color:bold green) >> %(else)%(if)%(worktree)%(then)+ %(color:green) >> %(else) %(end)%(end) >> >> (respecting the user's color config, of course, rather than hard-coded >> colors). >> >> Trying that out, though, I'm not sure if we properly support nested >> if's. That might be a bug we have to fix first. > > Sorry, false alarm. I just had a typo in my format. > > This seems to work with the patch I posted earlier: > > git for-each-ref \ > --format='%(if)%(HEAD)%(then)* %(color:bold green)%(else)%(if)%(worktree)%(then)+ %(color:green)%(else) %(end)%(end)%(refname)' \ > refs/heads > > It sure would be nice if there was a way to insert line breaks without > impacting the output. ;) > > -Peff I envy all of you who seem to have had a lot of fun while I was doing something else (which were rather boring but still needed precision--my least favorite kind of task X-<). The only comment I have is that I strongly suspect we will regret if we used an overly bland "worktree" to a rather narrrow "is this ref checked out in any worktree?" when we notice we want to learn other things that are related to "worktree". Other than that, very nicely done. ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH] branch: colorize branches checked out in a linked working tree the same way as the current branch is colorized 2018-09-27 21:22 ` Junio C Hamano @ 2018-09-28 1:05 ` Jeff King 2018-09-28 1:28 ` Junio C Hamano 0 siblings, 1 reply; 136+ messages in thread From: Jeff King @ 2018-09-28 1:05 UTC (permalink / raw) To: Junio C Hamano; +Cc: Rafael Ascensão, Nickolai Belakovski, git On Thu, Sep 27, 2018 at 02:22:49PM -0700, Junio C Hamano wrote: > The only comment I have is that I strongly suspect we will regret if > we used an overly bland "worktree" to a rather narrrow "is this ref > checked out in any worktree?" when we notice we want to learn other > things that are related to "worktree". Other than that, very nicely > done. Yeah, I should have mentioned that. %(worktree) was just a placeholder. Perhaps something like %(worktree-HEAD) would make more sense (the idea is that it is an extension of the existing %(HEAD) placeholder). Alternatively, %(HEAD) could return "*" or "+" depending on whether it's the current worktree head. That would mildly break an existing format like: %(if)%(HEAD)%(then) *%(color:green)%(end)%(refname) since it would start coloring worktree HEADs the same way. It would be rewritten as: %(if:equals=*)%(HEAD)%(then)...real HEAD... %(else)%(if:equals=+)%(HEAD)%(then)...worktree HEAD... %(else)...regular ref... %(end)%(end) I think that's perhaps nicer, but I'm not sure we want even such a minor regression. -Peff ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH] branch: colorize branches checked out in a linked working tree the same way as the current branch is colorized 2018-09-28 1:05 ` Jeff King @ 2018-09-28 1:28 ` Junio C Hamano 0 siblings, 0 replies; 136+ messages in thread From: Junio C Hamano @ 2018-09-28 1:28 UTC (permalink / raw) To: Jeff King; +Cc: Rafael Ascensão, Nickolai Belakovski, git Jeff King <peff@peff.net> writes: > Alternatively, %(HEAD) could return "*" or "+" depending on whether it's > the current worktree head. That would mildly break an existing format > like: > > %(if)%(HEAD)%(then) *%(color:green)%(end)%(refname) > > since it would start coloring worktree HEADs the same way. It would be > rewritten as: > > %(if:equals=*)%(HEAD)%(then)...real HEAD... > %(else)%(if:equals=+)%(HEAD)%(then)...worktree HEAD... > %(else)...regular ref... > %(end)%(end) > > I think that's perhaps nicer, but I'm not sure we want even such a minor > regression. I tend to think it is not worth having to worry about it by changing the meaning of %(HEAD) marking to save the effort to find a new token to fill that placeholder. Your %(worktreeHEAD) is good enough, I would think. ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH] branch: colorize branches checked out in a linked working tree the same way as the current branch is colorized 2018-09-27 19:35 ` Jeff King 2018-09-27 19:41 ` Jeff King @ 2018-09-27 21:35 ` Rafael Ascensão 2018-09-28 1:07 ` Jeff King 1 sibling, 1 reply; 136+ messages in thread From: Rafael Ascensão @ 2018-09-27 21:35 UTC (permalink / raw) To: Jeff King; +Cc: Nickolai Belakovski, git On Thu, Sep 27, 2018 at 03:35:59PM -0400, Jeff King wrote: > On Thu, Sep 27, 2018 at 08:28:04PM +0100, Rafael Ascensão wrote: > > But if we're open to change how branches are displayed maybe a config > > option like branch.format (probably not the best name choice) that can > > be set to the 'for-each-ref --format' syntax would be way more flexible. > > We have that already, don't we? > git branch has --format, but there's no way (at least to my knowledge) to define a value in gitconfig to be used by $git branch. Having branch --format available, making an alias is a possible route. (Either by wrapping branch --format or for-each-ref itself). But I was referring to changing the output of the default $git branch; ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH] branch: colorize branches checked out in a linked working tree the same way as the current branch is colorized 2018-09-27 21:35 ` Rafael Ascensão @ 2018-09-28 1:07 ` Jeff King 0 siblings, 0 replies; 136+ messages in thread From: Jeff King @ 2018-09-28 1:07 UTC (permalink / raw) To: Rafael Ascensão; +Cc: Nickolai Belakovski, git On Thu, Sep 27, 2018 at 10:35:11PM +0100, Rafael Ascensão wrote: > git branch has --format, but there's no way (at least to my knowledge) > to define a value in gitconfig to be used by $git branch. Oh, you're right. I was thinking of the branch.sort we just added in v2.19. I agree that having branch.format (and a matching tag.format) would be useful. -Peff ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH] branch: colorize branches checked out in a linked working tree the same way as the current branch is colorized 2018-09-27 19:28 ` Rafael Ascensão 2018-09-27 19:35 ` Jeff King @ 2018-09-27 20:02 ` Ævar Arnfjörð Bjarmason 2018-09-27 20:16 ` Nickolai Belakovski 1 sibling, 1 reply; 136+ messages in thread From: Ævar Arnfjörð Bjarmason @ 2018-09-27 20:02 UTC (permalink / raw) To: Rafael Ascensão; +Cc: Jeff King, Nickolai Belakovski, git On Thu, Sep 27 2018, Rafael Ascensão wrote: > On Thu, Sep 27, 2018 at 02:17:08PM -0400, Jeff King wrote: >> Do we want to limit this to git-branch, though? Ideally any output you >> get from git-branch could be replicated with for-each-ref (or with >> a custom "branch --format"). >> >> I.e., could we have a format in ref-filter that matches HEAD, but >> returns a distinct symbol for a worktree HEAD? That would allow a few >> things: > > I was going to suggest using dim green and green for elsewhere and here > respectively, in a similar way how range-diff uses it to show different > versions of the same diff. It would be really useful to (just via E-Mail to start) itemize the colors we use in various places and what they mean. E.g. I thought green here made sense because in "diff" we show the old/new as red/green, so the branch you're on is "new" in the same sense, i.e. it's what your current state is. But maybe there's cases where that doesn't "rhyme" as it were. ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH] branch: colorize branches checked out in a linked working tree the same way as the current branch is colorized 2018-09-27 20:02 ` Ævar Arnfjörð Bjarmason @ 2018-09-27 20:16 ` Nickolai Belakovski 2018-09-27 20:40 ` Rafael Ascensão 0 siblings, 1 reply; 136+ messages in thread From: Nickolai Belakovski @ 2018-09-27 20:16 UTC (permalink / raw) To: avarab; +Cc: rafa.almas, Jeff King, git Not to hijack my own thread, but FWIW git branch -r shows remote branches in red, but old/new status of a remote branch is ambiguous (could have new stuff, could be out of date). Also, git branch -vv shows remote tracking branches in blue. One could argue it should be red since git branch -r is in red. But yea, probably best to take this topic to its own thread. On Thu, Sep 27, 2018 at 1:02 PM Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote: > > > On Thu, Sep 27 2018, Rafael Ascensão wrote: > > > On Thu, Sep 27, 2018 at 02:17:08PM -0400, Jeff King wrote: > >> Do we want to limit this to git-branch, though? Ideally any output you > >> get from git-branch could be replicated with for-each-ref (or with > >> a custom "branch --format"). > >> > >> I.e., could we have a format in ref-filter that matches HEAD, but > >> returns a distinct symbol for a worktree HEAD? That would allow a few > >> things: > > > > I was going to suggest using dim green and green for elsewhere and here > > respectively, in a similar way how range-diff uses it to show different > > versions of the same diff. > > It would be really useful to (just via E-Mail to start) itemize the > colors we use in various places and what they mean. > > E.g. I thought green here made sense because in "diff" we show the > old/new as red/green, so the branch you're on is "new" in the same > sense, i.e. it's what your current state is. > > But maybe there's cases where that doesn't "rhyme" as it were. ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH] branch: colorize branches checked out in a linked working tree the same way as the current branch is colorized 2018-09-27 20:16 ` Nickolai Belakovski @ 2018-09-27 20:40 ` Rafael Ascensão 2018-11-11 23:58 ` [PATCH v2 0/2] refactoring branch colorization to ref-filter nbelakovski 0 siblings, 1 reply; 136+ messages in thread From: Rafael Ascensão @ 2018-09-27 20:40 UTC (permalink / raw) To: Nickolai Belakovski; +Cc: avarab, Jeff King, git On Thu, Sep 27, 2018 at 01:16:19PM -0700, Nickolai Belakovski wrote: > > Not to hijack my own thread, but FWIW git branch -r shows remote > branches in red, but old/new status of a remote branch is ambiguous > (could have new stuff, could be out of date). Also, git branch -vv > shows remote tracking branches in blue. One could argue it should be > red since git branch -r is in red. > For me remote branches being red means: they're here but you cannot write to them. They are like 'read-only/disabled' branches. Under this interpretation red makes sense. On Thu, Sep 27, 2018 at 9:02 PM Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote: > > E.g. I thought green here made sense because in "diff" we show the > old/new as red/green, so the branch you're on is "new" in the same > sense, i.e. it's what your current state is. > I still defend using green and dim green for this case. Because all these worktrees are in a sense active. They're checked out in some place. It's just the case that the particular one that we are in is probably more relevant than the others. -- Cheers Rafael Ascensão ^ permalink raw reply [flat|nested] 136+ messages in thread
* [PATCH v2 0/2] refactoring branch colorization to ref-filter 2018-09-27 20:40 ` Rafael Ascensão @ 2018-11-11 23:58 ` nbelakovski 2018-11-11 23:58 ` [PATCH v2 1/2] ref-filter: add worktree atom nbelakovski ` (2 more replies) 0 siblings, 3 replies; 136+ messages in thread From: nbelakovski @ 2018-11-11 23:58 UTC (permalink / raw) To: rafa.almas; +Cc: avarab, git, nbelakovski, peff From: Nickolai Belakovski <nbelakovski@gmail.com> Finally found some time to follow up on this :) I decided to take the approach suggested by Peff for simplicity. I have another version which uses a hash map to store *all* of the information returned by get_worktrees, information which can then be accessed in the fashion %(workree:path) or %(worktree:is_detached), but it seemed like a lot of code to add and there was no use case to justify the addition of a hash map at this time. If there's interest though, I can make a separate patch after this one to introduce those changes. They build directly off of the changes introduced here. I've split this work into two commits since the items are logically separate. CI results: https://travis-ci.org/nbelakovski/git/builds/453723727 Nickolai Belakovski (2): ref-filter: add worktree atom branch: Mark and colorize a branch differently if it is checked out in a linked worktree builtin/branch.c | 22 +++++++++++++--------- color.h | 18 ++++++++++++++++++ ref-filter.c | 31 +++++++++++++++++++++++++++++++ t/t3200-branch.sh | 8 ++++---- t/t3203-branch-output.sh | 21 +++++++++++++++++++++ t/t6302-for-each-ref-filter.sh | 15 +++++++++++++++ t/test-lib-functions.sh | 6 ++++++ 7 files changed, 108 insertions(+), 13 deletions(-) -- 2.14.2 ^ permalink raw reply [flat|nested] 136+ messages in thread
* [PATCH v2 1/2] ref-filter: add worktree atom 2018-11-11 23:58 ` [PATCH v2 0/2] refactoring branch colorization to ref-filter nbelakovski @ 2018-11-11 23:58 ` nbelakovski 2018-11-12 10:11 ` Junio C Hamano 2018-11-12 12:23 ` Jeff King 2018-11-11 23:58 ` [PATCH v2 2/2] branch: Mark and colorize a branch differently if it is checked out in a linked worktree nbelakovski 2018-12-16 21:57 ` [PATCH v3 0/3] nbelakovski 2 siblings, 2 replies; 136+ messages in thread From: nbelakovski @ 2018-11-11 23:58 UTC (permalink / raw) To: rafa.almas; +Cc: avarab, git, nbelakovski, peff From: Nickolai Belakovski <nbelakovski@gmail.com> Add an atom expressing whether the particular ref is checked out in a linked worktree. Signed-off-by: Nickolai Belakovski <nbelakovski@gmail.com> --- ref-filter.c | 31 +++++++++++++++++++++++++++++++ t/t6302-for-each-ref-filter.sh | 15 +++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/ref-filter.c b/ref-filter.c index 0c45ed9d94..53e2504f5d 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -20,6 +20,7 @@ #include "commit-slab.h" #include "commit-graph.h" #include "commit-reach.h" +#include "worktree.h" static struct ref_msg { const char *gone; @@ -114,6 +115,7 @@ static struct used_atom { } objectname; struct refname_atom refname; char *head; + struct string_list worktree_heads; } u; } *used_atom; static int used_atom_cnt, need_tagged, need_symref; @@ -420,6 +422,28 @@ static int head_atom_parser(const struct ref_format *format, struct used_atom *a return 0; } +static int worktree_head_atom_parser(const struct ref_format *format, + struct used_atom *atom, + const char *arg, + struct strbuf *unused_err) +{ + struct worktree **worktrees = get_worktrees(0); + int i; + + string_list_init(&atom->u.worktree_heads, 1); + + for (i = 0; worktrees[i]; i++) { + if (worktrees[i]->head_ref) + string_list_append(&atom->u.worktree_heads, + worktrees[i]->head_ref); + } + + string_list_sort(&atom->u.worktree_heads); + + free_worktrees(worktrees); + return 0; +} + static struct { const char *name; info_source source; @@ -461,6 +485,7 @@ static struct { { "flag", SOURCE_NONE }, { "HEAD", SOURCE_NONE, FIELD_STR, head_atom_parser }, { "color", SOURCE_NONE, FIELD_STR, color_atom_parser }, + { "worktree", SOURCE_NONE, FIELD_STR, worktree_head_atom_parser }, { "align", SOURCE_NONE, FIELD_STR, align_atom_parser }, { "end", SOURCE_NONE }, { "if", SOURCE_NONE, FIELD_STR, if_atom_parser }, @@ -1594,6 +1619,12 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err) else v->s = xstrdup(" "); continue; + } else if (!strcmp(name, "worktree")) { + if (string_list_has_string(&atom->u.worktree_heads, ref->refname)) + v->s = xstrdup("+"); + else + v->s = xstrdup(" "); + continue; } else if (starts_with(name, "align")) { v->handler = align_atom_handler; v->s = xstrdup(""); diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh index fc067ed672..5e6d249d4c 100755 --- a/t/t6302-for-each-ref-filter.sh +++ b/t/t6302-for-each-ref-filter.sh @@ -441,4 +441,19 @@ test_expect_success '--merged is incompatible with --no-merged' ' test_must_fail git for-each-ref --merged HEAD --no-merged HEAD ' +test_expect_success '"add" a worktree' ' + mkdir worktree_dir && + git worktree add -b master_worktree worktree_dir master +' + +test_expect_success 'validate worktree atom' ' + cat >expect <<-\EOF && + master: checked out in a worktree + master_worktree: checked out in a worktree + side: not checked out in a worktree +EOF + git for-each-ref --format="%(refname:short): %(if)%(worktree)%(then)checked out in a worktree%(else)not checked out in a worktree%(end)" refs/heads/ >actual && + test_cmp expect actual +' + test_done -- 2.14.2 ^ permalink raw reply related [flat|nested] 136+ messages in thread
* Re: [PATCH v2 1/2] ref-filter: add worktree atom 2018-11-11 23:58 ` [PATCH v2 1/2] ref-filter: add worktree atom nbelakovski @ 2018-11-12 10:11 ` Junio C Hamano 2018-11-12 12:22 ` Jeff King 2018-11-12 12:23 ` Jeff King 1 sibling, 1 reply; 136+ messages in thread From: Junio C Hamano @ 2018-11-12 10:11 UTC (permalink / raw) To: nbelakovski; +Cc: rafa.almas, avarab, git, peff nbelakovski@gmail.com writes: > > +static int worktree_head_atom_parser(const struct ref_format *format, > + struct used_atom *atom, > + const char *arg, > + struct strbuf *unused_err) This and ... > +{ > + struct worktree **worktrees = get_worktrees(0); > + int i; > + > + string_list_init(&atom->u.worktree_heads, 1); > + > + for (i = 0; worktrees[i]; i++) { > + if (worktrees[i]->head_ref) > + string_list_append(&atom->u.worktree_heads, > + worktrees[i]->head_ref); ... this makes me suspect that you are using tabstop != 8 and that is causing you to indent these lines overly deeply. Please don't, while working on this codebase. > + } > + > + string_list_sort(&atom->u.worktree_heads); > + > + free_worktrees(worktrees); > + return 0; > +} So..., this function collects any and all branches that are checked out in some worktree, and sort them _without_ dedup. The user of the resulting information (i.e. atom->u.worktree_heads) cannot tell where each of the listed branches is checked out. I wonder if "The worktree at /local/src/wt1 has this branch checked out" is something the user of %(worktree) atom, or a variant thereof e.g. "%(worktree:detailed)", may want to learn, but because that information is lost when this function returns, such an enhancement cannot be done without fixing this funciton. Also, I am not sure if this "list of some info on worktrees" really belongs to an individual atom. For one thing, if a format includes more than one instance of %(worktree) atoms, you'd iterate over the worktrees as many times as the number of these atoms you have. Is there another existing atom that "caches" expensive piece of information per used_atom[] element like this one? Essentially I am trying to convince myself that the approach taken by the patch is a sane one by finding a precedent. > + } else if (!strcmp(name, "worktree")) { > + if (string_list_has_string(&atom->u.worktree_heads, ref->refname)) I thought we were moving towards killing the use of string_list as a look-up table, as we do not want to see thoughtless copy&paste such a code from parts of the code that are not performance critical to a part. Not very satisfying. I think we can let this pass, and later add a wrapper around hashmap that is meant to only be used to replace string-list used for this exact purpose, i.e. key is a string, and there is no need to iterate over the existing elements in any sorted order. Optionally, we can limit the look up to only checking for existence, if it makes the code for the wrapper simpler. > + v->s = xstrdup("+"); > + else > + v->s = xstrdup(" "); > + continue; > } else if (starts_with(name, "align")) { > v->handler = align_atom_handler; > v->s = xstrdup(""); > diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh > index fc067ed672..5e6d249d4c 100755 > --- a/t/t6302-for-each-ref-filter.sh > +++ b/t/t6302-for-each-ref-filter.sh > @@ -441,4 +441,19 @@ test_expect_success '--merged is incompatible with --no-merged' ' > test_must_fail git for-each-ref --merged HEAD --no-merged HEAD > ' > > +test_expect_success '"add" a worktree' ' > + mkdir worktree_dir && > + git worktree add -b master_worktree worktree_dir master > +' > + > +test_expect_success 'validate worktree atom' ' > + cat >expect <<-\EOF && > + master: checked out in a worktree > + master_worktree: checked out in a worktree > + side: not checked out in a worktree As you started the here-doc with <<-, the next line EOF does not have to be flushed to the left. Indent it just the same way with a tab. > +EOF The following line begins with a broken indentation, it seems. > + git for-each-ref --format="%(refname:short): %(if)%(worktree)%(then)checked out in a worktree%(else)not checked out in a worktree%(end)" refs/heads/ >actual && > + test_cmp expect actual > +' > + > test_done ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v2 1/2] ref-filter: add worktree atom 2018-11-12 10:11 ` Junio C Hamano @ 2018-11-12 12:22 ` Jeff King 2018-11-13 1:38 ` Junio C Hamano 0 siblings, 1 reply; 136+ messages in thread From: Jeff King @ 2018-11-12 12:22 UTC (permalink / raw) To: Junio C Hamano; +Cc: nbelakovski, rafa.almas, avarab, git On Mon, Nov 12, 2018 at 07:11:23PM +0900, Junio C Hamano wrote: > > + } > > + > > + string_list_sort(&atom->u.worktree_heads); > > + > > + free_worktrees(worktrees); > > + return 0; > > +} > > So..., this function collects any and all branches that are checked > out in some worktree, and sort them _without_ dedup. The user of > the resulting information (i.e. atom->u.worktree_heads) cannot tell > where each of the listed branches is checked out. > > I wonder if "The worktree at /local/src/wt1 has this branch checked > out" is something the user of %(worktree) atom, or a variant thereof > e.g. "%(worktree:detailed)", may want to learn, but because that > information is lost when this function returns, such an enhancement > cannot be done without fixing this funciton. Hmm. I think for the purposes of this series we could jump straight to converting %(worktree) to mean "the path of the worktree for which this branch is HEAD, or the empty string otherwise". Then the caller from git-branch (or anybody wanting to emulate it) could still do: %(if)%(worktree)%(then)+ %(refname)%(end) As a bonus, the decision to use "+" becomes a lot easier. It is no longer a part of the format language that we must promise forever, but simply a porcelain decision by git-branch. > Also, I am not sure if this "list of some info on worktrees" really > belongs to an individual atom. For one thing, if a format includes > more than one instance of %(worktree) atoms, you'd iterate over the > worktrees as many times as the number of these atoms you have. Is > there another existing atom that "caches" expensive piece of > information per used_atom[] element like this one? Essentially I am > trying to convince myself that the approach taken by the patch is a > sane one by finding a precedent. Yes, we faced this a bit with Olga's cat-file conversion patches (where we had a shared struct object_info). There probably should just be a file-global data-structure storing the worktree info once (in an ideal world, it would be part of a "struct ref_format" that uses no global variables, but that is not how the code is structured today). > > + } else if (!strcmp(name, "worktree")) { > > + if (string_list_has_string(&atom->u.worktree_heads, ref->refname)) > > I thought we were moving towards killing the use of string_list as a > look-up table, as we do not want to see thoughtless copy&paste such > a code from parts of the code that are not performance critical to a > part. Not very satisfying. > > I think we can let this pass, and later add a wrapper around > hashmap that is meant to only be used to replace string-list > used for this exact purpose, i.e. key is a string, and there > is no need to iterate over the existing elements in any > sorted order. Optionally, we can limit the look up to only > checking for existence, if it makes the code for the wrapper > simpler. This came up over in another thread yesterday, too. So yeah, perhaps we should move on that (I am OK punting on it for this series and converting it later, though). -Peff ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v2 1/2] ref-filter: add worktree atom 2018-11-12 12:22 ` Jeff King @ 2018-11-13 1:38 ` Junio C Hamano 2018-11-21 14:05 ` Nickolai Belakovski 0 siblings, 1 reply; 136+ messages in thread From: Junio C Hamano @ 2018-11-13 1:38 UTC (permalink / raw) To: Jeff King; +Cc: nbelakovski, rafa.almas, avarab, git Jeff King <peff@peff.net> writes: >> I wonder if "The worktree at /local/src/wt1 has this branch checked >> out" is something the user of %(worktree) atom, or a variant thereof >> e.g. "%(worktree:detailed)", may want to learn, but because that >> information is lost when this function returns, such an enhancement >> cannot be done without fixing this funciton. > > Hmm. I think for the purposes of this series we could jump straight to > converting %(worktree) to mean "the path of the worktree for which this > branch is HEAD, or the empty string otherwise". > > Then the caller from git-branch (or anybody wanting to emulate it) could > still do: > > %(if)%(worktree)%(then)+ %(refname)%(end) > > As a bonus, the decision to use "+" becomes a lot easier. It is no > longer a part of the format language that we must promise forever, but > simply a porcelain decision by git-branch. Yeah, thanks for following through the thought process to the logical conclusion. If a branch is multply checked out, which is a condition "git worktree" and "git checkout" ought to prevent from happening, we could leave the result unspecified but a non-empty string, or something like that. > file-global data-structure storing the worktree info once (in an ideal > world, it would be part of a "struct ref_format" that uses no global > variables, but that is not how the code is structured today). Yes, I agree that would be the ideal longer-term direction to move this code in. >> > + } else if (!strcmp(name, "worktree")) { >> > + if (string_list_has_string(&atom->u.worktree_heads, ref->refname)) >> >> I thought we were moving towards killing the use of string_list as a >> look-up table, as we do not want to see thoughtless copy&paste such >> a code from parts of the code that are not performance critical to a >> part. Not very satisfying. >> >> I think we can let this pass, and later add a wrapper around >> hashmap that is meant to only be used to replace string-list >> used for this exact purpose, i.e. key is a string, and there >> is no need to iterate over the existing elements in any >> sorted order. Optionally, we can limit the look up to only >> checking for existence, if it makes the code for the wrapper >> simpler. > > This came up over in another thread yesterday, too. So yeah, perhaps we > should move on that (I am OK punting on it for this series and > converting it later, though). FWIW, I am OK punting and leaving, too. ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v2 1/2] ref-filter: add worktree atom 2018-11-13 1:38 ` Junio C Hamano @ 2018-11-21 14:05 ` Nickolai Belakovski 2018-11-21 14:08 ` Jeff King 0 siblings, 1 reply; 136+ messages in thread From: Nickolai Belakovski @ 2018-11-21 14:05 UTC (permalink / raw) To: Junio C Hamano Cc: Jeff King, Rafael Ascensão, Ævar Arnfjörð Bjarmason, git I think if we move to making this atom just store worktree path, that needs to be implemented as a hashmap of refname->wtpath, which would also solve this string_list issue, correct? Just making sure I'm not missing something before I submit another patch. On Tue, Nov 13, 2018 at 2:38 AM Junio C Hamano <gitster@pobox.com> wrote: > > Jeff King <peff@peff.net> writes: > > >> I wonder if "The worktree at /local/src/wt1 has this branch checked > >> out" is something the user of %(worktree) atom, or a variant thereof > >> e.g. "%(worktree:detailed)", may want to learn, but because that > >> information is lost when this function returns, such an enhancement > >> cannot be done without fixing this funciton. > > > > Hmm. I think for the purposes of this series we could jump straight to > > converting %(worktree) to mean "the path of the worktree for which this > > branch is HEAD, or the empty string otherwise". > > > > Then the caller from git-branch (or anybody wanting to emulate it) could > > still do: > > > > %(if)%(worktree)%(then)+ %(refname)%(end) > > > > As a bonus, the decision to use "+" becomes a lot easier. It is no > > longer a part of the format language that we must promise forever, but > > simply a porcelain decision by git-branch. > > Yeah, thanks for following through the thought process to the > logical conclusion. If a branch is multply checked out, which is a > condition "git worktree" and "git checkout" ought to prevent from > happening, we could leave the result unspecified but a non-empty > string, or something like that. > > > file-global data-structure storing the worktree info once (in an ideal > > world, it would be part of a "struct ref_format" that uses no global > > variables, but that is not how the code is structured today). > > Yes, I agree that would be the ideal longer-term direction to move > this code in. > > >> > + } else if (!strcmp(name, "worktree")) { > >> > + if (string_list_has_string(&atom->u.worktree_heads, ref->refname)) > >> > >> I thought we were moving towards killing the use of string_list as a > >> look-up table, as we do not want to see thoughtless copy&paste such > >> a code from parts of the code that are not performance critical to a > >> part. Not very satisfying. > >> > >> I think we can let this pass, and later add a wrapper around > >> hashmap that is meant to only be used to replace string-list > >> used for this exact purpose, i.e. key is a string, and there > >> is no need to iterate over the existing elements in any > >> sorted order. Optionally, we can limit the look up to only > >> checking for existence, if it makes the code for the wrapper > >> simpler. > > > > This came up over in another thread yesterday, too. So yeah, perhaps we > > should move on that (I am OK punting on it for this series and > > converting it later, though). > > FWIW, I am OK punting and leaving, too. ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v2 1/2] ref-filter: add worktree atom 2018-11-21 14:05 ` Nickolai Belakovski @ 2018-11-21 14:08 ` Jeff King 0 siblings, 0 replies; 136+ messages in thread From: Jeff King @ 2018-11-21 14:08 UTC (permalink / raw) To: Nickolai Belakovski Cc: Junio C Hamano, Rafael Ascensão, Ævar Arnfjörð Bjarmason, git On Wed, Nov 21, 2018 at 03:05:04PM +0100, Nickolai Belakovski wrote: > I think if we move to making this atom just store worktree path, that > needs to be implemented as a hashmap of refname->wtpath, which would > also solve this string_list issue, correct? Just making sure I'm not > missing something before I submit another patch. string_list has a "util" field, so you actually _can_ use it to create a mapping. I do think a hashmap is a little more obvious. OTOH, the hashmap API is a little tricky; if we are going to add a "strmap" API soon, it may be simpler to just use a string_list now and convert to strmap when it is a available. -Peff ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v2 1/2] ref-filter: add worktree atom 2018-11-11 23:58 ` [PATCH v2 1/2] ref-filter: add worktree atom nbelakovski 2018-11-12 10:11 ` Junio C Hamano @ 2018-11-12 12:23 ` Jeff King 1 sibling, 0 replies; 136+ messages in thread From: Jeff King @ 2018-11-12 12:23 UTC (permalink / raw) To: nbelakovski; +Cc: rafa.almas, avarab, git On Sun, Nov 11, 2018 at 03:58:30PM -0800, nbelakovski@gmail.com wrote: > From: Nickolai Belakovski <nbelakovski@gmail.com> > > Add an atom expressing whether the particular ref is checked out in a > linked worktree. > > Signed-off-by: Nickolai Belakovski <nbelakovski@gmail.com> > --- > ref-filter.c | 31 +++++++++++++++++++++++++++++++ > t/t6302-for-each-ref-filter.sh | 15 +++++++++++++++ > 2 files changed, 46 insertions(+) I left some more comments elsewhere in the thread, but one more thing to note: this probably needs to touch Documentation/git-for-each-ref.txt to describe the new placeholder. -Peff ^ permalink raw reply [flat|nested] 136+ messages in thread
* [PATCH v2 2/2] branch: Mark and colorize a branch differently if it is checked out in a linked worktree 2018-11-11 23:58 ` [PATCH v2 0/2] refactoring branch colorization to ref-filter nbelakovski 2018-11-11 23:58 ` [PATCH v2 1/2] ref-filter: add worktree atom nbelakovski @ 2018-11-11 23:58 ` nbelakovski 2018-11-12 10:20 ` Junio C Hamano 2018-12-16 21:57 ` [PATCH v3 0/3] nbelakovski 2 siblings, 1 reply; 136+ messages in thread From: nbelakovski @ 2018-11-11 23:58 UTC (permalink / raw) To: rafa.almas; +Cc: avarab, git, nbelakovski, peff From: Nickolai Belakovski <nbelakovski@gmail.com> In order to more clearly display which branches are active, the output of git branch is modified to mark branches checkout out in a linked worktree with a "+" and color them in a faint light green (in contrast to the current branch, which will still be denoted with a "*" and colored in green) This is meant to simplify workflows related to worktree, particularly due to the limitations of not being able to check out the same branch in two worktrees and the inability to delete a branch checked out in a worktree. When performing branch operations like checkout and delete, it would be useful to know more readily if the branches in which the user is interested are already checked out in a worktree. The git worktree list command contains the relevant information, however this is a much less frquently used command than git branch. Signed-off-by: Nickolai Belakovski <nbelakovski@gmail.com> --- builtin/branch.c | 22 +++++++++++++--------- color.h | 18 ++++++++++++++++++ t/t3200-branch.sh | 8 ++++---- t/t3203-branch-output.sh | 21 +++++++++++++++++++++ t/test-lib-functions.sh | 6 ++++++ 5 files changed, 62 insertions(+), 13 deletions(-) diff --git a/builtin/branch.c b/builtin/branch.c index 0c55f7f065..34f44c82d7 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -42,11 +42,12 @@ static struct object_id head_oid; static int branch_use_color = -1; static char branch_colors[][COLOR_MAXLEN] = { GIT_COLOR_RESET, - GIT_COLOR_NORMAL, /* PLAIN */ - GIT_COLOR_RED, /* REMOTE */ - GIT_COLOR_NORMAL, /* LOCAL */ - GIT_COLOR_GREEN, /* CURRENT */ - GIT_COLOR_BLUE, /* UPSTREAM */ + GIT_COLOR_NORMAL, /* PLAIN */ + GIT_COLOR_RED, /* REMOTE */ + GIT_COLOR_NORMAL, /* LOCAL */ + GIT_COLOR_GREEN, /* CURRENT */ + GIT_COLOR_BLUE, /* UPSTREAM */ + GIT_COLOR_FAINT_LIGHT_GREEN, /* WORKTREE */ }; enum color_branch { BRANCH_COLOR_RESET = 0, @@ -54,7 +55,8 @@ enum color_branch { BRANCH_COLOR_REMOTE = 2, BRANCH_COLOR_LOCAL = 3, BRANCH_COLOR_CURRENT = 4, - BRANCH_COLOR_UPSTREAM = 5 + BRANCH_COLOR_UPSTREAM = 5, + BRANCH_COLOR_WORKTREE = 6 }; static const char *color_branch_slots[] = { @@ -64,6 +66,7 @@ static const char *color_branch_slots[] = { [BRANCH_COLOR_LOCAL] = "local", [BRANCH_COLOR_CURRENT] = "current", [BRANCH_COLOR_UPSTREAM] = "upstream", + [BRANCH_COLOR_WORKTREE] = "worktree", }; static struct string_list output = STRING_LIST_INIT_DUP; @@ -342,9 +345,10 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r struct strbuf local = STRBUF_INIT; struct strbuf remote = STRBUF_INIT; - strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else) %s%%(end)", - branch_get_color(BRANCH_COLOR_CURRENT), - branch_get_color(BRANCH_COLOR_LOCAL)); + strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else)%%(if)%%(worktree)%%(then)+ %s%%(else) %s%%(end)%%(end)", + branch_get_color(BRANCH_COLOR_CURRENT), + branch_get_color(BRANCH_COLOR_WORKTREE), + branch_get_color(BRANCH_COLOR_LOCAL)); strbuf_addf(&remote, " %s", branch_get_color(BRANCH_COLOR_REMOTE)); diff --git a/color.h b/color.h index 98894d6a17..857653df73 100644 --- a/color.h +++ b/color.h @@ -42,6 +42,24 @@ struct strbuf; #define GIT_COLOR_FAINT_BLUE "\033[2;34m" #define GIT_COLOR_FAINT_MAGENTA "\033[2;35m" #define GIT_COLOR_FAINT_CYAN "\033[2;36m" +#define GIT_COLOR_LIGHT_RED "\033[91m" +#define GIT_COLOR_LIGHT_GREEN "\033[92m" +#define GIT_COLOR_LIGHT_YELLOW "\033[93m" +#define GIT_COLOR_LIGHT_BLUE "\033[94m" +#define GIT_COLOR_LIGHT_MAGENTA "\033[95m" +#define GIT_COLOR_LIGHT_CYAN "\033[96m" +#define GIT_COLOR_BOLD_LIGHT_RED "\033[1;91m" +#define GIT_COLOR_BOLD_LIGHT_GREEN "\033[1;92m" +#define GIT_COLOR_BOLD_LIGHT_YELLOW "\033[1;93m" +#define GIT_COLOR_BOLD_LIGHT_BLUE "\033[1;94m" +#define GIT_COLOR_BOLD_LIGHT_MAGENTA "\033[1;95m" +#define GIT_COLOR_BOLD_LIGHT_CYAN "\033[1;96m" +#define GIT_COLOR_FAINT_LIGHT_RED "\033[2;91m" +#define GIT_COLOR_FAINT_LIGHT_GREEN "\033[2;92m" +#define GIT_COLOR_FAINT_LIGHT_YELLOW "\033[2;93m" +#define GIT_COLOR_FAINT_LIGHT_BLUE "\033[2;94m" +#define GIT_COLOR_FAINT_LIGHT_MAGENTA "\033[2;95m" +#define GIT_COLOR_FAINT_LIGHT_CYAN "\033[2;96m" #define GIT_COLOR_BG_RED "\033[41m" #define GIT_COLOR_BG_GREEN "\033[42m" #define GIT_COLOR_BG_YELLOW "\033[43m" diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index 478b82cf9b..e404f6e23c 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -292,7 +292,7 @@ test_expect_success 'git branch --list -v with --abbrev' ' test_expect_success 'git branch --column' ' COLUMNS=81 git branch --column=column >actual && cat >expected <<\EOF && - a/b/c bam foo l * master n o/p r + a/b/c + bam foo l * master n o/p r abc bar j/k m/m master2 o/o q EOF test_cmp expected actual @@ -307,7 +307,7 @@ test_expect_success 'git branch --column with an extremely long branch name' ' cat >expected <<EOF && a/b/c abc - bam ++ bam bar foo j/k @@ -332,7 +332,7 @@ test_expect_success 'git branch with column.*' ' git config --unset column.branch && git config --unset column.ui && cat >expected <<\EOF && - a/b/c bam foo l * master n o/p r + a/b/c + bam foo l * master n o/p r abc bar j/k m/m master2 o/o q EOF test_cmp expected actual @@ -349,7 +349,7 @@ test_expect_success 'git branch -v with column.ui ignored' ' cat >expected <<\EOF && a/b/c abc - bam ++ bam bar foo j/k diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh index ee6787614c..06771fac64 100755 --- a/t/t3203-branch-output.sh +++ b/t/t3203-branch-output.sh @@ -240,6 +240,27 @@ test_expect_success 'git branch --format option' ' test_i18ncmp expect actual ' +test_expect_success '"add" a worktree' ' + mkdir worktree_dir && + git worktree add -b master_worktree worktree_dir master +' + +cat >expect <<'EOF' +* <GREEN>(HEAD detached from fromtag)<RESET> + ambiguous<RESET> + branch-one<RESET> + branch-two<RESET> + master<RESET> ++ <FAINT;LGREEN>master_worktree<RESET> + ref-to-branch<RESET> -> branch-one + ref-to-remote<RESET> -> origin/branch-one +EOF +test_expect_success TTY 'worktree colors correct' ' + test_terminal git branch >actual.raw && + test_decode_color <actual.raw >actual && + test_cmp expect actual +' + test_expect_success "set up color tests" ' echo "<RED>master<RESET>" >expect.color && echo "master" >expect.bare && diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh index 78d8c3783b..2831a42a88 100644 --- a/t/test-lib-functions.sh +++ b/t/test-lib-functions.sh @@ -61,6 +61,12 @@ test_decode_color () { if (n == 45) return "BMAGENTA"; if (n == 46) return "BCYAN"; if (n == 47) return "BWHITE"; + if (n == 91) return "LRED"; + if (n == 92) return "LGREEN"; + if (n == 93) return "LYELLOW"; + if (n == 94) return "LBLUE"; + if (n == 95) return "LMAGENTA"; + if (n == 96) return "LCYAN"; } { while (match($0, /\033\[[0-9;]*m/) != 0) { -- 2.14.2 ^ permalink raw reply related [flat|nested] 136+ messages in thread
* Re: [PATCH v2 2/2] branch: Mark and colorize a branch differently if it is checked out in a linked worktree 2018-11-11 23:58 ` [PATCH v2 2/2] branch: Mark and colorize a branch differently if it is checked out in a linked worktree nbelakovski @ 2018-11-12 10:20 ` Junio C Hamano 2018-11-12 12:14 ` Jeff King 0 siblings, 1 reply; 136+ messages in thread From: Junio C Hamano @ 2018-11-12 10:20 UTC (permalink / raw) To: nbelakovski; +Cc: rafa.almas, avarab, git, peff nbelakovski@gmail.com writes: > diff --git a/color.h b/color.h > index 98894d6a17..857653df73 100644 > --- a/color.h > +++ b/color.h > @@ -42,6 +42,24 @@ struct strbuf; > #define GIT_COLOR_FAINT_BLUE "\033[2;34m" > #define GIT_COLOR_FAINT_MAGENTA "\033[2;35m" > #define GIT_COLOR_FAINT_CYAN "\033[2;36m" > +#define GIT_COLOR_LIGHT_RED "\033[91m" > +#define GIT_COLOR_LIGHT_GREEN "\033[92m" > +#define GIT_COLOR_LIGHT_YELLOW "\033[93m" > +#define GIT_COLOR_LIGHT_BLUE "\033[94m" > +#define GIT_COLOR_LIGHT_MAGENTA "\033[95m" > +#define GIT_COLOR_LIGHT_CYAN "\033[96m" > +#define GIT_COLOR_BOLD_LIGHT_RED "\033[1;91m" > +#define GIT_COLOR_BOLD_LIGHT_GREEN "\033[1;92m" > +#define GIT_COLOR_BOLD_LIGHT_YELLOW "\033[1;93m" > +#define GIT_COLOR_BOLD_LIGHT_BLUE "\033[1;94m" > +#define GIT_COLOR_BOLD_LIGHT_MAGENTA "\033[1;95m" > +#define GIT_COLOR_BOLD_LIGHT_CYAN "\033[1;96m" > +#define GIT_COLOR_FAINT_LIGHT_RED "\033[2;91m" > +#define GIT_COLOR_FAINT_LIGHT_GREEN "\033[2;92m" > +#define GIT_COLOR_FAINT_LIGHT_YELLOW "\033[2;93m" > +#define GIT_COLOR_FAINT_LIGHT_BLUE "\033[2;94m" > +#define GIT_COLOR_FAINT_LIGHT_MAGENTA "\033[2;95m" > +#define GIT_COLOR_FAINT_LIGHT_CYAN "\033[2;96m" Hopefully you made sure that there is no other topic in-flight that touch this area before doing this change? Otherwise you'd be creating pointless merge conflict by futzing with spaces. Ditto for an earlier hunk of this patch. Thanks. ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v2 2/2] branch: Mark and colorize a branch differently if it is checked out in a linked worktree 2018-11-12 10:20 ` Junio C Hamano @ 2018-11-12 12:14 ` Jeff King 2018-11-12 18:07 ` Rafael Ascensão 0 siblings, 1 reply; 136+ messages in thread From: Jeff King @ 2018-11-12 12:14 UTC (permalink / raw) To: Junio C Hamano; +Cc: nbelakovski, rafa.almas, avarab, git On Mon, Nov 12, 2018 at 07:20:28PM +0900, Junio C Hamano wrote: > nbelakovski@gmail.com writes: > > > diff --git a/color.h b/color.h > > index 98894d6a17..857653df73 100644 > > --- a/color.h > > +++ b/color.h > > @@ -42,6 +42,24 @@ struct strbuf; > > #define GIT_COLOR_FAINT_BLUE "\033[2;34m" > > #define GIT_COLOR_FAINT_MAGENTA "\033[2;35m" > > #define GIT_COLOR_FAINT_CYAN "\033[2;36m" > > +#define GIT_COLOR_LIGHT_RED "\033[91m" > > +#define GIT_COLOR_LIGHT_GREEN "\033[92m" > > +#define GIT_COLOR_LIGHT_YELLOW "\033[93m" > > +#define GIT_COLOR_LIGHT_BLUE "\033[94m" > > +#define GIT_COLOR_LIGHT_MAGENTA "\033[95m" > > +#define GIT_COLOR_LIGHT_CYAN "\033[96m" > > +#define GIT_COLOR_BOLD_LIGHT_RED "\033[1;91m" > > +#define GIT_COLOR_BOLD_LIGHT_GREEN "\033[1;92m" > > +#define GIT_COLOR_BOLD_LIGHT_YELLOW "\033[1;93m" > > +#define GIT_COLOR_BOLD_LIGHT_BLUE "\033[1;94m" > > +#define GIT_COLOR_BOLD_LIGHT_MAGENTA "\033[1;95m" > > +#define GIT_COLOR_BOLD_LIGHT_CYAN "\033[1;96m" > > +#define GIT_COLOR_FAINT_LIGHT_RED "\033[2;91m" > > +#define GIT_COLOR_FAINT_LIGHT_GREEN "\033[2;92m" > > +#define GIT_COLOR_FAINT_LIGHT_YELLOW "\033[2;93m" > > +#define GIT_COLOR_FAINT_LIGHT_BLUE "\033[2;94m" > > +#define GIT_COLOR_FAINT_LIGHT_MAGENTA "\033[2;95m" > > +#define GIT_COLOR_FAINT_LIGHT_CYAN "\033[2;96m" > > Hopefully you made sure that there is no other topic in-flight that > touch this area before doing this change? Otherwise you'd be > creating pointless merge conflict by futzing with spaces. This hunk confused me for a minute, too. It's not changing spaces, but just adding a bunch of color variants. It would be nice if we could just do this with a run-time parse_color("bold red") or whatever, but we use these as static initializers. We don't strictly need anything more than FAINT_LIGHT_GREEN here. I don't have a strong opinion on adding just what we need versus being more complete. > Ditto for an earlier hunk of this patch. Yeah, I think this does apply to the earlier hunk that defines branch_colors[]. -Peff ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v2 2/2] branch: Mark and colorize a branch differently if it is checked out in a linked worktree 2018-11-12 12:14 ` Jeff King @ 2018-11-12 18:07 ` Rafael Ascensão 2018-11-13 1:45 ` Junio C Hamano 2018-11-13 14:49 ` Jeff King 0 siblings, 2 replies; 136+ messages in thread From: Rafael Ascensão @ 2018-11-12 18:07 UTC (permalink / raw) To: Jeff King; +Cc: Junio C Hamano, nbelakovski, avarab, git On Mon, Nov 12, 2018 at 07:14:23AM -0500, Jeff King wrote: > just adding a bunch of color variants. It would be nice if we could just > do this with a run-time parse_color("bold red") or whatever, but we use > these as static initializers. I suggested those colors, but now, I think this needs to be configurable. I suggested using green and dim green as the obvious theoretical choice but after using it for a while I found out that both shades are way too similar, making it really hard to tell by glancing at the output, especially when they're not side by side. If we continue with two dual green approach, current branch needs to be at least bold. But I'm not sure if it's enough. I've been trying some other colors, and cyan feels neutral-ish. I think: GIT_COLOR_BOLD_GREEN /* CURRENT */ GIT_COLOR_CYAN /* WORKTREE */ makes an ok combination. But I can see where personal preference starts to play a role here, as the logical solution isn't good enough. Which makes the case for being able to configure a bit stronger. Cheers, Rafael Ascensão ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v2 2/2] branch: Mark and colorize a branch differently if it is checked out in a linked worktree 2018-11-12 18:07 ` Rafael Ascensão @ 2018-11-13 1:45 ` Junio C Hamano 2018-11-13 14:49 ` Jeff King 1 sibling, 0 replies; 136+ messages in thread From: Junio C Hamano @ 2018-11-13 1:45 UTC (permalink / raw) To: Rafael Ascensão; +Cc: Jeff King, nbelakovski, avarab, git Rafael Ascensão <rafa.almas@gmail.com> writes: > But I can see where personal preference starts to play a role here, as > the logical solution isn't good enough. Which makes the case for being > able to configure a bit stronger. Yeah, our preference over time has always been "do not add to our default color palette to make the default output too colourful; instead allow the user to specify their choice". If this feature can be added like that, that would be preferrable, and if cyan (which usuallly is used to present "less interesting" piece of information and in our default palette) works well enough, maybe we should use that? ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v2 2/2] branch: Mark and colorize a branch differently if it is checked out in a linked worktree 2018-11-12 18:07 ` Rafael Ascensão 2018-11-13 1:45 ` Junio C Hamano @ 2018-11-13 14:49 ` Jeff King 2018-11-21 14:07 ` Nickolai Belakovski 1 sibling, 1 reply; 136+ messages in thread From: Jeff King @ 2018-11-13 14:49 UTC (permalink / raw) To: Rafael Ascensão; +Cc: Junio C Hamano, nbelakovski, avarab, git On Mon, Nov 12, 2018 at 06:07:18PM +0000, Rafael Ascensão wrote: > On Mon, Nov 12, 2018 at 07:14:23AM -0500, Jeff King wrote: > > just adding a bunch of color variants. It would be nice if we could just > > do this with a run-time parse_color("bold red") or whatever, but we use > > these as static initializers. > > I suggested those colors, but now, I think this needs to be > configurable. I think they are configurable in that patch, since it provides "worktree" as a n entry in color_branch_slots. But yeah, every color we add needs to be configurable, and this is really just about defaults. > I suggested using green and dim green as the obvious theoretical choice > but after using it for a while I found out that both shades are way too > similar, making it really hard to tell by glancing at the output, > especially when they're not side by side. > > If we continue with two dual green approach, current branch needs to be > at least bold. But I'm not sure if it's enough. > > I've been trying some other colors, and cyan feels neutral-ish. Yeah, cyan seems pretty reasonable to me. -Peff ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v2 2/2] branch: Mark and colorize a branch differently if it is checked out in a linked worktree 2018-11-13 14:49 ` Jeff King @ 2018-11-21 14:07 ` Nickolai Belakovski 0 siblings, 0 replies; 136+ messages in thread From: Nickolai Belakovski @ 2018-11-21 14:07 UTC (permalink / raw) To: Jeff King Cc: Rafael Ascensão, Junio C Hamano, Ævar Arnfjörð Bjarmason, git OK, I see 3 votes for cyan and 4-5 people participating in the thread, so I'll make it cyan in the next revision. On Tue, Nov 13, 2018 at 3:49 PM Jeff King <peff@peff.net> wrote: > > On Mon, Nov 12, 2018 at 06:07:18PM +0000, Rafael Ascensão wrote: > > > On Mon, Nov 12, 2018 at 07:14:23AM -0500, Jeff King wrote: > > > just adding a bunch of color variants. It would be nice if we could just > > > do this with a run-time parse_color("bold red") or whatever, but we use > > > these as static initializers. > > > > I suggested those colors, but now, I think this needs to be > > configurable. > > I think they are configurable in that patch, since it provides > "worktree" as a n entry in color_branch_slots. But yeah, every color we > add needs to be configurable, and this is really just about defaults. > > > I suggested using green and dim green as the obvious theoretical choice > > but after using it for a while I found out that both shades are way too > > similar, making it really hard to tell by glancing at the output, > > especially when they're not side by side. > > > > If we continue with two dual green approach, current branch needs to be > > at least bold. But I'm not sure if it's enough. > > > > I've been trying some other colors, and cyan feels neutral-ish. > > Yeah, cyan seems pretty reasonable to me. > > -Peff ^ permalink raw reply [flat|nested] 136+ messages in thread
* [PATCH v3 0/3] 2018-11-11 23:58 ` [PATCH v2 0/2] refactoring branch colorization to ref-filter nbelakovski 2018-11-11 23:58 ` [PATCH v2 1/2] ref-filter: add worktree atom nbelakovski 2018-11-11 23:58 ` [PATCH v2 2/2] branch: Mark and colorize a branch differently if it is checked out in a linked worktree nbelakovski @ 2018-12-16 21:57 ` nbelakovski 2018-12-16 21:57 ` [PATCH v3 1/3] ref-filter: add worktreepath atom nbelakovski ` (3 more replies) 2 siblings, 4 replies; 136+ messages in thread From: nbelakovski @ 2018-12-16 21:57 UTC (permalink / raw) To: git; +Cc: peff, rafa.almas, gitster, avarab, Nickolai Belakovski From: Nickolai Belakovski <nbelakovski@gmail.com> Finally got around to submitting latest changes. I think this addresses all the feedback The atom now returns the worktree path instead of '+' I stuck to cyan for the coloring, since it seemed most popular I added one more change to display the worktree path in cyan for git branch -vvv Not sure if it's in the best place, but it seemed like it would be nice to add the path in the same color so that there's some visibility as to why a particular branch is colored in cyan. If it proves to be controversial, I wouldn't want it to hold up this series, we can skip it and I can move discussion to a separate thread (or just forget it, as the case may be) Travis CI results: https://travis-ci.org/nbelakovski/git/builds/468569102 Nickolai Belakovski (3): ref-filter: add worktreepath atom branch: Mark and color a branch differently if it is checked out in a linked worktree branch: Add an extra verbose output displaying worktree path for refs checked out in a linked worktree Documentation/git-for-each-ref.txt | 4 +++ builtin/branch.c | 16 ++++++--- ref-filter.c | 70 ++++++++++++++++++++++++++++++++++++++ t/t3200-branch.sh | 8 ++--- t/t3203-branch-output.sh | 21 ++++++++++++ t/t6302-for-each-ref-filter.sh | 15 ++++++++ 6 files changed, 126 insertions(+), 8 deletions(-) -- 2.14.2 ^ permalink raw reply [flat|nested] 136+ messages in thread
* [PATCH v3 1/3] ref-filter: add worktreepath atom 2018-12-16 21:57 ` [PATCH v3 0/3] nbelakovski @ 2018-12-16 21:57 ` nbelakovski 2018-12-18 17:22 ` Jeff King 2018-12-16 21:57 ` [PATCH v3 2/3] branch: Mark and color a branch differently if it is checked out in a linked worktree nbelakovski ` (2 subsequent siblings) 3 siblings, 1 reply; 136+ messages in thread From: nbelakovski @ 2018-12-16 21:57 UTC (permalink / raw) To: git; +Cc: peff, rafa.almas, gitster, avarab, Nickolai Belakovski From: Nickolai Belakovski <nbelakovski@gmail.com> Add an atom proving the path of the linked worktree where this ref is checked out, if it is checked out in any linked worktrees, and empty string otherwise. --- Documentation/git-for-each-ref.txt | 4 +++ ref-filter.c | 70 ++++++++++++++++++++++++++++++++++++++ t/t6302-for-each-ref-filter.sh | 15 ++++++++ 3 files changed, 89 insertions(+) diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt index 901faef1bf..9590f7beab 100644 --- a/Documentation/git-for-each-ref.txt +++ b/Documentation/git-for-each-ref.txt @@ -209,6 +209,10 @@ symref:: `:lstrip` and `:rstrip` options in the same way as `refname` above. +worktreepath:: + The absolute path to the worktree in which the ref is checked + out, if it is checked out in any linked worktree. ' ' otherwise. + In addition to the above, for commit and tag objects, the header field names (`tree`, `parent`, `object`, `type`, and `tag`) can be used to specify the value in the header field. diff --git a/ref-filter.c b/ref-filter.c index 5de616befe..e8713484a1 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -20,6 +20,8 @@ #include "commit-slab.h" #include "commit-graph.h" #include "commit-reach.h" +#include "worktree.h" +#include "hashmap.h" static struct ref_msg { const char *gone; @@ -34,6 +36,8 @@ static struct ref_msg { "ahead %d, behind %d" }; +static struct worktree ** worktrees; + void setup_ref_filter_porcelain_msg(void) { msgs.gone = _("gone"); @@ -75,6 +79,12 @@ static struct expand_data { struct object_info info; } oi, oi_deref; +struct reftoworktreeinfo_entry { + struct hashmap_entry ent; // must be the first member! + char * ref; // key into map + struct worktree * wt; +}; + /* * An atom is a valid field atom listed below, possibly prefixed with * a "*" to denote deref_tag(). @@ -114,6 +124,7 @@ static struct used_atom { } objectname; struct refname_atom refname; char *head; + struct hashmap reftoworktreeinfo_map; } u; } *used_atom; static int used_atom_cnt, need_tagged, need_symref; @@ -420,6 +431,31 @@ static int head_atom_parser(const struct ref_format *format, struct used_atom *a return 0; } +static int worktree_atom_parser(const struct ref_format *format, + struct used_atom *atom, + const char *arg, + struct strbuf *unused_err) +{ + int i; + worktrees = get_worktrees(0); + + hashmap_init(&(atom->u.reftoworktreeinfo_map), NULL, NULL, 0); + + for (i = 0; worktrees[i]; i++) { + if (worktrees[i]->head_ref) { + struct reftoworktreeinfo_entry *entry; + FLEXPTR_ALLOC_STR(entry, ref, worktrees[i]->head_ref); + hashmap_entry_init(entry, strhash(entry->ref)); + + entry->wt = worktrees[i]; + + hashmap_add(&(atom->u.reftoworktreeinfo_map), entry); + } + } + + return 0; +} + static struct { const char *name; info_source source; @@ -461,6 +497,7 @@ static struct { { "flag", SOURCE_NONE }, { "HEAD", SOURCE_NONE, FIELD_STR, head_atom_parser }, { "color", SOURCE_NONE, FIELD_STR, color_atom_parser }, + { "worktreepath", SOURCE_NONE, FIELD_STR, worktree_atom_parser }, { "align", SOURCE_NONE, FIELD_STR, align_atom_parser }, { "end", SOURCE_NONE }, { "if", SOURCE_NONE, FIELD_STR, if_atom_parser }, @@ -1500,6 +1537,28 @@ static int get_object(struct ref_array_item *ref, int deref, struct object **obj return 0; } +static const char * get_worktree_info(const struct used_atom *atom, const struct ref_array_item *ref) +{ + struct strbuf val = STRBUF_INIT; + struct reftoworktreeinfo_entry * entry; + struct reftoworktreeinfo_entry * lookup_result; + + FLEXPTR_ALLOC_STR(entry, ref, ref->refname); + hashmap_entry_init(entry, strhash(entry->ref)); + lookup_result = hashmap_get(&(atom->u.reftoworktreeinfo_map), entry, NULL); + free(entry); + + if (lookup_result) + { + if (!strncmp(atom->name, "worktreepath", strlen(atom->name))) + strbuf_addstr(&val, lookup_result->wt->path); + } + else + strbuf_addstr(&val, " "); + + return strbuf_detach(&val, NULL); +} + /* * Parse the object referred by ref, and grab needed value. */ @@ -1537,6 +1596,10 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err) if (starts_with(name, "refname")) refname = get_refname(atom, ref); + else if (starts_with(name, "worktreepath")) { + v->s = get_worktree_info(atom, ref); + continue; + } else if (starts_with(name, "symref")) refname = get_symref(atom, ref); else if (starts_with(name, "upstream")) { @@ -2013,7 +2076,14 @@ void ref_array_clear(struct ref_array *array) int i; for (i = 0; i < used_atom_cnt; i++) + { + if (!strncmp(used_atom[i].name, "worktreepath", strlen("worktreepath"))) + { + hashmap_free(&(used_atom[i].u.reftoworktreeinfo_map), 1); + free_worktrees(worktrees); + } free((char *)used_atom[i].name); + } FREE_AND_NULL(used_atom); used_atom_cnt = 0; for (i = 0; i < array->nr; i++) diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh index fc067ed672..add70a4c3e 100755 --- a/t/t6302-for-each-ref-filter.sh +++ b/t/t6302-for-each-ref-filter.sh @@ -441,4 +441,19 @@ test_expect_success '--merged is incompatible with --no-merged' ' test_must_fail git for-each-ref --merged HEAD --no-merged HEAD ' +test_expect_success '"add" a worktree' ' + mkdir worktree_dir && + git worktree add -b master_worktree worktree_dir master +' + +test_expect_success 'validate worktree atom' ' + cat >expect <<-\EOF && + master: checked out in a worktree + master_worktree: checked out in a worktree + side: not checked out in a worktree + EOF + git for-each-ref --format="%(refname:short): %(if)%(worktreepath)%(then)checked out in a worktree%(else)not checked out in a worktree%(end)" refs/heads/ >actual && + test_cmp expect actual +' + test_done -- 2.14.2 ^ permalink raw reply related [flat|nested] 136+ messages in thread
* Re: [PATCH v3 1/3] ref-filter: add worktreepath atom 2018-12-16 21:57 ` [PATCH v3 1/3] ref-filter: add worktreepath atom nbelakovski @ 2018-12-18 17:22 ` Jeff King 2018-12-20 7:09 ` Nickolai Belakovski 0 siblings, 1 reply; 136+ messages in thread From: Jeff King @ 2018-12-18 17:22 UTC (permalink / raw) To: nbelakovski; +Cc: git, rafa.almas, gitster, avarab On Sun, Dec 16, 2018 at 01:57:57PM -0800, nbelakovski@gmail.com wrote: > From: Nickolai Belakovski <nbelakovski@gmail.com> > > Add an atom proving the path of the linked worktree where this ref is > checked out, if it is checked out in any linked worktrees, and empty > string otherwise. I stumbled over the word "proving" here. Maybe "showing" would be more clear? > diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt > index 901faef1bf..9590f7beab 100644 > --- a/Documentation/git-for-each-ref.txt > +++ b/Documentation/git-for-each-ref.txt > @@ -209,6 +209,10 @@ symref:: > `:lstrip` and `:rstrip` options in the same way as `refname` > above. > > +worktreepath:: > + The absolute path to the worktree in which the ref is checked > + out, if it is checked out in any linked worktree. ' ' otherwise. > + Normally single-quotes are used in asciidoc to emphasize text, and the quotes aren't passed through. Asciidoc (and asciidoctor) do seem to render the literal quotes here, which is good. I wonder if it would be more clear to just write it out, though, like: ...any linked worktree. Otherwise, replaced with a single space. Also, why are we replacing it with a single space? Wouldn't the empty string be more customary (and work with the other "if empty, then do this" formatting options)? > @@ -34,6 +36,8 @@ static struct ref_msg { > "ahead %d, behind %d" > }; > > +static struct worktree ** worktrees; Minor style nit: we put the "*" in a pointer declaration next to the variable name, without intervening whitespace. Like: static struct worktree **worktrees; > @@ -75,6 +79,12 @@ static struct expand_data { > struct object_info info; > } oi, oi_deref; > > +struct reftoworktreeinfo_entry { > + struct hashmap_entry ent; // must be the first member! > + char * ref; // key into map > + struct worktree * wt; > +}; A few style nits: - the "*" space thing from above (it's in other places below, too, but I won't point out each) - we prefer "/* */" comments, even for single-liners - since we do all-lowercase identifiers, use more underscores to break things up. E.g., ref_to_worktree_entry. Here we store the refname as a separate variable, but then point to the worktree itself to access wt->path. Why do we treat these differently? I.e., I'd expect to see either: 1. Each entry holding a single worktree object, and using its head_ref and path fields, like: struct ref_to_worktree_entry { struct hashmap_entry ent; /* must be first */ struct worktree *wt; }; .... entry = xmalloc(sizeof(*entry)); entry->wt = wt; hashmap_entry_init(entry, strhash(wt->head_ref)); ... strbuf_addstr(&out, result->wt->path); 2. Each entry containing just the bits it needs, like: struct ref_to_worktree_entry { struct hashmap_entry ent; /* must be first */ char *ref; char *path; }; ... /* * We could use FLEXPTR_ALLOC_STR() here, but it doesn't actually * support holding _two_ strings. Separate allocations probably * aren't a huge deal here, since there are only a handful of * worktrees. */ entry = xmalloc(sizeof(*entry)); entry->ref = wt->head_ref; entry->path = wt->path; hashmap_entry_init(entry, strhash(entry->ref)); ... strbuf_addstr(&out, result->path); I think the first one is strictly preferable unless we're worried about the lifetime of the "struct worktree" going away. I don't think that's an issue, though; they are ours until we call free_worktrees(). > @@ -114,6 +124,7 @@ static struct used_atom { > } objectname; > struct refname_atom refname; > char *head; > + struct hashmap reftoworktreeinfo_map; > } u; > } *used_atom; This uses one map for each %(worktree) we use. But won't they all be the same? It would ideally be associated with the ref-filter. There's no ref-filter context struct to hold this kind of data, just static globals in ref-filter.c (including this used_atom struct!). That's something we'll probably need to fix in the long run, but I think it would be reasonable to just have: static struct hashmap ref_to_worktree_map; next to the declaration of used_atom_cnt, need_symref, etc. And then those can all eventually get moved into a struct together. > @@ -461,6 +497,7 @@ static struct { > { "flag", SOURCE_NONE }, > { "HEAD", SOURCE_NONE, FIELD_STR, head_atom_parser }, > { "color", SOURCE_NONE, FIELD_STR, color_atom_parser }, > + { "worktreepath", SOURCE_NONE, FIELD_STR, worktree_atom_parser }, > { "align", SOURCE_NONE, FIELD_STR, align_atom_parser }, > { "end", SOURCE_NONE }, > { "if", SOURCE_NONE, FIELD_STR, if_atom_parser }, Marking as SOURCE_NONE makes sense. > +static const char * get_worktree_info(const struct used_atom *atom, const struct ref_array_item *ref) > +{ > + struct strbuf val = STRBUF_INIT; > + struct reftoworktreeinfo_entry * entry; > + struct reftoworktreeinfo_entry * lookup_result; > + > + FLEXPTR_ALLOC_STR(entry, ref, ref->refname); > + hashmap_entry_init(entry, strhash(entry->ref)); > + lookup_result = hashmap_get(&(atom->u.reftoworktreeinfo_map), entry, NULL); > + free(entry); We shouldn't need to do an allocation just for a lookup. That's what the extra "keydata" parameter is for in the comparison function. And I guess this is what led you to have "char *ref" in the struct, rather than reusing wt->head_ref (because you don't have a "struct worktree" here). You should be able to do it like this: struct hashmap_entry entry; struct ref_to_worktree_entry *result; hashmap_entry_init(entry, strhash(ref->refname)); result = hashmap_get(&ref_to_worktree_map, &entry, ref->refname)); ... and then your comparison function would look like this: int ref_to_worktree_hashcmp(const void *data, const void *entry, const void *entry_or_key, const void *keydata) { const struct ref_to_worktree_entry *a = entry; const struct ref_to_worktree_entry *b = entry; if (keydata) return strcmp(a->wt->head_ref, keydata); else return strcmp(a->wt->head_ref, b->wt->head_ref); } If you're thinking that this API is totally confusing and hard to figure out, I agree. It's optimized to avoid extra allocations. I wish we had a better one for simple cases (especially string->string mappings like this). Speaking of comparison functions, I didn't see one in your patch. Don't you need to pass one to hashmap_init? > + if (lookup_result) > + { > + if (!strncmp(atom->name, "worktreepath", strlen(atom->name))) > + strbuf_addstr(&val, lookup_result->wt->path); > + } > + else > + strbuf_addstr(&val, " "); What's this extra strncmp about? If we're _not_ a worktreepath atom, we'd still do the lookup only to put nothing in the string? I think we'd only call this function when populate_value() sees a worktreepath atom, though: > @@ -1537,6 +1596,10 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err) > > if (starts_with(name, "refname")) > refname = get_refname(atom, ref); > + else if (starts_with(name, "worktreepath")) { > + v->s = get_worktree_info(atom, ref); > + continue; > + } So it would be OK to drop the check of atom->name again inside get_worktree_info(). > @@ -2013,7 +2076,14 @@ void ref_array_clear(struct ref_array *array) > int i; > > for (i = 0; i < used_atom_cnt; i++) > + { > + if (!strncmp(used_atom[i].name, "worktreepath", strlen("worktreepath"))) > + { > + hashmap_free(&(used_atom[i].u.reftoworktreeinfo_map), 1); > + free_worktrees(worktrees); > + } And if we move the mapping out to a static global, then this only has to be done once, not once per atom. In fact, I think this could double-free "worktrees" with your current patch if you have two "%(worktree)" placeholders, since "worktrees" already is a global. > diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh > index fc067ed672..add70a4c3e 100755 > --- a/t/t6302-for-each-ref-filter.sh > +++ b/t/t6302-for-each-ref-filter.sh > @@ -441,4 +441,19 @@ test_expect_success '--merged is incompatible with --no-merged' ' > test_must_fail git for-each-ref --merged HEAD --no-merged HEAD > ' > > +test_expect_success '"add" a worktree' ' > + mkdir worktree_dir && > + git worktree add -b master_worktree worktree_dir master > +' > + > +test_expect_success 'validate worktree atom' ' > + cat >expect <<-\EOF && > + master: checked out in a worktree > + master_worktree: checked out in a worktree > + side: not checked out in a worktree > + EOF > + git for-each-ref --format="%(refname:short): %(if)%(worktreepath)%(then)checked out in a worktree%(else)not checked out in a worktree%(end)" refs/heads/ >actual && > + test_cmp expect actual > +' It's probably worth testing that the path we get is actually sane, too. I.e., expect something more like: cat >expect <<-\EOF master: $PWD master: $PWD/worktree side: not checked out EOF git for-each-ref \ --format="%(refname:short): %(if)%(worktreepath)%(then)%(worktreepath)%(else)not checked %out%(end) (I wish there was a way to avoid that really long line, but I don't think there is). -Peff ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v3 1/3] ref-filter: add worktreepath atom 2018-12-18 17:22 ` Jeff King @ 2018-12-20 7:09 ` Nickolai Belakovski 2018-12-20 14:59 ` Jeff King 0 siblings, 1 reply; 136+ messages in thread From: Nickolai Belakovski @ 2018-12-20 7:09 UTC (permalink / raw) To: Jeff King Cc: git, Rafael Ascensão, Junio C Hamano, Ævar Arnfjörð Bjarmason On Tue, Dec 18, 2018 at 9:22 AM Jeff King <peff@peff.net> wrote: > > On Sun, Dec 16, 2018 at 01:57:57PM -0800, nbelakovski@gmail.com wrote: > > > From: Nickolai Belakovski <nbelakovski@gmail.com> > > > > Add an atom proving the path of the linked worktree where this ref is > > checked out, if it is checked out in any linked worktrees, and empty > > string otherwise. > > I stumbled over the word "proving" here. Maybe "showing" would be more > clear? Oops, providing > > +worktreepath:: > > + The absolute path to the worktree in which the ref is checked > > + out, if it is checked out in any linked worktree. ' ' otherwise. > > + > > Also, why are we replacing it with a single space? Wouldn't the empty > string be more customary (and work with the other "if empty, then do > this" formatting options)? I was just following what was done for HEAD, but overall I agree that empty is preferable to single space, will change. > Minor style nit: we put the "*" in a pointer declaration next to the > variable name, without intervening whitespace. Like: > > static struct worktree **worktrees; Gotcha, will do, thanks for pointing it out. To sum up the hashmap comments: -I hadn't thought to re-use the head_ref of worktree as the key. That's clever. I like the readability of having separate entries for key and value, but I can see the benefit of not having to do an extra allocation. I can make up for the readability hit with a comment. -Actually, for any valid use case there will only be one instance of the map since the entries of used_atom are cached, but regardless it makes sense to keep per-atom info in used_atom and global context somewhere else, so I'll make that change to make it a static variable outside of used_atom. -Will change the lookup logic to remove the extra allocation. Since I'm letting the hashmap use its internal comparison function on the hash, I don't need to provide a comparison function. > What's this extra strncmp about? If we're _not_ a worktreepath atom, > we'd still do the lookup only to put nothing in the string? Leftover from an earlier iteration where I was going to support getting more info out of the worktree struct. I decided to limit scope to just the info I really needed for the branch change. I left it like this because I thought it would make the code more readable for someone who wanted to come in and add that extra info, but I think you're right that it ends up just reading kind of awkwardly. > > > @@ -2013,7 +2076,14 @@ void ref_array_clear(struct ref_array *array) > > int i; > > > > for (i = 0; i < used_atom_cnt; i++) > > + { > > + if (!strncmp(used_atom[i].name, "worktreepath", strlen("worktreepath"))) > > + { > > + hashmap_free(&(used_atom[i].u.reftoworktreeinfo_map), 1); > > + free_worktrees(worktrees); > > + } > > And if we move the mapping out to a static global, then this only has to > be done once, not once per atom. In fact, I think this could double-free > "worktrees" with your current patch if you have two "%(worktree)" > placeholders, since "worktrees" already is a global. Only if someone put a colon on one of the %(worktree) atoms, otherwise they're all cached, but as you say moot point anyway if the map is moved outside the used_atom structure. > > It's probably worth testing that the path we get is actually sane, too. > I.e., expect something more like: > > cat >expect <<-\EOF > master: $PWD > master: $PWD/worktree > side: not checked out > EOF > git for-each-ref \ > --format="%(refname:short): %(if)%(worktreepath)%(then)%(worktreepath)%(else)not checked %out%(end) > > (I wish there was a way to avoid that really long line, but I don't > think there is). > Yea good call, can do. Thanks for all the feedback, will try to turn these around quickly. ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v3 1/3] ref-filter: add worktreepath atom 2018-12-20 7:09 ` Nickolai Belakovski @ 2018-12-20 14:59 ` Jeff King 2018-12-24 8:47 ` [PATCH v4 0/3] nbelakovski 0 siblings, 1 reply; 136+ messages in thread From: Jeff King @ 2018-12-20 14:59 UTC (permalink / raw) To: Nickolai Belakovski Cc: git, Rafael Ascensão, Junio C Hamano, Ævar Arnfjörð Bjarmason On Wed, Dec 19, 2018 at 11:09:59PM -0800, Nickolai Belakovski wrote: > > Also, why are we replacing it with a single space? Wouldn't the empty > > string be more customary (and work with the other "if empty, then do > > this" formatting options)? > > I was just following what was done for HEAD, but overall I agree that > empty is preferable to single space, will change. Ah, right, that makes more sense. I do still think for %(HEAD) it's a little different because it is "+" or a single space, so always one character. Here we have some value or not, and in the "not" case for such things we usually give an empty string (e.g., for %(push), %(upstream), etc). > To sum up the hashmap comments: > -I hadn't thought to re-use the head_ref of worktree as the key. > That's clever. I like the readability of having separate entries for > key and value, but I can see the benefit of not having to do an extra > allocation. I can make up for the readability hit with a comment. Thanks, that makes sense. > -Actually, for any valid use case there will only be one instance of > the map since the entries of used_atom are cached, but regardless it > makes sense to keep per-atom info in used_atom and global context > somewhere else, so I'll make that change to make it a static variable > outside of used_atom. Ah, right, I forgot there was some magic around used_atom. I do still agree that the separate static global makes things a little simpler. > -Will change the lookup logic to remove the extra allocation. Since > I'm letting the hashmap use its internal comparison function on the > hash, I don't need to provide a comparison function. I don't think that works. The default function is always_equal(), which will treat two entries equal if they have the same hash value. I.e., any collisions would be considered a match. > Thanks for all the feedback, will try to turn these around quickly. Great, thanks! I'll be on vacation for the next two weeks, so I may be very slow to look at the next iteration. :) -Peff ^ permalink raw reply [flat|nested] 136+ messages in thread
* [PATCH v4 0/3] 2018-12-20 14:59 ` Jeff King @ 2018-12-24 8:47 ` nbelakovski 2018-12-24 8:47 ` [PATCH v4 1/3] ref-filter: add worktreepath atom nbelakovski ` (3 more replies) 0 siblings, 4 replies; 136+ messages in thread From: nbelakovski @ 2018-12-24 8:47 UTC (permalink / raw) To: git; +Cc: peff, rafa.almas, gitster, avarab, Nickolai Belakovski From: Nickolai Belakovski <nbelakovski@gmail.com> > I don't think that works. The default function is always_equal(), which > will treat two entries equal if they have the same hash value. I.e., any > collisions would be considered a match. You're absolutely right. I've added a compare function, but I left out the functionality for it to work with an entry passed in as a key. Doing so would mean the user would have to allocate a worktree struct, which just seems silly when the ref is all that's needed (and also defeats the purpose of avoiding extra allocations). And while most of the hashmap API seems OK, yea, this is definitely awful. It feels like it should just be able to take a key and return either an entry or NULL, and do away with entry_or_key and equals_function_data. Travis-CI results: https://travis-ci.org/nbelakovski/git/builds/471787317 Nickolai Belakovski (3): ref-filter: add worktreepath atom branch: Mark and color a branch differently if it is checked out in a linked worktree branch: Add an extra verbose output displaying worktree path for refs checked out in a linked worktree Documentation/git-for-each-ref.txt | 5 +++ builtin/branch.c | 16 ++++++--- ref-filter.c | 72 +++++++++++++++++++++++++++++++++++++- t/t3200-branch.sh | 8 ++--- t/t3203-branch-output.sh | 21 +++++++++++ t/t6302-for-each-ref-filter.sh | 15 ++++++++ 6 files changed, 128 insertions(+), 9 deletions(-) -- 2.14.2 ^ permalink raw reply [flat|nested] 136+ messages in thread
* [PATCH v4 1/3] ref-filter: add worktreepath atom 2018-12-24 8:47 ` [PATCH v4 0/3] nbelakovski @ 2018-12-24 8:47 ` nbelakovski 2019-01-03 5:40 ` Jeff King 2018-12-24 8:47 ` [PATCH v4 2/3] branch: Mark and color a branch differently if it is checked out in a linked worktree nbelakovski ` (2 subsequent siblings) 3 siblings, 1 reply; 136+ messages in thread From: nbelakovski @ 2018-12-24 8:47 UTC (permalink / raw) To: git; +Cc: peff, rafa.almas, gitster, avarab, Nickolai Belakovski From: Nickolai Belakovski <nbelakovski@gmail.com> Add an atom providing the path of the linked worktree where this ref is checked out, if it is checked out in any linked worktrees, and empty string otherwise. --- Documentation/git-for-each-ref.txt | 5 +++ ref-filter.c | 72 +++++++++++++++++++++++++++++++++++++- t/t6302-for-each-ref-filter.sh | 15 ++++++++ 3 files changed, 91 insertions(+), 1 deletion(-) diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt index 901faef1bf..caba1c23b8 100644 --- a/Documentation/git-for-each-ref.txt +++ b/Documentation/git-for-each-ref.txt @@ -209,6 +209,11 @@ symref:: `:lstrip` and `:rstrip` options in the same way as `refname` above. +worktreepath:: + The absolute path to the worktree in which the ref is checked + out, if it is checked out in any linked worktree. Empty string + otherwise. + In addition to the above, for commit and tag objects, the header field names (`tree`, `parent`, `object`, `type`, and `tag`) can be used to specify the value in the header field. diff --git a/ref-filter.c b/ref-filter.c index 5de616befe..240e7b80f8 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -20,6 +20,8 @@ #include "commit-slab.h" #include "commit-graph.h" #include "commit-reach.h" +#include "worktree.h" +#include "hashmap.h" static struct ref_msg { const char *gone; @@ -34,6 +36,8 @@ static struct ref_msg { "ahead %d, behind %d" }; +static struct worktree **worktrees; + void setup_ref_filter_porcelain_msg(void) { msgs.gone = _("gone"); @@ -75,6 +79,11 @@ static struct expand_data { struct object_info info; } oi, oi_deref; +struct ref_to_worktree_entry { + struct hashmap_entry ent; /* must be the first member! */ + struct worktree *wt; /* key is wt->head_ref */ +}; + /* * An atom is a valid field atom listed below, possibly prefixed with * a "*" to denote deref_tag(). @@ -116,7 +125,8 @@ static struct used_atom { char *head; } u; } *used_atom; -static int used_atom_cnt, need_tagged, need_symref; +static int used_atom_cnt, need_tagged, need_symref, has_worktree; +static struct hashmap ref_to_worktree_map; /* * Expand string, append it to strbuf *sb, then return error code ret. @@ -420,6 +430,42 @@ static int head_atom_parser(const struct ref_format *format, struct used_atom *a return 0; } +static int worktree_hashmap_cmpfnc(const void *unused_lookupdata, const void *existing_hashmap_entry_to_test, + const void *unused_key, const void *keydata_aka_refname) +{ + const struct ref_to_worktree_entry *e = existing_hashmap_entry_to_test; + return strcmp(e->wt->head_ref, keydata_aka_refname); +} + +static int worktree_atom_parser(const struct ref_format *format, + struct used_atom *atom, + const char *arg, + struct strbuf *unused_err) +{ + int i; + if (has_worktree) + return 0; + + worktrees = get_worktrees(0); + + hashmap_init(&ref_to_worktree_map, worktree_hashmap_cmpfnc, NULL, 0); + + for (i = 0; worktrees[i]; i++) { + if (worktrees[i]->head_ref) { + struct ref_to_worktree_entry *entry; + entry = xmalloc(sizeof(*entry)); + entry->wt = worktrees[i]; + hashmap_entry_init(entry, strhash(worktrees[i]->head_ref)); + + hashmap_add(&ref_to_worktree_map, entry); + } + } + + has_worktree = 1; + + return 0; +} + static struct { const char *name; info_source source; @@ -461,6 +507,7 @@ static struct { { "flag", SOURCE_NONE }, { "HEAD", SOURCE_NONE, FIELD_STR, head_atom_parser }, { "color", SOURCE_NONE, FIELD_STR, color_atom_parser }, + { "worktreepath", SOURCE_NONE, FIELD_STR, worktree_atom_parser }, { "align", SOURCE_NONE, FIELD_STR, align_atom_parser }, { "end", SOURCE_NONE }, { "if", SOURCE_NONE, FIELD_STR, if_atom_parser }, @@ -1500,6 +1547,20 @@ static int get_object(struct ref_array_item *ref, int deref, struct object **obj return 0; } +static const char *get_worktree_path(const struct used_atom *atom, const struct ref_array_item *ref) +{ + struct strbuf val = STRBUF_INIT; + struct hashmap_entry entry; + struct ref_to_worktree_entry *lookup_result; + + hashmap_entry_init(&entry, strhash(ref->refname)); + lookup_result = hashmap_get(&ref_to_worktree_map, &entry, ref->refname); + + strbuf_addstr(&val, lookup_result ? lookup_result->wt->path : ""); + + return strbuf_detach(&val, NULL); +} + /* * Parse the object referred by ref, and grab needed value. */ @@ -1537,6 +1598,10 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err) if (starts_with(name, "refname")) refname = get_refname(atom, ref); + else if (starts_with(name, "worktreepath")) { + v->s = get_worktree_path(atom, ref); + continue; + } else if (starts_with(name, "symref")) refname = get_symref(atom, ref); else if (starts_with(name, "upstream")) { @@ -2020,6 +2085,11 @@ void ref_array_clear(struct ref_array *array) free_array_item(array->items[i]); FREE_AND_NULL(array->items); array->nr = array->alloc = 0; + if (has_worktree) + { + hashmap_free(&ref_to_worktree_map, 1); + free_worktrees(worktrees); + } } static void do_merge_filter(struct ref_filter_cbdata *ref_cbdata) diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh index fc067ed672..d70517a6ae 100755 --- a/t/t6302-for-each-ref-filter.sh +++ b/t/t6302-for-each-ref-filter.sh @@ -441,4 +441,19 @@ test_expect_success '--merged is incompatible with --no-merged' ' test_must_fail git for-each-ref --merged HEAD --no-merged HEAD ' +test_expect_success '"add" a worktree' ' + mkdir worktree_dir && + git worktree add -b master_worktree worktree_dir master +' + +test_expect_success 'validate worktree atom' ' + { + echo master: $PWD && + echo master_worktree: $PWD/worktree_dir && + echo side: not checked out + } > expect && + git for-each-ref --format="%(refname:short): %(if)%(worktreepath)%(then)%(worktreepath)%(else)not checked out%(end)" refs/heads/ >actual && + test_cmp expect actual +' + test_done -- 2.14.2 ^ permalink raw reply related [flat|nested] 136+ messages in thread
* Re: [PATCH v4 1/3] ref-filter: add worktreepath atom 2018-12-24 8:47 ` [PATCH v4 1/3] ref-filter: add worktreepath atom nbelakovski @ 2019-01-03 5:40 ` Jeff King 2019-01-03 9:31 ` Eric Sunshine 0 siblings, 1 reply; 136+ messages in thread From: Jeff King @ 2019-01-03 5:40 UTC (permalink / raw) To: nbelakovski; +Cc: git, rafa.almas, gitster, avarab On Mon, Dec 24, 2018 at 12:47:54AM -0800, nbelakovski@gmail.com wrote: > [...] Thanks for keeping with this. I think we're getting quite close, though I did find a few small-ish issues. > @@ -34,6 +36,8 @@ static struct ref_msg { > "ahead %d, behind %d" > }; > > +static struct worktree **worktrees; > + Maybe define this near "struct hashmap ref_to_worktree_map" so it's more obvious that the two are related? > @@ -75,6 +79,11 @@ static struct expand_data { > struct object_info info; > } oi, oi_deref; > > +struct ref_to_worktree_entry { > + struct hashmap_entry ent; /* must be the first member! */ > + struct worktree *wt; /* key is wt->head_ref */ > +}; Indent with spaces? > -static int used_atom_cnt, need_tagged, need_symref; > +static int used_atom_cnt, need_tagged, need_symref, has_worktree; > +static struct hashmap ref_to_worktree_map; Makes sense. I thought at first has_worktree was a flag that we might care about between parsing and formatting, but it's really just a flag to say "we lazy-loaded the worktree list". > +static int worktree_hashmap_cmpfnc(const void *unused_lookupdata, const void *existing_hashmap_entry_to_test, > + const void *unused_key, const void *keydata_aka_refname) > +{ > + const struct ref_to_worktree_entry *e = existing_hashmap_entry_to_test; > + return strcmp(e->wt->head_ref, keydata_aka_refname); > +} So from the discussion in the cover letter, this needs to be more like: static int worktree_hashmap_cmpfnc(const void *unused_lookupdata, const void *ve1, const void *ve2, const void *keydata_aka_refname) { const struct ref_to_worktree_entry *e1 = ve1, *e2 = ve2; return strcmp(e1->wt->head_ref, keydata_aka_refname ? keydata_aka_refname : e2->wt->head_ref); } > +static int worktree_atom_parser(const struct ref_format *format, > + struct used_atom *atom, > + const char *arg, > + struct strbuf *unused_err) > +{ > + int i; > + if (has_worktree) > + return 0; Minor style nit, but please put a space between the declarations and the start of the code (not strictly necessary for a short function which has no other linebreaks, like the cmpfunc above, but here I think it's confusing not to). > + worktrees = get_worktrees(0); > + > + hashmap_init(&ref_to_worktree_map, worktree_hashmap_cmpfnc, NULL, 0); > + > + for (i = 0; worktrees[i]; i++) { > + if (worktrees[i]->head_ref) { > + struct ref_to_worktree_entry *entry; > + entry = xmalloc(sizeof(*entry)); > + entry->wt = worktrees[i]; > + hashmap_entry_init(entry, strhash(worktrees[i]->head_ref)); > + > + hashmap_add(&ref_to_worktree_map, entry); > + } > + } Makes sense to load the map. > +static const char *get_worktree_path(const struct used_atom *atom, const struct ref_array_item *ref) > +{ > + struct strbuf val = STRBUF_INIT; > + struct hashmap_entry entry; > + struct ref_to_worktree_entry *lookup_result; > + > + hashmap_entry_init(&entry, strhash(ref->refname)); > + lookup_result = hashmap_get(&ref_to_worktree_map, &entry, ref->refname); > + > + strbuf_addstr(&val, lookup_result ? lookup_result->wt->path : ""); > + > + return strbuf_detach(&val, NULL); > +} And that makes sense to look up an item in it. Good. Adding an empty string to a strbuf is a noop, so that part might more clearly be written as just: if (lookup_result) strbuf_addstr(&val, lookup_result->wt->path); We return a "const char *" here, but the result is always allocated. Do we leak the result? Or should this return a "char *"? I think there are a lot of other atoms that leak currently, but that is being fixed in another topic that is currently in pu. > @@ -2020,6 +2085,11 @@ void ref_array_clear(struct ref_array *array) > free_array_item(array->items[i]); > FREE_AND_NULL(array->items); > array->nr = array->alloc = 0; > + if (has_worktree) > + { > + hashmap_free(&ref_to_worktree_map, 1); > + free_worktrees(worktrees); > + } Here we free everything, but we don't unset has_worktree. So anybody trying to format more refs afterward would see our freed worktree list. We probably want: has_worktree = 0; here. Or simpler still, I think get_worktrees() will always return a non-NULL list (even if it is empty). So you could just drop has_worktree entirely, and use: if (worktrees) return; /* already loaded */; in the loading function, and: free_worktrees(worktrees); worktrees = NULL; here. > +test_expect_success '"add" a worktree' ' > + mkdir worktree_dir && > + git worktree add -b master_worktree worktree_dir master > +' > + > +test_expect_success 'validate worktree atom' ' > + { > + echo master: $PWD && > + echo master_worktree: $PWD/worktree_dir && > + echo side: not checked out > + } > expect && Minor style nit: use "} >expect" without the extra space. This checks the actual directories. Good. I can never remember the rules for when to use $PWD versus $(pwd) on Windows. We may run afoul of the distinction here. -Peff ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v4 1/3] ref-filter: add worktreepath atom 2019-01-03 5:40 ` Jeff King @ 2019-01-03 9:31 ` Eric Sunshine 0 siblings, 0 replies; 136+ messages in thread From: Eric Sunshine @ 2019-01-03 9:31 UTC (permalink / raw) To: Jeff King Cc: Nickolai Belakovski, Git List, Rafael Ascensao, Junio C Hamano, Ævar Arnfjörð Bjarmason On Thu, Jan 3, 2019 at 12:40 AM Jeff King <peff@peff.net> wrote: > On Mon, Dec 24, 2018 at 12:47:54AM -0800, nbelakovski@gmail.com wrote: > > +test_expect_success 'validate worktree atom' ' > > + { > > + echo master: $PWD && > > + echo master_worktree: $PWD/worktree_dir && > > + echo side: not checked out > > + } > expect && > > Minor style nit: use "} >expect" without the extra space. An interpolating here-doc would be even more natural: cat >expect <-EOF && master: $(pwd) master_worktree: $(pwd)/worktree_dir side: not checked out EOF > This checks the actual directories. Good. I can never remember the rules > for when to use $PWD versus $(pwd) on Windows. We may run afoul of the > distinction here. As I understand it, this is exactly a case in which you would need to use $(pwd); namely, when coming up with an "expect" value. t/README talks about it. ^ permalink raw reply [flat|nested] 136+ messages in thread
* [PATCH v4 2/3] branch: Mark and color a branch differently if it is checked out in a linked worktree 2018-12-24 8:47 ` [PATCH v4 0/3] nbelakovski 2018-12-24 8:47 ` [PATCH v4 1/3] ref-filter: add worktreepath atom nbelakovski @ 2018-12-24 8:47 ` nbelakovski 2018-12-24 8:47 ` [PATCH v4 3/3] branch: Add an extra verbose output displaying worktree path for refs " nbelakovski 2019-01-03 5:22 ` [PATCH v4 0/3] Jeff King 3 siblings, 0 replies; 136+ messages in thread From: nbelakovski @ 2018-12-24 8:47 UTC (permalink / raw) To: git; +Cc: peff, rafa.almas, gitster, avarab, Nickolai Belakovski From: Nickolai Belakovski <nbelakovski@gmail.com> In order to more clearly display which branches are active, the output of git branch is modified to mark branches checkout out in a linked worktree with a "+" and color them in cyan (in contrast to the current branch, which will still be denoted with a "*" and colored in green) This is meant to simplify workflows related to worktree, particularly due to the limitations of not being able to check out the same branch in two worktrees and the inability to delete a branch checked out in a worktree. When performing branch operations like checkout and delete, it would be useful to know more readily if the branches in which the user is interested are already checked out in a worktree. The git worktree list command contains the relevant information, however this is a much less frquently used command than git branch. --- builtin/branch.c | 12 ++++++++---- t/t3200-branch.sh | 8 ++++---- t/t3203-branch-output.sh | 21 +++++++++++++++++++++ 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/builtin/branch.c b/builtin/branch.c index 0c55f7f065..2a24153b78 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -47,6 +47,7 @@ static char branch_colors[][COLOR_MAXLEN] = { GIT_COLOR_NORMAL, /* LOCAL */ GIT_COLOR_GREEN, /* CURRENT */ GIT_COLOR_BLUE, /* UPSTREAM */ + GIT_COLOR_CYAN, /* WORKTREE */ }; enum color_branch { BRANCH_COLOR_RESET = 0, @@ -54,7 +55,8 @@ enum color_branch { BRANCH_COLOR_REMOTE = 2, BRANCH_COLOR_LOCAL = 3, BRANCH_COLOR_CURRENT = 4, - BRANCH_COLOR_UPSTREAM = 5 + BRANCH_COLOR_UPSTREAM = 5, + BRANCH_COLOR_WORKTREE = 6 }; static const char *color_branch_slots[] = { @@ -64,6 +66,7 @@ static const char *color_branch_slots[] = { [BRANCH_COLOR_LOCAL] = "local", [BRANCH_COLOR_CURRENT] = "current", [BRANCH_COLOR_UPSTREAM] = "upstream", + [BRANCH_COLOR_WORKTREE] = "worktree", }; static struct string_list output = STRING_LIST_INIT_DUP; @@ -342,9 +345,10 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r struct strbuf local = STRBUF_INIT; struct strbuf remote = STRBUF_INIT; - strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else) %s%%(end)", - branch_get_color(BRANCH_COLOR_CURRENT), - branch_get_color(BRANCH_COLOR_LOCAL)); + strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else)%%(if)%%(worktreepath)%%(then)+ %s%%(else) %s%%(end)%%(end)", + branch_get_color(BRANCH_COLOR_CURRENT), + branch_get_color(BRANCH_COLOR_WORKTREE), + branch_get_color(BRANCH_COLOR_LOCAL)); strbuf_addf(&remote, " %s", branch_get_color(BRANCH_COLOR_REMOTE)); diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index 478b82cf9b..e404f6e23c 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -292,7 +292,7 @@ test_expect_success 'git branch --list -v with --abbrev' ' test_expect_success 'git branch --column' ' COLUMNS=81 git branch --column=column >actual && cat >expected <<\EOF && - a/b/c bam foo l * master n o/p r + a/b/c + bam foo l * master n o/p r abc bar j/k m/m master2 o/o q EOF test_cmp expected actual @@ -307,7 +307,7 @@ test_expect_success 'git branch --column with an extremely long branch name' ' cat >expected <<EOF && a/b/c abc - bam ++ bam bar foo j/k @@ -332,7 +332,7 @@ test_expect_success 'git branch with column.*' ' git config --unset column.branch && git config --unset column.ui && cat >expected <<\EOF && - a/b/c bam foo l * master n o/p r + a/b/c + bam foo l * master n o/p r abc bar j/k m/m master2 o/o q EOF test_cmp expected actual @@ -349,7 +349,7 @@ test_expect_success 'git branch -v with column.ui ignored' ' cat >expected <<\EOF && a/b/c abc - bam ++ bam bar foo j/k diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh index ee6787614c..94ab05ad59 100755 --- a/t/t3203-branch-output.sh +++ b/t/t3203-branch-output.sh @@ -240,6 +240,27 @@ test_expect_success 'git branch --format option' ' test_i18ncmp expect actual ' +test_expect_success '"add" a worktree' ' + mkdir worktree_dir && + git worktree add -b master_worktree worktree_dir master +' + +cat >expect <<'EOF' +* <GREEN>(HEAD detached from fromtag)<RESET> + ambiguous<RESET> + branch-one<RESET> + branch-two<RESET> + master<RESET> ++ <CYAN>master_worktree<RESET> + ref-to-branch<RESET> -> branch-one + ref-to-remote<RESET> -> origin/branch-one +EOF +test_expect_success TTY 'worktree colors correct' ' + test_terminal git branch >actual.raw && + test_decode_color <actual.raw >actual && + test_cmp expect actual +' + test_expect_success "set up color tests" ' echo "<RED>master<RESET>" >expect.color && echo "master" >expect.bare && -- 2.14.2 ^ permalink raw reply related [flat|nested] 136+ messages in thread
* [PATCH v4 3/3] branch: Add an extra verbose output displaying worktree path for refs checked out in a linked worktree 2018-12-24 8:47 ` [PATCH v4 0/3] nbelakovski 2018-12-24 8:47 ` [PATCH v4 1/3] ref-filter: add worktreepath atom nbelakovski 2018-12-24 8:47 ` [PATCH v4 2/3] branch: Mark and color a branch differently if it is checked out in a linked worktree nbelakovski @ 2018-12-24 8:47 ` nbelakovski 2019-01-03 5:42 ` Jeff King 2019-01-03 5:22 ` [PATCH v4 0/3] Jeff King 3 siblings, 1 reply; 136+ messages in thread From: nbelakovski @ 2018-12-24 8:47 UTC (permalink / raw) To: git; +Cc: peff, rafa.almas, gitster, avarab, Nickolai Belakovski From: Nickolai Belakovski <nbelakovski@gmail.com> --- builtin/branch.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/builtin/branch.c b/builtin/branch.c index 2a24153b78..56589a3684 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -366,6 +366,10 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r strbuf_addstr(&local, branch_get_color(BRANCH_COLOR_RESET)); strbuf_addf(&local, " %s ", obname.buf); + if (filter->verbose > 2) + strbuf_addf(&local, "%s%%(if:notequals=*)%%(HEAD)%%(then)%%(if)%%(worktreepath)%%(then)%%(worktreepath) %%(end)%%(end)%s", + branch_get_color(BRANCH_COLOR_WORKTREE), branch_get_color(BRANCH_COLOR_RESET)); + if (filter->verbose > 1) strbuf_addf(&local, "%%(if)%%(upstream)%%(then)[%s%%(upstream:short)%s%%(if)%%(upstream:track)" "%%(then): %%(upstream:track,nobracket)%%(end)] %%(end)%%(contents:subject)", -- 2.14.2 ^ permalink raw reply related [flat|nested] 136+ messages in thread
* Re: [PATCH v4 3/3] branch: Add an extra verbose output displaying worktree path for refs checked out in a linked worktree 2018-12-24 8:47 ` [PATCH v4 3/3] branch: Add an extra verbose output displaying worktree path for refs " nbelakovski @ 2019-01-03 5:42 ` Jeff King 0 siblings, 0 replies; 136+ messages in thread From: Jeff King @ 2019-01-03 5:42 UTC (permalink / raw) To: nbelakovski; +Cc: git, rafa.almas, gitster, avarab On Mon, Dec 24, 2018 at 12:47:56AM -0800, nbelakovski@gmail.com wrote: > From: Nickolai Belakovski <nbelakovski@gmail.com> > > --- > builtin/branch.c | 4 ++++ > 1 file changed, 4 insertions(+) This patch should describe the new behavior in Documentation/git-branch.txt, I'd think. -Peff ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v4 0/3] 2018-12-24 8:47 ` [PATCH v4 0/3] nbelakovski ` (2 preceding siblings ...) 2018-12-24 8:47 ` [PATCH v4 3/3] branch: Add an extra verbose output displaying worktree path for refs " nbelakovski @ 2019-01-03 5:22 ` Jeff King 3 siblings, 0 replies; 136+ messages in thread From: Jeff King @ 2019-01-03 5:22 UTC (permalink / raw) To: nbelakovski; +Cc: git, rafa.almas, gitster, avarab On Mon, Dec 24, 2018 at 12:47:53AM -0800, nbelakovski@gmail.com wrote: > From: Nickolai Belakovski <nbelakovski@gmail.com> > > > I don't think that works. The default function is always_equal(), which > > will treat two entries equal if they have the same hash value. I.e., any > > collisions would be considered a match. > > You're absolutely right. I've added a compare function, but I left out the > functionality for it to work with an entry passed in as a key. Doing so would > mean the user would have to allocate a worktree struct, which just seems silly > when the ref is all that's needed (and also defeats the purpose of avoiding > extra allocations). Unfortunately, that doesn't quite work. :) Your compare function has to handle _both_ cases: two keys, or one key and a keydata. The former may be called when the hashmap has to compare two entries internally (e.g., when it has to re-bucket all of the entries after a resize). Your tests likely wouldn't run into this case in practice, since you'd only have a handful of worktrees. But if you added, say, thousands of worktrees and we had to grow the hash midway through the process, it would segfault. So you do need to handle the case when your keydata is NULL. In theory it would also be used for comparisons if we used a more clever data structure to hold entries within a bucket. But since we just use a linked list and linear search for now, we don't. > And while most of the hashmap API seems OK, yea, this is definitely awful. It > feels like it should just be able to take a key and return either an entry or > NULL, and do away with entry_or_key and equals_function_data. In your case, yeah, equals_function_data is not used at all (but there are a few call-sites which need it to avoid relying on global data). But the entry_or_key (coupled with keydata) is the magic that lets the same function be used for both lookups as well as internal entry comparisons. I think having two separate comparison functions would make this a lot more clear, though likely at the cost of having more boilerplate. -Peff ^ permalink raw reply [flat|nested] 136+ messages in thread
* [PATCH v3 2/3] branch: Mark and color a branch differently if it is checked out in a linked worktree 2018-12-16 21:57 ` [PATCH v3 0/3] nbelakovski 2018-12-16 21:57 ` [PATCH v3 1/3] ref-filter: add worktreepath atom nbelakovski @ 2018-12-16 21:57 ` nbelakovski 2018-12-16 21:57 ` [PATCH v3 3/3] branch: Add an extra verbose output displaying worktree path for refs " nbelakovski 2018-12-18 17:25 ` [PATCH v3 0/3] Jeff King 3 siblings, 0 replies; 136+ messages in thread From: nbelakovski @ 2018-12-16 21:57 UTC (permalink / raw) To: git; +Cc: peff, rafa.almas, gitster, avarab, Nickolai Belakovski From: Nickolai Belakovski <nbelakovski@gmail.com> In order to more clearly display which branches are active, the output of git branch is modified to mark branches checkout out in a linked worktree with a "+" and color them in cyan (in contrast to the current branch, which will still be denoted with a "*" and colored in green) This is meant to simplify workflows related to worktree, particularly due to the limitations of not being able to check out the same branch in two worktrees and the inability to delete a branch checked out in a worktree. When performing branch operations like checkout and delete, it would be useful to know more readily if the branches in which the user is interested are already checked out in a worktree. The git worktree list command contains the relevant information, however this is a much less frquently used command than git branch. --- builtin/branch.c | 12 ++++++++---- t/t3200-branch.sh | 8 ++++---- t/t3203-branch-output.sh | 21 +++++++++++++++++++++ 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/builtin/branch.c b/builtin/branch.c index 0c55f7f065..2a24153b78 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -47,6 +47,7 @@ static char branch_colors[][COLOR_MAXLEN] = { GIT_COLOR_NORMAL, /* LOCAL */ GIT_COLOR_GREEN, /* CURRENT */ GIT_COLOR_BLUE, /* UPSTREAM */ + GIT_COLOR_CYAN, /* WORKTREE */ }; enum color_branch { BRANCH_COLOR_RESET = 0, @@ -54,7 +55,8 @@ enum color_branch { BRANCH_COLOR_REMOTE = 2, BRANCH_COLOR_LOCAL = 3, BRANCH_COLOR_CURRENT = 4, - BRANCH_COLOR_UPSTREAM = 5 + BRANCH_COLOR_UPSTREAM = 5, + BRANCH_COLOR_WORKTREE = 6 }; static const char *color_branch_slots[] = { @@ -64,6 +66,7 @@ static const char *color_branch_slots[] = { [BRANCH_COLOR_LOCAL] = "local", [BRANCH_COLOR_CURRENT] = "current", [BRANCH_COLOR_UPSTREAM] = "upstream", + [BRANCH_COLOR_WORKTREE] = "worktree", }; static struct string_list output = STRING_LIST_INIT_DUP; @@ -342,9 +345,10 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r struct strbuf local = STRBUF_INIT; struct strbuf remote = STRBUF_INIT; - strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else) %s%%(end)", - branch_get_color(BRANCH_COLOR_CURRENT), - branch_get_color(BRANCH_COLOR_LOCAL)); + strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else)%%(if)%%(worktreepath)%%(then)+ %s%%(else) %s%%(end)%%(end)", + branch_get_color(BRANCH_COLOR_CURRENT), + branch_get_color(BRANCH_COLOR_WORKTREE), + branch_get_color(BRANCH_COLOR_LOCAL)); strbuf_addf(&remote, " %s", branch_get_color(BRANCH_COLOR_REMOTE)); diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index 478b82cf9b..e404f6e23c 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -292,7 +292,7 @@ test_expect_success 'git branch --list -v with --abbrev' ' test_expect_success 'git branch --column' ' COLUMNS=81 git branch --column=column >actual && cat >expected <<\EOF && - a/b/c bam foo l * master n o/p r + a/b/c + bam foo l * master n o/p r abc bar j/k m/m master2 o/o q EOF test_cmp expected actual @@ -307,7 +307,7 @@ test_expect_success 'git branch --column with an extremely long branch name' ' cat >expected <<EOF && a/b/c abc - bam ++ bam bar foo j/k @@ -332,7 +332,7 @@ test_expect_success 'git branch with column.*' ' git config --unset column.branch && git config --unset column.ui && cat >expected <<\EOF && - a/b/c bam foo l * master n o/p r + a/b/c + bam foo l * master n o/p r abc bar j/k m/m master2 o/o q EOF test_cmp expected actual @@ -349,7 +349,7 @@ test_expect_success 'git branch -v with column.ui ignored' ' cat >expected <<\EOF && a/b/c abc - bam ++ bam bar foo j/k diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh index ee6787614c..94ab05ad59 100755 --- a/t/t3203-branch-output.sh +++ b/t/t3203-branch-output.sh @@ -240,6 +240,27 @@ test_expect_success 'git branch --format option' ' test_i18ncmp expect actual ' +test_expect_success '"add" a worktree' ' + mkdir worktree_dir && + git worktree add -b master_worktree worktree_dir master +' + +cat >expect <<'EOF' +* <GREEN>(HEAD detached from fromtag)<RESET> + ambiguous<RESET> + branch-one<RESET> + branch-two<RESET> + master<RESET> ++ <CYAN>master_worktree<RESET> + ref-to-branch<RESET> -> branch-one + ref-to-remote<RESET> -> origin/branch-one +EOF +test_expect_success TTY 'worktree colors correct' ' + test_terminal git branch >actual.raw && + test_decode_color <actual.raw >actual && + test_cmp expect actual +' + test_expect_success "set up color tests" ' echo "<RED>master<RESET>" >expect.color && echo "master" >expect.bare && -- 2.14.2 ^ permalink raw reply related [flat|nested] 136+ messages in thread
* [PATCH v3 3/3] branch: Add an extra verbose output displaying worktree path for refs checked out in a linked worktree 2018-12-16 21:57 ` [PATCH v3 0/3] nbelakovski 2018-12-16 21:57 ` [PATCH v3 1/3] ref-filter: add worktreepath atom nbelakovski 2018-12-16 21:57 ` [PATCH v3 2/3] branch: Mark and color a branch differently if it is checked out in a linked worktree nbelakovski @ 2018-12-16 21:57 ` nbelakovski 2018-12-18 17:25 ` [PATCH v3 0/3] Jeff King 3 siblings, 0 replies; 136+ messages in thread From: nbelakovski @ 2018-12-16 21:57 UTC (permalink / raw) To: git; +Cc: peff, rafa.almas, gitster, avarab, Nickolai Belakovski From: Nickolai Belakovski <nbelakovski@gmail.com> --- builtin/branch.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/builtin/branch.c b/builtin/branch.c index 2a24153b78..56589a3684 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -366,6 +366,10 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r strbuf_addstr(&local, branch_get_color(BRANCH_COLOR_RESET)); strbuf_addf(&local, " %s ", obname.buf); + if (filter->verbose > 2) + strbuf_addf(&local, "%s%%(if:notequals=*)%%(HEAD)%%(then)%%(if)%%(worktreepath)%%(then)%%(worktreepath) %%(end)%%(end)%s", + branch_get_color(BRANCH_COLOR_WORKTREE), branch_get_color(BRANCH_COLOR_RESET)); + if (filter->verbose > 1) strbuf_addf(&local, "%%(if)%%(upstream)%%(then)[%s%%(upstream:short)%s%%(if)%%(upstream:track)" "%%(then): %%(upstream:track,nobracket)%%(end)] %%(end)%%(contents:subject)", -- 2.14.2 ^ permalink raw reply related [flat|nested] 136+ messages in thread
* Re: [PATCH v3 0/3] 2018-12-16 21:57 ` [PATCH v3 0/3] nbelakovski ` (2 preceding siblings ...) 2018-12-16 21:57 ` [PATCH v3 3/3] branch: Add an extra verbose output displaying worktree path for refs " nbelakovski @ 2018-12-18 17:25 ` Jeff King 3 siblings, 0 replies; 136+ messages in thread From: Jeff King @ 2018-12-18 17:25 UTC (permalink / raw) To: nbelakovski; +Cc: git, rafa.almas, gitster, avarab On Sun, Dec 16, 2018 at 01:57:56PM -0800, nbelakovski@gmail.com wrote: > Finally got around to submitting latest changes. > > I think this addresses all the feedback > > The atom now returns the worktree path instead of '+' Thanks, I think patch 1 is definitely going in the right direction. There are a few issues I found in its implementation, but they should be easy to fix (and won't affect the other patches). I don't really have a strong opinion on the behavior of the other patches. -Peff ^ permalink raw reply [flat|nested] 136+ messages in thread
* [PATCH v5 0/3] 2018-09-27 15:13 ` [PATCH] branch: colorize branches checked out in a linked working tree the same way as the current branch is colorized Nickolai Belakovski ` (2 preceding siblings ...) 2018-09-27 18:17 ` Jeff King @ 2019-01-06 0:26 ` nbelakovski 2019-01-06 0:26 ` [PATCH v5 1/3] ref-filter: add worktreepath atom nbelakovski ` (2 more replies) 2019-01-22 23:22 ` [PATCH v6 0/3] nbelakovski ` (7 subsequent siblings) 11 siblings, 3 replies; 136+ messages in thread From: nbelakovski @ 2019-01-06 0:26 UTC (permalink / raw) To: git; +Cc: peff, rafa.almas, gitster, avarab, Nickolai Belakovski From: Nickolai Belakovski <nbelakovski@gmail.com> Replying to my original email to try to clean up the email chain > Thanks for keeping with this. I think we're getting quite close Thanks to you as well for continuing to review the change set and provide feedback! It does feel rather close, I'm getting exciting about following it through, even if we just end up merging the worktreepath commit and not the ones to modify the branch output, since I can always just make a local alias that uses the worktreepath atom. The last set of changes all made sense, very non-controversial, so I've simply implemented them. Beyond that, I moved where the structures for the ref<->worktree map are defined now that they're no longer associated with used_atom. They still feel a little awkwardly placed to me; I couldn't quite find a way I liked of arranging them together while also sticking to the style in the rest of the code but I think it's a little better that all of the relevant structs and the cmpfnc are all in the same place. Travis-CI results: https://travis-ci.org/nbelakovski/git/builds/475825245 Nickolai Belakovski (3): ref-filter: add worktreepath atom branch: Mark and color a branch differently if it is checked out in a linked worktree branch: Add an extra verbose output displaying worktree path for refs checked out in a linked worktree Documentation/git-branch.txt | 20 ++++++----- Documentation/git-for-each-ref.txt | 5 +++ builtin/branch.c | 16 ++++++--- ref-filter.c | 71 ++++++++++++++++++++++++++++++++++++++ t/t3200-branch.sh | 8 ++--- t/t3203-branch-output.sh | 21 +++++++++++ t/t6302-for-each-ref-filter.sh | 15 ++++++++ 7 files changed, 140 insertions(+), 16 deletions(-) -- 2.14.2 ^ permalink raw reply [flat|nested] 136+ messages in thread
* [PATCH v5 1/3] ref-filter: add worktreepath atom 2019-01-06 0:26 ` [PATCH v5 0/3] nbelakovski @ 2019-01-06 0:26 ` nbelakovski 2019-01-07 18:20 ` Junio C Hamano 2019-01-06 0:26 ` [PATCH v5 2/3] branch: Mark and color a branch differently if it is checked out in a linked worktree nbelakovski 2019-01-06 0:26 ` [PATCH v5 3/3] branch: Add an extra verbose output displaying worktree path for refs " nbelakovski 2 siblings, 1 reply; 136+ messages in thread From: nbelakovski @ 2019-01-06 0:26 UTC (permalink / raw) To: git; +Cc: peff, rafa.almas, gitster, avarab, Nickolai Belakovski From: Nickolai Belakovski <nbelakovski@gmail.com> Add an atom providing the path of the linked worktree where this ref is checked out, if it is checked out in any linked worktrees, and empty string otherwise. --- Documentation/git-for-each-ref.txt | 5 +++ ref-filter.c | 71 ++++++++++++++++++++++++++++++++++++++ t/t6302-for-each-ref-filter.sh | 15 ++++++++ 3 files changed, 91 insertions(+) diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt index 901faef1bf..caba1c23b8 100644 --- a/Documentation/git-for-each-ref.txt +++ b/Documentation/git-for-each-ref.txt @@ -209,6 +209,11 @@ symref:: `:lstrip` and `:rstrip` options in the same way as `refname` above. +worktreepath:: + The absolute path to the worktree in which the ref is checked + out, if it is checked out in any linked worktree. Empty string + otherwise. + In addition to the above, for commit and tag objects, the header field names (`tree`, `parent`, `object`, `type`, and `tag`) can be used to specify the value in the header field. diff --git a/ref-filter.c b/ref-filter.c index 5de616befe..e7ca45f39b 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -20,6 +20,8 @@ #include "commit-slab.h" #include "commit-graph.h" #include "commit-reach.h" +#include "worktree.h" +#include "hashmap.h" static struct ref_msg { const char *gone; @@ -75,6 +77,22 @@ static struct expand_data { struct object_info info; } oi, oi_deref; +struct ref_to_worktree_entry { + struct hashmap_entry ent; /* must be the first member! */ + struct worktree *wt; /* key is wt->head_ref */ +}; + +static int ref_to_worktree_map_cmpfnc(const void *unused_lookupdata, const void *existing_hashmap_entry_to_test, + const void *key, const void *keydata_aka_refname) +{ + const struct ref_to_worktree_entry *e = existing_hashmap_entry_to_test; + const struct ref_to_worktree_entry *k = key; + return strcmp(e->wt->head_ref, keydata_aka_refname ? keydata_aka_refname : k->wt->head_ref); +} + +static struct hashmap ref_to_worktree_map; +static struct worktree **worktrees = NULL; + /* * An atom is a valid field atom listed below, possibly prefixed with * a "*" to denote deref_tag(). @@ -420,6 +438,34 @@ static int head_atom_parser(const struct ref_format *format, struct used_atom *a return 0; } +static int worktree_atom_parser(const struct ref_format *format, + struct used_atom *atom, + const char *arg, + struct strbuf *unused_err) +{ + int i; + + if (worktrees) + return 0; + + worktrees = get_worktrees(0); + + hashmap_init(&ref_to_worktree_map, ref_to_worktree_map_cmpfnc, NULL, 0); + + for (i = 0; worktrees[i]; i++) { + if (worktrees[i]->head_ref) { + struct ref_to_worktree_entry *entry; + entry = xmalloc(sizeof(*entry)); + entry->wt = worktrees[i]; + hashmap_entry_init(entry, strhash(worktrees[i]->head_ref)); + + hashmap_add(&ref_to_worktree_map, entry); + } + } + + return 0; +} + static struct { const char *name; info_source source; @@ -461,6 +507,7 @@ static struct { { "flag", SOURCE_NONE }, { "HEAD", SOURCE_NONE, FIELD_STR, head_atom_parser }, { "color", SOURCE_NONE, FIELD_STR, color_atom_parser }, + { "worktreepath", SOURCE_NONE, FIELD_STR, worktree_atom_parser }, { "align", SOURCE_NONE, FIELD_STR, align_atom_parser }, { "end", SOURCE_NONE }, { "if", SOURCE_NONE, FIELD_STR, if_atom_parser }, @@ -1500,6 +1547,21 @@ static int get_object(struct ref_array_item *ref, int deref, struct object **obj return 0; } +static char *get_worktree_path(const struct used_atom *atom, const struct ref_array_item *ref) +{ + struct strbuf val = STRBUF_INIT; + struct hashmap_entry entry; + struct ref_to_worktree_entry *lookup_result; + + hashmap_entry_init(&entry, strhash(ref->refname)); + lookup_result = hashmap_get(&ref_to_worktree_map, &entry, ref->refname); + + if (lookup_result) + strbuf_addstr(&val, lookup_result->wt->path); + + return strbuf_detach(&val, NULL); +} + /* * Parse the object referred by ref, and grab needed value. */ @@ -1537,6 +1599,10 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err) if (starts_with(name, "refname")) refname = get_refname(atom, ref); + else if (starts_with(name, "worktreepath")) { + v->s = get_worktree_path(atom, ref); + continue; + } else if (starts_with(name, "symref")) refname = get_symref(atom, ref); else if (starts_with(name, "upstream")) { @@ -2020,6 +2086,11 @@ void ref_array_clear(struct ref_array *array) free_array_item(array->items[i]); FREE_AND_NULL(array->items); array->nr = array->alloc = 0; + if (worktrees) + { + hashmap_free(&ref_to_worktree_map, 1); + free_worktrees(worktrees); + } } static void do_merge_filter(struct ref_filter_cbdata *ref_cbdata) diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh index fc067ed672..87e0222ea1 100755 --- a/t/t6302-for-each-ref-filter.sh +++ b/t/t6302-for-each-ref-filter.sh @@ -441,4 +441,19 @@ test_expect_success '--merged is incompatible with --no-merged' ' test_must_fail git for-each-ref --merged HEAD --no-merged HEAD ' +test_expect_success '"add" a worktree' ' + mkdir worktree_dir && + git worktree add -b master_worktree worktree_dir master +' + +test_expect_success 'validate worktree atom' ' + cat >expect <<-EOF && + master: $(pwd) + master_worktree: $(pwd)/worktree_dir + side: not checked out + EOF + git for-each-ref --format="%(refname:short): %(if)%(worktreepath)%(then)%(worktreepath)%(else)not checked out%(end)" refs/heads/ >actual && + test_cmp expect actual +' + test_done -- 2.14.2 ^ permalink raw reply related [flat|nested] 136+ messages in thread
* Re: [PATCH v5 1/3] ref-filter: add worktreepath atom 2019-01-06 0:26 ` [PATCH v5 1/3] ref-filter: add worktreepath atom nbelakovski @ 2019-01-07 18:20 ` Junio C Hamano 2019-01-18 22:17 ` Nickolai Belakovski 0 siblings, 1 reply; 136+ messages in thread From: Junio C Hamano @ 2019-01-07 18:20 UTC (permalink / raw) To: nbelakovski; +Cc: git, peff, rafa.almas, avarab nbelakovski@gmail.com writes: > +static struct hashmap ref_to_worktree_map; > +static struct worktree **worktrees = NULL; > + > /* > * An atom is a valid field atom listed below, possibly prefixed with > * a "*" to denote deref_tag(). > @@ -420,6 +438,34 @@ static int head_atom_parser(const struct ref_format *format, struct used_atom *a > return 0; > } > > +static int worktree_atom_parser(const struct ref_format *format, > + struct used_atom *atom, > + const char *arg, > + struct strbuf *unused_err) > +{ > + int i; > + > + if (worktrees) > + return 0; OK, so verify_ref_format() etc. will trigger this to be called via valid_atom[].parser when "%(worktreepath)" is seen in the user format, and then this grabs all the worktrees and record their paths in the hashmap. This if() statement makes sure that it happens only once. > + worktrees = get_worktrees(0); > + > + hashmap_init(&ref_to_worktree_map, ref_to_worktree_map_cmpfnc, NULL, 0); > + > + for (i = 0; worktrees[i]; i++) { > + if (worktrees[i]->head_ref) { > + struct ref_to_worktree_entry *entry; > + entry = xmalloc(sizeof(*entry)); > + entry->wt = worktrees[i]; > + hashmap_entry_init(entry, strhash(worktrees[i]->head_ref)); > + > + hashmap_add(&ref_to_worktree_map, entry); > + } > + } > + > + return 0; > +} > + > static struct { > const char *name; > info_source source; > @@ -461,6 +507,7 @@ static struct { > { "flag", SOURCE_NONE }, > { "HEAD", SOURCE_NONE, FIELD_STR, head_atom_parser }, > { "color", SOURCE_NONE, FIELD_STR, color_atom_parser }, > + { "worktreepath", SOURCE_NONE, FIELD_STR, worktree_atom_parser }, > { "align", SOURCE_NONE, FIELD_STR, align_atom_parser }, > { "end", SOURCE_NONE }, > { "if", SOURCE_NONE, FIELD_STR, if_atom_parser }, > @@ -1500,6 +1547,21 @@ static int get_object(struct ref_array_item *ref, int deref, struct object **obj > return 0; > } > > +static char *get_worktree_path(const struct used_atom *atom, const struct ref_array_item *ref) > +{ > + struct strbuf val = STRBUF_INIT; > + struct hashmap_entry entry; > + struct ref_to_worktree_entry *lookup_result; And then this will be called from populate_value() for each of the ref. It looks up the worktree that has checked out this branch, if any, from the hashmap, and yields the path to it. When seeing a tag or note ref, by definition that's not something we can have checked out in any worktree. I wonder if it is worth to optimize further by omitting this lookup when ref is not a local branch? IOW, with a typical number of worktrees and refs, how costly would ... > + hashmap_entry_init(&entry, strhash(ref->refname)); > + lookup_result = hashmap_get(&ref_to_worktree_map, &entry, ref->refname); ... this sequence of calls be. > + > + if (lookup_result) > + strbuf_addstr(&val, lookup_result->wt->path); > + > + return strbuf_detach(&val, NULL); > +} > + > /* > * Parse the object referred by ref, and grab needed value. > */ > @@ -1537,6 +1599,10 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err) > > if (starts_with(name, "refname")) > refname = get_refname(atom, ref); > + else if (starts_with(name, "worktreepath")) { > + v->s = get_worktree_path(atom, ref); > + continue; > + } > else if (starts_with(name, "symref")) > refname = get_symref(atom, ref); > else if (starts_with(name, "upstream")) { > @@ -2020,6 +2086,11 @@ void ref_array_clear(struct ref_array *array) > free_array_item(array->items[i]); > FREE_AND_NULL(array->items); > array->nr = array->alloc = 0; > + if (worktrees) > + { Have this opening brace at the end of the previous line, i.e. if (worktrees) { > + hashmap_free(&ref_to_worktree_map, 1); > + free_worktrees(worktrees); > + } > } What's the point of ref_array_clear()? What does the caller of this function really want? Is it merely to release the resources consumed? If so, then this is good enough, but then the existing calls to FREE_AND_NULL() for releasing resources in the function is overkill. Or is it envisioned that we are preparing a clean slate so that another call, possibly after the external environment changed, can be made into this machinery (i.e. imagine we lift ref-filter.c code and link it to a long running service process; after serving one for-each-ref request, a new worktree or a new branch may get created, and then we may get another for-each-ref request)? If that is the case, then the added code breaks that hope, as it leaves a dangling pointer in the worktrees variable. > static void do_merge_filter(struct ref_filter_cbdata *ref_cbdata) > diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh > index fc067ed672..87e0222ea1 100755 > --- a/t/t6302-for-each-ref-filter.sh > +++ b/t/t6302-for-each-ref-filter.sh > @@ -441,4 +441,19 @@ test_expect_success '--merged is incompatible with --no-merged' ' > test_must_fail git for-each-ref --merged HEAD --no-merged HEAD > ' > > +test_expect_success '"add" a worktree' ' > + mkdir worktree_dir && > + git worktree add -b master_worktree worktree_dir master > +' > + > +test_expect_success 'validate worktree atom' ' > + cat >expect <<-EOF && > + master: $(pwd) > + master_worktree: $(pwd)/worktree_dir > + side: not checked out > + EOF > + git for-each-ref --format="%(refname:short): %(if)%(worktreepath)%(then)%(worktreepath)%(else)not checked out%(end)" refs/heads/ >actual && > + test_cmp expect actual > +' > + > test_done ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v5 1/3] ref-filter: add worktreepath atom 2019-01-07 18:20 ` Junio C Hamano @ 2019-01-18 22:17 ` Nickolai Belakovski 2019-01-18 22:20 ` Nickolai Belakovski 0 siblings, 1 reply; 136+ messages in thread From: Nickolai Belakovski @ 2019-01-18 22:17 UTC (permalink / raw) To: Junio C Hamano Cc: git, Jeff King, Rafael Ascensão, Ævar Arnfjörð Bjarmason On Mon, Jan 7, 2019 at 10:20 AM Junio C Hamano <gitster@pobox.com> wrote: > When seeing a tag or note ref, by definition that's not something we > can have checked out in any worktree. I wonder if it is worth to > optimize further by omitting this lookup when ref is not a local > branch? > > IOW, with a typical number of worktrees and refs, how costly would ... > > > + hashmap_entry_init(&entry, strhash(ref->refname)); > > + lookup_result = hashmap_get(&ref_to_worktree_map, &entry, ref->refname); > > ... this sequence of calls be. > It certainly wouldn't be free. Every check would compute the hash of the refname and do a lookup into the hash table. If the bucket it looked up was empty that'd be it, but if it were non-empty a few more comparisons would happen. I think avoiding this would be check, we can simply check ref->kind == FILTER_REFS_BRANCHES ahead of calling into get_worktree_path and provide an empty string otherwise. > > free_array_item(array->items[i]); > > FREE_AND_NULL(array->items); > > array->nr = array->alloc = 0; > > + if (worktrees) > > + { > > > What's the point of ref_array_clear()? ... If that > is the case, then the added code breaks that hope, as it leaves a > dangling pointer in the worktrees variable. > Discussion of the point of ref_array_clear would be out of scope of this change, but your point is well taken that setting worktrees to NULL would be consistent with the rest of the function. Will implement. ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v5 1/3] ref-filter: add worktreepath atom 2019-01-18 22:17 ` Nickolai Belakovski @ 2019-01-18 22:20 ` Nickolai Belakovski 0 siblings, 0 replies; 136+ messages in thread From: Nickolai Belakovski @ 2019-01-18 22:20 UTC (permalink / raw) To: Junio C Hamano Cc: git, Jeff King, Rafael Ascensão, Ævar Arnfjörð Bjarmason On Fri, Jan 18, 2019 at 2:17 PM Nickolai Belakovski <nbelakovski@gmail.com> wrote: > > > I think avoiding this would be check, we can simply check ref->kind == > FILTER_REFS_BRANCHES ahead of calling into get_worktree_path and > provide an empty string otherwise. > *would be check -> would be cheap ^ permalink raw reply [flat|nested] 136+ messages in thread
* [PATCH v5 2/3] branch: Mark and color a branch differently if it is checked out in a linked worktree 2019-01-06 0:26 ` [PATCH v5 0/3] nbelakovski 2019-01-06 0:26 ` [PATCH v5 1/3] ref-filter: add worktreepath atom nbelakovski @ 2019-01-06 0:26 ` nbelakovski 2019-01-07 19:04 ` Junio C Hamano 2019-01-06 0:26 ` [PATCH v5 3/3] branch: Add an extra verbose output displaying worktree path for refs " nbelakovski 2 siblings, 1 reply; 136+ messages in thread From: nbelakovski @ 2019-01-06 0:26 UTC (permalink / raw) To: git; +Cc: peff, rafa.almas, gitster, avarab, Nickolai Belakovski From: Nickolai Belakovski <nbelakovski@gmail.com> In order to more clearly display which branches are active, the output of git branch is modified to mark branches checkout out in a linked worktree with a "+" and color them in cyan (in contrast to the current branch, which will still be denoted with a "*" and colored in green) This is meant to simplify workflows related to worktree, particularly due to the limitations of not being able to check out the same branch in two worktrees and the inability to delete a branch checked out in a worktree. When performing branch operations like checkout and delete, it would be useful to know more readily if the branches in which the user is interested are already checked out in a worktree. The git worktree list command contains the relevant information, however this is a much less frquently used command than git branch. --- Documentation/git-branch.txt | 15 ++++++++------- builtin/branch.c | 12 ++++++++---- t/t3200-branch.sh | 8 ++++---- t/t3203-branch-output.sh | 21 +++++++++++++++++++++ 4 files changed, 41 insertions(+), 15 deletions(-) diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index bf5316ffa9..b3eca6ffdc 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -26,13 +26,14 @@ DESCRIPTION ----------- If `--list` is given, or if there are no non-option arguments, existing -branches are listed; the current branch will be highlighted with an -asterisk. Option `-r` causes the remote-tracking branches to be listed, -and option `-a` shows both local and remote branches. If a `<pattern>` -is given, it is used as a shell wildcard to restrict the output to -matching branches. If multiple patterns are given, a branch is shown if -it matches any of the patterns. Note that when providing a -`<pattern>`, you must use `--list`; otherwise the command is interpreted +branches are listed; the current branch will be highlighted in green and +marked with an asterisk. Any branches checked out in linked worktrees will +be highlighted in cyan and marked with a plus sign. Option `-r` causes the +remote-tracking branches to be listed, and option `-a` shows both local and +remote branches. If a `<pattern>` is given, it is used as a shell wildcard to +restrict the output to matching branches. If multiple patterns are given, a +branch is shown if it matches any of the patterns. Note that when providing +a `<pattern>`, you must use `--list`; otherwise the command is interpreted as branch creation. With `--contains`, shows only the branches that contain the named commit diff --git a/builtin/branch.c b/builtin/branch.c index 0c55f7f065..2a24153b78 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -47,6 +47,7 @@ static char branch_colors[][COLOR_MAXLEN] = { GIT_COLOR_NORMAL, /* LOCAL */ GIT_COLOR_GREEN, /* CURRENT */ GIT_COLOR_BLUE, /* UPSTREAM */ + GIT_COLOR_CYAN, /* WORKTREE */ }; enum color_branch { BRANCH_COLOR_RESET = 0, @@ -54,7 +55,8 @@ enum color_branch { BRANCH_COLOR_REMOTE = 2, BRANCH_COLOR_LOCAL = 3, BRANCH_COLOR_CURRENT = 4, - BRANCH_COLOR_UPSTREAM = 5 + BRANCH_COLOR_UPSTREAM = 5, + BRANCH_COLOR_WORKTREE = 6 }; static const char *color_branch_slots[] = { @@ -64,6 +66,7 @@ static const char *color_branch_slots[] = { [BRANCH_COLOR_LOCAL] = "local", [BRANCH_COLOR_CURRENT] = "current", [BRANCH_COLOR_UPSTREAM] = "upstream", + [BRANCH_COLOR_WORKTREE] = "worktree", }; static struct string_list output = STRING_LIST_INIT_DUP; @@ -342,9 +345,10 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r struct strbuf local = STRBUF_INIT; struct strbuf remote = STRBUF_INIT; - strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else) %s%%(end)", - branch_get_color(BRANCH_COLOR_CURRENT), - branch_get_color(BRANCH_COLOR_LOCAL)); + strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else)%%(if)%%(worktreepath)%%(then)+ %s%%(else) %s%%(end)%%(end)", + branch_get_color(BRANCH_COLOR_CURRENT), + branch_get_color(BRANCH_COLOR_WORKTREE), + branch_get_color(BRANCH_COLOR_LOCAL)); strbuf_addf(&remote, " %s", branch_get_color(BRANCH_COLOR_REMOTE)); diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index 478b82cf9b..e404f6e23c 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -292,7 +292,7 @@ test_expect_success 'git branch --list -v with --abbrev' ' test_expect_success 'git branch --column' ' COLUMNS=81 git branch --column=column >actual && cat >expected <<\EOF && - a/b/c bam foo l * master n o/p r + a/b/c + bam foo l * master n o/p r abc bar j/k m/m master2 o/o q EOF test_cmp expected actual @@ -307,7 +307,7 @@ test_expect_success 'git branch --column with an extremely long branch name' ' cat >expected <<EOF && a/b/c abc - bam ++ bam bar foo j/k @@ -332,7 +332,7 @@ test_expect_success 'git branch with column.*' ' git config --unset column.branch && git config --unset column.ui && cat >expected <<\EOF && - a/b/c bam foo l * master n o/p r + a/b/c + bam foo l * master n o/p r abc bar j/k m/m master2 o/o q EOF test_cmp expected actual @@ -349,7 +349,7 @@ test_expect_success 'git branch -v with column.ui ignored' ' cat >expected <<\EOF && a/b/c abc - bam ++ bam bar foo j/k diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh index ee6787614c..94ab05ad59 100755 --- a/t/t3203-branch-output.sh +++ b/t/t3203-branch-output.sh @@ -240,6 +240,27 @@ test_expect_success 'git branch --format option' ' test_i18ncmp expect actual ' +test_expect_success '"add" a worktree' ' + mkdir worktree_dir && + git worktree add -b master_worktree worktree_dir master +' + +cat >expect <<'EOF' +* <GREEN>(HEAD detached from fromtag)<RESET> + ambiguous<RESET> + branch-one<RESET> + branch-two<RESET> + master<RESET> ++ <CYAN>master_worktree<RESET> + ref-to-branch<RESET> -> branch-one + ref-to-remote<RESET> -> origin/branch-one +EOF +test_expect_success TTY 'worktree colors correct' ' + test_terminal git branch >actual.raw && + test_decode_color <actual.raw >actual && + test_cmp expect actual +' + test_expect_success "set up color tests" ' echo "<RED>master<RESET>" >expect.color && echo "master" >expect.bare && -- 2.14.2 ^ permalink raw reply related [flat|nested] 136+ messages in thread
* Re: [PATCH v5 2/3] branch: Mark and color a branch differently if it is checked out in a linked worktree 2019-01-06 0:26 ` [PATCH v5 2/3] branch: Mark and color a branch differently if it is checked out in a linked worktree nbelakovski @ 2019-01-07 19:04 ` Junio C Hamano 2019-01-10 21:42 ` Philip Oakley 0 siblings, 1 reply; 136+ messages in thread From: Junio C Hamano @ 2019-01-07 19:04 UTC (permalink / raw) To: nbelakovski; +Cc: git, peff, rafa.almas, avarab nbelakovski@gmail.com writes: > From: Nickolai Belakovski <nbelakovski@gmail.com> > > In order to more clearly display which branches are active, the output > of git branch is modified to mark branches checkout out in a linked > worktree with a "+" and color them in cyan (in contrast to the current > branch, which will still be denoted with a "*" and colored in green) > > This is meant to simplify workflows related to worktree, particularly > due to the limitations of not being able to check out the same branch in > two worktrees and the inability to delete a branch checked out in a > worktree. When performing branch operations like checkout and delete, it > would be useful to know more readily if the branches in which the user > is interested are already checked out in a worktree. I do not think it is warranted to paint the safety features as "limitations". A branch that is checked out in another worktree cannot be checked out to be worked on, as that will make the checkout of the other worktree out of sync. If you want to work on that branch, you can either (1) go to that worktree that has a checkout of that branch and work there, or (2) go to that worktree that has a checkout of that branch, check out a different branch there, come back to the worktree you want to work in and check out that branch. Knowing where that other worktree is is the first step in either case. And a branch that is checked out in a worktree cannot be removed, as it is a sign that it is still being worked on for a branch to have been checked out somewhere. If you do want to remove that branch, you need to go to that worktree that has a checkout of that branch, check out a different branch there, and then remove it. Again, knowing where that other worktree is is the fist thing you need to know. But then I am not sure if the feature being added by these patches is a good match for that justification. For one thing, it would be more direct and helpful way for git checkout one-branch git branch -d one-branch to say "The branch `one-branch` is checked out in a worktree at $DIRECTORY" when they refused to go ahead. And that would eliminate the need for this new feature to help these two use cases. In fact, these two command already behave that way, so the paragraph I just commented on is not a good justification for this new feature at all. Besides, showing "That branch is checked out somewhere" would not help user to decide "ah, if I want to work on that branch, I need to chdir to that directory" with the patch in question, as it only shows "It is checked out _somewhere_" without saying where. > The git worktree list command contains the relevant information, however > this is a much less frquently used command than git branch. It is not a good justification. If the "relevant information" given by the command is necessary one, the user can run that command. If the situation where that "relevant information" becomes necessary is rare, the command is run much less frequently is not a problem---it is expected. And overloading a more frequently used command with information that is less frequently wanted is actually not a great design. A more relevant justification may be that even though the information can already be found in "worktree list" output, it would give us flexibility in presentation to allow the custom format in for-each-ref to show it. So, I am between moderately Meh to fairly negative on this step; Meh in the sense that "thanks to the previous step, we _could_ do this, it does not give incorrect information, and it makes the output more cheerful, but it does not add that much useful and actionable piece of information". ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v5 2/3] branch: Mark and color a branch differently if it is checked out in a linked worktree 2019-01-07 19:04 ` Junio C Hamano @ 2019-01-10 21:42 ` Philip Oakley 2019-01-13 1:41 ` Nickolai Belakovski 0 siblings, 1 reply; 136+ messages in thread From: Philip Oakley @ 2019-01-10 21:42 UTC (permalink / raw) To: Junio C Hamano, nbelakovski; +Cc: git, peff, rafa.almas, avarab On 07/01/2019 19:04, Junio C Hamano wrote: > nbelakovski@gmail.com writes: > >> From: Nickolai Belakovski <nbelakovski@gmail.com> >> >> In order to more clearly display which branches are active, the output >> of git branch is modified to mark branches checkout out in a linked >> worktree with a "+" and color them in cyan (in contrast to the current >> branch, which will still be denoted with a "*" and colored in green) >> >> This is meant to simplify workflows related to worktree, particularly >> due to the limitations of not being able to check out the same branch in >> two worktrees and the inability to delete a branch checked out in a >> worktree. When performing branch operations like checkout and delete, it >> would be useful to know more readily if the branches in which the user >> is interested are already checked out in a worktree. > I do not think it is warranted to paint the safety features as > "limitations". Is this not just a case of needing to clarify that this is 'safety' related to the _users_ mental model (or lack of) relative to the limited information that was previously given by the branch command's list. You are right that there is no data safety issue, but users make mistakes when they misunderstand the situation. > > A branch that is checked out in another worktree cannot be checked > out to be worked on, as that will make the checkout of the other > worktree out of sync. If you want to work on that branch, you can > either (1) go to that worktree that has a checkout of that branch > and work there, or (2) go to that worktree that has a checkout of > that branch, check out a different branch there, come back to the > worktree you want to work in and check out that branch. Knowing > where that other worktree is is the first step in either case. > > And a branch that is checked out in a worktree cannot be removed, as > it is a sign that it is still being worked on for a branch to have > been checked out somewhere. I'm not sure that all users will recognise the signs, which I think is one reason for the value of the patch. > If you do want to remove that branch, > you need to go to that worktree that has a checkout of that branch, > check out a different branch there, and then remove it. Again, > knowing where that other worktree is is the fist thing you need to > know. > > But then I am not sure if the feature being added by these patches > is a good match for that justification. I'd agree that the justification needs clarified. > > For one thing, it would be more direct and helpful way for > > git checkout one-branch > git branch -d one-branch > > to say "The branch `one-branch` is checked out in a worktree at > $DIRECTORY" when they refused to go ahead. And that would eliminate > the need for this new feature to help these two use cases. > > In fact, these two command already behave that way, so the paragraph > I just commented on is not a good justification for this new feature > at all. > > Besides, showing "That branch is checked out somewhere" would not > help user to decide "ah, if I want to work on that branch, I need to > chdir to that directory" with the patch in question, as it only > shows "It is checked out _somewhere_" without saying where. > >> The git worktree list command contains the relevant information, however >> this is a much less frquently used command than git branch. > It is not a good justification. If the "relevant information" given > by the command is necessary one, the user can run that command. If > the situation where that "relevant information" becomes necessary is > rare, the command is run much less frequently is not a problem---it > is expected. And overloading a more frequently used command with > information that is less frequently wanted is actually not a great > design. But leaving the older command unaware of the newer developments and the user unwise as to its missing info is equally a poor situation. > > A more relevant justification may be that even though the > information can already be found in "worktree list" output, it would > give us flexibility in presentation to allow the custom format in > for-each-ref to show it. > > So, I am between moderately Meh to fairly negative on this step; Meh > in the sense that "thanks to the previous step, we _could_ do this, > it does not give incorrect information, and it makes the output more > cheerful, but it does not add that much useful and actionable piece > of information". The patch did appear to me as being a proper update to the branch command to include the information about the branches in the other worktrees Philip ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v5 2/3] branch: Mark and color a branch differently if it is checked out in a linked worktree 2019-01-10 21:42 ` Philip Oakley @ 2019-01-13 1:41 ` Nickolai Belakovski 2019-01-14 18:18 ` Junio C Hamano 0 siblings, 1 reply; 136+ messages in thread From: Nickolai Belakovski @ 2019-01-13 1:41 UTC (permalink / raw) To: Philip Oakley Cc: Junio C Hamano, git, Jeff King, Rafael Ascensão, Ævar Arnfjörð Bjarmason On Thu, Jan 10, 2019 at 1:43 PM Philip Oakley <philipoakley@iee.org> wrote: > > On 07/01/2019 19:04, Junio C Hamano wrote: > > I do not think it is warranted to paint the safety features as > > "limitations". > > Is this not just a case of needing to clarify that this is 'safety' > related to the _users_ mental model (or lack of) relative to the limited > information that was previously given by the branch command's list. > > You are right that there is no data safety issue, but users make > mistakes when they misunderstand the situation. Not trying to paint anything one way or another. I found that these features got in the way of my workflows and didn't see any immediate reason why they had to exist. Thinking about it a bit more, is it unreasonable to delete a branch even if it's checked out in a worktree as long as the user uses git branch --delete --force or -D? This would leave the worktree in a detached head state, but all the data would be untouched. The output of deletion could mention that the branch had been checked out in a worktree so that the user is fully informed. Checking out the same branch in two worktrees should be technically possible to implement safely, but I don't see a use case for having the same branch checked out in multiple worktrees anyway. Why use multiple worktrees at that point? If a user really wants the same contents in two directories they can work around this limitation/safety feature by just making another branch pointing at the same commit. But anyway I'm just explaining why I chose the word 'limitation'. > > If you do want to remove that branch, > > you need to go to that worktree that has a checkout of that branch, > > check out a different branch there, and then remove it. Again, > > knowing where that other worktree is is the fist thing you need to > > know. This just seems silly to me when git branch --delete has a --force option. But that's off topic. > > > >> The git worktree list command contains the relevant information, however > >> this is a much less frquently used command than git branch. > > It is not a good justification. If the "relevant information" given > > by the command is necessary one, the user can run that command. If > > the situation where that "relevant information" becomes necessary is > > rare, the command is run much less frequently is not a problem---it > > is expected. And overloading a more frequently used command with > > information that is less frequently wanted is actually not a great > > design. > But leaving the older command unaware of the newer developments and the > user unwise as to its missing info is equally a poor situation. > > > > A more relevant justification may be that even though the > > information can already be found in "worktree list" output, it would > > give us flexibility in presentation to allow the custom format in > > for-each-ref to show it. > > > > So, I am between moderately Meh to fairly negative on this step; Meh > > in the sense that "thanks to the previous step, we _could_ do this, > > it does not give incorrect information, and it makes the output more > > cheerful, but it does not add that much useful and actionable piece > > of information". Allow me to add some color to my original commit message. The point of this patch is so that the user is not surprised when they see the message that this branch is checked out in another worktree when trying to delete it or check it out, since they have presumably run git branch recently and seen the formatted output indicating that a branch they may want to delete/checkout is checked out in a worktree. This was my frustration that prompted me to dive into this in the first place - I'm cleaning up my branches and all of a sudden git decides it doesn't want to let me delete one because it's checked out somewhere else, even though I know I don't care about it because I know the branch has already been merged upstream, or is old, or whatever. I thought, if git branch output could at least let me know that it's going to treat some branches differently, I can be proactive about things and go to my worktree and delete the branch, or skip trying to clean it up or not check it out. I'll pursue the above-mentioned topic of allowing git branch -D to allow the user to delete branches checked out in a worktree separately, but even if that goes through, I think this patch would still be useful in that it tells me that I can't check out the branches that are colored in cyan. Does that make more sense? ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v5 2/3] branch: Mark and color a branch differently if it is checked out in a linked worktree 2019-01-13 1:41 ` Nickolai Belakovski @ 2019-01-14 18:18 ` Junio C Hamano 2019-01-18 22:18 ` Nickolai Belakovski 0 siblings, 1 reply; 136+ messages in thread From: Junio C Hamano @ 2019-01-14 18:18 UTC (permalink / raw) To: Nickolai Belakovski Cc: Philip Oakley, git, Jeff King, Rafael Ascensão, Ævar Arnfjörð Bjarmason Nickolai Belakovski <nbelakovski@gmail.com> writes: > Not trying to paint anything one way or another. I found that these > features got in the way of my workflows and didn't see any immediate > reason why they had to exist. Thinking about it a bit more, is it > unreasonable to delete a branch even if it's checked out in a worktree > as long as the user uses git branch --delete --force or -D? [For ease of discussion, let's assume that our worktree has a checkout of branch B, and you are mucking with that branch in another worktree connected to the same repository] It is probably a sane enhancement to allow but require --force to delete branch B in the other worktree. It is a different matter to allow "git branch -m AnotherBranch B" run in the other worktree to overwrite branch B--it will be a disaster if we allowed it and then committed what we have in our worktree. > This would > leave the worktree in a detached head state, It does not. It will leave us on branch B that is unborn, and the next commit will start that branch with a root commit. > but all the data would be > untouched. As far as the person, who is working in our worktree, is concerned, she wanted to extend the history of branch B before you deleted it in another worktree. But because you deleted B while she was looking the other way, she ended up creating a root commit, losing all the history behind that branch. I'd grant you that "--force" will give us an excuse when she complains, though. Most likely, you and she are the same person, but one big point in the ability to work in multiple worktrees is to allow the user to switch context and multi-task. When she gives branch B its own worktree, she does so because she wants to have a stable place to work on it, without getting affected by other things she does to the repository in other worktrees. ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v5 2/3] branch: Mark and color a branch differently if it is checked out in a linked worktree 2019-01-14 18:18 ` Junio C Hamano @ 2019-01-18 22:18 ` Nickolai Belakovski 0 siblings, 0 replies; 136+ messages in thread From: Nickolai Belakovski @ 2019-01-18 22:18 UTC (permalink / raw) To: Junio C Hamano Cc: Philip Oakley, git, Jeff King, Rafael Ascensão, Ævar Arnfjörð Bjarmason I will start a separate thread containing these replies for the potential change to allow deleting branches checked out in worktrees. Getting back on track for this series, specifically this 2/3 patch, how do you feel about it? As I pointed out the goal is to communicate to the user that the branches marked/colored will behave differently from the other branches if the user tries to delete them or check them out. ^ permalink raw reply [flat|nested] 136+ messages in thread
* [PATCH v5 3/3] branch: Add an extra verbose output displaying worktree path for refs checked out in a linked worktree 2019-01-06 0:26 ` [PATCH v5 0/3] nbelakovski 2019-01-06 0:26 ` [PATCH v5 1/3] ref-filter: add worktreepath atom nbelakovski 2019-01-06 0:26 ` [PATCH v5 2/3] branch: Mark and color a branch differently if it is checked out in a linked worktree nbelakovski @ 2019-01-06 0:26 ` nbelakovski 2019-01-07 19:09 ` Junio C Hamano 2 siblings, 1 reply; 136+ messages in thread From: nbelakovski @ 2019-01-06 0:26 UTC (permalink / raw) To: git; +Cc: peff, rafa.almas, gitster, avarab, Nickolai Belakovski From: Nickolai Belakovski <nbelakovski@gmail.com> --- Documentation/git-branch.txt | 5 ++++- builtin/branch.c | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index b3eca6ffdc..6d1fc59e32 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -163,12 +163,15 @@ This option is only applicable in non-verbose mode. -v:: -vv:: +-vvv:: --verbose:: When in list mode, show sha1 and commit subject line for each head, along with relationship to upstream branch (if any). If given twice, print the name of the upstream branch, as well (see also `git remote - show <remote>`). + show <remote>`). If given 3 times, print the path of the linked + worktree, if applicable (not applicable for main worktree since + its path will be a subset of $PWD) -q:: --quiet:: diff --git a/builtin/branch.c b/builtin/branch.c index 2a24153b78..56589a3684 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -366,6 +366,10 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r strbuf_addstr(&local, branch_get_color(BRANCH_COLOR_RESET)); strbuf_addf(&local, " %s ", obname.buf); + if (filter->verbose > 2) + strbuf_addf(&local, "%s%%(if:notequals=*)%%(HEAD)%%(then)%%(if)%%(worktreepath)%%(then)%%(worktreepath) %%(end)%%(end)%s", + branch_get_color(BRANCH_COLOR_WORKTREE), branch_get_color(BRANCH_COLOR_RESET)); + if (filter->verbose > 1) strbuf_addf(&local, "%%(if)%%(upstream)%%(then)[%s%%(upstream:short)%s%%(if)%%(upstream:track)" "%%(then): %%(upstream:track,nobracket)%%(end)] %%(end)%%(contents:subject)", -- 2.14.2 ^ permalink raw reply related [flat|nested] 136+ messages in thread
* Re: [PATCH v5 3/3] branch: Add an extra verbose output displaying worktree path for refs checked out in a linked worktree 2019-01-06 0:26 ` [PATCH v5 3/3] branch: Add an extra verbose output displaying worktree path for refs " nbelakovski @ 2019-01-07 19:09 ` Junio C Hamano 0 siblings, 0 replies; 136+ messages in thread From: Junio C Hamano @ 2019-01-07 19:09 UTC (permalink / raw) To: nbelakovski; +Cc: git, peff, rafa.almas, avarab nbelakovski@gmail.com writes: > From: Nickolai Belakovski <nbelakovski@gmail.com> > > --- All three patches lack sign off. I am fairly negative on 2/3, but I think this one makes sense without introducing a new verbosity level. We do not promise stability of Porcelain command output and update the UI if we have useful information to give. Just making git branch --list -v -v show additional information should be sufficient. > diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt > index b3eca6ffdc..6d1fc59e32 100644 > --- a/Documentation/git-branch.txt > +++ b/Documentation/git-branch.txt > @@ -163,12 +163,15 @@ This option is only applicable in non-verbose mode. > > -v:: > -vv:: > +-vvv:: > --verbose:: > When in list mode, > show sha1 and commit subject line for each head, along with > relationship to upstream branch (if any). If given twice, print > the name of the upstream branch, as well (see also `git remote > - show <remote>`). > + show <remote>`). If given 3 times, print the path of the linked > + worktree, if applicable (not applicable for main worktree since > + its path will be a subset of $PWD) > > -q:: > --quiet:: > diff --git a/builtin/branch.c b/builtin/branch.c > index 2a24153b78..56589a3684 100644 > --- a/builtin/branch.c > +++ b/builtin/branch.c > @@ -366,6 +366,10 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r > strbuf_addstr(&local, branch_get_color(BRANCH_COLOR_RESET)); > strbuf_addf(&local, " %s ", obname.buf); > > + if (filter->verbose > 2) > + strbuf_addf(&local, "%s%%(if:notequals=*)%%(HEAD)%%(then)%%(if)%%(worktreepath)%%(then)%%(worktreepath) %%(end)%%(end)%s", > + branch_get_color(BRANCH_COLOR_WORKTREE), branch_get_color(BRANCH_COLOR_RESET)); > + > if (filter->verbose > 1) > strbuf_addf(&local, "%%(if)%%(upstream)%%(then)[%s%%(upstream:short)%s%%(if)%%(upstream:track)" > "%%(then): %%(upstream:track,nobracket)%%(end)] %%(end)%%(contents:subject)", ^ permalink raw reply [flat|nested] 136+ messages in thread
* [PATCH v6 0/3] 2018-09-27 15:13 ` [PATCH] branch: colorize branches checked out in a linked working tree the same way as the current branch is colorized Nickolai Belakovski ` (3 preceding siblings ...) 2019-01-06 0:26 ` [PATCH v5 0/3] nbelakovski @ 2019-01-22 23:22 ` nbelakovski 2019-01-22 23:22 ` [PATCH v6 1/3] ref-filter: add worktreepath atom nbelakovski ` (2 more replies) 2019-02-01 22:04 ` [PATCH v7 0/3] nbelakovski ` (6 subsequent siblings) 11 siblings, 3 replies; 136+ messages in thread From: nbelakovski @ 2019-01-22 23:22 UTC (permalink / raw) To: git; +Cc: peff, rafa.almas, gitster, avarab, Nickolai Belakovski From: Nickolai Belakovski <nbelakovski@gmail.com> From the latest round of comments: -Added a check in populate_value to only do the hashmap lookup for refs of type branch, since other types would not be checked out in a worktree -Reworded the commit message on 2/3 to make it clearer that the change in output is meant to inform users that the colored/marked branches will behave differently from the others upon attempts to delete or check out -Removed the extra verbose check on 3/3 and added the worktree path to the output if it exists -Set 'worktrees' variable to NULL after free-ing, to allow for ref-filter to be reentrant Travis-CI results: https://travis-ci.org/nbelakovski/git/builds/483134182 Nickolai Belakovski (3): ref-filter: add worktreepath atom branch: Mark and color a branch differently if it is checked out in a linked worktree branch: Add an extra verbose output displaying worktree path for refs checked out in a linked worktree Documentation/git-branch.txt | 22 +++++++----- Documentation/git-for-each-ref.txt | 5 +++ builtin/branch.c | 16 ++++++--- ref-filter.c | 74 ++++++++++++++++++++++++++++++++++++++ t/t3200-branch.sh | 8 ++--- t/t3203-branch-output.sh | 21 +++++++++++ t/t6302-for-each-ref-filter.sh | 15 ++++++++ 7 files changed, 144 insertions(+), 17 deletions(-) -- 2.14.2 ^ permalink raw reply [flat|nested] 136+ messages in thread
* [PATCH v6 1/3] ref-filter: add worktreepath atom 2019-01-22 23:22 ` [PATCH v6 0/3] nbelakovski @ 2019-01-22 23:22 ` nbelakovski 2019-01-23 18:57 ` Junio C Hamano 2019-01-22 23:23 ` [PATCH v6 2/3] branch: Mark and color a branch differently if it is checked out in a linked worktree nbelakovski 2019-01-22 23:23 ` [PATCH v6 3/3] branch: Add an extra verbose output displaying worktree path for refs " nbelakovski 2 siblings, 1 reply; 136+ messages in thread From: nbelakovski @ 2019-01-22 23:22 UTC (permalink / raw) To: git; +Cc: peff, rafa.almas, gitster, avarab, Nickolai Belakovski From: Nickolai Belakovski <nbelakovski@gmail.com> Add an atom providing the path of the linked worktree where this ref is checked out, if it is checked out in any linked worktrees, and empty string otherwise. --- Documentation/git-for-each-ref.txt | 5 +++ ref-filter.c | 74 ++++++++++++++++++++++++++++++++++++++ t/t6302-for-each-ref-filter.sh | 15 ++++++++ 3 files changed, 94 insertions(+) diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt index 774cecc7ed..6dcd39f6f6 100644 --- a/Documentation/git-for-each-ref.txt +++ b/Documentation/git-for-each-ref.txt @@ -214,6 +214,11 @@ symref:: `:lstrip` and `:rstrip` options in the same way as `refname` above. +worktreepath:: + The absolute path to the worktree in which the ref is checked + out, if it is checked out in any linked worktree. Empty string + otherwise. + In addition to the above, for commit and tag objects, the header field names (`tree`, `parent`, `object`, `type`, and `tag`) can be used to specify the value in the header field. diff --git a/ref-filter.c b/ref-filter.c index 422a9c9ae3..3c056cc148 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -20,6 +20,8 @@ #include "commit-slab.h" #include "commit-graph.h" #include "commit-reach.h" +#include "worktree.h" +#include "hashmap.h" static struct ref_msg { const char *gone; @@ -75,6 +77,22 @@ static struct expand_data { struct object_info info; } oi, oi_deref; +struct ref_to_worktree_entry { + struct hashmap_entry ent; /* must be the first member! */ + struct worktree *wt; /* key is wt->head_ref */ +}; + +static int ref_to_worktree_map_cmpfnc(const void *unused_lookupdata, const void *existing_hashmap_entry_to_test, + const void *key, const void *keydata_aka_refname) +{ + const struct ref_to_worktree_entry *e = existing_hashmap_entry_to_test; + const struct ref_to_worktree_entry *k = key; + return strcmp(e->wt->head_ref, keydata_aka_refname ? keydata_aka_refname : k->wt->head_ref); +} + +static struct hashmap ref_to_worktree_map; +static struct worktree **worktrees = NULL; + /* * An atom is a valid field atom listed below, possibly prefixed with * a "*" to denote deref_tag(). @@ -438,6 +456,34 @@ static int head_atom_parser(const struct ref_format *format, struct used_atom *a return 0; } +static int worktree_atom_parser(const struct ref_format *format, + struct used_atom *atom, + const char *arg, + struct strbuf *unused_err) +{ + int i; + + if (worktrees) + return 0; + + worktrees = get_worktrees(0); + + hashmap_init(&ref_to_worktree_map, ref_to_worktree_map_cmpfnc, NULL, 0); + + for (i = 0; worktrees[i]; i++) { + if (worktrees[i]->head_ref) { + struct ref_to_worktree_entry *entry; + entry = xmalloc(sizeof(*entry)); + entry->wt = worktrees[i]; + hashmap_entry_init(entry, strhash(worktrees[i]->head_ref)); + + hashmap_add(&ref_to_worktree_map, entry); + } + } + + return 0; +} + static struct { const char *name; info_source source; @@ -480,6 +526,7 @@ static struct { { "flag", SOURCE_NONE }, { "HEAD", SOURCE_NONE, FIELD_STR, head_atom_parser }, { "color", SOURCE_NONE, FIELD_STR, color_atom_parser }, + { "worktreepath", SOURCE_NONE, FIELD_STR, worktree_atom_parser }, { "align", SOURCE_NONE, FIELD_STR, align_atom_parser }, { "end", SOURCE_NONE }, { "if", SOURCE_NONE, FIELD_STR, if_atom_parser }, @@ -1525,6 +1572,21 @@ static int get_object(struct ref_array_item *ref, int deref, struct object **obj return 0; } +static char *get_worktree_path(const struct used_atom *atom, const struct ref_array_item *ref) +{ + struct strbuf val = STRBUF_INIT; + struct hashmap_entry entry; + struct ref_to_worktree_entry *lookup_result; + + hashmap_entry_init(&entry, strhash(ref->refname)); + lookup_result = hashmap_get(&ref_to_worktree_map, &entry, ref->refname); + + if (lookup_result) + strbuf_addstr(&val, lookup_result->wt->path); + + return strbuf_detach(&val, NULL); +} + /* * Parse the object referred by ref, and grab needed value. */ @@ -1562,6 +1624,13 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err) if (starts_with(name, "refname")) refname = get_refname(atom, ref); + else if (starts_with(name, "worktreepath")) { + if (ref->kind == FILTER_REFS_BRANCHES) + v->s = get_worktree_path(atom, ref); + else + v->s = xstrdup(""); + continue; + } else if (starts_with(name, "symref")) refname = get_symref(atom, ref); else if (starts_with(name, "upstream")) { @@ -2045,6 +2114,11 @@ void ref_array_clear(struct ref_array *array) free_array_item(array->items[i]); FREE_AND_NULL(array->items); array->nr = array->alloc = 0; + if (worktrees) { + hashmap_free(&ref_to_worktree_map, 1); + free_worktrees(worktrees); + worktrees = NULL; + } } static void do_merge_filter(struct ref_filter_cbdata *ref_cbdata) diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh index fc067ed672..87e0222ea1 100755 --- a/t/t6302-for-each-ref-filter.sh +++ b/t/t6302-for-each-ref-filter.sh @@ -441,4 +441,19 @@ test_expect_success '--merged is incompatible with --no-merged' ' test_must_fail git for-each-ref --merged HEAD --no-merged HEAD ' +test_expect_success '"add" a worktree' ' + mkdir worktree_dir && + git worktree add -b master_worktree worktree_dir master +' + +test_expect_success 'validate worktree atom' ' + cat >expect <<-EOF && + master: $(pwd) + master_worktree: $(pwd)/worktree_dir + side: not checked out + EOF + git for-each-ref --format="%(refname:short): %(if)%(worktreepath)%(then)%(worktreepath)%(else)not checked out%(end)" refs/heads/ >actual && + test_cmp expect actual +' + test_done -- 2.14.2 ^ permalink raw reply related [flat|nested] 136+ messages in thread
* Re: [PATCH v6 1/3] ref-filter: add worktreepath atom 2019-01-22 23:22 ` [PATCH v6 1/3] ref-filter: add worktreepath atom nbelakovski @ 2019-01-23 18:57 ` Junio C Hamano 2019-01-23 23:34 ` Nickolai Belakovski 0 siblings, 1 reply; 136+ messages in thread From: Junio C Hamano @ 2019-01-23 18:57 UTC (permalink / raw) To: nbelakovski; +Cc: git, peff, rafa.almas, avarab nbelakovski@gmail.com writes: > From: Nickolai Belakovski <nbelakovski@gmail.com> > > Add an atom providing the path of the linked worktree where this ref is > checked out, if it is checked out in any linked worktrees, and empty > string otherwise. > --- Missing sign-off? > +static int ref_to_worktree_map_cmpfnc(const void *unused_lookupdata, const void *existing_hashmap_entry_to_test, > + const void *key, const void *keydata_aka_refname) > +{ > + const struct ref_to_worktree_entry *e = existing_hashmap_entry_to_test; > + const struct ref_to_worktree_entry *k = key; > + return strcmp(e->wt->head_ref, keydata_aka_refname ? keydata_aka_refname : k->wt->head_ref); Overlong line. > +} > + > +static struct hashmap ref_to_worktree_map; > +static struct worktree **worktrees = NULL; No need to initialize static vars to 0/NULL. > @@ -438,6 +456,34 @@ static int head_atom_parser(const struct ref_format *format, struct used_atom *a > return 0; > } > > +static int worktree_atom_parser(const struct ref_format *format, > + struct used_atom *atom, > + const char *arg, > + struct strbuf *unused_err) > +{ > + int i; > + > + if (worktrees) > + return 0; > + > + worktrees = get_worktrees(0); > + > + hashmap_init(&ref_to_worktree_map, ref_to_worktree_map_cmpfnc, NULL, 0); > + > + for (i = 0; worktrees[i]; i++) { > + if (worktrees[i]->head_ref) { > + struct ref_to_worktree_entry *entry; > + entry = xmalloc(sizeof(*entry)); > + entry->wt = worktrees[i]; > + hashmap_entry_init(entry, strhash(worktrees[i]->head_ref)); > + > + hashmap_add(&ref_to_worktree_map, entry); > + } > + } > + > + return 0; > +} It is kind of interesting that a function for parsing an "atom" in "format" does not look at none of its arguments at all ;-) The fact that "%(worktreepath)" atom got noticed by verify_ref_format() alone is of interest for this function. The helper iterates over all worktrees, registers them in a hashmap ref_to_worktree_map, indexed by the head reference. OK. > +static char *get_worktree_path(const struct used_atom *atom, const struct ref_array_item *ref) > +{ > + struct strbuf val = STRBUF_INIT; > + struct hashmap_entry entry; > + struct ref_to_worktree_entry *lookup_result; > + > + hashmap_entry_init(&entry, strhash(ref->refname)); > + lookup_result = hashmap_get(&ref_to_worktree_map, &entry, ref->refname); > + > + if (lookup_result) > + strbuf_addstr(&val, lookup_result->wt->path); > + > + return strbuf_detach(&val, NULL); > +} We do not need a strbuf to do the above, do we? hashmap_entry_init(...); lookup_result = hashmap_get(...); if (lookup_result) return xstrdup(lookup_result->wt->path); else return xstrdup(""); or something like that, perhaps? > @@ -1562,6 +1624,13 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err) > > if (starts_with(name, "refname")) > refname = get_refname(atom, ref); > + else if (starts_with(name, "worktreepath")) { > + if (ref->kind == FILTER_REFS_BRANCHES) > + v->s = get_worktree_path(atom, ref); > + else > + v->s = xstrdup(""); > + continue; > + } I am wondering if get_worktree_path() being called should be the triggering event for lazy initialization worktree_atom_parser() is doing in this patch, instead of verify_ref_format() seeing the "%(worktreepath)" atom. Is there any codepath that wants to make sure the lazy initialization is done without/before populate_value() sees a use of the "%(worktreepath)" atom? If so, such a plan would not work, but otherwise, we can do the following: - rename worktree_atom_parser() to lazy_init_worktrees() or something, and remove all of its unused parameters. - remove parser callback for "worktreepath" from valid_atom[]. - call lazy_inti_worktrees() at the beginning of get_worktree_path(). Hmm? ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v6 1/3] ref-filter: add worktreepath atom 2019-01-23 18:57 ` Junio C Hamano @ 2019-01-23 23:34 ` Nickolai Belakovski 2019-01-24 18:26 ` Junio C Hamano 0 siblings, 1 reply; 136+ messages in thread From: Nickolai Belakovski @ 2019-01-23 23:34 UTC (permalink / raw) To: Junio C Hamano Cc: git, Jeff King, Rafael Ascensão, Ævar Arnfjörð Bjarmason On Wed, Jan 23, 2019 at 10:57 AM Junio C Hamano <gitster@pobox.com> wrote: > > Missing sign-off? > My mistake, forgot -s on format-patch. Will remember to add it next go-around. > > +static int ref_to_worktree_map_cmpfnc(const void *unused_lookupdata, const void *existing_hashmap_entry_to_test, > > + const void *key, const void *keydata_aka_refname) > > +{ > > + const struct ref_to_worktree_entry *e = existing_hashmap_entry_to_test; > > + const struct ref_to_worktree_entry *k = key; > > + return strcmp(e->wt->head_ref, keydata_aka_refname ? keydata_aka_refname : k->wt->head_ref); > > Overlong line. > > > +} > OK, should I shorten the function signature as well? Is it too long because it's 101 characters and you're trying to stick to 100? > > > + > > +static struct hashmap ref_to_worktree_map; > > +static struct worktree **worktrees = NULL; > > No need to initialize static vars to 0/NULL. OK > > We do not need a strbuf to do the above, do we? > > hashmap_entry_init(...); > lookup_result = hashmap_get(...); > if (lookup_result) > return xstrdup(lookup_result->wt->path); > else > return xstrdup(""); > > or something like that, perhaps? > Sure. > > @@ -1562,6 +1624,13 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err) > > > > if (starts_with(name, "refname")) > > refname = get_refname(atom, ref); > > + else if (starts_with(name, "worktreepath")) { > > + if (ref->kind == FILTER_REFS_BRANCHES) > > + v->s = get_worktree_path(atom, ref); > > + else > > + v->s = xstrdup(""); > > + continue; > > + } > > I am wondering if get_worktree_path() being called should be the > triggering event for lazy initialization worktree_atom_parser() is > doing in this patch, instead of verify_ref_format() seeing the > "%(worktreepath)" atom. Is there any codepath that wants to make > sure the lazy initialization is done without/before populate_value() > sees a use of the "%(worktreepath)" atom? If so, such a plan would > not work, but otherwise, we can do the following: > > - rename worktree_atom_parser() to lazy_init_worktrees() or > something, and remove all of its unused parameters. > > - remove parser callback for "worktreepath" from valid_atom[]. > > - call lazy_inti_worktrees() at the beginning of > get_worktree_path(). > > Hmm? Yes, the parser used the atom argument in an earlier version of this patch, but we since moved the map out of the atom since it only needs to exist once globally. Even though we have a caching mechanism for atoms it still seemed like a logical move to explicitly keep one instance of the map globally. Will make the change, thanks for taking a look at it through fresh eyes. ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v6 1/3] ref-filter: add worktreepath atom 2019-01-23 23:34 ` Nickolai Belakovski @ 2019-01-24 18:26 ` Junio C Hamano 2019-01-24 18:32 ` Jeff King 0 siblings, 1 reply; 136+ messages in thread From: Junio C Hamano @ 2019-01-24 18:26 UTC (permalink / raw) To: Nickolai Belakovski Cc: git, Jeff King, Rafael Ascensão, Ævar Arnfjörð Bjarmason Nickolai Belakovski <nbelakovski@gmail.com> writes: > Yes, the parser used the atom argument in an earlier version of this > patch, but we since moved the map out of the atom since it only needs > to exist once globally. Even though we have a caching mechanism for > atoms it still seemed like a logical move to explicitly keep one > instance of the map globally. I think that is a mistaken move from an earlier version to this one. The worktree-related stuff only becomes necessary and belongs to the %(worktreepath) atom, so unless there is a compelling reason not to, we should hang it there, instead of introducing a global. When you have --format='%(worktreepath) %(worktreepath)', you'd have only one shared instance of it in used_atom[] to ensure that we have a singleton instance. The object_info support in the file, which is relatively new, may be what you borrowed the wrong idea of preferring globals from; I think it should be taken as an anti-pattern. ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v6 1/3] ref-filter: add worktreepath atom 2019-01-24 18:26 ` Junio C Hamano @ 2019-01-24 18:32 ` Jeff King 2019-01-24 19:27 ` Junio C Hamano 2019-01-24 19:30 ` Junio C Hamano 0 siblings, 2 replies; 136+ messages in thread From: Jeff King @ 2019-01-24 18:32 UTC (permalink / raw) To: Junio C Hamano Cc: Nickolai Belakovski, git, Rafael Ascensão, Ævar Arnfjörð Bjarmason On Thu, Jan 24, 2019 at 10:26:18AM -0800, Junio C Hamano wrote: > Nickolai Belakovski <nbelakovski@gmail.com> writes: > > > Yes, the parser used the atom argument in an earlier version of this > > patch, but we since moved the map out of the atom since it only needs > > to exist once globally. Even though we have a caching mechanism for > > atoms it still seemed like a logical move to explicitly keep one > > instance of the map globally. > > I think that is a mistaken move from an earlier version to this > one. The worktree-related stuff only becomes necessary and belongs > to the %(worktreepath) atom, so unless there is a compelling reason > not to, we should hang it there, instead of introducing a global. > When you have --format='%(worktreepath) %(worktreepath)', you'd have > only one shared instance of it in used_atom[] to ensure that we have > a singleton instance. What if you have other atoms that need worktrees? E.g., does %(worktreepath:foo) use the same used_atom slot? What if we have another worktree-related atom? If anything, I'd suggest going in the opposite direction, and teaching the worktree code a function for looking up a working tree by ref. And then it can handle its own cache to implement that reverse-mapping efficiently. > The object_info support in the file, which is relatively new, may be > what you borrowed the wrong idea of preferring globals from; I think > it should be taken as an anti-pattern. And that one is a good example where we _do_ need the global, because we already have multiple atoms pulling from it. I think the right level is "global to the ref-filter context", but we do not have that concept yet (hence why used_atoms, etc, are all file-static globals). -Peff ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v6 1/3] ref-filter: add worktreepath atom 2019-01-24 18:32 ` Jeff King @ 2019-01-24 19:27 ` Junio C Hamano 2019-01-24 19:34 ` Jeff King 2019-01-24 19:30 ` Junio C Hamano 1 sibling, 1 reply; 136+ messages in thread From: Junio C Hamano @ 2019-01-24 19:27 UTC (permalink / raw) To: Jeff King Cc: Nickolai Belakovski, git, Rafael Ascensão, Ævar Arnfjörð Bjarmason Jeff King <peff@peff.net> writes: > If anything, I'd suggest going in the opposite direction, and teaching > the worktree code a function for looking up a working tree by ref. And > then it can handle its own cache to implement that reverse-mapping > efficiently. Yeah, that's a thought. Then "give me a worktree that checks out this ref" can be asked outside the context of for-each-ref and friends, which is a big plus. ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v6 1/3] ref-filter: add worktreepath atom 2019-01-24 19:27 ` Junio C Hamano @ 2019-01-24 19:34 ` Jeff King 0 siblings, 0 replies; 136+ messages in thread From: Jeff King @ 2019-01-24 19:34 UTC (permalink / raw) To: Junio C Hamano Cc: Nickolai Belakovski, git, Rafael Ascensão, Ævar Arnfjörð Bjarmason On Thu, Jan 24, 2019 at 11:27:36AM -0800, Junio C Hamano wrote: > Jeff King <peff@peff.net> writes: > > > If anything, I'd suggest going in the opposite direction, and teaching > > the worktree code a function for looking up a working tree by ref. And > > then it can handle its own cache to implement that reverse-mapping > > efficiently. > > Yeah, that's a thought. Then "give me a worktree that checks out > this ref" can be asked outside the context of for-each-ref and > friends, which is a big plus. One tricky thing is that we do not store a list of "struct worktree", but rather generate it on the fly when get_worktrees() is called. So having an API to ask for one ref's worktree at a time is slightly awkward. It's only by having (and caching) this full list in ref-filter.c that we can easily make the reverse-indexed hashmap. -Peff ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v6 1/3] ref-filter: add worktreepath atom 2019-01-24 18:32 ` Jeff King 2019-01-24 19:27 ` Junio C Hamano @ 2019-01-24 19:30 ` Junio C Hamano 2019-01-24 21:26 ` Jeff King 1 sibling, 1 reply; 136+ messages in thread From: Junio C Hamano @ 2019-01-24 19:30 UTC (permalink / raw) To: Jeff King Cc: Nickolai Belakovski, git, Rafael Ascensão, Ævar Arnfjörð Bjarmason Jeff King <peff@peff.net> writes: > What if you have other atoms that need worktrees? E.g., does > %(worktreepath:foo) use the same used_atom slot? What if we have another > worktree-related atom? > ... > And that one is a good example where we _do_ need the global, because we > already have multiple atoms pulling from it. I guess that we broke the original atom design by mistake when we added ":<modifiers>" support. There should have been one layer of indirection that binds the instances of the same atom with different modifiers together---I agree with you that we cannot avoid globals without fixing that mistake first. Thanks. ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v6 1/3] ref-filter: add worktreepath atom 2019-01-24 19:30 ` Junio C Hamano @ 2019-01-24 21:26 ` Jeff King 2019-01-31 20:53 ` Nickolai Belakovski 2019-01-31 21:42 ` Junio C Hamano 0 siblings, 2 replies; 136+ messages in thread From: Jeff King @ 2019-01-24 21:26 UTC (permalink / raw) To: Junio C Hamano Cc: Nickolai Belakovski, git, Rafael Ascensão, Ævar Arnfjörð Bjarmason On Thu, Jan 24, 2019 at 11:30:20AM -0800, Junio C Hamano wrote: > Jeff King <peff@peff.net> writes: > > > What if you have other atoms that need worktrees? E.g., does > > %(worktreepath:foo) use the same used_atom slot? What if we have another > > worktree-related atom? > > ... > > And that one is a good example where we _do_ need the global, because we > > already have multiple atoms pulling from it. > > I guess that we broke the original atom design by mistake when we > added ":<modifiers>" support. There should have been one layer of > indirection that binds the instances of the same atom with different > modifiers together---I agree with you that we cannot avoid globals > without fixing that mistake first. Yes, that's one way to fix it. I actually think the biggest mistake is having that used_atoms list in the first place, as we iterate over it several times asking "can we fill this in yet?". The way pretty.c does it is just to incrementally parse the commit, saving intermediate results. And in cat-file.c, we figure out what we need ahead of time in a single pass, and then just fill it in for each object (which sort of works out the same, but doesn't require that the parsing needed for item X is a strict superset of item Y). So I'd much rather see us parse the format into a real tree of nodes, and figure out (once) which properties of each object are required to fulfill that. Then for each object, we grab those properties, and then walk the tree to generate the output string. -Peff ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v6 1/3] ref-filter: add worktreepath atom 2019-01-24 21:26 ` Jeff King @ 2019-01-31 20:53 ` Nickolai Belakovski 2019-01-31 23:21 ` Jeff King 2019-01-31 21:42 ` Junio C Hamano 1 sibling, 1 reply; 136+ messages in thread From: Nickolai Belakovski @ 2019-01-31 20:53 UTC (permalink / raw) To: Jeff King Cc: Junio C Hamano, git, Rafael Ascensão, Ævar Arnfjörð Bjarmason So where does that leave us for this series? We could move hashmap back into used_atom, but if a user entered --format="%(worktreepath)%(worktreepath:)" we'd end up freeing worktrees twice. Not that that should stop us - that scenario is one where user input isn't sensible and personally I don't think it's necessary to protect against such things (unless the user was reasonably confused, but I don't see that as the case here). I agree with Jeff that a ref-filter "context" would help. And in more ways than one, it could help us decide ahead of time whether to check if a ref is a branch or a tag before doing a hashmap lookup or just skip the check (i.e. if there are no tags within the context, the check would only add cost). But I do believe that that would be outside the scope of this series. I think leaving it as globals is a tiny bit safer and also makes it easier to pack it into a context if/when we decide to do that work, but as always I'm open to other interpretations. On Thu, Jan 24, 2019 at 1:26 PM Jeff King <peff@peff.net> wrote: > > On Thu, Jan 24, 2019 at 11:30:20AM -0800, Junio C Hamano wrote: > > > Jeff King <peff@peff.net> writes: > > > > > What if you have other atoms that need worktrees? E.g., does > > > %(worktreepath:foo) use the same used_atom slot? What if we have another > > > worktree-related atom? > > > ... > > > And that one is a good example where we _do_ need the global, because we > > > already have multiple atoms pulling from it. > > > > I guess that we broke the original atom design by mistake when we > > added ":<modifiers>" support. There should have been one layer of > > indirection that binds the instances of the same atom with different > > modifiers together---I agree with you that we cannot avoid globals > > without fixing that mistake first. > > Yes, that's one way to fix it. > > I actually think the biggest mistake is having that used_atoms list in > the first place, as we iterate over it several times asking "can we fill > this in yet?". The way pretty.c does it is just to incrementally parse > the commit, saving intermediate results. And in cat-file.c, we figure > out what we need ahead of time in a single pass, and then just fill it > in for each object (which sort of works out the same, but doesn't > require that the parsing needed for item X is a strict superset of item > Y). > > So I'd much rather see us parse the format into a real tree of nodes, > and figure out (once) which properties of each object are required to > fulfill that. Then for each object, we grab those properties, and then > walk the tree to generate the output string. > > -Peff ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v6 1/3] ref-filter: add worktreepath atom 2019-01-31 20:53 ` Nickolai Belakovski @ 2019-01-31 23:21 ` Jeff King 0 siblings, 0 replies; 136+ messages in thread From: Jeff King @ 2019-01-31 23:21 UTC (permalink / raw) To: Nickolai Belakovski Cc: Junio C Hamano, git, Rafael Ascensão, Ævar Arnfjörð Bjarmason On Thu, Jan 31, 2019 at 12:53:24PM -0800, Nickolai Belakovski wrote: > So where does that leave us for this series? We could move hashmap > back into used_atom, but if a user entered > --format="%(worktreepath)%(worktreepath:)" we'd end up freeing > worktrees twice. Not that that should stop us - that scenario is one > where user input isn't sensible and personally I don't think it's > necessary to protect against such things (unless the user was > reasonably confused, but I don't see that as the case here). > > I agree with Jeff that a ref-filter "context" would help. And in more > ways than one, it could help us decide ahead of time whether to check > if a ref is a branch or a tag before doing a hashmap lookup or just > skip the check (i.e. if there are no tags within the context, the > check would only add cost). But I do believe that that would be > outside the scope of this series. > > I think leaving it as globals is a tiny bit safer and also makes it > easier to pack it into a context if/when we decide to do that work, > but as always I'm open to other interpretations. Yeah, I agree with this: global for now, and then easily moved into a context struct later (along with all the other existing globals). -Peff ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v6 1/3] ref-filter: add worktreepath atom 2019-01-24 21:26 ` Jeff King 2019-01-31 20:53 ` Nickolai Belakovski @ 2019-01-31 21:42 ` Junio C Hamano 2019-01-31 23:20 ` Jeff King 1 sibling, 1 reply; 136+ messages in thread From: Junio C Hamano @ 2019-01-31 21:42 UTC (permalink / raw) To: Jeff King Cc: Nickolai Belakovski, git, Rafael Ascensão, Ævar Arnfjörð Bjarmason Jeff King <peff@peff.net> writes: > On Thu, Jan 24, 2019 at 11:30:20AM -0800, Junio C Hamano wrote: > >> Jeff King <peff@peff.net> writes: >> >> > What if you have other atoms that need worktrees? E.g., does >> > %(worktreepath:foo) use the same used_atom slot? What if we have another >> > worktree-related atom? >> > ... >> > And that one is a good example where we _do_ need the global, because we >> > already have multiple atoms pulling from it. >> >> I guess that we broke the original atom design by mistake when we >> added ":<modifiers>" support. There should have been one layer of >> indirection that binds the instances of the same atom with different >> modifiers together---I agree with you that we cannot avoid globals >> without fixing that mistake first. > > Yes, that's one way to fix it. > > I actually think the biggest mistake is having that used_atoms list in > the first place, as we iterate over it several times asking "can we fill > this in yet?". The way pretty.c does it is just to incrementally parse > the commit, saving intermediate results. And in cat-file.c, we figure > out what we need ahead of time in a single pass, and then just fill it > in for each object (which sort of works out the same, but doesn't > require that the parsing needed for item X is a strict superset of item > Y). > > So I'd much rather see us parse the format into a real tree of nodes, > and figure out (once) which properties of each object are required to > fulfill that. Then for each object, we grab those properties, and then > walk the tree to generate the output string. That sounds like a sensible longer-term strategy. Let's however leave it outside the scope of this change. ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v6 1/3] ref-filter: add worktreepath atom 2019-01-31 21:42 ` Junio C Hamano @ 2019-01-31 23:20 ` Jeff King 0 siblings, 0 replies; 136+ messages in thread From: Jeff King @ 2019-01-31 23:20 UTC (permalink / raw) To: Junio C Hamano Cc: Nickolai Belakovski, git, Rafael Ascensão, Ævar Arnfjörð Bjarmason On Thu, Jan 31, 2019 at 01:42:14PM -0800, Junio C Hamano wrote: > > So I'd much rather see us parse the format into a real tree of nodes, > > and figure out (once) which properties of each object are required to > > fulfill that. Then for each object, we grab those properties, and then > > walk the tree to generate the output string. > > That sounds like a sensible longer-term strategy. Let's however > leave it outside the scope of this change. Yeah, sorry if I got us too far afield. It's definitely out of scope for this series. The takeaway I was trying to get at is that storing the worktree map as a static global is actually pretty reasonable given the current design. -Peff ^ permalink raw reply [flat|nested] 136+ messages in thread
* [PATCH v6 2/3] branch: Mark and color a branch differently if it is checked out in a linked worktree 2019-01-22 23:22 ` [PATCH v6 0/3] nbelakovski 2019-01-22 23:22 ` [PATCH v6 1/3] ref-filter: add worktreepath atom nbelakovski @ 2019-01-22 23:23 ` nbelakovski 2019-01-22 23:23 ` [PATCH v6 3/3] branch: Add an extra verbose output displaying worktree path for refs " nbelakovski 2 siblings, 0 replies; 136+ messages in thread From: nbelakovski @ 2019-01-22 23:23 UTC (permalink / raw) To: git; +Cc: peff, rafa.almas, gitster, avarab, Nickolai Belakovski From: Nickolai Belakovski <nbelakovski@gmail.com> The output of git branch is modified to mark branches checkout out in a linked worktree with a "+" and color them in cyan (in contrast to the current branch, which will still be denoted with a "*" and colored in green) This is meant to communicate to the user that the branches that are marked or colored will behave differently from other branches if the user attempts to check them out or delete them, since branches checked out in another worktree cannot be checked out or deleted. --- Documentation/git-branch.txt | 15 ++++++++------- builtin/branch.c | 12 ++++++++---- t/t3200-branch.sh | 8 ++++---- t/t3203-branch-output.sh | 21 +++++++++++++++++++++ 4 files changed, 41 insertions(+), 15 deletions(-) diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index bf5316ffa9..b3eca6ffdc 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -26,13 +26,14 @@ DESCRIPTION ----------- If `--list` is given, or if there are no non-option arguments, existing -branches are listed; the current branch will be highlighted with an -asterisk. Option `-r` causes the remote-tracking branches to be listed, -and option `-a` shows both local and remote branches. If a `<pattern>` -is given, it is used as a shell wildcard to restrict the output to -matching branches. If multiple patterns are given, a branch is shown if -it matches any of the patterns. Note that when providing a -`<pattern>`, you must use `--list`; otherwise the command is interpreted +branches are listed; the current branch will be highlighted in green and +marked with an asterisk. Any branches checked out in linked worktrees will +be highlighted in cyan and marked with a plus sign. Option `-r` causes the +remote-tracking branches to be listed, and option `-a` shows both local and +remote branches. If a `<pattern>` is given, it is used as a shell wildcard to +restrict the output to matching branches. If multiple patterns are given, a +branch is shown if it matches any of the patterns. Note that when providing +a `<pattern>`, you must use `--list`; otherwise the command is interpreted as branch creation. With `--contains`, shows only the branches that contain the named commit diff --git a/builtin/branch.c b/builtin/branch.c index 1be727209b..c2a86362bb 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -47,6 +47,7 @@ static char branch_colors[][COLOR_MAXLEN] = { GIT_COLOR_NORMAL, /* LOCAL */ GIT_COLOR_GREEN, /* CURRENT */ GIT_COLOR_BLUE, /* UPSTREAM */ + GIT_COLOR_CYAN, /* WORKTREE */ }; enum color_branch { BRANCH_COLOR_RESET = 0, @@ -54,7 +55,8 @@ enum color_branch { BRANCH_COLOR_REMOTE = 2, BRANCH_COLOR_LOCAL = 3, BRANCH_COLOR_CURRENT = 4, - BRANCH_COLOR_UPSTREAM = 5 + BRANCH_COLOR_UPSTREAM = 5, + BRANCH_COLOR_WORKTREE = 6 }; static const char *color_branch_slots[] = { @@ -64,6 +66,7 @@ static const char *color_branch_slots[] = { [BRANCH_COLOR_LOCAL] = "local", [BRANCH_COLOR_CURRENT] = "current", [BRANCH_COLOR_UPSTREAM] = "upstream", + [BRANCH_COLOR_WORKTREE] = "worktree", }; static struct string_list output = STRING_LIST_INIT_DUP; @@ -342,9 +345,10 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r struct strbuf local = STRBUF_INIT; struct strbuf remote = STRBUF_INIT; - strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else) %s%%(end)", - branch_get_color(BRANCH_COLOR_CURRENT), - branch_get_color(BRANCH_COLOR_LOCAL)); + strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else)%%(if)%%(worktreepath)%%(then)+ %s%%(else) %s%%(end)%%(end)", + branch_get_color(BRANCH_COLOR_CURRENT), + branch_get_color(BRANCH_COLOR_WORKTREE), + branch_get_color(BRANCH_COLOR_LOCAL)); strbuf_addf(&remote, " %s", branch_get_color(BRANCH_COLOR_REMOTE)); diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index 478b82cf9b..e404f6e23c 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -292,7 +292,7 @@ test_expect_success 'git branch --list -v with --abbrev' ' test_expect_success 'git branch --column' ' COLUMNS=81 git branch --column=column >actual && cat >expected <<\EOF && - a/b/c bam foo l * master n o/p r + a/b/c + bam foo l * master n o/p r abc bar j/k m/m master2 o/o q EOF test_cmp expected actual @@ -307,7 +307,7 @@ test_expect_success 'git branch --column with an extremely long branch name' ' cat >expected <<EOF && a/b/c abc - bam ++ bam bar foo j/k @@ -332,7 +332,7 @@ test_expect_success 'git branch with column.*' ' git config --unset column.branch && git config --unset column.ui && cat >expected <<\EOF && - a/b/c bam foo l * master n o/p r + a/b/c + bam foo l * master n o/p r abc bar j/k m/m master2 o/o q EOF test_cmp expected actual @@ -349,7 +349,7 @@ test_expect_success 'git branch -v with column.ui ignored' ' cat >expected <<\EOF && a/b/c abc - bam ++ bam bar foo j/k diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh index ee6787614c..94ab05ad59 100755 --- a/t/t3203-branch-output.sh +++ b/t/t3203-branch-output.sh @@ -240,6 +240,27 @@ test_expect_success 'git branch --format option' ' test_i18ncmp expect actual ' +test_expect_success '"add" a worktree' ' + mkdir worktree_dir && + git worktree add -b master_worktree worktree_dir master +' + +cat >expect <<'EOF' +* <GREEN>(HEAD detached from fromtag)<RESET> + ambiguous<RESET> + branch-one<RESET> + branch-two<RESET> + master<RESET> ++ <CYAN>master_worktree<RESET> + ref-to-branch<RESET> -> branch-one + ref-to-remote<RESET> -> origin/branch-one +EOF +test_expect_success TTY 'worktree colors correct' ' + test_terminal git branch >actual.raw && + test_decode_color <actual.raw >actual && + test_cmp expect actual +' + test_expect_success "set up color tests" ' echo "<RED>master<RESET>" >expect.color && echo "master" >expect.bare && -- 2.14.2 ^ permalink raw reply related [flat|nested] 136+ messages in thread
* [PATCH v6 3/3] branch: Add an extra verbose output displaying worktree path for refs checked out in a linked worktree 2019-01-22 23:22 ` [PATCH v6 0/3] nbelakovski 2019-01-22 23:22 ` [PATCH v6 1/3] ref-filter: add worktreepath atom nbelakovski 2019-01-22 23:23 ` [PATCH v6 2/3] branch: Mark and color a branch differently if it is checked out in a linked worktree nbelakovski @ 2019-01-22 23:23 ` nbelakovski 2 siblings, 0 replies; 136+ messages in thread From: nbelakovski @ 2019-01-22 23:23 UTC (permalink / raw) To: git; +Cc: peff, rafa.almas, gitster, avarab, Nickolai Belakovski From: Nickolai Belakovski <nbelakovski@gmail.com> --- Documentation/git-branch.txt | 7 +++++-- builtin/branch.c | 4 ++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index b3eca6ffdc..a1aef00af0 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -163,12 +163,15 @@ This option is only applicable in non-verbose mode. -v:: -vv:: +-vvv:: --verbose:: When in list mode, show sha1 and commit subject line for each head, along with relationship to upstream branch (if any). If given twice, print - the name of the upstream branch, as well (see also `git remote - show <remote>`). + the path of the linked worktree, if applicable (not applicable + for main worktree since user's path will already be in main + worktree) and the name of the upstream branch, as well (see also + `git remote show <remote>`). -q:: --quiet:: diff --git a/builtin/branch.c b/builtin/branch.c index c2a86362bb..0b8ba9e4c5 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -367,9 +367,13 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r strbuf_addf(&local, " %s ", obname.buf); if (filter->verbose > 1) + { + strbuf_addf(&local, "%%(if:notequals=*)%%(HEAD)%%(then)%%(if)%%(worktreepath)%%(then)(%s%%(worktreepath)%s) %%(end)%%(end)", + branch_get_color(BRANCH_COLOR_WORKTREE), branch_get_color(BRANCH_COLOR_RESET)); strbuf_addf(&local, "%%(if)%%(upstream)%%(then)[%s%%(upstream:short)%s%%(if)%%(upstream:track)" "%%(then): %%(upstream:track,nobracket)%%(end)] %%(end)%%(contents:subject)", branch_get_color(BRANCH_COLOR_UPSTREAM), branch_get_color(BRANCH_COLOR_RESET)); + } else strbuf_addf(&local, "%%(if)%%(upstream:track)%%(then)%%(upstream:track) %%(end)%%(contents:subject)"); -- 2.14.2 ^ permalink raw reply related [flat|nested] 136+ messages in thread
* [PATCH v7 0/3] 2018-09-27 15:13 ` [PATCH] branch: colorize branches checked out in a linked working tree the same way as the current branch is colorized Nickolai Belakovski ` (4 preceding siblings ...) 2019-01-22 23:22 ` [PATCH v6 0/3] nbelakovski @ 2019-02-01 22:04 ` nbelakovski 2019-02-01 22:28 ` Junio C Hamano 2019-02-01 22:04 ` [PATCH v7 1/3] ref-filter: add worktreepath atom nbelakovski ` (5 subsequent siblings) 11 siblings, 1 reply; 136+ messages in thread From: nbelakovski @ 2019-02-01 22:04 UTC (permalink / raw) To: git; +Cc: peff, rafa.almas, gitster, avarab, Nickolai Belakovski From: Nickolai Belakovski <nbelakovski@gmail.com> Moved initialization of ref_to_worktree_map outside of atom parser. Other minor fixes. Put the hashmap and the worktree struct in the same struct, to make it more obvious that they are to be initialized and free'd together. Travis CI results: https://travis-ci.org/nbelakovski/git/builds/487642061 Nickolai Belakovski (3): ref-filter: add worktreepath atom branch: Mark and color a branch differently if it is checked out in a linked worktree branch: Add an extra verbose output displaying worktree path for refs checked out in a linked worktree Documentation/git-branch.txt | 21 +++++----- Documentation/git-for-each-ref.txt | 5 +++ builtin/branch.c | 16 ++++++-- ref-filter.c | 78 ++++++++++++++++++++++++++++++++++++++ t/t3200-branch.sh | 8 ++-- t/t3203-branch-output.sh | 21 ++++++++++ t/t6302-for-each-ref-filter.sh | 15 ++++++++ 7 files changed, 147 insertions(+), 17 deletions(-) -- 2.14.2 ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v7 0/3] 2019-02-01 22:04 ` [PATCH v7 0/3] nbelakovski @ 2019-02-01 22:28 ` Junio C Hamano 2019-02-01 23:31 ` Nickolai Belakovski 0 siblings, 1 reply; 136+ messages in thread From: Junio C Hamano @ 2019-02-01 22:28 UTC (permalink / raw) To: nbelakovski; +Cc: git, peff, rafa.almas, avarab nbelakovski@gmail.com writes: > ref-filter: add worktreepath atom > branch: Mark and color a branch differently if it is checked out in a > linked worktree > branch: Add an extra verbose output displaying worktree path for refs > checked out in a linked worktree As you can see in "git shortlog --no-merges", later two patches would look quite out of place by having overlong title and starting the description(i.e. after "<area>: ") in a capital letter. It is still not clear why we would want 2/3, even though I think 3/3 is a good idea. This round signs off all the three patches, which is a great improvement ;-) ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v7 0/3] 2019-02-01 22:28 ` Junio C Hamano @ 2019-02-01 23:31 ` Nickolai Belakovski 0 siblings, 0 replies; 136+ messages in thread From: Nickolai Belakovski @ 2019-02-01 23:31 UTC (permalink / raw) To: Junio C Hamano Cc: Git List, Jeff King, Rafael Ascensão, Ævar Arnfjörð Bjarmason On Fri, Feb 1, 2019 at 2:54 PM Junio C Hamano <gitster@pobox.com> wrote: > > > As you can see in "git shortlog --no-merges", later two patches > would look quite out of place by having overlong title and starting > the description(i.e. after "<area>: ") in a capital letter. Hadn't looked at it that way. OK, will shorten/uncapitalize. > > It is still not clear why we would want 2/3, even though I think 3/3 > is a good idea. > It's interesting to me that you like 3/3 but not 2/3 :) My apologies for restating the commit message, but the point of 2/3 is to communicate to the user that highlighted/marked branches will behave differently from unhighlighted/unmarked branches for commands to check out or delete. I think this is useful since it gives the user actionable information ahead of time, as opposed to providing that information upon failure of checkout/delete. It also makes sense since 'git branch' is already highlighting the current branch, i.e. this is just an extension of that idea. As we've stated earlier in this thread, 1/3 allows for users to implement this on their own with a custom git branch format. Personally I think there's value in making it default, since it's adding information in a minimally intrusive way. I do believe that merely adding information isn't a good enough reason to change things, as information overload is a real thing, but in this case the output isn't changed for anyone not using a worktree (same goes for 3/3), and for someone using a worktree this provides useful and actionable information, IMO. Does that change your mind at all? ^ permalink raw reply [flat|nested] 136+ messages in thread
* [PATCH v7 1/3] ref-filter: add worktreepath atom 2018-09-27 15:13 ` [PATCH] branch: colorize branches checked out in a linked working tree the same way as the current branch is colorized Nickolai Belakovski ` (5 preceding siblings ...) 2019-02-01 22:04 ` [PATCH v7 0/3] nbelakovski @ 2019-02-01 22:04 ` nbelakovski 2019-02-01 22:20 ` Eric Sunshine 2019-02-01 22:31 ` Junio C Hamano 2019-02-01 22:04 ` [PATCH v7 2/3] branch: Mark and color a branch differently if it is checked out in a linked worktree nbelakovski ` (4 subsequent siblings) 11 siblings, 2 replies; 136+ messages in thread From: nbelakovski @ 2019-02-01 22:04 UTC (permalink / raw) To: git; +Cc: peff, rafa.almas, gitster, avarab, Nickolai Belakovski From: Nickolai Belakovski <nbelakovski@gmail.com> Add an atom providing the path of the linked worktree where this ref is checked out, if it is checked out in any linked worktrees, and empty string otherwise. Signed-off-by: Nickolai Belakovski <nbelakovski@gmail.com> --- Documentation/git-for-each-ref.txt | 5 +++ ref-filter.c | 78 ++++++++++++++++++++++++++++++++++++++ t/t6302-for-each-ref-filter.sh | 15 ++++++++ 3 files changed, 98 insertions(+) diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt index 774cecc7ed..6dcd39f6f6 100644 --- a/Documentation/git-for-each-ref.txt +++ b/Documentation/git-for-each-ref.txt @@ -214,6 +214,11 @@ symref:: `:lstrip` and `:rstrip` options in the same way as `refname` above. +worktreepath:: + The absolute path to the worktree in which the ref is checked + out, if it is checked out in any linked worktree. Empty string + otherwise. + In addition to the above, for commit and tag objects, the header field names (`tree`, `parent`, `object`, `type`, and `tag`) can be used to specify the value in the header field. diff --git a/ref-filter.c b/ref-filter.c index 422a9c9ae3..e46608476d 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -20,6 +20,8 @@ #include "commit-slab.h" #include "commit-graph.h" #include "commit-reach.h" +#include "worktree.h" +#include "hashmap.h" static struct ref_msg { const char *gone; @@ -75,6 +77,27 @@ static struct expand_data { struct object_info info; } oi, oi_deref; +struct ref_to_worktree_entry { + struct hashmap_entry ent; /* must be the first member! */ + struct worktree *wt; /* key is wt->head_ref */ +}; + +static int ref_to_worktree_map_cmpfnc(const void *unused_lookupdata, + const void *existing_hashmap_entry_to_test, + const void *key, + const void *keydata_aka_refname) +{ + const struct ref_to_worktree_entry *e = existing_hashmap_entry_to_test; + const struct ref_to_worktree_entry *k = key; + return strcmp(e->wt->head_ref, + keydata_aka_refname ? keydata_aka_refname : k->wt->head_ref); +} + +static struct ref_to_worktree_map { + struct hashmap map; + struct worktree **worktrees; +} ref_to_worktree_map; + /* * An atom is a valid field atom listed below, possibly prefixed with * a "*" to denote deref_tag(). @@ -480,6 +503,7 @@ static struct { { "flag", SOURCE_NONE }, { "HEAD", SOURCE_NONE, FIELD_STR, head_atom_parser }, { "color", SOURCE_NONE, FIELD_STR, color_atom_parser }, + { "worktreepath", SOURCE_NONE }, { "align", SOURCE_NONE, FIELD_STR, align_atom_parser }, { "end", SOURCE_NONE }, { "if", SOURCE_NONE, FIELD_STR, if_atom_parser }, @@ -1525,6 +1549,48 @@ static int get_object(struct ref_array_item *ref, int deref, struct object **obj return 0; } +static void populate_worktree_map(struct hashmap *map, struct worktree **worktrees) +{ + int i; + + for (i = 0; worktrees[i]; i++) { + if (worktrees[i]->head_ref) { + struct ref_to_worktree_entry *entry; + entry = xmalloc(sizeof(*entry)); + entry->wt = worktrees[i]; + hashmap_entry_init(entry, strhash(worktrees[i]->head_ref)); + + hashmap_add(map, entry); + } + } +} + +static void lazy_init_worktree_map(void) +{ + if (ref_to_worktree_map.worktrees) + return; + + ref_to_worktree_map.worktrees = get_worktrees(0); + hashmap_init(&(ref_to_worktree_map.map), ref_to_worktree_map_cmpfnc, NULL, 0); + populate_worktree_map(&(ref_to_worktree_map.map), ref_to_worktree_map.worktrees); +} + +static char *get_worktree_path(const struct used_atom *atom, const struct ref_array_item *ref) +{ + struct hashmap_entry entry; + struct ref_to_worktree_entry *lookup_result; + + lazy_init_worktree_map(); + + hashmap_entry_init(&entry, strhash(ref->refname)); + lookup_result = hashmap_get(&(ref_to_worktree_map.map), &entry, ref->refname); + + if (lookup_result) + return xstrdup(lookup_result->wt->path); + else + return xstrdup(""); +} + /* * Parse the object referred by ref, and grab needed value. */ @@ -1562,6 +1628,13 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err) if (starts_with(name, "refname")) refname = get_refname(atom, ref); + else if (starts_with(name, "worktreepath")) { + if (ref->kind == FILTER_REFS_BRANCHES) + v->s = get_worktree_path(atom, ref); + else + v->s = xstrdup(""); + continue; + } else if (starts_with(name, "symref")) refname = get_symref(atom, ref); else if (starts_with(name, "upstream")) { @@ -2045,6 +2118,11 @@ void ref_array_clear(struct ref_array *array) free_array_item(array->items[i]); FREE_AND_NULL(array->items); array->nr = array->alloc = 0; + if (ref_to_worktree_map.worktrees) { + hashmap_free(&(ref_to_worktree_map.map), 1); + free_worktrees(ref_to_worktree_map.worktrees); + ref_to_worktree_map.worktrees = NULL; + } } static void do_merge_filter(struct ref_filter_cbdata *ref_cbdata) diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh index fc067ed672..87e0222ea1 100755 --- a/t/t6302-for-each-ref-filter.sh +++ b/t/t6302-for-each-ref-filter.sh @@ -441,4 +441,19 @@ test_expect_success '--merged is incompatible with --no-merged' ' test_must_fail git for-each-ref --merged HEAD --no-merged HEAD ' +test_expect_success '"add" a worktree' ' + mkdir worktree_dir && + git worktree add -b master_worktree worktree_dir master +' + +test_expect_success 'validate worktree atom' ' + cat >expect <<-EOF && + master: $(pwd) + master_worktree: $(pwd)/worktree_dir + side: not checked out + EOF + git for-each-ref --format="%(refname:short): %(if)%(worktreepath)%(then)%(worktreepath)%(else)not checked out%(end)" refs/heads/ >actual && + test_cmp expect actual +' + test_done -- 2.14.2 ^ permalink raw reply related [flat|nested] 136+ messages in thread
* Re: [PATCH v7 1/3] ref-filter: add worktreepath atom 2019-02-01 22:04 ` [PATCH v7 1/3] ref-filter: add worktreepath atom nbelakovski @ 2019-02-01 22:20 ` Eric Sunshine 2019-02-01 22:41 ` Nickolai Belakovski 2019-02-01 22:31 ` Junio C Hamano 1 sibling, 1 reply; 136+ messages in thread From: Eric Sunshine @ 2019-02-01 22:20 UTC (permalink / raw) To: Nickolai Belakovski Cc: Git List, Jeff King, Rafael Ascensao, Junio C Hamano, Ævar Arnfjörð Bjarmason On Fri, Feb 1, 2019 at 5:04 PM <nbelakovski@gmail.com> wrote: > Add an atom providing the path of the linked worktree where this ref is > checked out, if it is checked out in any linked worktrees, and empty > string otherwise. > > Signed-off-by: Nickolai Belakovski <nbelakovski@gmail.com> > --- > diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt > @@ -214,6 +214,11 @@ symref:: > +worktreepath:: > + The absolute path to the worktree in which the ref is checked > + out, if it is checked out in any linked worktree. Empty string > + otherwise. This may have been asked previously, but is there a reason this name was chosen over the more extensible "worktree:" with "path" as a modifier (i.e. "worktree:path")? I scanned the thread a couple weeks ago and did see mention of "worktree:path" but did not find any followup. I ask because it's conceivable that someone in the future might want to retrieve other information about the worktree beyond its path (such as whether it's bare or detached, etc.). By using the form "worktree:<foo>", we leave that door open. (I'm not suggesting that this patch series needs to implement fetching of any of the other worktree properties, but just asking if "worktree:<foo>" should be considered.) > diff --git a/ref-filter.c b/ref-filter.c > @@ -1562,6 +1628,13 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err) > if (starts_with(name, "refname")) > refname = get_refname(atom, ref); > + else if (starts_with(name, "worktreepath")) { I think this was brought up previously, but shouldn't this be strcmp() rather than starts_with()? (starts_with() would be appropriate, if you went with the suggested "worktree:<foo>".) > diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh > @@ -441,4 +441,19 @@ test_expect_success '--merged is incompatible with --no-merged' ' > +test_expect_success '"add" a worktree' ' > + mkdir worktree_dir && > + git worktree add -b master_worktree worktree_dir master > +' I don't think 'mkdir' is needed since "git worktree add" should create the directory itself. > +test_expect_success 'validate worktree atom' ' > + cat >expect <<-EOF && > + master: $(pwd) > + master_worktree: $(pwd)/worktree_dir > + side: not checked out > + EOF > + git for-each-ref --format="%(refname:short): %(if)%(worktreepath)%(then)%(worktreepath)%(else)not checked out%(end)" refs/heads/ >actual && > + test_cmp expect actual > +' If this is the only test using that newly-created worktree, it might make sense to squash the two tests together. ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v7 1/3] ref-filter: add worktreepath atom 2019-02-01 22:20 ` Eric Sunshine @ 2019-02-01 22:41 ` Nickolai Belakovski 2019-02-04 18:14 ` Junio C Hamano 0 siblings, 1 reply; 136+ messages in thread From: Nickolai Belakovski @ 2019-02-01 22:41 UTC (permalink / raw) To: Eric Sunshine Cc: Git List, Jeff King, Rafael Ascensao, Junio C Hamano, Ævar Arnfjörð Bjarmason On Fri, Feb 1, 2019 at 2:20 PM Eric Sunshine <sunshine@sunshineco.com> wrote: > > On Fri, Feb 1, 2019 at 5:04 PM <nbelakovski@gmail.com> wrote: > > Add an atom providing the path of the linked worktree where this ref is > > checked out, if it is checked out in any linked worktrees, and empty > > string otherwise. > > > > Signed-off-by: Nickolai Belakovski <nbelakovski@gmail.com> > > --- > > diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt > > @@ -214,6 +214,11 @@ symref:: > > +worktreepath:: > > + The absolute path to the worktree in which the ref is checked > > + out, if it is checked out in any linked worktree. Empty string > > + otherwise. > > This may have been asked previously, but is there a reason this name > was chosen over the more extensible "worktree:" with "path" as a > modifier (i.e. "worktree:path")? I scanned the thread a couple weeks > ago and did see mention of "worktree:path" but did not find any > followup. I ask because it's conceivable that someone in the future > might want to retrieve other information about the worktree beyond its > path (such as whether it's bare or detached, etc.). By using the form > "worktree:<foo>", we leave that door open. (I'm not suggesting that > this patch series needs to implement fetching of any of the other > worktree properties, but just asking if "worktree:<foo>" should be > considered.) > There's been a little back and forth on it, but my understanding is that using the colon separator bypasses the caching mechanism in the atoms, so every instance of "worktree:path" in a format string would require a lookup. Future atoms should be along the lines of "worktreeisdetached", "worktreeisbare", etc. This is consistent with several of the other atoms, like objecttype/size/name, comitter/name/email/date. > > diff --git a/ref-filter.c b/ref-filter.c > > @@ -1562,6 +1628,13 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err) > > if (starts_with(name, "refname")) > > refname = get_refname(atom, ref); > > + else if (starts_with(name, "worktreepath")) { > > I think this was brought up previously, but shouldn't this be strcmp() > rather than starts_with()? > > (starts_with() would be appropriate, if you went with the suggested > "worktree:<foo>".) Not sure about it being brought up previously. starts_with seemed consistent with other uses but now I see there's several other instance of strcmp in populate value. Seems like a reasonable thing to change. I had previously implemented "worktree:<foo>" and must've left it alone after we went with worktreepath. > > > diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh > > @@ -441,4 +441,19 @@ test_expect_success '--merged is incompatible with --no-merged' ' > > +test_expect_success '"add" a worktree' ' > > + mkdir worktree_dir && > > + git worktree add -b master_worktree worktree_dir master > > +' > > I don't think 'mkdir' is needed since "git worktree add" should create > the directory itself. > > > +test_expect_success 'validate worktree atom' ' > > + cat >expect <<-EOF && > > + master: $(pwd) > > + master_worktree: $(pwd)/worktree_dir > > + side: not checked out > > + EOF > > + git for-each-ref --format="%(refname:short): %(if)%(worktreepath)%(then)%(worktreepath)%(else)not checked out%(end)" refs/heads/ >actual && > > + test_cmp expect actual > > +' > > If this is the only test using that newly-created worktree, it might > make sense to squash the two tests together. Sure, can do, on both points. ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v7 1/3] ref-filter: add worktreepath atom 2019-02-01 22:41 ` Nickolai Belakovski @ 2019-02-04 18:14 ` Junio C Hamano 2019-02-18 10:09 ` Nickolai Belakovski 0 siblings, 1 reply; 136+ messages in thread From: Junio C Hamano @ 2019-02-04 18:14 UTC (permalink / raw) To: Nickolai Belakovski Cc: Eric Sunshine, Git List, Jeff King, Rafael Ascensao, Ævar Arnfjörð Bjarmason Nickolai Belakovski <nbelakovski@gmail.com> writes: > There's been a little back and forth on it, but my understanding is > that using the colon separator bypasses the caching mechanism in the > atoms, so every instance of "worktree:path" in a format string would > require a lookup. Would that be a problem, though? You now have a singleton hashmap that is a file-scope global not tied to any particular atom, so...? ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v7 1/3] ref-filter: add worktreepath atom 2019-02-04 18:14 ` Junio C Hamano @ 2019-02-18 10:09 ` Nickolai Belakovski 0 siblings, 0 replies; 136+ messages in thread From: Nickolai Belakovski @ 2019-02-18 10:09 UTC (permalink / raw) To: Junio C Hamano Cc: Eric Sunshine, Git List, Jeff King, Rafael Ascensao, Ævar Arnfjörð Bjarmason Well, it sounded like we didn't like the ":" extender from another conversation on this thread. Do you think this patch should move back in that direction? On Tue, Feb 5, 2019 at 3:14 AM Junio C Hamano <gitster@pobox.com> wrote: > > Nickolai Belakovski <nbelakovski@gmail.com> writes: > > > There's been a little back and forth on it, but my understanding is > > that using the colon separator bypasses the caching mechanism in the > > atoms, so every instance of "worktree:path" in a format string would > > require a lookup. > > Would that be a problem, though? You now have a singleton hashmap > that is a file-scope global not tied to any particular atom, so...? ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v7 1/3] ref-filter: add worktreepath atom 2019-02-01 22:04 ` [PATCH v7 1/3] ref-filter: add worktreepath atom nbelakovski 2019-02-01 22:20 ` Eric Sunshine @ 2019-02-01 22:31 ` Junio C Hamano 1 sibling, 0 replies; 136+ messages in thread From: Junio C Hamano @ 2019-02-01 22:31 UTC (permalink / raw) To: nbelakovski; +Cc: git, peff, rafa.almas, avarab nbelakovski@gmail.com writes: > +static void lazy_init_worktree_map(void) > +{ > + if (ref_to_worktree_map.worktrees) > + return; > + > + ref_to_worktree_map.worktrees = get_worktrees(0); > + hashmap_init(&(ref_to_worktree_map.map), ref_to_worktree_map_cmpfnc, NULL, 0); > + populate_worktree_map(&(ref_to_worktree_map.map), ref_to_worktree_map.worktrees); > +} > + > +static char *get_worktree_path(const struct used_atom *atom, const struct ref_array_item *ref) > +{ > + struct hashmap_entry entry; > + struct ref_to_worktree_entry *lookup_result; > + > + lazy_init_worktree_map(); > + > + hashmap_entry_init(&entry, strhash(ref->refname)); > + lookup_result = hashmap_get(&(ref_to_worktree_map.map), &entry, ref->refname); > + > + if (lookup_result) > + return xstrdup(lookup_result->wt->path); > + else > + return xstrdup(""); > +} Makes more sense than the previous round; much simpler to have lazy-init in this function. Thanks. ^ permalink raw reply [flat|nested] 136+ messages in thread
* [PATCH v7 2/3] branch: Mark and color a branch differently if it is checked out in a linked worktree 2018-09-27 15:13 ` [PATCH] branch: colorize branches checked out in a linked working tree the same way as the current branch is colorized Nickolai Belakovski ` (6 preceding siblings ...) 2019-02-01 22:04 ` [PATCH v7 1/3] ref-filter: add worktreepath atom nbelakovski @ 2019-02-01 22:04 ` nbelakovski 2019-02-01 22:34 ` Junio C Hamano 2019-02-01 22:04 ` [PATCH v7 3/3] branch: Add an extra verbose output displaying worktree path for refs " nbelakovski ` (3 subsequent siblings) 11 siblings, 1 reply; 136+ messages in thread From: nbelakovski @ 2019-02-01 22:04 UTC (permalink / raw) To: git; +Cc: peff, rafa.almas, gitster, avarab, Nickolai Belakovski From: Nickolai Belakovski <nbelakovski@gmail.com> The output of git branch is modified to mark branches checkout out in a linked worktree with a "+" and color them in cyan (in contrast to the current branch, which will still be denoted with a "*" and colored in green) This is meant to communicate to the user that the branches that are marked or colored will behave differently from other branches if the user attempts to check them out or delete them, since branches checked out in another worktree cannot be checked out or deleted. Signed-off-by: Nickolai Belakovski <nbelakovski@gmail.com> --- Documentation/git-branch.txt | 15 ++++++++------- builtin/branch.c | 12 ++++++++---- t/t3200-branch.sh | 8 ++++---- t/t3203-branch-output.sh | 21 +++++++++++++++++++++ 4 files changed, 41 insertions(+), 15 deletions(-) diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index bf5316ffa9..b3eca6ffdc 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -26,13 +26,14 @@ DESCRIPTION ----------- If `--list` is given, or if there are no non-option arguments, existing -branches are listed; the current branch will be highlighted with an -asterisk. Option `-r` causes the remote-tracking branches to be listed, -and option `-a` shows both local and remote branches. If a `<pattern>` -is given, it is used as a shell wildcard to restrict the output to -matching branches. If multiple patterns are given, a branch is shown if -it matches any of the patterns. Note that when providing a -`<pattern>`, you must use `--list`; otherwise the command is interpreted +branches are listed; the current branch will be highlighted in green and +marked with an asterisk. Any branches checked out in linked worktrees will +be highlighted in cyan and marked with a plus sign. Option `-r` causes the +remote-tracking branches to be listed, and option `-a` shows both local and +remote branches. If a `<pattern>` is given, it is used as a shell wildcard to +restrict the output to matching branches. If multiple patterns are given, a +branch is shown if it matches any of the patterns. Note that when providing +a `<pattern>`, you must use `--list`; otherwise the command is interpreted as branch creation. With `--contains`, shows only the branches that contain the named commit diff --git a/builtin/branch.c b/builtin/branch.c index 1be727209b..c2a86362bb 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -47,6 +47,7 @@ static char branch_colors[][COLOR_MAXLEN] = { GIT_COLOR_NORMAL, /* LOCAL */ GIT_COLOR_GREEN, /* CURRENT */ GIT_COLOR_BLUE, /* UPSTREAM */ + GIT_COLOR_CYAN, /* WORKTREE */ }; enum color_branch { BRANCH_COLOR_RESET = 0, @@ -54,7 +55,8 @@ enum color_branch { BRANCH_COLOR_REMOTE = 2, BRANCH_COLOR_LOCAL = 3, BRANCH_COLOR_CURRENT = 4, - BRANCH_COLOR_UPSTREAM = 5 + BRANCH_COLOR_UPSTREAM = 5, + BRANCH_COLOR_WORKTREE = 6 }; static const char *color_branch_slots[] = { @@ -64,6 +66,7 @@ static const char *color_branch_slots[] = { [BRANCH_COLOR_LOCAL] = "local", [BRANCH_COLOR_CURRENT] = "current", [BRANCH_COLOR_UPSTREAM] = "upstream", + [BRANCH_COLOR_WORKTREE] = "worktree", }; static struct string_list output = STRING_LIST_INIT_DUP; @@ -342,9 +345,10 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r struct strbuf local = STRBUF_INIT; struct strbuf remote = STRBUF_INIT; - strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else) %s%%(end)", - branch_get_color(BRANCH_COLOR_CURRENT), - branch_get_color(BRANCH_COLOR_LOCAL)); + strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else)%%(if)%%(worktreepath)%%(then)+ %s%%(else) %s%%(end)%%(end)", + branch_get_color(BRANCH_COLOR_CURRENT), + branch_get_color(BRANCH_COLOR_WORKTREE), + branch_get_color(BRANCH_COLOR_LOCAL)); strbuf_addf(&remote, " %s", branch_get_color(BRANCH_COLOR_REMOTE)); diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index 478b82cf9b..e404f6e23c 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -292,7 +292,7 @@ test_expect_success 'git branch --list -v with --abbrev' ' test_expect_success 'git branch --column' ' COLUMNS=81 git branch --column=column >actual && cat >expected <<\EOF && - a/b/c bam foo l * master n o/p r + a/b/c + bam foo l * master n o/p r abc bar j/k m/m master2 o/o q EOF test_cmp expected actual @@ -307,7 +307,7 @@ test_expect_success 'git branch --column with an extremely long branch name' ' cat >expected <<EOF && a/b/c abc - bam ++ bam bar foo j/k @@ -332,7 +332,7 @@ test_expect_success 'git branch with column.*' ' git config --unset column.branch && git config --unset column.ui && cat >expected <<\EOF && - a/b/c bam foo l * master n o/p r + a/b/c + bam foo l * master n o/p r abc bar j/k m/m master2 o/o q EOF test_cmp expected actual @@ -349,7 +349,7 @@ test_expect_success 'git branch -v with column.ui ignored' ' cat >expected <<\EOF && a/b/c abc - bam ++ bam bar foo j/k diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh index ee6787614c..94ab05ad59 100755 --- a/t/t3203-branch-output.sh +++ b/t/t3203-branch-output.sh @@ -240,6 +240,27 @@ test_expect_success 'git branch --format option' ' test_i18ncmp expect actual ' +test_expect_success '"add" a worktree' ' + mkdir worktree_dir && + git worktree add -b master_worktree worktree_dir master +' + +cat >expect <<'EOF' +* <GREEN>(HEAD detached from fromtag)<RESET> + ambiguous<RESET> + branch-one<RESET> + branch-two<RESET> + master<RESET> ++ <CYAN>master_worktree<RESET> + ref-to-branch<RESET> -> branch-one + ref-to-remote<RESET> -> origin/branch-one +EOF +test_expect_success TTY 'worktree colors correct' ' + test_terminal git branch >actual.raw && + test_decode_color <actual.raw >actual && + test_cmp expect actual +' + test_expect_success "set up color tests" ' echo "<RED>master<RESET>" >expect.color && echo "master" >expect.bare && -- 2.14.2 ^ permalink raw reply related [flat|nested] 136+ messages in thread
* Re: [PATCH v7 2/3] branch: Mark and color a branch differently if it is checked out in a linked worktree 2019-02-01 22:04 ` [PATCH v7 2/3] branch: Mark and color a branch differently if it is checked out in a linked worktree nbelakovski @ 2019-02-01 22:34 ` Junio C Hamano 2019-02-01 23:12 ` Nickolai Belakovski 0 siblings, 1 reply; 136+ messages in thread From: Junio C Hamano @ 2019-02-01 22:34 UTC (permalink / raw) To: nbelakovski; +Cc: git, peff, rafa.almas, avarab nbelakovski@gmail.com writes: > If `--list` is given, or if there are no non-option arguments, existing > -branches are listed; the current branch will be highlighted with an > -asterisk. Option `-r` causes the remote-tracking branches to be listed, > -and option `-a` shows both local and remote branches. If a `<pattern>` > -is given, it is used as a shell wildcard to restrict the output to > -matching branches. If multiple patterns are given, a branch is shown if > -it matches any of the patterns. Note that when providing a > -`<pattern>`, you must use `--list`; otherwise the command is interpreted > +branches are listed; the current branch will be highlighted in green and > +marked with an asterisk. Any branches checked out in linked worktrees will > +be highlighted in cyan and marked with a plus sign. Option `-r` causes the > +remote-tracking branches to be listed, and option `-a` shows both local and > +remote branches. If a `<pattern>` is given, it is used as a shell wildcard to > +restrict the output to matching branches. If multiple patterns are given, a > +branch is shown if it matches any of the patterns. Note that when providing > +a `<pattern>`, you must use `--list`; otherwise the command is interpreted > as branch creation. I had to apply and then use --color-words to see what is going on. Please avoid unnecessary reflowing of the text that makes the patch harder than necessary to read. I still do not quite see the point of this step in the series, though. ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v7 2/3] branch: Mark and color a branch differently if it is checked out in a linked worktree 2019-02-01 22:34 ` Junio C Hamano @ 2019-02-01 23:12 ` Nickolai Belakovski 2019-02-04 18:18 ` Junio C Hamano 0 siblings, 1 reply; 136+ messages in thread From: Nickolai Belakovski @ 2019-02-01 23:12 UTC (permalink / raw) To: Junio C Hamano Cc: Git List, Jeff King, Rafael Ascensão, Ævar Arnfjörð Bjarmason On Fri, Feb 1, 2019 at 2:54 PM Junio C Hamano <gitster@pobox.com> wrote: > > > I had to apply and then use --color-words to see what is going on. > Please avoid unnecessary reflowing of the text that makes the patch > harder than necessary to read. I was trying to keep the line length consistent. How else can I accomplish that? ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v7 2/3] branch: Mark and color a branch differently if it is checked out in a linked worktree 2019-02-01 23:12 ` Nickolai Belakovski @ 2019-02-04 18:18 ` Junio C Hamano 0 siblings, 0 replies; 136+ messages in thread From: Junio C Hamano @ 2019-02-04 18:18 UTC (permalink / raw) To: Nickolai Belakovski Cc: Git List, Jeff King, Rafael Ascensão, Ævar Arnfjörð Bjarmason Nickolai Belakovski <nbelakovski@gmail.com> writes: > On Fri, Feb 1, 2019 at 2:54 PM Junio C Hamano <gitster@pobox.com> wrote: >> >> >> I had to apply and then use --color-words to see what is going on. >> Please avoid unnecessary reflowing of the text that makes the patch >> harder than necessary to read. > > I was trying to keep the line length consistent. How else can I accomplish that? By relying on the fact that in the final output it does not matter how the sources look like, e.g. diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index bf5316ffa9..b64b3bf458 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -27,7 +27,9 @@ DESCRIPTION If `--list` is given, or if there are no non-option arguments, existing branches are listed; the current branch will be highlighted with an -asterisk. Option `-r` causes the remote-tracking branches to be listed, +asterisk and shown in green. Any branch checked out in linked worktrees +is shown in cyan and marked with a plus sign. Option `-r` causes the +remote-tracking branches to be listed, and option `-a` shows both local and remote branches. If a `<pattern>` is given, it is used as a shell wildcard to restrict the output to matching branches. If multiple patterns are given, a branch is shown if ^ permalink raw reply related [flat|nested] 136+ messages in thread
* [PATCH v7 3/3] branch: Add an extra verbose output displaying worktree path for refs checked out in a linked worktree 2018-09-27 15:13 ` [PATCH] branch: colorize branches checked out in a linked working tree the same way as the current branch is colorized Nickolai Belakovski ` (7 preceding siblings ...) 2019-02-01 22:04 ` [PATCH v7 2/3] branch: Mark and color a branch differently if it is checked out in a linked worktree nbelakovski @ 2019-02-01 22:04 ` nbelakovski 2019-02-01 22:27 ` Eric Sunshine 2019-02-01 22:53 ` Junio C Hamano 2019-02-19 8:31 ` [PATCH v8 0/3] nbelakovski ` (2 subsequent siblings) 11 siblings, 2 replies; 136+ messages in thread From: nbelakovski @ 2019-02-01 22:04 UTC (permalink / raw) To: git; +Cc: peff, rafa.almas, gitster, avarab, Nickolai Belakovski From: Nickolai Belakovski <nbelakovski@gmail.com> Signed-off-by: Nickolai Belakovski <nbelakovski@gmail.com> --- Documentation/git-branch.txt | 6 ++++-- builtin/branch.c | 4 ++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index b3eca6ffdc..778be7080f 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -167,8 +167,10 @@ This option is only applicable in non-verbose mode. When in list mode, show sha1 and commit subject line for each head, along with relationship to upstream branch (if any). If given twice, print - the name of the upstream branch, as well (see also `git remote - show <remote>`). + the path of the linked worktree, if applicable (not applicable + for main worktree since user's path will already be in main + worktree) and the name of the upstream branch, as well (see also + `git remote show <remote>`). -q:: --quiet:: diff --git a/builtin/branch.c b/builtin/branch.c index c2a86362bb..0b8ba9e4c5 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -367,9 +367,13 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r strbuf_addf(&local, " %s ", obname.buf); if (filter->verbose > 1) + { + strbuf_addf(&local, "%%(if:notequals=*)%%(HEAD)%%(then)%%(if)%%(worktreepath)%%(then)(%s%%(worktreepath)%s) %%(end)%%(end)", + branch_get_color(BRANCH_COLOR_WORKTREE), branch_get_color(BRANCH_COLOR_RESET)); strbuf_addf(&local, "%%(if)%%(upstream)%%(then)[%s%%(upstream:short)%s%%(if)%%(upstream:track)" "%%(then): %%(upstream:track,nobracket)%%(end)] %%(end)%%(contents:subject)", branch_get_color(BRANCH_COLOR_UPSTREAM), branch_get_color(BRANCH_COLOR_RESET)); + } else strbuf_addf(&local, "%%(if)%%(upstream:track)%%(then)%%(upstream:track) %%(end)%%(contents:subject)"); -- 2.14.2 ^ permalink raw reply related [flat|nested] 136+ messages in thread
* Re: [PATCH v7 3/3] branch: Add an extra verbose output displaying worktree path for refs checked out in a linked worktree 2019-02-01 22:04 ` [PATCH v7 3/3] branch: Add an extra verbose output displaying worktree path for refs " nbelakovski @ 2019-02-01 22:27 ` Eric Sunshine 2019-02-01 22:45 ` Nickolai Belakovski 2019-02-01 22:53 ` Junio C Hamano 1 sibling, 1 reply; 136+ messages in thread From: Eric Sunshine @ 2019-02-01 22:27 UTC (permalink / raw) To: Nickolai Belakovski Cc: Git List, Jeff King, Rafael Ascensao, Junio C Hamano, Ævar Arnfjörð Bjarmason On Fri, Feb 1, 2019 at 5:04 PM <nbelakovski@gmail.com> wrote: > Subject: branch: Add an extra verbose output displaying worktree path for refs checked out in a linked worktree Overlong subject. Perhaps shorten it to: branch: display worktree path in -v -v mode or something, and use the longer description as the rest of the body of the commit message. > Signed-off-by: Nickolai Belakovski <nbelakovski@gmail.com> > --- > diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt > @@ -167,8 +167,10 @@ This option is only applicable in non-verbose mode. > When in list mode, > show sha1 and commit subject line for each head, along with > relationship to upstream branch (if any). If given twice, print > - the name of the upstream branch, as well (see also `git remote > - show <remote>`). > + the path of the linked worktree, if applicable (not applicable > + for main worktree since user's path will already be in main > + worktree) and the name of the upstream branch, as well (see also > + `git remote show <remote>`). I'm not sure I understand the "not applicable" explanation. When you say "user's path", do you mean the current working directory? What happens if the command is invoked from within one of the linked worktrees (not from within the main worktree)? ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v7 3/3] branch: Add an extra verbose output displaying worktree path for refs checked out in a linked worktree 2019-02-01 22:27 ` Eric Sunshine @ 2019-02-01 22:45 ` Nickolai Belakovski 0 siblings, 0 replies; 136+ messages in thread From: Nickolai Belakovski @ 2019-02-01 22:45 UTC (permalink / raw) To: Eric Sunshine Cc: Git List, Jeff King, Rafael Ascensao, Junio C Hamano, Ævar Arnfjörð Bjarmason On Fri, Feb 1, 2019 at 2:28 PM Eric Sunshine <sunshine@sunshineco.com> wrote: > > On Fri, Feb 1, 2019 at 5:04 PM <nbelakovski@gmail.com> wrote: > > Subject: branch: Add an extra verbose output displaying worktree path for refs checked out in a linked worktree > > Overlong subject. Perhaps shorten it to: > > branch: display worktree path in -v -v mode > > or something, and use the longer description as the rest of the body > of the commit message. OK > > > Signed-off-by: Nickolai Belakovski <nbelakovski@gmail.com> > > --- > > diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt > > @@ -167,8 +167,10 @@ This option is only applicable in non-verbose mode. > > When in list mode, > > show sha1 and commit subject line for each head, along with > > relationship to upstream branch (if any). If given twice, print > > - the name of the upstream branch, as well (see also `git remote > > - show <remote>`). > > + the path of the linked worktree, if applicable (not applicable > > + for main worktree since user's path will already be in main > > + worktree) and the name of the upstream branch, as well (see also > > + `git remote show <remote>`). > > I'm not sure I understand the "not applicable" explanation. When you > say "user's path", do you mean the current working directory? What > happens if the command is invoked from within one of the linked > worktrees (not from within the main worktree)? I should correct that to say "current worktree" as opposed to main, since that's what HEAD will give. And yes user's path means cwd. Does that make more sense? ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v7 3/3] branch: Add an extra verbose output displaying worktree path for refs checked out in a linked worktree 2019-02-01 22:04 ` [PATCH v7 3/3] branch: Add an extra verbose output displaying worktree path for refs " nbelakovski 2019-02-01 22:27 ` Eric Sunshine @ 2019-02-01 22:53 ` Junio C Hamano 2019-02-01 23:06 ` Nickolai Belakovski 1 sibling, 1 reply; 136+ messages in thread From: Junio C Hamano @ 2019-02-01 22:53 UTC (permalink / raw) To: nbelakovski; +Cc: git, peff, rafa.almas, avarab nbelakovski@gmail.com writes: > @@ -167,8 +167,10 @@ This option is only applicable in non-verbose mode. > When in list mode, > show sha1 and commit subject line for each head, along with > relationship to upstream branch (if any). If given twice, print > - the name of the upstream branch, as well (see also `git remote > - show <remote>`). > + the path of the linked worktree, if applicable (not applicable > + for main worktree since user's path will already be in main > + worktree) and the name of the upstream branch, as well (see also > + `git remote show <remote>`). It is unclear what you mean by "user's path"; I take it as the $(pwd) at least for now for the purpose of this review, but then I am not sure if I agree with that justification part "since..." If I start from a normal repository at /home/gitster/main.git, create a linked worktree of it at /home/gitster/alt.git, chdir to /home/gitster/alt.git and ask "git branch -v -v", then the branch that is checked out in the main.git "main worktree" is not shown? If the rule were "a branch that is checked out in one of the worktrees connected to the repository is shown with the path to that worktree" (i.e. no exception), I would understand it. If the rule were "a branch that is ... (the same sentence), unless it is the branch that is checked out in the *current* worktree", then I would understand it too. Puzzled. In any case, please add a test or two to protect this feature from unintended future breakages. Thanks. > > -q:: > --quiet:: > diff --git a/builtin/branch.c b/builtin/branch.c > index c2a86362bb..0b8ba9e4c5 100644 > --- a/builtin/branch.c > +++ b/builtin/branch.c > @@ -367,9 +367,13 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r > strbuf_addf(&local, " %s ", obname.buf); > > if (filter->verbose > 1) > + { > + strbuf_addf(&local, "%%(if:notequals=*)%%(HEAD)%%(then)%%(if)%%(worktreepath)%%(then)(%s%%(worktreepath)%s) %%(end)%%(end)", > + branch_get_color(BRANCH_COLOR_WORKTREE), branch_get_color(BRANCH_COLOR_RESET)); > strbuf_addf(&local, "%%(if)%%(upstream)%%(then)[%s%%(upstream:short)%s%%(if)%%(upstream:track)" > "%%(then): %%(upstream:track,nobracket)%%(end)] %%(end)%%(contents:subject)", > branch_get_color(BRANCH_COLOR_UPSTREAM), branch_get_color(BRANCH_COLOR_RESET)); > + } > else > strbuf_addf(&local, "%%(if)%%(upstream:track)%%(then)%%(upstream:track) %%(end)%%(contents:subject)"); ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v7 3/3] branch: Add an extra verbose output displaying worktree path for refs checked out in a linked worktree 2019-02-01 22:53 ` Junio C Hamano @ 2019-02-01 23:06 ` Nickolai Belakovski 2019-02-02 1:22 ` [RFC] Sample of test for git branch -vv nbelakovski 0 siblings, 1 reply; 136+ messages in thread From: Nickolai Belakovski @ 2019-02-01 23:06 UTC (permalink / raw) To: Junio C Hamano Cc: Git List, Jeff King, Rafael Ascensão, Ævar Arnfjörð Bjarmason On Fri, Feb 1, 2019 at 2:54 PM Junio C Hamano <gitster@pobox.com> wrote: > > If the rule were "a branch that is checked out in one of the > worktrees connected to the repository is shown with the path to that > worktree" (i.e. no exception), I would understand it. If the rule > were "a branch that is ... (the same sentence), unless it is the > branch that is checked out in the *current* worktree", then I would > understand it too. > It is the latter, as in, yes, I meant "current". I will update the docs as such. > > In any case, please add a test or two to protect this feature from > unintended future breakages. > > Thanks. > Will do. ^ permalink raw reply [flat|nested] 136+ messages in thread
* [RFC] Sample of test for git branch -vv 2019-02-01 23:06 ` Nickolai Belakovski @ 2019-02-02 1:22 ` nbelakovski 0 siblings, 0 replies; 136+ messages in thread From: nbelakovski @ 2019-02-02 1:22 UTC (permalink / raw) To: git; +Cc: peff, gitster, sunshine, Nickolai Belakovski From: Nickolai Belakovski <nbelakovski@gmail.com> I remember now why I didn't add a test for this one earlier. Testing git branch -vv is a little tricky for a couple reasons. For one thing, the output contains commit hashes, so the 'expect' file cannot be simply static. For another, the output doesn't have clear delimiters for all columns, so trying to extract only the commit hashes is tough. I took the approach of stripping all information before and including the commit hashes. You can see below how I did this. For the patch with worktreepath information, the worktreepath would appear before the commit message on just one of those lines, but I crafted this patch so that it can be applied to master. This works, but it's rather awkward and ugly. Does anyone have better suggestions either for how to test or how to implement? --- t/t3203-branch-output.sh | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh index 94ab05ad59..b836ca21fa 100755 --- a/t/t3203-branch-output.sh +++ b/t/t3203-branch-output.sh @@ -285,4 +284,22 @@ test_expect_success '--color overrides auto-color' ' test_cmp expect.color actual ' +test_expect_success 'test verbose verbose output' ' + cat >expect <<-EOF && + one + one + two + one + two + two + two + EOF + git branch -vv >tmp && + head -1 tmp >tmp2 && + SUBSTRLENGTH=$(awk "{print index(\$0, \"one\")}" <tmp2) && + awk -v len="$SUBSTRLENGTH" "{print substr(\$0,len,length(\$0))}" <tmp >actual && + test_cmp expect actual +' + + test_done -- 2.14.2 ^ permalink raw reply related [flat|nested] 136+ messages in thread
* [PATCH v8 0/3] 2018-09-27 15:13 ` [PATCH] branch: colorize branches checked out in a linked working tree the same way as the current branch is colorized Nickolai Belakovski ` (8 preceding siblings ...) 2019-02-01 22:04 ` [PATCH v7 3/3] branch: Add an extra verbose output displaying worktree path for refs " nbelakovski @ 2019-02-19 8:31 ` nbelakovski 2019-02-19 8:31 ` [PATCH v8 1/3] ref-filter: add worktreepath atom nbelakovski ` (3 more replies) 2019-03-16 1:38 ` [PATCH v9 0/3] nbelakovski 2019-04-29 5:19 ` [PATCH v10 0/3] nbelakovski 11 siblings, 4 replies; 136+ messages in thread From: nbelakovski @ 2019-02-19 8:31 UTC (permalink / raw) To: git; +Cc: peff, rafa.almas, gitster, avarab, Nickolai Belakovski From: Nickolai Belakovski <nbelakovski@gmail.com> I've made the various cosmetic changes that were suggested, as well as adding tests for 3/3 I don't have a particularly strong opinion on the subject of keeping the atom as "worktreepath" or changing it to "worktree:path". We did feel earlier in this thread that if we went with "worktree:path", then "worktree" is somewhat ambiguous, and that discussion led to deciding to have "worktree" return the path,. After that I chose to name it "worktreepath" because I like to make things explicit and intuitive. Travis CI results: https://travis-ci.org/nbelakovski/git/builds/494817576 Nickolai Belakovski (3): ref-filter: add worktreepath atom branch: update output to include worktree info branch: add worktree info on verbose output Documentation/git-branch.txt | 12 ++++-- Documentation/git-for-each-ref.txt | 5 +++ builtin/branch.c | 16 ++++++-- ref-filter.c | 78 ++++++++++++++++++++++++++++++++++++++ t/t3200-branch.sh | 8 ++-- t/t3203-branch-output.sh | 38 +++++++++++++++++++ t/t6302-for-each-ref-filter.sh | 11 ++++++ 7 files changed, 156 insertions(+), 12 deletions(-) -- 2.14.2 ^ permalink raw reply [flat|nested] 136+ messages in thread
* [PATCH v8 1/3] ref-filter: add worktreepath atom 2019-02-19 8:31 ` [PATCH v8 0/3] nbelakovski @ 2019-02-19 8:31 ` nbelakovski 2019-02-19 8:31 ` [PATCH v8 2/3] branch: update output to include worktree info nbelakovski ` (2 subsequent siblings) 3 siblings, 0 replies; 136+ messages in thread From: nbelakovski @ 2019-02-19 8:31 UTC (permalink / raw) To: git; +Cc: peff, rafa.almas, gitster, avarab, Nickolai Belakovski From: Nickolai Belakovski <nbelakovski@gmail.com> Add an atom providing the path of the linked worktree where this ref is checked out, if it is checked out in any linked worktrees, and empty string otherwise. Signed-off-by: Nickolai Belakovski <nbelakovski@gmail.com> --- Documentation/git-for-each-ref.txt | 5 +++ ref-filter.c | 78 ++++++++++++++++++++++++++++++++++++++ t/t6302-for-each-ref-filter.sh | 11 ++++++ 3 files changed, 94 insertions(+) diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt index 774cecc7ed..6dcd39f6f6 100644 --- a/Documentation/git-for-each-ref.txt +++ b/Documentation/git-for-each-ref.txt @@ -214,6 +214,11 @@ symref:: `:lstrip` and `:rstrip` options in the same way as `refname` above. +worktreepath:: + The absolute path to the worktree in which the ref is checked + out, if it is checked out in any linked worktree. Empty string + otherwise. + In addition to the above, for commit and tag objects, the header field names (`tree`, `parent`, `object`, `type`, and `tag`) can be used to specify the value in the header field. diff --git a/ref-filter.c b/ref-filter.c index 422a9c9ae3..05531b7534 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -20,6 +20,8 @@ #include "commit-slab.h" #include "commit-graph.h" #include "commit-reach.h" +#include "worktree.h" +#include "hashmap.h" static struct ref_msg { const char *gone; @@ -75,6 +77,27 @@ static struct expand_data { struct object_info info; } oi, oi_deref; +struct ref_to_worktree_entry { + struct hashmap_entry ent; /* must be the first member! */ + struct worktree *wt; /* key is wt->head_ref */ +}; + +static int ref_to_worktree_map_cmpfnc(const void *unused_lookupdata, + const void *existing_hashmap_entry_to_test, + const void *key, + const void *keydata_aka_refname) +{ + const struct ref_to_worktree_entry *e = existing_hashmap_entry_to_test; + const struct ref_to_worktree_entry *k = key; + return strcmp(e->wt->head_ref, + keydata_aka_refname ? keydata_aka_refname : k->wt->head_ref); +} + +static struct ref_to_worktree_map { + struct hashmap map; + struct worktree **worktrees; +} ref_to_worktree_map; + /* * An atom is a valid field atom listed below, possibly prefixed with * a "*" to denote deref_tag(). @@ -480,6 +503,7 @@ static struct { { "flag", SOURCE_NONE }, { "HEAD", SOURCE_NONE, FIELD_STR, head_atom_parser }, { "color", SOURCE_NONE, FIELD_STR, color_atom_parser }, + { "worktreepath", SOURCE_NONE }, { "align", SOURCE_NONE, FIELD_STR, align_atom_parser }, { "end", SOURCE_NONE }, { "if", SOURCE_NONE, FIELD_STR, if_atom_parser }, @@ -1525,6 +1549,48 @@ static int get_object(struct ref_array_item *ref, int deref, struct object **obj return 0; } +static void populate_worktree_map(struct hashmap *map, struct worktree **worktrees) +{ + int i; + + for (i = 0; worktrees[i]; i++) { + if (worktrees[i]->head_ref) { + struct ref_to_worktree_entry *entry; + entry = xmalloc(sizeof(*entry)); + entry->wt = worktrees[i]; + hashmap_entry_init(entry, strhash(worktrees[i]->head_ref)); + + hashmap_add(map, entry); + } + } +} + +static void lazy_init_worktree_map(void) +{ + if (ref_to_worktree_map.worktrees) + return; + + ref_to_worktree_map.worktrees = get_worktrees(0); + hashmap_init(&(ref_to_worktree_map.map), ref_to_worktree_map_cmpfnc, NULL, 0); + populate_worktree_map(&(ref_to_worktree_map.map), ref_to_worktree_map.worktrees); +} + +static char *get_worktree_path(const struct used_atom *atom, const struct ref_array_item *ref) +{ + struct hashmap_entry entry; + struct ref_to_worktree_entry *lookup_result; + + lazy_init_worktree_map(); + + hashmap_entry_init(&entry, strhash(ref->refname)); + lookup_result = hashmap_get(&(ref_to_worktree_map.map), &entry, ref->refname); + + if (lookup_result) + return xstrdup(lookup_result->wt->path); + else + return xstrdup(""); +} + /* * Parse the object referred by ref, and grab needed value. */ @@ -1562,6 +1628,13 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err) if (starts_with(name, "refname")) refname = get_refname(atom, ref); + else if (!strcmp(name, "worktreepath")) { + if (ref->kind == FILTER_REFS_BRANCHES) + v->s = get_worktree_path(atom, ref); + else + v->s = xstrdup(""); + continue; + } else if (starts_with(name, "symref")) refname = get_symref(atom, ref); else if (starts_with(name, "upstream")) { @@ -2045,6 +2118,11 @@ void ref_array_clear(struct ref_array *array) free_array_item(array->items[i]); FREE_AND_NULL(array->items); array->nr = array->alloc = 0; + if (ref_to_worktree_map.worktrees) { + hashmap_free(&(ref_to_worktree_map.map), 1); + free_worktrees(ref_to_worktree_map.worktrees); + ref_to_worktree_map.worktrees = NULL; + } } static void do_merge_filter(struct ref_filter_cbdata *ref_cbdata) diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh index fc067ed672..4ad20615d5 100755 --- a/t/t6302-for-each-ref-filter.sh +++ b/t/t6302-for-each-ref-filter.sh @@ -441,4 +441,15 @@ test_expect_success '--merged is incompatible with --no-merged' ' test_must_fail git for-each-ref --merged HEAD --no-merged HEAD ' +test_expect_success 'validate worktree atom' ' + cat >expect <<-EOF && + master: $(pwd) + master_worktree: $(pwd)/worktree_dir + side: not checked out + EOF + git worktree add -b master_worktree worktree_dir master && + git for-each-ref --format="%(refname:short): %(if)%(worktreepath)%(then)%(worktreepath)%(else)not checked out%(end)" refs/heads/ >actual && + test_cmp expect actual +' + test_done -- 2.14.2 ^ permalink raw reply related [flat|nested] 136+ messages in thread
* [PATCH v8 2/3] branch: update output to include worktree info 2019-02-19 8:31 ` [PATCH v8 0/3] nbelakovski 2019-02-19 8:31 ` [PATCH v8 1/3] ref-filter: add worktreepath atom nbelakovski @ 2019-02-19 8:31 ` nbelakovski 2019-02-21 12:44 ` Jeff King 2019-02-19 8:31 ` [PATCH v8 3/3] branch: add worktree info on verbose output nbelakovski 2019-02-21 12:36 ` [PATCH v8 0/3] Jeff King 3 siblings, 1 reply; 136+ messages in thread From: nbelakovski @ 2019-02-19 8:31 UTC (permalink / raw) To: git; +Cc: peff, rafa.almas, gitster, avarab, Nickolai Belakovski From: Nickolai Belakovski <nbelakovski@gmail.com> The output of git branch is modified to mark branches checkout out in a linked worktree with a "+" and color them in cyan (in contrast to the current branch, which will still be denoted with a "*" and colored in green) This is meant to communicate to the user that the branches that are marked or colored will behave differently from other branches if the user attempts to check them out or delete them, since branches checked out in another worktree cannot be checked out or deleted. Signed-off-by: Nickolai Belakovski <nbelakovski@gmail.com> --- Documentation/git-branch.txt | 6 ++++-- builtin/branch.c | 12 ++++++++---- t/t3200-branch.sh | 8 ++++---- t/t3203-branch-output.sh | 21 +++++++++++++++++++++ 4 files changed, 37 insertions(+), 10 deletions(-) diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index 3bd83a7cbd..f2e5a07d64 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -26,8 +26,10 @@ DESCRIPTION ----------- If `--list` is given, or if there are no non-option arguments, existing -branches are listed; the current branch will be highlighted with an -asterisk. Option `-r` causes the remote-tracking branches to be listed, +branches are listed; the current branch will be highlighted in green and +marked with an asterisk. Any branches checked out in linked worktrees will +be highlighted in cyan and marked with a plus sign. Option `-r` causes the +remote-tracking branches to be listed, and option `-a` shows both local and remote branches. If a `<pattern>` is given, it is used as a shell wildcard to restrict the output to matching branches. If multiple patterns are given, a branch is shown if diff --git a/builtin/branch.c b/builtin/branch.c index 1be727209b..c2a86362bb 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -47,6 +47,7 @@ static char branch_colors[][COLOR_MAXLEN] = { GIT_COLOR_NORMAL, /* LOCAL */ GIT_COLOR_GREEN, /* CURRENT */ GIT_COLOR_BLUE, /* UPSTREAM */ + GIT_COLOR_CYAN, /* WORKTREE */ }; enum color_branch { BRANCH_COLOR_RESET = 0, @@ -54,7 +55,8 @@ enum color_branch { BRANCH_COLOR_REMOTE = 2, BRANCH_COLOR_LOCAL = 3, BRANCH_COLOR_CURRENT = 4, - BRANCH_COLOR_UPSTREAM = 5 + BRANCH_COLOR_UPSTREAM = 5, + BRANCH_COLOR_WORKTREE = 6 }; static const char *color_branch_slots[] = { @@ -64,6 +66,7 @@ static const char *color_branch_slots[] = { [BRANCH_COLOR_LOCAL] = "local", [BRANCH_COLOR_CURRENT] = "current", [BRANCH_COLOR_UPSTREAM] = "upstream", + [BRANCH_COLOR_WORKTREE] = "worktree", }; static struct string_list output = STRING_LIST_INIT_DUP; @@ -342,9 +345,10 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r struct strbuf local = STRBUF_INIT; struct strbuf remote = STRBUF_INIT; - strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else) %s%%(end)", - branch_get_color(BRANCH_COLOR_CURRENT), - branch_get_color(BRANCH_COLOR_LOCAL)); + strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else)%%(if)%%(worktreepath)%%(then)+ %s%%(else) %s%%(end)%%(end)", + branch_get_color(BRANCH_COLOR_CURRENT), + branch_get_color(BRANCH_COLOR_WORKTREE), + branch_get_color(BRANCH_COLOR_LOCAL)); strbuf_addf(&remote, " %s", branch_get_color(BRANCH_COLOR_REMOTE)); diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index 478b82cf9b..e404f6e23c 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -292,7 +292,7 @@ test_expect_success 'git branch --list -v with --abbrev' ' test_expect_success 'git branch --column' ' COLUMNS=81 git branch --column=column >actual && cat >expected <<\EOF && - a/b/c bam foo l * master n o/p r + a/b/c + bam foo l * master n o/p r abc bar j/k m/m master2 o/o q EOF test_cmp expected actual @@ -307,7 +307,7 @@ test_expect_success 'git branch --column with an extremely long branch name' ' cat >expected <<EOF && a/b/c abc - bam ++ bam bar foo j/k @@ -332,7 +332,7 @@ test_expect_success 'git branch with column.*' ' git config --unset column.branch && git config --unset column.ui && cat >expected <<\EOF && - a/b/c bam foo l * master n o/p r + a/b/c + bam foo l * master n o/p r abc bar j/k m/m master2 o/o q EOF test_cmp expected actual @@ -349,7 +349,7 @@ test_expect_success 'git branch -v with column.ui ignored' ' cat >expected <<\EOF && a/b/c abc - bam ++ bam bar foo j/k diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh index ee6787614c..94ab05ad59 100755 --- a/t/t3203-branch-output.sh +++ b/t/t3203-branch-output.sh @@ -240,6 +240,27 @@ test_expect_success 'git branch --format option' ' test_i18ncmp expect actual ' +test_expect_success '"add" a worktree' ' + mkdir worktree_dir && + git worktree add -b master_worktree worktree_dir master +' + +cat >expect <<'EOF' +* <GREEN>(HEAD detached from fromtag)<RESET> + ambiguous<RESET> + branch-one<RESET> + branch-two<RESET> + master<RESET> ++ <CYAN>master_worktree<RESET> + ref-to-branch<RESET> -> branch-one + ref-to-remote<RESET> -> origin/branch-one +EOF +test_expect_success TTY 'worktree colors correct' ' + test_terminal git branch >actual.raw && + test_decode_color <actual.raw >actual && + test_cmp expect actual +' + test_expect_success "set up color tests" ' echo "<RED>master<RESET>" >expect.color && echo "master" >expect.bare && -- 2.14.2 ^ permalink raw reply related [flat|nested] 136+ messages in thread
* Re: [PATCH v8 2/3] branch: update output to include worktree info 2019-02-19 8:31 ` [PATCH v8 2/3] branch: update output to include worktree info nbelakovski @ 2019-02-21 12:44 ` Jeff King 2019-03-14 5:45 ` Nickolai Belakovski 0 siblings, 1 reply; 136+ messages in thread From: Jeff King @ 2019-02-21 12:44 UTC (permalink / raw) To: nbelakovski; +Cc: git, rafa.almas, gitster, avarab On Tue, Feb 19, 2019 at 05:31:22PM +0900, nbelakovski@gmail.com wrote: > From: Nickolai Belakovski <nbelakovski@gmail.com> > > The output of git branch is modified to mark branches checkout out in a s/checkout out/checked out/ ? > linked worktree with a "+" and color them in cyan (in contrast to the > current branch, which will still be denoted with a "*" and colored in green) > > This is meant to communicate to the user that the branches that are > marked or colored will behave differently from other branches if the user > attempts to check them out or delete them, since branches checked out in > another worktree cannot be checked out or deleted. I think this makes sense to have. You cannot "git checkout" such a marked branch (since it would conflict with the other worktree), and that alone seems to make it worth marking in the output. > diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt > index 3bd83a7cbd..f2e5a07d64 100644 > --- a/Documentation/git-branch.txt > +++ b/Documentation/git-branch.txt > @@ -26,8 +26,10 @@ DESCRIPTION > ----------- > > If `--list` is given, or if there are no non-option arguments, existing > -branches are listed; the current branch will be highlighted with an > -asterisk. Option `-r` causes the remote-tracking branches to be listed, > +branches are listed; the current branch will be highlighted in green and > +marked with an asterisk. Any branches checked out in linked worktrees will > +be highlighted in cyan and marked with a plus sign. Option `-r` causes the > +remote-tracking branches to be listed, This makes sense to me. > - strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else) %s%%(end)", > - branch_get_color(BRANCH_COLOR_CURRENT), > - branch_get_color(BRANCH_COLOR_LOCAL)); > + strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else)%%(if)%%(worktreepath)%%(then)+ %s%%(else) %s%%(end)%%(end)", > + branch_get_color(BRANCH_COLOR_CURRENT), > + branch_get_color(BRANCH_COLOR_WORKTREE), > + branch_get_color(BRANCH_COLOR_LOCAL)); Makes sense. The long line is ugly. Our format does not support breaking long lines, though we could break the C string, I think, like: strbuf_add(&local, "%%(if)%%(HEAD)" "%%(then)* %s" "%%(else)%(if)%%(worktreepath)" "%%(then)+ %s" "%%(else)" "%%(then) %s" "%%(end)%%(end)"); That's pretty ugly, too, but it at least shows the conditional structure. > diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh > index ee6787614c..94ab05ad59 100755 > --- a/t/t3203-branch-output.sh > +++ b/t/t3203-branch-output.sh > @@ -240,6 +240,27 @@ test_expect_success 'git branch --format option' ' > test_i18ncmp expect actual > ' > > +test_expect_success '"add" a worktree' ' > + mkdir worktree_dir && > + git worktree add -b master_worktree worktree_dir master > +' This mkdir gets deleted in the next patch. It should just not be added here. > +cat >expect <<'EOF' > +* <GREEN>(HEAD detached from fromtag)<RESET> > + ambiguous<RESET> > + branch-one<RESET> > + branch-two<RESET> > + master<RESET> > ++ <CYAN>master_worktree<RESET> > + ref-to-branch<RESET> -> branch-one > + ref-to-remote<RESET> -> origin/branch-one > +EOF > +test_expect_success TTY 'worktree colors correct' ' > + test_terminal git branch >actual.raw && > + test_decode_color <actual.raw >actual && > + test_cmp expect actual > +' We are not testing the auto-color behavior here, so I think you could just use "git branch --color >actual.raw" and drop the TTY prerequisite. That's shorter and simpler, and will let your test run on more platforms. -Peff ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v8 2/3] branch: update output to include worktree info 2019-02-21 12:44 ` Jeff King @ 2019-03-14 5:45 ` Nickolai Belakovski 0 siblings, 0 replies; 136+ messages in thread From: Nickolai Belakovski @ 2019-03-14 5:45 UTC (permalink / raw) To: Jeff King Cc: Git List, Rafael Ascensão, Junio C Hamano, Ævar Arnfjörð Bjarmason On Thu, Feb 21, 2019 at 4:44 AM Jeff King <peff@peff.net> wrote: > > On Tue, Feb 19, 2019 at 05:31:22PM +0900, nbelakovski@gmail.com wrote: > > > From: Nickolai Belakovski <nbelakovski@gmail.com> > > > > The output of git branch is modified to mark branches checkout out in a > > s/checkout out/checked out/ ? > Yes, thanks > > > - strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else) %s%%(end)", > > - branch_get_color(BRANCH_COLOR_CURRENT), > > - branch_get_color(BRANCH_COLOR_LOCAL)); > > + strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else)%%(if)%%(worktreepath)%%(then)+ %s%%(else) %s%%(end)%%(end)", > > + branch_get_color(BRANCH_COLOR_CURRENT), > > + branch_get_color(BRANCH_COLOR_WORKTREE), > > + branch_get_color(BRANCH_COLOR_LOCAL)); > > Makes sense. The long line is ugly. Our format does not support breaking > long lines, though we could break the C string, I think, like: > > strbuf_add(&local, > "%%(if)%%(HEAD)" > "%%(then)* %s" > "%%(else)%(if)%%(worktreepath)" > "%%(then)+ %s" > "%%(else)" > "%%(then) %s" > "%%(end)%%(end)"); > > That's pretty ugly, too, but it at least shows the conditional > structure. True, but other lines within that file are about as long. I'd feel that I should make all of them reflect the conditional structure if I'm going to make one of them reflect it. Granted none of the others have nested if's, but personally I think it's OK as is. The nested if is short enough. > > > > > +test_expect_success '"add" a worktree' ' > > + mkdir worktree_dir && > > + git worktree add -b master_worktree worktree_dir master > > +' > > This mkdir gets deleted in the next patch. It should just not be added > here. Whoops, removed > > > +cat >expect <<'EOF' > > +* <GREEN>(HEAD detached from fromtag)<RESET> > > + ambiguous<RESET> > > + branch-one<RESET> > > + branch-two<RESET> > > + master<RESET> > > ++ <CYAN>master_worktree<RESET> > > + ref-to-branch<RESET> -> branch-one > > + ref-to-remote<RESET> -> origin/branch-one > > +EOF > > +test_expect_success TTY 'worktree colors correct' ' > > + test_terminal git branch >actual.raw && > > + test_decode_color <actual.raw >actual && > > + test_cmp expect actual > > +' > > We are not testing the auto-color behavior here, so I think you could > just use "git branch --color >actual.raw" and drop the TTY prerequisite. > That's shorter and simpler, and will let your test run on more > platforms. > Done locally, will be part of v9 > -Peff ^ permalink raw reply [flat|nested] 136+ messages in thread
* [PATCH v8 3/3] branch: add worktree info on verbose output 2019-02-19 8:31 ` [PATCH v8 0/3] nbelakovski 2019-02-19 8:31 ` [PATCH v8 1/3] ref-filter: add worktreepath atom nbelakovski 2019-02-19 8:31 ` [PATCH v8 2/3] branch: update output to include worktree info nbelakovski @ 2019-02-19 8:31 ` nbelakovski 2019-02-21 12:59 ` Jeff King 2019-02-21 12:36 ` [PATCH v8 0/3] Jeff King 3 siblings, 1 reply; 136+ messages in thread From: nbelakovski @ 2019-02-19 8:31 UTC (permalink / raw) To: git; +Cc: peff, rafa.almas, gitster, avarab, Nickolai Belakovski From: Nickolai Belakovski <nbelakovski@gmail.com> To display worktree path for refs checked out in a linked worktree Signed-off-by: Nickolai Belakovski <nbelakovski@gmail.com> --- Documentation/git-branch.txt | 6 ++++-- builtin/branch.c | 4 ++++ t/t3203-branch-output.sh | 21 ++++++++++++++++++++- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index f2e5a07d64..326a45f648 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -168,8 +168,10 @@ This option is only applicable in non-verbose mode. When in list mode, show sha1 and commit subject line for each head, along with relationship to upstream branch (if any). If given twice, print - the name of the upstream branch, as well (see also `git remote - show <remote>`). + the path of the linked worktree, if applicable (not applicable + for current worktree since user's path will already be in current + worktree) and the name of the upstream branch, as well (see also + `git remote show <remote>`). -q:: --quiet:: diff --git a/builtin/branch.c b/builtin/branch.c index c2a86362bb..0b8ba9e4c5 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -367,9 +367,13 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r strbuf_addf(&local, " %s ", obname.buf); if (filter->verbose > 1) + { + strbuf_addf(&local, "%%(if:notequals=*)%%(HEAD)%%(then)%%(if)%%(worktreepath)%%(then)(%s%%(worktreepath)%s) %%(end)%%(end)", + branch_get_color(BRANCH_COLOR_WORKTREE), branch_get_color(BRANCH_COLOR_RESET)); strbuf_addf(&local, "%%(if)%%(upstream)%%(then)[%s%%(upstream:short)%s%%(if)%%(upstream:track)" "%%(then): %%(upstream:track,nobracket)%%(end)] %%(end)%%(contents:subject)", branch_get_color(BRANCH_COLOR_UPSTREAM), branch_get_color(BRANCH_COLOR_RESET)); + } else strbuf_addf(&local, "%%(if)%%(upstream:track)%%(then)%%(upstream:track) %%(end)%%(contents:subject)"); diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh index 94ab05ad59..012ddde7f2 100755 --- a/t/t3203-branch-output.sh +++ b/t/t3203-branch-output.sh @@ -241,7 +241,6 @@ test_expect_success 'git branch --format option' ' ' test_expect_success '"add" a worktree' ' - mkdir worktree_dir && git worktree add -b master_worktree worktree_dir master ' @@ -285,4 +284,24 @@ test_expect_success '--color overrides auto-color' ' test_cmp expect.color actual ' +# This test case has some special code to strip the first 30 characters or so +# of the output so that we do not have to put commit hashes into the expect +test_expect_success 'verbose output lists worktree path' ' + cat >expect <<-EOF && + one + one + two + one + two + ($(pwd)/worktree_dir) two + two + two + EOF + git branch -vv >tmp && + SUBSTRLENGTH=$(head -1 tmp | awk "{print index(\$0, \"one\")}") && + awk -v substrlength="$SUBSTRLENGTH" "{print substr(\$0,substrlength,length(\$0))}" <tmp >actual && + test_cmp expect actual +' + + test_done -- 2.14.2 ^ permalink raw reply related [flat|nested] 136+ messages in thread
* Re: [PATCH v8 3/3] branch: add worktree info on verbose output 2019-02-19 8:31 ` [PATCH v8 3/3] branch: add worktree info on verbose output nbelakovski @ 2019-02-21 12:59 ` Jeff King 2019-03-14 5:58 ` Nickolai Belakovski 0 siblings, 1 reply; 136+ messages in thread From: Jeff King @ 2019-02-21 12:59 UTC (permalink / raw) To: nbelakovski; +Cc: git, rafa.almas, gitster, avarab On Tue, Feb 19, 2019 at 05:31:23PM +0900, nbelakovski@gmail.com wrote: > From: Nickolai Belakovski <nbelakovski@gmail.com> > > To display worktree path for refs checked out in a linked worktree This would be a good place to describe why this is useful. :) I do not have an opinion myself. Patch 2 makes a lot of sense to me, but I don't know if people would like this one or not. I don't use "-v" myself, though, so what do I know. :) > diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt > index f2e5a07d64..326a45f648 100644 > --- a/Documentation/git-branch.txt > +++ b/Documentation/git-branch.txt > @@ -168,8 +168,10 @@ This option is only applicable in non-verbose mode. > When in list mode, > show sha1 and commit subject line for each head, along with > relationship to upstream branch (if any). If given twice, print > - the name of the upstream branch, as well (see also `git remote > - show <remote>`). > + the path of the linked worktree, if applicable (not applicable > + for current worktree since user's path will already be in current > + worktree) and the name of the upstream branch, as well (see also > + `git remote show <remote>`). That parenthetical feels a bit awkward. Maybe: ...print the path of the linked worktree (if any) and the name of the upstream branch, as well (see also `git remote show <remote>`). Note that the current worktree's HEAD will not have its path printed (it will always be your current directory). > diff --git a/builtin/branch.c b/builtin/branch.c > index c2a86362bb..0b8ba9e4c5 100644 > --- a/builtin/branch.c > +++ b/builtin/branch.c > @@ -367,9 +367,13 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r > strbuf_addf(&local, " %s ", obname.buf); > > if (filter->verbose > 1) > + { > + strbuf_addf(&local, "%%(if:notequals=*)%%(HEAD)%%(then)%%(if)%%(worktreepath)%%(then)(%s%%(worktreepath)%s) %%(end)%%(end)", > + branch_get_color(BRANCH_COLOR_WORKTREE), branch_get_color(BRANCH_COLOR_RESET)); > strbuf_addf(&local, "%%(if)%%(upstream)%%(then)[%s%%(upstream:short)%s%%(if)%%(upstream:track)" > "%%(then): %%(upstream:track,nobracket)%%(end)] %%(end)%%(contents:subject)", > branch_get_color(BRANCH_COLOR_UPSTREAM), branch_get_color(BRANCH_COLOR_RESET)); > + } Another unreadable long line (both the one you're adding, and the existing one!). I don't know if it's worth trying to clean these up, but if we do, it might be worth hitting the existing ones, too. I'm OK if that comes as a patch on top later on, though. > diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh > index 94ab05ad59..012ddde7f2 100755 > --- a/t/t3203-branch-output.sh > +++ b/t/t3203-branch-output.sh > @@ -241,7 +241,6 @@ test_expect_success 'git branch --format option' ' > ' > > test_expect_success '"add" a worktree' ' > - mkdir worktree_dir && > git worktree add -b master_worktree worktree_dir master > ' Here's that mysterious mkdir going away. :) > @@ -285,4 +284,24 @@ test_expect_success '--color overrides auto-color' ' > test_cmp expect.color actual > ' > > +# This test case has some special code to strip the first 30 characters or so > +# of the output so that we do not have to put commit hashes into the expect > +test_expect_success 'verbose output lists worktree path' ' > + cat >expect <<-EOF && > + one > + one > + two > + one > + two > + ($(pwd)/worktree_dir) two > + two > + two > + EOF > + git branch -vv >tmp && > + SUBSTRLENGTH=$(head -1 tmp | awk "{print index(\$0, \"one\")}") && > + awk -v substrlength="$SUBSTRLENGTH" "{print substr(\$0,substrlength,length(\$0))}" <tmp >actual && > + test_cmp expect actual > +' It's hard to tell if this awk is doing the right thing. I guess it works because git-branch tries to line up all of the hashes. I think the result might be easier to verify if we simply blanked the hashes. Unfortunately the output is actually pretty hard to parse. Since there are only two tip commits, perhaps it would not be so bad to just do something like this: diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh index 012ddde7f2..8065279be6 100755 --- a/t/t3203-branch-output.sh +++ b/t/t3203-branch-output.sh @@ -284,22 +284,20 @@ test_expect_success '--color overrides auto-color' ' test_cmp expect.color actual ' -# This test case has some special code to strip the first 30 characters or so -# of the output so that we do not have to put commit hashes into the expect test_expect_success 'verbose output lists worktree path' ' + one=$(git rev-parse --short HEAD) && + two=$(git rev-parse --short master) && cat >expect <<-EOF && - one - one - two - one - two - ($(pwd)/worktree_dir) two - two - two + * (HEAD detached from fromtag) $one one + ambiguous $one one + branch-one $two two + branch-two $one one + master $two two + + master_worktree $two ($(pwd)/worktree_dir) two + ref-to-branch $two two + ref-to-remote $two two EOF - git branch -vv >tmp && - SUBSTRLENGTH=$(head -1 tmp | awk "{print index(\$0, \"one\")}") && - awk -v substrlength="$SUBSTRLENGTH" "{print substr(\$0,substrlength,length(\$0))}" <tmp >actual && + git branch -vv >actual && test_cmp expect actual ' I don't like how it depends on the space alignment of the branches, but I do like that you can clearly see which branch is being annotated. -Peff ^ permalink raw reply related [flat|nested] 136+ messages in thread
* Re: [PATCH v8 3/3] branch: add worktree info on verbose output 2019-02-21 12:59 ` Jeff King @ 2019-03-14 5:58 ` Nickolai Belakovski 2019-03-18 2:12 ` Junio C Hamano 0 siblings, 1 reply; 136+ messages in thread From: Nickolai Belakovski @ 2019-03-14 5:58 UTC (permalink / raw) To: Jeff King Cc: Git List, Rafael Ascensão, Junio C Hamano, Ævar Arnfjörð Bjarmason On Thu, Feb 21, 2019 at 4:59 AM Jeff King <peff@peff.net> wrote: > > On Tue, Feb 19, 2019 at 05:31:23PM +0900, nbelakovski@gmail.com wrote: > > > From: Nickolai Belakovski <nbelakovski@gmail.com> > > > > To display worktree path for refs checked out in a linked worktree > > This would be a good place to describe why this is useful. :) > > I do not have an opinion myself. Patch 2 makes a lot of sense to me, but > I don't know if people would like this one or not. I don't use "-v" > myself, though, so what do I know. :) I threw this one in because I thought it wouldn't be clear to the average user why some branches are in cyan. By putting the worktree path in cyan on the next level of output I thought this would help the user make the connection, but actually I don't have strong feelings about this one. > > > diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt > > index f2e5a07d64..326a45f648 100644 > > --- a/Documentation/git-branch.txt > > +++ b/Documentation/git-branch.txt > > @@ -168,8 +168,10 @@ This option is only applicable in non-verbose mode. > > When in list mode, > > show sha1 and commit subject line for each head, along with > > relationship to upstream branch (if any). If given twice, print > > - the name of the upstream branch, as well (see also `git remote > > - show <remote>`). > > + the path of the linked worktree, if applicable (not applicable > > + for current worktree since user's path will already be in current > > + worktree) and the name of the upstream branch, as well (see also > > + `git remote show <remote>`). > > That parenthetical feels a bit awkward. Maybe: > > ...print the path of the linked worktree (if any) and the name of the > upstream branch, as well (see also `git remote show <remote>`). Note > that the current worktree's HEAD will not have its path printed (it > will always be your current directory). Sure I can make that change > > > diff --git a/builtin/branch.c b/builtin/branch.c > > index c2a86362bb..0b8ba9e4c5 100644 > > --- a/builtin/branch.c > > +++ b/builtin/branch.c > > @@ -367,9 +367,13 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r > > strbuf_addf(&local, " %s ", obname.buf); > > > > if (filter->verbose > 1) > > + { > > + strbuf_addf(&local, "%%(if:notequals=*)%%(HEAD)%%(then)%%(if)%%(worktreepath)%%(then)(%s%%(worktreepath)%s) %%(end)%%(end)", > > + branch_get_color(BRANCH_COLOR_WORKTREE), branch_get_color(BRANCH_COLOR_RESET)); > > strbuf_addf(&local, "%%(if)%%(upstream)%%(then)[%s%%(upstream:short)%s%%(if)%%(upstream:track)" > > "%%(then): %%(upstream:track,nobracket)%%(end)] %%(end)%%(contents:subject)", > > branch_get_color(BRANCH_COLOR_UPSTREAM), branch_get_color(BRANCH_COLOR_RESET)); > > + } > > Another unreadable long line (both the one you're adding, and the existing > one!). I don't know if it's worth trying to clean these up, but if we > do, it might be worth hitting the existing ones, too. > > I'm OK if that comes as a patch on top later on, though. Agreed, but there's enough lines like this that it'll just look inconsistent if only one were broken up. > > > diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh > index 012ddde7f2..8065279be6 100755 > --- a/t/t3203-branch-output.sh > +++ b/t/t3203-branch-output.sh > @@ -284,22 +284,20 @@ test_expect_success '--color overrides auto-color' ' > test_cmp expect.color actual > ' > > -# This test case has some special code to strip the first 30 characters or so > -# of the output so that we do not have to put commit hashes into the expect > test_expect_success 'verbose output lists worktree path' ' > + one=$(git rev-parse --short HEAD) && > + two=$(git rev-parse --short master) && > cat >expect <<-EOF && > - one > - one > - two > - one > - two > - ($(pwd)/worktree_dir) two > - two > - two > + * (HEAD detached from fromtag) $one one > + ambiguous $one one > + branch-one $two two > + branch-two $one one > + master $two two > + + master_worktree $two ($(pwd)/worktree_dir) two > + ref-to-branch $two two > + ref-to-remote $two two > EOF > - git branch -vv >tmp && > - SUBSTRLENGTH=$(head -1 tmp | awk "{print index(\$0, \"one\")}") && > - awk -v substrlength="$SUBSTRLENGTH" "{print substr(\$0,substrlength,length(\$0))}" <tmp >actual && > + git branch -vv >actual && > test_cmp expect actual > ' > > > I don't like how it depends on the space alignment of the branches, but > I do like that you can clearly see which branch is being annotated. Thanks for the suggestion. While I'm kinda proud of my awk thing, I think yours is a lot easier to read. Will add. > > -Peff ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v8 3/3] branch: add worktree info on verbose output 2019-03-14 5:58 ` Nickolai Belakovski @ 2019-03-18 2:12 ` Junio C Hamano 0 siblings, 0 replies; 136+ messages in thread From: Junio C Hamano @ 2019-03-18 2:12 UTC (permalink / raw) To: Nickolai Belakovski Cc: Jeff King, Git List, Rafael Ascensão, Ævar Arnfjörð Bjarmason Nickolai Belakovski <nbelakovski@gmail.com> writes: > On Thu, Feb 21, 2019 at 4:59 AM Jeff King <peff@peff.net> wrote: >> >> On Tue, Feb 19, 2019 at 05:31:23PM +0900, nbelakovski@gmail.com wrote: >> >> > From: Nickolai Belakovski <nbelakovski@gmail.com> >> > >> > To display worktree path for refs checked out in a linked worktree >> >> This would be a good place to describe why this is useful. :) >> >> I do not have an opinion myself. Patch 2 makes a lot of sense to me, but >> I don't know if people would like this one or not. I don't use "-v" >> myself, though, so what do I know. :) > I threw this one in because I thought it wouldn't be clear to the > average user why some > branches are in cyan. By putting the worktree path in cyan on the next > level of output > I thought this would help the user make the connection, but actually I > don't have strong > feelings about this one. I am moderately skeptical on 2/3, but together with 3/3 I think it makes sense. The fact that two branch names painted in different colors from the other ones, without knowing which one is checked out in what worktree, will not give enough information to allow the user to actually start working on one of them. All the user gets with 2/3 alone is "don't touch it---it is used elsewhere and I am not telling where", isn't it? ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v8 0/3] 2019-02-19 8:31 ` [PATCH v8 0/3] nbelakovski ` (2 preceding siblings ...) 2019-02-19 8:31 ` [PATCH v8 3/3] branch: add worktree info on verbose output nbelakovski @ 2019-02-21 12:36 ` Jeff King 2019-03-14 6:10 ` Nickolai Belakovski 3 siblings, 1 reply; 136+ messages in thread From: Jeff King @ 2019-02-21 12:36 UTC (permalink / raw) To: nbelakovski; +Cc: git, rafa.almas, gitster, avarab On Tue, Feb 19, 2019 at 05:31:20PM +0900, nbelakovski@gmail.com wrote: > From: Nickolai Belakovski <nbelakovski@gmail.com> > > I've made the various cosmetic changes that were suggested, as well as adding tests for 3/3 > > I don't have a particularly strong opinion on the subject of keeping the atom as "worktreepath" > or changing it to "worktree:path". We did feel earlier in this thread that if we went with > "worktree:path", then "worktree" is somewhat ambiguous, and that discussion led to deciding to > have "worktree" return the path,. After that I chose to name it "worktreepath" because I like to > make things explicit and intuitive. I am OK with it either way. We have used ":" for some variants (e.g., objectsize:disk). But we have also used long single names with related prefixes (e.g., objectname versus objecttype versus objectsize). Patch 1 looks good to me. Given that we're on v8 and most of the other comments are for patches 2 and 3, I think we might consider graduating it separately if the other two are not ready soon. It's independently useful, IMHO. I have a few comments on the others which I'll leave as replies there. -Peff ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v8 0/3] 2019-02-21 12:36 ` [PATCH v8 0/3] Jeff King @ 2019-03-14 6:10 ` Nickolai Belakovski 0 siblings, 0 replies; 136+ messages in thread From: Nickolai Belakovski @ 2019-03-14 6:10 UTC (permalink / raw) To: Jeff King Cc: Git List, Rafael Ascensão, Junio C Hamano, Ævar Arnfjörð Bjarmason > > Patch 1 looks good to me. Given that we're on v8 and most of the other > comments are for patches 2 and 3, I think we might consider graduating > it separately if the other two are not ready soon. It's independently > useful, IMHO. Patch 2 was my main motivation, so it would be nice to get it in together with 1 :) Patch 3, like I said in the thread on that one I don't have strong feeling about it. It was an attempt to provide a connection between the new cyan output and its intent, as opposed to having the user guess, but I think anyone who's using a worktree will figure it out sooner or later, and anyone not using a worktree will be unaffected. I'm willing to keep going with comments on patch 2. I can't imagine it would take many more revisions as it's much more straightforward than patch 1, it's basically just modifying one line of branch.c. If we decide to drop patch 3 fine with me. I'll send in v9 with the latest changes for both 2 and 3 sometime tomorrow unless I hear otherwise. Thanks for the feedback. ^ permalink raw reply [flat|nested] 136+ messages in thread
* [PATCH v9 0/3] 2018-09-27 15:13 ` [PATCH] branch: colorize branches checked out in a linked working tree the same way as the current branch is colorized Nickolai Belakovski ` (9 preceding siblings ...) 2019-02-19 8:31 ` [PATCH v8 0/3] nbelakovski @ 2019-03-16 1:38 ` nbelakovski 2019-03-16 1:38 ` [PATCH v9 1/3] ref-filter: add worktreepath atom nbelakovski ` (3 more replies) 2019-04-29 5:19 ` [PATCH v10 0/3] nbelakovski 11 siblings, 4 replies; 136+ messages in thread From: nbelakovski @ 2019-03-16 1:38 UTC (permalink / raw) To: git; +Cc: peff, gitster, rafa.almas, avarab, Nickolai Belakovski From: Nickolai Belakovski <nbelakovski@gmail.com> Cleanup on 2/3 and 3/3 To reiterate from elsewhere in the thread, I'd really like to get 1/3 and 2/3 in together. For 3/3 I'm basically indifferent. I see some value in it, but I also think it clutters up the verbose output, so I could understand if there's a lack of interest in it. Also, I changed my strategy for how I updated tests that were impacted by these changes. Instead of updating the impacted tests with the new expected output, I went back and made various tests more self-contained. This seemed like a more sensible strategy from the standpoint of decoupling tests and limiting scope of tests. Travis-CI results: https://travis-ci.org/nbelakovski/git/builds/506853143 Nickolai Belakovski (3): ref-filter: add worktreepath atom branch: update output to include worktree info branch: add worktree info on verbose output Documentation/git-branch.txt | 12 ++++-- Documentation/git-for-each-ref.txt | 5 +++ builtin/branch.c | 16 ++++++-- ref-filter.c | 78 ++++++++++++++++++++++++++++++++++++++ t/t3200-branch.sh | 16 +++++--- t/t3203-branch-output.sh | 43 ++++++++++++++++++++- t/t6302-for-each-ref-filter.sh | 13 +++++++ 7 files changed, 168 insertions(+), 15 deletions(-) -- 2.14.2 ^ permalink raw reply [flat|nested] 136+ messages in thread
* [PATCH v9 1/3] ref-filter: add worktreepath atom 2019-03-16 1:38 ` [PATCH v9 0/3] nbelakovski @ 2019-03-16 1:38 ` nbelakovski 2019-03-16 1:38 ` [PATCH v9 2/3] branch: update output to include worktree info nbelakovski ` (2 subsequent siblings) 3 siblings, 0 replies; 136+ messages in thread From: nbelakovski @ 2019-03-16 1:38 UTC (permalink / raw) To: git; +Cc: peff, gitster, rafa.almas, avarab, Nickolai Belakovski From: Nickolai Belakovski <nbelakovski@gmail.com> Add an atom providing the path of the linked worktree where this ref is checked out, if it is checked out in any linked worktrees, and empty string otherwise. Signed-off-by: Nickolai Belakovski <nbelakovski@gmail.com> --- Documentation/git-for-each-ref.txt | 5 +++ ref-filter.c | 78 ++++++++++++++++++++++++++++++++++++++ t/t6302-for-each-ref-filter.sh | 13 +++++++ 3 files changed, 96 insertions(+) diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt index 774cecc7ed..6dcd39f6f6 100644 --- a/Documentation/git-for-each-ref.txt +++ b/Documentation/git-for-each-ref.txt @@ -214,6 +214,11 @@ symref:: `:lstrip` and `:rstrip` options in the same way as `refname` above. +worktreepath:: + The absolute path to the worktree in which the ref is checked + out, if it is checked out in any linked worktree. Empty string + otherwise. + In addition to the above, for commit and tag objects, the header field names (`tree`, `parent`, `object`, `type`, and `tag`) can be used to specify the value in the header field. diff --git a/ref-filter.c b/ref-filter.c index 3aca105307..79cfec914a 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -20,6 +20,8 @@ #include "commit-slab.h" #include "commit-graph.h" #include "commit-reach.h" +#include "worktree.h" +#include "hashmap.h" static struct ref_msg { const char *gone; @@ -75,6 +77,27 @@ static struct expand_data { struct object_info info; } oi, oi_deref; +struct ref_to_worktree_entry { + struct hashmap_entry ent; /* must be the first member! */ + struct worktree *wt; /* key is wt->head_ref */ +}; + +static int ref_to_worktree_map_cmpfnc(const void *unused_lookupdata, + const void *existing_hashmap_entry_to_test, + const void *key, + const void *keydata_aka_refname) +{ + const struct ref_to_worktree_entry *e = existing_hashmap_entry_to_test; + const struct ref_to_worktree_entry *k = key; + return strcmp(e->wt->head_ref, + keydata_aka_refname ? keydata_aka_refname : k->wt->head_ref); +} + +static struct ref_to_worktree_map { + struct hashmap map; + struct worktree **worktrees; +} ref_to_worktree_map; + /* * An atom is a valid field atom listed below, possibly prefixed with * a "*" to denote deref_tag(). @@ -480,6 +503,7 @@ static struct { { "flag", SOURCE_NONE }, { "HEAD", SOURCE_NONE, FIELD_STR, head_atom_parser }, { "color", SOURCE_NONE, FIELD_STR, color_atom_parser }, + { "worktreepath", SOURCE_NONE }, { "align", SOURCE_NONE, FIELD_STR, align_atom_parser }, { "end", SOURCE_NONE }, { "if", SOURCE_NONE, FIELD_STR, if_atom_parser }, @@ -1529,6 +1553,48 @@ static int get_object(struct ref_array_item *ref, int deref, struct object **obj return 0; } +static void populate_worktree_map(struct hashmap *map, struct worktree **worktrees) +{ + int i; + + for (i = 0; worktrees[i]; i++) { + if (worktrees[i]->head_ref) { + struct ref_to_worktree_entry *entry; + entry = xmalloc(sizeof(*entry)); + entry->wt = worktrees[i]; + hashmap_entry_init(entry, strhash(worktrees[i]->head_ref)); + + hashmap_add(map, entry); + } + } +} + +static void lazy_init_worktree_map(void) +{ + if (ref_to_worktree_map.worktrees) + return; + + ref_to_worktree_map.worktrees = get_worktrees(0); + hashmap_init(&(ref_to_worktree_map.map), ref_to_worktree_map_cmpfnc, NULL, 0); + populate_worktree_map(&(ref_to_worktree_map.map), ref_to_worktree_map.worktrees); +} + +static char *get_worktree_path(const struct used_atom *atom, const struct ref_array_item *ref) +{ + struct hashmap_entry entry; + struct ref_to_worktree_entry *lookup_result; + + lazy_init_worktree_map(); + + hashmap_entry_init(&entry, strhash(ref->refname)); + lookup_result = hashmap_get(&(ref_to_worktree_map.map), &entry, ref->refname); + + if (lookup_result) + return xstrdup(lookup_result->wt->path); + else + return xstrdup(""); +} + /* * Parse the object referred by ref, and grab needed value. */ @@ -1566,6 +1632,13 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err) if (starts_with(name, "refname")) refname = get_refname(atom, ref); + else if (!strcmp(name, "worktreepath")) { + if (ref->kind == FILTER_REFS_BRANCHES) + v->s = get_worktree_path(atom, ref); + else + v->s = xstrdup(""); + continue; + } else if (starts_with(name, "symref")) refname = get_symref(atom, ref); else if (starts_with(name, "upstream")) { @@ -2049,6 +2122,11 @@ void ref_array_clear(struct ref_array *array) free_array_item(array->items[i]); FREE_AND_NULL(array->items); array->nr = array->alloc = 0; + if (ref_to_worktree_map.worktrees) { + hashmap_free(&(ref_to_worktree_map.map), 1); + free_worktrees(ref_to_worktree_map.worktrees); + ref_to_worktree_map.worktrees = NULL; + } } static void do_merge_filter(struct ref_filter_cbdata *ref_cbdata) diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh index fc067ed672..35408d53fd 100755 --- a/t/t6302-for-each-ref-filter.sh +++ b/t/t6302-for-each-ref-filter.sh @@ -441,4 +441,17 @@ test_expect_success '--merged is incompatible with --no-merged' ' test_must_fail git for-each-ref --merged HEAD --no-merged HEAD ' +test_expect_success 'validate worktree atom' ' + cat >expect <<-EOF && + master: $(pwd) + master_worktree: $(pwd)/worktree_dir + side: not checked out + EOF + git worktree add -b master_worktree worktree_dir master && + git for-each-ref --format="%(refname:short): %(if)%(worktreepath)%(then)%(worktreepath)%(else)not checked out%(end)" refs/heads/ >actual && + rm -r worktree_dir && + git worktree prune && + test_cmp expect actual +' + test_done -- 2.14.2 ^ permalink raw reply related [flat|nested] 136+ messages in thread
* [PATCH v9 2/3] branch: update output to include worktree info 2019-03-16 1:38 ` [PATCH v9 0/3] nbelakovski 2019-03-16 1:38 ` [PATCH v9 1/3] ref-filter: add worktreepath atom nbelakovski @ 2019-03-16 1:38 ` nbelakovski 2019-03-16 1:38 ` [PATCH v9 3/3] branch: add worktree info on verbose output nbelakovski 2019-03-18 5:30 ` [PATCH v9 0/3] Junio C Hamano 3 siblings, 0 replies; 136+ messages in thread From: nbelakovski @ 2019-03-16 1:38 UTC (permalink / raw) To: git; +Cc: peff, gitster, rafa.almas, avarab, Nickolai Belakovski From: Nickolai Belakovski <nbelakovski@gmail.com> The output of git branch is modified to mark branches checked out in a linked worktree with a "+" and color them in cyan (in contrast to the current branch, which will still be denoted with a "*" and colored in green) This is meant to communicate to the user that the branches that are marked or colored will behave differently from other branches if the user attempts to check them out or delete them, since branches checked out in another worktree cannot be checked out or deleted. Signed-off-by: Nickolai Belakovski <nbelakovski@gmail.com> --- Documentation/git-branch.txt | 6 ++++-- builtin/branch.c | 12 ++++++++---- t/t3200-branch.sh | 16 +++++++++++----- t/t3203-branch-output.sh | 24 ++++++++++++++++++++++-- 4 files changed, 45 insertions(+), 13 deletions(-) diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index 0cd87ddeff..7d18dffc4b 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -26,8 +26,10 @@ DESCRIPTION ----------- If `--list` is given, or if there are no non-option arguments, existing -branches are listed; the current branch will be highlighted with an -asterisk. Option `-r` causes the remote-tracking branches to be listed, +branches are listed; the current branch will be highlighted in green and +marked with an asterisk. Any branches checked out in linked worktrees will +be highlighted in cyan and marked with a plus sign. Option `-r` causes the +remote-tracking branches to be listed, and option `-a` shows both local and remote branches. If a `<pattern>` is given, it is used as a shell wildcard to restrict the output to matching branches. If multiple patterns are given, a branch is shown if diff --git a/builtin/branch.c b/builtin/branch.c index 4c83055730..350b039063 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -47,6 +47,7 @@ static char branch_colors[][COLOR_MAXLEN] = { GIT_COLOR_NORMAL, /* LOCAL */ GIT_COLOR_GREEN, /* CURRENT */ GIT_COLOR_BLUE, /* UPSTREAM */ + GIT_COLOR_CYAN, /* WORKTREE */ }; enum color_branch { BRANCH_COLOR_RESET = 0, @@ -54,7 +55,8 @@ enum color_branch { BRANCH_COLOR_REMOTE = 2, BRANCH_COLOR_LOCAL = 3, BRANCH_COLOR_CURRENT = 4, - BRANCH_COLOR_UPSTREAM = 5 + BRANCH_COLOR_UPSTREAM = 5, + BRANCH_COLOR_WORKTREE = 6 }; static const char *color_branch_slots[] = { @@ -64,6 +66,7 @@ static const char *color_branch_slots[] = { [BRANCH_COLOR_LOCAL] = "local", [BRANCH_COLOR_CURRENT] = "current", [BRANCH_COLOR_UPSTREAM] = "upstream", + [BRANCH_COLOR_WORKTREE] = "worktree", }; static struct string_list output = STRING_LIST_INIT_DUP; @@ -342,9 +345,10 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r struct strbuf local = STRBUF_INIT; struct strbuf remote = STRBUF_INIT; - strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else) %s%%(end)", - branch_get_color(BRANCH_COLOR_CURRENT), - branch_get_color(BRANCH_COLOR_LOCAL)); + strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else)%%(if)%%(worktreepath)%%(then)+ %s%%(else) %s%%(end)%%(end)", + branch_get_color(BRANCH_COLOR_CURRENT), + branch_get_color(BRANCH_COLOR_WORKTREE), + branch_get_color(BRANCH_COLOR_LOCAL)); strbuf_addf(&remote, " %s", branch_get_color(BRANCH_COLOR_REMOTE)); diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index 478b82cf9b..88719cc02c 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -202,18 +202,22 @@ test_expect_success 'git branch -M baz bam should succeed when baz is checked ou git worktree add -f bazdir2 baz && git branch -M baz bam && test $(git -C bazdir rev-parse --abbrev-ref HEAD) = bam && - test $(git -C bazdir2 rev-parse --abbrev-ref HEAD) = bam + test $(git -C bazdir2 rev-parse --abbrev-ref HEAD) = bam && + rm -r bazdir bazdir2 && + git worktree prune ' test_expect_success 'git branch -M baz bam should succeed within a worktree in which baz is checked out' ' git checkout -b baz && - git worktree add -f bazdir3 baz && + git worktree add -f bazdir baz && ( - cd bazdir3 && + cd bazdir && git branch -M baz bam && test $(git rev-parse --abbrev-ref HEAD) = bam ) && - test $(git rev-parse --abbrev-ref HEAD) = bam + test $(git rev-parse --abbrev-ref HEAD) = bam && + rm -r bazdir && + git worktree prune ' test_expect_success 'git branch -M master should work when master is checked out' ' @@ -774,7 +778,9 @@ test_expect_success 'test deleting branch without config' ' test_expect_success 'deleting currently checked out branch fails' ' git worktree add -b my7 my7 && test_must_fail git -C my7 branch -d my7 && - test_must_fail git branch -d my7 + test_must_fail git branch -d my7 && + rm -r my7 && + git worktree prune ' test_expect_success 'test --track without .fetch entries' ' diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh index be55148930..ceb74e0826 100755 --- a/t/t3203-branch-output.sh +++ b/t/t3203-branch-output.sh @@ -136,11 +136,13 @@ test_expect_success 'git branch `--show-current` works properly with worktrees' branch-two EOF git checkout branch-one && - git worktree add worktree branch-two && + git worktree add worktree_dir branch-two && { git branch --show-current && - git -C worktree branch --show-current + git -C worktree_dir branch --show-current } >actual && + rm -r worktree_dir && + git worktree prune && test_cmp expect actual ' @@ -284,6 +286,24 @@ test_expect_success 'git branch --format option' ' test_i18ncmp expect actual ' +test_expect_success 'worktree colors correct' ' + cat >expect <<-EOF && + * <GREEN>(HEAD detached from fromtag)<RESET> + ambiguous<RESET> + branch-one<RESET> + + <CYAN>branch-two<RESET> + master<RESET> + ref-to-branch<RESET> -> branch-one + ref-to-remote<RESET> -> origin/branch-one + EOF + git worktree add worktree_dir branch-two && + git branch --color >actual.raw && + rm -r worktree_dir && + git worktree prune && + test_decode_color <actual.raw >actual && + test_cmp expect actual +' + test_expect_success "set up color tests" ' echo "<RED>master<RESET>" >expect.color && echo "master" >expect.bare && -- 2.14.2 ^ permalink raw reply related [flat|nested] 136+ messages in thread
* [PATCH v9 3/3] branch: add worktree info on verbose output 2019-03-16 1:38 ` [PATCH v9 0/3] nbelakovski 2019-03-16 1:38 ` [PATCH v9 1/3] ref-filter: add worktreepath atom nbelakovski 2019-03-16 1:38 ` [PATCH v9 2/3] branch: update output to include worktree info nbelakovski @ 2019-03-16 1:38 ` nbelakovski 2019-03-18 12:10 ` SZEDER Gábor 2019-03-18 5:30 ` [PATCH v9 0/3] Junio C Hamano 3 siblings, 1 reply; 136+ messages in thread From: nbelakovski @ 2019-03-16 1:38 UTC (permalink / raw) To: git; +Cc: peff, gitster, rafa.almas, avarab, Nickolai Belakovski From: Nickolai Belakovski <nbelakovski@gmail.com> To display worktree path for refs checked out in a linked worktree Signed-off-by: Nickolai Belakovski <nbelakovski@gmail.com> --- Documentation/git-branch.txt | 6 ++++-- builtin/branch.c | 4 ++++ t/t3203-branch-output.sh | 19 +++++++++++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index 7d18dffc4b..d11d00583a 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -172,8 +172,10 @@ This option is only applicable in non-verbose mode. When in list mode, show sha1 and commit subject line for each head, along with relationship to upstream branch (if any). If given twice, print - the name of the upstream branch, as well (see also `git remote - show <remote>`). + the path of the linked worktree (if any) and the name of the upstream + branch, as well (see also `git remote show <remote>`). Note that the + current worktree's HEAD will not have its path printed (it will always + be your current directory). -q:: --quiet:: diff --git a/builtin/branch.c b/builtin/branch.c index 350b039063..3d1872babc 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -367,9 +367,13 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r strbuf_addf(&local, " %s ", obname.buf); if (filter->verbose > 1) + { + strbuf_addf(&local, "%%(if:notequals=*)%%(HEAD)%%(then)%%(if)%%(worktreepath)%%(then)(%s%%(worktreepath)%s) %%(end)%%(end)", + branch_get_color(BRANCH_COLOR_WORKTREE), branch_get_color(BRANCH_COLOR_RESET)); strbuf_addf(&local, "%%(if)%%(upstream)%%(then)[%s%%(upstream:short)%s%%(if)%%(upstream:track)" "%%(then): %%(upstream:track,nobracket)%%(end)] %%(end)%%(contents:subject)", branch_get_color(BRANCH_COLOR_UPSTREAM), branch_get_color(BRANCH_COLOR_RESET)); + } else strbuf_addf(&local, "%%(if)%%(upstream:track)%%(then)%%(upstream:track) %%(end)%%(contents:subject)"); diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh index ceb74e0826..e5dc23e225 100755 --- a/t/t3203-branch-output.sh +++ b/t/t3203-branch-output.sh @@ -328,4 +328,23 @@ test_expect_success '--color overrides auto-color' ' test_cmp expect.color actual ' +test_expect_success 'verbose output lists worktree path' ' + one=$(git rev-parse --short HEAD) && + two=$(git rev-parse --short master) && + cat >expect <<-EOF && + * (HEAD detached from fromtag) $one one + ambiguous $one one + branch-one $two two + + branch-two $one ($(pwd)/worktree_dir) one + master $two two + ref-to-branch $two two + ref-to-remote $two two + EOF + git worktree add worktree_dir branch-two && + git branch -vv >actual && + rm -r worktree_dir && + git worktree prune && + test_cmp expect actual +' + test_done -- 2.14.2 ^ permalink raw reply related [flat|nested] 136+ messages in thread
* Re: [PATCH v9 3/3] branch: add worktree info on verbose output 2019-03-16 1:38 ` [PATCH v9 3/3] branch: add worktree info on verbose output nbelakovski @ 2019-03-18 12:10 ` SZEDER Gábor 0 siblings, 0 replies; 136+ messages in thread From: SZEDER Gábor @ 2019-03-18 12:10 UTC (permalink / raw) To: nbelakovski; +Cc: git, peff, gitster, rafa.almas, avarab On Fri, Mar 15, 2019 at 06:38:07PM -0700, nbelakovski@gmail.com wrote: > diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh > index ceb74e0826..e5dc23e225 100755 > --- a/t/t3203-branch-output.sh > +++ b/t/t3203-branch-output.sh > @@ -328,4 +328,23 @@ test_expect_success '--color overrides auto-color' ' > test_cmp expect.color actual > ' > > +test_expect_success 'verbose output lists worktree path' ' > + one=$(git rev-parse --short HEAD) && > + two=$(git rev-parse --short master) && > + cat >expect <<-EOF && > + * (HEAD detached from fromtag) $one one This 'HEAD detached from ..." message is translated ... > + ambiguous $one one > + branch-one $two two > + + branch-two $one ($(pwd)/worktree_dir) one > + master $two two > + ref-to-branch $two two > + ref-to-remote $two two > + EOF > + git worktree add worktree_dir branch-two && > + git branch -vv >actual && > + rm -r worktree_dir && > + git worktree prune && > + test_cmp expect actual ... therefore here you should use 'test_i18ncmp', as otherwise the GETTEXT_POISON test run fails. > +' > + > test_done > -- > 2.14.2 > ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v9 0/3] 2019-03-16 1:38 ` [PATCH v9 0/3] nbelakovski ` (2 preceding siblings ...) 2019-03-16 1:38 ` [PATCH v9 3/3] branch: add worktree info on verbose output nbelakovski @ 2019-03-18 5:30 ` Junio C Hamano 3 siblings, 0 replies; 136+ messages in thread From: Junio C Hamano @ 2019-03-18 5:30 UTC (permalink / raw) To: nbelakovski; +Cc: git, peff, rafa.almas, avarab nbelakovski@gmail.com writes: > From: Nickolai Belakovski <nbelakovski@gmail.com> > > Cleanup on 2/3 and 3/3 I'll have to re-read the test part myself to see what you meant by the "strategy for updating tests" and if it makes sense, but what you wrote as the goal and approach in the cover letter all sounded sensible. Thanks, will requeue. ^ permalink raw reply [flat|nested] 136+ messages in thread
* [PATCH v10 0/3] 2018-09-27 15:13 ` [PATCH] branch: colorize branches checked out in a linked working tree the same way as the current branch is colorized Nickolai Belakovski ` (10 preceding siblings ...) 2019-03-16 1:38 ` [PATCH v9 0/3] nbelakovski @ 2019-04-29 5:19 ` nbelakovski 2019-04-29 5:19 ` [PATCH v10 1/3] ref-filter: add worktreepath atom nbelakovski ` (4 more replies) 11 siblings, 5 replies; 136+ messages in thread From: nbelakovski @ 2019-04-29 5:19 UTC (permalink / raw) To: git; +Cc: szeder.dev, Nickolai Belakovski From: Nickolai Belakovski <nbelakovski@gmail.com> Added test_i18ncmp per instructions from Szeder Is there some other part of the infrastructure that's testing for this? Because it did not fail in any of my Travis CI builds. Travis CI results: https://travis-ci.org/nbelakovski/git/builds/525801210 Nickolai Belakovski (3): ref-filter: add worktreepath atom branch: update output to include worktree info branch: add worktree info on verbose output Documentation/git-branch.txt | 12 ++++-- Documentation/git-for-each-ref.txt | 5 +++ builtin/branch.c | 16 ++++++-- ref-filter.c | 78 ++++++++++++++++++++++++++++++++++++++ t/t3200-branch.sh | 16 +++++--- t/t3203-branch-output.sh | 43 ++++++++++++++++++++- t/t6302-for-each-ref-filter.sh | 13 +++++++ 7 files changed, 168 insertions(+), 15 deletions(-) -- 2.14.2 ^ permalink raw reply [flat|nested] 136+ messages in thread
* [PATCH v10 1/3] ref-filter: add worktreepath atom 2019-04-29 5:19 ` [PATCH v10 0/3] nbelakovski @ 2019-04-29 5:19 ` nbelakovski 2019-04-29 5:19 ` [PATCH v10 2/3] branch: update output to include worktree info nbelakovski ` (3 subsequent siblings) 4 siblings, 0 replies; 136+ messages in thread From: nbelakovski @ 2019-04-29 5:19 UTC (permalink / raw) To: git; +Cc: szeder.dev, Nickolai Belakovski From: Nickolai Belakovski <nbelakovski@gmail.com> Add an atom providing the path of the linked worktree where this ref is checked out, if it is checked out in any linked worktrees, and empty string otherwise. Signed-off-by: Nickolai Belakovski <nbelakovski@gmail.com> --- Documentation/git-for-each-ref.txt | 5 +++ ref-filter.c | 78 ++++++++++++++++++++++++++++++++++++++ t/t6302-for-each-ref-filter.sh | 13 +++++++ 3 files changed, 96 insertions(+) diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt index 774cecc7ed..6dcd39f6f6 100644 --- a/Documentation/git-for-each-ref.txt +++ b/Documentation/git-for-each-ref.txt @@ -214,6 +214,11 @@ symref:: `:lstrip` and `:rstrip` options in the same way as `refname` above. +worktreepath:: + The absolute path to the worktree in which the ref is checked + out, if it is checked out in any linked worktree. Empty string + otherwise. + In addition to the above, for commit and tag objects, the header field names (`tree`, `parent`, `object`, `type`, and `tag`) can be used to specify the value in the header field. diff --git a/ref-filter.c b/ref-filter.c index 8d11a94cbd..13984fa8ca 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -20,6 +20,8 @@ #include "commit-slab.h" #include "commit-graph.h" #include "commit-reach.h" +#include "worktree.h" +#include "hashmap.h" static struct ref_msg { const char *gone; @@ -75,6 +77,27 @@ static struct expand_data { struct object_info info; } oi, oi_deref; +struct ref_to_worktree_entry { + struct hashmap_entry ent; /* must be the first member! */ + struct worktree *wt; /* key is wt->head_ref */ +}; + +static int ref_to_worktree_map_cmpfnc(const void *unused_lookupdata, + const void *existing_hashmap_entry_to_test, + const void *key, + const void *keydata_aka_refname) +{ + const struct ref_to_worktree_entry *e = existing_hashmap_entry_to_test; + const struct ref_to_worktree_entry *k = key; + return strcmp(e->wt->head_ref, + keydata_aka_refname ? keydata_aka_refname : k->wt->head_ref); +} + +static struct ref_to_worktree_map { + struct hashmap map; + struct worktree **worktrees; +} ref_to_worktree_map; + /* * An atom is a valid field atom listed below, possibly prefixed with * a "*" to denote deref_tag(). @@ -480,6 +503,7 @@ static struct { { "flag", SOURCE_NONE }, { "HEAD", SOURCE_NONE, FIELD_STR, head_atom_parser }, { "color", SOURCE_NONE, FIELD_STR, color_atom_parser }, + { "worktreepath", SOURCE_NONE }, { "align", SOURCE_NONE, FIELD_STR, align_atom_parser }, { "end", SOURCE_NONE }, { "if", SOURCE_NONE, FIELD_STR, if_atom_parser }, @@ -1529,6 +1553,48 @@ static int get_object(struct ref_array_item *ref, int deref, struct object **obj return 0; } +static void populate_worktree_map(struct hashmap *map, struct worktree **worktrees) +{ + int i; + + for (i = 0; worktrees[i]; i++) { + if (worktrees[i]->head_ref) { + struct ref_to_worktree_entry *entry; + entry = xmalloc(sizeof(*entry)); + entry->wt = worktrees[i]; + hashmap_entry_init(entry, strhash(worktrees[i]->head_ref)); + + hashmap_add(map, entry); + } + } +} + +static void lazy_init_worktree_map(void) +{ + if (ref_to_worktree_map.worktrees) + return; + + ref_to_worktree_map.worktrees = get_worktrees(0); + hashmap_init(&(ref_to_worktree_map.map), ref_to_worktree_map_cmpfnc, NULL, 0); + populate_worktree_map(&(ref_to_worktree_map.map), ref_to_worktree_map.worktrees); +} + +static char *get_worktree_path(const struct used_atom *atom, const struct ref_array_item *ref) +{ + struct hashmap_entry entry; + struct ref_to_worktree_entry *lookup_result; + + lazy_init_worktree_map(); + + hashmap_entry_init(&entry, strhash(ref->refname)); + lookup_result = hashmap_get(&(ref_to_worktree_map.map), &entry, ref->refname); + + if (lookup_result) + return xstrdup(lookup_result->wt->path); + else + return xstrdup(""); +} + /* * Parse the object referred by ref, and grab needed value. */ @@ -1566,6 +1632,13 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err) if (starts_with(name, "refname")) refname = get_refname(atom, ref); + else if (!strcmp(name, "worktreepath")) { + if (ref->kind == FILTER_REFS_BRANCHES) + v->s = get_worktree_path(atom, ref); + else + v->s = xstrdup(""); + continue; + } else if (starts_with(name, "symref")) refname = get_symref(atom, ref); else if (starts_with(name, "upstream")) { @@ -2049,6 +2122,11 @@ void ref_array_clear(struct ref_array *array) free_array_item(array->items[i]); FREE_AND_NULL(array->items); array->nr = array->alloc = 0; + if (ref_to_worktree_map.worktrees) { + hashmap_free(&(ref_to_worktree_map.map), 1); + free_worktrees(ref_to_worktree_map.worktrees); + ref_to_worktree_map.worktrees = NULL; + } } static void do_merge_filter(struct ref_filter_cbdata *ref_cbdata) diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh index fc067ed672..35408d53fd 100755 --- a/t/t6302-for-each-ref-filter.sh +++ b/t/t6302-for-each-ref-filter.sh @@ -441,4 +441,17 @@ test_expect_success '--merged is incompatible with --no-merged' ' test_must_fail git for-each-ref --merged HEAD --no-merged HEAD ' +test_expect_success 'validate worktree atom' ' + cat >expect <<-EOF && + master: $(pwd) + master_worktree: $(pwd)/worktree_dir + side: not checked out + EOF + git worktree add -b master_worktree worktree_dir master && + git for-each-ref --format="%(refname:short): %(if)%(worktreepath)%(then)%(worktreepath)%(else)not checked out%(end)" refs/heads/ >actual && + rm -r worktree_dir && + git worktree prune && + test_cmp expect actual +' + test_done -- 2.14.2 ^ permalink raw reply related [flat|nested] 136+ messages in thread
* [PATCH v10 2/3] branch: update output to include worktree info 2019-04-29 5:19 ` [PATCH v10 0/3] nbelakovski 2019-04-29 5:19 ` [PATCH v10 1/3] ref-filter: add worktreepath atom nbelakovski @ 2019-04-29 5:19 ` nbelakovski 2019-04-29 5:19 ` [PATCH v10 3/3] branch: add worktree info on verbose output nbelakovski ` (2 subsequent siblings) 4 siblings, 0 replies; 136+ messages in thread From: nbelakovski @ 2019-04-29 5:19 UTC (permalink / raw) To: git; +Cc: szeder.dev, Nickolai Belakovski From: Nickolai Belakovski <nbelakovski@gmail.com> The output of git branch is modified to mark branches checked out in a linked worktree with a "+" and color them in cyan (in contrast to the current branch, which will still be denoted with a "*" and colored in green) This is meant to communicate to the user that the branches that are marked or colored will behave differently from other branches if the user attempts to check them out or delete them, since branches checked out in another worktree cannot be checked out or deleted. Signed-off-by: Nickolai Belakovski <nbelakovski@gmail.com> --- Documentation/git-branch.txt | 6 ++++-- builtin/branch.c | 12 ++++++++---- t/t3200-branch.sh | 16 +++++++++++----- t/t3203-branch-output.sh | 24 ++++++++++++++++++++++-- 4 files changed, 45 insertions(+), 13 deletions(-) diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index 0cd87ddeff..7d18dffc4b 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -26,8 +26,10 @@ DESCRIPTION ----------- If `--list` is given, or if there are no non-option arguments, existing -branches are listed; the current branch will be highlighted with an -asterisk. Option `-r` causes the remote-tracking branches to be listed, +branches are listed; the current branch will be highlighted in green and +marked with an asterisk. Any branches checked out in linked worktrees will +be highlighted in cyan and marked with a plus sign. Option `-r` causes the +remote-tracking branches to be listed, and option `-a` shows both local and remote branches. If a `<pattern>` is given, it is used as a shell wildcard to restrict the output to matching branches. If multiple patterns are given, a branch is shown if diff --git a/builtin/branch.c b/builtin/branch.c index d4359b33ac..a295380f45 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -47,6 +47,7 @@ static char branch_colors[][COLOR_MAXLEN] = { GIT_COLOR_NORMAL, /* LOCAL */ GIT_COLOR_GREEN, /* CURRENT */ GIT_COLOR_BLUE, /* UPSTREAM */ + GIT_COLOR_CYAN, /* WORKTREE */ }; enum color_branch { BRANCH_COLOR_RESET = 0, @@ -54,7 +55,8 @@ enum color_branch { BRANCH_COLOR_REMOTE = 2, BRANCH_COLOR_LOCAL = 3, BRANCH_COLOR_CURRENT = 4, - BRANCH_COLOR_UPSTREAM = 5 + BRANCH_COLOR_UPSTREAM = 5, + BRANCH_COLOR_WORKTREE = 6 }; static const char *color_branch_slots[] = { @@ -64,6 +66,7 @@ static const char *color_branch_slots[] = { [BRANCH_COLOR_LOCAL] = "local", [BRANCH_COLOR_CURRENT] = "current", [BRANCH_COLOR_UPSTREAM] = "upstream", + [BRANCH_COLOR_WORKTREE] = "worktree", }; static struct string_list output = STRING_LIST_INIT_DUP; @@ -342,9 +345,10 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r struct strbuf local = STRBUF_INIT; struct strbuf remote = STRBUF_INIT; - strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else) %s%%(end)", - branch_get_color(BRANCH_COLOR_CURRENT), - branch_get_color(BRANCH_COLOR_LOCAL)); + strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else)%%(if)%%(worktreepath)%%(then)+ %s%%(else) %s%%(end)%%(end)", + branch_get_color(BRANCH_COLOR_CURRENT), + branch_get_color(BRANCH_COLOR_WORKTREE), + branch_get_color(BRANCH_COLOR_LOCAL)); strbuf_addf(&remote, " %s", branch_get_color(BRANCH_COLOR_REMOTE)); diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index 478b82cf9b..88719cc02c 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -202,18 +202,22 @@ test_expect_success 'git branch -M baz bam should succeed when baz is checked ou git worktree add -f bazdir2 baz && git branch -M baz bam && test $(git -C bazdir rev-parse --abbrev-ref HEAD) = bam && - test $(git -C bazdir2 rev-parse --abbrev-ref HEAD) = bam + test $(git -C bazdir2 rev-parse --abbrev-ref HEAD) = bam && + rm -r bazdir bazdir2 && + git worktree prune ' test_expect_success 'git branch -M baz bam should succeed within a worktree in which baz is checked out' ' git checkout -b baz && - git worktree add -f bazdir3 baz && + git worktree add -f bazdir baz && ( - cd bazdir3 && + cd bazdir && git branch -M baz bam && test $(git rev-parse --abbrev-ref HEAD) = bam ) && - test $(git rev-parse --abbrev-ref HEAD) = bam + test $(git rev-parse --abbrev-ref HEAD) = bam && + rm -r bazdir && + git worktree prune ' test_expect_success 'git branch -M master should work when master is checked out' ' @@ -774,7 +778,9 @@ test_expect_success 'test deleting branch without config' ' test_expect_success 'deleting currently checked out branch fails' ' git worktree add -b my7 my7 && test_must_fail git -C my7 branch -d my7 && - test_must_fail git branch -d my7 + test_must_fail git branch -d my7 && + rm -r my7 && + git worktree prune ' test_expect_success 'test --track without .fetch entries' ' diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh index be55148930..a3436bf25a 100755 --- a/t/t3203-branch-output.sh +++ b/t/t3203-branch-output.sh @@ -136,11 +136,13 @@ test_expect_success 'git branch `--show-current` works properly with worktrees' branch-two EOF git checkout branch-one && - git worktree add worktree branch-two && + git worktree add worktree_dir branch-two && { git branch --show-current && - git -C worktree branch --show-current + git -C worktree_dir branch --show-current } >actual && + rm -r worktree_dir && + git worktree prune && test_cmp expect actual ' @@ -284,6 +286,24 @@ test_expect_success 'git branch --format option' ' test_i18ncmp expect actual ' +test_expect_success 'worktree colors correct' ' + cat >expect <<-EOF && + * <GREEN>(HEAD detached from fromtag)<RESET> + ambiguous<RESET> + branch-one<RESET> + + <CYAN>branch-two<RESET> + master<RESET> + ref-to-branch<RESET> -> branch-one + ref-to-remote<RESET> -> origin/branch-one + EOF + git worktree add worktree_dir branch-two && + git branch --color >actual.raw && + rm -r worktree_dir && + git worktree prune && + test_decode_color <actual.raw >actual && + test_i18ncmp expect actual +' + test_expect_success "set up color tests" ' echo "<RED>master<RESET>" >expect.color && echo "master" >expect.bare && -- 2.14.2 ^ permalink raw reply related [flat|nested] 136+ messages in thread
* [PATCH v10 3/3] branch: add worktree info on verbose output 2019-04-29 5:19 ` [PATCH v10 0/3] nbelakovski 2019-04-29 5:19 ` [PATCH v10 1/3] ref-filter: add worktreepath atom nbelakovski 2019-04-29 5:19 ` [PATCH v10 2/3] branch: update output to include worktree info nbelakovski @ 2019-04-29 5:19 ` nbelakovski 2019-04-29 14:12 ` [PATCH v10 0/3] SZEDER Gábor 2019-04-29 20:57 ` Johannes Schindelin 4 siblings, 0 replies; 136+ messages in thread From: nbelakovski @ 2019-04-29 5:19 UTC (permalink / raw) To: git; +Cc: szeder.dev, Nickolai Belakovski From: Nickolai Belakovski <nbelakovski@gmail.com> To display worktree path for refs checked out in a linked worktree Signed-off-by: Nickolai Belakovski <nbelakovski@gmail.com> --- Documentation/git-branch.txt | 6 ++++-- builtin/branch.c | 4 ++++ t/t3203-branch-output.sh | 19 +++++++++++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index 7d18dffc4b..d11d00583a 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -172,8 +172,10 @@ This option is only applicable in non-verbose mode. When in list mode, show sha1 and commit subject line for each head, along with relationship to upstream branch (if any). If given twice, print - the name of the upstream branch, as well (see also `git remote - show <remote>`). + the path of the linked worktree (if any) and the name of the upstream + branch, as well (see also `git remote show <remote>`). Note that the + current worktree's HEAD will not have its path printed (it will always + be your current directory). -q:: --quiet:: diff --git a/builtin/branch.c b/builtin/branch.c index a295380f45..2cb45e42e1 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -367,9 +367,13 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r strbuf_addf(&local, " %s ", obname.buf); if (filter->verbose > 1) + { + strbuf_addf(&local, "%%(if:notequals=*)%%(HEAD)%%(then)%%(if)%%(worktreepath)%%(then)(%s%%(worktreepath)%s) %%(end)%%(end)", + branch_get_color(BRANCH_COLOR_WORKTREE), branch_get_color(BRANCH_COLOR_RESET)); strbuf_addf(&local, "%%(if)%%(upstream)%%(then)[%s%%(upstream:short)%s%%(if)%%(upstream:track)" "%%(then): %%(upstream:track,nobracket)%%(end)] %%(end)%%(contents:subject)", branch_get_color(BRANCH_COLOR_UPSTREAM), branch_get_color(BRANCH_COLOR_RESET)); + } else strbuf_addf(&local, "%%(if)%%(upstream:track)%%(then)%%(upstream:track) %%(end)%%(contents:subject)"); diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh index a3436bf25a..4bef8c7569 100755 --- a/t/t3203-branch-output.sh +++ b/t/t3203-branch-output.sh @@ -328,4 +328,23 @@ test_expect_success '--color overrides auto-color' ' test_cmp expect.color actual ' +test_expect_success 'verbose output lists worktree path' ' + one=$(git rev-parse --short HEAD) && + two=$(git rev-parse --short master) && + cat >expect <<-EOF && + * (HEAD detached from fromtag) $one one + ambiguous $one one + branch-one $two two + + branch-two $one ($(pwd)/worktree_dir) one + master $two two + ref-to-branch $two two + ref-to-remote $two two + EOF + git worktree add worktree_dir branch-two && + git branch -vv >actual && + rm -r worktree_dir && + git worktree prune && + test_i18ncmp expect actual +' + test_done -- 2.14.2 ^ permalink raw reply related [flat|nested] 136+ messages in thread
* Re: [PATCH v10 0/3] 2019-04-29 5:19 ` [PATCH v10 0/3] nbelakovski ` (2 preceding siblings ...) 2019-04-29 5:19 ` [PATCH v10 3/3] branch: add worktree info on verbose output nbelakovski @ 2019-04-29 14:12 ` SZEDER Gábor 2019-04-29 19:27 ` Nickolai Belakovski 2019-04-29 20:57 ` Johannes Schindelin 4 siblings, 1 reply; 136+ messages in thread From: SZEDER Gábor @ 2019-04-29 14:12 UTC (permalink / raw) To: nbelakovski; +Cc: git On Sun, Apr 28, 2019 at 10:19:41PM -0700, nbelakovski@gmail.com wrote: > From: Nickolai Belakovski <nbelakovski@gmail.com> > > Added test_i18ncmp per instructions from Szeder > > Is there some other part of the infrastructure that's testing for this? Because it did not fail in any of my Travis CI builds. > > Travis CI results: https://travis-ci.org/nbelakovski/git/builds/525801210 Testing with GETTEXT_POISON was broken since 6cdccfce (i18n: make GETTEXT_POISON a runtime option, 2018-11-08), and didn't catch any of these issues. See: https://public-inbox.org/git/xmqqlg0bvppc.fsf_-_@gitster-ct.c.googlers.com/T/#u The fix f88b9cb603 (gettext tests: export the restored GIT_TEST_GETTEXT_POISON, 2019-04-15) was merged to master just last week. ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v10 0/3] 2019-04-29 14:12 ` [PATCH v10 0/3] SZEDER Gábor @ 2019-04-29 19:27 ` Nickolai Belakovski 0 siblings, 0 replies; 136+ messages in thread From: Nickolai Belakovski @ 2019-04-29 19:27 UTC (permalink / raw) To: SZEDER Gábor; +Cc: Git List > Testing with GETTEXT_POISON was broken since 6cdccfce (i18n: make > GETTEXT_POISON a runtime option, 2018-11-08), and didn't catch any of > these issues. See: > > https://public-inbox.org/git/xmqqlg0bvppc.fsf_-_@gitster-ct.c.googlers.com/T/#u > > The fix f88b9cb603 (gettext tests: export the restored > GIT_TEST_GETTEXT_POISON, 2019-04-15) was merged to master just last > week. > Ah I see, thanks for the info. ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v10 0/3] 2019-04-29 5:19 ` [PATCH v10 0/3] nbelakovski ` (3 preceding siblings ...) 2019-04-29 14:12 ` [PATCH v10 0/3] SZEDER Gábor @ 2019-04-29 20:57 ` Johannes Schindelin 2019-04-29 21:33 ` Nickolai Belakovski [not found] ` <CAC05385Mc7pqiCd5mb+1c4WM+v7K=h=GMHuvkw9xizhRFJXXBA@mail.gmail.com> 4 siblings, 2 replies; 136+ messages in thread From: Johannes Schindelin @ 2019-04-29 20:57 UTC (permalink / raw) To: Nickolai Belakovski; +Cc: git, szeder.dev Hi, am I the only person who is puzzled every time this patch series with a completely empty subject and without any further explanation about the intent in the mail body is sent? Ciao, Johannes On Sun, 28 Apr 2019, nbelakovski@gmail.com wrote: > From: Nickolai Belakovski <nbelakovski@gmail.com> > > Added test_i18ncmp per instructions from Szeder > > Is there some other part of the infrastructure that's testing for this? Because it did not fail in any of my Travis CI builds. > > Travis CI results: https://travis-ci.org/nbelakovski/git/builds/525801210 > > Nickolai Belakovski (3): > ref-filter: add worktreepath atom > branch: update output to include worktree info > branch: add worktree info on verbose output > > Documentation/git-branch.txt | 12 ++++-- > Documentation/git-for-each-ref.txt | 5 +++ > builtin/branch.c | 16 ++++++-- > ref-filter.c | 78 ++++++++++++++++++++++++++++++++++++++ > t/t3200-branch.sh | 16 +++++--- > t/t3203-branch-output.sh | 43 ++++++++++++++++++++- > t/t6302-for-each-ref-filter.sh | 13 +++++++ > 7 files changed, 168 insertions(+), 15 deletions(-) > > -- > 2.14.2 > > ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v10 0/3] 2019-04-29 20:57 ` Johannes Schindelin @ 2019-04-29 21:33 ` Nickolai Belakovski [not found] ` <CAC05385Mc7pqiCd5mb+1c4WM+v7K=h=GMHuvkw9xizhRFJXXBA@mail.gmail.com> 1 sibling, 0 replies; 136+ messages in thread From: Nickolai Belakovski @ 2019-04-29 21:33 UTC (permalink / raw) To: Johannes Schindelin; +Cc: Git List, SZEDER Gábor Sorry, I'm not very accustomed to mailing list development. I had assumed that this was being threaded with the other messages in the series, hence leaving the subject blank and only putting new info in the body. In the future I'll add in an appropriate subject and brief re-hash in the body. Thanks for bringing it up. Nickolai On Mon, Apr 29, 2019 at 1:57 PM Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote: > > Hi, > > am I the only person who is puzzled every time this patch series with a > completely empty subject and without any further explanation about the > intent in the mail body is sent? > > Ciao, > Johannes > > > On Sun, 28 Apr 2019, nbelakovski@gmail.com wrote: > > > From: Nickolai Belakovski <nbelakovski@gmail.com> > > > > Added test_i18ncmp per instructions from Szeder > > > > Is there some other part of the infrastructure that's testing for this? Because it did not fail in any of my Travis CI builds. > > > > Travis CI results: https://travis-ci.org/nbelakovski/git/builds/525801210 > > > > Nickolai Belakovski (3): > > ref-filter: add worktreepath atom > > branch: update output to include worktree info > > branch: add worktree info on verbose output > > > > Documentation/git-branch.txt | 12 ++++-- > > Documentation/git-for-each-ref.txt | 5 +++ > > builtin/branch.c | 16 ++++++-- > > ref-filter.c | 78 ++++++++++++++++++++++++++++++++++++++ > > t/t3200-branch.sh | 16 +++++--- > > t/t3203-branch-output.sh | 43 ++++++++++++++++++++- > > t/t6302-for-each-ref-filter.sh | 13 +++++++ > > 7 files changed, 168 insertions(+), 15 deletions(-) > > > > -- > > 2.14.2 > > > > ^ permalink raw reply [flat|nested] 136+ messages in thread
[parent not found: <CAC05385Mc7pqiCd5mb+1c4WM+v7K=h=GMHuvkw9xizhRFJXXBA@mail.gmail.com>]
* Re: [PATCH v10 0/3] [not found] ` <CAC05385Mc7pqiCd5mb+1c4WM+v7K=h=GMHuvkw9xizhRFJXXBA@mail.gmail.com> @ 2019-04-30 21:46 ` Johannes Schindelin 0 siblings, 0 replies; 136+ messages in thread From: Johannes Schindelin @ 2019-04-30 21:46 UTC (permalink / raw) To: Nickolai Belakovski; +Cc: Git List, SZEDER Gábor Hi Nickolai, On Mon, 29 Apr 2019, Nickolai Belakovski wrote: > Sorry, I'm not very accustomed to mailing list development. I had assumed > that this was being threaded with the other messages in the series, hence > leaving the subject blank and only putting new info in the body. The openness of a mailing list-centric development is that everybody with an email address can participate [*1*]. The downside is that *anybody* with *any* mail program can be a reader, so you cannot assume *anything* about how people will read your mails. Some will read it in a mail program that color-codes levels of quotation. Others will not have any color whatsoever. Some thread. Some don't. In most mail programs, you cannot even search for a Message-ID. Which can be non-unique. So the perceived benefits of this way to run a project come at a price. > In the future I'll add in an appropriate subject and brief re-hash in the > body. Thanks for bringing it up. Thank you, Johannes Footnote *1*: Of course, it is not *all* that open. If you cannot convince your mail program to send mails in plain text only, and to stop reformatting mails e.g. to make them look better on cell phones (refusing this is the price of requiring patches to be sent in whitespace-preserving plain text emails), then you're not invited to the party. This is why I sometimes say, not altogether without reason, that you can use *any* mail program to participate in the Git project as long as it is `mutt`. ^ permalink raw reply [flat|nested] 136+ messages in thread
* [PATCH v2 0/3] bundle.c: remove "ref_list" in favor of string-list.c API @ 2021-06-21 15:16 Ævar Arnfjörð Bjarmason 2021-06-30 14:06 ` [PATCH v3 0/3] Ævar Arnfjörð Bjarmason 0 siblings, 1 reply; 136+ messages in thread From: Ævar Arnfjörð Bjarmason @ 2021-06-21 15:16 UTC (permalink / raw) To: git Cc: Junio C Hamano, Jeff King, Johannes Schindelin, Andrei Rybak, Ævar Arnfjörð Bjarmason This v2 addresses an omission noted by Andrei Rybak[1]. I didn't factor out the "name" into a variable in 2/3 for ease of reading 3/3. That's now done. 1. https://lore.kernel.org/git/23c4ce5f-7769-1d2c-3a97-ac9733f73a25@gmail.com/ Ævar Arnfjörð Bjarmason (3): bundle cmd: stop leaking memory from parse_options_cmd_bundle() bundle.c: use a temporary variable for OIDs and names bundle: remove "ref_list" in favor of string-list.c API builtin/bundle.c | 91 ++++++++++++++++++++++++++++++++---------------- bundle.c | 74 ++++++++++++++++++++++----------------- bundle.h | 20 +++++------ transport.c | 12 +++++-- 4 files changed, 122 insertions(+), 75 deletions(-) Range-diff against v1: 1: f4191088ac3 = 1: 932c0883ce0 bundle cmd: stop leaking memory from parse_options_cmd_bundle() 2: f297fd0432a ! 2: 7e0d57951e5 bundle.c: use a temporary variable for OIDs and names @@ bundle.c: int verify_bundle(struct repository *r, for (i = 0; i < p->nr; i++) { struct ref_list_entry *e = p->list + i; - struct object *o = parse_object(r, &e->oid); ++ const char *name = e->name; + struct object_id *oid = &e->oid; + struct object *o = parse_object(r, oid); if (o) { o->flags |= PREREQ_MARK; - add_pending_object(&revs, o, e->name); -@@ bundle.c: int verify_bundle(struct repository *r, +- add_pending_object(&revs, o, e->name); ++ add_pending_object(&revs, o, name); + continue; } if (++ret == 1) error("%s", message); - error("%s %s", oid_to_hex(&e->oid), e->name); -+ error("%s %s", oid_to_hex(oid), e->name); ++ error("%s %s", oid_to_hex(oid), name); } if (revs.pending.nr != p->nr) return ret; @@ bundle.c: int verify_bundle(struct repository *r, for (i = 0; i < p->nr; i++) { struct ref_list_entry *e = p->list + i; - struct object *o = parse_object(r, &e->oid); ++ const char *name = e->name; + struct object_id *oid = &e->oid; + struct object *o = parse_object(r, oid); assert(o); /* otherwise we'd have returned early */ @@ bundle.c: int verify_bundle(struct repository *r, if (++ret == 1) error("%s", message); - error("%s %s", oid_to_hex(&e->oid), e->name); -+ error("%s %s", oid_to_hex(oid), e->name); ++ error("%s %s", oid_to_hex(oid), name); } /* Clean up objects used, as they will be reused. */ @@ bundle.c: int verify_bundle(struct repository *r, ## transport.c ## @@ transport.c: static struct ref *get_refs_from_bundle(struct transport *transport, + for (i = 0; i < data->header.references.nr; i++) { struct ref_list_entry *e = data->header.references.list + i; - struct ref *ref = alloc_ref(e->name); +- struct ref *ref = alloc_ref(e->name); - oidcpy(&ref->old_oid, &e->oid); ++ const char *name = e->name; ++ struct ref *ref = alloc_ref(name); + struct object_id *oid = &e->oid; + oidcpy(&ref->old_oid, oid); ref->next = result; 3: 887313d3b02 ! 3: 9d9cb5aaf9e bundle: remove "ref_list" in favor of string-list.c API @@ bundle.c: int verify_bundle(struct repository *r, repo_init_revisions(r, &revs, NULL); for (i = 0; i < p->nr; i++) { - struct ref_list_entry *e = p->list + i; +- const char *name = e->name; - struct object_id *oid = &e->oid; + struct string_list_item *e = p->items + i; ++ const char *name = e->string; + struct object_id *oid = e->util; struct object *o = parse_object(r, oid); if (o) { o->flags |= PREREQ_MARK; -- add_pending_object(&revs, o, e->name); -+ add_pending_object(&revs, o, e->string); - continue; - } - if (++ret == 1) - error("%s", message); -- error("%s %s", oid_to_hex(oid), e->name); -+ error("%s %s", oid_to_hex(oid), e->string); - } - if (revs.pending.nr != p->nr) - return ret; @@ bundle.c: int verify_bundle(struct repository *r, i--; for (i = 0; i < p->nr; i++) { - struct ref_list_entry *e = p->list + i; +- const char *name = e->name; - struct object_id *oid = &e->oid; + struct string_list_item *e = p->items + i; ++ const char *name = e->string; + const struct object_id *oid = e->util; struct object *o = parse_object(r, oid); assert(o); /* otherwise we'd have returned early */ if (o->flags & SHOWN) - continue; - if (++ret == 1) - error("%s", message); -- error("%s %s", oid_to_hex(oid), e->name); -+ error("%s %s", oid_to_hex(oid), e->string); - } +@@ bundle.c: int verify_bundle(struct repository *r, /* Clean up objects used, as they will be reused. */ for (i = 0; i < p->nr; i++) { @@ transport.c: static struct ref *get_refs_from_bundle(struct transport *transport for (i = 0; i < data->header.references.nr; i++) { - struct ref_list_entry *e = data->header.references.list + i; -- struct ref *ref = alloc_ref(e->name); -- struct object_id *oid = &e->oid; +- const char *name = e->name; + struct string_list_item *e = data->header.references.items + i; -+ struct ref *ref = alloc_ref(e->string); -+ const struct object_id *oid = e->util; ++ const char *name = e->string; + struct ref *ref = alloc_ref(name); +- struct object_id *oid = &e->oid; ++ struct object_id *oid = e->util; oidcpy(&ref->old_oid, oid); ref->next = result; result = ref; -- 2.32.0.599.g3967b4fa4ac ^ permalink raw reply [flat|nested] 136+ messages in thread
* [PATCH v3 0/3] 2021-06-21 15:16 [PATCH v2 0/3] bundle.c: remove "ref_list" in favor of string-list.c API Ævar Arnfjörð Bjarmason @ 2021-06-30 14:06 ` Ævar Arnfjörð Bjarmason 2021-06-30 17:34 ` Jeff King 0 siblings, 1 reply; 136+ messages in thread From: Ævar Arnfjörð Bjarmason @ 2021-06-30 14:06 UTC (permalink / raw) To: git Cc: Junio C Hamano, Jeff King, Johannes Schindelin, Andrei Rybak, Ævar Arnfjörð Bjarmason Refactor the bundle API to use the string_list API instead of its own version of a similar API. See [1] for v2. Addresses comments by Jeff King about us being too overzelous in trying not to leak memory (the 'die_no_repo' is gone), and other flow/style comments of his. I also added a bundle_header_init() function for use in transport.c, and noticed a redundant call to string_list_clear() there. 1. https://lore.kernel.org/git/cover-0.3-00000000000-20210621T151357Z-avarab@gmail.com/ Ævar Arnfjörð Bjarmason (3): bundle cmd: stop leaking memory from parse_options_cmd_bundle() bundle.c: use a temporary variable for OIDs and names bundle: remove "ref_list" in favor of string-list.c API builtin/bundle.c | 74 ++++++++++++++++++++++++++++++------------------ bundle.c | 65 ++++++++++++++++++++++++++---------------- bundle.h | 21 +++++++------- transport.c | 10 +++++-- 4 files changed, 105 insertions(+), 65 deletions(-) Range-diff against v2: 1: 6a8b20a7cf3 < -: ----------- upload-pack: run is_repository_shallow() before setup_revisions() 2: d88b2c04102 < -: ----------- revision.h: unify "disable_stdin" and "read_from_stdin" 3: d433d7b24a3 < -: ----------- pack-objects.c: do stdin parsing via revision.c's API 4: e59a06c3148 < -: ----------- pack-objects.c: make use of REV_INFO_STDIN_LINE_PROCESS 5: f4191088ac3 ! 1: 3d0d7a8e8b5 bundle cmd: stop leaking memory from parse_options_cmd_bundle() @@ Commit message about those fixes if valgrind runs cleanly at the end without any leaks whatsoever. + An earlier version of this change went out of its way to not leak + memory on the die() codepaths here, but that was deemed too verbose to + worry about in a built-in that's dying anyway. The only reason we'd + need that is to appease a mode like SANITIZE=leak within the scope of + an entire test file. + Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com> ## builtin/bundle.c ## @@ builtin/bundle.c: static int cmd_bundle_create(int argc, const char **argv, cons struct strvec pack_opts; int version = -1; - -+ int die_no_repo = 0; + int ret; struct option options[] = { OPT_SET_INT('q', "quiet", &progress, @@ builtin/bundle.c: static int cmd_bundle_create(int argc, const char **argv, cons argc = parse_options_cmd_bundle(argc, argv, prefix, builtin_bundle_create_usage, options, &bundle_file); @@ builtin/bundle.c: static int cmd_bundle_create(int argc, const char **argv, const char *prefix) { - if (progress && all_progress_implied) - strvec_push(&pack_opts, "--all-progress-implied"); -- if (!startup_info->have_repository) -+ if (!startup_info->have_repository) { -+ die_no_repo = 1; -+ goto cleanup; -+ } -+ ret = !!create_bundle(the_repository, bundle_file, argc, argv, &pack_opts, version); -+cleanup: -+ free(bundle_file); -+ if (die_no_repo) + if (!startup_info->have_repository) die(_("Need a repository to create a bundle.")); - return !!create_bundle(the_repository, bundle_file, argc, argv, &pack_opts, version); ++ ret = !!create_bundle(the_repository, bundle_file, argc, argv, &pack_opts, version); ++ free(bundle_file); + return ret; } @@ builtin/bundle.c: static int cmd_bundle_create(int argc, const char **argv, cons struct bundle_header header; int bundle_fd = -1; - -+ int die_no_repo = 0; + int ret; struct option options[] = { OPT_END() @@ builtin/bundle.c: static int cmd_bundle_create(int argc, const char **argv, cons memset(&header, 0, sizeof(header)); - if ((bundle_fd = read_bundle_header(bundle_file, &header)) < 0) - return 1; -- if (!startup_info->have_repository) -- die(_("Need a repository to unbundle.")); -- return !!unbundle(the_repository, &header, bundle_fd, 0) || + if ((bundle_fd = read_bundle_header(bundle_file, &header)) < 0) { + ret = 1; + goto cleanup; + } -+ if (!startup_info->have_repository) { -+ die_no_repo = 1; -+ goto cleanup; -+ } + if (!startup_info->have_repository) + die(_("Need a repository to unbundle.")); +- return !!unbundle(the_repository, &header, bundle_fd, 0) || + ret = !!unbundle(the_repository, &header, bundle_fd, 0) || list_bundle_refs(&header, argc, argv); +cleanup: -+ if (die_no_repo) -+ die(_("Need a repository to unbundle.")); + free(bundle_file); + return ret; } 6: f297fd0432a ! 2: e47646d3a98 bundle.c: use a temporary variable for OIDs and names @@ bundle.c: int verify_bundle(struct repository *r, for (i = 0; i < p->nr; i++) { struct ref_list_entry *e = p->list + i; - struct object *o = parse_object(r, &e->oid); ++ const char *name = e->name; + struct object_id *oid = &e->oid; + struct object *o = parse_object(r, oid); if (o) { o->flags |= PREREQ_MARK; - add_pending_object(&revs, o, e->name); -@@ bundle.c: int verify_bundle(struct repository *r, +- add_pending_object(&revs, o, e->name); ++ add_pending_object(&revs, o, name); + continue; } if (++ret == 1) error("%s", message); - error("%s %s", oid_to_hex(&e->oid), e->name); -+ error("%s %s", oid_to_hex(oid), e->name); ++ error("%s %s", oid_to_hex(oid), name); } if (revs.pending.nr != p->nr) return ret; @@ bundle.c: int verify_bundle(struct repository *r, for (i = 0; i < p->nr; i++) { struct ref_list_entry *e = p->list + i; - struct object *o = parse_object(r, &e->oid); ++ const char *name = e->name; + struct object_id *oid = &e->oid; + struct object *o = parse_object(r, oid); assert(o); /* otherwise we'd have returned early */ @@ bundle.c: int verify_bundle(struct repository *r, if (++ret == 1) error("%s", message); - error("%s %s", oid_to_hex(&e->oid), e->name); -+ error("%s %s", oid_to_hex(oid), e->name); ++ error("%s %s", oid_to_hex(oid), name); } /* Clean up objects used, as they will be reused. */ @@ bundle.c: int verify_bundle(struct repository *r, ## transport.c ## @@ transport.c: static struct ref *get_refs_from_bundle(struct transport *transport, + for (i = 0; i < data->header.references.nr; i++) { struct ref_list_entry *e = data->header.references.list + i; - struct ref *ref = alloc_ref(e->name); +- struct ref *ref = alloc_ref(e->name); - oidcpy(&ref->old_oid, &e->oid); ++ const char *name = e->name; ++ struct ref *ref = alloc_ref(name); + struct object_id *oid = &e->oid; + oidcpy(&ref->old_oid, oid); ref->next = result; 7: 887313d3b02 ! 3: f1066ee1b9a bundle: remove "ref_list" in favor of string-list.c API @@ builtin/bundle.c: static int cmd_bundle_list_heads(int argc, const char **argv, - struct bundle_header header; + struct bundle_header header = BUNDLE_HEADER_INIT; int bundle_fd = -1; - int die_no_repo = 0; int ret; + struct option options[] = { @@ builtin/bundle.c: static int cmd_bundle_unbundle(int argc, const char **argv, const char *prefix) builtin_bundle_unbundle_usage, options, &bundle_file); /* bundle internals use argv[1] as further parameters */ @@ builtin/bundle.c: static int cmd_bundle_unbundle(int argc, const char **argv, co ret = 1; goto cleanup; @@ builtin/bundle.c: static int cmd_bundle_unbundle(int argc, const char **argv, const char *prefix) - } + die(_("Need a repository to unbundle.")); ret = !!unbundle(the_repository, &header, bundle_fd, 0) || list_bundle_refs(&header, argc, argv); + bundle_header_release(&header); cleanup: - if (die_no_repo) - die(_("Need a repository to unbundle.")); + free(bundle_file); + return ret; ## bundle.c ## @@ bundle.c: static struct { @@ bundle.c: static struct { -static void add_to_ref_list(const struct object_id *oid, const char *name, - struct ref_list *list) --{ ++void bundle_header_init(struct bundle_header *header) + { - ALLOC_GROW(list->list, list->nr + 1, list->alloc); - oidcpy(&list->list[list->nr].oid, oid); - list->list[list->nr].name = xstrdup(name); - list->nr++; --} -- - static int parse_capability(struct bundle_header *header, const char *capability) - { - const char *arg; -@@ bundle.c: static int parse_bundle_header(int fd, struct bundle_header *header, - /* The bundle header ends with an empty line */ - while (!strbuf_getwholeline_fd(&buf, fd, '\n') && - buf.len && buf.buf[0] != '\n') { -- struct object_id oid; -+ struct object_id *oid; - int is_prereq = 0; - const char *p; ++ memset(header, 0, sizeof(*header)); ++ string_list_init(&header->prerequisites, 1); ++ string_list_init(&header->references, 1); ++} ++ ++void bundle_header_release(struct bundle_header *header) ++{ ++ string_list_clear(&header->prerequisites, 1); ++ string_list_clear(&header->references, 1); + } + static int parse_capability(struct bundle_header *header, const char *capability) @@ bundle.c: static int parse_bundle_header(int fd, struct bundle_header *header, - * Prerequisites have object name that is optionally - * followed by SP and subject line. - */ -- if (parse_oid_hex_algop(buf.buf, &oid, &p, header->hash_algo) || -+ oid = xmalloc(sizeof(struct object_id)); -+ if (parse_oid_hex_algop(buf.buf, oid, &p, header->hash_algo) || - (*p && !isspace(*p)) || - (!is_prereq && !*p)) { - if (report_path) - error(_("unrecognized header: %s%s (%d)"), - (is_prereq ? "-" : ""), buf.buf, (int)buf.len); status = -1; -+ free(oid); break; } else { -- if (is_prereq) ++ struct object_id *dup = oiddup(&oid); + if (is_prereq) - add_to_ref_list(&oid, "", &header->prerequisites); -- else ++ string_list_append(&header->prerequisites, "")->util = dup; + else - add_to_ref_list(&oid, p + 1, &header->references); -+ const char *string = is_prereq ? "" : p + 1; -+ struct string_list *list = is_prereq -+ ? &header->prerequisites -+ : &header->references; -+ string_list_append(list, string)->util = oid; ++ string_list_append(&header->references, p + 1)->util = dup; } } @@ bundle.c: int verify_bundle(struct repository *r, repo_init_revisions(r, &revs, NULL); for (i = 0; i < p->nr; i++) { - struct ref_list_entry *e = p->list + i; +- const char *name = e->name; - struct object_id *oid = &e->oid; + struct string_list_item *e = p->items + i; ++ const char *name = e->string; + struct object_id *oid = e->util; struct object *o = parse_object(r, oid); if (o) { o->flags |= PREREQ_MARK; -- add_pending_object(&revs, o, e->name); -+ add_pending_object(&revs, o, e->string); - continue; - } - if (++ret == 1) - error("%s", message); -- error("%s %s", oid_to_hex(oid), e->name); -+ error("%s %s", oid_to_hex(oid), e->string); - } - if (revs.pending.nr != p->nr) - return ret; @@ bundle.c: int verify_bundle(struct repository *r, i--; for (i = 0; i < p->nr; i++) { - struct ref_list_entry *e = p->list + i; +- const char *name = e->name; - struct object_id *oid = &e->oid; + struct string_list_item *e = p->items + i; ++ const char *name = e->string; + const struct object_id *oid = e->util; struct object *o = parse_object(r, oid); assert(o); /* otherwise we'd have returned early */ if (o->flags & SHOWN) - continue; - if (++ret == 1) - error("%s", message); -- error("%s %s", oid_to_hex(oid), e->name); -+ error("%s %s", oid_to_hex(oid), e->string); - } +@@ bundle.c: int verify_bundle(struct repository *r, /* Clean up objects used, as they will be reused. */ for (i = 0; i < p->nr; i++) { @@ bundle.c: int verify_bundle(struct repository *r, r = &header->references; printf_ln(Q_("The bundle contains this ref:", -@@ bundle.c: int unbundle(struct repository *r, struct bundle_header *header, - return error(_("index-pack died")); - return 0; - } -+ -+void bundle_header_release(struct bundle_header *header) -+{ -+ string_list_clear(&header->prerequisites, 1); -+ string_list_clear(&header->references, 1); -+} ## bundle.h ## @@ @@ bundle.h + .prerequisites = STRING_LIST_INIT_DUP, \ + .references = STRING_LIST_INIT_DUP, \ +} ++void bundle_header_init(struct bundle_header *header); ++void bundle_header_release(struct bundle_header *header); + int is_bundle(const char *path, int quiet); int read_bundle_header(const char *path, struct bundle_header *header); int create_bundle(struct repository *r, const char *path, -@@ bundle.h: int unbundle(struct repository *r, struct bundle_header *header, - int bundle_fd, int flags); - int list_bundle_refs(struct bundle_header *header, - int argc, const char **argv); -+void bundle_header_release(struct bundle_header *header); - - #endif ## transport.c ## @@ transport.c: static struct ref *get_refs_from_bundle(struct transport *transport, @@ transport.c: static struct ref *get_refs_from_bundle(struct transport *transport for (i = 0; i < data->header.references.nr; i++) { - struct ref_list_entry *e = data->header.references.list + i; -- struct ref *ref = alloc_ref(e->name); -- struct object_id *oid = &e->oid; +- const char *name = e->name; + struct string_list_item *e = data->header.references.items + i; -+ struct ref *ref = alloc_ref(e->string); -+ const struct object_id *oid = e->util; ++ const char *name = e->string; + struct ref *ref = alloc_ref(name); +- struct object_id *oid = &e->oid; ++ struct object_id *oid = e->util; oidcpy(&ref->old_oid, oid); ref->next = result; result = ref; - } -+ string_list_clear(&data->header.references, 1); - return result; - } - @@ transport.c: static int close_bundle(struct transport *transport) struct bundle_transport_data *data = transport->data; if (data->fd > 0) @@ transport.c: struct transport *transport_get(struct remote *remote, const char * die(_("git-over-rsync is no longer supported")); } else if (url_is_local_not_ssh(url) && is_file(url) && is_bundle(url, 1)) { struct bundle_transport_data *data = xcalloc(1, sizeof(*data)); -+ string_list_init(&data->header.prerequisites, 1); -+ string_list_init(&data->header.references, 1); ++ bundle_header_init(&data->header); transport_check_allowed("file"); ret->data = data; ret->vtable = &bundle_vtable; -- 2.32.0.613.g8e17abc2eb ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v3 0/3] 2021-06-30 14:06 ` [PATCH v3 0/3] Ævar Arnfjörð Bjarmason @ 2021-06-30 17:34 ` Jeff King 2021-06-30 17:45 ` Jeff King 0 siblings, 1 reply; 136+ messages in thread From: Jeff King @ 2021-06-30 17:34 UTC (permalink / raw) To: Ævar Arnfjörð Bjarmason Cc: git, Junio C Hamano, Johannes Schindelin, Andrei Rybak On Wed, Jun 30, 2021 at 04:06:13PM +0200, Ævar Arnfjörð Bjarmason wrote: > Refactor the bundle API to use the string_list API instead of its own > version of a similar API. See [1] for v2. > > Addresses comments by Jeff King about us being too overzelous in > trying not to leak memory (the 'die_no_repo' is gone), and other > flow/style comments of his. > > I also added a bundle_header_init() function for use in transport.c, > and noticed a redundant call to string_list_clear() there. Thanks, all three look good to me. As an aside, having to have a separate bundle_header_init() and BUNDLE_HEADER_INIT is annoying (because they both must be kept up to date with each other), but quite common in our code base. I wonder if writing: void bundle_header_init(struct bundle_header *header) { struct bundle_header blank = BUNDLE_HEADER_INIT; memcpy(header, &blank, sizeof(*header)); } would let a smart enough compiler just init "header" in place without the extra copy (the performance of a single bundle_header almost certainly doesn't matter, but it might for other types). Just musing. ;) -Peff ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v3 0/3] 2021-06-30 17:34 ` Jeff King @ 2021-06-30 17:45 ` Jeff King 2021-06-30 18:00 ` Ævar Arnfjörð Bjarmason 0 siblings, 1 reply; 136+ messages in thread From: Jeff King @ 2021-06-30 17:45 UTC (permalink / raw) To: Ævar Arnfjörð Bjarmason Cc: git, Junio C Hamano, Johannes Schindelin, Andrei Rybak On Wed, Jun 30, 2021 at 01:34:07PM -0400, Jeff King wrote: > As an aside, having to have a separate bundle_header_init() and > BUNDLE_HEADER_INIT is annoying (because they both must be kept up to > date with each other), but quite common in our code base. I wonder if > writing: > > void bundle_header_init(struct bundle_header *header) > { > struct bundle_header blank = BUNDLE_HEADER_INIT; > memcpy(header, &blank, sizeof(*header)); > } > > would let a smart enough compiler just init "header" in place without > the extra copy (the performance of a single bundle_header almost > certainly doesn't matter, but it might for other types). > > Just musing. ;) For my own curiosity, the answer is yes: https://godbolt.org/z/s54dc6ss9 With "gcc -O2" the memcpy goes away and we init "header" directly. If we want to start using this technique widely, I don't think it should be part of your series, though. This probably applies to quite a few data structures, so it would make more sense to have a series which converts several. -Peff ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v3 0/3] 2021-06-30 17:45 ` Jeff King @ 2021-06-30 18:00 ` Ævar Arnfjörð Bjarmason 2021-07-01 10:53 ` Ævar Arnfjörð Bjarmason 0 siblings, 1 reply; 136+ messages in thread From: Ævar Arnfjörð Bjarmason @ 2021-06-30 18:00 UTC (permalink / raw) To: Jeff King; +Cc: git, Junio C Hamano, Johannes Schindelin, Andrei Rybak On Wed, Jun 30 2021, Jeff King wrote: > On Wed, Jun 30, 2021 at 01:34:07PM -0400, Jeff King wrote: > >> As an aside, having to have a separate bundle_header_init() and >> BUNDLE_HEADER_INIT is annoying (because they both must be kept up to >> date with each other), but quite common in our code base. I wonder if >> writing: >> >> void bundle_header_init(struct bundle_header *header) >> { >> struct bundle_header blank = BUNDLE_HEADER_INIT; >> memcpy(header, &blank, sizeof(*header)); >> } >> >> would let a smart enough compiler just init "header" in place without >> the extra copy (the performance of a single bundle_header almost >> certainly doesn't matter, but it might for other types). >> >> Just musing. ;) > > For my own curiosity, the answer is yes: https://godbolt.org/z/s54dc6ss9 > > With "gcc -O2" the memcpy goes away and we init "header" directly. > > If we want to start using this technique widely, I don't think it should > be part of your series, though. This probably applies to quite a few > data structures, so it would make more sense to have a series which > converts several. That's cool, yeah that would make quite a lot of code better. Thanks! ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v3 0/3] 2021-06-30 18:00 ` Ævar Arnfjörð Bjarmason @ 2021-07-01 10:53 ` Ævar Arnfjörð Bjarmason 0 siblings, 0 replies; 136+ messages in thread From: Ævar Arnfjörð Bjarmason @ 2021-07-01 10:53 UTC (permalink / raw) To: Jeff King; +Cc: git, Junio C Hamano, Johannes Schindelin, Andrei Rybak On Wed, Jun 30 2021, Ævar Arnfjörð Bjarmason wrote: > On Wed, Jun 30 2021, Jeff King wrote: > >> On Wed, Jun 30, 2021 at 01:34:07PM -0400, Jeff King wrote: >> >>> As an aside, having to have a separate bundle_header_init() and >>> BUNDLE_HEADER_INIT is annoying (because they both must be kept up to >>> date with each other), but quite common in our code base. I wonder if >>> writing: >>> >>> void bundle_header_init(struct bundle_header *header) >>> { >>> struct bundle_header blank = BUNDLE_HEADER_INIT; >>> memcpy(header, &blank, sizeof(*header)); >>> } >>> >>> would let a smart enough compiler just init "header" in place without >>> the extra copy (the performance of a single bundle_header almost >>> certainly doesn't matter, but it might for other types). >>> >>> Just musing. ;) >> >> For my own curiosity, the answer is yes: https://godbolt.org/z/s54dc6ss9 >> >> With "gcc -O2" the memcpy goes away and we init "header" directly. >> >> If we want to start using this technique widely, I don't think it should >> be part of your series, though. This probably applies to quite a few >> data structures, so it would make more sense to have a series which >> converts several. > > That's cool, yeah that would make quite a lot of code better. Thanks! Just for future reference: I submitted a small series to make use of this suggested idiom: https://lore.kernel.org/git/cover-0.5-00000000000-20210701T104855Z-avarab@gmail.com/ ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v2 1/3] Move the user-facing test library to test-lib-functions.sh
@ 2012-02-16 22:14 Junio C Hamano
2012-02-17 10:25 ` [PATCH v3 0/3] Thomas Rast
0 siblings, 1 reply; 136+ messages in thread
From: Junio C Hamano @ 2012-02-16 22:14 UTC (permalink / raw)
To: Thomas Rast; +Cc: git
Thomas Rast <trast@student.ethz.ch> writes:
> This just moves all the user-facing functions to a separate file and
> sources that instead.
>
> Signed-off-by: Thomas Rast <trast@student.ethz.ch>
> ---
> t/test-lib-functions.sh | 835 +++++++++++++++++++++++++++++++++++++++++++++++
> t/test-lib.sh | 552 +-------------------------------
> 2 files changed, 840 insertions(+), 547 deletions(-)
> create mode 100644 t/test-lib-functions.sh
I would have expected from the log description that the number of deleted
lines would be about the same as the number of added lines, and the
difference would primarily come from the addition of "include" aka "dot"
". ./test-lib-functions.sh" that becomes necessary in t/test-lib.sh, some
boilerplate material at the beginning of the new file e.g. "#!/bin/sh",
and copying (not moving) the same Copyright block to the new file.
But 835-552 = 283 feels way way more than that. What else is going on?
^ permalink raw reply [flat|nested] 136+ messages in thread
* [PATCH v3 0/3] 2012-02-16 22:14 [PATCH v2 1/3] Move the user-facing test library to test-lib-functions.sh Junio C Hamano @ 2012-02-17 10:25 ` Thomas Rast 2012-02-17 17:03 ` Junio C Hamano 0 siblings, 1 reply; 136+ messages in thread From: Thomas Rast @ 2012-02-17 10:25 UTC (permalink / raw) To: git; +Cc: Junio C Hamano Junio C Hamano wrote: > > --- > > t/test-lib-functions.sh | 835 +++++++++++++++++++++++++++++++++++++++++++++++ > > t/test-lib.sh | 552 +------------------------------- > > 2 files changed, 840 insertions(+), 547 deletions(-) > > create mode 100644 t/test-lib-functions.sh > > I would have expected from the log description that the number of deleted > lines would be about the same as the number of added lines, and the > difference would primarily come from the addition of "include" aka "dot" > ". ./test-lib-functions.sh" that becomes necessary in t/test-lib.sh, some > boilerplate material at the beginning of the new file e.g. "#!/bin/sh", > and copying (not moving) the same Copyright block to the new file. There were actually more mistakes lurking :-( so I am resending the whole series. I also put in the copyright that you asked for. I verified the results by looking at the diff between a reverse git-show for test-lib.sh and a forward git-show for test-lib-functions.sh, which looks as follows: --- /dev/fd/63 2012-02-17 10:55:32.994197654 +0100 +++ /dev/fd/62 2012-02-17 10:55:32.994197654 +0100 @@ -9,17 +9,29 @@ Signed-off-by: Thomas Rast <trast@student.ethz.ch> -diff --git b/t/test-lib.sh a/t/test-lib.sh -index 1da3f40..e28d5fd 100644 ---- b/t/test-lib.sh -+++ a/t/test-lib.sh -@@ -223,9 +223,248 @@ die () { - GIT_EXIT_OK= - trap 'die' EXIT - --# The user-facing functions are loaded from a separate file so that --# test_perf subshells can have them too --. "${TEST_DIRECTORY:-.}"/test-lib-functions.sh +diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh +new file mode 100644 +index 0000000..7b3b4be +--- /dev/null ++++ b/t/test-lib-functions.sh +@@ -0,0 +1,565 @@ ++#!/bin/sh ++# ++# Copyright (c) 2005 Junio C Hamano ++# ++# This program is free software: you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation, either version 2 of the License, or ++# (at your option) any later version. ++# ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program. If not, see http://www.gnu.org/licenses/ . ++ +# The semantics of the editor variables are that of invoking +# sh -c "$EDITOR \"$@\"" files ... +# @@ -192,7 +204,6 @@ + git config "$@" +} + -+ +test_config_global () { + test_when_finished "test_unconfig --global '$1'" && + git config --global "$@" @@ -262,13 +273,7 @@ + esac + return 1 +} - - # You are not expected to call test_ok_ and test_failure_ directly, use - # the text_expect_* functions instead. -@@ -313,6 +552,313 @@ test_skip () { - esac - } - ++ +test_expect_failure () { + test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq= + test "$#" = 2 || @@ -575,7 +580,3 @@ + mv .git/hooks .git/hooks-disabled + ) || exit +} -+ - test_done () { - GIT_EXIT_OK=t - Thomas Rast (3): Move the user-facing test library to test-lib-functions.sh Introduce a performance testing framework Add a performance test for git-grep Makefile | 22 +- t/Makefile | 43 ++- t/perf/.gitignore | 2 + t/perf/Makefile | 15 + t/perf/README | 146 ++++++++++ t/perf/aggregate.perl | 166 +++++++++++ t/perf/min_time.perl | 21 ++ t/perf/p0000-perf-lib-sanity.sh | 41 +++ t/perf/p0001-rev-list.sh | 17 ++ t/perf/p7810-grep.sh | 23 ++ t/perf/perf-lib.sh | 198 ++++++++++++++ t/perf/run | 82 ++++++ t/test-lib-functions.sh | 565 ++++++++++++++++++++++++++++++++++++++ t/test-lib.sh | 574 ++------------------------------------- 14 files changed, 1363 insertions(+), 552 deletions(-) create mode 100644 t/perf/.gitignore create mode 100644 t/perf/Makefile create mode 100644 t/perf/README create mode 100755 t/perf/aggregate.perl create mode 100755 t/perf/min_time.perl create mode 100755 t/perf/p0000-perf-lib-sanity.sh create mode 100755 t/perf/p0001-rev-list.sh create mode 100755 t/perf/p7810-grep.sh create mode 100644 t/perf/perf-lib.sh create mode 100755 t/perf/run create mode 100644 t/test-lib-functions.sh -- 1.7.9.1.365.ge223f ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v3 0/3] 2012-02-17 10:25 ` [PATCH v3 0/3] Thomas Rast @ 2012-02-17 17:03 ` Junio C Hamano 2012-02-17 23:28 ` Junio C Hamano 0 siblings, 1 reply; 136+ messages in thread From: Junio C Hamano @ 2012-02-17 17:03 UTC (permalink / raw) To: Thomas Rast; +Cc: git Thomas Rast <trast@student.ethz.ch> writes: > There were actually more mistakes lurking :-( so I am resending the > whole series. Ok, will requeue. The diff you attached to this cover letter looked at least halfway sane, compared to the previous round ;-), though it is not exactly clear what goes to lib-test-functions and what goes to lib-test (for example, you moved test_expect_success to 'test-functions', but it calls test_ok_ that is in 'test-lib', and test_ok_ is directly used by test_perf in the new 't/perf/perf-lib.sh'), making it harder for people to decide where to put their additions to the test infrastructure from now on. There needs a bit of description in the first patch to guide them. I seem to be getting intermittent test failures, and every time the failing tests are different, when these three are queued to 'pu'. I didn't look for what goes wrong and how. Thanks. ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v3 0/3] 2012-02-17 17:03 ` Junio C Hamano @ 2012-02-17 23:28 ` Junio C Hamano 2012-02-18 0:51 ` Jeff King 0 siblings, 1 reply; 136+ messages in thread From: Junio C Hamano @ 2012-02-17 23:28 UTC (permalink / raw) To: Thomas Rast, Jehan Bing; +Cc: git Junio C Hamano <gitster@pobox.com> writes: > Thomas Rast <trast@student.ethz.ch> writes: > ... > I seem to be getting intermittent test failures, and every time the > failing tests are different, when these three are queued to 'pu'. I didn't > look for what goes wrong and how. False alarm. I suspect that it is jb/required-filter topic that is causing intermittent failures from convert.c depending on the timing of how fast filter subprocess dies vs how fast we consume its result or something. Repeatedly running t0021 like this: $ cd t $ while sh t0021-conversion.sh ; do :; done under load seems to make it fail every once in a while. test_must_fail: died by signal: git add test.fc Are we dying on SIGPIPE or something? ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v3 0/3] 2012-02-17 23:28 ` Junio C Hamano @ 2012-02-18 0:51 ` Jeff King 2012-02-18 7:27 ` Junio C Hamano 0 siblings, 1 reply; 136+ messages in thread From: Jeff King @ 2012-02-18 0:51 UTC (permalink / raw) To: Junio C Hamano; +Cc: Thomas Rast, Jehan Bing, git On Fri, Feb 17, 2012 at 03:28:51PM -0800, Junio C Hamano wrote: > Junio C Hamano <gitster@pobox.com> writes: > > > Thomas Rast <trast@student.ethz.ch> writes: > > ... > > I seem to be getting intermittent test failures, and every time the > > failing tests are different, when these three are queued to 'pu'. I didn't > > look for what goes wrong and how. > > False alarm. I suspect that it is jb/required-filter topic that is causing > intermittent failures from convert.c depending on the timing of how fast > filter subprocess dies vs how fast we consume its result or something. > > Repeatedly running t0021 like this: > > $ cd t > $ while sh t0021-conversion.sh ; do :; done > > under load seems to make it fail every once in a while. > > test_must_fail: died by signal: git add test.fc > > Are we dying on SIGPIPE or something? I would be unsurprised if that is the case. Joey Hess mentioned similar issues with hooks a month or two ago. And I have been seeing intermittent failures of t5541 under load that I traced back to SIGPIPE. I've been meaning to dig further and come up with a good solution. Here's some previous discussion: http://article.gmane.org/gmane.comp.version-control.git/186291 I'd be happy if we just ignored SIGPIPE everywhere, but turned it on for the log family. -Peff ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v3 0/3] 2012-02-18 0:51 ` Jeff King @ 2012-02-18 7:27 ` Junio C Hamano 2012-02-18 8:52 ` Jeff King 0 siblings, 1 reply; 136+ messages in thread From: Junio C Hamano @ 2012-02-18 7:27 UTC (permalink / raw) To: Jeff King; +Cc: Thomas Rast, Jehan Bing, git Jeff King <peff@peff.net> writes: > I'd be happy if we just ignored SIGPIPE everywhere, but turned it on for > the log family. Hmmmm, now you confused me... What is special about the log family? Do you mean "when we use pager"? But then we are writing into the pager, which the user can make it exit, which in turn causes us to write into the pipe, so I would expect that we would want to ignore SIGPIPE --- ah, then we explicitly catch error in xwrite() and say die() which we do not want. So you want to let SIGPIPE silently kill us when the pager is in use; is that it? ^ permalink raw reply [flat|nested] 136+ messages in thread
* Re: [PATCH v3 0/3] 2012-02-18 7:27 ` Junio C Hamano @ 2012-02-18 8:52 ` Jeff King 0 siblings, 0 replies; 136+ messages in thread From: Jeff King @ 2012-02-18 8:52 UTC (permalink / raw) To: Junio C Hamano; +Cc: Thomas Rast, Jehan Bing, git On Fri, Feb 17, 2012 at 11:27:26PM -0800, Junio C Hamano wrote: > > I'd be happy if we just ignored SIGPIPE everywhere, but turned it on for > > the log family. > > Hmmmm, now you confused me... What is special about the log family? Do > you mean "when we use pager"? But then we are writing into the pager, > which the user can make it exit, which in turn causes us to write into the > pipe, so I would expect that we would want to ignore SIGPIPE --- ah, then > we explicitly catch error in xwrite() and say die() which we do not want. Sort of. I mentioned the log family because those are the commands that tend to generate large amounts of input, and which are likely to hit SIGPIPE. But let me elaborate on my thinking a bit. There are basically three types of writing callsites in git: 1. Careful calls to write() or fwrite(). These are the majority of calls. In these, we check the return value of write() and die, return the error code, or whatever is appropriate for the callsite. SIGPIPE kills the program before the careful code gets the chance to make a decision about what to do. At best, this is a nuisance; even if the program is going to die(), it's likely that the custom code could produce a useful error message. At worst it causes bugs. For example, we may write to a subprocess that only needs part of our input to make a decision (e.g., some hooks and credential helpers). With SIGPIPE, we end up dying even though no error has occurred. Worse, these are often annoying heisenbugs that depend on the timing of write() and close() between the two processes. 2. Non-careful calls of small, fixed-size output. Things like "git remote" will write some output to stdout via printf and not bother checking the return code. As the main purpose of the program is to create output, it's OK to be killed by SIGPIPE if it happens due to a write to stdout. But respecting SIGPIPE doesn't buy as all that much. It might save us from making a few write() calls that will go to nowhere, but most of the work has already been done by the time we are outputting. And it has a cost to the other careful write calls in the same program, because respecting SIGPIPE is a per-process thing. So for something like "git remote show foo", while we would like SIGPIPE to kick in for output to stdout, we would not want it for a pipe we opened to talk to ls-remote. 3. Non-careful calls of large, streaming data. Commands like "git log" will non-carefully output to stdout, as well, but they will generate tons of data, consuming possibly minutes of CPU time (e.g., "git log -p"). If whoever is reading the output stops doing so, we really want to kill the program to avoid wasting CPU time. In this instance, SIGPIPE is a big win. It still has the downside that careful calls in the same program will be subject to using SIGPIPE. For "log" and friends, this is probably OK with the current code, as we don't make a lot of pipes. But that is somewhat of an implementation detail. E.g., "git log -p" with external diff or textconv writes to a tempfile, and then runs a subprocess with the tempfile as input. But we could just as easily have used pipes, and may choose to do so in the future. You may even be able to trigger a convert_to_git filter in the current code, which does use pipes. So basically, I find SIGPIPE to be a simplistic too-heavy hammer that ends up affecting all writes to pipes, when in reality we are only interested in affecting ones to some "main" output (usually stdout). That works OK for small Unix-y programs like "head", but is overly simplistic for something as big as git. In an ideal world, we could set a per-descriptor version of SIGPIPE, and just turn it on for stdout (or somehow find out which descriptor caused the SIGPIPE after the fact). But that's not possible. So our next best thing would be to actually check the results of these non-careful writes. Unfortunately, this means either: a. wrapping every printf with a function that will appropriately die on error. This makes the code more cumbersome. or b. occasionally checking ferror(stdout) while doing long streams (e.g., checking it after each commit is written in git log, and aborting if we saw a write error). This is less cumbersome, but it does mean that errno may or may not still be accurate by the time we notice the error. So it's hard to reliably differentiate EPIPE from other errors. It would be nice, for example, to have git log exit silently on EPIPE, but properly print an error for something like ENOSPC. But perhaps that isn't a big deal, as I believe right now that we would silently ignore something like ENOSPC. Less robust than that is to just ignore SIGPIPE in most git programs (which don't benefit from it, and where it is only a liability), but then manually enable it for the few that care (the log family, and perhaps diff. Maybe things like "git tag -l", though that output tends to be pretty small). But I think it would work OK in practice, because those commands don't tend to make pipes other than the "main" output. And it's quite easy to implement. > So you want to let SIGPIPE silently kill us when the pager is in use; is > that it? That's an OK heuristic, as the pager being in use is a good sign that we will generate long, streaming output. It has two downsides, though. One is that it suffers from the too-heavy hammer of the preceding paragraph. The other is that it only catches when _we_ create the pipe. You would also want to catch something like: git rev-list | head and stop the traversal. So I think it is less about whether a pager is in use and more about whether we are creating long output that would benefit from being cut off early. The pager is an indicator of that, but it's not a perfect one; I think we'd do better to mark those spots manually (i.e., by re-enabling SIGPIPE in commands that we deem appropriate). -Peff ^ permalink raw reply [flat|nested] 136+ messages in thread
end of thread, other threads:[~2021-07-01 10:53 UTC | newest] Thread overview: 136+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- [not found] <"<CAC05386q2iGoiJ_fRgwoOTF23exEN2D1+oh4VjajEvYQ58O1TQ@mail.gmail.com> 2018-09-27 15:13 ` [PATCH] branch: colorize branches checked out in a linked working tree the same way as the current branch is colorized Nickolai Belakovski 2018-09-27 15:33 ` Ævar Arnfjörð Bjarmason 2018-09-27 17:46 ` Nickolai Belakovski [not found] ` <CAC05387S9P+w8yqqcjkQDnURYSgQmqtukxS4KvqJu-kDA+_o0g@mail.gmail.com> 2018-09-27 17:59 ` Ævar Arnfjörð Bjarmason 2018-10-02 20:41 ` Johannes Schindelin 2018-09-27 17:58 ` Duy Nguyen 2018-09-27 18:17 ` Jeff King 2018-09-27 18:39 ` Nickolai Belakovski 2018-09-27 18:51 ` Jeff King 2018-09-27 19:28 ` Rafael Ascensão 2018-09-27 19:35 ` Jeff King 2018-09-27 19:41 ` Jeff King 2018-09-27 21:22 ` Junio C Hamano 2018-09-28 1:05 ` Jeff King 2018-09-28 1:28 ` Junio C Hamano 2018-09-27 21:35 ` Rafael Ascensão 2018-09-28 1:07 ` Jeff King 2018-09-27 20:02 ` Ævar Arnfjörð Bjarmason 2018-09-27 20:16 ` Nickolai Belakovski 2018-09-27 20:40 ` Rafael Ascensão 2018-11-11 23:58 ` [PATCH v2 0/2] refactoring branch colorization to ref-filter nbelakovski 2018-11-11 23:58 ` [PATCH v2 1/2] ref-filter: add worktree atom nbelakovski 2018-11-12 10:11 ` Junio C Hamano 2018-11-12 12:22 ` Jeff King 2018-11-13 1:38 ` Junio C Hamano 2018-11-21 14:05 ` Nickolai Belakovski 2018-11-21 14:08 ` Jeff King 2018-11-12 12:23 ` Jeff King 2018-11-11 23:58 ` [PATCH v2 2/2] branch: Mark and colorize a branch differently if it is checked out in a linked worktree nbelakovski 2018-11-12 10:20 ` Junio C Hamano 2018-11-12 12:14 ` Jeff King 2018-11-12 18:07 ` Rafael Ascensão 2018-11-13 1:45 ` Junio C Hamano 2018-11-13 14:49 ` Jeff King 2018-11-21 14:07 ` Nickolai Belakovski 2018-12-16 21:57 ` [PATCH v3 0/3] nbelakovski 2018-12-16 21:57 ` [PATCH v3 1/3] ref-filter: add worktreepath atom nbelakovski 2018-12-18 17:22 ` Jeff King 2018-12-20 7:09 ` Nickolai Belakovski 2018-12-20 14:59 ` Jeff King 2018-12-24 8:47 ` [PATCH v4 0/3] nbelakovski 2018-12-24 8:47 ` [PATCH v4 1/3] ref-filter: add worktreepath atom nbelakovski 2019-01-03 5:40 ` Jeff King 2019-01-03 9:31 ` Eric Sunshine 2018-12-24 8:47 ` [PATCH v4 2/3] branch: Mark and color a branch differently if it is checked out in a linked worktree nbelakovski 2018-12-24 8:47 ` [PATCH v4 3/3] branch: Add an extra verbose output displaying worktree path for refs " nbelakovski 2019-01-03 5:42 ` Jeff King 2019-01-03 5:22 ` [PATCH v4 0/3] Jeff King 2018-12-16 21:57 ` [PATCH v3 2/3] branch: Mark and color a branch differently if it is checked out in a linked worktree nbelakovski 2018-12-16 21:57 ` [PATCH v3 3/3] branch: Add an extra verbose output displaying worktree path for refs " nbelakovski 2018-12-18 17:25 ` [PATCH v3 0/3] Jeff King 2019-01-06 0:26 ` [PATCH v5 0/3] nbelakovski 2019-01-06 0:26 ` [PATCH v5 1/3] ref-filter: add worktreepath atom nbelakovski 2019-01-07 18:20 ` Junio C Hamano 2019-01-18 22:17 ` Nickolai Belakovski 2019-01-18 22:20 ` Nickolai Belakovski 2019-01-06 0:26 ` [PATCH v5 2/3] branch: Mark and color a branch differently if it is checked out in a linked worktree nbelakovski 2019-01-07 19:04 ` Junio C Hamano 2019-01-10 21:42 ` Philip Oakley 2019-01-13 1:41 ` Nickolai Belakovski 2019-01-14 18:18 ` Junio C Hamano 2019-01-18 22:18 ` Nickolai Belakovski 2019-01-06 0:26 ` [PATCH v5 3/3] branch: Add an extra verbose output displaying worktree path for refs " nbelakovski 2019-01-07 19:09 ` Junio C Hamano 2019-01-22 23:22 ` [PATCH v6 0/3] nbelakovski 2019-01-22 23:22 ` [PATCH v6 1/3] ref-filter: add worktreepath atom nbelakovski 2019-01-23 18:57 ` Junio C Hamano 2019-01-23 23:34 ` Nickolai Belakovski 2019-01-24 18:26 ` Junio C Hamano 2019-01-24 18:32 ` Jeff King 2019-01-24 19:27 ` Junio C Hamano 2019-01-24 19:34 ` Jeff King 2019-01-24 19:30 ` Junio C Hamano 2019-01-24 21:26 ` Jeff King 2019-01-31 20:53 ` Nickolai Belakovski 2019-01-31 23:21 ` Jeff King 2019-01-31 21:42 ` Junio C Hamano 2019-01-31 23:20 ` Jeff King 2019-01-22 23:23 ` [PATCH v6 2/3] branch: Mark and color a branch differently if it is checked out in a linked worktree nbelakovski 2019-01-22 23:23 ` [PATCH v6 3/3] branch: Add an extra verbose output displaying worktree path for refs " nbelakovski 2019-02-01 22:04 ` [PATCH v7 0/3] nbelakovski 2019-02-01 22:28 ` Junio C Hamano 2019-02-01 23:31 ` Nickolai Belakovski 2019-02-01 22:04 ` [PATCH v7 1/3] ref-filter: add worktreepath atom nbelakovski 2019-02-01 22:20 ` Eric Sunshine 2019-02-01 22:41 ` Nickolai Belakovski 2019-02-04 18:14 ` Junio C Hamano 2019-02-18 10:09 ` Nickolai Belakovski 2019-02-01 22:31 ` Junio C Hamano 2019-02-01 22:04 ` [PATCH v7 2/3] branch: Mark and color a branch differently if it is checked out in a linked worktree nbelakovski 2019-02-01 22:34 ` Junio C Hamano 2019-02-01 23:12 ` Nickolai Belakovski 2019-02-04 18:18 ` Junio C Hamano 2019-02-01 22:04 ` [PATCH v7 3/3] branch: Add an extra verbose output displaying worktree path for refs " nbelakovski 2019-02-01 22:27 ` Eric Sunshine 2019-02-01 22:45 ` Nickolai Belakovski 2019-02-01 22:53 ` Junio C Hamano 2019-02-01 23:06 ` Nickolai Belakovski 2019-02-02 1:22 ` [RFC] Sample of test for git branch -vv nbelakovski 2019-02-19 8:31 ` [PATCH v8 0/3] nbelakovski 2019-02-19 8:31 ` [PATCH v8 1/3] ref-filter: add worktreepath atom nbelakovski 2019-02-19 8:31 ` [PATCH v8 2/3] branch: update output to include worktree info nbelakovski 2019-02-21 12:44 ` Jeff King 2019-03-14 5:45 ` Nickolai Belakovski 2019-02-19 8:31 ` [PATCH v8 3/3] branch: add worktree info on verbose output nbelakovski 2019-02-21 12:59 ` Jeff King 2019-03-14 5:58 ` Nickolai Belakovski 2019-03-18 2:12 ` Junio C Hamano 2019-02-21 12:36 ` [PATCH v8 0/3] Jeff King 2019-03-14 6:10 ` Nickolai Belakovski 2019-03-16 1:38 ` [PATCH v9 0/3] nbelakovski 2019-03-16 1:38 ` [PATCH v9 1/3] ref-filter: add worktreepath atom nbelakovski 2019-03-16 1:38 ` [PATCH v9 2/3] branch: update output to include worktree info nbelakovski 2019-03-16 1:38 ` [PATCH v9 3/3] branch: add worktree info on verbose output nbelakovski 2019-03-18 12:10 ` SZEDER Gábor 2019-03-18 5:30 ` [PATCH v9 0/3] Junio C Hamano 2019-04-29 5:19 ` [PATCH v10 0/3] nbelakovski 2019-04-29 5:19 ` [PATCH v10 1/3] ref-filter: add worktreepath atom nbelakovski 2019-04-29 5:19 ` [PATCH v10 2/3] branch: update output to include worktree info nbelakovski 2019-04-29 5:19 ` [PATCH v10 3/3] branch: add worktree info on verbose output nbelakovski 2019-04-29 14:12 ` [PATCH v10 0/3] SZEDER Gábor 2019-04-29 19:27 ` Nickolai Belakovski 2019-04-29 20:57 ` Johannes Schindelin 2019-04-29 21:33 ` Nickolai Belakovski [not found] ` <CAC05385Mc7pqiCd5mb+1c4WM+v7K=h=GMHuvkw9xizhRFJXXBA@mail.gmail.com> 2019-04-30 21:46 ` Johannes Schindelin 2021-06-21 15:16 [PATCH v2 0/3] bundle.c: remove "ref_list" in favor of string-list.c API Ævar Arnfjörð Bjarmason 2021-06-30 14:06 ` [PATCH v3 0/3] Ævar Arnfjörð Bjarmason 2021-06-30 17:34 ` Jeff King 2021-06-30 17:45 ` Jeff King 2021-06-30 18:00 ` Ævar Arnfjörð Bjarmason 2021-07-01 10:53 ` Ævar Arnfjörð Bjarmason -- strict thread matches above, loose matches on Subject: below -- 2012-02-16 22:14 [PATCH v2 1/3] Move the user-facing test library to test-lib-functions.sh Junio C Hamano 2012-02-17 10:25 ` [PATCH v3 0/3] Thomas Rast 2012-02-17 17:03 ` Junio C Hamano 2012-02-17 23:28 ` Junio C Hamano 2012-02-18 0:51 ` Jeff King 2012-02-18 7:27 ` Junio C Hamano 2012-02-18 8:52 ` Jeff King
Code repositories for project(s) associated with this public inbox https://80x24.org/mirrors/git.git 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 read-only IMAP folder(s) and NNTP newsgroup(s).