expose a helper function peel_to_type().
[gitweb.git] / builtin-rerere.c
index 318d959d89669f09e12b1b97ca62f100e1a58ecd..b0c17bde879d42c16fe5c7f0756763cd638f8bac 100644 (file)
@@ -1,3 +1,4 @@
+#include "builtin.h"
 #include "cache.h"
 #include "path-list.h"
 #include "xdiff/xdiff.h"
@@ -12,6 +13,9 @@ static const char git_rerere_usage[] =
 static int cutoff_noresolve = 15;
 static int cutoff_resolve = 60;
 
+/* if rerere_enabled == -1, fall back to detection of .git/rr-cache */
+static int rerere_enabled = -1;
+
 static char *merge_rr_path;
 
 static const char *rr_path(const char *name, const char *file)
@@ -57,25 +61,9 @@ static int write_rr(struct path_list *rr, int out_fd)
                    write_in_full(out_fd, path, length) != length)
                        die("unable to write rerere record");
        }
-       close(out_fd);
-       return commit_lock_file(&write_lock);
-}
-
-struct buffer {
-       char *ptr;
-       int nr, alloc;
-};
-
-static void append_line(struct buffer *buffer, const char *line)
-{
-       int len = strlen(line);
-
-       if (buffer->nr + len > buffer->alloc) {
-               buffer->alloc = alloc_nr(buffer->nr + len);
-               buffer->ptr = xrealloc(buffer->ptr, buffer->alloc);
-       }
-       memcpy(buffer->ptr + buffer->nr, line, len);
-       buffer->nr += len;
+       if (commit_lock_file(&write_lock) != 0)
+               die("unable to write rerere record");
+       return 0;
 }
 
 static int handle_file(const char *path,
@@ -84,10 +72,9 @@ static int handle_file(const char *path,
        SHA_CTX ctx;
        char buf[1024];
        int hunk = 0, hunk_no = 0;
-       struct buffer minus = { NULL, 0, 0 }, plus = { NULL, 0, 0 };
-       struct buffer *one = &minus, *two = +
+       struct strbuf one, two;
        FILE *f = fopen(path, "r");
-       FILE *out;
+       FILE *out = NULL;
 
        if (!f)
                return error("Could not open %s", path);
@@ -98,46 +85,50 @@ static int handle_file(const char *path,
                        fclose(f);
                        return error("Could not write %s", output);
                }
-       } else
-               out = NULL;
+       }
 
        if (sha1)
                SHA1_Init(&ctx);
 
