{"thread":{"id":"40984","subject":"[PATCH v7] Add git-grep threads param","startedAt":"2015-12-13T15:17:36Z","lastAt":"2015-12-13T19:25:06Z","messageCount":3,"participants":["Victor Leschuk","Eric Sunshine"],"isPatch":true,"patchVersion":7,"patchTotal":null},"messages":[{"id":"274347","messageId":"1450019856-14609-1-git-send-email-vleschuk@accesssoftek.com","threadId":"40984","inReplyTo":null,"subject":"[PATCH v7] Add git-grep threads param","fromName":"Victor Leschuk","fromEmail":"vleschuk@gmail.com","sentAt":"2015-12-13T15:17:36Z","receivedAt":"2015-12-13T15:17:36Z","isPatch":true,"sender":{"key":"vleschuk@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1045374?v=4"},"body":" \"git grep\" can now be configured (or told from the command line)\n how many threads to use when searching in the working tree files.\n\n Changes to default behavior: number of threads now doesn't depend\n on online_cpus(), e.g. if specific number is not configured\n GREP_NUM_THREADS_DEFAULT (8) threads will be used even on 1-core CPU.\n\n Reason: multithreading can improve performance even on single core machines\n as IO is also a major factor here. Using multiple threads can significantly\n boost grep performance when working on slow filesystems (or repo isn't cached)\n or through network (for example repo is located on NFS).\n\nSigned-off-by: Victor Leschuk <vleschuk@accesssoftek.com>\n---\nHistory of changes from the first version ($gmane/280053/:\n\t* Param renamed from threads-num to threads\n\t* Short version of '--threads' cmd key was removed\n\t* Made num_threads 'decision-tree' more obvious \n\t  and easy to edit for future use ($gmane/280089)\n\t* Moved option description to more suitable place in documentation ($gmane/280188)\n\t* Hid threads param from 'external' grep.c, made it private for builtin/grep.c ($gmane/280188)\n\t* Improved num_threads 'decision-tree', got rid of dependency on online_cpus ($gmane/280299)\n\t* Improved param documentation ($gmane/280299)\n\t* Fixed broken t7811: moved all threads_num setup to 1 place (for -O option it was in wrong place) ($gmane/281160)\n\t* Fixed 'invalid number of threads' message so that it could be translated ($gmane/281160)\n\t* Got rid of grep_threads_config() - its too trivial to be separate function ($gmane/281160)\n\t* Fixed xcalloc() args (sizeof(pthread_t) -> sizeof(*threads)) to correspond to general git style ($gmane/281160)\n\t* Improved commit message to explain why online_cpus() is now not used in threads_num setup ($gmane/281160)\n\n\n Documentation/config.txt               |  7 ++++++\n Documentation/git-grep.txt             | 15 ++++++++++++\n builtin/grep.c                         | 42 ++++++++++++++++++++++------------\n contrib/completion/git-completion.bash |  1 +\n 4 files changed, 50 insertions(+), 15 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 2d06b11..687b9ad 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1450,6 +1450,13 @@ grep.extendedRegexp::\n \toption is ignored when the 'grep.patternType' option is set to a value\n \tother than 'default'.\n \n+grep.threads::\n+\tNumber of grep worker threads, use it to tune up performance on\n+\tyour machines. Leave it unset (or set to 0) for default behavior,\n+\twhich for now is using 8 threads for all systems.\n+\tDefault behavior can be changed in future versions\n+\tto better suit hardware and circumstances.\n+\n gpg.program::\n \tUse this custom program instead of \"gpg\" found on $PATH when\n \tmaking or verifying a PGP signature. The program must support the\ndiff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt\nindex 4a44d6d..8222a83 100644\n--- a/Documentation/git-grep.txt\n+++ b/Documentation/git-grep.txt\n@@ -23,6 +23,7 @@ SYNOPSIS\n \t   [--break] [--heading] [-p | --show-function]\n \t   [-A <post-context>] [-B <pre-context>] [-C <context>]\n \t   [-W | --function-context]\n+\t   [--threads <num>]\n \t   [-f <file>] [-e] <pattern>\n \t   [--and|--or|--not|(|)|-e <pattern>...]\n \t   [ [--[no-]exclude-standard] [--cached | --no-index | --untracked] | <tree>...]\n@@ -53,6 +54,13 @@ grep.extendedRegexp::\n \toption is ignored when the 'grep.patternType' option is set to a value\n \tother than 'default'.\n \n+grep.threads::\n+\tNumber of grep worker threads, use it to tune up performance on\n+\tyour machines. Leave it unset (or set to 0) for default behavior,\n+\twhich for now is using 8 threads for all systems.\n+\tDefault behavior can be changed in future versions\n+\tto better suit hardware and circumstances.\n+\n grep.fullName::\n \tIf set to true, enable '--full-name' option by default.\n \n@@ -227,6 +235,13 @@ OPTIONS\n \teffectively showing the whole function in which the match was\n \tfound.\n \n+--threads <num>::\n+\tNumber of grep worker threads, use it to tune up performance on\n+\tyour machines. Leave it unset (or set to 0) for default behavior,\n+\twhich for now is using 8 threads for all systems.\n+\tDefault behavior can be changed in future versions\n+\tto better suit hardware and circumstances.\n+\n -f <file>::\n \tRead patterns from <file>, one per line.\n \ndiff --git a/builtin/grep.c b/builtin/grep.c\nindex 4229cae..756b6af 100644\n--- a/builtin/grep.c\n+++ b/builtin/grep.c\n@@ -24,11 +24,11 @@ static char const * const grep_usage[] = {\n \tNULL\n };\n \n-static int use_threads = 1;\n+#define GREP_NUM_THREADS_DEFAULT 8\n+static int num_threads = 0;\n \n #ifndef NO_PTHREADS\n-#define THREADS 8\n-static pthread_t threads[THREADS];\n+static pthread_t *threads;\n \n /* We use one producer thread and THREADS consumer\n  * threads. The producer adds struct work_items to 'todo' and the\n@@ -63,13 +63,13 @@ static pthread_mutex_t grep_mutex;\n \n static inline void grep_lock(void)\n {\n-\tif (use_threads)\n+\tif (num_threads)\n \t\tpthread_mutex_lock(&grep_mutex);\n }\n \n static inline void grep_unlock(void)\n {\n-\tif (use_threads)\n+\tif (num_threads)\n \t\tpthread_mutex_unlock(&grep_mutex);\n }\n \n@@ -206,7 +206,8 @@ static void start_threads(struct grep_opt *opt)\n \t\tstrbuf_init(&todo[i].out, 0);\n \t}\n \n-\tfor (i = 0; i < ARRAY_SIZE(threads); i++) {\n+\tthreads = xcalloc(num_threads, sizeof(*threads));\n+\tfor (i = 0; i < num_threads; i++) {\n \t\tint err;\n \t\tstruct grep_opt *o = grep_opt_dup(opt);\n \t\to->output = strbuf_out;\n@@ -238,12 +239,14 @@ static int wait_all(void)\n \tpthread_cond_broadcast(&cond_add);\n \tgrep_unlock();\n \n-\tfor (i = 0; i < ARRAY_SIZE(threads); i++) {\n+\tfor (i = 0; i < num_threads; i++) {\n \t\tvoid *h;\n \t\tpthread_join(threads[i], &h);\n \t\thit |= (int) (intptr_t) h;\n \t}\n \n+\tfree(threads);\n+\n \tpthread_mutex_destroy(&grep_mutex);\n \tpthread_mutex_destroy(&grep_read_mutex);\n \tpthread_mutex_destroy(&grep_attr_mutex);\n@@ -267,6 +270,10 @@ static int grep_cmd_config(const char *var, const char *value, void *cb)\n \tint st = grep_config(var, value, cb);\n \tif (git_color_default_config(var, value, cb) < 0)\n \t\tst = -1;\n+\n+\tif (!strcmp(var, \"grep.threads\"))\n+\t\tnum_threads = git_config_int(var, value); /* Sanity check of value will be perfomed later */\n+\n \treturn st;\n }\n \n@@ -294,7 +301,7 @@ static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1,\n \t}\n \n #ifndef NO_PTHREADS\n-\tif (use_threads) {\n+\tif (num_threads) {\n \t\tadd_work(opt, GREP_SOURCE_SHA1, pathbuf.buf, path, sha1);\n \t\tstrbuf_release(&pathbuf);\n \t\treturn 0;\n@@ -323,7 +330,7 @@ static int grep_file(struct grep_opt *opt, const char *filename)\n \t\tstrbuf_addstr(&buf, filename);\n \n #ifndef NO_PTHREADS\n-\tif (use_threads) {\n+\tif (num_threads) {\n \t\tadd_work(opt, GREP_SOURCE_FILE, buf.buf, filename, filename);\n \t\tstrbuf_release(&buf);\n \t\treturn 0;\n@@ -697,6 +704,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n \t\t\tN_(\"show <n> context lines before matches\")),\n \t\tOPT_INTEGER('A', \"after-context\", &opt.post_context,\n \t\t\tN_(\"show <n> context lines after matches\")),\n+\t\tOPT_INTEGER(0, \"threads\", &num_threads,\n+\t\t\tN_(\"use <n> worker threads\")),\n \t\tOPT_NUMBER_CALLBACK(&opt, N_(\"shortcut for -C NUM\"),\n \t\t\tcontext_callback),\n \t\tOPT_BOOL('p', \"show-function\", &opt.funcname,\n@@ -786,7 +795,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n \t\topt.output_priv = &path_list;\n \t\topt.output = append_path;\n \t\tstring_list_append(&path_list, show_in_pager);\n-\t\tuse_threads = 0;\n \t}\n \n \tif (!opt.pattern_list)\n@@ -817,14 +825,18 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n \t}\n \n #ifndef NO_PTHREADS\n-\tif (list.nr || cached || online_cpus() == 1)\n-\t\tuse_threads = 0;\n+\tif (list.nr || cached || show_in_pager)\n+\t\tnum_threads = 0; /* Can not multi-thread object lookup */\n+\telse if (num_threads == 0)\n+\t\tnum_threads = GREP_NUM_THREADS_DEFAULT; /* User didn't specify value, or just wants default behavior */\n+\telse if (num_threads < 0)\n+\t\tdie(_(\"invalid number of threads specified (%d)\"), num_threads);\n #else\n-\tuse_threads = 0;\n+\tnum_threads = 0;\n #endif\n \n #ifndef NO_PTHREADS\n-\tif (use_threads) {\n+\tif (num_threads) {\n \t\tif (!(opt.name_only || opt.unmatch_name_only || opt.count)\n \t\t    && (opt.pre_context || opt.post_context ||\n \t\t\topt.file_break || opt.funcbody))\n@@ -894,7 +906,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n \t\thit = grep_objects(&opt, &pathspec, &list);\n \t}\n \n-\tif (use_threads)\n+\tif (num_threads)\n \t\thit |= wait_all();\n \tif (hit && show_in_pager)\n \t\trun_pager(&opt, prefix);\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 111b053..d5c3e3f 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -1311,6 +1311,7 @@ _git_grep ()\n \t\t\t--full-name --line-number\n \t\t\t--extended-regexp --basic-regexp --fixed-strings\n \t\t\t--perl-regexp\n+\t\t\t--threads\n \t\t\t--files-with-matches --name-only\n \t\t\t--files-without-match\n \t\t\t--max-depth\n-- \n2.6.3.369.g3e7f205.dirty\n"},{"id":"274357","messageId":"CAPig+cTybb9d4enQjCuAefexcP=rPG2SF3VvvB78SUW1UmkuQw@mail.gmail.com","threadId":"40984","inReplyTo":"1450019856-14609-1-git-send-email-vleschuk@accesssoftek.com","subject":"Re: [PATCH v7] Add git-grep threads param","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-12-13T19:13:45Z","receivedAt":"2015-12-13T19:13:45Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sun, Dec 13, 2015 at 10:17 AM, Victor Leschuk <vleschuk@gmail.com> wrote:\n>  \"git grep\" can now be configured (or told from the command line)\n>  how many threads to use when searching in the working tree files.\n>\n>  Changes to default behavior: number of threads now doesn't depend\n>  on online_cpus(), e.g. if specific number is not configured\n>  GREP_NUM_THREADS_DEFAULT (8) threads will be used even on 1-core CPU.\n>\n>  Reason: multithreading can improve performance even on single core machines\n>  as IO is also a major factor here. Using multiple threads can significantly\n>  boost grep performance when working on slow filesystems (or repo isn't cached)\n>  or through network (for example repo is located on NFS).\n>\n> Signed-off-by: Victor Leschuk <vleschuk@accesssoftek.com>\n> ---\n\nHmm, v7 doesn't seem to address any of the v6 review comments here[1].\nWas that review merely overlooked or did you disagree with the\nreviewer?\n\n[1]: http://article.gmane.org/gmane.comp.version-control.git/281817\n\n> History of changes from the first version ($gmane/280053/:\n\nIt's generally more helpful to reviewers if you provide a link to the\nprevious version and describe the changes since that version. (If you\nprefer, it's also okay to describe all changes, but they should be\norganized by version so that it's obvious which changes were made in\nwhich version.)\n\n>         * Param renamed from threads-num to threads\n>         * Short version of '--threads' cmd key was removed\n>         * Made num_threads 'decision-tree' more obvious\n>           and easy to edit for future use ($gmane/280089)\n>         * Moved option description to more suitable place in documentation ($gmane/280188)\n>         * Hid threads param from 'external' grep.c, made it private for builtin/grep.c ($gmane/280188)\n>         * Improved num_threads 'decision-tree', got rid of dependency on online_cpus ($gmane/280299)\n>         * Improved param documentation ($gmane/280299)\n>         * Fixed broken t7811: moved all threads_num setup to 1 place (for -O option it was in wrong place) ($gmane/281160)\n>         * Fixed 'invalid number of threads' message so that it could be translated ($gmane/281160)\n>         * Got rid of grep_threads_config() - its too trivial to be separate function ($gmane/281160)\n>         * Fixed xcalloc() args (sizeof(pthread_t) -> sizeof(*threads)) to correspond to general git style ($gmane/281160)\n>         * Improved commit message to explain why online_cpus() is now not used in threads_num setup ($gmane/281160)\n>\n> diff --git a/Documentation/config.txt b/Documentation/config.txt\n> index 2d06b11..687b9ad 100644\n> --- a/Documentation/config.txt\n> +++ b/Documentation/config.txt\n> @@ -1450,6 +1450,13 @@ grep.extendedRegexp::\n>         option is ignored when the 'grep.patternType' option is set to a value\n>         other than 'default'.\n>\n> +grep.threads::\n> +       Number of grep worker threads, use it to tune up performance on\n> +       your machines. Leave it unset (or set to 0) for default behavior,\n> +       which for now is using 8 threads for all systems.\n> +       Default behavior can be changed in future versions\n> +       to better suit hardware and circumstances.\n> +\n>  gpg.program::\n>         Use this custom program instead of \"gpg\" found on $PATH when\n>         making or verifying a PGP signature. The program must support the\n> diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt\n> index 4a44d6d..8222a83 100644\n> --- a/Documentation/git-grep.txt\n> +++ b/Documentation/git-grep.txt\n> @@ -23,6 +23,7 @@ SYNOPSIS\n>            [--break] [--heading] [-p | --show-function]\n>            [-A <post-context>] [-B <pre-context>] [-C <context>]\n>            [-W | --function-context]\n> +          [--threads <num>]\n>            [-f <file>] [-e] <pattern>\n>            [--and|--or|--not|(|)|-e <pattern>...]\n>            [ [--[no-]exclude-standard] [--cached | --no-index | --untracked] | <tree>...]\n> @@ -53,6 +54,13 @@ grep.extendedRegexp::\n>         option is ignored when the 'grep.patternType' option is set to a value\n>         other than 'default'.\n>\n> +grep.threads::\n> +       Number of grep worker threads, use it to tune up performance on\n> +       your machines. Leave it unset (or set to 0) for default behavior,\n> +       which for now is using 8 threads for all systems.\n> +       Default behavior can be changed in future versions\n> +       to better suit hardware and circumstances.\n> +\n>  grep.fullName::\n>         If set to true, enable '--full-name' option by default.\n>\n> @@ -227,6 +235,13 @@ OPTIONS\n>         effectively showing the whole function in which the match was\n>         found.\n>\n> +--threads <num>::\n> +       Number of grep worker threads, use it to tune up performance on\n> +       your machines. Leave it unset (or set to 0) for default behavior,\n> +       which for now is using 8 threads for all systems.\n> +       Default behavior can be changed in future versions\n> +       to better suit hardware and circumstances.\n> +\n>  -f <file>::\n>         Read patterns from <file>, one per line.\n>\n> diff --git a/builtin/grep.c b/builtin/grep.c\n> index 4229cae..756b6af 100644\n> --- a/builtin/grep.c\n> +++ b/builtin/grep.c\n> @@ -24,11 +24,11 @@ static char const * const grep_usage[] = {\n>         NULL\n>  };\n>\n> -static int use_threads = 1;\n> +#define GREP_NUM_THREADS_DEFAULT 8\n> +static int num_threads = 0;\n>\n>  #ifndef NO_PTHREADS\n> -#define THREADS 8\n> -static pthread_t threads[THREADS];\n> +static pthread_t *threads;\n>\n>  /* We use one producer thread and THREADS consumer\n>   * threads. The producer adds struct work_items to 'todo' and the\n> @@ -63,13 +63,13 @@ static pthread_mutex_t grep_mutex;\n>\n>  static inline void grep_lock(void)\n>  {\n> -       if (use_threads)\n> +       if (num_threads)\n>                 pthread_mutex_lock(&grep_mutex);\n>  }\n>\n>  static inline void grep_unlock(void)\n>  {\n> -       if (use_threads)\n> +       if (num_threads)\n>                 pthread_mutex_unlock(&grep_mutex);\n>  }\n>\n> @@ -206,7 +206,8 @@ static void start_threads(struct grep_opt *opt)\n>                 strbuf_init(&todo[i].out, 0);\n>         }\n>\n> -       for (i = 0; i < ARRAY_SIZE(threads); i++) {\n> +       threads = xcalloc(num_threads, sizeof(*threads));\n> +       for (i = 0; i < num_threads; i++) {\n>                 int err;\n>                 struct grep_opt *o = grep_opt_dup(opt);\n>                 o->output = strbuf_out;\n> @@ -238,12 +239,14 @@ static int wait_all(void)\n>         pthread_cond_broadcast(&cond_add);\n>         grep_unlock();\n>\n> -       for (i = 0; i < ARRAY_SIZE(threads); i++) {\n> +       for (i = 0; i < num_threads; i++) {\n>                 void *h;\n>                 pthread_join(threads[i], &h);\n>                 hit |= (int) (intptr_t) h;\n>         }\n>\n> +       free(threads);\n> +\n>         pthread_mutex_destroy(&grep_mutex);\n>         pthread_mutex_destroy(&grep_read_mutex);\n>         pthread_mutex_destroy(&grep_attr_mutex);\n> @@ -267,6 +270,10 @@ static int grep_cmd_config(const char *var, const char *value, void *cb)\n>         int st = grep_config(var, value, cb);\n>         if (git_color_default_config(var, value, cb) < 0)\n>                 st = -1;\n> +\n> +       if (!strcmp(var, \"grep.threads\"))\n> +               num_threads = git_config_int(var, value); /* Sanity check of value will be perfomed later */\n> +\n>         return st;\n>  }\n>\n> @@ -294,7 +301,7 @@ static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1,\n>         }\n>\n>  #ifndef NO_PTHREADS\n> -       if (use_threads) {\n> +       if (num_threads) {\n>                 add_work(opt, GREP_SOURCE_SHA1, pathbuf.buf, path, sha1);\n>                 strbuf_release(&pathbuf);\n>                 return 0;\n> @@ -323,7 +330,7 @@ static int grep_file(struct grep_opt *opt, const char *filename)\n>                 strbuf_addstr(&buf, filename);\n>\n>  #ifndef NO_PTHREADS\n> -       if (use_threads) {\n> +       if (num_threads) {\n>                 add_work(opt, GREP_SOURCE_FILE, buf.buf, filename, filename);\n>                 strbuf_release(&buf);\n>                 return 0;\n> @@ -697,6 +704,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n>                         N_(\"show <n> context lines before matches\")),\n>                 OPT_INTEGER('A', \"after-context\", &opt.post_context,\n>                         N_(\"show <n> context lines after matches\")),\n> +               OPT_INTEGER(0, \"threads\", &num_threads,\n> +                       N_(\"use <n> worker threads\")),\n>                 OPT_NUMBER_CALLBACK(&opt, N_(\"shortcut for -C NUM\"),\n>                         context_callback),\n>                 OPT_BOOL('p', \"show-function\", &opt.funcname,\n> @@ -786,7 +795,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n>                 opt.output_priv = &path_list;\n>                 opt.output = append_path;\n>                 string_list_append(&path_list, show_in_pager);\n> -               use_threads = 0;\n>         }\n>\n>         if (!opt.pattern_list)\n> @@ -817,14 +825,18 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n>         }\n>\n>  #ifndef NO_PTHREADS\n> -       if (list.nr || cached || online_cpus() == 1)\n> -               use_threads = 0;\n> +       if (list.nr || cached || show_in_pager)\n> +               num_threads = 0; /* Can not multi-thread object lookup */\n> +       else if (num_threads == 0)\n> +               num_threads = GREP_NUM_THREADS_DEFAULT; /* User didn't specify value, or just wants default behavior */\n> +       else if (num_threads < 0)\n> +               die(_(\"invalid number of threads specified (%d)\"), num_threads);\n>  #else\n> -       use_threads = 0;\n> +       num_threads = 0;\n>  #endif\n>\n>  #ifndef NO_PTHREADS\n> -       if (use_threads) {\n> +       if (num_threads) {\n>                 if (!(opt.name_only || opt.unmatch_name_only || opt.count)\n>                     && (opt.pre_context || opt.post_context ||\n>                         opt.file_break || opt.funcbody))\n> @@ -894,7 +906,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n>                 hit = grep_objects(&opt, &pathspec, &list);\n>         }\n>\n> -       if (use_threads)\n> +       if (num_threads)\n>                 hit |= wait_all();\n>         if (hit && show_in_pager)\n>                 run_pager(&opt, prefix);\n> diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\n> index 111b053..d5c3e3f 100644\n> --- a/contrib/completion/git-completion.bash\n> +++ b/contrib/completion/git-completion.bash\n> @@ -1311,6 +1311,7 @@ _git_grep ()\n>                         --full-name --line-number\n>                         --extended-regexp --basic-regexp --fixed-strings\n>                         --perl-regexp\n> +                       --threads\n>                         --files-with-matches --name-only\n>                         --files-without-match\n>                         --max-depth\n> --\n> 2.6.3.369.g3e7f205.dirty\n"},{"id":"274359","messageId":"566DC612.60409@gmail.com","threadId":"40984","inReplyTo":"CAPig+cTybb9d4enQjCuAefexcP=rPG2SF3VvvB78SUW1UmkuQw@mail.gmail.com","subject":"Re: [PATCH v7] Add git-grep threads param","fromName":"Victor Leschuk","fromEmail":"vleschuk@gmail.com","sentAt":"2015-12-13T19:25:06Z","receivedAt":"2015-12-13T19:25:06Z","isPatch":true,"sender":{"key":"vleschuk@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1045374?v=4"},"body":"Hello Eric:\n> Hmm, v7 doesn't seem to address any of the v6 review comments here[1].\n> Was that review merely overlooked or did you disagree with the\n> reviewer?\n>\n> [1]: http://article.gmane.org/gmane.comp.version-control.git/281817\nSorry, it looks like I missed that letter. My bad.\n>\n>> History of changes from the first version ($gmane/280053/:\n> It's generally more helpful to reviewers if you provide a link to the\n> previous version and describe the changes since that version. (If you\n> prefer, it's also okay to describe all changes, but they should be\n> organized by version so that it's obvious which changes were made in\n> which version.)\nI will prepare v8 and list changes starting from v6. Please kindly \ndisregard this one.\n"}]}