aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--plumbing/format/config/common.go54
-rw-r--r--plumbing/format/config/common_test.go8
-rw-r--r--plumbing/format/config/option.go10
-rw-r--r--plumbing/format/config/option_test.go15
-rw-r--r--plumbing/format/config/section.go85
-rw-r--r--plumbing/format/config/section_test.go253
-rw-r--r--remote.go15
-rw-r--r--remote_test.go2
-rw-r--r--submodule.go24
-rw-r--r--worktree.go6
-rw-r--r--worktree_test.go59
11 files changed, 471 insertions, 60 deletions
diff --git a/plumbing/format/config/common.go b/plumbing/format/config/common.go
index 8f98ad1..6d689ea 100644
--- a/plumbing/format/config/common.go
+++ b/plumbing/format/config/common.go
@@ -44,28 +44,14 @@ func (c *Config) Section(name string) *Section {
return s
}
-// AddOption adds an option to a given section and subsection. Use the
-// NoSubsection constant for the subsection argument if no subsection is wanted.
-func (c *Config) AddOption(section string, subsection string, key string, value string) *Config {
- if subsection == "" {
- c.Section(section).AddOption(key, value)
- } else {
- c.Section(section).Subsection(subsection).AddOption(key, value)
- }
-
- return c
-}
-
-// SetOption sets an option to a given section and subsection. Use the
-// NoSubsection constant for the subsection argument if no subsection is wanted.
-func (c *Config) SetOption(section string, subsection string, key string, value string) *Config {
- if subsection == "" {
- c.Section(section).SetOption(key, value)
- } else {
- c.Section(section).Subsection(subsection).SetOption(key, value)
+// HasSection checks if the Config has a section with the specified name.
+func (c *Config) HasSection(name string) bool {
+ for _, s := range c.Sections {
+ if s.IsName(name) {
+ return true
+ }
}
-
- return c
+ return false
}
// RemoveSection removes a section from a config file.
@@ -81,7 +67,7 @@ func (c *Config) RemoveSection(name string) *Config {
return c
}
-// RemoveSubsection remove s a subsection from a config file.
+// RemoveSubsection remove a subsection from a config file.
func (c *Config) RemoveSubsection(section string, subsection string) *Config {
for _, s := range c.Sections {
if s.IsName(section) {
@@ -97,3 +83,27 @@ func (c *Config) RemoveSubsection(section string, subsection string) *Config {
return c
}
+
+// AddOption adds an option to a given section and subsection. Use the
+// NoSubsection constant for the subsection argument if no subsection is wanted.
+func (c *Config) AddOption(section string, subsection string, key string, value string) *Config {
+ if subsection == "" {
+ c.Section(section).AddOption(key, value)
+ } else {
+ c.Section(section).Subsection(subsection).AddOption(key, value)
+ }
+
+ return c
+}
+
+// SetOption sets an option to a given section and subsection. Use the
+// NoSubsection constant for the subsection argument if no subsection is wanted.
+func (c *Config) SetOption(section string, subsection string, key string, value string) *Config {
+ if subsection == "" {
+ c.Section(section).SetOption(key, value)
+ } else {
+ c.Section(section).Subsection(subsection).SetOption(key, value)
+ }
+
+ return c
+}
diff --git a/plumbing/format/config/common_test.go b/plumbing/format/config/common_test.go
index 365b53f..dca38df 100644
--- a/plumbing/format/config/common_test.go
+++ b/plumbing/format/config/common_test.go
@@ -64,6 +64,14 @@ func (s *CommonSuite) TestConfig_AddOption(c *C) {
c.Assert(obtained, DeepEquals, expected)
}
+func (s *CommonSuite) TestConfig_HasSection(c *C) {
+ sect := New().
+ AddOption("section1", "sub1", "key1", "value1").
+ AddOption("section1", "sub2", "key1", "value1")
+ c.Assert(sect.HasSection("section1"), Equals, true)
+ c.Assert(sect.HasSection("section2"), Equals, false)
+}
+
func (s *CommonSuite) TestConfig_RemoveSection(c *C) {
sect := New().
AddOption("section1", NoSubsection, "key1", "value1").
diff --git a/plumbing/format/config/option.go b/plumbing/format/config/option.go
index 218f992..cad3948 100644
--- a/plumbing/format/config/option.go
+++ b/plumbing/format/config/option.go
@@ -54,6 +54,16 @@ func (opts Options) Get(key string) string {
return ""
}
+// Has checks if an Option exist with the given key.
+func (opts Options) Has(key string) bool {
+ for _, o := range opts {
+ if o.IsKey(key) {
+ return true
+ }
+ }
+ return false
+}
+
// GetAll returns all possible values for the same key.
func (opts Options) GetAll(key string) []string {
result := []string{}
diff --git a/plumbing/format/config/option_test.go b/plumbing/format/config/option_test.go
index 8588de1..49b4855 100644
--- a/plumbing/format/config/option_test.go
+++ b/plumbing/format/config/option_test.go
@@ -8,6 +8,21 @@ type OptionSuite struct{}
var _ = Suite(&OptionSuite{})
+func (s *OptionSuite) TestOptions_Has(c *C) {
+ o := Options{
+ &Option{"k", "v"},
+ &Option{"ok", "v1"},
+ &Option{"K", "v2"},
+ }
+ c.Assert(o.Has("k"), Equals, true)
+ c.Assert(o.Has("K"), Equals, true)
+ c.Assert(o.Has("ok"), Equals, true)
+ c.Assert(o.Has("unexistant"), Equals, false)
+
+ o = Options{}
+ c.Assert(o.Has("k"), Equals, false)
+}
+
func (s *OptionSuite) TestOptions_GetAll(c *C) {
o := Options{
&Option{"k", "v"},
diff --git a/plumbing/format/config/section.go b/plumbing/format/config/section.go
index f743da6..07f72f3 100644
--- a/plumbing/format/config/section.go
+++ b/plumbing/format/config/section.go
@@ -64,31 +64,6 @@ func (s *Section) IsName(name string) bool {
return strings.EqualFold(s.Name, name)
}
-// Option return the value for the specified key. Empty string is returned if
-// key does not exists.
-func (s *Section) Option(key string) string {
- return s.Options.Get(key)
-}
-
-// AddOption adds a new Option to the Section. The updated Section is returned.
-func (s *Section) AddOption(key string, value string) *Section {
- s.Options = s.Options.withAddedOption(key, value)
- return s
-}
-
-// SetOption adds a new Option to the Section. If the option already exists, is replaced.
-// The updated Section is returned.
-func (s *Section) SetOption(key string, value string) *Section {
- s.Options = s.Options.withSettedOption(key, value)
- return s
-}
-
-// Remove an option with the specified key. The updated Section is returned.
-func (s *Section) RemoveOption(key string) *Section {
- s.Options = s.Options.withoutOption(key)
- return s
-}
-
// Subsection returns a Subsection from the specified Section. If the
// Subsection does not exists, new one is created and added to Section.
func (s *Section) Subsection(name string) *Subsection {
@@ -115,6 +90,55 @@ func (s *Section) HasSubsection(name string) bool {
return false
}
+// RemoveSubsection removes a subsection from a Section.
+func (s *Section) RemoveSubsection(name string) *Section {
+ result := Subsections{}
+ for _, s := range s.Subsections {
+ if !s.IsName(name) {
+ result = append(result, s)
+ }
+ }
+
+ s.Subsections = result
+ return s
+}
+
+// Option return the value for the specified key. Empty string is returned if
+// key does not exists.
+func (s *Section) Option(key string) string {
+ return s.Options.Get(key)
+}
+
+// OptionAll returns all possible values for an option with the specified key.
+// If the option does not exists, an empty slice will be returned.
+func (s *Section) OptionAll(key string) []string {
+ return s.Options.GetAll(key)
+}
+
+// HasOption checks if the Section has an Option with the given key.
+func (s *Section) HasOption(key string) bool {
+ return s.Options.Has(key)
+}
+
+// AddOption adds a new Option to the Section. The updated Section is returned.
+func (s *Section) AddOption(key string, value string) *Section {
+ s.Options = s.Options.withAddedOption(key, value)
+ return s
+}
+
+// SetOption adds a new Option to the Section. If the option already exists, is replaced.
+// The updated Section is returned.
+func (s *Section) SetOption(key string, value string) *Section {
+ s.Options = s.Options.withSettedOption(key, value)
+ return s
+}
+
+// Remove an option with the specified key. The updated Section is returned.
+func (s *Section) RemoveOption(key string) *Section {
+ s.Options = s.Options.withoutOption(key)
+ return s
+}
+
// IsName checks if the name of the subsection is exactly the specified name.
func (s *Subsection) IsName(name string) bool {
return s.Name == name
@@ -126,6 +150,17 @@ func (s *Subsection) Option(key string) string {
return s.Options.Get(key)
}
+// OptionAll returns all possible values for an option with the specified key.
+// If the option does not exists, an empty slice will be returned.
+func (s *Subsection) OptionAll(key string) []string {
+ return s.Options.GetAll(key)
+}
+
+// HasOption checks if the Subsection has an Option with the given key.
+func (s *Subsection) HasOption(key string) bool {
+ return s.Options.Has(key)
+}
+
// AddOption adds a new Option to the Subsection. The updated Subsection is returned.
func (s *Subsection) AddOption(key string, value string) *Subsection {
s.Options = s.Options.withAddedOption(key, value)
diff --git a/plumbing/format/config/section_test.go b/plumbing/format/config/section_test.go
index 0290386..c7cc4a9 100644
--- a/plumbing/format/config/section_test.go
+++ b/plumbing/format/config/section_test.go
@@ -8,6 +8,115 @@ type SectionSuite struct{}
var _ = Suite(&SectionSuite{})
+func (s *SectionSuite) TestSections_GoString(c *C) {
+ sects := Sections{
+ &Section{
+ Options: []*Option{
+ {Key: "key1", Value: "value1"},
+ {Key: "key2", Value: "value2"},
+ },
+ },
+ &Section{
+ Options: []*Option{
+ {Key: "key1", Value: "value3"},
+ {Key: "key2", Value: "value4"},
+ },
+ },
+ }
+
+ expected := "&config.Section{Name:\"\", Options:&config.Option{Key:\"key1\", Value:\"value1\"}, &config.Option{Key:\"key2\", Value:\"value2\"}, Subsections:}, &config.Section{Name:\"\", Options:&config.Option{Key:\"key1\", Value:\"value3\"}, &config.Option{Key:\"key2\", Value:\"value4\"}, Subsections:}"
+ c.Assert(sects.GoString(), Equals, expected)
+}
+
+func (s *SectionSuite) TestSubsections_GoString(c *C) {
+ sects := Subsections{
+ &Subsection{
+ Options: []*Option{
+ {Key: "key1", Value: "value1"},
+ {Key: "key2", Value: "value2"},
+ {Key: "key1", Value: "value3"},
+ },
+ },
+ &Subsection{
+ Options: []*Option{
+ {Key: "key1", Value: "value1"},
+ {Key: "key2", Value: "value2"},
+ {Key: "key1", Value: "value3"},
+ },
+ },
+ }
+
+ expected := "&config.Subsection{Name:\"\", Options:&config.Option{Key:\"key1\", Value:\"value1\"}, &config.Option{Key:\"key2\", Value:\"value2\"}, &config.Option{Key:\"key1\", Value:\"value3\"}}, &config.Subsection{Name:\"\", Options:&config.Option{Key:\"key1\", Value:\"value1\"}, &config.Option{Key:\"key2\", Value:\"value2\"}, &config.Option{Key:\"key1\", Value:\"value3\"}}"
+ c.Assert(sects.GoString(), Equals, expected)
+}
+
+func (s *SectionSuite) TestSection_IsName(c *C) {
+ sect := &Section{
+ Name: "name1",
+ }
+
+ c.Assert(sect.IsName("name1"), Equals, true)
+ c.Assert(sect.IsName("Name1"), Equals, true)
+}
+
+func (s *SectionSuite) TestSection_Subsection(c *C) {
+ subSect1 := &Subsection{
+ Name: "name1",
+ Options: Options{
+ &Option{Key: "key1", Value: "value1"},
+ },
+ }
+ sect := &Section{
+ Subsections: Subsections{
+ subSect1,
+ },
+ }
+
+ c.Assert(sect.Subsection("name1"), DeepEquals, subSect1)
+
+ subSect2 := &Subsection{
+ Name: "name2",
+ }
+ c.Assert(sect.Subsection("name2"), DeepEquals, subSect2)
+}
+
+func (s *SectionSuite) TestSection_HasSubsection(c *C) {
+ sect := &Section{
+ Subsections: Subsections{
+ &Subsection{
+ Name: "name1",
+ },
+ },
+ }
+
+ c.Assert(sect.HasSubsection("name1"), Equals, true)
+ c.Assert(sect.HasSubsection("name2"), Equals, false)
+}
+
+func (s *SectionSuite) TestSection_RemoveSubsection(c *C) {
+ sect := &Section{
+ Subsections: Subsections{
+ &Subsection{
+ Name: "name1",
+ },
+ &Subsection{
+ Name: "name2",
+ },
+ },
+ }
+
+ expected := &Section{
+ Subsections: Subsections{
+ &Subsection{
+ Name: "name2",
+ },
+ },
+ }
+ c.Assert(sect.RemoveSubsection("name1"), DeepEquals, expected)
+ c.Assert(sect.HasSubsection("name1"), Equals, false)
+ c.Assert(sect.HasSubsection("name2"), Equals, true)
+}
+
func (s *SectionSuite) TestSection_Option(c *C) {
sect := &Section{
Options: []*Option{
@@ -21,17 +130,71 @@ func (s *SectionSuite) TestSection_Option(c *C) {
c.Assert(sect.Option("key1"), Equals, "value3")
}
-func (s *SectionSuite) TestSubsection_Option(c *C) {
- sect := &Subsection{
+func (s *SectionSuite) TestSection_OptionAll(c *C) {
+ sect := &Section{
Options: []*Option{
{Key: "key1", Value: "value1"},
{Key: "key2", Value: "value2"},
{Key: "key1", Value: "value3"},
},
}
- c.Assert(sect.Option("otherkey"), Equals, "")
- c.Assert(sect.Option("key2"), Equals, "value2")
- c.Assert(sect.Option("key1"), Equals, "value3")
+ c.Assert(sect.OptionAll("otherkey"), DeepEquals, []string{})
+ c.Assert(sect.OptionAll("key2"), DeepEquals, []string{"value2"})
+ c.Assert(sect.OptionAll("key1"), DeepEquals, []string{"value1", "value3"})
+}
+
+func (s *SectionSuite) TestSection_HasOption(c *C) {
+ sect := &Section{
+ Options: []*Option{
+ {Key: "key1", Value: "value1"},
+ {Key: "key2", Value: "value2"},
+ {Key: "key1", Value: "value3"},
+ },
+ }
+ c.Assert(sect.HasOption("otherkey"), Equals, false)
+ c.Assert(sect.HasOption("key2"), Equals, true)
+ c.Assert(sect.HasOption("key1"), Equals, true)
+}
+
+func (s *SectionSuite) TestSection_AddOption(c *C) {
+ sect := &Section{
+ Options: []*Option{
+ {"key1", "value1"},
+ },
+ }
+ sect1 := &Section{
+ Options: []*Option{
+ {"key1", "value1"},
+ {"key2", "value2"},
+ },
+ }
+ c.Assert(sect.AddOption("key2", "value2"), DeepEquals, sect1)
+
+ sect2 := &Section{
+ Options: []*Option{
+ {"key1", "value1"},
+ {"key2", "value2"},
+ {"key1", "value3"},
+ },
+ }
+ c.Assert(sect.AddOption("key1", "value3"), DeepEquals, sect2)
+}
+
+func (s *SectionSuite) TestSection_SetOption(c *C) {
+ sect := &Section{
+ Options: []*Option{
+ {Key: "key1", Value: "value1"},
+ {Key: "key2", Value: "value2"},
+ },
+ }
+
+ expected := &Section{
+ Options: []*Option{
+ {Key: "key2", Value: "value2"},
+ {Key: "key1", Value: "value4"},
+ },
+ }
+ c.Assert(sect.SetOption("key1", "value4"), DeepEquals, expected)
}
func (s *SectionSuite) TestSection_RemoveOption(c *C) {
@@ -52,7 +215,16 @@ func (s *SectionSuite) TestSection_RemoveOption(c *C) {
c.Assert(sect.RemoveOption("key1"), DeepEquals, expected)
}
-func (s *SectionSuite) TestSubsection_RemoveOption(c *C) {
+func (s *SectionSuite) TestSubsection_IsName(c *C) {
+ sect := &Subsection{
+ Name: "name1",
+ }
+
+ c.Assert(sect.IsName("name1"), Equals, true)
+ c.Assert(sect.IsName("Name1"), Equals, false)
+}
+
+func (s *SectionSuite) TestSubsection_Option(c *C) {
sect := &Subsection{
Options: []*Option{
{Key: "key1", Value: "value1"},
@@ -60,14 +232,59 @@ func (s *SectionSuite) TestSubsection_RemoveOption(c *C) {
{Key: "key1", Value: "value3"},
},
}
- c.Assert(sect.RemoveOption("otherkey"), DeepEquals, sect)
+ c.Assert(sect.Option("otherkey"), Equals, "")
+ c.Assert(sect.Option("key2"), Equals, "value2")
+ c.Assert(sect.Option("key1"), Equals, "value3")
+}
- expected := &Subsection{
+func (s *SectionSuite) TestSubsection_OptionAll(c *C) {
+ sect := &Subsection{
Options: []*Option{
+ {Key: "key1", Value: "value1"},
{Key: "key2", Value: "value2"},
+ {Key: "key1", Value: "value3"},
},
}
- c.Assert(sect.RemoveOption("key1"), DeepEquals, expected)
+ c.Assert(sect.OptionAll("otherkey"), DeepEquals, []string{})
+ c.Assert(sect.OptionAll("key2"), DeepEquals, []string{"value2"})
+ c.Assert(sect.OptionAll("key1"), DeepEquals, []string{"value1", "value3"})
+}
+
+func (s *SectionSuite) TestSubsection_HasOption(c *C) {
+ sect := &Subsection{
+ Options: []*Option{
+ {Key: "key1", Value: "value1"},
+ {Key: "key2", Value: "value2"},
+ {Key: "key1", Value: "value3"},
+ },
+ }
+ c.Assert(sect.HasOption("otherkey"), Equals, false)
+ c.Assert(sect.HasOption("key2"), Equals, true)
+ c.Assert(sect.HasOption("key1"), Equals, true)
+}
+
+func (s *SectionSuite) TestSubsection_AddOption(c *C) {
+ sect := &Subsection{
+ Options: []*Option{
+ {"key1", "value1"},
+ },
+ }
+ sect1 := &Subsection{
+ Options: []*Option{
+ {"key1", "value1"},
+ {"key2", "value2"},
+ },
+ }
+ c.Assert(sect.AddOption("key2", "value2"), DeepEquals, sect1)
+
+ sect2 := &Subsection{
+ Options: []*Option{
+ {"key1", "value1"},
+ {"key2", "value2"},
+ {"key1", "value3"},
+ },
+ }
+ c.Assert(sect.AddOption("key1", "value3"), DeepEquals, sect2)
}
func (s *SectionSuite) TestSubsection_SetOption(c *C) {
@@ -88,3 +305,21 @@ func (s *SectionSuite) TestSubsection_SetOption(c *C) {
}
c.Assert(sect.SetOption("key1", "value1", "value4"), DeepEquals, expected)
}
+
+func (s *SectionSuite) TestSubsection_RemoveOption(c *C) {
+ sect := &Subsection{
+ Options: []*Option{
+ {Key: "key1", Value: "value1"},
+ {Key: "key2", Value: "value2"},
+ {Key: "key1", Value: "value3"},
+ },
+ }
+ c.Assert(sect.RemoveOption("otherkey"), DeepEquals, sect)
+
+ expected := &Subsection{
+ Options: []*Option{
+ {Key: "key2", Value: "value2"},
+ },
+ }
+ c.Assert(sect.RemoveOption("key1"), DeepEquals, expected)
+}
diff --git a/remote.go b/remote.go
index 89b3051..d88e8e6 100644
--- a/remote.go
+++ b/remote.go
@@ -32,6 +32,19 @@ var (
ErrExactSHA1NotSupported = errors.New("server does not support exact SHA1 refspec")
)
+type NoMatchingRefSpecError struct {
+ refSpec config.RefSpec
+}
+
+func (e NoMatchingRefSpecError) Error() string {
+ return fmt.Sprintf("couldn't find remote ref %q", e.refSpec.Src())
+}
+
+func (e NoMatchingRefSpecError) Is(target error) bool {
+ _, ok := target.(NoMatchingRefSpecError)
+ return ok
+}
+
const (
// This describes the maximum number of commits to walk when
// computing the haves to send to a server, for each ref in the
@@ -751,7 +764,7 @@ func doCalculateRefs(
})
if !matched && !s.IsWildcard() {
- return fmt.Errorf("couldn't find remote ref %q", s.Src())
+ return NoMatchingRefSpecError{refSpec: s}
}
return err
diff --git a/remote_test.go b/remote_test.go
index c6ea9ea..3446f1a 100644
--- a/remote_test.go
+++ b/remote_test.go
@@ -3,6 +3,7 @@ package git
import (
"bytes"
"context"
+ "errors"
"io"
"io/ioutil"
"os"
@@ -145,6 +146,7 @@ func (s *RemoteSuite) TestFetchNonExistantReference(c *C) {
})
c.Assert(err, ErrorMatches, "couldn't find remote ref.*")
+ c.Assert(errors.Is(err, NoMatchingRefSpecError{}), Equals, true)
}
func (s *RemoteSuite) TestFetchContext(c *C) {
diff --git a/submodule.go b/submodule.go
index dff26b0..b6bef46 100644
--- a/submodule.go
+++ b/submodule.go
@@ -5,6 +5,8 @@ import (
"context"
"errors"
"fmt"
+ "net/url"
+ "path"
"github.com/go-git/go-billy/v5"
"github.com/go-git/go-git/v5/config"
@@ -131,9 +133,29 @@ func (s *Submodule) Repository() (*Repository, error) {
return nil, err
}
+ moduleURL, err := url.Parse(s.c.URL)
+ if err != nil {
+ return nil, err
+ }
+
+ if !path.IsAbs(moduleURL.Path) {
+ remotes, err := s.w.r.Remotes()
+ if err != nil {
+ return nil, err
+ }
+
+ rootURL, err := url.Parse(remotes[0].c.URLs[0])
+ if err != nil {
+ return nil, err
+ }
+
+ rootURL.Path = path.Join(rootURL.Path, moduleURL.Path)
+ *moduleURL = *rootURL
+ }
+
_, err = r.CreateRemote(&config.RemoteConfig{
Name: DefaultRemoteName,
- URLs: []string{s.c.URL},
+ URLs: []string{moduleURL.String()},
})
return r, err
diff --git a/worktree.go b/worktree.go
index 62ad03b..1c89a02 100644
--- a/worktree.go
+++ b/worktree.go
@@ -716,7 +716,11 @@ func (w *Worktree) readGitmodulesFile() (*config.Modules, error) {
}
m := config.NewModules()
- return m, m.Unmarshal(input)
+ if err := m.Unmarshal(input); err != nil {
+ return m, err
+ }
+
+ return m, nil
}
// Clean the worktree by removing untracked files.
diff --git a/worktree_test.go b/worktree_test.go
index 59c80af..8a7586a 100644
--- a/worktree_test.go
+++ b/worktree_test.go
@@ -4,6 +4,7 @@ import (
"bytes"
"context"
"errors"
+ "io"
"io/ioutil"
"os"
"path/filepath"
@@ -12,7 +13,7 @@ import (
"testing"
"time"
- fixtures "github.com/go-git/go-git-fixtures/v4"
+ "github.com/go-git/go-git-fixtures/v4"
"github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/filemode"
@@ -519,6 +520,62 @@ func (s *WorktreeSuite) TestCheckoutSubmoduleInitialized(c *C) {
c.Assert(status.IsClean(), Equals, true)
}
+func (s *WorktreeSuite) TestCheckoutRelativePathSubmoduleInitialized(c *C) {
+ url := "https://github.com/git-fixtures/submodule.git"
+ r := s.NewRepository(fixtures.ByURL(url).One())
+
+ // modify the .gitmodules from original one
+ file, err := r.wt.OpenFile(".gitmodules", os.O_WRONLY|os.O_TRUNC, 0666)
+ c.Assert(err, IsNil)
+
+ n, err := io.WriteString(file, `[submodule "basic"]
+ path = basic
+ url = ../basic.git
+[submodule "itself"]
+ path = itself
+ url = ../submodule.git`)
+ c.Assert(err, IsNil)
+ c.Assert(n, Not(Equals), 0)
+
+ w, err := r.Worktree()
+ c.Assert(err, IsNil)
+
+ w.Add(".gitmodules")
+ w.Commit("test", &CommitOptions{})
+
+ // test submodule path
+ modules, err := w.readGitmodulesFile()
+
+ c.Assert(modules.Submodules["basic"].URL, Equals, "../basic.git")
+ c.Assert(modules.Submodules["itself"].URL, Equals, "../submodule.git")
+
+ basicSubmodule, err := w.Submodule("basic")
+ c.Assert(err, IsNil)
+ basicRepo, err := basicSubmodule.Repository()
+ c.Assert(err, IsNil)
+ basicRemotes, err := basicRepo.Remotes()
+ c.Assert(err, IsNil)
+ c.Assert(basicRemotes[0].Config().URLs[0], Equals, "https://github.com/git-fixtures/basic.git")
+
+ itselfSubmodule, err := w.Submodule("itself")
+ c.Assert(err, IsNil)
+ itselfRepo, err := itselfSubmodule.Repository()
+ c.Assert(err, IsNil)
+ itselfRemotes, err := itselfRepo.Remotes()
+ c.Assert(err, IsNil)
+ c.Assert(itselfRemotes[0].Config().URLs[0], Equals, "https://github.com/git-fixtures/submodule.git")
+
+ sub, err := w.Submodules()
+ c.Assert(err, IsNil)
+
+ err = sub.Update(&SubmoduleUpdateOptions{Init: true, RecurseSubmodules: DefaultSubmoduleRecursionDepth})
+ c.Assert(err, IsNil)
+
+ status, err := w.Status()
+ c.Assert(err, IsNil)
+ c.Assert(status.IsClean(), Equals, true)
+}
+
func (s *WorktreeSuite) TestCheckoutIndexMem(c *C) {
fs := memfs.New()
w := &Worktree{