/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 shamap.py

Fix some more tests.

Show diffs side-by-side

added added

removed removed

Lines of Context:
25
25
 
26
26
import bzrlib
27
27
from bzrlib import (
 
28
    registry,
28
29
    trace,
29
30
    )
 
31
from bzrlib.transport import (
 
32
    get_transport,
 
33
    )
30
34
 
31
35
 
32
36
def get_cache_dir():
42
46
    return ret
43
47
 
44
48
 
 
49
def get_remote_cache_transport():
 
50
    return get_transport(get_cache_dir())
 
51
 
 
52
 
45
53
def check_pysqlite_version(sqlite3):
46
54
    """Check that sqlite library is compatible.
47
55
 
75
83
        return _mapdbs.cache
76
84
 
77
85
 
78
 
class InventorySHAMap(object):
79
 
    """Maps inventory file ids to Git SHAs."""
80
 
 
81
 
    def lookup_blob(self, file_id, revision):
 
86
class GitShaMap(object):
 
87
    """Git<->Bzr revision id mapping database."""
 
88
 
 
89
    def lookup_git_sha(self, sha):
 
90
        """Lookup a Git sha in the database.
 
91
        :param sha: Git object sha
 
92
        :return: (type, type_data) with type_data:
 
93
            revision: revid, tree sha
 
94
        """
 
95
        raise NotImplementedError(self.lookup_git_sha)
 
96
 
 
97
    def lookup_blob_id(self, file_id, revision):
82
98
        """Retrieve a Git blob SHA by file id.
83
99
 
84
100
        :param file_id: File id of the file/symlink
85
101
        :param revision: revision in which the file was last changed.
86
102
        """
87
 
        raise NotImplementedError(self.lookup_blob)
 
103
        raise NotImplementedError(self.lookup_blob_id)
88
104
 
89
 
    def lookup_tree(self, file_id):
 
105
    def lookup_tree_id(self, file_id, revision):
90
106
        """Retrieve a Git tree SHA by file id.
91
107
        """
92
 
        raise NotImplementedError(self.lookup_tree)
93
 
 
94
 
 
95
 
class GitShaMap(object):
96
 
    """Git<->Bzr revision id mapping database."""
97
 
 
98
 
    def _add_entry(self, sha, type, type_data):
99
 
        """Add a new entry to the database.
100
 
        """
101
 
        raise NotImplementedError(self._add_entry)
102
 
 
103
 
    def add_entries(self, revid, parent_revids, commit_sha, root_tree_sha, 
104
 
                    entries):
105
 
        """Add multiple new entries to the database.
106
 
        """
107
 
        for (fileid, kind, hexsha, revision) in entries:
108
 
            self._add_entry(hexsha, kind, (fileid, revision))
109
 
        self._add_entry(commit_sha, "commit", (revid, root_tree_sha))
110
 
 
111
 
    def get_inventory_sha_map(self, revid):
112
 
        """Return the inventory SHA map for a revision.
113
 
 
114
 
        :param revid: Revision to fetch the map for
115
 
        :return: A `InventorySHAMap`
116
 
        """
117
 
        raise NotImplementedError(self.get_inventory_sha_map)
118
 
 
119
 
    def lookup_git_sha(self, sha):
120
 
        """Lookup a Git sha in the database.
121
 
        :param sha: Git object sha
122
 
        :return: (type, type_data) with type_data:
123
 
            revision: revid, tree sha
124
 
        """
125
 
        raise NotImplementedError(self.lookup_git_sha)
 
108
        raise NotImplementedError(self.lookup_tree_id)
126
109
 
127
110
    def revids(self):
128
111
        """List the revision ids known."""
149
132
        """Abort any pending changes."""
150
133
 
151
134
 
 
135
class ContentCache(object):
 
136
    """Object that can cache Git objects."""
 
137
 
 
138
    def __getitem__(self, sha):
 
139
        """Retrieve an item, by SHA."""
 
140
        raise NotImplementedError(self.__getitem__)
 
141
 
 
142
 
 
143
class BzrGitCacheFormat(object):
 
144
 
 
145
    def get_format_string(self):
 
146
        """Return a single-line unique format string for this cache format."""
 
147
        raise NotImplementedError(self.get_format_string)
 
148
 
 
149
    def open(self, transport):
 
150
        """Open this format on a transport."""
 
151
        raise NotImplementedError(self.open)
 
152
 
 
153
    def initialize(self, transport):
 
154
        transport.put_bytes('format', self.get_format_string())
 
155
 
 
156
    @classmethod
 
157
    def from_transport(self, transport):
 
158
        """Open a cache file present on a transport, or initialize one.
 
