rerere: refactor rerere logic to make it independent from I/O
authorJunio C Hamano <gitster@pobox.com>
Fri, 25 Dec 2009 22:34:53 +0000 (14:34 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 11 Jan 2010 02:03:21 +0000 (18:03 -0800)
This splits the handle_file() function into in-core part and I/O
parts of the logic to create the preimage, so that we can compute
the conflict identifier without having to use temporary files.

Earlier, I thought the output from handle_file() should also be
refactored, but it is always about writing preimage (or thisimage)
that is used for later three-way merge, so it is saner to keep it
to always write to FILE *.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
rerere.c

index 88bb4f102c22b217ef02d064eb57ae9729daf473..db1d42f1b61777c0099de01c4e917a57e1f68dca 100644 (file)
--- a/rerere.c
+++ b/rerere.c
@@ -83,8 +83,37 @@ static inline void ferr_puts(const char *s, FILE *fp, int *err)
        ferr_write(s, strlen(s), fp, err);
 }
 
-static int handle_file(const char *path,
-        unsigned char *sha1, const char *output)
+struct rerere_io {
+       int (*getline)(struct strbuf *, struct rerere_io *);
+       FILE *output;
+       int wrerror;
+       /* some more stuff */
+};
+
+static void rerere_io_putstr(const char *str, struct rerere_io *io)
+{
+       if (io->output)
+               ferr_puts(str, io->output, &io->wrerror);
+}
+
+static void rerere_io_putmem(const char *mem, size_t sz, struct rerere_io *io)
+{
+       if (io->output)
+               ferr_write(mem, sz, io->output, &io->wrerror);
+}
+
+struct rerere_io_file {
+       struct rerere_io io;
+       FILE *input;
+};
+
+static int rerere_file_getline(struct strbuf *sb, struct rerere_io *io_)
+{
+       struct rerere_io_file *io = (struct rerere_io_file *)io_;
+       return strbuf_getwholeline(sb, io->input, '\n');
+}
+
+static int handle_path(unsigned char *sha1, struct rerere_io *io)
 {
        git_SHA_CTX ctx;
        int hunk_no = 0;
@@ -93,25 +122,11 @@ static int handle_file(const char *path,
        } hunk = RR_CONTEXT;
        struct strbuf one = STRBUF_INIT, two = STRBUF_INIT;
        struct strbuf buf = STRBUF_INIT;
-       FILE *f = fopen(path, "r");
-       FILE *out = NULL;
-       int wrerror = 0;
-
-       if (!f)
-               return error("Could not open %s", path);
-
-       if (output) {
-               out = fopen(output, "w");
-               if (!out) {
-                       fclose(f);
-                       return error("Could not write %s", output);
-               }
-       }
 
        if (sha1)
                git_SHA1_Init(&ctx);
 
-       while (!strbuf_getwholeline(&buf, f, '\n')) {
+       while (!io->getline(&buf, io)) {
                if (!prefixcmp(buf.buf, "<<<<<<< ")) {
                        if (hunk != RR_CONTEXT)
                                goto bad;
@@ -131,13 +146,11 @@ static int handle_file(const char *path,
                                strbuf_swap(&one, &two);
                        hunk_no++;
                        hunk = RR_CONTEXT;
-                       if (out) {
-                               ferr_puts("<<<<<<<\n", out, &wrerror);
-                               ferr_write(one.buf, one.len, out, &wrerror);
-                               ferr_puts("=======\n", out, &wrerror);
-                               ferr_write(two.buf, two.len, out, &wrerror);
-                               ferr_puts(">>>>>>>\n", out, &wrerror);
-                       }
+                       rerere_io_putstr("<<<<<<<\n", io);
+                       rerere_io_putmem(one.buf, one.len, io);
+                       rerere_io_putstr("=======\n", io);
+                       rerere_io_putmem(two.buf, two.len, io);
+                       rerere_io_putstr(">>>>>>>\n", io);
                        if (sha1) {
                                git_SHA1_Update(&ctx, one.buf ? one.buf : "",
                                            one.len + 1);
@@ -152,8 +165,8 @@ static int handle_file(const char *path,
                        ; /* discard */
                else if (hunk == RR_SIDE_2)
                        strbuf_addstr(&two, buf.buf);
-               else if (out)
-                       ferr_puts(buf.buf, out, &wrerror);
+               else
+                       rerere_io_putstr(buf.buf, io);
                continue;
        bad:
                hunk = 99; /* force error exit */
@@ -163,21 +176,49 @@ static int handle_file(const char *path,
        strbuf_release(&two);
        strbuf_release(&buf);
 
-       fclose(f);
-       if (wrerror)
-               error("There were errors while writing %s (%s)",
-                     path, strerror(wrerror));
-       if (out && fclose(out))
-               wrerror = error("Failed to flush %s: %s",
-                               path, strerror(errno));
        if (sha1)
                git_SHA1_Final(sha1, &ctx);
-       if (hunk != RR_CONTEXT) {
+       if (hunk != RR_CONTEXT)
+               return -1;
+       return hunk_no;
+}
+
+static int handle_file(const char *path, unsigned char *sha1, const char *output)
+{
+       int hunk_no = 0;
+       struct rerere_io_file io;
+
+       memset(&io, 0, sizeof(io));
+       io.io.getline = rerere_file_getline;
+       io.input = fopen(path, "r");
+       io.io.wrerror = 0;
+       if (!io.input)
+               return error("Could not open %s", path);
+
+       if (output) {
+               io.io.output = fopen(output, "w");
+               if (!io.io.output) {
+                       fclose(io.input);
+                       return error("Could not write %s", output);
+               }
+       }
+
+       hunk_no = handle_path(sha1, (struct rerere_io *)&io);
+
+       fclose(io.input);
+       if (io.io.wrerror)
+               error("There were errors while writing %s (%s)",
+                     path, strerror(io.io.wrerror));
+       if (io.io.output && fclose(io.io.output))
+               io.io.wrerror = error("Failed to flush %s: %s",
+                                     path, strerror(errno));
+
+       if (hunk_no < 0) {
                if (output)
                        unlink_or_warn(output);
                return error("Could not parse conflict hunks in %s", path);
        }
-       if (wrerror)
+       if (io.io.wrerror)
                return -1;
        return hunk_no;
 }