X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=builtin-apply.c;h=cfd8fceb9033cf5c4c7924d96437b94a9c4fe226;hb=0eb5ebc1d862737ea2b313d0d77d120b3d114099;hp=9fcfe3955dcd1aaca92a812b6253a331290922fa;hpb=7d3580d74c6d801419dd91416f9a81750b7d4e2c;p=git.git diff --git a/builtin-apply.c b/builtin-apply.c index 9fcfe3955..cfd8fceb9 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -12,7 +12,8 @@ #include "blob.h" #include "delta.h" #include "builtin.h" -#include "path-list.h" +#include "string-list.h" +#include "dir.h" /* * --check turns on checking that the working tree matches the @@ -46,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, @@ -58,6 +59,8 @@ static int whitespace_error; static int squelch_whitespace_errors = 5; static int applied_after_fixing_ws; static const char *patch_input_file; +static const char *root; +static int root_len; static void parse_whitespace_option(const char *option) { @@ -192,7 +195,7 @@ struct image { * the case where more than one patches touch the same file. */ -static struct path_list fn_table; +static struct string_list fn_table; static uint32_t hash_line(const char *cp, size_t len) { @@ -272,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 @@ -318,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; @@ -340,6 +342,8 @@ static char *find_name(const char *line, char *def, int p_value, int terminate) */ strbuf_remove(&name, 0, cp - name.buf); free(def); + if (root) + strbuf_insert(&name, 0, root, root_len); return strbuf_detach(&name, NULL); } } @@ -378,6 +382,14 @@ static char *find_name(const char *line, char *def, int p_value, int terminate) free(def); } + if (root) { + char *ret = xmalloc(root_len + len + 1); + strcpy(ret, root); + memcpy(ret + root_len, start, len); + ret[root_len + len] = '\0'; + return ret; + } + return xmemdupz(start, len); } @@ -494,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; } } @@ -662,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; @@ -728,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; @@ -797,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; @@ -919,7 +934,7 @@ static void recount_diff(char *line, int size, struct fragment *fragment) newlines++; continue; case '\\': - break; + continue; case '@': ret = size < 3 || prefixcmp(line, "@@ "); break; @@ -1495,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); /* @@ -1684,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); @@ -1984,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 @@ @@ -1991,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. @@ -2238,12 +2255,12 @@ static int read_file_or_gitlink(struct cache_entry *ce, struct strbuf *buf) static struct patch *in_fn_table(const char *name) { - struct path_list_item *item; + struct string_list_item *item; if (name == NULL) return NULL; - item = path_list_lookup(name, &fn_table); + item = string_list_lookup(name, &fn_table); if (item != NULL) return (struct patch *)item->util; @@ -2252,7 +2269,7 @@ static struct patch *in_fn_table(const char *name) static void add_to_fn_table(struct patch *patch) { - struct path_list_item *item; + struct string_list_item *item; /* * Always add new_name unless patch is a deletion @@ -2260,7 +2277,7 @@ static void add_to_fn_table(struct patch *patch) * file creations and copies */ if (patch->new_name != NULL) { - item = path_list_insert(patch->new_name, &fn_table); + item = string_list_insert(patch->new_name, &fn_table); item->util = patch; } @@ -2269,22 +2286,21 @@ static void add_to_fn_table(struct patch *patch) * later chunks shouldn't patch old names */ if ((patch->new_name == NULL) || (patch->is_rename)) { - item = path_list_insert(patch->old_name, &fn_table); + 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 ((tpatch = in_fn_table(patch->old_name)) != NULL) { + 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); @@ -2363,7 +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; + struct patch *tpatch = NULL; int stat_ret = 0; unsigned st_mode = 0; @@ -2377,7 +2393,9 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s return 0; assert(patch->is_new <= 0); - if ((tpatch = in_fn_table(old_name)) != NULL) { + + 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); } @@ -2387,6 +2405,7 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s if (stat_ret && errno != ENOENT) return error("%s: %s", old_name, strerror(errno)); } + if (check_index && !tpatch) { int pos = cache_name_pos(old_name, strlen(old_name)); if (pos < 0) { @@ -2566,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); } @@ -2716,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); } } } @@ -2765,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; @@ -2784,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; @@ -2975,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; @@ -3030,13 +3058,12 @@ static void prefix_patches(struct patch *p) 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; /* FIXME - memory leak when using multiple patch files as inputs */ - memset(&fn_table, 0, sizeof(struct path_list)); - strbuf_init(&buf, 0); + memset(&fn_table, 0, sizeof(struct string_list)); patch_input_file = filename; read_patch_file(&buf, fd); offset = 0; @@ -3138,10 +3165,12 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix) 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")) { @@ -3240,6 +3269,18 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix) options |= RECOUNT; continue; } + if (!prefixcmp(arg, "--directory=")) { + arg += strlen("--directory="); + root_len = strlen(arg); + if (root_len && arg[root_len - 1] != '/') { + char *new_root; + root = new_root = xmalloc(root_len + 2); + strcpy(new_root, arg); + strcpy(new_root + root_len++, "/"); + } else + root = arg; + continue; + } if (0 < prefix_length) arg = prefix_filename(prefix, prefix_length, arg);