159
 
 
160
        :param transport: Transport to use
 
161
        :return: A BzrGitCache instance
 
162
        """
 
163
        try:
 
164
            format_name = transport.get_bytes('format')
 
165
            format = formats.get(format_name)
 
166
        except bzrlib.errors.NoSuchFile:
 
167
            format = formats.get('default')
 
168
            format.initialize(transport)
 
169
        return format.open(transport)
 
170
 
 
171
    @classmethod
 
172
    def from_repository(cls, repository):
 
173
        """Open a cache file for a repository.
 
174
 
 
175
        This will use the repository's transport to store the cache file, or
 
176
        use the users global cache directory if the repository has no 
 
177
        transport associated with it.
 
178
 
 
179
        :param repository: Repository to open the cache for
 
180
        :return: A `BzrGitCache`
 
181
        """
 
182
        repo_transport = getattr(repository, "_transport", None)
 
183
        if repo_transport is not None:
 
184
            # Even if we don't write to this repo, we should be able 
 
185
            # to update its cache.
 
186
            repo_transport = remove_readonly_transport_decorator(repo_transport)
 
187
            try:
 
188
                repo_transport.mkdir('git')
 
189
            except bzrlib.errors.FileExists:
 
190
                pass
 
191
            transport = repo_transport.clone('git')
 
192
        else:
 
193
            transport = get_remote_cache_transport()
 
194
        return cls.from_transport(transport)
 
195
 
 
196
 
 
197
class CacheUpdater(object):
 
198
 
 
199
    def add_object(self, obj, ie):
 
200
        raise NotImplementedError(self.add_object)
 
201
 
 
202
    def finish(self):
 
203
        raise NotImplementedError(self.finish)
 
204
 
 
205
 
 
206
class BzrGitCache(object):
 
207
    """Caching backend."""
 
208
 
 
209
    def __init__(self, idmap, content_cache, cache_updater_klass):
 
210
        self.idmap = idmap
 
211
        self.content_cache = content_cache
 
212
        self._cache_updater_klass = cache_updater_klass
 
213
 
 
214
    def get_updater(self, rev):
 
215
        return self._cache_updater_klass(self, rev)
 
216
 
 
217
 
 
218
DictBzrGitCache = lambda: BzrGitCache(DictGitShaMap(), None, DictCacheUpdater)
 
219
 
 
220
 
 
221
class DictCacheUpdater(CacheUpdater):
 
222
 
 
223
    def __init__(self, cache, rev):
 
224
        self.cache = cache
 
225
        self.revid = rev.revision_id
 
226
        self.parent_revids = rev.parent_ids
 
227
        self._commit = None
 
228
        self._entries = []
 
229
 
 
230
    def add_object(self, obj, ie):
 
231
        if obj.type_name == "commit":
 
232
            self._commit = obj
 
233
            assert ie is None
 
234
            type_data = (self.revid, self._commit.tree)
 
235
            self.cache.idmap._by_revid[self.revid] = obj.id
 
236
        elif obj.type_name in ("blob", "tree"):
 
237
            if ie is not None:
 
238
                if obj.type_name == "blob":
 
239
                    revision = ie.revision
 
240
                else:
 
241
                    revision = self.revid
 
242
                type_data = (ie.file_id, revision)
 
243
                self.cache.idmap._by_fileid.setdefault(type_data[1], {})[type_data[0]] =\
 
244
                    obj.id
 
245
        else:
 
246
            raise AssertionError
 
247
        self.cache.idmap._by_sha[obj.id] = (obj.type_name, type_data)
 
248
 
 
249
    def finish(self):
 
250
        if self._commit is None:
 
251
            raise AssertionError("No commit object added")
 
252
        return self._commit
 
253
 
 
254
 
152
255
class DictGitShaMap(GitShaMap):
153
256
 
154
257
    def __init__(self):
155
258
        self._by_sha = {}
156
259
        self._by_fileid = {}
157
 
 
158
 
    def _add_entry(self, sha, type, type_data):
159
 
        self._by_sha[sha] = (type, type_data)
160
 
        if type in ("blob", "tree"):
161
 
            self._by_fileid.setdefault(type_data[1], {})[type_data[0]] = sha
162
 
 
163
 
    def get_inventory_sha_map(self, revid):
164
 
 
165
 
        class DictInventorySHAMap(InventorySHAMap):
166
 
 
167
 
            def __init__(self, base, revid):
168
 
                self._base = base
169
 
                self.revid = revid
170
 
 
171
 
            def lookup_blob(self, fileid, revision):
172
 
                return self._base._by_fileid[revision][fileid]
173
 
 
174
 
            def lookup_tree(self, fileid):
175
 
                return self._base._by_fileid[self.revid][fileid]
176
 
 
177
 
        return DictInventorySHAMap(self, revid)
 
260
        self._by_revid = {}
 
261
 
 
262
    def lookup_blob_id(self, fileid, revision):
 
263
        return self._by_fileid[revision][fileid]
178
264
 
179
265
    def lookup_git_sha(self, sha):
180
266
        return self._by_sha[sha]
181
267
 
 
268
    def lookup_tree_id(self, fileid, revision):
 
269
        return self._by_fileid[revision][fileid]
 
270
 
 
271
    def lookup_commit(self, revid):
 
272
        return self._by_revid[revid]
 
273
 
182
274
    def revids(self):
183
275
        for key, (type, type_data) in self._by_sha.iteritems():
184
276
            if type == "commit":
188
280
        return self._by_sha.iterkeys()
189
281
 
190
282
 
 
283
class SqliteCacheUpdater(CacheUpdater):
 
284
 
 
285
    def __init__(self, cache, rev):
 
286
        self.cache = cache
 
287
        self.db = self.cache.idmap.db
 
288
        self.revid = rev.revision_id
 
289
        self._commit = None
 
290
        self._trees = []
 
291
        self._blobs = []
 
292
 
 
293
    def add_object(self, obj, ie):
 
294
        if obj.type_name == "commit":
 
295
            self._commit = obj
 
296
            assert ie is None
 
297
        elif obj.type_name == "tree":
 
298
            if ie is not None:
 
299
                self._trees.append((obj.id, ie.file_id, self.revid))
 
300
        elif obj.type_name == "blob":
 
301
            if ie is not None:
 
302
                self._blobs.append((obj.id, ie.file_id, ie.revision))
 
303
        else:
 
304
            raise AssertionError
 
305
 
 
306
    def finish(self):
 
307
        if self._commit is None:
 
308
            raise AssertionError("No commit object added")
 
309
        self.db.executemany(
 
310
            "replace into trees (sha1, fileid, revid) values (?, ?, ?)",
 
311
            self._trees)
 
312
        self.db.executemany(
 
313
            "replace into blobs (sha1, fileid, revid) values (?, ?, ?)",
 
314
            self._blobs)
 
315
        self.db.execute(
 
316
            "replace into commits (sha1, revid, tree_sha) values (?, ?, ?)",
 
317
            (self._commit.id, self.revid, self._commit.tree))
 
318
        return self._commit
 
319
 
 
320
 
 
321
SqliteBzrGitCache = lambda p: BzrGitCache(SqliteGitShaMap(p), None, SqliteCacheUpdater)
 
322
 
 
323
 
 
324
class SqliteGitCacheFormat(BzrGitCacheFormat):
 
325
 
 
326
    def get_format_string(self):
 
327
        return 'bzr-git sha map version 1 using sqlite\n'
 
328
 
 
329
    def open(self, transport):
 
330
        try:
 
331
            basepath = transport.local_abspath(".")
 
332
        except bzrlib.errors.NotLocalUrl:
 
333
            basepath = get_cache_dir()
 
334
        return SqliteBzrGitCache(os.path.join(basepath, "idmap.db"))
 
335
 
 
336
 
191
337
class SqliteGitShaMap(GitShaMap):
192
338
 
193
339
    def __init__(self, path=None):
226
372
    def __repr__(self):
227
373
        return "%s(%r)" % (self.__class__.__name__, self.path)
228
374
    
229
 
    @classmethod
230
 
    def from_repository(cls, repository):
231
 
        try:
232
 
            transport = getattr(repository, "_transport", None)
233
 
            if transport is not None:
234
 
                return cls(os.path.join(transport.local_abspath("."), "git.db"))
235
 
        except bzrlib.errors.NotLocalUrl:
236
 
            pass
237
 
        return cls(os.path.join(get_cache_dir(), "remote.db"))
238
 
 
239
375
    def lookup_commit(self, revid):
240
376
        row = self.db.execute("select sha1 from commits where revid = ?", (revid,)).fetchone()
241
377
        if row is not None:
245
381
    def commit_write_group(self):
246
382
        self.db.commit()
247
383
 
248
 
    def add_entries(self, revid, parent_revids, commit_sha, root_tree_sha,
249
 
                    entries):
250
 
        trees = []
251
 
        blobs = []
252
 
        for (fileid, kind, hexsha, revision) in entries:
253
 
            if kind is None:
254
 
                continue
255
 
            if kind == "tree":
256
 
                trees.append((hexsha, fileid, revid))
257
 
            elif kind == "blob":
258
 
                blobs.append((hexsha, fileid, revision))
259
 
            else:
260
 
                raise AssertionError
261
 
        if trees:
262
 
            self.db.executemany("replace into trees (sha1, fileid, revid) values (?, ?, ?)", trees)
263
 
        if blobs:
264
 
            self.db.executemany("replace into blobs (sha1, fileid, revid) values (?, ?, ?)", blobs)
265
 
        self._add_entry(commit_sha, "commit", (revid, root_tree_sha))
266
 
 
267
 
    def _add_entry(self, sha, type, type_data):
268
 
        """Add a new entry to the database.
