git-lfs/lfs/gitscanner_remotes.go
Billy Keyes 5a7569a5d3 git: improve performance of remote ref listing
When repositories have a large number (hundreds of thousands) of refs, Git
LFS adds noticeable overhead on the time taken by `git show-ref` and
`git ls-remote`. To reduce this overhead:

* Avoid regular expressions when parsing lines of Git command output.
  Regexp evaluation was the most expensive part of this process in
  profiling.

* Minimize the number of loops when computing skipped refs by combining
  the missing and skipped checks and converting the list of remote refs
  into a pre-filtered set.

In a repository with ~300k remote references, these changes reduce the
time spent in the pre-push command from 8.26s to 6.31s.
2020-07-01 22:51:27 -07:00

39 lines
1.5 KiB
Go

package lfs
import (
"github.com/git-lfs/git-lfs/git"
"github.com/git-lfs/git-lfs/tools"
)
// calcSkippedRefs checks that locally cached versions of remote refs are still
// present on the remote before they are used 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 command.
func calcSkippedRefs(remote string) []string {
cachedRemoteRefs, _ := git.CachedRemoteRefs(remote)
actualRemoteRefs, _ := git.RemoteRefs(remote)
// The set of remote refs can be very large. Since CachedRemoteRefs only
// returns branches, filter the remote refs and convert them to a set for
// faster lookups in the skip calculation loop.
actualRemoteBranchRefs := tools.NewStringSet()
for _, ref := range actualRemoteRefs {
if ref.Type == git.RefTypeRemoteBranch {
actualRemoteBranchRefs.Add(ref.Name)
}
}
// 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.
var skippedRefs []string
for _, cachedRef := range cachedRemoteRefs {
if actualRemoteBranchRefs.Contains(cachedRef.Name) {
skippedRefs = append(skippedRefs, "^"+cachedRef.Sha)
}
}
return skippedRefs
}