Auto-generate man pages for all StGit commands
[stgit] / stgit / commands / export.py
CommitLineData
fcee87cf
CM
1"""Export command
2"""
3
4__copyright__ = """
5Copyright (C) 2005, Catalin Marinas <catalin.marinas@gmail.com>
6
7This program is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License version 2 as
9published by the Free Software Foundation.
10
11This program is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with this program; if not, write to the Free Software
18Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19"""
20
3f19450c 21import os
575bbdae 22from stgit.argparse import opt
3f19450c 23from stgit.commands import common
20a52e06 24from stgit import argparse, git, templates
3f19450c
CM
25from stgit.out import out
26from stgit.lib import git as gitlib
fcee87cf 27
575bbdae
KH
28help = 'Export patches to a directory'
29usage = ['[options] [<patch1>] [<patch2>] [<patch3>..<patch4>]']
30description = """
8560c67b
CM
31Export a range of applied patches to a given directory (defaults to
32'patches-<branch>') in a standard unified GNU diff format. A template
33file (defaulting to '.git/patchexport.tmpl' or
94d18868
YD
34'~/.stgit/templates/patchexport.tmpl' or
35'/usr/share/stgit/templates/patchexport.tmpl') can be used for the
23a88c7d
CM
36patch format. The following variables are supported in the template
37file:
26aab5b0
CM
38
39 %(description)s - patch description
99e73103
CM
40 %(shortdescr)s - the first line of the patch description
41 %(longdescr)s - the rest of the patch description, after the first line
26aab5b0
CM
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
575bbdae
KH
47 %(commemail)s - committer's e-mail"""
48
49options = [
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()
26aab5b0 65
3f19450c 66directory = common.DirectoryHasRepositoryLib()
fcee87cf
CM
67
68def func(parser, options, args):
8560c67b
CM
69 """Export a range of patches.
70 """
3f19450c
CM
71 stack = directory.repository.get_stack(options.branch)
72
8560c67b
CM
73 if options.dir:
74 dirname = options.dir
fcee87cf 75 else:
3f19450c 76 dirname = 'patches-%s' % stack.name
7b601c9e 77 directory.cd_to_topdir()
fcee87cf 78
2f7c8b0b 79 if not options.branch and git.local_changes():
27ac2b7e
KH
80 out.warn('Local changes in the tree;'
81 ' you might want to commit them first')
fcee87cf 82
1fceece7
CM
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+')
fcee87cf 87
3f19450c
CM
88 applied = stack.patchorder.applied
89 unapplied = stack.patchorder.unapplied
8560c67b 90 if len(args) != 0:
06f9bd6b 91 patches = common.parse_patches(args, applied + unapplied, len(applied))
fcee87cf 92 else:
6b1e0111 93 patches = applied
fcee87cf
CM
94
95 num = len(patches)
16ad223e 96 if num == 0:
4675b4bb 97 raise common.CmdException, 'No patches applied'
16ad223e 98
fcee87cf
CM
99 zpadding = len(str(num))
100 if zpadding < 2:
101 zpadding = 2
102
23a88c7d
CM
103 # get the template
104 if options.template:
1f3bb017 105 tmpl = file(options.template).read()
23a88c7d 106 else:
1f3bb017
CM
107 tmpl = templates.get_template('patchexport.tmpl')
108 if not tmpl:
109 tmpl = ''
23a88c7d 110
6c4e4b68 111 # note the base commit for this series
1fceece7 112 if not options.stdout:
3f19450c 113 base_commit = stack.patches.get(patches[0]).commit.sha1
1fceece7 114 print >> series, '# This series applies on GIT commit %s' % base_commit
6c4e4b68 115
fcee87cf
CM
116 patch_no = 1;
117 for p in patches:
118 pname = p
8560c67b 119 if options.patch:
099ff6cd 120 pname = '%s.patch' % pname
8560c67b
CM
121 elif options.extension:
122 pname = '%s.%s' % (pname, options.extension)
fcee87cf
CM
123 if options.numbered:
124 pname = '%s-%s' % (str(patch_no).zfill(zpadding), pname)
125 pfile = os.path.join(dirname, pname)
1fceece7
CM
126 if not options.stdout:
127 print >> series, pname
fcee87cf 128
fcee87cf 129 # get the patch description
3f19450c
CM
130 patch = stack.patches.get(p)
131 cd = patch.commit.data
fcee87cf 132
3f19450c 133 descr = cd.message.strip()
99e73103
CM
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
3f19450c
CM
140 diff = stack.repository.diff_tree(cd.parent.data.tree, cd.tree, options.diff_flags)
141
142 tmpl_dict = {'description': descr,
99e73103
CM
143 'shortdescr': short_descr,
144 'longdescr': long_descr,
3f19450c
CM
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}
fcee87cf
CM
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:
4675b4bb 158 raise common.CmdException, 'Unknown patch template variable: %s' \
fcee87cf
CM
159 % err
160 except TypeError:
4675b4bb 161 raise common.CmdException, 'Only "%(name)s" variables are ' \
fcee87cf 162 'supported in the patch template'
fcee87cf 163
1fceece7
CM
164 if options.stdout:
165 f = sys.stdout
166 else:
167 f = open(pfile, 'w+')
168
169 if options.stdout and num > 1:
27ac2b7e 170 print '-'*79
3f19450c 171 print patch.name
27ac2b7e 172 print '-'*79
1fceece7 173
1fceece7 174 f.write(descr)
a45cea15 175 f.write(diff)
1fceece7
CM
176 if not options.stdout:
177 f.close()
fcee87cf
CM
178 patch_no += 1
179
1fceece7
CM
180 if not options.stdout:
181 series.close()