Merge branch 'nd/sparse-co-fix'
authorJunio C Hamano <gitster@pobox.com>
Fri, 20 May 2011 03:37:21 +0000 (20:37 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 20 May 2011 03:37:21 +0000 (20:37 -0700)
* nd/sparse-co-fix:
sparse checkout: do not eagerly decide the fate for whole directory
t1011: fix sparse-checkout initialization and add new file

t/t1011-read-tree-sparse-checkout.sh
unpack-trees.c
index de84e35c4357c7329b3fae749228df28f31a9d3f..20a50eba5b77e23b6e540402b4eabb311feabd0d 100755 (executable)
@@ -17,19 +17,21 @@ test_expect_success 'setup' '
        cat >expected <<-\EOF &&
        100644 77f0ba1734ed79d12881f81b36ee134de6a3327b 0       init.t
        100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0       sub/added
+       100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0       sub/addedtoo
        100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0       subsub/added
        EOF
        cat >expected.swt <<-\EOF &&
        H init.t
        H sub/added
+       H sub/addedtoo
        H subsub/added
        EOF
 
        test_commit init &&
        echo modified >>init.t &&
        mkdir sub subsub &&
-       touch sub/added subsub/added &&
-       git add init.t sub/added subsub/added &&
+       touch sub/added sub/addedtoo subsub/added &&
+       git add init.t sub/added sub/addedtoo subsub/added &&
        git commit -m "modified and added" &&
        git tag top &&
        git rm sub/added &&
@@ -83,6 +85,7 @@ test_expect_success 'match directories with trailing slash' '
        cat >expected.swt-noinit <<-\EOF &&
        S init.t
        H sub/added
+       H sub/addedtoo
        S subsub/added
        EOF
 
@@ -95,7 +98,7 @@ test_expect_success 'match directories with trailing slash' '
 '
 
 test_expect_success 'match directories without trailing slash' '
-       echo sub >>.git/info/sparse-checkout &&
+       echo sub >.git/info/sparse-checkout &&
        git read-tree -m -u HEAD &&
        git ls-files -t >result &&
        test_cmp expected.swt-noinit result &&
@@ -103,8 +106,49 @@ test_expect_success 'match directories without trailing slash' '
        test -f sub/added
 '
 
+test_expect_success 'match directories with negated patterns' '
+       cat >expected.swt-negation <<\EOF &&
+S init.t
+S sub/added
+H sub/addedtoo
+S subsub/added
+EOF
+
+       cat >.git/info/sparse-checkout <<\EOF &&
+sub
+!sub/added
+EOF
+       git read-tree -m -u HEAD &&
+       git ls-files -t >result &&
+       test_cmp expected.swt-negation result &&
+       test ! -f init.t &&
+       test ! -f sub/added &&
+       test -f sub/addedtoo
+'
+
+test_expect_success 'match directories with negated patterns (2)' '
+       cat >expected.swt-negation2 <<\EOF &&
+H init.t
+H sub/added
+S sub/addedtoo
+H subsub/added
+EOF
+
+       cat >.git/info/sparse-checkout <<\EOF &&
+/*
+!sub
+sub/added
+EOF
+       git read-tree -m -u HEAD &&
+       git ls-files -t >result &&
+       test_cmp expected.swt-negation2 result &&
+       test -f init.t &&
+       test -f sub/added &&
+       test ! -f sub/addedtoo
+'
+
 test_expect_success 'match directory pattern' '
-       echo "s?b" >>.git/info/sparse-checkout &&
+       echo "s?b" >.git/info/sparse-checkout &&
        git read-tree -m -u HEAD &&
        git ls-files -t >result &&
        test_cmp expected.swt-noinit result &&
@@ -116,6 +160,7 @@ test_expect_success 'checkout area changes' '
        cat >expected.swt-nosub <<-\EOF &&
        H init.t
        S sub/added
+       S sub/addedtoo
        S subsub/added
        EOF
 
index 500ebcfd545772fb17e5fb14bba42bf4be468b75..0bc4b2ddca2d96e16c3f7b32e8e922f426706be6 100644 (file)
@@ -814,43 +814,45 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str
        return mask;
 }
 
+static int clear_ce_flags_1(struct cache_entry **cache, int nr,
+                           char *prefix, int prefix_len,
+                           int select_mask, int clear_mask,
+                           struct exclude_list *el, int defval);
+
 /* Whole directory matching */
 static int clear_ce_flags_dir(struct cache_entry **cache, int nr,
                              char *prefix, int prefix_len,
                              char *basename,
                              int select_mask, int clear_mask,
-                             struct exclude_list *el)
+                             struct exclude_list *el, int defval)
 {
-       struct cache_entry **cache_end = cache + nr;
+       struct cache_entry **cache_end;
        int dtype = DT_DIR;
        int ret = excluded_from_list(prefix, prefix_len, basename, &dtype, el);
 
        prefix[prefix_len++] = '/';
 
-       /* included, no clearing for any entries under this directory */
-       if (!ret) {
-               for (; cache != cache_end; cache++) {
-                       struct cache_entry *ce = *cache;
-                       if (strncmp(ce->name, prefix, prefix_len))
-                               break;
-               }
-               return nr - (cache_end - cache);
-       }
+       /* If undecided, use matching result of parent dir in defval */
+       if (ret < 0)
+               ret = defval;
 
-       /* excluded, clear all selected entries under this directory. */
-       if (ret == 1) {
-               for (; cache != cache_end; cache++) {
-                       struct cache_entry *ce = *cache;
-                       if (select_mask && !(ce->ce_flags & select_mask))
-                               continue;
-                       if (strncmp(ce->name, prefix, prefix_len))
-                               break;
-                       ce->ce_flags &= ~clear_mask;
-               }
-               return nr - (cache_end - cache);
+       for (cache_end = cache; cache_end != cache + nr; cache_end++) {
+               struct cache_entry *ce = *cache_end;
+               if (strncmp(ce->name, prefix, prefix_len))
+                       break;
        }
 
-       return 0;
+       /*
+        * TODO: check el, if there are no patterns that may conflict
+        * with ret (iow, we know in advance the incl/excl
+        * decision for the entire directory), clear flag here without
+        * calling clear_ce_flags_1(). That function will call
+        * the expensive excluded_from_list() on every entry.
+        */
+       return clear_ce_flags_1(cache, cache_end - cache,
+                               prefix, prefix_len,
+                               select_mask, clear_mask,
+                               el, ret);
 }
 
 /*
@@ -871,7 +873,7 @@ static int clear_ce_flags_dir(struct cache_entry **cache, int nr,
 static int clear_ce_flags_1(struct cache_entry **cache, int nr,
                            char *prefix, int prefix_len,
                            int select_mask, int clear_mask,
-                           struct exclude_list *el)
+                           struct exclude_list *el, int defval)
 {
        struct cache_entry **cache_end = cache + nr;
 
@@ -882,7 +884,7 @@ static int clear_ce_flags_1(struct cache_entry **cache, int nr,
        while(cache != cache_end) {
                struct cache_entry *ce = *cache;
                const char *name, *slash;
-               int len, dtype;
+               int len, dtype, ret;
 
                if (select_mask && !(ce->ce_flags & select_mask)) {
                        cache++;
@@ -911,7 +913,7 @@ static int clear_ce_flags_1(struct cache_entry **cache, int nr,
                                                       prefix, prefix_len + len,
                                                       prefix + prefix_len,
                                                       select_mask, clear_mask,
-                                                      el);
+                                                      el, defval);
 
                        /* clear_c_f_dir eats a whole dir already? */
                        if (processed) {
@@ -922,13 +924,16 @@ static int clear_ce_flags_1(struct cache_entry **cache, int nr,
                        prefix[prefix_len + len++] = '/';
                        cache += clear_ce_flags_1(cache, cache_end - cache,
                                                  prefix, prefix_len + len,
-                                                 select_mask, clear_mask, el);
+                                                 select_mask, clear_mask, el, defval);
                        continue;
                }
 
                /* Non-directory */
                dtype = ce_to_dtype(ce);
-               if (excluded_from_list(ce->name, ce_namelen(ce), name, &dtype, el) > 0)
+               ret = excluded_from_list(ce->name, ce_namelen(ce), name, &dtype, el);
+               if (ret < 0)
+                       ret = defval;
+               if (ret > 0)
                        ce->ce_flags &= ~clear_mask;
                cache++;
        }
@@ -943,7 +948,7 @@ static int clear_ce_flags(struct cache_entry **cache, int nr,
        return clear_ce_flags_1(cache, nr,
                                prefix, 0,
                                select_mask, clear_mask,
-                               el);
+                               el, 0);
 }
 
 /*