/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
0.358.2 by Jelmer Vernooij
Refresh copyright headers, add my email.
1
# Copyright (C) 2010-2018 Jelmer Vernooij <jelmer@jelmer.uk>
0.246.1 by Jelmer Vernooij
Add TransportRepo class.
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
0.358.1 by Jelmer Vernooij
Fix FSF address.
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
0.246.1 by Jelmer Vernooij
Add TransportRepo class.
16
17
"""A Git repository implementation that uses a Bazaar transport."""
18
0.200.1594 by Jelmer Vernooij
Use absolute_import everywhere.
19
from __future__ import absolute_import
20
0.200.952 by Jelmer Vernooij
Write git pack files rather than loose objects.
21
from cStringIO import StringIO
0.246.1 by Jelmer Vernooij
Add TransportRepo class.
22
0.200.1340 by Jelmer Vernooij
Add basic support for alternates.
23
import os
0.284.5 by Jelmer Vernooij
Implement TransportRepo._determine_file_mode.
24
import sys
0.200.1576 by Jelmer Vernooij
Merge a bunch of fixes from store-roundtrip-info.
25
import urllib
0.200.1340 by Jelmer Vernooij
Add basic support for alternates.
26
0.246.1 by Jelmer Vernooij
Add TransportRepo class.
27
from dulwich.errors import (
28
    NotGitRepository,
0.246.7 by Jelmer Vernooij
more work.
29
    NoIndexPresent,
0.246.1 by Jelmer Vernooij
Add TransportRepo class.
30
    )
0.246.4 by Jelmer Vernooij
more work on transportgit.
31
from dulwich.objects import (
32
    ShaFile,
33
    )
34
from dulwich.object_store import (
35
    PackBasedObjectStore,
36
    PACKDIR,
37
    )
38
from dulwich.pack import (
0.200.1003 by Jelmer Vernooij
Initial work on supporting move_in_thin_pack.
39
    MemoryPackIndex,
0.246.7 by Jelmer Vernooij
more work.
40
    PackData,
0.246.4 by Jelmer Vernooij
more work on transportgit.
41
    Pack,
0.200.936 by Jelmer Vernooij
Test transportgit.
42
    iter_sha1,
0.246.7 by Jelmer Vernooij
more work.
43
    load_pack_index_file,
0.200.1003 by Jelmer Vernooij
Initial work on supporting move_in_thin_pack.
44
    write_pack_data,
0.200.936 by Jelmer Vernooij
Test transportgit.
45
    write_pack_index_v2,
0.246.4 by Jelmer Vernooij
more work on transportgit.
46
    )
0.246.1 by Jelmer Vernooij
Add TransportRepo class.
47
from dulwich.repo import (
48
    BaseRepo,
0.200.1775 by Jelmer Vernooij
Assume newer dulwich.
49
    InfoRefsContainer,
0.257.1 by Jelmer Vernooij
use transport repo objects even for local access.
50
    RefsContainer,
0.200.1119 by Jelmer Vernooij
Refactor repository initialization.
51
    BASE_DIRECTORIES,
0.200.1776 by Jelmer Vernooij
Basic support for commondir.
52
    COMMONDIR,
0.257.1 by Jelmer Vernooij
use transport repo objects even for local access.
53
    INDEX_FILENAME,
0.246.1 by Jelmer Vernooij
Add TransportRepo class.
54
    OBJECTDIR,
0.257.1 by Jelmer Vernooij
use transport repo objects even for local access.
55
    REFSDIR,
56
    SYMREF,
57
    check_ref_format,
58
    read_packed_refs_with_peeled,
59
    read_packed_refs,
60
    write_packed_refs,
0.246.1 by Jelmer Vernooij
Add TransportRepo class.
61
    )
62
0.200.1641 by Jelmer Vernooij
Use relative imports where possible.
63
from ... import (
0.200.1340 by Jelmer Vernooij
Add basic support for alternates.
64
    transport as _mod_transport,
65
    )
0.200.1641 by Jelmer Vernooij
Use relative imports where possible.
66
from ...errors import (
0.200.1718 by Jelmer Vernooij
Support AlreadyControlDirError.
67
    AlreadyControlDirError,
0.200.933 by Jelmer Vernooij
Allow directories to already exist :-)
68
    FileExists,
0.246.1 by Jelmer Vernooij
Add TransportRepo class.
69
    NoSuchFile,
0.200.946 by Jelmer Vernooij
Fix reading pack files over http.
70
    TransportNotPossible,
0.246.1 by Jelmer Vernooij
Add TransportRepo class.
71
    )
72
73
0.257.1 by Jelmer Vernooij
use transport repo objects even for local access.
74
class TransportRefsContainer(RefsContainer):
75
    """Refs container that reads refs from a transport."""
