# HG changeset patch # User Pierre-Yves David # Date 1347313464 -7200 # Node ID 965fbe04fd96f2cb3b84e5f20b93e406a9087b03 # Parent ad1561723ddeb7cc9d6696fdad76baed91e39e7b amend: wrap all commit operations in a single transaction This allows proper recovery of an interrupted amend process. No changes are made to the logic besides: - indent operations into a single try-except clause, - some comment and code wrapping to 80 chars, - strip logic should not be contained in the transaction and is extracted from the main code. diff -r ad1561723dde -r 965fbe04fd96 mercurial/cmdutil.py --- a/mercurial/cmdutil.py Sat Aug 25 15:37:28 2012 +0200 +++ b/mercurial/cmdutil.py Mon Sep 10 23:44:24 2012 +0200 @@ -1580,123 +1580,132 @@ try: wlock = repo.wlock() lock = repo.lock() - # First, do a regular commit to record all changes in the working - # directory (if there are any) - ui.callhooks = False + tr = repo.transaction('amend') try: - node = commit(ui, repo, commitfunc, pats, opts) - finally: - ui.callhooks = True - ctx = repo[node] + # First, do a regular commit to record all changes in the working + # directory (if there are any) + ui.callhooks = False + try: + node = commit(ui, repo, commitfunc, pats, opts) + finally: + ui.callhooks = True + ctx = repo[node] - # Participating changesets: - # - # node/ctx o - new (intermediate) commit that contains changes from - # | working dir to go into amending commit (or a workingctx - # | if there were no changes) - # | - # old o - changeset to amend - # | - # base o - parent of amending changeset + # Participating changesets: + # + # node/ctx o - new (intermediate) commit that contains changes + # | from working dir to go into amending commit + # | (or a workingctx if there were no changes) + # | + # old o - changeset to amend + # | + # base o - parent of amending changeset - # Update extra dict from amended commit (e.g. to preserve graft source) - extra.update(old.extra()) + # Update extra dict from amended commit (e.g. to preserve graft + # source) + extra.update(old.extra()) - # Also update it from the intermediate commit or from the wctx - extra.update(ctx.extra()) + # Also update it from the intermediate commit or from the wctx + extra.update(ctx.extra()) - files = set(old.files()) + files = set(old.files()) - # Second, we use either the commit we just did, or if there were no - # changes the parent of the working directory as the version of the - # files in the final amend commit - if node: - ui.note(_('copying changeset %s to %s\n') % (ctx, base)) + # Second, we use either the commit we just did, or if there were no + # changes the parent of the working directory as the version of the + # files in the final amend commit + if node: + ui.note(_('copying changeset %s to %s\n') % (ctx, base)) - user = ctx.user() - date = ctx.date() - message = ctx.description() - # Recompute copies (avoid recording a -> b -> a) - copied = copies.pathcopies(base, ctx) + user = ctx.user() + date = ctx.date() + message = ctx.description() + # Recompute copies (avoid recording a -> b -> a) + copied = copies.pathcopies(base, ctx) - # Prune files which were reverted by the updates: if old introduced - # file X and our intermediate commit, node, renamed that file, then - # those two files are the same and we can discard X from our list - # of files. Likewise if X was deleted, it's no longer relevant - files.update(ctx.files()) + # Prune files which were reverted by the updates: if old + # introduced file X and our intermediate commit, node, + # renamed that file, then those two files are the same and + # we can discard X from our list of files. Likewise if X + # was deleted, it's no longer relevant + files.update(ctx.files()) - def samefile(f): - if f in ctx.manifest(): - a = ctx.filectx(f) - if f in base.manifest(): - b = base.filectx(f) - return (not a.cmp(b) - and a.flags() == b.flags()) + def samefile(f): + if f in ctx.manifest(): + a = ctx.filectx(f) + if f in base.manifest(): + b = base.filectx(f) + return (not a.cmp(b) + and a.flags() == b.flags()) + else: + return False else: - return False - else: - return f not in base.manifest() - files = [f for f in files if not samefile(f)] + return f not in base.manifest() + files = [f for f in files if not samefile(f)] - def filectxfn(repo, ctx_, path): - try: - fctx = ctx[path] - flags = fctx.flags() - mctx = context.memfilectx(fctx.path(), fctx.data(), - islink='l' in flags, - isexec='x' in flags, - copied=copied.get(path)) - return mctx - except KeyError: - raise IOError - else: - ui.note(_('copying changeset %s to %s\n') % (old, base)) + def filectxfn(repo, ctx_, path): + try: + fctx = ctx[path] + flags = fctx.flags() + mctx = context.memfilectx(fctx.path(), fctx.data(), + islink='l' in flags, + isexec='x' in flags, + copied=copied.get(path)) + return mctx + except KeyError: + raise IOError + else: + ui.note(_('copying changeset %s to %s\n') % (old, base)) - # Use version of files as in the old cset - def filectxfn(repo, ctx_, path): - try: - return old.filectx(path) - except KeyError: - raise IOError + # Use version of files as in the old cset + def filectxfn(repo, ctx_, path): + try: + return old.filectx(path) + except KeyError: + raise IOError - # See if we got a message from -m or -l, if not, open the editor - # with the message of the changeset to amend - user = opts.get('user') or old.user() - date = opts.get('date') or old.date() - message = logmessage(ui, opts) - if not message: - cctx = context.workingctx(repo, old.description(), user, date, - extra, - repo.status(base.node(), old.node())) - message = commitforceeditor(repo, cctx, []) + # See if we got a message from -m or -l, if not, open the editor + # with the message of the changeset to amend + user = opts.get('user') or old.user() + date = opts.get('date') or old.date() + message = logmessage(ui, opts) + if not message: + cctx = context.workingctx(repo, old.description(), + user, date, extra, + repo.status(base.node(), + old.node())) + message = commitforceeditor(repo, cctx, []) - new = context.memctx(repo, - parents=[base.node(), nullid], - text=message, - files=files, - filectxfn=filectxfn, - user=user, - date=date, - extra=extra) - ph = repo.ui.config('phases', 'new-commit', phases.draft) - try: - repo.ui.setconfig('phases', 'new-commit', old.phase()) - newid = repo.commitctx(new) + new = context.memctx(repo, + parents=[base.node(), nullid], + text=message, + files=files, + filectxfn=filectxfn, + user=user, + date=date, + extra=extra) + ph = repo.ui.config('phases', 'new-commit', phases.draft) + try: + repo.ui.setconfig('phases', 'new-commit', old.phase()) + newid = repo.commitctx(new) + finally: + repo.ui.setconfig('phases', 'new-commit', ph) + if newid != old.node(): + # Reroute the working copy parent to the new changeset + repo.setparents(newid, nullid) + + # Move bookmarks from old parent to amend commit + bms = repo.nodebookmarks(old.node()) + if bms: + for bm in bms: + repo._bookmarks[bm] = newid + bookmarks.write(repo) + #commit the whole amend process + tr.close() finally: - repo.ui.setconfig('phases', 'new-commit', ph) + tr.release() + # Strip the intermediate commit (if there was one) and the amended + # commit if newid != old.node(): - # Reroute the working copy parent to the new changeset - repo.setparents(newid, nullid) - - # Move bookmarks from old parent to amend commit - bms = repo.nodebookmarks(old.node()) - if bms: - for bm in bms: - repo._bookmarks[bm] = newid - bookmarks.write(repo) - - # Strip the intermediate commit (if there was one) and the amended - # commit if node: ui.note(_('stripping intermediate changeset %s\n') % ctx) ui.note(_('stripping amended changeset %s\n') % old)