{"thread":{"id":"30989","subject":"[PATCH v4 00/19] \"git apply --3way\"","startedAt":"2012-07-10T07:03:53Z","lastAt":"2012-07-10T07:04:12Z","messageCount":20,"participants":["Junio C Hamano"],"isPatch":true,"patchVersion":4,"patchTotal":19},"messages":[{"id":"194843","messageId":"1341903852-4815-1-git-send-email-gitster@pobox.com","threadId":"30989","inReplyTo":null,"subject":"[PATCH v4 00/19] \"git apply --3way\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-07-10T07:03:53Z","receivedAt":"2012-07-10T07:03:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"With finishing touches (mostly updates to in-code comments and log\nmessages). Previous ones were:\n\n    http://thread.gmane.org/gmane.comp.version-control.git/197538\n    http://thread.gmane.org/gmane.comp.version-control.git/197637\n    http://thread.gmane.org/gmane.comp.version-control.git/199936\n\nTeach \"git apply\" a similar \"-3\"way merge fallback option with this\nseries, and you can now apply your patches without having to reduce\ncontext.  As it will leave the conflicted halves in the index and\nlet you manually resolve conflicts in the working tree, \"--3way\"\nimplies \"--index\", and cannot be used with \"--cached\" or \"--reject\".\n\nI've been using this in my regular workflow, which involves a fair\namount of \"git diff >P.diff\" later followed by \"git apply P.diff\"\nwhen flipping patches in topics around, and things seem to work OK.\n\nJunio C Hamano (19):\n  apply: fix an incomplete comment in check_patch()\n  apply: a bit more comments on PATH_TO_BE_DELETED\n  apply: clear_image() clears things a bit more\n  apply: refactor read_file_or_gitlink()\n  apply: factor out checkout_target() helper function\n  apply: split load_preimage() helper function out\n  apply: refactor \"previous patch\" logic\n  apply: further split load_preimage()\n  apply: move check_to_create_blob() closer to its sole caller\n  apply: move \"already exists\" logic to check_to_create()\n  apply: accept -3/--3way command line option\n  apply: fall back on three-way merge\n  apply: plug the three-way merge logic in\n  apply: move verify_index_match() higher\n  apply: --3way with add/add conflict\n  apply: register conflicted stages to the index\n  apply: allow rerere() to work on --3way results\n  apply: document --3way option\n  apply: tests for the --3way option\n\n Documentation/git-apply.txt |  11 +-\n builtin/apply.c             | 557 ++++++++++++++++++++++++++++++++++----------\n t/t4108-apply-threeway.sh   | 157 +++++++++++++\n t/t4117-apply-reject.sh     |   8 +\n 4 files changed, 615 insertions(+), 118 deletions(-)\n create mode 100755 t/t4108-apply-threeway.sh\n\n-- \n1.7.11.1.294.g68a9409\n"},{"id":"194844","messageId":"1341903852-4815-2-git-send-email-gitster@pobox.com","threadId":"30989","inReplyTo":"1341903852-4815-1-git-send-email-gitster@pobox.com","subject":"[PATCH v4 01/19] apply: fix an incomplete comment in check_patch()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-07-10T07:03:54Z","receivedAt":"2012-07-10T07:03:54Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This check is not only about type-change (for which it would be\nsufficient to check only was_deleted()) but is also about a swap\nrename.  Otherwise to_be_deleted() check is not justified.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin/apply.c | 24 +++++++++++++++---------\n 1 file changed, 15 insertions(+), 9 deletions(-)\n\ndiff --git a/builtin/apply.c b/builtin/apply.c\nindex 725712d..44f6de9 100644\n--- a/builtin/apply.c\n+++ b/builtin/apply.c\n@@ -3218,16 +3218,22 @@ static int check_patch(struct patch *patch)\n \t\treturn status;\n \told_name = patch->old_name;\n \n+\t/*\n+\t * A type-change diff is always split into a patch to delete\n+\t * old, immediately followed by a patch to create new (see\n+\t * diff.c::run_diff()); in such a case it is Ok that the entry\n+\t * to be deleted by the previous patch is still in the working\n+\t * tree and in the index.\n+\t *\n+\t * A patch to swap-rename between A and B would first rename A\n+\t * to B and then rename B to A.  While applying the first one,\n+\t * the presense of B should not stop A from getting renamed to\n+\t * B; ask to_be_deleted() about the later rename.  Removal of\n+\t * B and rename from A to B is handled the same way by asking\n+\t * was_deleted().\n+\t */\n \tif ((tpatch = in_fn_table(new_name)) &&\n-\t\t\t(was_deleted(tpatch) || to_be_deleted(tpatch)))\n-\t\t/*\n-\t\t * A type-change diff is always split into a patch to\n-\t\t * delete old, immediately followed by a patch to\n-\t\t * create new (see diff.c::run_diff()); in such a case\n-\t\t * it is Ok that the entry to be deleted by the\n-\t\t * previous patch is still in the working tree and in\n-\t\t * the index.\n-\t\t */\n+\t    (was_deleted(tpatch) || to_be_deleted(tpatch)))\n \t\tok_if_exists = 1;\n \telse\n \t\tok_if_exists = 0;\n-- \n1.7.11.1.294.g68a9409\n"},{"id":"194845","messageId":"1341903852-4815-3-git-send-email-gitster@pobox.com","threadId":"30989","inReplyTo":"1341903852-4815-1-git-send-email-gitster@pobox.com","subject":"[PATCH v4 02/19] apply: a bit more comments on PATH_TO_BE_DELETED","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-07-10T07:03:55Z","receivedAt":"2012-07-10T07:03:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"The code is littered with to_be_deleted() whose purpose is not so clear.\nDescribe where it matters.  Also remove an extra space before \"#define\"\nthat snuck in by mistake at 7fac0ee (builtin-apply: keep information about\nfiles to be deleted, 2009-04-11).\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin/apply.c | 10 ++++++++--\n 1 file changed, 8 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/apply.c b/builtin/apply.c\nindex 44f6de9..35460c9 100644\n--- a/builtin/apply.c\n+++ b/builtin/apply.c\n@@ -2970,9 +2970,15 @@ static struct patch *in_fn_table(const char *name)\n  * item->util in the filename table records the status of the path.\n  * Usually it points at a patch (whose result records the contents\n  * of it after applying it), but it could be PATH_WAS_DELETED for a\n- * path that a previously applied patch has already removed.\n+ * path that a previously applied patch has already removed, or\n+ * PATH_TO_BE_DELETED for a path that a later patch would remove.\n+ *\n+ * The latter is needed to deal with a case where two paths A and B\n+ * are swapped by first renaming A to B and then renaming B to A;\n+ * moving A to B should not be prevented due to presense of B as we\n+ * will remove it in a later patch.\n  */\n- #define PATH_TO_BE_DELETED ((struct patch *) -2)\n+#define PATH_TO_BE_DELETED ((struct patch *) -2)\n #define PATH_WAS_DELETED ((struct patch *) -1)\n \n static int to_be_deleted(struct patch *patch)\n-- \n1.7.11.1.294.g68a9409\n"},{"id":"194846","messageId":"1341903852-4815-4-git-send-email-gitster@pobox.com","threadId":"30989","inReplyTo":"1341903852-4815-1-git-send-email-gitster@pobox.com","subject":"[PATCH v4 03/19] apply: clear_image() clears things a bit more","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-07-10T07:03:56Z","receivedAt":"2012-07-10T07:03:56Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"The clear_image() function did not clear the line table in the image\nstructure; this does not matter for the current callers, as the function\nis only called from the codepaths that deal with binary patches where the\nline table is never populated, and the codepaths that do populate the line\ntable free it themselves.\n\nBut it will start to matter when we introduce a codepath to retry a failed\npatch, so make sure it clears and frees everything.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin/apply.c | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/apply.c b/builtin/apply.c\nindex 35460c9..09f5df3 100644\n--- a/builtin/apply.c\n+++ b/builtin/apply.c\n@@ -371,8 +371,8 @@ static void prepare_image(struct image *image, char *buf, size_t len,\n static void clear_image(struct image *image)\n {\n \tfree(image->buf);\n-\timage->buf = NULL;\n-\timage->len = 0;\n+\tfree(image->line_allocated);\n+\tmemset(image, 0, sizeof(*image));\n }\n \n /* fmt must contain _one_ %s and no other substitution */\n-- \n1.7.11.1.294.g68a9409\n"},{"id":"194848","messageId":"1341903852-4815-5-git-send-email-gitster@pobox.com","threadId":"30989","inReplyTo":"1341903852-4815-1-git-send-email-gitster@pobox.com","subject":"[PATCH v4 04/19] apply: refactor read_file_or_gitlink()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-07-10T07:03:57Z","receivedAt":"2012-07-10T07:03:57Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Reading a blob out of the object store does not have to require that the\ncaller has a cache entry for it.\n\nCreate a read_blob_object() helper function that takes the object name and\nmode, and use it to reimplement the original function as a thin wrapper to\nit.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin/apply.c | 18 +++++++++++-------\n 1 file changed, 11 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/apply.c b/builtin/apply.c\nindex 09f5df3..15bcbb0 100644\n--- a/builtin/apply.c\n+++ b/builtin/apply.c\n@@ -2930,20 +2930,17 @@ static int apply_fragments(struct image *img, struct patch *patch)\n \treturn 0;\n }\n \n-static int read_file_or_gitlink(struct cache_entry *ce, struct strbuf *buf)\n+static int read_blob_object(struct strbuf *buf, const unsigned char *sha1, unsigned mode)\n {\n-\tif (!ce)\n-\t\treturn 0;\n-\n-\tif (S_ISGITLINK(ce->ce_mode)) {\n+\tif (S_ISGITLINK(mode)) {\n \t\tstrbuf_grow(buf, 100);\n-\t\tstrbuf_addf(buf, \"Subproject commit %s\\n\", sha1_to_hex(ce->sha1));\n+\t\tstrbuf_addf(buf, \"Subproject commit %s\\n\", sha1_to_hex(sha1));\n \t} else {\n \t\tenum object_type type;\n \t\tunsigned long sz;\n \t\tchar *result;\n \n-\t\tresult = read_sha1_file(ce->sha1, &type, &sz);\n+\t\tresult = read_sha1_file(sha1, &type, &sz);\n \t\tif (!result)\n \t\t\treturn -1;\n \t\t/* XXX read_sha1_file NUL-terminates */\n@@ -2952,6 +2949,13 @@ static int read_file_or_gitlink(struct cache_entry *ce, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int read_file_or_gitlink(struct cache_entry *ce, struct strbuf *buf)\n+{\n+\tif (!ce)\n+\t\treturn 0;\n+\treturn read_blob_object(buf, ce->sha1, ce->ce_mode);\n+}\n+\n static struct patch *in_fn_table(const char *name)\n {\n \tstruct string_list_item *item;\n-- \n1.7.11.1.294.g68a9409\n"},{"id":"194847","messageId":"1341903852-4815-6-git-send-email-gitster@pobox.com","threadId":"30989","inReplyTo":"1341903852-4815-1-git-send-email-gitster@pobox.com","subject":"[PATCH v4 05/19] apply: factor out checkout_target() helper function","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-07-10T07:03:58Z","receivedAt":"2012-07-10T07:03:58Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"When a patch wants to touch a path, if the path exists in the index\nbut is missing in the working tree, \"git apply --index\" checks out\nthe file to the working tree from the index automatically and then\napplies the patch.\n\nSplit this logic out to a separate helper function.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin/apply.c | 20 +++++++++++++-------\n 1 file changed, 13 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/apply.c b/builtin/apply.c\nindex 15bcbb0..487e403 100644\n--- a/builtin/apply.c\n+++ b/builtin/apply.c\n@@ -3034,6 +3034,18 @@ static void prepare_fn_table(struct patch *patch)\n \t}\n }\n \n+static int checkout_target(struct cache_entry *ce, struct stat *st)\n+{\n+\tstruct checkout costate;\n+\n+\tmemset(&costate, 0, sizeof(costate));\n+\tcostate.base_dir = \"\";\n+\tcostate.refresh_cache = 1;\n+\tif (checkout_entry(ce, &costate, NULL) || lstat(ce->name, st))\n+\t\treturn error(_(\"cannot checkout %s\"), ce->name);\n+\treturn 0;\n+}\n+\n static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n@@ -3163,13 +3175,7 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s\n \t\t}\n \t\t*ce = active_cache[pos];\n \t\tif (stat_ret < 0) {\n-\t\t\tstruct checkout costate;\n-\t\t\t/* checkout */\n-\t\t\tmemset(&costate, 0, sizeof(costate));\n-\t\t\tcostate.base_dir = \"\";\n-\t\t\tcostate.refresh_cache = 1;\n-\t\t\tif (checkout_entry(*ce, &costate, NULL) ||\n-\t\t\t    lstat(old_name, st))\n+\t\t\tif (checkout_target(*ce, st))\n \t\t\t\treturn -1;\n \t\t}\n \t\tif (!cached && verify_index_match(*ce, st))\n-- \n1.7.11.1.294.g68a9409\n"},{"id":"194849","messageId":"1341903852-4815-7-git-send-email-gitster@pobox.com","threadId":"30989","inReplyTo":"1341903852-4815-1-git-send-email-gitster@pobox.com","subject":"[PATCH v4 06/19] apply: split load_preimage() helper function out","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-07-10T07:03:59Z","receivedAt":"2012-07-10T07:03:59Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Given a patch for a single path, the function apply_data() reads the\npreimage in core, and applies the change represented in the patch.\n\nSeparate out the first part that reads the preimage into a separate\nhelper function load_preimage().\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin/apply.c | 15 ++++++++++++---\n 1 file changed, 12 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/apply.c b/builtin/apply.c\nindex 487e403..4d2546f 100644\n--- a/builtin/apply.c\n+++ b/builtin/apply.c\n@@ -3046,10 +3046,10 @@ static int checkout_target(struct cache_entry *ce, struct stat *st)\n \treturn 0;\n }\n \n-static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)\n+static int load_preimage(struct image *image,\n+\t\t\t struct patch *patch, struct stat *st, struct cache_entry *ce)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n-\tstruct image image;\n \tsize_t len;\n \tchar *img;\n \tstruct patch *tpatch;\n@@ -3086,7 +3086,16 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *\n \t}\n \n \timg = strbuf_detach(&buf, &len);\n-\tprepare_image(&image, img, len, !patch->is_binary);\n+\tprepare_image(image, img, len, !patch->is_binary);\n+\treturn 0;\n+}\n+\n+static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)\n+{\n+\tstruct image image;\n+\n+\tif (load_preimage(&image, patch, st, ce) < 0)\n+\t\treturn -1;\n \n \tif (apply_fragments(&image, patch) < 0)\n \t\treturn -1; /* note with --reject this succeeds. */\n-- \n1.7.11.1.294.g68a9409\n"},{"id":"194850","messageId":"1341903852-4815-8-git-send-email-gitster@pobox.com","threadId":"30989","inReplyTo":"1341903852-4815-1-git-send-email-gitster@pobox.com","subject":"[PATCH v4 07/19] apply: refactor \"previous patch\" logic","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-07-10T07:04:00Z","receivedAt":"2012-07-10T07:04:00Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"The code to grab the result of application of a previous patch in the\ninput was mixed with error message generation for a case where a later\npatch tries to modify contents of a path that has been removed.\n\nThe same code is duplicated elsewhere in the code.  Introduce a helper\nto clarify what is going on.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin/apply.c | 82 +++++++++++++++++++++++++++++++++++++++------------------\n 1 file changed, 56 insertions(+), 26 deletions(-)\n\ndiff --git a/builtin/apply.c b/builtin/apply.c\nindex 4d2546f..5642433 100644\n--- a/builtin/apply.c\n+++ b/builtin/apply.c\n@@ -3046,22 +3046,50 @@ static int checkout_target(struct cache_entry *ce, struct stat *st)\n \treturn 0;\n }\n \n+static struct patch *previous_patch(struct patch *patch, int *gone)\n+{\n+\tstruct patch *previous;\n+\n+\t*gone = 0;\n+\tif (patch->is_copy || patch->is_rename)\n+\t\treturn NULL; /* \"git\" patches do not depend on the order */\n+\n+\tprevious = in_fn_table(patch->old_name);\n+\tif (!previous)\n+\t\treturn NULL;\n+\n+\tif (to_be_deleted(previous))\n+\t\treturn NULL; /* the deletion hasn't happened yet */\n+\n+\tif (was_deleted(previous))\n+\t\t*gone = 1;\n+\n+\treturn previous;\n+}\n+\n+/*\n+ * We are about to apply \"patch\"; populate the \"image\" with the\n+ * current version we have, from the working tree or from the index,\n+ * depending on the situation e.g. --cached/--index.  If we are\n+ * applying a non-git patch that incrementally updates the tree,\n+ * we read from the result of a previous diff.\n+ */\n static int load_preimage(struct image *image,\n \t\t\t struct patch *patch, struct stat *st, struct cache_entry *ce)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n \tsize_t len;\n \tchar *img;\n-\tstruct patch *tpatch;\n+\tstruct patch *previous;\n+\tint status;\n \n-\tif (!(patch->is_copy || patch->is_rename) &&\n-\t    (tpatch = in_fn_table(patch->old_name)) != NULL && !to_be_deleted(tpatch)) {\n-\t\tif (was_deleted(tpatch)) {\n-\t\t\treturn error(_(\"patch %s has been renamed/deleted\"),\n-\t\t\t\tpatch->old_name);\n-\t\t}\n+\tprevious = previous_patch(patch, &status);\n+\tif (status)\n+\t\treturn error(_(\"path %s has been renamed/deleted\"),\n+\t\t\t     patch->old_name);\n+\tif (previous) {\n \t\t/* We have a patched copy in memory; use that. */\n-\t\tstrbuf_add(&buf, tpatch->result, tpatch->resultsize);\n+\t\tstrbuf_add(&buf, previous->result, previous->resultsize);\n \t} else if (cached) {\n \t\tif (read_file_or_gitlink(ce, &buf))\n \t\t\treturn error(_(\"read of %s failed\"), patch->old_name);\n@@ -3143,39 +3171,41 @@ static int verify_index_match(struct cache_entry *ce, struct stat *st)\n \treturn ce_match_stat(ce, st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE);\n }\n \n+/*\n+ * If \"patch\" that we are looking at modifies or deletes what we have,\n+ * we would want it not to lose any local modification we have, either\n+ * in the working tree or in the index.\n+ *\n+ * This also decides if a non-git patch is a creation patch or a\n+ * modification to an existing empty file.  We do not check the state\n+ * of the current tree for a creation patch in this function; the caller\n+ * check_patch() separately makes sure (and errors out otherwise) that\n+ * the path the patch creates does not exist in the current tree.\n+ */\n static int check_preimage(struct patch *patch, struct cache_entry **ce, struct stat *st)\n {\n \tconst char *old_name = patch->old_name;\n-\tstruct patch *tpatch = NULL;\n-\tint stat_ret = 0;\n+\tstruct patch *previous = NULL;\n+\tint stat_ret = 0, status;\n \tunsigned st_mode = 0;\n \n-\t/*\n-\t * Make sure that we do not have local modifications from the\n-\t * index when we are looking at the index.  Also make sure\n-\t * we have the preimage file to be patched in the work tree,\n-\t * unless --cached, which tells git to apply only in the index.\n-\t */\n \tif (!old_name)\n \t\treturn 0;\n \n \tassert(patch->is_new <= 0);\n+\tprevious = previous_patch(patch, &status);\n \n-\tif (!(patch->is_copy || patch->is_rename) &&\n-\t    (tpatch = in_fn_table(old_name)) != NULL && !to_be_deleted(tpatch)) {\n-\t\tif (was_deleted(tpatch))\n-\t\t\treturn error(_(\"%s: has been deleted/renamed\"), old_name);\n-\t\tst_mode = tpatch->new_mode;\n+\tif (status)\n+\t\treturn error(_(\"path %s has been renamed/deleted\"), old_name);\n+\tif (previous) {\n+\t\tst_mode = previous->new_mode;\n \t} else if (!cached) {\n \t\tstat_ret = lstat(old_name, st);\n \t\tif (stat_ret && errno != ENOENT)\n \t\t\treturn error(_(\"%s: %s\"), old_name, strerror(errno));\n \t}\n \n-\tif (to_be_deleted(tpatch))\n-\t\ttpatch = NULL;\n-\n-\tif (check_index && !tpatch) {\n+\tif (check_index && !previous) {\n \t\tint pos = cache_name_pos(old_name, strlen(old_name));\n \t\tif (pos < 0) {\n \t\t\tif (patch->is_new < 0)\n@@ -3197,7 +3227,7 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s\n \t\treturn error(_(\"%s: %s\"), old_name, strerror(errno));\n \t}\n \n-\tif (!cached && !tpatch)\n+\tif (!cached && !previous)\n \t\tst_mode = ce_mode_from_stat(*ce, st->st_mode);\n \n \tif (patch->is_new < 0)\n-- \n1.7.11.1.294.g68a9409\n"},{"id":"194851","messageId":"1341903852-4815-9-git-send-email-gitster@pobox.com","threadId":"30989","inReplyTo":"1341903852-4815-1-git-send-email-gitster@pobox.com","subject":"[PATCH v4 08/19] apply: further split load_preimage()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-07-10T07:04:01Z","receivedAt":"2012-07-10T07:04:01Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"load_preimage() is very specific to grab the current contents for\nthe path given by patch->old_name.  Split the logic that grabs the\ncontents for a path out of it into a separate load_patch_target()\nfunction.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin/apply.c | 59 ++++++++++++++++++++++++++++++++++++++-------------------\n 1 file changed, 40 insertions(+), 19 deletions(-)\n\ndiff --git a/builtin/apply.c b/builtin/apply.c\nindex 5642433..ee1a960 100644\n--- a/builtin/apply.c\n+++ b/builtin/apply.c\n@@ -3067,6 +3067,31 @@ static struct patch *previous_patch(struct patch *patch, int *gone)\n \treturn previous;\n }\n \n+#define SUBMODULE_PATCH_WITHOUT_INDEX 1\n+\n+static int load_patch_target(struct strbuf *buf,\n+\t\t\t     struct cache_entry *ce,\n+\t\t\t     struct stat *st,\n+\t\t\t     const char *name,\n+\t\t\t     unsigned expected_mode)\n+{\n+\tif (cached) {\n+\t\tif (read_file_or_gitlink(ce, buf))\n+\t\t\treturn error(_(\"read of %s failed\"), name);\n+\t} else if (name) {\n+\t\tif (S_ISGITLINK(expected_mode)) {\n+\t\t\tif (ce)\n+\t\t\t\treturn read_file_or_gitlink(ce, buf);\n+\t\t\telse\n+\t\t\t\treturn SUBMODULE_PATCH_WITHOUT_INDEX;\n+\t\t} else {\n+\t\t\tif (read_old_data(st, name, buf))\n+\t\t\t\treturn error(_(\"read of %s failed\"), name);\n+\t\t}\n+\t}\n+\treturn 0;\n+}\n+\n /*\n  * We are about to apply \"patch\"; populate the \"image\" with the\n  * current version we have, from the working tree or from the index,\n@@ -3090,26 +3115,22 @@ static int load_preimage(struct image *image,\n \tif (previous) {\n \t\t/* We have a patched copy in memory; use that. */\n \t\tstrbuf_add(&buf, previous->result, previous->resultsize);\n-\t} else if (cached) {\n-\t\tif (read_file_or_gitlink(ce, &buf))\n+\t} else {\n+\t\tstatus = load_patch_target(&buf, ce, st,\n+\t\t\t\t\t   patch->old_name, patch->old_mode);\n+\t\tif (status < 0)\n+\t\t\treturn status;\n+\t\telse if (status == SUBMODULE_PATCH_WITHOUT_INDEX) {\n+\t\t\t/*\n+\t\t\t * There is no way to apply subproject\n+\t\t\t * patch without looking at the index.\n+\t\t\t * NEEDSWORK: shouldn't this be flagged\n+\t\t\t * as an error???\n+\t\t\t */\n+\t\t\tfree_fragment_list(patch->fragments);\n+\t\t\tpatch->fragments = NULL;\n+\t\t} else if (status) {\n \t\t\treturn error(_(\"read of %s failed\"), patch->old_name);\n-\t} else if (patch->old_name) {\n-\t\tif (S_ISGITLINK(patch->old_mode)) {\n-\t\t\tif (ce) {\n-\t\t\t\tread_file_or_gitlink(ce, &buf);\n-\t\t\t} else {\n-\t\t\t\t/*\n-\t\t\t\t * There is no way to apply subproject\n-\t\t\t\t * patch without looking at the index.\n-\t\t\t\t * NEEDSWORK: shouldn't this be flagged\n-\t\t\t\t * as an error???\n-\t\t\t\t */\n-\t\t\t\tfree_fragment_list(patch->fragments);\n-\t\t\t\tpatch->fragments = NULL;\n-\t\t\t}\n-\t\t} else {\n-\t\t\tif (read_old_data(st, patch->old_name, &buf))\n-\t\t\t\treturn error(_(\"read of %s failed\"), patch->old_name);\n \t\t}\n \t}\n \n-- \n1.7.11.1.294.g68a9409\n"},{"id":"194861","messageId":"1341903852-4815-10-git-send-email-gitster@pobox.com","threadId":"30989","inReplyTo":"1341903852-4815-1-git-send-email-gitster@pobox.com","subject":"[PATCH v4 09/19] apply: move check_to_create_blob() closer to its sole caller","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-07-10T07:04:02Z","receivedAt":"2012-07-10T07:04:02Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Signed-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin/apply.c | 46 +++++++++++++++++++++++-----------------------\n 1 file changed, 23 insertions(+), 23 deletions(-)\n\ndiff --git a/builtin/apply.c b/builtin/apply.c\nindex ee1a960..cd25e63 100644\n--- a/builtin/apply.c\n+++ b/builtin/apply.c\n@@ -3159,29 +3159,6 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *\n \treturn 0;\n }\n \n-static int check_to_create_blob(const char *new_name, int ok_if_exists)\n-{\n-\tstruct stat nst;\n-\tif (!lstat(new_name, &nst)) {\n-\t\tif (S_ISDIR(nst.st_mode) || ok_if_exists)\n-\t\t\treturn 0;\n-\t\t/*\n-\t\t * A leading component of new_name might be a symlink\n-\t\t * that is going to be removed with this patch, but\n-\t\t * still pointing at somewhere that has the path.\n-\t\t * In such a case, path \"new_name\" does not exist as\n-\t\t * far as git is concerned.\n-\t\t */\n-\t\tif (has_symlink_leading_path(new_name, strlen(new_name)))\n-\t\t\treturn 0;\n-\n-\t\treturn error(_(\"%s: already exists in working directory\"), new_name);\n-\t}\n-\telse if ((errno != ENOENT) && (errno != ENOTDIR))\n-\t\treturn error(\"%s: %s\", new_name, strerror(errno));\n-\treturn 0;\n-}\n-\n static int verify_index_match(struct cache_entry *ce, struct stat *st)\n {\n \tif (S_ISGITLINK(ce->ce_mode)) {\n@@ -3272,6 +3249,29 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s\n \treturn 0;\n }\n \n+static int check_to_create_blob(const char *new_name, int ok_if_exists)\n+{\n+\tstruct stat nst;\n+\tif (!lstat(new_name, &nst)) {\n+\t\tif (S_ISDIR(nst.st_mode) || ok_if_exists)\n+\t\t\treturn 0;\n+\t\t/*\n+\t\t * A leading component of new_name might be a symlink\n+\t\t * that is going to be removed with this patch, but\n+\t\t * still pointing at somewhere that has the path.\n+\t\t * In such a case, path \"new_name\" does not exist as\n+\t\t * far as git is concerned.\n+\t\t */\n+\t\tif (has_symlink_leading_path(new_name, strlen(new_name)))\n+\t\t\treturn 0;\n+\n+\t\treturn error(_(\"%s: already exists in working directory\"), new_name);\n+\t}\n+\telse if ((errno != ENOENT) && (errno != ENOTDIR))\n+\t\treturn error(\"%s: %s\", new_name, strerror(errno));\n+\treturn 0;\n+}\n+\n /*\n  * Check and apply the patch in-core; leave the result in patch->result\n  * for the caller to write it out to the final destination.\n-- \n1.7.11.1.294.g68a9409\n"},{"id":"194862","messageId":"1341903852-4815-11-git-send-email-gitster@pobox.com","threadId":"30989","inReplyTo":"1341903852-4815-1-git-send-email-gitster@pobox.com","subject":"[PATCH v4 10/19] apply: move \"already exists\" logic to check_to_create()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-07-10T07:04:03Z","receivedAt":"2012-07-10T07:04:03Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"The check_to_create_blob() function used to check only the case\nwhere we are applying to the working tree.  Rename the function to\ncheck_to_create() and make it also responsible for checking the case\nwhere we apply to the index.  Also make its caller responsible for\nissuing an error message.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin/apply.c | 40 +++++++++++++++++++++++++++++-----------\n 1 file changed, 29 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/apply.c b/builtin/apply.c\nindex cd25e63..7379687 100644\n--- a/builtin/apply.c\n+++ b/builtin/apply.c\n@@ -3249,9 +3249,21 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s\n \treturn 0;\n }\n \n-static int check_to_create_blob(const char *new_name, int ok_if_exists)\n+\n+#define EXISTS_IN_INDEX 1\n+#define EXISTS_IN_WORKTREE 2\n+\n+static int check_to_create(const char *new_name, int ok_if_exists)\n {\n \tstruct stat nst;\n+\n+\tif (check_index &&\n+\t    cache_name_pos(new_name, strlen(new_name)) >= 0 &&\n+\t    !ok_if_exists)\n+\t\treturn EXISTS_IN_INDEX;\n+\tif (cached)\n+\t\treturn 0;\n+\n \tif (!lstat(new_name, &nst)) {\n \t\tif (S_ISDIR(nst.st_mode) || ok_if_exists)\n \t\t\treturn 0;\n@@ -3265,10 +3277,10 @@ static int check_to_create_blob(const char *new_name, int ok_if_exists)\n \t\tif (has_symlink_leading_path(new_name, strlen(new_name)))\n \t\t\treturn 0;\n \n-\t\treturn error(_(\"%s: already exists in working directory\"), new_name);\n-\t}\n-\telse if ((errno != ENOENT) && (errno != ENOTDIR))\n+\t\treturn EXISTS_IN_WORKTREE;\n+\t} else if ((errno != ENOENT) && (errno != ENOTDIR)) {\n \t\treturn error(\"%s: %s\", new_name, strerror(errno));\n+\t}\n \treturn 0;\n }\n \n@@ -3316,15 +3328,21 @@ static int check_patch(struct patch *patch)\n \n \tif (new_name &&\n \t    ((0 < patch->is_new) | (0 < patch->is_rename) | patch->is_copy)) {\n-\t\tif (check_index &&\n-\t\t    cache_name_pos(new_name, strlen(new_name)) >= 0 &&\n-\t\t    !ok_if_exists)\n+\t\tint err = check_to_create(new_name, ok_if_exists);\n+\n+\t\tswitch (err) {\n+\t\tcase 0:\n+\t\t\tbreak; /* happy */\n+\t\tcase EXISTS_IN_INDEX:\n \t\t\treturn error(_(\"%s: already exists in index\"), new_name);\n-\t\tif (!cached) {\n-\t\t\tint err = check_to_create_blob(new_name, ok_if_exists);\n-\t\t\tif (err)\n-\t\t\t\treturn err;\n+\t\t\tbreak;\n+\t\tcase EXISTS_IN_WORKTREE:\n+\t\t\treturn error(_(\"%s: already exists in working directory\"),\n+\t\t\t\t     new_name);\n+\t\tdefault:\n+\t\t\treturn err;\n \t\t}\n+\n \t\tif (!patch->new_mode) {\n \t\t\tif (0 < patch->is_new)\n \t\t\t\tpatch->new_mode = S_IFREG | 0644;\n-- \n1.7.11.1.294.g68a9409\n"},{"id":"194852","messageId":"1341903852-4815-12-git-send-email-gitster@pobox.com","threadId":"30989","inReplyTo":"1341903852-4815-1-git-send-email-gitster@pobox.com","subject":"[PATCH v4 11/19] apply: accept -3/--3way command line option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-07-10T07:04:04Z","receivedAt":"2012-07-10T07:04:04Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Begin teaching the three-way merge fallback logic \"git am -3\" uses\nto the underlying \"git apply\".  It only implements the command line\nparsing part, and does not do anything interesting yet, other than\nmaking sure that \"--reject\" and \"--3way\" are not given together, and\nmaking \"--3way\" imply \"--index\".\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin/apply.c         | 25 +++++++++++++++++++++++--\n t/t4117-apply-reject.sh |  8 ++++++++\n 2 files changed, 31 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/apply.c b/builtin/apply.c\nindex 7379687..51b695b 100644\n--- a/builtin/apply.c\n+++ b/builtin/apply.c\n@@ -46,6 +46,7 @@ static int apply_with_reject;\n static int apply_verbosely;\n static int allow_overlap;\n static int no_add;\n+static int threeway;\n static const char *fake_ancestor;\n static int line_termination = '\\n';\n static unsigned int p_context = UINT_MAX;\n@@ -3139,6 +3140,12 @@ static int load_preimage(struct image *image,\n \treturn 0;\n }\n \n+static int try_threeway(struct image *image, struct patch *patch,\n+\t\t\tstruct stat *st, struct cache_entry *ce)\n+{\n+\treturn -1; /* for now */\n+}\n+\n static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)\n {\n \tstruct image image;\n@@ -3146,8 +3153,11 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *\n \tif (load_preimage(&image, patch, st, ce) < 0)\n \t\treturn -1;\n \n-\tif (apply_fragments(&image, patch) < 0)\n-\t\treturn -1; /* note with --reject this succeeds. */\n+\tif (apply_fragments(&image, patch) < 0) {\n+\t\t/* Note: with --reject, apply_fragments() returns 0 */\n+\t\tif (!threeway || try_threeway(&image, patch, st, ce) < 0)\n+\t\t\treturn -1;\n+\t}\n \tpatch->result = image.buf;\n \tpatch->resultsize = image.len;\n \tadd_to_fn_table(patch);\n@@ -4079,6 +4089,8 @@ int cmd_apply(int argc, const char **argv, const char *prefix_)\n \t\t\t\"apply a patch without touching the working tree\"),\n \t\tOPT_BOOLEAN(0, \"apply\", &force_apply,\n \t\t\t\"also apply the patch (use with --stat/--summary/--check)\"),\n+\t\tOPT_BOOL('3', \"3way\", &threeway,\n+\t\t\t \"attempt three-way merge if a patch does not apply\"),\n \t\tOPT_FILENAME(0, \"build-fake-ancestor\", &fake_ancestor,\n \t\t\t\"build a temporary index based on embedded index information\"),\n \t\t{ OPTION_CALLBACK, 'z', NULL, NULL, NULL,\n@@ -4127,6 +4139,15 @@ int cmd_apply(int argc, const char **argv, const char *prefix_)\n \targc = parse_options(argc, argv, prefix, builtin_apply_options,\n \t\t\tapply_usage, 0);\n \n+\tif (apply_with_reject && threeway)\n+\t\tdie(\"--reject and --3way cannot be used together.\");\n+\tif (cached && threeway)\n+\t\tdie(\"--cached and --3way cannot be used together.\");\n+\tif (threeway) {\n+\t\tif (is_not_gitdir)\n+\t\t\tdie(_(\"--3way outside a repository\"));\n+\t\tcheck_index = 1;\n+\t}\n \tif (apply_with_reject)\n \t\tapply = apply_verbosely = 1;\n \tif (!force_apply && (diffstat || numstat || summary || check || fake_ancestor))\ndiff --git a/t/t4117-apply-reject.sh b/t/t4117-apply-reject.sh\nindex e9ccd16..8e15ecb 100755\n--- a/t/t4117-apply-reject.sh\n+++ b/t/t4117-apply-reject.sh\n@@ -46,6 +46,14 @@ test_expect_success setup '\n \tcat file1 >saved.file1\n '\n \n+test_expect_success 'apply --reject is incompatible with --3way' '\n+\ttest_when_finished \"cat saved.file1 >file1\" &&\n+\tgit diff >patch.0 &&\n+\tgit checkout file1 &&\n+\ttest_must_fail git apply --reject --3way patch.0 &&\n+\tgit diff --exit-code\n+'\n+\n test_expect_success 'apply without --reject should fail' '\n \n \tif git apply patch.1\n-- \n1.7.11.1.294.g68a9409\n"},{"id":"194856","messageId":"1341903852-4815-13-git-send-email-gitster@pobox.com","threadId":"30989","inReplyTo":"1341903852-4815-1-git-send-email-gitster@pobox.com","subject":"[PATCH v4 12/19] apply: fall back on three-way merge","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-07-10T07:04:05Z","receivedAt":"2012-07-10T07:04:05Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Grab the preimage blob the patch claims to be based on out of the object\nstore, apply the patch, and then call three-way-merge function.  This step\nstill does not plug the actual three-way merge logic yet, but we are\ngetting there.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin/apply.c | 46 +++++++++++++++++++++++++++++++++++++++++++++-\n 1 file changed, 45 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/apply.c b/builtin/apply.c\nindex 51b695b..5a7201f 100644\n--- a/builtin/apply.c\n+++ b/builtin/apply.c\n@@ -3140,10 +3140,54 @@ static int load_preimage(struct image *image,\n \treturn 0;\n }\n \n+static int three_way_merge(struct image *image,\n+\t\t\t   char *path,\n+\t\t\t   const unsigned char *base,\n+\t\t\t   const unsigned char *ours,\n+\t\t\t   const unsigned char *theirs)\n+{\n+\treturn -1; /* for now */\n+}\n+\n static int try_threeway(struct image *image, struct patch *patch,\n \t\t\tstruct stat *st, struct cache_entry *ce)\n {\n-\treturn -1; /* for now */\n+\tunsigned char pre_sha1[20], post_sha1[20], our_sha1[20];\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tsize_t len;\n+\tchar *img;\n+\tstruct image tmp_image;\n+\n+\t/* No point falling back to 3-way merge in these cases */\n+\tif (patch->is_new || patch->is_delete ||\n+\t    S_ISGITLINK(patch->old_mode) || S_ISGITLINK(patch->new_mode))\n+\t\treturn -1;\n+\n+\t/* Preimage the patch was prepared for */\n+\tif (get_sha1(patch->old_sha1_prefix, pre_sha1) ||\n+\t    read_blob_object(&buf, pre_sha1, patch->old_mode))\n+\t\treturn error(\"repository lacks the necessary blob to fall back on 3-way merge.\");\n+\timg = strbuf_detach(&buf, &len);\n+\tprepare_image(&tmp_image, img, len, 1);\n+\t/* Apply the patch to get the post image */\n+\tif (apply_fragments(&tmp_image, patch) < 0) {\n+\t\tclear_image(&tmp_image);\n+\t\treturn -1;\n+\t}\n+\t/* post_sha1[] is theirs */\n+\twrite_sha1_file(tmp_image.buf, tmp_image.len, blob_type, post_sha1);\n+\tclear_image(&tmp_image);\n+\n+\t/* our_sha1[] is ours */\n+\tif (load_preimage(&tmp_image, patch, st, ce))\n+\t\treturn error(\"cannot read the current contents of '%s'\",\n+\t\t\t     patch->old_name);\n+\twrite_sha1_file(tmp_image.buf, tmp_image.len, blob_type, our_sha1);\n+\tclear_image(&tmp_image);\n+\n+\t/* in-core three-way merge between post and our using pre as base */\n+\treturn three_way_merge(image,\n+\t\t\t       patch->new_name, pre_sha1, our_sha1, post_sha1);\n }\n \n static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)\n-- \n1.7.11.1.294.g68a9409\n"},{"id":"194853","messageId":"1341903852-4815-14-git-send-email-gitster@pobox.com","threadId":"30989","inReplyTo":"1341903852-4815-1-git-send-email-gitster@pobox.com","subject":"[PATCH v4 13/19] apply: plug the three-way merge logic in","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-07-10T07:04:06Z","receivedAt":"2012-07-10T07:04:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"When a patch does not apply to what we have, but we know the preimage the\npatch was made against, we apply the patch to the preimage to compute what\nthe patch author wanted the result to look like, and attempt a three-way\nmerge between the result and our version, using the intended preimage as\nthe base version.\n\nWhen we are applying the patch using the index, we would additionally need\nto add the object names of these three blobs involved in the merge, which\nis not yet done in this step, but we add a field to \"struct patch\" so that\nlater write-out step can use it.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin/apply.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++---\n 1 file changed, 50 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/apply.c b/builtin/apply.c\nindex 5a7201f..d84958b 100644\n--- a/builtin/apply.c\n+++ b/builtin/apply.c\n@@ -16,6 +16,8 @@\n #include \"dir.h\"\n #include \"diff.h\"\n #include \"parse-options.h\"\n+#include \"xdiff-interface.h\"\n+#include \"ll-merge.h\"\n \n /*\n  *  --check turns on checking that the working tree matches the\n@@ -194,12 +196,16 @@ struct patch {\n \tunsigned int is_copy:1;\n \tunsigned int is_rename:1;\n \tunsigned int recount:1;\n+\tunsigned int conflicted_threeway:1;\n \tstruct fragment *fragments;\n \tchar *result;\n \tsize_t resultsize;\n \tchar old_sha1_prefix[41];\n \tchar new_sha1_prefix[41];\n \tstruct patch *next;\n+\n+\t/* three-way fallback result */\n+\tunsigned char threeway_stage[3][20];\n };\n \n static void free_fragment_list(struct fragment *list)\n@@ -3146,7 +3152,29 @@ static int three_way_merge(struct image *image,\n \t\t\t   const unsigned char *ours,\n \t\t\t   const unsigned char *theirs)\n {\n-\treturn -1; /* for now */\n+\tmmfile_t base_file, our_file, their_file;\n+\tmmbuffer_t result = { NULL };\n+\tint status;\n+\n+\tread_mmblob(&base_file, base);\n+\tread_mmblob(&our_file, ours);\n+\tread_mmblob(&their_file, theirs);\n+\tstatus = ll_merge(&result, path,\n+\t\t\t  &base_file, \"base\",\n+\t\t\t  &our_file, \"ours\",\n+\t\t\t  &their_file, \"theirs\", NULL);\n+\tfree(base_file.ptr);\n+\tfree(our_file.ptr);\n+\tfree(their_file.ptr);\n+\tif (status < 0 || !result.ptr) {\n+\t\tfree(result.ptr);\n+\t\treturn -1;\n+\t}\n+\tclear_image(image);\n+\timage->buf = result.ptr;\n+\timage->len = result.size;\n+\n+\treturn status;\n }\n \n static int try_threeway(struct image *image, struct patch *patch,\n@@ -3155,6 +3183,7 @@ static int try_threeway(struct image *image, struct patch *patch,\n \tunsigned char pre_sha1[20], post_sha1[20], our_sha1[20];\n \tstruct strbuf buf = STRBUF_INIT;\n \tsize_t len;\n+\tint status;\n \tchar *img;\n \tstruct image tmp_image;\n \n@@ -3167,6 +3196,9 @@ static int try_threeway(struct image *image, struct patch *patch,\n \tif (get_sha1(patch->old_sha1_prefix, pre_sha1) ||\n \t    read_blob_object(&buf, pre_sha1, patch->old_mode))\n \t\treturn error(\"repository lacks the necessary blob to fall back on 3-way merge.\");\n+\n+\tfprintf(stderr, \"Falling back to three-way merge...\\n\");\n+\n \timg = strbuf_detach(&buf, &len);\n \tprepare_image(&tmp_image, img, len, 1);\n \t/* Apply the patch to get the post image */\n@@ -3186,8 +3218,23 @@ static int try_threeway(struct image *image, struct patch *patch,\n \tclear_image(&tmp_image);\n \n \t/* in-core three-way merge between post and our using pre as base */\n-\treturn three_way_merge(image,\n-\t\t\t       patch->new_name, pre_sha1, our_sha1, post_sha1);\n+\tstatus = three_way_merge(image, patch->new_name,\n+\t\t\t\t pre_sha1, our_sha1, post_sha1);\n+\tif (status < 0) {\n+\t\tfprintf(stderr, \"Failed to fall back on three-way merge...\\n\");\n+\t\treturn status;\n+\t}\n+\n+\tif (status) {\n+\t\tpatch->conflicted_threeway = 1;\n+\t\thashcpy(patch->threeway_stage[0], pre_sha1);\n+\t\thashcpy(patch->threeway_stage[1], our_sha1);\n+\t\thashcpy(patch->threeway_stage[2], post_sha1);\n+\t\tfprintf(stderr, \"Applied patch to '%s' with conflicts.\\n\", patch->new_name);\n+\t} else {\n+\t\tfprintf(stderr, \"Applied patch to '%s' cleanly.\\n\", patch->new_name);\n+\t}\n+\treturn 0;\n }\n \n static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)\n-- \n1.7.11.1.294.g68a9409\n"},{"id":"194855","messageId":"1341903852-4815-15-git-send-email-gitster@pobox.com","threadId":"30989","inReplyTo":"1341903852-4815-1-git-send-email-gitster@pobox.com","subject":"[PATCH v4 14/19] apply: move verify_index_match() higher","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-07-10T07:04:07Z","receivedAt":"2012-07-10T07:04:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"We will be adding another caller of this function in a later patch.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin/apply.c | 20 ++++++++++----------\n 1 file changed, 10 insertions(+), 10 deletions(-)\n\ndiff --git a/builtin/apply.c b/builtin/apply.c\nindex d84958b..682852c 100644\n--- a/builtin/apply.c\n+++ b/builtin/apply.c\n@@ -3074,6 +3074,16 @@ static struct patch *previous_patch(struct patch *patch, int *gone)\n \treturn previous;\n }\n \n+static int verify_index_match(struct cache_entry *ce, struct stat *st)\n+{\n+\tif (S_ISGITLINK(ce->ce_mode)) {\n+\t\tif (!S_ISDIR(st->st_mode))\n+\t\t\treturn -1;\n+\t\treturn 0;\n+\t}\n+\treturn ce_match_stat(ce, st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE);\n+}\n+\n #define SUBMODULE_PATCH_WITHOUT_INDEX 1\n \n static int load_patch_target(struct strbuf *buf,\n@@ -3260,16 +3270,6 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *\n \treturn 0;\n }\n \n-static int verify_index_match(struct cache_entry *ce, struct stat *st)\n-{\n-\tif (S_ISGITLINK(ce->ce_mode)) {\n-\t\tif (!S_ISDIR(st->st_mode))\n-\t\t\treturn -1;\n-\t\treturn 0;\n-\t}\n-\treturn ce_match_stat(ce, st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE);\n-}\n-\n /*\n  * If \"patch\" that we are looking at modifies or deletes what we have,\n  * we would want it not to lose any local modification we have, either\n-- \n1.7.11.1.294.g68a9409\n"},{"id":"194854","messageId":"1341903852-4815-16-git-send-email-gitster@pobox.com","threadId":"30989","inReplyTo":"1341903852-4815-1-git-send-email-gitster@pobox.com","subject":"[PATCH v4 15/19] apply: --3way with add/add conflict","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-07-10T07:04:08Z","receivedAt":"2012-07-10T07:04:08Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"When a patch wants to create a path, but we already have it in our\ncurrent state, pretend as if the patch and we independently added\nthe same path and cause add/add conflict, so that the user can\nresolve it just like \"git merge\" in the same situation.\n\nFor that purpose, implement load_current() in terms of the\nload_patch_target() helper introduced earlier to read the current\ncontents from the path given by patch->new_name (patch->old_name is\nNULL for a creation patch).\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin/apply.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++-------\n 1 file changed, 62 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/apply.c b/builtin/apply.c\nindex 682852c..24efb3f 100644\n--- a/builtin/apply.c\n+++ b/builtin/apply.c\n@@ -197,6 +197,7 @@ struct patch {\n \tunsigned int is_rename:1;\n \tunsigned int recount:1;\n \tunsigned int conflicted_threeway:1;\n+\tunsigned int direct_to_threeway:1;\n \tstruct fragment *fragments;\n \tchar *result;\n \tsize_t resultsize;\n@@ -3187,6 +3188,48 @@ static int three_way_merge(struct image *image,\n \treturn status;\n }\n \n+/*\n+ * When directly falling back to add/add three-way merge, we read from\n+ * the current contents of the new_name.  In no cases other than that\n+ * this function will be called.\n+ */\n+static int load_current(struct image *image, struct patch *patch)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tint status, pos;\n+\tsize_t len;\n+\tchar *img;\n+\tstruct stat st;\n+\tstruct cache_entry *ce;\n+\tchar *name = patch->new_name;\n+\tunsigned mode = patch->new_mode;\n+\n+\tif (!patch->is_new)\n+\t\tdie(\"BUG: patch to %s is not a creation\", patch->old_name);\n+\n+\tpos = cache_name_pos(name, strlen(name));\n+\tif (pos < 0)\n+\t\treturn error(_(\"%s: does not exist in index\"), name);\n+\tce = active_cache[pos];\n+\tif (lstat(name, &st)) {\n+\t\tif (errno != ENOENT)\n+\t\t\treturn error(_(\"%s: %s\"), name, strerror(errno));\n+\t\tif (checkout_target(ce, &st))\n+\t\t\treturn -1;\n+\t}\n+\tif (verify_index_match(ce, &st))\n+\t\treturn error(_(\"%s: does not match index\"), name);\n+\n+\tstatus = load_patch_target(&buf, ce, &st, name, mode);\n+\tif (status < 0)\n+\t\treturn status;\n+\telse if (status)\n+\t\treturn -1;\n+\timg = strbuf_detach(&buf, &len);\n+\tprepare_image(image, img, len, !patch->is_binary);\n+\treturn 0;\n+}\n+\n static int try_threeway(struct image *image, struct patch *patch,\n \t\t\tstruct stat *st, struct cache_entry *ce)\n {\n@@ -3198,13 +3241,15 @@ static int try_threeway(struct image *image, struct patch *patch,\n \tstruct image tmp_image;\n \n \t/* No point falling back to 3-way merge in these cases */\n-\tif (patch->is_new || patch->is_delete ||\n+\tif (patch->is_delete ||\n \t    S_ISGITLINK(patch->old_mode) || S_ISGITLINK(patch->new_mode))\n \t\treturn -1;\n \n \t/* Preimage the patch was prepared for */\n-\tif (get_sha1(patch->old_sha1_prefix, pre_sha1) ||\n-\t    read_blob_object(&buf, pre_sha1, patch->old_mode))\n+\tif (patch->is_new)\n+\t\twrite_sha1_file(\"\", 0, blob_type, pre_sha1);\n+\telse if (get_sha1(patch->old_sha1_prefix, pre_sha1) ||\n+\t\t read_blob_object(&buf, pre_sha1, patch->old_mode))\n \t\treturn error(\"repository lacks the necessary blob to fall back on 3-way merge.\");\n \n \tfprintf(stderr, \"Falling back to three-way merge...\\n\");\n@@ -3221,9 +3266,15 @@ static int try_threeway(struct image *image, struct patch *patch,\n \tclear_image(&tmp_image);\n \n \t/* our_sha1[] is ours */\n-\tif (load_preimage(&tmp_image, patch, st, ce))\n-\t\treturn error(\"cannot read the current contents of '%s'\",\n-\t\t\t     patch->old_name);\n+\tif (patch->is_new) {\n+\t\tif (load_current(&tmp_image, patch))\n+\t\t\treturn error(\"cannot read the current contents of '%s'\",\n+\t\t\t\t     patch->new_name);\n+\t} else {\n+\t\tif (load_preimage(&tmp_image, patch, st, ce))\n+\t\t\treturn error(\"cannot read the current contents of '%s'\",\n+\t\t\t\t     patch->old_name);\n+\t}\n \twrite_sha1_file(tmp_image.buf, tmp_image.len, blob_type, our_sha1);\n \tclear_image(&tmp_image);\n \n@@ -3254,7 +3305,8 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *\n \tif (load_preimage(&image, patch, st, ce) < 0)\n \t\treturn -1;\n \n-\tif (apply_fragments(&image, patch) < 0) {\n+\tif (patch->direct_to_threeway ||\n+\t    apply_fragments(&image, patch) < 0) {\n \t\t/* Note: with --reject, apply_fragments() returns 0 */\n \t\tif (!threeway || try_threeway(&image, patch, st, ce) < 0)\n \t\t\treturn -1;\n@@ -3431,7 +3483,9 @@ static int check_patch(struct patch *patch)\n \t    ((0 < patch->is_new) | (0 < patch->is_rename) | patch->is_copy)) {\n \t\tint err = check_to_create(new_name, ok_if_exists);\n \n-\t\tswitch (err) {\n+\t\tif (err && threeway) {\n+\t\t\tpatch->direct_to_threeway = 1;\n+\t\t} else switch (err) {\n \t\tcase 0:\n \t\t\tbreak; /* happy */\n \t\tcase EXISTS_IN_INDEX:\n-- \n1.7.11.1.294.g68a9409\n"},{"id":"194858","messageId":"1341903852-4815-17-git-send-email-gitster@pobox.com","threadId":"30989","inReplyTo":"1341903852-4815-1-git-send-email-gitster@pobox.com","subject":"[PATCH v4 16/19] apply: register conflicted stages to the index","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-07-10T07:04:09Z","receivedAt":"2012-07-10T07:04:09Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Now we have all the necessary logic to fall back on three-way merge when\nthe patch does not cleanly apply, insert the conflicted entries to the\nindex as appropriate.  This obviously triggers only when the \"--index\"\noption is used.\n\nWhen we fall back to three-way merge and some of the merges fail, just\nlike the case where the \"--reject\" option was specified and we had to\nwrite some \"*.rej\" files out for unapplicable patches, exit the command\nwith non-zero status without showing the diffstat and summary.  Otherwise\nthey would make the list of problematic paths scroll off the display.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin/apply.c           | 66 +++++++++++++++++++++++++++++++++++----\n t/t4108-apply-threeway.sh | 78 +++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 138 insertions(+), 6 deletions(-)\n create mode 100755 t/t4108-apply-threeway.sh\n\ndiff --git a/builtin/apply.c b/builtin/apply.c\nindex 24efb3f..dc52c94 100644\n--- a/builtin/apply.c\n+++ b/builtin/apply.c\n@@ -3288,7 +3288,10 @@ static int try_threeway(struct image *image, struct patch *patch,\n \n \tif (status) {\n \t\tpatch->conflicted_threeway = 1;\n-\t\thashcpy(patch->threeway_stage[0], pre_sha1);\n+\t\tif (patch->is_new)\n+\t\t\thashclr(patch->threeway_stage[0]);\n+\t\telse\n+\t\t\thashcpy(patch->threeway_stage[0], pre_sha1);\n \t\thashcpy(patch->threeway_stage[1], our_sha1);\n \t\thashcpy(patch->threeway_stage[2], post_sha1);\n \t\tfprintf(stderr, \"Applied patch to '%s' with conflicts.\\n\", patch->new_name);\n@@ -3852,6 +3855,32 @@ static void create_one_file(char *path, unsigned mode, const char *buf, unsigned\n \tdie_errno(_(\"unable to write file '%s' mode %o\"), path, mode);\n }\n \n+static void add_conflicted_stages_file(struct patch *patch)\n+{\n+\tint stage, namelen;\n+\tunsigned ce_size, mode;\n+\tstruct cache_entry *ce;\n+\n+\tif (!update_index)\n+\t\treturn;\n+\tnamelen = strlen(patch->new_name);\n+\tce_size = cache_entry_size(namelen);\n+\tmode = patch->new_mode ? patch->new_mode : (S_IFREG | 0644);\n+\n+\tremove_file_from_cache(patch->new_name);\n+\tfor (stage = 1; stage < 4; stage++) {\n+\t\tif (is_null_sha1(patch->threeway_stage[stage - 1]))\n+\t\t\tcontinue;\n+\t\tce = xcalloc(1, ce_size);\n+\t\tmemcpy(ce->name, patch->new_name, namelen);\n+\t\tce->ce_mode = create_ce_mode(mode);\n+\t\tce->ce_flags = create_ce_flags(namelen, stage);\n+\t\thashcpy(ce->sha1, patch->threeway_stage[stage - 1]);\n+\t\tif (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0)\n+\t\t\tdie(_(\"unable to add cache entry for %s\"), patch->new_name);\n+\t}\n+}\n+\n static void create_file(struct patch *patch)\n {\n \tchar *path = patch->new_name;\n@@ -3862,7 +3891,11 @@ static void create_file(struct patch *patch)\n \tif (!mode)\n \t\tmode = S_IFREG | 0644;\n \tcreate_one_file(path, mode, buf, size);\n-\tadd_index_file(path, mode, buf, size);\n+\n+\tif (patch->conflicted_threeway)\n+\t\tadd_conflicted_stages_file(patch);\n+\telse\n+\t\tadd_index_file(path, mode, buf, size);\n }\n \n /* phase zero is to remove, phase one is to create */\n@@ -3964,6 +3997,7 @@ static int write_out_results(struct patch *list)\n \tint phase;\n \tint errs = 0;\n \tstruct patch *l;\n+\tstruct string_list cpath = STRING_LIST_INIT_DUP;\n \n \tfor (phase = 0; phase < 2; phase++) {\n \t\tl = list;\n@@ -3972,12 +4006,28 @@ static int write_out_results(struct patch *list)\n \t\t\t\terrs = 1;\n \t\t\telse {\n \t\t\t\twrite_out_one_result(l, phase);\n-\t\t\t\tif (phase == 1 && write_out_one_reject(l))\n-\t\t\t\t\terrs = 1;\n+\t\t\t\tif (phase == 1) {\n+\t\t\t\t\tif (write_out_one_reject(l))\n+\t\t\t\t\t\terrs = 1;\n+\t\t\t\t\tif (l->conflicted_threeway) {\n+\t\t\t\t\t\tstring_list_append(&cpath, l->new_name);\n+\t\t\t\t\t\terrs = 1;\n+\t\t\t\t\t}\n+\t\t\t\t}\n \t\t\t}\n \t\t\tl = l->next;\n \t\t}\n \t}\n+\n+\tif (cpath.nr) {\n+\t\tstruct string_list_item *item;\n+\n+\t\tsort_string_list(&cpath);\n+\t\tfor_each_string_list_item(item, &cpath)\n+\t\t\tfprintf(stderr, \"U %s\\n\", item->string);\n+\t\tstring_list_clear(&cpath, 0);\n+\t}\n+\n \treturn errs;\n }\n \n@@ -4100,8 +4150,12 @@ static int apply_patch(int fd, const char *filename, int options)\n \t    !apply_with_reject)\n \t\texit(1);\n \n-\tif (apply && write_out_results(list))\n-\t\texit(1);\n+\tif (apply && write_out_results(list)) {\n+\t\tif (apply_with_reject)\n+\t\t\texit(1);\n+\t\t/* with --3way, we still need to write the index out */\n+\t\treturn 1;\n+\t}\n \n \tif (fake_ancestor)\n \t\tbuild_fake_ancestor(list, fake_ancestor);\ndiff --git a/t/t4108-apply-threeway.sh b/t/t4108-apply-threeway.sh\nnew file mode 100755\nindex 0000000..475dfb5\n--- /dev/null\n+++ b/t/t4108-apply-threeway.sh\n@@ -0,0 +1,78 @@\n+#!/bin/sh\n+\n+test_description='git apply --3way'\n+\n+. ./test-lib.sh\n+\n+create_file () {\n+\tfor i\n+\tdo\n+\t\techo \"$i\"\n+\tdone\n+}\n+\n+sanitize_conflicted_diff () {\n+\tsed -e '\n+\t\t/^index /d\n+\t\ts/^\\(+[<>][<>][<>][<>]*\\) .*/\\1/\n+\t'\n+}\n+\n+test_expect_success setup '\n+\ttest_tick &&\n+\tcreate_file >one 1 2 3 4 5 6 7 &&\n+\tcat one >two &&\n+\tgit add one two &&\n+\tgit commit -m initial &&\n+\n+\tgit branch side &&\n+\n+\ttest_tick &&\n+\tcreate_file >one 1 two 3 4 5 six 7 &&\n+\tcreate_file >two 1 two 3 4 5 6 7 &&\n+\tgit commit -a -m master &&\n+\n+\tgit checkout side &&\n+\tcreate_file >one 1 2 3 4 five 6 7 &&\n+\tcreate_file >two 1 2 3 4 five 6 7 &&\n+\tgit commit -a -m side &&\n+\n+\tgit checkout master\n+'\n+\n+test_expect_success 'apply without --3way' '\n+\tgit diff side^ side >P.diff &&\n+\n+\t# should fail to apply\n+\tgit reset --hard &&\n+\tgit checkout master^0 &&\n+\ttest_must_fail git apply --index P.diff &&\n+\t# should leave things intact\n+\tgit diff-files --exit-code &&\n+\tgit diff-index --exit-code --cached HEAD\n+'\n+\n+test_expect_success 'apply with --3way' '\n+\t# Merging side should be similar to applying this patch\n+\tgit diff ...side >P.diff &&\n+\n+\t# The corresponding conflicted merge\n+\tgit reset --hard &&\n+\tgit checkout master^0 &&\n+\ttest_must_fail git merge --no-commit side &&\n+\tgit ls-files -s >expect.ls &&\n+\tgit diff HEAD | sanitize_conflicted_diff >expect.diff &&\n+\n+\t# should fail to apply\n+\tgit reset --hard &&\n+\tgit checkout master^0 &&\n+\ttest_must_fail git apply --index --3way P.diff &&\n+\tgit ls-files -s >actual.ls &&\n+\tgit diff HEAD | sanitize_conflicted_diff >actual.diff &&\n+\n+\t# The result should resemble the corresponding merge\n+\ttest_cmp expect.ls actual.ls &&\n+\ttest_cmp expect.diff actual.diff\n+'\n+\n+test_done\n-- \n1.7.11.1.294.g68a9409\n"},{"id":"194860","messageId":"1341903852-4815-18-git-send-email-gitster@pobox.com","threadId":"30989","inReplyTo":"1341903852-4815-1-git-send-email-gitster@pobox.com","subject":"[PATCH v4 17/19] apply: allow rerere() to work on --3way results","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-07-10T07:04:10Z","receivedAt":"2012-07-10T07:04:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Signed-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin/apply.c           |  3 +++\n t/t4108-apply-threeway.sh | 25 +++++++++++++++++++++++++\n 2 files changed, 28 insertions(+)\n\ndiff --git a/builtin/apply.c b/builtin/apply.c\nindex dc52c94..cd68862 100644\n--- a/builtin/apply.c\n+++ b/builtin/apply.c\n@@ -18,6 +18,7 @@\n #include \"parse-options.h\"\n #include \"xdiff-interface.h\"\n #include \"ll-merge.h\"\n+#include \"rerere.h\"\n \n /*\n  *  --check turns on checking that the working tree matches the\n@@ -4026,6 +4027,8 @@ static int write_out_results(struct patch *list)\n \t\tfor_each_string_list_item(item, &cpath)\n \t\t\tfprintf(stderr, \"U %s\\n\", item->string);\n \t\tstring_list_clear(&cpath, 0);\n+\n+\t\trerere(0);\n \t}\n \n \treturn errs;\ndiff --git a/t/t4108-apply-threeway.sh b/t/t4108-apply-threeway.sh\nindex 475dfb5..e6d4da6 100755\n--- a/t/t4108-apply-threeway.sh\n+++ b/t/t4108-apply-threeway.sh\n@@ -75,4 +75,29 @@ test_expect_success 'apply with --3way' '\n \ttest_cmp expect.diff actual.diff\n '\n \n+test_expect_success 'apply with --3way with rerere enabled' '\n+\tgit config rerere.enabled true &&\n+\n+\t# Merging side should be similar to applying this patch\n+\tgit diff ...side >P.diff &&\n+\n+\t# The corresponding conflicted merge\n+\tgit reset --hard &&\n+\tgit checkout master^0 &&\n+\ttest_must_fail git merge --no-commit side &&\n+\n+\t# Manually resolve and record the resolution\n+\tcreate_file 1 two 3 4 five six 7 >one &&\n+\tgit rerere &&\n+\tcat one >expect &&\n+\n+\t# should fail to apply\n+\tgit reset --hard &&\n+\tgit checkout master^0 &&\n+\ttest_must_fail git apply --index --3way P.diff &&\n+\n+\t# but rerere should have replayed the recorded resolution\n+\ttest_cmp expect one\n+'\n+\n test_done\n-- \n1.7.11.1.294.g68a9409\n"},{"id":"194857","messageId":"1341903852-4815-19-git-send-email-gitster@pobox.com","threadId":"30989","inReplyTo":"1341903852-4815-1-git-send-email-gitster@pobox.com","subject":"[PATCH v4 18/19] apply: document --3way option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-07-10T07:04:11Z","receivedAt":"2012-07-10T07:04:11Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Signed-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/git-apply.txt | 11 ++++++++++-\n 1 file changed, 10 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-apply.txt b/Documentation/git-apply.txt\nindex afd2c9a..634b84e 100644\n--- a/Documentation/git-apply.txt\n+++ b/Documentation/git-apply.txt\n@@ -9,7 +9,7 @@ git-apply - Apply a patch to files and/or to the index\n SYNOPSIS\n --------\n [verse]\n-'git apply' [--stat] [--numstat] [--summary] [--check] [--index]\n+'git apply' [--stat] [--numstat] [--summary] [--check] [--index] [--3way]\n \t  [--apply] [--no-add] [--build-fake-ancestor=<file>] [-R | --reverse]\n \t  [--allow-binary-replacement | --binary] [--reject] [-z]\n \t  [-p<n>] [-C<n>] [--inaccurate-eof] [--recount] [--cached]\n@@ -72,6 +72,15 @@ OPTIONS\n \tcached data, apply the patch, and store the result in the index\n \twithout using the working tree. This implies `--index`.\n \n+-3::\n+--3way::\n+\tWhen the patch does not apply cleanly, fall back on 3-way merge if\n+\tthe patch records the identity of blobs it is supposed to apply to,\n+\tand we have those blobs available locally, possibly leaving the\n+\tconflict markers in the files in the working tree for the user to\n+\tresolve.  This option implies the `--index` option, and is incompatible\n+\twith the `--reject` and the `--cached` options.\n+\n --build-fake-ancestor=<file>::\n \tNewer 'git diff' output has embedded 'index information'\n \tfor each blob to help identify the original version that\n-- \n1.7.11.1.294.g68a9409\n"},{"id":"194859","messageId":"1341903852-4815-20-git-send-email-gitster@pobox.com","threadId":"30989","inReplyTo":"1341903852-4815-1-git-send-email-gitster@pobox.com","subject":"[PATCH v4 19/19] apply: tests for the --3way option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-07-10T07:04:12Z","receivedAt":"2012-07-10T07:04:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Signed-off-by: Junio C Hamano <gitster@pobox.com>\n---\n t/t4108-apply-threeway.sh | 54 +++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 54 insertions(+)\n\ndiff --git a/t/t4108-apply-threeway.sh b/t/t4108-apply-threeway.sh\nindex e6d4da6..fa5d4ef 100755\n--- a/t/t4108-apply-threeway.sh\n+++ b/t/t4108-apply-threeway.sh\n@@ -100,4 +100,58 @@ test_expect_success 'apply with --3way with rerere enabled' '\n \ttest_cmp expect one\n '\n \n+test_expect_success 'apply -3 with add/add conflict setup' '\n+\tgit reset --hard &&\n+\n+\tgit checkout -b adder &&\n+\tcreate_file 1 2 3 4 5 6 7 >three &&\n+\tcreate_file 1 2 3 4 5 6 7 >four &&\n+\tgit add three four &&\n+\tgit commit -m \"add three and four\" &&\n+\n+\tgit checkout -b another adder^ &&\n+\tcreate_file 1 2 3 4 5 6 7 >three &&\n+\tcreate_file 1 2 3 four 5 6 7 >four &&\n+\tgit add three four &&\n+\tgit commit -m \"add three and four\" &&\n+\n+\t# Merging another should be similar to applying this patch\n+\tgit diff adder...another >P.diff &&\n+\n+\tgit checkout adder^0 &&\n+\ttest_must_fail git merge --no-commit another &&\n+\tgit ls-files -s >expect.ls &&\n+\tgit diff HEAD | sanitize_conflicted_diff >expect.diff\n+'\n+\n+test_expect_success 'apply -3 with add/add conflict' '\n+\t# should fail to apply ...\n+\tgit reset --hard &&\n+\tgit checkout adder^0 &&\n+\ttest_must_fail git apply --index --3way P.diff &&\n+\t# ... and leave conflicts in the index and in the working tree\n+\tgit ls-files -s >actual.ls &&\n+\tgit diff HEAD | sanitize_conflicted_diff >actual.diff &&\n+\n+\t# The result should resemble the corresponding merge\n+\ttest_cmp expect.ls actual.ls &&\n+\ttest_cmp expect.diff actual.diff\n+'\n+\n+test_expect_success 'apply -3 with add/add conflict (dirty working tree)' '\n+\t# should fail to apply ...\n+\tgit reset --hard &&\n+\tgit checkout adder^0 &&\n+\techo >>four &&\n+\tcat four >four.save &&\n+\tcat three >three.save &&\n+\tgit ls-files -s >expect.ls &&\n+\ttest_must_fail git apply --index --3way P.diff &&\n+\t# ... and should not touch anything\n+\tgit ls-files -s >actual.ls &&\n+\ttest_cmp expect.ls actual.ls &&\n+\ttest_cmp four.save four &&\n+\ttest_cmp three.save three\n+'\n+\n test_done\n-- \n1.7.11.1.294.g68a9409\n"}]}