1488
1498
except KeyError:
1491
entry = next(self._tree.iter_entries_by_dir(
1492
specific_files=[path]))[1]
1493
except StopIteration:
1501
for child in self._tree.iter_child_entries(path):
1502
childpath = joinpath(path, child.name)
1503
yield self.trans_id_tree_path(childpath)
1504
except errors.NoSuchFile:
1495
children = getattr(entry, 'children', {})
1496
for child in children:
1497
childpath = joinpath(path, child)
1498
yield self.trans_id_tree_path(childpath)
1500
1507
def new_orphan(self, trans_id, parent_id):
1501
1508
raise NotImplementedError(self.new_orphan)
1504
class GitPreviewTree(PreviewTree):
1511
class GitPreviewTree(PreviewTree, MutableGitIndexTree):
1505
1512
"""Partial implementation of Tree to support show_diff_trees"""
1507
1514
def __init__(self, transform):
1508
1515
PreviewTree.__init__(self, transform)
1516
MutableGitIndexTree.__init__(self)
1509
1517
self._final_paths = FinalPaths(transform)
1518
self.store = transform._tree.store
1520
def _supports_executable(self):
1521
return self._transform._limbo_supports_executable()
1511
1523
def walkdirs(self, prefix=''):
1512
1524
pending = [self._transform.root]
1535
1547
yield parent_path, children
1536
1548
pending.extend(sorted(subdirs, key=self._final_paths.get_path,
1551
def iter_changes(self, from_tree, include_unchanged=False,
1552
specific_files=None, pb=None, extra_trees=None,
1553
require_versioned=True, want_unversioned=False):
1554
"""See InterTree.iter_changes.
1556
This has a fast path that is only used when the from_tree matches
1557
the transform tree, and no fancy options are supplied.
1559
return InterTree.get(from_tree, self).iter_changes(
1560
include_unchanged=include_unchanged,
1561
specific_files=specific_files,
1563
extra_trees=extra_trees,
1564
require_versioned=require_versioned,
1565
want_unversioned=want_unversioned)
1567
def get_file(self, path):
1568
"""See Tree.get_file"""
1569
trans_id = self._path2trans_id(path)
1570
if trans_id is None:
1571
raise errors.NoSuchFile(path)
1572
if trans_id in self._transform._removed_contents:
1573
raise errors.NoSuchFile(path)
1574
if trans_id not in self._transform._new_contents:
1575
orig_path = self._transform.tree_path(trans_id)
1576
return self._transform._tree.get_file(orig_path)
1577
name = self._transform._limbo_name(trans_id)
1578
return open(name, 'rb')
1580
def get_symlink_target(self, path):
1581
"""See Tree.get_symlink_target"""
1582
trans_id = self._path2trans_id(path)
1583
if trans_id is None:
1584
raise errors.NoSuchFile(path)
1585
if trans_id not in self._transform._new_contents:
1586
orig_path = self._transform.tree_path(trans_id)
1587
return self._transform._tree.get_symlink_target(orig_path)
1588
name = self._transform._limbo_name(trans_id)
1589
return osutils.readlink(name)
1591
def annotate_iter(self, path, default_revision=_mod_revision.CURRENT_REVISION):
1592
trans_id = self._path2trans_id(path)
1593
if trans_id is None:
1594
raise errors.NoSuchFile(path)
1595
orig_path = self._transform.tree_path(trans_id)
1596
if orig_path is not None:
1597
old_annotation = self._transform._tree.annotate_iter(
1598
orig_path, default_revision=default_revision)
1602
lines = self.get_file_lines(path)
1603
except errors.NoSuchFile:
1605
return annotate.reannotate([old_annotation], lines, default_revision)
1607
def get_file_text(self, path):
1608
"""Return the byte content of a file.
1610
:param path: The path of the file.
1612
:returns: A single byte string for the whole file.
1614
with self.get_file(path) as my_file:
1615
return my_file.read()
1617
def get_file_lines(self, path):
1618
"""Return the content of a file, as lines.
1620
:param path: The path of the file.
1622
return osutils.split_lines(self.get_file_text(path))
1625
possible_extras = set(self._transform.trans_id_tree_path(p) for p
1626
in self._transform._tree.extras())
1627
possible_extras.update(self._transform._new_contents)
1628
possible_extras.update(self._transform._removed_id)
1629
for trans_id in possible_extras:
1630
if not self._transform.final_is_versioned(trans_id):
1631
yield self._final_paths._determine_path(trans_id)
1633
def path_content_summary(self, path):
1634
trans_id = self._path2trans_id(path)
1635
tt = self._transform
1636
tree_path = tt.tree_path(trans_id)
1637
kind = tt._new_contents.get(trans_id)
1639
if tree_path is None or trans_id in tt._removed_contents:
1640
return 'missing', None, None, None
1641
summary = tt._tree.path_content_summary(tree_path)
1642
kind, size, executable, link_or_sha1 = summary
1645
limbo_name = tt._limbo_name(trans_id)
1646
if trans_id in tt._new_reference_revision:
1647
kind = 'tree-reference'
1649
statval = os.lstat(limbo_name)
1650
size = statval.st_size
1651
if not tt._limbo_supports_executable():
1654
executable = statval.st_mode & S_IEXEC
1658
if kind == 'symlink':
1659
link_or_sha1 = os.readlink(limbo_name)
1660
if not isinstance(link_or_sha1, text_type):
1661
link_or_sha1 = link_or_sha1.decode(osutils._fs_enc)
1662
executable = tt._new_executability.get(trans_id, executable)
1663
return kind, size, executable, link_or_sha1
1665
def get_file_mtime(self, path):
1666
"""See Tree.get_file_mtime"""
1667
trans_id = self._path2trans_id(path)
1668
if trans_id is None:
1669
raise errors.NoSuchFile(path)
1670
if trans_id not in self._transform._new_contents:
1671
return self._transform._tree.get_file_mtime(
1672
self._transform.tree_path(trans_id))
1673
name = self._transform._limbo_name(trans_id)
1674
statval = os.lstat(name)
1675
return statval.st_mtime
1677
def is_versioned(self, path):
1678
trans_id = self._path2trans_id(path)
1679
if trans_id is None:
1680
# It doesn't exist, so it's not versioned.
1682
if trans_id in self._transform._versioned:
1684
orig_path = self._transform.tree_path(trans_id)
1685
return self._transform._tree.is_versioned(orig_path)
1687
def iter_entries_by_dir(self, specific_files=None, recurse_nested=False):
1689
raise NotImplementedError(
1690
'follow tree references not yet supported')
1692
# This may not be a maximally efficient implementation, but it is
1693
# reasonably straightforward. An implementation that grafts the
1694
# TreeTransform changes onto the tree's iter_entries_by_dir results
1695
# might be more efficient, but requires tricky inferences about stack
1697
ordered_ids = self._list_files_by_dir()
1698
for trans_id in ordered_ids:
1700
yield self._final_paths.get_path(trans_id), entry
1702
def _list_files_by_dir(self):
1703
todo = [ROOT_PARENT]
1705
while len(todo) > 0:
1707
children = list(self._all_children(parent))
1708
paths = dict(zip(children, self._final_paths.get_paths(children)))
1709
children.sort(key=paths.get)
1710
todo.extend(reversed(children))
1711
for trans_id in children:
1712
ordered_ids.append(trans_id)
1715
def revision_tree(self, revision_id):
1716
return self._transform._tree.revision_tree(revision_id)
1718
def _stat_limbo_file(self, trans_id):
1719
name = self._transform._limbo_name(trans_id)
1720
return os.lstat(name)