Skip to content
This repository was archived by the owner on Sep 11, 2020. It is now read-only.

Submodules init and update #270

Merged
merged 12 commits into from
Feb 21, 2017
7 changes: 3 additions & 4 deletions _examples/clone/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,11 @@ func main() {
directory := os.Args[2]

// Clone the given repository to the given directory
Info("git clone %s %s", url, directory)
Info("git clone %s %s --recursive", url, directory)

r, err := git.PlainClone(directory, false, &git.CloneOptions{
URL: url,
RecursiveSubmodules: true,
Depth: 1,
URL: url,
RecurseSubmodules: git.DefaultRecursivity,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would change the name from DefaultRecursivity to DefaultRecurseModules or DefaultSubmoduleRecursivity or something like that, so that it does not get confused with other parameters related with recursivite of non-submodule stuff (e.g. reference resolution).

})

CheckIfError(err)
Expand Down
29 changes: 27 additions & 2 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,18 @@ import (
"srcd.works/go-git.v4/plumbing/transport"
)

// SubmoduleRescursivity defines how depth will affect any submodule recursive
// operation
type SubmoduleRescursivity int
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alternative name: SubmoduleRecursionDepth, explain what negative values will do and how to make it infinite.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just change the time to uint, infinity by itself is not allowed, a number should be given always, even if is very high


const (
// DefaultRemoteName name of the default Remote, just like git command
DefaultRemoteName = "origin"

// NoRecursivity disables the recursion for a submodule operation
NoRecursivity SubmoduleRescursivity = 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alternative: NonRecursive.

// DefaultRecursivity allow recursion in a submodule operation
DefaultRecursivity SubmoduleRescursivity = 10
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alternative: DefaultSubmoduleRecursionDepth.

)

var (
Expand All @@ -32,10 +41,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
// RecurseSubmodules 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

end the paragraph with a full stop.

RecursiveSubmodules bool
RecurseSubmodules SubmoduleRescursivity
// 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
Expand Down Expand Up @@ -71,6 +80,9 @@ type PullOptions struct {
Depth int
// Auth credentials, if required, to use with the remote repository
Auth transport.AuthMethod
// RecurseSubmodules controls if new commits of all populated submodules
// should be fetched too
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

End the sentence with a full stop.

RecurseSubmodules SubmoduleRescursivity
// 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
Expand Down Expand Up @@ -152,3 +164,16 @@ func (o *PushOptions) Validate() error {

return nil
}

// SubmoduleUpdateOptions describes how a submodule update should be performed
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add full stop.

type SubmoduleUpdateOptions struct {
// Init initializes the submodules recorded in the index
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add full stop.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs much better explanation. It is a bool, but does not make sense with the comment.

Init bool
// NoFetch tell to the update command to don’t fetch new objects from the
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to don’t -> to not

// remote site.
NoFetch bool
// RecurseSubmodules the update is performed not only in the submodules of
// the current repository but also in any nested submodules inside those
// submodules (and so on). Until the SubmoduleRescursivity is reached.
RecurseSubmodules SubmoduleRescursivity
}
23 changes: 18 additions & 5 deletions repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,16 +350,16 @@ func (r *Repository) clone(o *CloneOptions) error {
return err
}

if o.RecursiveSubmodules && r.wt != nil {
if err := r.initSubmodules(); err != nil {
if o.RecurseSubmodules != NoRecursivity && r.wt != nil {
if err := r.updateSubmodules(o.RecurseSubmodules); err != nil {
return err
}
}

return r.updateRemoteConfig(remote, o, c, head)
}

func (r *Repository) initSubmodules() error {
func (r *Repository) updateSubmodules(recursion SubmoduleRescursivity) error {
w, err := r.Worktree()
if err != nil {
return err
Expand All @@ -370,7 +370,10 @@ func (r *Repository) initSubmodules() error {
return err
}

return s.Init()
return s.Update(&SubmoduleUpdateOptions{
Init: true,
RecurseSubmodules: recursion,
})
}

func (r *Repository) cloneRefSpec(o *CloneOptions,
Expand Down Expand Up @@ -546,7 +549,17 @@ func (r *Repository) Pull(o *PullOptions) error {
return NoErrAlreadyUpToDate
}

return r.updateWorktree()
if err := r.updateWorktree(); err != nil {
return err
}

if o.RecurseSubmodules != NoRecursivity && r.wt != nil {
if err := r.updateSubmodules(o.RecurseSubmodules); err != nil {
return err
}
}

return nil
}

func (r *Repository) updateWorktree() error {
Expand Down
41 changes: 41 additions & 0 deletions repository_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,25 @@ func (s *RepositorySuite) TestPlainClone(c *C) {
c.Assert(remotes, HasLen, 1)
}

func (s *RepositorySuite) TestPlainCloneWithRecurseSubmodules(c *C) {
dir, err := ioutil.TempDir("", "plain-clone-submodule")
c.Assert(err, IsNil)
defer os.RemoveAll(dir)

path := fixtures.ByTag("submodule").One().Worktree().Base()

r, err := PlainClone(dir, false, &CloneOptions{
URL: fmt.Sprintf("file://%s", path),
RecurseSubmodules: DefaultRecursivity,
})

c.Assert(err, IsNil)

cfg, err := r.Config()
c.Assert(cfg.Remotes, HasLen, 1)
c.Assert(cfg.Submodules, HasLen, 2)
}

func (s *RepositorySuite) TestFetch(c *C) {
r, _ := Init(memory.NewStorage(), nil)
_, err := r.CreateRemote(&config.RemoteConfig{
Expand Down Expand Up @@ -562,6 +581,28 @@ func (s *RepositorySuite) TestPullProgress(c *C) {
c.Assert(buf.Len(), Not(Equals), 0)
}

func (s *RepositorySuite) TestPullProgressWithRecursion(c *C) {
path := fixtures.ByTag("submodule").One().Worktree().Base()

dir, err := ioutil.TempDir("", "plain-clone-submodule")
c.Assert(err, IsNil)
defer os.RemoveAll(dir)

r, _ := PlainInit(dir, false)
r.CreateRemote(&config.RemoteConfig{
Name: DefaultRemoteName,
URL: fmt.Sprintf("file://%s", path),
})

err = r.Pull(&PullOptions{
RecurseSubmodules: DefaultRecursivity,
})
c.Assert(err, IsNil)

cfg, err := r.Config()
c.Assert(cfg.Submodules, HasLen, 2)
}

func (s *RepositorySuite) TestPullAdd(c *C) {
path := fixtures.Basic().ByTag("worktree").One().Worktree().Base()

Expand Down
128 changes: 110 additions & 18 deletions submodule.go
Original file line number Diff line number Diff line change
@@ -1,60 +1,141 @@
package git

import (
"errors"

"srcd.works/go-git.v4/config"
"srcd.works/go-git.v4/plumbing"
)

var (
ErrSubmoduleAlreadyInitialized = errors.New("submodule already initialized")
ErrSubmoduleNotInitialized = errors.New("submodule not initialized")
)

// Submodule a submodule allows you to keep another Git repository in a
// subdirectory of your repository.
type Submodule struct {
m *config.Submodule
initialized bool

c *config.Submodule
w *Worktree
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change this to WorkTree.

// r is the submodule repository
r *Repository
}

// Config returns the submodule config
func (s *Submodule) Config() *config.Submodule {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not make m public instead of a getter?

return s.m
return s.c
}

// Init initialize the submodule reading the recoreded Entry in the index for
// the given submodule
func (s *Submodule) Init() error {
e, err := s.w.readIndexEntry(s.m.Path)
cfg, err := s.w.r.Storer.Config()
if err != nil {
return err
}

_, err = s.r.CreateRemote(&config.RemoteConfig{
_, ok := cfg.Submodules[s.c.Name]
if ok {
return ErrSubmoduleAlreadyInitialized
}

s.initialized = true

cfg.Submodules[s.c.Name] = s.c
return s.w.r.Storer.SetConfig(cfg)
}

// Repository returns the Repository represented by this submodule
func (s *Submodule) Repository() (*Repository, error) {
storer, err := s.w.r.Storer.Module(s.c.Name)
if err != nil {
return nil, err
}

_, err = storer.Reference(plumbing.HEAD)
if err != nil && err != plumbing.ErrReferenceNotFound {
return nil, err
}

worktree := s.w.fs.Dir(s.c.Path)
if err == nil {
return Open(storer, worktree)
}

r, err := Init(storer, worktree)
if err != nil {
return nil, err
}

_, err = r.CreateRemote(&config.RemoteConfig{
Name: DefaultRemoteName,
URL: s.m.URL,
URL: s.c.URL,
})

return r, err
}

// Update the registered submodule to match what the superproject expects, the
// submodule should be initilized first calling the Init method or setting in
// the options SubmoduleUpdateOptions.Init equals true
func (s *Submodule) Update(o *SubmoduleUpdateOptions) error {
if !s.initialized && !o.Init {
return ErrSubmoduleNotInitialized
}

if !s.initialized && o.Init {
if err := s.Init(); err != nil {
return err
}
}

e, err := s.w.readIndexEntry(s.c.Path)
if err != nil {
return err
}

return s.fetchAndCheckout(e.Hash)
r, err := s.Repository()
if err != nil {
return err
}

if err := s.fetchAndCheckout(r, o, e.Hash); err != nil {
return err
}

return s.doRecrusiveUpdate(r, o)
}

// Update the registered submodule to match what the superproject expects
func (s *Submodule) Update() error {
e, err := s.w.readIndexEntry(s.m.Path)
func (s *Submodule) doRecrusiveUpdate(r *Repository, o *SubmoduleUpdateOptions) error {
if o.RecurseSubmodules == NoRecursivity {
return nil
}

w, err := r.Worktree()
if err != nil {
return err
}

return s.fetchAndCheckout(e.Hash)
l, err := w.Submodules()
if err != nil {
return err
}

new := &SubmoduleUpdateOptions{}
*new = *o
new.RecurseSubmodules--
return l.Update(new)
}

func (s *Submodule) fetchAndCheckout(hash plumbing.Hash) error {
if err := s.r.Fetch(&FetchOptions{}); err != nil && err != NoErrAlreadyUpToDate {
return err
func (s *Submodule) fetchAndCheckout(r *Repository, o *SubmoduleUpdateOptions, hash plumbing.Hash) error {
if !o.NoFetch {
err := r.Fetch(&FetchOptions{})
if err != nil && err != NoErrAlreadyUpToDate {
return err
}
}

w, err := s.r.Worktree()
w, err := r.Worktree()
if err != nil {
return err
}
Expand All @@ -64,13 +145,13 @@ func (s *Submodule) fetchAndCheckout(hash plumbing.Hash) error {
}

head := plumbing.NewHashReference(plumbing.HEAD, hash)
return s.r.Storer.SetReference(head)
return r.Storer.SetReference(head)
}

// Submodules list of several submodules from the same repository
type Submodules []*Submodule

// Init initialize the submodule recorded in the index
// Init initializes the submodules in this list
func (s Submodules) Init() error {
for _, sub := range s {
if err := sub.Init(); err != nil {
Expand All @@ -80,3 +161,14 @@ func (s Submodules) Init() error {

return nil
}

// 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 {
return err
}
}

return nil
}
Loading