76
0.311.3 by Jelmer Vernooij
Fix handling of lightweight checkouts.
77
    def __init__(self, transport, worktree_transport=None):
0.257.1 by Jelmer Vernooij
use transport repo objects even for local access.
78
        self.transport = transport
0.311.3 by Jelmer Vernooij
Fix handling of lightweight checkouts.
79
        if worktree_transport is None:
80
            worktree_transport = transport
81
        self.worktree_transport = worktree_transport
0.257.1 by Jelmer Vernooij
use transport repo objects even for local access.
82
        self._packed_refs = None
83
        self._peeled_refs = None
84
85
    def __repr__(self):
86
        return "%s(%r)" % (self.__class__.__name__, self.transport)
87
0.257.2 by Jelmer Vernooij
Fix transportgit.
88
    def _ensure_dir_exists(self, path):
89
        for n in range(path.count("/")):
90
            dirname = "/".join(path.split("/")[:n+1])
91
            try:
92
                self.transport.mkdir(dirname)
93
            except FileExists:
94
                pass
95
0.257.1 by Jelmer Vernooij
use transport repo objects even for local access.
96
    def subkeys(self, base):
0.377.1 by Jelmer Vernooij
Fix some remote operations and add more tests.
97
        """Refs present in this container under a base.
98
99
        :param base: The base to return refs under.
100
        :return: A set of valid refs in this container under the base; the base
101
            prefix is stripped from the ref names returned.
102
        """
0.257.1 by Jelmer Vernooij
use transport repo objects even for local access.
103
        keys = set()
0.377.1 by Jelmer Vernooij
Fix some remote operations and add more tests.
104
        base_len = len(base) + 1
105
        for refname in self.allkeys():
106
            if refname.startswith(base):
107
                keys.add(refname[base_len:])
0.257.1 by Jelmer Vernooij
use transport repo objects even for local access.
108
        return keys
109
110
    def allkeys(self):
111
        keys = set()
0.200.1387 by Jelmer Vernooij
Avoid using HEAD.
112
        try:
0.311.3 by Jelmer Vernooij
Fix handling of lightweight checkouts.
113
            self.worktree_transport.get_bytes("HEAD")
0.200.1387 by Jelmer Vernooij
Avoid using HEAD.
114
        except NoSuchFile:
115
            pass
116
        else:
0.257.1 by Jelmer Vernooij
use transport repo objects even for local access.
117
            keys.add("HEAD")
118
        try:
0.257.2 by Jelmer Vernooij
Fix transportgit.
119
            iter_files = list(self.transport.clone("refs").iter_files_recursive())
120
            for filename in iter_files:
0.200.1576 by Jelmer Vernooij
Merge a bunch of fixes from store-roundtrip-info.
121
                refname = "refs/%s" % urllib.unquote(filename)
0.257.1 by Jelmer Vernooij
use transport repo objects even for local access.
122
                if check_ref_format(refname):
123
                    keys.add(refname)
0.257.2 by Jelmer Vernooij
Fix transportgit.
124
        except (TransportNotPossible, NoSuchFile):
125
            pass
0.257.1 by Jelmer Vernooij
use transport repo objects even for local access.
126
        keys.update(self.get_packed_refs())
127
        return keys
128
129
    def get_packed_refs(self):
130
        """Get contents of the packed-refs file.
131
132
        :return: Dictionary mapping ref names to SHA1s
133
134
        :note: Will return an empty dictionary when no packed-refs file is
135
            present.
136
        """
137
        # TODO: invalidate the cache on repacking
138
        if self._packed_refs is None:
139
            # set both to empty because we want _peeled_refs to be
140
            # None if and only if _packed_refs is also None.
141
            self._packed_refs = {}
142
            self._peeled_refs = {}
143
            try:
144
                f = self.transport.get("packed-refs")
145
            except NoSuchFile:
146
                return {}
147
            try:
148
                first_line = iter(f).next().rstrip()
149
                if (first_line.startswith("# pack-refs") and " peeled" in
150
                        first_line):
151
                    for sha, name, peeled in read_packed_refs_with_peeled(f):
152
                        self._packed_refs[name] = sha
153
                        if peeled:
154
                            self._peeled_refs[name] = peeled
155
                else:
156
                    f.seek(0)
157
                    for sha, name in read_packed_refs(f):
158
                        self._packed_refs[name] = sha
159
            finally:
160
                f.close()
161
        return self._packed_refs
162
163
    def get_peeled(self, name):
164
        """Return the cached peeled value of a ref, if available.
165
166
        :param name: Name of the ref to peel
167
        :return: The peeled value of the ref. If the ref is known not point to a
168
            tag, this will be the SHA the ref refers to. If the ref may point to
169
            a tag, but no cached information is available, None is returned.
170
        """
171
        self.get_packed_refs()
172
        if self._peeled_refs is None or name not in self._packed_refs:
