1
# Copyright (C) 2010 Jelmer Vernooij <jelmer@samba.org>
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
"""A Git repository implementation that uses a Bazaar transport."""
19
from cStringIO import StringIO
21
from dulwich.errors import (
25
from dulwich.objects import (
28
from dulwich.object_store import (
32
from dulwich.pack import (
39
from dulwich.repo import (
46
from bzrlib.errors import (
53
class TransportRepo(BaseRepo):
55
def __init__(self, transport):
56
self.transport = transport
58
if self.transport.has(".git/info/refs"):
60
self._controltransport = self.transport.clone('.git')
61
elif self.transport.has("info/refs"):
63
self._controltransport = self.transport
65
raise NotGitRepository(self.transport)
67
raise NotGitRepository(self.transport)
68
object_store = TransportObjectStore(
69
self._controltransport.clone(OBJECTDIR))
71
refs["HEAD"] = self._controltransport.get_bytes("HEAD").rstrip("\n")
72
refs.update(read_info_refs(self._controltransport.get('info/refs')))
73
super(TransportRepo, self).__init__(object_store,
74
DictRefsContainer(refs))
76
def get_named_file(self, path):
77
"""Get a file from the control dir with a specific name.
79
Although the filename should be interpreted as a filename relative to
80
the control dir in a disk-baked Repo, the object returned need not be
81
pointing to a file in that location.
83
:param path: The path to the file, relative to the control dir.
84
:return: An open file object, or None if the file does not exist.
87
return self._controltransport.get(path.lstrip('/'))
92
"""Open the index for this repository."""
93
raise NoIndexPresent()
96
return "<TransportRepo for %r>" % self.transport
99
class TransportObjectStore(PackBasedObjectStore):
100
"""Git-style object store that exists on disk."""
102
def __init__(self, transport):
103
"""Open an object store.
105
:param transport: Transport to open data from
107
super(TransportObjectStore, self).__init__()
108
self.transport = transport
109
self.pack_transport = self.transport.clone(PACKDIR)
111
def _pack_cache_stale(self):
114
def _pack_names(self):
116
f = self.transport.get('info/packs')
118
return self.pack_transport.list_dir(".")
121
for line in f.readlines():
122
line = line.rstrip("\n")
125
(kind, name) = line.split(" ", 1)
131
def _load_packs(self):
133
for name in self._pack_names():
134
if name.startswith("pack-") and name.endswith(".pack"):
136
size = self.pack_transport.stat(name).st_size
137
except TransportNotPossible:
139
# FIXME: This reads the whole pack file at once
140
f = self.pack_transport.get(name)
142
return PackData(name, StringIO(contents), size=len(contents))
144
pd = lambda: PackData(name, self.pack_transport.get(name),
146
idxname = name.replace(".pack", ".idx")
147
idx = lambda: load_pack_index_file(idxname, self.pack_transport.get(idxname))
148
pack = Pack.from_lazy_objects(pd, idx)
152
def _iter_loose_objects(self):
153
for base in self.transport.list_dir('.'):
156
for rest in self.transport.list_dir(base):
159
def _split_loose_object(self, sha):
160
return (sha[:2], sha[2:])
162
def _get_loose_object(self, sha):
163
path = '%s/%s' % self._split_loose_object(sha)
165
return ShaFile.from_file(self.transport.get(path))
169
def add_object(self, obj):
170
"""Add a single object to this object store.
172
:param obj: Object to add
174
(dir, file) = self._split_loose_object(obj.id)
176
self.transport.mkdir(dir)
179
path = "%s/%s" % (dir, file)
180
if self.transport.has(path):
181
return # Already there, no need to write again
182
self.transport.put_bytes(path, obj.as_legacy_object())
184
def move_in_pack(self, f):
185
"""Move a specific file containing a pack into the pack directory.
187
:note: The file should be on the same file system as the
190
:param path: Path to the pack file.
193
p = PackData(None, f, len(f.getvalue()))
194
entries = p.sorted_entries()
195
basename = "pack-%s" % iter_sha1(entry[0] for entry in entries)
197
self.pack_transport.put_file(basename + ".pack", f)
199
write_pack_index_v2(idxfile, entries, p.get_stored_checksum())
201
self.pack_transport.put_file(basename + ".idx", idxfile)
203
idx = load_pack_index_file(basename+".idx", idxfile)
204
final_pack = Pack.from_objects(p, idx)
205
self._add_known_pack(final_pack)
209
"""Add a new pack to this object store.
211
:return: Fileobject to write to and a commit function to
212
call when the pack is finished.
214
from cStringIO import StringIO
217
if len(f.getvalue()) > 0:
218
return self.move_in_pack(f)
224
def init(cls, transport):
225
transport.mkdir('info')
226
transport.mkdir(PACKDIR)
227
return cls(transport)