git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH/RFC 0/3] git rerere unresolve file

From
Junio C Hamano <gitster@pobox.com>
Date
Nov 22, 2009, 02:53 UTC
Message-ID
<7v6393mfqz.fsf@alter.siamese.dyndns.org>
In-Reply-To
<200911211958.40872.j6t@kdbg.org>
Johannes Sixt <j6t@kdbg.org> writes:
> ... But 
> there is no other way to remove an incorrect resolution short of purging the 
> whole rr-cache.
No, no no, no.  You do not have to.

As long as you can find out the <preimage,postimage> pair, iow, the subdirectory used by .git/rr-cache/, you can remove only "postimage" from there and redo your merge. "ls -t1 .git/rr-cache/*/thisimage | head" would be one way to manually find out which one it is.

For example, let's take this merge as an example.
commit 697eb20061dfa00838df32ac24c414dfb4a1f920
Merge: bba637b 46b77a6
Author: Junio C Hamano <gitster@pobox.com>
    Merge branch 'jk/1.7.0-status' into jch
You may not have rerere entries for this, so follow me along for a while.
 $ git checkout bba637b
 $ git merge 46b77a6

This will have a conflict. We are priming your rerere database, so we cheat (notice the last dot ".").

 $ git checkout 697eb200 -- .
 $ git rerere
Now reset it away, and redo the merge
 $ git reset --hard
 $ git checkout bba637b
 $ git merge 46b77a6
It will autoresolve the way I resolved in 697eb200
Suppose you do not like the resolution; you can do:
    $ git checkout --conflict=merge Documentation/git-commit.txt
    $ git rerere

to force updating "thisimage". The newest one is the entry you are looking for.

    $ ls -1t .git/rr-cache/*/thisimage | head -n 1
    .git/rr-cache/02aac459b0c777f92a8ca6f1e449e6760d366c20/thisimage

I can remove "postimage" from the directory, recreate conflict again, run rerere to remember it anew, resolve and have rerere remember the new resolution.

A tool support to compute the conflict hash would be part of a nice solution.

    $ git checkout --conflict=merge Documentation/git-commit.txt
    $ git rerere hash
    02aac459b0c777f92a8ca6f1e449e6760d366c20 Documentation/git-commit.txt    

Instead of giving "hash" subcommand and having the user to remove postimage from the directory, the tool can remove it, of course.

    $ git checkout --conflict=merge Documentation/git-commit.txt
    $ git rerere forget Documentation/git-commit.txt
    02aac459b0c777f92a8ca6f1e449e6760d366c20 Documentation/git-commit.txt    
    $ edit Documentation/git-commit.txt ;# come up with a better resolution 
    $ git rerere ;# record it
The attached are rough sketch of such "hash/forget" subcommands.
I however think the best user experience would go like this:
 * Run "git merge"; rerere replays an earlier resolution.
   $ git merge ...
   User decides that it is not a desirable one.
 * User fixes it and creates a better resolution in the work tree.  The
   user may fix-up the autoresolution or the user may first do "checkout
   --conflict=merge" and resolves the conflict from scratch.
   It does not matter how the updated resolution is prepared in the work
   tree.
 * Then the user tells rerere that "this is the corrected resolution",
   perhaps
   $ git rerere update Documentation/git-commit.txt
   This will
   - Internally recompute the original conflicted state, i.e. run
     "checkout --conflict=merge Documentation/git-commit.txt" in-core;
   - feed it to rerere.c::handle_file() to learn the conflict hash;
   - read the user's updated resolution from the work tree, and update
     "postimage" with it.

There is no need to change MERGE_RR at all if you did it the way outlined in the above.

My "hash" would be more-or-less a useless command. It still might be useful as a plumbing command, but it is mostly for debugging.

The "forget" subcommand may be useful, but the real implementation should be the first half of the "update", iow, recreate conflict in-core in order to compute the conflict hash, and drop existing "postimage", without replacing it from the work tree. We should leave it up to the user using "checkout --conflict" to reproduce the conflict in the work tree.

 builtin-rerere.c |    4 +++
 rerere.c         |   58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 rerere.h         |    2 +
 3 files changed, 64 insertions(+), 0 deletions(-)
diff --git a/builtin-rerere.c b/builtin-rerere.c
index 343d6cd..8865313 100644
--- a/builtin-rerere.c
+++ b/builtin-rerere.c
@@ -108,6 +108,10 @@ 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], "hash"))
+		return rerere_report_hash();
+	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 29f95f6..4899219 100644
--- a/rerere.c
+++ b/rerere.c
@@ -392,3 +392,61 @@ int rerere(void)
 		return 0;
 	return do_plain_rerere(&merge_rr, fd);
 }
+
+int rerere_forget(const char **path)
+{
+	struct string_list conflict = { NULL, 0, 0, 1 };
+	int i;
+
+	find_conflict(&conflict);
+
+	/*
+	 * Note note note.  I am being lazy here.  The loop should not
+	 * iterate over "path" like this.
+	 *
+	 * It should instead iterate over conflict entries, using "path"
+	 * as pathspec to filter, so that you can say "I am done with
+	 * all the conflict in Documentation/ area".
+	 */
+	for (i = 0; path[i]; i++) { 
+		unsigned char sha1[20];
+		const char *postimage;
+		int ret;
+
+		if (!string_list_has_string(&conflict, path[i])) {
+			error("No such conflicted path %s\n", path[i]);
+			continue;
+		}
+		ret = handle_file(path[i], sha1, NULL);
+		if (ret < 1) {
+			error("No conflict in work tree %s\n", path[i]);
+			continue;
+		}
+		postimage = rerere_path(sha1_to_hex(sha1), "postimage");
+		if (!unlink(postimage))
+			fprintf(stderr, "forgot resolution for %s\n", path[i]);
+		else if (errno == ENOENT)
+			error("no remembered resolution for %s", path[i]);
+		else
+			error("cannot unlink %s: %s", postimage, strerror(errno));
+	}
+	return 0;
+}
+
+int rerere_report_hash(void)
+{
+	struct string_list conflict = { NULL, 0, 0, 1 };
+	int i;
+
+	find_conflict(&conflict);
+	for (i = 0; i < conflict.nr; i++) {
+		const char *path = conflict.items[i].string;
+		unsigned char sha1[20];
+		int ret;
+		ret = handle_file(path, sha1, NULL);
+		if (ret < 1)
+			continue;
+		printf("%s %s\n", sha1_to_hex(sha1), path);
+	}
+	return 0;
+}
diff --git a/rerere.h b/rerere.h
index 13313f3..a10dea9 100644
--- a/rerere.h
+++ b/rerere.h
@@ -7,5 +7,7 @@ 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_report_hash(void);
+extern int rerere_forget(const char **path);
 
 #endif
Previous: Johannes SixtNext: Johannes Sixt
Message 5 of 7 in “git rerere unresolve file”
  1. 0/3 git rerere unresolve fileJohannes Sixt, Nov 21, 2009
  2. 1/3 rerere: keep a list of resolved files in MERGE_RRJohannes Sixt, Nov 21, 2009
  3. 2/3 rerere: make recording of the preimage reusableJohannes Sixt, Nov 21, 2009
  4. 3/3 git rerere unresolve file...Johannes Sixt, Nov 21, 2009
  5. Junio C HamanoNov 22, 2009
  6. Johannes SixtNov 22, 2009
  7. Nanako ShiraishiNov 24, 2009

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.