From 957a0fe2e59343f10f10620fab787f7e5abf3ab8 Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Fri, 5 Aug 2016 18:00:26 -0400 Subject: status: rename long-format print routines Rename the various wt_status_print*() routines to be wt_longstatus_print*() to make it clear that these routines are only concerned with the normal/long status output and reduce developer confusion as other status formats are added in the future. Signed-off-by: Jeff Hostetler Signed-off-by: Junio C Hamano --- builtin/commit.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'builtin/commit.c') diff --git a/builtin/commit.c b/builtin/commit.c index 1f6dbcd0d0..b80273b539 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -515,7 +515,7 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int break; case STATUS_FORMAT_NONE: case STATUS_FORMAT_LONG: - wt_status_print(s); + wt_longstatus_print(s); break; } @@ -1403,7 +1403,7 @@ int cmd_status(int argc, const char **argv, const char *prefix) case STATUS_FORMAT_LONG: s.verbose = verbose; s.ignore_submodule_arg = ignore_submodule_arg; - wt_status_print(&s); + wt_longstatus_print(&s); break; } return 0; -- cgit v1.2.3 From be7e795efe35d3e50199af5452b218e5121b1713 Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Fri, 5 Aug 2016 18:00:27 -0400 Subject: status: cleanup API to wt_status_print Refactor the API between builtin/commit.c and wt-status.[ch]. Hide the details of the various wt_*status_print() routines inside wt-status.c behind a single (new) wt_status_print() routine. Eliminate the switch statements from builtin/commit.c. Allow details of new status formats to be isolated within wt-status.c Signed-off-by: Jeff Hostetler Signed-off-by: Junio C Hamano --- builtin/commit.c | 51 +++++++++------------------------------------------ wt-status.c | 25 ++++++++++++++++++++++--- wt-status.h | 16 ++++++++++++---- 3 files changed, 43 insertions(+), 49 deletions(-) (limited to 'builtin/commit.c') diff --git a/builtin/commit.c b/builtin/commit.c index b80273b539..a792deb9bd 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -142,14 +142,7 @@ static int show_ignored_in_status, have_option_m; static const char *only_include_assumed; static struct strbuf message = STRBUF_INIT; -static enum status_format { - STATUS_FORMAT_NONE = 0, - STATUS_FORMAT_LONG, - STATUS_FORMAT_SHORT, - STATUS_FORMAT_PORCELAIN, - - STATUS_FORMAT_UNSPECIFIED -} status_format = STATUS_FORMAT_UNSPECIFIED; +static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED; static int opt_parse_m(const struct option *opt, const char *arg, int unset) { @@ -500,24 +493,11 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int s->fp = fp; s->nowarn = nowarn; s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0; + s->status_format = status_format; + s->ignore_submodule_arg = ignore_submodule_arg; wt_status_collect(s); - - switch (status_format) { - case STATUS_FORMAT_SHORT: - wt_shortstatus_print(s); - break; - case STATUS_FORMAT_PORCELAIN: - wt_porcelain_print(s); - 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); - break; - } + wt_status_print(s); return s->commitable; } @@ -1099,7 +1079,7 @@ static const char *read_commit_message(const char *name) * is not in effect here. */ static struct status_deferred_config { - enum status_format status_format; + enum wt_status_format status_format; int show_branch; } status_deferred_config = { STATUS_FORMAT_UNSPECIFIED, @@ -1381,6 +1361,9 @@ int cmd_status(int argc, const char **argv, const char *prefix) s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0; s.ignore_submodule_arg = ignore_submodule_arg; + s.status_format = status_format; + s.verbose = verbose; + wt_status_collect(&s); if (0 <= fd) @@ -1389,23 +1372,7 @@ int cmd_status(int argc, const char **argv, const char *prefix) if (s.relative_paths) s.prefix = prefix; - switch (status_format) { - case STATUS_FORMAT_SHORT: - wt_shortstatus_print(&s); - break; - case STATUS_FORMAT_PORCELAIN: - wt_porcelain_print(&s); - break; - case STATUS_FORMAT_UNSPECIFIED: - die("BUG: finalize_deferred_config() should have been called"); - break; - case STATUS_FORMAT_NONE: - case STATUS_FORMAT_LONG: - s.verbose = verbose; - s.ignore_submodule_arg = ignore_submodule_arg; - wt_longstatus_print(&s); - break; - } + wt_status_print(&s); return 0; } diff --git a/wt-status.c b/wt-status.c index 961bbc7a21..e0dda24ae6 100644 --- a/wt-status.c +++ b/wt-status.c @@ -1447,7 +1447,7 @@ static void wt_longstatus_print_state(struct wt_status *s, show_bisect_in_progress(s, state, state_color); } -void wt_longstatus_print(struct wt_status *s) +static void wt_longstatus_print(struct wt_status *s) { const char *branch_color = color(WT_STATUS_ONBRANCH, s); const char *branch_status_color = color(WT_STATUS_HEADER, s); @@ -1714,7 +1714,7 @@ static void wt_shortstatus_print_tracking(struct wt_status *s) fputc(s->null_termination ? '\0' : '\n', s->fp); } -void wt_shortstatus_print(struct wt_status *s) +static void wt_shortstatus_print(struct wt_status *s) { int i; @@ -1746,7 +1746,7 @@ void wt_shortstatus_print(struct wt_status *s) } } -void wt_porcelain_print(struct wt_status *s) +static void wt_porcelain_print(struct wt_status *s) { s->use_color = 0; s->relative_paths = 0; @@ -1754,3 +1754,22 @@ void wt_porcelain_print(struct wt_status *s) s->no_gettext = 1; wt_shortstatus_print(s); } + +void wt_status_print(struct wt_status *s) +{ + switch (s->status_format) { + case STATUS_FORMAT_SHORT: + wt_shortstatus_print(s); + break; + case STATUS_FORMAT_PORCELAIN: + wt_porcelain_print(s); + 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); + break; + } +} diff --git a/wt-status.h b/wt-status.h index 2023a3cef6..9389076f69 100644 --- a/wt-status.h +++ b/wt-status.h @@ -43,6 +43,15 @@ struct wt_status_change_data { unsigned new_submodule_commits : 1; }; +enum wt_status_format { + STATUS_FORMAT_NONE = 0, + STATUS_FORMAT_LONG, + STATUS_FORMAT_SHORT, + STATUS_FORMAT_PORCELAIN, + + STATUS_FORMAT_UNSPECIFIED +}; + struct wt_status { int is_initial; char *branch; @@ -66,6 +75,8 @@ struct wt_status { int show_branch; int hints; + enum wt_status_format status_format; + /* These are computed during processing of the individual sections */ int commitable; int workdir_dirty; @@ -99,6 +110,7 @@ struct wt_status_state { void wt_status_truncate_message_at_cut_line(struct strbuf *); 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); int wt_status_check_rebase(const struct worktree *wt, @@ -106,10 +118,6 @@ int wt_status_check_rebase(const struct worktree *wt, int wt_status_check_bisect(const struct worktree *wt, struct wt_status_state *state); -void wt_longstatus_print(struct wt_status *s); -void wt_shortstatus_print(struct wt_status *s); -void wt_porcelain_print(struct wt_status *s); - __attribute__((format (printf, 3, 4))) void status_printf_ln(struct wt_status *s, const char *color, const char *fmt, ...); __attribute__((format (printf, 3, 4))) -- cgit v1.2.3 From c4f596b98ed305ceb594857fa34524fedc30a031 Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Fri, 5 Aug 2016 18:00:28 -0400 Subject: status: support --porcelain[=] Update --porcelain argument to take optional version parameter to allow multiple porcelain formats to be supported in the future. The token "v1" is the default value and indicates the traditional porcelain format. (The token "1" is an alias for that.) Signed-off-by: Jeff Hostetler Signed-off-by: Junio C Hamano --- Documentation/git-status.txt | 7 +++++-- builtin/commit.c | 21 ++++++++++++++++++--- t/t7060-wtstatus.sh | 21 +++++++++++++++++++++ 3 files changed, 44 insertions(+), 5 deletions(-) (limited to 'builtin/commit.c') diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt index e1e8f57cdd..6b1454bd42 100644 --- a/Documentation/git-status.txt +++ b/Documentation/git-status.txt @@ -32,11 +32,14 @@ OPTIONS --branch:: Show the branch and tracking info even in short-format. ---porcelain:: +--porcelain[=]:: Give the output in an easy-to-parse format for scripts. This is similar to the short output, but will remain stable across Git versions and regardless of user configuration. See below for details. ++ +The version parameter is used to specify the format version. +This is optional and defaults to the original version 'v1' format. --long:: Give the output in the long-format. This is the default. @@ -96,7 +99,7 @@ configuration variable documented in linkgit:git-config[1]. -z:: Terminate entries with NUL, instead of LF. This implies - the `--porcelain` output format if no other format is given. + the `--porcelain=v1` output format if no other format is given. --column[=]:: --no-column:: diff --git a/builtin/commit.c b/builtin/commit.c index a792deb9bd..185ac35d02 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -144,6 +144,21 @@ static struct strbuf message = STRBUF_INIT; static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED; +static int opt_parse_porcelain(const struct option *opt, const char *arg, int unset) +{ + enum wt_status_format *value = (enum wt_status_format *)opt->value; + if (unset) + *value = STATUS_FORMAT_NONE; + else if (!arg) + *value = STATUS_FORMAT_PORCELAIN; + else if (!strcmp(arg, "v1") || !strcmp(arg, "1")) + *value = STATUS_FORMAT_PORCELAIN; + else + die("unsupported porcelain version '%s'", arg); + + return 0; +} + static int opt_parse_m(const struct option *opt, const char *arg, int unset) { struct strbuf *buf = opt->value; @@ -1316,9 +1331,9 @@ int cmd_status(int argc, const char **argv, const char *prefix) N_("show status concisely"), STATUS_FORMAT_SHORT), OPT_BOOL('b', "branch", &s.show_branch, N_("show branch information")), - OPT_SET_INT(0, "porcelain", &status_format, - N_("machine-readable output"), - STATUS_FORMAT_PORCELAIN), + { OPTION_CALLBACK, 0, "porcelain", &status_format, + N_("version"), N_("machine-readable output"), + PARSE_OPT_OPTARG, opt_parse_porcelain }, OPT_SET_INT(0, "long", &status_format, N_("show status in long format (default)"), STATUS_FORMAT_LONG), diff --git a/t/t7060-wtstatus.sh b/t/t7060-wtstatus.sh index 44bf1d84af..00e0cebff3 100755 --- a/t/t7060-wtstatus.sh +++ b/t/t7060-wtstatus.sh @@ -228,4 +228,25 @@ test_expect_success 'status --branch with detached HEAD' ' test_i18ncmp expected actual ' +## Duplicate the above test and verify --porcelain=v1 arg parsing. +test_expect_success 'status --porcelain=v1 --branch with detached HEAD' ' + git reset --hard && + git checkout master^0 && + git status --branch --porcelain=v1 >actual && + cat >expected <<-EOF && + ## HEAD (no branch) + ?? .gitconfig + ?? actual + ?? expect + ?? expected + ?? mdconflict/ + EOF + test_i18ncmp expected actual +' + +## Verify parser error on invalid --porcelain argument. +test_expect_success 'status --porcelain=bogus' ' + test_must_fail git status --porcelain=bogus +' + test_done -- cgit v1.2.3 From 1ecdecce621009a4d039d061d514056501d0ed8f Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Thu, 11 Aug 2016 10:45:57 -0400 Subject: status: collect per-file data for --porcelain=v2 Collect extra per-file data for porcelain V2 format. The output of `git status --porcelain` leaves out many details about the current status that clients might like to have. This can force them to be less efficient as they may need to launch secondary commands (and try to match the logic within git) to accumulate this extra information. For example, a GUI IDE might want the file mode to display the correct icon for a changed item (without having to stat it afterwards). Signed-off-by: Jeff Hostetler Signed-off-by: Junio C Hamano --- builtin/commit.c | 3 +++ wt-status.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- wt-status.h | 4 ++++ 3 files changed, 69 insertions(+), 2 deletions(-) (limited to 'builtin/commit.c') diff --git a/builtin/commit.c b/builtin/commit.c index 185ac35d02..3d222d3970 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -153,6 +153,8 @@ static int opt_parse_porcelain(const struct option *opt, const char *arg, int un *value = STATUS_FORMAT_PORCELAIN; else if (!strcmp(arg, "v1") || !strcmp(arg, "1")) *value = STATUS_FORMAT_PORCELAIN; + else if (!strcmp(arg, "v2") || !strcmp(arg, "2")) + *value = STATUS_FORMAT_PORCELAIN_V2; else die("unsupported porcelain version '%s'", arg); @@ -1104,6 +1106,7 @@ static struct status_deferred_config { static void finalize_deferred_config(struct wt_status *s) { int use_deferred_config = (status_format != STATUS_FORMAT_PORCELAIN && + status_format != STATUS_FORMAT_PORCELAIN_V2 && !s->null_termination); if (s->null_termination) { diff --git a/wt-status.c b/wt-status.c index e0dda24ae6..dd0a1ab264 100644 --- a/wt-status.c +++ b/wt-status.c @@ -434,6 +434,31 @@ static void wt_status_collect_changed_cb(struct diff_queue_struct *q, if (S_ISGITLINK(p->two->mode)) d->new_submodule_commits = !!oidcmp(&p->one->oid, &p->two->oid); + + switch (p->status) { + case DIFF_STATUS_ADDED: + die("BUG: worktree status add???"); + break; + + case DIFF_STATUS_DELETED: + d->mode_index = p->one->mode; + oidcpy(&d->oid_index, &p->one->oid); + /* mode_worktree is zero for a delete. */ + break; + + case DIFF_STATUS_MODIFIED: + case DIFF_STATUS_TYPE_CHANGED: + case DIFF_STATUS_UNMERGED: + d->mode_index = p->one->mode; + d->mode_worktree = p->two->mode; + oidcpy(&d->oid_index, &p->one->oid); + break; + + case DIFF_STATUS_UNKNOWN: + die("BUG: worktree status unknown???"); + break; + } + } } @@ -479,12 +504,36 @@ static void wt_status_collect_updated_cb(struct diff_queue_struct *q, if (!d->index_status) d->index_status = p->status; switch (p->status) { + case DIFF_STATUS_ADDED: + /* Leave {mode,oid}_head zero for an add. */ + d->mode_index = p->two->mode; + oidcpy(&d->oid_index, &p->two->oid); + break; + case DIFF_STATUS_DELETED: + d->mode_head = p->one->mode; + oidcpy(&d->oid_head, &p->one->oid); + /* Leave {mode,oid}_index zero for a delete. */ + break; + case DIFF_STATUS_COPIED: case DIFF_STATUS_RENAMED: d->head_path = xstrdup(p->one->path); + d->score = p->score * 100 / MAX_SCORE; + /* fallthru */ + case DIFF_STATUS_MODIFIED: + case DIFF_STATUS_TYPE_CHANGED: + d->mode_head = p->one->mode; + d->mode_index = p->two->mode; + oidcpy(&d->oid_head, &p->one->oid); + oidcpy(&d->oid_index, &p->two->oid); break; case DIFF_STATUS_UNMERGED: d->stagemask = unmerged_mask(p->two->path); + /* + * Don't bother setting {mode,oid}_{head,index} since the print + * code will output the stage values directly and not use the + * values in these fields. + */ break; } } @@ -565,9 +614,17 @@ static void wt_status_collect_changes_initial(struct wt_status *s) if (ce_stage(ce)) { d->index_status = DIFF_STATUS_UNMERGED; d->stagemask |= (1 << (ce_stage(ce) - 1)); - } - else + /* + * Don't bother setting {mode,oid}_{head,index} since the print + * code will output the stage values directly and not use the + * values in these fields. + */ + } else { d->index_status = DIFF_STATUS_ADDED; + /* Leave {mode,oid}_head zero for adds. */ + d->mode_index = ce->ce_mode; + hashcpy(d->oid_index.hash, ce->sha1); + } } } @@ -1764,6 +1821,9 @@ void wt_status_print(struct wt_status *s) case STATUS_FORMAT_PORCELAIN: wt_porcelain_print(s); break; + case STATUS_FORMAT_PORCELAIN_V2: + /* TODO */ + break; case STATUS_FORMAT_UNSPECIFIED: die("BUG: finalize_deferred_config() should have been called"); break; diff --git a/wt-status.h b/wt-status.h index 9389076f69..43fd3fccf6 100644 --- a/wt-status.h +++ b/wt-status.h @@ -38,6 +38,9 @@ struct wt_status_change_data { int worktree_status; int index_status; int stagemask; + int score; + int mode_head, mode_index, mode_worktree; + struct object_id oid_head, oid_index; char *head_path; unsigned dirty_submodule : 2; unsigned new_submodule_commits : 1; @@ -48,6 +51,7 @@ enum wt_status_format { STATUS_FORMAT_LONG, STATUS_FORMAT_SHORT, STATUS_FORMAT_PORCELAIN, + STATUS_FORMAT_PORCELAIN_V2, STATUS_FORMAT_UNSPECIFIED }; -- cgit v1.2.3 From d9fc746cd77910a7dec53abfec36df5c699b33c2 Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Thu, 11 Aug 2016 10:45:59 -0400 Subject: status: print branch info with --porcelain=v2 --branch Expand porcelain v2 output to include branch and tracking branch information. This includes the commit id, the branch, the upstream branch, and the ahead and behind counts. Signed-off-by: Jeff Hostetler Signed-off-by: Junio C Hamano --- builtin/commit.c | 5 ++++ wt-status.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wt-status.h | 1 + 3 files changed, 96 insertions(+) (limited to 'builtin/commit.c') diff --git a/builtin/commit.c b/builtin/commit.c index 3d222d3970..5504afe53a 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -510,6 +510,8 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int s->fp = fp; s->nowarn = nowarn; s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0; + if (!s->is_initial) + hashcpy(s->sha1_commit, sha1); s->status_format = status_format; s->ignore_submodule_arg = ignore_submodule_arg; @@ -1378,6 +1380,9 @@ int cmd_status(int argc, const char **argv, const char *prefix) fd = hold_locked_index(&index_lock, 0); s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0; + if (!s.is_initial) + hashcpy(s.sha1_commit, sha1); + s.ignore_submodule_arg = ignore_submodule_arg; s.status_format = status_format; s.verbose = verbose; diff --git a/wt-status.c b/wt-status.c index bd9075b7ee..ebe4a1e247 100644 --- a/wt-status.c +++ b/wt-status.c @@ -1812,6 +1812,92 @@ static void wt_porcelain_print(struct wt_status *s) wt_shortstatus_print(s); } +/* + * Print branch information for porcelain v2 output. These lines + * are printed when the '--branch' parameter is given. + * + * # branch.oid + * # branch.head + * [# branch.upstream + * [# branch.ab + -]] + * + * ::= the current commit hash or the the literal + * "(initial)" to indicate an initialized repo + * with no commits. + * + * ::= the current branch name or + * "(detached)" literal when detached head or + * "(unknown)" when something is wrong. + * + * ::= the upstream branch name, when set. + * + * ::= integer ahead value, when upstream set + * and the commit is present (not gone). + * + * ::= integer behind value, when upstream set + * and commit is present. + * + * + * The end-of-line is defined by the -z flag. + * + * ::= NUL when -z, + * LF when NOT -z. + * + */ +static void wt_porcelain_v2_print_tracking(struct wt_status *s) +{ + 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); + + if (!s->branch) + fprintf(s->fp, "# branch.head %s%c", "(unknown)", eol); + else { + 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; + else + branch_name = ""; + } else { + branch_name = NULL; + skip_prefix(s->branch, "refs/heads/", &branch_name); + + fprintf(s->fp, "# branch.head %s%c", branch_name, eol); + } + + /* Lookup stats on the upstream tracking branch, if set. */ + branch = branch_get(branch_name); + base = NULL; + ab_info = (stat_tracking_info(branch, &nr_ahead, &nr_behind, &base) == 0); + if (base) { + base = shorten_unambiguous_ref(base, 0); + fprintf(s->fp, "# branch.upstream %s%c", base, eol); + free((char *)base); + + if (ab_info) + fprintf(s->fp, "# branch.ab +%d -%d%c", nr_ahead, nr_behind, eol); + } + } + + free(state.branch); + free(state.onto); + free(state.detached_from); +} + /* * Convert various submodule status values into a * fixed-length string of characters in the buffer provided. @@ -2058,6 +2144,7 @@ static void wt_porcelain_v2_print_other( /* * Print porcelain V2 status. * + * [] * []* * []* * []* @@ -2070,6 +2157,9 @@ static void wt_porcelain_v2_print(struct wt_status *s) struct string_list_item *it; int i; + if (s->show_branch) + wt_porcelain_v2_print_tracking(s); + for (i = 0; i < s->change.nr; i++) { it = &(s->change.items[i]); d = it->util; diff --git a/wt-status.h b/wt-status.h index 43fd3fccf6..e401837707 100644 --- a/wt-status.h +++ b/wt-status.h @@ -80,6 +80,7 @@ struct wt_status { int hints; enum wt_status_format status_format; + unsigned char sha1_commit[GIT_SHA1_RAWSZ]; /* when not Initial */ /* These are computed during processing of the individual sections */ int commitable; -- cgit v1.2.3