Merge branch 'nd/ita-cleanup' into maint
authorJunio C Hamano <gitster@pobox.com>
Thu, 28 Jul 2016 18:25:51 +0000 (11:25 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 28 Jul 2016 18:25:51 +0000 (11:25 -0700)
Git does not know what the contents in the index should be for a
path added with "git add -N" yet, so "git grep --cached" should not
show hits (or show lack of hits, with -L) in such a path, but that
logic does not apply to "git grep", i.e. searching in the working
tree files. But we did so by mistake, which has been corrected.

* nd/ita-cleanup:
grep: fix grepping for "intent to add" files
t7810-grep.sh: fix a whitespace inconsistency
t7810-grep.sh: fix duplicated test name

1  2 
builtin/grep.c
t/t7810-grep.sh
diff --combined builtin/grep.c
index 462e60790165a333d58d81ec65ae4ae6da8d4f79,08548cc70df514b9172e7d1d7583f86f581ac78e..ae738312aadd48e74497368917e82f221f3abe45
@@@ -24,11 -24,11 +24,11 @@@ static char const * const grep_usage[] 
        NULL
  };
  
 -static int use_threads = 1;
 +#define GREP_NUM_THREADS_DEFAULT 8
 +static int num_threads;
  
  #ifndef NO_PTHREADS
 -#define THREADS 8
 -static pthread_t threads[THREADS];
 +static pthread_t *threads;
  
  /* We use one producer thread and THREADS consumer
   * threads. The producer adds struct work_items to 'todo' and the
@@@ -63,13 -63,13 +63,13 @@@ static pthread_mutex_t grep_mutex
  
  static inline void grep_lock(void)
  {
 -      if (use_threads)
 +      if (num_threads)
                pthread_mutex_lock(&grep_mutex);
  }
  
  static inline void grep_unlock(void)
  {
 -      if (use_threads)
 +      if (num_threads)
                pthread_mutex_unlock(&grep_mutex);
  }
  
@@@ -206,8 -206,7 +206,8 @@@ static void start_threads(struct grep_o
                strbuf_init(&todo[i].out, 0);
        }
  
 -      for (i = 0; i < ARRAY_SIZE(threads); i++) {
 +      threads = xcalloc(num_threads, sizeof(*threads));
 +      for (i = 0; i < num_threads; i++) {
                int err;
                struct grep_opt *o = grep_opt_dup(opt);
                o->output = strbuf_out;
@@@ -239,14 -238,12 +239,14 @@@ static int wait_all(void
        pthread_cond_broadcast(&cond_add);
        grep_unlock();
  
 -      for (i = 0; i < ARRAY_SIZE(threads); i++) {
 +      for (i = 0; i < num_threads; i++) {
                void *h;
                pthread_join(threads[i], &h);
                hit |= (int) (intptr_t) h;
        }
  
 +      free(threads);
 +
        pthread_mutex_destroy(&grep_mutex);
        pthread_mutex_destroy(&grep_read_mutex);
        pthread_mutex_destroy(&grep_attr_mutex);
@@@ -270,14 -267,6 +270,14 @@@ static int grep_cmd_config(const char *
        int st = grep_config(var, value, cb);
        if (git_color_default_config(var, value, cb) < 0)
                st = -1;
 +
 +      if (!strcmp(var, "grep.threads")) {
 +              num_threads = git_config_int(var, value);
 +              if (num_threads < 0)
 +                      die(_("invalid number of threads specified (%d) for %s"),
 +                          num_threads, var);
 +      }
 +
        return st;
  }
  
@@@ -305,7 -294,7 +305,7 @@@ static int grep_sha1(struct grep_opt *o
        }
  
  #ifndef NO_PTHREADS
 -      if (use_threads) {
 +      if (num_threads) {
                add_work(opt, GREP_SOURCE_SHA1, pathbuf.buf, path, sha1);
                strbuf_release(&pathbuf);
                return 0;
@@@ -334,7 -323,7 +334,7 @@@ static int grep_file(struct grep_opt *o
                strbuf_addstr(&buf, filename);
  
  #ifndef NO_PTHREADS
 -      if (use_threads) {
 +      if (num_threads) {
                add_work(opt, GREP_SOURCE_FILE, buf.buf, filename, filename);
                strbuf_release(&buf);
                return 0;
@@@ -365,17 -354,17 +365,17 @@@ static void append_path(struct grep_op
  static void run_pager(struct grep_opt *opt, const char *prefix)
  {
        struct string_list *path_list = opt->output_priv;
 -      const char **argv = xmalloc(sizeof(const char *) * (path_list->nr + 1));
 +      struct child_process child = CHILD_PROCESS_INIT;
        int i, status;
  
        for (i = 0; i < path_list->nr; i++)
 -              argv[i] = path_list->items[i].string;
 -      argv[path_list->nr] = NULL;
 +              argv_array_push(&child.args, path_list->items[i].string);
 +      child.dir = prefix;
 +      child.use_shell = 1;
  
 -      status = run_command_v_opt_cd_env(argv, RUN_USING_SHELL, prefix, NULL);
 +      status = run_command(&child);
        if (status)
                exit(status);
 -      free(argv);
  }
  
  static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int cached)
  
        for (nr = 0; nr < active_nr; nr++) {
                const struct cache_entry *ce = active_cache[nr];
-               if (!S_ISREG(ce->ce_mode) || ce_intent_to_add(ce))
+               if (!S_ISREG(ce->ce_mode))
                        continue;
                if (!ce_path_match(ce, pathspec, NULL))
                        continue;
                 * cache version instead
                 */
                if (cached || (ce->ce_flags & CE_VALID) || ce_skip_worktree(ce)) {
-                       if (ce_stage(ce))
+                       if (ce_stage(ce) || ce_intent_to_add(ce))
                                continue;
                        hit |= grep_sha1(opt, ce->sha1, ce->name, 0, ce->name);
                }
