[PATCH 15 of 19] mq: use decorator to wrap writing commands with locks and automatic savedirty
Mads Kiilerich
mads at kiilerich.com
Thu Jan 12 19:32:49 CST 2012
# HG changeset patch
# User Mads Kiilerich <mads at kiilerich.com>
# Date 1326245685 -3600
# Node ID 8bccb99f4ff21f335503798db047cf27c79cd77c
# Parent 342cbabf227347bd3cd65464ce9a19980df0be9b
mq: use decorator to wrap writing commands with locks and automatic savedirty
This cleans up the mq code and makes it less fragile and ensures that all
non-read-only commands see and modify a consistent snapshot of the repo and the
mq meta data.
The mq state files are not append only, and consistency for unlocked read-only
operations can thus not be guaranteed with Mercurials locking scheme. This
patch introduces locking the places where it seems most critical to have
consistent data: when modifying the repo in any way.
The new locking might be too broad for some operations and it might prevent
some kinds of read-only operations on read-only repositories, such as read-only
sub-commands of commands that also can modify the repository.
diff --git a/hgext/mq.py b/hgext/mq.py
--- a/hgext/mq.py
+++ b/hgext/mq.py
@@ -57,6 +57,23 @@
cmdtable = {}
command = cmdutil.command(cmdtable)
+def mqlocksavedirty(func):
+ """Wrap a command in a wlock and lock and create a new queue instance that
+ thus (assuming this protocol is followed consistently) is guaranteed to be
+ consistent. mq.savedirty is run before releasing the locks."""
+ def wrapper(ui, repo, *args, **kwargs):
+ wlock = lock = None
+ try:
+ wlock = repo.wlock()
+ lock = repo.lock()
+ repo.mq = queue(repo.ui, repo.path)
+ return func(ui, repo, *args, **kwargs)
+ finally:
+ repo.mq.savedirty()
+ release(lock, wlock)
+ wrapper.__doc__ = getattr(func, '__doc__', None)
+ return wrapper
+
# Patch names looks like unix-file names.
# They must be joinable with queue directory and result in the patch path.
normname = util.normpath
@@ -1853,6 +1870,7 @@
('r', 'rev', [],
_('stop managing a revision (DEPRECATED)'), _('REV'))],
_('hg qdelete [-k] [PATCH]...'))
+ at mqlocksavedirty
def delete(ui, repo, *patches, **opts):
"""remove patches from queue
@@ -1935,6 +1953,7 @@
('g', 'git', None, _('use git extended diff format')),
('P', 'push', None, _('qpush after importing'))],
_('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...'))
+ at mqlocksavedirty
def qimport(ui, repo, *filename, **opts):
"""import a patch
@@ -1980,6 +1999,7 @@
return q.push(repo, None)
return 0
+ at mqlocksavedirty
def qinit(ui, repo, create):
"""initialize a new queue repository
@@ -2181,6 +2201,7 @@
_('add "Date: <DATE>" to patch'), _('DATE'))
] + commands.walkopts + commands.commitopts,
_('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...'))
+ at mqlocksavedirty
def new(ui, repo, patch, *args, **opts):
"""create a new patch
@@ -2235,6 +2256,7 @@
_('add/update date field in patch with given date'), _('DATE'))
] + commands.walkopts + commands.commitopts,
_('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...'))
+ at mqlocksavedirty
def refresh(ui, repo, *pats, **opts):
"""update the current patch
@@ -2304,6 +2326,7 @@
('k', 'keep', None, _('keep folded patch files')),
] + commands.commitopts,
_('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...'))
+ at mqlocksavedirty
def fold(ui, repo, *files, **opts):
"""fold the named patches into the current patch
@@ -2374,6 +2397,7 @@
@command("qgoto",
[('f', 'force', None, _('overwrite any local changes'))],
_('hg qgoto [OPTION]... PATCH'))
+ at mqlocksavedirty
def goto(ui, repo, patch, **opts):
'''push or pop patches until named patch is at top of stack
@@ -2391,6 +2415,7 @@
[('l', 'list', None, _('list all patches and guards')),
('n', 'none', None, _('drop all guards'))],
_('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]'))
+ at mqlocksavedirty
def guard(ui, repo, *args, **opts):
'''set or print guards for a patch
@@ -2512,6 +2537,7 @@
_('merge queue name (DEPRECATED)'), _('NAME')),
('', 'move', None, _('reorder patch series and apply only the patch'))],
_('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]'))
+ at mqlocksavedirty
def push(ui, repo, patch=None, **opts):
"""push the next patch onto the stack
@@ -2544,6 +2570,7 @@
_('queue name to pop (DEPRECATED)'), _('NAME')),
('f', 'force', None, _('forget any local changes to patched files'))],
_('hg qpop [-a] [-f] [PATCH | INDEX]'))
+ at mqlocksavedirty
def pop(ui, repo, patch=None, **opts):
"""pop the current patch off the stack
@@ -2566,6 +2593,7 @@
return ret
@command("qrename|qmv", [], _('hg qrename PATCH1 [PATCH2]'))
+ at mqlocksavedirty
def rename(ui, repo, patch, name=None, **opts):
"""rename a patch
@@ -2627,6 +2655,7 @@
[('d', 'delete', None, _('delete save entry')),
('u', 'update', None, _('update queue working directory'))],
_('hg qrestore [-d] [-u] REV'))
+ at mqlocksavedirty
def restore(ui, repo, rev, **opts):
"""restore the queue state saved by a revision (DEPRECATED)
@@ -2645,6 +2674,7 @@
('e', 'empty', None, _('clear queue status file')),
('f', 'force', None, _('force copy'))] + commands.commitopts,
_('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'))
+ at mqlocksavedirty
def save(ui, repo, **opts):
"""save current queue state (DEPRECATED)
@@ -2690,6 +2720,7 @@
('', 'nobackup', None, _('no backups (DEPRECATED)')),
('k', 'keep', None, _("do not modify working copy during strip"))],
_('hg strip [-k] [-f] [-n] REV...'))
+ at mqlocksavedirty
def strip(ui, repo, *revs, **opts):
"""strip changesets and all their descendants from the repository
@@ -2779,6 +2810,7 @@
('', 'pop', None, _('pop to before first guarded applied patch')),
('', 'reapply', None, _('pop, then reapply patches'))],
_('hg qselect [OPTION]... [GUARD]...'))
+ at mqlocksavedirty
def select(ui, repo, *args, **opts):
'''set or print guarded patches to push
@@ -2887,6 +2919,7 @@
@command("qfinish",
[('a', 'applied', None, _('finish all applied changesets'))],
_('hg qfinish [-a] [REV]...'))
+ at mqlocksavedirty
def finish(ui, repo, *revrange, **opts):
"""move applied patches into repository history
@@ -2932,6 +2965,7 @@
('', 'purge', False, _('delete queue, and remove patch dir')),
],
_('[OPTION] [QUEUE]'))
+ at mqlocksavedirty
def qqueue(ui, repo, name=None, **opts):
'''manage multiple patch queues
More information about the Mercurial-devel
mailing list