def func(parser, options, args): """Rebase the current stack """ if len(args) != 1: parser.error('incorrect number of arguments') repository = directory.repository stack = repository.get_stack() iw = repository.default_iw if stack.protected: raise CmdException('This branch is protected. Rebase is not permitted') target = git_commit(args[0], repository) applied = stack.patchorder.applied retval = prepare_rebase(stack, 'rebase') if retval: return retval rebase(stack, iw, target) if not options.nopush: return post_rebase(stack, applied, 'rebase', check_merged=options.merged)
def func(parser, options, args): """Rebase the current stack""" if options.interactive: # destination is optional for '--interactive' if len(args) not in (0, 1): parser.error('incorrect number of arguments') else: if len(args) != 1: parser.error('incorrect number of arguments') repository = directory.repository stack = repository.get_stack() iw = repository.default_iw if stack.protected: raise CmdException('This branch is protected. Rebase is not permitted') if len(args) > 0: target = git_commit(args[0], repository) else: target = stack.base if options.autostash and not iw.worktree_clean(): repository.run(['git', 'stash', 'push']).run() stashed_worktree = True else: stashed_worktree = False applied = stack.patchorder.applied retval = prepare_rebase(stack, 'rebase') if retval: return retval rebase(stack, iw, target) if options.interactive: retval = __do_rebase_interactive(repository, applied, check_merged=options.merged) elif not options.nopush: retval = post_rebase(stack, applied, 'rebase', check_merged=options.merged) else: retval = None if stashed_worktree: try: repository.run(['git', 'stash', 'pop']).run() except RunException: raise StashPopConflictException() return retval
def func(parser, options, args): """Rebase the current stack """ if len(args) != 1: parser.error('incorrect number of arguments') if crt_series.get_protected(): raise CmdException('This branch is protected. Rebase is not permitted') check_local_changes() check_conflicts() check_head_top_equal(crt_series) # ensure an exception is raised before popping on non-existent target if git_id(crt_series, args[0]) is None: raise GitException('Unknown revision: %s' % args[0]) applied = prepare_rebase(crt_series) rebase(crt_series, args[0]) post_rebase(crt_series, applied, options.nopush, options.merged) print_crt_patch(crt_series)
def func(parser, options, args): """Rebase the current stack""" if options.interactive: # destination is optional for '--interactive' if len(args) not in (0, 1): parser.error('incorrect number of arguments') else: if len(args) != 1: parser.error('incorrect number of arguments') repository = directory.repository stack = repository.get_stack() iw = repository.default_iw if stack.protected: raise CmdException('This branch is protected. Rebase is not permitted') if len(args) > 0: target = git_commit(args[0], repository) else: target = stack.base applied = stack.patchorder.applied retval = prepare_rebase(stack, 'rebase') if retval: return retval rebase(stack, iw, target) if options.interactive: return __do_rebase_interactive(repository, applied, check_merged=options.merged) elif not options.nopush: return post_rebase(stack, applied, 'rebase', check_merged=options.merged)
def func(parser, options, args): """Pull the changes from a remote repository""" repository = directory.repository iw = repository.default_iw stack = repository.get_stack() policy = config.get('branch.%s.stgit.pull-policy' % stack.name) or config.get('stgit.pull-policy') if policy not in ['pull', 'fetch-rebase', 'rebase']: raise GitConfigException('Unsupported pull-policy "%s"' % policy) remote_name = None if policy == 'rebase': # parent is local if len(args) == 1: parser.error( 'specifying a repository is meaningless for policy="%s"' % (policy, )) elif len(args) > 0: parser.error('incorrect number of arguments') else: # parent is remote if len(args) > 1: parser.error('incorrect number of arguments') if len(args) >= 1: remote_name = args[0] else: remote_name = stack.parent_remote if policy in ['pull', 'fetch-rebase'] and remote_name is None: parser.error( 'There is no tracking information for the current branch.\n' 'Please specify the remote repository to pull from.') if stack.protected: raise CmdException('This branch is protected. Pulls are not permitted') applied = stack.patchorder.applied retval = prepare_rebase(stack, 'pull') if retval: return retval # pull the remote changes if policy == 'pull': out.info('Pulling from "%s"' % remote_name) pull(repository, remote_name) elif policy == 'fetch-rebase': out.info('Fetching from "%s"' % remote_name) fetch(repository, remote_name) try: target = repository.rev_parse('FETCH_HEAD') except RepositoryException: out.error('Could not find the remote head to rebase onto - ' 'fix branch.%s.merge in .git/config' % stack.name) out.error('Pushing any patches back...') post_rebase(stack, applied, 'pull', check_merged=False) raise rebase(stack, iw, target) elif policy == 'rebase': value = config.get('branch.%s.stgit.parentbranch' % stack.name) if value: parent_commit = git_commit(value, repository) else: try: parent_commit = repository.rev_parse('heads/origin') except RepositoryException: raise CmdException('Cannot find a parent branch for "%s"' % stack.name) else: out.warn( 'No parent branch declared for stack "%s", defaulting to' '"heads/origin".' % stack.name, 'Consider setting "branch.%s.stgit.parentbranch" with ' '"git config".' % stack.name, ) rebase(stack, iw, parent_commit) if not options.nopush: post_rebase(stack, applied, 'pull', check_merged=options.merged) # maybe tidy up if config.getbool('stgit.keepoptimized'): repository.repack()
def func(parser, options, args): """Pull the changes from a remote repository """ policy = config.get('branch.%s.stgit.pull-policy' % crt_series.get_name()) or \ config.get('stgit.pull-policy') if policy == 'rebase': # parent is local if len(args) == 1: parser.error( 'specifying a repository is meaningless for policy="%s"' % policy) if len(args) > 0: parser.error('incorrect number of arguments') else: # parent is remote if len(args) > 1: parser.error('incorrect number of arguments') if len(args) >= 1: repository = args[0] else: repository = crt_series.get_parent_remote() if crt_series.get_protected(): raise CmdException('This branch is protected. Pulls are not permitted') check_local_changes() check_conflicts() check_head_top_equal(crt_series) if policy not in ['pull', 'fetch-rebase', 'rebase']: raise GitConfigException('Unsupported pull-policy "%s"' % policy) applied = prepare_rebase(crt_series) # pull the remote changes if policy == 'pull': out.info('Pulling from "%s"' % repository) git.pull(repository) elif policy == 'fetch-rebase': out.info('Fetching from "%s"' % repository) git.fetch(repository) try: target = git.fetch_head() except git.GitException: out.error( 'Could not find the remote head to rebase onto - fix branch.%s.merge in .git/config' % crt_series.get_name()) out.error('Pushing any patches back...') post_rebase(crt_series, applied, False, False) raise rebase(crt_series, target) elif policy == 'rebase': rebase(crt_series, crt_series.get_parent_branch()) post_rebase(crt_series, applied, options.nopush, options.merged) # maybe tidy up if config.getbool('stgit.keepoptimized'): git.repack() print_crt_patch(crt_series)
def func(parser, options, args): """Pull the changes from a remote repository """ policy = config.get('branch.%s.stgit.pull-policy' % crt_series.get_name()) or \ config.get('stgit.pull-policy') if policy == 'rebase': # parent is local if len(args) == 1: parser.error('specifying a repository is meaningless for policy="%s"' % policy) if len(args) > 0: parser.error('incorrect number of arguments') else: # parent is remote if len(args) > 1: parser.error('incorrect number of arguments') if len(args) >= 1: repository = args[0] else: repository = crt_series.get_parent_remote() if crt_series.get_protected(): raise CmdException('This branch is protected. Pulls are not permitted') check_local_changes() check_conflicts() check_head_top_equal(crt_series) if policy not in ['pull', 'fetch-rebase', 'rebase']: raise GitConfigException('Unsupported pull-policy "%s"' % policy) applied = prepare_rebase(crt_series) # pull the remote changes if policy == 'pull': out.info('Pulling from "%s"' % repository) git.pull(repository) elif policy == 'fetch-rebase': out.info('Fetching from "%s"' % repository) git.fetch(repository) try: target = git.fetch_head() except git.GitException: out.error('Could not find the remote head to rebase onto - fix branch.%s.merge in .git/config' % crt_series.get_name()) out.error('Pushing any patches back...') post_rebase(crt_series, applied, False, False) raise rebase(crt_series, target) elif policy == 'rebase': rebase(crt_series, crt_series.get_parent_branch()) post_rebase(crt_series, applied, options.nopush, options.merged) # maybe tidy up if config.get('stgit.keepoptimized') == 'yes': git.repack() print_crt_patch(crt_series)
def __do_rebase_interactive(repository, previously_applied_patches, check_merged): """Opens an interactive editor, generates instruction list, and executes instructions.""" stack = repository.get_stack() if len(stack.patchorder.all) == 0: return utils.STGIT_SUCCESS name_len = max((len(s) for s in stack.patchorder.all)) line = 'keep {{:<{name_len}}} # {{}}'.format(name_len=name_len) # create a list of all patches to send to the editor raw_editor_instructions = [] for pn in previously_applied_patches: patch = (pn, __get_description(stack, pn)) raw_editor_instructions.append(line.format(*patch)) raw_editor_instructions.append(INTERACTIVE_APPLY_LINE) for pn in stack.patchorder.all: if pn in previously_applied_patches: continue patch = (pn, __get_description(stack, pn)) raw_editor_instructions.append(line.format(*patch)) raw_editor_instructions.append(INTERACTIVE_HELP_INSTRUCTIONS) # open an editor to let the user generate the 'todo' instructions todo = edit_string( '\n'.join(raw_editor_instructions), '.stgit-rebase-interactive.txt', ) # parse the instructions we've been given seen_apply_line = False instructions = [] for line in todo.splitlines(): line = line.strip() # record when we find the APPLY_LINE so we know which patches to apply if INTERACTIVE_APPLY_LINE in line: if INTERACTIVE_APPLY_LINE == line: seen_apply_line = True else: raise CmdException("Bad APPLY_LINE: '%s'" % line) # ignore comment lines if not line or line.startswith('#'): continue # parse a single instruction match = re.match(r'(\S+) (\S+).*', line) if not match: raise CmdException("Bad todo line: '%s'" % line) instruction_str, patch_name = match.groups() if patch_name not in stack.patchorder.all: raise CmdException("Bad patch name '%s'" % patch_name) if instruction_str in ('k', 'keep'): instruction_type = Action.KEEP elif instruction_str in ('e', 'edit'): instruction_type = Action.EDIT elif instruction_str in ('s', 'squash'): instruction_type = Action.SQUASH elif instruction_str in ('f', 'fix', 'fixup'): instruction_type = Action.FIXUP elif instruction_str in ('h', 'hide'): instruction_type = Action.HIDE elif instruction_str in ('d', 'delete'): instruction_type = Action.DELETE else: raise CmdException("Unknown instruction '%s'" % instruction_str) # save the instruction to execute later instructions.append( Instruction(patch_name, instruction_type, not seen_apply_line)) index = 0 while index < len(instructions): patch_name = instructions[index].patch_name if instructions[index].action == Action.HIDE: hide_patches(stack, stack.repository.default_iw, {patch_name}) instructions.pop(index) index = max(0, index - 1) # re-run this index another time continue if instructions[index].action == Action.DELETE: delete_patches(stack, stack.repository.default_iw, {patch_name}) instructions.pop(index) index = max(0, index - 1) # re-run this index another time continue if instructions[index].action == Action.FIXUP and index > 0: base_patch = instructions[index - 1].patch_name __fixup_patches(stack, [base_patch, patch_name]) instructions.pop(index) # remove 'fixed' (ie deleted) patch index -= 1 # re-run this index another time continue if instructions[index].action == Action.EDIT: cd = stack.patches[patch_name].data cd, new_patch_name, failed_diff = edit.interactive_edit_patch( stack.repository, cd, patch_name, edit_diff=True, diff_flags="") if failed_diff is not None: return utils.STGIT_COMMAND_ERROR _, new_patch_name = edit.perform_edit( stack, cd, patch_name, new_patch_name, edit_diff=True, diff_flags="", ) if new_patch_name and patch_name != new_patch_name: instructions[index] = Instruction( new_patch_name, instructions[index].action, instructions[index].apply, ) patch_name = new_patch_name instructions = __perform_squashes(instructions, index, stack) index += 1 post_rebase( stack, [ instruction.patch_name for instruction in instructions if instruction.apply ], 'rebase', check_merged, ) return utils.STGIT_SUCCESS
def __do_rebase_interactive(repository, previously_applied_patches, check_merged): """Opens an interactive editor, generates instruction list, and executes instructions.""" stack = repository.get_stack() if len(stack.patchorder.all) == 0: return utils.STGIT_SUCCESS name_len = max((len(s) for s in stack.patchorder.all)) line = 'keep {{:<{name_len}}} # {{}}'.format(name_len=name_len) # create a list of all patches to send to the editor instructions = [] for pn in previously_applied_patches: patch = (pn, __get_description(stack, pn)) instructions.append(line.format(*patch)) instructions.append(INTERACTIVE_APPLY_LINE) for pn in stack.patchorder.all: if pn in previously_applied_patches: continue patch = (pn, __get_description(stack, pn)) instructions.append(line.format(*patch)) instructions.append(INTERACTIVE_HELP_INSTRUCTIONS) # open an editor to let the user generate the 'todo' instructions todo = edit_string( '\n'.join(instructions), '.stgit-rebase-interactive.txt', ) # parse the instructions we've been given seen_apply_line = False instructions = [] for line in todo.splitlines(): line = line.strip() # record when we find the APPLY_LINE so we know which patches to apply if INTERACTIVE_APPLY_LINE in line: if INTERACTIVE_APPLY_LINE == line: seen_apply_line = True else: raise CmdException("Bad APPLY_LINE: '%s'" % line) # ignore comment lines if not line or line.startswith('#'): continue # parse a single instruction match = re.match(r'(\S+) (\S+).*', line) if not match: raise CmdException("Bad todo line: '%s'" % line) instruction_str, patch_name = match.groups() if patch_name not in stack.patchorder.all: raise CmdException("Bad patch name '%s'" % patch_name) if instruction_str in ('k', 'keep'): instruction_type = Action.KEEP elif instruction_str in ('d', 'delete'): instruction_type = Action.DELETE elif instruction_str in ('s', 'squash'): instruction_type = Action.SQUASH else: raise CmdException("Unknown instruction '%s'" % instruction_str) # save the instruction to execute later instructions.append( Instruction(patch_name, instruction_type, not seen_apply_line)) # execute all the non-squash instructions first. we use a copy to make sure # we don't skip any instructions as we delete from the list for instruction in copy.copy(instructions): if instruction.apply: post_rebase(stack, {instruction.patch_name}, 'rebase', check_merged) if instruction.action == Action.DELETE: delete_patches(stack, stack.repository.default_iw, {instruction.patch_name}) instructions.remove(instruction) # execute squashes last because patch names may change during squashes. this # is a double loop so we can handle multiple chains of multiple squashes for index in itertools.count(): if index >= len(instructions): break # reached the end of the instruction list # determine how many of the next N instructions are squashes squashes_found = False for num_squashes in itertools.count(1): if len(instructions) <= index + num_squashes: break # reached the end of the instruction list if instructions[index + num_squashes].action == Action.SQUASH: squashes_found = True else: break # not a squash; chain is over # execute squash if squashes_found: squashes = instructions[index:index + num_squashes] squash( stack, stack.repository.default_iw, None, None, None, [p.patch_name for p in squashes], ) # remove the squashed patches from the instruction set instructions = (instructions[:index + 1] + instructions[index + num_squashes:]) return utils.STGIT_SUCCESS