{"thread":{"id":"1703","subject":"[PATCH] Change read-tree to merge before using the index.","startedAt":"2005-08-31T03:48:27Z","lastAt":"2005-09-05T02:55:43Z","messageCount":11,"participants":["Daniel Barkalow","Junio C Hamano","Catalin Marinas"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"7964","messageId":"Pine.LNX.4.63.0508302317380.23242@iabervon.org","threadId":"1703","inReplyTo":null,"subject":"[PATCH 0/2] Reorganize read-tree","fromName":"Daniel Barkalow","fromEmail":"barkalow@iabervon.org","sentAt":"2005-08-31T03:48:27Z","receivedAt":"2005-08-31T03:48:27Z","isPatch":true,"sender":{"key":"barkalow@iabervon.org","avatar":"https://avatars.githubusercontent.com/u/55364219?v=4"},"body":"I got mostly done with this before Linus mentioned the possibility of\nhaving multiple index entries in the same stage for a single path. I\nfinished it anyway, but I'm not sure that we won't want to know which of\nthe common ancestors contributed which, and, if some of them don't have a\npath, we wouldn't be able to tell. The other advantages I see to this\napproach are:\n\n - it uses the more common parser of tree objects, moving toward having\n   only one (diff-cache still uses read_tree(), however).\n - it doesn't need to do very complicated things with the index; the\n   original read-tree does a bunch of stuff with an index with a gap in\n   the middle containing obsolete entries.\n - it uses a much simpler method of finding directory/file conflicts,\n   which is possible because the struct trees represent directories as\n   well as files.\n - it deals with each path completely before going on to the next one,\n   instead of first dealing with each input tree and then dealing with\n   each path.\n - it removes a lot of intimate knowledge of the index structure from the\n   program.\n\nThe general idea is that it figures out what trees you want, and then\niterates through the entry lists together, recursing into directories, and\ncalls the merge function with an array of the index entries (not yet\nadded) for the path in each tree; the merge function adds the appropriate\nthings to the index.\n\nNote that this set doesn't include calling merge functions with multiple\nancestors or remotes; that can be done when we've decided on whether my\nversion of read-tree is worth using.\n\nThere are various potential refinements, plus removing a bunch of memory\nleaks, still to do, but I think this is sufficiently close to review.\n\n(Refinements: it ought to have two indices in memory, the old and the new,\nand never modify the old and only append to the new, to simplify things\nfurther; it ought to use a sentinal value for the index entry to indicate\nthat there is something in the tree to conflict with there being a file at\nthe given path; the --emu23 logic could be clearer)\n\nThe first patch adds a few functions to the object library.\nThe second patch changes read-tree around; It is essentially a rewrite,\nexcept for the merge functions and main().\n\n\t-Daniel\n*This .sig left intentionally blank*\n"},{"id":"7965","messageId":"Pine.LNX.4.63.0508302348350.23242@iabervon.org","threadId":"1703","inReplyTo":"Pine.LNX.4.63.0508302317380.23242@iabervon.org","subject":"[PATCH 1/2] Object model additions for read-tree","fromName":"Daniel Barkalow","fromEmail":"barkalow@iabervon.org","sentAt":"2005-08-31T03:49:10Z","receivedAt":"2005-08-31T03:49:10Z","isPatch":true,"sender":{"key":"barkalow@iabervon.org","avatar":"https://avatars.githubusercontent.com/u/55364219?v=4"},"body":"Adds object_list_append() and a function to get the struct tree from an ent.\n\nSigned-off-by: Daniel Barkalow <barkalow@iabervon.org>\n---\n\n object.c |   11 +++++++++++\n object.h |    3 +++\n tree.c   |   19 +++++++++++++++++++\n tree.h   |    3 +++\n 4 files changed, 36 insertions(+), 0 deletions(-)\n\n49d33c385aa69d17c991300f73e77c6718a2b4a6\ndiff --git a/object.c b/object.c\n--- a/object.c\n+++ b/object.c\n@@ -184,6 +184,17 @@ struct object_list *object_list_insert(s\n         return new_list;\n }\n\n+void object_list_append(struct object *item,\n+\t\t\tstruct object_list **list_p)\n+{\n+\twhile (*list_p) {\n+\t\tlist_p = &((*list_p)->next);\n+\t}\n+\t*list_p = xmalloc(sizeof(struct object_list));\n+\t(*list_p)->next = NULL;\n+\t(*list_p)->item = item;\n+}\n+\n unsigned object_list_length(struct object_list *list)\n {\n \tunsigned ret = 0;\ndiff --git a/object.h b/object.h\n--- a/object.h\n+++ b/object.h\n@@ -41,6 +41,9 @@ void mark_reachable(struct object *obj,\n struct object_list *object_list_insert(struct object *item,\n \t\t\t\t       struct object_list **list_p);\n\n+void object_list_append(struct object *item,\n+\t\t\tstruct object_list **list_p);\n+\n unsigned object_list_length(struct object_list *list);\n\n int object_list_contains(struct object_list *list, struct object *obj);\ndiff --git a/tree.c b/tree.c\n--- a/tree.c\n+++ b/tree.c\n@@ -1,5 +1,7 @@\n #include \"tree.h\"\n #include \"blob.h\"\n+#include \"commit.h\"\n+#include \"tag.h\"\n #include \"cache.h\"\n #include <stdlib.h>\n\n@@ -212,3 +214,20 @@ int parse_tree(struct tree *item)\n \tfree(buffer);\n \treturn ret;\n }\n+\n+struct tree *parse_tree_indirect(const unsigned char *sha1)\n+{\n+\tstruct object *obj = parse_object(sha1);\n+\tdo {\n+\t\tif (!obj)\n+\t\t\treturn NULL;\n+\t\tif (obj->type == tree_type)\n+\t\t\treturn (struct tree *) obj;\n+\t\telse if (obj->type == commit_type)\n+\t\t\treturn ((struct commit *) obj)->tree;\n+\t\telse if (obj->type == tag_type)\n+\t\t\tobj = ((struct tag *) obj)->tagged;\n+\t\telse\n+\t\t\treturn NULL;\n+\t} while (1);\n+}\ndiff --git a/tree.h b/tree.h\n--- a/tree.h\n+++ b/tree.h\n@@ -32,4 +32,7 @@ int parse_tree_buffer(struct tree *item,\n\n int parse_tree(struct tree *tree);\n\n+/* Parses and returns the tree in the given ent, chasing tags and commits. */\n+struct tree *parse_tree_indirect(const unsigned char *sha1);\n+\n #endif /* TREE_H */\n"},{"id":"7963","messageId":"Pine.LNX.4.63.0508302349140.23242@iabervon.org","threadId":"1703","inReplyTo":"Pine.LNX.4.63.0508302317380.23242@iabervon.org","subject":"[PATCH] Change read-tree to merge before using the index.","fromName":"Daniel Barkalow","fromEmail":"barkalow@iabervon.org","sentAt":"2005-08-31T03:49:59Z","receivedAt":"2005-08-31T03:49:59Z","isPatch":true,"sender":{"key":"barkalow@iabervon.org","avatar":"https://avatars.githubusercontent.com/u/55364219?v=4"},"body":"Signed-off-by: Daniel Barkalow <barkalow@iabervon.org>\n---\n\n read-tree.c |  522 ++++++++++++++++++++++++++++++++++-------------------------\n 1 files changed, 297 insertions(+), 225 deletions(-)\n\nd0f45ad81db2e133c49c23bd09c5615da344bb5c\ndiff --git a/read-tree.c b/read-tree.c\n--- a/read-tree.c\n+++ b/read-tree.c\n@@ -5,28 +5,280 @@\n  */\n #include \"cache.h\"\n\n-static int stage = 0;\n+#include \"object.h\"\n+#include \"tree.h\"\n+\n+static int merge = 0;\n+static int emu23 = 0;\n static int update = 0;\n\n-static int unpack_tree(unsigned char *sha1)\n+static struct object_list *trees = NULL;\n+\n+typedef int (*merge_fn_t)(struct cache_entry **src,\n+\t\t\t  struct cache_entry **dest,\n+\t\t\t  int df_conflicts_2,\n+\t\t\t  int df_conflicts_3);\n+\n+static int unpack_trees_rec(struct tree_entry_list **posns, int len,\n+\t\t\t    const char *base, merge_fn_t fn,\n+\t\t\t    int file2, int file3, int *indpos)\n+{\n+\tint baselen = strlen(base);\n+\tint src_size = len + 1;\n+\tif (emu23)\n+\t\tsrc_size++;\n+\tif (src_size > 4)\n+\t\tsrc_size = 4;\n+\tdo {\n+\t\tint i;\n+\t\tchar *first = NULL;\n+\t\tint pathlen;\n+\t\tunsigned ce_size;\n+\t\tint dir2 = 0;\n+\t\tint dir3 = 0;\n+\t\tint subfile2 = file2;\n+\t\tint subfile3 = file3;\n+\t\tstruct tree_entry_list **subposns = NULL;\n+\t\tstruct cache_entry **src = NULL;\n+\t\tchar *cache_name = NULL;\n+\n+\t\t/* Find the first name in the input. */\n+\n+\t\t/* Check the cache */\n+\t\tif (merge && *indpos < active_nr) {\n+\t\t\t/* This is a bit tricky: */\n+\t\t\t/* If the index has a subdirectory (with\n+\t\t\t * contents) as the first name, it'll get a\n+\t\t\t * filename like \"foo/bar\". But that's after\n+\t\t\t * \"foo\", so the entry in trees will get\n+\t\t\t * handled first, at which point we'll go into\n+\t\t\t * \"foo\", and deal with \"bar\" from the index,\n+\t\t\t * because the base will be \"foo/\". The only\n+\t\t\t * way we can actually have \"foo/bar\" first of\n+\t\t\t * all the things is if the trees don't\n+\t\t\t * contain \"foo\" at all, in which case we'll\n+\t\t\t * handle \"foo/bar\" without going into the\n+\t\t\t * directory, but that's fine (and will return\n+\t\t\t * an error anyway, with the added unknown\n+\t\t\t * file case.\n+\t\t\t */\n+\n+\t\t\tcache_name = active_cache[*indpos]->name;\n+\t\t\tif (strlen(cache_name) > baselen &&\n+\t\t\t    !memcmp(cache_name, base, baselen)) {\n+\t\t\t\tcache_name += baselen;\n+\t\t\t\tfirst = cache_name;\n+\t\t\t} else {\n+\t\t\t\tcache_name = NULL;\n+\t\t\t}\n+\t\t}\n+\n+\t\tfor (i = 0; i < len; i++) {\n+\t\t\tif (!posns[i])\n+\t\t\t\tcontinue;\n+\t\t\tif (!first || strcmp(first, posns[i]->name) > 0)\n+\t\t\t\tfirst = posns[i]->name;\n+\t\t}\n+\t\t/* No name means we're done */\n+\t\tif (!first)\n+\t\t\treturn 0;\n+\n+\t\tpathlen = strlen(first);\n+\t\tce_size = cache_entry_size(baselen + pathlen);\n+\n+\t\tif (cache_name && !strcmp(cache_name, first)) {\n+\t\t\tsrc = xmalloc(sizeof(struct cache_entry *) *\n+\t\t\t\t      src_size);\n+\t\t\tmemset(src, 0,\n+\t\t\t       sizeof(struct cache_entry *) *\n+\t\t\t       src_size);\n+\t\t\tsrc[0] = active_cache[*indpos];\n+\t\t\tremove_cache_entry_at(*indpos);\n+\t\t\tif (emu23) {\n+\t\t\t\t// we need this in stage 2 as well as stage 0\n+\t\t\t\tstruct cache_entry *copy =\n+\t\t\t\t\txmalloc(ce_size);\n+\t\t\t\tmemcpy(copy, src[0], ce_size);\n+\t\t\t\tcopy->ce_flags =\n+\t\t\t\t\tcreate_ce_flags(baselen + pathlen, 2);\n+\t\t\t\tif (dir2 || file2) {\n+\t\t\t\t\tdie(\"cannot merge index and our head tree\");\n+\t\t\t\t}\n+\t\t\t\tsrc[2] = copy;\n+\t\t\t\tsubfile2 = 1;\n+\t\t\t}\n+\t\t}\n+\n+\t\tfor (i = 0; i < len; i++) {\n+\t\t\tstruct cache_entry *ce;\n+\t\t\tint ce_stage;\n+\t\t\tif (!posns[i] ||\n+\t\t\t    strcmp(first, posns[i]->name)) {\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\n+\t\t\tif (len > 3) {\n+\t\t\t\tif (i - len + 3 >= 0)\n+\t\t\t\t\tce_stage = i - len + 3 + merge;\n+\t\t\t\telse\n+\t\t\t\t\tce_stage = merge;\n+\t\t\t} else {\n+\t\t\t\tce_stage = i + merge;\n+\t\t\t}\n+\t\t\tif (emu23 && ce_stage == 2)\n+\t\t\t\tce_stage = 3;\n+\n+\t\t\tif (posns[i]->directory) {\n+\t\t\t\tif (!subposns) {\n+\t\t\t\t\tint posnlen = sizeof(struct tree_list_entry *) * len;\n+\t\t\t\t\tsubposns = xmalloc(posnlen);\n+\t\t\t\t\tmemset(subposns, 0, posnlen);\n+\t\t\t\t}\n+\t\t\t\tparse_tree(posns[i]->item.tree);\n+\t\t\t\tsubposns[i] = posns[i]->item.tree->entries;\n+\t\t\t\tposns[i] = posns[i]->next;\n+\t\t\t\tif (emu23 && ce_stage == 1)\n+\t\t\t\t\tdir2 = 1;\n+\t\t\t\tif (ce_stage == 2)\n+\t\t\t\t\tdir2 = 1;\n+\t\t\t\tif (ce_stage == 3)\n+\t\t\t\t\tdir3 = 1;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\n+\t\t\tif (ce_stage == 2)\n+\t\t\t\tsubfile2 = 1;\n+\t\t\tif (ce_stage == 3)\n+\t\t\t\tsubfile3 = 1;\n+\n+\t\t\tce = xmalloc(ce_size);\n+\t\t\tmemset(ce, 0, ce_size);\n+\t\t\tce->ce_mode = create_ce_mode(posns[i]->mode);\n+\t\t\tce->ce_flags = create_ce_flags(baselen + pathlen,\n+\t\t\t\t\t\t       ce_stage);\n+\t\t\tmemcpy(ce->name, base, baselen);\n+\t\t\tmemcpy(ce->name + baselen, first, pathlen + 1);\n+\n+\t\t\tif (!src) {\n+\t\t\t\tsrc = xmalloc(sizeof(struct cache_entry *) *\n+\t\t\t\t\t      src_size);\n+\t\t\t\tmemset(src, 0,\n+\t\t\t\t       sizeof(struct cache_entry *) *\n+\t\t\t\t       src_size);\n+\t\t\t}\n+\n+\t\t\tmemcpy(ce->sha1, posns[i]->item.any->sha1, 20);\n+\t\t\tif (ce_stage == 1 && emu23 && !src[2]) {\n+\t\t\t\t// we need this in stage 2 as well as stage 1\n+\t\t\t\tstruct cache_entry *copy =\n+\t\t\t\t\txmalloc(ce_size);\n+\t\t\t\tmemcpy(copy, ce, ce_size);\n+\t\t\t\tcopy->ce_flags =\n+\t\t\t\t\tcreate_ce_flags(baselen + pathlen, 2);\n+\t\t\t\tif (dir2 || file2) {\n+\t\t\t\t\tdie(\"cannot merge index and our head tree\");\n+\t\t\t\t}\n+\t\t\t\tsrc[2] = copy;\n+\t\t\t\tsubfile2 = 1;\n+\t\t\t}\n+\t\t\tsrc[ce_stage] = ce;\n+\t\t\tposns[i] = posns[i]->next;\n+\t\t}\n+\t\tif (src) {\n+\t\t\tif (fn) {\n+\t\t\t\tint ret;\n+\t\t\t\t/*\n+\t\t\t\tprintf(\"%s:\\n\", first);\n+\t\t\t\tfor (i = 0; i < src_size; i++) {\n+\t\t\t\t\tprintf(\" %d \", i);\n+\t\t\t\t\tif (src[i])\n+\t\t\t\t\t\tprintf(\"%s\\n\", sha1_to_hex(src[i]->sha1));\n+\t\t\t\t\telse\n+\t\t\t\t\t\tprintf(\"\\n\");\n+\t\t\t\t}\n+\t\t\t\t*/\n+\t\t\t\tret = fn(src, NULL,\n+\t\t\t\t\t (file2 || dir2),\n+\t\t\t\t\t (file3 || dir3));\n+\t\t\t\t/*printf(\"Added %d entries\\n\", ret);*/\n+\t\t\t\t*indpos += ret;\n+\t\t\t} else {\n+\t\t\t\tfor (i = 0; i < src_size; i++) {\n+\t\t\t\t\tif (src[i]) {\n+\t\t\t\t\t\tadd_cache_entry(src[i], ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t\tif (subposns) {\n+\t\t\tchar *newbase = xmalloc(baselen + 2 + pathlen);\n+\t\t\tmemcpy(newbase, base, baselen);\n+\t\t\tmemcpy(newbase + baselen, first, pathlen);\n+\t\t\tnewbase[baselen + pathlen] = '/';\n+\t\t\tnewbase[baselen + pathlen + 1] = '\\0';\n+\t\t\tif (unpack_trees_rec(subposns, len, newbase, fn,\n+\t\t\t\t\t     subfile2, subfile3, indpos))\n+\t\t\t\treturn -1;\n+\t\t}\n+\t} while (1);\n+}\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 void check_updates(struct cache_entry **src, int nr)\n {\n-\tvoid *buffer;\n-\tunsigned long size;\n-\tint ret;\n+\tstatic struct checkout state = {\n+\t\t.base_dir = \"\",\n+\t\t.force = 1,\n+\t\t.quiet = 1,\n+\t\t.refresh_cache = 1,\n+\t};\n+\tunsigned short mask = htons(CE_UPDATE);\n+\twhile (nr--) {\n+\t\tstruct cache_entry *ce = *src++;\n+\t\tif (!ce->ce_mode) {\n+\t\t\tif (update)\n+\t\t\t\tunlink(ce->name);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (ce->ce_flags & mask) {\n+\t\t\tce->ce_flags &= ~mask;\n+\t\t\tif (update)\n+\t\t\t\tcheckout_entry(ce, &state);\n+\t\t}\n+\t}\n+}\n\n-\tbuffer = read_object_with_reference(sha1, \"tree\", &size, NULL);\n-\tif (!buffer)\n+static int unpack_trees(merge_fn_t fn)\n+{\n+\tint indpos = 0;\n+\tunsigned len = object_list_length(trees);\n+\tstruct tree_entry_list **posns =\n+\t\txmalloc(len * sizeof(struct tree_entry_list *));\n+\tint i;\n+\tstruct object_list *posn = trees;\n+\tfor (i = 0; i < len; i++) {\n+\t\tposns[i] = ((struct tree *) posn->item)->entries;\n+\t\tposn = posn->next;\n+\t}\n+\tif (unpack_trees_rec(posns, len, \"\", fn, 0, 0, &indpos))\n \t\treturn -1;\n-\tret = read_tree(buffer, size, stage, NULL);\n-\tfree(buffer);\n-\treturn ret;\n+\n+\tcheck_updates(active_cache, active_nr);\n+\treturn 0;\n }\n\n-static int path_matches(struct cache_entry *a, struct cache_entry *b)\n+static int list_tree(unsigned char *sha1)\n {\n-\tint len = ce_namelen(a);\n-\treturn ce_namelen(b) == len &&\n-\t\t!memcmp(a->name, b->name, len);\n+\tstruct tree *tree = parse_tree_indirect(sha1);\n+\tif (!tree)\n+\t\treturn -1;\n+\tobject_list_append(&tree->object, &trees);\n+\treturn 0;\n }\n\n static int same(struct cache_entry *a, struct cache_entry *b)\n@@ -91,17 +343,6 @@ static void verify_uptodate(struct cache\n \tdie(\"Entry '%s' not uptodate. Cannot merge.\", ce->name);\n }\n\n-/*\n- * If the old tree contained a CE that isn't even in the\n- * result, that's always a problem, regardless of whether\n- * it's up-to-date or not (ie it can be a file that we\n- * have updated but not committed yet).\n- */\n-static void reject_merge(struct cache_entry *ce)\n-{\n-\tdie(\"Entry '%s' would be overwritten by merge. Cannot merge.\", ce->name);\n-}\n-\n static int merged_entry_internal(struct cache_entry *merge, struct cache_entry *old, struct cache_entry **dst, int allow_dirty)\n {\n \tmerge->ce_flags |= htons(CE_UPDATE);\n@@ -120,7 +361,7 @@ static int merged_entry_internal(struct\n \t\t}\n \t}\n \tmerge->ce_flags &= ~htons(CE_STAGEMASK);\n-\t*dst++ = merge;\n+\tadd_cache_entry(merge, ADD_CACHE_OK_TO_ADD);\n \treturn 1;\n }\n\n@@ -139,76 +380,19 @@ static int deleted_entry(struct cache_en\n \tif (old)\n \t\tverify_uptodate(old);\n \tce->ce_mode = 0;\n-\t*dst++ = ce;\n+\tadd_cache_entry(ce, ADD_CACHE_OK_TO_ADD);\n \treturn 1;\n }\n\n-static int causes_df_conflict(struct cache_entry *ce, int stage,\n-\t\t\t      struct cache_entry **dst_,\n-\t\t\t      struct cache_entry **next_,\n-\t\t\t      int tail)\n-{\n-\t/* This is called during the merge operation and walking\n-\t * the active_cache[] array is messy, because it is in the\n-\t * middle of overlapping copy operation.  The invariants\n-\t * are:\n-\t * (1) active_cache points at the first (zeroth) entry.\n-\t * (2) up to dst pointer are resolved entries.\n-\t * (3) from the next pointer (head-inclusive) to the tail\n-\t *     of the active_cache array have the remaining paths\n-\t *     to be processed.  There can be a gap between dst\n-\t *     and next.  Note that next is called \"src\" in the\n-\t *     merge_cache() function, and tail is the original\n-\t *     end of active_cache array when merge_cache() started.\n-\t * (4) the path corresponding to *ce is not found in (2)\n-\t *     or (3).  It is in the gap.\n-\t *\n-\t *  active_cache -----......+++++++++++++.\n-\t *                    ^dst  ^next        ^tail\n-\t */\n-\tint i, next, dst;\n-\tconst char *path = ce->name;\n-\tint namelen = ce_namelen(ce);\n-\n-\tnext = next_ - active_cache;\n-\tdst = dst_ - active_cache;\n-\n-\tfor (i = 0; i < tail; i++) {\n-\t\tint entlen, len;\n-\t\tconst char *one, *two;\n-\t\tif (dst <= i && i < next)\n-\t\t\tcontinue;\n-\t\tce = active_cache[i];\n-\t\tif (ce_stage(ce) != stage)\n-\t\t\tcontinue;\n-\t\t/* If ce->name is a prefix of path, then path is a file\n-\t\t * that hangs underneath ce->name, which is bad.\n-\t\t * If path is a prefix of ce->name, then it is the\n-\t\t * other way around which also is bad.\n-\t\t */\n-\t\tentlen = ce_namelen(ce);\n-\t\tif (namelen == entlen)\n-\t\t\tcontinue;\n-\t\tif (namelen < entlen) {\n-\t\t\tlen = namelen;\n-\t\t\tone = path;\n-\t\t\ttwo = ce->name;\n-\t\t} else {\n-\t\t\tlen = entlen;\n-\t\t\tone = ce->name;\n-\t\t\ttwo = path;\n-\t\t}\n-\t\tif (memcmp(one, two, len))\n-\t\t\tcontinue;\n-\t\tif (two[len] == '/')\n-\t\t\treturn 1;\n-\t}\n-\treturn 0;\n+static int keep_entry(struct cache_entry *ce, struct cache_entry **dst)\n+{\n+\tadd_cache_entry(ce, ADD_CACHE_OK_TO_ADD);\n+\treturn 1;\n }\n\n static int threeway_merge(struct cache_entry *stages[4],\n \t\t\t  struct cache_entry **dst,\n-\t\t\t  struct cache_entry **next, int tail)\n+\t\t\t  int df_conflict_2, int df_conflict_3)\n {\n \tstruct cache_entry *old = stages[0];\n \tstruct cache_entry *a = stages[1], *b = stages[2], *c = stages[3];\n@@ -218,7 +402,7 @@ static int threeway_merge(struct cache_e\n \t/* #5ALT */\n \tif (!a && b && c && same(b, c)) {\n \t\tif (old && !same(b, old))\n-\t\t\treturn -1;\n+\t\t\treject_merge(old);\n \t\treturn merged_entry_allow_dirty(b, old, dst);\n \t}\n \t/* #2ALT and #3ALT */\n@@ -269,12 +453,12 @@ static int threeway_merge(struct cache_e\n \t\t *     and vice versa.\n \t\t */\n \t\tif (c) { /* #2ALT */\n-\t\t\tif (!causes_df_conflict(c, 2, dst, next, tail) &&\n+\t\t\tif (!df_conflict_2 &&\n \t\t\t    (!old || same(c, old)))\n \t\t\t\treturn merged_entry_allow_dirty(c, old, dst);\n \t\t}\n \t\telse { /* #3ALT */\n-\t\t\tif (!causes_df_conflict(b, 3, dst, next, tail) &&\n+\t\t\tif (!df_conflict_3 &&\n \t\t\t    (!old || same(b, old)))\n \t\t\t\treturn merged_entry_allow_dirty(b, old, dst);\n \t\t}\n@@ -293,7 +477,7 @@ static int threeway_merge(struct cache_e\n \t */\n \tif (old) {\n \t\tif (!b || !same(old, b))\n-\t\t\treturn -1;\n+\t\t\treject_merge(old);\n \t}\n \tmerge = merge_entries(a, b, c);\n \tif (merge)\n@@ -301,9 +485,9 @@ static int threeway_merge(struct cache_e\n \tif (old)\n \t\tverify_uptodate(old);\n \tcount = 0;\n-\tif (a) { *dst++ = a; count++; }\n-\tif (b) { *dst++ = b; count++; }\n-\tif (c) { *dst++ = c; count++; }\n+\tif (a) { count += keep_entry(a, dst + count); }\n+\tif (b) { count += keep_entry(b, dst + count); }\n+\tif (c) { count += keep_entry(c, dst + count); }\n \treturn count;\n }\n\n@@ -317,14 +501,11 @@ static int threeway_merge(struct cache_e\n  *\n  */\n static int twoway_merge(struct cache_entry **src, struct cache_entry **dst,\n-\t\t\tstruct cache_entry **next, int tail)\n+\t\t\tint df23, int df32)\n {\n \tstruct cache_entry *current = src[0];\n \tstruct cache_entry *oldtree = src[1], *newtree = src[2];\n\n-\tif (src[3])\n-\t\treturn -1;\n-\n \tif (current) {\n \t\tif ((!oldtree && !newtree) || /* 4 and 5 */\n \t\t    (!oldtree && newtree &&\n@@ -334,8 +515,7 @@ static int twoway_merge(struct cache_ent\n \t\t    (oldtree && newtree &&\n \t\t     !same(oldtree, newtree) && /* 18 and 19*/\n \t\t     same(current, newtree))) {\n-\t\t\t*dst++ = current;\n-\t\t\treturn 1;\n+\t\t\treturn keep_entry(current, dst);\n \t\t}\n \t\telse if (oldtree && !newtree && same(current, oldtree)) {\n \t\t\t/* 10 or 11 */\n@@ -346,9 +526,16 @@ static int twoway_merge(struct cache_ent\n \t\t\t/* 20 or 21 */\n \t\t\treturn merged_entry(newtree, current, dst);\n \t\t}\n-\t\telse\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, dst);\n@@ -357,137 +544,25 @@ static int twoway_merge(struct cache_ent\n }\n\n /*\n- * Two-way merge emulated with three-way merge.\n- *\n- * This treats \"read-tree -m H M\" by transforming it internally\n- * into \"read-tree -m H I+H M\", where I+H is a tree that would\n- * contain the contents of the current index file, overlayed on\n- * top of H.  Unlike the traditional two-way merge, this leaves\n- * the stages in the resulting index file and lets the user resolve\n- * the merge conflicts using standard tools for three-way merge.\n- *\n- * This function is just to set-up such an arrangement, and the\n- * actual merge uses threeway_merge() function.\n- */\n-static void setup_emu23(void)\n-{\n-\t/* stage0 contains I, stage1 H, stage2 M.\n-\t * move stage2 to stage3, and create stage2 entries\n-\t * by scanning stage0 and stage1 entries.\n-\t */\n-\tint i, namelen, size;\n-\tstruct cache_entry *ce, *stage2;\n-\n-\tfor (i = 0; i < active_nr; i++) {\n-\t\tce = active_cache[i];\n-\t\tif (ce_stage(ce) != 2)\n-\t\t\tcontinue;\n-\t\t/* hoist them up to stage 3 */\n-\t\tnamelen = ce_namelen(ce);\n-\t\tce->ce_flags = create_ce_flags(namelen, 3);\n-\t}\n-\n-\tfor (i = 0; i < active_nr; i++) {\n-\t\tce = active_cache[i];\n-\t\tif (ce_stage(ce) > 1)\n-\t\t\tcontinue;\n-\t\tnamelen = ce_namelen(ce);\n-\t\tsize = cache_entry_size(namelen);\n-\t\tstage2 = xmalloc(size);\n-\t\tmemcpy(stage2, ce, size);\n-\t\tstage2->ce_flags = create_ce_flags(namelen, 2);\n-\t\tif (add_cache_entry(stage2, ADD_CACHE_OK_TO_ADD) < 0)\n-\t\t\tdie(\"cannot merge index and our head tree\");\n-\n-\t\t/* We are done with this name, so skip to next name */\n-\t\twhile (i < active_nr &&\n-\t\t       ce_namelen(active_cache[i]) == namelen &&\n-\t\t       !memcmp(active_cache[i]->name, ce->name, namelen))\n-\t\t\ti++;\n-\t\ti--; /* compensate for the loop control */\n-\t}\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, struct cache_entry **dst,\n-\t\t\tstruct cache_entry **next, int tail)\n+\t\t\tint df23, int df32)\n {\n \tstruct cache_entry *old = src[0];\n \tstruct cache_entry *a = src[1];\n\n-\tif (src[2] || src[3])\n-\t\treturn -1;\n-\n \tif (!a)\n \t\treturn 0;\n \tif (old && same(old, a)) {\n-\t\t*dst++ = old;\n-\t\treturn 1;\n+\t\treturn keep_entry(old, dst);\n \t}\n \treturn merged_entry(a, NULL, dst);\n }\n\n-static void check_updates(struct cache_entry **src, int nr)\n-{\n-\tstatic struct checkout state = {\n-\t\t.base_dir = \"\",\n-\t\t.force = 1,\n-\t\t.quiet = 1,\n-\t\t.refresh_cache = 1,\n-\t};\n-\tunsigned short mask = htons(CE_UPDATE);\n-\twhile (nr--) {\n-\t\tstruct cache_entry *ce = *src++;\n-\t\tif (!ce->ce_mode) {\n-\t\t\tif (update)\n-\t\t\t\tunlink(ce->name);\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (ce->ce_flags & mask) {\n-\t\t\tce->ce_flags &= ~mask;\n-\t\t\tif (update)\n-\t\t\t\tcheckout_entry(ce, &state);\n-\t\t}\n-\t}\n-}\n-\n-typedef int (*merge_fn_t)(struct cache_entry **, struct cache_entry **, struct cache_entry **, int);\n-\n-static void merge_cache(struct cache_entry **src, int nr, merge_fn_t fn)\n-{\n-\tstruct cache_entry **dst = src;\n-\tint tail = nr;\n-\n-\twhile (nr) {\n-\t\tint entries;\n-\t\tstruct cache_entry *name, *ce, *stages[4] = { NULL, };\n-\n-\t\tname = ce = *src;\n-\t\tfor (;;) {\n-\t\t\tint stage = ce_stage(ce);\n-\t\t\tstages[stage] = ce;\n-\t\t\tce = *++src;\n-\t\t\tactive_nr--;\n-\t\t\tif (!--nr)\n-\t\t\t\tbreak;\n-\t\t\tif (!path_matches(ce, name))\n-\t\t\t\tbreak;\n-\t\t}\n-\n-\t\tentries = fn(stages, dst, src, tail);\n-\t\tif (entries < 0)\n-\t\t\treject_merge(name);\n-\t\tdst += entries;\n-\t\tactive_nr += entries;\n-\t}\n-\tcheck_updates(active_cache, active_nr);\n-}\n-\n static int read_cache_unmerged(void)\n {\n \tint i, deleted;\n@@ -516,8 +591,9 @@ static struct cache_file cache_file;\n\n int main(int argc, char **argv)\n {\n-\tint i, newfd, merge, reset, emu23;\n+\tint i, newfd, reset, stage = 0;\n \tunsigned char sha1[20];\n+\tmerge_fn_t fn = NULL;\n\n \tnewfd = hold_index_file_for_update(&cache_file, get_index_file());\n \tif (newfd < 0)\n@@ -569,9 +645,7 @@ int main(int argc, char **argv)\n\n \t\tif (get_sha1(arg, sha1) < 0)\n \t\t\tusage(read_tree_usage);\n-\t\tif (stage > 3)\n-\t\t\tusage(read_tree_usage);\n-\t\tif (unpack_tree(sha1) < 0)\n+\t\tif (list_tree(sha1) < 0)\n \t\t\tdie(\"failed to unpack tree object %s\", arg);\n \t\tstage++;\n \t}\n@@ -583,19 +657,17 @@ int main(int argc, char **argv)\n \t\t\t[2] = twoway_merge,\n \t\t\t[3] = threeway_merge,\n \t\t};\n-\t\tmerge_fn_t fn;\n\n-\t\tif (stage < 2 || stage > 4)\n+\t\tif (stage < 2)\n \t\t\tdie(\"just how do you expect me to merge %d trees?\", stage-1);\n \t\tif (emu23 && stage != 3)\n \t\t\tdie(\"--emu23 takes only two trees\");\n \t\tfn = merge_function[stage-1];\n \t\tif (stage == 3 && emu23) {\n-\t\t\tsetup_emu23();\n \t\t\tfn = merge_function[3];\n \t\t}\n-\t\tmerge_cache(active_cache, active_nr, fn);\n \t}\n+\tunpack_trees(fn);\n \tif (write_cache(newfd, active_cache, active_nr) ||\n \t    commit_index_file(&cache_file))\n \t\tdie(\"unable to write new index file\");\n"},{"id":"7967","messageId":"7voe7ehb7z.fsf@assigned-by-dhcp.cox.net","threadId":"1703","inReplyTo":"Pine.LNX.4.63.0508302317380.23242@iabervon.org","subject":"Re: [PATCH 0/2] Reorganize read-tree","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-08-31T06:28:00Z","receivedAt":"2005-08-31T06:28:00Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Dan, I really really *REALLY* wanted to try this out in \"pu\"\nbranch and even was about to rig some torture chamber for\ntesting before applying the patch, but you got the shiny blue\nbat X-<.\n\nI've checked with both marc and gmane [*], and believe the\nproblem is not on my end.  The MUA seems to have stripped all\nthe trailing whitespaces, so you do not have a single SP on\nempty context lines, and lack excess trailing whitespace on line\n41 of object.h (object_list_insert declaration), for example.\n\nA patch to SubmittingPatches, MUA specific help section for\nusers of Pine 4.63 would be very much appreciated.\n\n[References]\n\nhttp://marc.theaimsgroup.com/?l=git&m=112545993929060&q=raw\nhttp://www.mail-archive.com/git@vger.kernel.org/msg03590.html\n"},{"id":"7972","messageId":"tnxy86ikxan.fsf@arm.com","threadId":"1703","inReplyTo":"Pine.LNX.4.63.0508302317380.23242@iabervon.org","subject":"Re: [PATCH 0/2] Reorganize read-tree","fromName":"Catalin Marinas","fromEmail":"catalin.marinas@gmail.com","sentAt":"2005-08-31T14:15:12Z","receivedAt":"2005-08-31T14:15:12Z","isPatch":true,"sender":{"key":"catalin.marinas@gmail.com","avatar":null},"body":"Daniel Barkalow <barkalow@iabervon.org> wrote:\n> I got mostly done with this before Linus mentioned the possibility of\n> having multiple index entries in the same stage for a single path. I\n> finished it anyway, but I'm not sure that we won't want to know which of\n> the common ancestors contributed which, and, if some of them don't have a\n> path, we wouldn't be able to tell.\n\nI don't have time to look at the patch and I don't have a good\nknowledge of the GIT internals, so I will just ask. Does this patch\nchanges the call convention for git-merge-one-file-script? I have my\nown script for StGIT and I would need to know whether it is affected\nor not.\n\nThanks.\n\n-- \nCatalin\n"},{"id":"7973","messageId":"Pine.LNX.4.63.0508311244410.23242@iabervon.org","threadId":"1703","inReplyTo":"7voe7ehb7z.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 0/2] Reorganize read-tree","fromName":"Daniel Barkalow","fromEmail":"barkalow@iabervon.org","sentAt":"2005-08-31T16:56:11Z","receivedAt":"2005-08-31T16:56:11Z","isPatch":true,"sender":{"key":"barkalow@iabervon.org","avatar":"https://avatars.githubusercontent.com/u/55364219?v=4"},"body":"On Tue, 30 Aug 2005, Junio C Hamano wrote:\n\n> Dan, I really really *REALLY* wanted to try this out in \"pu\"\n> branch and even was about to rig some torture chamber for\n> testing before applying the patch, but you got the shiny blue\n> bat X-<.\n\nI'll send a replacement with the settings correct.\n\n> A patch to SubmittingPatches, MUA specific help section for\n> users of Pine 4.63 would be very much appreciated.\n\nAh, it looks like a recent version changed the default behavior to do the \nright thing, and inverted the sense of the configuration option. (Either \nthat or Gentoo did it.) So you need to set the \n\"no-strip-whitespace-before-send\" option, unless the option you have is \n\"strip-whitespace-before-send\", in which case you should avoid checking \nit.\n\nI don't actually have things set up for preparing patches from work, \nalthough I can resend the patches I prepared earlier.\n\n\t-Daniel\n*This .sig left intentionally blank*\n"},{"id":"7974","messageId":"Pine.LNX.4.63.0508311256220.23242@iabervon.org","threadId":"1703","inReplyTo":"Pine.LNX.4.63.0508302317380.23242@iabervon.org","subject":"[PATCH 1/2 (resend)] Object model additions for read-tree","fromName":"Daniel Barkalow","fromEmail":"barkalow@iabervon.org","sentAt":"2005-08-31T16:57:00Z","receivedAt":"2005-08-31T16:57:00Z","isPatch":true,"sender":{"key":"barkalow@iabervon.org","avatar":"https://avatars.githubusercontent.com/u/55364219?v=4"},"body":"Adds object_list_append() and a function to get the struct tree from an ent.\n\nSigned-off-by: Daniel Barkalow <barkalow@iabervon.org>\n\n---\n\n object.c |   11 +++++++++++\n object.h |    3 +++\n tree.c   |   19 +++++++++++++++++++\n tree.h   |    3 +++\n 4 files changed, 36 insertions(+), 0 deletions(-)\n\n49d33c385aa69d17c991300f73e77c6718a2b4a6\ndiff --git a/object.c b/object.c\n--- a/object.c\n+++ b/object.c\n@@ -184,6 +184,17 @@ struct object_list *object_list_insert(s\n         return new_list;\n }\n \n+void object_list_append(struct object *item,\n+\t\t\tstruct object_list **list_p)\n+{\n+\twhile (*list_p) {\n+\t\tlist_p = &((*list_p)->next);\n+\t}\n+\t*list_p = xmalloc(sizeof(struct object_list));\n+\t(*list_p)->next = NULL;\n+\t(*list_p)->item = item;\n+}\n+\n unsigned object_list_length(struct object_list *list)\n {\n \tunsigned ret = 0;\ndiff --git a/object.h b/object.h\n--- a/object.h\n+++ b/object.h\n@@ -41,6 +41,9 @@ void mark_reachable(struct object *obj, \n struct object_list *object_list_insert(struct object *item, \n \t\t\t\t       struct object_list **list_p);\n \n+void object_list_append(struct object *item,\n+\t\t\tstruct object_list **list_p);\n+\n unsigned object_list_length(struct object_list *list);\n \n int object_list_contains(struct object_list *list, struct object *obj);\ndiff --git a/tree.c b/tree.c\n--- a/tree.c\n+++ b/tree.c\n@@ -1,5 +1,7 @@\n #include \"tree.h\"\n #include \"blob.h\"\n+#include \"commit.h\"\n+#include \"tag.h\"\n #include \"cache.h\"\n #include <stdlib.h>\n \n@@ -212,3 +214,20 @@ int parse_tree(struct tree *item)\n \tfree(buffer);\n \treturn ret;\n }\n+\n+struct tree *parse_tree_indirect(const unsigned char *sha1)\n+{\n+\tstruct object *obj = parse_object(sha1);\n+\tdo {\n+\t\tif (!obj)\n+\t\t\treturn NULL;\n+\t\tif (obj->type == tree_type)\n+\t\t\treturn (struct tree *) obj;\n+\t\telse if (obj->type == commit_type)\n+\t\t\treturn ((struct commit *) obj)->tree;\n+\t\telse if (obj->type == tag_type)\n+\t\t\tobj = ((struct tag *) obj)->tagged;\n+\t\telse\n+\t\t\treturn NULL;\n+\t} while (1);\n+}\ndiff --git a/tree.h b/tree.h\n--- a/tree.h\n+++ b/tree.h\n@@ -32,4 +32,7 @@ int parse_tree_buffer(struct tree *item,\n \n int parse_tree(struct tree *tree);\n \n+/* Parses and returns the tree in the given ent, chasing tags and commits. */\n+struct tree *parse_tree_indirect(const unsigned char *sha1);\n+\n #endif /* TREE_H */\n"},{"id":"7975","messageId":"Pine.LNX.4.63.0508311257040.23242@iabervon.org","threadId":"1703","inReplyTo":"Pine.LNX.4.63.0508302317380.23242@iabervon.org","subject":"[PATCH 2/2 (resend)] Change read-tree to merge before using the index.","fromName":"Daniel Barkalow","fromEmail":"barkalow@iabervon.org","sentAt":"2005-08-31T16:57:38Z","receivedAt":"2005-08-31T16:57:38Z","isPatch":true,"sender":{"key":"barkalow@iabervon.org","avatar":"https://avatars.githubusercontent.com/u/55364219?v=4"},"body":"Signed-off-by: Daniel Barkalow <barkalow@iabervon.org>\n\n---\n\n read-tree.c |  522 ++++++++++++++++++++++++++++++++++-------------------------\n 1 files changed, 297 insertions(+), 225 deletions(-)\n\nd0f45ad81db2e133c49c23bd09c5615da344bb5c\ndiff --git a/read-tree.c b/read-tree.c\n--- a/read-tree.c\n+++ b/read-tree.c\n@@ -5,28 +5,280 @@\n  */\n #include \"cache.h\"\n \n-static int stage = 0;\n+#include \"object.h\"\n+#include \"tree.h\"\n+\n+static int merge = 0;\n+static int emu23 = 0;\n static int update = 0;\n \n-static int unpack_tree(unsigned char *sha1)\n+static struct object_list *trees = NULL;\n+\n+typedef int (*merge_fn_t)(struct cache_entry **src, \n+\t\t\t  struct cache_entry **dest, \n+\t\t\t  int df_conflicts_2,\n+\t\t\t  int df_conflicts_3);\n+\n+static int unpack_trees_rec(struct tree_entry_list **posns, int len,\n+\t\t\t    const char *base, merge_fn_t fn, \n+\t\t\t    int file2, int file3, int *indpos)\n+{\n+\tint baselen = strlen(base);\n+\tint src_size = len + 1;\n+\tif (emu23)\n+\t\tsrc_size++;\n+\tif (src_size > 4)\n+\t\tsrc_size = 4;\n+\tdo {\n+\t\tint i;\n+\t\tchar *first = NULL;\n+\t\tint pathlen;\n+\t\tunsigned ce_size;\n+\t\tint dir2 = 0;\n+\t\tint dir3 = 0;\n+\t\tint subfile2 = file2;\n+\t\tint subfile3 = file3;\n+\t\tstruct tree_entry_list **subposns = NULL;\n+\t\tstruct cache_entry **src = NULL;\n+\t\tchar *cache_name = NULL;\n+\n+\t\t/* Find the first name in the input. */\n+\n+\t\t/* Check the cache */\n+\t\tif (merge && *indpos < active_nr) {\n+\t\t\t/* This is a bit tricky: */\n+\t\t\t/* If the index has a subdirectory (with\n+\t\t\t * contents) as the first name, it'll get a\n+\t\t\t * filename like \"foo/bar\". But that's after\n+\t\t\t * \"foo\", so the entry in trees will get\n+\t\t\t * handled first, at which point we'll go into\n+\t\t\t * \"foo\", and deal with \"bar\" from the index,\n+\t\t\t * because the base will be \"foo/\". The only\n+\t\t\t * way we can actually have \"foo/bar\" first of\n+\t\t\t * all the things is if the trees don't\n+\t\t\t * contain \"foo\" at all, in which case we'll\n+\t\t\t * handle \"foo/bar\" without going into the\n+\t\t\t * directory, but that's fine (and will return\n+\t\t\t * an error anyway, with the added unknown\n+\t\t\t * file case.\n+\t\t\t */\n+\n+\t\t\tcache_name = active_cache[*indpos]->name;\n+\t\t\tif (strlen(cache_name) > baselen &&\n+\t\t\t    !memcmp(cache_name, base, baselen)) {\n+\t\t\t\tcache_name += baselen;\n+\t\t\t\tfirst = cache_name;\n+\t\t\t} else {\n+\t\t\t\tcache_name = NULL;\n+\t\t\t}\n+\t\t}\n+\n+\t\tfor (i = 0; i < len; i++) {\n+\t\t\tif (!posns[i])\n+\t\t\t\tcontinue;\n+\t\t\tif (!first || strcmp(first, posns[i]->name) > 0)\n+\t\t\t\tfirst = posns[i]->name;\n+\t\t}\n+\t\t/* No name means we're done */\n+\t\tif (!first)\n+\t\t\treturn 0;\n+\n+\t\tpathlen = strlen(first);\n+\t\tce_size = cache_entry_size(baselen + pathlen);\n+\n+\t\tif (cache_name && !strcmp(cache_name, first)) {\n+\t\t\tsrc = xmalloc(sizeof(struct cache_entry *) * \n+\t\t\t\t      src_size);\n+\t\t\tmemset(src, 0,\n+\t\t\t       sizeof(struct cache_entry *) * \n+\t\t\t       src_size);\n+\t\t\tsrc[0] = active_cache[*indpos];\n+\t\t\tremove_cache_entry_at(*indpos);\n+\t\t\tif (emu23) {\n+\t\t\t\t// we need this in stage 2 as well as stage 0\n+\t\t\t\tstruct cache_entry *copy =\n+\t\t\t\t\txmalloc(ce_size);\n+\t\t\t\tmemcpy(copy, src[0], ce_size);\n+\t\t\t\tcopy->ce_flags = \n+\t\t\t\t\tcreate_ce_flags(baselen + pathlen, 2);\n+\t\t\t\tif (dir2 || file2) {\n+\t\t\t\t\tdie(\"cannot merge index and our head tree\");\n+\t\t\t\t}\n+\t\t\t\tsrc[2] = copy;\n+\t\t\t\tsubfile2 = 1;\n+\t\t\t}\n+\t\t}\n+\n+\t\tfor (i = 0; i < len; i++) {\n+\t\t\tstruct cache_entry *ce;\n+\t\t\tint ce_stage;\n+\t\t\tif (!posns[i] ||\n+\t\t\t    strcmp(first, posns[i]->name)) {\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\n+\t\t\tif (len > 3) {\n+\t\t\t\tif (i - len + 3 >= 0)\n+\t\t\t\t\tce_stage = i - len + 3 + merge;\n+\t\t\t\telse\n+\t\t\t\t\tce_stage = merge;\n+\t\t\t} else {\n+\t\t\t\tce_stage = i + merge;\n+\t\t\t}\n+\t\t\tif (emu23 && ce_stage == 2)\n+\t\t\t\tce_stage = 3;\n+\n+\t\t\tif (posns[i]->directory) {\n+\t\t\t\tif (!subposns) {\n+\t\t\t\t\tint posnlen = sizeof(struct tree_list_entry *) * len;\n+\t\t\t\t\tsubposns = xmalloc(posnlen);\n+\t\t\t\t\tmemset(subposns, 0, posnlen);\n+\t\t\t\t}\n+\t\t\t\tparse_tree(posns[i]->item.tree);\n+\t\t\t\tsubposns[i] = posns[i]->item.tree->entries;\n+\t\t\t\tposns[i] = posns[i]->next;\n+\t\t\t\tif (emu23 && ce_stage == 1)\n+\t\t\t\t\tdir2 = 1;\n+\t\t\t\tif (ce_stage == 2)\n+\t\t\t\t\tdir2 = 1;\n+\t\t\t\tif (ce_stage == 3)\n+\t\t\t\t\tdir3 = 1;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\n+\t\t\tif (ce_stage == 2)\n+\t\t\t\tsubfile2 = 1;\n+\t\t\tif (ce_stage == 3)\n+\t\t\t\tsubfile3 = 1;\n+\n+\t\t\tce = xmalloc(ce_size);\n+\t\t\tmemset(ce, 0, ce_size);\n+\t\t\tce->ce_mode = create_ce_mode(posns[i]->mode);\n+\t\t\tce->ce_flags = create_ce_flags(baselen + pathlen,\n+\t\t\t\t\t\t       ce_stage);\n+\t\t\tmemcpy(ce->name, base, baselen);\n+\t\t\tmemcpy(ce->name + baselen, first, pathlen + 1);\n+\n+\t\t\tif (!src) {\n+\t\t\t\tsrc = xmalloc(sizeof(struct cache_entry *) * \n+\t\t\t\t\t      src_size);\n+\t\t\t\tmemset(src, 0,\n+\t\t\t\t       sizeof(struct cache_entry *) * \n+\t\t\t\t       src_size);\n+\t\t\t}\n+\n+\t\t\tmemcpy(ce->sha1, posns[i]->item.any->sha1, 20);\n+\t\t\tif (ce_stage == 1 && emu23 && !src[2]) {\n+\t\t\t\t// we need this in stage 2 as well as stage 1\n+\t\t\t\tstruct cache_entry *copy =\n+\t\t\t\t\txmalloc(ce_size);\n+\t\t\t\tmemcpy(copy, ce, ce_size);\n+\t\t\t\tcopy->ce_flags = \n+\t\t\t\t\tcreate_ce_flags(baselen + pathlen, 2);\n+\t\t\t\tif (dir2 || file2) {\n+\t\t\t\t\tdie(\"cannot merge index and our head tree\");\n+\t\t\t\t}\n+\t\t\t\tsrc[2] = copy;\n+\t\t\t\tsubfile2 = 1;\n+\t\t\t}\n+\t\t\tsrc[ce_stage] = ce;\n+\t\t\tposns[i] = posns[i]->next;\n+\t\t}\n+\t\tif (src) {\n+\t\t\tif (fn) {\n+\t\t\t\tint ret;\n+\t\t\t\t/*\n+\t\t\t\tprintf(\"%s:\\n\", first);\n+\t\t\t\tfor (i = 0; i < src_size; i++) {\n+\t\t\t\t\tprintf(\" %d \", i);\n+\t\t\t\t\tif (src[i])\n+\t\t\t\t\t\tprintf(\"%s\\n\", sha1_to_hex(src[i]->sha1));\n+\t\t\t\t\telse\n+\t\t\t\t\t\tprintf(\"\\n\");\n+\t\t\t\t}\n+\t\t\t\t*/\n+\t\t\t\tret = fn(src, NULL,\n+\t\t\t\t\t (file2 || dir2),\n+\t\t\t\t\t (file3 || dir3));\n+\t\t\t\t/*printf(\"Added %d entries\\n\", ret);*/\n+\t\t\t\t*indpos += ret;\n+\t\t\t} else {\n+\t\t\t\tfor (i = 0; i < src_size; i++) {\n+\t\t\t\t\tif (src[i]) {\n+\t\t\t\t\t\tadd_cache_entry(src[i], ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t\tif (subposns) {\n+\t\t\tchar *newbase = xmalloc(baselen + 2 + pathlen);\n+\t\t\tmemcpy(newbase, base, baselen);\n+\t\t\tmemcpy(newbase + baselen, first, pathlen);\n+\t\t\tnewbase[baselen + pathlen] = '/';\n+\t\t\tnewbase[baselen + pathlen + 1] = '\\0';\n+\t\t\tif (unpack_trees_rec(subposns, len, newbase, fn,\n+\t\t\t\t\t     subfile2, subfile3, indpos))\n+\t\t\t\treturn -1;\n+\t\t}\n+\t} while (1);\n+}\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 void check_updates(struct cache_entry **src, int nr)\n {\n-\tvoid *buffer;\n-\tunsigned long size;\n-\tint ret;\n+\tstatic struct checkout state = {\n+\t\t.base_dir = \"\",\n+\t\t.force = 1,\n+\t\t.quiet = 1,\n+\t\t.refresh_cache = 1,\n+\t};\n+\tunsigned short mask = htons(CE_UPDATE);\n+\twhile (nr--) {\n+\t\tstruct cache_entry *ce = *src++;\n+\t\tif (!ce->ce_mode) {\n+\t\t\tif (update)\n+\t\t\t\tunlink(ce->name);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (ce->ce_flags & mask) {\n+\t\t\tce->ce_flags &= ~mask;\n+\t\t\tif (update)\n+\t\t\t\tcheckout_entry(ce, &state);\n+\t\t}\n+\t}\n+}\n \n-\tbuffer = read_object_with_reference(sha1, \"tree\", &size, NULL);\n-\tif (!buffer)\n+static int unpack_trees(merge_fn_t fn)\n+{\n+\tint indpos = 0;\n+\tunsigned len = object_list_length(trees);\n+\tstruct tree_entry_list **posns = \n+\t\txmalloc(len * sizeof(struct tree_entry_list *));\n+\tint i;\n+\tstruct object_list *posn = trees;\n+\tfor (i = 0; i < len; i++) {\n+\t\tposns[i] = ((struct tree *) posn->item)->entries;\n+\t\tposn = posn->next;\n+\t}\n+\tif (unpack_trees_rec(posns, len, \"\", fn, 0, 0, &indpos))\n \t\treturn -1;\n-\tret = read_tree(buffer, size, stage, NULL);\n-\tfree(buffer);\n-\treturn ret;\n+\n+\tcheck_updates(active_cache, active_nr);\n+\treturn 0;\n }\n \n-static int path_matches(struct cache_entry *a, struct cache_entry *b)\n+static int list_tree(unsigned char *sha1)\n {\n-\tint len = ce_namelen(a);\n-\treturn ce_namelen(b) == len &&\n-\t\t!memcmp(a->name, b->name, len);\n+\tstruct tree *tree = parse_tree_indirect(sha1);\n+\tif (!tree)\n+\t\treturn -1;\n+\tobject_list_append(&tree->object, &trees);\n+\treturn 0;\n }\n \n static int same(struct cache_entry *a, struct cache_entry *b)\n@@ -91,17 +343,6 @@ static void verify_uptodate(struct cache\n \tdie(\"Entry '%s' not uptodate. Cannot merge.\", ce->name);\n }\n \n-/*\n- * If the old tree contained a CE that isn't even in the\n- * result, that's always a problem, regardless of whether\n- * it's up-to-date or not (ie it can be a file that we\n- * have updated but not committed yet).\n- */\n-static void reject_merge(struct cache_entry *ce)\n-{\n-\tdie(\"Entry '%s' would be overwritten by merge. Cannot merge.\", ce->name);\n-}\n-\n static int merged_entry_internal(struct cache_entry *merge, struct cache_entry *old, struct cache_entry **dst, int allow_dirty)\n {\n \tmerge->ce_flags |= htons(CE_UPDATE);\n@@ -120,7 +361,7 @@ static int merged_entry_internal(struct \n \t\t}\n \t}\n \tmerge->ce_flags &= ~htons(CE_STAGEMASK);\n-\t*dst++ = merge;\n+\tadd_cache_entry(merge, ADD_CACHE_OK_TO_ADD);\n \treturn 1;\n }\n \n@@ -139,76 +380,19 @@ static int deleted_entry(struct cache_en\n \tif (old)\n \t\tverify_uptodate(old);\n \tce->ce_mode = 0;\n-\t*dst++ = ce;\n+\tadd_cache_entry(ce, ADD_CACHE_OK_TO_ADD);\n \treturn 1;\n }\n \n-static int causes_df_conflict(struct cache_entry *ce, int stage,\n-\t\t\t      struct cache_entry **dst_,\n-\t\t\t      struct cache_entry **next_,\n-\t\t\t      int tail)\n-{\n-\t/* This is called during the merge operation and walking\n-\t * the active_cache[] array is messy, because it is in the\n-\t * middle of overlapping copy operation.  The invariants\n-\t * are:\n-\t * (1) active_cache points at the first (zeroth) entry.\n-\t * (2) up to dst pointer are resolved entries.\n-\t * (3) from the next pointer (head-inclusive) to the tail\n-\t *     of the active_cache array have the remaining paths\n-\t *     to be processed.  There can be a gap between dst\n-\t *     and next.  Note that next is called \"src\" in the\n-\t *     merge_cache() function, and tail is the original\n-\t *     end of active_cache array when merge_cache() started.\n-\t * (4) the path corresponding to *ce is not found in (2)\n-\t *     or (3).  It is in the gap.\n-\t *\n-\t *  active_cache -----......+++++++++++++.\n-\t *                    ^dst  ^next        ^tail\n-\t */\n-\tint i, next, dst;\n-\tconst char *path = ce->name;\n-\tint namelen = ce_namelen(ce);\n-\n-\tnext = next_ - active_cache;\n-\tdst = dst_ - active_cache;\n-\n-\tfor (i = 0; i < tail; i++) {\n-\t\tint entlen, len;\n-\t\tconst char *one, *two;\n-\t\tif (dst <= i && i < next)\n-\t\t\tcontinue;\n-\t\tce = active_cache[i];\n-\t\tif (ce_stage(ce) != stage)\n-\t\t\tcontinue;\n-\t\t/* If ce->name is a prefix of path, then path is a file\n-\t\t * that hangs underneath ce->name, which is bad.\n-\t\t * If path is a prefix of ce->name, then it is the\n-\t\t * other way around which also is bad.\n-\t\t */\n-\t\tentlen = ce_namelen(ce);\n-\t\tif (namelen == entlen)\n-\t\t\tcontinue;\n-\t\tif (namelen < entlen) {\n-\t\t\tlen = namelen;\n-\t\t\tone = path;\n-\t\t\ttwo = ce->name;\n-\t\t} else {\n-\t\t\tlen = entlen;\n-\t\t\tone = ce->name;\n-\t\t\ttwo = path;\n-\t\t}\n-\t\tif (memcmp(one, two, len))\n-\t\t\tcontinue;\n-\t\tif (two[len] == '/')\n-\t\t\treturn 1;\n-\t}\n-\treturn 0;\n+static int keep_entry(struct cache_entry *ce, struct cache_entry **dst)\n+{\n+\tadd_cache_entry(ce, ADD_CACHE_OK_TO_ADD);\n+\treturn 1;\n }\n \n static int threeway_merge(struct cache_entry *stages[4],\n \t\t\t  struct cache_entry **dst,\n-\t\t\t  struct cache_entry **next, int tail)\n+\t\t\t  int df_conflict_2, int df_conflict_3)\n {\n \tstruct cache_entry *old = stages[0];\n \tstruct cache_entry *a = stages[1], *b = stages[2], *c = stages[3];\n@@ -218,7 +402,7 @@ static int threeway_merge(struct cache_e\n \t/* #5ALT */\n \tif (!a && b && c && same(b, c)) {\n \t\tif (old && !same(b, old))\n-\t\t\treturn -1;\n+\t\t\treject_merge(old);\n \t\treturn merged_entry_allow_dirty(b, old, dst);\n \t}\n \t/* #2ALT and #3ALT */\n@@ -269,12 +453,12 @@ static int threeway_merge(struct cache_e\n \t\t *     and vice versa.\n \t\t */\n \t\tif (c) { /* #2ALT */\n-\t\t\tif (!causes_df_conflict(c, 2, dst, next, tail) &&\n+\t\t\tif (!df_conflict_2 &&\n \t\t\t    (!old || same(c, old)))\n \t\t\t\treturn merged_entry_allow_dirty(c, old, dst);\n \t\t}\n \t\telse { /* #3ALT */\n-\t\t\tif (!causes_df_conflict(b, 3, dst, next, tail) &&\n+\t\t\tif (!df_conflict_3 &&\n \t\t\t    (!old || same(b, old)))\n \t\t\t\treturn merged_entry_allow_dirty(b, old, dst);\n \t\t}\n@@ -293,7 +477,7 @@ static int threeway_merge(struct cache_e\n \t */\n \tif (old) {\n \t\tif (!b || !same(old, b))\n-\t\t\treturn -1;\n+\t\t\treject_merge(old);\n \t}\n \tmerge = merge_entries(a, b, c);\n \tif (merge)\n@@ -301,9 +485,9 @@ static int threeway_merge(struct cache_e\n \tif (old)\n \t\tverify_uptodate(old);\n \tcount = 0;\n-\tif (a) { *dst++ = a; count++; }\n-\tif (b) { *dst++ = b; count++; }\n-\tif (c) { *dst++ = c; count++; }\n+\tif (a) { count += keep_entry(a, dst + count); }\n+\tif (b) { count += keep_entry(b, dst + count); }\n+\tif (c) { count += keep_entry(c, dst + count); }\n \treturn count;\n }\n \n@@ -317,14 +501,11 @@ static int threeway_merge(struct cache_e\n  *\n  */\n static int twoway_merge(struct cache_entry **src, struct cache_entry **dst,\n-\t\t\tstruct cache_entry **next, int tail)\n+\t\t\tint df23, int df32)\n {\n \tstruct cache_entry *current = src[0];\n \tstruct cache_entry *oldtree = src[1], *newtree = src[2];\n \n-\tif (src[3])\n-\t\treturn -1;\n-\n \tif (current) {\n \t\tif ((!oldtree && !newtree) || /* 4 and 5 */\n \t\t    (!oldtree && newtree &&\n@@ -334,8 +515,7 @@ static int twoway_merge(struct cache_ent\n \t\t    (oldtree && newtree &&\n \t\t     !same(oldtree, newtree) && /* 18 and 19*/\n \t\t     same(current, newtree))) {\n-\t\t\t*dst++ = current;\n-\t\t\treturn 1;\n+\t\t\treturn keep_entry(current, dst);\n \t\t}\n \t\telse if (oldtree && !newtree && same(current, oldtree)) {\n \t\t\t/* 10 or 11 */\n@@ -346,9 +526,16 @@ static int twoway_merge(struct cache_ent\n \t\t\t/* 20 or 21 */\n \t\t\treturn merged_entry(newtree, current, dst);\n \t\t}\n-\t\telse\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, dst);\n@@ -357,137 +544,25 @@ static int twoway_merge(struct cache_ent\n }\n \n /*\n- * Two-way merge emulated with three-way merge.\n- *\n- * This treats \"read-tree -m H M\" by transforming it internally\n- * into \"read-tree -m H I+H M\", where I+H is a tree that would\n- * contain the contents of the current index file, overlayed on\n- * top of H.  Unlike the traditional two-way merge, this leaves\n- * the stages in the resulting index file and lets the user resolve\n- * the merge conflicts using standard tools for three-way merge.\n- *\n- * This function is just to set-up such an arrangement, and the\n- * actual merge uses threeway_merge() function.\n- */\n-static void setup_emu23(void)\n-{\n-\t/* stage0 contains I, stage1 H, stage2 M.\n-\t * move stage2 to stage3, and create stage2 entries\n-\t * by scanning stage0 and stage1 entries.\n-\t */\n-\tint i, namelen, size;\n-\tstruct cache_entry *ce, *stage2;\n-\n-\tfor (i = 0; i < active_nr; i++) {\n-\t\tce = active_cache[i];\n-\t\tif (ce_stage(ce) != 2)\n-\t\t\tcontinue;\n-\t\t/* hoist them up to stage 3 */\n-\t\tnamelen = ce_namelen(ce);\n-\t\tce->ce_flags = create_ce_flags(namelen, 3);\n-\t}\n-\n-\tfor (i = 0; i < active_nr; i++) {\n-\t\tce = active_cache[i];\n-\t\tif (ce_stage(ce) > 1)\n-\t\t\tcontinue;\n-\t\tnamelen = ce_namelen(ce);\n-\t\tsize = cache_entry_size(namelen);\n-\t\tstage2 = xmalloc(size);\n-\t\tmemcpy(stage2, ce, size);\n-\t\tstage2->ce_flags = create_ce_flags(namelen, 2);\n-\t\tif (add_cache_entry(stage2, ADD_CACHE_OK_TO_ADD) < 0)\n-\t\t\tdie(\"cannot merge index and our head tree\");\n-\n-\t\t/* We are done with this name, so skip to next name */\n-\t\twhile (i < active_nr &&\n-\t\t       ce_namelen(active_cache[i]) == namelen &&\n-\t\t       !memcmp(active_cache[i]->name, ce->name, namelen))\n-\t\t\ti++;\n-\t\ti--; /* compensate for the loop control */\n-\t}\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, struct cache_entry **dst,\n-\t\t\tstruct cache_entry **next, int tail)\n+\t\t\tint df23, int df32)\n {\n \tstruct cache_entry *old = src[0];\n \tstruct cache_entry *a = src[1];\n \n-\tif (src[2] || src[3])\n-\t\treturn -1;\n-\n \tif (!a)\n \t\treturn 0;\n \tif (old && same(old, a)) {\n-\t\t*dst++ = old;\n-\t\treturn 1;\n+\t\treturn keep_entry(old, dst);\n \t}\n \treturn merged_entry(a, NULL, dst);\n }\n \n-static void check_updates(struct cache_entry **src, int nr)\n-{\n-\tstatic struct checkout state = {\n-\t\t.base_dir = \"\",\n-\t\t.force = 1,\n-\t\t.quiet = 1,\n-\t\t.refresh_cache = 1,\n-\t};\n-\tunsigned short mask = htons(CE_UPDATE);\n-\twhile (nr--) {\n-\t\tstruct cache_entry *ce = *src++;\n-\t\tif (!ce->ce_mode) {\n-\t\t\tif (update)\n-\t\t\t\tunlink(ce->name);\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (ce->ce_flags & mask) {\n-\t\t\tce->ce_flags &= ~mask;\n-\t\t\tif (update)\n-\t\t\t\tcheckout_entry(ce, &state);\n-\t\t}\n-\t}\n-}\n-\n-typedef int (*merge_fn_t)(struct cache_entry **, struct cache_entry **, struct cache_entry **, int);\n-\n-static void merge_cache(struct cache_entry **src, int nr, merge_fn_t fn)\n-{\n-\tstruct cache_entry **dst = src;\n-\tint tail = nr;\n-\n-\twhile (nr) {\n-\t\tint entries;\n-\t\tstruct cache_entry *name, *ce, *stages[4] = { NULL, };\n-\n-\t\tname = ce = *src;\n-\t\tfor (;;) {\n-\t\t\tint stage = ce_stage(ce);\n-\t\t\tstages[stage] = ce;\n-\t\t\tce = *++src;\n-\t\t\tactive_nr--;\n-\t\t\tif (!--nr)\n-\t\t\t\tbreak;\n-\t\t\tif (!path_matches(ce, name))\n-\t\t\t\tbreak;\n-\t\t}\n-\n-\t\tentries = fn(stages, dst, src, tail);\n-\t\tif (entries < 0)\n-\t\t\treject_merge(name);\n-\t\tdst += entries;\n-\t\tactive_nr += entries;\n-\t}\n-\tcheck_updates(active_cache, active_nr);\n-}\n-\n static int read_cache_unmerged(void)\n {\n \tint i, deleted;\n@@ -516,8 +591,9 @@ static struct cache_file cache_file;\n \n int main(int argc, char **argv)\n {\n-\tint i, newfd, merge, reset, emu23;\n+\tint i, newfd, reset, stage = 0;\n \tunsigned char sha1[20];\n+\tmerge_fn_t fn = NULL;\n \n \tnewfd = hold_index_file_for_update(&cache_file, get_index_file());\n \tif (newfd < 0)\n@@ -569,9 +645,7 @@ int main(int argc, char **argv)\n \n \t\tif (get_sha1(arg, sha1) < 0)\n \t\t\tusage(read_tree_usage);\n-\t\tif (stage > 3)\n-\t\t\tusage(read_tree_usage);\n-\t\tif (unpack_tree(sha1) < 0)\n+\t\tif (list_tree(sha1) < 0)\n \t\t\tdie(\"failed to unpack tree object %s\", arg);\n \t\tstage++;\n \t}\n@@ -583,19 +657,17 @@ int main(int argc, char **argv)\n \t\t\t[2] = twoway_merge,\n \t\t\t[3] = threeway_merge,\n \t\t};\n-\t\tmerge_fn_t fn;\n \n-\t\tif (stage < 2 || stage > 4)\n+\t\tif (stage < 2)\n \t\t\tdie(\"just how do you expect me to merge %d trees?\", stage-1);\n \t\tif (emu23 && stage != 3)\n \t\t\tdie(\"--emu23 takes only two trees\");\n \t\tfn = merge_function[stage-1];\n \t\tif (stage == 3 && emu23) { \n-\t\t\tsetup_emu23();\n \t\t\tfn = merge_function[3];\n \t\t}\n-\t\tmerge_cache(active_cache, active_nr, fn);\n \t}\n+\tunpack_trees(fn);\n \tif (write_cache(newfd, active_cache, active_nr) ||\n \t    commit_index_file(&cache_file))\n \t\tdie(\"unable to write new index file\");\n"},{"id":"7976","messageId":"Pine.LNX.4.63.0508311257480.23242@iabervon.org","threadId":"1703","inReplyTo":"tnxy86ikxan.fsf@arm.com","subject":"Re: [PATCH 0/2] Reorganize read-tree","fromName":"Daniel Barkalow","fromEmail":"barkalow@iabervon.org","sentAt":"2005-08-31T17:04:39Z","receivedAt":"2005-08-31T17:04:39Z","isPatch":true,"sender":{"key":"barkalow@iabervon.org","avatar":"https://avatars.githubusercontent.com/u/55364219?v=4"},"body":"On Wed, 31 Aug 2005, Catalin Marinas wrote:\n\n> Daniel Barkalow <barkalow@iabervon.org> wrote:\n> > I got mostly done with this before Linus mentioned the possibility of\n> > having multiple index entries in the same stage for a single path. I\n> > finished it anyway, but I'm not sure that we won't want to know which of\n> > the common ancestors contributed which, and, if some of them don't have a\n> > path, we wouldn't be able to tell.\n> \n> I don't have time to look at the patch and I don't have a good\n> knowledge of the GIT internals, so I will just ask. Does this patch\n> changes the call convention for git-merge-one-file-script? I have my\n> own script for StGIT and I would need to know whether it is affected\n> or not.\n\nNope, it only changes the trivial merge calling convention within \nread-tree.c; I think it's plausible that we might like to add information \nat some point, but the short-term goal is just to prevent a few bad cases \nin trivial merges.\n"},{"id":"8072","messageId":"7virxg6zj3.fsf@assigned-by-dhcp.cox.net","threadId":"1703","inReplyTo":"Pine.LNX.4.63.0508302317380.23242@iabervon.org","subject":"Re: [PATCH 0/2] Reorganize read-tree","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-09-05T02:04:16Z","receivedAt":"2005-09-05T02:04:16Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Daniel Barkalow <barkalow@iabervon.org> writes:\n\n> I got mostly done with this before Linus mentioned the possibility of\n> having multiple index entries in the same stage for a single path. I\n> finished it anyway, but I'm not sure that we won't want to know which of\n> the common ancestors contributed which, and, if some of them don't have a\n> path, we wouldn't be able to tell. The other advantages I see to this\n> approach are:\n\nI've finished reading your patch, after beating it reasonably\nheavily by feeding combinations of nonsense trees to make sure\nit produces the same result as the original implementation.  I\nhave not found any regression from the read-tree in \"master\"\nbranch, after you fixed the path ordering issues.\n\n> There are various potential refinements, plus removing a bunch of memory\n> leaks, still to do, but I think this is sufficiently close to review.\n\nI am not so worried about the leaks right now; they are\nsomething that could be fixed before it hits the \"master\"\nbranch.\n\nI like your approach of reading the input trees, along with the\nexisting index contents, and re-populating the index one path at\na time.  It probably is more readable.\n\nI further think that you can get the best of both worlds, by\ninventing a convention that mode=0 entry means 'this path does\nnot exist in this tree'. This would allow you to have multiple\nentries at the same stage and still tell which one came from\nwhich tree.  Instead of calling fn in unpack_trees(), you could\nmake it only unpack the tree into the index, and then after\nunpacking is done, call fn() repeatedly to resolve the resulting\nindex.  Of course the semantics of merge_fn_t needs to change\nbut if you feed N trees, the caller of a merge_fn_t function\nneeds to pick up the first N entries (because you use mode=0\nentry to mean 'missing from this tree', each path will always\nhave N entries) and feed them to the merge function in one call,\nthen pick up the next N entries and feed them in the next call,\nand so on.  I think that would simplify that part of the code\neven further.\n\nSo if you are making an octopus of 4 trees (one being our\ncurrent branch) using 2 merge bases, your intermediate index\nwould look like:\n\n    mode   SHA1   stage path\n    100644 XXXXX  0     foo\tfrom original index\n    000000 0{40}  1     foo\tmerge base #1 did not have foo\n    100644 ZZZZZ  1     foo\tmerge base #2 has it\n    100644 XXXXX  2     foo\tour current head\n    100644 ZZZZZ  3     foo\tother head #1 being merged into us\n    100644 YYYYY  3     foo\tother head #2 being merged into us\n    100644 ZZZZZ  3     foo\tother head #3 being merged into us\n\nWe cannot write something like this out without breaking\nbackward compatibility, but I personally think this breakage is\nOK, because what is being broken is the index file format, not\ntree object format, and the index file is by definition local to\na repository.  IOW, it is not too much to ask people not to use\nold tools to read new index file they created using new tools.\n"},{"id":"8073","messageId":"Pine.LNX.4.63.0509042238420.23242@iabervon.org","threadId":"1703","inReplyTo":"7virxg6zj3.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 0/2] Reorganize read-tree","fromName":"Daniel Barkalow","fromEmail":"barkalow@iabervon.org","sentAt":"2005-09-05T02:55:43Z","receivedAt":"2005-09-05T02:55:43Z","isPatch":true,"sender":{"key":"barkalow@iabervon.org","avatar":"https://avatars.githubusercontent.com/u/55364219?v=4"},"body":"On Sun, 4 Sep 2005, Junio C Hamano wrote:\n\n> Daniel Barkalow <barkalow@iabervon.org> writes:\n> \n> > I got mostly done with this before Linus mentioned the possibility of\n> > having multiple index entries in the same stage for a single path. I\n> > finished it anyway, but I'm not sure that we won't want to know which of\n> > the common ancestors contributed which, and, if some of them don't have a\n> > path, we wouldn't be able to tell. The other advantages I see to this\n> > approach are:\n> \n> I've finished reading your patch, after beating it reasonably\n> heavily by feeding combinations of nonsense trees to make sure\n> it produces the same result as the original implementation.  I\n> have not found any regression from the read-tree in \"master\"\n> branch, after you fixed the path ordering issues.\n\nGood.\n\n> > There are various potential refinements, plus removing a bunch of memory\n> > leaks, still to do, but I think this is sufficiently close to review.\n> \n> I am not so worried about the leaks right now; they are\n> something that could be fixed before it hits the \"master\"\n> branch.\n\nRight.\n\n> I like your approach of reading the input trees, along with the\n> existing index contents, and re-populating the index one path at\n> a time.  It probably is more readable.\n> \n> I further think that you can get the best of both worlds, by\n> inventing a convention that mode=0 entry means 'this path does\n> not exist in this tree'. This would allow you to have multiple\n> entries at the same stage and still tell which one came from\n> which tree.  Instead of calling fn in unpack_trees(), you could\n> make it only unpack the tree into the index, and then after\n> unpacking is done, call fn() repeatedly to resolve the resulting\n> index. \n\nI think that almost all of the benefit actually comes from calling fn() in \nunpack_trees() and not putting anything in the index before merging. \nWithout that, you need the complex index management and the complicated \nsearch for DF conflicts. The main point of not reading everything into the \nindex before calling fn() on stuff is that the index is actually really \ndifficult to deal with in this situation (because you are simultaneously \nmoving through it, removing and modifying entries, and searching it for \nconflicts). The improvement in readability comes from not doing this.\n\n\t-Daniel\n*This .sig left intentionally blank*\n"}]}