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
--- 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