Пример #1
0
    def test_symlink(self):
        repo_dir = tempfile.mkdtemp()
        self.addCleanup(shutil.rmtree, repo_dir)
        with Repo.init(repo_dir) as repo:

            # Populate repo
            filed = Blob.from_string(b'file d')
            filee = Blob.from_string(b'd')

            tree = Tree()
            tree[b'c/d'] = (stat.S_IFREG | 0o644, filed.id)
            tree[b'c/e'] = (stat.S_IFLNK, filee.id)  # symlink

            repo.object_store.add_objects([(o, None)
                for o in [filed, filee, tree]])

            build_index_from_tree(repo.path, repo.index_path(),
                    repo.object_store, tree.id)

            # Verify index entries
            index = repo.open_index()

            # symlink to d
            epath = os.path.join(repo.path, 'c', 'e')
            self.assertTrue(os.path.exists(epath))
            self.assertReasonableIndexEntry(
                index[b'c/e'], stat.S_IFLNK,
                0 if sys.platform == 'win32' else 1,
                filee.id)
            self.assertFileContents(epath, 'd', symlink=True)
Пример #2
0
    def _tree_from_structure(self, structure):
        # TODO : Support directories
        tree = Tree()

        for file_info in structure:

            # str only
            try:
                data = file_info["data"].encode("ascii")
                name = file_info["name"].encode("ascii")
                mode = file_info["mode"]
            except:
                # Skip file on encoding errors
                continue

            blob = Blob()

            blob.data = data

            # Store file's contents
            self.repo.object_store.add_object(blob)

            # Add blob entry
            tree.add(name, mode, blob.id)

        # Store tree
        self.repo.object_store.add_object(tree)

        return tree.id
Пример #3
0
    def stage(self, paths):
        """Stage a set of paths.

        :param paths: List of paths, relative to the repository path
        """
        from dulwich.index import cleanup_mode
        index = self.open_index()
        for path in paths:
            blob = Blob()
            try:
                st = os.stat(path)
            except OSError:
                # File no longer exists
                del index[path]
            else:
                f = open(path, 'rb')
                try:
                    blob.data = f.read()
                finally:
                    f.close()
                self.object_store.add_object(blob)
                # XXX: Cleanup some of the other file properties as well?
                index[path] = (st.st_ctime, st.st_mtime, st.st_dev, st.st_ino,
                    cleanup_mode(st.st_mode), st.st_uid, st.st_gid, st.st_size,
                    blob.id, 0)
        index.write()
Пример #4
0
    def stage(self, paths):
        """Stage a set of paths.

        :param paths: List of paths, relative to the repository path
        """
        if isinstance(paths, basestring):
            paths = [paths]
        from dulwich.index import index_entry_from_stat
        index = self.open_index()
        for path in paths:
            full_path = os.path.join(self.path, path)
            try:
                st = os.stat(full_path)
            except OSError:
                # File no longer exists
                try:
                    del index[path]
                except KeyError:
                    pass # already removed
            else:
                blob = Blob()
                f = open(full_path, 'rb')
                try:
                    blob.data = f.read()
                finally:
                    f.close()
                self.object_store.add_object(blob)
                index[path] = index_entry_from_stat(st, blob.id, 0)
        index.write()
Пример #5
0
 def test_delta_medium_object(self):
     # This tests an object set that will have a copy operation
     # 2**20 in size.
     with self.get_pack(pack1_sha) as orig_pack:
         orig_blob = orig_pack[a_sha]
         new_blob = Blob()
         new_blob.data = orig_blob.data + (b'x' * 2 ** 20)
         new_blob_2 = Blob()
         new_blob_2.data = new_blob.data + b'y'
         all_to_pack = list(orig_pack.pack_tuples()) + [(new_blob, None),
                                                        (new_blob_2, None)]
     pack_path = os.path.join(self._tempdir, b'pack_with_deltas')
     write_pack(pack_path, all_to_pack, deltify=True)
     output = run_git_or_fail(['verify-pack', '-v', pack_path])
     self.assertEqual(set(x[0].id for x in all_to_pack),
                      _git_verify_pack_object_list(output))
     # We specifically made a new blob that should be a delta
     # against the blob a_sha, so make sure we really got only 3
     # non-delta objects:
     got_non_delta = int(_NON_DELTA_RE.search(output).group('non_delta'))
     self.assertEqual(
         3, got_non_delta,
         'Expected 3 non-delta objects, got %d' % got_non_delta)
     # We expect one object to have a delta chain length of two
     # (new_blob_2), so let's verify that actually happens:
     self.assertIn(b'chain length = 2', output)
Пример #6
0
    def test_git_dir(self):
        repo_dir = tempfile.mkdtemp()
        self.addCleanup(shutil.rmtree, repo_dir)
        with Repo.init(repo_dir) as repo:

            # Populate repo
            filea = Blob.from_string(b'file a')
            filee = Blob.from_string(b'd')

            tree = Tree()
            tree[b'.git/a'] = (stat.S_IFREG | 0o644, filea.id)
            tree[b'c/e'] = (stat.S_IFREG | 0o644, filee.id)

            repo.object_store.add_objects([(o, None)
                for o in [filea, filee, tree]])

            build_index_from_tree(repo.path, repo.index_path(),
                    repo.object_store, tree.id)

            # Verify index entries
            index = repo.open_index()
            self.assertEqual(len(index), 1)

            # filea
            apath = os.path.join(repo.path, '.git', 'a')
            self.assertFalse(os.path.exists(apath))

            # filee
            epath = os.path.join(repo.path, 'c', 'e')
            self.assertTrue(os.path.exists(epath))
            self.assertReasonableIndexEntry(index[b'c/e'],
                stat.S_IFREG | 0o644, 1, filee.id)
            self.assertFileContents(epath, b'd')
