[PATCH] Fix issue 1719, inotify dirstate when dirty (v2: improved matching + extended test)

Simon Heimberg simohe at besonet.ch
Sat Aug 15 16:50:19 CDT 2009


# HG changeset patch
# User Simon Heimberg <simohe at besonet.ch>
# Date 1250372350 -7200
# Node ID 68874a314191de4a27fbf817fc857d5770a62932
# Parent  bde485306b9dd5c8ea8fd9ee939d8879833689d6
inotify: when dirstate is dirty, force local state (fix issue1719)

The inotify server always returns the dirstate from disc. When the dirstate
is modified but not yet written to disc, this is the wrong state. The client
fixes the inconsistent states.

Add a test with mq for issue1719 and a python test

diff -r bde485306b9d -r 68874a314191 hgext/inotify/__init__.py
--- a/hgext/inotify/__init__.py	Sam Aug 15 23:39:10 2009 +0200
+++ b/hgext/inotify/__init__.py	Sam Aug 15 23:39:10 2009 +0200
@@ -10,8 +10,9 @@
 
 # todo: socket permissions
 
+import bisect
 from mercurial.i18n import _
-from mercurial import cmdutil, util
+from mercurial import cmdutil, util, match as _match
 import server
 from weakref import proxy
 from client import client, QueryFailed
@@ -92,6 +93,45 @@
                                 if f not in a:
                                     ui.warn('*** inotify: %s -%s\n' % (c, f))
                         result = r2
+                    if self._dirty:
+                        # dirstate in memory is modified
+                        #
+                        # Test for all files if the state on disc is consistent
+                        # with the state in memory. If it is not use the one
+                        # from the memory.
+
+                        exact = match.matchfn == match.exact
+                        if files and exact:
+                            unseen = set(files)
+                        elif clean:
+                            # add all files from dirstate for potential fixing
+                            unseen = set(self)
+                        else:
+                            # do not check clean files
+                            unseen = set(f for f in self if self[f] != 'n')
+
+                        fix = set()
+                        matchingstates = 'n nm a r nma ? ? n'.split()
+                        for files, matching in zip(result, matchingstates):
+                            for f in files[:]:
+                                unseen.discard(f)
+                                if self[f] not in matching:
+                                    # inconsistent state
+                                    fix.add(f)
+                                    files.remove(f)
+                        if not exact and (files or match.anypats()):
+                            # only keep matching files
+                            unseen = set(f for f in unseen if match(f))
+                        fix.update(unseen)
+                        if fix: # fix states
+                            fmatch = _match.exact(match._root, match._cwd, fix)
+                            # query dirstate in memory for inconsistent files
+                            fresult = super(inotifydirstate, self).status(
+                                fmatch, ignored, clean, unknown)
+                            for st, fst in zip(result, fresult):
+                                for f in fst:
+                                    # insert filename sorted in result
+                                    bisect.insort(st, f)
                     return result
             return super(inotifydirstate, self).status(
                 match, ignored, clean, unknown)
