Skip to content
This repository was archived by the owner on Sep 11, 2020. It is now read-only.

Commit 2ab6d5c

Browse files
authored
Merge pull request #1066 from keybase/strib/909-git-push-speedup-when-local
remote: speed up pushes when the "remote" repo is local
2 parents dcc9f37 + 3889c64 commit 2ab6d5c

File tree

6 files changed

+104
-17
lines changed

6 files changed

+104
-17
lines changed

config/config.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"sort"
99
"strconv"
1010

11+
"gopkg.in/src-d/go-git.v4/internal/url"
1112
format "gopkg.in/src-d/go-git.v4/plumbing/format/config"
1213
)
1314

@@ -399,3 +400,7 @@ func (c *RemoteConfig) marshal() *format.Subsection {
399400

400401
return c.raw
401402
}
403+
404+
func (c *RemoteConfig) IsFirstURLLocal() bool {
405+
return url.IsLocalEndpoint(c.URLs[0])
406+
}

internal/url/url.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package url
2+
3+
import (
4+
"regexp"
5+
)
6+
7+
var (
8+
isSchemeRegExp = regexp.MustCompile(`^[^:]+://`)
9+
scpLikeUrlRegExp = regexp.MustCompile(`^(?:(?P<user>[^@]+)@)?(?P<host>[^:\s]+):(?:(?P<port>[0-9]{1,5})/)?(?P<path>[^\\].*)$`)
10+
)
11+
12+
// MatchesScheme returns true if the given string matches a URL-like
13+
// format scheme.
14+
func MatchesScheme(url string) bool {
15+
return isSchemeRegExp.MatchString(url)
16+
}
17+
18+
// MatchesScpLike returns true if the given string matches an SCP-like
19+
// format scheme.
20+
func MatchesScpLike(url string) bool {
21+
return scpLikeUrlRegExp.MatchString(url)
22+
}
23+
24+
// FindScpLikeComponents returns the user, host, port and path of the
25+
// given SCP-like URL.
26+
func FindScpLikeComponents(url string) (user, host, port, path string) {
27+
m := scpLikeUrlRegExp.FindStringSubmatch(url)
28+
return m[1], m[2], m[3], m[4]
29+
}
30+
31+
// IsLocalEndpoint returns true if the given URL string specifies a
32+
// local file endpoint. For example, on a Linux machine,
33+
// `/home/user/src/go-git` would match as a local endpoint, but
34+
// `https://github.com/src-d/go-git` would not.
35+
func IsLocalEndpoint(url string) bool {
36+
return !MatchesScheme(url) && !MatchesScpLike(url)
37+
}

