diff options
author | Máximo Cuadros <mcuadros@gmail.com> | 2017-04-15 23:13:45 +0200 |
---|---|---|
committer | Máximo Cuadros <mcuadros@gmail.com> | 2017-04-15 23:13:45 +0200 |
commit | 4b0fc1eb6937b6e5f8569794e8b669443e2c7584 (patch) | |
tree | 9f2fdb086be2b96ff9c2169fdb93e19e5a298490 /submodule.go | |
parent | 601c85a8834cd78e317f1ba435475fa18162053a (diff) | |
download | go-git-4b0fc1eb6937b6e5f8569794e8b669443e2c7584.tar.gz |
worktree: reset and checkout support for submodules
Diffstat (limited to 'submodule.go')
-rw-r--r-- | submodule.go | 161 |
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) +} |