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

Commit 24c1878

Browse files
committed
new repository constructors and worktree
1 parent 1c2602a commit 24c1878

File tree

8 files changed

+535
-113
lines changed

8 files changed

+535
-113
lines changed

common_test.go

+23-4
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,19 @@ import (
44
"fmt"
55
"testing"
66

7+
"srcd.works/go-billy.v1/os"
8+
79
"gopkg.in/src-d/go-git.v4/fixtures"
10+
"gopkg.in/src-d/go-git.v4/plumbing"
811
"gopkg.in/src-d/go-git.v4/plumbing/format/packfile"
912
"gopkg.in/src-d/go-git.v4/plumbing/storer"
1013
"gopkg.in/src-d/go-git.v4/plumbing/transport"
1114
"gopkg.in/src-d/go-git.v4/plumbing/transport/client"
15+
"gopkg.in/src-d/go-git.v4/storage/filesystem"
16+
"gopkg.in/src-d/go-git.v4/storage/memory"
1217

1318
. "gopkg.in/check.v1"
19+
memoryfs "srcd.works/go-billy.v1/memory"
1420
)
1521

1622
func Test(t *testing.T) { TestingT(t) }
@@ -44,7 +50,14 @@ func (s *BaseSuite) buildBasicRepository(c *C) {
4450
}
4551

4652
func (s *BaseSuite) NewRepository(f *fixtures.Fixture) *Repository {
47-
r, err := NewFilesystemRepository(f.DotGit().Base())
53+
54+
fs := os.New(f.DotGit().Base())
55+
st, err := filesystem.NewStorage(fs)
56+
if err != nil {
57+
panic(err)
58+
}
59+
60+
r, err := Open(st, fs)
4861
if err != nil {
4962
panic(err)
5063
}
@@ -58,13 +71,12 @@ func (s *BaseSuite) NewRepositoryFromPackfile(f *fixtures.Fixture) *Repository {
5871
return r
5972
}
6073

61-
r := NewMemoryRepository()
62-
74+
storer := memory.NewStorage()
6375
p := f.Packfile()
6476
defer p.Close()
6577

6678
n := packfile.NewScanner(p)
67-
d, err := packfile.NewDecoder(n, r.s)
79+
d, err := packfile.NewDecoder(n, storer)
6880
if err != nil {
6981
panic(err)
7082
}
@@ -74,6 +86,13 @@ func (s *BaseSuite) NewRepositoryFromPackfile(f *fixtures.Fixture) *Repository {
7486
panic(err)
7587
}
7688

89+
storer.SetReference(plumbing.NewHashReference(plumbing.HEAD, f.Head))
90+
91+
r, err := Open(storer, memoryfs.New())
92+
if err != nil {
93+
panic(err)
94+
}
95+
7796
s.cache[h] = r
7897
return r
7998
}

