Merge branch 'jc/apply-ws-fix-tab-in-indent'
authorJunio C Hamano <gitster@pobox.com>
Wed, 3 Apr 2013 16:34:22 +0000 (09:34 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 3 Apr 2013 16:34:22 +0000 (09:34 -0700)
"git apply --whitespace=fix" was not prepared to see a line getting
longer after fixing whitespaces (e.g. tab-in-indent aka Python).

* jc/apply-ws-fix-tab-in-indent:
test: resurrect q_to_tab
apply --whitespace=fix: avoid running over the postimage buffer

1  2 
builtin/apply.c
t/test-lib-functions.sh
diff --combined builtin/apply.c
index 06f5320b18bc5690ae944a722d19ec5496687228,df773c75b9833038327b9411d6d2e3b950f96c0f..5b882d01f6622b1be4f494d2f4a3cbda5adeec08
@@@ -1041,17 -1041,15 +1041,17 @@@ static int gitdiff_renamedst(const cha
  
  static int gitdiff_similarity(const char *line, struct patch *patch)
  {
 -      if ((patch->score = strtoul(line, NULL, 10)) == ULONG_MAX)
 -              patch->score = 0;
 +      unsigned long val = strtoul(line, NULL, 10);
 +      if (val <= 100)
 +              patch->score = val;
        return 0;
  }
  
  static int gitdiff_dissimilarity(const char *line, struct patch *patch)
  {
 -      if ((patch->score = strtoul(line, NULL, 10)) == ULONG_MAX)
 -              patch->score = 0;
 +      unsigned long val = strtoul(line, NULL, 10);
 +      if (val <= 100)
 +              patch->score = val;
        return 0;
  }
  
@@@ -2097,7 -2095,7 +2097,7 @@@ static void update_pre_post_images(stru
                                   char *buf,
                                   size_t len, size_t postlen)
  {
 -      int i, ctx;
 +      int i, ctx, reduced;
        char *new, *old, *fixed;
        struct image fixed_preimage;
  
         * free "oldlines".
         */
        prepare_image(&fixed_preimage, buf, len, 1);
 -      assert(fixed_preimage.nr == preimage->nr);
 -      for (i = 0; i < preimage->nr; i++)
 +      assert(postlen
 +             ? fixed_preimage.nr == preimage->nr
 +             : fixed_preimage.nr <= preimage->nr);
 +      for (i = 0; i < fixed_preimage.nr; i++)
                fixed_preimage.line[i].flag = preimage->line[i].flag;
        free(preimage->line_allocated);
        *preimage = fixed_preimage;
  
        /*
         * Adjust the common context lines in postimage. This can be
-        * done in-place when we are just doing whitespace fixing,
-        * which does not make the string grow, but needs a new buffer
-        * when ignoring whitespace causes the update, since in this case
-        * we could have e.g. tabs converted to multiple spaces.
+        * done in-place when we are shrinking it with whitespace
+        * fixing, but needs a new buffer when ignoring whitespace or
+        * expanding leading tabs to spaces.
+        *
         * We trust the caller to tell us if the update can be done
         * in place (postlen==0) or not.
         */
        else
                new = old;
        fixed = preimage->buf;
 -      for (i = ctx = 0; i < postimage->nr; i++) {
 +
 +      for (i = reduced = ctx = 0; i < postimage->nr; i++) {
                size_t len = postimage->line[i].len;
                if (!(postimage->line[i].flag & LINE_COMMON)) {
                        /* an added line -- no counterparts in preimage */
                        fixed += preimage->line[ctx].len;
                        ctx++;
                }
 -              if (preimage->nr <= ctx)
 -                      die(_("oops"));
 +
 +              /*
 +               * preimage is expected to run out, if the caller
 +               * fixed addition of trailing blank lines.
 +               */
 +              if (preimage->nr <= ctx) {
 +                      reduced++;
 +                      continue;
 +              }
  
                /* and copy it in, while fixing the line length */
                len = preimage->line[ctx].len;
  
        /* Fix the length of the whole thing */
        postimage->len = new - postimage->buf;
 +      postimage->nr -= reduced;
  }
  
  static int match_fragment(struct image *img,
        int i;
        char *fixed_buf, *buf, *orig, *target;
        struct strbuf fixed;
-       size_t fixed_len;
+       size_t fixed_len, postlen;
        int preimage_limit;
  
        if (preimage->nr + try_lno <= img->nr) {
        strbuf_init(&fixed, preimage->len + 1);
        orig = preimage->buf;
        target = img->buf + try;
+       postlen = 0;
        for (i = 0; i < preimage_limit; i++) {
                size_t oldlen = preimage->line[i].len;
                size_t tgtlen = img->line[try_lno + i].len;
                match = (tgtfix.len == fixed.len - fixstart &&
                         !memcmp(tgtfix.buf, fixed.buf + fixstart,
                                             fixed.len - fixstart));
+               postlen += tgtfix.len;
  
                strbuf_release(&tgtfix);
                if (!match)
         * hunk match.  Update the context lines in the postimage.
         */
        fixed_buf = strbuf_detach(&fixed, &fixed_len);
+       if (postlen < postimage->len)
+               postlen = 0;
        update_pre_post_images(preimage, postimage,
-                              fixed_buf, fixed_len, 0);
+                              fixed_buf, fixed_len, postlen);
        return 1;
  
   unmatch_exit:
@@@ -3600,40 -3591,6 +3604,40 @@@ static int get_current_sha1(const char 
        return 0;
  }
  
 +static int preimage_sha1_in_gitlink_patch(struct patch *p, unsigned char sha1[20])
 +{
 +      /*
 +       * A usable gitlink patch has only one fragment (hunk) that looks like:
 +       * @@ -1 +1 @@
 +       * -Subproject commit <old sha1>
 +       * +Subproject commit <new sha1>
 +       * or
 +       * @@ -1 +0,0 @@
 +       * -Subproject commit <old sha1>
 +       * for a removal patch.
 +       */
 +      struct fragment *hunk = p->fragments;
 +      static const char heading[] = "-Subproject commit ";
 +      char *preimage;
 +
 +      if (/* does the patch have only one hunk? */
 +          hunk && !hunk->next &&
 +          /* is its preimage one line? */
 +          hunk->oldpos == 1 && hunk->oldlines == 1 &&
 +          /* does preimage begin with the heading? */
 +          (preimage = memchr(hunk->patch, '\n', hunk->size)) != NULL &&
 +          !prefixcmp(++preimage, heading) &&
 +          /* does it record full SHA-1? */
 +          !get_sha1_hex(preimage + sizeof(heading) - 1, sha1) &&
 +          preimage[sizeof(heading) + 40 - 1] == '\n' &&
 +          /* does the abbreviated name on the index line agree with it? */
 +          !prefixcmp(preimage + sizeof(heading) - 1, p->old_sha1_prefix))
 +              return 0; /* it all looks fine */
 +
 +      /* we may have full object name on the index line */
 +      return get_sha1_hex(p->old_sha1_prefix, sha1);
 +}
 +
  /* Build an index that contains the just the files needed for a 3way merge */
  static void build_fake_ancestor(struct patch *list, const char *filename)
  {
         * worth showing the new sha1 prefix, but until then...
         */
        for (patch = list; patch; patch = patch->next) {
 -              const unsigned char *sha1_ptr;
                unsigned char sha1[20];
                struct cache_entry *ce;
                const char *name;
                name = patch->old_name ? patch->old_name : patch->new_name;
                if (0 < patch->is_new)
                        continue;
 -              else if (get_sha1_blob(patch->old_sha1_prefix, sha1))
 -                      /* git diff has no index line for mode/type changes */
 -                      if (!patch->lines_added && !patch->lines_deleted) {
 -                              if (get_current_sha1(patch->old_name, sha1))
 -                                      die("mode change for %s, which is not "
 -                                              "in current HEAD", name);
 -                              sha1_ptr = sha1;
 -                      } else
 -                              die("sha1 information is lacking or useless "
 -                                      "(%s).", name);
 -              else
 -                      sha1_ptr = sha1;
  
 -              ce = make_cache_entry(patch->old_mode, sha1_ptr, name, 0, 0);
 +              if (S_ISGITLINK(patch->old_mode)) {
 +                      if (!preimage_sha1_in_gitlink_patch(patch, sha1))
 +                              ; /* ok, the textual part looks sane */
 +                      else
 +                              die("sha1 information is lacking or useless for submoule %s",
 +                                  name);
 +              } else if (!get_sha1_blob(patch->old_sha1_prefix, sha1)) {
 +                      ; /* ok */
 +              } else if (!patch->lines_added && !patch->lines_deleted) {
 +                      /* mode-only change: update the current */
 +                      if (get_current_sha1(patch->old_name, sha1))
 +                              die("mode change for %s, which is not "
 +                                  "in current HEAD", name);
 +              } else
 +                      die("sha1 information is lacking or useless "
 +                          "(%s).", name);
 +
 +              ce = make_cache_entry(patch->old_mode, sha1, name, 0, 0);
                if (!ce)
                        die(_("make_cache_entry failed for path '%s'"), name);
                if (add_index_entry(&result, ce, ADD_CACHE_OK_TO_ADD))
diff --combined t/test-lib-functions.sh
index 61d0804435d3e8aed2f3b2c0308bc1a9143f1f5a,4a8bac282bee1253fe9b26e78c1cf3bfc0d7fec0..42c2258b4fdf37b9d2e440f0918f8e4e4f7608f0
@@@ -91,6 -91,10 +91,10 @@@ q_to_tab () 
        tr Q '\011'
  }
  
+ qz_to_tab_space () {
+       tr QZ '\011\040'
+ }
  append_cr () {
        sed -e 's/$/Q/' | tr Q '\015'
  }
@@@ -135,12 -139,12 +139,12 @@@ test_pause () 
        fi
  }
  
 -# Call test_commit with the arguments "<message> [<file> [<contents>]]"
 +# Call test_commit with the arguments "<message> [<file> [<contents> [<tag>]]]"
  #
  # This will commit a file with the given contents and the given commit
 -# message.  It will also add a tag with <message> as name.
 +# message, and tag the resulting commit with the given tag name.
  #
 -# Both <file> and <contents> default to <message>.
 +# <file>, <contents>, and <tag> all default to <message>.
  
  test_commit () {
        notick= &&
                test_tick
        fi &&
        git commit $signoff -m "$1" &&
 -      git tag "$1"
 +      git tag "${4:-$1}"
  }
  
  # Call test_merge with the arguments "<message> <commit>", where <commit>
@@@ -275,15 -279,6 +279,15 @@@ test_have_prereq () 
  
        for prerequisite
        do
 +              case "$prerequisite" in
 +              !*)
 +                      negative_prereq=t
 +                      prerequisite=${prerequisite#!}
 +                      ;;
 +              *)
 +                      negative_prereq=
 +              esac
 +
                case " $lazily_tested_prereq " in
                *" $prerequisite "*)
                        ;;
                total_prereq=$(($total_prereq + 1))
                case "$satisfied_prereq" in
                *" $prerequisite "*)
 +                      satisfied_this_prereq=t
 +                      ;;
 +              *)
 +                      satisfied_this_prereq=
 +              esac
 +
 +              case "$satisfied_this_prereq,$negative_prereq" in
 +              t,|,t)
                        ok_prereq=$(($ok_prereq + 1))
                        ;;
                *)
 -                      # Keep a list of missing prerequisites
 +                      # Keep a list of missing prerequisites; restore
 +                      # the negative marker if necessary.
 +                      prerequisite=${negative_prereq:+!}$prerequisite
                        if test -z "$missing_prereq"
                        then
                                missing_prereq=$prerequisite
@@@ -602,13 -587,6 +606,13 @@@ test_cmp() 
        $GIT_TEST_CMP "$@"
  }
  
 +# Tests that its two parameters refer to the same revision
 +test_cmp_rev () {
 +      git rev-parse --verify "$1" >expect.rev &&
 +      git rev-parse --verify "$2" >actual.rev &&
 +      test_cmp expect.rev actual.rev
 +}
 +
  # Print a sequence of numbers or letters in increasing order.  This is
  # similar to GNU seq(1), but the latter might not be available
  # everywhere (and does not do letters).  It may be used like: