@@ -429,10 +429,100 @@ static int rev_same_tree_as_empty(struct rev_info *revs, struct commit *commit)
429429 return retval >= 0 && (tree_difference == REV_TREE_SAME );
430430}
431431
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+
432521static void try_to_simplify_commit (struct rev_info * revs , struct commit * commit )
433522{
434523 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 ;
436526
437527 /*
438528 * 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)
456546 if (!revs -> dense && !commit -> parents -> next )
457547 return ;
458548
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 ++ ) {
461552 struct commit * p = parent -> item ;
462553
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+ }
471580 if (parse_commit (p ) < 0 )
472581 die ("cannot simplify commit %s (because of %s)" ,
473582 sha1_to_hex (commit -> object .sha1 ),
474583 sha1_to_hex (p -> object .sha1 ));
475584 switch (rev_compare_tree (revs , p , commit )) {
476585 case REV_TREE_SAME :
477- tree_same = 1 ;
478586 if (!revs -> simplify_history || (p -> object .flags & UNINTERESTING )) {
479587 /* Even if a merge with an uninteresting
480588 * side branch brought the entire change
481589 * we are interested in, we do not want
482590 * to lose the other branches of this
483591 * merge, so we just keep going.
484592 */
485- pp = & parent -> next ;
593+ if (ts )
594+ ts -> treesame [nth_parent ] = 1 ;
486595 continue ;
487596 }
488597 parent -> next = NULL ;
@@ -511,12 +620,11 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
511620 case REV_TREE_OLD :
512621 case REV_TREE_DIFFERENT :
513622 tree_changed = 1 ;
514- pp = & parent -> next ;
515623 continue ;
516624 }
517625 die ("bad tree compare for commit %s" , sha1_to_hex (commit -> object .sha1 ));
518626 }
519- if (tree_changed && ! tree_same )
627+ if (tree_changed )
520628 return ;
521629 commit -> object .flags |= TREESAME ;
522630}
@@ -1947,28 +2055,32 @@ static void add_child(struct rev_info *revs, struct commit *parent, struct commi
19472055 l -> next = add_decoration (& revs -> children , & parent -> object , l );
19482056}
19492057
1950- static int remove_duplicate_parents (struct commit * commit )
2058+ static int remove_duplicate_parents (struct rev_info * revs , struct commit * commit )
19512059{
2060+ struct treesame_state * ts = lookup_decoration (& revs -> treesame , & commit -> object );
19522061 struct commit_list * * pp , * p ;
19532062 int surviving_parents ;
19542063
19552064 /* Examine existing parents while marking ones we have seen... */
19562065 pp = & commit -> parents ;
2066+ surviving_parents = 0 ;
19572067 while ((p = * pp ) != NULL ) {
19582068 struct commit * parent = p -> item ;
19592069 if (parent -> object .flags & TMP_MARK ) {
19602070 * pp = p -> next ;
2071+ if (ts )
2072+ compact_treesame (revs , commit , surviving_parents );
19612073 continue ;
19622074 }
19632075 parent -> object .flags |= TMP_MARK ;
2076+ surviving_parents ++ ;
19642077 pp = & p -> next ;
19652078 }
1966- /* count them while clearing the temporary mark */
1967- surviving_parents = 0 ;
2079+ /* clear the temporary mark */
19682080 for (p = commit -> parents ; p ; p = p -> next ) {
19692081 p -> item -> object .flags &= ~TMP_MARK ;
1970- surviving_parents ++ ;
19712082 }
2083+ /* no update_treesame() - removing duplicates can't affect TREESAME */
19722084 return surviving_parents ;
19732085}
19742086
@@ -1988,6 +2100,70 @@ static struct merge_simplify_state *locate_simplify_state(struct rev_info *revs,
19882100 return st ;
19892101}
19902102
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+
19912167static struct commit_list * * simplify_one (struct rev_info * revs , struct commit * commit , struct commit_list * * tail )
19922168{
19932169 struct commit_list * p ;
@@ -2032,7 +2208,9 @@ static struct commit_list **simplify_one(struct rev_info *revs, struct commit *c
20322208 }
20332209
20342210 /*
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.
20362214 */
20372215 for (p = commit -> parents ; p ; p = p -> next ) {
20382216 pst = locate_simplify_state (revs , p -> item );
@@ -2044,31 +2222,30 @@ static struct commit_list **simplify_one(struct rev_info *revs, struct commit *c
20442222 if (revs -> first_parent_only )
20452223 cnt = 1 ;
20462224 else
2047- cnt = remove_duplicate_parents (commit );
2225+ cnt = remove_duplicate_parents (revs , commit );
20482226
20492227 /*
20502228 * It is possible that we are a merge and one side branch
20512229 * 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 .
20542232 *
20552233 * o----X X: the commit we are looking at;
20562234 * / / o: a commit that touches the paths;
20572235 * ---o----'
20582236 *
2059- * Further reduce the parents by removing redundant parents .
2237+ * Detect and simplify this case .
20602238 */
20612239 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 );
20662243 }
20672244
20682245 /*
20692246 * A commit simplifies to itself if it is a root, if it is
20702247 * 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
20722249 * (the first two cases are already handled at the beginning of
20732250 * this function).
20742251 *
@@ -2176,6 +2353,10 @@ int prepare_revision_walk(struct rev_info *revs)
21762353 if (!revs -> leak_pending )
21772354 free (list );
21782355
2356+ /* Signal whether we need per-parent treesame decoration */
2357+ if (revs -> simplify_merges )
2358+ revs -> treesame .name = "treesame" ;
2359+
21792360 if (revs -> no_walk != REVISION_WALK_NO_WALK_UNSORTED )
21802361 commit_list_sort_by_date (& revs -> commits );
21812362 if (revs -> no_walk )
@@ -2235,7 +2416,7 @@ static int rewrite_parents(struct rev_info *revs, struct commit *commit)
22352416 }
22362417 pp = & parent -> next ;
22372418 }
2238- remove_duplicate_parents (commit );
2419+ remove_duplicate_parents (revs , commit );
22392420 return 0 ;
22402421}
22412422
0 commit comments