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