aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--_examples/clone/main.go5
-rw-r--r--common.go19
-rw-r--r--doc.go2
-rw-r--r--options.go4
-rw-r--r--remote.go9
-rw-r--r--remote_test.go3
-rw-r--r--repository.go40
-rw-r--r--repository_test.go18
-rw-r--r--submodule.go30
-rw-r--r--worktree.go62
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)
diff --git a/common.go b/common.go
index 1a05dc3..6174339 100644
--- a/common.go
+++ b/common.go
@@ -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
diff --git a/doc.go b/doc.go
index 3d817fe..7f57cbd 100644
--- a/doc.go
+++ b/doc.go
@@ -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"
diff --git a/options.go b/options.go
index 120c472..1f83c36 100644
--- a/options.go
+++ b/options.go
@@ -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
diff --git a/remote.go b/remote.go
index 2d243e9..a51946b 100644
--- a/remote.go
+++ b/remote.go
@@ -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
}