summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2009-12-25 15:51:32 -0800
committerJunio C Hamano <gitster@pobox.com>2010-01-10 18:10:36 -0800
commitdea4562bf5d1c27cd6c01b9cb65c3a8b8ee99a69 (patch)
treebdcd9bf016f18206cb361aebb644e6ea9fe9c718
parent27d6b08536838fff0e3568a57cc622ca1c39bf01 (diff)
downloadgit-dea4562bf5d1c27cd6c01b9cb65c3a8b8ee99a69.tar.gz
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>
-rw-r--r--builtin-rerere.c2
-rw-r--r--rerere.c133
-rw-r--r--rerere.h1
-rwxr-xr-xt/t2030-unresolve-info.sh25
4 files changed, 161 insertions, 0 deletions
diff --git a/builtin-rerere.c b/builtin-rerere.c
index 2be9ffb77b..0253abf9b6 100644
--- a/builtin-rerere.c
+++ b/builtin-rerere.c
@@ -110,6 +110,8 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)
if (!strcmp(argv[1], "-h"))
usage(git_rerere_usage);
+ else if (!strcmp(argv[1], "forget"))
+ return rerere_forget(argv + 2);
fd = setup_rerere(&merge_rr);
if (fd < 0)
diff --git a/rerere.c b/rerere.c
index db1d42f1b6..d92990a6bb 100644
--- a/rerere.c
+++ b/rerere.c
@@ -3,6 +3,9 @@
#include "rerere.h"
#include "xdiff/xdiff.h"
#include "xdiff-interface.h"
+#include "dir.h"
+#include "resolve-undo.h"
+#include "ll-merge.h"
/* if rerere_enabled == -1, fall back to detection of .git/rr-cache */
static int rerere_enabled = -1;
@@ -223,6 +226,87 @@ static int handle_file(const char *path, unsigned char *sha1, const char *output
return hunk_no;
}
+struct rerere_io_mem {
+ struct rerere_io io;
+ struct strbuf input;
+};
+
+static int rerere_mem_getline(struct strbuf *sb, struct rerere_io *io_)
+{
+ struct rerere_io_mem *io = (struct rerere_io_mem *)io_;
+ char *ep;
+ size_t len;
+
+ strbuf_release(sb);
+ if (!io->input.len)
+ return -1;
+ ep = strchrnul(io->input.buf, '\n');
+ if (*ep == '\n')
+ ep++;
+ len = ep - io->input.buf;
+ strbuf_add(sb, io->input.buf, len);
+ strbuf_remove(&io->input, 0, len);
+ return 0;
+}
+
+static int handle_cache(const char *path, unsigned char *sha1, const char *output)
+{
+ mmfile_t mmfile[3];
+ mmbuffer_t result = {NULL, 0};
+ struct cache_entry *ce;
+ int pos, len, i, hunk_no;
+ struct rerere_io_mem io;
+
+ /*
+ * Reproduce the conflicted merge in-core
+ */
+ len = strlen(path);
+ pos = cache_name_pos(path, len);
+ if (0 <= pos)
+ return -1;
+ pos = -pos - 1;
+
+ for (i = 0; i < 3; i++) {
+ enum object_type type;
+ unsigned long size;
+
+ mmfile[i].size = 0;
+ mmfile[i].ptr = NULL;
+ if (active_nr <= pos)
+ break;
+ ce = active_cache[pos++];
+ if (ce_namelen(ce) != len || memcmp(ce->name, path, len)
+ || ce_stage(ce) != i + 1)
+ break;
+ mmfile[i].ptr = read_sha1_file(ce->sha1, &type, &size);
+ mmfile[i].size = size;
+ }
+ for (i = 0; i < 3; i++) {
+ if (!mmfile[i].ptr && !mmfile[i].size)
+ mmfile[i].ptr = xstrdup("");
+ }
+ ll_merge(&result, path, &mmfile[0],
+ &mmfile[1], "ours",
+ &mmfile[2], "theirs", 0);
+ for (i = 0; i < 3; i++)
+ free(mmfile[i].ptr);
+
+ memset(&io, 0, sizeof(&io));
+ io.io.getline = rerere_mem_getline;
+ if (output)
+ io.io.output = fopen(output, "w");
+ else
+ io.io.output = NULL;
+ strbuf_init(&io.input, 0);
+ strbuf_attach(&io.input, result.ptr, result.size, result.size);
+
+ hunk_no = handle_path(sha1, (struct rerere_io *)&io);
+ strbuf_release(&io.input);
+ if (io.io.output)
+ fclose(io.io.output);
+ return hunk_no;
+}
+
static int find_conflict(struct string_list *conflict)
{
int i;
@@ -434,3 +518,52 @@ int rerere(void)
return 0;
return do_plain_rerere(&merge_rr, fd);
}
+
+static int rerere_forget_one_path(const char *path, struct string_list *rr)
+{
+ const char *filename;
+ char *hex;
+ unsigned char sha1[20];
+ int ret;
+
+ ret = handle_cache(path, sha1, NULL);
+ if (ret < 1)
+ return error("Could not parse conflict hunks in '%s'", path);
+ hex = xstrdup(sha1_to_hex(sha1));
+ filename = rerere_path(hex, "postimage");
+ if (unlink(filename))
+ return (errno == ENOENT
+ ? error("no remembered resolution for %s", path)
+ : error("cannot unlink %s: %s", filename, strerror(errno)));
+
+ handle_cache(path, sha1, rerere_path(hex, "preimage"));
+ fprintf(stderr, "Updated preimage for '%s'\n", path);
+
+
+ string_list_insert(path, rr)->util = hex;
+ fprintf(stderr, "Forgot resolution for %s\n", path);
+ return 0;
+}
+
+int rerere_forget(const char **pathspec)
+{
+ int i, fd;
+ struct string_list conflict = { NULL, 0, 0, 1 };
+ struct string_list merge_rr = { NULL, 0, 0, 1 };
+
+ if (read_cache() < 0)
+ return error("Could not read index");
+
+ fd = setup_rerere(&merge_rr);
+
+ unmerge_cache(pathspec);
+ find_conflict(&conflict);
+ for (i = 0; i < conflict.nr; i++) {
+ struct string_list_item *it = &conflict.items[i];
+ if (!match_pathspec(pathspec, it->string, strlen(it->string),
+ 0, NULL))
+ continue;
+ rerere_forget_one_path(it->string, &merge_rr);
+ }
+ return write_rr(&merge_rr, fd);
+}
diff --git a/rerere.h b/rerere.h
index 13313f3f2b..36560ff2f5 100644
--- a/rerere.h
+++ b/rerere.h
@@ -7,5 +7,6 @@ extern int setup_rerere(struct string_list *);
extern int rerere(void);
extern const char *rerere_path(const char *hex, const char *file);
extern int has_rerere_resolution(const char *hex);
+extern int rerere_forget(const char **);
#endif
diff --git a/t/t2030-unresolve-info.sh b/t/t2030-unresolve-info.sh
index 28e2eb1cec..a38bd6df84 100755
--- a/t/t2030-unresolve-info.sh
+++ b/t/t2030-unresolve-info.sh
@@ -115,4 +115,29 @@ test_expect_success 'unmerge with plumbing' '
test $(wc -l <actual) = 3
'
+test_expect_success 'rerere and rerere --forget' '
+ mkdir .git/rr-cache &&
+ prime_resolve_undo &&
+ echo record the resolution &&
+ git rerere &&
+ rerere_id=$(cd .git/rr-cache && echo */postimage) &&
+ rerere_id=${rerere_id%/postimage} &&
+ test -f .git/rr-cache/$rerere_id/postimage &&
+ git checkout -m file &&
+ echo resurrect the conflict &&
+ grep "^=======" file &&
+ echo reresolve the conflict &&
+ git rerere &&
+ test "z$(cat file)" = zdifferent &&
+ echo register the resolution again &&
+ git add file &&
+ check_resolve_undo kept file initial:file second:file third:file &&
+ test -z "$(git ls-files -u)" &&
+ git rerere forget file &&
+ ! test -f .git/rr-cache/$rerere_id/postimage &&
+ tr "\0" "\n" <.git/MERGE_RR >actual &&
+ echo "$rerere_id file" >expect &&
+ test_cmp expect actual
+'
+
test_done