+def get_patch(stack, given_patch):
+ """Get the name of the patch we are to refresh."""
+ if given_patch:
+ patch_name = given_patch
+ if not stack.patches.exists(patch_name):
+ raise common.CmdException('%s: no such patch' % patch_name)
+ return patch_name
+ else:
+ if not stack.patchorder.applied:
+ raise common.CmdException(
+ 'Cannot refresh top patch, because no patches are applied')
+ return stack.patchorder.applied[-1]
+
+def list_files(stack, patch_name, args, index, update):
+ """Figure out which files to update."""
+ if index:
+ # --index: Don't update the index.
+ return set()
+ paths = stack.repository.default_iw.changed_files(
+ stack.head.data.tree, args or [])
+ if update:
+ # --update: Restrict update to the paths that were already
+ # part of the patch.
+ paths &= stack.patches.get(patch_name).files()
+ return paths
+
+def write_tree(stack, paths, temp_index):
+ """Possibly update the index, and then write its tree.
+ @return: The written tree.
+ @rtype: L{Tree<stgit.git.Tree>}"""
+ def go(index):
+ if paths:
+ iw = git.IndexAndWorktree(index, stack.repository.default_worktree)
+ iw.update_index(paths)
+ return index.write_tree()
+ if temp_index:
+ index = stack.repository.temp_index()
+ try:
+ index.read_tree(stack.head)
+ return go(index)
+ finally:
+ index.delete()
+ stack.repository.default_iw.update_index(paths)
+ else:
+ return go(stack.repository.default_index)
+
+def make_temp_patch(stack, patch_name, paths, temp_index):
+ """Commit index to temp patch, in a complete transaction. If any path
+ limiting is in effect, use a temp index."""
+ tree = write_tree(stack, paths, temp_index)
+ commit = stack.repository.commit(git.CommitData(
+ tree = tree, parents = [stack.head],
+ message = 'Refresh of %s' % patch_name))
+ temp_name = utils.make_patch_name('refresh-temp', stack.patches.exists)
+ trans = transaction.StackTransaction(stack,
+ 'refresh (create temporary patch)')
+ trans.patches[temp_name] = commit
+ trans.applied.append(temp_name)
+ return trans.run(stack.repository.default_iw,
+ print_current_patch = False), temp_name
+
+def absorb_applied(trans, iw, patch_name, temp_name):
+ """Absorb the temp patch (C{temp_name}) into the given patch
+ (C{patch_name}), which must be applied.
+
+ @return: C{True} if we managed to absorb the temp patch, C{False}
+ if we had to leave it for the user to deal with."""
+ temp_absorbed = False
+ try:
+ # Pop any patch on top of the patch we're refreshing.
+ to_pop = trans.applied[trans.applied.index(patch_name)+1:]
+ if len(to_pop) > 1:
+ popped = trans.pop_patches(lambda pn: pn in to_pop)
+ assert not popped # no other patches were popped
+ trans.push_patch(temp_name, iw)
+ assert to_pop.pop() == temp_name
+
+ # Absorb the temp patch.
+ temp_cd = trans.patches[temp_name].data
+ assert trans.patches[patch_name] == temp_cd.parent
+ trans.patches[patch_name] = trans.stack.repository.commit(
+ trans.patches[patch_name].data.set_tree(temp_cd.tree))
+ popped = trans.delete_patches(lambda pn: pn == temp_name, quiet = True)
+ assert not popped # the temp patch was topmost
+ temp_absorbed = True
+
+ # Push back any patch we were forced to pop earlier.
+ for pn in to_pop:
+ trans.push_patch(pn, iw)
+ except transaction.TransactionHalted:
+ pass
+ return temp_absorbed
+
+def absorb_unapplied(trans, iw, patch_name, temp_name):
+ """Absorb the temp patch (C{temp_name}) into the given patch
+ (C{patch_name}), which must be unapplied.
+
+ @param iw: Not used.
+ @return: C{True} if we managed to absorb the temp patch, C{False}
+ if we had to leave it for the user to deal with."""
+
+ # Pop the temp patch.
+ popped = trans.pop_patches(lambda pn: pn == temp_name)
+ assert not popped # the temp patch was topmost
+
+ # Try to create the new tree of the refreshed patch. (This is the
+ # same operation as pushing the temp patch onto the patch we're
+ # trying to refresh -- but we don't have a worktree to spill
+ # conflicts to, so if the simple merge doesn't succeed, we have to
+ # give up.)
+ patch_cd = trans.patches[patch_name].data
+ temp_cd = trans.patches[temp_name].data
+ new_tree = trans.stack.repository.simple_merge(
+ base = temp_cd.parent.data.tree,
+ ours = patch_cd.tree, theirs = temp_cd.tree)
+ if new_tree:
+ # It worked. Refresh the patch with the new tree, and delete
+ # the temp patch.
+ trans.patches[patch_name] = trans.stack.repository.commit(
+ patch_cd.set_tree(new_tree))
+ popped = trans.delete_patches(lambda pn: pn == temp_name, quiet = True)
+ assert not popped # the temp patch was not applied
+ return True