def __refresh_spill(annotate): stack = directory.repository.current_stack # Fetch the topmost patch. patchname = get_patch(stack, None) cd = stack.patches[patchname].data # Set the tree of the patch to the parent. cd = cd.set_tree(cd.parent.data.tree) log_msg = 'refresh (spill)' if annotate: log_msg += '\n\n' + annotate trans = StackTransaction(stack, log_msg, allow_conflicts=True) trans.patches[patchname] = stack.repository.commit(cd) try: # Either a complete success, or a conflict during push. But in # either case, we've successfully effected the edits the user # asked us for. return trans.run() except TransactionException: # Transaction aborted -- we couldn't check out files due to # dirty index/worktree. The edits were not carried out. out.error('Unable to spill the topmost patch') return utils.STGIT_COMMAND_ERROR
def absorb(stack, patch_name, temp_name, edit_fun, annotate=None): """Absorb the temp patch into the target patch.""" if annotate: log_msg = 'refresh\n\n' + annotate else: log_msg = 'refresh' trans = StackTransaction(stack, log_msg) iw = stack.repository.default_iw if patch_name in trans.applied: absorb_func = absorb_applied else: absorb_func = absorb_unapplied if absorb_func(trans, iw, patch_name, temp_name, edit_fun): def info_msg(): pass else: def info_msg(): out.warn( 'The new changes did not apply cleanly to %s.' % patch_name, 'They were saved in %s.' % temp_name, ) r = trans.run(iw) info_msg() return r
def func(parser, options, args): """Create a new patch.""" stack = directory.repository.current_stack if stack.repository.default_index.conflicts(): raise CmdException( 'Cannot create a new patch -- resolve conflicts first') # Choose a name for the new patch -- or None, which means make one # up later when we've gotten hold of the commit message. if len(args) == 0: name = None elif len(args) == 1: name = args[0] if not stack.patches.is_name_valid(name): raise CmdException('Invalid patch name: "%s"' % name) elif name in stack.patches: raise CmdException('%s: patch already exists' % name) else: parser.error('incorrect number of arguments') if options.verbose: verbose = options.verbose else: verbose = config.getbool('stgit.new.verbose') or False cd = CommitData( tree=stack.head.data.tree, parents=[stack.head], message='', author=Person.author(), committer=Person.committer(), ) cd = update_commit_data( stack.repository, cd, message=options.message, author=options.author(cd.author), trailers=options.trailers, edit=(not options.save_template and options.message is None), verbose=verbose, ) if options.save_template: options.save_template(cd.message) return utils.STGIT_SUCCESS if not options.no_verify: cd = run_commit_msg_hook(stack.repository, cd) if name is None: name = stack.patches.make_name(cd.message_str) # Write the new patch. check_head_top_equal(stack) trans = StackTransaction(stack) trans.patches[name] = stack.repository.commit(cd) trans.applied.append(name) return trans.execute('new: %s' % name)
def make_temp_patch(stack, patch_name, tree): """Commit tree to temp patch, in a complete transaction.""" commit = stack.repository.commit( CommitData( tree=tree, parents=[stack.head], message='Refresh of %s' % patch_name, )) temp_name = utils.make_patch_name('refresh-temp', stack.patches.exists) trans = StackTransaction(stack, 'refresh (create temporary patch)') trans.patches[temp_name] = commit trans.applied.append(temp_name) return ( trans.run(stack.repository.default_iw, print_current_patch=False), temp_name, )
def func(parser, options, args): """Create a new patch.""" stack = directory.repository.current_stack if stack.repository.default_index.conflicts(): raise CmdException( 'Cannot create a new patch -- resolve conflicts first') # Choose a name for the new patch -- or None, which means make one # up later when we've gotten hold of the commit message. if len(args) == 0: name = None elif len(args) == 1: name = args[0] if stack.patches.exists(name): raise CmdException('%s: patch already exists' % name) elif not stack.patches.is_name_valid(name): raise CmdException('Invalid patch name: "%s"' % name) else: parser.error('incorrect number of arguments') cd = CommitData( tree=stack.head.data.tree, parents=[stack.head], message='', author=Person.author(), committer=Person.committer(), ) cd = update_commit_data(cd, options) if options.save_template: options.save_template(cd.message.encode('utf-8')) return utils.STGIT_SUCCESS if not options.no_verify: cd = run_commit_msg_hook(stack.repository, cd) if name is None: name = utils.make_patch_name(cd.message, stack.patches.exists) assert stack.patches.is_name_valid(name) # Write the new patch. stack.repository.default_iw trans = StackTransaction(stack, 'new: %s' % name) trans.patches[name] = stack.repository.commit(cd) trans.applied.append(name) return trans.run()
def prepare_rebase(stack, cmd_name): # pop all patches iw = stack.repository.default_iw trans = StackTransaction(stack, '%s (pop)' % cmd_name, check_clean_iw=iw) out.start('Popping all applied patches') try: trans.reorder_patches( applied=[], unapplied=trans.applied + trans.unapplied, iw=iw, allow_interactive=True, ) except TransactionException: pass retval = trans.run(iw, print_current_patch=False) if retval: out.done('Failed to pop applied patches') else: out.done() return retval
def absorb(stack, patch_name, temp_name, edit_fun, annotate=None): """Absorb the temp patch into the target patch.""" log_msg = 'refresh' if annotate: log_msg += '\n\n' + annotate check_head_top_equal(stack) trans = StackTransaction(stack) iw = stack.repository.default_iw if patch_name in trans.applied: absorbed = absorb_applied(trans, iw, patch_name, temp_name, edit_fun) else: absorbed = absorb_unapplied(trans, iw, patch_name, temp_name, edit_fun) r = trans.execute(log_msg, iw) if not absorbed: out.warn( 'The new changes did not apply cleanly to %s.' % patch_name, 'They were saved in %s.' % temp_name, ) return r
def post_rebase(stack, applied, cmd_name, check_merged): iw = stack.repository.default_iw trans = StackTransaction(stack, '%s (reapply)' % cmd_name) try: if check_merged: merged = set(trans.check_merged(applied)) else: merged = set() for pn in applied: trans.push_patch( pn, iw, allow_interactive=True, already_merged=pn in merged ) except TransactionHalted: pass return trans.run(iw)
def hide_patches(stack, iw, patches): def allow_conflicts(trans): # Allow conflicts if the topmost patch stays the same. if stack.patchorder.applied: return trans.applied and trans.applied[ -1] == stack.patchorder.applied[-1] else: return not trans.applied trans = StackTransaction(stack, 'hide', allow_conflicts=allow_conflicts) try: to_push = trans.hide_patches(lambda pn: pn in patches) for pn in to_push: trans.push_patch(pn, iw) except TransactionHalted: pass return trans.execute('hide', iw)
def __pick_commit(stack, ref_stack, iw, commit, patchname, options): """Pick a commit.""" repository = stack.repository if options.name: patchname = options.name elif patchname and options.revert: patchname = 'revert-' + patchname if patchname: patchname = find_patch_name(patchname, stack.patches.exists) else: patchname = make_patch_name(commit.data.message_str, stack.patches.exists) if options.parent: parent = git_commit(options.parent, repository, ref_stack.name) else: parent = commit.data.parent if not options.revert: bottom = parent top = commit else: bottom = commit top = parent if options.fold: out.start('Folding commit %s' % commit.sha1) diff = repository.diff_tree(bottom.data.tree, top.data.tree, pathlimits=options.file) if diff: try: # try a direct git apply first iw.apply(diff, quiet=True) except MergeException: if options.file: out.done('conflict(s)') out.error('%s does not apply cleanly' % patchname) return STGIT_CONFLICT else: try: iw.merge( bottom.data.tree, stack.head.data.tree, top.data.tree, ) except MergeConflictException as e: out.done('%s conflicts' % len(e.conflicts)) out.error('%s does not apply cleanly' % patchname, *e.conflicts) return STGIT_CONFLICT out.done() else: out.done('no changes') return STGIT_SUCCESS elif options.update: files = [ fn1 for _, _, _, _, _, fn1, fn2 in repository.diff_tree_files( stack.top.data.parent.data.tree, stack.top.data.tree) ] diff = repository.diff_tree(bottom.data.tree, top.data.tree, pathlimits=files) out.start('Updating with commit %s' % commit.sha1) try: iw.apply(diff, quiet=True) except MergeException: out.done('conflict(s)') out.error('%s does not apply cleanly' % patchname) return STGIT_CONFLICT else: out.done() return STGIT_SUCCESS else: author = commit.data.author message = commit.data.message_str if options.revert: author = Person.author() if message: lines = message.splitlines() subject = lines[0] body = '\n'.join(lines[2:]) else: subject = commit.sha1 body = '' message = 'Revert "%s"\n\nThis reverts commit %s.\n\n%s\n' % ( subject, commit.sha1, body, ) elif options.expose: fmt = config.get('stgit.pick.expose-format') message = Run('git', 'show', '--no-patch', '--pretty=' + fmt, commit.sha1).raw_output() message = message.rstrip() + '\n' out.start('Importing commit %s' % commit.sha1) new_commit = repository.commit( CommitData( tree=top.data.tree, parents=[bottom], message=message, author=author, )) trans = StackTransaction( stack, 'pick %s from %s' % (patchname, ref_stack.name)) trans.patches[patchname] = new_commit trans.unapplied.append(patchname) if not options.unapplied: try: trans.push_patch(patchname, iw, allow_interactive=True) except TransactionHalted: pass retval = trans.run(iw, print_current_patch=False) if retval == STGIT_CONFLICT: out.done('conflict(s)') elif stack.patches.get(patchname).is_empty(): out.done('empty patch') else: out.done() return retval
def func(parser, options, args): repository = directory.repository if options.create: if len(args) == 0 or len(args) > 2: parser.error('incorrect number of arguments') branch_name = args[0] committish = None if len(args) < 2 else args[1] if committish: check_local_changes(repository) check_conflicts(repository.default_iw) try: stack = repository.get_stack() except (DetachedHeadException, StackException): pass else: check_head_top_equal(stack) stack = __create_branch(branch_name, committish) out.info('Branch "%s" created' % branch_name) log.log_entry(stack, 'branch --create %s' % stack.name) return elif options.clone: cur_branch = Branch(repository, repository.current_branch_name) if len(args) == 0: clone_name = cur_branch.name + time.strftime('-%C%y%m%d-%H%M%S') elif len(args) == 1: clone_name = args[0] else: parser.error('incorrect number of arguments') check_local_changes(repository) check_conflicts(repository.default_iw) try: stack = repository.current_stack except StackException: stack = None base = repository.refs.get(repository.head_ref) else: check_head_top_equal(stack) base = stack.base out.start('Cloning current branch to "%s"' % clone_name) clone = Stack.create( repository, name=clone_name, create_at=base, parent_remote=cur_branch.parent_remote, parent_branch=cur_branch.name, ) if stack: for pn in stack.patchorder.all_visible: patch = stack.patches.get(pn) clone.patches.new(pn, patch.commit, 'clone %s' % stack.name) clone.patchorder.set_order(applied=[], unapplied=stack.patchorder.all_visible, hidden=[]) trans = StackTransaction(clone, 'clone') try: for pn in stack.patchorder.applied: trans.push_patch(pn) except TransactionHalted: pass trans.run() prefix = 'branch.%s.' % cur_branch.name new_prefix = 'branch.%s.' % clone.name for n, v in list(config.getstartswith(prefix)): config.set(n.replace(prefix, new_prefix, 1), v) clone.set_description('clone of "%s"' % cur_branch.name) clone.switch_to() out.done() log.copy_log(log.default_repo(), cur_branch.name, clone.name, 'branch --clone') return elif options.delete: if len(args) != 1: parser.error('incorrect number of arguments') __delete_branch(args[0], options.force) log.delete_log(log.default_repo(), args[0]) return elif options.cleanup: if not args: name = repository.current_branch_name elif len(args) == 1: name = args[0] else: parser.error('incorrect number of arguments') __cleanup_branch(name, options.force) log.delete_log(log.default_repo(), name) return elif options.list: if len(args) != 0: parser.error('incorrect number of arguments') branch_names = sorted( ref.replace('refs/heads/', '', 1) for ref in repository.refs if ref.startswith('refs/heads/') and not ref.endswith('.stgit')) if branch_names: out.info('Available branches:') max_len = max(len(name) for name in branch_names) for branch_name in branch_names: __print_branch(branch_name, max_len) else: out.info('No branches') return elif options.protect: if len(args) == 0: branch_name = repository.current_branch_name elif len(args) == 1: branch_name = args[0] else: parser.error('incorrect number of arguments') try: stack = repository.get_stack(branch_name) except StackException: raise CmdException('Branch "%s" is not controlled by StGIT' % branch_name) out.start('Protecting branch "%s"' % branch_name) stack.protected = True out.done() return elif options.rename: if len(args) == 1: stack = repository.current_stack new_name = args[0] elif len(args) == 2: stack = repository.get_stack(args[0]) new_name = args[1] else: parser.error('incorrect number of arguments') old_name = stack.name stack.rename(new_name) out.info('Renamed branch "%s" to "%s"' % (old_name, new_name)) log.rename_log(repository, old_name, new_name, 'branch --rename') return elif options.unprotect: if len(args) == 0: branch_name = repository.current_branch_name elif len(args) == 1: branch_name = args[0] else: parser.error('incorrect number of arguments') try: stack = repository.get_stack(branch_name) except StackException: raise CmdException('Branch "%s" is not controlled by StGIT' % branch_name) out.info('Unprotecting branch "%s"' % branch_name) stack.protected = False out.done() return elif options.description is not None: if len(args) == 0: branch_name = repository.current_branch_name elif len(args) == 1: branch_name = args[0] else: parser.error('incorrect number of arguments') Branch(repository, branch_name).set_description(options.description) return elif len(args) == 1: branch_name = args[0] if branch_name == repository.current_branch_name: raise CmdException('Branch "%s" is already the current branch' % branch_name) if not options.merge: check_local_changes(repository) check_conflicts(repository.default_iw) try: stack = repository.get_stack() except StackException: pass else: check_head_top_equal(stack) out.start('Switching to branch "%s"' % branch_name) Branch(repository, branch_name).switch_to() out.done() return # default action: print the current branch if len(args) != 0: parser.error('incorrect number of arguments') out.stdout(directory.repository.current_branch_name)
def func(parser, options, args): """Repair inconsistencies in StGit metadata.""" if args: parser.error('incorrect number of arguments') repository = directory.repository stack = repository.get_stack() if stack.protected: raise CmdException( 'This branch is protected. Modification is not permitted.') patchorder = stack.patchorder patches = list(patchorder.all) # Find commits that aren't patches, and applied patches. patchify = [] # commits to definitely patchify maybe_patchify = [] # commits to patchify if we find a patch below them applied = [] c = stack.head while len(c.data.parents) == 1: for pn in patchorder.all: if stack.patches[pn] == c: applied.append(pn) patchify.extend(maybe_patchify) maybe_patchify = [] break else: maybe_patchify.append(c) c = c.data.parent if stack.base == c: # Reaching the original stack base can happen if, for example, the first # applied patch is amended. In this case, any commits descending from the # stack base should be patchified. patchify.extend(maybe_patchify) maybe_patchify = [] # Once the base commit has been found, we know that no existing patches # can be found be searching further. break applied.reverse() patchify.reverse() # Find patches unreachable behind a merge. if c != stack.base: merge = c todo = set([c]) seen = set() unreachable = set() while todo: c = todo.pop() seen.add(c) todo |= set(c.data.parents) - seen if any(stack.patches[pn] == c for pn in patches): unreachable.add(c) if unreachable: out.warn( ('%d patch%s are hidden below the merge commit' % (len(unreachable), ['es', ''][len(unreachable) == 1])), '%s,' % merge.sha1, 'and will be considered unapplied.', ) # Make patches of any linear sequence of commits on top of a patch. if patchify: out.start('Creating %d new patch%s' % (len(patchify), ['es', ''][len(patchify) == 1])) for c in patchify: pn = stack.patches.make_name(c.data.message_str) out.info('Creating patch %s from commit %s' % (pn, c.sha1)) stack.patches.new(pn, c, 'repair') applied.append(pn) out.done() # Figure out hidden hidden = [pn for pn in patches if pn in patchorder.hidden] # Write the applied/unapplied files. out.start('Checking patch appliedness') unapplied = [ pn for pn in patches if pn not in applied and pn not in hidden ] for pn in patchorder.all: if pn not in patches: out.info('%s is gone' % pn) for pn in applied: if pn not in patchorder.applied: out.info('%s is now applied' % pn) for pn in unapplied: if pn not in patchorder.unapplied: out.info('%s is now unapplied' % pn) for pn in hidden: if pn not in patchorder.hidden: out.info('%s is now hidden' % pn) out.done() orig_order = {pn: i for i, pn in enumerate(patchorder.all)} def patchname_key(p): i = orig_order.get(p, len(orig_order)) return i, p trans = StackTransaction(stack) try: trans.applied = applied trans.unapplied = sorted(unapplied, key=patchname_key) trans.hidden = sorted(hidden, key=patchname_key) except TransactionHalted: pass return trans.execute('repair')
def __get_published(stack, tree): """Check the patches that were already published.""" trans = StackTransaction(stack, 'publish') published = trans.check_merged(trans.applied, tree=tree, quiet=True) trans.abort() return published
def func(parser, options, args): """Synchronise a range of patches """ repository = directory.repository stack = repository.get_stack() if options.ref_branch: remote_stack = repository.get_stack(options.ref_branch) if remote_stack.name == stack.name: raise CmdException('Cannot synchronise with the current branch') remote_patches = remote_stack.patchorder.applied def merge_patch(commit, pname): return __branch_merge_patch(remote_stack, stack, commit, pname) elif options.series: patchdir = os.path.dirname(options.series) remote_patches = [] with open(options.series) as f: for line in f: pn = re.sub('#.*$', '', line).strip() if not pn: continue remote_patches.append(pn) def merge_patch(commit, pname): return __series_merge_patch(patchdir, stack, commit, pname) else: raise CmdException('No remote branch or series specified') applied = list(stack.patchorder.applied) unapplied = list(stack.patchorder.unapplied) if options.all: patches = applied elif len(args) != 0: patches = parse_patches(args, applied + unapplied, len(applied), ordered=True) elif applied: patches = [applied[-1]] else: parser.error('no patches applied') assert patches # only keep the patches to be synchronised sync_patches = [p for p in patches if p in remote_patches] if not sync_patches: raise CmdException('No common patches to be synchronised') iw = repository.default_iw # pop to the one before the first patch to be synchronised first_patch = sync_patches[0] if first_patch in applied: to_pop = applied[applied.index(first_patch) + 1:] if to_pop: trans = StackTransaction(stack, 'sync (pop)', check_clean_iw=iw) popped_extra = trans.pop_patches(lambda pn: pn in to_pop) assert not popped_extra retval = trans.run(iw) assert not retval pushed = [first_patch] else: to_pop = [] pushed = [] popped = to_pop + [p for p in patches if p in unapplied] trans = StackTransaction(stack, 'sync', check_clean_iw=iw) try: for p in pushed + popped: if p in popped: trans.push_patch(p, iw=iw) if p not in sync_patches: # nothing to synchronise continue # the actual sync out.start('Synchronising "%s"' % p) commit = trans.patches[p] # the actual merging (either from a branch or an external file) tree = merge_patch(commit, p) if tree: trans.patches[p] = commit.data.set_tree(tree).commit( repository) out.done('updated') else: out.done() except TransactionHalted: pass return trans.run(iw)
def __create_patch(filename, message, author_name, author_email, author_date, diff, options): """Create a new patch on the stack """ stack = directory.repository.current_stack if options.name: name = options.name if not stack.patches.is_name_valid(name): raise CmdException('Invalid patch name: %s' % name) elif filename: name = os.path.basename(filename) else: name = '' if options.stripname: name = __strip_patch_name(name) if not name: if options.ignore or options.replace: def unacceptable_name(name): return False else: unacceptable_name = stack.patches.exists name = make_patch_name(message, unacceptable_name) else: # fix possible invalid characters in the patch name name = re.sub(r'[^\w.]+', '-', name).strip('-') assert stack.patches.is_name_valid(name) if options.ignore and name in stack.patchorder.applied: out.info('Ignoring already applied patch "%s"' % name) return out.start('Importing patch "%s"' % name) author = Person( author_name, author_email, Date.maybe(author_date), ) author = options.author(author) try: if not diff: out.warn('No diff found, creating empty patch') tree = stack.head.data.tree else: iw = stack.repository.default_iw iw.apply( diff, quiet=False, reject=options.reject, strip=options.strip ) tree = iw.index.write_tree() cd = CommitData( tree=tree, parents=[stack.head], author=author, message=message, ) cd = update_commit_data( cd, message=None, author=None, sign_str=options.sign_str, edit=options.edit, ) commit = stack.repository.commit(cd) trans = StackTransaction(stack, 'import: %s' % name) try: if options.replace and name in stack.patchorder.unapplied: trans.delete_patches(lambda pn: pn == name, quiet=True) trans.patches[name] = commit trans.applied.append(name) except TransactionHalted: pass trans.run() finally: out.done()
def _squash(stack, iw, name, msg, save_template, patches, no_verify=False): # If a name was supplied on the command line, make sure it's OK. def bad_name(pn): return pn not in patches and stack.patches.exists(pn) def get_name(cd): return name or utils.make_patch_name(cd.message_str, bad_name) if name and bad_name(name): raise CmdException('Patch name "%s" already taken' % name) def make_squashed_patch(trans, new_commit_data): name = get_name(new_commit_data) trans.patches[name] = stack.repository.commit(new_commit_data) trans.unapplied.insert(0, name) trans = StackTransaction(stack, 'squash', allow_conflicts=True) push_new_patch = bool(set(patches) & set(trans.applied)) try: new_commit_data = _squash_patches(trans, patches, msg, save_template, no_verify) if new_commit_data: # We were able to construct the squashed commit # automatically. So just delete its constituent patches. to_push = trans.delete_patches(lambda pn: pn in patches) else: # Automatic construction failed. So push the patches # consecutively, so that a second construction attempt is # guaranteed to work. to_push = trans.pop_patches(lambda pn: pn in patches) for pn in patches: trans.push_patch(pn, iw) new_commit_data = _squash_patches(trans, patches, msg, save_template, no_verify) popped_extra = trans.delete_patches(lambda pn: pn in patches) assert not popped_extra make_squashed_patch(trans, new_commit_data) # Push the new patch if necessary, and any unrelated patches we've # had to pop out of the way. if push_new_patch: trans.push_patch(get_name(new_commit_data), iw) for pn in to_push: trans.push_patch(pn, iw) except SaveTemplateDone: trans.abort(iw) return except TransactionHalted: pass return trans.run(iw)
def func(parser, options, args): """Repair inconsistencies in StGit metadata.""" if args: parser.error('incorrect number of arguments') repository = directory.repository stack = repository.get_stack() if stack.protected: raise CmdException( 'This branch is protected. Modification is not permitted.') patchorder = stack.patchorder patches = [stack.patches.get(pn) for pn in patchorder.all] # Find commits that aren't patches, and applied patches. patchify = [] # commits to definitely patchify maybe_patchify = [] # commits to patchify if we find a patch below them applied = [] c = stack.head while len(c.data.parents) == 1: for p in patches: if p.commit == c: applied.append(p) patchify.extend(maybe_patchify) maybe_patchify = [] break else: maybe_patchify.append(c) c = c.data.parent applied.reverse() patchify.reverse() # Find patches unreachable behind a merge. merge = c todo = set([c]) seen = set() unreachable = set() while todo: c = todo.pop() seen.add(c) todo |= set(c.data.parents) - seen if any(p.commit == c for p in patches): unreachable.add(c) if unreachable: out.warn(('%d patch%s are hidden below the merge commit' % (len(unreachable), ['es', ''][len(unreachable) == 1])), '%s,' % merge.sha1, 'and will be considered unapplied.') # Make patches of any linear sequence of commits on top of a patch. if applied and patchify: out.start('Creating %d new patch%s' % (len(patchify), ['es', ''][len(patchify) == 1])) for c in patchify: pn = make_patch_name( c.data.message, unacceptable=lambda name: any(p.name == name for p in patches), ) out.info('Creating patch %s from commit %s' % (pn, c.sha1)) applied.append(stack.patches.new(pn, c, 'repair')) out.done() # Figure out hidden hidden = [p for p in patches if p.name in patchorder.hidden] # Write the applied/unapplied files. out.start('Checking patch appliedness') unapplied = [p for p in patches if p not in applied and p not in hidden] for pn in patchorder.all: if all(pn != p.name for p in patches): out.info('%s is gone' % pn) for p in applied: if p.name not in patchorder.applied: out.info('%s is now applied' % p.name) for p in unapplied: if p.name not in patchorder.unapplied: out.info('%s is now unapplied' % p.name) for p in hidden: if p.name not in patchorder.hidden: out.info('%s is now hidden' % p.name) out.done() orig_order = dict((pn, i) for i, pn in enumerate(patchorder.all)) def patchname_key(p): i = orig_order.get(p, len(orig_order)) return i, p trans = StackTransaction(stack, 'repair', check_clean_iw=False, allow_bad_head=True) try: trans.applied = [p.name for p in applied] trans.unapplied = sorted((p.name for p in unapplied), key=patchname_key) trans.hidden = sorted((p.name for p in hidden), key=patchname_key) except TransactionHalted: pass return trans.run()
def __create_patch(filename, message, patch_name, author_name, author_email, author_date, diff, options): """Create a new patch on the stack""" stack = directory.repository.current_stack if patch_name: name = patch_name elif options.name: name = options.name elif filename: name = os.path.basename(filename) else: name = '' if options.stripname: # Removing leading numbers and trailing extension name = re.sub( r'''^ (?:[0-9]+-)? # Optional leading patch number (.*?) # Patch name group (non-greedy) (?:\.(?:diff|patch))? # Optional .diff or .patch extension $ ''', r'\g<1>', name, flags=re.VERBOSE, ) need_unique = not (options.ignore or options.replace) if name: name = stack.patches.make_name(name, unique=need_unique, lower=False) else: name = stack.patches.make_name(message, unique=need_unique, lower=True) if options.ignore and name in stack.patchorder.applied: out.info('Ignoring already applied patch "%s"' % name) return out.start('Importing patch "%s"' % name) author = options.author( Person( author_name, author_email, Date.maybe(author_date), )) try: if not diff: out.warn('No diff found, creating empty patch') tree = stack.head.data.tree else: iw = stack.repository.default_iw iw.apply( diff, quiet=False, reject=options.reject, strip=options.strip, context_lines=options.context_lines, ) tree = iw.index.write_tree() cd = CommitData( tree=tree, parents=[stack.head], author=author, message=message, ) cd = update_commit_data( stack.repository, cd, message=None, author=None, trailers=options.trailers, edit=options.edit, ) commit = stack.repository.commit(cd) trans = StackTransaction(stack) try: if options.replace and name in stack.patchorder.unapplied: trans.delete_patches(lambda pn: pn == name, quiet=True) trans.patches[name] = commit trans.applied.append(name) except TransactionHalted: pass trans.execute('import: %s' % name) finally: out.done()