@@ -3,61 +3,172 @@ package git
3
3
import (
4
4
"errors"
5
5
"fmt"
6
+ "os"
6
7
7
8
"gopkg.in/src-d/go-git.v4/config"
8
9
"gopkg.in/src-d/go-git.v4/plumbing"
9
10
"gopkg.in/src-d/go-git.v4/plumbing/object"
10
11
"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/sideband"
11
12
"gopkg.in/src-d/go-git.v4/plumbing/storer"
12
13
"gopkg.in/src-d/go-git.v4/storage/filesystem"
13
- "gopkg.in/src-d/go-git.v4/storage/memory"
14
14
15
+ billy "srcd.works/go-billy.v1"
15
16
osfs "srcd.works/go-billy.v1/os"
16
17
)
17
18
18
19
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" )
24
28
)
25
29
26
30
// Repository giturl string, auth common.AuthMethod repository struct
27
31
type Repository struct {
28
- r map [string ]* Remote
29
- s Storer
32
+ r map [string ]* Remote
33
+ s Storer
34
+ wt billy.Filesystem
30
35
31
36
// Progress is where the human readable information sent by the server is
32
37
// stored, if nil nothing is stored and the capability (if supported)
33
38
// no-progress, is sent to the server to avoid send this information
34
39
Progress sideband.Progress
35
40
}
36
41
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
41
93
}
42
94
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 )
48
147
if err != nil {
49
148
return nil , err
50
149
}
51
150
52
- return NewRepository ( s )
151
+ return Open ( s , wt )
53
152
}
54
153
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 {
57
167
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
+ }
61
172
}
62
173
63
174
// Config return the repository config
@@ -136,15 +247,6 @@ func (r *Repository) DeleteRemote(name string) error {
136
247
137
248
// Clone clones a remote repository
138
249
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
-
148
250
if err := o .Validate (); err != nil {
149
251
return err
150
252
}
@@ -183,6 +285,10 @@ func (r *Repository) Clone(o *CloneOptions) error {
183
285
return err
184
286
}
185
287
288
+ if err := r .updateWorktree (); err != nil {
289
+ return err
290
+ }
291
+
186
292
return r .updateRemoteConfig (remote , o , c , head )
187
293
}
188
294
@@ -315,20 +421,6 @@ func updateReferenceStorerIfNeeded(
315
421
return false , nil
316
422
}
317
423
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
-
332
424
// Pull incorporates changes from a remote repository into the current branch.
333
425
// Returns nil if the operation is successful, NoErrAlreadyUpToDate if there are
334
426
// no changes to be fetched, or an error.
@@ -372,7 +464,25 @@ func (r *Repository) Pull(o *PullOptions) error {
372
464
return NoErrAlreadyUpToDate
373
465
}
374
466
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 ())
376
486
}
377
487
378
488
// Fetch fetches changes from a remote repository.
@@ -512,3 +622,17 @@ func (r *Repository) Reference(name plumbing.ReferenceName, resolved bool) (
512
622
func (r * Repository ) References () (storer.ReferenceIter , error ) {
513
623
return r .s .IterReferences ()
514
624
}
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