Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions docs-master/Config.md
Original file line number Diff line number Diff line change
Expand Up @@ -396,9 +396,12 @@ git:
- master
- main

# Prefix to use when skipping hooks. E.g. if set to 'WIP', then pre-commit hooks
Comment thread
slavshik marked this conversation as resolved.
# will be skipped when the commit message starts with 'WIP'
skipHookPrefix: WIP
# Prefixes to use when skipping hooks. E.g. if set to ['WIP'], then pre-commit
# hooks will be skipped when the commit message starts with 'WIP'. The first
# entry in the array will be used for the "Commit changes without pre-commit
# hook" command.
skipHookPrefixes:
- WIP

# If true, periodically fetch from remote
autoFetch: true
Expand Down
18 changes: 14 additions & 4 deletions pkg/commands/git_commands/commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/go-errors/errors"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/samber/lo"
)

var ErrInvalidCommitIndex = errors.New("invalid commit index")
Expand Down Expand Up @@ -85,11 +86,16 @@ func (self *CommitCommands) ResetToCommit(hash string, strength string, envVars
Run()
}

func (self *CommitCommands) hasSkipHookPrefix(subject string) bool {
return lo.SomeBy(self.UserConfig().Git.SkipHookPrefixes, func(prefix string) bool {
return strings.HasPrefix(subject, prefix)
})
}

