aboutsummaryrefslogtreecommitdiffstats
path: root/repository/config_git.go
diff options
context:
space:
mode:
Diffstat (limited to 'repository/config_git.go')
-rw-r--r--repository/config_git.go185
1 files changed, 185 insertions, 0 deletions
diff --git a/repository/config_git.go b/repository/config_git.go
new file mode 100644
index 00000000..81b9b819
--- /dev/null
+++ b/repository/config_git.go
@@ -0,0 +1,185 @@
+package repository
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+
+ "github.com/blang/semver"
+ "github.com/pkg/errors"
+)
+
+type gitConfig struct {
+ version *semver.Version
+ execFn func(args ...string) (string, error)
+}
+
+func NewGitConfig(repo *GitRepo, global bool) *gitConfig {
+ version, _ := repo.GitVersion()
+
+ if global {
+ return &gitConfig{
+ execFn: func(args ...string) (string, error) {
+ args = append([]string{"config", "--global"}, args...)
+ return repo.runGitCommand(args...)
+ },
+ version: version,
+ }
+ }
+
+ return &gitConfig{
+ execFn: func(args ...string) (string, error) {
+ args = append([]string{"config"}, args...)
+ return repo.runGitCommand(args...)
+ },
+ version: version,
+ }
+}
+
+// StoreConfig store a single key/value pair in the config of the repo
+func (gc *gitConfig) Store(key string, value string) error {
+ _, err := gc.execFn("--replace-all", key, value)
+
+ return err
+}
+
+// ReadConfigs read all key/value pair matching the key prefix
+func (gc *gitConfig) ReadAll(keyPrefix string) (map[string]string, error) {
+ stdout, err := gc.execFn("--get-regexp", keyPrefix)
+
+ // / \
+ // / ! \
+ // -------
+ //
+ // There can be a legitimate error here, but I see no portable way to
+ // distinguish them from the git error that say "no matching value exist"
+ if err != nil {
+ return nil, nil
+ }
+
+ lines := strings.Split(stdout, "\n")
+
+ result := make(map[string]string, len(lines))
+
+ for _, line := range lines {
+ if strings.TrimSpace(line) == "" {
+ continue
+ }
+
+ parts := strings.Fields(line)
+ if len(parts) != 2 {
+ return nil, fmt.Errorf("bad git config: %s", line)
+ }
+
+ result[parts[0]] = parts[1]
+ }
+
+ return result, nil
+}
+
+func (gc *gitConfig) ReadString(key string) (string, error) {
+ stdout, err := gc.execFn("--get-all", key)
+
+ // / \
+ // / ! \
+ // -------
+ //
+ // There can be a legitimate error here, but I see no portable way to
+ // distinguish them from the git error that say "no matching value exist"
+ if err != nil {
+ return "", ErrNoConfigEntry
+ }
+
+ lines := strings.Split(stdout, "\n")
+
+ if len(lines) == 0 {
+ return "", ErrNoConfigEntry
+ }
+ if len(lines) > 1 {
+ return "", ErrMultipleConfigEntry
+ }
+
+ return lines[0], nil
+}
+
+func (gc *gitConfig) ReadBool(key string) (bool, error) {
+ val, err := gc.ReadString(key)
+ if err != nil {
+ return false, err
+ }
+
+ return strconv.ParseBool(val)
+}
+
+func (gc *gitConfig) rmSection(keyPrefix string) error {
+ _, err := gc.execFn("--remove-section", keyPrefix)
+ return err
+}
+
+func (gc *gitConfig) unsetAll(keyPrefix string) error {
+ _, err := gc.execFn("--unset-all", keyPrefix)
+ return err
+}
+
+// return keyPrefix section
+// example: sectionFromKey(a.b.c.d) return a.b.c
+func sectionFromKey(keyPrefix string) string {
+ s := strings.Split(keyPrefix, ".")
+ if len(s) == 1 {
+ return keyPrefix
+ }
+
+ return strings.Join(s[:len(s)-1], ".")
+}
+
+// rmConfigs with git version lesser than 2.18
+func (gc *gitConfig) rmConfigsGitVersionLT218(keyPrefix string) error {
+ // try to remove key/value pair by key
+ err := gc.unsetAll(keyPrefix)
+ if err != nil {
+ return gc.rmSection(keyPrefix)
+ }
+
+ m, err := gc.ReadAll(sectionFromKey(keyPrefix))
+ if err != nil {
+ return err
+ }
+
+ // if section doesn't have any left key/value remove the section
+ if len(m) == 0 {
+ return gc.rmSection(sectionFromKey(keyPrefix))
+ }
+
+ return nil
+}
+
+// RmConfigs remove all key/value pair matching the key prefix
+func (gc *gitConfig) RemoveAll(keyPrefix string) error {
+ // starting from git 2.18.0 sections are automatically deleted when the last existing
+ // key/value is removed. Before 2.18.0 we should remove the section
+ // see https://github.com/git/git/blob/master/Documentation/RelNotes/2.18.0.txt#L379
+ lt218, err := gc.gitVersionLT218()
+ if err != nil {
+ return errors.Wrap(err, "getting git version")
+ }
+
+ if lt218 {
+ return gc.rmConfigsGitVersionLT218(keyPrefix)
+ }
+
+ err = gc.unsetAll(keyPrefix)
+ if err != nil {
+ return gc.rmSection(keyPrefix)
+ }
+
+ return nil
+}
+
+func (gc *gitConfig) gitVersionLT218() (bool, error) {
+ gitVersion218, err := semver.Make("2.18.0")
+ if err != nil {
+ return false, err
+ }
+
+ return gc.version.LT(gitVersion218), nil
+}