269
 
        """
270
 
        assert isinstance(type_data, tuple)
271
 
        if sha is None:
272
 
            return
273
 
        assert isinstance(sha, str), "type was %r" % sha
274
 
        if type == "commit":
275
 
            self.db.execute("replace into commits (sha1, revid, tree_sha) values (?, ?, ?)", (sha, type_data[0], type_data[1]))
276
 
        elif type in ("blob", "tree"):
277
 
            self.db.execute("replace into %ss (sha1, fileid, revid) values (?, ?, ?)" % type, (sha, type_data[0], type_data[1]))
278
 
        else:
279
 
            raise AssertionError("Unknown type %s" % type)
280
 
 
281
 
    def get_inventory_sha_map(self, revid):
282
 
        class SqliteInventorySHAMap(InventorySHAMap):
283
 
 
284
 
            def __init__(self, db, revid):
285
 
                self.db = db
286
 
                self.revid = revid
287
 
 
288
 
            def lookup_blob(self, fileid, revision):
289
 
                row = self.db.execute("select sha1 from blobs where fileid = ? and revid = ?", (fileid, revision)).fetchone()
290
 
                if row is not None:
291
 
                    return row[0]
292
 
                raise KeyError(fileid)
293
 
 
294
 
            def lookup_tree(self, fileid):
295
 
                row = self.db.execute("select sha1 from trees where fileid = ? and revid = ?", (fileid, self.revid)).fetchone()
296
 
                if row is not None:
297
 
                    return row[0]
298
 
                raise KeyError(fileid)
299
 
 
300
 
        return SqliteInventorySHAMap(self.db, revid)
 
384
    def lookup_blob_id(self, fileid, revision):
 
385
        row = self.db.execute("select sha1 from blobs where fileid = ? and revid = ?", (fileid, revision)).fetchone()
 
386
        if row is not None:
 
387
            return row[0]
 
388
        raise KeyError(fileid)
 
389
 
 
390
    def lookup_tree_id(self, fileid, revision):
 
391
        row = self.db.execute("select sha1 from trees where fileid = ? and revid = ?", (fileid, revision)).fetchone()
 
392
        if row is not None:
 
393
            return row[0]
 
394
        raise KeyError(fileid)
301
395
 
302
396
    def lookup_git_sha(self, sha):
303
397
        """Lookup a Git sha in the database.
