rerere: do use multiple variants
[gitweb.git] / rerere.c
index 0cf857bcc784eebd956f1f2b59d4af3eb752692b..353227cdaf9214d48c0d656698c7e2a94847d843 100644 (file)
--- 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)