diff options
author | Konstantin Ryabitsev <konstantin@linuxfoundation.org> | 2020-05-15 17:38:36 -0400 |
---|---|---|
committer | Konstantin Ryabitsev <konstantin@linuxfoundation.org> | 2020-05-15 17:44:23 -0400 |
commit | e4e1f6e35d147bb567e3c2d996e628fe1ca6467a (patch) | |
tree | dd184ff645423dcf2a4c38c0170f75d7618beb96 /b4/diff.py | |
parent | 49b6717b785b82258f3647c79095673a8a3d8bac (diff) | |
download | b4-e4e1f6e35d147bb567e3c2d996e628fe1ca6467a.tar.gz |
Initial implementation of b4 diff
Based on feedback from Jason Gunthorpe, implement diffing of series by
creating fake git-am commit ranges. Here's an easy example:
b4 diff 20200511192156.1618284-1-mic@digikod.net
Suggested-by: Jason Gunthorpe <jgg@ziepe.ca>
Signed-off-by: Konstantin Ryabitsev <konstantin@linuxfoundation.org>
Diffstat (limited to 'b4/diff.py')
-rw-r--r-- | b4/diff.py | 134 |
1 files changed, 134 insertions, 0 deletions
diff --git a/b4/diff.py b/b4/diff.py new file mode 100644 index 0000000..2d9ebe0 --- /dev/null +++ b/b4/diff.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (C) 2020 by the Linux Foundation +# +__author__ = 'Konstantin Ryabitsev <konstantin@linuxfoundation.org>' + +import os +import sys +import b4 +import b4.mbox +import mailbox +from tempfile import mkstemp + + +logger = b4.logger + + +def make_fake_commit_range(gitdir, lser): + logger.info('Preparing fake-am for v%s: %s', lser.revision, lser.subject) + with b4.git_temp_worktree(gitdir): + # We are in a temporary chdir at this time, so writing to a known file should be safe + mbxf = '.__git-am__' + mbx = mailbox.mbox(mbxf) + # Logic largely borrowed from gj_tools + seenfiles = set() + for lmsg in lser.patches[1:]: + logger.debug('Looking at %s', lmsg.full_subject) + lmsg.load_hashes() + for fn, fi in lmsg.blob_indexes: + if fn in seenfiles: + # We already processed this file, so this blob won't match + continue + seenfiles.add(fn) + if set(fi) == {'0'}: + # New file creation, nothing to do here + logger.debug(' New file: %s', fn) + continue + # Try to grab full ref_id of this hash + ecode, out = b4.git_run_command(gitdir, ['rev-parse', fi]) + if ecode > 0: + logger.critical(' ERROR: Could not find matching blob for %s (%s)', fn, fi) + # TODO: better handling + return None, None + logger.debug(' Found matching blob for: %s', fn) + fullref = out.strip() + gitargs = ['update-index', '--add', '--cacheinfo', f'0644,{fullref},{fn}'] + ecode, out = b4.git_run_command(None, gitargs) + if ecode > 0: + logger.critical(' ERROR: Could not run update-index for %s (%s)', fn, fullref) + return None, None + mbx.add(lmsg.msg.as_string(policy=b4.emlpolicy).encode('utf-8')) + + mbx.close() + ecode, out = b4.git_run_command(None, ['write-tree']) + if ecode > 0: + logger.critical('ERROR: Could not write fake-am tree') + return None, None + treeid = out.strip() + # At this point we have a worktree with files that should cleanly receive a git am + gitargs = ['commit-tree', treeid + '^{tree}', '-F', '-'] + ecode, out = b4.git_run_command(None, gitargs, stdin='Initial fake commit'.encode('utf-8')) + if ecode > 0: + logger.critical('ERROR: Could not commit-tree') + return None, None + start_commit = out.strip() + b4.git_run_command(None, ['reset', '--hard', start_commit]) + ecode, out = b4.git_run_command(None, ['am', mbxf]) + if ecode > 0: + logger.critical('ERROR: Could not fake-am version %s', lser.revision) + return None, None + ecode, out = b4.git_run_command(None, ['rev-parse', 'HEAD']) + end_commit = out.strip() + logger.info(' range: %.12s..%.12s', start_commit, end_commit) + + return start_commit, end_commit + + +def main(cmdargs): + msgid = b4.get_msgid(cmdargs) + if cmdargs.wantvers and len(cmdargs.wantvers) > 2: + logger.critical('Can only compare two versions at a time') + sys.exit(1) + + # start by grabbing the mbox provided + savefile = mkstemp('b4-diff-to')[1] + mboxfile = b4.get_pi_thread_by_msgid(msgid, savefile, useproject=cmdargs.useproject, nocache=cmdargs.nocache) + if mboxfile is None: + logger.critical('Unable to retrieve thread: %s', msgid) + return + logger.info('Retrieved %s messages in the thread', len(mboxfile)) + b4.mbox.get_extra_series(mboxfile, direction=-1, wantvers=cmdargs.wantvers) + mbx = mailbox.mbox(mboxfile) + count = len(mbx) + logger.info('---') + logger.info('Analyzing %s messages in the thread', count) + lmbx = b4.LoreMailbox() + for key, msg in mbx.items(): + lmbx.add_message(msg) + if cmdargs.wantvers and len(cmdargs.wantvers) == 1: + upper = max(lmbx.series.keys()) + lower = cmdargs.wantvers[0] + elif cmdargs.wantvers and len(cmdargs.wantvers) == 2: + upper = max(cmdargs.wantvers) + lower = min(cmdargs.wantvers) + else: + upper = max(lmbx.series.keys()) + lower = min(lmbx.series.keys()) + + if upper not in lmbx.series: + logger.critical('Could not find revision %s', upper) + sys.exit(1) + if lower not in lmbx.series: + logger.critical('Could not find revision %s', lower) + sys.exit(1) + + # Prepare the lower fake-am range + lsc, lec = make_fake_commit_range(cmdargs.gitdir, lmbx.series[lower]) + if lsc is None or lec is None: + logger.critical('---') + logger.critical('Could not create fake-am range for lower series v%s', lower) + os.unlink(mboxfile) + sys.exit(1) + # Prepare the upper fake-am range + usc, uec = make_fake_commit_range(cmdargs.gitdir, lmbx.series[upper]) + if usc is None or uec is None: + logger.critical('---') + logger.critical('Could not create fake-am range for upper series v%s', upper) + os.unlink(mboxfile) + sys.exit(1) + logger.info('---') + logger.info('Success, you may now run:') + logger.info(' git range-diff %.12s..%.12s %.12s..%.12s', lsc, lec, usc, uec) + |