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

Pierre-Yves David pierre-yves.david at ens-lyon.org
Mon Jul 30 18:16:25 CDT 2012


# HG changeset patch
# User Pierre-Yves David <pierre-yves.david at ens-lyon.org>
# Date 1343689377 -7200
# Branch stable
# Node ID 1edb3dd232b9976a21bd3583dc3dfb29f833a6d3
# Parent  88f903c158c68499851eb7fd70859065c9a28170
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
@@ -1572,125 +1572,133 @@
     try:
         lock = repo.lock()
         try:
-            # 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)
-            newid = repo.commitctx(new)
-            if newid != old.node():
-                # Reroute the working copy parent to the new changeset
-                repo.setparents(newid, nullid)
+                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)
+                    # 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()
 
                 # 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)
-                repair.strip(ui, repo, old.node(), topic='amend-backup')
+                if newid != old.node():
+                    if node:
+                        ui.note(_('stripping intermediate changeset %s\n')
+                                % ctx)
+                    ui.note(_('stripping amended changeset %s\n') % old)
+                    repair.strip(ui, repo, old.node(), topic='amend-backup')
+            finally:
+                tr.release()
         finally:
             lock.release()
     finally:


More information about the Mercurial-devel mailing list