{"thread":{"id":"5046","subject":"[PATCH 1/3] read-trees: refactor the unpack_trees() part","startedAt":"2006-07-30T18:25:18Z","lastAt":"2006-07-31T00:48:34Z","messageCount":4,"participants":["Johannes Schindelin","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"24380","messageId":"Pine.LNX.4.63.0607302024090.29667@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"5046","inReplyTo":null,"subject":"[PATCH 1/3] read-trees: refactor the unpack_trees() part","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-07-30T18:25:18Z","receivedAt":"2006-07-30T18:25:18Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nBasically, the options are passed by a struct unpack_trees_options now.\nThat's all.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n\n\tIt is a real pity we do not have \"subcopies\" in our diff file \n\tformat yet, because \"copy\" and \"rename \" do not help in this case.\n\n Makefile            |    4 \n builtin-read-tree.c |  571 ++++++++-------------------------------------------\n unpack-trees.c      |  394 +++++++++++++++++++++++++++++++++++\n unpack-trees.h      |   30 +++\n 4 files changed, 517 insertions(+), 482 deletions(-)\n\ndiff --git a/Makefile b/Makefile\nindex c7b161b..4de2011 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -232,7 +232,7 @@ LIB_H = \\\n \tblob.h cache.h commit.h csum-file.h delta.h \\\n \tdiff.h object.h pack.h pkt-line.h quote.h refs.h \\\n \trun-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \\\n-\ttree-walk.h log-tree.h dir.h path-list.h\n+\ttree-walk.h log-tree.h dir.h path-list.h unpack-trees.h\n \n DIFF_OBJS = \\\n \tdiff.o diff-lib.o diffcore-break.o diffcore-order.o \\\n@@ -247,7 +247,7 @@ LIB_OBJS = \\\n \tserver-info.o setup.o sha1_file.o sha1_name.o strbuf.o \\\n \ttag.o tree.o usage.o config.o environment.o ctype.o copy.o \\\n \tfetch-clone.o revision.o pager.o tree-walk.o xdiff-interface.o \\\n-\talloc.o merge-file.o path-list.o $(DIFF_OBJS)\n+\talloc.o merge-file.o path-list.o unpack-trees.o $(DIFF_OBJS)\n \n BUILTIN_OBJS = \\\n \tbuiltin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \\\ndiff --git a/builtin-read-tree.c b/builtin-read-tree.c\nindex 49c10bf..a9852c7 100644\n--- a/builtin-read-tree.c\n+++ b/builtin-read-tree.c\n@@ -11,411 +11,17 @@ #include \"object.h\"\n #include \"tree.h\"\n #include \"tree-walk.h\"\n #include \"cache-tree.h\"\n-#include <sys/time.h>\n-#include <signal.h>\n+#include \"unpack-trees.h\"\n #include \"builtin.h\"\n \n-static int reset = 0;\n-static int merge = 0;\n-static int update = 0;\n-static int index_only = 0;\n-static int nontrivial_merge = 0;\n-static int trivial_merges_only = 0;\n-static int aggressive = 0;\n-static int verbose_update = 0;\n-static volatile int progress_update = 0;\n-static const char *prefix = NULL;\n-\n-static int head_idx = -1;\n-static int merge_size = 0;\n-\n static struct object_list *trees = NULL;\n \n-static struct cache_entry df_conflict_entry;\n-\n-struct tree_entry_list {\n-\tstruct tree_entry_list *next;\n-\tunsigned directory : 1;\n-\tunsigned executable : 1;\n-\tunsigned symlink : 1;\n-\tunsigned int mode;\n-\tconst char *name;\n-\tconst unsigned char *sha1;\n-};\n-\n-static struct tree_entry_list df_conflict_list;\n-\n-typedef int (*merge_fn_t)(struct cache_entry **src);\n-\n-static struct tree_entry_list *create_tree_entry_list(struct tree *tree)\n-{\n-\tstruct tree_desc desc;\n-\tstruct name_entry one;\n-\tstruct tree_entry_list *ret = NULL;\n-\tstruct tree_entry_list **list_p = &ret;\n-\n-\tdesc.buf = tree->buffer;\n-\tdesc.size = tree->size;\n-\n-\twhile (tree_entry(&desc, &one)) {\n-\t\tstruct tree_entry_list *entry;\n-\n-\t\tentry = xmalloc(sizeof(struct tree_entry_list));\n-\t\tentry->name = one.path;\n-\t\tentry->sha1 = one.sha1;\n-\t\tentry->mode = one.mode;\n-\t\tentry->directory = S_ISDIR(one.mode) != 0;\n-\t\tentry->executable = (one.mode & S_IXUSR) != 0;\n-\t\tentry->symlink = S_ISLNK(one.mode) != 0;\n-\t\tentry->next = NULL;\n-\n-\t\t*list_p = entry;\n-\t\tlist_p = &entry->next;\n-\t}\n-\treturn ret;\n-}\n-\n-static int entcmp(const char *name1, int dir1, const char *name2, int dir2)\n-{\n-\tint len1 = strlen(name1);\n-\tint len2 = strlen(name2);\n-\tint len = len1 < len2 ? len1 : len2;\n-\tint ret = memcmp(name1, name2, len);\n-\tunsigned char c1, c2;\n-\tif (ret)\n-\t\treturn ret;\n-\tc1 = name1[len];\n-\tc2 = name2[len];\n-\tif (!c1 && dir1)\n-\t\tc1 = '/';\n-\tif (!c2 && dir2)\n-\t\tc2 = '/';\n-\tret = (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;\n-\tif (c1 && c2 && !ret)\n-\t\tret = len1 - len2;\n-\treturn ret;\n-}\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, int *indpos)\n-{\n-\tint baselen = strlen(base);\n-\tint src_size = len + 1;\n-\tdo {\n-\t\tint i;\n-\t\tconst char *first;\n-\t\tint firstdir = 0;\n-\t\tint pathlen;\n-\t\tunsigned ce_size;\n-\t\tstruct tree_entry_list **subposns;\n-\t\tstruct cache_entry **src;\n-\t\tint any_files = 0;\n-\t\tint any_dirs = 0;\n-\t\tchar *cache_name;\n-\t\tint ce_stage;\n-\n-\t\t/* Find the first name in the input. */\n-\n-\t\tfirst = NULL;\n-\t\tcache_name = NULL;\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-#if DBRT_DEBUG > 1\n-\t\tif (first)\n-\t\t\tprintf(\"index %s\\n\", first);\n-#endif\n-\t\tfor (i = 0; i < len; i++) {\n-\t\t\tif (!posns[i] || posns[i] == &df_conflict_list)\n-\t\t\t\tcontinue;\n-#if DBRT_DEBUG > 1\n-\t\t\tprintf(\"%d %s\\n\", i + 1, posns[i]->name);\n-#endif\n-\t\t\tif (!first || entcmp(first, firstdir,\n-\t\t\t\t\t     posns[i]->name, \n-\t\t\t\t\t     posns[i]->directory) > 0) {\n-\t\t\t\tfirst = posns[i]->name;\n-\t\t\t\tfirstdir = posns[i]->directory;\n-\t\t\t}\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\tsrc = xcalloc(src_size, sizeof(struct cache_entry *));\n-\n-\t\tsubposns = xcalloc(len, sizeof(struct tree_list_entry *));\n-\n-\t\tif (cache_name && !strcmp(cache_name, first)) {\n-\t\t\tany_files = 1;\n-\t\t\tsrc[0] = active_cache[*indpos];\n-\t\t\tremove_cache_entry_at(*indpos);\n-\t\t}\n-\n-\t\tfor (i = 0; i < len; i++) {\n-\t\t\tstruct cache_entry *ce;\n-\n-\t\t\tif (!posns[i] ||\n-\t\t\t    (posns[i] != &df_conflict_list &&\n-\t\t\t     strcmp(first, posns[i]->name))) {\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\n-\t\t\tif (posns[i] == &df_conflict_list) {\n-\t\t\t\tsrc[i + merge] = &df_conflict_entry;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\n-\t\t\tif (posns[i]->directory) {\n-\t\t\t\tstruct tree *tree = lookup_tree(posns[i]->sha1);\n-\t\t\t\tany_dirs = 1;\n-\t\t\t\tparse_tree(tree);\n-\t\t\t\tsubposns[i] = create_tree_entry_list(tree);\n-\t\t\t\tposns[i] = posns[i]->next;\n-\t\t\t\tsrc[i + merge] = &df_conflict_entry;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\n-\t\t\tif (!merge)\n-\t\t\t\tce_stage = 0;\n-\t\t\telse if (i + 1 < head_idx)\n-\t\t\t\tce_stage = 1;\n-\t\t\telse if (i + 1 > head_idx)\n-\t\t\t\tce_stage = 3;\n-\t\t\telse\n-\t\t\t\tce_stage = 2;\n-\n-\t\t\tce = xcalloc(1, 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\tany_files = 1;\n-\n-\t\t\tmemcpy(ce->sha1, posns[i]->sha1, 20);\n-\t\t\tsrc[i + merge] = ce;\n-\t\t\tsubposns[i] = &df_conflict_list;\n-\t\t\tposns[i] = posns[i]->next;\n-\t\t}\n-\t\tif (any_files) {\n-\t\t\tif (merge) {\n-\t\t\t\tint ret;\n-\n-#if DBRT_DEBUG > 1\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-#endif\n-\t\t\t\tret = fn(src);\n-\t\t\t\t\n-#if DBRT_DEBUG > 1\n-\t\t\t\tprintf(\"Added %d entries\\n\", ret);\n-#endif\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 (any_dirs) {\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     indpos))\n-\t\t\t\treturn -1;\n-\t\t\tfree(newbase);\n-\t\t}\n-\t\tfree(subposns);\n-\t\tfree(src);\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-/* Unlink the last component and attempt to remove leading\n- * directories, in case this unlink is the removal of the\n- * last entry in the directory -- empty directories are removed.\n- */\n-static void unlink_entry(char *name)\n-{\n-\tchar *cp, *prev;\n-\n-\tif (unlink(name))\n-\t\treturn;\n-\tprev = NULL;\n-\twhile (1) {\n-\t\tint status;\n-\t\tcp = strrchr(name, '/');\n-\t\tif (prev)\n-\t\t\t*prev = '/';\n-\t\tif (!cp)\n-\t\t\tbreak;\n-\n-\t\t*cp = 0;\n-\t\tstatus = rmdir(name);\n-\t\tif (status) {\n-\t\t\t*cp = '/';\n-\t\t\tbreak;\n-\t\t}\n-\t\tprev = cp;\n-\t}\n-}\n-\n-static void progress_interval(int signum)\n-{\n-\tprogress_update = 1;\n-}\n-\n-static void setup_progress_signal(void)\n-{\n-\tstruct sigaction sa;\n-\tstruct itimerval v;\n-\n-\tmemset(&sa, 0, sizeof(sa));\n-\tsa.sa_handler = progress_interval;\n-\tsigemptyset(&sa.sa_mask);\n-\tsa.sa_flags = SA_RESTART;\n-\tsigaction(SIGALRM, &sa, NULL);\n-\n-\tv.it_interval.tv_sec = 1;\n-\tv.it_interval.tv_usec = 0;\n-\tv.it_value = v.it_interval;\n-\tsetitimer(ITIMER_REAL, &v, NULL);\n-}\n-\n-static struct checkout state;\n-static void check_updates(struct cache_entry **src, int nr)\n-{\n-\tunsigned short mask = htons(CE_UPDATE);\n-\tunsigned last_percent = 200, cnt = 0, total = 0;\n-\n-\tif (update && verbose_update) {\n-\t\tfor (total = cnt = 0; cnt < nr; cnt++) {\n-\t\t\tstruct cache_entry *ce = src[cnt];\n-\t\t\tif (!ce->ce_mode || ce->ce_flags & mask)\n-\t\t\t\ttotal++;\n-\t\t}\n-\n-\t\t/* Don't bother doing this for very small updates */\n-\t\tif (total < 250)\n-\t\t\ttotal = 0;\n-\n-\t\tif (total) {\n-\t\t\tfprintf(stderr, \"Checking files out...\\n\");\n-\t\t\tsetup_progress_signal();\n-\t\t\tprogress_update = 1;\n-\t\t}\n-\t\tcnt = 0;\n-\t}\n-\n-\twhile (nr--) {\n-\t\tstruct cache_entry *ce = *src++;\n-\n-\t\tif (total) {\n-\t\t\tif (!ce->ce_mode || ce->ce_flags & mask) {\n-\t\t\t\tunsigned percent;\n-\t\t\t\tcnt++;\n-\t\t\t\tpercent = (cnt * 100) / total;\n-\t\t\t\tif (percent != last_percent ||\n-\t\t\t\t    progress_update) {\n-\t\t\t\t\tfprintf(stderr, \"%4u%% (%u/%u) done\\r\",\n-\t\t\t\t\t\tpercent, cnt, total);\n-\t\t\t\t\tlast_percent = percent;\n-\t\t\t\t\tprogress_update = 0;\n-\t\t\t\t}\n-\t\t\t}\n-\t\t}\n-\t\tif (!ce->ce_mode) {\n-\t\t\tif (update)\n-\t\t\t\tunlink_entry(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, NULL);\n-\t\t}\n-\t}\n-\tif (total) {\n-\t\tsignal(SIGALRM, SIG_IGN);\n-\t\tfputc('\\n', stderr);\n-\t}\n-}\n-\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-\tint i;\n-\tstruct object_list *posn = trees;\n-\tmerge_size = len;\n-\n-\tif (len) {\n-\t\tposns = xmalloc(len * sizeof(struct tree_entry_list *));\n-\t\tfor (i = 0; i < len; i++) {\n-\t\t\tposns[i] = create_tree_entry_list((struct tree *) posn->item);\n-\t\t\tposn = posn->next;\n-\t\t}\n-\t\tif (unpack_trees_rec(posns, len, prefix ? prefix : \"\",\n-\t\t\t\t     fn, &indpos))\n-\t\t\treturn -1;\n-\t}\n-\n-\tif (trivial_merges_only && nontrivial_merge)\n-\t\tdie(\"Merge requires file-level merging\");\n-\n-\tcheck_updates(active_cache, active_nr);\n-\treturn 0;\n-}\n-\n static int list_tree(unsigned char *sha1)\n {\n \tstruct tree *tree = parse_tree_indirect(sha1);\n@@ -440,11 +46,12 @@ static int same(struct cache_entry *a, s\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+static void verify_uptodate(struct cache_entry *ce,\n+\t\tstruct unpack_trees_options *o)\n {\n \tstruct stat st;\n \n-\tif (index_only || reset)\n+\tif (o->index_only || o->reset)\n \t\treturn;\n \n \tif (!lstat(ce->name, &st)) {\n@@ -453,7 +60,7 @@ static void verify_uptodate(struct cache\n \t\t\treturn;\n \t\terrno = 0;\n \t}\n-\tif (reset) {\n+\tif (o->reset) {\n \t\tce->ce_flags |= htons(CE_UPDATE);\n \t\treturn;\n \t}\n@@ -472,18 +79,20 @@ static void invalidate_ce_path(struct ca\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+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 (index_only || reset || !update)\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+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@@ -497,12 +106,12 @@ static int merged_entry(struct cache_ent\n \t\tif (same(old, merge)) {\n \t\t\t*merge = *old;\n \t\t} else {\n-\t\t\tverify_uptodate(old);\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\");\n+\t\tverify_absent(merge->name, \"overwritten\", o);\n \t\tinvalidate_ce_path(merge);\n \t}\n \n@@ -511,12 +120,13 @@ static int merged_entry(struct cache_ent\n \treturn 1;\n }\n \n-static int deleted_entry(struct cache_entry *ce, struct cache_entry *old)\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);\n+\t\tverify_uptodate(old, o);\n \telse\n-\t\tverify_absent(ce->name, \"removed\");\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@@ -545,11 +155,12 @@ static void show_stage_entry(FILE *o,\n }\n #endif\n \n-static int threeway_merge(struct cache_entry **stages)\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[head_idx + 1];\n+\tstruct cache_entry *remote = stages[o->head_idx + 1];\n \tint count;\n \tint head_match = 0;\n \tint remote_match = 0;\n@@ -562,7 +173,7 @@ static int threeway_merge(struct cache_e\n \tint no_anc_exists = 1;\n \tint i;\n \n-\tfor (i = 1; i < head_idx; i++) {\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@@ -573,14 +184,14 @@ static int threeway_merge(struct cache_e\n \t}\n \n \tindex = stages[0];\n-\thead = stages[head_idx];\n+\thead = stages[o->head_idx];\n \n-\tif (head == &df_conflict_entry) {\n+\tif (head == &o->df_conflict_entry) {\n \t\tdf_conflict_head = 1;\n \t\thead = NULL;\n \t}\n \n-\tif (remote == &df_conflict_entry) {\n+\tif (remote == &o->df_conflict_entry) {\n \t\tdf_conflict_remote = 1;\n \t\tremote = NULL;\n \t}\n@@ -596,7 +207,7 @@ static int threeway_merge(struct cache_e\n \t * and #14.\n \t */\n \tif (!same(remote, head)) {\n-\t\tfor (i = 1; i < head_idx; i++) {\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@@ -614,7 +225,7 @@ static int threeway_merge(struct cache_e\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);\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@@ -627,10 +238,10 @@ static int threeway_merge(struct cache_e\n \tif (head) {\n \t\t/* #5ALT, #15 */\n \t\tif (same(head, remote))\n-\t\t\treturn merged_entry(head, index);\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);\n+\t\t\treturn merged_entry(head, index, o);\n \t}\n \n \t/* #1 */\n@@ -640,7 +251,7 @@ static int threeway_merge(struct cache_e\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 (aggressive) {\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@@ -651,16 +262,16 @@ static int threeway_merge(struct cache_e\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);\n+\t\t\t\treturn deleted_entry(index, index, o);\n \t\t\telse if (path)\n-\t\t\t\tverify_absent(path, \"removed\");\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);\n+\t\t\treturn merged_entry(head, index, o);\n \n \t}\n \n@@ -669,17 +280,17 @@ static int threeway_merge(struct cache_e\n \t * conflict resolution files. \n \t */\n \tif (index) {\n-\t\tverify_uptodate(index);\n+\t\tverify_uptodate(index, o);\n \t}\n \telse if (path)\n-\t\tverify_absent(path, \"overwritten\");\n+\t\tverify_absent(path, \"overwritten\", o);\n \n-\tnontrivial_merge = 1;\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 < head_idx; i++) {\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@@ -708,14 +319,15 @@ #endif\n  * \"carry forward\" rule, please see <Documentation/git-read-tree.txt>.\n  *\n  */\n-static int twoway_merge(struct cache_entry **src)\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 (merge_size != 2)\n+\tif (o->merge_size != 2)\n \t\treturn error(\"Cannot do a twoway merge of %d trees\",\n-\t\t\t     merge_size);\n+\t\t\t     o->merge_size);\n \n \tif (current) {\n \t\tif ((!oldtree && !newtree) || /* 4 and 5 */\n@@ -730,12 +342,12 @@ static int twoway_merge(struct cache_ent\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);\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);\n+\t\t\treturn merged_entry(newtree, current, o);\n \t\t}\n \t\telse {\n \t\t\t/* all other failures */\n@@ -749,9 +361,9 @@ static int twoway_merge(struct cache_ent\n \t\t}\n \t}\n \telse if (newtree)\n-\t\treturn merged_entry(newtree, current);\n+\t\treturn merged_entry(newtree, current, o);\n \telse\n-\t\treturn deleted_entry(oldtree, current);\n+\t\treturn deleted_entry(oldtree, current, o);\n }\n \n /*\n@@ -760,20 +372,21 @@ static int twoway_merge(struct cache_ent\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+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 (merge_size != 1)\n+\tif (o->merge_size != 1)\n \t\treturn error(\"Cannot do a bind merge of %d trees\\n\",\n-\t\t\t     merge_size);\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);\n+\t\treturn merged_entry(a, NULL, o);\n }\n \n /*\n@@ -782,19 +395,20 @@ static int bind_merge(struct cache_entry\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+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 (merge_size != 1)\n+\tif (o->merge_size != 1)\n \t\treturn error(\"Cannot do a oneway merge of %d trees\",\n-\t\t\t     merge_size);\n+\t\t\t     o->merge_size);\n \n \tif (!a)\n-\t\treturn deleted_entry(old, old);\n+\t\treturn deleted_entry(old, old, o);\n \tif (old && same(old, a)) {\n-\t\tif (reset) {\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@@ -802,7 +416,7 @@ static int oneway_merge(struct cache_ent\n \t\t}\n \t\treturn keep_entry(old);\n \t}\n-\treturn merged_entry(a, old);\n+\treturn merged_entry(a, old, o);\n }\n \n static int read_cache_unmerged(void)\n@@ -874,14 +488,12 @@ int cmd_read_tree(int argc, const char *\n {\n \tint i, newfd, stage = 0;\n \tunsigned char sha1[20];\n-\tmerge_fn_t fn = NULL;\n+\tstruct unpack_trees_options opts;\n \n-\tdf_conflict_list.next = &df_conflict_list;\n-\tstate.base_dir = \"\";\n-\tstate.force = 1;\n-\tstate.quiet = 1;\n-\tstate.refresh_cache = 1;\n+\tmemset(&opts, 0, sizeof(opts));\n+\topts.head_idx = -1;\n \n+\tsetup_git_directory();\n \tgit_config(git_default_config);\n \n \tnewfd = hold_lock_file_for_update(&lock_file, get_index_file());\n@@ -890,8 +502,6 @@ int cmd_read_tree(int argc, const char *\n \n \tgit_config(git_default_config);\n \n-\tmerge = 0;\n-\treset = 0;\n \tfor (i = 1; i < argc; i++) {\n \t\tconst char *arg = argv[i];\n \n@@ -899,12 +509,12 @@ int cmd_read_tree(int argc, const char *\n \t\t * the working tree.\n \t\t */\n \t\tif (!strcmp(arg, \"-u\")) {\n-\t\t\tupdate = 1;\n+\t\t\topts.update = 1;\n \t\t\tcontinue;\n \t\t}\n \n \t\tif (!strcmp(arg, \"-v\")) {\n-\t\t\tverbose_update = 1;\n+\t\t\topts.verbose_update = 1;\n \t\t\tcontinue;\n \t\t}\n \n@@ -912,7 +522,7 @@ int cmd_read_tree(int argc, const char *\n \t\t * not even look at the working tree.\n \t\t */\n \t\tif (!strcmp(arg, \"-i\")) {\n-\t\t\tindex_only = 1;\n+\t\t\topts.index_only = 1;\n \t\t\tcontinue;\n \t\t}\n \n@@ -921,10 +531,10 @@ int cmd_read_tree(int argc, const char *\n \t\t * given subdirectory.\n \t\t */\n \t\tif (!strncmp(arg, \"--prefix=\", 9)) {\n-\t\t\tif (stage || merge || prefix)\n+\t\t\tif (stage || opts.merge || opts.prefix)\n \t\t\t\tusage(read_tree_usage);\n-\t\t\tprefix = arg + 9;\n-\t\t\tmerge = 1;\n+\t\t\topts.prefix = arg + 9;\n+\t\t\topts.merge = 1;\n \t\t\tstage = 1;\n \t\t\tif (read_cache_unmerged())\n \t\t\t\tdie(\"you need to resolve your current index first\");\n@@ -936,38 +546,38 @@ int cmd_read_tree(int argc, const char *\n \t\t * correspond to them.\n \t\t */\n \t\tif (!strcmp(arg, \"--reset\")) {\n-\t\t\tif (stage || merge || prefix)\n+\t\t\tif (stage || opts.merge || opts.prefix)\n \t\t\t\tusage(read_tree_usage);\n-\t\t\treset = 1;\n-\t\t\tmerge = 1;\n+\t\t\topts.reset = 1;\n+\t\t\topts.merge = 1;\n \t\t\tstage = 1;\n \t\t\tread_cache_unmerged();\n \t\t\tcontinue;\n \t\t}\n \n \t\tif (!strcmp(arg, \"--trivial\")) {\n-\t\t\ttrivial_merges_only = 1;\n+\t\t\topts.trivial_merges_only = 1;\n \t\t\tcontinue;\n \t\t}\n \n \t\tif (!strcmp(arg, \"--aggressive\")) {\n-\t\t\taggressive = 1;\n+\t\t\topts.aggressive = 1;\n \t\t\tcontinue;\n \t\t}\n \n \t\t/* \"-m\" stands for \"merge\", meaning we start in stage 1 */\n \t\tif (!strcmp(arg, \"-m\")) {\n-\t\t\tif (stage || merge || prefix)\n+\t\t\tif (stage || opts.merge || opts.prefix)\n \t\t\t\tusage(read_tree_usage);\n \t\t\tif (read_cache_unmerged())\n \t\t\t\tdie(\"you need to resolve your current index first\");\n \t\t\tstage = 1;\n-\t\t\tmerge = 1;\n+\t\t\topts.merge = 1;\n \t\t\tcontinue;\n \t\t}\n \n \t\t/* using -u and -i at the same time makes no sense */\n-\t\tif (1 < index_only + update)\n+\t\tif (1 < opts.index_only + opts.update)\n \t\t\tusage(read_tree_usage);\n \n \t\tif (get_sha1(arg, sha1))\n@@ -976,52 +586,53 @@ int cmd_read_tree(int argc, const char *\n \t\t\tdie(\"failed to unpack tree object %s\", arg);\n \t\tstage++;\n \t}\n-\tif ((update||index_only) && !merge)\n+\tif ((opts.update||opts.index_only) && !opts.merge)\n \t\tusage(read_tree_usage);\n \n-\tif (prefix) {\n-\t\tint pfxlen = strlen(prefix);\n+\tif (opts.prefix) {\n+\t\tint pfxlen = strlen(opts.prefix);\n \t\tint pos;\n-\t\tif (prefix[pfxlen-1] != '/')\n+\t\tif (opts.prefix[pfxlen-1] != '/')\n \t\t\tdie(\"prefix must end with /\");\n \t\tif (stage != 2)\n \t\t\tdie(\"binding merge takes only one tree\");\n-\t\tpos = cache_name_pos(prefix, pfxlen);\n+\t\tpos = cache_name_pos(opts.prefix, pfxlen);\n \t\tif (0 <= pos)\n \t\t\tdie(\"corrupt index file\");\n \t\tpos = -pos-1;\n \t\tif (pos < active_nr &&\n-\t\t    !strncmp(active_cache[pos]->name, prefix, pfxlen))\n-\t\t\tdie(\"subdirectory '%s' already exists.\", prefix);\n-\t\tpos = cache_name_pos(prefix, pfxlen-1);\n+\t\t    !strncmp(active_cache[pos]->name, opts.prefix, pfxlen))\n+\t\t\tdie(\"subdirectory '%s' already exists.\", opts.prefix);\n+\t\tpos = cache_name_pos(opts.prefix, pfxlen-1);\n \t\tif (0 <= pos)\n-\t\t\tdie(\"file '%.*s' already exists.\", pfxlen-1, prefix);\n+\t\t\tdie(\"file '%.*s' already exists.\",\n+\t\t\t\t\tpfxlen-1, opts.prefix);\n \t}\n \n-\tif (merge) {\n+\tif (opts.merge) {\n \t\tif (stage < 2)\n \t\t\tdie(\"just how do you expect me to merge %d trees?\", stage-1);\n \t\tswitch (stage - 1) {\n \t\tcase 1:\n-\t\t\tfn = prefix ? bind_merge : oneway_merge;\n+\t\t\topts.fn = opts.prefix ? bind_merge : oneway_merge;\n \t\t\tbreak;\n \t\tcase 2:\n-\t\t\tfn = twoway_merge;\n+\t\t\topts.fn = twoway_merge;\n \t\t\tbreak;\n \t\tcase 3:\n \t\tdefault:\n-\t\t\tfn = threeway_merge;\n+\t\t\topts.fn = threeway_merge;\n \t\t\tcache_tree_free(&active_cache_tree);\n \t\t\tbreak;\n \t\t}\n \n \t\tif (stage - 1 >= 3)\n-\t\t\thead_idx = stage - 2;\n+\t\t\topts.head_idx = stage - 2;\n \t\telse\n-\t\t\thead_idx = 1;\n+\t\t\topts.head_idx = 1;\n \t}\n \n-\tunpack_trees(fn);\n+\tunpack_trees(trees, &opts);\n \n \t/*\n \t * When reading only one tree (either the most basic form,\n@@ -1029,7 +640,7 @@ int cmd_read_tree(int argc, const char *\n \t * valid cache-tree because the index must match exactly\n \t * what came from the tree.\n \t */\n-\tif (trees && trees->item && !prefix && (!merge || (stage == 2))) {\n+\tif (trees && trees->item && !opts.prefix && (!opts.merge || (stage == 2))) {\n \t\tcache_tree_free(&active_cache_tree);\n \t\tprime_cache_tree();\n \t}\ndiff --git a/unpack-trees.c b/unpack-trees.c\nnew file mode 100644\nindex 0000000..ecff92d\n--- /dev/null\n+++ b/unpack-trees.c\n@@ -0,0 +1,394 @@\n+#include <signal.h>\n+#include <sys/time.h>\n+#include \"cache.h\"\n+#include \"tree.h\"\n+#include \"tree-walk.h\"\n+#include \"unpack-trees.h\"\n+\n+struct tree_entry_list {\n+\tstruct tree_entry_list *next;\n+\tunsigned directory : 1;\n+\tunsigned executable : 1;\n+\tunsigned symlink : 1;\n+\tunsigned int mode;\n+\tconst char *name;\n+\tconst unsigned char *sha1;\n+};\n+\n+static struct tree_entry_list *create_tree_entry_list(struct tree *tree)\n+{\n+\tstruct tree_desc desc;\n+\tstruct name_entry one;\n+\tstruct tree_entry_list *ret = NULL;\n+\tstruct tree_entry_list **list_p = &ret;\n+\n+\tdesc.buf = tree->buffer;\n+\tdesc.size = tree->size;\n+\n+\twhile (tree_entry(&desc, &one)) {\n+\t\tstruct tree_entry_list *entry;\n+\n+\t\tentry = xmalloc(sizeof(struct tree_entry_list));\n+\t\tentry->name = one.path;\n+\t\tentry->sha1 = one.sha1;\n+\t\tentry->mode = one.mode;\n+\t\tentry->directory = S_ISDIR(one.mode) != 0;\n+\t\tentry->executable = (one.mode & S_IXUSR) != 0;\n+\t\tentry->symlink = S_ISLNK(one.mode) != 0;\n+\t\tentry->next = NULL;\n+\n+\t\t*list_p = entry;\n+\t\tlist_p = &entry->next;\n+\t}\n+\treturn ret;\n+}\n+\n+static int entcmp(const char *name1, int dir1, const char *name2, int dir2)\n+{\n+\tint len1 = strlen(name1);\n+\tint len2 = strlen(name2);\n+\tint len = len1 < len2 ? len1 : len2;\n+\tint ret = memcmp(name1, name2, len);\n+\tunsigned char c1, c2;\n+\tif (ret)\n+\t\treturn ret;\n+\tc1 = name1[len];\n+\tc2 = name2[len];\n+\tif (!c1 && dir1)\n+\t\tc1 = '/';\n+\tif (!c2 && dir2)\n+\t\tc2 = '/';\n+\tret = (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;\n+\tif (c1 && c2 && !ret)\n+\t\tret = len1 - len2;\n+\treturn ret;\n+}\n+\n+static int unpack_trees_rec(struct tree_entry_list **posns, int len,\n+\t\t\t    const char *base, struct unpack_trees_options *o,\n+\t\t\t    int *indpos,\n+\t\t\t    struct tree_entry_list *df_conflict_list)\n+{\n+\tint baselen = strlen(base);\n+\tint src_size = len + 1;\n+\tdo {\n+\t\tint i;\n+\t\tconst char *first;\n+\t\tint firstdir = 0;\n+\t\tint pathlen;\n+\t\tunsigned ce_size;\n+\t\tstruct tree_entry_list **subposns;\n+\t\tstruct cache_entry **src;\n+\t\tint any_files = 0;\n+\t\tint any_dirs = 0;\n+\t\tchar *cache_name;\n+\t\tint ce_stage;\n+\n+\t\t/* Find the first name in the input. */\n+\n+\t\tfirst = NULL;\n+\t\tcache_name = NULL;\n+\n+\t\t/* Check the cache */\n+\t\tif (o->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+#if DBRT_DEBUG > 1\n+\t\tif (first)\n+\t\t\tprintf(\"index %s\\n\", first);\n+#endif\n+\t\tfor (i = 0; i < len; i++) {\n+\t\t\tif (!posns[i] || posns[i] == df_conflict_list)\n+\t\t\t\tcontinue;\n+#if DBRT_DEBUG > 1\n+\t\t\tprintf(\"%d %s\\n\", i + 1, posns[i]->name);\n+#endif\n+\t\t\tif (!first || entcmp(first, firstdir,\n+\t\t\t\t\t     posns[i]->name, \n+\t\t\t\t\t     posns[i]->directory) > 0) {\n+\t\t\t\tfirst = posns[i]->name;\n+\t\t\t\tfirstdir = posns[i]->directory;\n+\t\t\t}\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\tsrc = xcalloc(src_size, sizeof(struct cache_entry *));\n+\n+\t\tsubposns = xcalloc(len, sizeof(struct tree_list_entry *));\n+\n+\t\tif (cache_name && !strcmp(cache_name, first)) {\n+\t\t\tany_files = 1;\n+\t\t\tsrc[0] = active_cache[*indpos];\n+\t\t\tremove_cache_entry_at(*indpos);\n+\t\t}\n+\n+\t\tfor (i = 0; i < len; i++) {\n+\t\t\tstruct cache_entry *ce;\n+\n+\t\t\tif (!posns[i] ||\n+\t\t\t    (posns[i] != df_conflict_list &&\n+\t\t\t     strcmp(first, posns[i]->name))) {\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\n+\t\t\tif (posns[i] == df_conflict_list) {\n+\t\t\t\tsrc[i + o->merge] = &o->df_conflict_entry;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\n+\t\t\tif (posns[i]->directory) {\n+\t\t\t\tstruct tree *tree = lookup_tree(posns[i]->sha1);\n+\t\t\t\tany_dirs = 1;\n+\t\t\t\tparse_tree(tree);\n+\t\t\t\tsubposns[i] = create_tree_entry_list(tree);\n+\t\t\t\tposns[i] = posns[i]->next;\n+\t\t\t\tsrc[i + o->merge] = &o->df_conflict_entry;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\n+\t\t\tif (!o->merge)\n+\t\t\t\tce_stage = 0;\n+\t\t\telse if (i + 1 < o->head_idx)\n+\t\t\t\tce_stage = 1;\n+\t\t\telse if (i + 1 > o->head_idx)\n+\t\t\t\tce_stage = 3;\n+\t\t\telse\n+\t\t\t\tce_stage = 2;\n+\n+\t\t\tce = xcalloc(1, 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\tany_files = 1;\n+\n+\t\t\tmemcpy(ce->sha1, posns[i]->sha1, 20);\n+\t\t\tsrc[i + o->merge] = ce;\n+\t\t\tsubposns[i] = df_conflict_list;\n+\t\t\tposns[i] = posns[i]->next;\n+\t\t}\n+\t\tif (any_files) {\n+\t\t\tif (o->merge) {\n+\t\t\t\tint ret;\n+\n+#if DBRT_DEBUG > 1\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+#endif\n+\t\t\t\tret = o->fn(src, o);\n+\t\t\t\t\n+#if DBRT_DEBUG > 1\n+\t\t\t\tprintf(\"Added %d entries\\n\", ret);\n+#endif\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 (any_dirs) {\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, o,\n+\t\t\t\t\t     indpos, df_conflict_list))\n+\t\t\t\treturn -1;\n+\t\t\tfree(newbase);\n+\t\t}\n+\t\tfree(subposns);\n+\t\tfree(src);\n+\t} while (1);\n+}\n+\n+/* Unlink the last component and attempt to remove leading\n+ * directories, in case this unlink is the removal of the\n+ * last entry in the directory -- empty directories are removed.\n+ */\n+static void unlink_entry(char *name)\n+{\n+\tchar *cp, *prev;\n+\n+\tif (unlink(name))\n+\t\treturn;\n+\tprev = NULL;\n+\twhile (1) {\n+\t\tint status;\n+\t\tcp = strrchr(name, '/');\n+\t\tif (prev)\n+\t\t\t*prev = '/';\n+\t\tif (!cp)\n+\t\t\tbreak;\n+\n+\t\t*cp = 0;\n+\t\tstatus = rmdir(name);\n+\t\tif (status) {\n+\t\t\t*cp = '/';\n+\t\t\tbreak;\n+\t\t}\n+\t\tprev = cp;\n+\t}\n+}\n+\n+static volatile int progress_update = 0;\n+\n+static void progress_interval(int signum)\n+{\n+\tprogress_update = 1;\n+}\n+\n+static void setup_progress_signal(void)\n+{\n+\tstruct sigaction sa;\n+\tstruct itimerval v;\n+\n+\tmemset(&sa, 0, sizeof(sa));\n+\tsa.sa_handler = progress_interval;\n+\tsigemptyset(&sa.sa_mask);\n+\tsa.sa_flags = SA_RESTART;\n+\tsigaction(SIGALRM, &sa, NULL);\n+\n+\tv.it_interval.tv_sec = 1;\n+\tv.it_interval.tv_usec = 0;\n+\tv.it_value = v.it_interval;\n+\tsetitimer(ITIMER_REAL, &v, NULL);\n+}\n+\n+static struct checkout state;\n+static void check_updates(struct cache_entry **src, int nr,\n+\t\tstruct unpack_trees_options *o)\n+{\n+\tunsigned short mask = htons(CE_UPDATE);\n+\tunsigned last_percent = 200, cnt = 0, total = 0;\n+\n+\tif (o->update && o->verbose_update) {\n+\t\tfor (total = cnt = 0; cnt < nr; cnt++) {\n+\t\t\tstruct cache_entry *ce = src[cnt];\n+\t\t\tif (!ce->ce_mode || ce->ce_flags & mask)\n+\t\t\t\ttotal++;\n+\t\t}\n+\n+\t\t/* Don't bother doing this for very small updates */\n+\t\tif (total < 250)\n+\t\t\ttotal = 0;\n+\n+\t\tif (total) {\n+\t\t\tfprintf(stderr, \"Checking files out...\\n\");\n+\t\t\tsetup_progress_signal();\n+\t\t\tprogress_update = 1;\n+\t\t}\n+\t\tcnt = 0;\n+\t}\n+\n+\twhile (nr--) {\n+\t\tstruct cache_entry *ce = *src++;\n+\n+\t\tif (total) {\n+\t\t\tif (!ce->ce_mode || ce->ce_flags & mask) {\n+\t\t\t\tunsigned percent;\n+\t\t\t\tcnt++;\n+\t\t\t\tpercent = (cnt * 100) / total;\n+\t\t\t\tif (percent != last_percent ||\n+\t\t\t\t    progress_update) {\n+\t\t\t\t\tfprintf(stderr, \"%4u%% (%u/%u) done\\r\",\n+\t\t\t\t\t\tpercent, cnt, total);\n+\t\t\t\t\tlast_percent = percent;\n+\t\t\t\t\tprogress_update = 0;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t\tif (!ce->ce_mode) {\n+\t\t\tif (o->update)\n+\t\t\t\tunlink_entry(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 (o->update)\n+\t\t\t\tcheckout_entry(ce, &state, NULL);\n+\t\t}\n+\t}\n+\tif (total) {\n+\t\tsignal(SIGALRM, SIG_IGN);\n+\t\tfputc('\\n', stderr);\n+\t}\n+}\n+\n+int unpack_trees(struct object_list *trees, struct unpack_trees_options *o)\n+{\n+\tint indpos = 0;\n+\tunsigned len = object_list_length(trees);\n+\tstruct tree_entry_list **posns;\n+\tint i;\n+\tstruct object_list *posn = trees;\n+\tstruct tree_entry_list df_conflict_list;\n+\n+\tdf_conflict_list.next = &df_conflict_list;\n+\tstate.base_dir = \"\";\n+\tstate.force = 1;\n+\tstate.quiet = 1;\n+\tstate.refresh_cache = 1;\n+\n+\to->merge_size = len;\n+\n+\tif (len) {\n+\t\tposns = xmalloc(len * sizeof(struct tree_entry_list *));\n+\t\tfor (i = 0; i < len; i++) {\n+\t\t\tposns[i] = create_tree_entry_list((struct tree *) posn->item);\n+\t\t\tposn = posn->next;\n+\t\t}\n+\t\tif (unpack_trees_rec(posns, len, o->prefix ? o->prefix : \"\",\n+\t\t\t\t     o, &indpos, &df_conflict_list))\n+\t\t\treturn -1;\n+\t}\n+\n+\tif (o->trivial_merges_only && o->nontrivial_merge)\n+\t\tdie(\"Merge requires file-level merging\");\n+\n+\tcheck_updates(active_cache, active_nr, o);\n+\treturn 0;\n+}\n+\n+\ndiff --git a/unpack-trees.h b/unpack-trees.h\nnew file mode 100644\nindex 0000000..babfcd7\n--- /dev/null\n+++ b/unpack-trees.h\n@@ -0,0 +1,30 @@\n+#ifndef UNPACK_TREES_H\n+#define UNPACK_TREES_H\n+\n+struct unpack_trees_options;\n+\n+typedef int (*merge_fn_t)(struct cache_entry **src,\n+\t\tstruct unpack_trees_options *options);\n+\n+struct unpack_trees_options {\n+\tint reset;\n+\tint merge;\n+\tint update;\n+\tint index_only;\n+\tint nontrivial_merge;\n+\tint trivial_merges_only;\n+\tint verbose_update;\n+\tint aggressive;\n+\tconst char *prefix;\n+\tmerge_fn_t fn;\n+\n+\tint head_idx;\n+\tint merge_size;\n+\n+\tstruct cache_entry df_conflict_entry;\n+};\n+\n+extern int unpack_trees(struct object_list *trees,\n+\t\tstruct unpack_trees_options *options);\n+\n+#endif\n-- \n1.4.2.rc2.g76b2-dirty\n"},{"id":"24389","messageId":"7v4pwydbbr.fsf@assigned-by-dhcp.cox.net","threadId":"5046","inReplyTo":"Pine.LNX.4.63.0607302024090.29667@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [PATCH 1/3] read-trees: refactor the unpack_trees() part","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-07-30T21:36:40Z","receivedAt":"2006-07-30T21:36:40Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> diff --git a/unpack-trees.h b/unpack-trees.h\n> new file mode 100644\n> index 0000000..babfcd7\n> --- /dev/null\n> +++ b/unpack-trees.h\n> @@ -0,0 +1,30 @@\n> +#ifndef UNPACK_TREES_H\n> +#define UNPACK_TREES_H\n>...\n> +struct unpack_trees_options {\n> +\tint reset;\n> +\tint merge;\n> +\tint update;\n> +\tint index_only;\n> +\tint nontrivial_merge;\n> +\tint trivial_merges_only;\n> +\tint verbose_update;\n> +\tint aggressive;\n> +\tconst char *prefix;\n> +\tmerge_fn_t fn;\n> +\n> +\tint head_idx;\n> +\tint merge_size;\n> +\n> +\tstruct cache_entry df_conflict_entry;\n> +};\n\nHmmmm.....\n\ngcc -o unpack-trees.o -c -O2 -Werror -ansi -pedantic -std=c99 -D_XOPEN_SOURCE=500 -D_BSD_SOURCE -Wall -Wdeclaration-after-statement -g -DSHA1_HEADER='<openssl/sha.h>' -DNO_STRLCPY unpack-trees.c\ncc1: warnings being treated as errors\nIn file included from unpack-trees.c:7:\nunpack-trees.h:24: warning: invalid use of structure with flexible array member\nmake: *** [unpack-trees.o] Error 1\n"},{"id":"24393","messageId":"Pine.LNX.4.63.0607310205260.29667@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"5046","inReplyTo":"7v4pwydbbr.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 1/3] read-trees: refactor the unpack_trees() part","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-07-31T00:14:24Z","receivedAt":"2006-07-31T00:14:24Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sun, 30 Jul 2006, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > diff --git a/unpack-trees.h b/unpack-trees.h\n> > new file mode 100644\n> > index 0000000..babfcd7\n> > --- /dev/null\n> > +++ b/unpack-trees.h\n> > @@ -0,0 +1,30 @@\n> > +#ifndef UNPACK_TREES_H\n> > +#define UNPACK_TREES_H\n> >...\n> > +struct unpack_trees_options {\n> > +\tint reset;\n> > +\tint merge;\n> > +\tint update;\n> > +\tint index_only;\n> > +\tint nontrivial_merge;\n> > +\tint trivial_merges_only;\n> > +\tint verbose_update;\n> > +\tint aggressive;\n> > +\tconst char *prefix;\n> > +\tmerge_fn_t fn;\n> > +\n> > +\tint head_idx;\n> > +\tint merge_size;\n> > +\n> > +\tstruct cache_entry df_conflict_entry;\n> > +};\n> \n> Hmmmm.....\n> \n> gcc -o unpack-trees.o -c -O2 -Werror -ansi -pedantic -std=c99 -D_XOPEN_SOURCE=500 -D_BSD_SOURCE -Wall -Wdeclaration-after-statement -g -DSHA1_HEADER='<openssl/sha.h>' -DNO_STRLCPY unpack-trees.c\n> cc1: warnings being treated as errors\n> In file included from unpack-trees.c:7:\n> unpack-trees.h:24: warning: invalid use of structure with flexible array member\n> make: *** [unpack-trees.o] Error 1\n\nDarn, darn, DARN!\n\nAs far as I can see, that flexible array member is the member \"name\" of \ncache_entry. And as I can see further, it is used _nowhere_. At least not \ninside that structure.\n\nHow about this (on top of that patch):\n\n-- 8< --\n[PATCH 1.5/3] unpack-trees: please C99 standard\n\nEven if it is not a technical error in _this_ case, a struct with\na flexible array member should not be put inside another struct.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n unpack-trees.c |   10 ++++++----\n unpack-trees.h |    2 +-\n 2 files changed, 7 insertions(+), 5 deletions(-)\n\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex b8a355f..3cb92cb 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -168,7 +168,7 @@ #endif\n \t\t\t}\n \n \t\t\tif (posns[i] == df_conflict_list) {\n-\t\t\t\tsrc[i + o->merge] = &o->df_conflict_entry;\n+\t\t\t\tsrc[i + o->merge] = o->df_conflict_entry;\n \t\t\t\tcontinue;\n \t\t\t}\n \n@@ -178,7 +178,7 @@ #endif\n \t\t\t\tparse_tree(tree);\n \t\t\t\tsubposns[i] = create_tree_entry_list(tree);\n \t\t\t\tposns[i] = posns[i]->next;\n-\t\t\t\tsrc[i + o->merge] = &o->df_conflict_entry;\n+\t\t\t\tsrc[i + o->merge] = o->df_conflict_entry;\n \t\t\t\tcontinue;\n \t\t\t}\n \n@@ -370,6 +370,7 @@ int unpack_trees(struct object_list *tre\n \tint i;\n \tstruct object_list *posn = trees;\n \tstruct tree_entry_list df_conflict_list;\n+\tstruct cache_entry df_conflict_entry;\n \n \tmemset(&df_conflict_list, 0, sizeof(df_conflict_list));\n \tdf_conflict_list.next = &df_conflict_list;\n@@ -380,6 +381,7 @@ int unpack_trees(struct object_list *tre\n \tstate.refresh_cache = 1;\n \n \to->merge_size = len;\n+\to->df_conflict_entry = &df_conflict_entry;\n \n \tif (len) {\n \t\tposns = xmalloc(len * sizeof(struct tree_entry_list *));\n@@ -562,12 +564,12 @@ int threeway_merge(struct cache_entry **\n \tindex = stages[0];\n \thead = stages[o->head_idx];\n \n-\tif (head == &o->df_conflict_entry) {\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+\tif (remote == o->df_conflict_entry) {\n \t\tdf_conflict_remote = 1;\n \t\tremote = NULL;\n \t}\ndiff --git a/unpack-trees.h b/unpack-trees.h\nindex 826b263..c460162 100644\n--- a/unpack-trees.h\n+++ b/unpack-trees.h\n@@ -21,7 +21,7 @@ struct unpack_trees_options {\n \tint head_idx;\n \tint merge_size;\n \n-\tstruct cache_entry df_conflict_entry;\n+\tstruct cache_entry *df_conflict_entry;\n };\n \n extern int unpack_trees(struct object_list *trees,\n-- \n1.4.2.rc2.g53906\n"},{"id":"24399","messageId":"Pine.LNX.4.63.0607310238170.29667@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"5046","inReplyTo":"Pine.LNX.4.63.0607310205260.29667@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [PATCH 1/3] read-trees: refactor the unpack_trees() part","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-07-31T00:48:34Z","receivedAt":"2006-07-31T00:48:34Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Mon, 31 Jul 2006, Johannes Schindelin wrote:\n\n> How about this (on top of that patch):\n> \n> -- 8< --\n> [PATCH 1.5/3] unpack-trees: please C99 standard\n\nOf course, I got bitten by \"git-commit\" not regarding any \ngit-update-index'ed files after starting the editor... This is needed as \nwell:\n\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 3cb92cb..e595d23 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -381,6 +381,7 @@ int unpack_trees(struct object_list *tre\n \tstate.refresh_cache = 1;\n \n \to->merge_size = len;\n+\tmemset(&df_conflict_entry, 0, sizeof(df_conflict_entry));\n \to->df_conflict_entry = &df_conflict_entry;\n \n \tif (len) {\n\nSorry for the noise.\n\nCiao,\nDscho\n"}]}