diff --git a/docs/Config.md b/docs/Config.md index 5d034695bcd..29b3dd6e345 100644 --- a/docs/Config.md +++ b/docs/Config.md @@ -341,14 +341,6 @@ git: # If true, do not allow force pushes disableForcePushing: false - # See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#predefined-commit-message-prefix - commitPrefix: - # pattern to match on. E.g. for 'feature/AB-123' to match on the AB-123 use "^\\w+\\/(\\w+-\\w+).*" - pattern: "" - - # Replace directive. E.g. for 'feature/AB-123' to start the commit message with 'AB-123 ' use "[$1] " - replace: "" - # See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#predefined-branch-name-prefix branchPrefix: "" @@ -931,20 +923,29 @@ Example: ```yaml git: commitPrefix: - pattern: "^\\w+\\/(\\w+-\\w+).*" - replace: '[$1] ' + - pattern: "^\\w+\\/(\\w+-\\w+).*" + replace: '[$1] ' ``` -If you want repository-specific prefixes, you can map them with `commitPrefixes`. If you have both `commitPrefixes` defined and an entry in `commitPrefixes` for the current repo, the `commitPrefixes` entry is given higher precedence. Repository folder names must be an exact match. +If you want repository-specific prefixes, you can map them with `commitPrefixes`. If you have both entries in `commitPrefix` defined and an repository match in `commitPrefixes` for the current repo, the `commitPrefixes` entry is given higher precedence. Repository folder names must be an exact match. ```yaml git: commitPrefixes: my_project: # This is repository folder name - pattern: "^\\w+\\/(\\w+-\\w+).*" - replace: '[$1] ' + - pattern: "^\\w+\\/(\\w+-\\w+).*" + replace: '[$1] ' + commitPrefix: + - pattern: "^(\\w+)-.*" # A more general match for any leading word + replace : '[$1] ' + - pattern: ".*" # The final fallthrough regex that copies over the whole branch name + replace : '[$0] ' ``` +> Outside of `my_project`, only the `commitPrefix` entry will be attempted. +Inside of `my_project`, the repository specific pattern will be attempted. +If there is no match, the `commitPrefix` entries will then be attempted in order until a match is found + > [!IMPORTANT] > The way golang regex works is when you use `$n` in the replacement string, where `n` is a number, it puts the nth captured subgroup at that place. If `n` is out of range because there aren't that many capture groups in the regex, it puts an empty string there. > diff --git a/pkg/config/app_config.go b/pkg/config/app_config.go index 5d240b87d04..bf85184f646 100644 --- a/pkg/config/app_config.go +++ b/pkg/config/app_config.go @@ -212,6 +212,22 @@ func loadUserConfig(configFiles []*ConfigFile, base *UserConfig) (*UserConfig, e return base, nil } +func (a *CommitPrefixConfigs) UnmarshalYAML(value *yaml.Node) error { + var multi []CommitPrefixConfig + err := value.Decode(&multi) + if err != nil { + var single CommitPrefixConfig + err := value.Decode(&single) + if err != nil { + return err + } + *a = []CommitPrefixConfig{single} + } else { + *a = multi + } + return nil +} + // Do any backward-compatibility migrations of things that have changed in the // config over time; examples are renaming a key to a better name, moving a key // from one container to another, or changing the type of a key (e.g. from bool diff --git a/pkg/config/user_config.go b/pkg/config/user_config.go index 3df5c5a9ba4..6260cb91109 100644 --- a/pkg/config/user_config.go +++ b/pkg/config/user_config.go @@ -256,9 +256,9 @@ type GitConfig struct { // If true, do not allow force pushes DisableForcePushing bool `yaml:"disableForcePushing"` // See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#predefined-commit-message-prefix - CommitPrefix *CommitPrefixConfig `yaml:"commitPrefix"` + CommitPrefix CommitPrefixConfigs `yaml:"commitPrefix"` // See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#predefined-commit-message-prefix - CommitPrefixes map[string]CommitPrefixConfig `yaml:"commitPrefixes"` + CommitPrefixes map[string]CommitPrefixConfigs `yaml:"commitPrefixes"` // See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#predefined-branch-name-prefix BranchPrefix string `yaml:"branchPrefix"` // If true, parse emoji strings in commit messages e.g. render :rocket: as 🚀 @@ -271,6 +271,8 @@ type GitConfig struct { TruncateCopiedCommitHashesTo int `yaml:"truncateCopiedCommitHashesTo"` } +type CommitPrefixConfigs []CommitPrefixConfig + type PagerType string func (PagerType) JSONSchemaExtend(schema *jsonschema.Schema) { @@ -784,7 +786,7 @@ func GetDefaultConfig() *UserConfig { BranchLogCmd: "git log --graph --color=always --abbrev-commit --decorate --date=relative --pretty=medium {{branchName}} --", AllBranchesLogCmd: "git log --graph --all --color=always --abbrev-commit --decorate --date=relative --pretty=medium", DisableForcePushing: false, - CommitPrefixes: map[string]CommitPrefixConfig(nil), + CommitPrefixes: map[string]CommitPrefixConfigs(nil), BranchPrefix: "", ParseEmoji: false, TruncateCopiedCommitHashesTo: 12, diff --git a/pkg/gui/controllers/helpers/working_tree_helper.go b/pkg/gui/controllers/helpers/working_tree_helper.go index 6f6e0eaaba2..bfa6de35b50 100644 --- a/pkg/gui/controllers/helpers/working_tree_helper.go +++ b/pkg/gui/controllers/helpers/working_tree_helper.go @@ -152,8 +152,8 @@ func (self *WorkingTreeHelper) HandleCommitPress() error { message := self.c.Contexts().CommitMessage.GetPreservedMessageAndLogError() if message == "" { - commitPrefixConfig := self.commitPrefixConfigForRepo() - if commitPrefixConfig != nil { + commitPrefixConfigs := self.commitPrefixConfigsForRepo() + for _, commitPrefixConfig := range commitPrefixConfigs { prefixPattern := commitPrefixConfig.Pattern prefixReplace := commitPrefixConfig.Replace branchName := self.refHelper.GetCheckedOutRef().Name @@ -165,6 +165,7 @@ func (self *WorkingTreeHelper) HandleCommitPress() error { if rgx.MatchString(branchName) { prefix := rgx.ReplaceAllString(branchName, prefixReplace) message = prefix + break } } } @@ -228,11 +229,11 @@ func (self *WorkingTreeHelper) prepareFilesForCommit() error { return nil } -func (self *WorkingTreeHelper) commitPrefixConfigForRepo() *config.CommitPrefixConfig { +func (self *WorkingTreeHelper) commitPrefixConfigsForRepo() config.CommitPrefixConfigs { cfg, ok := self.c.UserConfig().Git.CommitPrefixes[self.c.Git().RepoPaths.RepoName()] if ok { - return &cfg + return append(cfg, self.c.UserConfig().Git.CommitPrefix...) + } else { + return self.c.UserConfig().Git.CommitPrefix } - - return self.c.UserConfig().Git.CommitPrefix } diff --git a/pkg/integration/tests/commit/commit_wip_with_prefix.go b/pkg/integration/tests/commit/commit_wip_with_prefix.go index a39a168fe95..69e956ac028 100644 --- a/pkg/integration/tests/commit/commit_wip_with_prefix.go +++ b/pkg/integration/tests/commit/commit_wip_with_prefix.go @@ -10,7 +10,7 @@ var CommitWipWithPrefix = NewIntegrationTest(NewIntegrationTestArgs{ ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(cfg *config.AppConfig) { - cfg.GetUserConfig().Git.CommitPrefixes = map[string]config.CommitPrefixConfig{"repo": {Pattern: "^\\w+\\/(\\w+-\\w+).*", Replace: "[$1]: "}} + cfg.GetUserConfig().Git.CommitPrefixes = map[string]config.CommitPrefixConfigs{"repo": {{Pattern: "^\\w+\\/(\\w+-\\w+).*", Replace: "[$1]: "}}} }, SetupRepo: func(shell *Shell) { shell.NewBranch("feature/TEST-002") diff --git a/pkg/integration/tests/commit/commit_with_fallthrough_prefix.go b/pkg/integration/tests/commit/commit_with_fallthrough_prefix.go new file mode 100644 index 00000000000..10dc627ea32 --- /dev/null +++ b/pkg/integration/tests/commit/commit_with_fallthrough_prefix.go @@ -0,0 +1,53 @@ +package commit + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var CommitWithFallthroughPrefix = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Commit with defined config commitPrefix", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(cfg *config.AppConfig) { + cfg.GetUserConfig().Git.CommitPrefix = config.CommitPrefixConfigs{ + {Pattern: "^doesntmatch-(\\w+).*", Replace: "[BAD $1]: "}, + {Pattern: "^\\w+\\/(\\w+-\\w+).*", Replace: "[GOOD $1]: "}, + } + cfg.GetUserConfig().Git.CommitPrefixes = map[string]config.CommitPrefixConfigs{ + "DifferentProject": {{Pattern: "^otherthatdoesn'tmatch-(\\w+).*", Replace: "[BAD $1]: "}}, + } + }, + SetupRepo: func(shell *Shell) { + shell.NewBranch("feature/TEST-001") + shell.CreateFile("test-commit-prefix", "This is foo bar") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Commits(). + IsEmpty() + + t.Views().Files(). + IsFocused(). + PressPrimaryAction(). + Press(keys.Files.CommitChanges) + + t.ExpectPopup().CommitMessagePanel(). + Title(Equals("Commit summary")). + InitialText(Equals("[GOOD TEST-001]: ")). + Type("my commit message"). + Cancel() + + t.Views().Files(). + IsFocused(). + Press(keys.Files.CommitChanges) + + t.ExpectPopup().CommitMessagePanel(). + Title(Equals("Commit summary")). + InitialText(Equals("[GOOD TEST-001]: my commit message")). + Type(". Added something else"). + Confirm() + + t.Views().Commits().Focus() + t.Views().Main().Content(Contains("[GOOD TEST-001]: my commit message. Added something else")) + }, +}) diff --git a/pkg/integration/tests/commit/commit_with_global_prefix.go b/pkg/integration/tests/commit/commit_with_global_prefix.go index f5e67fba3e9..53fc7b15828 100644 --- a/pkg/integration/tests/commit/commit_with_global_prefix.go +++ b/pkg/integration/tests/commit/commit_with_global_prefix.go @@ -10,7 +10,7 @@ var CommitWithGlobalPrefix = NewIntegrationTest(NewIntegrationTestArgs{ ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(cfg *config.AppConfig) { - cfg.GetUserConfig().Git.CommitPrefix = &config.CommitPrefixConfig{Pattern: "^\\w+\\/(\\w+-\\w+).*", Replace: "[$1]: "} + cfg.GetUserConfig().Git.CommitPrefix = config.CommitPrefixConfigs{{Pattern: "^\\w+\\/(\\w+-\\w+).*", Replace: "[$1]: "}} }, SetupRepo: func(shell *Shell) { shell.NewBranch("feature/TEST-001") diff --git a/pkg/integration/tests/commit/commit_with_non_matching_branch_name.go b/pkg/integration/tests/commit/commit_with_non_matching_branch_name.go index 98f35d6d23f..1eaa4253764 100644 --- a/pkg/integration/tests/commit/commit_with_non_matching_branch_name.go +++ b/pkg/integration/tests/commit/commit_with_non_matching_branch_name.go @@ -10,10 +10,10 @@ var CommitWithNonMatchingBranchName = NewIntegrationTest(NewIntegrationTestArgs{ ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(cfg *config.AppConfig) { - cfg.GetUserConfig().Git.CommitPrefix = &config.CommitPrefixConfig{ + cfg.GetUserConfig().Git.CommitPrefix = config.CommitPrefixConfigs{{ Pattern: "^\\w+\\/(\\w+-\\w+).*", Replace: "[$1]: ", - } + }} }, SetupRepo: func(shell *Shell) { shell.NewBranch("branchnomatch") diff --git a/pkg/integration/tests/commit/commit_with_prefix.go b/pkg/integration/tests/commit/commit_with_prefix.go index fa49b0baf83..8707a384d70 100644 --- a/pkg/integration/tests/commit/commit_with_prefix.go +++ b/pkg/integration/tests/commit/commit_with_prefix.go @@ -10,11 +10,11 @@ var CommitWithPrefix = NewIntegrationTest(NewIntegrationTestArgs{ ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(cfg *config.AppConfig) { - cfg.GetUserConfig().Git.CommitPrefixes = map[string]config.CommitPrefixConfig{ - "repo": { + cfg.GetUserConfig().Git.CommitPrefixes = map[string]config.CommitPrefixConfigs{ + "repo": {{ Pattern: `^\w+/(\w+-\w+).*`, Replace: "[$1]: ", - }, + }}, } }, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index b9b480e910c..816c0bb0e9e 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -93,6 +93,7 @@ var tests = []*components.IntegrationTest{ commit.CommitMultiline, commit.CommitSwitchToEditor, commit.CommitWipWithPrefix, + commit.CommitWithFallthroughPrefix, commit.CommitWithGlobalPrefix, commit.CommitWithNonMatchingBranchName, commit.CommitWithPrefix, diff --git a/schema/config.json b/schema/config.json index 8ce2c57389e..add8966246d 100644 --- a/schema/config.json +++ b/schema/config.json @@ -638,28 +638,7 @@ "default": false }, "commitPrefix": { - "properties": { - "pattern": { - "type": "string", - "description": "pattern to match on. E.g. for 'feature/AB-123' to match on the AB-123 use \"^\\\\w+\\\\/(\\\\w+-\\\\w+).*\"", - "examples": [ - "^\\w+\\/(\\w+-\\w+).*" - ] - }, - "replace": { - "type": "string", - "description": "Replace directive. E.g. for 'feature/AB-123' to start the commit message with 'AB-123 ' use \"[$1] \"", - "examples": [ - "[$1]" - ] - } - }, - "additionalProperties": false, - "type": "object", - "description": "See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#predefined-commit-message-prefix" - }, - "commitPrefixes": { - "additionalProperties": { + "items": { "properties": { "pattern": { "type": "string", @@ -679,6 +658,33 @@ "additionalProperties": false, "type": "object" }, + "type": "array", + "description": "See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#predefined-commit-message-prefix" + }, + "commitPrefixes": { + "additionalProperties": { + "items": { + "properties": { + "pattern": { + "type": "string", + "description": "pattern to match on. E.g. for 'feature/AB-123' to match on the AB-123 use \"^\\\\w+\\\\/(\\\\w+-\\\\w+).*\"", + "examples": [ + "^\\w+\\/(\\w+-\\w+).*" + ] + }, + "replace": { + "type": "string", + "description": "Replace directive. E.g. for 'feature/AB-123' to start the commit message with 'AB-123 ' use \"[$1] \"", + "examples": [ + "[$1]" + ] + } + }, + "additionalProperties": false, + "type": "object" + }, + "type": "array" + }, "type": "object", "description": "See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#predefined-commit-message-prefix" },