git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
From: Johannes Sixt <j6t@kdbg.org>
To: git@vger.kernel.org
Subject: [PATCH/RFC 3/3] git rerere unresolve file...
Date: Sat, 21 Nov 2009 20:02:47 +0100	[thread overview]
Message-ID: <200911212002.47757.j6t@kdbg.org> (raw)
In-Reply-To: <200911212001.06207.j6t@kdbg.org>

There was no way to remove a recorded resolution short of removing the
entire .git/rr-cache. This gets in the way when an incorrect resolution
was recorded.

This implements the subcommand 'git rerere unresolve' that selectively
removes the recorded resolution of the specified files. It also reverts
the resolution so that the files again have conflict markers. However,
these unresolved conflict markers not necessarily reflect "ours" and
"theirs" correctly because the preimage that was stored in the cache has
the conflicted sides in a canonical order.

In handle_file(), the checks for the beginning and end of a conflict
region have to be loosened slightly -- there can be any whitespace,
including a LF, not just a blank -- because after the conflict regions are
restored, there are no trailing blanks and a subsequent 'git rerere' would
not recognize them anymore.

'git checkout --conflict=merge path...' can restore the conflicted file
as well, but it does not remove the recorded resolution.

Signed-off-by: Johannes Sixt <j6t@kdbg.org>
---
 Documentation/git-rerere.txt |   10 +++++-
 builtin-rerere.c             |    9 +++--
 rerere.c                     |   74 ++++++++++++++++++++++++++++++++++++++----
 rerere.h                     |    2 +
 4 files changed, 84 insertions(+), 11 deletions(-)

diff --git a/Documentation/git-rerere.txt b/Documentation/git-rerere.txt
index 7dd515b..c9bf3f1 100644
--- a/Documentation/git-rerere.txt
+++ b/Documentation/git-rerere.txt
@@ -7,7 +7,7 @@ git-rerere - Reuse recorded resolution of conflicted merges
 
 SYNOPSIS
 --------
-'git rerere' ['clear'|'diff'|'status'|'gc']
+'git rerere' ['clear'|'diff'|'status'|'unresolve file...'|'gc']
 
 DESCRIPTION
 -----------
@@ -52,6 +52,14 @@ conflicts.  Additional arguments are passed directly to the system
 Like 'diff', but this only prints the filenames that will be tracked
 for resolutions.
 
+'unresolve'::
+
+Removes the resolution that was recorded for the specified files.
+The conflict sections are restored as well, although the direction of
+the "ours" and "theirs" sections may be inverted.
+You can use 'git checkout --conflict=merge file' to restore the
+original conflict markers in the correct direction.
+
 'gc'::
 
 This prunes records of conflicted merges that
diff --git a/builtin-rerere.c b/builtin-rerere.c
index 275827d..5d3a3a5 100644
--- a/builtin-rerere.c
+++ b/builtin-rerere.c
@@ -7,7 +7,7 @@
 #include "xdiff-interface.h"
 
 static const char git_rerere_usage[] =
-"git rerere [clear | status | diff | gc]";
+"git rerere [clear | status | diff | unresolve file... | gc]";
 
 /* these values are days */
 static int cutoff_noresolve = 15;
@@ -102,7 +102,7 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)
 {
 	struct string_list merge_rr = { NULL, 0, 0, 1 };
 	struct string_list merge_rr_done = { NULL, 0, 0, 1 };
-	int i, fd;
+	int i, fd, ret = 0;
 
 	if (argc < 2)
 		return rerere();
@@ -129,10 +129,13 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)
 			const char *name = (const char *)merge_rr.items[i].util;
 			diff_two(rerere_path(name, "preimage"), path, path, path);
 		}
+	else if (!strcmp(argv[1], "unresolve"))
+		ret = unresolve_rerere(&merge_rr, &merge_rr_done,
+				       get_pathspec(prefix, &argv[2]), fd);
 	else
 		usage(git_rerere_usage);
 
 	string_list_clear(&merge_rr, 1);
 	string_list_clear(&merge_rr_done, 1);