173
            # No cache: no peeled refs were read, or this ref is loose
174
            return None
175
        if name in self._peeled_refs:
176
            return self._peeled_refs[name]
177
        else:
178
            # Known not peelable
179
            return self[name]
180
181
    def read_loose_ref(self, name):
182
        """Read a reference file and return its contents.
183
184
        If the reference file a symbolic reference, only read the first line of
185
        the file. Otherwise, only read the first 40 bytes.
186
187
        :param name: the refname to read, relative to refpath
188
        :return: The contents of the ref file, or None if the file does not
189
            exist.
190
        :raises IOError: if any other error occurs
191
        """
0.311.3 by Jelmer Vernooij
Fix handling of lightweight checkouts.
192
        if name == b'HEAD':
193
            transport = self.worktree_transport
194
        else:
195
            transport = self.transport
0.257.1 by Jelmer Vernooij
use transport repo objects even for local access.
196
        try:
0.311.3 by Jelmer Vernooij
Fix handling of lightweight checkouts.
197
            f = transport.get(name)
0.257.1 by Jelmer Vernooij
use transport repo objects even for local access.
198
        except NoSuchFile:
199
            return None
0.200.1572 by Jelmer Vernooij
Fix compatibility for fetching over dumb transport.
200
        f = StringIO(f.read())
0.257.1 by Jelmer Vernooij
use transport repo objects even for local access.
201
        try:
202
            header = f.read(len(SYMREF))
203
            if header == SYMREF:
204
                # Read only the first line
205
                return header + iter(f).next().rstrip("\r\n")
206
            else:
207
                # Read only the first 40 bytes
208
                return header + f.read(40-len(SYMREF))
209
        finally:
210
            f.close()
211
212
    def _remove_packed_ref(self, name):
213
        if self._packed_refs is None:
214
            return
215
        # reread cached refs from disk, while holding the lock
216
217
        self._packed_refs = None
218
        self.get_packed_refs()
219
220
        if name not in self._packed_refs:
221
            return
222
223
        del self._packed_refs[name]
224
        if name in self._peeled_refs:
225
            del self._peeled_refs[name]
0.200.954 by Jelmer Vernooij
Avoid writing to memory first unless strictly necessary.
226
        f = self.transport.open_write_stream("packed-refs")
227
        try:
228
            write_packed_refs(f, self._packed_refs, self._peeled_refs)
229
        finally:
230
            f.close()
0.257.1 by Jelmer Vernooij
use transport repo objects even for local access.
231
232
    def set_symbolic_ref(self, name, other):
233
        """Make a ref point at another ref.
234
235
        :param name: Name of the ref to set
236
        :param other: Name of the ref to point at
237
        """
238
        self._check_refname(name)
239
        self._check_refname(other)
0.311.3 by Jelmer Vernooij
Fix handling of lightweight checkouts.
240
        if name != b'HEAD':
241
            transport = self.transport
242
            self._ensure_dir_exists(name)
243
        else:
244
            transport = self.worktree_transport
245
        transport.put_bytes(name, SYMREF + other + '\n')
0.257.1 by Jelmer Vernooij
use transport repo objects even for local access.
246
247
    def set_if_equals(self, name, old_ref, new_ref):
248
        """Set a refname to new_ref only if it currently equals old_ref.
249
250
        This method follows all symbolic references, and can be used to perform
251
        an atomic compare-and-swap operation.
252
253
        :param name: The refname to set.
254
        :param old_ref: The old sha the refname must refer to, or None to set
255
            unconditionally.
256
        :param new_ref: The new sha the refname will refer to.
257
        :return: True if the set was successful, False otherwise.
258
        """
259
        try:
0.284.2 by Jelmer Vernooij
Use new RefsContainer.follow().
260
            realnames, _ = self.follow(name)
261
            realname = realnames[-1]
262
        except (KeyError, IndexError):
0.257.1 by Jelmer Vernooij
use transport repo objects even for local access.
263
            realname = name
0.311.3 by Jelmer Vernooij
Fix handling of lightweight checkouts.
264
        if realname == b'HEAD':
265
            transport = self.worktree_transport
266
        else:
267
            transport = self.transport
268
            self._ensure_dir_exists(realname)
269
        transport.put_bytes(realname, new_ref+"\n")
0.257.1 by Jelmer Vernooij
use transport repo objects even for local access.
270
        return True
271
272
    def add_if_new(self, name, ref):
273
        """Add a new reference only if it does not already exist.
274
275
        This method follows symrefs, and only ensures that the last ref in the
276
        chain does not exist.
277
278
        :param name: The refname to set.
279
        :param ref: The new sha the refname will refer to.
280
        :return: True if the add was successful, False otherwise.
281
        """
282
        try:
0.284.2 by Jelmer Vernooij
Use new RefsContainer.follow().
283
            realnames, contents = self.follow(name)