306
400
        :return: (type, type_data) with type_data:
307
401
            revision: revid, tree sha
308
402
        """
309
 
        def format(type, row):
310
 
            return (type, (row[0], row[1]))
311
403
        row = self.db.execute("select revid, tree_sha from commits where sha1 = ?", (sha,)).fetchone()
312
404
        if row is not None:
313
 
            return format("commit", row)
 
405
            return ("commit", row)
314
406
        row = self.db.execute("select fileid, revid from blobs where sha1 = ?", (sha,)).fetchone()
315
407
        if row is not None:
316
 
            return format("blob", row)
 
408
            return ("blob", row)
317
409
        row = self.db.execute("select fileid, revid from trees where sha1 = ?", (sha,)).fetchone()
318
410
        if row is not None:
319
 
            return format("tree", row)
 
411
            return ("tree", row)
320
412
        raise KeyError(sha)
321
413
 
322
414
    def revids(self):
326
418
    def sha1s(self):
327
419
        """List the SHA1s."""
328
420
        for table in ("blobs", "commits", "trees"):
329
 
            trace.note(table)
330
 
            for (row,) in self.db.execute("select sha1 from %s" % table):
331
 
                yield row
332
 
 
333
 
 
334
 
TDB_MAP_VERSION = 3
335
 
TDB_HASH_SIZE = 50000
 
421
            for (sha,) in self.db.execute("select sha1 from %s" % table):
 
422
                yield sha
 
423
 
 
424
 
 
425
class TdbCacheUpdater(CacheUpdater):
 
426
 
 
427
    def __init__(self, cache, rev):
 
428
        self.cache = cache
 
429
        self.db = cache.idmap.db
 
430
        self.revid = rev.revision_id
 
431
        self.parent_revids = rev.parent_ids
 
432
        self._commit = None
 
433
        self._entries = []
 
434
 
 
435
    def add_object(self, obj, ie):
 
436
        sha = obj.sha().digest()
 
437
        if obj.type_name == "commit":
 
438
            self.db["commit\0" + self.revid] = "\0".join((sha, obj.tree))
 
439
            type_data = (self.revid, obj.tree)
 
440
            self._commit = obj
 
441
            assert ie is None
 
442
        elif obj.type_name == "blob":
 
443
            if ie is None:
 
444
                return
 
445
            self.db["\0".join(("blob", ie.file_id, ie.revision))] = sha
 
446
            type_data = (ie.file_id, ie.revision)
 
447
        elif obj.type_name == "tree":
 
448
            if ie is None:
 
449
                return
 
450
            type_data = (ie.file_id, self.revid)
 
451
        else:
 
452
            raise AssertionError
 
453
        self.db["git\0" + sha] = "\0".join((obj.type_name, ) + type_data)
 
454
 
 
455
    def finish(self):
 
456
        if self._commit is None:
 
457
            raise AssertionError("No commit object added")
 
458
        return self._commit
 
459
 
 
460
 
 
461
TdbBzrGitCache = lambda p: BzrGitCache(TdbGitShaMap(p), None, TdbCacheUpdater)
 
462
 
 
463
class TdbGitCacheFormat(BzrGitCacheFormat):
 
464
 
 
465
    def get_format_string(self):
 
466
        return 'bzr-git sha map version 3 using tdb\n'
 
467
 
 
468
    def open(self, transport):
 
469
        try:
 
470
            basepath = transport.local_abspath(".")
 
471
        except bzrlib.errors.NotLocalUrl:
 
472
            basepath = get_cache_dir()
 
473
        try:
 
474
            return TdbBzrGitCache(os.path.join(basepath, "idmap.tdb"))
 
475
        except ImportError:
 
476
            raise ImportError(
 
477
                "Unable to open existing bzr-git cache because 'tdb' is not "
 
478
                "installed.")
336
479
 
337
480
 
338
481
class TdbGitShaMap(GitShaMap):
346
489
    "blob fileid revid" -> "<sha1>"
347
490
    """
