1 # -*- coding: utf-8 -*-
4 Copyright (C) 2006, Karl Hasselström <kha@treskal.com>
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.
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.
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
21 from optparse import OptionParser, make_option
23 from stgit.commands.common import *
24 from stgit.utils import *
25 from stgit import stack, git
27 help = 'turn regular GIT commits into StGIT patches'
28 usage = """%prog [<patchnames>] | -n NUM [<prefix>]] | -t <committish>
30 Take one or more git commits at the base of the current stack and turn
31 them into StGIT patches. The new patches are created as applied patches
32 at the bottom of the stack. This is the exact opposite of 'stg commit'.
34 By default, the number of patches to uncommit is determined by the
35 number of patch names provided on the command line. First name is used
36 for the first patch to uncommit, i.e. for the newest patch.
38 The -n/--number option specifies the number of patches to uncommit. In
39 this case, at most one patch name may be specified. It is used as
40 prefix to which the patch number is appended. If no patch names are
41 provided on the command line, StGIT automatically generates them based
42 on the first line of the patch description.
44 The -t/--to option specifies that all commits up to and including the
45 given commit should be uncommitted.
47 Only commits with exactly one parent can be uncommitted; in other
48 words, you can't uncommit a merge."""
50 options = [make_option('-n', '--number', type = 'int',
51 help = 'uncommit the specified number of commits'),
52 make_option('-t', '--to',
53 help = 'uncommit to the specified commit')]
55 def func(parser, options, args):
56 """Uncommit a number of patches.
60 parser.error('cannot give both --to and --number')
62 parser.error('cannot specify patch name with --to')
63 patch_nr = patchnames = None
64 to_commit = git.rev_parse(options.to)
66 if options.number <= 0:
67 parser.error('invalid value passed to --number')
69 patch_nr = options.number
75 patchnames = ['%s%d' % (args[0], i)
76 for i in xrange(patch_nr, 0, -1)]
78 parser.error('when using --number, specify at most one patch name')
84 patch_nr = len(patchnames)
86 if crt_series.get_protected():
88 'This branch is protected. Uncommit is not permitted'
90 def get_commit(commit_id):
91 commit = git.Commit(commit_id)
93 parent, = commit.get_parents()
95 raise CmdException('Commit %s does not have exactly one parent'
97 return (commit, commit_id, parent)
100 next_commit = crt_series.get_base()
102 print 'Uncommitting %d patches...' % patch_nr,
103 for i in xrange(patch_nr):
104 commit, commit_id, parent = get_commit(next_commit)
105 commits.append((commit, commit_id, parent))
108 print 'Uncommitting to %s...' % to_commit
110 commit, commit_id, parent = get_commit(next_commit)
111 commits.append((commit, commit_id, parent))
112 if commit_id == to_commit:
115 patch_nr = len(commits)
118 for (commit, commit_id, parent), patchname in \
119 zip(commits, patchnames or [None for i in xrange(len(commits))]):
120 author_name, author_email, author_date = \
121 name_email_date(commit.get_author())
122 crt_series.new_patch(patchname,
123 can_edit = False, before_existing = True,
124 top = commit_id, bottom = parent,
125 message = commit.get_log(),
126 author_name = author_name,
127 author_email = author_email,
128 author_date = author_date)