Skip to content

Commit 92ca2e4

Browse files
committed
Implemented gitdb, it should be a fully functional git database with full read support, and the ability to write loose objects
1 parent 6fd8d74 commit 92ca2e4

7 files changed

Lines changed: 131 additions & 25 deletions

File tree

db/base.py

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
"""Contains implementations of database retrieveing objects"""
22
from gitdb.util import (
33
pool,
4-
join
4+
join,
5+
LazyMixin,
6+
to_bin_sha
57
)
68

9+
from gitdb.exc import BadObject
10+
711
from async import (
812
ChannelThreadTask
913
)
1014

15+
from itertools import chain
16+
1117

1218
__all__ = ('ObjectDBR', 'ObjectDBW', 'FileDBBase', 'CompoundDB', 'CachingDB')
1319

@@ -182,22 +188,40 @@ def _set_cache_(self, attr):
182188
if attr == '_dbs':
183189
self._dbs = list()
184190

191+
def _db_query(self, sha):
192+
""":return: database containing the given 20 or 40 byte sha
193+
:raise BadObject:"""
194+
# most databases use binary representations, prevent converting
195+
# it everytime a database is being queried
196+
sha = to_bin_sha(sha)
197+
for db in self._dbs:
198+
if db.has_object(sha):
199+
return db
200+
# END for each database
201+
raise BadObject(sha)
202+
185203
#{ ObjectDBR interface
186204

187205
def has_object(self, sha):
188-
raise NotImplementedError("To be implemented in subclass")
206+
try:
207+
self._db_query(sha)
208+
return True
209+
except BadObject:
210+
return False
211+
# END handle exceptions
189212

190213
def info(self, sha):
191-
raise NotImplementedError("To be implemented in subclass")
214+
return self._db_query(sha).info(sha)
192215

193216
def stream(self, sha):
194-
raise NotImplementedError()
217+
return self._db_query(sha).stream(sha)
195218

196219
def size(self):
197-
raise NotImplementedError()
220+
""":return: total size of all contained databases"""
221+
return reduce(lambda x,y: x+y, (db.size() for db in self._dbs), 0)
198222

199223
def sha_iter(self):
200-
raise NotImplementedError()
224+
return chain(*(db.sha_iter() for db in self._dbs))
201225

202226
#} END object DBR Interface
203227

db/git.py

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
from base import (
2-
CompoundDB,
3-
FileDBBase,
4-
)
2+
CompoundDB,
3+
ObjectDBW,
4+
FileDBBase
5+
)
56

67
from loose import LooseObjectDB
78
from pack import PackedDB
@@ -13,7 +14,7 @@
1314

1415
__all__ = ('GitDB', )
1516

16-
class GitDB(FileDBBase, CompoundDB):
17+
class GitDB(FileDBBase, ObjectDBW, CompoundDB):
1718
"""A git-style object database, which contains all objects in the 'objects'
1819
subdirectory"""
1920
# Configuration
@@ -22,7 +23,7 @@ class GitDB(FileDBBase, CompoundDB):
2223
ReferenceDBCls = ReferenceDB
2324

2425
# Directories
25-
packs_dir = 'packs'
26+
packs_dir = 'pack'
2627
loose_dir = ''
2728
alternates_dir = os.path.join('info', 'alternates')
2829

@@ -31,19 +32,42 @@ def __init__(self, root_path):
3132
super(GitDB, self).__init__(root_path)
3233

3334
def _set_cache_(self, attr):
34-
if attr == '_dbs':
35+
if attr == '_dbs' or attr == '_loose_db':
3536
self._dbs = list()
37+
loose_db = None
3638
for subpath, dbcls in ((self.packs_dir, self.PackDBCls),
3739
(self.loose_dir, self.LooseDBCls),
3840
(self.alternates_dir, self.ReferenceDBCls)):
3941
path = self.db_path(subpath)
4042
if os.path.exists(path):
4143
self._dbs.append(dbcls(path))
44+
if dbcls is self.LooseDBCls:
45+
loose_db = self._dbs[-1]
46+
# END remember loose db
4247
# END check path exists
4348
# END for each db type
4449

4550
# should have at least one subdb
4651
if not self._dbs:
4752
raise InvalidDBRoot(self.root_path())
53+
# END handle error
54+
55+
# we the first one should have the store method
56+
assert loose_db is not None and hasattr(loose_db, 'store'), "First database needs store functionality"
57+
58+
# finally set the value
59+
self._loose_db = loose_db
60+
4861
# END handle dbs
4962

63+
#{ ObjectDBW interface
64+
65+
def store(self, istream):
66+
return self._loose_db.store(istream)
67+
68+
def ostream(self):
69+
return self._loose_db.ostream()
70+
71+
def set_ostream(self, ostream):
72+
return self._loose_db.set_ostream(ostream)
73+
#} END objectdbw interface

db/pack.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ def sha_iter(self):
110110

111111
def size(self):
112112
sizes = [item[1].index().size() for item in self._entities]
113-
return reduce(lambda x,y: x+y, sizes)
113+
return reduce(lambda x,y: x+y, sizes, 0)
114114

