chiark / gitweb /
Prevent most commands from running when there are conflicts
authorKarl Hasselström <kha@treskal.com>
Tue, 20 May 2008 21:33:25 +0000 (23:33 +0200)
committerKarl Hasselström <kha@treskal.com>
Tue, 20 May 2008 21:33:25 +0000 (23:33 +0200)
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 <kha@treskal.com>
stgit/commands/clean.py
stgit/commands/coalesce.py
stgit/commands/commit.py
stgit/commands/delete.py
stgit/commands/edit.py
stgit/commands/uncommit.py
stgit/lib/transaction.py

index dbbc961127f8cac57db6a8c613b44efbcdf4b534..a5effb61cbc70c46724c1ba84c6955f4c26a9f73 100644 (file)
@@ -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]:
index 7eb89db647edc0ee1f5de0deec6d62826dce302b..07a9753e5e526198aa5002d2286712c6b0d8c165 100644 (file)
@@ -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)
index f8927b5c4b722c2da8768c123a0e180f03844fa6..e95b67f1c5a6aa2b1c0254669b021e9a80d93f0a 100644 (file)
@@ -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))):
index 4321b0acfca7c43da8d0dc3f9a3ff7990ccd2e65..13a23c6946317a6000c59b12ef0aaf4c50952acb 100644 (file)
@@ -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:
index 6d7fbd5785107190a0f77fe32d9b2a2a97d77c68..a8499c6301527ef8c4e107c82678cd1b4e2a93c7 100644 (file)
@@ -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)
index 415267ecf34a15f3ca0369d9addf42cf8fa0cd6d..05e49e01f952268fd571211e9f95adb79f61d300 100644 (file)
@@ -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
index 1ece01ea3e55996d8ccb82351a85b016d58fa865..874f81bd582d7cca1eb1dbe429848618fcd14d81 100644 (file)
@@ -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')