aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJavi Fontan <jfontan@gmail.com>2018-05-14 17:16:16 +0200
committerJavi Fontan <jfontan@gmail.com>2018-05-14 17:16:16 +0200
commit939793b42974c12abf2d2b65facee489004a9e06 (patch)
tree9e8386962b8000781c851dc9367839d9e8d2ce65
parente63b032e91ce35e0ecd5f27d43be655625e8af36 (diff)
downloadgo-git-939793b42974c12abf2d2b65facee489004a9e06.tar.gz
git: remote, Do not iterate all references on update.
The current code iterates all the references in the remote to check if they match the refspec. This is OK when the refspec is a wildcard but is a waste of time when they are not. A hash with references is generated for fast access before starting the update and used only when the refspec is not a wildcard. In a repository with 7800 references this meant 7800 * 7800 checks. With the current code it took 8m30s to update the references. With the new code it takes less than 0.5s. References are already extensively tested in remote_test.go. Signed-off-by: Javi Fontan <jfontan@gmail.com>
-rw-r--r--remote.go24
1 files changed, 22 insertions, 2 deletions
diff --git a/remote.go b/remote.go
index 4b86955..60461d6 100644
--- a/remote.go
+++ b/remote.go
@@ -371,14 +371,22 @@ func (r *Remote) addReferencesToUpdate(
refspecs []config.RefSpec,
localRefs []*plumbing.Reference,
remoteRefs storer.ReferenceStorer,
- req *packp.ReferenceUpdateRequest) error {
+ req *packp.ReferenceUpdateRequest,
+) error {
+ // This references dictionary will be used to search references by name.
+ refsDict := make(map[string]*plumbing.Reference)
+ for _, ref := range localRefs {
+ refsDict[ref.Name().String()] = ref
+ }
+
for _, rs := range refspecs {
if rs.IsDelete() {
if err := r.deleteReferences(rs, remoteRefs, req); err != nil {
return err
}
} else {
- if err := r.addOrUpdateReferences(rs, localRefs, remoteRefs, req); err != nil {
+ err := r.addOrUpdateReferences(rs, localRefs, refsDict, remoteRefs, req)
+ if err != nil {
return err
}
}
@@ -390,9 +398,21 @@ func (r *Remote) addReferencesToUpdate(
func (r *Remote) addOrUpdateReferences(
rs config.RefSpec,
localRefs []*plumbing.Reference,
+ refsDict map[string]*plumbing.Reference,
remoteRefs storer.ReferenceStorer,
req *packp.ReferenceUpdateRequest,
) error {
+ // If it is not a wilcard refspec we can directly search for the reference
+ // in the references dictionary.
+ if !rs.IsWildcard() {
+ ref, ok := refsDict[rs.Src()]
+ if !ok {
+ return nil
+ }
+
+ return r.addReferenceIfRefSpecMatches(rs, remoteRefs, ref, req)
+ }
+
for _, ref := range localRefs {
err := r.addReferenceIfRefSpecMatches(rs, remoteRefs, ref, req)
if err != nil {