upload: reject --fix with --ignore-hooks or --no-verify --fix asks the pre-upload hook to apply its fixups, but: * --no-verify skips the hook entirely, so --fix silently does nothing. * --ignore-hooks uploads the original commits even if the hook fails, so any fixups applied to the working tree are not part of the upload. Whether --fix should then apply fixups or be ignored is ambiguous. Reject both combinations up front, before any hook runs, instead of picking an interpretation. Bug: 562921767 Test: python3 -m pytest tests/test_subcmds_upload.py Change-Id: Ib00ae69004642f2e763212b82e7c39f3b4da52eb Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/642664 Tested-by: Rahul Yadav <yadavrah@google.com> Reviewed-by: Mike Frysinger <vapier@google.com> Commit-Queue: Rahul Yadav <yadavrah@google.com>
diff --git a/subcmds/upload.py b/subcmds/upload.py index 2215f9e..8b5ae96 100644 --- a/subcmds/upload.py +++ b/subcmds/upload.py
@@ -742,6 +742,22 @@ avail = project.GetUploadableBranches(opt.branch) return (project_idx, avail, cbr) + def ValidateOptions(self, opt: optparse.Values, args: List[str]) -> None: + if opt.fix: + if opt.ignore_hooks: + self.OptionParser.error( + "--fix and --ignore-hooks are mutually exclusive options: " + "with --ignore-hooks, the original commits are uploaded, " + "so fixups would only modify the working tree and would " + "not be part of the upload" + ) + if opt.bypass_hooks: + self.OptionParser.error( + "--fix and --no-verify are mutually exclusive options: " + "--no-verify skips the pre-upload hook, so --fix would " + "have no effect" + ) + def Execute(self, opt, args): projects = self.GetProjects( args, all_manifests=not opt.this_manifest_only
diff --git a/tests/test_subcmds_upload.py b/tests/test_subcmds_upload.py index 53666fe..980520b 100644 --- a/tests/test_subcmds_upload.py +++ b/tests/test_subcmds_upload.py
@@ -233,3 +233,45 @@ cmd._MultipleBranches(opt, pending, _STUB_PEOPLE) mock_edit.assert_called_once() mock_upload.assert_called_once_with(opt, [branch1], _STUB_PEOPLE) + + +@pytest.mark.parametrize( + "argv, expected", + ( + (["--fix", "--ignore-hooks"], "not be part of the upload"), + (["--fix", "--no-verify"], "--fix would have no effect"), + ( + ["--fix", "--ignore-hooks", "--no-verify"], + "--fix and --ignore-hooks are mutually exclusive", + ), + ), +) +def test_ValidateOptions_fix_conflicts( + cmd: upload.Upload, + capsys: pytest.CaptureFixture, + argv: List[str], + expected: str, +) -> None: + """Check --fix is rejected with --ignore-hooks or --no-verify.""" + opt, args = cmd.OptionParser.parse_args(argv) + with pytest.raises(SystemExit): + cmd.ValidateOptions(opt, args) + assert expected in capsys.readouterr().err + + +@pytest.mark.parametrize( + "argv", + ( + [], + ["--fix"], + ["--fix", "--verify"], + ["--fix", "-y"], + ["--ignore-hooks"], + ["--no-verify"], + ["--ignore-hooks", "--no-verify"], + ), +) +def test_ValidateOptions_allowed(cmd: upload.Upload, argv: List[str]) -> None: + """Check valid hook option combinations are accepted.""" + opt, args = cmd.OptionParser.parse_args(argv) + cmd.ValidateOptions(opt, args)