X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=blobdiff_plain;f=builtin-apply.c;h=a3f075df4bd91de8d3423420cfcffd9722ad9586;hb=971dfa19598324f49bd3a010ca629c91d6a0f758;hp=2af625a43587c5ee46f450dde264266c50e8d59b;hpb=ee810b715912347775fa06a0d5b16092b4908d66;p=git.git diff --git a/builtin-apply.c b/builtin-apply.c index 2af625a43..a3f075df4 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -1508,105 +1508,74 @@ static int read_old_data(struct stat *st, const char *path, struct strbuf *buf) case S_IFREG: if (strbuf_read_file(buf, path, st->st_size) != st->st_size) return error("unable to open or read %s", path); - convert_to_git(path, buf->buf, buf->len, buf); + convert_to_git(path, buf->buf, buf->len, buf, 0); return 0; default: return -1; } } -static int copy_wsfix(char *output, const char *patch, int plen, - unsigned ws_rule) +static void update_pre_post_images(struct image *preimage, + struct image *postimage, + char *buf, + size_t len) { - /* - * plen is number of bytes to be copied from patch, starting - * at patch. Typically patch[plen-1] is '\n', unless this is - * the incomplete last line. - */ - int i; - int add_nl_to_tail = 0; - int fixed = 0; - int last_tab_in_indent = -1; - int last_space_in_indent = -1; - int need_fix_leading_space = 0; - char *buf; + int i, ctx; + char *new, *old, *fixed; + struct image fixed_preimage; /* - * Strip trailing whitespace + * Update the preimage with whitespace fixes. Note that we + * are not losing preimage->buf -- apply_one_fragment() will + * free "oldlines". */ - if ((ws_rule & WS_TRAILING_SPACE) && - (2 < plen && isspace(patch[plen-2]))) { - if (patch[plen-1] == '\n') - add_nl_to_tail = 1; - plen--; - while (0 < plen && isspace(patch[plen-1])) - plen--; - fixed = 1; - } + prepare_image(&fixed_preimage, buf, len, 1); + assert(fixed_preimage.nr == preimage->nr); + for (i = 0; i < preimage->nr; i++) + fixed_preimage.line[i].flag = preimage->line[i].flag; + free(preimage->line_allocated); + *preimage = fixed_preimage; /* - * Check leading whitespaces (indent) + * Adjust the common context lines in postimage, in place. + * This is possible because whitespace fixing does not make + * the string grow. */ - for (i = 0; i < plen; i++) { - char ch = patch[i]; - if (ch == '\t') { - last_tab_in_indent = i; - if ((ws_rule & WS_SPACE_BEFORE_TAB) && - 0 <= last_space_in_indent) - need_fix_leading_space = 1; - } else if (ch == ' ') { - last_space_in_indent = i; - if ((ws_rule & WS_INDENT_WITH_NON_TAB) && - 8 <= i - last_tab_in_indent) - need_fix_leading_space = 1; - } else - break; - } + new = old = postimage->buf; + fixed = preimage->buf; + for (i = 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 */ + memmove(new, old, len); + old += len; + new += len; + continue; + } - buf = output; - if (need_fix_leading_space) { - /* Process indent ourselves */ - int consecutive_spaces = 0; - int last = last_tab_in_indent + 1; + /* a common context -- skip it in the original postimage */ + old += len; - if (ws_rule & WS_INDENT_WITH_NON_TAB) { - /* have "last" point at one past the indent */ - if (last_tab_in_indent < last_space_in_indent) - last = last_space_in_indent + 1; - else - last = last_tab_in_indent + 1; + /* and find the corresponding one in the fixed preimage */ + while (ctx < preimage->nr && + !(preimage->line[ctx].flag & LINE_COMMON)) { + fixed += preimage->line[ctx].len; + ctx++; } + if (preimage->nr <= ctx) + die("oops"); - /* - * between patch[0..last-1], strip the funny spaces, - * updating them to tab as needed. - */ - for (i = 0; i < last; i++) { - char ch = patch[i]; - if (ch != ' ') { - consecutive_spaces = 0; - *output++ = ch; - } else { - consecutive_spaces++; - if (consecutive_spaces == 8) { - *output++ = '\t'; - consecutive_spaces = 0; - } - } - } - while (0 < consecutive_spaces--) - *output++ = ' '; - plen -= last; - patch += last; - fixed = 1; + /* and copy it in, while fixing the line length */ + len = preimage->line[ctx].len; + memcpy(new, fixed, len); + new += len; + fixed += len; + postimage->line[i].len = len; + ctx++; } - memcpy(output, patch, plen); - if (add_nl_to_tail) - output[plen++] = '\n'; - if (fixed) - applied_after_fixing_ws++; - return output + plen - buf; + /* Fix the length of the whole thing */ + postimage->len = new - postimage->buf; } static int match_fragment(struct image *img, @@ -1614,9 +1583,11 @@ static int match_fragment(struct image *img, struct image *postimage, unsigned long try, int try_lno, + unsigned ws_rule, int match_beginning, int match_end) { int i; + char *fixed_buf, *buf, *orig, *target; if (preimage->nr + try_lno > img->nr) return 0; @@ -1645,10 +1616,68 @@ static int match_fragment(struct image *img, !memcmp(img->buf + try, preimage->buf, preimage->len)) return 1; + if (ws_error_action != correct_ws_error) + return 0; + + /* + * The hunk does not apply byte-by-byte, but the hash says + * it might with whitespace fuzz. + */ + fixed_buf = xmalloc(preimage->len + 1); + buf = fixed_buf; + orig = preimage->buf; + target = img->buf + try; + for (i = 0; i < preimage->nr; i++) { + size_t fixlen; /* length after fixing the preimage */ + size_t oldlen = preimage->line[i].len; + size_t tgtlen = img->line[try_lno + i].len; + size_t tgtfixlen; /* length after fixing the target line */ + char tgtfixbuf[1024], *tgtfix; + int match; + + /* Try fixing the line in the preimage */ + fixlen = ws_fix_copy(buf, orig, oldlen, ws_rule, NULL); + + /* Try fixing the line in the target */ + if (sizeof(tgtfixbuf) < tgtlen) + tgtfix = tgtfixbuf; + else + tgtfix = xmalloc(tgtlen); + tgtfixlen = ws_fix_copy(tgtfix, target, tgtlen, ws_rule, NULL); + + /* + * If they match, either the preimage was based on + * a version before our tree fixed whitespace breakage, + * or we are lacking a whitespace-fix patch the tree + * the preimage was based on already had (i.e. target + * has whitespace breakage, the preimage doesn't). + * In either case, we are fixing the whitespace breakages + * so we might as well take the fix together with their + * real change. + */ + match = (tgtfixlen == fixlen && !memcmp(tgtfix, buf, fixlen)); + + if (tgtfix != tgtfixbuf) + free(tgtfix); + if (!match) + goto unmatch_exit; + + orig += oldlen; + buf += fixlen; + target += tgtlen; + } + /* - * NEEDSWORK: We can optionally match fuzzily here, but - * that is for a later round. + * Yes, the preimage is based on an older version that still + * has whitespace breakages unfixed, and fixing them makes the + * hunk match. Update the context lines in the postimage. */ + update_pre_post_images(preimage, postimage, + fixed_buf, buf - fixed_buf); + return 1; + + unmatch_exit: + free(fixed_buf); return 0; } @@ -1656,6 +1685,7 @@ static int find_pos(struct image *img, struct image *preimage, struct image *postimage, int line, + unsigned ws_rule, int match_beginning, int match_end) { int i; @@ -1675,6 +1705,9 @@ static int find_pos(struct image *img, else if (match_end) line = img->nr - preimage->nr; + if (line > img->nr) + line = img->nr; + try = 0; for (i = 0; i < line; i++) try += img->line[i].len; @@ -1691,7 +1724,7 @@ static int find_pos(struct image *img, for (i = 0; ; i++) { if (match_fragment(img, preimage, postimage, - try, try_lno, + try, try_lno, ws_rule, match_beginning, match_end)) return try_lno; @@ -1869,7 +1902,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag, added = plen; } else { - added = copy_wsfix(new, patch + 1, plen, ws_rule); + added = ws_fix_copy(new, patch + 1, plen, ws_rule, &applied_after_fixing_ws); } add_line_info(&postimage, new, added, (first == '+' ? 0 : LINE_COMMON)); @@ -1930,8 +1963,8 @@ static int apply_one_fragment(struct image *img, struct fragment *frag, for (;;) { - applied_pos = find_pos(img, &preimage, &postimage, - pos, match_beginning, match_end); + applied_pos = find_pos(img, &preimage, &postimage, pos, + ws_rule, match_beginning, match_end); if (applied_pos >= 0) break; @@ -2138,7 +2171,7 @@ static int read_file_or_gitlink(struct cache_entry *ce, struct strbuf *buf) if (!ce) return 0; - if (S_ISGITLINK(ntohl(ce->ce_mode))) { + if (S_ISGITLINK(ce->ce_mode)) { strbuf_grow(buf, 100); strbuf_addf(buf, "Subproject commit %s\n", sha1_to_hex(ce->sha1)); } else { @@ -2223,7 +2256,7 @@ static int check_to_create_blob(const char *new_name, int ok_if_exists) static int verify_index_match(struct cache_entry *ce, struct stat *st) { - if (S_ISGITLINK(ntohl(ce->ce_mode))) { + if (S_ISGITLINK(ce->ce_mode)) { if (!S_ISDIR(st->st_mode)) return -1; return 0; @@ -2282,12 +2315,12 @@ static int check_patch(struct patch *patch, struct patch *prev_patch) return error("%s: does not match index", old_name); if (cached) - st_mode = ntohl(ce->ce_mode); + st_mode = ce->ce_mode; } else if (stat_ret < 0) return error("%s: %s", old_name, strerror(errno)); if (!cached) - st_mode = ntohl(ce_mode_from_stat(ce, st.st_mode)); + st_mode = ce_mode_from_stat(ce, st.st_mode); if (patch->is_new < 0) patch->is_new = 0; @@ -2588,7 +2621,7 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned ce = xcalloc(1, ce_size); memcpy(ce->name, path, namelen); ce->ce_mode = create_ce_mode(mode); - ce->ce_flags = htons(namelen); + ce->ce_flags = namelen; if (S_ISGITLINK(mode)) { const char *s = buf; @@ -2946,6 +2979,8 @@ static int apply_patch(int fd, const char *filename, int inaccurate_eof) static int git_apply_config(const char *var, const char *value) { if (!strcmp(var, "apply.whitespace")) { + if (!value) + return config_error_nonbool(var); apply_default_whitespace = xstrdup(value); return 0; }