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

Commit e2e4e7f

Browse files
committed
config: marshal and unmarshal implementation
1 parent b0a32a7 commit e2e4e7f

File tree

3 files changed

+169
-95
lines changed

3 files changed

+169
-95
lines changed

config/config.go

+111
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22
package config
33

44
import (
5+
"bytes"
56
"errors"
67
"fmt"
8+
9+
"gopkg.in/src-d/go-git.v4/plumbing/format/config"
710
)
811

912
const (
@@ -27,17 +30,24 @@ var (
2730
)
2831

2932
// Config contains the repository configuration
33+
// https://www.kernel.org/pub/software/scm/git/docs/git-config.html
3034
type Config struct {
3135
Core struct {
3236
IsBare bool
3337
}
3438
Remotes map[string]*RemoteConfig
39+
40+
// contains the raw information of a config file, the main goal is preserve
41+
// the parsed information from the original format, to avoid miss not
42+
// supported properties
43+
raw *config.Config
3544
}
3645

3746
// NewConfig returns a new empty Config
3847
func NewConfig() *Config {
3948
return &Config{
4049
Remotes: make(map[string]*RemoteConfig, 0),
50+
raw: config.New(),
4151
}
4252
}
4353

@@ -56,11 +66,82 @@ func (c *Config) Validate() error {
5666
return nil
5767
}
5868

69+
const (
70+
remoteSection = "remote"
71+
coreSection = "core"
72+
fetchKey = "fetch"
73+
urlKey = "url"
74+
bareKey = "bare"
75+
)
76+
77+
// Unmarshal parses a git-config file and stores it
78+
func (c *Config) Unmarshal(b []byte) error {
79+
r := bytes.NewBuffer(b)
80+
d := config.NewDecoder(r)
81+
82+
c.raw = config.New()
83+
if err := d.Decode(c.raw); err != nil {
84+
return err
85+
}
86+
87+
c.unmarshalCore()
88+
c.unmarshalRemotes()
89+
return nil
90+
}
91+
92+
func (c *Config) unmarshalCore() {
93+
s := c.raw.Section(coreSection)
94+
if s.Options.Get(bareKey) == "true" {
95+
c.Core.IsBare = true
96+
}
97+
}
98+
99+
func (c *Config) unmarshalRemotes() {
100+
s := c.raw.Section(remoteSection)
101+
for _, sub := range s.Subsections {
102+
r := &RemoteConfig{}
103+
r.unmarshal(sub)
104+
105+
c.Remotes[r.Name] = r
106+
}
107+
}
108+
109+
// Marshal returns Config encoded as a git-config file
110+
func (c *Config) Marshal() ([]byte, error) {
111+
c.marshalCore()
112+
c.marshalRemotes()
113+
114+
buf := bytes.NewBuffer(nil)
115+
if err := config.NewEncoder(buf).Encode(c.raw); err != nil {
116+
return nil, err
117+
}
118+
119+
return buf.Bytes(), nil
120+
}
121+
122+
func (c *Config) marshalCore() {
123+
s := c.raw.Section(coreSection)
124+
s.SetOption(bareKey, fmt.Sprintf("%t", c.Core.IsBare))
125+
}
126+
127+
func (c *Config) marshalRemotes() {
128+
s := c.raw.Section(remoteSection)
129+
s.Subsections = make(config.Subsections, len(c.Remotes))
130+
131+
var i int
132+
for _, r := range c.Remotes {
133+
s.Subsections[i] = r.marshal()
134+
i++
135+
}
136+
}
137+
59138
// RemoteConfig contains the configuration for a given repository
60139
type RemoteConfig struct {
61140
Name string
62141
URL string
63142
Fetch []RefSpec
143+
144+
raw *config.Subsection
64145
}
65146

66147
// Validate validate the fields and set the default values
@@ -79,3 +160,33 @@ func (c *RemoteConfig) Validate() error {
79160

80161
return nil
81162
}
163+
164+
func (c *RemoteConfig) unmarshal(s *config.Subsection) {
165+
c.raw = s
166+
167+
fetch := []RefSpec{}
168+
for _, f := range c.raw.Options.GetAll(fetchKey) {
169+
rs := RefSpec(f)
170+
if rs.IsValid() {
171+
fetch = append(fetch, rs)
172+
}
173+
}
174+
175+
c.Name = c.raw.Name
176+
c.URL = c.raw.Option(urlKey)
177+
c.Fetch = fetch
178+
}
179+
180+
func (c *RemoteConfig) marshal() *config.Subsection {
181+
if c.raw == nil {
182+
c.raw = &config.Subsection{}
183+
}
184+
185+
c.raw.Name = c.Name
186+
c.raw.SetOption(urlKey, c.URL)
187+
for _, rs := range c.Fetch {
188+
c.raw.SetOption(fetchKey, rs.String())
189+
}
190+
191+
return c.raw
192+
}

config/config_test.go

+46-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,51 @@ type ConfigSuite struct{}
66

77
var _ = Suite(&ConfigSuite{})
88

9-
func (s *ConfigSuite) TestConfigValidateInvalidRemote(c *C) {
9+
func (s *ConfigSuite) TestUnmarshall(c *C) {
10+
input := []byte(`[core]
11+
bare = true
12+
[remote "origin"]
13+
url = [email protected]:mcuadros/go-git.git
14+
fetch = +refs/heads/*:refs/remotes/origin/*
15+
[branch "master"]
16+
remote = origin
17+
merge = refs/heads/master
18+
`)
19+
20+
cfg := NewConfig()
21+
err := cfg.Unmarshal(input)
22+
c.Assert(err, IsNil)
23+
24+
c.Assert(cfg.Core.IsBare, Equals, true)
25+
c.Assert(cfg.Remotes, HasLen, 1)
26+
c.Assert(cfg.Remotes["origin"].Name, Equals, "origin")
27+
c.Assert(cfg.Remotes["origin"].URL, Equals, "[email protected]:mcuadros/go-git.git")
28+
c.Assert(cfg.Remotes["origin"].Fetch, DeepEquals, []RefSpec{"+refs/heads/*:refs/remotes/origin/*"})
29+
}
30+
31+
func (s *ConfigSuite) TestUnmarshallMarshall(c *C) {
32+
input := []byte(`[core]
33+
bare = true
34+
custom = ignored
35+
[remote "origin"]
36+
url = [email protected]:mcuadros/go-git.git
37+
fetch = +refs/heads/*:refs/remotes/origin/*
38+
mirror = true
39+
[branch "master"]
40+
remote = origin
41+
merge = refs/heads/master
42+
`)
43+
44+
cfg := NewConfig()
45+
err := cfg.Unmarshal(input)
46+
c.Assert(err, IsNil)
47+
48+
output, err := cfg.Marshal()
49+
c.Assert(err, IsNil)
50+
c.Assert(output, DeepEquals, input)
51+
}
52+
53+
func (s *ConfigSuite) TestValidateInvalidRemote(c *C) {
1054
config := &Config{
1155
Remotes: map[string]*RemoteConfig{
1256
"foo": {Name: "foo"},
@@ -16,7 +60,7 @@ func (s *ConfigSuite) TestConfigValidateInvalidRemote(c *C) {
1660
c.Assert(config.Validate(), Equals, ErrRemoteConfigEmptyURL)
1761
}
1862

19-
func (s *ConfigSuite) TestConfigValidateInvalidKey(c *C) {
63+
func (s *ConfigSuite) TestValidateInvalidKey(c *C) {
2064
config := &Config{
2165
Remotes: map[string]*RemoteConfig{
2266
"bar": {Name: "foo"},

storage/filesystem/config.go

+12-93
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,21 @@
11
package filesystem
22

33
import (
4-
"fmt"
54
"os"
65

6+
"io/ioutil"
7+
78
"gopkg.in/src-d/go-git.v4/config"
8-
gitconfig "gopkg.in/src-d/go-git.v4/plumbing/format/config"
99
"gopkg.in/src-d/go-git.v4/storage/filesystem/internal/dotgit"
1010
)
1111

12-
const (
13-
remoteSection = "remote"
14-
coreSection = "core"
15-
fetchKey = "fetch"
16-
urlKey = "url"
17-
bareKey = "bare"
18-
)
19-
2012
type ConfigStorage struct {
2113
dir *dotgit.DotGit
2214
}
2315

2416
func (c *ConfigStorage) Config() (*config.Config, error) {
2517
cfg := config.NewConfig()
2618

27-
ini, err := c.unmarshal()
28-
if err != nil {
29-
return nil, err
30-
}
31-
32-
c.unmarshalCore(cfg, ini)
33-
c.unmarshalRemotes(cfg, ini)
34-
35-
return cfg, nil
36-
}
37-
38-
func (c *ConfigStorage) unmarshal() (*gitconfig.Config, error) {
39-
cfg := gitconfig.New()
40-
4119
f, err := c.dir.Config()
4220
if err != nil {
4321
if os.IsNotExist(err) {
@@ -49,94 +27,35 @@ func (c *ConfigStorage) unmarshal() (*gitconfig.Config, error) {
4927

5028
defer f.Close()
5129

52-
d := gitconfig.NewDecoder(f)
53-
if err := d.Decode(cfg); err != nil {
30+
b, err := ioutil.ReadAll(f)
31+
if err != nil {
5432
return nil, err
5533
}
5634

57-
return cfg, nil
58-
}
59-
60-
func (c *ConfigStorage) unmarshalCore(cfg *config.Config, ini *gitconfig.Config) {
61-
s := ini.Section(coreSection)
62-
if s.Options.Get(bareKey) == "true" {
63-
cfg.Core.IsBare = true
64-
}
65-
}
66-
67-
func (c *ConfigStorage) unmarshalRemotes(cfg *config.Config, ini *gitconfig.Config) {
68-
s := ini.Section(remoteSection)
69-
for _, sub := range s.Subsections {
70-
r := c.unmarshalRemote(sub)
71-
cfg.Remotes[r.Name] = r
72-
}
73-
}
74-
75-
func (c *ConfigStorage) unmarshalRemote(s *gitconfig.Subsection) *config.RemoteConfig {
76-
fetch := []config.RefSpec{}
77-
for _, f := range s.Options.GetAll(fetchKey) {
78-
rs := config.RefSpec(f)
79-
if rs.IsValid() {
80-
fetch = append(fetch, rs)
81-
}
35+
if err := cfg.Unmarshal(b); err != nil {
36+
return nil, err
8237
}
8338

84-
return &config.RemoteConfig{
85-
Name: s.Name,
86-
URL: s.Option(urlKey),
87-
Fetch: fetch,
88-
}
39+
return cfg, nil
8940
}
9041

9142
func (c *ConfigStorage) SetConfig(cfg *config.Config) error {
9243
if err := cfg.Validate(); err != nil {
9344
return err
9445
}
9546

96-
ini, err := c.unmarshal()
47+
f, err := c.dir.ConfigWriter()
9748
if err != nil {
9849
return err
9950
}
10051

101-
c.marshalCore(cfg, ini)
102-
c.marshalRemotes(cfg, ini)
103-
return c.marshal(ini)
104-
}
105-
106-
func (c *ConfigStorage) marshalCore(cfg *config.Config, ini *gitconfig.Config) {
107-
s := ini.Section(coreSection)
108-
s.AddOption(bareKey, fmt.Sprintf("%t", cfg.Core.IsBare))
109-
}
110-
111-
func (c *ConfigStorage) marshalRemotes(cfg *config.Config, ini *gitconfig.Config) {
112-
s := ini.Section(remoteSection)
113-
s.Subsections = make(gitconfig.Subsections, len(cfg.Remotes))
114-
115-
var i int
116-
for _, r := range cfg.Remotes {
117-
s.Subsections[i] = c.marshalRemote(r)
118-
i++
119-
}
120-
}
121-
122-
func (c *ConfigStorage) marshalRemote(r *config.RemoteConfig) *gitconfig.Subsection {
123-
s := &gitconfig.Subsection{Name: r.Name}
124-
s.AddOption(urlKey, r.URL)
125-
for _, rs := range r.Fetch {
126-
s.AddOption(fetchKey, rs.String())
127-
}
128-
129-
return s
130-
}
52+
defer f.Close()
13153

132-
func (c *ConfigStorage) marshal(ini *gitconfig.Config) error {
133-
f, err := c.dir.ConfigWriter()
54+
b, err := cfg.Marshal()
13455
if err != nil {
13556
return err
13657
}
13758

138-
defer f.Close()
139-
140-
e := gitconfig.NewEncoder(f)
141-
return e.Encode(ini)
59+
_, err = f.Write(b)
60+
return err
14261
}

0 commit comments

Comments
 (0)