Make sure none of the ancestor folders of a new refname is in packed-refs
authorKS Chan <mrkschan@gmail.com>
Wed, 19 Dec 2018 13:41:01 +0000 (21:41 +0800)
committerJelmer Vernooij <jelmer@jelmer.uk>
Sat, 29 Dec 2018 16:26:15 +0000 (16:26 +0000)
dulwich/refs.py
dulwich/tests/test_refs.py

index 000bb515dba4de87af9bb2fb802157a090713088..88dea193d85b3c74053c1f78286a910b0ec0523a 100644 (file)
@@ -687,6 +687,16 @@ class DiskRefsContainer(RefsContainer):
         except (KeyError, IndexError):
             realname = name
         filename = self.refpath(realname)
+
+        # make sure none of the ancestor folders is in packed refs
+        probe_ref = os.path.dirname(realname)
+        packed_refs = self.get_packed_refs()
+        while probe_ref:
+            if packed_refs.get(probe_ref, None) is not None:
+                raise OSError(errno.ENOTDIR,
+                              'Not a directory: {}'.format(filename))
+            probe_ref = os.path.dirname(probe_ref)
+
         ensure_dir_exists(os.path.dirname(filename))
         with GitFile(filename, 'wb') as f:
             if old_ref is not None:
index 2898f1a3da880d28bfb3d0c977d9e322734945b3..fb4e2cff090e7943fcf19f0c4064eaa60a6fdead 100644 (file)
@@ -340,6 +340,27 @@ class DiskRefsContainerTests(RefsContainerTests, TestCase):
                          f.read()[:40])
         f.close()
 
+        self.assertRaises(
+            OSError, self._refs.__setitem__,
+            b'refs/some/ref/sub', b'42d06bd4b77fed026b154d16493e5deab78f02ec')
+
+    def test_setitem_packed(self):
+        # It's allowed to set a new ref on a packed ref, the new ref will be placed outside on refs/
+        self._refs[b'refs/heads/packed'] = (
+            b'3ec9c43c84ff242e3ef4a9fc5bc111fd780a76a8'
+        )
+        f = open(os.path.join(self._refs.path, b'refs', b'heads', b'packed'),
+                 'rb')
+        self.assertEqual(b'3ec9c43c84ff242e3ef4a9fc5bc111fd780a76a8',
+                         f.read()[:40])
+        f.close()
+
+        self._refs._packed_refs[b'refs/some/packed'] = b'42d06bd4b77fed026b154d16493e5deab78f02ec'
+        self.assertRaises(
+            OSError, self._refs.__setitem__,
+            b'refs/some/packed/sub',
+            b'42d06bd4b77fed026b154d16493e5deab78f02ec')
+
     def test_setitem_symbolic(self):
         ones = b'1' * 40
         self._refs[b'HEAD'] = ones