diff --git a/rerere.c b/rerere.c index 33b1348ebb..0cf857bcc7 100644 --- a/rerere.c +++ b/rerere.c @@ -30,14 +30,17 @@ static int rerere_dir_alloc; #define RR_HAS_PREIMAGE 2 static struct rerere_dir { unsigned char sha1[20]; - unsigned char status; + int status_alloc, status_nr; + unsigned char *status; } **rerere_dir; static void free_rerere_dirs(void) { int i; - for (i = 0; i < rerere_dir_nr; i++) + for (i = 0; i < rerere_dir_nr; i++) { + free(rerere_dir[i]->status); free(rerere_dir[i]); + } free(rerere_dir); rerere_dir_nr = rerere_dir_alloc = 0; rerere_dir = NULL; @@ -53,17 +56,59 @@ static const char *rerere_id_hex(const struct rerere_id *id) return sha1_to_hex(id->collection->sha1); } +static void fit_variant(struct rerere_dir *rr_dir, int variant) +{ + variant++; + ALLOC_GROW(rr_dir->status, variant, rr_dir->status_alloc); + if (rr_dir->status_nr < variant) { + memset(rr_dir->status + rr_dir->status_nr, + '\0', variant - rr_dir->status_nr); + rr_dir->status_nr = variant; + } +} + +static void assign_variant(struct rerere_id *id) +{ + int variant; + struct rerere_dir *rr_dir = id->collection; + + variant = id->variant; + if (variant < 0) { + variant = 0; /* for now */ + } + fit_variant(rr_dir, variant); + id->variant = variant; +} + const char *rerere_path(const struct rerere_id *id, const char *file) { if (!file) return git_path("rr-cache/%s", rerere_id_hex(id)); - return git_path("rr-cache/%s/%s", rerere_id_hex(id), file); + if (id->variant <= 0) + return git_path("rr-cache/%s/%s", rerere_id_hex(id), file); + + return git_path("rr-cache/%s/%s.%d", + rerere_id_hex(id), file, id->variant); } -static int is_rr_file(const char *name, const char *filename) +static int is_rr_file(const char *name, const char *filename, int *variant) { - return !strcmp(name, filename); + const char *suffix; + char *ep; + + if (!strcmp(name, filename)) { + *variant = 0; + return 1; + } + if (!skip_prefix(name, filename, &suffix) || *suffix != '.') + return 0; + + errno = 0; + *variant = strtol(suffix + 1, &ep, 10); + if (errno || *ep) + return 0; + return 1; } static void scan_rerere_dir(struct rerere_dir *rr_dir) @@ -74,10 +119,15 @@ static void scan_rerere_dir(struct rerere_dir *rr_dir) if (!dir) return; while ((de = readdir(dir)) != NULL) { - if (is_rr_file(de->d_name, "postimage")) - rr_dir->status |= RR_HAS_POSTIMAGE; - else if (is_rr_file(de->d_name, "preimage")) - rr_dir->status |= RR_HAS_PREIMAGE; + int variant; + + if (is_rr_file(de->d_name, "postimage", &variant)) { + fit_variant(rr_dir, variant); + rr_dir->status[variant] |= RR_HAS_POSTIMAGE; + } else if (is_rr_file(de->d_name, "preimage", &variant)) { + fit_variant(rr_dir, variant); + rr_dir->status[variant] |= RR_HAS_PREIMAGE; + } } closedir(dir); } @@ -100,7 +150,9 @@ static struct rerere_dir *find_rerere_dir(const char *hex) if (pos < 0) { rr_dir = xmalloc(sizeof(*rr_dir)); hashcpy(rr_dir->sha1, sha1); - rr_dir->status = 0; + rr_dir->status = NULL; + rr_dir->status_nr = 0; + rr_dir->status_alloc = 0; pos = -1 - pos; /* Make sure the array is big enough ... */ @@ -118,19 +170,27 @@ static struct rerere_dir *find_rerere_dir(const char *hex) static int has_rerere_resolution(const struct rerere_id *id) { const int both = RR_HAS_POSTIMAGE|RR_HAS_PREIMAGE; + int variant = id->variant; - return ((id->collection->status & both) == both); + if (variant < 0) + return 0; + return ((id->collection->status[variant] & both) == both); } static int has_rerere_preimage(const struct rerere_id *id) { - return (id->collection->status & RR_HAS_PREIMAGE); + int variant = id->variant; + + if (variant < 0) + return 0; + return (id->collection->status[variant] & RR_HAS_PREIMAGE); } static struct rerere_id *new_rerere_id_hex(char *hex) { struct rerere_id *id = xmalloc(sizeof(*id)); id->collection = find_rerere_dir(hex); + id->variant = -1; /* not known yet */ return id; } @@ -157,16 +217,26 @@ static void read_rr(struct string_list *rr) char *path; unsigned char sha1[20]; struct rerere_id *id; + int variant; /* There has to be the hash, tab, path and then NUL */ if (buf.len < 42 || get_sha1_hex(buf.buf, sha1)) die("corrupt MERGE_RR"); - if (buf.buf[40] != '\t') + if (buf.buf[40] != '.') { + variant = 0; + path = buf.buf + 40; + } else { + errno = 0; + variant = strtol(buf.buf + 41, &path, 10); + if (errno) + die("corrupt MERGE_RR"); + } + if (*(path++) != '\t') die("corrupt MERGE_RR"); buf.buf[40] = '\0'; - path = buf.buf + 41; id = new_rerere_id_hex(buf.buf); + id->variant = variant; string_list_insert(rr, path)->util = id; } strbuf_release(&buf); @@ -187,9 +257,16 @@ static int write_rr(struct string_list *rr, int out_fd) id = rr->items[i].util; if (!id) continue; - strbuf_addf(&buf, "%s\t%s%c", - rerere_id_hex(id), - rr->items[i].string, 0); + assert(id->variant >= 0); + if (0 < id->variant) + strbuf_addf(&buf, "%s.%d\t%s%c", + rerere_id_hex(id), id->variant, + rr->items[i].string, 0); + else + strbuf_addf(&buf, "%s\t%s%c", + rerere_id_hex(id), + rr->items[i].string, 0); + if (write_in_full(out_fd, buf.buf, buf.len) != buf.len) die("unable to write rerere record"); @@ -752,7 +829,12 @@ static void do_rerere_one_path(struct string_list_item *rr_item, struct string_list *update) { const char *path = rr_item->string; - const struct rerere_id *id = rr_item->util; + struct rerere_id *id = rr_item->util; + int variant; + + if (id->variant < 0) + assign_variant(id); + variant = id->variant; if (!has_rerere_preimage(id)) { /* @@ -761,13 +843,13 @@ static void do_rerere_one_path(struct string_list_item *rr_item, * the "preimage" file. */ handle_file(path, NULL, rerere_path(id, "preimage")); - if (id->collection->status & RR_HAS_POSTIMAGE) { + if (id->collection->status[variant] & RR_HAS_POSTIMAGE) { const char *path = rerere_path(id, "postimage"); if (unlink(path)) die_errno("cannot unlink stray '%s'", path); - id->collection->status &= ~RR_HAS_POSTIMAGE; + id->collection->status[variant] &= ~RR_HAS_POSTIMAGE; } - id->collection->status |= RR_HAS_PREIMAGE; + id->collection->status[variant] |= RR_HAS_PREIMAGE; fprintf(stderr, "Recorded preimage for '%s'\n", path); return; } else if (has_rerere_resolution(id)) { @@ -784,7 +866,7 @@ static void do_rerere_one_path(struct string_list_item *rr_item, } else if (!handle_file(path, NULL, NULL)) { /* The user has resolved it. */ copy_file(rerere_path(id, "postimage"), path, 0666); - id->collection->status |= RR_HAS_POSTIMAGE; + id->collection->status[variant] |= RR_HAS_POSTIMAGE; fprintf(stderr, "Recorded resolution for '%s'.\n", path); } else { return; @@ -919,6 +1001,7 @@ 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 */ filename = rerere_path(id, "postimage"); if (unlink(filename)) return (errno == ENOENT diff --git a/rerere.h b/rerere.h index faf5a2308d..64d38d9704 100644 --- a/rerere.h +++ b/rerere.h @@ -18,6 +18,7 @@ extern void *RERERE_RESOLVED; struct rerere_dir; struct rerere_id { struct rerere_dir *collection; + int variant; }; extern int setup_rerere(struct string_list *, int);