def func(parser, options, args): """Show commit log and diff """ if options.applied: patches = crt_series.get_applied() elif options.unapplied: patches = crt_series.get_unapplied() elif len(args) == 0: patches = ['HEAD'] elif '..' in ' '.join(args): # patch ranges applied = crt_series.get_applied() unapplied = crt_series.get_unapplied() patches = parse_patches(args, applied + unapplied + \ crt_series.get_hidden(), len(applied)) else: # individual patches or commit ids patches = args if not options.stat: options.diff_flags.extend(color_diff_flags()) commit_ids = [git_id(crt_series, patch) for patch in patches] commit_str = '\n'.join([git.pretty_commit(commit_id, flags = options.diff_flags) for commit_id in commit_ids]) if options.stat: commit_str = gitlib.diffstat(commit_str) if commit_str: pager(commit_str)
def func(parser, options, args): stack = directory.repository.current_stack patches = common.parse_patches(args, list(stack.patchorder.all)) if len(patches) < 2: raise common.CmdException('Need at least two patches') return _squash(stack, stack.repository.default_iw, options.name, options.message, options.save_template, patches)
def func(parser, options, args): """Delete one or more patches.""" stack = directory.repository.get_stack(options.branch) if options.branch: iw = None # can't use index/workdir to manipulate another branch else: iw = stack.repository.default_iw if args: patches = set(common.parse_patches(args, list(stack.patchorder.all), len(stack.patchorder.applied))) else: parser.error('No patches specified') if options.spill: if set(stack.patchorder.applied[-len(patches):]) != patches: parser.error('Can only spill topmost applied patches') iw = None # don't touch index+worktree 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 = transaction.StackTransaction(stack, 'delete', allow_conflicts = allow_conflicts) try: to_push = trans.delete_patches(lambda pn: pn in patches) for pn in to_push: trans.push_patch(pn, iw) except transaction.TransactionHalted: pass return trans.run(iw)
def func(parser, options, args): if options.branch: stack = directory.repository.get_stack(options.branch) else: stack = directory.repository.current_stack patches = common.parse_patches(args, list(stack.patchorder.all)) logref = log.log_ref(stack.name) try: logcommit = stack.repository.refs.get(logref) except KeyError: out.info('Log is empty') return if options.clear: log.delete_log(stack.repository, stack.name) return stacklog = log.get_log_entry(stack.repository, logref, logcommit) pathlim = [os.path.join('patches', pn) for pn in patches] if options.graphical: for o in ['diff', 'number', 'full']: if getattr(options, o): parser.error('cannot combine --graphical and --%s' % o) # Discard the exit codes generated by SIGINT, SIGKILL, and SIGTERM. run.Run(*(['gitk', stacklog.simplified.sha1, '--'] + pathlim) ).returns([0, -2, -9, -15]).run() else: show_log(stacklog.simplified, pathlim, options.number, options.full, options.diff)
def func(parser, options, args): """Commit a number of patches.""" stack = directory.repository.current_stack args = common.parse_patches(args, list(stack.patchorder.all_visible)) if len([x for x in [args, options.number is not None, options.all] if x]) > 1: parser.error('too many options') if args: patches = [pn for pn in stack.patchorder.all_visible if pn in args] bad = set(args) - set(patches) if bad: raise common.CmdException('Nonexistent or hidden patch names: %s' % ', '.join(sorted(bad))) elif options.number is not None: if options.number <= len(stack.patchorder.applied): patches = stack.patchorder.applied[:options.number] else: raise common.CmdException('There are not that many applied patches') elif options.all: patches = stack.patchorder.applied else: patches = stack.patchorder.applied[:1] if not patches: raise common.CmdException('No patches to commit') iw = stack.repository.default_iw def allow_conflicts(trans): # As long as the topmost patch stays where it is, it's OK to # run "stg commit" with conflicts in the index. return len(trans.applied) >= 1 trans = transaction.StackTransaction(stack, 'commit', allow_conflicts = allow_conflicts) try: common_prefix = 0 for i in range(min(len(stack.patchorder.applied), len(patches))): if stack.patchorder.applied[i] == patches[i]: common_prefix += 1 else: break if common_prefix < len(patches): to_push = [pn for pn in stack.patchorder.applied[common_prefix:] if pn not in patches[common_prefix:]] # this pops all the applied patches from common_prefix trans.pop_patches(lambda pn: pn in to_push) for pn in patches[common_prefix:]: trans.push_patch(pn, iw) else: to_push = [] new_base = trans.patches[patches[-1]] for pn in patches: trans.patches[pn] = None trans.applied = [pn for pn in trans.applied if pn not in patches] trans.base = new_base out.info('Committed %d patch%s' % (len(patches), ['es', ''][len(patches) == 1])) for pn in to_push: trans.push_patch(pn, iw) except transaction.TransactionHalted: pass return trans.run(iw)
def func(parser, options, args): """Import a commit object as a new patch """ if not args: parser.error('incorrect number of arguments') if options.file and not options.fold: parser.error('--file can only be specified with --fold') if not options.unapplied: check_local_changes() check_conflicts() check_head_top_equal(crt_series) if options.ref_branch: remote_series = Series(options.ref_branch) else: remote_series = crt_series applied = remote_series.get_applied() unapplied = remote_series.get_unapplied() try: patches = parse_patches(args, applied + unapplied, len(applied)) commit_id = None except CmdException: if len(args) > 1: raise # no patches found, try a commit id commit_id = git_id(remote_series, args[0]) if not commit_id and len(patches) > 1: if options.name: raise CmdException('--name can only be specified with one patch') if options.parent: raise CmdException('--parent can only be specified with one patch') if options.update and not crt_series.get_current(): raise CmdException('No patches applied') if commit_id: # Try to guess a patch name if the argument was <branch>:<patch> try: patchname = args[0].split(':')[1] except IndexError: patchname = None __pick_commit(commit_id, patchname, options) else: if options.unapplied: patches.reverse() for patch in patches: __pick_commit(git_id(remote_series, patch), patch, options) print_crt_patch(crt_series)
def func(parser, options, args): """Pushes the given patches or the first unapplied onto the stack.""" stack = directory.repository.current_stack iw = stack.repository.default_iw clean_iw = (not options.keep and iw) or None trans = transaction.StackTransaction(stack, 'push', check_clean_iw = clean_iw) if options.number == 0: # explicitly allow this without any warning/error message return if not trans.unapplied: raise common.CmdException('No patches to push') if options.all: patches = list(trans.unapplied) elif options.number is not None: patches = trans.unapplied[:options.number] elif not args: patches = [trans.unapplied[0]] else: patches = common.parse_patches(args, trans.unapplied) if not patches: raise common.CmdException('No patches to push') if options.reverse: patches.reverse() if options.set_tree: for pn in patches: trans.push_tree(pn) else: try: if options.merged: merged = set(trans.check_merged(patches)) else: merged = set() for pn in patches: trans.push_patch(pn, iw, allow_interactive = True, already_merged = pn in merged) except transaction.TransactionHalted: pass return trans.run(iw)
def func(parser, options, args): """Pop the given patches or the topmost one from the stack.""" stack = directory.repository.current_stack iw = stack.repository.default_iw clean_iw = (not options.keep and not options.spill and iw) or None trans = transaction.StackTransaction(stack, 'pop', check_clean_iw=clean_iw) if options.number == 0: # explicitly allow this without any warning/error message return if not trans.applied: raise common.CmdException('No patches applied') if options.all: patches = trans.applied elif options.number is not None: # reverse it twice to also work with negative or bigger than # the length numbers patches = trans.applied[::-1][:options.number][::-1] elif not args: patches = [trans.applied[-1]] else: patches = common.parse_patches(args, trans.applied, ordered=True) if not patches: #FIXME: Why is this an error, and not just a noop ? raise common.CmdException('No patches to pop') if options.spill: if set(stack.patchorder.applied[-len(patches):]) != set(patches): parser.error('Can only spill topmost applied patches') iw = None # don't touch index+worktree applied = [p for p in trans.applied if p not in set(patches)] unapplied = patches + trans.unapplied try: trans.reorder_patches(applied, unapplied, iw=iw, allow_interactive=True) except transaction.TransactionException: pass return trans.run(iw)
def func(parser, options, args): """Delete one or more patches.""" stack = directory.repository.get_stack(options.branch) if options.branch: iw = None # can't use index/workdir to manipulate another branch else: iw = stack.repository.default_iw if args and options.top: parser.error('Either --top or patches must be specified') elif args: patches = set(common.parse_patches(args, list(stack.patchorder.all), len(stack.patchorder.applied))) elif options.top: applied = stack.patchorder.applied if applied: patches = set([applied[-1]]) else: raise common.CmdException('No patches applied') else: parser.error('No patches specified') if options.spill: if set(stack.patchorder.applied[-len(patches):]) != patches: parser.error('Can only spill topmost applied patches') iw = None # don't touch index+worktree 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 = transaction.StackTransaction(stack, 'delete', allow_conflicts=allow_conflicts) try: to_push = trans.delete_patches(lambda pn: pn in patches) for pn in to_push: trans.push_patch(pn, iw) except transaction.TransactionHalted: pass return trans.run(iw)
def func(parser, options, args): """Unhide a range of patch in the series.""" stack = directory.repository.current_stack trans = transaction.StackTransaction(stack, 'unhide') if not args: parser.error('No patches specified') patches = common.parse_patches(args, trans.all_patches) for p in patches: if p not in trans.hidden: raise common.CmdException('Patch "%s" not hidden' % p) applied = list(trans.applied) unapplied = trans.unapplied + patches hidden = [p for p in trans.hidden if p not in set(patches)] trans.reorder_patches(applied, unapplied, hidden) return trans.run()
def func(parser, options, args): """Unhide a range of patch in the series.""" stack = directory.repository.current_stack trans = transaction.StackTransaction(stack, 'unhide') if not args: parser.error('No patches specified') patches = parse_patches(args, trans.all_patches) for p in patches: if p not in trans.hidden: raise CmdException('Patch "%s" not hidden' % p) applied = list(trans.applied) unapplied = trans.unapplied + patches hidden = [p for p in trans.hidden if p not in set(patches)] trans.reorder_patches(applied, unapplied, hidden) return trans.run()
def func(parser, options, args): """Pop the given patches or the topmost one from the stack.""" stack = directory.repository.current_stack iw = stack.repository.default_iw clean_iw = (not options.keep and not options.spill and iw) or None trans = transaction.StackTransaction(stack, 'pop', check_clean_iw = clean_iw) if options.number == 0: # explicitly allow this without any warning/error message return if not trans.applied: raise common.CmdException('No patches applied') if options.all: patches = trans.applied elif options.number is not None: # reverse it twice to also work with negative or bigger than # the length numbers patches = trans.applied[::-1][:options.number][::-1] elif not args: patches = [trans.applied[-1]] else: patches = common.parse_patches(args, trans.applied, ordered = True) if not patches: #FIXME: Why is this an error, and not just a noop ? raise common.CmdException('No patches to pop') if options.spill: if set(stack.patchorder.applied[-len(patches):]) != set(patches): parser.error('Can only spill topmost applied patches') iw = None # don't touch index+worktree applied = [p for p in trans.applied if p not in set(patches)] unapplied = patches + trans.unapplied try: trans.reorder_patches(applied, unapplied, iw = iw, allow_interactive = True) except transaction.TransactionException: pass return trans.run(iw)
def func(parser, options, args): """Sink patches down the stack. """ stack = directory.repository.current_stack if options.to and options.to not in stack.patchorder.applied: raise common.CmdException( 'Cannot sink below %s since it is not applied' % options.to ) if len(args) > 0: patches = common.parse_patches(args, stack.patchorder.all) else: # current patch patches = list(stack.patchorder.applied[-1:]) if not patches: raise common.CmdException('No patches to sink') if options.to and options.to in patches: raise common.CmdException('Cannot have a sinked patch as target') applied = [p for p in stack.patchorder.applied if p not in patches] if options.to: insert_idx = applied.index(options.to) else: insert_idx = 0 applied = applied[:insert_idx] + patches + applied[insert_idx:] unapplied = [p for p in stack.patchorder.unapplied if p not in patches] iw = stack.repository.default_iw clean_iw = (not options.keep and iw) or None trans = transaction.StackTransaction( stack, 'sink', check_clean_iw=clean_iw ) try: trans.reorder_patches( applied, unapplied, iw=iw, allow_interactive=True ) except transaction.TransactionHalted: pass return trans.run(iw)
def func(parser, options, args): """Reorder patches to make the named patch the topmost one. """ if options.series and args: parser.error('<patches> cannot be used with --series') elif not options.series and not args: parser.error('incorrect number of arguments') stack = directory.repository.current_stack if options.series: if options.series == '-': f = io.open(sys.stdin.fileno()) else: f = io.open(options.series) patches = [] for line in f: patch = re.sub('#.*$', '', line).strip() if patch: patches.append(patch) else: patches = common.parse_patches(args, stack.patchorder.all) if not patches: raise common.CmdException('No patches to float') applied = [p for p in stack.patchorder.applied if p not in patches] + \ patches unapplied = [p for p in stack.patchorder.unapplied if p not in patches] iw = stack.repository.default_iw clean_iw = (not options.keep and iw) or None trans = transaction.StackTransaction(stack, 'float', check_clean_iw=clean_iw) try: trans.reorder_patches(applied, unapplied, iw=iw) except transaction.TransactionHalted: pass return trans.run(iw)
def func(parser, options, args): """Hide a range of patch in the series.""" stack = directory.repository.current_stack trans = transaction.StackTransaction(stack, 'hide') if not args: parser.error('No patches specified') patches = common.parse_patches(args, trans.all_patches) for p in patches: if p in trans.hidden: out.warn('Patch "%s" already hidden' % p) patches = [p for p in patches if p not in set(trans.hidden)] applied = [p for p in trans.applied if p not in set(patches)] unapplied = [p for p in trans.unapplied if p not in set(patches)] hidden = patches + trans.hidden trans.reorder_patches(applied, unapplied, hidden) return trans.run()
def func(parser, options, args): """Reorder patches to make the named patch the topmost one. """ if options.series and args: parser.error('<patches> cannot be used with --series') elif not options.series and not args: parser.error('incorrect number of arguments') stack = directory.repository.current_stack if options.series: if options.series == '-': f = sys.stdin else: f = open(options.series) patches = [] for line in f: patch = re.sub('#.*$', '', line).strip() if patch: patches.append(patch) else: patches = common.parse_patches(args, stack.patchorder.all) if not patches: raise common.CmdException('No patches to float') applied = [p for p in stack.patchorder.applied if p not in patches] + \ patches unapplied = [p for p in stack.patchorder.unapplied if p not in patches] iw = stack.repository.default_iw clean_iw = (not options.keep and iw) or None trans = transaction.StackTransaction(stack, 'float', check_clean_iw = clean_iw) try: trans.reorder_patches(applied, unapplied, iw = iw) except transaction.TransactionHalted: pass return trans.run(iw)
def func(parser, options, args): """Sink patches down the stack. """ stack = directory.repository.current_stack if options.to and not options.to in stack.patchorder.applied: raise common.CmdException('Cannot sink below %s since it is not applied' % options.to) if len(args) > 0: patches = common.parse_patches(args, stack.patchorder.all) else: # current patch patches = list(stack.patchorder.applied[-1:]) if not patches: raise common.CmdException('No patches to sink') if options.to and options.to in patches: raise common.CmdException('Cannot have a sinked patch as target') applied = [p for p in stack.patchorder.applied if p not in patches] if options.to: insert_idx = applied.index(options.to) else: insert_idx = 0 applied = applied[:insert_idx] + patches + applied[insert_idx:] unapplied = [p for p in stack.patchorder.unapplied if p not in patches] iw = stack.repository.default_iw clean_iw = (not options.keep and iw) or None trans = transaction.StackTransaction(stack, 'sink', check_clean_iw = clean_iw) try: trans.reorder_patches(applied, unapplied, iw = iw, allow_interactive = True) except transaction.TransactionHalted: pass return trans.run(iw)
def func(parser, options, args): """Pop the given patches or the topmost one from the stack.""" stack = directory.repository.current_stack iw = stack.repository.default_iw clean_iw = (not options.keep and iw) or None trans = transaction.StackTransaction(stack, 'pop', check_clean_iw = clean_iw) if options.number == 0: # explicitly allow this without any warning/error message return if not trans.applied: raise common.CmdException('No patches applied') if options.all: patches = trans.applied elif options.number is not None: # reverse it twice to also work with negative or bigger than # the length numbers patches = trans.applied[::-1][:options.number][::-1] elif not args: patches = [trans.applied[-1]] else: patches = common.parse_patches(args, trans.applied, ordered = True) if not patches: raise common.CmdException('No patches to pop') applied = [p for p in trans.applied if not p in set(patches)] unapplied = patches + trans.unapplied try: trans.reorder_patches(applied, unapplied, iw = iw) except transaction.TransactionException: pass return trans.run(iw)
def func(parser, options, args): if options.clear: if (options.diff or options.number or options.full or options.graphical): parser.error('cannot combine --clear with other options') elif args: parser.error('cannot combine --clear with patch arguments') if options.graphical: for o in ['diff', 'number', 'full']: if getattr(options, o): parser.error('cannot combine --graphical and --%s' % o) stack = directory.repository.get_stack(options.branch) patches = common.parse_patches(args, list(stack.patchorder.all)) logref = log.log_ref(stack.name) try: logcommit = stack.repository.refs.get(logref) except KeyError: out.info('Log is empty') return if options.clear: log.delete_log(stack.repository, stack.name) return stacklog = log.get_log_entry(stack.repository, logref, logcommit) pathlim = [os.path.join('patches', pn) for pn in patches] if options.graphical: cmd = ['gitk', stacklog.simplified.sha1, '--'] + pathlim # Discard the exit codes generated by SIGINT, SIGKILL, and SIGTERM. Run(*cmd).returns([0, -2, -9, -15]).run() else: show_log(stacklog.simplified, pathlim, options.number, options.full, options.diff)
def func(parser, options, args): """Import a commit object as a new patch """ if not args: parser.error('incorrect number of arguments') if options.file and not options.fold: parser.error('--file can only be specified with --fold') repository = directory.repository stack = repository.get_stack() iw = repository.default_iw if not options.unapplied: check_local_changes(repository) check_conflicts(iw) check_head_top_equal(stack) if options.ref_branch: ref_stack = repository.get_stack(options.ref_branch) else: ref_stack = stack try: patches = parse_patches( args, ref_stack.patchorder.all_visible, len(ref_stack.patchorder.applied), ) commit = None except CmdException: if len(args) > 1: raise branch, patch = parse_rev(args[0]) if not branch: commit = git_commit(patch, repository, options.ref_branch) patches = [] else: ref_stack = repository.get_stack(branch) patches = parse_patches( [patch], ref_stack.patchorder.all_visible, len(ref_stack.patchorder.applied), ) commit = None if not commit and len(patches) > 1: if options.name: raise CmdException('--name can only be specified with one patch') if options.parent: raise CmdException('--parent can only be specified with one patch') if options.update and not stack.patchorder.applied: raise CmdException('No patches applied') if commit: patchname = None retval = __pick_commit(stack, ref_stack, iw, commit, patchname, options) else: if options.unapplied: patches.reverse() for patchname in patches: commit = git_commit(patchname, repository, ref_stack.name) retval = __pick_commit(stack, ref_stack, iw, commit, patchname, options) if retval != STGIT_SUCCESS: break if retval == STGIT_SUCCESS: print_current_patch(stack) return retval
def func(parser, options, args): """Send the patches by e-mail using the patchmail.tmpl file as a template """ stack = directory.repository.current_stack applied = stack.patchorder.applied if options.all: patches = applied elif len(args) >= 1: unapplied = stack.patchorder.unapplied patches = parse_patches(args, applied + unapplied, len(applied)) else: raise CmdException('Incorrect options. Unknown patches to send') # early test for sender identity __get_sender() out.start('Checking the validity of the patches') for p in patches: if stack.patches.get(p).is_empty(): raise CmdException('Cannot send empty patch "%s"' % p) out.done() total_nr = len(patches) if total_nr == 0: raise CmdException('No patches to send') if options.in_reply_to: if options.no_thread or options.unrelated: raise CmdException('--in-reply-to option not allowed with ' '--no-thread or --unrelated') ref_id = options.in_reply_to else: ref_id = None # get username/password if sending by SMTP __set_smtp_credentials(options) # send the cover message (if any) if options.cover or options.edit_cover: if options.unrelated: raise CmdException('cover sending not allowed with --unrelated') # find the template file if options.cover: with io.open(options.cover, 'r') as f: tmpl = f.read() else: tmpl = templates.get_template('covermail.tmpl') if not tmpl: raise CmdException('No cover message template file found') msg_id = __send_message('cover', tmpl, options, patches) # subsequent e-mails are seen as replies to the first one if not options.no_thread: ref_id = msg_id # send the patches if options.template: with io.open(options.template, 'r') as f: tmpl = f.read() else: if options.attach: tmpl = templates.get_template('mailattch.tmpl') elif options.attach_inline: tmpl = templates.get_template('patchandattch.tmpl') else: tmpl = templates.get_template('patchmail.tmpl') if not tmpl: raise CmdException('No e-mail template file found') for (p, n) in zip(patches, range(1, total_nr + 1)): msg_id = __send_message('patch', tmpl, options, p, n, total_nr, ref_id) # subsequent e-mails are seen as replies to the first one if not options.no_thread and not options.unrelated and not ref_id: ref_id = msg_id
def func(parser, options, args): """Show the patch series """ if options.all and options.short: raise common.CmdException, 'combining --all and --short is meaningless' stack = directory.repository.get_stack(options.branch) if options.missing: cmp_stack = stack stack = directory.repository.get_stack(options.missing) # current series patches applied = unapplied = hidden = () if options.applied or options.unapplied or options.hidden: if options.all: raise common.CmdException, \ '--all cannot be used with --applied/unapplied/hidden' if options.applied: applied = stack.patchorder.applied if options.unapplied: unapplied = stack.patchorder.unapplied if options.hidden: hidden = stack.patchorder.hidden elif options.all: applied = stack.patchorder.applied unapplied = stack.patchorder.unapplied hidden = stack.patchorder.hidden else: applied = stack.patchorder.applied unapplied = stack.patchorder.unapplied if options.missing: cmp_patches = cmp_stack.patchorder.all else: cmp_patches = () # the filtering range covers the whole series if args: show_patches = parse_patches(args, applied + unapplied + hidden, len(applied)) else: show_patches = applied + unapplied + hidden # missing filtering show_patches = [p for p in show_patches if p not in cmp_patches] # filter the patches applied = [p for p in applied if p in show_patches] unapplied = [p for p in unapplied if p in show_patches] hidden = [p for p in hidden if p in show_patches] if options.short: nr = int(config.get('stgit.shortnr')) if len(applied) > nr: applied = applied[-(nr+1):] n = len(unapplied) if n > nr: unapplied = unapplied[:nr] elif n < nr: hidden = hidden[:nr-n] patches = applied + unapplied + hidden if options.count: out.stdout(len(patches)) return if not patches: return if options.showbranch: branch_str = stack.name + ':' else: branch_str = '' max_len = 0 if len(patches) > 0: max_len = max([len(i + branch_str) for i in patches]) if applied: for p in applied[:-1]: __print_patch(stack, p, branch_str, '+ ', max_len, options, config.get("stgit.color.applied")) __print_patch(stack, applied[-1], branch_str, '> ', max_len, options, config.get("stgit.color.current")) for p in unapplied: __print_patch(stack, p, branch_str, '- ', max_len, options, config.get("stgit.color.unapplied")) for p in hidden: __print_patch(stack, p, branch_str, '! ', max_len, options, config.get("stgit.color.hidden"))
def func(parser, options, args): """Pushes the given patches or the first unapplied onto the stack.""" stack = directory.repository.current_stack iw = stack.repository.default_iw if options.number == 0: # explicitly allow this without any warning/error message return if not stack.patchorder.unapplied: raise CmdException('No patches to push') if options.all: if options.noapply: raise CmdException('Cannot use --noapply with --all') patches = list(stack.patchorder.unapplied) elif options.number is not None: if options.noapply: raise CmdException('Cannot use --noapply with --number') patches = list(stack.patchorder.unapplied[: options.number]) elif not args: if options.noapply: raise CmdException('Must supply patch names with --noapply') patches = [stack.patchorder.unapplied[0]] else: try: patches = parse_patches(args, stack.patchorder.unapplied) except CmdException as e: try: patches = parse_patches(args, stack.patchorder.applied) except CmdException: raise e else: raise CmdException( 'Patch%s already applied: %s' % ('es' if len(patches) > 1 else '', ', '.join(patches)) ) assert patches check_head_top_equal(stack) if not options.keep and not options.noapply: check_index_and_worktree_clean(stack) trans = transaction.StackTransaction(stack) if options.reverse: patches.reverse() if options.set_tree: if options.noapply: raise CmdException('Cannot use --noapply with --set-tree') for pn in patches: trans.push_tree(pn) elif options.noapply: if options.merged: raise CmdException('Cannot use --noapply with --merged') unapplied = patches + [pn for pn in trans.unapplied if pn not in patches] trans.reorder_patches(trans.applied, unapplied) else: try: if options.merged: merged = set(trans.check_merged(patches)) else: merged = set() for pn in patches: trans.push_patch( pn, iw, allow_interactive=True, already_merged=pn in merged ) except transaction.TransactionHalted: pass return trans.execute('push', iw)
def func(parser, options, args): """Export a range of patches. """ stack = directory.repository.get_stack(options.branch) if options.dir: dirname = options.dir else: dirname = 'patches-%s' % stack.name directory.cd_to_topdir() if not options.branch and git.local_changes(): out.warn('Local changes in the tree;' ' you might want to commit them first') if not options.stdout: if not os.path.isdir(dirname): os.makedirs(dirname) series = file(os.path.join(dirname, 'series'), 'w+') applied = stack.patchorder.applied unapplied = stack.patchorder.unapplied if len(args) != 0: patches = common.parse_patches(args, applied + unapplied, len(applied)) else: patches = applied num = len(patches) if num == 0: raise common.CmdException, 'No patches applied' zpadding = len(str(num)) if zpadding < 2: zpadding = 2 # get the template if options.template: tmpl = file(options.template).read() else: tmpl = templates.get_template('patchexport.tmpl') if not tmpl: tmpl = '' # note the base commit for this series if not options.stdout: base_commit = stack.patches.get(patches[0]).commit.sha1 print >> series, '# This series applies on GIT commit %s' % base_commit patch_no = 1; for p in patches: pname = p if options.patch: pname = '%s.patch' % pname elif options.extension: pname = '%s.%s' % (pname, options.extension) if options.numbered: pname = '%s-%s' % (str(patch_no).zfill(zpadding), pname) pfile = os.path.join(dirname, pname) if not options.stdout: print >> series, pname # get the patch description patch = stack.patches.get(p) cd = patch.commit.data descr = cd.message.strip() descr_lines = descr.split('\n') short_descr = descr_lines[0].rstrip() long_descr = reduce(lambda x, y: x + '\n' + y, descr_lines[1:], '').strip() diff = stack.repository.diff_tree(cd.parent.data.tree, cd.tree, options.diff_flags) tmpl_dict = {'description': descr, 'shortdescr': short_descr, 'longdescr': long_descr, 'diffstat': gitlib.diffstat(diff).rstrip(), 'authname': cd.author.name, 'authemail': cd.author.email, 'authdate': cd.author.date.isoformat(), 'commname': cd.committer.name, 'commemail': cd.committer.email} for key in tmpl_dict: if not tmpl_dict[key]: tmpl_dict[key] = '' try: descr = tmpl % tmpl_dict except KeyError, err: raise common.CmdException, 'Unknown patch template variable: %s' \ % err except TypeError: raise common.CmdException, 'Only "%(name)s" variables are ' \ 'supported in the patch template'
def func(parser, options, args): """Commit a number of patches.""" stack = directory.repository.current_stack args = common.parse_patches(args, list(stack.patchorder.all_visible)) exclusive = [args, options.number is not None, options.all] if sum(map(bool, exclusive)) > 1: parser.error('too many options') if args: patches = [pn for pn in stack.patchorder.all_visible if pn in args] bad = set(args) - set(patches) if bad: raise common.CmdException('Nonexistent or hidden patch names: %s' % (', '.join(sorted(bad)), )) elif options.number is not None: if options.number <= len(stack.patchorder.applied): patches = stack.patchorder.applied[:options.number] else: raise common.CmdException( 'There are not that many applied patches') elif options.all: patches = stack.patchorder.applied else: patches = stack.patchorder.applied[:1] if not patches: raise common.CmdException('No patches to commit') iw = stack.repository.default_iw def allow_conflicts(trans): # As long as the topmost patch stays where it is, it's OK to # run "stg commit" with conflicts in the index. return len(trans.applied) >= 1 trans = transaction.StackTransaction(stack, 'commit', allow_conflicts=allow_conflicts) try: common_prefix = 0 for i in range(min(len(stack.patchorder.applied), len(patches))): if stack.patchorder.applied[i] == patches[i]: common_prefix += 1 else: break if common_prefix < len(patches): to_push = [ pn for pn in stack.patchorder.applied[common_prefix:] if pn not in patches[common_prefix:] ] # this pops all the applied patches from common_prefix trans.pop_patches(lambda pn: pn in to_push) for pn in patches[common_prefix:]: trans.push_patch(pn, iw) else: to_push = [] new_base = trans.patches[patches[-1]] for pn in patches: trans.patches[pn] = None trans.applied = [pn for pn in trans.applied if pn not in patches] trans.base = new_base out.info('Committed %d patch%s' % (len(patches), ['es', ''][len(patches) == 1])) for pn in to_push: trans.push_patch(pn, iw) except transaction.TransactionHalted: pass return trans.run(iw)
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 func(parser, options, args): """Synchronise a range of patches """ if options.ref_branch: remote_series = stack.Series(options.ref_branch) if options.ref_branch == crt_series.get_name(): raise CmdException('Cannot synchronise with the current branch') remote_patches = remote_series.get_applied() # the merge function merge_patch(patch, pname) merge_patch = lambda patch, pname: \ __branch_merge_patch(remote_series, pname) elif options.series: patchdir = os.path.dirname(options.series) remote_patches = [] with open(options.series) as f: for line in f: p = re.sub('#.*$', '', line).strip() if not p: continue remote_patches.append(p) # the merge function merge_patch(patch, pname) merge_patch = lambda patch, pname: \ __series_merge_patch(patch.get_bottom(), patchdir, pname) else: raise CmdException('No remote branch or series specified') applied = crt_series.get_applied() unapplied = crt_series.get_unapplied() if options.all: patches = applied elif len(args) != 0: patches = parse_patches(args, applied + unapplied, len(applied), ordered = True) elif applied: patches = [crt_series.get_current()] else: parser.error('no patches applied') if not patches: raise CmdException('No patches to synchronise') __check_all() # 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') # 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: pop_patches(crt_series, to_pop[::-1]) pushed = [first_patch] else: to_pop = [] pushed = [] popped = to_pop + [p for p in patches if p in unapplied] for p in pushed + popped: if p in popped: # push this patch push_patches(crt_series, [p]) if p not in sync_patches: # nothing to synchronise continue # the actual sync out.start('Synchronising "%s"' % p) patch = crt_series.get_patch(p) top = patch.get_top() # reset the patch backup information. patch.set_top(top, backup = True) # the actual merging (either from a branch or an external file) merge_patch(patch, p) if git.local_changes(verbose = False): # index (cache) already updated by the git merge. The # backup information was already reset above crt_series.refresh_patch(cache_update = False, backup = False, log = 'sync') out.done('updated') else: out.done()
def func(parser, options, args): """Synchronise a range of patches """ if options.ref_branch: remote_series = stack.Series(options.ref_branch) if options.ref_branch == crt_series.get_name(): raise CmdException('Cannot synchronise with the current branch') remote_patches = remote_series.get_applied() def merge_patch(patch, pname): return __branch_merge_patch(remote_series, 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(patch, pname): return __series_merge_patch( patch.get_bottom(), patchdir, pname, ) else: raise CmdException('No remote branch or series specified') applied = crt_series.get_applied() unapplied = crt_series.get_unapplied() if options.all: patches = applied elif len(args) != 0: patches = parse_patches(args, applied + unapplied, len(applied), ordered=True) elif applied: patches = [crt_series.get_current()] else: parser.error('no patches applied') if not patches: raise CmdException('No patches to synchronise') __check_all() # 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') # 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: pop_patches(crt_series, to_pop[::-1]) pushed = [first_patch] else: to_pop = [] pushed = [] popped = to_pop + [p for p in patches if p in unapplied] for p in pushed + popped: if p in popped: # push this patch push_patches(crt_series, [p]) if p not in sync_patches: # nothing to synchronise continue # the actual sync out.start('Synchronising "%s"' % p) patch = crt_series.get_patch(p) top = patch.get_top() # reset the patch backup information. patch.set_top(top, backup=True) # the actual merging (either from a branch or an external file) merge_patch(patch, p) if git.local_changes(): # index (cache) already updated by the git merge. The # backup information was already reset above crt_series.refresh_patch( cache_update=False, backup=False, log='sync' ) out.done('updated') else: out.done()
def func(parser, options, args): """Show the patch series """ if options.all and options.short: raise common.CmdException('combining --all and --short is meaningless') stack = directory.repository.get_stack(options.branch) if options.missing: cmp_stack = stack stack = directory.repository.get_stack(options.missing) # current series patches applied = unapplied = hidden = () if options.applied or options.unapplied or options.hidden: if options.all: raise common.CmdException('--all cannot be used with' ' --applied/unapplied/hidden') if options.applied: applied = stack.patchorder.applied if options.unapplied: unapplied = stack.patchorder.unapplied if options.hidden: hidden = stack.patchorder.hidden elif options.all: applied = stack.patchorder.applied unapplied = stack.patchorder.unapplied hidden = stack.patchorder.hidden else: applied = stack.patchorder.applied unapplied = stack.patchorder.unapplied if options.missing: cmp_patches = cmp_stack.patchorder.all else: cmp_patches = () # the filtering range covers the whole series if args: show_patches = parse_patches(args, applied + unapplied + hidden, len(applied)) else: show_patches = applied + unapplied + hidden # missing filtering show_patches = [p for p in show_patches if p not in cmp_patches] # filter the patches applied = [p for p in applied if p in show_patches] unapplied = [p for p in unapplied if p in show_patches] hidden = [p for p in hidden if p in show_patches] if options.short: nr = int(config.get('stgit.shortnr')) if len(applied) > nr: applied = applied[-(nr + 1):] n = len(unapplied) if n > nr: unapplied = unapplied[:nr] elif n < nr: hidden = hidden[:nr - n] patches = applied + unapplied + hidden if options.count: out.stdout(len(patches)) return if not patches: return if options.showbranch: branch_str = stack.name + ':' else: branch_str = '' max_len = 0 if len(patches) > 0: max_len = max([len(i + branch_str) for i in patches]) if applied: for p in applied[:-1]: __print_patch( stack, p, branch_str, '+ ', max_len, options, config.get("stgit.color.applied"), ) __print_patch( stack, applied[-1], branch_str, '> ', max_len, options, config.get("stgit.color.current"), ) for p in unapplied: __print_patch( stack, p, branch_str, '- ', max_len, options, config.get("stgit.color.unapplied"), ) for p in hidden: __print_patch( stack, p, branch_str, '! ', max_len, options, config.get("stgit.color.hidden"), )
def func(parser, options, args): """Export a range of patches. """ stack = directory.repository.get_stack(options.branch) if options.dir: dirname = options.dir else: dirname = 'patches-%s' % stack.name directory.cd_to_topdir() if not options.branch and git.local_changes(): out.warn('Local changes in the tree;' ' you might want to commit them first') applied = stack.patchorder.applied unapplied = stack.patchorder.unapplied if len(args) != 0: patches = common.parse_patches(args, applied + unapplied, len(applied)) else: patches = applied num = len(patches) if num == 0: raise common.CmdException('No patches applied') zpadding = len(str(num)) if zpadding < 2: zpadding = 2 # get the template if options.template: with io.open(options.template, 'r') as f: tmpl = f.read() else: tmpl = templates.get_template('patchexport.tmpl') if not tmpl: tmpl = '' if not options.stdout: if not os.path.isdir(dirname): os.makedirs(dirname) series = io.open(os.path.join(dirname, 'series'), 'w') # note the base commit for this series base_commit = stack.base.sha1 print('# This series applies on GIT commit %s' % base_commit, file=series) for patch_no, p in enumerate(patches, 1): pname = p if options.patch: pname = '%s.patch' % pname elif options.extension: pname = '%s.%s' % (pname, options.extension) if options.numbered: pname = '%s-%s' % (str(patch_no).zfill(zpadding), pname) pfile = os.path.join(dirname, pname) if not options.stdout: print(pname, file=series) # get the patch description patch = stack.patches.get(p) cd = patch.commit.data descr = cd.message.strip() descr_lines = descr.split('\n') short_descr = descr_lines[0].rstrip() long_descr = '\n'.join(descr_lines[1:]).strip() diff = stack.repository.diff_tree(cd.parent.data.tree, cd.tree, options.diff_flags) tmpl_dict = { 'description': descr, 'shortdescr': short_descr, 'longdescr': long_descr, 'diffstat': gitlib.diffstat(diff).rstrip(), 'authname': cd.author.name, 'authemail': cd.author.email, 'authdate': cd.author.date.isoformat(), 'commname': cd.committer.name, 'commemail': cd.committer.email } try: descr = templates.specialize_template(tmpl, tmpl_dict) except KeyError as err: raise common.CmdException('Unknown patch template variable: %s' % err) except TypeError: raise common.CmdException('Only "%(name)s" variables are ' 'supported in the patch template') if options.stdout: if hasattr(sys.stdout, 'buffer'): f = sys.stdout.buffer else: f = sys.stdout else: f = io.open(pfile, 'wb') if options.stdout and num > 1: f.write('\n'.join(['-' * 79, patch.name, '-' * 79, '']).encode('utf-8')) f.write(descr) f.write(diff) if not options.stdout: f.close() if not options.stdout: series.close()
def func(parser, options, args): """Send the patches by e-mail using the patchmail.tmpl file as a template """ applied = crt_series.get_applied() if options.all: patches = applied elif len(args) >= 1: unapplied = crt_series.get_unapplied() patches = parse_patches(args, applied + unapplied, len(applied)) else: raise CmdException('Incorrect options. Unknown patches to send') # early test for sender identity __get_sender() out.start('Checking the validity of the patches') for p in patches: if crt_series.empty_patch(p): raise CmdException('Cannot send empty patch "%s"' % p) out.done() total_nr = len(patches) if total_nr == 0: raise CmdException('No patches to send') if options.in_reply_to: if options.no_thread or options.unrelated: raise CmdException('--in-reply-to option not allowed with ' '--no-thread or --unrelated') ref_id = options.in_reply_to else: ref_id = None # get username/password if sending by SMTP __set_smtp_credentials(options) # send the cover message (if any) if options.cover or options.edit_cover: if options.unrelated: raise CmdException('cover sending not allowed with --unrelated') # find the template file if options.cover: with open(options.cover) as f: tmpl = f.read() else: tmpl = templates.get_template('covermail.tmpl') if not tmpl: raise CmdException('No cover message template file found') msg_id = __send_message('cover', tmpl, options, patches) # subsequent e-mails are seen as replies to the first one if not options.no_thread: ref_id = msg_id # send the patches if options.template: with open(options.template) as f: tmpl = f.read() else: if options.attach: tmpl = templates.get_template('mailattch.tmpl') elif options.attach_inline: tmpl = templates.get_template('patchandattch.tmpl') else: tmpl = templates.get_template('patchmail.tmpl') if not tmpl: raise CmdException('No e-mail template file found') for (p, n) in zip(patches, range(1, total_nr + 1)): msg_id = __send_message('patch', tmpl, options, p, n, total_nr, ref_id) # subsequent e-mails are seen as replies to the first one if not options.no_thread and not options.unrelated and not ref_id: ref_id = msg_id