X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=builtin-apply.c;h=cfd8fceb9033cf5c4c7924d96437b94a9c4fe226;hb=0eb5ebc1d862737ea2b313d0d77d120b3d114099;hp=6c3db60b659533c612d17852f93fd4ae99519ad6;hpb=8ee4a6c2ec6738cfbc815dc59e44825f2a9b9f15;p=git.git diff --git a/builtin-apply.c b/builtin-apply.c index 6c3db60b6..cfd8fceb9 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -12,6 +12,8 @@ #include "blob.h" #include "delta.h" #include "builtin.h" +#include "string-list.h" +#include "dir.h" /* * --check turns on checking that the working tree matches the @@ -45,7 +47,7 @@ static const char *fake_ancestor; static int line_termination = '\n'; static unsigned long p_context = ULONG_MAX; static const char apply_usage[] = -"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--cached] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [--reverse] [--reject] [--verbose] [-z] [-pNUM] [-CNUM] [--whitespace=] ..."; +"git apply [--stat] [--numstat] [--summary] [--check] [--index] [--cached] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [--reverse] [--reject] [--verbose] [-z] [-pNUM] [-CNUM] [--whitespace=] ..."; static enum ws_error_action { nowarn_ws_error, @@ -155,6 +157,7 @@ struct patch { unsigned int is_binary:1; unsigned int is_copy:1; unsigned int is_rename:1; + unsigned int recount:1; struct fragment *fragments; char *result; size_t resultsize; @@ -187,6 +190,13 @@ struct image { struct line *line; }; +/* + * Records filenames that have been touched, in order to handle + * the case where more than one patches touch the same file. + */ + +static struct string_list fn_table; + static uint32_t hash_line(const char *cp, size_t len) { size_t i; @@ -265,7 +275,7 @@ static void say_patch_name(FILE *output, const char *pre, static void read_patch_file(struct strbuf *sb, int fd) { if (strbuf_read(sb, fd, 0) < 0) - die("git-apply: read returned %s", strerror(errno)); + die("git apply: read returned %s", strerror(errno)); /* * Make sure that we have some slop in the buffer @@ -311,13 +321,12 @@ static char *find_name(const char *line, char *def, int p_value, int terminate) const char *start = line; if (*line == '"') { - struct strbuf name; + struct strbuf name = STRBUF_INIT; /* * Proposed "new-style" GNU patch/diff format; see * http://marc.theaimsgroup.com/?l=git&m=112927316408690&w=2 */ - strbuf_init(&name, 0); if (!unquote_c_style(&name, line, NULL)) { char *cp; @@ -497,17 +506,17 @@ static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name, name = orig_name; len = strlen(name); if (isnull) - die("git-apply: bad git-diff - expected /dev/null, got %s on line %d", name, linenr); + die("git apply: bad git-diff - expected /dev/null, got %s on line %d", name, linenr); another = find_name(line, NULL, p_value, TERM_TAB); if (!another || memcmp(another, name, len)) - die("git-apply: bad git-diff - inconsistent %s filename on line %d", oldnew, linenr); + die("git apply: bad git-diff - inconsistent %s filename on line %d", oldnew, linenr); free(another); return orig_name; } else { /* expect "/dev/null" */ if (memcmp("/dev/null", line, 9) || line[9] != '\n') - die("git-apply: bad git-diff - expected /dev/null on line %d", linenr); + die("git apply: bad git-diff - expected /dev/null on line %d", linenr); return NULL; } } @@ -665,11 +674,8 @@ static char *git_header_name(char *line, int llen) if (*line == '"') { const char *cp; - struct strbuf first; - struct strbuf sp; - - strbuf_init(&first, 0); - strbuf_init(&sp, 0); + struct strbuf first = STRBUF_INIT; + struct strbuf sp = STRBUF_INIT; if (unquote_c_style(&first, line, &second)) goto free_and_fail1; @@ -731,10 +737,9 @@ static char *git_header_name(char *line, int llen) */ for (second = name; second < line + llen; second++) { if (*second == '"') { - struct strbuf sp; + struct strbuf sp = STRBUF_INIT; const char *np; - strbuf_init(&sp, 0); if (unquote_c_style(&sp, second, NULL)) goto free_and_fail2; @@ -800,6 +805,13 @@ static int parse_git_header(char *line, int len, unsigned int size, struct patch * the default name from the header. */ 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); + free(patch->def_name); + patch->def_name = s; + } line += len; size -= len; @@ -894,6 +906,56 @@ static int parse_range(const char *line, int len, int offset, const char *expect return offset + ex; } +static void recount_diff(char *line, int size, struct fragment *fragment) +{ + int oldlines = 0, newlines = 0, ret = 0; + + if (size < 1) { + warning("recount: ignore empty hunk"); + return; + } + + for (;;) { + int len = linelen(line, size); + size -= len; + line += len; + + if (size < 1) + break; + + switch (*line) { + case ' ': case '\n': + newlines++; + /* fall through */ + case '-': + oldlines++; + continue; + case '+': + newlines++; + continue; + case '\\': + continue; + case '@': + ret = size < 3 || prefixcmp(line, "@@ "); + break; + case 'd': + ret = size < 5 || prefixcmp(line, "diff "); + break; + default: + ret = -1; + break; + } + if (ret) { + warning("recount: unexpected line: %.*s", + (int)linelen(line, size), line); + return; + } + break; + } + fragment->oldlines = oldlines; + fragment->newlines = newlines; +} + /* * Parse a unified diff fragment header of the * form "@@ -a,b +c,d @@" @@ -991,8 +1053,7 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc static void check_whitespace(const char *line, int len, unsigned ws_rule) { char *err; - unsigned result = check_and_emit_line(line + 1, len - 1, ws_rule, - NULL, NULL, NULL, NULL); + unsigned result = ws_check(line + 1, len - 1, ws_rule); if (!result) return; @@ -1003,7 +1064,7 @@ static void check_whitespace(const char *line, int len, unsigned ws_rule) else { err = whitespace_error_string(result); fprintf(stderr, "%s:%d: %s.\n%.*s\n", - patch_input_file, linenr, err, len - 2, line + 1); + patch_input_file, linenr, err, len - 2, line + 1); free(err); } } @@ -1025,6 +1086,8 @@ static int parse_fragment(char *line, unsigned long size, offset = parse_fragment_header(line, len, fragment); if (offset < 0) return -1; + if (offset > 0 && patch->recount) + recount_diff(line + offset, size - offset, fragment); oldlines = fragment->oldlines; newlines = fragment->newlines; leading = 0; @@ -1447,11 +1510,10 @@ static const char minuses[]= static void show_stats(struct patch *patch) { - struct strbuf qname; + struct strbuf qname = STRBUF_INIT; char *cp = patch->new_name ? patch->new_name : patch->old_name; int max, add, del; - strbuf_init(&qname, 0); quote_c_style(cp, &qname, NULL, 0); /* @@ -1636,7 +1698,7 @@ static int match_fragment(struct image *img, fixlen = ws_fix_copy(buf, orig, oldlen, ws_rule, NULL); /* Try fixing the line in the target */ - if (sizeof(tgtfixbuf) < tgtlen) + if (sizeof(tgtfixbuf) > tgtlen) tgtfix = tgtfixbuf; else tgtfix = xmalloc(tgtlen); @@ -1936,6 +1998,8 @@ static int apply_one_fragment(struct image *img, struct fragment *frag, /* * A hunk to change lines at the beginning would begin with * @@ -1,L +N,M @@ + * but we need to be careful. -U0 that inserts before the second + * line also has this pattern. * * And a hunk to add to an empty file would begin with * @@ -0,0 +N,M @@ @@ -1943,7 +2007,8 @@ static int apply_one_fragment(struct image *img, struct fragment *frag, * In other words, a hunk that is (frag->oldpos <= 1) with or * without leading context must match at the beginning. */ - match_beginning = frag->oldpos <= 1; + match_beginning = (!frag->oldpos || + (frag->oldpos == 1 && !unidiff_zero)); /* * A hunk without trailing lines must match at the end. @@ -2188,15 +2253,61 @@ static int read_file_or_gitlink(struct cache_entry *ce, struct strbuf *buf) return 0; } +static struct patch *in_fn_table(const char *name) +{ + struct string_list_item *item; + + if (name == NULL) + return NULL; + + item = string_list_lookup(name, &fn_table); + if (item != NULL) + return (struct patch *)item->util; + + return NULL; +} + +static void add_to_fn_table(struct patch *patch) +{ + struct string_list_item *item; + + /* + * Always add new_name unless patch is a deletion + * This should cover the cases for normal diffs, + * file creations and copies + */ + if (patch->new_name != NULL) { + item = string_list_insert(patch->new_name, &fn_table); + item->util = patch; + } + + /* + * store a failure on rename/deletion cases because + * later chunks shouldn't patch old names + */ + if ((patch->new_name == NULL) || (patch->is_rename)) { + item = string_list_insert(patch->old_name, &fn_table); + item->util = (struct patch *) -1; + } +} + static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce) { - struct strbuf buf; + struct strbuf buf = STRBUF_INIT; struct image image; size_t len; char *img; + struct patch *tpatch; - strbuf_init(&buf, 0); - if (cached) { + if (!(patch->is_copy || patch->is_rename) && + ((tpatch = in_fn_table(patch->old_name)) != NULL)) { + if (tpatch == (struct patch *) -1) { + return error("patch %s has been renamed/deleted", + patch->old_name); + } + /* We have a patched copy in memory use that */ + strbuf_add(&buf, tpatch->result, tpatch->resultsize); + } else if (cached) { if (read_file_or_gitlink(ce, &buf)) return error("read of %s failed", patch->old_name); } else if (patch->old_name) { @@ -2223,6 +2334,7 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry * return -1; /* note with --reject this succeeds. */ patch->result = image.buf; patch->resultsize = image.len; + add_to_fn_table(patch); free(image.line_allocated); if (0 < patch->is_delete && patch->resultsize) @@ -2267,6 +2379,7 @@ static int verify_index_match(struct cache_entry *ce, struct stat *st) static int check_preimage(struct patch *patch, struct cache_entry **ce, struct stat *st) { const char *old_name = patch->old_name; + struct patch *tpatch = NULL; int stat_ret = 0; unsigned st_mode = 0; @@ -2280,12 +2393,20 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s return 0; assert(patch->is_new <= 0); - if (!cached) { + + if (!(patch->is_copy || patch->is_rename) && + (tpatch = in_fn_table(old_name)) != NULL) { + if (tpatch == (struct patch *) -1) { + return error("%s: has been deleted/renamed", old_name); + } + st_mode = tpatch->new_mode; + } else if (!cached) { stat_ret = lstat(old_name, st); if (stat_ret && errno != ENOENT) return error("%s: %s", old_name, strerror(errno)); } - if (check_index) { + + if (check_index && !tpatch) { int pos = cache_name_pos(old_name, strlen(old_name)); if (pos < 0) { if (patch->is_new < 0) @@ -2337,7 +2458,7 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s return 0; } -static int check_patch(struct patch *patch, struct patch *prev_patch) +static int check_patch(struct patch *patch) { struct stat st; const char *old_name = patch->old_name; @@ -2354,8 +2475,7 @@ static int check_patch(struct patch *patch, struct patch *prev_patch) return status; old_name = patch->old_name; - if (new_name && prev_patch && 0 < prev_patch->is_delete && - !strcmp(prev_patch->old_name, new_name)) + if (in_fn_table(new_name) == (struct patch *) -1) /* * A type-change diff is always split into a patch to * delete old, immediately followed by a patch to @@ -2405,15 +2525,14 @@ static int check_patch(struct patch *patch, struct patch *prev_patch) static int check_patch_list(struct patch *patch) { - struct patch *prev_patch = NULL; int err = 0; - for (prev_patch = NULL; patch ; patch = patch->next) { + while (patch) { if (apply_verbosely) say_patch_name(stderr, "Checking patch ", patch, "...\n"); - err |= check_patch(patch, prev_patch); - prev_patch = patch; + err |= check_patch(patch); + patch = patch->next; } return err; } @@ -2466,6 +2585,8 @@ static void build_fake_ancestor(struct patch *list, const char *filename) sha1_ptr = sha1; ce = make_cache_entry(patch->old_mode, sha1_ptr, 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)) die ("Could not add %s to temporary index", name); } @@ -2616,15 +2737,7 @@ static void remove_file(struct patch *patch, int rmdir_empty) warning("unable to remove submodule %s", patch->old_name); } else if (!unlink(patch->old_name) && rmdir_empty) { - char *name = xstrdup(patch->old_name); - char *end = strrchr(name, '/'); - while (end) { - *end = 0; - if (rmdir(name)) - break; - end = strrchr(name, '/'); - } - free(name); + remove_path(patch->old_name); } } } @@ -2665,7 +2778,7 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned static int try_create_file(const char *path, unsigned int mode, const char *buf, unsigned long size) { int fd; - struct strbuf nbuf; + struct strbuf nbuf = STRBUF_INIT; if (S_ISGITLINK(mode)) { struct stat st; @@ -2684,7 +2797,6 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf, if (fd < 0) return -1; - strbuf_init(&nbuf, 0); if (convert_to_working_tree(path, buf, size, &nbuf)) { size = nbuf.len; buf = nbuf.buf; @@ -2875,29 +2987,45 @@ static int write_out_results(struct patch *list, int skipped_patch) static struct lock_file lock_file; -static struct excludes { - struct excludes *next; - const char *path; -} *excludes; +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(name, &limit_by_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; - struct excludes *x = excludes; - while (x) { - if (fnmatch(x->path, pathname, 0) == 0) - return 0; - x = x->next; - } + 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; } - return 1; + + /* 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 (!fnmatch(it->string, pathname, 0)) + 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; @@ -2924,14 +3052,18 @@ static void prefix_patches(struct patch *p) } } -static int apply_patch(int fd, const char *filename, int inaccurate_eof) +#define INACCURATE_EOF (1<<0) +#define RECOUNT (1<<1) + +static int apply_patch(int fd, const char *filename, int options) { size_t offset; - struct strbuf buf; + struct strbuf buf = STRBUF_INIT; struct patch *list = NULL, **listp = &list; int skipped_patch = 0; - strbuf_init(&buf, 0); + /* FIXME - memory leak when using multiple patch files as inputs */ + memset(&fn_table, 0, sizeof(struct string_list)); patch_input_file = filename; read_patch_file(&buf, fd); offset = 0; @@ -2940,7 +3072,8 @@ static int apply_patch(int fd, const char *filename, int inaccurate_eof) int nr; patch = xcalloc(1, sizeof(*patch)); - patch->inaccurate_eof = inaccurate_eof; + patch->inaccurate_eof = !!(options & INACCURATE_EOF); + patch->recount = !!(options & RECOUNT); nr = parse_chunk(buf.buf + offset, buf.len - offset, patch); if (nr < 0) break; @@ -3009,7 +3142,7 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix) { int i; int read_stdin = 1; - int inaccurate_eof = 0; + int options = 0; int errs = 0; int is_not_gitdir; @@ -3027,15 +3160,17 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix) int fd; if (!strcmp(arg, "-")) { - errs |= apply_patch(0, "", inaccurate_eof); + errs |= apply_patch(0, "", options); read_stdin = 0; continue; } if (!prefixcmp(arg, "--exclude=")) { - struct excludes *x = xmalloc(sizeof(*x)); - x->path = arg + 10; - x->next = excludes; - excludes = x; + add_name_limit(arg + 10, 1); + continue; + } + if (!prefixcmp(arg, "--include=")) { + add_name_limit(arg + 10, 0); + has_include = 1; continue; } if (!prefixcmp(arg, "-p")) { @@ -3127,11 +3262,15 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix) continue; } if (!strcmp(arg, "--inaccurate-eof")) { - inaccurate_eof = 1; + options |= INACCURATE_EOF; + continue; + } + if (!strcmp(arg, "--recount")) { + options |= RECOUNT; continue; } - if (!prefixcmp(arg, "--root=")) { - arg += strlen("--root="); + if (!prefixcmp(arg, "--directory=")) { + arg += strlen("--directory="); root_len = strlen(arg); if (root_len && arg[root_len - 1] != '/') { char *new_root; @@ -3150,12 +3289,12 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix) die("can't open patch '%s': %s", arg, strerror(errno)); read_stdin = 0; set_default_whitespace_mode(whitespace_option); - errs |= apply_patch(fd, arg, inaccurate_eof); + errs |= apply_patch(fd, arg, options); close(fd); } set_default_whitespace_mode(whitespace_option); if (read_stdin) - errs |= apply_patch(0, "", inaccurate_eof); + errs |= apply_patch(0, "", options); if (whitespace_error) { if (squelch_whitespace_errors && squelch_whitespace_errors < whitespace_error) {