Files
cdesouza-chromium 49edc62217 [git-cr] Adding git cr commit --fixup=reassign:hash (#37056)
This is just a shortcut for `brockit.py reassign <hash>`.
2026-06-06 23:19:07 +01:00

199 lines
6.7 KiB
Python

# Copyright (c) 2026 The Brave Authors. All rights reserved.
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at https://mozilla.org/MPL/2.0/.
"""git_cr commit — wraps `git commit` with brave-core hook environment flags.
Forwards user-specified tags, issues, and culprit commit hashes to the
commit-msg hook via environment variables. All other arguments pass through
to `git commit` unchanged.
Usage
-----
git cr commit [--tagged TAG[,TAG…]] [--issue NUM[,NUM…]]
[--culprit HASH[,HASH…]] [<git-commit-args> …]
git cr commit -m "Fix login button alignment"
git cr commit --tagged WIP -m "Work in progress"
git cr commit --fixup=reassign:<ref> # shortcut for `brockit.py reassign`
Custom flags (forwarded to the commit-msg hook via environment variables):
--tagged Comma-separated tags → $tags
--issue Comma-separated issue numbers → $issue
--culprit Comma-separated commit hashes → $culprit
All other arguments are forwarded verbatim to git commit.
"""
from __future__ import annotations
import argparse
import os
import platform
import stat
import subprocess
from dataclasses import dataclass
from typing import ClassVar, NoReturn
import _boot # noqa: F401
from alias.base import (HOOK_DEST, WINDOWS_SHIM, UserValidationError,
check_hooks_path)
def _check_hook_ready() -> None:
"""Raise UserValidationError if the commit-msg hook is not ready to run.
Checks core.hooksPath first, then verifies the hook file exists and is
executable (POSIX) or contains the expected shim content (Windows).
"""
check_hooks_path()
ready = False
if HOOK_DEST.exists():
if platform.system() == 'Windows':
try:
ready = HOOK_DEST.read_bytes() == WINDOWS_SHIM
except OSError:
pass
else:
ready = bool(HOOK_DEST.stat().st_mode & stat.S_IXUSR)
if not ready:
raise UserValidationError(
'git_cr: commit-msg hook is not installed or not executable.\n'
'Run: git cr install-hook')
def _run_commit(
tagged: str | None,
issue: str | None,
culprit: str | None,
git_args: list[str],
) -> int:
"""Execute git commit with hook environment variables injected."""
_check_hook_ready()
env = os.environ.copy()
if tagged:
env['tags'] = tagged
if issue:
env['issue'] = issue
if culprit:
env['culprit'] = culprit
return subprocess.run(['git', 'commit'] + git_args, check=False,
env=env).returncode
class _LenientArgumentParser(argparse.ArgumentParser):
"""An `ArgumentParser` that raises instead of terminating on error.
This class is useful when parsing shortcuts, as we just bail out when the
arguments don't match.
"""
def error(self, message: str) -> NoReturn:
raise ValueError(message)
@dataclass(frozen=True)
class _ReassignShortcut:
"""The `git cr commit --fixup=reassign:<ref>` shortcut.
This is a convenience shortcut for brockit's `reassign` task. It can be used
either as:
git cr commit --fixup=reassign:HEAD
or
git cr commit --fixup reassign:HEAD
`from_args` is the entry point: it returns a ready-to-run instance when
the shortcut is present and valid, or None when the args are an ordinary
commit.
"""
# Value prefix that selects this mode, mirroring git's own `amend:` /
# `reword:` fixup modes.
_PREFIX: ClassVar[str] = 'reassign:'
# The commit reference whose authorship is being reassigned.
target: str
@staticmethod
def from_args(args: list[str]) -> _ReassignShortcut | None:
"""Build the shortcut from *args*, or return None when it isn't used.
This function checks for the presence of the reassign shortcut use that
we are interested in, and if found, parses out the args provided,
strictly looking only for `--fixup` and validating the format used for
its value.
"""
if not any(_ReassignShortcut._PREFIX in arg for arg in args):
return None
parser = _LenientArgumentParser(add_help=False)
parser.add_argument('--fixup')
try:
parsed, other_args = parser.parse_known_args(args)
except ValueError:
return None
fixup = parsed.fixup
if fixup is None or not fixup.startswith(_ReassignShortcut._PREFIX):
return None
if other_args:
raise UserValidationError(
'git_cr: --fixup=reassign:<ref> cannot be combined with '
f'other arguments; remove: {" ".join(other_args)}')
target = fixup[len(_ReassignShortcut._PREFIX):]
if not target:
raise UserValidationError(
'git_cr: --fixup=reassign: requires a commit reference, '
'e.g. --fixup=reassign:HEAD~2')
return _ReassignShortcut(target)
def run(self) -> int:
"""Run brockit's `Reassign` task for the captured target.
The commit-msg hook is irrelevant (brockit commits with --no-verify),
so it is not checked.
"""
# Imported lazily: brockit is a heavy module, so we only import it when
# needed.
import brockit
try:
brockit.Reassign().run(change=self.target)
except (brockit.InvalidInputException, brockit.BadOutcomeException,
brockit.ActionNeededException):
# Handling brockit exceptions as failures to create the reassingment
# commit.
return 1
return 0
def cmd_commit(args: list[str]) -> int:
"""Parse commit flags and invoke git commit with injected environment."""
# Let's try first to check for the reassignment shortcut being used.
reassign = _ReassignShortcut.from_args(args)
if reassign is not None:
return reassign.run()
parser = argparse.ArgumentParser(
prog='git cr commit',
description='git commit wrapper with brave-core hook flags',
epilog='All other arguments are forwarded verbatim to git commit.',
)
parser.add_argument(
'--tagged',
metavar='TAG[,TAG...]',
help='Comma-separated tags injected as $tags for the commit-msg hook',
)
parser.add_argument(
'--issue',
metavar='NUM[,NUM...]',
help='GitHub issue numbers injected as $issue',
)
parser.add_argument(
'--culprit',
metavar='HASH[,HASH...]',
help='Chromium commit hashes injected as $culprit',
)
parsed, extra = parser.parse_known_args(args)
return _run_commit(parsed.tagged, parsed.issue, parsed.culprit, extra)