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

Commit 5e73f01

Browse files
alcortesmmcuadros
authored andcommitted
Adds support to open local repositories and to use file-based object storage (#55)
* remove some comments * idx writer/reader * Shut up ssh tests, they are annoying * Add file scheme test to clients * Add dummy file client * Add test fot file client * Make tests use fixture endpoint * add parser for packed-refs format * add parser for packed-refs format * WIP adding dir.Refs() tests * Add test for fixture refs * refs parser for the refs directory * Documentation * Add Capabilities to file client * tgz.Exatract now accpets a path instead of a Reader * fix bug in idxfile fanout calculation * remove dead code * packfile documentation * clean packfile parser code * add core.Object.Content() and returns errors for core.ObjectStorage.Iter() * add seekable storage * add dir repos to NewRepository * clean prints * Add dir client documentation to README * Organize the README * README * Clean tgz package * Clean temp dirs after tgz tests * Gometalinter on gitdir * Clean pattern function * metalinter tgz * metalinter gitdir * gitdir coverage and remove seekable packfile filedescriptor leak * gitdir Idxfile tests and remove file descriptor leak * gitdir Idxfile tests when no idx is found * clean storage/seekable/internal/index and some formats/idxfile API issues * clean storage/seekable * clean formats/idx * turn packfile/doc.go into packfile/doc.txt * move formats/packfile/reader to decoder * fix packfile decoder error names * improve documentation * comment packfile decoder errors * comment public API (format/packfile) * remve duplicated code in packfile decoder test * move tracking_reader into an internal package and clean it * use iota for packfile format * rename packfile parse.go to packfile object_at.go * clean packfile deltas * fix delta header size bug * improve delta documentation * clean packfile deltas * clean packfiles deltas * clean repository.go * Remove go 1.5 from Travis CI Because go 1.5 does not suport internal packages. * change local repo scheme to local:// * change "local://" to "file://" as the local scheme * fix broken indentation * shortens names of variables in short scopes * more shortening of variable names * more shortening of variable names * Rename git dir client to "file", as the scheme used for it * Fix file format ctor name, now that the package name has change * Sortcut local repo constructor to not use remotes The object storage is build directly in the repository ctor, instead of creating a remote and waiting for the user to pull it. * update README and fix some errors in it * remove file scheme client * Local respositories has now a new ctor This is, they are no longer identified by the scheme of the URL, but are created different from inception. * remove unused URL field form Repository * move all git dir logic to seekable sotrage ctor * fix documentation * Make formats/file/dir an internal package to storage/seekable * change package storage/seekable to storage/fs * clean storage/fs * overall storage/fs clean * more cleaning * some metalinter fixes * upgrade cshared to last changes * remove dead code * fix test error info * remove file scheme check from clients * fix test error message * fix test error message * fix error messages * style changes * fix comments everywhere * style changes * style changes * scaffolding and tests for local packfiles without ifx files * outsource index building from packfile to the packfile decoder * refactor packfile header reading into a new function * move code to generate index from packfile back to index package * add header parsing * fix documentation errata * add undeltified and OFS delta support for index building from the packfile * add tests for packfile with ref-deltas * support for packfiles with ref-deltas and no idx * refactor packfile format parser to reuse code * refactor packfile format parser to reuse code * refactor packfile format parser to reuse code * refactor packfile format parser to reuse code * refactor packfile format parser to reuse code * WIP refactor packfile format parser to reuse code * refactor packfile format parser to reuse code * remove prints from tests * remove prints from tests * refactor packfile.core into packfile.parser * rename packfile reader to something that shows it is a recaller * rename cannot recall error * rename packfile.Reader to packfile.ReadRecaller and document * speed up test by using StreamReader instead of SeekableReader when possible * clean packfile StreamReader * stream_reader tests * refactor packfile.StreamReader into packfile.StreamReadRecaller * refactor packfile.SeekableReader into packfile.SeekableReadRecaller and document it * generalize packfile.StreamReadRecaller test to all packfile.ReadRecaller implementations * speed up storage/fs tests * speed up tests in . by loading packfiles in memory * speed up repository tests by using and smaller fixture * restore doc.go files * rename packfile.ReadRecaller implementations to shorter names * update comments to type changes * packfile.Parser test (WIP) * packfile.Parser tests and add ForgetAll() to packfile.ReadRecaller * add test for packfile.ReadRecaller.ForgetAll() * clarify seekable being able to recallByOffset forgetted objects * use better names for internal maps * metalinter packfile package * speed up some tests * documentation fixes * change storage.fs package name to storage.proxy to avoid confusion with new filesystem support * New fs package and os transparent implementation Now NewRepositoryFromFS receives a fs and a path and tests are modified accordingly, but it is still not using for anything. * add fs to gitdir and proxy.store * reduce fs interface for easier implementation * remove garbage dirs from tgz tests * change file name gitdir/dir.go to gitdir/gitdir.go * fs.OS tests * metalinter utils/fs * add NewRepositoryFromFS documentation to README * Readability fixes to README * move tgz to an external dependency * move filesystem impl. example to example dir * rename proxy/store.go to proxy/storage.go for coherence with memory/storage.go * rename proxy package to seekable
1 parent 808076a commit 5e73f01

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+6630
-924
lines changed

.travis.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ language: go
22

33
go:
44
- 1.4
5-
- 1.5
65
- 1.6
76
- tip
87

README.md

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,10 @@ if err := r.PullDefault(); err != nil {
4949
panic(err)
5050
}
5151

52-
iter := r.Commits()
52+
iter, err := r.Commits()
53+
if err != nil {
54+
panic(err)
55+
}
5356
defer iter.Close()
5457

5558
for {
@@ -112,6 +115,61 @@ if err != nil {
112115
fmt.Println(commit)
113116
```
114117

118+
Creating a repository from an ordinary local git directory (that has been
119+
previously prepared by running `git gc` on it).
120+
121+
```go
122+
// Download any git repository and prepare it as as follows:
123+
//
124+
// $ git clone https://github.com/src-d/go-git /tmp/go-git
125+
// $ pushd /tmp/go-git ; git gc ; popd
126+
//
127+
// Then, create a go-git repository from the local content
128+
// and print its commits as follows:
129+
130+
package main
131+
132+
import (
133+
"fmt"
134+
"io"
135+
136+
"gopkg.in/src-d/go-git.v3"
137+
"gopkg.in/src-d/go-git.v3/utils/fs"
138+
)
139+
140+
func main() {
141+
fs := fs.NewOS() // a simple proxy for the local host filesystem
142+
path := "/tmp/go-git/.git"
143+
144+
repo, err := git.NewRepositoryFromFS(fs, path)
145+
if err != nil {
146+
panic(err)
147+
}
148+
149+
iter, err := repo.Commits()
150+
if err != nil {
151+
panic(err)
152+
}
153+
defer iter.Close()
154+
155+
for {
156+
commit, err := iter.Next()
157+
if err != nil {
158+
if err == io.EOF {
159+
break
160+
}
161+
panic(err)
162+
}
163+
164+
fmt.Println(commit)
165+
}
166+
}
167+
```
168+
169+
Implementing your own filesystem will let you access repositories stored on
170+
remote services (e.g. amazon S3), see the
171+
[examples](https://github.com/src-d/go-git/tree/master/examples/fs_implementation/)
172+
directory for a simple filesystem implementation and usage.
115173

116174
Wrapping
117175
--------

blame_test.go

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package git
22

33
import (
4+
"bytes"
5+
"io/ioutil"
46
"os"
57

68
"gopkg.in/src-d/go-git.v3/core"
@@ -19,24 +21,23 @@ var _ = Suite(&BlameCommon{})
1921
func (s *BlameCommon) SetUpSuite(c *C) {
2022
s.repos = make(map[string]*Repository, 0)
2123
for _, fixRepo := range fixtureRepos {
22-
repo := NewPlainRepository()
23-
repo.URL = fixRepo.url
24+
r := NewPlainRepository()
2425

25-
d, err := os.Open(fixRepo.packfile)
26+
f, err := os.Open(fixRepo.packfile)
2627
c.Assert(err, IsNil)
2728

28-
r := packfile.NewReader(d)
29-
// TODO: how to know the format of a pack file ahead of time?
30-
// Some info at:
31-
// https://codewords.recurse.com/issues/three/unpacking-git-packfiles
32-
r.Format = packfile.OFSDeltaFormat
29+
data, err := ioutil.ReadAll(f)
30+
c.Assert(err, IsNil)
31+
32+
stream := packfile.NewStream(bytes.NewReader(data))
3333

34-
_, err = r.Read(repo.Storage)
34+
d := packfile.NewDecoder(stream)
35+
err = d.Decode(r.Storage)
3536
c.Assert(err, IsNil)
3637

37-
c.Assert(d.Close(), IsNil)
38+
c.Assert(f.Close(), IsNil)
3839

39-
s.repos[fixRepo.url] = repo
40+
s.repos[fixRepo.url] = r
4041
}
4142
}
4243

@@ -48,22 +49,22 @@ type blameTest struct {
4849
}
4950

5051
func (s *BlameCommon) mockBlame(t blameTest, c *C) (blame *Blame) {
51-
repo, ok := s.repos[t.repo]
52+
r, ok := s.repos[t.repo]
5253
c.Assert(ok, Equals, true)
5354

54-
commit, err := repo.Commit(core.NewHash(t.rev))
55-
c.Assert(err, IsNil, Commentf("%v: repo=%s, rev=%s", err, repo, t.rev))
55+
commit, err := r.Commit(core.NewHash(t.rev))
56+
c.Assert(err, IsNil, Commentf("%v: repo=%s, rev=%s", err, r, t.rev))
5657

57-
file, err := commit.File(t.path)
58+
f, err := commit.File(t.path)
5859
c.Assert(err, IsNil)
59-
lines, err := file.Lines()
60+
lines, err := f.Lines()
6061
c.Assert(err, IsNil)
6162
c.Assert(len(t.blames), Equals, len(lines), Commentf(
6263
"repo=%s, path=%s, rev=%s: the number of lines in the file and the number of expected blames differ (len(blames)=%d, len(lines)=%d)\nblames=%#q\nlines=%#q", t.repo, t.path, t.rev, len(t.blames), len(lines), t.blames, lines))
6364

6465
blamedLines := make([]*line, 0, len(t.blames))
6566
for i := range t.blames {
66-
commit, err := repo.Commit(core.NewHash(t.blames[i]))
67+
commit, err := r.Commit(core.NewHash(t.blames[i]))
6768
c.Assert(err, IsNil)
6869
l := &line{
6970
author: commit.Author.Email,
@@ -82,17 +83,17 @@ func (s *BlameCommon) mockBlame(t blameTest, c *C) (blame *Blame) {
8283
// run a blame on all the suite's tests
8384
func (s *BlameCommon) TestBlame(c *C) {
8485
for _, t := range blameTests {
85-
expected := s.mockBlame(t, c)
86+
exp := s.mockBlame(t, c)
8687

87-
repo, ok := s.repos[t.repo]
88+
r, ok := s.repos[t.repo]
8889
c.Assert(ok, Equals, true)
8990

90-
commit, err := repo.Commit(core.NewHash(t.rev))
91+
commit, err := r.Commit(core.NewHash(t.rev))
9192
c.Assert(err, IsNil)
9293

93-
obtained, err := commit.Blame(t.path)
94+
obt, err := commit.Blame(t.path)
9495
c.Assert(err, IsNil)
95-
c.Assert(obtained, DeepEquals, expected)
96+
c.Assert(obt, DeepEquals, exp)
9697
}
9798
}
9899

@@ -105,16 +106,18 @@ func repeat(s string, n int) []string {
105106
for i := 0; i < n; i++ {
106107
r = append(r, s)
107108
}
109+
108110
return r
109111
}
110112

111113
// utility function to concat slices
112114
func concat(vargs ...[]string) []string {
113-
var result []string
115+
var r []string
114116
for _, ss := range vargs {
115-
result = append(result, ss...)
117+
r = append(r, ss...)
116118
}
117-
return result
119+
120+
return r
118121
}
119122

120123
var blameTests = [...]blameTest{

clients/common.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,10 @@ func NewGitUploadPackService(repoURL string) (common.GitUploadPackService, error
5555
if err != nil {
5656
return nil, fmt.Errorf("invalid url %q", repoURL)
5757
}
58-
service, ok := KnownProtocols[u.Scheme]
58+
s, ok := KnownProtocols[u.Scheme]
5959
if !ok {
6060
return nil, fmt.Errorf("unsupported scheme %q", u.Scheme)
6161
}
6262

63-
return service, nil
63+
return s, nil
6464
}

clients/common/common.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -236,11 +236,7 @@ func (r *GitUploadPackInfo) decodeHeaderLine(line string) {
236236
}
237237

238238
func (r *GitUploadPackInfo) isValidLine(line string) bool {
239-
if line[0] == '#' {
240-
return false
241-
}
242-
243-
return true
239+
return line[0] != '#'
244240
}
245241

246242
func (r *GitUploadPackInfo) readLine(line string) {

clients/common_test.go

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,41 @@ package clients
33
import (
44
"fmt"
55
"io"
6+
"os"
67
"testing"
78

8-
. "gopkg.in/check.v1"
99
"gopkg.in/src-d/go-git.v3/clients/common"
10+
11+
"github.com/alcortesm/tgz"
12+
. "gopkg.in/check.v1"
1013
)
1114

1215
func Test(t *testing.T) { TestingT(t) }
1316

14-
type SuiteCommon struct{}
17+
type SuiteCommon struct {
18+
dirFixturePath string
19+
}
1520

1621
var _ = Suite(&SuiteCommon{})
1722

23+
const fixtureTGZ = "../storage/seekable/internal/gitdir/fixtures/spinnaker-gc.tgz"
24+
25+
func (s *SuiteCommon) SetUpSuite(c *C) {
26+
var err error
27+
s.dirFixturePath, err = tgz.Extract(fixtureTGZ)
28+
c.Assert(err, IsNil)
29+
}
30+
31+
func (s *SuiteCommon) TearDownSuite(c *C) {
32+
err := os.RemoveAll(s.dirFixturePath)
33+
c.Assert(err, IsNil)
34+
}
35+
1836
func (s *SuiteCommon) TestNewGitUploadPackService(c *C) {
1937
var tests = [...]struct {
20-
input string
21-
err bool
22-
expected string
38+
input string
39+
err bool
40+
exp string
2341
}{
2442
{"://example.com", true, "<nil>"},
2543
{"badscheme://github.com/src-d/go-git", true, "<nil>"},
@@ -30,8 +48,10 @@ func (s *SuiteCommon) TestNewGitUploadPackService(c *C) {
3048

3149
for i, t := range tests {
3250
output, err := NewGitUploadPackService(t.input)
33-
c.Assert(err != nil, Equals, t.err, Commentf("%d) %q: wrong error value", i, t.input))
34-
c.Assert(typeAsString(output), Equals, t.expected, Commentf("%d) %q: wrong type", i, t.input))
51+
c.Assert(err != nil, Equals, t.err,
52+
Commentf("%d) %q: wrong error value (was: %s)", i, t.input, err))
53+
c.Assert(typeAsString(output), Equals, t.exp,
54+
Commentf("%d) %q: wrong type", i, t.input))
3555
}
3656
}
3757

@@ -70,7 +90,6 @@ func (s *SuiteCommon) TestInstallProtocol(c *C) {
7090

7191
for i, t := range tests {
7292
if t.panic {
73-
fmt.Println(t.service == nil)
7493
c.Assert(func() { InstallProtocol(t.scheme, t.service) }, PanicMatches, `nil service`)
7594
continue
7695
}

clients/ssh/git_upload_pack_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// +build ssh
2+
13
package ssh
24

35
import (

common_test.go

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package git
22

33
import (
4+
"bytes"
45
"io"
6+
"io/ioutil"
57
"os"
68
"testing"
79

@@ -29,21 +31,22 @@ func (s *MockGitUploadPackService) ConnectWithAuth(url common.Endpoint, auth com
2931
}
3032

3133
func (s *MockGitUploadPackService) Info() (*common.GitUploadPackInfo, error) {
32-
hash := core.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5")
34+
h := core.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5")
3335

34-
cap := common.NewCapabilities()
35-
cap.Decode("6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEADmulti_ack thin-pack side-band side-band-64k ofs-delta shallow no-progress include-tag multi_ack_detailed no-done symref=HEAD:refs/heads/master agent=git/2:2.4.8~dbussink-fix-enterprise-tokens-compilation-1167-gc7006cf")
36+
c := common.NewCapabilities()
37+
c.Decode("6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEADmulti_ack thin-pack side-band side-band-64k ofs-delta shallow no-progress include-tag multi_ack_detailed no-done symref=HEAD:refs/heads/master agent=git/2:2.4.8~dbussink-fix-enterprise-tokens-compilation-1167-gc7006cf")
3638

3739
return &common.GitUploadPackInfo{
38-
Capabilities: cap,
39-
Head: hash,
40-
Refs: map[string]core.Hash{"refs/heads/master": hash},
40+
Capabilities: c,
41+
Head: h,
42+
Refs: map[string]core.Hash{"refs/heads/master": h},
4143
}, nil
4244
}
4345

4446
func (s *MockGitUploadPackService) Fetch(*common.GitUploadPackRequest) (io.ReadCloser, error) {
4547
var err error
4648
s.RC, err = os.Open("formats/packfile/fixtures/git-fixture.ref-delta")
49+
4750
return s.RC, err
4851
}
4952

@@ -65,20 +68,28 @@ func unpackFixtures(c *C, fixtures ...[]packedFixture) map[string]*Repository {
6568
if _, existing := repos[fixture.url]; existing {
6669
continue
6770
}
68-
repos[fixture.url] = NewPlainRepository()
6971

70-
d, err := os.Open(fixture.packfile)
71-
c.Assert(err, IsNil)
72+
comment := Commentf("fixture packfile: %q", fixture.packfile)
7273

73-
r := packfile.NewReader(d)
74-
r.Format = packfile.OFSDeltaFormat // This is hardcoded because we don't have a good way to sniff the format
74+
repos[fixture.url] = NewPlainRepository()
7575

76-
_, err = r.Read(repos[fixture.url].Storage)
76+
f, err := os.Open(fixture.packfile)
77+
c.Assert(err, IsNil, comment)
78+
79+
// increase memory consumption to speed up tests
80+
data, err := ioutil.ReadAll(f)
7781
c.Assert(err, IsNil)
82+
memStream := bytes.NewReader(data)
83+
r := packfile.NewStream(memStream)
84+
85+
d := packfile.NewDecoder(r)
86+
err = d.Decode(repos[fixture.url].Storage)
87+
c.Assert(err, IsNil, comment)
7888

79-
c.Assert(d.Close(), IsNil)
89+
c.Assert(f.Close(), IsNil, comment)
8090
}
8191
}
92+
8293
return repos
8394
}
8495

core/object.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,16 @@ type Object interface {
3434
SetType(ObjectType)
3535
Size() int64
3636
SetSize(int64)
37+
Content() []byte
3738
Reader() (ObjectReader, error)
3839
Writer() (ObjectWriter, error)
3940
}
4041

4142
// ObjectStorage generic storage of objects
4243
type ObjectStorage interface {
43-
New() (Object, error)
4444
Set(Object) (Hash, error)
4545
Get(Hash) (Object, error)
46-
Iter(ObjectType) ObjectIter
46+
Iter(ObjectType) (ObjectIter, error)
4747
}
4848

4949
// ObjectType internal object type's

0 commit comments

Comments
 (0)