/brz/remove-bazaar

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