0.257.1 by Jelmer Vernooij
use transport repo objects even for local access.
284
            if contents is not None:
285
                return False
0.284.2 by Jelmer Vernooij
Use new RefsContainer.follow().
286
            realname = realnames[-1]
287
        except (KeyError, IndexError):
0.257.1 by Jelmer Vernooij
use transport repo objects even for local access.
288
            realname = name
289
        self._check_refname(realname)
0.311.3 by Jelmer Vernooij
Fix handling of lightweight checkouts.
290
        if realname == b'HEAD':
291
            transport = self.worktree_transport
292
        else:
293
            transport = self.transport
294
            self._ensure_dir_exists(realname)
295
        transport.put_bytes(realname, ref+"\n")
0.257.1 by Jelmer Vernooij
use transport repo objects even for local access.
296
        return True
297
298
    def remove_if_equals(self, name, old_ref):
299
        """Remove a refname only if it currently equals old_ref.
300
301
        This method does not follow symbolic references. It can be used to
302
        perform an atomic compare-and-delete operation.
303
304
        :param name: The refname to delete.
305
        :param old_ref: The old sha the refname must refer to, or None to delete
306
            unconditionally.
307
        :return: True if the delete was successful, False otherwise.
308
        """
309
        self._check_refname(name)
310
        # may only be packed
0.311.3 by Jelmer Vernooij
Fix handling of lightweight checkouts.
311
        if name == b'HEAD':
312
            transport = self.worktree_transport
313
        else:
314
            transport = self.transport
0.257.1 by Jelmer Vernooij
use transport repo objects even for local access.
315
        try:
0.311.3 by Jelmer Vernooij
Fix handling of lightweight checkouts.
316
            transport.delete(name)
0.257.1 by Jelmer Vernooij
use transport repo objects even for local access.
317
        except NoSuchFile:
318
            pass
319
        self._remove_packed_ref(name)
320
        return True
321
0.200.1459 by Jelmer Vernooij
Implement TransportRefsContainer.get.
322
    def get(self, name, default=None):
323
        try:
324
            return self[name]
325
        except KeyError:
326
            return default
327
0.257.1 by Jelmer Vernooij
use transport repo objects even for local access.
328
0.246.1 by Jelmer Vernooij
Add TransportRepo class.
329
class TransportRepo(BaseRepo):
330
0.200.1485 by Jelmer Vernooij
Keep track of refs text when opening bare repository.
331
    def __init__(self, transport, bare, refs_text=None):
0.246.1 by Jelmer Vernooij
Add TransportRepo class.
332
        self.transport = transport
0.200.1387 by Jelmer Vernooij
Avoid using HEAD.
333
        self.bare = bare
334
        if self.bare:
335
            self._controltransport = self.transport
336
        else:
337
            self._controltransport = self.transport.clone('.git')
0.200.1776 by Jelmer Vernooij
Basic support for commondir.
338
        commondir = self.get_named_file(COMMONDIR)
339
        if commondir is not None:
340
            with commondir:
341
                commondir = os.path.join(
342
                    self.controldir(),
343
                    commondir.read().rstrip(b"\r\n").decode(
344
                        sys.getfilesystemencoding()))
345
                self._commontransport = \
346
                    _mod_transport.get_transport_from_path(commondir)
347
        else:
348
            self._commontransport = self._controltransport
0.246.1 by Jelmer Vernooij
Add TransportRepo class.
349
        object_store = TransportObjectStore(
0.200.1776 by Jelmer Vernooij
Basic support for commondir.
350
            self._commontransport.clone(OBJECTDIR))
0.200.1485 by Jelmer Vernooij
Keep track of refs text when opening bare repository.
351
        if refs_text is not None:
352
            refs_container = InfoRefsContainer(StringIO(refs_text))
0.200.1572 by Jelmer Vernooij
Fix compatibility for fetching over dumb transport.
353
            try:
0.200.1776 by Jelmer Vernooij
Basic support for commondir.
354
                head = TransportRefsContainer(self._commontransport).read_loose_ref("HEAD")
0.200.1572 by Jelmer Vernooij
Fix compatibility for fetching over dumb transport.
355
            except KeyError:
356
                pass
357
            else:
358
                refs_container._refs["HEAD"] = head
0.200.1485 by Jelmer Vernooij
Keep track of refs text when opening bare repository.
359
        else:
0.311.3 by Jelmer Vernooij
Fix handling of lightweight checkouts.
360
            refs_container = TransportRefsContainer(
361
                    self._commontransport, self._controltransport)
0.200.1636 by Jelmer Vernooij
Some formatting fixes.
362
        super(TransportRepo, self).__init__(object_store,
0.200.1485 by Jelmer Vernooij
Keep track of refs text when opening bare repository.
363
                refs_container)
