Read .gitignore from index if it is skip-worktree
authorNguyễn Thái Ngọc Duy <pclouds@gmail.com>
Thu, 20 Aug 2009 13:47:01 +0000 (20:47 +0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 24 Aug 2009 00:13:33 +0000 (17:13 -0700)
This adds index as a prerequisite for directory listing (with
exclude).  At the moment directory listing is used by "git clean",
"git add", "git ls-files" and "git status"/"git commit" and
unpack_trees()-related commands.  These commands have been
checked/modified to populate index before doing directory listing.

add_excludes_from_file() does not enable this feature, because it
is used to read .git/info/exclude and some explicit files specified
by "git ls-files".

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Documentation/technical/api-directory-listing.txt
builtin-clean.c
builtin-ls-files.c
dir.c
t/t3001-ls-files-others-exclude.sh
t/t7300-clean.sh

index 5bbd18f0206604416c3833b7541a5b55b7e63976..add6f435b59e3df095e507d8a5d4f911cee9d3b2 100644 (file)
@@ -58,6 +58,9 @@ The result of the enumeration is left in these fields::
 Calling sequence
 ----------------
 
+Note: index may be looked at for .gitignore files that are CE_SKIP_WORKTREE
+marked. If you to exclude files, make sure you have loaded index first.
+
 * Prepare `struct dir_struct dir` and clear it with `memset(&dir, 0,
   sizeof(dir))`.
 
index 2d8c735d4881a005e4aa5006d9781b71631bb0af..e424b77e6b7367a5704d586ab38d34908684de55 100644 (file)
@@ -71,11 +71,13 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 
        dir.flags |= DIR_SHOW_OTHER_DIRECTORIES;
 
+       if (read_cache() < 0)
+               die("index file corrupt");
+
        if (!ignored)
                setup_standard_excludes(&dir);
 
        pathspec = get_pathspec(prefix, argv);
-       read_cache();
 
        fill_directory(&dir, pathspec);
 
index ad7e44784f1b067f79333f22232e52e20642ab95..2e47242b9d28077fa256170365c69f84493d3707 100644 (file)
@@ -485,6 +485,9 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
                prefix_offset = strlen(prefix);
        git_config(git_default_config, NULL);
 
+       if (read_cache() < 0)
+               die("index file corrupt");
+
        argc = parse_options(argc, argv, prefix, builtin_ls_files_options,
                        ls_files_usage, 0);
        if (show_tag || show_valid_bit) {
@@ -513,7 +516,6 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
        pathspec = get_pathspec(prefix, argv);
 
        /* be nice with submodule paths ending in a slash */
-       read_cache();
        if (pathspec)
                strip_trailing_slash_from_submodules();
 
diff --git a/dir.c b/dir.c
index 1170d6467547ad3d72533d7fa640fd8021cbd9e0..e8e5b7917d6207139c78b5c18b0e918aa55772bd 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -200,11 +200,35 @@ void add_exclude(const char *string, const char *base,
        which->excludes[which->nr++] = x;
 }
 
+static void *read_skip_worktree_file_from_index(const char *path, size_t *size)
+{
+       int pos, len;
+       unsigned long sz;
+       enum object_type type;
+       void *data;
+       struct index_state *istate = &the_index;
+
+       len = strlen(path);
+       pos = index_name_pos(istate, path, len);
+       if (pos < 0)
+               return NULL;
+       if (!ce_skip_worktree(istate->cache[pos]))
+               return NULL;
+       data = read_sha1_file(istate->cache[pos]->sha1, &type, &sz);
+       if (!data || type != OBJ_BLOB) {
+               free(data);
+               return NULL;
+       }
+       *size = xsize_t(sz);
+       return data;
+}
+
 static int add_excludes_from_file_1(const char *fname,
                                    const char *base,
                                    int baselen,
                                    char **buf_p,
-                                   struct exclude_list *which)
+                                   struct exclude_list *which,
+                                   int check_index)
 {
        struct stat st;
        int fd, i;
@@ -212,20 +236,26 @@ static int add_excludes_from_file_1(const char *fname,
        char *buf, *entry;
 
        fd = open(fname, O_RDONLY);
-       if (fd < 0 || fstat(fd, &st) < 0)
-               goto err;
-       size = xsize_t(st.st_size);
-       if (size == 0) {
-               close(fd);
-               return 0;
+       if (fd < 0 || fstat(fd, &st) < 0) {
+               if (0 <= fd)
+                       close(fd);
+               if (!check_index ||
+                   (buf = read_skip_worktree_file_from_index(fname, &size)) == NULL)
+                       return -1;
        }
-       buf = xmalloc(size+1);
-       if (read_in_full(fd, buf, size) != size)
-       {
-               free(buf);
-               goto err;
+       else {
+               size = xsize_t(st.st_size);
+               if (size == 0) {
+                       close(fd);
+                       return 0;
+               }
+               buf = xmalloc(size);
+               if (read_in_full(fd, buf, size) != size) {
+                       close(fd);
+                       return -1;
+               }
+               close(fd);
        }
-       close(fd);
 
        if (buf_p)
                *buf_p = buf;
@@ -240,17 +270,12 @@ static int add_excludes_from_file_1(const char *fname,
                }
        }
        return 0;
-
- err:
-       if (0 <= fd)
-               close(fd);
-       return -1;
 }
 
 void add_excludes_from_file(struct dir_struct *dir, const char *fname)
 {
        if (add_excludes_from_file_1(fname, "", 0, NULL,
-                                    &dir->exclude_list[EXC_FILE]) < 0)
+                                    &dir->exclude_list[EXC_FILE], 0) < 0)
                die("cannot use %s as an exclude file", fname);
 }
 
@@ -301,7 +326,7 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
                strcpy(dir->basebuf + stk->baselen, dir->exclude_per_dir);
                add_excludes_from_file_1(dir->basebuf,
                                         dir->basebuf, stk->baselen,
-                                        &stk->filebuf, el);
+                                        &stk->filebuf, el, 1);
                dir->exclude_stack = stk;
                current = stk->baselen;
        }
