mirror of
https://github.com/lukaszraczylo/git-velocity.git
synced 2026-07-05 06:05:03 +00:00
Add limits on the git clone based on the start date.
This commit is contained in:
+18
-1
@@ -170,7 +170,24 @@ func (a *App) collectRepoData(ctx context.Context, owner, name string, dateRange
|
|||||||
if a.gitRepo != nil {
|
if a.gitRepo != nil {
|
||||||
// Clone/update repository locally
|
// Clone/update repository locally
|
||||||
token := a.config.Auth.GithubToken
|
token := a.config.Auth.GithubToken
|
||||||
cloneErr := a.gitRepo.EnsureCloned(ctx, owner, name, token)
|
|
||||||
|
// Determine clone options (shallow clone if enabled)
|
||||||
|
var cloneOpts *git.CloneOptions
|
||||||
|
if a.config.Options.ShallowClone && dateRange.Start != nil {
|
||||||
|
// Get commit count since start date to determine shallow clone depth
|
||||||
|
commitCount, countErr := a.client.GetCommitCountSince(ctx, owner, name, *dateRange.Start)
|
||||||
|
if countErr != nil {
|
||||||
|
a.log(" Warning: failed to get commit count for shallow clone: %v", countErr)
|
||||||
|
// Proceed with full clone
|
||||||
|
} else if commitCount > 0 {
|
||||||
|
// Add buffer for safety margin
|
||||||
|
depth := commitCount + a.config.Options.ShallowCloneBuffer
|
||||||
|
cloneOpts = &git.CloneOptions{Depth: depth}
|
||||||
|
a.log(" Using shallow clone (depth: %d = %d commits + %d buffer)", depth, commitCount, a.config.Options.ShallowCloneBuffer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cloneErr := a.gitRepo.EnsureClonedWithOptions(ctx, owner, name, token, cloneOpts)
|
||||||
if cloneErr != nil {
|
if cloneErr != nil {
|
||||||
a.log(" Warning: failed to clone repository locally, falling back to API: %v", cloneErr)
|
a.log(" Warning: failed to clone repository locally, falling back to API: %v", cloneErr)
|
||||||
// Fallback to API
|
// Fallback to API
|
||||||
|
|||||||
@@ -150,6 +150,8 @@ type OptionsConfig struct {
|
|||||||
AdditionalBotPatterns []string `yaml:"additional_bot_patterns"` // User-defined patterns (added to hardcoded defaults)
|
AdditionalBotPatterns []string `yaml:"additional_bot_patterns"` // User-defined patterns (added to hardcoded defaults)
|
||||||
CloneDirectory string `yaml:"clone_directory"` // Directory for local git clones
|
CloneDirectory string `yaml:"clone_directory"` // Directory for local git clones
|
||||||
UseLocalGit bool `yaml:"use_local_git"` // Use local git for commits (faster)
|
UseLocalGit bool `yaml:"use_local_git"` // Use local git for commits (faster)
|
||||||
|
ShallowClone bool `yaml:"shallow_clone"` // Use shallow clone based on date range (faster cloning)
|
||||||
|
ShallowCloneBuffer int `yaml:"shallow_clone_buffer"` // Extra commits to fetch beyond date range (default: 100)
|
||||||
UserAliases []UserAlias `yaml:"user_aliases,omitempty"` // Manual email/name to login mappings
|
UserAliases []UserAlias `yaml:"user_aliases,omitempty"` // Manual email/name to login mappings
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,6 +231,8 @@ func DefaultConfig() *Config {
|
|||||||
AdditionalBotPatterns: []string{}, // Users can add custom patterns here
|
AdditionalBotPatterns: []string{}, // Users can add custom patterns here
|
||||||
CloneDirectory: "./.repos",
|
CloneDirectory: "./.repos",
|
||||||
UseLocalGit: true, // Default to faster local git analysis
|
UseLocalGit: true, // Default to faster local git analysis
|
||||||
|
ShallowClone: true, // Default to shallow clone for faster cloning
|
||||||
|
ShallowCloneBuffer: 25, // Extra commits beyond date range for safety margin
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,8 +52,19 @@ func (r *Repository) repoPath(owner, name string) string {
|
|||||||
return filepath.Join(r.baseDir, owner, name)
|
return filepath.Join(r.baseDir, owner, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CloneOptions contains options for cloning a repository
|
||||||
|
type CloneOptions struct {
|
||||||
|
// Depth limits the clone to the specified number of commits (0 = full clone)
|
||||||
|
Depth int
|
||||||
|
}
|
||||||
|
|
||||||
// EnsureCloned ensures a repository is cloned and up to date
|
// EnsureCloned ensures a repository is cloned and up to date
|
||||||
func (r *Repository) EnsureCloned(ctx context.Context, owner, name, token string) error {
|
func (r *Repository) EnsureCloned(ctx context.Context, owner, name, token string) error {
|
||||||
|
return r.EnsureClonedWithOptions(ctx, owner, name, token, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnsureClonedWithOptions ensures a repository is cloned with specific options
|
||||||
|
func (r *Repository) EnsureClonedWithOptions(ctx context.Context, owner, name, token string, opts *CloneOptions) error {
|
||||||
repoPath := r.repoPath(owner, name)
|
repoPath := r.repoPath(owner, name)
|
||||||
|
|
||||||
// Check if already cloned
|
// Check if already cloned
|
||||||
@@ -65,12 +76,16 @@ func (r *Repository) EnsureCloned(ctx context.Context, owner, name, token string
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Clone the repository
|
// Clone the repository
|
||||||
r.progress(fmt.Sprintf(" Cloning %s/%s...", owner, name))
|
if opts != nil && opts.Depth > 0 {
|
||||||
return r.clone(ctx, owner, name, token, repoPath)
|
r.progress(fmt.Sprintf(" Shallow cloning %s/%s (depth: %d)...", owner, name, opts.Depth))
|
||||||
|
} else {
|
||||||
|
r.progress(fmt.Sprintf(" Cloning %s/%s...", owner, name))
|
||||||
|
}
|
||||||
|
return r.clone(ctx, owner, name, token, repoPath, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// clone clones a repository using go-git
|
// clone clones a repository using go-git
|
||||||
func (r *Repository) clone(ctx context.Context, owner, name, token, destPath string) error {
|
func (r *Repository) clone(ctx context.Context, owner, name, token, destPath string, opts *CloneOptions) error {
|
||||||
// Create parent directory
|
// Create parent directory
|
||||||
if err := os.MkdirAll(filepath.Dir(destPath), 0750); err != nil {
|
if err := os.MkdirAll(filepath.Dir(destPath), 0750); err != nil {
|
||||||
return fmt.Errorf("failed to create parent directory: %w", err)
|
return fmt.Errorf("failed to create parent directory: %w", err)
|
||||||
@@ -83,6 +98,11 @@ func (r *Repository) clone(ctx context.Context, owner, name, token, destPath str
|
|||||||
Progress: nil, // Could add progress writer here
|
Progress: nil, // Could add progress writer here
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply shallow clone depth if provided
|
||||||
|
if opts != nil && opts.Depth > 0 {
|
||||||
|
cloneOpts.Depth = opts.Depth
|
||||||
|
}
|
||||||
|
|
||||||
// Add authentication if token provided
|
// Add authentication if token provided
|
||||||
if token != "" {
|
if token != "" {
|
||||||
cloneOpts.Auth = &http.BasicAuth{
|
cloneOpts.Auth = &http.BasicAuth{
|
||||||
|
|||||||
@@ -272,6 +272,53 @@ func (c *Client) ListOrgRepos(ctx context.Context, org, pattern string) ([]strin
|
|||||||
return allRepos, nil
|
return allRepos, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetCommitCountSince returns the approximate number of commits since a given date.
|
||||||
|
// This is used to determine the optimal shallow clone depth.
|
||||||
|
// It makes a single lightweight API call with per_page=1 to get pagination info.
|
||||||
|
func (c *Client) GetCommitCountSince(ctx context.Context, owner, repo string, since time.Time) (int, error) {
|
||||||
|
opts := &github.CommitsListOptions{
|
||||||
|
Since: since,
|
||||||
|
ListOptions: github.ListOptions{
|
||||||
|
PerPage: 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var resp *github.Response
|
||||||
|
err := c.retryWithBackoff(ctx, "get commit count", func() error {
|
||||||
|
var err error
|
||||||
|
_, resp, err = c.gh.Repositories.ListCommits(ctx, owner, repo, opts)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("failed to get commit count: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GitHub returns pagination info in the response
|
||||||
|
// LastPage indicates total number of pages (with 1 item per page = total commits)
|
||||||
|
if resp.LastPage > 0 {
|
||||||
|
return resp.LastPage, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If LastPage is 0, there's only one page (or no commits)
|
||||||
|
// In this case, we need to check if there are any commits at all
|
||||||
|
if resp.FirstPage == 0 && resp.NextPage == 0 {
|
||||||
|
// Make another call to actually count
|
||||||
|
opts.ListOptions.PerPage = 100
|
||||||
|
var commits []*github.RepositoryCommit
|
||||||
|
err := c.retryWithBackoff(ctx, "count commits", func() error {
|
||||||
|
var err error
|
||||||
|
commits, _, err = c.gh.Repositories.ListCommits(ctx, owner, repo, opts)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return len(commits), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1, nil
|
||||||
|
}
|
||||||
|
|
||||||
// FetchCommits fetches commits from a repository within a date range
|
// FetchCommits fetches commits from a repository within a date range
|
||||||
func (c *Client) FetchCommits(ctx context.Context, owner, repo string, since, until *time.Time) ([]models.Commit, error) {
|
func (c *Client) FetchCommits(ctx context.Context, owner, repo string, since, until *time.Time) ([]models.Commit, error) {
|
||||||
cacheKey := fmt.Sprintf("commits:%s/%s:%v:%v", owner, repo, since, until)
|
cacheKey := fmt.Sprintf("commits:%s/%s:%v:%v", owner, repo, since, until)
|
||||||
|
|||||||
Reference in New Issue
Block a user