mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-07-30 03:23:08 +03:00
use cached git config
This commit is contained in:
59
pkg/commands/git_config/cached_git_config.go
Normal file
59
pkg/commands/git_config/cached_git_config.go
Normal file
@ -0,0 +1,59 @@
|
||||
package git_config
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type IGitConfig interface {
|
||||
Get(string) string
|
||||
GetBool(string) bool
|
||||
}
|
||||
|
||||
type CachedGitConfig struct {
|
||||
cache map[string]string
|
||||
getKey func(string) (string, error)
|
||||
log *logrus.Entry
|
||||
}
|
||||
|
||||
func NewStdCachedGitConfig(log *logrus.Entry) *CachedGitConfig {
|
||||
return NewCachedGitConfig(getGitConfigValue, log)
|
||||
}
|
||||
|
||||
func NewCachedGitConfig(getKey func(string) (string, error), log *logrus.Entry) *CachedGitConfig {
|
||||
return &CachedGitConfig{
|
||||
cache: make(map[string]string),
|
||||
getKey: getKey,
|
||||
log: log,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *CachedGitConfig) Get(key string) string {
|
||||
if value, ok := self.cache[key]; ok {
|
||||
self.log.Debugf("using cache for key " + key)
|
||||
return value
|
||||
}
|
||||
|
||||
value := self.getAux(key)
|
||||
self.cache[key] = value
|
||||
return value
|
||||
}
|
||||
|
||||
func (self *CachedGitConfig) getAux(key string) string {
|
||||
value, err := self.getKey(key)
|
||||
if err != nil {
|
||||
self.log.Debugf("Error getting git config value for key: " + key + ". Error: " + err.Error())
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(value)
|
||||
}
|
||||
|
||||
func (self *CachedGitConfig) GetBool(key string) bool {
|
||||
return isTruthy(self.Get(key))
|
||||
}
|
||||
|
||||
func isTruthy(value string) bool {
|
||||
lcValue := strings.ToLower(value)
|
||||
return lcValue == "true" || lcValue == "1" || lcValue == "yes" || lcValue == "on"
|
||||
}
|
116
pkg/commands/git_config/cached_git_config_test.go
Normal file
116
pkg/commands/git_config/cached_git_config_test.go
Normal file
@ -0,0 +1,116 @@
|
||||
package git_config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetBool(t *testing.T) {
|
||||
type scenario struct {
|
||||
testName string
|
||||
mockResponses map[string]string
|
||||
expected bool
|
||||
}
|
||||
|
||||
scenarios := []scenario{
|
||||
{
|
||||
"Option global and local config commit.gpgsign is not set",
|
||||
map[string]string{},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"Some other random key is set",
|
||||
map[string]string{"blah": "blah"},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"Option commit.gpgsign is true",
|
||||
map[string]string{"commit.gpgsign": "True"},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"Option commit.gpgsign is on",
|
||||
map[string]string{"commit.gpgsign": "ON"},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"Option commit.gpgsign is yes",
|
||||
map[string]string{"commit.gpgsign": "YeS"},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"Option commit.gpgsign is 1",
|
||||
map[string]string{"commit.gpgsign": "1"},
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
s := s
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
fake := NewFakeGitConfig(s.mockResponses)
|
||||
real := NewCachedGitConfig(
|
||||
func(key string) (string, error) {
|
||||
return fake.Get(key), nil
|
||||
},
|
||||
utils.NewDummyLog(),
|
||||
)
|
||||
result := real.GetBool("commit.gpgsign")
|
||||
assert.Equal(t, s.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
type scenario struct {
|
||||
testName string
|
||||
mockResponses map[string]string
|
||||
expected string
|
||||
}
|
||||
|
||||
scenarios := []scenario{
|
||||
{
|
||||
"not set",
|
||||
map[string]string{},
|
||||
"",
|
||||
},
|
||||
{
|
||||
"is set",
|
||||
map[string]string{"commit.gpgsign": "blah"},
|
||||
"blah",
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
s := s
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
fake := NewFakeGitConfig(s.mockResponses)
|
||||
real := NewCachedGitConfig(
|
||||
func(key string) (string, error) {
|
||||
return fake.Get(key), nil
|
||||
},
|
||||
utils.NewDummyLog(),
|
||||
)
|
||||
result := real.Get("commit.gpgsign")
|
||||
assert.Equal(t, s.expected, result)
|
||||
})
|
||||
}
|
||||
|
||||
// verifying that the cache is used
|
||||
count := 0
|
||||
real := NewCachedGitConfig(
|
||||
func(key string) (string, error) {
|
||||
count++
|
||||
assert.Equal(t, "commit.gpgsign", key)
|
||||
return "blah", nil
|
||||
},
|
||||
utils.NewDummyLog(),
|
||||
)
|
||||
result := real.Get("commit.gpgsign")
|
||||
assert.Equal(t, "blah", result)
|
||||
result = real.Get("commit.gpgsign")
|
||||
assert.Equal(t, "blah", result)
|
||||
assert.Equal(t, 1, count)
|
||||
}
|
22
pkg/commands/git_config/fake_git_config.go
Normal file
22
pkg/commands/git_config/fake_git_config.go
Normal file
@ -0,0 +1,22 @@
|
||||
package git_config
|
||||
|
||||
type FakeGitConfig struct {
|
||||
mockResponses map[string]string
|
||||
}
|
||||
|
||||
func NewFakeGitConfig(mockResponses map[string]string) *FakeGitConfig {
|
||||
return &FakeGitConfig{
|
||||
mockResponses: mockResponses,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *FakeGitConfig) Get(key string) string {
|
||||
if self.mockResponses == nil {
|
||||
return ""
|
||||
}
|
||||
return self.mockResponses[key]
|
||||
}
|
||||
|
||||
func (self *FakeGitConfig) GetBool(key string) bool {
|
||||
return isTruthy(self.Get(key))
|
||||
}
|
56
pkg/commands/git_config/get_key.go
Normal file
56
pkg/commands/git_config/get_key.go
Normal file
@ -0,0 +1,56 @@
|
||||
package git_config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/secureexec"
|
||||
)
|
||||
|
||||
// including license from https://github.com/tcnksm/go-gitconfig because this file is an adaptation of that repo's code
|
||||
// Copyright (c) 2014 tcnksm
|
||||
|
||||
// MIT License
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
func getGitConfigValue(key string) (string, error) {
|
||||
gitArgs := []string{"config", "--get", "--null", key}
|
||||
var stdout bytes.Buffer
|
||||
cmd := secureexec.Command("git", gitArgs...)
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = ioutil.Discard
|
||||
|
||||
err := cmd.Run()
|
||||
if exitError, ok := err.(*exec.ExitError); ok {
|
||||
if waitStatus, ok := exitError.Sys().(syscall.WaitStatus); ok {
|
||||
if waitStatus.ExitStatus() == 1 {
|
||||
return "", fmt.Errorf("the key `%s` is not found", key)
|
||||
}
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
return strings.TrimRight(stdout.String(), "\000"), nil
|
||||
}
|
Reference in New Issue
Block a user