{"thread":{"id":"5047","subject":"[PATCH 2/3] read-tree: move merge functions to the library","startedAt":"2006-07-30T18:26:15Z","lastAt":"2006-07-30T18:26:15Z","messageCount":1,"participants":["Johannes Schindelin"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"24381","messageId":"Pine.LNX.4.63.0607302025370.29667@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"5047","inReplyTo":null,"subject":"[PATCH 2/3] read-tree: move merge functions to the library","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-07-30T18:26:15Z","receivedAt":"2006-07-30T18:26:15Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nThis will allow merge-recursive to use the read-tree functionality\nwithout exec()ing git-read-tree.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n\n\tLike the first patch, this is mainly a big code move.\n\n builtin-read-tree.c |  398 --------------------------------------------------\n unpack-trees.c      |  402 +++++++++++++++++++++++++++++++++++++++++++++++++++\n unpack-trees.h      |    5 +\n 3 files changed, 408 insertions(+), 397 deletions(-)\n\ndiff --git a/builtin-read-tree.c b/builtin-read-tree.c\nindex a9852c7..8869ced 100644\n--- a/builtin-read-tree.c\n+++ b/builtin-read-tree.c\n@@ -3,10 +3,8 @@\n  *\n  * Copyright (C) Linus Torvalds, 2005\n  */\n-#define DBRT_DEBUG 1\n \n #include \"cache.h\"\n-\n #include \"object.h\"\n #include \"tree.h\"\n #include \"tree-walk.h\"\n@@ -16,12 +14,6 @@ #include \"builtin.h\"\n \n static struct object_list *trees = NULL;\n \n-static void reject_merge(struct cache_entry *ce)\n-{\n-\tdie(\"Entry '%s' would be overwritten by merge. Cannot merge.\", \n-\t    ce->name);\n-}\n-\n static int list_tree(unsigned char *sha1)\n {\n \tstruct tree *tree = parse_tree_indirect(sha1);\n@@ -31,394 +23,6 @@ static int list_tree(unsigned char *sha1\n \treturn 0;\n }\n \n-static int same(struct cache_entry *a, struct cache_entry *b)\n-{\n-\tif (!!a != !!b)\n-\t\treturn 0;\n-\tif (!a && !b)\n-\t\treturn 1;\n-\treturn a->ce_mode == b->ce_mode && \n-\t\t!memcmp(a->sha1, b->sha1, 20);\n-}\n-\n-\n-/*\n- * When a CE gets turned into an unmerged entry, we\n- * want it to be up-to-date\n- */\n-static void verify_uptodate(struct cache_entry *ce,\n-\t\tstruct unpack_trees_options *o)\n-{\n-\tstruct stat st;\n-\n-\tif (o->index_only || o->reset)\n-\t\treturn;\n-\n-\tif (!lstat(ce->name, &st)) {\n-\t\tunsigned changed = ce_match_stat(ce, &st, 1);\n-\t\tif (!changed)\n-\t\t\treturn;\n-\t\terrno = 0;\n-\t}\n-\tif (o->reset) {\n-\t\tce->ce_flags |= htons(CE_UPDATE);\n-\t\treturn;\n-\t}\n-\tif (errno == ENOENT)\n-\t\treturn;\n-\tdie(\"Entry '%s' not uptodate. Cannot merge.\", ce->name);\n-}\n-\n-static void invalidate_ce_path(struct cache_entry *ce)\n-{\n-\tif (ce)\n-\t\tcache_tree_invalidate_path(active_cache_tree, ce->name);\n-}\n-\n-/*\n- * We do not want to remove or overwrite a working tree file that\n- * is not tracked.\n- */\n-static void verify_absent(const char *path, const char *action,\n-\t\tstruct unpack_trees_options *o)\n-{\n-\tstruct stat st;\n-\n-\tif (o->index_only || o->reset || !o->update)\n-\t\treturn;\n-\tif (!lstat(path, &st))\n-\t\tdie(\"Untracked working tree file '%s' \"\n-\t\t    \"would be %s by merge.\", path, action);\n-}\n-\n-static int merged_entry(struct cache_entry *merge, struct cache_entry *old,\n-\t\tstruct unpack_trees_options *o)\n-{\n-\tmerge->ce_flags |= htons(CE_UPDATE);\n-\tif (old) {\n-\t\t/*\n-\t\t * See if we can re-use the old CE directly?\n-\t\t * That way we get the uptodate stat info.\n-\t\t *\n-\t\t * This also removes the UPDATE flag on\n-\t\t * a match.\n-\t\t */\n-\t\tif (same(old, merge)) {\n-\t\t\t*merge = *old;\n-\t\t} else {\n-\t\t\tverify_uptodate(old, o);\n-\t\t\tinvalidate_ce_path(old);\n-\t\t}\n-\t}\n-\telse {\n-\t\tverify_absent(merge->name, \"overwritten\", o);\n-\t\tinvalidate_ce_path(merge);\n-\t}\n-\n-\tmerge->ce_flags &= ~htons(CE_STAGEMASK);\n-\tadd_cache_entry(merge, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);\n-\treturn 1;\n-}\n-\n-static int deleted_entry(struct cache_entry *ce, struct cache_entry *old,\n-\t\tstruct unpack_trees_options *o)\n-{\n-\tif (old)\n-\t\tverify_uptodate(old, o);\n-\telse\n-\t\tverify_absent(ce->name, \"removed\", o);\n-\tce->ce_mode = 0;\n-\tadd_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);\n-\tinvalidate_ce_path(ce);\n-\treturn 1;\n-}\n-\n-static int keep_entry(struct cache_entry *ce)\n-{\n-\tadd_cache_entry(ce, ADD_CACHE_OK_TO_ADD);\n-\treturn 1;\n-}\n-\n-#if DBRT_DEBUG\n-static void show_stage_entry(FILE *o,\n-\t\t\t     const char *label, const struct cache_entry *ce)\n-{\n-\tif (!ce)\n-\t\tfprintf(o, \"%s (missing)\\n\", label);\n-\telse\n-\t\tfprintf(o, \"%s%06o %s %d\\t%s\\n\",\n-\t\t\tlabel,\n-\t\t\tntohl(ce->ce_mode),\n-\t\t\tsha1_to_hex(ce->sha1),\n-\t\t\tce_stage(ce),\n-\t\t\tce->name);\n-}\n-#endif\n-\n-static int threeway_merge(struct cache_entry **stages,\n-\t\tstruct unpack_trees_options *o)\n-{\n-\tstruct cache_entry *index;\n-\tstruct cache_entry *head; \n-\tstruct cache_entry *remote = stages[o->head_idx + 1];\n-\tint count;\n-\tint head_match = 0;\n-\tint remote_match = 0;\n-\tconst char *path = NULL;\n-\n-\tint df_conflict_head = 0;\n-\tint df_conflict_remote = 0;\n-\n-\tint any_anc_missing = 0;\n-\tint no_anc_exists = 1;\n-\tint i;\n-\n-\tfor (i = 1; i < o->head_idx; i++) {\n-\t\tif (!stages[i])\n-\t\t\tany_anc_missing = 1;\n-\t\telse {\n-\t\t\tif (!path)\n-\t\t\t\tpath = stages[i]->name;\n-\t\t\tno_anc_exists = 0;\n-\t\t}\n-\t}\n-\n-\tindex = stages[0];\n-\thead = stages[o->head_idx];\n-\n-\tif (head == &o->df_conflict_entry) {\n-\t\tdf_conflict_head = 1;\n-\t\thead = NULL;\n-\t}\n-\n-\tif (remote == &o->df_conflict_entry) {\n-\t\tdf_conflict_remote = 1;\n-\t\tremote = NULL;\n-\t}\n-\n-\tif (!path && index)\n-\t\tpath = index->name;\n-\tif (!path && head)\n-\t\tpath = head->name;\n-\tif (!path && remote)\n-\t\tpath = remote->name;\n-\n-\t/* First, if there's a #16 situation, note that to prevent #13\n-\t * and #14.\n-\t */\n-\tif (!same(remote, head)) {\n-\t\tfor (i = 1; i < o->head_idx; i++) {\n-\t\t\tif (same(stages[i], head)) {\n-\t\t\t\thead_match = i;\n-\t\t\t}\n-\t\t\tif (same(stages[i], remote)) {\n-\t\t\t\tremote_match = i;\n-\t\t\t}\n-\t\t}\n-\t}\n-\n-\t/* We start with cases where the index is allowed to match\n-\t * something other than the head: #14(ALT) and #2ALT, where it\n-\t * is permitted to match the result instead.\n-\t */\n-\t/* #14, #14ALT, #2ALT */\n-\tif (remote && !df_conflict_head && head_match && !remote_match) {\n-\t\tif (index && !same(index, remote) && !same(index, head))\n-\t\t\treject_merge(index);\n-\t\treturn merged_entry(remote, index, o);\n-\t}\n-\t/*\n-\t * If we have an entry in the index cache, then we want to\n-\t * make sure that it matches head.\n-\t */\n-\tif (index && !same(index, head)) {\n-\t\treject_merge(index);\n-\t}\n-\n-\tif (head) {\n-\t\t/* #5ALT, #15 */\n-\t\tif (same(head, remote))\n-\t\t\treturn merged_entry(head, index, o);\n-\t\t/* #13, #3ALT */\n-\t\tif (!df_conflict_remote && remote_match && !head_match)\n-\t\t\treturn merged_entry(head, index, o);\n-\t}\n-\n-\t/* #1 */\n-\tif (!head && !remote && any_anc_missing)\n-\t\treturn 0;\n-\n-\t/* Under the new \"aggressive\" rule, we resolve mostly trivial\n-\t * cases that we historically had git-merge-one-file resolve.\n-\t */\n-\tif (o->aggressive) {\n-\t\tint head_deleted = !head && !df_conflict_head;\n-\t\tint remote_deleted = !remote && !df_conflict_remote;\n-\t\t/*\n-\t\t * Deleted in both.\n-\t\t * Deleted in one and unchanged in the other.\n-\t\t */\n-\t\tif ((head_deleted && remote_deleted) ||\n-\t\t    (head_deleted && remote && remote_match) ||\n-\t\t    (remote_deleted && head && head_match)) {\n-\t\t\tif (index)\n-\t\t\t\treturn deleted_entry(index, index, o);\n-\t\t\telse if (path)\n-\t\t\t\tverify_absent(path, \"removed\", o);\n-\t\t\treturn 0;\n-\t\t}\n-\t\t/*\n-\t\t * Added in both, identically.\n-\t\t */\n-\t\tif (no_anc_exists && head && remote && same(head, remote))\n-\t\t\treturn merged_entry(head, index, o);\n-\n-\t}\n-\n-\t/* Below are \"no merge\" cases, which require that the index be\n-\t * up-to-date to avoid the files getting overwritten with\n-\t * conflict resolution files. \n-\t */\n-\tif (index) {\n-\t\tverify_uptodate(index, o);\n-\t}\n-\telse if (path)\n-\t\tverify_absent(path, \"overwritten\", o);\n-\n-\to->nontrivial_merge = 1;\n-\n-\t/* #2, #3, #4, #6, #7, #9, #11. */\n-\tcount = 0;\n-\tif (!head_match || !remote_match) {\n-\t\tfor (i = 1; i < o->head_idx; i++) {\n-\t\t\tif (stages[i]) {\n-\t\t\t\tkeep_entry(stages[i]);\n-\t\t\t\tcount++;\n-\t\t\t\tbreak;\n-\t\t\t}\n-\t\t}\n-\t}\n-#if DBRT_DEBUG\n-\telse {\n-\t\tfprintf(stderr, \"read-tree: warning #16 detected\\n\");\n-\t\tshow_stage_entry(stderr, \"head   \", stages[head_match]);\n-\t\tshow_stage_entry(stderr, \"remote \", stages[remote_match]);\n-\t}\n-#endif\n-\tif (head) { count += keep_entry(head); }\n-\tif (remote) { count += keep_entry(remote); }\n-\treturn count;\n-}\n-\n-/*\n- * Two-way merge.\n- *\n- * The rule is to \"carry forward\" what is in the index without losing\n- * information across a \"fast forward\", favoring a successful merge\n- * over a merge failure when it makes sense.  For details of the\n- * \"carry forward\" rule, please see <Documentation/git-read-tree.txt>.\n- *\n- */\n-static int twoway_merge(struct cache_entry **src,\n-\t\tstruct unpack_trees_options *o)\n-{\n-\tstruct cache_entry *current = src[0];\n-\tstruct cache_entry *oldtree = src[1], *newtree = src[2];\n-\n-\tif (o->merge_size != 2)\n-\t\treturn error(\"Cannot do a twoway merge of %d trees\",\n-\t\t\t     o->merge_size);\n-\n-\tif (current) {\n-\t\tif ((!oldtree && !newtree) || /* 4 and 5 */\n-\t\t    (!oldtree && newtree &&\n-\t\t     same(current, newtree)) || /* 6 and 7 */\n-\t\t    (oldtree && newtree &&\n-\t\t     same(oldtree, newtree)) || /* 14 and 15 */\n-\t\t    (oldtree && newtree &&\n-\t\t     !same(oldtree, newtree) && /* 18 and 19*/\n-\t\t     same(current, newtree))) {\n-\t\t\treturn keep_entry(current);\n-\t\t}\n-\t\telse if (oldtree && !newtree && same(current, oldtree)) {\n-\t\t\t/* 10 or 11 */\n-\t\t\treturn deleted_entry(oldtree, current, o);\n-\t\t}\n-\t\telse if (oldtree && newtree &&\n-\t\t\t same(current, oldtree) && !same(current, newtree)) {\n-\t\t\t/* 20 or 21 */\n-\t\t\treturn merged_entry(newtree, current, o);\n-\t\t}\n-\t\telse {\n-\t\t\t/* all other failures */\n-\t\t\tif (oldtree)\n-\t\t\t\treject_merge(oldtree);\n-\t\t\tif (current)\n-\t\t\t\treject_merge(current);\n-\t\t\tif (newtree)\n-\t\t\t\treject_merge(newtree);\n-\t\t\treturn -1;\n-\t\t}\n-\t}\n-\telse if (newtree)\n-\t\treturn merged_entry(newtree, current, o);\n-\telse\n-\t\treturn deleted_entry(oldtree, current, o);\n-}\n-\n-/*\n- * Bind merge.\n- *\n- * Keep the index entries at stage0, collapse stage1 but make sure\n- * stage0 does not have anything there.\n- */\n-static int bind_merge(struct cache_entry **src,\n-\t\tstruct unpack_trees_options *o)\n-{\n-\tstruct cache_entry *old = src[0];\n-\tstruct cache_entry *a = src[1];\n-\n-\tif (o->merge_size != 1)\n-\t\treturn error(\"Cannot do a bind merge of %d trees\\n\",\n-\t\t\t     o->merge_size);\n-\tif (a && old)\n-\t\tdie(\"Entry '%s' overlaps.  Cannot bind.\", a->name);\n-\tif (!a)\n-\t\treturn keep_entry(old);\n-\telse\n-\t\treturn merged_entry(a, NULL, o);\n-}\n-\n-/*\n- * One-way merge.\n- *\n- * The rule is:\n- * - take the stat information from stage0, take the data from stage1\n- */\n-static int oneway_merge(struct cache_entry **src,\n-\t\tstruct unpack_trees_options *o)\n-{\n-\tstruct cache_entry *old = src[0];\n-\tstruct cache_entry *a = src[1];\n-\n-\tif (o->merge_size != 1)\n-\t\treturn error(\"Cannot do a oneway merge of %d trees\",\n-\t\t\t     o->merge_size);\n-\n-\tif (!a)\n-\t\treturn deleted_entry(old, old, o);\n-\tif (old && same(old, a)) {\n-\t\tif (o->reset) {\n-\t\t\tstruct stat st;\n-\t\t\tif (lstat(old->name, &st) ||\n-\t\t\t    ce_match_stat(old, &st, 1))\n-\t\t\t\told->ce_flags |= htons(CE_UPDATE);\n-\t\t}\n-\t\treturn keep_entry(old);\n-\t}\n-\treturn merged_entry(a, old, o);\n-}\n-\n static int read_cache_unmerged(void)\n {\n \tint i;\n@@ -432,7 +36,7 @@ static int read_cache_unmerged(void)\n \t\tif (ce_stage(ce)) {\n \t\t\tif (last && !strcmp(ce->name, last->name))\n \t\t\t\tcontinue;\n-\t\t\tinvalidate_ce_path(ce);\n+\t\t\tcache_tree_invalidate_path(active_cache_tree, ce->name);\n \t\t\tlast = ce;\n \t\t\tce->ce_mode = 0;\n \t\t\tce->ce_flags &= ~htons(CE_STAGEMASK);\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex ecff92d..b8a355f 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -3,8 +3,11 @@ #include <sys/time.h>\n #include \"cache.h\"\n #include \"tree.h\"\n #include \"tree-walk.h\"\n+#include \"cache-tree.h\"\n #include \"unpack-trees.h\"\n \n+#define DBRT_DEBUG 1\n+\n struct tree_entry_list {\n \tstruct tree_entry_list *next;\n \tunsigned directory : 1;\n@@ -22,6 +25,9 @@ static struct tree_entry_list *create_tr\n \tstruct tree_entry_list *ret = NULL;\n \tstruct tree_entry_list **list_p = &ret;\n \n+\tif (!tree->object.parsed)\n+\t\tparse_tree(tree);\n+\n \tdesc.buf = tree->buffer;\n \tdesc.size = tree->size;\n \n@@ -365,7 +371,9 @@ int unpack_trees(struct object_list *tre\n \tstruct object_list *posn = trees;\n \tstruct tree_entry_list df_conflict_list;\n \n+\tmemset(&df_conflict_list, 0, sizeof(df_conflict_list));\n \tdf_conflict_list.next = &df_conflict_list;\n+\tmemset(&state, 0, sizeof(state));\n \tstate.base_dir = \"\";\n \tstate.force = 1;\n \tstate.quiet = 1;\n@@ -391,4 +399,398 @@ int unpack_trees(struct object_list *tre\n \treturn 0;\n }\n \n+/* Here come the merge functions */\n+\n+static void reject_merge(struct cache_entry *ce)\n+{\n+\tdie(\"Entry '%s' would be overwritten by merge. Cannot merge.\", \n+\t    ce->name);\n+}\n+\n+static int same(struct cache_entry *a, struct cache_entry *b)\n+{\n+\tif (!!a != !!b)\n+\t\treturn 0;\n+\tif (!a && !b)\n+\t\treturn 1;\n+\treturn a->ce_mode == b->ce_mode && \n+\t\t!memcmp(a->sha1, b->sha1, 20);\n+}\n+\n+\n+/*\n+ * When a CE gets turned into an unmerged entry, we\n+ * want it to be up-to-date\n+ */\n+static void verify_uptodate(struct cache_entry *ce,\n+\t\tstruct unpack_trees_options *o)\n+{\n+\tstruct stat st;\n+\n+\tif (o->index_only || o->reset)\n+\t\treturn;\n+\n+\tif (!lstat(ce->name, &st)) {\n+\t\tunsigned changed = ce_match_stat(ce, &st, 1);\n+\t\tif (!changed)\n+\t\t\treturn;\n+\t\terrno = 0;\n+\t}\n+\tif (o->reset) {\n+\t\tce->ce_flags |= htons(CE_UPDATE);\n+\t\treturn;\n+\t}\n+\tif (errno == ENOENT)\n+\t\treturn;\n+\tdie(\"Entry '%s' not uptodate. Cannot merge.\", ce->name);\n+}\n+\n+static void invalidate_ce_path(struct cache_entry *ce)\n+{\n+\tif (ce)\n+\t\tcache_tree_invalidate_path(active_cache_tree, ce->name);\n+}\n+\n+/*\n+ * We do not want to remove or overwrite a working tree file that\n+ * is not tracked.\n+ */\n+static void verify_absent(const char *path, const char *action,\n+\t\tstruct unpack_trees_options *o)\n+{\n+\tstruct stat st;\n+\n+\tif (o->index_only || o->reset || !o->update)\n+\t\treturn;\n+\tif (!lstat(path, &st))\n+\t\tdie(\"Untracked working tree file '%s' \"\n+\t\t    \"would be %s by merge.\", path, action);\n+}\n+\n+static int merged_entry(struct cache_entry *merge, struct cache_entry *old,\n+\t\tstruct unpack_trees_options *o)\n+{\n+\tmerge->ce_flags |= htons(CE_UPDATE);\n+\tif (old) {\n+\t\t/*\n+\t\t * See if we can re-use the old CE directly?\n+\t\t * That way we get the uptodate stat info.\n+\t\t *\n+\t\t * This also removes the UPDATE flag on\n+\t\t * a match.\n+\t\t */\n+\t\tif (same(old, merge)) {\n+\t\t\t*merge = *old;\n+\t\t} else {\n+\t\t\tverify_uptodate(old, o);\n+\t\t\tinvalidate_ce_path(old);\n+\t\t}\n+\t}\n+\telse {\n+\t\tverify_absent(merge->name, \"overwritten\", o);\n+\t\tinvalidate_ce_path(merge);\n+\t}\n+\n+\tmerge->ce_flags &= ~htons(CE_STAGEMASK);\n+\tadd_cache_entry(merge, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);\n+\treturn 1;\n+}\n+\n+static int deleted_entry(struct cache_entry *ce, struct cache_entry *old,\n+\t\tstruct unpack_trees_options *o)\n+{\n+\tif (old)\n+\t\tverify_uptodate(old, o);\n+\telse\n+\t\tverify_absent(ce->name, \"removed\", o);\n+\tce->ce_mode = 0;\n+\tadd_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);\n+\tinvalidate_ce_path(ce);\n+\treturn 1;\n+}\n+\n+static int keep_entry(struct cache_entry *ce)\n+{\n+\tadd_cache_entry(ce, ADD_CACHE_OK_TO_ADD);\n+\treturn 1;\n+}\n+\n+#if DBRT_DEBUG\n+static void show_stage_entry(FILE *o,\n+\t\t\t     const char *label, const struct cache_entry *ce)\n+{\n+\tif (!ce)\n+\t\tfprintf(o, \"%s (missing)\\n\", label);\n+\telse\n+\t\tfprintf(o, \"%s%06o %s %d\\t%s\\n\",\n+\t\t\tlabel,\n+\t\t\tntohl(ce->ce_mode),\n+\t\t\tsha1_to_hex(ce->sha1),\n+\t\t\tce_stage(ce),\n+\t\t\tce->name);\n+}\n+#endif\n+\n+int threeway_merge(struct cache_entry **stages,\n+\t\tstruct unpack_trees_options *o)\n+{\n+\tstruct cache_entry *index;\n+\tstruct cache_entry *head; \n+\tstruct cache_entry *remote = stages[o->head_idx + 1];\n+\tint count;\n+\tint head_match = 0;\n+\tint remote_match = 0;\n+\tconst char *path = NULL;\n+\n+\tint df_conflict_head = 0;\n+\tint df_conflict_remote = 0;\n+\n+\tint any_anc_missing = 0;\n+\tint no_anc_exists = 1;\n+\tint i;\n \n+\tfor (i = 1; i < o->head_idx; i++) {\n+\t\tif (!stages[i])\n+\t\t\tany_anc_missing = 1;\n+\t\telse {\n+\t\t\tif (!path)\n+\t\t\t\tpath = stages[i]->name;\n+\t\t\tno_anc_exists = 0;\n+\t\t}\n+\t}\n+\n+\tindex = stages[0];\n+\thead = stages[o->head_idx];\n+\n+\tif (head == &o->df_conflict_entry) {\n+\t\tdf_conflict_head = 1;\n+\t\thead = NULL;\n+\t}\n+\n+\tif (remote == &o->df_conflict_entry) {\n+\t\tdf_conflict_remote = 1;\n+\t\tremote = NULL;\n+\t}\n+\n+\tif (!path && index)\n+\t\tpath = index->name;\n+\tif (!path && head)\n+\t\tpath = head->name;\n+\tif (!path && remote)\n+\t\tpath = remote->name;\n+\n+\t/* First, if there's a #16 situation, note that to prevent #13\n+\t * and #14.\n+\t */\n+\tif (!same(remote, head)) {\n+\t\tfor (i = 1; i < o->head_idx; i++) {\n+\t\t\tif (same(stages[i], head)) {\n+\t\t\t\thead_match = i;\n+\t\t\t}\n+\t\t\tif (same(stages[i], remote)) {\n+\t\t\t\tremote_match = i;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\t/* We start with cases where the index is allowed to match\n+\t * something other than the head: #14(ALT) and #2ALT, where it\n+\t * is permitted to match the result instead.\n+\t */\n+\t/* #14, #14ALT, #2ALT */\n+\tif (remote && !df_conflict_head && head_match && !remote_match) {\n+\t\tif (index && !same(index, remote) && !same(index, head))\n+\t\t\treject_merge(index);\n+\t\treturn merged_entry(remote, index, o);\n+\t}\n+\t/*\n+\t * If we have an entry in the index cache, then we want to\n+\t * make sure that it matches head.\n+\t */\n+\tif (index && !same(index, head)) {\n+\t\treject_merge(index);\n+\t}\n+\n+\tif (head) {\n+\t\t/* #5ALT, #15 */\n+\t\tif (same(head, remote))\n+\t\t\treturn merged_entry(head, index, o);\n+\t\t/* #13, #3ALT */\n+\t\tif (!df_conflict_remote && remote_match && !head_match)\n+\t\t\treturn merged_entry(head, index, o);\n+\t}\n+\n+\t/* #1 */\n+\tif (!head && !remote && any_anc_missing)\n+\t\treturn 0;\n+\n+\t/* Under the new \"aggressive\" rule, we resolve mostly trivial\n+\t * cases that we historically had git-merge-one-file resolve.\n+\t */\n+\tif (o->aggressive) {\n+\t\tint head_deleted = !head && !df_conflict_head;\n+\t\tint remote_deleted = !remote && !df_conflict_remote;\n+\t\t/*\n+\t\t * Deleted in both.\n+\t\t * Deleted in one and unchanged in the other.\n+\t\t */\n+\t\tif ((head_deleted && remote_deleted) ||\n+\t\t    (head_deleted && remote && remote_match) ||\n+\t\t    (remote_deleted && head && head_match)) {\n+\t\t\tif (index)\n+\t\t\t\treturn deleted_entry(index, index, o);\n+\t\t\telse if (path)\n+\t\t\t\tverify_absent(path, \"removed\", o);\n+\t\t\treturn 0;\n+\t\t}\n+\t\t/*\n+\t\t * Added in both, identically.\n+\t\t */\n+\t\tif (no_anc_exists && head && remote && same(head, remote))\n+\t\t\treturn merged_entry(head, index, o);\n+\n+\t}\n+\n+\t/* Below are \"no merge\" cases, which require that the index be\n+\t * up-to-date to avoid the files getting overwritten with\n+\t * conflict resolution files. \n+\t */\n+\tif (index) {\n+\t\tverify_uptodate(index, o);\n+\t}\n+\telse if (path)\n+\t\tverify_absent(path, \"overwritten\", o);\n+\n+\to->nontrivial_merge = 1;\n+\n+\t/* #2, #3, #4, #6, #7, #9, #11. */\n+\tcount = 0;\n+\tif (!head_match || !remote_match) {\n+\t\tfor (i = 1; i < o->head_idx; i++) {\n+\t\t\tif (stages[i]) {\n+\t\t\t\tkeep_entry(stages[i]);\n+\t\t\t\tcount++;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t}\n+#if DBRT_DEBUG\n+\telse {\n+\t\tfprintf(stderr, \"read-tree: warning #16 detected\\n\");\n+\t\tshow_stage_entry(stderr, \"head   \", stages[head_match]);\n+\t\tshow_stage_entry(stderr, \"remote \", stages[remote_match]);\n+\t}\n+#endif\n+\tif (head) { count += keep_entry(head); }\n+\tif (remote) { count += keep_entry(remote); }\n+\treturn count;\n+}\n+\n+/*\n+ * Two-way merge.\n+ *\n+ * The rule is to \"carry forward\" what is in the index without losing\n+ * information across a \"fast forward\", favoring a successful merge\n+ * over a merge failure when it makes sense.  For details of the\n+ * \"carry forward\" rule, please see <Documentation/git-read-tree.txt>.\n+ *\n+ */\n+int twoway_merge(struct cache_entry **src,\n+\t\tstruct unpack_trees_options *o)\n+{\n+\tstruct cache_entry *current = src[0];\n+\tstruct cache_entry *oldtree = src[1], *newtree = src[2];\n+\n+\tif (o->merge_size != 2)\n+\t\treturn error(\"Cannot do a twoway merge of %d trees\",\n+\t\t\t     o->merge_size);\n+\n+\tif (current) {\n+\t\tif ((!oldtree && !newtree) || /* 4 and 5 */\n+\t\t    (!oldtree && newtree &&\n+\t\t     same(current, newtree)) || /* 6 and 7 */\n+\t\t    (oldtree && newtree &&\n+\t\t     same(oldtree, newtree)) || /* 14 and 15 */\n+\t\t    (oldtree && newtree &&\n+\t\t     !same(oldtree, newtree) && /* 18 and 19*/\n+\t\t     same(current, newtree))) {\n+\t\t\treturn keep_entry(current);\n+\t\t}\n+\t\telse if (oldtree && !newtree && same(current, oldtree)) {\n+\t\t\t/* 10 or 11 */\n+\t\t\treturn deleted_entry(oldtree, current, o);\n+\t\t}\n+\t\telse if (oldtree && newtree &&\n+\t\t\t same(current, oldtree) && !same(current, newtree)) {\n+\t\t\t/* 20 or 21 */\n+\t\t\treturn merged_entry(newtree, current, o);\n+\t\t}\n+\t\telse {\n+\t\t\t/* all other failures */\n+\t\t\tif (oldtree)\n+\t\t\t\treject_merge(oldtree);\n+\t\t\tif (current)\n+\t\t\t\treject_merge(current);\n+\t\t\tif (newtree)\n+\t\t\t\treject_merge(newtree);\n+\t\t\treturn -1;\n+\t\t}\n+\t}\n+\telse if (newtree)\n+\t\treturn merged_entry(newtree, current, o);\n+\telse\n+\t\treturn deleted_entry(oldtree, current, o);\n+}\n+\n+/*\n+ * Bind merge.\n+ *\n+ * Keep the index entries at stage0, collapse stage1 but make sure\n+ * stage0 does not have anything there.\n+ */\n+int bind_merge(struct cache_entry **src,\n+\t\tstruct unpack_trees_options *o)\n+{\n+\tstruct cache_entry *old = src[0];\n+\tstruct cache_entry *a = src[1];\n+\n+\tif (o->merge_size != 1)\n+\t\treturn error(\"Cannot do a bind merge of %d trees\\n\",\n+\t\t\t     o->merge_size);\n+\tif (a && old)\n+\t\tdie(\"Entry '%s' overlaps.  Cannot bind.\", a->name);\n+\tif (!a)\n+\t\treturn keep_entry(old);\n+\telse\n+\t\treturn merged_entry(a, NULL, o);\n+}\n+\n+/*\n+ * One-way merge.\n+ *\n+ * The rule is:\n+ * - take the stat information from stage0, take the data from stage1\n+ */\n+int oneway_merge(struct cache_entry **src,\n+\t\tstruct unpack_trees_options *o)\n+{\n+\tstruct cache_entry *old = src[0];\n+\tstruct cache_entry *a = src[1];\n+\n+\tif (o->merge_size != 1)\n+\t\treturn error(\"Cannot do a oneway merge of %d trees\",\n+\t\t\t     o->merge_size);\n+\n+\tif (!a)\n+\t\treturn deleted_entry(old, old, o);\n+\tif (old && same(old, a)) {\n+\t\tif (o->reset) {\n+\t\t\tstruct stat st;\n+\t\t\tif (lstat(old->name, &st) ||\n+\t\t\t    ce_match_stat(old, &st, 1))\n+\t\t\t\told->ce_flags |= htons(CE_UPDATE);\n+\t\t}\n+\t\treturn keep_entry(old);\n+\t}\n+\treturn merged_entry(a, old, o);\n+}\ndiff --git a/unpack-trees.h b/unpack-trees.h\nindex babfcd7..826b263 100644\n--- a/unpack-trees.h\n+++ b/unpack-trees.h\n@@ -27,4 +27,9 @@ struct unpack_trees_options {\n extern int unpack_trees(struct object_list *trees,\n \t\tstruct unpack_trees_options *options);\n \n+int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o);\n+int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o);\n+int bind_merge(struct cache_entry **src, struct unpack_trees_options *o);\n+int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o);\n+\n #endif\n-- \n1.4.2.rc2.g76b2-dirty\n"}]}