plumbing/revlist/revlist.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,20 @@ func Objects(
2121
objs,
2222
ignore []plumbing.Hash,
2323
) ([]plumbing.Hash, error) {
24-
ignore, err := objects(s, ignore, nil, true)
24+
return ObjectsWithStorageForIgnores(s, s, objs, ignore)
25+
}
26+
27+
// ObjectsWithStorageForIgnores is the same as Objects, but a
28+
// secondary storage layer can be provided, to be used to finding the
29+
// full set of objects to be ignored while finding the reachable
30+
// objects. This is useful when the main `s` storage layer is slow
31+
// and/or remote, while the ignore list is available somewhere local.
32+
func ObjectsWithStorageForIgnores(
33+
s, ignoreStore storer.EncodedObjectStorer,
34+
objs,
35+
ignore []plumbing.Hash,
36+
) ([]plumbing.Hash, error) {
37+
ignore, err := objects(ignoreStore, ignore, nil, true)
2538
if err != nil {
2639
return nil, err
2740
}
@@ -114,7 +127,6 @@ func reachableObjects(
114127
i := object.NewCommitPreorderIter(commit, seen, ignore)
115128
pending := make(map[plumbing.Hash]bool)
116129
addPendingParents(pending, visited, commit)
117-
118130
for {
119131
commit, err := i.Next()
120132
if err == io.EOF {

plumbing/revlist/revlist_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,32 @@ func (s *RevListSuite) TestRevListObjectsTagObject(c *C) {
129129
c.Assert(len(hist), Equals, len(expected))
130130
}
131131

132+
func (s *RevListSuite) TestRevListObjectsWithStorageForIgnores(c *C) {
133+
sto := filesystem.NewStorage(
134+
fixtures.ByTag("merge-conflict").One().DotGit(),
135+
cache.NewObjectLRUDefault())
136+
137+
// The "merge-conflict" repo has one extra commit in it, with a
138+
// two files modified in two different subdirs.
139+
expected := map[string]bool{
140+
"1980fcf55330d9d94c34abee5ab734afecf96aba": true, // commit
141+
"73d9cf44e9045254346c73f6646b08f9302c8570": true, // root dir
142+
"e8435d512a98586bd2e4fcfcdf04101b0bb1b500": true, // go/
143+
"257cc5642cb1a054f08cc83f2d943e56fd3ebe99": true, // haskal.hs
144+
"d499a1a0b79b7d87a35155afd0c1cce78b37a91c": true, // example.go
145+
"d108adc364fb6f21395d011ae2c8a11d96905b0d": true, // haskal/
146+
}
147+
148+
hist, err := ObjectsWithStorageForIgnores(sto, s.Storer, []plumbing.Hash{plumbing.NewHash("1980fcf55330d9d94c34abee5ab734afecf96aba")}, []plumbing.Hash{plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5")})
149+
c.Assert(err, IsNil)
150+
151+
for _, h := range hist {
152+
c.Assert(expected[h.String()], Equals, true)
153+
}
154+
155+
c.Assert(len(hist), Equals, len(expected))
156+
}
157+
132158
// ---
133159
// | |\
134160
// | | * b8e471f Creating changelog

plumbing/transport/common.go

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ import (
1919
"fmt"
2020
"io"
2121
"net/url"
22-
"regexp"
2322
"strconv"
2423
"strings"
2524

25+
giturl "gopkg.in/src-d/go-git.v4/internal/url"
2626
"gopkg.in/src-d/go-git.v4/plumbing"
2727
"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp"
2828
"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability"
@@ -224,34 +224,28 @@ func getPath(u *url.URL) string {
224224
return res
225225
}
226226

227-
var (
228-
isSchemeRegExp = regexp.MustCompile(`^[^:]+://`)
229-
scpLikeUrlRegExp = regexp.MustCompile(`^(?:(?P<user>[^@]+)@)?(?P<host>[^:\s]+):(?:(?P<port>[0-9]{1,5})/)?(?P<path>[^\\].*)$`)
230-
)
231-
232227
func parseSCPLike(endpoint string) (*Endpoint, bool) {
233-
if isSchemeRegExp.MatchString(endpoint) || !scpLikeUrlRegExp.MatchString(endpoint) {
228+
if giturl.MatchesScheme(endpoint) || !giturl.MatchesScpLike(endpoint) {
234229
return nil, false
235230
}
236231

237-
m := scpLikeUrlRegExp.FindStringSubmatch(endpoint)
238-
239-
port, err := strconv.Atoi(m[3])
232+
user, host, portStr, path := giturl.FindScpLikeComponents(endpoint)
233+
port, err := strconv.Atoi(portStr)
240234
if err != nil {
241235
port = 22
242236
}
243237

244238
return &Endpoint{
245239
Protocol: "ssh",
246-
User: m[1],
247-
Host: m[2],
240+
User: user,
241+
Host: host,
248242
Port: port,
249-
Path: m[4],
243+
Path: path,
250244
}, true
251245
}
252246

253247
func parseFile(endpoint string) (*Endpoint, bool) {
254-
if isSchemeRegExp.MatchString(endpoint) {
248+
if giturl.MatchesScheme(endpoint) {
255249
return nil, false
256250
}
257251

remote.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ import (
66
"fmt"
77
"io"
88

9+
"gopkg.in/src-d/go-billy.v4/osfs"
910
"gopkg.in/src-d/go-git.v4/config"
1011
"gopkg.in/src-d/go-git.v4/plumbing"
12+
"gopkg.in/src-d/go-git.v4/plumbing/cache"
1113
"gopkg.in/src-d/go-git.v4/plumbing/format/packfile"
1214
"gopkg.in/src-d/go-git.v4/plumbing/object"
1315
"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp"
@@ -18,6 +20,7 @@ import (
1820
"gopkg.in/src-d/go-git.v4/plumbing/transport"
1921
"gopkg.in/src-d/go-git.v4/plumbing/transport/client"
2022
"gopkg.in/src-d/go-git.v4/storage"
23+
"gopkg.in/src-d/go-git.v4/storage/filesystem"
2124
"gopkg.in/src-d/go-git.v4/storage/memory"
2225
"gopkg.in/src-d/go-git.v4/utils/ioutil"
2326
)
@@ -149,7 +152,17 @@ func (r *Remote) PushContext(ctx context.Context, o *PushOptions) (err error) {
149152
var hashesToPush []plumbing.Hash
150153
// Avoid the expensive revlist operation if we're only doing deletes.
151154
if !allDelete {
152-
hashesToPush, err = revlist.Objects(r.s, objects, haves)
155+
if r.c.IsFirstURLLocal() {
156+
// If we're are pushing to a local repo, it might be much
157+
// faster to use a local storage layer to get the commits
158+
// to ignore, when calculating the object revlist.
159+
localStorer := filesystem.NewStorage(
160+
osfs.New(r.c.URLs[0]), cache.NewObjectLRUDefault())
161+
hashesToPush, err = revlist.ObjectsWithStorageForIgnores(
162+
r.s, localStorer, objects, haves)
163+
} else {
164+
hashesToPush, err = revlist.Objects(r.s, objects, haves)
165+
}
153166
if err != nil {
154167
return err
155168
}

0 commit comments

Comments
 (0)