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]:
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)
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))):
+ 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:
# 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)
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
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)
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):
'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(
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')