From: Karl Hasselström Date: Sun, 21 Sep 2008 12:17:41 +0000 (+0200) Subject: New command: stg undo X-Git-Tag: v0.15-rc1~141 X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~mdw/git/stgit/commitdiff_plain/069a1e9574171e5070c0ea9c7055bf985e08a64a New command: stg undo Basically, this is just a user-friendly way to access a subset of the functionality of "stg reset". Signed-off-by: Karl Hasselström --- diff --git a/stgit/commands/undo.py b/stgit/commands/undo.py new file mode 100644 index 0000000..4f782a0 --- /dev/null +++ b/stgit/commands/undo.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- + +__copyright__ = """ +Copyright (C) 2008, Karl Hasselström + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License version 2 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +""" + +from stgit.argparse import opt +from stgit.commands import common +from stgit.lib import git, log, transaction +from stgit.out import out + +help = 'Undo the last operation' +kind = 'stack' +usage = [''] +description = """ +Reset the patch stack to the previous state. Consecutive invocations +of "stg undo" will take you ever further into the past.""" + +options = [ + opt('-n', '--number', type = 'int', metavar = 'N', default = 1, + short = 'Undo the last N commands'), + opt('--hard', action = 'store_true', + short = 'Discard changes in your index/worktree')] + +directory = common.DirectoryHasRepositoryLib() + +def func(parser, options, args): + stack = directory.repository.current_stack + if options.number < 1: + raise common.CmdException('Bad number of commands to undo') + state = log.undo_state(stack, options.number) + trans = transaction.StackTransaction(stack, 'undo %d' % options.number, + discard_changes = options.hard) + try: + log.reset_stack(trans, stack.repository.default_iw, state, []) + except transaction.TransactionHalted: + pass + return trans.run(stack.repository.default_iw) diff --git a/stgit/lib/log.py b/stgit/lib/log.py index 43f87ce..8b7d2e4 100644 --- a/stgit/lib/log.py +++ b/stgit/lib/log.py @@ -99,6 +99,7 @@ The simplified log is exactly like the full log, except that its only parent is the (simplified) previous log entry, if any. It's purpose is mainly ease of visualization.""" +import re from stgit.lib import git, stack as libstack from stgit import exception, utils from stgit.out import out @@ -157,6 +158,12 @@ class LogEntry(object): return self.patches[self.applied[0]].data.parent else: return self.head + @property + def top(self): + if self.applied: + return self.patches[self.applied[-1]] + else: + return self.head @classmethod def from_stack(cls, prev, stack, message): return cls( @@ -447,3 +454,30 @@ def reset_stack(trans, iw, state, only_patches): else: # Recreate the exact order specified by the goal state. trans.reorder_patches(state.applied, state.unapplied, state.hidden, iw) + +def undo_state(stack, undo_steps): + """Find the log entry C{undo_steps} steps in the past. (Successive + undo operations are supposed to "add up", so if we find other undo + operations along the way we have to add those undo steps to + C{undo_steps}.) + + @return: The log entry that is the destination of the undo + operation + @rtype: L{LogEntry}""" + ref = log_ref(stack.name) + try: + commit = stack.repository.refs.get(ref) + except KeyError: + raise LogException('Log is empty') + log = get_log_entry(stack.repository, ref, commit) + while undo_steps > 0: + msg = log.message.strip() + m = re.match(r'^undo\s+(\d+)$', msg) + if m: + undo_steps += int(m.group(1)) + else: + undo_steps -= 1 + if not log.prev: + raise LogException('Not enough undo information available') + log = log.prev + return log diff --git a/t/t3102-undo.sh b/t/t3102-undo.sh new file mode 100755 index 0000000..9373522 --- /dev/null +++ b/t/t3102-undo.sh @@ -0,0 +1,81 @@ +#!/bin/sh + +test_description='Simple test cases for "stg undo"' + +. ./test-lib.sh + +# Ignore our own output files. +cat > .git/info/exclude <> a && + git add a && + git commit -m a && + echo 111 >> a && + git commit -a -m p1 && + echo 222 >> a && + git commit -a -m p2 && + echo 333 >> a && + git commit -a -m p3 && + stg uncommit -n 3 && + stg pop +' + +cat > expected.txt < expected.txt < expected.txt < expected.txt < expected.txt < .git/info/exclude <> a && + git add a && + git commit -m a && + echo 111 >> a && + git commit -a -m p1 && + echo 222 >> a && + git commit -a -m p2 && + echo 333 >> a && + git commit -a -m p3 && + stg uncommit -n 3 +' + +cat > expected.txt < actual.txt && + test_cmp expected.txt actual.txt && + test "$(echo $(stg series))" = "+ p1 > p3 - p2" +' + +test_expect_success 'Try to undo without --hard' ' + command_error stg undo && + stg status a > actual.txt && + test_cmp expected.txt actual.txt && + test "$(echo $(stg series))" = "+ p1 > p3 - p2" +' + +cat > expected.txt < actual.txt && + test_cmp expected.txt actual.txt && + test "$(echo $(stg series))" = "> p1 - p3 - p2" +' + +test_done