@@ -708,84 +708,117 @@ private static string EncodeAsHex(byte[] buffer)
708
708
/// Gets the number of commits in the longest single path between
709
709
/// the specified branch's head and the most distant ancestor (inclusive).
710
710
/// </summary>
711
- /// <param name="commit ">The commit to measure the height of.</param>
711
+ /// <param name="startingCommit ">The commit to measure the height of.</param>
712
712
/// <param name="tracker">The caching tracker for storing or fetching version information per commit.</param>
713
713
/// <param name="continueStepping">
714
714
/// A function that returns <c>false</c> when we reach a commit that
715
715
/// should not be included in the height calculation.
716
716
/// May be null to count the height to the original commit.
717
717
/// </param>
718
718
/// <returns>The height of the branch.</returns>
719
- private static int GetCommitHeight ( Commit commit , GitWalkTracker tracker , Func < Commit , bool > continueStepping )
719
+ private static int GetCommitHeight ( Commit startingCommit , GitWalkTracker tracker , Func < Commit , bool > continueStepping )
720
720
{
721
- Requires . NotNull ( commit , nameof ( commit ) ) ;
721
+ Requires . NotNull ( startingCommit , nameof ( startingCommit ) ) ;
722
722
Requires . NotNull ( tracker , nameof ( tracker ) ) ;
723
723
724
- if ( ! tracker . TryGetVersionHeight ( commit , out int height ) )
724
+ if ( continueStepping is object && ! continueStepping ( startingCommit ) )
725
+ {
726
+ return 0 ;
727
+ }
728
+
729
+ var commitsToEvaluate = new Stack < Commit > ( ) ;
730
+ bool TryCalculateHeight ( Commit commit )
725
731
{
726
- if ( continueStepping == null || continueStepping ( commit ) )
732
+ // Get max height among all parents, or schedule all missing parents for their own evaluation and return false.
733
+ int maxHeightAmongParents = 0 ;
734
+ bool parentMissing = false ;
735
+ foreach ( Commit parent in commit . Parents )
736
+ {
737
+ if ( ! tracker . TryGetVersionHeight ( parent , out int parentHeight ) )
738
+ {
739
+ if ( continueStepping is object && ! continueStepping ( parent ) )
740
+ {
741
+ // This parent isn't supposed to contribute to height.
742
+ continue ;
743
+ }
744
+
745
+ commitsToEvaluate . Push ( parent ) ;
746
+ parentMissing = true ;
747
+ }
748
+ else
749
+ {
750
+ maxHeightAmongParents = Math . Max ( maxHeightAmongParents , parentHeight ) ;
751
+ }
752
+ }
753
+
754
+ if ( parentMissing )
727
755
{
728
- var versionOptions = tracker . GetVersion ( commit ) ;
729
- var pathFilters = versionOptions ? . PathFilters ;
756
+ return false ;
757
+ }
730
758
731
- var includePaths =
732
- pathFilters
733
- ? . Where ( filter => ! filter . IsExclude )
734
- . Select ( filter => filter . RepoRelativePath )
735
- . ToList ( ) ;
759
+ var versionOptions = tracker . GetVersion ( commit ) ;
760
+ var pathFilters = versionOptions ? . PathFilters ;
736
761
737
- var excludePaths = pathFilters ? . Where ( filter => filter . IsExclude ) . ToList ( ) ;
762
+ var includePaths =
763
+ pathFilters
764
+ ? . Where ( filter => ! filter . IsExclude )
765
+ . Select ( filter => filter . RepoRelativePath )
766
+ . ToList ( ) ;
738
767
739
- var ignoreCase = commit . GetRepository ( ) . Config . Get < bool > ( "core.ignorecase" ) ? . Value ?? false ;
740
- bool ContainsRelevantChanges ( IEnumerable < TreeEntryChanges > changes ) =>
741
- excludePaths . Count == 0
742
- ? changes . Any ( )
743
- // If there is a single change that isn't excluded,
744
- // then this commit is relevant.
745
- : changes . Any ( change => ! excludePaths . Any ( exclude => exclude . Excludes ( change . Path , ignoreCase ) ) ) ;
768
+ var excludePaths = pathFilters ? . Where ( filter => filter . IsExclude ) . ToList ( ) ;
746
769
747
- height = 1 ;
770
+ var ignoreCase = commit . GetRepository ( ) . Config . Get < bool > ( "core.ignorecase" ) ? . Value ?? false ;
771
+ bool ContainsRelevantChanges ( IEnumerable < TreeEntryChanges > changes ) =>
772
+ excludePaths . Count == 0
773
+ ? changes . Any ( )
774
+ // If there is a single change that isn't excluded,
775
+ // then this commit is relevant.
776
+ : changes . Any ( change => ! excludePaths . Any ( exclude => exclude . Excludes ( change . Path , ignoreCase ) ) ) ;
748
777
749
- if ( includePaths != null )
750
- {
751
- // If there are no include paths, or any of the include
752
- // paths refer to the root of the repository, then do not
753
- // filter the diff at all.
754
- var diffInclude =
755
- includePaths . Count == 0 || pathFilters . Any ( filter => filter . IsRoot )
756
- ? null
757
- : includePaths ;
758
-
759
- // If the diff between this commit and any of its parents
760
- // does not touch a path that we care about, don't bump the
761
- // height.
762
- var relevantCommit =
763
- commit . Parents . Any ( )
764
- ? commit . Parents . Any ( parent => ContainsRelevantChanges ( commit . GetRepository ( ) . Diff
765
- . Compare < TreeChanges > ( parent . Tree , commit . Tree , diffInclude ) ) )
766
- : ContainsRelevantChanges ( commit . GetRepository ( ) . Diff
767
- . Compare < TreeChanges > ( null , commit . Tree , diffInclude ) ) ;
768
-
769
- if ( ! relevantCommit )
770
- {
771
- height = 0 ;
772
- }
773
- }
778
+ int height = 1 ;
774
779
775
- if ( commit . Parents . Any ( ) )
780
+ if ( includePaths != null )
781
+ {
782
+ // If there are no include paths, or any of the include
783
+ // paths refer to the root of the repository, then do not
784
+ // filter the diff at all.
785
+ var diffInclude =
786
+ includePaths . Count == 0 || pathFilters . Any ( filter => filter . IsRoot )
787
+ ? null
788
+ : includePaths ;
789
+
790
+ // If the diff between this commit and any of its parents
791
+ // does not touch a path that we care about, don't bump the
792
+ // height.
793
+ var relevantCommit =
794
+ commit . Parents . Any ( )
795
+ ? commit . Parents . Any ( parent => ContainsRelevantChanges ( commit . GetRepository ( ) . Diff
796
+ . Compare < TreeChanges > ( parent . Tree , commit . Tree , diffInclude ) ) )
797
+ : ContainsRelevantChanges ( commit . GetRepository ( ) . Diff
798
+ . Compare < TreeChanges > ( null , commit . Tree , diffInclude ) ) ;
799
+
800
+ if ( ! relevantCommit )
776
801
{
777
- height += commit . Parents . Max ( p => GetCommitHeight ( p , tracker , continueStepping ) ) ;
802
+ height = 0 ;
778
803
}
779
804
}
780
- else
805
+
806
+ tracker . RecordHeight ( commit , height + maxHeightAmongParents ) ;
807
+ return true ;
808
+ }
809
+
810
+ commitsToEvaluate . Push ( startingCommit ) ;
811
+ while ( commitsToEvaluate . Count > 0 )
812
+ {
813
+ Commit commit = commitsToEvaluate . Peek ( ) ;
814
+ if ( tracker . TryGetVersionHeight ( commit , out _ ) || TryCalculateHeight ( commit ) )
781
815
{
782
- height = 0 ;
816
+ commitsToEvaluate . Pop ( ) ;
783
817
}
784
-
785
- tracker . RecordHeight ( commit , height ) ;
786
818
}
787
819
788
- return height ;
820
+ Assumes . True ( tracker . TryGetVersionHeight ( startingCommit , out int result ) ) ;
821
+ return result ;
789
822
}
790
823
791
824
/// <summary>
0 commit comments