@matheusbsbs / matheus-bagliano / commits / 1ee26571e9
Fork 0

traverse_trees(): handle D/F conflict case sanely

traverse_trees() is supposed to call its callback with all the matching entries from the given trees. The current algorithm keeps a pointer to each of the tree being traversed, and feeds the entry with the earliest name to the callback. This breaks down if the trees being traversed looks like this: A B t-1 t t-2 u t/a v When we are currently looking at an entry "t-1" in tree A, and tree B has returned "t", feeding "t" from the B and not feeding anything from A, only because "t-1" sorts later than "t", will miss an entry for a subtree "t" behind the current entry in tree A. This introduces extended_entry_extract() helper function that gives what name is expected from the tree, and implements a mechanism to look-ahead in the tree object using it, to make sure such a case is handled sanely. Traversal in tree A in the above example will first return "t" to match that of B, and then the next request for an entry to A then returns "t-1". This roughly corresponds to what Linus's "prepare for one-entry lookahead" wanted to do, but because this does implement look ahead, t6035 and one more test in t1012 reveal that the approach would not work without adjusting the side that walks the index in unpack_trees() as well. Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Sep 19, 2009 at 14:07 UTC 1ee26571e97c64bccecfd26d1a1c1c631412a1b7
3 files changed +237 -46
t/t1012-read-tree-df.sh
+2 -2
@@ -76,7 +76,7 @@ test_expect_failure '3-way (2)' '
76 EOF
77 '
78
79 -test_expect_success '3-way (3)' '
79 +test_expect_failure '3-way (3)' '
80 settree A-010 &&
81 git read-tree -m -u O-010 A-010 B-010 &&
82 checkindex <<-EOF
@@ -90,7 +90,7 @@ test_expect_success '3-way (3)' '
90 EOF
91 '
92
93 -test_expect_success '2-way (1)' '
93 +test_expect_failure '2-way (1)' '
94 settree O-020 &&
95 git read-tree -m -u O-020 A-020 &&
96 checkindex <<-EOF
t/t6035-merge-dir-to-symlink.sh
+1 -1
@@ -48,7 +48,7 @@ test_expect_success 'setup for merge test' '
48 git tag baseline
49 '
50
51 -test_expect_success 'do not lose a/b-2/c/d in merge (resolve)' '
51 +test_expect_failure 'do not lose a/b-2/c/d in merge (resolve)' '
52 git reset --hard &&
53 git checkout baseline^0 &&
54 git merge -s resolve master &&
tree-walk.c
+234 -43
@@ -60,13 +60,6 @@ void *fill_tree_descriptor(struct tree_desc *desc, const unsigned char *sha1)
60 return buf;
61 }
62
63 -static int entry_compare(struct name_entry *a, struct name_entry *b)
64 -{
65 - return df_name_compare(
66 - a->path, tree_entry_len(a->path, a->sha1), a->mode,
67 - b->path, tree_entry_len(b->path, b->sha1), b->mode);
68 -}
69 -
63 static void entry_clear(struct name_entry *a)
64 {
65 memset(a, 0, sizeof(*a));
@@ -138,66 +131,264 @@ char *make_traverse_path(char *path, const struct traverse_info *info, const str
131 return path;
132 }
133
134 +struct tree_desc_skip {
135 + struct tree_desc_skip *prev;
136 + const void *ptr;
137 +};
138 +
139 +struct tree_desc_x {
140 + struct tree_desc d;
141 + struct tree_desc_skip *skip;
142 +};
143 +
144 +static int name_compare(const char *a, int a_len,
145 + const char *b, int b_len)
146 +{
147 + int len = (a_len < b_len) ? a_len : b_len;
148 + int cmp = memcmp(a, b, len);
149 + if (cmp)
150 + return cmp;
151 + return (a_len - b_len);
152 +}
153 +
154 +static int check_entry_match(const char *a, int a_len, const char *b, int b_len)
155 +{
156 + /*
157 + * The caller wants to pick *a* from a tree or nothing.
158 + * We are looking at *b* in a tree.
159 + *
160 + * (0) If a and b are the same name, we are trivially happy.
161 + *
162 + * There are three possibilities where *a* could be hiding
163 + * behind *b*.
164 + *
165 + * (1) *a* == "t", *b* == "ab" i.e. *b* sorts earlier than *a* no
166 + * matter what.
167 + * (2) *a* == "t", *b* == "t-2" and "t" is a subtree in the tree;
168 + * (3) *a* == "t-2", *b* == "t" and "t-2" is a blob in the tree.
169 + *
170 + * Otherwise we know *a* won't appear in the tree without
171 + * scanning further.
172 + */
173 +
174 + int cmp = name_compare(a, a_len, b, b_len);
175 +
176 + /* Most common case first -- reading sync'd trees */
177 + if (!cmp)
178 + return cmp;
179 +
180 + if (0 < cmp) {
181 + /* a comes after b; it does not matter if it is case (3)
182 + if (b_len < a_len && !memcmp(a, b, b_len) && a[b_len] < '/')
183 + return 1;
184 + */
185 + return 1; /* keep looking */
186 + }
187 +
188 + /* b comes after a; are we looking at case (2)? */
189 + if (a_len < b_len && !memcmp(a, b, a_len) && b[a_len] < '/')
190 + return 1; /* keep looking */
191 +
192 + return -1; /* a cannot appear in the tree */
193 +}
194 +
195 +/*
196 + * From the extended tree_desc, extract the first name entry, while
197 + * paying attention to the candidate "first" name. Most importantly,
198 + * when looking for an entry, if there are entries that sorts earlier
199 + * in the tree object representation than that name, skip them and
200 + * process the named entry first. We will remember that we haven't
201 + * processed the first entry yet, and in the later call skip the
202 + * entry we processed early when update_extended_entry() is called.
203 + *
204 + * E.g. if the underlying tree object has these entries:
205 + *
206 + * blob "t-1"
207 + * blob "t-2"
208 + * tree "t"
209 + * blob "t=1"
210 + *
211 + * and the "first" asks for "t", remember that we still need to
212 + * process "t-1" and "t-2" but extract "t". After processing the
213 + * entry "t" from this call, the caller will let us know by calling
214 + * update_extended_entry() that we can remember "t" has been processed
215 + * already.
216 + */
217 +
218 +static void extended_entry_extract(struct tree_desc_x *t,
219 + struct name_entry *a,
220 + const char *first,
221 + int first_len)
222 +{
223 + const char *path;
224 + int len;
225 + struct tree_desc probe;
226 + struct tree_desc_skip *skip;
227 +
228 + /*
229 + * Extract the first entry from the tree_desc, but skip the
230 + * ones that we already returned in earlier rounds.
231 + */
232 + while (1) {
233 + if (!t->d.size) {
234 + entry_clear(a);
235 + break; /* not found */
236 + }
237 + entry_extract(&t->d, a);
238 + for (skip = t->skip; skip; skip = skip->prev)
239 + if (a->path == skip->ptr)
240 + break; /* found */
241 + if (!skip)
242 + break;
243 + /* We have processed this entry already. */
244 + update_tree_entry(&t->d);
245 + }
246 +
247 + if (!first || !a->path)
248 + return;
249 +
250 + /*
251 + * The caller wants "first" from this tree, or nothing.
252 + */
253 + path = a->path;
254 + len = tree_entry_len(a->path, a->sha1);
255 + switch (check_entry_match(first, first_len, path, len)) {
256 + case -1:
257 + entry_clear(a);
258 + case 0:
259 + return;
260 + default:
261 + break;
262 + }
263 +
264 + /*
265 + * We need to look-ahead -- we suspect that a subtree whose
266 + * name is "first" may be hiding behind the current entry "path".
267 + */
268 + probe = t->d;
269 + while (probe.size) {
270 + entry_extract(&probe, a);
271 + path = a->path;
272 + len = tree_entry_len(a->path, a->sha1);
273 + switch (check_entry_match(first, first_len, path, len)) {
274 + case -1:
275 + entry_clear(a);
276 + case 0:
277 + return;
278 + default:
279 + update_tree_entry(&probe);
280 + break;
281 + }
282 + /* keep looking */
283 + }
284 + entry_clear(a);
285 +}
286 +
287 +static void update_extended_entry(struct tree_desc_x *t, struct name_entry *a)
288 +{
289 + if (t->d.entry.path == a->path) {
290 + update_tree_entry(&t->d);
291 + } else {
292 + /* we have returned this entry early */
293 + struct tree_desc_skip *skip = xmalloc(sizeof(*skip));
294 + skip->ptr = a->path;
295 + skip->prev = t->skip;
296 + t->skip = skip;
297 + }
298 +}
299 +
300 +static void free_extended_entry(struct tree_desc_x *t)
301 +{
302 + struct tree_desc_skip *p, *s;
303 +
304 + for (s = t->skip; s; s = p) {
305 + p = s->prev;
306 + free(s);
307 + }
308 +}
309 +
310 int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
311 {
312 int ret = 0;
313 struct name_entry *entry = xmalloc(n*sizeof(*entry));
314 + int i;
315 + struct tree_desc_x *tx = xcalloc(n, sizeof(*tx));
316 +
317 + for (i = 0; i < n; i++)
318 + tx[i].d = t[i];
319
320 for (;;) {
147 - unsigned long mask = 0;
148 - unsigned long dirmask = 0;
149 - int i, last;
321 + unsigned long mask, dirmask;
322 + const char *first = NULL;
323 + int first_len = 0;
324 + struct name_entry *e;
325 + int len;
326
151 - last = -1;
327 for (i = 0; i < n; i++) {
153 - if (!t[i].size)
328 + e = entry + i;
329 + extended_entry_extract(tx + i, e, NULL, 0);
330 + }
331 +
332 + /*
333 + * A tree may have "t-2" at the current location even
334 + * though it may have "t" that is a subtree behind it,
335 + * and another tree may return "t". We want to grab
336 + * all "t" from all trees to match in such a case.
337 + */
338 + for (i = 0; i < n; i++) {
339 + e = entry + i;
340 + if (!e->path)
341 continue;
155 - entry_extract(t+i, entry+i);
156 - if (last >= 0) {
157 - int cmp = entry_compare(entry+i, entry+last);
158 -
159 - /*
160 - * Is the new name bigger than the old one?
161 - * Ignore it
162 - */
163 - if (cmp > 0)
342 + len = tree_entry_len(e->path, e->sha1);
343 + if (!first) {
344 + first = e->path;
345 + first_len = len;
346 + continue;
347 + }
348 + if (name_compare(e->path, len, first, first_len) < 0) {
349 + first = e->path;
350 + first_len = len;
351 + }
352 + }
353 +
354 + if (first) {
355 + for (i = 0; i < n; i++) {
356 + e = entry + i;
357 + extended_entry_extract(tx + i, e, first, first_len);
358 + /* Cull the ones that are not the earliest */
359 + if (!e->path)
360 continue;
165 - /*
166 - * Is the new name smaller than the old one?
167 - * Ignore all old ones
168 - */
169 - if (cmp < 0)
170 - mask = 0;
361 + len = tree_entry_len(e->path, e->sha1);
362 + if (name_compare(e->path, len, first, first_len))
363 + entry_clear(e);
364 }
365 + }
366 +
367 + /* Now we have in entry[i] the earliest name from the trees */
368 + mask = 0;
369 + dirmask = 0;
370 + for (i = 0; i < n; i++) {
371 + if (!entry[i].path)
372 + continue;
373 mask |= 1ul << i;
374 if (S_ISDIR(entry[i].mode))
375 dirmask |= 1ul << i;
175 - last = i;
376 }
377 if (!mask)
378 break;
179 - dirmask &= mask;
180 -
181 - /*
182 - * Clear all the unused name-entries.
183 - */
184 - for (i = 0; i < n; i++) {
185 - if (mask & (1ul << i))
186 - continue;
187 - entry_clear(entry + i);
188 - }
379 ret = info->fn(n, mask, dirmask, entry, info);
380 if (ret < 0)
381 break;
192 - if (ret)
193 - mask &= ret;
382 + mask &= ret;
383 ret = 0;
195 - for (i = 0; i < n; i++) {
384 + for (i = 0; i < n; i++)
385 if (mask & (1ul << i))
197 - update_tree_entry(t + i);
198 - }
386 + update_extended_entry(tx + i, entry + i);
387 }
388 free(entry);
389 + for (i = 0; i < n; i++)
390 + free_extended_entry(tx + i);
391 + free(tx);
392 return ret;
393 }
394