mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-08-24 10:13:41 -05:00
Pull git-flow prefix parsing into a config-level helper
Lift the inline parsing in FinishCmdObj into parseGitFlowPrefixMap on ConfigCommands. The caller now does a direct map lookup against the parsed prefix → branchType map instead of iterating the raw config output and suffix-matching. This is preparation for adding git-flow-next support, which needs to merge a second config schema into the same map. One incidental change: a branch name without a slash now returns NotAGitFlowBranch immediately, rather than falling through the line loop with an empty suffix. Previously a configured gitflow.prefix.X whose value happened to equal the entire branch name could match — never a useful outcome.
This commit is contained in:
committed by
Stefan Haller
parent
53e39dc7b8
commit
415015c66a
@@ -1,6 +1,7 @@
|
||||
package git_commands
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_config"
|
||||
@@ -116,6 +117,42 @@ func (self *ConfigCommands) GetGitFlowPrefixes() string {
|
||||
return self.gitConfig.GetGeneral("--local --get-regexp gitflow.prefix")
|
||||
}
|
||||
|
||||
// parseGitFlowPrefixMap parses git-flow config output into a prefix → branchType map.
|
||||
// Line format: "gitflow.prefix.<type> <prefix>". Prefixes are normalized to end in "/".
|
||||
func parseGitFlowPrefixMap(legacyOutput string) map[string]string {
|
||||
legacyRegexp := regexp.MustCompile(`gitflow\.prefix\.(\S+)\s+(.*)`)
|
||||
prefixToType := make(map[string]string)
|
||||
for line := range strings.SplitSeq(legacyOutput, "\n") {
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
if m := legacyRegexp.FindStringSubmatch(line); len(m) == 3 {
|
||||
prefix := normalizeGitFlowPrefix(m[2])
|
||||
if prefix == "" {
|
||||
continue
|
||||
}
|
||||
prefixToType[prefix] = m[1]
|
||||
}
|
||||
}
|
||||
return prefixToType
|
||||
}
|
||||
|
||||
func normalizeGitFlowPrefix(prefix string) string {
|
||||
prefix = strings.TrimSpace(prefix)
|
||||
if prefix == "" {
|
||||
return ""
|
||||
}
|
||||
if !strings.HasSuffix(prefix, "/") {
|
||||
return prefix + "/"
|
||||
}
|
||||
return prefix
|
||||
}
|
||||
|
||||
func (self *ConfigCommands) GetGitFlowPrefixMap() map[string]string {
|
||||
return parseGitFlowPrefixMap(self.GetGitFlowPrefixes())
|
||||
}
|
||||
|
||||
func (self *ConfigCommands) GetCoreCommentChar() byte {
|
||||
if commentCharStr := self.gitConfig.Get("core.commentChar"); len(commentCharStr) == 1 {
|
||||
return commentCharStr[0]
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
package git_commands
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_config"
|
||||
"github.com/jesseduffield/lazygit/pkg/common"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestParseGitFlowPrefixMap(t *testing.T) {
|
||||
type scenario struct {
|
||||
testName string
|
||||
legacyOutput string
|
||||
expected map[string]string
|
||||
}
|
||||
scenarios := []scenario{
|
||||
{
|
||||
testName: "empty input",
|
||||
legacyOutput: "",
|
||||
expected: map[string]string{},
|
||||
},
|
||||
{
|
||||
testName: "feature and hotfix",
|
||||
legacyOutput: "gitflow.prefix.feature feature/\ngitflow.prefix.hotfix hotfix/",
|
||||
expected: map[string]string{"feature/": "feature", "hotfix/": "hotfix"},
|
||||
},
|
||||
{
|
||||
testName: "prefix normalized with trailing slash",
|
||||
legacyOutput: "gitflow.prefix.feature feature",
|
||||
expected: map[string]string{"feature/": "feature"},
|
||||
},
|
||||
{
|
||||
testName: "malformed lines skipped",
|
||||
legacyOutput: "gitflow.prefix.feature feature/\nnot-a-valid-line\ngitflow.prefix.hotfix hotfix/",
|
||||
expected: map[string]string{"feature/": "feature", "hotfix/": "hotfix"},
|
||||
},
|
||||
{
|
||||
testName: "blank lines and whitespace ignored",
|
||||
legacyOutput: " \n gitflow.prefix.feature feature/ \n \n ",
|
||||
expected: map[string]string{"feature/": "feature"},
|
||||
},
|
||||
}
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
got := parseGitFlowPrefixMap(s.legacyOutput)
|
||||
assert.Equal(t, s.expected, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetGitFlowPrefixMap(t *testing.T) {
|
||||
type scenario struct {
|
||||
testName string
|
||||
gitConfigMockResponses map[string]string
|
||||
expected map[string]string
|
||||
}
|
||||
scenarios := []scenario{
|
||||
{
|
||||
testName: "empty when no config",
|
||||
gitConfigMockResponses: nil,
|
||||
expected: map[string]string{},
|
||||
},
|
||||
{
|
||||
testName: "correct map from legacy output",
|
||||
gitConfigMockResponses: map[string]string{
|
||||
"--local --get-regexp gitflow.prefix": "gitflow.prefix.feature feature/\ngitflow.prefix.hotfix hotfix/",
|
||||
},
|
||||
expected: map[string]string{"feature/": "feature", "hotfix/": "hotfix"},
|
||||
},
|
||||
{
|
||||
testName: "prefix normalized with trailing slash",
|
||||
gitConfigMockResponses: map[string]string{
|
||||
"--local --get-regexp gitflow.prefix": "gitflow.prefix.feature feature",
|
||||
},
|
||||
expected: map[string]string{"feature/": "feature"},
|
||||
},
|
||||
{
|
||||
testName: "malformed lines skipped",
|
||||
gitConfigMockResponses: map[string]string{
|
||||
"--local --get-regexp gitflow.prefix": "gitflow.prefix.feature feature/\nnot-a-valid-line\n",
|
||||
},
|
||||
expected: map[string]string{"feature/": "feature"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
config := NewConfigCommands(common.NewDummyCommon(), git_config.NewFakeGitConfig(s.gitConfigMockResponses))
|
||||
got := config.GetGitFlowPrefixMap()
|
||||
assert.Equal(t, s.expected, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package git_commands
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
@@ -25,26 +24,15 @@ func (self *FlowCommands) GitFlowEnabled() bool {
|
||||
}
|
||||
|
||||
func (self *FlowCommands) FinishCmdObj(branchName string) (*oscommands.CmdObj, error) {
|
||||
prefixes := self.config.GetGitFlowPrefixes()
|
||||
prefixMap := self.config.GetGitFlowPrefixMap()
|
||||
|
||||
// need to find out what kind of branch this is
|
||||
prefix := strings.SplitAfterN(branchName, "/", 2)[0]
|
||||
suffix := strings.Replace(branchName, prefix, "", 1)
|
||||
|
||||
branchType := ""
|
||||
for line := range strings.SplitSeq(strings.TrimSpace(prefixes), "\n") {
|
||||
if strings.HasPrefix(line, "gitflow.prefix.") && strings.HasSuffix(line, prefix) {
|
||||
|
||||
regex := regexp.MustCompile("gitflow.prefix.([^ ]*) .*")
|
||||
matches := regex.FindAllStringSubmatch(line, 1)
|
||||
|
||||
if len(matches) > 0 && len(matches[0]) > 1 {
|
||||
branchType = matches[0][1]
|
||||
break
|
||||
}
|
||||
}
|
||||
prefixPart, suffix, ok := strings.Cut(branchName, "/")
|
||||
if !ok || prefixPart == "" || suffix == "" {
|
||||
return nil, errors.New(self.Tr.NotAGitFlowBranch)
|
||||
}
|
||||
prefix := prefixPart + "/"
|
||||
|
||||
branchType := prefixMap[prefix]
|
||||
if branchType == "" {
|
||||
return nil, errors.New(self.Tr.NotAGitFlowBranch)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user