diff options
author | Máximo Cuadros <mcuadros@gmail.com> | 2017-02-14 00:25:09 +0100 |
---|---|---|
committer | Máximo Cuadros <mcuadros@gmail.com> | 2017-02-14 00:25:09 +0100 |
commit | 7e990a811d9e23b5a3573c405b70f06a1be9e7b6 (patch) | |
tree | 5cf329829edb41a562319f6242c13e90e350d365 | |
parent | 65351f835dcaa4b50dd44bce7bf3f2e31582dadc (diff) | |
download | go-git-7e990a811d9e23b5a3573c405b70f06a1be9e7b6.tar.gz |
submodule init and update implementation
-rw-r--r-- | common.go | 2 | ||||
-rw-r--r-- | submodule.go | 68 | ||||
-rw-r--r-- | submodule_test.go | 114 | ||||
-rw-r--r-- | worktree.go | 79 | ||||
-rw-r--r-- | worktree_test.go | 32 |
5 files changed, 263 insertions, 32 deletions
@@ -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) +} |