{"thread":{"id":"14360","subject":"[PATCH] git-rerere.txt: Mention rr-cache directory","startedAt":"2008-07-09T00:17:33Z","lastAt":"2008-07-09T18:25:12Z","messageCount":11,"participants":["Stephan Beyer","Johannes Schindelin","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"82655","messageId":"1215562653-5043-1-git-send-email-s-beyer@gmx.net","threadId":"14360","inReplyTo":null,"subject":"[PATCH] git-rerere.txt: Mention rr-cache directory","fromName":"Stephan Beyer","fromEmail":"s-beyer@gmx.net","sentAt":"2008-07-09T00:17:33Z","receivedAt":"2008-07-09T00:17:33Z","isPatch":true,"sender":{"key":"s-beyer@gmx.net","avatar":"https://avatars.githubusercontent.com/u/143889?v=4"},"body":"If a user reads the rerere documentation, he or she is not told to\ncreate the $GIT_DIR/rr-cache directory to be able to use git-rerere.\nThis patch adds such a remark.\n\nSigned-off-by: Stephan Beyer <s-beyer@gmx.net>\n---\n Documentation/git-rerere.txt |    8 ++++----\n 1 files changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/git-rerere.txt b/Documentation/git-rerere.txt\nindex 678bfd3..667505c 100644\n--- a/Documentation/git-rerere.txt\n+++ b/Documentation/git-rerere.txt\n@@ -23,8 +23,8 @@ initial manual merge, and later by noticing the same automerge\n results and applying the previously recorded hand resolution.\n \n [NOTE]\n-You need to set the configuration variable rerere.enabled to\n-enable this command.\n+You need to set the configuration variable `rerere.enabled` and create\n+the `rr-cache` directory under `$GIT_DIR` to enable this command.\n \n \n COMMANDS\n@@ -170,8 +170,8 @@ As a convenience measure, 'git-merge' automatically invokes\n records it if it is a new conflict, or reuses the earlier hand\n resolve when it is not.  'git-commit' also invokes 'git-rerere'\n when recording a merge result.  What this means is that you do\n-not have to do anything special yourself (Note: you still have\n-to set the config variable rerere.enabled to enable this command).\n+not have to do anything special yourself, except making sure\n+that `rerere.enabled` is set and `$GIT_DIR/rr-cache` exists.\n \n In our example, when you did the test merge, the manual\n resolution is recorded, and it will be reused when you do the\n-- \n1.5.6.2.574.gd4568\n"},{"id":"82656","messageId":"alpine.DEB.1.00.0807090225050.5277@eeepc-johanness","threadId":"14360","inReplyTo":"1215562653-5043-1-git-send-email-s-beyer@gmx.net","subject":"Re: [PATCH] git-rerere.txt: Mention rr-cache directory","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-07-09T00:25:45Z","receivedAt":"2008-07-09T00:25:45Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Wed, 9 Jul 2008, Stephan Beyer wrote:\n\n> If a user reads the rerere documentation, he or she is not told to \n> create the $GIT_DIR/rr-cache directory to be able to use git-rerere. \n\nIs it?  The config setting is not enough?  Then that is a bug, and should \nnot be blessed by a bug in the documentation.\n\nCiao,\nDscho\n"},{"id":"82658","messageId":"alpine.DEB.1.00.0807090230560.5277@eeepc-johanness","threadId":"14360","inReplyTo":"alpine.DEB.1.00.0807090225050.5277@eeepc-johanness","subject":"Re: [PATCH] git-rerere.txt: Mention rr-cache directory","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-07-09T00:38:15Z","receivedAt":"2008-07-09T00:38:15Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Wed, 9 Jul 2008, Johannes Schindelin wrote:\n\n> On Wed, 9 Jul 2008, Stephan Beyer wrote:\n> \n> > If a user reads the rerere documentation, he or she is not told to \n> > create the $GIT_DIR/rr-cache directory to be able to use git-rerere. \n> \n> Is it?  The config setting is not enough?  Then that is a bug, and should \n> not be blessed by a bug in the documentation.\n\nOkay, I took a look:\n\n-- snip --\nstatic int is_rerere_enabled(void)\n{\n        struct stat st;\n        const char *rr_cache;\n        int rr_cache_exists;\n\n        if (!rerere_enabled)\n                return 0;\n\n        rr_cache = git_path(\"rr-cache\");\n        rr_cache_exists = !stat(rr_cache, &st) && S_ISDIR(st.st_mode);\n        if (rerere_enabled < 0)\n                return rr_cache_exists;\n\n        if (!rr_cache_exists &&\n            (mkdir(rr_cache, 0777) || adjust_shared_perm(rr_cache)))\n                die(\"Could not create directory %s\", rr_cache);\n        return 1;\n}\n-- snap --\n\nAs you can see, in the case rerere_enabled < 0 (i.e. the config did not \nsay anything about rerere), it is assumed enabled _exactly_ when \n.git/rr_cache/ exists.\n\nBut if it is > 0, the directory is created.\n\nOf course, this only holds true when the config is read, i.e. when \nsetup_rerere() was called in time.  Which is the case when you call \nrerere() (as is done both from cmd_rerere() as well as cmd_commit()).\n\nOf course, I haven't tested it.  Other than running the test script, that \nis.\n\nSo care to elaborate what is going wrong?\n\nBTW I think it is a horrible thing that rerere() is declared in commit.h \n(a libgit header), but implemented in builtin-rerere.c, which is not part \nof libgit.a.\n\nCiao,\nDscho\n"},{"id":"82661","messageId":"7vk5fvq2w4.fsf@gitster.siamese.dyndns.org","threadId":"14360","inReplyTo":"alpine.DEB.1.00.0807090230560.5277@eeepc-johanness","subject":"Re: [PATCH] git-rerere.txt: Mention rr-cache directory","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-07-09T01:09:47Z","receivedAt":"2008-07-09T01:09:47Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> Of course, this only holds true when the config is read, i.e. when \n> setup_rerere() was called in time.  Which is the case when you call \n> rerere() (as is done both from cmd_rerere() as well as cmd_commit()).\n>\n> Of course, I haven't tested it.  Other than running the test script, that \n> is.\n>\n> So care to elaborate what is going wrong?\n\nVery interesting question indeed.\n"},{"id":"82700","messageId":"20080709092103.GA5185@leksak.fem-net","threadId":"14360","inReplyTo":"alpine.DEB.1.00.0807090230560.5277@eeepc-johanness","subject":"Re: [PATCH] git-rerere.txt: Mention rr-cache directory","fromName":"Stephan Beyer","fromEmail":"s-beyer@gmx.net","sentAt":"2008-07-09T09:21:03Z","receivedAt":"2008-07-09T09:21:03Z","isPatch":true,"sender":{"key":"s-beyer@gmx.net","avatar":"https://avatars.githubusercontent.com/u/143889?v=4"},"body":"Hi,\n\nOn Wed, Jul 09, Johannes Schindelin wrote:\n> On Wed, 9 Jul 2008, Johannes Schindelin wrote:\n> > On Wed, 9 Jul 2008, Stephan Beyer wrote:\n> > \n> > > If a user reads the rerere documentation, he or she is not told to \n> > > create the $GIT_DIR/rr-cache directory to be able to use git-rerere. \n> > \n> > Is it?  The config setting is not enough?  Then that is a bug, and should \n> > not be blessed by a bug in the documentation.\n\nI don't know if this is exactly a bug, at least config.txt says:\n\nrerere.enabled::\n\tActivate recording of resolved conflicts, so that identical\n\tconflict hunks can be resolved automatically, should they\n\tbe encountered again.  linkgit:git-rerere[1] command is by\n\tdefault enabled if you create `rr-cache` directory under\n\t`$GIT_DIR`, but can be disabled by setting this option to false.\n\nSo according to *that* documentation it is right, according to the\ngit-rerere.txt documentation it was wrong, and according to the\nsource it is...\n> -- snip --\n> static int is_rerere_enabled(void)\n> {\n>         struct stat st;\n>         const char *rr_cache;\n>         int rr_cache_exists;\n> \n>         if (!rerere_enabled)\n>                 return 0;\n> \n>         rr_cache = git_path(\"rr-cache\");\n>         rr_cache_exists = !stat(rr_cache, &st) && S_ISDIR(st.st_mode);\n>         if (rerere_enabled < 0)\n>                 return rr_cache_exists;\n> \n>         if (!rr_cache_exists &&\n>             (mkdir(rr_cache, 0777) || adjust_shared_perm(rr_cache)))\n>                 die(\"Could not create directory %s\", rr_cache);\n>         return 1;\n> }\n> -- snap --\n> \n> As you can see, in the case rerere_enabled < 0 (i.e. the config did not \n> say anything about rerere), it is assumed enabled _exactly_ when \n> .git/rr_cache/ exists.\n> \n> But if it is > 0, the directory is created.\n\nYes.\nThere is also an important part in the file:\n-- snip --\n/* if rerere_enabled == -1, fall back to detection of .git/rr-cache */\nstatic int rerere_enabled = -1;\n-- snap --\n\nSo this means it's the following behavior:\n - rerere_enabled < 0: enable, iff rr-cache exists\n - rerere_enabled ==0: disable\n - rerere_enabled > 0: enable and create rr-cache if it does not exist\n\nSo this is my fault, sorry :) Both documentations (config.txt and\ngit-rerere.txt) are right.\nIt was not explicitly enabled on this one machine which lead me to the\nwrong assumption. I should sync my ~/.gitconfig more often ;)\n\nSorry for that,\n  Stephan\n\n-- \nStephan Beyer <s-beyer@gmx.net>, PGP 0x6EDDD207FCC5040F\n"},{"id":"82702","messageId":"1215598683-9685-1-git-send-email-s-beyer@gmx.net","threadId":"14360","inReplyTo":"alpine.DEB.1.00.0807090230560.5277@eeepc-johanness","subject":"[PATCH] rerere: Separate libgit and builtin functions","fromName":"Stephan Beyer","fromEmail":"s-beyer@gmx.net","sentAt":"2008-07-09T10:18:03Z","receivedAt":"2008-07-09T10:18:03Z","isPatch":true,"sender":{"key":"s-beyer@gmx.net","avatar":"https://avatars.githubusercontent.com/u/143889?v=4"},"body":"This patch moves rerere()-related functions into a newly created\nrerere.c file.  Also setup_rerere() is moved into rerere.c to decrease\ncode duplications for rerere.c and builtin-rerere.c.\n\nSigned-off-by: Stephan Beyer <s-beyer@gmx.net>\n---\n\n> BTW I think it is a horrible thing that rerere() is declared in commit.h\n> (a libgit header), but implemented in builtin-rerere.c, which is not part\n> of libgit.a.\n\nOk, I give it a try.\n\n Makefile                     |    2 +\n builtin-rerere.c             |  324 +++---------------------------------------\n builtin-rerere.c => rerere.c |  133 +-----------------\n rerere.h                     |    9 ++\n 4 files changed, 32 insertions(+), 436 deletions(-)\n copy builtin-rerere.c => rerere.c (66%)\n create mode 100644 rerere.h\n\ndiff --git a/Makefile b/Makefile\nindex bddd1a7..4e23c29 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -363,6 +363,7 @@ LIB_H += quote.h\n LIB_H += reflog-walk.h\n LIB_H += refs.h\n LIB_H += remote.h\n+LIB_H += rerere.h\n LIB_H += revision.h\n LIB_H += run-command.h\n LIB_H += sha1-lookup.h\n@@ -447,6 +448,7 @@ LIB_OBJS += read-cache.o\n LIB_OBJS += reflog-walk.o\n LIB_OBJS += refs.o\n LIB_OBJS += remote.o\n+LIB_OBJS += rerere.o\n LIB_OBJS += revision.o\n LIB_OBJS += run-command.o\n LIB_OBJS += server-info.o\ndiff --git a/builtin-rerere.c b/builtin-rerere.c\nindex 85222d9..669a963 100644\n--- a/builtin-rerere.c\n+++ b/builtin-rerere.c\n@@ -1,6 +1,7 @@\n #include \"builtin.h\"\n #include \"cache.h\"\n #include \"path-list.h\"\n+#include \"rerere.h\"\n #include \"xdiff/xdiff.h\"\n #include \"xdiff-interface.h\"\n \n@@ -13,183 +14,11 @@ static const char git_rerere_usage[] =\n static int cutoff_noresolve = 15;\n static int cutoff_resolve = 60;\n \n-/* if rerere_enabled == -1, fall back to detection of .git/rr-cache */\n-static int rerere_enabled = -1;\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 = 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\tint length = strlen(path) + 1;\n-\t\tif (write_in_full(out_fd, rr->items[i].util, 40) != 40 ||\n-\t\t    write_in_full(out_fd, \"\\t\", 1) != 1 ||\n-\t\t    write_in_full(out_fd, path, length) != length)\n-\t\t\tdie(\"unable to write rerere record\");\n-\t}\n-\tif (commit_lock_file(&write_lock) != 0)\n-\t\tdie(\"unable to write rerere record\");\n-\treturn 0;\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 strbuf one, two;\n-\tFILE *f = fopen(path, \"r\");\n-\tFILE *out = NULL;\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}\n-\n-\tif (sha1)\n-\t\tSHA1_Init(&ctx);\n-\n-\tstrbuf_init(&one, 0);\n-\tstrbuf_init(&two,  0);\n-\twhile (fgets(buf, sizeof(buf), f)) {\n-\t\tif (!prefixcmp(buf, \"<<<<<<< \"))\n-\t\t\thunk = 1;\n-\t\telse if (!prefixcmp(buf, \"=======\"))\n-\t\t\thunk = 2;\n-\t\telse if (!prefixcmp(buf, \">>>>>>> \")) {\n-\t\t\tint cmp = strbuf_cmp(&one, &two);\n-\n-\t\t\thunk_no++;\n-\t\t\thunk = 0;\n-\t\t\tif (cmp > 0) {\n-\t\t\t\tstrbuf_swap(&one, &two);\n-\t\t\t}\n-\t\t\tif (out) {\n-\t\t\t\tfputs(\"<<<<<<<\\n\", out);\n-\t\t\t\tfwrite(one.buf, one.len, 1, out);\n-\t\t\t\tfputs(\"=======\\n\", out);\n-\t\t\t\tfwrite(two.buf, two.len, 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.buf ? one.buf : \"\",\n-\t\t\t\t\t    one.len + 1);\n-\t\t\t\tSHA1_Update(&ctx, two.buf ? two.buf : \"\",\n-\t\t\t\t\t    two.len + 1);\n-\t\t\t}\n-\t\t\tstrbuf_reset(&one);\n-\t\t\tstrbuf_reset(&two);\n-\t\t} else if (hunk == 1)\n-\t\t\tstrbuf_addstr(&one, buf);\n-\t\telse if (hunk == 2)\n-\t\t\tstrbuf_addstr(&two, buf);\n-\t\telse if (out)\n-\t\t\tfputs(buf, out);\n-\t}\n-\tstrbuf_release(&one);\n-\tstrbuf_release(&two);\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+1 < active_nr; i++) {\n-\t\tstruct cache_entry *e2 = active_cache[i];\n-\t\tstruct cache_entry *e3 = active_cache[i+1];\n-\t\tif (ce_stage(e2) == 2 &&\n-\t\t    ce_stage(e3) == 3 &&\n-\t\t    ce_same_name(e2, e3) &&\n-\t\t    S_ISREG(e2->ce_mode) &&\n-\t\t    S_ISREG(e3->ce_mode)) {\n-\t\t\tpath_list_insert((const char *)e2->name, conflict);\n-\t\t\ti++; /* skip over both #2 and #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@@ -198,6 +27,17 @@ static void unlink_rr_item(const char *name)\n \trmdir(git_path(\"rr-cache/%s\", name));\n }\n \n+static int git_rerere_gc_config(const char *var, const char *value, void *cb)\n+{\n+\tif (!strcmp(var, \"gc.rerereresolved\"))\n+\t\tcutoff_resolve = git_config_int(var, value);\n+\telse if (!strcmp(var, \"gc.rerereunresolved\"))\n+\t\tcutoff_noresolve = git_config_int(var, value);\n+\telse\n+\t\treturn git_default_config(var, value, cb);\n+\treturn 0;\n+}\n+\n static void garbage_collect(struct path_list *rr)\n {\n \tstruct path_list to_remove = { NULL, 0, 0, 1 };\n@@ -207,6 +47,7 @@ static void garbage_collect(struct path_list *rr)\n \tint len, i, cutoff;\n \ttime_t now = time(NULL), then;\n \n+\tgit_config(git_rerere_gc_config, NULL);\n \tstrlcpy(buf, git_path(\"rr-cache\"), sizeof(buf));\n \tlen = strlen(buf);\n \tdir = opendir(buf);\n@@ -267,148 +108,19 @@ static int diff_two(const char *file1, const char *label1,\n \treturn 0;\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(rr_path(name, \"postimage\"), path, 0666);\n-tail_optimization:\n-\t\tif (i < rr->nr - 1)\n-\t\t\tmemmove(rr->items + i,\n-\t\t\t\trr->items + i + 1,\n-\t\t\t\tsizeof(rr->items[0]) * (rr->nr - i - 1));\n-\t\trr->nr--;\n-\t\ti--;\n-\t}\n-\n-\treturn write_rr(rr, fd);\n-}\n-\n-static int git_rerere_config(const char *var, const char *value, void *cb)\n-{\n-\tif (!strcmp(var, \"gc.rerereresolved\"))\n-\t\tcutoff_resolve = git_config_int(var, value);\n-\telse if (!strcmp(var, \"gc.rerereunresolved\"))\n-\t\tcutoff_noresolve = git_config_int(var, value);\n-\telse if (!strcmp(var, \"rerere.enabled\"))\n-\t\trerere_enabled = git_config_bool(var, value);\n-\telse\n-\t\treturn git_default_config(var, value, cb);\n-\treturn 0;\n-}\n-\n-static int is_rerere_enabled(void)\n-{\n-\tstruct stat st;\n-\tconst char *rr_cache;\n-\tint rr_cache_exists;\n-\n-\tif (!rerere_enabled)\n-\t\treturn 0;\n-\n-\trr_cache = git_path(\"rr-cache\");\n-\trr_cache_exists = !stat(rr_cache, &st) && S_ISDIR(st.st_mode);\n-\tif (rerere_enabled < 0)\n-\t\treturn rr_cache_exists;\n-\n-\tif (!rr_cache_exists &&\n-\t    (mkdir(rr_cache, 0777) || adjust_shared_perm(rr_cache)))\n-\t\tdie(\"Could not create directory %s\", rr_cache);\n-\treturn 1;\n-}\n-\n-static int setup_rerere(struct path_list *merge_rr)\n-{\n-\tint fd;\n-\n-\tgit_config(git_rerere_config, NULL);\n-\tif (!is_rerere_enabled())\n-\t\treturn -1;\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-\treturn fd;\n-}\n-\n-int rerere(void)\n-{\n-\tstruct path_list merge_rr = { NULL, 0, 0, 1 };\n-\tint fd;\n-\n-\tfd = setup_rerere(&merge_rr);\n-\tif (fd < 0)\n-\t\treturn 0;\n-\treturn do_plain_rerere(&merge_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;\n \n+\tif (argc < 2)\n+\t\treturn rerere();\n+\n \tfd = setup_rerere(&merge_rr);\n \tif (fd < 0)\n \t\treturn 0;\n \n-\tif (argc < 2)\n-\t\treturn do_plain_rerere(&merge_rr, fd);\n-\telse if (!strcmp(argv[1], \"clear\")) {\n+\tif (!strcmp(argv[1], \"clear\")) {\n \t\tfor (i = 0; i < merge_rr.nr; i++) {\n \t\t\tstruct stat st;\n \t\t\tconst char *name = (const char *)merge_rr.items[i].util;\n@@ -417,7 +129,7 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)\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\tunlink(git_path(\"rr-cache/MERGE_RR\"));\n \t} else if (!strcmp(argv[1], \"gc\"))\n \t\tgarbage_collect(&merge_rr);\n \telse if (!strcmp(argv[1], \"status\"))\ndiff --git a/builtin-rerere.c b/rerere.c\nsimilarity index 66%\ncopy from builtin-rerere.c\ncopy to rerere.c\nindex 85222d9..5c22bed 100644\n--- a/builtin-rerere.c\n+++ b/rerere.c\n@@ -1,18 +1,11 @@\n-#include \"builtin.h\"\n #include \"cache.h\"\n #include \"path-list.h\"\n+#include \"rerere.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 /* if rerere_enabled == -1, fall back to detection of .git/rr-cache */\n static int rerere_enabled = -1;\n \n@@ -190,83 +183,6 @@ static int merge(const char *name, const char *path)\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\tif (write_in_full(1, ptr[i].ptr, ptr[i].size) != ptr[i].size)\n-\t\t\treturn -1;\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-\tmemset(&xecfg, 0, sizeof(xecfg));\n-\txecfg.ctxlen = 3;\n-\tecb.outf = outf;\n-\txdi_diff(&minus, &plus, &xpp, &xecfg, &ecb);\n-\n-\tfree(minus.ptr);\n-\tfree(plus.ptr);\n-\treturn 0;\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@@ -341,11 +257,7 @@ tail_optimization:\n \n static int git_rerere_config(const char *var, const char *value, void *cb)\n {\n-\tif (!strcmp(var, \"gc.rerereresolved\"))\n-\t\tcutoff_resolve = git_config_int(var, value);\n-\telse if (!strcmp(var, \"gc.rerereunresolved\"))\n-\t\tcutoff_noresolve = git_config_int(var, value);\n-\telse if (!strcmp(var, \"rerere.enabled\"))\n+\tif (!strcmp(var, \"rerere.enabled\"))\n \t\trerere_enabled = git_config_bool(var, value);\n \telse\n \t\treturn git_default_config(var, value, cb);\n@@ -372,7 +284,7 @@ static int is_rerere_enabled(void)\n \treturn 1;\n }\n \n-static int setup_rerere(struct path_list *merge_rr)\n+int setup_rerere(struct path_list *merge_rr)\n {\n \tint fd;\n \n@@ -396,42 +308,3 @@ int rerere(void)\n \t\treturn 0;\n \treturn do_plain_rerere(&merge_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;\n-\n-\tfd = setup_rerere(&merge_rr);\n-\tif (fd < 0)\n-\t\treturn 0;\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\tstruct stat st;\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-}\ndiff --git a/rerere.h b/rerere.h\nnew file mode 100644\nindex 0000000..35b0fa8\n--- /dev/null\n+++ b/rerere.h\n@@ -0,0 +1,9 @@\n+#ifndef RERERE_H\n+#define RERERE_H\n+\n+#include \"path-list.h\"\n+\n+extern int setup_rerere(struct path_list *);\n+extern int rerere(void);\n+\n+#endif\n-- \n1.5.6.2.574.gd4568\n"},{"id":"82709","messageId":"alpine.DEB.1.00.0807091355220.5277@eeepc-johanness","threadId":"14360","inReplyTo":"1215598683-9685-1-git-send-email-s-beyer@gmx.net","subject":"Re: [PATCH] rerere: Separate libgit and builtin functions","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-07-09T11:55:54Z","receivedAt":"2008-07-09T11:55:54Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Wed, 9 Jul 2008, Stephan Beyer wrote:\n\n> This patch moves rerere()-related functions into a newly created \n> rerere.c file.  Also setup_rerere() is moved into rerere.c to decrease \n> code duplications for rerere.c and builtin-rerere.c.\n\nIt is not moved to decrease code duplication, but because it is needed by \nrerere().\n\nCiao,\nDscho\n"},{"id":"82710","messageId":"20080709120030.GA10528@leksak.fem-net","threadId":"14360","inReplyTo":"alpine.DEB.1.00.0807091355220.5277@eeepc-johanness","subject":"Re: [PATCH] rerere: Separate libgit and builtin functions","fromName":"Stephan Beyer","fromEmail":"s-beyer@gmx.net","sentAt":"2008-07-09T12:00:30Z","receivedAt":"2008-07-09T12:00:30Z","isPatch":true,"sender":{"key":"s-beyer@gmx.net","avatar":"https://avatars.githubusercontent.com/u/143889?v=4"},"body":"> > This patch moves rerere()-related functions into a newly created \n> > rerere.c file.  Also setup_rerere() is moved into rerere.c to decrease \n> > code duplications for rerere.c and builtin-rerere.c.\n> \n> It is not moved to decrease code duplication, but because it is needed by \n> rerere().\n\nRight.\n\nAnd btw the patch seems to be broken. (At least I can't apply it\nhere...)   Sorry :( Usually I test applying before sending but this time\nlunch was eliciting.\n\nWell, I'm going to fix that.\n\n-- \nStephan Beyer <s-beyer@gmx.net>, PGP 0x6EDDD207FCC5040F\n"},{"id":"82712","messageId":"alpine.DEB.1.00.0807091356050.5277@eeepc-johanness","threadId":"14360","inReplyTo":"1215598683-9685-1-git-send-email-s-beyer@gmx.net","subject":"Re: [PATCH] rerere: Separate libgit and builtin functions","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-07-09T12:03:53Z","receivedAt":"2008-07-09T12:03:53Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Wed, 9 Jul 2008, Stephan Beyer wrote:\n\n>  Makefile                     |    2 +\n>  builtin-rerere.c             |  324 +++---------------------------------------\n>  builtin-rerere.c => rerere.c |  133 +-----------------\n>  rerere.h                     |    9 ++\n>  4 files changed, 32 insertions(+), 436 deletions(-)\n\nHeh, that sounds nice!  Deleting way more lines than adding!  :-)\n\n>  copy builtin-rerere.c => rerere.c (66%)\n\nOh, well :-)\n\n> diff --git a/builtin-rerere.c b/rerere.c\n> similarity index 66%\n> copy from builtin-rerere.c\n> copy to rerere.c\n> index 85222d9..5c22bed 100644\n> --- a/builtin-rerere.c\n> +++ b/rerere.c\n> @@ -1,18 +1,11 @@\n> -#include \"builtin.h\"\n>  #include \"cache.h\"\n>  #include \"path-list.h\"\n> +#include \"rerere.h\"\n>  #include \"xdiff/xdiff.h\"\n>  #include \"xdiff-interface.h\"\n>  \n>  #include <time.h>\n\nNo longer necessary, is it?\n\n> diff --git a/rerere.h b/rerere.h\n> new file mode 100644\n> index 0000000..35b0fa8\n> --- /dev/null\n> +++ b/rerere.h\n> @@ -0,0 +1,9 @@\n> +#ifndef RERERE_H\n> +#define RERERE_H\n> +\n> +#include \"path-list.h\"\n> +\n> +extern int setup_rerere(struct path_list *);\n\nWhy?\n\nAh, it is needed for \"gc\".  Maybe this needs documentation, since it cause \nsome minutes of head scratching with yours truly.\n\nCiao,\nDscho\n"},{"id":"82717","messageId":"1215608337-5486-1-git-send-email-s-beyer@gmx.net","threadId":"14360","inReplyTo":"20080709120030.GA10528@leksak.fem-net","subject":"[PATCHv2] rerere: Separate libgit and builtin functions","fromName":"Stephan Beyer","fromEmail":"s-beyer@gmx.net","sentAt":"2008-07-09T12:58:57Z","receivedAt":"2008-07-09T12:58:57Z","isPatch":false,"sender":{"key":"s-beyer@gmx.net","avatar":"https://avatars.githubusercontent.com/u/143889?v=4"},"body":"This patch moves rerere()-related functions into a newly created\nrerere.c file.\nThe setup_rerere() function is needed by both rerere() and cmd_rerere(),\nso this function is moved to rerere.c and declared non-static (and \"extern\")\nin newly created rerere.h file.\n\nSigned-off-by: Stephan Beyer <s-beyer@gmx.net>\n---\nHi again,\n\nso now it should apply cleanly.\nSeems that I had not built the topic branch of the former patch\non top of the latest master by accident.\n\nRegards,\n  Stephan\n\n Makefile                     |    2 +\n builtin-commit.c             |    1 +\n builtin-rerere.c             |  362 ++---------------------------------------\n commit.h                     |    1 -\n builtin-rerere.c => rerere.c |  129 +---------------\n rerere.h                     |    9 +\n 6 files changed, 33 insertions(+), 471 deletions(-)\n copy builtin-rerere.c => rerere.c (71%)\n create mode 100644 rerere.h\n\ndiff --git a/Makefile b/Makefile\nindex 4796565..de15163 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -363,6 +363,7 @@ LIB_H += quote.h\n LIB_H += reflog-walk.h\n LIB_H += refs.h\n LIB_H += remote.h\n+LIB_H += rerere.h\n LIB_H += revision.h\n LIB_H += run-command.h\n LIB_H += sha1-lookup.h\n@@ -447,6 +448,7 @@ LIB_OBJS += read-cache.o\n LIB_OBJS += reflog-walk.o\n LIB_OBJS += refs.o\n LIB_OBJS += remote.o\n+LIB_OBJS += rerere.o\n LIB_OBJS += revision.o\n LIB_OBJS += run-command.o\n LIB_OBJS += server-info.o\ndiff --git a/builtin-commit.c b/builtin-commit.c\nindex 745c11e..bdc83df 100644\n--- a/builtin-commit.c\n+++ b/builtin-commit.c\n@@ -22,6 +22,7 @@\n #include \"utf8.h\"\n #include \"parse-options.h\"\n #include \"path-list.h\"\n+#include \"rerere.h\"\n #include \"unpack-trees.h\"\n \n static const char * const builtin_commit_usage[] = {\ndiff --git a/builtin-rerere.c b/builtin-rerere.c\nindex 839b26e..5d40e16 100644\n--- a/builtin-rerere.c\n+++ b/builtin-rerere.c\n@@ -1,11 +1,10 @@\n #include \"builtin.h\"\n #include \"cache.h\"\n #include \"path-list.h\"\n+#include \"rerere.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@@ -13,14 +12,6 @@ static const char git_rerere_usage[] =\n static int cutoff_noresolve = 15;\n static int cutoff_resolve = 60;\n \n-/* if rerere_enabled == -1, fall back to detection of .git/rr-cache */\n-static int rerere_enabled = -1;\n-\n-/* automatically update cleanly resolved paths to the index */\n-static int rerere_autoupdate;\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@@ -38,179 +29,6 @@ static int has_resolution(const char *name)\n \treturn !stat(rr_path(name, \"postimage\"), &st);\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 = 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;\n-\t\tint length;\n-\t\tif (!rr->items[i].util)\n-\t\t\tcontinue;\n-\t\tpath = rr->items[i].path;\n-\t\tlength = strlen(path) + 1;\n-\t\tif (write_in_full(out_fd, rr->items[i].util, 40) != 40 ||\n-\t\t    write_in_full(out_fd, \"\\t\", 1) != 1 ||\n-\t\t    write_in_full(out_fd, path, length) != length)\n-\t\t\tdie(\"unable to write rerere record\");\n-\t}\n-\tif (commit_lock_file(&write_lock) != 0)\n-\t\tdie(\"unable to write rerere record\");\n-\treturn 0;\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 strbuf one, two;\n-\tFILE *f = fopen(path, \"r\");\n-\tFILE *out = NULL;\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}\n-\n-\tif (sha1)\n-\t\tSHA1_Init(&ctx);\n-\n-\tstrbuf_init(&one, 0);\n-\tstrbuf_init(&two,  0);\n-\twhile (fgets(buf, sizeof(buf), f)) {\n-\t\tif (!prefixcmp(buf, \"<<<<<<< \"))\n-\t\t\thunk = 1;\n-\t\telse if (!prefixcmp(buf, \"=======\"))\n-\t\t\thunk = 2;\n-\t\telse if (!prefixcmp(buf, \">>>>>>> \")) {\n-\t\t\tif (strbuf_cmp(&one, &two) > 0)\n-\t\t\t\tstrbuf_swap(&one, &two);\n-\t\t\thunk_no++;\n-\t\t\thunk = 0;\n-\t\t\tif (out) {\n-\t\t\t\tfputs(\"<<<<<<<\\n\", out);\n-\t\t\t\tfwrite(one.buf, one.len, 1, out);\n-\t\t\t\tfputs(\"=======\\n\", out);\n-\t\t\t\tfwrite(two.buf, two.len, 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.buf ? one.buf : \"\",\n-\t\t\t\t\t    one.len + 1);\n-\t\t\t\tSHA1_Update(&ctx, two.buf ? two.buf : \"\",\n-\t\t\t\t\t    two.len + 1);\n-\t\t\t}\n-\t\t\tstrbuf_reset(&one);\n-\t\t\tstrbuf_reset(&two);\n-\t\t} else if (hunk == 1)\n-\t\t\tstrbuf_addstr(&one, buf);\n-\t\telse if (hunk == 2)\n-\t\t\tstrbuf_addstr(&two, buf);\n-\t\telse if (out)\n-\t\t\tfputs(buf, out);\n-\t}\n-\tstrbuf_release(&one);\n-\tstrbuf_release(&two);\n-\n-\tfclose(f);\n-\tif (out)\n-\t\tfclose(out);\n-\tif (sha1)\n-\t\tSHA1_Final(sha1, &ctx);\n-\tif (hunk) {\n-\t\tif (output)\n-\t\t\tunlink(output);\n-\t\treturn error(\"Could not parse conflict hunks in %s\", path);\n-\t}\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+1 < active_nr; i++) {\n-\t\tstruct cache_entry *e2 = active_cache[i];\n-\t\tstruct cache_entry *e3 = active_cache[i+1];\n-\t\tif (ce_stage(e2) == 2 &&\n-\t\t    ce_stage(e3) == 3 &&\n-\t\t    ce_same_name(e2, e3) &&\n-\t\t    S_ISREG(e2->ce_mode) &&\n-\t\t    S_ISREG(e3->ce_mode)) {\n-\t\t\tpath_list_insert((const char *)e2->name, conflict);\n-\t\t\ti++; /* skip over both #2 and #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@@ -219,6 +37,17 @@ static void unlink_rr_item(const char *name)\n \trmdir(git_path(\"rr-cache/%s\", name));\n }\n \n+static int git_rerere_gc_config(const char *var, const char *value, void *cb)\n+{\n+\tif (!strcmp(var, \"gc.rerereresolved\"))\n+\t\tcutoff_resolve = git_config_int(var, value);\n+\telse if (!strcmp(var, \"gc.rerereunresolved\"))\n+\t\tcutoff_noresolve = git_config_int(var, value);\n+\telse\n+\t\treturn git_default_config(var, value, cb);\n+\treturn 0;\n+}\n+\n static void garbage_collect(struct path_list *rr)\n {\n \tstruct path_list to_remove = { NULL, 0, 0, 1 };\n@@ -227,6 +56,7 @@ static void garbage_collect(struct path_list *rr)\n \tint i, cutoff;\n \ttime_t now = time(NULL), then;\n \n+\tgit_config(git_rerere_gc_config, NULL);\n \tdir = opendir(git_path(\"rr-cache\"));\n \twhile ((e = readdir(dir))) {\n \t\tconst char *name = e->d_name;\n@@ -279,181 +109,25 @@ static int diff_two(const char *file1, const char *label1,\n \treturn 0;\n }\n \n-static struct lock_file index_lock;\n-\n-static int update_paths(struct path_list *update)\n-{\n-\tint i;\n-\tint fd = hold_locked_index(&index_lock, 0);\n-\tint status = 0;\n-\n-\tif (fd < 0)\n-\t\treturn -1;\n-\n-\tfor (i = 0; i < update->nr; i++) {\n-\t\tstruct path_list_item *item = &update->items[i];\n-\t\tif (add_file_to_cache(item->path, ADD_CACHE_IGNORE_ERRORS))\n-\t\t\tstatus = -1;\n-\t}\n-\n-\tif (!status && active_cache_changed) {\n-\t\tif (write_cache(fd, active_cache, active_nr) ||\n-\t\t    commit_locked_index(&index_lock))\n-\t\t\tdie(\"Unable to write new index file\");\n-\t} else if (fd >= 0)\n-\t\trollback_lock_file(&index_lock);\n-\treturn status;\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-\tstruct path_list update = { 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\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 (has_resolution(name)) {\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\tif (rerere_autoupdate)\n-\t\t\t\t\tpath_list_insert(path, &update);\n-\t\t\t\tgoto mark_resolved;\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(rr_path(name, \"postimage\"), path, 0666);\n-\tmark_resolved:\n-\t\trr->items[i].util = NULL;\n-\t}\n-\n-\tif (update.nr)\n-\t\tupdate_paths(&update);\n-\n-\treturn write_rr(rr, fd);\n-}\n-\n-static int git_rerere_config(const char *var, const char *value, void *cb)\n-{\n-\tif (!strcmp(var, \"gc.rerereresolved\"))\n-\t\tcutoff_resolve = git_config_int(var, value);\n-\telse if (!strcmp(var, \"gc.rerereunresolved\"))\n-\t\tcutoff_noresolve = git_config_int(var, value);\n-\telse if (!strcmp(var, \"rerere.enabled\"))\n-\t\trerere_enabled = git_config_bool(var, value);\n-\telse if (!strcmp(var, \"rerere.autoupdate\"))\n-\t\trerere_autoupdate = git_config_bool(var, value);\n-\telse\n-\t\treturn git_default_config(var, value, cb);\n-\treturn 0;\n-}\n-\n-static int is_rerere_enabled(void)\n-{\n-\tstruct stat st;\n-\tconst char *rr_cache;\n-\tint rr_cache_exists;\n-\n-\tif (!rerere_enabled)\n-\t\treturn 0;\n-\n-\trr_cache = git_path(\"rr-cache\");\n-\trr_cache_exists = !stat(rr_cache, &st) && S_ISDIR(st.st_mode);\n-\tif (rerere_enabled < 0)\n-\t\treturn rr_cache_exists;\n-\n-\tif (!rr_cache_exists &&\n-\t    (mkdir(rr_cache, 0777) || adjust_shared_perm(rr_cache)))\n-\t\tdie(\"Could not create directory %s\", rr_cache);\n-\treturn 1;\n-}\n-\n-static int setup_rerere(struct path_list *merge_rr)\n-{\n-\tint fd;\n-\n-\tgit_config(git_rerere_config, NULL);\n-\tif (!is_rerere_enabled())\n-\t\treturn -1;\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-\treturn fd;\n-}\n-\n-int rerere(void)\n-{\n-\tstruct path_list merge_rr = { NULL, 0, 0, 1 };\n-\tint fd;\n-\n-\tfd = setup_rerere(&merge_rr);\n-\tif (fd < 0)\n-\t\treturn 0;\n-\treturn do_plain_rerere(&merge_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;\n \n+\tif (argc < 2)\n+\t\treturn rerere();\n+\n \tfd = setup_rerere(&merge_rr);\n \tif (fd < 0)\n \t\treturn 0;\n \n-\tif (argc < 2)\n-\t\treturn do_plain_rerere(&merge_rr, fd);\n-\telse if (!strcmp(argv[1], \"clear\")) {\n+\tif (!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 (!has_resolution(name))\n \t\t\t\tunlink_rr_item(name);\n \t\t}\n-\t\tunlink(merge_rr_path);\n+\t\tunlink(git_path(\"rr-cache/MERGE_RR\"));\n \t} else if (!strcmp(argv[1], \"gc\"))\n \t\tgarbage_collect(&merge_rr);\n \telse if (!strcmp(argv[1], \"status\"))\ndiff --git a/commit.h b/commit.h\nindex 2d94d41..fda7ad0 100644\n--- a/commit.h\n+++ b/commit.h\n@@ -131,7 +131,6 @@ extern struct commit_list *get_shallow_commits(struct object_array *heads,\n int in_merge_bases(struct commit *, struct commit **, int);\n \n extern int interactive_add(int argc, const char **argv, const char *prefix);\n-extern int rerere(void);\n \n static inline int single_parent(struct commit *commit)\n {\ndiff --git a/builtin-rerere.c b/rerere.c\nsimilarity index 71%\ncopy from builtin-rerere.c\ncopy to rerere.c\nindex 839b26e..bbd8c00 100644\n--- a/builtin-rerere.c\n+++ b/rerere.c\n@@ -1,18 +1,9 @@\n-#include \"builtin.h\"\n #include \"cache.h\"\n #include \"path-list.h\"\n+#include \"rerere.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 /* if rerere_enabled == -1, fall back to detection of .git/rr-cache */\n static int rerere_enabled = -1;\n \n@@ -26,12 +17,6 @@ static const char *rr_path(const char *name, const char *file)\n \treturn git_path(\"rr-cache/%s/%s\", name, file);\n }\n \n-static time_t rerere_created_at(const char *name)\n-{\n-\tstruct stat st;\n-\treturn stat(rr_path(name, \"preimage\"), &st) ? (time_t) 0 : st.st_mtime;\n-}\n-\n static int has_resolution(const char *name)\n {\n \tstruct stat st;\n@@ -211,74 +196,6 @@ static int merge(const char *name, const char *path)\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-\tDIR *dir;\n-\tstruct dirent *e;\n-\tint i, cutoff;\n-\ttime_t now = time(NULL), then;\n-\n-\tdir = opendir(git_path(\"rr-cache\"));\n-\twhile ((e = readdir(dir))) {\n-\t\tconst char *name = e->d_name;\n-\t\tif (name[0] == '.' &&\n-\t\t    (name[1] == '\\0' || (name[1] == '.' && name[2] == '\\0')))\n-\t\t\tcontinue;\n-\t\tthen = rerere_created_at(name);\n-\t\tif (!then)\n-\t\t\tcontinue;\n-\t\tcutoff = (has_resolution(name)\n-\t\t\t  ? cutoff_resolve : cutoff_noresolve);\n-\t\tif (then < now - cutoff * 86400)\n-\t\t\tpath_list_append(name, &to_remove);\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\tif (write_in_full(1, ptr[i].ptr, ptr[i].size) != ptr[i].size)\n-\t\t\treturn -1;\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-\tmemset(&xecfg, 0, sizeof(xecfg));\n-\txecfg.ctxlen = 3;\n-\tecb.outf = outf;\n-\txdi_diff(&minus, &plus, &xpp, &xecfg, &ecb);\n-\n-\tfree(minus.ptr);\n-\tfree(plus.ptr);\n-\treturn 0;\n-}\n-\n static struct lock_file index_lock;\n \n static int update_paths(struct path_list *update)\n@@ -378,11 +295,7 @@ static int do_plain_rerere(struct path_list *rr, int fd)\n \n static int git_rerere_config(const char *var, const char *value, void *cb)\n {\n-\tif (!strcmp(var, \"gc.rerereresolved\"))\n-\t\tcutoff_resolve = git_config_int(var, value);\n-\telse if (!strcmp(var, \"gc.rerereunresolved\"))\n-\t\tcutoff_noresolve = git_config_int(var, value);\n-\telse if (!strcmp(var, \"rerere.enabled\"))\n+\tif (!strcmp(var, \"rerere.enabled\"))\n \t\trerere_enabled = git_config_bool(var, value);\n \telse if (!strcmp(var, \"rerere.autoupdate\"))\n \t\trerere_autoupdate = git_config_bool(var, value);\n@@ -411,7 +324,7 @@ static int is_rerere_enabled(void)\n \treturn 1;\n }\n \n-static int setup_rerere(struct path_list *merge_rr)\n+int setup_rerere(struct path_list *merge_rr)\n {\n \tint fd;\n \n@@ -435,39 +348,3 @@ int rerere(void)\n \t\treturn 0;\n \treturn do_plain_rerere(&merge_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;\n-\n-\tfd = setup_rerere(&merge_rr);\n-\tif (fd < 0)\n-\t\treturn 0;\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 (!has_resolution(name))\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-}\ndiff --git a/rerere.h b/rerere.h\nnew file mode 100644\nindex 0000000..35b0fa8\n--- /dev/null\n+++ b/rerere.h\n@@ -0,0 +1,9 @@\n+#ifndef RERERE_H\n+#define RERERE_H\n+\n+#include \"path-list.h\"\n+\n+extern int setup_rerere(struct path_list *);\n+extern int rerere(void);\n+\n+#endif\n-- \n1.5.6\n"},{"id":"82741","messageId":"7v4p6ylxtj.fsf@gitster.siamese.dyndns.org","threadId":"14360","inReplyTo":"alpine.DEB.1.00.0807091356050.5277@eeepc-johanness","subject":"Re: [PATCH] rerere: Separate libgit and builtin functions","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-07-09T18:25:12Z","receivedAt":"2008-07-09T18:25:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> On Wed, 9 Jul 2008, Stephan Beyer wrote:\n>\n>>  Makefile                     |    2 +\n>>  builtin-rerere.c             |  324 +++---------------------------------------\n>>  builtin-rerere.c => rerere.c |  133 +-----------------\n>>  rerere.h                     |    9 ++\n>>  4 files changed, 32 insertions(+), 436 deletions(-)\n>\n> Heh, that sounds nice!  Deleting way more lines than adding!  :-)\n>\n>>  copy builtin-rerere.c => rerere.c (66%)\n>\n> Oh, well :-)\n\nWe may want to fix this, though.  I haven't verified by looking at the\ndiffstat summary code, but what I think is happening is that the bogus 436\nis the result of counting the lines removed from builtin-rerere.c (that\nlost many of its lines to rerere.c), and also counting the lines that were\nremove from rerere.c pretending as if rerere.c had full contents of\nbuiltin-rerere.c initially (i.e., counting the lines that were _not_ moved\nto rerere.c).\n\nIt may just be the matter of subtracting the size of the preimage of\ncopied files (in this case, builtin-rerere.c that was used to initialize\nrerere.c) from the grand total of deleted lines.  I dunno.\n"}]}