2440
2439
new_iterator = new_inv.iter_entries_by_dir()
2441
2440
# we will be modifying the dirstate, so we need a stable iterator. In
2442
2441
# future we might write one, for now we just clone the state into a
2443
# list using a deep copy so that forward changes don't make the logic
2444
# more complex. Using a shallow copy results in all entries being seen
2445
# but the state of the entries being wrong, and that leads to stale
2446
# entries being left behind.
2442
# list using a copy so that we see every original item and don't have
2443
# to adjust the position when items are inserted or deleted in the
2444
# underlying dirstate.
2447
2445
old_iterator = iter(list(self._iter_entries()))
2448
2446
# both must have roots so this is safe:
2449
2447
current_new = new_iterator.next()
2494
2492
# new is finished
2496
2494
trace.mutter("Truncating from old '%s/%s'.",
2497
current_old[0][0].decode('utf'),
2495
current_old[0][0].decode('utf8'),
2498
2496
current_old[0][1].decode('utf8'))
2499
2497
self._make_absent(current_old)
2500
2498
current_old = advance(old_iterator)
2534
2532
# without seeing it in the new list. so it must be gone.
2536
2534
trace.mutter("Deleting from old '%s/%s'.",
2537
current_old[0][0].decode('utf'),
2535
current_old[0][0].decode('utf8'),
2538
2536
current_old[0][1].decode('utf8'))
2539
2537
self._make_absent(current_old)
2540
2538
current_old = advance(old_iterator)
2638
2636
# grab one of them and use it to generate parent
2639
2637
# relocation/absent entries.
2640
2638
new_entry = key, [new_details]
2641
for other_key in existing_keys:
2639
# existing_keys can be changed as we iterate.
2640
for other_key in tuple(existing_keys):
2642
2641
# change the record at other to be a pointer to this new
2643
2642
# record. The loop looks similar to the change to
2644
2643
# relocations when updating an existing record but its not:
2649
2648
if not present:
2650
2649
raise AssertionError('could not find block for %s' % (
2652
other_entry_index, present = self._find_entry_index(other_key,
2653
self._dirblocks[other_block_index][1])
2651
other_block = self._dirblocks[other_block_index][1]
2652
other_entry_index, present = self._find_entry_index(
2653
other_key, other_block)
2654
2654
if not present:
2655
raise AssertionError('could not find entry for %s' % (
2655
raise AssertionError(
2656
'update_minimal: could not find other entry for %s'
2657
2658
if path_utf8 is None:
2658
2659
raise AssertionError('no path')
2659
other_block = self._dirblocks[other_block_index][1]
2660
2660
# Turn this other location into a reference to the new
2661
# location. This also updates the aliased iterator that
2662
# set_state_from_inventory uses so that the old entry, if
2663
# not already examined, is skipped over.
2664
other_block[other_entry_index][1][0] = \
2665
('r', path_utf8, 0, False, '')
2666
if len(other_block[other_entry_index][1]) == 1:
2667
# We only have one tree in use, so an 'r' record is not
2668
# needed: remove it.
2669
other_block.pop(other_entry_index)
2661
# location. This also updates the aliased iterator
2662
# (current_old in set_state_from_inventory) so that the old
2663
# entry, if not already examined, is skipped over by that
2665
other_entry = other_block[other_entry_index]
2666
other_entry[1][0] = ('r', path_utf8, 0, False, '')
2667
self._maybe_remove_row(other_block, other_entry_index,
2671
# adds a tuple to the new details for each column
2672
# - either by copying an existing relocation pointer inside that column
2673
# - or by creating a new pointer to the right row inside that column
2671
2674
num_present_parents = self._num_present_parents()
2675
if num_present_parents:
2676
other_key = list(existing_keys)[0]
2672
2677
for lookup_index in xrange(1, num_present_parents + 1):
2673
2678
# grab any one entry, use it to find the right path.
2674
2679
# TODO: optimise this to reduce memory use in highly
2681
2686
update_entry_index, present = \
2682
2687
self._find_entry_index(other_key, self._dirblocks[update_block_index][1])
2683
2688
if not present:
2684
raise AssertionError('could not find entry for %s' % (other_key,))
2689
raise AssertionError('update_minimal: could not find entry for %s' % (other_key,))
2685
2690
update_details = self._dirblocks[update_block_index][1][update_entry_index][1][lookup_index]
2686
2691
if update_details[0] in 'ar': # relocated, absent
2687
2692
# its a pointer or absent in lookup_index's tree, use
2734
2739
self._dirblock_state = DirState.IN_MEMORY_MODIFIED
2741
def _maybe_remove_row(self, block, index, id_index):
2742
"""Remove index if it is absent or relocated across the row.
2744
id_index is updated accordingly.
2746
present_in_row = False
2747
entry = block[index]
2748
for column in entry[1]:
2749
if column[0] not in 'ar':
2750
present_in_row = True
2752
if not present_in_row:
2754
id_index[entry[0][2]].remove(entry[0])
2736
2756
def _validate(self):
2737
2757
"""Check that invariants on the dirblock are correct.