rerere: allow multiple variants to exist

The shape of the conflict in a path determines the conflict ID. The preimage and postimage pair that was recorded for the conflict ID previously may or may not replay well for the conflict we just saw. Currently, we punt when the previous resolution does not cleanly replay, but ideally we should then be able to record the currently conflicted path by assigning a new 'variant', and then record the resolution the user is going to make. Introduce a mechanism to have more than one variant for a given conflict ID; we do not actually assign any variant other than 0th variant yet at this step. Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Jul 23, 2015 at 14:23 UTC a13d13700b05442855447670d7c3313f99f5da3c
2 files changed +106 -22
rerere.c
+105 -22
@@ -30,14 +30,17 @@ static int rerere_dir_alloc;
30 #define RR_HAS_PREIMAGE 2
31 static struct rerere_dir {
32 unsigned char sha1[20];
33 - unsigned char status;
33 + int status_alloc, status_nr;
34 + unsigned char *status;
35 } **rerere_dir;
36
37 static void free_rerere_dirs(void)
38 {
39 int i;
39 - for (i = 0; i < rerere_dir_nr; i++)
40 + for (i = 0; i < rerere_dir_nr; i++) {
41 + free(rerere_dir[i]->status);
42 free(rerere_dir[i]);
43 + }
44 free(rerere_dir);
45 rerere_dir_nr = rerere_dir_alloc = 0;
46 rerere_dir = NULL;
@@ -53,17 +56,59 @@ static const char *rerere_id_hex(const struct rerere_id *id)
56 return sha1_to_hex(id->collection->sha1);
57 }
58
59 +static void fit_variant(struct rerere_dir *rr_dir, int variant)
60 +{
61 + variant++;
62 + ALLOC_GROW(rr_dir->status, variant, rr_dir->status_alloc);
63 + if (rr_dir->status_nr < variant) {
64 + memset(rr_dir->status + rr_dir->status_nr,
65 + '\0', variant - rr_dir->status_nr);
66 + rr_dir->status_nr = variant;
67 + }
68 +}
69 +
70 +static void assign_variant(struct rerere_id *id)
71 +{
72 + int variant;
73 + struct rerere_dir *rr_dir = id->collection;
74 +
75 + variant = id->variant;
76 + if (variant < 0) {
77 + variant = 0; /* for now */
78 + }
79 + fit_variant(rr_dir, variant);
80 + id->variant = variant;
81 +}
82 +
83 const char *rerere_path(const struct rerere_id *id, const char *file)
84 {
85 if (!file)
86 return git_path("rr-cache/%s", rerere_id_hex(id));
87
61 - return git_path("rr-cache/%s/%s", rerere_id_hex(id), file);
88 + if (id->variant <= 0)
89 + return git_path("rr-cache/%s/%s", rerere_id_hex(id), file);
90 +
91 + return git_path("rr-cache/%s/%s.%d",
92 + rerere_id_hex(id), file, id->variant);
93 }
94
64 -static int is_rr_file(const char *name, const char *filename)
95 +static int is_rr_file(const char *name, const char *filename, int *variant)
96 {
66 - return !strcmp(name, filename);
97 + const char *suffix;
98 + char *ep;
99 +
100 + if (!strcmp(name, filename)) {
101 + *variant = 0;
102 + return 1;
103 + }
104 + if (!skip_prefix(name, filename, &suffix) || *suffix != '.')
105 + return 0;
106 +
107 + errno = 0;
108 + *variant = strtol(suffix + 1, &ep, 10);
109 + if (errno || *ep)
110 + return 0;
111 + return 1;
112 }
113
114 static void scan_rerere_dir(struct rerere_dir *rr_dir)
@@ -74,10 +119,15 @@ static void scan_rerere_dir(struct rerere_dir *rr_dir)
119 if (!dir)
120 return;
121 while ((de = readdir(dir)) != NULL) {
77 - if (is_rr_file(de->d_name, "postimage"))
78 - rr_dir->status |= RR_HAS_POSTIMAGE;
79 - else if (is_rr_file(de->d_name, "preimage"))
80 - rr_dir->status |= RR_HAS_PREIMAGE;
122 + int variant;
123 +
124 + if (is_rr_file(de->d_name, "postimage", &variant)) {
125 + fit_variant(rr_dir, variant);
126 + rr_dir->status[variant] |= RR_HAS_POSTIMAGE;
127 + } else if (is_rr_file(de->d_name, "preimage", &variant)) {
128 + fit_variant(rr_dir, variant);
129 + rr_dir->status[variant] |= RR_HAS_PREIMAGE;
130 + }
131 }
132 closedir(dir);
133 }
@@ -100,7 +150,9 @@ static struct rerere_dir *find_rerere_dir(const char *hex)
150 if (pos < 0) {
151 rr_dir = xmalloc(sizeof(*rr_dir));
152 hashcpy(rr_dir->sha1, sha1);
103 - rr_dir->status = 0;
153 + rr_dir->status = NULL;
154 + rr_dir->status_nr = 0;
155 + rr_dir->status_alloc = 0;
156 pos = -1 - pos;
157
158 /* Make sure the array is big enough ... */
@@ -118,19 +170,27 @@ static struct rerere_dir *find_rerere_dir(const char *hex)
170 static int has_rerere_resolution(const struct rerere_id *id)
171 {
172 const int both = RR_HAS_POSTIMAGE|RR_HAS_PREIMAGE;
173 + int variant = id->variant;
174
122 - return ((id->collection->status & both) == both);
175 + if (variant < 0)
176 + return 0;
177 + return ((id->collection->status[variant] & both) == both);
178 }
179
180 static int has_rerere_preimage(const struct rerere_id *id)
181 {
127 - return (id->collection->status & RR_HAS_PREIMAGE);
182 + int variant = id->variant;
183 +
184 + if (variant < 0)
185 + return 0;
186 + return (id->collection->status[variant] & RR_HAS_PREIMAGE);
187 }
188
189 static struct rerere_id *new_rerere_id_hex(char *hex)
190 {
191 struct rerere_id *id = xmalloc(sizeof(*id));
192 id->collection = find_rerere_dir(hex);
193 + id->variant = -1; /* not known yet */
194 return id;
195 }
196
@@ -157,16 +217,26 @@ static void read_rr(struct string_list *rr)
217 char *path;
218 unsigned char sha1[20];
219 struct rerere_id *id;
220 + int variant;
221
222 /* There has to be the hash, tab, path and then NUL */
223 if (buf.len < 42 || get_sha1_hex(buf.buf, sha1))
224 die("corrupt MERGE_RR");
225
165 - if (buf.buf[40] != '\t')
226 + if (buf.buf[40] != '.') {
227 + variant = 0;
228 + path = buf.buf + 40;
229 + } else {
230 + errno = 0;
231 + variant = strtol(buf.buf + 41, &path, 10);
232 + if (errno)
233 + die("corrupt MERGE_RR");
234 + }
235 + if (*(path++) != '\t')
236 die("corrupt MERGE_RR");
237 buf.buf[40] = '\0';
168 - path = buf.buf + 41;
238 id = new_rerere_id_hex(buf.buf);
239 + id->variant = variant;
240 string_list_insert(rr, path)->util = id;
241 }
242 strbuf_release(&buf);
@@ -187,9 +257,16 @@ static int write_rr(struct string_list *rr, int out_fd)
257 id = rr->items[i].util;
258 if (!id)
259 continue;
190 - strbuf_addf(&buf, "%s\t%s%c",
191 - rerere_id_hex(id),
192 - rr->items[i].string, 0);
260 + assert(id->variant >= 0);
261 + if (0 < id->variant)
262 + strbuf_addf(&buf, "%s.%d\t%s%c",
263 + rerere_id_hex(id), id->variant,
264 + rr->items[i].string, 0);
265 + else
266 + strbuf_addf(&buf, "%s\t%s%c",
267 + rerere_id_hex(id),
268 + rr->items[i].string, 0);
269 +
270 if (write_in_full(out_fd, buf.buf, buf.len) != buf.len)
271 die("unable to write rerere record");
272
@@ -752,7 +829,12 @@ static void do_rerere_one_path(struct string_list_item *rr_item,
829 struct string_list *update)
830 {
831 const char *path = rr_item->string;
755 - const struct rerere_id *id = rr_item->util;
832 + struct rerere_id *id = rr_item->util;
833 + int variant;
834 +
835 + if (id->variant < 0)
836 + assign_variant(id);
837 + variant = id->variant;
838
839 if (!has_rerere_preimage(id)) {
840 /*
@@ -761,13 +843,13 @@ static void do_rerere_one_path(struct string_list_item *rr_item,
843 * the "preimage" file.
844 */
845 handle_file(path, NULL, rerere_path(id, "preimage"));
764 - if (id->collection->status & RR_HAS_POSTIMAGE) {
846 + if (id->collection->status[variant] & RR_HAS_POSTIMAGE) {
847 const char *path = rerere_path(id, "postimage");
848 if (unlink(path))
849 die_errno("cannot unlink stray '%s'", path);
768 - id->collection->status &= ~RR_HAS_POSTIMAGE;
850 + id->collection->status[variant] &= ~RR_HAS_POSTIMAGE;
851 }
770 - id->collection->status |= RR_HAS_PREIMAGE;
852 + id->collection->status[variant] |= RR_HAS_PREIMAGE;
853 fprintf(stderr, "Recorded preimage for '%s'\n", path);
854 return;
855 } else if (has_rerere_resolution(id)) {
@@ -784,7 +866,7 @@ static void do_rerere_one_path(struct string_list_item *rr_item,
866 } else if (!handle_file(path, NULL, NULL)) {
867 /* The user has resolved it. */
868 copy_file(rerere_path(id, "postimage"), path, 0666);
787 - id->collection->status |= RR_HAS_POSTIMAGE;
869 + id->collection->status[variant] |= RR_HAS_POSTIMAGE;
870 fprintf(stderr, "Recorded resolution for '%s'.\n", path);
871 } else {
872 return;
@@ -919,6 +1001,7 @@ static int rerere_forget_one_path(const char *path, struct string_list *rr)
1001
1002 /* Nuke the recorded resolution for the conflict */
1003 id = new_rerere_id(sha1);
1004 + id->variant = 0; /* for now */
1005 filename = rerere_path(id, "postimage");
1006 if (unlink(filename))
1007 return (errno == ENOENT
rerere.h
+1
@@ -18,6 +18,7 @@ extern void *RERERE_RESOLVED;
18 struct rerere_dir;
19 struct rerere_id {
20 struct rerere_dir *collection;
21 + int variant;
22 };
23
24 extern int setup_rerere(struct string_list *, int);