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