0.246.1 by Jelmer Vernooij
Add TransportRepo class.
364
0.200.1658 by Jelmer Vernooij
Fix handling of ignores - return patterns that matched.
365
    def controldir(self):
366
        return self._controltransport.local_abspath('.')
367
368
    @property
369
    def path(self):
370
        return self.transport.local_abspath('.')
371
0.284.5 by Jelmer Vernooij
Implement TransportRepo._determine_file_mode.
372
    def _determine_file_mode(self):
373
        # Be consistent with bzr
374
        if sys.platform == 'win32':
375
            return False
376
        return True
377
0.246.1 by Jelmer Vernooij
Add TransportRepo class.
378
    def get_named_file(self, path):
379
        """Get a file from the control dir with a specific name.
380
381
        Although the filename should be interpreted as a filename relative to
382
        the control dir in a disk-baked Repo, the object returned need not be
383
        pointing to a file in that location.
384
385
        :param path: The path to the file, relative to the control dir.
386
        :return: An open file object, or None if the file does not exist.
387
        """
388
        try:
389
            return self._controltransport.get(path.lstrip('/'))
390
        except NoSuchFile:
391
            return None
392
0.200.1119 by Jelmer Vernooij
Refactor repository initialization.
393
    def _put_named_file(self, relpath, contents):
394
        self._controltransport.put_bytes(relpath, contents)
395
0.257.1 by Jelmer Vernooij
use transport repo objects even for local access.
396
    def index_path(self):
397
        """Return the path to the index file."""
398
        return self._controltransport.local_abspath(INDEX_FILENAME)
399
0.246.1 by Jelmer Vernooij
Add TransportRepo class.
400
    def open_index(self):
401
        """Open the index for this repository."""
0.257.1 by Jelmer Vernooij
use transport repo objects even for local access.
402
        from dulwich.index import Index
403
        if not self.has_index():
404
            raise NoIndexPresent()
405
        return Index(self.index_path())
406
407
    def has_index(self):
0.257.2 by Jelmer Vernooij
Fix transportgit.
408
        """Check if an index is present."""
409
        # Bare repos must never have index files; non-bare repos may have a
410
        # missing index file, which is treated as empty.
411
        return not self.bare
0.246.1 by Jelmer Vernooij
Add TransportRepo class.
412
0.200.1546 by Jelmer Vernooij
Provide get_config.
413
    def get_config(self):
0.200.1547 by Jelmer Vernooij
Support setting branch nicks.
414
        from dulwich.config import ConfigFile
415
        try:
416
            return ConfigFile.from_file(self._controltransport.get('config'))
417
        except NoSuchFile:
418
            return ConfigFile()
419
420
    def get_config_stack(self):
421
        from dulwich.config import StackedConfig
0.200.1546 by Jelmer Vernooij
Provide get_config.
422
        backends = []
0.200.1547 by Jelmer Vernooij
Support setting branch nicks.
423
        p = self.get_config()
424
        if p is not None:
425
            backends.append(p)
426
            writable = p
427
        else:
428
            writable = None
0.200.1546 by Jelmer Vernooij
Provide get_config.
429
        backends.extend(StackedConfig.default_backends())
0.200.1547 by Jelmer Vernooij
Support setting branch nicks.
430
        return StackedConfig(backends, writable=writable)
0.200.1546 by Jelmer Vernooij
Provide get_config.
431
0.246.1 by Jelmer Vernooij
Add TransportRepo class.
432
    def __repr__(self):
0.200.1031 by Jelmer Vernooij
Better __repr__.
433
        return "<%s for %r>" % (self.__class__.__name__, self.transport)
0.246.1 by Jelmer Vernooij
Add TransportRepo class.
434
0.200.1119 by Jelmer Vernooij
Refactor repository initialization.
435
    @classmethod
436
    def init(cls, transport, bare=False):
437
        if not bare:
0.200.1718 by Jelmer Vernooij
Support AlreadyControlDirError.
438
            try:
439
                transport.mkdir(".git")
440
            except FileExists:
441
                raise AlreadyControlDirError(transport.base)
0.200.1119 by Jelmer Vernooij
Refactor repository initialization.
442
            control_transport = transport.clone(".git")
443
        else:
444
            control_transport = transport
445
        for d in BASE_DIRECTORIES:
0.200.1718 by Jelmer Vernooij
Support AlreadyControlDirError.
446
            try:
447
                control_transport.mkdir("/".join(d))
448
            except FileExists:
449
                pass
450
        try:
451
            control_transport.mkdir(OBJECTDIR)
452
        except FileExists:
453
            raise AlreadyControlDirError(transport.base)
0.200.1119 by Jelmer Vernooij
Refactor repository initialization.
454
        TransportObjectStore.init(control_transport.clone(OBJECTDIR))
