{"thread":{"id":"22054","subject":"[PATCH 7/9] rerere: remove silly 1024-byte line limit","startedAt":"2009-12-29T21:42:29Z","lastAt":"2010-01-11T21:05:54Z","messageCount":21,"participants":["Junio C Hamano","Johannes Sixt"],"isPatch":true,"patchVersion":1,"patchTotal":9},"messages":[{"id":"130490","messageId":"1262122958-9378-1-git-send-email-gitster@pobox.com","threadId":"22054","inReplyTo":"7v6393mfqz.fsf@alter.siamese.dyndns.org","subject":"[PATCH 0/9] Undoing conflict resolution","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-12-29T21:42:29Z","receivedAt":"2009-12-29T21:42:29Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This series resurrects the topic we discussed later November.\n\nI wrote a bit about this in http://gitster.livejournal.com/43665.html but\nas usual with my patches, they do come with minimum set of tests but it\ncontains no documentation updates.  The user visible changes are:\n\n - ls-files has a new option primarily for debugging;\n - update-index has a new option --clear-resolve-undo;\n - rerere has a new subcommand forget.\n\nJunio C Hamano (9):\n  builtin-merge.c: use standard active_cache macros\n  resolve-undo: record resolved conflicts in a new index extension\n    section\n  resolve-undo: basic tests\n  resolve-undo: allow plumbing to clear the information\n  resolve-undo: \"checkout -m path\" uses resolve-undo information\n  resolve-undo: teach \"update-index --unresolve\" to use resolve-undo\n    info\n  rerere: remove silly 1024-byte line limit\n  rerere: refactor rerere logic to make it independent from I/O\n  rerere forget path: forget recorded resolution\n\n Makefile                  |    2 +\n builtin-checkout.c        |    6 +\n builtin-ls-files.c        |   43 +++++++-\n builtin-merge.c           |    9 +-\n builtin-read-tree.c       |    2 +\n builtin-rerere.c          |    2 +\n builtin-update-index.c    |   18 +++-\n cache.h                   |    4 +\n read-cache.c              |   18 +++\n rerere.c                  |  259 +++++++++++++++++++++++++++++++++++++-------\n rerere.h                  |    1 +\n resolve-undo.c            |  176 ++++++++++++++++++++++++++++++\n resolve-undo.h            |   16 +++\n t/t2030-unresolve-info.sh |  140 ++++++++++++++++++++++++\n 14 files changed, 648 insertions(+), 48 deletions(-)\n create mode 100644 resolve-undo.c\n create mode 100644 resolve-undo.h\n create mode 100755 t/t2030-unresolve-info.sh\n"},{"id":"130488","messageId":"1262122958-9378-2-git-send-email-gitster@pobox.com","threadId":"22054","inReplyTo":"1262122958-9378-1-git-send-email-gitster@pobox.com","subject":"[PATCH 1/9] builtin-merge.c: use standard active_cache macros","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-12-29T21:42:30Z","receivedAt":"2009-12-29T21:42:30Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Instead of using the low-level index_state interface, use the bog standard\nactive_cache and active_nr macros to access the cache entries when using the\ndefault one.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-merge.c |    5 ++---\n 1 files changed, 2 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin-merge.c b/builtin-merge.c\nindex f1c84d7..6cb804b 100644\n--- a/builtin-merge.c\n+++ b/builtin-merge.c\n@@ -618,11 +618,10 @@ static void count_diff_files(struct diff_queue_struct *q,\n \n static int count_unmerged_entries(void)\n {\n-\tconst struct index_state *state = &the_index;\n \tint i, ret = 0;\n \n-\tfor (i = 0; i < state->cache_nr; i++)\n-\t\tif (ce_stage(state->cache[i]))\n+\tfor (i = 0; i < active_nr; i++)\n+\t\tif (ce_stage(active_cache[i]))\n \t\t\tret++;\n \n \treturn ret;\n-- \n1.6.6\n"},{"id":"130486","messageId":"1262122958-9378-3-git-send-email-gitster@pobox.com","threadId":"22054","inReplyTo":"1262122958-9378-1-git-send-email-gitster@pobox.com","subject":"[PATCH 2/9] resolve-undo: record resolved conflicts in a new index extension section","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-12-29T21:42:31Z","receivedAt":"2009-12-29T21:42:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"When resolving a conflict using \"git add\" to create a stage #0 entry, or\n\"git rm\" to remove entries at higher stages, remove_index_entry_at()\nfunction is eventually called to remove unmerged (i.e. higher stage)\nentries from the index.  Introduce a \"resolve_undo_info\" structure and\nkeep track of the removed cache entries, and save it in a new index\nextension section in the index_state.\n\nOperations like \"read-tree -m\", \"merge\", \"checkout [-m] <branch>\" and\n\"reset\" are signs that recorded information in the index is no longer\nnecessary.  The data is removed from the index extension when operations\nstart; they may leave conflicted entries in the index, and later user\nactions like \"git add\" will record their conflicted states afresh.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Makefile            |    2 +\n builtin-checkout.c  |    2 +\n builtin-merge.c     |    4 +-\n builtin-read-tree.c |    2 +\n cache.h             |    2 +\n read-cache.c        |   18 ++++++++\n resolve-undo.c      |  117 +++++++++++++++++++++++++++++++++++++++++++++++++++\n resolve-undo.h      |   14 ++++++\n 8 files changed, 160 insertions(+), 1 deletions(-)\n create mode 100644 resolve-undo.c\n create mode 100644 resolve-undo.h\n\ndiff --git a/Makefile b/Makefile\nindex 4a1e5bc..762898a 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -483,6 +483,7 @@ LIB_H += reflog-walk.h\n LIB_H += refs.h\n LIB_H += remote.h\n LIB_H += rerere.h\n+LIB_H += resolve-undo.h\n LIB_H += revision.h\n LIB_H += run-command.h\n LIB_H += sha1-lookup.h\n@@ -578,6 +579,7 @@ LIB_OBJS += refs.o\n LIB_OBJS += remote.o\n LIB_OBJS += replace_object.o\n LIB_OBJS += rerere.o\n+LIB_OBJS += resolve-undo.o\n LIB_OBJS += revision.o\n LIB_OBJS += run-command.o\n LIB_OBJS += server-info.o\ndiff --git a/builtin-checkout.c b/builtin-checkout.c\nindex 64f3a11..a0fe7a4 100644\n--- a/builtin-checkout.c\n+++ b/builtin-checkout.c\n@@ -17,6 +17,7 @@\n #include \"blob.h\"\n #include \"xdiff-interface.h\"\n #include \"ll-merge.h\"\n+#include \"resolve-undo.h\"\n \n static const char * const checkout_usage[] = {\n \t\"git checkout [options] <branch>\",\n@@ -370,6 +371,7 @@ static int merge_working_tree(struct checkout_opts *opts,\n \tif (read_cache_preload(NULL) < 0)\n \t\treturn error(\"corrupt index file\");\n \n+\tresolve_undo_clear();\n \tif (opts->force) {\n \t\tret = reset_tree(new->commit->tree, opts, 1);\n \t\tif (ret)\ndiff --git a/builtin-merge.c b/builtin-merge.c\nindex 6cb804b..6bc2f7a 100644\n--- a/builtin-merge.c\n+++ b/builtin-merge.c\n@@ -24,6 +24,7 @@\n #include \"rerere.h\"\n #include \"help.h\"\n #include \"merge-recursive.h\"\n+#include \"resolve-undo.h\"\n \n #define DEFAULT_TWOHEAD (1<<0)\n #define DEFAULT_OCTOPUS (1<<1)\n@@ -604,6 +605,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,\n \t\tdiscard_cache();\n \t\tif (read_cache() < 0)\n \t\t\tdie(\"failed to read the cache\");\n+\t\tresolve_undo_clear();\n \t\treturn ret;\n \t}\n }\n@@ -851,7 +853,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \tif (read_cache_unmerged())\n \t\tdie(\"You are in the middle of a conflicted merge.\"\n \t\t\t\t\" (index unmerged)\");\n-\n+\tresolve_undo_clear();\n \t/*\n \t * Check if we are _not_ on a detached HEAD, i.e. if there is a\n \t * current branch.\ndiff --git a/builtin-read-tree.c b/builtin-read-tree.c\nindex 2a3a32c..7d378b7 100644\n--- a/builtin-read-tree.c\n+++ b/builtin-read-tree.c\n@@ -13,6 +13,7 @@\n #include \"dir.h\"\n #include \"builtin.h\"\n #include \"parse-options.h\"\n+#include \"resolve-undo.h\"\n \n static int nr_trees;\n static struct tree *trees[MAX_UNPACK_TREES];\n@@ -122,6 +123,7 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)\n \t\t\tdie(\"You need to resolve your current index first\");\n \t\tstage = opts.merge = 1;\n \t}\n+\tresolve_undo_clear();\n \n \tfor (i = 0; i < argc; i++) {\n \t\tconst char *arg = argv[i];\ndiff --git a/cache.h b/cache.h\nindex bf468e5..310d9e6 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -282,6 +282,7 @@ static inline int ce_to_dtype(const struct cache_entry *ce)\n struct index_state {\n \tstruct cache_entry **cache;\n \tunsigned int cache_nr, cache_alloc, cache_changed;\n+\tstruct string_list *resolve_undo;\n \tstruct cache_tree *cache_tree;\n \tstruct cache_time timestamp;\n \tvoid *alloc;\n@@ -336,6 +337,7 @@ static inline void remove_name_hash(struct cache_entry *ce)\n #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))\n #define cache_name_exists(name, namelen, igncase) index_name_exists(&the_index, (name), (namelen), (igncase))\n #define cache_name_is_other(name, namelen) index_name_is_other(&the_index, (name), (namelen))\n+#define resolve_undo_clear() resolve_undo_clear_index(&the_index)\n #endif\n \n enum object_type {\ndiff --git a/read-cache.c b/read-cache.c\nindex 1bbaf1c..9e0fb04 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -14,6 +14,7 @@\n #include \"diffcore.h\"\n #include \"revision.h\"\n #include \"blob.h\"\n+#include \"resolve-undo.h\"\n \n /* Index extensions.\n  *\n@@ -26,6 +27,7 @@\n \n #define CACHE_EXT(s) ( (s[0]<<24)|(s[1]<<16)|(s[2]<<8)|(s[3]) )\n #define CACHE_EXT_TREE 0x54524545\t/* \"TREE\" */\n+#define CACHE_EXT_RESOLVE_UNDO 0x52455543 /* \"REUN\" */\n \n struct index_state the_index;\n \n@@ -449,6 +451,7 @@ int remove_index_entry_at(struct index_state *istate, int pos)\n {\n \tstruct cache_entry *ce = istate->cache[pos];\n \n+\trecord_resolve_undo(istate, ce);\n \tremove_name_hash(ce);\n \tistate->cache_changed = 1;\n \tistate->cache_nr--;\n@@ -1170,6 +1173,9 @@ static int read_index_extension(struct index_state *istate,\n \tcase CACHE_EXT_TREE:\n \t\tistate->cache_tree = cache_tree_read(data, sz);\n \t\tbreak;\n+\tcase CACHE_EXT_RESOLVE_UNDO:\n+\t\tistate->resolve_undo = resolve_undo_read(data, sz);\n+\t\tbreak;\n \tdefault:\n \t\tif (*ext < 'A' || 'Z' < *ext)\n \t\t\treturn error(\"index uses %.4s extension, which we do not understand\",\n@@ -1349,6 +1355,7 @@ int is_index_unborn(struct index_state *istate)\n \n int discard_index(struct index_state *istate)\n {\n+\tresolve_undo_clear_index(istate);\n \tistate->cache_nr = 0;\n \tistate->cache_changed = 0;\n \tistate->timestamp.sec = 0;\n@@ -1574,6 +1581,17 @@ int write_index(struct index_state *istate, int newfd)\n \t\tif (err)\n \t\t\treturn -1;\n \t}\n+\tif (istate->resolve_undo) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\n+\t\tresolve_undo_write(&sb, istate->resolve_undo);\n+\t\terr = write_index_ext_header(&c, newfd, CACHE_EXT_RESOLVE_UNDO,\n+\t\t\t\t\t     sb.len) < 0\n+\t\t\t|| ce_write(&c, newfd, sb.buf, sb.len) < 0;\n+\t\tstrbuf_release(&sb);\n+\t\tif (err)\n+\t\t\treturn -1;\n+\t}\n \n \tif (ce_flush(&c, newfd) || fstat(newfd, &st))\n \t\treturn -1;\ndiff --git a/resolve-undo.c b/resolve-undo.c\nnew file mode 100644\nindex 0000000..86e8547\n--- /dev/null\n+++ b/resolve-undo.c\n@@ -0,0 +1,117 @@\n+#include \"cache.h\"\n+#include \"resolve-undo.h\"\n+#include \"string-list.h\"\n+\n+/* The only error case is to run out of memory in string-list */\n+void record_resolve_undo(struct index_state *istate, struct cache_entry *ce)\n+{\n+\tstruct string_list_item *lost;\n+\tstruct resolve_undo_info *ui;\n+\tstruct string_list *resolve_undo;\n+\tint stage = ce_stage(ce);\n+\n+\tif (!stage)\n+\t\treturn;\n+\n+\tif (!istate->resolve_undo) {\n+\t\tresolve_undo = xcalloc(1, sizeof(*resolve_undo));\n+\t\tresolve_undo->strdup_strings = 1;\n+\t\tistate->resolve_undo = resolve_undo;\n+\t}\n+\tresolve_undo = istate->resolve_undo;\n+\tlost = string_list_insert(ce->name, resolve_undo);\n+\tif (!lost->util)\n+\t\tlost->util = xcalloc(1, sizeof(*ui));\n+\tui = lost->util;\n+\thashcpy(ui->sha1[stage - 1], ce->sha1);\n+\tui->mode[stage - 1] = ce->ce_mode;\n+}\n+\n+static int write_one(struct string_list_item *item, void *cbdata)\n+{\n+\tstruct strbuf *sb = cbdata;\n+\tstruct resolve_undo_info *ui = item->util;\n+\tint i;\n+\n+\tif (!ui)\n+\t\treturn 0;\n+\tstrbuf_addstr(sb, item->string);\n+\tstrbuf_addch(sb, 0);\n+\tfor (i = 0; i < 3; i++)\n+\t\tstrbuf_addf(sb, \"%o%c\", ui->mode[i], 0);\n+\tfor (i = 0; i < 3; i++) {\n+\t\tif (!ui->mode[i])\n+\t\t\tcontinue;\n+\t\tstrbuf_add(sb, ui->sha1[i], 20);\n+\t}\n+\treturn 0;\n+}\n+\n+void resolve_undo_write(struct strbuf *sb, struct string_list *resolve_undo)\n+{\n+\tfor_each_string_list(write_one, resolve_undo, sb);\n+}\n+\n+struct string_list *resolve_undo_read(void *data, unsigned long size)\n+{\n+\tstruct string_list *resolve_undo;\n+\tsize_t len;\n+\tchar *endptr;\n+\tint i;\n+\n+\tresolve_undo = xcalloc(1, sizeof(*resolve_undo));\n+\tresolve_undo->strdup_strings = 1;\n+\n+\twhile (size) {\n+\t\tstruct string_list_item *lost;\n+\t\tstruct resolve_undo_info *ui;\n+\n+\t\tlen = strlen(data) + 1;\n+\t\tif (size <= len)\n+\t\t\tgoto error;\n+\t\tlost = string_list_insert(data, resolve_undo);\n+\t\tif (!lost->util)\n+\t\t\tlost->util = xcalloc(1, sizeof(*ui));\n+\t\tui = lost->util;\n+\t\tsize -= len;\n+\t\tdata += len;\n+\n+\t\tfor (i = 0; i < 3; i++) {\n+\t\t\tui->mode[i] = strtoul(data, &endptr, 8);\n+\t\t\tif (!endptr || endptr == data || *endptr)\n+\t\t\t\tgoto error;\n+\t\t\tlen = (endptr + 1) - (char*)data;\n+\t\t\tif (size <= len)\n+\t\t\t\tgoto error;\n+\t\t\tsize -= len;\n+\t\t\tdata += len;\n+\t\t}\n+\n+\t\tfor (i = 0; i < 3; i++) {\n+\t\t\tif (!ui->mode[i])\n+\t\t\t\tcontinue;\n+\t\t\tif (size < 20)\n+\t\t\t\tgoto error;\n+\t\t\thashcpy(ui->sha1[i], data);\n+\t\t\tsize -= 20;\n+\t\t\tdata += 20;\n+\t\t}\n+\t}\n+\treturn resolve_undo;\n+\n+error:\n+\tstring_list_clear(resolve_undo, 1);\n+\terror(\"Index records invalid resolve-undo information\");\n+\treturn NULL;\n+}\n+\n+void resolve_undo_clear_index(struct index_state *istate)\n+{\n+\tstruct string_list *resolve_undo = istate->resolve_undo;\n+\tif (!resolve_undo)\n+\t\treturn;\n+\tstring_list_clear(resolve_undo, 1);\n+\tfree(resolve_undo);\n+\tistate->resolve_undo = NULL;\n+\tistate->cache_changed = 1;\n+}\ndiff --git a/resolve-undo.h b/resolve-undo.h\nnew file mode 100644\nindex 0000000..74194d0\n--- /dev/null\n+++ b/resolve-undo.h\n@@ -0,0 +1,14 @@\n+#ifndef RESOLVE_UNDO_H\n+#define RESOLVE_UNDO_H\n+\n+struct resolve_undo_info {\n+\tunsigned int mode[3];\n+\tunsigned char sha1[3][20];\n+};\n+\n+extern void record_resolve_undo(struct index_state *, struct cache_entry *);\n+extern void resolve_undo_write(struct strbuf *, struct string_list *);\n+extern struct string_list *resolve_undo_read(void *, unsigned long);\n+extern void resolve_undo_clear_index(struct index_state *);\n+\n+#endif\n-- \n1.6.6\n"},{"id":"130483","messageId":"1262122958-9378-4-git-send-email-gitster@pobox.com","threadId":"22054","inReplyTo":"1262122958-9378-1-git-send-email-gitster@pobox.com","subject":"[PATCH 3/9] resolve-undo: basic tests","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-12-29T21:42:32Z","receivedAt":"2009-12-29T21:42:32Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Make sure that resolving a failed merge with git add records\nthe conflicted state, committing the result keeps that state,\nand checking out another commit clears the state.\n\n\"git ls-files\" learns a new option --resolve-undo to show the\nrecorded information.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-ls-files.c        |   43 +++++++++++++++++++++-\n t/t2030-unresolve-info.sh |   88 +++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 130 insertions(+), 1 deletions(-)\n create mode 100755 t/t2030-unresolve-info.sh\n\ndiff --git a/builtin-ls-files.c b/builtin-ls-files.c\nindex c9a03e5..ef3a068 100644\n--- a/builtin-ls-files.c\n+++ b/builtin-ls-files.c\n@@ -11,6 +11,8 @@\n #include \"builtin.h\"\n #include \"tree.h\"\n #include \"parse-options.h\"\n+#include \"resolve-undo.h\"\n+#include \"string-list.h\"\n \n static int abbrev;\n static int show_deleted;\n@@ -18,6 +20,7 @@ static int show_cached;\n static int show_others;\n static int show_stage;\n static int show_unmerged;\n+static int show_resolve_undo;\n static int show_modified;\n static int show_killed;\n static int show_valid_bit;\n@@ -37,6 +40,7 @@ static const char *tag_removed = \"\";\n static const char *tag_other = \"\";\n static const char *tag_killed = \"\";\n static const char *tag_modified = \"\";\n+static const char *tag_resolve_undo = \"\";\n \n static void show_dir_entry(const char *tag, struct dir_entry *ent)\n {\n@@ -155,6 +159,38 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)\n \twrite_name_quoted(ce->name + offset, stdout, line_terminator);\n }\n \n+static int show_one_ru(struct string_list_item *item, void *cbdata)\n+{\n+\tint offset = prefix_offset;\n+\tconst char *path = item->string;\n+\tstruct resolve_undo_info *ui = item->util;\n+\tint i, len;\n+\n+\tlen = strlen(path);\n+\tif (len < prefix_len)\n+\t\treturn 0; /* outside of the prefix */\n+\tif (!match_pathspec(pathspec, path, len, prefix_len, ps_matched))\n+\t\treturn 0; /* uninterested */\n+\tfor (i = 0; i < 3; i++) {\n+\t\tif (!ui->mode[i])\n+\t\t\tcontinue;\n+\t\tprintf(\"%s%06o %s %d\\t\", tag_resolve_undo, ui->mode[i],\n+\t\t       abbrev\n+\t\t       ? find_unique_abbrev(ui->sha1[i], abbrev)\n+\t\t       : sha1_to_hex(ui->sha1[i]),\n+\t\t       i + 1);\n+\t\twrite_name_quoted(path + offset, stdout, line_terminator);\n+\t}\n+\treturn 0;\n+}\n+\n+static void show_ru_info(const char *prefix)\n+{\n+\tif (!the_index.resolve_undo)\n+\t\treturn;\n+\tfor_each_string_list(show_one_ru, the_index.resolve_undo, NULL);\n+}\n+\n static void show_files(struct dir_struct *dir, const char *prefix)\n {\n \tint i;\n@@ -454,6 +490,8 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)\n \t\t\tDIR_HIDE_EMPTY_DIRECTORIES),\n \t\tOPT_BOOLEAN('u', \"unmerged\", &show_unmerged,\n \t\t\t\"show unmerged files in the output\"),\n+\t\tOPT_BOOLEAN(0, \"resolve-undo\", &show_resolve_undo,\n+\t\t\t    \"show resolve-undo information\"),\n \t\t{ OPTION_CALLBACK, 'x', \"exclude\", &dir.exclude_list[EXC_CMDL], \"pattern\",\n \t\t\t\"skip files matching pattern\",\n \t\t\t0, option_parse_exclude },\n@@ -490,6 +528,7 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)\n \t\ttag_modified = \"C \";\n \t\ttag_other = \"? \";\n \t\ttag_killed = \"K \";\n+\t\ttag_resolve_undo = \"U \";\n \t}\n \tif (show_modified || show_others || show_deleted || (dir.flags & DIR_SHOW_IGNORED) || show_killed)\n \t\trequire_work_tree = 1;\n@@ -529,7 +568,7 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)\n \n \t/* With no flags, we default to showing the cached files */\n \tif (!(show_stage | show_deleted | show_others | show_unmerged |\n-\t      show_killed | show_modified))\n+\t      show_killed | show_modified | show_resolve_undo))\n \t\tshow_cached = 1;\n \n \tif (prefix)\n@@ -544,6 +583,8 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)\n \t\toverlay_tree_on_cache(with_tree, prefix);\n \t}\n \tshow_files(&dir, prefix);\n+\tif (show_resolve_undo)\n+\t\tshow_ru_info(prefix);\n \n \tif (ps_matched) {\n \t\tint bad;\ndiff --git a/t/t2030-unresolve-info.sh b/t/t2030-unresolve-info.sh\nnew file mode 100755\nindex 0000000..785c8b3\n--- /dev/null\n+++ b/t/t2030-unresolve-info.sh\n@@ -0,0 +1,88 @@\n+#!/bin/sh\n+\n+test_description='undoing resolution'\n+\n+. ./test-lib.sh\n+\n+check_resolve_undo () {\n+\tmsg=$1\n+\tshift\n+\twhile case $# in\n+\t0)\tbreak ;;\n+\t1|2|3)\tdie \"Bug in check-resolve-undo test\" ;;\n+\tesac\n+\tdo\n+\t\tpath=$1\n+\t\tshift\n+\t\tfor stage in 1 2 3\n+\t\tdo\n+\t\t\tsha1=$1\n+\t\t\tshift\n+\t\t\tcase \"$sha1\" in\n+\t\t\t'') continue ;;\n+\t\t\tesac\n+\t\t\tsha1=$(git rev-parse --verify \"$sha1\")\n+\t\t\tprintf \"100644 %s %s\\t%s\\n\" $sha1 $stage $path\n+\t\tdone\n+\tdone >\"$msg.expect\" &&\n+\tgit ls-files --resolve-undo >\"$msg.actual\" &&\n+\ttest_cmp \"$msg.expect\" \"$msg.actual\"\n+}\n+\n+prime_resolve_undo () {\n+\tgit reset --hard &&\n+\tgit checkout second^0 &&\n+\ttest_tick &&\n+\ttest_must_fail git merge third^0 &&\n+\techo merge does not leave anything &&\n+\tcheck_resolve_undo empty &&\n+\techo different >file &&\n+\tgit add file &&\n+\techo resolving records &&\n+\tcheck_resolve_undo recorded file initial:file second:file third:file\n+}\n+\n+test_expect_success setup '\n+\ttest_commit initial file first &&\n+\tgit branch side &&\n+\tgit branch another &&\n+\ttest_commit second file second &&\n+\tgit checkout side &&\n+\ttest_commit third file third &&\n+\tgit checkout another &&\n+\ttest_commit fourth file fourth &&\n+\tgit checkout master\n+'\n+\n+test_expect_success 'add records switch clears' '\n+\tprime_resolve_undo &&\n+\ttest_tick &&\n+\tgit commit -m merged &&\n+\techo committing keeps &&\n+\tcheck_resolve_undo kept file initial:file second:file third:file &&\n+\tgit checkout second^0 &&\n+\techo switching clears &&\n+\tcheck_resolve_undo cleared\n+'\n+\n+test_expect_success 'rm records reset clears' '\n+\tprime_resolve_undo &&\n+\ttest_tick &&\n+\tgit commit -m merged &&\n+\techo committing keeps &&\n+\tcheck_resolve_undo kept file initial:file second:file third:file &&\n+\n+\techo merge clears upfront &&\n+\ttest_must_fail git merge fourth^0 &&\n+\tcheck_resolve_undo nuked &&\n+\n+\tgit rm -f file &&\n+\techo resolving records &&\n+\tcheck_resolve_undo recorded file initial:file HEAD:file fourth:file &&\n+\n+\tgit reset --hard &&\n+\techo resetting discards &&\n+\tcheck_resolve_undo discarded\n+'\n+\n+test_done\n-- \n1.6.6\n"},{"id":"130485","messageId":"1262122958-9378-5-git-send-email-gitster@pobox.com","threadId":"22054","inReplyTo":"1262122958-9378-1-git-send-email-gitster@pobox.com","subject":"[PATCH 4/9] resolve-undo: allow plumbing to clear the information","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-12-29T21:42:33Z","receivedAt":"2009-12-29T21:42:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"At the Porcelain level, operations such as merge that populate an\ninitially cleanly merged index with conflicted entries clear the\nresolve-undo information upfront.  Give scripted Porcelains a way\nto do the same, by implementing \"update-index --clear-resolve-info\".\n\nWith this, a scripted Porcelain may \"update-index --clear-resolve-info\"\nfirst and repeatedly run \"update-index --cacheinfo\" to stuff unmerged\nentries to the index, to be resolved by the user with \"git add\" and\nstuff.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-update-index.c    |    5 +++++\n t/t2030-unresolve-info.sh |   12 ++++++++++++\n 2 files changed, 17 insertions(+), 0 deletions(-)\n\ndiff --git a/builtin-update-index.c b/builtin-update-index.c\nindex a6b7f2d..a19e786 100644\n--- a/builtin-update-index.c\n+++ b/builtin-update-index.c\n@@ -9,6 +9,7 @@\n #include \"tree-walk.h\"\n #include \"builtin.h\"\n #include \"refs.h\"\n+#include \"resolve-undo.h\"\n \n /*\n  * Default to not allowing changes to the list of files. The\n@@ -703,6 +704,10 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)\n \t\t\t\tverbose = 1;\n \t\t\t\tcontinue;\n \t\t\t}\n+\t\t\tif (!strcmp(path, \"--clear-resolve-undo\")) {\n+\t\t\t\tresolve_undo_clear();\n+\t\t\t\tcontinue;\n+\t\t\t}\n \t\t\tif (!strcmp(path, \"-h\") || !strcmp(path, \"--help\"))\n \t\t\t\tusage(update_index_usage);\n \t\t\tdie(\"unknown option %s\", path);\ndiff --git a/t/t2030-unresolve-info.sh b/t/t2030-unresolve-info.sh\nindex 785c8b3..9844802 100755\n--- a/t/t2030-unresolve-info.sh\n+++ b/t/t2030-unresolve-info.sh\n@@ -85,4 +85,16 @@ test_expect_success 'rm records reset clears' '\n \tcheck_resolve_undo discarded\n '\n \n+test_expect_success 'plumbing clears' '\n+\tprime_resolve_undo &&\n+\ttest_tick &&\n+\tgit commit -m merged &&\n+\techo committing keeps &&\n+\tcheck_resolve_undo kept file initial:file second:file third:file &&\n+\n+\techo plumbing clear &&\n+\tgit update-index --clear-resolve-undo &&\n+\tcheck_resolve_undo cleared\n+'\n+\n test_done\n-- \n1.6.6\n"},{"id":"130491","messageId":"1262122958-9378-6-git-send-email-gitster@pobox.com","threadId":"22054","inReplyTo":"1262122958-9378-1-git-send-email-gitster@pobox.com","subject":"[PATCH 5/9] resolve-undo: \"checkout -m path\" uses resolve-undo information","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-12-29T21:42:34Z","receivedAt":"2009-12-29T21:42:34Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Once you resolved conflicts by \"git add path\", you cannot recreate the\nconflicted state with \"git checkout -m path\", because you lost information\nfrom higher stages in the index when you resolved them.\n\nSince we record the necessary information in the resolve-undo index\nextension these days, we can reproduce the unmerged state in the index and\ncheck it out.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-checkout.c        |    4 +++\n cache.h                   |    1 +\n resolve-undo.c            |   59 +++++++++++++++++++++++++++++++++++++++++++++\n resolve-undo.h            |    2 +\n t/t2030-unresolve-info.sh |   11 ++++++++\n 5 files changed, 77 insertions(+), 0 deletions(-)\n\ndiff --git a/builtin-checkout.c b/builtin-checkout.c\nindex a0fe7a4..bdef1aa 100644\n--- a/builtin-checkout.c\n+++ b/builtin-checkout.c\n@@ -235,6 +235,10 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec,\n \tif (report_path_error(ps_matched, pathspec, 0))\n \t\treturn 1;\n \n+\t/* \"checkout -m path\" to recreate conflicted state */\n+\tif (opts->merge)\n+\t\tunmerge_cache(pathspec);\n+\n \t/* Any unmerged paths? */\n \tfor (pos = 0; pos < active_nr; pos++) {\n \t\tstruct cache_entry *ce = active_cache[pos];\ndiff --git a/cache.h b/cache.h\nindex 310d9e6..f479f09 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -338,6 +338,7 @@ static inline void remove_name_hash(struct cache_entry *ce)\n #define cache_name_exists(name, namelen, igncase) index_name_exists(&the_index, (name), (namelen), (igncase))\n #define cache_name_is_other(name, namelen) index_name_is_other(&the_index, (name), (namelen))\n #define resolve_undo_clear() resolve_undo_clear_index(&the_index)\n+#define unmerge_cache(pathspec) unmerge_index(&the_index, pathspec)\n #endif\n \n enum object_type {\ndiff --git a/resolve-undo.c b/resolve-undo.c\nindex 86e8547..37d73cd 100644\n--- a/resolve-undo.c\n+++ b/resolve-undo.c\n@@ -1,4 +1,5 @@\n #include \"cache.h\"\n+#include \"dir.h\"\n #include \"resolve-undo.h\"\n #include \"string-list.h\"\n \n@@ -115,3 +116,61 @@ void resolve_undo_clear_index(struct index_state *istate)\n \tistate->resolve_undo = NULL;\n \tistate->cache_changed = 1;\n }\n+\n+int unmerge_index_entry_at(struct index_state *istate, int pos)\n+{\n+\tstruct cache_entry *ce;\n+\tstruct string_list_item *item;\n+\tstruct resolve_undo_info *ru;\n+\tint i, err = 0;\n+\n+\tif (!istate->resolve_undo)\n+\t\treturn pos;\n+\n+\tce = istate->cache[pos];\n+\tif (ce_stage(ce)) {\n+\t\t/* already unmerged */\n+\t\twhile ((pos < istate->cache_nr) &&\n+\t\t       ! strcmp(istate->cache[pos]->name, ce->name))\n+\t\t\tpos++;\n+\t\treturn pos - 1; /* return the last entry processed */\n+\t}\n+\titem = string_list_lookup(ce->name, istate->resolve_undo);\n+\tif (!item)\n+\t\treturn pos;\n+\tru = item->util;\n+\tif (!ru)\n+\t\treturn pos;\n+\tremove_index_entry_at(istate, pos);\n+\tfor (i = 0; i < 3; i++) {\n+\t\tstruct cache_entry *nce;\n+\t\tif (!ru->mode[i])\n+\t\t\tcontinue;\n+\t\tnce = make_cache_entry(ru->mode[i], ru->sha1[i],\n+\t\t\t\t       ce->name, i + 1, 0);\n+\t\tif (add_index_entry(istate, nce, ADD_CACHE_OK_TO_ADD)) {\n+\t\t\terr = 1;\n+\t\t\terror(\"cannot unmerge '%s'\", ce->name);\n+\t\t}\n+\t}\n+\tif (err)\n+\t\treturn pos;\n+\tfree(ru);\n+\titem->util = NULL;\n+\treturn unmerge_index_entry_at(istate, pos);\n+}\n+\n+void unmerge_index(struct index_state *istate, const char **pathspec)\n+{\n+\tint i;\n+\n+\tif (!istate->resolve_undo)\n+\t\treturn;\n+\n+\tfor (i = 0; i < istate->cache_nr; i++) {\n+\t\tstruct cache_entry *ce = istate->cache[i];\n+\t\tif (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, NULL))\n+\t\t\tcontinue;\n+\t\ti = unmerge_index_entry_at(istate, i);\n+\t}\n+}\ndiff --git a/resolve-undo.h b/resolve-undo.h\nindex 74194d0..e4e5c1b 100644\n--- a/resolve-undo.h\n+++ b/resolve-undo.h\n@@ -10,5 +10,7 @@ extern void record_resolve_undo(struct index_state *, struct cache_entry *);\n extern void resolve_undo_write(struct strbuf *, struct string_list *);\n extern struct string_list *resolve_undo_read(void *, unsigned long);\n extern void resolve_undo_clear_index(struct index_state *);\n+extern int unmerge_index_entry_at(struct index_state *, int);\n+extern void unmerge_index(struct index_state *, const char **);\n \n #endif\ndiff --git a/t/t2030-unresolve-info.sh b/t/t2030-unresolve-info.sh\nindex 9844802..ea65f39 100755\n--- a/t/t2030-unresolve-info.sh\n+++ b/t/t2030-unresolve-info.sh\n@@ -97,4 +97,15 @@ test_expect_success 'plumbing clears' '\n \tcheck_resolve_undo cleared\n '\n \n+test_expect_success 'add records checkout -m undoes' '\n+\tprime_resolve_undo &&\n+\tgit diff HEAD &&\n+\tgit checkout --conflict=merge file &&\n+\techo checkout used the record and removed it &&\n+\tcheck_resolve_undo removed &&\n+\techo the index and the work tree is unmerged again &&\n+\tgit diff >actual &&\n+\tgrep \"^++<<<<<<<\" actual\n+'\n+\n test_done\n-- \n1.6.6\n"},{"id":"130489","messageId":"1262122958-9378-7-git-send-email-gitster@pobox.com","threadId":"22054","inReplyTo":"1262122958-9378-1-git-send-email-gitster@pobox.com","subject":"[PATCH 6/9] resolve-undo: teach \"update-index --unresolve\" to use resolve-undo info","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-12-29T21:42:35Z","receivedAt":"2009-12-29T21:42:35Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"The update-index plumbing command had a hacky --unresolve implementation\nthat was written back in the days when merge was the only way for users to\nend up with higher stages in the index, and assumed that stage #2 must\nhave come from HEAD, stage #3 from MERGE_HEAD and didn't bother to compute\nthe stage #1 information.\n\nThere were several issues with this approach:\n\n - These days, merge is not the only command, and conflicts coming from\n   commands like cherry-pick, \"am -3\", etc. cannot be recreated by looking\n   at MERGE_HEAD;\n\n - For a conflict that came from a merge that had renames, picking up the\n   same path from MERGE_HEAD and HEAD wouldn't help recreating it, either;\n\n - It may have been Ok not to recreate stage #1 back when it was written,\n   because \"diff --ours/--theirs\" were the only availble ways to review\n   conflicts and they don't need stage #1 information.  \"diff --cc\" that\n   was invented much later is a lot more useful way but it needs stage #1.\n\nWe can use resolve-undo information recorded in the index extension to\nsolve all of these issues.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-update-index.c    |   13 ++++++++++++-\n cache.h                   |    1 +\n t/t2030-unresolve-info.sh |    7 +++++++\n 3 files changed, 20 insertions(+), 1 deletions(-)\n\ndiff --git a/builtin-update-index.c b/builtin-update-index.c\nindex a19e786..750db16 100644\n--- a/builtin-update-index.c\n+++ b/builtin-update-index.c\n@@ -433,7 +433,18 @@ static int unresolve_one(const char *path)\n \n \t/* See if there is such entry in the index. */\n \tpos = cache_name_pos(path, namelen);\n-\tif (pos < 0) {\n+\tif (0 <= pos) {\n+\t\t/* already merged */\n+\t\tpos = unmerge_cache_entry_at(pos);\n+\t\tif (pos < active_nr) {\n+\t\t\tstruct cache_entry *ce = active_cache[pos];\n+\t\t\tif (ce_stage(ce) &&\n+\t\t\t    ce_namelen(ce) == namelen &&\n+\t\t\t    !memcmp(ce->name, path, namelen))\n+\t\t\t\treturn 0;\n+\t\t}\n+\t\t/* no resolve-undo information; fall back */\n+\t} else {\n \t\t/* If there isn't, either it is unmerged, or\n \t\t * resolved as \"removed\" by mistake.  We do not\n \t\t * want to do anything in the former case.\ndiff --git a/cache.h b/cache.h\nindex f479f09..97b4a74 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -338,6 +338,7 @@ static inline void remove_name_hash(struct cache_entry *ce)\n #define cache_name_exists(name, namelen, igncase) index_name_exists(&the_index, (name), (namelen), (igncase))\n #define cache_name_is_other(name, namelen) index_name_is_other(&the_index, (name), (namelen))\n #define resolve_undo_clear() resolve_undo_clear_index(&the_index)\n+#define unmerge_cache_entry_at(at) unmerge_index_entry_at(&the_index, at)\n #define unmerge_cache(pathspec) unmerge_index(&the_index, pathspec)\n #endif\n \ndiff --git a/t/t2030-unresolve-info.sh b/t/t2030-unresolve-info.sh\nindex ea65f39..28e2eb1 100755\n--- a/t/t2030-unresolve-info.sh\n+++ b/t/t2030-unresolve-info.sh\n@@ -108,4 +108,11 @@ test_expect_success 'add records checkout -m undoes' '\n \tgrep \"^++<<<<<<<\" actual\n '\n \n+test_expect_success 'unmerge with plumbing' '\n+\tprime_resolve_undo &&\n+\tgit update-index --unresolve file &&\n+\tgit ls-files -u >actual &&\n+\ttest $(wc -l <actual) = 3\n+'\n+\n test_done\n-- \n1.6.6\n"},{"id":"130482","messageId":"1262122958-9378-8-git-send-email-gitster@pobox.com","threadId":"22054","inReplyTo":"1262122958-9378-1-git-send-email-gitster@pobox.com","subject":"[PATCH 7/9] rerere: remove silly 1024-byte line limit","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-12-29T21:42:36Z","receivedAt":"2009-12-29T21:42:36Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ever since 658f365 (Make git-rerere a builtin, 2006-12-20) rewrote it, it\nkept this line-length limit regression, even after we started using strbuf\nin the same function in 19b358e (Use strbuf API in buitin-rerere.c,\n2007-09-06).\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n rerere.c |   19 ++++++++++---------\n 1 files changed, 10 insertions(+), 9 deletions(-)\n\ndiff --git a/rerere.c b/rerere.c\nindex 29f95f6..88bb4f1 100644\n--- a/rerere.c\n+++ b/rerere.c\n@@ -87,12 +87,12 @@ static int handle_file(const char *path,\n \t unsigned char *sha1, const char *output)\n {\n \tgit_SHA_CTX ctx;\n-\tchar buf[1024];\n \tint hunk_no = 0;\n \tenum {\n \t\tRR_CONTEXT = 0, RR_SIDE_1, RR_SIDE_2, RR_ORIGINAL,\n \t} hunk = RR_CONTEXT;\n \tstruct strbuf one = STRBUF_INIT, two = STRBUF_INIT;\n+\tstruct strbuf buf = STRBUF_INIT;\n \tFILE *f = fopen(path, \"r\");\n \tFILE *out = NULL;\n \tint wrerror = 0;\n@@ -111,20 +111,20 @@ static int handle_file(const char *path,\n \tif (sha1)\n \t\tgit_SHA1_Init(&ctx);\n \n-\twhile (fgets(buf, sizeof(buf), f)) {\n-\t\tif (!prefixcmp(buf, \"<<<<<<< \")) {\n+\twhile (!strbuf_getwholeline(&buf, f, '\\n')) {\n+\t\tif (!prefixcmp(buf.buf, \"<<<<<<< \")) {\n \t\t\tif (hunk != RR_CONTEXT)\n \t\t\t\tgoto bad;\n \t\t\thunk = RR_SIDE_1;\n-\t\t} else if (!prefixcmp(buf, \"|||||||\") && isspace(buf[7])) {\n+\t\t} else if (!prefixcmp(buf.buf, \"|||||||\") && isspace(buf.buf[7])) {\n \t\t\tif (hunk != RR_SIDE_1)\n \t\t\t\tgoto bad;\n \t\t\thunk = RR_ORIGINAL;\n-\t\t} else if (!prefixcmp(buf, \"=======\") && isspace(buf[7])) {\n+\t\t} else if (!prefixcmp(buf.buf, \"=======\") && isspace(buf.buf[7])) {\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.buf, \">>>>>>> \")) {\n \t\t\tif (hunk != RR_SIDE_2)\n \t\t\t\tgoto bad;\n \t\t\tif (strbuf_cmp(&one, &two) > 0)\n@@ -147,13 +147,13 @@ static int handle_file(const char *path,\n \t\t\tstrbuf_reset(&one);\n \t\t\tstrbuf_reset(&two);\n \t\t} else if (hunk == RR_SIDE_1)\n-\t\t\tstrbuf_addstr(&one, buf);\n+\t\t\tstrbuf_addstr(&one, buf.buf);\n \t\telse if (hunk == RR_ORIGINAL)\n \t\t\t; /* discard */\n \t\telse if (hunk == RR_SIDE_2)\n-\t\t\tstrbuf_addstr(&two, buf);\n+\t\t\tstrbuf_addstr(&two, buf.buf);\n \t\telse if (out)\n-\t\t\tferr_puts(buf, out, &wrerror);\n+\t\t\tferr_puts(buf.buf, out, &wrerror);\n \t\tcontinue;\n \tbad:\n \t\thunk = 99; /* force error exit */\n@@ -161,6 +161,7 @@ static int handle_file(const char *path,\n \t}\n \tstrbuf_release(&one);\n \tstrbuf_release(&two);\n+\tstrbuf_release(&buf);\n \n \tfclose(f);\n \tif (wrerror)\n-- \n1.6.6\n"},{"id":"130484","messageId":"1262122958-9378-9-git-send-email-gitster@pobox.com","threadId":"22054","inReplyTo":"1262122958-9378-1-git-send-email-gitster@pobox.com","subject":"[PATCH 8/9] rerere: refactor rerere logic to make it independent from I/O","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-12-29T21:42:37Z","receivedAt":"2009-12-29T21:42:37Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This splits the handle_file() function into in-core part and I/O\nparts of the logic to create the preimage, so that we can compute\nthe conflict identifier without having to use temporary files.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n rerere.c |  117 +++++++++++++++++++++++++++++++++++++++++++------------------\n 1 files changed, 82 insertions(+), 35 deletions(-)\n\ndiff --git a/rerere.c b/rerere.c\nindex 88bb4f1..f013ae7 100644\n--- a/rerere.c\n+++ b/rerere.c\n@@ -83,8 +83,41 @@ static inline void ferr_puts(const char *s, FILE *fp, int *err)\n \tferr_write(s, strlen(s), fp, err);\n }\n \n-static int handle_file(const char *path,\n-\t unsigned char *sha1, const char *output)\n+struct rerere_io {\n+\tint (*getline)(struct strbuf *, struct rerere_io *);\n+\tvoid (*putstr)(const char *, struct rerere_io *);\n+\tvoid (*putmem)(const char *, size_t, struct rerere_io *);\n+\t/* some more stuff */\n+};\n+\n+struct rerere_io_file {\n+\tstruct rerere_io io;\n+\tFILE *input;\n+\tFILE *output;\n+\tint wrerror;\n+};\n+\n+static int rerere_file_getline(struct strbuf *sb, struct rerere_io *io_)\n+{\n+\tstruct rerere_io_file *io = (struct rerere_io_file *)io_;\n+\treturn strbuf_getwholeline(sb, io->input, '\\n');\n+}\n+\n+static void rerere_file_putstr(const char *str, struct rerere_io *io_)\n+{\n+\tstruct rerere_io_file *io = (struct rerere_io_file *)io_;\n+\tif (io->output)\n+\t\tferr_puts(str, io->output, &io->wrerror);\n+}\n+\n+static void rerere_file_putmem(const char *mem, size_t sz, struct rerere_io *io_)\n+{\n+\tstruct rerere_io_file *io = (struct rerere_io_file *)io_;\n+\tif (io->output)\n+\t\tferr_write(mem, sz, io->output, &io->wrerror);\n+}\n+\n+static int handle_path(unsigned char *sha1, struct rerere_io *io)\n {\n \tgit_SHA_CTX ctx;\n \tint hunk_no = 0;\n@@ -93,25 +126,11 @@ static int handle_file(const char *path,\n \t} hunk = RR_CONTEXT;\n \tstruct strbuf one = STRBUF_INIT, two = STRBUF_INIT;\n \tstruct strbuf buf = STRBUF_INIT;\n-\tFILE *f = fopen(path, \"r\");\n-\tFILE *out = NULL;\n-\tint wrerror = 0;\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\tgit_SHA1_Init(&ctx);\n \n-\twhile (!strbuf_getwholeline(&buf, f, '\\n')) {\n+\twhile (!io->getline(&buf, io)) {\n \t\tif (!prefixcmp(buf.buf, \"<<<<<<< \")) {\n \t\t\tif (hunk != RR_CONTEXT)\n \t\t\t\tgoto bad;\n@@ -131,13 +150,11 @@ static int handle_file(const char *path,\n \t\t\t\tstrbuf_swap(&one, &two);\n \t\t\thunk_no++;\n \t\t\thunk = RR_CONTEXT;\n-\t\t\tif (out) {\n-\t\t\t\tferr_puts(\"<<<<<<<\\n\", out, &wrerror);\n-\t\t\t\tferr_write(one.buf, one.len, out, &wrerror);\n-\t\t\t\tferr_puts(\"=======\\n\", out, &wrerror);\n-\t\t\t\tferr_write(two.buf, two.len, out, &wrerror);\n-\t\t\t\tferr_puts(\">>>>>>>\\n\", out, &wrerror);\n-\t\t\t}\n+\t\t\tio->putstr(\"<<<<<<<\\n\", io);\n+\t\t\tio->putmem(one.buf, one.len, io);\n+\t\t\tio->putstr(\"=======\\n\", io);\n+\t\t\tio->putmem(two.buf, two.len, io);\n+\t\t\tio->putstr(\">>>>>>>\\n\", io);\n \t\t\tif (sha1) {\n \t\t\t\tgit_SHA1_Update(&ctx, one.buf ? one.buf : \"\",\n \t\t\t\t\t    one.len + 1);\n@@ -152,8 +169,8 @@ static int handle_file(const char *path,\n \t\t\t; /* discard */\n \t\telse if (hunk == RR_SIDE_2)\n \t\t\tstrbuf_addstr(&two, buf.buf);\n-\t\telse if (out)\n-\t\t\tferr_puts(buf.buf, out, &wrerror);\n+\t\telse\n+\t\t\tio->putstr(buf.buf, io);\n \t\tcontinue;\n \tbad:\n \t\thunk = 99; /* force error exit */\n@@ -163,21 +180,51 @@ static int handle_file(const char *path,\n \tstrbuf_release(&two);\n \tstrbuf_release(&buf);\n \n-\tfclose(f);\n-\tif (wrerror)\n-\t\terror(\"There were errors while writing %s (%s)\",\n-\t\t      path, strerror(wrerror));\n-\tif (out && fclose(out))\n-\t\twrerror = error(\"Failed to flush %s: %s\",\n-\t\t\t\tpath, strerror(errno));\n \tif (sha1)\n \t\tgit_SHA1_Final(sha1, &ctx);\n-\tif (hunk != RR_CONTEXT) {\n+\tif (hunk != RR_CONTEXT)\n+\t\treturn -1;\n+\treturn hunk_no;\n+}\n+\n+static int handle_file(const char *path, unsigned char *sha1, const char *output)\n+{\n+\tint hunk_no = 0;\n+\tstruct rerere_io_file io;\n+\n+\tmemset(&io, 0, sizeof(io));\n+\tio.io.getline = rerere_file_getline;\n+\tio.io.putstr = rerere_file_putstr;\n+\tio.io.putmem = rerere_file_putmem;\n+\tio.input = fopen(path, \"r\");\n+\tio.wrerror = 0;\n+\tif (!io.input)\n+\t\treturn error(\"Could not open %s\", path);\n+\n+\tif (output) {\n+\t\tio.output = fopen(output, \"w\");\n+\t\tif (!io.output) {\n+\t\t\tfclose(io.input);\n+\t\t\treturn error(\"Could not write %s\", output);\n+\t\t}\n+\t}\n+\n+\thunk_no = handle_path(sha1, (struct rerere_io *)&io);\n+\n+\tfclose(io.input);\n+\tif (io.wrerror)\n+\t\terror(\"There were errors while writing %s (%s)\",\n+\t\t      path, strerror(io.wrerror));\n+\tif (io.output && fclose(io.output))\n+\t\tio.wrerror = error(\"Failed to flush %s: %s\",\n+\t\t\t\t   path, strerror(errno));\n+\n+\tif (hunk_no < 0) {\n \t\tif (output)\n \t\t\tunlink_or_warn(output);\n \t\treturn error(\"Could not parse conflict hunks in %s\", path);\n \t}\n-\tif (wrerror)\n+\tif (io.wrerror)\n \t\treturn -1;\n \treturn hunk_no;\n }\n-- \n1.6.6\n"},{"id":"130487","messageId":"1262122958-9378-10-git-send-email-gitster@pobox.com","threadId":"22054","inReplyTo":"1262122958-9378-1-git-send-email-gitster@pobox.com","subject":"[PATCH 9/9] rerere forget path: forget recorded resolution","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-12-29T21:42:38Z","receivedAt":"2009-12-29T21:42:38Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"After you find out an earlier resolution you told rerere to use was a\nmismerge, there is no easy way to clear it.  A new subcommand \"forget\" can\nbe used to tell git to forget a recorded resolution, so that you can redo\nthe merge from scratch.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-rerere.c          |    2 +\n rerere.c                  |  127 +++++++++++++++++++++++++++++++++++++++++++++\n rerere.h                  |    1 +\n t/t2030-unresolve-info.sh |   22 ++++++++\n 4 files changed, 152 insertions(+), 0 deletions(-)\n\ndiff --git a/builtin-rerere.c b/builtin-rerere.c\nindex 2be9ffb..0253abf 100644\n--- a/builtin-rerere.c\n+++ b/builtin-rerere.c\n@@ -110,6 +110,8 @@ 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], \"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 f013ae7..c1da6f6 100644\n--- a/rerere.c\n+++ b/rerere.c\n@@ -3,6 +3,9 @@\n #include \"rerere.h\"\n #include \"xdiff/xdiff.h\"\n #include \"xdiff-interface.h\"\n+#include \"dir.h\"\n+#include \"resolve-undo.h\"\n+#include \"ll-merge.h\"\n \n /* if rerere_enabled == -1, fall back to detection of .git/rr-cache */\n static int rerere_enabled = -1;\n@@ -229,6 +232,95 @@ static int handle_file(const char *path, unsigned char *sha1, const char *output\n \treturn hunk_no;\n }\n \n+struct rerere_io_mem {\n+\tstruct rerere_io io;\n+\tstruct strbuf input;\n+};\n+\n+static int rerere_mem_getline(struct strbuf *sb, struct rerere_io *io_)\n+{\n+\tstruct rerere_io_mem *io = (struct rerere_io_mem *)io_;\n+\tchar *ep;\n+\tsize_t len;\n+\n+\tstrbuf_release(sb);\n+\tif (!io->input.len)\n+\t\treturn -1;\n+\tep = strchrnul(io->input.buf, '\\n');\n+\tif (*ep == '\\n')\n+\t\tep++;\n+\tlen = ep - io->input.buf;\n+\tstrbuf_add(sb, io->input.buf, len);\n+\tstrbuf_remove(&io->input, 0, len);\n+\treturn 0;\n+}\n+\n+static void rerere_mem_putstr(const char *str, struct rerere_io *io_)\n+{\n+\t; /* no-op */\n+}\n+\n+\n+static void rerere_mem_putmem(const char *mem, size_t sz, struct rerere_io *io_)\n+{\n+\t; /* no-op */\n+}\n+\n+static int handle_cache(const char *path, unsigned char *sha1)\n+{\n+\tmmfile_t mmfile[3];\n+\tmmbuffer_t result = {NULL, 0};\n+\tstruct cache_entry *ce;\n+\tint pos, len, i, hunk_no;\n+\tstruct rerere_io_mem io;\n+\n+\t/*\n+\t * Reproduce the conflicted merge in-core\n+\t */\n+\tlen = strlen(path);\n+\tpos = cache_name_pos(path, len);\n+\tif (0 <= pos)\n+\t\treturn -1;\n+\tpos = -pos - 1;\n+\n+\tfor (i = 0; i < 3; i++) {\n+\t\tenum object_type type;\n+\t\tunsigned long size;\n+\n+\t\tmmfile[i].size = 0;\n+\t\tmmfile[i].ptr = NULL;\n+\t\tif (active_nr <= pos)\n+\t\t\tbreak;\n+\t\tce = active_cache[pos++];\n+\t\tif (ce_namelen(ce) != len || memcmp(ce->name, path, len)\n+\t\t    || ce_stage(ce) != i + 1)\n+\t\t\tbreak;\n+\t\tmmfile[i].ptr = read_sha1_file(ce->sha1, &type, &size);\n+\t\tmmfile[i].size = size;\n+\t}\n+\tfor (i = 0; i < 3; i++) {\n+\t\tif (!mmfile[i].ptr && !mmfile[i].size)\n+\t\t\tmmfile[i].ptr = xstrdup(\"\");\n+\t}\n+\tll_merge(&result, path, &mmfile[0],\n+\t\t &mmfile[1], \"ours\",\n+\t\t &mmfile[2], \"theirs\", 0);\n+\tfor (i = 0; i < 3; i++)\n+\t\tfree(mmfile[i].ptr);\n+\n+\tmemset(&io, 0, sizeof(&io));\n+\tio.io.getline = rerere_mem_getline;\n+\tio.io.putstr = rerere_mem_putstr;\n+\tio.io.putmem = rerere_mem_putmem;\n+\n+\tstrbuf_init(&io.input, 0);\n+\tstrbuf_attach(&io.input, result.ptr, result.size, result.size);\n+\n+\thunk_no = handle_path(sha1, (struct rerere_io *)&io);\n+\tstrbuf_release(&io.input);\n+\treturn hunk_no;\n+}\n+\n static int find_conflict(struct string_list *conflict)\n {\n \tint i;\n@@ -440,3 +532,38 @@ int rerere(void)\n \t\treturn 0;\n \treturn do_plain_rerere(&merge_rr, fd);\n }\n+\n+int rerere_forget(const char **pathspec)\n+{\n+\tint i;\n+\tstruct string_list conflict = { NULL, 0, 0, 1 };\n+\n+\tif (read_cache() < 0)\n+\t\treturn error(\"Could not read index\");\n+\n+\tunmerge_cache(pathspec);\n+\tfind_conflict(&conflict);\n+\tfor (i = 0; i < conflict.nr; i++) {\n+\t\tunsigned char sha1[20];\n+\t\tconst char *postimage;\n+\t\tint ret;\n+\t\tstruct string_list_item *it = &conflict.items[i];\n+\t\tif (!match_pathspec(pathspec, it->string, strlen(it->string),\n+\t\t\t\t    0, NULL))\n+\t\t\tcontinue;\n+\t\tret = handle_cache(it->string, sha1);\n+\t\tif (ret < 1) {\n+\t\t\terror(\"Could not parse conflict hunks in '%s'\",\n+\t\t\t      it->string);\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\", it->string);\n+\t\telse if (errno == ENOENT)\n+\t\t\terror(\"no remembered resolution for %s\", it->string);\n+\t\telse\n+\t\t\terror(\"cannot unlink %s: %s\", postimage, strerror(errno));\n+\t}\n+\treturn 0;\n+}\ndiff --git a/rerere.h b/rerere.h\nindex 13313f3..36560ff 100644\n--- a/rerere.h\n+++ b/rerere.h\n@@ -7,5 +7,6 @@ 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_forget(const char **);\n \n #endif\ndiff --git a/t/t2030-unresolve-info.sh b/t/t2030-unresolve-info.sh\nindex 28e2eb1..92e006b 100755\n--- a/t/t2030-unresolve-info.sh\n+++ b/t/t2030-unresolve-info.sh\n@@ -115,4 +115,26 @@ test_expect_success 'unmerge with plumbing' '\n \ttest $(wc -l <actual) = 3\n '\n \n+test_expect_success 'rerere and rerere --forget' '\n+\tmkdir .git/rr-cache &&\n+\tprime_resolve_undo &&\n+\techo record the resolution &&\n+\tgit rerere &&\n+\trerere_id=$(cd .git/rr-cache && echo */postimage) &&\n+\trerere_id=${rerere_id%/postimage} &&\n+\ttest -f .git/rr-cache/$rerere_id/postimage &&\n+\tgit checkout -m file &&\n+\techo resurrect the conflict &&\n+\tgrep \"^=======\" file &&\n+\techo reresolve the conflict &&\n+\tgit rerere &&\n+\ttest \"z$(cat file)\" = zdifferent &&\n+\techo register the resolution again &&\n+\tgit add file &&\n+\tcheck_resolve_undo kept file initial:file second:file third:file &&\n+\ttest -z \"$(git ls-files -u)\" &&\n+\tgit rerere forget file &&\n+\t! test -f .git/rr-cache/$rerere_id/postimage\n+'\n+\n test_done\n-- \n1.6.6\n"},{"id":"130864","messageId":"4B43AE38.9070800@kdbg.org","threadId":"22054","inReplyTo":"1262122958-9378-10-git-send-email-gitster@pobox.com","subject":"Re: [PATCH 9/9] rerere forget path: forget recorded resolution","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2010-01-05T21:25:12Z","receivedAt":"2010-01-05T21:25:12Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Junio C Hamano schrieb:\n> After you find out an earlier resolution you told rerere to use was a\n> mismerge, there is no easy way to clear it.  A new subcommand \"forget\" can\n> be used to tell git to forget a recorded resolution, so that you can redo\n> the merge from scratch.\n> ...\n> diff --git a/rerere.c b/rerere.c\n> index f013ae7..c1da6f6 100644\n> --- a/rerere.c\n> +++ b/rerere.c\n> ...\n> +static int handle_cache(const char *path, unsigned char *sha1)\n> +{\n> +...\n> +\tll_merge(&result, path, &mmfile[0],\n> +\t\t &mmfile[1], \"ours\",\n> +\t\t &mmfile[2], \"theirs\", 0);\n\nWhen you simply call ll_merge(), will it obey any merge drivers that are \ndefined in .gitattributes? Do we care about them?\n\nI already had an implementation of \"rerere forget\" before you presented \nthis solution, but it relies on that the user calls \"checkout \n--conflict=merge\" first. One reason (besides its simplicity) was that it \ndoes not have to care how the merge is computed.\n\n[I haven't submitted my solution, yet, because I haven't had the time to \ndo this large merge where I expect to make use \"rerere forget\", testing \nits usefulness.]\n\n-- Hannes\n"},{"id":"130882","messageId":"7v4on0oxcs.fsf@alter.siamese.dyndns.org","threadId":"22054","inReplyTo":"4B43AE38.9070800@kdbg.org","subject":"Re: [PATCH 9/9] rerere forget path: forget recorded resolution","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-01-06T01:06:43Z","receivedAt":"2010-01-06T01:06:43Z","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> Junio C Hamano schrieb:\n>> After you find out an earlier resolution you told rerere to use was a\n>> mismerge, there is no easy way to clear it.  A new subcommand \"forget\" can\n>> be used to tell git to forget a recorded resolution, so that you can redo\n>> the merge from scratch.\n>> ...\n>> diff --git a/rerere.c b/rerere.c\n>> index f013ae7..c1da6f6 100644\n>> --- a/rerere.c\n>> +++ b/rerere.c\n>> ...\n>> +static int handle_cache(const char *path, unsigned char *sha1)\n>> +{\n>> +...\n>> +\tll_merge(&result, path, &mmfile[0],\n>> +\t\t &mmfile[1], \"ours\",\n>> +\t\t &mmfile[2], \"theirs\", 0);\n>\n> When you simply call ll_merge(), will it obey any merge drivers that\n> are defined in .gitattributes? Do we care about them?\n>\n> I already had an implementation of \"rerere forget\" before you\n> presented this solution, but it relies on that the user calls\n> \"checkout --conflict=merge\" first. One reason (besides its simplicity)\n> was that it does not have to care how the merge is computed.\n\nDoesn't \"checkout --conflict=merge\" use the same ll_merge() machinery?\n\n> [I haven't submitted my solution, yet, because I haven't had the time\n> to do this large merge where I expect to make use \"rerere forget\",\n> testing its usefulness.]\n>\n> -- Hannes\n"},{"id":"130905","messageId":"4B444FF9.1000004@kdbg.org","threadId":"22054","inReplyTo":"7v4on0oxcs.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 9/9] rerere forget path: forget recorded resolution","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2010-01-06T08:55:21Z","receivedAt":"2010-01-06T08:55:21Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Junio C Hamano schrieb:\n> Johannes Sixt <j6t@kdbg.org> writes:\n>> When you simply call ll_merge(), will it obey any merge drivers that\n>> are defined in .gitattributes? Do we care about them?\n>>\n>> I already had an implementation of \"rerere forget\" before you\n>> presented this solution, but it relies on that the user calls\n>> \"checkout --conflict=merge\" first. One reason (besides its simplicity)\n>> was that it does not have to care how the merge is computed.\n> \n> Doesn't \"checkout --conflict=merge\" use the same ll_merge() machinery?\n\nIt does, without setting up .gitattributes, either (IIUC), which is a bug IMO.\n\nThat said, I consider your solution superior because it works without \ndepending on conflict markers in the file in the worktree. Nevertheless, \nwe should think about whether merge drivers of .gitattributes should be \nobeyed. I think they should: For example, a specialized XML merge driver \ncould leave conflicts that are different from those that merge-recursive \ngenerates.\n\n-- Hannes\n"},{"id":"130922","messageId":"7vr5q31871.fsf@alter.siamese.dyndns.org","threadId":"22054","inReplyTo":"4B444FF9.1000004@kdbg.org","subject":"Re: [PATCH 9/9] rerere forget path: forget recorded resolution","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-01-06T16:58:42Z","receivedAt":"2010-01-06T16:58:42Z","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>> Doesn't \"checkout --conflict=merge\" use the same ll_merge() machinery?\n>\n> It does, without setting up .gitattributes, either (IIUC), which is a bug IMO.\n\nI don't think there is any bug there; git_checkattr() that is used by\ngit_path_check_merge() autoinitializes the attribute machinery.\n\nHowever, I think you need this fix for \"checkout -m\" to work as intended,\nif your custom merge driver has recursive attribute set to 'binary' or\nsomething:\n\ndiff --git a/builtin-checkout.c b/builtin-checkout.c\nindex 64f3a11..50f5079 100644\n--- a/builtin-checkout.c\n+++ b/builtin-checkout.c\n@@ -167,7 +167,7 @@ static int checkout_merged(int pos, struct checkout *state)\n \tfill_mm(active_cache[pos+2]->sha1, &theirs);\n \n \tstatus = ll_merge(&result_buf, path, &ancestor,\n-\t\t\t  &ours, \"ours\", &theirs, \"theirs\", 1);\n+\t\t\t  &ours, \"ours\", &theirs, \"theirs\", 0);\n \tfree(ancestor.ptr);\n \tfree(ours.ptr);\n \tfree(theirs.ptr);\n\nI don't know why I decided to pass \"1\" to ll_merge() there; it doesn't\nmake any sense and looks like an untested bug to me.\n\nThe ll_merge() call jc/cache-unmerge topic adds for \"rerere forget\"\ndoesn't share this problem.\n"},{"id":"130955","messageId":"7vzl4qopxt.fsf@alter.siamese.dyndns.org","threadId":"22054","inReplyTo":"7vr5q31871.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 9/9] rerere forget path: forget recorded resolution","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-01-06T21:59:10Z","receivedAt":"2010-01-06T21:59:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> However, I think you need this fix for \"checkout -m\" to work as intended,\n\nHere is a cleaner reroll with tests, meant to be applied on v1.6.1-rc1 or\nlater.\n\n-- >8 --\nSubject: [PATCH] checkout -m path: fix recreating conflicts\n\nWe should tell ll_merge() that the 3-way merge between stages #2 and #3 is\nan outermost merge, not a virtual-ancestor creation.\n\nBack when this code was originally written, users couldn't write custom\nmerge drivers easily, so the bug didn't matter, but these days it does.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-checkout.c |    2 +-\n t/t7201-co.sh      |   57 ++++++++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 58 insertions(+), 1 deletions(-)\n\ndiff --git a/builtin-checkout.c b/builtin-checkout.c\nindex 7f3bd7b..e41e73b 100644\n--- a/builtin-checkout.c\n+++ b/builtin-checkout.c\n@@ -179,7 +179,7 @@ static int checkout_merged(int pos, struct checkout *state)\n \tfill_mm(active_cache[pos+2]->sha1, &theirs);\n \n \tstatus = ll_merge(&result_buf, path, &ancestor,\n-\t\t\t  &ours, \"ours\", &theirs, \"theirs\", 1);\n+\t\t\t  &ours, \"ours\", &theirs, \"theirs\", 0);\n \tfree(ancestor.ptr);\n \tfree(ours.ptr);\n \tfree(theirs.ptr);\ndiff --git a/t/t7201-co.sh b/t/t7201-co.sh\nindex 0e21632..3214ad2 100755\n--- a/t/t7201-co.sh\n+++ b/t/t7201-co.sh\n@@ -534,4 +534,61 @@ test_expect_success 'failing checkout -b should not break working tree' '\n \n '\n \n+(\n+ echo \"#!$SHELL_PATH\"\n+ cat <<\\EOF\n+O=$1 A=$2 B=$3\n+cat \"$A\" >.tmp\n+exec >\"$A\"\n+echo '<<<<<<< filfre-theirs'\n+cat \"$B\"\n+echo '||||||| filfre-common'\n+cat \"$O\"\n+echo '======='\n+cat \".tmp\"\n+echo '>>>>>>> filfre-ours'\n+rm -f .tmp\n+exit 1\n+EOF\n+) >filfre.sh\n+chmod +x filfre.sh\n+\n+test_expect_success 'custom merge driver with checkout -m' '\n+\tgit reset --hard &&\n+\n+\tgit config merge.filfre.driver \"./filfre.sh %O %A %B\" &&\n+\tgit config merge.filfre.name \"Feel-free merge driver\" &&\n+\tgit config merge.filfre.recursive binary &&\n+\techo \"arm merge=filfre\" >.gitattributes &&\n+\n+\tgit checkout -b left &&\n+\techo neutral >arm &&\n+\tgit add arm .gitattributes &&\n+\ttest_tick &&\n+\tgit commit -m neutral &&\n+\tgit branch right &&\n+\n+\techo left >arm &&\n+\ttest_tick &&\n+\tgit commit -a -m left &&\n+\tgit checkout right &&\n+\n+\techo right >arm &&\n+\ttest_tick &&\n+\tgit commit -a -m right &&\n+\n+\ttest_must_fail git merge left &&\n+\t(\n+\t\tfor t in filfre-common left right\n+\t\tdo\n+\t\t\tgrep $t arm || exit 1\n+\t\tdone\n+\t\texit 0\n+\t) &&\n+\n+\tmv arm expect &&\n+\tgit checkout -m arm &&\n+\ttest_cmp expect arm\n+'\n+\n test_done\n-- \n1.6.6.184.ge163d\n"},{"id":"131133","messageId":"201001082255.51499.j6t@kdbg.org","threadId":"22054","inReplyTo":"1262122958-9378-10-git-send-email-gitster@pobox.com","subject":"Re: [PATCH 9/9] rerere forget path: forget recorded resolution","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2010-01-08T21:55:51Z","receivedAt":"2010-01-08T21:55:51Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Your implementation forgets to re-insert the forgotten resolutions into\nMERGE_RR, therefore, the next 'git rerere' does not record the new\nresolution.\n\nIn my implementation of 'rerere forget', I had the following tests.\nThe latter two of the three new tests fail with your implementation.\n\n-- Hannes\n\ndiff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh\nindex a6bc028..a3f0c18 100755\n--- a/t/t4200-rerere.sh\n+++ b/t/t4200-rerere.sh\n@@ -75,8 +75,9 @@ test_expect_success 'no postimage or thisimage yet' \\\n test_expect_success 'preimage has right number of lines' '\n \n \tcnt=$(sed -ne \"/^<<<<<<</,/^>>>>>>>/p\" $rr/preimage | wc -l) &&\n-\ttest $cnt = 13\n-\n+\ttest $cnt = 13 &&\n+\tcnt=$(grep \"To die! T\" $rr/preimage | wc -l) &&\n+\ttest $cnt = 1\n '\n \n git show first:a1 > a1\n@@ -142,7 +143,23 @@ test_expect_success 'rerere kicked in' \"! grep ^=======$ a1\"\n \n test_expect_success 'rerere prefers first change' 'test_cmp a1 expect'\n \n-rm $rr/postimage\n+test_expect_success 'rerere forget drops postimage' '\n+\tgit rerere forget a1 &&\n+\t! test -f $rr/postimage\n+'\n+\n+test_expect_success 'conflict hash is the same as before' '\n+\ttest $sha1 = \"$(perl -pe \"s/\t.*//\" .git/MERGE_RR)\"\n+'\n+\n+test_expect_success 'preimage was updated' '\n+\tcnt=$(sed -ne \"/^<<<<<<</,/^>>>>>>>/p\" $rr/preimage | wc -l) &&\n+\ttest $cnt = 13 &&\n+\tcnt=$(grep \"To die! T\" $rr/preimage | wc -l) &&\n+\ttest $cnt = 2\n+'\n+\n+rm -f $rr/postimage\n echo \"$sha1\ta1\" | perl -pe 'y/\\012/\\000/' > .git/MERGE_RR\n \n test_expect_success 'rerere clear' 'git rerere clear'\n"},{"id":"131145","messageId":"7vpr5krxoo.fsf@alter.siamese.dyndns.org","threadId":"22054","inReplyTo":"201001082255.51499.j6t@kdbg.org","subject":"Re: [PATCH 9/9] rerere forget path: forget recorded resolution","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-01-08T23:20:23Z","receivedAt":"2010-01-08T23:20:23Z","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> Your implementation forgets to re-insert the forgotten resolutions into\n> MERGE_RR, therefore, the next 'git rerere' does not record the new\n> resolution.\n>\n> In my implementation of 'rerere forget', I had the following tests.\n\nPlease filfre to roll a patch that adds the tests and code that inserts\nnecessary MERGE_RR entries, if the feature is pressing; unfortunately I\ndon't think I will have much git time during the coming weekend.\n"},{"id":"131246","messageId":"7v4omte72j.fsf@alter.siamese.dyndns.org","threadId":"22054","inReplyTo":"7vpr5krxoo.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 9/9] rerere forget path: forget recorded resolution","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-01-11T01:58:12Z","receivedAt":"2010-01-11T01:58:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Johannes Sixt <j6t@kdbg.org> writes:\n>\n>> Your implementation forgets to re-insert the forgotten resolutions into\n>> MERGE_RR, therefore, the next 'git rerere' does not record the new\n>> resolution.\n>>\n>> In my implementation of 'rerere forget', I had the following tests.\n>\n> Please filfre to roll a patch that adds the tests and code that inserts\n> necessary MERGE_RR entries, if the feature is pressing; unfortunately I\n> don't think I will have much git time during the coming weekend.\n\nI ended up doing this myself.  As we are dropping the postimage and adding\na new MERGE_RR entry, I also think that it is safer to update the preimage\nwith the conflict we got for this round, so I added that as well.\n\nHowever, I think there is a room for improvement in preimage handling.\n\nCurrently, the rerere database is indexed with the conflict hash and for\neach conflict hash you can record a single preimage-postimage pair to\nreplay.  But you can have conflicts with the same conflict hash, but with\nslightly different contexts outside the conflicted region, and the right\nresolution can be different depending on the outside context.\n\nIn the traditional implementation, I punted this issue by noticing\nconflicts in the three-way merge between pre, post and this images.  If\npreimage is too different from the conflicted contents we got during this\nmerge, then the previous resolution should not apply.\n\nBut I think the right solution would be to have more than one preimage and\npostimage pairs (preimage.0 vs postimage.0,... etc.) and try to use each\nof them in handle_path() until it finds one that can be used to cleanly\nmerge with the conflict we got in thisimage during this round.\n"},{"id":"131306","messageId":"201001112022.31257.j6t@kdbg.org","threadId":"22054","inReplyTo":"7v4omte72j.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 9/9] rerere forget path: forget recorded resolution","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2010-01-11T19:22:31Z","receivedAt":"2010-01-11T19:22:31Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"On Montag, 11. Januar 2010, Junio C Hamano wrote:\n> I ended up doing this myself.  As we are dropping the postimage and adding\n> a new MERGE_RR entry, I also think that it is safer to update the preimage\n> with the conflict we got for this round, so I added that as well.\n\nThank you, it appears to work as expected. It is actually very important to \nupdate the preimage as well, otherwise, the new postimage can contain \nunrelated additional changes.\n\n> However, I think there is a room for improvement in preimage handling.\n>\n> Currently, the rerere database is indexed with the conflict hash and for\n> each conflict hash you can record a single preimage-postimage pair to\n> replay.  But you can have conflicts with the same conflict hash, but with\n> slightly different contexts outside the conflicted region, and the right\n> resolution can be different depending on the outside context.\n\nI did encounter a case where the same resolution would apply to all conflicts \nthat have the same conflict hash, so it's not quite what you talk about. But \nnot all conflicts were automatically resolved. I haven't yet analyzed what \nhappened - it could just be that the xdl_merge call fails due to the \ndifferences in the text immediately outside the conflict markers.\n\n> In the traditional implementation, I punted this issue by noticing\n> conflicts in the three-way merge between pre, post and this images.  If\n> preimage is too different from the conflicted contents we got during this\n> merge, then the previous resolution should not apply.\n>\n> But I think the right solution would be to have more than one preimage and\n> postimage pairs (preimage.0 vs postimage.0,... etc.) and try to use each\n> of them in handle_path() until it finds one that can be used to cleanly\n> merge with the conflict we got in thisimage during this round.\n\nThe situation happens rarely, so I don't know if we should care. OTOH, *when* \nthe situation arises, and a recorded resolution is applied incorrectly, it \nmay be quite annoying. Dunno.\n\n-- Hannes\n"},{"id":"131311","messageId":"7v3a2cif04.fsf@alter.siamese.dyndns.org","threadId":"22054","inReplyTo":"201001112022.31257.j6t@kdbg.org","subject":"Re: [PATCH 9/9] rerere forget path: forget recorded resolution","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-01-11T20:05:31Z","receivedAt":"2010-01-11T20:05:31Z","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> I did encounter a case where the same resolution would apply to all\n> conflicts that have the same conflict hash, so it's not quite what you\n> talk about. But not all conflicts were automatically resolved. I haven't\n> yet analyzed what happened - it could just be that the xdl_merge call\n> fails due to the differences in the text immediately outside the\n> conflict markers.\n\nActually it is _very_ easy to fool rerere to do something totally\nunexpected, and I have been thinking about using the similarity comparison\nalgorithm on the region outside the conflicted area between preimage and\nthisimage and reject use of rerere.\n\nTry this in an empty directory.\n\n-- >8 --\n\n#!/bin/sh\n\ngit init\n\ncreate_numbers () {\n\tfor n in 0 1 2 3 4 \"$1\" 5 6 7 8 9\n\tdo\n\t\techo $n\n\tdone >numbers.txt\n}\n\ncreate_letters () {\n\tfor l in a b c d e \"$1\" f g h i j\n\tdo\n\t\techo $l\n\tdone >letters.txt\n}\n\ncreate_files () {\n\tcreate_numbers \"$1\"\n\tcreate_letters \"$1\"\n}\n\ncreate_files \"\"\ngit add numbers.txt letters.txt\ngit commit -m initial\ngit branch side\n\ncreate_files \"+\"\ngit commit -a -m master\n\ngit checkout side\ncreate_files \"-\"\ngit commit -a -m side\n\nmkdir -p .git/rr-cache\n\n# On this history we changed an empty line to +; merge\n# with another history that changed it to -\ngit checkout master^0\ngit merge side\n\n# The above should have conflicted.  The resolution is to '='\n\ncreate_numbers \"=\"\ngit rerere\n\ngit rerere status\ngit rerere diff\ncat numbers.txt\ncat letters.txt\n\n-- 8< --\n\nNow, immediately after this sequence, rerere will give you an disaster.\n"},{"id":"131315","messageId":"201001112205.54404.j6t@kdbg.org","threadId":"22054","inReplyTo":"7v3a2cif04.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 9/9] rerere forget path: forget recorded resolution","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2010-01-11T21:05:54Z","receivedAt":"2010-01-11T21:05:54Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"On Montag, 11. Januar 2010, Junio C Hamano wrote:\n> Actually it is _very_ easy to fool rerere to do something totally\n> unexpected, and I have been thinking about using the similarity comparison\n> algorithm on the region outside the conflicted area between preimage and\n> thisimage and reject use of rerere.\n>\n> Try this in an empty directory.\n>[snip]\n> Now, immediately after this sequence, rerere will give you an disaster.\n\nIndeed. The problem here is that two entirely different resolutions are \nrecorded for the same conflict hash *in one run of rerere*. The damage can be \navoided if conflict hashes are not reused in do_plain_rerere (in the first \nloop). (Though, I'm currently not in the mood to look into this in more \ndepth.)\n\nOf course, this does not mean that *both* conflicts can be resolved \nautomatically when the merge is repeated. In my use-case this would have been \ndesirable (and even your example would suggest it is, but that is not \ngenerally true).\n\n-- Hannes\n"}]}