Пример #7
0
 def test_delta_large_object(self):
     # This tests an object set that will have a copy operation
     # 2**25 in size. This is a copy large enough that it requires
     # two copy operations in git's binary delta format.
     raise SkipTest('skipping slow, large test')
     with self.get_pack(pack1_sha) as orig_pack:
         orig_blob = orig_pack[a_sha]
         new_blob = Blob()
         new_blob.data = 'big blob' + ('x' * 2 ** 25)
         new_blob_2 = Blob()
         new_blob_2.data = new_blob.data + 'y'
         all_to_pack = list(orig_pack.pack_tuples()) + [(new_blob, None),
                                                        (new_blob_2, None)]
     pack_path = os.path.join(self._tempdir, "pack_with_deltas")
     write_pack(pack_path, all_to_pack, deltify=True)
     output = run_git_or_fail(['verify-pack', '-v', pack_path])
     self.assertEqual(set(x[0].id for x in all_to_pack),
                      _git_verify_pack_object_list(output))
     # We specifically made a new blob that should be a delta
     # against the blob a_sha, so make sure we really got only 4
     # non-delta objects:
     got_non_delta = int(_NON_DELTA_RE.search(output).group('non_delta'))
     self.assertEqual(
         4, got_non_delta,
         'Expected 4 non-delta objects, got %d' % got_non_delta)
Пример #8
0
    def test_commit_with_change(self):
        a = Blob.from_string(b"The Foo\n")
        ta = Tree()
        ta.add(b"somename", 0o100644, a.id)
        ca = make_commit(tree=ta.id)
        b = Blob.from_string(b"The Bar\n")
        tb = Tree()
        tb.add(b"somename", 0o100644, b.id)
        cb = make_commit(tree=tb.id, parents=[ca.id])
        self.repo.object_store.add_objects(
            [(a, None), (b, None), (ta, None), (tb, None),
             (ca, None), (cb, None)])
        outstream = StringIO()
        porcelain.show(self.repo.path, objects=[cb.id], outstream=outstream)
        self.assertMultiLineEqual(outstream.getvalue(), """\
--------------------------------------------------
commit: 2c6b6c9cb72c130956657e1fdae58e5b103744fa
Author: Test Author <*****@*****.**>
Committer: Test Committer <*****@*****.**>
Date:   Fri Jan 01 2010 00:00:00 +0000

Test message.

diff --git a/somename b/somename
index ea5c7bf..fd38bcb 100644
--- a/somename
+++ b/somename
@@ -1 +1 @@
-The Foo
+The Bar
""")
Пример #9
0
 def test_set_chunks(self):
     b = Blob()
     b.chunked = [b'te', b'st', b' 5\n']
     self.assertEqual(b'test 5\n', b.data)
     b.chunked = [b'te', b'st', b' 6\n']
     self.assertEqual(b'test 6\n', b.as_raw_string())
     self.assertEqual(b'test 6\n', bytes(b))
Пример #10
0
 def test_object_diff_bin_blob_force(self):
     f = StringIO()
     # Prepare two slightly different PNG headers
     b1 = Blob.from_string(
         "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52"
         "\x00\x00\x01\xd5\x00\x00\x00\x9f\x08\x04\x00\x00\x00\x05\x04\x8b")
     b2 = Blob.from_string(
         "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52"
         "\x00\x00\x01\xd5\x00\x00\x00\x9f\x08\x03\x00\x00\x00\x98\xd3\xb3")
     store = MemoryObjectStore()
     store.add_objects([(b1, None), (b2, None)])
     write_object_diff(f, store, ('foo.png', 0644, b1.id),
                                 ('bar.png', 0644, b2.id), diff_binary=True)
     self.assertEqual([
         'diff --git a/foo.png b/bar.png',
         'index f73e47d..06364b7 644',
         '--- a/foo.png',
         '+++ b/bar.png',
         '@@ -1,4 +1,4 @@',
         ' \x89PNG',
         ' \x1a',
         ' \x00\x00\x00',
         '-IHDR\x00\x00\x01\xd5\x00\x00\x00\x9f\x08\x04\x00\x00\x00\x05\x04\x8b',
         '\\ No newline at end of file',
         '+IHDR\x00\x00\x01\xd5\x00\x00\x00\x9f\x08\x03\x00\x00\x00\x98\xd3\xb3',
         '\\ No newline at end of file'
         ], f.getvalue().splitlines())
Пример #11
0
    def test_emit_commit(self):
        b = Blob()
        b.data = "FOO"
        t = Tree()
        t.add(stat.S_IFREG | 0644, "foo", b.id)
        c = Commit()
        c.committer = c.author = "Jelmer <jelmer@host>"
        c.author_time = c.commit_time = 1271345553
        c.author_timezone = c.commit_timezone = 0
        c.message = "msg"
        c.tree = t.id
        self.store.add_objects([(b, None), (t, None), (c, None)])
        self.fastexporter.emit_commit(c, "refs/heads/master")
        self.assertEquals("""blob
mark :1
data 3
FOO
commit refs/heads/master
mark :2
author Jelmer <jelmer@host> 1271345553 +0000
committer Jelmer <jelmer@host> 1271345553 +0000
data 3
msg
M 644 1 foo
""", self.stream.getvalue())
Пример #12
0
 def content(mode, hexsha):
     if hexsha is None:
         return Blob.from_string(b'')
     elif S_ISGITLINK(mode):
         return Blob.from_string(b"Submodule commit " + hexsha + b"\n")
     else:
         return store[hexsha]
Пример #13
0
 def test_object_diff_bin_blob_force(self):
     f = BytesIO()
     # Prepare two slightly different PNG headers
     b1 = Blob.from_string(
         b"\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52"
         b"\x00\x00\x01\xd5\x00\x00\x00\x9f\x08\x04\x00\x00\x00\x05\x04\x8b"
     )
     b2 = Blob.from_string(
         b"\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52"
         b"\x00\x00\x01\xd5\x00\x00\x00\x9f\x08\x03\x00\x00\x00\x98\xd3\xb3"
     )
     store = MemoryObjectStore()
     store.add_objects([(b1, None), (b2, None)])
     write_object_diff(f, store, (b"foo.png", 0o644, b1.id), (b"bar.png", 0o644, b2.id), diff_binary=True)
     self.assertEqual(
         [
             b"diff --git a/foo.png b/bar.png",
             b"index f73e47d..06364b7 644",
             b"--- a/foo.png",
             b"+++ b/bar.png",
             b"@@ -1,4 +1,4 @@",
             b" \x89PNG",
             b" \x1a",
             b" \x00\x00\x00",
             b"-IHDR\x00\x00\x01\xd5\x00\x00\x00\x9f\x08\x04\x00\x00\x00\x05\x04\x8b",
             b"\\ No newline at end of file",
             b"+IHDR\x00\x00\x01\xd5\x00\x00\x00\x9f\x08\x03\x00\x00\x00\x98\xd3\xb3",
             b"\\ No newline at end of file",
         ],
         f.getvalue().splitlines(),
     )
