summaryrefslogtreecommitdiff
path: root/builtin-apply.c
diff options
context:
space:
mode:
Diffstat (limited to 'builtin-apply.c')
-rw-r--r--builtin-apply.c87
1 files changed, 74 insertions, 13 deletions
diff --git a/builtin-apply.c b/builtin-apply.c
index 64cf8af8f6..9fcfe3955d 100644
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -12,6 +12,7 @@
#include "blob.h"
#include "delta.h"
#include "builtin.h"
+#include "path-list.h"
/*
* --check turns on checking that the working tree matches the
@@ -186,6 +187,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 path_list fn_table;
+
static uint32_t hash_line(const char *cp, size_t len)
{
size_t i;
@@ -1030,8 +1038,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;
@@ -1042,7 +1049,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);
}
}
@@ -2229,15 +2236,62 @@ 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 path_list_item *item;
+
+ if (name == NULL)
+ return NULL;
+
+ item = path_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 path_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 = path_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 = path_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 image image;
size_t len;
char *img;
+ struct patch *tpatch;
strbuf_init(&buf, 0);
- if (cached) {
+
+ if ((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) {
@@ -2264,6 +2318,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)
@@ -2308,6 +2363,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;
int stat_ret = 0;
unsigned st_mode = 0;
@@ -2321,12 +2377,17 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
return 0;
assert(patch->is_new <= 0);
- if (!cached) {
+ if ((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)
@@ -2378,7 +2439,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;
@@ -2395,8 +2456,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
@@ -2446,15 +2506,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;
}
@@ -2975,6 +3034,8 @@ static int apply_patch(int fd, const char *filename, int options)
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);
patch_input_file = filename;
read_patch_file(&buf, fd);