From 15793646acba291dc2709cb82d3f72cd5be8c6c0 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 16 May 2012 15:31:18 -0700 Subject: apply: fix an incomplete comment in check_patch() This check is not only about type-change (for which it would be sufficient to check only was_deleted()) but is also about a swap rename. Otherwise to_be_deleted() check is not justified. Signed-off-by: Junio C Hamano --- builtin/apply.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) (limited to 'builtin') diff --git a/builtin/apply.c b/builtin/apply.c index 725712d78..44f6de903 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -3218,16 +3218,22 @@ static int check_patch(struct patch *patch) return status; old_name = patch->old_name; + /* + * A type-change diff is always split into a patch to delete + * old, immediately followed by a patch to create new (see + * diff.c::run_diff()); in such a case it is Ok that the entry + * to be deleted by the previous patch is still in the working + * tree and in the index. + * + * A patch to swap-rename between A and B would first rename A + * to B and then rename B to A. While applying the first one, + * the presense of B should not stop A from getting renamed to + * B; ask to_be_deleted() about the later rename. Removal of + * B and rename from A to B is handled the same way by asking + * was_deleted(). + */ if ((tpatch = in_fn_table(new_name)) && - (was_deleted(tpatch) || to_be_deleted(tpatch))) - /* - * A type-change diff is always split into a patch to - * delete old, immediately followed by a patch to - * create new (see diff.c::run_diff()); in such a case - * it is Ok that the entry to be deleted by the - * previous patch is still in the working tree and in - * the index. - */ + (was_deleted(tpatch) || to_be_deleted(tpatch))) ok_if_exists = 1; else ok_if_exists = 0; -- cgit v1.2.1 From f3b8f91a6919c8ab806016791c4a4bfd63c5169c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 16 May 2012 13:21:39 -0700 Subject: apply: a bit more comments on PATH_TO_BE_DELETED The code is littered with to_be_deleted() whose purpose is not so clear. Describe where it matters. Also remove an extra space before "#define" that snuck in by mistake at 7fac0ee (builtin-apply: keep information about files to be deleted, 2009-04-11). Signed-off-by: Junio C Hamano --- builtin/apply.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'builtin') diff --git a/builtin/apply.c b/builtin/apply.c index 44f6de903..35460c940 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -2970,9 +2970,15 @@ static struct patch *in_fn_table(const char *name) * item->util in the filename table records the status of the path. * Usually it points at a patch (whose result records the contents * of it after applying it), but it could be PATH_WAS_DELETED for a - * path that a previously applied patch has already removed. + * path that a previously applied patch has already removed, or + * PATH_TO_BE_DELETED for a path that a later patch would remove. + * + * The latter is needed to deal with a case where two paths A and B + * are swapped by first renaming A to B and then renaming B to A; + * moving A to B should not be prevented due to presense of B as we + * will remove it in a later patch. */ - #define PATH_TO_BE_DELETED ((struct patch *) -2) +#define PATH_TO_BE_DELETED ((struct patch *) -2) #define PATH_WAS_DELETED ((struct patch *) -1) static int to_be_deleted(struct patch *patch) -- cgit v1.2.1 From 798b9ce87bfcd18896f9b0e21918b7bf31109b32 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 8 May 2012 14:38:06 -0700 Subject: apply: clear_image() clears things a bit more The clear_image() function did not clear the line table in the image structure; this does not matter for the current callers, as the function is only called from the codepaths that deal with binary patches where the line table is never populated, and the codepaths that do populate the line table free it themselves. But it will start to matter when we introduce a codepath to retry a failed patch, so make sure it clears and frees everything. Signed-off-by: Junio C Hamano --- builtin/apply.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'builtin') diff --git a/builtin/apply.c b/builtin/apply.c index 35460c940..09f5df3b9 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -371,8 +371,8 @@ static void prepare_image(struct image *image, char *buf, size_t len, static void clear_image(struct image *image) { free(image->buf); - image->buf = NULL; - image->len = 0; + free(image->line_allocated); + memset(image, 0, sizeof(*image)); } /* fmt must contain _one_ %s and no other substitution */ -- cgit v1.2.1 From e42a96e7726365557b5865957927dc1ca272cad2 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 8 May 2012 15:11:02 -0700 Subject: apply: refactor read_file_or_gitlink() Reading a blob out of the object store does not have to require that the caller has a cache entry for it. Create a read_blob_object() helper function that takes the object name and mode, and use it to reimplement the original function as a thin wrapper to it. Signed-off-by: Junio C Hamano --- builtin/apply.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'builtin') diff --git a/builtin/apply.c b/builtin/apply.c index 09f5df3b9..15bcbb0f2 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -2930,20 +2930,17 @@ static int apply_fragments(struct image *img, struct patch *patch) return 0; } -static int read_file_or_gitlink(struct cache_entry *ce, struct strbuf *buf) +static int read_blob_object(struct strbuf *buf, const unsigned char *sha1, unsigned mode) { - if (!ce) - return 0; - - if (S_ISGITLINK(ce->ce_mode)) { + if (S_ISGITLINK(mode)) { strbuf_grow(buf, 100); - strbuf_addf(buf, "Subproject commit %s\n", sha1_to_hex(ce->sha1)); + strbuf_addf(buf, "Subproject commit %s\n", sha1_to_hex(sha1)); } else { enum object_type type; unsigned long sz; char *result; - result = read_sha1_file(ce->sha1, &type, &sz); + result = read_sha1_file(sha1, &type, &sz); if (!result) return -1; /* XXX read_sha1_file NUL-terminates */ @@ -2952,6 +2949,13 @@ static int read_file_or_gitlink(struct cache_entry *ce, struct strbuf *buf) return 0; } +static int read_file_or_gitlink(struct cache_entry *ce, struct strbuf *buf) +{ + if (!ce) + return 0; + return read_blob_object(buf, ce->sha1, ce->ce_mode); +} + static struct patch *in_fn_table(const char *name) { struct string_list_item *item; -- cgit v1.2.1 From f4c66eeddd37d3e5f4043df89c2679fbadeb57bd Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 12 Jun 2012 22:47:12 -0700 Subject: apply: factor out checkout_target() helper function When a patch wants to touch a path, if the path exists in the index but is missing in the working tree, "git apply --index" checks out the file to the working tree from the index automatically and then applies the patch. Split this logic out to a separate helper function. Signed-off-by: Junio C Hamano --- builtin/apply.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) (limited to 'builtin') diff --git a/builtin/apply.c b/builtin/apply.c index 15bcbb0f2..487e4034a 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -3034,6 +3034,18 @@ static void prepare_fn_table(struct patch *patch) } } +static int checkout_target(struct cache_entry *ce, struct stat *st) +{ + struct checkout costate; + + memset(&costate, 0, sizeof(costate)); + costate.base_dir = ""; + costate.refresh_cache = 1; + if (checkout_entry(ce, &costate, NULL) || lstat(ce->name, st)) + return error(_("cannot checkout %s"), ce->name); + return 0; +} + static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce) { struct strbuf buf = STRBUF_INIT; @@ -3163,13 +3175,7 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s } *ce = active_cache[pos]; if (stat_ret < 0) { - struct checkout costate; - /* checkout */ - memset(&costate, 0, sizeof(costate)); - costate.base_dir = ""; - costate.refresh_cache = 1; - if (checkout_entry(*ce, &costate, NULL) || - lstat(old_name, st)) + if (checkout_target(*ce, st)) return -1; } if (!cached && verify_index_match(*ce, st)) -- cgit v1.2.1 From 37b9c903eb825ca741ea0eddc942309f892b682f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 8 May 2012 13:35:21 -0700 Subject: apply: split load_preimage() helper function out Given a patch for a single path, the function apply_data() reads the preimage in core, and applies the change represented in the patch. Separate out the first part that reads the preimage into a separate helper function load_preimage(). Signed-off-by: Junio C Hamano --- builtin/apply.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'builtin') diff --git a/builtin/apply.c b/builtin/apply.c index 487e4034a..4d2546f5e 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -3046,10 +3046,10 @@ static int checkout_target(struct cache_entry *ce, struct stat *st) return 0; } -static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce) +static int load_preimage(struct image *image, + struct patch *patch, struct stat *st, struct cache_entry *ce) { struct strbuf buf = STRBUF_INIT; - struct image image; size_t len; char *img; struct patch *tpatch; @@ -3086,7 +3086,16 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry * } img = strbuf_detach(&buf, &len); - prepare_image(&image, img, len, !patch->is_binary); + prepare_image(image, img, len, !patch->is_binary); + return 0; +} + +static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce) +{ + struct image image; + + if (load_preimage(&image, patch, st, ce) < 0) + return -1; if (apply_fragments(&image, patch) < 0) return -1; /* note with --reject this succeeds. */ -- cgit v1.2.1 From ccf998b297c6d04053eaa9d61e9cb4187bccd0a9 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 16 May 2012 14:03:52 -0700 Subject: apply: refactor "previous patch" logic The code to grab the result of application of a previous patch in the input was mixed with error message generation for a case where a later patch tries to modify contents of a path that has been removed. The same code is duplicated elsewhere in the code. Introduce a helper to clarify what is going on. Signed-off-by: Junio C Hamano --- builtin/apply.c | 82 +++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 56 insertions(+), 26 deletions(-) (limited to 'builtin') diff --git a/builtin/apply.c b/builtin/apply.c index 4d2546f5e..564243340 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -3046,22 +3046,50 @@ static int checkout_target(struct cache_entry *ce, struct stat *st) return 0; } +static struct patch *previous_patch(struct patch *patch, int *gone) +{ + struct patch *previous; + + *gone = 0; + if (patch->is_copy || patch->is_rename) + return NULL; /* "git" patches do not depend on the order */ + + previous = in_fn_table(patch->old_name); + if (!previous) + return NULL; + + if (to_be_deleted(previous)) + return NULL; /* the deletion hasn't happened yet */ + + if (was_deleted(previous)) + *gone = 1; + + return previous; +} + +/* + * We are about to apply "patch"; populate the "image" with the + * current version we have, from the working tree or from the index, + * depending on the situation e.g. --cached/--index. If we are + * applying a non-git patch that incrementally updates the tree, + * we read from the result of a previous diff. + */ static int load_preimage(struct image *image, struct patch *patch, struct stat *st, struct cache_entry *ce) { struct strbuf buf = STRBUF_INIT; size_t len; char *img; - struct patch *tpatch; + struct patch *previous; + int status; - if (!(patch->is_copy || patch->is_rename) && - (tpatch = in_fn_table(patch->old_name)) != NULL && !to_be_deleted(tpatch)) { - if (was_deleted(tpatch)) { - return error(_("patch %s has been renamed/deleted"), - patch->old_name); - } + previous = previous_patch(patch, &status); + if (status) + return error(_("path %s has been renamed/deleted"), + patch->old_name); + if (previous) { /* We have a patched copy in memory; use that. */ - strbuf_add(&buf, tpatch->result, tpatch->resultsize); + strbuf_add(&buf, previous->result, previous->resultsize); } else if (cached) { if (read_file_or_gitlink(ce, &buf)) return error(_("read of %s failed"), patch->old_name); @@ -3143,39 +3171,41 @@ static int verify_index_match(struct cache_entry *ce, struct stat *st) return ce_match_stat(ce, st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE); } +/* + * If "patch" that we are looking at modifies or deletes what we have, + * we would want it not to lose any local modification we have, either + * in the working tree or in the index. + * + * This also decides if a non-git patch is a creation patch or a + * modification to an existing empty file. We do not check the state + * of the current tree for a creation patch in this function; the caller + * check_patch() separately makes sure (and errors out otherwise) that + * the path the patch creates does not exist in the current tree. + */ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct stat *st) { const char *old_name = patch->old_name; - struct patch *tpatch = NULL; - int stat_ret = 0; + struct patch *previous = NULL; + int stat_ret = 0, status; unsigned st_mode = 0; - /* - * Make sure that we do not have local modifications from the - * index when we are looking at the index. Also make sure - * we have the preimage file to be patched in the work tree, - * unless --cached, which tells git to apply only in the index. - */ if (!old_name) return 0; assert(patch->is_new <= 0); + previous = previous_patch(patch, &status); - if (!(patch->is_copy || patch->is_rename) && - (tpatch = in_fn_table(old_name)) != NULL && !to_be_deleted(tpatch)) { - if (was_deleted(tpatch)) - return error(_("%s: has been deleted/renamed"), old_name); - st_mode = tpatch->new_mode; + if (status) + return error(_("path %s has been renamed/deleted"), old_name); + if (previous) { + st_mode = previous->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 (to_be_deleted(tpatch)) - tpatch = NULL; - - if (check_index && !tpatch) { + if (check_index && !previous) { int pos = cache_name_pos(old_name, strlen(old_name)); if (pos < 0) { if (patch->is_new < 0) @@ -3197,7 +3227,7 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s return error(_("%s: %s"), old_name, strerror(errno)); } - if (!cached && !tpatch) + if (!cached && !previous) st_mode = ce_mode_from_stat(*ce, st->st_mode); if (patch->is_new < 0) -- cgit v1.2.1 From 5a812661694efed28b4446724278bcc21fe6d6d6 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 12 Jun 2012 15:23:54 -0700 Subject: apply: further split load_preimage() load_preimage() is very specific to grab the current contents for the path given by patch->old_name. Split the logic that grabs the contents for a path out of it into a separate load_patch_target() function. Signed-off-by: Junio C Hamano --- builtin/apply.c | 59 ++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 19 deletions(-) (limited to 'builtin') diff --git a/builtin/apply.c b/builtin/apply.c index 564243340..ee1a96089 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -3067,6 +3067,31 @@ static struct patch *previous_patch(struct patch *patch, int *gone) return previous; } +#define SUBMODULE_PATCH_WITHOUT_INDEX 1 + +static int load_patch_target(struct strbuf *buf, + struct cache_entry *ce, + struct stat *st, + const char *name, + unsigned expected_mode) +{ + if (cached) { + if (read_file_or_gitlink(ce, buf)) + return error(_("read of %s failed"), name); + } else if (name) { + if (S_ISGITLINK(expected_mode)) { + if (ce) + return read_file_or_gitlink(ce, buf); + else + return SUBMODULE_PATCH_WITHOUT_INDEX; + } else { + if (read_old_data(st, name, buf)) + return error(_("read of %s failed"), name); + } + } + return 0; +} + /* * We are about to apply "patch"; populate the "image" with the * current version we have, from the working tree or from the index, @@ -3090,26 +3115,22 @@ static int load_preimage(struct image *image, if (previous) { /* We have a patched copy in memory; use that. */ strbuf_add(&buf, previous->result, previous->resultsize); - } else if (cached) { - if (read_file_or_gitlink(ce, &buf)) + } else { + status = load_patch_target(&buf, ce, st, + patch->old_name, patch->old_mode); + if (status < 0) + return status; + else if (status == SUBMODULE_PATCH_WITHOUT_INDEX) { + /* + * There is no way to apply subproject + * patch without looking at the index. + * NEEDSWORK: shouldn't this be flagged + * as an error??? + */ + free_fragment_list(patch->fragments); + patch->fragments = NULL; + } else if (status) { return error(_("read of %s failed"), patch->old_name); - } else if (patch->old_name) { - if (S_ISGITLINK(patch->old_mode)) { - if (ce) { - read_file_or_gitlink(ce, &buf); - } else { - /* - * There is no way to apply subproject - * patch without looking at the index. - * NEEDSWORK: shouldn't this be flagged - * as an error??? - */ - free_fragment_list(patch->fragments); - patch->fragments = NULL; - } - } else { - if (read_old_data(st, patch->old_name, &buf)) - return error(_("read of %s failed"), patch->old_name); } } -- cgit v1.2.1 From 813ebf82215ce3e07631ea7ab12b30c609adcccd Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 7 Jun 2012 14:06:47 -0700 Subject: apply: move check_to_create_blob() closer to its sole caller Signed-off-by: Junio C Hamano --- builtin/apply.c | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) (limited to 'builtin') diff --git a/builtin/apply.c b/builtin/apply.c index ee1a96089..cd25e6309 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -3159,29 +3159,6 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry * return 0; } -static int check_to_create_blob(const char *new_name, int ok_if_exists) -{ - struct stat nst; - if (!lstat(new_name, &nst)) { - if (S_ISDIR(nst.st_mode) || ok_if_exists) - return 0; - /* - * A leading component of new_name might be a symlink - * that is going to be removed with this patch, but - * still pointing at somewhere that has the path. - * In such a case, path "new_name" does not exist as - * far as git is concerned. - */ - if (has_symlink_leading_path(new_name, strlen(new_name))) - return 0; - - return error(_("%s: already exists in working directory"), new_name); - } - else if ((errno != ENOENT) && (errno != ENOTDIR)) - return error("%s: %s", new_name, strerror(errno)); - return 0; -} - static int verify_index_match(struct cache_entry *ce, struct stat *st) { if (S_ISGITLINK(ce->ce_mode)) { @@ -3272,6 +3249,29 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s return 0; } +static int check_to_create_blob(const char *new_name, int ok_if_exists) +{ + struct stat nst; + if (!lstat(new_name, &nst)) { + if (S_ISDIR(nst.st_mode) || ok_if_exists) + return 0; + /* + * A leading component of new_name might be a symlink + * that is going to be removed with this patch, but + * still pointing at somewhere that has the path. + * In such a case, path "new_name" does not exist as + * far as git is concerned. + */ + if (has_symlink_leading_path(new_name, strlen(new_name))) + return 0; + + return error(_("%s: already exists in working directory"), new_name); + } + else if ((errno != ENOENT) && (errno != ENOTDIR)) + return error("%s: %s", new_name, strerror(errno)); + return 0; +} + /* * Check and apply the patch in-core; leave the result in patch->result * for the caller to write it out to the final destination. -- cgit v1.2.1 From ec15be02679ed5a98e4a96b23b99dd1b90196fae Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 7 Jun 2012 14:10:19 -0700 Subject: apply: move "already exists" logic to check_to_create() The check_to_create_blob() function used to check only the case where we are applying to the working tree. Rename the function to check_to_create() and make it also responsible for checking the case where we apply to the index. Also make its caller responsible for issuing an error message. Signed-off-by: Junio C Hamano --- builtin/apply.c | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) (limited to 'builtin') diff --git a/builtin/apply.c b/builtin/apply.c index cd25e6309..7379687f1 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -3249,9 +3249,21 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s return 0; } -static int check_to_create_blob(const char *new_name, int ok_if_exists) + +#define EXISTS_IN_INDEX 1 +#define EXISTS_IN_WORKTREE 2 + +static int check_to_create(const char *new_name, int ok_if_exists) { struct stat nst; + + if (check_index && + cache_name_pos(new_name, strlen(new_name)) >= 0 && + !ok_if_exists) + return EXISTS_IN_INDEX; + if (cached) + return 0; + if (!lstat(new_name, &nst)) { if (S_ISDIR(nst.st_mode) || ok_if_exists) return 0; @@ -3265,10 +3277,10 @@ static int check_to_create_blob(const char *new_name, int ok_if_exists) if (has_symlink_leading_path(new_name, strlen(new_name))) return 0; - return error(_("%s: already exists in working directory"), new_name); - } - else if ((errno != ENOENT) && (errno != ENOTDIR)) + return EXISTS_IN_WORKTREE; + } else if ((errno != ENOENT) && (errno != ENOTDIR)) { return error("%s: %s", new_name, strerror(errno)); + } return 0; } @@ -3316,15 +3328,21 @@ static int check_patch(struct patch *patch) if (new_name && ((0 < patch->is_new) | (0 < patch->is_rename) | patch->is_copy)) { - if (check_index && - cache_name_pos(new_name, strlen(new_name)) >= 0 && - !ok_if_exists) + int err = check_to_create(new_name, ok_if_exists); + + switch (err) { + case 0: + break; /* happy */ + case EXISTS_IN_INDEX: return error(_("%s: already exists in index"), new_name); - if (!cached) { - int err = check_to_create_blob(new_name, ok_if_exists); - if (err) - return err; + break; + case EXISTS_IN_WORKTREE: + return error(_("%s: already exists in working directory"), + new_name); + default: + return err; } + if (!patch->new_mode) { if (0 < patch->is_new) patch->new_mode = S_IFREG | 0644; -- cgit v1.2.1 From cfb6f9acc392561bb339763564e21b684e50ce83 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 8 May 2012 13:21:53 -0700 Subject: apply: accept -3/--3way command line option Begin teaching the three-way merge fallback logic "git am -3" uses to the underlying "git apply". It only implements the command line parsing part, and does not do anything interesting yet, other than making sure that "--reject" and "--3way" are not given together, and making "--3way" imply "--index". Signed-off-by: Junio C Hamano --- builtin/apply.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) (limited to 'builtin') diff --git a/builtin/apply.c b/builtin/apply.c index 7379687f1..51b695bac 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -46,6 +46,7 @@ static int apply_with_reject; static int apply_verbosely; static int allow_overlap; static int no_add; +static int threeway; static const char *fake_ancestor; static int line_termination = '\n'; static unsigned int p_context = UINT_MAX; @@ -3139,6 +3140,12 @@ static int load_preimage(struct image *image, return 0; } +static int try_threeway(struct image *image, struct patch *patch, + struct stat *st, struct cache_entry *ce) +{ + return -1; /* for now */ +} + static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce) { struct image image; @@ -3146,8 +3153,11 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry * if (load_preimage(&image, patch, st, ce) < 0) return -1; - if (apply_fragments(&image, patch) < 0) - return -1; /* note with --reject this succeeds. */ + if (apply_fragments(&image, patch) < 0) { + /* Note: with --reject, apply_fragments() returns 0 */ + if (!threeway || try_threeway(&image, patch, st, ce) < 0) + return -1; + } patch->result = image.buf; patch->resultsize = image.len; add_to_fn_table(patch); @@ -4079,6 +4089,8 @@ int cmd_apply(int argc, const char **argv, const char *prefix_) "apply a patch without touching the working tree"), OPT_BOOLEAN(0, "apply", &force_apply, "also apply the patch (use with --stat/--summary/--check)"), + OPT_BOOL('3', "3way", &threeway, + "attempt three-way merge if a patch does not apply"), OPT_FILENAME(0, "build-fake-ancestor", &fake_ancestor, "build a temporary index based on embedded index information"), { OPTION_CALLBACK, 'z', NULL, NULL, NULL, @@ -4127,6 +4139,15 @@ int cmd_apply(int argc, const char **argv, const char *prefix_) argc = parse_options(argc, argv, prefix, builtin_apply_options, apply_usage, 0); + if (apply_with_reject && threeway) + die("--reject and --3way cannot be used together."); + if (cached && threeway) + die("--cached and --3way cannot be used together."); + if (threeway) { + if (is_not_gitdir) + die(_("--3way outside a repository")); + check_index = 1; + } if (apply_with_reject) apply = apply_verbosely = 1; if (!force_apply && (diffstat || numstat || summary || check || fake_ancestor)) -- cgit v1.2.1 From 519d1a5b4e044459bd7323cfa6a7cb98cec5c884 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 8 Jun 2012 09:54:10 -0700 Subject: apply: fall back on three-way merge Grab the preimage blob the patch claims to be based on out of the object store, apply the patch, and then call three-way-merge function. This step still does not plug the actual three-way merge logic yet, but we are getting there. Signed-off-by: Junio C Hamano --- builtin/apply.c | 46 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) (limited to 'builtin') diff --git a/builtin/apply.c b/builtin/apply.c index 51b695bac..5a7201fc8 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -3140,10 +3140,54 @@ static int load_preimage(struct image *image, return 0; } +static int three_way_merge(struct image *image, + char *path, + const unsigned char *base, + const unsigned char *ours, + const unsigned char *theirs) +{ + return -1; /* for now */ +} + static int try_threeway(struct image *image, struct patch *patch, struct stat *st, struct cache_entry *ce) { - return -1; /* for now */ + unsigned char pre_sha1[20], post_sha1[20], our_sha1[20]; + struct strbuf buf = STRBUF_INIT; + size_t len; + char *img; + struct image tmp_image; + + /* No point falling back to 3-way merge in these cases */ + if (patch->is_new || patch->is_delete || + S_ISGITLINK(patch->old_mode) || S_ISGITLINK(patch->new_mode)) + return -1; + + /* Preimage the patch was prepared for */ + if (get_sha1(patch->old_sha1_prefix, pre_sha1) || + read_blob_object(&buf, pre_sha1, patch->old_mode)) + return error("repository lacks the necessary blob to fall back on 3-way merge."); + img = strbuf_detach(&buf, &len); + prepare_image(&tmp_image, img, len, 1); + /* Apply the patch to get the post image */ + if (apply_fragments(&tmp_image, patch) < 0) { + clear_image(&tmp_image); + return -1; + } + /* post_sha1[] is theirs */ + write_sha1_file(tmp_image.buf, tmp_image.len, blob_type, post_sha1); + clear_image(&tmp_image); + + /* our_sha1[] is ours */ + if (load_preimage(&tmp_image, patch, st, ce)) + return error("cannot read the current contents of '%s'", + patch->old_name); + write_sha1_file(tmp_image.buf, tmp_image.len, blob_type, our_sha1); + clear_image(&tmp_image); + + /* in-core three-way merge between post and our using pre as base */ + return three_way_merge(image, + patch->new_name, pre_sha1, our_sha1, post_sha1); } static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce) -- cgit v1.2.1 From 28ff051268463e93613998a017a10d3de45abbc0 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 9 May 2012 16:10:51 -0700 Subject: apply: plug the three-way merge logic in When a patch does not apply to what we have, but we know the preimage the patch was made against, we apply the patch to the preimage to compute what the patch author wanted the result to look like, and attempt a three-way merge between the result and our version, using the intended preimage as the base version. When we are applying the patch using the index, we would additionally need to add the object names of these three blobs involved in the merge, which is not yet done in this step, but we add a field to "struct patch" so that later write-out step can use it. Signed-off-by: Junio C Hamano --- builtin/apply.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 3 deletions(-) (limited to 'builtin') diff --git a/builtin/apply.c b/builtin/apply.c index 5a7201fc8..d84958b59 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -16,6 +16,8 @@ #include "dir.h" #include "diff.h" #include "parse-options.h" +#include "xdiff-interface.h" +#include "ll-merge.h" /* * --check turns on checking that the working tree matches the @@ -194,12 +196,16 @@ struct patch { unsigned int is_copy:1; unsigned int is_rename:1; unsigned int recount:1; + unsigned int conflicted_threeway:1; struct fragment *fragments; char *result; size_t resultsize; char old_sha1_prefix[41]; char new_sha1_prefix[41]; struct patch *next; + + /* three-way fallback result */ + unsigned char threeway_stage[3][20]; }; static void free_fragment_list(struct fragment *list) @@ -3146,7 +3152,29 @@ static int three_way_merge(struct image *image, const unsigned char *ours, const unsigned char *theirs) { - return -1; /* for now */ + mmfile_t base_file, our_file, their_file; + mmbuffer_t result = { NULL }; + int status; + + read_mmblob(&base_file, base); + read_mmblob(&our_file, ours); + read_mmblob(&their_file, theirs); + status = ll_merge(&result, path, + &base_file, "base", + &our_file, "ours", + &their_file, "theirs", NULL); + free(base_file.ptr); + free(our_file.ptr); + free(their_file.ptr); + if (status < 0 || !result.ptr) { + free(result.ptr); + return -1; + } + clear_image(image); + image->buf = result.ptr; + image->len = result.size; + + return status; } static int try_threeway(struct image *image, struct patch *patch, @@ -3155,6 +3183,7 @@ static int try_threeway(struct image *image, struct patch *patch, unsigned char pre_sha1[20], post_sha1[20], our_sha1[20]; struct strbuf buf = STRBUF_INIT; size_t len; + int status; char *img; struct image tmp_image; @@ -3167,6 +3196,9 @@ static int try_threeway(struct image *image, struct patch *patch, if (get_sha1(patch->old_sha1_prefix, pre_sha1) || read_blob_object(&buf, pre_sha1, patch->old_mode)) return error("repository lacks the necessary blob to fall back on 3-way merge."); + + fprintf(stderr, "Falling back to three-way merge...\n"); + img = strbuf_detach(&buf, &len); prepare_image(&tmp_image, img, len, 1); /* Apply the patch to get the post image */ @@ -3186,8 +3218,23 @@ static int try_threeway(struct image *image, struct patch *patch, clear_image(&tmp_image); /* in-core three-way merge between post and our using pre as base */ - return three_way_merge(image, - patch->new_name, pre_sha1, our_sha1, post_sha1); + status = three_way_merge(image, patch->new_name, + pre_sha1, our_sha1, post_sha1); + if (status < 0) { + fprintf(stderr, "Failed to fall back on three-way merge...\n"); + return status; + } + + if (status) { + patch->conflicted_threeway = 1; + hashcpy(patch->threeway_stage[0], pre_sha1); + hashcpy(patch->threeway_stage[1], our_sha1); + hashcpy(patch->threeway_stage[2], post_sha1); + fprintf(stderr, "Applied patch to '%s' with conflicts.\n", patch->new_name); + } else { + fprintf(stderr, "Applied patch to '%s' cleanly.\n", patch->new_name); + } + return 0; } static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce) -- cgit v1.2.1 From e09837e25d50cba97ffcc2934801dbd861e4276d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 12 Jun 2012 21:16:02 -0700 Subject: apply: move verify_index_match() higher We will be adding another caller of this function in a later patch. Signed-off-by: Junio C Hamano --- builtin/apply.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'builtin') diff --git a/builtin/apply.c b/builtin/apply.c index d84958b59..682852c71 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -3074,6 +3074,16 @@ static struct patch *previous_patch(struct patch *patch, int *gone) return previous; } +static int verify_index_match(struct cache_entry *ce, struct stat *st) +{ + if (S_ISGITLINK(ce->ce_mode)) { + if (!S_ISDIR(st->st_mode)) + return -1; + return 0; + } + return ce_match_stat(ce, st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE); +} + #define SUBMODULE_PATCH_WITHOUT_INDEX 1 static int load_patch_target(struct strbuf *buf, @@ -3260,16 +3270,6 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry * return 0; } -static int verify_index_match(struct cache_entry *ce, struct stat *st) -{ - if (S_ISGITLINK(ce->ce_mode)) { - if (!S_ISDIR(st->st_mode)) - return -1; - return 0; - } - return ce_match_stat(ce, st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE); -} - /* * If "patch" that we are looking at modifies or deletes what we have, * we would want it not to lose any local modification we have, either -- cgit v1.2.1 From 099f3c421a446e2eef3a1bb6aea0e451711ddd88 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 7 Jun 2012 15:04:11 -0700 Subject: apply: --3way with add/add conflict When a patch wants to create a path, but we already have it in our current state, pretend as if the patch and we independently added the same path and cause add/add conflict, so that the user can resolve it just like "git merge" in the same situation. For that purpose, implement load_current() in terms of the load_patch_target() helper introduced earlier to read the current contents from the path given by patch->new_name (patch->old_name is NULL for a creation patch). Signed-off-by: Junio C Hamano --- builtin/apply.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 62 insertions(+), 8 deletions(-) (limited to 'builtin') diff --git a/builtin/apply.c b/builtin/apply.c index 682852c71..24efb3f43 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -197,6 +197,7 @@ struct patch { unsigned int is_rename:1; unsigned int recount:1; unsigned int conflicted_threeway:1; + unsigned int direct_to_threeway:1; struct fragment *fragments; char *result; size_t resultsize; @@ -3187,6 +3188,48 @@ static int three_way_merge(struct image *image, return status; } +/* + * When directly falling back to add/add three-way merge, we read from + * the current contents of the new_name. In no cases other than that + * this function will be called. + */ +static int load_current(struct image *image, struct patch *patch) +{ + struct strbuf buf = STRBUF_INIT; + int status, pos; + size_t len; + char *img; + struct stat st; + struct cache_entry *ce; + char *name = patch->new_name; + unsigned mode = patch->new_mode; + + if (!patch->is_new) + die("BUG: patch to %s is not a creation", patch->old_name); + + pos = cache_name_pos(name, strlen(name)); + if (pos < 0) + return error(_("%s: does not exist in index"), name); + ce = active_cache[pos]; + if (lstat(name, &st)) { + if (errno != ENOENT) + return error(_("%s: %s"), name, strerror(errno)); + if (checkout_target(ce, &st)) + return -1; + } + if (verify_index_match(ce, &st)) + return error(_("%s: does not match index"), name); + + status = load_patch_target(&buf, ce, &st, name, mode); + if (status < 0) + return status; + else if (status) + return -1; + img = strbuf_detach(&buf, &len); + prepare_image(image, img, len, !patch->is_binary); + return 0; +} + static int try_threeway(struct image *image, struct patch *patch, struct stat *st, struct cache_entry *ce) { @@ -3198,13 +3241,15 @@ static int try_threeway(struct image *image, struct patch *patch, struct image tmp_image; /* No point falling back to 3-way merge in these cases */ - if (patch->is_new || patch->is_delete || + if (patch->is_delete || S_ISGITLINK(patch->old_mode) || S_ISGITLINK(patch->new_mode)) return -1; /* Preimage the patch was prepared for */ - if (get_sha1(patch->old_sha1_prefix, pre_sha1) || - read_blob_object(&buf, pre_sha1, patch->old_mode)) + if (patch->is_new) + write_sha1_file("", 0, blob_type, pre_sha1); + else if (get_sha1(patch->old_sha1_prefix, pre_sha1) || + read_blob_object(&buf, pre_sha1, patch->old_mode)) return error("repository lacks the necessary blob to fall back on 3-way merge."); fprintf(stderr, "Falling back to three-way merge...\n"); @@ -3221,9 +3266,15 @@ static int try_threeway(struct image *image, struct patch *patch, clear_image(&tmp_image); /* our_sha1[] is ours */ - if (load_preimage(&tmp_image, patch, st, ce)) - return error("cannot read the current contents of '%s'", - patch->old_name); + if (patch->is_new) { + if (load_current(&tmp_image, patch)) + return error("cannot read the current contents of '%s'", + patch->new_name); + } else { + if (load_preimage(&tmp_image, patch, st, ce)) + return error("cannot read the current contents of '%s'", + patch->old_name); + } write_sha1_file(tmp_image.buf, tmp_image.len, blob_type, our_sha1); clear_image(&tmp_image); @@ -3254,7 +3305,8 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry * if (load_preimage(&image, patch, st, ce) < 0) return -1; - if (apply_fragments(&image, patch) < 0) { + if (patch->direct_to_threeway || + apply_fragments(&image, patch) < 0) { /* Note: with --reject, apply_fragments() returns 0 */ if (!threeway || try_threeway(&image, patch, st, ce) < 0) return -1; @@ -3431,7 +3483,9 @@ static int check_patch(struct patch *patch) ((0 < patch->is_new) | (0 < patch->is_rename) | patch->is_copy)) { int err = check_to_create(new_name, ok_if_exists); - switch (err) { + if (err && threeway) { + patch->direct_to_threeway = 1; + } else switch (err) { case 0: break; /* happy */ case EXISTS_IN_INDEX: -- cgit v1.2.1 From 4f4a6cb9882516d75f345772c9bf00b891753f10 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 9 May 2012 16:50:58 -0700 Subject: apply: register conflicted stages to the index Now we have all the necessary logic to fall back on three-way merge when the patch does not cleanly apply, insert the conflicted entries to the index as appropriate. This obviously triggers only when the "--index" option is used. When we fall back to three-way merge and some of the merges fail, just like the case where the "--reject" option was specified and we had to write some "*.rej" files out for unapplicable patches, exit the command with non-zero status without showing the diffstat and summary. Otherwise they would make the list of problematic paths scroll off the display. Signed-off-by: Junio C Hamano --- builtin/apply.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 60 insertions(+), 6 deletions(-) (limited to 'builtin') diff --git a/builtin/apply.c b/builtin/apply.c index 24efb3f43..dc52c9475 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -3288,7 +3288,10 @@ static int try_threeway(struct image *image, struct patch *patch, if (status) { patch->conflicted_threeway = 1; - hashcpy(patch->threeway_stage[0], pre_sha1); + if (patch->is_new) + hashclr(patch->threeway_stage[0]); + else + hashcpy(patch->threeway_stage[0], pre_sha1); hashcpy(patch->threeway_stage[1], our_sha1); hashcpy(patch->threeway_stage[2], post_sha1); fprintf(stderr, "Applied patch to '%s' with conflicts.\n", patch->new_name); @@ -3852,6 +3855,32 @@ static void create_one_file(char *path, unsigned mode, const char *buf, unsigned die_errno(_("unable to write file '%s' mode %o"), path, mode); } +static void add_conflicted_stages_file(struct patch *patch) +{ + int stage, namelen; + unsigned ce_size, mode; + struct cache_entry *ce; + + if (!update_index) + return; + namelen = strlen(patch->new_name); + ce_size = cache_entry_size(namelen); + mode = patch->new_mode ? patch->new_mode : (S_IFREG | 0644); + + remove_file_from_cache(patch->new_name); + for (stage = 1; stage < 4; stage++) { + if (is_null_sha1(patch->threeway_stage[stage - 1])) + continue; + ce = xcalloc(1, ce_size); + memcpy(ce->name, patch->new_name, namelen); + ce->ce_mode = create_ce_mode(mode); + ce->ce_flags = create_ce_flags(namelen, stage); + hashcpy(ce->sha1, patch->threeway_stage[stage - 1]); + if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0) + die(_("unable to add cache entry for %s"), patch->new_name); + } +} + static void create_file(struct patch *patch) { char *path = patch->new_name; @@ -3862,7 +3891,11 @@ static void create_file(struct patch *patch) if (!mode) mode = S_IFREG | 0644; create_one_file(path, mode, buf, size); - add_index_file(path, mode, buf, size); + + if (patch->conflicted_threeway) + add_conflicted_stages_file(patch); + else + add_index_file(path, mode, buf, size); } /* phase zero is to remove, phase one is to create */ @@ -3964,6 +3997,7 @@ static int write_out_results(struct patch *list) int phase; int errs = 0; struct patch *l; + struct string_list cpath = STRING_LIST_INIT_DUP; for (phase = 0; phase < 2; phase++) { l = list; @@ -3972,12 +4006,28 @@ static int write_out_results(struct patch *list) errs = 1; else { write_out_one_result(l, phase); - if (phase == 1 && write_out_one_reject(l)) - errs = 1; + if (phase == 1) { + if (write_out_one_reject(l)) + errs = 1; + if (l->conflicted_threeway) { + string_list_append(&cpath, l->new_name); + errs = 1; + } + } } l = l->next; } } + + if (cpath.nr) { + struct string_list_item *item; + + sort_string_list(&cpath); + for_each_string_list_item(item, &cpath) + fprintf(stderr, "U %s\n", item->string); + string_list_clear(&cpath, 0); + } + return errs; } @@ -4100,8 +4150,12 @@ static int apply_patch(int fd, const char *filename, int options) !apply_with_reject) exit(1); - if (apply && write_out_results(list)) - exit(1); + if (apply && write_out_results(list)) { + if (apply_with_reject) + exit(1); + /* with --3way, we still need to write the index out */ + return 1; + } if (fake_ancestor) build_fake_ancestor(list, fake_ancestor); -- cgit v1.2.1 From f2633ebd766b7f5817ae0ec69060170bb9b1501f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 10 May 2012 13:56:49 -0700 Subject: apply: allow rerere() to work on --3way results Signed-off-by: Junio C Hamano --- builtin/apply.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'builtin') diff --git a/builtin/apply.c b/builtin/apply.c index dc52c9475..cd68862aa 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -18,6 +18,7 @@ #include "parse-options.h" #include "xdiff-interface.h" #include "ll-merge.h" +#include "rerere.h" /* * --check turns on checking that the working tree matches the @@ -4026,6 +4027,8 @@ static int write_out_results(struct patch *list) for_each_string_list_item(item, &cpath) fprintf(stderr, "U %s\n", item->string); string_list_clear(&cpath, 0); + + rerere(0); } return errs; -- cgit v1.2.1