diff -r bde485306b9d -r 68874a314191 tests/test-inotify-dirty.py
--- /dev/null	Don Jan 01 00:00:00 1970 +0000
+++ b/tests/test-inotify-dirty.py	Sam Aug 15 23:39:10 2009 +0200
@@ -0,0 +1,83 @@
+#!/bin/env python
+
+'Test inotify status while wlock is held (=dirstate is dirty)'
+
+import os, time, sys
+from mercurial import hg, extensions, ui, match
+
+def pstatus(match, ignored=False, clean=False, unknown=False):
+    res = repo.dirstate.status(match, ignored, clean, unknown)
+    for st, files in zip('LMARDUIC', res):
+        if files:
+            print '%s: %s' % (st, ','.join(files)),
+    print ''
+
+def pnstatus(match):
+    pstatus(match, True)
+    pstatus(match, True, True)
+    pstatus(match, True, True, True)
+    pstatus(match, True, False, True)
+    pstatus(match, False, True)
+
+u = ui.ui()
+if os.environ.get('HG_TEST_NO_INOTIFY'):
+    print '#### test without inotify' # for generating correct out file
+else:
+    extensions.load(u, 'inotify', '')
+    try:
+        import hgext.inotify.linux.watcher
+        return True
+    except ImportError:
+        sys.stderr.write('missing feature: inotify extension support\n')
+        sys.exit(80)
+
+os.mkdir('r')
+open('r/.hgignore', 'w').write('.hgignore\n')
+
+repo = hg.repository(u, 'r', create=True)
+
+open('r/cc', 'w').write('0')
+open('r/rr', 'w').write('1')
+open('r/mm', 'w').write('2')
+open('r/dd', 'w').write('3')
+open('r/ll', 'w').write('4')
+open('r/uu', 'w').write('5')
+open('r/aa', 'w').write('6')
+
+time.sleep(1) # wait for second to change
+
+wlock = repo.wlock()
+repo.add('cc rr mm dd ll'.split())
+repo.commit('1st')
+wlock.release()
+
+wlock = repo.wlock()
+open('r/mm', 'w').write('10')
+open('r/ll', 'w').write('0')
+os.unlink('r/dd')
+repo.add(['aa'])
+repo.remove(['rr'])
+
+print '% exact, all'
+m = match.exact(repo.root, '', 'cc rr mm uu aa dd ll ii'.split())
+pnstatus(m)
+
+print '% exact, none'
+m = match.exact(repo.root, '', ['kk'])
+pstatus(m)
+pstatus(m, False, True)
+pstatus(m, True, True, True)
+
+print '% always'
+m = match.always(repo.root, '')
+pnstatus(m)
+
+print '% match *'
+m = match.match(repo.root, '', ['*'])
+pnstatus(m)
+
+print '% match some'
+m = match.match(repo.root, '', 'aa dd cc'.split())
+pnstatus(m)
+
+wlock.release()
diff -r bde485306b9d -r 68874a314191 tests/test-inotify-dirty.py.out
--- /dev/null	Don Jan 01 00:00:00 1970 +0000
+++ b/tests/test-inotify-dirty.py.out	Sam Aug 15 23:39:10 2009 +0200
@@ -0,0 +1,28 @@
+% exact, all
+L: ll M: mm A: aa R: rr D: dd 
+L: ll M: mm A: aa R: rr D: dd C: cc 
+L: ll M: mm A: aa R: rr D: dd U: uu C: cc 
+L: ll M: mm A: aa R: rr D: dd U: uu 
+L: ll M: mm A: aa R: rr D: dd C: cc 
+% exact, none
+
+
+
+% always
+L: ll M: mm A: aa R: rr D: dd I: .hgignore 
+L: ll M: mm A: aa R: rr D: dd I: .hgignore C: cc 
+L: ll M: mm A: aa R: rr D: dd U: uu I: .hgignore C: cc 
+L: ll M: mm A: aa R: rr D: dd U: uu I: .hgignore 
+L: ll M: mm A: aa R: rr D: dd C: cc 
+% match *
+L: ll M: mm A: aa R: rr D: dd I: .hgignore 
+L: ll M: mm A: aa R: rr D: dd I: .hgignore C: cc 
+L: ll M: mm A: aa R: rr D: dd U: uu I: .hgignore C: cc 
+L: ll M: mm A: aa R: rr D: dd U: uu I: .hgignore 
+L: ll M: mm A: aa R: rr D: dd C: cc 
+% match some
+A: aa D: dd 
+A: aa D: dd C: cc 
+A: aa D: dd C: cc 
+A: aa D: dd 
+A: aa D: dd C: cc 
diff -r bde485306b9d -r 68874a314191 tests/test-inotify-mq
--- /dev/null	Don Jan 01 00:00:00 1970 +0000
+++ b/tests/test-inotify-mq	Sam Aug 15 23:39:10 2009 +0200
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+"$TESTDIR/hghave" inotify || exit 80
+
+# qpush changeset which adds or deletes a file succedes (issue1719)
+
+echo [extensions] >> $HGRCPATH
+echo inotify= >>  $HGRCPATH
+echo mq= >>  $HGRCPATH
+
+hg init a
+cd a
+
+hg qnew adda
+echo 11 > a
+hg add a
+hg qref
+hg qpo
+hg qpu
+hg st
+
+hg qnew rma
+hg rm a
+hg qref
+hg qpo
+hg qpu
+hg st
+
+hg qpo -a
diff -r bde485306b9d -r 68874a314191 tests/test-inotify-mq.out
--- /dev/null	Don Jan 01 00:00:00 1970 +0000
+++ b/tests/test-inotify-mq.out	Sam Aug 15 23:39:10 2009 +0200
@@ -0,0 +1,11 @@
+popping adda
+patch queue now empty
+applying adda
+now at: adda
+popping rma
+now at: adda
+applying rma
+now at: rma
+popping rma
+popping adda
+patch queue now empty


More information about the Mercurial-devel mailing list