Пример #14
0
    def test_git_dir(self):
        if os.name != "posix":
            self.skipTest("test depends on POSIX shell")

        repo_dir = tempfile.mkdtemp()
        repo = Repo.init(repo_dir)
        self.addCleanup(shutil.rmtree, repo_dir)

        # Populate repo
        filea = Blob.from_string("file a")
        filee = Blob.from_string("d")

        tree = Tree()
        tree[".git/a"] = (stat.S_IFREG | 0o644, filea.id)
        tree["c/e"] = (stat.S_IFREG | 0o644, filee.id)

        repo.object_store.add_objects([(o, None) for o in [filea, filee, tree]])

        build_index_from_tree(repo.path, repo.index_path(), repo.object_store, tree.id)

        # Verify index entries
        index = repo.open_index()
        self.assertEqual(len(index), 1)

        # filea
        apath = os.path.join(repo.path, ".git", "a")
        self.assertFalse(os.path.exists(apath))

        # filee
        epath = os.path.join(repo.path, "c", "e")
        self.assertTrue(os.path.exists(epath))
        self.assertReasonableIndexEntry(index["c/e"], stat.S_IFREG | 0o644, 1, filee.id)
        self.assertFileContents(epath, "d")
Пример #15
0
    def stage(self, paths):
        """Stage a set of paths.

        :param paths: List of paths, relative to the repository path
        """
        from dulwich.index import cleanup_mode
        index = self.open_index()
        for path in paths:
            full_path = os.path.join(self.path, path)
            blob = Blob()
            try:
                st = os.stat(full_path)
            except OSError:
                # File no longer exists
                try:
                    del index[path]
                except KeyError:
                    pass  # Doesn't exist in the index either
            else:
                with open(full_path, 'rb') as f:
                    blob.data = f.read()
                self.object_store.add_object(blob)
                # XXX: Cleanup some of the other file properties as well?
                index[path] = (st.st_ctime, st.st_mtime, st.st_dev, st.st_ino,
                    cleanup_mode(st.st_mode), st.st_uid, st.st_gid, st.st_size,
                    blob.id, 0)
        index.write()
Пример #16
0
def blob_from_path(basepath, path):
    """Returns a tuple of (sha_id, mode, blob)
    """
    fullpath = os.path.join(basepath, path)
    with open(fullpath, 'rb') as working_file:
        blob = Blob()
        blob.data = working_file.read()
    return (path, os.stat(fullpath).st_mode, blob)
Пример #17
0
 def test_simple_delta(self):
     b1 = Blob.from_string("a" * 101)
     b2 = Blob.from_string("a" * 100)
     delta = create_delta(b1.as_raw_string(), b2.as_raw_string())
     self.assertEqual([
         (b1.type_num, b1.sha().digest(), None, b1.as_raw_string()),
         (b2.type_num, b2.sha().digest(), b1.sha().digest(), delta)
         ],
         list(deltify_pack_objects([(b1, ""), (b2, "")])))
Пример #18
0
 def test_single_blob(self):
     blob = Blob()
     blob.data = b"foo"
     self.store.add_object(blob)
     blobs = [(b"bla", blob.id, stat.S_IFREG)]
     rootid = commit_tree(self.store, blobs)
     self.assertEqual(rootid, b"1a1e80437220f9312e855c37ac4398b68e5c1d50")
     self.assertEqual((stat.S_IFREG, blob.id), self.store[rootid][b"bla"])
     self.assertEqual(set([rootid, blob.id]), set(self.store._data.keys()))
Пример #19
0
 def test_git_dir(self):
     obj = Tree()
     a = Blob()
     a.data = b"foo"
     obj.add(b".git", 0o100644, a.id)
     self.repo.object_store.add_objects(
         [(a, None), (obj, None)])
     self.assertEqual(
             [(obj.id, 'invalid name .git')],
             [(sha, str(e)) for (sha, e) in porcelain.fsck(self.repo)])
Пример #20
0
    def test_nonempty(self):
        if os.name != "posix":
            self.skipTest("test depends on POSIX shell")

        repo_dir = tempfile.mkdtemp()
        repo = Repo.init(repo_dir)
        self.addCleanup(shutil.rmtree, repo_dir)

        # Populate repo
        filea = Blob.from_string("file a")
        fileb = Blob.from_string("file b")
        filed = Blob.from_string("file d")
        filee = Blob.from_string("d")

        tree = Tree()
        tree["a"] = (stat.S_IFREG | 0o644, filea.id)
        tree["b"] = (stat.S_IFREG | 0o644, fileb.id)
        tree["c/d"] = (stat.S_IFREG | 0o644, filed.id)
        tree["c/e"] = (stat.S_IFLNK, filee.id)  # symlink

        repo.object_store.add_objects([(o, None) for o in [filea, fileb, filed, filee, tree]])

        build_index_from_tree(repo.path, repo.index_path(), repo.object_store, tree.id)

        # Verify index entries
        index = repo.open_index()
        self.assertEqual(len(index), 4)

        # filea
        apath = os.path.join(repo.path, "a")
        self.assertTrue(os.path.exists(apath))
        self.assertReasonableIndexEntry(index["a"], stat.S_IFREG | 0o644, 6, filea.id)
        self.assertFileContents(apath, "file a")

        # fileb
        bpath = os.path.join(repo.path, "b")
        self.assertTrue(os.path.exists(bpath))
        self.assertReasonableIndexEntry(index["b"], stat.S_IFREG | 0o644, 6, fileb.id)
        self.assertFileContents(bpath, "file b")

        # filed
        dpath = os.path.join(repo.path, "c", "d")
        self.assertTrue(os.path.exists(dpath))
        self.assertReasonableIndexEntry(index["c/d"], stat.S_IFREG | 0o644, 6, filed.id)
        self.assertFileContents(dpath, "file d")

        # symlink to d
        epath = os.path.join(repo.path, "c", "e")
        self.assertTrue(os.path.exists(epath))
        self.assertReasonableIndexEntry(index["c/e"], stat.S_IFLNK, 1, filee.id)
        self.assertFileContents(epath, "d", symlink=True)

        # Verify no extra files
        self.assertEqual([".git", "a", "b", "c"], sorted(os.listdir(repo.path)))
        self.assertEqual(["d", "e"], sorted(os.listdir(os.path.join(repo.path, "c"))))
