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