index c65bca838881938e2f924cfe62b2c303dbd5b1cd..132c4765cbe8ffc8b92deb5d3e6ca05c012d7c37 100755 (executable)
@@ -64,6 +64,8 @@ two/*.4
 echo '!*.2
 !*.8' >one/two/.gitignore
 
+allignores='.gitignore one/.gitignore one/two/.gitignore'
+
 test_expect_success \
     'git ls-files --others with various exclude options.' \
     'git ls-files --others \
@@ -85,6 +87,26 @@ test_expect_success \
        >output &&
      test_cmp expect output'
 
+test_expect_success 'setup skip-worktree gitignore' '
+       git add $allignores &&
+       git update-index --skip-worktree $allignores &&
+       rm $allignores
+'
+
+test_expect_success \
+    'git ls-files --others with various exclude options.' \
+    'git ls-files --others \
+       --exclude=\*.6 \
+       --exclude-per-directory=.gitignore \
+       --exclude-from=.git/ignore \
+       >output &&
+     test_cmp expect output'
+
+test_expect_success 'restore gitignore' '
+       git checkout $allignores &&
+       rm .git/index
+'
+
 cat > excludes-file <<\EOF
 *.[1-8]
 e*
index 929d5d4d3b9d55f570cef1617a0716b17265c988..8073d02be59b56f8cecb38fd4e0b92483ff73cde 100755 (executable)
@@ -22,6 +22,25 @@ test_expect_success 'setup' '
 
 '
 
+test_expect_success 'git clean with skip-worktree .gitignore' '
+       git update-index --skip-worktree .gitignore &&
+       rm .gitignore &&
+       mkdir -p build docs &&
+       touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+       git clean &&
+       test -f Makefile &&
+       test -f README &&
+       test -f src/part1.c &&
+       test -f src/part2.c &&
+       test ! -f a.out &&
+       test ! -f src/part3.c &&
+       test -f docs/manual.txt &&
+       test -f obj.o &&
+       test -f build/lib.so &&
+       git update-index --no-skip-worktree .gitignore &&
+       git checkout .gitignore
+'
+
 test_expect_success 'git clean' '
 
        mkdir -p build docs &&