All of lore.kernel.org
 help / color / mirror / Atom feed
* [StGit PATCH 0/2] stg undo
@ 2008-05-09  0:44 Karl Hasselström
  2008-05-09  0:44 ` [StGit PATCH 1/2] Move stack reset function to a shared location Karl Hasselström
  2008-05-09  0:44 ` [StGit PATCH 2/2] New command: stg undo Karl Hasselström
  0 siblings, 2 replies; 3+ messages in thread
From: Karl Hasselström @ 2008-05-09  0:44 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: git

Here's a fully general undo command for StGit. It uses the same
machinery as the "stg reset" I posted previously, but is more
convenient to use when all you want to do is undo the last operation.

The next step would be to remove all traces of "push --undo" et al.,
but I'll save that for another day.

For trying this out, pull from

  git://repo.or.cz/stgit/kha.git experimental

since a few of the patches below these two have been updated as well.
(For example, the log branch in <branchname>.stgit^ is now much more
human-readable.)

---

Karl Hasselström (2):
      New command: stg undo
      Move stack reset function to a shared location


 stgit/commands/reset.py  |   70 ++++---------------------------------
 stgit/commands/undo.py   |   49 ++++++++++++++++++++++++++
 stgit/lib/log.py         |   76 +++++++++++++++++++++++++++++++++++++++++
 stgit/lib/transaction.py |    1 +
 stgit/main.py            |    2 +
 t/t3102-undo.sh          |   86 ++++++++++++++++++++++++++++++++++++++++++++++
 t/t3103-undo-hard.sh     |   56 ++++++++++++++++++++++++++++++
 7 files changed, 277 insertions(+), 63 deletions(-)
 create mode 100644 stgit/commands/undo.py
 create mode 100755 t/t3102-undo.sh
 create mode 100755 t/t3103-undo-hard.sh

-- 
Karl Hasselström, kha@treskal.com
      www.treskal.com/kalle

^ permalink raw reply	[flat|nested] 3+ messages in thread

* [StGit PATCH 1/2] Move stack reset function to a shared location
  2008-05-09  0:44 [StGit PATCH 0/2] stg undo Karl Hasselström
@ 2008-05-09  0:44 ` Karl Hasselström
  2008-05-09  0:44 ` [StGit PATCH 2/2] New command: stg undo Karl Hasselström
  1 sibling, 0 replies; 3+ messages in thread
From: Karl Hasselström @ 2008-05-09  0:44 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: git

Move reset_stack() from commands/reset.py to lib/log.py, so that more
commands besides reset can use it. (No such commands exist currently,
but undo and redo will use it.)

Signed-off-by: Karl Hasselström <kha@treskal.com>

---

 stgit/commands/reset.py  |   70 +++++-----------------------------------------
 stgit/lib/log.py         |   57 +++++++++++++++++++++++++++++++++++++
 stgit/lib/transaction.py |    1 +
 3 files changed, 65 insertions(+), 63 deletions(-)


diff --git a/stgit/commands/reset.py b/stgit/commands/reset.py
index bcc4c6a..2bbac8b 100644
--- a/stgit/commands/reset.py
+++ b/stgit/commands/reset.py
@@ -45,67 +45,6 @@ directory = common.DirectoryHasRepositoryLib()
 options = [make_option('--hard', action = 'store_true',
                        help = 'discard changes in your index/worktree')]
 
-def reset_stack(stack, iw, state, only_patches, hard):
-    only_patches = set(only_patches)
-    def mask(s):
-        if only_patches:
-            return s & only_patches
-        else:
-            return s
-    patches_to_reset = mask(set(state.applied + state.unapplied))
-    existing_patches = set(stack.patchorder.all)
-    to_delete = mask(existing_patches - patches_to_reset)
-    trans = transaction.StackTransaction(stack, 'reset', discard_changes = hard)
-
-    # If we have to change the stack base, we need to pop all patches
-    # first.
-    if not only_patches and trans.base != state.base:
-        trans.pop_patches(lambda pn: True)
-        out.info('Setting stack base to %s' % state.base.sha1)
-        trans.base = state.base
-
-    # In one go, do all the popping we have to in order to pop the
-    # patches we're going to delete or modify.
-    def mod(pn):
-        if only_patches and not pn in only_patches:
-            return False
-        if pn in to_delete:
-            return True
-        if stack.patches.get(pn).commit != state.patches.get(pn, None):
-            return True
-        return False
-    trans.pop_patches(mod)
-
-    # Delete and modify/create patches. We've previously popped all
-    # patches that we touch in this step.
-    trans.delete_patches(lambda pn: pn in to_delete)
-    for pn in patches_to_reset:
-        if pn in existing_patches:
-            if trans.patches[pn] == state.patches[pn]:
-                continue
-            else:
-                out.info('Resetting %s' % pn)
-        else:
-            trans.unapplied.append(pn)
-            out.info('Resurrecting %s' % pn)
-        trans.patches[pn] = state.patches[pn]
-
-    # Push/pop patches as necessary.
-    try:
-        if only_patches:
-            # Push all the patches that we've popped, if they still
-            # exist.
-            pushable = set(trans.unapplied)
-            for pn in stack.patchorder.applied:
-                if pn in pushable:
-                    trans.push_patch(pn, iw)
-        else:
-            # Recreate the exact order specified by the goal state.
-            trans.reorder_patches(state.applied, state.unapplied, iw)
-    except transaction.TransactionHalted:
-        pass
-    return trans.run(iw)
-
 def func(parser, options, args):
     stack = directory.repository.current_stack
     if len(args) >= 1:
