Dulwich.io dulwich / ded4ab1
Add really basic LFSStore. Jelmer Vernooń≥ a month ago
3 changed file(s) with 120 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 # lfs.py -- Implementation of the LFS
1 # Copyright (C) 2020 Jelmer Vernooij
2 #
3 # Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU
4 # General Public License as public by the Free Software Foundation; version 2.0
5 # or (at your option) any later version. You can redistribute it and/or
6 # modify it under the terms of either of these two licenses.
7 #
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
13 #
14 # You should have received a copy of the licenses; if not, see
15 # <http://www.gnu.org/licenses/> for a copy of the GNU General Public License
16 # and <http://www.apache.org/licenses/LICENSE-2.0> for a copy of the Apache
17 # License, Version 2.0.
18 #
19
20 import hashlib
21 import os
22 import tempfile
23
24
25 class LFSStore(object):
26 """Stores objects on disk, indexed by SHA256."""
27
28 def __init__(self, path):
29 self.path = path
30
31 @classmethod
32 def create(cls, lfs_dir):
33 if not os.path.isdir(lfs_dir):
34 os.mkdir(lfs_dir)
35 os.mkdir(os.path.join(lfs_dir, 'tmp'))
36 os.mkdir(os.path.join(lfs_dir, 'objects'))
37 return cls(lfs_dir)
38
39 @classmethod
40 def from_repo(cls, repo, create=False):
41 lfs_dir = os.path.join(repo.controldir, 'lfs')
42 if create:
43 return cls.create(lfs_dir)
44 return cls(lfs_dir)
45
46 def _sha_path(self, sha):
47 return os.path.join(self.path, 'objects', sha[0:2], sha[2:4], sha)
48
49 def open_object(self, sha):
50 """Open an object by sha."""
51 try:
52 return open(self._sha_path(sha), 'rb')
53 except FileNotFoundError:
54 raise KeyError(sha)
55
56 def write_object(self, chunks):
57 """Write an object.
58
59 Returns: object SHA
60 """
61 sha = hashlib.sha256()
62 tmpdir = os.path.join(self.path, 'tmp')
63 with tempfile.NamedTemporaryFile(
64 dir=tmpdir, mode='wb', delete=False) as f:
65 for chunk in chunks:
66 sha.update(chunk)
67 f.write(chunk)
68 f.flush()
69 tmppath = f.name
70 path = self._sha_path(sha.hexdigest())
71 if not os.path.exists(os.path.dirname(path)):
72 os.makedirs(os.path.dirname(path))
73 os.rename(tmppath, path)
74 return sha.hexdigest()
111111 'hooks',
112112 'ignore',
113113 'index',
114 'lfs',
114115 'line_ending',
115116 'lru_cache',
116117 'mailmap',
0 # test_lfs.py -- tests for LFS
1 # Copyright (C) 2020 Jelmer Vernooij <jelmer@jelmer.uk>
2 #
3 # Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU
4 # General Public License as public by the Free Software Foundation; version 2.0
5 # or (at your option) any later version. You can redistribute it and/or
6 # modify it under the terms of either of these two licenses.
7 #
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
13 #
14 # You should have received a copy of the licenses; if not, see
15 # <http://www.gnu.org/licenses/> for a copy of the GNU General Public License
16 # and <http://www.apache.org/licenses/LICENSE-2.0> for a copy of the Apache
17 # License, Version 2.0.
18 #
19
20 """Tests for LFS support."""
21
22 from . import TestCase
23 from ..lfs import LFSStore
24 import shutil
25 import tempfile
26
27
28 class LFSTests(TestCase):
29
30 def setUp(self):
31 super(LFSTests, self).setUp()
32 self.test_dir = tempfile.mkdtemp()
33 self.addCleanup(shutil.rmtree, self.test_dir)
34 self.lfs = LFSStore.create(self.test_dir)
35
36 def test_create(self):
37 sha = self.lfs.write_object([b'a', b'b'])
38 with self.lfs.open_object(sha) as f:
39 self.assertEqual(b'ab', f.read())
40
41 def test_missing(self):
42 self.assertRaises(
43 KeyError, self.lfs.open_object, 'abcdeabcdeabcdeabcde')