From: Karl Hasselström Date: Sun, 21 Sep 2008 12:17:41 +0000 (+0200) Subject: Add a --hard flag to stg reset X-Git-Tag: v0.15-rc1~144 X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~mdw/git/stgit/commitdiff_plain/9690617a55cba571d0cb25ed846ef41942063020 Add a --hard flag to stg reset 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 --- diff --git a/stgit/commands/reset.py b/stgit/commands/reset.py index b637841..6db9559 100644 --- a/stgit/commands/reset.py +++ b/stgit/commands/reset.py @@ -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 """ +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' -usage = [' []'] +usage = ['[options] []'] 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.""" -options = [] +options = [ + opt('--hard', action = 'store_true', + short = 'Discard changes in your index/worktree')] 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: @@ -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) - 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. @@ -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') - return reset_stack(stack, stack.repository.default_iw, state, patches) + return reset_stack(stack, stack.repository.default_iw, state, patches, + options.hard) diff --git a/stgit/lib/git.py b/stgit/lib/git.py index ac3e9d0..dd49605 100644 --- a/stgit/lib/git.py +++ b/stgit/lib/git.py @@ -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) + 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? diff --git a/stgit/lib/transaction.py b/stgit/lib/transaction.py index 20d87e1..84d72d5 100644 --- a/stgit/lib/transaction.py +++ b/stgit/lib/transaction.py @@ -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.""" - 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) @@ -85,6 +92,7 @@ class StackTransaction(object): 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: @@ -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() - 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 @@ -130,7 +138,10 @@ class StackTransaction(object): 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(): diff --git a/t/t3101-reset-hard.sh b/t/t3101-reset-hard.sh new file mode 100755 index 0000000..2807ba3 --- /dev/null +++ b/t/t3101-reset-hard.sh @@ -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 <> 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 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 < actual.txt && + test_cmp expected.txt actual.txt && + test "$(echo $(stg series))" = "> p1 - p3 - p2" +' + +test_done