{"thread":{"id":"31921","subject":"[PATCH] git-status: show short sequencer state","startedAt":"2012-10-23T18:58:47Z","lastAt":"2012-10-23T18:58:47Z","messageCount":1,"participants":["Phil Hord"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"201744","messageId":"1351018727-26815-1-git-send-email-hordp@cisco.com","threadId":"31921","inReplyTo":null,"subject":"[PATCH] git-status: show short sequencer state","fromName":"Phil Hord","fromEmail":"hordp@cisco.com","sentAt":"2012-10-23T18:58:47Z","receivedAt":"2012-10-23T18:58:47Z","isPatch":true,"sender":{"key":"phil.hord@gmail.com","avatar":"https://avatars.githubusercontent.com/u/123908?v=4"},"body":"Recently git-status learned to display the state of the git\nsequencer in long form to help the user remember an interrupted\ncommand.  This information is also useful in short form to\nhumans and scripts, but no option is available to boil it down.\n\nTeach git-status to report the sequencer state in short form\nusing a new --sequencer (-S) switch.  Output zero or more\nsimple state token strings indicating the deduced state of the\ngit sequencer.\n\nIntroduce a common function to determine the current sequencer\nstate so the regular status function and this short version can\nshare common code.\n\nAdd a substate to wt_status_state to track more detailed\ninformation about a state, such as \"conflicted\" or \"resolved\".\nMove the am_empty_patch flage out of wt_status_state and into\nthis new substate.\n\nState token strings which may be emitted and their meanings:\n    merge              a git-merge is in progress\n    am                 a git-am is in progress\n    rebase             a git-rebase is in progress\n    rebase-interactive a git-rebase--interactive is in progress\n    cherry-pick        a git-cherry-pick is in progress\n    bisect             a git-bisect is in progress\n    conflicted         there are unresolved conflicts\n    resolved           conflicts have been resolved\n    editing            interactive rebase stopped to edit a commit\n    edited             interactive rebase edit has been edited\n    splitting          interactive rebase, commit is being split\n\nI also considered adding these tokens, but I decided it was not\nappropriate since these changes are not sequencer-related.  But\nit is possible I am being too short-sighted or have chosen the\nswitch name poorly.\n    clean\n    index\n    modified\n    untracked\n---\n Documentation/git-status.txt |  18 +++++++\n builtin/commit.c             |  12 ++++-\n wt-status.c                  | 125 +++++++++++++++++++++++++++++++++++--------\n wt-status.h                  |  14 ++++-\n 4 files changed, 145 insertions(+), 24 deletions(-)\n\ndiff --git a/Documentation/git-status.txt b/Documentation/git-status.txt\nindex 67e5f53..31ffabd 100644\n--- a/Documentation/git-status.txt\n+++ b/Documentation/git-status.txt\n@@ -38,6 +38,24 @@ OPTIONS\n \tacross git versions and regardless of user configuration. See\n \tbelow for details.\n \n+-S::\n+--sequence::\n+\tShow the git sequencer status. This shows zero or more tokens\n+\tdescribing the state of several git sequence operations. Each\n+\ttoken is separated by a newline.\n++\n+\tmerge              a merge is in progress\n+\tam                 an am is in progress\n+\trebase             a rebase is in progress\n+\trebase-interactive an interactive rebase is in progress\n+\tcherry-pick        a cherry-pick is in progress\n+\tbisect             a bisect is in progress\n+\tconflicted         there are unresolved conflicts\n+\tresolved           conflicts have been resolved\n+\tediting            interactive rebase stopped to edit a commit\n+\tedited             interactive rebase edit has been edited\n+\tsplitting          interactive rebase, commit is being split\n+\n -u[<mode>]::\n --untracked-files[=<mode>]::\n \tShow untracked files.\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex a17a5df..9706ed9 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -114,7 +114,8 @@ static struct strbuf message = STRBUF_INIT;\n static enum {\n \tSTATUS_FORMAT_LONG,\n \tSTATUS_FORMAT_SHORT,\n-\tSTATUS_FORMAT_PORCELAIN\n+\tSTATUS_FORMAT_PORCELAIN,\n+\tSTATUS_FORMAT_SEQUENCER\n } status_format = STATUS_FORMAT_LONG;\n \n static int opt_parse_m(const struct option *opt, const char *arg, int unset)\n@@ -454,6 +455,9 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int\n \tcase STATUS_FORMAT_PORCELAIN:\n \t\twt_porcelain_print(s);\n \t\tbreak;\n+\tcase STATUS_FORMAT_SEQUENCER:\n+\t\twt_sequencer_print(s);\n+\t\tbreak;\n \tcase STATUS_FORMAT_LONG:\n \t\twt_status_print(s);\n \t\tbreak;\n@@ -1156,6 +1160,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_BOOLEAN('b', \"branch\", &s.show_branch,\n \t\t\t    N_(\"show branch information\")),\n+\t\tOPT_SET_INT('S', \"sequence\", &status_format,\n+\t\t\t\tN_(\"show sequencer state\"),\n+\t\t\t\tSTATUS_FORMAT_SEQUENCER),\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@@ -1216,6 +1223,9 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \tcase STATUS_FORMAT_PORCELAIN:\n \t\twt_porcelain_print(&s);\n \t\tbreak;\n+\tcase STATUS_FORMAT_SEQUENCER:\n+\t\twt_sequencer_print(&s);\n+\t\tbreak;\n \tcase STATUS_FORMAT_LONG:\n \t\ts.verbose = verbose;\n \t\ts.ignore_submodule_arg = ignore_submodule_arg;\ndiff --git a/wt-status.c b/wt-status.c\nindex 2a9658b..81d91e3 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -781,7 +781,7 @@ static void show_merge_in_progress(struct wt_status *s,\n \t\t\t\tstruct wt_status_state *state,\n \t\t\t\tconst char *color)\n {\n-\tif (has_unmerged(s)) {\n+\tif (state->substate == WT_SUBSTATE_CONFLICTED) {\n \t\tstatus_printf_ln(s, color, _(\"You have unmerged paths.\"));\n \t\tif (advice_status_hints)\n \t\t\tstatus_printf_ln(s, color,\n@@ -802,11 +802,11 @@ static void show_am_in_progress(struct wt_status *s,\n {\n \tstatus_printf_ln(s, color,\n \t\t_(\"You are in the middle of an am session.\"));\n-\tif (state->am_empty_patch)\n+\tif (state->substate == WT_SUBSTATE_AM_EMPTY)\n \t\tstatus_printf_ln(s, color,\n \t\t\t_(\"The current patch is empty.\"));\n \tif (advice_status_hints) {\n-\t\tif (!state->am_empty_patch)\n+\t\tif (state->substate == WT_SUBSTATE_CONFLICTED)\n \t\t\tstatus_printf_ln(s, color,\n \t\t\t\t_(\"  (fix conflicts and then run \\\"git am --resolved\\\")\"));\n \t\tstatus_printf_ln(s, color,\n@@ -867,9 +867,7 @@ static void show_rebase_in_progress(struct wt_status *s,\n \t\t\t\tstruct wt_status_state *state,\n \t\t\t\tconst char *color)\n {\n-\tstruct stat st;\n-\n-\tif (has_unmerged(s)) {\n+\tif (state->substate == WT_SUBSTATE_CONFLICTED) {\n \t\tstatus_printf_ln(s, color, _(\"You are currently rebasing.\"));\n \t\tif (advice_status_hints) {\n \t\t\tstatus_printf_ln(s, color,\n@@ -879,19 +877,19 @@ static void show_rebase_in_progress(struct wt_status *s,\n \t\t\tstatus_printf_ln(s, color,\n \t\t\t\t_(\"  (use \\\"git rebase --abort\\\" to check out the original branch)\"));\n \t\t}\n-\t} else if (state->rebase_in_progress || !stat(git_path(\"MERGE_MSG\"), &st)) {\n+\t} else if (state->substate == WT_SUBSTATE_RESOLVED) {\n \t\tstatus_printf_ln(s, color, _(\"You are currently rebasing.\"));\n \t\tif (advice_status_hints)\n \t\t\tstatus_printf_ln(s, color,\n \t\t\t\t_(\"  (all conflicts fixed: run \\\"git rebase --continue\\\")\"));\n-\t} else if (split_commit_in_progress(s)) {\n+\t} else if (state->substate == WT_SUBSTATE_SPLITTING) {\n \t\tstatus_printf_ln(s, color, _(\"You are currently splitting a commit during a rebase.\"));\n \t\tif (advice_status_hints)\n \t\t\tstatus_printf_ln(s, color,\n \t\t\t\t_(\"  (Once your working directory is clean, run \\\"git rebase --continue\\\")\"));\n \t} else {\n \t\tstatus_printf_ln(s, color, _(\"You are currently editing a commit during a rebase.\"));\n-\t\tif (advice_status_hints && !s->amend) {\n+\t\tif (advice_status_hints && state->substate == WT_SUBSTATE_EDITING) {\n \t\t\tstatus_printf_ln(s, color,\n \t\t\t\t_(\"  (use \\\"git commit --amend\\\" to amend the current commit)\"));\n \t\t\tstatus_printf_ln(s, color,\n@@ -907,7 +905,7 @@ static void show_cherry_pick_in_progress(struct wt_status *s,\n {\n \tstatus_printf_ln(s, color, _(\"You are currently cherry-picking.\"));\n \tif (advice_status_hints) {\n-\t\tif (has_unmerged(s))\n+\t\tif (state->substate == WT_SUBSTATE_CONFLICTED)\n \t\t\tstatus_printf_ln(s, color,\n \t\t\t\t_(\"  (fix conflicts and run \\\"git commit\\\")\"));\n \t\telse\n@@ -928,34 +926,68 @@ static void show_bisect_in_progress(struct wt_status *s,\n \twt_status_print_trailer(s);\n }\n \n-static void wt_status_print_state(struct wt_status *s)\n+static void wt_status_get_state(struct wt_status *s , struct wt_status_state *state)\n {\n-\tconst char *state_color = color(WT_STATUS_HEADER, s);\n-\tstruct wt_status_state state;\n \tstruct stat st;\n \n-\tmemset(&state, 0, sizeof(state));\n+\tmemset(state, 0, sizeof(*state));\n \n+\t// Determine main sequencer activity\n \tif (!stat(git_path(\"MERGE_HEAD\"), &st)) {\n-\t\tstate.merge_in_progress = 1;\n+\t\tstate->merge_in_progress = 1;\n \t} else if (!stat(git_path(\"rebase-apply\"), &st)) {\n \t\tif (!stat(git_path(\"rebase-apply/applying\"), &st)) {\n-\t\t\tstate.am_in_progress = 1;\n+\t\t\tstate->am_in_progress = 1;\n \t\t\tif (!stat(git_path(\"rebase-apply/patch\"), &st) && !st.st_size)\n-\t\t\t\tstate.am_empty_patch = 1;\n+\t\t\t\tstate->substate = WT_SUBSTATE_AM_EMPTY;\n+\t\t\telse\n+\t\t\t\tstate->substate = WT_SUBSTATE_CONFLICTED;\n \t\t} else {\n-\t\t\tstate.rebase_in_progress = 1;\n+\t\t\tstate->rebase_in_progress = 1;\n \t\t}\n \t} else if (!stat(git_path(\"rebase-merge\"), &st)) {\n \t\tif (!stat(git_path(\"rebase-merge/interactive\"), &st))\n-\t\t\tstate.rebase_interactive_in_progress = 1;\n+\t\t\tstate->rebase_interactive_in_progress = 1;\n \t\telse\n-\t\t\tstate.rebase_in_progress = 1;\n+\t\t\tstate->rebase_in_progress = 1;\n \t} else if (!stat(git_path(\"CHERRY_PICK_HEAD\"), &st)) {\n-\t\tstate.cherry_pick_in_progress = 1;\n+\t\tstate->cherry_pick_in_progress = 1;\n \t}\n \tif (!stat(git_path(\"BISECT_LOG\"), &st))\n-\t\tstate.bisect_in_progress = 1;\n+\t\tstate->bisect_in_progress = 1;\n+\n+\t// Check for unmerged files\n+\tif (state->rebase_in_progress || state->rebase_interactive_in_progress ||\n+\t\t\tstate->cherry_pick_in_progress || state->merge_in_progress) {\n+\t\tif (has_unmerged(s)) {\n+\t\t\tstate->substate = WT_SUBSTATE_CONFLICTED;\n+\t\t} else {\n+\t\t\tstate->substate = WT_SUBSTATE_RESOLVED;\n+\t\t}\n+\t}\n+\n+\t// Interactive Rebase is more nuanced\n+\tif (state->rebase_interactive_in_progress && state->substate != WT_SUBSTATE_CONFLICTED) {\n+\t\tif (!stat(git_path(\"MERGE_MSG\"), &st)) {\n+\t\t\tstate->substate = WT_SUBSTATE_RESOLVED;\n+\t\t} else if (split_commit_in_progress(s)) {\n+\t\t\tstate->substate = WT_SUBSTATE_SPLITTING;\n+\t\t} else {\n+\t\t\tif (s->amend) {\n+\t\t\t\tstate->substate = WT_SUBSTATE_EDITED;\n+\t\t\t} else {\n+\t\t\t\tstate->substate = WT_SUBSTATE_EDITING;\n+\t\t\t}\n+\t\t}\n+\t}\n+}\n+\n+static void wt_status_print_state(struct wt_status *s)\n+{\n+\tconst char *state_color = color(WT_STATUS_HEADER, s);\n+\tstruct wt_status_state state;\n+\n+\twt_status_get_state(s, &state);\n \n \tif (state.merge_in_progress)\n \t\tshow_merge_in_progress(s, &state, state_color);\n@@ -1192,6 +1224,55 @@ static void wt_shortstatus_print_tracking(struct wt_status *s)\n \tfputc(s->null_termination ? '\\0' : '\\n', s->fp);\n }\n \n+static void wt_print_token(struct wt_status *s, const char *token)\n+{\n+\tfprintf(s->fp, \"%s\", token);\n+\tfputc(s->null_termination ? '\\0' : '\\n', s->fp);\n+}\n+\n+void wt_sequencer_print(struct wt_status *s)\n+{\n+\tstruct wt_status_state state;\n+\n+\twt_status_get_state(s, &state);\n+\n+\tif (state.merge_in_progress)\n+\t\twt_print_token(s, \"merge\");\n+\tif (state.am_in_progress)\n+\t\twt_print_token(s, \"am\");\n+\tif (state.rebase_in_progress)\n+\t\twt_print_token(s, \"rebase\");\n+\tif (state.rebase_interactive_in_progress)\n+\t\twt_print_token(s, \"rebase-interactive\");\n+\tif (state.cherry_pick_in_progress)\n+\t\twt_print_token(s, \"cherry-pick\");\n+\tif (state.bisect_in_progress)\n+\t\twt_print_token(s, \"bisect\");\n+\n+\tswitch (state.substate) {\n+\tcase WT_SUBSTATE_NOMINAL:\n+\t\tbreak;\n+\tcase WT_SUBSTATE_CONFLICTED:\n+\t\twt_print_token(s, \"conflicted\");\n+\t\tbreak;\n+\tcase WT_SUBSTATE_RESOLVED:\n+\t\twt_print_token(s, \"resolved\");\n+\t\tbreak;\n+\tcase WT_SUBSTATE_EDITED:\n+\t\twt_print_token(s, \"edited\");\n+\t\tbreak;\n+\tcase WT_SUBSTATE_EDITING:\n+\t\twt_print_token(s, \"editing\");\n+\t\tbreak;\n+\tcase WT_SUBSTATE_SPLITTING:\n+\t\twt_print_token(s, \"splitting\");\n+\t\tbreak;\n+\tcase WT_SUBSTATE_AM_EMPTY:\n+\t\twt_print_token(s, \"am-empty\");\n+\t\tbreak;\n+\t}\n+}\n+\n void wt_shortstatus_print(struct wt_status *s)\n {\n \tint i;\ndiff --git a/wt-status.h b/wt-status.h\nindex 236b41f..900889c 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -59,6 +59,7 @@ struct wt_status {\n \tunsigned colopts;\n \tint null_termination;\n \tint show_branch;\n+\tint show_sequencer;\n \n \t/* These are computed during processing of the individual sections */\n \tint commitable;\n@@ -71,14 +72,24 @@ struct wt_status {\n \tstruct string_list ignored;\n };\n \n+enum wt_status_substate {\n+\tWT_SUBSTATE_NOMINAL = 0,\n+\tWT_SUBSTATE_CONFLICTED,\n+\tWT_SUBSTATE_RESOLVED,\n+\tWT_SUBSTATE_SPLITTING,\n+\tWT_SUBSTATE_EDITING,\n+\tWT_SUBSTATE_EDITED,\n+\tWT_SUBSTATE_AM_EMPTY,\n+};\n+\n struct wt_status_state {\n \tint merge_in_progress;\n \tint am_in_progress;\n-\tint am_empty_patch;\n \tint rebase_in_progress;\n \tint rebase_interactive_in_progress;\n \tint cherry_pick_in_progress;\n \tint bisect_in_progress;\n+\tenum wt_status_substate substate;\n };\n \n void wt_status_prepare(struct wt_status *s);\n@@ -86,6 +97,7 @@ void wt_status_print(struct wt_status *s);\n void wt_status_collect(struct wt_status *s);\n \n void wt_shortstatus_print(struct wt_status *s);\n+void wt_sequencer_print(struct wt_status *s);\n void wt_porcelain_print(struct wt_status *s);\n \n void status_printf_ln(struct wt_status *s, const char *color, const char *fmt, ...)\n-- \n1.8.0.2.gc921d59.dirty\n"}]}