-	return 0;
+	return ret;
 }
diff --git a/rerere.c b/rerere.c
index 11fef88..b31008d 100644
--- a/rerere.c
+++ b/rerere.c
@@ -140,7 +140,7 @@ static int handle_file(const char *path,
 		git_SHA1_Init(&ctx);
 
 	while (fgets(buf, sizeof(buf), f)) {
-		if (!prefixcmp(buf, "<<<<<<< ")) {
+		if (!prefixcmp(buf, "<<<<<<<") && isspace(buf[7])) {
 			if (hunk != RR_CONTEXT)
 				goto bad;
 			hunk = RR_SIDE_1;
@@ -152,7 +152,7 @@ static int handle_file(const char *path,
 			if (hunk != RR_SIDE_1 && hunk != RR_ORIGINAL)
 				goto bad;
 			hunk = RR_SIDE_2;
-		} else if (!prefixcmp(buf, ">>>>>>> ")) {
+		} else if (!prefixcmp(buf, ">>>>>>>") && isspace(buf[7])) {
 			if (hunk != RR_SIDE_2)
 				goto bad;
 			if (strbuf_cmp(&one, &two) > 0)
@@ -248,23 +248,29 @@ static int record_preimage(struct string_list *rr, const char *path, int 
force)
 	return 0;
 }
 
-static int merge(const char *name, const char *path)
+#define RESOLVE 0
+#define UNRESOLVE 1
+#define UNRESOLVE_CHECK 2
+
+static int merge(const char *name, const char *path, int mode)
 {
 	int ret;
 	mmfile_t cur, base, other;
 	mmbuffer_t result = {NULL, 0};
 	xpparam_t xpp = {XDF_NEED_MINIMAL};
+	const char *basename = mode == RESOLVE ? "preimage" : "postimage";
+	const char *othername = mode == RESOLVE ? "postimage" : "preimage";
 
 	if (handle_file(path, NULL, rerere_path(name, "thisimage")) < 0)
 		return 1;
 
 	if (read_mmfile(&cur, rerere_path(name, "thisimage")) ||
-			read_mmfile(&base, rerere_path(name, "preimage")) ||
-			read_mmfile(&other, rerere_path(name, "postimage")))
+			read_mmfile(&base, rerere_path(name, basename)) ||
+			read_mmfile(&other, rerere_path(name, othername)))
 		return 1;
 	ret = xdl_merge(&base, &cur, "", &other, "",
 			&xpp, XDL_MERGE_ZEALOUS, &result);
-	if (!ret) {
+	if (!ret && mode != UNRESOLVE_CHECK) {
 		FILE *f = fopen(path, "w");
 		if (!f)
 			return error("Could not open %s: %s", path,
@@ -347,7 +353,7 @@ static int do_plain_rerere(struct string_list *rr,
 		const char *name = (const char *)rr->items[i].util;
 
 		if (has_rerere_resolution(name)) {
-			if (!merge(name, path)) {
+			if (!merge(name, path, RESOLVE)) {
 				if (rerere_autoupdate)
 					string_list_insert(path, &update);
 				fprintf(stderr,
@@ -433,3 +439,57 @@ int rerere(void)
 		return 0;
 	return do_plain_rerere(&merge_rr, &merge_rr_done, fd);
 }
+
+static int unresolve_check(struct string_list *rr_done, const char *path)
+{
+	struct string_list_item *item = string_list_lookup(path, rr_done);
+
+	if (!item) {
+		warning("not resolved using a previous resolution: %s", path);
+		return 0;
+	}
+	if (!has_rerere_resolution(item->util))
+		return error("no resolution found for %s", path);
+	if (merge(item->util, path, UNRESOLVE_CHECK))
+		return error("cannot revert resolution of %s", path);
+	return 0;
+}
+
+int unresolve_rerere_1(struct string_list *rr, struct string_list *rr_done,
+		       const char *path)
+{
+	struct string_list_item *item = string_list_lookup(path, rr_done);
+
+	if (!item)
+		return 0;
+	if (merge(item->util, path, UNRESOLVE))
+		return -1;
+
+	if (record_preimage(rr, path, 1))
+		return error("no conflict markers found: %s", path);
+
+	item->util = NULL;
+	unlink_or_warn(rerere_path(item->util, "postimage"));
+	return 0;
+}
+
+int unresolve_rerere(struct string_list *rr, struct string_list *rr_done,
+		     const char **pathspec, int fd)
+{
+	int i, ret = 0;
+
+	if (!pathspec)
+		return error("unresolve what?");
+
+	for (i = 0; pathspec[i]; i++) {
+		ret |= unresolve_check(rr_done, pathspec[i]);
+	}
+	if (ret)
+		return ret;
+
+	for (i = 0; !ret && pathspec[i]; i++) {
+		ret = unresolve_rerere_1(rr, rr_done, pathspec[i]);
+	}
+	ret |= write_rr(rr, rr_done, fd);
+	return ret;
+}
diff --git a/rerere.h b/rerere.h
index 9bb2f13..a0fa50f 100644
--- a/rerere.h
+++ b/rerere.h
@@ -7,5 +7,7 @@ extern int setup_rerere(struct string_list *, 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 unresolve_rerere(struct string_list *, struct string_list *,
+	const char **, int);
 
 #endif
-- 
1.6.5.2.182.ge039a

  reply	other threads:[~2009-11-21 20:48 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-11-21 18:58 [PATCH/RFC 0/3] git rerere unresolve file Johannes Sixt
2009-11-21 19:00 ` [PATCH/RFC 1/3] rerere: keep a list of resolved files in MERGE_RR Johannes Sixt
2009-11-21 19:01   ` [PATCH/RFC 2/3] rerere: make recording of the preimage reusable Johannes Sixt
2009-11-21 19:02     ` Johannes Sixt [this message]
2009-11-22  2:53 ` [PATCH/RFC 0/3] git rerere unresolve file Junio C Hamano
2009-11-22 14:19   ` Johannes Sixt
2009-11-24 23:40     ` Nanako Shiraishi
2009-12-29 21:42   ` [PATCH 0/9] Undoing conflict resolution Junio C Hamano
2009-12-29 21:42     ` [PATCH 1/9] builtin-merge.c: use standard active_cache macros Junio C Hamano
2009-12-29 21:42     ` [PATCH 2/9] resolve-undo: record resolved conflicts in a new index extension section Junio C Hamano
2009-12-29 21:42     ` [PATCH 3/9] resolve-undo: basic tests Junio C Hamano
2009-12-29 21:42     ` [PATCH 4/9] resolve-undo: allow plumbing to clear the information Junio C Hamano
2009-12-29 21:42     ` [PATCH 5/9] resolve-undo: "checkout -m path" uses resolve-undo information Junio C Hamano
2009-12-29 21:42     ` [PATCH 6/9] resolve-undo: teach "update-index --unresolve" to use resolve-undo info Junio C Hamano
2009-12-29 21:42     ` [PATCH 7/9] rerere: remove silly 1024-byte line limit Junio C Hamano
2009-12-29 21:42     ` [PATCH 8/9] rerere: refactor rerere logic to make it independent from I/O Junio C Hamano
2009-12-29 21:42     ` [PATCH 9/9] rerere forget path: forget recorded resolution Junio C Hamano
2010-01-05 21:25       ` Johannes Sixt
2010-01-06  1:06         ` Junio C Hamano
2010-01-06  8:55           ` Johannes Sixt
2010-01-06 16:58             ` Junio C Hamano
2010-01-06 21:59               ` Junio C Hamano
2010-01-08 21:55       ` Johannes Sixt
2010-01-08 23:20         ` Junio C Hamano
2010-01-11  1:58           ` Junio C Hamano
2010-01-11 19:22             ` Johannes Sixt
2010-01-11 20:05               ` Junio C Hamano
2010-01-11 21:05                 ` Johannes Sixt

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: http://vger.kernel.org/majordomo-info.html

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=200911212002.47757.j6t@kdbg.org \
    --to=j6t@kdbg.org \
    --cc=git@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).