[PATCH 2 of 5 V2] amend: wrap all commit operation into a single transaction

Pierre-Yves David pierre-yves.david at ens-lyon.org
Sat Aug 25 18:50:28 CDT 2012


# HG changeset patch
# User Pierre-Yves David <pierre-yves.david at ens-lyon.org>
# Date 1345826644 -7200
# Node ID 479afc9e76c9f26dace52d7d7fdac1217620b2fd
# Parent  16ee1c30f73d97dba52be1bcaf570a2ab9c9f9d4
amend: wrap all commit operation into a single transaction

This allows proper recovery of interrupted amend process.
No changes are made the the logic beside:

- 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 are extracted from
  the main code.

diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -1573,118 +1573,127 @@
     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)
+            # 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
+
+            # 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())
+
+            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))
+
+                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())
+
+                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 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))
+
+                # 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, [])
+
+            new = context.memctx(repo,
+                                 parents=[base.node(), nullid],
+                                 text=message,
+                                 files=files,
+                                 filectxfn=filectxfn,
+                                 user=user,
+                                 date=date,
+                                 extra=extra)
+            newid = repo.commitctx(new)
+            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:
-            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
-
-        # 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())
-
-        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))
-
-            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())
-
-            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 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))
-
-            # 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, [])
-
-        new = context.memctx(repo,
-                             parents=[base.node(), nullid],
-                             text=message,
-                             files=files,
-                             filectxfn=filectxfn,
-                             user=user,
-                             date=date,
-                             extra=extra)
-        newid = repo.commitctx(new)
+            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)


More information about the Mercurial-devel mailing list