X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=builtin-apply.c;h=abe35387156cf2774c8bc619d74b93c5e83285a8;hb=61397d4b8da2f2ab3fd767c6058ad05ca10269b8;hp=436d9e188070df0e4a51186716b033658a8672b5;hpb=c7757948dc88ab744fd13acc3d0808cca31f9717;p=git.git diff --git a/builtin-apply.c b/builtin-apply.c index 436d9e188..f94d0dbf4 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -6,7 +6,6 @@ * This applies patches on top of some (arbitrary) version of the SCM. * */ -#include #include "cache.h" #include "cache-tree.h" #include "quote.h" @@ -29,8 +28,9 @@ static int newfd = -1; static int unidiff_zero; static int p_value = 1; +static int p_value_known; static int check_index; -static int write_index; +static int update_index; static int cached; static int diffstat; static int numstat; @@ -145,6 +145,7 @@ struct patch { unsigned long deflate_origlen; int lines_added, lines_deleted; int score; + unsigned int is_toplevel_relative:1; unsigned int inaccurate_eof:1; unsigned int is_binary:1; unsigned int is_copy:1; @@ -239,7 +240,7 @@ static int name_terminate(const char *name, int namelen, int c, int terminate) return 1; } -static char * find_name(const char *line, char *def, int p_value, int terminate) +static char *find_name(const char *line, char *def, int p_value, int terminate) { int len; const char *start = line; @@ -312,11 +313,54 @@ static char * find_name(const char *line, char *def, int p_value, int terminate) return name; } +static int count_slashes(const char *cp) +{ + int cnt = 0; + char ch; + + while ((ch = *cp++)) + if (ch == '/') + cnt++; + return cnt; +} + +/* + * Given the string after "--- " or "+++ ", guess the appropriate + * p_value for the given patch. + */ +static int guess_p_value(const char *nameline) +{ + char *name, *cp; + int val = -1; + + if (is_dev_null(nameline)) + return -1; + name = find_name(nameline, NULL, 0, TERM_SPACE | TERM_TAB); + if (!name) + return -1; + cp = strchr(name, '/'); + if (!cp) + val = 0; + else if (prefix) { + /* + * Does it begin with "a/$our-prefix" and such? Then this is + * very likely to apply to our directory. + */ + if (!strncmp(name, prefix, prefix_length)) + val = count_slashes(prefix); + else { + cp++; + if (!strncmp(cp, prefix, prefix_length)) + val = count_slashes(prefix) + 1; + } + } + free(name); + return val; +} + /* * Get the name etc info from the --/+++ lines of a traditional patch header * - * NOTE! This hardcodes "-p1" behaviour in filename detection. - * * FIXME! The end-of-filename heuristics are kind of screwy. For existing * files, we can happily check the index for a match, but for creating a * new file we should try to match whatever "patch" does. I have no idea. @@ -327,6 +371,16 @@ static void parse_traditional_patch(const char *first, const char *second, struc first += 4; /* skip "--- " */ second += 4; /* skip "+++ " */ + if (!p_value_known) { + int p, q; + p = guess_p_value(first); + q = guess_p_value(second); + if (p < 0) p = q; + if (0 <= p && p == q) { + p_value = p; + p_value_known = 1; + } + } if (is_dev_null(first)) { patch->is_new = 1; patch->is_delete = 0; @@ -363,7 +417,7 @@ static int gitdiff_hdrend(const char *line, struct patch *patch) static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name, const char *oldnew) { if (!orig_name && !isnull) - return find_name(line, NULL, 1, TERM_TAB); + return find_name(line, NULL, p_value, TERM_TAB); if (orig_name) { int len; @@ -373,7 +427,7 @@ static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name, len = strlen(name); if (isnull) die("git-apply: bad git-diff - expected /dev/null, got %s on line %d", name, linenr); - another = find_name(line, NULL, 1, TERM_TAB); + 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); free(another); @@ -788,6 +842,7 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc { unsigned long offset, len; + patch->is_toplevel_relative = 0; patch->is_rename = patch->is_copy = 0; patch->is_new = patch->is_delete = -1; patch->old_mode = patch->new_mode = 0; @@ -812,7 +867,8 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc struct fragment dummy; if (parse_fragment_header(line, len, &dummy) < 0) continue; - error("patch fragment without header at line %d: %.*s", linenr, (int)len-1, line); + die("patch fragment without header at line %d: %.*s", + linenr, (int)len-1, line); } if (size < len + 6) @@ -831,6 +887,7 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc die("git diff header lacks filename information (line %d)", linenr); patch->old_name = patch->new_name = patch->def_name; } + patch->is_toplevel_relative = 1; *hdrsize = git_hdr_len; return offset; } @@ -1129,11 +1186,11 @@ static struct fragment *parse_binary_hunk(char **buf_p, *status_p = 0; - if (!strncmp(buffer, "delta ", 6)) { + if (!prefixcmp(buffer, "delta ")) { patch_method = BINARY_DELTA_DEFLATED; origlen = strtoul(buffer + 6, NULL, 10); } - else if (!strncmp(buffer, "literal ", 8)) { + else if (!prefixcmp(buffer, "literal ")) { patch_method = BINARY_LITERAL_DEFLATED; origlen = strtoul(buffer + 8, NULL, 10); } @@ -1393,28 +1450,39 @@ static void show_stats(struct patch *patch) free(qname); } -static int read_old_data(struct stat *st, const char *path, void *buf, unsigned long size) +static int read_old_data(struct stat *st, const char *path, char **buf_p, unsigned long *alloc_p, unsigned long *size_p) { int fd; unsigned long got; + unsigned long nsize; + char *nbuf; + unsigned long size = *size_p; + char *buf = *buf_p; switch (st->st_mode & S_IFMT) { case S_IFLNK: - return readlink(path, buf, size); + return readlink(path, buf, size) != size; case S_IFREG: fd = open(path, O_RDONLY); if (fd < 0) return error("unable to open %s", path); got = 0; for (;;) { - int ret = xread(fd, (char *) buf + got, size - got); + int ret = xread(fd, buf + got, size - got); if (ret <= 0) break; got += ret; } close(fd); - return got; - + nsize = got; + nbuf = convert_to_git(path, buf, &nsize); + if (nbuf) { + free(buf); + *buf_p = nbuf; + *alloc_p = nsize; + *size_p = nsize; + } + return got != size; default: return -1; } @@ -1539,7 +1607,8 @@ static int apply_line(char *output, const char *patch, int plen) int need_fix_leading_space = 0; char *buf; - if ((new_whitespace != strip_whitespace) || !whitespace_error) { + if ((new_whitespace != strip_whitespace) || !whitespace_error || + *patch != '+') { memcpy(output, patch + 1, plen); return plen; } @@ -1655,6 +1724,8 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i /* Ignore it, we already handled it */ break; default: + if (apply_verbosely) + error("invalid start of line: '%c'", first); return -1; } patch += len; @@ -1752,6 +1823,9 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i } } + if (offset && apply_verbosely) + error("while searching for:\n%.*s", oldsize, oldlines); + free(old); free(new); return offset; @@ -1838,11 +1912,11 @@ static int apply_binary(struct buffer_desc *desc, struct patch *patch) if (has_sha1_file(sha1)) { /* We already have the postimage */ - char type[10]; + enum object_type type; unsigned long size; free(desc->buffer); - desc->buffer = read_sha1_file(sha1, type, &size); + desc->buffer = read_sha1_file(sha1, &type, &size); if (!desc->buffer) return error("the necessary postimage %s for " "'%s' cannot be read", @@ -1898,8 +1972,8 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry * buf = NULL; if (cached) { if (ce) { - char type[20]; - buf = read_sha1_file(ce->sha1, type, &size); + enum object_type type; + buf = read_sha1_file(ce->sha1, &type, &size); if (!buf) return error("read of %s failed", patch->old_name); @@ -1907,10 +1981,10 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry * } } else if (patch->old_name) { - size = st->st_size; + size = xsize_t(st->st_size); alloc = size + 8192; buf = xmalloc(alloc); - if (read_old_data(st, patch->old_name, buf, alloc) != size) + if (read_old_data(st, patch->old_name, &buf, &alloc, &size)) return error("read of %s failed", patch->old_name); } @@ -1988,7 +2062,7 @@ static int check_patch(struct patch *patch, struct patch *prev_patch) return error("%s: %s", old_name, strerror(errno)); if (!cached) - st_mode = ntohl(create_ce_mode(st.st_mode)); + st_mode = ntohl(ce_mode_from_stat(ce, st.st_mode)); if (patch->is_new < 0) patch->is_new = 0; @@ -2232,15 +2306,26 @@ static void patch_stats(struct patch *patch) } } -static void remove_file(struct patch *patch) +static void remove_file(struct patch *patch, int rmdir_empty) { - if (write_index) { + if (update_index) { if (remove_file_from_cache(patch->old_name) < 0) die("unable to remove %s from index", patch->old_name); cache_tree_invalidate_path(active_cache_tree, patch->old_name); } - if (!cached) - unlink(patch->old_name); + if (!cached) { + 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); + } + } } static void add_index_file(const char *path, unsigned mode, void *buf, unsigned long size) @@ -2250,7 +2335,7 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned int namelen = strlen(path); unsigned ce_size = cache_entry_size(namelen); - if (!write_index) + if (!update_index) return; ce = xcalloc(1, ce_size); @@ -2271,15 +2356,22 @@ 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; + char *nbuf; - if (S_ISLNK(mode)) + if (has_symlinks && S_ISLNK(mode)) /* Although buf:size is counted string, it also is NUL * terminated. */ return symlink(buf, path); + fd = open(path, O_CREAT | O_EXCL | O_WRONLY, (mode & 0100) ? 0777 : 0666); if (fd < 0) return -1; + + nbuf = convert_to_working_tree(path, buf, &size); + if (nbuf) + buf = nbuf; + while (size) { int written = xwrite(fd, buf, size); if (written < 0) @@ -2291,6 +2383,8 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf, } if (close(fd) < 0) die("closing file %s: %s", path, strerror(errno)); + if (nbuf) + free(nbuf); return 0; } @@ -2318,8 +2412,7 @@ static void create_one_file(char *path, unsigned mode, const char *buf, unsigned * used to be. */ struct stat st; - errno = 0; - if (!lstat(path, &st) && S_ISDIR(st.st_mode) && !rmdir(path)) + if (!lstat(path, &st) && (!S_ISDIR(st.st_mode) || !rmdir(path))) errno = EEXIST; } @@ -2362,7 +2455,7 @@ static void write_out_one_result(struct patch *patch, int phase) { if (patch->is_delete > 0) { if (phase == 0) - remove_file(patch); + remove_file(patch, 1); return; } if (patch->is_new > 0 || patch->is_copy) { @@ -2375,7 +2468,7 @@ static void write_out_one_result(struct patch *patch, int phase) * thing: remove the old, write the new */ if (phase == 0) - remove_file(patch); + remove_file(patch, 0); if (phase == 1) create_file(patch); } @@ -2497,6 +2590,32 @@ static int use_patch(struct patch *p) return 1; } +static void prefix_one(char **name) +{ + char *old_name = *name; + if (!old_name) + return; + *name = xstrdup(prefix_filename(prefix, prefix_length, *name)); + free(old_name); +} + +static void prefix_patches(struct patch *p) +{ + if (!prefix || p->is_toplevel_relative) + return; + for ( ; p; p = p->next) { + if (p->new_name == p->old_name) { + char *prefixed = p->new_name; + prefix_one(&prefixed); + p->new_name = p->old_name = prefixed; + } + else { + prefix_one(&p->new_name); + prefix_one(&p->old_name); + } + } +} + static int apply_patch(int fd, const char *filename, int inaccurate_eof) { unsigned long offset, size; @@ -2519,11 +2638,14 @@ static int apply_patch(int fd, const char *filename, int inaccurate_eof) break; if (apply_in_reverse) reverse_patches(patch); + if (prefix) + prefix_patches(patch); if (use_patch(patch)) { patch_stats(patch); *listp = patch; listp = &patch->next; - } else { + } + else { /* perhaps free it a bit better? */ free(patch); skipped_patch++; @@ -2535,10 +2657,10 @@ static int apply_patch(int fd, const char *filename, int inaccurate_eof) if (whitespace_error && (new_whitespace == error_on_whitespace)) apply = 0; - write_index = check_index && apply; - if (write_index && newfd < 0) - newfd = hold_lock_file_for_update(&lock_file, - get_index_file(), 1); + update_index = check_index && apply; + if (update_index && newfd < 0) + newfd = hold_locked_index(&lock_file, 1); + if (check_index) { if (read_cache() < 0) die("unable to read index file"); @@ -2578,15 +2700,22 @@ static int git_apply_config(const char *var, const char *value) } -int cmd_apply(int argc, const char **argv, const char *prefix) +int cmd_apply(int argc, const char **argv, const char *unused_prefix) { int i; int read_stdin = 1; int inaccurate_eof = 0; int errs = 0; + int is_not_gitdir = 0; const char *whitespace_option = NULL; + prefix = setup_git_directory_gently(&is_not_gitdir); + prefix_length = prefix ? strlen(prefix) : 0; + git_config(git_apply_config); + if (apply_default_whitespace) + parse_whitespace_option(apply_default_whitespace); + for (i = 1; i < argc; i++) { const char *arg = argv[i]; char *end; @@ -2597,15 +2726,16 @@ int cmd_apply(int argc, const char **argv, const char *prefix) read_stdin = 0; continue; } - if (!strncmp(arg, "--exclude=", 10)) { + if (!prefixcmp(arg, "--exclude=")) { struct excludes *x = xmalloc(sizeof(*x)); x->path = arg + 10; x->next = excludes; excludes = x; continue; } - if (!strncmp(arg, "-p", 2)) { + if (!prefixcmp(arg, "-p")) { p_value = atoi(arg + 2); + p_value_known = 1; continue; } if (!strcmp(arg, "--no-add")) { @@ -2637,10 +2767,14 @@ int cmd_apply(int argc, const char **argv, const char *prefix) continue; } if (!strcmp(arg, "--index")) { + if (is_not_gitdir) + die("--index outside a repository"); check_index = 1; continue; } if (!strcmp(arg, "--cached")) { + if (is_not_gitdir) + die("--cached outside a repository"); check_index = 1; cached = 1; continue; @@ -2658,13 +2792,13 @@ int cmd_apply(int argc, const char **argv, const char *prefix) line_termination = 0; continue; } - if (!strncmp(arg, "-C", 2)) { + if (!prefixcmp(arg, "-C")) { p_context = strtoul(arg + 2, &end, 0); if (*end != '\0') die("unrecognized context count '%s'", arg + 2); continue; } - if (!strncmp(arg, "--whitespace=", 13)) { + if (!prefixcmp(arg, "--whitespace=")) { whitespace_option = arg + 13; parse_whitespace_option(arg + 13); continue; @@ -2681,7 +2815,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) apply = apply_with_reject = apply_verbosely = 1; continue; } - if (!strcmp(arg, "--verbose")) { + if (!strcmp(arg, "-v") || !strcmp(arg, "--verbose")) { apply_verbosely = 1; continue; } @@ -2689,14 +2823,6 @@ int cmd_apply(int argc, const char **argv, const char *prefix) inaccurate_eof = 1; continue; } - - if (check_index && prefix_length < 0) { - prefix = setup_git_directory(); - prefix_length = prefix ? strlen(prefix) : 0; - git_config(git_apply_config); - if (!whitespace_option && apply_default_whitespace) - parse_whitespace_option(apply_default_whitespace); - } if (0 < prefix_length) arg = prefix_filename(prefix, prefix_length, arg); @@ -2739,9 +2865,9 @@ int cmd_apply(int argc, const char **argv, const char *prefix) whitespace_error == 1 ? "s" : ""); } - if (write_index) { + if (update_index) { if (write_cache(newfd, active_cache, active_nr) || - close(newfd) || commit_lock_file(&lock_file)) + close(newfd) || commit_locked_index(&lock_file)) die("Unable to write new index file"); }