@@ -113,5 +52,10 @@ def func(parser, options, args):
         state = log.Log(stack.repository, ref, stack.repository.rev_parse(ref))
     else:
         raise common.CmdException('Wrong number of arguments')
-    return reset_stack(stack, stack.repository.default_iw, state, patches,
-                       options.hard)
+    trans = transaction.StackTransaction(stack, 'reset',
+                                         discard_changes = options.hard)
+    try:
+        log.reset_stack(trans, stack.repository.default_iw, state, patches)
+    except transaction.TransactionHalted:
+        pass
+    return trans.run(stack.repository.default_iw)
diff --git a/stgit/lib/log.py b/stgit/lib/log.py
index e9e89ff..dfc2b9f 100644
--- a/stgit/lib/log.py
+++ b/stgit/lib/log.py
@@ -180,3 +180,60 @@ def copy_log(repo, src_branch, dst_branch, msg):
 
 def default_repo():
     return stack.Repository.default()
+
+def reset_stack(trans, iw, state, only_patches):
+    only_patches = set(only_patches)
+    def mask(s):
+        if only_patches:
+            return s & only_patches
+        else:
+            return s
+    patches_to_reset = mask(set(state.applied + state.unapplied))
+    existing_patches = set(trans.all_patches)
+    original_applied_order = list(trans.applied)
+    to_delete = mask(existing_patches - patches_to_reset)
+
+    # If we have to change the stack base, we need to pop all patches
+    # first.
+    if not only_patches and trans.base != state.base:
+        trans.pop_patches(lambda pn: True)
+        out.info('Setting stack base to %s' % state.base.sha1)
+        trans.base = state.base
+
+    # In one go, do all the popping we have to in order to pop the
+    # patches we're going to delete or modify.
+    def mod(pn):
+        if only_patches and not pn in only_patches:
+            return False
+        if pn in to_delete:
+            return True
+        if trans.patches[pn] != state.patches.get(pn, None):
+            return True
+        return False
+    trans.pop_patches(mod)
+
+    # Delete and modify/create patches. We've previously popped all
+    # patches that we touch in this step.
+    trans.delete_patches(lambda pn: pn in to_delete)
+    for pn in patches_to_reset:
+        if pn in existing_patches:
+            if trans.patches[pn] == state.patches[pn]:
+                continue
+            else:
+                out.info('Resetting %s' % pn)
+        else:
+            trans.unapplied.append(pn)
+            out.info('Resurrecting %s' % pn)
+        trans.patches[pn] = state.patches[pn]
+
+    # Push/pop patches as necessary.
+    if only_patches:
+        # Push all the patches that we've popped, if they still
+        # exist.
+        pushable = set(trans.unapplied)
+        for pn in original_applied_order:
+            if pn in pushable:
+                trans.push_patch(pn, iw)
+    else:
+        # Recreate the exact order specified by the goal state.
+        trans.reorder_patches(state.applied, state.unapplied, iw)
diff --git a/stgit/lib/transaction.py b/stgit/lib/transaction.py
index a008780..dd64eb6 100644
--- a/stgit/lib/transaction.py
+++ b/stgit/lib/transaction.py
@@ -60,6 +60,7 @@ class StackTransaction(object):
     def __set_unapplied(self, val):
         self.__unapplied = list(val)
     unapplied = property(lambda self: self.__unapplied, __set_unapplied)
