{"thread":{"id":"21698","subject":"[PATCH/RFC 1/3] rerere: keep a list of resolved files in MERGE_RR","startedAt":"2009-11-21T18:58:40Z","lastAt":"2009-11-24T23:40:48Z","messageCount":7,"participants":["Johannes Sixt","Junio C Hamano","Nanako Shiraishi"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"128074","messageId":"200911211958.40872.j6t@kdbg.org","threadId":"21698","inReplyTo":null,"subject":"[PATCH/RFC 0/3] git rerere unresolve file","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2009-11-21T18:58:40Z","receivedAt":"2009-11-21T18:58:40Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"When you commit an incorrect conflict resolution, git rerere gets in the way: \nrerere clear only clears cache entries of not yet resolved conflicts. But \nthere is no other way to remove an incorrect resolution short of purging the \nwhole rr-cache.\n\nThis series implements 'git rerere unresolve' that removes a recorded \nresolution.\n\nThis is RFC for two reasons:\n\n- It changes the contents of MERGE_RR in a way that *may* interfer in unwanted \nways with older versions that do not understand unresolve.\n\n- rerere unresolve also restores the conflict regions. I'm not sure whether \nthis is good because there is 'git checkout --conflict=merge file' that does \nit in a better way. See the commit message of patch 3.\n\nIf rerere unresolve does *not* restore conflict regions (and record the result \nas preimage right away) we are facing a problem: When the resolution is \ncommitted, the postimage has differences from the preimage that are not \nrelated to the conflict. This may inhibit reusing the resolution later when \nthe file has new changes.\n\nJohannes Sixt (3):\n  rerere: keep a list of resolved files in MERGE_RR\n  rerere: make recording of the preimage reusable\n  git rerere unresolve file...\n\n Documentation/git-rerere.txt |   10 +++-\n builtin-rerere.c             |   13 +++-\n rerere.c                     |  153 ++++++++++++++++++++++++++++++++++-------\n rerere.h                     |    4 +-\n 4 files changed, 148 insertions(+), 32 deletions(-)\n"},{"id":"128071","messageId":"200911212000.19326.j6t@kdbg.org","threadId":"21698","inReplyTo":"200911211958.40872.j6t@kdbg.org","subject":"[PATCH/RFC 1/3] rerere: keep a list of resolved files in MERGE_RR","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2009-11-21T19:00:19Z","receivedAt":"2009-11-21T19:00:19Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Previously, MERGE_RR entries that rerere detected as resolved were removed\nfrom MERGE_RR so that it contained only entries for files that still had\nconflicts.\n\nThis changes the \"database\" format to also keep the entries about files\nfor which resolutions have been recorded. The purpose is to allow that\nresolved conflicts can be unresolved. To do that it is necessary to have\na mapping from the file name to the conflict hash.\n\nThe new format of MERGE_RR is:\n\n1. Entries about unresolved conflicts.\n2. An all-zeros SHA1 as boundary marker.\n3. Entries about resolved conflicts.\n\nSigned-off-by: Johannes Sixt <j6t@kdbg.org>\n---\n builtin-rerere.c |    4 +++-\n rerere.c         |   49 ++++++++++++++++++++++++++++++++++++++++---------\n rerere.h         |    2 +-\n 3 files changed, 44 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin-rerere.c b/builtin-rerere.c\nindex adfb7b5..275827d 100644\n--- a/builtin-rerere.c\n+++ b/builtin-rerere.c\n@@ -101,12 +101,13 @@ static int diff_two(const char *file1, const char *label1,\n int cmd_rerere(int argc, const char **argv, const char *prefix)\n {\n \tstruct string_list merge_rr = { NULL, 0, 0, 1 };\n+\tstruct string_list merge_rr_done = { 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+\tfd = setup_rerere(&merge_rr, &merge_rr_done);\n \tif (fd < 0)\n \t\treturn 0;\n \n@@ -132,5 +133,6 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)\n \t\tusage(git_rerere_usage);\n \n \tstring_list_clear(&merge_rr, 1);\n+\tstring_list_clear(&merge_rr_done, 1);\n \treturn 0;\n }\ndiff --git a/rerere.c b/rerere.c\nindex 29f95f6..84d0bb7 100644\n--- a/rerere.c\n+++ b/rerere.c\n@@ -23,8 +23,20 @@ int has_rerere_resolution(const char *hex)\n \treturn !stat(rerere_path(hex, \"postimage\"), &st);\n }\n \n-static void read_rr(struct string_list *rr)\n+/*\n+ * If MERGE_RR is read and rewritten by an old version, the section bound\n+ * would not be preserved, and file names from both sections would be\n+ * interleaved. We must make sure that the section marker does not end up\n+ * in an arbitrary position. In particular, the safest result is that all\n+ * paths are now in the first (\"not done\") section. Therefore, we choose\n+ * a file name that sorts last (for all practical purposes).\n+ */\n+static const char section_mark[] =\n+\t\"0000000000000000000000000000000000000000\\t\\377\\377\\377\\377\";\n+\n+static void read_rr(struct string_list *rr, struct string_list *rr_done)\n {\n+\tstruct string_list *section = rr;\n \tunsigned char sha1[20];\n \tchar buf[PATH_MAX];\n \tFILE *in = fopen(merge_rr_path, \"r\");\n@@ -43,14 +55,19 @@ static void read_rr(struct string_list *rr)\n \t\t\t; /* do nothing */\n \t\tif (i == sizeof(buf))\n \t\t\tdie(\"filename too long\");\n-\t\tstring_list_insert(buf, rr)->util = name;\n+\t\tif (prefixcmp(section_mark, name)) {\n+\t\t\tstring_list_insert(buf, section)->util = name;\n+\t\t} else {\n+\t\t\tfree(name);\n+\t\t\tsection = rr_done;\n+\t\t}\n \t}\n \tfclose(in);\n }\n \n static struct lock_file write_lock;\n \n-static int write_rr(struct string_list *rr, int out_fd)\n+static void write_rr_section(struct string_list *rr, int out_fd)\n {\n \tint i;\n \tfor (i = 0; i < rr->nr; i++) {\n@@ -65,6 +82,17 @@ static int write_rr(struct string_list *rr, int out_fd)\n \t\t    write_in_full(out_fd, path, length) != length)\n \t\t\tdie(\"unable to write rerere record\");\n \t}\n+}\n+\n+static int write_rr(struct string_list *rr,\n+\t\t    struct string_list *rr_done, int out_fd)\n+{\n+\tint length = strlen(section_mark)+1;\n+\n+\twrite_rr_section(rr, out_fd);\n+\tif (write_in_full(out_fd, section_mark, length) != length)\n+\t\tdie(\"unable to write rerere record\");\n+\twrite_rr_section(rr_done, out_fd);\n \tif (commit_lock_file(&write_lock) != 0)\n \t\tdie(\"unable to write rerere record\");\n \treturn 0;\n@@ -263,7 +291,8 @@ static int update_paths(struct string_list *update)\n \treturn status;\n }\n \n-static int do_plain_rerere(struct string_list *rr, int fd)\n+static int do_plain_rerere(struct string_list *rr,\n+\t\t\t   struct string_list *rr_done, int fd)\n {\n \tstruct string_list conflict = { NULL, 0, 0, 1 };\n \tstruct string_list update = { NULL, 0, 0, 1 };\n@@ -328,13 +357,14 @@ static int do_plain_rerere(struct string_list *rr, int fd)\n \t\tfprintf(stderr, \"Recorded resolution for '%s'.\\n\", path);\n \t\tcopy_file(rerere_path(name, \"postimage\"), path, 0666);\n \tmark_resolved:\n+\t\tstring_list_insert(path, rr_done)->util = rr->items[i].util;\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+\treturn write_rr(rr, rr_done, fd);\n }\n \n static int git_rerere_config(const char *var, const char *value, void *cb)\n@@ -367,7 +397,7 @@ static int is_rerere_enabled(void)\n \treturn 1;\n }\n \n-int setup_rerere(struct string_list *merge_rr)\n+int setup_rerere(struct string_list *merge_rr, struct string_list *merge_rr_done)\n {\n \tint fd;\n \n@@ -378,17 +408,18 @@ int setup_rerere(struct string_list *merge_rr)\n \tmerge_rr_path = git_pathdup(\"MERGE_RR\");\n \tfd = hold_lock_file_for_update(&write_lock, merge_rr_path,\n \t\t\t\t       LOCK_DIE_ON_ERROR);\n-\tread_rr(merge_rr);\n+\tread_rr(merge_rr, merge_rr_done);\n \treturn fd;\n }\n \n int rerere(void)\n {\n \tstruct string_list merge_rr = { NULL, 0, 0, 1 };\n+\tstruct string_list merge_rr_done = { NULL, 0, 0, 1 };\n \tint fd;\n \n-\tfd = setup_rerere(&merge_rr);\n+\tfd = setup_rerere(&merge_rr, &merge_rr_done);\n \tif (fd < 0)\n \t\treturn 0;\n-\treturn do_plain_rerere(&merge_rr, fd);\n+\treturn do_plain_rerere(&merge_rr, &merge_rr_done, fd);\n }\ndiff --git a/rerere.h b/rerere.h\nindex 13313f3..9bb2f13 100644\n--- a/rerere.h\n+++ b/rerere.h\n@@ -3,7 +3,7 @@\n \n #include \"string-list.h\"\n \n-extern int setup_rerere(struct string_list *);\n+extern int setup_rerere(struct string_list *, struct string_list *);\n extern int rerere(void);\n extern const char *rerere_path(const char *hex, const char *file);\n extern int has_rerere_resolution(const char *hex);\n-- \n1.6.5.2.182.ge039a\n"},{"id":"128079","messageId":"200911212001.06207.j6t@kdbg.org","threadId":"21698","inReplyTo":"200911212000.19326.j6t@kdbg.org","subject":"[PATCH/RFC 2/3] rerere: make recording of the preimage reusable","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2009-11-21T19:01:06Z","receivedAt":"2009-11-21T19:01:06Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"This factors the code that computes the conflict hash and records the\npreimage into a helper function.\n\nSigned-off-by: Johannes Sixt <j6t@kdbg.org>\n---\n rerere.c |   30 ++++++++++++++++++++----------\n 1 files changed, 20 insertions(+), 10 deletions(-)\n\ndiff --git a/rerere.c b/rerere.c\nindex 84d0bb7..11fef88 100644\n--- a/rerere.c\n+++ b/rerere.c\n@@ -229,6 +229,25 @@ static int find_conflict(struct string_list *conflict)\n \treturn 0;\n }\n \n+static int record_preimage(struct string_list *rr, const char *path, int force)\n+{\n+\tunsigned char sha1[20];\n+\tchar *hex;\n+\tint ret;\n+\n+\tret = handle_file(path, sha1, NULL);\n+\tif (ret < 1)\n+\t\treturn -1;\n+\n+\thex = xstrdup(sha1_to_hex(sha1));\n+\tstring_list_insert(path, rr)->util = hex;\n+\tif (mkdir(git_path(\"rr-cache/%s\", hex), 0755) && !force)\n+\t\treturn -1;\n+\n+\thandle_file(path, NULL, rerere_path(hex, \"preimage\"));\n+\treturn 0;\n+}\n+\n static int merge(const char *name, const char *path)\n {\n \tint ret;\n@@ -310,17 +329,8 @@ static int do_plain_rerere(struct string_list *rr,\n \tfor (i = 0; i < conflict.nr; i++) {\n \t\tconst char *path = conflict.items[i].string;\n \t\tif (!string_list_has_string(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\tstring_list_insert(path, rr)->util = hex;\n-\t\t\tif (mkdir(git_path(\"rr-cache/%s\", hex), 0755))\n+\t\t\tif (record_preimage(rr, path, 0))\n \t\t\t\tcontinue;\n-\t\t\thandle_file(path, NULL, rerere_path(hex, \"preimage\"));\n \t\t\tfprintf(stderr, \"Recorded preimage for '%s'\\n\", path);\n \t\t}\n \t}\n-- \n1.6.5.2.182.ge039a\n"},{"id":"128081","messageId":"200911212002.47757.j6t@kdbg.org","threadId":"21698","inReplyTo":"200911212001.06207.j6t@kdbg.org","subject":"[PATCH/RFC 3/3] git rerere unresolve file...","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2009-11-21T19:02:47Z","receivedAt":"2009-11-21T19:02:47Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"There was no way to remove a recorded resolution short of removing the\nentire .git/rr-cache. This gets in the way when an incorrect resolution\nwas recorded.\n\nThis implements the subcommand 'git rerere unresolve' that selectively\nremoves the recorded resolution of the specified files. It also reverts\nthe resolution so that the files again have conflict markers. However,\nthese unresolved conflict markers not necessarily reflect \"ours\" and\n\"theirs\" correctly because the preimage that was stored in the cache has\nthe conflicted sides in a canonical order.\n\nIn handle_file(), the checks for the beginning and end of a conflict\nregion have to be loosened slightly -- there can be any whitespace,\nincluding a LF, not just a blank -- because after the conflict regions are\nrestored, there are no trailing blanks and a subsequent 'git rerere' would\nnot recognize them anymore.\n\n'git checkout --conflict=merge path...' can restore the conflicted file\nas well, but it does not remove the recorded resolution.\n\nSigned-off-by: Johannes Sixt <j6t@kdbg.org>\n---\n Documentation/git-rerere.txt |   10 +++++-\n builtin-rerere.c             |    9 +++--\n rerere.c                     |   74 ++++++++++++++++++++++++++++++++++++++----\n rerere.h                     |    2 +\n 4 files changed, 84 insertions(+), 11 deletions(-)\n\ndiff --git a/Documentation/git-rerere.txt b/Documentation/git-rerere.txt\nindex 7dd515b..c9bf3f1 100644\n--- a/Documentation/git-rerere.txt\n+++ b/Documentation/git-rerere.txt\n@@ -7,7 +7,7 @@ git-rerere - Reuse recorded resolution of conflicted merges\n \n SYNOPSIS\n --------\n-'git rerere' ['clear'|'diff'|'status'|'gc']\n+'git rerere' ['clear'|'diff'|'status'|'unresolve file...'|'gc']\n \n DESCRIPTION\n -----------\n@@ -52,6 +52,14 @@ conflicts.  Additional arguments are passed directly to the system\n Like 'diff', but this only prints the filenames that will be tracked\n for resolutions.\n \n+'unresolve'::\n+\n+Removes the resolution that was recorded for the specified files.\n+The conflict sections are restored as well, although the direction of\n+the \"ours\" and \"theirs\" sections may be inverted.\n+You can use 'git checkout --conflict=merge file' to restore the\n+original conflict markers in the correct direction.\n+\n 'gc'::\n \n This prunes records of conflicted merges that\ndiff --git a/builtin-rerere.c b/builtin-rerere.c\nindex 275827d..5d3a3a5 100644\n--- a/builtin-rerere.c\n+++ b/builtin-rerere.c\n@@ -7,7 +7,7 @@\n #include \"xdiff-interface.h\"\n \n static const char git_rerere_usage[] =\n-\"git rerere [clear | status | diff | gc]\";\n+\"git rerere [clear | status | diff | unresolve file... | gc]\";\n \n /* these values are days */\n static int cutoff_noresolve = 15;\n@@ -102,7 +102,7 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)\n {\n \tstruct string_list merge_rr = { NULL, 0, 0, 1 };\n \tstruct string_list merge_rr_done = { NULL, 0, 0, 1 };\n-\tint i, fd;\n+\tint i, fd, ret = 0;\n \n \tif (argc < 2)\n \t\treturn rerere();\n@@ -129,10 +129,13 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)\n \t\t\tconst char *name = (const char *)merge_rr.items[i].util;\n \t\t\tdiff_two(rerere_path(name, \"preimage\"), path, path, path);\n \t\t}\n+\telse if (!strcmp(argv[1], \"unresolve\"))\n+\t\tret = unresolve_rerere(&merge_rr, &merge_rr_done,\n+\t\t\t\t       get_pathspec(prefix, &argv[2]), fd);\n \telse\n \t\tusage(git_rerere_usage);\n \n \tstring_list_clear(&merge_rr, 1);\n \tstring_list_clear(&merge_rr_done, 1);\n-\treturn 0;\n+\treturn ret;\n }\ndiff --git a/rerere.c b/rerere.c\nindex 11fef88..b31008d 100644\n--- a/rerere.c\n+++ b/rerere.c\n@@ -140,7 +140,7 @@ static int handle_file(const char *path,\n \t\tgit_SHA1_Init(&ctx);\n \n \twhile (fgets(buf, sizeof(buf), f)) {\n-\t\tif (!prefixcmp(buf, \"<<<<<<< \")) {\n+\t\tif (!prefixcmp(buf, \"<<<<<<<\") && isspace(buf[7])) {\n \t\t\tif (hunk != RR_CONTEXT)\n \t\t\t\tgoto bad;\n \t\t\thunk = RR_SIDE_1;\n@@ -152,7 +152,7 @@ static int handle_file(const char *path,\n \t\t\tif (hunk != RR_SIDE_1 && hunk != RR_ORIGINAL)\n \t\t\t\tgoto bad;\n \t\t\thunk = RR_SIDE_2;\n-\t\t} else if (!prefixcmp(buf, \">>>>>>> \")) {\n+\t\t} else if (!prefixcmp(buf, \">>>>>>>\") && isspace(buf[7])) {\n \t\t\tif (hunk != RR_SIDE_2)\n \t\t\t\tgoto bad;\n \t\t\tif (strbuf_cmp(&one, &two) > 0)\n@@ -248,23 +248,29 @@ static int record_preimage(struct string_list *rr, const char *path, int \nforce)\n \treturn 0;\n }\n \n-static int merge(const char *name, const char *path)\n+#define RESOLVE 0\n+#define UNRESOLVE 1\n+#define UNRESOLVE_CHECK 2\n+\n+static int merge(const char *name, const char *path, int mode)\n {\n \tint ret;\n \tmmfile_t cur, base, other;\n \tmmbuffer_t result = {NULL, 0};\n \txpparam_t xpp = {XDF_NEED_MINIMAL};\n+\tconst char *basename = mode == RESOLVE ? \"preimage\" : \"postimage\";\n+\tconst char *othername = mode == RESOLVE ? \"postimage\" : \"preimage\";\n \n \tif (handle_file(path, NULL, rerere_path(name, \"thisimage\")) < 0)\n \t\treturn 1;\n \n \tif (read_mmfile(&cur, rerere_path(name, \"thisimage\")) ||\n-\t\t\tread_mmfile(&base, rerere_path(name, \"preimage\")) ||\n-\t\t\tread_mmfile(&other, rerere_path(name, \"postimage\")))\n+\t\t\tread_mmfile(&base, rerere_path(name, basename)) ||\n+\t\t\tread_mmfile(&other, rerere_path(name, othername)))\n \t\treturn 1;\n \tret = xdl_merge(&base, &cur, \"\", &other, \"\",\n \t\t\t&xpp, XDL_MERGE_ZEALOUS, &result);\n-\tif (!ret) {\n+\tif (!ret && mode != UNRESOLVE_CHECK) {\n \t\tFILE *f = fopen(path, \"w\");\n \t\tif (!f)\n \t\t\treturn error(\"Could not open %s: %s\", path,\n@@ -347,7 +353,7 @@ static int do_plain_rerere(struct string_list *rr,\n \t\tconst char *name = (const char *)rr->items[i].util;\n \n \t\tif (has_rerere_resolution(name)) {\n-\t\t\tif (!merge(name, path)) {\n+\t\t\tif (!merge(name, path, RESOLVE)) {\n \t\t\t\tif (rerere_autoupdate)\n \t\t\t\t\tstring_list_insert(path, &update);\n \t\t\t\tfprintf(stderr,\n@@ -433,3 +439,57 @@ int rerere(void)\n \t\treturn 0;\n \treturn do_plain_rerere(&merge_rr, &merge_rr_done, fd);\n }\n+\n+static int unresolve_check(struct string_list *rr_done, const char *path)\n+{\n+\tstruct string_list_item *item = string_list_lookup(path, rr_done);\n+\n+\tif (!item) {\n+\t\twarning(\"not resolved using a previous resolution: %s\", path);\n+\t\treturn 0;\n+\t}\n+\tif (!has_rerere_resolution(item->util))\n+\t\treturn error(\"no resolution found for %s\", path);\n+\tif (merge(item->util, path, UNRESOLVE_CHECK))\n+\t\treturn error(\"cannot revert resolution of %s\", path);\n+\treturn 0;\n+}\n+\n+int unresolve_rerere_1(struct string_list *rr, struct string_list *rr_done,\n+\t\t       const char *path)\n+{\n+\tstruct string_list_item *item = string_list_lookup(path, rr_done);\n+\n+\tif (!item)\n+\t\treturn 0;\n+\tif (merge(item->util, path, UNRESOLVE))\n+\t\treturn -1;\n+\n+\tif (record_preimage(rr, path, 1))\n+\t\treturn error(\"no conflict markers found: %s\", path);\n+\n+\titem->util = NULL;\n+\tunlink_or_warn(rerere_path(item->util, \"postimage\"));\n+\treturn 0;\n+}\n+\n+int unresolve_rerere(struct string_list *rr, struct string_list *rr_done,\n+\t\t     const char **pathspec, int fd)\n+{\n+\tint i, ret = 0;\n+\n+\tif (!pathspec)\n+\t\treturn error(\"unresolve what?\");\n+\n+\tfor (i = 0; pathspec[i]; i++) {\n+\t\tret |= unresolve_check(rr_done, pathspec[i]);\n+\t}\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tfor (i = 0; !ret && pathspec[i]; i++) {\n+\t\tret = unresolve_rerere_1(rr, rr_done, pathspec[i]);\n+\t}\n+\tret |= write_rr(rr, rr_done, fd);\n+\treturn ret;\n+}\ndiff --git a/rerere.h b/rerere.h\nindex 9bb2f13..a0fa50f 100644\n--- a/rerere.h\n+++ b/rerere.h\n@@ -7,5 +7,7 @@ extern int setup_rerere(struct string_list *, struct string_list *);\n extern int rerere(void);\n extern const char *rerere_path(const char *hex, const char *file);\n extern int has_rerere_resolution(const char *hex);\n+extern int unresolve_rerere(struct string_list *, struct string_list *,\n+\tconst char **, int);\n \n #endif\n-- \n1.6.5.2.182.ge039a\n"},{"id":"128089","messageId":"7v6393mfqz.fsf@alter.siamese.dyndns.org","threadId":"21698","inReplyTo":"200911211958.40872.j6t@kdbg.org","subject":"Re: [PATCH/RFC 0/3] git rerere unresolve file","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-11-22T02:53:56Z","receivedAt":"2009-11-22T02:53:56Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Sixt <j6t@kdbg.org> writes:\n\n> ... But \n> there is no other way to remove an incorrect resolution short of purging the \n> whole rr-cache.\n\nNo, no no, no.  You do not have to.\n\nAs long as you can find out the <preimage,postimage> pair, iow, the\nsubdirectory used by .git/rr-cache/, you can remove only \"postimage\" from\nthere and redo your merge.  \"ls -t1 .git/rr-cache/*/thisimage | head\"\nwould be one way to manually find out which one it is.\n\nFor example, let's take this merge as an example.\n\ncommit 697eb20061dfa00838df32ac24c414dfb4a1f920\nMerge: bba637b 46b77a6\nAuthor: Junio C Hamano <gitster@pobox.com>\n\n    Merge branch 'jk/1.7.0-status' into jch\n\nYou may not have rerere entries for this, so follow me along for a while.\n\n $ git checkout bba637b\n $ git merge 46b77a6\n\nThis will have a conflict.  We are priming your rerere database, so we\ncheat (notice the last dot \".\").\n\n $ git checkout 697eb200 -- .\n $ git rerere\n\nNow reset it away, and redo the merge\n\n $ git reset --hard\n $ git checkout bba637b\n $ git merge 46b77a6\n\nIt will autoresolve the way I resolved in 697eb200\n\nSuppose you do not like the resolution; you can do:\n\n    $ git checkout --conflict=merge Documentation/git-commit.txt\n    $ git rerere\n\nto force updating \"thisimage\".  The newest one is the entry you are\nlooking for.\n\n    $ ls -1t .git/rr-cache/*/thisimage | head -n 1\n    .git/rr-cache/02aac459b0c777f92a8ca6f1e449e6760d366c20/thisimage\n\nI can remove \"postimage\" from the directory, recreate conflict again,\nrun rerere to remember it anew, resolve and have rerere remember the new\nresolution.\n\nA tool support to compute the conflict hash would be part of a nice\nsolution.\n\n    $ git checkout --conflict=merge Documentation/git-commit.txt\n    $ git rerere hash\n    02aac459b0c777f92a8ca6f1e449e6760d366c20 Documentation/git-commit.txt    \n\nInstead of giving \"hash\" subcommand and having the user to remove\npostimage from the directory, the tool can remove it, of course.\n\n    $ git checkout --conflict=merge Documentation/git-commit.txt\n    $ git rerere forget Documentation/git-commit.txt\n    02aac459b0c777f92a8ca6f1e449e6760d366c20 Documentation/git-commit.txt    \n    $ edit Documentation/git-commit.txt ;# come up with a better resolution \n    $ git rerere ;# record it\n\nThe attached are rough sketch of such \"hash/forget\" subcommands.\n\nI however think the best user experience would go like this:\n\n * Run \"git merge\"; rerere replays an earlier resolution.\n\n   $ git merge ...\n\n   User decides that it is not a desirable one.\n\n * User fixes it and creates a better resolution in the work tree.  The\n   user may fix-up the autoresolution or the user may first do \"checkout\n   --conflict=merge\" and resolves the conflict from scratch.\n\n   It does not matter how the updated resolution is prepared in the work\n   tree.\n\n * Then the user tells rerere that \"this is the corrected resolution\",\n   perhaps\n\n   $ git rerere update Documentation/git-commit.txt\n\n   This will\n\n   - Internally recompute the original conflicted state, i.e. run\n     \"checkout --conflict=merge Documentation/git-commit.txt\" in-core;\n\n   - feed it to rerere.c::handle_file() to learn the conflict hash;\n\n   - read the user's updated resolution from the work tree, and update\n     \"postimage\" with it.\n\nThere is no need to change MERGE_RR at all if you did it the way outlined\nin the above.\n\nMy \"hash\" would be more-or-less a useless command.  It still might be\nuseful as a plumbing command, but it is mostly for debugging.\n\nThe \"forget\" subcommand may be useful, but the real implementation should\nbe the first half of the \"update\", iow, recreate conflict in-core in order\nto compute the conflict hash, and drop existing \"postimage\", without\nreplacing it from the work tree.  We should leave it up to the user using\n\"checkout --conflict\" to reproduce the conflict in the work tree.\n\n builtin-rerere.c |    4 +++\n rerere.c         |   58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++\n rerere.h         |    2 +\n 3 files changed, 64 insertions(+), 0 deletions(-)\n\ndiff --git a/builtin-rerere.c b/builtin-rerere.c\nindex 343d6cd..8865313 100644\n--- a/builtin-rerere.c\n+++ b/builtin-rerere.c\n@@ -108,6 +108,10 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)\n \n \tif (!strcmp(argv[1], \"-h\"))\n \t\tusage(git_rerere_usage);\n+\telse if (!strcmp(argv[1], \"hash\"))\n+\t\treturn rerere_report_hash();\n+\telse if (!strcmp(argv[1], \"forget\"))\n+\t\treturn rerere_forget(argv + 2);\n \n \tfd = setup_rerere(&merge_rr);\n \tif (fd < 0)\ndiff --git a/rerere.c b/rerere.c\nindex 29f95f6..4899219 100644\n--- a/rerere.c\n+++ b/rerere.c\n@@ -392,3 +392,61 @@ int rerere(void)\n \t\treturn 0;\n \treturn do_plain_rerere(&merge_rr, fd);\n }\n+\n+int rerere_forget(const char **path)\n+{\n+\tstruct string_list conflict = { NULL, 0, 0, 1 };\n+\tint i;\n+\n+\tfind_conflict(&conflict);\n+\n+\t/*\n+\t * Note note note.  I am being lazy here.  The loop should not\n+\t * iterate over \"path\" like this.\n+\t *\n+\t * It should instead iterate over conflict entries, using \"path\"\n+\t * as pathspec to filter, so that you can say \"I am done with\n+\t * all the conflict in Documentation/ area\".\n+\t */\n+\tfor (i = 0; path[i]; i++) { \n+\t\tunsigned char sha1[20];\n+\t\tconst char *postimage;\n+\t\tint ret;\n+\n+\t\tif (!string_list_has_string(&conflict, path[i])) {\n+\t\t\terror(\"No such conflicted path %s\\n\", path[i]);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tret = handle_file(path[i], sha1, NULL);\n+\t\tif (ret < 1) {\n+\t\t\terror(\"No conflict in work tree %s\\n\", path[i]);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tpostimage = rerere_path(sha1_to_hex(sha1), \"postimage\");\n+\t\tif (!unlink(postimage))\n+\t\t\tfprintf(stderr, \"forgot resolution for %s\\n\", path[i]);\n+\t\telse if (errno == ENOENT)\n+\t\t\terror(\"no remembered resolution for %s\", path[i]);\n+\t\telse\n+\t\t\terror(\"cannot unlink %s: %s\", postimage, strerror(errno));\n+\t}\n+\treturn 0;\n+}\n+\n+int rerere_report_hash(void)\n+{\n+\tstruct string_list conflict = { NULL, 0, 0, 1 };\n+\tint i;\n+\n+\tfind_conflict(&conflict);\n+\tfor (i = 0; i < conflict.nr; i++) {\n+\t\tconst char *path = conflict.items[i].string;\n+\t\tunsigned char sha1[20];\n+\t\tint ret;\n+\t\tret = handle_file(path, sha1, NULL);\n+\t\tif (ret < 1)\n+\t\t\tcontinue;\n+\t\tprintf(\"%s %s\\n\", sha1_to_hex(sha1), path);\n+\t}\n+\treturn 0;\n+}\ndiff --git a/rerere.h b/rerere.h\nindex 13313f3..a10dea9 100644\n--- a/rerere.h\n+++ b/rerere.h\n@@ -7,5 +7,7 @@ extern int setup_rerere(struct string_list *);\n extern int rerere(void);\n extern const char *rerere_path(const char *hex, const char *file);\n extern int has_rerere_resolution(const char *hex);\n+extern int rerere_report_hash(void);\n+extern int rerere_forget(const char **path);\n \n #endif\n"},{"id":"128102","messageId":"200911221519.55658.j6t@kdbg.org","threadId":"21698","inReplyTo":"7v6393mfqz.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH/RFC 0/3] git rerere unresolve file","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2009-11-22T14:19:54Z","receivedAt":"2009-11-22T14:19:54Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"On Sonntag, 22. November 2009, Junio C Hamano wrote:\n> Johannes Sixt <j6t@kdbg.org> writes:\n> > ... But\n> > there is no other way to remove an incorrect resolution short of purging\n> > the whole rr-cache.\n>\n> No, no no, no.  You do not have to.\n\nOh, yeah, I know. But even for people who know how to drive plumbing commands, \nthis:\n\n> \"ls -t1 .git/rr-cache/*/thisimage | head\"\n> would be one way to manually find out which one it is.\n\ndoes not count as \"you can\" in my book. ;) It assumes that there are at most a \nhandful conflicted files. In my case, for example, I have to fix a merge \nwhere there are ~100 conflicted files.\n\n>  * Then the user tells rerere that \"this is the corrected resolution\",\n>    perhaps\n>\n>    $ git rerere update Documentation/git-commit.txt\n>\n>    This will\n>\n>    - Internally recompute the original conflicted state, i.e. run\n>      \"checkout --conflict=merge Documentation/git-commit.txt\" in-core;\n>\n>    - feed it to rerere.c::handle_file() to learn the conflict hash;\n>\n>    - read the user's updated resolution from the work tree, and update\n>      \"postimage\" with it.\n>\n> ...\n>\n> The \"forget\" subcommand may be useful, but the real implementation should\n> be the first half of the \"update\", iow, recreate conflict in-core in order\n> to compute the conflict hash, and drop existing \"postimage\", without\n> replacing it from the work tree.  We should leave it up to the user using\n> \"checkout --conflict\" to reproduce the conflict in the work tree.\n\nThank you for your elaborate feedback and the guidelines about how to move \nforward.\n\nI actually think that 'forget' should be the primary subcommand. Because after \nthe postimage was purged, the next implicit or explicit 'git rerere' will \nrecord the resolution anyway. I'll see what I can do.\n\n-- Hannes\n"},{"id":"128268","messageId":"20091125084048.6117@nanako3.lavabit.com","threadId":"21698","inReplyTo":"200911221519.55658.j6t@kdbg.org","subject":"Re: [PATCH/RFC 0/3] git rerere unresolve file","fromName":"Nanako Shiraishi","fromEmail":"nanako3@lavabit.com","sentAt":"2009-11-24T23:40:48Z","receivedAt":"2009-11-24T23:40:48Z","isPatch":true,"sender":{"key":"nanako3@lavabit.com","avatar":"https://gravatar.com/avatar/3777b9e201c5883a62b1a6fdf7c53f2d712d1d80989146063ea861e33aad72a8?d=mp&s=160"},"body":"Quoting Johannes Sixt <j6t@kdbg.org>\n\n> On Sonntag, 22. November 2009, Junio C Hamano wrote:\n> [snip]\n>>  * Then the user tells rerere that \"this is the corrected resolution\",\n>>    perhaps\n>>\n>>    $ git rerere update Documentation/git-commit.txt\n>>\n>>    This will\n>>\n>>    - Internally recompute the original conflicted state, i.e. run\n>>      \"checkout --conflict=merge Documentation/git-commit.txt\" in-core;\n>>\n>>    - feed it to rerere.c::handle_file() to learn the conflict hash;\n>>\n>>    - read the user's updated resolution from the work tree, and update\n>>      \"postimage\" with it.\n>>\n>> ...\n>>\n>> The \"forget\" subcommand may be useful, but the real implementation should\n>> be the first half of the \"update\", iow, recreate conflict in-core in order\n>> to compute the conflict hash, and drop existing \"postimage\", without\n>> replacing it from the work tree.  We should leave it up to the user using\n>> \"checkout --conflict\" to reproduce the conflict in the work tree.\n>\n> Thank you for your elaborate feedback and the guidelines about how to move \n> forward.\n>\n> I actually think that 'forget' should be the primary subcommand. Because after \n> the postimage was purged, the next implicit or explicit 'git rerere' will \n> record the resolution anyway. I'll see what I can do.\n>\n> -- Hannes\n\nI think 'forget' should be the primary interface to this new feature too. If I mistakenly recorded an incorrect merge in the past and I'm trying to do a better job this time, I wouldn't be confident enough to be able to say 'update' to mark the attempt I made this time, and it may take more than one attempts for me to reach a good result. Running 'forget' once will let me retry until I run 'rerere' again.\n\n-- \nNanako Shiraishi\nhttp://ivory.ap.teacup.com/nanako3/\n"}]}