chiark / gitweb /
c4c1cf8954502890d078f87ab10f57feb9810024
[stgit] / stgit / commands / coalesce.py
1 # -*- coding: utf-8 -*-
2
3 __copyright__ = """
4 Copyright (C) 2007, Karl Hasselström <kha@treskal.com>
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License version 2 as
8 published by the Free Software Foundation.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 """
19
20 from optparse import make_option
21 from stgit.out import *
22 from stgit import utils
23 from stgit.commands import common
24 from stgit.lib import git, transaction
25
26 help = 'coalesce two or more patches into one'
27 usage = """%prog [options] <patches>
28
29 Coalesce two or more patches, creating one big patch that contains all
30 their changes. The patches must all be applied, and must be
31 consecutive."""
32
33 directory = common.DirectoryHasRepositoryLib()
34 options = [make_option('-n', '--name', help = 'name of coalesced patch'),
35            make_option('-m', '--message',
36                        help = 'commit message of coalesced patch')]
37
38 def _coalesce(stack, name, msg, patches):
39     applied = stack.patchorder.applied
40
41     # Make sure the patches are consecutive.
42     applied_ix = dict((applied[i], i) for i in xrange(len(applied)))
43     ixes = list(sorted(applied_ix[p] for p in patches))
44     i0, i1 = ixes[0], ixes[-1]
45     if i1 - i0 + 1 != len(patches):
46         raise common.CmdException('The patches must be consecutive')
47
48     # Make a commit for the coalesced patch.
49     def bad_name(pn):
50         return pn not in patches and stack.patches.exists(pn)
51     if name and bad_name(name):
52         raise common.CmdException('Patch name "%s" already taken')
53     ps = [stack.patches.get(pn) for pn in applied[i0:i1+1]]
54     if msg == None:
55         msg = '\n\n'.join('%s\n\n%s' % (p.name.ljust(70, '-'),
56                                         p.commit.data.message)
57                           for p in ps)
58         msg = utils.edit_string(msg, '.stgit-coalesce.txt').strip()
59     if not name:
60         name = utils.make_patch_name(msg, bad_name)
61     cd = git.Commitdata(tree = ps[-1].commit.data.tree,
62                         parents = ps[0].commit.data.parents, message = msg)
63
64     # Rewrite refs.
65     trans = transaction.StackTransaction(stack, 'stg coalesce')
66     for pn in applied[i0:i1+1]:
67         trans.patches[pn] = None
68     parent = trans.patches[name] = stack.repository.commit(cd)
69     trans.applied = applied[:i0]
70     trans.applied.append(name)
71     for pn in applied[i1+1:]:
72         p = stack.patches.get(pn)
73         parent = trans.patches[pn] = stack.repository.commit(
74             p.commit.data.set_parent(parent))
75         trans.applied.append(pn)
76     trans.run()
77
78 def func(parser, options, args):
79     stack = directory.repository.current_stack
80     applied = set(stack.patchorder.applied)
81     patches = set(common.parse_patches(args, list(stack.patchorder.applied)))
82     if len(patches) < 2:
83         raise common.CmdException('Need at least two patches')
84     _coalesce(stack, options.name, options.message, patches)