repository: add StoreTimestamp/StoreBool to the config interface

amine created

repository: move the gitVersion logic to *gitConfig struct

Change summary

repository/config.go      | 25 ++++++++++-
repository/config_git.go  | 86 ++++++++++++++++++++++++++--------------
repository/config_mem.go  | 12 +++++
repository/git.go         | 40 -------------------
repository/git_test.go    |  4 
repository/git_testing.go |  4 
6 files changed, 93 insertions(+), 78 deletions(-)

Detailed changes

repository/config.go 🔗

@@ -1,11 +1,20 @@
 package repository
 
-import "time"
+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 of the repo
-	Store(key string, value string) error
+	// 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)
@@ -28,3 +37,13 @@ type Config interface {
 	// 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
+}

repository/config_git.go 🔗

@@ -2,6 +2,7 @@ package repository
 
 import (
 	"fmt"
+	"regexp"
 	"strconv"
 	"strings"
 	"time"
@@ -13,42 +14,41 @@ import (
 var _ Config = &gitConfig{}
 
 type gitConfig struct {
-	version *semver.Version
-	execFn  func(args ...string) (string, error)
+	execFn func(args ...string) (string, error)
 }
 
 func newGitConfig(repo *GitRepo, global bool) *gitConfig {
-	version, _ := repo.GitVersion()
-
+	configCmdFlag := "--local"
 	if global {
-		return &gitConfig{
-			execFn: func(args ...string) (string, error) {
-				args = append([]string{"config", "--global"}, args...)
-				return repo.runGitCommand(args...)
-			},
-			version: version,
-		}
+		configCmdFlag = "--global"
 	}
-
 	return &gitConfig{
 		execFn: func(args ...string) (string, error) {
-			args = append([]string{"config", "--local"}, args...)
+			if len(args) > 0 && args[0] == "config" {
+				args = append([]string{args[0], configCmdFlag}, args[1:]...)
+			}
 			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)
-
+func (gc *gitConfig) StoreString(key string, value string) error {
+	_, err := gc.execFn("config", "--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())))
+}
+
 // 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)
+	stdout, err := gc.execFn("config", "--get-regexp", keyPrefix)
 
 	//   / \
 	//  / ! \
@@ -81,7 +81,7 @@ func (gc *gitConfig) ReadAll(keyPrefix string) (map[string]string, error) {
 }
 
 func (gc *gitConfig) ReadString(key string) (string, error) {
-	stdout, err := gc.execFn("--get-all", key)
+	stdout, err := gc.execFn("config", "--get-all", key)
 
 	//   / \
 	//  / ! \
@@ -119,22 +119,16 @@ func (gc *gitConfig) ReadTimestamp(key string) (*time.Time, error) {
 	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
+	return parseTimestamp(value)
 }
 
 func (gc *gitConfig) rmSection(keyPrefix string) error {
-	_, err := gc.execFn("--remove-section", keyPrefix)
+	_, err := gc.execFn("config", "--remove-section", keyPrefix)
 	return err
 }
 
 func (gc *gitConfig) unsetAll(keyPrefix string) error {
-	_, err := gc.execFn("--unset-all", keyPrefix)
+	_, err := gc.execFn("config", "--unset-all", keyPrefix)
 	return err
 }
 
@@ -192,11 +186,43 @@ func (gc *gitConfig) RemoveAll(keyPrefix string) error {
 	return nil
 }
 
+func (gc *gitConfig) gitVersion() (*semver.Version, error) {
+	versionOut, err := gc.execFn("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) {
-	gitVersion218, err := semver.Make("2.18.0")
+	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 gc.version.LT(gitVersion218), nil
+	return version.LT(gitVersion218), nil
 }

repository/config_mem.go 🔗

@@ -6,6 +6,8 @@ import (
 	"time"
 )
 
+var _ Config = &memConfig{}
+
 type memConfig struct {
 	config map[string]string
 }
@@ -14,11 +16,19 @@ func newMemConfig(config map[string]string) *memConfig {
 	return &memConfig{config: config}
 }
 
-func (mc *memConfig) Store(key, value string) error {
+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 {

repository/git.go 🔗

@@ -7,10 +7,8 @@ import (
 	"io"
 	"os/exec"
 	"path"
-	"regexp"
 	"strings"
 
-	"github.com/blang/semver"
 	"github.com/pkg/errors"
 
 	"github.com/MichaelMure/git-bug/util/git"
@@ -206,44 +204,6 @@ func (repo *GitRepo) GetRemotes() (map[string]string, error) {
 	return remotes, nil
 }
 
-func (repo *GitRepo) GitVersion() (*semver.Version, error) {
-	versionOut, err := repo.runGitCommand("version")
-	if err != nil {
-		return nil, 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 nil, fmt.Errorf("unreadable git version %s", versionOut)
-	}
-
-	version, err := semver.Make(extracted)
-	if err != nil {
-		return nil, err
-	}
-
-	return &version, nil
-}
-
-func (repo *GitRepo) gitVersionLT218() (bool, error) {
-	version, err := repo.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
-}
-
 // FetchRefs fetch git refs from a remote
 func (repo *GitRepo) FetchRefs(remote, refSpec string) (string, error) {
 	stdout, err := repo.runGitCommand("fetch", remote, refSpec)

repository/git_test.go 🔗

@@ -13,13 +13,13 @@ func TestConfig(t *testing.T) {
 
 	config := repo.LocalConfig()
 
-	err := config.Store("section.key", "value")
+	err := config.StoreString("section.key", "value")
 	assert.NoError(t, err)
 
 	val, err := config.ReadString("section.key")
 	assert.Equal(t, "value", val)
 
-	err = config.Store("section.true", "true")
+	err = config.StoreString("section.true", "true")
 	assert.NoError(t, err)
 
 	val2, err := config.ReadBool("section.true")

repository/git_testing.go 🔗

@@ -32,10 +32,10 @@ func CreateTestRepo(bare bool) *GitRepo {
 	}
 
 	config := repo.LocalConfig()
-	if err := config.Store("user.name", "testuser"); err != nil {
+	if err := config.StoreString("user.name", "testuser"); err != nil {
 		log.Fatal("failed to set user.name for test repository: ", err)
 	}
-	if err := config.Store("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)
 	}