aboutsummaryrefslogtreecommitdiffstats
path: root/submodule.go
diff options
context:
space:
mode:
authorMáximo Cuadros <mcuadros@gmail.com>2017-04-17 17:36:28 +0200
committerGitHub <noreply@github.com>2017-04-17 17:36:28 +0200
commitc991d2d1c2253ac246441bc92bf97804a73c0bf2 (patch)
tree9a6965fb0023262ea17d02bcedd9f48204a80750 /submodule.go
parent057f1dd4fa44df509cb96977c0a8193407a22767 (diff)
parent4b0fc1eb6937b6e5f8569794e8b669443e2c7584 (diff)
downloadgo-git-c991d2d1c2253ac246441bc92bf97804a73c0bf2.tar.gz
Merge pull request #344 from mcuadros/submodules-checkout
worktree: reset and checkout support for submodules
Diffstat (limited to 'submodule.go')
-rw-r--r--submodule.go161
1 files changed, 154 insertions, 7 deletions
diff --git a/submodule.go b/submodule.go
index c711a2b..fd23b9d 100644
--- a/submodule.go
+++ b/submodule.go
@@ -1,10 +1,13 @@
package git
import (
+ "bytes"
"errors"
+ "fmt"
"gopkg.in/src-d/go-git.v4/config"
"gopkg.in/src-d/go-git.v4/plumbing"
+ "gopkg.in/src-d/go-git.v4/plumbing/format/index"
)
var (
@@ -15,6 +18,7 @@ var (
// Submodule a submodule allows you to keep another Git repository in a
// subdirectory of your repository.
type Submodule struct {
+ // initialized defines if a submodule was already initialized.
initialized bool
c *config.Submodule
@@ -26,7 +30,7 @@ func (s *Submodule) Config() *config.Submodule {
return s.c
}
-// Init initialize the submodule reading the recoreded Entry in the index for
+// Init initialize the submodule reading the recorded Entry in the index for
// the given submodule
func (s *Submodule) Init() error {
cfg, err := s.w.r.Storer.Config()
@@ -45,8 +49,54 @@ func (s *Submodule) Init() error {
return s.w.r.Storer.SetConfig(cfg)
}
+// Status returns the status of the submodule.
+func (s *Submodule) Status() (*SubmoduleStatus, error) {
+ idx, err := s.w.r.Storer.Index()
+ if err != nil {
+ return nil, err
+ }
+
+ return s.status(idx)
+}
+
+func (s *Submodule) status(idx *index.Index) (*SubmoduleStatus, error) {
+ e, err := idx.Entry(s.c.Path)
+ if err != nil {
+ return nil, err
+ }
+
+ status := &SubmoduleStatus{
+ Path: s.c.Path,
+ Expected: e.Hash,
+ }
+
+ if !s.initialized {
+ return status, nil
+ }
+
+ r, err := s.Repository()
+ if err != nil {
+ return nil, err
+ }
+
+ head, err := r.Head()
+ if err == nil {
+ status.Current = head.Hash()
+ }
+
+ if err != nil && err == plumbing.ErrReferenceNotFound {
+ err = nil
+ }
+
+ return status, err
+}
+
// Repository returns the Repository represented by this submodule
func (s *Submodule) Repository() (*Repository, error) {
+ if !s.initialized {
+ return nil, ErrSubmoduleNotInitialized
+ }
+
storer, err := s.w.r.Storer.Module(s.c.Name)
if err != nil {
return nil, err
@@ -76,9 +126,13 @@ func (s *Submodule) Repository() (*Repository, error) {
}
// Update the registered submodule to match what the superproject expects, the
-// submodule should be initilized first calling the Init method or setting in
+// submodule should be initialized first calling the Init method or setting in
// the options SubmoduleUpdateOptions.Init equals true
func (s *Submodule) Update(o *SubmoduleUpdateOptions) error {
+ return s.update(o, plumbing.ZeroHash)
+}
+
+func (s *Submodule) update(o *SubmoduleUpdateOptions, forceHash plumbing.Hash) error {
if !s.initialized && !o.Init {
return ErrSubmoduleNotInitialized
}
@@ -89,17 +143,27 @@ func (s *Submodule) Update(o *SubmoduleUpdateOptions) error {
}
}
- e, err := s.w.readIndexEntry(s.c.Path)
+ idx, err := s.w.r.Storer.Index()
if err != nil {
return err
}
+ hash := forceHash
+ if hash.IsZero() {
+ e, err := idx.Entry(s.c.Path)
+ if err != nil {
+ return err
+ }
+
+ hash = e.Hash
+ }
+
r, err := s.Repository()
if err != nil {
return err
}
- if err := s.fetchAndCheckout(r, o, e.Hash); err != nil {
+ if err := s.fetchAndCheckout(r, o, hash); err != nil {
return err
}
@@ -123,6 +187,7 @@ func (s *Submodule) doRecursiveUpdate(r *Repository, o *SubmoduleUpdateOptions)
new := &SubmoduleUpdateOptions{}
*new = *o
+
new.RecurseSubmodules--
return l.Update(new)
}
@@ -148,10 +213,10 @@ func (s *Submodule) fetchAndCheckout(r *Repository, o *SubmoduleUpdateOptions, h
return r.Storer.SetReference(head)
}
-// Submodules list of several submodules from the same repository
+// Submodules list of several submodules from the same repository.
type Submodules []*Submodule
-// Init initializes the submodules in this list
+// Init initializes the submodules in this list.
func (s Submodules) Init() error {
for _, sub := range s {
if err := sub.Init(); err != nil {
@@ -162,7 +227,7 @@ func (s Submodules) Init() error {
return nil
}
-// Update updates all the submodules in this list
+// Update updates all the submodules in this list.
func (s Submodules) Update(o *SubmoduleUpdateOptions) error {
for _, sub := range s {
if err := sub.Update(o); err != nil {
@@ -172,3 +237,85 @@ func (s Submodules) Update(o *SubmoduleUpdateOptions) error {
return nil
}
+
+// Status returns the status of the submodules.
+func (s Submodules) Status() (SubmodulesStatus, error) {
+ var list SubmodulesStatus
+
+ var r *Repository
+ for _, sub := range s {
+ if r == nil {
+ r = sub.w.r
+ }
+
+ idx, err := r.Storer.Index()
+ if err != nil {
+ return nil, err
+ }
+
+ status, err := sub.status(idx)
+ if err != nil {
+ return nil, err
+ }
+
+ list = append(list, status)
+ }
+
+ return list, nil
+}
+
+// SubmodulesStatus contains the status for all submodiles in the worktree
+type SubmodulesStatus []*SubmoduleStatus
+
+// String is equivalent to `git submodule status`
+func (s SubmodulesStatus) String() string {
+ buf := bytes.NewBuffer(nil)
+ for _, sub := range s {
+ fmt.Fprintln(buf, sub)
+ }
+
+ return buf.String()
+}
+
+// SubmoduleStatus contains the status for a submodule in the worktree
+type SubmoduleStatus struct {
+ Path string
+ Current plumbing.Hash
+ Expected plumbing.Hash
+ Branch plumbing.ReferenceName
+}
+
+// IsClean is the HEAD of the submodule is equals to the expected commit
+func (s *SubmoduleStatus) IsClean() bool {
+ return s.Current == s.Expected
+}
+
+// String is equivalent to `git submodule status <submodule>`
+//
+// This will print the SHA-1 of the currently checked out commit for a
+// submodule, along with the submodule path and the output of git describe fo
+// the SHA-1. Each SHA-1 will be prefixed with - if the submodule is not
+// initialized, + if the currently checked out submodule commit does not match
+// the SHA-1 found in the index of the containing repository.
+func (s *SubmoduleStatus) String() string {
+ var extra string
+ var status = ' '
+
+ if s.Current.IsZero() {
+ status = '-'
+ } else if !s.IsClean() {
+ status = '+'
+ }
+
+ if len(s.Branch) != 0 {
+ extra = string(s.Branch[5:])
+ } else if !s.Current.IsZero() {
+ extra = s.Current.String()[:7]
+ }
+
+ if extra != "" {
+ extra = fmt.Sprintf(" (%s)", extra)
+ }
+
+ return fmt.Sprintf("%c%s %s%s", status, s.Expected, s.Path, extra)
+}