596
605
# Either we will find them here, or we can mark them as
599
if middle_files[0] == first_dir_name:
608
if middle_files[0] == first_path:
600
609
# We might need to go before this location
601
pre.append(first_dir_name)
602
if middle_files[-1] == last_dir_name:
603
post.insert(0, last_dir_name)
610
pre.append(first_path)
611
if middle_files[-1] == last_path:
612
post.insert(0, last_path)
605
614
# Find out what paths we have
606
paths = {first_dir_name:[first_fields]}
607
# last_dir_name might == first_dir_name so we need to be
615
paths = {first_path:[first_fields]}
616
# last_path might == first_path so we need to be
608
617
# careful if we should append rather than overwrite
609
618
if last_entry_num != first_entry_num:
610
paths.setdefault(last_dir_name, []).append(last_fields)
619
paths.setdefault(last_path, []).append(last_fields)
611
620
for num in xrange(first_entry_num+1, last_entry_num):
612
621
# TODO: jam 20070223 We are already splitting here, so
613
622
# shouldn't we just split the whole thing rather
614
623
# than doing the split again in add_one_record?
615
624
fields = entries[num].split('\0')
616
dir_name = (fields[1], fields[2])
617
paths.setdefault(dir_name, []).append(fields)
626
path = fields[1] + '/' + fields[2]
629
paths.setdefault(path, []).append(fields)
619
for dir_name in middle_files:
620
for fields in paths.get(dir_name, []):
631
for path in middle_files:
632
for fields in paths.get(path, []):
621
633
# offset by 1 because of the opening '\0'
622
634
# consider changing fields_to_entry to avoid the
623
635
# extra list slice
624
636
entry = fields_to_entry(fields[1:])
625
found.setdefault(dir_name, []).append(entry)
637
found.setdefault(path, []).append(entry)
627
639
# Now we have split up everything into pre, middle, and post, and
628
640
# we have handled everything that fell in 'middle'.
1166
def update_basis_by_delta(self, delta, new_revid):
1167
"""Update the parents of this tree after a commit.
1169
This gives the tree one parent, with revision id new_revid. The
1170
inventory delta is applied to the current basis tree to generate the
1171
inventory for the parent new_revid, and all other parent trees are
1174
Note that an exception during the operation of this method will leave
1175
the dirstate in a corrupt state where it should not be saved.
1177
Finally, we expect all changes to be synchronising the basis tree with
1180
:param new_revid: The new revision id for the trees parent.
1181
:param delta: An inventory delta (see apply_inventory_delta) describing
1182
the changes from the current left most parent revision to new_revid.
1184
self._read_dirblocks_if_needed()
1185
self._discard_merge_parents()
1186
if self._ghosts != []:
1187
raise NotImplementedError(self.update_basis_by_delta)
1188
if len(self._parents) == 0:
1189
# setup a blank tree, the most simple way.
1190
empty_parent = DirState.NULL_PARENT_DETAILS
1191
for entry in self._iter_entries():
1192
entry[1].append(empty_parent)
1193
self._parents.append(new_revid)
1195
self._parents[0] = new_revid
1197
delta = sorted(delta, reverse=True)
1201
# The paths this function accepts are unicode and must be encoded as we
1203
encode = cache_utf8.encode
1204
inv_to_entry = self._inv_entry_to_details
1205
# delta is now (deletes, changes), (adds) in reverse lexographical
1207
# deletes in reverse lexographic order are safe to process in situ.
1208
# renames are not, as a rename from any path could go to a path
1209
# lexographically lower, so we transform renames into delete, add pairs,
1210
# expanding them recursively as needed.
1211
# At the same time, to reduce interface friction we convert the input
1212
# inventory entries to dirstate.
1213
root_only = ('', '')
1214
for old_path, new_path, file_id, inv_entry in delta:
1215
if old_path is None:
1216
adds.append((None, encode(new_path), file_id,
1217
inv_to_entry(inv_entry), True))
1218
elif new_path is None:
1219
deletes.append((encode(old_path), None, file_id, None, True))
1220
elif (old_path, new_path) != root_only:
1222
# Because renames must preserve their children we must have
1223
# processed all relocations and removes before hand. The sort
1224
# order ensures we've examined the child paths, but we also
1225
# have to execute the removals, or the split to an add/delete
1226
# pair will result in the deleted item being reinserted, or
1227
# renamed items being reinserted twice - and possibly at the
1228
# wrong place. Splitting into a delete/add pair also simplifies
1229
# the handling of entries with ('f', ...), ('r' ...) because
1230
# the target of the 'r' is old_path here, and we add that to
1231
# deletes, meaning that the add handler does not need to check
1232
# for 'r' items on every pass.
1233
self._update_basis_apply_deletes(deletes)
1235
new_path_utf8 = encode(new_path)
1236
# Split into an add/delete pair recursively.
1237
adds.append((None, new_path_utf8, file_id,
1238
inv_to_entry(inv_entry), False))
1239
# Expunge deletes that we've seen so that deleted/renamed
1240
# children of a rename directory are handled correctly.
1241
new_deletes = reversed(list(self._iter_child_entries(1,
1243
# Remove the current contents of the tree at orig_path, and
1244
# reinsert at the correct new path.
1245
for entry in new_deletes:
1247
source_path = entry[0][0] + '/' + entry[0][1]
1249
source_path = entry[0][1]
1250
target_path = new_path_utf8 + source_path[len(old_path):]
1251
adds.append((None, target_path, entry[0][2], entry[1][1], False))
1253
(source_path, target_path, entry[0][2], None, False))
1255
(encode(old_path), new_path, file_id, None, False))
1257
# changes to just the root should not require remove/insertion
1259
changes.append((encode(old_path), encode(new_path), file_id,
1260
inv_to_entry(inv_entry)))
1262
# Finish expunging deletes/first half of renames.
1263
self._update_basis_apply_deletes(deletes)
1264
# Reinstate second half of renames and new paths.
1265
self._update_basis_apply_adds(adds)
1266
# Apply in-situ changes.
1267
self._update_basis_apply_changes(changes)
1269
self._dirblock_state = DirState.IN_MEMORY_MODIFIED
1270
self._header_state = DirState.IN_MEMORY_MODIFIED
1271
self._id_index = None
1274
def _update_basis_apply_adds(self, adds):
1275
"""Apply a sequence of adds to tree 1 during update_basis_by_delta.
1277
They may be adds, or renames that have been split into add/delete
1280
:param adds: A sequence of adds. Each add is a tuple:
1281
(None, new_path_utf8, file_id, (entry_details), real_add). real_add
1282
is False when the add is the second half of a remove-and-reinsert
1283
pair created to handle renames and deletes.
1285
# Adds are accumulated partly from renames, so can be in any input
1288
# adds is now in lexographic order, which places all parents before
1289
# their children, so we can process it linearly.
1291
for old_path, new_path, file_id, new_details, real_add in adds:
1292
assert old_path is None
1293
# the entry for this file_id must be in tree 0.
1294
entry = self._get_entry(0, file_id, new_path)
1295
if entry[0][2] != file_id:
1296
raise errors.BzrError('dirstate: cannot apply delta, working'
1297
' tree does not contain new entry %r %r' %
1298
(new_path, file_id))
1299
if real_add and entry[1][1][0] not in absent:
1300
raise errors.BzrError('dirstate: inconsistent delta, with '
1301
'tree 0. %r %r' % (new_path, file_id))
1302
# We don't need to update the target of an 'r' because the handling
1303
# of renames turns all 'r' situations into a delete at the original
1305
entry[1][1] = new_details
1307
def _update_basis_apply_changes(self, changes):
1308
"""Apply a sequence of changes to tree 1 during update_basis_by_delta.
1310
:param adds: A sequence of changes. Each change is a tuple:
1311
(path_utf8, path_utf8, file_id, (entry_details))
1314
for old_path, new_path, file_id, new_details in changes:
1315
assert old_path == new_path
1316
# the entry for this file_id must be in tree 0.
1317
entry = self._get_entry(0, file_id, new_path)
1318
if entry[0][2] != file_id:
1319
raise errors.BzrError('dirstate: cannot apply delta, working'
1320
' tree does not contain new entry %r %r' %
1321
(new_path, file_id))
1322
if (entry[1][0][0] in absent or
1323
entry[1][1][0] in absent):
1324
raise errors.BzrError('dirstate: inconsistent delta, with '
1325
'tree 0. %r %r' % (new_path, file_id))
1326
entry[1][1] = new_details
1328
def _update_basis_apply_deletes(self, deletes):
1329
"""Apply a sequence of deletes to tree 1 during update_basis_by_delta.
1331
They may be deletes, or renames that have been split into add/delete
1334
:param deletes: A sequence of deletes. Each delete is a tuple:
1335
(old_path_utf8, new_path_utf8, file_id, None, real_delete).
1336
real_delete is True when the desired outcome is an actual deletion
1337
rather than the rename handling logic temporarily deleting a path
1338
during the replacement of a parent.
1340
null = DirState.NULL_PARENT_DETAILS
1341
for old_path, new_path, file_id, _, real_delete in deletes:
1343
assert new_path is None
1345
assert new_path is not None
1346
# the entry for this file_id must be in tree 1.
1347
dirname, basename = osutils.split(old_path)
1348
block_index, entry_index, dir_present, file_present = \
1349
self._get_block_entry_index(dirname, basename, 1)
1350
if not file_present:
1351
raise errors.BzrError('dirstate: cannot apply delta, basis'
1352
' tree does not contain new entry %r %r' %
1353
(old_path, file_id))
1354
entry = self._dirblocks[block_index][1][entry_index]
1355
if entry[0][2] != file_id:
1356
raise errors.BzrError('mismatched file_id in tree 1 %r %r' %
1357
(old_path, file_id))
1359
if entry[1][0][0] != 'a':
1360
raise errors.BzrError('dirstate: inconsistent delta, with '
1361
'tree 0. %r %r' % (old_path, file_id))
1362
del self._dirblocks[block_index][1][entry_index]
1364
if entry[1][0][0] == 'a':
1365
raise errors.BzrError('dirstate: inconsistent delta, with '
1366
'tree 0. %r %r' % (old_path, file_id))
1367
elif entry[1][0][0] == 'r':
1368
# implement the rename
1369
del self._dirblocks[block_index][1][entry_index]
1371
# it is being resurrected here, so blank it out temporarily.
1372
self._dirblocks[block_index][1][entry_index][1][1] = null
1091
1374
def update_entry(self, entry, abspath, stat_value,
1092
1375
_stat_to_minikind=_stat_to_minikind,
1093
1376
_pack_stat=pack_stat):
1551
1875
self._read_header_if_needed()
1552
1876
if self._dirblock_state == DirState.NOT_IN_MEMORY:
1553
# move the _state_file pointer to after the header (in case bisect
1554
# has been called in the mean time)
1555
self._state_file.seek(self._end_of_header)
1556
text = self._state_file.read()
1557
# TODO: check the crc checksums. crc_measured = zlib.crc32(text)
1559
fields = text.split('\0')
1560
# Remove the last blank entry
1561
trailing = fields.pop()
1562
assert trailing == ''
1563
# consider turning fields into a tuple.
1565
# skip the first field which is the trailing null from the header.
1567
# Each line now has an extra '\n' field which is not used
1568
# so we just skip over it
1570
# 3 fields for the key
1571
# + number of fields per tree_data (5) * tree count
1573
num_present_parents = self._num_present_parents()
1574
tree_count = 1 + num_present_parents
1575
entry_size = self._fields_per_entry()
1576
expected_field_count = entry_size * self._num_entries
1577
field_count = len(fields)
1578
# this checks our adjustment, and also catches file too short.
1579
assert field_count - cur == expected_field_count, \
1580
'field count incorrect %s != %s, entry_size=%s, '\
1581
'num_entries=%s fields=%r' % (
1582
field_count - cur, expected_field_count, entry_size,
1583
self._num_entries, fields)
1585
if num_present_parents == 1:
1586
# Bind external functions to local names
1588
# We access all fields in order, so we can just iterate over
1589
# them. Grab an straight iterator over the fields. (We use an
1590
# iterator because we don't want to do a lot of additions, nor
1591
# do we want to do a lot of slicing)
1592
next = iter(fields).next
1593
# Move the iterator to the current position
1594
for x in xrange(cur):
1596
# The two blocks here are deliberate: the root block and the
1597
# contents-of-root block.
1598
self._dirblocks = [('', []), ('', [])]
1599
current_block = self._dirblocks[0][1]
1600
current_dirname = ''
1601
append_entry = current_block.append
1602
for count in xrange(self._num_entries):
1606
if dirname != current_dirname:
1607
# new block - different dirname
1609
current_dirname = dirname
1610
self._dirblocks.append((current_dirname, current_block))
1611
append_entry = current_block.append
1612
# we know current_dirname == dirname, so re-use it to avoid
1613
# creating new strings
1614
entry = ((current_dirname, name, file_id),
1617
next(), # fingerprint
1618
_int(next()), # size
1619
next() == 'y', # executable
1620
next(), # packed_stat or revision_id
1624
next(), # fingerprint
1625
_int(next()), # size
1626
next() == 'y', # executable
1627
next(), # packed_stat or revision_id
1631
assert trailing == '\n'
1632
# append the entry to the current block
1634
self._split_root_dirblock_into_contents()
1636
fields_to_entry = self._get_fields_to_entry()
1637
entries = [fields_to_entry(fields[pos:pos+entry_size])
1638
for pos in xrange(cur, field_count, entry_size)]
1639
self._entries_to_current_state(entries)
1640
# To convert from format 2 => format 3
1641
# self._dirblocks = sorted(self._dirblocks,
1642
# key=lambda blk:blk[0].split('/'))
1643
# To convert from format 3 => format 2
1644
# self._dirblocks = sorted(self._dirblocks)
1645
self._dirblock_state = DirState.IN_MEMORY_UNMODIFIED
1877
_read_dirblocks(self)
1647
1879
def _read_header(self):
1648
1880
"""This reads in the metadata header, and the parent ids.
2367
2648
self._split_path_cache = {}
2369
2650
def _requires_lock(self):
2370
"""Checks that a lock is currently held by someone on the dirstate"""
2651
"""Check that a lock is currently held by someone on the dirstate."""
2371
2652
if not self._lock_token:
2372
2653
raise errors.ObjectNotLocked(self)
2375
def bisect_dirblock(dirblocks, dirname, lo=0, hi=None, cache={}):
2376
"""Return the index where to insert dirname into the dirblocks.
2378
The return value idx is such that all directories blocks in dirblock[:idx]
2379
have names < dirname, and all blocks in dirblock[idx:] have names >=
2382
Optional args lo (default 0) and hi (default len(dirblocks)) bound the
2383
slice of a to be searched.
2388
dirname_split = cache[dirname]
2390
dirname_split = dirname.split('/')
2391
cache[dirname] = dirname_split
2394
# Grab the dirname for the current dirblock
2395
cur = dirblocks[mid][0]
2397
cur_split = cache[cur]
2399
cur_split = cur.split('/')
2400
cache[cur] = cur_split
2401
if cur_split < dirname_split: lo = mid+1
2656
# Try to load the compiled form if possible
2658
from bzrlib._dirstate_helpers_c import (
2659
_read_dirblocks_c as _read_dirblocks,
2660
bisect_dirblock_c as bisect_dirblock,
2661
_bisect_path_left_c as _bisect_path_left,
2662
_bisect_path_right_c as _bisect_path_right,
2663
cmp_by_dirs_c as cmp_by_dirs,
2666
from bzrlib._dirstate_helpers_py import (
2667
_read_dirblocks_py as _read_dirblocks,
2668
bisect_dirblock_py as bisect_dirblock,
2669
_bisect_path_left_py as _bisect_path_left,
2670
_bisect_path_right_py as _bisect_path_right,
2671
cmp_by_dirs_py as cmp_by_dirs,