Пример #21
0
 def test_nested(self):
     blob = Blob()
     blob.data = "foo"
     self.store.add_object(blob)
     blobs = [("bla/bar", blob.id, stat.S_IFREG)]
     rootid = commit_tree(self.store, blobs)
     self.assertEqual(rootid, "d92b959b216ad0d044671981196781b3258fa537")
     dirid = self.store[rootid]["bla"][1]
     self.assertEqual(dirid, "c1a1deb9788150829579a8b4efa6311e7b638650")
     self.assertEqual((stat.S_IFDIR, dirid), self.store[rootid]["bla"])
     self.assertEqual((stat.S_IFREG, blob.id), self.store[dirid]["bar"])
     self.assertEqual(set([rootid, dirid, blob.id]), set(self.store._data.keys()))
Пример #22
0
    def test_nonempty(self):
        repo_dir = tempfile.mkdtemp()
        self.addCleanup(shutil.rmtree, repo_dir)
        with Repo.init(repo_dir) as repo:

            # Populate repo
            filea = Blob.from_string(b'file a')
            fileb = Blob.from_string(b'file b')
            filed = Blob.from_string(b'file d')

            tree = Tree()
            tree[b'a'] = (stat.S_IFREG | 0o644, filea.id)
            tree[b'b'] = (stat.S_IFREG | 0o644, fileb.id)
            tree[b'c/d'] = (stat.S_IFREG | 0o644, filed.id)

            repo.object_store.add_objects([(o, None)
                for o in [filea, fileb, filed, tree]])

            build_index_from_tree(repo.path, repo.index_path(),
                    repo.object_store, tree.id)

            # Verify index entries
            index = repo.open_index()
            self.assertEqual(len(index), 3)

            # filea
            apath = os.path.join(repo.path, 'a')
            self.assertTrue(os.path.exists(apath))
            self.assertReasonableIndexEntry(index[b'a'],
                stat.S_IFREG | 0o644, 6, filea.id)
            self.assertFileContents(apath, b'file a')

            # fileb
            bpath = os.path.join(repo.path, 'b')
            self.assertTrue(os.path.exists(bpath))
            self.assertReasonableIndexEntry(index[b'b'],
                stat.S_IFREG | 0o644, 6, fileb.id)
            self.assertFileContents(bpath, b'file b')

            # filed
            dpath = os.path.join(repo.path, 'c', 'd')
            self.assertTrue(os.path.exists(dpath))
            self.assertReasonableIndexEntry(index[b'c/d'],
                stat.S_IFREG | 0o644, 6, filed.id)
            self.assertFileContents(dpath, b'file d')

            # Verify no extra files
            self.assertEqual(['.git', 'a', 'b', 'c'],
                sorted(os.listdir(repo.path)))
            self.assertEqual(['d'],
                sorted(os.listdir(os.path.join(repo.path, 'c'))))
Пример #23
0
 def test_tree_diff(self):
     f = StringIO()
     store = MemoryObjectStore()
     added = Blob.from_string("add\n")
     removed = Blob.from_string("removed\n")
     changed1 = Blob.from_string("unchanged\nremoved\n")
     changed2 = Blob.from_string("unchanged\nadded\n")
     unchanged = Blob.from_string("unchanged\n")
     tree1 = Tree()
     tree1.add(0644, "removed.txt", removed.id)
     tree1.add(0644, "changed.txt", changed1.id)
     tree1.add(0644, "unchanged.txt", changed1.id)
     tree2 = Tree()
     tree2.add(0644, "added.txt", added.id)
     tree2.add(0644, "changed.txt", changed2.id)
     tree1.add(0644, "unchanged.txt", changed1.id)
     store.add_objects([(o, None) for o in [
         tree1, tree2, added, removed, changed1, changed2, unchanged]])
     write_tree_diff(f, store, tree1.id, tree2.id)
     self.assertEquals([
         'diff --git a/changed.txt b/changed.txt',
         'index bf84e48..1be2436 644',
         '--- a/changed.txt',
         '+++ b/changed.txt',
         '@@ -1,2 +1,2 @@',
         ' unchanged',
         '-removed',
         '+added',
         'diff --git a/removed.txt /dev/null',
         'deleted mode 644',
         'index 2c3f0b3..e69de29',
         '--- a/removed.txt',
         '+++ /dev/null',
         '@@ -1,1 +1,0 @@',
         '-removed',
         'diff --git a/unchanged.txt /dev/null',
         'deleted mode 644',
         'index bf84e48..e69de29',
         '--- a/unchanged.txt',
         '+++ /dev/null',
         '@@ -1,2 +1,0 @@',
         '-unchanged',
         '-removed',
         'diff --git /dev/null b/added.txt',
         'new mode 644',
         'index e69de29..76d4bb8 644',
         '--- /dev/null',
         '+++ b/added.txt',
         '@@ -1,0 +1,1 @@',
         '+add'
         ], f.getvalue().splitlines())
Пример #24
0
def blob_from_path_and_stat(path, st):
    """Create a blob from a path and a stat object.

    :param path: Full path to file
    :param st: A stat object
    :return: A `Blob` object
    """
    blob = Blob()
    if not stat.S_ISLNK(st.st_mode):
        with open(path, 'rb') as f:
            blob.data = f.read()
    else:
        blob.data = os.readlink(path)
    return blob
Пример #25
0
 def test_blob_diff(self):
     f = BytesIO()
     write_blob_diff(f, ("foo.txt", 0o644, Blob.from_string(b"old\nsame\n")),
                        ("bar.txt", 0o644, Blob.from_string(b"new\nsame\n")))
     self.assertEqual([
         b"diff --git a/foo.txt b/bar.txt",
         b"index 3b0f961..a116b51 644",
         b"--- a/foo.txt",
         b"+++ b/bar.txt",
         b"@@ -1,2 +1,2 @@",
         b"-old",
         b"+new",
         b" same"
         ], f.getvalue().splitlines())
