{"thread":{"id":"43494","subject":"[PATCH 3/3] Make git-rerere a builtin","startedAt":"2006-12-20T16:39:41Z","lastAt":"2006-12-20T16:39:41Z","messageCount":1,"participants":["Johannes Schindelin"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"295121","messageId":"Pine.LNX.4.63.0612201738000.19693@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"43494","inReplyTo":null,"subject":"[PATCH 3/3] Make git-rerere a builtin","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-12-20T16:39:41Z","receivedAt":"2006-12-20T16:39:41Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nThe perl version used modules which are non-standard in some setups.\nThis patch brings the full power of rerere to a wider audience.\n\nSigned-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n---\n\n\tIf this is not liked, at least we have a test now. And I can use \n\tit on all my machines.\n\n Makefile         |    2 +-\n builtin-rerere.c |  407 ++++++++++++++++++++++++++++++++++++++++++++++++++++++\n builtin.h        |    1 +\n git-rerere.perl  |  284 -------------------------------------\n git.c            |    1 +\n 5 files changed, 410 insertions(+), 285 deletions(-)\n\ndiff --git a/Makefile b/Makefile\nindex 2c19c4d..6ce3ada 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -179,7 +179,6 @@ SCRIPT_SH = \\\n SCRIPT_PERL = \\\n \tgit-add--interactive.perl \\\n \tgit-archimport.perl git-cvsimport.perl git-relink.perl \\\n-\tgit-rerere.perl \\\n \tgit-cvsserver.perl \\\n \tgit-svnimport.perl git-cvsexportcommit.perl \\\n \tgit-send-email.perl git-svn.perl\n@@ -294,6 +293,7 @@ BUILTIN_OBJS = \\\n \tbuiltin-push.o \\\n \tbuiltin-read-tree.o \\\n \tbuiltin-repo-config.o \\\n+\tbuiltin-rerere.o \\\n \tbuiltin-rev-list.o \\\n \tbuiltin-rev-parse.o \\\n \tbuiltin-rm.o \\\ndiff --git a/builtin-rerere.c b/builtin-rerere.c\nnew file mode 100644\nindex 0000000..191aeec\n--- /dev/null\n+++ b/builtin-rerere.c\n@@ -0,0 +1,407 @@\n+#include \"cache.h\"\n+#include \"path-list.h\"\n+#include \"xdiff/xdiff.h\"\n+#include \"xdiff-interface.h\"\n+\n+#include <time.h>\n+\n+static const char git_rerere_usage[] =\n+\"git-rerere [clear | status | diff | gc]\";\n+\n+/* these values are days */\n+static int cutoff_noresolve = 15;\n+static int cutoff_resolve = 60;\n+\n+static char *merge_rr_path;\n+\n+static const char *rr_path(const char *name, const char *file)\n+{\n+\treturn git_path(\"rr-cache/%s/%s\", name, file);\n+}\n+\n+static void read_rr(struct path_list *rr)\n+{\n+\tunsigned char sha1[20];\n+\tchar buf[PATH_MAX];\n+\tFILE *in = fopen(merge_rr_path, \"r\");\n+\tif (!in)\n+\t\treturn;\n+\twhile (fread(buf, 40, 1, in) == 1) {\n+\t\tint i;\n+\t\tchar *name;\n+\t\tif (get_sha1_hex(buf, sha1))\n+\t\t\tdie(\"corrupt MERGE_RR\");\n+\t\tbuf[40] = '\\0';\n+\t\tname = xstrdup(buf);\n+\t\tif (fgetc(in) != '\\t')\n+\t\t\tdie(\"corrupt MERGE_RR\");\n+\t\tfor (i = 0; i < sizeof(buf) && (buf[i] = fgetc(in)); i++)\n+\t\t\t; /* do nothing */\n+\t\tif (i == sizeof(buf))\n+\t\t\tdie(\"filename too long\");\n+\t\tpath_list_insert(buf, rr)->util = xstrdup(name);\n+\t}\n+\tfclose(in);\n+}\n+\n+static struct lock_file write_lock;\n+\n+static int write_rr(struct path_list *rr, int out_fd)\n+{\n+\tint i;\n+\tfor (i = 0; i < rr->nr; i++) {\n+\t\tconst char *path = rr->items[i].path;\n+\t\twrite(out_fd, rr->items[i].util, 40);\n+\t\twrite(out_fd, \"\\t\", 1);\n+\t\twrite(out_fd, path, strlen(path) + 1);\n+\t}\n+\tclose(out_fd);\n+\treturn commit_lock_file(&write_lock);\n+}\n+\n+struct buffer {\n+\tchar *ptr;\n+\tint nr, alloc;\n+};\n+\n+static void append_line(struct buffer *buffer, const char *line)\n+{\n+\tint len = strlen(line);\n+\n+\tif (buffer->nr + len > buffer->alloc) {\n+\t\tbuffer->alloc = alloc_nr(buffer->nr + len);\n+\t\tbuffer->ptr = xrealloc(buffer->ptr, buffer->alloc);\n+\t}\n+\tmemcpy(buffer->ptr + buffer->nr, line, len);\n+\tbuffer->nr += len;\n+}\n+\n+static int handle_file(const char *path,\n+\t unsigned char *sha1, const char *output)\n+{\n+\tSHA_CTX ctx;\n+\tchar buf[1024];\n+\tint hunk = 0, hunk_no = 0;\n+\tstruct buffer minus = { NULL, 0, 0 }, plus = { NULL, 0, 0 };\n+\tstruct buffer *one = &minus, *two = &plus;\n+\tFILE *f = fopen(path, \"r\");\n+\tFILE *out;\n+\n+\tif (!f)\n+\t\treturn error(\"Could not open %s\", path);\n+\n+\tif (output) {\n+\t\tout = fopen(output, \"w\");\n+\t\tif (!out) {\n+\t\t\tfclose(f);\n+\t\t\treturn error(\"Could not write %s\", output);\n+\t\t}\n+\t} else\n+\t\tout = NULL;\n+\n+\tif (sha1)\n+\t\tSHA1_Init(&ctx);\n+\n+\twhile (fgets(buf, sizeof(buf), f)) {\n+\t\tif (!strncmp(\"<<<<<<< \", buf, 8))\n+\t\t\thunk = 1;\n+\t\telse if (!strncmp(\"=======\", buf, 7))\n+\t\t\thunk = 2;\n+\t\telse if (!strncmp(\">>>>>>> \", buf, 8)) {\n+\t\t\thunk_no++;\n+\t\t\thunk = 0;\n+\t\t\tif (memcmp(one->ptr, two->ptr, one->nr < two->nr ?\n+\t\t\t\t\t\tone->nr : two->nr) > 0) {\n+\t\t\t\tstruct buffer *swap = one;\n+\t\t\t\tone = two;\n+\t\t\t\ttwo = swap;\n+\t\t\t}\n+\t\t\tif (out) {\n+\t\t\t\tfputs(\"<<<<<<<\\n\", out);\n+\t\t\t\tfwrite(one->ptr, one->nr, 1, out);\n+\t\t\t\tfputs(\"=======\\n\", out);\n+\t\t\t\tfwrite(two->ptr, two->nr, 1, out);\n+\t\t\t\tfputs(\">>>>>>>\\n\", out);\n+\t\t\t}\n+\t\t\tif (sha1) {\n+\t\t\t\tSHA1_Update(&ctx, one->ptr, one->nr);\n+\t\t\t\tSHA1_Update(&ctx, \"\\0\", 1);\n+\t\t\t\tSHA1_Update(&ctx, two->ptr, two->nr);\n+\t\t\t\tSHA1_Update(&ctx, \"\\0\", 1);\n+\t\t\t}\n+\t\t} else if (hunk == 1)\n+\t\t\tappend_line(one, buf);\n+\t\telse if (hunk == 2)\n+\t\t\tappend_line(two, buf);\n+\t\telse if (out)\n+\t\t\tfputs(buf, out);\n+\t}\n+\n+\tfclose(f);\n+\tif (out)\n+\t\tfclose(out);\n+\tif (sha1)\n+\t\tSHA1_Final(sha1, &ctx);\n+\treturn hunk_no;\n+}\n+\n+static int find_conflict(struct path_list *conflict)\n+{\n+\tint i;\n+\tif (read_cache() < 0)\n+\t\treturn error(\"Could not read index\");\n+\tfor (i = 0; i + 2 < active_nr; i++) {\n+\t\tstruct cache_entry *e1 = active_cache[i];\n+\t\tstruct cache_entry *e2 = active_cache[i + 1];\n+\t\tstruct cache_entry *e3 = active_cache[i + 2];\n+\t\tif (ce_stage(e1) == 1 && ce_stage(e2) == 2 &&\n+\t\t\t\tce_stage(e3) == 3 && ce_same_name(e1, e2) &&\n+\t\t\t\tce_same_name(e1, e3)) {\n+\t\t\tpath_list_insert((const char *)e1->name, conflict);\n+\t\t\ti += 3;\n+\t\t}\n+\t}\n+\treturn 0;\n+}\n+\n+static int merge(const char *name, const char *path)\n+{\n+\tint ret;\n+\tmmfile_t cur, base, other;\n+\tmmbuffer_t result = {NULL, 0};\n+\txpparam_t xpp = {XDF_NEED_MINIMAL};\n+\n+\tif (handle_file(path, NULL, rr_path(name, \"thisimage\")) < 0)\n+\t\treturn 1;\n+\n+\tif (read_mmfile(&cur, rr_path(name, \"thisimage\")) ||\n+\t\t\tread_mmfile(&base, rr_path(name, \"preimage\")) ||\n+\t\t\tread_mmfile(&other, rr_path(name, \"postimage\")))\n+\t\treturn 1;\n+\tret = xdl_merge(&base, &cur, \"\", &other, \"\",\n+\t\t\t&xpp, XDL_MERGE_ZEALOUS, &result);\n+\tif (!ret) {\n+\t\tFILE *f = fopen(path, \"w\");\n+\t\tif (!f)\n+\t\t\treturn error(\"Could not write to %s\", path);\n+\t\tfwrite(result.ptr, result.size, 1, f);\n+\t\tfclose(f);\n+\t}\n+\n+\tfree(cur.ptr);\n+\tfree(base.ptr);\n+\tfree(other.ptr);\n+\tfree(result.ptr);\n+\n+\treturn ret;\n+}\n+\n+static void unlink_rr_item(const char *name)\n+{\n+\tunlink(rr_path(name, \"thisimage\"));\n+\tunlink(rr_path(name, \"preimage\"));\n+\tunlink(rr_path(name, \"postimage\"));\n+\trmdir(git_path(\"rr-cache/%s\", name));\n+}\n+\n+static void garbage_collect(struct path_list *rr)\n+{\n+\tstruct path_list to_remove = { NULL, 0, 0, 1 };\n+\tchar buf[1024];\n+\tDIR *dir;\n+\tstruct dirent *e;\n+\tint len, i, cutoff;\n+\ttime_t now = time(NULL), then;\n+\n+\tstrlcpy(buf, git_path(\"rr-cache\"), sizeof(buf));\n+\tlen = strlen(buf);\n+\tdir = opendir(buf);\n+\tstrcpy(buf + len++, \"/\");\n+\twhile ((e = readdir(dir))) {\n+\t\tconst char *name = e->d_name;\n+\t\tstruct stat st;\n+\t\tif (name[0] == '.' && (name[1] == '\\0' ||\n+\t\t\t\t\t(name[1] == '.' && name[2] == '\\0')))\n+\t\t\tcontinue;\n+\t\ti = snprintf(buf + len, sizeof(buf) - len, \"%s\", name);\n+\t\tstrlcpy(buf + len + i, \"/preimage\", sizeof(buf) - len - i);\n+\t\tif (stat(buf, &st))\n+\t\t\tcontinue;\n+\t\tthen = st.st_mtime;\n+\t\tstrlcpy(buf + len + i, \"/postimage\", sizeof(buf) - len - i);\n+\t\tcutoff = stat(buf, &st) ? cutoff_noresolve : cutoff_resolve;\n+\t\tif (then < now - cutoff * 86400) {\n+\t\t\tbuf[len + i] = '\\0';\n+\t\t\tpath_list_insert(xstrdup(name), &to_remove);\n+\t\t}\n+\t}\n+\tfor (i = 0; i < to_remove.nr; i++)\n+\t\tunlink_rr_item(to_remove.items[i].path);\n+\tpath_list_clear(&to_remove, 0);\n+}\n+\n+static int outf(void *dummy, mmbuffer_t *ptr, int nbuf)\n+{\n+\tint i;\n+\tfor (i = 0; i < nbuf; i++)\n+\t\twrite(1, ptr[i].ptr, ptr[i].size);\n+\treturn 0;\n+}\n+\n+static int diff_two(const char *file1, const char *label1,\n+\t\tconst char *file2, const char *label2)\n+{\n+\txpparam_t xpp;\n+\txdemitconf_t xecfg;\n+\txdemitcb_t ecb;\n+\tmmfile_t minus, plus;\n+\n+\tif (read_mmfile(&minus, file1) || read_mmfile(&plus, file2))\n+\t\treturn 1;\n+\n+\tprintf(\"--- a/%s\\n+++ b/%s\\n\", label1, label2);\n+\tfflush(stdout);\n+\txpp.flags = XDF_NEED_MINIMAL;\n+\txecfg.ctxlen = 3;\n+\txecfg.flags = 0;\n+\tecb.outf = outf;\n+\txdl_diff(&minus, &plus, &xpp, &xecfg, &ecb);\n+\n+\tfree(minus.ptr);\n+\tfree(plus.ptr);\n+\treturn 0;\n+}\n+\n+static int copy_file(const char *src, const char *dest)\n+{\n+\tFILE *in, *out;\n+\tchar buffer[32768];\n+\tint count;\n+\n+\tif (!(in = fopen(src, \"r\")))\n+\t\treturn error(\"Could not open %s\", src);\n+\tif (!(out = fopen(dest, \"w\")))\n+\t\treturn error(\"Could not open %s\", dest);\n+\twhile ((count = fread(buffer, 1, sizeof(buffer), in)))\n+\t\tfwrite(buffer, 1, count, out);\n+\tfclose(in);\n+\tfclose(out);\n+\treturn 0;\n+\t\n+}\n+\n+static int do_plain_rerere(struct path_list *rr, int fd)\n+{\n+\tstruct path_list conflict = { NULL, 0, 0, 1 };\n+\tint i;\n+\n+\tfind_conflict(&conflict);\n+\n+\t/*\n+\t * MERGE_RR records paths with conflicts immediately after merge\n+\t * failed.  Some of the conflicted paths might have been hand resolved\n+\t * in the working tree since then, but the initial run would catch all\n+\t * and register their preimages.\n+\t */\n+\n+\tfor (i = 0; i < conflict.nr; i++) {\n+\t\tconst char *path = conflict.items[i].path;\n+\t\tif (!path_list_has_path(rr, path)) {\n+\t\t\tunsigned char sha1[20];\n+\t\t\tchar *hex;\n+\t\t\tint ret;\n+\t\t\tret = handle_file(path, sha1, NULL);\n+\t\t\tif (ret < 1)\n+\t\t\t\tcontinue;\n+\t\t\thex = xstrdup(sha1_to_hex(sha1));\n+\t\t\tpath_list_insert(path, rr)->util = hex;\n+\t\t\tif (mkdir(git_path(\"rr-cache/%s\", hex), 0755))\n+\t\t\t\tcontinue;;\n+\t\t\thandle_file(path, NULL, rr_path(hex, \"preimage\"));\n+\t\t\tfprintf(stderr, \"Recorded preimage for '%s'\\n\", path);\n+\t\t}\n+\t}\n+\n+\t/*\n+\t * Now some of the paths that had conflicts earlier might have been\n+\t * hand resolved.  Others may be similar to a conflict already that\n+\t * was resolved before.\n+\t */\n+\n+\tfor (i = 0; i < rr->nr; i++) {\n+\t\tstruct stat st;\n+\t\tint ret;\n+\t\tconst char *path = rr->items[i].path;\n+\t\tconst char *name = (const char *)rr->items[i].util;\n+\n+\t\tif (!stat(rr_path(name, \"preimage\"), &st) &&\n+\t\t\t\t!stat(rr_path(name, \"postimage\"), &st)) {\n+\t\t\tif (!merge(name, path)) {\n+\t\t\t\tfprintf(stderr, \"Resolved '%s' using \"\n+\t\t\t\t\t\t\"previous resolution.\\n\", path);\n+\t\t\t\tgoto tail_optimization;\n+\t\t\t}\n+\t\t}\n+\n+\t\t/* Let's see if we have resolved it. */\n+\t\tret = handle_file(path, NULL, NULL);\n+\t\tif (ret)\n+\t\t\tcontinue;\n+\n+\t\tfprintf(stderr, \"Recorded resolution for '%s'.\\n\", path);\n+\t\tcopy_file(path, rr_path(name, \"postimage\"));\n+tail_optimization:\n+\t\tif (i < rr->nr - 1) {\n+\t\t\tmemmove(rr->items + i,\n+\t\t\t\t\trr->items + i + 1,\n+\t\t\t\t\trr->nr - i - 1);\n+\t\t}\n+\t\trr->nr--;\n+\t\ti--;\n+\t}\n+\n+\treturn write_rr(rr, fd);\n+}\n+\n+int cmd_rerere(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct path_list merge_rr = { NULL, 0, 0, 1 };\n+\tint i, fd = -1;\n+\tstruct stat st;\n+\n+\tif (stat(git_path(\"rr-cache\"), &st) || !S_ISDIR(st.st_mode))\n+\t\treturn 0;\n+\n+\tmerge_rr_path = xstrdup(git_path(\"rr-cache/MERGE_RR\"));\n+\tfd = hold_lock_file_for_update(&write_lock, merge_rr_path, 1);\n+\tread_rr(&merge_rr);\n+\n+\tif (argc < 2)\n+\t\treturn do_plain_rerere(&merge_rr, fd);\n+\telse if (!strcmp(argv[1], \"clear\")) {\n+\t\tfor (i = 0; i < merge_rr.nr; i++) {\n+\t\t\tconst char *name = (const char *)merge_rr.items[i].util;\n+\t\t\tif (!stat(git_path(\"rr-cache/%s\", name), &st) &&\n+\t\t\t\t\tS_ISDIR(st.st_mode) &&\n+\t\t\t\t\tstat(rr_path(name, \"postimage\"), &st))\n+\t\t\t\tunlink_rr_item(name);\n+\t\t}\n+\t\tunlink(merge_rr_path);\n+\t} else if (!strcmp(argv[1], \"gc\"))\n+\t\tgarbage_collect(&merge_rr);\n+\telse if (!strcmp(argv[1], \"status\"))\n+\t\tfor (i = 0; i < merge_rr.nr; i++)\n+\t\t\tprintf(\"%s\\n\", merge_rr.items[i].path);\n+\telse if (!strcmp(argv[1], \"diff\"))\n+\t\tfor (i = 0; i < merge_rr.nr; i++) {\n+\t\t\tconst char *path = merge_rr.items[i].path;\n+\t\t\tconst char *name = (const char *)merge_rr.items[i].util;\n+\t\t\tdiff_two(rr_path(name, \"preimage\"), path, path, path);\n+\t\t}\n+\telse\n+\t\tusage(git_rerere_usage);\n+\n+\tpath_list_clear(&merge_rr, 1);\n+\treturn 0;\n+}\n+\ndiff --git a/builtin.h b/builtin.h\nindex 08519e7..8ffd8b2 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -52,6 +52,7 @@ extern int cmd_prune_packed(int argc, const char **argv, const char *prefix);\n extern int cmd_push(int argc, const char **argv, const char *prefix);\n extern int cmd_read_tree(int argc, const char **argv, const char *prefix);\n extern int cmd_repo_config(int argc, const char **argv, const char *prefix);\n+extern int cmd_rerere(int argc, const char **argv, const char *prefix);\n extern int cmd_rev_list(int argc, const char **argv, const char *prefix);\n extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);\n extern int cmd_rm(int argc, const char **argv, const char *prefix);\ndiff --git a/git-rerere.perl b/git-rerere.perl\ndeleted file mode 100755\nindex 4f69209..0000000\n--- a/git-rerere.perl\n+++ /dev/null\n@@ -1,284 +0,0 @@\n-#!/usr/bin/perl\n-#\n-# REuse REcorded REsolve.  This tool records a conflicted automerge\n-# result and its hand resolution, and helps to resolve future\n-# automerge that results in the same conflict.\n-#\n-# To enable this feature, create a directory 'rr-cache' under your\n-# .git/ directory.\n-\n-use Digest;\n-use File::Path;\n-use File::Copy;\n-\n-my $git_dir = $::ENV{GIT_DIR} || \".git\";\n-my $rr_dir = \"$git_dir/rr-cache\";\n-my $merge_rr = \"$git_dir/rr-cache/MERGE_RR\";\n-\n-my %merge_rr = ();\n-\n-sub read_rr {\n-\tif (!-f $merge_rr) {\n-\t\t%merge_rr = ();\n-\t\treturn;\n-\t}\n-\tmy $in;\n-\tlocal $/ = \"\\0\";\n-\topen $in, \"<$merge_rr\" or die \"$!: $merge_rr\";\n-\twhile (<$in>) {\n-\t\tchomp;\n-\t\tmy ($name, $path) = /^([0-9a-f]{40})\\t(.*)$/s;\n-\t\t$merge_rr{$path} = $name;\n-\t}\n-\tclose $in;\n-}\n-\n-sub write_rr {\n-\tmy $out;\n-\topen $out, \">$merge_rr\" or die \"$!: $merge_rr\";\n-\tfor my $path (sort keys %merge_rr) {\n-\t\tmy $name = $merge_rr{$path};\n-\t\tprint $out \"$name\\t$path\\0\";\n-\t}\n-\tclose $out;\n-}\n-\n-sub compute_conflict_name {\n-\tmy ($path) = @_;\n-\tmy @side = ();\n-\tmy $in;\n-\topen $in, \"<$path\"  or die \"$!: $path\";\n-\n-\tmy $sha1 = Digest->new(\"SHA-1\");\n-\tmy $hunk = 0;\n-\twhile (<$in>) {\n-\t\tif (/^<<<<<<< .*/) {\n-\t\t\t$hunk++;\n-\t\t\t@side = ([], undef);\n-\t\t}\n-\t\telsif (/^=======$/) {\n-\t\t\t$side[1] = [];\n-\t\t}\n-\t\telsif (/^>>>>>>> .*/) {\n-\t\t\tmy ($one, $two);\n-\t\t\t$one = join('', @{$side[0]});\n-\t\t\t$two = join('', @{$side[1]});\n-\t\t\tif ($two le $one) {\n-\t\t\t\t($one, $two) = ($two, $one);\n-\t\t\t}\n-\t\t\t$sha1->add($one);\n-\t\t\t$sha1->add(\"\\0\");\n-\t\t\t$sha1->add($two);\n-\t\t\t$sha1->add(\"\\0\");\n-\t\t\t@side = ();\n-\t\t}\n-\t\telsif (@side == 0) {\n-\t\t\tnext;\n-\t\t}\n-\t\telsif (defined $side[1]) {\n-\t\t\tpush @{$side[1]}, $_;\n-\t\t}\n-\t\telse {\n-\t\t\tpush @{$side[0]}, $_;\n-\t\t}\n-\t}\n-\tclose $in;\n-\treturn ($sha1->hexdigest, $hunk);\n-}\n-\n-sub record_preimage {\n-\tmy ($path, $name) = @_;\n-\tmy @side = ();\n-\tmy ($in, $out);\n-\topen $in, \"<$path\"  or die \"$!: $path\";\n-\topen $out, \">$name\" or die \"$!: $name\";\n-\n-\twhile (<$in>) {\n-\t\tif (/^<<<<<<< .*/) {\n-\t\t\t@side = ([], undef);\n-\t\t}\n-\t\telsif (/^=======$/) {\n-\t\t\t$side[1] = [];\n-\t\t}\n-\t\telsif (/^>>>>>>> .*/) {\n-\t\t\tmy ($one, $two);\n-\t\t\t$one = join('', @{$side[0]});\n-\t\t\t$two = join('', @{$side[1]});\n-\t\t\tif ($two le $one) {\n-\t\t\t\t($one, $two) = ($two, $one);\n-\t\t\t}\n-\t\t\tprint $out \"<<<<<<<\\n\";\n-\t\t\tprint $out $one;\n-\t\t\tprint $out \"=======\\n\";\n-\t\t\tprint $out $two;\n-\t\t\tprint $out \">>>>>>>\\n\";\n-\t\t\t@side = ();\n-\t\t}\n-\t\telsif (@side == 0) {\n-\t\t\tprint $out $_;\n-\t\t}\n-\t\telsif (defined $side[1]) {\n-\t\t\tpush @{$side[1]}, $_;\n-\t\t}\n-\t\telse {\n-\t\t\tpush @{$side[0]}, $_;\n-\t\t}\n-\t}\n-\tclose $out;\n-\tclose $in;\n-}\n-\n-sub find_conflict {\n-\tmy $in;\n-\tlocal $/ = \"\\0\";\n-\tmy $pid = open($in, '-|');\n-\tdie \"$!\" unless defined $pid;\n-\tif (!$pid) {\n-\t\texec(qw(git ls-files -z -u)) or die \"$!: ls-files\";\n-\t}\n-\tmy %path = ();\n-\tmy @path = ();\n-\twhile (<$in>) {\n-\t\tchomp;\n-\t\tmy ($mode, $sha1, $stage, $path) =\n-\t\t    /^([0-7]+) ([0-9a-f]{40}) ([123])\\t(.*)$/s;\n-\t\t$path{$path} |= (1 << $stage);\n-\t}\n-\tclose $in;\n-\twhile (my ($path, $status) = each %path) {\n-\t\tif ($status == 14) { push @path, $path; }\n-\t}\n-\treturn @path;\n-}\n-\n-sub merge {\n-\tmy ($name, $path) = @_;\n-\trecord_preimage($path, \"$rr_dir/$name/thisimage\");\n-\tunless (system('git', 'merge-file', map { \"$rr_dir/$name/${_}image\" }\n-\t\t       qw(this pre post))) {\n-\t\tmy $in;\n-\t\topen $in, \"<$rr_dir/$name/thisimage\" or\n-\t\t    die \"$!: $name/thisimage\";\n-\t\tmy $out;\n-\t\topen $out, \">$path\" or die \"$!: $path\";\n-\t\twhile (<$in>) { print $out $_; }\n-\t\tclose $in;\n-\t\tclose $out;\n-\t\treturn 1;\n-\t}\n-\treturn 0;\n-}\n-\n-sub garbage_collect_rerere {\n-\t# We should allow specifying these from the command line and\n-\t# that is why the caller gives @ARGV to us, but I am lazy.\n-\n-\tmy $cutoff_noresolve = 15; # two weeks\n-\tmy $cutoff_resolve = 60; # two months\n-\tmy @to_remove;\n-\twhile (<$rr_dir/*/preimage>) {\n-\t\tmy ($dir) = /^(.*)\\/preimage$/;\n-\t\tmy $cutoff = ((-f \"$dir/postimage\")\n-\t\t\t      ? $cutoff_resolve\n-\t\t\t      : $cutoff_noresolve);\n-\t\tmy $age = -M \"$_\";\n-\t\tif ($cutoff <= $age) {\n-\t\t\tpush @to_remove, $dir;\n-\t\t}\n-\t}\n-\tif (@to_remove) {\n-\t\trmtree(\\@to_remove);\n-\t}\n-}\n-\n--d \"$rr_dir\" || exit(0);\n-\n-read_rr();\n-\n-if (@ARGV) {\n-\tmy $arg = shift @ARGV;\n-\tif ($arg eq 'clear') {\n-\t\tfor my $path (keys %merge_rr) {\n-\t\t\tmy $name = $merge_rr{$path};\n-\t\t\tif (-d \"$rr_dir/$name\" &&\n-\t\t\t    ! -f \"$rr_dir/$name/postimage\") {\n-\t\t\t\trmtree([\"$rr_dir/$name\"]);\n-\t\t\t}\n-\t\t}\n-\t\tunlink $merge_rr;\n-\t}\n-\telsif ($arg eq 'status') {\n-\t\tfor my $path (keys %merge_rr) {\n-\t\t\tprint $path, \"\\n\";\n-\t\t}\n-\t}\n-\telsif ($arg eq 'diff') {\n-\t\tfor my $path (keys %merge_rr) {\n-\t\t\tmy $name = $merge_rr{$path};\n-\t\t\tsystem('diff', ((@ARGV == 0) ? ('-u') : @ARGV),\n-\t\t\t\t'-L', \"a/$path\", '-L', \"b/$path\",\n-\t\t\t\t\"$rr_dir/$name/preimage\", $path);\n-\t\t}\n-\t}\n-\telsif ($arg eq 'gc') {\n-\t\tgarbage_collect_rerere(@ARGV);\n-\t}\n-\telse {\n-\t\tdie \"$0 unknown command: $arg\\n\";\n-\t}\n-\texit 0;\n-}\n-\n-my %conflict = map { $_ => 1 } find_conflict();\n-\n-# MERGE_RR records paths with conflicts immediately after merge\n-# failed.  Some of the conflicted paths might have been hand resolved\n-# in the working tree since then, but the initial run would catch all\n-# and register their preimages.\n-\n-for my $path (keys %conflict) {\n-\t# This path has conflict.  If it is not recorded yet,\n-\t# record the pre-image.\n-\tif (!exists $merge_rr{$path}) {\n-\t\tmy ($name, $hunk) = compute_conflict_name($path);\n-\t\tnext unless ($hunk);\n-\t\t$merge_rr{$path} = $name;\n-\t\tif (! -d \"$rr_dir/$name\") {\n-\t\t\tmkpath(\"$rr_dir/$name\", 0, 0777);\n-\t\t\tprint STDERR \"Recorded preimage for '$path'\\n\";\n-\t\t\trecord_preimage($path, \"$rr_dir/$name/preimage\");\n-\t\t}\n-\t}\n-}\n-\n-# Now some of the paths that had conflicts earlier might have been\n-# hand resolved.  Others may be similar to a conflict already that\n-# was resolved before.\n-\n-for my $path (keys %merge_rr) {\n-\tmy $name = $merge_rr{$path};\n-\n-\t# We could resolve this automatically if we have images.\n-\tif (-f \"$rr_dir/$name/preimage\" &&\n-\t    -f \"$rr_dir/$name/postimage\") {\n-\t\tif (merge($name, $path)) {\n-\t\t\tprint STDERR \"Resolved '$path' using previous resolution.\\n\";\n-\t\t\t# Then we do not have to worry about this path\n-\t\t\t# anymore.\n-\t\t\tdelete $merge_rr{$path};\n-\t\t\tnext;\n-\t\t}\n-\t}\n-\n-\t# Let's see if we have resolved it.\n-\t(undef, my $hunk) = compute_conflict_name($path);\n-\tnext if ($hunk);\n-\n-\tprint STDERR \"Recorded resolution for '$path'.\\n\";\n-\tcopy($path, \"$rr_dir/$name/postimage\");\n-\t# And we do not have to worry about this path anymore.\n-\tdelete $merge_rr{$path};\n-}\n-\n-# Write out the rest.\n-write_rr();\ndiff --git a/git.c b/git.c\nindex 016ee8a..202f25d 100644\n--- a/git.c\n+++ b/git.c\n@@ -257,6 +257,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)\n \t\t{ \"push\", cmd_push, RUN_SETUP },\n \t\t{ \"read-tree\", cmd_read_tree, RUN_SETUP },\n \t\t{ \"repo-config\", cmd_repo_config },\n+\t\t{ \"rerere\", cmd_rerere, RUN_SETUP },\n \t\t{ \"rev-list\", cmd_rev_list, RUN_SETUP },\n \t\t{ \"rev-parse\", cmd_rev_parse, RUN_SETUP },\n \t\t{ \"rm\", cmd_rm, RUN_SETUP },\n-- \n1.4.4.2.g35224\n"}]}