// Background task, must call waitg.Done() once at end func pruneTaskGetRetainedWorktree(retainChan chan string, errorChan chan error, waitg *sync.WaitGroup) { defer waitg.Done() // Retain other worktree HEADs too // Working copy, branch & maybe commit is different but repo is shared allWorktreeRefs, err := git.GetAllWorkTreeHEADs(config.LocalGitStorageDir) if err != nil { errorChan <- err return } // Don't repeat any commits, worktrees are always on their own branches but // may point to the same commit commits := tools.NewStringSet() // current HEAD is done elsewhere headref, err := git.CurrentRef() if err != nil { errorChan <- err return } commits.Add(headref.Sha) for _, ref := range allWorktreeRefs { if commits.Add(ref.Sha) { // Worktree is on a different commit waitg.Add(1) // Don't need to 'cd' to worktree since we share same repo go pruneTaskGetRetainedAtRef(ref.Sha, retainChan, errorChan, waitg) } } }
// Background task, must call waitg.Done() once at end func pruneTaskGetRetainedCurrentAndRecentRefs(fetchconf config.FetchPruneConfig, retainChan chan string, errorChan chan error, waitg *sync.WaitGroup) { defer waitg.Done() // We actually increment the waitg in this func since we kick off sub-goroutines // Make a list of what unique commits to keep, & search backward from commits := tools.NewStringSet() // Do current first ref, err := git.CurrentRef() if err != nil { errorChan <- err return } commits.Add(ref.Sha) waitg.Add(1) go pruneTaskGetRetainedAtRef(ref.Sha, retainChan, errorChan, waitg) // Now recent if fetchconf.FetchRecentRefsDays > 0 { pruneRefDays := fetchconf.FetchRecentRefsDays + fetchconf.PruneOffsetDays tracerx.Printf("PRUNE: Retaining non-HEAD refs within %d (%d+%d) days", pruneRefDays, fetchconf.FetchRecentRefsDays, fetchconf.PruneOffsetDays) refsSince := time.Now().AddDate(0, 0, -pruneRefDays) // Keep all recent refs including any recent remote branches refs, err := git.RecentBranches(refsSince, fetchconf.FetchRecentRefsIncludeRemotes, "") if err != nil { Panic(err, "Could not scan for recent refs") } for _, ref := range refs { if commits.Add(ref.Sha) { // A new commit waitg.Add(1) go pruneTaskGetRetainedAtRef(ref.Sha, retainChan, errorChan, waitg) } } } // For every unique commit we've fetched, check recent commits too // Only if we're fetching recent commits, otherwise only keep at refs if fetchconf.FetchRecentCommitsDays > 0 { pruneCommitDays := fetchconf.FetchRecentCommitsDays + fetchconf.PruneOffsetDays for commit := range commits.Iter() { // We measure from the last commit at the ref summ, err := git.GetCommitSummary(commit) if err != nil { errorChan <- fmt.Errorf("Couldn't scan commits at %v: %v", commit, err) continue } commitsSince := summ.CommitDate.AddDate(0, 0, -pruneCommitDays) waitg.Add(1) go pruneTaskGetPreviousVersionsOfRef(commit, commitsSince, retainChan, errorChan, waitg) } } }
// Get additional arguments needed to limit 'git rev-list' to just the changes // in revTo that are also not on remoteName. // // Returns a slice of string command arguments, and a slice of string git // commits to pass to `git rev-list` via STDIN. func revListArgsRefVsRemote(refTo, remoteName string) ([]string, []string) { // We need to check that the locally cached versions of remote refs are still // present on the remote before we use them as a 'from' point. If the // server implements garbage collection and a remote branch had been deleted // since we last did 'git fetch --prune', then the objects in that branch may // have also been deleted on the server if unreferenced. // If some refs are missing on the remote, use a more explicit diff cachedRemoteRefs, _ := git.CachedRemoteRefs(remoteName) actualRemoteRefs, _ := git.RemoteRefs(remoteName) // Only check for missing refs on remote; if the ref is different it has moved // forward probably, and if not and the ref has changed to a non-descendant // (force push) then that will cause a re-evaluation in a subsequent command anyway missingRefs := tools.NewStringSet() for _, cachedRef := range cachedRemoteRefs { found := false for _, realRemoteRef := range actualRemoteRefs { if cachedRef.Type == realRemoteRef.Type && cachedRef.Name == realRemoteRef.Name { found = true break } } if !found { missingRefs.Add(cachedRef.Name) } } if len(missingRefs) > 0 { // Use only the non-missing refs as 'from' points commits := make([]string, 1, len(cachedRemoteRefs)+1) commits[0] = refTo for _, cachedRef := range cachedRemoteRefs { if !missingRefs.Contains(cachedRef.Name) { commits = append(commits, "^"+cachedRef.Sha) } } return []string{"--stdin"}, commits } else { // Safe to use cached return []string{refTo, "--not", "--remotes=" + remoteName}, nil } }
func newUploadContext(dryRun bool) *uploadContext { return &uploadContext{ DryRun: dryRun, uploadedOids: tools.NewStringSet(), } }