fixtures/fixtures.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,10 @@ var fixtures = Fixtures{{
6767
URL: "https://github.com/git-fixtures/basic.git",
6868
DotGitHash: plumbing.NewHash("935e5ac17c41c309c356639816ea0694a568c484"),
6969
}, {
70-
70+
Tags: []string{".git", "worktree"},
71+
URL: "https://github.com/git-fixtures/basic.git",
72+
WorktreeHash: plumbing.NewHash("e4413db6700d0e72e7680b17c3d5ebbc2d1861bc"),
73+
}, {
7174
Tags: []string{"packfile", ".git", "unpacked", "multi-packfile"},
7275
URL: "https://github.com/src-d/go-git.git",
7376
Head: plumbing.NewHash("e8788ad9165781196e917292d6055cba1d78664e"),
@@ -155,6 +158,7 @@ type Fixture struct {
155158
Head plumbing.Hash
156159
PackfileHash plumbing.Hash
157160
DotGitHash plumbing.Hash
161+
WorktreeHash plumbing.Hash
158162
ObjectsCount int32
159163
}
160164

plumbing/reference.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ func (r ReferenceName) Short() string {
4242
}
4343

4444
const (
45-
HEAD ReferenceName = "HEAD"
45+
HEAD ReferenceName = "HEAD"
46+
Master ReferenceName = "refs/heads/master"
4647
)
4748

4849
// Reference is a representation of git reference

remote_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ func (s *RemoteSuite) TestPushToEmptyRepository(c *C) {
229229

230230
dstSto, err := filesystem.NewStorage(dstFs)
231231
c.Assert(err, IsNil)
232-
dstRepo, err := NewRepository(dstSto)
232+
dstRepo, err := Open(dstSto, nil)
233233
c.Assert(err, IsNil)
234234

235235
iter, err := sto.IterReferences()

repository.go

+171-47
Original file line numberDiff line numberDiff line change
@@ -3,61 +3,172 @@ package git
33
import (
44
"errors"
55
"fmt"
6+
"os"
67

78
"gopkg.in/src-d/go-git.v4/config"
89
"gopkg.in/src-d/go-git.v4/plumbing"
910
"gopkg.in/src-d/go-git.v4/plumbing/object"
1011
"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/sideband"
1112
"gopkg.in/src-d/go-git.v4/plumbing/storer"
1213
"gopkg.in/src-d/go-git.v4/storage/filesystem"
13-
"gopkg.in/src-d/go-git.v4/storage/memory"
1414

15+
billy "srcd.works/go-billy.v1"
1516
osfs "srcd.works/go-billy.v1/os"
1617
)
1718

1819
var (
19-
ErrObjectNotFound = errors.New("object not found")
20-
ErrInvalidReference = errors.New("invalid reference, should be a tag or a branch")
21-
ErrRepositoryNonEmpty = errors.New("repository non empty")
22-
ErrRemoteNotFound = errors.New("remote not found")
23-
ErrRemoteExists = errors.New("remote already exists")
20+
ErrObjectNotFound = errors.New("object not found")
21+
ErrInvalidReference = errors.New("invalid reference, should be a tag or a branch")
22+
ErrRepositoryNotExists = errors.New("repository not exists")
23+
ErrRepositoryAlreadyExists = errors.New("repository already exists")
24+
ErrRemoteNotFound = errors.New("remote not found")
25+
ErrRemoteExists = errors.New("remote already exists")
26+
ErrWorktreeNotProvided = errors.New("worktree should be provided")
27+
ErrIsBareRepository = errors.New("worktree not available in a bare repository")
2428
)
2529

2630
// Repository giturl string, auth common.AuthMethod repository struct
2731
type Repository struct {
28-
r map[string]*Remote
29-
s Storer
32+
r map[string]*Remote
33+
s Storer
34+
wt billy.Filesystem
3035

3136
// Progress is where the human readable information sent by the server is
3237
// stored, if nil nothing is stored and the capability (if supported)
3338
// no-progress, is sent to the server to avoid send this information
3439
Progress sideband.Progress
3540
}
3641

37-
// NewMemoryRepository creates a new repository, backed by a memory.Storage
38-
func NewMemoryRepository() *Repository {
39-
r, _ := NewRepository(memory.NewStorage())
40-
return r
42+
// Init create an empty git repository, based on the given Storer and worktree.
43+
// The worktree Filesystem is optional, if nil a bare repository is created. If
44+
// the given storer is not empty ErrRepositoryAlreadyExists is returned
45+
func Init(s Storer, worktree billy.Filesystem) (*Repository, error) {
46+
r := newRepository(s, worktree)
47+
_, err := r.Reference(plumbing.HEAD, false)
48+
switch err {
49+
case plumbing.ErrReferenceNotFound:
50+
case nil:
51+
return nil, ErrRepositoryAlreadyExists
52+
default:
53+
return nil, err
54+
}
55+
56+
h := plumbing.NewSymbolicReference(plumbing.HEAD, plumbing.Master)
57+
if err := s.SetReference(h); err != nil {
58+
return nil, err
59+
}
60+
61+
if worktree == nil {
62+
r.setIsBare(true)
63+
}
64+
65+
return r, nil
66+
}
67+
68+
// Open opens a git repository using the given Storer and worktree filesystem,
69+
// if the given storer is complete empty ErrRepositoryNotExists is returned.
70+
// The worktree can be nil when the repository being open is bare, if the
71+
// a non-bare repository is not provied and worktree is nil, the err
72+
// ErrWorktreeNotProvided is returned
73+
func Open(s Storer, worktree billy.Filesystem) (*Repository, error) {
74+
_, err := s.Reference(plumbing.HEAD)
75+
if err == plumbing.ErrReferenceNotFound {
76+
return nil, ErrRepositoryNotExists
77+
}
78+
79+
if err != nil {
80+
return nil, err
81+
}
82+
83+
cfg, err := s.Config()
84+
if err != nil {
85+
return nil, err
86+
}
87+
88+
if !cfg.Core.IsBare && worktree == nil {
89+
return nil, ErrWorktreeNotProvided
90+
}
91+
92+
return newRepository(s, worktree), nil
4193
}
4294

43-
// NewFilesystemRepository creates a new repository, backed by a filesystem.Storage
44-
// based on a fs.OS, if you want to use a custom one you need to use the function
45-
// NewRepository and build you filesystem.Storage
46-
func NewFilesystemRepository(path string) (*Repository, error) {
47-
s, err := filesystem.NewStorage(osfs.New(path))
95+
// Clone a repository into the given Storer and worktree Filesystem with the
96+
// given options, if worktree is nil a bare repository is created. If the given
97+
// storer is not empty ErrRepositoryAlreadyExists is returned
98+
func Clone(s Storer, worktree billy.Filesystem, o *CloneOptions) (*Repository, error) {
99+
r, err := Init(s, worktree)
100+
if err != nil {
101+
return nil, err
102+
}
103+
104+
return r, r.Clone(o)
105+
}
106+
107+
// PlainInit create an empty git repository at the given path. isBare defines
108+
// if the repository will have worktree (non-bare) or not (bare), if the path
109+
// is not empty ErrRepositoryAlreadyExists is returned
110+
func PlainInit(path string, isBare bool) (*Repository, error) {
111+
var wt, dot billy.Filesystem
112+
113+
if isBare {
114+
dot = osfs.New(path)
115+
} else {
116+
wt = osfs.New(path)
117+
dot = wt.Dir(".git")
118+
}
119+
120+
s, err := filesystem.NewStorage(dot)
121+
if err != nil {
122+
return nil, err
123+
}
124+
125+
return Init(s, wt)
126+
}
127+
128+
// PlainOpen opens a git repository from the given path. It detects is the
129+
// repository is bare or a normal one. If the path doesn't contain a valid
130+
// repository ErrRepositoryNotExists is returned
131+
func PlainOpen(path string) (*Repository, error) {
132+
var wt, dot billy.Filesystem
133+
134+
fs := osfs.New(path)
135+
if _, err := fs.Stat(".git"); err != nil {
136+
if !os.IsNotExist(err) {
137+
return nil, err
138+
}
139+
140+
dot = fs
141+
} else {
142+
wt = fs
143+
dot = fs.Dir(".git")
144+
}
145+
146+
s, err := filesystem.NewStorage(dot)
48147
if err != nil {
49148
return nil, err
50149
}
51150

52-
return NewRepository(s)
151+
return Open(s, wt)
53152
}
54153

55-
// NewRepository creates a new repository with the given Storage
56-
func NewRepository(s Storer) (*Repository, error) {
154+
// PlainClone a repository into the path with the given options, isBare defines
155+
// if the new repository will be bare or normal. If the path is not empty
156+
// ErrRepositoryAlreadyExists is returned
157+
func PlainClone(path string, isBare bool, o *CloneOptions) (*Repository, error) {
158+
r, err := PlainInit(path, isBare)
159+
if err != nil {
160+
return nil, err
161+
}
162+
163+
return r, r.Clone(o)
164+
}
165+
166+
func newRepository(s Storer, worktree billy.Filesystem) *Repository {
57167
return &Repository{
58-
s: s,
59-
r: make(map[string]*Remote, 0),
60-
}, nil
168+
s: s,
169+
wt: worktree,
170+
r: make(map[string]*Remote, 0),
171+
}
61172
}
62173

63174
// Config return the repository config
@@ -136,15 +247,6 @@ func (r *Repository) DeleteRemote(name string) error {
136247

137248
// Clone clones a remote repository
138249
func (r *Repository) Clone(o *CloneOptions) error {
139-
empty, err := r.IsEmpty()
140-
if err != nil {
141-
return err
142-
}
143-
144-
if !empty {
145-
return ErrRepositoryNonEmpty
146-
}
147-
148250
if err := o.Validate(); err != nil {
149251
return err
150252
}
@@ -183,6 +285,10 @@ func (r *Repository) Clone(o *CloneOptions) error {
183285
return err
184286
}
185287

288+
if err := r.updateWorktree(); err != nil {
289+
return err
290+
}
291+
186292
return r.updateRemoteConfig(remote, o, c, head)
187293
}
188294

@@ -315,20 +421,6 @@ func updateReferenceStorerIfNeeded(
315421
return false, nil
316422
}
317423

318-
// IsEmpty returns true if the repository is empty
319-
func (r *Repository) IsEmpty() (bool, error) {
320-
iter, err := r.References()
321-
if err != nil {
322-
return false, err
323-
}
324-
325-
var count int
326-
return count == 0, iter.ForEach(func(r *plumbing.Reference) error {
327-
count++
328-
return nil
329-
})
330-
}
331-
332424
// Pull incorporates changes from a remote repository into the current branch.
333425
// Returns nil if the operation is successful, NoErrAlreadyUpToDate if there are
334426
// no changes to be fetched, or an error.
@@ -372,7 +464,25 @@ func (r *Repository) Pull(o *PullOptions) error {
372464
return NoErrAlreadyUpToDate
373465
}
374466

375-
return nil
467+
return r.updateWorktree()
468+
}
469+
470+
func (r *Repository) updateWorktree() error {
471+
if r.wt == nil {
472+
return nil
473+
}
474+
475+
w, err := r.Worktree(nil)
476+
if err != nil {
477+
return err
478+
}
479+
480+
h, err := r.Head()
481+
if err != nil {
482+
return err
483+
}
484+
485+
return w.Checkout(h.Hash())
376486
}
377487

378488
// Fetch fetches changes from a remote repository.
@@ -512,3 +622,17 @@ func (r *Repository) Reference(name plumbing.ReferenceName, resolved bool) (
512622
func (r *Repository) References() (storer.ReferenceIter, error) {
513623
return r.s.IterReferences()
514624
}
625+
626+
// Worktree returns a worktree based on the given fs, if nil the default
627+
// worktree will be used.
628+
func (r *Repository) Worktree(fs billy.Filesystem) (*Worktree, error) {
629+
if r.wt == nil && fs == nil {
630+
return nil, ErrIsBareRepository
631+
}
632+
633+
if fs == nil {
634+
fs = r.wt
635+
}
636+
637+
return &Worktree{r: r, fs: fs}, nil
638+
}

0 commit comments

Comments
 (0)