+    all_patches = property(lambda self: self.__applied + self.__unapplied)
     def __set_base(self, val):
         assert (not self.__applied
                 or self.patches[self.applied[0]].data.parent == val)

^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [StGit PATCH 2/2] New command: stg undo
  2008-05-09  0:44 [StGit PATCH 0/2] stg undo Karl Hasselström
  2008-05-09  0:44 ` [StGit PATCH 1/2] Move stack reset function to a shared location Karl Hasselström
@ 2008-05-09  0:44 ` Karl Hasselström
  1 sibling, 0 replies; 3+ messages in thread
From: Karl Hasselström @ 2008-05-09  0:44 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: git

Basically, this is just a user-friendly way to access a subset of the
functionality of "stg reset".

Signed-off-by: Karl Hasselström <kha@treskal.com>

---

 stgit/commands/undo.py  |   38 +++++++--------------
 stgit/lib/log.py        |   19 ++++++++++
 stgit/main.py           |    2 +
 t/t3102-undo.sh         |   86 +++++++++++++++++++++++++++++++++++++++++++++++
 t/t3103-undo-hard.sh    |   10 +++--
 5 files changed, 125 insertions(+), 30 deletions(-)
 copy stgit/commands/{reset.py => undo.py} (58%)
 create mode 100755 t/t3102-undo.sh
 copy t/{t3101-reset-hard.sh => t3103-undo-hard.sh} (82%)


diff --git a/stgit/commands/reset.py b/stgit/commands/undo.py
similarity index 58%
copy from stgit/commands/reset.py
copy to stgit/commands/undo.py
index 2bbac8b..b1d7de9 100644
--- a/stgit/commands/reset.py
+++ b/stgit/commands/undo.py
@@ -22,40 +22,28 @@ from stgit.commands import common
 from stgit.lib import git, log, transaction
 from stgit.out import out
 
-help = 'reset the patch stack to an earlier state'
-usage = """%prog [options] <state> [<patchnames>]
+help = 'undo the last operation'
+usage = """%prog [options]
 
-Reset the patch stack to an earlier state. The state is specified with
-a commit from a stack log; for a branch foo, StGit stores the stack
-log in foo.stgit^. So to undo the last N StGit commands, you would say
-
-  stg reset foo.stgit^~N
-
-or, if you are not sure how many steps to undo, you can view the log
-with "git log" or gitk
-
-  gitk foo.stgit^
-
-and then reset to any sha1 you see in the log.
-
-If one or more patch names are given, reset only those patches, and
-leave the rest alone."""
+Reset the patch stack to the previous state. Consecutive invocations
+of "stg undo" will take you ever further into the past."""
 
 directory = common.DirectoryHasRepositoryLib()