Пример #26
0
    def commit_diff(self, commit):
        """Return the list of changes introduced by `commit`."""
        from klaus.utils import guess_is_binary

        if commit.parents:
            parent_tree = self[commit.parents[0]].tree
        else:
            parent_tree = None

        summary = {'nfiles': 0, 'nadditions':  0, 'ndeletions':  0}
        file_changes = []  # the changes in detail

        dulwich_changes = self.object_store.tree_changes(parent_tree, commit.tree)
        for (oldpath, newpath), (oldmode, newmode), (oldsha, newsha) in dulwich_changes:
            summary['nfiles'] += 1
            try:
                oldblob = self.object_store[oldsha] if oldsha else Blob.from_string(b'')
                newblob = self.object_store[newsha] if newsha else Blob.from_string(b'')
            except KeyError:
                # newsha/oldsha are probably related to submodules.
                # Dulwich will handle that.
                pass

            # Check for binary files -- can't show diffs for these
            if guess_is_binary(newblob) or \
               guess_is_binary(oldblob):
                file_changes.append({
                    'is_binary': True,
                    'old_filename': oldpath or '/dev/null',
                    'new_filename': newpath or '/dev/null',
                    'chunks': None
                })
                continue

            additions, deletions, chunks = render_diff(
                oldblob.splitlines(), newblob.splitlines())
            change = {
                'is_binary': False,
                'old_filename': oldpath or '/dev/null',
                'new_filename': newpath or '/dev/null',
                'chunks': chunks,
                'additions': additions,
                'deletions': deletions,
            }
            summary['nadditions'] += additions
            summary['ndeletions'] += deletions
            file_changes.append(change)

        return summary, file_changes
Пример #27
0
 def test_deltas_work(self):
     orig_pack = self.get_pack(pack1_sha)
     orig_blob = orig_pack[a_sha]
     new_blob = Blob()
     new_blob.data = orig_blob.data + "x"
     all_to_pack = list(orig_pack.pack_tuples()) + [(new_blob, None)]
     pack_path = os.path.join(self._tempdir, "pack_with_deltas")
     write_pack(pack_path, all_to_pack, deltify=True)
     output = run_git_or_fail(["verify-pack", "-v", pack_path])
     self.assertEqual(set(x[0].id for x in all_to_pack), _git_verify_pack_object_list(output))
     # We specifically made a new blob that should be a delta
     # against the blob a_sha, so make sure we really got only 3
     # non-delta objects:
     got_non_delta = int(_NON_DELTA_RE.search(output).group("non_delta"))
     self.assertEqual(3, got_non_delta, "Expected 3 non-delta objects, got %d" % got_non_delta)
Пример #28
0
 def test_splitlines(self):
     for case in [
             [],
             [b'foo\nbar\n'],
             [b'bl\na', b'blie'],
             [b'bl\na', b'blie', b'bloe\n'],
             [b'', b'bl\na', b'blie', b'bloe\n'],
             [b'', b'', b'', b'bla\n'],
             [b'', b'', b'', b'bla\n', b''],
             [b'bl', b'', b'a\naaa'],
             [b'a\naaa', b'a'],
             ]:
         b = Blob()
         b.chunked = case
         self.assertEqual(b.data.splitlines(True), b.splitlines())
Пример #29
0
def blob_from_path_and_stat(fs_path, st):
    """Create a blob from a path and a stat object.

    :param fs_path: Full file system path to file
    :param st: A stat object
    :return: A `Blob` object
    """
    assert isinstance(fs_path, bytes)
    blob = Blob()
    if not stat.S_ISLNK(st.st_mode):
        with open(fs_path, 'rb') as f:
            blob.data = f.read()
    else:
        blob.data = os.readlink(fs_path)
    return blob
Пример #30
0
 def test_simple(self):
     c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1],
         [3, 1, 2]])
     b = Blob()
     b.data = b"foo the bar"
     t = Tree()
     t.add(b"somename", 0o100644, b.id)
     self.repo.object_store.add_object(t)
     self.repo.object_store.add_object(b)
     sha = porcelain.commit_tree(
         self.repo.path, t.id, message=b"Withcommit.",
         author=b"Joe <*****@*****.**>",
         committer=b"Jane <*****@*****.**>")
     self.assertTrue(isinstance(sha, bytes))
     self.assertEqual(len(sha), 40)
Пример #31
0
 def test_deltas_work(self):
     orig_pack = self.get_pack(pack1_sha)
     orig_blob = orig_pack[a_sha]
     new_blob = Blob()
     new_blob.data = orig_blob.data + b'x'
     all_to_pack = list(orig_pack.pack_tuples()) + [(new_blob, None)]
     pack_path = os.path.join(self._tempdir, b'pack_with_deltas')
     write_pack(pack_path, all_to_pack, deltify=True)
     output = run_git_or_fail(['verify-pack', '-v', pack_path])
     self.assertEqual(set(x[0].id for x in all_to_pack),
                      _git_verify_pack_object_list(output))
     # We specifically made a new blob that should be a delta
     # against the blob a_sha, so make sure we really got only 3
     # non-delta objects:
     got_non_delta = int(_NON_DELTA_RE.search(output).group('non_delta'))
     self.assertEqual(
         3, got_non_delta,
         'Expected 3 non-delta objects, got %d' % got_non_delta)
Пример #32
0
 def test_object_diff_blob(self):
     f = BytesIO()
     b1 = Blob.from_string("old\nsame\n")
     b2 = Blob.from_string("new\nsame\n")
     store = MemoryObjectStore()
     store.add_objects([(b1, None), (b2, None)])
     write_object_diff(f, store, ("foo.txt", 0o644, b1.id),
                                 ("bar.txt", 0o644, b2.id))
     self.assertEqual([
         "diff --git a/foo.txt b/bar.txt",
         "index 3b0f961..a116b51 644",
         "--- a/foo.txt",
         "+++ b/bar.txt",
         "@@ -1,2 +1,2 @@",
         "-old",
         "+new",
         " same"
         ], f.getvalue().splitlines())
Пример #33
0
 def test_missing_added_file(self):
     self.build_tree(['a'])
     self.wt.add(['a'])
     os.unlink('a')
     a = Blob.from_string(b'contents of a\n')
     t = Tree()
     t.add(b"a", 0, ZERO_SHA)
     self.expectDelta([((None, b''), (None, stat.S_IFDIR), (None, t.id)),
                       ((None, b'a'), (None, 0), (None, ZERO_SHA))], [])
