refs: add method iterator_begin
[gitweb.git] / rerere.c
index 2b870e02a2bc12cb6925f57b3983021c5224c513..c8b9f407872f431399a4c5c321742c32cded5711 100644 (file)
--- a/rerere.c
+++ b/rerere.c
@@ -21,8 +21,6 @@ static int rerere_enabled = -1;
 /* automatically update cleanly resolved paths to the index */
 static int rerere_autoupdate;
 
-static char *merge_rr_path;
-
 static int rerere_dir_nr;
 static int rerere_dir_alloc;
 
@@ -202,7 +200,7 @@ static struct rerere_id *new_rerere_id(unsigned char *sha1)
 static void read_rr(struct string_list *rr)
 {
        struct strbuf buf = STRBUF_INIT;
-       FILE *in = fopen(merge_rr_path, "r");
+       FILE *in = fopen(git_path_merge_rr(), "r");
 
        if (!in)
                return;
@@ -600,6 +598,8 @@ static int find_conflict(struct string_list *conflict)
 int rerere_remaining(struct string_list *merge_rr)
 {
        int i;
+       if (setup_rerere(merge_rr, RERERE_READONLY))
+               return 0;
        if (read_cache() < 0)
                return error("Could not read index");
 
@@ -621,6 +621,33 @@ int rerere_remaining(struct string_list *merge_rr)
        return 0;
 }
 
+/*
+ * Try using the given conflict resolution "ID" to see
+ * if that recorded conflict resolves cleanly what we
+ * got in the "cur".
+ */
+static int try_merge(const struct rerere_id *id, const char *path,
+                    mmfile_t *cur, mmbuffer_t *result)
+{
+       int ret;
+       mmfile_t base = {NULL, 0}, other = {NULL, 0};
+
+       if (read_mmfile(&base, rerere_path(id, "preimage")) ||
+           read_mmfile(&other, rerere_path(id, "postimage")))
+               ret = 1;
+       else
+               /*
+                * A three-way merge. Note that this honors user-customizable
+                * low-level merge driver settings.
+                */
+               ret = ll_merge(result, path, &base, NULL, cur, "", &other, "", NULL);
+
+       free(base.ptr);
+       free(other.ptr);
+
+       return ret;
+}
+
 /*
  * Find the conflict identified by "id"; the change between its
  * "preimage" (i.e. a previous contents with conflict markers) and its
@@ -635,30 +662,20 @@ static int merge(const struct rerere_id *id, const char *path)
 {
        FILE *f;
        int ret;
-       mmfile_t cur = {NULL, 0}, base = {NULL, 0}, other = {NULL, 0};
+       mmfile_t cur = {NULL, 0};
        mmbuffer_t result = {NULL, 0};
 
        /*
         * Normalize the conflicts in path and write it out to
         * "thisimage" temporary file.
         */
-       if (handle_file(path, NULL, rerere_path(id, "thisimage")) < 0) {
-               ret = 1;
-               goto out;
-       }
-
-       if (read_mmfile(&cur, rerere_path(id, "thisimage")) ||
-           read_mmfile(&base, rerere_path(id, "preimage")) ||
-           read_mmfile(&other, rerere_path(id, "postimage"))) {
+       if ((handle_file(path, NULL, rerere_path(id, "thisimage")) < 0) ||
+           read_mmfile(&cur, rerere_path(id, "thisimage"))) {
                ret = 1;
                goto out;
        }
 
-       /*
-        * A three-way merge. Note that this honors user-customizable
-        * low-level merge driver settings.
-        */
-       ret = ll_merge(&result, path, &base, NULL, &cur, "", &other, "", NULL);
+       ret = try_merge(id, path, &cur, &result);
        if (ret)
                goto out;
 
@@ -684,8 +701,6 @@ static int merge(const struct rerere_id *id, const char *path)
 
 out:
        free(cur.ptr);
-       free(base.ptr);
-       free(other.ptr);
        free(result.ptr);
 
        return ret;
@@ -855,21 +870,21 @@ static void git_rerere_config(void)
        git_config(git_default_config, NULL);
 }
 
+static GIT_PATH_FUNC(git_path_rr_cache, "rr-cache")
+
 static int is_rerere_enabled(void)
 {
-       const char *rr_cache;
        int rr_cache_exists;
 
        if (!rerere_enabled)
                return 0;
 
-       rr_cache = git_path("rr-cache");
-       rr_cache_exists = is_directory(rr_cache);
+       rr_cache_exists = is_directory(git_path_rr_cache());
        if (rerere_enabled < 0)
                return rr_cache_exists;
 
-       if (!rr_cache_exists && mkdir_in_gitdir(rr_cache))
-               die("Could not create directory %s", rr_cache);
+       if (!rr_cache_exists && mkdir_in_gitdir(git_path_rr_cache()))
+               die("Could not create directory %s", git_path_rr_cache());
        return 1;
 }
 
@@ -883,9 +898,11 @@ int setup_rerere(struct string_list *merge_rr, int flags)
 
        if (flags & (RERERE_AUTOUPDATE|RERERE_NOAUTOUPDATE))
                rerere_autoupdate = !!(flags & RERERE_AUTOUPDATE);
-       merge_rr_path = git_pathdup("MERGE_RR");
-       fd = hold_lock_file_for_update(&write_lock, merge_rr_path,
-                                      LOCK_DIE_ON_ERROR);
+       if (flags & RERERE_READONLY)
+               fd = 0;
+       else
+               fd = hold_lock_file_for_update(&write_lock, git_path_merge_rr(),
+                                              LOCK_DIE_ON_ERROR);
        read_rr(merge_rr);
        return fd;
 }
