From 61f0188edcea55dbcfa1c3a35da0c34fed10fd54 Mon Sep 17 00:00:00 2001 From: "Santiago M. Mola" Date: Wed, 26 Oct 2016 18:41:39 +0200 Subject: formats/config: Added encoder/decoder for git config files. (#97) * WIP: Add config format parser. * add decoder based on gcfg. Portions of code taken from: https://github.com/go-gcfg/gcfg/blob/5b9f94ee80b2331c3982477bd84be8edd857df33/read.go * add git config encoder and config methods. * use format/config in storage/filesystem for read * use format/config in storage/filesystem to write * formats/config: improve docs. * formats/config: improve tests. * formats/config: use our fork of gcfg; improve api. * formats/config: improve api. * storage/filesystem: fix gofmt * formats/config: use NoSubsection constant. * formats/config: add doc.go * formats/config: requested sytle changes. * formats/config: do not use *_test packages. --- storage/filesystem/config.go | 103 ++++++++++++++++++++++++++++---------- storage/filesystem/config_test.go | 47 +++++++---------- 2 files changed, 96 insertions(+), 54 deletions(-) (limited to 'storage/filesystem') diff --git a/storage/filesystem/config.go b/storage/filesystem/config.go index 1cff05a..84252eb 100644 --- a/storage/filesystem/config.go +++ b/storage/filesystem/config.go @@ -1,58 +1,79 @@ package filesystem import ( - "fmt" - "io" - - "gopkg.in/gcfg.v1" "gopkg.in/src-d/go-git.v4/config" + gitconfig "gopkg.in/src-d/go-git.v4/formats/config" "gopkg.in/src-d/go-git.v4/storage/filesystem/internal/dotgit" ) +const ( + remoteSection = "remote" + fetchKey = "fetch" + urlKey = "url" +) + type ConfigStorage struct { dir *dotgit.DotGit } func (c *ConfigStorage) Remote(name string) (*config.RemoteConfig, error) { - file, err := c.read() + cfg, err := c.read() if err != nil { return nil, err } - r, ok := file.Remotes[name] - if ok { - return r, nil + s := cfg.Section(remoteSection).Subsection(name) + if s == nil { + return nil, config.ErrRemoteConfigNotFound } - return nil, config.ErrRemoteConfigNotFound + return parseRemote(s), nil } func (c *ConfigStorage) Remotes() ([]*config.RemoteConfig, error) { - file, err := c.read() + cfg, err := c.read() if err != nil { return nil, err } - remotes := make([]*config.RemoteConfig, len(file.Remotes)) - - var i int - for _, r := range file.Remotes { - remotes[i] = r + remotes := []*config.RemoteConfig{} + sect := cfg.Section(remoteSection) + for _, s := range sect.Subsections { + remotes = append(remotes, parseRemote(s)) } return remotes, nil } func (c *ConfigStorage) SetRemote(r *config.RemoteConfig) error { - return nil - return fmt.Errorf("set remote - not implemented yet") + cfg, err := c.read() + if err != nil { + return err + } + + s := cfg.Section(remoteSection).Subsection(r.Name) + s.Name = r.Name + s.SetOption(urlKey, r.URL) + s.RemoveOption(fetchKey) + for _, rs := range r.Fetch { + s.AddOption(fetchKey, rs.String()) + } + + return c.write(cfg) } func (c *ConfigStorage) DeleteRemote(name string) error { - return fmt.Errorf("delete - remote not implemented yet") + cfg, err := c.read() + if err != nil { + return err + } + + cfg = cfg.RemoveSubsection(remoteSection, name) + + return c.write(cfg) } -func (c *ConfigStorage) read() (*ConfigFile, error) { +func (c *ConfigStorage) read() (*gitconfig.Config, error) { f, err := c.dir.Config() if err != nil { return nil, err @@ -60,15 +81,45 @@ func (c *ConfigStorage) read() (*ConfigFile, error) { defer f.Close() - config := &ConfigFile{} - return config, config.Decode(f) + cfg := gitconfig.New() + d := gitconfig.NewDecoder(f) + err = d.Decode(cfg) + if err != nil { + return nil, err + } + + return cfg, nil } -type ConfigFile struct { - Remotes map[string]*config.RemoteConfig `gcfg:"remote"` +func (c *ConfigStorage) write(cfg *gitconfig.Config) error { + f, err := c.dir.Config() + if err != nil { + return err + } + + defer f.Close() + + e := gitconfig.NewEncoder(f) + err = e.Encode(cfg) + if err != nil { + return err + } + + return nil } -// Decode decode a git config file intro the ConfigStore -func (c *ConfigFile) Decode(r io.Reader) error { - return gcfg.FatalOnly(gcfg.ReadInto(c, r)) +func parseRemote(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) + } + } + + return &config.RemoteConfig{ + Name: s.Name, + URL: s.Option(urlKey), + Fetch: fetch, + } } diff --git a/storage/filesystem/config_test.go b/storage/filesystem/config_test.go index cbff1e0..20af595 100644 --- a/storage/filesystem/config_test.go +++ b/storage/filesystem/config_test.go @@ -1,7 +1,7 @@ package filesystem import ( - "bytes" + "gopkg.in/src-d/go-git.v4/formats/config" . "gopkg.in/check.v1" ) @@ -10,31 +10,22 @@ type ConfigSuite struct{} var _ = Suite(&ConfigSuite{}) -func (s *ConfigSuite) TestConfigFileDecode(c *C) { - config := &ConfigFile{} - - err := config.Decode(bytes.NewBuffer(configFixture)) - c.Assert(err, IsNil) - - c.Assert(config.Remotes, HasLen, 2) - c.Assert(config.Remotes["origin"].URL, Equals, "git@github.com:src-d/go-git.git") - c.Assert(config.Remotes["origin"].Fetch, HasLen, 1) - c.Assert(config.Remotes["origin"].Fetch[0].String(), Equals, "+refs/heads/*:refs/remotes/origin/*") +func (s *ConfigSuite) TestParseRemote(c *C) { + remote := parseRemote(&config.Subsection{ + Name: "origin", + Options: []*config.Option{ + { + Key: "url", + Value: "git@github.com:src-d/go-git.git", + }, + { + Key: "fetch", + Value: "+refs/heads/*:refs/remotes/origin/*", + }, + }, + }) + + c.Assert(remote.URL, Equals, "git@github.com:src-d/go-git.git") + c.Assert(remote.Fetch, HasLen, 1) + c.Assert(remote.Fetch[0].String(), Equals, "+refs/heads/*:refs/remotes/origin/*") } - -var configFixture = []byte(` -[core] - repositoryformatversion = 0 - filemode = true - bare = false - logallrefupdates = true -[remote "origin"] - url = git@github.com:src-d/go-git.git - fetch = +refs/heads/*:refs/remotes/origin/* -[branch "v4"] - remote = origin - merge = refs/heads/v4 -[remote "mcuadros"] - url = git@github.com:mcuadros/go-git.git - fetch = +refs/heads/*:refs/remotes/mcuadros/* -`) -- cgit