{"thread":{"id":"50075","subject":"[PATCH 1/7] diff: export diffstat interface","startedAt":"2018-12-20T12:09:22Z","lastAt":"2019-03-08T20:48:30Z","messageCount":76,"participants":["Daniel Ferreira via GitGitGadget","Slavica Djukic via GitGitGadget","Johannes Schindelin","Slavica Djukic","Phillip Wood","Slavica Đukić via GitGitGadget","Ævar Arnfjörð Bjarmason","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":7},"messages":[{"id":"365623","messageId":"737767b6f4e0367a7797020bd07a6b4357a47413.1545307756.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.git.gitgitgadget@gmail.com","subject":"[PATCH 1/7] diff: export diffstat interface","fromName":"Daniel Ferreira via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2018-12-20T12:09:19Z","receivedAt":"2018-12-20T12:09:22Z","isPatch":true,"sender":{"key":"bnmvco@gmail.com","avatar":"https://avatars.githubusercontent.com/u/735397?v=4"},"body":"From: Daniel Ferreira <bnmvco@gmail.com>\n\nMake the diffstat interface (namely, the diffstat_t struct and\ncompute_diffstat) no longer be internal to diff.c and allow it to be used\nby other parts of git.\n\nThis is helpful for code that may want to easily extract information\nfrom files using the diff machinery, while flushing it differently from\nhow the show_* functions used by diff_flush() do it. One example is the\nbuiltin implementation of git-add--interactive's status.\n\nSigned-off-by: Daniel Ferreira <bnmvco@gmail.com>\nSigned-off-by: Slavica Djukic <slawica92@hotmail.com>\n---\n diff.c | 36 ++++++++++++++----------------------\n diff.h | 18 ++++++++++++++++++\n 2 files changed, 32 insertions(+), 22 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex dc9965e836..46a7d8cf29 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -2421,22 +2421,6 @@ static void pprint_rename(struct strbuf *name, const char *a, const char *b)\n \t}\n }\n \n-struct diffstat_t {\n-\tint nr;\n-\tint alloc;\n-\tstruct diffstat_file {\n-\t\tchar *from_name;\n-\t\tchar *name;\n-\t\tchar *print_name;\n-\t\tconst char *comments;\n-\t\tunsigned is_unmerged:1;\n-\t\tunsigned is_binary:1;\n-\t\tunsigned is_renamed:1;\n-\t\tunsigned is_interesting:1;\n-\t\tuintmax_t added, deleted;\n-\t} **files;\n-};\n-\n static struct diffstat_file *diffstat_add(struct diffstat_t *diffstat,\n \t\t\t\t\t  const char *name_a,\n \t\t\t\t\t  const char *name_b)\n@@ -5922,12 +5906,7 @@ void diff_flush(struct diff_options *options)\n \t    dirstat_by_line) {\n \t\tstruct diffstat_t diffstat;\n \n-\t\tmemset(&diffstat, 0, sizeof(struct diffstat_t));\n-\t\tfor (i = 0; i < q->nr; i++) {\n-\t\t\tstruct diff_filepair *p = q->queue[i];\n-\t\t\tif (check_pair_status(p))\n-\t\t\t\tdiff_flush_stat(p, options, &diffstat);\n-\t\t}\n+\t\tcompute_diffstat(options, &diffstat);\n \t\tif (output_format & DIFF_FORMAT_NUMSTAT)\n \t\t\tshow_numstat(&diffstat, options);\n \t\tif (output_format & DIFF_FORMAT_DIFFSTAT)\n@@ -6227,6 +6206,19 @@ static int is_submodule_ignored(const char *path, struct diff_options *options)\n \treturn ignored;\n }\n \n+void compute_diffstat(struct diff_options *options, struct diffstat_t *diffstat)\n+{\n+\tint i;\n+\tstruct diff_queue_struct *q = &diff_queued_diff;\n+\n+\tmemset(diffstat, 0, sizeof(struct diffstat_t));\n+\tfor (i = 0; i < q->nr; i++) {\n+\t\tstruct diff_filepair *p = q->queue[i];\n+\t\tif (check_pair_status(p))\n+\t\t\tdiff_flush_stat(p, options, diffstat);\n+\t}\n+}\n+\n void diff_addremove(struct diff_options *options,\n \t\t    int addremove, unsigned mode,\n \t\t    const struct object_id *oid,\ndiff --git a/diff.h b/diff.h\nindex ce5e8a8183..7809db3039 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -239,6 +239,22 @@ void diff_emit_submodule_error(struct diff_options *o, const char *err);\n void diff_emit_submodule_pipethrough(struct diff_options *o,\n \t\t\t\t     const char *line, int len);\n \n+struct diffstat_t {\n+\tint nr;\n+\tint alloc;\n+\tstruct diffstat_file {\n+\t\tchar *from_name;\n+\t\tchar *name;\n+\t\tchar *print_name;\n+\t\tconst char *comments;\n+\t\tunsigned is_unmerged:1;\n+\t\tunsigned is_binary:1;\n+\t\tunsigned is_renamed:1;\n+\t\tunsigned is_interesting:1;\n+\t\tuintmax_t added, deleted;\n+\t} **files;\n+};\n+\n enum color_diff {\n \tDIFF_RESET = 0,\n \tDIFF_CONTEXT = 1,\n@@ -327,6 +343,8 @@ void diff_change(struct diff_options *,\n \n struct diff_filepair *diff_unmerge(struct diff_options *, const char *path);\n \n+void compute_diffstat(struct diff_options *options, struct diffstat_t *diffstat);\n+\n #define DIFF_SETUP_REVERSE      \t1\n #define DIFF_SETUP_USE_SIZE_CACHE\t4\n \n-- \ngitgitgadget\n\n"},{"id":"365624","messageId":"91b1963125f7dfdd46fe5166125ee59ef2557b13.1545307756.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.git.gitgitgadget@gmail.com","subject":"[PATCH 2/7] add--helper: create builtin helper for interactive add","fromName":"Daniel Ferreira via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2018-12-20T12:09:20Z","receivedAt":"2018-12-20T12:09:24Z","isPatch":true,"sender":{"key":"bnmvco@gmail.com","avatar":"https://avatars.githubusercontent.com/u/735397?v=4"},"body":"From: Daniel Ferreira <bnmvco@gmail.com>\n\nCreate a builtin helper for git-add--interactive, which right now is not\nable to do anything.\n\nThis is the first step in an effort to convert git-add--interactive.perl\nto a C builtin, in search for better portability, expressibility and\nperformance (specially on non-POSIX systems like Windows).\n\nAdditionally, an eventual complete port of git-add--interactive would\nremove the last \"big\" Git script to have Perl as a dependency, allowing\nmost Git users to have a NOPERL build running without big losses.\n\nSigned-off-by: Daniel Ferreira <bnmvco@gmail.com>\nSigned-off-by: Slavica Djukic <slawica92@hotmail.com>\n---\n .gitignore            | 1 +\n Makefile              | 1 +\n builtin.h             | 1 +\n builtin/add--helper.c | 6 ++++++\n git.c                 | 1 +\n 5 files changed, 10 insertions(+)\n create mode 100644 builtin/add--helper.c\n\ndiff --git a/.gitignore b/.gitignore\nindex 0d77ea5894..2ee71ed217 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -15,6 +15,7 @@\n /git\n /git-add\n /git-add--interactive\n+/git-add--helper\n /git-am\n /git-annotate\n /git-apply\ndiff --git a/Makefile b/Makefile\nindex 1a44c811aa..9c84b80739 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1023,6 +1023,7 @@ LIB_OBJS += xdiff-interface.o\n LIB_OBJS += zlib.o\n \n BUILTIN_OBJS += builtin/add.o\n+BUILTIN_OBJS += builtin/add--helper.o\n BUILTIN_OBJS += builtin/am.o\n BUILTIN_OBJS += builtin/annotate.o\n BUILTIN_OBJS += builtin/apply.o\ndiff --git a/builtin.h b/builtin.h\nindex 6538932e99..dd811ef7d5 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -128,6 +128,7 @@ extern void setup_auto_pager(const char *cmd, int def);\n extern int is_builtin(const char *s);\n \n extern int cmd_add(int argc, const char **argv, const char *prefix);\n+extern int cmd_add__helper(int argc, const char **argv, const char *prefix);\n extern int cmd_am(int argc, const char **argv, const char *prefix);\n extern int cmd_annotate(int argc, const char **argv, const char *prefix);\n extern int cmd_apply(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/add--helper.c b/builtin/add--helper.c\nnew file mode 100644\nindex 0000000000..6a97f0e191\n--- /dev/null\n+++ b/builtin/add--helper.c\n@@ -0,0 +1,6 @@\n+#include \"builtin.h\"\n+\n+int cmd_add__helper(int argc, const char **argv, const char *prefix)\n+{\n+\treturn 0;\n+}\ndiff --git a/git.c b/git.c\nindex 2f604a41ea..5507591f2e 100644\n--- a/git.c\n+++ b/git.c\n@@ -443,6 +443,7 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)\n \n static struct cmd_struct commands[] = {\n \t{ \"add\", cmd_add, RUN_SETUP | NEED_WORK_TREE },\n+\t{ \"add--helper\", cmd_add__helper, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"am\", cmd_am, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"annotate\", cmd_annotate, RUN_SETUP | NO_PARSEOPT },\n \t{ \"apply\", cmd_apply, RUN_SETUP_GENTLY },\n-- \ngitgitgadget\n\n"},{"id":"365625","messageId":"4950c889aacd869a8f4fd30b8d5cde4b43957347.1545307756.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.git.gitgitgadget@gmail.com","subject":"[PATCH 4/7] add--interactive.perl: use add--helper --status for status_cmd","fromName":"Daniel Ferreira via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2018-12-20T12:09:23Z","receivedAt":"2018-12-20T12:09:26Z","isPatch":true,"sender":{"key":"bnmvco@gmail.com","avatar":"https://avatars.githubusercontent.com/u/735397?v=4"},"body":"From: Daniel Ferreira <bnmvco@gmail.com>\n\nCall the newly introduced add--helper builtin on\nstatus_cmd() instead of relying on add--interactive's Perl\nfunctions to build print the numstat.\n\nSigned-off-by: Daniel Ferreira <bnmvco@gmail.com>\nSigned-off-by: Slavica Djukic <slawica92@hotmail.com>\n---\n git-add--interactive.perl | 4 +---\n 1 file changed, 1 insertion(+), 3 deletions(-)\n\ndiff --git a/git-add--interactive.perl b/git-add--interactive.perl\nindex 20eb81cc92..a6536f9cf3 100755\n--- a/git-add--interactive.perl\n+++ b/git-add--interactive.perl\n@@ -597,9 +597,7 @@ EOF\n }\n \n sub status_cmd {\n-\tlist_and_choose({ LIST_ONLY => 1, HEADER => $status_head },\n-\t\t\tlist_modified());\n-\tprint \"\\n\";\n+\tsystem(qw(git add--helper --status));\n }\n \n sub say_n_paths {\n-- \ngitgitgadget\n\n"},{"id":"365626","messageId":"d247ef69fe2c9d017b8d363de554a52269c0d281.1545307756.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.git.gitgitgadget@gmail.com","subject":"[PATCH 3/7] add-interactive.c: implement status command","fromName":"Daniel Ferreira via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2018-12-20T12:09:22Z","receivedAt":"2018-12-20T12:09:28Z","isPatch":true,"sender":{"key":"bnmvco@gmail.com","avatar":"https://avatars.githubusercontent.com/u/735397?v=4"},"body":"From: Daniel Ferreira <bnmvco@gmail.com>\n\nAdd new files: add-interactive.c and add-interactive.h, which\nwill be used for implementing \"application logic\" of git add -i,\nwhereas add--helper.c will be used mostly for parsing the command line.\nWe're a bit lax with the command-line parsing, as the command is\nintended to be called only by one internal user: the add--interactive script.\n\nImplement add --interactive's status command in add-interactive.c and\nuse it in builtin add--helper.c.\n\nIt prints a numstat comparing changed files between a) the worktree and\nthe index; b) the index and the HEAD.\n\nTo do so, we use run_diff_index() and run_diff_files() to get changed\nfiles, use the diffstat API on them to get the numstat and use a\ncombination of a hashmap and qsort() to print the result in\nO(n) + O(n lg n) complexity.\n\nThis is the first interactive add command implemented in C of those\nanticipated by the previous commit, which introduced\nthe add--helper built-in.\n\nSigned-off-by: Daniel Ferreira <bnmvco@gmail.com>\nSigned-off-by: Slavica Djukic <slawica92@hotmail.com>\n---\n Makefile              |   1 +\n add-interactive.c     | 246 ++++++++++++++++++++++++++++++++++++++++++\n add-interactive.h     |   8 ++\n builtin/add--helper.c |  32 ++++++\n 4 files changed, 287 insertions(+)\n create mode 100644 add-interactive.c\n create mode 100644 add-interactive.h\n\ndiff --git a/Makefile b/Makefile\nindex 9c84b80739..2a4a5cc37b 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -827,6 +827,7 @@ LIB_H = $(shell $(FIND) . \\\n \t-name '*.h' -print)\n \n LIB_OBJS += abspath.o\n+LIB_OBJS += add-interactive.o\n LIB_OBJS += advice.o\n LIB_OBJS += alias.o\n LIB_OBJS += alloc.o\ndiff --git a/add-interactive.c b/add-interactive.c\nnew file mode 100644\nindex 0000000000..c55d934186\n--- /dev/null\n+++ b/add-interactive.c\n@@ -0,0 +1,246 @@\n+#include \"add-interactive.h\"\n+#include \"cache.h\"\n+#include \"commit.h\"\n+#include \"color.h\"\n+#include \"config.h\"\n+#include \"diffcore.h\"\n+#include \"revision.h\"\n+\n+#define HEADER_INDENT \"      \"\n+\n+enum collection_phase {\n+\tWORKTREE,\n+\tINDEX\n+};\n+\n+struct file_stat {\n+\tstruct hashmap_entry ent;\n+\tstruct {\n+\t\tuintmax_t added, deleted;\n+\t} index, worktree;\n+\tchar name[FLEX_ARRAY];\n+};\n+\n+struct collection_status {\n+\tenum collection_phase phase;\n+\n+\tconst char *reference;\n+\tstruct pathspec pathspec;\n+\n+\tstruct hashmap file_map;\n+};\n+\n+static int use_color = -1;\n+enum color_add_i {\n+\tCOLOR_PROMPT,\n+\tCOLOR_HEADER,\n+\tCOLOR_HELP,\n+\tCOLOR_ERROR\n+};\n+\n+static char colors[][COLOR_MAXLEN] = {\n+\tGIT_COLOR_BOLD_BLUE, /* Prompt */\n+\tGIT_COLOR_BOLD,      /* Header */\n+\tGIT_COLOR_BOLD_RED,  /* Help */\n+\tGIT_COLOR_BOLD_RED   /* Error */\n+};\n+\n+static const char *get_color(enum color_add_i ix)\n+{\n+\tif (want_color(use_color))\n+\t\treturn colors[ix];\n+\treturn \"\";\n+}\n+\n+static int parse_color_slot(const char *slot)\n+{\n+\tif (!strcasecmp(slot, \"prompt\"))\n+\t\treturn COLOR_PROMPT;\n+\tif (!strcasecmp(slot, \"header\"))\n+\t\treturn COLOR_HEADER;\n+\tif (!strcasecmp(slot, \"help\"))\n+\t\treturn COLOR_HELP;\n+\tif (!strcasecmp(slot, \"error\"))\n+\t\treturn COLOR_ERROR;\n+\n+\treturn -1;\n+}\n+\n+int add_i_config(const char *var,\n+\t\tconst char *value, void *cbdata)\n+{\n+\tconst char *name;\n+\n+\tif (!strcmp(var, \"color.interactive\")) {\n+\t\tuse_color = git_config_colorbool(var, value);\n+\t\treturn 0;\n+\t}\n+\n+\tif (skip_prefix(var, \"color.interactive.\", &name)) {\n+\t\tint slot = parse_color_slot(name);\n+\t\tif (slot < 0)\n+\t\t\treturn 0;\n+\t\tif (!value)\n+\t\t\treturn config_error_nonbool(var);\n+\t\treturn color_parse(value, colors[slot]);\n+\t}\n+\n+\treturn git_default_config(var, value, cbdata);\n+}\n+\n+static int hash_cmp(const void *unused_cmp_data, const void *entry,\n+\t\t\tconst void *entry_or_key, const void *keydata)\n+{\n+\tconst struct file_stat *e1 = entry, *e2 = entry_or_key;\n+\tconst char *name = keydata ? keydata : e2->name;\n+\n+\treturn strcmp(e1->name, name);\n+}\n+\n+static int alphabetical_cmp(const void *a, const void *b)\n+{\n+\tstruct file_stat *f1 = *((struct file_stat **)a);\n+\tstruct file_stat *f2 = *((struct file_stat **)b);\n+\n+\treturn strcmp(f1->name, f2->name);\n+}\n+\n+static void collect_changes_cb(struct diff_queue_struct *q,\n+\t\t\t\t\t struct diff_options *options,\n+\t\t\t\t\t void *data)\n+{\n+\tstruct collection_status *s = data;\n+\tstruct diffstat_t stat = { 0 };\n+\tint i;\n+\n+\tif (!q->nr)\n+\t\treturn;\n+\n+\tcompute_diffstat(options, &stat);\n+\n+\tfor (i = 0; i < stat.nr; i++) {\n+\t\tstruct file_stat *entry;\n+\t\tconst char *name = stat.files[i]->name;\n+\t\tunsigned int hash = strhash(name);\n+\n+\t\tentry = hashmap_get_from_hash(&s->file_map, hash, name);\n+\t\tif (!entry) {\n+\t\t\tFLEX_ALLOC_STR(entry, name, name);\n+\t\t\thashmap_entry_init(entry, hash);\n+\t\t\thashmap_add(&s->file_map, entry);\n+\t\t}\n+\n+\t\tif (s->phase == WORKTREE) {\n+\t\t\tentry->worktree.added = stat.files[i]->added;\n+\t\t\tentry->worktree.deleted = stat.files[i]->deleted;\n+\t\t} else if (s->phase == INDEX) {\n+\t\t\tentry->index.added = stat.files[i]->added;\n+\t\t\tentry->index.deleted = stat.files[i]->deleted;\n+\t\t}\n+\t}\n+}\n+\n+static void collect_changes_worktree(struct collection_status *s)\n+{\n+\tstruct rev_info rev;\n+\n+\ts->phase = WORKTREE;\n+\n+\tinit_revisions(&rev, NULL);\n+\tsetup_revisions(0, NULL, &rev, NULL);\n+\n+\trev.max_count = 0;\n+\n+\trev.diffopt.flags.ignore_dirty_submodules = 1;\n+\trev.diffopt.output_format = DIFF_FORMAT_CALLBACK;\n+\trev.diffopt.format_callback = collect_changes_cb;\n+\trev.diffopt.format_callback_data = s;\n+\n+\trun_diff_files(&rev, 0);\n+}\n+\n+static void collect_changes_index(struct collection_status *s)\n+{\n+\tstruct rev_info rev;\n+\tstruct setup_revision_opt opt = { 0 };\n+\n+\ts->phase = INDEX;\n+\n+\tinit_revisions(&rev, NULL);\n+\topt.def = s->reference;\n+\tsetup_revisions(0, NULL, &rev, &opt);\n+\n+\trev.diffopt.output_format = DIFF_FORMAT_CALLBACK;\n+\trev.diffopt.format_callback = collect_changes_cb;\n+\trev.diffopt.format_callback_data = s;\n+\n+\trun_diff_index(&rev, 1);\n+}\n+\n+void add_i_print_modified(void)\n+{\n+\tint i = 0;\n+\tstruct collection_status s;\n+\t/* TRANSLATORS: you can adjust this to align \"git add -i\" status menu */\n+\tconst char *modified_fmt = _(\"%12s %12s %s\");\n+\tconst char *header_color = get_color(COLOR_HEADER);\n+\tstruct object_id sha1;\n+\n+\tstruct hashmap_iter iter;\n+\tstruct file_stat **files;\n+\tstruct file_stat *entry;\n+\n+\tif (read_cache() < 0)\n+\t\treturn;\n+\n+\ts.reference = !get_oid(\"HEAD\", &sha1) ? \"HEAD\": empty_tree_oid_hex();\n+\thashmap_init(&s.file_map, hash_cmp, NULL, 0);\n+\n+\tcollect_changes_worktree(&s);\n+\tcollect_changes_index(&s);\n+\n+\tif (hashmap_get_size(&s.file_map) < 1) {\n+\t\tprintf(\"\\n\");\n+\t\treturn;\n+\t}\n+\n+\tprintf(HEADER_INDENT);\n+\tcolor_fprintf(stdout, header_color, modified_fmt, _(\"staged\"),\n+\t\t\t_(\"unstaged\"), _(\"path\"));\n+\tprintf(\"\\n\");\n+\n+\thashmap_iter_init(&s.file_map, &iter);\n+\n+\tfiles = xcalloc(hashmap_get_size(&s.file_map), sizeof(struct file_stat *));\n+\twhile ((entry = hashmap_iter_next(&iter))) {\n+\t\tfiles[i++] = entry;\n+\t}\n+\tQSORT(files, hashmap_get_size(&s.file_map), alphabetical_cmp);\n+\n+\tfor (i = 0; i < hashmap_get_size(&s.file_map); i++) {\n+\t\tstruct file_stat *f = files[i];\n+\n+\t\tchar worktree_changes[50];\n+\t\tchar index_changes[50];\n+\n+\t\tif (f->worktree.added || f->worktree.deleted)\n+\t\t\tsnprintf(worktree_changes, 50, \"+%\"PRIuMAX\"/-%\"PRIuMAX, f->worktree.added,\n+\t\t\t\t\tf->worktree.deleted);\n+\t\telse\n+\t\t\tsnprintf(worktree_changes, 50, \"%s\", _(\"nothing\"));\n+\n+\t\tif (f->index.added || f->index.deleted)\n+\t\t\tsnprintf(index_changes, 50, \"+%\"PRIuMAX\"/-%\"PRIuMAX, f->index.added,\n+\t\t\t\t\tf->index.deleted);\n+\t\telse\n+\t\t\tsnprintf(index_changes, 50, \"%s\", _(\"unchanged\"));\n+\n+\t\tprintf(\" %2d: \", i + 1);\n+\t\tprintf(modified_fmt, index_changes, worktree_changes, f->name);\n+\t\tprintf(\"\\n\");\n+\t}\n+\tprintf(\"\\n\");\n+\n+\tfree(files);\n+\thashmap_free(&s.file_map, 1);\n+}\ndiff --git a/add-interactive.h b/add-interactive.h\nnew file mode 100644\nindex 0000000000..1f4747553c\n--- /dev/null\n+++ b/add-interactive.h\n@@ -0,0 +1,8 @@\n+#ifndef ADD_INTERACTIVE_H\n+#define ADD_INTERACTIVE_H\n+\n+int add_i_config(const char *var, const char *value, void *cbdata);\n+\n+void add_i_print_modified(void);\n+\n+#endif\n\\ No newline at end of file\ndiff --git a/builtin/add--helper.c b/builtin/add--helper.c\nindex 6a97f0e191..43545d9af5 100644\n--- a/builtin/add--helper.c\n+++ b/builtin/add--helper.c\n@@ -1,6 +1,38 @@\n+#include \"add-interactive.h\"\n #include \"builtin.h\"\n+#include \"config.h\"\n+#include \"revision.h\"\n+\n+static const char * const builtin_add_helper_usage[] = {\n+\tN_(\"git add-interactive--helper <command>\"),\n+\tNULL\n+};\n+\n+enum cmd_mode {\n+\tDEFAULT = 0,\n+\tSTATUS\n+};\n \n int cmd_add__helper(int argc, const char **argv, const char *prefix)\n {\n+\tenum cmd_mode mode = DEFAULT;\n+\n+\tstruct option options[] = {\n+\t\tOPT_CMDMODE(0, \"status\", &mode,\n+\t\t\t N_(\"print status information with diffstat\"), STATUS),\n+\t\tOPT_END()\n+\t};\n+\n+\tgit_config(add_i_config, NULL);\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     builtin_add_helper_usage,\n+\t\t\t     PARSE_OPT_KEEP_ARGV0);\n+\n+\tif (mode == STATUS)\n+\t\tadd_i_print_modified();\n+\telse\n+\t\tusage_with_options(builtin_add_helper_usage,\n+\t\t\t\t   options);\n+\n \treturn 0;\n }\n-- \ngitgitgadget\n\n"},{"id":"365627","messageId":"19fdea5db197527ce306f23cc6fb71aadc9465cd.1545307756.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.git.gitgitgadget@gmail.com","subject":"[PATCH 5/7] add-interactive.c: implement show-help command","fromName":"Slavica Djukic via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2018-12-20T12:09:25Z","receivedAt":"2018-12-20T12:09:28Z","isPatch":true,"sender":{"key":"slavicadj.ip2018@gmail.com","avatar":null},"body":"From: Slavica Djukic <slawica92@hotmail.com>\n\nImplement show-help command in add-interactive.c and use it in\nbuiltin add--helper.c.\n\nUse command name \"show-help\" instead of \"help\": add--helper is\nbuiltin, hence add--helper --help would be intercepted by\nhandle_builtin and re-routed to the help command, without ever\ncalling cmd_add__helper().\n\nSigned-off-by: Slavica Djukic <slawica92@hotmail.com>\n---\n add-interactive.c     | 19 +++++++++++++++++++\n add-interactive.h     |  2 ++\n builtin/add--helper.c |  7 ++++++-\n 3 files changed, 27 insertions(+), 1 deletion(-)\n\ndiff --git a/add-interactive.c b/add-interactive.c\nindex c55d934186..ff5bfbac49 100644\n--- a/add-interactive.c\n+++ b/add-interactive.c\n@@ -8,6 +8,16 @@\n \n #define HEADER_INDENT \"      \"\n \n+/* TRANSLATORS: please do not translate the command names\n+   'status', 'update', 'revert', etc. */\n+static const char help_info[] = \n+\t\tN_(\"status        - show paths with changes\\n\"\n+\t\t\"update        - add working tree state to the staged set of changes\\n\"\n+\t\t\"revert        - revert staged set of changes back to the HEAD version\\n\"\n+\t\t\"patch         - pick hunks and update selectively\\n\"\n+\t\t\"diff          - view diff between HEAD and index\\n\"\n+\t\t\"add untracked - add contents of untracked files to the staged set of changes\");\n+\n enum collection_phase {\n \tWORKTREE,\n \tINDEX\n@@ -244,3 +254,12 @@ void add_i_print_modified(void)\n \tfree(files);\n \thashmap_free(&s.file_map, 1);\n }\n+\n+void show_help(void)\n+{\n+\tconst char *help_color = get_color(COLOR_HELP);\n+\tconst char *modified_fmt = _(\"%s\");\n+\tprintf(\"\\n\");\n+\tcolor_fprintf(stdout, help_color, modified_fmt, _(help_info));\n+\tprintf(\"\\n\");\n+}\ndiff --git a/add-interactive.h b/add-interactive.h\nindex 1f4747553c..a74c65b7e1 100644\n--- a/add-interactive.h\n+++ b/add-interactive.h\n@@ -5,4 +5,6 @@ int add_i_config(const char *var, const char *value, void *cbdata);\n \n void add_i_print_modified(void);\n \n+void show_help(void);\n+\n #endif\n\\ No newline at end of file\ndiff --git a/builtin/add--helper.c b/builtin/add--helper.c\nindex 43545d9af5..e288412d56 100644\n--- a/builtin/add--helper.c\n+++ b/builtin/add--helper.c\n@@ -10,7 +10,8 @@ static const char * const builtin_add_helper_usage[] = {\n \n enum cmd_mode {\n \tDEFAULT = 0,\n-\tSTATUS\n+\tSTATUS,\n+\tHELP\n };\n \n int cmd_add__helper(int argc, const char **argv, const char *prefix)\n@@ -20,6 +21,8 @@ int cmd_add__helper(int argc, const char **argv, const char *prefix)\n \tstruct option options[] = {\n \t\tOPT_CMDMODE(0, \"status\", &mode,\n \t\t\t N_(\"print status information with diffstat\"), STATUS),\n+\t\tOPT_CMDMODE(0, \"show-help\", &mode,\n+\t\t\t N_(\"show help\"), HELP),\n \t\tOPT_END()\n \t};\n \n@@ -30,6 +33,8 @@ int cmd_add__helper(int argc, const char **argv, const char *prefix)\n \n \tif (mode == STATUS)\n \t\tadd_i_print_modified();\n+\telse if (mode == HELP)\n+\t\tshow_help();\n \telse\n \t\tusage_with_options(builtin_add_helper_usage,\n \t\t\t\t   options);\n-- \ngitgitgadget\n\n"},{"id":"365628","messageId":"86d85face8ba543fb4a03adbc42dc930ee318f74.1545307756.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.git.gitgitgadget@gmail.com","subject":"[PATCH 6/7] Git.pm: introduce environment variable GIT_TEST_PRETEND_TTY","fromName":"Slavica Djukic via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2018-12-20T12:09:26Z","receivedAt":"2018-12-20T12:09:30Z","isPatch":true,"sender":{"key":"slavicadj.ip2018@gmail.com","avatar":null},"body":"From: Slavica Djukic <slawica92@hotmail.com>\n\nTo enable testing the colored output on Windows, enable TTY\nby using environment variable GIT_TEST_PRETEND_TTY.\n\nThis is the original idea by Johannes Schindelin.\n\nSigned-off-by: Slavica Djukic <slawica92@hotmail.com>\n---\n color.c     | 4 ++++\n perl/Git.pm | 2 +-\n 2 files changed, 5 insertions(+), 1 deletion(-)\n\ndiff --git a/color.c b/color.c\nindex ebb222ec33..4aa6cc3442 100644\n--- a/color.c\n+++ b/color.c\n@@ -323,6 +323,10 @@ static int check_auto_color(int fd)\n {\n \tstatic int color_stderr_is_tty = -1;\n \tint *is_tty_p = fd == 1 ? &color_stdout_is_tty : &color_stderr_is_tty;\n+\t\n+\tif (git_env_bool(\"GIT_TEST_PRETEND_TTY\", 0))\n+\t\treturn 1;\n+\n \tif (*is_tty_p < 0)\n \t\t*is_tty_p = isatty(fd);\n \tif (*is_tty_p || (fd == 1 && pager_in_use() && pager_use_color)) {\ndiff --git a/perl/Git.pm b/perl/Git.pm\nindex d856930b2e..51a1ce0617 100644\n--- a/perl/Git.pm\n+++ b/perl/Git.pm\n@@ -757,7 +757,7 @@ and returns boolean (true for \"use color\", false for \"do not use color\").\n \n sub get_colorbool {\n \tmy ($self, $var) = @_;\n-\tmy $stdout_to_tty = (-t STDOUT) ? \"true\" : \"false\";\n+\tmy $stdout_to_tty = $ENV{GIT_TEST_PRETEND_TTY} || (-t STDOUT) ? \"true\" : \"false\";\n \tmy $use_color = $self->command_oneline('config', '--get-colorbool',\n \t\t\t\t\t       $var, $stdout_to_tty);\n \treturn ($use_color eq 'true');\n-- \ngitgitgadget\n\n"},{"id":"365629","messageId":"060806010e77ee81c251d88629c16ae001247a70.1545307756.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.git.gitgitgadget@gmail.com","subject":"[PATCH 7/7] add--interactive.perl: use add--helper --show-help for help_cmd","fromName":"Slavica Djukic via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2018-12-20T12:09:27Z","receivedAt":"2018-12-20T12:09:31Z","isPatch":true,"sender":{"key":"slavicadj.ip2018@gmail.com","avatar":null},"body":"From: Slavica Djukic <slawica92@hotmail.com>\n\nChange help_cmd sub in git-add--interactive.perl to use\nshow-help command from builtin add--helper.\n\nAdd test to t3701-add-interactive to verify that show-help\noutputs expected content. Use GIT_PRETENT_TTY\nintroduced in earlier commit to be able to test output color\non Windows.\n\nSigned-off-by: Slavica Djukic <slawica92@hotmail.com>\n---\n git-add--interactive.perl  | 11 +----------\n t/t3701-add-interactive.sh | 25 +++++++++++++++++++++++++\n 2 files changed, 26 insertions(+), 10 deletions(-)\n\ndiff --git a/git-add--interactive.perl b/git-add--interactive.perl\nindex a6536f9cf3..32ee729a58 100755\n--- a/git-add--interactive.perl\n+++ b/git-add--interactive.perl\n@@ -1717,16 +1717,7 @@ sub quit_cmd {\n }\n \n sub help_cmd {\n-# TRANSLATORS: please do not translate the command names\n-# 'status', 'update', 'revert', etc.\n-\tprint colored $help_color, __ <<'EOF' ;\n-status        - show paths with changes\n-update        - add working tree state to the staged set of changes\n-revert        - revert staged set of changes back to the HEAD version\n-patch         - pick hunks and update selectively\n-diff          - view diff between HEAD and index\n-add untracked - add contents of untracked files to the staged set of changes\n-EOF\n+\tsystem(qw(git add--helper --show-help));\n }\n \n sub process_args {\ndiff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh\nindex 65dfbc033a..9c9d5bd935 100755\n--- a/t/t3701-add-interactive.sh\n+++ b/t/t3701-add-interactive.sh\n@@ -639,4 +639,29 @@ test_expect_success 'add -p patch editing works with pathological context lines'\n \ttest_cmp expected-2 actual\n '\n \n+test_expect_success 'show help from add--helper' '\n+\tgit reset --hard &&\n+\tcat >expect <<-\\EOF &&\n+\n+\t<BOLD>*** Commands ***<RESET>\n+\t  1: <BOLD;BLUE>s<RESET>tatus\t  2: <BOLD;BLUE>u<RESET>pdate\t  3: <BOLD;BLUE>r<RESET>evert\t  4: <BOLD;BLUE>a<RESET>dd untracked\n+\t  5: <BOLD;BLUE>p<RESET>atch\t  6: <BOLD;BLUE>d<RESET>iff\t  7: <BOLD;BLUE>q<RESET>uit\t  8: <BOLD;BLUE>h<RESET>elp\n+\t<BOLD;BLUE>What now<RESET>> \n+\t<BOLD;RED>status        - show paths with changes\n+\tupdate        - add working tree state to the staged set of changes\n+\trevert        - revert staged set of changes back to the HEAD version\n+\tpatch         - pick hunks and update selectively\n+\tdiff          - view diff between HEAD and index\n+\tadd untracked - add contents of untracked files to the staged set of changes<RESET>\n+\t<BOLD>*** Commands ***<RESET>\n+\t  1: <BOLD;BLUE>s<RESET>tatus\t  2: <BOLD;BLUE>u<RESET>pdate\t  3: <BOLD;BLUE>r<RESET>evert\t  4: <BOLD;BLUE>a<RESET>dd untracked\n+\t  5: <BOLD;BLUE>p<RESET>atch\t  6: <BOLD;BLUE>d<RESET>iff\t  7: <BOLD;BLUE>q<RESET>uit\t  8: <BOLD;BLUE>h<RESET>elp\n+\t<BOLD;BLUE>What now<RESET>> \n+\tBye.\n+\tEOF\n+\ttest_write_lines h | GIT_TEST_PRETEND_TTY=1 git add -i >actual.colored &&\n+\ttest_decode_color <actual.colored >actual &&\n+\ttest_i18ncmp expect actual\n+'\n+\n test_done\n-- \ngitgitgadget\n"},{"id":"365630","messageId":"pull.103.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":null,"subject":"[PATCH 0/7] Turn git add-i into built-in","fromName":"Johannes Schindelin","fromEmail":"gitgitgadget@gmail.com","sentAt":"2018-12-20T12:34:01Z","receivedAt":"2018-12-20T12:34:13Z","isPatch":true,"sender":{"key":"gitgitgadget@gmail.com","avatar":null},"body":"From: \"Slavica Đukić via GitGitGadget\" <gitgitgadget@gmail.com>\n\nThis is the first version of a patch series to start porting\ngit-add--interactive from Perl to C. Daniel Ferreira's patch series used as\na head start:\nhttps://public-inbox.org/git/1494907234-28903-1-git-send-email-bnmvco@gmail.com/t/#u\n\nDaniel Ferreira (4):\n  diff: export diffstat interface\n  add--helper: create builtin helper for interactive add\n  add-interactive.c: implement status command\n  add--interactive.perl: use add--helper --status for status_cmd\n\nSlavica Djukic (3):\n  add-interactive.c: implement show-help command\n  Git.pm: introduce environment variable GIT_TEST_PRETEND_TTY\n  add--interactive.perl: use add--helper --show-help for help_cmd\n\n .gitignore                 |   1 +\n Makefile                   |   2 +\n add-interactive.c          | 265 +++++++++++++++++++++++++++++++++++++\n add-interactive.h          |  10 ++\n builtin.h                  |   1 +\n builtin/add--helper.c      |  43 ++++++\n color.c                    |   4 +\n diff.c                     |  36 ++---\n diff.h                     |  18 +++\n git-add--interactive.perl  |  15 +--\n git.c                      |   1 +\n perl/Git.pm                |   2 +-\n t/t3701-add-interactive.sh |  25 ++++\n 13 files changed, 387 insertions(+), 36 deletions(-)\n create mode 100644 add-interactive.c\n create mode 100644 add-interactive.h\n create mode 100644 builtin/add--helper.c\n\n\nbase-commit: b21ebb671bb7dea8d342225f0d66c41f4e54d5ca\nPublished-As: https://github.com/gitgitgadget/git/releases/tags/pr-103%2FslavicaDj%2Fadd-i-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-103/slavicaDj/add-i-v1\nPull-Request: https://github.com/gitgitgadget/git/pull/103\n-- \ngitgitgadget\n"},{"id":"365631","messageId":"nycvar.QRO.7.76.6.1812201336250.41@tvgsbejvaqbjf.bet","threadId":"50075","inReplyTo":"pull.103.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 0/7] Turn git add-i into built-in","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2018-12-20T12:37:49Z","receivedAt":"2018-12-20T12:38:11Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\njust a note: this mail had not been sent by GitGitGadget (and hence has me\nas sender, even if the email address is GitGitGadget's) because it still\nhas problems with From: and To: and Cc: headers that contain non-ASCII\ncharacters. I hope to find the time soon to work on this.\n\nCiao,\nJohannes\n\nOn Thu, 20 Dec 2018, Johannes Schindelin wrote:\n\n> From: \"Slavica Đukić via GitGitGadget\" <gitgitgadget@gmail.com>\n> \n> This is the first version of a patch series to start porting\n> git-add--interactive from Perl to C. Daniel Ferreira's patch series used as\n> a head start:\n> https://public-inbox.org/git/1494907234-28903-1-git-send-email-bnmvco@gmail.com/t/#u\n> \n> Daniel Ferreira (4):\n>   diff: export diffstat interface\n>   add--helper: create builtin helper for interactive add\n>   add-interactive.c: implement status command\n>   add--interactive.perl: use add--helper --status for status_cmd\n> \n> Slavica Djukic (3):\n>   add-interactive.c: implement show-help command\n>   Git.pm: introduce environment variable GIT_TEST_PRETEND_TTY\n>   add--interactive.perl: use add--helper --show-help for help_cmd\n> \n>  .gitignore                 |   1 +\n>  Makefile                   |   2 +\n>  add-interactive.c          | 265 +++++++++++++++++++++++++++++++++++++\n>  add-interactive.h          |  10 ++\n>  builtin.h                  |   1 +\n>  builtin/add--helper.c      |  43 ++++++\n>  color.c                    |   4 +\n>  diff.c                     |  36 ++---\n>  diff.h                     |  18 +++\n>  git-add--interactive.perl  |  15 +--\n>  git.c                      |   1 +\n>  perl/Git.pm                |   2 +-\n>  t/t3701-add-interactive.sh |  25 ++++\n>  13 files changed, 387 insertions(+), 36 deletions(-)\n>  create mode 100644 add-interactive.c\n>  create mode 100644 add-interactive.h\n>  create mode 100644 builtin/add--helper.c\n> \n> \n> base-commit: b21ebb671bb7dea8d342225f0d66c41f4e54d5ca\n> Published-As: https://github.com/gitgitgadget/git/releases/tags/pr-103%2FslavicaDj%2Fadd-i-v1\n> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-103/slavicaDj/add-i-v1\n> Pull-Request: https://github.com/gitgitgadget/git/pull/103\n> -- \n> gitgitgadget\n> "},{"id":"366557","messageId":"5f3c374f-9a10-1a12-e5f5-5e0a2514c120@gmail.com","threadId":"50075","inReplyTo":"pull.103.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 0/7] Turn git add-i into built-in","fromName":"Slavica Djukic","fromEmail":"slavicadj.ip2018@gmail.com","sentAt":"2019-01-11T14:09:22Z","receivedAt":"2019-01-11T14:09:30Z","isPatch":true,"sender":{"key":"slavicadj.ip2018@gmail.com","avatar":null},"body":"\nOn 20-Dec-18 1:34 PM, Johannes Schindelin wrote:\n> From: \"Slavica Đukić via GitGitGadget\" <gitgitgadget@gmail.com>\n>\n> This is the first version of a patch series to start porting\n> git-add--interactive from Perl to C. Daniel Ferreira's patch series used as\n> a head start:\n> https://public-inbox.org/git/1494907234-28903-1-git-send-email-bnmvco@gmail.com/t/#u\n\n\nPing? :)\n\n\n>\n> Daniel Ferreira (4):\n>    diff: export diffstat interface\n>    add--helper: create builtin helper for interactive add\n>    add-interactive.c: implement status command\n>    add--interactive.perl: use add--helper --status for status_cmd\n>\n> Slavica Djukic (3):\n>    add-interactive.c: implement show-help command\n>    Git.pm: introduce environment variable GIT_TEST_PRETEND_TTY\n>    add--interactive.perl: use add--helper --show-help for help_cmd\n>\n>   .gitignore                 |   1 +\n>   Makefile                   |   2 +\n>   add-interactive.c          | 265 +++++++++++++++++++++++++++++++++++++\n>   add-interactive.h          |  10 ++\n>   builtin.h                  |   1 +\n>   builtin/add--helper.c      |  43 ++++++\n>   color.c                    |   4 +\n>   diff.c                     |  36 ++---\n>   diff.h                     |  18 +++\n>   git-add--interactive.perl  |  15 +--\n>   git.c                      |   1 +\n>   perl/Git.pm                |   2 +-\n>   t/t3701-add-interactive.sh |  25 ++++\n>   13 files changed, 387 insertions(+), 36 deletions(-)\n>   create mode 100644 add-interactive.c\n>   create mode 100644 add-interactive.h\n>   create mode 100644 builtin/add--helper.c\n>\n>\n> base-commit: b21ebb671bb7dea8d342225f0d66c41f4e54d5ca\n> Published-As: https://github.com/gitgitgadget/git/releases/tags/pr-103%2FslavicaDj%2Fadd-i-v1\n> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-103/slavicaDj/add-i-v1\n> Pull-Request: https://github.com/gitgitgadget/git/pull/103\n"},{"id":"366645","messageId":"6a8fb6b5-31bf-df19-5ab6-997cd1d2379f@talktalk.net","threadId":"50075","inReplyTo":"19fdea5db197527ce306f23cc6fb71aadc9465cd.1545307756.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 5/7] add-interactive.c: implement show-help command","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2019-01-14T11:12:09Z","receivedAt":"2019-01-14T11:12:09Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Slavica\n\nI think the basics of this patch are fine, I've got a few comments below\n\nOn 20/12/2018 12:09, Slavica Djukic via GitGitGadget wrote:\n> From: Slavica Djukic <slawica92@hotmail.com>\n> \n> Implement show-help command in add-interactive.c and use it in\n> builtin add--helper.c.\n> \n> Use command name \"show-help\" instead of \"help\": add--helper is\n> builtin, hence add--helper --help would be intercepted by\n> handle_builtin and re-routed to the help command, without ever\n> calling cmd_add__helper().\n> \n> Signed-off-by: Slavica Djukic <slawica92@hotmail.com>\n> ---\n>  add-interactive.c     | 19 +++++++++++++++++++\n>  add-interactive.h     |  2 ++\n>  builtin/add--helper.c |  7 ++++++-\n>  3 files changed, 27 insertions(+), 1 deletion(-)\n> \n> diff --git a/add-interactive.c b/add-interactive.c\n> index c55d934186..ff5bfbac49 100644\n> --- a/add-interactive.c\n> +++ b/add-interactive.c\n> @@ -8,6 +8,16 @@\n>  \n>  #define HEADER_INDENT \"      \"\n>  \n> +/* TRANSLATORS: please do not translate the command names\n> +   'status', 'update', 'revert', etc. */\n> +static const char help_info[] = \n> +\t\tN_(\"status        - show paths with changes\\n\"\n> +\t\t\"update        - add working tree state to the staged set of changes\\n\"\n> +\t\t\"revert        - revert staged set of changes back to the HEAD version\\n\"\n> +\t\t\"patch         - pick hunks and update selectively\\n\"\n> +\t\t\"diff          - view diff between HEAD and index\\n\"\n> +\t\t\"add untracked - add contents of untracked files to the staged set of changes\");\n\nIf we were starting from scratch I'd suggest only translating the help\ntext, not the command names as this would avoid any possible problems\nwith the command names and indentation. As we already have this\ntranslated it is easier to leave it as is unless the translators think\nit would be useful to change it. I'd be inclined to put this definition\nnext to the function that uses it (or possibly in that function) so it\nis easy to see what text will be printed when reading the function.\n\n> +\n>  enum collection_phase {\n>  \tWORKTREE,\n>  \tINDEX\n> @@ -244,3 +254,12 @@ void add_i_print_modified(void)\n>  \tfree(files);\n>  \thashmap_free(&s.file_map, 1);\n>  }\n> +\n> +void show_help(void)\n> +{\n> +\tconst char *help_color = get_color(COLOR_HELP);\n> +\tconst char *modified_fmt = _(\"%s\");\n\nThis does not need to be translated, also I'm not sure we need a\nseparate variable (unless something funny is happening - why has it got\n'modified' in its name?)\n\n> +\tprintf(\"\\n\");\n> +\tcolor_fprintf(stdout, help_color, modified_fmt, _(help_info));\n> +\tprintf(\"\\n\");\n> +}\n> diff --git a/add-interactive.h b/add-interactive.h\n> index 1f4747553c..a74c65b7e1 100644\n> --- a/add-interactive.h\n> +++ b/add-interactive.h\n> @@ -5,4 +5,6 @@ int add_i_config(const char *var, const char *value, void *cbdata);\n>  \n>  void add_i_print_modified(void);\n>  \n> +void show_help(void);\n\nAs this is a global function I think it needs a more specific name to\navoid clashing with a function that shows the help for a different\ncommand. Maybe add_i_show_help() to match add_i_print_modified()?\n\n> +\n>  #endif\n> \\ No newline at end of file\n\nC files should end with a new line (it is actually undefined behavior if\nthey don't!)\n\nBest Wishes\n\nPhillip\n\n> diff --git a/builtin/add--helper.c b/builtin/add--helper.c\n> index 43545d9af5..e288412d56 100644\n> --- a/builtin/add--helper.c\n> +++ b/builtin/add--helper.c\n> @@ -10,7 +10,8 @@ static const char * const builtin_add_helper_usage[] = {\n>  \n>  enum cmd_mode {\n>  \tDEFAULT = 0,\n> -\tSTATUS\n> +\tSTATUS,\n> +\tHELP\n>  };\n>  \n>  int cmd_add__helper(int argc, const char **argv, const char *prefix)\n> @@ -20,6 +21,8 @@ int cmd_add__helper(int argc, const char **argv, const char *prefix)\n>  \tstruct option options[] = {\n>  \t\tOPT_CMDMODE(0, \"status\", &mode,\n>  \t\t\t N_(\"print status information with diffstat\"), STATUS),\n> +\t\tOPT_CMDMODE(0, \"show-help\", &mode,\n> +\t\t\t N_(\"show help\"), HELP),\n>  \t\tOPT_END()\n>  \t};\n>  \n> @@ -30,6 +33,8 @@ int cmd_add__helper(int argc, const char **argv, const char *prefix)\n>  \n>  \tif (mode == STATUS)\n>  \t\tadd_i_print_modified();\n> +\telse if (mode == HELP)\n> +\t\tshow_help();\n>  \telse\n>  \t\tusage_with_options(builtin_add_helper_usage,\n>  \t\t\t\t   options);\n> \n\n"},{"id":"366646","messageId":"fd6bab55-871c-4f4d-84e9-98bfe5d54f23@talktalk.net","threadId":"50075","inReplyTo":"86d85face8ba543fb4a03adbc42dc930ee318f74.1545307756.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 6/7] Git.pm: introduce environment variable GIT_TEST_PRETEND_TTY","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2019-01-14T11:13:46Z","receivedAt":"2019-01-14T11:13:44Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Salvica/Johannes\n\nOn 20/12/2018 12:09, Slavica Djukic via GitGitGadget wrote:\n> From: Slavica Djukic <slawica92@hotmail.com>\n> \n> To enable testing the colored output on Windows, enable TTY\n> by using environment variable GIT_TEST_PRETEND_TTY.\n> \n> This is the original idea by Johannes Schindelin.\n\nI normally use GIT_PAGER_IN_USE=1 to force colored output, is there some\nreason that does not work here?\n\nBest Wishes\n\nPhillip\n\n> \n> Signed-off-by: Slavica Djukic <slawica92@hotmail.com>\n> ---\n>  color.c     | 4 ++++\n>  perl/Git.pm | 2 +-\n>  2 files changed, 5 insertions(+), 1 deletion(-)\n> \n> diff --git a/color.c b/color.c\n> index ebb222ec33..4aa6cc3442 100644\n> --- a/color.c\n> +++ b/color.c\n> @@ -323,6 +323,10 @@ static int check_auto_color(int fd)\n>  {\n>  \tstatic int color_stderr_is_tty = -1;\n>  \tint *is_tty_p = fd == 1 ? &color_stdout_is_tty : &color_stderr_is_tty;\n> +\t\n> +\tif (git_env_bool(\"GIT_TEST_PRETEND_TTY\", 0))\n> +\t\treturn 1;\n> +\n>  \tif (*is_tty_p < 0)\n>  \t\t*is_tty_p = isatty(fd);\n>  \tif (*is_tty_p || (fd == 1 && pager_in_use() && pager_use_color)) {\n> diff --git a/perl/Git.pm b/perl/Git.pm\n> index d856930b2e..51a1ce0617 100644\n> --- a/perl/Git.pm\n> +++ b/perl/Git.pm\n> @@ -757,7 +757,7 @@ and returns boolean (true for \"use color\", false for \"do not use color\").\n>  \n>  sub get_colorbool {\n>  \tmy ($self, $var) = @_;\n> -\tmy $stdout_to_tty = (-t STDOUT) ? \"true\" : \"false\";\n> +\tmy $stdout_to_tty = $ENV{GIT_TEST_PRETEND_TTY} || (-t STDOUT) ? \"true\" : \"false\";\n>  \tmy $use_color = $self->command_oneline('config', '--get-colorbool',\n>  \t\t\t\t\t       $var, $stdout_to_tty);\n>  \treturn ($use_color eq 'true');\n> \n\n"},{"id":"366647","messageId":"e8e7ee69-67d2-dc96-722a-8b16aa3f26ec@talktalk.net","threadId":"50075","inReplyTo":"060806010e77ee81c251d88629c16ae001247a70.1545307756.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 7/7] add--interactive.perl: use add--helper --show-help for help_cmd","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2019-01-14T11:17:36Z","receivedAt":"2019-01-14T11:17:34Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Slavica\n\nOn 20/12/2018 12:09, Slavica Djukic via GitGitGadget wrote:\n> From: Slavica Djukic <slawica92@hotmail.com>\n> \n> Change help_cmd sub in git-add--interactive.perl to use\n> show-help command from builtin add--helper.\n> \n> Add test to t3701-add-interactive to verify that show-help\n> outputs expected content. Use GIT_PRETENT_TTY\n> introduced in earlier commit to be able to test output color\n> on Windows.\n> \n\nIt's great to see you adding a test for this, if it isn't too much work\nperhaps you could add it before changing the implementation to\ndemonstrate that there are no changes introduced by the conversion to C.\n\nBest Wishes\n\nPhillip\n\n> Signed-off-by: Slavica Djukic <slawica92@hotmail.com>\n> ---\n>  git-add--interactive.perl  | 11 +----------\n>  t/t3701-add-interactive.sh | 25 +++++++++++++++++++++++++\n>  2 files changed, 26 insertions(+), 10 deletions(-)\n> \n> diff --git a/git-add--interactive.perl b/git-add--interactive.perl\n> index a6536f9cf3..32ee729a58 100755\n> --- a/git-add--interactive.perl\n> +++ b/git-add--interactive.perl\n> @@ -1717,16 +1717,7 @@ sub quit_cmd {\n>  }\n>  \n>  sub help_cmd {\n> -# TRANSLATORS: please do not translate the command names\n> -# 'status', 'update', 'revert', etc.\n> -\tprint colored $help_color, __ <<'EOF' ;\n> -status        - show paths with changes\n> -update        - add working tree state to the staged set of changes\n> -revert        - revert staged set of changes back to the HEAD version\n> -patch         - pick hunks and update selectively\n> -diff          - view diff between HEAD and index\n> -add untracked - add contents of untracked files to the staged set of changes\n> -EOF\n> +\tsystem(qw(git add--helper --show-help));\n>  }\n>  \n>  sub process_args {\n> diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh\n> index 65dfbc033a..9c9d5bd935 100755\n> --- a/t/t3701-add-interactive.sh\n> +++ b/t/t3701-add-interactive.sh\n> @@ -639,4 +639,29 @@ test_expect_success 'add -p patch editing works with pathological context lines'\n>  \ttest_cmp expected-2 actual\n>  '\n>  \n> +test_expect_success 'show help from add--helper' '\n> +\tgit reset --hard &&\n> +\tcat >expect <<-\\EOF &&\n> +\n> +\t<BOLD>*** Commands ***<RESET>\n> +\t  1: <BOLD;BLUE>s<RESET>tatus\t  2: <BOLD;BLUE>u<RESET>pdate\t  3: <BOLD;BLUE>r<RESET>evert\t  4: <BOLD;BLUE>a<RESET>dd untracked\n> +\t  5: <BOLD;BLUE>p<RESET>atch\t  6: <BOLD;BLUE>d<RESET>iff\t  7: <BOLD;BLUE>q<RESET>uit\t  8: <BOLD;BLUE>h<RESET>elp\n> +\t<BOLD;BLUE>What now<RESET>> \n> +\t<BOLD;RED>status        - show paths with changes\n> +\tupdate        - add working tree state to the staged set of changes\n> +\trevert        - revert staged set of changes back to the HEAD version\n> +\tpatch         - pick hunks and update selectively\n> +\tdiff          - view diff between HEAD and index\n> +\tadd untracked - add contents of untracked files to the staged set of changes<RESET>\n> +\t<BOLD>*** Commands ***<RESET>\n> +\t  1: <BOLD;BLUE>s<RESET>tatus\t  2: <BOLD;BLUE>u<RESET>pdate\t  3: <BOLD;BLUE>r<RESET>evert\t  4: <BOLD;BLUE>a<RESET>dd untracked\n> +\t  5: <BOLD;BLUE>p<RESET>atch\t  6: <BOLD;BLUE>d<RESET>iff\t  7: <BOLD;BLUE>q<RESET>uit\t  8: <BOLD;BLUE>h<RESET>elp\n> +\t<BOLD;BLUE>What now<RESET>> \n> +\tBye.\n> +\tEOF\n> +\ttest_write_lines h | GIT_TEST_PRETEND_TTY=1 git add -i >actual.colored &&\n> +\ttest_decode_color <actual.colored >actual &&\n> +\ttest_i18ncmp expect actual\n> +'\n> +\n>  test_done\n> \n\n"},{"id":"366706","messageId":"5413d570-d071-2361-1ff9-6854d0d4f584@gmail.com","threadId":"50075","inReplyTo":"fd6bab55-871c-4f4d-84e9-98bfe5d54f23@talktalk.net","subject":"Re: [PATCH 6/7] Git.pm: introduce environment variable GIT_TEST_PRETEND_TTY","fromName":"Slavica Djukic","fromEmail":"slavicadj.ip2018@gmail.com","sentAt":"2019-01-15T12:45:50Z","receivedAt":"2019-01-15T12:45:56Z","isPatch":true,"sender":{"key":"slavicadj.ip2018@gmail.com","avatar":null},"body":"Hi Phillip,\n\nFirst of all, thank you for taking your time to review this patch series.\n\nOn 14-Jan-19 12:13 PM, Phillip Wood wrote:\n> Hi Salvica/Johannes\n>\n> On 20/12/2018 12:09, Slavica Djukic via GitGitGadget wrote:\n>> From: Slavica Djukic <slawica92@hotmail.com>\n>>\n>> To enable testing the colored output on Windows, enable TTY\n>> by using environment variable GIT_TEST_PRETEND_TTY.\n>>\n>> This is the original idea by Johannes Schindelin.\n> I normally use GIT_PAGER_IN_USE=1 to force colored output, is there some\n> reason that does not work here?\n\n\nI tried that approach, and it turns out GIT_PAGER_IN_USE=1 does force \ncolored output.\n\nAs for the changes suggested in the other two e-mails -- I will apply \nthem as well and send re-roll.\n\n\nThank you,\n\nSlavica\n\n\n>\n> Best Wishes\n>\n> Phillip\n>\n>> Signed-off-by: Slavica Djukic <slawica92@hotmail.com>\n>> ---\n>>   color.c     | 4 ++++\n>>   perl/Git.pm | 2 +-\n>>   2 files changed, 5 insertions(+), 1 deletion(-)\n>>\n>> diff --git a/color.c b/color.c\n>> index ebb222ec33..4aa6cc3442 100644\n>> --- a/color.c\n>> +++ b/color.c\n>> @@ -323,6 +323,10 @@ static int check_auto_color(int fd)\n>>   {\n>>   \tstatic int color_stderr_is_tty = -1;\n>>   \tint *is_tty_p = fd == 1 ? &color_stdout_is_tty : &color_stderr_is_tty;\n>> +\t\n>> +\tif (git_env_bool(\"GIT_TEST_PRETEND_TTY\", 0))\n>> +\t\treturn 1;\n>> +\n>>   \tif (*is_tty_p < 0)\n>>   \t\t*is_tty_p = isatty(fd);\n>>   \tif (*is_tty_p || (fd == 1 && pager_in_use() && pager_use_color)) {\n>> diff --git a/perl/Git.pm b/perl/Git.pm\n>> index d856930b2e..51a1ce0617 100644\n>> --- a/perl/Git.pm\n>> +++ b/perl/Git.pm\n>> @@ -757,7 +757,7 @@ and returns boolean (true for \"use color\", false for \"do not use color\").\n>>   \n>>   sub get_colorbool {\n>>   \tmy ($self, $var) = @_;\n>> -\tmy $stdout_to_tty = (-t STDOUT) ? \"true\" : \"false\";\n>> +\tmy $stdout_to_tty = $ENV{GIT_TEST_PRETEND_TTY} || (-t STDOUT) ? \"true\" : \"false\";\n>>   \tmy $use_color = $self->command_oneline('config', '--get-colorbool',\n>>   \t\t\t\t\t       $var, $stdout_to_tty);\n>>   \treturn ($use_color eq 'true');\n>>\n"},{"id":"366709","messageId":"nycvar.QRO.7.76.6.1901151448570.41@tvgsbejvaqbjf.bet","threadId":"50075","inReplyTo":"fd6bab55-871c-4f4d-84e9-98bfe5d54f23@talktalk.net","subject":"Re: [PATCH 6/7] Git.pm: introduce environment variable GIT_TEST_PRETEND_TTY","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-01-15T13:50:32Z","receivedAt":"2019-01-15T13:51:01Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Phillip,\n\nOn Mon, 14 Jan 2019, Phillip Wood wrote:\n\n> Hi Salvica/Johannes\n\nClose ;-)\n\n> On 20/12/2018 12:09, Slavica Djukic via GitGitGadget wrote:\n> > From: Slavica Djukic <slawica92@hotmail.com>\n> > \n> > To enable testing the colored output on Windows, enable TTY\n> > by using environment variable GIT_TEST_PRETEND_TTY.\n> > \n> > This is the original idea by Johannes Schindelin.\n> \n> I normally use GIT_PAGER_IN_USE=1 to force colored output, is there some\n> reason that does not work here?\n\nAs Slavica found out, you need to set `TERM`, too, for that to work. And\neven then, it is more of a \"happens to do what we want\" rather than a\n\"this is what we want to happen\"...\n\nBut I'm fine either way, the color code does not really *need* more\ncomplexity, as it were...\n\nCiao,\nDscho\n"},{"id":"366722","messageId":"c3daa35c-5242-65f8-15ab-9b520812438f@talktalk.net","threadId":"50075","inReplyTo":"nycvar.QRO.7.76.6.1901151448570.41@tvgsbejvaqbjf.bet","subject":"Re: [PATCH 6/7] Git.pm: introduce environment variable GIT_TEST_PRETEND_TTY","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2019-01-15T16:09:17Z","receivedAt":"2019-01-15T16:09:25Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 15/01/2019 13:50, Johannes Schindelin wrote:\n> Hi Phillip,\n> \n> On Mon, 14 Jan 2019, Phillip Wood wrote:\n> \n>> Hi Salvica/Johannes\n> \n> Close ;-)\n\nSorry Slavica\n\n> \n>> On 20/12/2018 12:09, Slavica Djukic via GitGitGadget wrote:\n>>> From: Slavica Djukic <slawica92@hotmail.com>\n>>>\n>>> To enable testing the colored output on Windows, enable TTY\n>>> by using environment variable GIT_TEST_PRETEND_TTY.\n>>>\n>>> This is the original idea by Johannes Schindelin.\n>>\n>> I normally use GIT_PAGER_IN_USE=1 to force colored output, is there some\n>> reason that does not work here?\n> \n> As Slavica found out, you need to set `TERM`, too, for that to work. And\n> even then, it is more of a \"happens to do what we want\" rather than a\n> \"this is what we want to happen\"...\n> \n> But I'm fine either way, the color code does not really *need* more\n> complexity, as it were...\n\nThanks for explaining, I didn't know the TERM needed to be set as well. \nIf GIT_PAGER_IN_USE=1 works then maybe it is better to avoid adding any \nmore complexity.\n\nBest Wishes\n\nPhillip\n\n> \n> Ciao,\n> Dscho\n> \n\n"},{"id":"367054","messageId":"pull.103.v2.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.git.gitgitgadget@gmail.com","subject":"[PATCH v2 0/7] Turn git add-i into built-in","fromName":"Slavica Đukić via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-01-18T07:47:01Z","receivedAt":"2019-01-18T07:47:06Z","isPatch":true,"sender":{"key":"name:Slavica Đukić","avatar":null},"body":"This is the first version of a patch series to start porting\ngit-add--interactive from Perl to C. Daniel Ferreira's patch series used as\na head start:\nhttps://public-inbox.org/git/1494907234-28903-1-git-send-email-bnmvco@gmail.com/t/#u\n\nChanges since v1:\n\n * rename show_help() to add_i_show_help()\n * move static const char help_info[] to add_i_show_help() and translate\n   only the help text\n * add newline at the end of add-interactive.h\n * modify commits so that the test is introduced before making\n   git-add--interactive.perl's help_cmd to use add_i_show_help\n * use variables GIT_PAGER_IN_USE=true and TERM=vt100 in test as alternative\n   for GIT_PRETEND_TTY\n\nDaniel Ferreira (4):\n  diff: export diffstat interface\n  add--helper: create builtin helper for interactive add\n  add-interactive.c: implement status command\n  add--interactive.perl: use add--helper --status for status_cmd\n\nSlavica Djukic (3):\n  add-interactive.c: implement show-help command\n  t3701-add-interactive: test add_i_show_help()\n  add--interactive.perl: use add--helper --show-help for help_cmd\n\n .gitignore                 |   1 +\n Makefile                   |   2 +\n add-interactive.c          | 269 +++++++++++++++++++++++++++++++++++++\n add-interactive.h          |  10 ++\n builtin.h                  |   1 +\n builtin/add--helper.c      |  43 ++++++\n diff.c                     |  36 ++---\n diff.h                     |  18 +++\n git-add--interactive.perl  |  15 +--\n git.c                      |   1 +\n t/t3701-add-interactive.sh |  24 ++++\n 11 files changed, 385 insertions(+), 35 deletions(-)\n create mode 100644 add-interactive.c\n create mode 100644 add-interactive.h\n create mode 100644 builtin/add--helper.c\n\n\nbase-commit: b21ebb671bb7dea8d342225f0d66c41f4e54d5ca\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-103%2FslavicaDj%2Fadd-i-v2\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-103/slavicaDj/add-i-v2\nPull-Request: https://github.com/gitgitgadget/git/pull/103\n\nRange-diff vs v1:\n\n 1:  737767b6f4 = 1:  737767b6f4 diff: export diffstat interface\n 2:  91b1963125 = 2:  91b1963125 add--helper: create builtin helper for interactive add\n 3:  d247ef69fe = 3:  d247ef69fe add-interactive.c: implement status command\n 4:  4950c889aa = 4:  4950c889aa add--interactive.perl: use add--helper --status for status_cmd\n 5:  19fdea5db1 ! 5:  cf4e913a5a add-interactive.c: implement show-help command\n     @@ -16,33 +16,30 @@\n       --- a/add-interactive.c\n       +++ b/add-interactive.c\n      @@\n     - \n     - #define HEADER_INDENT \"      \"\n     - \n     -+/* TRANSLATORS: please do not translate the command names\n     -+   'status', 'update', 'revert', etc. */\n     -+static const char help_info[] = \n     -+\t\tN_(\"status        - show paths with changes\\n\"\n     -+\t\t\"update        - add working tree state to the staged set of changes\\n\"\n     -+\t\t\"revert        - revert staged set of changes back to the HEAD version\\n\"\n     -+\t\t\"patch         - pick hunks and update selectively\\n\"\n     -+\t\t\"diff          - view diff between HEAD and index\\n\"\n     -+\t\t\"add untracked - add contents of untracked files to the staged set of changes\");\n     -+\n     - enum collection_phase {\n     - \tWORKTREE,\n     - \tINDEX\n     -@@\n       \tfree(files);\n       \thashmap_free(&s.file_map, 1);\n       }\n      +\n     -+void show_help(void)\n     ++void add_i_show_help(void)\n      +{\n      +\tconst char *help_color = get_color(COLOR_HELP);\n     -+\tconst char *modified_fmt = _(\"%s\");\n     ++\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"status\"), \n     ++\t\tN_(\"        - show paths with changes\"));\n     ++\tprintf(\"\\n\");\n     ++\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"update\"), \n     ++\t\tN_(\"        - add working tree state to the staged set of changes\"));\n     ++\tprintf(\"\\n\");\t\n     ++\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"revert\"),\n     ++\t\tN_(\"        - revert staged set of changes back to the HEAD version\"));\n      +\tprintf(\"\\n\");\n     -+\tcolor_fprintf(stdout, help_color, modified_fmt, _(help_info));\n     ++\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"patch\"),\n     ++\t\tN_(\"         - pick hunks and update selectively\"));\n     ++\tprintf(\"\\n\");\n     ++\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"diff\"),\n     ++\t\tN_(\"          - view diff between HEAD and index\"));\n     ++\tprintf(\"\\n\");\n     ++\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"add untracked\"),\n     ++\t\tN_(\" - add contents of untracked files to the staged set of changes\"));\n      +\tprintf(\"\\n\");\n      +}\n      \n     @@ -53,10 +50,11 @@\n       \n       void add_i_print_modified(void);\n       \n     -+void show_help(void);\n     -+\n     - #endif\n     +-#endif\n       \\ No newline at end of file\n     ++void add_i_show_help(void);\n     ++\n     ++#endif\n      \n       diff --git a/builtin/add--helper.c b/builtin/add--helper.c\n       --- a/builtin/add--helper.c\n     @@ -85,7 +83,7 @@\n       \tif (mode == STATUS)\n       \t\tadd_i_print_modified();\n      +\telse if (mode == HELP)\n     -+\t\tshow_help();\n     ++\t\tadd_i_show_help();\n       \telse\n       \t\tusage_with_options(builtin_add_helper_usage,\n       \t\t\t\t   options);\n 6:  86d85face8 < -:  ---------- Git.pm: introduce environment variable GIT_TEST_PRETEND_TTY\n -:  ---------- > 6:  2b4714b8d0 t3701-add-interactive: test add_i_show_help()\n 7:  060806010e ! 7:  6ede6d9251 add--interactive.perl: use add--helper --show-help for help_cmd\n     @@ -5,11 +5,6 @@\n          Change help_cmd sub in git-add--interactive.perl to use\n          show-help command from builtin add--helper.\n      \n     -    Add test to t3701-add-interactive to verify that show-help\n     -    outputs expected content. Use GIT_PRETENT_TTY\n     -    introduced in earlier commit to be able to test output color\n     -    on Windows.\n     -\n          Signed-off-by: Slavica Djukic <slawica92@hotmail.com>\n      \n       diff --git a/git-add--interactive.perl b/git-add--interactive.perl\n     @@ -33,37 +28,3 @@\n       }\n       \n       sub process_args {\n     -\n     - diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh\n     - --- a/t/t3701-add-interactive.sh\n     - +++ b/t/t3701-add-interactive.sh\n     -@@\n     - \ttest_cmp expected-2 actual\n     - '\n     - \n     -+test_expect_success 'show help from add--helper' '\n     -+\tgit reset --hard &&\n     -+\tcat >expect <<-\\EOF &&\n     -+\n     -+\t<BOLD>*** Commands ***<RESET>\n     -+\t  1: <BOLD;BLUE>s<RESET>tatus\t  2: <BOLD;BLUE>u<RESET>pdate\t  3: <BOLD;BLUE>r<RESET>evert\t  4: <BOLD;BLUE>a<RESET>dd untracked\n     -+\t  5: <BOLD;BLUE>p<RESET>atch\t  6: <BOLD;BLUE>d<RESET>iff\t  7: <BOLD;BLUE>q<RESET>uit\t  8: <BOLD;BLUE>h<RESET>elp\n     -+\t<BOLD;BLUE>What now<RESET>> \n     -+\t<BOLD;RED>status        - show paths with changes\n     -+\tupdate        - add working tree state to the staged set of changes\n     -+\trevert        - revert staged set of changes back to the HEAD version\n     -+\tpatch         - pick hunks and update selectively\n     -+\tdiff          - view diff between HEAD and index\n     -+\tadd untracked - add contents of untracked files to the staged set of changes<RESET>\n     -+\t<BOLD>*** Commands ***<RESET>\n     -+\t  1: <BOLD;BLUE>s<RESET>tatus\t  2: <BOLD;BLUE>u<RESET>pdate\t  3: <BOLD;BLUE>r<RESET>evert\t  4: <BOLD;BLUE>a<RESET>dd untracked\n     -+\t  5: <BOLD;BLUE>p<RESET>atch\t  6: <BOLD;BLUE>d<RESET>iff\t  7: <BOLD;BLUE>q<RESET>uit\t  8: <BOLD;BLUE>h<RESET>elp\n     -+\t<BOLD;BLUE>What now<RESET>> \n     -+\tBye.\n     -+\tEOF\n     -+\ttest_write_lines h | GIT_TEST_PRETEND_TTY=1 git add -i >actual.colored &&\n     -+\ttest_decode_color <actual.colored >actual &&\n     -+\ttest_i18ncmp expect actual\n     -+'\n     -+\n     - test_done\n\n-- \ngitgitgadget\n"},{"id":"367055","messageId":"737767b6f4e0367a7797020bd07a6b4357a47413.1547797620.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.v2.git.gitgitgadget@gmail.com","subject":"[PATCH v2 1/7] diff: export diffstat interface","fromName":"Daniel Ferreira via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-01-18T07:47:02Z","receivedAt":"2019-01-18T07:47:07Z","isPatch":true,"sender":{"key":"bnmvco@gmail.com","avatar":"https://avatars.githubusercontent.com/u/735397?v=4"},"body":"From: Daniel Ferreira <bnmvco@gmail.com>\n\nMake the diffstat interface (namely, the diffstat_t struct and\ncompute_diffstat) no longer be internal to diff.c and allow it to be used\nby other parts of git.\n\nThis is helpful for code that may want to easily extract information\nfrom files using the diff machinery, while flushing it differently from\nhow the show_* functions used by diff_flush() do it. One example is the\nbuiltin implementation of git-add--interactive's status.\n\nSigned-off-by: Daniel Ferreira <bnmvco@gmail.com>\nSigned-off-by: Slavica Djukic <slawica92@hotmail.com>\n---\n diff.c | 36 ++++++++++++++----------------------\n diff.h | 18 ++++++++++++++++++\n 2 files changed, 32 insertions(+), 22 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex dc9965e836..46a7d8cf29 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -2421,22 +2421,6 @@ static void pprint_rename(struct strbuf *name, const char *a, const char *b)\n \t}\n }\n \n-struct diffstat_t {\n-\tint nr;\n-\tint alloc;\n-\tstruct diffstat_file {\n-\t\tchar *from_name;\n-\t\tchar *name;\n-\t\tchar *print_name;\n-\t\tconst char *comments;\n-\t\tunsigned is_unmerged:1;\n-\t\tunsigned is_binary:1;\n-\t\tunsigned is_renamed:1;\n-\t\tunsigned is_interesting:1;\n-\t\tuintmax_t added, deleted;\n-\t} **files;\n-};\n-\n static struct diffstat_file *diffstat_add(struct diffstat_t *diffstat,\n \t\t\t\t\t  const char *name_a,\n \t\t\t\t\t  const char *name_b)\n@@ -5922,12 +5906,7 @@ void diff_flush(struct diff_options *options)\n \t    dirstat_by_line) {\n \t\tstruct diffstat_t diffstat;\n \n-\t\tmemset(&diffstat, 0, sizeof(struct diffstat_t));\n-\t\tfor (i = 0; i < q->nr; i++) {\n-\t\t\tstruct diff_filepair *p = q->queue[i];\n-\t\t\tif (check_pair_status(p))\n-\t\t\t\tdiff_flush_stat(p, options, &diffstat);\n-\t\t}\n+\t\tcompute_diffstat(options, &diffstat);\n \t\tif (output_format & DIFF_FORMAT_NUMSTAT)\n \t\t\tshow_numstat(&diffstat, options);\n \t\tif (output_format & DIFF_FORMAT_DIFFSTAT)\n@@ -6227,6 +6206,19 @@ static int is_submodule_ignored(const char *path, struct diff_options *options)\n \treturn ignored;\n }\n \n+void compute_diffstat(struct diff_options *options, struct diffstat_t *diffstat)\n+{\n+\tint i;\n+\tstruct diff_queue_struct *q = &diff_queued_diff;\n+\n+\tmemset(diffstat, 0, sizeof(struct diffstat_t));\n+\tfor (i = 0; i < q->nr; i++) {\n+\t\tstruct diff_filepair *p = q->queue[i];\n+\t\tif (check_pair_status(p))\n+\t\t\tdiff_flush_stat(p, options, diffstat);\n+\t}\n+}\n+\n void diff_addremove(struct diff_options *options,\n \t\t    int addremove, unsigned mode,\n \t\t    const struct object_id *oid,\ndiff --git a/diff.h b/diff.h\nindex ce5e8a8183..7809db3039 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -239,6 +239,22 @@ void diff_emit_submodule_error(struct diff_options *o, const char *err);\n void diff_emit_submodule_pipethrough(struct diff_options *o,\n \t\t\t\t     const char *line, int len);\n \n+struct diffstat_t {\n+\tint nr;\n+\tint alloc;\n+\tstruct diffstat_file {\n+\t\tchar *from_name;\n+\t\tchar *name;\n+\t\tchar *print_name;\n+\t\tconst char *comments;\n+\t\tunsigned is_unmerged:1;\n+\t\tunsigned is_binary:1;\n+\t\tunsigned is_renamed:1;\n+\t\tunsigned is_interesting:1;\n+\t\tuintmax_t added, deleted;\n+\t} **files;\n+};\n+\n enum color_diff {\n \tDIFF_RESET = 0,\n \tDIFF_CONTEXT = 1,\n@@ -327,6 +343,8 @@ void diff_change(struct diff_options *,\n \n struct diff_filepair *diff_unmerge(struct diff_options *, const char *path);\n \n+void compute_diffstat(struct diff_options *options, struct diffstat_t *diffstat);\n+\n #define DIFF_SETUP_REVERSE      \t1\n #define DIFF_SETUP_USE_SIZE_CACHE\t4\n \n-- \ngitgitgadget\n\n"},{"id":"367056","messageId":"91b1963125f7dfdd46fe5166125ee59ef2557b13.1547797620.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.v2.git.gitgitgadget@gmail.com","subject":"[PATCH v2 2/7] add--helper: create builtin helper for interactive add","fromName":"Daniel Ferreira via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-01-18T07:47:03Z","receivedAt":"2019-01-18T07:47:09Z","isPatch":true,"sender":{"key":"bnmvco@gmail.com","avatar":"https://avatars.githubusercontent.com/u/735397?v=4"},"body":"From: Daniel Ferreira <bnmvco@gmail.com>\n\nCreate a builtin helper for git-add--interactive, which right now is not\nable to do anything.\n\nThis is the first step in an effort to convert git-add--interactive.perl\nto a C builtin, in search for better portability, expressibility and\nperformance (specially on non-POSIX systems like Windows).\n\nAdditionally, an eventual complete port of git-add--interactive would\nremove the last \"big\" Git script to have Perl as a dependency, allowing\nmost Git users to have a NOPERL build running without big losses.\n\nSigned-off-by: Daniel Ferreira <bnmvco@gmail.com>\nSigned-off-by: Slavica Djukic <slawica92@hotmail.com>\n---\n .gitignore            | 1 +\n Makefile              | 1 +\n builtin.h             | 1 +\n builtin/add--helper.c | 6 ++++++\n git.c                 | 1 +\n 5 files changed, 10 insertions(+)\n create mode 100644 builtin/add--helper.c\n\ndiff --git a/.gitignore b/.gitignore\nindex 0d77ea5894..2ee71ed217 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -15,6 +15,7 @@\n /git\n /git-add\n /git-add--interactive\n+/git-add--helper\n /git-am\n /git-annotate\n /git-apply\ndiff --git a/Makefile b/Makefile\nindex 1a44c811aa..9c84b80739 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1023,6 +1023,7 @@ LIB_OBJS += xdiff-interface.o\n LIB_OBJS += zlib.o\n \n BUILTIN_OBJS += builtin/add.o\n+BUILTIN_OBJS += builtin/add--helper.o\n BUILTIN_OBJS += builtin/am.o\n BUILTIN_OBJS += builtin/annotate.o\n BUILTIN_OBJS += builtin/apply.o\ndiff --git a/builtin.h b/builtin.h\nindex 6538932e99..dd811ef7d5 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -128,6 +128,7 @@ extern void setup_auto_pager(const char *cmd, int def);\n extern int is_builtin(const char *s);\n \n extern int cmd_add(int argc, const char **argv, const char *prefix);\n+extern int cmd_add__helper(int argc, const char **argv, const char *prefix);\n extern int cmd_am(int argc, const char **argv, const char *prefix);\n extern int cmd_annotate(int argc, const char **argv, const char *prefix);\n extern int cmd_apply(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/add--helper.c b/builtin/add--helper.c\nnew file mode 100644\nindex 0000000000..6a97f0e191\n--- /dev/null\n+++ b/builtin/add--helper.c\n@@ -0,0 +1,6 @@\n+#include \"builtin.h\"\n+\n+int cmd_add__helper(int argc, const char **argv, const char *prefix)\n+{\n+\treturn 0;\n+}\ndiff --git a/git.c b/git.c\nindex 2f604a41ea..5507591f2e 100644\n--- a/git.c\n+++ b/git.c\n@@ -443,6 +443,7 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)\n \n static struct cmd_struct commands[] = {\n \t{ \"add\", cmd_add, RUN_SETUP | NEED_WORK_TREE },\n+\t{ \"add--helper\", cmd_add__helper, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"am\", cmd_am, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"annotate\", cmd_annotate, RUN_SETUP | NO_PARSEOPT },\n \t{ \"apply\", cmd_apply, RUN_SETUP_GENTLY },\n-- \ngitgitgadget\n\n"},{"id":"367057","messageId":"4950c889aacd869a8f4fd30b8d5cde4b43957347.1547797620.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.v2.git.gitgitgadget@gmail.com","subject":"[PATCH v2 4/7] add--interactive.perl: use add--helper --status for status_cmd","fromName":"Daniel Ferreira via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-01-18T07:47:05Z","receivedAt":"2019-01-18T07:47:09Z","isPatch":true,"sender":{"key":"bnmvco@gmail.com","avatar":"https://avatars.githubusercontent.com/u/735397?v=4"},"body":"From: Daniel Ferreira <bnmvco@gmail.com>\n\nCall the newly introduced add--helper builtin on\nstatus_cmd() instead of relying on add--interactive's Perl\nfunctions to build print the numstat.\n\nSigned-off-by: Daniel Ferreira <bnmvco@gmail.com>\nSigned-off-by: Slavica Djukic <slawica92@hotmail.com>\n---\n git-add--interactive.perl | 4 +---\n 1 file changed, 1 insertion(+), 3 deletions(-)\n\ndiff --git a/git-add--interactive.perl b/git-add--interactive.perl\nindex 20eb81cc92..a6536f9cf3 100755\n--- a/git-add--interactive.perl\n+++ b/git-add--interactive.perl\n@@ -597,9 +597,7 @@ sub prompt_help_cmd {\n }\n \n sub status_cmd {\n-\tlist_and_choose({ LIST_ONLY => 1, HEADER => $status_head },\n-\t\t\tlist_modified());\n-\tprint \"\\n\";\n+\tsystem(qw(git add--helper --status));\n }\n \n sub say_n_paths {\n-- \ngitgitgadget\n\n"},{"id":"367058","messageId":"cf4e913a5a01cfb9e9b8b83b222cd4647fbc0bf2.1547797620.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.v2.git.gitgitgadget@gmail.com","subject":"[PATCH v2 5/7] add-interactive.c: implement show-help command","fromName":"Slavica Djukic via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-01-18T07:47:06Z","receivedAt":"2019-01-18T07:47:10Z","isPatch":true,"sender":{"key":"slavicadj.ip2018@gmail.com","avatar":null},"body":"From: Slavica Djukic <slawica92@hotmail.com>\n\nImplement show-help command in add-interactive.c and use it in\nbuiltin add--helper.c.\n\nUse command name \"show-help\" instead of \"help\": add--helper is\nbuiltin, hence add--helper --help would be intercepted by\nhandle_builtin and re-routed to the help command, without ever\ncalling cmd_add__helper().\n\nSigned-off-by: Slavica Djukic <slawica92@hotmail.com>\n---\n add-interactive.c     | 23 +++++++++++++++++++++++\n add-interactive.h     |  4 +++-\n builtin/add--helper.c |  7 ++++++-\n 3 files changed, 32 insertions(+), 2 deletions(-)\n\ndiff --git a/add-interactive.c b/add-interactive.c\nindex c55d934186..76c3f4c3eb 100644\n--- a/add-interactive.c\n+++ b/add-interactive.c\n@@ -244,3 +244,26 @@ void add_i_print_modified(void)\n \tfree(files);\n \thashmap_free(&s.file_map, 1);\n }\n+\n+void add_i_show_help(void)\n+{\n+\tconst char *help_color = get_color(COLOR_HELP);\n+\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"status\"), \n+\t\tN_(\"        - show paths with changes\"));\n+\tprintf(\"\\n\");\n+\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"update\"), \n+\t\tN_(\"        - add working tree state to the staged set of changes\"));\n+\tprintf(\"\\n\");\t\n+\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"revert\"),\n+\t\tN_(\"        - revert staged set of changes back to the HEAD version\"));\n+\tprintf(\"\\n\");\n+\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"patch\"),\n+\t\tN_(\"         - pick hunks and update selectively\"));\n+\tprintf(\"\\n\");\n+\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"diff\"),\n+\t\tN_(\"          - view diff between HEAD and index\"));\n+\tprintf(\"\\n\");\n+\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"add untracked\"),\n+\t\tN_(\" - add contents of untracked files to the staged set of changes\"));\n+\tprintf(\"\\n\");\n+}\ndiff --git a/add-interactive.h b/add-interactive.h\nindex 1f4747553c..46e17c5c71 100644\n--- a/add-interactive.h\n+++ b/add-interactive.h\n@@ -5,4 +5,6 @@ int add_i_config(const char *var, const char *value, void *cbdata);\n \n void add_i_print_modified(void);\n \n-#endif\n\\ No newline at end of file\n+void add_i_show_help(void);\n+\n+#endif\ndiff --git a/builtin/add--helper.c b/builtin/add--helper.c\nindex 43545d9af5..a3b3a68b68 100644\n--- a/builtin/add--helper.c\n+++ b/builtin/add--helper.c\n@@ -10,7 +10,8 @@ static const char * const builtin_add_helper_usage[] = {\n \n enum cmd_mode {\n \tDEFAULT = 0,\n-\tSTATUS\n+\tSTATUS,\n+\tHELP\n };\n \n int cmd_add__helper(int argc, const char **argv, const char *prefix)\n@@ -20,6 +21,8 @@ int cmd_add__helper(int argc, const char **argv, const char *prefix)\n \tstruct option options[] = {\n \t\tOPT_CMDMODE(0, \"status\", &mode,\n \t\t\t N_(\"print status information with diffstat\"), STATUS),\n+\t\tOPT_CMDMODE(0, \"show-help\", &mode,\n+\t\t\t N_(\"show help\"), HELP),\n \t\tOPT_END()\n \t};\n \n@@ -30,6 +33,8 @@ int cmd_add__helper(int argc, const char **argv, const char *prefix)\n \n \tif (mode == STATUS)\n \t\tadd_i_print_modified();\n+\telse if (mode == HELP)\n+\t\tadd_i_show_help();\n \telse\n \t\tusage_with_options(builtin_add_helper_usage,\n \t\t\t\t   options);\n-- \ngitgitgadget\n\n"},{"id":"367059","messageId":"d247ef69fe2c9d017b8d363de554a52269c0d281.1547797620.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.v2.git.gitgitgadget@gmail.com","subject":"[PATCH v2 3/7] add-interactive.c: implement status command","fromName":"Daniel Ferreira via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-01-18T07:47:04Z","receivedAt":"2019-01-18T07:47:12Z","isPatch":true,"sender":{"key":"bnmvco@gmail.com","avatar":"https://avatars.githubusercontent.com/u/735397?v=4"},"body":"From: Daniel Ferreira <bnmvco@gmail.com>\n\nAdd new files: add-interactive.c and add-interactive.h, which\nwill be used for implementing \"application logic\" of git add -i,\nwhereas add--helper.c will be used mostly for parsing the command line.\nWe're a bit lax with the command-line parsing, as the command is\nintended to be called only by one internal user: the add--interactive script.\n\nImplement add --interactive's status command in add-interactive.c and\nuse it in builtin add--helper.c.\n\nIt prints a numstat comparing changed files between a) the worktree and\nthe index; b) the index and the HEAD.\n\nTo do so, we use run_diff_index() and run_diff_files() to get changed\nfiles, use the diffstat API on them to get the numstat and use a\ncombination of a hashmap and qsort() to print the result in\nO(n) + O(n lg n) complexity.\n\nThis is the first interactive add command implemented in C of those\nanticipated by the previous commit, which introduced\nthe add--helper built-in.\n\nSigned-off-by: Daniel Ferreira <bnmvco@gmail.com>\nSigned-off-by: Slavica Djukic <slawica92@hotmail.com>\n---\n Makefile              |   1 +\n add-interactive.c     | 246 ++++++++++++++++++++++++++++++++++++++++++\n add-interactive.h     |   8 ++\n builtin/add--helper.c |  32 ++++++\n 4 files changed, 287 insertions(+)\n create mode 100644 add-interactive.c\n create mode 100644 add-interactive.h\n\ndiff --git a/Makefile b/Makefile\nindex 9c84b80739..2a4a5cc37b 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -827,6 +827,7 @@ LIB_H = $(shell $(FIND) . \\\n \t-name '*.h' -print)\n \n LIB_OBJS += abspath.o\n+LIB_OBJS += add-interactive.o\n LIB_OBJS += advice.o\n LIB_OBJS += alias.o\n LIB_OBJS += alloc.o\ndiff --git a/add-interactive.c b/add-interactive.c\nnew file mode 100644\nindex 0000000000..c55d934186\n--- /dev/null\n+++ b/add-interactive.c\n@@ -0,0 +1,246 @@\n+#include \"add-interactive.h\"\n+#include \"cache.h\"\n+#include \"commit.h\"\n+#include \"color.h\"\n+#include \"config.h\"\n+#include \"diffcore.h\"\n+#include \"revision.h\"\n+\n+#define HEADER_INDENT \"      \"\n+\n+enum collection_phase {\n+\tWORKTREE,\n+\tINDEX\n+};\n+\n+struct file_stat {\n+\tstruct hashmap_entry ent;\n+\tstruct {\n+\t\tuintmax_t added, deleted;\n+\t} index, worktree;\n+\tchar name[FLEX_ARRAY];\n+};\n+\n+struct collection_status {\n+\tenum collection_phase phase;\n+\n+\tconst char *reference;\n+\tstruct pathspec pathspec;\n+\n+\tstruct hashmap file_map;\n+};\n+\n+static int use_color = -1;\n+enum color_add_i {\n+\tCOLOR_PROMPT,\n+\tCOLOR_HEADER,\n+\tCOLOR_HELP,\n+\tCOLOR_ERROR\n+};\n+\n+static char colors[][COLOR_MAXLEN] = {\n+\tGIT_COLOR_BOLD_BLUE, /* Prompt */\n+\tGIT_COLOR_BOLD,      /* Header */\n+\tGIT_COLOR_BOLD_RED,  /* Help */\n+\tGIT_COLOR_BOLD_RED   /* Error */\n+};\n+\n+static const char *get_color(enum color_add_i ix)\n+{\n+\tif (want_color(use_color))\n+\t\treturn colors[ix];\n+\treturn \"\";\n+}\n+\n+static int parse_color_slot(const char *slot)\n+{\n+\tif (!strcasecmp(slot, \"prompt\"))\n+\t\treturn COLOR_PROMPT;\n+\tif (!strcasecmp(slot, \"header\"))\n+\t\treturn COLOR_HEADER;\n+\tif (!strcasecmp(slot, \"help\"))\n+\t\treturn COLOR_HELP;\n+\tif (!strcasecmp(slot, \"error\"))\n+\t\treturn COLOR_ERROR;\n+\n+\treturn -1;\n+}\n+\n+int add_i_config(const char *var,\n+\t\tconst char *value, void *cbdata)\n+{\n+\tconst char *name;\n+\n+\tif (!strcmp(var, \"color.interactive\")) {\n+\t\tuse_color = git_config_colorbool(var, value);\n+\t\treturn 0;\n+\t}\n+\n+\tif (skip_prefix(var, \"color.interactive.\", &name)) {\n+\t\tint slot = parse_color_slot(name);\n+\t\tif (slot < 0)\n+\t\t\treturn 0;\n+\t\tif (!value)\n+\t\t\treturn config_error_nonbool(var);\n+\t\treturn color_parse(value, colors[slot]);\n+\t}\n+\n+\treturn git_default_config(var, value, cbdata);\n+}\n+\n+static int hash_cmp(const void *unused_cmp_data, const void *entry,\n+\t\t\tconst void *entry_or_key, const void *keydata)\n+{\n+\tconst struct file_stat *e1 = entry, *e2 = entry_or_key;\n+\tconst char *name = keydata ? keydata : e2->name;\n+\n+\treturn strcmp(e1->name, name);\n+}\n+\n+static int alphabetical_cmp(const void *a, const void *b)\n+{\n+\tstruct file_stat *f1 = *((struct file_stat **)a);\n+\tstruct file_stat *f2 = *((struct file_stat **)b);\n+\n+\treturn strcmp(f1->name, f2->name);\n+}\n+\n+static void collect_changes_cb(struct diff_queue_struct *q,\n+\t\t\t\t\t struct diff_options *options,\n+\t\t\t\t\t void *data)\n+{\n+\tstruct collection_status *s = data;\n+\tstruct diffstat_t stat = { 0 };\n+\tint i;\n+\n+\tif (!q->nr)\n+\t\treturn;\n+\n+\tcompute_diffstat(options, &stat);\n+\n+\tfor (i = 0; i < stat.nr; i++) {\n+\t\tstruct file_stat *entry;\n+\t\tconst char *name = stat.files[i]->name;\n+\t\tunsigned int hash = strhash(name);\n+\n+\t\tentry = hashmap_get_from_hash(&s->file_map, hash, name);\n+\t\tif (!entry) {\n+\t\t\tFLEX_ALLOC_STR(entry, name, name);\n+\t\t\thashmap_entry_init(entry, hash);\n+\t\t\thashmap_add(&s->file_map, entry);\n+\t\t}\n+\n+\t\tif (s->phase == WORKTREE) {\n+\t\t\tentry->worktree.added = stat.files[i]->added;\n+\t\t\tentry->worktree.deleted = stat.files[i]->deleted;\n+\t\t} else if (s->phase == INDEX) {\n+\t\t\tentry->index.added = stat.files[i]->added;\n+\t\t\tentry->index.deleted = stat.files[i]->deleted;\n+\t\t}\n+\t}\n+}\n+\n+static void collect_changes_worktree(struct collection_status *s)\n+{\n+\tstruct rev_info rev;\n+\n+\ts->phase = WORKTREE;\n+\n+\tinit_revisions(&rev, NULL);\n+\tsetup_revisions(0, NULL, &rev, NULL);\n+\n+\trev.max_count = 0;\n+\n+\trev.diffopt.flags.ignore_dirty_submodules = 1;\n+\trev.diffopt.output_format = DIFF_FORMAT_CALLBACK;\n+\trev.diffopt.format_callback = collect_changes_cb;\n+\trev.diffopt.format_callback_data = s;\n+\n+\trun_diff_files(&rev, 0);\n+}\n+\n+static void collect_changes_index(struct collection_status *s)\n+{\n+\tstruct rev_info rev;\n+\tstruct setup_revision_opt opt = { 0 };\n+\n+\ts->phase = INDEX;\n+\n+\tinit_revisions(&rev, NULL);\n+\topt.def = s->reference;\n+\tsetup_revisions(0, NULL, &rev, &opt);\n+\n+\trev.diffopt.output_format = DIFF_FORMAT_CALLBACK;\n+\trev.diffopt.format_callback = collect_changes_cb;\n+\trev.diffopt.format_callback_data = s;\n+\n+\trun_diff_index(&rev, 1);\n+}\n+\n+void add_i_print_modified(void)\n+{\n+\tint i = 0;\n+\tstruct collection_status s;\n+\t/* TRANSLATORS: you can adjust this to align \"git add -i\" status menu */\n+\tconst char *modified_fmt = _(\"%12s %12s %s\");\n+\tconst char *header_color = get_color(COLOR_HEADER);\n+\tstruct object_id sha1;\n+\n+\tstruct hashmap_iter iter;\n+\tstruct file_stat **files;\n+\tstruct file_stat *entry;\n+\n+\tif (read_cache() < 0)\n+\t\treturn;\n+\n+\ts.reference = !get_oid(\"HEAD\", &sha1) ? \"HEAD\": empty_tree_oid_hex();\n+\thashmap_init(&s.file_map, hash_cmp, NULL, 0);\n+\n+\tcollect_changes_worktree(&s);\n+\tcollect_changes_index(&s);\n+\n+\tif (hashmap_get_size(&s.file_map) < 1) {\n+\t\tprintf(\"\\n\");\n+\t\treturn;\n+\t}\n+\n+\tprintf(HEADER_INDENT);\n+\tcolor_fprintf(stdout, header_color, modified_fmt, _(\"staged\"),\n+\t\t\t_(\"unstaged\"), _(\"path\"));\n+\tprintf(\"\\n\");\n+\n+\thashmap_iter_init(&s.file_map, &iter);\n+\n+\tfiles = xcalloc(hashmap_get_size(&s.file_map), sizeof(struct file_stat *));\n+\twhile ((entry = hashmap_iter_next(&iter))) {\n+\t\tfiles[i++] = entry;\n+\t}\n+\tQSORT(files, hashmap_get_size(&s.file_map), alphabetical_cmp);\n+\n+\tfor (i = 0; i < hashmap_get_size(&s.file_map); i++) {\n+\t\tstruct file_stat *f = files[i];\n+\n+\t\tchar worktree_changes[50];\n+\t\tchar index_changes[50];\n+\n+\t\tif (f->worktree.added || f->worktree.deleted)\n+\t\t\tsnprintf(worktree_changes, 50, \"+%\"PRIuMAX\"/-%\"PRIuMAX, f->worktree.added,\n+\t\t\t\t\tf->worktree.deleted);\n+\t\telse\n+\t\t\tsnprintf(worktree_changes, 50, \"%s\", _(\"nothing\"));\n+\n+\t\tif (f->index.added || f->index.deleted)\n+\t\t\tsnprintf(index_changes, 50, \"+%\"PRIuMAX\"/-%\"PRIuMAX, f->index.added,\n+\t\t\t\t\tf->index.deleted);\n+\t\telse\n+\t\t\tsnprintf(index_changes, 50, \"%s\", _(\"unchanged\"));\n+\n+\t\tprintf(\" %2d: \", i + 1);\n+\t\tprintf(modified_fmt, index_changes, worktree_changes, f->name);\n+\t\tprintf(\"\\n\");\n+\t}\n+\tprintf(\"\\n\");\n+\n+\tfree(files);\n+\thashmap_free(&s.file_map, 1);\n+}\ndiff --git a/add-interactive.h b/add-interactive.h\nnew file mode 100644\nindex 0000000000..1f4747553c\n--- /dev/null\n+++ b/add-interactive.h\n@@ -0,0 +1,8 @@\n+#ifndef ADD_INTERACTIVE_H\n+#define ADD_INTERACTIVE_H\n+\n+int add_i_config(const char *var, const char *value, void *cbdata);\n+\n+void add_i_print_modified(void);\n+\n+#endif\n\\ No newline at end of file\ndiff --git a/builtin/add--helper.c b/builtin/add--helper.c\nindex 6a97f0e191..43545d9af5 100644\n--- a/builtin/add--helper.c\n+++ b/builtin/add--helper.c\n@@ -1,6 +1,38 @@\n+#include \"add-interactive.h\"\n #include \"builtin.h\"\n+#include \"config.h\"\n+#include \"revision.h\"\n+\n+static const char * const builtin_add_helper_usage[] = {\n+\tN_(\"git add-interactive--helper <command>\"),\n+\tNULL\n+};\n+\n+enum cmd_mode {\n+\tDEFAULT = 0,\n+\tSTATUS\n+};\n \n int cmd_add__helper(int argc, const char **argv, const char *prefix)\n {\n+\tenum cmd_mode mode = DEFAULT;\n+\n+\tstruct option options[] = {\n+\t\tOPT_CMDMODE(0, \"status\", &mode,\n+\t\t\t N_(\"print status information with diffstat\"), STATUS),\n+\t\tOPT_END()\n+\t};\n+\n+\tgit_config(add_i_config, NULL);\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     builtin_add_helper_usage,\n+\t\t\t     PARSE_OPT_KEEP_ARGV0);\n+\n+\tif (mode == STATUS)\n+\t\tadd_i_print_modified();\n+\telse\n+\t\tusage_with_options(builtin_add_helper_usage,\n+\t\t\t\t   options);\n+\n \treturn 0;\n }\n-- \ngitgitgadget\n\n"},{"id":"367060","messageId":"6ede6d92511d372eb30a78d59912135faad069aa.1547797620.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.v2.git.gitgitgadget@gmail.com","subject":"[PATCH v2 7/7] add--interactive.perl: use add--helper --show-help for help_cmd","fromName":"Slavica Djukic via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-01-18T07:47:07Z","receivedAt":"2019-01-18T07:47:13Z","isPatch":true,"sender":{"key":"slavicadj.ip2018@gmail.com","avatar":null},"body":"From: Slavica Djukic <slawica92@hotmail.com>\n\nChange help_cmd sub in git-add--interactive.perl to use\nshow-help command from builtin add--helper.\n\nSigned-off-by: Slavica Djukic <slawica92@hotmail.com>\n---\n git-add--interactive.perl | 11 +----------\n 1 file changed, 1 insertion(+), 10 deletions(-)\n\ndiff --git a/git-add--interactive.perl b/git-add--interactive.perl\nindex a6536f9cf3..32ee729a58 100755\n--- a/git-add--interactive.perl\n+++ b/git-add--interactive.perl\n@@ -1717,16 +1717,7 @@ sub quit_cmd {\n }\n \n sub help_cmd {\n-# TRANSLATORS: please do not translate the command names\n-# 'status', 'update', 'revert', etc.\n-\tprint colored $help_color, __ <<'EOF' ;\n-status        - show paths with changes\n-update        - add working tree state to the staged set of changes\n-revert        - revert staged set of changes back to the HEAD version\n-patch         - pick hunks and update selectively\n-diff          - view diff between HEAD and index\n-add untracked - add contents of untracked files to the staged set of changes\n-EOF\n+\tsystem(qw(git add--helper --show-help));\n }\n \n sub process_args {\n-- \ngitgitgadget\n"},{"id":"367061","messageId":"2b4714b8d04f98a6d35f3f4f15881406fa87c320.1547797620.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.v2.git.gitgitgadget@gmail.com","subject":"[PATCH v2 6/7] t3701-add-interactive: test add_i_show_help()","fromName":"Slavica Djukic via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-01-18T07:47:06Z","receivedAt":"2019-01-18T07:47:18Z","isPatch":true,"sender":{"key":"slavicadj.ip2018@gmail.com","avatar":null},"body":"From: Slavica Djukic <slawica92@hotmail.com>\n\nAdd test to t3701-add-interactive to verify that add_i_show_help()\noutputs expected content.\nAlso, add it before changing git-add--interactive.perl's help_cmd\nto demonstrate that there are no changes introduced by the\nconversion to C.\nPrefix git add -i call with GIT_PAGER_IN_USE=true TERM=vt100\nto force colored output on Windows.\n\nSigned-off-by: Slavica Djukic <slawica92@hotmail.com>\n---\n t/t3701-add-interactive.sh | 24 ++++++++++++++++++++++++\n 1 file changed, 24 insertions(+)\n\ndiff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh\nindex 65dfbc033a..14e3286995 100755\n--- a/t/t3701-add-interactive.sh\n+++ b/t/t3701-add-interactive.sh\n@@ -639,4 +639,28 @@ test_expect_success 'add -p patch editing works with pathological context lines'\n \ttest_cmp expected-2 actual\n '\n \n+test_expect_success 'show help from add--helper' '\n+\tgit reset --hard &&\n+\tcat >expect <<-\\EOF &&\n+\n+\t<BOLD>*** Commands ***<RESET>\n+\t  1: <BOLD;BLUE>s<RESET>tatus\t  2: <BOLD;BLUE>u<RESET>pdate\t  3: <BOLD;BLUE>r<RESET>evert\t  4: <BOLD;BLUE>a<RESET>dd untracked\n+\t  5: <BOLD;BLUE>p<RESET>atch\t  6: <BOLD;BLUE>d<RESET>iff\t  7: <BOLD;BLUE>q<RESET>uit\t  8: <BOLD;BLUE>h<RESET>elp\n+\t<BOLD;BLUE>What now<RESET>> <BOLD;RED>status        - show paths with changes<RESET>\n+\t<BOLD;RED>update        - add working tree state to the staged set of changes<RESET>\n+\t<BOLD;RED>revert        - revert staged set of changes back to the HEAD version<RESET>\n+\t<BOLD;RED>patch         - pick hunks and update selectively<RESET>\n+\t<BOLD;RED>diff          - view diff between HEAD and index<RESET>\n+\t<BOLD;RED>add untracked - add contents of untracked files to the staged set of changes<RESET>\n+\t<BOLD>*** Commands ***<RESET>\n+\t  1: <BOLD;BLUE>s<RESET>tatus\t  2: <BOLD;BLUE>u<RESET>pdate\t  3: <BOLD;BLUE>r<RESET>evert\t  4: <BOLD;BLUE>a<RESET>dd untracked\n+\t  5: <BOLD;BLUE>p<RESET>atch\t  6: <BOLD;BLUE>d<RESET>iff\t  7: <BOLD;BLUE>q<RESET>uit\t  8: <BOLD;BLUE>h<RESET>elp\n+\t<BOLD;BLUE>What now<RESET>> \n+\tBye.\n+\tEOF\n+\ttest_write_lines h | GIT_PAGER_IN_USE=true TERM=vt100 git add -i >actual.colored &&\n+\ttest_decode_color <actual.colored >actual &&\n+\ttest_i18ncmp expect actual\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"367082","messageId":"01255e61-a3b1-e509-9f33-36ad07640b4f@talktalk.net","threadId":"50075","inReplyTo":"cf4e913a5a01cfb9e9b8b83b222cd4647fbc0bf2.1547797620.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 5/7] add-interactive.c: implement show-help command","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2019-01-18T11:20:51Z","receivedAt":"2019-01-18T11:20:49Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Slavica\n\nI think this round is looking good I've got a couple of comments about\nthe translation of the help text but everything else looks fine to me\nnow. In future when you're posting a new version it's helpful CC the\npeople who commented on the previous version(s).\n\nOn 18/01/2019 07:47, Slavica Djukic via GitGitGadget wrote:\n> From: Slavica Djukic <slawica92@hotmail.com>\n> \n> Implement show-help command in add-interactive.c and use it in\n> builtin add--helper.c.\n> \n> Use command name \"show-help\" instead of \"help\": add--helper is\n> builtin, hence add--helper --help would be intercepted by\n> handle_builtin and re-routed to the help command, without ever\n> calling cmd_add__helper().\n> \n> Signed-off-by: Slavica Djukic <slawica92@hotmail.com>\n> ---\n>  add-interactive.c     | 23 +++++++++++++++++++++++\n>  add-interactive.h     |  4 +++-\n>  builtin/add--helper.c |  7 ++++++-\n>  3 files changed, 32 insertions(+), 2 deletions(-)\n> \n> diff --git a/add-interactive.c b/add-interactive.c\n> index c55d934186..76c3f4c3eb 100644\n> --- a/add-interactive.c\n> +++ b/add-interactive.c\n> @@ -244,3 +244,26 @@ void add_i_print_modified(void)\n>  \tfree(files);\n>  \thashmap_free(&s.file_map, 1);\n>  }\n> +\n> +void add_i_show_help(void)\n> +{\n> +\tconst char *help_color = get_color(COLOR_HELP);\n> +\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"status\"), \n> +\t\tN_(\"        - show paths with changes\"));\n> +\tprintf(\"\\n\");\n\nThere seems to be a bit of confusion with the translation of these\nmessages. \"status\" does not want to be translated so it shouldn't be in\n_() - it can just go in the format string as can the indentation and the\n\"\\n\" (or we could use color_fprintf_ln() to automatically add a newline\nat the end. N_() is used to mark static strings for translation so the\ngettext utilities pick up the text to be translated but (because\ninitializes for static variables must be compile-time constants) does\nnot do anything when the program runs - if you have 'const char *s =\nN_(hello);' you have to do '_(s)' to get the translated version. Here we\ncan just pass the untranslated string directly to gettext so it should\nbe _(\"show paths with changes\"). Putting all that together we get\n\n\tcolor_fprintf(stdout, help_color, \"status        - %s\\n\",\n\t\t\t_(\"show paths with changes\");\n\n\nBest Wishes\n\nPhillip\n\n> +\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"update\"), \n> +\t\tN_(\"        - add working tree state to the staged set of changes\"));\n> +\tprintf(\"\\n\");\t\n> +\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"revert\"),\n> +\t\tN_(\"        - revert staged set of changes back to the HEAD version\"));\n> +\tprintf(\"\\n\");\n> +\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"patch\"),\n> +\t\tN_(\"         - pick hunks and update selectively\"));\n> +\tprintf(\"\\n\");\n> +\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"diff\"),\n> +\t\tN_(\"          - view diff between HEAD and index\"));\n> +\tprintf(\"\\n\");\n> +\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"add untracked\"),\n> +\t\tN_(\" - add contents of untracked files to the staged set of changes\"));\n> +\tprintf(\"\\n\");\n> +}\n> diff --git a/add-interactive.h b/add-interactive.h\n> index 1f4747553c..46e17c5c71 100644\n> --- a/add-interactive.h\n> +++ b/add-interactive.h\n> @@ -5,4 +5,6 @@ int add_i_config(const char *var, const char *value, void *cbdata);\n>  \n>  void add_i_print_modified(void);\n>  \n> -#endif\n> \\ No newline at end of file\n> +void add_i_show_help(void);\n> +\n> +#endif\n> diff --git a/builtin/add--helper.c b/builtin/add--helper.c\n> index 43545d9af5..a3b3a68b68 100644\n> --- a/builtin/add--helper.c\n> +++ b/builtin/add--helper.c\n> @@ -10,7 +10,8 @@ static const char * const builtin_add_helper_usage[] = {\n>  \n>  enum cmd_mode {\n>  \tDEFAULT = 0,\n> -\tSTATUS\n> +\tSTATUS,\n> +\tHELP\n>  };\n>  \n>  int cmd_add__helper(int argc, const char **argv, const char *prefix)\n> @@ -20,6 +21,8 @@ int cmd_add__helper(int argc, const char **argv, const char *prefix)\n>  \tstruct option options[] = {\n>  \t\tOPT_CMDMODE(0, \"status\", &mode,\n>  \t\t\t N_(\"print status information with diffstat\"), STATUS),\n> +\t\tOPT_CMDMODE(0, \"show-help\", &mode,\n> +\t\t\t N_(\"show help\"), HELP),\n>  \t\tOPT_END()\n>  \t};\n>  \n> @@ -30,6 +33,8 @@ int cmd_add__helper(int argc, const char **argv, const char *prefix)\n>  \n>  \tif (mode == STATUS)\n>  \t\tadd_i_print_modified();\n> +\telse if (mode == HELP)\n> +\t\tadd_i_show_help();\n>  \telse\n>  \t\tusage_with_options(builtin_add_helper_usage,\n>  \t\t\t\t   options);\n> \n\n"},{"id":"367083","messageId":"78b5a684-0963-0ca0-78fa-87bc4b18b3a6@talktalk.net","threadId":"50075","inReplyTo":"2b4714b8d04f98a6d35f3f4f15881406fa87c320.1547797620.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 6/7] t3701-add-interactive: test add_i_show_help()","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2019-01-18T11:23:18Z","receivedAt":"2019-01-18T11:23:15Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Slavica\n\nThanks for moving this up, it's really good to add the test before the\nconversion to C.\n\nBest Wishes\n\nPhillip\n\nOn 18/01/2019 07:47, Slavica Djukic via GitGitGadget wrote:\n> From: Slavica Djukic <slawica92@hotmail.com>\n> \n> Add test to t3701-add-interactive to verify that add_i_show_help()\n> outputs expected content.\n> Also, add it before changing git-add--interactive.perl's help_cmd\n> to demonstrate that there are no changes introduced by the\n> conversion to C.\n> Prefix git add -i call with GIT_PAGER_IN_USE=true TERM=vt100\n> to force colored output on Windows.\n> \n> Signed-off-by: Slavica Djukic <slawica92@hotmail.com>\n> ---\n>  t/t3701-add-interactive.sh | 24 ++++++++++++++++++++++++\n>  1 file changed, 24 insertions(+)\n> \n> diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh\n> index 65dfbc033a..14e3286995 100755\n> --- a/t/t3701-add-interactive.sh\n> +++ b/t/t3701-add-interactive.sh\n> @@ -639,4 +639,28 @@ test_expect_success 'add -p patch editing works with pathological context lines'\n>  \ttest_cmp expected-2 actual\n>  '\n>  \n> +test_expect_success 'show help from add--helper' '\n> +\tgit reset --hard &&\n> +\tcat >expect <<-\\EOF &&\n> +\n> +\t<BOLD>*** Commands ***<RESET>\n> +\t  1: <BOLD;BLUE>s<RESET>tatus\t  2: <BOLD;BLUE>u<RESET>pdate\t  3: <BOLD;BLUE>r<RESET>evert\t  4: <BOLD;BLUE>a<RESET>dd untracked\n> +\t  5: <BOLD;BLUE>p<RESET>atch\t  6: <BOLD;BLUE>d<RESET>iff\t  7: <BOLD;BLUE>q<RESET>uit\t  8: <BOLD;BLUE>h<RESET>elp\n> +\t<BOLD;BLUE>What now<RESET>> <BOLD;RED>status        - show paths with changes<RESET>\n> +\t<BOLD;RED>update        - add working tree state to the staged set of changes<RESET>\n> +\t<BOLD;RED>revert        - revert staged set of changes back to the HEAD version<RESET>\n> +\t<BOLD;RED>patch         - pick hunks and update selectively<RESET>\n> +\t<BOLD;RED>diff          - view diff between HEAD and index<RESET>\n> +\t<BOLD;RED>add untracked - add contents of untracked files to the staged set of changes<RESET>\n> +\t<BOLD>*** Commands ***<RESET>\n> +\t  1: <BOLD;BLUE>s<RESET>tatus\t  2: <BOLD;BLUE>u<RESET>pdate\t  3: <BOLD;BLUE>r<RESET>evert\t  4: <BOLD;BLUE>a<RESET>dd untracked\n> +\t  5: <BOLD;BLUE>p<RESET>atch\t  6: <BOLD;BLUE>d<RESET>iff\t  7: <BOLD;BLUE>q<RESET>uit\t  8: <BOLD;BLUE>h<RESET>elp\n> +\t<BOLD;BLUE>What now<RESET>> \n> +\tBye.\n> +\tEOF\n> +\ttest_write_lines h | GIT_PAGER_IN_USE=true TERM=vt100 git add -i >actual.colored &&\n> +\ttest_decode_color <actual.colored >actual &&\n> +\ttest_i18ncmp expect actual\n> +'\n> +\n>  test_done\n> \n\n"},{"id":"367086","messageId":"f44a034a-fa67-c345-e4eb-e3410c4543ba@gmail.com","threadId":"50075","inReplyTo":"01255e61-a3b1-e509-9f33-36ad07640b4f@talktalk.net","subject":"Re: [PATCH v2 5/7] add-interactive.c: implement show-help command","fromName":"Slavica Djukic","fromEmail":"slavicadj.ip2018@gmail.com","sentAt":"2019-01-18T12:19:37Z","receivedAt":"2019-01-18T12:19:47Z","isPatch":true,"sender":{"key":"slavicadj.ip2018@gmail.com","avatar":null},"body":"Hi Phillip,\n\nOn 18-Jan-19 12:20 PM, Phillip Wood wrote:\n> Hi Slavica\n>\n> I think this round is looking good I've got a couple of comments about\n> the translation of the help text but everything else looks fine to me\n> now. In future when you're posting a new version it's helpful CC the\n> people who commented on the previous version(s).\n\n\nThanks for taking your time to review patches again. I'm sorry for \nomitting you\n\nin CC, but I've sent re-roll through GitGitGadget, and I guess I thought \nit would pick it up.\n\nI'll see what happened and keep that in mind.\n\n\n>\n> On 18/01/2019 07:47, Slavica Djukic via GitGitGadget wrote:\n>> From: Slavica Djukic <slawica92@hotmail.com>\n>>\n>> Implement show-help command in add-interactive.c and use it in\n>> builtin add--helper.c.\n>>\n>> Use command name \"show-help\" instead of \"help\": add--helper is\n>> builtin, hence add--helper --help would be intercepted by\n>> handle_builtin and re-routed to the help command, without ever\n>> calling cmd_add__helper().\n>>\n>> Signed-off-by: Slavica Djukic <slawica92@hotmail.com>\n>> ---\n>>   add-interactive.c     | 23 +++++++++++++++++++++++\n>>   add-interactive.h     |  4 +++-\n>>   builtin/add--helper.c |  7 ++++++-\n>>   3 files changed, 32 insertions(+), 2 deletions(-)\n>>\n>> diff --git a/add-interactive.c b/add-interactive.c\n>> index c55d934186..76c3f4c3eb 100644\n>> --- a/add-interactive.c\n>> +++ b/add-interactive.c\n>> @@ -244,3 +244,26 @@ void add_i_print_modified(void)\n>>   \tfree(files);\n>>   \thashmap_free(&s.file_map, 1);\n>>   }\n>> +\n>> +void add_i_show_help(void)\n>> +{\n>> +\tconst char *help_color = get_color(COLOR_HELP);\n>> +\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"status\"),\n>> +\t\tN_(\"        - show paths with changes\"));\n>> +\tprintf(\"\\n\");\n> There seems to be a bit of confusion with the translation of these\n> messages. \"status\" does not want to be translated so it shouldn't be in\n> _() - it can just go in the format string as can the indentation and the\n> \"\\n\" (or we could use color_fprintf_ln() to automatically add a newline\n> at the end. N_() is used to mark static strings for translation so the\n> gettext utilities pick up the text to be translated but (because\n> initializes for static variables must be compile-time constants) does\n> not do anything when the program runs - if you have 'const char *s =\n> N_(hello);' you have to do '_(s)' to get the translated version. Here we\n> can just pass the untranslated string directly to gettext so it should\n> be _(\"show paths with changes\"). Putting all that together we get\n>\n> \tcolor_fprintf(stdout, help_color, \"status        - %s\\n\",\n> \t\t\t_(\"show paths with changes\");\n\n\nI thought _() was for strings that were already translated,\n\nand N_() for strings that weren't. And I now see that I also tried to \ntranslate command\n\nnames as well, just the opposite of what you suggested... Thanks for \nclarifying this.\n\n\n>\n>\n> Best Wishes\n>\n> Phillip\n>\n>> +\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"update\"),\n>> +\t\tN_(\"        - add working tree state to the staged set of changes\"));\n>> +\tprintf(\"\\n\");\t\n>> +\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"revert\"),\n>> +\t\tN_(\"        - revert staged set of changes back to the HEAD version\"));\n>> +\tprintf(\"\\n\");\n>> +\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"patch\"),\n>> +\t\tN_(\"         - pick hunks and update selectively\"));\n>> +\tprintf(\"\\n\");\n>> +\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"diff\"),\n>> +\t\tN_(\"          - view diff between HEAD and index\"));\n>> +\tprintf(\"\\n\");\n>> +\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"add untracked\"),\n>> +\t\tN_(\" - add contents of untracked files to the staged set of changes\"));\n>> +\tprintf(\"\\n\");\n>> +}\n>> diff --git a/add-interactive.h b/add-interactive.h\n>> index 1f4747553c..46e17c5c71 100644\n>> --- a/add-interactive.h\n>> +++ b/add-interactive.h\n>> @@ -5,4 +5,6 @@ int add_i_config(const char *var, const char *value, void *cbdata);\n>>   \n>>   void add_i_print_modified(void);\n>>   \n>> -#endif\n>> \\ No newline at end of file\n>> +void add_i_show_help(void);\n>> +\n>> +#endif\n>> diff --git a/builtin/add--helper.c b/builtin/add--helper.c\n>> index 43545d9af5..a3b3a68b68 100644\n>> --- a/builtin/add--helper.c\n>> +++ b/builtin/add--helper.c\n>> @@ -10,7 +10,8 @@ static const char * const builtin_add_helper_usage[] = {\n>>   \n>>   enum cmd_mode {\n>>   \tDEFAULT = 0,\n>> -\tSTATUS\n>> +\tSTATUS,\n>> +\tHELP\n>>   };\n>>   \n>>   int cmd_add__helper(int argc, const char **argv, const char *prefix)\n>> @@ -20,6 +21,8 @@ int cmd_add__helper(int argc, const char **argv, const char *prefix)\n>>   \tstruct option options[] = {\n>>   \t\tOPT_CMDMODE(0, \"status\", &mode,\n>>   \t\t\t N_(\"print status information with diffstat\"), STATUS),\n>> +\t\tOPT_CMDMODE(0, \"show-help\", &mode,\n>> +\t\t\t N_(\"show help\"), HELP),\n>>   \t\tOPT_END()\n>>   \t};\n>>   \n>> @@ -30,6 +33,8 @@ int cmd_add__helper(int argc, const char **argv, const char *prefix)\n>>   \n>>   \tif (mode == STATUS)\n>>   \t\tadd_i_print_modified();\n>> +\telse if (mode == HELP)\n>> +\t\tadd_i_show_help();\n>>   \telse\n>>   \t\tusage_with_options(builtin_add_helper_usage,\n>>   \t\t\t\t   options);\n>>\n"},{"id":"367101","messageId":"f58a3664-db5e-b081-a1e6-8869ce3ed392@talktalk.net","threadId":"50075","inReplyTo":"VI1PR05MB577331CCE110D2EAE325927CA69C0@VI1PR05MB5773.eurprd05.prod.outlook.com","subject":"Re: [PATCH v2 5/7] add-interactive.c: implement show-help command","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2019-01-18T14:25:04Z","receivedAt":"2019-01-18T14:25:08Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Slavica\n\nOn 18/01/2019 12:19, Slavica Đukić wrote:\n> Hi Phillip,\n> \n> On 18-Jan-19 12:20 PM, Phillip Wood wrote:\n>> Hi Slavica\n>>\n>> I think this round is looking good I've got a couple of comments about\n>> the translation of the help text but everything else looks fine to me\n>> now. In future when you're posting a new version it's helpful CC the\n>> people who commented on the previous version(s).\n> \n> \n> Thanks for taking your time to review patches again. I'm sorry for\n> omitting you\n> \n> in CC, but I've sent re-roll through GitGitGadget, and I guess I thought\n> it would pick it up.\n> \n> I'll see what happened and keep that in mind.\n\nI'm not sure what GitGitGadget does about CC'ing people but Johannes \nwill know\n\n>> On 18/01/2019 07:47, Slavica Djukic via GitGitGadget wrote:\n>>> From: Slavica Djukic <slawica92@hotmail.com>\n>>>\n>>> Implement show-help command in add-interactive.c and use it in\n>>> builtin add--helper.c.\n>>>\n>>> Use command name \"show-help\" instead of \"help\": add--helper is\n>>> builtin, hence add--helper --help would be intercepted by\n>>> handle_builtin and re-routed to the help command, without ever\n>>> calling cmd_add__helper().\n>>>\n>>> Signed-off-by: Slavica Djukic <slawica92@hotmail.com>\n>>> ---\n>>>    add-interactive.c     | 23 +++++++++++++++++++++++\n>>>    add-interactive.h     |  4 +++-\n>>>    builtin/add--helper.c |  7 ++++++-\n>>>    3 files changed, 32 insertions(+), 2 deletions(-)\n>>>\n>>> diff --git a/add-interactive.c b/add-interactive.c\n>>> index c55d934186..76c3f4c3eb 100644\n>>> --- a/add-interactive.c\n>>> +++ b/add-interactive.c\n>>> @@ -244,3 +244,26 @@ void add_i_print_modified(void)\n>>>    \tfree(files);\n>>>    \thashmap_free(&s.file_map, 1);\n>>>    }\n>>> +\n>>> +void add_i_show_help(void)\n>>> +{\n>>> +\tconst char *help_color = get_color(COLOR_HELP);\n>>> +\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"status\"),\n>>> +\t\tN_(\"        - show paths with changes\"));\n>>> +\tprintf(\"\\n\");\n>> There seems to be a bit of confusion with the translation of these\n>> messages. \"status\" does not want to be translated so it shouldn't be in\n>> _() - it can just go in the format string as can the indentation and the\n>> \"\\n\" (or we could use color_fprintf_ln() to automatically add a newline\n>> at the end. N_() is used to mark static strings for translation so the\n>> gettext utilities pick up the text to be translated but (because\n>> initializes for static variables must be compile-time constants) does\n>> not do anything when the program runs - if you have 'const char *s =\n>> N_(hello);' you have to do '_(s)' to get the translated version. Here we\n>> can just pass the untranslated string directly to gettext so it should\n>> be _(\"show paths with changes\"). Putting all that together we get\n>>\n>> \tcolor_fprintf(stdout, help_color, \"status        - %s\\n\",\n>> \t\t\t_(\"show paths with changes\");\n> \n> \n> I thought _() was for strings that were already translated,\n> and N_() for strings that weren't. And I now see that I also tried to\n> translate command names as well, just the opposite of what you suggested...\n > Thanks for clarifying this.\n\nI hope my explanation made sense, feel free to email if you want to \ncheck anything.\n\nHaving thought about it, I don't think we should add \"\\n\" to the format \nstring as it means the color will be reset after the new line, it should \nuse color_fprintf_ln() instead which adds a new line after it has reset \nthe color.\n\nBest Wishes\n\nPhillip\n\n>> Best Wishes\n>>\n>> Phillip\n>>\n>>> +\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"update\"),\n>>> +\t\tN_(\"        - add working tree state to the staged set of changes\"));\n>>> +\tprintf(\"\\n\");\t\n>>> +\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"revert\"),\n>>> +\t\tN_(\"        - revert staged set of changes back to the HEAD version\"));\n>>> +\tprintf(\"\\n\");\n>>> +\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"patch\"),\n>>> +\t\tN_(\"         - pick hunks and update selectively\"));\n>>> +\tprintf(\"\\n\");\n>>> +\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"diff\"),\n>>> +\t\tN_(\"          - view diff between HEAD and index\"));\n>>> +\tprintf(\"\\n\");\n>>> +\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"add untracked\"),\n>>> +\t\tN_(\" - add contents of untracked files to the staged set of changes\"));\n>>> +\tprintf(\"\\n\");\n>>> +}\n>>> diff --git a/add-interactive.h b/add-interactive.h\n>>> index 1f4747553c..46e17c5c71 100644\n>>> --- a/add-interactive.h\n>>> +++ b/add-interactive.h\n>>> @@ -5,4 +5,6 @@ int add_i_config(const char *var, const char *value, void *cbdata);\n>>>    \n>>>    void add_i_print_modified(void);\n>>>    \n>>> -#endif\n>>> \\ No newline at end of file\n>>> +void add_i_show_help(void);\n>>> +\n>>> +#endif\n>>> diff --git a/builtin/add--helper.c b/builtin/add--helper.c\n>>> index 43545d9af5..a3b3a68b68 100644\n>>> --- a/builtin/add--helper.c\n>>> +++ b/builtin/add--helper.c\n>>> @@ -10,7 +10,8 @@ static const char * const builtin_add_helper_usage[] = {\n>>>    \n>>>    enum cmd_mode {\n>>>    \tDEFAULT = 0,\n>>> -\tSTATUS\n>>> +\tSTATUS,\n>>> +\tHELP\n>>>    };\n>>>    \n>>>    int cmd_add__helper(int argc, const char **argv, const char *prefix)\n>>> @@ -20,6 +21,8 @@ int cmd_add__helper(int argc, const char **argv, const char *prefix)\n>>>    \tstruct option options[] = {\n>>>    \t\tOPT_CMDMODE(0, \"status\", &mode,\n>>>    \t\t\t N_(\"print status information with diffstat\"), STATUS),\n>>> +\t\tOPT_CMDMODE(0, \"show-help\", &mode,\n>>> +\t\t\t N_(\"show help\"), HELP),\n>>>    \t\tOPT_END()\n>>>    \t};\n>>>    \n>>> @@ -30,6 +33,8 @@ int cmd_add__helper(int argc, const char **argv, const char *prefix)\n>>>    \n>>>    \tif (mode == STATUS)\n>>>    \t\tadd_i_print_modified();\n>>> +\telse if (mode == HELP)\n>>> +\t\tadd_i_show_help();\n>>>    \telse\n>>>    \t\tusage_with_options(builtin_add_helper_usage,\n>>>    \t\t\t\t   options);\n>>>\n\n"},{"id":"367139","messageId":"nycvar.QRO.7.76.6.1901182138570.41@tvgsbejvaqbjf.bet","threadId":"50075","inReplyTo":"f58a3664-db5e-b081-a1e6-8869ce3ed392@talktalk.net","subject":"Re: [PATCH v2 5/7] add-interactive.c: implement show-help command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-01-18T20:40:45Z","receivedAt":"2019-01-18T20:41:15Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Phillip,\n\nOn Fri, 18 Jan 2019, Phillip Wood wrote:\n\n> On 18/01/2019 12:19, Slavica Đukić wrote:\n> > \n> > On 18-Jan-19 12:20 PM, Phillip Wood wrote:\n> > >\n> > > I think this round is looking good I've got a couple of comments\n> > > about the translation of the help text but everything else looks\n> > > fine to me now. In future when you're posting a new version it's\n> > > helpful CC the people who commented on the previous version(s).\n> > \n> > \n> > Thanks for taking your time to review patches again. I'm sorry for\n> > omitting you\n> > \n> > in CC, but I've sent re-roll through GitGitGadget, and I guess I\n> > thought it would pick it up.\n> > \n> > I'll see what happened and keep that in mind.\n> \n> I'm not sure what GitGitGadget does about CC'ing people but Johannes\n> will know\n\nThe idea is to have a footer (read: last line) of the PR description of\nthe form:\n\n\tCc: Name <email@example.org>, Other <other@example.org>\n\ni.e. a comma-separated list of recipients to Cc.\n\nYes, it is under-documented, but I still need to implement more features\nbefore I can start to polish documentation.\n\nCiao,\nDscho\n\n> \n> > > On 18/01/2019 07:47, Slavica Djukic via GitGitGadget wrote:\n> > > > From: Slavica Djukic <slawica92@hotmail.com>\n> > > >\n> > > > Implement show-help command in add-interactive.c and use it in\n> > > > builtin add--helper.c.\n> > > >\n> > > > Use command name \"show-help\" instead of \"help\": add--helper is\n> > > > builtin, hence add--helper --help would be intercepted by\n> > > > handle_builtin and re-routed to the help command, without ever\n> > > > calling cmd_add__helper().\n> > > >\n> > > > Signed-off-by: Slavica Djukic <slawica92@hotmail.com>\n> > > > ---\n> > > >    add-interactive.c     | 23 +++++++++++++++++++++++\n> > > >    add-interactive.h     |  4 +++-\n> > > >    builtin/add--helper.c |  7 ++++++-\n> > > >    3 files changed, 32 insertions(+), 2 deletions(-)\n> > > >\n> > > > diff --git a/add-interactive.c b/add-interactive.c\n> > > > index c55d934186..76c3f4c3eb 100644\n> > > > --- a/add-interactive.c\n> > > > +++ b/add-interactive.c\n> > > > @@ -244,3 +244,26 @@ void add_i_print_modified(void)\n> > > >     free(files);\n> > > >     hashmap_free(&s.file_map, 1);\n> > > >    }\n> > > > +\n> > > > +void add_i_show_help(void)\n> > > > +{\n> > > > +\tconst char *help_color = get_color(COLOR_HELP);\n> > > > +\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"status\"),\n> > > > +\t\tN_(\"        - show paths with changes\"));\n> > > > +\tprintf(\"\\n\");\n> > > There seems to be a bit of confusion with the translation of these\n> > > messages. \"status\" does not want to be translated so it shouldn't be in\n> > > _() - it can just go in the format string as can the indentation and the\n> > > \"\\n\" (or we could use color_fprintf_ln() to automatically add a newline\n> > > at the end. N_() is used to mark static strings for translation so the\n> > > gettext utilities pick up the text to be translated but (because\n> > > initializes for static variables must be compile-time constants) does\n> > > not do anything when the program runs - if you have 'const char *s =\n> > > N_(hello);' you have to do '_(s)' to get the translated version. Here we\n> > > can just pass the untranslated string directly to gettext so it should\n> > > be _(\"show paths with changes\"). Putting all that together we get\n> > >\n> > >  color_fprintf(stdout, help_color, \"status        - %s\\n\",\n> > >    _(\"show paths with changes\");\n> > \n> > \n> > I thought _() was for strings that were already translated,\n> > and N_() for strings that weren't. And I now see that I also tried to\n> > translate command names as well, just the opposite of what you suggested...\n> > Thanks for clarifying this.\n> \n> I hope my explanation made sense, feel free to email if you want to check\n> anything.\n> \n> Having thought about it, I don't think we should add \"\\n\" to the format string\n> as it means the color will be reset after the new line, it should use\n> color_fprintf_ln() instead which adds a new line after it has reset the color.\n> \n> Best Wishes\n> \n> Phillip\n> \n> > > Best Wishes\n> > >\n> > > Phillip\n> > >\n> > > > +\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"update\"),\n> > > > +\t\tN_(\"        - add working tree state to the staged set of\n> > > > changes\"));\n> > > > +\tprintf(\"\\n\");\t\n> > > > +\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"revert\"),\n> > > > +\t\tN_(\"        - revert staged set of changes back to the HEAD\n> > > > version\"));\n> > > > +\tprintf(\"\\n\");\n> > > > +\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"patch\"),\n> > > > +\t\tN_(\"         - pick hunks and update selectively\"));\n> > > > +\tprintf(\"\\n\");\n> > > > +\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"diff\"),\n> > > > +\t\tN_(\"          - view diff between HEAD and index\"));\n> > > > +\tprintf(\"\\n\");\n> > > > +\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"add untracked\"),\n> > > > +\t\tN_(\" - add contents of untracked files to the staged set of\n> > > > changes\"));\n> > > > +\tprintf(\"\\n\");\n> > > > +}\n> > > > diff --git a/add-interactive.h b/add-interactive.h\n> > > > index 1f4747553c..46e17c5c71 100644\n> > > > --- a/add-interactive.h\n> > > > +++ b/add-interactive.h\n> > > > @@ -5,4 +5,6 @@ int add_i_config(const char *var, const char *value,\n> > > > void *cbdata);\n> > > >    \n> > > >    void add_i_print_modified(void);\n> > > >    \n> > > > -#endif\n> > > > \\ No newline at end of file\n> > > > +void add_i_show_help(void);\n> > > > +\n> > > > +#endif\n> > > > diff --git a/builtin/add--helper.c b/builtin/add--helper.c\n> > > > index 43545d9af5..a3b3a68b68 100644\n> > > > --- a/builtin/add--helper.c\n> > > > +++ b/builtin/add--helper.c\n> > > > @@ -10,7 +10,8 @@ static const char * const builtin_add_helper_usage[] =\n> > > > {\n> > > >    \n> > > >    enum cmd_mode {\n> > > >    \tDEFAULT = 0,\n> > > > -\tSTATUS\n> > > > +\tSTATUS,\n> > > > +\tHELP\n> > > >    };\n> > > >    \n> > > >    int cmd_add__helper(int argc, const char **argv, const char *prefix)\n> > > > @@ -20,6 +21,8 @@ int cmd_add__helper(int argc, const char **argv, const\n> > > > char *prefix)\n> > > >     struct option options[] = {\n> > > >      OPT_CMDMODE(0, \"status\", &mode,\n> > > >    \t\t\t N_(\"print status information with diffstat\"),\n> > > > STATUS),\n> > > > +\t\tOPT_CMDMODE(0, \"show-help\", &mode,\n> > > > +\t\t\t N_(\"show help\"), HELP),\n> > > >     \tOPT_END()\n> > > >     };\n> > > >    @@ -30,6 +33,8 @@ int cmd_add__helper(int argc, const char **argv,\n> > > > const char *prefix)\n> > > >    \n> > > >     if (mode == STATUS)\n> > > >    \t\tadd_i_print_modified();\n> > > > +\telse if (mode == HELP)\n> > > > +\t\tadd_i_show_help();\n> > > >     else\n> > > >      usage_with_options(builtin_add_helper_usage,\n> > > >           options);\n> > > >\n> \n> \n> "},{"id":"367219","messageId":"pull.103.v3.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.v2.git.gitgitgadget@gmail.com","subject":"[PATCH v3 0/7] Turn git add-i into built-in","fromName":"Slavica Đukić via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-01-21T09:13:39Z","receivedAt":"2019-01-21T09:13:44Z","isPatch":true,"sender":{"key":"name:Slavica Đukić","avatar":null},"body":"This is the first version of a patch series to start porting\ngit-add--interactive from Perl to C. Daniel Ferreira's patch series used as\na head start:\nhttps://public-inbox.org/git/1494907234-28903-1-git-send-email-bnmvco@gmail.com/t/#u\n\nChanges since v2:\n\n * fixed translation misuse and avoided command names translation in \n   add_i_show_help\n\nDaniel Ferreira (4):\n  diff: export diffstat interface\n  add--helper: create builtin helper for interactive add\n  add-interactive.c: implement status command\n  add--interactive.perl: use add--helper --status for status_cmd\n\nSlavica Djukic (3):\n  add-interactive.c: implement show-help command\n  t3701-add-interactive: test add_i_show_help()\n  add--interactive.perl: use add--helper --show-help for help_cmd\n\n .gitignore                 |   1 +\n Makefile                   |   2 +\n add-interactive.c          | 263 +++++++++++++++++++++++++++++++++++++\n add-interactive.h          |  10 ++\n builtin.h                  |   1 +\n builtin/add--helper.c      |  43 ++++++\n diff.c                     |  36 ++---\n diff.h                     |  18 +++\n git-add--interactive.perl  |  15 +--\n git.c                      |   1 +\n t/t3701-add-interactive.sh |  24 ++++\n 11 files changed, 379 insertions(+), 35 deletions(-)\n create mode 100644 add-interactive.c\n create mode 100644 add-interactive.h\n create mode 100644 builtin/add--helper.c\n\n\nbase-commit: b21ebb671bb7dea8d342225f0d66c41f4e54d5ca\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-103%2FslavicaDj%2Fadd-i-v3\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-103/slavicaDj/add-i-v3\nPull-Request: https://github.com/gitgitgadget/git/pull/103\n\nRange-diff vs v2:\n\n 1:  737767b6f4 = 1:  737767b6f4 diff: export diffstat interface\n 2:  91b1963125 = 2:  91b1963125 add--helper: create builtin helper for interactive add\n 3:  d247ef69fe = 3:  d247ef69fe add-interactive.c: implement status command\n 4:  4950c889aa = 4:  4950c889aa add--interactive.perl: use add--helper --status for status_cmd\n 5:  cf4e913a5a ! 5:  581b108c9c add-interactive.c: implement show-help command\n     @@ -23,24 +23,18 @@\n      +void add_i_show_help(void)\n      +{\n      +\tconst char *help_color = get_color(COLOR_HELP);\n     -+\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"status\"), \n     -+\t\tN_(\"        - show paths with changes\"));\n     -+\tprintf(\"\\n\");\n     -+\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"update\"), \n     -+\t\tN_(\"        - add working tree state to the staged set of changes\"));\n     -+\tprintf(\"\\n\");\t\n     -+\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"revert\"),\n     -+\t\tN_(\"        - revert staged set of changes back to the HEAD version\"));\n     -+\tprintf(\"\\n\");\n     -+\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"patch\"),\n     -+\t\tN_(\"         - pick hunks and update selectively\"));\n     -+\tprintf(\"\\n\");\n     -+\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"diff\"),\n     -+\t\tN_(\"          - view diff between HEAD and index\"));\n     -+\tprintf(\"\\n\");\n     -+\tcolor_fprintf(stdout, help_color, \"%s%s\", _(\"add untracked\"),\n     -+\t\tN_(\" - add contents of untracked files to the staged set of changes\"));\n     -+\tprintf(\"\\n\");\n     ++\tcolor_fprintf_ln(stdout, help_color, \"status        - %s\",\n     ++\t\t\t_(\"show paths with changes\"));\n     ++\tcolor_fprintf_ln(stdout, help_color, \"update        - %s\",\n     ++\t\t\t_(\"add working tree state to the staged set of changes\"));\n     ++\tcolor_fprintf_ln(stdout, help_color, \"revert        - %s\",\n     ++\t\t\t_(\"revert staged set of changes back to the HEAD version\"));\n     ++\tcolor_fprintf_ln(stdout, help_color, \"patch         - %s\",\n     ++\t\t\t_(\"pick hunks and update selectively\"));\n     ++\tcolor_fprintf_ln(stdout, help_color, \"diff          - %s\",\n     ++\t\t\t_(\"view diff between HEAD and index\"));\n     ++\tcolor_fprintf_ln(stdout, help_color, \"add untracked - %s\",\n     ++\t\t\t_(\"add contents of untracked files to the staged set of changes\"));\n      +}\n      \n       diff --git a/add-interactive.h b/add-interactive.h\n 6:  2b4714b8d0 = 6:  aede733318 t3701-add-interactive: test add_i_show_help()\n 7:  6ede6d9251 = 7:  b9a1a7e37a add--interactive.perl: use add--helper --show-help for help_cmd\n\n-- \ngitgitgadget\n"},{"id":"367220","messageId":"737767b6f4e0367a7797020bd07a6b4357a47413.1548062019.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.v3.git.gitgitgadget@gmail.com","subject":"[PATCH v3 1/7] diff: export diffstat interface","fromName":"Daniel Ferreira via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-01-21T09:13:40Z","receivedAt":"2019-01-21T09:13:45Z","isPatch":true,"sender":{"key":"bnmvco@gmail.com","avatar":"https://avatars.githubusercontent.com/u/735397?v=4"},"body":"From: Daniel Ferreira <bnmvco@gmail.com>\n\nMake the diffstat interface (namely, the diffstat_t struct and\ncompute_diffstat) no longer be internal to diff.c and allow it to be used\nby other parts of git.\n\nThis is helpful for code that may want to easily extract information\nfrom files using the diff machinery, while flushing it differently from\nhow the show_* functions used by diff_flush() do it. One example is the\nbuiltin implementation of git-add--interactive's status.\n\nSigned-off-by: Daniel Ferreira <bnmvco@gmail.com>\nSigned-off-by: Slavica Djukic <slawica92@hotmail.com>\n---\n diff.c | 36 ++++++++++++++----------------------\n diff.h | 18 ++++++++++++++++++\n 2 files changed, 32 insertions(+), 22 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex dc9965e836..46a7d8cf29 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -2421,22 +2421,6 @@ static void pprint_rename(struct strbuf *name, const char *a, const char *b)\n \t}\n }\n \n-struct diffstat_t {\n-\tint nr;\n-\tint alloc;\n-\tstruct diffstat_file {\n-\t\tchar *from_name;\n-\t\tchar *name;\n-\t\tchar *print_name;\n-\t\tconst char *comments;\n-\t\tunsigned is_unmerged:1;\n-\t\tunsigned is_binary:1;\n-\t\tunsigned is_renamed:1;\n-\t\tunsigned is_interesting:1;\n-\t\tuintmax_t added, deleted;\n-\t} **files;\n-};\n-\n static struct diffstat_file *diffstat_add(struct diffstat_t *diffstat,\n \t\t\t\t\t  const char *name_a,\n \t\t\t\t\t  const char *name_b)\n@@ -5922,12 +5906,7 @@ void diff_flush(struct diff_options *options)\n \t    dirstat_by_line) {\n \t\tstruct diffstat_t diffstat;\n \n-\t\tmemset(&diffstat, 0, sizeof(struct diffstat_t));\n-\t\tfor (i = 0; i < q->nr; i++) {\n-\t\t\tstruct diff_filepair *p = q->queue[i];\n-\t\t\tif (check_pair_status(p))\n-\t\t\t\tdiff_flush_stat(p, options, &diffstat);\n-\t\t}\n+\t\tcompute_diffstat(options, &diffstat);\n \t\tif (output_format & DIFF_FORMAT_NUMSTAT)\n \t\t\tshow_numstat(&diffstat, options);\n \t\tif (output_format & DIFF_FORMAT_DIFFSTAT)\n@@ -6227,6 +6206,19 @@ static int is_submodule_ignored(const char *path, struct diff_options *options)\n \treturn ignored;\n }\n \n+void compute_diffstat(struct diff_options *options, struct diffstat_t *diffstat)\n+{\n+\tint i;\n+\tstruct diff_queue_struct *q = &diff_queued_diff;\n+\n+\tmemset(diffstat, 0, sizeof(struct diffstat_t));\n+\tfor (i = 0; i < q->nr; i++) {\n+\t\tstruct diff_filepair *p = q->queue[i];\n+\t\tif (check_pair_status(p))\n+\t\t\tdiff_flush_stat(p, options, diffstat);\n+\t}\n+}\n+\n void diff_addremove(struct diff_options *options,\n \t\t    int addremove, unsigned mode,\n \t\t    const struct object_id *oid,\ndiff --git a/diff.h b/diff.h\nindex ce5e8a8183..7809db3039 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -239,6 +239,22 @@ void diff_emit_submodule_error(struct diff_options *o, const char *err);\n void diff_emit_submodule_pipethrough(struct diff_options *o,\n \t\t\t\t     const char *line, int len);\n \n+struct diffstat_t {\n+\tint nr;\n+\tint alloc;\n+\tstruct diffstat_file {\n+\t\tchar *from_name;\n+\t\tchar *name;\n+\t\tchar *print_name;\n+\t\tconst char *comments;\n+\t\tunsigned is_unmerged:1;\n+\t\tunsigned is_binary:1;\n+\t\tunsigned is_renamed:1;\n+\t\tunsigned is_interesting:1;\n+\t\tuintmax_t added, deleted;\n+\t} **files;\n+};\n+\n enum color_diff {\n \tDIFF_RESET = 0,\n \tDIFF_CONTEXT = 1,\n@@ -327,6 +343,8 @@ void diff_change(struct diff_options *,\n \n struct diff_filepair *diff_unmerge(struct diff_options *, const char *path);\n \n+void compute_diffstat(struct diff_options *options, struct diffstat_t *diffstat);\n+\n #define DIFF_SETUP_REVERSE      \t1\n #define DIFF_SETUP_USE_SIZE_CACHE\t4\n \n-- \ngitgitgadget\n\n"},{"id":"367221","messageId":"91b1963125f7dfdd46fe5166125ee59ef2557b13.1548062019.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.v3.git.gitgitgadget@gmail.com","subject":"[PATCH v3 2/7] add--helper: create builtin helper for interactive add","fromName":"Daniel Ferreira via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-01-21T09:13:42Z","receivedAt":"2019-01-21T09:13:47Z","isPatch":true,"sender":{"key":"bnmvco@gmail.com","avatar":"https://avatars.githubusercontent.com/u/735397?v=4"},"body":"From: Daniel Ferreira <bnmvco@gmail.com>\n\nCreate a builtin helper for git-add--interactive, which right now is not\nable to do anything.\n\nThis is the first step in an effort to convert git-add--interactive.perl\nto a C builtin, in search for better portability, expressibility and\nperformance (specially on non-POSIX systems like Windows).\n\nAdditionally, an eventual complete port of git-add--interactive would\nremove the last \"big\" Git script to have Perl as a dependency, allowing\nmost Git users to have a NOPERL build running without big losses.\n\nSigned-off-by: Daniel Ferreira <bnmvco@gmail.com>\nSigned-off-by: Slavica Djukic <slawica92@hotmail.com>\n---\n .gitignore            | 1 +\n Makefile              | 1 +\n builtin.h             | 1 +\n builtin/add--helper.c | 6 ++++++\n git.c                 | 1 +\n 5 files changed, 10 insertions(+)\n create mode 100644 builtin/add--helper.c\n\ndiff --git a/.gitignore b/.gitignore\nindex 0d77ea5894..2ee71ed217 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -15,6 +15,7 @@\n /git\n /git-add\n /git-add--interactive\n+/git-add--helper\n /git-am\n /git-annotate\n /git-apply\ndiff --git a/Makefile b/Makefile\nindex 1a44c811aa..9c84b80739 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1023,6 +1023,7 @@ LIB_OBJS += xdiff-interface.o\n LIB_OBJS += zlib.o\n \n BUILTIN_OBJS += builtin/add.o\n+BUILTIN_OBJS += builtin/add--helper.o\n BUILTIN_OBJS += builtin/am.o\n BUILTIN_OBJS += builtin/annotate.o\n BUILTIN_OBJS += builtin/apply.o\ndiff --git a/builtin.h b/builtin.h\nindex 6538932e99..dd811ef7d5 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -128,6 +128,7 @@ extern void setup_auto_pager(const char *cmd, int def);\n extern int is_builtin(const char *s);\n \n extern int cmd_add(int argc, const char **argv, const char *prefix);\n+extern int cmd_add__helper(int argc, const char **argv, const char *prefix);\n extern int cmd_am(int argc, const char **argv, const char *prefix);\n extern int cmd_annotate(int argc, const char **argv, const char *prefix);\n extern int cmd_apply(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/add--helper.c b/builtin/add--helper.c\nnew file mode 100644\nindex 0000000000..6a97f0e191\n--- /dev/null\n+++ b/builtin/add--helper.c\n@@ -0,0 +1,6 @@\n+#include \"builtin.h\"\n+\n+int cmd_add__helper(int argc, const char **argv, const char *prefix)\n+{\n+\treturn 0;\n+}\ndiff --git a/git.c b/git.c\nindex 2f604a41ea..5507591f2e 100644\n--- a/git.c\n+++ b/git.c\n@@ -443,6 +443,7 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)\n \n static struct cmd_struct commands[] = {\n \t{ \"add\", cmd_add, RUN_SETUP | NEED_WORK_TREE },\n+\t{ \"add--helper\", cmd_add__helper, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"am\", cmd_am, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"annotate\", cmd_annotate, RUN_SETUP | NO_PARSEOPT },\n \t{ \"apply\", cmd_apply, RUN_SETUP_GENTLY },\n-- \ngitgitgadget\n\n"},{"id":"367223","messageId":"4950c889aacd869a8f4fd30b8d5cde4b43957347.1548062019.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.v3.git.gitgitgadget@gmail.com","subject":"[PATCH v3 4/7] add--interactive.perl: use add--helper --status for status_cmd","fromName":"Daniel Ferreira via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-01-21T09:13:43Z","receivedAt":"2019-01-21T09:13:48Z","isPatch":true,"sender":{"key":"bnmvco@gmail.com","avatar":"https://avatars.githubusercontent.com/u/735397?v=4"},"body":"From: Daniel Ferreira <bnmvco@gmail.com>\n\nCall the newly introduced add--helper builtin on\nstatus_cmd() instead of relying on add--interactive's Perl\nfunctions to build print the numstat.\n\nSigned-off-by: Daniel Ferreira <bnmvco@gmail.com>\nSigned-off-by: Slavica Djukic <slawica92@hotmail.com>\n---\n git-add--interactive.perl | 4 +---\n 1 file changed, 1 insertion(+), 3 deletions(-)\n\ndiff --git a/git-add--interactive.perl b/git-add--interactive.perl\nindex 20eb81cc92..a6536f9cf3 100755\n--- a/git-add--interactive.perl\n+++ b/git-add--interactive.perl\n@@ -597,9 +597,7 @@ sub prompt_help_cmd {\n }\n \n sub status_cmd {\n-\tlist_and_choose({ LIST_ONLY => 1, HEADER => $status_head },\n-\t\t\tlist_modified());\n-\tprint \"\\n\";\n+\tsystem(qw(git add--helper --status));\n }\n \n sub say_n_paths {\n-- \ngitgitgadget\n\n"},{"id":"367222","messageId":"581b108c9c3a090660ee655263c4639d1bf409d6.1548062019.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.v3.git.gitgitgadget@gmail.com","subject":"[PATCH v3 5/7] add-interactive.c: implement show-help command","fromName":"Slavica Djukic via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-01-21T09:13:44Z","receivedAt":"2019-01-21T09:13:49Z","isPatch":true,"sender":{"key":"slavicadj.ip2018@gmail.com","avatar":null},"body":"From: Slavica Djukic <slawica92@hotmail.com>\n\nImplement show-help command in add-interactive.c and use it in\nbuiltin add--helper.c.\n\nUse command name \"show-help\" instead of \"help\": add--helper is\nbuiltin, hence add--helper --help would be intercepted by\nhandle_builtin and re-routed to the help command, without ever\ncalling cmd_add__helper().\n\nSigned-off-by: Slavica Djukic <slawica92@hotmail.com>\n---\n add-interactive.c     | 17 +++++++++++++++++\n add-interactive.h     |  4 +++-\n builtin/add--helper.c |  7 ++++++-\n 3 files changed, 26 insertions(+), 2 deletions(-)\n\ndiff --git a/add-interactive.c b/add-interactive.c\nindex c55d934186..d239037bc1 100644\n--- a/add-interactive.c\n+++ b/add-interactive.c\n@@ -244,3 +244,20 @@ void add_i_print_modified(void)\n \tfree(files);\n \thashmap_free(&s.file_map, 1);\n }\n+\n+void add_i_show_help(void)\n+{\n+\tconst char *help_color = get_color(COLOR_HELP);\n+\tcolor_fprintf_ln(stdout, help_color, \"status        - %s\",\n+\t\t\t_(\"show paths with changes\"));\n+\tcolor_fprintf_ln(stdout, help_color, \"update        - %s\",\n+\t\t\t_(\"add working tree state to the staged set of changes\"));\n+\tcolor_fprintf_ln(stdout, help_color, \"revert        - %s\",\n+\t\t\t_(\"revert staged set of changes back to the HEAD version\"));\n+\tcolor_fprintf_ln(stdout, help_color, \"patch         - %s\",\n+\t\t\t_(\"pick hunks and update selectively\"));\n+\tcolor_fprintf_ln(stdout, help_color, \"diff          - %s\",\n+\t\t\t_(\"view diff between HEAD and index\"));\n+\tcolor_fprintf_ln(stdout, help_color, \"add untracked - %s\",\n+\t\t\t_(\"add contents of untracked files to the staged set of changes\"));\n+}\ndiff --git a/add-interactive.h b/add-interactive.h\nindex 1f4747553c..46e17c5c71 100644\n--- a/add-interactive.h\n+++ b/add-interactive.h\n@@ -5,4 +5,6 @@ int add_i_config(const char *var, const char *value, void *cbdata);\n \n void add_i_print_modified(void);\n \n-#endif\n\\ No newline at end of file\n+void add_i_show_help(void);\n+\n+#endif\ndiff --git a/builtin/add--helper.c b/builtin/add--helper.c\nindex 43545d9af5..a3b3a68b68 100644\n--- a/builtin/add--helper.c\n+++ b/builtin/add--helper.c\n@@ -10,7 +10,8 @@ static const char * const builtin_add_helper_usage[] = {\n \n enum cmd_mode {\n \tDEFAULT = 0,\n-\tSTATUS\n+\tSTATUS,\n+\tHELP\n };\n \n int cmd_add__helper(int argc, const char **argv, const char *prefix)\n@@ -20,6 +21,8 @@ int cmd_add__helper(int argc, const char **argv, const char *prefix)\n \tstruct option options[] = {\n \t\tOPT_CMDMODE(0, \"status\", &mode,\n \t\t\t N_(\"print status information with diffstat\"), STATUS),\n+\t\tOPT_CMDMODE(0, \"show-help\", &mode,\n+\t\t\t N_(\"show help\"), HELP),\n \t\tOPT_END()\n \t};\n \n@@ -30,6 +33,8 @@ int cmd_add__helper(int argc, const char **argv, const char *prefix)\n \n \tif (mode == STATUS)\n \t\tadd_i_print_modified();\n+\telse if (mode == HELP)\n+\t\tadd_i_show_help();\n \telse\n \t\tusage_with_options(builtin_add_helper_usage,\n \t\t\t\t   options);\n-- \ngitgitgadget\n\n"},{"id":"367224","messageId":"aede733318bb6fccc1c3d16adf442135d3cb0692.1548062019.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.v3.git.gitgitgadget@gmail.com","subject":"[PATCH v3 6/7] t3701-add-interactive: test add_i_show_help()","fromName":"Slavica Djukic via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-01-21T09:13:45Z","receivedAt":"2019-01-21T09:13:50Z","isPatch":true,"sender":{"key":"slavicadj.ip2018@gmail.com","avatar":null},"body":"From: Slavica Djukic <slawica92@hotmail.com>\n\nAdd test to t3701-add-interactive to verify that add_i_show_help()\noutputs expected content.\nAlso, add it before changing git-add--interactive.perl's help_cmd\nto demonstrate that there are no changes introduced by the\nconversion to C.\nPrefix git add -i call with GIT_PAGER_IN_USE=true TERM=vt100\nto force colored output on Windows.\n\nSigned-off-by: Slavica Djukic <slawica92@hotmail.com>\n---\n t/t3701-add-interactive.sh | 24 ++++++++++++++++++++++++\n 1 file changed, 24 insertions(+)\n\ndiff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh\nindex 65dfbc033a..14e3286995 100755\n--- a/t/t3701-add-interactive.sh\n+++ b/t/t3701-add-interactive.sh\n@@ -639,4 +639,28 @@ test_expect_success 'add -p patch editing works with pathological context lines'\n \ttest_cmp expected-2 actual\n '\n \n+test_expect_success 'show help from add--helper' '\n+\tgit reset --hard &&\n+\tcat >expect <<-\\EOF &&\n+\n+\t<BOLD>*** Commands ***<RESET>\n+\t  1: <BOLD;BLUE>s<RESET>tatus\t  2: <BOLD;BLUE>u<RESET>pdate\t  3: <BOLD;BLUE>r<RESET>evert\t  4: <BOLD;BLUE>a<RESET>dd untracked\n+\t  5: <BOLD;BLUE>p<RESET>atch\t  6: <BOLD;BLUE>d<RESET>iff\t  7: <BOLD;BLUE>q<RESET>uit\t  8: <BOLD;BLUE>h<RESET>elp\n+\t<BOLD;BLUE>What now<RESET>> <BOLD;RED>status        - show paths with changes<RESET>\n+\t<BOLD;RED>update        - add working tree state to the staged set of changes<RESET>\n+\t<BOLD;RED>revert        - revert staged set of changes back to the HEAD version<RESET>\n+\t<BOLD;RED>patch         - pick hunks and update selectively<RESET>\n+\t<BOLD;RED>diff          - view diff between HEAD and index<RESET>\n+\t<BOLD;RED>add untracked - add contents of untracked files to the staged set of changes<RESET>\n+\t<BOLD>*** Commands ***<RESET>\n+\t  1: <BOLD;BLUE>s<RESET>tatus\t  2: <BOLD;BLUE>u<RESET>pdate\t  3: <BOLD;BLUE>r<RESET>evert\t  4: <BOLD;BLUE>a<RESET>dd untracked\n+\t  5: <BOLD;BLUE>p<RESET>atch\t  6: <BOLD;BLUE>d<RESET>iff\t  7: <BOLD;BLUE>q<RESET>uit\t  8: <BOLD;BLUE>h<RESET>elp\n+\t<BOLD;BLUE>What now<RESET>> \n+\tBye.\n+\tEOF\n+\ttest_write_lines h | GIT_PAGER_IN_USE=true TERM=vt100 git add -i >actual.colored &&\n+\ttest_decode_color <actual.colored >actual &&\n+\ttest_i18ncmp expect actual\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"367225","messageId":"b9a1a7e37a477e978f19cbcc9b41f80519de54da.1548062019.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.v3.git.gitgitgadget@gmail.com","subject":"[PATCH v3 7/7] add--interactive.perl: use add--helper --show-help for help_cmd","fromName":"Slavica Djukic via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-01-21T09:13:46Z","receivedAt":"2019-01-21T09:13:52Z","isPatch":true,"sender":{"key":"slavicadj.ip2018@gmail.com","avatar":null},"body":"From: Slavica Djukic <slawica92@hotmail.com>\n\nChange help_cmd sub in git-add--interactive.perl to use\nshow-help command from builtin add--helper.\n\nSigned-off-by: Slavica Djukic <slawica92@hotmail.com>\n---\n git-add--interactive.perl | 11 +----------\n 1 file changed, 1 insertion(+), 10 deletions(-)\n\ndiff --git a/git-add--interactive.perl b/git-add--interactive.perl\nindex a6536f9cf3..32ee729a58 100755\n--- a/git-add--interactive.perl\n+++ b/git-add--interactive.perl\n@@ -1717,16 +1717,7 @@ sub quit_cmd {\n }\n \n sub help_cmd {\n-# TRANSLATORS: please do not translate the command names\n-# 'status', 'update', 'revert', etc.\n-\tprint colored $help_color, __ <<'EOF' ;\n-status        - show paths with changes\n-update        - add working tree state to the staged set of changes\n-revert        - revert staged set of changes back to the HEAD version\n-patch         - pick hunks and update selectively\n-diff          - view diff between HEAD and index\n-add untracked - add contents of untracked files to the staged set of changes\n-EOF\n+\tsystem(qw(git add--helper --show-help));\n }\n \n sub process_args {\n-- \ngitgitgadget\n"},{"id":"367226","messageId":"d247ef69fe2c9d017b8d363de554a52269c0d281.1548062019.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.v3.git.gitgitgadget@gmail.com","subject":"[PATCH v3 3/7] add-interactive.c: implement status command","fromName":"Daniel Ferreira via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-01-21T09:13:42Z","receivedAt":"2019-01-21T09:13:54Z","isPatch":true,"sender":{"key":"bnmvco@gmail.com","avatar":"https://avatars.githubusercontent.com/u/735397?v=4"},"body":"From: Daniel Ferreira <bnmvco@gmail.com>\n\nAdd new files: add-interactive.c and add-interactive.h, which\nwill be used for implementing \"application logic\" of git add -i,\nwhereas add--helper.c will be used mostly for parsing the command line.\nWe're a bit lax with the command-line parsing, as the command is\nintended to be called only by one internal user: the add--interactive script.\n\nImplement add --interactive's status command in add-interactive.c and\nuse it in builtin add--helper.c.\n\nIt prints a numstat comparing changed files between a) the worktree and\nthe index; b) the index and the HEAD.\n\nTo do so, we use run_diff_index() and run_diff_files() to get changed\nfiles, use the diffstat API on them to get the numstat and use a\ncombination of a hashmap and qsort() to print the result in\nO(n) + O(n lg n) complexity.\n\nThis is the first interactive add command implemented in C of those\nanticipated by the previous commit, which introduced\nthe add--helper built-in.\n\nSigned-off-by: Daniel Ferreira <bnmvco@gmail.com>\nSigned-off-by: Slavica Djukic <slawica92@hotmail.com>\n---\n Makefile              |   1 +\n add-interactive.c     | 246 ++++++++++++++++++++++++++++++++++++++++++\n add-interactive.h     |   8 ++\n builtin/add--helper.c |  32 ++++++\n 4 files changed, 287 insertions(+)\n create mode 100644 add-interactive.c\n create mode 100644 add-interactive.h\n\ndiff --git a/Makefile b/Makefile\nindex 9c84b80739..2a4a5cc37b 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -827,6 +827,7 @@ LIB_H = $(shell $(FIND) . \\\n \t-name '*.h' -print)\n \n LIB_OBJS += abspath.o\n+LIB_OBJS += add-interactive.o\n LIB_OBJS += advice.o\n LIB_OBJS += alias.o\n LIB_OBJS += alloc.o\ndiff --git a/add-interactive.c b/add-interactive.c\nnew file mode 100644\nindex 0000000000..c55d934186\n--- /dev/null\n+++ b/add-interactive.c\n@@ -0,0 +1,246 @@\n+#include \"add-interactive.h\"\n+#include \"cache.h\"\n+#include \"commit.h\"\n+#include \"color.h\"\n+#include \"config.h\"\n+#include \"diffcore.h\"\n+#include \"revision.h\"\n+\n+#define HEADER_INDENT \"      \"\n+\n+enum collection_phase {\n+\tWORKTREE,\n+\tINDEX\n+};\n+\n+struct file_stat {\n+\tstruct hashmap_entry ent;\n+\tstruct {\n+\t\tuintmax_t added, deleted;\n+\t} index, worktree;\n+\tchar name[FLEX_ARRAY];\n+};\n+\n+struct collection_status {\n+\tenum collection_phase phase;\n+\n+\tconst char *reference;\n+\tstruct pathspec pathspec;\n+\n+\tstruct hashmap file_map;\n+};\n+\n+static int use_color = -1;\n+enum color_add_i {\n+\tCOLOR_PROMPT,\n+\tCOLOR_HEADER,\n+\tCOLOR_HELP,\n+\tCOLOR_ERROR\n+};\n+\n+static char colors[][COLOR_MAXLEN] = {\n+\tGIT_COLOR_BOLD_BLUE, /* Prompt */\n+\tGIT_COLOR_BOLD,      /* Header */\n+\tGIT_COLOR_BOLD_RED,  /* Help */\n+\tGIT_COLOR_BOLD_RED   /* Error */\n+};\n+\n+static const char *get_color(enum color_add_i ix)\n+{\n+\tif (want_color(use_color))\n+\t\treturn colors[ix];\n+\treturn \"\";\n+}\n+\n+static int parse_color_slot(const char *slot)\n+{\n+\tif (!strcasecmp(slot, \"prompt\"))\n+\t\treturn COLOR_PROMPT;\n+\tif (!strcasecmp(slot, \"header\"))\n+\t\treturn COLOR_HEADER;\n+\tif (!strcasecmp(slot, \"help\"))\n+\t\treturn COLOR_HELP;\n+\tif (!strcasecmp(slot, \"error\"))\n+\t\treturn COLOR_ERROR;\n+\n+\treturn -1;\n+}\n+\n+int add_i_config(const char *var,\n+\t\tconst char *value, void *cbdata)\n+{\n+\tconst char *name;\n+\n+\tif (!strcmp(var, \"color.interactive\")) {\n+\t\tuse_color = git_config_colorbool(var, value);\n+\t\treturn 0;\n+\t}\n+\n+\tif (skip_prefix(var, \"color.interactive.\", &name)) {\n+\t\tint slot = parse_color_slot(name);\n+\t\tif (slot < 0)\n+\t\t\treturn 0;\n+\t\tif (!value)\n+\t\t\treturn config_error_nonbool(var);\n+\t\treturn color_parse(value, colors[slot]);\n+\t}\n+\n+\treturn git_default_config(var, value, cbdata);\n+}\n+\n+static int hash_cmp(const void *unused_cmp_data, const void *entry,\n+\t\t\tconst void *entry_or_key, const void *keydata)\n+{\n+\tconst struct file_stat *e1 = entry, *e2 = entry_or_key;\n+\tconst char *name = keydata ? keydata : e2->name;\n+\n+\treturn strcmp(e1->name, name);\n+}\n+\n+static int alphabetical_cmp(const void *a, const void *b)\n+{\n+\tstruct file_stat *f1 = *((struct file_stat **)a);\n+\tstruct file_stat *f2 = *((struct file_stat **)b);\n+\n+\treturn strcmp(f1->name, f2->name);\n+}\n+\n+static void collect_changes_cb(struct diff_queue_struct *q,\n+\t\t\t\t\t struct diff_options *options,\n+\t\t\t\t\t void *data)\n+{\n+\tstruct collection_status *s = data;\n+\tstruct diffstat_t stat = { 0 };\n+\tint i;\n+\n+\tif (!q->nr)\n+\t\treturn;\n+\n+\tcompute_diffstat(options, &stat);\n+\n+\tfor (i = 0; i < stat.nr; i++) {\n+\t\tstruct file_stat *entry;\n+\t\tconst char *name = stat.files[i]->name;\n+\t\tunsigned int hash = strhash(name);\n+\n+\t\tentry = hashmap_get_from_hash(&s->file_map, hash, name);\n+\t\tif (!entry) {\n+\t\t\tFLEX_ALLOC_STR(entry, name, name);\n+\t\t\thashmap_entry_init(entry, hash);\n+\t\t\thashmap_add(&s->file_map, entry);\n+\t\t}\n+\n+\t\tif (s->phase == WORKTREE) {\n+\t\t\tentry->worktree.added = stat.files[i]->added;\n+\t\t\tentry->worktree.deleted = stat.files[i]->deleted;\n+\t\t} else if (s->phase == INDEX) {\n+\t\t\tentry->index.added = stat.files[i]->added;\n+\t\t\tentry->index.deleted = stat.files[i]->deleted;\n+\t\t}\n+\t}\n+}\n+\n+static void collect_changes_worktree(struct collection_status *s)\n+{\n+\tstruct rev_info rev;\n+\n+\ts->phase = WORKTREE;\n+\n+\tinit_revisions(&rev, NULL);\n+\tsetup_revisions(0, NULL, &rev, NULL);\n+\n+\trev.max_count = 0;\n+\n+\trev.diffopt.flags.ignore_dirty_submodules = 1;\n+\trev.diffopt.output_format = DIFF_FORMAT_CALLBACK;\n+\trev.diffopt.format_callback = collect_changes_cb;\n+\trev.diffopt.format_callback_data = s;\n+\n+\trun_diff_files(&rev, 0);\n+}\n+\n+static void collect_changes_index(struct collection_status *s)\n+{\n+\tstruct rev_info rev;\n+\tstruct setup_revision_opt opt = { 0 };\n+\n+\ts->phase = INDEX;\n+\n+\tinit_revisions(&rev, NULL);\n+\topt.def = s->reference;\n+\tsetup_revisions(0, NULL, &rev, &opt);\n+\n+\trev.diffopt.output_format = DIFF_FORMAT_CALLBACK;\n+\trev.diffopt.format_callback = collect_changes_cb;\n+\trev.diffopt.format_callback_data = s;\n+\n+\trun_diff_index(&rev, 1);\n+}\n+\n+void add_i_print_modified(void)\n+{\n+\tint i = 0;\n+\tstruct collection_status s;\n+\t/* TRANSLATORS: you can adjust this to align \"git add -i\" status menu */\n+\tconst char *modified_fmt = _(\"%12s %12s %s\");\n+\tconst char *header_color = get_color(COLOR_HEADER);\n+\tstruct object_id sha1;\n+\n+\tstruct hashmap_iter iter;\n+\tstruct file_stat **files;\n+\tstruct file_stat *entry;\n+\n+\tif (read_cache() < 0)\n+\t\treturn;\n+\n+\ts.reference = !get_oid(\"HEAD\", &sha1) ? \"HEAD\": empty_tree_oid_hex();\n+\thashmap_init(&s.file_map, hash_cmp, NULL, 0);\n+\n+\tcollect_changes_worktree(&s);\n+\tcollect_changes_index(&s);\n+\n+\tif (hashmap_get_size(&s.file_map) < 1) {\n+\t\tprintf(\"\\n\");\n+\t\treturn;\n+\t}\n+\n+\tprintf(HEADER_INDENT);\n+\tcolor_fprintf(stdout, header_color, modified_fmt, _(\"staged\"),\n+\t\t\t_(\"unstaged\"), _(\"path\"));\n+\tprintf(\"\\n\");\n+\n+\thashmap_iter_init(&s.file_map, &iter);\n+\n+\tfiles = xcalloc(hashmap_get_size(&s.file_map), sizeof(struct file_stat *));\n+\twhile ((entry = hashmap_iter_next(&iter))) {\n+\t\tfiles[i++] = entry;\n+\t}\n+\tQSORT(files, hashmap_get_size(&s.file_map), alphabetical_cmp);\n+\n+\tfor (i = 0; i < hashmap_get_size(&s.file_map); i++) {\n+\t\tstruct file_stat *f = files[i];\n+\n+\t\tchar worktree_changes[50];\n+\t\tchar index_changes[50];\n+\n+\t\tif (f->worktree.added || f->worktree.deleted)\n+\t\t\tsnprintf(worktree_changes, 50, \"+%\"PRIuMAX\"/-%\"PRIuMAX, f->worktree.added,\n+\t\t\t\t\tf->worktree.deleted);\n+\t\telse\n+\t\t\tsnprintf(worktree_changes, 50, \"%s\", _(\"nothing\"));\n+\n+\t\tif (f->index.added || f->index.deleted)\n+\t\t\tsnprintf(index_changes, 50, \"+%\"PRIuMAX\"/-%\"PRIuMAX, f->index.added,\n+\t\t\t\t\tf->index.deleted);\n+\t\telse\n+\t\t\tsnprintf(index_changes, 50, \"%s\", _(\"unchanged\"));\n+\n+\t\tprintf(\" %2d: \", i + 1);\n+\t\tprintf(modified_fmt, index_changes, worktree_changes, f->name);\n+\t\tprintf(\"\\n\");\n+\t}\n+\tprintf(\"\\n\");\n+\n+\tfree(files);\n+\thashmap_free(&s.file_map, 1);\n+}\ndiff --git a/add-interactive.h b/add-interactive.h\nnew file mode 100644\nindex 0000000000..1f4747553c\n--- /dev/null\n+++ b/add-interactive.h\n@@ -0,0 +1,8 @@\n+#ifndef ADD_INTERACTIVE_H\n+#define ADD_INTERACTIVE_H\n+\n+int add_i_config(const char *var, const char *value, void *cbdata);\n+\n+void add_i_print_modified(void);\n+\n+#endif\n\\ No newline at end of file\ndiff --git a/builtin/add--helper.c b/builtin/add--helper.c\nindex 6a97f0e191..43545d9af5 100644\n--- a/builtin/add--helper.c\n+++ b/builtin/add--helper.c\n@@ -1,6 +1,38 @@\n+#include \"add-interactive.h\"\n #include \"builtin.h\"\n+#include \"config.h\"\n+#include \"revision.h\"\n+\n+static const char * const builtin_add_helper_usage[] = {\n+\tN_(\"git add-interactive--helper <command>\"),\n+\tNULL\n+};\n+\n+enum cmd_mode {\n+\tDEFAULT = 0,\n+\tSTATUS\n+};\n \n int cmd_add__helper(int argc, const char **argv, const char *prefix)\n {\n+\tenum cmd_mode mode = DEFAULT;\n+\n+\tstruct option options[] = {\n+\t\tOPT_CMDMODE(0, \"status\", &mode,\n+\t\t\t N_(\"print status information with diffstat\"), STATUS),\n+\t\tOPT_END()\n+\t};\n+\n+\tgit_config(add_i_config, NULL);\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     builtin_add_helper_usage,\n+\t\t\t     PARSE_OPT_KEEP_ARGV0);\n+\n+\tif (mode == STATUS)\n+\t\tadd_i_print_modified();\n+\telse\n+\t\tusage_with_options(builtin_add_helper_usage,\n+\t\t\t\t   options);\n+\n \treturn 0;\n }\n-- \ngitgitgadget\n\n"},{"id":"367227","messageId":"87fttm49hv.fsf@evledraar.gmail.com","threadId":"50075","inReplyTo":"b9a1a7e37a477e978f19cbcc9b41f80519de54da.1548062019.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 7/7] add--interactive.perl: use add--helper --show-help for help_cmd","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2019-01-21T09:59:08Z","receivedAt":"2019-01-21T09:59:13Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Jan 21 2019, Slavica Djukic via GitGitGadget wrote:\n\n> From: Slavica Djukic <slawica92@hotmail.com>\n>\n> Change help_cmd sub in git-add--interactive.perl to use\n> show-help command from builtin add--helper.\n>\n> Signed-off-by: Slavica Djukic <slawica92@hotmail.com>\n> ---\n>  git-add--interactive.perl | 11 +----------\n>  1 file changed, 1 insertion(+), 10 deletions(-)\n>\n> diff --git a/git-add--interactive.perl b/git-add--interactive.perl\n> index a6536f9cf3..32ee729a58 100755\n> --- a/git-add--interactive.perl\n> +++ b/git-add--interactive.perl\n> @@ -1717,16 +1717,7 @@ sub quit_cmd {\n>  }\n>\n>  sub help_cmd {\n> -# TRANSLATORS: please do not translate the command names\n> -# 'status', 'update', 'revert', etc.\n> -\tprint colored $help_color, __ <<'EOF' ;\n> -status        - show paths with changes\n> -update        - add working tree state to the staged set of changes\n> -revert        - revert staged set of changes back to the HEAD version\n> -patch         - pick hunks and update selectively\n> -diff          - view diff between HEAD and index\n> -add untracked - add contents of untracked files to the staged set of changes\n> -EOF\n> +\tsystem(qw(git add--helper --show-help));\n>  }\n>\n>  sub process_args {\n\nBoth this and an earlier change in this series replaces a callback\ncommand with an invocation of system() without any error checking. So if\nthis add-helper fails for whatever reason we'll silently fail to report\nit.\n\nI think it makes sense to put something like the following WIP code\nearlier in the series. Then if the command was e.g. [\"false\", \"git\",\n\"status\"] we'd see:\n\n    What now> s\n    oh noes when running 'false git status': returned error '1'\n\nWIP patch. Obviously not ready as-is, but feel free to consider this to\nhave my SOB & adapt it.\n\ndiff --git a/git-add--interactive.perl b/git-add--interactive.perl\nindex 20eb81cc92..1cd5f8122b 100755\n--- a/git-add--interactive.perl\n+++ b/git-add--interactive.perl\n@@ -1777,7 +1777,7 @@ sub process_args {\n }\n\n sub main_loop {\n-\tmy @cmd = ([ 'status', \\&status_cmd, ],\n+\tmy @cmd = ([ 'status', [\"git\", \"status\"] ],\n \t\t   [ 'update', \\&update_cmd, ],\n \t\t   [ 'revert', \\&revert_cmd, ],\n \t\t   [ 'add untracked', \\&add_untracked_cmd, ],\n@@ -1794,11 +1794,23 @@ sub main_loop {\n \t\t\t\t\t     ON_EOF => \\&quit_cmd,\n \t\t\t\t\t     IMMEDIATE => 1 }, @cmd);\n \t\tif ($it) {\n-\t\t\teval {\n-\t\t\t\t$it->[1]->();\n-\t\t\t};\n-\t\t\tif ($@) {\n-\t\t\t\tprint \"$@\";\n+\t\t\tmy $cb = $it->[1];\n+\t\t\tif (ref $cb eq 'CODE') {\n+\t\t\t\teval {\n+\t\t\t\t\t$cb->();\n+\t\t\t\t\t1;\n+\t\t\t\t} or do {\n+\t\t\t\t\tprint \"$@\";\n+\t\t\t\t};\n+\t\t\t} else {\n+\t\t\t\tif (system(@$cb) != 0) {\n+\t\t\t\t\tif ($? == -1 || $? & 127) {\n+\t\t\t\t\t\tprint STDERR \"oh noes when running '@$cb': unexpected '$?'\\n\";\n+\t\t\t\t\t} else {\n+\t\t\t\t\t\tmy $ret = $? >> 8;\n+\t\t\t\t\t\tprint STDERR \"oh noes when running '@$cb': returned error '$ret'\\n\";\n+\t\t\t\t\t}\n+\t\t\t\t}\n \t\t\t}\n \t\t}\n \t}\n"},{"id":"367230","messageId":"1224d852-6009-7c91-764a-f1015ed253b5@gmail.com","threadId":"50075","inReplyTo":"87fttm49hv.fsf@evledraar.gmail.com","subject":"Re: [PATCH v3 7/7] add--interactive.perl: use add--helper --show-help for help_cmd","fromName":"Slavica Djukic","fromEmail":"slavicadj.ip2018@gmail.com","sentAt":"2019-01-21T11:59:22Z","receivedAt":"2019-01-21T11:59:32Z","isPatch":true,"sender":{"key":"slavicadj.ip2018@gmail.com","avatar":null},"body":"Hello Ævar,\n\nthanks for taking time and making review.\n\nOn 21-Jan-19 10:59 AM, Ævar Arnfjörð Bjarmason wrote:\n> On Mon, Jan 21 2019, Slavica Djukic via GitGitGadget wrote:\n>\n>> From: Slavica Djukic <slawica92@hotmail.com>\n>>\n>> Change help_cmd sub in git-add--interactive.perl to use\n>> show-help command from builtin add--helper.\n>>\n>> Signed-off-by: Slavica Djukic <slawica92@hotmail.com>\n>> ---\n>>   git-add--interactive.perl | 11 +----------\n>>   1 file changed, 1 insertion(+), 10 deletions(-)\n>>\n>> diff --git a/git-add--interactive.perl b/git-add--interactive.perl\n>> index a6536f9cf3..32ee729a58 100755\n>> --- a/git-add--interactive.perl\n>> +++ b/git-add--interactive.perl\n>> @@ -1717,16 +1717,7 @@ sub quit_cmd {\n>>   }\n>>\n>>   sub help_cmd {\n>> -# TRANSLATORS: please do not translate the command names\n>> -# 'status', 'update', 'revert', etc.\n>> -\tprint colored $help_color, __ <<'EOF' ;\n>> -status        - show paths with changes\n>> -update        - add working tree state to the staged set of changes\n>> -revert        - revert staged set of changes back to the HEAD version\n>> -patch         - pick hunks and update selectively\n>> -diff          - view diff between HEAD and index\n>> -add untracked - add contents of untracked files to the staged set of changes\n>> -EOF\n>> +\tsystem(qw(git add--helper --show-help));\n>>   }\n>>\n>>   sub process_args {\n> Both this and an earlier change in this series replaces a callback\n> command with an invocation of system() without any error checking. So if\n> this add-helper fails for whatever reason we'll silently fail to report\n> it.\n>\n> I think it makes sense to put something like the following WIP code\n> earlier in the series. Then if the command was e.g. [\"false\", \"git\",\n> \"status\"] we'd see:\n>\n>      What now> s\n>      oh noes when running 'false git status': returned error '1'\n>\n> WIP patch. Obviously not ready as-is, but feel free to consider this to\n> have my SOB & adapt it.\n\n\nAnd thank you for writing up this WIP patch.  I will adapt it and have \nyour SOB.\n\n-Slavica\n\n\n>\n> diff --git a/git-add--interactive.perl b/git-add--interactive.perl\n> index 20eb81cc92..1cd5f8122b 100755\n> --- a/git-add--interactive.perl\n> +++ b/git-add--interactive.perl\n> @@ -1777,7 +1777,7 @@ sub process_args {\n>   }\n>\n>   sub main_loop {\n> -\tmy @cmd = ([ 'status', \\&status_cmd, ],\n> +\tmy @cmd = ([ 'status', [\"git\", \"status\"] ],\n>   \t\t   [ 'update', \\&update_cmd, ],\n>   \t\t   [ 'revert', \\&revert_cmd, ],\n>   \t\t   [ 'add untracked', \\&add_untracked_cmd, ],\n> @@ -1794,11 +1794,23 @@ sub main_loop {\n>   \t\t\t\t\t     ON_EOF => \\&quit_cmd,\n>   \t\t\t\t\t     IMMEDIATE => 1 }, @cmd);\n>   \t\tif ($it) {\n> -\t\t\teval {\n> -\t\t\t\t$it->[1]->();\n> -\t\t\t};\n> -\t\t\tif ($@) {\n> -\t\t\t\tprint \"$@\";\n> +\t\t\tmy $cb = $it->[1];\n> +\t\t\tif (ref $cb eq 'CODE') {\n> +\t\t\t\teval {\n> +\t\t\t\t\t$cb->();\n> +\t\t\t\t\t1;\n> +\t\t\t\t} or do {\n> +\t\t\t\t\tprint \"$@\";\n> +\t\t\t\t};\n> +\t\t\t} else {\n> +\t\t\t\tif (system(@$cb) != 0) {\n> +\t\t\t\t\tif ($? == -1 || $? & 127) {\n> +\t\t\t\t\t\tprint STDERR \"oh noes when running '@$cb': unexpected '$?'\\n\";\n> +\t\t\t\t\t} else {\n> +\t\t\t\t\t\tmy $ret = $? >> 8;\n> +\t\t\t\t\t\tprint STDERR \"oh noes when running '@$cb': returned error '$ret'\\n\";\n> +\t\t\t\t\t}\n> +\t\t\t\t}\n>   \t\t\t}\n>   \t\t}\n>   \t}\n>\n"},{"id":"367641","messageId":"4a830716-f627-85e0-39bb-f61d7185ea2b@talktalk.net","threadId":"50075","inReplyTo":"aede733318bb6fccc1c3d16adf442135d3cb0692.1548062019.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 6/7] t3701-add-interactive: test add_i_show_help()","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2019-01-25T11:01:38Z","receivedAt":"2019-01-25T11:01:34Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Slavica\n\nHaving suggested that you move this patch up the series, I thought I\nshould check that it does actually pass before the rewrite.\nUnfortunately when I run it it fails, but it is due to a missing\ntrailing space in the expected file. I get\n\n--- expect\t2019-01-25 10:42:06.794251740 +0000\n+++ actual\t2019-01-25 10:42:07.044127788 +0000\n@@ -11,5 +11,5 @@\n <BOLD>*** Commands ***<RESET>\n   1: <BOLD;BLUE>s<RESET>tatus\t  2: <BOLD;BLUE>u<RESET>pdate\t  3:\n<BOLD;BLUE>r<RESET>evert\t  4: <BOLD;BLUE>a<RESET>dd untracked\n   5: <BOLD;BLUE>p<RESET>atch\t  6: <BOLD;BLUE>d<RESET>iff\t  7:\n<BOLD;BLUE>q<RESET>uit\t  8: <BOLD;BLUE>h<RESET>elp\n-<BOLD;BLUE>What now<RESET>>\n+<BOLD;BLUE>What now<RESET>>\n Bye.\n\nThere should be a space after '<BOLD;BLUE>What now<RESET>>' as the\nprompt has a space after it. The space is actually in the patch but it\ngot eaten by 'git am' (I have core.whitespace set but I think cleaning\nup trailing whitespace is on by default anyway). I think the best way to\nmake this patch easier to apply would be to use '$SP' which is already\nset up in earlier tests to avoid trailing whitespace in here documents.\nYou'll need to change\ncat >expect <<-\\EOF\nto\ncat >expect <<-EOF\nto enable variable interpolation in the here document as well.\n\nBest Wishes\n\nPhillip\n\n\nOn 21/01/2019 09:13, Slavica Djukic via GitGitGadget wrote:\n> From: Slavica Djukic <slawica92@hotmail.com>\n> \n> Add test to t3701-add-interactive to verify that add_i_show_help()\n> outputs expected content.\n> Also, add it before changing git-add--interactive.perl's help_cmd\n> to demonstrate that there are no changes introduced by the\n> conversion to C.\n> Prefix git add -i call with GIT_PAGER_IN_USE=true TERM=vt100\n> to force colored output on Windows.\n> \n> Signed-off-by: Slavica Djukic <slawica92@hotmail.com>\n> ---\n>  t/t3701-add-interactive.sh | 24 ++++++++++++++++++++++++\n>  1 file changed, 24 insertions(+)\n> \n> diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh\n> index 65dfbc033a..14e3286995 100755\n> --- a/t/t3701-add-interactive.sh\n> +++ b/t/t3701-add-interactive.sh\n> @@ -639,4 +639,28 @@ test_expect_success 'add -p patch editing works with pathological context lines'\n>  \ttest_cmp expected-2 actual\n>  '\n>  \n> +test_expect_success 'show help from add--helper' '\n> +\tgit reset --hard &&\n> +\tcat >expect <<-\\EOF &&\n> +\n> +\t<BOLD>*** Commands ***<RESET>\n> +\t  1: <BOLD;BLUE>s<RESET>tatus\t  2: <BOLD;BLUE>u<RESET>pdate\t  3: <BOLD;BLUE>r<RESET>evert\t  4: <BOLD;BLUE>a<RESET>dd untracked\n> +\t  5: <BOLD;BLUE>p<RESET>atch\t  6: <BOLD;BLUE>d<RESET>iff\t  7: <BOLD;BLUE>q<RESET>uit\t  8: <BOLD;BLUE>h<RESET>elp\n> +\t<BOLD;BLUE>What now<RESET>> <BOLD;RED>status        - show paths with changes<RESET>\n> +\t<BOLD;RED>update        - add working tree state to the staged set of changes<RESET>\n> +\t<BOLD;RED>revert        - revert staged set of changes back to the HEAD version<RESET>\n> +\t<BOLD;RED>patch         - pick hunks and update selectively<RESET>\n> +\t<BOLD;RED>diff          - view diff between HEAD and index<RESET>\n> +\t<BOLD;RED>add untracked - add contents of untracked files to the staged set of changes<RESET>\n> +\t<BOLD>*** Commands ***<RESET>\n> +\t  1: <BOLD;BLUE>s<RESET>tatus\t  2: <BOLD;BLUE>u<RESET>pdate\t  3: <BOLD;BLUE>r<RESET>evert\t  4: <BOLD;BLUE>a<RESET>dd untracked\n> +\t  5: <BOLD;BLUE>p<RESET>atch\t  6: <BOLD;BLUE>d<RESET>iff\t  7: <BOLD;BLUE>q<RESET>uit\t  8: <BOLD;BLUE>h<RESET>elp\n> +\t<BOLD;BLUE>What now<RESET>> \n> +\tBye.\n> +\tEOF\n> +\ttest_write_lines h | GIT_PAGER_IN_USE=true TERM=vt100 git add -i >actual.colored &&\n> +\ttest_decode_color <actual.colored >actual &&\n> +\ttest_i18ncmp expect actual\n> +'\n> +\n>  test_done\n> \n\n"},{"id":"367644","messageId":"8f7c2f2a-fdb2-33a8-17f9-5d3564d695a7@gmail.com","threadId":"50075","inReplyTo":"4a830716-f627-85e0-39bb-f61d7185ea2b@talktalk.net","subject":"Re: [PATCH v3 6/7] t3701-add-interactive: test add_i_show_help()","fromName":"Slavica Djukic","fromEmail":"slavicadj.ip2018@gmail.com","sentAt":"2019-01-25T11:36:12Z","receivedAt":"2019-01-25T11:36:27Z","isPatch":true,"sender":{"key":"slavicadj.ip2018@gmail.com","avatar":null},"body":"Hi Phillip,\n\nOn 25-Jan-19 12:01 PM, Phillip Wood wrote:\n> Hi Slavica\n>\n> Having suggested that you move this patch up the series, I thought I\n> should check that it does actually pass before the rewrite.\n> Unfortunately when I run it it fails, but it is due to a missing\n> trailing space in the expected file. I get\n>\n> --- expect\t2019-01-25 10:42:06.794251740 +0000\n> +++ actual\t2019-01-25 10:42:07.044127788 +0000\n> @@ -11,5 +11,5 @@\n>   <BOLD>*** Commands ***<RESET>\n>     1: <BOLD;BLUE>s<RESET>tatus\t  2: <BOLD;BLUE>u<RESET>pdate\t  3:\n> <BOLD;BLUE>r<RESET>evert\t  4: <BOLD;BLUE>a<RESET>dd untracked\n>     5: <BOLD;BLUE>p<RESET>atch\t  6: <BOLD;BLUE>d<RESET>iff\t  7:\n> <BOLD;BLUE>q<RESET>uit\t  8: <BOLD;BLUE>h<RESET>elp\n> -<BOLD;BLUE>What now<RESET>>\n> +<BOLD;BLUE>What now<RESET>>\n>   Bye.\n>\n> There should be a space after '<BOLD;BLUE>What now<RESET>>' as the\n> prompt has a space after it. The space is actually in the patch but it\n> got eaten by 'git am' (I have core.whitespace set but I think cleaning\n> up trailing whitespace is on by default anyway). I think the best way to\n> make this patch easier to apply would be to use '$SP' which is already\n> set up in earlier tests to avoid trailing whitespace in here documents.\n> You'll need to change\n> cat >expect <<-\\EOF\n> to\n> cat >expect <<-EOF\n> to enable variable interpolation in the here document as well.\n\n\nThanks for checking the test. I had no idea that this might happen.\n\nI'll send new iteration soon, along with this changes.\n\n\n-Slavica\n\n\n>\n> Best Wishes\n>\n> Phillip\n>\n>\n> On 21/01/2019 09:13, Slavica Djukic via GitGitGadget wrote:\n>> From: Slavica Djukic <slawica92@hotmail.com>\n>>\n>> Add test to t3701-add-interactive to verify that add_i_show_help()\n>> outputs expected content.\n>> Also, add it before changing git-add--interactive.perl's help_cmd\n>> to demonstrate that there are no changes introduced by the\n>> conversion to C.\n>> Prefix git add -i call with GIT_PAGER_IN_USE=true TERM=vt100\n>> to force colored output on Windows.\n>>\n>> Signed-off-by: Slavica Djukic <slawica92@hotmail.com>\n>> ---\n>>   t/t3701-add-interactive.sh | 24 ++++++++++++++++++++++++\n>>   1 file changed, 24 insertions(+)\n>>\n>> diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh\n>> index 65dfbc033a..14e3286995 100755\n>> --- a/t/t3701-add-interactive.sh\n>> +++ b/t/t3701-add-interactive.sh\n>> @@ -639,4 +639,28 @@ test_expect_success 'add -p patch editing works with pathological context lines'\n>>   \ttest_cmp expected-2 actual\n>>   '\n>>   \n>> +test_expect_success 'show help from add--helper' '\n>> +\tgit reset --hard &&\n>> +\tcat >expect <<-\\EOF &&\n>> +\n>> +\t<BOLD>*** Commands ***<RESET>\n>> +\t  1: <BOLD;BLUE>s<RESET>tatus\t  2: <BOLD;BLUE>u<RESET>pdate\t  3: <BOLD;BLUE>r<RESET>evert\t  4: <BOLD;BLUE>a<RESET>dd untracked\n>> +\t  5: <BOLD;BLUE>p<RESET>atch\t  6: <BOLD;BLUE>d<RESET>iff\t  7: <BOLD;BLUE>q<RESET>uit\t  8: <BOLD;BLUE>h<RESET>elp\n>> +\t<BOLD;BLUE>What now<RESET>> <BOLD;RED>status        - show paths with changes<RESET>\n>> +\t<BOLD;RED>update        - add working tree state to the staged set of changes<RESET>\n>> +\t<BOLD;RED>revert        - revert staged set of changes back to the HEAD version<RESET>\n>> +\t<BOLD;RED>patch         - pick hunks and update selectively<RESET>\n>> +\t<BOLD;RED>diff          - view diff between HEAD and index<RESET>\n>> +\t<BOLD;RED>add untracked - add contents of untracked files to the staged set of changes<RESET>\n>> +\t<BOLD>*** Commands ***<RESET>\n>> +\t  1: <BOLD;BLUE>s<RESET>tatus\t  2: <BOLD;BLUE>u<RESET>pdate\t  3: <BOLD;BLUE>r<RESET>evert\t  4: <BOLD;BLUE>a<RESET>dd untracked\n>> +\t  5: <BOLD;BLUE>p<RESET>atch\t  6: <BOLD;BLUE>d<RESET>iff\t  7: <BOLD;BLUE>q<RESET>uit\t  8: <BOLD;BLUE>h<RESET>elp\n>> +\t<BOLD;BLUE>What now<RESET>>\n>> +\tBye.\n>> +\tEOF\n>> +\ttest_write_lines h | GIT_PAGER_IN_USE=true TERM=vt100 git add -i >actual.colored &&\n>> +\ttest_decode_color <actual.colored >actual &&\n>> +\ttest_i18ncmp expect actual\n>> +'\n>> +\n>>   test_done\n>>\n"},{"id":"367645","messageId":"737767b6f4e0367a7797020bd07a6b4357a47413.1548419009.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.v4.git.gitgitgadget@gmail.com","subject":"[PATCH v4 1/7] diff: export diffstat interface","fromName":"Daniel Ferreira via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-01-25T12:23:31Z","receivedAt":"2019-01-25T12:23:35Z","isPatch":true,"sender":{"key":"bnmvco@gmail.com","avatar":"https://avatars.githubusercontent.com/u/735397?v=4"},"body":"From: Daniel Ferreira <bnmvco@gmail.com>\n\nMake the diffstat interface (namely, the diffstat_t struct and\ncompute_diffstat) no longer be internal to diff.c and allow it to be used\nby other parts of git.\n\nThis is helpful for code that may want to easily extract information\nfrom files using the diff machinery, while flushing it differently from\nhow the show_* functions used by diff_flush() do it. One example is the\nbuiltin implementation of git-add--interactive's status.\n\nSigned-off-by: Daniel Ferreira <bnmvco@gmail.com>\nSigned-off-by: Slavica Djukic <slawica92@hotmail.com>\n---\n diff.c | 36 ++++++++++++++----------------------\n diff.h | 18 ++++++++++++++++++\n 2 files changed, 32 insertions(+), 22 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex dc9965e836..46a7d8cf29 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -2421,22 +2421,6 @@ static void pprint_rename(struct strbuf *name, const char *a, const char *b)\n \t}\n }\n \n-struct diffstat_t {\n-\tint nr;\n-\tint alloc;\n-\tstruct diffstat_file {\n-\t\tchar *from_name;\n-\t\tchar *name;\n-\t\tchar *print_name;\n-\t\tconst char *comments;\n-\t\tunsigned is_unmerged:1;\n-\t\tunsigned is_binary:1;\n-\t\tunsigned is_renamed:1;\n-\t\tunsigned is_interesting:1;\n-\t\tuintmax_t added, deleted;\n-\t} **files;\n-};\n-\n static struct diffstat_file *diffstat_add(struct diffstat_t *diffstat,\n \t\t\t\t\t  const char *name_a,\n \t\t\t\t\t  const char *name_b)\n@@ -5922,12 +5906,7 @@ void diff_flush(struct diff_options *options)\n \t    dirstat_by_line) {\n \t\tstruct diffstat_t diffstat;\n \n-\t\tmemset(&diffstat, 0, sizeof(struct diffstat_t));\n-\t\tfor (i = 0; i < q->nr; i++) {\n-\t\t\tstruct diff_filepair *p = q->queue[i];\n-\t\t\tif (check_pair_status(p))\n-\t\t\t\tdiff_flush_stat(p, options, &diffstat);\n-\t\t}\n+\t\tcompute_diffstat(options, &diffstat);\n \t\tif (output_format & DIFF_FORMAT_NUMSTAT)\n \t\t\tshow_numstat(&diffstat, options);\n \t\tif (output_format & DIFF_FORMAT_DIFFSTAT)\n@@ -6227,6 +6206,19 @@ static int is_submodule_ignored(const char *path, struct diff_options *options)\n \treturn ignored;\n }\n \n+void compute_diffstat(struct diff_options *options, struct diffstat_t *diffstat)\n+{\n+\tint i;\n+\tstruct diff_queue_struct *q = &diff_queued_diff;\n+\n+\tmemset(diffstat, 0, sizeof(struct diffstat_t));\n+\tfor (i = 0; i < q->nr; i++) {\n+\t\tstruct diff_filepair *p = q->queue[i];\n+\t\tif (check_pair_status(p))\n+\t\t\tdiff_flush_stat(p, options, diffstat);\n+\t}\n+}\n+\n void diff_addremove(struct diff_options *options,\n \t\t    int addremove, unsigned mode,\n \t\t    const struct object_id *oid,\ndiff --git a/diff.h b/diff.h\nindex ce5e8a8183..7809db3039 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -239,6 +239,22 @@ void diff_emit_submodule_error(struct diff_options *o, const char *err);\n void diff_emit_submodule_pipethrough(struct diff_options *o,\n \t\t\t\t     const char *line, int len);\n \n+struct diffstat_t {\n+\tint nr;\n+\tint alloc;\n+\tstruct diffstat_file {\n+\t\tchar *from_name;\n+\t\tchar *name;\n+\t\tchar *print_name;\n+\t\tconst char *comments;\n+\t\tunsigned is_unmerged:1;\n+\t\tunsigned is_binary:1;\n+\t\tunsigned is_renamed:1;\n+\t\tunsigned is_interesting:1;\n+\t\tuintmax_t added, deleted;\n+\t} **files;\n+};\n+\n enum color_diff {\n \tDIFF_RESET = 0,\n \tDIFF_CONTEXT = 1,\n@@ -327,6 +343,8 @@ void diff_change(struct diff_options *,\n \n struct diff_filepair *diff_unmerge(struct diff_options *, const char *path);\n \n+void compute_diffstat(struct diff_options *options, struct diffstat_t *diffstat);\n+\n #define DIFF_SETUP_REVERSE      \t1\n #define DIFF_SETUP_USE_SIZE_CACHE\t4\n \n-- \ngitgitgadget\n\n"},{"id":"367646","messageId":"pull.103.v4.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.v3.git.gitgitgadget@gmail.com","subject":"[PATCH v4 0/7] Turn git add-i into built-in","fromName":"Slavica Đukić via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-01-25T12:23:30Z","receivedAt":"2019-01-25T12:23:36Z","isPatch":true,"sender":{"key":"name:Slavica Đukić","avatar":null},"body":"This is the first version of a patch series to start porting\ngit-add--interactive from Perl to C. Daniel Ferreira's patch series used as\na head start:\nhttps://public-inbox.org/git/1494907234-28903-1-git-send-email-bnmvco@gmail.com/t/#u\n\nChanges since v3:\n\n * add error check when calling add--helper's functions from\n   git-add--interactive.perl\n * replace trailing whitespace in the test with variable $SP used in earlier\n   tests\n\nCc: Phillip Wood phillip.wood@dunelm.org.uk [phillip.wood@dunelm.org.uk]\n\nDaniel Ferreira (4):\n  diff: export diffstat interface\n  add--helper: create builtin helper for interactive add\n  add-interactive.c: implement status command\n  add--interactive.perl: use add--helper --status for status_cmd\n\nSlavica Djukic (3):\n  add-interactive.c: implement show-help command\n  t3701-add-interactive: test add_i_show_help()\n  add--interactive.perl: use add--helper --show-help for help_cmd\n\n .gitignore                 |   1 +\n Makefile                   |   2 +\n add-interactive.c          | 263 +++++++++++++++++++++++++++++++++++++\n add-interactive.h          |  10 ++\n builtin.h                  |   1 +\n builtin/add--helper.c      |  43 ++++++\n diff.c                     |  36 ++---\n diff.h                     |  18 +++\n git-add--interactive.perl  |  17 +--\n git.c                      |   1 +\n t/t3701-add-interactive.sh |  24 ++++\n 11 files changed, 381 insertions(+), 35 deletions(-)\n create mode 100644 add-interactive.c\n create mode 100644 add-interactive.h\n create mode 100644 builtin/add--helper.c\n\n\nbase-commit: b21ebb671bb7dea8d342225f0d66c41f4e54d5ca\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-103%2FslavicaDj%2Fadd-i-v4\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-103/slavicaDj/add-i-v4\nPull-Request: https://github.com/gitgitgadget/git/pull/103\n\nRange-diff vs v3:\n\n 1:  737767b6f4 = 1:  737767b6f4 diff: export diffstat interface\n 2:  91b1963125 = 2:  91b1963125 add--helper: create builtin helper for interactive add\n 3:  d247ef69fe = 3:  d247ef69fe add-interactive.c: implement status command\n 4:  4950c889aa ! 4:  fb3f9378ac add--interactive.perl: use add--helper --status for status_cmd\n     @@ -2,12 +2,21 @@\n      \n          add--interactive.perl: use add--helper --status for status_cmd\n      \n     -    Call the newly introduced add--helper builtin on\n     +    Call the newly introduced add--helper builtin in\n          status_cmd() instead of relying on add--interactive's Perl\n     -    functions to build print the numstat.\n     +    functions to print the numstat.\n     +\n     +    If an error occurs, it will be reported, but the Perl script will\n     +    not exit, since the add--helper is called within an eval block.\n     +\n     +    As the Perl script will go away soon, so will this scenario, where\n     +    the built-in helper is called  from the Perl script. Combined with\n     +    the fact that it would be hard to test, we'll pass on adding\n     +    a regression test for this.\n      \n          Signed-off-by: Daniel Ferreira <bnmvco@gmail.com>\n          Signed-off-by: Slavica Djukic <slawica92@hotmail.com>\n     +    Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n      \n       diff --git a/git-add--interactive.perl b/git-add--interactive.perl\n       --- a/git-add--interactive.perl\n     @@ -19,7 +28,8 @@\n      -\tlist_and_choose({ LIST_ONLY => 1, HEADER => $status_head },\n      -\t\t\tlist_modified());\n      -\tprint \"\\n\";\n     -+\tsystem(qw(git add--helper --status));\n     ++\tmy @status_cmd = (\"git\", \"add--helper\", \"--status\");\n     ++\t!system(@status_cmd) or die \"@status_cmd exited with code $?\";\n       }\n       \n       sub say_n_paths {\n 5:  581b108c9c = 5:  ab16afd1d5 add-interactive.c: implement show-help command\n 6:  aede733318 ! 6:  0a27304a84 t3701-add-interactive: test add_i_show_help()\n     @@ -7,6 +7,7 @@\n          Also, add it before changing git-add--interactive.perl's help_cmd\n          to demonstrate that there are no changes introduced by the\n          conversion to C.\n     +\n          Prefix git add -i call with GIT_PAGER_IN_USE=true TERM=vt100\n          to force colored output on Windows.\n      \n     @@ -21,7 +22,7 @@\n       \n      +test_expect_success 'show help from add--helper' '\n      +\tgit reset --hard &&\n     -+\tcat >expect <<-\\EOF &&\n     ++\tcat >expect <<-EOF &&\n      +\n      +\t<BOLD>*** Commands ***<RESET>\n      +\t  1: <BOLD;BLUE>s<RESET>tatus\t  2: <BOLD;BLUE>u<RESET>pdate\t  3: <BOLD;BLUE>r<RESET>evert\t  4: <BOLD;BLUE>a<RESET>dd untracked\n     @@ -35,7 +36,7 @@\n      +\t<BOLD>*** Commands ***<RESET>\n      +\t  1: <BOLD;BLUE>s<RESET>tatus\t  2: <BOLD;BLUE>u<RESET>pdate\t  3: <BOLD;BLUE>r<RESET>evert\t  4: <BOLD;BLUE>a<RESET>dd untracked\n      +\t  5: <BOLD;BLUE>p<RESET>atch\t  6: <BOLD;BLUE>d<RESET>iff\t  7: <BOLD;BLUE>q<RESET>uit\t  8: <BOLD;BLUE>h<RESET>elp\n     -+\t<BOLD;BLUE>What now<RESET>> \n     ++\t<BOLD;BLUE>What now<RESET>>$SP\n      +\tBye.\n      +\tEOF\n      +\ttest_write_lines h | GIT_PAGER_IN_USE=true TERM=vt100 git add -i >actual.colored &&\n 7:  b9a1a7e37a ! 7:  ca2a7c4375 add--interactive.perl: use add--helper --show-help for help_cmd\n     @@ -5,7 +5,15 @@\n          Change help_cmd sub in git-add--interactive.perl to use\n          show-help command from builtin add--helper.\n      \n     +    If an error occurs, it will be reported, but the Perl script will\n     +    not exit, since the add--helper is called within an eval block.\n     +\n     +    Just like the change where the Perl script calls the add--helper\n     +    to print the numstat, also here we forgo adding a regression test:\n     +    the Perl script is on its way out (and this patch is part of that journey).\n     +\n          Signed-off-by: Slavica Djukic <slawica92@hotmail.com>\n     +    Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n      \n       diff --git a/git-add--interactive.perl b/git-add--interactive.perl\n       --- a/git-add--interactive.perl\n     @@ -24,7 +32,8 @@\n      -diff          - view diff between HEAD and index\n      -add untracked - add contents of untracked files to the staged set of changes\n      -EOF\n     -+\tsystem(qw(git add--helper --show-help));\n     ++\tmy @help_cmd = (\"git\", \"add--helper\", \"--show-help\");\n     ++\t!system(@help_cmd) or die \"@help_cmd exited with code $?\";\n       }\n       \n       sub process_args {\n\n-- \ngitgitgadget\n"},{"id":"367647","messageId":"91b1963125f7dfdd46fe5166125ee59ef2557b13.1548419009.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.v4.git.gitgitgadget@gmail.com","subject":"[PATCH v4 2/7] add--helper: create builtin helper for interactive add","fromName":"Daniel Ferreira via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-01-25T12:23:32Z","receivedAt":"2019-01-25T12:23:37Z","isPatch":true,"sender":{"key":"bnmvco@gmail.com","avatar":"https://avatars.githubusercontent.com/u/735397?v=4"},"body":"From: Daniel Ferreira <bnmvco@gmail.com>\n\nCreate a builtin helper for git-add--interactive, which right now is not\nable to do anything.\n\nThis is the first step in an effort to convert git-add--interactive.perl\nto a C builtin, in search for better portability, expressibility and\nperformance (specially on non-POSIX systems like Windows).\n\nAdditionally, an eventual complete port of git-add--interactive would\nremove the last \"big\" Git script to have Perl as a dependency, allowing\nmost Git users to have a NOPERL build running without big losses.\n\nSigned-off-by: Daniel Ferreira <bnmvco@gmail.com>\nSigned-off-by: Slavica Djukic <slawica92@hotmail.com>\n---\n .gitignore            | 1 +\n Makefile              | 1 +\n builtin.h             | 1 +\n builtin/add--helper.c | 6 ++++++\n git.c                 | 1 +\n 5 files changed, 10 insertions(+)\n create mode 100644 builtin/add--helper.c\n\ndiff --git a/.gitignore b/.gitignore\nindex 0d77ea5894..2ee71ed217 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -15,6 +15,7 @@\n /git\n /git-add\n /git-add--interactive\n+/git-add--helper\n /git-am\n /git-annotate\n /git-apply\ndiff --git a/Makefile b/Makefile\nindex 1a44c811aa..9c84b80739 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1023,6 +1023,7 @@ LIB_OBJS += xdiff-interface.o\n LIB_OBJS += zlib.o\n \n BUILTIN_OBJS += builtin/add.o\n+BUILTIN_OBJS += builtin/add--helper.o\n BUILTIN_OBJS += builtin/am.o\n BUILTIN_OBJS += builtin/annotate.o\n BUILTIN_OBJS += builtin/apply.o\ndiff --git a/builtin.h b/builtin.h\nindex 6538932e99..dd811ef7d5 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -128,6 +128,7 @@ extern void setup_auto_pager(const char *cmd, int def);\n extern int is_builtin(const char *s);\n \n extern int cmd_add(int argc, const char **argv, const char *prefix);\n+extern int cmd_add__helper(int argc, const char **argv, const char *prefix);\n extern int cmd_am(int argc, const char **argv, const char *prefix);\n extern int cmd_annotate(int argc, const char **argv, const char *prefix);\n extern int cmd_apply(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/add--helper.c b/builtin/add--helper.c\nnew file mode 100644\nindex 0000000000..6a97f0e191\n--- /dev/null\n+++ b/builtin/add--helper.c\n@@ -0,0 +1,6 @@\n+#include \"builtin.h\"\n+\n+int cmd_add__helper(int argc, const char **argv, const char *prefix)\n+{\n+\treturn 0;\n+}\ndiff --git a/git.c b/git.c\nindex 2f604a41ea..5507591f2e 100644\n--- a/git.c\n+++ b/git.c\n@@ -443,6 +443,7 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)\n \n static struct cmd_struct commands[] = {\n \t{ \"add\", cmd_add, RUN_SETUP | NEED_WORK_TREE },\n+\t{ \"add--helper\", cmd_add__helper, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"am\", cmd_am, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"annotate\", cmd_annotate, RUN_SETUP | NO_PARSEOPT },\n \t{ \"apply\", cmd_apply, RUN_SETUP_GENTLY },\n-- \ngitgitgadget\n\n"},{"id":"367648","messageId":"d247ef69fe2c9d017b8d363de554a52269c0d281.1548419009.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.v4.git.gitgitgadget@gmail.com","subject":"[PATCH v4 3/7] add-interactive.c: implement status command","fromName":"Daniel Ferreira via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-01-25T12:23:33Z","receivedAt":"2019-01-25T12:23:41Z","isPatch":true,"sender":{"key":"bnmvco@gmail.com","avatar":"https://avatars.githubusercontent.com/u/735397?v=4"},"body":"From: Daniel Ferreira <bnmvco@gmail.com>\n\nAdd new files: add-interactive.c and add-interactive.h, which\nwill be used for implementing \"application logic\" of git add -i,\nwhereas add--helper.c will be used mostly for parsing the command line.\nWe're a bit lax with the command-line parsing, as the command is\nintended to be called only by one internal user: the add--interactive script.\n\nImplement add --interactive's status command in add-interactive.c and\nuse it in builtin add--helper.c.\n\nIt prints a numstat comparing changed files between a) the worktree and\nthe index; b) the index and the HEAD.\n\nTo do so, we use run_diff_index() and run_diff_files() to get changed\nfiles, use the diffstat API on them to get the numstat and use a\ncombination of a hashmap and qsort() to print the result in\nO(n) + O(n lg n) complexity.\n\nThis is the first interactive add command implemented in C of those\nanticipated by the previous commit, which introduced\nthe add--helper built-in.\n\nSigned-off-by: Daniel Ferreira <bnmvco@gmail.com>\nSigned-off-by: Slavica Djukic <slawica92@hotmail.com>\n---\n Makefile              |   1 +\n add-interactive.c     | 246 ++++++++++++++++++++++++++++++++++++++++++\n add-interactive.h     |   8 ++\n builtin/add--helper.c |  32 ++++++\n 4 files changed, 287 insertions(+)\n create mode 100644 add-interactive.c\n create mode 100644 add-interactive.h\n\ndiff --git a/Makefile b/Makefile\nindex 9c84b80739..2a4a5cc37b 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -827,6 +827,7 @@ LIB_H = $(shell $(FIND) . \\\n \t-name '*.h' -print)\n \n LIB_OBJS += abspath.o\n+LIB_OBJS += add-interactive.o\n LIB_OBJS += advice.o\n LIB_OBJS += alias.o\n LIB_OBJS += alloc.o\ndiff --git a/add-interactive.c b/add-interactive.c\nnew file mode 100644\nindex 0000000000..c55d934186\n--- /dev/null\n+++ b/add-interactive.c\n@@ -0,0 +1,246 @@\n+#include \"add-interactive.h\"\n+#include \"cache.h\"\n+#include \"commit.h\"\n+#include \"color.h\"\n+#include \"config.h\"\n+#include \"diffcore.h\"\n+#include \"revision.h\"\n+\n+#define HEADER_INDENT \"      \"\n+\n+enum collection_phase {\n+\tWORKTREE,\n+\tINDEX\n+};\n+\n+struct file_stat {\n+\tstruct hashmap_entry ent;\n+\tstruct {\n+\t\tuintmax_t added, deleted;\n+\t} index, worktree;\n+\tchar name[FLEX_ARRAY];\n+};\n+\n+struct collection_status {\n+\tenum collection_phase phase;\n+\n+\tconst char *reference;\n+\tstruct pathspec pathspec;\n+\n+\tstruct hashmap file_map;\n+};\n+\n+static int use_color = -1;\n+enum color_add_i {\n+\tCOLOR_PROMPT,\n+\tCOLOR_HEADER,\n+\tCOLOR_HELP,\n+\tCOLOR_ERROR\n+};\n+\n+static char colors[][COLOR_MAXLEN] = {\n+\tGIT_COLOR_BOLD_BLUE, /* Prompt */\n+\tGIT_COLOR_BOLD,      /* Header */\n+\tGIT_COLOR_BOLD_RED,  /* Help */\n+\tGIT_COLOR_BOLD_RED   /* Error */\n+};\n+\n+static const char *get_color(enum color_add_i ix)\n+{\n+\tif (want_color(use_color))\n+\t\treturn colors[ix];\n+\treturn \"\";\n+}\n+\n+static int parse_color_slot(const char *slot)\n+{\n+\tif (!strcasecmp(slot, \"prompt\"))\n+\t\treturn COLOR_PROMPT;\n+\tif (!strcasecmp(slot, \"header\"))\n+\t\treturn COLOR_HEADER;\n+\tif (!strcasecmp(slot, \"help\"))\n+\t\treturn COLOR_HELP;\n+\tif (!strcasecmp(slot, \"error\"))\n+\t\treturn COLOR_ERROR;\n+\n+\treturn -1;\n+}\n+\n+int add_i_config(const char *var,\n+\t\tconst char *value, void *cbdata)\n+{\n+\tconst char *name;\n+\n+\tif (!strcmp(var, \"color.interactive\")) {\n+\t\tuse_color = git_config_colorbool(var, value);\n+\t\treturn 0;\n+\t}\n+\n+\tif (skip_prefix(var, \"color.interactive.\", &name)) {\n+\t\tint slot = parse_color_slot(name);\n+\t\tif (slot < 0)\n+\t\t\treturn 0;\n+\t\tif (!value)\n+\t\t\treturn config_error_nonbool(var);\n+\t\treturn color_parse(value, colors[slot]);\n+\t}\n+\n+\treturn git_default_config(var, value, cbdata);\n+}\n+\n+static int hash_cmp(const void *unused_cmp_data, const void *entry,\n+\t\t\tconst void *entry_or_key, const void *keydata)\n+{\n+\tconst struct file_stat *e1 = entry, *e2 = entry_or_key;\n+\tconst char *name = keydata ? keydata : e2->name;\n+\n+\treturn strcmp(e1->name, name);\n+}\n+\n+static int alphabetical_cmp(const void *a, const void *b)\n+{\n+\tstruct file_stat *f1 = *((struct file_stat **)a);\n+\tstruct file_stat *f2 = *((struct file_stat **)b);\n+\n+\treturn strcmp(f1->name, f2->name);\n+}\n+\n+static void collect_changes_cb(struct diff_queue_struct *q,\n+\t\t\t\t\t struct diff_options *options,\n+\t\t\t\t\t void *data)\n+{\n+\tstruct collection_status *s = data;\n+\tstruct diffstat_t stat = { 0 };\n+\tint i;\n+\n+\tif (!q->nr)\n+\t\treturn;\n+\n+\tcompute_diffstat(options, &stat);\n+\n+\tfor (i = 0; i < stat.nr; i++) {\n+\t\tstruct file_stat *entry;\n+\t\tconst char *name = stat.files[i]->name;\n+\t\tunsigned int hash = strhash(name);\n+\n+\t\tentry = hashmap_get_from_hash(&s->file_map, hash, name);\n+\t\tif (!entry) {\n+\t\t\tFLEX_ALLOC_STR(entry, name, name);\n+\t\t\thashmap_entry_init(entry, hash);\n+\t\t\thashmap_add(&s->file_map, entry);\n+\t\t}\n+\n+\t\tif (s->phase == WORKTREE) {\n+\t\t\tentry->worktree.added = stat.files[i]->added;\n+\t\t\tentry->worktree.deleted = stat.files[i]->deleted;\n+\t\t} else if (s->phase == INDEX) {\n+\t\t\tentry->index.added = stat.files[i]->added;\n+\t\t\tentry->index.deleted = stat.files[i]->deleted;\n+\t\t}\n+\t}\n+}\n+\n+static void collect_changes_worktree(struct collection_status *s)\n+{\n+\tstruct rev_info rev;\n+\n+\ts->phase = WORKTREE;\n+\n+\tinit_revisions(&rev, NULL);\n+\tsetup_revisions(0, NULL, &rev, NULL);\n+\n+\trev.max_count = 0;\n+\n+\trev.diffopt.flags.ignore_dirty_submodules = 1;\n+\trev.diffopt.output_format = DIFF_FORMAT_CALLBACK;\n+\trev.diffopt.format_callback = collect_changes_cb;\n+\trev.diffopt.format_callback_data = s;\n+\n+\trun_diff_files(&rev, 0);\n+}\n+\n+static void collect_changes_index(struct collection_status *s)\n+{\n+\tstruct rev_info rev;\n+\tstruct setup_revision_opt opt = { 0 };\n+\n+\ts->phase = INDEX;\n+\n+\tinit_revisions(&rev, NULL);\n+\topt.def = s->reference;\n+\tsetup_revisions(0, NULL, &rev, &opt);\n+\n+\trev.diffopt.output_format = DIFF_FORMAT_CALLBACK;\n+\trev.diffopt.format_callback = collect_changes_cb;\n+\trev.diffopt.format_callback_data = s;\n+\n+\trun_diff_index(&rev, 1);\n+}\n+\n+void add_i_print_modified(void)\n+{\n+\tint i = 0;\n+\tstruct collection_status s;\n+\t/* TRANSLATORS: you can adjust this to align \"git add -i\" status menu */\n+\tconst char *modified_fmt = _(\"%12s %12s %s\");\n+\tconst char *header_color = get_color(COLOR_HEADER);\n+\tstruct object_id sha1;\n+\n+\tstruct hashmap_iter iter;\n+\tstruct file_stat **files;\n+\tstruct file_stat *entry;\n+\n+\tif (read_cache() < 0)\n+\t\treturn;\n+\n+\ts.reference = !get_oid(\"HEAD\", &sha1) ? \"HEAD\": empty_tree_oid_hex();\n+\thashmap_init(&s.file_map, hash_cmp, NULL, 0);\n+\n+\tcollect_changes_worktree(&s);\n+\tcollect_changes_index(&s);\n+\n+\tif (hashmap_get_size(&s.file_map) < 1) {\n+\t\tprintf(\"\\n\");\n+\t\treturn;\n+\t}\n+\n+\tprintf(HEADER_INDENT);\n+\tcolor_fprintf(stdout, header_color, modified_fmt, _(\"staged\"),\n+\t\t\t_(\"unstaged\"), _(\"path\"));\n+\tprintf(\"\\n\");\n+\n+\thashmap_iter_init(&s.file_map, &iter);\n+\n+\tfiles = xcalloc(hashmap_get_size(&s.file_map), sizeof(struct file_stat *));\n+\twhile ((entry = hashmap_iter_next(&iter))) {\n+\t\tfiles[i++] = entry;\n+\t}\n+\tQSORT(files, hashmap_get_size(&s.file_map), alphabetical_cmp);\n+\n+\tfor (i = 0; i < hashmap_get_size(&s.file_map); i++) {\n+\t\tstruct file_stat *f = files[i];\n+\n+\t\tchar worktree_changes[50];\n+\t\tchar index_changes[50];\n+\n+\t\tif (f->worktree.added || f->worktree.deleted)\n+\t\t\tsnprintf(worktree_changes, 50, \"+%\"PRIuMAX\"/-%\"PRIuMAX, f->worktree.added,\n+\t\t\t\t\tf->worktree.deleted);\n+\t\telse\n+\t\t\tsnprintf(worktree_changes, 50, \"%s\", _(\"nothing\"));\n+\n+\t\tif (f->index.added || f->index.deleted)\n+\t\t\tsnprintf(index_changes, 50, \"+%\"PRIuMAX\"/-%\"PRIuMAX, f->index.added,\n+\t\t\t\t\tf->index.deleted);\n+\t\telse\n+\t\t\tsnprintf(index_changes, 50, \"%s\", _(\"unchanged\"));\n+\n+\t\tprintf(\" %2d: \", i + 1);\n+\t\tprintf(modified_fmt, index_changes, worktree_changes, f->name);\n+\t\tprintf(\"\\n\");\n+\t}\n+\tprintf(\"\\n\");\n+\n+\tfree(files);\n+\thashmap_free(&s.file_map, 1);\n+}\ndiff --git a/add-interactive.h b/add-interactive.h\nnew file mode 100644\nindex 0000000000..1f4747553c\n--- /dev/null\n+++ b/add-interactive.h\n@@ -0,0 +1,8 @@\n+#ifndef ADD_INTERACTIVE_H\n+#define ADD_INTERACTIVE_H\n+\n+int add_i_config(const char *var, const char *value, void *cbdata);\n+\n+void add_i_print_modified(void);\n+\n+#endif\n\\ No newline at end of file\ndiff --git a/builtin/add--helper.c b/builtin/add--helper.c\nindex 6a97f0e191..43545d9af5 100644\n--- a/builtin/add--helper.c\n+++ b/builtin/add--helper.c\n@@ -1,6 +1,38 @@\n+#include \"add-interactive.h\"\n #include \"builtin.h\"\n+#include \"config.h\"\n+#include \"revision.h\"\n+\n+static const char * const builtin_add_helper_usage[] = {\n+\tN_(\"git add-interactive--helper <command>\"),\n+\tNULL\n+};\n+\n+enum cmd_mode {\n+\tDEFAULT = 0,\n+\tSTATUS\n+};\n \n int cmd_add__helper(int argc, const char **argv, const char *prefix)\n {\n+\tenum cmd_mode mode = DEFAULT;\n+\n+\tstruct option options[] = {\n+\t\tOPT_CMDMODE(0, \"status\", &mode,\n+\t\t\t N_(\"print status information with diffstat\"), STATUS),\n+\t\tOPT_END()\n+\t};\n+\n+\tgit_config(add_i_config, NULL);\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     builtin_add_helper_usage,\n+\t\t\t     PARSE_OPT_KEEP_ARGV0);\n+\n+\tif (mode == STATUS)\n+\t\tadd_i_print_modified();\n+\telse\n+\t\tusage_with_options(builtin_add_helper_usage,\n+\t\t\t\t   options);\n+\n \treturn 0;\n }\n-- \ngitgitgadget\n\n"},{"id":"367649","messageId":"0a27304a840a88fded721259b795762e9b786530.1548419009.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.v4.git.gitgitgadget@gmail.com","subject":"[PATCH v4 6/7] t3701-add-interactive: test add_i_show_help()","fromName":"Slavica Djukic via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-01-25T12:23:35Z","receivedAt":"2019-01-25T12:23:41Z","isPatch":true,"sender":{"key":"slavicadj.ip2018@gmail.com","avatar":null},"body":"From: Slavica Djukic <slawica92@hotmail.com>\n\nAdd test to t3701-add-interactive to verify that add_i_show_help()\noutputs expected content.\nAlso, add it before changing git-add--interactive.perl's help_cmd\nto demonstrate that there are no changes introduced by the\nconversion to C.\n\nPrefix git add -i call with GIT_PAGER_IN_USE=true TERM=vt100\nto force colored output on Windows.\n\nSigned-off-by: Slavica Djukic <slawica92@hotmail.com>\n---\n t/t3701-add-interactive.sh | 24 ++++++++++++++++++++++++\n 1 file changed, 24 insertions(+)\n\ndiff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh\nindex 65dfbc033a..91aaef2932 100755\n--- a/t/t3701-add-interactive.sh\n+++ b/t/t3701-add-interactive.sh\n@@ -639,4 +639,28 @@ test_expect_success 'add -p patch editing works with pathological context lines'\n \ttest_cmp expected-2 actual\n '\n \n+test_expect_success 'show help from add--helper' '\n+\tgit reset --hard &&\n+\tcat >expect <<-EOF &&\n+\n+\t<BOLD>*** Commands ***<RESET>\n+\t  1: <BOLD;BLUE>s<RESET>tatus\t  2: <BOLD;BLUE>u<RESET>pdate\t  3: <BOLD;BLUE>r<RESET>evert\t  4: <BOLD;BLUE>a<RESET>dd untracked\n+\t  5: <BOLD;BLUE>p<RESET>atch\t  6: <BOLD;BLUE>d<RESET>iff\t  7: <BOLD;BLUE>q<RESET>uit\t  8: <BOLD;BLUE>h<RESET>elp\n+\t<BOLD;BLUE>What now<RESET>> <BOLD;RED>status        - show paths with changes<RESET>\n+\t<BOLD;RED>update        - add working tree state to the staged set of changes<RESET>\n+\t<BOLD;RED>revert        - revert staged set of changes back to the HEAD version<RESET>\n+\t<BOLD;RED>patch         - pick hunks and update selectively<RESET>\n+\t<BOLD;RED>diff          - view diff between HEAD and index<RESET>\n+\t<BOLD;RED>add untracked - add contents of untracked files to the staged set of changes<RESET>\n+\t<BOLD>*** Commands ***<RESET>\n+\t  1: <BOLD;BLUE>s<RESET>tatus\t  2: <BOLD;BLUE>u<RESET>pdate\t  3: <BOLD;BLUE>r<RESET>evert\t  4: <BOLD;BLUE>a<RESET>dd untracked\n+\t  5: <BOLD;BLUE>p<RESET>atch\t  6: <BOLD;BLUE>d<RESET>iff\t  7: <BOLD;BLUE>q<RESET>uit\t  8: <BOLD;BLUE>h<RESET>elp\n+\t<BOLD;BLUE>What now<RESET>>$SP\n+\tBye.\n+\tEOF\n+\ttest_write_lines h | GIT_PAGER_IN_USE=true TERM=vt100 git add -i >actual.colored &&\n+\ttest_decode_color <actual.colored >actual &&\n+\ttest_i18ncmp expect actual\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"367650","messageId":"ab16afd1d546ad640fd81338a602c6df1d9ae5ab.1548419009.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.v4.git.gitgitgadget@gmail.com","subject":"[PATCH v4 5/7] add-interactive.c: implement show-help command","fromName":"Slavica Djukic via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-01-25T12:23:35Z","receivedAt":"2019-01-25T12:23:42Z","isPatch":true,"sender":{"key":"slavicadj.ip2018@gmail.com","avatar":null},"body":"From: Slavica Djukic <slawica92@hotmail.com>\n\nImplement show-help command in add-interactive.c and use it in\nbuiltin add--helper.c.\n\nUse command name \"show-help\" instead of \"help\": add--helper is\nbuiltin, hence add--helper --help would be intercepted by\nhandle_builtin and re-routed to the help command, without ever\ncalling cmd_add__helper().\n\nSigned-off-by: Slavica Djukic <slawica92@hotmail.com>\n---\n add-interactive.c     | 17 +++++++++++++++++\n add-interactive.h     |  4 +++-\n builtin/add--helper.c |  7 ++++++-\n 3 files changed, 26 insertions(+), 2 deletions(-)\n\ndiff --git a/add-interactive.c b/add-interactive.c\nindex c55d934186..d239037bc1 100644\n--- a/add-interactive.c\n+++ b/add-interactive.c\n@@ -244,3 +244,20 @@ void add_i_print_modified(void)\n \tfree(files);\n \thashmap_free(&s.file_map, 1);\n }\n+\n+void add_i_show_help(void)\n+{\n+\tconst char *help_color = get_color(COLOR_HELP);\n+\tcolor_fprintf_ln(stdout, help_color, \"status        - %s\",\n+\t\t\t_(\"show paths with changes\"));\n+\tcolor_fprintf_ln(stdout, help_color, \"update        - %s\",\n+\t\t\t_(\"add working tree state to the staged set of changes\"));\n+\tcolor_fprintf_ln(stdout, help_color, \"revert        - %s\",\n+\t\t\t_(\"revert staged set of changes back to the HEAD version\"));\n+\tcolor_fprintf_ln(stdout, help_color, \"patch         - %s\",\n+\t\t\t_(\"pick hunks and update selectively\"));\n+\tcolor_fprintf_ln(stdout, help_color, \"diff          - %s\",\n+\t\t\t_(\"view diff between HEAD and index\"));\n+\tcolor_fprintf_ln(stdout, help_color, \"add untracked - %s\",\n+\t\t\t_(\"add contents of untracked files to the staged set of changes\"));\n+}\ndiff --git a/add-interactive.h b/add-interactive.h\nindex 1f4747553c..46e17c5c71 100644\n--- a/add-interactive.h\n+++ b/add-interactive.h\n@@ -5,4 +5,6 @@ int add_i_config(const char *var, const char *value, void *cbdata);\n \n void add_i_print_modified(void);\n \n-#endif\n\\ No newline at end of file\n+void add_i_show_help(void);\n+\n+#endif\ndiff --git a/builtin/add--helper.c b/builtin/add--helper.c\nindex 43545d9af5..a3b3a68b68 100644\n--- a/builtin/add--helper.c\n+++ b/builtin/add--helper.c\n@@ -10,7 +10,8 @@ static const char * const builtin_add_helper_usage[] = {\n \n enum cmd_mode {\n \tDEFAULT = 0,\n-\tSTATUS\n+\tSTATUS,\n+\tHELP\n };\n \n int cmd_add__helper(int argc, const char **argv, const char *prefix)\n@@ -20,6 +21,8 @@ int cmd_add__helper(int argc, const char **argv, const char *prefix)\n \tstruct option options[] = {\n \t\tOPT_CMDMODE(0, \"status\", &mode,\n \t\t\t N_(\"print status information with diffstat\"), STATUS),\n+\t\tOPT_CMDMODE(0, \"show-help\", &mode,\n+\t\t\t N_(\"show help\"), HELP),\n \t\tOPT_END()\n \t};\n \n@@ -30,6 +33,8 @@ int cmd_add__helper(int argc, const char **argv, const char *prefix)\n \n \tif (mode == STATUS)\n \t\tadd_i_print_modified();\n+\telse if (mode == HELP)\n+\t\tadd_i_show_help();\n \telse\n \t\tusage_with_options(builtin_add_helper_usage,\n \t\t\t\t   options);\n-- \ngitgitgadget\n\n"},{"id":"367651","messageId":"fb3f9378aca1ca2e5d59b518b0066cbe273537a4.1548419009.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.v4.git.gitgitgadget@gmail.com","subject":"[PATCH v4 4/7] add--interactive.perl: use add--helper --status for status_cmd","fromName":"Daniel Ferreira via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-01-25T12:23:34Z","receivedAt":"2019-01-25T12:23:43Z","isPatch":true,"sender":{"key":"bnmvco@gmail.com","avatar":"https://avatars.githubusercontent.com/u/735397?v=4"},"body":"From: Daniel Ferreira <bnmvco@gmail.com>\n\nCall the newly introduced add--helper builtin in\nstatus_cmd() instead of relying on add--interactive's Perl\nfunctions to print the numstat.\n\nIf an error occurs, it will be reported, but the Perl script will\nnot exit, since the add--helper is called within an eval block.\n\nAs the Perl script will go away soon, so will this scenario, where\nthe built-in helper is called  from the Perl script. Combined with\nthe fact that it would be hard to test, we'll pass on adding\na regression test for this.\n\nSigned-off-by: Daniel Ferreira <bnmvco@gmail.com>\nSigned-off-by: Slavica Djukic <slawica92@hotmail.com>\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n git-add--interactive.perl | 5 ++---\n 1 file changed, 2 insertions(+), 3 deletions(-)\n\ndiff --git a/git-add--interactive.perl b/git-add--interactive.perl\nindex 20eb81cc92..c2c6b4d5e3 100755\n--- a/git-add--interactive.perl\n+++ b/git-add--interactive.perl\n@@ -597,9 +597,8 @@ sub prompt_help_cmd {\n }\n \n sub status_cmd {\n-\tlist_and_choose({ LIST_ONLY => 1, HEADER => $status_head },\n-\t\t\tlist_modified());\n-\tprint \"\\n\";\n+\tmy @status_cmd = (\"git\", \"add--helper\", \"--status\");\n+\t!system(@status_cmd) or die \"@status_cmd exited with code $?\";\n }\n \n sub say_n_paths {\n-- \ngitgitgadget\n\n"},{"id":"367652","messageId":"ca2a7c43750c66b75681e76321fef816464b8764.1548419009.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.v4.git.gitgitgadget@gmail.com","subject":"[PATCH v4 7/7] add--interactive.perl: use add--helper --show-help for help_cmd","fromName":"Slavica Djukic via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-01-25T12:23:36Z","receivedAt":"2019-01-25T12:23:46Z","isPatch":true,"sender":{"key":"slavicadj.ip2018@gmail.com","avatar":null},"body":"From: Slavica Djukic <slawica92@hotmail.com>\n\nChange help_cmd sub in git-add--interactive.perl to use\nshow-help command from builtin add--helper.\n\nIf an error occurs, it will be reported, but the Perl script will\nnot exit, since the add--helper is called within an eval block.\n\nJust like the change where the Perl script calls the add--helper\nto print the numstat, also here we forgo adding a regression test:\nthe Perl script is on its way out (and this patch is part of that journey).\n\nSigned-off-by: Slavica Djukic <slawica92@hotmail.com>\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n git-add--interactive.perl | 12 ++----------\n 1 file changed, 2 insertions(+), 10 deletions(-)\n\ndiff --git a/git-add--interactive.perl b/git-add--interactive.perl\nindex c2c6b4d5e3..88b7be6602 100755\n--- a/git-add--interactive.perl\n+++ b/git-add--interactive.perl\n@@ -1718,16 +1718,8 @@ sub quit_cmd {\n }\n \n sub help_cmd {\n-# TRANSLATORS: please do not translate the command names\n-# 'status', 'update', 'revert', etc.\n-\tprint colored $help_color, __ <<'EOF' ;\n-status        - show paths with changes\n-update        - add working tree state to the staged set of changes\n-revert        - revert staged set of changes back to the HEAD version\n-patch         - pick hunks and update selectively\n-diff          - view diff between HEAD and index\n-add untracked - add contents of untracked files to the staged set of changes\n-EOF\n+\tmy @help_cmd = (\"git\", \"add--helper\", \"--show-help\");\n+\t!system(@help_cmd) or die \"@help_cmd exited with code $?\";\n }\n \n sub process_args {\n-- \ngitgitgadget\n"},{"id":"367654","messageId":"c7d3ce15-567a-20b5-1bde-2a7fdd88c4e5@gmail.com","threadId":"50075","inReplyTo":"pull.103.v4.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v4 0/7] Turn git add-i into built-in","fromName":"Slavica Djukic","fromEmail":"slavicadj.ip2018@gmail.com","sentAt":"2019-01-25T12:37:42Z","receivedAt":"2019-01-25T12:37:56Z","isPatch":true,"sender":{"key":"slavicadj.ip2018@gmail.com","avatar":null},"body":"Hi Phillip,\n\nSorry for omitting you in Cc, again.\nI accidentally wrote yours and Ævar's email prefixed with Cc:, rather \nthen separating\nthem with comma, like Johannes said I should.\n\n-Slavica\n\nOn 25-Jan-19 1:23 PM, Slavica Đukić via GitGitGadget wrote:\n> This is the first version of a patch series to start porting\n> git-add--interactive from Perl to C. Daniel Ferreira's patch series used as\n> a head start:\n> https://public-inbox.org/git/1494907234-28903-1-git-send-email-bnmvco@gmail.com/t/#u\n>\n> Changes since v3:\n>\n>   * add error check when calling add--helper's functions from\n>     git-add--interactive.perl\n>   * replace trailing whitespace in the test with variable $SP used in earlier\n>     tests\n>\n> Cc: Phillip Wood phillip.wood@dunelm.org.uk [phillip.wood@dunelm.org.uk]\n>\n> Daniel Ferreira (4):\n>    diff: export diffstat interface\n>    add--helper: create builtin helper for interactive add\n>    add-interactive.c: implement status command\n>    add--interactive.perl: use add--helper --status for status_cmd\n>\n> Slavica Djukic (3):\n>    add-interactive.c: implement show-help command\n>    t3701-add-interactive: test add_i_show_help()\n>    add--interactive.perl: use add--helper --show-help for help_cmd\n>\n>   .gitignore                 |   1 +\n>   Makefile                   |   2 +\n>   add-interactive.c          | 263 +++++++++++++++++++++++++++++++++++++\n>   add-interactive.h          |  10 ++\n>   builtin.h                  |   1 +\n>   builtin/add--helper.c      |  43 ++++++\n>   diff.c                     |  36 ++---\n>   diff.h                     |  18 +++\n>   git-add--interactive.perl  |  17 +--\n>   git.c                      |   1 +\n>   t/t3701-add-interactive.sh |  24 ++++\n>   11 files changed, 381 insertions(+), 35 deletions(-)\n>   create mode 100644 add-interactive.c\n>   create mode 100644 add-interactive.h\n>   create mode 100644 builtin/add--helper.c\n>\n>\n> base-commit: b21ebb671bb7dea8d342225f0d66c41f4e54d5ca\n> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-103%2FslavicaDj%2Fadd-i-v4\n> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-103/slavicaDj/add-i-v4\n> Pull-Request: https://github.com/gitgitgadget/git/pull/103\n>\n> Range-diff vs v3:\n>\n>   1:  737767b6f4 = 1:  737767b6f4 diff: export diffstat interface\n>   2:  91b1963125 = 2:  91b1963125 add--helper: create builtin helper for interactive add\n>   3:  d247ef69fe = 3:  d247ef69fe add-interactive.c: implement status command\n>   4:  4950c889aa ! 4:  fb3f9378ac add--interactive.perl: use add--helper --status for status_cmd\n>       @@ -2,12 +2,21 @@\n>        \n>            add--interactive.perl: use add--helper --status for status_cmd\n>        \n>       -    Call the newly introduced add--helper builtin on\n>       +    Call the newly introduced add--helper builtin in\n>            status_cmd() instead of relying on add--interactive's Perl\n>       -    functions to build print the numstat.\n>       +    functions to print the numstat.\n>       +\n>       +    If an error occurs, it will be reported, but the Perl script will\n>       +    not exit, since the add--helper is called within an eval block.\n>       +\n>       +    As the Perl script will go away soon, so will this scenario, where\n>       +    the built-in helper is called  from the Perl script. Combined with\n>       +    the fact that it would be hard to test, we'll pass on adding\n>       +    a regression test for this.\n>        \n>            Signed-off-by: Daniel Ferreira <bnmvco@gmail.com>\n>            Signed-off-by: Slavica Djukic <slawica92@hotmail.com>\n>       +    Signed-off-by: Ã†var ArnfjÃ¶rÃ° Bjarmason <avarab@gmail.com>\n>        \n>         diff --git a/git-add--interactive.perl b/git-add--interactive.perl\n>         --- a/git-add--interactive.perl\n>       @@ -19,7 +28,8 @@\n>        -\tlist_and_choose({ LIST_ONLY => 1, HEADER => $status_head },\n>        -\t\t\tlist_modified());\n>        -\tprint \"\\n\";\n>       -+\tsystem(qw(git add--helper --status));\n>       ++\tmy @status_cmd = (\"git\", \"add--helper\", \"--status\");\n>       ++\t!system(@status_cmd) or die \"@status_cmd exited with code $?\";\n>         }\n>         \n>         sub say_n_paths {\n>   5:  581b108c9c = 5:  ab16afd1d5 add-interactive.c: implement show-help command\n>   6:  aede733318 ! 6:  0a27304a84 t3701-add-interactive: test add_i_show_help()\n>       @@ -7,6 +7,7 @@\n>            Also, add it before changing git-add--interactive.perl's help_cmd\n>            to demonstrate that there are no changes introduced by the\n>            conversion to C.\n>       +\n>            Prefix git add -i call with GIT_PAGER_IN_USE=true TERM=vt100\n>            to force colored output on Windows.\n>        \n>       @@ -21,7 +22,7 @@\n>         \n>        +test_expect_success 'show help from add--helper' '\n>        +\tgit reset --hard &&\n>       -+\tcat >expect <<-\\EOF &&\n>       ++\tcat >expect <<-EOF &&\n>        +\n>        +\t<BOLD>*** Commands ***<RESET>\n>        +\t  1: <BOLD;BLUE>s<RESET>tatus\t  2: <BOLD;BLUE>u<RESET>pdate\t  3: <BOLD;BLUE>r<RESET>evert\t  4: <BOLD;BLUE>a<RESET>dd untracked\n>       @@ -35,7 +36,7 @@\n>        +\t<BOLD>*** Commands ***<RESET>\n>        +\t  1: <BOLD;BLUE>s<RESET>tatus\t  2: <BOLD;BLUE>u<RESET>pdate\t  3: <BOLD;BLUE>r<RESET>evert\t  4: <BOLD;BLUE>a<RESET>dd untracked\n>        +\t  5: <BOLD;BLUE>p<RESET>atch\t  6: <BOLD;BLUE>d<RESET>iff\t  7: <BOLD;BLUE>q<RESET>uit\t  8: <BOLD;BLUE>h<RESET>elp\n>       -+\t<BOLD;BLUE>What now<RESET>>\n>       ++\t<BOLD;BLUE>What now<RESET>>$SP\n>        +\tBye.\n>        +\tEOF\n>        +\ttest_write_lines h | GIT_PAGER_IN_USE=true TERM=vt100 git add -i >actual.colored &&\n>   7:  b9a1a7e37a ! 7:  ca2a7c4375 add--interactive.perl: use add--helper --show-help for help_cmd\n>       @@ -5,7 +5,15 @@\n>            Change help_cmd sub in git-add--interactive.perl to use\n>            show-help command from builtin add--helper.\n>        \n>       +    If an error occurs, it will be reported, but the Perl script will\n>       +    not exit, since the add--helper is called within an eval block.\n>       +\n>       +    Just like the change where the Perl script calls the add--helper\n>       +    to print the numstat, also here we forgo adding a regression test:\n>       +    the Perl script is on its way out (and this patch is part of that journey).\n>       +\n>            Signed-off-by: Slavica Djukic <slawica92@hotmail.com>\n>       +    Signed-off-by: Ã†var ArnfjÃ¶rÃ° Bjarmason <avarab@gmail.com>\n>        \n>         diff --git a/git-add--interactive.perl b/git-add--interactive.perl\n>         --- a/git-add--interactive.perl\n>       @@ -24,7 +32,8 @@\n>        -diff          - view diff between HEAD and index\n>        -add untracked - add contents of untracked files to the staged set of changes\n>        -EOF\n>       -+\tsystem(qw(git add--helper --show-help));\n>       ++\tmy @help_cmd = (\"git\", \"add--helper\", \"--show-help\");\n>       ++\t!system(@help_cmd) or die \"@help_cmd exited with code $?\";\n>         }\n>         \n>         sub process_args {\n>\n"},{"id":"368330","messageId":"2096b6c5-a164-f4eb-df4c-3468ff553abd@talktalk.net","threadId":"50075","inReplyTo":"c7d3ce15-567a-20b5-1bde-2a7fdd88c4e5@gmail.com","subject":"Re: [PATCH v4 0/7] Turn git add-i into built-in","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2019-02-01T14:37:05Z","receivedAt":"2019-02-01T14:37:16Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Slavica\n\nOn 25/01/2019 12:37, Slavica Djukic wrote:\n> Hi Phillip,\n> \n> Sorry for omitting you in Cc, again.\n> I accidentally wrote yours and Ævar's email prefixed with Cc:, rather \n> then separating\n> them with comma, like Johannes said I should.\n\nDon't worry, thanks for emailing here. I've looked at the range diff and \nI think your patches are good.\n\nBest Wishes\n\nPhillip\n\n> \n> -Slavica\n> \n> On 25-Jan-19 1:23 PM, Slavica Đukić via GitGitGadget wrote:\n>> This is the first version of a patch series to start porting\n>> git-add--interactive from Perl to C. Daniel Ferreira's patch series \n>> used as\n>> a head start:\n>> https://public-inbox.org/git/1494907234-28903-1-git-send-email-bnmvco@gmail.com/t/#u \n>>\n>>\n>> Changes since v3:\n>>\n>>   * add error check when calling add--helper's functions from\n>>     git-add--interactive.perl\n>>   * replace trailing whitespace in the test with variable $SP used in \n>> earlier\n>>     tests\n>>\n>> Cc: Phillip Wood phillip.wood@dunelm.org.uk [phillip.wood@dunelm.org.uk]\n>>\n>> Daniel Ferreira (4):\n>>    diff: export diffstat interface\n>>    add--helper: create builtin helper for interactive add\n>>    add-interactive.c: implement status command\n>>    add--interactive.perl: use add--helper --status for status_cmd\n>>\n>> Slavica Djukic (3):\n>>    add-interactive.c: implement show-help command\n>>    t3701-add-interactive: test add_i_show_help()\n>>    add--interactive.perl: use add--helper --show-help for help_cmd\n>>\n>>   .gitignore                 |   1 +\n>>   Makefile                   |   2 +\n>>   add-interactive.c          | 263 +++++++++++++++++++++++++++++++++++++\n>>   add-interactive.h          |  10 ++\n>>   builtin.h                  |   1 +\n>>   builtin/add--helper.c      |  43 ++++++\n>>   diff.c                     |  36 ++---\n>>   diff.h                     |  18 +++\n>>   git-add--interactive.perl  |  17 +--\n>>   git.c                      |   1 +\n>>   t/t3701-add-interactive.sh |  24 ++++\n>>   11 files changed, 381 insertions(+), 35 deletions(-)\n>>   create mode 100644 add-interactive.c\n>>   create mode 100644 add-interactive.h\n>>   create mode 100644 builtin/add--helper.c\n>>\n>>\n>> base-commit: b21ebb671bb7dea8d342225f0d66c41f4e54d5ca\n>> Published-As: \n>> https://github.com/gitgitgadget/git/releases/tag/pr-103%2FslavicaDj%2Fadd-i-v4 \n>>\n>> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git \n>> pr-103/slavicaDj/add-i-v4\n>> Pull-Request: https://github.com/gitgitgadget/git/pull/103\n>>\n>> Range-diff vs v3:\n>>\n>>   1:  737767b6f4 = 1:  737767b6f4 diff: export diffstat interface\n>>   2:  91b1963125 = 2:  91b1963125 add--helper: create builtin helper \n>> for interactive add\n>>   3:  d247ef69fe = 3:  d247ef69fe add-interactive.c: implement status \n>> command\n>>   4:  4950c889aa ! 4:  fb3f9378ac add--interactive.perl: use \n>> add--helper --status for status_cmd\n>>       @@ -2,12 +2,21 @@\n>>            add--interactive.perl: use add--helper --status for status_cmd\n>>       -    Call the newly introduced add--helper builtin on\n>>       +    Call the newly introduced add--helper builtin in\n>>            status_cmd() instead of relying on add--interactive's Perl\n>>       -    functions to build print the numstat.\n>>       +    functions to print the numstat.\n>>       +\n>>       +    If an error occurs, it will be reported, but the Perl \n>> script will\n>>       +    not exit, since the add--helper is called within an eval \n>> block.\n>>       +\n>>       +    As the Perl script will go away soon, so will this \n>> scenario, where\n>>       +    the built-in helper is called  from the Perl script. \n>> Combined with\n>>       +    the fact that it would be hard to test, we'll pass on adding\n>>       +    a regression test for this.\n>>            Signed-off-by: Daniel Ferreira <bnmvco@gmail.com>\n>>            Signed-off-by: Slavica Djukic <slawica92@hotmail.com>\n>>       +    Signed-off-by: Ã†var ArnfjÃ¶rÃ° Bjarmason <avarab@gmail.com>\n>>         diff --git a/git-add--interactive.perl \n>> b/git-add--interactive.perl\n>>         --- a/git-add--interactive.perl\n>>       @@ -19,7 +28,8 @@\n>>        -    list_and_choose({ LIST_ONLY => 1, HEADER => $status_head },\n>>        -            list_modified());\n>>        -    print \"\\n\";\n>>       -+    system(qw(git add--helper --status));\n>>       ++    my @status_cmd = (\"git\", \"add--helper\", \"--status\");\n>>       ++    !system(@status_cmd) or die \"@status_cmd exited with code \n>> $?\";\n>>         }\n>>         sub say_n_paths {\n>>   5:  581b108c9c = 5:  ab16afd1d5 add-interactive.c: implement \n>> show-help command\n>>   6:  aede733318 ! 6:  0a27304a84 t3701-add-interactive: test \n>> add_i_show_help()\n>>       @@ -7,6 +7,7 @@\n>>            Also, add it before changing git-add--interactive.perl's \n>> help_cmd\n>>            to demonstrate that there are no changes introduced by the\n>>            conversion to C.\n>>       +\n>>            Prefix git add -i call with GIT_PAGER_IN_USE=true TERM=vt100\n>>            to force colored output on Windows.\n>>       @@ -21,7 +22,7 @@\n>>        +test_expect_success 'show help from add--helper' '\n>>        +    git reset --hard &&\n>>       -+    cat >expect <<-\\EOF &&\n>>       ++    cat >expect <<-EOF &&\n>>        +\n>>        +    <BOLD>*** Commands ***<RESET>\n>>        +      1: <BOLD;BLUE>s<RESET>tatus      2: \n>> <BOLD;BLUE>u<RESET>pdate      3: <BOLD;BLUE>r<RESET>evert      4: \n>> <BOLD;BLUE>a<RESET>dd untracked\n>>       @@ -35,7 +36,7 @@\n>>        +    <BOLD>*** Commands ***<RESET>\n>>        +      1: <BOLD;BLUE>s<RESET>tatus      2: \n>> <BOLD;BLUE>u<RESET>pdate      3: <BOLD;BLUE>r<RESET>evert      4: \n>> <BOLD;BLUE>a<RESET>dd untracked\n>>        +      5: <BOLD;BLUE>p<RESET>atch      6: \n>> <BOLD;BLUE>d<RESET>iff      7: <BOLD;BLUE>q<RESET>uit      8: \n>> <BOLD;BLUE>h<RESET>elp\n>>       -+    <BOLD;BLUE>What now<RESET>>\n>>       ++    <BOLD;BLUE>What now<RESET>>$SP\n>>        +    Bye.\n>>        +    EOF\n>>        +    test_write_lines h | GIT_PAGER_IN_USE=true TERM=vt100 git \n>> add -i >actual.colored &&\n>>   7:  b9a1a7e37a ! 7:  ca2a7c4375 add--interactive.perl: use \n>> add--helper --show-help for help_cmd\n>>       @@ -5,7 +5,15 @@\n>>            Change help_cmd sub in git-add--interactive.perl to use\n>>            show-help command from builtin add--helper.\n>>       +    If an error occurs, it will be reported, but the Perl \n>> script will\n>>       +    not exit, since the add--helper is called within an eval \n>> block.\n>>       +\n>>       +    Just like the change where the Perl script calls the \n>> add--helper\n>>       +    to print the numstat, also here we forgo adding a \n>> regression test:\n>>       +    the Perl script is on its way out (and this patch is part \n>> of that journey).\n>>       +\n>>            Signed-off-by: Slavica Djukic <slawica92@hotmail.com>\n>>       +    Signed-off-by: Ã†var ArnfjÃ¶rÃ° Bjarmason <avarab@gmail.com>\n>>         diff --git a/git-add--interactive.perl \n>> b/git-add--interactive.perl\n>>         --- a/git-add--interactive.perl\n>>       @@ -24,7 +32,8 @@\n>>        -diff          - view diff between HEAD and index\n>>        -add untracked - add contents of untracked files to the staged \n>> set of changes\n>>        -EOF\n>>       -+    system(qw(git add--helper --show-help));\n>>       ++    my @help_cmd = (\"git\", \"add--helper\", \"--show-help\");\n>>       ++    !system(@help_cmd) or die \"@help_cmd exited with code $?\";\n>>         }\n>>         sub process_args {\n>>\n\n"},{"id":"369704","messageId":"d839f0c082ece5298647f8dfbfd0566d59c58172.1550662887.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.v5.git.gitgitgadget@gmail.com","subject":"[PATCH v5 01/10] diff: export diffstat interface","fromName":"Daniel Ferreira via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-02-20T11:41:29Z","receivedAt":"2019-02-20T11:41:33Z","isPatch":true,"sender":{"key":"bnmvco@gmail.com","avatar":"https://avatars.githubusercontent.com/u/735397?v=4"},"body":"From: Daniel Ferreira <bnmvco@gmail.com>\n\nMake the diffstat interface (namely, the diffstat_t struct and\ncompute_diffstat) no longer be internal to diff.c and allow it to be used\nby other parts of git.\n\nThis is helpful for code that may want to easily extract information\nfrom files using the diff machinery, while flushing it differently from\nhow the show_* functions used by diff_flush() do it. One example is the\nbuiltin implementation of git-add--interactive's status.\n\nMentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\nSigned-off-by: Daniel Ferreira <bnmvco@gmail.com>\nSigned-off-by: Slavica Djukic <slawica92@hotmail.com>\n---\n diff.c | 36 ++++++++++++++----------------------\n diff.h | 18 ++++++++++++++++++\n 2 files changed, 32 insertions(+), 22 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex 5306c48652..e290a95f4b 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -2489,22 +2489,6 @@ static void pprint_rename(struct strbuf *name, const char *a, const char *b)\n \t}\n }\n \n-struct diffstat_t {\n-\tint nr;\n-\tint alloc;\n-\tstruct diffstat_file {\n-\t\tchar *from_name;\n-\t\tchar *name;\n-\t\tchar *print_name;\n-\t\tconst char *comments;\n-\t\tunsigned is_unmerged:1;\n-\t\tunsigned is_binary:1;\n-\t\tunsigned is_renamed:1;\n-\t\tunsigned is_interesting:1;\n-\t\tuintmax_t added, deleted;\n-\t} **files;\n-};\n-\n static struct diffstat_file *diffstat_add(struct diffstat_t *diffstat,\n \t\t\t\t\t  const char *name_a,\n \t\t\t\t\t  const char *name_b)\n@@ -6001,12 +5985,7 @@ void diff_flush(struct diff_options *options)\n \t    dirstat_by_line) {\n \t\tstruct diffstat_t diffstat;\n \n-\t\tmemset(&diffstat, 0, sizeof(struct diffstat_t));\n-\t\tfor (i = 0; i < q->nr; i++) {\n-\t\t\tstruct diff_filepair *p = q->queue[i];\n-\t\t\tif (check_pair_status(p))\n-\t\t\t\tdiff_flush_stat(p, options, &diffstat);\n-\t\t}\n+\t\tcompute_diffstat(options, &diffstat);\n \t\tif (output_format & DIFF_FORMAT_NUMSTAT)\n \t\t\tshow_numstat(&diffstat, options);\n \t\tif (output_format & DIFF_FORMAT_DIFFSTAT)\n@@ -6306,6 +6285,19 @@ static int is_submodule_ignored(const char *path, struct diff_options *options)\n \treturn ignored;\n }\n \n+void compute_diffstat(struct diff_options *options, struct diffstat_t *diffstat)\n+{\n+\tint i;\n+\tstruct diff_queue_struct *q = &diff_queued_diff;\n+\n+\tmemset(diffstat, 0, sizeof(struct diffstat_t));\n+\tfor (i = 0; i < q->nr; i++) {\n+\t\tstruct diff_filepair *p = q->queue[i];\n+\t\tif (check_pair_status(p))\n+\t\t\tdiff_flush_stat(p, options, diffstat);\n+\t}\n+}\n+\n void diff_addremove(struct diff_options *options,\n \t\t    int addremove, unsigned mode,\n \t\t    const struct object_id *oid,\ndiff --git a/diff.h b/diff.h\nindex b512d0477a..a4a986ad6e 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -240,6 +240,22 @@ void diff_emit_submodule_error(struct diff_options *o, const char *err);\n void diff_emit_submodule_pipethrough(struct diff_options *o,\n \t\t\t\t     const char *line, int len);\n \n+struct diffstat_t {\n+\tint nr;\n+\tint alloc;\n+\tstruct diffstat_file {\n+\t\tchar *from_name;\n+\t\tchar *name;\n+\t\tchar *print_name;\n+\t\tconst char *comments;\n+\t\tunsigned is_unmerged:1;\n+\t\tunsigned is_binary:1;\n+\t\tunsigned is_renamed:1;\n+\t\tunsigned is_interesting:1;\n+\t\tuintmax_t added, deleted;\n+\t} **files;\n+};\n+\n enum color_diff {\n \tDIFF_RESET = 0,\n \tDIFF_CONTEXT = 1,\n@@ -328,6 +344,8 @@ void diff_change(struct diff_options *,\n \n struct diff_filepair *diff_unmerge(struct diff_options *, const char *path);\n \n+void compute_diffstat(struct diff_options *options, struct diffstat_t *diffstat);\n+\n #define DIFF_SETUP_REVERSE      \t1\n #define DIFF_SETUP_USE_SIZE_CACHE\t4\n \n-- \ngitgitgadget\n\n"},{"id":"369705","messageId":"304c3863b123e08536412019e881967bf01dc4a4.1550662887.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.v5.git.gitgitgadget@gmail.com","subject":"[PATCH v5 02/10] add--helper: create builtin helper for interactive add","fromName":"Daniel Ferreira via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-02-20T11:41:29Z","receivedAt":"2019-02-20T11:41:34Z","isPatch":true,"sender":{"key":"bnmvco@gmail.com","avatar":"https://avatars.githubusercontent.com/u/735397?v=4"},"body":"From: Daniel Ferreira <bnmvco@gmail.com>\n\nCreate a builtin helper for git-add--interactive, which at this point\nis not doing anything.\n\nThis is the first step in an effort to convert git-add--interactive.perl\nto a C builtin, in search for better portability, expressibility and\nperformance (specially on non-POSIX systems like Windows).\n\nAdditionally, an eventual complete port of git-add--interactive would\nremove the last \"big\" Git script to have Perl as a dependency, allowing\nmost Git users to have a NOPERL build running without big losses.\n\nMentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\nSigned-off-by: Daniel Ferreira <bnmvco@gmail.com>\nSigned-off-by: Slavica Djukic <slawica92@hotmail.com>\n---\n .gitignore            | 1 +\n Makefile              | 1 +\n builtin.h             | 1 +\n builtin/add--helper.c | 6 ++++++\n git.c                 | 1 +\n 5 files changed, 10 insertions(+)\n create mode 100644 builtin/add--helper.c\n\ndiff --git a/.gitignore b/.gitignore\nindex 7374587f9d..7b6d7681f9 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -16,6 +16,7 @@\n /git\n /git-add\n /git-add--interactive\n+/git-add--helper\n /git-am\n /git-annotate\n /git-apply\ndiff --git a/Makefile b/Makefile\nindex f0b2299172..e64e202deb 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1043,6 +1043,7 @@ LIB_OBJS += xdiff-interface.o\n LIB_OBJS += zlib.o\n \n BUILTIN_OBJS += builtin/add.o\n+BUILTIN_OBJS += builtin/add--helper.o\n BUILTIN_OBJS += builtin/am.o\n BUILTIN_OBJS += builtin/annotate.o\n BUILTIN_OBJS += builtin/apply.o\ndiff --git a/builtin.h b/builtin.h\nindex 6538932e99..dd811ef7d5 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -128,6 +128,7 @@ extern void setup_auto_pager(const char *cmd, int def);\n extern int is_builtin(const char *s);\n \n extern int cmd_add(int argc, const char **argv, const char *prefix);\n+extern int cmd_add__helper(int argc, const char **argv, const char *prefix);\n extern int cmd_am(int argc, const char **argv, const char *prefix);\n extern int cmd_annotate(int argc, const char **argv, const char *prefix);\n extern int cmd_apply(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/add--helper.c b/builtin/add--helper.c\nnew file mode 100644\nindex 0000000000..6a97f0e191\n--- /dev/null\n+++ b/builtin/add--helper.c\n@@ -0,0 +1,6 @@\n+#include \"builtin.h\"\n+\n+int cmd_add__helper(int argc, const char **argv, const char *prefix)\n+{\n+\treturn 0;\n+}\ndiff --git a/git.c b/git.c\nindex 2dd588674f..cb42591f37 100644\n--- a/git.c\n+++ b/git.c\n@@ -444,6 +444,7 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)\n \n static struct cmd_struct commands[] = {\n \t{ \"add\", cmd_add, RUN_SETUP | NEED_WORK_TREE },\n+\t{ \"add--helper\", cmd_add__helper, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"am\", cmd_am, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"annotate\", cmd_annotate, RUN_SETUP | NO_PARSEOPT },\n \t{ \"apply\", cmd_apply, RUN_SETUP_GENTLY },\n-- \ngitgitgadget\n\n"},{"id":"369706","messageId":"pull.103.v5.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.v4.git.gitgitgadget@gmail.com","subject":"[PATCH v5 00/10] Turn git add-i into built-in","fromName":"Slavica Đukić via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-02-20T11:41:27Z","receivedAt":"2019-02-20T11:41:36Z","isPatch":true,"sender":{"key":"name:Slavica Đukić","avatar":null},"body":"This is the first version of a patch series to start porting\ngit-add--interactive from Perl to C. Daniel Ferreira's patch series used as\na head start:\nhttps://public-inbox.org/git/1494907234-28903-1-git-send-email-bnmvco@gmail.com/t/#u\n\nChanges since v4:\n\n * rename print_modified to list_modifed\n * the big change was implementing list_and_choose, which resulted in code\n   refactoring, i.e. separating list_modified and status_cmd and making\n   status_cmd use both list_modified and list_and_choose\n * implement struct choice instead of struct stuff_item as main data\n   structure for list_and_choose\n * introduce list_only option and implement support for !list_only users\n * introduce highlighting of unique prefixes\n\nNote that authorship handling is slightly changed. In some of the commits, I\nused Original-patch-by instead of listing Daniel Ferreira as author.\n\nAlso, I would like to point out that my Outreachy internship is going to\nfinish on March 4 and I would really appreciate reviews before it does.\n\nDaniel Ferreira (3):\n  diff: export diffstat interface\n  add--helper: create builtin helper for interactive add\n  add--interactive.perl: use add--helper --status for status_cmd\n\nSlavica Djukic (7):\n  add-interactive.c: implement list_modified\n  add-interactive.c: implement list_and_choose\n  add-interactive.c: implement status command\n  add-interactive.c: add support for list_only option\n  add-interactive.c: implement show-help command\n  t3701-add-interactive: test add_i_show_help()\n  add--interactive.perl: use add--helper --show-help for help_cmd\n\n .gitignore                 |   1 +\n Makefile                   |   2 +\n add-interactive.c          | 819 +++++++++++++++++++++++++++++++++++++\n add-interactive.h          |  10 +\n builtin.h                  |   1 +\n builtin/add--helper.c      |  43 ++\n diff.c                     |  36 +-\n diff.h                     |  18 +\n git-add--interactive.perl  |  17 +-\n git.c                      |   1 +\n t/t3701-add-interactive.sh |  24 ++\n 11 files changed, 937 insertions(+), 35 deletions(-)\n create mode 100644 add-interactive.c\n create mode 100644 add-interactive.h\n create mode 100644 builtin/add--helper.c\n\n\nbase-commit: ca1b4116483b397e78483376296bcd23916ab553\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-103%2FslavicaDj%2Fadd-i-v5\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-103/slavicaDj/add-i-v5\nPull-Request: https://github.com/gitgitgadget/git/pull/103\n\nRange-diff vs v4:\n\n  1:  737767b6f4 !  1:  d839f0c082 diff: export diffstat interface\n     @@ -11,6 +11,7 @@\n          how the show_* functions used by diff_flush() do it. One example is the\n          builtin implementation of git-add--interactive's status.\n      \n     +    Mentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n          Signed-off-by: Daniel Ferreira <bnmvco@gmail.com>\n          Signed-off-by: Slavica Djukic <slawica92@hotmail.com>\n      \n  2:  91b1963125 !  2:  304c3863b1 add--helper: create builtin helper for interactive add\n     @@ -2,8 +2,8 @@\n      \n          add--helper: create builtin helper for interactive add\n      \n     -    Create a builtin helper for git-add--interactive, which right now is not\n     -    able to do anything.\n     +    Create a builtin helper for git-add--interactive, which at this point\n     +    is not doing anything.\n      \n          This is the first step in an effort to convert git-add--interactive.perl\n          to a C builtin, in search for better portability, expressibility and\n     @@ -13,6 +13,7 @@\n          remove the last \"big\" Git script to have Perl as a dependency, allowing\n          most Git users to have a NOPERL build running without big losses.\n      \n     +    Mentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n          Signed-off-by: Daniel Ferreira <bnmvco@gmail.com>\n          Signed-off-by: Slavica Djukic <slawica92@hotmail.com>\n      \n  3:  d247ef69fe !  3:  8790ffaa39 add-interactive.c: implement status command\n     @@ -1,17 +1,12 @@\n     -Author: Daniel Ferreira <bnmvco@gmail.com>\n     +Author: Slavica Djukic <slawica92@hotmail.com>\n      \n     -    add-interactive.c: implement status command\n     +    add-interactive.c: implement list_modified\n      \n     -    Add new files: add-interactive.c and add-interactive.h, which\n     -    will be used for implementing \"application logic\" of git add -i,\n     -    whereas add--helper.c will be used mostly for parsing the command line.\n     -    We're a bit lax with the command-line parsing, as the command is\n     -    intended to be called only by one internal user: the add--interactive script.\n     +    Implement list_modified from Perl, which will be used\n     +    by most of the *_cmd functions, including status in the\n     +    following commit.\n      \n     -    Implement add --interactive's status command in add-interactive.c and\n     -    use it in builtin add--helper.c.\n     -\n     -    It prints a numstat comparing changed files between a) the worktree and\n     +    It lists a numstat comparing changed files between a) the worktree and\n          the index; b) the index and the HEAD.\n      \n          To do so, we use run_diff_index() and run_diff_files() to get changed\n     @@ -19,11 +14,13 @@\n          combination of a hashmap and qsort() to print the result in\n          O(n) + O(n lg n) complexity.\n      \n     -    This is the first interactive add command implemented in C of those\n     -    anticipated by the previous commit, which introduced\n     -    the add--helper built-in.\n     +    Add new file: add-interactive.c which will be used for implementing\n     +    \"application logic\" of git add -i (alongside with add-interactive.h,\n     +    added in later commit), whereas add--helper.c will be used mostly\n     +    for parsing the command line.\n      \n     -    Signed-off-by: Daniel Ferreira <bnmvco@gmail.com>\n     +    Mentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n     +    Original-patch-by: Daniel Ferreira <bnmvco@gmail.com>\n          Signed-off-by: Slavica Djukic <slawica92@hotmail.com>\n      \n       diff --git a/Makefile b/Makefile\n     @@ -43,7 +40,6 @@\n       --- /dev/null\n       +++ b/add-interactive.c\n      @@\n     -+#include \"add-interactive.h\"\n      +#include \"cache.h\"\n      +#include \"commit.h\"\n      +#include \"color.h\"\n     @@ -51,8 +47,6 @@\n      +#include \"diffcore.h\"\n      +#include \"revision.h\"\n      +\n     -+#define HEADER_INDENT \"      \"\n     -+\n      +enum collection_phase {\n      +\tWORKTREE,\n      +\tINDEX\n     @@ -75,66 +69,8 @@\n      +\tstruct hashmap file_map;\n      +};\n      +\n     -+static int use_color = -1;\n     -+enum color_add_i {\n     -+\tCOLOR_PROMPT,\n     -+\tCOLOR_HEADER,\n     -+\tCOLOR_HELP,\n     -+\tCOLOR_ERROR\n     -+};\n     -+\n     -+static char colors[][COLOR_MAXLEN] = {\n     -+\tGIT_COLOR_BOLD_BLUE, /* Prompt */\n     -+\tGIT_COLOR_BOLD,      /* Header */\n     -+\tGIT_COLOR_BOLD_RED,  /* Help */\n     -+\tGIT_COLOR_BOLD_RED   /* Error */\n     -+};\n     -+\n     -+static const char *get_color(enum color_add_i ix)\n     -+{\n     -+\tif (want_color(use_color))\n     -+\t\treturn colors[ix];\n     -+\treturn \"\";\n     -+}\n     -+\n     -+static int parse_color_slot(const char *slot)\n     -+{\n     -+\tif (!strcasecmp(slot, \"prompt\"))\n     -+\t\treturn COLOR_PROMPT;\n     -+\tif (!strcasecmp(slot, \"header\"))\n     -+\t\treturn COLOR_HEADER;\n     -+\tif (!strcasecmp(slot, \"help\"))\n     -+\t\treturn COLOR_HELP;\n     -+\tif (!strcasecmp(slot, \"error\"))\n     -+\t\treturn COLOR_ERROR;\n     -+\n     -+\treturn -1;\n     -+}\n     -+\n     -+int add_i_config(const char *var,\n     -+\t\tconst char *value, void *cbdata)\n     -+{\n     -+\tconst char *name;\n     -+\n     -+\tif (!strcmp(var, \"color.interactive\")) {\n     -+\t\tuse_color = git_config_colorbool(var, value);\n     -+\t\treturn 0;\n     -+\t}\n     -+\n     -+\tif (skip_prefix(var, \"color.interactive.\", &name)) {\n     -+\t\tint slot = parse_color_slot(name);\n     -+\t\tif (slot < 0)\n     -+\t\t\treturn 0;\n     -+\t\tif (!value)\n     -+\t\t\treturn config_error_nonbool(var);\n     -+\t\treturn color_parse(value, colors[slot]);\n     -+\t}\n     -+\n     -+\treturn git_default_config(var, value, cbdata);\n     -+}\n     -+\n      +static int hash_cmp(const void *unused_cmp_data, const void *entry,\n     -+\t\t\tconst void *entry_or_key, const void *keydata)\n     ++\t\t    const void *entry_or_key, const void *keydata)\n      +{\n      +\tconst struct file_stat *e1 = entry, *e2 = entry_or_key;\n      +\tconst char *name = keydata ? keydata : e2->name;\n     @@ -151,8 +87,8 @@\n      +}\n      +\n      +static void collect_changes_cb(struct diff_queue_struct *q,\n     -+\t\t\t\t\t struct diff_options *options,\n     -+\t\t\t\t\t void *data)\n     ++\t\t\t       struct diff_options *options,\n     ++\t\t\t       void *data)\n      +{\n      +\tstruct collection_status *s = data;\n      +\tstruct diffstat_t stat = { 0 };\n     @@ -222,128 +158,73 @@\n      +\trun_diff_index(&rev, 1);\n      +}\n      +\n     -+void add_i_print_modified(void)\n     ++static int is_inital_commit(void)\n      +{\n     -+\tint i = 0;\n     -+\tstruct collection_status s;\n     -+\t/* TRANSLATORS: you can adjust this to align \"git add -i\" status menu */\n     -+\tconst char *modified_fmt = _(\"%12s %12s %s\");\n     -+\tconst char *header_color = get_color(COLOR_HEADER);\n      +\tstruct object_id sha1;\n     ++\tif (get_oid(\"HEAD\", &sha1))\n     ++\t\treturn 1;\n     ++\treturn 0;\n     ++}\n     ++\n     ++static const char *get_diff_reference(void)\n     ++{\n     ++\tif(is_inital_commit())\n     ++\t\treturn empty_tree_oid_hex();\n     ++\treturn \"HEAD\";\n     ++}\n     ++\n     ++static void filter_files(const char *filter, struct hashmap *file_map,\n     ++\t\t\t struct file_stat **files)\n     ++{\n     ++\n     ++\tfor (int i = 0; i < hashmap_get_size(file_map); i++) {\n     ++\t\tstruct file_stat *f = files[i];\n     ++\n     ++\t\tif ((!(f->worktree.added || f->worktree.deleted)) &&\n     ++\t\t   (!strcmp(filter, \"file-only\")))\n     ++\t\t\t\thashmap_remove(file_map, f, NULL);\n      +\n     ++\t\tif ((!(f->index.added || f->index.deleted)) &&\n     ++\t\t   (!strcmp(filter, \"index-only\")))\n     ++\t\t\t\thashmap_remove(file_map, f, NULL);\n     ++\t}\n     ++}\n     ++\n     ++static struct collection_status *list_modified(struct repository *r, const char *filter)\n     ++{\n     ++\tint i = 0;\n     ++\tstruct collection_status *s = xcalloc(1, sizeof(*s));\n      +\tstruct hashmap_iter iter;\n      +\tstruct file_stat **files;\n      +\tstruct file_stat *entry;\n      +\n     -+\tif (read_cache() < 0)\n     -+\t\treturn;\n     ++\tif (repo_read_index(r) < 0) {\n     ++\t\tprintf(\"\\n\");\n     ++\t\treturn NULL;\n     ++\t}\n      +\n     -+\ts.reference = !get_oid(\"HEAD\", &sha1) ? \"HEAD\": empty_tree_oid_hex();\n     -+\thashmap_init(&s.file_map, hash_cmp, NULL, 0);\n     ++\ts->reference = get_diff_reference();\n     ++\thashmap_init(&s->file_map, hash_cmp, NULL, 0);\n      +\n     -+\tcollect_changes_worktree(&s);\n     -+\tcollect_changes_index(&s);\n     ++\tcollect_changes_worktree(s);\n     ++\tcollect_changes_index(s);\n      +\n     -+\tif (hashmap_get_size(&s.file_map) < 1) {\n     ++\tif (hashmap_get_size(&s->file_map) < 1) {\n      +\t\tprintf(\"\\n\");\n     -+\t\treturn;\n     ++\t\treturn NULL;\n      +\t}\n      +\n     -+\tprintf(HEADER_INDENT);\n     -+\tcolor_fprintf(stdout, header_color, modified_fmt, _(\"staged\"),\n     -+\t\t\t_(\"unstaged\"), _(\"path\"));\n     -+\tprintf(\"\\n\");\n     -+\n     -+\thashmap_iter_init(&s.file_map, &iter);\n     ++\thashmap_iter_init(&s->file_map, &iter);\n      +\n     -+\tfiles = xcalloc(hashmap_get_size(&s.file_map), sizeof(struct file_stat *));\n     ++\tfiles = xcalloc(hashmap_get_size(&s->file_map), sizeof(struct file_stat *));\n      +\twhile ((entry = hashmap_iter_next(&iter))) {\n      +\t\tfiles[i++] = entry;\n      +\t}\n     -+\tQSORT(files, hashmap_get_size(&s.file_map), alphabetical_cmp);\n     ++\tQSORT(files, hashmap_get_size(&s->file_map), alphabetical_cmp);\n      +\n     -+\tfor (i = 0; i < hashmap_get_size(&s.file_map); i++) {\n     -+\t\tstruct file_stat *f = files[i];\n     -+\n     -+\t\tchar worktree_changes[50];\n     -+\t\tchar index_changes[50];\n     -+\n     -+\t\tif (f->worktree.added || f->worktree.deleted)\n     -+\t\t\tsnprintf(worktree_changes, 50, \"+%\"PRIuMAX\"/-%\"PRIuMAX, f->worktree.added,\n     -+\t\t\t\t\tf->worktree.deleted);\n     -+\t\telse\n     -+\t\t\tsnprintf(worktree_changes, 50, \"%s\", _(\"nothing\"));\n     -+\n     -+\t\tif (f->index.added || f->index.deleted)\n     -+\t\t\tsnprintf(index_changes, 50, \"+%\"PRIuMAX\"/-%\"PRIuMAX, f->index.added,\n     -+\t\t\t\t\tf->index.deleted);\n     -+\t\telse\n     -+\t\t\tsnprintf(index_changes, 50, \"%s\", _(\"unchanged\"));\n     -+\n     -+\t\tprintf(\" %2d: \", i + 1);\n     -+\t\tprintf(modified_fmt, index_changes, worktree_changes, f->name);\n     -+\t\tprintf(\"\\n\");\n     -+\t}\n     -+\tprintf(\"\\n\");\n     ++\tif (filter)\n     ++\t\tfilter_files(filter, &s->file_map, files);\n      +\n      +\tfree(files);\n     -+\thashmap_free(&s.file_map, 1);\n     ++\treturn s;\n      +}\n     -\n     - diff --git a/add-interactive.h b/add-interactive.h\n     - new file mode 100644\n     - --- /dev/null\n     - +++ b/add-interactive.h\n     -@@\n     -+#ifndef ADD_INTERACTIVE_H\n     -+#define ADD_INTERACTIVE_H\n     -+\n     -+int add_i_config(const char *var, const char *value, void *cbdata);\n     -+\n     -+void add_i_print_modified(void);\n     -+\n     -+#endif\n     - \\ No newline at end of file\n     -\n     - diff --git a/builtin/add--helper.c b/builtin/add--helper.c\n     - --- a/builtin/add--helper.c\n     - +++ b/builtin/add--helper.c\n     -@@\n     -+#include \"add-interactive.h\"\n     - #include \"builtin.h\"\n     -+#include \"config.h\"\n     -+#include \"revision.h\"\n     -+\n     -+static const char * const builtin_add_helper_usage[] = {\n     -+\tN_(\"git add-interactive--helper <command>\"),\n     -+\tNULL\n     -+};\n     -+\n     -+enum cmd_mode {\n     -+\tDEFAULT = 0,\n     -+\tSTATUS\n     -+};\n     - \n     - int cmd_add__helper(int argc, const char **argv, const char *prefix)\n     - {\n     -+\tenum cmd_mode mode = DEFAULT;\n     -+\n     -+\tstruct option options[] = {\n     -+\t\tOPT_CMDMODE(0, \"status\", &mode,\n     -+\t\t\t N_(\"print status information with diffstat\"), STATUS),\n     -+\t\tOPT_END()\n     -+\t};\n     -+\n     -+\tgit_config(add_i_config, NULL);\n     -+\targc = parse_options(argc, argv, NULL, options,\n     -+\t\t\t     builtin_add_helper_usage,\n     -+\t\t\t     PARSE_OPT_KEEP_ARGV0);\n     -+\n     -+\tif (mode == STATUS)\n     -+\t\tadd_i_print_modified();\n     -+\telse\n     -+\t\tusage_with_options(builtin_add_helper_usage,\n     -+\t\t\t\t   options);\n     -+\n     - \treturn 0;\n     - }\n  -:  ---------- >  4:  a97b29d274 add-interactive.c: implement list_and_choose\n  -:  ---------- >  5:  9a72aabe6c add-interactive.c: implement status command\n  4:  fb3f9378ac !  6:  883963ee6e add--interactive.perl: use add--helper --status for status_cmd\n     @@ -14,6 +14,7 @@\n          the fact that it would be hard to test, we'll pass on adding\n          a regression test for this.\n      \n     +    Mentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n          Signed-off-by: Daniel Ferreira <bnmvco@gmail.com>\n          Signed-off-by: Slavica Djukic <slawica92@hotmail.com>\n          Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n  -:  ---------- >  7:  7912f37517 add-interactive.c: add support for list_only option\n  5:  ab16afd1d5 !  8:  441321fc3d add-interactive.c: implement show-help command\n     @@ -10,31 +10,32 @@\n          handle_builtin and re-routed to the help command, without ever\n          calling cmd_add__helper().\n      \n     +    Mentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n          Signed-off-by: Slavica Djukic <slawica92@hotmail.com>\n      \n       diff --git a/add-interactive.c b/add-interactive.c\n       --- a/add-interactive.c\n       +++ b/add-interactive.c\n      @@\n     - \tfree(files);\n     - \thashmap_free(&s.file_map, 1);\n     + \tfree(s);\n     + \tfree_choices(&choices);\n       }\n      +\n      +void add_i_show_help(void)\n      +{\n      +\tconst char *help_color = get_color(COLOR_HELP);\n      +\tcolor_fprintf_ln(stdout, help_color, \"status        - %s\",\n     -+\t\t\t_(\"show paths with changes\"));\n     ++\t\t\t _(\"show paths with changes\"));\n      +\tcolor_fprintf_ln(stdout, help_color, \"update        - %s\",\n     -+\t\t\t_(\"add working tree state to the staged set of changes\"));\n     ++\t\t\t _(\"add working tree state to the staged set of changes\"));\n      +\tcolor_fprintf_ln(stdout, help_color, \"revert        - %s\",\n     -+\t\t\t_(\"revert staged set of changes back to the HEAD version\"));\n     ++\t\t\t _(\"revert staged set of changes back to the HEAD version\"));\n      +\tcolor_fprintf_ln(stdout, help_color, \"patch         - %s\",\n     -+\t\t\t_(\"pick hunks and update selectively\"));\n     ++\t\t\t _(\"pick hunks and update selectively\"));\n      +\tcolor_fprintf_ln(stdout, help_color, \"diff          - %s\",\n     -+\t\t\t_(\"view diff between HEAD and index\"));\n     ++\t\t\t _(\"view diff between HEAD and index\"));\n      +\tcolor_fprintf_ln(stdout, help_color, \"add untracked - %s\",\n     -+\t\t\t_(\"add contents of untracked files to the staged set of changes\"));\n     ++\t\t\t _(\"add contents of untracked files to the staged set of changes\"));\n      +}\n      \n       diff --git a/add-interactive.h b/add-interactive.h\n     @@ -42,13 +43,11 @@\n       +++ b/add-interactive.h\n      @@\n       \n     - void add_i_print_modified(void);\n     + void add_i_status(void);\n       \n     --#endif\n     - \\ No newline at end of file\n      +void add_i_show_help(void);\n      +\n     -+#endif\n     + #endif\n      \n       diff --git a/builtin/add--helper.c b/builtin/add--helper.c\n       --- a/builtin/add--helper.c\n     @@ -66,16 +65,16 @@\n      @@\n       \tstruct option options[] = {\n       \t\tOPT_CMDMODE(0, \"status\", &mode,\n     - \t\t\t N_(\"print status information with diffstat\"), STATUS),\n     + \t\t\t    N_(\"print status information with diffstat\"), STATUS),\n      +\t\tOPT_CMDMODE(0, \"show-help\", &mode,\n     -+\t\t\t N_(\"show help\"), HELP),\n     ++\t\t\t    N_(\"show help\"), HELP),\n       \t\tOPT_END()\n       \t};\n       \n      @@\n       \n       \tif (mode == STATUS)\n     - \t\tadd_i_print_modified();\n     + \t\tadd_i_status();\n      +\telse if (mode == HELP)\n      +\t\tadd_i_show_help();\n       \telse\n  6:  0a27304a84 !  9:  315ae8b211 t3701-add-interactive: test add_i_show_help()\n     @@ -11,6 +11,7 @@\n          Prefix git add -i call with GIT_PAGER_IN_USE=true TERM=vt100\n          to force colored output on Windows.\n      \n     +    Mentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n          Signed-off-by: Slavica Djukic <slawica92@hotmail.com>\n      \n       diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh\n  7:  ca2a7c4375 ! 10:  2b4bdce730 add--interactive.perl: use add--helper --show-help for help_cmd\n     @@ -12,6 +12,7 @@\n          to print the numstat, also here we forgo adding a regression test:\n          the Perl script is on its way out (and this patch is part of that journey).\n      \n     +    Mentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n          Signed-off-by: Slavica Djukic <slawica92@hotmail.com>\n          Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n      \n\n-- \ngitgitgadget\n"},{"id":"369707","messageId":"441321fc3dbbb8427ea7ec2663b4c21db8771c6c.1550662887.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.v5.git.gitgitgadget@gmail.com","subject":"[PATCH v5 08/10] add-interactive.c: implement show-help command","fromName":"Slavica Djukic via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-02-20T11:41:34Z","receivedAt":"2019-02-20T11:41:39Z","isPatch":true,"sender":{"key":"slavicadj.ip2018@gmail.com","avatar":null},"body":"From: Slavica Djukic <slawica92@hotmail.com>\n\nImplement show-help command in add-interactive.c and use it in\nbuiltin add--helper.c.\n\nUse command name \"show-help\" instead of \"help\": add--helper is\nbuiltin, hence add--helper --help would be intercepted by\nhandle_builtin and re-routed to the help command, without ever\ncalling cmd_add__helper().\n\nMentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\nSigned-off-by: Slavica Djukic <slawica92@hotmail.com>\n---\n add-interactive.c     | 17 +++++++++++++++++\n add-interactive.h     |  2 ++\n builtin/add--helper.c |  7 ++++++-\n 3 files changed, 25 insertions(+), 1 deletion(-)\n\ndiff --git a/add-interactive.c b/add-interactive.c\nindex 3c2e972413..ad05bf7e1f 100644\n--- a/add-interactive.c\n+++ b/add-interactive.c\n@@ -800,3 +800,20 @@ void add_i_status(void)\n \tfree(s);\n \tfree_choices(&choices);\n }\n+\n+void add_i_show_help(void)\n+{\n+\tconst char *help_color = get_color(COLOR_HELP);\n+\tcolor_fprintf_ln(stdout, help_color, \"status        - %s\",\n+\t\t\t _(\"show paths with changes\"));\n+\tcolor_fprintf_ln(stdout, help_color, \"update        - %s\",\n+\t\t\t _(\"add working tree state to the staged set of changes\"));\n+\tcolor_fprintf_ln(stdout, help_color, \"revert        - %s\",\n+\t\t\t _(\"revert staged set of changes back to the HEAD version\"));\n+\tcolor_fprintf_ln(stdout, help_color, \"patch         - %s\",\n+\t\t\t _(\"pick hunks and update selectively\"));\n+\tcolor_fprintf_ln(stdout, help_color, \"diff          - %s\",\n+\t\t\t _(\"view diff between HEAD and index\"));\n+\tcolor_fprintf_ln(stdout, help_color, \"add untracked - %s\",\n+\t\t\t _(\"add contents of untracked files to the staged set of changes\"));\n+}\ndiff --git a/add-interactive.h b/add-interactive.h\nindex 8ef3d2e82b..ddeedd3a33 100644\n--- a/add-interactive.h\n+++ b/add-interactive.h\n@@ -5,4 +5,6 @@ int add_i_config(const char *var, const char *value, void *cbdata);\n \n void add_i_status(void);\n \n+void add_i_show_help(void);\n+\n #endif\ndiff --git a/builtin/add--helper.c b/builtin/add--helper.c\nindex 464d2245f3..1fe64bc7fb 100644\n--- a/builtin/add--helper.c\n+++ b/builtin/add--helper.c\n@@ -10,7 +10,8 @@ static const char * const builtin_add_helper_usage[] = {\n \n enum cmd_mode {\n \tDEFAULT = 0,\n-\tSTATUS\n+\tSTATUS,\n+\tHELP\n };\n \n int cmd_add__helper(int argc, const char **argv, const char *prefix)\n@@ -20,6 +21,8 @@ int cmd_add__helper(int argc, const char **argv, const char *prefix)\n \tstruct option options[] = {\n \t\tOPT_CMDMODE(0, \"status\", &mode,\n \t\t\t    N_(\"print status information with diffstat\"), STATUS),\n+\t\tOPT_CMDMODE(0, \"show-help\", &mode,\n+\t\t\t    N_(\"show help\"), HELP),\n \t\tOPT_END()\n \t};\n \n@@ -30,6 +33,8 @@ int cmd_add__helper(int argc, const char **argv, const char *prefix)\n \n \tif (mode == STATUS)\n \t\tadd_i_status();\n+\telse if (mode == HELP)\n+\t\tadd_i_show_help();\n \telse\n \t\tusage_with_options(builtin_add_helper_usage,\n \t\t\t\t   options);\n-- \ngitgitgadget\n\n"},{"id":"369708","messageId":"2b4bdce7300f3b6ba70f00a9eb524f5fa1ceff8f.1550662887.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.v5.git.gitgitgadget@gmail.com","subject":"[PATCH v5 10/10] add--interactive.perl: use add--helper --show-help for help_cmd","fromName":"Slavica Djukic via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-02-20T11:41:35Z","receivedAt":"2019-02-20T11:41:41Z","isPatch":true,"sender":{"key":"slavicadj.ip2018@gmail.com","avatar":null},"body":"From: Slavica Djukic <slawica92@hotmail.com>\n\nChange help_cmd sub in git-add--interactive.perl to use\nshow-help command from builtin add--helper.\n\nIf an error occurs, it will be reported, but the Perl script will\nnot exit, since the add--helper is called within an eval block.\n\nJust like the change where the Perl script calls the add--helper\nto print the numstat, also here we forgo adding a regression test:\nthe Perl script is on its way out (and this patch is part of that journey).\n\nMentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\nSigned-off-by: Slavica Djukic <slawica92@hotmail.com>\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n git-add--interactive.perl | 12 ++----------\n 1 file changed, 2 insertions(+), 10 deletions(-)\n\ndiff --git a/git-add--interactive.perl b/git-add--interactive.perl\nindex c2c6b4d5e3..88b7be6602 100755\n--- a/git-add--interactive.perl\n+++ b/git-add--interactive.perl\n@@ -1718,16 +1718,8 @@ sub quit_cmd {\n }\n \n sub help_cmd {\n-# TRANSLATORS: please do not translate the command names\n-# 'status', 'update', 'revert', etc.\n-\tprint colored $help_color, __ <<'EOF' ;\n-status        - show paths with changes\n-update        - add working tree state to the staged set of changes\n-revert        - revert staged set of changes back to the HEAD version\n-patch         - pick hunks and update selectively\n-diff          - view diff between HEAD and index\n-add untracked - add contents of untracked files to the staged set of changes\n-EOF\n+\tmy @help_cmd = (\"git\", \"add--helper\", \"--show-help\");\n+\t!system(@help_cmd) or die \"@help_cmd exited with code $?\";\n }\n \n sub process_args {\n-- \ngitgitgadget\n"},{"id":"369709","messageId":"315ae8b2119c154b155b53085dcaa17612db2938.1550662887.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.v5.git.gitgitgadget@gmail.com","subject":"[PATCH v5 09/10] t3701-add-interactive: test add_i_show_help()","fromName":"Slavica Djukic via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-02-20T11:41:35Z","receivedAt":"2019-02-20T11:41:42Z","isPatch":true,"sender":{"key":"slavicadj.ip2018@gmail.com","avatar":null},"body":"From: Slavica Djukic <slawica92@hotmail.com>\n\nAdd test to t3701-add-interactive to verify that add_i_show_help()\noutputs expected content.\nAlso, add it before changing git-add--interactive.perl's help_cmd\nto demonstrate that there are no changes introduced by the\nconversion to C.\n\nPrefix git add -i call with GIT_PAGER_IN_USE=true TERM=vt100\nto force colored output on Windows.\n\nMentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\nSigned-off-by: Slavica Djukic <slawica92@hotmail.com>\n---\n t/t3701-add-interactive.sh | 24 ++++++++++++++++++++++++\n 1 file changed, 24 insertions(+)\n\ndiff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh\nindex 65dfbc033a..91aaef2932 100755\n--- a/t/t3701-add-interactive.sh\n+++ b/t/t3701-add-interactive.sh\n@@ -639,4 +639,28 @@ test_expect_success 'add -p patch editing works with pathological context lines'\n \ttest_cmp expected-2 actual\n '\n \n+test_expect_success 'show help from add--helper' '\n+\tgit reset --hard &&\n+\tcat >expect <<-EOF &&\n+\n+\t<BOLD>*** Commands ***<RESET>\n+\t  1: <BOLD;BLUE>s<RESET>tatus\t  2: <BOLD;BLUE>u<RESET>pdate\t  3: <BOLD;BLUE>r<RESET>evert\t  4: <BOLD;BLUE>a<RESET>dd untracked\n+\t  5: <BOLD;BLUE>p<RESET>atch\t  6: <BOLD;BLUE>d<RESET>iff\t  7: <BOLD;BLUE>q<RESET>uit\t  8: <BOLD;BLUE>h<RESET>elp\n+\t<BOLD;BLUE>What now<RESET>> <BOLD;RED>status        - show paths with changes<RESET>\n+\t<BOLD;RED>update        - add working tree state to the staged set of changes<RESET>\n+\t<BOLD;RED>revert        - revert staged set of changes back to the HEAD version<RESET>\n+\t<BOLD;RED>patch         - pick hunks and update selectively<RESET>\n+\t<BOLD;RED>diff          - view diff between HEAD and index<RESET>\n+\t<BOLD;RED>add untracked - add contents of untracked files to the staged set of changes<RESET>\n+\t<BOLD>*** Commands ***<RESET>\n+\t  1: <BOLD;BLUE>s<RESET>tatus\t  2: <BOLD;BLUE>u<RESET>pdate\t  3: <BOLD;BLUE>r<RESET>evert\t  4: <BOLD;BLUE>a<RESET>dd untracked\n+\t  5: <BOLD;BLUE>p<RESET>atch\t  6: <BOLD;BLUE>d<RESET>iff\t  7: <BOLD;BLUE>q<RESET>uit\t  8: <BOLD;BLUE>h<RESET>elp\n+\t<BOLD;BLUE>What now<RESET>>$SP\n+\tBye.\n+\tEOF\n+\ttest_write_lines h | GIT_PAGER_IN_USE=true TERM=vt100 git add -i >actual.colored &&\n+\ttest_decode_color <actual.colored >actual &&\n+\ttest_i18ncmp expect actual\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"369710","messageId":"883963ee6e9cb430a6f37822114ffe03a0550c45.1550662887.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.v5.git.gitgitgadget@gmail.com","subject":"[PATCH v5 06/10] add--interactive.perl: use add--helper --status for status_cmd","fromName":"Daniel Ferreira via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-02-20T11:41:32Z","receivedAt":"2019-02-20T11:41:43Z","isPatch":true,"sender":{"key":"bnmvco@gmail.com","avatar":"https://avatars.githubusercontent.com/u/735397?v=4"},"body":"From: Daniel Ferreira <bnmvco@gmail.com>\n\nCall the newly introduced add--helper builtin in\nstatus_cmd() instead of relying on add--interactive's Perl\nfunctions to print the numstat.\n\nIf an error occurs, it will be reported, but the Perl script will\nnot exit, since the add--helper is called within an eval block.\n\nAs the Perl script will go away soon, so will this scenario, where\nthe built-in helper is called  from the Perl script. Combined with\nthe fact that it would be hard to test, we'll pass on adding\na regression test for this.\n\nMentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\nSigned-off-by: Daniel Ferreira <bnmvco@gmail.com>\nSigned-off-by: Slavica Djukic <slawica92@hotmail.com>\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n git-add--interactive.perl | 5 ++---\n 1 file changed, 2 insertions(+), 3 deletions(-)\n\ndiff --git a/git-add--interactive.perl b/git-add--interactive.perl\nindex 20eb81cc92..c2c6b4d5e3 100755\n--- a/git-add--interactive.perl\n+++ b/git-add--interactive.perl\n@@ -597,9 +597,8 @@ sub prompt_help_cmd {\n }\n \n sub status_cmd {\n-\tlist_and_choose({ LIST_ONLY => 1, HEADER => $status_head },\n-\t\t\tlist_modified());\n-\tprint \"\\n\";\n+\tmy @status_cmd = (\"git\", \"add--helper\", \"--status\");\n+\t!system(@status_cmd) or die \"@status_cmd exited with code $?\";\n }\n \n sub say_n_paths {\n-- \ngitgitgadget\n\n"},{"id":"369711","messageId":"a97b29d2741e0e5ae8cf45b1c7cafc56afaa66ef.1550662887.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.v5.git.gitgitgadget@gmail.com","subject":"[PATCH v5 04/10] add-interactive.c: implement list_and_choose","fromName":"Slavica Djukic via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-02-20T11:41:31Z","receivedAt":"2019-02-20T11:41:43Z","isPatch":true,"sender":{"key":"slavicadj.ip2018@gmail.com","avatar":null},"body":"From: Slavica Djukic <slawica92@hotmail.com>\n\nThis is one of the two functions translated from Perl script, besides\nlist_modified that will be used by *_cmd functions (including status)\nto collect index/worktree changes, list those changes and let user\nmake a choice.\n\nAt this point, it only prints worktree and index changes, but later on\nin this patch series there will be upgraded to highlighting unique\nprefixes of list items and let user pick those items.\n\nMentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\nSigned-off-by: Slavica Djukic <slawica92@hotmail.com>\n---\n add-interactive.c | 127 ++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 127 insertions(+)\n\ndiff --git a/add-interactive.c b/add-interactive.c\nindex 7d096207d4..9a475a5d48 100644\n--- a/add-interactive.c\n+++ b/add-interactive.c\n@@ -5,6 +5,8 @@\n #include \"diffcore.h\"\n #include \"revision.h\"\n \n+#define HEADER_INDENT \"      \"\n+\n enum collection_phase {\n \tWORKTREE,\n \tINDEX\n@@ -27,6 +29,61 @@ struct collection_status {\n \tstruct hashmap file_map;\n };\n \n+struct list_and_choose_options {\n+\tint column_n;\n+\tunsigned singleton:1;\n+\tunsigned list_flat:1;\n+\tunsigned list_only:1;\n+\tunsigned list_only_file_names:1;\n+\tunsigned immediate:1;\n+\tchar *header;\n+\tconst char *prompt;\n+\tvoid (*on_eof_fn)(void);\n+};\n+\n+struct choice {\n+\tstruct hashmap_entry e;\n+\tchar type;\n+\tunion {\n+\t\tvoid (*command_fn)(void);\n+\t\tstruct {\n+\t\t\tstruct {\n+\t\t\t\tuintmax_t added, deleted;\n+\t\t\t} index, worktree;\n+\t\t} file;\n+\t} u;\n+\tsize_t prefix_length;\n+\tconst char *name;\n+};\n+\n+struct choices {\n+\tstruct choice **choices;\n+\tsize_t alloc, nr;\n+};\n+#define CHOICES_INIT { NULL, 0, 0 }\n+\n+static int use_color = -1;\n+enum color_add_i {\n+\tCOLOR_PROMPT,\n+\tCOLOR_HEADER,\n+\tCOLOR_HELP,\n+\tCOLOR_ERROR\n+};\n+\n+static char colors[][COLOR_MAXLEN] = {\n+\tGIT_COLOR_BOLD_BLUE, /* Prompt */\n+\tGIT_COLOR_BOLD,      /* Header */\n+\tGIT_COLOR_BOLD_RED,  /* Help */\n+\tGIT_COLOR_RESET      /* Reset */\n+};\n+\n+static const char *get_color(enum color_add_i ix)\n+{\n+\tif (want_color(use_color))\n+\t\treturn colors[ix];\n+\treturn \"\";\n+}\n+\n static int hash_cmp(const void *unused_cmp_data, const void *entry,\n \t\t    const void *entry_or_key, const void *keydata)\n {\n@@ -186,3 +243,73 @@ static struct collection_status *list_modified(struct repository *r, const char\n \tfree(files);\n \treturn s;\n }\n+\n+static struct choices *list_and_choose(struct choices *data,\n+\t\t\t\t       struct list_and_choose_options *opts)\n+{\n+\tif (!data)\n+\t\treturn NULL;\n+\n+\twhile (1) {\n+\t\tint last_lf = 0;\n+\n+\t\tif (opts->header) {\n+\t\t\tconst char *header_color = get_color(COLOR_HEADER);\n+\t\t\tif (!opts->list_flat)\n+\t\t\t\tprintf(HEADER_INDENT);\n+\t\t\tcolor_fprintf_ln(stdout, header_color, \"%s\", opts->header);\n+\t\t}\n+\n+\t\tfor (int i = 0; i < data->nr; i++) {\n+\t\t\tstruct choice *c = data->choices[i];\n+\t\t\tchar *print;\n+\t\t\tconst char *modified_fmt = _(\"%12s %12s %s\");\n+\t\t\tchar worktree_changes[50];\n+\t\t\tchar index_changes[50];\n+\t\t\tchar print_buf[100];\n+\n+\t\t\tprint = (char *)c->name;\n+\t\t\t\n+\t\t\tif ((data->choices[i]->type == 'f') && (!opts->list_only_file_names)) {\n+\t\t\t\tuintmax_t worktree_added = c->u.file.worktree.added;\n+\t\t\t\tuintmax_t worktree_deleted = c->u.file.worktree.deleted;\n+\t\t\t\tuintmax_t index_added = c->u.file.index.added;\n+\t\t\t\tuintmax_t index_deleted = c->u.file.index.deleted;\n+\n+\t\t\t\tif (worktree_added || worktree_deleted)\n+\t\t\t\t\tsnprintf(worktree_changes, 50, \"+%\"PRIuMAX\"/-%\"PRIuMAX,\n+\t\t\t\t\t\t worktree_added, worktree_deleted);\n+\t\t\t\telse\n+\t\t\t\t\tsnprintf(worktree_changes, 50, \"%s\", _(\"nothing\"));\n+\n+\t\t\t\tif (index_added || index_deleted)\n+\t\t\t\t\tsnprintf(index_changes, 50, \"+%\"PRIuMAX\"/-%\"PRIuMAX,\n+\t\t\t\t\t\t index_added, index_deleted);\n+\t\t\t\telse\n+\t\t\t\t\tsnprintf(index_changes, 50, \"%s\", _(\"unchanged\"));\n+\n+\t\t\t\tsnprintf(print_buf, 100, modified_fmt, index_changes,\n+\t\t\t\t\t worktree_changes, print);\n+\t\t\t\tprint = xmalloc(strlen(print_buf) + 1);\n+\t\t\t\tsnprintf(print, 100, \"%s\", print_buf);\n+\t\t\t}\n+\n+\t\t\tprintf(\" %2d: %s\", i + 1, print);\n+\n+\t\t\tif ((opts->list_flat) && ((i + 1) % (opts->column_n))) {\n+\t\t\t\tprintf(\"\\t\");\n+\t\t\t\tlast_lf = 0;\n+\t\t\t}\n+\t\t\telse {\n+\t\t\t\tprintf(\"\\n\");\n+\t\t\t\tlast_lf = 1;\n+\t\t\t}\n+\n+\t\t}\n+\n+\t\tif (!last_lf)\n+\t\t\tprintf(\"\\n\");\n+\n+\t\treturn NULL;\n+\t}\n+}\n-- \ngitgitgadget\n\n"},{"id":"369712","messageId":"7912f375170ca266a4ce0f9f755f8d8fecdf3d37.1550662887.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.v5.git.gitgitgadget@gmail.com","subject":"[PATCH v5 07/10] add-interactive.c: add support for list_only option","fromName":"Slavica Djukic via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-02-20T11:41:33Z","receivedAt":"2019-02-20T11:41:45Z","isPatch":true,"sender":{"key":"slavicadj.ip2018@gmail.com","avatar":null},"body":"From: Slavica Djukic <slawica92@hotmail.com>\n\nIf list_only option is not set, (i.e. we want to pick elements\nfrom the list, not just display them), highlight unique prefixes\nof list elements and let user make a choice as shown in\nprompt_help_cmd and singleton_prompt_help_cmd.\n\nInput that is expected from user is full line.\nAlthough that's also the case with Perl script in this\nparticular situation, there is also sub prompt_single_character,\nwhich deals with single keystroke.\n\nEver since f7a4cea25e3ee1c8f27777bc4293dca0210fa573,\nwe did not use _getch() in our code base's C code, and this would\nbe the first user. There are portability issues with _getch()\n(or getch()) that we would like to avoid.\nThat said, from now on, every other input will also be expected\nto be full line, rather than single keystroke.\n\nMentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\nSigned-off-by: Slavica Djukic <slawica92@hotmail.com>\n---\n add-interactive.c | 378 +++++++++++++++++++++++++++++++++++++++++++++-\n 1 file changed, 373 insertions(+), 5 deletions(-)\n\ndiff --git a/add-interactive.c b/add-interactive.c\nindex 6bf8a90d9d..3c2e972413 100644\n--- a/add-interactive.c\n+++ b/add-interactive.c\n@@ -70,18 +70,27 @@ struct choices {\n };\n #define CHOICES_INIT { NULL, 0, 0 }\n \n+struct prefix_entry {\n+\tstruct hashmap_entry e;\n+\tconst char *name;\n+\tsize_t prefix_length;\n+\tstruct choice *item;\n+};\n+\n static int use_color = -1;\n enum color_add_i {\n \tCOLOR_PROMPT,\n \tCOLOR_HEADER,\n \tCOLOR_HELP,\n-\tCOLOR_ERROR\n+\tCOLOR_ERROR,\n+\tCOLOR_RESET\n };\n \n static char colors[][COLOR_MAXLEN] = {\n \tGIT_COLOR_BOLD_BLUE, /* Prompt */\n \tGIT_COLOR_BOLD,      /* Header */\n \tGIT_COLOR_BOLD_RED,  /* Help */\n+\tGIT_COLOR_BOLD_RED,  /* Error */\n \tGIT_COLOR_RESET      /* Reset */\n };\n \n@@ -102,6 +111,8 @@ static int parse_color_slot(const char *slot)\n \t\treturn COLOR_HELP;\n \tif (!strcasecmp(slot, \"error\"))\n \t\treturn COLOR_ERROR;\n+\tif (!strcasecmp(slot, \"reset\"))\n+\t\treturn COLOR_RESET;\n \n \treturn -1;\n }\n@@ -288,14 +299,248 @@ static struct collection_status *list_modified(struct repository *r, const char\n \treturn s;\n }\n \n+static int map_cmp(const void *unused_cmp_data,\n+\t\t   const void *entry,\n+\t\t   const void *entry_or_key,\n+\t\t   const void *unused_keydata)\n+{\n+\tconst struct choice *a = entry;\n+\tconst struct choice *b = entry_or_key;\n+\tif((a->prefix_length == b->prefix_length) &&\n+\t  (strncmp(a->name, b->name, a->prefix_length) == 0))\n+\t\treturn 0;\n+\treturn 1;\n+}\n+\n+static struct prefix_entry *new_prefix_entry(const char *name,\n+\t\t\t\t\t     size_t prefix_length,\n+\t\t\t\t\t     struct choice *item)\n+{\n+\tstruct prefix_entry *result = xcalloc(1, sizeof(*result));\n+\tresult->name = name;\n+\tresult->prefix_length = prefix_length;\n+\tresult->item = item;\n+\thashmap_entry_init(result, memhash(name, prefix_length));\n+\treturn result;\n+}\n+\n+static void find_unique_prefixes(struct choices *data)\n+{\n+\tint soft_limit = 0;\n+\tint hard_limit = 4;\n+\tstruct hashmap map;\n+\n+\thashmap_init(&map, map_cmp, NULL, 0);\n+\n+\tfor (int i = 0; i < data->nr; i++) {\n+\t\tstruct prefix_entry *e = xcalloc(1, sizeof(*e));\n+\t\tstruct prefix_entry *e2;\n+\t\te->name = data->choices[i]->name;\n+\t\te->item = data->choices[i];\n+\n+\t\tfor (int j = soft_limit + 1; j <= hard_limit; j++) {\n+\t\t\tif (!isascii(e->name[j]))\n+\t\t\t\tbreak;\n+\n+\t\t\te->prefix_length = j;\n+\t\t\thashmap_entry_init(e, memhash(e->name, j));\n+\t\t\te2 = hashmap_get(&map, e, NULL);\n+\t\t\tif (!e2) {\n+\t\t\t\te->item->prefix_length = j;\n+\t\t\t\thashmap_add(&map, e);\n+\t\t\t\te = NULL;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\n+\t\t\tif (!e2->item) {\n+\t\t\t\tcontinue; /* non-unique prefix */\n+\t\t\t}\n+\n+\t\t\tif (j != e2->item->prefix_length)\n+\t\t\t\tBUG(\"Hashmap entry has unexpected prefix length (%\"PRIuMAX\"/ != %\"PRIuMAX\"/)\",\n+\t\t\t\t   (uintmax_t)j, (uintmax_t)e2->item->prefix_length);\n+\n+\t\t\t/* skip common prefix */\n+\t\t\tfor (j++; j <= hard_limit && e->name[j - 1]; j++) {\n+\t\t\t\tif (e->item->name[j - 1] != e2->item->name[j - 1])\n+\t\t\t\t\tbreak;\n+\t\t\t\thashmap_add(&map, new_prefix_entry(e->name, j, NULL));\n+\t\t\t}\n+\t\t\tif (j <= hard_limit && e2->name[j - 1]) {\n+\t\t\t\te2->item->prefix_length = j;\n+\t\t\t\thashmap_add(&map, new_prefix_entry(e2->name, j, e2->item));\n+\t\t\t}\n+\t\t\telse {\n+\t\t\t\te2->item->prefix_length = 0;\n+\t\t\t}\n+\t\t\te2->item = NULL;\n+\n+\t\t\tif (j <= hard_limit && e->name[j - 1]) {\n+\t\t\t\te->item->prefix_length = j;\n+\t\t\t\thashmap_add(&map, new_prefix_entry(e->name,\n+\t\t\t\t\t\t\t\t   e->item->prefix_length, e->item));\n+\t\t\t\te = NULL;\n+\t\t\t}\n+\t\t\telse\n+\t\t\t\te->item->prefix_length = 0;\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tfree(e);\n+\t}\n+}\n+\n+static int find_unique(char *string, struct choices *data)\n+{\n+\tint found = 0;\n+\tint i = 0;\n+\tint hit = 0;\n+\n+\tfor (i = 0; i < data->nr; i++) {\n+\t\tstruct choice *item = data->choices[i];\n+\t\thit = 0;\n+\t\tif (!strcmp(item->name, string))\n+\t\t\thit = 1;\n+\t\tif (hit && found)\n+\t\t\treturn 0;\n+\t\tif (hit)\n+\t\t\tfound = i + 1;\n+\t}\n+\n+\treturn found;\n+}\n+\n+/* filters out prefixes which have special meaning to list_and_choose() */\n+static int is_valid_prefix(const char *prefix)\n+{\n+\tregex_t *regex;\n+\tconst char *pattern = \"(\\\\s,)|(^-)|(^[0-9]+)\";\n+\tint is_valid = 0;\n+\n+\tregex = xmalloc(sizeof(*regex));\n+\tif (regcomp(regex, pattern, REG_EXTENDED))\n+\t\treturn 0;\n+\n+\tis_valid = prefix &&\n+\t\t   regexec(regex, prefix, 0, NULL, 0) &&\n+\t\t   strcmp(prefix, \"*\") &&\n+\t\t   strcmp(prefix, \"?\");\n+\tfree(regex);\n+\treturn is_valid;\n+}\n+\n+/* return a string with the prefix highlighted */\n+/* for now use square brackets; later might use ANSI colors (underline, bold) */\n+static char *highlight_prefix(struct choice *item)\n+{\n+\tstruct strbuf buf;\n+\tstruct strbuf prefix;\n+\tstruct strbuf remainder;\n+\tconst char *prompt_color = get_color(COLOR_PROMPT);\n+\tconst char *reset_color = get_color(COLOR_RESET);\n+\tint remainder_size = strlen(item->name) - item->prefix_length;\n+\n+\tstrbuf_init(&buf, 0);\n+\n+\tstrbuf_init(&prefix, 0);\n+\tstrbuf_add(&prefix, item->name, item->prefix_length);\n+\n+\tstrbuf_init(&remainder, 0);\n+\tstrbuf_add(&remainder, item->name + item->prefix_length,\n+\t\t   remainder_size + 1);\n+\n+\tif(!prefix.buf) {\n+\t\tstrbuf_release(&buf);\n+\t\tstrbuf_release(&prefix);\n+\t\treturn remainder.buf;\n+\t}\n+\t\n+\tif (!is_valid_prefix(prefix.buf)) {\n+\t\tstrbuf_addstr(&buf, prefix.buf);\n+\t\tstrbuf_addstr(&buf, remainder.buf);\n+\t}\n+\telse if (!use_color) {\n+\t\tstrbuf_addstr(&buf, \"[\");\n+\t\tstrbuf_addstr(&buf, prefix.buf);\n+\t\tstrbuf_addstr(&buf, \"]\");\n+\t\tstrbuf_addstr(&buf, remainder.buf);\n+\t}\n+\telse {\n+\t\tstrbuf_addstr(&buf, prompt_color);\n+\t\tstrbuf_addstr(&buf, prefix.buf);\n+\t\tstrbuf_addstr(&buf, reset_color);\n+\t\tstrbuf_addstr(&buf, remainder.buf);\n+\t}\n+\n+\tstrbuf_release(&prefix);\n+\tstrbuf_release(&remainder);\n+\n+\treturn buf.buf;\n+}\n+\n+static void singleton_prompt_help_cmd(void)\n+{\n+\tconst char *help_color = get_color(COLOR_HELP);\n+\tcolor_fprintf_ln(stdout, help_color, \"%s\", _(\"Prompt help:\"));\n+\tcolor_fprintf_ln(stdout, help_color, \"1          - %s\",\n+\t\t\t _(\"select a numbered item\"));\n+\tcolor_fprintf_ln(stdout, help_color, \"foo        - %s\",\n+\t\t\t _(\"select item based on unique prefix\"));\n+\tcolor_fprintf_ln(stdout, help_color, \"           - %s\",\n+\t\t\t _(\"(empty) select nothing\"));\n+}\n+\n+static void prompt_help_cmd(void)\n+{\n+\tconst char *help_color = get_color(COLOR_HELP);\n+\tcolor_fprintf_ln(stdout, help_color, \"%s\",\n+\t\t\t _(\"Prompt help:\"));\n+\tcolor_fprintf_ln(stdout, help_color, \"1          - %s\",\n+\t\t\t _(\"select a single item\"));\n+\tcolor_fprintf_ln(stdout, help_color, \"3-5        - %s\",\n+\t\t\t _(\"select a range of items\"));\n+\tcolor_fprintf_ln(stdout, help_color, \"2-3,6-9    - %s\",\n+\t\t\t _(\"select multiple ranges\"));\n+\tcolor_fprintf_ln(stdout, help_color, \"foo        - %s\",\n+\t\t\t _(\"select item based on unique prefix\"));\n+\tcolor_fprintf_ln(stdout, help_color, \"-...       - %s\",\n+\t\t\t _(\"unselect specified items\"));\n+\tcolor_fprintf_ln(stdout, help_color, \"*          - %s\",\n+\t\t\t _(\"choose all items\"));\n+\tcolor_fprintf_ln(stdout, help_color, \"           - %s\",\n+\t\t\t _(\"(empty) finish selecting\"));\n+}\n+\n static struct choices *list_and_choose(struct choices *data,\n \t\t\t\t       struct list_and_choose_options *opts)\n {\n-\tif (!data)\n+\tchar *chosen_choices = xcalloc(data->nr, sizeof(char *));\n+\tstruct choices *results = xcalloc(1, sizeof(*results));\n+\tint chosen_size = 0;\n+\n+\tif (!data) {\n+\t\tfree(chosen_choices);\n+\t\tfree(results);\n \t\treturn NULL;\n+\t}\n+\t\n+\tif (!opts->list_only)\n+\t\tfind_unique_prefixes(data);\n \n+top:\n \twhile (1) {\n \t\tint last_lf = 0;\n+\t\tconst char *prompt_color = get_color(COLOR_PROMPT);\n+\t\tconst char *error_color = get_color(COLOR_ERROR);\n+\t\tstruct strbuf input = STRBUF_INIT;\n+\t\tstruct strbuf choice;\n+\t\tstruct strbuf token;\n+\t\tchar *token_tmp;\n+\t\tregex_t *regex_dash_range;\n+\t\tregex_t *regex_number;\n+\t\tconst char *pattern_dash_range;\n+\t\tconst char *pattern_number;\n+\t\tconst char delim[] = \" ,\";\n \n \t\tif (opts->header) {\n \t\t\tconst char *header_color = get_color(COLOR_HEADER);\n@@ -306,13 +551,17 @@ static struct choices *list_and_choose(struct choices *data,\n \n \t\tfor (int i = 0; i < data->nr; i++) {\n \t\t\tstruct choice *c = data->choices[i];\n+\t\t\tchar chosen = chosen_choices[i]? '*' : ' ';\n \t\t\tchar *print;\n \t\t\tconst char *modified_fmt = _(\"%12s %12s %s\");\n \t\t\tchar worktree_changes[50];\n \t\t\tchar index_changes[50];\n \t\t\tchar print_buf[100];\n \n-\t\t\tprint = (char *)c->name;\n+\t\t\tif (!opts->list_only)\n+\t\t\t\tprint = highlight_prefix(data->choices[i]);\n+\t\t\telse\n+\t\t\t\tprint = (char *)c->name;\n \t\t\t\n \t\t\tif ((data->choices[i]->type == 'f') && (!opts->list_only_file_names)) {\n \t\t\t\tuintmax_t worktree_added = c->u.file.worktree.added;\n@@ -338,7 +587,7 @@ static struct choices *list_and_choose(struct choices *data,\n \t\t\t\tsnprintf(print, 100, \"%s\", print_buf);\n \t\t\t}\n \n-\t\t\tprintf(\" %2d: %s\", i + 1, print);\n+\t\t\tprintf(\"%c%2d: %s\", chosen, i + 1, print);\n \n \t\t\tif ((opts->list_flat) && ((i + 1) % (opts->column_n))) {\n \t\t\t\tprintf(\"\\t\");\n@@ -354,8 +603,126 @@ static struct choices *list_and_choose(struct choices *data,\n \t\tif (!last_lf)\n \t\t\tprintf(\"\\n\");\n \n-\t\treturn NULL;\n+\t\tif (opts->list_only)\n+\t\t\treturn NULL;\n+\n+\t\tcolor_fprintf(stdout, prompt_color, \"%s\", opts->prompt);\n+\t\tif(opts->singleton)\n+\t\t\tprintf(\"> \");\n+\t\telse\n+\t\t\tprintf(\">> \");\n+\n+\t\tfflush(stdout);\n+\t\tstrbuf_getline(&input, stdin);\n+\t\tstrbuf_trim(&input);\n+\n+\t\tif (!input.buf)\n+\t\t\tbreak;\n+\t\t\n+\t\tif (!input.buf[0]) {\n+\t\t\tprintf(\"\\n\");\n+\t\t\tif (opts->on_eof_fn)\n+\t\t\t\topts->on_eof_fn();\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tif (!strcmp(input.buf, \"?\")) {\n+\t\t\topts->singleton? singleton_prompt_help_cmd() : prompt_help_cmd();\n+\t\t\tgoto top;\n+\t\t}\n+\n+\t\ttoken_tmp = strtok(input.buf, delim);\n+\t\tstrbuf_init(&token, 0);\n+\t\tstrbuf_add(&token, token_tmp, strlen(token_tmp));\n+\n+\t\twhile (1) {\n+\t\t\tint choose = 1;\n+\t\t\tint bottom = 0, top = 0;\n+\t\t\tstrbuf_init(&choice, 0);\n+\t\t\tstrbuf_addbuf(&choice, &token);\n+\n+\t\t\t/* Input that begins with '-'; unchoose */\n+\t\t\tpattern_dash_range = \"^-\";\n+\t\t\tregex_dash_range = xmalloc(sizeof(*regex_dash_range));\n+\n+\t\t\tif (regcomp(regex_dash_range, pattern_dash_range, REG_EXTENDED))\n+\t\t\t\tBUG(\"regex compilation for pattern %s failed\",\n+\t\t\t\t   pattern_dash_range);\n+\t\t\tif (!regexec(regex_dash_range, choice.buf, 0, NULL, 0)) {\n+\t\t\t\tchoose = 0;\n+\t\t\t\t/* remove dash from input */\n+\t\t\t\tstrbuf_remove(&choice, 0, 1);\n+\t\t\t}\n+\n+\t\t\t/* A range can be specified like 5-7 or 5-. */\n+\t\t\tpattern_dash_range = \"^([0-9]+)-([0-9]*)$\";\n+\t\t\tpattern_number = \"^[0-9]+$\";\n+\t\t\tregex_number = xmalloc(sizeof(*regex_number));\n+\n+\t\t\tif (regcomp(regex_dash_range, pattern_dash_range, REG_EXTENDED))\n+\t\t\t\tBUG(\"regex compilation for pattern %s failed\",\n+\t\t\t\t   pattern_dash_range);\n+\t\t\tif (regcomp(regex_number, pattern_number, REG_EXTENDED))\n+\t\t\t\tBUG(\"regex compilation for pattern %s failed\", pattern_number);\n+\n+\t\t\tif (!regexec(regex_dash_range, choice.buf, 0, NULL, 0)) {\n+\t\t\t\tconst char delim_dash[] = \"-\";\n+\t\t\t\tchar *num = NULL;\n+\t\t\t\tnum = strtok(choice.buf, delim_dash);\n+\t\t\t\tbottom = atoi(num);\n+\t\t\t\tnum = strtok(NULL, delim_dash);\n+\t\t\t\ttop = num? atoi(num) : (1 + data->nr);\n+\t\t\t}\n+\t\t\telse if (!regexec(regex_number, choice.buf, 0, NULL, 0))\n+\t\t\t\tbottom = top = atoi(choice.buf);\n+\t\t\telse if (!strcmp(choice.buf, \"*\")) {\n+\t\t\t\tbottom = 1;\n+\t\t\t\ttop = 1 + data->nr;\n+\t\t\t}\n+\t\t\telse {\n+\t\t\t\tbottom = top = find_unique(choice.buf, data);\n+\t\t\t\tif (!bottom) {\n+\t\t\t\t\tcolor_fprintf_ln(stdout, error_color, _(\"Huh (%s)?\"), choice.buf);\n+\t\t\t\t\tgoto top;\n+\t\t\t\t}\n+\t\t\t}\n+\n+\t\t\tif (opts->singleton && bottom != top) {\n+\t\t\t\tcolor_fprintf_ln(stdout, error_color, _(\"Huh (%s)?\"), choice.buf);\n+\t\t\t\tgoto top;\n+\t\t\t}\n+\n+\t\t\tfor (int i = bottom - 1; i <= top - 1; i++) {\n+\t\t\t\tif (data->nr <= i || i < 0)\n+\t\t\t\t\tcontinue;\n+\t\t\t\tchosen_choices[i] = choose;\n+\t\t\t\tif (choose == 1)\n+\t\t\t\t\tchosen_size++;\n+\t\t\t}\n+\n+\t\t\tstrbuf_release(&token);\n+\t\t\tstrbuf_release(&choice);\n+\n+\t\t\ttoken_tmp = strtok(NULL, delim);\n+\t\t\tif (!token_tmp)\n+\t\t\t\tbreak;\n+\t\t\tstrbuf_init(&token, 0);\n+\t\t\tstrbuf_add(&token, token_tmp, strlen(token_tmp));\n+\t\t}\n+\n+\t\tif ((opts->immediate) || !(strcmp(choice.buf, \"*\")))\n+\t\t\tbreak;\n \t}\n+\n+\tfor (int i = 0; i < data->nr; i++) {\n+\t\tif (chosen_choices[i]) {\n+\t\t\tALLOC_GROW(results->choices, results->nr + 1, results->alloc);\n+\t\t\tresults->choices[results->nr++] = data->choices[i];\n+\t\t}\n+\t}\n+\n+\tfree(chosen_choices);\n+\treturn results;\n }\n \n static struct choice *make_choice(const char *name )\n@@ -412,6 +779,7 @@ void add_i_status(void)\n \tconst char *modified_fmt = _(\"%12s %12s %s\");\n \tconst char type = 'f';\n \n+\topts.list_only = 1;\n \topts.header = xmalloc(sizeof(char) * (HEADER_MAXLEN + 1));\n \tsnprintf(opts.header, HEADER_MAXLEN + 1, modified_fmt,\n \t\t _(\"staged\"), _(\"unstaged\"), _(\"path\"));\n-- \ngitgitgadget\n\n"},{"id":"369713","messageId":"9a72aabe6cf1001d373c933086a79ace29b98806.1550662887.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.v5.git.gitgitgadget@gmail.com","subject":"[PATCH v5 05/10] add-interactive.c: implement status command","fromName":"Slavica Djukic via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-02-20T11:41:32Z","receivedAt":"2019-02-20T11:41:45Z","isPatch":true,"sender":{"key":"slavicadj.ip2018@gmail.com","avatar":null},"body":"From: Slavica Djukic <slawica92@hotmail.com>\n\nImplement add --interactive's status command in add-interactive.c and\nuse it in builtin add--helper.c.\nThis is the first interactive add command implemented in C of those\nanticipated by the previous commit, which introduced\nthe add--helper built-in.\n\nImplement additional helper functions dealing with struct choice and\ncolors.\n\nUse list_modified to get the data, and then pass it to list_and_choose\nto show it in appropriate format.\n\nWe're a bit lax with the command-line parsing, as the command is\nintended to be called only by one internal user: the add--interactive script.\n\nMentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\nOriginal-patch-by: Daniel Ferreira <bnmvco@gmail.com>\nSigned-off-by: Slavica Djukic <slawica92@hotmail.com>\n---\n add-interactive.c     | 119 ++++++++++++++++++++++++++++++++++++++++++\n add-interactive.h     |   8 +++\n builtin/add--helper.c |  32 ++++++++++++\n 3 files changed, 159 insertions(+)\n create mode 100644 add-interactive.h\n\ndiff --git a/add-interactive.c b/add-interactive.c\nindex 9a475a5d48..6bf8a90d9d 100644\n--- a/add-interactive.c\n+++ b/add-interactive.c\n@@ -1,3 +1,4 @@\n+#include \"add-interactive.h\"\n #include \"cache.h\"\n #include \"commit.h\"\n #include \"color.h\"\n@@ -7,6 +8,8 @@\n \n #define HEADER_INDENT \"      \"\n \n+#define HEADER_MAXLEN 30\n+\n enum collection_phase {\n \tWORKTREE,\n \tINDEX\n@@ -29,6 +32,11 @@ struct collection_status {\n \tstruct hashmap file_map;\n };\n \n+struct command {\n+\tchar *name;\n+\tvoid (*command_fn)(void);\n+};\n+\n struct list_and_choose_options {\n \tint column_n;\n \tunsigned singleton:1;\n@@ -84,6 +92,42 @@ static const char *get_color(enum color_add_i ix)\n \treturn \"\";\n }\n \n+static int parse_color_slot(const char *slot)\n+{\n+\tif (!strcasecmp(slot, \"prompt\"))\n+\t\treturn COLOR_PROMPT;\n+\tif (!strcasecmp(slot, \"header\"))\n+\t\treturn COLOR_HEADER;\n+\tif (!strcasecmp(slot, \"help\"))\n+\t\treturn COLOR_HELP;\n+\tif (!strcasecmp(slot, \"error\"))\n+\t\treturn COLOR_ERROR;\n+\n+\treturn -1;\n+}\n+\n+int add_i_config(const char *var,\n+\t\t const char *value, void *cbdata)\n+{\n+\tconst char *name;\n+\n+\tif (!strcmp(var, \"color.interactive\")) {\n+\t\tuse_color = git_config_colorbool(var, value);\n+\t\treturn 0;\n+\t}\n+\n+\tif (skip_prefix(var, \"color.interactive.\", &name)) {\n+\t\tint slot = parse_color_slot(name);\n+\t\tif (slot < 0)\n+\t\t\treturn 0;\n+\t\tif (!value)\n+\t\t\treturn config_error_nonbool(var);\n+\t\treturn color_parse(value, colors[slot]);\n+\t}\n+\n+\treturn git_default_config(var, value, cbdata);\n+}\n+\n static int hash_cmp(const void *unused_cmp_data, const void *entry,\n \t\t    const void *entry_or_key, const void *keydata)\n {\n@@ -313,3 +357,78 @@ static struct choices *list_and_choose(struct choices *data,\n \t\treturn NULL;\n \t}\n }\n+\n+static struct choice *make_choice(const char *name )\n+{\n+\tstruct choice *choice;\n+\tFLEXPTR_ALLOC_STR(choice, name, name);\n+\treturn choice;\n+}\n+\n+static struct choice *add_choice(struct choices *choices, const char type,\n+\t\t\t\t struct file_stat *file, struct command *command)\n+{\n+\tstruct choice *choice;\n+\tswitch (type) {\n+\t\tcase 'f':\n+\t\t\tchoice = make_choice(file->name);\n+\t\t\tchoice->u.file.index.added = file->index.added;\n+\t\t\tchoice->u.file.index.deleted = file->index.deleted;\n+\t\t\tchoice->u.file.worktree.added = file->worktree.added;\n+\t\t\tchoice->u.file.worktree.deleted = file->worktree.deleted;\n+\t\t\tbreak;\n+\t\tcase 'c':\n+\t\t\tchoice = make_choice(command->name);\n+\t\t\tchoice->u.command_fn = command->command_fn;\n+\t\t\tbreak;\n+\t}\n+\tchoice->type = type;\n+\n+\tALLOC_GROW(choices->choices, choices->nr + 1, choices->alloc);\n+\tchoices->choices[choices->nr++] = choice;\n+\n+\treturn choice;\n+}\n+\n+static void free_choices(struct choices *choices)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < choices->nr; i++)\n+\t\tfree(choices->choices[i]);\n+\tfree(choices->choices);\n+\tchoices->choices = NULL;\n+\tchoices->nr = choices->alloc = 0;\n+}\n+\n+void add_i_status(void)\n+{\n+\tstruct collection_status *s;\n+\tstruct list_and_choose_options opts = { 0 };\n+\tstruct hashmap *map;\n+\tstruct hashmap_iter iter;\n+\tstruct choices choices = CHOICES_INIT;\n+\tstruct file_stat *entry;\n+\tconst char *modified_fmt = _(\"%12s %12s %s\");\n+\tconst char type = 'f';\n+\n+\topts.header = xmalloc(sizeof(char) * (HEADER_MAXLEN + 1));\n+\tsnprintf(opts.header, HEADER_MAXLEN + 1, modified_fmt,\n+\t\t _(\"staged\"), _(\"unstaged\"), _(\"path\"));\n+\n+\ts = list_modified(the_repository, NULL);\n+\tif (s == NULL)\n+\t\treturn;\n+\n+\tmap = &s->file_map;\n+\thashmap_iter_init(map, &iter);\n+\twhile ((entry = hashmap_iter_next(&iter))) {\n+\t\tadd_choice(&choices, type, entry, NULL);\n+\t}\n+\n+\tlist_and_choose(&choices, &opts);\n+\n+\thashmap_free(&s->file_map, 1);\n+\tfree(s);\n+\tfree_choices(&choices);\n+}\ndiff --git a/add-interactive.h b/add-interactive.h\nnew file mode 100644\nindex 0000000000..8ef3d2e82b\n--- /dev/null\n+++ b/add-interactive.h\n@@ -0,0 +1,8 @@\n+#ifndef ADD_INTERACTIVE_H\n+#define ADD_INTERACTIVE_H\n+\n+int add_i_config(const char *var, const char *value, void *cbdata);\n+\n+void add_i_status(void);\n+\n+#endif\ndiff --git a/builtin/add--helper.c b/builtin/add--helper.c\nindex 6a97f0e191..464d2245f3 100644\n--- a/builtin/add--helper.c\n+++ b/builtin/add--helper.c\n@@ -1,6 +1,38 @@\n+#include \"add-interactive.h\"\n #include \"builtin.h\"\n+#include \"config.h\"\n+#include \"revision.h\"\n+\n+static const char * const builtin_add_helper_usage[] = {\n+\tN_(\"git add-interactive--helper <command>\"),\n+\tNULL\n+};\n+\n+enum cmd_mode {\n+\tDEFAULT = 0,\n+\tSTATUS\n+};\n \n int cmd_add__helper(int argc, const char **argv, const char *prefix)\n {\n+\tenum cmd_mode mode = DEFAULT;\n+\n+\tstruct option options[] = {\n+\t\tOPT_CMDMODE(0, \"status\", &mode,\n+\t\t\t    N_(\"print status information with diffstat\"), STATUS),\n+\t\tOPT_END()\n+\t};\n+\n+\tgit_config(add_i_config, NULL);\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     builtin_add_helper_usage,\n+\t\t\t     PARSE_OPT_KEEP_ARGV0);\n+\n+\tif (mode == STATUS)\n+\t\tadd_i_status();\n+\telse\n+\t\tusage_with_options(builtin_add_helper_usage,\n+\t\t\t\t   options);\n+\n \treturn 0;\n }\n-- \ngitgitgadget\n\n"},{"id":"369714","messageId":"8790ffaa394603279927f9cd4c80f1d06bb5f976.1550662887.git.gitgitgadget@gmail.com","threadId":"50075","inReplyTo":"pull.103.v5.git.gitgitgadget@gmail.com","subject":"[PATCH v5 03/10] add-interactive.c: implement list_modified","fromName":"Slavica Djukic via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-02-20T11:41:30Z","receivedAt":"2019-02-20T11:41:47Z","isPatch":true,"sender":{"key":"slavicadj.ip2018@gmail.com","avatar":null},"body":"From: Slavica Djukic <slawica92@hotmail.com>\n\nImplement list_modified from Perl, which will be used\nby most of the *_cmd functions, including status in the\nfollowing commit.\n\nIt lists a numstat comparing changed files between a) the worktree and\nthe index; b) the index and the HEAD.\n\nTo do so, we use run_diff_index() and run_diff_files() to get changed\nfiles, use the diffstat API on them to get the numstat and use a\ncombination of a hashmap and qsort() to print the result in\nO(n) + O(n lg n) complexity.\n\nAdd new file: add-interactive.c which will be used for implementing\n\"application logic\" of git add -i (alongside with add-interactive.h,\nadded in later commit), whereas add--helper.c will be used mostly\nfor parsing the command line.\n\nMentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\nOriginal-patch-by: Daniel Ferreira <bnmvco@gmail.com>\nSigned-off-by: Slavica Djukic <slawica92@hotmail.com>\n---\n Makefile          |   1 +\n add-interactive.c | 188 ++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 189 insertions(+)\n create mode 100644 add-interactive.c\n\ndiff --git a/Makefile b/Makefile\nindex e64e202deb..1b3c06b633 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -847,6 +847,7 @@ LIB_H = $(shell $(FIND) . \\\n \t-name '*.h' -print)\n \n LIB_OBJS += abspath.o\n+LIB_OBJS += add-interactive.o\n LIB_OBJS += advice.o\n LIB_OBJS += alias.o\n LIB_OBJS += alloc.o\ndiff --git a/add-interactive.c b/add-interactive.c\nnew file mode 100644\nindex 0000000000..7d096207d4\n--- /dev/null\n+++ b/add-interactive.c\n@@ -0,0 +1,188 @@\n+#include \"cache.h\"\n+#include \"commit.h\"\n+#include \"color.h\"\n+#include \"config.h\"\n+#include \"diffcore.h\"\n+#include \"revision.h\"\n+\n+enum collection_phase {\n+\tWORKTREE,\n+\tINDEX\n+};\n+\n+struct file_stat {\n+\tstruct hashmap_entry ent;\n+\tstruct {\n+\t\tuintmax_t added, deleted;\n+\t} index, worktree;\n+\tchar name[FLEX_ARRAY];\n+};\n+\n+struct collection_status {\n+\tenum collection_phase phase;\n+\n+\tconst char *reference;\n+\tstruct pathspec pathspec;\n+\n+\tstruct hashmap file_map;\n+};\n+\n+static int hash_cmp(const void *unused_cmp_data, const void *entry,\n+\t\t    const void *entry_or_key, const void *keydata)\n+{\n+\tconst struct file_stat *e1 = entry, *e2 = entry_or_key;\n+\tconst char *name = keydata ? keydata : e2->name;\n+\n+\treturn strcmp(e1->name, name);\n+}\n+\n+static int alphabetical_cmp(const void *a, const void *b)\n+{\n+\tstruct file_stat *f1 = *((struct file_stat **)a);\n+\tstruct file_stat *f2 = *((struct file_stat **)b);\n+\n+\treturn strcmp(f1->name, f2->name);\n+}\n+\n+static void collect_changes_cb(struct diff_queue_struct *q,\n+\t\t\t       struct diff_options *options,\n+\t\t\t       void *data)\n+{\n+\tstruct collection_status *s = data;\n+\tstruct diffstat_t stat = { 0 };\n+\tint i;\n+\n+\tif (!q->nr)\n+\t\treturn;\n+\n+\tcompute_diffstat(options, &stat);\n+\n+\tfor (i = 0; i < stat.nr; i++) {\n+\t\tstruct file_stat *entry;\n+\t\tconst char *name = stat.files[i]->name;\n+\t\tunsigned int hash = strhash(name);\n+\n+\t\tentry = hashmap_get_from_hash(&s->file_map, hash, name);\n+\t\tif (!entry) {\n+\t\t\tFLEX_ALLOC_STR(entry, name, name);\n+\t\t\thashmap_entry_init(entry, hash);\n+\t\t\thashmap_add(&s->file_map, entry);\n+\t\t}\n+\n+\t\tif (s->phase == WORKTREE) {\n+\t\t\tentry->worktree.added = stat.files[i]->added;\n+\t\t\tentry->worktree.deleted = stat.files[i]->deleted;\n+\t\t} else if (s->phase == INDEX) {\n+\t\t\tentry->index.added = stat.files[i]->added;\n+\t\t\tentry->index.deleted = stat.files[i]->deleted;\n+\t\t}\n+\t}\n+}\n+\n+static void collect_changes_worktree(struct collection_status *s)\n+{\n+\tstruct rev_info rev;\n+\n+\ts->phase = WORKTREE;\n+\n+\tinit_revisions(&rev, NULL);\n+\tsetup_revisions(0, NULL, &rev, NULL);\n+\n+\trev.max_count = 0;\n+\n+\trev.diffopt.flags.ignore_dirty_submodules = 1;\n+\trev.diffopt.output_format = DIFF_FORMAT_CALLBACK;\n+\trev.diffopt.format_callback = collect_changes_cb;\n+\trev.diffopt.format_callback_data = s;\n+\n+\trun_diff_files(&rev, 0);\n+}\n+\n+static void collect_changes_index(struct collection_status *s)\n+{\n+\tstruct rev_info rev;\n+\tstruct setup_revision_opt opt = { 0 };\n+\n+\ts->phase = INDEX;\n+\n+\tinit_revisions(&rev, NULL);\n+\topt.def = s->reference;\n+\tsetup_revisions(0, NULL, &rev, &opt);\n+\n+\trev.diffopt.output_format = DIFF_FORMAT_CALLBACK;\n+\trev.diffopt.format_callback = collect_changes_cb;\n+\trev.diffopt.format_callback_data = s;\n+\n+\trun_diff_index(&rev, 1);\n+}\n+\n+static int is_inital_commit(void)\n+{\n+\tstruct object_id sha1;\n+\tif (get_oid(\"HEAD\", &sha1))\n+\t\treturn 1;\n+\treturn 0;\n+}\n+\n+static const char *get_diff_reference(void)\n+{\n+\tif(is_inital_commit())\n+\t\treturn empty_tree_oid_hex();\n+\treturn \"HEAD\";\n+}\n+\n+static void filter_files(const char *filter, struct hashmap *file_map,\n+\t\t\t struct file_stat **files)\n+{\n+\n+\tfor (int i = 0; i < hashmap_get_size(file_map); i++) {\n+\t\tstruct file_stat *f = files[i];\n+\n+\t\tif ((!(f->worktree.added || f->worktree.deleted)) &&\n+\t\t   (!strcmp(filter, \"file-only\")))\n+\t\t\t\thashmap_remove(file_map, f, NULL);\n+\n+\t\tif ((!(f->index.added || f->index.deleted)) &&\n+\t\t   (!strcmp(filter, \"index-only\")))\n+\t\t\t\thashmap_remove(file_map, f, NULL);\n+\t}\n+}\n+\n+static struct collection_status *list_modified(struct repository *r, const char *filter)\n+{\n+\tint i = 0;\n+\tstruct collection_status *s = xcalloc(1, sizeof(*s));\n+\tstruct hashmap_iter iter;\n+\tstruct file_stat **files;\n+\tstruct file_stat *entry;\n+\n+\tif (repo_read_index(r) < 0) {\n+\t\tprintf(\"\\n\");\n+\t\treturn NULL;\n+\t}\n+\n+\ts->reference = get_diff_reference();\n+\thashmap_init(&s->file_map, hash_cmp, NULL, 0);\n+\n+\tcollect_changes_worktree(s);\n+\tcollect_changes_index(s);\n+\n+\tif (hashmap_get_size(&s->file_map) < 1) {\n+\t\tprintf(\"\\n\");\n+\t\treturn NULL;\n+\t}\n+\n+\thashmap_iter_init(&s->file_map, &iter);\n+\n+\tfiles = xcalloc(hashmap_get_size(&s->file_map), sizeof(struct file_stat *));\n+\twhile ((entry = hashmap_iter_next(&iter))) {\n+\t\tfiles[i++] = entry;\n+\t}\n+\tQSORT(files, hashmap_get_size(&s->file_map), alphabetical_cmp);\n+\n+\tif (filter)\n+\t\tfilter_files(filter, &s->file_map, files);\n+\n+\tfree(files);\n+\treturn s;\n+}\n-- \ngitgitgadget\n\n"},{"id":"369829","messageId":"xmqq4l8xuiy9.fsf@gitster-ct.c.googlers.com","threadId":"50075","inReplyTo":"d839f0c082ece5298647f8dfbfd0566d59c58172.1550662887.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v5 01/10] diff: export diffstat interface","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-02-21T17:53:18Z","receivedAt":"2019-02-21T17:53:23Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Daniel Ferreira via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> @@ -6001,12 +5985,7 @@ void diff_flush(struct diff_options *options)\n>  \t    dirstat_by_line) {\n>  \t\tstruct diffstat_t diffstat;\n>  \n> -\t\tmemset(&diffstat, 0, sizeof(struct diffstat_t));\n> -\t\tfor (i = 0; i < q->nr; i++) {\n> -\t\t\tstruct diff_filepair *p = q->queue[i];\n> -\t\t\tif (check_pair_status(p))\n> -\t\t\t\tdiff_flush_stat(p, options, &diffstat);\n> -\t\t}\n> +\t\tcompute_diffstat(options, &diffstat);\n>  \t\tif (output_format & DIFF_FORMAT_NUMSTAT)\n>  \t\t\tshow_numstat(&diffstat, options);\n>  \t\tif (output_format & DIFF_FORMAT_DIFFSTAT)\n\nIn the post-context of this hunk there are series of \"if we are\nshowing this kind of diffstat, pass &diffstat to a helper that shows\nit\" calls, and this piece of code itself is guarded by \"if we are\nshowing any of these kinds of diffstat, enter this block\".  So a\nhelper function that computes necessary data in &diffstat upfront\ndoes make sense and makes the code readable quite a lot.\n\nBut...\n\n\n> +void compute_diffstat(struct diff_options *options, struct diffstat_t *diffstat)\n> +{\n> +\tint i;\n> +\tstruct diff_queue_struct *q = &diff_queued_diff;\n\n... as a reusable helper, it would make a saner API if you did not\nto hardcode the dependency to the singleton diff_queued_diff\n(i.e. instead, pass a pointer to struct diff_queue_struct as a\nparameter---the caller has it as 'q' at the callsite).\n\nOther than that, makes sense to me; thanks.\n\nHere is a meta question, which is mostly meant to those who use\ngitgitgadget@.  The person who wanted to send this copy of the patch\nthis time (i.e. Slavica, not Daniel) is not shown anywhere on the\nheader of the e-mail, so the response to the message will not go to\nSlavica at all, even though it probably is visible by monitoring\nwhere gitgitgadget@ delivers (i.e. the PR comments).\n\nIs that desirable as a reviewee?  I manually added Slavica's address\ntaken from the S-o-b: line to this response just in case, but if it\nis not desirable, I'll stop doing so.\n\nThanks.\n\n\n\n"},{"id":"369830","messageId":"xmqqzhqpt483.fsf@gitster-ct.c.googlers.com","threadId":"50075","inReplyTo":"304c3863b123e08536412019e881967bf01dc4a4.1550662887.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v5 02/10] add--helper: create builtin helper for interactive add","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-02-21T17:56:44Z","receivedAt":"2019-02-21T17:56:49Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Daniel Ferreira via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> diff --git a/git.c b/git.c\n> index 2dd588674f..cb42591f37 100644\n> --- a/git.c\n> +++ b/git.c\n> @@ -444,6 +444,7 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)\n>  \n>  static struct cmd_struct commands[] = {\n>  \t{ \"add\", cmd_add, RUN_SETUP | NEED_WORK_TREE },\n> +\t{ \"add--helper\", cmd_add__helper, RUN_SETUP | NEED_WORK_TREE },\n>  \t{ \"am\", cmd_am, RUN_SETUP | NEED_WORK_TREE },\n>  \t{ \"annotate\", cmd_annotate, RUN_SETUP | NO_PARSEOPT },\n>  \t{ \"apply\", cmd_apply, RUN_SETUP_GENTLY },\n\nWhen adding a more complex replacement command we often cannot write\nRUN_SETUP nor NEED_WORK_TREE here and instead do the job ourselves\nin cmd_ourcommand() the hard way.  But because this one is only for\nhelping \"add -i\", we can let the git.c::run_builtin() aka \"git\npotty\" do the hard part, which is very nice.\n\nMakes sense so far.\n\nThanks.\n"},{"id":"369833","messageId":"xmqqtvgxt0ze.fsf@gitster-ct.c.googlers.com","threadId":"50075","inReplyTo":"8790ffaa394603279927f9cd4c80f1d06bb5f976.1550662887.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v5 03/10] add-interactive.c: implement list_modified","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-02-21T19:06:45Z","receivedAt":"2019-02-21T19:06:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Slavica Djukic via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> diff --git a/add-interactive.c b/add-interactive.c\n> new file mode 100644\n> index 0000000000..7d096207d4\n> --- /dev/null\n> +++ b/add-interactive.c\n> @@ -0,0 +1,188 @@\n> +#include \"cache.h\"\n> +#include \"commit.h\"\n> +#include \"color.h\"\n> +#include \"config.h\"\n> +#include \"diffcore.h\"\n> +#include \"revision.h\"\n> +\n> +enum collection_phase {\n> +\tWORKTREE,\n> +\tINDEX\n> +};\n> +\n> +struct file_stat {\n> +\tstruct hashmap_entry ent;\n> +\tstruct {\n> +\t\tuintmax_t added, deleted;\n> +\t} index, worktree;\n> +\tchar name[FLEX_ARRAY];\n> +};\n> +\n> +struct collection_status {\n> +\tenum collection_phase phase;\n> +\n> +\tconst char *reference;\n> +\tstruct pathspec pathspec;\n> +\n> +\tstruct hashmap file_map;\n> +};\n> +\n> +static int hash_cmp(const void *unused_cmp_data, const void *entry,\n> +\t\t    const void *entry_or_key, const void *keydata)\n> +{\n> +\tconst struct file_stat *e1 = entry, *e2 = entry_or_key;\n> +\tconst char *name = keydata ? keydata : e2->name;\n> +\n> +\treturn strcmp(e1->name, name);\n> +}\n\nThis one is used with hashmap API, which gives unfortunate name\n\"cmp_fn\" to its callback, which does not necessarily need to\ncompare.  It only needs to say \"are they different (yes/no)?\"\nwithout having to order them (iow, it would not break anything if\nthe above is changed to return strcmp(name, e1->name)).\n\nRather than naming the callback after the name of the API that calls\nit (i.e. \"hash\"), it would make it more descriptive if it were named\nafter what is being compared for equality, in this case, pathname.\nPerhaps \"pathname_equal()\" or something?\n\n> +static int alphabetical_cmp(const void *a, const void *b)\n> +{\n> +\tstruct file_stat *f1 = *((struct file_stat **)a);\n> +\tstruct file_stat *f2 = *((struct file_stat **)b);\n> +\n> +\treturn strcmp(f1->name, f2->name);\n> +}\n\nThis one is used with qsort(), and there are two things you may want\nthe function name to tell the readers.  \"what is compared\" and \"how\nit is compared\".  Alphabetical is a bit fuzzy (different people sort\n\"a.txt\", \"A.txt\", and \"b.txt\" in different order) and it gives a\npiece of information of secondary importance (i.e. it only starts to\nmake sense after you know you are ordering pathnames), so perhaps\n\"pathname_cmp()\" or something may be a better name.\n\n> +\n> +static void collect_changes_cb(struct diff_queue_struct *q,\n> +\t\t\t       struct diff_options *options,\n> +\t\t\t       void *data)\n> +{\n> +\tstruct collection_status *s = data;\n> +\tstruct diffstat_t stat = { 0 };\n> +\tint i;\n> +\n> +\tif (!q->nr)\n> +\t\treturn;\n> +\n> +\tcompute_diffstat(options, &stat);\n\nRemember what I said on patch 01/10?  This callback function is\ndesigned to take arbitrary diff_queue_struct q as its parameter and\nwork on it, but compute_diffstat() is misdesigned and cannot work on\nanything but the singleton diff_queued_diff, which is an impedance\nmismatch that can easily be corrected by going back to 01/10 and\nfixing it.\n\n> +\tfor (i = 0; i < stat.nr; i++) {\n> +\t\tstruct file_stat *entry;\n> +\t\tconst char *name = stat.files[i]->name;\n> +\t\tunsigned int hash = strhash(name);\n> +\n> +\t\tentry = hashmap_get_from_hash(&s->file_map, hash, name);\n> +\t\tif (!entry) {\n> +\t\t\tFLEX_ALLOC_STR(entry, name, name);\n> +\t\t\thashmap_entry_init(entry, hash);\n> +\t\t\thashmap_add(&s->file_map, entry);\n> +\t\t}\n\nThe path may already be in the collection_status.file_map from the\nprevious run when \"diff-index --cached\" is run, in which case we avoid\nadding it twice, which makes sense.\n\n> +\t\tif (s->phase == WORKTREE) {\n> +\t\t\tentry->worktree.added = stat.files[i]->added;\n> +\t\t\tentry->worktree.deleted = stat.files[i]->deleted;\n> +\t\t} else if (s->phase == INDEX) {\n> +\t\t\tentry->index.added = stat.files[i]->added;\n> +\t\t\tentry->index.deleted = stat.files[i]->deleted;\n> +\t\t}\n\nAs the set of phases will not going to grow, not having the final\n'else BUG(\"phase is neither WORKTREE nor INDEX\");' here is OK.\n\nBut stepping back a bit, if we know we will not grow the phases,\nthen it may be simpler *and* equally descriptive to rename .phase\nfield to a boolean \".collecting_from_index\" that literally means\n\"are we collecting from the index?\" and that way we can also get rid\nof the enum.\n\n> +\t}\n> +}\n> +\n> +static void collect_changes_worktree(struct collection_status *s)\n> +{\n> +\tstruct rev_info rev;\n> +\n> +\ts->phase = WORKTREE;\n> +\n> +\tinit_revisions(&rev, NULL);\n> +\tsetup_revisions(0, NULL, &rev, NULL);\n> +\n> +\trev.max_count = 0;\n\nThe convention to (ab)use the max_count field to specify the\nunmerged stage, against which the working tree file is compared for\nan unmerged path, is one of the older quirks of the run_diff_files()\nAPI.  \"git diff-files\" itself when running cmd_diff_files() leaves\nrev.max_count untouched to get a normal output (as opposed to the\ncase when it is told to do --base/--ours/--theirs), so it ends up\npassing -1 in this field in such a case.\n\nThis explicit assignment to 0 must be explained with in-code comment\nwhy we want to do so.\n\n> +\trev.diffopt.flags.ignore_dirty_submodules = 1;\n\nOK, this matches what \"sub list_modified\" does.\n\n> +\trev.diffopt.output_format = DIFF_FORMAT_CALLBACK;\n> +\trev.diffopt.format_callback = collect_changes_cb;\n> +\trev.diffopt.format_callback_data = s;\n> +\trun_diff_files(&rev, 0);\n> +}\n> +\n> +static void collect_changes_index(struct collection_status *s)\n> +{\n> +\tstruct rev_info rev;\n> +\tstruct setup_revision_opt opt = { 0 };\n> +\n> +\ts->phase = INDEX;\n> +\n> +\tinit_revisions(&rev, NULL);\n> +\topt.def = s->reference;\n> +\tsetup_revisions(0, NULL, &rev, &opt);\n> +\n> +\trev.diffopt.output_format = DIFF_FORMAT_CALLBACK;\n> +\trev.diffopt.format_callback = collect_changes_cb;\n> +\trev.diffopt.format_callback_data = s;\n> +\n> +\trun_diff_index(&rev, 1);\n> +}\n> +\n> +static int is_inital_commit(void)\n> +{\n> +\tstruct object_id sha1;\n> +\tif (get_oid(\"HEAD\", &sha1))\n\nUsing the newer get_oid() API (as opposed to get_sha1() API) while\ncalling the variable that receives its result \"sha1\" is an\noxymoron.  Most users of the API call it \"oid\", I think.\n\n> +\t\treturn 1;\n> +\treturn 0;\n> +}\n\ninital???\n\nGrep for \"unborn\" in the codebase.  It probably makes sense to call\nit on_unborn_branch() instead.\n\n\tstatic int on_unborn_branch(void)\n\t{\n\t\tstruct object_id oid;\n\t\treturn !!get_oid(\"HEAD\", &oid);\n\t}\n\nEventually, the users of \"unborn\" in sequencer.c and builtin/reset.c\nmay want to share the implementation but the helper is so small that\nwe probably should not worry about it until the topic is done and\nleave it for a later clean-up.\n\n> +static const char *get_diff_reference(void)\n> +{\n> +\tif(is_inital_commit())\n\nStyle.  Do not forget SP between \"if\" and \"(\".\n\n> +\t\treturn empty_tree_oid_hex();\n> +\treturn \"HEAD\";\n> +}\n\nAlthough probably\n\n\treturn on_unborn_branch() ? empty_tree_oid_hex() : \"HEAD\";\n\nis clear enough.\n\n> +static void filter_files(const char *filter, struct hashmap *file_map,\n> +\t\t\t struct file_stat **files)\n> +{\n> +\n> +\tfor (int i = 0; i < hashmap_get_size(file_map); i++) {\n\nThe declaration of \"int i\" there is one of the C99ism we do not use,\nIIRC (\"git grep 'for (int ' \\*.c\" comes up empty).\n\n> +\t\tstruct file_stat *f = files[i];\n> +\n> +\t\tif ((!(f->worktree.added || f->worktree.deleted)) &&\n> +\t\t   (!strcmp(filter, \"file-only\")))\n> +\t\t\t\thashmap_remove(file_map, f, NULL);\n> +\n> +\t\tif ((!(f->index.added || f->index.deleted)) &&\n> +\t\t   (!strcmp(filter, \"index-only\")))\n> +\t\t\t\thashmap_remove(file_map, f, NULL);\n> +\t}\n\nAs part of the \"interactive\" machinery, obvious inefficiency in the\ncode may not matter that much, but having to make exact string\ncomparison for an unmodified parameter twice for each iteration does\nnot look like showing a good taste.\n\nWhat is the earliest time the code can decide that it is asked to do\na \"file-only\" filtering?  What I am trying to get at is that if we\nknow early enough, instead of grabbing outputs from \"diff-files\" and\n\"diff-index --cached\", only to drop such entries that do not have\noutput in \"diff-files\", we could run only \"diff-index --cached\"\nwithout spending any cycle to run \"diff-files\" in the first place.\n\nEven if such a reorganizing of the callflow is impossible (I didn't\ncheck), at least a micro optimization would be:\n\n\tint i;\n\tint file_only = !strcmp(filter, \"file-only\");\n\tint index_only = !strcmp(filter, \"index-only\");\n\n\tfor (i = 0; i < hashmap_get_size(file_map), i++) {\n\t\tstruct file_stat *f = files[i];\n\t\tif (file_only && !(f->worktree.added || f->worktree.deleted))\n\t\t\thashmap_remove(file_map, f, NULL);\n\t\t...\n\t}\n\n\n\n> +}\n> +\n> +static struct collection_status *list_modified(struct repository *r, const char *filter)\n> +{\n> +\tint i = 0;\n> +\tstruct collection_status *s = xcalloc(1, sizeof(*s));\n> +\tstruct hashmap_iter iter;\n> +\tstruct file_stat **files;\n> +\tstruct file_stat *entry;\n> +\n> +\tif (repo_read_index(r) < 0) {\n> +\t\tprintf(\"\\n\");\n> +\t\treturn NULL;\n> +\t}\n> +\n> +\ts->reference = get_diff_reference();\n> +\thashmap_init(&s->file_map, hash_cmp, NULL, 0);\n> +\n> +\tcollect_changes_worktree(s);\n> +\tcollect_changes_index(s);\n> +\n> +\tif (hashmap_get_size(&s->file_map) < 1) {\n> +\t\tprintf(\"\\n\");\n> +\t\treturn NULL;\n> +\t}\n> +\n> +\thashmap_iter_init(&s->file_map, &iter);\n> +\n> +\tfiles = xcalloc(hashmap_get_size(&s->file_map), sizeof(struct file_stat *));\n> +\twhile ((entry = hashmap_iter_next(&iter))) {\n> +\t\tfiles[i++] = entry;\n> +\t}\n> +\tQSORT(files, hashmap_get_size(&s->file_map), alphabetical_cmp);\n> +\n> +\tif (filter)\n> +\t\tfilter_files(filter, &s->file_map, files);\n\nAhh, this answers my previous question, no?  The \"filter\" parameter\ngiven to this function will never change, so we could inspect it\neven before we call collect_chagnes_{worktree,index}().  If the\ncaller says \"I want to see only the paths that appear in the\ndiff-files output\", there is no need to call collect_changes_index()\nat all, no?\n\n> +\n> +\tfree(files);\n> +\treturn s;\n> +}\n"},{"id":"369847","messageId":"xmqqef80ubsu.fsf@gitster-ct.c.googlers.com","threadId":"50075","inReplyTo":"xmqqtvgxt0ze.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v5 03/10] add-interactive.c: implement list_modified","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-02-21T20:27:45Z","receivedAt":"2019-02-21T20:27:50Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\nA few things I missed in the previous message.\n\n>> +\tfor (i = 0; i < stat.nr; i++) {\n>> +\t\tstruct file_stat *entry;\n>> +\t\tconst char *name = stat.files[i]->name;\n>> +\t\tunsigned int hash = strhash(name);\n>> +\n>> +\t\tentry = hashmap_get_from_hash(&s->file_map, hash, name);\n>> +\t\tif (!entry) {\n>> +\t\t\tFLEX_ALLOC_STR(entry, name, name);\n>> +\t\t\thashmap_entry_init(entry, hash);\n>> +\t\t\thashmap_add(&s->file_map, entry);\n>> +\t\t}\n>\n> The path may already be in the collection_status.file_map from the\n> previous run when \"diff-index --cached\" is run, in which case we avoid\n> adding it twice, which makes sense.\n>\n>> +\t\tif (s->phase == WORKTREE) {\n>> +\t\t\tentry->worktree.added = stat.files[i]->added;\n>> +\t\t\tentry->worktree.deleted = stat.files[i]->deleted;\n>> +\t\t} else if (s->phase == INDEX) {\n>> +\t\t\tentry->index.added = stat.files[i]->added;\n>> +\t\t\tentry->index.deleted = stat.files[i]->deleted;\n>> +\t\t}\n>\n> As the set of phases will not going to grow, not having the final\n> 'else BUG(\"phase is neither WORKTREE nor INDEX\");' here is OK.\n>\n> But stepping back a bit, if we know we will not grow the phases,\n> then it may be simpler *and* equally descriptive to rename .phase\n> field to a boolean \".collecting_from_index\" that literally means\n> \"are we collecting from the index?\" and that way we can also get rid\n> of the enum.\n\n... so that this can become\n\n\tif (s->collecting_from_index) {\n\t\tentry->index.added = stat.files[i]->added;\n\t\tentry->index.deleted = stat.files[i]->deleted;\n\t} else {\n\t\t...\n\t}\n\nwithout \"else if\" and without having to worry about \"what if phase\nis neither?\".\n\n> Grep for \"unborn\" in the codebase.  It probably makes sense to call\n> it on_unborn_branch() instead.\n>\n> \tstatic int on_unborn_branch(void)\n> \t{\n> \t\tstruct object_id oid;\n> \t\treturn !!get_oid(\"HEAD\", &oid);\n> \t}\n>\n> Eventually, the users of \"unborn\" in sequencer.c and builtin/reset.c\n> may want to share the implementation but the helper is so small that\n> we probably should not worry about it until the topic is done and\n> leave it for a later clean-up.\n\nAnd before such a clean-up happens, the implementation of the helper\nwould want to be improved.  \"Does 'rev-parse --verify HEAD' work?\"\nwas an easiest way to see if we are on an unborn branch from a\nscript that works most of the time, but as we are rewriting it in C,\nwe should use the more direct and correct API to see if \"HEAD\" is\na symref, and if it points at a branch that does not yet exist,\nwhich is available in the refs API as resolve_ref_unsafe().\n\nOne issue with lazy use of get_oid(\"HEAD\") is that the function\ndwims and tries to find HEAD in common hierarchies like\n.git/refs/heads/HEAD etc. when .git/HEAD does not work.  We do not\nwant such a dwimmery when seeing \"are we on an unborn branch?\".\n\n>> +static struct collection_status *list_modified(struct repository *r, const char *filter)\n>> +{\n>> +\tint i = 0;\n>> +\tstruct collection_status *s = xcalloc(1, sizeof(*s));\n>> +\tstruct hashmap_iter iter;\n>> +\tstruct file_stat **files;\n>> +\tstruct file_stat *entry;\n>> +\n>> +\tif (repo_read_index(r) < 0) {\n>> +\t\tprintf(\"\\n\");\n>> +\t\treturn NULL;\n>> +\t}\n>> +\n>> +\ts->reference = get_diff_reference();\n>> +\thashmap_init(&s->file_map, hash_cmp, NULL, 0);\n>> +\n>> +\tcollect_changes_worktree(s);\n>> +\tcollect_changes_index(s);\n>> +\n>> +\tif (hashmap_get_size(&s->file_map) < 1) {\n>> +\t\tprintf(\"\\n\");\n>> +\t\treturn NULL;\n>> +\t}\n\nThe non-error codepath of this function does not do any output, but\nwe see two \"just emit newline\" before returning NUULL to signal an\nerror to the caller\" in the above.  Such a printing from this level\nin the callchain (although we haven't seen callers of this function\nyet at this point in the series) is wrong.  Presumably, the caller, \nwhen it obtains a non-NULL 's', does something useful and maybe as a\npart of the \"useful\" thing prints something to the standard output.\nThen the caller is also responsible for handling a NULL return.  I.e.\nupon seeing such a NULL collection status, if the party that invoked\nthe caller wants to see a single empty line for whatever reason (which\nin turn is a questionable practice, if you ask me, but at this point\nin the series we haven't seen what that invoker is doing, so for now\nlets assume that it is sane to want to see an empty line), the caller\nshould do the putchar('\\n').\n\nAlso, these two \"return NULL\" leaks 's'.\n"},{"id":"369881","messageId":"xmqqef80s9t2.fsf@gitster-ct.c.googlers.com","threadId":"50075","inReplyTo":"pull.103.v5.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v5 00/10] Turn git add-i into built-in","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-02-22T04:53:45Z","receivedAt":"2019-02-22T04:53:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Slavica Đukić via GitGitGadget\"  <gitgitgadget@gmail.com> writes:\n\n> This is the first version of a patch series to start porting\n> git-add--interactive from Perl to C. Daniel Ferreira's patch series used as\n> a head start:\n> https://public-inbox.org/git/1494907234-28903-1-git-send-email-bnmvco@gmail.com/t/#u\n>\n> Changes since v4:\n>\n>  * rename print_modified to list_modifed\n>  * the big change was implementing list_and_choose, which resulted in code\n>    refactoring, i.e. separating list_modified and status_cmd and making\n>    status_cmd use both list_modified and list_and_choose\n>  * implement struct choice instead of struct stuff_item as main data\n>    structure for list_and_choose\n>  * introduce list_only option and implement support for !list_only users\n>  * introduce highlighting of unique prefixes\n>\n> Note that authorship handling is slightly changed. In some of the commits, I\n> used Original-patch-by instead of listing Daniel Ferreira as author.\n\nSounds good to me.\n\n"},{"id":"369890","messageId":"0f6b3dc7-eccf-3352-22a8-97ef052a6ada@gmail.com","threadId":"50075","inReplyTo":"xmqq4l8xuiy9.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v5 01/10] diff: export diffstat interface","fromName":"Slavica Djukic","fromEmail":"slavicadj.ip2018@gmail.com","sentAt":"2019-02-22T09:03:49Z","receivedAt":"2019-02-22T09:03:55Z","isPatch":true,"sender":{"key":"slavicadj.ip2018@gmail.com","avatar":null},"body":"Hello Junio,\n\nThank you for suggestions and for taking time to look at\nthis patch series.\n\nOn 21-Feb-19 6:53 PM, Junio C Hamano wrote:\n> \"Daniel Ferreira via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n>\n>> @@ -6001,12 +5985,7 @@ void diff_flush(struct diff_options *options)\n>>   \t    dirstat_by_line) {\n>>   \t\tstruct diffstat_t diffstat;\n>>   \n>> -\t\tmemset(&diffstat, 0, sizeof(struct diffstat_t));\n>> -\t\tfor (i = 0; i < q->nr; i++) {\n>> -\t\t\tstruct diff_filepair *p = q->queue[i];\n>> -\t\t\tif (check_pair_status(p))\n>> -\t\t\t\tdiff_flush_stat(p, options, &diffstat);\n>> -\t\t}\n>> +\t\tcompute_diffstat(options, &diffstat);\n>>   \t\tif (output_format & DIFF_FORMAT_NUMSTAT)\n>>   \t\t\tshow_numstat(&diffstat, options);\n>>   \t\tif (output_format & DIFF_FORMAT_DIFFSTAT)\n> In the post-context of this hunk there are series of \"if we are\n> showing this kind of diffstat, pass &diffstat to a helper that shows\n> it\" calls, and this piece of code itself is guarded by \"if we are\n> showing any of these kinds of diffstat, enter this block\".  So a\n> helper function that computes necessary data in &diffstat upfront\n> does make sense and makes the code readable quite a lot.\n>\n> But...\n>\n>\n>> +void compute_diffstat(struct diff_options *options, struct diffstat_t *diffstat)\n>> +{\n>> +\tint i;\n>> +\tstruct diff_queue_struct *q = &diff_queued_diff;\n> ... as a reusable helper, it would make a saner API if you did not\n> to hardcode the dependency to the singleton diff_queued_diff\n> (i.e. instead, pass a pointer to struct diff_queue_struct as a\n> parameter---the caller has it as 'q' at the callsite).\n>\n> Other than that, makes sense to me; thanks.\n>\n> Here is a meta question, which is mostly meant to those who use\n> gitgitgadget@.  The person who wanted to send this copy of the patch\n> this time (i.e. Slavica, not Daniel) is not shown anywhere on the\n> header of the e-mail, so the response to the message will not go to\n> Slavica at all, even though it probably is visible by monitoring\n> where gitgitgadget@ delivers (i.e. the PR comments).\n>\n> Is that desirable as a reviewee?  I manually added Slavica's address\n> taken from the S-o-b: line to this response just in case, but if it\n> is not desirable, I'll stop doing so.\n\n\nSince gitgitgadget puts reviewer's e-mails in PR comments and send\ne-mails (to me) as well, you don't need to add manually my address.\n\nBut thanks for thinking about this and doing it just in case\nI don't get e-mails.\n\n\n>\n> Thanks.\n>\n"},{"id":"369906","messageId":"750a1319-bf00-9cd3-088c-dae7645ffa8a@gmail.com","threadId":"50075","inReplyTo":"xmqqtvgxt0ze.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v5 03/10] add-interactive.c: implement list_modified","fromName":"Slavica Djukic","fromEmail":"slavicadj.ip2018@gmail.com","sentAt":"2019-02-22T11:35:28Z","receivedAt":"2019-02-22T11:35:35Z","isPatch":true,"sender":{"key":"slavicadj.ip2018@gmail.com","avatar":null},"body":"\nOn 21-Feb-19 8:06 PM, Junio C Hamano wrote:\n> \"Slavica Djukic via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n>\n>> diff --git a/add-interactive.c b/add-interactive.c\n>> new file mode 100644\n>> index 0000000000..7d096207d4\n>> --- /dev/null\n>> +++ b/add-interactive.c\n>> @@ -0,0 +1,188 @@\n>> +#include \"cache.h\"\n>> +#include \"commit.h\"\n>> +#include \"color.h\"\n>> +#include \"config.h\"\n>> +#include \"diffcore.h\"\n>> +#include \"revision.h\"\n>> +\n>> +enum collection_phase {\n>> +\tWORKTREE,\n>> +\tINDEX\n>> +};\n>> +\n>> +struct file_stat {\n>> +\tstruct hashmap_entry ent;\n>> +\tstruct {\n>> +\t\tuintmax_t added, deleted;\n>> +\t} index, worktree;\n>> +\tchar name[FLEX_ARRAY];\n>> +};\n>> +\n>> +struct collection_status {\n>> +\tenum collection_phase phase;\n>> +\n>> +\tconst char *reference;\n>> +\tstruct pathspec pathspec;\n>> +\n>> +\tstruct hashmap file_map;\n>> +};\n>> +\n>> +static int hash_cmp(const void *unused_cmp_data, const void *entry,\n>> +\t\t    const void *entry_or_key, const void *keydata)\n>> +{\n>> +\tconst struct file_stat *e1 = entry, *e2 = entry_or_key;\n>> +\tconst char *name = keydata ? keydata : e2->name;\n>> +\n>> +\treturn strcmp(e1->name, name);\n>> +}\n> This one is used with hashmap API, which gives unfortunate name\n> \"cmp_fn\" to its callback, which does not necessarily need to\n> compare.  It only needs to say \"are they different (yes/no)?\"\n> without having to order them (iow, it would not break anything if\n> the above is changed to return strcmp(name, e1->name)).\n>\n> Rather than naming the callback after the name of the API that calls\n> it (i.e. \"hash\"), it would make it more descriptive if it were named\n> after what is being compared for equality, in this case, pathname.\n> Perhaps \"pathname_equal()\" or something?\n>\n>> +static int alphabetical_cmp(const void *a, const void *b)\n>> +{\n>> +\tstruct file_stat *f1 = *((struct file_stat **)a);\n>> +\tstruct file_stat *f2 = *((struct file_stat **)b);\n>> +\n>> +\treturn strcmp(f1->name, f2->name);\n>> +}\n> This one is used with qsort(), and there are two things you may want\n> the function name to tell the readers.  \"what is compared\" and \"how\n> it is compared\".  Alphabetical is a bit fuzzy (different people sort\n> \"a.txt\", \"A.txt\", and \"b.txt\" in different order) and it gives a\n> piece of information of secondary importance (i.e. it only starts to\n> make sense after you know you are ordering pathnames), so perhaps\n> \"pathname_cmp()\" or something may be a better name.\n>\n>> +\n>> +static void collect_changes_cb(struct diff_queue_struct *q,\n>> +\t\t\t       struct diff_options *options,\n>> +\t\t\t       void *data)\n>> +{\n>> +\tstruct collection_status *s = data;\n>> +\tstruct diffstat_t stat = { 0 };\n>> +\tint i;\n>> +\n>> +\tif (!q->nr)\n>> +\t\treturn;\n>> +\n>> +\tcompute_diffstat(options, &stat);\n> Remember what I said on patch 01/10?  This callback function is\n> designed to take arbitrary diff_queue_struct q as its parameter and\n> work on it, but compute_diffstat() is misdesigned and cannot work on\n> anything but the singleton diff_queued_diff, which is an impedance\n> mismatch that can easily be corrected by going back to 01/10 and\n> fixing it.\n>\n>> +\tfor (i = 0; i < stat.nr; i++) {\n>> +\t\tstruct file_stat *entry;\n>> +\t\tconst char *name = stat.files[i]->name;\n>> +\t\tunsigned int hash = strhash(name);\n>> +\n>> +\t\tentry = hashmap_get_from_hash(&s->file_map, hash, name);\n>> +\t\tif (!entry) {\n>> +\t\t\tFLEX_ALLOC_STR(entry, name, name);\n>> +\t\t\thashmap_entry_init(entry, hash);\n>> +\t\t\thashmap_add(&s->file_map, entry);\n>> +\t\t}\n> The path may already be in the collection_status.file_map from the\n> previous run when \"diff-index --cached\" is run, in which case we avoid\n> adding it twice, which makes sense.\n>\n>> +\t\tif (s->phase == WORKTREE) {\n>> +\t\t\tentry->worktree.added = stat.files[i]->added;\n>> +\t\t\tentry->worktree.deleted = stat.files[i]->deleted;\n>> +\t\t} else if (s->phase == INDEX) {\n>> +\t\t\tentry->index.added = stat.files[i]->added;\n>> +\t\t\tentry->index.deleted = stat.files[i]->deleted;\n>> +\t\t}\n> As the set of phases will not going to grow, not having the final\n> 'else BUG(\"phase is neither WORKTREE nor INDEX\");' here is OK.\n>\n> But stepping back a bit, if we know we will not grow the phases,\n> then it may be simpler *and* equally descriptive to rename .phase\n> field to a boolean \".collecting_from_index\" that literally means\n> \"are we collecting from the index?\" and that way we can also get rid\n> of the enum.\n>\n>> +\t}\n>> +}\n>> +\n>> +static void collect_changes_worktree(struct collection_status *s)\n>> +{\n>> +\tstruct rev_info rev;\n>> +\n>> +\ts->phase = WORKTREE;\n>> +\n>> +\tinit_revisions(&rev, NULL);\n>> +\tsetup_revisions(0, NULL, &rev, NULL);\n>> +\n>> +\trev.max_count = 0;\n> The convention to (ab)use the max_count field to specify the\n> unmerged stage, against which the working tree file is compared for\n> an unmerged path, is one of the older quirks of the run_diff_files()\n> API.  \"git diff-files\" itself when running cmd_diff_files() leaves\n> rev.max_count untouched to get a normal output (as opposed to the\n> case when it is told to do --base/--ours/--theirs), so it ends up\n> passing -1 in this field in such a case.\n>\n> This explicit assignment to 0 must be explained with in-code comment\n> why we want to do so.\n>\n>> +\trev.diffopt.flags.ignore_dirty_submodules = 1;\n> OK, this matches what \"sub list_modified\" does.\n>\n>> +\trev.diffopt.output_format = DIFF_FORMAT_CALLBACK;\n>> +\trev.diffopt.format_callback = collect_changes_cb;\n>> +\trev.diffopt.format_callback_data = s;\n>> +\trun_diff_files(&rev, 0);\n>> +}\n>> +\n>> +static void collect_changes_index(struct collection_status *s)\n>> +{\n>> +\tstruct rev_info rev;\n>> +\tstruct setup_revision_opt opt = { 0 };\n>> +\n>> +\ts->phase = INDEX;\n>> +\n>> +\tinit_revisions(&rev, NULL);\n>> +\topt.def = s->reference;\n>> +\tsetup_revisions(0, NULL, &rev, &opt);\n>> +\n>> +\trev.diffopt.output_format = DIFF_FORMAT_CALLBACK;\n>> +\trev.diffopt.format_callback = collect_changes_cb;\n>> +\trev.diffopt.format_callback_data = s;\n>> +\n>> +\trun_diff_index(&rev, 1);\n>> +}\n>> +\n>> +static int is_inital_commit(void)\n>> +{\n>> +\tstruct object_id sha1;\n>> +\tif (get_oid(\"HEAD\", &sha1))\n> Using the newer get_oid() API (as opposed to get_sha1() API) while\n> calling the variable that receives its result \"sha1\" is an\n> oxymoron.  Most users of the API call it \"oid\", I think.\n>\n>> +\t\treturn 1;\n>> +\treturn 0;\n>> +}\n> inital???\n>\n> Grep for \"unborn\" in the codebase.  It probably makes sense to call\n> it on_unborn_branch() instead.\n>\n> \tstatic int on_unborn_branch(void)\n> \t{\n> \t\tstruct object_id oid;\n> \t\treturn !!get_oid(\"HEAD\", &oid);\n> \t}\n>\n> Eventually, the users of \"unborn\" in sequencer.c and builtin/reset.c\n> may want to share the implementation but the helper is so small that\n> we probably should not worry about it until the topic is done and\n> leave it for a later clean-up.\n>\n>> +static const char *get_diff_reference(void)\n>> +{\n>> +\tif(is_inital_commit())\n> Style.  Do not forget SP between \"if\" and \"(\".\n>\n>> +\t\treturn empty_tree_oid_hex();\n>> +\treturn \"HEAD\";\n>> +}\n> Although probably\n>\n> \treturn on_unborn_branch() ? empty_tree_oid_hex() : \"HEAD\";\n>\n> is clear enough.\n>\n>> +static void filter_files(const char *filter, struct hashmap *file_map,\n>> +\t\t\t struct file_stat **files)\n>> +{\n>> +\n>> +\tfor (int i = 0; i < hashmap_get_size(file_map); i++) {\n> The declaration of \"int i\" there is one of the C99ism we do not use,\n> IIRC (\"git grep 'for (int ' \\*.c\" comes up empty).\n>\n>> +\t\tstruct file_stat *f = files[i];\n>> +\n>> +\t\tif ((!(f->worktree.added || f->worktree.deleted)) &&\n>> +\t\t   (!strcmp(filter, \"file-only\")))\n>> +\t\t\t\thashmap_remove(file_map, f, NULL);\n>> +\n>> +\t\tif ((!(f->index.added || f->index.deleted)) &&\n>> +\t\t   (!strcmp(filter, \"index-only\")))\n>> +\t\t\t\thashmap_remove(file_map, f, NULL);\n>> +\t}\n> As part of the \"interactive\" machinery, obvious inefficiency in the\n> code may not matter that much, but having to make exact string\n> comparison for an unmodified parameter twice for each iteration does\n> not look like showing a good taste.\n>\n> What is the earliest time the code can decide that it is asked to do\n> a \"file-only\" filtering?  What I am trying to get at is that if we\n> know early enough, instead of grabbing outputs from \"diff-files\" and\n> \"diff-index --cached\", only to drop such entries that do not have\n> output in \"diff-files\", we could run only \"diff-index --cached\"\n> without spending any cycle to run \"diff-files\" in the first place.\n>\n> Even if such a reorganizing of the callflow is impossible (I didn't\n> check), at least a micro optimization would be:\n>\n> \tint i;\n> \tint file_only = !strcmp(filter, \"file-only\");\n> \tint index_only = !strcmp(filter, \"index-only\");\n>\n> \tfor (i = 0; i < hashmap_get_size(file_map), i++) {\n> \t\tstruct file_stat *f = files[i];\n> \t\tif (file_only && !(f->worktree.added || f->worktree.deleted))\n> \t\t\thashmap_remove(file_map, f, NULL);\n> \t\t...\n> \t}\n>\n>\n>\n>> +}\n>> +\n>> +static struct collection_status *list_modified(struct repository *r, const char *filter)\n>> +{\n>> +\tint i = 0;\n>> +\tstruct collection_status *s = xcalloc(1, sizeof(*s));\n>> +\tstruct hashmap_iter iter;\n>> +\tstruct file_stat **files;\n>> +\tstruct file_stat *entry;\n>> +\n>> +\tif (repo_read_index(r) < 0) {\n>> +\t\tprintf(\"\\n\");\n>> +\t\treturn NULL;\n>> +\t}\n>> +\n>> +\ts->reference = get_diff_reference();\n>> +\thashmap_init(&s->file_map, hash_cmp, NULL, 0);\n>> +\n>> +\tcollect_changes_worktree(s);\n>> +\tcollect_changes_index(s);\n>> +\n>> +\tif (hashmap_get_size(&s->file_map) < 1) {\n>> +\t\tprintf(\"\\n\");\n>> +\t\treturn NULL;\n>> +\t}\n>> +\n>> +\thashmap_iter_init(&s->file_map, &iter);\n>> +\n>> +\tfiles = xcalloc(hashmap_get_size(&s->file_map), sizeof(struct file_stat *));\n>> +\twhile ((entry = hashmap_iter_next(&iter))) {\n>> +\t\tfiles[i++] = entry;\n>> +\t}\n>> +\tQSORT(files, hashmap_get_size(&s->file_map), alphabetical_cmp);\n>> +\n>> +\tif (filter)\n>> +\t\tfilter_files(filter, &s->file_map, files);\n> Ahh, this answers my previous question, no?  The \"filter\" parameter\n> given to this function will never change, so we could inspect it\n> even before we call collect_chagnes_{worktree,index}().  If the\n> caller says \"I want to see only the paths that appear in the\n> diff-files output\", there is no need to call collect_changes_index()\n> at all, no?\n\n\nYes, this answers your previous question. The \"filter\" parameter doesn't\nchange and we can collect specific changes right away instead of collecting\nall of them and applying filter after.\n\nThanks for this one and all the other comments.\n\n\n>\n>> +\n>> +\tfree(files);\n>> +\treturn s;\n>> +}\n"},{"id":"369908","messageId":"d9fda542-3a40-068e-280f-a1ea9f678b0e@gmail.com","threadId":"50075","inReplyTo":"xmqqef80ubsu.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v5 03/10] add-interactive.c: implement list_modified","fromName":"Slavica Djukic","fromEmail":"slavicadj.ip2018@gmail.com","sentAt":"2019-02-22T12:18:01Z","receivedAt":"2019-02-22T12:18:10Z","isPatch":true,"sender":{"key":"slavicadj.ip2018@gmail.com","avatar":null},"body":"\nOn 21-Feb-19 9:27 PM, Junio C Hamano wrote:\n> Junio C Hamano <gitster@pobox.com> writes:\n>\n> A few things I missed in the previous message.\n>\n>>> +\tfor (i = 0; i < stat.nr; i++) {\n>>> +\t\tstruct file_stat *entry;\n>>> +\t\tconst char *name = stat.files[i]->name;\n>>> +\t\tunsigned int hash = strhash(name);\n>>> +\n>>> +\t\tentry = hashmap_get_from_hash(&s->file_map, hash, name);\n>>> +\t\tif (!entry) {\n>>> +\t\t\tFLEX_ALLOC_STR(entry, name, name);\n>>> +\t\t\thashmap_entry_init(entry, hash);\n>>> +\t\t\thashmap_add(&s->file_map, entry);\n>>> +\t\t}\n>> The path may already be in the collection_status.file_map from the\n>> previous run when \"diff-index --cached\" is run, in which case we avoid\n>> adding it twice, which makes sense.\n>>\n>>> +\t\tif (s->phase == WORKTREE) {\n>>> +\t\t\tentry->worktree.added = stat.files[i]->added;\n>>> +\t\t\tentry->worktree.deleted = stat.files[i]->deleted;\n>>> +\t\t} else if (s->phase == INDEX) {\n>>> +\t\t\tentry->index.added = stat.files[i]->added;\n>>> +\t\t\tentry->index.deleted = stat.files[i]->deleted;\n>>> +\t\t}\n>> As the set of phases will not going to grow, not having the final\n>> 'else BUG(\"phase is neither WORKTREE nor INDEX\");' here is OK.\n>>\n>> But stepping back a bit, if we know we will not grow the phases,\n>> then it may be simpler *and* equally descriptive to rename .phase\n>> field to a boolean \".collecting_from_index\" that literally means\n>> \"are we collecting from the index?\" and that way we can also get rid\n>> of the enum.\n> ... so that this can become\n>\n> \tif (s->collecting_from_index) {\n> \t\tentry->index.added = stat.files[i]->added;\n> \t\tentry->index.deleted = stat.files[i]->deleted;\n> \t} else {\n> \t\t...\n> \t}\n>\n> without \"else if\" and without having to worry about \"what if phase\n> is neither?\".\n>\n>> Grep for \"unborn\" in the codebase.  It probably makes sense to call\n>> it on_unborn_branch() instead.\n>>\n>> \tstatic int on_unborn_branch(void)\n>> \t{\n>> \t\tstruct object_id oid;\n>> \t\treturn !!get_oid(\"HEAD\", &oid);\n>> \t}\n>>\n>> Eventually, the users of \"unborn\" in sequencer.c and builtin/reset.c\n>> may want to share the implementation but the helper is so small that\n>> we probably should not worry about it until the topic is done and\n>> leave it for a later clean-up.\n> And before such a clean-up happens, the implementation of the helper\n> would want to be improved.  \"Does 'rev-parse --verify HEAD' work?\"\n> was an easiest way to see if we are on an unborn branch from a\n> script that works most of the time, but as we are rewriting it in C,\n> we should use the more direct and correct API to see if \"HEAD\" is\n> a symref, and if it points at a branch that does not yet exist,\n> which is available in the refs API as resolve_ref_unsafe().\n>\n> One issue with lazy use of get_oid(\"HEAD\") is that the function\n> dwims and tries to find HEAD in common hierarchies like\n> .git/refs/heads/HEAD etc. when .git/HEAD does not work.  We do not\n> want such a dwimmery when seeing \"are we on an unborn branch?\".\n>\n>>> +static struct collection_status *list_modified(struct repository *r, const char *filter)\n>>> +{\n>>> +\tint i = 0;\n>>> +\tstruct collection_status *s = xcalloc(1, sizeof(*s));\n>>> +\tstruct hashmap_iter iter;\n>>> +\tstruct file_stat **files;\n>>> +\tstruct file_stat *entry;\n>>> +\n>>> +\tif (repo_read_index(r) < 0) {\n>>> +\t\tprintf(\"\\n\");\n>>> +\t\treturn NULL;\n>>> +\t}\n>>> +\n>>> +\ts->reference = get_diff_reference();\n>>> +\thashmap_init(&s->file_map, hash_cmp, NULL, 0);\n>>> +\n>>> +\tcollect_changes_worktree(s);\n>>> +\tcollect_changes_index(s);\n>>> +\n>>> +\tif (hashmap_get_size(&s->file_map) < 1) {\n>>> +\t\tprintf(\"\\n\");\n>>> +\t\treturn NULL;\n>>> +\t}\n> The non-error codepath of this function does not do any output, but\n> we see two \"just emit newline\" before returning NUULL to signal an\n> error to the caller\" in the above.  Such a printing from this level\n> in the callchain (although we haven't seen callers of this function\n> yet at this point in the series) is wrong.  Presumably, the caller,\n> when it obtains a non-NULL 's', does something useful and maybe as a\n> part of the \"useful\" thing prints something to the standard output.\n> Then the caller is also responsible for handling a NULL return.  I.e.\n> upon seeing such a NULL collection status, if the party that invoked\n> the caller wants to see a single empty line for whatever reason (which\n> in turn is a questionable practice, if you ask me, but at this point\n> in the series we haven't seen what that invoker is doing, so for now\n> lets assume that it is sane to want to see an empty line), the caller\n> should do the putchar('\\n').\n\n\nCaller of list_modified is add_i_status in the later commit, and yes\nit should definitely take care of this newline.\nIt should print it after calling list_modified *and* list_and_choose.\n\nIt is done in the same way in the Perl script:\nsub status_cmd {\n     list_and_choose({ LIST_ONLY => 1, HEADER => $status_head },\n             list_modified());\n     print \"\\n\";\n}\n\nI overlooked this because print_modified (now list_modified) in\nthe previous iteration was equivalent of status command, and\neverything was handled there.\nAfter refactoring, I missed to pick up those printfs.\n\n\n> Also, these two \"return NULL\" leaks 's'.\n\n\nI guess I missed some more things. Thank you.\n\n\n>\n"},{"id":"369979","messageId":"xmqqd0njpkd5.fsf@gitster-ct.c.googlers.com","threadId":"50075","inReplyTo":"a97b29d2741e0e5ae8cf45b1c7cafc56afaa66ef.1550662887.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v5 04/10] add-interactive.c: implement list_and_choose","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-02-22T21:46:14Z","receivedAt":"2019-02-22T21:46:20Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Slavica Djukic via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> +#define HEADER_INDENT \"      \"\n> +\n>  enum collection_phase {\n>  \tWORKTREE,\n>  \tINDEX\n> @@ -27,6 +29,61 @@ struct collection_status {\n>  \tstruct hashmap file_map;\n>  };\n>  \n> +struct list_and_choose_options {\n> +\tint column_n;\n> +\tunsigned singleton:1;\n> +\tunsigned list_flat:1;\n> +\tunsigned list_only:1;\n> +\tunsigned list_only_file_names:1;\n> +\tunsigned immediate:1;\n> +\tchar *header;\n> +\tconst char *prompt;\n\nMakes a reader wonder if \"header\" can also be const (not to be taken\nas a suggestion to bend backwards to make it so).\n\n> +\tvoid (*on_eof_fn)(void);\n> +};\n> +\n> +struct choice {\n> +\tstruct hashmap_entry e;\n> +\tchar type;\n\nIf this is for choosing among the member of union, possible value(s)\nfor the type member and which value corresponds to which union\nmember must be documented somewhere, perhaps as a comment around\nhere.\n\n> +\tunion {\n> +\t\tvoid (*command_fn)(void);\n> +\t\tstruct {\n> +\t\t\tstruct {\n> +\t\t\t\tuintmax_t added, deleted;\n> +\t\t\t} index, worktree;\n> +\t\t} file;\n> +\t} u;\n> +\tsize_t prefix_length;\n> +\tconst char *name;\n> +};\n> +\n> +struct choices {\n> +\tstruct choice **choices;\n\nIn general, do not name an array in plural.  An exception is when\nthe code mostly refers to the array as a whole.\n\nWhen most accesses are to individual elements, then it would be a\nbig win to be able to see choice[2] and pronounce it \"the second\nchoice\" (you do not say \"the second choices\").\n\n> +\tsize_t alloc, nr;\n> +};\n> +#define CHOICES_INIT { NULL, 0, 0 }\n> +\n> +static int use_color = -1;\n> +enum color_add_i {\n> +\tCOLOR_PROMPT,\n> +\tCOLOR_HEADER,\n> +\tCOLOR_HELP,\n> +\tCOLOR_ERROR\n> +};\n> +\n> +static char colors[][COLOR_MAXLEN] = {\n\nDo not be overly selfish to assume that this will stay to be the\nonly color pallette in this file.  If this is the color palette for\nlist_and_choose, then have it in its name, e.g. list_and_choose_color[]\nor something like that.\n\n> +\tGIT_COLOR_BOLD_BLUE, /* Prompt */\n> +\tGIT_COLOR_BOLD,      /* Header */\n> +\tGIT_COLOR_BOLD_RED,  /* Help */\n> +\tGIT_COLOR_RESET      /* Reset */\n> +};\n\nIs the above list of values and comments correct?  \n\nDoesn't each member of enum correspond to each element in\nlist_and_choose_color[][COLOR_MAXLEN] array?  It does not exactly\nmatch my intuition to have help text in red and error messages in\nplain color.\n\n> @@ -186,3 +243,73 @@ static struct collection_status *list_modified(struct repository *r, const char\n>  \tfree(files);\n>  \treturn s;\n>  }\n> +\n> +static struct choices *list_and_choose(struct choices *data,\n> +\t\t\t\t       struct list_and_choose_options *opts)\n> +{\n> +\tif (!data)\n> +\t\treturn NULL;\n> +\n> +\twhile (1) {\n> +\t\tint last_lf = 0;\n> +\n> +\t\tif (opts->header) {\n> +\t\t\tconst char *header_color = get_color(COLOR_HEADER);\n> +\t\t\tif (!opts->list_flat)\n> +\t\t\t\tprintf(HEADER_INDENT);\n\nI won't complain if this is sufficient for the application, but the\nabove would not allow different level of indentation depending on\nwhat header is being shown.  It may make sense to get rid of list_flat\nboolean and instead allow a new \"const char *header_indent\" member\nin the opts structure supplied by the caller.\n\nDon't use printf() when you _know_ you want to show a simple string\nwithout any % interpolation.  fputs(HEADER_INDENT, stdout) would suffice.\n\n> +\t\t\tcolor_fprintf_ln(stdout, header_color, \"%s\", opts->header);\n> +\t\t}\n> +\n> +\t\tfor (int i = 0; i < data->nr; i++) {\n\nWe do not say \"for (int i\" (see a previous review).\n\n> +\t\t\tstruct choice *c = data->choices[i];\n> +\t\t\tchar *print;\n> +\t\t\tconst char *modified_fmt = _(\"%12s %12s %s\");\n> +\t\t\tchar worktree_changes[50];\n> +\t\t\tchar index_changes[50];\n> +\t\t\tchar print_buf[100];\n\nIt appears that many of these variables are only needed inside \"we\nare showing 'f' and not just names\" block.  Can their scope be\nnarrowed?\n\n> +\t\t\tprint = (char *)c->name;\n\nYuck.  Stuff c->name into print_buf[] instead and get rid of \"print\"\npointer.\n\n> +\t\t\tif ((data->choices[i]->type == 'f') && (!opts->list_only_file_names)) {\n> +\t\t\t\tuintmax_t worktree_added = c->u.file.worktree.added;\n> +\t\t\t\tuintmax_t worktree_deleted = c->u.file.worktree.deleted;\n> +\t\t\t\tuintmax_t index_added = c->u.file.index.added;\n> +\t\t\t\tuintmax_t index_deleted = c->u.file.index.deleted;\n> +\n> +\t\t\t\tif (worktree_added || worktree_deleted)\n> +\t\t\t\t\tsnprintf(worktree_changes, 50, \"+%\"PRIuMAX\"/-%\"PRIuMAX,\n> +\t\t\t\t\t\t worktree_added, worktree_deleted);\n> +\t\t\t\telse\n> +\t\t\t\t\tsnprintf(worktree_changes, 50, \"%s\", _(\"nothing\"));\n> +\t\t\t\tif (index_added || index_deleted)\n> +\t\t\t\t\tsnprintf(index_changes, 50, \"+%\"PRIuMAX\"/-%\"PRIuMAX,\n> +\t\t\t\t\t\t index_added, index_deleted);\n> +\t\t\t\telse\n> +\t\t\t\t\tsnprintf(index_changes, 50, \"%s\", _(\"unchanged\"));\n> +\n> +\t\t\t\tsnprintf(print_buf, 100, modified_fmt, index_changes,\n> +\t\t\t\t\t worktree_changes, print);\n\nAll of the above look overly repetitious; a helper function that\ntakes a pointer to \"struct { uintmax_t a, d; }\" and populates\nchanges[] buffer would cut them down by half, but other than that\nI do not see a room for drastic improvement here X-<.\n\nOh, it would greatly help to use two strbuf for wt/ix_changes that\nare defined outside the loop that is strbuf_reset() after each\niteration and use things like strbuf_addf().\n\n> +\t\t\t\tprint = xmalloc(strlen(print_buf) + 1);\n> +\t\t\t\tsnprintf(print, 100, \"%s\", print_buf);\n\nLikewise.\n\n> +\t\t\t}\n> +\n> +\t\t\tprintf(\" %2d: %s\", i + 1, print);\n> +\t\t\tif ((opts->list_flat) && ((i + 1) % (opts->column_n))) {\n> +\t\t\t\tprintf(\"\\t\");\n> +\t\t\t\tlast_lf = 0;\n> +\t\t\t}\n> +\t\t\telse {\n> +\t\t\t\tprintf(\"\\n\");\n> +\t\t\t\tlast_lf = 1;\n> +\t\t\t}\n> +\n> +\t\t}\n> +\n> +\t\tif (!last_lf)\n> +\t\t\tprintf(\"\\n\");\n> +\n> +\t\treturn NULL;\n> +\t}\n> +}\n\nThis obviously only lists but does not let you choose at this step\nin the series, but that is OK.\n\nBut I see a deeper problem with the design of this helper.  The\nthings this helper can list is quite limited.\n\nThe original was designed so that the shown strings are prepared by\nthe caller and this helper is solely responsible for showing the\nchoices, giving prompt, and accepting choice (in various abbreviated\nforms), all _WITHOUT_ having to know the meaning of what is in the\nlist.  It gave us a much better separation of labor and\nresponsibility between the caller and the callee, I would think.\n"},{"id":"369982","messageId":"xmqq4l8vpj6x.fsf@gitster-ct.c.googlers.com","threadId":"50075","inReplyTo":"9a72aabe6cf1001d373c933086a79ace29b98806.1550662887.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v5 05/10] add-interactive.c: implement status command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-02-22T22:11:34Z","receivedAt":"2019-02-22T22:11:40Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Slavica Djukic via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> +static int parse_color_slot(const char *slot)\n> +{\n> +\tif (!strcasecmp(slot, \"prompt\"))\n> +\t\treturn COLOR_PROMPT;\n> +\tif (!strcasecmp(slot, \"header\"))\n> +\t\treturn COLOR_HEADER;\n> +\tif (!strcasecmp(slot, \"help\"))\n> +\t\treturn COLOR_HELP;\n> +\tif (!strcasecmp(slot, \"error\"))\n> +\t\treturn COLOR_ERROR;\n\nAs these are \"color.interactive.<name>\", matching case-insensitively\nis the right thing to do.  Good.\n\nIf we would end up increasing the number of slots, we may need to\nswitch to LOOKUP_CONFIG(), but for just four of them, this will do.\n\n> @@ -313,3 +357,78 @@ static struct choices *list_and_choose(struct choices *data,\n>  \t\treturn NULL;\n>  \t}\n>  }\n> +\n> +static struct choice *make_choice(const char *name )\n\nExcess SP before ')'?\n\n> +{\n> +\tstruct choice *choice;\n\nStyle: have a blank line here to delineate decls (at the beginning\nof a function) and stmts (at all the decls).\n\n> +\tFLEXPTR_ALLOC_STR(choice, name, name);\n> +\treturn choice;\n> +}\n> +\n> +static struct choice *add_choice(struct choices *choices, const char type,\n> +\t\t\t\t struct file_stat *file, struct command *command)\n> +{\n> +\tstruct choice *choice;\n\nDitto here.\n\n> +\tswitch (type) {\n> +\t\tcase 'f':\n\nStyle: in our codebase, switch and case are indented to the same\nlevel, with the body of each case arm indented one more level.\n\n> +\t\t\tchoice = make_choice(file->name);\n> +\t\t\tchoice->u.file.index.added = file->index.added;\n> +\t\t\tchoice->u.file.index.deleted = file->index.deleted;\n> +\t\t\tchoice->u.file.worktree.added = file->worktree.added;\n> +\t\t\tchoice->u.file.worktree.deleted = file->worktree.deleted;\n\nWould it make sense to make sure that all of file->index,\nu.file.index, file->wt, u.file.wt are exactly the same type of\nstruct by introducing\n\n\tstruct adddel { uintmax_t add, del; };\n\nin a very early part of the series, and embed that structure as a\nmember in \"struct choice\" and \"struct file_stat\"?  That way, these\nassignments would become two structure assignments, that would be\nmuch easier to read.\n\n> +void add_i_status(void)\n> +{\n> +\tstruct collection_status *s;\n> +\tstruct list_and_choose_options opts = { 0 };\n> +\tstruct hashmap *map;\n> +\tstruct hashmap_iter iter;\n> +\tstruct choices choices = CHOICES_INIT;\n> +\tstruct file_stat *entry;\n> +\tconst char *modified_fmt = _(\"%12s %12s %s\");\n> +\tconst char type = 'f';\n> +\n> +\topts.header = xmalloc(sizeof(char) * (HEADER_MAXLEN + 1));\n> +\tsnprintf(opts.header, HEADER_MAXLEN + 1, modified_fmt,\n> +\t\t _(\"staged\"), _(\"unstaged\"), _(\"path\"));\n\nIs there aversion to use of strbuf among your mentors?\n\n\n> +\ts = list_modified(the_repository, NULL);\n> +\tif (s == NULL)\n> +\t\treturn;\n> +\n> +\tmap = &s->file_map;\n> +\thashmap_iter_init(map, &iter);\n> +\twhile ((entry = hashmap_iter_next(&iter))) {\n> +\t\tadd_choice(&choices, type, entry, NULL);\n> +\t}\n> +\n> +\tlist_and_choose(&choices, &opts);\n\nIn what order are these filenames given?  Whatever random order the\nhashmap happens to store them in?\n\nI vaguely recall in an earlier step the code used hashmap to collect\nbut at the end produced a sorted list out of the final result.\nShouldn't we be iterating over that sorted list instead?  Do we even\nneed the hashmap at this point?\n\n> +\thashmap_free(&s->file_map, 1);\n> +\tfree(s);\n> +\tfree_choices(&choices);\n\nDid we just leak opt.header?\n\n> +}\n> diff --git a/add-interactive.h b/add-interactive.h\n> new file mode 100644\n> index 0000000000..8ef3d2e82b\n> --- /dev/null\n> +++ b/add-interactive.h\n> @@ -0,0 +1,8 @@\n> +#ifndef ADD_INTERACTIVE_H\n> +#define ADD_INTERACTIVE_H\n> +\n> +int add_i_config(const char *var, const char *value, void *cbdata);\n> +\n> +void add_i_status(void);\n> +\n> +#endif\n> diff --git a/builtin/add--helper.c b/builtin/add--helper.c\n> index 6a97f0e191..464d2245f3 100644\n> --- a/builtin/add--helper.c\n> +++ b/builtin/add--helper.c\n> @@ -1,6 +1,38 @@\n> +#include \"add-interactive.h\"\n>  #include \"builtin.h\"\n> +#include \"config.h\"\n> +#include \"revision.h\"\n> +\n> +static const char * const builtin_add_helper_usage[] = {\n> +\tN_(\"git add-interactive--helper <command>\"),\n> +\tNULL\n> +};\n> +\n> +enum cmd_mode {\n> +\tDEFAULT = 0,\n> +\tSTATUS\n> +};\n>  \n>  int cmd_add__helper(int argc, const char **argv, const char *prefix)\n>  {\n> +\tenum cmd_mode mode = DEFAULT;\n> +\n> +\tstruct option options[] = {\n> +\t\tOPT_CMDMODE(0, \"status\", &mode,\n> +\t\t\t    N_(\"print status information with diffstat\"), STATUS),\n> +\t\tOPT_END()\n> +\t};\n> +\n> +\tgit_config(add_i_config, NULL);\n> +\targc = parse_options(argc, argv, NULL, options,\n> +\t\t\t     builtin_add_helper_usage,\n> +\t\t\t     PARSE_OPT_KEEP_ARGV0);\n> +\n> +\tif (mode == STATUS)\n> +\t\tadd_i_status();\n> +\telse\n> +\t\tusage_with_options(builtin_add_helper_usage,\n> +\t\t\t\t   options);\n> +\n>  \treturn 0;\n>  }\n"},{"id":"370411","messageId":"22f856ad-8248-7f1b-a720-636da6286e82@gmail.com","threadId":"50075","inReplyTo":"xmqq4l8vpj6x.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v5 05/10] add-interactive.c: implement status command","fromName":"Slavica Djukic","fromEmail":"slavicadj.ip2018@gmail.com","sentAt":"2019-03-01T11:08:56Z","receivedAt":"2019-03-01T11:09:05Z","isPatch":true,"sender":{"key":"slavicadj.ip2018@gmail.com","avatar":null},"body":"\nOn 22-Feb-19 11:11 PM, Junio C Hamano wrote:\n> \"Slavica Djukic via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n>\n>> +static int parse_color_slot(const char *slot)\n>> +{\n>> +\tif (!strcasecmp(slot, \"prompt\"))\n>> +\t\treturn COLOR_PROMPT;\n>> +\tif (!strcasecmp(slot, \"header\"))\n>> +\t\treturn COLOR_HEADER;\n>> +\tif (!strcasecmp(slot, \"help\"))\n>> +\t\treturn COLOR_HELP;\n>> +\tif (!strcasecmp(slot, \"error\"))\n>> +\t\treturn COLOR_ERROR;\n> As these are \"color.interactive.<name>\", matching case-insensitively\n> is the right thing to do.  Good.\n>\n> If we would end up increasing the number of slots, we may need to\n> switch to LOOKUP_CONFIG(), but for just four of them, this will do.\n>\n>> @@ -313,3 +357,78 @@ static struct choices *list_and_choose(struct choices *data,\n>>   \t\treturn NULL;\n>>   \t}\n>>   }\n>> +\n>> +static struct choice *make_choice(const char *name )\n> Excess SP before ')'?\n>\n>> +{\n>> +\tstruct choice *choice;\n> Style: have a blank line here to delineate decls (at the beginning\n> of a function) and stmts (at all the decls).\n>\n>> +\tFLEXPTR_ALLOC_STR(choice, name, name);\n>> +\treturn choice;\n>> +}\n>> +\n>> +static struct choice *add_choice(struct choices *choices, const char type,\n>> +\t\t\t\t struct file_stat *file, struct command *command)\n>> +{\n>> +\tstruct choice *choice;\n> Ditto here.\n>\n>> +\tswitch (type) {\n>> +\t\tcase 'f':\n> Style: in our codebase, switch and case are indented to the same\n> level, with the body of each case arm indented one more level.\n>\n>> +\t\t\tchoice = make_choice(file->name);\n>> +\t\t\tchoice->u.file.index.added = file->index.added;\n>> +\t\t\tchoice->u.file.index.deleted = file->index.deleted;\n>> +\t\t\tchoice->u.file.worktree.added = file->worktree.added;\n>> +\t\t\tchoice->u.file.worktree.deleted = file->worktree.deleted;\n> Would it make sense to make sure that all of file->index,\n> u.file.index, file->wt, u.file.wt are exactly the same type of\n> struct by introducing\n>\n> \tstruct adddel { uintmax_t add, del; };\n>\n> in a very early part of the series, and embed that structure as a\n> member in \"struct choice\" and \"struct file_stat\"?  That way, these\n> assignments would become two structure assignments, that would be\n> much easier to read.\n\n\nYes, definitely. I worked on this and it will be included in the next \niteration.\n\n\n>\n>> +void add_i_status(void)\n>> +{\n>> +\tstruct collection_status *s;\n>> +\tstruct list_and_choose_options opts = { 0 };\n>> +\tstruct hashmap *map;\n>> +\tstruct hashmap_iter iter;\n>> +\tstruct choices choices = CHOICES_INIT;\n>> +\tstruct file_stat *entry;\n>> +\tconst char *modified_fmt = _(\"%12s %12s %s\");\n>> +\tconst char type = 'f';\n>> +\n>> +\topts.header = xmalloc(sizeof(char) * (HEADER_MAXLEN + 1));\n>> +\tsnprintf(opts.header, HEADER_MAXLEN + 1, modified_fmt,\n>> +\t\t _(\"staged\"), _(\"unstaged\"), _(\"path\"));\n> Is there aversion to use of strbuf among your mentors?\n>\n>\n>> +\ts = list_modified(the_repository, NULL);\n>> +\tif (s == NULL)\n>> +\t\treturn;\n>> +\n>> +\tmap = &s->file_map;\n>> +\thashmap_iter_init(map, &iter);\n>> +\twhile ((entry = hashmap_iter_next(&iter))) {\n>> +\t\tadd_choice(&choices, type, entry, NULL);\n>> +\t}\n>> +\n>> +\tlist_and_choose(&choices, &opts);\n> In what order are these filenames given?  Whatever random order the\n> hashmap happens to store them in?\n>\n> I vaguely recall in an earlier step the code used hashmap to collect\n> but at the end produced a sorted list out of the final result.\n> Shouldn't we be iterating over that sorted list instead?  Do we even\n> need the hashmap at this point?\n\n\nWe actually don't need hashmap at this point, I've changed this so that\nlist_modified returns produced sorted list.\nI've also applied all other suggestions in this message.\nThank you.\n\n>> +\thashmap_free(&s->file_map, 1);\n>> +\tfree(s);\n>> +\tfree_choices(&choices);\n> Did we just leak opt.header?\n>\n>> +}\n>> diff --git a/add-interactive.h b/add-interactive.h\n>> new file mode 100644\n>> index 0000000000..8ef3d2e82b\n>> --- /dev/null\n>> +++ b/add-interactive.h\n>> @@ -0,0 +1,8 @@\n>> +#ifndef ADD_INTERACTIVE_H\n>> +#define ADD_INTERACTIVE_H\n>> +\n>> +int add_i_config(const char *var, const char *value, void *cbdata);\n>> +\n>> +void add_i_status(void);\n>> +\n>> +#endif\n>> diff --git a/builtin/add--helper.c b/builtin/add--helper.c\n>> index 6a97f0e191..464d2245f3 100644\n>> --- a/builtin/add--helper.c\n>> +++ b/builtin/add--helper.c\n>> @@ -1,6 +1,38 @@\n>> +#include \"add-interactive.h\"\n>>   #include \"builtin.h\"\n>> +#include \"config.h\"\n>> +#include \"revision.h\"\n>> +\n>> +static const char * const builtin_add_helper_usage[] = {\n>> +\tN_(\"git add-interactive--helper <command>\"),\n>> +\tNULL\n>> +};\n>> +\n>> +enum cmd_mode {\n>> +\tDEFAULT = 0,\n>> +\tSTATUS\n>> +};\n>>   \n>>   int cmd_add__helper(int argc, const char **argv, const char *prefix)\n>>   {\n>> +\tenum cmd_mode mode = DEFAULT;\n>> +\n>> +\tstruct option options[] = {\n>> +\t\tOPT_CMDMODE(0, \"status\", &mode,\n>> +\t\t\t    N_(\"print status information with diffstat\"), STATUS),\n>> +\t\tOPT_END()\n>> +\t};\n>> +\n>> +\tgit_config(add_i_config, NULL);\n>> +\targc = parse_options(argc, argv, NULL, options,\n>> +\t\t\t     builtin_add_helper_usage,\n>> +\t\t\t     PARSE_OPT_KEEP_ARGV0);\n>> +\n>> +\tif (mode == STATUS)\n>> +\t\tadd_i_status();\n>> +\telse\n>> +\t\tusage_with_options(builtin_add_helper_usage,\n>> +\t\t\t\t   options);\n>> +\n>>   \treturn 0;\n>>   }\n"},{"id":"370412","messageId":"25f71e47-acad-e985-4f3f-ecde77f883d6@gmail.com","threadId":"50075","inReplyTo":"xmqqd0njpkd5.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v5 04/10] add-interactive.c: implement list_and_choose","fromName":"Slavica Djukic","fromEmail":"slavicadj.ip2018@gmail.com","sentAt":"2019-03-01T11:20:57Z","receivedAt":"2019-03-01T11:21:06Z","isPatch":true,"sender":{"key":"slavicadj.ip2018@gmail.com","avatar":null},"body":"\nOn 22-Feb-19 10:46 PM, Junio C Hamano wrote:\n> \"Slavica Djukic via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n>\n>> +#define HEADER_INDENT \"      \"\n>> +\n>>   enum collection_phase {\n>>   \tWORKTREE,\n>>   \tINDEX\n>> @@ -27,6 +29,61 @@ struct collection_status {\n>>   \tstruct hashmap file_map;\n>>   };\n>>   \n>> +struct list_and_choose_options {\n>> +\tint column_n;\n>> +\tunsigned singleton:1;\n>> +\tunsigned list_flat:1;\n>> +\tunsigned list_only:1;\n>> +\tunsigned list_only_file_names:1;\n>> +\tunsigned immediate:1;\n>> +\tchar *header;\n>> +\tconst char *prompt;\n> Makes a reader wonder if \"header\" can also be const (not to be taken\n> as a suggestion to bend backwards to make it so).\n>\n>> +\tvoid (*on_eof_fn)(void);\n>> +};\n>> +\n>> +struct choice {\n>> +\tstruct hashmap_entry e;\n>> +\tchar type;\n> If this is for choosing among the member of union, possible value(s)\n> for the type member and which value corresponds to which union\n> member must be documented somewhere, perhaps as a comment around\n> here.\n>\n>> +\tunion {\n>> +\t\tvoid (*command_fn)(void);\n>> +\t\tstruct {\n>> +\t\t\tstruct {\n>> +\t\t\t\tuintmax_t added, deleted;\n>> +\t\t\t} index, worktree;\n>> +\t\t} file;\n>> +\t} u;\n>> +\tsize_t prefix_length;\n>> +\tconst char *name;\n>> +};\n>> +\n>> +struct choices {\n>> +\tstruct choice **choices;\n> In general, do not name an array in plural.  An exception is when\n> the code mostly refers to the array as a whole.\n>\n> When most accesses are to individual elements, then it would be a\n> big win to be able to see choice[2] and pronounce it \"the second\n> choice\" (you do not say \"the second choices\").\n>\n>> +\tsize_t alloc, nr;\n>> +};\n>> +#define CHOICES_INIT { NULL, 0, 0 }\n>> +\n>> +static int use_color = -1;\n>> +enum color_add_i {\n>> +\tCOLOR_PROMPT,\n>> +\tCOLOR_HEADER,\n>> +\tCOLOR_HELP,\n>> +\tCOLOR_ERROR\n>> +};\n>> +\n>> +static char colors[][COLOR_MAXLEN] = {\n> Do not be overly selfish to assume that this will stay to be the\n> only color pallette in this file.  If this is the color palette for\n> list_and_choose, then have it in its name, e.g. list_and_choose_color[]\n> or something like that.\n>\n>> +\tGIT_COLOR_BOLD_BLUE, /* Prompt */\n>> +\tGIT_COLOR_BOLD,      /* Header */\n>> +\tGIT_COLOR_BOLD_RED,  /* Help */\n>> +\tGIT_COLOR_RESET      /* Reset */\n>> +};\n> Is the above list of values and comments correct?\n>\n> Doesn't each member of enum correspond to each element in\n> list_and_choose_color[][COLOR_MAXLEN] array?  It does not exactly\n> match my intuition to have help text in red and error messages in\n> plain color.\n\n\nI noticed I didn't add colors in corresponding commits, but list is \ncorrect -- later\non in patch series there is\n\nGIT_COLOR_BOLD_RED, /* Error*/\n\nadded so that error messages are shown in red.\n\n\nHelp text is also in red following up what is happening in \ngit-add--interactive.perl.\n\n>> @@ -186,3 +243,73 @@ static struct collection_status *list_modified(struct repository *r, const char\n>>   \tfree(files);\n>>   \treturn s;\n>>   }\n>> +\n>> +static struct choices *list_and_choose(struct choices *data,\n>> +\t\t\t\t       struct list_and_choose_options *opts)\n>> +{\n>> +\tif (!data)\n>> +\t\treturn NULL;\n>> +\n>> +\twhile (1) {\n>> +\t\tint last_lf = 0;\n>> +\n>> +\t\tif (opts->header) {\n>> +\t\t\tconst char *header_color = get_color(COLOR_HEADER);\n>> +\t\t\tif (!opts->list_flat)\n>> +\t\t\t\tprintf(HEADER_INDENT);\n> I won't complain if this is sufficient for the application, but the\n> above would not allow different level of indentation depending on\n> what header is being shown.  It may make sense to get rid of list_flat\n> boolean and instead allow a new \"const char *header_indent\" member\n> in the opts structure supplied by the caller.\n>\n> Don't use printf() when you _know_ you want to show a simple string\n> without any % interpolation.  fputs(HEADER_INDENT, stdout) would suffice.\n>\n>> +\t\t\tcolor_fprintf_ln(stdout, header_color, \"%s\", opts->header);\n>> +\t\t}\n>> +\n>> +\t\tfor (int i = 0; i < data->nr; i++) {\n> We do not say \"for (int i\" (see a previous review).\n>\n>> +\t\t\tstruct choice *c = data->choices[i];\n>> +\t\t\tchar *print;\n>> +\t\t\tconst char *modified_fmt = _(\"%12s %12s %s\");\n>> +\t\t\tchar worktree_changes[50];\n>> +\t\t\tchar index_changes[50];\n>> +\t\t\tchar print_buf[100];\n> It appears that many of these variables are only needed inside \"we\n> are showing 'f' and not just names\" block.  Can their scope be\n> narrowed?\n\n\nYes, I will change this.\n\n\n>\n>> +\t\t\tprint = (char *)c->name;\n> Yuck.  Stuff c->name into print_buf[] instead and get rid of \"print\"\n> pointer.\n>\n>> +\t\t\tif ((data->choices[i]->type == 'f') && (!opts->list_only_file_names)) {\n>> +\t\t\t\tuintmax_t worktree_added = c->u.file.worktree.added;\n>> +\t\t\t\tuintmax_t worktree_deleted = c->u.file.worktree.deleted;\n>> +\t\t\t\tuintmax_t index_added = c->u.file.index.added;\n>> +\t\t\t\tuintmax_t index_deleted = c->u.file.index.deleted;\n>> +\n>> +\t\t\t\tif (worktree_added || worktree_deleted)\n>> +\t\t\t\t\tsnprintf(worktree_changes, 50, \"+%\"PRIuMAX\"/-%\"PRIuMAX,\n>> +\t\t\t\t\t\t worktree_added, worktree_deleted);\n>> +\t\t\t\telse\n>> +\t\t\t\t\tsnprintf(worktree_changes, 50, \"%s\", _(\"nothing\"));\n>> +\t\t\t\tif (index_added || index_deleted)\n>> +\t\t\t\t\tsnprintf(index_changes, 50, \"+%\"PRIuMAX\"/-%\"PRIuMAX,\n>> +\t\t\t\t\t\t index_added, index_deleted);\n>> +\t\t\t\telse\n>> +\t\t\t\t\tsnprintf(index_changes, 50, \"%s\", _(\"unchanged\"));\n>> +\n>> +\t\t\t\tsnprintf(print_buf, 100, modified_fmt, index_changes,\n>> +\t\t\t\t\t worktree_changes, print);\n> All of the above look overly repetitious; a helper function that\n> takes a pointer to \"struct { uintmax_t a, d; }\" and populates\n> changes[] buffer would cut them down by half, but other than that\n> I do not see a room for drastic improvement here X-<.\n>\n> Oh, it would greatly help to use two strbuf for wt/ix_changes that\n> are defined outside the loop that is strbuf_reset() after each\n> iteration and use things like strbuf_addf().\n>\n>> +\t\t\t\tprint = xmalloc(strlen(print_buf) + 1);\n>> +\t\t\t\tsnprintf(print, 100, \"%s\", print_buf);\n> Likewise.\n>\n>> +\t\t\t}\n>> +\n>> +\t\t\tprintf(\" %2d: %s\", i + 1, print);\n>> +\t\t\tif ((opts->list_flat) && ((i + 1) % (opts->column_n))) {\n>> +\t\t\t\tprintf(\"\\t\");\n>> +\t\t\t\tlast_lf = 0;\n>> +\t\t\t}\n>> +\t\t\telse {\n>> +\t\t\t\tprintf(\"\\n\");\n>> +\t\t\t\tlast_lf = 1;\n>> +\t\t\t}\n>> +\n>> +\t\t}\n>> +\n>> +\t\tif (!last_lf)\n>> +\t\t\tprintf(\"\\n\");\n>> +\n>> +\t\treturn NULL;\n>> +\t}\n>> +}\n> This obviously only lists but does not let you choose at this step\n> in the series, but that is OK.\n>\n> But I see a deeper problem with the design of this helper.  The\n> things this helper can list is quite limited.\n>\n> The original was designed so that the shown strings are prepared by\n> the caller and this helper is solely responsible for showing the\n> choices, giving prompt, and accepting choice (in various abbreviated\n> forms), all _WITHOUT_ having to know the meaning of what is in the\n> list.  It gave us a much better separation of labor and\n> responsibility between the caller and the callee, I would think.\n>\n\nThanks for pointing this out. I talked to my mentor and I'm now working on\nmaking list_and_choose more \"type-independent\".\n\nI didn't reply to all suggestions in this message, but I did apply them \nin the code.\n\n>\n"},{"id":"370588","messageId":"917cc2e8-f35e-4882-141a-2de22149be48@gmail.com","threadId":"50075","inReplyTo":"pull.103.v5.git.gitgitgadget@gmail.com","subject":"End of Outreachy internship","fromName":"Slavica Djukic","fromEmail":"slavicadj.ip2018@gmail.com","sentAt":"2019-03-04T10:49:46Z","receivedAt":"2019-03-04T10:49:56Z","isPatch":false,"sender":{"key":"slavicadj.ip2018@gmail.com","avatar":null},"body":"Hi everyone,\n\nToday is officially the last day of my Outreachy internship.\nI wanted to say a few things about my experience and future of the\nproject I worked on.\nProject was \"Turn git add -i into built-in\" with Johannes Schindelin\nas my mentor.\n\nI truly had amazing time, I learned so much about\ncoding and Git itself, went to Git Merge and FOSDEM and had a chance\nto meet great people.\nI've never contributed before to open-source, and now that I did,\nI find it invaluable experience.\nAlthough my obligations at this point don't allow me to work on\nthe project, I will try my best to contribute in the future to Git\nor/and other great open-source projects.\nThat said, my mentor Johannes Schindelin will continue to work on\nthe project from now on.\n\nWhat is implemented in this project so far:\ncommands: status and help, helper functions like highlight_prefix(),\nfind_unique_prefixes(), is_valid_prefix(), etc., but most importantly:\nlist_and_choose() and list_modified().\nMost of the add -i commands will use those two functions to collect,\nshow the data and let the user make a choice.\n\nWhat is left to be done:\nIn the current patch series, I got a lot of suggestion from Junio,\nand I applied all of them -- the only thing left is correcting\nlist_and_choose more to make it type-independent.\nIn the future, all the other commands: update, revert, add untracked,\npatch, diff and quit.\n\nI would like to thank all reviewers for useful suggestions and\ncomprehension.\nAnd thousand thanks to Johannes Schindelin, who turned out to be\nthe most amazing mentor.\n\n-Slavica Djukic\n\nOn 20-Feb-19 12:41 PM, Slavica Đukić via GitGitGadget wrote:\n> This is the first version of a patch series to start porting\n> git-add--interactive from Perl to C. Daniel Ferreira's patch series used as\n> a head start:\n> https://public-inbox.org/git/1494907234-28903-1-git-send-email-bnmvco@gmail.com/t/#u\n>\n> Changes since v4:\n>\n>   * rename print_modified to list_modifed\n>   * the big change was implementing list_and_choose, which resulted in code\n>     refactoring, i.e. separating list_modified and status_cmd and making\n>     status_cmd use both list_modified and list_and_choose\n>   * implement struct choice instead of struct stuff_item as main data\n>     structure for list_and_choose\n>   * introduce list_only option and implement support for !list_only users\n>   * introduce highlighting of unique prefixes\n>\n> Note that authorship handling is slightly changed. In some of the commits, I\n> used Original-patch-by instead of listing Daniel Ferreira as author.\n>\n> Also, I would like to point out that my Outreachy internship is going to\n> finish on March 4 and I would really appreciate reviews before it does.\n>\n> Daniel Ferreira (3):\n>    diff: export diffstat interface\n>    add--helper: create builtin helper for interactive add\n>    add--interactive.perl: use add--helper --status for status_cmd\n>\n> Slavica Djukic (7):\n>    add-interactive.c: implement list_modified\n>    add-interactive.c: implement list_and_choose\n>    add-interactive.c: implement status command\n>    add-interactive.c: add support for list_only option\n>    add-interactive.c: implement show-help command\n>    t3701-add-interactive: test add_i_show_help()\n>    add--interactive.perl: use add--helper --show-help for help_cmd\n>\n>   .gitignore                 |   1 +\n>   Makefile                   |   2 +\n>   add-interactive.c          | 819 +++++++++++++++++++++++++++++++++++++\n>   add-interactive.h          |  10 +\n>   builtin.h                  |   1 +\n>   builtin/add--helper.c      |  43 ++\n>   diff.c                     |  36 +-\n>   diff.h                     |  18 +\n>   git-add--interactive.perl  |  17 +-\n>   git.c                      |   1 +\n>   t/t3701-add-interactive.sh |  24 ++\n>   11 files changed, 937 insertions(+), 35 deletions(-)\n>   create mode 100644 add-interactive.c\n>   create mode 100644 add-interactive.h\n>   create mode 100644 builtin/add--helper.c\n>\n>\n> base-commit: ca1b4116483b397e78483376296bcd23916ab553\n> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-103%2FslavicaDj%2Fadd-i-v5\n> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-103/slavicaDj/add-i-v5\n> Pull-Request: https://github.com/gitgitgadget/git/pull/103\n>\n> Range-diff vs v4:\n>\n>    1:  737767b6f4 !  1:  d839f0c082 diff: export diffstat interface\n>       @@ -11,6 +11,7 @@\n>            how the show_* functions used by diff_flush() do it. One example is the\n>            builtin implementation of git-add--interactive's status.\n>        \n>       +    Mentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n>            Signed-off-by: Daniel Ferreira <bnmvco@gmail.com>\n>            Signed-off-by: Slavica Djukic <slawica92@hotmail.com>\n>        \n>    2:  91b1963125 !  2:  304c3863b1 add--helper: create builtin helper for interactive add\n>       @@ -2,8 +2,8 @@\n>        \n>            add--helper: create builtin helper for interactive add\n>        \n>       -    Create a builtin helper for git-add--interactive, which right now is not\n>       -    able to do anything.\n>       +    Create a builtin helper for git-add--interactive, which at this point\n>       +    is not doing anything.\n>        \n>            This is the first step in an effort to convert git-add--interactive.perl\n>            to a C builtin, in search for better portability, expressibility and\n>       @@ -13,6 +13,7 @@\n>            remove the last \"big\" Git script to have Perl as a dependency, allowing\n>            most Git users to have a NOPERL build running without big losses.\n>        \n>       +    Mentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n>            Signed-off-by: Daniel Ferreira <bnmvco@gmail.com>\n>            Signed-off-by: Slavica Djukic <slawica92@hotmail.com>\n>        \n>    3:  d247ef69fe !  3:  8790ffaa39 add-interactive.c: implement status command\n>       @@ -1,17 +1,12 @@\n>       -Author: Daniel Ferreira <bnmvco@gmail.com>\n>       +Author: Slavica Djukic <slawica92@hotmail.com>\n>        \n>       -    add-interactive.c: implement status command\n>       +    add-interactive.c: implement list_modified\n>        \n>       -    Add new files: add-interactive.c and add-interactive.h, which\n>       -    will be used for implementing \"application logic\" of git add -i,\n>       -    whereas add--helper.c will be used mostly for parsing the command line.\n>       -    We're a bit lax with the command-line parsing, as the command is\n>       -    intended to be called only by one internal user: the add--interactive script.\n>       +    Implement list_modified from Perl, which will be used\n>       +    by most of the *_cmd functions, including status in the\n>       +    following commit.\n>        \n>       -    Implement add --interactive's status command in add-interactive.c and\n>       -    use it in builtin add--helper.c.\n>       -\n>       -    It prints a numstat comparing changed files between a) the worktree and\n>       +    It lists a numstat comparing changed files between a) the worktree and\n>            the index; b) the index and the HEAD.\n>        \n>            To do so, we use run_diff_index() and run_diff_files() to get changed\n>       @@ -19,11 +14,13 @@\n>            combination of a hashmap and qsort() to print the result in\n>            O(n) + O(n lg n) complexity.\n>        \n>       -    This is the first interactive add command implemented in C of those\n>       -    anticipated by the previous commit, which introduced\n>       -    the add--helper built-in.\n>       +    Add new file: add-interactive.c which will be used for implementing\n>       +    \"application logic\" of git add -i (alongside with add-interactive.h,\n>       +    added in later commit), whereas add--helper.c will be used mostly\n>       +    for parsing the command line.\n>        \n>       -    Signed-off-by: Daniel Ferreira <bnmvco@gmail.com>\n>       +    Mentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n>       +    Original-patch-by: Daniel Ferreira <bnmvco@gmail.com>\n>            Signed-off-by: Slavica Djukic <slawica92@hotmail.com>\n>        \n>         diff --git a/Makefile b/Makefile\n>       @@ -43,7 +40,6 @@\n>         --- /dev/null\n>         +++ b/add-interactive.c\n>        @@\n>       -+#include \"add-interactive.h\"\n>        +#include \"cache.h\"\n>        +#include \"commit.h\"\n>        +#include \"color.h\"\n>       @@ -51,8 +47,6 @@\n>        +#include \"diffcore.h\"\n>        +#include \"revision.h\"\n>        +\n>       -+#define HEADER_INDENT \"      \"\n>       -+\n>        +enum collection_phase {\n>        +\tWORKTREE,\n>        +\tINDEX\n>       @@ -75,66 +69,8 @@\n>        +\tstruct hashmap file_map;\n>        +};\n>        +\n>       -+static int use_color = -1;\n>       -+enum color_add_i {\n>       -+\tCOLOR_PROMPT,\n>       -+\tCOLOR_HEADER,\n>       -+\tCOLOR_HELP,\n>       -+\tCOLOR_ERROR\n>       -+};\n>       -+\n>       -+static char colors[][COLOR_MAXLEN] = {\n>       -+\tGIT_COLOR_BOLD_BLUE, /* Prompt */\n>       -+\tGIT_COLOR_BOLD,      /* Header */\n>       -+\tGIT_COLOR_BOLD_RED,  /* Help */\n>       -+\tGIT_COLOR_BOLD_RED   /* Error */\n>       -+};\n>       -+\n>       -+static const char *get_color(enum color_add_i ix)\n>       -+{\n>       -+\tif (want_color(use_color))\n>       -+\t\treturn colors[ix];\n>       -+\treturn \"\";\n>       -+}\n>       -+\n>       -+static int parse_color_slot(const char *slot)\n>       -+{\n>       -+\tif (!strcasecmp(slot, \"prompt\"))\n>       -+\t\treturn COLOR_PROMPT;\n>       -+\tif (!strcasecmp(slot, \"header\"))\n>       -+\t\treturn COLOR_HEADER;\n>       -+\tif (!strcasecmp(slot, \"help\"))\n>       -+\t\treturn COLOR_HELP;\n>       -+\tif (!strcasecmp(slot, \"error\"))\n>       -+\t\treturn COLOR_ERROR;\n>       -+\n>       -+\treturn -1;\n>       -+}\n>       -+\n>       -+int add_i_config(const char *var,\n>       -+\t\tconst char *value, void *cbdata)\n>       -+{\n>       -+\tconst char *name;\n>       -+\n>       -+\tif (!strcmp(var, \"color.interactive\")) {\n>       -+\t\tuse_color = git_config_colorbool(var, value);\n>       -+\t\treturn 0;\n>       -+\t}\n>       -+\n>       -+\tif (skip_prefix(var, \"color.interactive.\", &name)) {\n>       -+\t\tint slot = parse_color_slot(name);\n>       -+\t\tif (slot < 0)\n>       -+\t\t\treturn 0;\n>       -+\t\tif (!value)\n>       -+\t\t\treturn config_error_nonbool(var);\n>       -+\t\treturn color_parse(value, colors[slot]);\n>       -+\t}\n>       -+\n>       -+\treturn git_default_config(var, value, cbdata);\n>       -+}\n>       -+\n>        +static int hash_cmp(const void *unused_cmp_data, const void *entry,\n>       -+\t\t\tconst void *entry_or_key, const void *keydata)\n>       ++\t\t    const void *entry_or_key, const void *keydata)\n>        +{\n>        +\tconst struct file_stat *e1 = entry, *e2 = entry_or_key;\n>        +\tconst char *name = keydata ? keydata : e2->name;\n>       @@ -151,8 +87,8 @@\n>        +}\n>        +\n>        +static void collect_changes_cb(struct diff_queue_struct *q,\n>       -+\t\t\t\t\t struct diff_options *options,\n>       -+\t\t\t\t\t void *data)\n>       ++\t\t\t       struct diff_options *options,\n>       ++\t\t\t       void *data)\n>        +{\n>        +\tstruct collection_status *s = data;\n>        +\tstruct diffstat_t stat = { 0 };\n>       @@ -222,128 +158,73 @@\n>        +\trun_diff_index(&rev, 1);\n>        +}\n>        +\n>       -+void add_i_print_modified(void)\n>       ++static int is_inital_commit(void)\n>        +{\n>       -+\tint i = 0;\n>       -+\tstruct collection_status s;\n>       -+\t/* TRANSLATORS: you can adjust this to align \"git add -i\" status menu */\n>       -+\tconst char *modified_fmt = _(\"%12s %12s %s\");\n>       -+\tconst char *header_color = get_color(COLOR_HEADER);\n>        +\tstruct object_id sha1;\n>       ++\tif (get_oid(\"HEAD\", &sha1))\n>       ++\t\treturn 1;\n>       ++\treturn 0;\n>       ++}\n>       ++\n>       ++static const char *get_diff_reference(void)\n>       ++{\n>       ++\tif(is_inital_commit())\n>       ++\t\treturn empty_tree_oid_hex();\n>       ++\treturn \"HEAD\";\n>       ++}\n>       ++\n>       ++static void filter_files(const char *filter, struct hashmap *file_map,\n>       ++\t\t\t struct file_stat **files)\n>       ++{\n>       ++\n>       ++\tfor (int i = 0; i < hashmap_get_size(file_map); i++) {\n>       ++\t\tstruct file_stat *f = files[i];\n>       ++\n>       ++\t\tif ((!(f->worktree.added || f->worktree.deleted)) &&\n>       ++\t\t   (!strcmp(filter, \"file-only\")))\n>       ++\t\t\t\thashmap_remove(file_map, f, NULL);\n>        +\n>       ++\t\tif ((!(f->index.added || f->index.deleted)) &&\n>       ++\t\t   (!strcmp(filter, \"index-only\")))\n>       ++\t\t\t\thashmap_remove(file_map, f, NULL);\n>       ++\t}\n>       ++}\n>       ++\n>       ++static struct collection_status *list_modified(struct repository *r, const char *filter)\n>       ++{\n>       ++\tint i = 0;\n>       ++\tstruct collection_status *s = xcalloc(1, sizeof(*s));\n>        +\tstruct hashmap_iter iter;\n>        +\tstruct file_stat **files;\n>        +\tstruct file_stat *entry;\n>        +\n>       -+\tif (read_cache() < 0)\n>       -+\t\treturn;\n>       ++\tif (repo_read_index(r) < 0) {\n>       ++\t\tprintf(\"\\n\");\n>       ++\t\treturn NULL;\n>       ++\t}\n>        +\n>       -+\ts.reference = !get_oid(\"HEAD\", &sha1) ? \"HEAD\": empty_tree_oid_hex();\n>       -+\thashmap_init(&s.file_map, hash_cmp, NULL, 0);\n>       ++\ts->reference = get_diff_reference();\n>       ++\thashmap_init(&s->file_map, hash_cmp, NULL, 0);\n>        +\n>       -+\tcollect_changes_worktree(&s);\n>       -+\tcollect_changes_index(&s);\n>       ++\tcollect_changes_worktree(s);\n>       ++\tcollect_changes_index(s);\n>        +\n>       -+\tif (hashmap_get_size(&s.file_map) < 1) {\n>       ++\tif (hashmap_get_size(&s->file_map) < 1) {\n>        +\t\tprintf(\"\\n\");\n>       -+\t\treturn;\n>       ++\t\treturn NULL;\n>        +\t}\n>        +\n>       -+\tprintf(HEADER_INDENT);\n>       -+\tcolor_fprintf(stdout, header_color, modified_fmt, _(\"staged\"),\n>       -+\t\t\t_(\"unstaged\"), _(\"path\"));\n>       -+\tprintf(\"\\n\");\n>       -+\n>       -+\thashmap_iter_init(&s.file_map, &iter);\n>       ++\thashmap_iter_init(&s->file_map, &iter);\n>        +\n>       -+\tfiles = xcalloc(hashmap_get_size(&s.file_map), sizeof(struct file_stat *));\n>       ++\tfiles = xcalloc(hashmap_get_size(&s->file_map), sizeof(struct file_stat *));\n>        +\twhile ((entry = hashmap_iter_next(&iter))) {\n>        +\t\tfiles[i++] = entry;\n>        +\t}\n>       -+\tQSORT(files, hashmap_get_size(&s.file_map), alphabetical_cmp);\n>       ++\tQSORT(files, hashmap_get_size(&s->file_map), alphabetical_cmp);\n>        +\n>       -+\tfor (i = 0; i < hashmap_get_size(&s.file_map); i++) {\n>       -+\t\tstruct file_stat *f = files[i];\n>       -+\n>       -+\t\tchar worktree_changes[50];\n>       -+\t\tchar index_changes[50];\n>       -+\n>       -+\t\tif (f->worktree.added || f->worktree.deleted)\n>       -+\t\t\tsnprintf(worktree_changes, 50, \"+%\"PRIuMAX\"/-%\"PRIuMAX, f->worktree.added,\n>       -+\t\t\t\t\tf->worktree.deleted);\n>       -+\t\telse\n>       -+\t\t\tsnprintf(worktree_changes, 50, \"%s\", _(\"nothing\"));\n>       -+\n>       -+\t\tif (f->index.added || f->index.deleted)\n>       -+\t\t\tsnprintf(index_changes, 50, \"+%\"PRIuMAX\"/-%\"PRIuMAX, f->index.added,\n>       -+\t\t\t\t\tf->index.deleted);\n>       -+\t\telse\n>       -+\t\t\tsnprintf(index_changes, 50, \"%s\", _(\"unchanged\"));\n>       -+\n>       -+\t\tprintf(\" %2d: \", i + 1);\n>       -+\t\tprintf(modified_fmt, index_changes, worktree_changes, f->name);\n>       -+\t\tprintf(\"\\n\");\n>       -+\t}\n>       -+\tprintf(\"\\n\");\n>       ++\tif (filter)\n>       ++\t\tfilter_files(filter, &s->file_map, files);\n>        +\n>        +\tfree(files);\n>       -+\thashmap_free(&s.file_map, 1);\n>       ++\treturn s;\n>        +}\n>       -\n>       - diff --git a/add-interactive.h b/add-interactive.h\n>       - new file mode 100644\n>       - --- /dev/null\n>       - +++ b/add-interactive.h\n>       -@@\n>       -+#ifndef ADD_INTERACTIVE_H\n>       -+#define ADD_INTERACTIVE_H\n>       -+\n>       -+int add_i_config(const char *var, const char *value, void *cbdata);\n>       -+\n>       -+void add_i_print_modified(void);\n>       -+\n>       -+#endif\n>       - \\ No newline at end of file\n>       -\n>       - diff --git a/builtin/add--helper.c b/builtin/add--helper.c\n>       - --- a/builtin/add--helper.c\n>       - +++ b/builtin/add--helper.c\n>       -@@\n>       -+#include \"add-interactive.h\"\n>       - #include \"builtin.h\"\n>       -+#include \"config.h\"\n>       -+#include \"revision.h\"\n>       -+\n>       -+static const char * const builtin_add_helper_usage[] = {\n>       -+\tN_(\"git add-interactive--helper <command>\"),\n>       -+\tNULL\n>       -+};\n>       -+\n>       -+enum cmd_mode {\n>       -+\tDEFAULT = 0,\n>       -+\tSTATUS\n>       -+};\n>       -\n>       - int cmd_add__helper(int argc, const char **argv, const char *prefix)\n>       - {\n>       -+\tenum cmd_mode mode = DEFAULT;\n>       -+\n>       -+\tstruct option options[] = {\n>       -+\t\tOPT_CMDMODE(0, \"status\", &mode,\n>       -+\t\t\t N_(\"print status information with diffstat\"), STATUS),\n>       -+\t\tOPT_END()\n>       -+\t};\n>       -+\n>       -+\tgit_config(add_i_config, NULL);\n>       -+\targc = parse_options(argc, argv, NULL, options,\n>       -+\t\t\t     builtin_add_helper_usage,\n>       -+\t\t\t     PARSE_OPT_KEEP_ARGV0);\n>       -+\n>       -+\tif (mode == STATUS)\n>       -+\t\tadd_i_print_modified();\n>       -+\telse\n>       -+\t\tusage_with_options(builtin_add_helper_usage,\n>       -+\t\t\t\t   options);\n>       -+\n>       - \treturn 0;\n>       - }\n>    -:  ---------- >  4:  a97b29d274 add-interactive.c: implement list_and_choose\n>    -:  ---------- >  5:  9a72aabe6c add-interactive.c: implement status command\n>    4:  fb3f9378ac !  6:  883963ee6e add--interactive.perl: use add--helper --status for status_cmd\n>       @@ -14,6 +14,7 @@\n>            the fact that it would be hard to test, we'll pass on adding\n>            a regression test for this.\n>        \n>       +    Mentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n>            Signed-off-by: Daniel Ferreira <bnmvco@gmail.com>\n>            Signed-off-by: Slavica Djukic <slawica92@hotmail.com>\n>            Signed-off-by: Ã†var ArnfjÃ¶rÃ° Bjarmason <avarab@gmail.com>\n>    -:  ---------- >  7:  7912f37517 add-interactive.c: add support for list_only option\n>    5:  ab16afd1d5 !  8:  441321fc3d add-interactive.c: implement show-help command\n>       @@ -10,31 +10,32 @@\n>            handle_builtin and re-routed to the help command, without ever\n>            calling cmd_add__helper().\n>        \n>       +    Mentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n>            Signed-off-by: Slavica Djukic <slawica92@hotmail.com>\n>        \n>         diff --git a/add-interactive.c b/add-interactive.c\n>         --- a/add-interactive.c\n>         +++ b/add-interactive.c\n>        @@\n>       - \tfree(files);\n>       - \thashmap_free(&s.file_map, 1);\n>       + \tfree(s);\n>       + \tfree_choices(&choices);\n>         }\n>        +\n>        +void add_i_show_help(void)\n>        +{\n>        +\tconst char *help_color = get_color(COLOR_HELP);\n>        +\tcolor_fprintf_ln(stdout, help_color, \"status        - %s\",\n>       -+\t\t\t_(\"show paths with changes\"));\n>       ++\t\t\t _(\"show paths with changes\"));\n>        +\tcolor_fprintf_ln(stdout, help_color, \"update        - %s\",\n>       -+\t\t\t_(\"add working tree state to the staged set of changes\"));\n>       ++\t\t\t _(\"add working tree state to the staged set of changes\"));\n>        +\tcolor_fprintf_ln(stdout, help_color, \"revert        - %s\",\n>       -+\t\t\t_(\"revert staged set of changes back to the HEAD version\"));\n>       ++\t\t\t _(\"revert staged set of changes back to the HEAD version\"));\n>        +\tcolor_fprintf_ln(stdout, help_color, \"patch         - %s\",\n>       -+\t\t\t_(\"pick hunks and update selectively\"));\n>       ++\t\t\t _(\"pick hunks and update selectively\"));\n>        +\tcolor_fprintf_ln(stdout, help_color, \"diff          - %s\",\n>       -+\t\t\t_(\"view diff between HEAD and index\"));\n>       ++\t\t\t _(\"view diff between HEAD and index\"));\n>        +\tcolor_fprintf_ln(stdout, help_color, \"add untracked - %s\",\n>       -+\t\t\t_(\"add contents of untracked files to the staged set of changes\"));\n>       ++\t\t\t _(\"add contents of untracked files to the staged set of changes\"));\n>        +}\n>        \n>         diff --git a/add-interactive.h b/add-interactive.h\n>       @@ -42,13 +43,11 @@\n>         +++ b/add-interactive.h\n>        @@\n>         \n>       - void add_i_print_modified(void);\n>       + void add_i_status(void);\n>         \n>       --#endif\n>       - \\ No newline at end of file\n>        +void add_i_show_help(void);\n>        +\n>       -+#endif\n>       + #endif\n>        \n>         diff --git a/builtin/add--helper.c b/builtin/add--helper.c\n>         --- a/builtin/add--helper.c\n>       @@ -66,16 +65,16 @@\n>        @@\n>         \tstruct option options[] = {\n>         \t\tOPT_CMDMODE(0, \"status\", &mode,\n>       - \t\t\t N_(\"print status information with diffstat\"), STATUS),\n>       + \t\t\t    N_(\"print status information with diffstat\"), STATUS),\n>        +\t\tOPT_CMDMODE(0, \"show-help\", &mode,\n>       -+\t\t\t N_(\"show help\"), HELP),\n>       ++\t\t\t    N_(\"show help\"), HELP),\n>         \t\tOPT_END()\n>         \t};\n>         \n>        @@\n>         \n>         \tif (mode == STATUS)\n>       - \t\tadd_i_print_modified();\n>       + \t\tadd_i_status();\n>        +\telse if (mode == HELP)\n>        +\t\tadd_i_show_help();\n>         \telse\n>    6:  0a27304a84 !  9:  315ae8b211 t3701-add-interactive: test add_i_show_help()\n>       @@ -11,6 +11,7 @@\n>            Prefix git add -i call with GIT_PAGER_IN_USE=true TERM=vt100\n>            to force colored output on Windows.\n>        \n>       +    Mentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n>            Signed-off-by: Slavica Djukic <slawica92@hotmail.com>\n>        \n>         diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh\n>    7:  ca2a7c4375 ! 10:  2b4bdce730 add--interactive.perl: use add--helper --show-help for help_cmd\n>       @@ -12,6 +12,7 @@\n>            to print the numstat, also here we forgo adding a regression test:\n>            the Perl script is on its way out (and this patch is part of that journey).\n>        \n>       +    Mentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n>            Signed-off-by: Slavica Djukic <slawica92@hotmail.com>\n>            Signed-off-by: Ã†var ArnfjÃ¶rÃ° Bjarmason <avarab@gmail.com>\n>        \n>\n"},{"id":"371007","messageId":"nycvar.QRO.7.76.6.1903082138350.41@tvgsbejvaqbjf.bet","threadId":"50075","inReplyTo":"xmqqzhqpt483.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v5 02/10] add--helper: create builtin helper for interactive add","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-03-08T20:48:01Z","receivedAt":"2019-03-08T20:48:30Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Thu, 21 Feb 2019, Junio C Hamano wrote:\n\n> \"Daniel Ferreira via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n> \n> > diff --git a/git.c b/git.c\n> > index 2dd588674f..cb42591f37 100644\n> > --- a/git.c\n> > +++ b/git.c\n> > @@ -444,6 +444,7 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)\n> >  \n> >  static struct cmd_struct commands[] = {\n> >  \t{ \"add\", cmd_add, RUN_SETUP | NEED_WORK_TREE },\n> > +\t{ \"add--helper\", cmd_add__helper, RUN_SETUP | NEED_WORK_TREE },\n> >  \t{ \"am\", cmd_am, RUN_SETUP | NEED_WORK_TREE },\n> >  \t{ \"annotate\", cmd_annotate, RUN_SETUP | NO_PARSEOPT },\n> >  \t{ \"apply\", cmd_apply, RUN_SETUP_GENTLY },\n> \n> When adding a more complex replacement command we often cannot write\n> RUN_SETUP nor NEED_WORK_TREE here and instead do the job ourselves\n> in cmd_ourcommand() the hard way.  But because this one is only for\n> helping \"add -i\", we can let the git.c::run_builtin() aka \"git\n> potty\" do the hard part, which is very nice.\n\nSadly, I ran into a quite curious (and seemingly equally insurmountable)\nproblem with this approach: when I use `system()` to execute a Git\nbuilt-in from the Perl script, and when that built-in then consumes even a\nsingle byte of stdin, that file descriptor gets closed in the Perl\nprocess, and no amount of file descriptor duplication and redirecting that\nI could come up helped. After calling the built-in helper to run, say, the\n`update` command, the main interactive add process always exited on me due\nto EOF on stdin, and I could not make it work.\n\nTo be sure, this happens only with MSYS2's Perl. If I try the same on\nLinux (WSL, really), everything works Just Fine.\n\nSo my current approach is actually the other way round: I started\nimplementing the main loop in C, and implemented all commands except for\n`patch` so far, and I think I will just hand off to the Perl script for\nthat command for now.\n\nUnfortunately that means that my current state looks already quite a bit\ndifferent than what you reviewed. My hope is, however, that it will make\nfor a nice \"story\" once I have a version to show that I think could be\napplied as-is.\n\nCiao,\nDscho\n"}]}