commands: add debuglocks to report/clear lock state
authorMatt Mackall <mpm@selenic.com>
Fri, 26 Sep 2014 16:44:11 -0500
changeset 22559 4e0b696a1cb3
parent 22558 064a912ebfc2
child 22560 4109cc16279e
commands: add debuglocks to report/clear lock state
mercurial/commands.py
tests/test-completion.t
tests/test-help.t
tests/test-push-hook-lock.t
--- a/mercurial/commands.py	Wed Sep 24 11:20:35 2014 -0700
+++ b/mercurial/commands.py	Fri Sep 26 16:44:11 2014 -0500
@@ -9,7 +9,7 @@
 from lock import release
 from i18n import _
 import os, re, difflib, time, tempfile, errno, shlex
-import sys
+import sys, socket
 import hg, scmutil, util, revlog, copies, error, bookmarks
 import patch, help, encoding, templatekw, discovery
 import archival, changegroup, cmdutil, hbisect
@@ -2338,6 +2338,78 @@
     ui.write('\n'.join(sorted(completions)))
     ui.write('\n')
 
+@command('debuglocks',
+         [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
+          ('W', 'force-wlock', None,
+           _('free the working state lock (DANGEROUS)'))],
+         _(''))
+def debuglocks(ui, repo, **opts):
+    """show or modify state of locks
+
+    By default, this command will show which locks are held. This
+    includes the user and process holding the lock, the amount of time
+    the lock has been held, and the machine name where the process is
+    running if it's not local.
+
+    Locks protect the integrity of Mercurial's data, so should be
+    treated with care. System crashes or other interruptions may cause
+    locks to not be properly released, though Mercurial will usually
+    detect and remove such stale locks automatically.
+
+    However, detecting stale locks may not always be possible (for
+    instance, on a shared filesystem). Removing locks may also be
+    blocked by filesystem permissions.
+
+    Returns 0 if no locks are held.
+
+    """
+
+    if opts.get('force_lock'):
+        repo.svfs.unlink('lock')
+    if opts.get('force_wlock'):
+        repo.vfs.unlink('wlock')
+    if opts.get('force_lock') or opts.get('force_lock'):
+        return 0
+
+    now = time.time()
+    held = 0
+
+    def report(vfs, name, method):
+        # this causes stale locks to get reaped for more accurate reporting
+        try:
+            l = method(False)
+        except error.LockHeld:
+            l = None
+
+        if l:
+            l.release()
+        else:
+            try:
+                stat = repo.svfs.lstat(name)
+                age = now - stat.st_mtime
+                user = util.username(stat.st_uid)
+                locker = vfs.readlock(name)
+                if ":" in locker:
+                    host, pid = locker.split(':')
+                    if host == socket.gethostname():
+                        locker = 'user %s, process %s' % (user, pid)
+                    else:
+                        locker = 'user %s, process %s, host %s' \
+                                 % (user, pid, host)
+                ui.write("%-6s %s (%ds)\n" % (name + ":", locker, age))
+                return 1
+            except OSError, e:
+                if e.errno != errno.ENOENT:
+                    raise
+
+        ui.write("%-6s free\n" % (name + ":"))
+        return 0
+
+    held += report(repo.svfs, "lock", repo.lock)
+    held += report(repo.vfs, "wlock", repo.wlock)
+
+    return held
+
 @command('debugobsolete',
         [('', 'flags', 0, _('markers flag')),
          ('', 'record-parents', False,
--- a/tests/test-completion.t	Wed Sep 24 11:20:35 2014 -0700
+++ b/tests/test-completion.t	Fri Sep 26 16:44:11 2014 -0500
@@ -89,6 +89,7 @@
   debuginstall
   debugknown
   debuglabelcomplete
+  debuglocks
   debugobsolete
   debugpathcomplete
   debugpushkey
@@ -245,6 +246,7 @@
   debuginstall: 
   debugknown: 
   debuglabelcomplete: 
+  debuglocks: force-lock, force-wlock
   debugobsolete: flags, record-parents, rev, date, user
   debugpathcomplete: full, normal, added, removed
   debugpushkey: 
--- a/tests/test-help.t	Wed Sep 24 11:20:35 2014 -0700
+++ b/tests/test-help.t	Fri Sep 26 16:44:11 2014 -0500
@@ -781,6 +781,7 @@
    debugknown    test whether node ids are known to a repo
    debuglabelcomplete
                  complete "labels" - tags, open branch names, bookmark names
+   debuglocks    show or modify state of locks
    debugobsolete
                  create arbitrary obsolete marker
    debugoptDEP   (no help text available)
--- a/tests/test-push-hook-lock.t	Wed Sep 24 11:20:35 2014 -0700
+++ b/tests/test-push-hook-lock.t	Fri Sep 26 16:44:11 2014 -0500
@@ -16,6 +16,7 @@
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
   $ echo '[hooks]' >> 2/.hg/hgrc
+  $ echo 'pretxnchangegroup.a = hg debuglocks; true' >> 2/.hg/hgrc
   $ echo 'changegroup.push = hg push -qf ../1' >> 2/.hg/hgrc
 
   $ echo bar >> 3/foo
@@ -28,4 +29,6 @@
   adding manifests
   adding file changes
   added 1 changesets with 1 changes to 1 files
+  lock:  user *, process * (*s) (glob)
+  wlock: free