diff options
author | amine <hilalyamine@gmail.com> | 2019-10-31 15:46:09 +0100 |
---|---|---|
committer | amine <hilalyamine@gmail.com> | 2019-10-31 15:46:09 +0100 |
commit | ab935674a26f2eef5d8014c615b9b5bc1f402135 (patch) | |
tree | 0a1283552ec54460fc3bef464ac26d61fc034066 /repository/config_git.go | |
parent | 11b4a1beb7e1ab8809515a5ce21e8708fba7f300 (diff) | |
download | git-bug-ab935674a26f2eef5d8014c615b9b5bc1f402135.tar.gz |
repository: config interface and implementation rework
Diffstat (limited to 'repository/config_git.go')
-rw-r--r-- | repository/config_git.go | 185 |
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 +} |