* [PATCH 0/2] Fix --short and --porcelain options for commit @ 2018-04-18 3:06 Samuel Lijin 2018-04-18 3:06 ` [PATCH 1/2] commit: fix --short and --porcelain Samuel Lijin ` (4 more replies) 0 siblings, 5 replies; 26+ messages in thread From: Samuel Lijin @ 2018-04-18 3:06 UTC (permalink / raw) To: git; +Cc: Samuel Lijin Hi all - I last contributed about a year ago and I've finally found the time to start contributing again, and hopefully I'll stick around this time. Figured I'd start with something small :) Samuel Lijin (2): commit: fix --short and --porcelain wt-status: const-ify all printf helper methods t/t7501-commit.sh | 4 ++-- wt-status.c | 57 +++++++++++++++++++++++++++++++++++-------------------- wt-status.h | 4 ++-- 3 files changed, 40 insertions(+), 25 deletions(-) -- 2.16.2 ^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH 1/2] commit: fix --short and --porcelain 2018-04-18 3:06 [PATCH 0/2] Fix --short and --porcelain options for commit Samuel Lijin @ 2018-04-18 3:06 ` Samuel Lijin 2018-04-18 18:38 ` Martin Ågren 2018-04-20 7:08 ` Eric Sunshine 2018-04-18 3:06 ` [PATCH 2/2] wt-status: const-ify all printf helper methods Samuel Lijin ` (3 subsequent siblings) 4 siblings, 2 replies; 26+ messages in thread From: Samuel Lijin @ 2018-04-18 3:06 UTC (permalink / raw) To: git; +Cc: Samuel Lijin Make invoking `git commit` with `--short` or `--porcelain` return status code zero when there is something to commit. Mark the commitable flag in the wt_status object in the call to `wt_status_collect()`, instead of in `wt_longstatus_print_updated()`, and simplify the logic in the latter function to take advantage of the logic shifted to the former. Signed-off-by: Samuel Lijin <sxlijin@gmail.com> --- t/t7501-commit.sh | 4 ++-- wt-status.c | 39 +++++++++++++++++++++++++++------------ 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh index fa61b1a4e..85a8217fd 100755 --- a/t/t7501-commit.sh +++ b/t/t7501-commit.sh @@ -87,12 +87,12 @@ test_expect_success '--dry-run with stuff to commit returns ok' ' git commit -m next -a --dry-run ' -test_expect_failure '--short with stuff to commit returns ok' ' +test_expect_success '--short with stuff to commit returns ok' ' echo bongo bongo bongo >>file && git commit -m next -a --short ' -test_expect_failure '--porcelain with stuff to commit returns ok' ' +test_expect_success '--porcelain with stuff to commit returns ok' ' echo bongo bongo bongo >>file && git commit -m next -a --porcelain ' diff --git a/wt-status.c b/wt-status.c index 50815e5fa..26b0a6221 100644 --- a/wt-status.c +++ b/wt-status.c @@ -718,6 +718,19 @@ static void wt_status_collect_untracked(struct wt_status *s) s->untracked_in_ms = (getnanotime() - t_begin) / 1000000; } +static void wt_status_mark_commitable(struct wt_status *s) { + int i; + + for (i = 0; i < s->change.nr; i++) { + struct wt_status_change_data *d = (s->change.items[i]).util; + + if (d->index_status && d->index_status != DIFF_STATUS_UNMERGED) { + s->commitable = 1; + return; + } + } +} + void wt_status_collect(struct wt_status *s) { wt_status_collect_changes_worktree(s); @@ -726,7 +739,10 @@ void wt_status_collect(struct wt_status *s) wt_status_collect_changes_initial(s); else wt_status_collect_changes_index(s); + wt_status_collect_untracked(s); + + wt_status_mark_commitable(s); } static void wt_longstatus_print_unmerged(struct wt_status *s) @@ -754,26 +770,25 @@ static void wt_longstatus_print_unmerged(struct wt_status *s) static void wt_longstatus_print_updated(struct wt_status *s) { - int shown_header = 0; - int i; + if (!s->commitable) { + return; + } + + wt_longstatus_print_cached_header(s); + int i; for (i = 0; i < s->change.nr; i++) { struct wt_status_change_data *d; struct string_list_item *it; it = &(s->change.items[i]); d = it->util; - if (!d->index_status || - d->index_status == DIFF_STATUS_UNMERGED) - continue; - if (!shown_header) { - wt_longstatus_print_cached_header(s); - s->commitable = 1; - shown_header = 1; + if (d->index_status && + d->index_status != DIFF_STATUS_UNMERGED) { + wt_longstatus_print_change_data(s, WT_STATUS_UPDATED, it); } - wt_longstatus_print_change_data(s, WT_STATUS_UPDATED, it); } - if (shown_header) - wt_longstatus_print_trailer(s); + + wt_longstatus_print_trailer(s); } /* -- 2.16.2 ^ permalink raw reply related [flat|nested] 26+ messages in thread
* Re: [PATCH 1/2] commit: fix --short and --porcelain 2018-04-18 3:06 ` [PATCH 1/2] commit: fix --short and --porcelain Samuel Lijin @ 2018-04-18 18:38 ` Martin Ågren [not found] ` <CAJZjrdW3X8eaSit85otKV2HvHmu0NDGcnnnrtxHME03q=eWW-Q@mail.gmail.com> 2018-04-20 7:08 ` Eric Sunshine 1 sibling, 1 reply; 26+ messages in thread From: Martin Ågren @ 2018-04-18 18:38 UTC (permalink / raw) To: Samuel Lijin; +Cc: Git Mailing List Hi Samuel, Welcome back. :-) On 18 April 2018 at 05:06, Samuel Lijin <sxlijin@gmail.com> wrote: > Make invoking `git commit` with `--short` or `--porcelain` return status > code zero when there is something to commit. > > Mark the commitable flag in the wt_status object in the call to > `wt_status_collect()`, instead of in `wt_longstatus_print_updated()`, > and simplify the logic in the latter function to take advantage of the > logic shifted to the former. The subject is sort of vague about what is being fixed. Maybe "commit: fix return code of ...", or "wt-status: set `commitable` when collecting, not when printing". Or something... I can't come up with something brilliant off the top of my head. I did not understand the first paragraph until I had read the second and peaked at the code. Maybe tell the story the other way around? Something like this: Mark the `commitable` flag in the wt_status object in `wt_status_collect()`, instead of in `wt_longstatus_print_updated()`, and simplify the logic in the latter function to take advantage of the logic shifted to the former. This means that callers do need to actually use the printer function to collect the `commitable` flag -- it is sufficient to call `wt_status_collect()`. As a result, invoking `git commit` with `--short` or `--porcelain` results in return status code zero when there is something to commit. This fixes two bugs documented in our test suite. > t/t7501-commit.sh | 4 ++-- > wt-status.c | 39 +++++++++++++++++++++++++++------------ > 2 files changed, 29 insertions(+), 14 deletions(-) I tried to find somewhere in the documentation where this bug was described (git-commit.txt or git-status.txt), but failed. So there should be nothing to update there. > +static void wt_status_mark_commitable(struct wt_status *s) { > + int i; > + > + for (i = 0; i < s->change.nr; i++) { > + struct wt_status_change_data *d = (s->change.items[i]).util; > + > + if (d->index_status && d->index_status != DIFF_STATUS_UNMERGED) { > + s->commitable = 1; > + return; > + } > + } > +} This helper does exactly what the old code did inside `wt_longstatus_print_updated()` with regards to `commitable`. Ok. This function does not reset `commitable` to 0, so reusing a `struct wt_status` won't necessarily work out. I have not thought about whether such a caller would be horribly broken for other reasons... > void wt_status_collect(struct wt_status *s) > { > wt_status_collect_changes_worktree(s); > @@ -726,7 +739,10 @@ void wt_status_collect(struct wt_status *s) > wt_status_collect_changes_initial(s); > else > wt_status_collect_changes_index(s); > + > wt_status_collect_untracked(s); > + > + wt_status_mark_commitable(s); > } So whenever we `..._collect()`, `commitable` is set for us. This is the only caller of the new helper, so in order to be able to trust `commitable`, one needs to call `wt_status_collect()`. Seems a reasonable assumption to make that the caller will remember to do so before printing. (And all current users do, so we're not regressing in some user.) > static void wt_longstatus_print_unmerged(struct wt_status *s) > @@ -754,26 +770,25 @@ static void wt_longstatus_print_unmerged(struct wt_status *s) > > static void wt_longstatus_print_updated(struct wt_status *s) > { > - int shown_header = 0; > - int i; > + if (!s->commitable) { > + return; > + } Regarding my comment above: If you forget to `..._collect()` first, this function is a no-op. > + > + wt_longstatus_print_cached_header(s); > > + int i; You should leave this variable declaration at the top of the function. > for (i = 0; i < s->change.nr; i++) { > struct wt_status_change_data *d; > struct string_list_item *it; > it = &(s->change.items[i]); > d = it->util; > - if (!d->index_status || > - d->index_status == DIFF_STATUS_UNMERGED) > - continue; > - if (!shown_header) { > - wt_longstatus_print_cached_header(s); > - s->commitable = 1; > - shown_header = 1; > + if (d->index_status && > + d->index_status != DIFF_STATUS_UNMERGED) { > + wt_longstatus_print_change_data(s, WT_STATUS_UPDATED, it); > } > - wt_longstatus_print_change_data(s, WT_STATUS_UPDATED, it); > } > - if (shown_header) > - wt_longstatus_print_trailer(s); > + > + wt_longstatus_print_trailer(s); > } This rewrite matches the original logic, assuming we can trust `commitable`. The result is a function called `print()` which does not modify the struct it is given for printing. Nice. So you can make the argument a `const struct wt_status *`. Except this function uses helpers that are missing the `const`. You fix that in patch 2/2. I would probably have made that patch as 1/2, then done this patch as 2/2 ending the commit message with something like "As a result, we can mark the argument as `const`.", or even just silently inserting the `const` for this one function. Just a thought. Martin ^ permalink raw reply [flat|nested] 26+ messages in thread
[parent not found: <CAJZjrdW3X8eaSit85otKV2HvHmu0NDGcnnnrtxHME03q=eWW-Q@mail.gmail.com>]
* Re: [PATCH 1/2] commit: fix --short and --porcelain [not found] ` <CAJZjrdW3X8eaSit85otKV2HvHmu0NDGcnnnrtxHME03q=eWW-Q@mail.gmail.com> @ 2018-04-19 3:55 ` Samuel Lijin 0 siblings, 0 replies; 26+ messages in thread From: Samuel Lijin @ 2018-04-19 3:55 UTC (permalink / raw) To: Martin Ågren, git@vger.kernel.org On Wed, Apr 18, 2018 at 8:55 PM, Samuel Lijin <sxlijin@gmail.com> wrote: > Thanks for the quick review! > > On Wed, Apr 18, 2018 at 11:38 AM, Martin Ågren <martin.agren@gmail.com> wrote: >> Hi Samuel, >> >> Welcome back. :-) >> >> On 18 April 2018 at 05:06, Samuel Lijin <sxlijin@gmail.com> wrote: >>> Make invoking `git commit` with `--short` or `--porcelain` return status >>> code zero when there is something to commit. >>> >>> Mark the commitable flag in the wt_status object in the call to >>> `wt_status_collect()`, instead of in `wt_longstatus_print_updated()`, >>> and simplify the logic in the latter function to take advantage of the >>> logic shifted to the former. >> >> The subject is sort of vague about what is being fixed. Maybe "commit: >> fix return code of ...", or "wt-status: set `commitable` when >> collecting, not when printing". Or something... I can't come up with >> something brilliant off the top of my head. >> >> I did not understand the first paragraph until I had read the second and >> peaked at the code. Maybe tell the story the other way around? Something >> like this: >> >> Mark the `commitable` flag in the wt_status object in >> `wt_status_collect()`, instead of in `wt_longstatus_print_updated()`, >> and simplify the logic in the latter function to take advantage of the >> logic shifted to the former. >> >> This means that callers do need to actually use the printer function >> to collect the `commitable` flag -- it is sufficient to call >> `wt_status_collect()`. >> >> As a result, invoking `git commit` with `--short` or `--porcelain` >> results in return status code zero when there is something to commit. >> This fixes two bugs documented in our test suite. > > That definitely works better. Will fix when I reroll. > >>> t/t7501-commit.sh | 4 ++-- >>> wt-status.c | 39 +++++++++++++++++++++++++++------------ >>> 2 files changed, 29 insertions(+), 14 deletions(-) >> >> I tried to find somewhere in the documentation where this bug was >> described (git-commit.txt or git-status.txt), but failed. So there >> should be nothing to update there. >> >>> +static void wt_status_mark_commitable(struct wt_status *s) { >>> + int i; >>> + >>> + for (i = 0; i < s->change.nr; i++) { >>> + struct wt_status_change_data *d = (s->change.items[i]).util; >>> + >>> + if (d->index_status && d->index_status != DIFF_STATUS_UNMERGED) { >>> + s->commitable = 1; >>> + return; >>> + } >>> + } >>> +} >> >> This helper does exactly what the old code did inside >> `wt_longstatus_print_updated()` with regards to `commitable`. Ok. >> >> This function does not reset `commitable` to 0, so reusing a `struct >> wt_status` won't necessarily work out. I have not thought about whether >> such a caller would be horribly broken for other reasons... >> >>> void wt_status_collect(struct wt_status *s) >>> { >>> wt_status_collect_changes_worktree(s); >>> @@ -726,7 +739,10 @@ void wt_status_collect(struct wt_status *s) >>> wt_status_collect_changes_initial(s); >>> else >>> wt_status_collect_changes_index(s); >>> + >>> wt_status_collect_untracked(s); >>> + >>> + wt_status_mark_commitable(s); >>> } >> >> So whenever we `..._collect()`, `commitable` is set for us. This is the >> only caller of the new helper, so in order to be able to trust >> `commitable`, one needs to call `wt_status_collect()`. Seems a >> reasonable assumption to make that the caller will remember to do so >> before printing. (And all current users do, so we're not regressing in >> some user.) >> >>> static void wt_longstatus_print_unmerged(struct wt_status *s) >>> @@ -754,26 +770,25 @@ static void wt_longstatus_print_unmerged(struct wt_status *s) >>> >>> static void wt_longstatus_print_updated(struct wt_status *s) >>> { >>> - int shown_header = 0; >>> - int i; >>> + if (!s->commitable) { >>> + return; >>> + } >> >> Regarding my comment above: If you forget to `..._collect()` first, this >> function is a no-op. >> >>> + >>> + wt_longstatus_print_cached_header(s); >>> >>> + int i; >> >> You should leave this variable declaration at the top of the function. >> >>> for (i = 0; i < s->change.nr; i++) { >>> struct wt_status_change_data *d; >>> struct string_list_item *it; >>> it = &(s->change.items[i]); >>> d = it->util; >>> - if (!d->index_status || >>> - d->index_status == DIFF_STATUS_UNMERGED) >>> - continue; >>> - if (!shown_header) { >>> - wt_longstatus_print_cached_header(s); >>> - s->commitable = 1; >>> - shown_header = 1; >>> + if (d->index_status && >>> + d->index_status != DIFF_STATUS_UNMERGED) { >>> + wt_longstatus_print_change_data(s, WT_STATUS_UPDATED, it); >>> } >>> - wt_longstatus_print_change_data(s, WT_STATUS_UPDATED, it); >>> } >>> - if (shown_header) >>> - wt_longstatus_print_trailer(s); >>> + >>> + wt_longstatus_print_trailer(s); >>> } >> >> This rewrite matches the original logic, assuming we can trust >> `commitable`. The result is a function called `print()` which does not >> modify the struct it is given for printing. Nice. So you can make the >> argument a `const struct wt_status *`. Except this function uses helpers >> that are missing the `const`. >> >> You fix that in patch 2/2. I would probably have made that patch as 1/2, >> then done this patch as 2/2 ending the commit message with something >> like "As a result, we can mark the argument as `const`.", or even just >> silently inserting the `const` for this one function. Just a thought. > > I originally ordered it the way I did because in the constify-first > scenario, "fix t7501" and "const-ify wt_longstatus_print_updated" > seemed like two logically separate patches to me (which would have > made the patch series three patches instead of two). I'm happy to > reroll in whichever fashion if people care strongly though. > >> Martin ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 1/2] commit: fix --short and --porcelain 2018-04-18 3:06 ` [PATCH 1/2] commit: fix --short and --porcelain Samuel Lijin 2018-04-18 18:38 ` Martin Ågren @ 2018-04-20 7:08 ` Eric Sunshine 1 sibling, 0 replies; 26+ messages in thread From: Eric Sunshine @ 2018-04-20 7:08 UTC (permalink / raw) To: Samuel Lijin; +Cc: Git List On Tue, Apr 17, 2018 at 11:06 PM, Samuel Lijin <sxlijin@gmail.com> wrote: > Make invoking `git commit` with `--short` or `--porcelain` return status > code zero when there is something to commit. > > Mark the commitable flag in the wt_status object in the call to > `wt_status_collect()`, instead of in `wt_longstatus_print_updated()`, > and simplify the logic in the latter function to take advantage of the > logic shifted to the former. > > Signed-off-by: Samuel Lijin <sxlijin@gmail.com> > --- > diff --git a/wt-status.c b/wt-status.c > @@ -754,26 +770,25 @@ static void wt_longstatus_print_unmerged(struct wt_status *s) > static void wt_longstatus_print_updated(struct wt_status *s) > { > - int shown_header = 0; > - int i; > + if (!s->commitable) { > + return; > + } > + > + wt_longstatus_print_cached_header(s); > > + int i; > for (i = 0; i < s->change.nr; i++) { Declaration after statement: Declare 'i' at the top of the function as it was before this patch. ^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH 2/2] wt-status: const-ify all printf helper methods 2018-04-18 3:06 [PATCH 0/2] Fix --short and --porcelain options for commit Samuel Lijin 2018-04-18 3:06 ` [PATCH 1/2] commit: fix --short and --porcelain Samuel Lijin @ 2018-04-18 3:06 ` Samuel Lijin 2018-04-26 9:25 ` [PATCH v2 0/2] Fix --short and --porcelain options for commit Samuel Lijin ` (2 subsequent siblings) 4 siblings, 0 replies; 26+ messages in thread From: Samuel Lijin @ 2018-04-18 3:06 UTC (permalink / raw) To: git; +Cc: Samuel Lijin Change the method signatures of all printf helper methods to take a `const struct wt_status *` rather than a `struct wt_status *`. Signed-off-by: Samuel Lijin <sxlijin@gmail.com> --- wt-status.c | 18 +++++++++--------- wt-status.h | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/wt-status.c b/wt-status.c index 26b0a6221..55d29bc09 100644 --- a/wt-status.c +++ b/wt-status.c @@ -33,7 +33,7 @@ static char default_wt_status_colors[][COLOR_MAXLEN] = { GIT_COLOR_NIL, /* WT_STATUS_ONBRANCH */ }; -static const char *color(int slot, struct wt_status *s) +static const char *color(int slot, const struct wt_status *s) { const char *c = ""; if (want_color(s->use_color)) @@ -43,7 +43,7 @@ static const char *color(int slot, struct wt_status *s) return c; } -static void status_vprintf(struct wt_status *s, int at_bol, const char *color, +static void status_vprintf(const struct wt_status *s, int at_bol, const char *color, const char *fmt, va_list ap, const char *trail) { struct strbuf sb = STRBUF_INIT; @@ -89,7 +89,7 @@ static void status_vprintf(struct wt_status *s, int at_bol, const char *color, strbuf_release(&sb); } -void status_printf_ln(struct wt_status *s, const char *color, +void status_printf_ln(const struct wt_status *s, const char *color, const char *fmt, ...) { va_list ap; @@ -99,7 +99,7 @@ void status_printf_ln(struct wt_status *s, const char *color, va_end(ap); } -void status_printf(struct wt_status *s, const char *color, +void status_printf(const struct wt_status *s, const char *color, const char *fmt, ...) { va_list ap; @@ -109,7 +109,7 @@ void status_printf(struct wt_status *s, const char *color, va_end(ap); } -static void status_printf_more(struct wt_status *s, const char *color, +static void status_printf_more(const struct wt_status *s, const char *color, const char *fmt, ...) { va_list ap; @@ -192,7 +192,7 @@ static void wt_longstatus_print_unmerged_header(struct wt_status *s) status_printf_ln(s, c, "%s", ""); } -static void wt_longstatus_print_cached_header(struct wt_status *s) +static void wt_longstatus_print_cached_header(const struct wt_status *s) { const char *c = color(WT_STATUS_HEADER, s); @@ -239,7 +239,7 @@ static void wt_longstatus_print_other_header(struct wt_status *s, status_printf_ln(s, c, "%s", ""); } -static void wt_longstatus_print_trailer(struct wt_status *s) +static void wt_longstatus_print_trailer(const struct wt_status *s) { status_printf_ln(s, color(WT_STATUS_HEADER, s), "%s", ""); } @@ -332,7 +332,7 @@ static void wt_longstatus_print_unmerged_data(struct wt_status *s, strbuf_release(&onebuf); } -static void wt_longstatus_print_change_data(struct wt_status *s, +static void wt_longstatus_print_change_data(const struct wt_status *s, int change_type, struct string_list_item *it) { @@ -768,7 +768,7 @@ static void wt_longstatus_print_unmerged(struct wt_status *s) } -static void wt_longstatus_print_updated(struct wt_status *s) +static void wt_longstatus_print_updated(const struct wt_status *s) { if (!s->commitable) { return; diff --git a/wt-status.h b/wt-status.h index 430770b85..83a1f7c00 100644 --- a/wt-status.h +++ b/wt-status.h @@ -135,9 +135,9 @@ int wt_status_check_bisect(const struct worktree *wt, struct wt_status_state *state); __attribute__((format (printf, 3, 4))) -void status_printf_ln(struct wt_status *s, const char *color, const char *fmt, ...); +void status_printf_ln(const struct wt_status *s, const char *color, const char *fmt, ...); __attribute__((format (printf, 3, 4))) -void status_printf(struct wt_status *s, const char *color, const char *fmt, ...); +void status_printf(const struct wt_status *s, const char *color, const char *fmt, ...); /* The following functions expect that the caller took care of reading the index. */ int has_unstaged_changes(int ignore_submodules); -- 2.16.2 ^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH v2 0/2] Fix --short and --porcelain options for commit 2018-04-18 3:06 [PATCH 0/2] Fix --short and --porcelain options for commit Samuel Lijin 2018-04-18 3:06 ` [PATCH 1/2] commit: fix --short and --porcelain Samuel Lijin 2018-04-18 3:06 ` [PATCH 2/2] wt-status: const-ify all printf helper methods Samuel Lijin @ 2018-04-26 9:25 ` Samuel Lijin 2018-07-15 11:08 ` [PATCH v3 0/3] Fix --short/--porcelain options for git commit Samuel Lijin ` (3 more replies) 2018-04-26 9:25 ` [PATCH v2 1/2] commit: fix --short and --porcelain options Samuel Lijin 2018-04-26 9:25 ` [PATCH v2 2/2] wt-status: const-ify all printf helper methods Samuel Lijin 4 siblings, 4 replies; 26+ messages in thread From: Samuel Lijin @ 2018-04-26 9:25 UTC (permalink / raw) To: git; +Cc: Samuel Lijin Rerolling patch series to fix t7501. Samuel Lijin (2): commit: fix --short and --porcelain options wt-status: const-ify all printf helper methods t/t7501-commit.sh | 4 ++-- wt-status.c | 56 ++++++++++++++++++++++++++++++----------------- wt-status.h | 4 ++-- 3 files changed, 40 insertions(+), 24 deletions(-) -- 2.17.0 ^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH v3 0/3] Fix --short/--porcelain options for git commit 2018-04-26 9:25 ` [PATCH v2 0/2] Fix --short and --porcelain options for commit Samuel Lijin @ 2018-07-15 11:08 ` Samuel Lijin 2018-07-23 2:08 ` [PATCH v4 0/4] Rerolling patch series to fix t7501 Samuel Lijin ` (4 more replies) 2018-07-15 11:08 ` [PATCH v3 1/3] t7501: add merge conflict tests for " Samuel Lijin ` (2 subsequent siblings) 3 siblings, 5 replies; 26+ messages in thread From: Samuel Lijin @ 2018-07-15 11:08 UTC (permalink / raw) To: git; +Cc: Samuel Lijin Take 3. Addressed the issue that Junio turned up the last time I sent this out for review. I'm not entirely sure I like the way I added the tests in the first patch, but it's unclear to me if there's actually a pattern for setting up and tearing down the same env for multiple test methods. There are also other tests in t7501 that rely on state left from earlier tests, so it's not really clear to me what the best thing to do here is. Also added a FIXME in the second patch for something I think should be fixed, but doesn't make sense to fix in this patch series. Samuel Lijin (3): t7501: add merge conflict tests for dry run wt-status: teach wt_status_collect about merges in progress commit: fix exit code for --short/--porcelain builtin/commit.c | 32 +++--- ref-filter.c | 3 +- t/t7501-commit.sh | 49 +++++++-- wt-status.c | 260 +++++++++++++++++++++++++--------------------- wt-status.h | 13 +-- 5 files changed, 208 insertions(+), 149 deletions(-) -- 2.18.0 ^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH v4 0/4] Rerolling patch series to fix t7501 2018-07-15 11:08 ` [PATCH v3 0/3] Fix --short/--porcelain options for git commit Samuel Lijin @ 2018-07-23 2:08 ` Samuel Lijin 2018-07-30 22:15 ` Junio C Hamano 2018-07-23 2:09 ` [PATCH v4 1/4] t7501: add coverage for flags which imply dry runs Samuel Lijin ` (3 subsequent siblings) 4 siblings, 1 reply; 26+ messages in thread From: Samuel Lijin @ 2018-07-23 2:08 UTC (permalink / raw) To: git; +Cc: Samuel Lijin Following up on Junio's review from last time. Samuel Lijin (4): t7501: add coverage for flags which imply dry runs wt-status: rename commitable to committable wt-status: teach wt_status_collect about merges in progress commit: fix exit code when doing a dry run builtin/commit.c | 32 +++--- ref-filter.c | 3 +- t/t7501-commit.sh | 150 ++++++++++++++++++++++++--- wt-status.c | 258 ++++++++++++++++++++++++---------------------- wt-status.h | 13 +-- 5 files changed, 298 insertions(+), 158 deletions(-) -- 2.18.0 ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v4 0/4] Rerolling patch series to fix t7501 2018-07-23 2:08 ` [PATCH v4 0/4] Rerolling patch series to fix t7501 Samuel Lijin @ 2018-07-30 22:15 ` Junio C Hamano 0 siblings, 0 replies; 26+ messages in thread From: Junio C Hamano @ 2018-07-30 22:15 UTC (permalink / raw) To: Samuel Lijin; +Cc: git Samuel Lijin <sxlijin@gmail.com> writes: > Following up on Junio's review from last time. > > Samuel Lijin (4): > t7501: add coverage for flags which imply dry runs > wt-status: rename commitable to committable > wt-status: teach wt_status_collect about merges in progress > commit: fix exit code when doing a dry run > > builtin/commit.c | 32 +++--- > ref-filter.c | 3 +- > t/t7501-commit.sh | 150 ++++++++++++++++++++++++--- > wt-status.c | 258 ++++++++++++++++++++++++---------------------- > wt-status.h | 13 +-- > 5 files changed, 298 insertions(+), 158 deletions(-) It seems that t7512 & t7060 break with this topic queued. ^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH v4 1/4] t7501: add coverage for flags which imply dry runs 2018-07-15 11:08 ` [PATCH v3 0/3] Fix --short/--porcelain options for git commit Samuel Lijin 2018-07-23 2:08 ` [PATCH v4 0/4] Rerolling patch series to fix t7501 Samuel Lijin @ 2018-07-23 2:09 ` Samuel Lijin 2018-07-23 2:09 ` [PATCH v4 2/4] wt-status: rename commitable to committable Samuel Lijin ` (2 subsequent siblings) 4 siblings, 0 replies; 26+ messages in thread From: Samuel Lijin @ 2018-07-23 2:09 UTC (permalink / raw) To: git; +Cc: Samuel Lijin The behavior of git commit, when doing a dry run, changes if there are unresolved/resolved merge conflicts, but the test suite currently only asserts that `git commit --dry-run` succeeds when all merge conflicts are resolved. Add tests to document the behavior of all flags (i.e. `--dry-run`, `--short`, `--porcelain`, and `--long`) which imply a dry run when (1) there are only unresolved merge conflicts, (2) when there are both unresolved and resolved merge conflicts, and (3) when all merge conflicts are resolved. When testing behavior involving resolved merge conflicts, resolve merge conflicts by replacing each merge conflict with completely new contents, rather than choosing the contents associated with one of the parent commits, since the latter decision has no bearing on the behavior of a dry run commit invocation. Verify that a dry run invocation of git commit does not create a new commit by asserting that HEAD has not changed, instead of by crafting the commit. Signed-off-by: Samuel Lijin <sxlijin@gmail.com> --- t/t7501-commit.sh | 146 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 132 insertions(+), 14 deletions(-) diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh index 9dbbd01fc..e49dfd0a2 100755 --- a/t/t7501-commit.sh +++ b/t/t7501-commit.sh @@ -664,24 +664,142 @@ test_expect_success '--only works on to-be-born branch' ' test_cmp expected actual ' -test_expect_success '--dry-run with conflicts fixed from a merge' ' - # setup two branches with conflicting information - # in the same file, resolve the conflict, - # call commit with --dry-run - echo "Initial contents, unimportant" >test-file && - git add test-file && +test_expect_success 'prepare commits that can be used to trigger a merge conflict' ' + # setup two branches with conflicting contents in two paths + echo "Initial contents, unimportant" | tee test-file1 test-file2 && + git add test-file1 test-file2 && git commit -m "Initial commit" && - echo "commit-1-state" >test-file && - git commit -m "commit 1" -i test-file && + echo "commit-1-state" | tee test-file1 test-file2 && + git commit -m "commit 1" -i test-file1 test-file2 && git tag commit-1 && git checkout -b branch-2 HEAD^1 && - echo "commit-2-state" >test-file && - git commit -m "commit 2" -i test-file && - ! $(git merge --no-commit commit-1) && - echo "commit-2-state" >test-file && - git add test-file && + echo "commit-2-state" | tee test-file1 test-file2 && + git commit -m "commit 2" -i test-file1 test-file2 && + git tag commit-2 +' + +test_expect_success '--dry-run with only unresolved merge conflicts' ' + git reset --hard commit-2 && + test_must_fail git merge --no-commit commit-1 && + test_must_fail git commit --dry-run && + git rev-parse commit-2 >expected && + git rev-parse HEAD >actual && + test_cmp expected actual +' + +test_expect_success '--short with only unresolved merge conflicts' ' + git reset --hard commit-2 && + test_must_fail git merge --no-commit commit-1 && + test_must_fail git commit --short && + git rev-parse commit-2 >expected && + git rev-parse HEAD >actual && + test_cmp expected actual +' + +test_expect_success '--porcelain with only unresolved merge conflicts' ' + git reset --hard commit-2 && + test_must_fail git merge --no-commit commit-1 && + test_must_fail git commit --porcelain && + git rev-parse commit-2 >expected && + git rev-parse HEAD >actual && + test_cmp expected actual +' + +test_expect_success '--long with only unresolved merge conflicts' ' + git reset --hard commit-2 && + test_must_fail git merge --no-commit commit-1 && + test_must_fail git commit --long && + git rev-parse commit-2 >expected && + git rev-parse HEAD >actual && + test_cmp expected actual +' + +test_expect_failure '--dry-run with resolved and unresolved merge conflicts' ' + git reset --hard commit-2 && + test_must_fail git merge --no-commit commit-1 && + echo "resolve one merge conflict" >test-file1 && + git add test-file1 && + test_must_fail git commit --dry-run && + git rev-parse commit-2 >expected && + git rev-parse HEAD >actual && + test_cmp expected actual +' + +test_expect_success '--short with resolved and unresolved merge conflicts' ' + git reset --hard commit-2 && + test_must_fail git merge --no-commit commit-1 && + echo "resolve one merge conflict" >test-file1 && + git add test-file1 && + test_must_fail git commit --short && + git rev-parse commit-2 >expected && + git rev-parse HEAD >actual && + test_cmp expected actual +' + +test_expect_success '--porcelain with resolved and unresolved merge conflicts' ' + git reset --hard commit-2 && + test_must_fail git merge --no-commit commit-1 && + echo "resolve one merge conflict" >test-file1 && + git add test-file1 && + test_must_fail git commit --porcelain && + git rev-parse commit-2 >expected && + git rev-parse HEAD >actual && + test_cmp expected actual +' + +test_expect_failure '--long with resolved and unresolved merge conflicts' ' + git reset --hard commit-2 && + test_must_fail git merge --no-commit commit-1 && + echo "resolve one merge conflict" >test-file1 && + git add test-file1 && + test_must_fail git commit --long && + git rev-parse commit-2 >expected && + git rev-parse HEAD >actual && + test_cmp expected actual +' + +test_expect_success '--dry-run with only resolved merge conflicts' ' + git reset --hard commit-2 && + test_must_fail git merge --no-commit commit-1 && + echo "resolve all merge conflicts" | tee test-file1 test-file2 && + git add test-file1 test-file2 && git commit --dry-run && - git commit -m "conflicts fixed from merge." + git rev-parse commit-2 >expected && + git rev-parse HEAD >actual && + test_cmp expected actual +' + +test_expect_failure '--short with only resolved merge conflicts' ' + git reset --hard commit-2 && + test_must_fail git merge --no-commit commit-1 && + echo "resolve all merge conflicts" | tee test-file1 test-file2 && + git add test-file1 test-file2 && + git commit --short && + git rev-parse commit-2 >expected && + git rev-parse HEAD >actual && + test_cmp expected actual +' + +test_expect_failure '--porcelain with only resolved merge conflicts' ' + git reset --hard commit-2 && + test_must_fail git merge --no-commit commit-1 && + echo "resolve all merge conflicts" | tee test-file1 test-file2 && + git add test-file1 test-file2 && + git commit --porcelain && + git rev-parse commit-2 >expected && + git rev-parse HEAD >actual && + test_cmp expected actual +' + +test_expect_success '--long with only resolved merge conflicts' ' + git reset --hard commit-2 && + test_must_fail git merge --no-commit commit-1 && + echo "resolve all merge conflicts" | tee test-file1 test-file2 && + git add test-file1 test-file2 && + git commit --long && + git rev-parse commit-2 >expected && + git rev-parse HEAD >actual && + test_cmp expected actual ' test_done -- 2.18.0 ^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH v4 2/4] wt-status: rename commitable to committable 2018-07-15 11:08 ` [PATCH v3 0/3] Fix --short/--porcelain options for git commit Samuel Lijin 2018-07-23 2:08 ` [PATCH v4 0/4] Rerolling patch series to fix t7501 Samuel Lijin 2018-07-23 2:09 ` [PATCH v4 1/4] t7501: add coverage for flags which imply dry runs Samuel Lijin @ 2018-07-23 2:09 ` Samuel Lijin 2018-07-23 2:09 ` [PATCH v4 3/4] wt-status: teach wt_status_collect about merges in progress Samuel Lijin 2018-07-23 2:09 ` [PATCH v4 4/4] commit: fix exit code when doing a dry run Samuel Lijin 4 siblings, 0 replies; 26+ messages in thread From: Samuel Lijin @ 2018-07-23 2:09 UTC (permalink / raw) To: git; +Cc: Samuel Lijin Fix a typo in the name of the committable bit in wt_status_state. Signed-off-by: Samuel Lijin <sxlijin@gmail.com> --- builtin/commit.c | 18 +++++++++--------- wt-status.c | 10 +++++----- wt-status.h | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/builtin/commit.c b/builtin/commit.c index 158e3f843..32f9db33b 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -507,7 +507,7 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int wt_status_collect(s); wt_status_print(s); - return s->commitable; + return s->committable; } static int is_a_merge(const struct commit *current_head) @@ -653,7 +653,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, { struct stat statbuf; struct strbuf committer_ident = STRBUF_INIT; - int commitable; + int committable; struct strbuf sb = STRBUF_INIT; const char *hook_arg1 = NULL; const char *hook_arg2 = NULL; @@ -870,7 +870,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, saved_color_setting = s->use_color; s->use_color = 0; - commitable = run_status(s->fp, index_file, prefix, 1, s); + committable = run_status(s->fp, index_file, prefix, 1, s); s->use_color = saved_color_setting; } else { struct object_id oid; @@ -888,7 +888,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, for (i = 0; i < active_nr; i++) if (ce_intent_to_add(active_cache[i])) ita_nr++; - commitable = active_nr - ita_nr > 0; + committable = active_nr - ita_nr > 0; } else { /* * Unless the user did explicitly request a submodule @@ -904,7 +904,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, if (ignore_submodule_arg && !strcmp(ignore_submodule_arg, "all")) flags.ignore_submodules = 1; - commitable = index_differs_from(parent, &flags, 1); + committable = index_differs_from(parent, &flags, 1); } } strbuf_release(&committer_ident); @@ -916,7 +916,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, * explicit --allow-empty. In the cherry-pick case, it may be * empty due to conflict resolution, which the user should okay. */ - if (!commitable && whence != FROM_MERGE && !allow_empty && + if (!committable && whence != FROM_MERGE && !allow_empty && !(amend && is_a_merge(current_head))) { s->display_comment_prefix = old_display_comment_prefix; run_status(stdout, index_file, prefix, 0, s); @@ -1186,14 +1186,14 @@ static int parse_and_validate_options(int argc, const char *argv[], static int dry_run_commit(int argc, const char **argv, const char *prefix, const struct commit *current_head, struct wt_status *s) { - int commitable; + int committable; const char *index_file; index_file = prepare_index(argc, argv, prefix, current_head, 1); - commitable = run_status(stdout, index_file, prefix, 0, s); + committable = run_status(stdout, index_file, prefix, 0, s); rollback_index_files(); - return commitable ? 0 : 1; + return committable ? 0 : 1; } define_list_config_array_extra(color_status_slots, {"added"}); diff --git a/wt-status.c b/wt-status.c index 8827a256d..18ea333a5 100644 --- a/wt-status.c +++ b/wt-status.c @@ -773,7 +773,7 @@ static void wt_longstatus_print_updated(struct wt_status *s) continue; if (!shown_header) { wt_longstatus_print_cached_header(s); - s->commitable = 1; + s->committable = 1; shown_header = 1; } wt_longstatus_print_change_data(s, WT_STATUS_UPDATED, it); @@ -1008,7 +1008,7 @@ static void wt_longstatus_print_verbose(struct wt_status *s) rev.diffopt.use_color = 0; wt_status_add_cut_line(s->fp); } - if (s->verbose > 1 && s->commitable) { + if (s->verbose > 1 && s->committable) { /* print_updated() printed a header, so do we */ if (s->fp != stdout) wt_longstatus_print_trailer(s); @@ -1089,7 +1089,7 @@ static void show_merge_in_progress(struct wt_status *s, _(" (use \"git merge --abort\" to abort the merge)")); } } else { - s-> commitable = 1; + s-> committable = 1; status_printf_ln(s, color, _("All conflicts fixed but you are still merging.")); if (s->hints) @@ -1665,14 +1665,14 @@ static void wt_longstatus_print(struct wt_status *s) "new files yourself (see 'git help status')."), s->untracked_in_ms / 1000.0); } - } else if (s->commitable) + } else if (s->committable) status_printf_ln(s, GIT_COLOR_NORMAL, _("Untracked files not listed%s"), s->hints ? _(" (use -u option to show untracked files)") : ""); if (s->verbose) wt_longstatus_print_verbose(s); - if (!s->commitable) { + if (!s->committable) { if (s->amend) status_printf_ln(s, GIT_COLOR_NORMAL, _("No changes")); else if (s->nowarn) diff --git a/wt-status.h b/wt-status.h index 1673d146f..937b2c352 100644 --- a/wt-status.h +++ b/wt-status.h @@ -96,7 +96,7 @@ struct wt_status { unsigned char sha1_commit[GIT_MAX_RAWSZ]; /* when not Initial */ /* These are computed during processing of the individual sections */ - int commitable; + int committable; int workdir_dirty; const char *index_file; FILE *fp; -- 2.18.0 ^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH v4 3/4] wt-status: teach wt_status_collect about merges in progress 2018-07-15 11:08 ` [PATCH v3 0/3] Fix --short/--porcelain options for git commit Samuel Lijin ` (2 preceding siblings ...) 2018-07-23 2:09 ` [PATCH v4 2/4] wt-status: rename commitable to committable Samuel Lijin @ 2018-07-23 2:09 ` Samuel Lijin 2018-07-23 2:09 ` [PATCH v4 4/4] commit: fix exit code when doing a dry run Samuel Lijin 4 siblings, 0 replies; 26+ messages in thread From: Samuel Lijin @ 2018-07-23 2:09 UTC (permalink / raw) To: git; +Cc: Samuel Lijin To fix the breakages documented by t7501, the next patch in this series will teach wt_status_collect() how to set the committable bit, instead of having wt_longstatus_print_updated() and show_merge_in_progress() set it (which is what currently happens). To set the committable bit correctly, however, wt_status_collect() needs to know whether or not there is a merge in progress (if a merge is in progress, the bit (1) should not be set if there are unresolved merge conflicts and (2) should be set even if the index is the same as HEAD), so teach its (two) callers to create, initialize, and pass in instances of wt_status_state, which records this metadata. Since wt_longstatus_print() and show_merge_in_progress() are in the same callpaths and currently create and init copies of wt_status_state, remove that logic and instead pass wt_status_state through. Make wt_status_get_state() easier to use, add a helper method to clean up wt_status_state, const-ify as many struct pointers in method signatures as possible, and add a FIXME for a struct pointer which should be const but isn't (that this patch series will not address). Signed-off-by: Samuel Lijin <sxlijin@gmail.com> --- gitster: I kept the FIXME around because it wasn't clear whether or not you were opposed to it. For what it's worth, there are only two callsites that can't be const-ified because of this one item. builtin/commit.c | 14 ++-- ref-filter.c | 3 +- wt-status.c | 178 +++++++++++++++++++++++------------------------ wt-status.h | 11 +-- 4 files changed, 105 insertions(+), 101 deletions(-) diff --git a/builtin/commit.c b/builtin/commit.c index 32f9db33b..dd3e83053 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -485,6 +485,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix static int run_status(FILE *fp, const char *index_file, const char *prefix, int nowarn, struct wt_status *s) { + struct wt_status_state state; struct object_id oid; if (s->relative_paths) @@ -504,8 +505,10 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int s->status_format = status_format; s->ignore_submodule_arg = ignore_submodule_arg; - wt_status_collect(s); - wt_status_print(s); + wt_status_get_state(s, &state); + wt_status_collect(s, &state); + wt_status_print(s, &state); + wt_status_clear_state(&state); return s->committable; } @@ -1295,6 +1298,7 @@ int cmd_status(int argc, const char **argv, const char *prefix) static int no_renames = -1; static const char *rename_score_arg = (const char *)-1; static struct wt_status s; + struct wt_status_state state; int fd; struct object_id oid; static struct option builtin_status_options[] = { @@ -1379,7 +1383,8 @@ int cmd_status(int argc, const char **argv, const char *prefix) s.rename_score = parse_rename_score(&rename_score_arg); } - wt_status_collect(&s); + wt_status_get_state(&s, &state); + wt_status_collect(&s, &state); if (0 <= fd) update_index_if_able(&the_index, &index_lock); @@ -1387,7 +1392,8 @@ int cmd_status(int argc, const char **argv, const char *prefix) if (s.relative_paths) s.prefix = prefix; - wt_status_print(&s); + wt_status_print(&s, &state); + wt_status_clear_state(&state); return 0; } diff --git a/ref-filter.c b/ref-filter.c index 492f2b770..bc9b6b274 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -1381,8 +1381,7 @@ char *get_head_description(void) { struct strbuf desc = STRBUF_INIT; struct wt_status_state state; - memset(&state, 0, sizeof(state)); - wt_status_get_state(&state, 1); + wt_status_get_state(NULL, &state); if (state.rebase_in_progress || state.rebase_interactive_in_progress) { if (state.branch) diff --git a/wt-status.c b/wt-status.c index 18ea333a5..af83fae68 100644 --- a/wt-status.c +++ b/wt-status.c @@ -33,7 +33,7 @@ static char default_wt_status_colors[][COLOR_MAXLEN] = { GIT_COLOR_NIL, /* WT_STATUS_ONBRANCH */ }; -static const char *color(int slot, struct wt_status *s) +static const char *color(int slot, const struct wt_status *s) { const char *c = ""; if (want_color(s->use_color)) @@ -43,7 +43,7 @@ static const char *color(int slot, struct wt_status *s) return c; } -static void status_vprintf(struct wt_status *s, int at_bol, const char *color, +static void status_vprintf(const struct wt_status *s, int at_bol, const char *color, const char *fmt, va_list ap, const char *trail) { struct strbuf sb = STRBUF_INIT; @@ -89,7 +89,7 @@ static void status_vprintf(struct wt_status *s, int at_bol, const char *color, strbuf_release(&sb); } -void status_printf_ln(struct wt_status *s, const char *color, +void status_printf_ln(const struct wt_status *s, const char *color, const char *fmt, ...) { va_list ap; @@ -99,7 +99,7 @@ void status_printf_ln(struct wt_status *s, const char *color, va_end(ap); } -void status_printf(struct wt_status *s, const char *color, +void status_printf(const struct wt_status *s, const char *color, const char *fmt, ...) { va_list ap; @@ -109,7 +109,7 @@ void status_printf(struct wt_status *s, const char *color, va_end(ap); } -static void status_printf_more(struct wt_status *s, const char *color, +static void status_printf_more(const struct wt_status *s, const char *color, const char *fmt, ...) { va_list ap; @@ -143,7 +143,7 @@ void wt_status_prepare(struct wt_status *s) s->rename_limit = -1; } -static void wt_longstatus_print_unmerged_header(struct wt_status *s) +static void wt_longstatus_print_unmerged_header(const struct wt_status *s) { int i; int del_mod_conflict = 0; @@ -195,7 +195,7 @@ static void wt_longstatus_print_unmerged_header(struct wt_status *s) status_printf_ln(s, c, "%s", ""); } -static void wt_longstatus_print_cached_header(struct wt_status *s) +static void wt_longstatus_print_cached_header(const struct wt_status *s) { const char *c = color(WT_STATUS_HEADER, s); @@ -211,7 +211,7 @@ static void wt_longstatus_print_cached_header(struct wt_status *s) status_printf_ln(s, c, "%s", ""); } -static void wt_longstatus_print_dirty_header(struct wt_status *s, +static void wt_longstatus_print_dirty_header(const struct wt_status *s, int has_deleted, int has_dirty_submodules) { @@ -230,7 +230,7 @@ static void wt_longstatus_print_dirty_header(struct wt_status *s, status_printf_ln(s, c, "%s", ""); } -static void wt_longstatus_print_other_header(struct wt_status *s, +static void wt_longstatus_print_other_header(const struct wt_status *s, const char *what, const char *how) { @@ -242,7 +242,7 @@ static void wt_longstatus_print_other_header(struct wt_status *s, status_printf_ln(s, c, "%s", ""); } -static void wt_longstatus_print_trailer(struct wt_status *s) +static void wt_longstatus_print_trailer(const struct wt_status *s) { status_printf_ln(s, color(WT_STATUS_HEADER, s), "%s", ""); } @@ -308,7 +308,7 @@ static int maxwidth(const char *(*label)(int), int minval, int maxval) return result; } -static void wt_longstatus_print_unmerged_data(struct wt_status *s, +static void wt_longstatus_print_unmerged_data(const struct wt_status *s, struct string_list_item *it) { const char *c = color(WT_STATUS_UNMERGED, s); @@ -335,7 +335,7 @@ static void wt_longstatus_print_unmerged_data(struct wt_status *s, strbuf_release(&onebuf); } -static void wt_longstatus_print_change_data(struct wt_status *s, +static void wt_longstatus_print_change_data(const struct wt_status *s, int change_type, struct string_list_item *it) { @@ -724,7 +724,7 @@ static void wt_status_collect_untracked(struct wt_status *s) s->untracked_in_ms = (getnanotime() - t_begin) / 1000000; } -void wt_status_collect(struct wt_status *s) +void wt_status_collect(struct wt_status *s, const struct wt_status_state *state) { wt_status_collect_changes_worktree(s); @@ -732,10 +732,11 @@ void wt_status_collect(struct wt_status *s) wt_status_collect_changes_initial(s); else wt_status_collect_changes_index(s); + wt_status_collect_untracked(s); } -static void wt_longstatus_print_unmerged(struct wt_status *s) +static void wt_longstatus_print_unmerged(const struct wt_status *s) { int shown_header = 0; int i; @@ -787,7 +788,7 @@ static void wt_longstatus_print_updated(struct wt_status *s) * 0 : no change * 1 : some change but no delete */ -static int wt_status_check_worktree_changes(struct wt_status *s, +static int wt_status_check_worktree_changes(const struct wt_status *s, int *dirty_submodules) { int i; @@ -811,7 +812,7 @@ static int wt_status_check_worktree_changes(struct wt_status *s, return changes; } -static void wt_longstatus_print_changed(struct wt_status *s) +static void wt_longstatus_print_changed(const struct wt_status *s) { int i, dirty_submodules; int worktree_changes = wt_status_check_worktree_changes(s, &dirty_submodules); @@ -843,7 +844,7 @@ static int stash_count_refs(struct object_id *ooid, struct object_id *noid, return 0; } -static void wt_longstatus_print_stash_summary(struct wt_status *s) +static void wt_longstatus_print_stash_summary(const struct wt_status *s) { int stash_count = 0; @@ -855,7 +856,7 @@ static void wt_longstatus_print_stash_summary(struct wt_status *s) stash_count); } -static void wt_longstatus_print_submodule_summary(struct wt_status *s, int uncommitted) +static void wt_longstatus_print_submodule_summary(const struct wt_status *s, int uncommitted) { struct child_process sm_summary = CHILD_PROCESS_INIT; struct strbuf cmd_stdout = STRBUF_INIT; @@ -901,8 +902,8 @@ static void wt_longstatus_print_submodule_summary(struct wt_status *s, int uncom strbuf_release(&summary); } -static void wt_longstatus_print_other(struct wt_status *s, - struct string_list *l, +static void wt_longstatus_print_other(const struct wt_status *s, + const struct string_list *l, const char *what, const char *how) { @@ -975,7 +976,7 @@ void wt_status_add_cut_line(FILE *fp) strbuf_release(&buf); } -static void wt_longstatus_print_verbose(struct wt_status *s) +static void wt_longstatus_print_verbose(const struct wt_status *s) { struct rev_info rev; struct setup_revision_opt opt; @@ -1029,7 +1030,7 @@ static void wt_longstatus_print_verbose(struct wt_status *s) } } -static void wt_longstatus_print_tracking(struct wt_status *s) +static void wt_longstatus_print_tracking(const struct wt_status *s) { struct strbuf sb = STRBUF_INIT; const char *cp, *ep, *branch_name; @@ -1063,7 +1064,7 @@ static void wt_longstatus_print_tracking(struct wt_status *s) strbuf_release(&sb); } -static int has_unmerged(struct wt_status *s) +static int has_unmerged(const struct wt_status *s) { int i; @@ -1077,7 +1078,7 @@ static int has_unmerged(struct wt_status *s) } static void show_merge_in_progress(struct wt_status *s, - struct wt_status_state *state, + const struct wt_status_state *state, const char *color) { if (has_unmerged(s)) { @@ -1099,8 +1100,8 @@ static void show_merge_in_progress(struct wt_status *s, wt_longstatus_print_trailer(s); } -static void show_am_in_progress(struct wt_status *s, - struct wt_status_state *state, +static void show_am_in_progress(const struct wt_status *s, + const struct wt_status_state *state, const char *color) { status_printf_ln(s, color, @@ -1138,7 +1139,7 @@ static char *read_line_from_git_path(const char *filename) } } -static int split_commit_in_progress(struct wt_status *s) +static int split_commit_in_progress(const struct wt_status *s) { int split_in_progress = 0; char *head, *orig_head, *rebase_amend, *rebase_orig_head; @@ -1232,8 +1233,8 @@ static int read_rebase_todolist(const char *fname, struct string_list *lines) return 0; } -static void show_rebase_information(struct wt_status *s, - struct wt_status_state *state, +static void show_rebase_information(const struct wt_status *s, + const struct wt_status_state *state, const char *color) { if (state->rebase_interactive_in_progress) { @@ -1286,8 +1287,8 @@ static void show_rebase_information(struct wt_status *s, } } -static void print_rebase_state(struct wt_status *s, - struct wt_status_state *state, +static void print_rebase_state(const struct wt_status *s, + const struct wt_status_state *state, const char *color) { if (state->branch) @@ -1300,8 +1301,8 @@ static void print_rebase_state(struct wt_status *s, _("You are currently rebasing.")); } -static void show_rebase_in_progress(struct wt_status *s, - struct wt_status_state *state, +static void show_rebase_in_progress(const struct wt_status *s, + const struct wt_status_state *state, const char *color) { struct stat st; @@ -1353,8 +1354,8 @@ static void show_rebase_in_progress(struct wt_status *s, wt_longstatus_print_trailer(s); } -static void show_cherry_pick_in_progress(struct wt_status *s, - struct wt_status_state *state, +static void show_cherry_pick_in_progress(const struct wt_status *s, + const struct wt_status_state *state, const char *color) { status_printf_ln(s, color, _("You are currently cherry-picking commit %s."), @@ -1372,8 +1373,8 @@ static void show_cherry_pick_in_progress(struct wt_status *s, wt_longstatus_print_trailer(s); } -static void show_revert_in_progress(struct wt_status *s, - struct wt_status_state *state, +static void show_revert_in_progress(const struct wt_status *s, + const struct wt_status_state *state, const char *color) { status_printf_ln(s, color, _("You are currently reverting commit %s."), @@ -1391,8 +1392,8 @@ static void show_revert_in_progress(struct wt_status *s, wt_longstatus_print_trailer(s); } -static void show_bisect_in_progress(struct wt_status *s, - struct wt_status_state *state, +static void show_bisect_in_progress(const struct wt_status *s, + const struct wt_status_state *state, const char *color) { if (state->branch) @@ -1546,12 +1547,16 @@ int wt_status_check_bisect(const struct worktree *wt, return 0; } -void wt_status_get_state(struct wt_status_state *state, - int get_detached_from) +void wt_status_get_state( + const struct wt_status *s, struct wt_status_state *state) { + int get_detached_from = + (s == NULL) || (s->branch && !strcmp(s->branch, "HEAD")); struct stat st; struct object_id oid; + memset(state, 0, sizeof(*state)); + if (!stat(git_path_merge_head(the_repository), &st)) { state->merge_in_progress = 1; } else if (wt_status_check_rebase(NULL, state)) { @@ -1572,8 +1577,15 @@ void wt_status_get_state(struct wt_status_state *state, wt_status_get_detached_from(state); } +void wt_status_clear_state(struct wt_status_state *state) +{ + free(state->branch); + free(state->onto); + free(state->detached_from); +} + static void wt_longstatus_print_state(struct wt_status *s, - struct wt_status_state *state) + const struct wt_status_state *state) { const char *state_color = color(WT_STATUS_HEADER, s); if (state->merge_in_progress) @@ -1590,30 +1602,25 @@ static void wt_longstatus_print_state(struct wt_status *s, show_bisect_in_progress(s, state, state_color); } -static void wt_longstatus_print(struct wt_status *s) +static void wt_longstatus_print(struct wt_status *s, const struct wt_status_state *state) { const char *branch_color = color(WT_STATUS_ONBRANCH, s); const char *branch_status_color = color(WT_STATUS_HEADER, s); - struct wt_status_state state; - - memset(&state, 0, sizeof(state)); - wt_status_get_state(&state, - s->branch && !strcmp(s->branch, "HEAD")); if (s->branch) { const char *on_what = _("On branch "); const char *branch_name = s->branch; if (!strcmp(branch_name, "HEAD")) { branch_status_color = color(WT_STATUS_NOBRANCH, s); - if (state.rebase_in_progress || state.rebase_interactive_in_progress) { - if (state.rebase_interactive_in_progress) + if (state->rebase_in_progress || state->rebase_interactive_in_progress) { + if (state->rebase_interactive_in_progress) on_what = _("interactive rebase in progress; onto "); else on_what = _("rebase in progress; onto "); - branch_name = state.onto; - } else if (state.detached_from) { - branch_name = state.detached_from; - if (state.detached_at) + branch_name = state->onto; + } else if (state->detached_from) { + branch_name = state->detached_from; + if (state->detached_at) on_what = _("HEAD detached at "); else on_what = _("HEAD detached from "); @@ -1630,10 +1637,7 @@ static void wt_longstatus_print(struct wt_status *s) wt_longstatus_print_tracking(s); } - wt_longstatus_print_state(s, &state); - free(state.branch); - free(state.onto); - free(state.detached_from); + wt_longstatus_print_state(s, state); if (s->is_initial) { status_printf_ln(s, color(WT_STATUS_HEADER, s), "%s", ""); @@ -1708,7 +1712,7 @@ static void wt_longstatus_print(struct wt_status *s) } static void wt_shortstatus_unmerged(struct string_list_item *it, - struct wt_status *s) + const struct wt_status *s) { struct wt_status_change_data *d = it->util; const char *how = "??"; @@ -1735,7 +1739,7 @@ static void wt_shortstatus_unmerged(struct string_list_item *it, } static void wt_shortstatus_status(struct string_list_item *it, - struct wt_status *s) + const struct wt_status *s) { struct wt_status_change_data *d = it->util; @@ -1778,7 +1782,7 @@ static void wt_shortstatus_status(struct string_list_item *it, } static void wt_shortstatus_other(struct string_list_item *it, - struct wt_status *s, const char *sign) + const struct wt_status *s, const char *sign) { if (s->null_termination) { fprintf(stdout, "%s %s%c", sign, it->string, 0); @@ -1792,7 +1796,7 @@ static void wt_shortstatus_other(struct string_list_item *it, } } -static void wt_shortstatus_print_tracking(struct wt_status *s) +static void wt_shortstatus_print_tracking(const struct wt_status *s) { struct branch *branch; const char *header_color = color(WT_STATUS_HEADER, s); @@ -1868,7 +1872,7 @@ static void wt_shortstatus_print_tracking(struct wt_status *s) fputc(s->null_termination ? '\0' : '\n', s->fp); } -static void wt_shortstatus_print(struct wt_status *s) +static void wt_shortstatus_print(const struct wt_status *s) { struct string_list_item *it; @@ -1932,18 +1936,14 @@ static void wt_porcelain_print(struct wt_status *s) * upstream. When AHEAD_BEHIND_QUICK is requested and the branches * are different, '?' will be substituted for the actual count. */ -static void wt_porcelain_v2_print_tracking(struct wt_status *s) +static void wt_porcelain_v2_print_tracking(const struct wt_status *s, const struct wt_status_state *state) { struct branch *branch; const char *base; const char *branch_name; - struct wt_status_state state; int ab_info, nr_ahead, nr_behind; char eol = s->null_termination ? '\0' : '\n'; - memset(&state, 0, sizeof(state)); - wt_status_get_state(&state, s->branch && !strcmp(s->branch, "HEAD")); - fprintf(s->fp, "# branch.oid %s%c", (s->is_initial ? "(initial)" : sha1_to_hex(s->sha1_commit)), eol); @@ -1954,10 +1954,10 @@ static void wt_porcelain_v2_print_tracking(struct wt_status *s) if (!strcmp(s->branch, "HEAD")) { fprintf(s->fp, "# branch.head %s%c", "(detached)", eol); - if (state.rebase_in_progress || state.rebase_interactive_in_progress) - branch_name = state.onto; - else if (state.detached_from) - branch_name = state.detached_from; + if (state->rebase_in_progress || state->rebase_interactive_in_progress) + branch_name = state->onto; + else if (state->detached_from) + branch_name = state->detached_from; else branch_name = ""; } else { @@ -1991,10 +1991,6 @@ static void wt_porcelain_v2_print_tracking(struct wt_status *s) } } } - - free(state.branch); - free(state.onto); - free(state.detached_from); } /* @@ -2002,7 +1998,7 @@ static void wt_porcelain_v2_print_tracking(struct wt_status *s) * fixed-length string of characters in the buffer provided. */ static void wt_porcelain_v2_submodule_state( - struct wt_status_change_data *d, + const struct wt_status_change_data *d, char sub[5]) { if (S_ISGITLINK(d->mode_head) || @@ -2025,8 +2021,8 @@ static void wt_porcelain_v2_submodule_state( * Fix-up changed entries before we print them. */ static void wt_porcelain_v2_fix_up_changed( - struct string_list_item *it, - struct wt_status *s) + const struct string_list_item *it, + const struct wt_status *s) { struct wt_status_change_data *d = it->util; @@ -2074,8 +2070,8 @@ static void wt_porcelain_v2_fix_up_changed( * Print porcelain v2 info for tracked entries with changes. */ static void wt_porcelain_v2_print_changed_entry( - struct string_list_item *it, - struct wt_status *s) + const struct string_list_item *it, + const struct wt_status *s) { struct wt_status_change_data *d = it->util; struct strbuf buf = STRBUF_INIT; @@ -2138,8 +2134,8 @@ static void wt_porcelain_v2_print_changed_entry( * Print porcelain v2 status info for unmerged entries. */ static void wt_porcelain_v2_print_unmerged_entry( - struct string_list_item *it, - struct wt_status *s) + const struct string_list_item *it, + const struct wt_status *s) { struct wt_status_change_data *d = it->util; const struct cache_entry *ce; @@ -2219,8 +2215,8 @@ static void wt_porcelain_v2_print_unmerged_entry( * Print porcelain V2 status info for untracked and ignored entries. */ static void wt_porcelain_v2_print_other( - struct string_list_item *it, - struct wt_status *s, + const struct string_list_item *it, + const struct wt_status *s, char prefix) { struct strbuf buf = STRBUF_INIT; @@ -2250,14 +2246,14 @@ static void wt_porcelain_v2_print_other( * [<v2_ignored_items>]* * */ -static void wt_porcelain_v2_print(struct wt_status *s) +static void wt_porcelain_v2_print(const struct wt_status *s, const struct wt_status_state *state) { struct wt_status_change_data *d; struct string_list_item *it; int i; if (s->show_branch) - wt_porcelain_v2_print_tracking(s); + wt_porcelain_v2_print_tracking(s, state); for (i = 0; i < s->change.nr; i++) { it = &(s->change.items[i]); @@ -2284,7 +2280,9 @@ static void wt_porcelain_v2_print(struct wt_status *s) } } -void wt_status_print(struct wt_status *s) +// FIXME: `struct wt_status *` should be `const struct wt_status` but because +// `wt_porcelain_print()` modifies it, that has to first be fixed +void wt_status_print(struct wt_status *s, const struct wt_status_state *state) { switch (s->status_format) { case STATUS_FORMAT_SHORT: @@ -2294,14 +2292,14 @@ void wt_status_print(struct wt_status *s) wt_porcelain_print(s); break; case STATUS_FORMAT_PORCELAIN_V2: - wt_porcelain_v2_print(s); + wt_porcelain_v2_print(s, state); break; case STATUS_FORMAT_UNSPECIFIED: BUG("finalize_deferred_config() should have been called"); break; case STATUS_FORMAT_NONE: case STATUS_FORMAT_LONG: - wt_longstatus_print(s); + wt_longstatus_print(s, state); break; } } diff --git a/wt-status.h b/wt-status.h index 937b2c352..341bda9dc 100644 --- a/wt-status.h +++ b/wt-status.h @@ -128,18 +128,19 @@ struct wt_status_state { size_t wt_status_locate_end(const char *s, size_t len); void wt_status_add_cut_line(FILE *fp); void wt_status_prepare(struct wt_status *s); -void wt_status_print(struct wt_status *s); -void wt_status_collect(struct wt_status *s); -void wt_status_get_state(struct wt_status_state *state, int get_detached_from); +void wt_status_print(struct wt_status *s, const struct wt_status_state *state); +void wt_status_collect(struct wt_status *s, const struct wt_status_state *state); +void wt_status_get_state(const struct wt_status *s, struct wt_status_state *state); +void wt_status_clear_state(struct wt_status_state *state); int wt_status_check_rebase(const struct worktree *wt, struct wt_status_state *state); int wt_status_check_bisect(const struct worktree *wt, struct wt_status_state *state); __attribute__((format (printf, 3, 4))) -void status_printf_ln(struct wt_status *s, const char *color, const char *fmt, ...); +void status_printf_ln(const struct wt_status *s, const char *color, const char *fmt, ...); __attribute__((format (printf, 3, 4))) -void status_printf(struct wt_status *s, const char *color, const char *fmt, ...); +void status_printf(const struct wt_status *s, const char *color, const char *fmt, ...); /* The following functions expect that the caller took care of reading the index. */ int has_unstaged_changes(int ignore_submodules); -- 2.18.0 ^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH v4 4/4] commit: fix exit code when doing a dry run 2018-07-15 11:08 ` [PATCH v3 0/3] Fix --short/--porcelain options for git commit Samuel Lijin ` (3 preceding siblings ...) 2018-07-23 2:09 ` [PATCH v4 3/4] wt-status: teach wt_status_collect about merges in progress Samuel Lijin @ 2018-07-23 2:09 ` Samuel Lijin 4 siblings, 0 replies; 26+ messages in thread From: Samuel Lijin @ 2018-07-23 2:09 UTC (permalink / raw) To: git; +Cc: Samuel Lijin In wt-status.c, the s->committable bit is set only in the call tree of wt_longstatus_print(), and it is not always set correctly. This means that in normal cases, if there are changes to be committed, or if there is a merge in progress and all conflicts have been resolved, `--dry-run` and `--long` return the correct exit code but `--short` and `--porcelain` do not, even though all four flags imply dry run. Moreover, if there is a merge in progress and some but not all conflicts have been resolved, `--short` and `--porcelain` only return the correct exit code by coincidence (because the codepaths they follow never touch the committable bit), whereas `--dry-run` and `--long` return the wrong exit code. Teach wt_status_collect() to set s->committable correctly (if a merge is in progress, committable should be set iff there are no unmerged changes; otherwise, committable should be set iff there are changes in the index) so that all four flags which imply dry runs return the correct exit code in the above described situations and mark the documenting tests as fixed. Use the index_status field in the wt_status_change_data structs in has_unmerged() to determine whether or not there are unmerged paths, instead of the stagemask field, to improve readability. Also stop setting s->committable in wt_longstatus_print_updated() and show_merge_in_progress(), and const-ify wt_status_state in the method signatures in those callpaths. Signed-off-by: Samuel Lijin <sxlijin@gmail.com> --- t/t7501-commit.sh | 12 +++---- wt-status.c | 80 +++++++++++++++++++++++++++++------------------ 2 files changed, 55 insertions(+), 37 deletions(-) diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh index e49dfd0a2..6dba526e6 100755 --- a/t/t7501-commit.sh +++ b/t/t7501-commit.sh @@ -99,12 +99,12 @@ test_expect_success '--dry-run with stuff to commit returns ok' ' git commit -m next -a --dry-run ' -test_expect_failure '--short with stuff to commit returns ok' ' +test_expect_success '--short with stuff to commit returns ok' ' echo bongo bongo bongo >>file && git commit -m next -a --short ' -test_expect_failure '--porcelain with stuff to commit returns ok' ' +test_expect_success '--porcelain with stuff to commit returns ok' ' echo bongo bongo bongo >>file && git commit -m next -a --porcelain ' @@ -714,7 +714,7 @@ test_expect_success '--long with only unresolved merge conflicts' ' test_cmp expected actual ' -test_expect_failure '--dry-run with resolved and unresolved merge conflicts' ' +test_expect_success '--dry-run with resolved and unresolved merge conflicts' ' git reset --hard commit-2 && test_must_fail git merge --no-commit commit-1 && echo "resolve one merge conflict" >test-file1 && @@ -747,7 +747,7 @@ test_expect_success '--porcelain with resolved and unresolved merge conflicts' ' test_cmp expected actual ' -test_expect_failure '--long with resolved and unresolved merge conflicts' ' +test_expect_success '--long with resolved and unresolved merge conflicts' ' git reset --hard commit-2 && test_must_fail git merge --no-commit commit-1 && echo "resolve one merge conflict" >test-file1 && @@ -769,7 +769,7 @@ test_expect_success '--dry-run with only resolved merge conflicts' ' test_cmp expected actual ' -test_expect_failure '--short with only resolved merge conflicts' ' +test_expect_success '--short with only resolved merge conflicts' ' git reset --hard commit-2 && test_must_fail git merge --no-commit commit-1 && echo "resolve all merge conflicts" | tee test-file1 test-file2 && @@ -780,7 +780,7 @@ test_expect_failure '--short with only resolved merge conflicts' ' test_cmp expected actual ' -test_expect_failure '--porcelain with only resolved merge conflicts' ' +test_expect_success '--porcelain with only resolved merge conflicts' ' git reset --hard commit-2 && test_must_fail git merge --no-commit commit-1 && echo "resolve all merge conflicts" | tee test-file1 test-file2 && diff --git a/wt-status.c b/wt-status.c index af83fae68..fc239f61c 100644 --- a/wt-status.c +++ b/wt-status.c @@ -724,6 +724,38 @@ static void wt_status_collect_untracked(struct wt_status *s) s->untracked_in_ms = (getnanotime() - t_begin) / 1000000; } +static int has_unmerged(const struct wt_status *s) +{ + int i; + + for (i = 0; i < s->change.nr; i++) { + struct wt_status_change_data *d = (s->change.items[i]).util; + if (d->index_status == DIFF_STATUS_UNMERGED) + return 1; + } + return 0; +} + +static void wt_status_mark_committable( + struct wt_status *s, const struct wt_status_state *state) +{ + int i; + + if (state->merge_in_progress) { + s->committable = !has_unmerged(s); + return; + } + + for (i = 0; i < s->change.nr; i++) { + struct wt_status_change_data *d = (s->change.items[i]).util; + + if (d->index_status) { + s->committable = 1; + return; + } + } +} + void wt_status_collect(struct wt_status *s, const struct wt_status_state *state) { wt_status_collect_changes_worktree(s); @@ -734,6 +766,8 @@ void wt_status_collect(struct wt_status *s, const struct wt_status_state *state) wt_status_collect_changes_index(s); wt_status_collect_untracked(s); + + wt_status_mark_committable(s, state); } static void wt_longstatus_print_unmerged(const struct wt_status *s) @@ -759,28 +793,27 @@ static void wt_longstatus_print_unmerged(const struct wt_status *s) } -static void wt_longstatus_print_updated(struct wt_status *s) +static void wt_longstatus_print_updated(const struct wt_status *s) { - int shown_header = 0; int i; + if (!s->committable) + return; + + wt_longstatus_print_cached_header(s); + for (i = 0; i < s->change.nr; i++) { struct wt_status_change_data *d; struct string_list_item *it; it = &(s->change.items[i]); d = it->util; - if (!d->index_status || - d->index_status == DIFF_STATUS_UNMERGED) - continue; - if (!shown_header) { - wt_longstatus_print_cached_header(s); - s->committable = 1; - shown_header = 1; + if (d->index_status && + d->index_status != DIFF_STATUS_UNMERGED) { + wt_longstatus_print_change_data(s, WT_STATUS_UPDATED, it); } - wt_longstatus_print_change_data(s, WT_STATUS_UPDATED, it); } - if (shown_header) - wt_longstatus_print_trailer(s); + + wt_longstatus_print_trailer(s); } /* @@ -1064,21 +1097,7 @@ static void wt_longstatus_print_tracking(const struct wt_status *s) strbuf_release(&sb); } -static int has_unmerged(const struct wt_status *s) -{ - int i; - - for (i = 0; i < s->change.nr; i++) { - struct wt_status_change_data *d; - d = s->change.items[i].util; - if (d->stagemask) - return 1; - } - return 0; -} - -static void show_merge_in_progress(struct wt_status *s, - const struct wt_status_state *state, +static void show_merge_in_progress(const struct wt_status *s, const char *color) { if (has_unmerged(s)) { @@ -1090,7 +1109,6 @@ static void show_merge_in_progress(struct wt_status *s, _(" (use \"git merge --abort\" to abort the merge)")); } } else { - s-> committable = 1; status_printf_ln(s, color, _("All conflicts fixed but you are still merging.")); if (s->hints) @@ -1584,12 +1602,12 @@ void wt_status_clear_state(struct wt_status_state *state) free(state->detached_from); } -static void wt_longstatus_print_state(struct wt_status *s, +static void wt_longstatus_print_state(const struct wt_status *s, const struct wt_status_state *state) { const char *state_color = color(WT_STATUS_HEADER, s); if (state->merge_in_progress) - show_merge_in_progress(s, state, state_color); + show_merge_in_progress(s, state_color); else if (state->am_in_progress) show_am_in_progress(s, state, state_color); else if (state->rebase_in_progress || state->rebase_interactive_in_progress) @@ -1602,7 +1620,7 @@ static void wt_longstatus_print_state(struct wt_status *s, show_bisect_in_progress(s, state, state_color); } -static void wt_longstatus_print(struct wt_status *s, const struct wt_status_state *state) +static void wt_longstatus_print(const struct wt_status *s, const struct wt_status_state *state) { const char *branch_color = color(WT_STATUS_ONBRANCH, s); const char *branch_status_color = color(WT_STATUS_HEADER, s); -- 2.18.0 ^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH v3 1/3] t7501: add merge conflict tests for dry run 2018-04-26 9:25 ` [PATCH v2 0/2] Fix --short and --porcelain options for commit Samuel Lijin 2018-07-15 11:08 ` [PATCH v3 0/3] Fix --short/--porcelain options for git commit Samuel Lijin @ 2018-07-15 11:08 ` Samuel Lijin 2018-07-17 17:05 ` Junio C Hamano 2018-07-15 11:08 ` [PATCH v3 2/3] wt-status: teach wt_status_collect about merges in progress Samuel Lijin 2018-07-15 11:08 ` [PATCH v3 3/3] commit: fix exit code for --short/--porcelain Samuel Lijin 3 siblings, 1 reply; 26+ messages in thread From: Samuel Lijin @ 2018-07-15 11:08 UTC (permalink / raw) To: git; +Cc: Samuel Lijin The behavior of git commit when doing a dry run changes if there are unfixed/fixed merge conflits, but the test suite currently only asserts that `git commit --dry-run` succeeds when all merge conflicts are fixed. Add tests to document the behavior of all flags which imply a dry run when (1) there is at least one unfixed merge conflict and (2) when all merge conflicts are all fixed. Signed-off-by: Samuel Lijin <sxlijin@gmail.com> --- t/t7501-commit.sh | 45 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh index fa61b1a4e..be087e73f 100755 --- a/t/t7501-commit.sh +++ b/t/t7501-commit.sh @@ -652,7 +652,8 @@ test_expect_success '--only works on to-be-born branch' ' test_cmp expected actual ' -test_expect_success '--dry-run with conflicts fixed from a merge' ' +# set up env for tests of --dry-run given fixed/unfixed merge conflicts +test_expect_success 'setup env with unfixed merge conflicts' ' # setup two branches with conflicting information # in the same file, resolve the conflict, # call commit with --dry-run @@ -665,11 +666,45 @@ test_expect_success '--dry-run with conflicts fixed from a merge' ' git checkout -b branch-2 HEAD^1 && echo "commit-2-state" >test-file && git commit -m "commit 2" -i test-file && - ! $(git merge --no-commit commit-1) && - echo "commit-2-state" >test-file && + test_expect_code 1 git merge --no-commit commit-1 +' + +test_expect_success '--dry-run with unfixed merge conflicts' ' + test_expect_code 1 git commit --dry-run +' + +test_expect_success '--short with unfixed merge conflicts' ' + test_expect_code 1 git commit --short +' + +test_expect_success '--porcelain with unfixed merge conflicts' ' + test_expect_code 1 git commit --porcelain +' + +test_expect_success '--long with unfixed merge conflicts' ' + test_expect_code 1 git commit --long +' + +test_expect_success '--dry-run with conflicts fixed from a merge' ' + echo "merge-conflicts-fixed" >test-file && git add test-file && - git commit --dry-run && - git commit -m "conflicts fixed from merge." + git commit --dry-run +' + +test_expect_failure '--short with conflicts fixed from a merge' ' + git commit --short +' + +test_expect_failure '--porcelain with conflicts fixed from a merge' ' + git commit --porcelain +' + +test_expect_success '--long with conflicts fixed from a merge' ' + git commit --long +' + +test_expect_success '--message with conflicts fixed from a merge' ' + git commit --message "conflicts fixed from merge." ' test_done -- 2.18.0 ^ permalink raw reply related [flat|nested] 26+ messages in thread
* Re: [PATCH v3 1/3] t7501: add merge conflict tests for dry run 2018-07-15 11:08 ` [PATCH v3 1/3] t7501: add merge conflict tests for " Samuel Lijin @ 2018-07-17 17:05 ` Junio C Hamano 2018-07-17 17:45 ` Junio C Hamano 0 siblings, 1 reply; 26+ messages in thread From: Junio C Hamano @ 2018-07-17 17:05 UTC (permalink / raw) To: Samuel Lijin; +Cc: git Samuel Lijin <sxlijin@gmail.com> writes: > The behavior of git commit when doing a dry run changes if there are > unfixed/fixed merge conflits, but the test suite currently only asserts > that `git commit --dry-run` succeeds when all merge conflicts are fixed. > > Add tests to document the behavior of all flags which imply a dry run > when (1) there is at least one unfixed merge conflict and (2) when all > merge conflicts are all fixed. s/conflits/conflicts/ s/fixed/resolved/g (both above and in the patch text) s/unfixed/unresolved/g (both above and in the patch text) > Signed-off-by: Samuel Lijin <sxlijin@gmail.com> > --- > t/t7501-commit.sh | 45 ++++++++++++++++++++++++++++++++++++++++----- > 1 file changed, 40 insertions(+), 5 deletions(-) > > diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh > index fa61b1a4e..be087e73f 100755 > --- a/t/t7501-commit.sh > +++ b/t/t7501-commit.sh > @@ -652,7 +652,8 @@ test_expect_success '--only works on to-be-born branch' ' > test_cmp expected actual > ' > > -test_expect_success '--dry-run with conflicts fixed from a merge' ' > +# set up env for tests of --dry-run given fixed/unfixed merge conflicts > +test_expect_success 'setup env with unfixed merge conflicts' ' > # setup two branches with conflicting information > # in the same file, resolve the conflict, > # call commit with --dry-run > @@ -665,11 +666,45 @@ test_expect_success '--dry-run with conflicts fixed from a merge' ' > git checkout -b branch-2 HEAD^1 && > echo "commit-2-state" >test-file && > git commit -m "commit 2" -i test-file && > - ! $(git merge --no-commit commit-1) && > - echo "commit-2-state" >test-file && > + test_expect_code 1 git merge --no-commit commit-1 The original is bad and also embarrassing. Whatever comes out of the standard output of "git merge" is $IFS split and executed as a shell command (which likely results in "no such command" failure) and it tries to make sure that a failure happens. The right way to write that line (without your enhancement in this patch) would have been: test_must_fail git merge --no-commit commit-1 && I doubt it is a good idea to hardcode exit status of 1 by using test_expect_code, though. "git merge --help" does not say anything about "1 means this failure, 2 means that failure, 3 means that other failure". And my quick forward scan of this series does not tell me that you are trying to declare that from here on we _will_ make that promise to the end users by carving the exit status(es) in stone. The same about "git commit"'s exit code in the following four tests. > +' > + > +test_expect_success '--dry-run with unfixed merge conflicts' ' > + test_expect_code 1 git commit --dry-run > +' > + > +test_expect_success '--short with unfixed merge conflicts' ' > + test_expect_code 1 git commit --short > +' > + > +test_expect_success '--porcelain with unfixed merge conflicts' ' > + test_expect_code 1 git commit --porcelain > +' > + > +test_expect_success '--long with unfixed merge conflicts' ' > + test_expect_code 1 git commit --long > +' > + > +test_expect_success '--dry-run with conflicts fixed from a merge' ' > + echo "merge-conflicts-fixed" >test-file && The original test pretended that we resolved favouring the current state with "commit-2-state" in the file, as if we ran "-s ours". Is there a reason why we now use a different contents, or is this just a change based on subjective preference? Not saying that the latter is necessrily bad; just trying to understand why we are making this change. > git add test-file && > - git commit --dry-run && > - git commit -m "conflicts fixed from merge." > + git commit --dry-run OK, the original tried --dry-run to ensure it exited with 0 status (i.e. have something to commit) and then did a commit to record the updated state with a message. You are checking only the dry-run part, leaving the check of the final commit's status to another test. > +' > + > +test_expect_failure '--short with conflicts fixed from a merge' ' > + git commit --short > +' With "test_expect_failure", you are saying that "--short" _should_ exit with 0 but currently it does not. An untold expectation is that even with the breakage with the exit code, the command still honors the (implicit) --dry-run correctly and does not create a new commit. That was actually tested in the original. By &&-chaining like this git commit --dry-run && git commit -m "conflicts fixed from merge." we would have noticed if a newly introduced bug caused the first step "commit --dry-run" to return non-zero status (because then the step would fail), or if it stopped being dry-run and made a commit (because then the next step would fail with "nothing to commit"). But by splitting these into separate tests, the patch makes such a potential failure with "git commit --short" break the later steps. Not very nice. It may be a better change to just do in the original one git add test-file && git commit --dry-run && + git commit --short && + git commit --long && + git commit --porcelain && git commit -m "conflicts fixed from merge." without adding these new and separate tests, and then mark that one to expect a failure (because it would pass up to the --dry-run commit, but the --short commit would fail) at this step, perhaps? > +test_expect_failure '--porcelain with conflicts fixed from a merge' ' > + git commit --porcelain > +' > + > +test_expect_success '--long with conflicts fixed from a merge' ' > + git commit --long > +' > + > +test_expect_success '--message with conflicts fixed from a merge' ' > + git commit --message "conflicts fixed from merge." > ' > > test_done ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v3 1/3] t7501: add merge conflict tests for dry run 2018-07-17 17:05 ` Junio C Hamano @ 2018-07-17 17:45 ` Junio C Hamano 0 siblings, 0 replies; 26+ messages in thread From: Junio C Hamano @ 2018-07-17 17:45 UTC (permalink / raw) To: Samuel Lijin; +Cc: git Junio C Hamano <gitster@pobox.com> writes: > But by splitting these into separate tests, the patch makes such a > potential failure with "git commit --short" break the later steps. > > Not very nice. > > It may be a better change to just do in the original one > > git add test-file && > git commit --dry-run && > + git commit --short && > + git commit --long && > + git commit --porcelain && > git commit -m "conflicts fixed from merge." > > without adding these new and separate tests, and then mark that one > to expect a failure (because it would pass up to the --dry-run > commit, but the --short commit would fail) at this step, perhaps? Of course, if you want to be more thorough, anticipating that other people in their future updates may break --short but not --long or --porcelain, testing each option in separate test_expect_success is a necessary way to do so, but then you'd need to actually be more thorough, by not merely running each of them in separate test_expect_success block but also arranging that each of them start in an expected state to try the thing we want it to try. That is for opt in --dry-run --short --long --porcelain do test_expect_success "commit $opt" ' set up the conflicted state after merge && git commit $opt ' done where the "set up the state" part makes sure it can tolerate potential mistakes of previous run of "git commit $opt" (e.g. it by mistake made a commit, making the index identical to HEAD and taking us out of "merge in progress" state). But from your 1/3 I did not get the impression that you particularly want to be more thorough, and from your 3/3 I did not get the impression that you anticipate --short/--long/--porcelain may get broken independently. And if that is the case, then chaining all of them together like the above is a more honest way to express that we are only doing a minimum set of testing. Thanks. ^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH v3 2/3] wt-status: teach wt_status_collect about merges in progress 2018-04-26 9:25 ` [PATCH v2 0/2] Fix --short and --porcelain options for commit Samuel Lijin 2018-07-15 11:08 ` [PATCH v3 0/3] Fix --short/--porcelain options for git commit Samuel Lijin 2018-07-15 11:08 ` [PATCH v3 1/3] t7501: add merge conflict tests for " Samuel Lijin @ 2018-07-15 11:08 ` Samuel Lijin 2018-07-17 17:15 ` Junio C Hamano 2018-07-15 11:08 ` [PATCH v3 3/3] commit: fix exit code for --short/--porcelain Samuel Lijin 3 siblings, 1 reply; 26+ messages in thread From: Samuel Lijin @ 2018-07-15 11:08 UTC (permalink / raw) To: git; +Cc: Samuel Lijin To fix the breakages documented by t7501, the next patch in this series will teach wt_status_collect() to set the committable bit, instead of having wt_longstatus_print_updated() and show_merge_in_progress() set it (which is what currently happens). Unfortunately, wt_status_collect() needs to know whether or not there is a merge in progress to set the bit correctly, so teach its (two) callers to create, initialize, and pass in instances of wt_status_state, which records this metadata. Since wt_longstatus_print() and show_merge_in_progress() are in the same callpaths and currently create and init copies of wt_status_state, remove that logic and instead pass wt_status_state through. Make wt_status_get_state easier to use, add a helper method to clean up wt_status_state, const-ify as many struct pointers in method signatures as possible, and add a FIXME for a struct pointer which should be const but isn't (that this patch series will not address). Signed-off-by: Samuel Lijin <sxlijin@gmail.com> --- builtin/commit.c | 32 ++++---- ref-filter.c | 3 +- wt-status.c | 188 +++++++++++++++++++++++------------------------ wt-status.h | 13 ++-- 4 files changed, 120 insertions(+), 116 deletions(-) diff --git a/builtin/commit.c b/builtin/commit.c index 37fcb55ab..79ef4f11a 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -463,6 +463,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix static int run_status(FILE *fp, const char *index_file, const char *prefix, int nowarn, struct wt_status *s) { + struct wt_status_state state; struct object_id oid; if (s->relative_paths) @@ -482,10 +483,12 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int s->status_format = status_format; s->ignore_submodule_arg = ignore_submodule_arg; - wt_status_collect(s); - wt_status_print(s); + wt_status_get_state(s, &state); + wt_status_collect(s, &state); + wt_status_print(s, &state); + wt_status_clear_state(&state); - return s->commitable; + return s->committable; } static int is_a_merge(const struct commit *current_head) @@ -631,7 +634,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, { struct stat statbuf; struct strbuf committer_ident = STRBUF_INIT; - int commitable; + int committable; struct strbuf sb = STRBUF_INIT; const char *hook_arg1 = NULL; const char *hook_arg2 = NULL; @@ -848,7 +851,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, saved_color_setting = s->use_color; s->use_color = 0; - commitable = run_status(s->fp, index_file, prefix, 1, s); + committable = run_status(s->fp, index_file, prefix, 1, s); s->use_color = saved_color_setting; } else { struct object_id oid; @@ -866,7 +869,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, for (i = 0; i < active_nr; i++) if (ce_intent_to_add(active_cache[i])) ita_nr++; - commitable = active_nr - ita_nr > 0; + committable = active_nr - ita_nr > 0; } else { /* * Unless the user did explicitly request a submodule @@ -882,7 +885,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, if (ignore_submodule_arg && !strcmp(ignore_submodule_arg, "all")) flags.ignore_submodules = 1; - commitable = index_differs_from(parent, &flags, 1); + committable = index_differs_from(parent, &flags, 1); } } strbuf_release(&committer_ident); @@ -894,7 +897,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, * explicit --allow-empty. In the cherry-pick case, it may be * empty due to conflict resolution, which the user should okay. */ - if (!commitable && whence != FROM_MERGE && !allow_empty && + if (!committable && whence != FROM_MERGE && !allow_empty && !(amend && is_a_merge(current_head))) { s->display_comment_prefix = old_display_comment_prefix; run_status(stdout, index_file, prefix, 0, s); @@ -1164,14 +1167,14 @@ static int parse_and_validate_options(int argc, const char *argv[], static int dry_run_commit(int argc, const char **argv, const char *prefix, const struct commit *current_head, struct wt_status *s) { - int commitable; + int committable; const char *index_file; index_file = prepare_index(argc, argv, prefix, current_head, 1); - commitable = run_status(stdout, index_file, prefix, 0, s); + committable = run_status(stdout, index_file, prefix, 0, s); rollback_index_files(); - return commitable ? 0 : 1; + return committable ? 0 : 1; } static int parse_status_slot(const char *slot) @@ -1266,6 +1269,7 @@ static int git_status_config(const char *k, const char *v, void *cb) int cmd_status(int argc, const char **argv, const char *prefix) { static struct wt_status s; + struct wt_status_state state; int fd; struct object_id oid; static struct option builtin_status_options[] = { @@ -1338,7 +1342,8 @@ int cmd_status(int argc, const char **argv, const char *prefix) s.status_format = status_format; s.verbose = verbose; - wt_status_collect(&s); + wt_status_get_state(&s, &state); + wt_status_collect(&s, &state); if (0 <= fd) update_index_if_able(&the_index, &index_lock); @@ -1346,7 +1351,8 @@ int cmd_status(int argc, const char **argv, const char *prefix) if (s.relative_paths) s.prefix = prefix; - wt_status_print(&s); + wt_status_print(&s, &state); + wt_status_clear_state(&state); return 0; } diff --git a/ref-filter.c b/ref-filter.c index 9a333e21b..280ef9713 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -1306,8 +1306,7 @@ char *get_head_description(void) { struct strbuf desc = STRBUF_INIT; struct wt_status_state state; - memset(&state, 0, sizeof(state)); - wt_status_get_state(&state, 1); + wt_status_get_state(NULL, &state); if (state.rebase_in_progress || state.rebase_interactive_in_progress) strbuf_addf(&desc, _("(no branch, rebasing %s)"), diff --git a/wt-status.c b/wt-status.c index 50815e5fa..75d389944 100644 --- a/wt-status.c +++ b/wt-status.c @@ -33,7 +33,7 @@ static char default_wt_status_colors[][COLOR_MAXLEN] = { GIT_COLOR_NIL, /* WT_STATUS_ONBRANCH */ }; -static const char *color(int slot, struct wt_status *s) +static const char *color(int slot, const struct wt_status *s) { const char *c = ""; if (want_color(s->use_color)) @@ -43,7 +43,7 @@ static const char *color(int slot, struct wt_status *s) return c; } -static void status_vprintf(struct wt_status *s, int at_bol, const char *color, +static void status_vprintf(const struct wt_status *s, int at_bol, const char *color, const char *fmt, va_list ap, const char *trail) { struct strbuf sb = STRBUF_INIT; @@ -89,7 +89,7 @@ static void status_vprintf(struct wt_status *s, int at_bol, const char *color, strbuf_release(&sb); } -void status_printf_ln(struct wt_status *s, const char *color, +void status_printf_ln(const struct wt_status *s, const char *color, const char *fmt, ...) { va_list ap; @@ -99,7 +99,7 @@ void status_printf_ln(struct wt_status *s, const char *color, va_end(ap); } -void status_printf(struct wt_status *s, const char *color, +void status_printf(const struct wt_status *s, const char *color, const char *fmt, ...) { va_list ap; @@ -109,7 +109,7 @@ void status_printf(struct wt_status *s, const char *color, va_end(ap); } -static void status_printf_more(struct wt_status *s, const char *color, +static void status_printf_more(const struct wt_status *s, const char *color, const char *fmt, ...) { va_list ap; @@ -140,7 +140,7 @@ void wt_status_prepare(struct wt_status *s) s->display_comment_prefix = 0; } -static void wt_longstatus_print_unmerged_header(struct wt_status *s) +static void wt_longstatus_print_unmerged_header(const struct wt_status *s) { int i; int del_mod_conflict = 0; @@ -192,7 +192,7 @@ static void wt_longstatus_print_unmerged_header(struct wt_status *s) status_printf_ln(s, c, "%s", ""); } -static void wt_longstatus_print_cached_header(struct wt_status *s) +static void wt_longstatus_print_cached_header(const struct wt_status *s) { const char *c = color(WT_STATUS_HEADER, s); @@ -208,7 +208,7 @@ static void wt_longstatus_print_cached_header(struct wt_status *s) status_printf_ln(s, c, "%s", ""); } -static void wt_longstatus_print_dirty_header(struct wt_status *s, +static void wt_longstatus_print_dirty_header(const struct wt_status *s, int has_deleted, int has_dirty_submodules) { @@ -227,7 +227,7 @@ static void wt_longstatus_print_dirty_header(struct wt_status *s, status_printf_ln(s, c, "%s", ""); } -static void wt_longstatus_print_other_header(struct wt_status *s, +static void wt_longstatus_print_other_header(const struct wt_status *s, const char *what, const char *how) { @@ -239,7 +239,7 @@ static void wt_longstatus_print_other_header(struct wt_status *s, status_printf_ln(s, c, "%s", ""); } -static void wt_longstatus_print_trailer(struct wt_status *s) +static void wt_longstatus_print_trailer(const struct wt_status *s) { status_printf_ln(s, color(WT_STATUS_HEADER, s), "%s", ""); } @@ -305,7 +305,7 @@ static int maxwidth(const char *(*label)(int), int minval, int maxval) return result; } -static void wt_longstatus_print_unmerged_data(struct wt_status *s, +static void wt_longstatus_print_unmerged_data(const struct wt_status *s, struct string_list_item *it) { const char *c = color(WT_STATUS_UNMERGED, s); @@ -332,7 +332,7 @@ static void wt_longstatus_print_unmerged_data(struct wt_status *s, strbuf_release(&onebuf); } -static void wt_longstatus_print_change_data(struct wt_status *s, +static void wt_longstatus_print_change_data(const struct wt_status *s, int change_type, struct string_list_item *it) { @@ -718,7 +718,7 @@ static void wt_status_collect_untracked(struct wt_status *s) s->untracked_in_ms = (getnanotime() - t_begin) / 1000000; } -void wt_status_collect(struct wt_status *s) +void wt_status_collect(struct wt_status *s, const struct wt_status_state *state) { wt_status_collect_changes_worktree(s); @@ -726,10 +726,11 @@ void wt_status_collect(struct wt_status *s) wt_status_collect_changes_initial(s); else wt_status_collect_changes_index(s); + wt_status_collect_untracked(s); } -static void wt_longstatus_print_unmerged(struct wt_status *s) +static void wt_longstatus_print_unmerged(const struct wt_status *s) { int shown_header = 0; int i; @@ -767,7 +768,7 @@ static void wt_longstatus_print_updated(struct wt_status *s) continue; if (!shown_header) { wt_longstatus_print_cached_header(s); - s->commitable = 1; + s->committable = 1; shown_header = 1; } wt_longstatus_print_change_data(s, WT_STATUS_UPDATED, it); @@ -781,7 +782,7 @@ static void wt_longstatus_print_updated(struct wt_status *s) * 0 : no change * 1 : some change but no delete */ -static int wt_status_check_worktree_changes(struct wt_status *s, +static int wt_status_check_worktree_changes(const struct wt_status *s, int *dirty_submodules) { int i; @@ -805,7 +806,7 @@ static int wt_status_check_worktree_changes(struct wt_status *s, return changes; } -static void wt_longstatus_print_changed(struct wt_status *s) +static void wt_longstatus_print_changed(const struct wt_status *s) { int i, dirty_submodules; int worktree_changes = wt_status_check_worktree_changes(s, &dirty_submodules); @@ -837,7 +838,7 @@ static int stash_count_refs(struct object_id *ooid, struct object_id *noid, return 0; } -static void wt_longstatus_print_stash_summary(struct wt_status *s) +static void wt_longstatus_print_stash_summary(const struct wt_status *s) { int stash_count = 0; @@ -849,7 +850,7 @@ static void wt_longstatus_print_stash_summary(struct wt_status *s) stash_count); } -static void wt_longstatus_print_submodule_summary(struct wt_status *s, int uncommitted) +static void wt_longstatus_print_submodule_summary(const struct wt_status *s, int uncommitted) { struct child_process sm_summary = CHILD_PROCESS_INIT; struct strbuf cmd_stdout = STRBUF_INIT; @@ -895,8 +896,8 @@ static void wt_longstatus_print_submodule_summary(struct wt_status *s, int uncom strbuf_release(&summary); } -static void wt_longstatus_print_other(struct wt_status *s, - struct string_list *l, +static void wt_longstatus_print_other(const struct wt_status *s, + const struct string_list *l, const char *what, const char *how) { @@ -969,7 +970,7 @@ void wt_status_add_cut_line(FILE *fp) strbuf_release(&buf); } -static void wt_longstatus_print_verbose(struct wt_status *s) +static void wt_longstatus_print_verbose(const struct wt_status *s) { struct rev_info rev; struct setup_revision_opt opt; @@ -1000,7 +1001,7 @@ static void wt_longstatus_print_verbose(struct wt_status *s) rev.diffopt.use_color = 0; wt_status_add_cut_line(s->fp); } - if (s->verbose > 1 && s->commitable) { + if (s->verbose > 1 && s->committable) { /* print_updated() printed a header, so do we */ if (s->fp != stdout) wt_longstatus_print_trailer(s); @@ -1021,7 +1022,7 @@ static void wt_longstatus_print_verbose(struct wt_status *s) } } -static void wt_longstatus_print_tracking(struct wt_status *s) +static void wt_longstatus_print_tracking(const struct wt_status *s) { struct strbuf sb = STRBUF_INIT; const char *cp, *ep, *branch_name; @@ -1055,7 +1056,7 @@ static void wt_longstatus_print_tracking(struct wt_status *s) strbuf_release(&sb); } -static int has_unmerged(struct wt_status *s) +static int has_unmerged(const struct wt_status *s) { int i; @@ -1069,7 +1070,7 @@ static int has_unmerged(struct wt_status *s) } static void show_merge_in_progress(struct wt_status *s, - struct wt_status_state *state, + const struct wt_status_state *state, const char *color) { if (has_unmerged(s)) { @@ -1081,7 +1082,7 @@ static void show_merge_in_progress(struct wt_status *s, _(" (use \"git merge --abort\" to abort the merge)")); } } else { - s-> commitable = 1; + s-> committable = 1; status_printf_ln(s, color, _("All conflicts fixed but you are still merging.")); if (s->hints) @@ -1091,8 +1092,8 @@ static void show_merge_in_progress(struct wt_status *s, wt_longstatus_print_trailer(s); } -static void show_am_in_progress(struct wt_status *s, - struct wt_status_state *state, +static void show_am_in_progress(const struct wt_status *s, + const struct wt_status_state *state, const char *color) { status_printf_ln(s, color, @@ -1130,7 +1131,7 @@ static char *read_line_from_git_path(const char *filename) } } -static int split_commit_in_progress(struct wt_status *s) +static int split_commit_in_progress(const struct wt_status *s) { int split_in_progress = 0; char *head, *orig_head, *rebase_amend, *rebase_orig_head; @@ -1224,8 +1225,8 @@ static int read_rebase_todolist(const char *fname, struct string_list *lines) return 0; } -static void show_rebase_information(struct wt_status *s, - struct wt_status_state *state, +static void show_rebase_information(const struct wt_status *s, + const struct wt_status_state *state, const char *color) { if (state->rebase_interactive_in_progress) { @@ -1278,8 +1279,8 @@ static void show_rebase_information(struct wt_status *s, } } -static void print_rebase_state(struct wt_status *s, - struct wt_status_state *state, +static void print_rebase_state(const struct wt_status *s, + const struct wt_status_state *state, const char *color) { if (state->branch) @@ -1292,8 +1293,8 @@ static void print_rebase_state(struct wt_status *s, _("You are currently rebasing.")); } -static void show_rebase_in_progress(struct wt_status *s, - struct wt_status_state *state, +static void show_rebase_in_progress(const struct wt_status *s, + const struct wt_status_state *state, const char *color) { struct stat st; @@ -1345,8 +1346,8 @@ static void show_rebase_in_progress(struct wt_status *s, wt_longstatus_print_trailer(s); } -static void show_cherry_pick_in_progress(struct wt_status *s, - struct wt_status_state *state, +static void show_cherry_pick_in_progress(const struct wt_status *s, + const struct wt_status_state *state, const char *color) { status_printf_ln(s, color, _("You are currently cherry-picking commit %s."), @@ -1364,8 +1365,8 @@ static void show_cherry_pick_in_progress(struct wt_status *s, wt_longstatus_print_trailer(s); } -static void show_revert_in_progress(struct wt_status *s, - struct wt_status_state *state, +static void show_revert_in_progress(const struct wt_status *s, + const struct wt_status_state *state, const char *color) { status_printf_ln(s, color, _("You are currently reverting commit %s."), @@ -1383,8 +1384,8 @@ static void show_revert_in_progress(struct wt_status *s, wt_longstatus_print_trailer(s); } -static void show_bisect_in_progress(struct wt_status *s, - struct wt_status_state *state, +static void show_bisect_in_progress(const struct wt_status *s, + const struct wt_status_state *state, const char *color) { if (state->branch) @@ -1538,12 +1539,16 @@ int wt_status_check_bisect(const struct worktree *wt, return 0; } -void wt_status_get_state(struct wt_status_state *state, - int get_detached_from) +void wt_status_get_state( + const struct wt_status *s, struct wt_status_state *state) { + int get_detached_from = + (s == NULL) || (s->branch && !strcmp(s->branch, "HEAD")); struct stat st; struct object_id oid; + memset(state, 0, sizeof(*state)); + if (!stat(git_path_merge_head(), &st)) { state->merge_in_progress = 1; } else if (wt_status_check_rebase(NULL, state)) { @@ -1564,8 +1569,15 @@ void wt_status_get_state(struct wt_status_state *state, wt_status_get_detached_from(state); } +void wt_status_clear_state(struct wt_status_state *state) +{ + free(state->branch); + free(state->onto); + free(state->detached_from); +} + static void wt_longstatus_print_state(struct wt_status *s, - struct wt_status_state *state) + const struct wt_status_state *state) { const char *state_color = color(WT_STATUS_HEADER, s); if (state->merge_in_progress) @@ -1582,30 +1594,25 @@ static void wt_longstatus_print_state(struct wt_status *s, show_bisect_in_progress(s, state, state_color); } -static void wt_longstatus_print(struct wt_status *s) +static void wt_longstatus_print(struct wt_status *s, const struct wt_status_state *state) { const char *branch_color = color(WT_STATUS_ONBRANCH, s); const char *branch_status_color = color(WT_STATUS_HEADER, s); - struct wt_status_state state; - - memset(&state, 0, sizeof(state)); - wt_status_get_state(&state, - s->branch && !strcmp(s->branch, "HEAD")); if (s->branch) { const char *on_what = _("On branch "); const char *branch_name = s->branch; if (!strcmp(branch_name, "HEAD")) { branch_status_color = color(WT_STATUS_NOBRANCH, s); - if (state.rebase_in_progress || state.rebase_interactive_in_progress) { - if (state.rebase_interactive_in_progress) + if (state->rebase_in_progress || state->rebase_interactive_in_progress) { + if (state->rebase_interactive_in_progress) on_what = _("interactive rebase in progress; onto "); else on_what = _("rebase in progress; onto "); - branch_name = state.onto; - } else if (state.detached_from) { - branch_name = state.detached_from; - if (state.detached_at) + branch_name = state->onto; + } else if (state->detached_from) { + branch_name = state->detached_from; + if (state->detached_at) on_what = _("HEAD detached at "); else on_what = _("HEAD detached from "); @@ -1622,10 +1629,7 @@ static void wt_longstatus_print(struct wt_status *s) wt_longstatus_print_tracking(s); } - wt_longstatus_print_state(s, &state); - free(state.branch); - free(state.onto); - free(state.detached_from); + wt_longstatus_print_state(s, state); if (s->is_initial) { status_printf_ln(s, color(WT_STATUS_HEADER, s), "%s", ""); @@ -1657,14 +1661,14 @@ static void wt_longstatus_print(struct wt_status *s) "new files yourself (see 'git help status')."), s->untracked_in_ms / 1000.0); } - } else if (s->commitable) + } else if (s->committable) status_printf_ln(s, GIT_COLOR_NORMAL, _("Untracked files not listed%s"), s->hints ? _(" (use -u option to show untracked files)") : ""); if (s->verbose) wt_longstatus_print_verbose(s); - if (!s->commitable) { + if (!s->committable) { if (s->amend) status_printf_ln(s, GIT_COLOR_NORMAL, _("No changes")); else if (s->nowarn) @@ -1700,7 +1704,7 @@ static void wt_longstatus_print(struct wt_status *s) } static void wt_shortstatus_unmerged(struct string_list_item *it, - struct wt_status *s) + const struct wt_status *s) { struct wt_status_change_data *d = it->util; const char *how = "??"; @@ -1727,7 +1731,7 @@ static void wt_shortstatus_unmerged(struct string_list_item *it, } static void wt_shortstatus_status(struct string_list_item *it, - struct wt_status *s) + const struct wt_status *s) { struct wt_status_change_data *d = it->util; @@ -1770,7 +1774,7 @@ static void wt_shortstatus_status(struct string_list_item *it, } static void wt_shortstatus_other(struct string_list_item *it, - struct wt_status *s, const char *sign) + const struct wt_status *s, const char *sign) { if (s->null_termination) { fprintf(stdout, "%s %s%c", sign, it->string, 0); @@ -1784,7 +1788,7 @@ static void wt_shortstatus_other(struct string_list_item *it, } } -static void wt_shortstatus_print_tracking(struct wt_status *s) +static void wt_shortstatus_print_tracking(const struct wt_status *s) { struct branch *branch; const char *header_color = color(WT_STATUS_HEADER, s); @@ -1860,7 +1864,7 @@ static void wt_shortstatus_print_tracking(struct wt_status *s) fputc(s->null_termination ? '\0' : '\n', s->fp); } -static void wt_shortstatus_print(struct wt_status *s) +static void wt_shortstatus_print(const struct wt_status *s) { struct string_list_item *it; @@ -1924,18 +1928,14 @@ static void wt_porcelain_print(struct wt_status *s) * upstream. When AHEAD_BEHIND_QUICK is requested and the branches * are different, '?' will be substituted for the actual count. */ -static void wt_porcelain_v2_print_tracking(struct wt_status *s) +static void wt_porcelain_v2_print_tracking(const struct wt_status *s, const struct wt_status_state *state) { struct branch *branch; const char *base; const char *branch_name; - struct wt_status_state state; int ab_info, nr_ahead, nr_behind; char eol = s->null_termination ? '\0' : '\n'; - memset(&state, 0, sizeof(state)); - wt_status_get_state(&state, s->branch && !strcmp(s->branch, "HEAD")); - fprintf(s->fp, "# branch.oid %s%c", (s->is_initial ? "(initial)" : sha1_to_hex(s->sha1_commit)), eol); @@ -1946,10 +1946,10 @@ static void wt_porcelain_v2_print_tracking(struct wt_status *s) if (!strcmp(s->branch, "HEAD")) { fprintf(s->fp, "# branch.head %s%c", "(detached)", eol); - if (state.rebase_in_progress || state.rebase_interactive_in_progress) - branch_name = state.onto; - else if (state.detached_from) - branch_name = state.detached_from; + if (state->rebase_in_progress || state->rebase_interactive_in_progress) + branch_name = state->onto; + else if (state->detached_from) + branch_name = state->detached_from; else branch_name = ""; } else { @@ -1983,10 +1983,6 @@ static void wt_porcelain_v2_print_tracking(struct wt_status *s) } } } - - free(state.branch); - free(state.onto); - free(state.detached_from); } /* @@ -1994,7 +1990,7 @@ static void wt_porcelain_v2_print_tracking(struct wt_status *s) * fixed-length string of characters in the buffer provided. */ static void wt_porcelain_v2_submodule_state( - struct wt_status_change_data *d, + const struct wt_status_change_data *d, char sub[5]) { if (S_ISGITLINK(d->mode_head) || @@ -2017,8 +2013,8 @@ static void wt_porcelain_v2_submodule_state( * Fix-up changed entries before we print them. */ static void wt_porcelain_v2_fix_up_changed( - struct string_list_item *it, - struct wt_status *s) + const struct string_list_item *it, + const struct wt_status *s) { struct wt_status_change_data *d = it->util; @@ -2066,8 +2062,8 @@ static void wt_porcelain_v2_fix_up_changed( * Print porcelain v2 info for tracked entries with changes. */ static void wt_porcelain_v2_print_changed_entry( - struct string_list_item *it, - struct wt_status *s) + const struct string_list_item *it, + const struct wt_status *s) { struct wt_status_change_data *d = it->util; struct strbuf buf = STRBUF_INIT; @@ -2130,8 +2126,8 @@ static void wt_porcelain_v2_print_changed_entry( * Print porcelain v2 status info for unmerged entries. */ static void wt_porcelain_v2_print_unmerged_entry( - struct string_list_item *it, - struct wt_status *s) + const struct string_list_item *it, + const struct wt_status *s) { struct wt_status_change_data *d = it->util; const struct cache_entry *ce; @@ -2211,8 +2207,8 @@ static void wt_porcelain_v2_print_unmerged_entry( * Print porcelain V2 status info for untracked and ignored entries. */ static void wt_porcelain_v2_print_other( - struct string_list_item *it, - struct wt_status *s, + const struct string_list_item *it, + const struct wt_status *s, char prefix) { struct strbuf buf = STRBUF_INIT; @@ -2242,14 +2238,14 @@ static void wt_porcelain_v2_print_other( * [<v2_ignored_items>]* * */ -static void wt_porcelain_v2_print(struct wt_status *s) +static void wt_porcelain_v2_print(const struct wt_status *s, const struct wt_status_state *state) { struct wt_status_change_data *d; struct string_list_item *it; int i; if (s->show_branch) - wt_porcelain_v2_print_tracking(s); + wt_porcelain_v2_print_tracking(s, state); for (i = 0; i < s->change.nr; i++) { it = &(s->change.items[i]); @@ -2276,7 +2272,9 @@ static void wt_porcelain_v2_print(struct wt_status *s) } } -void wt_status_print(struct wt_status *s) +// FIXME: `struct wt_status *` should be `const struct wt_status` but because +// `wt_porcelain_print()` modifies it, that has to first be fixed +void wt_status_print(struct wt_status *s, const struct wt_status_state *state) { switch (s->status_format) { case STATUS_FORMAT_SHORT: @@ -2286,14 +2284,14 @@ void wt_status_print(struct wt_status *s) wt_porcelain_print(s); break; case STATUS_FORMAT_PORCELAIN_V2: - wt_porcelain_v2_print(s); + wt_porcelain_v2_print(s, state); break; case STATUS_FORMAT_UNSPECIFIED: die("BUG: finalize_deferred_config() should have been called"); break; case STATUS_FORMAT_NONE: case STATUS_FORMAT_LONG: - wt_longstatus_print(s); + wt_longstatus_print(s, state); break; } } diff --git a/wt-status.h b/wt-status.h index 430770b85..6cccfd7a0 100644 --- a/wt-status.h +++ b/wt-status.h @@ -94,7 +94,7 @@ struct wt_status { unsigned char sha1_commit[GIT_MAX_RAWSZ]; /* when not Initial */ /* These are computed during processing of the individual sections */ - int commitable; + int committable; int workdir_dirty; const char *index_file; FILE *fp; @@ -126,18 +126,19 @@ struct wt_status_state { size_t wt_status_locate_end(const char *s, size_t len); void wt_status_add_cut_line(FILE *fp); void wt_status_prepare(struct wt_status *s); -void wt_status_print(struct wt_status *s); -void wt_status_collect(struct wt_status *s); -void wt_status_get_state(struct wt_status_state *state, int get_detached_from); +void wt_status_print(struct wt_status *s, const struct wt_status_state *state); +void wt_status_collect(struct wt_status *s, const struct wt_status_state *state); +void wt_status_get_state(const struct wt_status *s, struct wt_status_state *state); +void wt_status_clear_state(struct wt_status_state *state); int wt_status_check_rebase(const struct worktree *wt, struct wt_status_state *state); int wt_status_check_bisect(const struct worktree *wt, struct wt_status_state *state); __attribute__((format (printf, 3, 4))) -void status_printf_ln(struct wt_status *s, const char *color, const char *fmt, ...); +void status_printf_ln(const struct wt_status *s, const char *color, const char *fmt, ...); __attribute__((format (printf, 3, 4))) -void status_printf(struct wt_status *s, const char *color, const char *fmt, ...); +void status_printf(const struct wt_status *s, const char *color, const char *fmt, ...); /* The following functions expect that the caller took care of reading the index. */ int has_unstaged_changes(int ignore_submodules); -- 2.18.0 ^ permalink raw reply related [flat|nested] 26+ messages in thread
* Re: [PATCH v3 2/3] wt-status: teach wt_status_collect about merges in progress 2018-07-15 11:08 ` [PATCH v3 2/3] wt-status: teach wt_status_collect about merges in progress Samuel Lijin @ 2018-07-17 17:15 ` Junio C Hamano 0 siblings, 0 replies; 26+ messages in thread From: Junio C Hamano @ 2018-07-17 17:15 UTC (permalink / raw) To: Samuel Lijin; +Cc: git Samuel Lijin <sxlijin@gmail.com> writes: > To fix the breakages documented by t7501, the next patch in this series > will teach wt_status_collect() to set the committable bit, instead of > having wt_longstatus_print_updated() and show_merge_in_progress() set it > (which is what currently happens). Unfortunately, wt_status_collect() > needs to know whether or not there is a merge in progress to set the bit > correctly, s/correctly,/correctly (a brief desription of why),/ would be nicer. The description might be (after a merge, it is OK for the result to be identical to HEAD, which usually causes a "nothing to commit" error) or something like that. > so teach its (two) callers to create, initialize, and pass > in instances of wt_status_state, which records this metadata. > > Since wt_longstatus_print() and show_merge_in_progress() are in the same > callpaths and currently create and init copies of wt_status_state, > remove that logic and instead pass wt_status_state through. OK. Sounds like a good clean-up. > Make wt_status_get_state easier to use, add a helper method to clean up Your description so far marked function names with trailing (); let's do so consistently for wt_status_get_state(), too. > wt_status_state, const-ify as many struct pointers in method signatures > as possible, and add a FIXME for a struct pointer which should be const > but isn't (that this patch series will not address). "should be but isn't" because...? I am wondering if it is better to leave _all_ constifying to a later effort, if we are leaving some of them behind anyway. It would be better only if it will make this patch easier to read if we did so. Also you did s/commitable/committable/ everywhere, which was somewhat distracting. It would have been nicer to follow if that were a separate preparatory clean-up patch. > > Signed-off-by: Samuel Lijin <sxlijin@gmail.com> > --- > builtin/commit.c | 32 ++++---- > ref-filter.c | 3 +- > wt-status.c | 188 +++++++++++++++++++++++------------------------ > wt-status.h | 13 ++-- > 4 files changed, 120 insertions(+), 116 deletions(-) > > diff --git a/builtin/commit.c b/builtin/commit.c > index 37fcb55ab..79ef4f11a 100644 > --- a/builtin/commit.c > +++ b/builtin/commit.c > @@ -463,6 +463,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix > static int run_status(FILE *fp, const char *index_file, const char *prefix, int nowarn, > struct wt_status *s) > { > + struct wt_status_state state; > struct object_id oid; > > if (s->relative_paths) > @@ -482,10 +483,12 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int > s->status_format = status_format; > s->ignore_submodule_arg = ignore_submodule_arg; > > - wt_status_collect(s); > - wt_status_print(s); > + wt_status_get_state(s, &state); > + wt_status_collect(s, &state); > + wt_status_print(s, &state); > + wt_status_clear_state(&state); > > - return s->commitable; > + return s->committable; > } > > static int is_a_merge(const struct commit *current_head) > @@ -631,7 +634,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, > { > struct stat statbuf; > struct strbuf committer_ident = STRBUF_INIT; > - int commitable; > + int committable; > struct strbuf sb = STRBUF_INIT; > const char *hook_arg1 = NULL; > const char *hook_arg2 = NULL; > @@ -848,7 +851,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, > > saved_color_setting = s->use_color; > s->use_color = 0; > - commitable = run_status(s->fp, index_file, prefix, 1, s); > + committable = run_status(s->fp, index_file, prefix, 1, s); > s->use_color = saved_color_setting; > } else { > struct object_id oid; > @@ -866,7 +869,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, > for (i = 0; i < active_nr; i++) > if (ce_intent_to_add(active_cache[i])) > ita_nr++; > - commitable = active_nr - ita_nr > 0; > + committable = active_nr - ita_nr > 0; > } else { > /* > * Unless the user did explicitly request a submodule > @@ -882,7 +885,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, > if (ignore_submodule_arg && > !strcmp(ignore_submodule_arg, "all")) > flags.ignore_submodules = 1; > - commitable = index_differs_from(parent, &flags, 1); > + committable = index_differs_from(parent, &flags, 1); > } > } > strbuf_release(&committer_ident); > @@ -894,7 +897,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, > * explicit --allow-empty. In the cherry-pick case, it may be > * empty due to conflict resolution, which the user should okay. > */ > - if (!commitable && whence != FROM_MERGE && !allow_empty && > + if (!committable && whence != FROM_MERGE && !allow_empty && > !(amend && is_a_merge(current_head))) { > s->display_comment_prefix = old_display_comment_prefix; > run_status(stdout, index_file, prefix, 0, s); > @@ -1164,14 +1167,14 @@ static int parse_and_validate_options(int argc, const char *argv[], > static int dry_run_commit(int argc, const char **argv, const char *prefix, > const struct commit *current_head, struct wt_status *s) > { > - int commitable; > + int committable; > const char *index_file; > > index_file = prepare_index(argc, argv, prefix, current_head, 1); > - commitable = run_status(stdout, index_file, prefix, 0, s); > + committable = run_status(stdout, index_file, prefix, 0, s); > rollback_index_files(); > > - return commitable ? 0 : 1; > + return committable ? 0 : 1; > } > > static int parse_status_slot(const char *slot) > @@ -1266,6 +1269,7 @@ static int git_status_config(const char *k, const char *v, void *cb) > int cmd_status(int argc, const char **argv, const char *prefix) > { > static struct wt_status s; > + struct wt_status_state state; > int fd; > struct object_id oid; > static struct option builtin_status_options[] = { > @@ -1338,7 +1342,8 @@ int cmd_status(int argc, const char **argv, const char *prefix) > s.status_format = status_format; > s.verbose = verbose; > > - wt_status_collect(&s); > + wt_status_get_state(&s, &state); > + wt_status_collect(&s, &state); > > if (0 <= fd) > update_index_if_able(&the_index, &index_lock); > @@ -1346,7 +1351,8 @@ int cmd_status(int argc, const char **argv, const char *prefix) > if (s.relative_paths) > s.prefix = prefix; > > - wt_status_print(&s); > + wt_status_print(&s, &state); > + wt_status_clear_state(&state); > return 0; > } > > diff --git a/ref-filter.c b/ref-filter.c > index 9a333e21b..280ef9713 100644 > --- a/ref-filter.c > +++ b/ref-filter.c > @@ -1306,8 +1306,7 @@ char *get_head_description(void) > { > struct strbuf desc = STRBUF_INIT; > struct wt_status_state state; > - memset(&state, 0, sizeof(state)); > - wt_status_get_state(&state, 1); > + wt_status_get_state(NULL, &state); > if (state.rebase_in_progress || > state.rebase_interactive_in_progress) > strbuf_addf(&desc, _("(no branch, rebasing %s)"), > diff --git a/wt-status.c b/wt-status.c > index 50815e5fa..75d389944 100644 > --- a/wt-status.c > +++ b/wt-status.c > @@ -33,7 +33,7 @@ static char default_wt_status_colors[][COLOR_MAXLEN] = { > GIT_COLOR_NIL, /* WT_STATUS_ONBRANCH */ > }; > > -static const char *color(int slot, struct wt_status *s) > +static const char *color(int slot, const struct wt_status *s) > { > const char *c = ""; > if (want_color(s->use_color)) > @@ -43,7 +43,7 @@ static const char *color(int slot, struct wt_status *s) > return c; > } > > -static void status_vprintf(struct wt_status *s, int at_bol, const char *color, > +static void status_vprintf(const struct wt_status *s, int at_bol, const char *color, > const char *fmt, va_list ap, const char *trail) > { > struct strbuf sb = STRBUF_INIT; > @@ -89,7 +89,7 @@ static void status_vprintf(struct wt_status *s, int at_bol, const char *color, > strbuf_release(&sb); > } > > -void status_printf_ln(struct wt_status *s, const char *color, > +void status_printf_ln(const struct wt_status *s, const char *color, > const char *fmt, ...) > { > va_list ap; > @@ -99,7 +99,7 @@ void status_printf_ln(struct wt_status *s, const char *color, > va_end(ap); > } > > -void status_printf(struct wt_status *s, const char *color, > +void status_printf(const struct wt_status *s, const char *color, > const char *fmt, ...) > { > va_list ap; > @@ -109,7 +109,7 @@ void status_printf(struct wt_status *s, const char *color, > va_end(ap); > } > > -static void status_printf_more(struct wt_status *s, const char *color, > +static void status_printf_more(const struct wt_status *s, const char *color, > const char *fmt, ...) > { > va_list ap; > @@ -140,7 +140,7 @@ void wt_status_prepare(struct wt_status *s) > s->display_comment_prefix = 0; > } > > -static void wt_longstatus_print_unmerged_header(struct wt_status *s) > +static void wt_longstatus_print_unmerged_header(const struct wt_status *s) > { > int i; > int del_mod_conflict = 0; > @@ -192,7 +192,7 @@ static void wt_longstatus_print_unmerged_header(struct wt_status *s) > status_printf_ln(s, c, "%s", ""); > } > > -static void wt_longstatus_print_cached_header(struct wt_status *s) > +static void wt_longstatus_print_cached_header(const struct wt_status *s) > { > const char *c = color(WT_STATUS_HEADER, s); > > @@ -208,7 +208,7 @@ static void wt_longstatus_print_cached_header(struct wt_status *s) > status_printf_ln(s, c, "%s", ""); > } > > -static void wt_longstatus_print_dirty_header(struct wt_status *s, > +static void wt_longstatus_print_dirty_header(const struct wt_status *s, > int has_deleted, > int has_dirty_submodules) > { > @@ -227,7 +227,7 @@ static void wt_longstatus_print_dirty_header(struct wt_status *s, > status_printf_ln(s, c, "%s", ""); > } > > -static void wt_longstatus_print_other_header(struct wt_status *s, > +static void wt_longstatus_print_other_header(const struct wt_status *s, > const char *what, > const char *how) > { > @@ -239,7 +239,7 @@ static void wt_longstatus_print_other_header(struct wt_status *s, > status_printf_ln(s, c, "%s", ""); > } > > -static void wt_longstatus_print_trailer(struct wt_status *s) > +static void wt_longstatus_print_trailer(const struct wt_status *s) > { > status_printf_ln(s, color(WT_STATUS_HEADER, s), "%s", ""); > } > @@ -305,7 +305,7 @@ static int maxwidth(const char *(*label)(int), int minval, int maxval) > return result; > } > > -static void wt_longstatus_print_unmerged_data(struct wt_status *s, > +static void wt_longstatus_print_unmerged_data(const struct wt_status *s, > struct string_list_item *it) > { > const char *c = color(WT_STATUS_UNMERGED, s); > @@ -332,7 +332,7 @@ static void wt_longstatus_print_unmerged_data(struct wt_status *s, > strbuf_release(&onebuf); > } > > -static void wt_longstatus_print_change_data(struct wt_status *s, > +static void wt_longstatus_print_change_data(const struct wt_status *s, > int change_type, > struct string_list_item *it) > { > @@ -718,7 +718,7 @@ static void wt_status_collect_untracked(struct wt_status *s) > s->untracked_in_ms = (getnanotime() - t_begin) / 1000000; > } > > -void wt_status_collect(struct wt_status *s) > +void wt_status_collect(struct wt_status *s, const struct wt_status_state *state) > { > wt_status_collect_changes_worktree(s); > > @@ -726,10 +726,11 @@ void wt_status_collect(struct wt_status *s) > wt_status_collect_changes_initial(s); > else > wt_status_collect_changes_index(s); > + > wt_status_collect_untracked(s); > } > > -static void wt_longstatus_print_unmerged(struct wt_status *s) > +static void wt_longstatus_print_unmerged(const struct wt_status *s) > { > int shown_header = 0; > int i; > @@ -767,7 +768,7 @@ static void wt_longstatus_print_updated(struct wt_status *s) > continue; > if (!shown_header) { > wt_longstatus_print_cached_header(s); > - s->commitable = 1; > + s->committable = 1; > shown_header = 1; > } > wt_longstatus_print_change_data(s, WT_STATUS_UPDATED, it); > @@ -781,7 +782,7 @@ static void wt_longstatus_print_updated(struct wt_status *s) > * 0 : no change > * 1 : some change but no delete > */ > -static int wt_status_check_worktree_changes(struct wt_status *s, > +static int wt_status_check_worktree_changes(const struct wt_status *s, > int *dirty_submodules) > { > int i; > @@ -805,7 +806,7 @@ static int wt_status_check_worktree_changes(struct wt_status *s, > return changes; > } > > -static void wt_longstatus_print_changed(struct wt_status *s) > +static void wt_longstatus_print_changed(const struct wt_status *s) > { > int i, dirty_submodules; > int worktree_changes = wt_status_check_worktree_changes(s, &dirty_submodules); > @@ -837,7 +838,7 @@ static int stash_count_refs(struct object_id *ooid, struct object_id *noid, > return 0; > } > > -static void wt_longstatus_print_stash_summary(struct wt_status *s) > +static void wt_longstatus_print_stash_summary(const struct wt_status *s) > { > int stash_count = 0; > > @@ -849,7 +850,7 @@ static void wt_longstatus_print_stash_summary(struct wt_status *s) > stash_count); > } > > -static void wt_longstatus_print_submodule_summary(struct wt_status *s, int uncommitted) > +static void wt_longstatus_print_submodule_summary(const struct wt_status *s, int uncommitted) > { > struct child_process sm_summary = CHILD_PROCESS_INIT; > struct strbuf cmd_stdout = STRBUF_INIT; > @@ -895,8 +896,8 @@ static void wt_longstatus_print_submodule_summary(struct wt_status *s, int uncom > strbuf_release(&summary); > } > > -static void wt_longstatus_print_other(struct wt_status *s, > - struct string_list *l, > +static void wt_longstatus_print_other(const struct wt_status *s, > + const struct string_list *l, > const char *what, > const char *how) > { > @@ -969,7 +970,7 @@ void wt_status_add_cut_line(FILE *fp) > strbuf_release(&buf); > } > > -static void wt_longstatus_print_verbose(struct wt_status *s) > +static void wt_longstatus_print_verbose(const struct wt_status *s) > { > struct rev_info rev; > struct setup_revision_opt opt; > @@ -1000,7 +1001,7 @@ static void wt_longstatus_print_verbose(struct wt_status *s) > rev.diffopt.use_color = 0; > wt_status_add_cut_line(s->fp); > } > - if (s->verbose > 1 && s->commitable) { > + if (s->verbose > 1 && s->committable) { > /* print_updated() printed a header, so do we */ > if (s->fp != stdout) > wt_longstatus_print_trailer(s); > @@ -1021,7 +1022,7 @@ static void wt_longstatus_print_verbose(struct wt_status *s) > } > } > > -static void wt_longstatus_print_tracking(struct wt_status *s) > +static void wt_longstatus_print_tracking(const struct wt_status *s) > { > struct strbuf sb = STRBUF_INIT; > const char *cp, *ep, *branch_name; > @@ -1055,7 +1056,7 @@ static void wt_longstatus_print_tracking(struct wt_status *s) > strbuf_release(&sb); > } > > -static int has_unmerged(struct wt_status *s) > +static int has_unmerged(const struct wt_status *s) > { > int i; > > @@ -1069,7 +1070,7 @@ static int has_unmerged(struct wt_status *s) > } > > static void show_merge_in_progress(struct wt_status *s, > - struct wt_status_state *state, > + const struct wt_status_state *state, > const char *color) > { > if (has_unmerged(s)) { > @@ -1081,7 +1082,7 @@ static void show_merge_in_progress(struct wt_status *s, > _(" (use \"git merge --abort\" to abort the merge)")); > } > } else { > - s-> commitable = 1; > + s-> committable = 1; > status_printf_ln(s, color, > _("All conflicts fixed but you are still merging.")); > if (s->hints) > @@ -1091,8 +1092,8 @@ static void show_merge_in_progress(struct wt_status *s, > wt_longstatus_print_trailer(s); > } > > -static void show_am_in_progress(struct wt_status *s, > - struct wt_status_state *state, > +static void show_am_in_progress(const struct wt_status *s, > + const struct wt_status_state *state, > const char *color) > { > status_printf_ln(s, color, > @@ -1130,7 +1131,7 @@ static char *read_line_from_git_path(const char *filename) > } > } > > -static int split_commit_in_progress(struct wt_status *s) > +static int split_commit_in_progress(const struct wt_status *s) > { > int split_in_progress = 0; > char *head, *orig_head, *rebase_amend, *rebase_orig_head; > @@ -1224,8 +1225,8 @@ static int read_rebase_todolist(const char *fname, struct string_list *lines) > return 0; > } > > -static void show_rebase_information(struct wt_status *s, > - struct wt_status_state *state, > +static void show_rebase_information(const struct wt_status *s, > + const struct wt_status_state *state, > const char *color) > { > if (state->rebase_interactive_in_progress) { > @@ -1278,8 +1279,8 @@ static void show_rebase_information(struct wt_status *s, > } > } > > -static void print_rebase_state(struct wt_status *s, > - struct wt_status_state *state, > +static void print_rebase_state(const struct wt_status *s, > + const struct wt_status_state *state, > const char *color) > { > if (state->branch) > @@ -1292,8 +1293,8 @@ static void print_rebase_state(struct wt_status *s, > _("You are currently rebasing.")); > } > > -static void show_rebase_in_progress(struct wt_status *s, > - struct wt_status_state *state, > +static void show_rebase_in_progress(const struct wt_status *s, > + const struct wt_status_state *state, > const char *color) > { > struct stat st; > @@ -1345,8 +1346,8 @@ static void show_rebase_in_progress(struct wt_status *s, > wt_longstatus_print_trailer(s); > } > > -static void show_cherry_pick_in_progress(struct wt_status *s, > - struct wt_status_state *state, > +static void show_cherry_pick_in_progress(const struct wt_status *s, > + const struct wt_status_state *state, > const char *color) > { > status_printf_ln(s, color, _("You are currently cherry-picking commit %s."), > @@ -1364,8 +1365,8 @@ static void show_cherry_pick_in_progress(struct wt_status *s, > wt_longstatus_print_trailer(s); > } > > -static void show_revert_in_progress(struct wt_status *s, > - struct wt_status_state *state, > +static void show_revert_in_progress(const struct wt_status *s, > + const struct wt_status_state *state, > const char *color) > { > status_printf_ln(s, color, _("You are currently reverting commit %s."), > @@ -1383,8 +1384,8 @@ static void show_revert_in_progress(struct wt_status *s, > wt_longstatus_print_trailer(s); > } > > -static void show_bisect_in_progress(struct wt_status *s, > - struct wt_status_state *state, > +static void show_bisect_in_progress(const struct wt_status *s, > + const struct wt_status_state *state, > const char *color) > { > if (state->branch) > @@ -1538,12 +1539,16 @@ int wt_status_check_bisect(const struct worktree *wt, > return 0; > } > > -void wt_status_get_state(struct wt_status_state *state, > - int get_detached_from) > +void wt_status_get_state( > + const struct wt_status *s, struct wt_status_state *state) > { > + int get_detached_from = > + (s == NULL) || (s->branch && !strcmp(s->branch, "HEAD")); > struct stat st; > struct object_id oid; > > + memset(state, 0, sizeof(*state)); > + > if (!stat(git_path_merge_head(), &st)) { > state->merge_in_progress = 1; > } else if (wt_status_check_rebase(NULL, state)) { > @@ -1564,8 +1569,15 @@ void wt_status_get_state(struct wt_status_state *state, > wt_status_get_detached_from(state); > } > > +void wt_status_clear_state(struct wt_status_state *state) > +{ > + free(state->branch); > + free(state->onto); > + free(state->detached_from); > +} > + > static void wt_longstatus_print_state(struct wt_status *s, > - struct wt_status_state *state) > + const struct wt_status_state *state) > { > const char *state_color = color(WT_STATUS_HEADER, s); > if (state->merge_in_progress) > @@ -1582,30 +1594,25 @@ static void wt_longstatus_print_state(struct wt_status *s, > show_bisect_in_progress(s, state, state_color); > } > > -static void wt_longstatus_print(struct wt_status *s) > +static void wt_longstatus_print(struct wt_status *s, const struct wt_status_state *state) > { > const char *branch_color = color(WT_STATUS_ONBRANCH, s); > const char *branch_status_color = color(WT_STATUS_HEADER, s); > - struct wt_status_state state; > - > - memset(&state, 0, sizeof(state)); > - wt_status_get_state(&state, > - s->branch && !strcmp(s->branch, "HEAD")); > > if (s->branch) { > const char *on_what = _("On branch "); > const char *branch_name = s->branch; > if (!strcmp(branch_name, "HEAD")) { > branch_status_color = color(WT_STATUS_NOBRANCH, s); > - if (state.rebase_in_progress || state.rebase_interactive_in_progress) { > - if (state.rebase_interactive_in_progress) > + if (state->rebase_in_progress || state->rebase_interactive_in_progress) { > + if (state->rebase_interactive_in_progress) > on_what = _("interactive rebase in progress; onto "); > else > on_what = _("rebase in progress; onto "); > - branch_name = state.onto; > - } else if (state.detached_from) { > - branch_name = state.detached_from; > - if (state.detached_at) > + branch_name = state->onto; > + } else if (state->detached_from) { > + branch_name = state->detached_from; > + if (state->detached_at) > on_what = _("HEAD detached at "); > else > on_what = _("HEAD detached from "); > @@ -1622,10 +1629,7 @@ static void wt_longstatus_print(struct wt_status *s) > wt_longstatus_print_tracking(s); > } > > - wt_longstatus_print_state(s, &state); > - free(state.branch); > - free(state.onto); > - free(state.detached_from); > + wt_longstatus_print_state(s, state); > > if (s->is_initial) { > status_printf_ln(s, color(WT_STATUS_HEADER, s), "%s", ""); > @@ -1657,14 +1661,14 @@ static void wt_longstatus_print(struct wt_status *s) > "new files yourself (see 'git help status')."), > s->untracked_in_ms / 1000.0); > } > - } else if (s->commitable) > + } else if (s->committable) > status_printf_ln(s, GIT_COLOR_NORMAL, _("Untracked files not listed%s"), > s->hints > ? _(" (use -u option to show untracked files)") : ""); > > if (s->verbose) > wt_longstatus_print_verbose(s); > - if (!s->commitable) { > + if (!s->committable) { > if (s->amend) > status_printf_ln(s, GIT_COLOR_NORMAL, _("No changes")); > else if (s->nowarn) > @@ -1700,7 +1704,7 @@ static void wt_longstatus_print(struct wt_status *s) > } > > static void wt_shortstatus_unmerged(struct string_list_item *it, > - struct wt_status *s) > + const struct wt_status *s) > { > struct wt_status_change_data *d = it->util; > const char *how = "??"; > @@ -1727,7 +1731,7 @@ static void wt_shortstatus_unmerged(struct string_list_item *it, > } > > static void wt_shortstatus_status(struct string_list_item *it, > - struct wt_status *s) > + const struct wt_status *s) > { > struct wt_status_change_data *d = it->util; > > @@ -1770,7 +1774,7 @@ static void wt_shortstatus_status(struct string_list_item *it, > } > > static void wt_shortstatus_other(struct string_list_item *it, > - struct wt_status *s, const char *sign) > + const struct wt_status *s, const char *sign) > { > if (s->null_termination) { > fprintf(stdout, "%s %s%c", sign, it->string, 0); > @@ -1784,7 +1788,7 @@ static void wt_shortstatus_other(struct string_list_item *it, > } > } > > -static void wt_shortstatus_print_tracking(struct wt_status *s) > +static void wt_shortstatus_print_tracking(const struct wt_status *s) > { > struct branch *branch; > const char *header_color = color(WT_STATUS_HEADER, s); > @@ -1860,7 +1864,7 @@ static void wt_shortstatus_print_tracking(struct wt_status *s) > fputc(s->null_termination ? '\0' : '\n', s->fp); > } > > -static void wt_shortstatus_print(struct wt_status *s) > +static void wt_shortstatus_print(const struct wt_status *s) > { > struct string_list_item *it; > > @@ -1924,18 +1928,14 @@ static void wt_porcelain_print(struct wt_status *s) > * upstream. When AHEAD_BEHIND_QUICK is requested and the branches > * are different, '?' will be substituted for the actual count. > */ > -static void wt_porcelain_v2_print_tracking(struct wt_status *s) > +static void wt_porcelain_v2_print_tracking(const struct wt_status *s, const struct wt_status_state *state) > { > struct branch *branch; > const char *base; > const char *branch_name; > - struct wt_status_state state; > int ab_info, nr_ahead, nr_behind; > char eol = s->null_termination ? '\0' : '\n'; > > - memset(&state, 0, sizeof(state)); > - wt_status_get_state(&state, s->branch && !strcmp(s->branch, "HEAD")); > - > fprintf(s->fp, "# branch.oid %s%c", > (s->is_initial ? "(initial)" : sha1_to_hex(s->sha1_commit)), > eol); > @@ -1946,10 +1946,10 @@ static void wt_porcelain_v2_print_tracking(struct wt_status *s) > if (!strcmp(s->branch, "HEAD")) { > fprintf(s->fp, "# branch.head %s%c", "(detached)", eol); > > - if (state.rebase_in_progress || state.rebase_interactive_in_progress) > - branch_name = state.onto; > - else if (state.detached_from) > - branch_name = state.detached_from; > + if (state->rebase_in_progress || state->rebase_interactive_in_progress) > + branch_name = state->onto; > + else if (state->detached_from) > + branch_name = state->detached_from; > else > branch_name = ""; > } else { > @@ -1983,10 +1983,6 @@ static void wt_porcelain_v2_print_tracking(struct wt_status *s) > } > } > } > - > - free(state.branch); > - free(state.onto); > - free(state.detached_from); > } > > /* > @@ -1994,7 +1990,7 @@ static void wt_porcelain_v2_print_tracking(struct wt_status *s) > * fixed-length string of characters in the buffer provided. > */ > static void wt_porcelain_v2_submodule_state( > - struct wt_status_change_data *d, > + const struct wt_status_change_data *d, > char sub[5]) > { > if (S_ISGITLINK(d->mode_head) || > @@ -2017,8 +2013,8 @@ static void wt_porcelain_v2_submodule_state( > * Fix-up changed entries before we print them. > */ > static void wt_porcelain_v2_fix_up_changed( > - struct string_list_item *it, > - struct wt_status *s) > + const struct string_list_item *it, > + const struct wt_status *s) > { > struct wt_status_change_data *d = it->util; > > @@ -2066,8 +2062,8 @@ static void wt_porcelain_v2_fix_up_changed( > * Print porcelain v2 info for tracked entries with changes. > */ > static void wt_porcelain_v2_print_changed_entry( > - struct string_list_item *it, > - struct wt_status *s) > + const struct string_list_item *it, > + const struct wt_status *s) > { > struct wt_status_change_data *d = it->util; > struct strbuf buf = STRBUF_INIT; > @@ -2130,8 +2126,8 @@ static void wt_porcelain_v2_print_changed_entry( > * Print porcelain v2 status info for unmerged entries. > */ > static void wt_porcelain_v2_print_unmerged_entry( > - struct string_list_item *it, > - struct wt_status *s) > + const struct string_list_item *it, > + const struct wt_status *s) > { > struct wt_status_change_data *d = it->util; > const struct cache_entry *ce; > @@ -2211,8 +2207,8 @@ static void wt_porcelain_v2_print_unmerged_entry( > * Print porcelain V2 status info for untracked and ignored entries. > */ > static void wt_porcelain_v2_print_other( > - struct string_list_item *it, > - struct wt_status *s, > + const struct string_list_item *it, > + const struct wt_status *s, > char prefix) > { > struct strbuf buf = STRBUF_INIT; > @@ -2242,14 +2238,14 @@ static void wt_porcelain_v2_print_other( > * [<v2_ignored_items>]* > * > */ > -static void wt_porcelain_v2_print(struct wt_status *s) > +static void wt_porcelain_v2_print(const struct wt_status *s, const struct wt_status_state *state) > { > struct wt_status_change_data *d; > struct string_list_item *it; > int i; > > if (s->show_branch) > - wt_porcelain_v2_print_tracking(s); > + wt_porcelain_v2_print_tracking(s, state); > > for (i = 0; i < s->change.nr; i++) { > it = &(s->change.items[i]); > @@ -2276,7 +2272,9 @@ static void wt_porcelain_v2_print(struct wt_status *s) > } > } > > -void wt_status_print(struct wt_status *s) > +// FIXME: `struct wt_status *` should be `const struct wt_status` but because > +// `wt_porcelain_print()` modifies it, that has to first be fixed > +void wt_status_print(struct wt_status *s, const struct wt_status_state *state) > { > switch (s->status_format) { > case STATUS_FORMAT_SHORT: > @@ -2286,14 +2284,14 @@ void wt_status_print(struct wt_status *s) > wt_porcelain_print(s); > break; > case STATUS_FORMAT_PORCELAIN_V2: > - wt_porcelain_v2_print(s); > + wt_porcelain_v2_print(s, state); > break; > case STATUS_FORMAT_UNSPECIFIED: > die("BUG: finalize_deferred_config() should have been called"); > break; > case STATUS_FORMAT_NONE: > case STATUS_FORMAT_LONG: > - wt_longstatus_print(s); > + wt_longstatus_print(s, state); > break; > } > } > diff --git a/wt-status.h b/wt-status.h > index 430770b85..6cccfd7a0 100644 > --- a/wt-status.h > +++ b/wt-status.h > @@ -94,7 +94,7 @@ struct wt_status { > unsigned char sha1_commit[GIT_MAX_RAWSZ]; /* when not Initial */ > > /* These are computed during processing of the individual sections */ > - int commitable; > + int committable; > int workdir_dirty; > const char *index_file; > FILE *fp; > @@ -126,18 +126,19 @@ struct wt_status_state { > size_t wt_status_locate_end(const char *s, size_t len); > void wt_status_add_cut_line(FILE *fp); > void wt_status_prepare(struct wt_status *s); > -void wt_status_print(struct wt_status *s); > -void wt_status_collect(struct wt_status *s); > -void wt_status_get_state(struct wt_status_state *state, int get_detached_from); > +void wt_status_print(struct wt_status *s, const struct wt_status_state *state); > +void wt_status_collect(struct wt_status *s, const struct wt_status_state *state); > +void wt_status_get_state(const struct wt_status *s, struct wt_status_state *state); > +void wt_status_clear_state(struct wt_status_state *state); > int wt_status_check_rebase(const struct worktree *wt, > struct wt_status_state *state); > int wt_status_check_bisect(const struct worktree *wt, > struct wt_status_state *state); > > __attribute__((format (printf, 3, 4))) > -void status_printf_ln(struct wt_status *s, const char *color, const char *fmt, ...); > +void status_printf_ln(const struct wt_status *s, const char *color, const char *fmt, ...); > __attribute__((format (printf, 3, 4))) > -void status_printf(struct wt_status *s, const char *color, const char *fmt, ...); > +void status_printf(const struct wt_status *s, const char *color, const char *fmt, ...); > > /* The following functions expect that the caller took care of reading the index. */ > int has_unstaged_changes(int ignore_submodules); ^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH v3 3/3] commit: fix exit code for --short/--porcelain 2018-04-26 9:25 ` [PATCH v2 0/2] Fix --short and --porcelain options for commit Samuel Lijin ` (2 preceding siblings ...) 2018-07-15 11:08 ` [PATCH v3 2/3] wt-status: teach wt_status_collect about merges in progress Samuel Lijin @ 2018-07-15 11:08 ` Samuel Lijin 2018-07-17 17:33 ` Junio C Hamano 3 siblings, 1 reply; 26+ messages in thread From: Samuel Lijin @ 2018-07-15 11:08 UTC (permalink / raw) To: git; +Cc: Samuel Lijin In wt-status.c, the s->commitable bit is set only in the call tree of wt_longstatus_print(), which means that when there are changes to be committed or all merge conflicts have been resolved, --dry-run and --long return the correct exit code, but --short and --porcelain do not, even though they both imply --dry-run. Teach wt_status_collect() to set s->committable correctly so that --short and --porcelain return the correct exit code in the above described situations and mark the documenting tests as fixed. Also stop setting s->committable in wt_longstatus_print_updated() and show_merge_in_progress(), and const-ify wt_status_state in the method signatures in those callpaths. Signed-off-by: Samuel Lijin <sxlijin@gmail.com> --- t/t7501-commit.sh | 8 ++--- wt-status.c | 82 +++++++++++++++++++++++++++++------------------ 2 files changed, 55 insertions(+), 35 deletions(-) diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh index be087e73f..b6492322f 100755 --- a/t/t7501-commit.sh +++ b/t/t7501-commit.sh @@ -87,12 +87,12 @@ test_expect_success '--dry-run with stuff to commit returns ok' ' git commit -m next -a --dry-run ' -test_expect_failure '--short with stuff to commit returns ok' ' +test_expect_success '--short with stuff to commit returns ok' ' echo bongo bongo bongo >>file && git commit -m next -a --short ' -test_expect_failure '--porcelain with stuff to commit returns ok' ' +test_expect_success '--porcelain with stuff to commit returns ok' ' echo bongo bongo bongo >>file && git commit -m next -a --porcelain ' @@ -691,11 +691,11 @@ test_expect_success '--dry-run with conflicts fixed from a merge' ' git commit --dry-run ' -test_expect_failure '--short with conflicts fixed from a merge' ' +test_expect_success '--short with conflicts fixed from a merge' ' git commit --short ' -test_expect_failure '--porcelain with conflicts fixed from a merge' ' +test_expect_success '--porcelain with conflicts fixed from a merge' ' git commit --porcelain ' diff --git a/wt-status.c b/wt-status.c index 75d389944..4ba657978 100644 --- a/wt-status.c +++ b/wt-status.c @@ -718,6 +718,39 @@ static void wt_status_collect_untracked(struct wt_status *s) s->untracked_in_ms = (getnanotime() - t_begin) / 1000000; } +static int has_unmerged(const struct wt_status *s) +{ + int i; + + for (i = 0; i < s->change.nr; i++) { + struct wt_status_change_data *d; + d = s->change.items[i].util; + if (d->stagemask) + return 1; + } + return 0; +} + +static void wt_status_mark_committable( + struct wt_status *s, const struct wt_status_state *state) +{ + int i; + + if (state->merge_in_progress && !has_unmerged(s)) { + s->committable = 1; + return; + } + + for (i = 0; i < s->change.nr; i++) { + struct wt_status_change_data *d = (s->change.items[i]).util; + + if (d->index_status && d->index_status != DIFF_STATUS_UNMERGED) { + s->committable = 1; + return; + } + } +} + void wt_status_collect(struct wt_status *s, const struct wt_status_state *state) { wt_status_collect_changes_worktree(s); @@ -728,6 +761,8 @@ void wt_status_collect(struct wt_status *s, const struct wt_status_state *state) wt_status_collect_changes_index(s); wt_status_collect_untracked(s); + + wt_status_mark_committable(s, state); } static void wt_longstatus_print_unmerged(const struct wt_status *s) @@ -753,28 +788,28 @@ static void wt_longstatus_print_unmerged(const struct wt_status *s) } -static void wt_longstatus_print_updated(struct wt_status *s) +static void wt_longstatus_print_updated(const struct wt_status *s) { - int shown_header = 0; int i; + if (!s->committable) { + return; + } + + wt_longstatus_print_cached_header(s); + for (i = 0; i < s->change.nr; i++) { struct wt_status_change_data *d; struct string_list_item *it; it = &(s->change.items[i]); d = it->util; - if (!d->index_status || - d->index_status == DIFF_STATUS_UNMERGED) - continue; - if (!shown_header) { - wt_longstatus_print_cached_header(s); - s->committable = 1; - shown_header = 1; + if (d->index_status && + d->index_status != DIFF_STATUS_UNMERGED) { + wt_longstatus_print_change_data(s, WT_STATUS_UPDATED, it); } - wt_longstatus_print_change_data(s, WT_STATUS_UPDATED, it); } - if (shown_header) - wt_longstatus_print_trailer(s); + + wt_longstatus_print_trailer(s); } /* @@ -1056,21 +1091,7 @@ static void wt_longstatus_print_tracking(const struct wt_status *s) strbuf_release(&sb); } -static int has_unmerged(const struct wt_status *s) -{ - int i; - - for (i = 0; i < s->change.nr; i++) { - struct wt_status_change_data *d; - d = s->change.items[i].util; - if (d->stagemask) - return 1; - } - return 0; -} - -static void show_merge_in_progress(struct wt_status *s, - const struct wt_status_state *state, +static void show_merge_in_progress(const struct wt_status *s, const char *color) { if (has_unmerged(s)) { @@ -1082,7 +1103,6 @@ static void show_merge_in_progress(struct wt_status *s, _(" (use \"git merge --abort\" to abort the merge)")); } } else { - s-> committable = 1; status_printf_ln(s, color, _("All conflicts fixed but you are still merging.")); if (s->hints) @@ -1576,12 +1596,12 @@ void wt_status_clear_state(struct wt_status_state *state) free(state->detached_from); } -static void wt_longstatus_print_state(struct wt_status *s, +static void wt_longstatus_print_state(const struct wt_status *s, const struct wt_status_state *state) { const char *state_color = color(WT_STATUS_HEADER, s); if (state->merge_in_progress) - show_merge_in_progress(s, state, state_color); + show_merge_in_progress(s, state_color); else if (state->am_in_progress) show_am_in_progress(s, state, state_color); else if (state->rebase_in_progress || state->rebase_interactive_in_progress) @@ -1594,7 +1614,7 @@ static void wt_longstatus_print_state(struct wt_status *s, show_bisect_in_progress(s, state, state_color); } -static void wt_longstatus_print(struct wt_status *s, const struct wt_status_state *state) +static void wt_longstatus_print(const struct wt_status *s, const struct wt_status_state *state) { const char *branch_color = color(WT_STATUS_ONBRANCH, s); const char *branch_status_color = color(WT_STATUS_HEADER, s); -- 2.18.0 ^ permalink raw reply related [flat|nested] 26+ messages in thread
* Re: [PATCH v3 3/3] commit: fix exit code for --short/--porcelain 2018-07-15 11:08 ` [PATCH v3 3/3] commit: fix exit code for --short/--porcelain Samuel Lijin @ 2018-07-17 17:33 ` Junio C Hamano 2018-07-19 9:31 ` Samuel Lijin 0 siblings, 1 reply; 26+ messages in thread From: Junio C Hamano @ 2018-07-17 17:33 UTC (permalink / raw) To: Samuel Lijin; +Cc: git Samuel Lijin <sxlijin@gmail.com> writes: > diff --git a/wt-status.c b/wt-status.c > index 75d389944..4ba657978 100644 > --- a/wt-status.c > +++ b/wt-status.c > @@ -718,6 +718,39 @@ static void wt_status_collect_untracked(struct wt_status *s) > s->untracked_in_ms = (getnanotime() - t_begin) / 1000000; > } > > +static int has_unmerged(const struct wt_status *s) > +{ > + int i; > + > + for (i = 0; i < s->change.nr; i++) { > + struct wt_status_change_data *d; > + d = s->change.items[i].util; > + if (d->stagemask) > + return 1; > + } > + return 0; > +} > + > +static void wt_status_mark_committable( > + struct wt_status *s, const struct wt_status_state *state) > +{ > + int i; > + > + if (state->merge_in_progress && !has_unmerged(s)) { > + s->committable = 1; > + return; > + } Is this trying to say: During/after a merge, if there is no higher stage entry in the index, we can commit. I am wondering if we also should say: During/after a merge, if there is any unresolved conflict in the index, we cannot commit. in which case the above becomes more like this: if (state->merge_in_progress) { s->committable = !has_unmerged(s); return; } But with your patch, with no remaining conflict in the index during a merge, the control comes here and goes into the next loop. > + for (i = 0; i < s->change.nr; i++) { > + struct wt_status_change_data *d = (s->change.items[i]).util; > + > + if (d->index_status && d->index_status != DIFF_STATUS_UNMERGED) { > + s->committable = 1; > + return; > + } > + } The loop seems to say "As long as there is one entry in the index that is not in conflict and is different from the HEAD, then we can commit". Is that correct? Imagine there are two paths A and B in the branches involved in a merge, and A cleanly resolves (say, we take their version because our history did not touch it since we diverged) while B has conflict. We'll come to this loop (because we are in a merge but have some unmerged paths) and we find that A is different from HEAD, happily set committable bit and return. I _think_ with the change to "what happens during merge" above that I suggested, this loop automatically becomes correct, but I didn't think it through. If there are ways other than .merge_in_progress that place conflicted entries in the index, then this loop is still incorrect and would want to be more like: for (i = 0; i < s->change.nr; i++) { struct wt_status_change_data *d = (s->change.items[i]).util; if (d->index_status == DIFF_STATUS_UNMERGED) { s->committable = 0; return; } if (d->index_status) s->committable = 1; } i.e. we declare "not ready to commit" if there is *any* conflicted entry, but otherwise set committable to 1 if we see any entry that is different from HEAD (to declare succcess once we successfully loop through to the last entry without seeing any conflict). > void wt_status_collect(struct wt_status *s, const struct wt_status_state *state) > { > wt_status_collect_changes_worktree(s); > @@ -728,6 +761,8 @@ void wt_status_collect(struct wt_status *s, const struct wt_status_state *state) > wt_status_collect_changes_index(s); > > wt_status_collect_untracked(s); > + > + wt_status_mark_committable(s, state); > } > > static void wt_longstatus_print_unmerged(const struct wt_status *s) > @@ -753,28 +788,28 @@ static void wt_longstatus_print_unmerged(const struct wt_status *s) > > } > > -static void wt_longstatus_print_updated(struct wt_status *s) > +static void wt_longstatus_print_updated(const struct wt_status *s) > { > - int shown_header = 0; > int i; > > + if (!s->committable) { > + return; > + } No need to have {} around a single statement. Especially when you know you won't be touching the line (e.g. to later add more statements in the block) in this last patch in a series. > + wt_longstatus_print_cached_header(s); > + ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v3 3/3] commit: fix exit code for --short/--porcelain 2018-07-17 17:33 ` Junio C Hamano @ 2018-07-19 9:31 ` Samuel Lijin 0 siblings, 0 replies; 26+ messages in thread From: Samuel Lijin @ 2018-07-19 9:31 UTC (permalink / raw) To: Junio C Hamano; +Cc: git@vger.kernel.org Thanks for the review. On Tue, Jul 17, 2018 at 10:33 AM Junio C Hamano <gitster@pobox.com> wrote: > > Samuel Lijin <sxlijin@gmail.com> writes: > > > diff --git a/wt-status.c b/wt-status.c > > index 75d389944..4ba657978 100644 > > --- a/wt-status.c > > +++ b/wt-status.c > > @@ -718,6 +718,39 @@ static void wt_status_collect_untracked(struct wt_status *s) > > s->untracked_in_ms = (getnanotime() - t_begin) / 1000000; > > } > > > > +static int has_unmerged(const struct wt_status *s) > > +{ > > + int i; > > + > > + for (i = 0; i < s->change.nr; i++) { > > + struct wt_status_change_data *d; > > + d = s->change.items[i].util; > > + if (d->stagemask) > > + return 1; > > + } > > + return 0; > > +} > > + > > +static void wt_status_mark_committable( > > + struct wt_status *s, const struct wt_status_state *state) > > +{ > > + int i; > > + > > + if (state->merge_in_progress && !has_unmerged(s)) { > > + s->committable = 1; > > + return; > > + } > > Is this trying to say: > > During/after a merge, if there is no higher stage entry in > the index, we can commit. > > I am wondering if we also should say: > > During/after a merge, if there is any unresolved conflict in > the index, we cannot commit. > > in which case the above becomes more like this: > > if (state->merge_in_progress) { > s->committable = !has_unmerged(s); > return; > } > > But with your patch, with no remaining conflict in the index during > a merge, the control comes here and goes into the next loop. > > > + for (i = 0; i < s->change.nr; i++) { > > + struct wt_status_change_data *d = (s->change.items[i]).util; > > + > > + if (d->index_status && d->index_status != DIFF_STATUS_UNMERGED) { > > + s->committable = 1; > > + return; > > + } > > + } > > The loop seems to say "As long as there is one entry in the index > that is not in conflict and is different from the HEAD, then we can > commit". Is that correct? > > Imagine there are two paths A and B in the branches involved in a > merge, and A cleanly resolves (say, we take their version because > our history did not touch it since we diverged) while B has > conflict. We'll come to this loop (because we are in a merge but > have some unmerged paths) and we find that A is different from HEAD, > happily set committable bit and return. I'll be honest: when I wrote this, I didn't think too much about what the code was actually doing, semantically speaking: I was assuming that the behavior that set the commitable bit in the call tree of wt_longstatus_print() was correct, and that it was just a matter of mechanically copying that logic over to the --short/--porcelain call paths. Looking into this more deeply, I think you're right, but more problematically, this is technically a bug with the current Git code that seems to be cancelled out by another bug: wt_status_state apparently does not correctly reflect the state of the index when it reaches wt_longstatus_print_updated(). Working from master (f55ff46c9), I modified the last test in t7501 to look like this: →.echo "Initial contents, unimportant" | tee test-file1 test-file2 && →.git add test-file1 test-file2 && →.git commit -m "Initial commit" && →.echo "commit-1-state" | tee test-file1 test-file2 && →.git commit -m "commit 1" -i test-file1 test-file2 && →.git tag commit-1 && →.git checkout -b branch-2 HEAD^1 && →.echo "commit-2-state" | tee test-file1 test-file2 && →.git commit -m "commit 2" -i test-file1 test-file2 && →.! $(git merge --no-commit commit-1) && →.echo "commit-2-state" | tee test-file1 && →.git add test-file1 && →.git commit --dry-run && →.git commit -m "conflicts fixed from merge." And once inside gdb did this: (gdb) b wt-status.c:766 Breakpoint 1 at 0x205d73: file wt-status.c, line 766. (gdb) r Starting program: /home/pockets/git/git commit --dry-run [Thread debugging using libthread_db enabled] Using host libthread_db library "/usr/lib/libthread_db.so.1". On branch branch-2 You have unmerged paths. (fix conflicts and run "git commit") (use "git merge --abort" to abort the merge) Breakpoint 1, wt_longstatus_print_updated (s=0x555555a29960 <s>) at wt-status.c:766 warning: Source file is more recent than executable. 760 for (i = 0; i < s->change.nr; i++) { (gdb) print s->change.nr $1 = 1 Can you confirm I'm not crazy, and am analyzing this correctly? > I _think_ with the change to "what happens during merge" above that > I suggested, this loop automatically becomes correct, but I didn't > think it through. If there are ways other than .merge_in_progress > that place conflicted entries in the index, then this loop is still > incorrect and would want to be more like: > > for (i = 0; i < s->change.nr; i++) { > struct wt_status_change_data *d = (s->change.items[i]).util; > > if (d->index_status == DIFF_STATUS_UNMERGED) { > s->committable = 0; > return; > } > if (d->index_status) > s->committable = 1; > } > > i.e. we declare "not ready to commit" if there is *any* conflicted > entry, but otherwise set committable to 1 if we see any entry that > is different from HEAD (to declare succcess once we successfully > loop through to the last entry without seeing any conflict). > > > void wt_status_collect(struct wt_status *s, const struct wt_status_state *state) > > { > > wt_status_collect_changes_worktree(s); > > @@ -728,6 +761,8 @@ void wt_status_collect(struct wt_status *s, const struct wt_status_state *state) > > wt_status_collect_changes_index(s); > > > > wt_status_collect_untracked(s); > > + > > + wt_status_mark_committable(s, state); > > } > > > > static void wt_longstatus_print_unmerged(const struct wt_status *s) > > @@ -753,28 +788,28 @@ static void wt_longstatus_print_unmerged(const struct wt_status *s) > > > > } > > > > -static void wt_longstatus_print_updated(struct wt_status *s) > > +static void wt_longstatus_print_updated(const struct wt_status *s) > > { > > - int shown_header = 0; > > int i; > > > > + if (!s->committable) { > > + return; > > + } > > No need to have {} around a single statement. Especially when you > know you won't be touching the line (e.g. to later add more > statements in the block) in this last patch in a series. > > > + wt_longstatus_print_cached_header(s); > > + ^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH v2 1/2] commit: fix --short and --porcelain options 2018-04-18 3:06 [PATCH 0/2] Fix --short and --porcelain options for commit Samuel Lijin ` (2 preceding siblings ...) 2018-04-26 9:25 ` [PATCH v2 0/2] Fix --short and --porcelain options for commit Samuel Lijin @ 2018-04-26 9:25 ` Samuel Lijin 2018-05-02 5:50 ` Junio C Hamano 2018-04-26 9:25 ` [PATCH v2 2/2] wt-status: const-ify all printf helper methods Samuel Lijin 4 siblings, 1 reply; 26+ messages in thread From: Samuel Lijin @ 2018-04-26 9:25 UTC (permalink / raw) To: git; +Cc: Samuel Lijin Mark the commitable flag in the wt_status object in the call to `wt_status_collect()`, instead of in `wt_longstatus_print_updated()`, and simplify the logic in the latter function to take advantage of the logic shifted to the former. This means that callers do not need to use `wt_longstatus_print_updated()` to collect the `commitable` flag; calling `wt_status_collect()` is sufficient. As a result, invoking `git commit` with `--short` or `--porcelain` (which imply `--dry-run`, but previously returned an inconsistent error code inconsistent with dry run behavior) correctly returns status code zero when there is something to commit. This fixes two bugs documented in the test suite. Signed-off-by: Samuel Lijin <sxlijin@gmail.com> --- t/t7501-commit.sh | 4 ++-- wt-status.c | 38 +++++++++++++++++++++++++++----------- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh index fa61b1a4e..85a8217fd 100755 --- a/t/t7501-commit.sh +++ b/t/t7501-commit.sh @@ -87,12 +87,12 @@ test_expect_success '--dry-run with stuff to commit returns ok' ' git commit -m next -a --dry-run ' -test_expect_failure '--short with stuff to commit returns ok' ' +test_expect_success '--short with stuff to commit returns ok' ' echo bongo bongo bongo >>file && git commit -m next -a --short ' -test_expect_failure '--porcelain with stuff to commit returns ok' ' +test_expect_success '--porcelain with stuff to commit returns ok' ' echo bongo bongo bongo >>file && git commit -m next -a --porcelain ' diff --git a/wt-status.c b/wt-status.c index 50815e5fa..2e5452731 100644 --- a/wt-status.c +++ b/wt-status.c @@ -718,6 +718,19 @@ static void wt_status_collect_untracked(struct wt_status *s) s->untracked_in_ms = (getnanotime() - t_begin) / 1000000; } +static void wt_status_mark_commitable(struct wt_status *s) { + int i; + + for (i = 0; i < s->change.nr; i++) { + struct wt_status_change_data *d = (s->change.items[i]).util; + + if (d->index_status && d->index_status != DIFF_STATUS_UNMERGED) { + s->commitable = 1; + return; + } + } +} + void wt_status_collect(struct wt_status *s) { wt_status_collect_changes_worktree(s); @@ -726,7 +739,10 @@ void wt_status_collect(struct wt_status *s) wt_status_collect_changes_initial(s); else wt_status_collect_changes_index(s); + wt_status_collect_untracked(s); + + wt_status_mark_commitable(s); } static void wt_longstatus_print_unmerged(struct wt_status *s) @@ -754,26 +770,26 @@ static void wt_longstatus_print_unmerged(struct wt_status *s) static void wt_longstatus_print_updated(struct wt_status *s) { - int shown_header = 0; int i; + if (!s->commitable) { + return; + } + + wt_longstatus_print_cached_header(s); + for (i = 0; i < s->change.nr; i++) { struct wt_status_change_data *d; struct string_list_item *it; it = &(s->change.items[i]); d = it->util; - if (!d->index_status || - d->index_status == DIFF_STATUS_UNMERGED) - continue; - if (!shown_header) { - wt_longstatus_print_cached_header(s); - s->commitable = 1; - shown_header = 1; + if (d->index_status && + d->index_status != DIFF_STATUS_UNMERGED) { + wt_longstatus_print_change_data(s, WT_STATUS_UPDATED, it); } - wt_longstatus_print_change_data(s, WT_STATUS_UPDATED, it); } - if (shown_header) - wt_longstatus_print_trailer(s); + + wt_longstatus_print_trailer(s); } /* -- 2.17.0 ^ permalink raw reply related [flat|nested] 26+ messages in thread
* Re: [PATCH v2 1/2] commit: fix --short and --porcelain options 2018-04-26 9:25 ` [PATCH v2 1/2] commit: fix --short and --porcelain options Samuel Lijin @ 2018-05-02 5:50 ` Junio C Hamano 2018-05-02 15:52 ` Samuel Lijin 0 siblings, 1 reply; 26+ messages in thread From: Junio C Hamano @ 2018-05-02 5:50 UTC (permalink / raw) To: Samuel Lijin; +Cc: git Samuel Lijin <sxlijin@gmail.com> writes: > Mark the commitable flag in the wt_status object in the call to > `wt_status_collect()`, instead of in `wt_longstatus_print_updated()`, > and simplify the logic in the latter function to take advantage of the > logic shifted to the former. This means that callers do not need to use > `wt_longstatus_print_updated()` to collect the `commitable` flag; > calling `wt_status_collect()` is sufficient. > > As a result, invoking `git commit` with `--short` or `--porcelain` > (which imply `--dry-run`, but previously returned an inconsistent error > code inconsistent with dry run behavior) correctly returns status code > zero when there is something to commit. This fixes two bugs documented > in the test suite. Hmm, I couldn't quite get what the above two paragraphs were trying to say, but I think I figured out by looking at wt_status.c before applying this patch, so let me see if I correctly understand what this patch is about by thinking a bit aloud. There are only two assignments to s->commitable in wt-status.c; one happens in wt_longstatus_print_updated(), when the function notices there is even one record to be shown (i.e. there is an "updated" path) and the other in show_merge_in_progress() which is called by wt_longstatus_prpint_state(). The latter codepath happens when we are in a merge and there is no remaining conflicted paths (the code allows the contents to be committed to be identical to HEAD). Both are called from wt_longstatus_print(), which in turn is called by wt_status_print(). The implication of the above observation is that we do not set commitable bit (by the way, shouldn't we spell it with two 'T's?) if we are not doing the long format status. The title hints at it but "fix" is too vague. It would be easier to understand if it began like this (i.e. state problem clearly first, before outlining the solution): [PATCH 1/2] commit: fix exit status under --short/--porcelain options In wt-status.c, s->commitable bit is set only in the codepaths reachable from wt_status_print() when output format is STATUS_FORMAT_LONG as a side effect of printing bits of status. Consequently, when running with --short and --porcelain options, the bit is not set to reflect if there is anything to be committed, and "git commit --short" or "--porcelain" (both of which imply "--dry-run") failed to signal if there is anything to commit with its exit status. Instead, update s->commitable bit in wt_status_collect(), regardless of the output format. ... Is that what is going on here? Yours made it sound as if moving the code to _collect() was done for the sake of moving code around and simplifying the logic, and bugfix fell out of the move merely as a side effect, which probably was the source of my confusion. > +static void wt_status_mark_commitable(struct wt_status *s) { > + int i; > + > + for (i = 0; i < s->change.nr; i++) { > + struct wt_status_change_data *d = (s->change.items[i]).util; > + > + if (d->index_status && d->index_status != DIFF_STATUS_UNMERGED) { > + s->commitable = 1; > + return; > + } > + } > +} I am not sure if this is sufficient. From a cursory look of the existing code (and vague recollection in my ageing brain ;-), I think we say it is committable if (1) when not merging, there is something to show in the "to be committed" section (i.e. there must be something changed since HEAD in the index). (2) when merging, no conflicting paths remain (i.e. change.nr being zero is fine). So it is unclear to me how you are dealing with (2) under "--short" option, which does not call show_merge_in_progress() to catch that case. ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v2 1/2] commit: fix --short and --porcelain options 2018-05-02 5:50 ` Junio C Hamano @ 2018-05-02 15:52 ` Samuel Lijin 0 siblings, 0 replies; 26+ messages in thread From: Samuel Lijin @ 2018-05-02 15:52 UTC (permalink / raw) To: Junio C Hamano; +Cc: git@vger.kernel.org On Tue, May 1, 2018 at 10:50 PM Junio C Hamano <gitster@pobox.com> wrote: > Samuel Lijin <sxlijin@gmail.com> writes: > > Mark the commitable flag in the wt_status object in the call to > > `wt_status_collect()`, instead of in `wt_longstatus_print_updated()`, > > and simplify the logic in the latter function to take advantage of the > > logic shifted to the former. This means that callers do not need to use > > `wt_longstatus_print_updated()` to collect the `commitable` flag; > > calling `wt_status_collect()` is sufficient. > > > > As a result, invoking `git commit` with `--short` or `--porcelain` > > (which imply `--dry-run`, but previously returned an inconsistent error > > code inconsistent with dry run behavior) correctly returns status code > > zero when there is something to commit. This fixes two bugs documented > > in the test suite. > Hmm, I couldn't quite get what the above two paragraphs were trying > to say, but I think I figured out by looking at wt_status.c before > applying this patch, so let me see if I correctly understand what > this patch is about by thinking a bit aloud. > There are only two assignments to s->commitable in wt-status.c; one > happens in wt_longstatus_print_updated(), when the function notices > there is even one record to be shown (i.e. there is an "updated" > path) and the other in show_merge_in_progress() which is called by > wt_longstatus_prpint_state(). The latter codepath happens when we > are in a merge and there is no remaining conflicted paths (the code > allows the contents to be committed to be identical to HEAD). Both > are called from wt_longstatus_print(), which in turn is called by > wt_status_print(). > The implication of the above observation is that we do not set > commitable bit (by the way, shouldn't we spell it with two 'T's?) Yep, MW confirms: https://www.merriam-webster.com/dictionary/commitable I didn't think to check how common "commitable" is in the codebase, but it doesn't seem to be too many, looking at the output of `git grep commitable`, so I'll add that to the patch series when I reroll. > if we are not doing the long format status. The title hints at it > but "fix" is too vague. It would be easier to understand if it > began like this (i.e. state problem clearly first, before outlining > the solution): > [PATCH 1/2] commit: fix exit status under --short/--porcelain options > In wt-status.c, s->commitable bit is set only in the > codepaths reachable from wt_status_print() when output > format is STATUS_FORMAT_LONG as a side effect of printing > bits of status. Consequently, when running with --short and > --porcelain options, the bit is not set to reflect if there > is anything to be committed, and "git commit --short" or > "--porcelain" (both of which imply "--dry-run") failed to > signal if there is anything to commit with its exit status. > Instead, update s->commitable bit in wt_status_collect(), > regardless of the output format. ... > Is that what is going on here? Yours made it sound as if moving the > code to _collect() was done for the sake of moving code around and > simplifying the logic, and bugfix fell out of the move merely as a > side effect, which probably was the source of my confusion. Yep, that's right. I wasn't sure if the imperative tone was required for the whole commit or just the description, hence the awkward structure. (I also wasn't sure how strict the 70 char limit on the description was.) > > +static void wt_status_mark_commitable(struct wt_status *s) { > > + int i; > > + > > + for (i = 0; i < s->change.nr; i++) { > > + struct wt_status_change_data *d = (s->change.items[i]).util; > > + > > + if (d->index_status && d->index_status != DIFF_STATUS_UNMERGED) { > > + s->commitable = 1; > > + return; > > + } > > + } > > +} > I am not sure if this is sufficient. From a cursory look of the > existing code (and vague recollection in my ageing brain ;-), I > think we say it is committable if > (1) when not merging, there is something to show in the "to be > committed" section (i.e. there must be something changed since > HEAD in the index). > (2) when merging, no conflicting paths remain (i.e. change.nr being > zero is fine). > So it is unclear to me how you are dealing with (2) under "--short" > option, which does not call show_merge_in_progress() to catch that > case. And the answer there is that I'm not :) I had hoped that there was a test to catch mistakes like this but evidently not. Thanks for pointing that out, I'll add a test to catch that. I'm also realizing that I didn't const-ify wt_longstatus_print() in the next patch, which is another reason I didn't catch this. This seems a bit trickier to handle. What do you think of this approach: (1) move wt_status_state into wt_status and (2) move the call to wt_status_get_state from wt_longstatus_print/wt_porcelain_v2_print_tracking to wt_status_collect? (I kind of also want to suggest enum-ifying some of wt_status_state, but that feels beyond the scope of this and also prone to other pitfalls.) ^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH v2 2/2] wt-status: const-ify all printf helper methods 2018-04-18 3:06 [PATCH 0/2] Fix --short and --porcelain options for commit Samuel Lijin ` (3 preceding siblings ...) 2018-04-26 9:25 ` [PATCH v2 1/2] commit: fix --short and --porcelain options Samuel Lijin @ 2018-04-26 9:25 ` Samuel Lijin 4 siblings, 0 replies; 26+ messages in thread From: Samuel Lijin @ 2018-04-26 9:25 UTC (permalink / raw) To: git; +Cc: Samuel Lijin Change the method signatures of all printf helper methods to take a `const struct wt_status *` rather than a `struct wt_status *`. Signed-off-by: Samuel Lijin <sxlijin@gmail.com> --- wt-status.c | 18 +++++++++--------- wt-status.h | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/wt-status.c b/wt-status.c index 2e5452731..4360bbfd7 100644 --- a/wt-status.c +++ b/wt-status.c @@ -33,7 +33,7 @@ static char default_wt_status_colors[][COLOR_MAXLEN] = { GIT_COLOR_NIL, /* WT_STATUS_ONBRANCH */ }; -static const char *color(int slot, struct wt_status *s) +static const char *color(int slot, const struct wt_status *s) { const char *c = ""; if (want_color(s->use_color)) @@ -43,7 +43,7 @@ static const char *color(int slot, struct wt_status *s) return c; } -static void status_vprintf(struct wt_status *s, int at_bol, const char *color, +static void status_vprintf(const struct wt_status *s, int at_bol, const char *color, const char *fmt, va_list ap, const char *trail) { struct strbuf sb = STRBUF_INIT; @@ -89,7 +89,7 @@ static void status_vprintf(struct wt_status *s, int at_bol, const char *color, strbuf_release(&sb); } -void status_printf_ln(struct wt_status *s, const char *color, +void status_printf_ln(const struct wt_status *s, const char *color, const char *fmt, ...) { va_list ap; @@ -99,7 +99,7 @@ void status_printf_ln(struct wt_status *s, const char *color, va_end(ap); } -void status_printf(struct wt_status *s, const char *color, +void status_printf(const struct wt_status *s, const char *color, const char *fmt, ...) { va_list ap; @@ -109,7 +109,7 @@ void status_printf(struct wt_status *s, const char *color, va_end(ap); } -static void status_printf_more(struct wt_status *s, const char *color, +static void status_printf_more(const struct wt_status *s, const char *color, const char *fmt, ...) { va_list ap; @@ -192,7 +192,7 @@ static void wt_longstatus_print_unmerged_header(struct wt_status *s) status_printf_ln(s, c, "%s", ""); } -static void wt_longstatus_print_cached_header(struct wt_status *s) +static void wt_longstatus_print_cached_header(const struct wt_status *s) { const char *c = color(WT_STATUS_HEADER, s); @@ -239,7 +239,7 @@ static void wt_longstatus_print_other_header(struct wt_status *s, status_printf_ln(s, c, "%s", ""); } -static void wt_longstatus_print_trailer(struct wt_status *s) +static void wt_longstatus_print_trailer(const struct wt_status *s) { status_printf_ln(s, color(WT_STATUS_HEADER, s), "%s", ""); } @@ -332,7 +332,7 @@ static void wt_longstatus_print_unmerged_data(struct wt_status *s, strbuf_release(&onebuf); } -static void wt_longstatus_print_change_data(struct wt_status *s, +static void wt_longstatus_print_change_data(const struct wt_status *s, int change_type, struct string_list_item *it) { @@ -768,7 +768,7 @@ static void wt_longstatus_print_unmerged(struct wt_status *s) } -static void wt_longstatus_print_updated(struct wt_status *s) +static void wt_longstatus_print_updated(const struct wt_status *s) { int i; diff --git a/wt-status.h b/wt-status.h index 430770b85..83a1f7c00 100644 --- a/wt-status.h +++ b/wt-status.h @@ -135,9 +135,9 @@ int wt_status_check_bisect(const struct worktree *wt, struct wt_status_state *state); __attribute__((format (printf, 3, 4))) -void status_printf_ln(struct wt_status *s, const char *color, const char *fmt, ...); +void status_printf_ln(const struct wt_status *s, const char *color, const char *fmt, ...); __attribute__((format (printf, 3, 4))) -void status_printf(struct wt_status *s, const char *color, const char *fmt, ...); +void status_printf(const struct wt_status *s, const char *color, const char *fmt, ...); /* The following functions expect that the caller took care of reading the index. */ int has_unstaged_changes(int ignore_submodules); -- 2.17.0 ^ permalink raw reply related [flat|nested] 26+ messages in thread
end of thread, other threads:[~2018-07-30 22:15 UTC | newest] Thread overview: 26+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2018-04-18 3:06 [PATCH 0/2] Fix --short and --porcelain options for commit Samuel Lijin 2018-04-18 3:06 ` [PATCH 1/2] commit: fix --short and --porcelain Samuel Lijin 2018-04-18 18:38 ` Martin Ågren [not found] ` <CAJZjrdW3X8eaSit85otKV2HvHmu0NDGcnnnrtxHME03q=eWW-Q@mail.gmail.com> 2018-04-19 3:55 ` Samuel Lijin 2018-04-20 7:08 ` Eric Sunshine 2018-04-18 3:06 ` [PATCH 2/2] wt-status: const-ify all printf helper methods Samuel Lijin 2018-04-26 9:25 ` [PATCH v2 0/2] Fix --short and --porcelain options for commit Samuel Lijin 2018-07-15 11:08 ` [PATCH v3 0/3] Fix --short/--porcelain options for git commit Samuel Lijin 2018-07-23 2:08 ` [PATCH v4 0/4] Rerolling patch series to fix t7501 Samuel Lijin 2018-07-30 22:15 ` Junio C Hamano 2018-07-23 2:09 ` [PATCH v4 1/4] t7501: add coverage for flags which imply dry runs Samuel Lijin 2018-07-23 2:09 ` [PATCH v4 2/4] wt-status: rename commitable to committable Samuel Lijin 2018-07-23 2:09 ` [PATCH v4 3/4] wt-status: teach wt_status_collect about merges in progress Samuel Lijin 2018-07-23 2:09 ` [PATCH v4 4/4] commit: fix exit code when doing a dry run Samuel Lijin 2018-07-15 11:08 ` [PATCH v3 1/3] t7501: add merge conflict tests for " Samuel Lijin 2018-07-17 17:05 ` Junio C Hamano 2018-07-17 17:45 ` Junio C Hamano 2018-07-15 11:08 ` [PATCH v3 2/3] wt-status: teach wt_status_collect about merges in progress Samuel Lijin 2018-07-17 17:15 ` Junio C Hamano 2018-07-15 11:08 ` [PATCH v3 3/3] commit: fix exit code for --short/--porcelain Samuel Lijin 2018-07-17 17:33 ` Junio C Hamano 2018-07-19 9:31 ` Samuel Lijin 2018-04-26 9:25 ` [PATCH v2 1/2] commit: fix --short and --porcelain options Samuel Lijin 2018-05-02 5:50 ` Junio C Hamano 2018-05-02 15:52 ` Samuel Lijin 2018-04-26 9:25 ` [PATCH v2 2/2] wt-status: const-ify all printf helper methods Samuel Lijin
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).