aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Muré <batolettre@gmail.com>2019-11-01 22:16:57 +0000
committerGitHub <noreply@github.com>2019-11-01 22:16:57 +0000
commitf5193cc76dd1a1c1bb55194a64ef90bae2278115 (patch)
tree74c5f184df4fb27bec0d6f809ce9816ec133d80f
parent11b4a1beb7e1ab8809515a5ce21e8708fba7f300 (diff)
parentf9f82957a78ea0d3cacddbc6914ce98b94ddf7bd (diff)
downloadgit-bug-f5193cc76dd1a1c1bb55194a64ef90bae2278115.tar.gz
Merge pull request #240 from MichaelMure/repo-config
repository config interface and implementation rework
-rw-r--r--bridge/core/bridge.go10
-rw-r--r--cache/repo_cache.go35
-rw-r--r--commands/webui.go2
-rw-r--r--identity/identity.go8
-rw-r--r--repository/config.go49
-rw-r--r--repository/config_git.go225
-rw-r--r--repository/config_mem.go84
-rw-r--r--repository/git.go191
-rw-r--r--repository/git_test.go33
-rw-r--r--repository/git_testing.go5
-rw-r--r--repository/mock_repo.go74
-rw-r--r--repository/repo.go21
12 files changed, 435 insertions, 302 deletions
diff --git a/bridge/core/bridge.go b/bridge/core/bridge.go
index 6fd28b03..47a89389 100644
--- a/bridge/core/bridge.go
+++ b/bridge/core/bridge.go
@@ -134,7 +134,7 @@ func DefaultBridge(repo *cache.RepoCache) (*Bridge, error) {
// ConfiguredBridges return the list of bridge that are configured for the given
// repo
func ConfiguredBridges(repo repository.RepoCommon) ([]string, error) {
- configs, err := repo.ReadConfigs(bridgeConfigKeyPrefix + ".")
+ configs, err := repo.LocalConfig().ReadAll(bridgeConfigKeyPrefix + ".")
if err != nil {
return nil, errors.Wrap(err, "can't read configured bridges")
}
@@ -171,7 +171,7 @@ func ConfiguredBridges(repo repository.RepoCommon) ([]string, error) {
func BridgeExist(repo repository.RepoCommon, name string) bool {
keyPrefix := fmt.Sprintf("git-bug.bridge.%s.", name)
- conf, err := repo.ReadConfigs(keyPrefix)
+ conf, err := repo.LocalConfig().ReadAll(keyPrefix)
return err == nil && len(conf) > 0
}
@@ -188,7 +188,7 @@ func RemoveBridge(repo repository.RepoCommon, name string) error {
}
keyPrefix := fmt.Sprintf("git-bug.bridge.%s", name)
- return repo.RmConfigs(keyPrefix)
+ return repo.LocalConfig().RemoveAll(keyPrefix)
}
// Configure run the target specific configuration process
@@ -211,7 +211,7 @@ func (b *Bridge) storeConfig(conf Configuration) error {
for key, val := range conf {
storeKey := fmt.Sprintf("git-bug.bridge.%s.%s", b.Name, key)
- err := b.repo.StoreConfig(storeKey, val)
+ err := b.repo.LocalConfig().StoreString(storeKey, val)
if err != nil {
return errors.Wrap(err, "error while storing bridge configuration")
}
@@ -235,7 +235,7 @@ func (b *Bridge) ensureConfig() error {
func loadConfig(repo repository.RepoCommon, name string) (Configuration, error) {
keyPrefix := fmt.Sprintf("git-bug.bridge.%s.", name)
- pairs, err := repo.ReadConfigs(keyPrefix)
+ pairs, err := repo.LocalConfig().ReadAll(keyPrefix)
if err != nil {
return nil, errors.Wrap(err, "error while reading bridge configuration")
}
diff --git a/cache/repo_cache.go b/cache/repo_cache.go
index bc095856..ec4cf436 100644
--- a/cache/repo_cache.go
+++ b/cache/repo_cache.go
@@ -99,6 +99,16 @@ func NewRepoCache(r repository.ClockedRepo) (*RepoCache, error) {
return c, c.write()
}
+// LocalConfig give access to the repository scoped configuration
+func (c *RepoCache) LocalConfig() repository.Config {
+ return c.repo.LocalConfig()
+}
+
+// GlobalConfig give access to the git global configuration
+func (c *RepoCache) GlobalConfig() repository.Config {
+ return c.repo.GlobalConfig()
+}
+
// GetPath returns the path to the repo.
func (c *RepoCache) GetPath() string {
return c.repo.GetPath()
@@ -124,31 +134,6 @@ func (c *RepoCache) GetUserEmail() (string, error) {
return c.repo.GetUserEmail()
}
-// StoreConfig store a single key/value pair in the config of the repo
-func (c *RepoCache) StoreConfig(key string, value string) error {
- return c.repo.StoreConfig(key, value)
-}
-
-// ReadConfigs read all key/value pair matching the key prefix
-func (c *RepoCache) ReadConfigs(keyPrefix string) (map[string]string, error) {
- return c.repo.ReadConfigs(keyPrefix)
-}
-
-// ReadConfigBool read a single boolean value from the config
-func (c *RepoCache) ReadConfigBool(key string) (bool, error) {
- return c.repo.ReadConfigBool(key)
-}
-
-// ReadConfigBool read a single string value from the config
-func (c *RepoCache) ReadConfigString(key string) (string, error) {
- return c.repo.ReadConfigString(key)
-}
-
-// RmConfigs remove all key/value pair matching the key prefix
-func (c *RepoCache) RmConfigs(keyPrefix string) error {
- return c.repo.RmConfigs(keyPrefix)
-}
-
func (c *RepoCache) lock() error {
lockPath := repoLockFilePath(c.repo)
diff --git a/commands/webui.go b/commands/webui.go
index d6b6a661..8e735e55 100644
--- a/commands/webui.go
+++ b/commands/webui.go
@@ -100,7 +100,7 @@ func runWebUI(cmd *cobra.Command, args []string) error {
fmt.Printf("Graphql Playground: http://%s/playground\n", addr)
fmt.Println("Press Ctrl+c to quit")
- configOpen, err := repo.ReadConfigBool(webUIOpenConfigKey)
+ configOpen, err := repo.LocalConfig().ReadBool(webUIOpenConfigKey)
if err == repository.ErrNoConfigEntry {
// default to true
configOpen = true
diff --git a/identity/identity.go b/identity/identity.go
index 765b77cd..b7d44a4b 100644
--- a/identity/identity.go
+++ b/identity/identity.go
@@ -220,7 +220,7 @@ func NewFromGitUser(repo repository.Repo) (*Identity, error) {
// IsUserIdentitySet tell if the user identity is correctly set.
func IsUserIdentitySet(repo repository.RepoCommon) (bool, error) {
- configs, err := repo.ReadConfigs(identityConfigKey)
+ configs, err := repo.LocalConfig().ReadAll(identityConfigKey)
if err != nil {
return false, err
}
@@ -234,12 +234,12 @@ func IsUserIdentitySet(repo repository.RepoCommon) (bool, error) {
// SetUserIdentity store the user identity's id in the git config
func SetUserIdentity(repo repository.RepoCommon, identity *Identity) error {
- return repo.StoreConfig(identityConfigKey, identity.Id().String())
+ return repo.LocalConfig().StoreString(identityConfigKey, identity.Id().String())
}
// GetUserIdentity read the current user identity, set with a git config entry
func GetUserIdentity(repo repository.Repo) (*Identity, error) {
- configs, err := repo.ReadConfigs(identityConfigKey)
+ configs, err := repo.LocalConfig().ReadAll(identityConfigKey)
if err != nil {
return nil, err
}
@@ -263,7 +263,7 @@ func GetUserIdentity(repo repository.Repo) (*Identity, error) {
i, err := ReadLocal(repo, id)
if err == ErrIdentityNotExist {
- innerErr := repo.RmConfigs(identityConfigKey)
+ innerErr := repo.LocalConfig().RemoveAll(identityConfigKey)
if innerErr != nil {
_, _ = fmt.Fprintln(os.Stderr, errors.Wrap(innerErr, "can't clear user identity").Error())
}
diff --git a/repository/config.go b/repository/config.go
new file mode 100644
index 00000000..ec5094e0
--- /dev/null
+++ b/repository/config.go
@@ -0,0 +1,49 @@
+package repository
+
+import (
+ "strconv"
+ "time"
+)
+
+// Config represent the common function interacting with the repository config storage
+type Config interface {
+ // Store writes a single key/value pair in the config
+ StoreString(key, value string) error
+
+ // Store writes a key and timestamp value to the config
+ StoreTimestamp(key string, value time.Time) error
+
+ // Store writes a key and boolean value to the config
+ StoreBool(key string, value bool) error
+
+ // ReadAll reads all key/value pair matching the key prefix
+ ReadAll(keyPrefix string) (map[string]string, error)
+
+ // ReadBool read a single boolean value from the config
+ // Return ErrNoConfigEntry or ErrMultipleConfigEntry if
+ // there is zero or more than one entry for this key
+ ReadBool(key string) (bool, error)
+
+ // ReadBool read a single string value from the config
+ // Return ErrNoConfigEntry or ErrMultipleConfigEntry if
+ // there is zero or more than one entry for this key
+ ReadString(key string) (string, error)
+
+ // ReadTimestamp read a single timestamp value from the config
+ // Return ErrNoConfigEntry or ErrMultipleConfigEntry if
+ // there is zero or more than one entry for this key
+ ReadTimestamp(key string) (*time.Time, error)
+
+ // RemoveAll removes all key/value pair matching the key prefix
+ RemoveAll(keyPrefix string) error
+}
+
+func parseTimestamp(s string) (*time.Time, error) {
+ timestamp, err := strconv.Atoi(s)
+ if err != nil {
+ return nil, err
+ }
+
+ t := time.Unix(int64(timestamp), 0)
+ return &t, nil
+}
diff --git a/repository/config_git.go b/repository/config_git.go
new file mode 100644
index 00000000..eac882a2
--- /dev/null
+++ b/repository/config_git.go
@@ -0,0 +1,225 @@
+package repository
+
+import (
+ "fmt"
+ "regexp"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/blang/semver"
+ "github.com/pkg/errors"
+)
+
+var _ Config = &gitConfig{}
+
+type gitConfig struct {
+ repo *GitRepo
+ localityFlag string
+}
+
+func newGitConfig(repo *GitRepo, global bool) *gitConfig {
+ localityFlag := "--local"
+ if global {
+ localityFlag = "--global"
+ }
+ return &gitConfig{
+ repo: repo,
+ localityFlag: localityFlag,
+ }
+}
+
+// StoreString store a single key/value pair in the config of the repo
+func (gc *gitConfig) StoreString(key string, value string) error {
+ _, err := gc.repo.runGitCommand("config", gc.localityFlag, "--replace-all", key, value)
+ return err
+}
+
+func (gc *gitConfig) StoreBool(key string, value bool) error {
+ return gc.StoreString(key, strconv.FormatBool(value))
+}
+
+func (gc *gitConfig) StoreTimestamp(key string, value time.Time) error {
+ return gc.StoreString(key, strconv.Itoa(int(value.Unix())))
+}
+
+// ReadAll read all key/value pair matching the key prefix
+func (gc *gitConfig) ReadAll(keyPrefix string) (map[string]string, error) {
+ stdout, err := gc.repo.runGitCommand("config", gc.localityFlag, "--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.repo.runGitCommand("config", gc.localityFlag, "--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) ReadTimestamp(key string) (*time.Time, error) {
+ value, err := gc.ReadString(key)
+ if err != nil {
+ return nil, err
+ }
+ return parseTimestamp(value)
+}
+
+func (gc *gitConfig) rmSection(keyPrefix string) error {
+ _, err := gc.repo.runGitCommand("config", gc.localityFlag, "--remove-section", keyPrefix)
+ return err
+}
+
+func (gc *gitConfig) unsetAll(keyPrefix string) error {
+ _, err := gc.repo.runGitCommand("config", gc.localityFlag, "--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) gitVersion() (*semver.Version, error) {
+ versionOut, err := gc.repo.runGitCommand("version")
+ if err != nil {
+ return nil, err
+ }
+ return parseGitVersion(versionOut)
+}
+
+func parseGitVersion(versionOut string) (*semver.Version, error) {
+ // extract the version and truncate potential bad parts
+ // ex: 2.23.0.rc1 instead of 2.23.0-rc1
+ r := regexp.MustCompile(`(\d+\.){1,2}\d+`)
+
+ extracted := r.FindString(versionOut)
+ if extracted == "" {
+ return nil, fmt.Errorf("unreadable git version %s", versionOut)
+ }
+
+ version, err := semver.Make(extracted)
+ if err != nil {
+ return nil, err
+ }
+
+ return &version, nil
+}
+
+func (gc *gitConfig) gitVersionLT218() (bool, error) {
+ version, err := gc.gitVersion()
+ if err != nil {
+ return false, err
+ }
+
+ version218string := "2.18.0"
+ gitVersion218, err := semver.Make(version218string)
+ if err != nil {
+ return false, err
+ }
+
+ return version.LT(gitVersion218), nil
+}
diff --git a/repository/config_mem.go b/repository/config_mem.go
new file mode 100644
index 00000000..e2cffd9c
--- /dev/null
+++ b/repository/config_mem.go
@@ -0,0 +1,84 @@
+package repository
+
+import (
+ "strconv"
+ "strings"
+ "time"
+)
+
+var _ Config = &memConfig{}
+
+type memConfig struct {
+ config map[string]string
+}
+
+func newMemConfig(config map[string]string) *memConfig {
+ return &memConfig{config: config}
+}
+
+func (mc *memConfig) StoreString(key, value string) error {
+ mc.config[key] = value
+ return nil
+}
+
+func (mc *memConfig) StoreBool(key string, value bool) error {
+ return mc.StoreString(key, strconv.FormatBool(value))
+}
+
+func (mc *memConfig) StoreTimestamp(key string, value time.Time) error {
+ return mc.StoreString(key, strconv.Itoa(int(value.Unix())))
+}
+
+func (mc *memConfig) ReadAll(keyPrefix string) (map[string]string, error) {
+ result := make(map[string]string)
+ for key, val := range mc.config {
+ if strings.HasPrefix(key, keyPrefix) {
+ result[key] = val
+ }
+ }
+ return result, nil
+}
+
+func (mc *memConfig) ReadString(key string) (string, error) {
+ // unlike git, the mock can only store one value for the same key
+ val, ok := mc.config[key]
+ if !ok {
+ return "", ErrNoConfigEntry
+ }
+
+ return val, nil
+}
+
+func (mc *memConfig) ReadBool(key string) (bool, error) {
+ // unlike git, the mock can only store one value for the same key
+ val, ok := mc.config[key]
+ if !ok {
+ return false, ErrNoConfigEntry
+ }
+
+ return strconv.ParseBool(val)
+}
+
+func (mc *memConfig) ReadTimestamp(key string) (*time.Time, error) {
+ value, err := mc.ReadString(key)
+ if err != nil {
+ return nil, err
+ }
+ timestamp, err := strconv.Atoi(value)
+ if err != nil {
+ return nil, err
+ }
+
+ t := time.Unix(int64(timestamp), 0)
+ return &t, nil
+}
+
+// RmConfigs remove all key/value pair matching the key prefix
+func (mc *memConfig) RemoveAll(keyPrefix string) error {
+ for key := range mc.config {
+ if strings.HasPrefix(key, keyPrefix) {
+ delete(mc.config, key)
+ }
+ }
+ return nil
+}
diff --git a/repository/git.go b/repository/git.go
index 93016cd9..2b00d1f2 100644
--- a/repository/git.go
+++ b/repository/git.go
@@ -7,11 +7,8 @@ import (
"io"
"os/exec"
"path"
- "regexp"
- "strconv"
"strings"
- "github.com/blang/semver"
"github.com/pkg/errors"
"github.com/MichaelMure/git-bug/util/git"
@@ -33,16 +30,26 @@ type GitRepo struct {
editClock *lamport.Persisted
}
+// LocalConfig give access to the repository scoped configuration
+func (repo *GitRepo) LocalConfig() Config {
+ return newGitConfig(repo, false)
+}
+
+// GlobalConfig give access to the git global configuration
+func (repo *GitRepo) GlobalConfig() Config {
+ return newGitConfig(repo, true)
+}
+
// Run the given git command with the given I/O reader/writers, returning an error if it fails.
func (repo *GitRepo) runGitCommandWithIO(stdin io.Reader, stdout, stderr io.Writer, args ...string) error {
- repopath:=repo.Path
- if repopath==".git" {
+ repopath := repo.Path
+ if repopath == ".git" {
// seeduvax> trangely the git command sometimes fail for very unknown
// reason wihtout this replacement.
// observed with rev-list command when git-bug is called from git
// hook script, even the same command with same args runs perfectly
- // when called directly from the same hook script.
- repopath=""
+ // when called directly from the same hook script.
+ repopath = ""
}
// fmt.Printf("[%s] Running git %s\n", repopath, strings.Join(args, " "))
@@ -125,7 +132,7 @@ func NewGitRepo(path string, witnesser Witnesser) (*GitRepo, error) {
// InitGitRepo create a new empty git repo at the given path
func InitGitRepo(path string) (*GitRepo, error) {
- repo := &GitRepo{Path: path+"/.git"}
+ repo := &GitRepo{Path: path + "/.git"}
err := repo.createClocks()
if err != nil {
return nil, err
@@ -197,174 +204,6 @@ func (repo *GitRepo) GetRemotes() (map[string]string, error) {
return remotes, nil
}
-// StoreConfig store a single key/value pair in the config of the repo
-func (repo *GitRepo) StoreConfig(key string, value string) error {
- _, err := repo.runGitCommand("config", "--replace-all", key, value)
-
- return err
-}
-
-// ReadConfigs read all key/value pair matching the key prefix
-func (repo *GitRepo) ReadConfigs(keyPrefix string) (map[string]string, error) {
- stdout, err := repo.runGitCommand("config", "--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 (repo *GitRepo) ReadConfigBool(key string) (bool, error) {
- val, err := repo.ReadConfigString(key)
- if err != nil {
- return false, err
- }
-
- return strconv.ParseBool(val)
-}
-
-func (repo *GitRepo) ReadConfigString(key string) (string, error) {
- stdout, err := repo.runGitCommand("config", "--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 (repo *GitRepo) rmSection(keyPrefix string) error {
- _, err := repo.runGitCommand("config", "--remove-section", keyPrefix)
- return err
-}
-
-func (repo *GitRepo) unsetAll(keyPrefix string) error {
- _, err := repo.runGitCommand("config", "--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 (repo *GitRepo) rmConfigsGitVersionLT218(keyPrefix string) error {
- // try to remove key/value pair by key
- err := repo.unsetAll(keyPrefix)
- if err != nil {
- return repo.rmSection(keyPrefix)
- }
-
- m, err := repo.ReadConfigs(sectionFromKey(keyPrefix))
- if err != nil {
- return err
- }
-
- // if section doesn't have any left key/value remove the section
- if len(m) == 0 {
- return repo.rmSection(sectionFromKey(keyPrefix))
- }
-
- return nil
-}
-
-// RmConfigs remove all key/value pair matching the key prefix
-func (repo *GitRepo) RmConfigs(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 := repo.gitVersionLT218()
- if err != nil {
- return errors.Wrap(err, "getting git version")
- }
-
- if lt218 {
- return repo.rmConfigsGitVersionLT218(keyPrefix)
- }
-
- err = repo.unsetAll(keyPrefix)
- if err != nil {
- return repo.rmSection(keyPrefix)
- }
-
- return nil
-}
-
-func (repo *GitRepo) gitVersionLT218() (bool, error) {
- versionOut, err := repo.runGitCommand("version")
- if err != nil {
- return false, err
- }
-
- // extract the version and truncate potential bad parts
- // ex: 2.23.0.rc1 instead of 2.23.0-rc1
- r := regexp.MustCompile(`(\d+\.){1,2}\d+`)
-
- extracted := r.FindString(versionOut)
- if extracted == "" {
- return false, fmt.Errorf("unreadable git version %s", versionOut)
- }
-
- version, err := semver.Make(extracted)
- if err != nil {
- return false, err
- }
-
- version218string := "2.18.0"
- gitVersion218, err := semver.Make(version218string)
- if err != nil {
- return false, err
- }
-
- return version.LT(gitVersion218), nil
-}
-
// FetchRefs fetch git refs from a remote
func (repo *GitRepo) FetchRefs(remote, refSpec string) (string, error) {
stdout, err := repo.runGitCommand("fetch", remote, refSpec)
diff --git a/repository/git_test.go b/repository/git_test.go
index 20bf6ec3..de442e39 100644
--- a/repository/git_test.go
+++ b/repository/git_test.go
@@ -11,56 +11,57 @@ func TestConfig(t *testing.T) {
repo := CreateTestRepo(false)
defer CleanupTestRepos(t, repo)
- err := repo.StoreConfig("section.key", "value")
+ err := repo.LocalConfig().StoreString("section.key", "value")
assert.NoError(t, err)
- val, err := repo.ReadConfigString("section.key")
+ val, err := repo.LocalConfig().ReadString("section.key")
+ assert.NoError(t, err)
assert.Equal(t, "value", val)
- err = repo.StoreConfig("section.true", "true")
+ err = repo.LocalConfig().StoreString("section.true", "true")
assert.NoError(t, err)
- val2, err := repo.ReadConfigBool("section.true")
+ val2, err := repo.LocalConfig().ReadBool("section.true")
+ assert.NoError(t, err)
assert.Equal(t, true, val2)
- configs, err := repo.ReadConfigs("section")
+ configs, err := repo.LocalConfig().ReadAll("section")
assert.NoError(t, err)
assert.Equal(t, configs, map[string]string{
"section.key": "value",
"section.true": "true",
})
- err = repo.RmConfigs("section.true")
+ err = repo.LocalConfig().RemoveAll("section.true")
assert.NoError(t, err)
- configs, err = repo.ReadConfigs("section")
+ configs, err = repo.LocalConfig().ReadAll("section")
assert.NoError(t, err)
assert.Equal(t, configs, map[string]string{
"section.key": "value",
})
- _, err = repo.ReadConfigBool("section.true")
+ _, err = repo.LocalConfig().ReadBool("section.true")
assert.Equal(t, ErrNoConfigEntry, err)
- err = repo.RmConfigs("section.nonexistingkey")
+ err = repo.LocalConfig().RemoveAll("section.nonexistingkey")
assert.Error(t, err)
- err = repo.RmConfigs("section.key")
+ err = repo.LocalConfig().RemoveAll("section.key")
assert.NoError(t, err)
- _, err = repo.ReadConfigString("section.key")
+ _, err = repo.LocalConfig().ReadString("section.key")
assert.Equal(t, ErrNoConfigEntry, err)
- err = repo.RmConfigs("nonexistingsection")
+ err = repo.LocalConfig().RemoveAll("nonexistingsection")
assert.Error(t, err)
- err = repo.RmConfigs("section")
+ err = repo.LocalConfig().RemoveAll("section")
assert.Error(t, err)
- _, err = repo.ReadConfigString("section.key")
+ _, err = repo.LocalConfig().ReadString("section.key")
assert.Error(t, err)
- err = repo.RmConfigs("section.key")
+ err = repo.LocalConfig().RemoveAll("section.key")
assert.Error(t, err)
-
}
diff --git a/repository/git_testing.go b/repository/git_testing.go
index 36c53c1d..37a15d93 100644
--- a/repository/git_testing.go
+++ b/repository/git_testing.go
@@ -31,10 +31,11 @@ func CreateTestRepo(bare bool) *GitRepo {
log.Fatal(err)
}
- if err := repo.StoreConfig("user.name", "testuser"); err != nil {
+ config := repo.LocalConfig()
+ if err := config.StoreString("user.name", "testuser"); err != nil {
log.Fatal("failed to set user.name for test repository: ", err)
}
- if err := repo.StoreConfig("user.email", "testuser@example.com"); err != nil {
+ if err := config.StoreString("user.email", "testuser@example.com"); err != nil {
log.Fatal("failed to set user.email for test repository: ", err)
}
diff --git a/repository/mock_repo.go b/repository/mock_repo.go
index 23534b89..26c02ede 100644
--- a/repository/mock_repo.go
+++ b/repository/mock_repo.go
@@ -3,8 +3,6 @@ package repository
import (
"crypto/sha1"
"fmt"
- "strconv"
- "strings"
"github.com/MichaelMure/git-bug/util/git"
"github.com/MichaelMure/git-bug/util/lamport"
@@ -14,13 +12,14 @@ var _ ClockedRepo = &mockRepoForTest{}
// mockRepoForTest defines an instance of Repo that can be used for testing.
type mockRepoForTest struct {
- config map[string]string
- blobs map[git.Hash][]byte
- trees map[git.Hash]string
- commits map[git.Hash]commit
- refs map[string]git.Hash
- createClock lamport.Clock
- editClock lamport.Clock
+ config map[string]string
+ globalConfig map[string]string
+ blobs map[git.Hash][]byte
+ trees map[git.Hash]string
+ commits map[git.Hash]commit
+ refs map[string]git.Hash
+ createClock lamport.Clock
+ editClock lamport.Clock
}
type commit struct {
@@ -40,6 +39,16 @@ func NewMockRepoForTest() *mockRepoForTest {
}
}
+// LocalConfig give access to the repository scoped configuration
+func (r *mockRepoForTest) LocalConfig() Config {
+ return newMemConfig(r.config)
+}
+
+// GlobalConfig give access to the git global configuration
+func (r *mockRepoForTest) GlobalConfig() Config {
+ return newMemConfig(r.globalConfig)
+}
+
// GetPath returns the path to the repo.
func (r *mockRepoForTest) GetPath() string {
return "~/mockRepo/"
@@ -66,53 +75,6 @@ func (r *mockRepoForTest) GetRemotes() (map[string]string, error) {
}, nil
}
-func (r *mockRepoForTest) StoreConfig(key string, value string) error {
- r.config[key] = value
- return nil
-}
-
-func (r *mockRepoForTest) ReadConfigs(keyPrefix string) (map[string]string, error) {
- result := make(map[string]string)
-
- for key, val := range r.config {
- if strings.HasPrefix(key, keyPrefix) {
- result[key] = val
- }
- }
-
- return result, nil
-}
-
-func (r *mockRepoForTest) ReadConfigBool(key string) (bool, error) {
- // unlike git, the mock can only store one value for the same key
- val, ok := r.config[key]
- if !ok {
- return false, ErrNoConfigEntry
- }
-
- return strconv.ParseBool(val)
-}
-
-func (r *mockRepoForTest) ReadConfigString(key string) (string, error) {
- // unlike git, the mock can only store one value for the same key
- val, ok := r.config[key]
- if !ok {
- return "", ErrNoConfigEntry
- }
-
- return val, nil
-}
-
-// RmConfigs remove all key/value pair matching the key prefix
-func (r *mockRepoForTest) RmConfigs(keyPrefix string) error {
- for key := range r.config {
- if strings.HasPrefix(key, keyPrefix) {
- delete(r.config, key)
- }
- }
- return nil
-}
-
// PushRefs push git refs to a remote
func (r *mockRepoForTest) PushRefs(remote string, refSpec string) (string, error) {
return "", nil
diff --git a/repository/repo.go b/repository/repo.go
index 44204493..7d655bde 100644
--- a/repository/repo.go
+++ b/repository/repo.go
@@ -30,24 +30,11 @@ type RepoCommon interface {
// GetRemotes returns the configured remotes repositories.
GetRemotes() (map[string]string, error)
- // StoreConfig store a single key/value pair in the config of the repo
- StoreConfig(key string, value string) error
+ // LocalConfig give access to the repository scoped configuration
+ LocalConfig() Config
- // ReadConfigs read all key/value pair matching the key prefix
- ReadConfigs(keyPrefix string) (map[string]string, error)
-
- // ReadConfigBool read a single boolean value from the config
- // Return ErrNoConfigEntry or ErrMultipleConfigEntry if there is zero or more than one entry
- // for this key
- ReadConfigBool(key string) (bool, error)
-
- // ReadConfigBool read a single string value from the config
- // Return ErrNoConfigEntry or ErrMultipleConfigEntry if there is zero or more than one entry
- // for this key
- ReadConfigString(key string) (string, error)
-
- // RmConfigs remove all key/value pair matching the key prefix
- RmConfigs(keyPrefix string) error
+ // GlobalConfig give access to the git global configuration
+ GlobalConfig() Config
}
// Repo represents a source code repository.