Merge branch 'tk/apply-dev-null-verify-name-fix'
authorJunio C Hamano <gitster@pobox.com>
Wed, 28 Feb 2018 21:37:55 +0000 (13:37 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 28 Feb 2018 21:37:55 +0000 (13:37 -0800)
Many places in "git apply" knew that "/dev/null" that signals
"there is no such file on this side of the diff" can be followed by
whitespace and garbage when parsing a patch, except for one, which
made an otherwise valid patch (e.g. ones from subversion) rejected.

* tk/apply-dev-null-verify-name-fix:
apply: handle Subversion diffs with /dev/null gracefully
apply: demonstrate a problem applying svn diffs

1  2 
apply.c
diff --combined apply.c
index 40a368b3153f90ff0061894d0af5db5b401a00d9,3aadbae30523d7f41140220363805da3fd593118..7c88d4ee71304825a475fc1e67ed38b48621cb3a
+++ b/apply.c
@@@ -75,10 -75,13 +75,10 @@@ static int parse_ignorewhitespace_optio
  }
  
  int init_apply_state(struct apply_state *state,
 -                   const char *prefix,
 -                   struct lock_file *lock_file)
 +                   const char *prefix)
  {
        memset(state, 0, sizeof(*state));
        state->prefix = prefix;
 -      state->lock_file = lock_file;
 -      state->newfd = -1;
        state->apply = 1;
        state->line_termination = '\n';
        state->p_value = 1;
@@@ -143,6 -146,8 +143,6 @@@ int check_apply_state(struct apply_stat
        }
        if (state->check_index)
                state->unsafe_paths = 0;
 -      if (!state->lock_file)
 -              return error("BUG: state->lock_file should not be NULL");
  
        if (state->apply_verbosity <= verbosity_silent) {
                state->saved_error_routine = get_error_routine();
@@@ -300,33 -305,52 +300,33 @@@ static uint32_t hash_line(const char *c
  static int fuzzy_matchlines(const char *s1, size_t n1,
                            const char *s2, size_t n2)
  {
 -      const char *last1 = s1 + n1 - 1;
 -      const char *last2 = s2 + n2 - 1;
 -      int result = 0;
 +      const char *end1 = s1 + n1;
 +      const char *end2 = s2 + n2;
  
        /* ignore line endings */
 -      while ((*last1 == '\r') || (*last1 == '\n'))
 -              last1--;
 -      while ((*last2 == '\r') || (*last2 == '\n'))
 -              last2--;
 -
 -      /* 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;
 -      while (!result) {
 -              result = *s1++ - *s2++;
 -              /*
 -               * Skip whitespace inside. We check for whitespace on
 -               * both buffers because we don't want "a b" to match
 -               * "ab"
 -               */
 -              if (isspace(*s1) && isspace(*s2)) {
 -                      while (isspace(*s1) && s1 <= last1)
 +      while (s1 < end1 && (end1[-1] == '\r' || end1[-1] == '\n'))
 +              end1--;
 +      while (s2 < end2 && (end2[-1] == '\r' || end2[-1] == '\n'))
 +              end2--;
 +
 +      while (s1 < end1 && s2 < end2) {
 +              if (isspace(*s1)) {
 +                      /*
 +                       * Skip whitespace. We check on both buffers
 +                       * because we don't want "a b" to match "ab".
 +                       */
 +                      if (!isspace(*s2))
 +                              return 0;
 +                      while (s1 < end1 && isspace(*s1))
                                s1++;
 -                      while (isspace(*s2) && s2 <= last2)
 +                      while (s2 < end2 && isspace(*s2))
                                s2++;
 -              }
 -              /*
 -               * If we reached the end on one side only,
 -               * lines don't match
 -               */
 -              if (
 -                  ((s2 > last2) && (s1 <= last1)) ||
 -                  ((s1 > last1) && (s2 <= last2)))
 +              } else if (*s1++ != *s2++)
                        return 0;
 -              if ((s1 > last1) && (s2 > last2))
 -                      break;
        }
  
 -      return !result;
 +      /* If we reached the end on one side only, lines don't match. */
 +      return s1 == end1 && s2 == end2;
  }
  
  static void add_line_info(struct image *img, const char *bol, size_t len, unsigned flag)
@@@ -788,13 -812,16 +788,13 @@@ static int has_epoch_timestamp(const ch
         * 1970-01-01, and the seconds part must be "00".
         */
        const char stamp_regexp[] =
 -              "^(1969-12-31|1970-01-01)"
 -              " "
 -              "[0-2][0-9]:[0-5][0-9]:00(\\.0+)?"
 +              "^[0-2][0-9]:([0-5][0-9]):00(\\.0+)?"
                " "
                "([-+][0-2][0-9]:?[0-5][0-9])\n";
        const char *timestamp = NULL, *cp, *colon;
        static regex_t *stamp;
        regmatch_t m[10];
 -      int zoneoffset;
 -      int hourminute;
 +      int zoneoffset, epoch_hour, hour, minute;
        int status;
  
        for (cp = nameline; *cp != '\n'; cp++) {
        }
        if (!timestamp)
                return 0;
 +
 +      /*
 +       * YYYY-MM-DD hh:mm:ss must be from either 1969-12-31
 +       * (west of GMT) or 1970-01-01 (east of GMT)
 +       */
 +      if (skip_prefix(timestamp, "1969-12-31 ", &timestamp))
 +              epoch_hour = 24;
 +      else if (skip_prefix(timestamp, "1970-01-01 ", &timestamp))
 +              epoch_hour = 0;
 +      else
 +              return 0;
 +
        if (!stamp) {
                stamp = xmalloc(sizeof(*stamp));
                if (regcomp(stamp, stamp_regexp, REG_EXTENDED)) {
                return 0;
        }
  
 +      hour = strtol(timestamp, NULL, 10);
 +      minute = strtol(timestamp + m[1].rm_so, NULL, 10);
 +
        zoneoffset = strtol(timestamp + m[3].rm_so + 1, (char **) &colon, 10);
        if (*colon == ':')
                zoneoffset = zoneoffset * 60 + strtol(colon + 1, NULL, 10);
        if (timestamp[m[3].rm_so] == '-')
                zoneoffset = -zoneoffset;
  
 -      /*
 -       * YYYY-MM-DD hh:mm:ss must be from either 1969-12-31
 -       * (west of GMT) or 1970-01-01 (east of GMT)
 -       */
 -      if ((zoneoffset < 0 && memcmp(timestamp, "1969-12-31", 10)) ||
 -          (0 <= zoneoffset && memcmp(timestamp, "1970-01-01", 10)))
 -              return 0;
 -
 -      hourminute = (strtol(timestamp + 11, NULL, 10) * 60 +
 -                    strtol(timestamp + 14, NULL, 10) -
 -                    zoneoffset);
 -
 -      return ((zoneoffset < 0 && hourminute == 1440) ||
 -              (0 <= zoneoffset && !hourminute));
 +      return hour * 60 + minute - zoneoffset == epoch_hour * 60;
  }
  
  /*
@@@ -950,7 -975,7 +950,7 @@@ static int gitdiff_verify_name(struct a
                }
                free(another);
        } else {
-               if (!starts_with(line, "/dev/null\n"))
+               if (!is_dev_null(line))
                        return error(_("git apply: bad git-diff - expected /dev/null on line %d"), state->linenr);
        }
  
@@@ -2263,8 -2288,8 +2263,8 @@@ static void show_stats(struct apply_sta
  static int read_old_data(struct stat *st, struct patch *patch,
                         const char *path, struct strbuf *buf)
  {
 -      enum safe_crlf safe_crlf = patch->crlf_in_old ?
 -              SAFE_CRLF_KEEP_CRLF : SAFE_CRLF_RENORMALIZE;
 +      int conv_flags = patch->crlf_in_old ?
 +              CONV_EOL_KEEP_CRLF : CONV_EOL_RENORMALIZE;
        switch (st->st_mode & S_IFMT) {
        case S_IFLNK:
                if (strbuf_readlink(buf, path, st->st_size) < 0)
                 * should never look at the index when explicit crlf option
                 * is given.
                 */
 -              convert_to_git(NULL, path, buf->buf, buf->len, buf, safe_crlf);
 +              convert_to_git(NULL, path, buf->buf, buf->len, buf, conv_flags);
                return 0;
        default:
                return -1;
@@@ -2896,7 -2921,6 +2896,7 @@@ static int apply_one_fragment(struct ap
                        if (plen && (ws_rule & WS_BLANK_AT_EOF) &&
                            ws_blank_line(patch + 1, plen, ws_rule))
                                is_blank_context = 1;
 +                      /* fallthrough */
                case '-':
                        memcpy(old, patch + 1, plen);
                        add_line_info(&preimage, old, plen,
                        old += plen;
                        if (first == '-')
                                break;
 -              /* Fall-through for ' ' */
 +                      /* fallthrough */
                case '+':
                        /* --no-add does not add new lines */
                        if (first == '+' && state->no_add)
            newlines.len > 0 && newlines.buf[newlines.len - 1] == '\n') {
                old--;
                strbuf_setlen(&newlines, newlines.len - 1);
 +              preimage.line_allocated[preimage.nr - 1].len--;
 +              postimage.line_allocated[postimage.nr - 1].len--;
        }
  
        leading = frag->leading;
@@@ -3154,7 -3176,7 +3154,7 @@@ static int apply_binary(struct apply_st
                 * See if the old one matches what the patch
                 * applies to.
                 */
 -              hash_sha1_file(img->buf, img->len, blob_type, oid.hash);
 +              hash_object_file(img->buf, img->len, blob_type, &oid);
                if (strcmp(oid_to_hex(&oid), patch->old_sha1_prefix))
                        return error(_("the patch applies to '%s' (%s), "
                                       "which does not match the "
                                     name);
  
                /* verify that the result matches */
 -              hash_sha1_file(img->buf, img->len, blob_type, oid.hash);
 +              hash_object_file(img->buf, img->len, blob_type, &oid);
                if (strcmp(oid_to_hex(&oid), patch->new_sha1_prefix))
                        return error(_("binary patch to '%s' creates incorrect result (expecting %s, got %s)"),
                                name, patch->new_sha1_prefix, oid_to_hex(&oid));
@@@ -3554,8 -3576,8 +3554,8 @@@ static int try_threeway(struct apply_st
  
        /* Preimage the patch was prepared for */
        if (patch->is_new)
 -              write_sha1_file("", 0, blob_type, pre_oid.hash);
 -      else if (get_sha1(patch->old_sha1_prefix, pre_oid.hash) ||
 +              write_object_file("", 0, blob_type, &pre_oid);
 +      else if (get_oid(patch->old_sha1_prefix, &pre_oid) ||
                 read_blob_object(&buf, &pre_oid, patch->old_mode))
                return error(_("repository lacks the necessary blob to fall back on 3-way merge."));
  
                return -1;
        }
        /* post_oid is theirs */
 -      write_sha1_file(tmp_image.buf, tmp_image.len, blob_type, post_oid.hash);
 +      write_object_file(tmp_image.buf, tmp_image.len, blob_type, &post_oid);
        clear_image(&tmp_image);
  
        /* our_oid is ours */
                        return error(_("cannot read the current contents of '%s'"),
                                     patch->old_name);
        }
 -      write_sha1_file(tmp_image.buf, tmp_image.len, blob_type, our_oid.hash);
 +      write_object_file(tmp_image.buf, tmp_image.len, blob_type, &our_oid);
        clear_image(&tmp_image);
  
        /* in-core three-way merge between post and our using pre as base */
@@@ -4079,7 -4101,7 +4079,7 @@@ static int build_fake_ancestor(struct a
                        else
                                return error(_("sha1 information is lacking or "
                                               "useless for submodule %s"), name);
 -              } else if (!get_sha1_blob(patch->old_sha1_prefix, oid.hash)) {
 +              } else if (!get_oid_blob(patch->old_sha1_prefix, &oid)) {
                        ; /* ok */
                } else if (!patch->lines_added && !patch->lines_deleted) {
                        /* mode-only change: update the current */
@@@ -4291,7 -4313,7 +4291,7 @@@ static int add_index_file(struct apply_
                        }
                        fill_stat_cache_info(ce, &st);
                }
 -              if (write_sha1_file(buf, size, blob_type, ce->oid.hash) < 0) {
 +              if (write_object_file(buf, size, blob_type, &ce->oid) < 0) {
                        free(ce);
                        return error(_("unable to create backing store "
                                       "for newly created file %s"), path);
@@@ -4687,13 -4709,13 +4687,13 @@@ static int apply_patch(struct apply_sta
                state->apply = 0;
  
        state->update_index = state->check_index && state->apply;
 -      if (state->update_index && state->newfd < 0) {
 +      if (state->update_index && !is_lock_file_locked(&state->lock_file)) {
                if (state->index_file)
 -                      state->newfd = hold_lock_file_for_update(state->lock_file,
 -                                                               state->index_file,
 -                                                               LOCK_DIE_ON_ERROR);
 +                      hold_lock_file_for_update(&state->lock_file,
 +                                                state->index_file,
 +                                                LOCK_DIE_ON_ERROR);
                else
 -                      state->newfd = hold_locked_index(state->lock_file, LOCK_DIE_ON_ERROR);
 +                      hold_locked_index(&state->lock_file, LOCK_DIE_ON_ERROR);
        }
  
        if (state->check_index && read_apply_cache(state) < 0) {
@@@ -4889,18 -4911,22 +4889,18 @@@ int apply_all_patches(struct apply_stat
        }
  
        if (state->update_index) {
 -              res = write_locked_index(&the_index, state->lock_file, COMMIT_LOCK);
 +              res = write_locked_index(&the_index, &state->lock_file, COMMIT_LOCK);
                if (res) {
                        error(_("Unable to write new index file"));
                        res = -128;
                        goto end;
                }
 -              state->newfd = -1;
        }
  
        res = !!errs;
  
  end:
 -      if (state->newfd >= 0) {
 -              rollback_lock_file(state->lock_file);
 -              state->newfd = -1;
 -      }
 +      rollback_lock_file(&state->lock_file);
  
        if (state->apply_verbosity <= verbosity_silent) {
                set_error_routine(state->saved_error_routine);