{"thread":{"id":"4806","subject":"[WIP] Status update on merge-recursive in C","startedAt":"2006-07-08T16:42:41Z","lastAt":"2006-07-27T09:41:59Z","messageCount":6,"participants":["Johannes Schindelin","Pavel Roskin","Junio C Hamano"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"23418","messageId":"Pine.LNX.4.63.0607081840280.29667@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"4806","inReplyTo":null,"subject":"[WIP] Status update on merge-recursive in C","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-07-08T16:42:41Z","receivedAt":"2006-07-08T16:42:41Z","isPatch":false,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nThis is just an update for people being interested. Alex and me were\nbusy with that project for a few days now. While it has progressed nicely,\nthere are quite a couple TODOs in merge-recursive.c, just search for \"TODO\".\n\nFor impatient people: yes, it passes all the tests, and yes, according\nto the evil test Alex did, it is faster than the Python script.\n\nBut no, it is not yet finished. Biggest points are:\n\n- there are still three external calls\n- in the end, it should not be necessary to write the index more than once\n  (just before exiting)\n- a lot of things can be refactored to make the code easier and shorter\n\nBTW we cannot just plug in git-merge-tree yet, because git-merge-tree\ndoes not handle renames at all.\n\nThis patch is meant for testing, and as such,\n\n- it compile the program to git-merge-recur\n- it adjusts the scripts and tests to use git-merge-recur instead of\n  git-merge-recursive\n- it provides \"TEST\", a script to execute the tests regarding -recursive\n- it inlines the changes to read-cache.c (read_cache_from(), discard_cache()\n  and refresh_cache_entry())\n\nBrought to you by Alex Riesen and Dscho\n\n---\n\n Makefile                |    8 \n TEST                    |   10 \n cache.h                 |    4 \n git-merge.sh            |    6 \n git-rebase.sh           |    4 \n merge-recursive.c       | 1560 +++++++++++++++++++++++++++++++++++++++++++++++\n path-list.c             |  105 +++\n path-list.h             |   22 +\n read-cache.c            |  104 ++-\n t/t3402-rebase-merge.sh |    2 \n 10 files changed, 1773 insertions(+), 52 deletions(-)\n\ndiff --git a/Makefile b/Makefile\nindex 8d429a0..11feefe 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -179,7 +179,8 @@ PROGRAMS = \\\n \tgit-upload-pack$X git-verify-pack$X \\\n \tgit-symbolic-ref$X \\\n \tgit-name-rev$X git-pack-redundant$X git-repo-config$X git-var$X \\\n-\tgit-describe$X git-merge-tree$X git-blame$X git-imap-send$X\n+\tgit-describe$X git-merge-tree$X git-blame$X git-imap-send$X \\\n+\tgit-merge-recur$X\n \n BUILT_INS = git-log$X git-whatchanged$X git-show$X git-update-ref$X \\\n \tgit-count-objects$X git-diff$X git-push$X git-mailsplit$X \\\n@@ -647,6 +648,11 @@ git-http-push$X: revision.o http.o http-\n \t$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \\\n \t\t$(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)\n \n+merge-recursive.o path-list.o: path-list.h\n+git-merge-recur$X: merge-recursive.o path-list.o $(LIB_FILE)\n+\t$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \\\n+\t\t$(LIBS)\n+\n $(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H)\n $(patsubst git-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h)\n $(DIFF_OBJS): diffcore.h\ndiff --git a/TEST b/TEST\nnew file mode 100755\nindex 0000000..d530983\n--- /dev/null\n+++ b/TEST\n@@ -0,0 +1,10 @@\n+#!/bin/sh -x\n+cd t || exit\n+./t3400-rebase.sh              \"$@\" && \\\n+./t6020-merge-df.sh            \"$@\" && \\\n+./t3401-rebase-partial.sh      \"$@\" && \\\n+./t6021-merge-criss-cross.sh   \"$@\" && \\\n+./t3402-rebase-merge.sh        \"$@\" && \\\n+./t6022-merge-rename.sh        \"$@\" && \\\n+./t6010-merge-base.sh          \"$@\" && \\\n+:\ndiff --git a/cache.h b/cache.h\nindex 7b5c91c..08777ca 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -115,6 +115,7 @@ #define cache_entry_size(len) ((offsetof\n extern struct cache_entry **active_cache;\n extern unsigned int active_nr, active_alloc, active_cache_changed;\n extern struct cache_tree *active_cache_tree;\n+extern int cache_errno;\n \n #define GIT_DIR_ENVIRONMENT \"GIT_DIR\"\n #define DEFAULT_GIT_DIR_ENVIRONMENT \".git\"\n@@ -142,13 +143,16 @@ #define alloc_nr(x) (((x)+16)*3/2)\n \n /* Initialize and use the cache information */\n extern int read_cache(void);\n+extern int read_cache_from(const char *path);\n extern int write_cache(int newfd, struct cache_entry **cache, int entries);\n+extern int discard_cache(void);\n extern int verify_path(const char *path);\n extern int cache_name_pos(const char *name, int namelen);\n #define ADD_CACHE_OK_TO_ADD 1\t\t/* Ok to add */\n #define ADD_CACHE_OK_TO_REPLACE 2\t/* Ok to replace file/directory */\n #define ADD_CACHE_SKIP_DFCHECK 4\t/* Ok to skip DF conflict checks */\n extern int add_cache_entry(struct cache_entry *ce, int option);\n+extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);\n extern int remove_cache_entry_at(int pos);\n extern int remove_file_from_cache(const char *path);\n extern int ce_same_name(struct cache_entry *a, struct cache_entry *b);\ndiff --git a/git-merge.sh b/git-merge.sh\nindex 24e3b50..b26ca14 100755\n--- a/git-merge.sh\n+++ b/git-merge.sh\n@@ -9,15 +9,15 @@ USAGE='[-n] [--no-commit] [--squash] [-s\n LF='\n '\n \n-all_strategies='recursive octopus resolve stupid ours'\n-default_twohead_strategies='recursive'\n+all_strategies='recur recur octopus resolve stupid ours'\n+default_twohead_strategies='recur'\n default_octopus_strategies='octopus'\n no_trivial_merge_strategies='ours'\n use_strategies=\n \n index_merge=t\n if test \"@@NO_PYTHON@@\"; then\n-\tall_strategies='resolve octopus stupid ours'\n+\tall_strategies='recur resolve octopus stupid ours'\n \tdefault_twohead_strategies='resolve'\n fi\n \ndiff --git a/git-rebase.sh b/git-rebase.sh\nindex 3945e06..5d4c7d2 100755\n--- a/git-rebase.sh\n+++ b/git-rebase.sh\n@@ -35,7 +35,7 @@ If you would prefer to skip this patch, \n To restore the original branch and stop rebasing run \\\"git rebase --abort\\\".\n \"\n unset newbase\n-strategy=recursive\n+strategy=recur\n do_merge=\n dotest=$GIT_DIR/.dotest-merge\n prec=4\n@@ -292,7 +292,7 @@ then\n \texit $?\n fi\n \n-if test \"@@NO_PYTHON@@\" && test \"$strategy\" = \"recursive\"\n+if test \"@@NO_PYTHON@@\" && test \"$strategy\" = \"recur\"\n then\n \tdie 'The recursive merge strategy currently relies on Python,\n which this installation of git was not configured with.  Please consider\ndiff --git a/merge-recursive.c b/merge-recursive.c\nnew file mode 100644\nindex 0000000..07a2b38\n--- /dev/null\n+++ b/merge-recursive.c\n@@ -0,0 +1,1560 @@\n+/*\n+ * Recursive Merge algorithm stolen from git-merge-recursive.py by\n+ * Fredrik Kuivinen.\n+ * The thieves were Alex Riesen and Johannes Schindelin, in June/July 2006\n+ */\n+#include <stdarg.h>\n+#include <string.h>\n+#include <assert.h>\n+#include <sys/wait.h>\n+#include <sys/types.h>\n+#include <sys/stat.h>\n+#include <time.h>\n+#include \"cache.h\"\n+#include \"cache-tree.h\"\n+#include \"commit.h\"\n+#include \"blob.h\"\n+#include \"tree-walk.h\"\n+#include \"diff.h\"\n+#include \"diffcore.h\"\n+#include \"run-command.h\"\n+#include \"tag.h\"\n+\n+#include \"path-list.h\"\n+\n+/*#define DEBUG*/\n+\n+#ifdef DEBUG\n+#define debug(args, ...) fprintf(stderr, args, ## __VA_ARGS__)\n+#else\n+#define debug(args, ...)\n+#endif\n+\n+#ifdef DEBUG\n+#include \"quote.h\"\n+static void show_ce_entry(const char *tag, struct cache_entry *ce)\n+{\n+\tif (tag && *tag &&\n+\t    (ce->ce_flags & htons(CE_VALID))) {\n+\t\tstatic char alttag[4];\n+\t\tmemcpy(alttag, tag, 3);\n+\t\tif (isalpha(tag[0]))\n+\t\t\talttag[0] = tolower(tag[0]);\n+\t\telse if (tag[0] == '?')\n+\t\t\talttag[0] = '!';\n+\t\telse {\n+\t\t\talttag[0] = 'v';\n+\t\t\talttag[1] = tag[0];\n+\t\t\talttag[2] = ' ';\n+\t\t\talttag[3] = 0;\n+\t\t}\n+\t\ttag = alttag;\n+\t}\n+\n+\tfprintf(stderr,\"%s%06o %s %d\\t\",\n+\t\t\ttag,\n+\t\t\tntohl(ce->ce_mode),\n+\t\t\tsha1_to_hex(ce->sha1),\n+\t\t\tce_stage(ce));\n+\twrite_name_quoted(\"\", 0, ce->name,\n+\t\t\t'\\n', stderr);\n+\tfputc('\\n', stderr);\n+}\n+\n+static void ls_files() {\n+\tint i;\n+\tfor (i = 0; i < active_nr; i++) {\n+\t\tstruct cache_entry *ce = active_cache[i];\n+\t\tshow_ce_entry(\"\", ce);\n+\t}\n+\tfprintf(stderr, \"---\\n\");\n+}\n+#endif\n+\n+/*\n+ * A virtual commit has\n+ * - (const char *)commit->util set to the name, and\n+ * - *(int *)commit->object.sha1 set to the virtual id.\n+ */\n+static const char *commit_title(struct commit *commit, int *len)\n+{\n+\tconst char *s = \"(null commit)\";\n+\t*len = strlen(s);\n+\n+\tif ( commit->util ) {\n+\t\ts = commit->util;\n+\t\t*len = strlen(s);\n+\t} else {\n+\t\tif ( parse_commit(commit) != 0 ) {\n+\t\t\ts = \"(bad commit)\";\n+\t\t\t*len = strlen(s);\n+\t\t} else {\n+\t\t\ts = commit->buffer;\n+\t\t\tchar prev = '\\0';\n+\t\t\twhile ( *s ) {\n+\t\t\t\tif ( '\\n' == prev && '\\n' == *s ) {\n+\t\t\t\t\t++s;\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t\tprev = *s++;\n+\t\t\t}\n+\t\t\t*len = 0;\n+\t\t\twhile ( s[*len] && '\\n' != s[*len] )\n+\t\t\t\t++(*len);\n+\t\t}\n+\t}\n+\treturn s;\n+}\n+\n+static const char *commit_hex_sha1(const struct commit *commit)\n+{\n+\treturn commit->util ? \"virtual\" : commit ?\n+\t\tsha1_to_hex(commit->object.sha1) : \"undefined\";\n+}\n+\n+static unsigned commit_list_count(const struct commit_list *l)\n+{\n+\tunsigned c = 0;\n+\tfor (; l; l = l->next )\n+\t\tc++;\n+\treturn c;\n+}\n+\n+static struct commit *make_virtual_commit(struct tree *tree, const char *comment)\n+{\n+\tstruct commit *commit = xcalloc(1, sizeof(struct commit));\n+\tstatic unsigned virtual_id = 1;\n+\tcommit->tree = tree;\n+\tcommit->util = (void*)comment;\n+\t*(int*)commit->object.sha1 = virtual_id++;\n+\treturn commit;\n+}\n+\n+/*\n+ * TODO: we should not have to copy the SHA1s around, but rather reference\n+ * them. That way, sha_eq() is just sha1 == sha2.\n+ */\n+static int sha_eq(const unsigned char *a, const unsigned char *b)\n+{\n+\tif ( !a && !b )\n+\t\treturn 2;\n+\treturn a && b && memcmp(a, b, 20) == 0;\n+}\n+\n+static void memswp(void *p1, void *p2, unsigned n)\n+{\n+\tunsigned char *a = p1, *b = p2;\n+\twhile ( n-- ) {\n+\t\t*a ^= *b;\n+\t\t*b ^= *a;\n+\t\t*a ^= *b;\n+\t\t++a;\n+\t\t++b;\n+\t}\n+}\n+\n+/*\n+ * TODO: we should convert the merge_result users to\n+ * \tint blabla(..., struct commit **result)\n+ * like everywhere else in git.\n+ * Same goes for merge_tree_result and merge_file_info.\n+ */\n+struct merge_result\n+{\n+\tstruct commit *commit;\n+\tunsigned clean:1;\n+};\n+\n+struct merge_tree_result\n+{\n+\tstruct tree *tree;\n+\tunsigned clean:1;\n+};\n+\n+/*\n+ * TODO: check if we can just reuse the active_cache structure: it is already\n+ * sorted (by name, stage).\n+ * Only problem: do not write it when flushing the cache.\n+ */\n+struct stage_data\n+{\n+\tstruct\n+\t{\n+\t\tunsigned mode;\n+\t\tunsigned char sha[20];\n+\t} stages[4];\n+\tunsigned processed:1;\n+};\n+\n+static struct path_list currentFileSet = {NULL, 0, 0, 1};\n+static struct path_list currentDirectorySet = {NULL, 0, 0, 1};\n+\n+static int output_indent = 0;\n+\n+static void output(const char *fmt, ...)\n+{\n+\tva_list args;\n+\tint i;\n+\tfor ( i = output_indent; i--; )\n+\t\tfputs(\"  \", stdout);\n+\tva_start(args, fmt);\n+\tvfprintf(stdout, fmt, args);\n+\tva_end(args);\n+\tfputc('\\n', stdout);\n+}\n+\n+static const char *original_index_file;\n+static const char *temporary_index_file;\n+static int cache_dirty = 0;\n+\n+static int flush_cache()\n+{\n+\t/* flush temporary index */\n+\tstruct lock_file *lock = xcalloc(1, sizeof(struct lock_file));\n+\tint fd = hold_lock_file_for_update(lock, getenv(\"GIT_INDEX_FILE\"));\n+\tif (fd < 0)\n+\t\tdie(\"could not lock %s\", temporary_index_file);\n+\tif (write_cache(fd, active_cache, active_nr) ||\n+\t\t\tclose(fd) || commit_lock_file(lock))\n+\t\tdie (\"unable to write %s\", getenv(\"GIT_INDEX_FILE\"));\n+\tdiscard_cache();\n+\tcache_dirty = 0;\n+\treturn 0;\n+}\n+\n+static void setup_index(int temp)\n+{\n+\tconst char *idx = temp ? temporary_index_file: original_index_file;\n+\tif (cache_dirty)\n+\t\tdie(\"fatal: cache changed flush_cache();\");\n+\tunlink(temporary_index_file);\n+\tsetenv(\"GIT_INDEX_FILE\", idx, 1);\n+\tdiscard_cache();\n+}\n+\n+static struct cache_entry *make_cache_entry(unsigned int mode,\n+\t\tconst unsigned char *sha1, const char *path, int stage, int refresh)\n+{\n+\tint size, len;\n+\tstruct cache_entry *ce;\n+\n+\tif (!verify_path(path))\n+\t\treturn NULL;\n+\n+\tlen = strlen(path);\n+\tsize = cache_entry_size(len);\n+\tce = xcalloc(1, size);\n+\n+\tmemcpy(ce->sha1, sha1, 20);\n+\tmemcpy(ce->name, path, len);\n+\tce->ce_flags = create_ce_flags(len, stage);\n+\tce->ce_mode = create_ce_mode(mode);\n+\n+\tif (refresh)\n+\t\treturn refresh_cache_entry(ce, 0);\n+\n+\treturn ce;\n+}\n+\n+static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,\n+\t\tconst char *path, int stage, int refresh, int options)\n+{\n+\tstruct cache_entry *ce;\n+\tif (!cache_dirty)\n+\t\tread_cache_from(getenv(\"GIT_INDEX_FILE\"));\n+\tcache_dirty++;\n+\tce = make_cache_entry(mode, sha1 ? sha1 : null_sha1, path, stage, refresh);\n+\tif (!ce)\n+\t\treturn error(\"cache_addinfo failed: %s\", strerror(cache_errno));\n+\treturn add_cache_entry(ce, options);\n+}\n+\n+/*\n+ * This is a global variable which is used in a number of places but\n+ * only written to in the 'merge' function.\n+ *\n+ * index_only == 1    => Don't leave any non-stage 0 entries in the cache and\n+ *                       don't update the working directory.\n+ *               0    => Leave unmerged entries in the cache and update\n+ *                       the working directory.\n+ */\n+static int index_only = 0;\n+\n+/*\n+ * TODO: this can be streamlined by refactoring builtin-read-tree.c\n+ */\n+static int git_read_tree(const struct tree *tree)\n+{\n+#if 0\n+\tfprintf(stderr, \"GIT_INDEX_FILE='%s' git-read-tree %s\\n\",\n+\t\tgetenv(\"GIT_INDEX_FILE\"),\n+\t\tsha1_to_hex(tree->object.sha1));\n+#endif\n+\tconst char *argv[] = { \"git-read-tree\", NULL, NULL, };\n+\tif (cache_dirty)\n+\t\tdie(\"read-tree with dirty cache\");\n+\targv[1] = sha1_to_hex(tree->object.sha1);\n+\tint rc = run_command_v(2, argv);\n+\treturn rc < 0 ? -1: rc;\n+}\n+\n+/*\n+ * TODO: this can be streamlined by refactoring builtin-read-tree.c\n+ */\n+static int git_merge_trees(const char *update_arg,\n+\t\t\t   struct tree *common,\n+\t\t\t   struct tree *head,\n+\t\t\t   struct tree *merge)\n+{\n+#if 0\n+\tfprintf(stderr, \"GIT_INDEX_FILE='%s' git-read-tree %s -m %s %s %s\\n\",\n+\t\tgetenv(\"GIT_INDEX_FILE\"),\n+\t\tupdate_arg,\n+\t\tsha1_to_hex(common->object.sha1),\n+\t\tsha1_to_hex(head->object.sha1),\n+\t\tsha1_to_hex(merge->object.sha1));\n+#endif\n+\tconst char *argv[] = {\n+\t\t\"git-read-tree\", NULL, \"-m\", NULL, NULL, NULL,\n+\t\tNULL,\n+\t};\n+\tif (cache_dirty)\n+\t\tflush_cache();\n+\targv[1] = update_arg;\n+\targv[3] = sha1_to_hex(common->object.sha1);\n+\targv[4] = sha1_to_hex(head->object.sha1);\n+\targv[5] = sha1_to_hex(merge->object.sha1);\n+\tint rc = run_command_v(6, argv);\n+\treturn rc < 0 ? -1: rc;\n+}\n+\n+/*\n+ * TODO: this can be streamlined by refactoring builtin-write-tree.c\n+ */\n+static struct tree *git_write_tree()\n+{\n+#if 0\n+\tfprintf(stderr, \"GIT_INDEX_FILE='%s' git-write-tree\\n\",\n+\t\tgetenv(\"GIT_INDEX_FILE\"));\n+#endif\n+\tif (cache_dirty)\n+\t\tflush_cache();\n+\tFILE *fp = popen(\"git-write-tree 2>/dev/null\", \"r\");\n+\tchar buf[41];\n+\tunsigned char sha1[20];\n+\tint ch;\n+\tunsigned i = 0;\n+\twhile ( (ch = fgetc(fp)) != EOF )\n+\t\tif ( i < sizeof(buf)-1 && ch >= '0' && ch <= 'f' )\n+\t\t\tbuf[i++] = ch;\n+\t\telse\n+\t\t\tbreak;\n+\tint rc = pclose(fp);\n+\tif ( rc == -1 || WEXITSTATUS(rc) )\n+\t\treturn NULL;\n+\tbuf[i] = '\\0';\n+\tif ( get_sha1(buf, sha1) != 0 )\n+\t\treturn NULL;\n+\treturn lookup_tree(sha1);\n+}\n+\n+/*\n+ * TODO: get rid of files_and_dirs; we do not use it except for\n+ * current_file_set and current_dir_set, which are global already.\n+ */\n+static struct\n+{\n+\tstruct path_list *files;\n+\tstruct path_list *dirs;\n+} files_and_dirs;\n+\n+static int save_files_dirs(const unsigned char *sha1,\n+\t\tconst char *base, int baselen, const char *path,\n+\t\tunsigned int mode, int stage)\n+{\n+\tint len = strlen(path);\n+\tchar *newpath = malloc(baselen + len + 1);\n+\tmemcpy(newpath, base, baselen);\n+\tmemcpy(newpath + baselen, path, len);\n+\tnewpath[baselen + len] = '\\0';\n+\n+\tif (S_ISDIR(mode))\n+\t\tpath_list_insert(newpath, files_and_dirs.dirs);\n+\telse\n+\t\tpath_list_insert(newpath, files_and_dirs.files);\n+\tfree(newpath);\n+\n+\treturn READ_TREE_RECURSIVE;\n+}\n+\n+static int get_files_dirs(struct tree *tree,\n+\t\t\t  struct path_list *files,\n+\t\t\t  struct path_list *dirs)\n+{\n+\tint n;\n+\tfiles_and_dirs.files = files;\n+\tfiles_and_dirs.dirs = dirs;\n+\tdebug(\"get_files_dirs ...\\n\");\n+\tif (read_tree_recursive(tree, \"\", 0, 0, NULL, save_files_dirs) != 0) {\n+\t\tdebug(\"  get_files_dirs done (0)\\n\");\n+\t\treturn 0;\n+\t}\n+\tn = files->nr + dirs->nr;\n+\tdebug(\"  get_files_dirs done (%d)\\n\", n);\n+\treturn n;\n+}\n+\n+/*\n+ * TODO: this wrapper is so small, we can use path_list_lookup directly.\n+ * Same goes for index_entry_get(), free_index_entries(), find_rename_bysrc(),\n+ * free_rename_entries().\n+ */\n+static struct stage_data *index_entry_find(struct path_list *ents,\n+\t\t\t\t\t    const char *path)\n+{\n+\tstruct path_list_item *item = path_list_lookup(path, ents);\n+\tif (item)\n+\t\treturn item->util;\n+\treturn NULL;\n+}\n+\n+static struct stage_data *index_entry_get(struct path_list *ents,\n+\t\t\t\t\t   const char *path)\n+{\n+\tstruct path_list_item *item = path_list_lookup(path, ents);\n+\n+\tif (item == NULL) {\n+\t\titem = path_list_insert(path, ents);\n+\t\titem->util = xcalloc(1, sizeof(struct stage_data));\n+\t}\n+\treturn item->util;\n+}\n+\n+/*\n+ * TODO: since the result of index_entry_from_db() is tucked into a\n+ * path_list anyway, this helper can do that already.\n+ */\n+/*\n+ * Returns a index_entry instance which doesn't have to correspond to\n+ * a real cache entry in Git's index.\n+ */\n+static struct stage_data *index_entry_from_db(const char *path,\n+\t\t\t\t\t       struct tree *o,\n+\t\t\t\t\t       struct tree *a,\n+\t\t\t\t\t       struct tree *b)\n+{\n+\tstruct stage_data *e = xcalloc(1, sizeof(struct stage_data));\n+\tget_tree_entry(o->object.sha1, path,\n+\t\t\te->stages[1].sha, &e->stages[1].mode);\n+\tget_tree_entry(a->object.sha1, path,\n+\t\t\te->stages[2].sha, &e->stages[2].mode);\n+\tget_tree_entry(b->object.sha1, path,\n+\t\t\te->stages[3].sha, &e->stages[3].mode);\n+\treturn e;\n+}\n+\n+static void free_index_entries(struct path_list **ents)\n+{\n+\tif (!*ents)\n+\t\treturn;\n+\n+\tpath_list_clear(*ents, 1);\n+\tfree(*ents);\n+\t*ents = NULL;\n+}\n+\n+/*\n+ * Create a dictionary mapping file names to CacheEntry objects. The\n+ * dictionary contains one entry for every path with a non-zero stage entry.\n+ */\n+static struct path_list *get_unmerged()\n+{\n+\tstruct path_list *unmerged = xcalloc(1, sizeof(struct path_list));\n+\tint i;\n+\n+\tunmerged->strdup_paths = 1;\n+\tif (!cache_dirty) {\n+\t\tread_cache_from(getenv(\"GIT_INDEX_FILE\"));\n+\t\tcache_dirty++;\n+\t}\n+\tfor (i = 0; i < active_nr; i++) {\n+\t\tstruct cache_entry *ce = active_cache[i];\n+\t\tif (!ce_stage(ce))\n+\t\t\tcontinue;\n+\n+\t\tstruct stage_data *e = index_entry_get(unmerged, ce->name);\n+\t\te->stages[ce_stage(ce)].mode = ntohl(ce->ce_mode);\n+\t\tmemcpy(e->stages[ce_stage(ce)].sha, ce->sha1, 20);\n+\t}\n+\n+\tdebug(\"  get_unmerged done\\n\");\n+\treturn unmerged;\n+}\n+\n+struct rename\n+{\n+\tstruct diff_filepair *pair;\n+\tstruct stage_data *src_entry;\n+\tstruct stage_data *dst_entry;\n+\tunsigned processed:1;\n+};\n+\n+static struct rename *find_rename_bysrc(struct path_list *e,\n+\t\t\t\t\t      const char *name)\n+{\n+\tstruct path_list_item *item = path_list_lookup(name, e);\n+\tif (item)\n+\t\treturn item->util;\n+\treturn NULL;\n+}\n+\n+static void free_rename_entries(struct path_list **list)\n+{\n+\tif (!*list)\n+\t\treturn;\n+\n+\tpath_list_clear(*list, 0);\n+\tfree(*list);\n+\t*list = NULL;\n+}\n+\n+/*\n+ * Get information of all renames which occured between 'oTree' and\n+ * 'tree'. We need the three trees in the merge ('oTree', 'aTree' and\n+ * 'bTree') to be able to associate the correct cache entries with\n+ * the rename information. 'tree' is always equal to either aTree or bTree.\n+ */\n+static struct path_list *get_renames(struct tree *tree,\n+\t\t\t\t\tstruct tree *oTree,\n+\t\t\t\t\tstruct tree *aTree,\n+\t\t\t\t\tstruct tree *bTree,\n+\t\t\t\t\tstruct path_list *entries)\n+{\n+#ifdef DEBUG\n+\ttime_t t = time(0);\n+\tdebug(\"getRenames ...\\n\");\n+#endif\n+\tint i;\n+\tstruct path_list *renames = xcalloc(1, sizeof(struct path_list));\n+\tstruct diff_options opts;\n+\tdiff_setup(&opts);\n+\topts.recursive = 1;\n+\topts.detect_rename = DIFF_DETECT_RENAME;\n+\topts.output_format = DIFF_FORMAT_NO_OUTPUT;\n+\tif (diff_setup_done(&opts) < 0)\n+\t\tdie(\"diff setup failed\");\n+\tdiff_tree_sha1(oTree->object.sha1, tree->object.sha1, \"\", &opts);\n+\tdiffcore_std(&opts);\n+\tfor (i = 0; i < diff_queued_diff.nr; ++i) {\n+\t\tstruct rename *re;\n+\t\tstruct diff_filepair *pair = diff_queued_diff.queue[i];\n+\t\tif (pair->status != 'R') {\n+\t\t\tdiff_free_filepair(pair);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tre = xmalloc(sizeof(*re));\n+\t\tre->processed = 0;\n+\t\tre->pair = pair;\n+\t\tre->src_entry = index_entry_find(entries, re->pair->one->path);\n+\t\t/* TODO: should it not be an error, if src_entry was found? */\n+\t\tif ( !re->src_entry ) {\n+\t\t\tre->src_entry = index_entry_from_db(re->pair->one->path,\n+\t\t\t\t\toTree, aTree, bTree);\n+\t\t\tstruct path_list_item *item =\n+\t\t\t\tpath_list_insert(re->pair->one->path, entries);\n+\t\t\titem->util = re->src_entry;\n+\t\t}\n+\t\tre->dst_entry = index_entry_find(entries, re->pair->two->path);\n+\t\tif ( !re->dst_entry ) {\n+\t\t\tre->dst_entry = index_entry_from_db(re->pair->two->path,\n+\t\t\t\t\toTree, aTree, bTree);\n+\t\t\tstruct path_list_item *item =\n+\t\t\t\tpath_list_insert(re->pair->two->path, entries);\n+\t\t\titem->util = re->dst_entry;\n+\t\t}\n+\t\tstruct path_list_item *item = path_list_insert(pair->one->path, renames);\n+\t\titem->util = re;\n+\t}\n+\topts.output_format = DIFF_FORMAT_NO_OUTPUT;\n+\tdiff_queued_diff.nr = 0;\n+\tdiff_flush(&opts);\n+\tdebug(\"  getRenames done in %ld\\n\", time(0)-t);\n+\treturn renames;\n+}\n+\n+/*\n+ * TODO: the code would be way nicer, if we had a struct containing just sha1 and mode.\n+ * In this particular case, we might get away reusing stage_data, no?\n+ */\n+int update_stages(const char *path,\n+\t\t   unsigned char *osha, unsigned omode,\n+\t\t   unsigned char *asha, unsigned amode,\n+\t\t   unsigned char *bsha, unsigned bmode,\n+\t\t   int clear /* =True */)\n+{\n+\tint options = ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE;\n+\tif ( clear ) \n+\t\tif (add_cacheinfo(0, null_sha1, path, 0, 0, options))\n+\t\t\treturn -1;\n+\tif ( omode )\n+\t\tif (add_cacheinfo(omode, osha, path, 1, 0, options))\n+\t\t\treturn -1;\n+\tif ( amode )\n+\t\tif (add_cacheinfo(omode, osha, path, 2, 0, options))\n+\t\t\treturn -1;\n+\tif ( bmode )\n+\t\tif (add_cacheinfo(omode, osha, path, 3, 0, options))\n+\t\t\treturn -1;\n+\treturn 0;\n+}\n+\n+/*\n+ * TODO: there has to be a function in libgit doing this exact thing.\n+ */\n+static int remove_path(const char *name)\n+{\n+\tint ret;\n+\tchar *slash;\n+\n+\tret = unlink(name);\n+\tif ( ret )\n+\t\treturn ret;\n+\tint len = strlen(name);\n+\tchar *dirs = malloc(len+1);\n+\tmemcpy(dirs, name, len);\n+\tdirs[len] = '\\0';\n+\twhile ( (slash = strrchr(name, '/')) ) {\n+\t\t*slash = '\\0';\n+\t\tlen = slash - name;\n+\t\tif ( rmdir(name) != 0 )\n+\t\t\tbreak;\n+\t}\n+\tfree(dirs);\n+\treturn ret;\n+}\n+\n+/* General TODO: unC99ify the code: no declaration after code */\n+/* General TODO: no javaIfiCation: rename updateCache to update_cache */\n+/*\n+ * TODO: once we no longer call external programs, we'd probably be better of\n+ * not setting / getting the environment variable GIT_INDEX_FILE all the time.\n+ */\n+int remove_file(int clean, const char *path)\n+{\n+\tint updateCache = index_only || clean;\n+\tint updateWd = !index_only;\n+\n+\tif ( updateCache ) {\n+\t\tif (!cache_dirty)\n+\t\t\tread_cache_from(getenv(\"GIT_INDEX_FILE\"));\n+\t\tcache_dirty++;\n+\t\tif (remove_file_from_cache(path))\n+\t\t\treturn -1;\n+\t}\n+\tif ( updateWd )\n+\t{\n+\t\tunlink(path);\n+\t\tif ( errno != ENOENT || errno != EISDIR )\n+\t\t\treturn -1;\n+\t\tremove_path(path);\n+\t}\n+\treturn 0;\n+}\n+\n+static char *unique_path(const char *path, const char *branch)\n+{\n+\tchar *newpath = xmalloc(strlen(path) + 1 + strlen(branch) + 8 + 1);\n+\tstrcpy(newpath, path);\n+\tstrcat(newpath, \"~\");\n+\tchar *p = newpath + strlen(newpath);\n+\tstrcpy(p, branch);\n+\tfor ( ; *p; ++p )\n+\t\tif ( '/' == *p )\n+\t\t\t*p = '_';\n+\tint suffix = 0;\n+\tstruct stat st;\n+\twhile ( path_list_has_path(&currentFileSet, newpath) ||\n+\t\tpath_list_has_path(&currentDirectorySet, newpath) ||\n+\t\tlstat(newpath, &st) == 0 ) {\n+\t\tsprintf(p, \"_%d\", suffix++);\n+\t}\n+\tpath_list_insert(newpath, &currentFileSet);\n+\treturn newpath;\n+}\n+\n+/*\n+ * TODO: except for create_last, this so looks like\n+ * safe_create_leading_directories().\n+ */\n+static int mkdir_p(const char *path, unsigned long mode, int create_last)\n+{\n+\tchar *buf = strdup(path);\n+\tchar *p;\n+\n+\tfor ( p = buf; *p; ++p ) {\n+\t\tif ( *p != '/' )\n+\t\t\tcontinue;\n+\t\t*p = '\\0';\n+\t\tif (mkdir(buf, mode)) {\n+\t\t\tint e = errno;\n+\t\t\tif ( e == EEXIST ) {\n+\t\t\t\tstruct stat st;\n+\t\t\t\tif ( !stat(buf, &st) && S_ISDIR(st.st_mode) )\n+\t\t\t\t\tgoto next; /* ok */\n+\t\t\t\terrno = e;\n+\t\t\t}\n+\t\t\tfree(buf);\n+\t\t\treturn -1;\n+\t\t}\n+\tnext:\n+\t\t*p = '/';\n+\t}\n+\tfree(buf);\n+\tif ( create_last && mkdir(path, mode) )\n+\t\treturn -1;\n+\treturn 0;\n+}\n+\n+static void flush_buffer(int fd, const char *buf, unsigned long size)\n+{\n+\twhile (size > 0) {\n+\t\tlong ret = xwrite(fd, buf, size);\n+\t\tif (ret < 0) {\n+\t\t\t/* Ignore epipe */\n+\t\t\tif (errno == EPIPE)\n+\t\t\t\tbreak;\n+\t\t\tdie(\"merge-recursive: %s\", strerror(errno));\n+\t\t} else if (!ret) {\n+\t\t\tdie(\"merge-recursive: disk full?\");\n+\t\t}\n+\t\tsize -= ret;\n+\t\tbuf += ret;\n+\t}\n+}\n+\n+/* General TODO: reindent according to guide lines (no if ( blabla )) */\n+void update_file_flags(const unsigned char *sha,\n+\t\t   unsigned mode,\n+\t\t   const char *path,\n+\t\t   int updateCache,\n+\t\t   int updateWd)\n+{\n+\tif ( index_only )\n+\t\tupdateWd = 0;\n+\n+\tif ( updateWd ) {\n+\t\tchar type[20];\n+\t\tvoid *buf;\n+\t\tunsigned long size;\n+\n+\t\tbuf = read_sha1_file(sha, type, &size);\n+\t\tif (!buf)\n+\t\t\tdie(\"cannot read object %s '%s'\", sha1_to_hex(sha), path);\n+\t\tif ( strcmp(type, blob_type) != 0 )\n+\t\t\tdie(\"blob expected for %s '%s'\", sha1_to_hex(sha), path);\n+\n+\t\tif ( S_ISREG(mode) ) {\n+\t\t\tif ( mkdir_p(path, 0777, 0 /* don't create last element */) )\n+\t\t\t\tdie(\"failed to create path %s: %s\", path, strerror(errno));\n+\t\t\tunlink(path);\n+\t\t\tif ( mode & 0100 )\n+\t\t\t\tmode = 0777;\n+\t\t\telse\n+\t\t\t\tmode = 0666;\n+\t\t\tint fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);\n+\t\t\tif ( fd < 0 )\n+\t\t\t\tdie(\"failed to open %s: %s\", path, strerror(errno));\n+\t\t\tflush_buffer(fd, buf, size);\n+\t\t\tclose(fd);\n+\t\t} else if ( S_ISLNK(mode) ) {\n+\t\t\tchar *linkTarget = malloc(size + 1);\n+\t\t\tmemcpy(linkTarget, buf, size);\n+\t\t\tlinkTarget[size] = '\\0';\n+\t\t\tmkdir_p(path, 0777, 0);\n+\t\t\tsymlink(linkTarget, path);\n+\t\t} else\n+\t\t\tdie(\"do not know what to do with %06o %s '%s'\",\n+\t\t\t    mode, sha1_to_hex(sha), path);\n+\t}\n+\tif ( updateCache )\n+\t\tadd_cacheinfo(mode, sha, path, 0, updateWd, ADD_CACHE_OK_TO_ADD);\n+}\n+\n+/* TODO: is this often used? if not, do direct call */\n+void update_file(int clean,\n+\t\tconst unsigned char *sha,\n+\t\tunsigned mode,\n+\t\tconst char *path)\n+{\n+\tupdate_file_flags(sha, mode, path, index_only || clean, !index_only);\n+}\n+\n+/* Low level file merging, update and removal */\n+\n+struct merge_file_info\n+{\n+\tunsigned char sha[20];\n+\tunsigned mode;\n+\tunsigned clean:1,\n+\t\t merge:1;\n+};\n+\n+static char *git_unpack_file(const unsigned char *sha1, char *path)\n+{\n+\tvoid *buf;\n+\tchar type[20];\n+\tunsigned long size;\n+\tint fd;\n+\n+\tbuf = read_sha1_file(sha1, type, &size);\n+\tif (!buf || strcmp(type, blob_type))\n+\t\tdie(\"unable to read blob object %s\", sha1_to_hex(sha1));\n+\n+\tstrcpy(path, \".merge_file_XXXXXX\");\n+\tfd = mkstemp(path);\n+\tif (fd < 0)\n+\t\tdie(\"unable to create temp-file\");\n+\tflush_buffer(fd, buf, size);\n+\tclose(fd);\n+\treturn path;\n+}\n+\n+/*\n+ * TODO: the signature would be much more efficient using stage_data\n+ */\n+static struct merge_file_info merge_file(const char *oPath,\n+\t\t\t\t\t const unsigned char *oSha,\n+\t\t\t\t\t unsigned oMode,\n+\t\t\t\t\t const char *aPath,\n+\t\t\t\t\t const unsigned char *aSha,\n+\t\t\t\t\t unsigned aMode,\n+\t\t\t\t\t const char *bPath,\n+\t\t\t\t\t const unsigned char *bSha,\n+\t\t\t\t\t unsigned bMode,\n+\t\t\t\t\t const char *branch1Name,\n+\t\t\t\t\t const char *branch2Name)\n+{\n+\tstruct merge_file_info result;\n+\tresult.merge = 0;\n+\tresult.clean = 1;\n+\n+\tif ( (S_IFMT & aMode) != (S_IFMT & bMode) ) {\n+\t\tresult.clean = 0;\n+\t\tif ( S_ISREG(aMode) ) {\n+\t\t\tresult.mode = aMode;\n+\t\t\tmemcpy(result.sha, aSha, 20);\n+\t\t} else {\n+\t\t\tresult.mode = bMode;\n+\t\t\tmemcpy(result.sha, bSha, 20);\n+\t\t}\n+\t} else {\n+\t\tif ( memcmp(aSha, oSha, 20) != 0 && memcmp(bSha, oSha, 20) != 0 )\n+\t\t\tresult.merge = 1;\n+\n+\t\tresult.mode = aMode == oMode ? bMode: aMode;\n+\n+\t\tif ( memcmp(aSha, oSha, 20) == 0 )\n+\t\t\tmemcpy(result.sha, bSha, 20);\n+\t\telse if ( memcmp(bSha, oSha, 20) == 0 )\n+\t\t\tmemcpy(result.sha, aSha, 20);\n+\t\telse if ( S_ISREG(aMode) ) {\n+\n+\t\t\tint code = 1;\n+\t\t\tchar orig[PATH_MAX];\n+\t\t\tchar src1[PATH_MAX];\n+\t\t\tchar src2[PATH_MAX];\n+\n+\t\t\tgit_unpack_file(oSha, orig);\n+\t\t\tgit_unpack_file(aSha, src1);\n+\t\t\tgit_unpack_file(bSha, src2);\n+\n+\t\t\tconst char *argv[] = {\n+\t\t\t\t\"merge\", \"-L\", NULL, \"-L\", NULL, \"-L\", NULL,\n+\t\t\t\tsrc1, orig, src2,\n+\t\t\t\tNULL\n+\t\t\t};\n+\t\t\tchar *la, *lb, *lo;\n+\t\t\targv[2] = la = strdup(mkpath(\"%s/%s\", branch1Name, aPath));\n+\t\t\targv[6] = lb = strdup(mkpath(\"%s/%s\", branch2Name, bPath));\n+\t\t\targv[4] = lo = strdup(mkpath(\"orig/%s\", oPath));\n+\n+#if 0\n+\t\t\tprintf(\"%s %s %s %s %s %s %s %s %s %s\\n\",\n+\t\t\t       argv[0], argv[1], argv[2], argv[3], argv[4],\n+\t\t\t       argv[5], argv[6], argv[7], argv[8], argv[9]);\n+#endif\n+\t\t\tcode = run_command_v(10, argv);\n+\n+\t\t\tfree(la);\n+\t\t\tfree(lb);\n+\t\t\tfree(lo);\n+\t\t\tif ( code && code < -256 ) {\n+\t\t\t\tdie(\"Failed to execute 'merge'. merge(1) is used as the \"\n+\t\t\t\t    \"file-level merge tool. Is 'merge' in your path?\");\n+\t\t\t}\n+\t\t\tstruct stat st;\n+\t\t\tint fd = open(src1, O_RDONLY);\n+\t\t\tif (fd < 0 || fstat(fd, &st) < 0 ||\n+\t\t\t\t\tindex_fd(result.sha, fd, &st, 1,\n+\t\t\t\t\t\t\"blob\"))\n+\t\t\t\tdie(\"Unable to add %s to database\", src1);\n+\t\t\tclose(fd);\n+\n+\t\t\tunlink(orig);\n+\t\t\tunlink(src1);\n+\t\t\tunlink(src2);\n+\n+\t\t\tresult.clean = WEXITSTATUS(code) == 0;\n+\t\t} else {\n+\t\t\tif ( !(S_ISLNK(aMode) || S_ISLNK(bMode)) )\n+\t\t\t\tdie(\"cannot merge modes?\");\n+\n+\t\t\tmemcpy(result.sha, aSha, 20);\n+\n+\t\t\tif ( memcmp(aSha, bSha, 20) != 0 )\n+\t\t\t\tresult.clean = 0;\n+\t\t}\n+\t}\n+\n+\treturn result;\n+}\n+\n+static void conflict_rename_rename(struct rename *ren1,\n+\t\t\t\t   const char *branch1,\n+\t\t\t\t   struct rename *ren2,\n+\t\t\t\t   const char *branch2)\n+{\n+\tchar *del[2];\n+\tint delp = 0;\n+\tconst char *ren1_dst = ren1->pair->two->path;\n+\tconst char *ren2_dst = ren2->pair->two->path;\n+\tconst char *dstName1 = ren1_dst;\n+\tconst char *dstName2 = ren2_dst;\n+\tif (path_list_has_path(&currentDirectorySet, ren1_dst)) {\n+\t\tdstName1 = del[delp++] = unique_path(ren1_dst, branch1);\n+\t\toutput(\"%s is a directory in %s adding as %s instead\",\n+\t\t       ren1_dst, branch2, dstName1);\n+\t\tremove_file(0, ren1_dst);\n+\t}\n+\tif (path_list_has_path(&currentDirectorySet, ren2_dst)) {\n+\t\tdstName2 = del[delp++] = unique_path(ren2_dst, branch2);\n+\t\toutput(\"%s is a directory in %s adding as %s instead\",\n+\t\t       ren2_dst, branch1, dstName2);\n+\t\tremove_file(0, ren2_dst);\n+\t}\n+\tupdate_stages(dstName1,\n+\t\t      NULL, 0,\n+\t\t      ren1->pair->two->sha1, ren1->pair->two->mode,\n+\t\t      NULL, 0,\n+\t\t      1 /* clear */);\n+\tupdate_stages(dstName2,\n+\t\t      NULL, 0,\n+\t\t      NULL, 0,\n+\t\t      ren2->pair->two->sha1, ren2->pair->two->mode,\n+\t\t      1 /* clear */);\n+\twhile ( delp-- )\n+\t\tfree(del[delp]);\n+}\n+\n+static void conflict_rename_dir(struct rename *ren1,\n+\t\t\t\tconst char *branch1)\n+{\n+\tchar *newPath = unique_path(ren1->pair->two->path, branch1);\n+\toutput(\"Renaming %s to %s instead\", ren1->pair->one->path, newPath);\n+\tremove_file(0, ren1->pair->two->path);\n+\tupdate_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, newPath);\n+\tfree(newPath);\n+}\n+\n+static void conflict_rename_rename_2(struct rename *ren1,\n+\t\t\t\t     const char *branch1,\n+\t\t\t\t     struct rename *ren2,\n+\t\t\t\t     const char *branch2)\n+{\n+\tchar *newPath1 = unique_path(ren1->pair->two->path, branch1);\n+\tchar *newPath2 = unique_path(ren2->pair->two->path, branch2);\n+\toutput(\"Renaming %s to %s and %s to %s instead\",\n+\t       ren1->pair->one->path, newPath1,\n+\t       ren2->pair->one->path, newPath2);\n+\tremove_file(0, ren1->pair->two->path);\n+\tupdate_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, newPath1);\n+\tupdate_file(0, ren2->pair->two->sha1, ren2->pair->two->mode, newPath2);\n+\tfree(newPath2);\n+\tfree(newPath1);\n+}\n+\n+/* General TODO: get rid of all the debug messages */\n+static int process_renames(struct path_list *renamesA,\n+\t\t\t   struct path_list *renamesB,\n+\t\t\t   const char *branchNameA,\n+\t\t\t   const char *branchNameB)\n+{\n+\tint cleanMerge = 1, i;\n+\tstruct path_list srcNames = {NULL, 0, 0, 0}, byDstA = {NULL, 0, 0, 0}, byDstB = {NULL, 0, 0, 0};\n+\tconst struct rename *sre;\n+\n+\t/*\n+\t * TODO: think about a saner way to do this.\n+\t * Since both renamesA and renamesB are sorted, it should\n+\t * be much more efficient to traverse both simultaneously,\n+\t * only byDstA and byDstB should be needed.\n+\t */\n+\tdebug(\"processRenames...\\n\");\n+\tfor (i = 0; i < renamesA->nr; i++) {\n+\t\tsre = renamesA->items[i].util;\n+\t\tpath_list_insert(sre->pair->one->path, &srcNames);\n+\t\tpath_list_insert(sre->pair->two->path, &byDstA)->util\n+\t\t\t= sre->dst_entry;\n+\t}\n+\tfor (i = 0; i < renamesB->nr; i++) {\n+\t\tsre = renamesB->items[i].util;\n+\t\tpath_list_insert(sre->pair->one->path, &srcNames);\n+\t\tpath_list_insert(sre->pair->two->path, &byDstB)->util\n+\t\t\t= sre->dst_entry;\n+\t}\n+\n+\tfor (i = 0; i < srcNames.nr; i++) {\n+\t\tchar *src = srcNames.items[i].path;\n+\t\tstruct path_list *renames1, *renames2, *renames2Dst;\n+\t\tstruct rename *ren1, *ren2;\n+\t\tconst char *branchName1, *branchName2;\n+\t\tren1 = find_rename_bysrc(renamesA, src);\n+\t\tren2 = find_rename_bysrc(renamesB, src);\n+\t\t/* TODO: refactor, so that 1/2 are not needed */\n+\t\tif ( ren1 ) {\n+\t\t\trenames1 = renamesA;\n+\t\t\trenames2 = renamesB;\n+\t\t\trenames2Dst = &byDstB;\n+\t\t\tbranchName1 = branchNameA;\n+\t\t\tbranchName2 = branchNameB;\n+\t\t} else {\n+\t\t\trenames1 = renamesB;\n+\t\t\trenames2 = renamesA;\n+\t\t\trenames2Dst = &byDstA;\n+\t\t\tbranchName1 = branchNameB;\n+\t\t\tbranchName2 = branchNameA;\n+\t\t\tstruct rename *tmp = ren2;\n+\t\t\tren2 = ren1;\n+\t\t\tren1 = tmp;\n+\t\t}\n+\n+\t\tren1->dst_entry->processed = 1;\n+\t\tren1->src_entry->processed = 1;\n+\n+\t\tif ( ren1->processed )\n+\t\t\tcontinue;\n+\t\tren1->processed = 1;\n+\n+\t\tconst char *ren1_src = ren1->pair->one->path;\n+\t\tconst char *ren1_dst = ren1->pair->two->path;\n+\n+\t\tif ( ren2 ) {\n+\t\t\tconst char *ren2_src = ren2->pair->one->path;\n+\t\t\tconst char *ren2_dst = ren2->pair->two->path;\n+\t\t\t/* Renamed in 1 and renamed in 2 */\n+\t\t\tif (strcmp(ren1_src, ren2_src) != 0)\n+\t\t\t\tdie(\"ren1.src != ren2.src\");\n+\t\t\tren2->dst_entry->processed = 1;\n+\t\t\tren2->processed = 1;\n+\t\t\tif (strcmp(ren1_dst, ren2_dst) != 0) {\n+\t\t\t\tcleanMerge = 0;\n+\t\t\t\toutput(\"CONFLICT (rename/rename): \"\n+\t\t\t\t       \"Rename %s->%s in branch %s \"\n+\t\t\t\t       \"rename %s->%s in %s\",\n+\t\t\t\t       src, ren1_dst, branchName1,\n+\t\t\t\t       src, ren2_dst, branchName2);\n+\t\t\t\tconflict_rename_rename(ren1, branchName1, ren2, branchName2);\n+\t\t\t} else {\n+\t\t\t\tremove_file(1, ren1_src);\n+\t\t\t\tstruct merge_file_info mfi;\n+\t\t\t\tmfi = merge_file(ren1_src,\n+\t\t\t\t\t\t ren1->pair->one->sha1,\n+\t\t\t\t\t\t ren1->pair->one->mode,\n+\t\t\t\t\t\t ren1_dst,\n+\t\t\t\t\t\t ren1->pair->two->sha1,\n+\t\t\t\t\t\t ren1->pair->two->mode,\n+\t\t\t\t\t\t ren2_dst,\n+\t\t\t\t\t\t ren2->pair->two->sha1,\n+\t\t\t\t\t\t ren2->pair->two->mode,\n+\t\t\t\t\t\t branchName1,\n+\t\t\t\t\t\t branchName2);\n+\t\t\t\tif ( mfi.merge || !mfi.clean )\n+\t\t\t\t\toutput(\"Renaming %s->%s\", src, ren1_dst);\n+\n+\t\t\t\tif ( mfi.merge )\n+\t\t\t\t\toutput(\"Auto-merging %s\", ren1_dst);\n+\n+\t\t\t\tif ( !mfi.clean ) {\n+\t\t\t\t\toutput(\"CONFLICT (content): merge conflict in %s\",\n+\t\t\t\t\t       ren1_dst);\n+\t\t\t\t\tcleanMerge = 0;\n+\n+\t\t\t\t\tif ( !index_only )\n+\t\t\t\t\t\tupdate_stages(ren1_dst,\n+\t\t\t\t\t\t\t      ren1->pair->one->sha1,\n+\t\t\t\t\t\t\t      ren1->pair->one->mode,\n+\t\t\t\t\t\t\t      ren1->pair->two->sha1,\n+\t\t\t\t\t\t\t      ren1->pair->two->mode,\n+\t\t\t\t\t\t\t      ren2->pair->two->sha1,\n+\t\t\t\t\t\t\t      ren2->pair->two->mode,\n+\t\t\t\t\t\t\t      1 /* clear */);\n+\t\t\t\t}\n+\t\t\t\tupdate_file(mfi.clean, mfi.sha, mfi.mode, ren1_dst);\n+\t\t\t}\n+\t\t} else {\n+\t\t\t/* Renamed in 1, maybe changed in 2 */\n+\t\t\tremove_file(1, ren1_src);\n+\n+\t\t\tunsigned char srcShaOtherBranch[20], dstShaOtherBranch[20];\n+\t\t\tunsigned srcModeOtherBranch, dstModeOtherBranch;\n+\n+\t\t\tint stage = renamesA == renames1 ? 3: 2;\n+\n+\t\t\tmemcpy(srcShaOtherBranch, ren1->src_entry->stages[stage].sha, 20);\n+\t\t\tsrcModeOtherBranch = ren1->src_entry->stages[stage].mode;\n+\n+\t\t\tmemcpy(dstShaOtherBranch, ren1->dst_entry->stages[stage].sha, 20);\n+\t\t\tdstModeOtherBranch = ren1->dst_entry->stages[stage].mode;\n+\n+\t\t\tint tryMerge = 0;\n+\t\t\tchar *newPath;\n+\n+\t\t\tif (path_list_has_path(&currentDirectorySet, ren1_dst)) {\n+\t\t\t\tcleanMerge = 0;\n+\t\t\t\toutput(\"CONFLICT (rename/directory): Rename %s->%s in %s \"\n+\t\t\t\t       \" directory %s added in %s\",\n+\t\t\t\t       ren1_src, ren1_dst, branchName1,\n+\t\t\t\t       ren1_dst, branchName2);\n+\t\t\t\tconflict_rename_dir(ren1, branchName1);\n+\t\t\t} else if ( memcmp(srcShaOtherBranch, null_sha1, 20) == 0 ) {\n+\t\t\t\tcleanMerge = 0;\n+\t\t\t\toutput(\"CONFLICT (rename/delete): Rename %s->%s in %s \"\n+\t\t\t\t       \"and deleted in %s\",\n+\t\t\t\t       ren1_src, ren1_dst, branchName1,\n+\t\t\t\t       branchName2);\n+\t\t\t\tupdate_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, ren1_dst);\n+\t\t\t} else if ( memcmp(dstShaOtherBranch, null_sha1, 20) != 0 ) {\n+\t\t\t\tcleanMerge = 0;\n+\t\t\t\ttryMerge = 1;\n+\t\t\t\toutput(\"CONFLICT (rename/add): Rename %s->%s in %s. \"\n+\t\t\t\t       \"%s added in %s\",\n+\t\t\t\t       ren1_src, ren1_dst, branchName1,\n+\t\t\t\t       ren1_dst, branchName2);\n+\t\t\t\tnewPath = unique_path(ren1_dst, branchName2);\n+\t\t\t\toutput(\"Adding as %s instead\", newPath);\n+\t\t\t\tupdate_file(0, dstShaOtherBranch, dstModeOtherBranch, newPath);\n+\t\t\t} else if ( (ren2 = find_rename_bysrc(renames2Dst, ren1_dst)) ) {\n+\t\t\t\tcleanMerge = 0;\n+\t\t\t\tren2->processed = 1;\n+\t\t\t\toutput(\"CONFLICT (rename/rename): Rename %s->%s in %s. \"\n+\t\t\t\t       \"Rename %s->%s in %s\",\n+\t\t\t\t       ren1_src, ren1_dst, branchName1,\n+\t\t\t\t       ren2->pair->one->path, ren2->pair->two->path, branchName2);\n+\t\t\t\tconflict_rename_rename_2(ren1, branchName1, ren2, branchName2);\n+\t\t\t} else\n+\t\t\t\ttryMerge = 1;\n+\n+\t\t\tif ( tryMerge ) {\n+\t\t\t\tconst char *oname = ren1_src;\n+\t\t\t\tconst char *aname = ren1_dst;\n+\t\t\t\tconst char *bname = ren1_src;\n+\t\t\t\tunsigned char osha[20], asha[20], bsha[20];\n+\t\t\t\tunsigned omode = ren1->pair->one->mode;\n+\t\t\t\tunsigned amode = ren1->pair->two->mode;\n+\t\t\t\tunsigned bmode = srcModeOtherBranch;\n+\t\t\t\tmemcpy(osha, ren1->pair->one->sha1, 20);\n+\t\t\t\tmemcpy(asha, ren1->pair->two->sha1, 20);\n+\t\t\t\tmemcpy(bsha, srcShaOtherBranch, 20);\n+\t\t\t\tconst char *aBranch = branchName1;\n+\t\t\t\tconst char *bBranch = branchName2;\n+\n+\t\t\t\tif ( renamesA != renames1 ) {\n+\t\t\t\t\tmemswp(&aname, &bname, sizeof(aname));\n+\t\t\t\t\tmemswp(asha, bsha, 20);\n+\t\t\t\t\tmemswp(&aBranch, &bBranch, sizeof(aBranch));\n+\t\t\t\t}\n+\t\t\t\tstruct merge_file_info mfi;\n+\t\t\t\tmfi = merge_file(oname, osha, omode,\n+\t\t\t\t\t\t aname, asha, amode,\n+\t\t\t\t\t\t bname, bsha, bmode,\n+\t\t\t\t\t\t aBranch, bBranch);\n+\n+\t\t\t\tif ( mfi.merge || !mfi.clean )\n+\t\t\t\t\toutput(\"Renaming %s => %s\", ren1_src, ren1_dst);\n+\t\t\t\tif ( mfi.merge )\n+\t\t\t\t\toutput(\"Auto-merging %s\", ren1_dst);\n+\t\t\t\tif ( !mfi.clean ) {\n+\t\t\t\t\toutput(\"CONFLICT (rename/modify): Merge conflict in %s\",\n+\t\t\t\t\t       ren1_dst);\n+\t\t\t\t\tcleanMerge = 0;\n+\n+\t\t\t\t\tif ( !index_only )\n+\t\t\t\t\t\tupdate_stages(ren1_dst,\n+\t\t\t\t\t\t\t      osha, omode,\n+\t\t\t\t\t\t\t      asha, amode,\n+\t\t\t\t\t\t\t      bsha, bmode,\n+\t\t\t\t\t\t\t      1 /* clear */);\n+\t\t\t\t}\n+\t\t\t\tupdate_file(mfi.clean, mfi.sha, mfi.mode, ren1_dst);\n+\t\t\t}\n+\t\t}\n+\t}\n+\tpath_list_clear(&srcNames, 0);\n+\tdebug(\"  processRenames done\\n\");\n+\n+\tif (cache_dirty)\n+\t\tflush_cache();\n+\treturn cleanMerge;\n+}\n+\n+static unsigned char *has_sha(const unsigned char *sha)\n+{\n+\treturn memcmp(sha, null_sha1, 20) == 0 ? NULL: (unsigned char *)sha;\n+}\n+\n+/* Per entry merge function */\n+static int process_entry(const char *path, struct stage_data *entry,\n+\t\t\t const char *branch1Name,\n+\t\t\t const char *branch2Name)\n+{\n+\t/*\n+\tprintf(\"processing entry, clean cache: %s\\n\", index_only ? \"yes\": \"no\");\n+\tprint_index_entry(\"\\tpath: \", entry);\n+\t*/\n+\tint cleanMerge = 1;\n+\tunsigned char *oSha = has_sha(entry->stages[1].sha);\n+\tunsigned char *aSha = has_sha(entry->stages[2].sha);\n+\tunsigned char *bSha = has_sha(entry->stages[3].sha);\n+\tunsigned oMode = entry->stages[1].mode;\n+\tunsigned aMode = entry->stages[2].mode;\n+\tunsigned bMode = entry->stages[3].mode;\n+\n+\tif ( oSha && (!aSha || !bSha) ) {\n+\t\t/* Case A: Deleted in one */\n+\t\tif ( (!aSha && !bSha) ||\n+\t\t     (sha_eq(aSha, oSha) && !bSha) ||\n+\t\t     (!aSha && sha_eq(bSha, oSha)) ) {\n+\t\t\t/* Deleted in both or deleted in one and\n+\t\t\t * unchanged in the other */\n+\t\t\tif ( aSha )\n+\t\t\t\toutput(\"Removing %s\", path);\n+\t\t\tremove_file(1, path);\n+\t\t} else {\n+\t\t\t/* Deleted in one and changed in the other */\n+\t\t\tcleanMerge = 0;\n+\t\t\tif ( !aSha ) {\n+\t\t\t\toutput(\"CONFLICT (delete/modify): %s deleted in %s \"\n+\t\t\t\t       \"and modified in %s. Version %s of %s left in tree.\",\n+\t\t\t\t       path, branch1Name,\n+\t\t\t\t       branch2Name, branch2Name, path);\n+\t\t\t\tupdate_file(0, bSha, bMode, path);\n+\t\t\t} else {\n+\t\t\t\toutput(\"CONFLICT (delete/modify): %s deleted in %s \"\n+\t\t\t\t       \"and modified in %s. Version %s of %s left in tree.\",\n+\t\t\t\t       path, branch2Name,\n+\t\t\t\t       branch1Name, branch1Name, path);\n+\t\t\t\tupdate_file(0, aSha, aMode, path);\n+\t\t\t}\n+\t\t}\n+\n+\t} else if ( (!oSha && aSha && !bSha) ||\n+\t\t    (!oSha && !aSha && bSha) ) {\n+\t\t/* Case B: Added in one. */\n+\t\tconst char *addBranch;\n+\t\tconst char *otherBranch;\n+\t\tunsigned mode;\n+\t\tconst unsigned char *sha;\n+\t\tconst char *conf;\n+\n+\t\tif ( aSha ) {\n+\t\t\taddBranch = branch1Name;\n+\t\t\totherBranch = branch2Name;\n+\t\t\tmode = aMode;\n+\t\t\tsha = aSha;\n+\t\t\tconf = \"file/directory\";\n+\t\t} else {\n+\t\t\taddBranch = branch2Name;\n+\t\t\totherBranch = branch1Name;\n+\t\t\tmode = bMode;\n+\t\t\tsha = bSha;\n+\t\t\tconf = \"directory/file\";\n+\t\t}\n+\t\tif ( path_list_has_path(&currentDirectorySet, path) ) {\n+\t\t\tcleanMerge = 0;\n+\t\t\tconst char *newPath = unique_path(path, addBranch);\n+\t\t\toutput(\"CONFLICT (%s): There is a directory with name %s in %s. \"\n+\t\t\t       \"Adding %s as %s\",\n+\t\t\t       conf, path, otherBranch, path, newPath);\n+\t\t\tremove_file(0, path);\n+\t\t\tupdate_file(0, sha, mode, newPath);\n+\t\t} else {\n+\t\t\toutput(\"Adding %s\", path);\n+\t\t\tupdate_file(1, sha, mode, path);\n+\t\t}\n+\t} else if ( !oSha && aSha && bSha ) {\n+\t\t/* Case C: Added in both (check for same permissions). */\n+\t\tif ( sha_eq(aSha, bSha) ) {\n+\t\t\tif ( aMode != bMode ) {\n+\t\t\t\tcleanMerge = 0;\n+\t\t\t\toutput(\"CONFLICT: File %s added identically in both branches, \"\n+\t\t\t\t       \"but permissions conflict %06o->%06o\",\n+\t\t\t\t       path, aMode, bMode);\n+\t\t\t\toutput(\"CONFLICT: adding with permission: %06o\", aMode);\n+\t\t\t\tupdate_file(0, aSha, aMode, path);\n+\t\t\t} else {\n+\t\t\t\t/* This case is handled by git-read-tree */\n+\t\t\t\tassert(0 && \"This case must be handled by git-read-tree\");\n+\t\t\t}\n+\t\t} else {\n+\t\t\tcleanMerge = 0;\n+\t\t\tconst char *newPath1 = unique_path(path, branch1Name);\n+\t\t\tconst char *newPath2 = unique_path(path, branch2Name);\n+\t\t\toutput(\"CONFLICT (add/add): File %s added non-identically \"\n+\t\t\t       \"in both branches. Adding as %s and %s instead.\",\n+\t\t\t       path, newPath1, newPath2);\n+\t\t\tremove_file(0, path);\n+\t\t\tupdate_file(0, aSha, aMode, newPath1);\n+\t\t\tupdate_file(0, bSha, bMode, newPath2);\n+\t\t}\n+\n+\t} else if ( oSha && aSha && bSha ) {\n+\t\t/* case D: Modified in both, but differently. */\n+\t\toutput(\"Auto-merging %s\", path);\n+\t\tstruct merge_file_info mfi;\n+\t\tmfi = merge_file(path, oSha, oMode,\n+\t\t\t\t path, aSha, aMode,\n+\t\t\t\t path, bSha, bMode,\n+\t\t\t\t branch1Name, branch2Name);\n+\n+\t\tif ( mfi.clean )\n+\t\t\tupdate_file(1, mfi.sha, mfi.mode, path);\n+\t\telse {\n+\t\t\tcleanMerge = 0;\n+\t\t\toutput(\"CONFLICT (content): Merge conflict in %s\", path);\n+\n+\t\t\tif ( index_only )\n+\t\t\t\tupdate_file(0, mfi.sha, mfi.mode, path);\n+\t\t\telse\n+\t\t\t\tupdate_file_flags(mfi.sha, mfi.mode, path,\n+\t\t\t\t\t      0 /* updateCache */, 1 /* updateWd */);\n+\t\t}\n+\t} else\n+\t\tdie(\"Fatal merge failure, shouldn't happen.\");\n+\n+\tif (cache_dirty)\n+\t\tflush_cache();\n+\n+\treturn cleanMerge;\n+}\n+\n+static struct merge_tree_result merge_trees(struct tree *head,\n+\t\t\t\t\t    struct tree *merge,\n+\t\t\t\t\t    struct tree *common,\n+\t\t\t\t\t    const char *branch1Name,\n+\t\t\t\t\t    const char *branch2Name)\n+{\n+\tint code;\n+\tstruct merge_tree_result result = { NULL, 0 };\n+\tif ( !memcmp(common->object.sha1, merge->object.sha1, 20) ) {\n+\t\toutput(\"Already uptodate!\");\n+\t\tresult.tree = head;\n+\t\tresult.clean = 1;\n+\t\treturn result;\n+\t}\n+\n+\tdebug(\"merge_trees ...\\n\");\n+\tcode = git_merge_trees(index_only ? \"-i\": \"-u\", common, head, merge);\n+\n+\tif ( code != 0 )\n+\t\tdie(\"merging of trees %s and %s failed\",\n+\t\t    sha1_to_hex(head->object.sha1),\n+\t\t    sha1_to_hex(merge->object.sha1));\n+\n+\tresult.tree = git_write_tree();\n+\n+\tif ( !result.tree ) {\n+\t\tpath_list_clear(&currentFileSet, 1);\n+\t\tpath_list_clear(&currentDirectorySet, 1);\n+\t\tget_files_dirs(head, &currentFileSet, &currentDirectorySet);\n+\t\tget_files_dirs(merge, &currentFileSet, &currentDirectorySet);\n+\n+\t\tstruct path_list *entries = get_unmerged();\n+\t\tstruct path_list *re_head, *re_merge;\n+\t\tre_head  = get_renames(head, common, head, merge, entries);\n+\t\tre_merge = get_renames(merge, common, head, merge, entries);\n+\t\tresult.clean = process_renames(re_head, re_merge,\n+\t\t\t\t\t       branch1Name, branch2Name);\n+\t\tdebug(\"\\tprocessing entries...\\n\");\n+\t\tint i;\n+\t\tfor (i = 0; i < entries->nr; i++) {\n+\t\t\tconst char *path = entries->items[i].path;\n+\t\t\tstruct stage_data *e = entries->items[i].util;\n+\t\t\tif (e->processed)\n+\t\t\t\tcontinue;\n+\t\t\tif (!process_entry(path, e, branch1Name, branch2Name))\n+\t\t\t\tresult.clean = 0;\n+\t\t}\n+\n+\t\tfree_rename_entries(&re_merge);\n+\t\tfree_rename_entries(&re_head);\n+\t\tfree_index_entries(&entries);\n+\n+\t\tif (result.clean || index_only)\n+\t\t\tresult.tree = git_write_tree();\n+\t\telse\n+\t\t\tresult.tree = NULL;\n+\t\tdebug(\"\\t  processing entries done\\n\");\n+\t} else {\n+\t\tresult.clean = 1;\n+\t\tprintf(\"merging of trees %s and %s resulted in %s\\n\",\n+\t\t       sha1_to_hex(head->object.sha1),\n+\t\t       sha1_to_hex(merge->object.sha1),\n+\t\t       sha1_to_hex(result.tree->object.sha1));\n+\t}\n+\n+\tdebug(\"  merge_trees done\\n\");\n+\treturn result;\n+}\n+\n+/*\n+ * Merge the commits h1 and h2, return the resulting virtual\n+ * commit object and a flag indicating the cleaness of the merge.\n+ */\n+static\n+struct merge_result merge(struct commit *h1,\n+\t\t\t  struct commit *h2,\n+\t\t\t  const char *branch1Name,\n+\t\t\t  const char *branch2Name,\n+\t\t\t  int callDepth /* =0 */,\n+\t\t\t  struct commit *ancestor /* =None */)\n+{\n+\tstruct merge_result result = { NULL, 0 };\n+\tconst char *msg;\n+\tint msglen;\n+\tstruct commit_list *ca = NULL, *iter;\n+\tstruct commit *mergedCA;\n+\tstruct merge_tree_result mtr;\n+\n+\toutput(\"Merging:\");\n+\tmsg = commit_title(h1, &msglen);\n+\t/* TODO: refactor. we always show the sha1 with the title */\n+\toutput(\"%s %.*s\", commit_hex_sha1(h1), msglen, msg);\n+\tmsg = commit_title(h2, &msglen);\n+\toutput(\"%s %.*s\", commit_hex_sha1(h2), msglen, msg);\n+\n+\tif ( ancestor )\n+\t\tcommit_list_insert(ancestor, &ca);\n+\telse\n+\t\tca = get_merge_bases(h1, h2, 1);\n+\n+\toutput(\"found %u common ancestor(s):\", commit_list_count(ca));\n+\tfor (iter = ca; iter; iter = iter->next) {\n+\t\tmsg = commit_title(iter->item, &msglen);\n+\t\toutput(\"%s %.*s\", commit_hex_sha1(iter->item), msglen, msg);\n+\t}\n+\n+\tmergedCA = pop_commit(&ca);\n+\n+\t/* TODO: what happens when merge with virtual commits fails? */\n+\tfor (iter = ca; iter; iter = iter->next) {\n+\t\toutput_indent = callDepth + 1;\n+\t\tresult = merge(mergedCA, iter->item,\n+\t\t\t       \"Temporary merge branch 1\",\n+\t\t\t       \"Temporary merge branch 2\",\n+\t\t\t       callDepth + 1,\n+\t\t\t       NULL);\n+\t\tmergedCA = result.commit;\n+\t\toutput_indent = callDepth;\n+\n+\t\tif ( !mergedCA )\n+\t\t\tdie(\"merge returned no commit\");\n+\t}\n+\n+\tif ( callDepth == 0 ) {\n+\t\tsetup_index(0);\n+\t\tindex_only = 0;\n+\t} else {\n+\t\tsetup_index(1);\n+\t\tgit_read_tree(h1->tree);\n+\t\tindex_only = 1;\n+\t}\n+\n+\tmtr = merge_trees(h1->tree, h2->tree,\n+\t\t\t  mergedCA->tree, branch1Name, branch2Name);\n+\n+\tif ( !ancestor && (mtr.clean || index_only) ) {\n+\t\tresult.commit = make_virtual_commit(mtr.tree, \"merged tree\");\n+\t\tcommit_list_insert(h1, &result.commit->parents);\n+\t\tcommit_list_insert(h2, &result.commit->parents->next);\n+\t} else\n+\t\tresult.commit = NULL;\n+\n+\tresult.clean = mtr.clean;\n+\treturn result;\n+}\n+\n+static struct commit *get_ref(const char *ref)\n+{\n+\tunsigned char sha1[20];\n+\tstruct object *object;\n+\t\n+\tif (get_sha1(ref, sha1))\n+\t\tdie(\"Could not resolve ref '%s'\", ref);\n+\tobject = deref_tag(parse_object(sha1), ref, strlen(ref));\n+\tif (object->type != TYPE_COMMIT)\n+\t\treturn NULL;\n+\tif (parse_commit((struct commit *)object))\n+\t\tdie(\"Could not parse commit '%s'\", sha1_to_hex(object->sha1));\n+\treturn (struct commit *)object;\n+}\n+\n+int main(int argc, char *argv[])\n+{\n+\tstatic const char *bases[2];\n+\tstatic unsigned bases_count = 0;\n+\n+\toriginal_index_file = getenv(\"GIT_INDEX_FILE\");\n+\n+\tif (!original_index_file)\n+\t\toriginal_index_file = strdup(git_path(\"index\"));\n+\n+\ttemporary_index_file = strdup(git_path(\"mrg-rcrsv-tmp-idx\"));\n+\n+\tif (argc < 4)\n+\t\tdie(\"Usage: %s <base>... -- <head> <remote> ...\\n\", argv[0]);\n+\n+\tint i;\n+\tfor (i = 1; i < argc; ++i) {\n+\t\tif (!strcmp(argv[i], \"--\"))\n+\t\t\tbreak;\n+\t\tif (bases_count < sizeof(bases)/sizeof(*bases))\n+\t\t\tbases[bases_count++] = argv[i];\n+\t}\n+\tif (argc - i != 3) /* \"--\" \"<head>\" \"<remote>\" */\n+\t\tdie(\"Not handling anything other than two heads merge.\");\n+\n+\tconst char *branch1, *branch2;\n+\n+\tbranch1 = argv[++i];\n+\tbranch2 = argv[++i];\n+\tprintf(\"Merging %s with %s\\n\", branch1, branch2);\n+\n+\tstruct merge_result result;\n+\tstruct commit *h1 = get_ref(branch1);\n+\tstruct commit *h2 = get_ref(branch2);\n+\n+\tif (bases_count == 1) {\n+\t\tstruct commit *ancestor = get_ref(bases[0]);\n+\t\tresult = merge(h1, h2, branch1, branch2, 0, ancestor);\n+\t} else\n+\t\tresult = merge(h1, h2, branch1, branch2, 0, NULL);\n+\n+\tif (cache_dirty)\n+\t\tflush_cache();\n+\n+\treturn result.clean ? 0: 1;\n+}\n+\n+/*\n+vim: sw=8 noet\n+*/\ndiff --git a/path-list.c b/path-list.c\nnew file mode 100644\nindex 0000000..f15a10d\n--- /dev/null\n+++ b/path-list.c\n@@ -0,0 +1,105 @@\n+#include <stdio.h>\n+#include \"cache.h\"\n+#include \"path-list.h\"\n+\n+/* if there is no exact match, point to the index where the entry could be\n+ * inserted */\n+static int get_entry_index(const struct path_list *list, const char *path,\n+\t\tint *exact_match)\n+{\n+\tint left = -1, right = list->nr;\n+\n+\twhile (left + 1 < right) {\n+\t\tint middle = (left + right) / 2;\n+\t\tint compare = strcmp(path, list->items[middle].path);\n+\t\tif (compare < 0)\n+\t\t\tright = middle;\n+\t\telse if (compare > 0)\n+\t\t\tleft = middle;\n+\t\telse {\n+\t\t\t*exact_match = 1;\n+\t\t\treturn middle;\n+\t\t}\n+\t}\n+\n+\t*exact_match = 0;\n+\treturn right;\n+}\n+\n+/* returns -1-index if already exists */\n+static int add_entry(struct path_list *list, const char *path)\n+{\n+\tint exact_match;\n+\tint index = get_entry_index(list, path, &exact_match);\n+\n+\tif (exact_match)\n+\t\treturn -1 - index;\n+\n+\tif (list->nr + 1 >= list->alloc) {\n+\t\tlist->alloc += 32;\n+\t\tlist->items = xrealloc(list->items, list->alloc\n+\t\t\t\t* sizeof(struct path_list_item));\n+\t}\n+\tif (index < list->nr)\n+\t\tmemmove(list->items + index + 1, list->items + index,\n+\t\t\t\t(list->nr - index)\n+\t\t\t\t* sizeof(struct path_list_item));\n+\tlist->items[index].path = list->strdup_paths ?\n+\t\tstrdup(path) : (char *)path;\n+\tlist->items[index].util = NULL;\n+\tlist->nr++;\n+\n+\treturn index;\n+}\n+\n+struct path_list_item *path_list_insert(const char *path, struct path_list *list)\n+{\n+\tint index = add_entry(list, path);\n+\n+\tif (index < 0)\n+\t\tindex = 1 - index;\n+\n+\treturn list->items + index;\n+}\n+\n+int path_list_has_path(const struct path_list *list, const char *path)\n+{\n+\tint exact_match;\n+\tget_entry_index(list, path, &exact_match);\n+\treturn exact_match;\n+}\n+\n+struct path_list_item *path_list_lookup(const char *path, struct path_list *list)\n+{\n+\tint exact_match, i = get_entry_index(list, path, &exact_match);\n+\tif (!exact_match)\n+\t\treturn NULL;\n+\treturn list->items + i;\n+}\n+\n+void path_list_clear(struct path_list *list, int free_items)\n+{\n+\tif (list->items) {\n+\t\tint i;\n+\t\tif (free_items)\n+\t\t\tfor (i = 0; i < list->nr; i++) {\n+\t\t\t\tif (list->strdup_paths)\n+\t\t\t\t\tfree(list->items[i].path);\n+\t\t\t\tif (list->items[i].util)\n+\t\t\t\t\tfree(list->items[i].util);\n+\t\t\t}\n+\t\tfree(list->items);\n+\t}\n+\tlist->items = NULL;\n+\tlist->nr = list->alloc = 0;\n+}\n+\n+void print_path_list(const char *text, const struct path_list *p)\n+{\n+\tint i;\n+\tif ( text )\n+\t\tprintf(\"%s\\n\", text);\n+\tfor (i = 0; i < p->nr; i++)\n+\t\tprintf(\"%s:%p\\n\", p->items[i].path, p->items[i].util);\n+}\n+\ndiff --git a/path-list.h b/path-list.h\nnew file mode 100644\nindex 0000000..d6401ea\n--- /dev/null\n+++ b/path-list.h\n@@ -0,0 +1,22 @@\n+#ifndef _PATH_LIST_H_\n+#define _PATH_LIST_H_\n+\n+struct path_list_item {\n+\tchar *path;\n+\tvoid *util;\n+};\n+struct path_list\n+{\n+\tstruct path_list_item *items;\n+\tunsigned int nr, alloc;\n+\tunsigned int strdup_paths:1;\n+};\n+\n+void print_path_list(const char *text, const struct path_list *p);\n+\n+int path_list_has_path(const struct path_list *list, const char *path);\n+void path_list_clear(struct path_list *list, int free_items);\n+struct path_list_item *path_list_insert(const char *path, struct path_list *list);\n+struct path_list_item *path_list_lookup(const char *path, struct path_list *list);\n+\n+#endif /* _PATH_LIST_H_ */\ndiff --git a/read-cache.c b/read-cache.c\nindex 3c32aae..f6e1b70 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -24,6 +24,11 @@ unsigned int active_nr = 0, active_alloc\n \n struct cache_tree *active_cache_tree = NULL;\n \n+int cache_errno = 0;\n+\n+static void *cache_mmap = NULL;\n+static size_t cache_mmap_size = 0;\n+\n /*\n  * This only updates the \"non-critical\" parts of the directory\n  * cache, ie the parts that aren't tracked by GIT, and only used\n@@ -577,22 +582,6 @@ int add_cache_entry(struct cache_entry *\n \treturn 0;\n }\n \n-/* Three functions to allow overloaded pointer return; see linux/err.h */\n-static inline void *ERR_PTR(long error)\n-{\n-\treturn (void *) error;\n-}\n-\n-static inline long PTR_ERR(const void *ptr)\n-{\n-\treturn (long) ptr;\n-}\n-\n-static inline long IS_ERR(const void *ptr)\n-{\n-\treturn (unsigned long)ptr > (unsigned long)-1000L;\n-}\n-\n /*\n  * \"refresh\" does not calculate a new sha1 file or bring the\n  * cache up-to-date for mode/content changes. But what it\n@@ -604,14 +593,16 @@ static inline long IS_ERR(const void *pt\n  * For example, you'd want to do this after doing a \"git-read-tree\",\n  * to link up the stat cache details with the proper files.\n  */\n-static struct cache_entry *refresh_entry(struct cache_entry *ce, int really)\n+struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really)\n {\n \tstruct stat st;\n \tstruct cache_entry *updated;\n \tint changed, size;\n \n-\tif (lstat(ce->name, &st) < 0)\n-\t\treturn ERR_PTR(-errno);\n+\tif (lstat(ce->name, &st) < 0) {\n+\t\tcache_errno = errno;\n+\t\treturn NULL;\n+\t}\n \n \tchanged = ce_match_stat(ce, &st, really);\n \tif (!changed) {\n@@ -619,11 +610,13 @@ static struct cache_entry *refresh_entry\n \t\t    !(ce->ce_flags & htons(CE_VALID)))\n \t\t\t; /* mark this one VALID again */\n \t\telse\n-\t\t\treturn NULL;\n+\t\t\treturn ce;\n \t}\n \n-\tif (ce_modified(ce, &st, really))\n-\t\treturn ERR_PTR(-EINVAL);\n+\tif (ce_modified(ce, &st, really)) {\n+\t\tcache_errno = EINVAL;\n+\t\treturn NULL;\n+\t}\n \n \tsize = ce_size(ce);\n \tupdated = xmalloc(size);\n@@ -666,13 +659,13 @@ int refresh_cache(unsigned int flags)\n \t\t\tcontinue;\n \t\t}\n \n-\t\tnew = refresh_entry(ce, really);\n-\t\tif (!new)\n+\t\tnew = refresh_cache_entry(ce, really);\n+\t\tif (new == ce)\n \t\t\tcontinue;\n-\t\tif (IS_ERR(new)) {\n-\t\t\tif (not_new && PTR_ERR(new) == -ENOENT)\n+\t\tif (!new) {\n+\t\t\tif (not_new && cache_errno == ENOENT)\n \t\t\t\tcontinue;\n-\t\t\tif (really && PTR_ERR(new) == -EINVAL) {\n+\t\t\tif (really && cache_errno == EINVAL) {\n \t\t\t\t/* If we are doing --really-refresh that\n \t\t\t\t * means the index is not valid anymore.\n \t\t\t\t */\n@@ -729,39 +722,43 @@ static int read_index_extension(const ch\n \n int read_cache(void)\n {\n+\treturn read_cache_from(get_index_file());\n+}\n+\n+/* remember to discard_cache() before reading a different cache! */\n+int read_cache_from(const char *path)\n+{\n \tint fd, i;\n \tstruct stat st;\n-\tunsigned long size, offset;\n-\tvoid *map;\n+\tunsigned long offset;\n \tstruct cache_header *hdr;\n \n \terrno = EBUSY;\n-\tif (active_cache)\n+\tif (cache_mmap)\n \t\treturn active_nr;\n \n \terrno = ENOENT;\n \tindex_file_timestamp = 0;\n-\tfd = open(get_index_file(), O_RDONLY);\n+\tfd = open(path, O_RDONLY);\n \tif (fd < 0) {\n \t\tif (errno == ENOENT)\n \t\t\treturn 0;\n \t\tdie(\"index file open failed (%s)\", strerror(errno));\n \t}\n \n-\tsize = 0; // avoid gcc warning\n-\tmap = MAP_FAILED;\n+\tcache_mmap = MAP_FAILED;\n \tif (!fstat(fd, &st)) {\n-\t\tsize = st.st_size;\n+\t\tcache_mmap_size = st.st_size;\n \t\terrno = EINVAL;\n-\t\tif (size >= sizeof(struct cache_header) + 20)\n-\t\t\tmap = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);\n+\t\tif (cache_mmap_size >= sizeof(struct cache_header) + 20)\n+\t\t\tcache_mmap = mmap(NULL, cache_mmap_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);\n \t}\n \tclose(fd);\n-\tif (map == MAP_FAILED)\n+\tif (cache_mmap == MAP_FAILED)\n \t\tdie(\"index file mmap failed (%s)\", strerror(errno));\n \n-\thdr = map;\n-\tif (verify_hdr(hdr, size) < 0)\n+\thdr = cache_mmap;\n+\tif (verify_hdr(hdr, cache_mmap_size) < 0)\n \t\tgoto unmap;\n \n \tactive_nr = ntohl(hdr->hdr_entries);\n@@ -770,12 +767,12 @@ int read_cache(void)\n \n \toffset = sizeof(*hdr);\n \tfor (i = 0; i < active_nr; i++) {\n-\t\tstruct cache_entry *ce = (struct cache_entry *) ((char *) map + offset);\n+\t\tstruct cache_entry *ce = (struct cache_entry *) ((char *) cache_mmap + offset);\n \t\toffset = offset + ce_size(ce);\n \t\tactive_cache[i] = ce;\n \t}\n \tindex_file_timestamp = st.st_mtime;\n-\twhile (offset <= size - 20 - 8) {\n+\twhile (offset <= cache_mmap_size - 20 - 8) {\n \t\t/* After an array of active_nr index entries,\n \t\t * there can be arbitrary number of extended\n \t\t * sections, each of which is prefixed with\n@@ -783,10 +780,10 @@ int read_cache(void)\n \t\t * in 4-byte network byte order.\n \t\t */\n \t\tunsigned long extsize;\n-\t\tmemcpy(&extsize, (char *) map + offset + 4, 4);\n+\t\tmemcpy(&extsize, (char *) cache_mmap + offset + 4, 4);\n \t\textsize = ntohl(extsize);\n-\t\tif (read_index_extension(((const char *) map) + offset,\n-\t\t\t\t\t (char *) map + offset + 8,\n+\t\tif (read_index_extension(((const char *) cache_mmap) + offset,\n+\t\t\t\t\t (char *) cache_mmap + offset + 8,\n \t\t\t\t\t extsize) < 0)\n \t\t\tgoto unmap;\n \t\toffset += 8;\n@@ -795,11 +792,28 @@ int read_cache(void)\n \treturn active_nr;\n \n unmap:\n-\tmunmap(map, size);\n+\tmunmap(cache_mmap, cache_mmap_size);\n \terrno = EINVAL;\n \tdie(\"index file corrupt\");\n }\n \n+int discard_cache()\n+{\n+\tint ret;\n+\t\n+\tif (cache_mmap == NULL)\n+\t\treturn 0;\n+\tret = munmap(cache_mmap, cache_mmap_size);\n+\tcache_mmap = NULL;\n+\tcache_mmap_size = 0;\n+\tactive_nr = active_cache_changed = 0;\n+\tindex_file_timestamp = 0;\n+\tcache_tree_free(&active_cache_tree);\n+\t\n+\t/* no need to throw away allocated active_cache */\n+\treturn ret;\n+}\n+\n #define WRITE_BUFFER_SIZE 8192\n static unsigned char write_buffer[WRITE_BUFFER_SIZE];\n static unsigned long write_buffer_len;\ndiff --git a/t/t3402-rebase-merge.sh b/t/t3402-rebase-merge.sh\nindex d34c6cf..b70e177 100755\n--- a/t/t3402-rebase-merge.sh\n+++ b/t/t3402-rebase-merge.sh\n@@ -51,7 +51,7 @@ test_expect_success setup '\n '\n \n test_expect_success 'reference merge' '\n-\tgit merge -s recursive \"reference merge\" HEAD master\n+\tgit merge -s recur \"reference merge\" HEAD master\n '\n \n test_expect_success rebase '\n"},{"id":"23449","messageId":"1152421230.2103.31.camel@dv","threadId":"4806","inReplyTo":"Pine.LNX.4.63.0607081840280.29667@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [WIP] Status update on merge-recursive in C","fromName":"Pavel Roskin","fromEmail":"proski@gnu.org","sentAt":"2006-07-09T05:00:30Z","receivedAt":"2006-07-09T05:00:30Z","isPatch":false,"sender":{"key":"proski@gnu.org","avatar":null},"body":"Hello, Dscho!\n\nOn Sat, 2006-07-08 at 18:42 +0200, Johannes Schindelin wrote:\n> This is just an update for people being interested. Alex and me were\n> busy with that project for a few days now. While it has progressed nicely,\n> there are quite a couple TODOs in merge-recursive.c, just search for \"TODO\".\n\nThat's great.  Just a few notes regarding the patch.\n\nWhy do you list \"recur\" twice in all_strategies in the git-merge.sh?\nThe second \"recur\" should probably be \"recursive\".\n\nTest for Python in the git-rebase.sh is probably wrong.  \"recur\" doesn't\nneed Python - that's the whole point.  In fact, if I build git with\nNO_PYTHON, TEST fails in t3401-rebase-partial.sh\n\nApart from that, the tests pass on i386, x86_64 and PowerPC, all running\nFedora Core 5.\n\nI tries to run sparse of the C files, and it complains about two things\n- variable declarations in the middle of the code (allowed by c99 but\nnot by ANSI C) and incomplete function declarations (no \"void\").  It's\nnot C++, let's stick to the C standards.\n\nI'm attaching a patch that fixes everything except variable\ndeclarations.  There are so many of them that it could be done once the\ncode stabilizes a bit.\n\nIt would be great to have a patch that actually removes the old\n\"recursive\" and makes the new strategy \"recursive\".  I understand it's\nthe plan, right?\n\n---\n\n git-merge.sh      |    2 +-\n git-rebase.sh     |    2 +-\n merge-recursive.c |    9 +++++----\n read-cache.c      |    2 +-\n 4 files changed, 8 insertions(+), 7 deletions(-)\n\ndiff --git a/git-merge.sh b/git-merge.sh\nindex b26ca14..240e5ee 100755\n--- a/git-merge.sh\n+++ b/git-merge.sh\n@@ -9,7 +9,7 @@ USAGE='[-n] [--no-commit] [--squash] [-s\n LF='\n '\n \n-all_strategies='recur recur octopus resolve stupid ours'\n+all_strategies='recur recursive octopus resolve stupid ours'\n default_twohead_strategies='recur'\n default_octopus_strategies='octopus'\n no_trivial_merge_strategies='ours'\ndiff --git a/git-rebase.sh b/git-rebase.sh\nindex 5d4c7d2..6472970 100755\n--- a/git-rebase.sh\n+++ b/git-rebase.sh\n@@ -292,7 +292,7 @@ then\n \texit $?\n fi\n \n-if test \"@@NO_PYTHON@@\" && test \"$strategy\" = \"recur\"\n+if test \"@@NO_PYTHON@@\" && test \"$strategy\" = \"recursive\"\n then\n \tdie 'The recursive merge strategy currently relies on Python,\n which this installation of git was not configured with.  Please\nconsider\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 07a2b38..1cae804 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -61,7 +61,8 @@ static void show_ce_entry(const char *ta\n \tfputc('\\n', stderr);\n }\n \n-static void ls_files() {\n+static void ls_files(void)\n+{\n \tint i;\n \tfor (i = 0; i < active_nr; i++) {\n \t\tstruct cache_entry *ce = active_cache[i];\n@@ -207,7 +208,7 @@ static const char *original_index_file;\n static const char *temporary_index_file;\n static int cache_dirty = 0;\n \n-static int flush_cache()\n+static int flush_cache(void)\n {\n \t/* flush temporary index */\n \tstruct lock_file *lock = xcalloc(1, sizeof(struct lock_file));\n@@ -331,7 +332,7 @@ #endif\n /*\n  * TODO: this can be streamlined by refactoring builtin-write-tree.c\n  */\n-static struct tree *git_write_tree()\n+static struct tree *git_write_tree(void)\n {\n #if 0\n \tfprintf(stderr, \"GIT_INDEX_FILE='%s' git-write-tree\\n\",\n@@ -467,7 +468,7 @@ static void free_index_entries(struct pa\n  * Create a dictionary mapping file names to CacheEntry objects. The\n  * dictionary contains one entry for every path with a non-zero stage\nentry.\n  */\n-static struct path_list *get_unmerged()\n+static struct path_list *get_unmerged(void)\n {\n \tstruct path_list *unmerged = xcalloc(1, sizeof(struct path_list));\n \tint i;\ndiff --git a/read-cache.c b/read-cache.c\nindex f6e1b70..0ef89a9 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -797,7 +797,7 @@ unmap:\n \tdie(\"index file corrupt\");\n }\n \n-int discard_cache()\n+int discard_cache(void)\n {\n \tint ret;\n \t\n\n\n-- \nRegards,\nPavel Roskin\n"},{"id":"23456","messageId":"7vpsgfnse8.fsf@assigned-by-dhcp.cox.net","threadId":"4806","inReplyTo":"1152421230.2103.31.camel@dv","subject":"Re: [WIP] Status update on merge-recursive in C","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-07-09T07:34:23Z","receivedAt":"2006-07-09T07:34:23Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Pavel Roskin <proski@gnu.org> writes:\n\n> Why do you list \"recur\" twice in all_strategies in the git-merge.sh?\n> The second \"recur\" should probably be \"recursive\".\n> ...\n> Test for Python in the git-rebase.sh is probably wrong.  \"recur\" doesn't\n> need Python - that's the whole point.  In fact, if I build git with\n> NO_PYTHON, TEST fails in t3401-rebase-partial.sh\n\nI noticed them as well but I understand that is why the patch is\nmarked as WIP.  I do not mind these details much at this point\nyet.\n\nI think it may make sense, during the development, to make\ngit-merge.sh notice an environment GIT_USE_RECUR_FOR_RECURSIVE\nand have it automatically use git-merge-recur when the user and\nscripts ask for recursive.  A preliminary patch (but it was\ntested) is attached.\n\n> I tries to run sparse of the C files, and it complains about two things\n> - variable declarations in the middle of the code (allowed by c99 but\n> not by ANSI C) and incomplete function declarations (no \"void\").  It's\n> not C++, let's stick to the C standards.\n\nI run, from time to time, with pedantic set of options (that is,\n\n  -O2 -Werror -ansi -pedantic -std=c99 -D_XOPEN_SOURCE=500 -D_BSD_SOURCE\n\n) just for fun, and noticed these problems as well, but left\nthem as they are, because I think Johannaes/Alex team would want\nto update the TODO items first and I felt fixing these up at\nthis point would give me unnecessary conflicts to resolve.\n\n> I'm attaching a patch that fixes everything except variable\n> declarations.  There are so many of them that it could be done once the\n> code stabilizes a bit.\n\nExactly.\n\n-- >8 --\nrecur vs recursive: help testing without touching too many stuff.\n\nDuring git-merge-recur development, you could set an environment\nvariable GIT_USE_RECUR_FOR_RECURSIVE to use WIP recur in place\nof the recursive strategy.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n\n---\ndiff --git a/TEST b/TEST\nindex d530983..7286e2a 100755\n--- a/TEST\n+++ b/TEST\n@@ -1,10 +1,14 @@\n #!/bin/sh -x\n+\n cd t || exit\n-./t3400-rebase.sh              \"$@\" && \\\n-./t6020-merge-df.sh            \"$@\" && \\\n-./t3401-rebase-partial.sh      \"$@\" && \\\n-./t6021-merge-criss-cross.sh   \"$@\" && \\\n-./t3402-rebase-merge.sh        \"$@\" && \\\n-./t6022-merge-rename.sh        \"$@\" && \\\n-./t6010-merge-base.sh          \"$@\" && \\\n+GIT_USE_RECUR_FOR_RECURSIVE=LetsTryIt\n+export GIT_USE_RECUR_FOR_RECURSIVE\n+\n+./t3400-rebase.sh              \"$@\" &&\n+./t6020-merge-df.sh            \"$@\" &&\n+./t3401-rebase-partial.sh      \"$@\" &&\n+./t6021-merge-criss-cross.sh   \"$@\" &&\n+./t3402-rebase-merge.sh        \"$@\" &&\n+./t6022-merge-rename.sh        \"$@\" &&\n+./t6010-merge-base.sh          \"$@\" &&\n :\ndiff --git a/git-merge.sh b/git-merge.sh\nindex b26ca14..9b68115 100755\n--- a/git-merge.sh\n+++ b/git-merge.sh\n@@ -9,8 +9,13 @@ USAGE='[-n] [--no-commit] [--squash] [-s\n LF='\n '\n \n-all_strategies='recur recur octopus resolve stupid ours'\n-default_twohead_strategies='recur'\n+all_strategies='recursive recur octopus resolve stupid ours'\n+case \"${GIT_USE_RECUR_FOR_RECURSIVE}\" in\n+'')\n+\tdefault_twohead_strategies=recursive ;;\n+?*)\n+\tdefault_twohead_strategies=recur ;;\n+esac\n default_octopus_strategies='octopus'\n no_trivial_merge_strategies='ours'\n use_strategies=\n@@ -110,6 +115,10 @@ do\n \t\t\tstrategy=\"$2\"\n \t\t\tshift ;;\n \t\tesac\n+\t\tcase \"$strategy,${GIT_USE_RECUR_FOR_RECURSIVE}\" in\n+\t\trecursive,?*)\n+\t\t\tstrategy=recur ;;\n+\t\tesac\n \t\tcase \" $all_strategies \" in\n \t\t*\" $strategy \"*)\n \t\t\tuse_strategies=\"$use_strategies$strategy \" ;;\ndiff --git a/git-rebase.sh b/git-rebase.sh\nindex 2a4c8c8..8c5da72 100755\n--- a/git-rebase.sh\n+++ b/git-rebase.sh\n@@ -35,7 +35,13 @@ If you would prefer to skip this patch, \n To restore the original branch and stop rebasing run \\\"git rebase --abort\\\".\n \"\n unset newbase\n-strategy=recur\n+case \"${GIT_USE_RECUR_FOR_RECURSIVE}\" in\n+'')\n+\tstrategy=recursive ;;\n+?*)\n+\tstrategy=recur ;;\n+esac\n+\n do_merge=\n dotest=$GIT_DIR/.dotest-merge\n prec=4\n@@ -198,6 +204,11 @@ do\n \tshift\n done\n \n+case \"$strategy,${GIT_USE_RECUR_FOR_RECURSIVE}\" in\n+recursive,?*)\n+\tstrategy=recur ;;\n+esac\n+\n # Make sure we do not have .dotest\n if test -z \"$do_merge\"\n then\n@@ -292,7 +303,7 @@ then\n \texit $?\n fi\n \n-if test \"@@NO_PYTHON@@\" && test \"$strategy\" = \"recur\"\n+if test \"@@NO_PYTHON@@\" && test \"$strategy\" = \"recursive\"\n then\n \tdie 'The recursive merge strategy currently relies on Python,\n which this installation of git was not configured with.  Please consider\ndiff --git a/t/t3402-rebase-merge.sh b/t/t3402-rebase-merge.sh\nindex b70e177..d34c6cf 100755\n--- a/t/t3402-rebase-merge.sh\n+++ b/t/t3402-rebase-merge.sh\n@@ -51,7 +51,7 @@ test_expect_success setup '\n '\n \n test_expect_success 'reference merge' '\n-\tgit merge -s recur \"reference merge\" HEAD master\n+\tgit merge -s recursive \"reference merge\" HEAD master\n '\n \n test_expect_success rebase '\n"},{"id":"23482","messageId":"Pine.LNX.4.63.0607091634010.29667@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"4806","inReplyTo":"1152421230.2103.31.camel@dv","subject":"Re: [WIP] Status update on merge-recursive in C","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-07-09T14:41:28Z","receivedAt":"2006-07-09T14:41:28Z","isPatch":false,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sun, 9 Jul 2006, Pavel Roskin wrote:\n\n> Hello, Dscho!\n> \n> On Sat, 2006-07-08 at 18:42 +0200, Johannes Schindelin wrote:\n> > This is just an update for people being interested. Alex and me were\n> > busy with that project for a few days now. While it has progressed nicely,\n> > there are quite a couple TODOs in merge-recursive.c, just search for \"TODO\".\n> \n> That's great.  Just a few notes regarding the patch.\n> \n> Why do you list \"recur\" twice in all_strategies in the git-merge.sh?\n> The second \"recur\" should probably be \"recursive\".\n\nThis is just make really, really sure that recursive is not tested, but \nrecur. I would be laughed at if I sent a patch for recur and tested in on \nrecursive, claiming the patch was fine, right?\n\nBesides, when recur is fixed (for the TODOs as well as for obscure test \ncases), there will be no change to git-merge.sh and git-rebase.sh, because \nrecur will be the new recursive.\n\n> Test for Python in the git-rebase.sh is probably wrong.  \"recur\" doesn't\n> need Python - that's the whole point.  In fact, if I build git with\n> NO_PYTHON, TEST fails in t3401-rebase-partial.sh\n\nAgain, I am not concerned about simple bugs as this right now. I want to \nfix the big things first. Biggest thing right now: reimplement the \nwhee^H^H^H^Hread-tree with -i, -m, and no option.\n\n> - variable declarations in the middle of the code (allowed by c99 but\n> not by ANSI C) and incomplete function declarations (no \"void\").  It's\n> not C++, let's stick to the C standards.\n\n>From my experience with Java, I know that this declaration in the middle \nof the code can make debugging much easier. That is why they are still \nthere. Like Junio said, once the thing is close to finished, we can fix \nthese.\n\n> I'm attaching a patch that fixes everything except variable\n> declarations.\n\nMay I ask you a favour? Can you redo the patch in, say, two weeks? Thank \nyou!\n\n> It would be great to have a patch that actually removes the old\n> \"recursive\" and makes the new strategy \"recursive\".  I understand it's\n> the plan, right?\n\nYes, sir.\n\nCiao,\nDscho\n"},{"id":"24184","messageId":"7vu053o2g7.fsf@assigned-by-dhcp.cox.net","threadId":"4806","inReplyTo":"Pine.LNX.4.63.0607091634010.29667@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [WIP] Status update on merge-recursive in C","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-07-27T08:51:36Z","receivedAt":"2006-07-27T08:51:36Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> On Sun, 9 Jul 2006, Pavel Roskin wrote:\n>\n>> - variable declarations in the middle of the code (allowed by c99 but\n>> not by ANSI C) and incomplete function declarations (no \"void\").  It's\n>> not C++, let's stick to the C standards.\n>...\n>> I'm attaching a patch that fixes everything except variable\n>> declarations.\n>\n> May I ask you a favour? Can you redo the patch in, say, two weeks? Thank \n> you!\n\nNow it's about two weeks so maybe we can apply this on top to\nclean up and prod you to continue ;-)?\n\n-- >8 --\n[PATCH] merge-recursive in C: pedantic.\n\nThis just cleans up mixed declaration and code warnings and\nincomplete function declarations, as Pavel earlier pointed out.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n merge-recursive.c |  147 ++++++++++++++++++++++++++++++-----------------------\n 1 files changed, 83 insertions(+), 64 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 8d30519..3735df1 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -25,9 +25,9 @@ #include \"path-list.h\"\n /*#define DEBUG*/\n \n #ifdef DEBUG\n-#define debug(args, ...) fprintf(stderr, args, ## __VA_ARGS__)\n+#define debug(...) fprintf(stderr, __VA_ARGS__)\n #else\n-#define debug(args, ...)\n+#define debug(...) do { ; /* nothing */ } while (0)\n #endif\n \n #ifdef DEBUG\n@@ -61,13 +61,14 @@ static void show_ce_entry(const char *ta\n \tfputc('\\n', stderr);\n }\n \n-static void ls_files() {\n+static void ls_files(void) {\n \tint i;\n \tfor (i = 0; i < active_nr; i++) {\n \t\tstruct cache_entry *ce = active_cache[i];\n \t\tshow_ce_entry(\"\", ce);\n \t}\n \tfprintf(stderr, \"---\\n\");\n+\tif (0) ls_files(); /* avoid \"unused\" warning */\n }\n #endif\n \n@@ -89,8 +90,8 @@ static const char *commit_title(struct c\n \t\t\ts = \"(bad commit)\";\n \t\t\t*len = strlen(s);\n \t\t} else {\n-\t\t\ts = commit->buffer;\n \t\t\tchar prev = '\\0';\n+\t\t\ts = commit->buffer;\n \t\t\twhile ( *s ) {\n \t\t\t\tif ( '\\n' == prev && '\\n' == *s ) {\n \t\t\t\t\t++s;\n@@ -207,7 +208,7 @@ static const char *original_index_file;\n static const char *temporary_index_file;\n static int cache_dirty = 0;\n \n-static int flush_cache()\n+static int flush_cache(void)\n {\n \t/* flush temporary index */\n \tstruct lock_file *lock = xcalloc(1, sizeof(struct lock_file));\n@@ -290,11 +291,12 @@ #if 0\n \t\tgetenv(\"GIT_INDEX_FILE\"),\n \t\tsha1_to_hex(tree->object.sha1));\n #endif\n+\tint rc;\n \tconst char *argv[] = { \"git-read-tree\", NULL, NULL, };\n \tif (cache_dirty)\n \t\tdie(\"read-tree with dirty cache\");\n \targv[1] = sha1_to_hex(tree->object.sha1);\n-\tint rc = run_command_v(2, argv);\n+\trc = run_command_v(2, argv);\n \treturn rc < 0 ? -1: rc;\n }\n \n@@ -314,6 +316,7 @@ #if 0\n \t\tsha1_to_hex(head->object.sha1),\n \t\tsha1_to_hex(merge->object.sha1));\n #endif\n+\tint rc;\n \tconst char *argv[] = {\n \t\t\"git-read-tree\", NULL, \"-m\", NULL, NULL, NULL,\n \t\tNULL,\n@@ -324,32 +327,34 @@ #endif\n \targv[3] = sha1_to_hex(common->object.sha1);\n \targv[4] = sha1_to_hex(head->object.sha1);\n \targv[5] = sha1_to_hex(merge->object.sha1);\n-\tint rc = run_command_v(6, argv);\n+\trc = run_command_v(6, argv);\n \treturn rc < 0 ? -1: rc;\n }\n \n /*\n  * TODO: this can be streamlined by refactoring builtin-write-tree.c\n  */\n-static struct tree *git_write_tree()\n+static struct tree *git_write_tree(void)\n {\n #if 0\n \tfprintf(stderr, \"GIT_INDEX_FILE='%s' git-write-tree\\n\",\n \t\tgetenv(\"GIT_INDEX_FILE\"));\n #endif\n-\tif (cache_dirty)\n-\t\tflush_cache();\n-\tFILE *fp = popen(\"git-write-tree 2>/dev/null\", \"r\");\n+\tFILE *fp;\n+\tint rc;\n \tchar buf[41];\n \tunsigned char sha1[20];\n \tint ch;\n \tunsigned i = 0;\n+\tif (cache_dirty)\n+\t\tflush_cache();\n+\tfp = popen(\"git-write-tree 2>/dev/null\", \"r\");\n \twhile ( (ch = fgetc(fp)) != EOF )\n \t\tif ( i < sizeof(buf)-1 && ch >= '0' && ch <= 'f' )\n \t\t\tbuf[i++] = ch;\n \t\telse\n \t\t\tbreak;\n-\tint rc = pclose(fp);\n+\trc = pclose(fp);\n \tif ( rc == -1 || WEXITSTATUS(rc) )\n \t\treturn NULL;\n \tbuf[i] = '\\0';\n@@ -467,7 +472,7 @@ static void free_index_entries(struct pa\n  * Create a dictionary mapping file names to CacheEntry objects. The\n  * dictionary contains one entry for every path with a non-zero stage entry.\n  */\n-static struct path_list *get_unmerged()\n+static struct path_list *get_unmerged(void)\n {\n \tstruct path_list *unmerged = xcalloc(1, sizeof(struct path_list));\n \tint i;\n@@ -479,10 +484,10 @@ static struct path_list *get_unmerged()\n \t}\n \tfor (i = 0; i < active_nr; i++) {\n \t\tstruct cache_entry *ce = active_cache[i];\n+\t\tstruct stage_data *e;\n \t\tif (!ce_stage(ce))\n \t\t\tcontinue;\n-\n-\t\tstruct stage_data *e = index_entry_get(unmerged, ce->name);\n+\t\te = index_entry_get(unmerged, ce->name);\n \t\te->stages[ce_stage(ce)].mode = ntohl(ce->ce_mode);\n \t\tmemcpy(e->stages[ce_stage(ce)].sha, ce->sha1, 20);\n \t}\n@@ -530,13 +535,16 @@ static struct path_list *get_renames(str\n \t\t\t\t\tstruct tree *bTree,\n \t\t\t\t\tstruct path_list *entries)\n {\n+\tint i;\n+\tstruct path_list *renames;\n+\tstruct diff_options opts;\n #ifdef DEBUG\n \ttime_t t = time(0);\n+\n \tdebug(\"getRenames ...\\n\");\n #endif\n-\tint i;\n-\tstruct path_list *renames = xcalloc(1, sizeof(struct path_list));\n-\tstruct diff_options opts;\n+\n+\trenames = xcalloc(1, sizeof(struct path_list));\n \tdiff_setup(&opts);\n \topts.recursive = 1;\n \topts.detect_rename = DIFF_DETECT_RENAME;\n@@ -547,6 +555,7 @@ #endif\n \tdiffcore_std(&opts);\n \tfor (i = 0; i < diff_queued_diff.nr; ++i) {\n \t\tstruct rename *re;\n+\t\tstruct path_list_item *item;\n \t\tstruct diff_filepair *pair = diff_queued_diff.queue[i];\n \t\tif (pair->status != 'R') {\n \t\t\tdiff_free_filepair(pair);\n@@ -560,19 +569,17 @@ #endif\n \t\tif ( !re->src_entry ) {\n \t\t\tre->src_entry = index_entry_from_db(re->pair->one->path,\n \t\t\t\t\toTree, aTree, bTree);\n-\t\t\tstruct path_list_item *item =\n-\t\t\t\tpath_list_insert(re->pair->one->path, entries);\n+\t\t\titem = path_list_insert(re->pair->one->path, entries);\n \t\t\titem->util = re->src_entry;\n \t\t}\n \t\tre->dst_entry = index_entry_find(entries, re->pair->two->path);\n \t\tif ( !re->dst_entry ) {\n \t\t\tre->dst_entry = index_entry_from_db(re->pair->two->path,\n \t\t\t\t\toTree, aTree, bTree);\n-\t\t\tstruct path_list_item *item =\n-\t\t\t\tpath_list_insert(re->pair->two->path, entries);\n+\t\t\titem = path_list_insert(re->pair->two->path, entries);\n \t\t\titem->util = re->dst_entry;\n \t\t}\n-\t\tstruct path_list_item *item = path_list_insert(pair->one->path, renames);\n+\t\titem = path_list_insert(pair->one->path, renames);\n \t\titem->util = re;\n \t}\n \topts.output_format = DIFF_FORMAT_NO_OUTPUT;\n@@ -613,14 +620,14 @@ int update_stages(const char *path,\n  */\n static int remove_path(const char *name)\n {\n-\tint ret;\n-\tchar *slash;\n+\tint ret, len;\n+\tchar *slash, *dirs;\n \n \tret = unlink(name);\n \tif ( ret )\n \t\treturn ret;\n-\tint len = strlen(name);\n-\tchar *dirs = malloc(len+1);\n+\tlen = strlen(name);\n+\tdirs = malloc(len+1);\n \tmemcpy(dirs, name, len);\n \tdirs[len] = '\\0';\n \twhile ( (slash = strrchr(name, '/')) ) {\n@@ -664,15 +671,17 @@ int remove_file(int clean, const char *p\n static char *unique_path(const char *path, const char *branch)\n {\n \tchar *newpath = xmalloc(strlen(path) + 1 + strlen(branch) + 8 + 1);\n+\tchar *p;\n+\tint suffix;\n+\tstruct stat st;\n \tstrcpy(newpath, path);\n \tstrcat(newpath, \"~\");\n-\tchar *p = newpath + strlen(newpath);\n+\tp = newpath + strlen(newpath);\n \tstrcpy(p, branch);\n \tfor ( ; *p; ++p )\n \t\tif ( '/' == *p )\n \t\t\t*p = '_';\n-\tint suffix = 0;\n-\tstruct stat st;\n+\tsuffix = 0;\n \twhile ( path_list_has_path(&currentFileSet, newpath) ||\n \t\tpath_list_has_path(&currentDirectorySet, newpath) ||\n \t\tlstat(newpath, &st) == 0 ) {\n@@ -754,6 +763,7 @@ void update_file_flags(const unsigned ch\n \t\t\tdie(\"blob expected for %s '%s'\", sha1_to_hex(sha), path);\n \n \t\tif ( S_ISREG(mode) ) {\n+\t\t\tint fd;\n \t\t\tif ( mkdir_p(path, 0777, 0 /* don't create last element */) )\n \t\t\t\tdie(\"failed to create path %s: %s\", path, strerror(errno));\n \t\t\tunlink(path);\n@@ -761,7 +771,7 @@ void update_file_flags(const unsigned ch\n \t\t\t\tmode = 0777;\n \t\t\telse\n \t\t\t\tmode = 0666;\n-\t\t\tint fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);\n+\t\t\tfd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);\n \t\t\tif ( fd < 0 )\n \t\t\t\tdie(\"failed to open %s: %s\", path, strerror(errno));\n \t\t\tflush_buffer(fd, buf, size);\n@@ -863,17 +873,19 @@ static struct merge_file_info merge_file\n \t\t\tchar orig[PATH_MAX];\n \t\t\tchar src1[PATH_MAX];\n \t\t\tchar src2[PATH_MAX];\n-\n-\t\t\tgit_unpack_file(oSha, orig);\n-\t\t\tgit_unpack_file(aSha, src1);\n-\t\t\tgit_unpack_file(bSha, src2);\n-\n \t\t\tconst char *argv[] = {\n \t\t\t\t\"merge\", \"-L\", NULL, \"-L\", NULL, \"-L\", NULL,\n \t\t\t\tsrc1, orig, src2,\n \t\t\t\tNULL\n \t\t\t};\n \t\t\tchar *la, *lb, *lo;\n+\t\t\tstruct stat st;\n+\t\t\tint fd;\n+\n+\t\t\tgit_unpack_file(oSha, orig);\n+\t\t\tgit_unpack_file(aSha, src1);\n+\t\t\tgit_unpack_file(bSha, src2);\n+\n \t\t\targv[2] = la = strdup(mkpath(\"%s/%s\", branch1Name, aPath));\n \t\t\targv[6] = lb = strdup(mkpath(\"%s/%s\", branch2Name, bPath));\n \t\t\targv[4] = lo = strdup(mkpath(\"orig/%s\", oPath));\n@@ -892,8 +904,7 @@ #endif\n \t\t\t\tdie(\"Failed to execute 'merge'. merge(1) is used as the \"\n \t\t\t\t    \"file-level merge tool. Is 'merge' in your path?\");\n \t\t\t}\n-\t\t\tstruct stat st;\n-\t\t\tint fd = open(src1, O_RDONLY);\n+\t\t\tfd = open(src1, O_RDONLY);\n \t\t\tif (fd < 0 || fstat(fd, &st) < 0 ||\n \t\t\t\t\tindex_fd(result.sha, fd, &st, 1,\n \t\t\t\t\t\t\"blob\"))\n@@ -1018,6 +1029,7 @@ static int process_renames(struct path_l\n \t\tstruct path_list *renames1, *renames2, *renames2Dst;\n \t\tstruct rename *ren1, *ren2;\n \t\tconst char *branchName1, *branchName2;\n+\t\tconst char *ren1_src, *ren1_dst;\n \t\tren1 = find_rename_bysrc(renamesA, src);\n \t\tren2 = find_rename_bysrc(renamesB, src);\n \t\t/* TODO: refactor, so that 1/2 are not needed */\n@@ -1028,12 +1040,13 @@ static int process_renames(struct path_l\n \t\t\tbranchName1 = branchNameA;\n \t\t\tbranchName2 = branchNameB;\n \t\t} else {\n+\t\t\tstruct rename *tmp;\n \t\t\trenames1 = renamesB;\n \t\t\trenames2 = renamesA;\n \t\t\trenames2Dst = &byDstA;\n \t\t\tbranchName1 = branchNameB;\n \t\t\tbranchName2 = branchNameA;\n-\t\t\tstruct rename *tmp = ren2;\n+\t\t\ttmp = ren2;\n \t\t\tren2 = ren1;\n \t\t\tren1 = tmp;\n \t\t}\n@@ -1045,8 +1058,8 @@ static int process_renames(struct path_l\n \t\t\tcontinue;\n \t\tren1->processed = 1;\n \n-\t\tconst char *ren1_src = ren1->pair->one->path;\n-\t\tconst char *ren1_dst = ren1->pair->two->path;\n+\t\tren1_src = ren1->pair->one->path;\n+\t\tren1_dst = ren1->pair->two->path;\n \n \t\tif ( ren2 ) {\n \t\t\tconst char *ren2_src = ren2->pair->one->path;\n@@ -1065,8 +1078,8 @@ static int process_renames(struct path_l\n \t\t\t\t       src, ren2_dst, branchName2);\n \t\t\t\tconflict_rename_rename(ren1, branchName1, ren2, branchName2);\n \t\t\t} else {\n-\t\t\t\tremove_file(1, ren1_src);\n \t\t\t\tstruct merge_file_info mfi;\n+\t\t\t\tremove_file(1, ren1_src);\n \t\t\t\tmfi = merge_file(ren1_src,\n \t\t\t\t\t\t ren1->pair->one->sha1,\n \t\t\t\t\t\t ren1->pair->one->mode,\n@@ -1102,13 +1115,17 @@ static int process_renames(struct path_l\n \t\t\t\tupdate_file(mfi.clean, mfi.sha, mfi.mode, ren1_dst);\n \t\t\t}\n \t\t} else {\n-\t\t\t/* Renamed in 1, maybe changed in 2 */\n-\t\t\tremove_file(1, ren1_src);\n-\n \t\t\tunsigned char srcShaOtherBranch[20], dstShaOtherBranch[20];\n \t\t\tunsigned srcModeOtherBranch, dstModeOtherBranch;\n \n-\t\t\tint stage = renamesA == renames1 ? 3: 2;\n+\t\t\tint stage;\n+\t\t\tint tryMerge = 0;\n+\t\t\tchar *newPath;\n+\n+\t\t\t/* Renamed in 1, maybe changed in 2 */\n+\t\t\tremove_file(1, ren1_src);\n+\n+\t\t\tstage = renamesA == renames1 ? 3: 2;\n \n \t\t\tmemcpy(srcShaOtherBranch, ren1->src_entry->stages[stage].sha, 20);\n \t\t\tsrcModeOtherBranch = ren1->src_entry->stages[stage].mode;\n@@ -1116,8 +1133,7 @@ static int process_renames(struct path_l\n \t\t\tmemcpy(dstShaOtherBranch, ren1->dst_entry->stages[stage].sha, 20);\n \t\t\tdstModeOtherBranch = ren1->dst_entry->stages[stage].mode;\n \n-\t\t\tint tryMerge = 0;\n-\t\t\tchar *newPath;\n+\t\t\ttryMerge = 0;\n \n \t\t\tif (path_list_has_path(&currentDirectorySet, ren1_dst)) {\n \t\t\t\tcleanMerge = 0;\n@@ -1162,18 +1178,19 @@ static int process_renames(struct path_l\n \t\t\t\tunsigned omode = ren1->pair->one->mode;\n \t\t\t\tunsigned amode = ren1->pair->two->mode;\n \t\t\t\tunsigned bmode = srcModeOtherBranch;\n+\t\t\t\tconst char *aBranch = branchName1;\n+\t\t\t\tconst char *bBranch = branchName2;\n+\t\t\t\tstruct merge_file_info mfi;\n+\n \t\t\t\tmemcpy(osha, ren1->pair->one->sha1, 20);\n \t\t\t\tmemcpy(asha, ren1->pair->two->sha1, 20);\n \t\t\t\tmemcpy(bsha, srcShaOtherBranch, 20);\n-\t\t\t\tconst char *aBranch = branchName1;\n-\t\t\t\tconst char *bBranch = branchName2;\n \n \t\t\t\tif ( renamesA != renames1 ) {\n \t\t\t\t\tmemswp(&aname, &bname, sizeof(aname));\n \t\t\t\t\tmemswp(asha, bsha, 20);\n \t\t\t\t\tmemswp(&aBranch, &bBranch, sizeof(aBranch));\n \t\t\t\t}\n-\t\t\t\tstruct merge_file_info mfi;\n \t\t\t\tmfi = merge_file(oname, osha, omode,\n \t\t\t\t\t\t aname, asha, amode,\n \t\t\t\t\t\t bname, bsha, bmode,\n@@ -1280,8 +1297,9 @@ static int process_entry(const char *pat\n \t\t\tconf = \"directory/file\";\n \t\t}\n \t\tif ( path_list_has_path(&currentDirectorySet, path) ) {\n+\t\t\tconst char *newPath;\n \t\t\tcleanMerge = 0;\n-\t\t\tconst char *newPath = unique_path(path, addBranch);\n+\t\t\tnewPath = unique_path(path, addBranch);\n \t\t\toutput(\"CONFLICT (%s): There is a directory with name %s in %s. \"\n \t\t\t       \"Adding %s as %s\",\n \t\t\t       conf, path, otherBranch, path, newPath);\n@@ -1306,9 +1324,10 @@ static int process_entry(const char *pat\n \t\t\t\tassert(0 && \"This case must be handled by git-read-tree\");\n \t\t\t}\n \t\t} else {\n+\t\t\tconst char *newPath1, *newPath2;\n \t\t\tcleanMerge = 0;\n-\t\t\tconst char *newPath1 = unique_path(path, branch1Name);\n-\t\t\tconst char *newPath2 = unique_path(path, branch2Name);\n+\t\t\tnewPath1 = unique_path(path, branch1Name);\n+\t\t\tnewPath2 = unique_path(path, branch2Name);\n \t\t\toutput(\"CONFLICT (add/add): File %s added non-identically \"\n \t\t\t       \"in both branches. Adding as %s and %s instead.\",\n \t\t\t       path, newPath1, newPath2);\n@@ -1319,8 +1338,8 @@ static int process_entry(const char *pat\n \n \t} else if ( oSha && aSha && bSha ) {\n \t\t/* case D: Modified in both, but differently. */\n-\t\toutput(\"Auto-merging %s\", path);\n \t\tstruct merge_file_info mfi;\n+\t\toutput(\"Auto-merging %s\", path);\n \t\tmfi = merge_file(path, oSha, oMode,\n \t\t\t\t path, aSha, aMode,\n \t\t\t\t path, bSha, bMode,\n@@ -1373,19 +1392,19 @@ static struct merge_tree_result merge_tr\n \tresult.tree = git_write_tree();\n \n \tif ( !result.tree ) {\n+\t\tstruct path_list *entries, *re_head, *re_merge;\n+\t\tint i;\n \t\tpath_list_clear(&currentFileSet, 1);\n \t\tpath_list_clear(&currentDirectorySet, 1);\n \t\tget_files_dirs(head, &currentFileSet, &currentDirectorySet);\n \t\tget_files_dirs(merge, &currentFileSet, &currentDirectorySet);\n \n-\t\tstruct path_list *entries = get_unmerged();\n-\t\tstruct path_list *re_head, *re_merge;\n+\t\tentries = get_unmerged();\n \t\tre_head  = get_renames(head, common, head, merge, entries);\n \t\tre_merge = get_renames(merge, common, head, merge, entries);\n \t\tresult.clean = process_renames(re_head, re_merge,\n \t\t\t\t\t       branch1Name, branch2Name);\n \t\tdebug(\"\\tprocessing entries...\\n\");\n-\t\tint i;\n \t\tfor (i = 0; i < entries->nr; i++) {\n \t\t\tconst char *path = entries->items[i].path;\n \t\t\tstruct stage_data *e = entries->items[i].util;\n@@ -1512,6 +1531,10 @@ int main(int argc, char *argv[])\n {\n \tstatic const char *bases[2];\n \tstatic unsigned bases_count = 0;\n+\tint i;\n+\tconst char *branch1, *branch2;\n+\tstruct merge_result result;\n+\tstruct commit *h1, *h2;\n \n \toriginal_index_file = getenv(\"GIT_INDEX_FILE\");\n \n@@ -1523,7 +1546,6 @@ int main(int argc, char *argv[])\n \tif (argc < 4)\n \t\tdie(\"Usage: %s <base>... -- <head> <remote> ...\\n\", argv[0]);\n \n-\tint i;\n \tfor (i = 1; i < argc; ++i) {\n \t\tif (!strcmp(argv[i], \"--\"))\n \t\t\tbreak;\n@@ -1533,15 +1555,12 @@ int main(int argc, char *argv[])\n \tif (argc - i != 3) /* \"--\" \"<head>\" \"<remote>\" */\n \t\tdie(\"Not handling anything other than two heads merge.\");\n \n-\tconst char *branch1, *branch2;\n-\n \tbranch1 = argv[++i];\n \tbranch2 = argv[++i];\n \tprintf(\"Merging %s with %s\\n\", branch1, branch2);\n \n-\tstruct merge_result result;\n-\tstruct commit *h1 = get_ref(branch1);\n-\tstruct commit *h2 = get_ref(branch2);\n+\th1 = get_ref(branch1);\n+\th2 = get_ref(branch2);\n \n \tif (bases_count == 1) {\n \t\tstruct commit *ancestor = get_ref(bases[0]);\n-- \n1.4.2.rc2.g544b\n"},{"id":"24190","messageId":"Pine.LNX.4.63.0607271127520.29667@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"4806","inReplyTo":"7vu053o2g7.fsf@assigned-by-dhcp.cox.net","subject":"Re: [WIP] Status update on merge-recursive in C","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-07-27T09:41:59Z","receivedAt":"2006-07-27T09:41:59Z","isPatch":false,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Thu, 27 Jul 2006, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > On Sun, 9 Jul 2006, Pavel Roskin wrote:\n> >\n> >> - variable declarations in the middle of the code (allowed by c99 but\n> >> not by ANSI C) and incomplete function declarations (no \"void\").  It's\n> >> not C++, let's stick to the C standards.\n> >...\n> >> I'm attaching a patch that fixes everything except variable\n> >> declarations.\n> >\n> > May I ask you a favour? Can you redo the patch in, say, two weeks? Thank \n> > you!\n> \n> Now it's about two weeks so maybe we can apply this on top to\n> clean up and prod you to continue ;-)?\n\n*grin*\n\nActually, we continued a little with it, but I mainly waited what would \nhappen to \"git-read-tree --rename\"...\n\nI will send an updated patch soon. The remaining TODOs after that are:\n\n$ grep TODO merge-recursive.c \n * TODO: we should not have to copy the SHA1s around, but rather reference\n * TODO: check if we can just reuse the active_cache structure: it is already\n * TODO: this can be streamlined by refactoring builtin-read-tree.c\n * TODO: this can be streamlined by refactoring builtin-read-tree.c\n * TODO: this can be streamlined by refactoring builtin-write-tree.c\n/* General TODO: no javaIfiCation: rename updateCache to update_cache */\n * TODO: once we no longer call external programs, we'd probably be better off\n/* General TODO: get rid of all the debug messages */\n                /* TODO: refactor, so that 1/2 are not needed */\n\nSo, the biggest problem I see: refactoring builtin-read-tree. I really \ncould use some help there -- I already tried 3 times to come up with a \nnice abstraction for unpack_trees() which could be used as a library \nfunction.\n\nCiao,\nDscho\n"}]}