From 781e549a76633dd45cdf305c3e73e26ab706e3c9 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Karl=20Hasselstr=C3=B6m?= Date: Tue, 20 May 2008 23:33:25 +0200 Subject: [PATCH] Prevent most commands from running when there are conflicts MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit When there are conflicts, we want most commands to fail, since the conflicts conceptually belong to the topmost patch. git read-tree already checks this for us when we check out a new tree, but there are operations where the top tree stays the same, e.g. stg new. This patch inserts a conflict check when the tree to check out is the same. By default, conflicts will prevent the checkout from succeeding, but commands can choose to override this if the same patch stays on top (for some definition of "same"). This change only affects the new-infrastructure commands; the others always refuse to run when there are local changes of any kind. Signed-off-by: Karl Hasselström --- stgit/commands/clean.py | 2 +- stgit/commands/coalesce.py | 3 ++- stgit/commands/commit.py | 7 ++++++- stgit/commands/delete.py | 10 +++++++++- stgit/commands/edit.py | 2 +- stgit/commands/uncommit.py | 3 ++- stgit/lib/transaction.py | 27 ++++++++++++++++++++++----- 7 files changed, 43 insertions(+), 11 deletions(-) diff --git a/stgit/commands/clean.py b/stgit/commands/clean.py index dbbc961..a5effb6 100644 --- a/stgit/commands/clean.py +++ b/stgit/commands/clean.py @@ -37,7 +37,7 @@ options = [make_option('-a', '--applied', def _clean(stack, clean_applied, clean_unapplied): - trans = transaction.StackTransaction(stack, 'clean') + trans = transaction.StackTransaction(stack, 'clean', allow_conflicts = True) def del_patch(pn): if pn in stack.patchorder.applied: if pn == stack.patchorder.applied[-1]: diff --git a/stgit/commands/coalesce.py b/stgit/commands/coalesce.py index 7eb89db..07a9753 100644 --- a/stgit/commands/coalesce.py +++ b/stgit/commands/coalesce.py @@ -79,7 +79,8 @@ def _coalesce(stack, iw, name, msg, save_template, patches): trans.patches[name] = stack.repository.commit(new_commit_data) trans.unapplied.insert(0, name) - trans = transaction.StackTransaction(stack, 'coalesce') + trans = transaction.StackTransaction(stack, 'coalesce', + allow_conflicts = True) push_new_patch = bool(set(patches) & set(trans.applied)) try: new_commit_data = _coalesce_patches(trans, patches, msg, save_template) diff --git a/stgit/commands/commit.py b/stgit/commands/commit.py index f8927b5..e95b67f 100644 --- a/stgit/commands/commit.py +++ b/stgit/commands/commit.py @@ -69,7 +69,12 @@ def func(parser, options, args): raise common.CmdException('No patches to commit') iw = stack.repository.default_iw - trans = transaction.StackTransaction(stack, 'commit') + def allow_conflicts(trans): + # As long as the topmost patch stays where it is, it's OK to + # run "stg commit" with conflicts in the index. + return len(trans.applied) >= 1 + trans = transaction.StackTransaction(stack, 'commit', + allow_conflicts = allow_conflicts) try: common_prefix = 0 for i in xrange(min(len(stack.patchorder.applied), len(patches))): diff --git a/stgit/commands/delete.py b/stgit/commands/delete.py index 4321b0a..13a23c6 100644 --- a/stgit/commands/delete.py +++ b/stgit/commands/delete.py @@ -46,7 +46,15 @@ def func(parser, options, args): + list(stack.patchorder.unapplied)))) else: parser.error('No patches specified') - trans = transaction.StackTransaction(stack, 'delete') + def allow_conflicts(trans): + # Allow conflicts if the topmost patch stays the same. + if stack.patchorder.applied: + return (trans.applied + and trans.applied[-1] == stack.patchorder.applied[-1]) + else: + return not trans.applied + trans = transaction.StackTransaction(stack, 'delete', + allow_conflicts = allow_conflicts) try: to_push = trans.delete_patches(lambda pn: pn in patches) for pn in to_push: diff --git a/stgit/commands/edit.py b/stgit/commands/edit.py index 6d7fbd5..a8499c6 100644 --- a/stgit/commands/edit.py +++ b/stgit/commands/edit.py @@ -173,7 +173,7 @@ def func(parser, options, args): # The patch applied, so now we have to rewrite the StGit patch # (and any patches on top of it). iw = stack.repository.default_iw - trans = transaction.StackTransaction(stack, 'edit') + trans = transaction.StackTransaction(stack, 'edit', allow_conflicts = True) if patchname in trans.applied: popped = trans.applied[trans.applied.index(patchname)+1:] assert not trans.pop_patches(lambda pn: pn in popped) diff --git a/stgit/commands/uncommit.py b/stgit/commands/uncommit.py index 415267e..05e49e0 100644 --- a/stgit/commands/uncommit.py +++ b/stgit/commands/uncommit.py @@ -131,7 +131,8 @@ def func(parser, options, args): taken_names.add(pn) patchnames.reverse() - trans = transaction.StackTransaction(stack, 'uncommit') + trans = transaction.StackTransaction(stack, 'uncommit', + allow_conflicts = True) for commit, pn in zip(commits, patchnames): trans.patches[pn] = commit trans.applied = list(reversed(patchnames)) + trans.applied diff --git a/stgit/lib/transaction.py b/stgit/lib/transaction.py index 1ece01e..874f81b 100644 --- a/stgit/lib/transaction.py +++ b/stgit/lib/transaction.py @@ -34,7 +34,7 @@ class _TransPatchMap(dict): return self.__stack.patches.get(pn).commit class StackTransaction(object): - def __init__(self, stack, msg): + def __init__(self, stack, msg, allow_conflicts = False): self.__stack = stack self.__msg = msg self.__patches = _TransPatchMap(stack) @@ -43,6 +43,10 @@ class StackTransaction(object): self.__error = None self.__current_tree = self.__stack.head.data.tree self.__base = self.__stack.base + if isinstance(allow_conflicts, bool): + self.__allow_conflicts = lambda trans: allow_conflicts + else: + self.__allow_conflicts = allow_conflicts stack = property(lambda self: self.__stack) patches = property(lambda self: self.__patches) def __set_applied(self, val): @@ -63,10 +67,19 @@ 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: - assert iw != None - iw.checkout(self.__current_tree, tree) - self.__current_tree = tree + if self.__current_tree == tree: + # 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 + # carrying them along to another patch is confusing. + if (self.__allow_conflicts(self) or iw == None + or not iw.index.conflicts()): + return + out.error('Need to resolve conflicts first') + self.__abort() + assert iw != None + iw.checkout(self.__current_tree, tree) + self.__current_tree = tree @staticmethod def __abort(): raise TransactionException( @@ -214,4 +227,8 @@ class StackTransaction(object): self.applied.append(pn) out.info('Pushed %s%s' % (pn, s)) if merge_conflict: + # We've just caused conflicts, so we must allow them in + # the final checkout. + self.__allow_conflicts = lambda trans: True + self.__halt('Merge conflict') -- 2.11.0