~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/knit.py

Merge from bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
279
279
    stored and retrieved.
280
280
    """
281
281
 
282
 
    def __init__(self, relpath, transport, file_mode=None, access_mode=None, 
 
282
    def __init__(self, relpath, transport, file_mode=None, access_mode=None,
283
283
                 factory=None, basis_knit=DEPRECATED_PARAMETER, delta=True,
284
 
                 create=False):
 
284
                 create=False, create_parent_dir=False, delay_create=False,
 
285
                 dir_mode=None):
285
286
        """Construct a knit at location specified by relpath.
286
287
        
287
288
        :param create: If not True, only open an existing knit.
 
289
        :param create_parent_dir: If True, create the parent directory if 
 
290
            creating the file fails. (This is used for stores with 
 
291
            hash-prefixes that may not exist yet)
 
292
        :param delay_create: The calling code is aware that the knit won't 
 
293
            actually be created until the first data is stored.
288
294
        """
289
295
        if deprecated_passed(basis_knit):
290
296
            warnings.warn("KnitVersionedFile.__(): The basis_knit parameter is"
301
307
        self.delta = delta
302
308
 
303
309
        self._index = _KnitIndex(transport, relpath + INDEX_SUFFIX,
304
 
            access_mode, create=create, file_mode=file_mode)
 
310
            access_mode, create=create, file_mode=file_mode,
 
311
            create_parent_dir=create_parent_dir, delay_create=delay_create,
 
312
            dir_mode=dir_mode)
305
313
        self._data = _KnitData(transport, relpath + DATA_SUFFIX,
306
 
            access_mode, create=create and not len(self), file_mode=file_mode)
 
314
            access_mode, create=create and not len(self), file_mode=file_mode,
 
315
            create_parent_dir=create_parent_dir, delay_create=delay_create,
 
316
            dir_mode=dir_mode)
307
317
 
308
318
    def __repr__(self):
309
319
        return '%s(%s)' % (self.__class__.__name__, 
403
413
        """See VersionedFile.copy_to()."""
404
414
        # copy the current index to a temp index to avoid racing with local
405
415
        # writes
406
 
        transport.put(name + INDEX_SUFFIX + '.tmp', self.transport.get(self._index._filename),)
 
416
        transport.put_file_non_atomic(name + INDEX_SUFFIX + '.tmp',
 
417
                self.transport.get(self._index._filename))
407
418
        # copy the data file
408
419
        f = self._data._open_file()
409
420
        try:
410
 
            transport.put(name + DATA_SUFFIX, f)
 
421
            transport.put_file(name + DATA_SUFFIX, f)
411
422
        finally:
412
423
            f.close()
413
424
        # move the copied index into place
414
425
        transport.move(name + INDEX_SUFFIX + '.tmp', name + INDEX_SUFFIX)
415
426
 
416
427
    def create_empty(self, name, transport, mode=None):
417
 
        return KnitVersionedFile(name, transport, factory=self.factory, delta=self.delta, create=True)
 
428
        return KnitVersionedFile(name, transport, factory=self.factory,
 
429
                                 delta=self.delta, create=True)
418
430
    
419
431
    def _fix_parents(self, version, new_parents):
420
432
        """Fix the parents list for version.
946
958
class _KnitComponentFile(object):
947
959
    """One of the files used to implement a knit database"""
948
960
 
949
 
    def __init__(self, transport, filename, mode, file_mode=None):
 
961
    def __init__(self, transport, filename, mode, file_mode=None,
 
962
                 create_parent_dir=False, dir_mode=None):
950
963
        self._transport = transport
951
964
        self._filename = filename
952
965
        self._mode = mode
953
 
        self._file_mode=file_mode
954
 
 
955
 
    def write_header(self):
956
 
        if self._transport.append(self._filename, StringIO(self.HEADER),
957
 
            mode=self._file_mode):
958
 
            raise KnitCorrupt(self._filename, 'misaligned after writing header')
 
966
        self._file_mode = file_mode
 
967
        self._dir_mode = dir_mode
 
968
        self._create_parent_dir = create_parent_dir
 
969
        self._need_to_create = False
959
970
 
960
971
    def check_header(self, fp):
961
972
        line = fp.readline()
1048
1059
                                   parents,
1049
1060
                                   index)
1050
1061
 
1051
 
    def __init__(self, transport, filename, mode, create=False, file_mode=None):
1052
 
        _KnitComponentFile.__init__(self, transport, filename, mode, file_mode)
 
1062
    def __init__(self, transport, filename, mode, create=False, file_mode=None,
 
1063
                 create_parent_dir=False, delay_create=False, dir_mode=None):
 
1064
        _KnitComponentFile.__init__(self, transport, filename, mode,
 
1065
                                    file_mode=file_mode,
 
1066
                                    create_parent_dir=create_parent_dir,
 
1067
                                    dir_mode=dir_mode)
1053
1068
        self._cache = {}
1054
1069
        # position in _history is the 'official' index for a revision
1055
1070
        # but the values may have come from a newer entry.
1124
1139
            except NoSuchFile, e:
