X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=apply.c;h=1083d4f31677357fe86c5ab85960df44a28ce721;hb=5b5d4d9e1b31997b3179e6a253d47b7eea03d0fa;hp=590adc6afafa98e389dcafc2f8c432db09412d4e;hpb=9add69b1b1f76db874ed899c46fbe0b252aa3c23;p=git.git diff --git a/apply.c b/apply.c index 590adc6a..1083d4f3 100644 --- a/apply.c +++ b/apply.c @@ -16,6 +16,11 @@ // --numstat does numeric diffstat, and doesn't actually apply // --index-info shows the old and new index info for paths if available. // +static const char *prefix; +static int prefix_length = -1; + +static int p_value = 1; +static int allow_binary_replacement = 0; static int check_index = 0; static int write_index = 0; static int diffstat = 0; @@ -27,7 +32,7 @@ static int no_add = 0; static int show_index_info = 0; static int line_termination = '\n'; static const char apply_usage[] = -"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--apply] [--no-add] [--index-info] [-z] ..."; +"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [-z] [-pNUM] ..."; /* * For "diff-stat" like behaviour, we keep track of the biggest change @@ -80,14 +85,11 @@ static void *read_patch_file(int fd, unsigned long *sizep) buffer = xrealloc(buffer, alloc); nr = alloc - size; } - nr = read(fd, buffer + size, nr); + nr = xread(fd, buffer + size, nr); if (!nr) break; - if (nr < 0) { - if (errno == EAGAIN) - continue; + if (nr < 0) die("git-apply: read returned %s", strerror(errno)); - } size += nr; } *sizep = size; @@ -216,7 +218,6 @@ static char * find_name(const char *line, char *def, int p_value, int terminate) */ static void parse_traditional_patch(const char *first, const char *second, struct patch *patch) { - int p_value = 1; char *name; first += 4; // skip "--- " @@ -891,17 +892,34 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch) patchsize = parse_single_patch(buffer + offset + hdrsize, size - offset - hdrsize, patch); - if (!patchsize && !metadata_changes(patch)) { - static const char binhdr[] = "Binary files "; - - if (sizeof(binhdr) - 1 < size - offset - hdrsize && - !memcmp(binhdr, buffer + hdrsize + offset, - sizeof(binhdr)-1)) - patch->is_binary = 1; + if (!patchsize) { + static const char *binhdr[] = { + "Binary files ", + "Files ", + NULL, + }; + int i; + int hd = hdrsize + offset; + unsigned long llen = linelen(buffer + hd, size - hd); + + if (!memcmp(" differ\n", buffer + hd + llen - 8, 8)) + for (i = 0; binhdr[i]; i++) { + int len = strlen(binhdr[i]); + if (len < size - hd && + !memcmp(binhdr[i], buffer + hd, len)) { + patch->is_binary = 1; + break; + } + } - if (patch->is_binary && !apply && !check) - ; - else + /* Empty patch cannot be applied if: + * - it is a binary patch and we do not do binary_replace, or + * - text patch without metadata change + */ + if ((apply || check) && + (patch->is_binary + ? !allow_binary_replacement + : !metadata_changes(patch))) die("patch with only garbage at line %d", linenr); } @@ -985,13 +1003,8 @@ static int read_old_data(struct stat *st, const char *path, void *buf, unsigned return error("unable to open %s", path); got = 0; for (;;) { - int ret = read(fd, buf + got, size - got); - if (ret < 0) { - if (errno == EAGAIN) - continue; - break; - } - if (!ret) + int ret = xread(fd, buf + got, size - got); + if (ret <= 0) break; got += ret; } @@ -1129,6 +1142,14 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag) size -= len; } +#ifdef NO_ACCURATE_DIFF + if (oldsize > 0 && old[oldsize - 1] == '\n' && + newsize > 0 && new[newsize - 1] == '\n') { + oldsize--; + newsize--; + } +#endif + offset = find_offset(buf, desc->size, old, oldsize, frag->newpos); if (offset >= 0) { int diff = newsize - oldsize; @@ -1155,10 +1176,77 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag) static int apply_fragments(struct buffer_desc *desc, struct patch *patch) { struct fragment *frag = patch->fragments; + const char *name = patch->old_name ? patch->old_name : patch->new_name; + + if (patch->is_binary) { + unsigned char sha1[20]; + + if (!allow_binary_replacement) + return error("cannot apply binary patch to '%s' " + "without --allow-binary-replacement", + name); + + /* For safety, we require patch index line to contain + * full 40-byte textual SHA1 for old and new, at least for now. + */ + if (strlen(patch->old_sha1_prefix) != 40 || + strlen(patch->new_sha1_prefix) != 40 || + get_sha1_hex(patch->old_sha1_prefix, sha1) || + get_sha1_hex(patch->new_sha1_prefix, sha1)) + return error("cannot apply binary patch to '%s' " + "without full index line", name); + + if (patch->old_name) { + unsigned char hdr[50]; + int hdrlen; + + /* See if the old one matches what the patch + * applies to. + */ + write_sha1_file_prepare(desc->buffer, desc->size, + "blob", sha1, hdr, &hdrlen); + if (strcmp(sha1_to_hex(sha1), patch->old_sha1_prefix)) + return error("the patch applies to '%s' (%s), " + "which does not match the " + "current contents.", + name, sha1_to_hex(sha1)); + } + else { + /* Otherwise, the old one must be empty. */ + if (desc->size) + return error("the patch applies to an empty " + "'%s' but it is not empty", name); + } + + /* For now, we do not record post-image data in the patch, + * and require the object already present in the recipient's + * object database. + */ + if (desc->buffer) { + free(desc->buffer); + desc->alloc = desc->size = 0; + } + get_sha1_hex(patch->new_sha1_prefix, sha1); + + if (memcmp(sha1, null_sha1, 20)) { + char type[10]; + unsigned long size; + + desc->buffer = read_sha1_file(sha1, type, &size); + if (!desc->buffer) + return error("the necessary postimage %s for " + "'%s' does not exist", + patch->new_sha1_prefix, name); + desc->alloc = desc->size = size; + } + + return 0; + } while (frag) { if (apply_one_fragment(desc, frag) < 0) - return error("patch failed: %s:%ld", patch->old_name, frag->oldpos); + return error("patch failed: %s:%ld", + name, frag->oldpos); frag = frag->next; } return 0; @@ -1200,6 +1288,7 @@ static int check_patch(struct patch *patch) struct stat st; const char *old_name = patch->old_name; const char *new_name = patch->new_name; + const char *name = old_name ? old_name : new_name; if (old_name) { int changed; @@ -1274,7 +1363,7 @@ static int check_patch(struct patch *patch) } if (apply_data(patch, &st) < 0) - return error("%s: patch does not apply", old_name); + return error("%s: patch does not apply", name); return 0; } @@ -1483,40 +1572,19 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned die("unable to add cache entry for %s", path); } -static void create_subdirectories(const char *path) -{ - int len = strlen(path); - char *buf = xmalloc(len + 1); - const char *slash = path; - - while ((slash = strchr(slash+1, '/')) != NULL) { - len = slash - path; - memcpy(buf, path, len); - buf[len] = 0; - if (mkdir(buf, 0777) < 0) { - if (errno != EEXIST) - break; - } - } - free(buf); -} - static int try_create_file(const char *path, unsigned int mode, const char *buf, unsigned long size) { int fd; if (S_ISLNK(mode)) return symlink(buf, path); - fd = open(path, O_CREAT | O_EXCL | O_WRONLY | O_TRUNC, (mode & 0100) ? 0777 : 0666); + fd = open(path, O_CREAT | O_EXCL | O_WRONLY, (mode & 0100) ? 0777 : 0666); if (fd < 0) return -1; while (size) { - int written = write(fd, buf, size); - if (written < 0) { - if (errno == EINTR || errno == EAGAIN) - continue; + int written = xwrite(fd, buf, size); + if (written < 0) die("writing file %s: %s", path, strerror(errno)); - } if (!written) die("out of space writing file %s", path); buf += written; @@ -1532,13 +1600,14 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf, * which is true 99% of the time anyway. If they don't, * we create them and try again. */ -static void create_one_file(const char *path, unsigned mode, const char *buf, unsigned long size) +static void create_one_file(char *path, unsigned mode, const char *buf, unsigned long size) { if (!try_create_file(path, mode, buf, size)) return; if (errno == ENOENT) { - create_subdirectories(path); + if (safe_create_leading_directories(path)) + return; if (!try_create_file(path, mode, buf, size)) return; } @@ -1557,14 +1626,15 @@ static void create_one_file(const char *path, unsigned mode, const char *buf, un } if (errno != EEXIST) break; - } + ++nr; + } } die("unable to write file %s mode %o", path, mode); } static void create_file(struct patch *patch) { - const char *path = patch->new_name; + char *path = patch->new_name; unsigned mode = patch->new_mode; unsigned long size = patch->resultsize; char *buf = patch->result; @@ -1620,6 +1690,12 @@ static int use_patch(struct patch *p) return 0; x = x->next; } + if (0 < prefix_length) { + int pathlen = strlen(pathname); + if (pathlen <= prefix_length || + memcmp(prefix, pathname, prefix_length)) + return 0; + } return 1; } @@ -1714,6 +1790,10 @@ int main(int argc, char **argv) excludes = x; continue; } + if (!strncmp(arg, "-p", 2)) { + p_value = atoi(arg + 2); + continue; + } if (!strcmp(arg, "--no-add")) { no_add = 1; continue; @@ -1723,6 +1803,10 @@ int main(int argc, char **argv) diffstat = 1; continue; } + if (!strcmp(arg, "--allow-binary-replacement")) { + allow_binary_replacement = 1; + continue; + } if (!strcmp(arg, "--numstat")) { apply = 0; numstat = 1; @@ -1755,6 +1839,15 @@ int main(int argc, char **argv) line_termination = 0; continue; } + + if (check_index && prefix_length < 0) { + prefix = setup_git_directory(); + prefix_length = prefix ? strlen(prefix) : 0; + git_config(git_default_config); + } + if (0 < prefix_length) + arg = prefix_filename(prefix, prefix_length, arg); + fd = open(arg, O_RDONLY); if (fd < 0) usage(apply_usage);