0.200.1387 by Jelmer Vernooij
Avoid using HEAD.
455
        ret = cls(transport, bare)
0.200.1119 by Jelmer Vernooij
Refactor repository initialization.
456
        ret.refs.set_symbolic_ref("HEAD", "refs/heads/master")
457
        ret._init_files(bare)
458
        return ret
459
0.246.4 by Jelmer Vernooij
more work on transportgit.
460
461
class TransportObjectStore(PackBasedObjectStore):
462
    """Git-style object store that exists on disk."""
463
464
    def __init__(self, transport):
465
        """Open an object store.
466
467
        :param transport: Transport to open data from
468
        """
469
        super(TransportObjectStore, self).__init__()
470
        self.transport = transport
471
        self.pack_transport = self.transport.clone(PACKDIR)
0.200.1340 by Jelmer Vernooij
Add basic support for alternates.
472
        self._alternates = None
0.200.1031 by Jelmer Vernooij
Better __repr__.
473
0.317.1 by Jelmer Vernooij
Implement TransportObjectStore.__eq__.
474
    def __eq__(self, other):
475
        if not isinstance(other, TransportObjectStore):
476
            return False
477
        return self.transport == other.transport
478
0.200.1031 by Jelmer Vernooij
Better __repr__.
479
    def __repr__(self):
480
        return "%s(%r)" % (self.__class__.__name__, self.transport)
481
0.200.1340 by Jelmer Vernooij
Add basic support for alternates.
482
    @property
483
    def alternates(self):
484
        if self._alternates is not None:
485
            return self._alternates
486
        self._alternates = []
487
        for path in self._read_alternate_paths():
488
            # FIXME: Check path
0.200.1342 by Jelmer Vernooij
Add basic support for alternates.
489
            t = _mod_transport.get_transport_from_path(path)
0.200.1340 by Jelmer Vernooij
Add basic support for alternates.
490
            self._alternates.append(self.__class__(t))
491
        return self._alternates
492
493
    def _read_alternate_paths(self):
494
        try:
495
            f = self.transport.get("info/alternates")
496
        except NoSuchFile:
497
            return []
498
        ret = []
499
        try:
0.200.1572 by Jelmer Vernooij
Fix compatibility for fetching over dumb transport.
500
            for l in f.read().splitlines():
0.200.1340 by Jelmer Vernooij
Add basic support for alternates.
501
                if l[0] == "#":
502
                    continue
503
                if os.path.isabs(l):
504
                    continue
505
                ret.append(l)
506
            return ret
507
        finally:
508
            f.close()
509
0.281.2 by William Grant
Fix TransportObjectStore to actually have packs with the new cache dict.
510
    @property
511
    def packs(self):
512
        # FIXME: Never invalidates.
513
        if not self._pack_cache:
514
            self._update_pack_cache()
515
        return self._pack_cache.values()
516
517
    def _update_pack_cache(self):
518
        for pack in self._load_packs():
519
            self._pack_cache[pack._basename] = pack
520
0.256.1 by Jelmer Vernooij
Allow using manual listing for pack contents.
521
    def _pack_names(self):
522
        try:
523
            f = self.transport.get('info/packs')
524
        except NoSuchFile:
525
            return self.pack_transport.list_dir(".")
526
        else:
0.200.935 by Jelmer Vernooij
Allow using manual listing for pack contents.
527
            ret = []
0.200.1572 by Jelmer Vernooij
Fix compatibility for fetching over dumb transport.
528
            for line in f.read().splitlines():
0.256.1 by Jelmer Vernooij
Allow using manual listing for pack contents.
529
                if not line:
530
                    continue
531
                (kind, name) = line.split(" ", 1)
532
                if kind != "P":
533
                    continue
0.200.935 by Jelmer Vernooij
Allow using manual listing for pack contents.
534
                ret.append(name)
535
            return ret
0.256.1 by Jelmer Vernooij
Allow using manual listing for pack contents.
536
0.200.1671 by Jelmer Vernooij
Fix pack tests.
537
    def _remove_pack(self, pack):
538
        self.pack_transport.delete(os.path.basename(pack.index.path))
539
        self.pack_transport.delete(pack.data.filename)
540
0.246.4 by Jelmer Vernooij
more work on transportgit.
541
    def _load_packs(self):
0.246.7 by Jelmer Vernooij
more work.
542
        ret = []
0.256.1 by Jelmer Vernooij
Allow using manual listing for pack contents.
543
        for name in self._pack_names():
0.246.9 by Jelmer Vernooij
Remove unused write code.
544
            if name.startswith("pack-") and name.endswith(".pack"):
0.200.946 by Jelmer Vernooij
Fix reading pack files over http.
545
                try:
546
                    size = self.pack_transport.stat(name).st_size
547
                except TransportNotPossible:
