diff -r 16f8107a489c -r 68474b72ea63 hgext/histedit.py --- a/hgext/histedit.py Fri Mar 10 15:43:31 2017 -0800 +++ b/hgext/histedit.py Fri Mar 10 15:52:29 2017 -0800 @@ -168,6 +168,15 @@ [histedit] dropmissing = True +By default, histedit will close the transaction after each action. For +performance purposes, you can configure histedit to use a single transaction +across the entire histedit. WARNING: This setting introduces a significant risk +of losing the work you've done in a histedit if the histedit aborts +unexpectedly:: + + [histedit] + singletransaction = True + """ from __future__ import absolute_import @@ -269,6 +278,7 @@ self.lock = lock self.wlock = wlock self.backupfile = None + self.tr = None if replacements is None: self.replacements = [] else: @@ -1098,18 +1108,45 @@ total = len(state.actions) pos = 0 - while state.actions: - state.write() - actobj = state.actions[0] - pos += 1 - ui.progress(_("editing"), pos, actobj.torule(), - _('changes'), total) - ui.debug('histedit: processing %s %s\n' % (actobj.verb,\ - actobj.torule())) - parentctx, replacement_ = actobj.run() - state.parentctxnode = parentctx.node() - state.replacements.extend(replacement_) - state.actions.pop(0) + state.tr = None + + # Force an initial state file write, so the user can run --abort/continue + # even if there's an exception before the first transaction serialize. + state.write() + try: + # Don't use singletransaction by default since it rolls the entire + # transaction back if an unexpected exception happens (like a + # pretxncommit hook throws, or the user aborts the commit msg editor). + if ui.configbool("histedit", "singletransaction", False): + # Don't use a 'with' for the transaction, since actions may close + # and reopen a transaction. For example, if the action executes an + # external process it may choose to commit the transaction first. + state.tr = repo.transaction('histedit') + + while state.actions: + state.write(tr=state.tr) + actobj = state.actions[0] + pos += 1 + ui.progress(_("editing"), pos, actobj.torule(), + _('changes'), total) + ui.debug('histedit: processing %s %s\n' % (actobj.verb,\ + actobj.torule())) + parentctx, replacement_ = actobj.run() + state.parentctxnode = parentctx.node() + state.replacements.extend(replacement_) + state.actions.pop(0) + + if state.tr is not None: + state.tr.close() + except error.InterventionRequired: + if state.tr is not None: + state.tr.close() + raise + except Exception: + if state.tr is not None: + state.tr.abort() + raise + state.write() ui.progress(_("editing"), None)