diff options
Diffstat (limited to 'b4/__init__.py')
-rw-r--r-- | b4/__init__.py | 115 |
1 files changed, 112 insertions, 3 deletions
diff --git a/b4/__init__.py b/b4/__init__.py index b6f25fe..3cdad1c 100644 --- a/b4/__init__.py +++ b/b4/__init__.py @@ -587,7 +587,7 @@ class LoreSeries: return mbx - def check_applies_clean(self, topdir, when=None): + def check_applies_clean(self, gitdir, when=None): # Go through indexes and see if this series should apply cleanly mismatches = 0 seenfiles = set() @@ -603,7 +603,7 @@ class LoreSeries: if set(bh) == {'0'}: # New file, will for sure apply clean continue - fullpath = os.path.join(topdir, fn) + fullpath = os.path.join(gitdir, fn) if when is None: if not os.path.exists(fullpath): mismatches += 1 @@ -611,7 +611,7 @@ class LoreSeries: cmdargs = ['hash-object', fullpath] ecode, out = git_run_command(None, cmdargs) else: - gitdir = os.path.join(topdir, '.git') + gitdir = os.path.join(gitdir, '.git') logger.debug('Checking hash on %s:%s', when, fn) # XXX: We should probably pipe the two commands instead of reading into memory, # so something to consider for the future @@ -632,6 +632,115 @@ class LoreSeries: return len(seenfiles), mismatches + def make_fake_am_range(self, gitdir): + start_commit = end_commit = None + # Do we have it in cache already? + cachedir = get_cache_dir() + # Use the msgid of the first non-None patch in the series + msgid = None + for lmsg in self.patches: + if lmsg is not None: + msgid = lmsg.msgid + break + if msgid is None: + logger.critical('Cannot operate on an empty series') + return None, None + cachefile = os.path.join(cachedir, '%s.fakeam' % urllib.parse.quote_plus(msgid)) + if os.path.exists(cachefile): + stalecache = False + with open(cachefile, 'r') as fh: + cachedata = fh.read() + chunks = cachedata.strip().split() + if len(chunks) == 2: + start_commit, end_commit = chunks + else: + stalecache = True + if start_commit is not None and end_commit is not None: + # Make sure they are still there + ecode, out = git_run_command(gitdir, ['cat-file', '-e', start_commit]) + if ecode > 0: + stalecache = True + else: + ecode, out = git_run_command(gitdir, ['cat-file', '-e', end_commit]) + if ecode > 0: + stalecache = True + else: + logger.debug('Using previously generated range') + return start_commit, end_commit + + if stalecache: + logger.debug('Stale cache for [v%s] %s', self.revision, self.subject) + os.unlink(cachefile) + + logger.info('Preparing fake-am for v%s: %s', self.revision, self.subject) + with 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 self.patches[1:]: + logger.debug('Looking at %s', lmsg.full_subject) + lmsg.load_hashes() + if not len(lmsg.blob_indexes): + logger.critical('ERROR: some patches do not have indexes') + logger.critical(' unable to create a fake-am range') + return None, None + 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 = git_run_command(gitdir, ['rev-parse', fi]) + if ecode > 0: + logger.critical(' ERROR: Could not find matching blob for %s (%s)', fn, fi) + logger.critical(' If you know on which tree this patchset is based,') + logger.critical(' add it as a remote and perform "git remote update"') + logger.critical(' in order to fetch the missing objects.') + 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 = 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=emlpolicy).encode('utf-8')) + + mbx.close() + ecode, out = 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 = 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() + git_run_command(None, ['reset', '--hard', start_commit]) + ecode, out = git_run_command(None, ['am', mbxf]) + if ecode > 0: + logger.critical('ERROR: Could not fake-am version %s', self.revision) + return None, None + ecode, out = git_run_command(None, ['rev-parse', 'HEAD']) + end_commit = out.strip() + logger.info(' range: %.12s..%.12s', start_commit, end_commit) + + with open(cachefile, 'w') as fh: + logger.debug('Saving into cache: %s', cachefile) + logger.debug(' %s..%s', start_commit, end_commit) + fh.write(f'{start_commit} {end_commit}\n') + + return start_commit, end_commit + def save_cover(self, outfile): cover_msg = self.patches[0].get_am_message(add_trailers=False, trailer_order=None) with open(outfile, 'w') as fh: |