0.257.2 by Jelmer Vernooij
Fix transportgit.
548
                    # FIXME: This reads the whole pack file at once
549
                    f = self.pack_transport.get(name)
550
                    contents = f.read()
551
                    pd = PackData(name, StringIO(contents), size=len(contents))
0.200.946 by Jelmer Vernooij
Fix reading pack files over http.
552
                else:
0.257.2 by Jelmer Vernooij
Fix transportgit.
553
                    pd = PackData(name, self.pack_transport.get(name),
0.200.946 by Jelmer Vernooij
Fix reading pack files over http.
554
                            size=size)
0.246.9 by Jelmer Vernooij
Remove unused write code.
555
                idxname = name.replace(".pack", ".idx")
0.257.2 by Jelmer Vernooij
Fix transportgit.
556
                idx = load_pack_index_file(idxname, self.pack_transport.get(idxname))
557
                pack = Pack.from_objects(pd, idx)
0.200.1671 by Jelmer Vernooij
Fix pack tests.
558
                pack._basename = idxname[:-4]
0.200.950 by Jelmer Vernooij
Load packs lazily.
559
                ret.append(pack)
0.246.7 by Jelmer Vernooij
more work.
560
        return ret
0.246.4 by Jelmer Vernooij
more work on transportgit.
561
562
    def _iter_loose_objects(self):
563
        for base in self.transport.list_dir('.'):
564
            if len(base) != 2:
565
                continue
566
            for rest in self.transport.list_dir(base):
567
                yield base+rest
568
569
    def _split_loose_object(self, sha):
570
        return (sha[:2], sha[2:])
571
0.257.1 by Jelmer Vernooij
use transport repo objects even for local access.
572
    def _remove_loose_object(self, sha):
573
        path = '%s/%s' % self._split_loose_object(sha)
0.257.2 by Jelmer Vernooij
Fix transportgit.
574
        self.transport.delete(path)
0.257.1 by Jelmer Vernooij
use transport repo objects even for local access.
575
0.246.4 by Jelmer Vernooij
more work on transportgit.
576
    def _get_loose_object(self, sha):
577
        path = '%s/%s' % self._split_loose_object(sha)
578
        try:
0.200.903 by Jelmer Vernooij
Fix compatibility with newer versions of Dulwich.
579
            return ShaFile.from_file(self.transport.get(path))
0.246.4 by Jelmer Vernooij
more work on transportgit.
580
        except NoSuchFile:
581
            return None
582
583
    def add_object(self, obj):
584
        """Add a single object to this object store.
585
586
        :param obj: Object to add
587
        """
588
        (dir, file) = self._split_loose_object(obj.id)
0.200.933 by Jelmer Vernooij
Allow directories to already exist :-)
589
        try:
590
            self.transport.mkdir(dir)
591
        except FileExists:
592
            pass
0.246.4 by Jelmer Vernooij
more work on transportgit.
593
        path = "%s/%s" % (dir, file)
594
        if self.transport.has(path):
595
            return # Already there, no need to write again
596
        self.transport.put_bytes(path, obj.as_legacy_object())
597
0.200.937 by Jelmer Vernooij
Avoid using os module in transportgit.
598
    def move_in_pack(self, f):
0.200.936 by Jelmer Vernooij
Test transportgit.
599
        """Move a specific file containing a pack into the pack directory.
600
601
        :note: The file should be on the same file system as the
602
            packs directory.
603
604
        :param path: Path to the pack file.
605
        """
0.200.937 by Jelmer Vernooij
Avoid using os module in transportgit.
606
        f.seek(0)
0.200.1682 by Jelmer Vernooij
Fix compatibility with newer versions of dulwich.
607
        p = PackData("", f, len(f.getvalue()))
0.200.936 by Jelmer Vernooij
Test transportgit.
608
        entries = p.sorted_entries()
0.200.937 by Jelmer Vernooij
Avoid using os module in transportgit.
609
        basename = "pack-%s" % iter_sha1(entry[0] for entry in entries)
0.200.1671 by Jelmer Vernooij
Fix pack tests.
610
        p._filename = basename + ".pack"
0.200.937 by Jelmer Vernooij
Avoid using os module in transportgit.
611
        f.seek(0)
612
        self.pack_transport.put_file(basename + ".pack", f)
0.200.954 by Jelmer Vernooij
Avoid writing to memory first unless strictly necessary.
613
        idxfile = self.pack_transport.open_write_stream(basename + ".idx")
614
        try:
615
            write_pack_index_v2(idxfile, entries, p.get_stored_checksum())
616
        finally:
617
            idxfile.close()
618
        idxfile = self.pack_transport.get(basename + ".idx")
0.200.952 by Jelmer Vernooij
Write git pack files rather than loose objects.
619
        idx = load_pack_index_file(basename+".idx", idxfile)
