def update_commit_data(cd, options): """Return a new CommitData object updated according to the command line options.""" # Set the commit message from commandline. if options.message is not None: cd = cd.set_message(options.message) # Modify author data. cd = cd.set_author(options.author(cd.author)) # Add Signed-off-by: or similar. if options.sign_str is not None: sign_str = options.sign_str else: sign_str = config.get("stgit.autosign") if sign_str is not None: cd = cd.set_message( add_sign_line(cd.message, sign_str, cd.committer.name, cd.committer.email)) # Let user edit the commit message manually, unless # --save-template or --message was specified. if not getattr(options, 'save_template', None) and options.message is None: tmpl = templates.get_template('patchdescr.tmpl') if tmpl: cd = cd.set_message(cd.message + tmpl) cd = cd.set_message(edit_string(cd.message, '.stgit-new.txt')) return cd
def _squash_patches(trans, patches, msg, save_template, no_verify=False): cd = trans.patches[patches[0]].data for pn in patches[1:]: c = trans.patches[pn] tree = trans.stack.repository.simple_merge( base=c.data.parent.data.tree, ours=cd.tree, theirs=c.data.tree, ) if not tree: return None cd = cd.set_tree(tree) if msg is None: msg = _append_comment( cd.message_str, '\n\n'.join('%s\n\n%s' % (pn.ljust(70, '-'), trans.patches[pn].data.message_str) for pn in patches[1:])) if save_template: save_template(msg.encode(cd.encoding)) raise SaveTemplateDone() else: msg = utils.edit_string(msg, '.stgit-squash.txt') msg = _strip_comment(msg).strip() cd = cd.set_message(msg) if not no_verify: cd = run_commit_msg_hook(trans.stack.repository, cd) return cd
def _squash_patches(trans, patches, msg, save_template): cd = trans.patches[patches[0]].data cd = git.CommitData(tree = cd.tree, parents = cd.parents) for pn in patches[1:]: c = trans.patches[pn] tree = trans.stack.repository.simple_merge( base = c.data.parent.data.tree, ours = cd.tree, theirs = c.data.tree) if not tree: return None cd = cd.set_tree(tree) if msg == None: msg = utils.append_comment( trans.patches[patches[0]].data.message, '\n\n'.join('%s\n\n%s' % (pn.ljust(70, '-'), trans.patches[pn].data.message) for pn in patches[1:])) if save_template: save_template(msg) raise SaveTemplateDone() else: msg = utils.edit_string(msg, '.stgit-squash.txt') msg = utils.strip_comment(msg).strip() cd = cd.set_message(msg) return cd
def update_commit_data( cd, message=None, author=None, sign_str=None, edit=False ): """Return a new CommitData object updated according to the command line options.""" # Set the commit message from commandline. if message is not None: cd = cd.set_message(message) # Modify author data. if author is not None: cd = cd.set_author(author) # Add Signed-off-by: or similar. if sign_str is None: sign_str = config.get("stgit.autosign") if sign_str: cd = cd.set_message( add_sign_line( cd.message, sign_str, cd.committer.name, cd.committer.email ) ) if edit: tmpl = templates.get_template('patchdescr.tmpl') if tmpl: cd = cd.set_message(cd.message + tmpl) cd = cd.set_message(edit_string(cd.message, '.stgit-new.txt')) return cd
def update_commit_data(cd, options): """Return a new CommitData object updated according to the command line options.""" # Set the commit message from commandline. if options.message is not None: cd = cd.set_message(options.message) # Modify author data. cd = cd.set_author(options.author(cd.author)) # Add Signed-off-by: or similar. if options.sign_str is not None: sign_str = options.sign_str else: sign_str = config.get("stgit.autosign") if sign_str is not None: cd = cd.set_message( add_sign_line(cd.message, sign_str, cd.committer.name, cd.committer.email)) # Let user edit the commit message manually, unless # --save-template or --message was specified. if not getattr(options, 'save_template', None) and options.message is None: cd = cd.set_message(edit_string(cd.message, '.stgit-new.txt')) return cd
def interactive_edit_patch(repo, cd, edit_diff, diff_flags, replacement_diff): """Edit the patch interactively. If C{edit_diff} is true, edit the diff as well. If C{replacement_diff} is not C{None}, it contains a diff to edit instead of the patch's real diff. Return a pair: the new L{CommitData<stgit.lib.git.CommitData>}; and the diff text if it didn't apply, or C{None} otherwise.""" return update_patch_description( repo, cd, utils.edit_string( patch_desc(repo, cd, edit_diff, diff_flags, replacement_diff), '.stgit-edit.' + ['txt', 'patch'][bool(edit_diff)]), edit_diff)
def update_commit_data(repo, cd, message=None, author=None, trailers=None, edit=False, verbose=False): """Create updated CommitData according to the command line options.""" iw = repo.default_iw # Set the commit message from commandline. if message is not None: cd = cd.set_message(message) # Modify author data. if author is not None: cd = cd.set_author(author) # Add Signed-off-by: or similar. if not trailers: autosign = config.get("stgit.autosign") if autosign: trailers = [(autosign, None)] if trailers: cd = cd.set_message( add_trailers(cd.message_str, trailers, cd.committer.name, cd.committer.email)) if edit: message_str = cd.message_str tmpl = templates.get_template('patchdescr.tmpl') if tmpl: message_str += tmpl status = '\n# '.join(_git_status()) message_str += COMMIT_MESSAGE_INSTRUCTIONS + status if verbose: # include a diff message_str += (COMMIT_MESSAGE_DEMARCATION_LINE + COMMIT_MESSAGE_INSTRUCTIONS_2) message_str += iw.diff( repo.rev_parse('HEAD').data.tree, binary=False, ).decode('utf-8') new_message = edit_string(message_str, '.stgit-new.txt') new_message = new_message.split(COMMIT_MESSAGE_DEMARCATION_LINE)[0] new_message = '\n'.join(line for line in new_message.splitlines() if not line.startswith('#')) cd = cd.set_message(new_message) return cd
def update_commit_data(cd, message=None, author=None, sign_str=None, edit=False, verbose=False): """Create updated CommitData according to the command line options.""" # Set the commit message from commandline. if message is not None: cd = cd.set_message(message) # Modify author data. if author is not None: cd = cd.set_author(author) # Add Signed-off-by: or similar. if sign_str is None: sign_str = config.get("stgit.autosign") if sign_str: cd = cd.set_message( add_trailer(cd.message_str, sign_str, cd.committer.name, cd.committer.email)) if edit: message_str = cd.message_str tmpl = templates.get_template('patchdescr.tmpl') if tmpl: message_str += tmpl status = '\n# '.join(_git_status()) message_str += COMMIT_MESSAGE_INSTRUCTIONS + status if verbose: # include a diff message_str += (COMMIT_MESSAGE_DEMARCATION_LINE + COMMIT_MESSAGE_INSTRUCTIONS_2) message_str += '\n'.join(_git_diff()) new_message = edit_string(message_str, '.stgit-new.txt') new_message = new_message.split(COMMIT_MESSAGE_DEMARCATION_LINE)[0] new_message = '\n'.join(line for line in new_message.splitlines() if not line.startswith('#')) cd = cd.set_message(new_message) return cd
def _squash_patches(trans, patches, name, msg, save_template, no_verify=False): cd = trans.patches[patches[0]].data for pn in patches[1:]: c = trans.patches[pn] tree = trans.stack.repository.simple_merge( base=c.data.parent.data.tree, ours=cd.tree, theirs=c.data.tree, ) if not tree: return None cd = cd.set_tree(tree) if msg is None: if name: msg = "# Squashing %s patches as '%s'.\n" % (len(patches), name) else: msg = "# Squashing %s patches.\n" % len(patches) for num, pn in enumerate(patches, 1): msg += "# This is the commit message for patch #%s (%s):" % ( num, pn, ) msg += "\n%s\n\n" % trans.patches[pn].data.message_str.rstrip() msg += ( "# Please enter the commit message for your patch. Lines starting\n" "# with '#' will be ignored.") if save_template: save_template(msg.encode(cd.encoding)) raise SaveTemplateDone() else: msg = utils.edit_string(msg, '.stgit-squash.txt') msg = '\n'.join(_strip_comments(msg)).strip() if not msg: raise CmdException('Aborting squash due to empty commit message') cd = cd.set_message(msg) if not no_verify: cd = run_commit_msg_hook(trans.stack.repository, cd) return cd
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