inotify: mark directories visited during lookup (issue1844)
authorNicolas Dumazet <nicdumz.commits@gmail.com>
Fri, 13 Nov 2009 08:31:10 +0900
changeset 9854 95e1867f765b
parent 9853 a033929bd34e
child 9855 f47c0881b16e
inotify: mark directories visited during lookup (issue1844) Emulate the match.dir calls that are made in dirstate.walk: * first mark the visited directories on the server side * then extend the transmitted response to include this directory list * and lastly call match.dir on each directory
hgext/inotify/client.py
hgext/inotify/common.py
hgext/inotify/server.py
tests/test-inotify
tests/test-inotify.out
--- a/hgext/inotify/client.py	Fri Nov 13 06:36:26 2009 +0100
+++ b/hgext/inotify/client.py	Fri Nov 13 08:31:10 2009 +0900
@@ -150,7 +150,16 @@
                 if names:
                     return filter(match, names.split('\0'))
             return []
-        return map(readnames, resphdr)
+        results = map(readnames, resphdr[:-1])
+
+        if names:
+            nbytes = resphdr[-1]
+            vdirs = cs.read(nbytes)
+            if vdirs:
+                for vdir in vdirs.split('\0'):
+                    match.dir(vdir)
+
+        return results
 
     @start_server
     def debugquery(self):
--- a/hgext/inotify/common.py	Fri Nov 13 06:36:26 2009 +0100
+++ b/hgext/inotify/common.py	Fri Nov 13 08:31:10 2009 +0900
@@ -24,16 +24,18 @@
   1) send protocol version number
   2) send query type
   3) send struct.pack'ed headers describing the length of the content:
-      e.g. for STAT, receive 8 integers describing the length of the
-      8 \0-separated string lists ( one list for each lmar!?ic status type )
+      e.g. for STAT, receive 9 integers describing the length of the
+      9 \0-separated string lists to be read:
+       * one file list for each lmar!?ic status type
+       * one list containing the directories visited during lookup
 
 """
 
-version = 2
+version = 3
 
 resphdrfmts = {
-    'STAT': '>llllllll', # status requests
-    'DBUG': '>l'         # debugging queries
+    'STAT': '>lllllllll', # status requests
+    'DBUG': '>l'          # debugging queries
 }
 resphdrsizes = dict((k, struct.calcsize(v))
                     for k, v in resphdrfmts.iteritems())
--- a/hgext/inotify/server.py	Fri Nov 13 06:36:26 2009 +0100
+++ b/hgext/inotify/server.py	Fri Nov 13 08:31:10 2009 +0900
@@ -237,7 +237,7 @@
                 ret = d
         return ret
 
-    def walk(self, states):
+    def walk(self, states, visited=None):
         """
         yield (filename, status) pairs for items in the trees
         that have status in states.
@@ -247,10 +247,12 @@
             if st in states:
                 yield join(self.path, file), st
         for dir in self.dirs.itervalues():
+            if visited is not None:
+                visited.add(dir.path)
             for e in dir.walk(states):
                 yield e
 
-    def lookup(self, states, path):
+    def lookup(self, states, path, visited):
         """
         yield root-relative filenames that match path, and whose
         status are in states:
@@ -272,16 +274,20 @@
                 tree = tree.dirs[dir]
         except KeyError:
             # path is not tracked
+            visited.add(tree.path)
             return
 
         try:
             # if path is a directory, walk it
-            for file, st in tree.dirs[last].walk(states):
+            target = tree.dirs[last]
+            visited.add(target.path)
+            for file, st in target.walk(states, visited):
                 yield file
         except KeyError:
             try:
                 if tree.files[last] in states:
                     # path is a file
+                    visited.add(tree.path)
                     yield path
             except KeyError:
                 # path is not tracked
@@ -725,6 +731,7 @@
             # answer.
             self.repowatcher.handle_timeout()
 
+        visited = set()
         if not names:
             def genresult(states, tree):
                 for fn, state in tree.walk(states):
@@ -732,7 +739,7 @@
         else:
             def genresult(states, tree):
                 for fn in names:
-                    for f in tree.lookup(states, fn):
+                    for f in tree.lookup(states, fn, visited):
                         yield f
 
         return ['\0'.join(r) for r in [
@@ -746,6 +753,7 @@
                 or [],
             [],
             'c' in states and genresult('n', self.repowatcher.tree) or [],
+            visited
             ]]
 
     def answer_dbug_query(self):
--- a/tests/test-inotify	Fri Nov 13 06:36:26 2009 +0100
+++ b/tests/test-inotify	Fri Nov 13 08:31:10 2009 +0900
@@ -70,4 +70,14 @@
 
 HGMERGE=internal:local hg up
 hg st
+
+# Test for 1844: "hg ci folder" will not commit all changes beneath "folder"
+mkdir 1844
+echo a > 1844/foo
+hg add 1844
+hg ci -m 'working'
+
+echo b >> 1844/foo
+hg ci 1844 -m 'broken'
+
 kill `cat hg.pid`
--- a/tests/test-inotify.out	Fri Nov 13 06:36:26 2009 +0100
+++ b/tests/test-inotify.out	Fri Nov 13 08:31:10 2009 +0900
@@ -44,3 +44,4 @@
 M a
 3 files updated, 1 files merged, 0 files removed, 0 files unresolved
 M a
+adding 1844/foo