asciidoc.conf: Steal updates from git
[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
575bbdae 20from stgit.argparse import opt
48b209cd 21from stgit.out import *
20a52e06 22from stgit import argparse, utils
48b209cd
KH
23from stgit.commands import common
24from stgit.lib import git, transaction
25
575bbdae
KH
26help = 'Coalesce two or more patches into one'
27usage = ['[options] <patches>']
28description = """
48b209cd 29Coalesce two or more patches, creating one big patch that contains all
dcd32afa
KH
30their changes.
31
32If there are conflicts when reordering the patches to match the order
33you specify, you will have to resolve them manually just as if you had
34done a sequence of pushes and pops yourself."""
48b209cd 35
575bbdae 36options = [opt('-n', '--name', short = 'Name of coalesced patch')
20a52e06 37 ] + argparse.message_options()
48b209cd 38
575bbdae
KH
39directory = common.DirectoryHasRepositoryLib()
40
d568af72
KH
41class SaveTemplateDone(Exception):
42 pass
43
44def _coalesce_patches(trans, patches, msg, save_template):
dcd32afa 45 cd = trans.patches[patches[0]].data
f5f22afe 46 cd = git.CommitData(tree = cd.tree, parents = cd.parents)
dcd32afa
KH
47 for pn in patches[1:]:
48 c = trans.patches[pn]
49 tree = trans.stack.repository.simple_merge(
50 base = c.data.parent.data.tree,
51 ours = cd.tree, theirs = c.data.tree)
52 if not tree:
53 return None
54 cd = cd.set_tree(tree)
55 if msg == None:
56 msg = '\n\n'.join('%s\n\n%s' % (pn.ljust(70, '-'),
57 trans.patches[pn].data.message)
58 for pn in patches)
d568af72
KH
59 if save_template:
60 save_template(msg)
61 raise SaveTemplateDone()
62 else:
63 msg = utils.edit_string(msg, '.stgit-coalesce.txt').strip()
dcd32afa
KH
64 cd = cd.set_message(msg)
65
66 return cd
48b209cd 67
d568af72 68def _coalesce(stack, iw, name, msg, save_template, patches):
48b209cd 69
dcd32afa 70 # If a name was supplied on the command line, make sure it's OK.
48b209cd
KH
71 def bad_name(pn):
72 return pn not in patches and stack.patches.exists(pn)
dcd32afa
KH
73 def get_name(cd):
74 return name or utils.make_patch_name(cd.message, bad_name)
48b209cd
KH
75 if name and bad_name(name):
76 raise common.CmdException('Patch name "%s" already taken')
48b209cd 77
dcd32afa
KH
78 def make_coalesced_patch(trans, new_commit_data):
79 name = get_name(new_commit_data)
80 trans.patches[name] = stack.repository.commit(new_commit_data)
81 trans.unapplied.insert(0, name)
82
781e549a
KH
83 trans = transaction.StackTransaction(stack, 'coalesce',
84 allow_conflicts = True)
dcd32afa 85 push_new_patch = bool(set(patches) & set(trans.applied))
dcd32afa 86 try:
d568af72 87 new_commit_data = _coalesce_patches(trans, patches, msg, save_template)
dcd32afa
KH
88 if new_commit_data:
89 # We were able to construct the coalesced commit
90 # automatically. So just delete its constituent patches.
91 to_push = trans.delete_patches(lambda pn: pn in patches)
92 else:
93 # Automatic construction failed. So push the patches
94 # consecutively, so that a second construction attempt is
95 # guaranteed to work.
96 to_push = trans.pop_patches(lambda pn: pn in patches)
97 for pn in patches:
98 trans.push_patch(pn, iw)
d568af72
KH
99 new_commit_data = _coalesce_patches(trans, patches, msg,
100 save_template)
dcd32afa
KH
101 assert not trans.delete_patches(lambda pn: pn in patches)
102 make_coalesced_patch(trans, new_commit_data)
103
104 # Push the new patch if necessary, and any unrelated patches we've
105 # had to pop out of the way.
106 if push_new_patch:
107 trans.push_patch(get_name(new_commit_data), iw)
108 for pn in to_push:
109 trans.push_patch(pn, iw)
d568af72
KH
110 except SaveTemplateDone:
111 trans.abort(iw)
112 return
dcd32afa
KH
113 except transaction.TransactionHalted:
114 pass
f9cc5e69 115 return trans.run(iw)
48b209cd
KH
116
117def func(parser, options, args):
118 stack = directory.repository.current_stack
a5920051 119 patches = common.parse_patches(args, list(stack.patchorder.all))
48b209cd
KH
120 if len(patches) < 2:
121 raise common.CmdException('Need at least two patches')
a0848ecf 122 return _coalesce(stack, stack.repository.default_iw, options.name,
f9cc5e69 123 options.message, options.save_template, patches)