With this flag, reset will overwrite any local changes. Useful e.g. when undoing a push that's polluted the index+worktree with a heap of conflicts. Signed-off-by: Karl Hasselström <kha@xxxxxxxxxxx> --- stgit/commands/reset.py | 14 +++++++----- stgit/lib/git.py | 4 +++ stgit/lib/transaction.py | 11 +++++++-- t/t3101-reset-hard.sh | 56 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 77 insertions(+), 8 deletions(-) create mode 100755 t/t3101-reset-hard.sh diff --git a/stgit/commands/reset.py b/stgit/commands/reset.py index e27f440..00226f0 100644 --- a/stgit/commands/reset.py +++ b/stgit/commands/reset.py @@ -17,12 +17,13 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """ +from optparse import make_option 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 <state> [<patchnames>] +usage = """%prog [options] <state> [<patchnames>] 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 @@ -41,9 +42,10 @@ If one or more patch names are given, reset only those patches, and leave the rest alone.""" directory = common.DirectoryHasRepositoryLib() -options = [] +options = [make_option('--hard', action = 'store_true', + help = 'discard changes in your index/worktree')] -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: @@ -53,7 +55,8 @@ def reset_stack(stack, iw, state, only_patches): 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, 'stg reset') + trans = transaction.StackTransaction(stack, 'stg reset', + discard_changes = hard) # If we have to change the stack base, we need to pop all patches # first. @@ -111,4 +114,5 @@ 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) + 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 5bb1c93..c044b46 100644 --- a/stgit/lib/git.py +++ b/stgit/lib/git.py @@ -605,6 +605,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 92bcfd5..a008780 100644 --- a/stgit/lib/transaction.py +++ b/stgit/lib/transaction.py @@ -36,7 +36,8 @@ class _TransPatchMap(dict): return self.__stack.patches.get(pn).commit class StackTransaction(object): - def __init__(self, stack, msg, allow_conflicts = False): + def __init__(self, stack, msg, discard_changes = False, + allow_conflicts = False): self.__stack = stack self.__msg = msg self.__patches = _TransPatchMap(stack) @@ -46,6 +47,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: @@ -70,7 +72,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 @@ -81,7 +83,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..1e02805 --- /dev/null +++ b/t/t3101-reset-hard.sh @@ -0,0 +1,56 @@ +#!/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' ' + ! stg pop p2 && + stg status a > actual.txt && + test_cmp expected.txt actual.txt && + test "$(echo $(stg applied))" = "p1 p3" && + test "$(echo $(stg unapplied))" = "p2" +' + +test_expect_success 'Try to reset without --hard' ' + ! stg reset master.stgit^~1 && + stg status a > actual.txt && + test_cmp expected.txt actual.txt && + test "$(echo $(stg applied))" = "p1 p3" && + test "$(echo $(stg unapplied))" = "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 applied))" = "p1" && + test "$(echo $(stg unapplied))" = "p3 p2" +' + +test_done -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html