158
167
def get_revision_count(self):
159
168
return self.revision_index.key_count()
170
def index_name(self, index_type, name):
171
"""Get the disk name of an index type for pack name 'name'."""
172
return name + Pack.index_definitions[index_type][0]
174
def index_offset(self, index_type):
175
"""Get the position in a index_size array for a given index type."""
176
return Pack.index_definitions[index_type][1]
161
178
def inventory_index_name(self, name):
162
179
"""The inv index is the name + .iix."""
163
180
return self.index_name('inventory', name)
174
191
"""The text index is the name + .tix."""
175
192
return self.index_name('text', name)
194
def _replace_index_with_readonly(self, index_type):
195
setattr(self, index_type + '_index',
196
self.index_class(self.index_transport,
197
self.index_name(index_type, self.name),
198
self.index_sizes[self.index_offset(index_type)]))
178
201
class ExistingPack(Pack):
179
202
"""An in memory proxy for an existing .pack and its disk indices."""
200
223
return not self.__eq__(other)
202
225
def __repr__(self):
203
return "<bzrlib.repofmt.pack_repo.Pack object at 0x%x, %s, %s" % (
204
id(self), self.pack_transport, self.name)
226
return "<%s.%s object at 0x%x, %s, %s" % (
227
self.__class__.__module__, self.__class__.__name__, id(self),
228
self.pack_transport, self.name)
231
class ResumedPack(ExistingPack):
233
def __init__(self, name, revision_index, inventory_index, text_index,
234
signature_index, upload_transport, pack_transport, index_transport,
236
"""Create a ResumedPack object."""
237
ExistingPack.__init__(self, pack_transport, name, revision_index,
238
inventory_index, text_index, signature_index)
239
self.upload_transport = upload_transport
240
self.index_transport = index_transport
241
self.index_sizes = [None, None, None, None]
243
('revision', revision_index),
244
('inventory', inventory_index),
245
('text', text_index),
246
('signature', signature_index),
248
for index_type, index in indices:
249
offset = self.index_offset(index_type)
250
self.index_sizes[offset] = index._size
251
self.index_class = pack_collection._index_class
252
self._state = 'resumed'
253
# XXX: perhaps check that the .pack file exists?
254
# XXX: should sanity check name: what if a client uses a 'name' of
255
# "../../../something/private"? could steal suspended wg from
258
def access_tuple(self):
259
if self._state == 'finished':
260
return Pack.access_tuple(self)
261
elif self._state == 'resumed':
262
return self.upload_transport, self.file_name()
264
raise AssertionError(self._state)
267
self.upload_transport.delete(self.file_name())
268
indices = [self.revision_index, self.inventory_index, self.text_index,
269
self.signature_index]
270
for index in indices:
271
index._transport.delete(index._name)
274
#XXX self._check_references()
275
new_name = '../packs/' + self.file_name()
276
self.upload_transport.rename(self.file_name(), new_name)
277
for index_type in ['revision', 'inventory', 'text', 'signature']:
278
old_name = self.index_name(index_type, self.name)
279
new_name = '../indices/' + old_name
280
self.upload_transport.rename(old_name, new_name)
281
self._replace_index_with_readonly(index_type)
282
self._state = 'finished'
207
285
class NewPack(Pack):
208
286
"""An in memory proxy for a pack which is being created."""
210
# A map of index 'type' to the file extension and position in the
212
index_definitions = {
213
'revision': ('.rix', 0),
214
'inventory': ('.iix', 1),
216
'signature': ('.six', 3),
219
288
def __init__(self, pack_collection, upload_suffix='', file_mode=None):
220
289
"""Create a NewPack instance.
376
445
# visible is smaller. On the other hand none will be seen until
377
446
# they're in the names list.
378
447
self.index_sizes = [None, None, None, None]
379
self._write_index('revision', self.revision_index, 'revision')
380
self._write_index('inventory', self.inventory_index, 'inventory')
381
self._write_index('text', self.text_index, 'file texts')
448
self._write_index('revision', self.revision_index, 'revision', suspend)
449
self._write_index('inventory', self.inventory_index, 'inventory',
451
self._write_index('text', self.text_index, 'file texts', suspend)
382
452
self._write_index('signature', self.signature_index,
383
'revision signatures')
453
'revision signatures', suspend)
384
454
self.write_stream.close()
385
455
# Note that this will clobber an existing pack with the same name,
386
456
# without checking for hash collisions. While this is undesirable this
393
463
# - try for HASH.pack
394
464
# - try for temporary-name
395
465
# - refresh the pack-list to see if the pack is now absent
396
self.upload_transport.rename(self.random_name,
397
'../packs/' + self.name + '.pack')
466
new_name = self.name + '.pack'
468
new_name = '../packs/' + new_name
469
self.upload_transport.rename(self.random_name, new_name)
398
470
self._state = 'finished'
399
471
if 'pack' in debug.debug_flags:
400
472
# XXX: size might be interesting?
401
mutter('%s: create_pack: pack renamed into place: %s%s->%s%s t+%6.3fs',
473
mutter('%s: create_pack: pack finished: %s%s->%s t+%6.3fs',
402
474
time.ctime(), self.upload_transport.base, self.random_name,
403
self.pack_transport, self.name,
404
time.time() - self.start_time)
475
new_name, time.time() - self.start_time)
407
478
"""Flush any current data."""
411
482
self._hash.update(bytes)
412
483
self._buffer[:] = [[], 0]
414
def index_name(self, index_type, name):
415
"""Get the disk name of an index type for pack name 'name'."""
416
return name + NewPack.index_definitions[index_type][0]
418
def index_offset(self, index_type):
419
"""Get the position in a index_size array for a given index type."""
420
return NewPack.index_definitions[index_type][1]
422
def _replace_index_with_readonly(self, index_type):
423
setattr(self, index_type + '_index',
424
self.index_class(self.index_transport,
425
self.index_name(index_type, self.name),
426
self.index_sizes[self.index_offset(index_type)]))
428
485
def set_write_cache_size(self, size):
429
486
self._cache_limit = size
431
def _write_index(self, index_type, index, label):
488
def _write_index(self, index_type, index, label, suspend=False):
432
489
"""Write out an index.
434
491
:param index_type: The type of index to write - e.g. 'revision'.
436
493
:param label: What label to give the index e.g. 'revision'.
438
495
index_name = self.index_name(index_type, self.name)
439
self.index_sizes[self.index_offset(index_type)] = \
440
self.index_transport.put_file(index_name, index.finish(),
441
mode=self._file_mode)
497
transport = self.upload_transport
499
transport = self.index_transport
500
self.index_sizes[self.index_offset(index_type)] = transport.put_file(
501
index_name, index.finish(), mode=self._file_mode)
442
502
if 'pack' in debug.debug_flags:
443
503
# XXX: size might be interesting?
444
504
mutter('%s: create_pack: wrote %s index: %s%s t+%6.3fs',
1493
1556
self.add_pack_to_memory(result)
1559
def _resume_pack(self, name):
1560
"""Get a suspended Pack object by name.
1562
:param name: The name of the pack - e.g. '123456'
1563
:return: A Pack object.
1566
rev_index = self._make_index(name, '.rix', resume=True)
1567
inv_index = self._make_index(name, '.iix', resume=True)
1568
txt_index = self._make_index(name, '.tix', resume=True)
1569
sig_index = self._make_index(name, '.six', resume=True)
1570
result = ResumedPack(name, rev_index, inv_index, txt_index,
1571
sig_index, self._upload_transport, self._pack_transport,
1572
self._index_transport, self)
1573
except errors.NoSuchFile, e:
1574
raise errors.UnresumableWriteGroups(self.repo, [name], str(e))
1575
self.add_pack_to_memory(result)
1576
self._resumed_packs.append(result)
1496
1579
def allocate(self, a_new_pack):
1497
1580
"""Allocate name in the list of packs.
1516
1599
return self._index_class(self.transport, 'pack-names', None
1517
1600
).iter_all_entries()
1519
def _make_index(self, name, suffix):
1602
def _make_index(self, name, suffix, resume=False):
1520
1603
size_offset = self._suffix_offsets[suffix]
1521
1604
index_name = name + suffix
1522
index_size = self._names[name][size_offset]
1523
return self._index_class(
1524
self._index_transport, index_name, index_size)
1606
transport = self._upload_transport
1607
index_size = transport.stat(index_name).st_size
1609
transport = self._index_transport
1610
index_size = self._names[name][size_offset]
1611
return self._index_class(transport, index_name, index_size)
1526
1613
def _max_pack_count(self, total_revisions):
1527
1614
"""Return the maximum number of packs to use for total revisions.
1802
1890
# case. -- mbp 20081113
1803
1891
self._remove_pack_indices(self._new_pack)
1804
1892
self._new_pack = None
1893
for resumed_pack in self._resumed_packs:
1895
resumed_pack.abort()
1897
# See comment in previous finally block.
1898
self._remove_pack_indices(resumed_pack)
1899
del self._resumed_packs[:]
1805
1900
self.repo._text_knit = None
1902
def _remove_resumed_pack_indices(self):
1903
for resumed_pack in self._resumed_packs:
1904
self._remove_pack_indices(resumed_pack)
1905
del self._resumed_packs[:]
1807
1907
def _commit_write_group(self):
1808
1908
self._remove_pack_indices(self._new_pack)
1909
should_autopack = False
1809
1910
if self._new_pack.data_inserted():
1810
1911
# get all the data to disk and read to use
1811
1912
self._new_pack.finish()
1812
1913
self.allocate(self._new_pack)
1813
1914
self._new_pack = None
1915
should_autopack = True
1917
self._new_pack.abort()
1918
self._new_pack = None
1919
for resumed_pack in self._resumed_packs:
1920
# XXX: this is a pretty ugly way to turn the resumed pack into a
1921
# properly committed pack.
1922
self._names[resumed_pack.name] = None
1923
self._remove_pack_from_memory(resumed_pack)
1924
resumed_pack.finish()
1925
self.allocate(resumed_pack)
1926
should_autopack = True
1927
del self._resumed_packs[:]
1814
1929
if not self.autopack():
1815
1930
# when autopack takes no steps, the names list is still
1817
1932
self._save_pack_names()
1933
self.repo._text_knit = None
1935
def _suspend_write_group(self):
1936
tokens = [pack.name for pack in self._resumed_packs]
1937
self._remove_pack_indices(self._new_pack)
1938
if self._new_pack.data_inserted():
1939
# get all the data to disk and read to use
1940
self._new_pack.finish(suspend=True)
1941
tokens.append(self._new_pack.name)
1942
self._new_pack = None
1819
1944
self._new_pack.abort()
1820
1945
self._new_pack = None
1946
self._remove_resumed_pack_indices()
1821
1947
self.repo._text_knit = None
1950
def _resume_write_group(self, tokens):
1951
for token in tokens:
1952
self._resume_pack(token)
1824
1955
class KnitPackRepository(KnitRepository):