/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to transportgit.py

Write git pack files rather than loose objects.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2010 Jelmer Vernooij <jelmer@samba.org>
 
2
#
 
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.
 
7
#
 
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.
 
12
#
 
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
 
16
 
 
17
"""A Git repository implementation that uses a Bazaar transport."""
 
18
 
 
19
from cStringIO import StringIO
 
20
 
 
21
from dulwich.errors import (
 
22
    NotGitRepository,
 
23
    NoIndexPresent,
 
24
    )
 
25
from dulwich.objects import (
 
26
    ShaFile,
 
27
    )
 
28
from dulwich.object_store import (
 
29
    PackBasedObjectStore,
 
30
    PACKDIR,
 
31
    )
 
32
from dulwich.pack import (
 
33
    PackData,
 
34
    Pack,
 
35
    iter_sha1,
 
36
    load_pack_index_file,
 
37
    write_pack_index_v2,
 
38
    )
 
39
from dulwich.repo import (
 
40
    BaseRepo,
 
41
    DictRefsContainer,
 
42
    OBJECTDIR,
 
43
    read_info_refs,
 
44
    )
 
45
 
 
46
from bzrlib.errors import (
 
47
    FileExists,
 
48
    NoSuchFile,
 
49
    TransportNotPossible,
 
50
    )
 
51
 
 
52
 
 
53
class TransportRepo(BaseRepo):
 
54
 
 
55
    def __init__(self, transport):
 
56
        self.transport = transport
 
57
        try:
 
58
            if self.transport.has(".git/info/refs"):
 
59
                self.bare = False
 
60
                self._controltransport = self.transport.clone('.git')
 
61
            elif self.transport.has("info/refs"):
 
62
                self.bare = True
 
63
                self._controltransport = self.transport
 
64
            else:
 
65
                raise NotGitRepository(self.transport)
 
66
        except NoSuchFile:
 
67
            raise NotGitRepository(self.transport)
 
68
        object_store = TransportObjectStore(
 
69
            self._controltransport.clone(OBJECTDIR))
 
70
        refs = {}
 
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))
 
75
 
 
76
    def get_named_file(self, path):
 
77
        """Get a file from the control dir with a specific name.
 
78
 
 
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.
 
82
 
 
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.
 
85
        """
 
86
        try:
 
87
            return self._controltransport.get(path.lstrip('/'))
 
88
        except NoSuchFile:
 
89
            return None
 
90
 
 
91
    def open_index(self):
 
92
        """Open the index for this repository."""
 
93
        raise NoIndexPresent()
 
94
 
 
95
    def __repr__(self):
 
96
        return "<TransportRepo for %r>" % self.transport
 
97
 
 
98
 
 
99
class TransportObjectStore(PackBasedObjectStore):
 
100
    """Git-style object store that exists on disk."""
 
101
 
 
102
    def __init__(self, transport):
 
103
        """Open an object store.
 
104
 
 
105
        :param transport: Transport to open data from
 
106
        """
 
107
        super(TransportObjectStore, self).__init__()
 
108
        self.transport = transport
 
109
        self.pack_transport = self.transport.clone(PACKDIR)
 
110
    
 
111
    def _pack_cache_stale(self):
 
112
        return False # FIXME
 
113
 
 
114
    def _pack_names(self):
 
115
        try:
 
116
            f = self.transport.get('info/packs')
 
117
        except NoSuchFile:
 
118
            return self.pack_transport.list_dir(".")
 
119
        else:
 
120
            ret = []
 
121
            for line in f.readlines():
 
122
                line = line.rstrip("\n")
 
123
                if not line:
 
124
                    continue
 
125
                (kind, name) = line.split(" ", 1)
 
126
                if kind != "P":
 
127
                    continue
 
128
                ret.append(name)
 
129
            return ret
 
130
 
 
131
    def _load_packs(self):
 
132
        ret = []
 
133
        for name in self._pack_names():
 
134
            if name.startswith("pack-") and name.endswith(".pack"):
 
135
                try:
 
136
                    size = self.pack_transport.stat(name).st_size
 
137
                except TransportNotPossible:
 
138
                    def pd():
 
139
                        # FIXME: This reads the whole pack file at once
 
140
                        f = self.pack_transport.get(name)
 
141
                        contents = f.read()
 
142
                        return PackData(name, StringIO(contents), size=len(contents))
 
143
                else:
 
144
                    pd = lambda: PackData(name, self.pack_transport.get(name),
 
145
                            size=size)
 
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)
 
149
                ret.append(pack)
 
150
        return ret
 
151
 
 
152
    def _iter_loose_objects(self):
 
153
        for base in self.transport.list_dir('.'):
 
154
            if len(base) != 2:
 
155
                continue
 
156
            for rest in self.transport.list_dir(base):
 
157
                yield base+rest
 
158
 
 
159
    def _split_loose_object(self, sha):
 
160
        return (sha[:2], sha[2:])
 
161
 
 
162
    def _get_loose_object(self, sha):
 
163
        path = '%s/%s' % self._split_loose_object(sha)
 
164
        try:
 
165
            return ShaFile.from_file(self.transport.get(path))
 
166
        except NoSuchFile:
 
167
            return None
 
168
 
 
169
    def add_object(self, obj):
 
170
        """Add a single object to this object store.
 
171
 
 
172
        :param obj: Object to add
 
173
        """
 
174
        (dir, file) = self._split_loose_object(obj.id)
 
175
        try:
 
176
            self.transport.mkdir(dir)
 
177
        except FileExists:
 
178
            pass
 
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())
 
183
 
 
184
    def move_in_pack(self, f):
 
185
        """Move a specific file containing a pack into the pack directory.
 
186
 
 
187
        :note: The file should be on the same file system as the
 
188
            packs directory.
 
189
 
 
190
        :param path: Path to the pack file.
 
191
        """
 
192
        f.seek(0)
 
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)
 
196
        f.seek(0)
 
197
        self.pack_transport.put_file(basename + ".pack", f)
 
198
        idxfile = StringIO()
 
199
        write_pack_index_v2(idxfile, entries, p.get_stored_checksum())
 
200
        idxfile.seek(0)
 
201
        self.pack_transport.put_file(basename + ".idx", idxfile)
 
202
        idxfile.seek(0)
 
203
        idx = load_pack_index_file(basename+".idx", idxfile)
 
204
        final_pack = Pack.from_objects(p, idx)
 
205
        self._add_known_pack(final_pack)
 
206
        return final_pack
 
207
 
 
208
    def add_pack(self):
 
209
        """Add a new pack to this object store. 
 
210
 
 
211
        :return: Fileobject to write to and a commit function to 
 
212
            call when the pack is finished.
 
213
        """
 
214
        from cStringIO import StringIO
 
215
        f = StringIO()
 
216
        def commit():
 
217
            if len(f.getvalue()) > 0:
 
218
                return self.move_in_pack(f)
 
219
            else:
 
220
                return None
 
221
        return f, commit
 
222
 
 
223
    @classmethod
 
224
    def init(cls, transport):
 
225
        transport.mkdir('info')
 
226
        transport.mkdir(PACKDIR)
 
227
        return cls(transport)