1125
1140
                if mode != 'w' or not create:
1126
1141
                    raise
1127
 
                self.write_header()
 
1142
                if delay_create:
 
1143
                    self._need_to_create = True
 
1144
                else:
 
1145
                    self._transport.put_bytes_non_atomic(self._filename,
 
1146
                        self.HEADER, mode=self._file_mode)
 
1147
 
1128
1148
        finally:
1129
1149
            pb.update('read knit index', total, total)
1130
1150
            pb.finished()
1245
1265
            assert isinstance(line, str), \
1246
1266
                'content must be utf-8 encoded: %r' % (line,)
1247
1267
            lines.append(line)
1248
 
        self._transport.append(self._filename, StringIO(''.join(lines)))
 
1268
        if not self._need_to_create:
 
1269
            self._transport.append_bytes(self._filename, ''.join(lines))
 
1270
        else:
 
1271
            sio = StringIO()
 
1272
            sio.write(self.HEADER)
 
1273
            sio.writelines(lines)
 
1274
            sio.seek(0)
 
1275
            self._transport.put_file_non_atomic(self._filename, sio,
 
1276
                                create_parent_dir=self._create_parent_dir,
 
1277
                                mode=self._file_mode,
 
1278
                                dir_mode=self._dir_mode)
 
1279
            self._need_to_create = False
 
1280
 
1249
1281
        # cache after writing, so that a failed write leads to missing cache
1250
1282
        # entries not extra ones. XXX TODO: RBC 20060502 in the event of a 
1251
1283
        # failure, reload the index or flush it or some such, to prevent
1255
1287
        
1256
1288
    def has_version(self, version_id):
1257
1289
        """True if the version is in the index."""
1258
 
        return self._cache.has_key(version_id)
 
1290
        return (version_id in self._cache)
1259
1291
 
1260
1292
    def get_position(self, version_id):
1261
1293
        """Return data position and size of specified version."""
1296
1328
class _KnitData(_KnitComponentFile):
1297
1329
    """Contents of the knit data file"""
1298
1330
 
1299
 
    HEADER = "# bzr knit data 8\n"
1300
 
 
1301
 
    def __init__(self, transport, filename, mode, create=False, file_mode=None):
1302
 
        _KnitComponentFile.__init__(self, transport, filename, mode)
 
1331
    def __init__(self, transport, filename, mode, create=False, file_mode=None,
 
1332
                 create_parent_dir=False, delay_create=False,
 
1333
                 dir_mode=None):
 
1334
        _KnitComponentFile.__init__(self, transport, filename, mode,
 
1335
                                    file_mode=file_mode,
 
1336
                                    create_parent_dir=create_parent_dir,
 
1337
                                    dir_mode=dir_mode)
1303
1338
        self._checked = False
1304
1339
        # TODO: jam 20060713 conceptually, this could spill to disk
1305
1340
        #       if the cached size gets larger than a certain amount
1308
1343
        self._cache = {}
1309
1344
        self._do_cache = False
1310
1345
        if create:
1311
 
            self._transport.put(self._filename, StringIO(''), mode=file_mode)
 
1346
            if delay_create:
 
1347
                self._need_to_create = create
 
1348
            else:
 
1349
                self._transport.put_bytes_non_atomic(self._filename, '',
 
1350
                                                     mode=self._file_mode)
1312
1351
 
1313
1352
    def enable_cache(self):
1314
1353
        """Enable caching of reads."""
1353
1392
        :return: the offset in the data file raw_data was written.
1354
1393
        """
1355
1394
        assert isinstance(raw_data, str), 'data must be plain bytes'
1356
 
        return self._transport.append(self._filename, StringIO(raw_data))
 
1395
        if not self._need_to_create:
 
1396
            return self._transport.append_bytes(self._filename, raw_data)
 
1397
        else:
 
1398
            self._transport.put_bytes_non_atomic(self._filename, raw_data,
 
1399
                                   create_parent_dir=self._create_parent_dir,
 
1400
                                   mode=self._file_mode,
 
1401
                                   dir_mode=self._dir_mode)
 
1402
            self._need_to_create = False
 
1403
            return 0
1357
1404
        
1358
1405
    def add_record(self, version_id, digest, lines):
1359
1406
        """Write new text record to disk.  Returns the position in the
1360
1407
        file where it was written."""
1361
1408
        size, sio = self._record_to_data(version_id, digest, lines)
1362
1409
        # write to disk
1363
 
        start_pos = self._transport.append(self._filename, sio)
 
1410
        if not self._need_to_create:
 
1411
            start_pos = self._transport.append_file(self._filename, sio)
 
1412
        else:
 
1413
            self._transport.put_file_non_atomic(self._filename, sio,
 
1414
                               create_parent_dir=self._create_parent_dir,
 
1415
                               mode=self._file_mode,
 
1416
                               dir_mode=self._dir_mode)
 
1417
            self._need_to_create = False
 
1418
            start_pos = 0
1364
1419
        if self._do_cache:
1365
1420
            self._cache[version_id] = sio.getvalue()
1366
1421
        return start_pos, size