Merge branch 'dw/check-ignore-sans-index'
authorJunio C Hamano <gitster@pobox.com>
Fri, 20 Sep 2013 19:37:32 +0000 (12:37 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 20 Sep 2013 19:37:32 +0000 (12:37 -0700)
"git check-ignore" follows the same rule as "git add" and "git
status" in that the ignore/exclude mechanism does not take effect
on paths that are already tracked. With "--no-index" option, it
can be used to diagnose which paths that should have been ignored
have been mistakenly added to the index.

* dw/check-ignore-sans-index:
check-ignore: Add option to ignore index contents

1  2 
builtin/check-ignore.c
t/t0008-ignores.sh
diff --combined builtin/check-ignore.c
index e2a1cef6843b7db2f7ffb4b9c89003397948c748,8b3c2e3e27c036718fdf62f7af9074410f44fa51..594463a11bcba3cf99748ccdaeb055bd79283ddd
@@@ -5,7 -5,7 +5,7 @@@
  #include "pathspec.h"
  #include "parse-options.h"
  
- static int quiet, verbose, stdin_paths, show_non_matching;
+ static int quiet, verbose, stdin_paths, show_non_matching, no_index;
  static const char * const check_ignore_usage[] = {
  "git check-ignore [options] pathname...",
  "git check-ignore [options] --stdin < <list-of-paths>",
@@@ -24,6 -24,8 +24,8 @@@ static const struct option check_ignore
                 N_("terminate input and output records by a NUL character")),
        OPT_BOOL('n', "non-matching", &show_non_matching,
                 N_("show non-matching input paths")),
+       OPT_BOOL(0, "no-index", &no_index,
+                N_("ignore index when checking")),
        OPT_END()
  };
  
