diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 16:14:31 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 16:14:31 +0000 |
commit | 2d5707c7479eacb3b1ad98e01b53f56a88f8fb78 (patch) | |
tree | d9c334e83692851c02e3e1b8e65570c97bc82481 /packaging/branch-from-patch | |
parent | Initial commit. (diff) | |
download | rsync-2d5707c7479eacb3b1ad98e01b53f56a88f8fb78.tar.xz rsync-2d5707c7479eacb3b1ad98e01b53f56a88f8fb78.zip |
Adding upstream version 3.2.7.upstream/3.2.7
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rwxr-xr-x | packaging/branch-from-patch | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/packaging/branch-from-patch b/packaging/branch-from-patch new file mode 100755 index 0000000..440b583 --- /dev/null +++ b/packaging/branch-from-patch @@ -0,0 +1,174 @@ +#!/usr/bin/env -S python3 -B + +# This script turns one or more diff files in the patches dir (which is +# expected to be a checkout of the rsync-patches git repo) into a branch +# in the main rsync git checkout. This allows the applied patch to be +# merged with the latest rsync changes and tested. To update the diff +# with the resulting changes, see the patch-update script. + +import os, sys, re, argparse, glob + +sys.path = ['packaging'] + sys.path + +from pkglib import * + +def main(): + global created, info, local_branch + + cur_branch, args.base_branch = check_git_state(args.base_branch, not args.skip_check, args.patches_dir) + + local_branch = get_patch_branches(args.base_branch) + + if args.delete_local_branches: + for name in sorted(local_branch): + branch = f"patch/{args.base_branch}/{name}" + cmd_chk(['git', 'branch', '-D', branch]) + local_branch = set() + + if args.add_missing: + for fn in sorted(glob.glob(f"{args.patches_dir}/*.diff")): + name = re.sub(r'\.diff$', '', re.sub(r'.+/', '', fn)) + if name not in local_branch and fn not in args.patch_files: + args.patch_files.append(fn) + + if not args.patch_files: + return + + for fn in args.patch_files: + if not fn.endswith('.diff'): + die(f"Filename is not a .diff file: {fn}") + if not os.path.isfile(fn): + die(f"File not found: {fn}") + + scanned = set() + info = { } + + patch_list = [ ] + for fn in args.patch_files: + m = re.match(r'^(?P<dir>.*?)(?P<name>[^/]+)\.diff$', fn) + patch = argparse.Namespace(**m.groupdict()) + if patch.name in scanned: + continue + patch.fn = fn + + lines = [ ] + commit_hash = None + with open(patch.fn, 'r', encoding='utf-8') as fh: + for line in fh: + m = re.match(r'^based-on: (\S+)', line) + if m: + commit_hash = m[1] + break + if (re.match(r'^index .*\.\..* \d', line) + or re.match(r'^diff --git ', line) + or re.match(r'^--- (old|a)/', line)): + break + lines.append(re.sub(r'\s*\Z', "\n", line, 1)) + info_txt = ''.join(lines).strip() + "\n" + lines = None + + parent = args.base_branch + patches = re.findall(r'patch -p1 <%s/(\S+)\.diff' % args.patches_dir, info_txt) + if patches: + last = patches.pop() + if last != patch.name: + warn(f"No identity patch line in {patch.fn}") + patches.append(last) + if patches: + parent = patches.pop() + if parent not in scanned: + diff_fn = patch.dir + parent + '.diff' + if not os.path.isfile(diff_fn): + die(f"Failed to find parent of {patch.fn}: {parent}") + # Add parent to args.patch_files so that we will look for the + # parent's parent. Any duplicates will be ignored. + args.patch_files.append(diff_fn) + else: + warn(f"No patch lines found in {patch.fn}") + + info[patch.name] = [ parent, info_txt, commit_hash ] + + patch_list.append(patch) + + created = set() + for patch in patch_list: + create_branch(patch) + + cmd_chk(['git', 'checkout', args.base_branch]) + + +def create_branch(patch): + if patch.name in created: + return + created.add(patch.name) + + parent, info_txt, commit_hash = info[patch.name] + parent = argparse.Namespace(dir=patch.dir, name=parent, fn=patch.dir + parent + '.diff') + + if parent.name == args.base_branch: + parent_branch = commit_hash if commit_hash else args.base_branch + else: + create_branch(parent) + parent_branch = '/'.join(['patch', args.base_branch, parent.name]) + + branch = '/'.join(['patch', args.base_branch, patch.name]) + print("\n" + '=' * 64) + print(f"Processing {branch} ({parent_branch})") + + if patch.name in local_branch: + cmd_chk(['git', 'branch', '-D', branch]) + + cmd_chk(['git', 'checkout', '-b', branch, parent_branch]) + + info_fn = 'PATCH.' + patch.name + with open(info_fn, 'w', encoding='utf-8') as fh: + fh.write(info_txt) + cmd_chk(['git', 'add', info_fn]) + + with open(patch.fn, 'r', encoding='utf-8') as fh: + patch_txt = fh.read() + + cmd_run('patch -p1'.split(), input=patch_txt) + + for fn in glob.glob('*.orig') + glob.glob('*/*.orig'): + os.unlink(fn) + + pos = 0 + new_file_re = re.compile(r'\nnew file mode (?P<mode>\d+)\s+--- /dev/null\s+\+\+\+ b/(?P<fn>.+)') + while True: + m = new_file_re.search(patch_txt, pos) + if not m: + break + os.chmod(m['fn'], int(m['mode'], 8)) + cmd_chk(['git', 'add', m['fn']]) + pos = m.end() + + while True: + cmd_chk('git status'.split()) + ans = input('Press Enter to commit, Ctrl-C to abort, or type a wild-name to add a new file: ') + if ans == '': + break + cmd_chk("git add " + ans, shell=True) + + while True: + s = cmd_run(['git', 'commit', '-a', '-m', f"Creating branch from {patch.name}.diff."]) + if not s.returncode: + break + s = cmd_run(['/bin/zsh']) + if s.returncode: + die('Aborting due to shell error code') + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description="Create a git patch branch from an rsync patch file.", add_help=False) + parser.add_argument('--branch', '-b', dest='base_branch', metavar='BASE_BRANCH', default='master', help="The branch the patch is based on. Default: master.") + parser.add_argument('--add-missing', '-a', action='store_true', help="Add a branch for every patches/*.diff that doesn't have a branch.") + parser.add_argument('--skip-check', action='store_true', help="Skip the check that ensures starting with a clean branch.") + parser.add_argument('--delete', dest='delete_local_branches', action='store_true', help="Delete all the local patch/BASE/* branches, not just the ones that are being recreated.") + parser.add_argument('--patches-dir', '-p', metavar='DIR', default='patches', help="Override the location of the rsync-patches dir. Default: patches.") + parser.add_argument('patch_files', metavar='patches/DIFF_FILE', nargs='*', help="Specify what patch diff files to process. Default: all of them.") + parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.") + args = parser.parse_args() + main() + +# vim: sw=4 et ft=python |