Dulwich.io dulwich / 0131716
Add a RefsContainer.watch interface. Fixes #751 Jelmer Vernooń≥ 10 days ago
2 changed file(s) with 53 addition(s) and 11 deletion(s). Raw diff Collapse all Expand all
392392 def watch(self):
393393 """Watch for changes to the refs in this container.
394394
395 Returns a context manager that yields tuples with (refname, old_sha,
396 new_sha)
395 Returns a context manager that yields tuples with (refname, new_sha)
397396 """
398397 raise NotImplementedError(self.watch)
399398
442441 def get_packed_refs(self):
443442 return {}
444443
445 def _notify(self, ref, oldsha, newsha):
444 def _notify(self, ref, newsha):
446445 for watcher in self._watchers:
447 watcher._notify((ref, oldsha, newsha))
446 watcher._notify((ref, newsha))
448447
449448 def watch(self):
450449 return _DictRefsWatcher(self)
454453 old = self.follow(name)[-1]
455454 new = SYMREF + other
456455 self._refs[name] = new
457 self._notify(name, old, new)
456 self._notify(name, new)
458457 self._log(name, old, new, committer=committer, timestamp=timestamp,
459458 timezone=timezone, message=message)
460459
467466 self._check_refname(realname)
468467 old = self._refs.get(realname)
469468 self._refs[realname] = new_ref
470 self._notify(realname, old, new_ref)
469 self._notify(realname, new_ref)
471470 self._log(realname, old, new_ref, committer=committer,
472471 timestamp=timestamp, timezone=timezone, message=message)
473472 return True
477476 if name in self._refs:
478477 return False
479478 self._refs[name] = ref
480 self._notify(name, None, ref)
479 self._notify(name, ref)
481480 self._log(name, None, ref, committer=committer, timestamp=timestamp,
482481 timezone=timezone, message=message)
483482 return True
491490 except KeyError:
492491 pass
493492 else:
494 self._notify(name, old, None)
493 self._notify(name, None)
495494 self._log(name, old, None, committer=committer,
496495 timestamp=timestamp, timezone=timezone, message=message)
497496 return True
543542 return self._peeled[name]
544543 except KeyError:
545544 return self._refs[name]
545
546
547 class _InotifyRefsWatcher(object):
548
549 def __init__(self, path):
550 import pyinotify
551 from queue import Queue
552 self.path = os.fsdecode(path)
553 self.manager = pyinotify.WatchManager()
554 self.manager.add_watch(
555 self.path, pyinotify.IN_DELETE |
556 pyinotify.IN_CLOSE_WRITE | pyinotify.IN_MOVED_TO, rec=True,
557 auto_add=True)
558
559 self.notifier = pyinotify.ThreadedNotifier(
560 self.manager, default_proc_fun=self._notify)
561 self.queue = Queue()
562
563 def _notify(self, event):
564 if event.dir:
565 return
566 if event.pathname.endswith('.lock'):
567 return
568 ref = os.fsencode(os.path.relpath(event.pathname, self.path))
569 if event.maskname == 'IN_DELETE':
570 self.queue.put_nowait((ref, None))
571 elif event.maskname in ('IN_CLOSE_WRITE', 'IN_MOVED_TO'):
572 with open(event.pathname, 'rb') as f:
573 sha = f.readline().rstrip(b'\n\r')
574 self.queue.put_nowait((ref, sha))
575
576 def __next__(self):
577 return self.queue.get()
578
579 def __enter__(self):
580 self.notifier.start()
581 return self
582
583 def __exit__(self, exc_type, exc_val, exc_tb):
584 self.notifier.stop()
585 return False
546586
547587
548588 class DiskRefsContainer(RefsContainer):
891931
892932 return True
893933
934 def watch(self):
935 import pyinotify # noqa: F401
936 return _InotifyRefsWatcher(self.path)
937
894938
895939 def _split_ref_line(line):
896940 """Split a single ref line into a tuple of SHA1 and name."""
332332 b'48d01bd4b77fed026b154d16493e5deab78f02ec')
333333 change = next(watcher)
334334 self.assertEqual(
335 (b'refs/remotes/origin/other', None,
335 (b'refs/remotes/origin/other',
336336 b'48d01bd4b77fed026b154d16493e5deab78f02ec'), change)
337337 self._refs[b'refs/remotes/origin/other'] = (
338338 b'48d01bd4b77fed026b154d16493e5deab78f02ed')
339339 change = next(watcher)
340340 self.assertEqual(
341341 (b'refs/remotes/origin/other',
342 b'48d01bd4b77fed026b154d16493e5deab78f02ec',
343342 b'48d01bd4b77fed026b154d16493e5deab78f02ed'), change)
344343 del self._refs[b'refs/remotes/origin/other']
345344 change = next(watcher)
346345 self.assertEqual(
347346 (b'refs/remotes/origin/other',
348 b'48d01bd4b77fed026b154d16493e5deab78f02ed',
349347 None), change)
350348
351349