{"thread":{"id":"17568","subject":"[RFC/PATCH] shortstatus v0","startedAt":"2009-02-04T22:05:48Z","lastAt":"2009-02-04T22:17:47Z","messageCount":2,"participants":["Tuncer Ayaz"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"103213","messageId":"747EF6E2-39BB-4076-AF34-90571B2D70D9@gmail.com","threadId":"17568","inReplyTo":null,"subject":"[RFC/PATCH] shortstatus v0","fromName":"Tuncer Ayaz","fromEmail":"tuncer.ayaz@gmail.com","sentAt":"2009-02-04T22:05:48Z","receivedAt":"2009-02-04T22:05:48Z","isPatch":true,"sender":{"key":"tuncer.ayaz@gmail.com","avatar":null},"body":"As discussed recently I started taking Junio's shortstatus patch\nfrom October 25th 2008 and integrated it into current master.\n\nThe following patch revision does work but does not print the\n'M ' prefixes yet due to either a patching error on my side or\nmissing code in the forward port of the patch. I still have\nto analyze that.\n\nWhat this patch still needs to implement is something like\n'shortstatus --mini' which prints:\n   anything modified          -> *\n   anything added             -> +\n   anything untracked/unknown -> ?\n\nSo if you have a repo where one file is modified,\na new file is added and an unknown file exists and\nis not ignored 'shortstatus --mini' shall print:\n+*?.\n\nThis is really useful for enhancing a Git enabled\nshell prompt with small but important information.\n\nRight now this is basically Junio's shortstatus\nfrom Oct 25th 2008 with no substantial change\nexcept a line or two.\n\n\nSigned-off-by: Tuncer Ayaz <tuncer.ayaz@gmail.com>\n---\n\n  .gitignore       |    1 +\n  Makefile         |    1 +\n  builtin-commit.c |   45 +++++++++++-\n  builtin-revert.c |    1 +\n  builtin.h        |    1 +\n  git.c            |    1 +\n  wt-status.c      |  213 ++++++++++++++++++++++++++++++++++++++++++ \n+-----------\n  wt-status.h      |    9 +++\n  8 files changed, 227 insertions(+), 45 deletions(-)\n\ndiff --git a/.gitignore b/.gitignore\nindex 1c57d4c..5666325 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -114,6 +114,7 @@ git-send-pack\n  git-sh-setup\n  git-shell\n  git-shortlog\n+git-shortstatus\n  git-show\n  git-show-branch\n  git-show-index\ndiff --git a/Makefile b/Makefile\nindex 9f3a8ab..304f15d 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -329,6 +329,7 @@ BUILT_INS += git-repo-config$X\n  BUILT_INS += git-show$X\n  BUILT_INS += git-stage$X\n  BUILT_INS += git-status$X\n+BUILT_INS += git-shortstatus$X\n  BUILT_INS += git-whatchanged$X\n\n  # what 'all' will build and 'install' will install, in gitexecdir\ndiff --git a/builtin-commit.c b/builtin-commit.c\nindex d6a3a62..624568c 100644\n--- a/builtin-commit.c\n+++ b/builtin-commit.c\n@@ -14,6 +14,7 @@\n  #include \"diffcore.h\"\n  #include \"commit.h\"\n  #include \"revision.h\"\n+#include \"string-list.h\"\n  #include \"wt-status.h\"\n  #include \"run-command.h\"\n  #include \"refs.h\"\n@@ -21,7 +22,6 @@\n  #include \"strbuf.h\"\n  #include \"utf8.h\"\n  #include \"parse-options.h\"\n-#include \"string-list.h\"\n  #include \"rerere.h\"\n  #include \"unpack-trees.h\"\n\n@@ -821,6 +821,49 @@ static int parse_and_validate_options(int argc,  \nconst char *argv[],\n  \treturn argc;\n  }\n\n+int cmd_shortstatus(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct wt_status s;\n+\tint i;\n+\n+\tread_cache();\n+\trefresh_cache(REFRESH_QUIET);\n+\twt_status_prepare(&s);\n+\twt_status_collect_changes(&s);\n+\tfor (i = 0; i < s.change.nr; i++) {\n+\t\tstruct wt_status_change_data *d;\n+\t\tstruct string_list_item *it;\n+\t\tchar pfx[1 + 3 + 1 + 1];\n+\n+\t\tit = &(s.change.items[i]);\n+\t\td = it->util;\n+\t\tswitch (d->index_status) {\n+\t\t\tcase DIFF_STATUS_COPIED:\n+\t\t\tcase DIFF_STATUS_RENAMED:\n+\t\t\t\tsprintf(pfx, \"%c%3d\",\n+\t\t\t\t\t\td->index_status,\n+\t\t\t\t\t\t(int)(d->index_score * 100 / MAX_SCORE));\n+\t\t\t\tbreak;\n+\t\t\tcase 0:\n+\t\t\t\tmemcpy(pfx, \"\t\", 4);\n+\t\t\t\tbreak;\n+\t\t\tdefault:\n+\t\t\t\tsprintf(pfx, \"%c\t  \", d->index_status);\n+\t\t\t\tbreak;\n+\t\t}\n+\t\tif (!d->worktree_status)\n+\t\t\tpfx[4] = ' ';\n+\t\telse\n+\t\t\tpfx[4] = d->worktree_status;\n+\t\tpfx[5] = '\\0';\n+\t\tprintf(\"%s \", pfx);\n+\t\tif (d->head_path)\n+\t\t\tprintf(\"%s -> \", d->head_path);\n+\t\tprintf(\"%s\\n\", it->string);\n+\t}\n+\treturn 0;\n+}\n+\n  int cmd_status(int argc, const char **argv, const char *prefix)\n  {\n  \tconst char *index_file;\ndiff --git a/builtin-revert.c b/builtin-revert.c\nindex d48313c..7dd7646 100644\n--- a/builtin-revert.c\n+++ b/builtin-revert.c\n@@ -3,6 +3,7 @@\n  #include \"object.h\"\n  #include \"commit.h\"\n  #include \"tag.h\"\n+#include \"string-list.h\"\n  #include \"wt-status.h\"\n  #include \"run-command.h\"\n  #include \"exec_cmd.h\"\ndiff --git a/builtin.h b/builtin.h\nindex 1495cf6..f054fc7 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -94,6 +94,7 @@ extern int cmd_shortlog(int argc, const char **argv,  \nconst char *prefix);\n  extern int cmd_show(int argc, const char **argv, const char *prefix);\n  extern int cmd_show_branch(int argc, const char **argv, const char  \n*prefix);\n  extern int cmd_status(int argc, const char **argv, const char  \n*prefix);\n+extern int cmd_shortstatus(int argc, const char **argv, const char  \n*prefix);\n  extern int cmd_stripspace(int argc, const char **argv, const char  \n*prefix);\n  extern int cmd_symbolic_ref(int argc, const char **argv, const char  \n*prefix);\n  extern int cmd_tag(int argc, const char **argv, const char *prefix);\ndiff --git a/git.c b/git.c\nindex c2b181e..4c0fa44 100644\n--- a/git.c\n+++ b/git.c\n@@ -344,6 +344,7 @@ static void handle_internal_command(int argc,  \nconst char **argv)\n  \t\t{ \"rm\", cmd_rm, RUN_SETUP },\n  \t\t{ \"send-pack\", cmd_send_pack, RUN_SETUP },\n  \t\t{ \"shortlog\", cmd_shortlog, USE_PAGER },\n+\t\t{ \"shortstatus\", cmd_shortstatus, RUN_SETUP | NEED_WORK_TREE },\n  \t\t{ \"show-branch\", cmd_show_branch, RUN_SETUP },\n  \t\t{ \"show\", cmd_show, RUN_SETUP | USE_PAGER },\n  \t\t{ \"status\", cmd_status, RUN_SETUP | NEED_WORK_TREE },\ndiff --git a/wt-status.c b/wt-status.c\nindex 96ff2f8..18042dc 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -1,4 +1,5 @@\n  #include \"cache.h\"\n+#include \"string-list.h\"\n  #include \"wt-status.h\"\n  #include \"color.h\"\n  #include \"object.h\"\n@@ -56,6 +57,7 @@ void wt_status_prepare(struct wt_status *s)\n  \ts->reference = \"HEAD\";\n  \ts->fp = stdout;\n  \ts->index_file = get_index_file();\n+\ts->change.strdup_strings = 1;\n  }\n\n  static void wt_status_print_cached_header(struct wt_status *s)\n@@ -98,18 +100,23 @@ static void wt_status_print_trailer(struct  \nwt_status *s)\n\n  #define quote_path quote_path_relative\n\n-static void wt_status_print_filepair(struct wt_status *s,\n-\t\t\t\t     int t, struct diff_filepair *p)\n+static void wt_status_print_change_data(struct wt_status *s,\n+\t\t\t\t\t\tint t,\n+\t\t\t\t\t\tint status,\n+\t\t\t\t\t\tchar *one_name,\n+\t\t\t\t\t\tchar *two_name,\n+\t\t\t\t\t\tint score)\n  {\n  \tconst char *c = color(t);\n  \tconst char *one, *two;\n  \tstruct strbuf onebuf = STRBUF_INIT, twobuf = STRBUF_INIT;\n\n-\tone = quote_path(p->one->path, -1, &onebuf, s->prefix);\n-\ttwo = quote_path(p->two->path, -1, &twobuf, s->prefix);\n+\tone = quote_path(one_name, -1, &onebuf, s->prefix);\n+\ttwo = quote_path(two_name, -1, &twobuf, s->prefix);\n+\n\n  \tcolor_fprintf(s->fp, color(WT_STATUS_HEADER), \"#\\t\");\n-\tswitch (p->status) {\n+\tswitch (status) {\n  \tcase DIFF_STATUS_ADDED:\n  \t\tcolor_fprintf(s->fp, c, \"new file:   %s\", one);\n  \t\tbreak;\n@@ -135,64 +142,88 @@ static void wt_status_print_filepair(struct  \nwt_status *s,\n  \t\tcolor_fprintf(s->fp, c, \"unmerged:   %s\", one);\n  \t\tbreak;\n  \tdefault:\n-\t\tdie(\"bug: unhandled diff status %c\", p->status);\n+\t\tdie(\"bug: unhandled diff status %c\", status);\n  \t}\n  \tfprintf(s->fp, \"\\n\");\n  \tstrbuf_release(&onebuf);\n  \tstrbuf_release(&twobuf);\n  }\n\n-static void wt_status_print_updated_cb(struct diff_queue_struct *q,\n-\t\tstruct diff_options *options,\n-\t\tvoid *data)\n+static void wt_status_collect_changed_cb(struct diff_queue_struct *q,\n+\t\t\t\t\t\t\tstruct diff_options *options,\n+\t\t\t\t\t\t\tvoid *data)\n  {\n  \tstruct wt_status *s = data;\n-\tint shown_header = 0;\n  \tint i;\n+\n+\tif (!q->nr)\n+\t\treturn;\n+\ts->workdir_dirty = 1;\n  \tfor (i = 0; i < q->nr; i++) {\n-\t\tif (q->queue[i]->status == 'U')\n-\t\t\tcontinue;\n-\t\tif (!shown_header) {\n-\t\t\twt_status_print_cached_header(s);\n-\t\t\ts->commitable = 1;\n-\t\t\tshown_header = 1;\n-\t\t}\n-\t\twt_status_print_filepair(s, WT_STATUS_UPDATED, q->queue[i]);\n+\t\tstruct diff_filepair *p;\n+\t\tstruct string_list_item *it;\n+\t\tstruct wt_status_change_data *d;\n+\n+\t\tp = q->queue[i];\n+\n+\t\td = xcalloc(1, sizeof(*d));\n+\t\td->worktree_status = p->status;\n+\t\tit = string_list_insert(p->one->path, &s->change);\n+\t\tit->util = d;\n  \t}\n-\tif (shown_header)\n-\t\twt_status_print_trailer(s);\n  }\n\n-static void wt_status_print_changed_cb(struct diff_queue_struct *q,\n-                        struct diff_options *options,\n-                        void *data)\n+static void wt_status_collect_updated_cb(struct diff_queue_struct *q,\n+\t\t\t\t\t\t\tstruct diff_options *options,\n+\t\t\t\t\t\t\tvoid *data)\n  {\n  \tstruct wt_status *s = data;\n  \tint i;\n-\tif (q->nr) {\n-\t\tint has_deleted = 0;\n-\t\ts->workdir_dirty = 1;\n-\t\tfor (i = 0; i < q->nr; i++)\n-\t\t\tif (q->queue[i]->status == DIFF_STATUS_DELETED) {\n-\t\t\t\thas_deleted = 1;\n+\n+\tfor (i = 0; i < q->nr; i++) {\n+\t\tstruct diff_filepair *p;\n+\t\tstruct string_list_item *it;\n+\t\tstruct wt_status_change_data *d;\n+\n+\t\tp = q->queue[i];\n+\t\tit = string_list_insert(p->two->path, &s->change);\n+\t\td = it->util;\n+\t\tif (!d) {\n+\t\t\td = xcalloc(1, sizeof(*d));\n+\t\t\tit->util = d;\n+\t\t}\n+\t\td->index_status = p->status;\n+\t\tswitch (p->status) {\n+\t\t\tcase DIFF_STATUS_COPIED:\n+\t\t\tcase DIFF_STATUS_RENAMED:\n+\t\t\t\td->head_path = xstrdup(p->one->path);\n+\t\t\t\td->index_score = p->score;\n  \t\t\t\tbreak;\n-\t\t\t}\n-\t\twt_status_print_dirty_header(s, has_deleted);\n+\t\t}\n  \t}\n-\tfor (i = 0; i < q->nr; i++)\n-\t\twt_status_print_filepair(s, WT_STATUS_CHANGED, q->queue[i]);\n-\tif (q->nr)\n-\t\twt_status_print_trailer(s);\n  }\n\n-static void wt_status_print_updated(struct wt_status *s)\n+static void wt_status_collect_changes_worktree(struct wt_status *s)\n  {\n  \tstruct rev_info rev;\n+\n+\tinit_revisions(&rev, NULL);\n+\tsetup_revisions(0, NULL, &rev, NULL);\n+\trev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;\n+\trev.diffopt.format_callback = wt_status_collect_changed_cb;\n+\trev.diffopt.format_callback_data = s;\n+\trun_diff_files(&rev, 0);\n+}\n+\n+static void wt_status_collect_changes_index(struct wt_status *s)\n+{\n+\tstruct rev_info rev;\n+\n  \tinit_revisions(&rev, NULL);\n  \tsetup_revisions(0, NULL, &rev,\n  \t\ts->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference);\n  \trev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;\n-\trev.diffopt.format_callback = wt_status_print_updated_cb;\n+\trev.diffopt.format_callback = wt_status_collect_updated_cb;\n  \trev.diffopt.format_callback_data = s;\n  \trev.diffopt.detect_rename = 1;\n  \trev.diffopt.rename_limit = 200;\n@@ -200,15 +231,107 @@ static void wt_status_print_updated(struct  \nwt_status *s)\n  \trun_diff_index(&rev, 1);\n  }\n\n+static void wt_status_collect_changes_initial(struct wt_status *s)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < active_nr; i++) {\n+\t\tstruct string_list_item *it;\n+\t\tstruct wt_status_change_data *d;\n+\n+\t\tit = string_list_insert(active_cache[i]->name, &s->change);\n+\t\td = it->util;\n+\t\tif (!d) {\n+\t\t\td = xcalloc(1, sizeof(*d));\n+\t\t\tit->util = d;\n+\t\t}\n+\t\td->index_status = DIFF_STATUS_ADDED;\n+\t}\n+}\n+\n+void wt_status_collect_changes(struct wt_status *s)\n+{\n+\twt_status_collect_changes_worktree(s);\n+\n+\tif (s->is_initial)\n+\t\twt_status_collect_changes_initial(s);\n+\telse\n+\t\twt_status_collect_changes_index(s);\n+}\n+\n+static void wt_status_print_updated(struct wt_status *s)\n+{\n+\tint shown_header = 0;\n+\tint i;\n+\n+\tfor (i = 0; i < s->change.nr; i++) {\n+\t\tstruct wt_status_change_data *d;\n+\t\tstruct string_list_item *it;\n+\t\tit = &(s->change.items[i]);\n+\t\td = it->util;\n+\t\tif (!d->index_status)\n+\t\t\tcontinue;\n+\t\tif (!shown_header) {\n+\t\t\twt_status_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,\n+\t\t\t\td->index_status,\n+\t\t\t\td->head_path ? d->head_path : it->string,\n+\t\t\t\tit->string,\n+\t\t\t\td->index_score);\n+\t}\n+\tif (shown_header)\n+\t\twt_status_print_trailer(s);\n+}\n+\n+/*\n+ * -1 : has delete\n+ *  0 : no change\n+ *  1 : some change but no delete\n+ */\n+static int wt_status_check_worktree_changes(struct wt_status *s)\n+{\n+\tint i;\n+\tint changes = 0;\n+\n+\tfor (i = 0; i < s->change.nr; i++) {\n+\t\tstruct wt_status_change_data *d;\n+\t\td = s->change.items[i].util;\n+\t\tif (!d->worktree_status)\n+\t\t\tcontinue;\n+\t\tchanges = 1;\n+\t\tif (d->worktree_status == DIFF_STATUS_DELETED)\n+\t\t\treturn -1;\n+\t}\n+\treturn changes;\n+}\n+\n  static void wt_status_print_changed(struct wt_status *s)\n  {\n-\tstruct rev_info rev;\n-\tinit_revisions(&rev, \"\");\n-\tsetup_revisions(0, NULL, &rev, NULL);\n-\trev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;\n-\trev.diffopt.format_callback = wt_status_print_changed_cb;\n-\trev.diffopt.format_callback_data = s;\n-\trun_diff_files(&rev, 0);\n+\tint i;\n+\tint worktree_changes = wt_status_check_worktree_changes(s);\n+\n+\tif (!worktree_changes)\n+\t\treturn;\n+\n+\twt_status_print_dirty_header(s, worktree_changes < 0);\n+\n+\tfor (i = 0; i < s->change.nr; i++) {\n+\t\tstruct wt_status_change_data *d;\n+\t\tstruct string_list_item *it;\n+\t\tit = &(s->change.items[i]);\n+\t\td = it->util;\n+\t\tif (!d->worktree_status)\n+\t\t\tcontinue;\n+\t\twt_status_print_change_data(s, WT_STATUS_CHANGED,\n+\t\t\t\td->worktree_status,\n+\t\t\t\tit->string,\n+\t\t\t\tit->string,\n+\t\t\t\t0);\n+\t}\n+\twt_status_print_trailer(s);\n  }\n\n  static void wt_status_print_submodule_summary(struct wt_status *s)\n@@ -338,6 +461,8 @@ void wt_status_print(struct wt_status *s)\n  \t\t\twt_status_print_tracking(s);\n  \t}\n\n+\twt_status_collect_changes(s);\n+\n  \tif (s->is_initial) {\n  \t\tcolor_fprintf_ln(s->fp, color(WT_STATUS_HEADER), \"#\");\n  \t\tcolor_fprintf_ln(s->fp, color(WT_STATUS_HEADER), \"# Initial  \ncommit\");\ndiff --git a/wt-status.h b/wt-status.h\nindex 78add09..00508c3 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -18,6 +18,13 @@ enum untracked_status_type {\n  };\n  extern enum untracked_status_type show_untracked_files;\n\n+struct wt_status_change_data {\n+\tint worktree_status;\n+\tint index_status;\n+\tint index_score;\n+\tchar *head_path;\n+};\n+\n  struct wt_status {\n  \tint is_initial;\n  \tchar *branch;\n@@ -33,6 +40,7 @@ struct wt_status {\n  \tconst char *index_file;\n  \tFILE *fp;\n  \tconst char *prefix;\n+\tstruct string_list change;\n  };\n\n  int git_status_config(const char *var, const char *value, void *cb);\n@@ -40,5 +48,6 @@ extern int wt_status_use_color;\n  extern int wt_status_relative_paths;\n  void wt_status_prepare(struct wt_status *s);\n  void wt_status_print(struct wt_status *s);\n+void wt_status_collect_changes(struct wt_status *s);\n\n  #endif /* STATUS_H */\n"},{"id":"103216","messageId":"4ac8254d0902041417v7f8c264cgf6044e91e27bd4f1@mail.gmail.com","threadId":"17568","inReplyTo":"747EF6E2-39BB-4076-AF34-90571B2D70D9@gmail.com","subject":"Re: [RFC/PATCH] shortstatus v0","fromName":"Tuncer Ayaz","fromEmail":"tuncer.ayaz@gmail.com","sentAt":"2009-02-04T22:17:47Z","receivedAt":"2009-02-04T22:17:47Z","isPatch":true,"sender":{"key":"tuncer.ayaz@gmail.com","avatar":null},"body":"On Wed, Feb 4, 2009 at 11:05 PM, Tuncer Ayaz <tuncer.ayaz@gmail.com> wrote:\n>\n\nLinebreaks got garbled.\nI had problems with Perl on this particular machine and didn't\nuse send-email. It looks like that was a bad decision :).\nI believe it's still readable and as it's just an RFC this\nshouldn't pose a difficulty for review.\nNext revisions will return to using a working git-send-email\ninstead of a MUA with interesting automagics.\n\n> As discussed recently I started taking Junio's shortstatus patch\n> from October 25th 2008 and integrated it into current master.\n>\n> The following patch revision does work but does not print the\n> 'M ' prefixes yet due to either a patching error on my side or\n> missing code in the forward port of the patch. I still have\n> to analyze that.\n>\n> What this patch still needs to implement is something like\n> 'shortstatus --mini' which prints:\n>  anything modified          -> *\n>  anything added             -> +\n>  anything untracked/unknown -> ?\n>\n> So if you have a repo where one file is modified,\n> a new file is added and an unknown file exists and\n> is not ignored 'shortstatus --mini' shall print:\n> +*?.\n>\n> This is really useful for enhancing a Git enabled\n> shell prompt with small but important information.\n>\n> Right now this is basically Junio's shortstatus\n> from Oct 25th 2008 with no substantial change\n> except a line or two.\n>\n>\n> Signed-off-by: Tuncer Ayaz <tuncer.ayaz@gmail.com>\n> ---\n>\n>  .gitignore       |    1 +\n>  Makefile         |    1 +\n>  builtin-commit.c |   45 +++++++++++-\n>  builtin-revert.c |    1 +\n>  builtin.h        |    1 +\n>  git.c            |    1 +\n>  wt-status.c      |  213 +++++++++++++++++++++++++++++++++++++++++++-----------\n>  wt-status.h      |    9 +++\n>  8 files changed, 227 insertions(+), 45 deletions(-)\n>\n> diff --git a/.gitignore b/.gitignore\n> index 1c57d4c..5666325 100644\n> --- a/.gitignore\n> +++ b/.gitignore\n> @@ -114,6 +114,7 @@ git-send-pack\n>  git-sh-setup\n>  git-shell\n>  git-shortlog\n> +git-shortstatus\n>  git-show\n>  git-show-branch\n>  git-show-index\n> diff --git a/Makefile b/Makefile\n> index 9f3a8ab..304f15d 100644\n> --- a/Makefile\n> +++ b/Makefile\n> @@ -329,6 +329,7 @@ BUILT_INS += git-repo-config$X\n>  BUILT_INS += git-show$X\n>  BUILT_INS += git-stage$X\n>  BUILT_INS += git-status$X\n> +BUILT_INS += git-shortstatus$X\n>  BUILT_INS += git-whatchanged$X\n>\n>  # what 'all' will build and 'install' will install, in gitexecdir\n> diff --git a/builtin-commit.c b/builtin-commit.c\n> index d6a3a62..624568c 100644\n> --- a/builtin-commit.c\n> +++ b/builtin-commit.c\n> @@ -14,6 +14,7 @@\n>  #include \"diffcore.h\"\n>  #include \"commit.h\"\n>  #include \"revision.h\"\n> +#include \"string-list.h\"\n>  #include \"wt-status.h\"\n>  #include \"run-command.h\"\n>  #include \"refs.h\"\n> @@ -21,7 +22,6 @@\n>  #include \"strbuf.h\"\n>  #include \"utf8.h\"\n>  #include \"parse-options.h\"\n> -#include \"string-list.h\"\n>  #include \"rerere.h\"\n>  #include \"unpack-trees.h\"\n>\n> @@ -821,6 +821,49 @@ static int parse_and_validate_options(int argc, const char *argv[],\n>        return argc;\n>  }\n>\n> +int cmd_shortstatus(int argc, const char **argv, const char *prefix)\n> +{\n> +       struct wt_status s;\n> +       int i;\n> +\n> +       read_cache();\n> +       refresh_cache(REFRESH_QUIET);\n> +       wt_status_prepare(&s);\n> +       wt_status_collect_changes(&s);\n> +       for (i = 0; i < s.change.nr; i++) {\n> +               struct wt_status_change_data *d;\n> +               struct string_list_item *it;\n> +               char pfx[1 + 3 + 1 + 1];\n> +\n> +               it = &(s.change.items[i]);\n> +               d = it->util;\n> +               switch (d->index_status) {\n> +                       case DIFF_STATUS_COPIED:\n> +                       case DIFF_STATUS_RENAMED:\n> +                               sprintf(pfx, \"%c%3d\",\n> +                                               d->index_status,\n> +                                               (int)(d->index_score * 100 / MAX_SCORE));\n> +                               break;\n> +                       case 0:\n> +                               memcpy(pfx, \"   \", 4);\n> +                               break;\n> +                       default:\n> +                               sprintf(pfx, \"%c          \", d->index_status);\n> +                               break;\n> +               }\n> +               if (!d->worktree_status)\n> +                       pfx[4] = ' ';\n> +               else\n> +                       pfx[4] = d->worktree_status;\n> +               pfx[5] = '\\0';\n> +               printf(\"%s \", pfx);\n> +               if (d->head_path)\n> +                       printf(\"%s -> \", d->head_path);\n> +               printf(\"%s\\n\", it->string);\n> +       }\n> +       return 0;\n> +}\n> +\n>  int cmd_status(int argc, const char **argv, const char *prefix)\n>  {\n>        const char *index_file;\n> diff --git a/builtin-revert.c b/builtin-revert.c\n> index d48313c..7dd7646 100644\n> --- a/builtin-revert.c\n> +++ b/builtin-revert.c\n> @@ -3,6 +3,7 @@\n>  #include \"object.h\"\n>  #include \"commit.h\"\n>  #include \"tag.h\"\n> +#include \"string-list.h\"\n>  #include \"wt-status.h\"\n>  #include \"run-command.h\"\n>  #include \"exec_cmd.h\"\n> diff --git a/builtin.h b/builtin.h\n> index 1495cf6..f054fc7 100644\n> --- a/builtin.h\n> +++ b/builtin.h\n> @@ -94,6 +94,7 @@ extern int cmd_shortlog(int argc, const char **argv, const char *prefix);\n>  extern int cmd_show(int argc, const char **argv, const char *prefix);\n>  extern int cmd_show_branch(int argc, const char **argv, const char *prefix);\n>  extern int cmd_status(int argc, const char **argv, const char *prefix);\n> +extern int cmd_shortstatus(int argc, const char **argv, const char *prefix);\n>  extern int cmd_stripspace(int argc, const char **argv, const char *prefix);\n>  extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);\n>  extern int cmd_tag(int argc, const char **argv, const char *prefix);\n> diff --git a/git.c b/git.c\n> index c2b181e..4c0fa44 100644\n> --- a/git.c\n> +++ b/git.c\n> @@ -344,6 +344,7 @@ static void handle_internal_command(int argc, const char **argv)\n>                { \"rm\", cmd_rm, RUN_SETUP },\n>                { \"send-pack\", cmd_send_pack, RUN_SETUP },\n>                { \"shortlog\", cmd_shortlog, USE_PAGER },\n> +               { \"shortstatus\", cmd_shortstatus, RUN_SETUP | NEED_WORK_TREE },\n>                { \"show-branch\", cmd_show_branch, RUN_SETUP },\n>                { \"show\", cmd_show, RUN_SETUP | USE_PAGER },\n>                { \"status\", cmd_status, RUN_SETUP | NEED_WORK_TREE },\n> diff --git a/wt-status.c b/wt-status.c\n> index 96ff2f8..18042dc 100644\n> --- a/wt-status.c\n> +++ b/wt-status.c\n> @@ -1,4 +1,5 @@\n>  #include \"cache.h\"\n> +#include \"string-list.h\"\n>  #include \"wt-status.h\"\n>  #include \"color.h\"\n>  #include \"object.h\"\n> @@ -56,6 +57,7 @@ void wt_status_prepare(struct wt_status *s)\n>        s->reference = \"HEAD\";\n>        s->fp = stdout;\n>        s->index_file = get_index_file();\n> +       s->change.strdup_strings = 1;\n>  }\n>\n>  static void wt_status_print_cached_header(struct wt_status *s)\n> @@ -98,18 +100,23 @@ static void wt_status_print_trailer(struct wt_status *s)\n>\n>  #define quote_path quote_path_relative\n>\n> -static void wt_status_print_filepair(struct wt_status *s,\n> -                                    int t, struct diff_filepair *p)\n> +static void wt_status_print_change_data(struct wt_status *s,\n> +                                               int t,\n> +                                               int status,\n> +                                               char *one_name,\n> +                                               char *two_name,\n> +                                               int score)\n>  {\n>        const char *c = color(t);\n>        const char *one, *two;\n>        struct strbuf onebuf = STRBUF_INIT, twobuf = STRBUF_INIT;\n>\n> -       one = quote_path(p->one->path, -1, &onebuf, s->prefix);\n> -       two = quote_path(p->two->path, -1, &twobuf, s->prefix);\n> +       one = quote_path(one_name, -1, &onebuf, s->prefix);\n> +       two = quote_path(two_name, -1, &twobuf, s->prefix);\n> +\n>\n>        color_fprintf(s->fp, color(WT_STATUS_HEADER), \"#\\t\");\n> -       switch (p->status) {\n> +       switch (status) {\n>        case DIFF_STATUS_ADDED:\n>                color_fprintf(s->fp, c, \"new file:   %s\", one);\n>                break;\n> @@ -135,64 +142,88 @@ static void wt_status_print_filepair(struct wt_status *s,\n>                color_fprintf(s->fp, c, \"unmerged:   %s\", one);\n>                break;\n>        default:\n> -               die(\"bug: unhandled diff status %c\", p->status);\n> +               die(\"bug: unhandled diff status %c\", status);\n>        }\n>        fprintf(s->fp, \"\\n\");\n>        strbuf_release(&onebuf);\n>        strbuf_release(&twobuf);\n>  }\n>\n> -static void wt_status_print_updated_cb(struct diff_queue_struct *q,\n> -               struct diff_options *options,\n> -               void *data)\n> +static void wt_status_collect_changed_cb(struct diff_queue_struct *q,\n> +                                                       struct diff_options *options,\n> +                                                       void *data)\n>  {\n>        struct wt_status *s = data;\n> -       int shown_header = 0;\n>        int i;\n> +\n> +       if (!q->nr)\n> +               return;\n> +       s->workdir_dirty = 1;\n>        for (i = 0; i < q->nr; i++) {\n> -               if (q->queue[i]->status == 'U')\n> -                       continue;\n> -               if (!shown_header) {\n> -                       wt_status_print_cached_header(s);\n> -                       s->commitable = 1;\n> -                       shown_header = 1;\n> -               }\n> -               wt_status_print_filepair(s, WT_STATUS_UPDATED, q->queue[i]);\n> +               struct diff_filepair *p;\n> +               struct string_list_item *it;\n> +               struct wt_status_change_data *d;\n> +\n> +               p = q->queue[i];\n> +\n> +               d = xcalloc(1, sizeof(*d));\n> +               d->worktree_status = p->status;\n> +               it = string_list_insert(p->one->path, &s->change);\n> +               it->util = d;\n>        }\n> -       if (shown_header)\n> -               wt_status_print_trailer(s);\n>  }\n>\n> -static void wt_status_print_changed_cb(struct diff_queue_struct *q,\n> -                        struct diff_options *options,\n> -                        void *data)\n> +static void wt_status_collect_updated_cb(struct diff_queue_struct *q,\n> +                                                       struct diff_options *options,\n> +                                                       void *data)\n>  {\n>        struct wt_status *s = data;\n>        int i;\n> -       if (q->nr) {\n> -               int has_deleted = 0;\n> -               s->workdir_dirty = 1;\n> -               for (i = 0; i < q->nr; i++)\n> -                       if (q->queue[i]->status == DIFF_STATUS_DELETED) {\n> -                               has_deleted = 1;\n> +\n> +       for (i = 0; i < q->nr; i++) {\n> +               struct diff_filepair *p;\n> +               struct string_list_item *it;\n> +               struct wt_status_change_data *d;\n> +\n> +               p = q->queue[i];\n> +               it = string_list_insert(p->two->path, &s->change);\n> +               d = it->util;\n> +               if (!d) {\n> +                       d = xcalloc(1, sizeof(*d));\n> +                       it->util = d;\n> +               }\n> +               d->index_status = p->status;\n> +               switch (p->status) {\n> +                       case DIFF_STATUS_COPIED:\n> +                       case DIFF_STATUS_RENAMED:\n> +                               d->head_path = xstrdup(p->one->path);\n> +                               d->index_score = p->score;\n>                                break;\n> -                       }\n> -               wt_status_print_dirty_header(s, has_deleted);\n> +               }\n>        }\n> -       for (i = 0; i < q->nr; i++)\n> -               wt_status_print_filepair(s, WT_STATUS_CHANGED, q->queue[i]);\n> -       if (q->nr)\n> -               wt_status_print_trailer(s);\n>  }\n>\n> -static void wt_status_print_updated(struct wt_status *s)\n> +static void wt_status_collect_changes_worktree(struct wt_status *s)\n>  {\n>        struct rev_info rev;\n> +\n> +       init_revisions(&rev, NULL);\n> +       setup_revisions(0, NULL, &rev, NULL);\n> +       rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;\n> +       rev.diffopt.format_callback = wt_status_collect_changed_cb;\n> +       rev.diffopt.format_callback_data = s;\n> +       run_diff_files(&rev, 0);\n> +}\n> +\n> +static void wt_status_collect_changes_index(struct wt_status *s)\n> +{\n> +       struct rev_info rev;\n> +\n>        init_revisions(&rev, NULL);\n>        setup_revisions(0, NULL, &rev,\n>                s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference);\n>        rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;\n> -       rev.diffopt.format_callback = wt_status_print_updated_cb;\n> +       rev.diffopt.format_callback = wt_status_collect_updated_cb;\n>        rev.diffopt.format_callback_data = s;\n>        rev.diffopt.detect_rename = 1;\n>        rev.diffopt.rename_limit = 200;\n> @@ -200,15 +231,107 @@ static void wt_status_print_updated(struct wt_status *s)\n>        run_diff_index(&rev, 1);\n>  }\n>\n> +static void wt_status_collect_changes_initial(struct wt_status *s)\n> +{\n> +       int i;\n> +\n> +       for (i = 0; i < active_nr; i++) {\n> +               struct string_list_item *it;\n> +               struct wt_status_change_data *d;\n> +\n> +               it = string_list_insert(active_cache[i]->name, &s->change);\n> +               d = it->util;\n> +               if (!d) {\n> +                       d = xcalloc(1, sizeof(*d));\n> +                       it->util = d;\n> +               }\n> +               d->index_status = DIFF_STATUS_ADDED;\n> +       }\n> +}\n> +\n> +void wt_status_collect_changes(struct wt_status *s)\n> +{\n> +       wt_status_collect_changes_worktree(s);\n> +\n> +       if (s->is_initial)\n> +               wt_status_collect_changes_initial(s);\n> +       else\n> +               wt_status_collect_changes_index(s);\n> +}\n> +\n> +static void wt_status_print_updated(struct wt_status *s)\n> +{\n> +       int shown_header = 0;\n> +       int i;\n> +\n> +       for (i = 0; i < s->change.nr; i++) {\n> +               struct wt_status_change_data *d;\n> +               struct string_list_item *it;\n> +               it = &(s->change.items[i]);\n> +               d = it->util;\n> +               if (!d->index_status)\n> +                       continue;\n> +               if (!shown_header) {\n> +                       wt_status_print_cached_header(s);\n> +                       s->commitable = 1;\n> +                       shown_header = 1;\n> +               }\n> +               wt_status_print_change_data(s, WT_STATUS_UPDATED,\n> +                               d->index_status,\n> +                               d->head_path ? d->head_path : it->string,\n> +                               it->string,\n> +                               d->index_score);\n> +       }\n> +       if (shown_header)\n> +               wt_status_print_trailer(s);\n> +}\n> +\n> +/*\n> + * -1 : has delete\n> + *  0 : no change\n> + *  1 : some change but no delete\n> + */\n> +static int wt_status_check_worktree_changes(struct wt_status *s)\n> +{\n> +       int i;\n> +       int changes = 0;\n> +\n> +       for (i = 0; i < s->change.nr; i++) {\n> +               struct wt_status_change_data *d;\n> +               d = s->change.items[i].util;\n> +               if (!d->worktree_status)\n> +                       continue;\n> +               changes = 1;\n> +               if (d->worktree_status == DIFF_STATUS_DELETED)\n> +                       return -1;\n> +       }\n> +       return changes;\n> +}\n> +\n>  static void wt_status_print_changed(struct wt_status *s)\n>  {\n> -       struct rev_info rev;\n> -       init_revisions(&rev, \"\");\n> -       setup_revisions(0, NULL, &rev, NULL);\n> -       rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;\n> -       rev.diffopt.format_callback = wt_status_print_changed_cb;\n> -       rev.diffopt.format_callback_data = s;\n> -       run_diff_files(&rev, 0);\n> +       int i;\n> +       int worktree_changes = wt_status_check_worktree_changes(s);\n> +\n> +       if (!worktree_changes)\n> +               return;\n> +\n> +       wt_status_print_dirty_header(s, worktree_changes < 0);\n> +\n> +       for (i = 0; i < s->change.nr; i++) {\n> +               struct wt_status_change_data *d;\n> +               struct string_list_item *it;\n> +               it = &(s->change.items[i]);\n> +               d = it->util;\n> +               if (!d->worktree_status)\n> +                       continue;\n> +               wt_status_print_change_data(s, WT_STATUS_CHANGED,\n> +                               d->worktree_status,\n> +                               it->string,\n> +                               it->string,\n> +                               0);\n> +       }\n> +       wt_status_print_trailer(s);\n>  }\n>\n>  static void wt_status_print_submodule_summary(struct wt_status *s)\n> @@ -338,6 +461,8 @@ void wt_status_print(struct wt_status *s)\n>                        wt_status_print_tracking(s);\n>        }\n>\n> +       wt_status_collect_changes(s);\n> +\n>        if (s->is_initial) {\n>                color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), \"#\");\n>                color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), \"# Initial commit\");\n> diff --git a/wt-status.h b/wt-status.h\n> index 78add09..00508c3 100644\n> --- a/wt-status.h\n> +++ b/wt-status.h\n> @@ -18,6 +18,13 @@ enum untracked_status_type {\n>  };\n>  extern enum untracked_status_type show_untracked_files;\n>\n> +struct wt_status_change_data {\n> +       int worktree_status;\n> +       int index_status;\n> +       int index_score;\n> +       char *head_path;\n> +};\n> +\n>  struct wt_status {\n>        int is_initial;\n>        char *branch;\n> @@ -33,6 +40,7 @@ struct wt_status {\n>        const char *index_file;\n>        FILE *fp;\n>        const char *prefix;\n> +       struct string_list change;\n>  };\n>\n>  int git_status_config(const char *var, const char *value, void *cb);\n> @@ -40,5 +48,6 @@ extern int wt_status_use_color;\n>  extern int wt_status_relative_paths;\n>  void wt_status_prepare(struct wt_status *s);\n>  void wt_status_print(struct wt_status *s);\n> +void wt_status_collect_changes(struct wt_status *s);\n>\n>  #endif /* STATUS_H */\n>\n"}]}