Add "stg coalesce"
[stgit] / stgit / commands / coalesce.py
CommitLineData
48b209cd
KH
1# -*- coding: utf-8 -*-
2
3__copyright__ = """
4Copyright (C) 2007, Karl Hasselström <kha@treskal.com>
5
6This program is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License version 2 as
8published by the Free Software Foundation.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18"""
19
20from optparse import make_option
21from stgit.out import *
22from stgit import utils
23from stgit.commands import common
24from stgit.lib import git, transaction
25
26help = 'coalesce two or more patches into one'
27usage = """%prog [options] <patches>
28
29Coalesce two or more patches, creating one big patch that contains all
30their changes. The patches must all be applied, and must be
31consecutive."""
32
33directory = common.DirectoryHasRepositoryLib()
34options = [make_option('-n', '--name', help = 'name of coalesced patch'),
35 make_option('-m', '--message',
36 help = 'commit message of coalesced patch')]
37
38def _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
78def 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)