aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cache/object.go4
-rw-r--r--common.go4
-rw-r--r--config/config.go130
-rw-r--r--config/config_test.go48
-rw-r--r--config/modules.go136
-rw-r--r--config/modules_test.go70
-rw-r--r--examples/progress/main.go15
-rw-r--r--fixtures/fixtures.go2
-rw-r--r--options.go29
-rw-r--r--plumbing/protocol/packp/sideband/demux.go3
-rw-r--r--plumbing/protocol/packp/sideband/demux_test.go11
-rw-r--r--remote.go10
-rw-r--r--remote_test.go37
-rw-r--r--repository.go20
-rw-r--r--repository_test.go45
-rw-r--r--storage/filesystem/config.go104
16 files changed, 489 insertions, 179 deletions
diff --git a/cache/object.go b/cache/object.go
index d6cd49b..cd57030 100644
--- a/cache/object.go
+++ b/cache/object.go
@@ -54,13 +54,13 @@ func (c *ObjectFIFO) Add(o plumbing.EncodedObject) {
c.actualSize += o.Size()
}
-// Get returns an object by his hash. If the object is not into the cache, it
+// Get returns an object by his hash. If the object is not found in the cache, it
// returns nil
func (c *ObjectFIFO) Get(k plumbing.Hash) plumbing.EncodedObject {
return c.objects[k]
}
-// Clear the content of this cache object
+// Clear the content of this object cache
func (c *ObjectFIFO) Clear() {
c.objects = make(map[plumbing.Hash]plumbing.EncodedObject)
c.order = newQueue(initialQueueSize)
diff --git a/common.go b/common.go
index 580a829..56390ac 100644
--- a/common.go
+++ b/common.go
@@ -9,8 +9,8 @@ import (
// Storer is a generic storage of objects, references and any information
// related to a particular repository. Some Storer implementations persist the
-// information in an system directory (such as `.git`) and others
-// implementations are in memmory being ephemeral
+// information in a system directory (such as `.git`) and others
+// implementations are in memory being ephemeral
type Storer interface {
storer.EncodedObjectStorer
storer.ReferenceStorer
diff --git a/config/config.go b/config/config.go
index a2b5012..bf0cea2 100644
--- a/config/config.go
+++ b/config/config.go
@@ -2,8 +2,11 @@
package config
import (
+ "bytes"
"errors"
"fmt"
+
+ format "gopkg.in/src-d/go-git.v4/plumbing/format/config"
)
const (
@@ -27,21 +30,32 @@ var (
)
// Config contains the repository configuration
+// ftp://www.kernel.org/pub/software/scm/git/docs/git-config.html#FILES
type Config struct {
+ // Core variables
Core struct {
+ // IsBare if true this repository is assumed to be bare and has no
+ // working directory associated with it
IsBare bool
}
+ // Remote list of repository remotes
Remotes map[string]*RemoteConfig
+
+ // contains the raw information of a config file, the main goal is preserve
+ // the parsed information from the original format, to avoid missing
+ // unsupported features.
+ raw *format.Config
}
// NewConfig returns a new empty Config
func NewConfig() *Config {
return &Config{
Remotes: make(map[string]*RemoteConfig, 0),
+ raw: format.New(),
}
}
-// Validate validate the fields and set the default values
+// Validate validates the fields and sets the default values
func (c *Config) Validate() error {
for name, r := range c.Remotes {
if r.Name != name {
@@ -56,14 +70,90 @@ func (c *Config) Validate() error {
return nil
}
-// RemoteConfig contains the configuration for a given repository
+const (
+ remoteSection = "remote"
+ coreSection = "core"
+ fetchKey = "fetch"
+ urlKey = "url"
+ bareKey = "bare"
+)
+
+// Unmarshal parses a git-config file and stores it
+func (c *Config) Unmarshal(b []byte) error {
+ r := bytes.NewBuffer(b)
+ d := format.NewDecoder(r)
+
+ c.raw = format.New()
+ if err := d.Decode(c.raw); err != nil {
+ return err
+ }
+
+ c.unmarshalCore()
+ c.unmarshalRemotes()
+ return nil
+}
+
+func (c *Config) unmarshalCore() {
+ s := c.raw.Section(coreSection)
+ if s.Options.Get(bareKey) == "true" {
+ c.Core.IsBare = true
+ }
+}
+
+func (c *Config) unmarshalRemotes() {
+ s := c.raw.Section(remoteSection)
+ for _, sub := range s.Subsections {
+ r := &RemoteConfig{}
+ r.unmarshal(sub)
+
+ c.Remotes[r.Name] = r
+ }
+}
+
+// Marshal returns Config encoded as a git-config file
+func (c *Config) Marshal() ([]byte, error) {
+ c.marshalCore()
+ c.marshalRemotes()
+
+ buf := bytes.NewBuffer(nil)
+ if err := format.NewEncoder(buf).Encode(c.raw); err != nil {
+ return nil, err
+ }
+
+ return buf.Bytes(), nil
+}
+
+func (c *Config) marshalCore() {
+ s := c.raw.Section(coreSection)
+ s.SetOption(bareKey, fmt.Sprintf("%t", c.Core.IsBare))
+}
+
+func (c *Config) marshalRemotes() {
+ s := c.raw.Section(remoteSection)
+ s.Subsections = make(format.Subsections, len(c.Remotes))
+
+ var i int
+ for _, r := range c.Remotes {
+ s.Subsections[i] = r.marshal()
+ i++
+ }
+}
+
+// RemoteConfig contains the configuration for a given remote repository
type RemoteConfig struct {
- Name string
- URL string
+ // Name of the remote
+ Name string
+ // URL the URL of a remote repository
+ URL string
+ // Fetch the default set of "refspec" for fetch operation
Fetch []RefSpec
+
+ // raw representation of the subsection, filled by marshal or unmarshal are
+ // called
+ raw *format.Subsection
}
-// Validate validate the fields and set the default values
+// Validate validates the fields and sets the default values
func (c *RemoteConfig) Validate() error {
if c.Name == "" {
return ErrRemoteConfigEmptyName
@@ -79,3 +169,33 @@ func (c *RemoteConfig) Validate() error {
return nil
}
+
+func (c *RemoteConfig) unmarshal(s *format.Subsection) {
+ c.raw = s
+
+ fetch := []RefSpec{}
+ for _, f := range c.raw.Options.GetAll(fetchKey) {
+ rs := RefSpec(f)
+ if rs.IsValid() {
+ fetch = append(fetch, rs)
+ }
+ }
+
+ c.Name = c.raw.Name
+ c.URL = c.raw.Option(urlKey)
+ c.Fetch = fetch
+}
+
+func (c *RemoteConfig) marshal() *format.Subsection {
+ if c.raw == nil {
+ c.raw = &format.Subsection{}
+ }
+
+ c.raw.Name = c.Name
+ c.raw.SetOption(urlKey, c.URL)
+ for _, rs := range c.Fetch {
+ c.raw.SetOption(fetchKey, rs.String())
+ }
+
+ return c.raw
+}
diff --git a/config/config_test.go b/config/config_test.go
index f2539d0..2bcefe4 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -6,7 +6,51 @@ type ConfigSuite struct{}
var _ = Suite(&ConfigSuite{})
-func (s *ConfigSuite) TestConfigValidateInvalidRemote(c *C) {
+func (s *ConfigSuite) TestUnmarshall(c *C) {
+ input := []byte(`[core]
+ bare = true
+[remote "origin"]
+ url = git@github.com:mcuadros/go-git.git
+ fetch = +refs/heads/*:refs/remotes/origin/*
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
+`)
+
+ cfg := NewConfig()
+ err := cfg.Unmarshal(input)
+ c.Assert(err, IsNil)
+
+ c.Assert(cfg.Core.IsBare, Equals, true)
+ c.Assert(cfg.Remotes, HasLen, 1)
+ c.Assert(cfg.Remotes["origin"].Name, Equals, "origin")
+ c.Assert(cfg.Remotes["origin"].URL, Equals, "git@github.com:mcuadros/go-git.git")
+ c.Assert(cfg.Remotes["origin"].Fetch, DeepEquals, []RefSpec{"+refs/heads/*:refs/remotes/origin/*"})
+}
+
+func (s *ConfigSuite) TestUnmarshallMarshall(c *C) {
+ input := []byte(`[core]
+ bare = true
+ custom = ignored
+[remote "origin"]
+ url = git@github.com:mcuadros/go-git.git
+ fetch = +refs/heads/*:refs/remotes/origin/*
+ mirror = true
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
+`)
+
+ cfg := NewConfig()
+ err := cfg.Unmarshal(input)
+ c.Assert(err, IsNil)
+
+ output, err := cfg.Marshal()
+ c.Assert(err, IsNil)
+ c.Assert(output, DeepEquals, input)
+}
+
+func (s *ConfigSuite) TestValidateInvalidRemote(c *C) {
config := &Config{
Remotes: map[string]*RemoteConfig{
"foo": {Name: "foo"},
@@ -16,7 +60,7 @@ func (s *ConfigSuite) TestConfigValidateInvalidRemote(c *C) {
c.Assert(config.Validate(), Equals, ErrRemoteConfigEmptyURL)
}
-func (s *ConfigSuite) TestConfigValidateInvalidKey(c *C) {
+func (s *ConfigSuite) TestValidateInvalidKey(c *C) {
config := &Config{
Remotes: map[string]*RemoteConfig{
"bar": {Name: "foo"},
diff --git a/config/modules.go b/config/modules.go
new file mode 100644
index 0000000..b1f35b8
--- /dev/null
+++ b/config/modules.go
@@ -0,0 +1,136 @@
+package config
+
+import (
+ "bytes"
+ "errors"
+
+ format "gopkg.in/src-d/go-git.v4/plumbing/format/config"
+)
+
+var (
+ ErrModuleEmptyURL = errors.New("module config: empty URL")
+ ErrModuleEmptyPath = errors.New("module config: empty path")
+)
+
+// Modules defines the submodules properties, represents a .gitmodules file
+// https://www.kernel.org/pub/software/scm/git/docs/gitmodules.html
+type Modules struct {
+ // Submodules is a map of submodules being the key the name of the submodule
+ Submodules map[string]*Submodule
+
+ raw *format.Config
+}
+
+// NewModules returns a new empty Modules
+func NewModules() *Modules {
+ return &Modules{
+ Submodules: make(map[string]*Submodule, 0),
+ raw: format.New(),
+ }
+}
+
+const (
+ submoduleSection = "submodule"
+ pathKey = "path"
+ branchKey = "branch"
+)
+
+// Unmarshal parses a git-config file and stores it
+func (m *Modules) Unmarshal(b []byte) error {
+ r := bytes.NewBuffer(b)
+ d := format.NewDecoder(r)
+
+ m.raw = format.New()
+ if err := d.Decode(m.raw); err != nil {
+ return err
+ }
+
+ s := m.raw.Section(submoduleSection)
+ for _, sub := range s.Subsections {
+ mod := &Submodule{}
+ mod.unmarshal(sub)
+
+ m.Submodules[mod.Path] = mod
+ }
+
+ return nil
+}
+
+// Marshal returns Modules encoded as a git-config file
+func (m *Modules) Marshal() ([]byte, error) {
+ s := m.raw.Section(submoduleSection)
+ s.Subsections = make(format.Subsections, len(m.Submodules))
+
+ var i int
+ for _, r := range m.Submodules {
+ s.Subsections[i] = r.marshal()
+ i++
+ }
+
+ buf := bytes.NewBuffer(nil)
+ if err := format.NewEncoder(buf).Encode(m.raw); err != nil {
+ return nil, err
+ }
+
+ return buf.Bytes(), nil
+}
+
+// Submodule defines a submodule
+type Submodule struct {
+ // Name module name
+ Name string
+ // Path defines the path, relative to the top-level directory of the Git
+ // working tree,
+ Path string
+ // URL defines a URL from which the submodule repository can be cloned.
+ URL string
+ // Branch is a remote branch name for tracking updates in the upstream
+ // submodule. Optional value.
+ Branch string
+
+ // raw representation of the subsection, filled by marshal or unmarshal are
+ // called
+ raw *format.Subsection
+}
+
+// Validate validates the fields and sets the default values
+func (m *Submodule) Validate() error {
+ if m.Path == "" {
+ return ErrModuleEmptyPath
+ }
+
+ if m.URL == "" {
+ return ErrModuleEmptyURL
+ }
+
+ return nil
+}
+
+func (m *Submodule) unmarshal(s *format.Subsection) {
+ m.raw = s
+
+ m.Name = m.raw.Name
+ m.Path = m.raw.Option(pathKey)
+ m.URL = m.raw.Option(urlKey)
+ m.Branch = m.raw.Option(branchKey)
+}
+
+func (m *Submodule) marshal() *format.Subsection {
+ if m.raw == nil {
+ m.raw = &format.Subsection{}
+ }
+
+ m.raw.Name = m.Name
+ if m.raw.Name == "" {
+ m.raw.Name = m.Path
+ }
+
+ m.raw.SetOption(pathKey, m.Path)
+ m.raw.SetOption(urlKey, m.URL)
+
+ if m.Branch != "" {
+ m.raw.SetOption(branchKey, m.Branch)
+ }
+
+ return m.raw
+}
diff --git a/config/modules_test.go b/config/modules_test.go
new file mode 100644
index 0000000..36cd93f
--- /dev/null
+++ b/config/modules_test.go
@@ -0,0 +1,70 @@
+package config
+
+import . "gopkg.in/check.v1"
+
+type ModulesSuite struct{}
+
+var _ = Suite(&ModulesSuite{})
+
+func (s *ModulesSuite) TestValidateMissingURL(c *C) {
+ m := &Submodule{Path: "foo"}
+ c.Assert(m.Validate(), Equals, ErrModuleEmptyURL)
+}
+
+func (s *ModulesSuite) TestValidateMissingName(c *C) {
+ m := &Submodule{URL: "bar"}
+ c.Assert(m.Validate(), Equals, ErrModuleEmptyPath)
+}
+
+func (s *ModulesSuite) TestMarshall(c *C) {
+ input := []byte(`[submodule "qux"]
+ path = qux
+ url = baz
+ branch = bar
+`)
+
+ cfg := NewModules()
+ cfg.Submodules["qux"] = &Submodule{Path: "qux", URL: "baz", Branch: "bar"}
+
+ output, err := cfg.Marshal()
+ c.Assert(err, IsNil)
+ c.Assert(output, DeepEquals, input)
+}
+
+func (s *ModulesSuite) TestUnmarshall(c *C) {
+ input := []byte(`[submodule "qux"]
+ path = qux
+ url = https://github.com/foo/qux.git
+[submodule "foo/bar"]
+ path = foo/bar
+ url = https://github.com/foo/bar.git
+ branch = dev
+`)
+
+ cfg := NewModules()
+ err := cfg.Unmarshal(input)
+ c.Assert(err, IsNil)
+
+ c.Assert(cfg.Submodules, HasLen, 2)
+ c.Assert(cfg.Submodules["qux"].Name, Equals, "qux")
+ c.Assert(cfg.Submodules["qux"].URL, Equals, "https://github.com/foo/qux.git")
+ c.Assert(cfg.Submodules["foo/bar"].Name, Equals, "foo/bar")
+ c.Assert(cfg.Submodules["foo/bar"].URL, Equals, "https://github.com/foo/bar.git")
+ c.Assert(cfg.Submodules["foo/bar"].Branch, Equals, "dev")
+}
+
+func (s *ModulesSuite) TestUnmarshallMarshall(c *C) {
+ input := []byte(`[submodule "foo/bar"]
+ path = foo/bar
+ url = https://github.com/foo/bar.git
+ ignore = all
+`)
+
+ cfg := NewModules()
+ err := cfg.Unmarshal(input)
+ c.Assert(err, IsNil)
+
+ output, err := cfg.Marshal()
+ c.Assert(err, IsNil)
+ c.Assert(string(output), DeepEquals, string(input))
+}
diff --git a/examples/progress/main.go b/examples/progress/main.go
index e0e4c1d..46ff57a 100644
--- a/examples/progress/main.go
+++ b/examples/progress/main.go
@@ -12,20 +12,17 @@ func main() {
url := os.Args[1]
directory := os.Args[2]
- r, err := git.NewFilesystemRepository(directory)
- CheckIfError(err)
-
- // as git does, when you make a clone, pull or some other operations, the
- // server sends information via the sideband, this information can being
- // collected provinding a io.Writer to the repository
- r.Progress = os.Stdout
-
// Clone the given repository to the given directory
Info("git clone %s %s", url, directory)
- err = r.Clone(&git.CloneOptions{
+ _, err := git.PlainClone(directory, false, &git.CloneOptions{
URL: url,
Depth: 1,
+
+ // as git does, when you make a clone, pull or some other operations the
+ // server sends information via the sideband, this information can being
+ // collected provinding a io.Writer to the CloneOptions options
+ Progress: os.Stdout,
})
CheckIfError(err)
diff --git a/fixtures/fixtures.go b/fixtures/fixtures.go
index 5bcb80d..b3fc717 100644
--- a/fixtures/fixtures.go
+++ b/fixtures/fixtures.go
@@ -269,7 +269,7 @@ func (g Fixtures) Exclude(tag string) Fixtures {
return r
}
-// Init set the correct path to be able to access to the fixtures files
+// Init sets the correct path to access the fixtures files
func Init() error {
srcs := build.Default.SrcDirs()
diff --git a/options.go b/options.go
index cb0859b..8abace2 100644
--- a/options.go
+++ b/options.go
@@ -5,6 +5,7 @@ import (
"gopkg.in/src-d/go-git.v4/config"
"gopkg.in/src-d/go-git.v4/plumbing"
+ "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/sideband"
"gopkg.in/src-d/go-git.v4/plumbing/transport"
)
@@ -18,7 +19,7 @@ var (
ErrInvalidRefSpec = errors.New("invalid refspec")
)
-// CloneOptions describe how a clone should be perform
+// CloneOptions describes how a clone should be performed
type CloneOptions struct {
// The (possibly remote) repository URL to clone from
URL string
@@ -32,9 +33,13 @@ type CloneOptions struct {
SingleBranch bool
// Limit fetching to the specified number of commits
Depth int
+ // 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
+ Progress sideband.Progress
}
-// Validate validate the fields and set the default values
+// Validate validates the fields and sets the default values
func (o *CloneOptions) Validate() error {
if o.URL == "" {
return ErrMissingURL
@@ -51,7 +56,7 @@ func (o *CloneOptions) Validate() error {
return nil
}
-// PullOptions describe how a pull should be perform.
+// PullOptions describes how a pull should be performed
type PullOptions struct {
// Name of the remote to be pulled. If empty, uses the default.
RemoteName string
@@ -63,9 +68,13 @@ type PullOptions struct {
Depth int
// Auth credentials, if required, to use with the remote repository
Auth transport.AuthMethod
+ // 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
+ Progress sideband.Progress
}
-// Validate validate the fields and set the default values.
+// Validate validates the fields and sets the default values.
func (o *PullOptions) Validate() error {
if o.RemoteName == "" {
o.RemoteName = DefaultRemoteName
@@ -78,7 +87,7 @@ func (o *PullOptions) Validate() error {
return nil
}
-// FetchOptions describe how a fetch should be perform
+// FetchOptions describes how a fetch should be performed
type FetchOptions struct {
// Name of the remote to fetch from. Defaults to origin.
RemoteName string
@@ -88,9 +97,13 @@ type FetchOptions struct {
Depth int
// Auth credentials, if required, to use with the remote repository
Auth transport.AuthMethod
+ // 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
+ Progress sideband.Progress
}
-// Validate validate the fields and set the default values
+// Validate validates the fields and sets the default values
func (o *FetchOptions) Validate() error {
if o.RemoteName == "" {
o.RemoteName = DefaultRemoteName
@@ -105,7 +118,7 @@ func (o *FetchOptions) Validate() error {
return nil
}
-// PushOptions describe how a push should be performed.
+// PushOptions describes how a push should be performed
type PushOptions struct {
// RemoteName is the name of the remote to be pushed to.
RemoteName string
@@ -116,7 +129,7 @@ type PushOptions struct {
Auth transport.AuthMethod
}
-// Validate validate the fields and set the default values
+// Validate validates the fields and sets the default values
func (o *PushOptions) Validate() error {
if o.RemoteName == "" {
o.RemoteName = DefaultRemoteName
diff --git a/plumbing/protocol/packp/sideband/demux.go b/plumbing/protocol/packp/sideband/demux.go
index 6470380..e16c497 100644
--- a/plumbing/protocol/packp/sideband/demux.go
+++ b/plumbing/protocol/packp/sideband/demux.go
@@ -11,9 +11,8 @@ import (
// ErrMaxPackedExceeded returned by Read, if the maximum packed size is exceeded
var ErrMaxPackedExceeded = errors.New("max. packed size exceeded")
-// Progress allows to read the progress information
+// Progress where the progress information is stored
type Progress interface {
- io.Reader
io.Writer
}
diff --git a/plumbing/protocol/packp/sideband/demux_test.go b/plumbing/protocol/packp/sideband/demux_test.go
index ee5f19a..3d2d6ed 100644
--- a/plumbing/protocol/packp/sideband/demux_test.go
+++ b/plumbing/protocol/packp/sideband/demux_test.go
@@ -84,23 +84,24 @@ func (s *SidebandSuite) TestDecodeFromFailingReader(c *C) {
func (s *SidebandSuite) TestDecodeWithProgress(c *C) {
expected := []byte("abcdefghijklmnopqrstuvwxyz")
- buf := bytes.NewBuffer(nil)
- e := pktline.NewEncoder(buf)
+ input := bytes.NewBuffer(nil)
+ e := pktline.NewEncoder(input)
e.Encode(PackData.WithPayload(expected[0:8]))
e.Encode(ProgressMessage.WithPayload([]byte{'F', 'O', 'O', '\n'}))
e.Encode(PackData.WithPayload(expected[8:16]))
e.Encode(PackData.WithPayload(expected[16:26]))
+ output := bytes.NewBuffer(nil)
content := make([]byte, 26)
- d := NewDemuxer(Sideband64k, buf)
- d.Progress = bytes.NewBuffer(nil)
+ d := NewDemuxer(Sideband64k, input)
+ d.Progress = output
n, err := io.ReadFull(d, content)
c.Assert(err, IsNil)
c.Assert(n, Equals, 26)
c.Assert(content, DeepEquals, expected)
- progress, err := ioutil.ReadAll(d.Progress)
+ progress, err := ioutil.ReadAll(output)
c.Assert(err, IsNil)
c.Assert(progress, DeepEquals, []byte{'F', 'O', 'O', '\n'})
}
diff --git a/remote.go b/remote.go
index a071b07..8fa6378 100644
--- a/remote.go
+++ b/remote.go
@@ -26,11 +26,10 @@ var NoErrAlreadyUpToDate = errors.New("already up-to-date")
type Remote struct {
c *config.RemoteConfig
s Storer
- p sideband.Progress
}
-func newRemote(s Storer, p sideband.Progress, c *config.RemoteConfig) *Remote {
- return &Remote{s: s, p: p, c: c}
+func newRemote(s Storer, c *config.RemoteConfig) *Remote {
+ return &Remote{s: s, c: c}
}
// Config return the config
@@ -59,6 +58,7 @@ func (r *Remote) Fetch(o *FetchOptions) error {
// TODO: Support deletes.
// TODO: Support pushing tags.
// TODO: Check if force update is given, otherwise reject non-fast forward.
+// TODO: Sideband support
func (r *Remote) Push(o *PushOptions) (err error) {
if o.RemoteName == "" {
o.RemoteName = r.c.Name
@@ -222,7 +222,7 @@ func (r *Remote) fetchPack(o *FetchOptions, s transport.UploadPackSession,
}
if err = packfile.UpdateObjectStorage(r.s,
- buildSidebandIfSupported(req.Capabilities, reader, r.p),
+ buildSidebandIfSupported(req.Capabilities, reader, o.Progress),
); err != nil {
return err
}
@@ -400,7 +400,7 @@ func (r *Remote) newUploadPackRequest(o *FetchOptions,
}
}
- if r.p == nil && ar.Capabilities.Supports(capability.NoProgress) {
+ if o.Progress == nil && ar.Capabilities.Supports(capability.NoProgress) {
if err := req.Capabilities.Set(capability.NoProgress); err != nil {
return nil, err
}
diff --git a/remote_test.go b/remote_test.go
index 626aece..0fb1dc0 100644
--- a/remote_test.go
+++ b/remote_test.go
@@ -26,25 +26,25 @@ type RemoteSuite struct {
var _ = Suite(&RemoteSuite{})
func (s *RemoteSuite) TestFetchInvalidEndpoint(c *C) {
- r := newRemote(nil, nil, &config.RemoteConfig{Name: "foo", URL: "qux"})
+ r := newRemote(nil, &config.RemoteConfig{Name: "foo", URL: "qux"})
err := r.Fetch(&FetchOptions{})
c.Assert(err, ErrorMatches, ".*invalid endpoint.*")
}
func (s *RemoteSuite) TestFetchNonExistentEndpoint(c *C) {
- r := newRemote(nil, nil, &config.RemoteConfig{Name: "foo", URL: "ssh://non-existent/foo.git"})
+ r := newRemote(nil, &config.RemoteConfig{Name: "foo", URL: "ssh://non-existent/foo.git"})
err := r.Fetch(&FetchOptions{})
c.Assert(err, NotNil)
}
func (s *RemoteSuite) TestFetchInvalidSchemaEndpoint(c *C) {
- r := newRemote(nil, nil, &config.RemoteConfig{Name: "foo", URL: "qux://foo"})
+ r := newRemote(nil, &config.RemoteConfig{Name: "foo", URL: "qux://foo"})
err := r.Fetch(&FetchOptions{})
c.Assert(err, ErrorMatches, ".*unsupported scheme.*")
}
func (s *RemoteSuite) TestFetchInvalidFetchOptions(c *C) {
- r := newRemote(nil, nil, &config.RemoteConfig{Name: "foo", URL: "qux://foo"})
+ r := newRemote(nil, &config.RemoteConfig{Name: "foo", URL: "qux://foo"})
invalid := config.RefSpec("^*$ñ")
err := r.Fetch(&FetchOptions{RefSpecs: []config.RefSpec{invalid}})
c.Assert(err, Equals, ErrInvalidRefSpec)
@@ -53,7 +53,7 @@ func (s *RemoteSuite) TestFetchInvalidFetchOptions(c *C) {
func (s *RemoteSuite) TestFetch(c *C) {
url := s.GetBasicLocalRepositoryURL()
sto := memory.NewStorage()
- r := newRemote(sto, nil, &config.RemoteConfig{Name: "foo", URL: url})
+ r := newRemote(sto, &config.RemoteConfig{Name: "foo", URL: url})
refspec := config.RefSpec("+refs/heads/*:refs/remotes/origin/*")
err := r.Fetch(&FetchOptions{
@@ -77,7 +77,7 @@ func (s *RemoteSuite) TestFetch(c *C) {
func (s *RemoteSuite) TestFetchDepth(c *C) {
url := s.GetBasicLocalRepositoryURL()
sto := memory.NewStorage()
- r := newRemote(sto, nil, &config.RemoteConfig{Name: "foo", URL: url})
+ r := newRemote(sto, &config.RemoteConfig{Name: "foo", URL: url})
refspec := config.RefSpec("+refs/heads/*:refs/remotes/origin/*")
err := r.Fetch(&FetchOptions{
@@ -112,11 +112,12 @@ func (s *RemoteSuite) TestFetchWithProgress(c *C) {
sto := memory.NewStorage()
buf := bytes.NewBuffer(nil)
- r := newRemote(sto, buf, &config.RemoteConfig{Name: "foo", URL: url})
+ r := newRemote(sto, &config.RemoteConfig{Name: "foo", URL: url})
refspec := config.RefSpec("+refs/heads/*:refs/remotes/origin/*")
err := r.Fetch(&FetchOptions{
RefSpecs: []config.RefSpec{refspec},
+ Progress: buf,
})
c.Assert(err, IsNil)
@@ -147,7 +148,7 @@ func (s *RemoteSuite) TestFetchWithPackfileWriter(c *C) {
mock := &mockPackfileWriter{Storer: fss}
url := s.GetBasicLocalRepositoryURL()
- r := newRemote(mock, nil, &config.RemoteConfig{Name: "foo", URL: url})
+ r := newRemote(mock, &config.RemoteConfig{Name: "foo", URL: url})
refspec := config.RefSpec("+refs/heads/*:refs/remotes/origin/*")
err = r.Fetch(&FetchOptions{
@@ -183,7 +184,7 @@ func (s *RemoteSuite) TestFetchNoErrAlreadyUpToDateWithNonCommitObjects(c *C) {
func (s *RemoteSuite) doTestFetchNoErrAlreadyUpToDate(c *C, url string) {
sto := memory.NewStorage()
- r := newRemote(sto, nil, &config.RemoteConfig{Name: "foo", URL: url})
+ r := newRemote(sto, &config.RemoteConfig{Name: "foo", URL: url})
refspec := config.RefSpec("+refs/heads/*:refs/remotes/origin/*")
o := &FetchOptions{
@@ -197,7 +198,7 @@ func (s *RemoteSuite) doTestFetchNoErrAlreadyUpToDate(c *C, url string) {
}
func (s *RemoteSuite) TestString(c *C) {
- r := newRemote(nil, nil, &config.RemoteConfig{
+ r := newRemote(nil, &config.RemoteConfig{
Name: "foo",
URL: "https://github.com/git-fixtures/basic.git",
})
@@ -216,7 +217,7 @@ func (s *RemoteSuite) TestPushToEmptyRepository(c *C) {
dstFs := fixtures.ByTag("empty").One().DotGit()
url := fmt.Sprintf("file://%s", dstFs.Base())
- r := newRemote(sto, nil, &config.RemoteConfig{
+ r := newRemote(sto, &config.RemoteConfig{
Name: DefaultRemoteName,
URL: url,
})
@@ -253,7 +254,7 @@ func (s *RemoteSuite) TestPushNoErrAlreadyUpToDate(c *C) {
sto, err := filesystem.NewStorage(f.DotGit())
c.Assert(err, IsNil)
url := fmt.Sprintf("file://%s", f.DotGit().Base())
- r := newRemote(sto, nil, &config.RemoteConfig{
+ r := newRemote(sto, &config.RemoteConfig{
Name: DefaultRemoteName,
URL: url,
})
@@ -266,32 +267,32 @@ func (s *RemoteSuite) TestPushNoErrAlreadyUpToDate(c *C) {
}
func (s *RemoteSuite) TestPushInvalidEndpoint(c *C) {
- r := newRemote(nil, nil, &config.RemoteConfig{Name: "foo", URL: "qux"})
+ r := newRemote(nil, &config.RemoteConfig{Name: "foo", URL: "qux"})
err := r.Push(&PushOptions{})
c.Assert(err, ErrorMatches, ".*invalid endpoint.*")
}
func (s *RemoteSuite) TestPushNonExistentEndpoint(c *C) {
- r := newRemote(nil, nil, &config.RemoteConfig{Name: "foo", URL: "ssh://non-existent/foo.git"})
+ r := newRemote(nil, &config.RemoteConfig{Name: "foo", URL: "ssh://non-existent/foo.git"})
err := r.Push(&PushOptions{})
c.Assert(err, NotNil)
}
func (s *RemoteSuite) TestPushInvalidSchemaEndpoint(c *C) {
- r := newRemote(nil, nil, &config.RemoteConfig{Name: "foo", URL: "qux://foo"})
+ r := newRemote(nil, &config.RemoteConfig{Name: "foo", URL: "qux://foo"})
err := r.Push(&PushOptions{})
c.Assert(err, ErrorMatches, ".*unsupported scheme.*")
}
func (s *RemoteSuite) TestPushInvalidFetchOptions(c *C) {
- r := newRemote(nil, nil, &config.RemoteConfig{Name: "foo", URL: "qux://foo"})
+ r := newRemote(nil, &config.RemoteConfig{Name: "foo", URL: "qux://foo"})
invalid := config.RefSpec("^*$ñ")
err := r.Push(&PushOptions{RefSpecs: []config.RefSpec{invalid}})
c.Assert(err, Equals, ErrInvalidRefSpec)
}
func (s *RemoteSuite) TestPushInvalidRefSpec(c *C) {
- r := newRemote(nil, nil, &config.RemoteConfig{
+ r := newRemote(nil, &config.RemoteConfig{
Name: DefaultRemoteName,
URL: "file:///some-url",
})
@@ -304,7 +305,7 @@ func (s *RemoteSuite) TestPushInvalidRefSpec(c *C) {
}
func (s *RemoteSuite) TestPushWrongRemoteName(c *C) {
- r := newRemote(nil, nil, &config.RemoteConfig{
+ r := newRemote(nil, &config.RemoteConfig{
Name: DefaultRemoteName,
URL: "file:///some-url",
})
diff --git a/repository.go b/repository.go
index 1d69fe8..56fbc3c 100644
--- a/repository.go
+++ b/repository.go
@@ -8,7 +8,6 @@ import (
"gopkg.in/src-d/go-git.v4/config"
"gopkg.in/src-d/go-git.v4/plumbing"
"gopkg.in/src-d/go-git.v4/plumbing/object"
- "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/sideband"
"gopkg.in/src-d/go-git.v4/plumbing/storer"
"gopkg.in/src-d/go-git.v4/storage/filesystem"
@@ -32,11 +31,6 @@ type Repository struct {
r map[string]*Remote
s Storer
wt billy.Filesystem
-
- // 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
- Progress sideband.Progress
}
// Init creates an empty git repository, based on the given Storer and worktree.
@@ -188,7 +182,7 @@ func (r *Repository) Remote(name string) (*Remote, error) {
return nil, ErrRemoteNotFound
}
- return newRemote(r.s, r.Progress, c), nil
+ return newRemote(r.s, c), nil
}
// Remotes return all the remotes
@@ -202,7 +196,7 @@ func (r *Repository) Remotes() ([]*Remote, error) {
var i int
for _, c := range cfg.Remotes {
- remotes[i] = newRemote(r.s, r.Progress, c)
+ remotes[i] = newRemote(r.s, c)
i++
}
@@ -215,7 +209,7 @@ func (r *Repository) CreateRemote(c *config.RemoteConfig) (*Remote, error) {
return nil, err
}
- remote := newRemote(r.s, r.Progress, c)
+ remote := newRemote(r.s, c)
cfg, err := r.s.Config()
if err != nil {
@@ -271,6 +265,7 @@ func (r *Repository) clone(o *CloneOptions) error {
RefSpecs: r.cloneRefSpec(o, c),
Depth: o.Depth,
Auth: o.Auth,
+ Progress: o.Progress,
})
if err != nil {
return err
@@ -435,8 +430,9 @@ func (r *Repository) Pull(o *PullOptions) error {
}
remoteRefs, err := remote.fetch(&FetchOptions{
- Depth: o.Depth,
- Auth: o.Auth,
+ Depth: o.Depth,
+ Auth: o.Auth,
+ Progress: o.Progress,
})
updated := true
@@ -515,7 +511,7 @@ func (r *Repository) Push(o *PushOptions) error {
return remote.Push(o)
}
-// object.Commit return the commit with the given hash
+// Commit return the commit with the given hash
func (r *Repository) Commit(h plumbing.Hash) (*object.Commit, error) {
return object.GetCommit(r.s, h)
}
diff --git a/repository_test.go b/repository_test.go
index 7d44a30..1c43c6e 100644
--- a/repository_test.go
+++ b/repository_test.go
@@ -129,21 +129,6 @@ func (s *RepositorySuite) TestCreateRemoteAndRemote(c *C) {
c.Assert(alt.Config().Name, Equals, "foo")
}
-func (s *RepositorySuite) TestRemoteWithProgress(c *C) {
- buf := bytes.NewBuffer(nil)
-
- r, _ := Init(memory.NewStorage(), nil)
- r.Progress = buf
-
- remote, err := r.CreateRemote(&config.RemoteConfig{
- Name: "foo",
- URL: "http://foo/foo.git",
- })
-
- c.Assert(err, IsNil)
- c.Assert(remote.p, Equals, buf)
-}
-
func (s *RepositorySuite) TestCreateRemoteInvalid(c *C) {
r, _ := Init(memory.NewStorage(), nil)
remote, err := r.CreateRemote(&config.RemoteConfig{})
@@ -449,6 +434,19 @@ func (s *RepositorySuite) TestPullCheckout(c *C) {
c.Assert(fi, HasLen, 8)
}
+func (s *RepositorySuite) TestCloneWithProgress(c *C) {
+ fs := memoryfs.New()
+
+ buf := bytes.NewBuffer(nil)
+ _, err := Clone(memory.NewStorage(), fs, &CloneOptions{
+ URL: s.GetBasicLocalRepositoryURL(),
+ Progress: buf,
+ })
+
+ c.Assert(err, IsNil)
+ c.Assert(buf.Len(), Not(Equals), 0)
+}
+
func (s *RepositorySuite) TestPullUpdateReferencesIfNeeded(c *C) {
r, _ := Init(memory.NewStorage(), nil)
r.CreateRemote(&config.RemoteConfig{
@@ -500,6 +498,23 @@ func (s *RepositorySuite) TestPullSingleBranch(c *C) {
c.Assert(storage.Objects, HasLen, 28)
}
+func (s *RepositorySuite) TestPullProgress(c *C) {
+ r, _ := Init(memory.NewStorage(), nil)
+
+ r.CreateRemote(&config.RemoteConfig{
+ Name: DefaultRemoteName,
+ URL: s.GetBasicLocalRepositoryURL(),
+ })
+
+ buf := bytes.NewBuffer(nil)
+ err := r.Pull(&PullOptions{
+ Progress: buf,
+ })
+
+ c.Assert(err, IsNil)
+ c.Assert(buf.Len(), Not(Equals), 0)
+}
+
func (s *RepositorySuite) TestPullAdd(c *C) {
path := fixtures.Basic().One().Worktree().Base()
diff --git a/storage/filesystem/config.go b/storage/filesystem/config.go
index 07e0433..cad698a 100644
--- a/storage/filesystem/config.go
+++ b/storage/filesystem/config.go
@@ -1,22 +1,13 @@
package filesystem
import (
- "fmt"
+ "io/ioutil"
"os"
"gopkg.in/src-d/go-git.v4/config"
- gitconfig "gopkg.in/src-d/go-git.v4/plumbing/format/config"
"gopkg.in/src-d/go-git.v4/storage/filesystem/internal/dotgit"
)
-const (
- remoteSection = "remote"
- coreSection = "core"
- fetchKey = "fetch"
- urlKey = "url"
- bareKey = "bare"
-)
-
type ConfigStorage struct {
dir *dotgit.DotGit
}
@@ -24,20 +15,6 @@ type ConfigStorage struct {
func (c *ConfigStorage) Config() (*config.Config, error) {
cfg := config.NewConfig()
- ini, err := c.unmarshal()
- if err != nil {
- return nil, err
- }
-
- c.unmarshalCore(cfg, ini)
- c.unmarshalRemotes(cfg, ini)
-
- return cfg, nil
-}
-
-func (c *ConfigStorage) unmarshal() (*gitconfig.Config, error) {
- cfg := gitconfig.New()
-
f, err := c.dir.Config()
if err != nil {
if os.IsNotExist(err) {
@@ -49,43 +26,16 @@ func (c *ConfigStorage) unmarshal() (*gitconfig.Config, error) {
defer f.Close()
- d := gitconfig.NewDecoder(f)
- if err := d.Decode(cfg); err != nil {
+ b, err := ioutil.ReadAll(f)
+ if err != nil {
return nil, err
}
- return cfg, nil
-}
-
-func (c *ConfigStorage) unmarshalCore(cfg *config.Config, ini *gitconfig.Config) {
- s := ini.Section(coreSection)
- if s.Options.Get(bareKey) == "true" {
- cfg.Core.IsBare = true
- }
-}
-
-func (c *ConfigStorage) unmarshalRemotes(cfg *config.Config, ini *gitconfig.Config) {
- s := ini.Section(remoteSection)
- for _, sub := range s.Subsections {
- r := c.unmarshalRemote(sub)
- cfg.Remotes[r.Name] = r
- }
-}
-
-func (c *ConfigStorage) unmarshalRemote(s *gitconfig.Subsection) *config.RemoteConfig {
- fetch := []config.RefSpec{}
- for _, f := range s.Options.GetAll(fetchKey) {
- rs := config.RefSpec(f)
- if rs.IsValid() {
- fetch = append(fetch, rs)
- }
+ if err := cfg.Unmarshal(b); err != nil {
+ return nil, err
}
- return &config.RemoteConfig{
- Name: s.Name,
- URL: s.Option(urlKey),
- Fetch: fetch,
- }
+ return cfg, nil
}
func (c *ConfigStorage) SetConfig(cfg *config.Config) error {
@@ -93,50 +43,18 @@ func (c *ConfigStorage) SetConfig(cfg *config.Config) error {
return err
}
- ini, err := c.unmarshal()
+ f, err := c.dir.ConfigWriter()
if err != nil {
return err
}
- c.marshalCore(cfg, ini)
- c.marshalRemotes(cfg, ini)
- return c.marshal(ini)
-}
-
-func (c *ConfigStorage) marshalCore(cfg *config.Config, ini *gitconfig.Config) {
- s := ini.Section(coreSection)
- s.AddOption(bareKey, fmt.Sprintf("%t", cfg.Core.IsBare))
-}
-
-func (c *ConfigStorage) marshalRemotes(cfg *config.Config, ini *gitconfig.Config) {
- s := ini.Section(remoteSection)
- s.Subsections = make(gitconfig.Subsections, len(cfg.Remotes))
-
- var i int
- for _, r := range cfg.Remotes {
- s.Subsections[i] = c.marshalRemote(r)
- i++
- }
-}
-
-func (c *ConfigStorage) marshalRemote(r *config.RemoteConfig) *gitconfig.Subsection {
- s := &gitconfig.Subsection{Name: r.Name}
- s.AddOption(urlKey, r.URL)
- for _, rs := range r.Fetch {
- s.AddOption(fetchKey, rs.String())
- }
-
- return s
-}
+ defer f.Close()
-func (c *ConfigStorage) marshal(ini *gitconfig.Config) error {
- f, err := c.dir.ConfigWriter()
+ b, err := cfg.Marshal()
if err != nil {
return err
}
- defer f.Close()
-
- e := gitconfig.NewEncoder(f)
- return e.Encode(ini)
+ _, err = f.Write(b)
+ return err
}