@@@ -438,7 -427,7 +438,7 @@@ static int grep_tree(struct grep_opt *o
                strbuf_add(base, entry.path, te_len);
  
                if (S_ISREG(entry.mode)) {
 -                      hit |= grep_sha1(opt, entry.sha1, base->buf, tn_len,
 +                      hit |= grep_sha1(opt, entry.oid->hash, base->buf, tn_len,
                                         check_attr ? base->buf + tn_len : NULL);
                }
                else if (S_ISDIR(entry.mode)) {
                        void *data;
                        unsigned long size;
  
 -                      data = lock_and_read_sha1_file(entry.sha1, &type, &size);
 +                      data = lock_and_read_sha1_file(entry.oid->hash, &type, &size);
                        if (!data)
                                die(_("unable to read tree (%s)"),
 -                                  sha1_to_hex(entry.sha1));
 +                                  oid_to_hex(entry.oid));
  
                        strbuf_addch(base, '/');
                        init_tree_desc(&sub, data, size);
@@@ -470,7 -459,7 +470,7 @@@ static int grep_object(struct grep_opt 
                       struct object *obj, const char *name, const char *path)
  {
        if (obj->type == OBJ_BLOB)
 -              return grep_sha1(opt, obj->sha1, name, 0, path);
 +              return grep_sha1(opt, obj->oid.hash, name, 0, path);
        if (obj->type == OBJ_COMMIT || obj->type == OBJ_TREE) {
                struct tree_desc tree;
                void *data;
                int hit, len;
  
                grep_read_lock();
 -              data = read_object_with_reference(obj->sha1, tree_type,
 +              data = read_object_with_reference(obj->oid.hash, tree_type,
                                                  &size, NULL);
                grep_read_unlock();
  
                if (!data)
 -                      die(_("unable to read tree (%s)"), sha1_to_hex(obj->sha1));
 +                      die(_("unable to read tree (%s)"), oid_to_hex(&obj->oid));
  
                len = name ? strlen(name) : 0;
                strbuf_init(&base, PATH_MAX + len + 1);
@@@ -522,14 -511,12 +522,14 @@@ static int grep_objects(struct grep_op
  }
  
  static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec,
 -                        int exc_std)
 +                        int exc_std, int use_index)
  {
        struct dir_struct dir;
        int i, hit = 0;
  
        memset(&dir, 0, sizeof(dir));
 +      if (!use_index)
 +              dir.flags |= DIR_NO_GITLINKS;
        if (exc_std)
                setup_standard_excludes(&dir);
  
@@@ -575,7 -562,7 +575,7 @@@ static int file_callback(const struct o
        patterns = from_stdin ? stdin : fopen(arg, "r");
        if (!patterns)
                die_errno(_("cannot open '%s'"), arg);
 -      while (strbuf_getline(&sb, patterns, '\n') == 0) {
 +      while (strbuf_getline(&sb, patterns) == 0) {
                /* ignore empty line like grep does */
                if (sb.len == 0)
                        continue;
@@@ -625,6 -612,11 +625,6 @@@ static int pattern_callback(const struc
        return 0;
  }
  
 -static int help_callback(const struct option *opt, const char *arg, int unset)
 -{
 -      return -1;
 -}
 -
  int cmd_grep(int argc, const char **argv, const char *prefix)
  {
        int hit = 0;
                        N_("show <n> context lines before matches")),
                OPT_INTEGER('A', "after-context", &opt.post_context,
                        N_("show <n> context lines after matches")),
 +              OPT_INTEGER(0, "threads", &num_threads,
 +                      N_("use <n> worker threads")),
                OPT_NUMBER_CALLBACK(&opt, N_("shortcut for -C NUM"),
                        context_callback),
                OPT_BOOL('p', "show-function", &opt.funcname,
                        PARSE_OPT_OPTARG, NULL, (intptr_t)default_pager },
                OPT_BOOL(0, "ext-grep", &external_grep_allowed__ignored,
                         N_("allow calling of grep(1) (ignored by this build)")),
 -              { OPTION_CALLBACK, 0, "help-all", NULL, NULL, N_("show usage"),
 -                PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, help_callback },
                OPT_END()
        };
  
 -      /*
 -       * 'git grep -h', unlike 'git grep -h <pattern>', is a request
 -       * to show usage information and exit.
 -       */
 -      if (argc == 2 && !strcmp(argv[1], "-h"))
 -              usage_with_options(grep_usage, options);
 -
        init_grep_defaults();
        git_config(grep_cmd_config, NULL);
        grep_init(&opt, prefix);
         */
        argc = parse_options(argc, argv, prefix, options, grep_usage,
                             PARSE_OPT_KEEP_DASHDASH |
 -                           PARSE_OPT_STOP_AT_NON_OPTION |
 -                           PARSE_OPT_NO_INTERNAL_HELP);
 +                           PARSE_OPT_STOP_AT_NON_OPTION);
        grep_commit_pattern_type(pattern_type_arg, &opt);
  
 -      if (use_index && !startup_info->have_repository)
 -              /* die the same way as if we did it at the beginning */
 -              setup_git_directory();
 +      if (use_index && !startup_info->have_repository) {
 +              int fallback = 0;
 +              git_config_get_bool("grep.fallbacktonoindex", &fallback);
 +              if (fallback)
 +                      use_index = 0;
 +              else
 +                      /* die the same way as if we did it at the beginning */
 +                      setup_git_directory();
 +      }
  
        /*
         * skip a -- separator; we know it cannot be
                opt.output_priv = &path_list;
                opt.output = append_path;
                string_list_append(&path_list, show_in_pager);
 -              use_threads = 0;
        }
  
        if (!opt.pattern_list)
        }
  
  #ifndef NO_PTHREADS
 -      if (list.nr || cached || online_cpus() == 1)
 -              use_threads = 0;
 +      if (list.nr || cached || show_in_pager)
 +              num_threads = 0;
 +      else if (num_threads == 0)
 +              num_threads = GREP_NUM_THREADS_DEFAULT;
 +      else if (num_threads < 0)
 +              die(_("invalid number of threads specified (%d)"), num_threads);
  #else
 -      use_threads = 0;
 +      num_threads = 0;
  #endif
  
  #ifndef NO_PTHREADS
 -      if (use_threads) {
 +      if (num_threads) {
                if (!(opt.name_only || opt.unmatch_name_only || opt.count)
                    && (opt.pre_context || opt.post_context ||
                        opt.file_break || opt.funcbody))
                int use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude;
                if (list.nr)
                        die(_("--no-index or --untracked cannot be used with revs."));
 -              hit = grep_directory(&opt, &pathspec, use_exclude);
 +              hit = grep_directory(&opt, &pathspec, use_exclude, use_index);
        } else if (0 <= opt_exclude) {
                die(_("--[no-]exclude-standard cannot be used for tracked contents."));
        } else if (!list.nr) {
                hit = grep_objects(&opt, &pathspec, &list);
        }
  
 -      if (use_threads)
 +      if (num_threads)
                hit |= wait_all();
        if (hit && show_in_pager)
                run_pager(&opt, prefix);
diff --combined t/t7810-grep.sh
index bd8ea1191f1092ed7662c2258b82c156de7defce,d0a7ebfaff7086e7a5846540ee266dd4c7074a3d..cf3f9ec63187f17cbfab40aaa3d514e281004275
@@@ -9,9 -9,7 +9,9 @@@ test_description='git grep various
  . ./test-lib.sh
  
  cat >hello.c <<EOF
 +#include <assert.h>
  #include <stdio.h>
 +
  int main(int argc, const char **argv)
  {
        printf("Hello world.\n");
@@@ -177,7 -175,7 +177,7 @@@ d
  
        test_expect_success "grep -c $L (no /dev/null)" '
                ! git grep -c test $H | grep /dev/null
-         '
+       '
  
        test_expect_success "grep --max-depth -1 $L" '
                {
@@@ -717,7 -715,6 +717,7 @@@ test_expect_success 'grep -p' 
  
  cat >expected <<EOF
  hello.c-#include <stdio.h>
 +hello.c-
  hello.c=int main(int argc, const char **argv)
  hello.c-{
  hello.c-      printf("Hello world.\n");
@@@ -743,16 -740,6 +743,16 @@@ test_expect_success 'grep -W' 
        test_cmp expected actual
  '
  
 +cat >expected <<EOF
 +hello.c-#include <assert.h>
 +hello.c:#include <stdio.h>
 +EOF
 +
 +test_expect_success 'grep -W shows no trailing empty lines' '
 +      git grep -W stdio >actual &&
 +      test_cmp expected actual
 +'
 +
  cat >expected <<EOF
  hello.c=      printf("Hello world.\n");
  hello.c:      return 0;
@@@ -804,12 -791,12 +804,12 @@@ test_expect_success 'outside of git rep
        } >non/expect.full &&
        echo file2:world >non/expect.sub &&
        (
 -              GIT_CEILING_DIRECTORIES="$(pwd)/non/git" &&
 +              GIT_CEILING_DIRECTORIES="$(pwd)/non" &&
                export GIT_CEILING_DIRECTORIES &&
                cd non/git &&
                test_must_fail git grep o &&
                git grep --no-index o >../actual.full &&
 -              test_cmp ../expect.full ../actual.full
 +              test_cmp ../expect.full ../actual.full &&
                cd sub &&
                test_must_fail git grep o &&
                git grep --no-index o >../../actual.sub &&
  
        echo ".*o*" >non/git/.gitignore &&
        (
 -              GIT_CEILING_DIRECTORIES="$(pwd)/non/git" &&
 +              GIT_CEILING_DIRECTORIES="$(pwd)/non" &&
                export GIT_CEILING_DIRECTORIES &&
                cd non/git &&
                test_must_fail git grep o &&
                test_cmp ../expect.full ../actual.full &&
  
                {
 -                      echo ".gitignore:.*o*"
 +                      echo ".gitignore:.*o*" &&
                        cat ../expect.full
                } >../expect.with.ignored &&
                git grep --no-index --no-exclude o >../actual.full &&
        )
  '
  
 +test_expect_success 'outside of git repository with fallbackToNoIndex' '
 +      rm -fr non &&
 +      mkdir -p non/git/sub &&
 +      echo hello >non/git/file1 &&
 +      echo world >non/git/sub/file2 &&
 +      cat <<-\EOF >non/expect.full &&
 +      file1:hello
 +      sub/file2:world
 +      EOF
 +      echo file2:world >non/expect.sub &&
 +      (
 +              GIT_CEILING_DIRECTORIES="$(pwd)/non" &&
 +              export GIT_CEILING_DIRECTORIES &&
 +              cd non/git &&
 +              test_must_fail git -c grep.fallbackToNoIndex=false grep o &&
 +              git -c grep.fallbackToNoIndex=true grep o >../actual.full &&
 +              test_cmp ../expect.full ../actual.full &&
 +              cd sub &&
 +              test_must_fail git -c grep.fallbackToNoIndex=false grep o &&
 +              git -c grep.fallbackToNoIndex=true grep o >../../actual.sub &&
 +              test_cmp ../../expect.sub ../../actual.sub
 +      ) &&
 +
 +      echo ".*o*" >non/git/.gitignore &&
 +      (
 +              GIT_CEILING_DIRECTORIES="$(pwd)/non" &&
 +              export GIT_CEILING_DIRECTORIES &&
 +              cd non/git &&
 +              test_must_fail git -c grep.fallbackToNoIndex=false grep o &&
 +              git -c grep.fallbackToNoIndex=true grep --exclude-standard o >../actual.full &&
 +              test_cmp ../expect.full ../actual.full &&
 +
 +              {
 +                      echo ".gitignore:.*o*" &&
 +                      cat ../expect.full
 +              } >../expect.with.ignored &&
 +              git -c grep.fallbackToNoIndex grep --no-exclude o >../actual.full &&
 +              test_cmp ../expect.with.ignored ../actual.full
 +      )
 +'
 +
  test_expect_success 'inside git repository but with --no-index' '
        rm -fr is &&
        mkdir -p is/git/sub &&
        )
  '
  
 +test_expect_success 'grep --no-index descends into repos, but not .git' '
 +      rm -fr non &&
 +      mkdir -p non/git &&
 +      (
 +              GIT_CEILING_DIRECTORIES="$(pwd)/non" &&
 +              export GIT_CEILING_DIRECTORIES &&
 +              cd non/git &&
 +
 +              echo magic >file &&
 +              git init repo &&
 +              (
 +                      cd repo &&
 +                      echo magic >file &&
 +                      git add file &&
 +                      git commit -m foo &&
 +                      echo magic >.git/file
 +              ) &&
 +
 +              cat >expect <<-\EOF &&
 +              file
 +              repo/file
 +              EOF
 +              git grep -l --no-index magic >actual &&
 +              test_cmp expect actual
 +      )
 +'
 +
  test_expect_success 'setup double-dash tests' '
  cat >double-dash <<EOF &&
  --
@@@ -1245,8 -1164,8 +1245,8 @@@ test_expect_success 'grep --heading' 
  
  cat >expected <<EOF
  <BOLD;GREEN>hello.c<RESET>
 -2:int main(int argc, const <BLACK;BYELLOW>char<RESET> **argv)
 -6:    /* <BLACK;BYELLOW>char<RESET> ?? */
 +4:int main(int argc, const <BLACK;BYELLOW>char<RESET> **argv)
 +8:    /* <BLACK;BYELLOW>char<RESET> ?? */
  
  <BOLD;GREEN>hello_world<RESET>
  3:Hel<BLACK;BYELLOW>lo_w<RESET>orld
@@@ -1353,7 -1272,7 +1353,7 @@@ test_expect_success 'grep --color -e A 
  '
  
  cat >expected <<EOF
 -hello.c-#include <stdio.h>
 +hello.c-
  hello.c=int main(int argc, const char **argv)
  hello.c-{
  hello.c:      pr<RED>int<RESET>f("<RED>Hello<RESET> world.\n");
@@@ -1377,4 -1296,62 +1377,62 @@@ test_expect_success 'grep --color -e A 
        test_cmp expected actual
  '
  
+ test_expect_success 'grep can find things only in the work tree' '
+       : >work-tree-only &&
+       git add work-tree-only &&
+       test_when_finished "git rm -f work-tree-only" &&
+       echo "find in work tree" >work-tree-only &&
+       git grep --quiet "find in work tree" &&
+       test_must_fail git grep --quiet --cached "find in work tree" &&
+       test_must_fail git grep --quiet "find in work tree" HEAD
+ '
+ test_expect_success 'grep can find things only in the work tree (i-t-a)' '
+       echo "intend to add this" >intend-to-add &&
+       git add -N intend-to-add &&
+       test_when_finished "git rm -f intend-to-add" &&
+       git grep --quiet "intend to add this" &&
+       test_must_fail git grep --quiet --cached "intend to add this" &&
+       test_must_fail git grep --quiet "intend to add this" HEAD
+ '
+ test_expect_success 'grep does not search work tree with assume unchanged' '
+       echo "intend to add this" >intend-to-add &&
+       git add -N intend-to-add &&
+       git update-index --assume-unchanged intend-to-add &&
+       test_when_finished "git rm -f intend-to-add" &&
+       test_must_fail git grep --quiet "intend to add this" &&
+       test_must_fail git grep --quiet --cached "intend to add this" &&
+       test_must_fail git grep --quiet "intend to add this" HEAD
+ '
+ test_expect_success 'grep can find things only in the index' '
+       echo "only in the index" >cache-this &&
+       git add cache-this &&
+       rm cache-this &&
+       test_when_finished "git rm --cached cache-this" &&
+       test_must_fail git grep --quiet "only in the index" &&
+       git grep --quiet --cached "only in the index" &&
+       test_must_fail git grep --quiet "only in the index" HEAD
+ '
+ test_expect_success 'grep does not report i-t-a with -L --cached' '
+       echo "intend to add this" >intend-to-add &&
+       git add -N intend-to-add &&
+       test_when_finished "git rm -f intend-to-add" &&
+       git ls-files | grep -v "^intend-to-add\$" >expected &&
+       git grep -L --cached "nonexistent_string" >actual &&
+       test_cmp expected actual
+ '
+ test_expect_success 'grep does not report i-t-a and assume unchanged with -L' '
+       echo "intend to add this" >intend-to-add-assume-unchanged &&
+       git add -N intend-to-add-assume-unchanged &&
+       test_when_finished "git rm -f intend-to-add-assume-unchanged" &&
+       git update-index --assume-unchanged intend-to-add-assume-unchanged &&
+       git ls-files | grep -v "^intend-to-add-assume-unchanged\$" >expected &&
+       git grep -L "nonexistent_string" >actual &&
+       test_cmp expected actual
+ '
  test_done