-options = [make_option('--hard', action = 'store_true',
+options = [make_option('-n', '--number', type = 'int', metavar = 'N',
+                       default = 1,
+                       help = 'undo the last N commands'),
+           make_option('--hard', action = 'store_true',
                        help = 'discard changes in your index/worktree')]
 
 def func(parser, options, args):
     stack = directory.repository.current_stack
-    if len(args) >= 1:
-        ref, patches = args[0], args[1:]
-        state = log.Log(stack.repository, ref, stack.repository.rev_parse(ref))
-    else:
-        raise common.CmdException('Wrong number of arguments')
-    trans = transaction.StackTransaction(stack, 'reset',
+    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, patches)
+        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 dfc2b9f..6d9fc6e 100644
--- a/stgit/lib/log.py
+++ b/stgit/lib/log.py
@@ -1,3 +1,4 @@
+import re
 from stgit.lib import git, stack
 from stgit import exception
 from stgit.out import out
@@ -155,6 +156,9 @@ class Log(object):
             self.base = self.patches[self.applied[0]].data.parent
         else:
             self.base = self.head
+    @property
+    def parents(self):
+        return self.commit.data.parents[2:]
 
 class FullLog(Log):
     full_log = property(lambda self: self.commit)
@@ -237,3 +241,18 @@ 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, iw)
+
+def undo_state(stack, undo_steps):
+    ref = log_ref(stack.name)
+    log = Log(stack.repository, ref, stack.repository.refs.get(ref))
+    while undo_steps > 0:
+        msg = log.commit.data.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.parents:
+            raise LogException('Not enough undo information available')
+        log = Log(stack.repository, log.parents[0].sha1, log.parents[0])
+    return log
diff --git a/stgit/main.py b/stgit/main.py
index 83e6b08..cf7b404 100644
--- a/stgit/main.py
+++ b/stgit/main.py
@@ -99,6 +99,7 @@ commands = Commands({
     'top':              'top',
     'unapplied':        'unapplied',
     'uncommit':         'uncommit',
+    'undo':             'undo',
     'unhide':           'unhide'
     })
 
@@ -129,6 +130,7 @@ stackcommands = (
     'top',
     'unapplied',
     'uncommit',
+    'undo',
     'unhide',
     )
 patchcommands = (
diff --git a/t/t3102-undo.sh b/t/t3102-undo.sh
new file mode 100755
index 0000000..1093f70
--- /dev/null
+++ b/t/t3102-undo.sh
@@ -0,0 +1,86 @@
+#!/bin/sh
+
+test_description='Simple test cases for "stg undo"'
+
+. ./test-lib.sh
+
+# Ignore our own output files.
+cat > .git/info/exclude <<EOF
+/expected.txt
+EOF
+
+test_expect_success 'Initialize StGit stack with three patches' '
+    stg init &&
+    echo 000 >> 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 <<EOF
+000
+111
+EOF
+test_expect_success 'Pop one patch ...' '
+    stg pop &&
+    test "$(echo $(stg applied))" = "p1" &&
+    test "$(echo $(stg unapplied))" = "p2 p3" &&
+    test_cmp expected.txt a
+'
+
+cat > expected.txt <<EOF
+000
+111
+222
+EOF
+test_expect_success '... and undo it' '
+    stg undo &&
+    test "$(echo $(stg applied))" = "p1 p2" &&
+    test "$(echo $(stg unapplied))" = "p3" &&
+    test_cmp expected.txt a
+'
+
+cat > expected.txt <<EOF
+000
+EOF
+test_expect_success 'Pop two patches ...' '
+    stg pop &&
+    stg pop &&
+    test "$(echo $(stg applied))" = "" &&
+    test "$(echo $(stg unapplied))" = "p1 p2 p3" &&
+    test_cmp expected.txt a
+'
+
+cat > expected.txt <<EOF
+000
+111
+222
+EOF
+test_expect_success '... and undo it' '
+    stg undo &&
+    stg undo &&
+    test "$(echo $(stg applied))" = "p1 p2" &&
+    test "$(echo $(stg unapplied))" = "p3" &&
+    test_cmp expected.txt a
+'
+
+cat > expected.txt <<EOF
+000
+111
+222
+EOF
+test_expect_success 'Undo past end of history' '
+    ! stg undo -n 100 &&
+    test "$(echo $(stg applied))" = "p1 p2" &&
+    test "$(echo $(stg unapplied))" = "p3" &&
+    test_cmp expected.txt a
+'
+
+test_done
diff --git a/t/t3101-reset-hard.sh b/t/t3103-undo-hard.sh
similarity index 82%
copy from t/t3101-reset-hard.sh
copy to t/t3103-undo-hard.sh
index 1e02805..21412f7 100755
--- a/t/t3101-reset-hard.sh
+++ b/t/t3103-undo-hard.sh
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-test_description='Simple test cases for "stg reset"'
+test_description='Simple test cases for "stg undo"'
 
 . ./test-lib.sh
 
@@ -35,8 +35,8 @@ test_expect_success 'Pop middle patch, creating a conflict' '
     test "$(echo $(stg unapplied))" = "p2"
 '
 
-test_expect_success 'Try to reset without --hard' '
-    ! stg reset master.stgit^~1 &&
+test_expect_success 'Try to undo without --hard' '
+    ! stg undo &&
     stg status a > actual.txt &&
     test_cmp expected.txt actual.txt &&
     test "$(echo $(stg applied))" = "p1 p3" &&
@@ -45,8 +45,8 @@ test_expect_success 'Try to reset without --hard' '
 
 cat > expected.txt <<EOF
 EOF
-test_expect_success 'Try to reset with --hard' '
-    stg reset --hard master.stgit^~1 &&
+test_expect_success 'Try to undo with --hard' '
+    stg undo --hard &&
     stg status a > actual.txt &&
     test_cmp expected.txt actual.txt &&
     test "$(echo $(stg applied))" = "p1" &&

^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2008-05-09  0:45 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-05-09  0:44 [StGit PATCH 0/2] stg undo Karl Hasselström
2008-05-09  0:44 ` [StGit PATCH 1/2] Move stack reset function to a shared location Karl Hasselström
2008-05-09  0:44 ` [StGit PATCH 2/2] New command: stg undo Karl Hasselström

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.