Пример #34
0
 def test_full_tree(self):
     c = self.make_commit(commit_time=30)
     t = Tree()
     t.add(b'data-x', 0o644, Blob().id)
     c.tree = t
     c1 = Commit()
     c1.set_raw_string(c.as_raw_string())
     self.assertEqual(t.id, c1.tree)
     self.assertEqual(c.as_raw_string(), c1.as_raw_string())
Пример #35
0
 def test_added_file(self):
     self.build_tree(['a'])
     self.wt.add(['a'])
     a = Blob.from_string(b'contents of a\n')
     t = Tree()
     t.add(b"a", stat.S_IFREG | 0o644, a.id)
     self.expectDelta([((None, b''), (None, stat.S_IFDIR), (None, t.id)),
                       ((None, b'a'), (None, stat.S_IFREG | 0o644),
                        (None, a.id))])
Пример #36
0
 def test_object_diff_bin_blob(self):
     f = BytesIO()
     # Prepare two slightly different PNG headers
     b1 = Blob.from_string(
         "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52"
         "\x00\x00\x01\xd5\x00\x00\x00\x9f\x08\x04\x00\x00\x00\x05\x04\x8b")
     b2 = Blob.from_string(
         "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52"
         "\x00\x00\x01\xd5\x00\x00\x00\x9f\x08\x03\x00\x00\x00\x98\xd3\xb3")
     store = MemoryObjectStore()
     store.add_objects([(b1, None), (b2, None)])
     write_object_diff(f, store, ('foo.png', 0o644, b1.id),
                                 ('bar.png', 0o644, b2.id))
     self.assertEqual([
         'diff --git a/foo.png b/bar.png',
         'index f73e47d..06364b7 644',
         'Binary files a/foo.png and b/bar.png differ'
         ], f.getvalue().splitlines())
Пример #37
0
 def commit_handler(self, cmd):
     """Process a CommitCommand."""
     commit = Commit()
     if cmd.author is not None:
         author = cmd.author
     else:
         author = cmd.committer
     (author_name, author_email, author_timestamp, author_timezone) = author
     (committer_name, committer_email, commit_timestamp,
      commit_timezone) = cmd.committer
     commit.author = author_name + b" <" + author_email + b">"
     commit.author_timezone = author_timezone
     commit.author_time = int(author_timestamp)
     commit.committer = committer_name + b" <" + committer_email + b">"
     commit.commit_timezone = commit_timezone
     commit.commit_time = int(commit_timestamp)
     commit.message = cmd.message
     commit.parents = []
     if cmd.from_:
         cmd.from_ = self.lookup_object(cmd.from_)
         self._reset_base(cmd.from_)
     for filecmd in cmd.iter_files():
         if filecmd.name == b"filemodify":
             if filecmd.data is not None:
                 blob = Blob.from_string(filecmd.data)
                 self.repo.object_store.add(blob)
                 blob_id = blob.id
             else:
                 blob_id = self.lookup_object(filecmd.dataref)
             self._contents[filecmd.path] = (filecmd.mode, blob_id)
         elif filecmd.name == b"filedelete":
             del self._contents[filecmd.path]
         elif filecmd.name == b"filecopy":
             self._contents[filecmd.dest_path] = self._contents[
                 filecmd.src_path]
         elif filecmd.name == b"filerename":
             self._contents[filecmd.new_path] = self._contents[
                 filecmd.old_path]
             del self._contents[filecmd.old_path]
         elif filecmd.name == b"filedeleteall":
             self._contents = {}
         else:
             raise Exception("Command %s not supported" % filecmd.name)
     commit.tree = commit_tree(
         self.repo.object_store,
         ((path, hexsha, mode)
          for (path, (mode, hexsha)) in self._contents.items()))
     if self.last_commit != ZERO_SHA:
         commit.parents.append(self.last_commit)
     for merge in cmd.merges:
         commit.parents.append(self.lookup_object(merge))
     self.repo.object_store.add_object(commit)
     self.repo[cmd.ref] = commit.id
     self.last_commit = commit.id
     if cmd.mark:
         self.markers[cmd.mark] = commit.id
Пример #38
0
    def _import_one(self, name, data, message, author=None):
        """Import a single object.

        :param name: Optional name of the object
        :param data: serialized object as bytes
        :param message: optional commit message
        :param author: optional author
        :return: etag
        """
        b = Blob()
        b.chunked = data
        tree = self._get_current_tree()
        name_enc = name.encode(DEFAULT_ENCODING)
        tree[name_enc] = (0o644 | stat.S_IFREG, b.id)
        self.repo.object_store.add_objects([(tree, ''), (b, name_enc)])
        self._commit_tree(tree.id,
                          message.encode(DEFAULT_ENCODING),
                          author=author)
        return b.id
Пример #39
0
 def test_tree_diff(self):
     f = BytesIO()
     store = MemoryObjectStore()
     added = Blob.from_string(b"add\n")
     removed = Blob.from_string(b"removed\n")
     changed1 = Blob.from_string(b"unchanged\nremoved\n")
     changed2 = Blob.from_string(b"unchanged\nadded\n")
     unchanged = Blob.from_string(b"unchanged\n")
     tree1 = Tree()
     tree1.add(b"removed.txt", 0o644, removed.id)
     tree1.add(b"changed.txt", 0o644, changed1.id)
     tree1.add(b"unchanged.txt", 0o644, changed1.id)
     tree2 = Tree()
     tree2.add(b"added.txt", 0o644, added.id)
     tree2.add(b"changed.txt", 0o644, changed2.id)
     tree2.add(b"unchanged.txt", 0o644, changed1.id)
     store.add_objects([(o, None) for o in [
         tree1, tree2, added, removed, changed1, changed2, unchanged]])
     write_tree_diff(f, store, tree1.id, tree2.id)
     self.assertEqual([
         b'diff --git a/added.txt b/added.txt',
         b'new file mode 644',
         b'index 0000000..76d4bb8',
         b'--- /dev/null',
         b'+++ b/added.txt',
         b'@@ -0,0 +1 @@',
         b'+add',
         b'diff --git a/changed.txt b/changed.txt',
         b'index bf84e48..1be2436 644',
         b'--- a/changed.txt',
         b'+++ b/changed.txt',
         b'@@ -1,2 +1,2 @@',
         b' unchanged',
         b'-removed',
         b'+added',
         b'diff --git a/removed.txt b/removed.txt',
         b'deleted file mode 644',
         b'index 2c3f0b3..0000000',
         b'--- a/removed.txt',
         b'+++ /dev/null',
         b'@@ -1 +0,0 @@',
         b'-removed',
         ], f.getvalue().splitlines())
