Merge branch 'jc/apply-ws-prefix'
authorJunio C Hamano <gitster@pobox.com>
Tue, 9 Sep 2014 19:53:58 +0000 (12:53 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 9 Sep 2014 19:53:58 +0000 (12:53 -0700)
Applying a patch not generated by Git in a subdirectory used to
check the whitespace breakage using the attributes for incorrect
paths. Also whitespace checks were performed even for paths
excluded via "git apply --exclude=<path>" mechanism.

* jc/apply-ws-prefix:
apply: omit ws check for excluded paths
apply: hoist use_patch() helper for path exclusion up
apply: use the right attribute for paths in non-Git patches

1  2 
builtin/apply.c
t/t4119-apply-config.sh
diff --combined builtin/apply.c
index be2b4ce2fd91640379b0a65d0aa90238d7edfd64,13319e8f2f21c49326c5bdbb1a7a3dcd9e41ccc5..6b7c764918cff18957f67a2e345c0610e459859d
@@@ -300,13 -300,11 +300,13 @@@ static int fuzzy_matchlines(const char 
        while ((*last2 == '\r') || (*last2 == '\n'))
                last2--;
  
 -      /* skip leading whitespace */
 -      while (isspace(*s1) && (s1 <= last1))
 -              s1++;
 -      while (isspace(*s2) && (s2 <= last2))
 -              s2++;
 +      /* skip leading whitespaces, if both begin with whitespace */
 +      if (s1 <= last1 && s2 <= last2 && isspace(*s1) && isspace(*s2)) {
 +              while (isspace(*s1) && (s1 <= last1))
 +                      s1++;
 +              while (isspace(*s2) && (s2 <= last2))
 +                      s2++;
 +      }
        /* early return if both lines are empty */
        if ((s1 > last1) && (s2 > last2))
                return 1;
@@@ -1075,7 -1073,7 +1075,7 @@@ static int gitdiff_index(const char *li
  
        line = ptr + 2;
        ptr = strchr(line, ' ');
 -      eol = strchr(line, '\n');
 +      eol = strchrnul(line, '\n');
  
        if (!ptr || eol < ptr)
                ptr = eol;
@@@ -1281,7 -1279,9 +1281,7 @@@ static int parse_git_header(const char 
         */
        patch->def_name = git_header_name(line, len);
        if (patch->def_name && root) {
 -              char *s = xmalloc(root_len + strlen(patch->def_name) + 1);
 -              strcpy(s, root);
 -              strcpy(s + root_len, patch->def_name);
 +              char *s = xstrfmt("%s%s", root, patch->def_name);
                free(patch->def_name);
                patch->def_name = s;
        }
@@@ -1920,6 -1920,66 +1920,66 @@@ static int parse_binary(char *buffer, u
        return used;
  }
  
 -              if (!fnmatch(it->string, pathname, 0))
+ static void prefix_one(char **name)
+ {
+       char *old_name = *name;
+       if (!old_name)
+               return;
+       *name = xstrdup(prefix_filename(prefix, prefix_length, *name));
+       free(old_name);
+ }
+ static void prefix_patch(struct patch *p)
+ {
+       if (!prefix || p->is_toplevel_relative)
+               return;
+       prefix_one(&p->new_name);
+       prefix_one(&p->old_name);
+ }
+ /*
+  * include/exclude
+  */
+ static struct string_list limit_by_name;
+ static int has_include;
+ static void add_name_limit(const char *name, int exclude)
+ {
+       struct string_list_item *it;
+       it = string_list_append(&limit_by_name, name);
+       it->util = exclude ? NULL : (void *) 1;
+ }
+ static int use_patch(struct patch *p)
+ {
+       const char *pathname = p->new_name ? p->new_name : p->old_name;
+       int i;
+       /* Paths outside are not touched regardless of "--include" */
+       if (0 < prefix_length) {
+               int pathlen = strlen(pathname);
+               if (pathlen <= prefix_length ||
+                   memcmp(prefix, pathname, prefix_length))
+                       return 0;
+       }
+       /* See if it matches any of exclude/include rule */
+       for (i = 0; i < limit_by_name.nr; i++) {
+               struct string_list_item *it = &limit_by_name.items[i];
++              if (!wildmatch(it->string, pathname, 0, NULL))
+                       return (it->util != NULL);
+       }
+       /*
+        * If we had any include, a path that does not match any rule is
+        * not used.  Otherwise, we saw bunch of exclude rules (or none)
+        * and such a path is used.
+        */
+       return !has_include;
+ }
  /*
   * Read the patch text in "buffer" that extends for "size" bytes; stop
   * reading after seeing a single patch (i.e. changes to a single file).
@@@ -1935,15 -1995,26 +1995,20 @@@ static int parse_chunk(char *buffer, un
        if (offset < 0)
                return offset;
  
-       patch->ws_rule = whitespace_rule(patch->new_name
-                                        ? patch->new_name
-                                        : patch->old_name);
+       prefix_patch(patch);
+       if (!use_patch(patch))
+               patch->ws_rule = 0;
+       else
+               patch->ws_rule = whitespace_rule(patch->new_name
+                                                ? patch->new_name
+                                                : patch->old_name);
  
        patchsize = parse_single_patch(buffer + offset + hdrsize,
                                       size - offset - hdrsize, patch);
  
        if (!patchsize) {
 -              static const char *binhdr[] = {
 -                      "Binary files ",
 -                      "Files ",
 -                      NULL,
 -              };
                static const char git_binary[] = "GIT binary patch\n";
 -              int i;
                int hd = hdrsize + offset;
                unsigned long llen = linelen(buffer + hd, size - hd);
  
                                patchsize = 0;
                }
                else if (!memcmp(" differ\n", buffer + hd + llen - 8, 8)) {
 +                      static const char *binhdr[] = {
 +                              "Binary files ",
 +                              "Files ",
 +                              NULL,
 +                      };
 +                      int i;
                        for (i = 0; binhdr[i]; i++) {
                                int len = strlen(binhdr[i]);
                                if (len < size - hd &&
@@@ -2867,7 -2932,9 +2932,7 @@@ static int apply_binary_fragment(struc
        case BINARY_LITERAL_DEFLATED:
                clear_image(img);
                img->len = fragment->size;
 -              img->buf = xmalloc(img->len+1);
 -              memcpy(img->buf, fragment->patch, img->len);
 -              img->buf[img->len] = '\0';
 +              img->buf = xmemdupz(fragment->patch, img->len);
                return 0;
        }
        return -1;
@@@ -3082,15 -3149,13 +3147,15 @@@ static void prepare_fn_table(struct pat
        }
  }
  
 -static int checkout_target(struct cache_entry *ce, struct stat *st)
 +static int checkout_target(struct index_state *istate,
 +                         struct cache_entry *ce, struct stat *st)
  {
        struct checkout costate;
  
        memset(&costate, 0, sizeof(costate));
        costate.base_dir = "";
        costate.refresh_cache = 1;
 +      costate.istate = istate;
        if (checkout_entry(ce, &costate, NULL) || lstat(ce->name, st))
                return error(_("cannot checkout %s"), ce->name);
        return 0;
@@@ -3257,7 -3322,7 +3322,7 @@@ static int load_current(struct image *i
        if (lstat(name, &st)) {
                if (errno != ENOENT)
                        return error(_("%s: %s"), name, strerror(errno));
 -              if (checkout_target(ce, &st))
 +              if (checkout_target(&the_index, ce, &st))
                        return -1;
        }
        if (verify_index_match(ce, &st))
@@@ -3411,7 -3476,7 +3476,7 @@@ static int check_preimage(struct patch 
                }
                *ce = active_cache[pos];
                if (stat_ret < 0) {
 -                      if (checkout_target(*ce, st))
 +                      if (checkout_target(&the_index, *ce, st))
                                return -1;
                }
                if (!cached && verify_index_match(*ce, st))
@@@ -3644,7 -3709,7 +3709,7 @@@ static void build_fake_ancestor(struct 
  {
        struct patch *patch;
        struct index_state result = { NULL };
 -      int fd;
 +      static struct lock_file lock;
  
        /* Once we start supporting the reverse patch, it may be
         * worth showing the new sha1 prefix, but until then...
                        die ("Could not add %s to temporary index", name);
        }
  
 -      fd = open(filename, O_WRONLY | O_CREAT, 0666);
 -      if (fd < 0 || write_index(&result, fd) || close(fd))
 +      hold_lock_file_for_update(&lock, filename, LOCK_DIE_ON_ERROR);
 +      if (write_locked_index(&result, &lock, COMMIT_LOCK))
                die ("Could not write temporary index to %s", filename);
  
        discard_index(&result);
@@@ -3845,10 -3910,9 +3910,10 @@@ static void add_index_file(const char *
        ce->ce_flags = create_ce_flags(0);
        ce->ce_namelen = namelen;
        if (S_ISGITLINK(mode)) {
 -              const char *s = buf;
 +              const char *s;
  
 -              if (get_sha1_hex(s + strlen("Subproject commit "), ce->sha1))
 +              if (!skip_prefix(buf, "Subproject commit ", &s) ||
 +                  get_sha1_hex(s, ce->sha1))
                        die(_("corrupt patch for submodule %s"), path);
        } else {
                if (!cached) {
@@@ -4127,64 -4191,6 +4192,6 @@@ static int write_out_results(struct pat
  
  static struct lock_file lock_file;
  
- static struct string_list limit_by_name;
- static int has_include;
- static void add_name_limit(const char *name, int exclude)
- {
-       struct string_list_item *it;
-       it = string_list_append(&limit_by_name, name);
-       it->util = exclude ? NULL : (void *) 1;
- }
- static int use_patch(struct patch *p)
- {
-       const char *pathname = p->new_name ? p->new_name : p->old_name;
-       int i;
-       /* Paths outside are not touched regardless of "--include" */
-       if (0 < prefix_length) {
-               int pathlen = strlen(pathname);
-               if (pathlen <= prefix_length ||
-                   memcmp(prefix, pathname, prefix_length))
-                       return 0;
-       }
-       /* See if it matches any of exclude/include rule */
-       for (i = 0; i < limit_by_name.nr; i++) {
-               struct string_list_item *it = &limit_by_name.items[i];
-               if (!wildmatch(it->string, pathname, 0, NULL))
-                       return (it->util != NULL);
-       }
-       /*
-        * If we had any include, a path that does not match any rule is
-        * not used.  Otherwise, we saw bunch of exclude rules (or none)
-        * and such a path is used.
-        */
-       return !has_include;
- }
- static void prefix_one(char **name)
- {
-       char *old_name = *name;
-       if (!old_name)
-               return;
-       *name = xstrdup(prefix_filename(prefix, prefix_length, *name));
-       free(old_name);
- }
- static void prefix_patches(struct patch *p)
- {
-       if (!prefix || p->is_toplevel_relative)
-               return;
-       for ( ; p; p = p->next) {
-               prefix_one(&p->new_name);
-               prefix_one(&p->old_name);
-       }
- }
  #define INACCURATE_EOF        (1<<0)
  #define RECOUNT               (1<<1)
  
@@@ -4210,8 -4216,6 +4217,6 @@@ static int apply_patch(int fd, const ch
                        break;
                if (apply_in_reverse)
                        reverse_patches(patch);
-               if (prefix)
-                       prefix_patches(patch);
                if (use_patch(patch)) {
                        patch_stats(patch);
                        *listp = patch;
@@@ -4502,7 -4506,8 +4507,7 @@@ int cmd_apply(int argc, const char **ar
        }
  
        if (update_index) {
 -              if (write_cache(newfd, active_cache, active_nr) ||
 -                  commit_locked_index(&lock_file))
 +              if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                        die(_("Unable to write new index file"));
        }
  
diff --combined t/t4119-apply-config.sh
index c393be691be42a0b0a982c07ffeee489d4075bce,be325fa1aee086a94ece935129cc96e897d9d4e3..a9a05838119c85bc017f1404b134d393029843b2
@@@ -68,7 -68,7 +68,7 @@@ test_expect_success 'apply --whitespace
        check_result sub/file1
  '
  
 -D=`pwd`
 +D=$(pwd)
  
  test_expect_success 'apply --whitespace=strip in subdir' '
  
@@@ -159,4 -159,21 +159,21 @@@ test_expect_success 'same but with trad
        check_result sub/file1
  '
  
+ test_expect_success 'in subdir with traditional patch input' '
+       cd "$D" &&
+       git config apply.whitespace strip &&
+       cat >.gitattributes <<-EOF &&
+       /* whitespace=blank-at-eol
+       sub/* whitespace=-blank-at-eol
+       EOF
+       rm -f sub/file1 &&
+       cp saved sub/file1 &&
+       git update-index --refresh &&
+       cd sub &&
+       git apply ../gpatch.file &&
+       echo "B " >expect &&
+       test_cmp expect file1
+ '
  test_done