From patchwork Mon Jul 6 03:42:02 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 240797 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 5 Jul 2020 21:42:02 -0600 Subject: [RFC PATCH 15/16] patman: Support updating a branch with review tags In-Reply-To: <20200706034203.2171077-1-sjg@chromium.org> References: <20200706034203.2171077-1-sjg@chromium.org> Message-ID: <20200706034203.2171077-16-sjg@chromium.org> It is tedious to add review tags into the local branch and errors can sometimes be made. Add an option to create a new branch with the review tags obtained from patchwork. Signed-off-by: Simon Glass --- tools/patman/README | 17 +++++++++++-- tools/patman/control.py | 9 ++++--- tools/patman/main.py | 7 +++++- tools/patman/status.py | 54 ++++++++++++++++++++++++++++++++++++++--- 4 files changed, 78 insertions(+), 9 deletions(-) diff --git a/tools/patman/README b/tools/patman/README index a85974740f..595a0d616b 100644 --- a/tools/patman/README +++ b/tools/patman/README @@ -11,11 +11,13 @@ This tool is a Python script which: - Runs the patches through checkpatch.pl and its own checks - Optionally emails them out to selected people -It also shows review tags from Patchwork so you can update your local patches. +It also has some Patchwork features: +- shows review tags from Patchwork so you can update your local patches +- pulls these down into a new branch on request It is intended to automate patch creation and make it a less error-prone process. It is useful for U-Boot and Linux work so far, -since it uses the checkpatch.pl script. +since they use the checkpatch.pl script. It is configured almost entirely by tags it finds in your commits. This means that you can work on a number of different branches at @@ -378,6 +380,17 @@ attracted another review each. If the series needs changes, you can update these commits with the new review tag before sending the next version of the series. +To automatically pull into these tags into a new branch, use the -d option: + + patman status -d mtrr4 + +This will create a new 'mtrr4' branch which is the same as your current branch +but has the new review tags in it. You can check that this worked with: + + patman -b mtrr4 status + +which should show that there are no new responses compared to this new branch. + Example Work Flow ================= diff --git a/tools/patman/control.py b/tools/patman/control.py index 3bb3c236e4..b6ba0a56c0 100644 --- a/tools/patman/control.py +++ b/tools/patman/control.py @@ -174,11 +174,11 @@ def send(args): args.limit, args.dry_run, args.in_reply_to, args.thread, args.smtp_server) -def patchwork_status(branch, count, start, end): +def patchwork_status(branch, count, start, end, dest_branch, force): """Check the status of patches in patchwork This finds the series in patchwork using the Series-link tag, checks for new - comments / review tags and displays them + review tags, displays then and creates a new branch with the review tags. Args: branch (str): Branch to create patches from (None = current) @@ -187,6 +187,9 @@ def patchwork_status(branch, count, start, end): start (int): Start partch to use (0=first / top of branch) end (int): End patch to use (0=last one in series, 1=one before that, etc.) + dest_branch (str): Name of new branch to create with the updated tags + (None to not create a branch) + force (bool): With dest_branch, force overwriting an existing branch Raises: ValueError: if the branch has no Series-link value @@ -203,4 +206,4 @@ def patchwork_status(branch, count, start, end): # Import this here to avoid failing on other commands if the dependencies # are not present from patman import status - status.check_patchwork_status(series, link, branch) + status.check_patchwork_status(series, link, branch, dest_branch, force) diff --git a/tools/patman/main.py b/tools/patman/main.py index 227eb3b228..c8fc035c10 100755 --- a/tools/patman/main.py +++ b/tools/patman/main.py @@ -90,6 +90,10 @@ AddCommonArgs(test_parser) status = subparsers.add_parser('status', help='Check status of patches in patchwork') +status.add_argument('-d', '--dest-branch', type=str, + help='Name of branch to create with collected responses') +status.add_argument('-f', '--force', action='store_true', + help='Force overwriting an existing branch') AddCommonArgs(status) # Parse options twice: first to get the project and second to handle @@ -156,7 +160,8 @@ elif args.cmd == 'send': elif args.cmd == 'status': ret_code = 0 try: - control.patchwork_status(args.branch, args.count, args.start, args.end) + control.patchwork_status(args.branch, args.count, args.start, args.end, + args.dest_branch, args.force) except Exception as e: print('patman: %s: %s' % (type(e).__name__, e)) if args.debug: diff --git a/tools/patman/status.py b/tools/patman/status.py index c2e98a5d11..71124d6e5e 100644 --- a/tools/patman/status.py +++ b/tools/patman/status.py @@ -304,7 +304,48 @@ def ShowResponses(rtags, indent, is_new): count += 1 return count -def check_patchwork_status(series, url, branch): +def CreateBranch(series, new_rtag_list, branch, dest_branch, overwrite): + if branch == dest_branch: + raise ValueError( + 'Destination branch must not be the same as the original branch') + repo = pygit2.Repository('.') + count = len(series.commits) + old_br = repo.branches[branch] + new_br = repo.branches.get(dest_branch) + if new_br: + if not overwrite: + raise ValueError("Branch '%s' already exists (-f to overwrite)" % + dest_branch) + new_br.delete() + target = repo.revparse_single('%s~%d' % (branch, count)) + repo.branches.local.create(dest_branch, target) + + num_added = 0 + for seq in range(count): + basket = repo.branches.get(dest_branch) + cherry = repo.revparse_single('%s~%d' % (branch, count - seq - 1)) + + base = repo.merge_base(cherry.oid, basket.target) + base_tree = cherry.parents[0].tree + + index = repo.merge_trees(base_tree, basket, cherry) + tree_id = index.write_tree(repo) + + author = cherry.author + committer = cherry.committer + lines = [] + for tag, people in new_rtag_list[seq].items(): + for who in people: + lines.append('%s: %s' % (tag, who)) + num_added += 1 + message = cherry.message + '\n'.join(lines) + + basket = repo.create_commit( + basket.name, cherry.author, cherry.committer, message, tree_id, + [basket.target]) + return num_added + +def check_patchwork_status(series, url, branch, dest_branch, force): patches = CollectPatches(series, url) col = terminal.Color() count = len(patches) @@ -330,5 +371,12 @@ def check_patchwork_status(series, url, branch): ShowResponses(base_rtags, indent, False) num_to_add += ShowResponses(new_rtags, indent, True) - print("%d new response%s available in patchwork" % - (num_to_add, 's' if num_to_add != 1 else '')) + print("%d new response%s available in patchwork%s" % + (num_to_add, 's' if num_to_add != 1 else '', + '' if dest_branch else ' (use -d to write them to a new branch)')) + + if dest_branch: + num_added = CreateBranch(series, new_rtag_list, branch, + dest_branch, force) + print("%d response%s added from patchwork into new branch '%s'" % + (num_added, 's' if num_added != 1 else '', dest_branch))