chiark / gitweb /
Add a new option to "stg applied" and "stg unapplied" that provides a count
[stgit] / stgit / commands / uncommit.py
1 # -*- coding: utf-8 -*-
2
3 __copyright__ = """
4 Copyright (C) 2006, 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 import sys, os
21 from optparse import OptionParser, make_option
22
23 from stgit.commands.common import *
24 from stgit.utils import *
25 from stgit import stack, git
26
27 help = 'turn regular GIT commits into StGIT patches'
28 usage = """%prog [options] [<patchname1> [<patchname2> ... ]]
29
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'.
33
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.
37
38 The --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.
41
42 If no patch names are provided on the command line, StGIT
43 automatically generates them based on the first line of the patch
44 description.
45
46 Only commits with exactly one parent can be uncommitted; in other
47 words, you can't uncommit a merge."""
48
49 options = [make_option('-n', '--number', type = 'int',
50                        help = 'uncommit the specified number of commits')]
51
52 def func(parser, options, args):
53     """Uncommit a number of patches.
54     """
55     if options.number:
56         if options.number <= 0:
57             parser.error('invalid value passed to --number')
58
59         patch_nr = options.number
60
61         if len(args) == 0:
62             patchnames = None
63         elif len(args) == 1:
64             # prefix specified
65             patchnames = ['%s%d' % (args[0], i)
66                           for i in xrange(patch_nr, 0, -1)]
67         else:
68             parser.error('when using --number, specify at most one patch name')
69     elif len(args) == 0:
70         patchnames = None
71         patch_nr = 1
72     else:
73         patchnames = args
74         patch_nr = len(patchnames)
75
76     if crt_series.get_protected():
77         raise CmdException, \
78               'This branch is protected. Uncommit is not permitted'
79
80     print 'Uncommitting %d patches...' % patch_nr,
81     sys.stdout.flush()
82
83     base_file = crt_series.get_base_file()
84
85     for n in xrange(0, patch_nr):
86         # retrieve the commit (only commits with a single parent are allowed)
87         commit_id = read_string(base_file)
88         commit = git.Commit(commit_id)
89         try:
90             parent, = commit.get_parents()
91         except ValueError:
92             raise CmdException, 'Commit %s does not have exactly one parent' \
93                   % commit_id
94         author_name, author_email, author_date = \
95                      name_email_date(commit.get_author())
96
97         if patchnames:
98             patchname = patchnames[n]
99         else:
100             patchname = make_patch_name(commit.get_log(),
101                                         crt_series.patch_exists)
102
103         crt_series.new_patch(patchname,
104                              can_edit = False, before_existing = True,
105                              top = commit_id, bottom = parent,
106                              message = commit.get_log(),
107                              author_name = author_name,
108                              author_email = author_email,
109                              author_date = author_date)
110         write_string(base_file, parent)
111
112     print 'done'