Пример #40
0
 def test_rename_no_changes(self):
     a = Blob.from_string(b'a')
     self.assertEqual([
         TreeChange(b'git:old', ('old', 'a'), False, (True, True),
                    (b'TREE_ROOT', b'TREE_ROOT'), ('old', 'a'),
                    ('file', 'file'), (False, False), False)
     ],
                      self.transform([('rename', (b'old',
                                                  stat.S_IFREG | 0o644, a),
                                       (b'a', stat.S_IFREG | 0o644, a))]))
Пример #41
0
 def test_import_tree_with_unusual_mode_file(self):
     blob = Blob.from_string(b"bar1")
     tree = Tree()
     tree.add(b"foo", stat.S_IFREG | 0o664, blob.id)
     objects = {blob.id: blob, tree.id: tree}
     ret, child_modes = import_git_tree(
         self._texts, self._mapping, b"bla", b"bla", (None, tree.id), None,
         None, b"somerevid", [], objects.__getitem__, (None, stat.S_IFDIR),
         DummyStoreUpdater(), self._mapping.generate_file_id)
     self.assertEqual(child_modes, {b"bla/foo": stat.S_IFREG | 0o664})
Пример #42
0
 def test_delete(self):
     b = Blob.from_string(b'b')
     self.assertEqual([
         TreeChange(b'git:a', ('a', None), True, (True, False),
                    (b'TREE_ROOT', None), ('a', None), ('file', None),
                    (False, None), False)
     ],
                      self.transform([('remove', (b'a',
                                                  stat.S_IFREG | 0o644, b),
                                       (None, None, None))]))
Пример #43
0
 def test_with_file(self):
     child_ie = InventoryFile(b'bar', 'bar', b'bar')
     b = Blob.from_string(b"bla")
     t1 = directory_to_tree('', [child_ie],
                            lambda p, x: b.id, {},
                            None,
                            allow_empty=False)
     t2 = Tree()
     t2.add(b"bar", 0o100644, b.id)
     self.assertEqual(t1, t2)
Пример #44
0
class ExpectedShaTests(TestCase):
    def setUp(self):
        super(ExpectedShaTests, self).setUp()
        self.obj = Blob()
        self.obj.data = b"foo"

    def test_none(self):
        _check_expected_sha(None, self.obj)

    def test_hex(self):
        _check_expected_sha(self.obj.sha().hexdigest().encode('ascii'),
                            self.obj)
        self.assertRaises(AssertionError, _check_expected_sha, b"0" * 40,
                          self.obj)

    def test_binary(self):
        _check_expected_sha(self.obj.sha().digest(), self.obj)
        self.assertRaises(AssertionError, _check_expected_sha, b"x" * 20,
                          self.obj)
Пример #45
0
 def test_blob_add(self):
     f = BytesIO()
     write_blob_diff(f, (None, None, None),
                     (b"bar.txt", 0o644, Blob.from_string(b"new\nsame\n")))
     self.assertEqual([
         b'diff --git /dev/null b/bar.txt', b'new mode 644',
         b'index 0000000..a116b51 644', b'--- /dev/null', b'+++ b/bar.txt',
         b'@@ -1,0 +1,2 @@', b'+new', b'+same'
     ],
                      f.getvalue().splitlines())
Пример #46
0
    def _reconstruct_blobs(self, keys):
        """Return a Git Blob object from a fileid and revision stored in bzr.

        :param fileid: File id of the text
        :param revision: Revision of the text
        """
        stream = self.repository.iter_files_bytes(
            ((key[0], key[1], key) for key in keys))
        for (file_id, revision, expected_sha), chunks in stream:
            blob = Blob()
            blob.chunked = chunks
            if blob.id != expected_sha and blob.data == "":
                # Perhaps it's a symlink ?
                tree = self.tree_cache.revision_tree(revision)
                path = tree.id2path(file_id)
                if tree.kind(path) == 'symlink':
                    blob = symlink_to_blob(tree.get_symlink_target(path))
            _check_expected_sha(expected_sha, blob)
            yield blob
Пример #47
0
 def test_copy_no_changes(self):
     a = Blob.from_string(b'a')
     delta = self.transform([('copy', (b'old', stat.S_IFREG | 0o644, a),
                              (b'a', stat.S_IFREG | 0o644, a))])
     expected_delta = TreeDelta()
     expected_delta.copied.append(
         TreeChange(b'git:a', ('old', 'a'), False, (True, True),
                    (b'TREE_ROOT', b'TREE_ROOT'), ('old', 'a'),
                    ('file', 'file'), (False, False), True))
     self.assertEqual(expected_delta, delta)
Пример #48
0
 def test_extra(self):
     self.build_tree(['a'])
     newa = Blob.from_string(b'contents of a\n')
     newt = Tree()
     newt.add(b"a", stat.S_IFREG | 0o644, newa.id)
     self.expectDelta([
         ('add', (None, None, None), (b'', stat.S_IFDIR, newt.id)),
         ('add', (None, None, None), (b'a', stat.S_IFREG | 0o644, newa.id)),
     ], [b'a'],
                      want_unversioned=True)
Пример #49
0
def test_current_tree_should_be_from_current_commit():
    repo = MemoryRepo()
    tree = Tree()
    repo.object_store.add_object(tree)
    repo.do_commit(tree=tree.id, message=b'first commit')
    tree.add(b'test', 0o100644, Blob().id)
    repo.object_store.add_object(tree)
    repo.do_commit(tree=tree.id, message=b'second commit')

    assert GitRepo(repo).current_tree.id == tree.id
Пример #50
0
 def test_delete(self):
     b = Blob.from_string(b'b')
     delta = self.transform([('remove', (b'a', stat.S_IFREG | 0o644, b),
                              (None, None, None))])
     expected_delta = TreeDelta()
     expected_delta.removed.append(
         TreeChange(b'git:a', ('a', None), True, (True, False),
                    (b'TREE_ROOT', None), ('a', None), ('file', None),
                    (False, None), False))
     self.assertEqual(delta, expected_delta)
