return (descr, authname, authemail, authdate, diff)
-def parse_patch(text):
+def parse_patch(text, contains_diff):
"""Parse the input text and return (description, authname,
authemail, authdate, diff)
"""
- descr, diff = __split_descr_diff(text)
- descr, authname, authemail, authdate = __parse_description(descr)
+ if contains_diff:
+ (text, diff) = __split_descr_diff(text)
+ else:
+ diff = None
+ (descr, authname, authemail, authdate) = __parse_description(text)
# we don't yet have an agreed place for the creation date.
# Just return None
from stgit.utils import *
from stgit.out import *
from stgit import argparse, stack, git
+from stgit.lib import git as gitlib
help = 'Show the tree diff'
kind = 'wc'
rev2 and git_id(crt_series, rev2),
diff_flags = options.diff_flags)
if options.stat:
- out.stdout_raw(git.diffstat(diff_str) + '\n')
+ out.stdout_raw(gitlib.diffstat(diff_str) + '\n')
else:
if diff_str:
pager(diff_str)
from stgit.argparse import opt
from stgit import argparse, git, utils
from stgit.commands import common
-from stgit.lib import git as gitlib, transaction
+from stgit.lib import git as gitlib, transaction, edit
from stgit.out import *
help = 'edit a patch description or diff'
directory = common.DirectoryHasRepositoryLib()
-def patch_diff(repository, cd, diff, diff_flags):
- if diff:
- diff = repository.diff_tree(cd.parent.data.tree, cd.tree, diff_flags)
- return '\n'.join([git.diffstat(diff), diff])
- else:
- return None
-
-def patch_description(cd, diff):
- """Generate a string containing the description to edit."""
-
- desc = ['From: %s <%s>' % (cd.author.name, cd.author.email),
- 'Date: %s' % cd.author.date.isoformat(),
- '',
- cd.message]
- if diff:
- desc += ['---',
- '',
- diff]
- return '\n'.join(desc)
-
-def patch_desc(repository, cd, failed_diff, diff, diff_flags):
- return patch_description(cd, failed_diff or patch_diff(
- repository, cd, diff, diff_flags))
-
-def update_patch_description(repository, cd, text):
- message, authname, authemail, authdate, diff = common.parse_patch(text)
- cd = (cd.set_message(message)
- .set_author(cd.author.set_name(authname)
- .set_email(authemail)
- .set_date(gitlib.Date.maybe(authdate))))
- failed_diff = None
- if diff:
- tree = repository.apply(cd.parent.data.tree, diff, quiet = False)
- if tree == None:
- failed_diff = diff
- else:
- cd = cd.set_tree(tree)
- return cd, failed_diff
-
def func(parser, options, args):
"""Edit the given patch or the current one.
"""
cd = orig_cd = stack.patches.get(patchname).commit.data
- # Read patch from user-provided description.
- if options.message == None:
- failed_diff = None
- else:
- cd, failed_diff = update_patch_description(stack.repository, cd,
- options.message)
-
- # Modify author and committer data.
- a, c = options.author(cd.author), options.committer(cd.committer)
- if (a, c) != (cd.author, cd.committer):
- cd = cd.set_author(a).set_committer(c)
-
- # Add Signed-off-by: or similar.
- if options.sign_str != None:
- cd = cd.set_message(utils.add_sign_line(
- cd.message, options.sign_str, gitlib.Person.committer().name,
- gitlib.Person.committer().email))
+ cd, failed_diff = edit.auto_edit_patch(
+ stack.repository, cd, msg = options.message, contains_diff = True,
+ author = options.author, committer = options.committer,
+ sign_str = options.sign_str)
if options.save_template:
options.save_template(
- patch_desc(stack.repository, cd, failed_diff,
- options.diff, options.diff_flags))
+ edit.patch_desc(stack.repository, cd,
+ options.diff, options.diff_flags, failed_diff))
return utils.STGIT_SUCCESS
- # Let user edit the patch manually.
if cd == orig_cd or options.edit:
- fn = '.stgit-edit.' + ['txt', 'patch'][bool(options.diff)]
- cd, failed_diff = update_patch_description(
- stack.repository, cd, utils.edit_string(
- patch_desc(stack.repository, cd, failed_diff,
- options.diff, options.diff_flags),
- fn))
+ cd, failed_diff = edit.interactive_edit_patch(
+ stack.repository, cd, options.diff, options.diff_flags, failed_diff)
def failed():
fn = '.stgit-failed.patch'
f = file(fn, 'w')
- f.write(patch_desc(stack.repository, cd, failed_diff,
- options.diff, options.diff_flags))
+ f.write(edit.patch_desc(stack.repository, cd,
+ options.diff, options.diff_flags, failed_diff))
f.close()
out.error('Edited patch did not apply.',
'It has been saved to "%s".' % fn)
tmpl_dict = {'description': descr,
'shortdescr': short_descr,
'longdescr': long_descr,
- 'diffstat': git.diffstat(diff).rstrip(),
+ 'diffstat': gitlib.diffstat(diff).rstrip(),
'authname': cd.author.name,
'authemail': cd.author.email,
'authdate': cd.author.date.isoformat(),
from stgit.utils import *
from stgit.out import *
from stgit import argparse, stack, git
+from stgit.lib import git as gitlib
help = 'Show the files modified by a patch (or the current patch)'
kind = 'patch'
rev2 = git_id(crt_series, '%s' % patch)
if options.stat:
- out.stdout_raw(git.diffstat(git.diff(rev1 = rev1, rev2 = rev2)) + '\n')
+ out.stdout_raw(gitlib.diffstat(git.diff(rev1 = rev1, rev2 = rev2)) + '\n')
elif options.bare:
out.stdout_raw(git.barefiles(rev1, rev2) + '\n')
else:
parse_mail(msg)
else:
message, author_name, author_email, author_date, diff = \
- parse_patch(f.read())
+ parse_patch(f.read(), contains_diff = True)
if filename:
f.close()
from stgit import argparse, stack, git, version, templates
from stgit.config import config
from stgit.run import Run
+from stgit.lib import git as gitlib
help = 'Send a patch or series of patches by e-mail'
kind = 'patch'
'number': number_str,
'shortlog': stack.shortlog(crt_series.get_patch(p)
for p in patches),
- 'diffstat': git.diffstat(git.diff(
+ 'diffstat': gitlib.diffstat(git.diff(
rev1 = git_id(crt_series, '%s^' % patches[0]),
rev2 = git_id(crt_series, '%s' % patches[-1])))}
# for backward template compatibility
'endofheaders': '',
'diff': diff,
- 'diffstat': git.diffstat(diff),
+ 'diffstat': gitlib.diffstat(diff),
# for backward template compatibility
'date': '',
'version': version_str,
else:
return ''
-def diffstat(diff):
- """Return the diffstat of the supplied diff."""
- return GRun('apply', '--stat', '--summary').raw_input(diff).raw_output()
-
def files(rev1, rev2, diff_flags = []):
"""Return the files modified between rev1 and rev2
"""
--- /dev/null
+"""This module contains utility functions for patch editing."""
+
+from stgit import utils
+from stgit.commands import common
+from stgit.lib import git
+
+def update_patch_description(repo, cd, text, contains_diff):
+ """Update the given L{CommitData<stgit.lib.git.CommitData>} with the
+ given text description, which may contain author name and time
+ stamp in addition to a new commit message. If C{contains_diff} is
+ true, it may also contain a replacement 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."""
+ (message, authname, authemail, authdate, diff
+ ) = common.parse_patch(text, contains_diff)
+ a = cd.author
+ for val, setter in [(authname, 'set_name'), (authemail, 'set_email'),
+ (git.Date.maybe(authdate), 'set_date')]:
+ if val != None:
+ a = getattr(a, setter)(val)
+ cd = cd.set_message(message).set_author(a)
+ failed_diff = None
+ if diff:
+ tree = repo.apply(cd.parent.data.tree, diff, quiet = False)
+ if tree == None:
+ failed_diff = diff
+ else:
+ cd = cd.set_tree(tree)
+ return cd, failed_diff
+
+def patch_desc(repo, cd, append_diff, diff_flags, replacement_diff):
+ """Return a description text for the patch, suitable for editing
+ and/or reimporting with L{update_patch_description()}.
+
+ @param cd: The L{CommitData<stgit.lib.git.CommitData>} to generate
+ a description of
+ @param append_diff: Whether to append the patch diff to the
+ description
+ @type append_diff: C{bool}
+ @param diff_flags: Extra parameters to pass to C{git diff}
+ @param replacement_diff: Diff text to use; or C{None} if it should
+ be computed from C{cd}
+ @type replacement_diff: C{str} or C{None}"""
+ desc = ['From: %s <%s>' % (cd.author.name, cd.author.email),
+ 'Date: %s' % cd.author.date.isoformat(),
+ '',
+ cd.message]
+ if append_diff:
+ if replacement_diff:
+ diff = replacement_diff
+ else:
+ just_diff = repo.diff_tree(cd.parent.data.tree, cd.tree, diff_flags)
+ diff = '\n'.join([git.diffstat(just_diff), just_diff])
+ desc += ['---', '', diff]
+ return '\n'.join(desc)
+
+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 auto_edit_patch(repo, cd, msg, contains_diff, author, committer, sign_str):
+ """Edit the patch noninteractively in a couple of ways:
+
+ - If C{msg} is not C{None}, parse it to find a replacement
+ message, and possibly also replacement author and
+ timestamp. If C{contains_diff} is true, also look for a
+ replacement diff.
+
+ - C{author} and C{committer} are two functions that take the
+ original L{Person<stgit.lib.git.Person>} value as argument,
+ and return the new one.
+
+ - C{sign_str}, if not C{None}, is a sign string to append to
+ the message.
+
+ Return a pair: the new L{CommitData<stgit.lib.git.CommitData>};
+ and the diff text if it didn't apply, or C{None} otherwise."""
+ if msg == None:
+ failed_diff = None
+ else:
+ cd, failed_diff = update_patch_description(repo, cd, msg, contains_diff)
+ a, c = author(cd.author), committer(cd.committer)
+ if (a, c) != (cd.author, cd.committer):
+ cd = cd.set_author(a).set_committer(c)
+ if sign_str != None:
+ cd = cd.set_message(utils.add_sign_line(
+ cd.message, sign_str, git.Person.committer().name,
+ git.Person.committer().email))
+ return cd, failed_diff
repository.run(['git', 'branch', create_at.sha1]).discard_output()
return cls(repository, name)
+
+def diffstat(diff):
+ """Return the diffstat of the supplied diff."""
+ return run.Run('git', 'apply', '--stat', '--summary'
+ ).raw_input(diff).raw_output()
{
cat > "$1" <<EOF
#!/bin/sh
-printf "\n$1\n" >> "\$1"
+printf "\n$1" >> "\$1"
EOF
chmod a+x "$1"
}
git config --unset stgit.editor
mkeditor twoliner
-test_expect_failure 'Both noninterative and interactive editing' '
+test_expect_success 'Both noninterative and interactive editing' '
EDITOR=./twoliner stg edit -e -m "oneliner" p2 &&
test "$(msg HEAD)" = "oneliner/twoliner"
'