diff options
Diffstat (limited to 'rerere.c')
-rw-r--r-- | rerere.c | 394 |
1 files changed, 317 insertions, 77 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"); } @@ -70,69 +73,135 @@ static int write_rr(struct string_list *rr, int out_fd) return 0; } -static int handle_file(const char *path, - unsigned char *sha1, const char *output) +static void ferr_write(const void *p, size_t count, FILE *fp, int *err) { - SHA_CTX ctx; - char buf[1024]; - int hunk = 0, hunk_no = 0; - struct strbuf one, two; - FILE *f = fopen(path, "r"); - FILE *out = NULL; - - if (!f) - return error("Could not open %s", path); + if (!count || *err) + return; + if (fwrite(p, count, 1, fp) != 1) + *err = errno; +} - if (output) { - out = fopen(output, "w"); - if (!out) { - fclose(f); - return error("Could not write %s", output); +static inline void ferr_puts(const char *s, FILE *fp, int *err) +{ + ferr_write(s, strlen(s), fp, err); +} + +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; + 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; + struct strbuf buf = STRBUF_INIT; if (sha1) - SHA1_Init(&ctx); + git_SHA1_Init(&ctx); - strbuf_init(&one, 0); - strbuf_init(&two, 0); - while (fgets(buf, sizeof(buf), f)) { - if (!prefixcmp(buf, "<<<<<<< ")) { - if (hunk) + 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 (is_cmarker(buf.buf, '|', marker_size, 0)) { + if (hunk != RR_SIDE_1) goto bad; - hunk = 1; - } else if (!prefixcmp(buf, "=======") && isspace(buf[7])) { - if (hunk != 1) + hunk = RR_ORIGINAL; + } else if (is_cmarker(buf.buf, '=', marker_size, 0)) { + if (hunk != RR_SIDE_1 && hunk != RR_ORIGINAL) goto bad; - hunk = 2; - } else if (!prefixcmp(buf, ">>>>>>> ")) { - if (hunk != 2) + hunk = RR_SIDE_2; + } 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 = 0; - if (out) { - fputs("<<<<<<<\n", out); - fwrite(one.buf, one.len, 1, out); - fputs("=======\n", out); - fwrite(two.buf, two.len, 1, out); - fputs(">>>>>>>\n", out); - } + hunk = RR_CONTEXT; + 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) { - SHA1_Update(&ctx, one.buf ? one.buf : "", + git_SHA1_Update(&ctx, one.buf ? one.buf : "", one.len + 1); - SHA1_Update(&ctx, two.buf ? two.buf : "", + git_SHA1_Update(&ctx, two.buf ? two.buf : "", two.len + 1); } strbuf_reset(&one); strbuf_reset(&two); - } else if (hunk == 1) - strbuf_addstr(&one, buf); - else if (hunk == 2) - strbuf_addstr(&two, buf); - else if (out) - fputs(buf, out); + } else if (hunk == RR_SIDE_1) + strbuf_addstr(&one, buf.buf); + else if (hunk == RR_ORIGINAL) + ; /* discard */ + else if (hunk == RR_SIDE_2) + strbuf_addstr(&two, buf.buf); + else + rerere_io_putstr(buf.buf, io); continue; bad: hunk = 99; /* force error exit */ @@ -140,17 +209,135 @@ static int handle_file(const char *path, } strbuf_release(&one); strbuf_release(&two); + strbuf_release(&buf); - fclose(f); - if (out) - fclose(out); if (sha1) - SHA1_Final(sha1, &ctx); - if (hunk) { + git_SHA1_Final(sha1, &ctx); + 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 (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; } @@ -179,23 +366,25 @@ static int merge(const char *name, const char *path) int ret; mmfile_t cur, base, other; 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"))) + if (read_mmfile(&cur, rerere_path(name, "thisimage")) || + read_mmfile(&base, rerere_path(name, "preimage")) || + read_mmfile(&other, rerere_path(name, "postimage"))) return 1; - ret = xdl_merge(&base, &cur, "", &other, "", - &xpp, XDL_MERGE_ZEALOUS, &result); + ret = ll_merge(&result, path, &base, &cur, "", &other, "", 0); if (!ret) { FILE *f = fopen(path, "w"); if (!f) - return error("Could not write to %s", path); - fwrite(result.ptr, result.size, 1, f); - fclose(f); + return error("Could not open %s: %s", path, + strerror(errno)); + if (fwrite(result.ptr, result.size, 1, f) != 1) + error("Could not write %s: %s", path, strerror(errno)); + if (fclose(f)) + return error("Writing %s failed: %s", path, + strerror(errno)); } free(cur.ptr); @@ -259,8 +448,8 @@ static int do_plain_rerere(struct string_list *rr, int fd) hex = xstrdup(sha1_to_hex(sha1)); 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")); + continue; + handle_file(path, NULL, rerere_path(hex, "preimage")); fprintf(stderr, "Recorded preimage for '%s'\n", path); } } @@ -276,7 +465,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); @@ -295,7 +484,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; } @@ -319,7 +508,6 @@ static int git_rerere_config(const char *var, const char *value, void *cb) static int is_rerere_enabled(void) { - struct stat st; const char *rr_cache; int rr_cache_exists; @@ -327,7 +515,7 @@ static int is_rerere_enabled(void) return 0; rr_cache = git_path("rr-cache"); - rr_cache_exists = !stat(rr_cache, &st) && S_ISDIR(st.st_mode); + rr_cache_exists = is_directory(rr_cache); if (rerere_enabled < 0) return rr_cache_exists; @@ -337,7 +525,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; @@ -345,19 +533,71 @@ int setup_rerere(struct string_list *merge_rr) if (!is_rerere_enabled()) return -1; - merge_rr_path = xstrdup(git_path("MERGE_RR")); - fd = hold_lock_file_for_update(&write_lock, merge_rr_path, 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); read_rr(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); +} |