Пример #51
0
 def test_blob_remove(self):
     f = BytesIO()
     write_blob_diff(f, ("bar.txt", 0o644, Blob.from_string("new\nsame\n")),
                     (None, None, None))
     self.assertEqual([
         'diff --git a/bar.txt /dev/null', 'deleted mode 644',
         'index a116b51..0000000', '--- a/bar.txt', '+++ /dev/null',
         '@@ -1,2 +1,0 @@', '-new', '-same'
     ],
                      f.getvalue().splitlines())
Пример #52
0
 def _get_example_tar_stream(self, *tar_stream_args, **tar_stream_kwargs):
     store = MemoryObjectStore()
     b1 = Blob.from_string(b"somedata")
     store.add_object(b1)
     t1 = Tree()
     t1.add(b"somename", 0o100644, b1.id)
     store.add_object(t1)
     stream = b''.join(
         tar_stream(store, t1, *tar_stream_args, **tar_stream_kwargs))
     return BytesIO(stream)
Пример #53
0
 def test_contains(self):
     self.branch.lock_write()
     self.addCleanup(self.branch.unlock)
     b = Blob()
     b.data = b'a\nb\nc\nd\ne\n'
     self.store.lock_read()
     self.addCleanup(self.store.unlock)
     self.assertFalse(b.id in self.store)
     bb = BranchBuilder(branch=self.branch)
     bb.start_series()
     bb.build_snapshot(None,
                       [('add', ('', None, 'directory', None)),
                        ('add', ('foo', b'foo-id', 'file', b'a\nb\nc\nd\ne\n')),
                        ])
     bb.finish_series()
     # read locks cache
     self.assertFalse(b.id in self.store)
     self.store.unlock()
     self.store.lock_read()
     self.assertTrue(b.id in self.store)
Пример #54
0
 def test_submodule(self):
     self.build_tree(['a/'])
     a = Blob.from_string(b'irrelevant\n')
     with self.wt.lock_tree_write():
         (index, index_path) = self.wt._lookup_index(b'a')
         index[b'a'] = IndexEntry(0, 0, 0, 0, S_IFGITLINK, 0, 0, 0, a.id, 0)
         self.wt._index_dirty = True
     t = Tree()
     t.add(b"a", S_IFGITLINK, a.id)
     self.store.add_object(t)
     self.expectDelta([], tree_id=t.id)
Пример #55
0
 def test_submodule_not_checked_out(self):
     a = Blob.from_string(b'irrelevant\n')
     with self.wt.lock_tree_write():
         (index, index_path) = self.wt._lookup_index(b'a')
         index[b'a'] = IndexEntry(0, 0, 0, 0, S_IFGITLINK, 0, 0, 0, a.id, 0)
         self.wt._index_dirty = True
     os.mkdir(self.wt.abspath('a'))
     t = Tree()
     t.add(b"a", S_IFGITLINK, a.id)
     self.store.add_object(t)
     self.expectDelta([], tree_id=t.id)
Пример #56
0
 def test_get_raw(self):
     self.branch.lock_write()
     self.addCleanup(self.branch.unlock)
     b = Blob()
     b.data = b'a\nb\nc\nd\ne\n'
     self.store.lock_read()
     self.addCleanup(self.store.unlock)
     self.assertRaises(KeyError, self.store.get_raw, b.id)
     bb = BranchBuilder(branch=self.branch)
     bb.start_series()
     bb.build_snapshot(None,
                       [('add', ('', None, 'directory', None)),
                        ('add', ('foo', b'foo-id', 'file', b'a\nb\nc\nd\ne\n')),
                        ])
     bb.finish_series()
     # read locks cache
     self.assertRaises(KeyError, self.store.get_raw, b.id)
     self.store.unlock()
     self.store.lock_read()
     self.assertEqual(b.as_raw_string(), self.store.get_raw(b.id)[1])
Пример #57
0
 def makeRepo(self, tree_contents):
     repo = GitRepo(self.repository_path)
     blobs = [(Blob.from_string(contents), filename)
              for (filename, contents) in tree_contents]
     repo.object_store.add_objects(blobs)
     root_id = dulwich.index.commit_tree(
         repo.object_store,
         [(filename, b.id, stat.S_IFREG | 0644) for (b, filename) in blobs])
     repo.do_commit(committer='Joe Foo <*****@*****.**>',
                    message=u'<The commit message>',
                    tree=root_id)
Пример #58
0
def create_repo(design, initialize):
    full_path = os.path.join(settings.GIT_ROOT, design.repo_path)
    os.makedirs(full_path)
    repo = Repo.init_bare(full_path)
    if initialize:
        blob = Blob.from_string(
            str("%s Git-a-thing design repository\n" % design.name))
        tree = Tree()
        tree.add("README", 0100644, blob.id)
        do_commit(repo, tree, [blob], "Initialize repository",
                  settings.GITATHING_COMMITER)
 def test_blob_diff(self):
     f = BytesIO()
     write_blob_diff(
         f,
         (b"foo.txt", 0o644, Blob.from_string(b"old\nsame\n")),
         (b"bar.txt", 0o644, Blob.from_string(b"new\nsame\n")),
     )
     self.assertEqual(
         [
             b"diff --git a/foo.txt b/bar.txt",
             b"index 3b0f961..a116b51 644",
             b"--- a/foo.txt",
             b"+++ b/bar.txt",
             b"@@ -1,2 +1,2 @@",
             b"-old",
             b"+new",
             b" same",
         ],
         f.getvalue().splitlines(),
     )
Пример #60
0
def blob_from_path_and_mode(fs_path, mode, tree_encoding="utf-8"):
    """Create a blob from a path and a stat object.

    Args:
      fs_path: Full file system path to file
      st: A stat object
    Returns: A `Blob` object
    """
    assert isinstance(fs_path, bytes)
    blob = Blob()
    if stat.S_ISLNK(mode):
        if sys.platform == "win32":
            # os.readlink on Python3 on Windows requires a unicode string.
            fs_path = os.fsdecode(fs_path)
            blob.data = os.readlink(fs_path).encode(tree_encoding)
        else:
            blob.data = os.readlink(fs_path)
    else:
        with open(fs_path, "rb") as f:
            blob.data = f.read()
    return blob