@@ -429,10 +429,100 @@ static int rev_same_tree_as_empty(struct rev_info *revs, struct commit *commit)
429
429
return retval >= 0 && (tree_difference == REV_TREE_SAME );
430
430
}
431
431
432
+ struct treesame_state {
433
+ unsigned int nparents ;
434
+ unsigned char treesame [FLEX_ARRAY ];
435
+ };
436
+
437
+ static struct treesame_state * initialise_treesame (struct rev_info * revs , struct commit * commit )
438
+ {
439
+ unsigned n = commit_list_count (commit -> parents );
440
+ struct treesame_state * st = xcalloc (1 , sizeof (* st ) + n );
441
+ st -> nparents = n ;
442
+ add_decoration (& revs -> treesame , & commit -> object , st );
443
+ return st ;
444
+ }
445
+
446
+ /*
447
+ * Must be called immediately after removing the nth_parent from a commit's
448
+ * parent list, if we are maintaining the per-parent treesame[] decoration.
449
+ * This does not recalculate the master TREESAME flag - update_treesame()
450
+ * should be called to update it after a sequence of treesame[] modifications
451
+ * that may have affected it.
452
+ */
453
+ static int compact_treesame (struct rev_info * revs , struct commit * commit , unsigned nth_parent )
454
+ {
455
+ struct treesame_state * st ;
456
+ int old_same ;
457
+
458
+ if (!commit -> parents ) {
459
+ /*
460
+ * Have just removed the only parent from a non-merge.
461
+ * Different handling, as we lack decoration.
462
+ */
463
+ if (nth_parent != 0 )
464
+ die ("compact_treesame %u" , nth_parent );
465
+ old_same = !!(commit -> object .flags & TREESAME );
466
+ if (rev_same_tree_as_empty (revs , commit ))
467
+ commit -> object .flags |= TREESAME ;
468
+ else
469
+ commit -> object .flags &= ~TREESAME ;
470
+ return old_same ;
471
+ }
472
+
473
+ st = lookup_decoration (& revs -> treesame , & commit -> object );
474
+ if (!st || nth_parent >= st -> nparents )
475
+ die ("compact_treesame %u" , nth_parent );
476
+
477
+ old_same = st -> treesame [nth_parent ];
478
+ memmove (st -> treesame + nth_parent ,
479
+ st -> treesame + nth_parent + 1 ,
480
+ st -> nparents - nth_parent - 1 );
481
+
482
+ /*
483
+ * If we've just become a non-merge commit, update TREESAME
484
+ * immediately, and remove the no-longer-needed decoration.
485
+ * If still a merge, defer update until update_treesame().
486
+ */
487
+ if (-- st -> nparents == 1 ) {
488
+ if (commit -> parents -> next )
489
+ die ("compact_treesame parents mismatch" );
490
+ if (st -> treesame [0 ] && revs -> dense )
491
+ commit -> object .flags |= TREESAME ;
492
+ else
493
+ commit -> object .flags &= ~TREESAME ;
494
+ free (add_decoration (& revs -> treesame , & commit -> object , NULL ));
495
+ }
496
+
497
+ return old_same ;
498
+ }
499
+
500
+ static unsigned update_treesame (struct rev_info * revs , struct commit * commit )
501
+ {
502
+ if (commit -> parents && commit -> parents -> next ) {
503
+ unsigned n ;
504
+ struct treesame_state * st ;
505
+
506
+ st = lookup_decoration (& revs -> treesame , & commit -> object );
507
+ if (!st )
508
+ die ("update_treesame %s" , sha1_to_hex (commit -> object .sha1 ));
509
+ commit -> object .flags |= TREESAME ;
510
+ for (n = 0 ; n < st -> nparents ; n ++ ) {
511
+ if (!st -> treesame [n ]) {
512
+ commit -> object .flags &= ~TREESAME ;
513
+ break ;
514
+ }
515
+ }
516
+ }
517
+
518
+ return commit -> object .flags & TREESAME ;
519
+ }
520
+
432
521
static void try_to_simplify_commit (struct rev_info * revs , struct commit * commit )
433
522
{
434
523
struct commit_list * * pp , * parent ;
435
- int tree_changed = 0 , tree_same = 0 , nth_parent = 0 ;
524
+ struct treesame_state * ts = NULL ;
525
+ int tree_changed = 0 , nth_parent ;
436
526
437
527
/*
438
528
* If we don't do pruning, everything is interesting
@@ -456,33 +546,52 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
456
546
if (!revs -> dense && !commit -> parents -> next )
457
547
return ;
458
548
459
- pp = & commit -> parents ;
460
- while ((parent = * pp ) != NULL ) {
549
+ for (pp = & commit -> parents , nth_parent = 0 ;
550
+ (parent = * pp ) != NULL ;
551
+ pp = & parent -> next , nth_parent ++ ) {
461
552
struct commit * p = parent -> item ;
462
553
463
- /*
464
- * Do not compare with later parents when we care only about
465
- * the first parent chain, in order to avoid derailing the
466
- * traversal to follow a side branch that brought everything
467
- * in the path we are limited to by the pathspec.
468
- */
469
- if (revs -> first_parent_only && nth_parent ++ )
470
- break ;
554
+ if (nth_parent == 1 ) {
555
+ /*
556
+ * This our second loop iteration - so we now know
557
+ * we're dealing with a merge.
558
+ *
559
+ * Do not compare with later parents when we care only about
560
+ * the first parent chain, in order to avoid derailing the
561
+ * traversal to follow a side branch that brought everything
562
+ * in the path we are limited to by the pathspec.
563
+ */
564
+ if (revs -> first_parent_only )
565
+ break ;
566
+ /*
567
+ * If this will remain a potentially-simplifiable
568
+ * merge, remember per-parent treesame if needed.
569
+ * Initialise the array with the comparison from our
570
+ * first iteration.
571
+ */
572
+ if (revs -> treesame .name &&
573
+ !revs -> simplify_history &&
574
+ !(commit -> object .flags & UNINTERESTING )) {
575
+ ts = initialise_treesame (revs , commit );
576
+ if (!tree_changed )
577
+ ts -> treesame [0 ] = 1 ;
578
+ }
579
+ }
471
580
if (parse_commit (p ) < 0 )
472
581
die ("cannot simplify commit %s (because of %s)" ,
473
582
sha1_to_hex (commit -> object .sha1 ),
474
583
sha1_to_hex (p -> object .sha1 ));
475
584
switch (rev_compare_tree (revs , p , commit )) {
476
585
case REV_TREE_SAME :
477
- tree_same = 1 ;
478
586
if (!revs -> simplify_history || (p -> object .flags & UNINTERESTING )) {
479
587
/* Even if a merge with an uninteresting
480
588
* side branch brought the entire change
481
589
* we are interested in, we do not want
482
590
* to lose the other branches of this
483
591
* merge, so we just keep going.
484
592
*/
485
- pp = & parent -> next ;
593
+ if (ts )
594
+ ts -> treesame [nth_parent ] = 1 ;
486
595
continue ;
487
596
}
488
597
parent -> next = NULL ;
@@ -511,12 +620,11 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
511
620
case REV_TREE_OLD :
512
621
case REV_TREE_DIFFERENT :
513
622
tree_changed = 1 ;
514
- pp = & parent -> next ;
515
623
continue ;
516
624
}
517
625
die ("bad tree compare for commit %s" , sha1_to_hex (commit -> object .sha1 ));
518
626
}
519
- if (tree_changed && ! tree_same )
627
+ if (tree_changed )
520
628
return ;
521
629
commit -> object .flags |= TREESAME ;
522
630
}
@@ -1947,28 +2055,32 @@ static void add_child(struct rev_info *revs, struct commit *parent, struct commi
1947
2055
l -> next = add_decoration (& revs -> children , & parent -> object , l );
1948
2056
}
1949
2057
1950
- static int remove_duplicate_parents (struct commit * commit )
2058
+ static int remove_duplicate_parents (struct rev_info * revs , struct commit * commit )
1951
2059
{
2060
+ struct treesame_state * ts = lookup_decoration (& revs -> treesame , & commit -> object );
1952
2061
struct commit_list * * pp , * p ;
1953
2062
int surviving_parents ;
1954
2063
1955
2064
/* Examine existing parents while marking ones we have seen... */
1956
2065
pp = & commit -> parents ;
2066
+ surviving_parents = 0 ;
1957
2067
while ((p = * pp ) != NULL ) {
1958
2068
struct commit * parent = p -> item ;
1959
2069
if (parent -> object .flags & TMP_MARK ) {
1960
2070
* pp = p -> next ;
2071
+ if (ts )
2072
+ compact_treesame (revs , commit , surviving_parents );
1961
2073
continue ;
1962
2074
}
1963
2075
parent -> object .flags |= TMP_MARK ;
2076
+ surviving_parents ++ ;
1964
2077
pp = & p -> next ;
1965
2078
}
1966
- /* count them while clearing the temporary mark */
1967
- surviving_parents = 0 ;
2079
+ /* clear the temporary mark */
1968
2080
for (p = commit -> parents ; p ; p = p -> next ) {
1969
2081
p -> item -> object .flags &= ~TMP_MARK ;
1970
- surviving_parents ++ ;
1971
2082
}
2083
+ /* no update_treesame() - removing duplicates can't affect TREESAME */
1972
2084
return surviving_parents ;
1973
2085
}
1974
2086
@@ -1988,6 +2100,70 @@ static struct merge_simplify_state *locate_simplify_state(struct rev_info *revs,
1988
2100
return st ;
1989
2101
}
1990
2102
2103
+ static int mark_redundant_parents (struct rev_info * revs , struct commit * commit )
2104
+ {
2105
+ struct commit_list * h = reduce_heads (commit -> parents );
2106
+ int i = 0 , marked = 0 ;
2107
+ struct commit_list * po , * pn ;
2108
+
2109
+ /* Want these for sanity-checking only */
2110
+ int orig_cnt = commit_list_count (commit -> parents );
2111
+ int cnt = commit_list_count (h );
2112
+
2113
+ /*
2114
+ * Not ready to remove items yet, just mark them for now, based
2115
+ * on the output of reduce_heads(). reduce_heads outputs the reduced
2116
+ * set in its original order, so this isn't too hard.
2117
+ */
2118
+ po = commit -> parents ;
2119
+ pn = h ;
2120
+ while (po ) {
2121
+ if (pn && po -> item == pn -> item ) {
2122
+ pn = pn -> next ;
2123
+ i ++ ;
2124
+ } else {
2125
+ po -> item -> object .flags |= TMP_MARK ;
2126
+ marked ++ ;
2127
+ }
2128
+ po = po -> next ;
2129
+ }
2130
+
2131
+ if (i != cnt || cnt + marked != orig_cnt )
2132
+ die ("mark_redundant_parents %d %d %d %d" , orig_cnt , cnt , i , marked );
2133
+
2134
+ free_commit_list (h );
2135
+
2136
+ return marked ;
2137
+ }
2138
+
2139
+ static int remove_marked_parents (struct rev_info * revs , struct commit * commit )
2140
+ {
2141
+ struct commit_list * * pp , * p ;
2142
+ int nth_parent , removed = 0 ;
2143
+
2144
+ pp = & commit -> parents ;
2145
+ nth_parent = 0 ;
2146
+ while ((p = * pp ) != NULL ) {
2147
+ struct commit * parent = p -> item ;
2148
+ if (parent -> object .flags & TMP_MARK ) {
2149
+ parent -> object .flags &= ~TMP_MARK ;
2150
+ * pp = p -> next ;
2151
+ free (p );
2152
+ removed ++ ;
2153
+ compact_treesame (revs , commit , nth_parent );
2154
+ continue ;
2155
+ }
2156
+ pp = & p -> next ;
2157
+ nth_parent ++ ;
2158
+ }
2159
+
2160
+ /* Removing parents can only increase TREESAMEness */
2161
+ if (removed && !(commit -> object .flags & TREESAME ))
2162
+ update_treesame (revs , commit );
2163
+
2164
+ return nth_parent ;
2165
+ }
2166
+
1991
2167
static struct commit_list * * simplify_one (struct rev_info * revs , struct commit * commit , struct commit_list * * tail )
1992
2168
{
1993
2169
struct commit_list * p ;
@@ -2032,7 +2208,9 @@ static struct commit_list **simplify_one(struct rev_info *revs, struct commit *c
2032
2208
}
2033
2209
2034
2210
/*
2035
- * Rewrite our list of parents.
2211
+ * Rewrite our list of parents. Note that this cannot
2212
+ * affect our TREESAME flags in any way - a commit is
2213
+ * always TREESAME to its simplification.
2036
2214
*/
2037
2215
for (p = commit -> parents ; p ; p = p -> next ) {
2038
2216
pst = locate_simplify_state (revs , p -> item );
@@ -2044,31 +2222,30 @@ static struct commit_list **simplify_one(struct rev_info *revs, struct commit *c
2044
2222
if (revs -> first_parent_only )
2045
2223
cnt = 1 ;
2046
2224
else
2047
- cnt = remove_duplicate_parents (commit );
2225
+ cnt = remove_duplicate_parents (revs , commit );
2048
2226
2049
2227
/*
2050
2228
* It is possible that we are a merge and one side branch
2051
2229
* does not have any commit that touches the given paths;
2052
- * in such a case, the immediate parents will be rewritten
2053
- * to different commits .
2230
+ * in such a case, the immediate parent from that branch
2231
+ * will be rewritten to be the merge base .
2054
2232
*
2055
2233
* o----X X: the commit we are looking at;
2056
2234
* / / o: a commit that touches the paths;
2057
2235
* ---o----'
2058
2236
*
2059
- * Further reduce the parents by removing redundant parents .
2237
+ * Detect and simplify this case .
2060
2238
*/
2061
2239
if (1 < cnt ) {
2062
- struct commit_list * h = reduce_heads (commit -> parents );
2063
- cnt = commit_list_count (h );
2064
- free_commit_list (commit -> parents );
2065
- commit -> parents = h ;
2240
+ int marked = mark_redundant_parents (revs , commit );
2241
+ if (marked )
2242
+ cnt = remove_marked_parents (revs , commit );
2066
2243
}
2067
2244
2068
2245
/*
2069
2246
* A commit simplifies to itself if it is a root, if it is
2070
2247
* UNINTERESTING, if it touches the given paths, or if it is a
2071
- * merge and its parents simplifies to more than one commits
2248
+ * merge and its parents simplify to more than one commit
2072
2249
* (the first two cases are already handled at the beginning of
2073
2250
* this function).
2074
2251
*
@@ -2176,6 +2353,10 @@ int prepare_revision_walk(struct rev_info *revs)
2176
2353
if (!revs -> leak_pending )
2177
2354
free (list );
2178
2355
2356
+ /* Signal whether we need per-parent treesame decoration */
2357
+ if (revs -> simplify_merges )
2358
+ revs -> treesame .name = "treesame" ;
2359
+
2179
2360
if (revs -> no_walk != REVISION_WALK_NO_WALK_UNSORTED )
2180
2361
commit_list_sort_by_date (& revs -> commits );
2181
2362
if (revs -> no_walk )
@@ -2235,7 +2416,7 @@ static int rewrite_parents(struct rev_info *revs, struct commit *commit)
2235
2416
}
2236
2417
pp = & parent -> next ;
2237
2418
}
2238
- remove_duplicate_parents (commit );
2419
+ remove_duplicate_parents (revs , commit );
2239
2420
return 0 ;
2240
2421
}
2241
2422
0 commit comments