diff --git a/.changes/unreleased/Added-20241201-115758.yaml b/.changes/unreleased/Added-20241201-115758.yaml new file mode 100644 index 00000000..08ef26e1 --- /dev/null +++ b/.changes/unreleased/Added-20241201-115758.yaml @@ -0,0 +1,3 @@ +kind: Added +body: 'commit {create, amend, split}branch create: Adds --no-verify flag to bypass pre-commit and commit-msg hooks.' +time: 2024-12-01T11:57:58.209941-08:00 diff --git a/branch_create.go b/branch_create.go index ab27b289..054838c2 100644 --- a/branch_create.go +++ b/branch_create.go @@ -22,6 +22,8 @@ type branchCreateCmd struct { All bool `short:"a" help:"Automatically stage modified and deleted files"` Message string `short:"m" placeholder:"MSG" help:"Commit message"` + NoVerify bool `help:"Bypass pre-commit and commit-msg hooks."` + Commit bool `negatable:"" default:"true" config:"branchCreate.commit" help:"Commit staged changes to the new branch, or create an empty commit"` } @@ -282,6 +284,7 @@ func (cmd *branchCreateCmd) commit( if err := repo.Commit(ctx, git.CommitRequest{ AllowEmpty: len(diff) == 0, Message: cmd.Message, + NoVerify: cmd.NoVerify, All: cmd.All, }); err != nil { if err := repo.Checkout(ctx, baseName); err != nil { diff --git a/commit_amend.go b/commit_amend.go index 8f665e5f..a37d5d24 100644 --- a/commit_amend.go +++ b/commit_amend.go @@ -15,7 +15,8 @@ type commitAmendCmd struct { All bool `short:"a" help:"Stage all changes before committing."` Message string `short:"m" placeholder:"MSG" help:"Use the given message as the commit message."` - NoEdit bool `help:"Don't edit the commit message"` + NoEdit bool `help:"Don't edit the commit message"` + NoVerify bool `help:"Bypass pre-commit and commit-msg hooks."` // TODO: // Remove this short form and put it on NoVerify. @@ -45,10 +46,11 @@ func (cmd *commitAmendCmd) Run(ctx context.Context, log *log.Logger, view ui.Vie } if err := repo.Commit(ctx, git.CommitRequest{ - Message: cmd.Message, - Amend: true, - NoEdit: cmd.NoEdit, - All: cmd.All, + Message: cmd.Message, + Amend: true, + NoEdit: cmd.NoEdit, + NoVerify: cmd.NoVerify, + All: cmd.All, }); err != nil { return fmt.Errorf("commit: %w", err) } diff --git a/commit_create.go b/commit_create.go index 705912ec..7c6013ec 100644 --- a/commit_create.go +++ b/commit_create.go @@ -12,9 +12,10 @@ import ( ) type commitCreateCmd struct { - All bool `short:"a" help:"Stage all changes before committing."` - Fixup string `help:"Create a fixup commit."` - Message string `short:"m" help:"Use the given message as the commit message."` + All bool `short:"a" help:"Stage all changes before committing."` + Fixup string `help:"Create a fixup commit."` + Message string `short:"m" help:"Use the given message as the commit message."` + NoVerify bool `help:"Bypass pre-commit and commit-msg hooks."` } func (*commitCreateCmd) Help() string { @@ -35,9 +36,10 @@ func (cmd *commitCreateCmd) Run(ctx context.Context, log *log.Logger, view ui.Vi } if err := repo.Commit(ctx, git.CommitRequest{ - Message: cmd.Message, - All: cmd.All, - Fixup: cmd.Fixup, + Message: cmd.Message, + All: cmd.All, + Fixup: cmd.Fixup, + NoVerify: cmd.NoVerify, }); err != nil { return fmt.Errorf("commit: %w", err) } diff --git a/commit_split.go b/commit_split.go index 88e775b8..a849ee50 100644 --- a/commit_split.go +++ b/commit_split.go @@ -12,7 +12,8 @@ import ( ) type commitSplitCmd struct { - Message string `short:"m" placeholder:"MSG" help:"Use the given message as the commit message."` + Message string `short:"m" placeholder:"MSG" help:"Use the given message as the commit message."` + NoVerify bool `help:"Bypass pre-commit and commit-msg hooks."` } func (*commitSplitCmd) Help() string { @@ -69,7 +70,10 @@ func (cmd *commitSplitCmd) Run(ctx context.Context, log *log.Logger, view ui.Vie return fmt.Errorf("select hunks: %w", err) } - if err := repo.Commit(ctx, git.CommitRequest{Message: cmd.Message}); err != nil { + if err := repo.Commit(ctx, git.CommitRequest{ + Message: cmd.Message, + NoVerify: cmd.NoVerify, + }); err != nil { return fmt.Errorf("commit: %w", err) } @@ -81,7 +85,10 @@ func (cmd *commitSplitCmd) Run(ctx context.Context, log *log.Logger, view ui.Vie // Commit will move HEAD to the new commit, // updating branch ref if necessary. - if err := repo.Commit(ctx, git.CommitRequest{ReuseMessage: head.String()}); err != nil { + if err := repo.Commit(ctx, git.CommitRequest{ + ReuseMessage: head.String(), + NoVerify: cmd.NoVerify, + }); err != nil { return fmt.Errorf("commit: %w", err) } diff --git a/doc/includes/cli-reference.md b/doc/includes/cli-reference.md index 59533be2..931e10a2 100644 --- a/doc/includes/cli-reference.md +++ b/doc/includes/cli-reference.md @@ -547,6 +547,7 @@ target (A) to the specified branch: * `-t`, `--target=BRANCH`: Branch to create the new branch above/below * `-a`, `--all`: Automatically stage modified and deleted files * `-m`, `--message=MSG`: Commit message +* `--no-verify`: Bypass pre-commit and commit-msg hooks. * `--[no-]commit` ([:material-wrench:{ .middle title="spice.branchCreate.commit" }](/cli/config.md#spicebranchcreatecommit)): Commit staged changes to the new branch, or create an empty commit **Configuration**: [spice.branchCreate.commit](/cli/config.md#spicebranchcreatecommit) @@ -778,6 +779,7 @@ followed by 'gs upstack restack'. * `-a`, `--all`: Stage all changes before committing. * `--fixup=STRING`: Create a fixup commit. * `-m`, `--message=STRING`: Use the given message as the commit message. +* `--no-verify`: Bypass pre-commit and commit-msg hooks. ### gs commit amend @@ -797,6 +799,7 @@ followed by 'gs upstack restack'. * `-a`, `--all`: Stage all changes before committing. * `-m`, `--message=MSG`: Use the given message as the commit message. * `--no-edit`: Don't edit the commit message +* `--no-verify`: Bypass pre-commit and commit-msg hooks. ### gs commit split @@ -813,6 +816,7 @@ Branches upstack are restacked as needed. **Flags** * `-m`, `--message=MSG`: Use the given message as the commit message. +* `--no-verify`: Bypass pre-commit and commit-msg hooks. ## Rebase diff --git a/internal/git/commit.go b/internal/git/commit.go index da5dc3a7..422a01bb 100644 --- a/internal/git/commit.go +++ b/internal/git/commit.go @@ -123,6 +123,9 @@ type CommitRequest struct { // Create a new commit which "fixes up" the commit at the given commitish. Fixup string + + // NoVerify allows a commit with pre-commit and commit-msg hooks bypassed. + NoVerify bool } // Commit runs the 'git commit' command, @@ -144,6 +147,9 @@ func (r *Repository) Commit(ctx context.Context, req CommitRequest) error { if req.AllowEmpty { args = append(args, "--allow-empty") } + if req.NoVerify { + args = append(args, "--no-verify") + } if req.ReuseMessage != "" { args = append(args, "-C", req.ReuseMessage) } diff --git a/testdata/script/branch_create_no_verify.txt b/testdata/script/branch_create_no_verify.txt new file mode 100644 index 00000000..37a11c13 --- /dev/null +++ b/testdata/script/branch_create_no_verify.txt @@ -0,0 +1,44 @@ +# Branch create with --no-verify. + +as 'Test ' +at '2024-07-02T20:18:32Z' + +cd repo +git init +git commit --allow-empty -m 'Initial commit' +gs repo init + +cp $WORK/extra/pre-commit .git/hooks/pre-commit +chmod 700 .git/hooks/pre-commit + +git add feature.txt +! gs branch create feature -m 'Add feature' +stderr 'exit status 1' + +gs branch create feature -m 'Add feature' --no-verify + +gs ll -a +cmp stderr $WORK/golden/ll.txt + +git diff HEAD^ +cmp stdout $WORK/golden/diff.txt + +-- repo/feature.txt -- +feature + +-- extra/pre-commit -- +exit 1 + +-- golden/ll.txt -- +┏━■ feature ◀ +┃ 5648694 Add feature (now) +main +-- golden/diff.txt -- +diff --git a/feature.txt b/feature.txt +new file mode 100644 +index 0000000..d0f029a +--- /dev/null ++++ b/feature.txt +@@ -0,0 +1,2 @@ ++feature ++ diff --git a/testdata/script/commit_amend_no_verify.txt b/testdata/script/commit_amend_no_verify.txt new file mode 100644 index 00000000..2f83ecc4 --- /dev/null +++ b/testdata/script/commit_amend_no_verify.txt @@ -0,0 +1,52 @@ +# Commit amend with --no-verify. + +as 'Test ' +at '2024-05-23T19:23:24Z' + +# setup +cd repo +git init +git commit --allow-empty -m 'Initial commit' +gs repo init + +git checkout -b feature +gs branch track --base main + +git add feature1.txt +gs cc -a -m 'Add feature' + +cp $WORK/extra/pre-commit .git/hooks/pre-commit +chmod 700 .git/hooks/pre-commit + +git add feature2.txt + +! gs ca -m 'Add feature 1 and feature 2' +stderr 'exit status 1' + +gs ca -m 'Add feature 1 and feature 2' --no-verify + +# verify the output +git log +cmp stdout $WORK/golden/log.1.txt + +-- repo/feature1.txt -- +Contents of feature 1. + +-- repo/feature2.txt -- +Contents of feature 2. + +-- extra/pre-commit -- +exit 1 + +-- golden/log.1.txt -- +commit 97daa3e151f2efd07538fb32e5a8d941816ab39b +Author: Test +Date: Thu May 23 19:23:24 2024 +0000 + + Add feature 1 and feature 2 + +commit 63c927d63e16e46e0f55c14031bdf4cf9a159a56 +Author: Test +Date: Thu May 23 19:23:24 2024 +0000 + + Initial commit diff --git a/testdata/script/commit_create_no_verify.txt b/testdata/script/commit_create_no_verify.txt new file mode 100644 index 00000000..f1b043ac --- /dev/null +++ b/testdata/script/commit_create_no_verify.txt @@ -0,0 +1,45 @@ +# Commit create with --no-verify. + +as 'Test ' +at '2024-03-30T14:59:32Z' + +# setup +cd repo +git init +git commit --allow-empty -m 'Initial commit' +gs repo init + +git checkout -b feature +gs branch track --base main + +cp $WORK/extra/pre-commit .git/hooks/pre-commit +chmod 700 .git/hooks/pre-commit + +git add foo.txt +! gs cc -m 'Add foo' +stderr 'exit status 1' + +gs cc -m 'Add foo' --no-verify + +# verify the output +git log +cmp stdout $WORK/golden/log.1.txt + +-- repo/foo.txt -- +Contents of foo. + +-- extra/pre-commit -- +exit 1 + +-- golden/log.1.txt -- +commit 91582344149997d5a513acf7b4d56a03452e23cd +Author: Test +Date: Sat Mar 30 14:59:32 2024 +0000 + + Add foo + +commit 9bad92b764fe1d56cb99b394f373a71cdefd8e86 +Author: Test +Date: Sat Mar 30 14:59:32 2024 +0000 + + Initial commit diff --git a/testdata/script/commit_split_no_verify.txt b/testdata/script/commit_split_no_verify.txt new file mode 100644 index 00000000..0901d01b --- /dev/null +++ b/testdata/script/commit_split_no_verify.txt @@ -0,0 +1,164 @@ +# Commit split with --no-verify. + +[!unix] skip # pending github.com/creack/pty/pull/155 + +as 'Test ' +at '2024-07-08T05:04:32Z' + +# setup +cd repo +git init +git commit --allow-empty -m 'Initial commit' +gs repo init + +git add feature1.txt feature2.txt +gs bc -m 'Add features' features + +git add feature3.txt +gs bc -m 'Add feature 3' feature3 +gs down + +cp $WORK/extra/pre-commit .git/hooks/pre-commit +chmod 700 .git/hooks/pre-commit + +! with-term -final exit $WORK/input.txt -- gs commit split -m 'Add feature 1' +stderr 'exit status 1' + +with-term -final exit $WORK/input.txt -- gs commit split -m 'Add feature 1' --no-verify +# git 2.45 added more options to interactive staging. +# Our captured output is of those options, so require at least 2.45. +[git:2.45] cmp stdout $WORK/golden/output.txt + +git log --patch +cmp stdout $WORK/golden/log.txt + +gs ll +cmp stderr $WORK/golden/ll.txt + +-- repo/feature1.txt -- +feature 1 + +-- repo/feature2.txt -- +feature 2 + +-- repo/feature3.txt -- +feature 3 + +-- extra/pre-commit -- +exit 1 + +-- input.txt -- +await feature1 +snapshot first +feed y\r +await feature2 +snapshot second +feed q\r +await + +-- golden/output.txt -- +### first ### +INF Select hunks to extract into a new commit +diff --git b/feature1.txt a/feature1.txt +new file mode 100644 +index 0000000..b30fa09 +--- /dev/null ++++ a/feature1.txt +@@ -0,0 +1,2 @@ ++feature 1 ++ +(1/1) Apply addition to index [y,n,q,a,d,e,p,?]? +### second ### +INF Select hunks to extract into a new commit +diff --git b/feature1.txt a/feature1.txt +new file mode 100644 +index 0000000..b30fa09 +--- /dev/null ++++ a/feature1.txt +@@ -0,0 +1,2 @@ ++feature 1 ++ +(1/1) Apply addition to index [y,n,q,a,d,e,p,?]? y + +diff --git b/feature2.txt a/feature2.txt +new file mode 100644 +index 0000000..4357a3d +--- /dev/null ++++ a/feature2.txt +@@ -0,0 +1,2 @@ ++feature 2 ++ +(1/1) Apply addition to index [y,n,q,a,d,e,p,?]? +### exit ### +INF Select hunks to extract into a new commit +diff --git b/feature1.txt a/feature1.txt +new file mode 100644 +index 0000000..b30fa09 +--- /dev/null ++++ a/feature1.txt +@@ -0,0 +1,2 @@ ++feature 1 ++ +(1/1) Apply addition to index [y,n,q,a,d,e,p,?]? y + +diff --git b/feature2.txt a/feature2.txt +new file mode 100644 +index 0000000..4357a3d +--- /dev/null ++++ a/feature2.txt +@@ -0,0 +1,2 @@ ++feature 2 ++ +(1/1) Apply addition to index [y,n,q,a,d,e,p,?]? q + +[features 5c5b54e] Add feature 1 + 1 file changed, 2 insertions(+) + create mode 100644 feature1.txt +[features d5849e7] Add features + Date: Mon Jul 8 05:04:32 2024 +0000 + 1 file changed, 2 insertions(+) + create mode 100644 feature2.txt +INF feature3: restacked on features +-- golden/log.txt -- +commit d5849e70445dbe7c0b8ea46c1cb22b175f5ceca0 +Author: Test +Date: Mon Jul 8 05:04:32 2024 +0000 + + Add features + +diff --git a/feature2.txt b/feature2.txt +new file mode 100644 +index 0000000..4357a3d +--- /dev/null ++++ b/feature2.txt +@@ -0,0 +1,2 @@ ++feature 2 ++ + +commit 5c5b54e2a2d6274e8b3862935ef979ea695a975c +Author: Test +Date: Mon Jul 8 05:04:32 2024 +0000 + + Add feature 1 + +diff --git a/feature1.txt b/feature1.txt +new file mode 100644 +index 0000000..b30fa09 +--- /dev/null ++++ b/feature1.txt +@@ -0,0 +1,2 @@ ++feature 1 ++ + +commit 2123b7327c4c4b5c3964ae826e4f10caa3888244 +Author: Test +Date: Mon Jul 8 05:04:32 2024 +0000 + + Initial commit +-- golden/ll.txt -- + ┏━□ feature3 + ┃ be650d2 Add feature 3 (now) +┏━┻■ features ◀ +┃ d5849e7 Add features (now) +┃ 5c5b54e Add feature 1 (now) +main