Forráskód Böngészése

Add a RefsContainer.watch interface. Fixes #751

Jelmer Vernooij 4 éve
szülő
commit
013171617f
2 módosított fájl, 53 hozzáadás és 11 törlés
  1. 52 8
      dulwich/refs.py
  2. 1 3
      dulwich/tests/test_refs.py

+ 52 - 8
dulwich/refs.py

@@ -393,8 +393,7 @@ class RefsContainer(object):
     def watch(self):
         """Watch for changes to the refs in this container.
 
-        Returns a context manager that yields tuples with (refname, old_sha,
-        new_sha)
+        Returns a context manager that yields tuples with (refname, new_sha)
         """
         raise NotImplementedError(self.watch)
 
@@ -443,9 +442,9 @@ class DictRefsContainer(RefsContainer):
     def get_packed_refs(self):
         return {}
 
-    def _notify(self, ref, oldsha, newsha):
+    def _notify(self, ref, newsha):
         for watcher in self._watchers:
-            watcher._notify((ref, oldsha, newsha))
+            watcher._notify((ref, newsha))
 
     def watch(self):
         return _DictRefsWatcher(self)
@@ -455,7 +454,7 @@ class DictRefsContainer(RefsContainer):
         old = self.follow(name)[-1]
         new = SYMREF + other
         self._refs[name] = new
-        self._notify(name, old, new)
+        self._notify(name, new)
         self._log(name, old, new, committer=committer, timestamp=timestamp,
                   timezone=timezone, message=message)
 
@@ -468,7 +467,7 @@ class DictRefsContainer(RefsContainer):
             self._check_refname(realname)
             old = self._refs.get(realname)
             self._refs[realname] = new_ref
-            self._notify(realname, old, new_ref)
+            self._notify(realname, new_ref)
             self._log(realname, old, new_ref, committer=committer,
                       timestamp=timestamp, timezone=timezone, message=message)
         return True
@@ -478,7 +477,7 @@ class DictRefsContainer(RefsContainer):
         if name in self._refs:
             return False
         self._refs[name] = ref
-        self._notify(name, None, ref)
+        self._notify(name, ref)
         self._log(name, None, ref, committer=committer, timestamp=timestamp,
                   timezone=timezone, message=message)
         return True
@@ -492,7 +491,7 @@ class DictRefsContainer(RefsContainer):
         except KeyError:
             pass
         else:
-            self._notify(name, old, None)
+            self._notify(name, None)
             self._log(name, old, None, committer=committer,
                       timestamp=timestamp, timezone=timezone, message=message)
         return True
@@ -546,6 +545,47 @@ class InfoRefsContainer(RefsContainer):
             return self._refs[name]
 
 
+class _InotifyRefsWatcher(object):
+
+    def __init__(self, path):
+        import pyinotify
+        from queue import Queue
+        self.path = os.fsdecode(path)
+        self.manager = pyinotify.WatchManager()
+        self.manager.add_watch(
+            self.path, pyinotify.IN_DELETE |
+            pyinotify.IN_CLOSE_WRITE | pyinotify.IN_MOVED_TO, rec=True,
+            auto_add=True)
+
+        self.notifier = pyinotify.ThreadedNotifier(
+            self.manager, default_proc_fun=self._notify)
+        self.queue = Queue()
+
+    def _notify(self, event):
+        if event.dir:
+            return
+        if event.pathname.endswith('.lock'):
+            return
+        ref = os.fsencode(os.path.relpath(event.pathname, self.path))
+        if event.maskname == 'IN_DELETE':
+            self.queue.put_nowait((ref, None))
+        elif event.maskname in ('IN_CLOSE_WRITE', 'IN_MOVED_TO'):
+            with open(event.pathname, 'rb') as f:
+                sha = f.readline().rstrip(b'\n\r')
+                self.queue.put_nowait((ref, sha))
+
+    def __next__(self):
+        return self.queue.get()
+
+    def __enter__(self):
+        self.notifier.start()
+        return self
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        self.notifier.stop()
+        return False
+
+
 class DiskRefsContainer(RefsContainer):
     """Refs container that reads refs from disk."""
 
@@ -892,6 +932,10 @@ class DiskRefsContainer(RefsContainer):
 
         return True
 
+    def watch(self):
+        import pyinotify  # noqa: F401
+        return _InotifyRefsWatcher(self.path)
+
 
 def _split_ref_line(line):
     """Split a single ref line into a tuple of SHA1 and name."""

+ 1 - 3
dulwich/tests/test_refs.py

@@ -333,20 +333,18 @@ class RefsContainerTests(object):
                 b'48d01bd4b77fed026b154d16493e5deab78f02ec')
             change = next(watcher)
             self.assertEqual(
-                (b'refs/remotes/origin/other', None,
+                (b'refs/remotes/origin/other',
                  b'48d01bd4b77fed026b154d16493e5deab78f02ec'), change)
             self._refs[b'refs/remotes/origin/other'] = (
                 b'48d01bd4b77fed026b154d16493e5deab78f02ed')
             change = next(watcher)
             self.assertEqual(
                 (b'refs/remotes/origin/other',
-                 b'48d01bd4b77fed026b154d16493e5deab78f02ec',
                  b'48d01bd4b77fed026b154d16493e5deab78f02ed'), change)
             del self._refs[b'refs/remotes/origin/other']
             change = next(watcher)
             self.assertEqual(
                 (b'refs/remotes/origin/other',
-                 b'48d01bd4b77fed026b154d16493e5deab78f02ed',
                  None), change)