diff options
-rw-r--r-- | _examples/clone/main.go | 5 | ||||
-rw-r--r-- | common.go | 19 | ||||
-rw-r--r-- | doc.go | 2 | ||||
-rw-r--r-- | options.go | 4 | ||||
-rw-r--r-- | remote.go | 9 | ||||
-rw-r--r-- | remote_test.go | 3 | ||||
-rw-r--r-- | repository.go | 40 | ||||
-rw-r--r-- | repository_test.go | 18 | ||||
-rw-r--r-- | submodule.go | 30 | ||||
-rw-r--r-- | worktree.go | 62 |
10 files changed, 142 insertions, 50 deletions
diff --git a/_examples/clone/main.go b/_examples/clone/main.go index 7d173a6..18d8a55 100644 --- a/_examples/clone/main.go +++ b/_examples/clone/main.go @@ -17,8 +17,9 @@ func main() { Info("git clone %s %s", url, directory) r, err := git.PlainClone(directory, false, &git.CloneOptions{ - URL: url, - Depth: 1, + URL: url, + RecursiveSubmodules: true, + Depth: 1, }) CheckIfError(err) @@ -1,23 +1,6 @@ package git -import ( - "strings" - - "srcd.works/go-git.v4/config" - "srcd.works/go-git.v4/plumbing/storer" -) - -// Storer is a generic storage of objects, references and any information -// related to a particular repository. The package srcd.works/go-git.v4/storage -// contains two implementation a filesystem base implementation (such as `.git`) -// and a memory implementations being ephemeral -type Storer interface { - storer.EncodedObjectStorer - storer.ReferenceStorer - storer.ShallowStorer - storer.IndexStorer - config.ConfigStorer -} +import "strings" // countLines returns the number of lines in a string à la git, this is // The newline character is assumed to be '\n'. The empty string @@ -7,4 +7,4 @@ // It is highly extensible, we have been following the open/close principle in // its design to facilitate extensions, mainly focusing the efforts on the // persistence of the objects. -package git +package git // import "srcd.works/go-git.v4" @@ -32,6 +32,10 @@ type CloneOptions struct { SingleBranch bool // Limit fetching to the specified number of commits Depth int + // RecursiveSubmodules after the clone is created, initialize all submodules + // within, using their default settings. This option is ignored if the + // cloned repository does not have a worktree + RecursiveSubmodules bool // Progress is where the human readable information sent by the server is // stored, if nil nothing is stored and the capability (if supported) // no-progress, is sent to the server to avoid send this information @@ -16,6 +16,7 @@ import ( "srcd.works/go-git.v4/plumbing/storer" "srcd.works/go-git.v4/plumbing/transport" "srcd.works/go-git.v4/plumbing/transport/client" + "srcd.works/go-git.v4/storage" "srcd.works/go-git.v4/storage/memory" "srcd.works/go-git.v4/utils/ioutil" ) @@ -25,10 +26,10 @@ var NoErrAlreadyUpToDate = errors.New("already up-to-date") // Remote represents a connection to a remote repository type Remote struct { c *config.RemoteConfig - s Storer + s storage.Storer } -func newRemote(s Storer, c *config.RemoteConfig) *Remote { +func newRemote(s storage.Storer, c *config.RemoteConfig) *Remote { return &Remote{s: s, c: c} } @@ -321,7 +322,9 @@ func getHaves(localRefs storer.ReferenceStorer) ([]plumbing.Hash, error) { return haves, nil } -func getWants(spec []config.RefSpec, localStorer Storer, remoteRefs storer.ReferenceStorer) ([]plumbing.Hash, error) { +func getWants( + spec []config.RefSpec, localStorer storage.Storer, remoteRefs storer.ReferenceStorer, +) ([]plumbing.Hash, error) { wantTags := true for _, s := range spec { if !s.IsWildcard() { diff --git a/remote_test.go b/remote_test.go index 1e34905..e13ef20 100644 --- a/remote_test.go +++ b/remote_test.go @@ -11,6 +11,7 @@ import ( "srcd.works/go-git.v4/config" "srcd.works/go-git.v4/plumbing" "srcd.works/go-git.v4/plumbing/storer" + "srcd.works/go-git.v4/storage" "srcd.works/go-git.v4/storage/filesystem" "srcd.works/go-git.v4/storage/memory" @@ -126,7 +127,7 @@ func (s *RemoteSuite) TestFetchWithProgress(c *C) { } type mockPackfileWriter struct { - Storer + storage.Storer PackfileWriterCalled bool } diff --git a/repository.go b/repository.go index a8dd7ef..2f5ff82 100644 --- a/repository.go +++ b/repository.go @@ -10,6 +10,7 @@ import ( "srcd.works/go-git.v4/plumbing" "srcd.works/go-git.v4/plumbing/object" "srcd.works/go-git.v4/plumbing/storer" + "srcd.works/go-git.v4/storage" "srcd.works/go-git.v4/storage/filesystem" "srcd.works/go-billy.v1" @@ -29,7 +30,7 @@ var ( // Repository represents a git repository type Repository struct { - Storer Storer + Storer storage.Storer r map[string]*Remote wt billy.Filesystem @@ -38,7 +39,7 @@ type Repository struct { // Init creates an empty git repository, based on the given Storer and worktree. // The worktree Filesystem is optional, if nil a bare repository is created. If // the given storer is not empty ErrRepositoryAlreadyExists is returned -func Init(s Storer, worktree billy.Filesystem) (*Repository, error) { +func Init(s storage.Storer, worktree billy.Filesystem) (*Repository, error) { r := newRepository(s, worktree) _, err := r.Reference(plumbing.HEAD, false) switch err { @@ -66,7 +67,7 @@ func Init(s Storer, worktree billy.Filesystem) (*Repository, error) { // The worktree can be nil when the repository being opened is bare, if the // repository is a normal one (not bare) and worktree is nil the err // ErrWorktreeNotProvided is returned -func Open(s Storer, worktree billy.Filesystem) (*Repository, error) { +func Open(s storage.Storer, worktree billy.Filesystem) (*Repository, error) { _, err := s.Reference(plumbing.HEAD) if err == plumbing.ErrReferenceNotFound { return nil, ErrRepositoryNotExists @@ -91,7 +92,7 @@ func Open(s Storer, worktree billy.Filesystem) (*Repository, error) { // Clone a repository into the given Storer and worktree Filesystem with the // given options, if worktree is nil a bare repository is created. If the given // storer is not empty ErrRepositoryAlreadyExists is returned -func Clone(s Storer, worktree billy.Filesystem, o *CloneOptions) (*Repository, error) { +func Clone(s storage.Storer, worktree billy.Filesystem, o *CloneOptions) (*Repository, error) { r, err := Init(s, worktree) if err != nil { return nil, err @@ -159,7 +160,7 @@ func PlainClone(path string, isBare bool, o *CloneOptions) (*Repository, error) return r, r.clone(o) } -func newRepository(s Storer, worktree billy.Filesystem) *Repository { +func newRepository(s storage.Storer, worktree billy.Filesystem) *Repository { return &Repository{ Storer: s, wt: worktree, @@ -247,12 +248,6 @@ func (r *Repository) clone(o *CloneOptions) error { return err } - // marks the repository as bare in the config, until we have Worktree, all - // the repository are bare - if err := r.setIsBare(true); err != nil { - return err - } - c := &config.RemoteConfig{ Name: o.RemoteName, URL: o.URL, @@ -270,11 +265,13 @@ func (r *Repository) clone(o *CloneOptions) error { Progress: o.Progress, }) if err != nil { + return err } head, err := storer.ResolveReference(remoteRefs, o.ReferenceName) if err != nil { + return err } @@ -283,12 +280,33 @@ func (r *Repository) clone(o *CloneOptions) error { } if err := r.updateWorktree(); err != nil { + fmt.Println("q", err) return err } + if o.RecursiveSubmodules && r.wt != nil { + if err := r.initSubmodules(); err != nil { + return err + } + } + return r.updateRemoteConfig(remote, o, c, head) } +func (r *Repository) initSubmodules() error { + w, err := r.Worktree() + if err != nil { + return err + } + + s, err := w.Submodules() + if err != nil { + return err + } + + return s.Init() +} + func (r *Repository) cloneRefSpec(o *CloneOptions, c *config.RemoteConfig) []config.RefSpec { diff --git a/repository_test.go b/repository_test.go index 1b5b345..9fd48fc 100644 --- a/repository_test.go +++ b/repository_test.go @@ -516,26 +516,21 @@ func (s *RepositorySuite) TestPullProgress(c *C) { } func (s *RepositorySuite) TestPullAdd(c *C) { - path := fixtures.Basic().One().Worktree().Base() + path := fixtures.Basic().ByTag("worktree").One().Worktree().Base() - r, _ := Init(memory.NewStorage(), nil) - err := r.clone(&CloneOptions{ + r, err := Clone(memory.NewStorage(), nil, &CloneOptions{ URL: fmt.Sprintf("file://%s", filepath.Join(path, ".git")), }) c.Assert(err, IsNil) storage := r.Storer.(*memory.Storage) - c.Assert(storage.Objects, HasLen, 31) + c.Assert(storage.Objects, HasLen, 28) branch, err := r.Reference("refs/heads/master", false) c.Assert(err, IsNil) c.Assert(branch.Hash().String(), Equals, "6ecf0ef2c2dffb796033e5a02219af86ec6584e5") - branch, err = r.Reference("refs/remotes/origin/branch", false) - c.Assert(err, IsNil) - c.Assert(branch.Hash().String(), Equals, "e8d3ffab552895c19b9fcf7aa264d277cde33881") - ExecuteOnPath(c, path, "touch foo", "git add foo", @@ -546,16 +541,11 @@ func (s *RepositorySuite) TestPullAdd(c *C) { c.Assert(err, IsNil) // the commit command has introduced a new commit, tree and blob - c.Assert(storage.Objects, HasLen, 34) + c.Assert(storage.Objects, HasLen, 31) branch, err = r.Reference("refs/heads/master", false) c.Assert(err, IsNil) c.Assert(branch.Hash().String(), Not(Equals), "6ecf0ef2c2dffb796033e5a02219af86ec6584e5") - - // the commit command, was in the local branch, so the remote should be read ok - branch, err = r.Reference("refs/remotes/origin/branch", false) - c.Assert(err, IsNil) - c.Assert(branch.Hash().String(), Equals, "e8d3ffab552895c19b9fcf7aa264d277cde33881") } func (s *RepositorySuite) TestPushToEmptyRepository(c *C) { diff --git a/submodule.go b/submodule.go new file mode 100644 index 0000000..32f730b --- /dev/null +++ b/submodule.go @@ -0,0 +1,30 @@ +package git + +import "srcd.works/go-git.v4/plumbing" + +type Submodule struct { + Name string + Branch string + URL string + + r *Repository +} + +func (s *Submodule) Init() error { + return s.r.clone(&CloneOptions{ + URL: s.URL, + ReferenceName: plumbing.ReferenceName(s.Branch), + }) +} + +type Submodules []*Submodule + +func (s Submodules) Init() error { + for _, sub := range s { + if err := sub.Init(); err != nil { + return err + } + } + + return nil +} diff --git a/worktree.go b/worktree.go index 58e008e..8aee0dc 100644 --- a/worktree.go +++ b/worktree.go @@ -4,8 +4,10 @@ import ( "errors" "fmt" "io" + "io/ioutil" "os" + "srcd.works/go-git.v4/config" "srcd.works/go-git.v4/plumbing" "srcd.works/go-git.v4/plumbing/format/index" "srcd.works/go-git.v4/plumbing/object" @@ -104,6 +106,7 @@ func (w *Worktree) Status() (Status, error) { files, err := readDirAll(w.fs) if err != nil { + fmt.Println("ch", err) return nil, err } @@ -167,6 +170,61 @@ func (w *Worktree) getMode(fi billy.FileInfo) os.FileMode { return object.FileMode } +const gitmodulesFile = ".gitmodules" + +func (w *Worktree) Submodules() (Submodules, error) { + l := make(Submodules, 0) + m, err := w.readGitmodulesFile() + if err != nil || m == nil { + return l, err + } + + for _, c := range m.Submodules { + s, err := w.newSubmodule(c) + if err != nil { + return nil, err + } + + l = append(l, s) + } + + return l, nil +} + +func (w *Worktree) newSubmodule(m *config.Submodule) (*Submodule, error) { + s, err := w.r.Storer.Module(m.Name) + if err != nil { + return nil, err + } + + return &Submodule{ + Name: m.Name, + URL: m.URL, + + r: newRepository(s, w.fs.Dir(m.Path)), + }, nil +} + +func (w *Worktree) readGitmodulesFile() (*config.Modules, error) { + f, err := w.fs.Open(gitmodulesFile) + if err != nil { + if os.IsNotExist(err) { + return nil, nil + } + + return nil, err + } + + input, err := ioutil.ReadAll(f) + if err != nil { + return nil, err + } + + m := config.NewModules() + return m, m.Unmarshal(input) + +} + // Status current status of a Worktree type Status map[string]*FileStatus @@ -287,6 +345,10 @@ func doReadDirAll(fs billy.Filesystem, path string, files map[string]billy.FileI l, err := fs.ReadDir(path) if err != nil { + if os.IsNotExist(err) { + return nil + } + return err } |