Show More
@@ -201,6 +201,92 b' def eventaction(code):' | |||||
201 | return wrapper |
|
201 | return wrapper | |
202 | return decorator |
|
202 | return decorator | |
203 |
|
203 | |||
|
204 | class directory(object): | |||
|
205 | """ | |||
|
206 | Representing a directory | |||
|
207 | ||||
|
208 | * path is the relative path from repo root to this directory | |||
|
209 | * files is a dict listing the files in this directory | |||
|
210 | - keys are file names | |||
|
211 | - values are file status | |||
|
212 | * dirs is a dict listing the subdirectories | |||
|
213 | - key are subdirectories names | |||
|
214 | - values are directory objects | |||
|
215 | """ | |||
|
216 | def __init__(self, relpath=''): | |||
|
217 | self.path = relpath | |||
|
218 | self.files = {} | |||
|
219 | self.dirs = {} | |||
|
220 | ||||
|
221 | def dir(self, relpath): | |||
|
222 | """ | |||
|
223 | Returns the directory contained at the relative path relpath. | |||
|
224 | Creates the intermediate directories if necessary. | |||
|
225 | """ | |||
|
226 | if not relpath: | |||
|
227 | return self | |||
|
228 | l = relpath.split('/') | |||
|
229 | ret = self | |||
|
230 | while l: | |||
|
231 | next = l.pop(0) | |||
|
232 | try: | |||
|
233 | ret = ret.dirs[next] | |||
|
234 | except KeyError: | |||
|
235 | d = directory(join(ret.path, next)) | |||
|
236 | ret.dirs[next] = d | |||
|
237 | ret = d | |||
|
238 | return ret | |||
|
239 | ||||
|
240 | def walk(self, states): | |||
|
241 | """ | |||
|
242 | yield (filename, status) pairs for items in the trees | |||
|
243 | that have status in states. | |||
|
244 | filenames are relative to the repo root | |||
|
245 | """ | |||
|
246 | for file, st in self.files.iteritems(): | |||
|
247 | if st in states: | |||
|
248 | yield join(self.path, file), st | |||
|
249 | for dir in self.dirs.itervalues(): | |||
|
250 | for e in dir.walk(states): | |||
|
251 | yield e | |||
|
252 | ||||
|
253 | def lookup(self, states, path): | |||
|
254 | """ | |||
|
255 | yield root-relative filenames that match path, and whose | |||
|
256 | status are in states: | |||
|
257 | * if path is a file, yield path | |||
|
258 | * if path is a directory, yield directory files | |||
|
259 | * if path is not tracked, yield nothing | |||
|
260 | """ | |||
|
261 | if path[-1] == '/': | |||
|
262 | path = path[:-1] | |||
|
263 | ||||
|
264 | paths = path.split('/') | |||
|
265 | ||||
|
266 | # we need to check separately for last node | |||
|
267 | last = paths.pop() | |||
|
268 | ||||
|
269 | tree = self | |||
|
270 | try: | |||
|
271 | for dir in paths: | |||
|
272 | tree = tree.dirs[dir] | |||
|
273 | except KeyError: | |||
|
274 | # path is not tracked | |||
|
275 | return | |||
|
276 | ||||
|
277 | try: | |||
|
278 | # if path is a directory, walk it | |||
|
279 | for file, st in tree.dirs[last].walk(states): | |||
|
280 | yield file | |||
|
281 | except KeyError: | |||
|
282 | try: | |||
|
283 | if tree.files[last] in states: | |||
|
284 | # path is a file | |||
|
285 | yield path | |||
|
286 | except KeyError: | |||
|
287 | # path is not tracked | |||
|
288 | pass | |||
|
289 | ||||
204 | class repowatcher(pollable): |
|
290 | class repowatcher(pollable): | |
205 | """ |
|
291 | """ | |
206 | Watches inotify events |
|
292 | Watches inotify events | |
@@ -231,9 +317,9 b' class repowatcher(pollable):' | |||||
231 | self.threshold = watcher.threshold(self.watcher) |
|
317 | self.threshold = watcher.threshold(self.watcher) | |
232 | self.fileno = self.watcher.fileno |
|
318 | self.fileno = self.watcher.fileno | |
233 |
|
319 | |||
234 |
self.tree = |
|
320 | self.tree = directory() | |
235 | self.statcache = {} |
|
321 | self.statcache = {} | |
236 |
self.statustrees = dict([(s, |
|
322 | self.statustrees = dict([(s, directory()) for s in self.statuskeys]) | |
237 |
|
323 | |||
238 | self.last_event = None |
|
324 | self.last_event = None | |
239 |
|
325 | |||
@@ -288,23 +374,6 b' class repowatcher(pollable):' | |||||
288 | self.add_watch(self.repo.path, inotify.IN_DELETE) |
|
374 | self.add_watch(self.repo.path, inotify.IN_DELETE) | |
289 | self.check_dirstate() |
|
375 | self.check_dirstate() | |
290 |
|
376 | |||
291 | def dir(self, tree, path): |
|
|||
292 | if path: |
|
|||
293 | for name in path.split('/'): |
|
|||
294 | tree = tree.setdefault(name, {}) |
|
|||
295 | return tree |
|
|||
296 |
|
||||
297 | def lookup(self, path, tree): |
|
|||
298 | if path: |
|
|||
299 | try: |
|
|||
300 | for name in path.split('/'): |
|
|||
301 | tree = tree[name] |
|
|||
302 | except KeyError: |
|
|||
303 | return 'x' |
|
|||
304 | except TypeError: |
|
|||
305 | return 'd' |
|
|||
306 | return tree |
|
|||
307 |
|
||||
308 | def filestatus(self, fn, st): |
|
377 | def filestatus(self, fn, st): | |
309 | try: |
|
378 | try: | |
310 | type_, mode, size, time = self.repo.dirstate._map[fn][:4] |
|
379 | type_, mode, size, time = self.repo.dirstate._map[fn][:4] | |
@@ -350,53 +419,47 b' class repowatcher(pollable):' | |||||
350 |
|
419 | |||
351 | def _updatestatus(self, wfn, newstatus): |
|
420 | def _updatestatus(self, wfn, newstatus): | |
352 | ''' |
|
421 | ''' | |
353 |
Update the stored status of a file |
|
422 | Update the stored status of a file. | |
354 |
|
423 | |||
355 | newstatus: - char in (statuskeys + 'ni'), new status to apply. |
|
424 | newstatus: - char in (statuskeys + 'ni'), new status to apply. | |
356 | - or None, to stop tracking wfn |
|
425 | - or None, to stop tracking wfn | |
357 | ''' |
|
426 | ''' | |
358 | root, fn = split(wfn) |
|
427 | root, fn = split(wfn) | |
359 |
d = self.dir( |
|
428 | d = self.tree.dir(root) | |
360 |
|
429 | |||
361 | oldstatus = d.get(fn) |
|
430 | oldstatus = d.files.get(fn) | |
362 | # oldstatus can be either: |
|
431 | # oldstatus can be either: | |
363 | # - None : fn is new |
|
432 | # - None : fn is new | |
364 | # - a char in statuskeys: fn is a (tracked) file |
|
433 | # - a char in statuskeys: fn is a (tracked) file | |
365 | # - a dict: fn is a directory |
|
|||
366 | isdir = isinstance(oldstatus, dict) |
|
|||
367 |
|
434 | |||
368 | if self.ui.debugflag and oldstatus != newstatus: |
|
435 | if self.ui.debugflag and oldstatus != newstatus: | |
369 | if isdir: |
|
436 | self.ui.note(_('status: %r %s -> %s\n') % | |
370 | self.ui.note(_('status: %r dir(%d) -> %s\n') % |
|
|||
371 | (wfn, len(oldstatus), newstatus)) |
|
|||
372 | else: |
|
|||
373 | self.ui.note(_('status: %r %s -> %s\n') % |
|
|||
374 | (wfn, oldstatus, newstatus)) |
|
437 | (wfn, oldstatus, newstatus)) | |
375 | if not isdir: |
|
438 | ||
376 |
|
|
439 | if oldstatus and oldstatus in self.statuskeys \ | |
377 |
|
|
440 | and oldstatus != newstatus: | |
378 |
|
|
441 | del self.statustrees[oldstatus].dir(root).files[fn] | |
379 |
|
|
442 | if newstatus and newstatus != 'i': | |
380 |
|
|
443 | d.files[fn] = newstatus | |
381 |
|
|
444 | if newstatus in self.statuskeys: | |
382 |
|
|
445 | dd = self.statustrees[newstatus].dir(root) | |
383 |
|
|
446 | if oldstatus != newstatus or fn not in dd.files: | |
384 |
|
|
447 | dd.files[fn] = newstatus | |
385 |
|
|
448 | else: | |
386 |
|
|
449 | d.files.pop(fn, None) | |
387 |
|
450 | |||
388 |
|
451 | |||
389 | def check_deleted(self, key): |
|
452 | def check_deleted(self, key): | |
390 | # Files that had been deleted but were present in the dirstate |
|
453 | # Files that had been deleted but were present in the dirstate | |
391 | # may have vanished from the dirstate; we must clean them up. |
|
454 | # may have vanished from the dirstate; we must clean them up. | |
392 | nuke = [] |
|
455 | nuke = [] | |
393 |
for wfn, ignore in self. |
|
456 | for wfn, ignore in self.statustrees[key].walk(key): | |
394 | if wfn not in self.repo.dirstate: |
|
457 | if wfn not in self.repo.dirstate: | |
395 | nuke.append(wfn) |
|
458 | nuke.append(wfn) | |
396 | for wfn in nuke: |
|
459 | for wfn in nuke: | |
397 | root, fn = split(wfn) |
|
460 | root, fn = split(wfn) | |
398 |
del self. |
|
461 | del self.statustrees[key].dir(root).files[fn] | |
399 |
del self.dir( |
|
462 | del self.tree.dir(root).files[fn] | |
400 |
|
463 | |||
401 | def scan(self, topdir=''): |
|
464 | def scan(self, topdir=''): | |
402 | ds = self.repo.dirstate._map.copy() |
|
465 | ds = self.repo.dirstate._map.copy() | |
@@ -438,18 +501,6 b' class repowatcher(pollable):' | |||||
438 | self.scan() |
|
501 | self.scan() | |
439 | self.ui.note(_('%s end dirstate reload\n') % self.event_time()) |
|
502 | self.ui.note(_('%s end dirstate reload\n') % self.event_time()) | |
440 |
|
503 | |||
441 | def walk(self, states, tree, prefix=''): |
|
|||
442 | # This is the "inner loop" when talking to the client. |
|
|||
443 |
|
||||
444 | for name, val in tree.iteritems(): |
|
|||
445 | path = join(prefix, name) |
|
|||
446 | try: |
|
|||
447 | if val in states: |
|
|||
448 | yield path, val |
|
|||
449 | except TypeError: |
|
|||
450 | for p in self.walk(states, val, path): |
|
|||
451 | yield p |
|
|||
452 |
|
||||
453 | def update_hgignore(self): |
|
504 | def update_hgignore(self): | |
454 | # An update of the ignore file can potentially change the |
|
505 | # An update of the ignore file can potentially change the | |
455 | # states of all unknown and ignored files. |
|
506 | # states of all unknown and ignored files. | |
@@ -537,9 +588,10 b' class repowatcher(pollable):' | |||||
537 | (self.event_time(), wpath)) |
|
588 | (self.event_time(), wpath)) | |
538 |
|
589 | |||
539 | if evt.mask & inotify.IN_ISDIR: |
|
590 | if evt.mask & inotify.IN_ISDIR: | |
540 |
tree = self.dir( |
|
591 | tree = self.tree.dir(wpath) | |
541 |
for wfn, ignore in |
|
592 | todelete = [wfn for wfn, ignore in tree.walk('?')] | |
542 | self.deletefile(join(wpath, wfn), '?') |
|
593 | for fn in todelete: | |
|
594 | self.deletefile(fn, '?') | |||
543 | self.scan(wpath) |
|
595 | self.scan(wpath) | |
544 | else: |
|
596 | else: | |
545 | self.deleted(wpath) |
|
597 | self.deleted(wpath) | |
@@ -669,18 +721,13 b' class server(pollable):' | |||||
669 |
|
721 | |||
670 | if not names: |
|
722 | if not names: | |
671 | def genresult(states, tree): |
|
723 | def genresult(states, tree): | |
672 |
for fn, state in |
|
724 | for fn, state in tree.walk(states): | |
673 | yield fn |
|
725 | yield fn | |
674 | else: |
|
726 | else: | |
675 | def genresult(states, tree): |
|
727 | def genresult(states, tree): | |
676 | for fn in names: |
|
728 | for fn in names: | |
677 |
|
|
729 | for f in tree.lookup(states, fn): | |
678 |
|
|
730 | yield f | |
679 | if l in states: |
|
|||
680 | yield fn |
|
|||
681 | except TypeError: |
|
|||
682 | for f, s in self.repowatcher.walk(states, l, fn): |
|
|||
683 | yield f |
|
|||
684 |
|
731 | |||
685 | return ['\0'.join(r) for r in [ |
|
732 | return ['\0'.join(r) for r in [ | |
686 | genresult('l', self.repowatcher.statustrees['l']), |
|
733 | genresult('l', self.repowatcher.statustrees['l']), |
General Comments 0
You need to be logged in to leave comments.
Login now