aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMáximo Cuadros <mcuadros@gmail.com>2017-02-14 00:25:09 +0100
committerMáximo Cuadros <mcuadros@gmail.com>2017-02-14 00:25:09 +0100
commit7e990a811d9e23b5a3573c405b70f06a1be9e7b6 (patch)
tree5cf329829edb41a562319f6242c13e90e350d365
parent65351f835dcaa4b50dd44bce7bf3f2e31582dadc (diff)
downloadgo-git-7e990a811d9e23b5a3573c405b70f06a1be9e7b6.tar.gz
submodule init and update implementation
-rw-r--r--common.go2
-rw-r--r--submodule.go68
-rw-r--r--submodule_test.go114
-rw-r--r--worktree.go79
-rw-r--r--worktree_test.go32
5 files changed, 263 insertions, 32 deletions
diff --git a/common.go b/common.go
index 6174339..f837a26 100644
--- a/common.go
+++ b/common.go
@@ -2,6 +2,8 @@ package git
import "strings"
+const defaultDotGitPath = ".git"
+
// countLines returns the number of lines in a string à la git, this is
// The newline character is assumed to be '\n'. The empty string
// contains 0 lines. If the last line of the string doesn't end with a
diff --git a/submodule.go b/submodule.go
index ea099f0..83c28b7 100644
--- a/submodule.go
+++ b/submodule.go
@@ -1,33 +1,79 @@
package git
import (
- "fmt"
-
+ "srcd.works/go-git.v4/config"
"srcd.works/go-git.v4/plumbing"
)
+// Submodule a submodule allows you to keep another Git repository in a
+// subdirectory of your repository.
type Submodule struct {
- Name string
- Branch string
- URL string
-
+ m *config.Submodule
+ w *Worktree
+ // r is the submodule repository
r *Repository
}
+// Config returns the submodule config
+func (s *Submodule) Config() *config.Submodule {
+ return s.m
+}
+
+// Init initialize the submodule reading the recoreded Entry in the index for
+// the given submodule
func (s *Submodule) Init() error {
- return s.r.clone(&CloneOptions{
- URL: s.URL,
- ReferenceName: plumbing.ReferenceName(s.Branch),
+ e, err := s.w.readIndexEntry(s.m.Path)
+ if err != nil {
+ return err
+ }
+
+ _, err = s.r.CreateRemote(&config.RemoteConfig{
+ Name: DefaultRemoteName,
+ URL: s.m.URL,
})
+
+ if err != nil {
+ return err
+ }
+
+ return s.fetchAndCheckout(e.Hash)
+}
+
+// Update the registered submodule to match what the superproject expects
+func (s *Submodule) Update() error {
+ e, err := s.w.readIndexEntry(s.m.Path)
+ if err != nil {
+ return err
+ }
+
+ return s.fetchAndCheckout(e.Hash)
+}
+
+func (s *Submodule) fetchAndCheckout(hash plumbing.Hash) error {
+ if err := s.r.Fetch(&FetchOptions{}); err != nil && err != NoErrAlreadyUpToDate {
+ return err
+ }
+
+ w, err := s.r.Worktree()
+ if err != nil {
+ return err
+ }
+
+ if err := w.Checkout(hash); err != nil {
+ return err
+ }
+
+ head := plumbing.NewHashReference(plumbing.HEAD, hash)
+ return s.r.Storer.SetReference(head)
}
+// Submodules list of several submodules from the same repository
type Submodules []*Submodule
+// Init initialize the submodule recorded in the index
func (s Submodules) Init() error {
for _, sub := range s {
- fmt.Println("clone", sub.URL)
if err := sub.Init(); err != nil {
- fmt.Println(err)
return err
}
}
diff --git a/submodule_test.go b/submodule_test.go
new file mode 100644
index 0000000..ed49927
--- /dev/null
+++ b/submodule_test.go
@@ -0,0 +1,114 @@
+package git
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+
+ "github.com/src-d/go-git-fixtures"
+
+ . "gopkg.in/check.v1"
+ "srcd.works/go-git.v4/plumbing"
+)
+
+type SubmoduleSuite struct {
+ BaseSuite
+ Worktree *Worktree
+ path string
+}
+
+var _ = Suite(&SubmoduleSuite{})
+
+func (s *SubmoduleSuite) SetUpTest(c *C) {
+ path := fixtures.ByTag("submodule").One().Worktree().Base()
+
+ dir, err := ioutil.TempDir("", "submodule")
+ c.Assert(err, IsNil)
+
+ r, err := PlainClone(dir, false, &CloneOptions{
+ URL: fmt.Sprintf("file://%s", filepath.Join(path)),
+ })
+
+ c.Assert(err, IsNil)
+
+ s.Repository = r
+ s.Worktree, err = r.Worktree()
+ c.Assert(err, IsNil)
+
+ s.path = path
+}
+
+func (s *SubmoduleSuite) TearDownTest(c *C) {
+ err := os.RemoveAll(s.path)
+ c.Assert(err, IsNil)
+}
+
+func (s *SubmoduleSuite) TestInit(c *C) {
+ sm, err := s.Worktree.Submodule("basic")
+ c.Assert(err, IsNil)
+
+ _, err = sm.r.Reference(plumbing.HEAD, true)
+ c.Assert(err, Equals, plumbing.ErrReferenceNotFound)
+
+ err = sm.Init()
+ c.Assert(err, IsNil)
+
+ ref, err := sm.r.Reference(plumbing.HEAD, true)
+ c.Assert(err, IsNil)
+ c.Assert(ref.Hash().String(), Equals, "6ecf0ef2c2dffb796033e5a02219af86ec6584e5")
+
+ w, err := sm.r.Worktree()
+ c.Assert(err, IsNil)
+
+ status, err := w.Status()
+ c.Assert(err, IsNil)
+ c.Assert(status.IsClean(), Equals, true)
+}
+
+func (s *SubmoduleSuite) TestUpdate(c *C) {
+ sm, err := s.Worktree.Submodule("basic")
+ c.Assert(err, IsNil)
+
+ _, err = sm.r.Reference(plumbing.HEAD, true)
+ c.Assert(err, Equals, plumbing.ErrReferenceNotFound)
+
+ err = sm.Init()
+ c.Assert(err, IsNil)
+
+ idx, err := s.Repository.Storer.Index()
+ c.Assert(err, IsNil)
+
+ for i, e := range idx.Entries {
+ if e.Name == "basic" {
+ e.Hash = plumbing.NewHash("b029517f6300c2da0f4b651b8642506cd6aaf45d")
+ }
+
+ idx.Entries[i] = e
+ }
+
+ err = s.Repository.Storer.SetIndex(idx)
+ c.Assert(err, IsNil)
+
+ err = sm.Update()
+ c.Assert(err, IsNil)
+
+ ref, err := sm.r.Reference(plumbing.HEAD, true)
+ c.Assert(err, IsNil)
+ c.Assert(ref.Hash().String(), Equals, "b029517f6300c2da0f4b651b8642506cd6aaf45d")
+
+}
+
+func (s *SubmoduleSuite) TestSubmodulesInit(c *C) {
+ sm, err := s.Worktree.Submodules()
+ c.Assert(err, IsNil)
+
+ err = sm.Init()
+ c.Assert(err, IsNil)
+
+ for _, m := range sm {
+ ref, err := m.r.Reference(plumbing.HEAD, true)
+ c.Assert(err, IsNil)
+ c.Assert(ref.Hash(), Not(Equals), plumbing.ZeroHash)
+ }
+}
diff --git a/worktree.go b/worktree.go
index 473f933..2a5b58a 100644
--- a/worktree.go
+++ b/worktree.go
@@ -16,6 +16,7 @@ import (
)
var ErrWorktreeNotClean = errors.New("worktree is not clean")
+var ErrSubmoduleNotFound = errors.New("submodule not found")
type Worktree struct {
r *Repository
@@ -46,7 +47,7 @@ func (w *Worktree) Checkout(commit plumbing.Hash) error {
walker := object.NewTreeWalker(t, true)
for {
- _, entry, err := walker.Next()
+ name, entry, err := walker.Next()
if err == io.EOF {
break
}
@@ -55,7 +56,7 @@ func (w *Worktree) Checkout(commit plumbing.Hash) error {
return err
}
- if err := w.checkoutEntry(&entry, idx); err != nil {
+ if err := w.checkoutEntry(name, &entry, idx); err != nil {
return err
}
}
@@ -63,19 +64,19 @@ func (w *Worktree) Checkout(commit plumbing.Hash) error {
return w.r.Storer.SetIndex(idx)
}
-func (w *Worktree) checkoutEntry(e *object.TreeEntry, idx *index.Index) error {
+func (w *Worktree) checkoutEntry(name string, e *object.TreeEntry, idx *index.Index) error {
if e.Mode == object.SubmoduleMode {
- return w.indexEntry(e, idx)
+ return w.addIndexFromTreeEntry(name, e, idx)
}
if e.Mode.IsDir() {
return nil
}
- return w.checkoutFile(e, idx)
+ return w.checkoutFile(name, e, idx)
}
-func (w *Worktree) checkoutFile(e *object.TreeEntry, idx *index.Index) error {
+func (w *Worktree) checkoutFile(name string, e *object.TreeEntry, idx *index.Index) error {
blob, err := object.GetBlob(w.r.Storer, e.Hash)
if err != nil {
return err
@@ -87,7 +88,7 @@ func (w *Worktree) checkoutFile(e *object.TreeEntry, idx *index.Index) error {
}
defer from.Close()
- to, err := w.fs.OpenFile(e.Name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, e.Mode.Perm())
+ to, err := w.fs.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, e.Mode.Perm())
if err != nil {
return err
}
@@ -97,30 +98,30 @@ func (w *Worktree) checkoutFile(e *object.TreeEntry, idx *index.Index) error {
}
defer to.Close()
- return w.indexFile(e, idx)
+ return w.addIndexFromFile(name, e, idx)
}
var fillSystemInfo func(e *index.Entry, sys interface{})
-func (w *Worktree) indexEntry(f *object.TreeEntry, idx *index.Index) error {
+func (w *Worktree) addIndexFromTreeEntry(name string, f *object.TreeEntry, idx *index.Index) error {
idx.Entries = append(idx.Entries, index.Entry{
Hash: f.Hash,
- Name: f.Name,
+ Name: name,
Mode: object.SubmoduleMode,
})
return nil
}
-func (w *Worktree) indexFile(f *object.TreeEntry, idx *index.Index) error {
- fi, err := w.fs.Stat(f.Name)
+func (w *Worktree) addIndexFromFile(name string, f *object.TreeEntry, idx *index.Index) error {
+ fi, err := w.fs.Stat(name)
if err != nil {
return err
}
e := index.Entry{
Hash: f.Hash,
- Name: f.Name,
+ Name: name,
Mode: w.getMode(fi),
ModifiedAt: fi.ModTime(),
Size: uint32(fi.Size()),
@@ -144,7 +145,6 @@ func (w *Worktree) Status() (Status, error) {
files, err := readDirAll(w.fs)
if err != nil {
- fmt.Println("ch", err)
return nil, err
}
@@ -210,6 +210,22 @@ func (w *Worktree) getMode(fi billy.FileInfo) os.FileMode {
const gitmodulesFile = ".gitmodules"
+// Submodule returns the submodule with the given name
+func (w *Worktree) Submodule(name string) (*Submodule, error) {
+ m, err := w.readGitmodulesFile()
+ if err != nil || m == nil {
+ return nil, err
+ }
+
+ c, ok := m.Submodules[name]
+ if !ok {
+ return nil, ErrSubmoduleNotFound
+ }
+
+ return w.newSubmodule(c)
+}
+
+// Submodules returns all the available submodules
func (w *Worktree) Submodules() (Submodules, error) {
l := make(Submodules, 0)
m, err := w.readGitmodulesFile()
@@ -235,11 +251,15 @@ func (w *Worktree) newSubmodule(m *config.Submodule) (*Submodule, error) {
return nil, err
}
- return &Submodule{
- Name: m.Name,
- URL: m.URL,
+ r, err := Init(s, w.fs.Dir(m.Path))
+ if err != nil {
+ return nil, err
+ }
- r: newRepository(s, w.fs.Dir(m.Path)),
+ return &Submodule{
+ m: m,
+ w: w,
+ r: r,
}, nil
}
@@ -263,6 +283,23 @@ func (w *Worktree) readGitmodulesFile() (*config.Modules, error) {
}
+func (w *Worktree) readIndexEntry(path string) (index.Entry, error) {
+ var e index.Entry
+
+ idx, err := w.r.Storer.Index()
+ if err != nil {
+ return e, err
+ }
+
+ for _, e := range idx.Entries {
+ if e.Name == path {
+ return e, nil
+ }
+ }
+
+ return e, fmt.Errorf("unable to find %q entry in the index", path)
+}
+
// Status current status of a Worktree
type Status map[string]*FileStatus
@@ -377,7 +414,7 @@ func readDirAll(filesystem billy.Filesystem) (map[string]billy.FileInfo, error)
}
func doReadDirAll(fs billy.Filesystem, path string, files map[string]billy.FileInfo) error {
- if path == ".git" {
+ if path == defaultDotGitPath {
return nil
}
@@ -392,6 +429,10 @@ func doReadDirAll(fs billy.Filesystem, path string, files map[string]billy.FileI
for _, info := range l {
file := fs.Join(path, info.Name())
+ if file == defaultDotGitPath {
+ continue
+ }
+
if !info.IsDir() {
files[file] = info
continue
diff --git a/worktree_test.go b/worktree_test.go
index 306b56d..81d35b1 100644
--- a/worktree_test.go
+++ b/worktree_test.go
@@ -7,6 +7,7 @@ import (
"srcd.works/go-git.v4/plumbing/format/index"
"srcd.works/go-git.v4/plumbing/object"
+ "github.com/src-d/go-git-fixtures"
. "gopkg.in/check.v1"
"srcd.works/go-billy.v1/memfs"
"srcd.works/go-billy.v1/osfs"
@@ -40,7 +41,7 @@ func (s *WorktreeSuite) TestCheckout(c *C) {
entries, err := fs.ReadDir("/")
c.Assert(err, IsNil)
- c.Assert(entries, HasLen, 9)
+ c.Assert(entries, HasLen, 8)
ch, err := fs.Open("CHANGELOG")
c.Assert(err, IsNil)
@@ -116,7 +117,6 @@ func (s *WorktreeSuite) TestCheckoutIndexOS(c *C) {
}
func (s *WorktreeSuite) TestStatus(c *C) {
-
h, err := s.Repository.Head()
c.Assert(err, IsNil)
@@ -164,3 +164,31 @@ func (s *WorktreeSuite) TestStatusModified(c *C) {
c.Assert(err, IsNil)
c.Assert(status.IsClean(), Equals, false)
}
+
+func (s *WorktreeSuite) TestSubmodule(c *C) {
+ path := fixtures.ByTag("submodule").One().Worktree().Base()
+ r, err := PlainOpen(path)
+ c.Assert(err, IsNil)
+
+ w, err := r.Worktree()
+ c.Assert(err, IsNil)
+
+ m, err := w.Submodule("basic")
+ c.Assert(err, IsNil)
+
+ c.Assert(m.Config().Name, Equals, "basic")
+}
+
+func (s *WorktreeSuite) TestSubmodules(c *C) {
+ path := fixtures.ByTag("submodule").One().Worktree().Base()
+ r, err := PlainOpen(path)
+ c.Assert(err, IsNil)
+
+ w, err := r.Worktree()
+ c.Assert(err, IsNil)
+
+ l, err := w.Submodules()
+ c.Assert(err, IsNil)
+
+ c.Assert(l, HasLen, 2)
+}