{"thread":{"id":"36414","subject":"[PATCH] hook for git status","startedAt":"2014-04-15T15:24:33Z","lastAt":"2014-04-15T15:24:33Z","messageCount":1,"participants":["Suvorov Ivan"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"238887","messageId":"1397575473.625837277@f361.i.mail.ru","threadId":"36414","inReplyTo":null,"subject":"[PATCH] hook for git status","fromName":"Suvorov Ivan","fromEmail":"sv_91@inbox.ru","sentAt":"2014-04-15T15:24:33Z","receivedAt":"2014-04-15T15:24:33Z","isPatch":true,"sender":{"key":"sv_91@inbox.ru","avatar":null},"body":"From 0f435feee87dc96b2e4890a5a5de7b1d0c0befe1 Mon Sep 17 00:00:00 2001\nFrom: magisterRab <sv_91@inbox.ru>\nDate: Tue, 1 Apr 2014 17:02:10 +0400\nSubject: [PATCH] hook for git status\n\nSigned-off-by: Ivan Suvorov <sv_91@inbox.ru>\n---\nhook for git status allows you to display additional fields when you run git status.\nFor example, for a hook with a content of\n\nSTATUS_FILE=$1\nNEW_SCOPE=$2\n\ncat > \"$STATUS_FILE\" <<EOF\nBinary files\nadd --binary\nlib.lib\nlib.a\nlib.o\n$NEW_SCOPE\nOther files\nadd --other\ndll.lib\ndll.a\ndll.o\nEOF\n\nhere's how it will look conclusion\n\nBinary files:\n (use \"git add --binary <file>...\" to include in what will be committed)\n\n lib.a\n lib.lib\n lib.o\n\nOther files:\n (use \"git add --other <file>...\" to include in what will be committed)\n\n dll.a\n dll.lib\n dll.o\n\n Documentation/git-status.txt | 10 +++\n Documentation/githooks.txt | 9 +++\n builtin/commit.c | 7 +++\n t/t7513-status-hook.sh | 137 +++++++++++++++++++++++++++++++++++++++++\n templates/hooks--status.sample | 36 +++++++++++\n wt-status.c | 88 ++++++++++++++++++++++++++\n wt-status.h | 1 +\n 7 files changed, 288 insertions(+)\n create mode 100644 t/t7513-status-hook.sh\n create mode 100755 templates/hooks--status.sample\n\ndiff --git a/Documentation/git-status.txt b/Documentation/git-status.txt\nindex def635f..6c81323 100644\n--- a/Documentation/git-status.txt\n+++ b/Documentation/git-status.txt\n@@ -94,6 +94,10 @@ configuration variable documented in linkgit:git-config[1].\n respectively.\n\n\n+--no-run-hook::\n+ This option bypasses the status hooks.\n+ See also linkgit:githooks[5].\n+ \n OUTPUT\n ------\n The output from this command is designed to be used as a commit\n@@ -218,9 +222,15 @@ ignored submodules you can either use the --ignore-submodules=dirty command\n line option or the 'git submodule summary' command, which shows a similar\n output but does not honor these settings.\n\n+HOOKS\n+-----\n+This command can run `status` hooks. See linkgit:githooks[5] for more\n+information.\n+\n SEE ALSO\n --------\n linkgit:gitignore[5]\n+linkgit:githooks[5]\n\n GIT\n ---\ndiff --git a/Documentation/githooks.txt b/Documentation/githooks.txt\nindex d954bf6..e3e3206 100644\n--- a/Documentation/githooks.txt\n+++ b/Documentation/githooks.txt\n@@ -381,6 +381,15 @@ rebase::\n The commits are guaranteed to be listed in the order that they were\n processed by rebase.\n\n+status\n+~~~~~~~~~~~\n+\n+This hook is invoked by 'git status', and can be bypassed\n+with `--no-run-hook` option. Exiting with non-zero status from this script\n+causes the 'git status' to abort.\n+\n+The default 'status' hook, when enabled, adds to the output \n+of the command 'git status' several files names.\n\n GIT\n ---\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 9cfef6c..16c430f 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -122,6 +122,7 @@ static enum commit_whence whence;\n static int sequencer_in_use;\n static int use_editor = 1, include_status = 1;\n static int show_ignored_in_status, have_option_m;\n+static int no_run_hook = 0;\n static const char *only_include_assumed;\n static struct strbuf message = STRBUF_INIT;\n\n@@ -1276,6 +1277,8 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n N_(\"ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)\"),\n PARSE_OPT_OPTARG, NULL, (intptr_t)\"all\" },\n OPT_COLUMN(0, \"column\", &s.colopts, N_(\"list untracked files in columns\")),\n+ OPT_BOOL(0, \"no-run-hook\", &no_run_hook,\n+ N_(\"bypass status hook\")),\n OPT_END(),\n };\n\n@@ -1296,6 +1299,10 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n PATHSPEC_PREFER_FULL,\n prefix, argv);\n\n+ if (no_run_hook)\n+ s.run_hook = 0;\n+ else\n+ s.run_hook = 1;\n read_cache_preload(&s.pathspec);\n refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &s.pathspec, NULL, NULL);\n\ndiff --git a/t/t7513-status-hook.sh b/t/t7513-status-hook.sh\nnew file mode 100644\nindex 0000000..584137e\n--- /dev/null\n+++ b/t/t7513-status-hook.sh\n@@ -0,0 +1,137 @@\n+#!/bin/sh\n+\n+test_description='status hook'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'with no hook' '\n+\n+ echo \"foo\" > file &&\n+ git status\n+\n+'\n+\n+test_expect_success '--no-run-hook with no hook' '\n+\n+ echo \"bar\" > file &&\n+ git status --no-run-hook\n+\n+'\n+\n+test_expect_success 'with no hook short' '\n+\n+ echo \"foo\" > file &&\n+ git status -s\n+\n+'\n+\n+test_expect_success '--no-run-hook with no hook short' '\n+\n+ echo \"bar\" > file &&\n+ git status --no-run-hook -s\n+\n+'\n+\n+# now install hook that always succeeds\n+HOOKDIR=\"$(git rev-parse --git-dir)/hooks\"\n+HOOK=\"$HOOKDIR/status\"\n+mkdir -p \"$HOOKDIR\"\n+cat > \"$HOOK\" <<EOF\n+#!/bin/sh\n+exit 0\n+EOF\n+chmod +x \"$HOOK\"\n+\n+test_expect_success 'with succeeding hook' '\n+\n+ echo \"more\" >> file &&\n+ git status\n+\n+'\n+\n+test_expect_success '--no-run-hook with succeeding hook' '\n+\n+ echo \"even more\" >> file &&\n+ git status --no-run-hook\n+\n+'\n+\n+test_expect_success 'with succeeding hook short' '\n+\n+ echo \"more\" >> file &&\n+ git status -s\n+\n+'\n+\n+test_expect_success '--no-run-hook with succeeding hook short' '\n+\n+ echo \"even more\" >> file &&\n+ git status --no-run-hook -s\n+\n+'\n+\n+# now a hook that fails\n+cat > \"$HOOK\" <<EOF\n+#!/bin/sh\n+exit 1\n+EOF\n+\n+test_expect_success 'with failing hook' '\n+\n+ echo \"another\" >> file &&\n+ test_must_fail git status\n+\n+'\n+\n+test_expect_success '--no-run-hook with failing hook' '\n+\n+ echo \"stuff\" >> file &&\n+ git status --no-run-hook\n+\n+'\n+\n+test_expect_success 'with failing hook short' '\n+\n+ echo \"another\" >> file &&\n+ test_must_fail git status -s\n+\n+'\n+\n+test_expect_success '--no-run-hook with failing hook short' '\n+\n+ echo \"stuff\" >> file &&\n+ git status --no-run-hook -s\n+\n+'\n+\n+chmod -x \"$HOOK\"\n+test_expect_success POSIXPERM 'with non-executable hook' '\n+\n+ echo \"content\" >> file &&\n+ git status\n+\n+'\n+\n+test_expect_success POSIXPERM '--no-run-hook with non-executable hook' '\n+\n+ echo \"more content\" >> file &&\n+ git status --no-run-hook\n+\n+'\n+\n+test_expect_success POSIXPERM 'with non-executable hook short' '\n+\n+ echo \"content\" >> file &&\n+ git status -s\n+\n+'\n+\n+test_expect_success POSIXPERM '--no-run-hook with non-executable hook short' '\n+\n+ echo \"more content\" >> file &&\n+ git status --no-run-hook -s\n+\n+'\n+chmod +x \"$HOOK\"\n+\n+test_done\ndiff --git a/templates/hooks--status.sample b/templates/hooks--status.sample\nnew file mode 100755\nindex 0000000..8739b44\n--- /dev/null\n+++ b/templates/hooks--status.sample\n@@ -0,0 +1,36 @@\n+#!/bin/sh\n+#\n+# An example hook script to verify what is about to be status.\n+# Called by \"git status\" with no arguments. The hook should\n+# exit with non-zero status after issuing an appropriate message if\n+# it wants to stop the status.\n+#\n+# To enable this hook, rename this file to \"status\".\n+#\n+# This hook is called with the following parameters:\n+#\n+# $1 -- Name and path of the file, from which it will take additional information for hook status\n+# $2 -- line separating the blocks of information\n+#\n+# Information in the file must be contained in the blocks, the blocks\n+# must be separated by a line separating. The first line of the block\n+# must contain the type of files in the block, the second line should\n+# contain a command to include in what will be committed, continue\n+# until the end of the block or file files names, one file name per line.\n+\n+STATUS_FILE=$1\n+NEW_SCOPE=$2\n+\n+cat > \"$STATUS_FILE\" <<EOF\n+Binary files\n+add --binary\n+lib.lib\n+lib.a\n+lib.o\n+$NEW_SCOPE\n+Other files\n+add --other\n+dll.lib\n+dll.a\n+dll.o\n+EOF\ndiff --git a/wt-status.c b/wt-status.c\nindex ec7344e..f5d5f72 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -1406,6 +1406,60 @@ void wt_status_print(struct wt_status *s)\n } else\n printf(_(\"nothing to commit, working directory clean\\n\"));\n }\n+\n+ if (s->run_hook) {\n+ const char *NEW_SCOPE = \"===================\";\n+\n+ const char *path_to_status_file = git_path(\"STATUS-MSG\");\n+ fclose(fopen(path_to_status_file, \"w\"));\n+\n+ if (run_hook_le(NULL, \"status\", path_to_status_file, NEW_SCOPE, NULL) != 0)\n+ die(_(\"hook status has error\"));\n+\n+ FILE *f = fopen(path_to_status_file, \"rt\");\n+ struct strbuf buf;\n+ strbuf_init(&buf, 0);\n+\n+ struct string_list other_files;\n+ struct strbuf description_files;\n+ strbuf_init(&description_files, 0);\n+ struct strbuf operation_files;\n+ strbuf_init(&operation_files, 0);\n+ int scope_line_number = 0;\n+\n+ while (strbuf_getline(&buf, f, '\\n') != EOF) {\n+ if (strcmp(buf.buf, NEW_SCOPE) == 0) {\n+ scope_line_number = 0;\n+ wt_status_print_other(s, &other_files,\n+ _(description_files.buf), operation_files.buf);\n+\n+ strbuf_release(&description_files);\n+ strbuf_release(&operation_files);\n+ string_list_clear(&other_files, 1);\n+ } else {\n+ if (scope_line_number == 0) {\n+ strbuf_init(&description_files, 0);\n+ strbuf_addbuf(&description_files, &buf);\n+ } else if (scope_line_number == 1) {\n+ strbuf_init(&operation_files, 0);\n+ strbuf_addbuf(&operation_files, &buf);\n+ } else {\n+ size_t size_buf;\n+ const char *line = strbuf_detach(&buf, &size_buf);\n+ string_list_insert(&other_files, line);\n+ }\n+ scope_line_number++;\n+ }\n+ }\n+ wt_status_print_other(s, &other_files,\n+ _(description_files.buf), operation_files.buf);\n+ strbuf_release(&buf);\n+ strbuf_release(&description_files);\n+ strbuf_release(&operation_files);\n+ string_list_clear(&other_files, 1);\n+\n+ fclose(f);\n+ }\n }\n\n static void wt_shortstatus_unmerged(struct string_list_item *it,\n@@ -1599,6 +1653,40 @@ void wt_shortstatus_print(struct wt_status *s)\n it = &(s->ignored.items[i]);\n wt_shortstatus_other(it, s, \"!!\");\n }\n+\n+ if (s->run_hook) {\n+ const char *NEW_SCOPE = \"===================\";\n+\n+ const char *path_to_status_file = git_path(\"STATUS-MSG\");\n+ fclose(fopen(path_to_status_file, \"w\"));\n+\n+ if (run_hook_le(NULL, \"status\", path_to_status_file, NEW_SCOPE, NULL) != 0)\n+ die(_(\"hook status has error\"));\n+\n+ FILE *f = fopen(path_to_status_file, \"rt\");\n+ struct strbuf buf;\n+ strbuf_init(&buf, 0);\n+\n+ int scope_line_number = 0;\n+\n+ while (strbuf_getline(&buf, f, '\\n') != EOF) {\n+ if (strcmp(buf.buf, NEW_SCOPE) == 0) {\n+ scope_line_number = 0;\n+ } else {\n+ if (scope_line_number == 0) {\n+ } else if (scope_line_number == 1) {\n+ } else {\n+ struct string_list_item it;\n+ it.string = buf.buf;\n+ wt_shortstatus_other(&it, s, \"::\");\n+ }\n+ scope_line_number++;\n+ }\n+ }\n+ strbuf_release(&buf);\n+\n+ fclose(f);\n+ }\n }\n\n void wt_porcelain_print(struct wt_status *s)\ndiff --git a/wt-status.h b/wt-status.h\nindex 283a9fe..ff94b22 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -55,6 +55,7 @@ struct wt_status {\n int relative_paths;\n int submodule_summary;\n int show_ignored_files;\n+ int run_hook;\n enum untracked_status_type show_untracked_files;\n const char *ignore_submodule_arg;\n char color_palette[WT_STATUS_MAXSLOT][COLOR_MAXLEN];\n-- \n1.8.5.2.msysgit.0"}]}