5 Copyright (C) 2005, Catalin Marinas <catalin.marinas@gmail.com>
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License version 2 as
9 published by the Free Software Foundation.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 from stgit.argparse import opt
23 from stgit.commands import common
24 from stgit import argparse, git, templates
25 from stgit.out import out
26 from stgit.lib import git as gitlib
28 help = 'Export patches to a directory'
30 usage = ['[options] [<patch1>] [<patch2>] [<patch3>..<patch4>]']
32 Export a range of applied patches to a given directory (defaults to
33 'patches-<branch>') in a standard unified GNU diff format. A template
34 file (defaulting to '.git/patchexport.tmpl' or
35 '~/.stgit/templates/patchexport.tmpl' or
36 '/usr/share/stgit/templates/patchexport.tmpl') can be used for the
37 patch format. The following variables are supported in the template
40 %(description)s - patch description
41 %(shortdescr)s - the first line of the patch description
42 %(longdescr)s - the rest of the patch description, after the first line
43 %(diffstat)s - the diff statistics
44 %(authname)s - author's name
45 %(authemail)s - author's e-mail
46 %(authdate)s - patch creation date
47 %(commname)s - committer's name
48 %(commemail)s - committer's e-mail"""
50 args = [argparse.patch_range(argparse.applied_patches,
51 argparse.unapplied_patches,
52 argparse.hidden_patches)]
54 opt('-d', '--dir', args = [argparse.dir],
55 short = 'Export patches to DIR instead of the default'),
56 opt('-p', '--patch', action = 'store_true',
57 short = 'Append .patch to the patch names'),
58 opt('-e', '--extension',
59 short = 'Append .EXTENSION to the patch names'),
60 opt('-n', '--numbered', action = 'store_true',
61 short = 'Prefix the patch names with order numbers'),
62 opt('-t', '--template', metavar = 'FILE', args = [argparse.files],
63 short = 'Use FILE as a template'),
64 opt('-b', '--branch', args = [argparse.stg_branches],
65 short = 'Use BRANCH instead of the default branch'),
66 opt('-s', '--stdout', action = 'store_true',
67 short = 'Dump the patches to the standard output'),
68 ] + argparse.diff_opts_option()
70 directory = common.DirectoryHasRepositoryLib()
72 def func(parser, options, args):
73 """Export a range of patches.
75 stack = directory.repository.get_stack(options.branch)
80 dirname = 'patches-%s' % stack.name
81 directory.cd_to_topdir()
83 if not options.branch and git.local_changes():
84 out.warn('Local changes in the tree;'
85 ' you might want to commit them first')
87 if not options.stdout:
88 if not os.path.isdir(dirname):
90 series = file(os.path.join(dirname, 'series'), 'w+')
92 applied = stack.patchorder.applied
93 unapplied = stack.patchorder.unapplied
95 patches = common.parse_patches(args, applied + unapplied, len(applied))
101 raise common.CmdException, 'No patches applied'
103 zpadding = len(str(num))
109 tmpl = file(options.template).read()
111 tmpl = templates.get_template('patchexport.tmpl')
115 # note the base commit for this series
116 if not options.stdout:
117 base_commit = stack.patches.get(patches[0]).commit.sha1
118 print >> series, '# This series applies on GIT commit %s' % base_commit
124 pname = '%s.patch' % pname
125 elif options.extension:
126 pname = '%s.%s' % (pname, options.extension)
128 pname = '%s-%s' % (str(patch_no).zfill(zpadding), pname)
129 pfile = os.path.join(dirname, pname)
130 if not options.stdout:
131 print >> series, pname
133 # get the patch description
134 patch = stack.patches.get(p)
135 cd = patch.commit.data
137 descr = cd.message.strip()
138 descr_lines = descr.split('\n')
140 short_descr = descr_lines[0].rstrip()
141 long_descr = reduce(lambda x, y: x + '\n' + y,
142 descr_lines[1:], '').strip()
144 diff = stack.repository.diff_tree(cd.parent.data.tree, cd.tree, options.diff_flags)
146 tmpl_dict = {'description': descr,
147 'shortdescr': short_descr,
148 'longdescr': long_descr,
149 'diffstat': gitlib.diffstat(diff).rstrip(),
150 'authname': cd.author.name,
151 'authemail': cd.author.email,
152 'authdate': cd.author.date.isoformat(),
153 'commname': cd.committer.name,
154 'commemail': cd.committer.email}
155 for key in tmpl_dict:
156 if not tmpl_dict[key]:
160 descr = tmpl % tmpl_dict
161 except KeyError, err:
162 raise common.CmdException, 'Unknown patch template variable: %s' \
165 raise common.CmdException, 'Only "%(name)s" variables are ' \
166 'supported in the patch template'
171 f = open(pfile, 'w+')
173 if options.stdout and num > 1:
180 if not options.stdout:
184 if not options.stdout: