| 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' |