35
34
ExistingLimbo, ImmortalLimbo, NoFinalPath,
36
35
UnableCreateSymlink)
37
36
from bzrlib.inventory import InventoryEntry
38
from bzrlib.osutils import (
37
from bzrlib.osutils import (file_kind, supports_executable, pathjoin, lexists,
38
delete_any, has_symlinks)
48
39
from bzrlib.progress import DummyProgress, ProgressPhase
49
40
from bzrlib.symbol_versioning import (
50
41
deprecated_function,
53
43
from bzrlib.trace import mutter, warning
54
44
from bzrlib import tree
278
258
a transaction has been unversioned, it is deliberately still returned.
279
259
(this will likely lead to an unversioned parent conflict.)
282
raise ValueError('None is not a valid file id')
283
261
if file_id in self._r_new_id and self._r_new_id[file_id] is not None:
284
262
return self._r_new_id[file_id]
263
elif file_id in self._tree.inventory:
264
return self.trans_id_tree_file_id(file_id)
265
elif file_id in self._non_present_ids:
266
return self._non_present_ids[file_id]
287
self._tree.iter_entries_by_dir([file_id]).next()
288
except StopIteration:
289
if file_id in self._non_present_ids:
290
return self._non_present_ids[file_id]
292
trans_id = self._assign_id()
293
self._non_present_ids[file_id] = trans_id
296
return self.trans_id_tree_file_id(file_id)
268
trans_id = self._assign_id()
269
self._non_present_ids[file_id] = trans_id
298
272
def canonical_path(self, path):
299
273
"""Get the canonical tree-relative path"""
480
450
del self._new_id[trans_id]
481
451
del self._r_new_id[file_id]
483
def new_paths(self, filesystem_only=False):
484
"""Determine the paths of all new and changed files.
486
:param filesystem_only: if True, only calculate values for files
487
that require renames or execute bit changes.
491
stale_ids = self._needs_rename.difference(self._new_name)
492
stale_ids.difference_update(self._new_parent)
493
stale_ids.difference_update(self._new_contents)
494
stale_ids.difference_update(self._new_id)
495
needs_rename = self._needs_rename.difference(stale_ids)
496
id_sets = (needs_rename, self._new_executability)
498
id_sets = (self._new_name, self._new_parent, self._new_contents,
499
self._new_id, self._new_executability)
500
for id_set in id_sets:
501
new_ids.update(id_set)
502
return sorted(FinalPaths(self).get_paths(new_ids))
504
def _inventory_altered(self):
505
"""Get the trans_ids and paths of files needing new inv entries."""
507
for id_set in [self._new_name, self._new_parent, self._new_id,
508
self._new_executability]:
509
new_ids.update(id_set)
510
changed_kind = set(self._removed_contents)
511
changed_kind.intersection_update(self._new_contents)
512
changed_kind.difference_update(new_ids)
513
changed_kind = (t for t in changed_kind if self.tree_kind(t) !=
515
new_ids.update(changed_kind)
516
return sorted(FinalPaths(self).get_paths(new_ids))
454
"""Determine the paths of all new and changed files"""
456
fp = FinalPaths(self)
457
for id_set in (self._new_name, self._new_parent, self._new_contents,
458
self._new_id, self._new_executability):
459
new_ids.update(id_set)
460
new_paths = [(fp.get_path(t), t) for t in new_ids]
518
464
def tree_kind(self, trans_id):
519
465
"""Determine the file kind in the working tree.
908
853
self._limbo_files[trans_id] = limbo_name
909
854
return limbo_name
911
def _set_executability(self, path, trans_id):
856
def _set_executability(self, path, entry, trans_id):
912
857
"""Set the executability of versioned files """
858
new_executability = self._new_executability[trans_id]
859
entry.executable = new_executability
913
860
if supports_executable():
914
new_executability = self._new_executability[trans_id]
915
861
abspath = self._tree.abspath(path)
916
862
current_mode = os.stat(abspath).st_mode
917
863
if new_executability:
1232
1178
:param no_conflicts: if True, the caller guarantees there are no
1233
1179
conflicts, so no check is made.
1234
:param precomputed_delta: An inventory delta to use instead of
1236
1180
:param _mover: Supply an alternate FileMover, for testing
1238
1182
if not no_conflicts:
1239
1183
conflicts = self.find_conflicts()
1240
1184
if len(conflicts) != 0:
1241
1185
raise MalformedTransform(conflicts=conflicts)
1186
inventory_delta = []
1242
1187
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1244
if precomputed_delta is None:
1245
child_pb.update('Apply phase', 0, 2)
1246
inventory_delta = self._generate_inventory_delta()
1249
inventory_delta = precomputed_delta
1251
1189
if _mover is None:
1252
1190
mover = _FileMover()
1256
child_pb.update('Apply phase', 0 + offset, 2 + offset)
1257
self._apply_removals(mover)
1258
child_pb.update('Apply phase', 1 + offset, 2 + offset)
1259
modified_paths = self._apply_insertions(mover)
1194
child_pb.update('Apply phase', 0, 2)
1195
self._apply_removals(inventory_delta, mover)
1196
child_pb.update('Apply phase', 1, 2)
1197
modified_paths = self._apply_insertions(inventory_delta, mover)
1261
1199
mover.rollback()
1269
1207
self.finalize()
1270
1208
return _TransformResults(modified_paths, self.rename_count)
1272
def _generate_inventory_delta(self):
1273
"""Generate an inventory delta for the current transform."""
1274
inventory_delta = []
1275
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1276
new_paths = self._inventory_altered()
1277
total_entries = len(new_paths) + len(self._removed_id)
1279
for num, trans_id in enumerate(self._removed_id):
1281
child_pb.update('removing file', num, total_entries)
1282
if trans_id == self._new_root:
1283
file_id = self._tree.get_root_id()
1285
file_id = self.tree_file_id(trans_id)
1286
# File-id isn't really being deleted, just moved
1287
if file_id in self._r_new_id:
1289
path = self._tree_id_paths[trans_id]
1290
inventory_delta.append((path, None, file_id, None))
1291
new_path_file_ids = dict((t, self.final_file_id(t)) for p, t in
1293
entries = self._tree.iter_entries_by_dir(
1294
new_path_file_ids.values())
1295
old_paths = dict((e.file_id, p) for p, e in entries)
1297
for num, (path, trans_id) in enumerate(new_paths):
1299
child_pb.update('adding file',
1300
num + len(self._removed_id), total_entries)
1301
file_id = new_path_file_ids[trans_id]
1306
kind = self.final_kind(trans_id)
1308
kind = self._tree.stored_kind(file_id)
1309
parent_trans_id = self.final_parent(trans_id)
1310
parent_file_id = new_path_file_ids.get(parent_trans_id)
1311
if parent_file_id is None:
1312
parent_file_id = self.final_file_id(parent_trans_id)
1313
if trans_id in self._new_reference_revision:
1314
new_entry = inventory.TreeReference(
1316
self._new_name[trans_id],
1317
self.final_file_id(self._new_parent[trans_id]),
1318
None, self._new_reference_revision[trans_id])
1320
new_entry = inventory.make_entry(kind,
1321
self.final_name(trans_id),
1322
parent_file_id, file_id)
1323
old_path = old_paths.get(new_entry.file_id)
1324
new_executability = self._new_executability.get(trans_id)
1325
if new_executability is not None:
1326
new_entry.executable = new_executability
1327
inventory_delta.append(
1328
(old_path, path, new_entry.file_id, new_entry))
1331
return inventory_delta
1333
def _apply_removals(self, mover):
1210
def _apply_removals(self, inventory_delta, mover):
1334
1211
"""Perform tree operations that remove directory/inventory names.
1336
1213
That is, delete files that are to be deleted, and put any files that
1337
1214
need renaming into limbo. This must be done in strict child-to-parent
1340
If inventory_delta is None, no inventory delta generation is performed.
1342
1217
tree_paths = list(self._tree_path_ids.iteritems())
1343
1218
tree_paths.sort(reverse=True)
1361
1236
self.rename_count += 1
1237
if trans_id in self._removed_id:
1238
if trans_id == self._new_root:
1239
file_id = self._tree.get_root_id()
1241
file_id = self.tree_file_id(trans_id)
1242
# File-id isn't really being deleted, just moved
1243
if file_id in self._r_new_id:
1245
inventory_delta.append((path, None, file_id, None))
1363
1247
child_pb.finished()
1365
def _apply_insertions(self, mover):
1249
def _apply_insertions(self, inventory_delta, mover):
1366
1250
"""Perform tree operations that insert directory/inventory names.
1368
1252
That is, create any files that need to be created, and restore from
1369
1253
limbo any files that needed renaming. This must be done in strict
1370
1254
parent-to-child order.
1372
If inventory_delta is None, no inventory delta is calculated, and
1373
no list of modified paths is returned.
1375
new_paths = self.new_paths(filesystem_only=True)
1256
new_paths = self.new_paths()
1376
1257
modified_paths = []
1377
new_path_file_ids = dict((t, self.final_file_id(t)) for p, t in
1379
1258
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1381
1261
for num, (path, trans_id) in enumerate(new_paths):
1383
child_pb.update('adding file', num, len(new_paths))
1384
full_path = self._tree.abspath(path)
1385
if trans_id in self._needs_rename:
1387
mover.rename(self._limbo_name(trans_id), full_path)
1389
# We may be renaming a dangling inventory id
1390
if e.errno != errno.ENOENT:
1393
self.rename_count += 1
1394
if (trans_id in self._new_contents or
1395
self.path_changed(trans_id)):
1263
child_pb.update('adding file', num, len(new_paths))
1264
if trans_id in self._new_contents or \
1265
self.path_changed(trans_id):
1266
full_path = self._tree.abspath(path)
1267
if trans_id in self._needs_rename:
1269
mover.rename(self._limbo_name(trans_id), full_path)
1271
# We may be renaming a dangling inventory id
1272
if e.errno != errno.ENOENT:
1275
self.rename_count += 1
1396
1276
if trans_id in self._new_contents:
1397
1277
modified_paths.append(full_path)
1278
completed_new.append(trans_id)
1279
file_id = self.final_file_id(trans_id)
1280
if file_id is not None and (trans_id in self._new_id or
1281
trans_id in self._new_name or trans_id in self._new_parent
1282
or trans_id in self._new_executability):
1284
kind = self.final_kind(trans_id)
1286
kind = self._tree.stored_kind(file_id)
1287
if trans_id in self._new_reference_revision:
1288
new_entry = inventory.TreeReference(
1289
self.final_file_id(trans_id),
1290
self._new_name[trans_id],
1291
self.final_file_id(self._new_parent[trans_id]),
1292
None, self._new_reference_revision[trans_id])
1294
new_entry = inventory.make_entry(kind,
1295
self.final_name(trans_id),
1296
self.final_file_id(self.final_parent(trans_id)),
1297
self.final_file_id(trans_id))
1299
old_path = self._tree.id2path(new_entry.file_id)
1300
except errors.NoSuchId:
1302
inventory_delta.append((old_path, path, new_entry.file_id,
1398
1305
if trans_id in self._new_executability:
1399
self._set_executability(path, trans_id)
1306
self._set_executability(path, new_entry, trans_id)
1401
1308
child_pb.finished()
1402
self._new_contents.clear()
1309
for trans_id in completed_new:
1310
del self._new_contents[trans_id]
1403
1311
return modified_paths
1475
1374
# InterTree.iter_changes.
1476
1375
return (changes is not None and changes[2])
1478
def _get_repository(self):
1479
repo = getattr(self._transform._tree, '_repository', None)
1481
repo = self._transform._tree.branch.repository
1484
def _iter_parent_trees(self):
1485
for revision_id in self.get_parent_ids():
1487
yield self.revision_tree(revision_id)
1488
except errors.NoSuchRevisionInTree:
1489
yield self._get_repository().revision_tree(revision_id)
1491
1377
def _get_file_revision(self, file_id, vf, tree_revision):
1492
parent_keys = [(file_id, self._file_revision(t, file_id)) for t in
1493
self._iter_parent_trees()]
1494
vf.add_lines((file_id, tree_revision), parent_keys,
1495
self.get_file(file_id).readlines())
1496
repo = self._get_repository()
1497
base_vf = repo.texts
1498
if base_vf not in vf.fallback_versionedfiles:
1499
vf.fallback_versionedfiles.append(base_vf)
1500
return tree_revision
1378
return self._transform._tree._get_file_revision(file_id, vf,
1502
1381
def _stat_limbo_file(self, file_id):
1503
1382
trans_id = self._transform.trans_id_file_id(file_id)
1504
1383
name = self._transform._limbo_name(trans_id)
1505
1384
return os.lstat(name)
1508
def _by_parent(self):
1509
if self.__by_parent is None:
1510
self.__by_parent = self._transform.by_parent()
1511
return self.__by_parent
1513
def _comparison_data(self, entry, path):
1514
kind, size, executable, link_or_sha1 = self.path_content_summary(path)
1515
if kind == 'missing':
1519
file_id = self._transform.final_file_id(self._path2trans_id(path))
1520
executable = self.is_executable(file_id, path)
1521
return kind, executable, None
1523
1386
def lock_read(self):
1524
1387
# Perhaps in theory, this should lock the TreeTransform?
1536
1399
return self._transform.final_file_id(self._transform.root)
1538
1401
def all_file_ids(self):
1539
tree_ids = set(self._transform._tree.all_file_ids())
1540
tree_ids.difference_update(self._transform.tree_file_id(t)
1541
for t in self._transform._removed_id)
1542
tree_ids.update(self._transform._new_id.values())
1402
return self._transform._tree.all_file_ids()
1545
1404
def __iter__(self):
1546
1405
return iter(self.all_file_ids())
1548
def has_id(self, file_id):
1549
if file_id in self._transform._r_new_id:
1551
elif file_id in self._transform._removed_id:
1554
return self._transform._tree.has_id(file_id)
1556
def _path2trans_id(self, path):
1557
# We must not use None here, because that is a valid value to store.
1558
trans_id = self._path2trans_id_cache.get(path, object)
1559
if trans_id is not object:
1561
segments = splitpath(path)
1562
cur_parent = self._transform.root
1563
for cur_segment in segments:
1564
for child in self._all_children(cur_parent):
1565
final_name = self._final_name_cache.get(child)
1566
if final_name is None:
1567
final_name = self._transform.final_name(child)
1568
self._final_name_cache[child] = final_name
1569
if final_name == cur_segment:
1573
self._path2trans_id_cache[path] = None
1575
self._path2trans_id_cache[path] = cur_parent
1407
def paths2ids(self, specific_files, trees=None, require_versioned=False):
1408
"""See Tree.paths2ids"""
1409
to_find = set(specific_files)
1411
for (file_id, paths, changed, versioned, parent, name, kind,
1412
executable) in self._transform.iter_changes():
1413
if paths[1] in to_find:
1414
result.append(file_id)
1415
to_find.remove(paths[1])
1416
result.update(self._transform._tree.paths2ids(to_find,
1417
trees=[], require_versioned=require_versioned))
1578
1420
def path2id(self, path):
1579
return self._transform.final_file_id(self._path2trans_id(path))
1421
return self._transform._tree.path2id(path)
1581
1423
def id2path(self, file_id):
1582
1424
trans_id = self._transform.trans_id_file_id(file_id)
1585
1427
except NoFinalPath:
1586
1428
raise errors.NoSuchId(self, file_id)
1588
def _all_children(self, trans_id):
1589
children = self._all_children_cache.get(trans_id)
1590
if children is not None:
1592
children = set(self._transform.iter_tree_children(trans_id))
1593
# children in the _new_parent set are provided by _by_parent.
1594
children.difference_update(self._transform._new_parent.keys())
1595
children.update(self._by_parent.get(trans_id, []))
1596
self._all_children_cache[trans_id] = children
1599
def iter_children(self, file_id):
1600
trans_id = self._transform.trans_id_file_id(file_id)
1601
for child_trans_id in self._all_children(trans_id):
1602
yield self._transform.final_file_id(child_trans_id)
1605
possible_extras = set(self._transform.trans_id_tree_path(p) for p
1606
in self._transform._tree.extras())
1607
possible_extras.update(self._transform._new_contents)
1608
possible_extras.update(self._transform._removed_id)
1609
for trans_id in possible_extras:
1610
if self._transform.final_file_id(trans_id) is None:
1611
yield self._final_paths._determine_path(trans_id)
1613
def _make_inv_entries(self, ordered_entries, specific_file_ids):
1614
for trans_id, parent_file_id in ordered_entries:
1615
file_id = self._transform.final_file_id(trans_id)
1618
if (specific_file_ids is not None
1619
and file_id not in specific_file_ids):
1622
kind = self._transform.final_kind(trans_id)
1624
kind = self._transform._tree.stored_kind(file_id)
1625
new_entry = inventory.make_entry(
1627
self._transform.final_name(trans_id),
1628
parent_file_id, file_id)
1629
yield new_entry, trans_id
1631
def _list_files_by_dir(self):
1632
todo = [ROOT_PARENT]
1634
while len(todo) > 0:
1636
parent_file_id = self._transform.final_file_id(parent)
1637
children = list(self._all_children(parent))
1638
paths = dict(zip(children, self._final_paths.get_paths(children)))
1639
children.sort(key=paths.get)
1640
todo.extend(reversed(children))
1641
for trans_id in children:
1642
ordered_ids.append((trans_id, parent_file_id))
1645
1430
def iter_entries_by_dir(self, specific_file_ids=None):
1646
# This may not be a maximally efficient implementation, but it is
1647
# reasonably straightforward. An implementation that grafts the
1648
# TreeTransform changes onto the tree's iter_entries_by_dir results
1649
# might be more efficient, but requires tricky inferences about stack
1651
ordered_ids = self._list_files_by_dir()
1652
for entry, trans_id in self._make_inv_entries(ordered_ids,
1654
yield unicode(self._final_paths.get_path(trans_id)), entry
1656
def list_files(self, include_root=False):
1657
"""See Tree.list_files."""
1658
# XXX This should behave like WorkingTree.list_files, but is really
1659
# more like RevisionTree.list_files.
1660
for path, entry in self.iter_entries_by_dir():
1661
if entry.name == '' and not include_root:
1663
yield path, 'V', entry.kind, entry.file_id, entry
1431
return self._transform._tree.iter_entries_by_dir(specific_file_ids)
1665
1433
def kind(self, file_id):
1666
1434
trans_id = self._transform.trans_id_file_id(file_id)
1667
1435
return self._transform.final_kind(trans_id)
1669
1437
def stored_kind(self, file_id):
1670
trans_id = self._transform.trans_id_file_id(file_id)
1672
return self._transform._new_contents[trans_id]
1674
return self._transform._tree.stored_kind(file_id)
1438
return self._transform._tree.stored_kind(file_id)
1676
1440
def get_file_mtime(self, file_id, path=None):
1677
1441
"""See Tree.get_file_mtime"""
1692
1453
def get_file_sha1(self, file_id, path=None, stat_value=None):
1693
trans_id = self._transform.trans_id_file_id(file_id)
1694
kind = self._transform._new_contents.get(trans_id)
1696
return self._transform._tree.get_file_sha1(file_id)
1698
fileobj = self.get_file(file_id)
1700
return sha_file(fileobj)
1454
return self._transform._tree.get_file_sha1(file_id)
1704
1456
def is_executable(self, file_id, path=None):
1707
trans_id = self._transform.trans_id_file_id(file_id)
1709
return self._transform._new_executability[trans_id]
1712
return self._transform._tree.is_executable(file_id, path)
1714
if e.errno == errno.ENOENT:
1717
except errors.NoSuchId:
1457
return self._transform._tree.is_executable(file_id, path)
1720
1459
def path_content_summary(self, path):
1721
trans_id = self._path2trans_id(path)
1722
tt = self._transform
1723
tree_path = tt._tree_id_paths.get(trans_id)
1724
kind = tt._new_contents.get(trans_id)
1726
if tree_path is None or trans_id in tt._removed_contents:
1727
return 'missing', None, None, None
1728
summary = tt._tree.path_content_summary(tree_path)
1729
kind, size, executable, link_or_sha1 = summary
1732
limbo_name = tt._limbo_name(trans_id)
1733
if trans_id in tt._new_reference_revision:
1734
kind = 'tree-reference'
1736
statval = os.lstat(limbo_name)
1737
size = statval.st_size
1738
if not supports_executable():
1741
executable = statval.st_mode & S_IEXEC
1745
if kind == 'symlink':
1746
link_or_sha1 = os.readlink(limbo_name)
1747
if supports_executable():
1748
executable = tt._new_executability.get(trans_id, executable)
1749
return kind, size, executable, link_or_sha1
1460
return self._transform._tree.path_content_summary(path)
1751
1462
def iter_changes(self, from_tree, include_unchanged=False,
1752
1463
specific_files=None, pb=None, extra_trees=None,
1753
1464
require_versioned=True, want_unversioned=False):
1754
1465
"""See InterTree.iter_changes.
1756
This has a fast path that is only used when the from_tree matches
1757
the transform tree, and no fancy options are supplied.
1467
This implementation does not support include_unchanged, specific_files,
1468
or want_unversioned. extra_trees, require_versioned, and pb are
1759
if (from_tree is not self._transform._tree or include_unchanged or
1760
specific_files or want_unversioned):
1761
return tree.InterTree(from_tree, self).iter_changes(
1762
include_unchanged=include_unchanged,
1763
specific_files=specific_files,
1765
extra_trees=extra_trees,
1766
require_versioned=require_versioned,
1767
want_unversioned=want_unversioned)
1471
if from_tree is not self._transform._tree:
1472
raise ValueError('from_tree must be transform source tree.')
1473
if include_unchanged:
1474
raise ValueError('include_unchanged is not supported')
1475
if specific_files is not None:
1476
raise ValueError('specific_files is not supported')
1768
1477
if want_unversioned:
1769
1478
raise ValueError('want_unversioned is not supported')
1770
1479
return self._transform.iter_changes()
1777
1486
name = self._transform._limbo_name(trans_id)
1778
1487
return open(name, 'rb')
1489
def get_file_text(self, file_id):
1490
text_file = self.get_file(file_id)
1492
return text_file.read()
1780
1496
def annotate_iter(self, file_id,
1781
1497
default_revision=_mod_revision.CURRENT_REVISION):
1782
changes = self._changes(file_id)
1786
changed_content, versioned, kind = (changes[2], changes[3],
1790
get_old = (kind[0] == 'file' and versioned[0])
1792
old_annotation = self._transform._tree.annotate_iter(file_id,
1793
default_revision=default_revision)
1797
return old_annotation
1798
if not changed_content:
1799
return old_annotation
1800
return annotate.reannotate([old_annotation],
1801
self.get_file(file_id).readlines(),
1498
return self._transform._tree.annotate_iter(file_id,
1499
default_revision=default_revision)
1804
1501
def get_symlink_target(self, file_id):
1805
1502
"""See Tree.get_symlink_target"""
1809
1506
name = self._transform._limbo_name(trans_id)
1810
1507
return os.readlink(name)
1812
def walkdirs(self, prefix=''):
1813
pending = [self._transform.root]
1814
while len(pending) > 0:
1815
parent_id = pending.pop()
1818
prefix = prefix.rstrip('/')
1819
parent_path = self._final_paths.get_path(parent_id)
1820
parent_file_id = self._transform.final_file_id(parent_id)
1821
for child_id in self._all_children(parent_id):
1822
path_from_root = self._final_paths.get_path(child_id)
1823
basename = self._transform.final_name(child_id)
1824
file_id = self._transform.final_file_id(child_id)
1826
kind = self._transform.final_kind(child_id)
1827
versioned_kind = kind
1830
versioned_kind = self._transform._tree.stored_kind(file_id)
1831
if versioned_kind == 'directory':
1832
subdirs.append(child_id)
1833
children.append((path_from_root, basename, kind, None,
1834
file_id, versioned_kind))
1836
if parent_path.startswith(prefix):
1837
yield (parent_path, parent_file_id), children
1838
pending.extend(sorted(subdirs, key=self._final_paths.get_path,
1509
def list_files(self, include_root=False):
1510
return self._transform._tree.list_files(include_root)
1512
def walkdirs(self, prefix=""):
1513
return self._transform._tree.walkdirs(prefix)
1841
1515
def get_parent_ids(self):
1842
return self._parent_ids
1844
def set_parent_ids(self, parent_ids):
1845
self._parent_ids = parent_ids
1516
return self._transform._tree.get_parent_ids()
1847
1518
def get_revision_tree(self, revision_id):
1848
1519
return self._transform._tree.get_revision_tree(revision_id)
1942
def _build_tree(tree, wt, accelerator_tree, hardlink, delta_from_tree):
1605
def _build_tree(tree, wt, accelerator_tree, hardlink):
1943
1606
"""See build_tree."""
1944
1607
for num, _unused in enumerate(wt.all_file_ids()):
1945
1608
if num > 0: # more than just a root
1946
1609
raise errors.WorkingTreeAlreadyPopulated(base=wt.basedir)
1947
existing_files = set()
1948
for dir, files in wt.walkdirs():
1949
existing_files.update(f[0] for f in files)
1950
1610
file_trans_id = {}
1951
1611
top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1952
1612
pp = ProgressPhase("Build phase", 2, top_pb)
1972
1632
deferred_contents = []
1974
total = len(tree.inventory)
1976
precomputed_delta = []
1978
precomputed_delta = None
1979
1634
for num, (tree_path, entry) in \
1980
1635
enumerate(tree.inventory.iter_entries_by_dir()):
1981
pb.update("Building tree", num - len(deferred_contents), total)
1636
pb.update("Building tree", num - len(deferred_contents),
1637
len(tree.inventory))
1982
1638
if entry.parent_id is None:
1984
1640
reparent = False
1985
1641
file_id = entry.file_id
1987
precomputed_delta.append((None, tree_path, file_id, entry))
1988
if tree_path in existing_files:
1989
target_path = wt.abspath(tree_path)
1642
target_path = wt.abspath(tree_path)
1990
1644
kind = file_kind(target_path)
1991
1648
if kind == "directory":
1993
1650
bzrdir.BzrDir.open(target_path)
2001
1658
tt.delete_contents(tt.trans_id_tree_path(tree_path))
2002
1659
if kind == 'directory':
2003
1660
reparent = True
1661
if entry.parent_id not in file_trans_id:
1662
raise AssertionError(
1663
'entry %s parent id %r is not in file_trans_id %r'
1664
% (entry, entry.parent_id, file_trans_id))
2004
1665
parent_id = file_trans_id[entry.parent_id]
2005
1666
if entry.kind == 'file':
2006
1667
# We *almost* replicate new_by_entry, so that we can defer
2007
1668
# getting the file text, and get them all at once.
2008
1669
trans_id = tt.create_path(entry.name, parent_id)
2009
1670
file_trans_id[file_id] = trans_id
2010
tt.version_file(file_id, trans_id)
2011
executable = tree.is_executable(file_id, tree_path)
1671
tt.version_file(entry.file_id, trans_id)
1672
executable = tree.is_executable(entry.file_id, tree_path)
1673
if executable is not None:
2013
1674
tt.set_executability(executable, trans_id)
2014
deferred_contents.append((file_id, trans_id))
1675
deferred_contents.append((entry.file_id, trans_id))
2016
1677
file_trans_id[file_id] = new_by_entry(tt, entry, parent_id,
2170
1824
tt.create_directory(trans_id)
2173
def create_from_tree(tt, trans_id, tree, file_id, bytes=None):
2174
"""Create new file contents according to tree contents."""
2175
kind = tree.kind(file_id)
2176
if kind == 'directory':
2177
tt.create_directory(trans_id)
2178
elif kind == "file":
2180
tree_file = tree.get_file(file_id)
2182
bytes = tree_file.readlines()
2185
tt.create_file(bytes, trans_id)
2186
elif kind == "symlink":
2187
tt.create_symlink(tree.get_symlink_target(file_id), trans_id)
2189
raise AssertionError('Unknown kind %r' % kind)
2192
1827
def create_entry_executability(tt, entry, trans_id):
2193
1828
"""Set the executability of a trans_id according to an inventory entry"""
2194
1829
if entry.kind == "file":
2245
1880
tt = TreeTransform(working_tree, pb)
2247
1882
pp = ProgressPhase("Revert phase", 3, pb)
2248
conflicts, merge_modified = _prepare_revert_transform(
2249
working_tree, target_tree, tt, filenames, backups, pp)
1884
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1886
merge_modified = _alter_files(working_tree, target_tree, tt,
1887
child_pb, filenames, backups)
1891
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1893
raw_conflicts = resolve_conflicts(tt, child_pb,
1894
lambda t, c: conflict_pass(t, c, target_tree))
1897
conflicts = cook_conflicts(raw_conflicts, tt)
2250
1898
if change_reporter:
2251
1899
change_reporter = delta._ChangeReporter(
2252
1900
unversioned_filter=working_tree.is_ignored)
2263
1911
return conflicts
2266
def _prepare_revert_transform(working_tree, target_tree, tt, filenames,
2267
backups, pp, basis_tree=None,
2268
merge_modified=None):
2270
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
2272
if merge_modified is None:
2273
merge_modified = working_tree.merge_modified()
2274
merge_modified = _alter_files(working_tree, target_tree, tt,
2275
child_pb, filenames, backups,
2276
merge_modified, basis_tree)
2280
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
2282
raw_conflicts = resolve_conflicts(tt, child_pb,
2283
lambda t, c: conflict_pass(t, c, target_tree))
2286
conflicts = cook_conflicts(raw_conflicts, tt)
2287
return conflicts, merge_modified
2290
1914
def _alter_files(working_tree, target_tree, tt, pb, specific_files,
2291
backups, merge_modified, basis_tree=None):
2292
if basis_tree is not None:
2293
basis_tree.lock_read()
1916
merge_modified = working_tree.merge_modified()
2294
1917
change_list = target_tree.iter_changes(working_tree,
2295
1918
specific_files=specific_files, pb=pb)
2296
if target_tree.get_root_id() is None:
1919
if target_tree.inventory.root is None:
2297
1920
skip_root = True
2299
1922
skip_root = False
2301
1925
deferred_files = []
2302
1926
for id_num, (file_id, path, changed_content, versioned, parent, name,
2369
1989
tt.version_file(file_id, trans_id)
2370
1990
if versioned == (True, False):
2371
1991
tt.unversion_file(trans_id)
2372
if (name[1] is not None and
1992
if (name[1] is not None and
2373
1993
(name[0] != name[1] or parent[0] != parent[1])):
2374
if name[1] == '' and parent[1] is None:
2375
parent_trans = ROOT_PARENT
2377
parent_trans = tt.trans_id_file_id(parent[1])
2378
tt.adjust_path(name[1], parent_trans, trans_id)
1995
name[1], tt.trans_id_file_id(parent[1]), trans_id)
2379
1996
if executable[0] != executable[1] and kind[1] == "file":
2380
1997
tt.set_executability(executable[1], trans_id)
2381
1998
for (trans_id, mode_id), bytes in target_tree.iter_files_bytes(