375
376
HEADER_FORMAT_2 = '#bazaar dirstate flat format 2\n'
376
377
HEADER_FORMAT_3 = '#bazaar dirstate flat format 3\n'
378
def __init__(self, path, sha1_provider):
379
def __init__(self, path, sha1_provider, worth_saving_limit=0):
379
380
"""Create a DirState object.
381
382
:param path: The path at which the dirstate file on disk should live.
382
383
:param sha1_provider: an object meeting the SHA1Provider interface.
384
:param worth_saving_limit: when the exact number of hash changed
385
entries is known, only bother saving the dirstate if more than
386
this count of entries have changed.
387
-1 means never save hash changes, 0 means always save hash changes.
384
389
# _header_state and _dirblock_state represent the current state
385
390
# of the dirstate metadata and the per-row data respectiely.
423
428
self._last_block_index = None
424
429
self._last_entry_index = None
430
# The set of known hash changes
431
self._known_hash_changes = set()
432
# How many hash changed entries can we have without saving
433
self._worth_saving_limit = worth_saving_limit
426
435
def __repr__(self):
427
436
return "%s(%r)" % \
428
437
(self.__class__.__name__, self._filename)
439
def _mark_modified(self, hash_changed_entries=None, header_modified=False):
440
"""Mark this dirstate as modified.
442
:param hash_changed_entries: if non-None, mark just these entries as
443
having their hash modified.
444
:param header_modified: mark the header modified as well, not just the
447
#trace.mutter_callsite(3, "modified hash entries: %s", hash_changed_entries)
448
if hash_changed_entries:
449
self._known_hash_changes.update([e[0] for e in hash_changed_entries])
450
if self._dirblock_state in (DirState.NOT_IN_MEMORY,
451
DirState.IN_MEMORY_UNMODIFIED):
452
# If the dirstate is already marked a IN_MEMORY_MODIFIED, then
453
# that takes precedence.
454
self._dirblock_state = DirState.IN_MEMORY_HASH_MODIFIED
456
# TODO: Since we now have a IN_MEMORY_HASH_MODIFIED state, we
457
# should fail noisily if someone tries to set
458
# IN_MEMORY_MODIFIED but we don't have a write-lock!
459
# We don't know exactly what changed so disable smart saving
460
self._dirblock_state = DirState.IN_MEMORY_MODIFIED
462
self._header_state = DirState.IN_MEMORY_MODIFIED
464
def _mark_unmodified(self):
465
"""Mark this dirstate as unmodified."""
466
self._header_state = DirState.IN_MEMORY_UNMODIFIED
467
self._dirblock_state = DirState.IN_MEMORY_UNMODIFIED
468
self._known_hash_changes = set()
430
470
def add(self, path, file_id, kind, stat, fingerprint):
431
471
"""Add a path to be tracked.
558
598
if kind == 'directory':
559
599
# insert a new dirblock
560
600
self._ensure_block(block_index, entry_index, utf8path)
561
self._dirblock_state = DirState.IN_MEMORY_MODIFIED
601
self._mark_modified()
562
602
if self._id_index:
563
603
self._add_to_id_index(self._id_index, entry_key)
1031
1071
self._ghosts = []
1032
1072
self._parents = [parents[0]]
1033
self._dirblock_state = DirState.IN_MEMORY_MODIFIED
1034
self._header_state = DirState.IN_MEMORY_MODIFIED
1073
self._mark_modified(header_modified=True)
1036
1075
def _empty_parent_info(self):
1037
1076
return [DirState.NULL_PARENT_DETAILS] * (len(self._parents) -
1567
1606
# the active tree.
1568
1607
raise errors.InconsistentDeltaDelta(delta, "error from _get_entry.")
1570
self._dirblock_state = DirState.IN_MEMORY_MODIFIED
1571
self._header_state = DirState.IN_MEMORY_MODIFIED
1609
self._mark_modified(header_modified=True)
1572
1610
self._id_index = None
1747
1785
and stat_value.st_ctime < self._cutoff_time):
1748
1786
entry[1][0] = ('f', sha1, stat_value.st_size, entry[1][0][3],
1750
self._dirblock_state = DirState.IN_MEMORY_MODIFIED
1788
self._mark_modified([entry])
1752
1790
def _sha_cutoff_time(self):
1753
1791
"""Return cutoff time.
1811
1849
"""Serialise the entire dirstate to a sequence of lines."""
1812
1850
if (self._header_state == DirState.IN_MEMORY_UNMODIFIED and
1813
1851
self._dirblock_state == DirState.IN_MEMORY_UNMODIFIED):
1814
# read whats on disk.
1852
# read what's on disk.
1815
1853
self._state_file.seek(0)
1816
1854
return self._state_file.readlines()
1818
1856
lines.append(self._get_parents_line(self.get_parent_ids()))
1819
1857
lines.append(self._get_ghosts_line(self._ghosts))
1820
# append the root line which is special cased
1821
lines.extend(map(self._entry_to_line, self._iter_entries()))
1858
lines.extend(self._get_entry_lines())
1822
1859
return self._get_output_lines(lines)
1824
1861
def _get_ghosts_line(self, ghost_ids):
1829
1866
"""Create a line for the state file for parents information."""
1830
1867
return '\0'.join([str(len(parent_ids))] + parent_ids)
1869
def _get_entry_lines(self):
1870
"""Create lines for entries."""
1871
return map(self._entry_to_line, self._iter_entries())
1832
1873
def _get_fields_to_entry(self):
1833
1874
"""Get a function which converts entry fields into a entry record.
2222
2263
"""The number of parent entries in each record row."""
2223
2264
return len(self._parents) - len(self._ghosts)
2226
def on_file(path, sha1_provider=None):
2267
def on_file(cls, path, sha1_provider=None, worth_saving_limit=0):
2227
2268
"""Construct a DirState on the file at path "path".
2229
2270
:param path: The path at which the dirstate file on disk should live.
2230
2271
:param sha1_provider: an object meeting the SHA1Provider interface.
2231
2272
If None, a DefaultSHA1Provider is used.
2273
:param worth_saving_limit: when the exact number of hash changed
2274
entries is known, only bother saving the dirstate if more than
2275
this count of entries have changed. -1 means never save.
2232
2276
:return: An unlocked DirState object, associated with the given path.
2234
2278
if sha1_provider is None:
2235
2279
sha1_provider = DefaultSHA1Provider()
2236
result = DirState(path, sha1_provider)
2280
result = cls(path, sha1_provider,
2281
worth_saving_limit=worth_saving_limit)
2239
2284
def _read_dirblocks_if_needed(self):
2331
2376
trace.mutter('Not saving DirState because '
2332
2377
'_changes_aborted is set.')
2334
if (self._header_state == DirState.IN_MEMORY_MODIFIED or
2335
self._dirblock_state == DirState.IN_MEMORY_MODIFIED):
2379
# TODO: Since we now distinguish IN_MEMORY_MODIFIED from
2380
# IN_MEMORY_HASH_MODIFIED, we should only fail quietly if we fail
2381
# to save an IN_MEMORY_HASH_MODIFIED, and fail *noisily* if we
2382
# fail to save IN_MEMORY_MODIFIED
2383
if self._worth_saving():
2337
2384
grabbed_write_lock = False
2338
2385
if self._lock_state != 'w':
2339
2386
grabbed_write_lock, new_lock = self._lock_token.temporary_write_lock()
2347
2394
# We couldn't grab a write lock, so we switch back to a read one
2397
lines = self.get_lines()
2350
2398
self._state_file.seek(0)
2351
self._state_file.writelines(self.get_lines())
2399
self._state_file.writelines(lines)
2352
2400
self._state_file.truncate()
2353
2401
self._state_file.flush()
2354
self._header_state = DirState.IN_MEMORY_UNMODIFIED
2355
self._dirblock_state = DirState.IN_MEMORY_UNMODIFIED
2402
self._mark_unmodified()
2357
2404
if grabbed_write_lock:
2358
2405
self._lock_token = self._lock_token.restore_read_lock()
2361
2408
# not changed contents. Since restore_read_lock may
2362
2409
# not be an atomic operation.
2411
def _worth_saving(self):
2412
"""Is it worth saving the dirstate or not?"""
2413
if (self._header_state == DirState.IN_MEMORY_MODIFIED
2414
or self._dirblock_state == DirState.IN_MEMORY_MODIFIED):
2416
if self._dirblock_state == DirState.IN_MEMORY_HASH_MODIFIED:
2417
if self._worth_saving_limit == -1:
2418
# We never save hash changes when the limit is -1
2420
# If we're using smart saving and only a small number of
2421
# entries have changed their hash, don't bother saving. John has
2422
# suggested using a heuristic here based on the size of the
2423
# changed files and/or tree. For now, we go with a configurable
2424
# number of changes, keeping the calculation time
2425
# as low overhead as possible. (This also keeps all existing
2426
# tests passing as the default is 0, i.e. always save.)
2427
if len(self._known_hash_changes) >= self._worth_saving_limit:
2364
2431
def _set_data(self, parent_ids, dirblocks):
2365
2432
"""Set the full dirstate data in memory.
2375
2442
# our memory copy is now authoritative.
2376
2443
self._dirblocks = dirblocks
2377
self._header_state = DirState.IN_MEMORY_MODIFIED
2378
self._dirblock_state = DirState.IN_MEMORY_MODIFIED
2444
self._mark_modified(header_modified=True)
2379
2445
self._parents = list(parent_ids)
2380
2446
self._id_index = None
2381
2447
self._packed_stat_index = None
2401
2467
self._make_absent(entry)
2402
2468
self.update_minimal(('', '', new_id), 'd',
2403
2469
path_utf8='', packed_stat=entry[1][0][4])
2404
self._dirblock_state = DirState.IN_MEMORY_MODIFIED
2470
self._mark_modified()
2471
# XXX: This was added by Ian, we need to make sure there
2472
# are tests for it, because it isn't in bzr.dev TRUNK
2473
# It looks like the only place it is called is in setting the root
2474
# id of the tree. So probably we never had an _id_index when we
2475
# don't even have a root yet.
2476
if self._id_index is not None:
2477
self._add_to_id_index(self._id_index, entry[0])
2406
2479
def set_parent_trees(self, trees, ghosts):
2407
2480
"""Set the parent trees for the dirstate.
2542
2615
self._entries_to_current_state(new_entries)
2543
2616
self._parents = [rev_id for rev_id, tree in trees]
2544
2617
self._ghosts = list(ghosts)
2545
self._header_state = DirState.IN_MEMORY_MODIFIED
2546
self._dirblock_state = DirState.IN_MEMORY_MODIFIED
2618
self._mark_modified(header_modified=True)
2547
2619
self._id_index = id_index
2549
2621
def _sort_entries(self, entry_list):
2686
2758
current_old[0][1].decode('utf8'))
2687
2759
self._make_absent(current_old)
2688
2760
current_old = advance(old_iterator)
2689
self._dirblock_state = DirState.IN_MEMORY_MODIFIED
2761
self._mark_modified()
2690
2762
self._id_index = None
2691
2763
self._packed_stat_index = None
2758
2830
if update_tree_details[0][0] == 'a': # absent
2759
2831
raise AssertionError('bad row %r' % (update_tree_details,))
2760
2832
update_tree_details[0] = DirState.NULL_PARENT_DETAILS
2761
self._dirblock_state = DirState.IN_MEMORY_MODIFIED
2833
self._mark_modified()
2762
2834
return last_reference
2764
2836
def update_minimal(self, key, minikind, executable=False, fingerprint='',
2933
3005
if not present:
2934
3006
self._dirblocks.insert(block_index, (subdir_key[0], []))
2936
self._dirblock_state = DirState.IN_MEMORY_MODIFIED
3008
self._mark_modified()
2938
3010
def _maybe_remove_row(self, block, index, id_index):
2939
3011
"""Remove index if it is absent or relocated across the row.
3253
3327
entry[1][0] = ('l', '', stat_value.st_size,
3254
3328
False, DirState.NULLSTAT)
3255
3329
if worth_saving:
3256
state._dirblock_state = DirState.IN_MEMORY_MODIFIED
3330
state._mark_modified([entry])
3257
3331
return link_or_sha1