@matheusbsbs / matheus-bagliano / commits / dea4562bf5
Fork 0

rerere forget path: forget recorded resolution

After you find out an earlier resolution you told rerere to use was a mismerge, there is no easy way to clear it. A new subcommand "forget" can be used to tell git to forget a recorded resolution, so that you can redo the merge from scratch. Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Dec 25, 2009 at 15:51 UTC dea4562bf5d1c27cd6c01b9cb65c3a8b8ee99a69
4 files changed +161
builtin-rerere.c
+2
@@ -110,6 +110,8 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)
110
111 if (!strcmp(argv[1], "-h"))
112 usage(git_rerere_usage);
113 + else if (!strcmp(argv[1], "forget"))
114 + return rerere_forget(argv + 2);
115
116 fd = setup_rerere(&merge_rr);
117 if (fd < 0)
rerere.c
+133
@@ -3,6 +3,9 @@
3 #include "rerere.h"
4 #include "xdiff/xdiff.h"
5 #include "xdiff-interface.h"
6 +#include "dir.h"
7 +#include "resolve-undo.h"
8 +#include "ll-merge.h"
9
10 /* if rerere_enabled == -1, fall back to detection of .git/rr-cache */
11 static int rerere_enabled = -1;
@@ -223,6 +226,87 @@ static int handle_file(const char *path, unsigned char *sha1, const char *output
226 return hunk_no;
227 }
228
229 +struct rerere_io_mem {
230 + struct rerere_io io;
231 + struct strbuf input;
232 +};
233 +
234 +static int rerere_mem_getline(struct strbuf *sb, struct rerere_io *io_)
235 +{
236 + struct rerere_io_mem *io = (struct rerere_io_mem *)io_;
237 + char *ep;
238 + size_t len;
239 +
240 + strbuf_release(sb);
241 + if (!io->input.len)
242 + return -1;
243 + ep = strchrnul(io->input.buf, '\n');
244 + if (*ep == '\n')
245 + ep++;
246 + len = ep - io->input.buf;
247 + strbuf_add(sb, io->input.buf, len);
248 + strbuf_remove(&io->input, 0, len);
249 + return 0;
250 +}
251 +
252 +static int handle_cache(const char *path, unsigned char *sha1, const char *output)
253 +{
254 + mmfile_t mmfile[3];
255 + mmbuffer_t result = {NULL, 0};
256 + struct cache_entry *ce;
257 + int pos, len, i, hunk_no;
258 + struct rerere_io_mem io;
259 +
260 + /*
261 + * Reproduce the conflicted merge in-core
262 + */
263 + len = strlen(path);
264 + pos = cache_name_pos(path, len);
265 + if (0 <= pos)
266 + return -1;
267 + pos = -pos - 1;
268 +
269 + for (i = 0; i < 3; i++) {
270 + enum object_type type;
271 + unsigned long size;
272 +
273 + mmfile[i].size = 0;
274 + mmfile[i].ptr = NULL;
275 + if (active_nr <= pos)
276 + break;
277 + ce = active_cache[pos++];
278 + if (ce_namelen(ce) != len || memcmp(ce->name, path, len)
279 + || ce_stage(ce) != i + 1)
280 + break;
281 + mmfile[i].ptr = read_sha1_file(ce->sha1, &type, &size);
282 + mmfile[i].size = size;
283 + }
284 + for (i = 0; i < 3; i++) {
285 + if (!mmfile[i].ptr && !mmfile[i].size)
286 + mmfile[i].ptr = xstrdup("");
287 + }
288 + ll_merge(&result, path, &mmfile[0],
289 + &mmfile[1], "ours",
290 + &mmfile[2], "theirs", 0);
291 + for (i = 0; i < 3; i++)
292 + free(mmfile[i].ptr);
293 +
294 + memset(&io, 0, sizeof(&io));
295 + io.io.getline = rerere_mem_getline;
296 + if (output)
297 + io.io.output = fopen(output, "w");
298 + else
299 + io.io.output = NULL;
300 + strbuf_init(&io.input, 0);
301 + strbuf_attach(&io.input, result.ptr, result.size, result.size);
302 +
303 + hunk_no = handle_path(sha1, (struct rerere_io *)&io);
304 + strbuf_release(&io.input);
305 + if (io.io.output)
306 + fclose(io.io.output);
307 + return hunk_no;
308 +}
309 +
310 static int find_conflict(struct string_list *conflict)
311 {
312 int i;
@@ -434,3 +518,52 @@ int rerere(void)
518 return 0;
519 return do_plain_rerere(&merge_rr, fd);
520 }
521 +
522 +static int rerere_forget_one_path(const char *path, struct string_list *rr)
523 +{
524 + const char *filename;
525 + char *hex;
526 + unsigned char sha1[20];
527 + int ret;
528 +
529 + ret = handle_cache(path, sha1, NULL);
530 + if (ret < 1)
531 + return error("Could not parse conflict hunks in '%s'", path);
532 + hex = xstrdup(sha1_to_hex(sha1));
533 + filename = rerere_path(hex, "postimage");
534 + if (unlink(filename))
535 + return (errno == ENOENT
536 + ? error("no remembered resolution for %s", path)
537 + : error("cannot unlink %s: %s", filename, strerror(errno)));
538 +
539 + handle_cache(path, sha1, rerere_path(hex, "preimage"));
540 + fprintf(stderr, "Updated preimage for '%s'\n", path);
541 +
542 +
543 + string_list_insert(path, rr)->util = hex;
544 + fprintf(stderr, "Forgot resolution for %s\n", path);
545 + return 0;
546 +}
547 +
548 +int rerere_forget(const char **pathspec)
549 +{
550 + int i, fd;
551 + struct string_list conflict = { NULL, 0, 0, 1 };
552 + struct string_list merge_rr = { NULL, 0, 0, 1 };
553 +
554 + if (read_cache() < 0)
555 + return error("Could not read index");
556 +
557 + fd = setup_rerere(&merge_rr);
558 +
559 + unmerge_cache(pathspec);
560 + find_conflict(&conflict);
561 + for (i = 0; i < conflict.nr; i++) {
562 + struct string_list_item *it = &conflict.items[i];
563 + if (!match_pathspec(pathspec, it->string, strlen(it->string),
564 + 0, NULL))
565 + continue;
566 + rerere_forget_one_path(it->string, &merge_rr);
567 + }
568 + return write_rr(&merge_rr, fd);
569 +}
rerere.h
+1
@@ -7,5 +7,6 @@ extern int setup_rerere(struct string_list *);
7 extern int rerere(void);
8 extern const char *rerere_path(const char *hex, const char *file);
9 extern int has_rerere_resolution(const char *hex);
10 +extern int rerere_forget(const char **);
11
12 #endif
t/t2030-unresolve-info.sh
+25
@@ -115,4 +115,29 @@ test_expect_success 'unmerge with plumbing' '
115 test $(wc -l <actual) = 3
116 '
117
118 +test_expect_success 'rerere and rerere --forget' '
119 + mkdir .git/rr-cache &&
120 + prime_resolve_undo &&
121 + echo record the resolution &&
122 + git rerere &&
123 + rerere_id=$(cd .git/rr-cache && echo */postimage) &&
124 + rerere_id=${rerere_id%/postimage} &&
125 + test -f .git/rr-cache/$rerere_id/postimage &&
126 + git checkout -m file &&
127 + echo resurrect the conflict &&
128 + grep "^=======" file &&
129 + echo reresolve the conflict &&
130 + git rerere &&
131 + test "z$(cat file)" = zdifferent &&
132 + echo register the resolution again &&
133 + git add file &&
134 + check_resolve_undo kept file initial:file second:file third:file &&
135 + test -z "$(git ls-files -u)" &&
136 + git rerere forget file &&
137 + ! test -f .git/rr-cache/$rerere_id/postimage &&
138 + tr "\0" "\n" <.git/MERGE_RR >actual &&
139 + echo "$rerere_id file" >expect &&
140 + test_cmp expect actual
141 +'
142 +
143 test_done