@@ -1023,7 +1040,33 @@ static int rerere_forget_one_path(const char *path, struct string_list *rr)
 
        /* Nuke the recorded resolution for the conflict */
        id = new_rerere_id(sha1);
-       id->variant = 0; /* for now */
+
+       for (id->variant = 0;
+            id->variant < id->collection->status_nr;
+            id->variant++) {
+               mmfile_t cur = { NULL, 0 };
+               mmbuffer_t result = {NULL, 0};
+               int cleanly_resolved;
+
+               if (!has_rerere_resolution(id))
+                       continue;
+
+               handle_cache(path, sha1, rerere_path(id, "thisimage"));
+               if (read_mmfile(&cur, rerere_path(id, "thisimage"))) {
+                       free(cur.ptr);
+                       return error("Failed to update conflicted state in '%s'",
+                                    path);
+               }
+               cleanly_resolved = !try_merge(id, path, &cur, &result);
+               free(result.ptr);
+               free(cur.ptr);
+               if (cleanly_resolved)
+                       break;
+       }
+
+       if (id->collection->status_nr <= id->variant)
+               return error("no remembered resolution for '%s'", path);
+
        filename = rerere_path(id, "postimage");
        if (unlink(filename))
                return (errno == ENOENT
@@ -1059,6 +1102,8 @@ int rerere_forget(struct pathspec *pathspec)
                return error("Could not read index");
 
        fd = setup_rerere(&merge_rr, RERERE_NOAUTOUPDATE);
+       if (fd < 0)
+               return 0;
 
        /*
         * The paths may have been resolved (incorrectly);
@@ -1134,6 +1179,9 @@ void rerere_gc(struct string_list *rr)
        int cutoff_noresolve = 15;
        int cutoff_resolve = 60;
 
+       if (setup_rerere(rr, 0) < 0)
+               return;
+
        git_config_get_int("gc.rerereresolved", &cutoff_resolve);
        git_config_get_int("gc.rerereunresolved", &cutoff_noresolve);
        git_config(git_default_config, NULL);
@@ -1169,6 +1217,7 @@ void rerere_gc(struct string_list *rr)
        for (i = 0; i < to_remove.nr; i++)
                rmdir(git_path("rr-cache/%s", to_remove.items[i].string));
        string_list_clear(&to_remove, 0);
+       rollback_lock_file(&write_lock);
 }
 
 /*
@@ -1182,6 +1231,9 @@ void rerere_clear(struct string_list *merge_rr)
 {
        int i;
 
+       if (setup_rerere(merge_rr, 0) < 0)
+               return;
+
        for (i = 0; i < merge_rr->nr; i++) {
                struct rerere_id *id = merge_rr->items[i].util;
                if (!has_rerere_resolution(id)) {
@@ -1189,5 +1241,6 @@ void rerere_clear(struct string_list *merge_rr)
                        rmdir(rerere_path(id, NULL));
                }
        }
-       unlink_or_warn(git_path("MERGE_RR"));
+       unlink_or_warn(git_path_merge_rr());
+       rollback_lock_file(&write_lock);
 }