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