diff options
Diffstat (limited to 'rerere.c')
-rw-r--r-- | rerere.c | 340 |
1 files changed, 276 insertions, 64 deletions
@@ -1,8 +1,11 @@ #include "cache.h" #include "string-list.h" #include "rerere.h" -#include "xdiff/xdiff.h" #include "xdiff-interface.h" +#include "dir.h" +#include "resolve-undo.h" +#include "ll-merge.h" +#include "attr.h" /* if rerere_enabled == -1, fall back to detection of .git/rr-cache */ static int rerere_enabled = -1; @@ -12,15 +15,15 @@ static int rerere_autoupdate; static char *merge_rr_path; -static const char *rr_path(const char *name, const char *file) +const char *rerere_path(const char *hex, const char *file) { - return git_path("rr-cache/%s/%s", name, file); + return git_path("rr-cache/%s/%s", hex, file); } -static int has_resolution(const char *name) +int has_rerere_resolution(const char *hex) { struct stat st; - return !stat(rr_path(name, "postimage"), &st); + return !stat(rerere_path(hex, "postimage"), &st); } static void read_rr(struct string_list *rr) @@ -61,7 +64,7 @@ static int write_rr(struct string_list *rr, int out_fd) path = rr->items[i].string; length = strlen(path) + 1; if (write_in_full(out_fd, rr->items[i].util, 40) != 40 || - write_in_full(out_fd, "\t", 1) != 1 || + write_str_in_full(out_fd, "\t") != 1 || write_in_full(out_fd, path, length) != length) die("unable to write rerere record"); } @@ -83,61 +86,106 @@ 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_putconflict(int ch, int size, struct rerere_io *io) +{ + char buf[64]; + + while (size) { + if (size < sizeof(buf) - 2) { + memset(buf, ch, size); + buf[size] = '\n'; + buf[size + 1] = '\0'; + size = 0; + } else { + int sz = sizeof(buf) - 1; + if (size <= sz) + sz -= (sz - size) + 1; + memset(buf, ch, sz); + buf[sz] = '\0'; + size -= sz; + } + rerere_io_putstr(buf, io); + } +} + +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 is_cmarker(char *buf, int marker_char, int marker_size, int want_sp) +{ + while (marker_size--) + if (*buf++ != marker_char) + return 0; + if (want_sp && *buf != ' ') + return 0; + return isspace(*buf); +} + +static int handle_path(unsigned char *sha1, struct rerere_io *io, int marker_size) { git_SHA_CTX ctx; - char buf[1024]; int hunk_no = 0; enum { RR_CONTEXT = 0, RR_SIDE_1, RR_SIDE_2, RR_ORIGINAL, } hunk = RR_CONTEXT; struct strbuf one = STRBUF_INIT, two = 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); - } - } + struct strbuf buf = STRBUF_INIT; if (sha1) git_SHA1_Init(&ctx); - while (fgets(buf, sizeof(buf), f)) { - if (!prefixcmp(buf, "<<<<<<< ")) { + while (!io->getline(&buf, io)) { + if (is_cmarker(buf.buf, '<', marker_size, 1)) { if (hunk != RR_CONTEXT) goto bad; hunk = RR_SIDE_1; - } else if (!prefixcmp(buf, "|||||||") && isspace(buf[7])) { + } else if (is_cmarker(buf.buf, '|', marker_size, 0)) { if (hunk != RR_SIDE_1) goto bad; hunk = RR_ORIGINAL; - } else if (!prefixcmp(buf, "=======") && isspace(buf[7])) { + } else if (is_cmarker(buf.buf, '=', marker_size, 0)) { if (hunk != RR_SIDE_1 && hunk != RR_ORIGINAL) goto bad; hunk = RR_SIDE_2; - } else if (!prefixcmp(buf, ">>>>>>> ")) { + } else if (is_cmarker(buf.buf, '>', marker_size, 1)) { if (hunk != RR_SIDE_2) goto bad; if (strbuf_cmp(&one, &two) > 0) 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_putconflict('<', marker_size, io); + rerere_io_putmem(one.buf, one.len, io); + rerere_io_putconflict('=', marker_size, io); + rerere_io_putmem(two.buf, two.len, io); + rerere_io_putconflict('>', marker_size, io); if (sha1) { git_SHA1_Update(&ctx, one.buf ? one.buf : "", one.len + 1); @@ -147,13 +195,13 @@ static int handle_file(const char *path, strbuf_reset(&one); strbuf_reset(&two); } else if (hunk == RR_SIDE_1) - strbuf_addstr(&one, buf); + strbuf_addstr(&one, buf.buf); else if (hunk == RR_ORIGINAL) ; /* discard */ else if (hunk == RR_SIDE_2) - strbuf_addstr(&two, buf); - else if (out) - ferr_puts(buf, out, &wrerror); + strbuf_addstr(&two, buf.buf); + else + rerere_io_putstr(buf.buf, io); continue; bad: hunk = 99; /* force error exit */ @@ -161,26 +209,138 @@ static int handle_file(const char *path, } strbuf_release(&one); 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; + int marker_size = ll_merge_marker_size(path); + + 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, marker_size); + + 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(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; } +struct rerere_io_mem { + struct rerere_io io; + struct strbuf input; +}; + +static int rerere_mem_getline(struct strbuf *sb, struct rerere_io *io_) +{ + struct rerere_io_mem *io = (struct rerere_io_mem *)io_; + char *ep; + size_t len; + + strbuf_release(sb); + if (!io->input.len) + return -1; + ep = strchrnul(io->input.buf, '\n'); + if (*ep == '\n') + ep++; + len = ep - io->input.buf; + strbuf_add(sb, io->input.buf, len); + strbuf_remove(&io->input, 0, len); + return 0; +} + +static int handle_cache(const char *path, unsigned char *sha1, const char *output) +{ + mmfile_t mmfile[3]; + mmbuffer_t result = {NULL, 0}; + struct cache_entry *ce; + int pos, len, i, hunk_no; + struct rerere_io_mem io; + int marker_size = ll_merge_marker_size(path); + + /* + * Reproduce the conflicted merge in-core + */ + len = strlen(path); + pos = cache_name_pos(path, len); + if (0 <= pos) + return -1; + pos = -pos - 1; + + for (i = 0; i < 3; i++) { + enum object_type type; + unsigned long size; + + mmfile[i].size = 0; + mmfile[i].ptr = NULL; + if (active_nr <= pos) + break; + ce = active_cache[pos++]; + if (ce_namelen(ce) != len || memcmp(ce->name, path, len) + || ce_stage(ce) != i + 1) + break; + mmfile[i].ptr = read_sha1_file(ce->sha1, &type, &size); + mmfile[i].size = size; + } + for (i = 0; i < 3; i++) { + if (!mmfile[i].ptr && !mmfile[i].size) + mmfile[i].ptr = xstrdup(""); + } + ll_merge(&result, path, &mmfile[0], + &mmfile[1], "ours", + &mmfile[2], "theirs", 0); + for (i = 0; i < 3; i++) + free(mmfile[i].ptr); + + memset(&io, 0, sizeof(io)); + io.io.getline = rerere_mem_getline; + if (output) + io.io.output = fopen(output, "w"); + else + io.io.output = NULL; + strbuf_init(&io.input, 0); + strbuf_attach(&io.input, result.ptr, result.size, result.size); + + hunk_no = handle_path(sha1, (struct rerere_io *)&io, marker_size); + strbuf_release(&io.input); + if (io.io.output) + fclose(io.io.output); + return hunk_no; +} + static int find_conflict(struct string_list *conflict) { int i; @@ -204,19 +364,19 @@ static int find_conflict(struct string_list *conflict) static int merge(const char *name, const char *path) { int ret; - mmfile_t cur, base, other; + mmfile_t cur = {NULL, 0}, base = {NULL, 0}, other = {NULL, 0}; mmbuffer_t result = {NULL, 0}; - xpparam_t xpp = {XDF_NEED_MINIMAL}; - if (handle_file(path, NULL, rr_path(name, "thisimage")) < 0) + if (handle_file(path, NULL, rerere_path(name, "thisimage")) < 0) return 1; - if (read_mmfile(&cur, rr_path(name, "thisimage")) || - read_mmfile(&base, rr_path(name, "preimage")) || - read_mmfile(&other, rr_path(name, "postimage"))) - return 1; - ret = xdl_merge(&base, &cur, "", &other, "", - &xpp, XDL_MERGE_ZEALOUS, &result); + if (read_mmfile(&cur, rerere_path(name, "thisimage")) || + read_mmfile(&base, rerere_path(name, "preimage")) || + read_mmfile(&other, rerere_path(name, "postimage"))) { + ret = 1; + goto out; + } + ret = ll_merge(&result, path, &base, &cur, "", &other, "", 0); if (!ret) { FILE *f = fopen(path, "w"); if (!f) @@ -229,6 +389,7 @@ static int merge(const char *name, const char *path) strerror(errno)); } +out: free(cur.ptr); free(base.ptr); free(other.ptr); @@ -291,7 +452,7 @@ static int do_plain_rerere(struct string_list *rr, int fd) string_list_insert(path, rr)->util = hex; if (mkdir(git_path("rr-cache/%s", hex), 0755)) continue; - handle_file(path, NULL, rr_path(hex, "preimage")); + handle_file(path, NULL, rerere_path(hex, "preimage")); fprintf(stderr, "Recorded preimage for '%s'\n", path); } } @@ -307,7 +468,7 @@ static int do_plain_rerere(struct string_list *rr, int fd) const char *path = rr->items[i].string; const char *name = (const char *)rr->items[i].util; - if (has_resolution(name)) { + if (has_rerere_resolution(name)) { if (!merge(name, path)) { if (rerere_autoupdate) string_list_insert(path, &update); @@ -326,7 +487,7 @@ static int do_plain_rerere(struct string_list *rr, int fd) continue; fprintf(stderr, "Recorded resolution for '%s'.\n", path); - copy_file(rr_path(name, "postimage"), path, 0666); + copy_file(rerere_path(name, "postimage"), path, 0666); mark_resolved: rr->items[i].util = NULL; } @@ -367,7 +528,7 @@ static int is_rerere_enabled(void) return 1; } -int setup_rerere(struct string_list *merge_rr) +int setup_rerere(struct string_list *merge_rr, int flags) { int fd; @@ -375,6 +536,8 @@ int setup_rerere(struct string_list *merge_rr) if (!is_rerere_enabled()) return -1; + 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); @@ -382,13 +545,62 @@ int setup_rerere(struct string_list *merge_rr) return fd; } -int rerere(void) +int rerere(int flags) { struct string_list merge_rr = { NULL, 0, 0, 1 }; int fd; - fd = setup_rerere(&merge_rr); + fd = setup_rerere(&merge_rr, flags); if (fd < 0) return 0; return do_plain_rerere(&merge_rr, fd); } + +static int rerere_forget_one_path(const char *path, struct string_list *rr) +{ + const char *filename; + char *hex; + unsigned char sha1[20]; + int ret; + + ret = handle_cache(path, sha1, NULL); + if (ret < 1) + return error("Could not parse conflict hunks in '%s'", path); + hex = xstrdup(sha1_to_hex(sha1)); + filename = rerere_path(hex, "postimage"); + if (unlink(filename)) + return (errno == ENOENT + ? error("no remembered resolution for %s", path) + : error("cannot unlink %s: %s", filename, strerror(errno))); + + handle_cache(path, sha1, rerere_path(hex, "preimage")); + fprintf(stderr, "Updated preimage for '%s'\n", path); + + + string_list_insert(path, rr)->util = hex; + fprintf(stderr, "Forgot resolution for %s\n", path); + return 0; +} + +int rerere_forget(const char **pathspec) +{ + int i, fd; + struct string_list conflict = { NULL, 0, 0, 1 }; + struct string_list merge_rr = { NULL, 0, 0, 1 }; + + if (read_cache() < 0) + return error("Could not read index"); + + fd = setup_rerere(&merge_rr, RERERE_NOAUTOUPDATE); + + unmerge_cache(pathspec); + find_conflict(&conflict); + for (i = 0; i < conflict.nr; i++) { + struct string_list_item *it = &conflict.items[i]; + if (!match_pathspec(pathspec, it->string, strlen(it->string), + 0, NULL)) + continue; + rerere_forget_one_path(it->string, &merge_rr); + } + return write_rr(&merge_rr, fd); +} |