aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--rerere.c98
-rwxr-xr-xt/t4200-rerere.sh2
2 files changed, 61 insertions, 39 deletions
diff --git a/rerere.c b/rerere.c
index 0cf857bcc..353227cda 100644
--- a/rerere.c
+++ b/rerere.c
@@ -74,7 +74,9 @@ static void assign_variant(struct rerere_id *id)
variant = id->variant;
if (variant < 0) {
- variant = 0; /* for now */
+ for (variant = 0; variant < rr_dir->status_nr; variant++)
+ if (!rr_dir->status[variant])
+ break;
}
fit_variant(rr_dir, variant);
id->variant = variant;
@@ -177,15 +179,6 @@ static int has_rerere_resolution(const struct rerere_id *id)
return ((id->collection->status[variant] & both) == both);
}
-static int has_rerere_preimage(const struct rerere_id *id)
-{
- int variant = id->variant;
-
- if (variant < 0)
- return 0;
- return (id->collection->status[variant] & RR_HAS_PREIMAGE);
-}
-
static struct rerere_id *new_rerere_id_hex(char *hex)
{
struct rerere_id *id = xmalloc(sizeof(*id));
@@ -818,6 +811,13 @@ static void update_paths(struct string_list *update)
rollback_lock_file(&index_lock);
}
+static void remove_variant(struct rerere_id *id)
+{
+ unlink_or_warn(rerere_path(id, "postimage"));
+ unlink_or_warn(rerere_path(id, "preimage"));
+ id->collection->status[id->variant] = 0;
+}
+
/*
* The path indicated by rr_item may still have conflict for which we
* have a recorded resolution, in which case replay it and optionally
@@ -830,32 +830,46 @@ static void do_rerere_one_path(struct string_list_item *rr_item,
{
const char *path = rr_item->string;
struct rerere_id *id = rr_item->util;
+ struct rerere_dir *rr_dir = id->collection;
int variant;
- if (id->variant < 0)
- assign_variant(id);
variant = id->variant;
- if (!has_rerere_preimage(id)) {
+ /* Has the user resolved it already? */
+ if (variant >= 0) {
+ if (!handle_file(path, NULL, NULL)) {
+ copy_file(rerere_path(id, "postimage"), path, 0666);
+ id->collection->status[variant] |= RR_HAS_POSTIMAGE;
+ fprintf(stderr, "Recorded resolution for '%s'.\n", path);
+ free_rerere_id(rr_item);
+ rr_item->util = NULL;
+ return;
+ }
/*
- * We are the first to encounter this conflict. Ask
- * handle_file() to write the normalized contents to
- * the "preimage" file.
+ * There may be other variants that can cleanly
+ * replay. Try them and update the variant number for
+ * this one.
*/
- handle_file(path, NULL, rerere_path(id, "preimage"));
- if (id->collection->status[variant] & RR_HAS_POSTIMAGE) {
- const char *path = rerere_path(id, "postimage");
- if (unlink(path))
- die_errno("cannot unlink stray '%s'", path);
- id->collection->status[variant] &= ~RR_HAS_POSTIMAGE;
- }
- id->collection->status[variant] |= RR_HAS_PREIMAGE;
- fprintf(stderr, "Recorded preimage for '%s'\n", path);
- return;
- } else if (has_rerere_resolution(id)) {
- /* Is there a recorded resolution we could attempt to apply? */
- if (merge(id, path))
- return; /* failed to replay */
+ }
+
+ /* Does any existing resolution apply cleanly? */
+ for (variant = 0; variant < rr_dir->status_nr; variant++) {
+ const int both = RR_HAS_PREIMAGE | RR_HAS_POSTIMAGE;
+ struct rerere_id vid = *id;
+
+ if ((rr_dir->status[variant] & both) != both)
+ continue;
+
+ vid.variant = variant;
+ if (merge(&vid, path))
+ continue; /* failed to replay */
+
+ /*
+ * If there already is a different variant that applies
+ * cleanly, there is no point maintaining our own variant.
+ */
+ if (0 <= id->variant && id->variant != variant)
+ remove_variant(id);
if (rerere_autoupdate)
string_list_insert(update, path);
@@ -863,16 +877,24 @@ static void do_rerere_one_path(struct string_list_item *rr_item,
fprintf(stderr,
"Resolved '%s' using previous resolution.\n",
path);
- } else if (!handle_file(path, NULL, NULL)) {
- /* The user has resolved it. */
- copy_file(rerere_path(id, "postimage"), path, 0666);
- id->collection->status[variant] |= RR_HAS_POSTIMAGE;
- fprintf(stderr, "Recorded resolution for '%s'.\n", path);
- } else {
+ free_rerere_id(rr_item);
+ rr_item->util = NULL;
return;
}
- free_rerere_id(rr_item);
- rr_item->util = NULL;
+
+ /* None of the existing one applies; we need a new variant */
+ assign_variant(id);
+
+ variant = id->variant;
+ handle_file(path, NULL, rerere_path(id, "preimage"));
+ if (id->collection->status[variant] & RR_HAS_POSTIMAGE) {
+ const char *path = rerere_path(id, "postimage");
+ if (unlink(path))
+ die_errno("cannot unlink stray '%s'", path);
+ id->collection->status[variant] &= ~RR_HAS_POSTIMAGE;
+ }
+ id->collection->status[variant] |= RR_HAS_PREIMAGE;
+ fprintf(stderr, "Recorded preimage for '%s'\n", path);
}
static int do_plain_rerere(struct string_list *rr, int fd)
diff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh
index 6fcc6d4e7..b1bda2092 100755
--- a/t/t4200-rerere.sh
+++ b/t/t4200-rerere.sh
@@ -412,7 +412,7 @@ concat_insert () {
cat early && printf "%s\n" "$@" && cat late "$last"
}
-test_expect_failure 'multiple identical conflicts' '
+test_expect_success 'multiple identical conflicts' '
git reset --hard &&
test_seq 1 6 >early &&