@matheusbsbs / matheus-bagliano / commits / da165f470e
Fork 0

unpack-trees.c: prepare for looking ahead in the index

This prepares but does not yet implement a look-ahead in the index entries when traverse-trees.c decides to give us tree entries in an order that does not match what is in the index. A case where a look-ahead in the index is necessary happens when merging branch B into branch A while the index matches the current branch A, using a tree O as their common ancestor, and these three trees looks like this: O A B t t t-i t-i t-i t-j t-j t/1 t/2 The traverse_trees() function gets "t", "t-i" and "t" from trees O, A and B first, and notices that A may have a matching "t" behind "t-i" and "t-j" (indeed it does), and tells A to give that entry instead. After unpacking blob "t" from tree B (as it hasn't changed since O in B and A removed it, it will result in its removal), it descends into directory "t/". The side that walked index in parallel to the tree traversal used to be implemented with one pointer, o->pos, that points at the next index entry to be processed. When this happens, the pointer o->pos still points at "t-i" that is the first entry. We should be able to skip "t-i" and "t-j" and locate "t/1" from the index while the recursive invocation of traverse_trees() walks and match entries found there, and later come back to process "t-i". While that look-ahead is not implemented yet, this adds a flag bit, CE_UNPACKED, to mark the entries in the index that has already been processed. o->pos pointer has been renamed to o->cache_bottom and it points at the first entry that may still need to be processed. Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Jan 7, 2010 at 14:59 UTC da165f470ede6540873d33cb8bc2ff8e13c60520
4 files changed +173 -65
cache.h
+2
@@ -178,6 +178,8 @@ struct cache_entry {
178 #define CE_HASHED (0x100000)
179 #define CE_UNHASHED (0x200000)
180
181 +#define CE_UNPACKED (0x1000000)
182 +
183 /*
184 * Extended on-disk flags
185 */
diff-lib.c
-18
@@ -359,21 +359,6 @@ static void do_oneway_diff(struct unpack_trees_options *o,
359 show_modified(revs, tree, idx, 1, cached, match_missing);
360 }
361
362 -static inline void skip_same_name(struct cache_entry *ce, struct unpack_trees_options *o)
363 -{
364 - int len = ce_namelen(ce);
365 - const struct index_state *index = o->src_index;
366 -
367 - while (o->pos < index->cache_nr) {
368 - struct cache_entry *next = index->cache[o->pos];
369 - if (len != ce_namelen(next))
370 - break;
371 - if (memcmp(ce->name, next->name, len))
372 - break;
373 - o->pos++;
374 - }
375 -}
376 -
362 /*
363 * The unpack_trees() interface is designed for merging, so
364 * the different source entries are designed primarily for
@@ -395,9 +380,6 @@ static int oneway_diff(struct cache_entry **src, struct unpack_trees_options *o)
380 struct cache_entry *tree = src[1];
381 struct rev_info *revs = o->unpack_data;
382
398 - if (idx && ce_stage(idx))
399 - skip_same_name(idx, o);
400 -
383 /*
384 * Unpack-trees generates a DF/conflict entry if
385 * there was a directory in the index and a tree
unpack-trees.c
+170 -46
@@ -126,18 +126,109 @@ static inline int call_unpack_fn(struct cache_entry **src, struct unpack_trees_o
126 return ret;
127 }
128
129 -static int unpack_index_entry(struct cache_entry *ce, struct unpack_trees_options *o)
129 +static void mark_ce_used(struct cache_entry *ce, struct unpack_trees_options *o)
130 +{
131 + ce->ce_flags |= CE_UNPACKED;
132 +
133 + if (o->cache_bottom < o->src_index->cache_nr &&
134 + o->src_index->cache[o->cache_bottom] == ce) {
135 + int bottom = o->cache_bottom;
136 + while (bottom < o->src_index->cache_nr &&
137 + o->src_index->cache[bottom]->ce_flags & CE_UNPACKED)
138 + bottom++;
139 + o->cache_bottom = bottom;
140 + }
141 +}
142 +
143 +static void mark_all_ce_unused(struct index_state *index)
144 +{
145 + int i;
146 + for (i = 0; i < index->cache_nr; i++)
147 + index->cache[i]->ce_flags &= ~CE_UNPACKED;
148 +}
149 +
150 +static int locate_in_src_index(struct cache_entry *ce,
151 + struct unpack_trees_options *o)
152 +{
153 + struct index_state *index = o->src_index;
154 + int len = ce_namelen(ce);
155 + int pos = index_name_pos(index, ce->name, len);
156 + if (pos < 0)
157 + pos = -1 - pos;
158 + return pos;
159 +}
160 +
161 +/*
162 + * We call unpack_index_entry() with an unmerged cache entry
163 + * only in diff-index, and it wants a single callback. Skip
164 + * the other unmerged entry with the same name.
165 + */
166 +static void mark_ce_used_same_name(struct cache_entry *ce,
167 + struct unpack_trees_options *o)
168 +{
169 + struct index_state *index = o->src_index;
170 + int len = ce_namelen(ce);
171 + int pos;
172 +
173 + for (pos = locate_in_src_index(ce, o); pos < index->cache_nr; pos++) {
174 + struct cache_entry *next = index->cache[pos];
175 + if (len != ce_namelen(next) ||
176 + memcmp(ce->name, next->name, len))
177 + break;
178 + mark_ce_used(next, o);
179 + }
180 +}
181 +
182 +static struct cache_entry *next_cache_entry(struct unpack_trees_options *o)
183 +{
184 + const struct index_state *index = o->src_index;
185 + int pos = o->cache_bottom;
186 +
187 + while (pos < index->cache_nr) {
188 + struct cache_entry *ce = index->cache[pos];
189 + if (!(ce->ce_flags & CE_UNPACKED))
190 + return ce;
191 + pos++;
192 + }
193 + return NULL;
194 +}
195 +
196 +static void add_same_unmerged(struct cache_entry *ce,
197 + struct unpack_trees_options *o)
198 +{
199 + struct index_state *index = o->src_index;
200 + int len = ce_namelen(ce);
201 + int pos = index_name_pos(index, ce->name, len);
202 +
203 + if (0 <= pos)
204 + die("programming error in a caller of mark_ce_used_same_name");
205 + for (pos = -pos - 1; pos < index->cache_nr; pos++) {
206 + struct cache_entry *next = index->cache[pos];
207 + if (len != ce_namelen(next) ||
208 + memcmp(ce->name, next->name, len))
209 + break;
210 + add_entry(o, next, 0, 0);
211 + mark_ce_used(next, o);
212 + }
213 +}
214 +
215 +static int unpack_index_entry(struct cache_entry *ce,
216 + struct unpack_trees_options *o)
217 {
218 struct cache_entry *src[5] = { ce, NULL, };
219 + int ret;
220
133 - o->pos++;
221 + mark_ce_used(ce, o);
222 if (ce_stage(ce)) {
223 if (o->skip_unmerged) {
224 add_entry(o, ce, 0, 0);
225 return 0;
226 }
227 }
140 - return call_unpack_fn(src, o);
228 + ret = call_unpack_fn(src, o);
229 + if (ce_stage(ce))
230 + mark_ce_used_same_name(ce, o);
231 + return ret;
232 }
233
234 static int traverse_trees_recursive(int n, unsigned long dirmask, unsigned long df_conflicts, struct name_entry *names, struct traverse_info *info)
@@ -212,6 +303,20 @@ static int compare_entry(const struct cache_entry *ce, const struct traverse_inf
303 return ce_namelen(ce) > traverse_path_len(info, n);
304 }
305
306 +static int ce_in_traverse_path(const struct cache_entry *ce,
307 + const struct traverse_info *info)
308 +{
309 + if (!info->prev)
310 + return 1;
311 + if (do_compare_entry(ce, info->prev, &info->name))
312 + return 0;
313 + /*
314 + * If ce (blob) is the same name as the path (which is a tree
315 + * we will be descending into), it won't be inside it.
316 + */
317 + return (info->pathlen < ce_namelen(ce));
318 +}
319 +
320 static struct cache_entry *create_ce_entry(const struct traverse_info *info, const struct name_entry *n, int stage)
321 {
322 int len = traverse_path_len(info, n);
@@ -300,23 +405,27 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str
405
406 /* Are we supposed to look at the index too? */
407 if (o->merge) {
303 - while (o->pos < o->src_index->cache_nr) {
304 - struct cache_entry *ce = o->src_index->cache[o->pos];
305 - int cmp = compare_entry(ce, info, p);
408 + while (1) {
409 + struct cache_entry *ce = next_cache_entry(o);
410 + int cmp;
411 + if (!ce)
412 + break;
413 + cmp = compare_entry(ce, info, p);
414 if (cmp < 0) {
415 if (unpack_index_entry(ce, o) < 0)
416 return unpack_failed(o, NULL);
417 continue;
418 }
419 if (!cmp) {
312 - o->pos++;
420 if (ce_stage(ce)) {
421 /*
315 - * If we skip unmerged index entries, we'll skip this
316 - * entry *and* the tree entries associated with it!
422 + * If we skip unmerged index
423 + * entries, we'll skip this
424 + * entry *and* the tree
425 + * entries associated with it!
426 */
427 if (o->skip_unmerged) {
319 - add_entry(o, ce, 0, 0);
428 + add_same_unmerged(ce, o);
429 return mask;
430 }
431 }
@@ -329,6 +438,13 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str
438 if (unpack_nondirectories(n, mask, dirmask, src, names, info) < 0)
439 return -1;
440
441 + if (src[0]) {
442 + if (ce_stage(src[0]))
443 + mark_ce_used_same_name(src[0], o);
444 + else
445 + mark_ce_used(src[0], o);
446 + }
447 +
448 /* Now handle any directories.. */
449 if (dirmask) {
450 unsigned long conflicts = mask & ~dirmask;
@@ -345,11 +461,13 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str
461 matches = cache_tree_matches_traversal(o->src_index->cache_tree,
462 names, info);
463 /*
348 - * Everything under the name matches. Adjust o->pos to
349 - * skip the entire hierarchy.
464 + * Everything under the name matches; skip the
465 + * entire hierarchy. diff_index_cached codepath
466 + * special cases D/F conflicts in such a way that
467 + * it does not do any look-ahead, so this is safe.
468 */
469 if (matches) {
352 - o->pos += matches;
470 + o->cache_bottom += matches;
471 return mask;
472 }
473 }
@@ -382,11 +500,10 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
500
501 memset(&o->result, 0, sizeof(o->result));
502 o->result.initialized = 1;
385 - if (o->src_index) {
386 - o->result.timestamp.sec = o->src_index->timestamp.sec;
387 - o->result.timestamp.nsec = o->src_index->timestamp.nsec;
388 - }
503 + o->result.timestamp.sec = o->src_index->timestamp.sec;
504 + o->result.timestamp.nsec = o->src_index->timestamp.nsec;
505 o->merge_size = len;
506 + mark_all_ce_unused(o->src_index);
507
508 if (!dfc)
509 dfc = xcalloc(1, cache_entry_size(0));
@@ -400,18 +517,38 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
517 info.fn = unpack_callback;
518 info.data = o;
519
520 + if (o->prefix) {
521 + /*
522 + * Unpack existing index entries that sort before the
523 + * prefix the tree is spliced into. Note that o->merge
524 + * is always true in this case.
525 + */
526 + while (1) {
527 + struct cache_entry *ce = next_cache_entry(o);
528 + if (!ce)
529 + break;
530 + if (ce_in_traverse_path(ce, &info))
531 + break;
532 + if (unpack_index_entry(ce, o) < 0)
533 + goto return_failed;
534 + }
535 + }
536 +
537 if (traverse_trees(len, t, &info) < 0)
404 - return unpack_failed(o, NULL);
538 + goto return_failed;
539 }
540
541 /* Any left-over entries in the index? */
542 if (o->merge) {
409 - while (o->pos < o->src_index->cache_nr) {
410 - struct cache_entry *ce = o->src_index->cache[o->pos];
543 + while (1) {
544 + struct cache_entry *ce = next_cache_entry(o);
545 + if (!ce)
546 + break;
547 if (unpack_index_entry(ce, o) < 0)
412 - return unpack_failed(o, NULL);
548 + goto return_failed;
549 }
550 }
551 + mark_all_ce_unused(o->src_index);
552
553 if (o->trivial_merges_only && o->nontrivial_merge)
554 return unpack_failed(o, "Merge requires file-level merging");
@@ -421,6 +558,10 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
558 if (o->dst_index)
559 *o->dst_index = o->result;
560 return ret;
561 +
562 +return_failed:
563 + mark_all_ce_unused(o->src_index);
564 + return unpack_failed(o, NULL);
565 }
566
567 /* Here come the merge functions */
@@ -522,7 +663,9 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,
663 * in that directory.
664 */
665 namelen = strlen(ce->name);
525 - for (i = o->pos; i < o->src_index->cache_nr; i++) {
666 + for (i = locate_in_src_index(ce, o);
667 + i < o->src_index->cache_nr;
668 + i++) {
669 struct cache_entry *ce2 = o->src_index->cache[i];
670 int len = ce_namelen(ce2);
671 if (len < namelen ||
@@ -530,12 +673,14 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,
673 ce2->name[namelen] != '/')
674 break;
675 /*
533 - * ce2->name is an entry in the subdirectory.
676 + * ce2->name is an entry in the subdirectory to be
677 + * removed.
678 */
679 if (!ce_stage(ce2)) {
680 if (verify_uptodate(ce2, o))
681 return -1;
682 add_entry(o, ce2, CE_REMOVE, 0);
683 + mark_ce_used(ce2, o);
684 }
685 cnt++;
686 }
@@ -591,7 +736,6 @@ static int verify_absent(struct cache_entry *ce, const char *action,
736 return 0;
737
738 if (!lstat(ce->name, &st)) {
594 - int ret;
739 int dtype = ce_to_dtype(ce);
740 struct cache_entry *result;
741
@@ -619,28 +763,8 @@ static int verify_absent(struct cache_entry *ce, const char *action,
763 * files that are in "foo/" we would lose
764 * them.
765 */
622 - ret = verify_clean_subdirectory(ce, action, o);
623 - if (ret < 0)
624 - return ret;
625 -
626 - /*
627 - * If this removed entries from the index,
628 - * what that means is:
629 - *
630 - * (1) the caller unpack_callback() saw path/foo
631 - * in the index, and it has not removed it because
632 - * it thinks it is handling 'path' as blob with
633 - * D/F conflict;
634 - * (2) we will return "ok, we placed a merged entry
635 - * in the index" which would cause o->pos to be
636 - * incremented by one;
637 - * (3) however, original o->pos now has 'path/foo'
638 - * marked with "to be removed".
639 - *
640 - * We need to increment it by the number of
641 - * deleted entries here.
642 - */
643 - o->pos += ret;
766 + if (verify_clean_subdirectory(ce, action, o) < 0)
767 + return -1;
768 return 0;
769 }
770
unpack-trees.h
+1 -1
@@ -30,7 +30,7 @@ struct unpack_trees_options {
30 diff_index_cached,
31 gently;
32 const char *prefix;
33 - int pos;
33 + int cache_bottom;
34 struct dir_struct *dir;
35 merge_fn_t fn;
36 struct unpack_trees_error_msgs msgs;