348
491
 
 
492
    TDB_MAP_VERSION = 3
 
493
    TDB_HASH_SIZE = 50000
 
494
 
349
495
    def __init__(self, path=None):
350
496
        import tdb
351
497
        self.path = path
353
499
            self.db = {}
354
500
        else:
355
501
            if not mapdbs().has_key(path):
356
 
                mapdbs()[path] = tdb.Tdb(path, TDB_HASH_SIZE, tdb.DEFAULT,
 
502
                mapdbs()[path] = tdb.Tdb(path, self.TDB_HASH_SIZE, tdb.DEFAULT,
357
503
                                          os.O_RDWR|os.O_CREAT)
358
504
            self.db = mapdbs()[path]
359
505
        try:
360
506
            if int(self.db["version"]) not in (2, 3):
361
507
                trace.warning("SHA Map is incompatible (%s -> %d), rebuilding database.",
362
 
                              self.db["version"], TDB_MAP_VERSION)
 
508
                              self.db["version"], self.TDB_MAP_VERSION)
363
509
                self.db.clear()
364
510
        except KeyError:
365
511
            pass
366
 
        self.db["version"] = str(TDB_MAP_VERSION)
 
512
        self.db["version"] = str(self.TDB_MAP_VERSION)
367
513
 
368
514
    def start_write_group(self):
369
515
        """Start writing changes."""
380
526
    def __repr__(self):
381
527
        return "%s(%r)" % (self.__class__.__name__, self.path)
382
528
 
383
 
    @classmethod
