11package plumbing
22
33import (
4+ "fmt"
45 "path/filepath"
56 "sort"
67 "strings"
@@ -28,6 +29,9 @@ type RenameAnalysis struct {
2829 // set it to the default value of 80 (80%).
2930 SimilarityThreshold int
3031
32+ // Timeout is the maximum time allowed to spend computing renames in a single commit.
33+ Timeout time.Duration
34+
3135 repository * git.Repository
3236
3337 l core.Logger
@@ -39,10 +43,18 @@ const (
3943 // CGit's default is 50%. Ours is 80% because 50% can be too computationally expensive.
4044 RenameAnalysisDefaultThreshold = 80
4145
46+ // RenameAnalysisDefaultTimeout is the default value of RenameAnalysis.Timeout (in milliseconds).
47+ RenameAnalysisDefaultTimeout = 60000
48+
4249 // ConfigRenameAnalysisSimilarityThreshold is the name of the configuration option
4350 // (RenameAnalysis.Configure()) which sets the similarity threshold.
4451 ConfigRenameAnalysisSimilarityThreshold = "RenameAnalysis.SimilarityThreshold"
4552
53+ // ConfigRenameAnalysisTimeout is the name of the configuration option
54+ // (RenameAnalysis.Configure()) which sets the maximum time allowed to spend
55+ // computing renames in a single commit.
56+ ConfigRenameAnalysisTimeout = "RenameAnalysis.Timeout"
57+
4658 // RenameAnalysisMinimumSize is the minimum size of a blob to be considered.
4759 RenameAnalysisMinimumSize = 32
4860
@@ -84,7 +96,13 @@ func (ra *RenameAnalysis) ListConfigurationOptions() []core.ConfigurationOption
8496 Description : "The threshold on the similarity index used to detect renames." ,
8597 Flag : "M" ,
8698 Type : core .IntConfigurationOption ,
87- Default : RenameAnalysisDefaultThreshold },
99+ Default : RenameAnalysisDefaultThreshold }, {
100+ Name : ConfigRenameAnalysisTimeout ,
101+ Description : "The maximum time (milliseconds) allowed to spend computing " +
102+ "renames in a single commit. 0 sets the default." ,
103+ Flag : "renames-timeout" ,
104+ Type : core .IntConfigurationOption ,
105+ Default : RenameAnalysisDefaultTimeout },
88106 }
89107 return options [:]
90108}
@@ -97,6 +115,12 @@ func (ra *RenameAnalysis) Configure(facts map[string]interface{}) error {
97115 if val , exists := facts [ConfigRenameAnalysisSimilarityThreshold ].(int ); exists {
98116 ra .SimilarityThreshold = val
99117 }
118+ if val , exists := facts [ConfigRenameAnalysisTimeout ].(int ); exists {
119+ if val < 0 {
120+ return fmt .Errorf ("negative renames detection timeout is not allowed: %d" , val )
121+ }
122+ ra .Timeout = time .Duration (val ) * time .Millisecond
123+ }
100124 return nil
101125}
102126
@@ -109,6 +133,9 @@ func (ra *RenameAnalysis) Initialize(repository *git.Repository) error {
109133 RenameAnalysisDefaultThreshold )
110134 ra .SimilarityThreshold = RenameAnalysisDefaultThreshold
111135 }
136+ if ra .Timeout == 0 {
137+ ra .Timeout = time .Duration (RenameAnalysisDefaultTimeout ) * time .Millisecond
138+ }
112139 ra .repository = repository
113140 return nil
114141}
@@ -119,6 +146,7 @@ func (ra *RenameAnalysis) Initialize(repository *git.Repository) error {
119146// This function returns the mapping with analysis results. The keys must be the same as
120147// in Provides(). If there was an error, nil is returned.
121148func (ra * RenameAnalysis ) Consume (deps map [string ]interface {}) (map [string ]interface {}, error ) {
149+ beginTime := time .Now ()
122150 changes := deps [DependencyTreeChanges ].(object.Changes )
123151 cache := deps [DependencyBlobCache ].(map [plumbing.Hash ]* CachedBlob )
124152
@@ -225,7 +253,7 @@ func (ra *RenameAnalysis) Consume(deps map[string]interface{}) (map[string]inter
225253 }()
226254 aStart := 0
227255 // we will try to find a matching added blob for each deleted blob
228- for d := 0 ; d < deletedBlobsA .Len (); d ++ {
256+ for d := 0 ; d < deletedBlobsA .Len () && time . Now (). Sub ( beginTime ) < ra . Timeout ; d ++ {
229257 myBlob := cache [deletedBlobsA [d ].change .From .TreeEntry .Hash ]
230258 mySize := deletedBlobsA [d ].size
231259 myName := filepath .Base (deletedBlobsA [d ].change .From .Name )
@@ -283,7 +311,7 @@ func (ra *RenameAnalysis) Consume(deps map[string]interface{}) (map[string]inter
283311 wg .Done ()
284312 }()
285313 dStart := 0
286- for a := 0 ; a < addedBlobsB .Len (); a ++ {
314+ for a := 0 ; a < addedBlobsB .Len () && time . Now (). Sub ( beginTime ) < ra . Timeout ; a ++ {
287315 myBlob := cache [addedBlobsB [a ].change .To .TreeEntry .Hash ]
288316 mySize := addedBlobsB [a ].size
289317 myName := filepath .Base (addedBlobsB [a ].change .To .Name )
0 commit comments