@@ -3,61 +3,172 @@ package git
33import (
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
1819var (
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
2731type 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
138249func (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) (
512622func (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