StGit: export: fix base commit reporting in series file
[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
b9756849 22import sys
575bbdae 23from stgit.argparse import opt
3f19450c 24from stgit.commands import common
20a52e06 25from stgit import argparse, git, templates
3f19450c
CM
26from stgit.out import out
27from stgit.lib import git as gitlib
fcee87cf 28
575bbdae 29help = 'Export patches to a directory'
33ff9cdd 30kind = 'patch'
54239abf 31usage = ['[options] [--] [<patch1>] [<patch2>] [<patch3>..<patch4>]']
575bbdae 32description = """
8560c67b
CM
33Export a range of applied patches to a given directory (defaults to
34'patches-<branch>') in a standard unified GNU diff format. A template
35file (defaulting to '.git/patchexport.tmpl' or
94d18868
YD
36'~/.stgit/templates/patchexport.tmpl' or
37'/usr/share/stgit/templates/patchexport.tmpl') can be used for the
23a88c7d
CM
38patch format. The following variables are supported in the template
39file:
26aab5b0
CM
40
41 %(description)s - patch description
99e73103
CM
42 %(shortdescr)s - the first line of the patch description
43 %(longdescr)s - the rest of the patch description, after the first line
26aab5b0
CM
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
575bbdae
KH
49 %(commemail)s - committer's e-mail"""
50
6c8a90e1
KH
51args = [argparse.patch_range(argparse.applied_patches,
52 argparse.unapplied_patches,
53 argparse.hidden_patches)]
575bbdae 54options = [
6c8a90e1 55 opt('-d', '--dir', args = [argparse.dir],
575bbdae
KH
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'),
6c8a90e1 63 opt('-t', '--template', metavar = 'FILE', args = [argparse.files],
575bbdae 64 short = 'Use FILE as a template'),
6c8a90e1 65 opt('-b', '--branch', args = [argparse.stg_branches],
575bbdae
KH
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()
26aab5b0 70
3f19450c 71directory = common.DirectoryHasRepositoryLib()
fcee87cf
CM
72
73def func(parser, options, args):
8560c67b
CM
74 """Export a range of patches.
75 """
3f19450c
CM
76 stack = directory.repository.get_stack(options.branch)
77
8560c67b
CM
78 if options.dir:
79 dirname = options.dir
fcee87cf 80 else:
3f19450c 81 dirname = 'patches-%s' % stack.name
7b601c9e 82 directory.cd_to_topdir()
fcee87cf 83
2f7c8b0b 84 if not options.branch and git.local_changes():
27ac2b7e
KH
85 out.warn('Local changes in the tree;'
86 ' you might want to commit them first')
fcee87cf 87
1fceece7
CM
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+')
fcee87cf 92
3f19450c
CM
93 applied = stack.patchorder.applied
94 unapplied = stack.patchorder.unapplied
8560c67b 95 if len(args) != 0:
06f9bd6b 96 patches = common.parse_patches(args, applied + unapplied, len(applied))
fcee87cf 97 else:
6b1e0111 98 patches = applied
fcee87cf
CM
99
100 num = len(patches)
16ad223e 101 if num == 0:
4675b4bb 102 raise common.CmdException, 'No patches applied'
16ad223e 103
fcee87cf
CM
104 zpadding = len(str(num))
105 if zpadding < 2:
106 zpadding = 2
107
23a88c7d
CM
108 # get the template
109 if options.template:
1f3bb017 110 tmpl = file(options.template).read()
23a88c7d 111 else:
1f3bb017
CM
112 tmpl = templates.get_template('patchexport.tmpl')
113 if not tmpl:
114 tmpl = ''
23a88c7d 115
6c4e4b68 116 # note the base commit for this series
1fceece7 117 if not options.stdout:
fa3f66e9 118 base_commit = stack.base.sha1
1fceece7 119 print >> series, '# This series applies on GIT commit %s' % base_commit
6c4e4b68 120
fcee87cf
CM
121 patch_no = 1;
122 for p in patches:
123 pname = p
8560c67b 124 if options.patch:
099ff6cd 125 pname = '%s.patch' % pname
8560c67b
CM
126 elif options.extension:
127 pname = '%s.%s' % (pname, options.extension)
fcee87cf
CM
128 if options.numbered:
129 pname = '%s-%s' % (str(patch_no).zfill(zpadding), pname)
130 pfile = os.path.join(dirname, pname)
1fceece7
CM
131 if not options.stdout:
132 print >> series, pname
fcee87cf 133
fcee87cf 134 # get the patch description
3f19450c
CM
135 patch = stack.patches.get(p)
136 cd = patch.commit.data
fcee87cf 137
3f19450c 138 descr = cd.message.strip()
99e73103
CM
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
3f19450c
CM
145 diff = stack.repository.diff_tree(cd.parent.data.tree, cd.tree, options.diff_flags)
146
147 tmpl_dict = {'description': descr,
99e73103
CM
148 'shortdescr': short_descr,
149 'longdescr': long_descr,
ef954fe6 150 'diffstat': gitlib.diffstat(diff).rstrip(),
3f19450c
CM
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}
fcee87cf
CM
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:
4675b4bb 163 raise common.CmdException, 'Unknown patch template variable: %s' \
fcee87cf
CM
164 % err
165 except TypeError:
4675b4bb 166 raise common.CmdException, 'Only "%(name)s" variables are ' \
fcee87cf 167 'supported in the patch template'
fcee87cf 168
1fceece7
CM
169 if options.stdout:
170 f = sys.stdout
171 else:
172 f = open(pfile, 'w+')
173
174 if options.stdout and num > 1:
27ac2b7e 175 print '-'*79
3f19450c 176 print patch.name
27ac2b7e 177 print '-'*79
1fceece7 178
1fceece7 179 f.write(descr)
a45cea15 180 f.write(diff)
1fceece7
CM
181 if not options.stdout:
182 f.close()
fcee87cf
CM
183 patch_no += 1
184
1fceece7
CM
185 if not options.stdout:
186 series.close()