aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMáximo Cuadros <mcuadros@gmail.com>2020-05-24 16:53:30 +0200
committerGitHub <noreply@github.com>2020-05-24 16:53:30 +0200
commite7f544844d6d736acfd9d75ee0d4a9d37f450103 (patch)
treedc44247231e6603bf10acaee91af5523120b4b84
parent6d8103df45ce09ffd5323b4ef46d26440400a54f (diff)
parentbaf8c2761217cd457ef672972d5c1fb4d066e95a (diff)
downloadgo-git-e7f544844d6d736acfd9d75ee0d4a9d37f450103.tar.gz
Merge pull request #75 from mcuadros/scope-config
repository.ConfigScoped and worktree.Commit with empty CommitOptions
-rw-r--r--_examples/README.md35
-rw-r--r--_examples/commit/main.go3
-rw-r--r--_examples/config/main.go73
-rw-r--r--config/config.go206
-rw-r--r--config/config_test.go204
-rw-r--r--go.mod1
-rw-r--r--go.sum2
-rw-r--r--options.go44
-rw-r--r--options_test.go8
-rw-r--r--plumbing/format/config/merged.go261
-rw-r--r--repository.go69
-rw-r--r--repository_test.go15
-rw-r--r--storage/filesystem/config.go67
-rw-r--r--storage/filesystem/dotgit/dotgit.go50
-rw-r--r--storage/filesystem/dotgit/dotgit_test.go6
-rw-r--r--submodule.go2
-rw-r--r--worktree_commit_test.go12
17 files changed, 376 insertions, 682 deletions
diff --git a/_examples/README.md b/_examples/README.md
index 6c17941..1d82fbd 100644
--- a/_examples/README.md
+++ b/_examples/README.md
@@ -3,26 +3,27 @@
Here you can find a list of annotated _go-git_ examples:
### Basic
-- [showcase](showcase/main.go) - A small showcase of the capabilities of _go-git_
-- [open](open/main.go) - Opening a existing repository cloned by _git_
-- [clone](clone/main.go) - Cloning a repository
+- [showcase](showcase/main.go) - A small showcase of the capabilities of _go-git_.
+- [open](open/main.go) - Opening a existing repository cloned by _git_.
+- [clone](clone/main.go) - Cloning a repository.
- [username and password](clone/auth/basic/username_password/main.go) - Cloning a repository
- using a username and password
+ using a username and password.
- [personal access token](clone/auth/basic/access_token/main.go) - Cloning
- a repository using a GitHub personal access token
-- [commit](commit/main.go) - Commit changes to the current branch to an existent repository
-- [push](push/main.go) - Push repository to default remote (origin)
-- [pull](pull/main.go) - Pull changes from a remote repository
-- [checkout](checkout/main.go) - Check out a specific commit from a repository
-- [log](log/main.go) - Emulate `git log` command output iterating all the commit history from HEAD reference
+ a repository using a GitHub personal access token.
+- [commit](commit/main.go) - Commit changes to the current branch to an existent repository.
+- [push](push/main.go) - Push repository to default remote (origin).
+- [pull](pull/main.go) - Pull changes from a remote repository.
+- [checkout](checkout/main.go) - Check out a specific commit from a repository.
+- [log](log/main.go) - Emulate `git log` command output iterating all the commit history from HEAD reference.
- [branch](branch/main.go) - How to create and remove branches or any other kind of reference.
-- [tag](tag/main.go) - List/print repository tags
-- [remotes](remotes/main.go) - Working with remotes: adding, removing, etc
-- [progress](progress/main.go) - Printing the progress information from the sideband
-- [revision](revision/main.go) - Solve a revision into a commit
-- [submodule](submodule/main.go) - Submodule update remote
+- [tag](tag/main.go) - List/print repository tags.
+- [remotes](remotes/main.go) - Working with remotes: adding, removing, etc.
+- [progress](progress/main.go) - Printing the progress information from the sideband.
+- [revision](revision/main.go) - Solve a revision into a commit.
+- [config](config/main.go) - Explains how to work with config files.
+- [submodule](submodule/main.go) - Submodule update remote.
### Advanced
-- [custom_http](custom_http/main.go) - Replacing the HTTP client using a custom one
+- [custom_http](custom_http/main.go) - Replacing the HTTP client using a custom one.
- [clone with context](context/main.go) - Cloning a repository with graceful cancellation.
-- [storage](storage/README.md) - Implementing a custom storage system
+- [storage](storage/README.md) - Implementing a custom storage system.
diff --git a/_examples/commit/main.go b/_examples/commit/main.go
index c83dc98..4529c84 100644
--- a/_examples/commit/main.go
+++ b/_examples/commit/main.go
@@ -46,7 +46,8 @@ func main() {
// Commits the current staging area to the repository, with the new file
// just created. We should provide the object.Signature of Author of the
- // commit.
+ // commit Since version 5.0.1, we can omit the Author signature, being read
+ // from the git config files.
Info("git commit -m \"example go-git commit\"")
commit, err := w.Commit("example go-git commit", &git.CommitOptions{
Author: &object.Signature{
diff --git a/_examples/config/main.go b/_examples/config/main.go
deleted file mode 100644
index 7c5592f..0000000
--- a/_examples/config/main.go
+++ /dev/null
@@ -1,73 +0,0 @@
-package main
-
-import (
- "github.com/go-git/go-git/v5"
- . "github.com/go-git/go-git/v5/_examples"
- format "github.com/go-git/go-git/v5/plumbing/format/config"
-
- "github.com/go-git/go-git/v5/config"
-)
-
-// Example of how to:
-// - Access basic local (i.e. ./.git/config) configuration params
-// - Set basic local config params
-// - Set custom local config params
-// - Access custom local config params
-// - Set global config params
-// - Access global & system config params
-
-func main() {
- // Open this repository
- // Info("git init")
- // r, err := git.Init(memory.NewStorage(), nil)
- Info("open local git repo")
- r, err := git.PlainOpen(".")
- CheckIfError(err)
-
- // Load the configuration
- cfg, err := r.Config()
- CheckIfError(err)
-
- // Get core local config params
- if cfg.Core.IsBare {
- Info("repo is bare")
- } else {
- Info("repo is not bare")
- }
-
- Info("worktree is %s", cfg.Core.Worktree)
-
- // Set basic local config params
- cfg.Remotes["origin"] = &config.RemoteConfig{
- Name: "origin",
- URLs: []string{"git@github.com:mcuadros/go-git.git"},
- }
-
- Info("origin remote: %+v", cfg.Remotes["origin"])
-
- // NOTE: The examples below show advanced usage of the config.Merged system, which should
- // only be used as a last resort if the basic data defined on the Config struct don't
- // suffice for what you're trying to do.
-
- // Set local custom config param
- cfg.Merged.AddOption(format.LocalScope, "custom", format.NoSubsection, "name", "Local Name")
-
- // Set global config param (~/.gitconfig)
- cfg.Merged.AddOption(format.GlobalScope, "custom", format.NoSubsection, "name", "Global Name")
-
- // Get custom config param (merged in the same way git does: system -> global -> local)
- Info("custom.name is %s", cfg.Merged.Section("custom").Option("name"))
-
- //In order to save the config file, you need to call SetConfig
- //After calling this go to .git/config and see the custom.name added and the changes to the remote
- r.Storer.SetConfig(cfg)
-
- // Get system config params (/etc/gitconfig)
- systemSections := cfg.Merged.SystemConfig().Sections
- for _, ss := range systemSections {
- Info("System section: %s", ss.Name)
- for _, o := range ss.Options {
- Info("\tOption: %s = %s", o.Key, o.Value)
- }
- }
-}
diff --git a/config/config.go b/config/config.go
index 2b427cb..7d6ab58 100644
--- a/config/config.go
+++ b/config/config.go
@@ -5,11 +5,16 @@ import (
"bytes"
"errors"
"fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
"sort"
"strconv"
"github.com/go-git/go-git/v5/internal/url"
format "github.com/go-git/go-git/v5/plumbing/format/config"
+ "github.com/mitchellh/go-homedir"
)
const (
@@ -32,6 +37,16 @@ var (
ErrRemoteConfigEmptyName = errors.New("remote config: empty name")
)
+// Scope defines the scope of a config file, such as local, global or system.
+type Scope int
+
+// Available ConfigScope's
+const (
+ LocalScope Scope = iota
+ GlobalScope
+ SystemScope
+)
+
// Config contains the repository configuration
// https://www.kernel.org/pub/software/scm/git/docs/git-config.html#FILES
type Config struct {
@@ -46,6 +61,27 @@ type Config struct {
CommentChar string
}
+ User struct {
+ // Name is the personal name of the author and the commiter of a commit.
+ Name string
+ // Email is the email of the author and the commiter of a commit.
+ Email string
+ }
+
+ Author struct {
+ // Name is the personal name of the author of a commit.
+ Name string
+ // Email is the email of the author of a commit.
+ Email string
+ }
+
+ Committer struct {
+ // Name is the personal name of the commiter of a commit.
+ Name string
+ // Email is the email of the the commiter of a commit.
+ Email string
+ }
+
Pack struct {
// Window controls the size of the sliding window for delta
// compression. The default is 10. A value of 0 turns off
@@ -66,9 +102,6 @@ type Config struct {
// preserve the parsed information from the original format, to avoid
// dropping unsupported fields.
Raw *format.Config
- // Merged contains the raw form of how git views the system (/etc/gitconfig),
- // global (~/.gitconfig), and local (./.git/config) config params.
- Merged *format.Merged
}
// NewConfig returns a new empty Config.
@@ -77,16 +110,85 @@ func NewConfig() *Config {
Remotes: make(map[string]*RemoteConfig),
Submodules: make(map[string]*Submodule),
Branches: make(map[string]*Branch),
- Merged: format.NewMerged(),
+ Raw: format.New(),
}
- config.Raw = config.Merged.LocalConfig()
-
config.Pack.Window = DefaultPackWindow
return config
}
+// ReadConfig reads a config file from a io.Reader.
+func ReadConfig(r io.Reader) (*Config, error) {
+ b, err := ioutil.ReadAll(r)
+ if err != nil {
+ return nil, err
+ }
+
+ cfg := NewConfig()
+ if err = cfg.Unmarshal(b); err != nil {
+ return nil, err
+ }
+
+ return cfg, nil
+}
+
+// LoadConfig loads a config file from a given scope. The returned Config,
+// contains exclusively information fom the given scope. If couldn't find a
+// config file to the given scope, a empty one is returned.
+func LoadConfig(scope Scope) (*Config, error) {
+ if scope == LocalScope {
+ return nil, fmt.Errorf("LocalScope should be read from the a ConfigStorer.")
+ }
+
+ files, err := Paths(scope)
+ if err != nil {
+ return nil, err
+ }
+
+ for _, file := range files {
+ f, err := os.Open(file)
+ if err != nil {
+ if os.IsNotExist(err) {
+ continue
+ }
+
+ return nil, err
+ }
+
+ defer f.Close()
+ return ReadConfig(f)
+ }
+
+ return NewConfig(), nil
+}
+
+// Paths returns the config file location for a given scope.
+func Paths(scope Scope) ([]string, error) {
+ var files []string
+ switch scope {
+ case GlobalScope:
+ xdg := os.Getenv("XDG_CONFIG_HOME")
+ if xdg != "" {
+ files = append(files, filepath.Join(xdg, "git/config"))
+ }
+
+ home, err := homedir.Dir()
+ if err != nil {
+ return nil, err
+ }
+
+ files = append(files,
+ filepath.Join(home, ".gitconfig"),
+ filepath.Join(home, ".config/git/config"),
+ )
+ case SystemScope:
+ files = append(files, "/etc/gitconfig")
+ }
+
+ return files, nil
+}
+
// Validate validates the fields and sets the default values.
func (c *Config) Validate() error {
for name, r := range c.Remotes {
@@ -118,6 +220,9 @@ const (
branchSection = "branch"
coreSection = "core"
packSection = "pack"
+ userSection = "user"
+ authorSection = "author"
+ committerSection = "committer"
fetchKey = "fetch"
urlKey = "url"
bareKey = "bare"
@@ -126,6 +231,8 @@ const (
windowKey = "window"
mergeKey = "merge"
rebaseKey = "rebase"
+ nameKey = "name"
+ emailKey = "email"
// DefaultPackWindow holds the number of previous objects used to
// generate deltas. The value 10 is the same used by git command.
@@ -134,38 +241,26 @@ const (
// Unmarshal parses a git-config file and stores it.
func (c *Config) Unmarshal(b []byte) error {
- return c.UnmarshalScoped(format.LocalScope, b)
-}
-
-func (c *Config) UnmarshalScoped(scope format.Scope, b []byte) error {
r := bytes.NewBuffer(b)
d := format.NewDecoder(r)
- c.Merged.ResetScopedConfig(scope)
-
- if err := d.Decode(c.Merged.ScopedConfig(scope)); err != nil {
+ c.Raw = format.New()
+ if err := d.Decode(c.Raw); err != nil {
return err
}
- if scope == format.LocalScope {
- c.Raw = c.Merged.LocalConfig()
-
- c.unmarshalCore()
- if err := c.unmarshalPack(); err != nil {
- return err
- }
- unmarshalSubmodules(c.Raw, c.Submodules)
-
- if err := c.unmarshalBranches(); err != nil {
- return err
- }
+ c.unmarshalCore()
+ c.unmarshalUser()
+ if err := c.unmarshalPack(); err != nil {
+ return err
+ }
+ unmarshalSubmodules(c.Raw, c.Submodules)
- if err := c.unmarshalRemotes(); err != nil {
- return err
- }
+ if err := c.unmarshalBranches(); err != nil {
+ return err
}
- return nil
+ return c.unmarshalRemotes()
}
func (c *Config) unmarshalCore() {
@@ -178,6 +273,20 @@ func (c *Config) unmarshalCore() {
c.Core.CommentChar = s.Options.Get(commentCharKey)
}
+func (c *Config) unmarshalUser() {
+ s := c.Raw.Section(userSection)
+ c.User.Name = s.Options.Get(nameKey)
+ c.User.Email = s.Options.Get(emailKey)
+
+ s = c.Raw.Section(authorSection)
+ c.Author.Name = s.Options.Get(nameKey)
+ c.Author.Email = s.Options.Get(emailKey)
+
+ s = c.Raw.Section(committerSection)
+ c.Committer.Name = s.Options.Get(nameKey)
+ c.Committer.Email = s.Options.Get(emailKey)
+}
+
func (c *Config) unmarshalPack() error {
s := c.Raw.Section(packSection)
window := s.Options.Get(windowKey)
@@ -236,26 +345,22 @@ func (c *Config) unmarshalBranches() error {
}
// Marshal returns Config encoded as a git-config file.
-func (c *Config) MarshalScope(scope format.Scope) ([]byte, error) {
+func (c *Config) Marshal() ([]byte, error) {
c.marshalCore()
+ c.marshalUser()
c.marshalPack()
c.marshalRemotes()
c.marshalSubmodules()
c.marshalBranches()
buf := bytes.NewBuffer(nil)
- cfg := c.Merged.ScopedConfig(scope)
- if err := format.NewEncoder(buf).Encode(cfg); err != nil {
+ if err := format.NewEncoder(buf).Encode(c.Raw); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
-func (c *Config) Marshal() ([]byte, error) {
- return c.MarshalScope(format.LocalScope)
-}
-
func (c *Config) marshalCore() {
s := c.Raw.Section(coreSection)
s.SetOption(bareKey, fmt.Sprintf("%t", c.Core.IsBare))
@@ -265,6 +370,35 @@ func (c *Config) marshalCore() {
}
}
+func (c *Config) marshalUser() {
+ s := c.Raw.Section(userSection)
+ if c.User.Name != "" {
+ s.SetOption(nameKey, c.User.Name)
+ }
+
+ if c.User.Email != "" {
+ s.SetOption(emailKey, c.User.Email)
+ }
+
+ s = c.Raw.Section(authorSection)
+ if c.Author.Name != "" {
+ s.SetOption(nameKey, c.Author.Name)
+ }
+
+ if c.Author.Email != "" {
+ s.SetOption(emailKey, c.Author.Email)
+ }
+
+ s = c.Raw.Section(committerSection)
+ if c.Committer.Name != "" {
+ s.SetOption(nameKey, c.Committer.Name)
+ }
+
+ if c.Committer.Email != "" {
+ s.SetOption(emailKey, c.Committer.Email)
+ }
+}
+
func (c *Config) marshalPack() {
s := c.Raw.Section(packSection)
if c.Pack.Window != DefaultPackWindow {
diff --git a/config/config_test.go b/config/config_test.go
index a2ece2a..e68626b 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -1,9 +1,8 @@
package config
import (
- . "gopkg.in/check.v1"
"github.com/go-git/go-git/v5/plumbing"
- format "github.com/go-git/go-git/v5/plumbing/format/config"
+ . "gopkg.in/check.v1"
)
type ConfigSuite struct{}
@@ -12,65 +11,23 @@ var _ = Suite(&ConfigSuite{})
func (s *ConfigSuite) TestUnmarshal(c *C) {
input := []byte(`[core]
- bare = true
+ bare = true
worktree = foo
commentchar = bar
+[user]
+ name = John Doe
+ email = john@example.com
+[author]
+ name = Jane Roe
+ email = jane@example.com
+[committer]
+ name = Richard Roe
+ email = richard@example.com
[pack]
window = 20
[remote "origin"]
- url = git@github.com:mcuadros/go-git.git
- fetch = +refs/heads/*:refs/remotes/origin/*
-[remote "alt"]
url = git@github.com:mcuadros/go-git.git
- url = git@github.com:src-d/go-git.git
fetch = +refs/heads/*:refs/remotes/origin/*
- fetch = +refs/pull/*:refs/remotes/origin/pull/*
-[remote "win-local"]
- url = X:\\Git\\
-[submodule "qux"]
- path = qux
- url = https://github.com/foo/qux.git
- branch = bar
-[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.Core.Worktree, Equals, "foo")
- c.Assert(cfg.Core.CommentChar, Equals, "bar")
- c.Assert(cfg.Pack.Window, Equals, uint(20))
- c.Assert(cfg.Remotes, HasLen, 3)
- c.Assert(cfg.Remotes["origin"].Name, Equals, "origin")
- c.Assert(cfg.Remotes["origin"].URLs, DeepEquals, []string{"git@github.com:mcuadros/go-git.git"})
- c.Assert(cfg.Remotes["origin"].Fetch, DeepEquals, []RefSpec{"+refs/heads/*:refs/remotes/origin/*"})
- c.Assert(cfg.Remotes["alt"].Name, Equals, "alt")
- c.Assert(cfg.Remotes["alt"].URLs, DeepEquals, []string{"git@github.com:mcuadros/go-git.git", "git@github.com:src-d/go-git.git"})
- c.Assert(cfg.Remotes["alt"].Fetch, DeepEquals, []RefSpec{"+refs/heads/*:refs/remotes/origin/*", "+refs/pull/*:refs/remotes/origin/pull/*"})
- c.Assert(cfg.Remotes["win-local"].Name, Equals, "win-local")
- c.Assert(cfg.Remotes["win-local"].URLs, DeepEquals, []string{"X:\\Git\\"})
- c.Assert(cfg.Submodules, HasLen, 1)
- 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["qux"].Branch, Equals, "bar")
- c.Assert(cfg.Branches["master"].Remote, Equals, "origin")
- c.Assert(cfg.Branches["master"].Merge, Equals, plumbing.ReferenceName("refs/heads/master"))
-}
-
-func (s *ConfigSuite) TestMergedUnmarshal(c *C) {
- localInput := []byte(`[core]
- bare = true
- worktree = foo
- commentchar = bar
-[pack]
- window = 20
-[remote "origin"]
- url = git@github.com:mcuadros/go-git.git
- fetch = +refs/heads/*:refs/remotes/origin/*
[remote "alt"]
url = git@github.com:mcuadros/go-git.git
url = git@github.com:src-d/go-git.git
@@ -79,37 +36,27 @@ func (s *ConfigSuite) TestMergedUnmarshal(c *C) {
[remote "win-local"]
url = X:\\Git\\
[submodule "qux"]
- path = qux
- url = https://github.com/foo/qux.git
+ path = qux
+ url = https://github.com/foo/qux.git
branch = bar
[branch "master"]
- remote = origin
- merge = refs/heads/master
-[user]
- name = Override
-`)
-
- globalInput := []byte(`
-[user]
- name = Soandso
- email = soandso@example.com
-[core]
- editor = nvim
-[push]
- default = simple
+ remote = origin
+ merge = refs/heads/master
`)
cfg := NewConfig()
-
- err := cfg.UnmarshalScoped(format.LocalScope, localInput)
- c.Assert(err, IsNil)
-
- err = cfg.UnmarshalScoped(format.GlobalScope, globalInput)
+ err := cfg.Unmarshal(input)
c.Assert(err, IsNil)
c.Assert(cfg.Core.IsBare, Equals, true)
c.Assert(cfg.Core.Worktree, Equals, "foo")
c.Assert(cfg.Core.CommentChar, Equals, "bar")
+ c.Assert(cfg.User.Name, Equals, "John Doe")
+ c.Assert(cfg.User.Email, Equals, "john@example.com")
+ c.Assert(cfg.Author.Name, Equals, "Jane Roe")
+ c.Assert(cfg.Author.Email, Equals, "jane@example.com")
+ c.Assert(cfg.Committer.Name, Equals, "Richard Roe")
+ c.Assert(cfg.Committer.Email, Equals, "richard@example.com")
c.Assert(cfg.Pack.Window, Equals, uint(20))
c.Assert(cfg.Remotes, HasLen, 3)
c.Assert(cfg.Remotes["origin"].Name, Equals, "origin")
@@ -126,9 +73,6 @@ func (s *ConfigSuite) TestMergedUnmarshal(c *C) {
c.Assert(cfg.Submodules["qux"].Branch, Equals, "bar")
c.Assert(cfg.Branches["master"].Remote, Equals, "origin")
c.Assert(cfg.Branches["master"].Merge, Equals, plumbing.ReferenceName("refs/heads/master"))
- c.Assert(cfg.Merged.Section("user").Option("name"), Equals, "Override")
- c.Assert(cfg.Merged.Section("user").Option("email"), Equals, "soandso@example.com")
- c.Assert(cfg.Merged.Section("push").Option("default"), Equals, "simple")
}
func (s *ConfigSuite) TestMarshal(c *C) {
@@ -190,100 +134,20 @@ func (s *ConfigSuite) TestMarshal(c *C) {
c.Assert(string(b), Equals, string(output))
}
-func (s *ConfigSuite) TestMergedMarshal(c *C) {
- localOutput := []byte(`[user]
- name = Override
-[custom]
- key = value
-[core]
- bare = true
- worktree = bar
-[pack]
- window = 20
-[remote "alt"]
- url = git@github.com:mcuadros/go-git.git
- url = git@github.com:src-d/go-git.git
- fetch = +refs/heads/*:refs/remotes/origin/*
- fetch = +refs/pull/*:refs/remotes/origin/pull/*
-[remote "origin"]
- url = git@github.com:mcuadros/go-git.git
-[remote "win-local"]
- url = "X:\\Git\\"
-[submodule "qux"]
- url = https://github.com/foo/qux.git
-[branch "master"]
- remote = origin
- merge = refs/heads/master
-`)
-
- globalOutput := []byte(`[user]
- name = Soandso
- email = soandso@example.com
-[core]
- editor = nvim
-[push]
- default = simple
-`)
-
- cfg := NewConfig()
-
- cfg.Core.IsBare = true
- cfg.Core.Worktree = "bar"
- cfg.Pack.Window = 20
- cfg.Remotes["origin"] = &RemoteConfig{
- Name: "origin",
- URLs: []string{"git@github.com:mcuadros/go-git.git"},
- }
-
- cfg.Remotes["alt"] = &RemoteConfig{
- Name: "alt",
- URLs: []string{"git@github.com:mcuadros/go-git.git", "git@github.com:src-d/go-git.git"},
- Fetch: []RefSpec{"+refs/heads/*:refs/remotes/origin/*", "+refs/pull/*:refs/remotes/origin/pull/*"},
- }
-
- cfg.Remotes["win-local"] = &RemoteConfig{
- Name: "win-local",
- URLs: []string{"X:\\Git\\"},
- }
-
- cfg.Submodules["qux"] = &Submodule{
- Name: "qux",
- URL: "https://github.com/foo/qux.git",
- }
-
- cfg.Branches["master"] = &Branch{
- Name: "master",
- Remote: "origin",
- Merge: "refs/heads/master",
- }
-
- cfg.Merged.GlobalConfig().Section("user").SetOption("name", "Soandso")
- cfg.Merged.LocalConfig().Section("user").SetOption("name", "Override")
- cfg.Merged.GlobalConfig().Section("user").SetOption("email", "soandso@example.com")
- cfg.Merged.GlobalConfig().Section("core").AddOption("editor", "nvim")
- cfg.Merged.LocalConfig().Section("custom").SetOption("key", "value")
- cfg.Merged.GlobalConfig().Section("push").AddOption("default", "simple")
-
- c.Assert(cfg.Merged.Section("user").Option("name"), Equals, "Override")
-
- localBytes, err := cfg.Marshal()
- c.Assert(err, IsNil)
- c.Assert(string(localBytes), Equals, string(localOutput))
-
- globalBytes, err := cfg.MarshalScope(format.GlobalScope)
- c.Assert(err, IsNil)
- c.Assert(string(globalBytes), Equals, string(globalOutput))
-
- systemBytes, err := cfg.MarshalScope(format.SystemScope)
- c.Assert(err, IsNil)
- c.Assert(string(systemBytes), Equals, "")
-}
-
func (s *ConfigSuite) TestUnmarshalMarshal(c *C) {
input := []byte(`[core]
bare = true
worktree = foo
custom = ignored
+[user]
+ name = John Doe
+ email = john@example.com
+[author]
+ name = Jane Roe
+ email = jane@example.com
+[committer]
+ name = Richard Roe
+ email = richard@example.co
[pack]
window = 20
[remote "origin"]
@@ -306,6 +170,12 @@ func (s *ConfigSuite) TestUnmarshalMarshal(c *C) {
c.Assert(string(output), DeepEquals, string(input))
}
+func (s *ConfigSuite) TestLoadConfig(c *C) {
+ cfg, err := LoadConfig(GlobalScope)
+ c.Assert(err, IsNil)
+ c.Assert(cfg.User.Email, Not(Equals), "")
+}
+
func (s *ConfigSuite) TestValidateConfig(c *C) {
config := &Config{
Remotes: map[string]*RemoteConfig{
diff --git a/go.mod b/go.mod
index 6d8da35..0c9cfd2 100644
--- a/go.mod
+++ b/go.mod
@@ -10,6 +10,7 @@ require (
github.com/go-git/go-billy/v5 v5.0.0
github.com/go-git/go-git-fixtures/v4 v4.0.1
github.com/google/go-cmp v0.3.0
+ github.com/imdario/mergo v0.3.9
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99
github.com/jessevdk/go-flags v1.4.0
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd
diff --git a/go.sum b/go.sum
index a73585e..e14e29a 100644
--- a/go.sum
+++ b/go.sum
@@ -22,6 +22,8 @@ github.com/go-git/go-git-fixtures/v4 v4.0.1 h1:q+IFMfLx200Q3scvt2hN79JsEzy4AmBTp
github.com/go-git/go-git-fixtures/v4 v4.0.1/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw=
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg=
+github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
diff --git a/options.go b/options.go
index 2a9727c..5367031 100644
--- a/options.go
+++ b/options.go
@@ -378,7 +378,8 @@ type CommitOptions struct {
// All automatically stage files that have been modified and deleted, but
// new files you have not told Git about are not affected.
All bool
- // Author is the author's signature of the commit.
+ // Author is the author's signature of the commit. If Author is empty the
+ // Name and Email is read from the config, and time.Now it's used as When.
Author *object.Signature
// Committer is the committer's signature of the commit. If Committer is
// nil the Author signature is used.
@@ -395,7 +396,9 @@ type CommitOptions struct {
// Validate validates the fields and sets the default values.
func (o *CommitOptions) Validate(r *Repository) error {
if o.Author == nil {
- return ErrMissingAuthor
+ if err := o.loadConfigAuthorAndCommitter(r); err != nil {
+ return err
+ }
}
if o.Committer == nil {
@@ -416,6 +419,43 @@ func (o *CommitOptions) Validate(r *Repository) error {
return nil
}
+func (o *CommitOptions) loadConfigAuthorAndCommitter(r *Repository) error {
+ cfg, err := r.ConfigScoped(config.SystemScope)
+ if err != nil {
+ return err
+ }
+
+ if o.Author == nil && cfg.Author.Email != "" && cfg.Author.Name != "" {
+ o.Author = &object.Signature{
+ Name: cfg.Author.Name,
+ Email: cfg.Author.Email,
+ When: time.Now(),
+ }
+ }
+
+ if o.Committer == nil && cfg.Committer.Email != "" && cfg.Committer.Name != "" {
+ o.Committer = &object.Signature{
+ Name: cfg.Committer.Name,
+ Email: cfg.Committer.Email,
+ When: time.Now(),
+ }
+ }
+
+ if o.Author == nil && cfg.User.Email != "" && cfg.User.Name != "" {
+ o.Author = &object.Signature{
+ Name: cfg.User.Name,
+ Email: cfg.User.Email,
+ When: time.Now(),
+ }
+ }
+
+ if o.Author == nil {
+ return ErrMissingAuthor
+ }
+
+ return nil
+}
+
var (
ErrMissingName = errors.New("name field is required")
ErrMissingTagger = errors.New("tagger field is required")
diff --git a/options_test.go b/options_test.go
index b9e9096..aa36dab 100644
--- a/options_test.go
+++ b/options_test.go
@@ -1,8 +1,8 @@
package git
import (
- . "gopkg.in/check.v1"
"github.com/go-git/go-git/v5/plumbing/object"
+ . "gopkg.in/check.v1"
)
type OptionsSuite struct {
@@ -18,12 +18,6 @@ func (s *OptionsSuite) TestCommitOptionsParentsFromHEAD(c *C) {
c.Assert(o.Parents, HasLen, 1)
}
-func (s *OptionsSuite) TestCommitOptionsMissingAuthor(c *C) {
- o := CommitOptions{}
- err := o.Validate(s.Repository)
- c.Assert(err, Equals, ErrMissingAuthor)
-}
-
func (s *OptionsSuite) TestCommitOptionsCommitter(c *C) {
sig := &object.Signature{}
diff --git a/plumbing/format/config/merged.go b/plumbing/format/config/merged.go
deleted file mode 100644
index a44f3b8..0000000
--- a/plumbing/format/config/merged.go
+++ /dev/null
@@ -1,261 +0,0 @@
-package config
-
-// Scope defines which configuration we're reading / writing. Git uses
-// a local config file in each repo (./.git/config) which corresponds
-// with LocalScope here. It's values have the highest priority (i.e. they
-// override those in the other two scopes). Next is the global config which
-// could also be described as a user config since it lives in the
-// ~/.gitconfig file. Last and lowest priority is the system config which
-// resides in the /etc/gitconfig file.
-type Scope int
-const (
- LocalScope Scope = iota
- GlobalScope
- SystemScope
-)
-
-type ScopedConfigs map[Scope]*Config
-
-// Merged defines a read-only view of the priority-merged config params
-// so that you can access the effective settings in the same way git does.
-// For example, if a user has defined a user.name value in ~/.gitconfig but
-// a different one in the current repo's ./.git/config file, this code:
-// config.Merged.Section("user").Option("name") will give you the value
-// from ./.git/config.
-type Merged struct {
- scopedConfigs ScopedConfigs
-}
-
-// MergedSection is a read-only Section for merged config views.
-type MergedSection struct {
- backingSection *Section
-}
-
-// MergedSubsection is a read-only Subsection for merged config views.
-type MergedSubsection struct {
- backingSubsection *Subsection
-}
-
-type MergedSubsections []*MergedSubsection
-
-func NewMerged() *Merged {
- cfg := &Merged{
- scopedConfigs: make(ScopedConfigs),
- }
- for s := LocalScope; s <= SystemScope; s++ {
- cfg.scopedConfigs[s] = New()
- }
-
- return cfg
-}
-
-func (m *Merged) ResetScopedConfig(scope Scope) {
- m.scopedConfigs[scope] = New()
-}
-
-// ScopedConfig allows accessing specific backing *Config instances.
-func (m *Merged) ScopedConfig(scope Scope) *Config {
- return m.scopedConfigs[scope]
-}
-
-// LocalConfig allows accessing the local (i.e. ./.git/config) backing
-// *Config instance.
-func (m *Merged) LocalConfig() *Config {
- return m.ScopedConfig(LocalScope)
-}
-
-// GlobalConfig allows accessing the global (i.e. ~/.gitconfig) backing
-// *Config instance.
-func (m *Merged) GlobalConfig() *Config {
- return m.ScopedConfig(GlobalScope)
-}
-
-// SystemConfig allows accessing the system (i.e. /etc/gitconfig) backing
-// *Config instance.
-func (m *Merged) SystemConfig() *Config {
- return m.ScopedConfig(SystemScope)
-}
-
-// SetLocalConfig allows updating the local (i.e. ./.git/config) config. If you
-// call config.SetConfig(...) on the containing top-level config instance after
-// this, your new local config params will be written to ./.git/config.
-func (m *Merged) SetLocalConfig(c *Config) {
- m.scopedConfigs[LocalScope] = c
-}
-
-// SetGlobalConfig allows updating the global (i.e. ~/.gitconfig) config. If you
-// call config.SetConfig(...) on the containing top-level config instance after
-// this, your new global config params will be written to ~/.gitconfig.
-func (m *Merged) SetGlobalConfig(c *Config) {
- m.scopedConfigs[GlobalScope] = c
-}
-
-// Config.Section creates the section if it doesn't exist, which is not
-// what we want in here.
-func (c *Config) hasSection(name string) bool {
- sections := c.Sections
- var found bool
-
- for _, s := range sections {
- if s.IsName(name) {
- found = true
- break
- }
- }
-
- return found
-}
-
-// Section returns a read-only *MergedSection view of the config in which
-// params are merged in the same order as git itself:
-// Local overrides global overrides system.
-func (m *Merged) Section(name string) *MergedSection {
- var mergedSection *MergedSection
-
- for s := SystemScope; s >= LocalScope; s-- {
- if m.scopedConfigs[s].hasSection(name) {
- sec := m.scopedConfigs[s].Section(name)
- if mergedSection == nil {
- mergedSection = NewMergedSection(sec)
- }
-
- if mergedSection.Options() == nil {
- mergedSection.backingSection.Options = sec.Options
- } else {
- for _, o := range sec.Options {
- mergedSection.backingSection.SetOption(o.Key, o.Value)
- }
- }
-
- if mergedSection.Subsections() == nil {
- mergedSection.backingSection.Subsections = sec.Subsections
- } else {
- for _, ss := range sec.Subsections {
- if mergedSection.HasSubsection(ss.Name) {
- for _, o := range ss.Options {
- mergedSection.backingSection.Subsection(ss.Name).SetOption(o.Key, o.Value)
- }
- } else {
- mergedSection.backingSection.Subsections = append(mergedSection.backingSection.Subsections, ss)
- }
- }
- }
- }
- }
-
- if mergedSection != nil {
- mergedSection.backingSection.Name = name
- }
-
- return mergedSection
-}
-
-// AddOption works just like config.AddOption except that it takes a Scope as its first argument.
-// This defines which config scope (local, global, or system) this option should be added to.
-func (m *Merged) AddOption(scope Scope, section string, subsection string, key string, value string) *Config {
- return m.ScopedConfig(scope).AddOption(section, subsection, key, value)
-}
-
-// SetOption works just like config.SetOption except that it takes a Scope as its first argument.
-// This defines which config scope (local, global, or system) this option should be set in.
-func (m *Merged) SetOption(scope Scope, section string, subsection string, key string, value string) *Config {
- return m.ScopedConfig(scope).SetOption(section, subsection, key, value)
-}
-
-// RemoveSection works just like config.RemoveSection except that it takes a Scope as its first argument.
-// This defines which config scope (local, global, or system) the section should be removed from.
-func (m *Merged) RemoveSection(scope Scope, name string) *Config {
- return m.ScopedConfig(scope).RemoveSection(name)
-}
-
-// RemoveSubsection works just like config.RemoveSubsection except that it takes a Scope as its first argument.
-// This defines which config scope (local, global, or system) the subsection should be removed from.
-func (m *Merged) RemoveSubsection(scope Scope, section string, subsection string) *Config {
- return m.ScopedConfig(scope).RemoveSubsection(section, subsection)
-}
-
-func copyOptions(os Options) Options {
- copiedOptions := make(Options, 0)
-
- for _, o := range os {
- copiedOptions = append(copiedOptions, o)
- }
-
- return copiedOptions
-}
-
-func copySubsections(ss Subsections) Subsections {
- copiedSubsections := make(Subsections, 0)
-
- for _, ss := range ss {
- copiedSubsections = append(copiedSubsections, &Subsection{
- Name: ss.Name,
- Options: copyOptions(ss.Options),
- })
- }
-
- return copiedSubsections
-}
-
-func NewMergedSection(backing *Section) *MergedSection {
- return &MergedSection{
- backingSection: &Section{
- Name: backing.Name,
- Options: copyOptions(backing.Options),
- Subsections: copySubsections(backing.Subsections),
- },
- }
-}
-
-func (ms *MergedSection) Name() string {
- return ms.backingSection.Name
-}
-
-func (ms *MergedSection) IsName(name string) bool {
- return ms.backingSection.IsName(name)
-}
-
-func (ms *MergedSection) Options() []*Option {
- return ms.backingSection.Options
-}
-
-func (ms *MergedSection) Option(key string) string {
- return ms.backingSection.Option(key)
-}
-
-func (ms *MergedSection) Subsections() MergedSubsections {
- mss := make(MergedSubsections, 0)
- for _, ss := range ms.backingSection.Subsections {
- mss = append(mss, NewMergedSubsection(ss))
- }
- return mss
-}
-
-func (ms *MergedSection) Subsection(name string) *MergedSubsection {
- return NewMergedSubsection(ms.backingSection.Subsection(name))
-}
-
-func (ms *MergedSection) HasSubsection(name string) bool {
- return ms.backingSection.HasSubsection(name)
-}
-
-func NewMergedSubsection(backing *Subsection) *MergedSubsection {
- return &MergedSubsection{backingSubsection: backing}
-}
-
-func (mss *MergedSubsection) Name() string {
- return mss.backingSubsection.Name
-}
-
-func (mss *MergedSubsection) IsName(name string) bool {
- return mss.backingSubsection.IsName(name)
-}
-
-func (mss *MergedSubsection) Options() []*Option {
- return mss.backingSubsection.Options
-}
-
-func (mss *MergedSubsection) Option(key string) string {
- return mss.backingSubsection.Option(key)
-}
-
diff --git a/repository.go b/repository.go
index c83a136..47318d1 100644
--- a/repository.go
+++ b/repository.go
@@ -13,7 +13,6 @@ import (
"strings"
"time"
- "golang.org/x/crypto/openpgp"
"github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/internal/revision"
"github.com/go-git/go-git/v5/plumbing"
@@ -24,6 +23,8 @@ import (
"github.com/go-git/go-git/v5/storage"
"github.com/go-git/go-git/v5/storage/filesystem"
"github.com/go-git/go-git/v5/utils/ioutil"
+ "github.com/imdario/mergo"
+ "golang.org/x/crypto/openpgp"
"github.com/go-git/go-billy/v5"
"github.com/go-git/go-billy/v5/osfs"
@@ -155,7 +156,7 @@ func setConfigWorktree(r *Repository, worktree, storage billy.Filesystem) error
return nil
}
- cfg, err := r.Storer.Config()
+ cfg, err := r.Config()
if err != nil {
return err
}
@@ -434,14 +435,56 @@ func cleanUpDir(path string, all bool) error {
return err
}
-// Config return the repository config
+// Config return the repository config. In a filesystem backed repository this
+// means read the `.git/config`.
func (r *Repository) Config() (*config.Config, error) {
return r.Storer.Config()
}
+// SetConfig marshall and writes the repository config. In a filesystem backed
+// repository this means write the `.git/config`. This function should be called
+// with the result of `Repository.Config` and never with the output of
+// `Repository.ConfigScoped`.
+func (r *Repository) SetConfig(cfg *config.Config) error {
+ return r.Storer.SetConfig(cfg)
+}
+
+// ConfigScoped returns the repository config, merged with requested scope and
+// lower. For example if, config.GlobalScope is given the local and global config
+// are returned merged in one config value.
+func (r *Repository) ConfigScoped(scope config.Scope) (*config.Config, error) {
+ // TODO(mcuadros): v6, add this as ConfigOptions.Scoped
+
+ var err error
+ system := config.NewConfig()
+ if scope >= config.SystemScope {
+ system, err = config.LoadConfig(config.SystemScope)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ global := config.NewConfig()
+ if scope >= config.GlobalScope {
+ global, err = config.LoadConfig(config.GlobalScope)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ local, err := r.Storer.Config()
+ if err != nil {
+ return nil, err
+ }
+
+ _ = mergo.Merge(global, system)
+ _ = mergo.Merge(local, global)
+ return local, nil
+}
+
// Remote return a remote if exists
func (r *Repository) Remote(name string) (*Remote, error) {
- cfg, err := r.Storer.Config()
+ cfg, err := r.Config()
if err != nil {
return nil, err
}
@@ -456,7 +499,7 @@ func (r *Repository) Remote(name string) (*Remote, error) {
// Remotes returns a list with all the remotes
func (r *Repository) Remotes() ([]*Remote, error) {
- cfg, err := r.Storer.Config()
+ cfg, err := r.Config()
if err != nil {
return nil, err
}
@@ -480,7 +523,7 @@ func (r *Repository) CreateRemote(c *config.RemoteConfig) (*Remote, error) {
remote := NewRemote(r.Storer, c)
- cfg, err := r.Storer.Config()
+ cfg, err := r.Config()
if err != nil {
return nil, err
}
@@ -511,7 +554,7 @@ func (r *Repository) CreateRemoteAnonymous(c *config.RemoteConfig) (*Remote, err
// DeleteRemote delete a remote from the repository and delete the config
func (r *Repository) DeleteRemote(name string) error {
- cfg, err := r.Storer.Config()
+ cfg, err := r.Config()
if err != nil {
return err
}
@@ -526,7 +569,7 @@ func (r *Repository) DeleteRemote(name string) error {
// Branch return a Branch if exists
func (r *Repository) Branch(name string) (*config.Branch, error) {
- cfg, err := r.Storer.Config()
+ cfg, err := r.Config()
if err != nil {
return nil, err
}
@@ -545,7 +588,7 @@ func (r *Repository) CreateBranch(c *config.Branch) error {
return err
}
- cfg, err := r.Storer.Config()
+ cfg, err := r.Config()
if err != nil {
return err
}
@@ -560,7 +603,7 @@ func (r *Repository) CreateBranch(c *config.Branch) error {
// DeleteBranch delete a Branch from the repository and delete the config
func (r *Repository) DeleteBranch(name string) error {
- cfg, err := r.Storer.Config()
+ cfg, err := r.Config()
if err != nil {
return err
}
@@ -835,7 +878,7 @@ func (r *Repository) cloneRefSpec(o *CloneOptions) []config.RefSpec {
}
func (r *Repository) setIsBare(isBare bool) error {
- cfg, err := r.Storer.Config()
+ cfg, err := r.Config()
if err != nil {
return err
}
@@ -851,7 +894,7 @@ func (r *Repository) updateRemoteConfigIfNeeded(o *CloneOptions, c *config.Remot
c.Fetch = r.cloneRefSpec(o)
- cfg, err := r.Storer.Config()
+ cfg, err := r.Config()
if err != nil {
return err
}
@@ -1541,7 +1584,7 @@ func (r *Repository) createNewObjectPack(cfg *RepackConfig) (h plumbing.Hash, er
return h, err
}
defer ioutil.CheckClose(wc, &err)
- scfg, err := r.Storer.Config()
+ scfg, err := r.Config()
if err != nil {
return h, err
}
diff --git a/repository_test.go b/repository_test.go
index e366373..37cd914 100644
--- a/repository_test.go
+++ b/repository_test.go
@@ -1870,6 +1870,21 @@ func (s *RepositorySuite) TestLogLimitWithOtherParamsPass(c *C) {
c.Assert(iterErr, Equals, io.EOF)
}
+func (s *RepositorySuite) TestConfigScoped(c *C) {
+ r, _ := Init(memory.NewStorage(), nil)
+ err := r.clone(context.Background(), &CloneOptions{
+ URL: s.GetBasicLocalRepositoryURL(),
+ })
+
+ cfg, err := r.ConfigScoped(config.LocalScope)
+ c.Assert(err, IsNil)
+ c.Assert(cfg.User.Email, Equals, "")
+
+ cfg, err = r.ConfigScoped(config.SystemScope)
+ c.Assert(err, IsNil)
+ c.Assert(cfg.User.Email, Not(Equals), "")
+}
+
func (s *RepositorySuite) TestCommit(c *C) {
r, _ := Init(memory.NewStorage(), nil)
err := r.clone(context.Background(), &CloneOptions{
diff --git a/storage/filesystem/config.go b/storage/filesystem/config.go
index dbdce54..78a6464 100644
--- a/storage/filesystem/config.go
+++ b/storage/filesystem/config.go
@@ -1,11 +1,9 @@
package filesystem
import (
- stdioutil "io/ioutil"
"os"
"github.com/go-git/go-git/v5/config"
- format "github.com/go-git/go-git/v5/plumbing/format/config"
"github.com/go-git/go-git/v5/storage/filesystem/dotgit"
"github.com/go-git/go-git/v5/utils/ioutil"
)
@@ -14,73 +12,18 @@ type ConfigStorage struct {
dir *dotgit.DotGit
}
-func (c *ConfigStorage) Config() (*config.Config, error) {
- cfg := config.NewConfig()
-
- // local config (./.git/config)
- f, err := c.dir.LocalConfig()
- if err != nil {
- if os.IsNotExist(err) {
- return cfg, nil
- }
-
- return nil, err
- }
-
- defer ioutil.CheckClose(f, &err)
-
- b, err := stdioutil.ReadAll(f)
- if err != nil {
- return cfg, err
- }
-
- if err = cfg.UnmarshalScoped(format.LocalScope, b); err != nil {
- return cfg, err
- }
-
- // global config (~/.gitconfig)
- f, err = c.dir.GlobalConfig()
+func (c *ConfigStorage) Config() (conf *config.Config, err error) {
+ f, err := c.dir.Config()
if err != nil {
if os.IsNotExist(err) {
- return cfg, nil
+ return config.NewConfig(), nil
}
return nil, err
}
defer ioutil.CheckClose(f, &err)
-
- b, err = stdioutil.ReadAll(f)
- if err != nil {
- return cfg, err
- }
-
- if err = cfg.UnmarshalScoped(format.GlobalScope, b); err != nil {
- return cfg, err
- }
-
- // system config (/etc/gitconfig)
- f, err = c.dir.SystemConfig()
- if err != nil {
- if os.IsNotExist(err) {
- return cfg, nil
- }
-
- return nil, err
- }
-
- defer ioutil.CheckClose(f, &err)
-
- b, err = stdioutil.ReadAll(f)
- if err != nil {
- return cfg, err
- }
-
- if err = cfg.UnmarshalScoped(format.SystemScope, b); err != nil {
- return cfg, err
- }
-
- return cfg, err
+ return config.ReadConfig(f)
}
func (c *ConfigStorage) SetConfig(cfg *config.Config) (err error) {
@@ -88,7 +31,7 @@ func (c *ConfigStorage) SetConfig(cfg *config.Config) (err error) {
return err
}
- f, err := c.dir.LocalConfigWriter()
+ f, err := c.dir.ConfigWriter()
if err != nil {
return err
}
diff --git a/storage/filesystem/dotgit/dotgit.go b/storage/filesystem/dotgit/dotgit.go
index 8c3896b..83c7683 100644
--- a/storage/filesystem/dotgit/dotgit.go
+++ b/storage/filesystem/dotgit/dotgit.go
@@ -21,17 +21,15 @@ import (
)
const (
- suffix = ".git"
- packedRefsPath = "packed-refs"
- localConfigPath = "config"
- globalConfigPath = ".gitconfig"
- systemConfigPath = "/etc/gitconfig"
- indexPath = "index"
- shallowPath = "shallow"
- modulePath = "modules"
- objectsPath = "objects"
- packPath = "pack"
- refsPath = "refs"
+ suffix = ".git"
+ packedRefsPath = "packed-refs"
+ configPath = "config"
+ indexPath = "index"
+ shallowPath = "shallow"
+ modulePath = "modules"
+ objectsPath = "objects"
+ packPath = "pack"
+ refsPath = "refs"
tmpPackedRefsPrefix = "._packed-refs"
@@ -157,32 +155,14 @@ func (d *DotGit) Close() error {
return nil
}
-// LocalConfigWriter returns a file pointer for write to the local config file
-func (d *DotGit) LocalConfigWriter() (billy.File, error) {
- return d.fs.Create(localConfigPath)
+// ConfigWriter returns a file pointer for write to the config file
+func (d *DotGit) ConfigWriter() (billy.File, error) {
+ return d.fs.Create(configPath)
}
-// LocalConfig returns a file pointer for read to the local config file
-func (d *DotGit) LocalConfig() (billy.File, error) {
- return d.fs.Open(localConfigPath)
-}
-
-// GlobalConfigWriter returns a file pointer for write to the global config file
-func (d *DotGit) GlobalConfigWriter() (billy.File, error) {
- return osfs.New(os.Getenv("HOME")).Create(globalConfigPath)
-}
-
-// GlobalConfig returns a file pointer for read to the global config file
-func (d *DotGit) GlobalConfig() (billy.File, error) {
- return osfs.New(os.Getenv("HOME")).Open(globalConfigPath)
-}
-
-// SystemConfigWriter doesn't exist because we typically do not have permission
-// to write to files in /etc.
-
-// SystemConfig returns a file pointer for read to the system config file
-func (d *DotGit) SystemConfig() (billy.File, error) {
- return osfs.New("/").Open(systemConfigPath)
+// Config returns a file pointer for read to the config file
+func (d *DotGit) Config() (billy.File, error) {
+ return d.fs.Open(configPath)
}
// IndexWriter returns a file pointer for write to the index file
diff --git a/storage/filesystem/dotgit/dotgit_test.go b/storage/filesystem/dotgit/dotgit_test.go
index 519f601..0a72aa6 100644
--- a/storage/filesystem/dotgit/dotgit_test.go
+++ b/storage/filesystem/dotgit/dotgit_test.go
@@ -344,7 +344,7 @@ func (s *SuiteDotGit) TestConfig(c *C) {
fs := fixtures.Basic().ByTag(".git").One().DotGit()
dir := New(fs)
- file, err := dir.LocalConfig()
+ file, err := dir.Config()
c.Assert(err, IsNil)
c.Assert(filepath.Base(file.Name()), Equals, "config")
}
@@ -357,13 +357,13 @@ func (s *SuiteDotGit) TestConfigWriteAndConfig(c *C) {
fs := osfs.New(tmp)
dir := New(fs)
- f, err := dir.LocalConfigWriter()
+ f, err := dir.ConfigWriter()
c.Assert(err, IsNil)
_, err = f.Write([]byte("foo"))
c.Assert(err, IsNil)
- f, err = dir.LocalConfig()
+ f, err = dir.Config()
c.Assert(err, IsNil)
cnt, err := ioutil.ReadAll(f)
diff --git a/submodule.go b/submodule.go
index 92ccdb1..dff26b0 100644
--- a/submodule.go
+++ b/submodule.go
@@ -35,7 +35,7 @@ func (s *Submodule) Config() *config.Submodule {
// Init initialize the submodule reading the recorded Entry in the index for
// the given submodule
func (s *Submodule) Init() error {
- cfg, err := s.w.r.Storer.Config()
+ cfg, err := s.w.r.Config()
if err != nil {
return err
}
diff --git a/worktree_commit_test.go b/worktree_commit_test.go
index aeb4a9a..6eafb15 100644
--- a/worktree_commit_test.go
+++ b/worktree_commit_test.go
@@ -24,16 +24,20 @@ import (
. "gopkg.in/check.v1"
)
-func (s *WorktreeSuite) TestCommitInvalidOptions(c *C) {
+func (s *WorktreeSuite) TestCommitEmptyOptions(c *C) {
r, err := Init(memory.NewStorage(), memfs.New())
c.Assert(err, IsNil)
w, err := r.Worktree()
c.Assert(err, IsNil)
- hash, err := w.Commit("", &CommitOptions{})
- c.Assert(err, Equals, ErrMissingAuthor)
- c.Assert(hash.IsZero(), Equals, true)
+ hash, err := w.Commit("foo", &CommitOptions{})
+ c.Assert(err, IsNil)
+ c.Assert(hash.IsZero(), Equals, false)
+
+ commit, err := r.CommitObject(hash)
+ c.Assert(err, IsNil)
+ c.Assert(commit.Author.Name, Not(Equals), "")
}
func (s *WorktreeSuite) TestCommitInitial(c *C) {