384
 
    def from_repository(cls, repository):
385
 
        try:
386
 
            transport = getattr(repository, "_transport", None)
387
 
            if transport is not None:
388
 
                return cls(os.path.join(transport.local_abspath("."), "git.tdb"))
389
 
        except bzrlib.errors.NotLocalUrl:
390
 
            pass
391
 
        return cls(os.path.join(get_cache_dir(), "remote.tdb"))
392
 
 
393
529
    def lookup_commit(self, revid):
394
530
        return sha_to_hex(self.db["commit\0" + revid][:20])
395
531
 
396
 
    def _add_entry(self, hexsha, type, type_data):
397
 
        """Add a new entry to the database.
398
 
        """
399
 
        if hexsha is None:
400
 
            sha = ""
401
 
        else:
402
 
            sha = hex_to_sha(hexsha)
403
 
            self.db["git\0" + sha] = "\0".join((type, type_data[0], type_data[1]))
404
 
        if type == "commit":
405
 
            self.db["commit\0" + type_data[0]] = "\0".join((sha, type_data[1]))
406
 
        elif type == "blob":
407
 
            self.db["\0".join(("blob", type_data[0], type_data[1]))] = sha
408
 
 
409
 
    def get_inventory_sha_map(self, revid):
410
 
 
411
 
        class TdbInventorySHAMap(InventorySHAMap):
412
 
 
413
 
            def __init__(self, db, revid):
414
 
                self.db = db
415
 
                self.revid = revid
416
 
 
417
 
            def lookup_blob(self, fileid, revision):
418
 
                return sha_to_hex(self.db["\0".join(("blob", fileid, revision))])
 
532
    def lookup_blob_id(self, fileid, revision):
 
533
        return sha_to_hex(self.db["\0".join(("blob", fileid, revision))])
419
534
                
420
 
        return TdbInventorySHAMap(self.db, revid)
421
 
 
422
535
    def lookup_git_sha(self, sha):
423
536
        """Lookup a Git sha in the database.
424
537
 
451
564
                yield sha_to_hex(key[4:])
452
565
 
453
566
 
 
567
formats = registry.Registry()
 
568
formats.register(TdbGitCacheFormat().get_format_string(),
 
569
    TdbGitCacheFormat())
 
570
formats.register(SqliteGitCacheFormat().get_format_string(),
 
571
    SqliteGitCacheFormat())
 
572
try:
 
573
    import tdb
 
574
except ImportError:
 
575
    formats.register('default', SqliteGitCacheFormat())
 
576
else:
 
577
    formats.register('default', TdbGitCacheFormat())
 
578
 
 
579
 
 
580
def migrate_ancient_formats(repo_transport):
 
581
    # Prefer migrating git.db over git.tdb, since the latter may not 
 
582
    # be openable on some platforms.
 
583
    if repo_transport.has("git.db"):
 
584
        SqliteGitCacheFormat().initialize(repo_transport.clone("git"))
 
585
        repo_transport.rename("git.db", "git/idmap.db")
 
586
    elif repo_transport.has("git.tdb"):
 
587
        TdbGitCacheFormat().initialize(repo_transport.clone("git"))
 
588
        repo_transport.rename("git.tdb", "git/idmap.tdb")
 
589
 
 
590
 
 
591
def remove_readonly_transport_decorator(transport):
 
592
    if transport.is_readonly():
 
593
        return transport._decorated
 
594
    return transport
 
595
 
 
596
 
454
597
def from_repository(repository):
455
 
    try:
456
 
        return TdbGitShaMap.from_repository(repository)
457
 
    except ImportError:
458
 
        return SqliteGitShaMap.from_repository(repository)
 
598
    """Open a cache file for a repository.
 
599
 
 
600
    If the repository is remote and there is no transport available from it
 
601
    this will use a local file in the users cache directory
 
602
    (typically ~/.cache/bazaar/git/)
 
603
 
 
604
    :param repository: A repository object
 
605
    """
 
606
    repo_transport = getattr(repository, "_transport", None)
 
607
    if repo_transport is not None:
 
608
        # Migrate older cache formats
 
609
        repo_transport = remove_readonly_transport_decorator(repo_transport)
 
610
        try:
 
611
            repo_transport.mkdir("git")
 
612
        except bzrlib.errors.FileExists:
 
613
            pass
 
614
        else:
 
615
            migrate_ancient_formats(repo_transport)
 
616
    return BzrGitCacheFormat.from_repository(repository)