[PATCH 1 of 5] amend: lock the repository during the whole process

Pierre-Yves David pierre-yves.david at ens-lyon.org
Fri Aug 24 14:17:59 CDT 2012


# HG changeset patch
# User Pierre-Yves David <pierre-yves.david at ens-lyon.org>
# Date 1345827562 -7200
# Node ID 3159d224951b36bf11edc49885e616ec72759c6f
# Parent  a0cf8f4cd38ba8c35132379b680ebf6d0e900e3d
amend: lock the repository during the whole process

Without this changes another writer can lock the repository in the middle the
amend process. The resulting mess can be pretty ugly.

This changeset only ensure that the repository is locked during the whole amend
by wrapping the code with a try, except. No changes to the wrapped code is made
beside:

- the indentation,
- docstring wrapping,
- removal of a dedicated locking around the stripping of old changesets.

diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -1570,126 +1570,129 @@
 
     wlock = repo.wlock()
     try:
-        # First, do a regular commit to record all changes in the working
-        # directory (if there are any)
-        ui.callhooks = False
+        lock = repo.lock()
         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)
 
-            # Strip the intermediate commit (if there was one) and the amended
-            # commit
-            lock = repo.lock()
-            try:
+                # 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')
-            finally:
-                lock.release()
+        finally:
+            lock.release()
     finally:
         wlock.release()
     return newid


More information about the Mercurial-devel mailing list