def __print_patch(stack, patch, branch_str, prefix, length, options, effects): """Print a patch name, description and various markers. """ if options.noprefix: prefix = '' elif options.empty: if stack.patches.get(patch).is_empty(): prefix = '0' + prefix else: prefix = ' ' + prefix patch_str = branch_str + patch if options.description or options.author: patch_str = patch_str.ljust(length) if options.description: output = prefix + patch_str + ' # ' + __get_description(stack, patch) elif options.author: output = prefix + patch_str + ' # ' + __get_author(stack, patch) else: output = prefix + patch_str if not effects: out.stdout(output) else: out.stdout(__render_text(output, effects))
def func(parser, options, args): """Show the patches modifying a file.""" repository = directory.repository stack = repository.get_stack(options.branch) if not stack.patchorder.applied: raise CmdException('No patches applied') iw = repository.default_iw if not args: files = iw.changed_files(stack.head.data.tree) else: files = iw.ls_files(stack.head.data.tree, args) if not files: raise CmdException('No files specified or no local changes') directory.cd_to_topdir() # Find set of revisions that modify the selected files. revs = set( repository.run( ['git', 'rev-list', '--stdin', stack.base.sha1 + '..' + stack.top.sha1] ) .raw_input('--\n' + '\n'.join(files)) .output_lines() ) diff_lines = [] for pn in stack.patchorder.applied: commit = stack.patches[pn] if commit.sha1 not in revs: continue if options.diff: diff_lines.extend( [ b'-' * 79, pn.encode('utf-8'), b'-' * 79, commit.data.message, b'---', b'', repository.diff_tree( commit.data.parent.data.tree, commit.data.tree, pathlimits=files, diff_opts=options.diff_flags + color_diff_flags(), ), ] ) else: out.stdout(pn) if options.diff: pager(b'\n'.join(diff_lines))
def func(parser, options, args): """Show the applied patches""" if len(args) == 0: id_str = 'HEAD' elif len(args) == 1: id_str = args[0] else: parser.error('incorrect number of arguments') out.stdout(git_commit(id_str, directory.repository).sha1)
def func(parser, options, args): """Show the applied patches """ if len(args) == 0: id_str = 'HEAD' elif len(args) == 1: id_str = args[0] else: parser.error('incorrect number of arguments') out.stdout(common.git_commit(id_str, directory.repository).sha1)
def func(parser, options, args): """Show the name of the topmost patch""" if len(args) != 0: parser.error('incorrect number of arguments') stack = directory.repository.get_stack(options.branch) applied = stack.patchorder.applied if applied: out.stdout(applied[-1]) else: raise CmdException('No patches applied')
def func(parser, options, args): """Show the name of the previous patch""" if len(args) != 0: parser.error('incorrect number of arguments') stack = directory.repository.get_stack(options.branch) applied = stack.patchorder.applied if applied and len(applied) >= 2: out.stdout(applied[-2]) else: raise CmdException('Not enough applied patches')
def func(parser, options, args): """Show the name of the next patch """ if len(args) != 0: parser.error('incorrect number of arguments') stack = directory.repository.get_stack(options.branch) unapplied = stack.patchorder.unapplied if unapplied: out.stdout(unapplied[0]) else: raise common.CmdException('No unapplied patches')
def func(parser, options, args): """Show the name of the topmost patch """ if len(args) != 0: parser.error('incorrect number of arguments') stack = directory.repository.get_stack(options.branch) applied = stack.patchorder.applied if applied: out.stdout(applied[-1]) else: raise common.CmdException, 'No patches applied'
def func(parser, options, args): """Show the name of the previous patch """ if len(args) != 0: parser.error('incorrect number of arguments') stack = directory.repository.get_stack(options.branch) applied = stack.patchorder.applied if applied and len(applied) >= 2: out.stdout(applied[-2]) else: raise common.CmdException, 'Not enough applied patches'
def __print_branch(branch_name, length): branch = Branch(directory.repository, branch_name) current = '>' if __is_current_branch(branch_name) else ' ' try: stack = directory.repository.get_stack(branch_name) except StackException: initialised = protected = ' ' else: initialised = 's' protected = 'p' if stack.protected else ' ' out.stdout(current + ' ' + initialised + protected + '\t' + branch_name.ljust(length) + ' | ' + (branch.get_description() or ''))
def __print_branch(branch_name, length): initialized = ' ' current = ' ' protected = ' ' branch = stack.Series(branch_name) if branch.is_initialised(): initialized = 's' if __is_current_branch(branch_name): current = '>' if branch.get_protected(): protected = 'p' out.stdout(current + ' ' + initialized + protected + '\t' + branch_name.ljust(length) + ' | ' + branch.get_description())
def func(parser, options, args): """Show the patches modifying a file """ if not args: files = [path for (stat, path) in git.tree_status(verbose=True)] # git.tree_status returns absolute paths else: files = git.ls_files(args) directory.cd_to_topdir() if not files: raise CmdException('No files specified or no local changes') applied = crt_series.get_applied() if not applied: raise CmdException('No patches applied') revs = git.modifying_revs(files, crt_series.get_base(), crt_series.get_head()) revs.reverse() # build the patch/revision mapping rev_patch = dict() for name in applied: patch = crt_series.get_patch(name) rev_patch[patch.get_top()] = patch # print the patch names diff_lines = [] for rev in revs: patch = rev_patch[rev] if options.diff: diff_lines.extend([ b'-' * 79, patch.get_name().encode('utf-8'), b'-' * 79, patch.get_description().encode('utf-8'), b'---', b'', git.diff(files, patch.get_bottom(), patch.get_top()), ]) else: out.stdout(patch.get_name()) if options.diff: pager(b'\n'.join(diff_lines))
def func(parser, options, args): """Show the patches modifying a file """ if not args: files = [path for (stat,path) in git.tree_status(verbose = True)] # git.tree_status returns absolute paths else: files = git.ls_files(args) directory.cd_to_topdir() if not files: raise CmdException('No files specified or no local changes') applied = crt_series.get_applied() if not applied: raise CmdException('No patches applied') revs = git.modifying_revs(files, crt_series.get_base(), crt_series.get_head()) revs.reverse() # build the patch/revision mapping rev_patch = dict() for name in applied: patch = crt_series.get_patch(name) rev_patch[patch.get_top()] = patch # print the patch names diff_output = '' for rev in revs: if rev in rev_patch: patch = rev_patch[rev] if options.diff: diff_output += diff_tmpl \ % (patch.get_name(), patch.get_description(), git.diff(files, patch.get_bottom(), patch.get_top())) else: out.stdout(patch.get_name()) if options.diff: pager(diff_output)
def func(parser, options, args): """Show the files modified by a patch (or the current patch) """ if options.bare and options.stat: raise CmdException('Cannot specify both --bare and --stat') repository = directory.repository if len(args) == 0: stack = repository.current_stack commit = stack.top elif len(args) == 1: branch, name = parse_rev(args[0]) stack = repository.get_stack(branch) if not stack.patches.exists(name): raise CmdException('%s: Unknown patch name' % name) commit = stack.patches.get(name).commit else: parser.error('incorrect number of arguments') if options.stat: cmd = ['git', 'diff-tree', '--stat', '--summary', '--no-commit-id'] cmd.extend(options.diff_flags) cmd.extend(color_diff_flags()) cmd.append(commit.sha1) out.stdout_bytes(repository.run(cmd).decoding(None).raw_output()) else: used = set() for dt in repository.diff_tree_files( commit.data.parent.data.tree, commit.data.tree ): _, _, _, _, status, oldname, newname = dt for filename in [oldname, newname]: if filename in used: continue else: used.add(filename) if options.bare: out.stdout(filename) else: out.stdout('%s %s' % (status, filename))
def func(parser, options, args): if options.create: if len(args) == 0 or len(args) > 2: parser.error('incorrect number of arguments') check_local_changes() check_conflicts() check_head_top_equal(crt_series) tree_id = None if len(args) >= 2: parentbranch = None try: branchpoint = git.rev_parse(args[1]) # parent branch? head_re = re.compile('refs/(heads|remotes)/') ref_re = re.compile(args[1] + '$') for ref in git.all_refs(): if head_re.match(ref) and ref_re.search(ref): # args[1] is a valid ref from the branchpoint # setting above parentbranch = args[1] break except git.GitException: # should use a more specific exception to catch only # non-git refs ? out.info('Don\'t know how to determine parent branch' ' from "%s"' % args[1]) # exception in branch = rev_parse() leaves branchpoint unbound branchpoint = None tree_id = git_id(crt_series, branchpoint or args[1]) if parentbranch: out.info('Recording "%s" as parent branch' % parentbranch) else: out.info('Don\'t know how to determine parent branch' ' from "%s"' % args[1]) else: # branch stack off current branch parentbranch = git.get_head_file() if parentbranch: parentremote = git.identify_remote(parentbranch) if parentremote: out.info('Using remote "%s" to pull parent from' % parentremote) else: out.info('Recording as a local branch') else: # no known parent branch, can't guess the remote parentremote = None stack.Series(args[0]).init( create_at=tree_id, parent_remote=parentremote, parent_branch=parentbranch, ) out.info('Branch "%s" created' % args[0]) log.compat_log_entry('branch --create') return elif options.clone: if len(args) == 0: clone = crt_series.name + time.strftime('-%C%y%m%d-%H%M%S') elif len(args) == 1: clone = args[0] else: parser.error('incorrect number of arguments') check_local_changes() check_conflicts() check_head_top_equal(crt_series) out.start('Cloning current branch to "%s"' % clone) crt_series.clone(clone) out.done() log.copy_log(log.default_repo(), crt_series.name, clone, '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 = crt_series.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') branches = set(git.get_heads()) for br in set(branches): m = re.match(r'^(.*)\.stgit$', br) if m and m.group(1) in branches: branches.remove(br) if branches: out.info('Available branches:') max_len = max([len(i) for i in branches]) for i in sorted(branches): __print_branch(i, max_len) else: out.info('No branches') return elif options.protect: if len(args) == 0: branch_name = crt_series.name elif len(args) == 1: branch_name = args[0] else: parser.error('incorrect number of arguments') branch = stack.Series(branch_name) if not branch.is_initialised(): raise CmdException('Branch "%s" is not controlled by StGIT' % branch_name) out.start('Protecting branch "%s"' % branch_name) branch.protect() out.done() return elif options.rename: if len(args) != 2: parser.error('incorrect number of arguments') if __is_current_branch(args[0]): raise CmdException('Renaming the current branch is not supported') stack.Series(args[0]).rename(args[1]) out.info('Renamed branch "%s" to "%s"' % (args[0], args[1])) log.rename_log(log.default_repo(), args[0], args[1], 'branch --rename') return elif options.unprotect: if len(args) == 0: branch_name = crt_series.name elif len(args) == 1: branch_name = args[0] else: parser.error('incorrect number of arguments') branch = stack.Series(branch_name) if not branch.is_initialised(): raise CmdException('Branch "%s" is not controlled by StGIT' % branch_name) out.info('Unprotecting branch "%s"' % branch_name) branch.unprotect() out.done() return elif options.description is not None: if len(args) == 0: branch_name = crt_series.name elif len(args) == 1: branch_name = args[0] else: parser.error('incorrect number of arguments') branch = stack.Series(branch_name) if not branch.is_initialised(): raise CmdException('Branch "%s" is not controlled by StGIT' % branch_name) branch.set_description(options.description) return elif len(args) == 1: if __is_current_branch(args[0]): raise CmdException('Branch "%s" is already the current branch' % args[0]) if not options.merge: check_local_changes() check_conflicts() check_head_top_equal(crt_series) out.start('Switching to branch "%s"' % args[0]) git.switch_branch(args[0]) out.done() return # default action: print the current branch if len(args) != 0: parser.error('incorrect number of arguments') out.stdout(crt_series.name)
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): """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): 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): """Publish the stack changes.""" repository = directory.repository stack = repository.get_stack(options.branch) if not args: public_ref = common.get_public_ref(stack.name) elif len(args) == 1: public_ref = args[0] else: parser.error('incorrect number of arguments') if not public_ref.startswith('refs/heads/'): public_ref = 'refs/heads/' + public_ref # just clone the stack if the public ref does not exist if not repository.refs.exists(public_ref): if options.unpublished or options.last: raise common.CmdException('"%s" does not exist' % public_ref) repository.refs.set(public_ref, stack.head, 'publish') out.info('Created "%s"' % public_ref) return public_head = repository.refs.get(public_ref) public_tree = public_head.data.tree # find the last published patch if options.last: last = __get_last(stack, public_tree) if not last: raise common.CmdException( 'Unable to find the last published patch ' '(possibly rebased stack)' ) out.info('%s' % last) return # check for same tree (already up to date) if public_tree.sha1 == stack.head.data.tree.sha1: out.info('"%s" already up to date' % public_ref) return # check for unpublished patches if options.unpublished: published = set(__get_published(stack, public_tree)) for p in stack.patchorder.applied: if p not in published: out.stdout(p) return if options.overwrite: repository.refs.set(public_ref, stack.head, 'publish') out.info('Overwrote "%s"' % public_ref) return # check for rebased stack. In this case we emulate a merge with the stack # base by setting two parents. merge_bases = set(repository.get_merge_bases(public_head, stack.base)) if public_head in merge_bases: # fast-forward the public ref repository.refs.set(public_ref, stack.head, 'publish') out.info('Fast-forwarded "%s"' % public_ref) return if stack.base not in merge_bases: message = 'Merge %s into %s' % ( repository.describe(stack.base).strip(), utils.strip_prefix('refs/heads/', public_ref), ) public_head = __create_commit( repository, stack.head.data.tree, [public_head, stack.base], options, message, ) repository.refs.set(public_ref, public_head, 'publish') out.info('Merged the stack base into "%s"' % public_ref) return # check for new patches from the last publishing. This is done by checking # whether the public tree is the same as the bottom of the checked patch. # If older patches were modified, new patches cannot be detected. The new # patches and their metadata are pushed directly to the published head. for p in stack.patchorder.applied: pc = stack.patches.get(p).commit if public_tree.sha1 == pc.data.parent.data.tree.sha1: if pc.data.is_nochange(): out.info('Ignored new empty patch "%s"' % p) continue cd = pc.data.set_parent(public_head) public_head = repository.commit(cd) public_tree = public_head.data.tree out.info('Published new patch "%s"' % p) # create a new commit (only happens if no new patches are detected) if public_tree.sha1 != stack.head.data.tree.sha1: public_head = __create_commit(repository, stack.head.data.tree, [public_head], options) # update the public head repository.refs.set(public_ref, public_head, 'publish') out.info('Updated "%s"' % public_ref)