func (self *CommitCommands) CommitCmdObj(summary string, description string, forceSkipHooks bool) *oscommands.CmdObj {
messageArgs := self.commitMessageArgs(summary, description)
skipHookPrefix := self.UserConfig().Git.SkipHookPrefix
cmdArgs := NewGitCmd("commit").
ArgIf(forceSkipHooks || (skipHookPrefix != "" && strings.HasPrefix(summary, skipHookPrefix)), "--no-verify").
ArgIf(forceSkipHooks || self.hasSkipHookPrefix(summary), "--no-verify").
ArgIf(self.signoffFlag() != "", self.signoffFlag()).
Arg(messageArgs...).
ToArgv()
Expand Down Expand Up @@ -286,8 +292,11 @@ func (self *CommitCommands) Revert(hashes []string, isMerge bool) error {
}

// CreateFixupCommit creates a commit that fixes up a previous commit
func (self *CommitCommands) CreateFixupCommit(hash string) error {
cmdArgs := NewGitCmd("commit").Arg("--fixup=" + hash).ToArgv()
func (self *CommitCommands) CreateFixupCommit(hash string, originalSubject string) error {
cmdArgs := NewGitCmd("commit").
ArgIf(self.hasSkipHookPrefix("fixup! "+originalSubject), "--no-verify").
Arg("--fixup=" + hash).
ToArgv()

return self.cmd.New(cmdArgs).Run()
}
Expand All @@ -299,6 +308,7 @@ func (self *CommitCommands) CreateAmendCommit(originalSubject, newSubject, newDe
description += "\n\n" + newDescription
}
cmdArgs := NewGitCmd("commit").
ArgIf(self.hasSkipHookPrefix(newSubject), "--no-verify").
Arg("-m", "amend! "+originalSubject).
Arg("-m", description).
ArgIf(!includeFileChanges, "--only", "--allow-empty").
Expand Down
207 changes: 147 additions & 60 deletions pkg/commands/git_commands/commit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,80 +51,88 @@ func TestCommitResetToCommit(t *testing.T) {

func TestCommitCommitCmdObj(t *testing.T) {
type scenario struct {
testName string
summary string
forceSkipHooks bool
description string
configSignoff bool
configSkipHookPrefix string
expectedArgs []string
testName string
summary string
forceSkipHooks bool
description string
configSignoff bool
configSkipHookPrefixes []string
expectedArgs []string
}

scenarios := []scenario{
{
testName: "Commit",
summary: "test",
forceSkipHooks: false,
configSignoff: false,
configSkipHookPrefix: "",
expectedArgs: []string{"commit", "-m", "test"},
testName: "Commit",
summary: "test",
forceSkipHooks: false,
configSignoff: false,
configSkipHookPrefixes: nil,
expectedArgs: []string{"commit", "-m", "test"},
},
{
testName: "Commit with --no-verify flag < only prefix",
summary: "WIP: test",
forceSkipHooks: false,
configSignoff: false,
configSkipHookPrefix: "WIP",
expectedArgs: []string{"commit", "--no-verify", "-m", "WIP: test"},
testName: "Commit with --no-verify flag < only prefix",
summary: "WIP: test",
forceSkipHooks: false,
configSignoff: false,
configSkipHookPrefixes: []string{"WIP"},
expectedArgs: []string{"commit", "--no-verify", "-m", "WIP: test"},
},
{
testName: "Commit with --no-verify flag < skip flag and prefix",
summary: "WIP: test",
forceSkipHooks: true,
configSignoff: false,
configSkipHookPrefix: "WIP",
expectedArgs: []string{"commit", "--no-verify", "-m", "WIP: test"},
testName: "Commit with --no-verify flag < skip flag and prefix",
summary: "WIP: test",
forceSkipHooks: true,
configSignoff: false,
configSkipHookPrefixes: []string{"WIP"},
expectedArgs: []string{"commit", "--no-verify", "-m", "WIP: test"},
},
{
testName: "Commit with --no-verify flag < skip flag no prefix",
summary: "test",
forceSkipHooks: true,
configSignoff: false,
configSkipHookPrefix: "WIP",
expectedArgs: []string{"commit", "--no-verify", "-m", "test"},
testName: "Commit with --no-verify flag < skip flag no prefix",
summary: "test",
forceSkipHooks: true,
configSignoff: false,
configSkipHookPrefixes: []string{"WIP"},
expectedArgs: []string{"commit", "--no-verify", "-m", "test"},
},
{
testName: "Commit with multiline message",
summary: "line1",
forceSkipHooks: false,
description: "line2",
configSignoff: false,
configSkipHookPrefix: "",
expectedArgs: []string{"commit", "-m", "line1", "-m", "line2"},
testName: "Commit with multiline message",
summary: "line1",
forceSkipHooks: false,
description: "line2",
configSignoff: false,
configSkipHookPrefixes: nil,
expectedArgs: []string{"commit", "-m", "line1", "-m", "line2"},
},
{
testName: "Commit with signoff",
summary: "test",
forceSkipHooks: false,
configSignoff: true,
configSkipHookPrefix: "",
expectedArgs: []string{"commit", "--signoff", "-m", "test"},
testName: "Commit with signoff",
summary: "test",
forceSkipHooks: false,
configSignoff: true,
configSkipHookPrefixes: nil,
expectedArgs: []string{"commit", "--signoff", "-m", "test"},
},
{
testName: "Commit with signoff and no-verify",
summary: "WIP: test",
forceSkipHooks: true,
configSignoff: true,
configSkipHookPrefix: "WIP",
expectedArgs: []string{"commit", "--no-verify", "--signoff", "-m", "WIP: test"},
testName: "Commit with signoff and no-verify",
summary: "WIP: test",
forceSkipHooks: true,
configSignoff: true,
configSkipHookPrefixes: []string{"WIP"},
expectedArgs: []string{"commit", "--no-verify", "--signoff", "-m", "WIP: test"},
},
{
testName: "Commit with multiple prefixes, second matches",
summary: "fixup! some commit",
forceSkipHooks: false,
configSignoff: false,
configSkipHookPrefixes: []string{"WIP", "fixup!", "squash!"},
expectedArgs: []string{"commit", "--no-verify", "-m", "fixup! some commit"},
},
}

for _, s := range scenarios {
t.Run(s.testName, func(t *testing.T) {
userConfig := config.GetDefaultConfig()
userConfig.Git.Commit.SignOff = s.configSignoff
userConfig.Git.SkipHookPrefix = s.configSkipHookPrefix
userConfig.Git.SkipHookPrefixes = s.configSkipHookPrefixes

runner := oscommands.NewFakeRunner(t).ExpectGitArgs(s.expectedArgs, "", nil)
instance := buildCommitCommands(commonDeps{userConfig: userConfig, runner: runner})
Expand Down Expand Up @@ -171,28 +179,70 @@ func TestCommitCommitEditorCmdObj(t *testing.T) {

func TestCommitCreateFixupCommit(t *testing.T) {
type scenario struct {
testName string
hash string
runner *oscommands.FakeCmdObjRunner
test func(error)
testName string
hash string
originalSubject string
userConfig *config.UserConfig
runner *oscommands.FakeCmdObjRunner
test func(error)
}

scenarios := []scenario{
{
testName: "valid case",
hash: "12345",
testName: "valid case",
hash: "12345",
originalSubject: "some commit",
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"commit", "--fixup=12345"}, "", nil),
test: func(err error) {
assert.NoError(t, err)
},
},
{
testName: "with matching skipHookPrefixes for original subject",
hash: "12345",
originalSubject: "WIP do stuff",
userConfig: &config.UserConfig{
Git: config.GitConfig{SkipHookPrefixes: []string{"fixup! WIP"}},
},
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"commit", "--no-verify", "--fixup=12345"}, "", nil),
test: func(err error) {
assert.NoError(t, err)
},
},
{
testName: "with non-matching skipHookPrefixes",
hash: "12345",
originalSubject: "some commit",
userConfig: &config.UserConfig{
Git: config.GitConfig{SkipHookPrefixes: []string{"WIP"}},
},
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"commit", "--fixup=12345"}, "", nil),
test: func(err error) {
assert.NoError(t, err)
},
},
{
testName: "with multiple prefixes including fixup! WIP",
hash: "12345",
originalSubject: "WIP my feature",
userConfig: &config.UserConfig{
Git: config.GitConfig{SkipHookPrefixes: []string{"WIP", "fixup! WIP"}},
},
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"commit", "--no-verify", "--fixup=12345"}, "", nil),
test: func(err error) {
assert.NoError(t, err)
},
},
}

for _, s := range scenarios {
t.Run(s.testName, func(t *testing.T) {
instance := buildCommitCommands(commonDeps{runner: s.runner})
s.test(instance.CreateFixupCommit(s.hash))
instance := buildCommitCommands(commonDeps{runner: s.runner, userConfig: s.userConfig})
s.test(instance.CreateFixupCommit(s.hash, s.originalSubject))
s.runner.CheckForMissingCalls()
})
}
Expand All @@ -205,6 +255,7 @@ func TestCommitCreateAmendCommit(t *testing.T) {
newSubject string
newDescription string
includeFileChanges bool
userConfig *config.UserConfig
runner *oscommands.FakeCmdObjRunner
}

Expand Down Expand Up @@ -236,11 +287,47 @@ func TestCommitCreateAmendCommit(t *testing.T) {
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"commit", "-m", "amend! original subject", "-m", "new subject", "--only", "--allow-empty"}, "", nil),
},
{
testName: "with matching skipHookPrefixes on new subject",
originalSubject: "original subject",
newSubject: "WIP new subject",
newDescription: "",
includeFileChanges: true,
userConfig: &config.UserConfig{
Git: config.GitConfig{SkipHookPrefixes: []string{"WIP"}},
},
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"commit", "--no-verify", "-m", "amend! original subject", "-m", "WIP new subject"}, "", nil),
},
{
testName: "with non-matching skipHookPrefixes",
originalSubject: "original subject",
newSubject: "new subject",
newDescription: "",
includeFileChanges: true,
userConfig: &config.UserConfig{
Git: config.GitConfig{SkipHookPrefixes: []string{"WIP"}},
},
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"commit", "-m", "amend! original subject", "-m", "new subject"}, "", nil),
},
{
testName: "renaming WIP commit to non-WIP runs hooks",
originalSubject: "WIP my feature",
newSubject: "Implement my feature",
newDescription: "",
includeFileChanges: true,
userConfig: &config.UserConfig{
Git: config.GitConfig{SkipHookPrefixes: []string{"WIP"}},
},
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"commit", "-m", "amend! WIP my feature", "-m", "Implement my feature"}, "", nil),
},
}

for _, s := range scenarios {
t.Run(s.testName, func(t *testing.T) {
instance := buildCommitCommands(commonDeps{runner: s.runner})
instance := buildCommitCommands(commonDeps{runner: s.runner, userConfig: s.userConfig})
err := instance.CreateAmendCommit(s.originalSubject, s.newSubject, s.newDescription, s.includeFileChanges)
assert.NoError(t, err)
s.runner.CheckForMissingCalls()
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/git_commands/rebase.go
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ func (self *RebaseCommands) getHashOfLastCommitMade() (string, error) {
func (self *RebaseCommands) AmendTo(commits []*models.Commit, commitIndex int) error {
commit := commits[commitIndex]

if err := self.commit.CreateFixupCommit(commit.Hash()); err != nil {
if err := self.commit.CreateFixupCommit(commit.Hash(), commit.Name); err != nil {
return err
}

Expand Down
Loading