+       strbuf_init(&one, 0);
+       strbuf_init(&two,  0);
        while (fgets(buf, sizeof(buf), f)) {
-               if (!strncmp("<<<<<<< ", buf, 8))
+               if (!prefixcmp(buf, "<<<<<<< "))
                        hunk = 1;
-               else if (!strncmp("=======", buf, 7))
+               else if (!prefixcmp(buf, "======="))
                        hunk = 2;
-               else if (!strncmp(">>>>>>> ", buf, 8)) {
+               else if (!prefixcmp(buf, ">>>>>>> ")) {
+                       int cmp = strbuf_cmp(&one, &two);
+
                        hunk_no++;
                        hunk = 0;
-                       if (memcmp(one->ptr, two->ptr, one->nr < two->nr ?
-                                               one->nr : two->nr) > 0) {
-                               struct buffer *swap = one;
-                               one = two;
-                               two = swap;
+                       if (cmp > 0) {
+                               strbuf_swap(&one, &two);
                        }
                        if (out) {
                                fputs("<<<<<<<\n", out);
-                               fwrite(one->ptr, one->nr, 1, out);
+                               fwrite(one.buf, one.len, 1, out);
                                fputs("=======\n", out);
-                               fwrite(two->ptr, two->nr, 1, out);
+                               fwrite(two.buf, two.len, 1, out);
                                fputs(">>>>>>>\n", out);
                        }
                        if (sha1) {
-                               SHA1_Update(&ctx, one->ptr, one->nr);
-                               SHA1_Update(&ctx, "\0", 1);
-                               SHA1_Update(&ctx, two->ptr, two->nr);
-                               SHA1_Update(&ctx, "\0", 1);
+                               SHA1_Update(&ctx, one.buf ? one.buf : "",
+                                           one.len + 1);
+                               SHA1_Update(&ctx, two.buf ? two.buf : "",
+                                           two.len + 1);
                        }
+                       strbuf_reset(&one);
+                       strbuf_reset(&two);
                } else if (hunk == 1)
-                       append_line(one, buf);
+                       strbuf_addstr(&one, buf);
                else if (hunk == 2)
-                       append_line(two, buf);
+                       strbuf_addstr(&two, buf);
                else if (out)
                        fputs(buf, out);
        }
+       strbuf_release(&one);
+       strbuf_release(&two);
 
        fclose(f);
        if (out)
@@ -152,15 +143,16 @@ static int find_conflict(struct path_list *conflict)
        int i;
        if (read_cache() < 0)
                return error("Could not read index");
-       for (i = 0; i + 2 < active_nr; i++) {
-               struct cache_entry *e1 = active_cache[i];
-               struct cache_entry *e2 = active_cache[i + 1];
-               struct cache_entry *e3 = active_cache[i + 2];
-               if (ce_stage(e1) == 1 && ce_stage(e2) == 2 &&
-                               ce_stage(e3) == 3 && ce_same_name(e1, e2) &&
-                               ce_same_name(e1, e3)) {
-                       path_list_insert((const char *)e1->name, conflict);
-                       i += 3;
+       for (i = 0; i+1 < active_nr; i++) {
+               struct cache_entry *e2 = active_cache[i];
+               struct cache_entry *e3 = active_cache[i+1];
+               if (ce_stage(e2) == 2 &&
+                   ce_stage(e3) == 3 &&
+                   ce_same_name(e2, e3) &&
+                   S_ISREG(e2->ce_mode) &&
+                   S_ISREG(e3->ce_mode)) {
+                       path_list_insert((const char *)e2->name, conflict);
+                       i++; /* skip over both #2 and #3 */
                }
        }
        return 0;
@@ -265,10 +257,10 @@ static int diff_two(const char *file1, const char *label1,
        printf("--- a/%s\n+++ b/%s\n", label1, label2);
        fflush(stdout);
        xpp.flags = XDF_NEED_MINIMAL;
+       memset(&xecfg, 0, sizeof(xecfg));
        xecfg.ctxlen = 3;
-       xecfg.flags = 0;
        ecb.outf = outf;
-       xdl_diff(&minus, &plus, &xpp, &xecfg, &ecb);
+       xdi_diff(&minus, &plus, &xpp, &xecfg, &ecb);
 
        free(minus.ptr);
        free(plus.ptr);
@@ -370,30 +362,72 @@ static int git_rerere_config(const char *var, const char *value)
                cutoff_resolve = git_config_int(var, value);
        else if (!strcmp(var, "gc.rerereunresolved"))
                cutoff_noresolve = git_config_int(var, value);
+       else if (!strcmp(var, "rerere.enabled"))
+               rerere_enabled = git_config_bool(var, value);
        else
                return git_default_config(var, value);
        return 0;
 }
 
-int cmd_rerere(int argc, const char **argv, const char *prefix)
+static int is_rerere_enabled(void)
 {
-       struct path_list merge_rr = { NULL, 0, 0, 1 };
-       int i, fd = -1;
        struct stat st;
+       const char *rr_cache;
+       int rr_cache_exists;
 
-       if (stat(git_path("rr-cache"), &st) || !S_ISDIR(st.st_mode))
+       if (!rerere_enabled)
                return 0;
 
+       rr_cache = git_path("rr-cache");
+       rr_cache_exists = !stat(rr_cache, &st) && S_ISDIR(st.st_mode);
+       if (rerere_enabled < 0)
+               return rr_cache_exists;
+
+       if (!rr_cache_exists &&
+           (mkdir(rr_cache, 0777) || adjust_shared_perm(rr_cache)))
+               die("Could not create directory %s", rr_cache);
+       return 1;
+}
+
+static int setup_rerere(struct path_list *merge_rr)
+{
+       int fd;
+
        git_config(git_rerere_config);
+       if (!is_rerere_enabled())
+               return -1;
 
        merge_rr_path = xstrdup(git_path("rr-cache/MERGE_RR"));
        fd = hold_lock_file_for_update(&write_lock, merge_rr_path, 1);
-       read_rr(&merge_rr);
+       read_rr(merge_rr);
+       return fd;
+}
+
+int rerere(void)
+{
+       struct path_list merge_rr = { NULL, 0, 0, 1 };
+       int fd;
+
+       fd = setup_rerere(&merge_rr);
+       if (fd < 0)
+               return 0;
+       return do_plain_rerere(&merge_rr, fd);
+}
+
+int cmd_rerere(int argc, const char **argv, const char *prefix)
+{
+       struct path_list merge_rr = { NULL, 0, 0, 1 };
+       int i, fd;
+
+       fd = setup_rerere(&merge_rr);
+       if (fd < 0)
+               return 0;
 
        if (argc < 2)
                return do_plain_rerere(&merge_rr, fd);
        else if (!strcmp(argv[1], "clear")) {
                for (i = 0; i < merge_rr.nr; i++) {
+                       struct stat st;
                        const char *name = (const char *)merge_rr.items[i].util;
                        if (!stat(git_path("rr-cache/%s", name), &st) &&
                                        S_ISDIR(st.st_mode) &&
@@ -418,4 +452,3 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)
        path_list_clear(&merge_rr, 1);
        return 0;
 }
-