chiark / gitweb /
Print conflict details with the new infrastructure (bug #11181)
[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()