def func(parser, options, args): """Clone the <repository> into the local <dir> and initialises the stack """ if len(args) != 2: parser.error('incorrect number of arguments') repository = args[0] local_dir = args[1] if os.path.exists(local_dir): raise CmdException, '"%s" exists. Remove it first' % local_dir print 'Cloning "%s" into "%s"...' % (repository, local_dir) git.clone(repository, local_dir) os.chdir(local_dir) git.checkout(tree_id='HEAD') # be sure to forget any cached value for .git, since we're going # to work on a brand new repository basedir.clear_cache() stack.Series().init() print 'done'
def git_id(rev): """Return the GIT id """ if not rev: return None try: patch, branch, patch_id = parse_rev(rev) if branch == None: series = crt_series else: series = stack.Series(branch) if patch == None: patch = series.get_current() if not patch: raise CmdException, 'No patches applied' if patch in series.get_applied() or patch in series.get_unapplied() or \ patch in series.get_hidden(): if patch_id in ['top', '', None]: return series.get_patch(patch).get_top() elif patch_id == 'bottom': return series.get_patch(patch).get_bottom() elif patch_id == 'top.old': return series.get_patch(patch).get_old_top() elif patch_id == 'bottom.old': return series.get_patch(patch).get_old_bottom() elif patch_id == 'log': return series.get_patch(patch).get_log() if patch == 'base' and patch_id == None: return series.get_base() except RevParseException: pass return git.rev_parse(rev + '^{commit}')
def __cleanup_branch(name, force=False): branch = stack.Series(name) if branch.get_protected(): raise CmdException( 'This branch is protected. Clean up is not permitted') out.start('Cleaning up branch "%s"' % name) branch.delete(force=force, cleanup=True) out.done()
def print_crt_patch(crt_series, branch=None): if not branch: patch = crt_series.get_current() else: patch = stack.Series(branch).get_current() if patch: out.info('Now at patch "%s"' % patch) else: out.info('No patches applied')
def __delete_branch(doomed_name, force=False): doomed = stack.Series(doomed_name) if __is_current_branch(doomed_name): raise CmdException('Cannot delete the current branch') if doomed.get_protected(): raise CmdException('This branch is protected. Delete is not permitted') out.start('Deleting branch "%s"' % doomed_name) doomed.delete(force) out.done()
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 __delete_branch(doomed_name, force=False): doomed = stack.Series(doomed_name) if doomed.get_protected(): raise CmdException, 'This branch is protected. Delete is not permitted' out.start('Deleting branch "%s"' % doomed_name) if __is_current_branch(doomed_name): check_local_changes() check_conflicts() check_head_top_equal() if doomed_name != 'master': git.switch_branch('master') doomed.delete(force) if doomed_name != 'master': git.delete_branch(doomed_name) out.done()
def func(parser, options, args): """Synchronise a range of patches """ if options.undo: if options.ref_branch or options.series: raise CmdException, \ '--undo cannot be specified with --ref-branch or --series' __check_all() out.start('Undoing the sync of "%s"' % crt_series.get_current()) crt_series.undo_refresh() git.reset() out.done() return 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 = [] f = file(options.series) for line in f: p = re.sub('#.*$', '', line).strip() if not p: continue remote_patches.append(p) f.close() # 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() if options.all: patches = applied elif len(args) != 0: patches = parse_patches(args, 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 popped = applied[applied.index(sync_patches[0]) + 1:] if popped: pop_patches(popped[::-1]) for p in sync_patches: if p in popped: # push to this patch idx = popped.index(p) + 1 push_patches(popped[:idx]) del popped[:idx] # the actual sync out.start('Synchronising "%s"' % p) patch = crt_series.get_patch(p) bottom = patch.get_bottom() top = patch.get_top() # reset the patch backup information. That's needed in case we # undo the sync but there were no changes made patch.set_bottom(bottom, backup = True) 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() # push the remaining patches if popped: push_patches(popped)
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.get_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.get_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.get_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.get_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.get_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.get_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') print(crt_series.get_name())
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 """ global crt_series if options.all and options.short: raise CmdException, 'combining --all and --short is meaningless' # current series patches if options.invisible: applied = unapplied = [] hidden = crt_series.get_hidden() elif options.all: applied = crt_series.get_applied() unapplied = crt_series.get_unapplied() hidden = crt_series.get_hidden() else: applied = crt_series.get_applied() unapplied = crt_series.get_unapplied() hidden = [] if options.missing: # switch the series, the one specified with --missing should # become the current cmp_series = crt_series crt_series = stack.Series(options.missing) stgit.commands.common.crt_series = crt_series cmp_patches = applied + unapplied + hidden # new current series patches if options.invisible: applied = unapplied = [] hidden = crt_series.get_hidden() elif options.all: applied = crt_series.get_applied() unapplied = crt_series.get_unapplied() hidden = crt_series.get_hidden() else: applied = crt_series.get_applied() unapplied = crt_series.get_unapplied() hidden = [] 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 = '@' + crt_series.get_name() else: branch_str = '' if options.graphical: if options.missing: raise CmdException, '--graphical not supported with --missing' if applied: gitk_args = ' %s^..%s' % (git_id(applied[0]), git_id(applied[-1])) else: gitk_args = '' for p in unapplied: patch_id = git_id(p) gitk_args += ' %s^..%s' % (patch_id, patch_id) if os.system('gitk%s' % gitk_args) != 0: raise CmdException, 'gitk execution failed' else: 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(p, branch_str, '+ ', '0 ', max_len, options) __print_patch(applied[-1], branch_str, '> ', '0>', max_len, options) for p in unapplied: __print_patch(p, branch_str, '- ', '0 ', max_len, options) for p in hidden: __print_patch(p, branch_str, '! ', '! ', max_len, options)
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() tree_id = None if len(args) >= 2: parentbranch = None try: branchpoint = git.rev_parse(args[1]) # first, look for branchpoint in well-known branch namespaces for namespace in ('refs/heads/', 'remotes/'): # check if branchpoint exists in namespace try: maybehead = git.rev_parse(namespace + args[1]) except git.GitException: maybehead = None # check if git resolved branchpoint to this namespace if maybehead and branchpoint == maybehead: # we are for sure referring to a branch parentbranch = namespace + args[1] 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 = branchpoint or git_id(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]) return elif options.clone: if len(args) == 0: clone = crt_series.get_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() out.start('Cloning current branch to "%s"' % clone) crt_series.clone(clone) out.done() return elif options.delete: if len(args) != 1: parser.error('incorrect number of arguments') __delete_branch(args[0], options.force) return elif options.list: if len(args) != 0: parser.error('incorrect number of arguments') branches = [] basepath = os.path.join(basedir.get(), 'refs', 'heads') for path, files, dirs in walk_tree(basepath): branches += [os.path.join(path, f) for f in files] branches.sort() if branches: out.info('Available branches:') max_len = max([len(i) for i in branches]) for i in branches: __print_branch(i, max_len) else: out.info('No branches') return elif options.protect: if len(args) == 0: branch_name = crt_series.get_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])) return elif options.unprotect: if len(args) == 0: branch_name = crt_series.get_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.get_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] check_local_changes() check_conflicts() check_head_top_equal() 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') print crt_series.get_name()