fix export -s
[stgit] / stgit / commands / export.py
1 """Export command
2 """
3
4 __copyright__ = """
5 Copyright (C) 2005, Catalin Marinas <catalin.marinas@gmail.com>
6
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.
10
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.
15
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
19 """
20
21 import os
22 import sys
23 from stgit.argparse import opt
24 from stgit.commands import common
25 from stgit import argparse, git, templates
26 from stgit.out import out
27 from stgit.lib import git as gitlib
28
29 help = 'Export patches to a directory'
30 kind = 'patch'
31 usage = ['[options] [<patch1>] [<patch2>] [<patch3>..<patch4>]']
32 description = """
33 Export a range of applied patches to a given directory (defaults to
34 'patches-<branch>') in a standard unified GNU diff format. A template
35 file (defaulting to '.git/patchexport.tmpl' or
36 '~/.stgit/templates/patchexport.tmpl' or
37 '/usr/share/stgit/templates/patchexport.tmpl') can be used for the
38 patch format. The following variables are supported in the template
39 file:
40
41 %(description)s - patch description
42 %(shortdescr)s - the first line of the patch description
43 %(longdescr)s - the rest of the patch description, after the first line
44 %(diffstat)s - the diff statistics
45 %(authname)s - author's name
46 %(authemail)s - author's e-mail
47 %(authdate)s - patch creation date
48 %(commname)s - committer's name
49 %(commemail)s - committer's e-mail"""
50
51 args = [argparse.patch_range(argparse.applied_patches,
52 argparse.unapplied_patches,
53 argparse.hidden_patches)]
54 options = [
55 opt('-d', '--dir', args = [argparse.dir],
56 short = 'Export patches to DIR instead of the default'),
57 opt('-p', '--patch', action = 'store_true',
58 short = 'Append .patch to the patch names'),
59 opt('-e', '--extension',
60 short = 'Append .EXTENSION to the patch names'),
61 opt('-n', '--numbered', action = 'store_true',
62 short = 'Prefix the patch names with order numbers'),
63 opt('-t', '--template', metavar = 'FILE', args = [argparse.files],
64 short = 'Use FILE as a template'),
65 opt('-b', '--branch', args = [argparse.stg_branches],
66 short = 'Use BRANCH instead of the default branch'),
67 opt('-s', '--stdout', action = 'store_true',
68 short = 'Dump the patches to the standard output'),
69 ] + argparse.diff_opts_option()
70
71 directory = common.DirectoryHasRepositoryLib()
72
73 def func(parser, options, args):
74 """Export a range of patches.
75 """
76 stack = directory.repository.get_stack(options.branch)
77
78 if options.dir:
79 dirname = options.dir
80 else:
81 dirname = 'patches-%s' % stack.name
82 directory.cd_to_topdir()
83
84 if not options.branch and git.local_changes():
85 out.warn('Local changes in the tree;'
86 ' you might want to commit them first')
87
88 if not options.stdout:
89 if not os.path.isdir(dirname):
90 os.makedirs(dirname)
91 series = file(os.path.join(dirname, 'series'), 'w+')
92
93 applied = stack.patchorder.applied
94 unapplied = stack.patchorder.unapplied
95 if len(args) != 0:
96 patches = common.parse_patches(args, applied + unapplied, len(applied))
97 else:
98 patches = applied
99
100 num = len(patches)
101 if num == 0:
102 raise common.CmdException, 'No patches applied'
103
104 zpadding = len(str(num))
105 if zpadding < 2:
106 zpadding = 2
107
108 # get the template
109 if options.template:
110 tmpl = file(options.template).read()
111 else:
112 tmpl = templates.get_template('patchexport.tmpl')
113 if not tmpl:
114 tmpl = ''
115
116 # note the base commit for this series
117 if not options.stdout:
118 base_commit = stack.patches.get(patches[0]).commit.sha1
119 print >> series, '# This series applies on GIT commit %s' % base_commit
120
121 patch_no = 1;
122 for p in patches:
123 pname = p
124 if options.patch:
125 pname = '%s.patch' % pname
126 elif options.extension:
127 pname = '%s.%s' % (pname, options.extension)
128 if options.numbered:
129 pname = '%s-%s' % (str(patch_no).zfill(zpadding), pname)
130 pfile = os.path.join(dirname, pname)
131 if not options.stdout:
132 print >> series, pname
133
134 # get the patch description
135 patch = stack.patches.get(p)
136 cd = patch.commit.data
137
138 descr = cd.message.strip()
139 descr_lines = descr.split('\n')
140
141 short_descr = descr_lines[0].rstrip()
142 long_descr = reduce(lambda x, y: x + '\n' + y,
143 descr_lines[1:], '').strip()
144
145 diff = stack.repository.diff_tree(cd.parent.data.tree, cd.tree, options.diff_flags)
146
147 tmpl_dict = {'description': descr,
148 'shortdescr': short_descr,
149 'longdescr': long_descr,
150 'diffstat': gitlib.diffstat(diff).rstrip(),
151 'authname': cd.author.name,
152 'authemail': cd.author.email,
153 'authdate': cd.author.date.isoformat(),
154 'commname': cd.committer.name,
155 'commemail': cd.committer.email}
156 for key in tmpl_dict:
157 if not tmpl_dict[key]:
158 tmpl_dict[key] = ''
159
160 try:
161 descr = tmpl % tmpl_dict
162 except KeyError, err:
163 raise common.CmdException, 'Unknown patch template variable: %s' \
164 % err
165 except TypeError:
166 raise common.CmdException, 'Only "%(name)s" variables are ' \
167 'supported in the patch template'
168
169 if options.stdout:
170 f = sys.stdout
171 else:
172 f = open(pfile, 'w+')
173
174 if options.stdout and num > 1:
175 print '-'*79
176 print patch.name
177 print '-'*79
178
179 f.write(descr)
180 f.write(diff)
181 if not options.stdout:
182 f.close()
183 patch_no += 1
184
185 if not options.stdout:
186 series.close()