{"thread":{"id":"13112","subject":"[StGit PATCH] New command: stg reset","startedAt":"2008-04-15T00:01:35Z","lastAt":"2008-04-15T00:01:35Z","messageCount":1,"participants":["Karl Hasselström"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"74390","messageId":"20080414235941.12803.40939.stgit@yoghurt","threadId":"13112","inReplyTo":null,"subject":"[StGit PATCH] New command: stg reset","fromName":"Karl Hasselström","fromEmail":"kha@treskal.com","sentAt":"2008-04-15T00:01:35Z","receivedAt":"2008-04-15T00:01:35Z","isPatch":true,"sender":{"key":"kha@treskal.com","avatar":"https://gravatar.com/avatar/f0120c734b5279b345075a28521e1ac66acb20c9913ffe9bf6ae97e53f7f3f13?d=mp&s=160"},"body":"Given a commit object from the log, resets the stack (or just the\nnamed patches) to the state given by that log entry.\n\nSigned-off-by: Karl Hasselström <kha@treskal.com>\n\n---\n\nNew and updated version that can optionally reset only a given subset\nof the patches. Plus, it comes with a test script!\n\n stgit/commands/reset.py |  111 +++++++++++++++++++++++++++++++++++\n stgit/main.py           |    2 +\n t/t3100-reset.sh        |  151 +++++++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 264 insertions(+), 0 deletions(-)\n create mode 100644 stgit/commands/reset.py\n create mode 100755 t/t3100-reset.sh\n\n\ndiff --git a/stgit/commands/reset.py b/stgit/commands/reset.py\nnew file mode 100644\nindex 0000000..b29d122\n--- /dev/null\n+++ b/stgit/commands/reset.py\n@@ -0,0 +1,111 @@\n+# -*- coding: utf-8 -*-\n+\n+__copyright__ = \"\"\"\n+Copyright (C) 2008, Karl Hasselström <kha@treskal.com>\n+\n+This program is free software; you can redistribute it and/or modify\n+it under the terms of the GNU General Public License version 2 as\n+published by the Free Software Foundation.\n+\n+This program is distributed in the hope that it will be useful,\n+but WITHOUT ANY WARRANTY; without even the implied warranty of\n+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n+GNU General Public License for more details.\n+\n+You should have received a copy of the GNU General Public License\n+along with this program; if not, write to the Free Software\n+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n+\"\"\"\n+\n+from stgit.commands import common\n+from stgit.lib import git, log, transaction\n+from stgit.out import out\n+\n+help = 'reset the patch stack to an earlier state'\n+usage = \"\"\"%prog <state> [<patchnames>]\n+\n+Reset the patch stack to an earlier state. The state is specified with\n+a commit from a stack log; for a branch foo, StGit stores the stack\n+log in foo.stgit^. So to undo the last N StGit commands, you would say\n+\n+  stg reset foo.stgit^~N\n+\n+or, if you are not sure how many steps to undo, you can view the log\n+with \"git log\" or gitk\n+\n+  gitk foo.stgit^\n+\n+and then reset to any sha1 you see in the log.\n+\n+If one or more patch names are given, reset only those patches, and\n+leave the rest alone.\"\"\"\n+\n+directory = common.DirectoryHasRepositoryLib()\n+options = []\n+\n+def reset_stack(stack, iw, state, only_patches):\n+    only_patches = set(only_patches)\n+    def mask(s):\n+        if only_patches:\n+            return s & only_patches\n+        else:\n+            return s\n+    patches_to_reset = mask(set(state.applied + state.unapplied))\n+    existing_patches = set(stack.patchorder.all)\n+    to_delete = mask(existing_patches - patches_to_reset)\n+    trans = transaction.StackTransaction(stack, 'stg reset')\n+\n+    # If we have to change the stack base, we need to pop all patches\n+    # first.\n+    if not only_patches and trans.base != state.base:\n+        trans.pop_patches(lambda pn: True)\n+        out.info('Setting stack base to %s' % state.base.sha1)\n+        trans.base = state.base\n+\n+    # In one go, do all the popping we have to in order to pop the\n+    # patches we're going to delete or modify.\n+    def mod(pn):\n+        if only_patches and not pn in only_patches:\n+            return False\n+        if pn in to_delete:\n+            return True\n+        if stack.patches.get(pn).commit != state.patches.get(pn, None):\n+            return True\n+        return False\n+    trans.pop_patches(mod)\n+\n+    # Delete and modify/create patches. We've previously popped all\n+    # patches that we touch in this step.\n+    trans.delete_patches(lambda pn: pn in to_delete)\n+    for pn in patches_to_reset:\n+        if pn in existing_patches:\n+            out.info('Resetting %s' % pn)\n+        else:\n+            trans.unapplied.append(pn)\n+            out.info('Resurrecting %s' % pn)\n+        trans.patches[pn] = state.patches[pn]\n+\n+    # Push/pop patches as necessary.\n+    try:\n+        if only_patches:\n+            # Push all the patches that we've popped, if they still\n+            # exist.\n+            pushable = set(trans.unapplied)\n+            for pn in stack.patchorder.applied:\n+                if pn in pushable:\n+                    trans.push_patch(pn, iw)\n+        else:\n+            # Recreate the exact order specified by the goal state.\n+            trans.reorder_patches(state.applied, state.unapplied, iw)\n+    except transaction.TransactionHalted:\n+        pass\n+    return trans.run(iw)\n+\n+def func(parser, options, args):\n+    stack = directory.repository.current_stack\n+    if len(args) >= 1:\n+        ref, patches = args[0], args[1:]\n+        state = log.Log(stack.repository, ref, stack.repository.rev_parse(ref))\n+    else:\n+        raise common.CmdException('Wrong number of arguments')\n+    return reset_stack(stack, stack.repository.default_iw, state, patches)\ndiff --git a/stgit/main.py b/stgit/main.py\nindex 663fdec..6cf344e 100644\n--- a/stgit/main.py\n+++ b/stgit/main.py\n@@ -89,6 +89,7 @@ commands = Commands({\n     'refresh':          'refresh',\n     'rename':           'rename',\n     'repair':           'repair',\n+    'reset':            'reset',\n     'resolved':         'resolved',\n     'series':           'series',\n     'show':             'show',\n@@ -122,6 +123,7 @@ stackcommands = (\n     'push',\n     'rebase',\n     'repair',\n+    'reset',\n     'series',\n     'sink',\n     'top',\ndiff --git a/t/t3100-reset.sh b/t/t3100-reset.sh\nnew file mode 100755\nindex 0000000..1ad95fa\n--- /dev/null\n+++ b/t/t3100-reset.sh\n@@ -0,0 +1,151 @@\n+#!/bin/sh\n+\n+test_description='Simple test cases for \"stg reset\"'\n+\n+. ./test-lib.sh\n+\n+# Ignore our own output files.\n+cat > .git/info/exclude <<EOF\n+/expected.txt\n+EOF\n+\n+test_expect_success 'Initialize StGit stack with three patches' '\n+    stg init &&\n+    echo 000 >> a &&\n+    git add a &&\n+    git commit -m a &&\n+    echo 111 >> a &&\n+    git commit -a -m p1 &&\n+    echo 222 >> a &&\n+    git commit -a -m p2 &&\n+    echo 333 >> a &&\n+    git commit -a -m p3 &&\n+    stg uncommit -n 3 &&\n+    stg pop\n+'\n+\n+cat > expected.txt <<EOF\n+000\n+111\n+EOF\n+test_expect_success 'Pop one patch ...' '\n+    stg pop &&\n+    test \"$(echo $(stg applied))\" = \"p1\" &&\n+    test \"$(echo $(stg unapplied))\" = \"p2 p3\" &&\n+    diff -u expected.txt a\n+'\n+\n+cat > expected.txt <<EOF\n+000\n+111\n+222\n+EOF\n+test_expect_success '... and undo it' '\n+    stg reset master.stgit^~1 &&\n+    test \"$(echo $(stg applied))\" = \"p1 p2\" &&\n+    test \"$(echo $(stg unapplied))\" = \"p3\" &&\n+    diff -u expected.txt a\n+'\n+\n+cat > expected.txt <<EOF\n+000\n+111\n+222\n+333\n+EOF\n+test_expect_success 'Push one patch ...' '\n+    stg push &&\n+    test \"$(echo $(stg applied))\" = \"p1 p2 p3\" &&\n+    test \"$(echo $(stg unapplied))\" = \"\" &&\n+    diff -u expected.txt a\n+'\n+\n+cat > expected.txt <<EOF\n+000\n+111\n+222\n+EOF\n+test_expect_success '... and undo it' '\n+    stg reset master.stgit^~1 &&\n+    test \"$(echo $(stg applied))\" = \"p1 p2\" &&\n+    test \"$(echo $(stg unapplied))\" = \"p3\" &&\n+    diff -u expected.txt a\n+'\n+\n+test_expect_success 'Commit one patch ...' '\n+    stg commit &&\n+    test \"$(echo $(stg applied))\" = \"p2\" &&\n+    test \"$(echo $(stg unapplied))\" = \"p3\"\n+'\n+\n+test_expect_success '... and undo it' '\n+    stg reset master.stgit^~1 &&\n+    test \"$(echo $(stg applied))\" = \"p1 p2\" &&\n+    test \"$(echo $(stg unapplied))\" = \"p3\"\n+'\n+\n+cat > expected.txt <<EOF\n+000\n+111\n+EOF\n+test_expect_success 'Delete two patches ...' '\n+    stg delete p2 p3 &&\n+    test \"$(echo $(stg applied))\" = \"p1\" &&\n+    test \"$(echo $(stg unapplied))\" = \"\" &&\n+    diff -u expected.txt a\n+'\n+\n+test_expect_success '... and undo one of the deletions ...' '\n+    stg reset master.stgit^~1 p3 &&\n+    test \"$(echo $(stg applied))\" = \"p1\" &&\n+    test \"$(echo $(stg unapplied))\" = \"p3\" &&\n+    diff -u expected.txt a\n+'\n+\n+test_expect_success '... then undo the first undo ...' '\n+    stg reset master.stgit^~1 &&\n+    test \"$(echo $(stg applied))\" = \"p1\" &&\n+    test \"$(echo $(stg unapplied))\" = \"\" &&\n+    diff -u expected.txt a\n+'\n+\n+cat > expected.txt <<EOF\n+000\n+111\n+222\n+EOF\n+test_expect_success '... and undo the other deletion' '\n+    stg reset master.stgit^~3 p2 &&\n+    stg push p2 &&\n+    test \"$(echo $(stg applied))\" = \"p1 p2\" &&\n+    test \"$(echo $(stg unapplied))\" = \"\" &&\n+    diff -u expected.txt a\n+'\n+\n+cat > expected.txt <<EOF\n+000\n+111\n+222\n+ggg\n+EOF\n+test_expect_success 'Refresh a patch ...' '\n+    echo ggg >> a &&\n+    stg refresh &&\n+    test \"$(echo $(stg applied))\" = \"p1 p2\" &&\n+    test \"$(echo $(stg unapplied))\" = \"\" &&\n+    diff -u expected.txt a\n+'\n+\n+cat > expected.txt <<EOF\n+000\n+111\n+222\n+EOF\n+test_expect_success '... and undo the refresh' '\n+    stg reset master.stgit^~1 &&\n+    test \"$(echo $(stg applied))\" = \"p1 p2\" &&\n+    test \"$(echo $(stg unapplied))\" = \"\" &&\n+    diff -u expected.txt a\n+'\n+\n+test_done\n"}]}