def diff_mboxes(cmdargs): chunks = list() for mboxfile in cmdargs.ambox: if not os.path.exists(mboxfile): logger.critical('Cannot open %s', mboxfile) return None, None mbx = mailbox.mbox(mboxfile) count = len(mbx) logger.info('Loading %s messages from %s', count, mboxfile) lmbx = b4.LoreMailbox() for key, msg in mbx.items(): lmbx.add_message(msg) if len(lmbx.series) < 1: logger.critical('No valid patches found in %s', mboxfile) sys.exit(1) if len(lmbx.series) > 1: logger.critical( 'More than one series version in %s, will use latest', mboxfile) chunks.append(lmbx.series[max(lmbx.series.keys())]) return chunks
def mbox_to_am(mboxfile, config, cmdargs): outdir = cmdargs.outdir wantver = cmdargs.wantver wantname = cmdargs.wantname covertrailers = cmdargs.covertrailers mbx = mailbox.mbox(mboxfile) count = len(mbx) logger.info('Analyzing %s messages in the thread', count) lmbx = b4.LoreMailbox() # Go through the mbox once to populate base series for key, msg in mbx.items(): lmbx.add_message(msg) lser = lmbx.get_series(revision=wantver) if lser is None and wantver is None: logger.critical('No patches found.') return if lser is None: logger.critical('Unable to find revision %s', wantver) return if len(lmbx.series) > 1 and not wantver: logger.info('Will use the latest revision: v%s', lser.revision) logger.info('You can pick other revisions using the -vN flag') if wantname: slug = wantname if wantname.find('.') > -1: slug = '.'.join(wantname.split('.')[:-1]) else: slug = lser.get_slug() am_filename = os.path.join(outdir, '%s.mbx' % slug) am_cover = os.path.join(outdir, '%s.cover' % slug) if os.path.exists(am_filename): os.unlink(am_filename) logger.info('---') logger.critical('Writing %s', am_filename) mbx = mailbox.mbox(am_filename) am_mbx = lser.save_am_mbox(mbx, cmdargs.noaddtrailers, covertrailers, trailer_order=config['trailer-order'], addmysob=cmdargs.addmysob, addlink=cmdargs.addlink, linkmask=config['linkmask']) logger.info('---') logger.critical('Total patches: %s', len(am_mbx)) if lser.has_cover and lser.patches[ 0].followup_trailers and not covertrailers: # Warn that some trailers were sent to the cover letter logger.critical('---') logger.critical('NOTE: Some trailers were sent to the cover letter:') for trailer in lser.patches[0].followup_trailers: logger.critical(' %s: %s', trailer[0], trailer[1]) logger.critical('NOTE: Rerun with -t to apply them to all patches') logger.critical('---') if not lser.complete: logger.critical('WARNING: Thread incomplete!') if lser.has_cover: lser.save_cover(am_cover) top_msgid = None first_body = None for lmsg in lser.patches: if lmsg is not None: first_body = lmsg.body top_msgid = lmsg.msgid break if top_msgid is None: logger.critical('Could not find any patches in the series.') return linkurl = config['linkmask'] % top_msgid if cmdargs.quiltready: q_dirname = os.path.join(outdir, '%s.patches' % slug) am_mbox_to_quilt(am_mbx, q_dirname) logger.critical('Quilt: %s', q_dirname) logger.critical(' Link: %s', linkurl) base_commit = None matches = re.search(r'base-commit: .*?([0-9a-f]+)', first_body, re.MULTILINE) if matches: base_commit = matches.groups()[0] else: # Try a more relaxed search matches = re.search(r'based on .*?([0-9a-f]{40})', first_body, re.MULTILINE) if matches: base_commit = matches.groups()[0] if base_commit: logger.critical(' Base: %s', base_commit) logger.critical(' git checkout -b %s %s', slug, base_commit) logger.critical(' git am %s', am_filename) else: logger.critical(' Base: not found, sorry') logger.critical(' git checkout -b %s master', slug) logger.critical(' git am %s', am_filename) am_mbx.close() return am_filename
def diff_same_thread_series(cmdargs): msgid = b4.get_msgid(cmdargs) wantvers = cmdargs.wantvers if wantvers and len(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] # Do we have a cache of this lookup? identifier = msgid if wantvers: identifier += '-' + '-'.join([str(x) for x in wantvers]) if cmdargs.useproject: identifier += '-' + cmdargs.useproject cachefile = b4.get_cache_file(identifier, suffix='diff.mbx') if os.path.exists(cachefile) and not cmdargs.nocache: logger.info('Using cached copy of the lookup') shutil.copyfile(cachefile, savefile) mboxfile = savefile else: 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 b4.mbox.get_extra_series(mboxfile, direction=-1, wantvers=wantvers) shutil.copyfile(mboxfile, cachefile) 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) mbx.close() os.unlink(savefile) if wantvers and len(wantvers) == 1: upper = max(lmbx.series.keys()) lower = wantvers[0] elif wantvers and len(wantvers) == 2: upper = max(wantvers) lower = min(wantvers) else: upper = max(lmbx.series.keys()) lower = min(lmbx.series.keys()) if upper == lower: logger.critical('ERROR: Could not auto-find previous revision') logger.critical( ' Run "b4 am -T" manually, then "b4 diff -m mbx1 mbx2"') return None, None if upper not in lmbx.series: return None, None if lower not in lmbx.series: return None, None if not lmbx.series[lower].complete: lmbx.backfill(lower) if not lmbx.series[upper].complete: lmbx.backfill(upper) return lmbx.series[lower], lmbx.series[upper]
def mbox_to_am(mboxfile, cmdargs): config = b4.get_main_config() outdir = cmdargs.outdir if outdir == '-': cmdargs.nocover = True wantver = cmdargs.wantver wantname = cmdargs.wantname covertrailers = cmdargs.covertrailers if os.path.isdir(mboxfile): mbx = mailbox.Maildir(mboxfile) else: mbx = mailbox.mbox(mboxfile) count = len(mbx) logger.info('Analyzing %s messages in the thread', count) lmbx = b4.LoreMailbox() # Go through the mbox once to populate base series for key, msg in mbx.items(): lmbx.add_message(msg) lser = lmbx.get_series(revision=wantver, sloppytrailers=cmdargs.sloppytrailers) if lser is None and wantver is None: logger.critical('No patches found.') return if lser is None: logger.critical('Unable to find revision %s', wantver) return if len(lmbx.series) > 1 and not wantver: logger.info('Will use the latest revision: v%s', lser.revision) logger.info('You can pick other revisions using the -vN flag') if wantname: slug = wantname if wantname.find('.') > -1: slug = '.'.join(wantname.split('.')[:-1]) gitbranch = slug else: slug = lser.get_slug(extended=True) gitbranch = lser.get_slug(extended=False) if outdir != '-': am_filename = os.path.join(outdir, '%s.mbx' % slug) am_cover = os.path.join(outdir, '%s.cover' % slug) if os.path.exists(am_filename): os.unlink(am_filename) else: # Create a temporary file that we will remove later am_filename = mkstemp('b4-am-stdout')[1] am_cover = None logger.info('---') if cmdargs.cherrypick: cherrypick = list() if cmdargs.cherrypick == '_': msgid = b4.get_msgid(cmdargs) # Only grab the exact msgid provided at = 0 for lmsg in lser.patches[1:]: at += 1 if lmsg and lmsg.msgid == msgid: cherrypick = [at] cmdargs.cherrypick = f'<{msgid}>' break if not len(cherrypick): logger.critical( 'Specified msgid is not present in the series, cannot cherrypick' ) sys.exit(1) elif cmdargs.cherrypick.find('*') >= 0: # Globbing on subject at = 0 for lmsg in lser.patches[1:]: at += 1 if fnmatch.fnmatch(lmsg.subject, cmdargs.cherrypick): cherrypick.append(at) if not len(cherrypick): logger.critical( 'Could not match "%s" to any subjects in the series', cmdargs.cherrypick) sys.exit(1) else: cherrypick = list( b4.parse_int_range(cmdargs.cherrypick, upper=len(lser.patches) - 1)) else: cherrypick = None logger.critical('Writing %s', am_filename) mbx = mailbox.mbox(am_filename) try: am_mbx = lser.save_am_mbox(mbx, noaddtrailers=cmdargs.noaddtrailers, covertrailers=covertrailers, trailer_order=config['trailer-order'], addmysob=cmdargs.addmysob, addlink=cmdargs.addlink, linkmask=config['linkmask'], cherrypick=cherrypick) except KeyError: sys.exit(1) logger.info('---') if cherrypick is None: logger.critical('Total patches: %s', len(am_mbx)) else: logger.info('Total patches: %s (cherrypicked: %s)', len(am_mbx), cmdargs.cherrypick) if lser.has_cover and lser.patches[ 0].followup_trailers and not covertrailers: # Warn that some trailers were sent to the cover letter logger.critical('---') logger.critical('NOTE: Some trailers were sent to the cover letter:') for trailer in lser.patches[0].followup_trailers: logger.critical(' %s: %s', trailer[0], trailer[1]) logger.critical('NOTE: Rerun with -t to apply them to all patches') if len(lser.trailer_mismatches): logger.critical('---') logger.critical( 'NOTE: some trailers ignored due to from/email mismatches:') for tname, tvalue, fname, femail in lser.trailer_mismatches: logger.critical(' ! Trailer: %s: %s', tname, tvalue) logger.critical(' Msg From: %s <%s>', fname, femail) logger.critical('NOTE: Rerun with -S to apply them anyway') topdir = None # Are we in a git tree and if so, what is our toplevel? gitargs = ['rev-parse', '--show-toplevel'] lines = b4.git_get_command_lines(None, gitargs) if len(lines) == 1: topdir = lines[0] if cmdargs.threeway: if not topdir: logger.critical('WARNING: cannot prepare 3-way (not in a git dir)') elif not lser.complete: logger.critical( 'WARNING: cannot prepare 3-way (series incomplete)') else: rstart, rend = lser.make_fake_am_range(gitdir=None) if rstart and rend: logger.info( 'Prepared a fake commit range for 3-way merge (%.12s..%.12s)', rstart, rend) logger.critical('---') if not lser.complete and not cmdargs.cherrypick: logger.critical('WARNING: Thread incomplete!') if lser.has_cover and not cmdargs.nocover: lser.save_cover(am_cover) top_msgid = None first_body = None for lmsg in lser.patches: if lmsg is not None: first_body = lmsg.body top_msgid = lmsg.msgid break if top_msgid is None: logger.critical('Could not find any patches in the series.') return linkurl = config['linkmask'] % top_msgid if cmdargs.quiltready: q_dirname = os.path.join(outdir, '%s.patches' % slug) am_mbox_to_quilt(am_mbx, q_dirname) logger.critical('Quilt: %s', q_dirname) logger.critical(' Link: %s', linkurl) base_commit = None matches = re.search(r'base-commit: .*?([0-9a-f]+)', first_body, re.MULTILINE) if matches: base_commit = matches.groups()[0] else: # Try a more relaxed search matches = re.search(r'based on .*?([0-9a-f]{40})', first_body, re.MULTILINE) if matches: base_commit = matches.groups()[0] if base_commit: logger.critical(' Base: %s', base_commit) logger.critical(' git checkout -b %s %s', gitbranch, base_commit) if cmdargs.outdir != '-': logger.critical(' git am %s', am_filename) else: cleanmsg = '' if topdir is not None: checked, mismatches = lser.check_applies_clean(topdir) if mismatches == 0 and checked != mismatches: cleanmsg = ' (applies clean to current tree)' elif cmdargs.guessbase: # Look at the last 10 tags and see if it applies cleanly to # any of them. I'm not sure how useful this is, but I'm going # to put it in for now and maybe remove later if it causes # problems or slowness if checked != mismatches: best_matches = mismatches cleanmsg = ' (best guess: current tree)' else: best_matches = None # sort the tags by authordate gitargs = ['tag', '-l', '--sort=-taggerdate'] lines = b4.git_get_command_lines(None, gitargs) if lines: # Check last 10 tags for tag in lines[:10]: logger.debug('Checking base-commit possibility for %s', tag) checked, mismatches = lser.check_applies_clean( topdir, tag) if mismatches == 0 and checked != mismatches: cleanmsg = ' (applies clean to: %s)' % tag break # did they all mismatch? if checked == mismatches: continue if best_matches is None or mismatches < best_matches: best_matches = mismatches cleanmsg = ' (best guess: %s)' % tag logger.critical(' Base: not found%s', cleanmsg) if cmdargs.outdir != '-': logger.critical(' git am %s', am_filename) am_mbx.close() if cmdargs.outdir == '-': logger.info('---') with open(am_filename, 'rb') as fh: shutil.copyfileobj(fh, sys.stdout.buffer) os.unlink(am_filename) thanks_record_am(lser, cherrypick=cherrypick)