0.200.937 by Jelmer Vernooij
Avoid using os module in transportgit.
620
        final_pack = Pack.from_objects(p, idx)
0.200.1671 by Jelmer Vernooij
Fix pack tests.
621
        final_pack._basename = basename
0.283.2 by Jelmer Vernooij
Use dulwich 0.9.7.
622
        self._add_known_pack(basename, final_pack)
0.200.936 by Jelmer Vernooij
Test transportgit.
623
        return final_pack
624
0.200.1003 by Jelmer Vernooij
Initial work on supporting move_in_thin_pack.
625
    def add_thin_pack(self):
626
        """Add a new thin pack to this object store.
627
628
        Thin packs are packs that contain deltas with parents that exist
629
        in a different pack.
630
        """
631
        from cStringIO import StringIO
632
        f = StringIO()
633
        def commit():
634
            if len(f.getvalue()) > 0:
635
                return self.move_in_thin_pack(f)
636
            else:
637
                return None
638
        return f, commit
639
640
    def move_in_thin_pack(self, f):
641
        """Move a specific file containing a pack into the pack directory.
642
643
        :note: The file should be on the same file system as the
644
            packs directory.
645
646
        :param path: Path to the pack file.
647
        """
648
        f.seek(0)
0.200.1298 by Jelmer Vernooij
Fix compatibility with newer versions of dulwich.
649
        data = PackData.from_file(self.get_raw, f, len(f.getvalue()))
0.200.1003 by Jelmer Vernooij
Initial work on supporting move_in_thin_pack.
650
        idx = MemoryPackIndex(data.sorted_entries(), data.get_stored_checksum())
651
        p = Pack.from_objects(data, idx)
652
653
        pack_sha = idx.objects_sha1()
654
0.200.1289 by Jelmer Vernooij
Switch to dulwich 0.8.0.
655
        datafile = self.pack_transport.open_write_stream(
656
                "pack-%s.pack" % pack_sha)
0.200.1003 by Jelmer Vernooij
Initial work on supporting move_in_thin_pack.
657
        try:
0.200.1289 by Jelmer Vernooij
Switch to dulwich 0.8.0.
658
            entries, data_sum = write_pack_data(datafile, p.pack_tuples())
0.200.1003 by Jelmer Vernooij
Initial work on supporting move_in_thin_pack.
659
        finally:
660
            datafile.close()
661
        entries.sort()
0.200.1289 by Jelmer Vernooij
Switch to dulwich 0.8.0.
662
        idxfile = self.pack_transport.open_write_stream(
663
            "pack-%s.idx" % pack_sha)
0.200.1003 by Jelmer Vernooij
Initial work on supporting move_in_thin_pack.
664
        try:
665
            write_pack_index_v2(idxfile, data.sorted_entries(), data_sum)
666
        finally:
667
            idxfile.close()
0.281.4 by William Grant
Update the other _add_known_pack callsite.
668
        basename = "pack-%s" % pack_sha
669
        final_pack = Pack(basename)
670
        self._add_known_pack(basename, final_pack)
0.200.1003 by Jelmer Vernooij
Initial work on supporting move_in_thin_pack.
671
        return final_pack
672
0.246.4 by Jelmer Vernooij
more work on transportgit.
673
    def add_pack(self):
0.200.1636 by Jelmer Vernooij
Some formatting fixes.
674
        """Add a new pack to this object store.
0.246.4 by Jelmer Vernooij
more work on transportgit.
675
0.200.1636 by Jelmer Vernooij
Some formatting fixes.
676
        :return: Fileobject to write to and a commit function to
0.246.4 by Jelmer Vernooij
more work on transportgit.
677
            call when the pack is finished.
678
        """
0.200.937 by Jelmer Vernooij
Avoid using os module in transportgit.
679
        from cStringIO import StringIO
680
        f = StringIO()
0.200.936 by Jelmer Vernooij
Test transportgit.
681
        def commit():
0.200.937 by Jelmer Vernooij
Avoid using os module in transportgit.
682
            if len(f.getvalue()) > 0:
683
                return self.move_in_pack(f)
0.200.936 by Jelmer Vernooij
Test transportgit.
684
            else:
685
                return None
0.200.1626 by Jelmer Vernooij
Fix compatibility with dulwich 0.9.1.
686
        def abort():
687
            return None
688
        return f, commit, abort
0.200.934 by Jelmer Vernooij
Add init function.
689
690
    @classmethod
691
    def init(cls, transport):
0.200.1718 by Jelmer Vernooij
Support AlreadyControlDirError.
692
        try:
693
            transport.mkdir('info')
694
        except FileExists:
695
            pass
696
        try:
697
            transport.mkdir(PACKDIR)
698
        except FileExists:
699
            pass
0.200.934 by Jelmer Vernooij
Add init function.
700
        return cls(transport)