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)
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
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()
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()
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)
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')
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)
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 """)
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))
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())
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())
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]
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(), )
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")
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()
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)
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, "")])))
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()))
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)])
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"))))
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()))
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'))))
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())
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
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())
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
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)
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())
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
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)
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)
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())
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))], [])
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())
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))])
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())
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
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
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())
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))]))
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})
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))]))
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)
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)
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())
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
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)
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)
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
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)
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())
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)
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)
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)
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)
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])
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)
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(), )
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