diff options
Diffstat (limited to 'builtin/apply.c')
-rw-r--r-- | builtin/apply.c | 144 |
1 files changed, 100 insertions, 44 deletions
diff --git a/builtin/apply.c b/builtin/apply.c index 156b3ce3b7..50912c928f 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -473,7 +473,7 @@ static char *find_name_gnu(const char *line, const char *def, int p_value) /* * Proposed "new-style" GNU patch/diff format; see - * http://marc.theaimsgroup.com/?l=git&m=112927316408690&w=2 + * http://marc.info/?l=git&m=112927316408690&w=2 */ if (unquote_c_style(&name, line, NULL)) { strbuf_release(&name); @@ -722,7 +722,7 @@ static char *find_name(const char *line, char *def, int p_value, int terminate) static char *find_name_traditional(const char *line, char *def, int p_value) { - size_t len = strlen(line); + size_t len; size_t date_len; if (*line == '"') { @@ -906,7 +906,7 @@ static void parse_traditional_patch(const char *first, const char *second, struc patch->old_name = name; } else { patch->old_name = name; - patch->new_name = xstrdup(name); + patch->new_name = null_strdup(name); } } if (!name) @@ -1041,15 +1041,17 @@ static int gitdiff_renamedst(const char *line, struct patch *patch) 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; } @@ -1919,7 +1921,7 @@ static int parse_binary(char *buffer, unsigned long size, struct patch *patch) } /* - * Read the patch text in "buffer" taht extends for "size" bytes; stop + * 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). * Create fragments (i.e. patch hunks) and hang them to the given patch. * Return the number of bytes consumed, so that the caller can call us @@ -2095,7 +2097,7 @@ static void update_pre_post_images(struct image *preimage, char *buf, size_t len, size_t postlen) { - int i, ctx; + int i, ctx, reduced; char *new, *old, *fixed; struct image fixed_preimage; @@ -2105,18 +2107,20 @@ static void update_pre_post_images(struct image *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. */ @@ -2126,7 +2130,8 @@ static void update_pre_post_images(struct image *preimage, 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 */ @@ -2145,8 +2150,15 @@ static void update_pre_post_images(struct image *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; @@ -2159,6 +2171,7 @@ static void update_pre_post_images(struct image *preimage, /* Fix the length of the whole thing */ postimage->len = new - postimage->buf; + postimage->nr -= reduced; } static int match_fragment(struct image *img, @@ -2172,7 +2185,7 @@ 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) { @@ -2322,6 +2335,7 @@ static int match_fragment(struct image *img, 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; @@ -2349,6 +2363,7 @@ static int match_fragment(struct image *img, match = (tgtfix.len == fixed.len - fixstart && !memcmp(tgtfix.buf, fixed.buf + fixstart, fixed.len - fixstart)); + postlen += tgtfix.len; strbuf_release(&tgtfix); if (!match) @@ -2386,8 +2401,10 @@ static int match_fragment(struct image *img, * 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: @@ -2982,7 +2999,7 @@ static int read_blob_object(struct strbuf *buf, const unsigned char *sha1, unsig return 0; } -static int read_file_or_gitlink(struct cache_entry *ce, struct strbuf *buf) +static int read_file_or_gitlink(const struct cache_entry *ce, struct strbuf *buf) { if (!ce) return 0; @@ -3012,7 +3029,7 @@ static struct patch *in_fn_table(const char *name) * * The latter is needed to deal with a case where two paths A and B * are swapped by first renaming A to B and then renaming B to A; - * moving A to B should not be prevented due to presense of B as we + * moving A to B should not be prevented due to presence of B as we * will remove it in a later patch. */ #define PATH_TO_BE_DELETED ((struct patch *) -2) @@ -3100,7 +3117,7 @@ static struct patch *previous_patch(struct patch *patch, int *gone) return previous; } -static int verify_index_match(struct cache_entry *ce, struct stat *st) +static int verify_index_match(const struct cache_entry *ce, struct stat *st) { if (S_ISGITLINK(ce->ce_mode)) { if (!S_ISDIR(st->st_mode)) @@ -3113,7 +3130,7 @@ static int verify_index_match(struct cache_entry *ce, struct stat *st) #define SUBMODULE_PATCH_WITHOUT_INDEX 1 static int load_patch_target(struct strbuf *buf, - struct cache_entry *ce, + const struct cache_entry *ce, struct stat *st, const char *name, unsigned expected_mode) @@ -3143,7 +3160,8 @@ static int load_patch_target(struct strbuf *buf, * we read from the result of a previous diff. */ static int load_preimage(struct image *image, - struct patch *patch, struct stat *st, struct cache_entry *ce) + struct patch *patch, struct stat *st, + const struct cache_entry *ce) { struct strbuf buf = STRBUF_INIT; size_t len; @@ -3256,7 +3274,7 @@ static int load_current(struct image *image, struct patch *patch) } static int try_threeway(struct image *image, struct patch *patch, - struct stat *st, struct cache_entry *ce) + struct stat *st, const struct cache_entry *ce) { unsigned char pre_sha1[20], post_sha1[20], our_sha1[20]; struct strbuf buf = STRBUF_INIT; @@ -3326,7 +3344,7 @@ static int try_threeway(struct image *image, struct patch *patch, return 0; } -static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce) +static int apply_data(struct patch *patch, struct stat *st, const struct cache_entry *ce) { struct image image; @@ -3496,7 +3514,7 @@ static int check_patch(struct patch *patch) * * A patch to swap-rename between A and B would first rename A * to B and then rename B to A. While applying the first one, - * the presense of B should not stop A from getting renamed to + * the presence of B should not stop A from getting renamed to * B; ask to_be_deleted() about the later rename. Removal of * B and rename from A to B is handled the same way by asking * was_deleted(). @@ -3508,7 +3526,7 @@ static int check_patch(struct patch *patch) ok_if_exists = 0; if (new_name && - ((0 < patch->is_new) | (0 < patch->is_rename) | patch->is_copy)) { + ((0 < patch->is_new) || patch->is_rename || patch->is_copy)) { int err = check_to_create(new_name, ok_if_exists); if (err && threeway) { @@ -3587,6 +3605,40 @@ static int get_current_sha1(const char *path, unsigned char *sha1) 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) { @@ -3598,7 +3650,6 @@ 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; @@ -3606,20 +3657,25 @@ static void build_fake_ancestor(struct patch *list, const char *filename) 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)) @@ -3792,7 +3848,7 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned const char *s = buf; if (get_sha1_hex(s + strlen("Subproject commit "), ce->sha1)) - die(_("corrupt patch for subproject %s"), path); + die(_("corrupt patch for submodule %s"), path); } else { if (!cached) { if (lstat(path, &st) < 0) |