1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
|
package git
import (
"bytes"
"errors"
"fmt"
"strconv"
"strings"
)
// ConfigEntry represents an entry in the gitconfig.
type ConfigEntry struct {
// Key is the entry's key, with any common `prefix` removed (see
// `Config()`).
Key string
// Value is the entry's value, as a string.
Value string
}
// Config represents the gitconfig, or part of the gitconfig, read by
// `ReadConfig()`.
type Config struct {
// Prefix is the key prefix that was read to fill this `Config`.
Prefix string
// Entries contains the configuration entries that matched
// `Prefix`, in the order that they are reported by `git config
// --list`.
Entries []ConfigEntry
}
// GetConfig returns the entries from gitconfig. If `prefix` is
// provided, then only include entries in that section, which must
// match the at a component boundary (as defined by
// `configKeyMatchesPrefix()`), and strip off the prefix in the keys
// that are returned.
func (repo *Repository) GetConfig(prefix string) (*Config, error) {
cmd := repo.GitCommand("config", "--list", "-z")
out, err := cmd.Output()
if err != nil {
return nil, fmt.Errorf("reading git configuration: %w", err)
}
config := Config{
Prefix: prefix,
}
for len(out) > 0 {
keyEnd := bytes.IndexByte(out, '\n')
if keyEnd == -1 {
return nil, errors.New("invalid output from 'git config'")
}
key := string(out[:keyEnd])
out = out[keyEnd+1:]
valueEnd := bytes.IndexByte(out, 0)
if valueEnd == -1 {
return nil, errors.New("invalid output from 'git config'")
}
value := string(out[:valueEnd])
out = out[valueEnd+1:]
ok, rest := configKeyMatchesPrefix(key, prefix)
if !ok {
continue
}
entry := ConfigEntry{
Key: rest,
Value: value,
}
config.Entries = append(config.Entries, entry)
}
return &config, nil
}
// FullKey returns the full gitconfig key name for the relative key
// name `key`.
func (config *Config) FullKey(key string) string {
if config.Prefix == "" {
return key
}
return fmt.Sprintf("%s.%s", config.Prefix, key)
}
// configKeyMatchesPrefix checks whether `key` starts with `prefix` at
// a component boundary (i.e., at a '.'). If yes, it returns `true`
// and the part of the key after the prefix; e.g.:
//
// configKeyMatchesPrefix("foo.bar", "foo") → true, "bar"
// configKeyMatchesPrefix("foo.bar", "foo.") → true, "bar"
// configKeyMatchesPrefix("foo.bar", "foo.bar") → true, ""
// configKeyMatchesPrefix("foo.bar", "foo.bar.") → false, ""
func configKeyMatchesPrefix(key, prefix string) (bool, string) {
if prefix == "" {
return true, key
}
if !strings.HasPrefix(key, prefix) {
return false, ""
}
if prefix[len(prefix)-1] == '.' {
return true, key[len(prefix):]
}
if len(key) == len(prefix) {
return true, ""
}
if key[len(prefix)] == '.' {
return true, key[len(prefix)+1:]
}
return false, ""
}
func (repo *Repository) ConfigStringDefault(key string, defaultValue string) (string, error) {
cmd := repo.GitCommand(
"config",
"--default", defaultValue,
key,
)
out, err := cmd.Output()
if err != nil {
return defaultValue, fmt.Errorf("running 'git config': %w", err)
}
if len(out) > 0 && out[len(out)-1] == '\n' {
out = out[:len(out)-1]
}
return string(out), nil
}
func (repo *Repository) ConfigBoolDefault(key string, defaultValue bool) (bool, error) {
cmd := repo.GitCommand(
"config",
"--type", "bool",
"--default", strconv.FormatBool(defaultValue),
key,
)
out, err := cmd.Output()
if err != nil {
return defaultValue, fmt.Errorf("running 'git config': %w", err)
}
s := string(bytes.TrimSpace(out))
value, err := strconv.ParseBool(s)
if err != nil {
return defaultValue, fmt.Errorf("unexpected bool value from 'git config': %q", s)
}
return value, nil
}
func (repo *Repository) ConfigIntDefault(key string, defaultValue int) (int, error) {
cmd := repo.GitCommand(
"config",
"--type", "int",
"--default", strconv.Itoa(defaultValue),
key,
)
out, err := cmd.Output()
if err != nil {
return defaultValue, fmt.Errorf("running 'git config': %w", err)
}
s := string(bytes.TrimSpace(out))
value, err := strconv.Atoi(s)
if err != nil {
return defaultValue, fmt.Errorf("unexpected int value from 'git config': %q", s)
}
return value, nil
}
|