diff --git a/fish/functions/cbl_ptchmn.fish b/fish/functions/cbl_ptchmn.fish index cce60289..220f451e 100644 --- a/fish/functions/cbl_ptchmn.fish +++ b/fish/functions/cbl_ptchmn.fish @@ -1,39 +1,7 @@ #!/usr/bin/env fish # SPDX-License-Identifier: MIT -# Copyright (C) 2021-2023 Nathan Chancellor +# Copyright (C) 2024 Nathan Chancellor -function cbl_ptchmn -d "Quilt-like patch management function for Linux" - in_kernel_tree; or return - - set repo (basename $PWD) - set out $GITHUB_FOLDER/patches/$repo/(git bn) - if not test -d $out - print_error "$out does not exist!" - return 1 - end - - switch $argv[1] - case -a --apply - git am $out/* - - case -s --sync - switch $repo - case linux-next linux linux-stable-'*'.'*' - case '*' - print_error "$repo not supported by cbl_ptchmn!" - return 1 - end - - set mfc (git mfc) - if test -z "$mfc" - print_error "My first commit does not exist?" - return 1 - end - - rm $out/* - git fp -o $out --base=$mfc^ $mfc^..HEAD - git -C $out aa - git -C $out c -m "patches: $repo: "(git bn)": sync as of"(git sha) - git -C $out push - end +function cbl_ptchmn -d "Wrapper for cbl_ptchmn.py" + $PYTHON_SCRIPTS_FOLDER/cbl_ptchmn.py $argv end diff --git a/python/scripts/cbl_ptchmn.py b/python/scripts/cbl_ptchmn.py new file mode 100755 index 00000000..f9605228 --- /dev/null +++ b/python/scripts/cbl_ptchmn.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 + +from argparse import ArgumentParser +from pathlib import Path +import subprocess +import os +import sys + +sys.path.append(str(Path(__file__).resolve().parents[1])) +# pylint: disable-next=wrong-import-position +import lib.utils # noqa: E402 + + +def git(directory, cmd, **kwargs): + return subprocess.run(['git', *cmd], + capture_output=True, + check=True, + cwd=directory, + text=True, + **kwargs) + + +def git_loud(directory, cmd, **kwargs): + lib.utils.print_cmd(['git', '-C', directory, *cmd]) + return git(directory, cmd, **kwargs) + + +def get_patches_folder(repo): + branch = git(repo, ['bn']).stdout.strip() + return Path(os.environ['GITHUB_FOLDER'], 'patches', repo.name, branch) + + +def parse_arguments(): + parser = ArgumentParser(description='Quilt-like patch management function for Linux') + + parser.add_argument('-C', + '--directory', + default=Path.cwd().resolve(), + help='Directory to run git commands in', + type=Path) + + mode_parser = parser.add_mutually_exclusive_group(required=True) + mode_parser.add_argument('-s', '--sync', action='store_true', help='Sync patches to repo') + mode_parser.add_argument('-a', '--apply', action='store_true', help='Apply patches from repo') + + return parser.parse_args() + + +def apply(repo, patches): + git(repo, ['am', *patches]) + + +def sync(repo, patches_output): + if repo.name not in ('linux', 'linux-next') and 'linux-stable' not in repo.name: + raise RuntimeError(f"Supplied repo ('{repo}, {repo.name}') is not supported by cbl_ptchmn!") + + if not (mfc := git(repo, ['mfc']).stdout.strip()): + raise RuntimeError('My first commit could not be found?') + + # Generate a list of patches to remove. The Python documentation states + # that it is unspecified to change the contents of a directory when using + # Path.iterdir() to iterate over it. + patches_to_remove = list(patches_output.iterdir()) + for item in patches_to_remove: + item.unlink() + + fp_cmd = ['fp', f"--base={mfc}^", '-o', patches_output, f"{mfc}^..HEAD"] + git_loud(repo, fp_cmd) + + status_cmd = ['--no-optional-locks', 'status', '-u', '--porcelain'] + if git(patches_output, status_cmd).stdout.strip(): + git(patches_output, ['aa']) + + sha = git(repo, ['sha']).stdout.strip() + cmt_msg = f"patches: {repo.name}: {patches_output.name}: sync as of {sha}" + git_loud(patches_output, ['c', '-m', cmt_msg]) + + git(patches_output, ['push']) + + +if __name__ == '__main__': + args = parse_arguments() + + if not Path(args.directory, 'Makefile').exists(): + raise RuntimeError( + f"Supplied repository ('{args.directory}') does not appear to be a Linux kernel tree?") + + if not (patches_folder := get_patches_folder(args.directory)).exists(): + raise RuntimeError(f"Derived patches folder ('{patches_folder}') does not exist!") + + if args.apply: + apply(args.directory, patches_folder.iterdir()) + if args.sync: + sync(args.directory, patches_folder)