{"thread":{"id":"42985","subject":"[PATCH v4 2/8] status: cleanup API to wt_status_print","startedAt":"2016-08-02T14:16:21Z","lastAt":"2016-08-05T21:47:47Z","messageCount":31,"participants":["Jeff Hostetler","Junio C Hamano","Johannes Schindelin","Jeff King","Stefan Beller"],"isPatch":true,"patchVersion":4,"patchTotal":8},"messages":[{"id":"292829","messageId":"1470147137-17498-3-git-send-email-git@jeffhostetler.com","threadId":"42985","inReplyTo":"1470147137-17498-1-git-send-email-git@jeffhostetler.com","subject":"[PATCH v4 2/8] status: cleanup API to wt_status_print","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2016-08-02T14:12:11Z","receivedAt":"2016-08-02T14:16:21Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"From: Jeff Hostetler <jeffhost@microsoft.com>\n\nRefactor the API between builtin/commit.c and wt-status.[ch].\nHide details of the various wt_*status_print() routines inside\nwt-status.c behind a single (new) wt_status_print() routine\nand eliminate the switch statements from builtin/commit.c\n\nThis will allow us to more easily add new status formats\nand isolate that within wt-status.c\n\nSigned-off-by: Jeff Hostetler <jeffhost@microsoft.com>\nSigned-off-by: Jeff Hostetler <git@jeffhostetler.com>\n---\n builtin/commit.c | 51 +++++++++------------------------------------------\n wt-status.c      | 25 ++++++++++++++++++++++---\n wt-status.h      | 16 ++++++++++++----\n 3 files changed, 43 insertions(+), 49 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex b80273b..a792deb 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -142,14 +142,7 @@ static int show_ignored_in_status, have_option_m;\n static const char *only_include_assumed;\n static struct strbuf message = STRBUF_INIT;\n \n-static enum status_format {\n-\tSTATUS_FORMAT_NONE = 0,\n-\tSTATUS_FORMAT_LONG,\n-\tSTATUS_FORMAT_SHORT,\n-\tSTATUS_FORMAT_PORCELAIN,\n-\n-\tSTATUS_FORMAT_UNSPECIFIED\n-} status_format = STATUS_FORMAT_UNSPECIFIED;\n+static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;\n \n static int opt_parse_m(const struct option *opt, const char *arg, int unset)\n {\n@@ -500,24 +493,11 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int\n \ts->fp = fp;\n \ts->nowarn = nowarn;\n \ts->is_initial = get_sha1(s->reference, sha1) ? 1 : 0;\n+\ts->status_format = status_format;\n+\ts->ignore_submodule_arg = ignore_submodule_arg;\n \n \twt_status_collect(s);\n-\n-\tswitch (status_format) {\n-\tcase STATUS_FORMAT_SHORT:\n-\t\twt_shortstatus_print(s);\n-\t\tbreak;\n-\tcase STATUS_FORMAT_PORCELAIN:\n-\t\twt_porcelain_print(s);\n-\t\tbreak;\n-\tcase STATUS_FORMAT_UNSPECIFIED:\n-\t\tdie(\"BUG: finalize_deferred_config() should have been called\");\n-\t\tbreak;\n-\tcase STATUS_FORMAT_NONE:\n-\tcase STATUS_FORMAT_LONG:\n-\t\twt_longstatus_print(s);\n-\t\tbreak;\n-\t}\n+\twt_status_print(s);\n \n \treturn s->commitable;\n }\n@@ -1099,7 +1079,7 @@ static const char *read_commit_message(const char *name)\n  * is not in effect here.\n  */\n static struct status_deferred_config {\n-\tenum status_format status_format;\n+\tenum wt_status_format status_format;\n \tint show_branch;\n } status_deferred_config = {\n \tSTATUS_FORMAT_UNSPECIFIED,\n@@ -1381,6 +1361,9 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \n \ts.is_initial = get_sha1(s.reference, sha1) ? 1 : 0;\n \ts.ignore_submodule_arg = ignore_submodule_arg;\n+\ts.status_format = status_format;\n+\ts.verbose = verbose;\n+\n \twt_status_collect(&s);\n \n \tif (0 <= fd)\n@@ -1389,23 +1372,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \tif (s.relative_paths)\n \t\ts.prefix = prefix;\n \n-\tswitch (status_format) {\n-\tcase STATUS_FORMAT_SHORT:\n-\t\twt_shortstatus_print(&s);\n-\t\tbreak;\n-\tcase STATUS_FORMAT_PORCELAIN:\n-\t\twt_porcelain_print(&s);\n-\t\tbreak;\n-\tcase STATUS_FORMAT_UNSPECIFIED:\n-\t\tdie(\"BUG: finalize_deferred_config() should have been called\");\n-\t\tbreak;\n-\tcase STATUS_FORMAT_NONE:\n-\tcase STATUS_FORMAT_LONG:\n-\t\ts.verbose = verbose;\n-\t\ts.ignore_submodule_arg = ignore_submodule_arg;\n-\t\twt_longstatus_print(&s);\n-\t\tbreak;\n-\t}\n+\twt_status_print(&s);\n \treturn 0;\n }\n \ndiff --git a/wt-status.c b/wt-status.c\nindex b9a58fd..a9031e4 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -1447,7 +1447,7 @@ static void wt_longstatus_print_state(struct wt_status *s,\n \t\tshow_bisect_in_progress(s, state, state_color);\n }\n \n-void wt_longstatus_print(struct wt_status *s)\n+static void wt_longstatus_print(struct wt_status *s)\n {\n \tconst char *branch_color = color(WT_STATUS_ONBRANCH, s);\n \tconst char *branch_status_color = color(WT_STATUS_HEADER, s);\n@@ -1714,7 +1714,7 @@ static void wt_shortstatus_print_tracking(struct wt_status *s)\n \tfputc(s->null_termination ? '\\0' : '\\n', s->fp);\n }\n \n-void wt_shortstatus_print(struct wt_status *s)\n+static void wt_shortstatus_print(struct wt_status *s)\n {\n \tint i;\n \n@@ -1746,7 +1746,7 @@ void wt_shortstatus_print(struct wt_status *s)\n \t}\n }\n \n-void wt_porcelain_print(struct wt_status *s)\n+static void wt_porcelain_print(struct wt_status *s)\n {\n \ts->use_color = 0;\n \ts->relative_paths = 0;\n@@ -1754,3 +1754,22 @@ void wt_porcelain_print(struct wt_status *s)\n \ts->no_gettext = 1;\n \twt_shortstatus_print(s);\n }\n+\n+void wt_status_print(struct wt_status *s)\n+{\n+\tswitch (s->status_format) {\n+\tcase STATUS_FORMAT_SHORT:\n+\t\twt_shortstatus_print(s);\n+\t\tbreak;\n+\tcase STATUS_FORMAT_PORCELAIN:\n+\t\twt_porcelain_print(s);\n+\t\tbreak;\n+\tcase STATUS_FORMAT_UNSPECIFIED:\n+\t\tdie(\"BUG: finalize_deferred_config() should have been called\");\n+\t\tbreak;\n+\tcase STATUS_FORMAT_NONE:\n+\tcase STATUS_FORMAT_LONG:\n+\t\twt_longstatus_print(s);\n+\t\tbreak;\n+\t}\n+}\ndiff --git a/wt-status.h b/wt-status.h\nindex 2023a3c..a859a12 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -43,6 +43,15 @@ struct wt_status_change_data {\n \tunsigned new_submodule_commits : 1;\n };\n \n+ enum wt_status_format {\n+\tSTATUS_FORMAT_NONE = 0,\n+\tSTATUS_FORMAT_LONG,\n+\tSTATUS_FORMAT_SHORT,\n+\tSTATUS_FORMAT_PORCELAIN,\n+\n+\tSTATUS_FORMAT_UNSPECIFIED\n+ };\n+\n struct wt_status {\n \tint is_initial;\n \tchar *branch;\n@@ -66,6 +75,8 @@ struct wt_status {\n \tint show_branch;\n \tint hints;\n \n+\tenum wt_status_format status_format;\n+\n \t/* These are computed during processing of the individual sections */\n \tint commitable;\n \tint workdir_dirty;\n@@ -99,6 +110,7 @@ struct wt_status_state {\n void wt_status_truncate_message_at_cut_line(struct strbuf *);\n void wt_status_add_cut_line(FILE *fp);\n void wt_status_prepare(struct wt_status *s);\n+void wt_status_print(struct wt_status *s);\n void wt_status_collect(struct wt_status *s);\n void wt_status_get_state(struct wt_status_state *state, int get_detached_from);\n int wt_status_check_rebase(const struct worktree *wt,\n@@ -106,10 +118,6 @@ int wt_status_check_rebase(const struct worktree *wt,\n int wt_status_check_bisect(const struct worktree *wt,\n \t\t\t   struct wt_status_state *state);\n \n-void wt_longstatus_print(struct wt_status *s);\n-void wt_shortstatus_print(struct wt_status *s);\n-void wt_porcelain_print(struct wt_status *s);\n-\n __attribute__((format (printf, 3, 4)))\n void status_printf_ln(struct wt_status *s, const char *color, const char *fmt, ...);\n __attribute__((format (printf, 3, 4)))\n-- \n2.8.0.rc4.17.gac42084.dirty\n\n"},{"id":"292830","messageId":"1470147137-17498-4-git-send-email-git@jeffhostetler.com","threadId":"42985","inReplyTo":"1470147137-17498-1-git-send-email-git@jeffhostetler.com","subject":"[PATCH v4 3/8] status: support --porcelain[=<version>]","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2016-08-02T14:12:12Z","receivedAt":"2016-08-02T14:16:28Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"From: Jeff Hostetler <jeffhost@microsoft.com>\n\nUpdate --porcelain argument to take optional version parameter\nto allow multiple porcelain formats to be supported in the future.\n\nThe token \"v1\" is the default value and indicates the traditional\nporcelain format.  (The token \"1\" is an alias for that.)\n\nSigned-off-by: Jeff Hostetler <jeffhost@microsoft.com>\nSigned-off-by: Jeff Hostetler <git@jeffhostetler.com>\n---\n Documentation/git-status.txt |  7 +++++--\n builtin/commit.c             | 21 ++++++++++++++++++---\n t/t7060-wtstatus.sh          | 21 +++++++++++++++++++++\n 3 files changed, 44 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-status.txt b/Documentation/git-status.txt\nindex e1e8f57..6b1454b 100644\n--- a/Documentation/git-status.txt\n+++ b/Documentation/git-status.txt\n@@ -32,11 +32,14 @@ OPTIONS\n --branch::\n \tShow the branch and tracking info even in short-format.\n \n---porcelain::\n+--porcelain[=<version>]::\n \tGive the output in an easy-to-parse format for scripts.\n \tThis is similar to the short output, but will remain stable\n \tacross Git versions and regardless of user configuration. See\n \tbelow for details.\n++\n+The version parameter is used to specify the format version.\n+This is optional and defaults to the original version 'v1' format.\n \n --long::\n \tGive the output in the long-format. This is the default.\n@@ -96,7 +99,7 @@ configuration variable documented in linkgit:git-config[1].\n \n -z::\n \tTerminate entries with NUL, instead of LF.  This implies\n-\tthe `--porcelain` output format if no other format is given.\n+\tthe `--porcelain=v1` output format if no other format is given.\n \n --column[=<options>]::\n --no-column::\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex a792deb..c3ae2c3 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -144,6 +144,21 @@ static struct strbuf message = STRBUF_INIT;\n \n static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;\n \n+static int opt_parse_porcelain(const struct option *opt, const char *arg, int unset)\n+{\n+\tenum wt_status_format *value = (enum wt_status_format *)opt->value;\n+\tif (unset)\n+\t\t*value = STATUS_FORMAT_NONE;\n+\telse if (!arg)\n+\t\t*value = STATUS_FORMAT_PORCELAIN;\n+\telse if (!strcmp(arg, \"v1\") || !strcmp(arg,\"1\"))\n+\t\t*value = STATUS_FORMAT_PORCELAIN;\n+\telse\n+\t\tdie(\"unsupported porcelain version '%s'\", arg);\n+\n+\treturn 0;\n+}\n+\n static int opt_parse_m(const struct option *opt, const char *arg, int unset)\n {\n \tstruct strbuf *buf = opt->value;\n@@ -1316,9 +1331,9 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \t\t\t    N_(\"show status concisely\"), STATUS_FORMAT_SHORT),\n \t\tOPT_BOOL('b', \"branch\", &s.show_branch,\n \t\t\t N_(\"show branch information\")),\n-\t\tOPT_SET_INT(0, \"porcelain\", &status_format,\n-\t\t\t    N_(\"machine-readable output\"),\n-\t\t\t    STATUS_FORMAT_PORCELAIN),\n+\t\t{ OPTION_CALLBACK, 0, \"porcelain\", &status_format,\n+\t\t  N_(\"version\"), N_(\"machine-readable output\"),\n+\t\t  PARSE_OPT_OPTARG, opt_parse_porcelain },\n \t\tOPT_SET_INT(0, \"long\", &status_format,\n \t\t\t    N_(\"show status in long format (default)\"),\n \t\t\t    STATUS_FORMAT_LONG),\ndiff --git a/t/t7060-wtstatus.sh b/t/t7060-wtstatus.sh\nindex 44bf1d8..00e0ceb 100755\n--- a/t/t7060-wtstatus.sh\n+++ b/t/t7060-wtstatus.sh\n@@ -228,4 +228,25 @@ test_expect_success 'status --branch with detached HEAD' '\n \ttest_i18ncmp expected actual\n '\n \n+## Duplicate the above test and verify --porcelain=v1 arg parsing.\n+test_expect_success 'status --porcelain=v1 --branch with detached HEAD' '\n+\tgit reset --hard &&\n+\tgit checkout master^0 &&\n+\tgit status --branch --porcelain=v1 >actual &&\n+\tcat >expected <<-EOF &&\n+\t## HEAD (no branch)\n+\t?? .gitconfig\n+\t?? actual\n+\t?? expect\n+\t?? expected\n+\t?? mdconflict/\n+\tEOF\n+\ttest_i18ncmp expected actual\n+'\n+\n+## Verify parser error on invalid --porcelain argument.\n+test_expect_success 'status --porcelain=bogus' '\n+\ttest_must_fail git status --porcelain=bogus\n+'\n+\n test_done\n-- \n2.8.0.rc4.17.gac42084.dirty\n\n"},{"id":"292831","messageId":"1470147137-17498-1-git-send-email-git@jeffhostetler.com","threadId":"42985","inReplyTo":null,"subject":"[PATCH v4 0/8] status: V2 porcelain status","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2016-08-02T14:12:09Z","receivedAt":"2016-08-02T14:16:33Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"This patch series adds porcelain V2 format to status.\nThis provides detailed information about file changes\nand about the current branch.\n\nThe new output is accessed via:\n    git status --porcelain=v2 [--branch]\n\nRelative to the V3 patch series, in this V4 patch series\nI've updated Documentation/git-status.txt for clarity.\n\nJeff Hostetler (8):\n  status: rename long-format print routines\n  status: cleanup API to wt_status_print\n  status: support --porcelain[=<version>]\n  status: per-file data collection for --porcelain=v2\n  status: print per-file porcelain v2 status data\n  status: print branch info with --porcelain=v2 --branch\n  git-status.txt: describe --porcelain=v2 format\n  status: tests for --porcelain=v2\n\n Documentation/git-status.txt | 130 +++++++++-\n builtin/commit.c             |  78 +++---\n t/t7060-wtstatus.sh          |  21 ++\n t/t7064-wtstatus-pv2.sh      | 585 +++++++++++++++++++++++++++++++++++++++++++\n wt-status.c                  | 567 ++++++++++++++++++++++++++++++++++++-----\n wt-status.h                  |  19 +-\n 6 files changed, 1289 insertions(+), 111 deletions(-)\n create mode 100755 t/t7064-wtstatus-pv2.sh\n\n-- \n2.8.0.rc4.17.gac42084.dirty\n\n"},{"id":"292832","messageId":"1470147137-17498-9-git-send-email-git@jeffhostetler.com","threadId":"42985","inReplyTo":"1470147137-17498-1-git-send-email-git@jeffhostetler.com","subject":"[PATCH v4 8/8] status: tests for --porcelain=v2","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2016-08-02T14:12:17Z","receivedAt":"2016-08-02T14:16:37Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"From: Jeff Hostetler <jeffhost@microsoft.com>\n\nUnit tests for porcelain v2 status format.\n\nSigned-off-by: Jeff Hostetler <jeffhost@microsoft.com>\nSigned-off-by: Jeff Hostetler <git@jeffhostetler.com>\n---\n t/t7064-wtstatus-pv2.sh | 585 ++++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 585 insertions(+)\n create mode 100755 t/t7064-wtstatus-pv2.sh\n\ndiff --git a/t/t7064-wtstatus-pv2.sh b/t/t7064-wtstatus-pv2.sh\nnew file mode 100755\nindex 0000000..ff0dd3d\n--- /dev/null\n+++ b/t/t7064-wtstatus-pv2.sh\n@@ -0,0 +1,585 @@\n+#!/bin/sh\n+\n+test_description='git status --porcelain=v2\n+\n+This test exercises porcelain V2 output for git status.'\n+\n+\n+. ./test-lib.sh\n+\n+test_expect_success setup '\n+\ttest_tick &&\n+\tgit config --local core.autocrlf false &&\n+\techo x >file_x &&\n+\techo y >file_y &&\n+\techo z >file_z &&\n+\tmkdir dir1 &&\n+\techo a >dir1/file_a &&\n+\techo b >dir1/file_b\n+'\n+\n+\n+##################################################################\n+## Confirm output prior to initial commit.\n+##################################################################\n+\n+test_expect_success pre_initial_commit_0 '\n+\tcat >expected <<-EOF &&\n+\t# branch.oid (initial)\n+\t# branch.head master\n+\t? actual\n+\t? dir1/\n+\t? expected\n+\t? file_x\n+\t? file_y\n+\t? file_z\n+\tEOF\n+\n+\tgit status --porcelain=v2 --branch --ignored --untracked-files=normal >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+\n+test_expect_success pre_initial_commit_1 '\n+\tgit add file_x file_y file_z dir1 &&\n+\tSHA_A=`git hash-object -t blob -- dir1/file_a` &&\n+\tSHA_B=`git hash-object -t blob -- dir1/file_b` &&\n+\tSHA_X=`git hash-object -t blob -- file_x` &&\n+\tSHA_Y=`git hash-object -t blob -- file_y` &&\n+\tSHA_Z=`git hash-object -t blob -- file_z` &&\n+\tSHA_ZERO=0000000000000000000000000000000000000000 &&\n+\n+\tcat >expected <<-EOF &&\n+\t# branch.oid (initial)\n+\t# branch.head master\n+\t1 A. N... 000000 100644 100644 $SHA_ZERO $SHA_A dir1/file_a\n+\t1 A. N... 000000 100644 100644 $SHA_ZERO $SHA_B dir1/file_b\n+\t1 A. N... 000000 100644 100644 $SHA_ZERO $SHA_X file_x\n+\t1 A. N... 000000 100644 100644 $SHA_ZERO $SHA_Y file_y\n+\t1 A. N... 000000 100644 100644 $SHA_ZERO $SHA_Z file_z\n+\t? actual\n+\t? expected\n+\tEOF\n+\n+\tgit status --porcelain=v2 --branch --ignored --untracked-files=all >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+## Try -z on the above\n+test_expect_success pre_initial_commit_2 '\n+\tcat >expected.lf <<-EOF &&\n+\t# branch.oid (initial)\n+\t# branch.head master\n+\t1 A. N... 000000 100644 100644 $SHA_ZERO $SHA_A dir1/file_a\n+\t1 A. N... 000000 100644 100644 $SHA_ZERO $SHA_B dir1/file_b\n+\t1 A. N... 000000 100644 100644 $SHA_ZERO $SHA_X file_x\n+\t1 A. N... 000000 100644 100644 $SHA_ZERO $SHA_Y file_y\n+\t1 A. N... 000000 100644 100644 $SHA_ZERO $SHA_Z file_z\n+\t? actual\n+\t? expected\n+\tEOF\n+\tperl -pe y/\\\\012/\\\\000/ <expected.lf >expected &&\n+\trm expected.lf &&\n+\n+\tgit status -z --porcelain=v2 --branch --ignored --untracked-files=all >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+##################################################################\n+## Create first commit. Confirm commit sha in new track header.\n+## Then make some changes on top of it.\n+##################################################################\n+\n+test_expect_success initial_commit_0 '\n+\tgit commit -m initial &&\n+\tH0=`git rev-parse HEAD` &&\n+\tcat >expected <<-EOF &&\n+\t# branch.oid $H0\n+\t# branch.head master\n+\t? actual\n+\t? expected\n+\tEOF\n+\n+\tgit status --porcelain=v2 --branch --ignored --untracked-files=all >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+\n+test_expect_success initial_commit_1 '\n+\techo x >>file_x &&\n+\tSHA_X1=`git hash-object -t blob -- file_x` &&\n+\trm file_z &&\n+\tH0=`git rev-parse HEAD` &&\n+\n+\tcat >expected <<-EOF &&\n+\t# branch.oid $H0\n+\t# branch.head master\n+\t1 .M N... 100644 100644 100644 $SHA_X $SHA_X file_x\n+\t1 .D N... 100644 100644 000000 $SHA_Z $SHA_Z file_z\n+\t? actual\n+\t? expected\n+\tEOF\n+\n+\tgit status --porcelain=v2 --branch --ignored --untracked-files=all >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+\n+test_expect_success initial_commit_2 '\n+\tgit add file_x &&\n+\tgit rm file_z &&\n+\tH0=`git rev-parse HEAD` &&\n+\n+\tcat >expected <<-EOF &&\n+\t# branch.oid $H0\n+\t# branch.head master\n+\t1 M. N... 100644 100644 100644 $SHA_X $SHA_X1 file_x\n+\t1 D. N... 100644 000000 000000 $SHA_Z $SHA_ZERO file_z\n+\t? actual\n+\t? expected\n+\tEOF\n+\n+\tgit status --porcelain=v2 --branch --ignored --untracked-files=all >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+\n+test_expect_success initial_commit_3 '\n+\tgit mv file_y renamed_y &&\n+\tH0=`git rev-parse HEAD` &&\n+\n+\tcat >expected.q <<-EOF &&\n+\t# branch.oid $H0\n+\t# branch.head master\n+\t1 M. N... 100644 100644 100644 $SHA_X $SHA_X1 file_x\n+\t1 D. N... 100644 000000 000000 $SHA_Z $SHA_ZERO file_z\n+\t2 R. N... 100644 100644 100644 $SHA_Y $SHA_Y R100 renamed_yQfile_y\n+\t? actual\n+\t? expected\n+\tEOF\n+\tq_to_tab <expected.q >expected &&\n+\trm expected.q &&\n+\n+\tgit status --porcelain=v2 --branch --ignored --untracked-files=all >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+\n+##################################################################\n+## Create second commit.\n+##################################################################\n+\n+test_expect_success second_commit_0 '\n+\tgit commit -m second &&\n+\tH1=`git rev-parse HEAD` &&\n+\n+\tcat >expected <<-EOF &&\n+\t# branch.oid $H1\n+\t# branch.head master\n+\t? actual\n+\t? expected\n+\tEOF\n+\n+\tgit status --porcelain=v2 --branch --ignored --untracked-files=all >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+\n+##################################################################\n+## Ignore a file\n+##################################################################\n+\n+test_expect_success ignore_file_0 '\n+\techo x.ign >.gitignore &&\n+\techo \"ignore me\" >x.ign &&\n+\tH1=`git rev-parse HEAD` &&\n+\n+\tcat >expected <<-EOF &&\n+\t# branch.oid $H1\n+\t# branch.head master\n+\t? .gitignore\n+\t? actual\n+\t? expected\n+\t! x.ign\n+\tEOF\n+\n+\tgit status --porcelain=v2 --branch --ignored --untracked-files=all >actual &&\n+\trm x.ign &&\n+\trm .gitignore &&\n+\ttest_cmp expected actual\n+'\n+\n+\n+##################################################################\n+## Create some conflicts.\n+##################################################################\n+\n+test_expect_success conflict_AA '\n+\tgit branch AA_A master &&\n+\tgit checkout AA_A &&\n+\techo \"Branch AA_A\" >conflict.txt &&\n+\tSHA_AA_A=`git hash-object -t blob -- conflict.txt` &&\n+\tgit add conflict.txt &&\n+\tgit commit -m \"branch aa_a\" &&\n+\n+\tgit branch AA_B master &&\n+\tgit checkout AA_B &&\n+\techo \"Branch AA_B\" >conflict.txt &&\n+\tSHA_AA_B=`git hash-object -t blob -- conflict.txt` &&\n+\tgit add conflict.txt &&\n+\tgit commit -m \"branch aa_b\" &&\n+\n+\tgit branch AA_M AA_B &&\n+\tgit checkout AA_M &&\n+\ttest_must_fail git merge AA_A &&\n+\n+\tHM=`git rev-parse HEAD` &&\n+\n+\tcat >expected <<-EOF &&\n+\t# branch.oid $HM\n+\t# branch.head AA_M\n+\tu AA N... 000000 100644 100644 100644 $SHA_ZERO $SHA_AA_B $SHA_AA_A conflict.txt\n+\t? actual\n+\t? expected\n+\tEOF\n+\n+\tgit status --porcelain=v2 --branch --ignored --untracked-files=all >actual &&\n+\tgit reset --hard &&\n+\ttest_cmp expected actual\n+'\n+\n+\n+test_expect_success conflict_UU '\n+\tgit branch UU_ANC master &&\n+\tgit checkout UU_ANC &&\n+\techo \"Ancestor\" >conflict.txt &&\n+\tSHA_UU_ANC=`git hash-object -t blob -- conflict.txt` &&\n+\tgit add conflict.txt &&\n+\tgit commit -m \"UU_ANC\" &&\n+\n+\tgit branch UU_A UU_ANC &&\n+\tgit checkout UU_A &&\n+\techo \"Branch UU_A\" >conflict.txt &&\n+\tSHA_UU_A=`git hash-object -t blob -- conflict.txt` &&\n+\tgit add conflict.txt &&\n+\tgit commit -m \"branch uu_a\" &&\n+\n+\tgit branch UU_B UU_ANC &&\n+\tgit checkout UU_B &&\n+\techo \"Branch UU_B\" >conflict.txt &&\n+\tSHA_UU_B=`git hash-object -t blob -- conflict.txt` &&\n+\tgit add conflict.txt &&\n+\tgit commit -m \"branch uu_b\" &&\n+\n+\tgit branch UU_M UU_B &&\n+\tgit checkout UU_M &&\n+\ttest_must_fail git merge UU_A &&\n+\n+\tHM=`git rev-parse HEAD` &&\n+\n+\tcat >expected <<-EOF &&\n+\t# branch.oid $HM\n+\t# branch.head UU_M\n+\tu UU N... 100644 100644 100644 100644 $SHA_UU_ANC $SHA_UU_B $SHA_UU_A conflict.txt\n+\t? actual\n+\t? expected\n+\tEOF\n+\n+\tgit status --porcelain=v2 --branch --ignored --untracked-files=all >actual &&\n+\tgit reset --hard &&\n+\ttest_cmp expected actual\n+'\n+\n+\n+##################################################################\n+## Test upstream fields in branch header\n+##################################################################\n+\n+test_expect_success 'upstream_fields_0' '\n+\tgit checkout master &&\n+\tgit clone . sub_repo &&\n+\t(\n+\t\t## Confirm local master tracks remote master.\n+\t\tcd sub_repo &&\n+\t\tHUF=`git rev-parse HEAD` &&\n+\n+\t\tcat >expected <<-EOF &&\n+\t\t# branch.oid $HUF\n+\t\t# branch.head master\n+\t\t# branch.upstream origin/master\n+\t\t# branch.ab +0 -0\n+\t\t? actual\n+\t\t? expected\n+\t\tEOF\n+\n+\t\tgit status --porcelain=v2 --branch --ignored --untracked-files=all >actual &&\n+\t\ttest_cmp expected actual &&\n+\n+\t\t## Test ahead/behind.\n+\t\techo xyz >file_xyz &&\n+\t\tgit add file_xyz &&\n+\t\tgit commit -m xyz &&\n+\n+\t\tHUF=`git rev-parse HEAD` &&\n+\n+\t\tcat >expected <<-EOF &&\n+\t\t# branch.oid $HUF\n+\t\t# branch.head master\n+\t\t# branch.upstream origin/master\n+\t\t# branch.ab +1 -0\n+\t\t? actual\n+\t\t? expected\n+\t\tEOF\n+\n+\t\tgit status --porcelain=v2 --branch --ignored --untracked-files=all >actual &&\n+\t\ttest_cmp expected actual &&\n+\n+\t\t## Test upstream-gone case. Fake this by pointing origin/master at\n+\t\t## a non-existing commit.\n+\t\tOLD=`git rev-parse origin/master` &&\n+\t\tNEW=$SHA_ZERO &&\n+\t\tmv .git/packed-refs .git/old-packed-refs &&\n+\t\tsed \"s/$OLD/$NEW/g\" <.git/old-packed-refs >.git/packed-refs &&\n+\n+\t\tHUF=`git rev-parse HEAD` &&\n+\n+\t\tcat >expected <<-EOF &&\n+\t\t# branch.oid $HUF\n+\t\t# branch.head master\n+\t\t# branch.upstream origin/master\n+\t\t? actual\n+\t\t? expected\n+\t\tEOF\n+\n+\t\tgit status --porcelain=v2 --branch --ignored --untracked-files=all >actual &&\n+\t\ttest_cmp expected actual\n+\t) &&\n+\trm -rf sub_repo\n+'\n+\n+\n+##################################################################\n+## Test submodule status flags.\n+##################################################################\n+\n+test_expect_success 'submodule_flags_0' '\n+\tgit checkout master &&\n+\tgit clone . sub_repo &&\n+\tgit clone . super_repo &&\n+\t(\tcd super_repo &&\n+\t\tgit submodule add ../sub_repo sub1 &&\n+\n+\t\t## Confirm stage/add of clean submodule.\n+\t\tHMOD=`git hash-object -t blob -- .gitmodules` &&\n+\t\tHSUP=`git rev-parse HEAD` &&\n+\t\tHSUB=$HSUP &&\n+\n+\t\tcat >expected <<-EOF &&\n+\t\t# branch.oid $HSUP\n+\t\t# branch.head master\n+\t\t# branch.upstream origin/master\n+\t\t# branch.ab +0 -0\n+\t\t1 A. N... 000000 100644 100644 $SHA_ZERO $HMOD .gitmodules\n+\t\t1 A. S... 000000 160000 160000 $SHA_ZERO $HSUB sub1\n+\t\t? actual\n+\t\t? expected\n+\t\tEOF\n+\n+\t\tgit status --porcelain=v2 --branch --ignored --untracked-files=all >actual &&\n+\t\ttest_cmp expected actual\n+\t)\n+'\n+\n+test_expect_success 'submodule_flags_1' '\n+\t(\tcd super_repo &&\n+\t\t## Make some untracked dirt in the submodule.\n+\t\t(\tcd sub1 &&\n+\t\t\techo \"dirt\" >file_in_sub\n+\t\t) &&\n+\n+\t\tHMOD=`git hash-object -t blob -- .gitmodules` &&\n+\t\tHSUP=`git rev-parse HEAD` &&\n+\t\tHSUB=$HSUP &&\n+\n+\t\tcat >expected <<-EOF &&\n+\t\t# branch.oid $HSUP\n+\t\t# branch.head master\n+\t\t# branch.upstream origin/master\n+\t\t# branch.ab +0 -0\n+\t\t1 A. N... 000000 100644 100644 $SHA_ZERO $HMOD .gitmodules\n+\t\t1 AM S..U 000000 160000 160000 $SHA_ZERO $HSUB sub1\n+\t\t? actual\n+\t\t? expected\n+\t\tEOF\n+\n+\t\tgit status --porcelain=v2 --branch --ignored --untracked-files=all >actual &&\n+\t\ttest_cmp expected actual\n+\t)\n+'\n+\n+test_expect_success 'submodule_flags_2' '\n+\t(\tcd super_repo &&\n+\t\t## Make some staged dirt in the submodule.\n+\t\t(\tcd sub1 &&\n+\t\t\tgit add file_in_sub\n+\t\t) &&\n+\n+\t\tHMOD=`git hash-object -t blob -- .gitmodules` &&\n+\t\tHSUP=`git rev-parse HEAD` &&\n+\t\tHSUB=$HSUP &&\n+\n+\t\tcat >expected <<-EOF &&\n+\t\t# branch.oid $HSUP\n+\t\t# branch.head master\n+\t\t# branch.upstream origin/master\n+\t\t# branch.ab +0 -0\n+\t\t1 A. N... 000000 100644 100644 $SHA_ZERO $HMOD .gitmodules\n+\t\t1 AM S.M. 000000 160000 160000 $SHA_ZERO $HSUB sub1\n+\t\t? actual\n+\t\t? expected\n+\t\tEOF\n+\n+\t\tgit status --porcelain=v2 --branch --ignored --untracked-files=all >actual &&\n+\t\ttest_cmp expected actual\n+\t)\n+'\n+\n+test_expect_success 'submodule_flags_3' '\n+\t(\tcd super_repo &&\n+\t\t## Make some staged and unstaged dirt (on the same file) in the submodule.\n+\t\t## This does not cause us to get S.MU (because the submodule does not report\n+\t\t## a \"?\" line for the unstaged changes).\n+\t\t(\tcd sub1 &&\n+\t\t\techo \"more dirt\" >>file_in_sub\n+\t\t) &&\n+\n+\t\tHMOD=`git hash-object -t blob -- .gitmodules` &&\n+\t\tHSUP=`git rev-parse HEAD` &&\n+\t\tHSUB=$HSUP &&\n+\n+\t\tcat >expected <<-EOF &&\n+\t\t# branch.oid $HSUP\n+\t\t# branch.head master\n+\t\t# branch.upstream origin/master\n+\t\t# branch.ab +0 -0\n+\t\t1 A. N... 000000 100644 100644 $SHA_ZERO $HMOD .gitmodules\n+\t\t1 AM S.M. 000000 160000 160000 $SHA_ZERO $HSUB sub1\n+\t\t? actual\n+\t\t? expected\n+\t\tEOF\n+\n+\t\tgit status --porcelain=v2 --branch --ignored --untracked-files=all >actual &&\n+\t\ttest_cmp expected actual\n+\t)\n+'\n+\n+test_expect_success 'submodule_flags_4' '\n+\t(\tcd super_repo &&\n+\t\t## Make some staged and untracked dirt (on different files) in the submodule.\n+\t\t(\tcd sub1 &&\n+\t\t\tgit add file_in_sub &&\n+\t\t\techo \"dirt\" >>another_file_in_sub\n+\t\t) &&\n+\n+\t\tHMOD=`git hash-object -t blob -- .gitmodules` &&\n+\t\tHSUP=`git rev-parse HEAD` &&\n+\t\tHSUB=$HSUP &&\n+\n+\t\tcat >expected <<-EOF &&\n+\t\t# branch.oid $HSUP\n+\t\t# branch.head master\n+\t\t# branch.upstream origin/master\n+\t\t# branch.ab +0 -0\n+\t\t1 A. N... 000000 100644 100644 $SHA_ZERO $HMOD .gitmodules\n+\t\t1 AM S.MU 000000 160000 160000 $SHA_ZERO $HSUB sub1\n+\t\t? actual\n+\t\t? expected\n+\t\tEOF\n+\n+\t\tgit status --porcelain=v2 --branch --ignored --untracked-files=all >actual &&\n+\t\ttest_cmp expected actual\n+\t)\n+'\n+\n+test_expect_success 'submodule_flags_5' '\n+\t(\tcd super_repo &&\n+\t\t## Make a new commit in the submodule.\n+\t\t(\tcd sub1 &&\n+\t\t\tgit add file_in_sub &&\n+\t\t\trm -f another_file_in_sub &&\n+\t\t\tgit commit -m \"new commit\"\n+\t\t) &&\n+\n+\t\tHMOD=`git hash-object -t blob -- .gitmodules` &&\n+\t\tHSUP=`git rev-parse HEAD` &&\n+\t\tHSUB=$HSUP &&\n+\n+\t\tcat >expected <<-EOF &&\n+\t\t# branch.oid $HSUP\n+\t\t# branch.head master\n+\t\t# branch.upstream origin/master\n+\t\t# branch.ab +0 -0\n+\t\t1 A. N... 000000 100644 100644 $SHA_ZERO $HMOD .gitmodules\n+\t\t1 AM SC.. 000000 160000 160000 $SHA_ZERO $HSUB sub1\n+\t\t? actual\n+\t\t? expected\n+\t\tEOF\n+\n+\t\tgit status --porcelain=v2 --branch --ignored --untracked-files=all >actual &&\n+\t\ttest_cmp expected actual\n+\t)\n+'\n+\n+test_expect_success 'submodule_flags_6' '\n+\t(\tcd super_repo &&\n+\t\t## Commit the new submodule commit in the super.\n+\t\tgit add sub1 &&\n+\t\tgit commit -m \"super commit\" &&\n+\n+\t\tHSUP=`git rev-parse HEAD` &&\n+\n+\t\tcat >expected <<-EOF &&\n+\t\t# branch.oid $HSUP\n+\t\t# branch.head master\n+\t\t# branch.upstream origin/master\n+\t\t# branch.ab +1 -0\n+\t\t? actual\n+\t\t? expected\n+\t\tEOF\n+\n+\t\tgit status --porcelain=v2 --branch --ignored --untracked-files=all >actual &&\n+\t\ttest_cmp expected actual\n+\t)\n+'\n+\n+test_expect_success 'submodule_flags_7' '\n+\t(\tcd super_repo &&\n+\t\t## Make some untracked dirt in the submodule.\n+\t\t(\tcd sub1 &&\n+\t\t\techo \"yet more dirt\" >>file_in_sub\n+\t\t) &&\n+\n+\t\tHMOD=`git hash-object -t blob -- .gitmodules` &&\n+\t\tHSUP=`git rev-parse HEAD` &&\n+\t\tHSUB=`(cd sub1 && git rev-parse HEAD)` &&\n+\n+\t\tcat >expected <<-EOF &&\n+\t\t# branch.oid $HSUP\n+\t\t# branch.head master\n+\t\t# branch.upstream origin/master\n+\t\t# branch.ab +1 -0\n+\t\t1 .M S.M. 160000 160000 160000 $HSUB $HSUB sub1\n+\t\t? actual\n+\t\t? expected\n+\t\tEOF\n+\n+\t\tgit status --porcelain=v2 --branch --ignored --untracked-files=all >actual &&\n+\t\ttest_cmp expected actual\n+\t)\n+'\n+\n+##################################################################\n+## The end.\n+##################################################################\n+\n+test_done\n-- \n2.8.0.rc4.17.gac42084.dirty\n\n"},{"id":"292833","messageId":"1470147137-17498-7-git-send-email-git@jeffhostetler.com","threadId":"42985","inReplyTo":"1470147137-17498-1-git-send-email-git@jeffhostetler.com","subject":"[PATCH v4 6/8] status: print branch info with --porcelain=v2 --branch","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2016-08-02T14:12:15Z","receivedAt":"2016-08-02T14:16:43Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"From: Jeff Hostetler <jeffhost@microsoft.com>\n\nExpand porcelain v2 output to include branch and tracking\nbranch information.  This includes the commit SHA, the branch,\nthe upstream branch, and the ahead and behind counts.\n\nSigned-off-by: Jeff Hostetler <jeffhost@microsoft.com>\nSigned-off-by: Jeff Hostetler <git@jeffhostetler.com>\n---\n builtin/commit.c |  5 ++++\n wt-status.c      | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n wt-status.h      |  1 +\n 3 files changed, 96 insertions(+)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 93ce28c..b1fd2d1 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -510,6 +510,8 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int\n \ts->fp = fp;\n \ts->nowarn = nowarn;\n \ts->is_initial = get_sha1(s->reference, sha1) ? 1 : 0;\n+\tif (!s->is_initial)\n+\t\thashcpy(s->sha1_commit, sha1);\n \ts->status_format = status_format;\n \ts->ignore_submodule_arg = ignore_submodule_arg;\n \n@@ -1378,6 +1380,9 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \tfd = hold_locked_index(&index_lock, 0);\n \n \ts.is_initial = get_sha1(s.reference, sha1) ? 1 : 0;\n+\tif (!s.is_initial)\n+\t\thashcpy(s.sha1_commit, sha1);\n+\n \ts.ignore_submodule_arg = ignore_submodule_arg;\n \ts.status_format = status_format;\n \ts.verbose = verbose;\ndiff --git a/wt-status.c b/wt-status.c\nindex 46061d4..592fbd2 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -1814,6 +1814,92 @@ static void wt_porcelain_print(struct wt_status *s)\n }\n \n /*\n+ * Print branch information for porcelain v2 output.  These lines\n+ * are printed when the '--branch' parameter is given.\n+ *\n+ *    # branch.oid <commit><eol>\n+ *    # branch.head <head><eol>\n+ *   [# branch.upstream <upstream><eol>\n+ *   [# branch.ab +<ahead> -<behind><eol>]]\n+ *\n+ *      <commit> ::= the current commit hash or the the literal\n+ *                   \"(initial)\" to indicate an initialized repo\n+ *                   with no commits.\n+ *\n+ *        <head> ::= <branch_name> the current branch name or\n+ *                   \"(detached)\" literal when detached head or\n+ *                   \"(unknown)\" when something is wrong.\n+ *\n+ *    <upstream> ::= the upstream branch name, when set.\n+ *\n+ *       <ahead> ::= integer ahead value, when upstream set\n+ *                   and the commit is present (not gone).\n+ *\n+ *      <behind> ::= integer behind value, when upstream set\n+ *                   and commit is present.\n+ *\n+ *\n+ * The end-of-line is defined by the -z flag.\n+ *\n+ *                 <eol> ::= NUL when -z,\n+ *                           LF when NOT -z.\n+ *\n+ */\n+static void wt_porcelain_v2_print_tracking(struct wt_status *s)\n+{\n+\tstruct branch *branch;\n+\tconst char *base;\n+\tconst char *branch_name;\n+\tstruct wt_status_state state;\n+\tint ab_info, nr_ahead, nr_behind;\n+\tchar eol = s->null_termination ? '\\0' : '\\n';\n+\n+\tmemset(&state, 0, sizeof(state));\n+\twt_status_get_state(&state, s->branch && !strcmp(s->branch, \"HEAD\"));\n+\n+\tfprintf(s->fp, \"# branch.oid %s%c\",\n+\t\t\t(s->is_initial ? \"(initial)\" : sha1_to_hex(s->sha1_commit)),\n+\t\t\teol);\n+\n+\tif (!s->branch)\n+\t\tfprintf(s->fp, \"# branch.head %s%c\", \"(unknown)\", eol);\n+\telse {\n+\t\tif (!strcmp(s->branch, \"HEAD\")) {\n+\t\t\tfprintf(s->fp, \"# branch.head %s%c\", \"(detached)\", eol);\n+\n+\t\t\tif (state.rebase_in_progress || state.rebase_interactive_in_progress)\n+\t\t\t\tbranch_name = state.onto;\n+\t\t\telse if (state.detached_from)\n+\t\t\t\tbranch_name = state.detached_from;\n+\t\t\telse\n+\t\t\t\tbranch_name = \"\";\n+\t\t} else {\n+\t\t\tbranch_name = NULL;\n+\t\t\tskip_prefix(s->branch, \"refs/heads/\", &branch_name);\n+\n+\t\t\tfprintf(s->fp, \"# branch.head %s%c\", branch_name, eol);\n+\t\t}\n+\n+\t\t/* Lookup stats on the upstream tracking branch, if set. */\n+\t\tbranch = branch_get(branch_name);\n+\t\tbase = NULL;\n+\t\tab_info = (stat_tracking_info(branch, &nr_ahead, &nr_behind, &base) == 0);\n+\t\tif (base) {\n+\t\t\tbase = shorten_unambiguous_ref(base, 0);\n+\t\t\tfprintf(s->fp, \"# branch.upstream %s%c\", base, eol);\n+\t\t\tfree((char *)base);\n+\n+\t\t\tif (ab_info)\n+\t\t\t\tfprintf(s->fp, \"# branch.ab +%d -%d%c\", nr_ahead, nr_behind, eol);\n+\t\t}\n+\t}\n+\n+\tfree(state.branch);\n+\tfree(state.onto);\n+\tfree(state.detached_from);\n+}\n+\n+/*\n  * Convert various submodule status values into a\n  * fixed-length string of characters in the buffer provided.\n  */\n@@ -2057,6 +2143,7 @@ static void wt_porcelain_v2_print_other(\n /*\n  * Print porcelain V2 status.\n  *\n+ * [<v2_branch>]\n  * [<v2_changed_items>]*\n  * [<v2_unmerged_items>]*\n  * [<v2_untracked_items>]*\n@@ -2069,6 +2156,9 @@ void wt_porcelain_v2_print(struct wt_status *s)\n \tstruct string_list_item *it;\n \tint i;\n \n+\tif (s->show_branch)\n+\t\twt_porcelain_v2_print_tracking(s);\n+\n \tfor (i = 0; i < s->change.nr; i++) {\n \t\tit = &(s->change.items[i]);\n \t\td = it->util;\ndiff --git a/wt-status.h b/wt-status.h\nindex 89a6d43..baedfe3 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -80,6 +80,7 @@ struct wt_status {\n \tint hints;\n \n \tenum wt_status_format status_format;\n+\tunsigned char sha1_commit[GIT_SHA1_RAWSZ]; /* when not Initial */\n \n \t/* These are computed during processing of the individual sections */\n \tint commitable;\n-- \n2.8.0.rc4.17.gac42084.dirty\n\n"},{"id":"292834","messageId":"1470147137-17498-6-git-send-email-git@jeffhostetler.com","threadId":"42985","inReplyTo":"1470147137-17498-1-git-send-email-git@jeffhostetler.com","subject":"[PATCH v4 5/8] status: print per-file porcelain v2 status data","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2016-08-02T14:12:14Z","receivedAt":"2016-08-02T14:16:48Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"From: Jeff Hostetler <jeffhost@microsoft.com>\n\nPrint per-file information in porcelain v2 format.\n\nSigned-off-by: Jeff Hostetler <jeffhost@microsoft.com>\nSigned-off-by: Jeff Hostetler <git@jeffhostetler.com>\n---\n wt-status.c | 283 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-\n 1 file changed, 282 insertions(+), 1 deletion(-)\n\ndiff --git a/wt-status.c b/wt-status.c\nindex 15d3349..46061d4 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -1813,6 +1813,287 @@ static void wt_porcelain_print(struct wt_status *s)\n \twt_shortstatus_print(s);\n }\n \n+/*\n+ * Convert various submodule status values into a\n+ * fixed-length string of characters in the buffer provided.\n+ */\n+static void wt_porcelain_v2_submodule_state(\n+\tstruct wt_status_change_data *d,\n+\tchar sub[5])\n+{\n+\tif (S_ISGITLINK(d->mode_head) ||\n+\t\tS_ISGITLINK(d->mode_index) ||\n+\t\tS_ISGITLINK(d->mode_worktree)) {\n+\t\tsub[0] = 'S';\n+\t\tsub[1] = d->new_submodule_commits ? 'C' : '.';\n+\t\tsub[2] = (d->dirty_submodule & DIRTY_SUBMODULE_MODIFIED) ? 'M' : '.';\n+\t\tsub[3] = (d->dirty_submodule & DIRTY_SUBMODULE_UNTRACKED) ? 'U' : '.';\n+\t} else {\n+\t\tsub[0] = 'N';\n+\t\tsub[1] = '.';\n+\t\tsub[2] = '.';\n+\t\tsub[3] = '.';\n+\t}\n+\tsub[4] = 0;\n+}\n+\n+/*\n+ * Fix-up changed entries before we print them.\n+ */\n+static void wt_porcelain_v2_fix_up_changed(\n+\tstruct string_list_item *it,\n+\tstruct wt_status *s)\n+{\n+\tstruct wt_status_change_data *d = it->util;\n+\n+\tif (!d->index_status) {\n+\t\t/*\n+\t\t * This entry is unchanged in the index (relative to the head).\n+\t\t * Therefore, the collect_updated_cb was never called for this\n+\t\t * entry (during the head-vs-index scan) and so the head column\n+\t\t * fields were never set.\n+\t\t *\n+\t\t * We must have data for the index column (from the\n+\t\t * index-vs-worktree scan (otherwise, this entry should not be\n+\t\t * in the list of changes)).\n+\t\t *\n+\t\t * Copy index column fields to the head column, so that our\n+\t\t * output looks complete.\n+\t\t */\n+\t\tassert(d->mode_head == 0);\n+\t\td->mode_head = d->mode_index;\n+\t\toidcpy(&d->oid_head, &d->oid_index);\n+\t}\n+\n+\tif (!d->worktree_status) {\n+\t\t/*\n+\t\t * This entry is unchanged in the worktree (relative to the index).\n+\t\t * Therefore, the collect_changed_cb was never called for this entry\n+\t\t * (during the index-vs-worktree scan) and so the worktree column\n+\t\t * fields were never set.\n+\t\t *\n+\t\t * We must have data for the index column (from the head-vs-index\n+\t\t * scan).\n+\t\t *\n+\t\t * Copy the index column fields to the worktree column so that\n+\t\t * our output looks complete.\n+\t\t *\n+\t\t * Note that we only have a mode field in the worktree column\n+\t\t * because the scan code tries really hard to not have to compute it.\n+\t\t */\n+\t\tassert(d->mode_worktree == 0);\n+\t\td->mode_worktree = d->mode_index;\n+\t}\n+}\n+\n+/*\n+ * Print porcelain v2 info for tracked entries with changes.\n+ */\n+static void wt_porcelain_v2_print_changed_entry(\n+\tstruct string_list_item *it,\n+\tstruct wt_status *s)\n+{\n+\tstruct wt_status_change_data *d = it->util;\n+\tstruct strbuf buf_current = STRBUF_INIT;\n+\tstruct strbuf buf_src = STRBUF_INIT;\n+\tconst char *path_current = NULL;\n+\tconst char *path_src = NULL;\n+\tchar key[3];\n+\tchar submodule_token[5];\n+\tchar sep_char, eol_char;\n+\n+\twt_porcelain_v2_fix_up_changed(it, s);\n+\twt_porcelain_v2_submodule_state(d, submodule_token);\n+\n+\tkey[0] = d->index_status ? d->index_status : '.';\n+\tkey[1] = d->worktree_status ? d->worktree_status : '.';\n+\tkey[2] = 0;\n+\n+\tif (s->null_termination) {\n+\t\t/*\n+\t\t * In -z mode, we DO NOT C-Quote pathnames.  Current path is ALWAYS first.\n+\t\t * A single NUL character separates them.\n+\t\t */\n+\t\tsep_char = '\\0';\n+\t\teol_char = '\\0';\n+\t\tpath_current = it->string;\n+\t\tpath_src = d->head_path;\n+\t} else {\n+\t\t/*\n+\t\t * Path(s) are C-Quoted if necessary. Current path is ALWAYS first.\n+\t\t * The source path is only present when necessary.\n+\t\t * A single TAB separates them (because paths can contain spaces\n+\t\t * which are not escaped and C-Quoting does escape TAB characters).\n+\t\t */\n+\t\tsep_char = '\\t';\n+\t\teol_char = '\\n';\n+\t\tpath_current = quote_path(it->string, s->prefix, &buf_current);\n+\t\tif (d->head_path)\n+\t\t\tpath_src = quote_path(d->head_path, s->prefix, &buf_src);\n+\t}\n+\n+\tif (path_src)\n+\t\tfprintf(s->fp, \"2 %s %s %06o %06o %06o %s %s %c%d %s%c%s%c\",\n+\t\t\t\tkey, submodule_token,\n+\t\t\t\td->mode_head, d->mode_index, d->mode_worktree,\n+\t\t\t\toid_to_hex(&d->oid_head), oid_to_hex(&d->oid_index),\n+\t\t\t\tkey[0], d->score,\n+\t\t\t\tpath_current, sep_char, path_src, eol_char);\n+\telse\n+\t\tfprintf(s->fp, \"1 %s %s %06o %06o %06o %s %s %s%c\",\n+\t\t\t\tkey, submodule_token,\n+\t\t\t\td->mode_head, d->mode_index, d->mode_worktree,\n+\t\t\t\toid_to_hex(&d->oid_head), oid_to_hex(&d->oid_index),\n+\t\t\t\tpath_current, eol_char);\n+\t\n+\tstrbuf_release(&buf_current);\n+\tstrbuf_release(&buf_src);\n+}\n+\n+/*\n+ * Print porcelain v2 status info for unmerged entries.\n+ */\n+static void wt_porcelain_v2_print_unmerged_entry(\n+\tstruct string_list_item *it,\n+\tstruct wt_status *s)\n+{\n+\tstruct wt_status_change_data *d = it->util;\n+\tconst struct cache_entry *ce;\n+\tstruct strbuf buf_current = STRBUF_INIT;\n+\tconst char *path_current = NULL;\n+\tint pos, stage, sum;\n+\tstruct {\n+\t\tint mode;\n+\t\tstruct object_id oid;\n+\t} stages[3];\n+\tchar *key;\n+\tchar submodule_token[5];\n+\tchar unmerged_prefix = 'u';\n+\tchar eol_char = s->null_termination ? '\\0' : '\\n';\n+\n+\twt_porcelain_v2_submodule_state(d, submodule_token);\n+\n+\tswitch (d->stagemask) {\n+\tcase 1: key = \"DD\"; break; /* both deleted */\n+\tcase 2: key = \"AU\"; break; /* added by us */\n+\tcase 3: key = \"UD\"; break; /* deleted by them */\n+\tcase 4: key = \"UA\"; break; /* added by them */\n+\tcase 5: key = \"DU\"; break; /* deleted by us */\n+\tcase 6: key = \"AA\"; break; /* both added */\n+\tcase 7: key = \"UU\"; break; /* both modified */\n+\t}\n+\n+\t/*\n+\t * Disregard d.aux.porcelain_v2 data that we accumulated\n+\t * for the head and index columns during the scans and\n+\t * replace with the actual stage data.\n+\t *\n+\t * Note that this is a last-one-wins for each the individual\n+\t * stage [123] columns in the event of multiple cache entries\n+\t * for same stage.\n+\t */\n+\tmemset(stages, 0, sizeof(stages));\n+\tsum = 0;\n+\tpos = cache_name_pos(it->string, strlen(it->string));\n+\tassert(pos < 0);\n+\tpos = -pos-1;\n+\twhile (pos < active_nr) {\n+\t\tce = active_cache[pos++];\n+\t\tstage = ce_stage(ce);\n+\t\tif (strcmp(ce->name, it->string) || !stage)\n+\t\t\tbreak;\n+\t\tstages[stage - 1].mode = ce->ce_mode;\n+\t\thashcpy(stages[stage - 1].oid.hash, ce->sha1);\n+\t\tsum |= (1 << (stage - 1));\n+\t}\n+\tif (sum != d->stagemask)\n+\t\tdie(\"BUG: observed stagemask 0x%x != expected stagemask 0x%x\", sum, d->stagemask);\n+\n+\tif (s->null_termination)\n+\t\tpath_current = it->string;\n+\telse\n+\t\tpath_current = quote_path(it->string, s->prefix, &buf_current);\n+\n+\tfprintf(s->fp, \"%c %s %s %06o %06o %06o %06o %s %s %s %s%c\",\n+\t\t\tunmerged_prefix, key, submodule_token,\n+\t\t\tstages[0].mode, /* stage 1 */\n+\t\t\tstages[1].mode, /* stage 2 */\n+\t\t\tstages[2].mode, /* stage 3 */\n+\t\t\td->mode_worktree,\n+\t\t\toid_to_hex(&stages[0].oid), /* stage 1 */\n+\t\t\toid_to_hex(&stages[1].oid), /* stage 2 */\n+\t\t\toid_to_hex(&stages[2].oid), /* stage 3 */\n+\t\t\tpath_current,\n+\t\t\teol_char);\n+\n+\tstrbuf_release(&buf_current);\n+}\n+\n+/*\n+ * Print porcelain V2 status info for untracked and ignored entries.\n+ */\n+static void wt_porcelain_v2_print_other(\n+\tstruct string_list_item *it,\n+\tstruct wt_status *s,\n+\tchar prefix)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tconst char *path;\n+\tchar eol_char;\n+\n+\tif (s->null_termination) {\n+\t\tpath = it->string;\n+\t\teol_char = '\\0';\n+\t} else {\n+\t\tpath = quote_path(it->string, s->prefix, &buf);\n+\t\teol_char = '\\n';\n+\t}\n+\n+\tfprintf(s->fp, \"%c %s%c\", prefix, path, eol_char);\n+\n+\tstrbuf_release(&buf);\n+}\n+\n+/*\n+ * Print porcelain V2 status.\n+ *\n+ * [<v2_changed_items>]*\n+ * [<v2_unmerged_items>]*\n+ * [<v2_untracked_items>]*\n+ * [<v2_ignored_items>]*\n+ *\n+ */\n+void wt_porcelain_v2_print(struct wt_status *s)\n+{\n+\tstruct wt_status_change_data *d;\n+\tstruct string_list_item *it;\n+\tint i;\n+\n+\tfor (i = 0; i < s->change.nr; i++) {\n+\t\tit = &(s->change.items[i]);\n+\t\td = it->util;\n+\t\tif (!d->stagemask)\n+\t\t\twt_porcelain_v2_print_changed_entry(it, s);\n+\t}\n+\n+\tfor (i = 0; i < s->change.nr; i++) {\n+\t\tit = &(s->change.items[i]);\n+\t\td = it->util;\n+\t\tif (d->stagemask)\n+\t\t\twt_porcelain_v2_print_unmerged_entry(it, s);\n+\t}\n+\n+\tfor (i = 0; i < s->untracked.nr; i++) {\n+\t\tit = &(s->untracked.items[i]);\n+\t\twt_porcelain_v2_print_other(it, s, '?');\n+\t}\n+\n+\tfor (i = 0; i < s->ignored.nr; i++) {\n+\t\tit = &(s->ignored.items[i]);\n+\t\twt_porcelain_v2_print_other(it, s, '!');\n+\t}\n+}\n+\n void wt_status_print(struct wt_status *s)\n {\n \tswitch (s->status_format) {\n@@ -1823,7 +2104,7 @@ void wt_status_print(struct wt_status *s)\n \t\twt_porcelain_print(s);\n \t\tbreak;\n \tcase STATUS_FORMAT_PORCELAIN_V2:\n-\t\t/* TODO */\n+\t\twt_porcelain_v2_print(s);\n \t\tbreak;\n \tcase STATUS_FORMAT_UNSPECIFIED:\n \t\tdie(\"BUG: finalize_deferred_config() should have been called\");\n-- \n2.8.0.rc4.17.gac42084.dirty\n\n"},{"id":"292835","messageId":"1470147137-17498-8-git-send-email-git@jeffhostetler.com","threadId":"42985","inReplyTo":"1470147137-17498-1-git-send-email-git@jeffhostetler.com","subject":"[PATCH v4 7/8] git-status.txt: describe --porcelain=v2 format","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2016-08-02T14:12:16Z","receivedAt":"2016-08-02T14:16:53Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"From: Jeff Hostetler <jeffhost@microsoft.com>\n\nUpdate status manpage to include information about\nporcelain V2 format.\n\nSigned-off-by: Jeff Hostetler <jeffhost@microsoft.com>\nSigned-off-by: Jeff Hostetler <git@jeffhostetler.com>\n---\n Documentation/git-status.txt | 123 +++++++++++++++++++++++++++++++++++++++++--\n 1 file changed, 119 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/git-status.txt b/Documentation/git-status.txt\nindex 6b1454b..76e7c92 100644\n--- a/Documentation/git-status.txt\n+++ b/Documentation/git-status.txt\n@@ -183,12 +183,12 @@ in which case `XY` are `!!`.\n \n If -b is used the short-format status is preceded by a line\n \n-## branchname tracking info\n+    ## branchname tracking info\n \n-Porcelain Format\n-~~~~~~~~~~~~~~~~\n+Porcelain Format Version 1\n+~~~~~~~~~~~~~~~~~~~~~~~~~~\n \n-The porcelain format is similar to the short format, but is guaranteed\n+Version 1 porcelain format is similar to the short format, but is guaranteed\n not to change in a backwards-incompatible way between Git versions or\n based on user configuration. This makes it ideal for parsing by scripts.\n The description of the short format above also describes the porcelain\n@@ -210,6 +210,121 @@ field from the first filename).  Third, filenames containing special\n characters are not specially formatted; no quoting or\n backslash-escaping is performed.\n \n+Porcelain Format Version 2\n+~~~~~~~~~~~~~~~~~~~~~~~~~~\n+\n+Version 2 format adds more detailed information about the state of\n+the worktree and changed items.  Version 2 also defines an extensible\n+set of easy to parse optional headers.\n+\n+Header lines start with \"#\" and are added in response to specific\n+command line arguments.  Parsers should ignore headers they\n+don't recognize.\n+\n+### Branch Headers\n+\n+If `--branch` is given, a series of header lines are printed with\n+information about the current branch.\n+\n+    Line                                     Notes\n+    ------------------------------------------------------------\n+    # branch.oid <commit> | (initial)        Current commit.\n+    # branch.head <branch> | (detached)      Current branch.\n+    # branch.upstream <upstream_branch>      If upstream is set.\n+    # branch.ab +<ahead> -<behind>           If upstream is set and\n+                                             the commit is present.\n+    ------------------------------------------------------------\n+\n+### Changed Tracked Entries\n+\n+Following the headers, a series of lines are printed for tracked\n+entries.  One of three different line formats may be used to describe\n+an entry depending on the type of change.  Tracked entries are printed\n+in an undefined order; parsers should allow for a mixture of the 3\n+line types in any order.\n+\n+Ordinary changed entries have the following format:\n+\n+    1 <XY> <sub> <mH> <mI> <mW> <hH> <hI> <path>\n+\n+Renamed or copied entries have the following format:\n+\n+    2 <XY> <sub> <mH> <mI> <mW> <hH> <hI> <X><nr> <path><sep><pathSrc>\n+\n+    Field       Meaning\n+    --------------------------------------------------------\n+    <XY>        A 2 character field containing the staged and\n+                unstaged XY values described in the short format,\n+                with unchanged indicated by a \".\" rather than\n+                a space.\n+    <sub>       A 4 character field describing the submodule state.\n+                \"N...\" when the entry is not a submodule.\n+                \"S<c><m><u>\" when the entry is a submodule.\n+                <c> is \"C\" if the commit changed; otherwise \".\".\n+                <m> is \"M\" if it has tracked changes; otherwise \".\".\n+                <u> is \"U\" if there are untracked changes; otherwise \".\".\n+    <mH>        The 6 character octal file mode in the HEAD.\n+    <mI>        The octal file mode in the index.\n+    <mW>        The octal file mode in the worktree.\n+    <hH>        The SHA1 value in the HEAD.\n+    <hI>        The SHA1 value in the index.\n+    <X><nr>     The rename or copied percentage score. For example \"R100\"\n+                or \"C75\".\n+    <path>      The current pathname.\n+    <sep>       When the `-z` option is used, the 2 pathnames are separated\n+                with a NUL (ASCII 0x00) byte; otherwise, a tab (ASCII 0x09)\n+                byte separates them.\n+    <pathSrc>   The original path. This is only present when the entry\n+                has been renamed or copied.\n+    --------------------------------------------------------\n+\n+Unmerged entries have the following format; the first character is\n+a \"u\" to distinguish from ordinary changed entries.\n+\n+    u <xy> <sub> <m1> <m2> <m3> <mW> <h1> <h2> <h3> <path>\n+\n+    Field       Meaning\n+    --------------------------------------------------------\n+    <XY>        A 2 character field describing the conflict type\n+                as described in the short format.\n+    <sub>       A 4 character field describing the submodule state\n+                as described above.\n+    <m1>        The octal file mode for stage 1.\n+    <m2>        The octal file mode for stage 2.\n+    <m3>        The octal file mode for stage 3.\n+    <mW>        The octal file mode in the worktree.\n+    <h1>        The SHA1 value for stage 1.\n+    <h2>        The SHA1 value for stage 2.\n+    <h3>        The SHA1 value for stage 3.\n+    <path>      The current pathname.\n+    --------------------------------------------------------\n+\n+### Other Items\n+\n+Following the tracked entries (and if requested), a series of\n+lines will be printed for untracked and then ignored items\n+found in the worktree.\n+\n+Untracked items have the following format:\n+\n+    ? <path>\n+\n+Ignored items have the following format:\n+\n+    ! <path>\n+\n+### Pathname Format Notes and -z\n+\n+When the `-z` option is given, pathnames are printed as is and\n+without any quoting and lines are terminated with a NUL (ASCII 0x00)\n+byte.\n+\n+Otherwise, all pathnames will be \"C-quoted\" if they contain any tab,\n+linefeed, double quote, or backslash characters. In C-quoting, these\n+characters will be replaced with the corresponding C-style escape\n+sequences and the resulting pathname will be double quoted.\n+\n+\n CONFIGURATION\n -------------\n \n-- \n2.8.0.rc4.17.gac42084.dirty\n\n"},{"id":"292836","messageId":"1470147137-17498-2-git-send-email-git@jeffhostetler.com","threadId":"42985","inReplyTo":"1470147137-17498-1-git-send-email-git@jeffhostetler.com","subject":"[PATCH v4 1/8] status: rename long-format print routines","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2016-08-02T14:12:10Z","receivedAt":"2016-08-02T14:16:58Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"From: Jeff Hostetler <jeffhost@microsoft.com>\n\nRenamed the various wt_status_print*() routines to be\nwt_longstatus_print*() to make it clear that these\nroutines are only concerned with the normal/long\nstatus output.\n\nThis will hopefully reduce confusion as other status\nformats are added in the future.\n\nSigned-off-by: Jeff Hostetler <jeffhost@microsoft.com>\nSigned-off-by: Jeff Hostetler <git@jeffhostetler.com>\n---\n builtin/commit.c |   4 +-\n wt-status.c      | 110 +++++++++++++++++++++++++++----------------------------\n wt-status.h      |   2 +-\n 3 files changed, 58 insertions(+), 58 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 1f6dbcd..b80273b 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -515,7 +515,7 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int\n \t\tbreak;\n \tcase STATUS_FORMAT_NONE:\n \tcase STATUS_FORMAT_LONG:\n-\t\twt_status_print(s);\n+\t\twt_longstatus_print(s);\n \t\tbreak;\n \t}\n \n@@ -1403,7 +1403,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \tcase STATUS_FORMAT_LONG:\n \t\ts.verbose = verbose;\n \t\ts.ignore_submodule_arg = ignore_submodule_arg;\n-\t\twt_status_print(&s);\n+\t\twt_longstatus_print(&s);\n \t\tbreak;\n \t}\n \treturn 0;\ndiff --git a/wt-status.c b/wt-status.c\nindex de62ab2..b9a58fd 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -139,7 +139,7 @@ void wt_status_prepare(struct wt_status *s)\n \ts->display_comment_prefix = 0;\n }\n \n-static void wt_status_print_unmerged_header(struct wt_status *s)\n+static void wt_longstatus_print_unmerged_header(struct wt_status *s)\n {\n \tint i;\n \tint del_mod_conflict = 0;\n@@ -191,7 +191,7 @@ static void wt_status_print_unmerged_header(struct wt_status *s)\n \tstatus_printf_ln(s, c, \"%s\", \"\");\n }\n \n-static void wt_status_print_cached_header(struct wt_status *s)\n+static void wt_longstatus_print_cached_header(struct wt_status *s)\n {\n \tconst char *c = color(WT_STATUS_HEADER, s);\n \n@@ -207,9 +207,9 @@ static void wt_status_print_cached_header(struct wt_status *s)\n \tstatus_printf_ln(s, c, \"%s\", \"\");\n }\n \n-static void wt_status_print_dirty_header(struct wt_status *s,\n-\t\t\t\t\t int has_deleted,\n-\t\t\t\t\t int has_dirty_submodules)\n+static void wt_longstatus_print_dirty_header(struct wt_status *s,\n+\t\t\t\t\t     int has_deleted,\n+\t\t\t\t\t     int has_dirty_submodules)\n {\n \tconst char *c = color(WT_STATUS_HEADER, s);\n \n@@ -226,9 +226,9 @@ static void wt_status_print_dirty_header(struct wt_status *s,\n \tstatus_printf_ln(s, c, \"%s\", \"\");\n }\n \n-static void wt_status_print_other_header(struct wt_status *s,\n-\t\t\t\t\t const char *what,\n-\t\t\t\t\t const char *how)\n+static void wt_longstatus_print_other_header(struct wt_status *s,\n+\t\t\t\t\t     const char *what,\n+\t\t\t\t\t     const char *how)\n {\n \tconst char *c = color(WT_STATUS_HEADER, s);\n \tstatus_printf_ln(s, c, \"%s:\", what);\n@@ -238,7 +238,7 @@ static void wt_status_print_other_header(struct wt_status *s,\n \tstatus_printf_ln(s, c, \"%s\", \"\");\n }\n \n-static void wt_status_print_trailer(struct wt_status *s)\n+static void wt_longstatus_print_trailer(struct wt_status *s)\n {\n \tstatus_printf_ln(s, color(WT_STATUS_HEADER, s), \"%s\", \"\");\n }\n@@ -304,8 +304,8 @@ static int maxwidth(const char *(*label)(int), int minval, int maxval)\n \treturn result;\n }\n \n-static void wt_status_print_unmerged_data(struct wt_status *s,\n-\t\t\t\t\t  struct string_list_item *it)\n+static void wt_longstatus_print_unmerged_data(struct wt_status *s,\n+\t\t\t\t\t      struct string_list_item *it)\n {\n \tconst char *c = color(WT_STATUS_UNMERGED, s);\n \tstruct wt_status_change_data *d = it->util;\n@@ -331,9 +331,9 @@ static void wt_status_print_unmerged_data(struct wt_status *s,\n \tstrbuf_release(&onebuf);\n }\n \n-static void wt_status_print_change_data(struct wt_status *s,\n-\t\t\t\t\tint change_type,\n-\t\t\t\t\tstruct string_list_item *it)\n+static void wt_longstatus_print_change_data(struct wt_status *s,\n+\t\t\t\t\t    int change_type,\n+\t\t\t\t\t    struct string_list_item *it)\n {\n \tstruct wt_status_change_data *d = it->util;\n \tconst char *c = color(change_type, s);\n@@ -378,7 +378,7 @@ static void wt_status_print_change_data(struct wt_status *s,\n \t\tstatus = d->worktree_status;\n \t\tbreak;\n \tdefault:\n-\t\tdie(\"BUG: unhandled change_type %d in wt_status_print_change_data\",\n+\t\tdie(\"BUG: unhandled change_type %d in wt_longstatus_print_change_data\",\n \t\t    change_type);\n \t}\n \n@@ -627,7 +627,7 @@ void wt_status_collect(struct wt_status *s)\n \twt_status_collect_untracked(s);\n }\n \n-static void wt_status_print_unmerged(struct wt_status *s)\n+static void wt_longstatus_print_unmerged(struct wt_status *s)\n {\n \tint shown_header = 0;\n \tint i;\n@@ -640,17 +640,17 @@ static void wt_status_print_unmerged(struct wt_status *s)\n \t\tif (!d->stagemask)\n \t\t\tcontinue;\n \t\tif (!shown_header) {\n-\t\t\twt_status_print_unmerged_header(s);\n+\t\t\twt_longstatus_print_unmerged_header(s);\n \t\t\tshown_header = 1;\n \t\t}\n-\t\twt_status_print_unmerged_data(s, it);\n+\t\twt_longstatus_print_unmerged_data(s, it);\n \t}\n \tif (shown_header)\n-\t\twt_status_print_trailer(s);\n+\t\twt_longstatus_print_trailer(s);\n \n }\n \n-static void wt_status_print_updated(struct wt_status *s)\n+static void wt_longstatus_print_updated(struct wt_status *s)\n {\n \tint shown_header = 0;\n \tint i;\n@@ -664,14 +664,14 @@ static void wt_status_print_updated(struct wt_status *s)\n \t\t    d->index_status == DIFF_STATUS_UNMERGED)\n \t\t\tcontinue;\n \t\tif (!shown_header) {\n-\t\t\twt_status_print_cached_header(s);\n+\t\t\twt_longstatus_print_cached_header(s);\n \t\t\ts->commitable = 1;\n \t\t\tshown_header = 1;\n \t\t}\n-\t\twt_status_print_change_data(s, WT_STATUS_UPDATED, it);\n+\t\twt_longstatus_print_change_data(s, WT_STATUS_UPDATED, it);\n \t}\n \tif (shown_header)\n-\t\twt_status_print_trailer(s);\n+\t\twt_longstatus_print_trailer(s);\n }\n \n /*\n@@ -703,7 +703,7 @@ static int wt_status_check_worktree_changes(struct wt_status *s,\n \treturn changes;\n }\n \n-static void wt_status_print_changed(struct wt_status *s)\n+static void wt_longstatus_print_changed(struct wt_status *s)\n {\n \tint i, dirty_submodules;\n \tint worktree_changes = wt_status_check_worktree_changes(s, &dirty_submodules);\n@@ -711,7 +711,7 @@ static void wt_status_print_changed(struct wt_status *s)\n \tif (!worktree_changes)\n \t\treturn;\n \n-\twt_status_print_dirty_header(s, worktree_changes < 0, dirty_submodules);\n+\twt_longstatus_print_dirty_header(s, worktree_changes < 0, dirty_submodules);\n \n \tfor (i = 0; i < s->change.nr; i++) {\n \t\tstruct wt_status_change_data *d;\n@@ -721,12 +721,12 @@ static void wt_status_print_changed(struct wt_status *s)\n \t\tif (!d->worktree_status ||\n \t\t    d->worktree_status == DIFF_STATUS_UNMERGED)\n \t\t\tcontinue;\n-\t\twt_status_print_change_data(s, WT_STATUS_CHANGED, it);\n+\t\twt_longstatus_print_change_data(s, WT_STATUS_CHANGED, it);\n \t}\n-\twt_status_print_trailer(s);\n+\twt_longstatus_print_trailer(s);\n }\n \n-static void wt_status_print_submodule_summary(struct wt_status *s, int uncommitted)\n+static void wt_longstatus_print_submodule_summary(struct wt_status *s, int uncommitted)\n {\n \tstruct child_process sm_summary = CHILD_PROCESS_INIT;\n \tstruct strbuf cmd_stdout = STRBUF_INIT;\n@@ -772,10 +772,10 @@ static void wt_status_print_submodule_summary(struct wt_status *s, int uncommitt\n \tstrbuf_release(&summary);\n }\n \n-static void wt_status_print_other(struct wt_status *s,\n-\t\t\t\t  struct string_list *l,\n-\t\t\t\t  const char *what,\n-\t\t\t\t  const char *how)\n+static void wt_longstatus_print_other(struct wt_status *s,\n+\t\t\t\t      struct string_list *l,\n+\t\t\t\t      const char *what,\n+\t\t\t\t      const char *how)\n {\n \tint i;\n \tstruct strbuf buf = STRBUF_INIT;\n@@ -785,7 +785,7 @@ static void wt_status_print_other(struct wt_status *s,\n \tif (!l->nr)\n \t\treturn;\n \n-\twt_status_print_other_header(s, what, how);\n+\twt_longstatus_print_other_header(s, what, how);\n \n \tfor (i = 0; i < l->nr; i++) {\n \t\tstruct string_list_item *it;\n@@ -845,7 +845,7 @@ void wt_status_add_cut_line(FILE *fp)\n \tstrbuf_release(&buf);\n }\n \n-static void wt_status_print_verbose(struct wt_status *s)\n+static void wt_longstatus_print_verbose(struct wt_status *s)\n {\n \tstruct rev_info rev;\n \tstruct setup_revision_opt opt;\n@@ -878,7 +878,7 @@ static void wt_status_print_verbose(struct wt_status *s)\n \tif (s->verbose > 1 && s->commitable) {\n \t\t/* print_updated() printed a header, so do we */\n \t\tif (s->fp != stdout)\n-\t\t\twt_status_print_trailer(s);\n+\t\t\twt_longstatus_print_trailer(s);\n \t\tstatus_printf_ln(s, c, _(\"Changes to be committed:\"));\n \t\trev.diffopt.a_prefix = \"c/\";\n \t\trev.diffopt.b_prefix = \"i/\";\n@@ -896,7 +896,7 @@ static void wt_status_print_verbose(struct wt_status *s)\n \t}\n }\n \n-static void wt_status_print_tracking(struct wt_status *s)\n+static void wt_longstatus_print_tracking(struct wt_status *s)\n {\n \tstruct strbuf sb = STRBUF_INIT;\n \tconst char *cp, *ep, *branch_name;\n@@ -959,7 +959,7 @@ static void show_merge_in_progress(struct wt_status *s,\n \t\t\tstatus_printf_ln(s, color,\n \t\t\t\t_(\"  (use \\\"git commit\\\" to conclude merge)\"));\n \t}\n-\twt_status_print_trailer(s);\n+\twt_longstatus_print_trailer(s);\n }\n \n static void show_am_in_progress(struct wt_status *s,\n@@ -980,7 +980,7 @@ static void show_am_in_progress(struct wt_status *s,\n \t\tstatus_printf_ln(s, color,\n \t\t\t_(\"  (use \\\"git am --abort\\\" to restore the original branch)\"));\n \t}\n-\twt_status_print_trailer(s);\n+\twt_longstatus_print_trailer(s);\n }\n \n static char *read_line_from_git_path(const char *filename)\n@@ -1204,7 +1204,7 @@ static void show_rebase_in_progress(struct wt_status *s,\n \t\t\t\t_(\"  (use \\\"git rebase --continue\\\" once you are satisfied with your changes)\"));\n \t\t}\n \t}\n-\twt_status_print_trailer(s);\n+\twt_longstatus_print_trailer(s);\n }\n \n static void show_cherry_pick_in_progress(struct wt_status *s,\n@@ -1223,7 +1223,7 @@ static void show_cherry_pick_in_progress(struct wt_status *s,\n \t\tstatus_printf_ln(s, color,\n \t\t\t_(\"  (use \\\"git cherry-pick --abort\\\" to cancel the cherry-pick operation)\"));\n \t}\n-\twt_status_print_trailer(s);\n+\twt_longstatus_print_trailer(s);\n }\n \n static void show_revert_in_progress(struct wt_status *s,\n@@ -1242,7 +1242,7 @@ static void show_revert_in_progress(struct wt_status *s,\n \t\tstatus_printf_ln(s, color,\n \t\t\t_(\"  (use \\\"git revert --abort\\\" to cancel the revert operation)\"));\n \t}\n-\twt_status_print_trailer(s);\n+\twt_longstatus_print_trailer(s);\n }\n \n static void show_bisect_in_progress(struct wt_status *s,\n@@ -1259,7 +1259,7 @@ static void show_bisect_in_progress(struct wt_status *s,\n \tif (s->hints)\n \t\tstatus_printf_ln(s, color,\n \t\t\t_(\"  (use \\\"git bisect reset\\\" to get back to the original branch)\"));\n-\twt_status_print_trailer(s);\n+\twt_longstatus_print_trailer(s);\n }\n \n /*\n@@ -1429,8 +1429,8 @@ void wt_status_get_state(struct wt_status_state *state,\n \t\twt_status_get_detached_from(state);\n }\n \n-static void wt_status_print_state(struct wt_status *s,\n-\t\t\t\t  struct wt_status_state *state)\n+static void wt_longstatus_print_state(struct wt_status *s,\n+\t\t\t\t      struct wt_status_state *state)\n {\n \tconst char *state_color = color(WT_STATUS_HEADER, s);\n \tif (state->merge_in_progress)\n@@ -1447,7 +1447,7 @@ static void wt_status_print_state(struct wt_status *s,\n \t\tshow_bisect_in_progress(s, state, state_color);\n }\n \n-void wt_status_print(struct wt_status *s)\n+void wt_longstatus_print(struct wt_status *s)\n {\n \tconst char *branch_color = color(WT_STATUS_ONBRANCH, s);\n \tconst char *branch_status_color = color(WT_STATUS_HEADER, s);\n@@ -1484,10 +1484,10 @@ void wt_status_print(struct wt_status *s)\n \t\tstatus_printf_more(s, branch_status_color, \"%s\", on_what);\n \t\tstatus_printf_more(s, branch_color, \"%s\\n\", branch_name);\n \t\tif (!s->is_initial)\n-\t\t\twt_status_print_tracking(s);\n+\t\t\twt_longstatus_print_tracking(s);\n \t}\n \n-\twt_status_print_state(s, &state);\n+\twt_longstatus_print_state(s, &state);\n \tfree(state.branch);\n \tfree(state.onto);\n \tfree(state.detached_from);\n@@ -1498,19 +1498,19 @@ void wt_status_print(struct wt_status *s)\n \t\tstatus_printf_ln(s, color(WT_STATUS_HEADER, s), \"%s\", \"\");\n \t}\n \n-\twt_status_print_updated(s);\n-\twt_status_print_unmerged(s);\n-\twt_status_print_changed(s);\n+\twt_longstatus_print_updated(s);\n+\twt_longstatus_print_unmerged(s);\n+\twt_longstatus_print_changed(s);\n \tif (s->submodule_summary &&\n \t    (!s->ignore_submodule_arg ||\n \t     strcmp(s->ignore_submodule_arg, \"all\"))) {\n-\t\twt_status_print_submodule_summary(s, 0);  /* staged */\n-\t\twt_status_print_submodule_summary(s, 1);  /* unstaged */\n+\t\twt_longstatus_print_submodule_summary(s, 0);  /* staged */\n+\t\twt_longstatus_print_submodule_summary(s, 1);  /* unstaged */\n \t}\n \tif (s->show_untracked_files) {\n-\t\twt_status_print_other(s, &s->untracked, _(\"Untracked files\"), \"add\");\n+\t\twt_longstatus_print_other(s, &s->untracked, _(\"Untracked files\"), \"add\");\n \t\tif (s->show_ignored_files)\n-\t\t\twt_status_print_other(s, &s->ignored, _(\"Ignored files\"), \"add -f\");\n+\t\t\twt_longstatus_print_other(s, &s->ignored, _(\"Ignored files\"), \"add -f\");\n \t\tif (advice_status_u_option && 2000 < s->untracked_in_ms) {\n \t\t\tstatus_printf_ln(s, GIT_COLOR_NORMAL, \"%s\", \"\");\n \t\t\tstatus_printf_ln(s, GIT_COLOR_NORMAL,\n@@ -1525,7 +1525,7 @@ void wt_status_print(struct wt_status *s)\n \t\t\t? _(\" (use -u option to show untracked files)\") : \"\");\n \n \tif (s->verbose)\n-\t\twt_status_print_verbose(s);\n+\t\twt_longstatus_print_verbose(s);\n \tif (!s->commitable) {\n \t\tif (s->amend)\n \t\t\tstatus_printf_ln(s, GIT_COLOR_NORMAL, _(\"No changes\"));\ndiff --git a/wt-status.h b/wt-status.h\nindex 2ca93f6..2023a3c 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -99,7 +99,6 @@ struct wt_status_state {\n void wt_status_truncate_message_at_cut_line(struct strbuf *);\n void wt_status_add_cut_line(FILE *fp);\n void wt_status_prepare(struct wt_status *s);\n-void wt_status_print(struct wt_status *s);\n void wt_status_collect(struct wt_status *s);\n void wt_status_get_state(struct wt_status_state *state, int get_detached_from);\n int wt_status_check_rebase(const struct worktree *wt,\n@@ -107,6 +106,7 @@ int wt_status_check_rebase(const struct worktree *wt,\n int wt_status_check_bisect(const struct worktree *wt,\n \t\t\t   struct wt_status_state *state);\n \n+void wt_longstatus_print(struct wt_status *s);\n void wt_shortstatus_print(struct wt_status *s);\n void wt_porcelain_print(struct wt_status *s);\n \n-- \n2.8.0.rc4.17.gac42084.dirty\n\n"},{"id":"292837","messageId":"1470147137-17498-5-git-send-email-git@jeffhostetler.com","threadId":"42985","inReplyTo":"1470147137-17498-1-git-send-email-git@jeffhostetler.com","subject":"[PATCH v4 4/8] status: per-file data collection for --porcelain=v2","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2016-08-02T14:12:13Z","receivedAt":"2016-08-02T14:17:03Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"From: Jeff Hostetler <jeffhost@microsoft.com>\n\nThe output of `git status --porcelain` leaves out many details\nabout the current status that clients might like to have.  This\ncan force them to be less efficient as they may need to launch\nsecondary commands (and try to match the logic within git) to\naccumulate this extra information.  For example, a GUI IDE might\nwant the file mode to display the correct icon for a changed\nitem (without having to stat it afterwards).\n\nSigned-off-by: Jeff Hostetler <jeffhost@microsoft.com>\nSigned-off-by: Jeff Hostetler <git@jeffhostetler.com>\n---\n builtin/commit.c |  3 +++\n wt-status.c      | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-\n wt-status.h      |  4 ++++\n 3 files changed, 69 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex c3ae2c3..93ce28c 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -153,6 +153,8 @@ static int opt_parse_porcelain(const struct option *opt, const char *arg, int un\n \t\t*value = STATUS_FORMAT_PORCELAIN;\n \telse if (!strcmp(arg, \"v1\") || !strcmp(arg,\"1\"))\n \t\t*value = STATUS_FORMAT_PORCELAIN;\n+\telse if (!strcmp(arg, \"v2\") || !strcmp(arg, \"2\"))\n+\t\t*value = STATUS_FORMAT_PORCELAIN_V2;\n \telse\n \t\tdie(\"unsupported porcelain version '%s'\", arg);\n \n@@ -1104,6 +1106,7 @@ static struct status_deferred_config {\n static void finalize_deferred_config(struct wt_status *s)\n {\n \tint use_deferred_config = (status_format != STATUS_FORMAT_PORCELAIN &&\n+\t\t\t\t   status_format != STATUS_FORMAT_PORCELAIN_V2 &&\n \t\t\t\t   !s->null_termination);\n \n \tif (s->null_termination) {\ndiff --git a/wt-status.c b/wt-status.c\nindex a9031e4..15d3349 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -434,6 +434,31 @@ static void wt_status_collect_changed_cb(struct diff_queue_struct *q,\n \t\tif (S_ISGITLINK(p->two->mode))\n \t\t\td->new_submodule_commits = !!oidcmp(&p->one->oid,\n \t\t\t\t\t\t\t    &p->two->oid);\n+\n+\t\tswitch (p->status) {\n+\t\tcase DIFF_STATUS_ADDED:\n+\t\t\tdie(\"BUG: worktree status add???\");\n+\t\t\tbreak;\n+\n+\t\tcase DIFF_STATUS_DELETED:\n+\t\t\td->mode_index = p->one->mode;\n+\t\t\toidcpy(&d->oid_index, &p->one->oid);\n+\t\t\t/* mode_worktree is zero for a delete. */\n+\t\t\tbreak;\n+\n+\t\tcase DIFF_STATUS_MODIFIED:\n+\t\tcase DIFF_STATUS_TYPE_CHANGED:\n+\t\tcase DIFF_STATUS_UNMERGED:\n+\t\t\td->mode_index = p->one->mode;\n+\t\t\td->mode_worktree = p->two->mode;\n+\t\t\toidcpy(&d->oid_index, &p->one->oid);\n+\t\t\tbreak;\n+\n+\t\tcase DIFF_STATUS_UNKNOWN:\n+\t\t\tdie(\"BUG: worktree status unknown???\");\n+\t\t\tbreak;\n+\t\t}\n+\n \t}\n }\n \n@@ -479,12 +504,36 @@ static void wt_status_collect_updated_cb(struct diff_queue_struct *q,\n \t\tif (!d->index_status)\n \t\t\td->index_status = p->status;\n \t\tswitch (p->status) {\n+\t\tcase DIFF_STATUS_ADDED:\n+\t\t\t/* Leave {mode,oid}_head zero for an add. */\n+\t\t\td->mode_index = p->two->mode;\n+\t\t\toidcpy(&d->oid_index, &p->two->oid);\n+\t\t\tbreak;\n+\t\tcase DIFF_STATUS_DELETED:\n+\t\t\td->mode_head = p->one->mode;\n+\t\t\toidcpy(&d->oid_head, &p->one->oid);\n+\t\t\t/* Leave {mode,oid}_index zero for a delete. */\n+\t\t\tbreak;\n+\t\t\t\n \t\tcase DIFF_STATUS_COPIED:\n \t\tcase DIFF_STATUS_RENAMED:\n \t\t\td->head_path = xstrdup(p->one->path);\n+\t\t\td->score = p->score * 100 / MAX_SCORE;\n+\t\t\t/* fallthru */\n+\t\tcase DIFF_STATUS_MODIFIED:\n+\t\tcase DIFF_STATUS_TYPE_CHANGED:\n+\t\t\td->mode_head = p->one->mode;\n+\t\t\td->mode_index = p->two->mode;\n+\t\t\toidcpy(&d->oid_head, &p->one->oid);\n+\t\t\toidcpy(&d->oid_index, &p->two->oid);\n \t\t\tbreak;\n \t\tcase DIFF_STATUS_UNMERGED:\n \t\t\td->stagemask = unmerged_mask(p->two->path);\n+\t\t\t/*\n+\t\t\t * Don't bother setting {mode,oid}_{head,index} since the print\n+\t\t\t * code will output the stage values directly and not use the\n+\t\t\t * values in these fields.\n+\t\t\t */\n \t\t\tbreak;\n \t\t}\n \t}\n@@ -565,9 +614,18 @@ static void wt_status_collect_changes_initial(struct wt_status *s)\n \t\tif (ce_stage(ce)) {\n \t\t\td->index_status = DIFF_STATUS_UNMERGED;\n \t\t\td->stagemask |= (1 << (ce_stage(ce) - 1));\n+\t\t\t/*\n+\t\t\t * Don't bother setting {mode,oid}_{head,index} since the print\n+\t\t\t * code will output the stage values directly and not use the\n+\t\t\t * values in these fields.\n+\t\t\t */\n \t\t}\n-\t\telse\n+\t\telse {\n \t\t\td->index_status = DIFF_STATUS_ADDED;\n+\t\t\t/* Leave {mode,oid}_head zero for adds. */\n+\t\t\td->mode_index = ce->ce_mode;\n+\t\t\thashcpy(d->oid_index.hash, ce->sha1);\n+\t\t}\n \t}\n }\n \n@@ -1764,6 +1822,9 @@ void wt_status_print(struct wt_status *s)\n \tcase STATUS_FORMAT_PORCELAIN:\n \t\twt_porcelain_print(s);\n \t\tbreak;\n+\tcase STATUS_FORMAT_PORCELAIN_V2:\n+\t\t/* TODO */\n+\t\tbreak;\n \tcase STATUS_FORMAT_UNSPECIFIED:\n \t\tdie(\"BUG: finalize_deferred_config() should have been called\");\n \t\tbreak;\ndiff --git a/wt-status.h b/wt-status.h\nindex a859a12..89a6d43 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -38,6 +38,9 @@ struct wt_status_change_data {\n \tint worktree_status;\n \tint index_status;\n \tint stagemask;\n+\tint score;\n+\tint mode_head, mode_index, mode_worktree;\n+\tstruct object_id oid_head, oid_index;\n \tchar *head_path;\n \tunsigned dirty_submodule       : 2;\n \tunsigned new_submodule_commits : 1;\n@@ -48,6 +51,7 @@ struct wt_status_change_data {\n \tSTATUS_FORMAT_LONG,\n \tSTATUS_FORMAT_SHORT,\n \tSTATUS_FORMAT_PORCELAIN,\n+\tSTATUS_FORMAT_PORCELAIN_V2,\n \n \tSTATUS_FORMAT_UNSPECIFIED\n  };\n-- \n2.8.0.rc4.17.gac42084.dirty\n\n"},{"id":"292901","messageId":"CAPc5daVuD7K7zKEG4yHTjBU77rUbdVcbSeBgX9eAJVMWMmxC2w@mail.gmail.com","threadId":"42985","inReplyTo":"alpine.DEB.2.20.1608031708560.107993@virtualbox","subject":"Re: [PATCH v4 0/8] status: V2 porcelain status","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-08-03T15:23:16Z","receivedAt":"2016-08-03T17:04:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"On Wed, Aug 3, 2016 at 8:09 AM, Johannes Schindelin\n<Johannes.Schindelin@gmx.de> wrote:\n> Hi Junio,\n>\n> Any word when it will be included in `pu`, at least?\n\nI've been waiting to see that the amount and quality of\ncomments from others indicate that the series passed\nthe phase that goes through frequent rerolls. Having a\nserious review in the thread that demonstrates and\nconcludes that it is well designed, well implemented,\nand ready to go would help, of course.\n"},{"id":"292910","messageId":"alpine.DEB.2.20.1608031708560.107993@virtualbox","threadId":"42985","inReplyTo":"1470147137-17498-1-git-send-email-git@jeffhostetler.com","subject":"Re: [PATCH v4 0/8] status: V2 porcelain status","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-03T15:09:53Z","receivedAt":"2016-08-03T17:04:55Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Tue, 2 Aug 2016, Jeff Hostetler wrote:\n\n> This patch series adds porcelain V2 format to status.\n> This provides detailed information about file changes\n> and about the current branch.\n> \n> The new output is accessed via:\n>     git status --porcelain=v2 [--branch]\n\nI was wondering... this patch series is at v4 already, and I really need\nit pretty urgently in Git for Windows.\n\nAny word when it will be included in `pu`, at least?\n\nCiao,\nDscho\n"},{"id":"292914","messageId":"alpine.DEB.2.20.1608031808130.107993@virtualbox","threadId":"42985","inReplyTo":"CAPc5daVuD7K7zKEG4yHTjBU77rUbdVcbSeBgX9eAJVMWMmxC2w@mail.gmail.com","subject":"Re: [PATCH v4 0/8] status: V2 porcelain status","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-03T16:10:52Z","receivedAt":"2016-08-03T17:04:59Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 3 Aug 2016, Junio C Hamano wrote:\n\n> On Wed, Aug 3, 2016 at 8:09 AM, Johannes Schindelin\n> <Johannes.Schindelin@gmx.de> wrote:\n>\n> > Any word when it will be included in `pu`, at least?\n> \n> I've been waiting to see that the amount and quality of\n> comments from others indicate that the series passed\n> the phase that goes through frequent rerolls. Having a\n> serious review in the thread that demonstrates and\n> concludes that it is well designed, well implemented,\n> and ready to go would help, of course.\n\nOh, I thought I had stated clearly already that what with Jeff being my\ncolleague and working on a feature I have a lot of interest in, I had\nreviewed this patch series even before it was submitted to the Git mailing\nlist.\n\nI would have been fine with the first iteration. And I had nothing to add\nto the subsequent iterations, they are just as fine for me. I think the\npatch series is ready to go.\n\nThe prospect of accelerating the Git prompt is also enticing.\n\nCiao,\nDscho\n"},{"id":"292942","messageId":"xmqq7fbxaiw8.fsf@gitster.mtv.corp.google.com","threadId":"42985","inReplyTo":"alpine.DEB.2.20.1608031808130.107993@virtualbox","subject":"Re: [PATCH v4 0/8] status: V2 porcelain status","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-08-03T16:59:03Z","receivedAt":"2016-08-03T17:05:35Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> On Wed, 3 Aug 2016, Junio C Hamano wrote:\n>\n>> On Wed, Aug 3, 2016 at 8:09 AM, Johannes Schindelin\n>> <Johannes.Schindelin@gmx.de> wrote:\n>>\n>> > Any word when it will be included in `pu`, at least?\n>> \n>> I've been waiting to see that the amount and quality of\n>> comments from others indicate that the series passed\n>> the phase that goes through frequent rerolls. Having a\n>> serious review in the thread that demonstrates and\n>> concludes that it is well designed, well implemented,\n>> and ready to go would help, of course.\n>\n> Oh, I thought I had stated clearly already that what with Jeff being my\n> colleague and working on a feature I have a lot of interest in, I had\n> reviewed this patch series even before it was submitted to the Git mailing\n> list.\n\n\nWhat I meant by a \"serious review\" is a bit more than \"I reviewed\nwithout involving the list, and you know me well enough, trust me\".\n\nEven if a reviewer does not see any more need to change the patch,\nperhaps because it is perfect after internal reviews, a reviewer can\nstill point out positive things, e.g. what is done well compared to\nother possible approaches and in what way the presented patch is\nbetter than possible alternatives.  See my response to your import-tars\nupdate for an example.\n\nThanks.\n"},{"id":"293001","messageId":"xmqqvazh7dam.fsf@gitster.mtv.corp.google.com","threadId":"42985","inReplyTo":"1470147137-17498-2-git-send-email-git@jeffhostetler.com","subject":"Re: [PATCH v4 1/8] status: rename long-format print routines","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-08-03T21:28:17Z","receivedAt":"2016-08-03T21:35:22Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff Hostetler <git@jeffhostetler.com> writes:\n\n> From: Jeff Hostetler <jeffhost@microsoft.com>\n>\n> Renamed the various wt_status_print*() routines to be\n> wt_longstatus_print*() to make it clear that these\n> routines are only concerned with the normal/long\n> status output.\n\nWe tend to write \"Rename the various...\", as if you the patch author\nis giving an order to \"become so\" to the codebase, or \"make it so\"\nto the maintainer.\n\nOther than that, this step looks straight-forward, and I agree with\nthe hope that this will reduce confusion that readers may have while\nreading a future version of this code.\n\n> This will hopefully reduce confusion as other status\n> formats are added in the future.\n>\n> Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>\n> Signed-off-by: Jeff Hostetler <git@jeffhostetler.com>\n\nHmm, are these physically the same people?  If so, which one do you\nwant to be known as?\n\nThanks.\n"},{"id":"293002","messageId":"xmqqr3a57cxm.fsf@gitster.mtv.corp.google.com","threadId":"42985","inReplyTo":"1470147137-17498-3-git-send-email-git@jeffhostetler.com","subject":"Re: [PATCH v4 2/8] status: cleanup API to wt_status_print","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-08-03T21:36:05Z","receivedAt":"2016-08-03T21:36:14Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff Hostetler <git@jeffhostetler.com> writes:\n\n> From: Jeff Hostetler <jeffhost@microsoft.com>\n>\n> Refactor the API between builtin/commit.c and wt-status.[ch].\n> Hide details of the various wt_*status_print() routines inside\n> wt-status.c behind a single (new) wt_status_print() routine\n> and eliminate the switch statements from builtin/commit.c\n>\n> This will allow us to more easily add new status formats\n> and isolate that within wt-status.c\n>\n> Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>\n> Signed-off-by: Jeff Hostetler <git@jeffhostetler.com>\n\nAgain, are these the same person?  I won't repeat this for the\nremainder of the series.\n\n> diff --git a/wt-status.h b/wt-status.h\n> index 2023a3c..a859a12 100644\n> --- a/wt-status.h\n> +++ b/wt-status.h\n> @@ -43,6 +43,15 @@ struct wt_status_change_data {\n>  \tunsigned new_submodule_commits : 1;\n>  };\n>  \n> + enum wt_status_format {\n> +\tSTATUS_FORMAT_NONE = 0,\n> +\tSTATUS_FORMAT_LONG,\n> +\tSTATUS_FORMAT_SHORT,\n> +\tSTATUS_FORMAT_PORCELAIN,\n> +\n> +\tSTATUS_FORMAT_UNSPECIFIED\n> + };\n\nIs it your editor, or is it your MUA?  This definition is indented\nby one SP, which is funny.\n\nAlso throughout the series, I saw a handful of blank lines that\nshould be empty but are not (e.g. three tabs and nothing else on a\nnew line).  I've fixed them up all but I won't be sending an\ninterdiff just for them, so please make sure they won't resurface\nwhen/if you reroll.\n\n>  struct wt_status {\n>  \tint is_initial;\n>  \tchar *branch;\n> @@ -66,6 +75,8 @@ struct wt_status {\n>  \tint show_branch;\n>  \tint hints;\n>  \n> +\tenum wt_status_format status_format;\n> +\n>  \t/* These are computed during processing of the individual sections */\n>  \tint commitable;\n>  \tint workdir_dirty;\n> @@ -99,6 +110,7 @@ struct wt_status_state {\n>  void wt_status_truncate_message_at_cut_line(struct strbuf *);\n>  void wt_status_add_cut_line(FILE *fp);\n>  void wt_status_prepare(struct wt_status *s);\n> +void wt_status_print(struct wt_status *s);\n>  void wt_status_collect(struct wt_status *s);\n>  void wt_status_get_state(struct wt_status_state *state, int get_detached_from);\n>  int wt_status_check_rebase(const struct worktree *wt,\n> @@ -106,10 +118,6 @@ int wt_status_check_rebase(const struct worktree *wt,\n>  int wt_status_check_bisect(const struct worktree *wt,\n>  \t\t\t   struct wt_status_state *state);\n>  \n> -void wt_longstatus_print(struct wt_status *s);\n> -void wt_shortstatus_print(struct wt_status *s);\n> -void wt_porcelain_print(struct wt_status *s);\n> -\n\nI think I agreed during the previous review round that shifting the\ndivision of labor boundary between the command and wt-status.[ch]\nthis way, to have the latter do more printing, is a good idea, and I\nstill do.\n\nThanks.\n"},{"id":"293009","messageId":"xmqqmvkt7cv9.fsf@gitster.mtv.corp.google.com","threadId":"42985","inReplyTo":"1470147137-17498-4-git-send-email-git@jeffhostetler.com","subject":"Re: [PATCH v4 3/8] status: support --porcelain[=<version>]","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-08-03T21:37:30Z","receivedAt":"2016-08-03T21:56:40Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff Hostetler <git@jeffhostetler.com> writes:\n\n> +\telse if (!strcmp(arg, \"v1\") || !strcmp(arg,\"1\"))\n\nMissing SP between arg, and \"1\" here (FYI, editing it here to fix\nwould affect a later patch that has this line in the context).\n\nOther than that looking good so far.\n"},{"id":"293078","messageId":"57A33582.8020507@jeffhostetler.com","threadId":"42985","inReplyTo":"xmqqvazh7dam.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v4 1/8] status: rename long-format print routines","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2016-08-04T12:30:58Z","receivedAt":"2016-08-04T12:33:18Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"\n\nOn 08/03/2016 05:28 PM, Junio C Hamano wrote:\n>>\n>> Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>\n>> Signed-off-by: Jeff Hostetler <git@jeffhostetler.com>\n>\n> Hmm, are these physically the same people?  If so, which one do you\n> want to be known as?\n\nYes, these are both my addresses.  Still struggling a little\nwith some SMTP issues.  Please use the jeffhost@microsoft.com address.\n\nThanks.\nJeff\n\n\n"},{"id":"293079","messageId":"57A33682.9080305@jeffhostetler.com","threadId":"42985","inReplyTo":"xmqqr3a57cxm.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v4 2/8] status: cleanup API to wt_status_print","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2016-08-04T12:35:14Z","receivedAt":"2016-08-04T12:37:25Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"\n\nOn 08/03/2016 05:36 PM, Junio C Hamano wrote:\n> Jeff Hostetler <git@jeffhostetler.com> writes:\n>\n>> From: Jeff Hostetler <jeffhost@microsoft.com>\n>> diff --git a/wt-status.h b/wt-status.h\n>> index 2023a3c..a859a12 100644\n>> --- a/wt-status.h\n>> +++ b/wt-status.h\n>> @@ -43,6 +43,15 @@ struct wt_status_change_data {\n>>   \tunsigned new_submodule_commits : 1;\n>>   };\n>>\n>> + enum wt_status_format {\n>> +\tSTATUS_FORMAT_NONE = 0,\n>> +\tSTATUS_FORMAT_LONG,\n>> +\tSTATUS_FORMAT_SHORT,\n>> +\tSTATUS_FORMAT_PORCELAIN,\n>> +\n>> +\tSTATUS_FORMAT_UNSPECIFIED\n>> + };\n>\n> Is it your editor, or is it your MUA?  This definition is indented\n> by one SP, which is funny.\n>\n> Also throughout the series, I saw a handful of blank lines that\n> should be empty but are not (e.g. three tabs and nothing else on a\n> new line).  I've fixed them up all but I won't be sending an\n> interdiff just for them, so please make sure they won't resurface\n> when/if you reroll.\n\nThat's odd.  I'll double check everything and trim\nthem in case I need to resubmit this.  Sorry.\n\nJeff\n"},{"id":"293208","messageId":"xmqqd1lnxi99.fsf@gitster.mtv.corp.google.com","threadId":"42985","inReplyTo":"1470147137-17498-7-git-send-email-git@jeffhostetler.com","subject":"Re: [PATCH v4 6/8] status: print branch info with --porcelain=v2 --branch","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-08-05T17:01:06Z","receivedAt":"2016-08-05T17:01:15Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff Hostetler <git@jeffhostetler.com> writes:\n\n>  /*\n> + * Print branch information for porcelain v2 output.  These lines\n> + * are printed when the '--branch' parameter is given.\n> + *\n> + *    # branch.oid <commit><eol>\n> + *    # branch.head <head><eol>\n\nJust bikeshedding, but ...\n\n> +\tif (!s->branch)\n> +\t\tfprintf(s->fp, \"# branch.head %s%c\", \"(unknown)\", eol);\n> +\telse {\n> +\t\tif (!strcmp(s->branch, \"HEAD\")) {\n> +\t\t\tfprintf(s->fp, \"# branch.head %s%c\", \"(detached)\", eol);\n> +\n> +\t\t\tif (state.rebase_in_progress || state.rebase_interactive_in_progress)\n> +\t\t\t\tbranch_name = state.onto;\n> +\t\t\telse if (state.detached_from)\n> +\t\t\t\tbranch_name = state.detached_from;\n> +\t\t\telse\n> +\t\t\t\tbranch_name = \"\";\n> +\t\t} else {\n> +\t\t\tbranch_name = NULL;\n> +\t\t\tskip_prefix(s->branch, \"refs/heads/\", &branch_name);\n> +\n> +\t\t\tfprintf(s->fp, \"# branch.head %s%c\", branch_name, eol);\n\n... given that we are showing branch name, perhaps \"branch.name\"\ninstead of \"branch.head\" is more appropriate?\n\nI wondered if \"# \" prefix before these lines is useful, by the way,\nand initially thought that the fact that these lines begin with\n\"branch.\" and not with the \"1/2/u $key\" sufficient clue for whoever\nreads them, but the reader can tell which kind of record it is by\nreading the first two characters of each line (i.e. if \"# \" that is\nnot the usual \"change info for a single file\"), so it is actually a\ngood idea.\n\nThanks.\n"},{"id":"293211","messageId":"xmqq60rfxfyl.fsf@gitster.mtv.corp.google.com","threadId":"42985","inReplyTo":"1470147137-17498-8-git-send-email-git@jeffhostetler.com","subject":"Re: [PATCH v4 7/8] git-status.txt: describe --porcelain=v2 format","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-08-05T17:50:42Z","receivedAt":"2016-08-05T17:50:54Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff Hostetler <git@jeffhostetler.com> writes:\n\n> +Porcelain Format Version 2\n> +~~~~~~~~~~~~~~~~~~~~~~~~~~\n> +\n> +Version 2 format adds more detailed information about the state of\n> +the worktree and changed items.  Version 2 also defines an extensible\n> +set of easy to parse optional headers.\n> +\n> +Header lines start with \"#\" and are added in response to specific\n> +command line arguments.  Parsers should ignore headers they\n> +don't recognize.\n> +\n> +### Branch Headers\n> +\n> +If `--branch` is given, a series of header lines are printed with\n> +information about the current branch.\n> +\n> +    Line                                     Notes\n> +    ------------------------------------------------------------\n> +    # branch.oid <commit> | (initial)        Current commit.\n> +    # branch.head <branch> | (detached)      Current branch.\n\nIn an earlier review I made a noise about \"branch.head\", but reading\nthis together with others in a context, \"branch.head\" is good and I\ndo not see a need to bikeshed it into \"branch.name\".\n\n> +    # branch.upstream <upstream_branch>      If upstream is set.\n> +    # branch.ab +<ahead> -<behind>           If upstream is set and\n> +                                             the commit is present.\n> +    ------------------------------------------------------------\n> +\n> +### Changed Tracked Entries\n> +\n> +Following the headers, a series of lines are printed for tracked\n> +entries.  One of three different line formats may be used to describe\n> +an entry depending on the type of change.  Tracked entries are printed\n> +in an undefined order; parsers should allow for a mixture of the 3\n> +line types in any order.\n> +\n> +Ordinary changed entries have the following format:\n> +\n> +    1 <XY> <sub> <mH> <mI> <mW> <hH> <hI> <path>\n> +\n> +Renamed or copied entries have the following format:\n> +\n> +    2 <XY> <sub> <mH> <mI> <mW> <hH> <hI> <X><nr> <path><sep><pathSrc>\n> +\n> +    Field       Meaning\n> +    --------------------------------------------------------\n> +    <XY>        A 2 character field containing the staged and\n> +                unstaged XY values described in the short format,\n> +                with unchanged indicated by a \".\" rather than\n> +                a space.\n> +    <sub>       A 4 character field describing the submodule state.\n> +                \"N...\" when the entry is not a submodule.\n> +                \"S<c><m><u>\" when the entry is a submodule.\n> +                <c> is \"C\" if the commit changed; otherwise \".\".\n> +                <m> is \"M\" if it has tracked changes; otherwise \".\".\n> +                <u> is \"U\" if there are untracked changes; otherwise \".\".\n> +    <mH>        The 6 character octal file mode in the HEAD.\n\nWe do want \"octal\" to be spelled out, but I doubt that we want to\nguarantee to the reading scripts that this will always be 6\ncharacters.\n\n> +    <mI>        The octal file mode in the index.\n> +    <mW>        The octal file mode in the worktree.\n> +    <hH>        The SHA1 value in the HEAD.\n> +    <hI>        The SHA1 value in the index.\n\nPlease avoid inventing a word \"SHA1 value\" that does not appear in\nDocumentation/glossary-content.txt; s/SHA1 value/object name/g.\nThis comment applies to the desciption for \"U\" lines below, too.\n\n> +    <X><nr>     The rename or copied percentage score. For example \"R100\"\n> +                or \"C75\".\n\nDocumentation/diff-format.txt seems to call this differently.\n\n\tThe rename or copy \"score\" number, e.g. \"R100\", \"C75\".\n\nperhaps?\n\n> +    <path>      The current pathname.\n\nUnless you are talking about \"2\" line, saying \"current\" is somewhat\nweird.  I _think_ <path> is the name in the index and in the working\ntree, as opposed to the original path in the HEAD, but the\ndistinction does not matter for \"1\" or \"U\" lines.\n\nAs this table is meant to be shared between \"1\" and \"2\", perhaps\n\n\t<path>     The pathname.  In a renamed/copied entry, this\n\t\t   is the path in the index and in the working tree.\n\t<origPath> The pathname in the commit at HEAD.  This is only\n                   present in a renamed/copied entry, and tells\n                   where the renamed/copied contents came from.\n\nor something like that may clarify it a bit better.  Then in the\ntable for \"U\" lines, we can just say \"The pathname\".\n\n> +    <sep>       When the `-z` option is used, the 2 pathnames are separated\n> +                with a NUL (ASCII 0x00) byte; otherwise, a tab (ASCII 0x09)\n> +                byte separates them.\n> +    <pathSrc>   The original path. This is only present when the entry\n> +                has been renamed or copied.\n\nIf it is explained as \"the original path\", wouldn't it be easier to\nread if it were called \"<origPath>\" instead of \"<pathSrc>\"?\n\n> +When the `-z` option is given, pathnames are printed as is and\n> +without any quoting and lines are terminated with a NUL (ASCII 0x00)\n> +byte.\n> +\n> +Otherwise, all pathnames will be \"C-quoted\" if they contain any tab,\n> +linefeed, double quote, or backslash characters. In C-quoting, these\n> +characters will be replaced with the corresponding C-style escape\n> +sequences and the resulting pathname will be double quoted.\n\nLooks good.\n\nI think I saw \"C-Quoted\" elsewhere (perhaps in a in-code comment),\nwhich should be spelled consistently i.e. \"C-quoted\".\n\nThanks.\n"},{"id":"293213","messageId":"xmqq1t23xeyb.fsf@gitster.mtv.corp.google.com","threadId":"42985","inReplyTo":"1470147137-17498-9-git-send-email-git@jeffhostetler.com","subject":"Re: [PATCH v4 8/8] status: tests for --porcelain=v2","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-08-05T18:12:28Z","receivedAt":"2016-08-05T18:12:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff Hostetler <git@jeffhostetler.com> writes:\n\n> +##################################################################\n> +## Confirm output prior to initial commit.\n> +##################################################################\n> +\n> +test_expect_success pre_initial_commit_0 '\n\nBikeshedding, but our codebase seems to prefer \"expect\" vs \"actual\".\n\n    $ git grep -e 'test_cmp expect ' t/ | wc -l\n    1882\n    $ git grep -e 'test_cmp expected ' t/ | wc -l\n    888\n\n> +\tcat >expected <<-EOF &&\n> +\t# branch.oid (initial)\n> +\t# branch.head master\n> +\t? actual\n> +\t? dir1/\n> +\t? expected\n> +\t? file_x\n> +\t? file_y\n> +\t? file_z\n> +\tEOF\n\nPerhaps throw these two entries to .gitignore to allow new tests in\nthe future could also use expect.1 vs actual.1 and somesuch?\n\n\tcat >.gitignore <<-\\EOF &&\n\texpect*\n        actual*\n        EOF\n\n> +test_expect_success pre_initial_commit_1 '\n> +\tgit add file_x file_y file_z dir1 &&\n> +\tSHA_A=`git hash-object -t blob -- dir1/file_a` &&\n> +\tSHA_B=`git hash-object -t blob -- dir1/file_b` &&\n> +\tSHA_X=`git hash-object -t blob -- file_x` &&\n> +\tSHA_Y=`git hash-object -t blob -- file_y` &&\n> +\tSHA_Z=`git hash-object -t blob -- file_z` &&\n\nPlease use $(commannd) instead of `command`.  Also \"SHA\" is probably\na bad prefix; either use \"SHA_1\" to be technically correct, or\nbetter yet use \"OID\", as we are moving towards abstracting the exact\nhash function name away.\n\n> +\tSHA_ZERO=0000000000000000000000000000000000000000 &&\n\nI think we made $_z40 available to you from t/test-lib.sh.\n\n> +## Try -z on the above\n> +test_expect_success pre_initial_commit_2 '\n> +\tcat >expected.lf <<-EOF &&\n> +\t# branch.oid (initial)\n> +\t# branch.head master\n> +\t1 A. N... 000000 100644 100644 $SHA_ZERO $SHA_A dir1/file_a\n> +\t1 A. N... 000000 100644 100644 $SHA_ZERO $SHA_B dir1/file_b\n> +\t1 A. N... 000000 100644 100644 $SHA_ZERO $SHA_X file_x\n> +\t1 A. N... 000000 100644 100644 $SHA_ZERO $SHA_Y file_y\n> +\t1 A. N... 000000 100644 100644 $SHA_ZERO $SHA_Z file_z\n> +\t? actual\n> +\t? expected\n> +\tEOF\n> +\tperl -pe y/\\\\012/\\\\000/ <expected.lf >expected &&\n> +\trm expected.lf &&\n\nAs you immediately remove expected.lf, the first \"cat\" process is\nrather pointless.  You can redirect here text <<-EOF directly into\nperl instead.  Also it would probably help to add a new helper\n\"lf_to_nul\" in t/test-lib-functions.sh around the place where\nnul_to_q, ..., tz_to_tab_space helpers are defined, which would\nallow us to say\n\n\tlf_to_nul >expect <<-EOF &&\n\t...\n        EOF\n\n> +test_expect_success initial_commit_3 '\n> +\tgit mv file_y renamed_y &&\n> +\tH0=`git rev-parse HEAD` &&\n> +\n> +\tcat >expected.q <<-EOF &&\n> +\t# branch.oid $H0\n> +\t# branch.head master\n> +\t1 M. N... 100644 100644 100644 $SHA_X $SHA_X1 file_x\n> +\t1 D. N... 100644 000000 000000 $SHA_Z $SHA_ZERO file_z\n> +\t2 R. N... 100644 100644 100644 $SHA_Y $SHA_Y R100 renamed_yQfile_y\n> +\t? actual\n> +\t? expected\n> +\tEOF\n> +\tq_to_tab <expected.q >expected &&\n> +\trm expected.q &&\n\nThe same comment applies (redirect directly into q_to_tab).\n\n> +##################################################################\n> +## Ignore a file\n> +##################################################################\n> +\n> +test_expect_success ignore_file_0 '\n> +\techo x.ign >.gitignore &&\n> +\techo \"ignore me\" >x.ign &&\n> +\tH1=`git rev-parse HEAD` &&\n> +\n> +\tcat >expected <<-EOF &&\n> +\t# branch.oid $H1\n> +\t# branch.head master\n> +\t? .gitignore\n> +\t? actual\n> +\t? expected\n> +\t! x.ign\n> +\tEOF\n> +\n> +\tgit status --porcelain=v2 --branch --ignored --untracked-files=all >actual &&\n> +\trm x.ign &&\n> +\trm .gitignore &&\n> +\ttest_cmp expected actual\n> +'\n\nYou do not seem to be checking a feature is not triggered when not\nasked throughout this test, e.g. making sure the output does not\nhave the \"# branch.*\" lines when --branch is not given, \"! x.ign\"\nis not shown when --ignored is not given, etc.\n\n> +##################################################################\n> +## Test upstream fields in branch header\n> +##################################################################\n> +\n> +test_expect_success 'upstream_fields_0' '\n> +\tgit checkout master &&\n> +\tgit clone . sub_repo &&\n> +\t(\n> +\t\t## Confirm local master tracks remote master.\n> +\t\tcd sub_repo &&\n> +\t\tHUF=`git rev-parse HEAD` &&\n> + ...\n> +\t\tgit status --porcelain=v2 --branch --ignored --untracked-files=all >actual &&\n> +\t\ttest_cmp expected actual\n> +\t) &&\n> +\trm -rf sub_repo\n\nIt probably is a good idea to use test_when_finished immediately\nbefore \"git clone . sub_repo\" to arrange this to happen even when\nany test in the subshell fails.\n"},{"id":"293214","messageId":"57A4D6C4.3010501@jeffhostetler.com","threadId":"42985","inReplyTo":"xmqqd1lnxi99.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v4 6/8] status: print branch info with --porcelain=v2 --branch","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2016-08-05T18:11:16Z","receivedAt":"2016-08-05T18:13:29Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"\n\nOn 08/05/2016 01:01 PM, Junio C Hamano wrote:\n> Jeff Hostetler <git@jeffhostetler.com> writes:\n>\n>>   /*\n>> + * Print branch information for porcelain v2 output.  These lines\n>> + * are printed when the '--branch' parameter is given.\n>> + *\n>> + *    # branch.oid <commit><eol>\n>> + *    # branch.head <head><eol>\n>\n> Just bikeshedding, but ...\n>\n>> +\tif (!s->branch)\n>> +\t\tfprintf(s->fp, \"# branch.head %s%c\", \"(unknown)\", eol);\n>> +\telse {\n>> +\t\tif (!strcmp(s->branch, \"HEAD\")) {\n>> +\t\t\tfprintf(s->fp, \"# branch.head %s%c\", \"(detached)\", eol);\n>> +\n>> +\t\t\tif (state.rebase_in_progress || state.rebase_interactive_in_progress)\n>> +\t\t\t\tbranch_name = state.onto;\n>> +\t\t\telse if (state.detached_from)\n>> +\t\t\t\tbranch_name = state.detached_from;\n>> +\t\t\telse\n>> +\t\t\t\tbranch_name = \"\";\n>> +\t\t} else {\n>> +\t\t\tbranch_name = NULL;\n>> +\t\t\tskip_prefix(s->branch, \"refs/heads/\", &branch_name);\n>> +\n>> +\t\t\tfprintf(s->fp, \"# branch.head %s%c\", branch_name, eol);\n>\n> ... given that we are showing branch name, perhaps \"branch.name\"\n> instead of \"branch.head\" is more appropriate?\n\nEither way is fine with me.  I just saw your comments on\ncommit v4-7/8. And leaving it as is is fine.\n\n>\n> I wondered if \"# \" prefix before these lines is useful, by the way,\n> and initially thought that the fact that these lines begin with\n> \"branch.\" and not with the \"1/2/u $key\" sufficient clue for whoever\n> reads them, but the reader can tell which kind of record it is by\n> reading the first two characters of each line (i.e. if \"# \" that is\n> not the usual \"change info for a single file\"), so it is actually a\n> good idea.\n\nYes, I used the \"#\" prefix to indicate a header line so that\nparsers can always look at the first character and decide.\n\nI set it up so the \"--branch\" argument creates \"# branch.*\" headers.\n\nIn my first patch series, I included worktree state information\n(such as in-a-rebase and the rebase step counts), but it was thought\nthat that should be an independent effort. So, using this model,\nif we later do add a \"--state\" argument, it would create \"# state.*\"\nheaders.  And we would not have to change the --porcelain=v2 version\nnumber.\n\n\nJeff\n"},{"id":"293216","messageId":"57A4DA85.2010809@jeffhostetler.com","threadId":"42985","inReplyTo":"xmqq60rfxfyl.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v4 7/8] git-status.txt: describe --porcelain=v2 format","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2016-08-05T18:27:17Z","receivedAt":"2016-08-05T18:29:28Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"\n\nOn 08/05/2016 01:50 PM, Junio C Hamano wrote:\n> Jeff Hostetler <git@jeffhostetler.com> writes:\n>\n>> +Porcelain Format Version 2\n>> +~~~~~~~~~~~~~~~~~~~~~~~~~~\n>> +\n>> +Version 2 format adds more detailed information about the state of\n>> +the worktree and changed items.  Version 2 also defines an extensible\n>> +set of easy to parse optional headers.\n>> +\n>> +Header lines start with \"#\" and are added in response to specific\n>> +command line arguments.  Parsers should ignore headers they\n>> +don't recognize.\n>> +\n>> +### Branch Headers\n>> +\n>> +If `--branch` is given, a series of header lines are printed with\n>> +information about the current branch.\n>> +\n>> +    Line                                     Notes\n>> +    ------------------------------------------------------------\n>> +    # branch.oid <commit> | (initial)        Current commit.\n>> +    # branch.head <branch> | (detached)      Current branch.\n>\n> In an earlier review I made a noise about \"branch.head\", but reading\n> this together with others in a context, \"branch.head\" is good and I\n> do not see a need to bikeshed it into \"branch.name\".\n\nright.\n\n\n>> +    # branch.upstream <upstream_branch>      If upstream is set.\n>> +    # branch.ab +<ahead> -<behind>           If upstream is set and\n>> +                                             the commit is present.\n>> +    ------------------------------------------------------------\n>> +\n>> +### Changed Tracked Entries\n>> +\n>> +Following the headers, a series of lines are printed for tracked\n>> +entries.  One of three different line formats may be used to describe\n>> +an entry depending on the type of change.  Tracked entries are printed\n>> +in an undefined order; parsers should allow for a mixture of the 3\n>> +line types in any order.\n>> +\n>> +Ordinary changed entries have the following format:\n>> +\n>> +    1 <XY> <sub> <mH> <mI> <mW> <hH> <hI> <path>\n>> +\n>> +Renamed or copied entries have the following format:\n>> +\n>> +    2 <XY> <sub> <mH> <mI> <mW> <hH> <hI> <X><nr> <path><sep><pathSrc>\n>> +\n>> +    Field       Meaning\n>> +    --------------------------------------------------------\n>> +    <XY>        A 2 character field containing the staged and\n>> +                unstaged XY values described in the short format,\n>> +                with unchanged indicated by a \".\" rather than\n>> +                a space.\n>> +    <sub>       A 4 character field describing the submodule state.\n>> +                \"N...\" when the entry is not a submodule.\n>> +                \"S<c><m><u>\" when the entry is a submodule.\n>> +                <c> is \"C\" if the commit changed; otherwise \".\".\n>> +                <m> is \"M\" if it has tracked changes; otherwise \".\".\n>> +                <u> is \"U\" if there are untracked changes; otherwise \".\".\n>> +    <mH>        The 6 character octal file mode in the HEAD.\n>\n> We do want \"octal\" to be spelled out, but I doubt that we want to\n> guarantee to the reading scripts that this will always be 6\n> characters.\n\ngood point. \"octal\" is sufficient.\n\n\n>> +    <mI>        The octal file mode in the index.\n>> +    <mW>        The octal file mode in the worktree.\n>> +    <hH>        The SHA1 value in the HEAD.\n>> +    <hI>        The SHA1 value in the index.\n>\n> Please avoid inventing a word \"SHA1 value\" that does not appear in\n> Documentation/glossary-content.txt; s/SHA1 value/object name/g.\n> This comment applies to the desciption for \"U\" lines below, too.\n\nok.\n\n\n>> +    <X><nr>     The rename or copied percentage score. For example \"R100\"\n>> +                or \"C75\".\n>\n> Documentation/diff-format.txt seems to call this differently.\n>\n> \tThe rename or copy \"score\" number, e.g. \"R100\", \"C75\".\n>\n> perhaps?\n\nright, this is the same score.\n\n\n>> +    <path>      The current pathname.\n>\n> Unless you are talking about \"2\" line, saying \"current\" is somewhat\n> weird.  I _think_ <path> is the name in the index and in the working\n> tree, as opposed to the original path in the HEAD, but the\n> distinction does not matter for \"1\" or \"U\" lines.\n\nRight.  The index path, since we only report renames for\nhead-vs-index.\n\n\n> As this table is meant to be shared between \"1\" and \"2\", perhaps\n>\n> \t<path>     The pathname.  In a renamed/copied entry, this\n> \t\t   is the path in the index and in the working tree.\n> \t<origPath> The pathname in the commit at HEAD.  This is only\n>                     present in a renamed/copied entry, and tells\n>                     where the renamed/copied contents came from.\n>\n> or something like that may clarify it a bit better.  Then in the\n> table for \"U\" lines, we can just say \"The pathname\".\n>\n>> +    <sep>       When the `-z` option is used, the 2 pathnames are separated\n>> +                with a NUL (ASCII 0x00) byte; otherwise, a tab (ASCII 0x09)\n>> +                byte separates them.\n>> +    <pathSrc>   The original path. This is only present when the entry\n>> +                has been renamed or copied.\n>\n> If it is explained as \"the original path\", wouldn't it be easier to\n> read if it were called \"<origPath>\" instead of \"<pathSrc>\"?\n\nyes.\n\n\n>> +When the `-z` option is given, pathnames are printed as is and\n>> +without any quoting and lines are terminated with a NUL (ASCII 0x00)\n>> +byte.\n>> +\n>> +Otherwise, all pathnames will be \"C-quoted\" if they contain any tab,\n>> +linefeed, double quote, or backslash characters. In C-quoting, these\n>> +characters will be replaced with the corresponding C-style escape\n>> +sequences and the resulting pathname will be double quoted.\n>\n> Looks good.\n>\n> I think I saw \"C-Quoted\" elsewhere (perhaps in a in-code comment),\n> which should be spelled consistently i.e. \"C-quoted\".\n\nRight.\n\nThanks,\nJeff\n\n\n\n"},{"id":"293217","messageId":"57A4DB8A.6020407@jeffhostetler.com","threadId":"42985","inReplyTo":"xmqq1t23xeyb.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v4 8/8] status: tests for --porcelain=v2","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2016-08-05T18:31:38Z","receivedAt":"2016-08-05T18:33:58Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"\n\nOn 08/05/2016 02:12 PM, Junio C Hamano wrote:\n> Jeff Hostetler <git@jeffhostetler.com> writes:\n>\n>> +##################################################################\n>> +## Confirm output prior to initial commit.\n>> +##################################################################\n>> +\n>> +test_expect_success pre_initial_commit_0 '\n>\n> Bikeshedding, but our codebase seems to prefer \"expect\" vs \"actual\".\n>\n>      $ git grep -e 'test_cmp expect ' t/ | wc -l\n>      1882\n>      $ git grep -e 'test_cmp expected ' t/ | wc -l\n>      888\n>\n>> +\tcat >expected <<-EOF &&\n>> +\t# branch.oid (initial)\n>> +\t# branch.head master\n>> +\t? actual\n>> +\t? dir1/\n>> +\t? expected\n>> +\t? file_x\n>> +\t? file_y\n>> +\t? file_z\n>> +\tEOF\n>\n> Perhaps throw these two entries to .gitignore to allow new tests in\n> the future could also use expect.1 vs actual.1 and somesuch?\n>\n> \tcat >.gitignore <<-\\EOF &&\n> \texpect*\n>          actual*\n>          EOF\n>\n>> +test_expect_success pre_initial_commit_1 '\n>> +\tgit add file_x file_y file_z dir1 &&\n>> +\tSHA_A=`git hash-object -t blob -- dir1/file_a` &&\n>> +\tSHA_B=`git hash-object -t blob -- dir1/file_b` &&\n>> +\tSHA_X=`git hash-object -t blob -- file_x` &&\n>> +\tSHA_Y=`git hash-object -t blob -- file_y` &&\n>> +\tSHA_Z=`git hash-object -t blob -- file_z` &&\n>\n> Please use $(commannd) instead of `command`.  Also \"SHA\" is probably\n> a bad prefix; either use \"SHA_1\" to be technically correct, or\n> better yet use \"OID\", as we are moving towards abstracting the exact\n> hash function name away.\n>\n>> +\tSHA_ZERO=0000000000000000000000000000000000000000 &&\n>\n> I think we made $_z40 available to you from t/test-lib.sh.\n>\n>> +## Try -z on the above\n>> +test_expect_success pre_initial_commit_2 '\n>> +\tcat >expected.lf <<-EOF &&\n>> +\t# branch.oid (initial)\n>> +\t# branch.head master\n>> +\t1 A. N... 000000 100644 100644 $SHA_ZERO $SHA_A dir1/file_a\n>> +\t1 A. N... 000000 100644 100644 $SHA_ZERO $SHA_B dir1/file_b\n>> +\t1 A. N... 000000 100644 100644 $SHA_ZERO $SHA_X file_x\n>> +\t1 A. N... 000000 100644 100644 $SHA_ZERO $SHA_Y file_y\n>> +\t1 A. N... 000000 100644 100644 $SHA_ZERO $SHA_Z file_z\n>> +\t? actual\n>> +\t? expected\n>> +\tEOF\n>> +\tperl -pe y/\\\\012/\\\\000/ <expected.lf >expected &&\n>> +\trm expected.lf &&\n>\n> As you immediately remove expected.lf, the first \"cat\" process is\n> rather pointless.  You can redirect here text <<-EOF directly into\n> perl instead.  Also it would probably help to add a new helper\n> \"lf_to_nul\" in t/test-lib-functions.sh around the place where\n> nul_to_q, ..., tz_to_tab_space helpers are defined, which would\n> allow us to say\n>\n> \tlf_to_nul >expect <<-EOF &&\n> \t...\n>          EOF\n>\n>> +test_expect_success initial_commit_3 '\n>> +\tgit mv file_y renamed_y &&\n>> +\tH0=`git rev-parse HEAD` &&\n>> +\n>> +\tcat >expected.q <<-EOF &&\n>> +\t# branch.oid $H0\n>> +\t# branch.head master\n>> +\t1 M. N... 100644 100644 100644 $SHA_X $SHA_X1 file_x\n>> +\t1 D. N... 100644 000000 000000 $SHA_Z $SHA_ZERO file_z\n>> +\t2 R. N... 100644 100644 100644 $SHA_Y $SHA_Y R100 renamed_yQfile_y\n>> +\t? actual\n>> +\t? expected\n>> +\tEOF\n>> +\tq_to_tab <expected.q >expected &&\n>> +\trm expected.q &&\n>\n> The same comment applies (redirect directly into q_to_tab).\n>\n>> +##################################################################\n>> +## Ignore a file\n>> +##################################################################\n>> +\n>> +test_expect_success ignore_file_0 '\n>> +\techo x.ign >.gitignore &&\n>> +\techo \"ignore me\" >x.ign &&\n>> +\tH1=`git rev-parse HEAD` &&\n>> +\n>> +\tcat >expected <<-EOF &&\n>> +\t# branch.oid $H1\n>> +\t# branch.head master\n>> +\t? .gitignore\n>> +\t? actual\n>> +\t? expected\n>> +\t! x.ign\n>> +\tEOF\n>> +\n>> +\tgit status --porcelain=v2 --branch --ignored --untracked-files=all >actual &&\n>> +\trm x.ign &&\n>> +\trm .gitignore &&\n>> +\ttest_cmp expected actual\n>> +'\n>\n> You do not seem to be checking a feature is not triggered when not\n> asked throughout this test, e.g. making sure the output does not\n> have the \"# branch.*\" lines when --branch is not given, \"! x.ign\"\n> is not shown when --ignored is not given, etc.\n>\n>> +##################################################################\n>> +## Test upstream fields in branch header\n>> +##################################################################\n>> +\n>> +test_expect_success 'upstream_fields_0' '\n>> +\tgit checkout master &&\n>> +\tgit clone . sub_repo &&\n>> +\t(\n>> +\t\t## Confirm local master tracks remote master.\n>> +\t\tcd sub_repo &&\n>> +\t\tHUF=`git rev-parse HEAD` &&\n>> + ...\n>> +\t\tgit status --porcelain=v2 --branch --ignored --untracked-files=all >actual &&\n>> +\t\ttest_cmp expected actual\n>> +\t) &&\n>> +\trm -rf sub_repo\n>\n> It probably is a good idea to use test_when_finished immediately\n> before \"git clone . sub_repo\" to arrange this to happen even when\n> any test in the subshell fails.\n>\n\n\nLots of good points here (and on the earlier commits in this\nseries).  I'll address and send up a new version shortly.\n\nThanks!\nJeff\n"},{"id":"293238","messageId":"20160805210222.a2rvlmioim4psbhw@sigill.intra.peff.net","threadId":"42985","inReplyTo":"1470147137-17498-6-git-send-email-git@jeffhostetler.com","subject":"Re: [PATCH v4 5/8] status: print per-file porcelain v2 status data","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-08-05T21:02:22Z","receivedAt":"2016-08-05T21:02:32Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Aug 02, 2016 at 10:12:14AM -0400, Jeff Hostetler wrote:\n\n> +static void wt_porcelain_v2_print_unmerged_entry(\n> +\tstruct string_list_item *it,\n> +\tstruct wt_status *s)\n> +{\n> +\tstruct wt_status_change_data *d = it->util;\n> +\tconst struct cache_entry *ce;\n> +\tstruct strbuf buf_current = STRBUF_INIT;\n> +\tconst char *path_current = NULL;\n> +\tint pos, stage, sum;\n> +\tstruct {\n> +\t\tint mode;\n> +\t\tstruct object_id oid;\n> +\t} stages[3];\n> +\tchar *key;\n> [...]\n> +\tswitch (d->stagemask) {\n> +\tcase 1: key = \"DD\"; break; /* both deleted */\n> +\tcase 2: key = \"AU\"; break; /* added by us */\n> +\tcase 3: key = \"UD\"; break; /* deleted by them */\n> +\tcase 4: key = \"UA\"; break; /* added by them */\n> +\tcase 5: key = \"DU\"; break; /* deleted by us */\n> +\tcase 6: key = \"AA\"; break; /* both added */\n> +\tcase 7: key = \"UU\"; break; /* both modified */\n> +\t}\n> [...]\n> +\tfprintf(s->fp, \"%c %s %s %06o %06o %06o %06o %s %s %s %s%c\",\n> +\t\t\tunmerged_prefix, key, submodule_token,\n\nCoverity complains that \"key\" can be uninitialized here. I think it's\nwrong, and just doesn't know that d->stagemask is constrained to 1-7.\n\nBut perhaps it is worth adding a:\n\n  default:\n\tdie(\"BUG: unhandled unmerged status %x\", d->stagemask);\n\nto the end of the switch. That would shut up Coverity, and give us a\nbetter indication if our constraint is violated.\n\n-Peff\n"},{"id":"293240","messageId":"CAPc5daUr7OgFeefbwWLWFt3KW_9X-ijRmHa2oj0--zY4fqt82A@mail.gmail.com","threadId":"42985","inReplyTo":"20160805210222.a2rvlmioim4psbhw@sigill.intra.peff.net","subject":"Re: [PATCH v4 5/8] status: print per-file porcelain v2 status data","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-08-05T21:09:48Z","receivedAt":"2016-08-05T21:10:20Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"On Fri, Aug 5, 2016 at 2:02 PM, Jeff King <peff@peff.net> wrote:\n> On Tue, Aug 02, 2016 at 10:12:14AM -0400, Jeff Hostetler wrote:\n>> +     switch (d->stagemask) {\n>> +     case 1: key = \"DD\"; break; /* both deleted */\n>> + ...\n>> +     case 7: key = \"UU\"; break; /* both modified */\n>> +     }\n>> [...]\n>> +     fprintf(s->fp, \"%c %s %s %06o %06o %06o %06o %s %s %s %s%c\",\n>> +                     unmerged_prefix, key, submodule_token,\n>\n> Coverity complains that \"key\" can be uninitialized here. I think it's\n> wrong, and just doesn't know that d->stagemask is constrained to 1-7.\n>\n> But perhaps it is worth adding a:\n>\n>   default:\n>         die(\"BUG: unhandled unmerged status %x\", d->stagemask);\n>\n> to the end of the switch. That would shut up Coverity, and give us a\n> better indication if our constraint is violated.\n\nThis is pure curiosity but I wonder if Coverity shuts up if we\ninstead switched on (d->stagemask & 07). Your \"default: BUG\"\nsuggestion is what we should use as a real fix, of course.\n"},{"id":"293241","messageId":"20160805211434.54mtaw2cty4gaxsr@sigill.intra.peff.net","threadId":"42985","inReplyTo":"CAPc5daUr7OgFeefbwWLWFt3KW_9X-ijRmHa2oj0--zY4fqt82A@mail.gmail.com","subject":"Re: [PATCH v4 5/8] status: print per-file porcelain v2 status data","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-08-05T21:14:35Z","receivedAt":"2016-08-05T21:14:42Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Aug 05, 2016 at 02:09:48PM -0700, Junio C Hamano wrote:\n\n> On Fri, Aug 5, 2016 at 2:02 PM, Jeff King <peff@peff.net> wrote:\n> > On Tue, Aug 02, 2016 at 10:12:14AM -0400, Jeff Hostetler wrote:\n> >> +     switch (d->stagemask) {\n> >> +     case 1: key = \"DD\"; break; /* both deleted */\n> >> + ...\n> >> +     case 7: key = \"UU\"; break; /* both modified */\n> >> +     }\n> >> [...]\n> >> +     fprintf(s->fp, \"%c %s %s %06o %06o %06o %06o %s %s %s %s%c\",\n> >> +                     unmerged_prefix, key, submodule_token,\n> >\n> > Coverity complains that \"key\" can be uninitialized here. I think it's\n> > wrong, and just doesn't know that d->stagemask is constrained to 1-7.\n> >\n> > But perhaps it is worth adding a:\n> >\n> >   default:\n> >         die(\"BUG: unhandled unmerged status %x\", d->stagemask);\n> >\n> > to the end of the switch. That would shut up Coverity, and give us a\n> > better indication if our constraint is violated.\n> \n> This is pure curiosity but I wonder if Coverity shuts up if we\n> instead switched on (d->stagemask & 07). Your \"default: BUG\"\n> suggestion is what we should use as a real fix, of course.\n\nI suspect yes. It's pretty clever about analyzing the flow and placing\nconstraints on values (though in this case \"& 07\" includes \"0\", which is\nnot handled in the switch).\n\nUnfortunately it is hard for me to test a one-off, as running it locally\nis a complete pain. Stefan set it up long ago to pull \"pu\" and email out\nthe results from their central servers, so I just scan those emails for\nthings that look like real issues.\n\n-Peff\n"},{"id":"293246","messageId":"CAPc5daUb-8uUU-BsLVFoYZKcySgMYGCu70hTwh1A7L8X0vSJ4Q@mail.gmail.com","threadId":"42985","inReplyTo":"20160805211434.54mtaw2cty4gaxsr@sigill.intra.peff.net","subject":"Re: [PATCH v4 5/8] status: print per-file porcelain v2 status data","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-08-05T21:21:10Z","receivedAt":"2016-08-05T21:21:37Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"On Fri, Aug 5, 2016 at 2:14 PM, Jeff King <peff@peff.net> wrote:\n>\n> Unfortunately it is hard for me to test a one-off, as running it locally\n> is a complete pain. Stefan set it up long ago to pull \"pu\" and email out\n> the results from their central servers, so I just scan those emails for\n> things that look like real issues.\n\nAh, running check on 'pu' is an excellent way to catch issues early,\nbefore they hit 'next'.\n\nThanks.\n"},{"id":"293250","messageId":"57A504C0.1060808@jeffhostetler.com","threadId":"42985","inReplyTo":"20160805210222.a2rvlmioim4psbhw@sigill.intra.peff.net","subject":"Re: [PATCH v4 5/8] status: print per-file porcelain v2 status data","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2016-08-05T21:27:28Z","receivedAt":"2016-08-05T21:29:51Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"\n\nOn 08/05/2016 05:02 PM, Jeff King wrote:\n> On Tue, Aug 02, 2016 at 10:12:14AM -0400, Jeff Hostetler wrote:\n>\n>> +static void wt_porcelain_v2_print_unmerged_entry(\n>> +\tstruct string_list_item *it,\n>> +\tstruct wt_status *s)\n>> +{\n>> +\tstruct wt_status_change_data *d = it->util;\n>> +\tconst struct cache_entry *ce;\n>> +\tstruct strbuf buf_current = STRBUF_INIT;\n>> +\tconst char *path_current = NULL;\n>> +\tint pos, stage, sum;\n>> +\tstruct {\n>> +\t\tint mode;\n>> +\t\tstruct object_id oid;\n>> +\t} stages[3];\n>> +\tchar *key;\n>> [...]\n>> +\tswitch (d->stagemask) {\n>> +\tcase 1: key = \"DD\"; break; /* both deleted */\n>> +\tcase 2: key = \"AU\"; break; /* added by us */\n>> +\tcase 3: key = \"UD\"; break; /* deleted by them */\n>> +\tcase 4: key = \"UA\"; break; /* added by them */\n>> +\tcase 5: key = \"DU\"; break; /* deleted by us */\n>> +\tcase 6: key = \"AA\"; break; /* both added */\n>> +\tcase 7: key = \"UU\"; break; /* both modified */\n>> +\t}\n>> [...]\n>> +\tfprintf(s->fp, \"%c %s %s %06o %06o %06o %06o %s %s %s %s%c\",\n>> +\t\t\tunmerged_prefix, key, submodule_token,\n>\n> Coverity complains that \"key\" can be uninitialized here. I think it's\n> wrong, and just doesn't know that d->stagemask is constrained to 1-7.\n>\n> But perhaps it is worth adding a:\n>\n>    default:\n> \tdie(\"BUG: unhandled unmerged status %x\", d->stagemask);\n>\n> to the end of the switch. That would shut up Coverity, and give us a\n> better indication if our constraint is violated.\n>\n> -Peff\n>\n\ngot it. thanks!\nJeff\n"},{"id":"293255","messageId":"CAGZ79kb2aO80csCfV=QbH8D1spdNLdkTCSPriZ8W9nSy6T5QAg@mail.gmail.com","threadId":"42985","inReplyTo":"20160805211434.54mtaw2cty4gaxsr@sigill.intra.peff.net","subject":"Re: [PATCH v4 5/8] status: print per-file porcelain v2 status data","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-08-05T21:43:49Z","receivedAt":"2016-08-05T21:44:15Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Fri, Aug 5, 2016 at 2:14 PM, Jeff King <peff@peff.net> wrote:\n\n>\n> Unfortunately it is hard for me to test a one-off, as running it locally\n> is a complete pain. Stefan set it up long ago to pull \"pu\" and email out\n> the results from their central servers, so I just scan those emails for\n> things that look like real issues.\n>\n\nI am glad this provides actual value. :)\n(I tend to ignore the emails nowadays as I am focusing on other stuff.\nI'll get back to down the number issues eventually, I promise ;)\n\nRunning one offs is \"relatively\" easy.\nI did that for other projects once upon a time.\nHere is my script to run the build:\n\n    #!/bin/bash\n    . .profile\n\n    cd coverity/git\n    git clean -dfx\n    git fetch --all\n    git checkout origin/pu\n    git am ../git_strbuf_stop_out_of_bounds_coverity.patch\n\n    descrip=\"scripted upload scanning github.com/gitster/git pu\"\n    name=$(git describe)\n\n    cov-build --dir cov-int make\n    tar czvf git-${name}.tgz cov-int\n\n    curl --form project=git \\\n      --form token=<sekret> \\\n      --form email=<my mail address> \\\n      --form file=@git-${name}.tgz \\\n      --form version=\"${name}\" \\\n      --form description=\"${descrip}\" \\\n      https://scan.coverity.com/builds?project=git\n\n    git clean -dfx\n\n\nYou can get a token if you look around, e.g. at\nhttps://scan.coverity.com/projects/git/builds/new\nand the email address is the one you signed up with\ncoverity (or the one from Github, if signed up via Github)\n\nI am currently applying one patch on top of pu,\n(id: 1434536209-31350-1-git-send-email-pclouds@gmail.com\nI can resend if you don't have that.)\n\nI am running this script as a cron job, but it can be run \"as is\"\nas well, you'd just need to toss in the one-off test patch.\n\nThanks,\nStefan\n"},{"id":"293257","messageId":"CAGZ79kZOv4rTuqg7kLWydPY4dCeivaBXZ8HeU8hxu79rPeu-5w@mail.gmail.com","threadId":"42985","inReplyTo":"CAGZ79kb2aO80csCfV=QbH8D1spdNLdkTCSPriZ8W9nSy6T5QAg@mail.gmail.com","subject":"Re: [PATCH v4 5/8] status: print per-file porcelain v2 status data","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-08-05T21:47:38Z","receivedAt":"2016-08-05T21:47:47Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Fri, Aug 5, 2016 at 2:43 PM, Stefan Beller <sbeller@google.com> wrote:\n\n>     cov-build --dir cov-int make\n\nFor this you need to download their analytic tool and put it in\nyour $PATH. Let me see if I need to update the tool so it\nenables finding more potential issues.\n\nSo that was an initial hurdle.\n"}]}