// --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 allow_binary_replacement = 0;
static int check_index = 0;
static int write_index = 0;
static int diffstat = 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] <patch>...";
+"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [-z] <patch>...";
/*
* For "diff-stat" like behaviour, we keep track of the biggest change
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;
int len;
ptr = strchr(line, '.');
- if (!ptr || ptr[1] != '.' || 40 <= ptr - line)
+ if (!ptr || ptr[1] != '.' || 40 < ptr - line)
return 0;
len = ptr - line;
memcpy(patch->old_sha1_prefix, line, len);
ptr = eol;
len = ptr - line;
- if (40 <= len)
+ if (40 < len)
return 0;
memcpy(patch->new_sha1_prefix, line, len);
patch->new_sha1_prefix[len] = 0;
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, 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);
}
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;
}
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;
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;
}
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;
}
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;
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;
}
diffstat = 1;
continue;
}
+ if (!strcmp(arg, "--allow-binary-replacement")) {
+ allow_binary_replacement = 1;
+ continue;
+ }
if (!strcmp(arg, "--numstat")) {
apply = 0;
numstat = 1;
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);