@@@ -64,45 -66,37 +66,45 @@@ static void output_exclude(const char *
  }
  
  static int check_ignore(struct dir_struct *dir,
 -                      const char *prefix, const char **pathspec)
 +                      const char *prefix, int argc, const char **argv)
  {
 -      const char *path, *full_path;
 +      const char *full_path;
        char *seen;
        int num_ignored = 0, dtype = DT_UNKNOWN, i;
        struct exclude *exclude;
 +      struct pathspec pathspec;
  
 -      if (!pathspec || !*pathspec) {
 +      if (!argc) {
                if (!quiet)
                        fprintf(stderr, "no pathspec given.\n");
                return 0;
        }
  
 +      /*
 +       * check-ignore just needs paths. Magic beyond :/ is really
 +       * irrelevant.
 +       */
 +      parse_pathspec(&pathspec,
 +                     PATHSPEC_ALL_MAGIC & ~PATHSPEC_FROMTOP,
 +                     PATHSPEC_SYMLINK_LEADING_PATH |
 +                     PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE |
 +                     PATHSPEC_KEEP_ORDER,
 +                     prefix, argv);
 +
        /*
         * look for pathspecs matching entries in the index, since these
         * should not be ignored, in order to be consistent with
         * 'git status', 'git add' etc.
         */
 -      seen = find_pathspecs_matching_against_index(pathspec);
 -      for (i = 0; pathspec[i]; i++) {
 -              path = pathspec[i];
 -              full_path = prefix_path(prefix, prefix
 -                                      ? strlen(prefix) : 0, path);
 -              full_path = check_path_for_gitlink(full_path);
 -              die_if_path_beyond_symlink(full_path, prefix);
 +      seen = find_pathspecs_matching_against_index(&pathspec);
 +      for (i = 0; i < pathspec.nr; i++) {
 +              full_path = pathspec.items[i].match;
                exclude = NULL;
                if (!seen[i]) {
                        exclude = last_exclude_matching(dir, full_path, &dtype);
                }
                if (!quiet && (exclude || show_non_matching))
 -                      output_exclude(path, exclude);
 +                      output_exclude(pathspec.items[i].original, exclude);
                if (exclude)
                        num_ignored++;
        }
@@@ -128,8 -122,7 +130,8 @@@ static int check_ignore_stdin_paths(str
                        strbuf_swap(&buf, &nbuf);
                }
                pathspec[0] = buf.buf;
 -              num_ignored += check_ignore(dir, prefix, (const char **)pathspec);
 +              num_ignored += check_ignore(dir, prefix,
 +                                          1, (const char **)pathspec);
                maybe_flush_or_die(stdout, "check-ignore to stdout");
        }
        strbuf_release(&buf);
@@@ -166,7 -159,7 +168,7 @@@ int cmd_check_ignore(int argc, const ch
                die(_("--non-matching is only valid with --verbose"));
  
        /* read_cache() is only necessary so we can watch out for submodules. */
-       if (read_cache() < 0)
+       if (!no_index && read_cache() < 0)
                die(_("index file corrupt"));
  
        memset(&dir, 0, sizeof(dir));
        if (stdin_paths) {
                num_ignored = check_ignore_stdin_paths(&dir, prefix);
        } else {
 -              num_ignored = check_ignore(&dir, prefix, argv);
 +              num_ignored = check_ignore(&dir, prefix, argc, argv);
                maybe_flush_or_die(stdout, "ignore to stdout");
        }
  
diff --combined t/t0008-ignores.sh
index 96f40fedfb7285b50243a1c1bbe64649ef7f8edb,d0826139a5a6d5b6cbcc9e296fa9b3938e371e3e..181513ab4fba47750b3e6b25eb105f2c8a4a011f
@@@ -66,11 -66,11 +66,11 @@@ test_check_ignore () 
  
        init_vars &&
        rm -f "$HOME/stdout" "$HOME/stderr" "$HOME/cmd" &&
-       echo git $global_args check-ignore $quiet_opt $verbose_opt $non_matching_opt $args \
+       echo git $global_args check-ignore $quiet_opt $verbose_opt $non_matching_opt $no_index_opt $args \
                >"$HOME/cmd" &&
        echo "$expect_code" >"$HOME/expected-exit-code" &&
        test_expect_code "$expect_code" \
-               git $global_args check-ignore $quiet_opt $verbose_opt $non_matching_opt $args \
+               git $global_args check-ignore $quiet_opt $verbose_opt $non_matching_opt $no_index_opt $args \
                >"$HOME/stdout" 2>"$HOME/stderr" &&
        test_cmp "$HOME/expected-stdout" "$HOME/stdout" &&
        stderr_empty_on_success "$expect_code"
@@@ -87,6 -87,9 +87,9 @@@
  # check-ignore --verbose output is the same as normal output except
  # for the extra first column.
  #
+ # A parameter is used to determine if the tests are run with the
+ # normal case (using the index), or with the --no-index option.
+ #
  # Arguments:
  #   - (optional) prereqs for this test, e.g. 'SYMLINKS'
  #   - test name
  #     from the other verbosity modes is automatically inferred
  #     from this value)
  #   - code to run (should invoke test_check_ignore)
- test_expect_success_multi () {
+ #   - index option: --index or --no-index
+ test_expect_success_multiple () {
        prereq=
-       if test $# -eq 4
+       if test $# -eq 5
        then
                prereq=$1
                shift
        fi
+       if test "$4" = "--index"
+       then
+               no_index_opt=
+       else
+               no_index_opt=$4
+       fi
        testname="$1" expect_all="$2" code="$3"
  
        expect_verbose=$( echo "$expect_all" | grep -v '^::     ' )
        expect=$( echo "$expect_verbose" | sed -e 's/.* //' )
  
-       test_expect_success $prereq "$testname" '
+       test_expect_success $prereq "$testname${no_index_opt:+ with $no_index_opt}" '
                expect "$expect" &&
                eval "$code"
        '
        then
                for quiet_opt in '-q' '--quiet'
                do
-                       test_expect_success $prereq "$testname${quiet_opt:+ with $quiet_opt}" "
+                       opts="${no_index_opt:+$no_index_opt }$quiet_opt"
+                       test_expect_success $prereq "$testname${opts:+ with $opts}" "
                        expect '' &&
                        $code
                "
  
        for verbose_opt in '-v' '--verbose'
        do
-               for non_matching_opt in '' ' -n' ' --non-matching'
+               for non_matching_opt in '' '-n' '--non-matching'
                do
                        if test -n "$non_matching_opt"
                        then
                                expect '$my_expect' &&
                                $code
                        "
-                       opts="$verbose_opt$non_matching_opt"
+                       opts="${no_index_opt:+$no_index_opt }$verbose_opt${non_matching_opt:+ $non_matching_opt}"
                        test_expect_success $prereq "$testname${opts:+ with $opts}" "$test_code"
                done
        done
        verbose_opt=
        non_matching_opt=
+       no_index_opt=
+ }
+ test_expect_success_multi () {
+       test_expect_success_multiple "$@" "--index"
+ }
+ test_expect_success_no_index_multi () {
+       test_expect_success_multiple "$@" "--no-index"
  }
  
  test_expect_success 'setup' '
@@@ -288,7 -308,7 +308,7 @@@ test_expect_success_multi 'needs work t
  
  # First make sure that the presence of a file in the working tree
  # does not impact results, but that the presence of a file in the
- # index does.
+ # index does unless the --no-index option is used.
  
  for subdir in '' 'a/'
  do
                "::     ${subdir}non-existent" \
                "test_check_ignore '${subdir}non-existent' 1"
  
+       test_expect_success_no_index_multi "non-existent file $where not ignored" \
+               "::     ${subdir}non-existent" \
+               "test_check_ignore '${subdir}non-existent' 1"
        test_expect_success_multi "non-existent file $where ignored" \
                ".gitignore:1:one       ${subdir}one" \
                "test_check_ignore '${subdir}one'"
  
+       test_expect_success_no_index_multi "non-existent file $where ignored" \
+               ".gitignore:1:one       ${subdir}one" \
+               "test_check_ignore '${subdir}one'"
        test_expect_success_multi "existing untracked file $where not ignored" \
                "::     ${subdir}not-ignored" \
                "test_check_ignore '${subdir}not-ignored' 1"
  
+       test_expect_success_no_index_multi "existing untracked file $where not ignored" \
+               "::     ${subdir}not-ignored" \
+               "test_check_ignore '${subdir}not-ignored' 1"
        test_expect_success_multi "existing tracked file $where not ignored" \
                "::     ${subdir}ignored-but-in-index" \
                "test_check_ignore '${subdir}ignored-but-in-index' 1"
  
+       test_expect_success_no_index_multi "existing tracked file $where shown as ignored" \
+               ".gitignore:2:ignored-* ${subdir}ignored-but-in-index" \
+               "test_check_ignore '${subdir}ignored-but-in-index'"
        test_expect_success_multi "existing untracked file $where ignored" \
                ".gitignore:2:ignored-* ${subdir}ignored-and-untracked" \
                "test_check_ignore '${subdir}ignored-and-untracked'"
  
+       test_expect_success_no_index_multi "existing untracked file $where ignored" \
+               ".gitignore:2:ignored-* ${subdir}ignored-and-untracked" \
+               "test_check_ignore '${subdir}ignored-and-untracked'"
        test_expect_success_multi "mix of file types $where" \
  "::   ${subdir}non-existent
  .gitignore:1:one      ${subdir}one
  ::    ${subdir}not-ignored
  ::    ${subdir}ignored-but-in-index
+ .gitignore:2:ignored-*        ${subdir}ignored-and-untracked" \
+               "test_check_ignore '
+                       ${subdir}non-existent
+                       ${subdir}one
+                       ${subdir}not-ignored
+                       ${subdir}ignored-but-in-index
+                       ${subdir}ignored-and-untracked'
+               "
+       test_expect_success_no_index_multi "mix of file types $where" \
+ "::   ${subdir}non-existent
+ .gitignore:1:one      ${subdir}one
+ ::    ${subdir}not-ignored
+ .gitignore:2:ignored-*        ${subdir}ignored-but-in-index
  .gitignore:2:ignored-*        ${subdir}ignored-and-untracked" \
                "test_check_ignore '
                        ${subdir}non-existent
@@@ -432,7 -486,7 +486,7 @@@ test_expect_success_multi SYMLINKS 'sym
  
  test_expect_success_multi SYMLINKS 'beyond a symlink' '' '
        test_check_ignore "a/symlink/foo" 128 &&
 -      test_stderr "fatal: '\''a/symlink/foo'\'' is beyond a symbolic link"
 +      test_stderr "fatal: pathspec '\''a/symlink/foo'\'' is beyond a symbolic link"
  '
  
  test_expect_success_multi SYMLINKS 'beyond a symlink from subdirectory' '' '
                cd a &&
                test_check_ignore "symlink/foo" 128
        ) &&
 -      test_stderr "fatal: '\''symlink/foo'\'' is beyond a symbolic link"
 +      test_stderr "fatal: pathspec '\''symlink/foo'\'' is beyond a symbolic link"
  '
  
  ############################################################################
  
  test_expect_success_multi 'submodule' '' '
        test_check_ignore "a/submodule/one" 128 &&
 -      test_stderr "fatal: Path '\''a/submodule/one'\'' is in submodule '\''a/submodule'\''"
 +      test_stderr "fatal: Pathspec '\''a/submodule/one'\'' is in submodule '\''a/submodule'\''"
  '
  
  test_expect_success_multi 'submodule from subdirectory' '' '
                cd a &&
                test_check_ignore "submodule/one" 128
        ) &&
 -      test_stderr "fatal: Path '\''a/submodule/one'\'' is in submodule '\''a/submodule'\''"
 +      test_stderr "fatal: Pathspec '\''submodule/one'\'' is in submodule '\''a/submodule'\''"
  '
  
  ############################################################################