chiark / gitweb /
Add a --hard flag to stg reset
authorKarl Hasselström <kha@treskal.com>
Sun, 21 Sep 2008 12:17:41 +0000 (14:17 +0200)
committerKarl Hasselström <kha@treskal.com>
Sun, 21 Sep 2008 12:19:07 +0000 (14:19 +0200)
With this flag, reset will overwrite any local changes. Useful e.g.
when undoing a push that has polluted the index+worktree with a heap
of conflicts.

Signed-off-by: Karl Hasselström <kha@treskal.com>
stgit/commands/reset.py
stgit/lib/git.py
stgit/lib/transaction.py
t/t3101-reset-hard.sh [new file with mode: 0755]

index b6378417d6217b09802da8d2e7694c67750a58a0..6db95592a3ab674122e215178a4d21ffc52fdcf1 100644 (file)
@@ -17,13 +17,14 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 """
 
 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 = 'Reset the patch stack to an earlier state'
 kind = 'stack'
 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'
 kind = 'stack'
-usage = ['<state> [<patchnames>]']
+usage = ['[options] <state> [<patchnames>]']
 description = """
 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
 description = """
 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
@@ -43,11 +44,13 @@ 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."""
 
 If one or more patch names are given, reset only those patches, and
 leave the rest alone."""
 
-options = []
+options = [
+    opt('--hard', action = 'store_true',
+        short = 'Discard changes in your index/worktree')]
 
 directory = common.DirectoryHasRepositoryLib()
 
 
 directory = common.DirectoryHasRepositoryLib()
 
-def reset_stack(stack, iw, state, only_patches):
+def reset_stack(stack, iw, state, only_patches, hard):
     only_patches = set(only_patches)
     def mask(s):
         if only_patches:
     only_patches = set(only_patches)
     def mask(s):
         if only_patches:
@@ -57,7 +60,7 @@ def reset_stack(stack, iw, state, only_patches):
     patches_to_reset = mask(set(state.applied + state.unapplied + state.hidden))
     existing_patches = set(stack.patchorder.all)
     to_delete = mask(existing_patches - patches_to_reset)
     patches_to_reset = mask(set(state.applied + state.unapplied + state.hidden))
     existing_patches = set(stack.patchorder.all)
     to_delete = mask(existing_patches - patches_to_reset)
-    trans = transaction.StackTransaction(stack, '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 we have to change the stack base, we need to pop all patches
     # first.
@@ -119,4 +122,5 @@ def func(parser, options, args):
         state = log.get_log_entry(stack.repository, ref)
     else:
         raise common.CmdException('Wrong number of arguments')
         state = log.get_log_entry(stack.repository, ref)
     else:
         raise common.CmdException('Wrong number of arguments')
-    return reset_stack(stack, stack.repository.default_iw, state, patches)
+    return reset_stack(stack, stack.repository.default_iw, state, patches,
+                       options.hard)
index ac3e9d0c46dd8339a4a824be5576e756150c5ff8..dd496057d46824ddf090afe5fe0dfb39eb4506af 100644 (file)
@@ -764,6 +764,10 @@ class IndexAndWorktree(RunWithEnvCwd):
     env = property(lambda self: utils.add_dict(self.__index.env,
                                                self.__worktree.env))
     cwd = property(lambda self: self.__worktree.directory)
     env = property(lambda self: utils.add_dict(self.__index.env,
                                                self.__worktree.env))
     cwd = property(lambda self: self.__worktree.directory)
+    def checkout_hard(self, tree):
+        assert isinstance(tree, Tree)
+        self.run(['git', 'read-tree', '--reset', '-u', tree.sha1]
+                 ).discard_output()
     def checkout(self, old_tree, new_tree):
         # TODO: Optionally do a 3-way instead of doing nothing when we
         # have a problem. Or maybe we should stash changes in a patch?
     def checkout(self, old_tree, new_tree):
         # TODO: Optionally do a 3-way instead of doing nothing when we
         # have a problem. Or maybe we should stash changes in a patch?
index 20d87e156752b3872f7c3d528e8b361800b9bc40..84d72d57655c6832b2a946bd06b5142ecbbce0ab 100644 (file)
@@ -74,7 +74,14 @@ class StackTransaction(object):
       method. This will either succeed in writing the updated state to
       your refs and index+worktree, or fail without having done
       anything."""
       method. This will either succeed in writing the updated state to
       your refs and index+worktree, or fail without having done
       anything."""
-    def __init__(self, stack, msg, allow_conflicts = False):
+    def __init__(self, stack, msg, discard_changes = False,
+                 allow_conflicts = False):
+        """Create a new L{StackTransaction}.
+
+        @param discard_changes: Discard any changes in index+worktree
+        @type discard_changes: bool
+        @param allow_conflicts: Whether to allow pre-existing conflicts
+        @type allow_conflicts: bool or function of L{StackTransaction}"""
         self.__stack = stack
         self.__msg = msg
         self.__patches = _TransPatchMap(stack)
         self.__stack = stack
         self.__msg = msg
         self.__patches = _TransPatchMap(stack)
@@ -85,6 +92,7 @@ class StackTransaction(object):
         self.__error = None
         self.__current_tree = self.__stack.head.data.tree
         self.__base = self.__stack.base
         self.__error = None
         self.__current_tree = self.__stack.head.data.tree
         self.__base = self.__stack.base
+        self.__discard_changes = discard_changes
         if isinstance(allow_conflicts, bool):
             self.__allow_conflicts = lambda trans: allow_conflicts
         else:
         if isinstance(allow_conflicts, bool):
             self.__allow_conflicts = lambda trans: allow_conflicts
         else:
@@ -119,7 +127,7 @@ class StackTransaction(object):
                 'This can happen if you modify a branch with git.',
                 '"stg repair --help" explains more about what to do next.')
             self.__abort()
                 'This can happen if you modify a branch with git.',
                 '"stg repair --help" explains more about what to do next.')
             self.__abort()
-        if self.__current_tree == tree:
+        if self.__current_tree == tree and not self.__discard_changes:
             # No tree change, but we still want to make sure that
             # there are no unresolved conflicts. Conflicts
             # conceptually "belong" to the topmost patch, and just
             # No tree change, but we still want to make sure that
             # there are no unresolved conflicts. Conflicts
             # conceptually "belong" to the topmost patch, and just
@@ -130,7 +138,10 @@ class StackTransaction(object):
             out.error('Need to resolve conflicts first')
             self.__abort()
         assert iw != None
             out.error('Need to resolve conflicts first')
             self.__abort()
         assert iw != None
-        iw.checkout(self.__current_tree, tree)
+        if self.__discard_changes:
+            iw.checkout_hard(tree)
+        else:
+            iw.checkout(self.__current_tree, tree)
         self.__current_tree = tree
     @staticmethod
     def __abort():
         self.__current_tree = tree
     @staticmethod
     def __abort():
diff --git a/t/t3101-reset-hard.sh b/t/t3101-reset-hard.sh
new file mode 100755 (executable)
index 0000000..2807ba3
--- /dev/null
@@ -0,0 +1,53 @@
+#!/bin/sh
+
+test_description='Simple test cases for "stg reset"'
+
+. ./test-lib.sh
+
+# Ignore our own output files.
+cat > .git/info/exclude <<EOF
+/expected.txt
+/actual.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
+'
+
+cat > expected.txt <<EOF
+C a
+EOF
+test_expect_success 'Pop middle patch, creating a conflict' '
+    conflict_old stg pop p2 &&
+    stg status a > actual.txt &&
+    test_cmp expected.txt actual.txt &&
+    test "$(echo $(stg series))" = "+ p1 > p3 - p2"
+'
+
+test_expect_success 'Try to reset without --hard' '
+    command_error stg reset master.stgit^~1 &&
+    stg status a > actual.txt &&
+    test_cmp expected.txt actual.txt &&
+    test "$(echo $(stg series))" = "+ p1 > p3 - p2"
+'
+
+cat > expected.txt <<EOF
+EOF
+test_expect_success 'Try to reset with --hard' '
+    stg reset --hard master.stgit^~1 &&
+    stg status a > actual.txt &&
+    test_cmp expected.txt actual.txt &&
+    test "$(echo $(stg series))" = "> p1 - p3 - p2"
+'
+
+test_done