20
20
lines by NL. The field delimiters are ommitted in the grammar, line delimiters
21
21
are not - this is done for clarity of reading. All string data is in utf8.
23
MINIKIND = "f" | "d" | "l" | "a" | "r" | "t";
26
WHOLE_NUMBER = {digit}, digit;
28
REVISION_ID = a non-empty utf8 string;
30
dirstate format = header line, full checksum, row count, parent details,
31
ghost_details, entries;
32
header line = "#bazaar dirstate flat format 3", NL;
33
full checksum = "crc32: ", ["-"], WHOLE_NUMBER, NL;
34
row count = "num_entries: ", WHOLE_NUMBER, NL;
35
parent_details = WHOLE NUMBER, {REVISION_ID}* NL;
36
ghost_details = WHOLE NUMBER, {REVISION_ID}*, NL;
38
entry = entry_key, current_entry_details, {parent_entry_details};
39
entry_key = dirname, basename, fileid;
40
current_entry_details = common_entry_details, working_entry_details;
41
parent_entry_details = common_entry_details, history_entry_details;
42
common_entry_details = MINIKIND, fingerprint, size, executable
43
working_entry_details = packed_stat
44
history_entry_details = REVISION_ID;
47
fingerprint = a nonempty utf8 sequence with meaning defined by minikind.
49
Given this definition, the following is useful to know:
50
entry (aka row) - all the data for a given key.
51
entry[0]: The key (dirname, basename, fileid)
55
entry[1]: The tree(s) data for this path and id combination.
56
entry[1][0]: The current tree
57
entry[1][1]: The second tree
59
For an entry for a tree, we have (using tree 0 - current tree) to demonstrate:
60
entry[1][0][0]: minikind
61
entry[1][0][1]: fingerprint
63
entry[1][0][3]: executable
64
entry[1][0][4]: packed_stat
66
entry[1][1][4]: revision_id
25
MINIKIND = "f" | "d" | "l" | "a" | "r" | "t";
28
WHOLE_NUMBER = {digit}, digit;
30
REVISION_ID = a non-empty utf8 string;
32
dirstate format = header line, full checksum, row count, parent details,
33
ghost_details, entries;
34
header line = "#bazaar dirstate flat format 3", NL;
35
full checksum = "crc32: ", ["-"], WHOLE_NUMBER, NL;
36
row count = "num_entries: ", WHOLE_NUMBER, NL;
37
parent_details = WHOLE NUMBER, {REVISION_ID}* NL;
38
ghost_details = WHOLE NUMBER, {REVISION_ID}*, NL;
40
entry = entry_key, current_entry_details, {parent_entry_details};
41
entry_key = dirname, basename, fileid;
42
current_entry_details = common_entry_details, working_entry_details;
43
parent_entry_details = common_entry_details, history_entry_details;
44
common_entry_details = MINIKIND, fingerprint, size, executable
45
working_entry_details = packed_stat
46
history_entry_details = REVISION_ID;
49
fingerprint = a nonempty utf8 sequence with meaning defined by minikind.
51
Given this definition, the following is useful to know::
53
entry (aka row) - all the data for a given key.
54
entry[0]: The key (dirname, basename, fileid)
58
entry[1]: The tree(s) data for this path and id combination.
59
entry[1][0]: The current tree
60
entry[1][1]: The second tree
62
For an entry for a tree, we have (using tree 0 - current tree) to demonstrate::
64
entry[1][0][0]: minikind
65
entry[1][0][1]: fingerprint
67
entry[1][0][3]: executable
68
entry[1][0][4]: packed_stat
72
entry[1][1][4]: revision_id
68
74
There may be multiple rows at the root, one per id present in the root, so the
69
in memory root row is now:
70
self._dirblocks[0] -> ('', [entry ...]),
71
and the entries in there are
74
entries[0][2]: file_id
75
entries[1][0]: The tree data for the current tree for this fileid at /
79
'r' is a relocated entry: This path is not present in this tree with this id,
80
but the id can be found at another location. The fingerprint is used to
81
point to the target location.
82
'a' is an absent entry: In that tree the id is not present at this path.
83
'd' is a directory entry: This path in this tree is a directory with the
84
current file id. There is no fingerprint for directories.
85
'f' is a file entry: As for directory, but it's a file. The fingerprint is the
86
sha1 value of the file's canonical form, i.e. after any read filters have
87
been applied to the convenience form stored in the working tree.
88
'l' is a symlink entry: As for directory, but a symlink. The fingerprint is the
90
't' is a reference to a nested subtree; the fingerprint is the referenced
75
in memory root row is now::
77
self._dirblocks[0] -> ('', [entry ...]),
79
and the entries in there are::
83
entries[0][2]: file_id
84
entries[1][0]: The tree data for the current tree for this fileid at /
89
'r' is a relocated entry: This path is not present in this tree with this
90
id, but the id can be found at another location. The fingerprint is
91
used to point to the target location.
92
'a' is an absent entry: In that tree the id is not present at this path.
93
'd' is a directory entry: This path in this tree is a directory with the
94
current file id. There is no fingerprint for directories.
95
'f' is a file entry: As for directory, but it's a file. The fingerprint is
96
the sha1 value of the file's canonical form, i.e. after any read
97
filters have been applied to the convenience form stored in the working
99
'l' is a symlink entry: As for directory, but a symlink. The fingerprint is
101
't' is a reference to a nested subtree; the fingerprint is the referenced
95
The entries on disk and in memory are ordered according to the following keys:
106
The entries on disk and in memory are ordered according to the following keys::
97
108
directory, as a list of components
101
112
--- Format 1 had the following different definition: ---
102
rows = dirname, NULL, basename, NULL, MINIKIND, NULL, fileid_utf8, NULL,
103
WHOLE NUMBER (* size *), NULL, packed stat, NULL, sha1|symlink target,
105
PARENT ROW = NULL, revision_utf8, NULL, MINIKIND, NULL, dirname, NULL,
106
basename, NULL, WHOLE NUMBER (* size *), NULL, "y" | "n", NULL,
116
rows = dirname, NULL, basename, NULL, MINIKIND, NULL, fileid_utf8, NULL,
117
WHOLE NUMBER (* size *), NULL, packed stat, NULL, sha1|symlink target,
119
PARENT ROW = NULL, revision_utf8, NULL, MINIKIND, NULL, dirname, NULL,
120
basename, NULL, WHOLE NUMBER (* size *), NULL, "y" | "n", NULL,
109
123
PARENT ROW's are emitted for every parent that is not in the ghosts details
110
124
line. That is, if the parents are foo, bar, baz, and the ghosts are bar, then
411
445
self._last_block_index = None
412
446
self._last_entry_index = None
447
# The set of known hash changes
448
self._known_hash_changes = set()
449
# How many hash changed entries can we have without saving
450
self._worth_saving_limit = worth_saving_limit
414
452
def __repr__(self):
415
453
return "%s(%r)" % \
416
454
(self.__class__.__name__, self._filename)
456
def _mark_modified(self, hash_changed_entries=None, header_modified=False):
457
"""Mark this dirstate as modified.
459
:param hash_changed_entries: if non-None, mark just these entries as
460
having their hash modified.
461
:param header_modified: mark the header modified as well, not just the
464
#trace.mutter_callsite(3, "modified hash entries: %s", hash_changed_entries)
465
if hash_changed_entries:
466
self._known_hash_changes.update([e[0] for e in hash_changed_entries])
467
if self._dirblock_state in (DirState.NOT_IN_MEMORY,
468
DirState.IN_MEMORY_UNMODIFIED):
469
# If the dirstate is already marked a IN_MEMORY_MODIFIED, then
470
# that takes precedence.
471
self._dirblock_state = DirState.IN_MEMORY_HASH_MODIFIED
473
# TODO: Since we now have a IN_MEMORY_HASH_MODIFIED state, we
474
# should fail noisily if someone tries to set
475
# IN_MEMORY_MODIFIED but we don't have a write-lock!
476
# We don't know exactly what changed so disable smart saving
477
self._dirblock_state = DirState.IN_MEMORY_MODIFIED
479
self._header_state = DirState.IN_MEMORY_MODIFIED
481
def _mark_unmodified(self):
482
"""Mark this dirstate as unmodified."""
483
self._header_state = DirState.IN_MEMORY_UNMODIFIED
484
self._dirblock_state = DirState.IN_MEMORY_UNMODIFIED
485
self._known_hash_changes = set()
418
487
def add(self, path, file_id, kind, stat, fingerprint):
419
488
"""Add a path to be tracked.
1669
1747
raise errors.InconsistentDelta(old_path, file_id,
1670
1748
'mismatched file_id in tree 1')
1671
1749
if real_delete:
1672
if entry[1][0][0] != 'a':
1673
self._changes_aborted = True
1674
raise errors.InconsistentDelta(old_path, file_id,
1675
'This was marked as a real delete, but the WT state'
1676
' claims that it still exists and is versioned.')
1677
del self._dirblocks[block_index][1][entry_index]
1750
if entry[1][0][0] == 'a':
1751
# The file was marked as deleted in the active
1752
# state, and it is now deleted in the basis state,
1753
# so just remove the record entirely
1754
del self._dirblocks[block_index][1][entry_index]
1756
# The basis entry needs to be marked deleted
1758
# If we are deleting a directory, we need to make sure
1759
# that all of its children are already deleted
1760
block_i, entry_i, d_present, f_present = \
1761
self._get_block_entry_index(old_path, '', 0)
1763
# The dir block is still present in the dirstate; this could
1764
# be due to it being in a parent tree, or a corrupt delta.
1765
for child_entry in self._dirblocks[block_i][1]:
1766
if child_entry[1][1][0] not in ('r', 'a'):
1767
self._changes_aborted = True
1768
raise errors.InconsistentDelta(old_path, entry[0][2],
1769
"The file id was deleted but its children were "
1679
1772
if entry[1][0][0] == 'a':
1680
1773
self._changes_aborted = True
2145
2243
def _get_id_index(self):
2146
"""Get an id index of self._dirblocks."""
2244
"""Get an id index of self._dirblocks.
2246
This maps from file_id => [(directory, name, file_id)] entries where
2247
that file_id appears in one of the trees.
2147
2249
if self._id_index is None:
2149
2251
for key, tree_details in self._iter_entries():
2150
id_index.setdefault(key[2], set()).add(key)
2252
self._add_to_id_index(id_index, key)
2151
2253
self._id_index = id_index
2152
2254
return self._id_index
2256
def _add_to_id_index(self, id_index, entry_key):
2257
"""Add this entry to the _id_index mapping."""
2258
# This code used to use a set for every entry in the id_index. However,
2259
# it is *rare* to have more than one entry. So a set is a large
2260
# overkill. And even when we do, we won't ever have more than the
2261
# number of parent trees. Which is still a small number (rarely >2). As
2262
# such, we use a simple tuple, and do our own uniqueness checks. While
2263
# the 'in' check is O(N) since N is nicely bounded it shouldn't ever
2264
# cause quadratic failure.
2265
# TODO: This should use StaticTuple
2266
file_id = entry_key[2]
2267
entry_key = static_tuple.StaticTuple.from_sequence(entry_key)
2268
if file_id not in id_index:
2269
id_index[file_id] = static_tuple.StaticTuple(entry_key,)
2271
entry_keys = id_index[file_id]
2272
if entry_key not in entry_keys:
2273
id_index[file_id] = entry_keys + (entry_key,)
2275
def _remove_from_id_index(self, id_index, entry_key):
2276
"""Remove this entry from the _id_index mapping.
2278
It is an programming error to call this when the entry_key is not
2281
file_id = entry_key[2]
2282
entry_keys = list(id_index[file_id])
2283
entry_keys.remove(entry_key)
2284
id_index[file_id] = static_tuple.StaticTuple.from_sequence(entry_keys)
2154
2286
def _get_output_lines(self, lines):
2155
2287
"""Format lines for final output.
2465
2642
new_details = []
2466
2643
for lookup_index in xrange(tree_index):
2467
2644
# boundary case: this is the first occurence of file_id
2468
# so there are no id_indexs, possibly take this out of
2645
# so there are no id_indexes, possibly take this out of
2470
if not len(id_index[file_id]):
2647
if not len(entry_keys):
2471
2648
new_details.append(DirState.NULL_PARENT_DETAILS)
2473
2650
# grab any one entry, use it to find the right path.
2474
# TODO: optimise this to reduce memory use in highly
2475
# fragmented situations by reusing the relocation
2477
a_key = iter(id_index[file_id]).next()
2651
a_key = iter(entry_keys).next()
2478
2652
if by_path[a_key][lookup_index][0] in ('r', 'a'):
2479
# its a pointer or missing statement, use it as is.
2653
# its a pointer or missing statement, use it as
2480
2655
new_details.append(by_path[a_key][lookup_index])
2482
2657
# we have the right key, make a pointer to it.
2483
2658
real_path = ('/'.join(a_key[0:2])).strip('/')
2484
new_details.append(('r', real_path, 0, False, ''))
2659
new_details.append(st('r', real_path, 0, False,
2485
2661
new_details.append(self._inv_entry_to_details(entry))
2486
2662
new_details.extend(new_location_suffix)
2487
2663
by_path[new_entry_key] = new_details
2488
id_index[file_id].add(new_entry_key)
2664
self._add_to_id_index(id_index, new_entry_key)
2489
2665
# --- end generation of full tree mappings
2491
2667
# sort and output all the entries