115115
#} END object db read
116116

db/ref.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
)
44

55
import os
6-
__all__ = ('CompoundDB', )
6+
__all__ = ('ReferenceDB', )
77

88
class ReferenceDB(CompoundDB):
99
"""A database consisting of database referred to in a file"""
@@ -35,7 +35,7 @@ def _update_dbs_from_ref_file(self):
3535
ref_paths = list()
3636
try:
3737
ref_paths = [l.strip() for l in open(self._ref_file, 'r').readlines()]
38-
except OSError:
38+
except (OSError, IOError):
3939
pass
4040
# END handle alternates
4141

@@ -55,7 +55,16 @@ def _update_dbs_from_ref_file(self):
5555
# sort them to maintain order
5656
added_paths = sorted(ref_paths_set - cur_ref_paths_set, key=lambda p: ref_paths.index(p))
5757
for path in added_paths:
58-
self._dbs.append(dbcls(path))
58+
try:
59+
db = dbcls(path)
60+
# force an update to verify path
61+
if isinstance(db, CompoundDB):
62+
db.databases()
63+
# END verification
64+
self._dbs.append(db)
65+
except Exception, e:
66+
# ignore invalid paths or issues
67+
pass
5968
# END for each path to add
6069

6170
def update_cache(self, force=False):

test/db/lib.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
from cStringIO import StringIO
2323

2424

25-
__all__ = ('TestDBBase', 'with_rw_directory', 'with_packs_rw' )
25+
__all__ = ('TestDBBase', 'with_rw_directory', 'with_packs_rw', 'fixture_path')
2626

2727
class TestDBBase(TestBase):
2828
"""Base class providing testing routines on databases"""

test/db/test_git.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,26 @@
11
from lib import *
22
from gitdb.db import GitDB
3+
from gitdb.base import OStream, OInfo
34

4-
class TestGitDB(TestBase):
5+
class TestGitDB(TestDBBase):
56

67
def test_reading(self):
7-
ldb = GitDB(fixture_path('../../.git/objects')
8-
self.fail("todo")
8+
gdb = GitDB(fixture_path('../../.git/objects'))
99

10+
# we have packs and loose objects, alternates doesn't necessarily exist
11+
assert 1 < len(gdb.databases()) < 4
12+
13+
# access should be possible
14+
gitdb_sha = "5690fd0d3304f378754b23b098bd7cb5f4aa1976"
15+
assert isinstance(gdb.info(gitdb_sha), OInfo)
16+
assert isinstance(gdb.stream(gitdb_sha), OStream)
17+
assert gdb.size() > 200
18+
assert len(list(gdb.sha_iter())) == gdb.size()
19+
20+
@with_rw_directory
21+
def test_writing(self, path):
22+
gdb = GitDB(path)
23+
24+
# its possible to write objects
25+
self._assert_object_writing(gdb)
26+
self._assert_object_writing_async(gdb)

test/db/test_ref.py

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,53 @@
11
from lib import *
22
from gitdb.db import ReferenceDB
33

4-
class TestReferenceDB(TestBase):
4+
import os
5+
6+
class TestReferenceDB(TestDBBase):
7+
8+
def make_alt_file(self, alt_path, alt_list):
9+
"""Create an alternates file which contains the given alternates.
10+
The list can be empty"""
11+
alt_file = open(alt_path, "wb")
12+
for alt in alt_list:
13+
alt_file.write(alt + "\n")
14+
alt_file.close()
515

616
@with_rw_directory
717
def test_writing(self, path):
8-
# TODO: setup alternate file
9-
alternates =
10-
ldb = ReferenceDB(path)
18+
null_sha_bin = '\0' * 20
19+
null_sha_hex = "0" * 40
20+
21+
alt_path = os.path.join(path, 'alternates')
22+
rdb = ReferenceDB(alt_path)
23+
assert len(rdb.databases()) == 0
24+
assert rdb.size() == 0
25+
assert len(list(rdb.sha_iter())) == 0
1126

1227
# try empty, non-existing
28+
assert not rdb.has_object(null_sha_hex)
29+
assert not rdb.has_object(null_sha_bin)
1330

31+
32+
# setup alternate file
1433
# add two, one is invalid
34+
own_repo_path = fixture_path('../../.git/objects') # use own repo
35+
self.make_alt_file(alt_path, [own_repo_path, "invalid/path"])
36+
rdb.update_cache()
37+
assert len(rdb.databases()) == 1
38+
39+
# we should now find a default revision of ours
40+
gitdb_sha = "5690fd0d3304f378754b23b098bd7cb5f4aa1976"
41+
assert rdb.has_object(gitdb_sha)
1542

1643
# remove valid
44+
self.make_alt_file(alt_path, ["just/one/invalid/path"])
45+
rdb.update_cache()
46+
assert len(rdb.databases()) == 0
1747

1848
# add valid
49+
self.make_alt_file(alt_path, [own_repo_path])
50+
rdb.update_cache()
51+
assert len(rdb.databases()) == 1
1952

20-
self.fail("todo")
2153

0 commit comments

Comments
 (0)