1836
1781
calculating one.
1837
1782
:param _mover: Supply an alternate FileMover, for testing
1839
for hook in MutableTree.hooks['pre_transform']:
1840
hook(self._tree, self)
1841
if not no_conflicts:
1842
self._check_malformed()
1843
with ui.ui_factory.nested_progress_bar() as child_pb:
1844
if precomputed_delta is None:
1845
child_pb.update(gettext('Apply phase'), 0, 2)
1846
inventory_delta = self._generate_inventory_delta()
1849
inventory_delta = precomputed_delta
1852
mover = _FileMover()
1856
child_pb.update(gettext('Apply phase'), 0 + offset, 2 + offset)
1857
self._apply_removals(mover)
1858
child_pb.update(gettext('Apply phase'), 1 + offset, 2 + offset)
1859
modified_paths = self._apply_insertions(mover)
1860
except BaseException:
1864
mover.apply_deletions()
1865
if not self.final_is_versioned(self.root):
1866
inventory_delta = [e for e in inventory_delta if e[0] != '']
1867
self._tree.apply_inventory_delta(inventory_delta)
1868
self._apply_observed_sha1s()
1871
return _TransformResults(modified_paths, self.rename_count)
1873
def _generate_inventory_delta(self):
1874
"""Generate an inventory delta for the current transform."""
1875
inventory_delta = []
1876
new_paths = self._inventory_altered()
1877
total_entries = len(new_paths) + len(self._removed_id)
1878
with ui.ui_factory.nested_progress_bar() as child_pb:
1879
for num, trans_id in enumerate(self._removed_id):
1881
child_pb.update(gettext('removing file'),
1883
if trans_id == self._new_root:
1884
file_id = self._tree.path2id('')
1886
file_id = self.tree_file_id(trans_id)
1887
# File-id isn't really being deleted, just moved
1888
if file_id in self._r_new_id:
1890
path = self._tree_id_paths[trans_id]
1891
inventory_delta.append((path, None, file_id, None))
1892
new_path_file_ids = dict((t, self.final_file_id(t)) for p, t in
1894
for num, (path, trans_id) in enumerate(new_paths):
1896
child_pb.update(gettext('adding file'),
1897
num + len(self._removed_id), total_entries)
1898
file_id = new_path_file_ids[trans_id]
1901
kind = self.final_kind(trans_id)
1903
kind = self._tree.stored_kind(self._tree.id2path(file_id))
1904
parent_trans_id = self.final_parent(trans_id)
1905
parent_file_id = new_path_file_ids.get(parent_trans_id)
1906
if parent_file_id is None:
1907
parent_file_id = self.final_file_id(parent_trans_id)
1908
if trans_id in self._new_reference_revision:
1909
new_entry = inventory.TreeReference(
1911
self._new_name[trans_id],
1912
self.final_file_id(self._new_parent[trans_id]),
1913
None, self._new_reference_revision[trans_id])
1915
new_entry = inventory.make_entry(kind,
1916
self.final_name(trans_id),
1917
parent_file_id, file_id)
1919
old_path = self._tree.id2path(new_entry.file_id)
1920
except errors.NoSuchId:
1922
new_executability = self._new_executability.get(trans_id)
1923
if new_executability is not None:
1924
new_entry.executable = new_executability
1925
inventory_delta.append(
1926
(old_path, path, new_entry.file_id, new_entry))
1927
return inventory_delta
1929
def _apply_removals(self, mover):
1930
"""Perform tree operations that remove directory/inventory names.
1932
That is, delete files that are to be deleted, and put any files that
1933
need renaming into limbo. This must be done in strict child-to-parent
1936
If inventory_delta is None, no inventory delta generation is performed.
1938
tree_paths = sorted(viewitems(self._tree_path_ids), reverse=True)
1939
with ui.ui_factory.nested_progress_bar() as child_pb:
1940
for num, (path, trans_id) in enumerate(tree_paths):
1941
# do not attempt to move root into a subdirectory of itself.
1944
child_pb.update(gettext('removing file'), num, len(tree_paths))
1945
full_path = self._tree.abspath(path)
1946
if trans_id in self._removed_contents:
1947
delete_path = os.path.join(self._deletiondir, trans_id)
1948
mover.pre_delete(full_path, delete_path)
1949
elif (trans_id in self._new_name or
1950
trans_id in self._new_parent):
1952
mover.rename(full_path, self._limbo_name(trans_id))
1953
except errors.TransformRenameFailed as e:
1954
if e.errno != errno.ENOENT:
1957
self.rename_count += 1
1959
def _apply_insertions(self, mover):
1960
"""Perform tree operations that insert directory/inventory names.
1962
That is, create any files that need to be created, and restore from
1963
limbo any files that needed renaming. This must be done in strict
1964
parent-to-child order.
1966
If inventory_delta is None, no inventory delta is calculated, and
1967
no list of modified paths is returned.
1969
new_paths = self.new_paths(filesystem_only=True)
1971
with ui.ui_factory.nested_progress_bar() as child_pb:
1972
for num, (path, trans_id) in enumerate(new_paths):
1974
child_pb.update(gettext('adding file'),
1975
num, len(new_paths))
1976
full_path = self._tree.abspath(path)
1977
if trans_id in self._needs_rename:
1979
mover.rename(self._limbo_name(trans_id), full_path)
1980
except errors.TransformRenameFailed as e:
1981
# We may be renaming a dangling inventory id
1982
if e.errno != errno.ENOENT:
1985
self.rename_count += 1
1986
# TODO: if trans_id in self._observed_sha1s, we should
1987
# re-stat the final target, since ctime will be
1988
# updated by the change.
1989
if (trans_id in self._new_contents
1990
or self.path_changed(trans_id)):
1991
if trans_id in self._new_contents:
1992
modified_paths.append(full_path)
1993
if trans_id in self._new_executability:
1994
self._set_executability(path, trans_id)
1995
if trans_id in self._observed_sha1s:
1996
o_sha1, o_st_val = self._observed_sha1s[trans_id]
1997
st = osutils.lstat(full_path)
1998
self._observed_sha1s[trans_id] = (o_sha1, st)
1999
for path, trans_id in new_paths:
2000
# new_paths includes stuff like workingtree conflicts. Only the
2001
# stuff in new_contents actually comes from limbo.
2002
if trans_id in self._limbo_files:
2003
del self._limbo_files[trans_id]
2004
self._new_contents.clear()
2005
return modified_paths
2007
def _apply_observed_sha1s(self):
2008
"""After we have finished renaming everything, update observed sha1s
2010
This has to be done after self._tree.apply_inventory_delta, otherwise
2011
it doesn't know anything about the files we are updating. Also, we want
2012
to do this as late as possible, so that most entries end up cached.
2014
# TODO: this doesn't update the stat information for directories. So
2015
# the first 'bzr status' will still need to rewrite
2016
# .bzr/checkout/dirstate. However, we at least don't need to
2017
# re-read all of the files.
2018
# TODO: If the operation took a while, we could do a time.sleep(3) here
2019
# to allow the clock to tick over and ensure we won't have any
2020
# problems. (we could observe start time, and finish time, and if
2021
# it is less than eg 10% overhead, add a sleep call.)
2022
paths = FinalPaths(self)
2023
for trans_id, observed in viewitems(self._observed_sha1s):
2024
path = paths.get_path(trans_id)
2025
self._tree._observed_sha1(path, observed)
2028
class TransformPreview(DiskTreeTransform):
2029
"""A TreeTransform for generating preview trees.
2031
Unlike TreeTransform, this version works when the input tree is a
2032
RevisionTree, rather than a WorkingTree. As a result, it tends to ignore
2033
unversioned files in the input tree.
2036
def __init__(self, tree, pb=None, case_sensitive=True):
2038
limbodir = osutils.mkdtemp(prefix='bzr-limbo-')
2039
DiskTreeTransform.__init__(self, tree, limbodir, pb, case_sensitive)
2041
def tree_kind(self, trans_id):
2042
path = self._tree_id_paths.get(trans_id)
2045
kind = self._tree.path_content_summary(path)[0]
2046
if kind == 'missing':
2050
def _set_mode(self, trans_id, mode_id, typefunc):
2051
"""Set the mode of new file contents.
2052
The mode_id is the existing file to get the mode from (often the same
2053
as trans_id). The operation is only performed if there's a mode match
2054
according to typefunc.
2056
# is it ok to ignore this? probably
2059
def iter_tree_children(self, parent_id):
2060
"""Iterate through the entry's tree children, if any"""
2062
path = self._tree_id_paths[parent_id]
2066
entry = next(self._tree.iter_entries_by_dir(
2067
specific_files=[path]))[1]
2068
except StopIteration:
2070
children = getattr(entry, 'children', {})
2071
for child in children:
2072
childpath = joinpath(path, child)
2073
yield self.trans_id_tree_path(childpath)
2075
def new_orphan(self, trans_id, parent_id):
2076
raise NotImplementedError(self.new_orphan)
2079
class _PreviewTree(inventorytree.InventoryTree):
2080
"""Partial implementation of Tree to support show_diff_trees"""
2082
def __init__(self, transform):
2083
self._transform = transform
2084
self._final_paths = FinalPaths(transform)
2085
self.__by_parent = None
2086
self._parent_ids = []
2087
self._all_children_cache = {}
2088
self._path2trans_id_cache = {}
2089
self._final_name_cache = {}
2090
self._iter_changes_cache = dict((c.file_id, c) for c in
2091
self._transform.iter_changes())
2093
def supports_tree_reference(self):
2094
# TODO(jelmer): Support tree references in _PreviewTree.
2095
# return self._transform._tree.supports_tree_reference()
2098
def _content_change(self, file_id):
2099
"""Return True if the content of this file changed"""
2100
changes = self._iter_changes_cache.get(file_id)
2101
return (changes is not None and changes.changed_content)
2103
def _get_repository(self):
2104
repo = getattr(self._transform._tree, '_repository', None)
2106
repo = self._transform._tree.branch.repository
2109
def _iter_parent_trees(self):
2110
for revision_id in self.get_parent_ids():
2112
yield self.revision_tree(revision_id)
2113
except errors.NoSuchRevisionInTree:
2114
yield self._get_repository().revision_tree(revision_id)
2116
def _get_file_revision(self, path, file_id, vf, tree_revision):
2118
(file_id, t.get_file_revision(t.id2path(file_id)))
2119
for t in self._iter_parent_trees()]
2120
vf.add_lines((file_id, tree_revision), parent_keys,
2121
self.get_file_lines(path))
2122
repo = self._get_repository()
2123
base_vf = repo.texts
2124
if base_vf not in vf.fallback_versionedfiles:
2125
vf.fallback_versionedfiles.append(base_vf)
2126
return tree_revision
2128
def _stat_limbo_file(self, trans_id):
2129
name = self._transform._limbo_name(trans_id)
2130
return os.lstat(name)
2133
def _by_parent(self):
2134
if self.__by_parent is None:
2135
self.__by_parent = self._transform.by_parent()
2136
return self.__by_parent
2138
def _comparison_data(self, entry, path):
2139
kind, size, executable, link_or_sha1 = self.path_content_summary(path)
2140
if kind == 'missing':
2144
executable = self.is_executable(path)
2145
return kind, executable, None
2147
def is_locked(self):
2150
def lock_read(self):
2151
# Perhaps in theory, this should lock the TreeTransform?
2152
return lock.LogicalLockResult(self.unlock)
2158
def root_inventory(self):
2159
"""This Tree does not use inventory as its backing data."""
2160
raise NotImplementedError(_PreviewTree.root_inventory)
2162
def all_file_ids(self):
2163
tree_ids = set(self._transform._tree.all_file_ids())
2164
tree_ids.difference_update(self._transform.tree_file_id(t)
2165
for t in self._transform._removed_id)
2166
tree_ids.update(viewvalues(self._transform._new_id))
2169
def all_versioned_paths(self):
2170
tree_paths = set(self._transform._tree.all_versioned_paths())
2172
tree_paths.difference_update(
2173
self._transform.trans_id_tree_path(t)
2174
for t in self._transform._removed_id)
2177
self._final_paths._determine_path(t)
2178
for t in self._transform._new_id)
2182
def _path2trans_id(self, path):
2183
# We must not use None here, because that is a valid value to store.
2184
trans_id = self._path2trans_id_cache.get(path, object)
2185
if trans_id is not object:
2187
segments = splitpath(path)
2188
cur_parent = self._transform.root
2189
for cur_segment in segments:
2190
for child in self._all_children(cur_parent):
2191
final_name = self._final_name_cache.get(child)
2192
if final_name is None:
2193
final_name = self._transform.final_name(child)
2194
self._final_name_cache[child] = final_name
2195
if final_name == cur_segment:
2199
self._path2trans_id_cache[path] = None
2201
self._path2trans_id_cache[path] = cur_parent
2204
def path2id(self, path):
2205
if isinstance(path, list):
2208
path = osutils.pathjoin(*path)
2209
return self._transform.final_file_id(self._path2trans_id(path))
2211
def id2path(self, file_id, recurse='down'):
2212
trans_id = self._transform.trans_id_file_id(file_id)
2214
return self._final_paths._determine_path(trans_id)
2216
raise errors.NoSuchId(self, file_id)
2218
def _all_children(self, trans_id):
2219
children = self._all_children_cache.get(trans_id)
2220
if children is not None:
2222
children = set(self._transform.iter_tree_children(trans_id))
2223
# children in the _new_parent set are provided by _by_parent.
2224
children.difference_update(self._transform._new_parent)
2225
children.update(self._by_parent.get(trans_id, []))
2226
self._all_children_cache[trans_id] = children
2230
possible_extras = set(self._transform.trans_id_tree_path(p) for p
2231
in self._transform._tree.extras())
2232
possible_extras.update(self._transform._new_contents)
2233
possible_extras.update(self._transform._removed_id)
2234
for trans_id in possible_extras:
2235
if not self._transform.final_is_versioned(trans_id):
2236
yield self._final_paths._determine_path(trans_id)
2238
def _make_inv_entries(self, ordered_entries, specific_files=None):
2239
for trans_id, parent_file_id in ordered_entries:
2240
file_id = self._transform.final_file_id(trans_id)
2243
if (specific_files is not None
2244
and self._final_paths.get_path(trans_id) not in specific_files):
2246
kind = self._transform.final_kind(trans_id)
2248
kind = self._transform._tree.stored_kind(
2249
self._transform._tree.id2path(file_id))
2250
new_entry = inventory.make_entry(
2252
self._transform.final_name(trans_id),
2253
parent_file_id, file_id)
2254
yield new_entry, trans_id
2256
def _list_files_by_dir(self):
2257
todo = [ROOT_PARENT]
2259
while len(todo) > 0:
2261
parent_file_id = self._transform.final_file_id(parent)
2262
children = list(self._all_children(parent))
2263
paths = dict(zip(children, self._final_paths.get_paths(children)))
2264
children.sort(key=paths.get)
2265
todo.extend(reversed(children))
2266
for trans_id in children:
2267
ordered_ids.append((trans_id, parent_file_id))
2270
def iter_child_entries(self, path):
2271
trans_id = self._path2trans_id(path)
2272
if trans_id is None:
2273
raise errors.NoSuchFile(path)
2274
todo = [(child_trans_id, trans_id) for child_trans_id in
2275
self._all_children(trans_id)]
2276
for entry, trans_id in self._make_inv_entries(todo):
2279
def iter_entries_by_dir(self, specific_files=None, recurse_nested=False):
2281
raise NotImplementedError(
2282
'follow tree references not yet supported')
2284
# This may not be a maximally efficient implementation, but it is
2285
# reasonably straightforward. An implementation that grafts the
2286
# TreeTransform changes onto the tree's iter_entries_by_dir results
2287
# might be more efficient, but requires tricky inferences about stack
2289
ordered_ids = self._list_files_by_dir()
2290
for entry, trans_id in self._make_inv_entries(ordered_ids,
2292
yield self._final_paths.get_path(trans_id), entry
2294
def _iter_entries_for_dir(self, dir_path):
2295
"""Return path, entry for items in a directory without recursing down."""
2297
dir_trans_id = self._path2trans_id(dir_path)
2298
dir_id = self._transform.final_file_id(dir_trans_id)
2299
for child_trans_id in self._all_children(dir_trans_id):
2300
ordered_ids.append((child_trans_id, dir_id))
2302
for entry, trans_id in self._make_inv_entries(ordered_ids):
2303
path_entries.append((self._final_paths.get_path(trans_id), entry))
2307
def list_files(self, include_root=False, from_dir=None, recursive=True,
2308
recurse_nested=False):
2309
"""See WorkingTree.list_files."""
2311
raise NotImplementedError(
2312
'follow tree references not yet supported')
2314
# XXX This should behave like WorkingTree.list_files, but is really
2315
# more like RevisionTree.list_files.
2321
prefix = from_dir + '/'
2322
entries = self.iter_entries_by_dir()
2323
for path, entry in entries:
2324
if entry.name == '' and not include_root:
2327
if not path.startswith(prefix):
2329
path = path[len(prefix):]
2330
yield path, 'V', entry.kind, entry
2332
if from_dir is None and include_root is True:
2333
root_entry = inventory.make_entry(
2334
'directory', '', ROOT_PARENT, self.path2id(''))
2335
yield '', 'V', 'directory', root_entry
2336
entries = self._iter_entries_for_dir(from_dir or '')
2337
for path, entry in entries:
2338
yield path, 'V', entry.kind, entry
2340
def kind(self, path):
2341
trans_id = self._path2trans_id(path)
2342
if trans_id is None:
2343
raise errors.NoSuchFile(path)
2344
return self._transform.final_kind(trans_id)
2346
def stored_kind(self, path):
2347
trans_id = self._path2trans_id(path)
2348
if trans_id is None:
2349
raise errors.NoSuchFile(path)
2351
return self._transform._new_contents[trans_id]
2353
return self._transform._tree.stored_kind(path)
2355
def get_file_mtime(self, path):
2356
"""See Tree.get_file_mtime"""
2357
file_id = self.path2id(path)
2359
raise errors.NoSuchFile(path)
2360
if not self._content_change(file_id):
2361
return self._transform._tree.get_file_mtime(
2362
self._transform._tree.id2path(file_id))
2363
trans_id = self._path2trans_id(path)
2364
return self._stat_limbo_file(trans_id).st_mtime
2366
def get_file_size(self, path):
2367
"""See Tree.get_file_size"""
2368
trans_id = self._path2trans_id(path)
2369
if trans_id is None:
2370
raise errors.NoSuchFile(path)
2371
kind = self._transform.final_kind(trans_id)
2374
if trans_id in self._transform._new_contents:
2375
return self._stat_limbo_file(trans_id).st_size
2376
if self.kind(path) == 'file':
2377
return self._transform._tree.get_file_size(path)
2381
def get_file_verifier(self, path, stat_value=None):
2382
trans_id = self._path2trans_id(path)
2383
if trans_id is None:
2384
raise errors.NoSuchFile(path)
2385
kind = self._transform._new_contents.get(trans_id)
2387
return self._transform._tree.get_file_verifier(path)
2389
with self.get_file(path) as fileobj:
2390
return ("SHA1", sha_file(fileobj))
2392
def get_file_sha1(self, path, stat_value=None):
2393
trans_id = self._path2trans_id(path)
2394
if trans_id is None:
2395
raise errors.NoSuchFile(path)
2396
kind = self._transform._new_contents.get(trans_id)
2398
return self._transform._tree.get_file_sha1(path)
2400
with self.get_file(path) as fileobj:
2401
return sha_file(fileobj)
2403
def get_reference_revision(self, path):
2404
trans_id = self._path2trans_id(path)
2405
if trans_id is None:
2406
raise errors.NoSuchFile(path)
2407
reference_revision = self._transform._new_reference_revision.get(trans_id)
2408
if reference_revision is None:
2409
return self._transform._tree.get_reference_revision(path)
2410
return reference_revision
2412
def is_executable(self, path):
2413
trans_id = self._path2trans_id(path)
2414
if trans_id is None:
2417
return self._transform._new_executability[trans_id]
2420
return self._transform._tree.is_executable(path)
2421
except OSError as e:
2422
if e.errno == errno.ENOENT:
2425
except errors.NoSuchFile:
2428
def has_filename(self, path):
2429
trans_id = self._path2trans_id(path)
2430
if trans_id in self._transform._new_contents:
2432
elif trans_id in self._transform._removed_contents:
2435
return self._transform._tree.has_filename(path)
2437
def path_content_summary(self, path):
2438
trans_id = self._path2trans_id(path)
2439
tt = self._transform
2440
tree_path = tt._tree_id_paths.get(trans_id)
2441
kind = tt._new_contents.get(trans_id)
2443
if tree_path is None or trans_id in tt._removed_contents:
2444
return 'missing', None, None, None
2445
summary = tt._tree.path_content_summary(tree_path)
2446
kind, size, executable, link_or_sha1 = summary
2449
limbo_name = tt._limbo_name(trans_id)
2450
if trans_id in tt._new_reference_revision:
2451
kind = 'tree-reference'
2453
statval = os.lstat(limbo_name)
2454
size = statval.st_size
2455
if not tt._limbo_supports_executable():
2458
executable = statval.st_mode & S_IEXEC
2462
if kind == 'symlink':
2463
link_or_sha1 = os.readlink(limbo_name)
2464
if not isinstance(link_or_sha1, text_type):
2465
link_or_sha1 = link_or_sha1.decode(osutils._fs_enc)
2466
executable = tt._new_executability.get(trans_id, executable)
2467
return kind, size, executable, link_or_sha1
2469
def iter_changes(self, from_tree, include_unchanged=False,
2470
specific_files=None, pb=None, extra_trees=None,
2471
require_versioned=True, want_unversioned=False):
2472
"""See InterTree.iter_changes.
2474
This has a fast path that is only used when the from_tree matches
2475
the transform tree, and no fancy options are supplied.
2477
if (from_tree is not self._transform._tree or include_unchanged
2478
or specific_files or want_unversioned):
2479
from .bzr.inventorytree import InterInventoryTree
2480
return InterInventoryTree(from_tree, self).iter_changes(
2481
include_unchanged=include_unchanged,
2482
specific_files=specific_files,
2484
extra_trees=extra_trees,
2485
require_versioned=require_versioned,
2486
want_unversioned=want_unversioned)
2487
if want_unversioned:
2488
raise ValueError('want_unversioned is not supported')
2489
return self._transform.iter_changes()
2491
def get_file(self, path):
2492
"""See Tree.get_file"""
2493
file_id = self.path2id(path)
2494
if not self._content_change(file_id):
2495
return self._transform._tree.get_file(path)
2496
trans_id = self._path2trans_id(path)
2497
name = self._transform._limbo_name(trans_id)
2498
return open(name, 'rb')
2500
def get_file_with_stat(self, path):
2501
return self.get_file(path), None
2503
def annotate_iter(self, path,
2504
default_revision=_mod_revision.CURRENT_REVISION):
2505
file_id = self.path2id(path)
2506
changes = self._iter_changes_cache.get(file_id)
2510
changed_content, versioned, kind = (
2511
changes.changed_content, changes.versioned, changes.kind)
2514
get_old = (kind[0] == 'file' and versioned[0])
2516
old_annotation = self._transform._tree.annotate_iter(
2517
path, default_revision=default_revision)
2521
return old_annotation
2522
if not changed_content:
2523
return old_annotation
2524
# TODO: This is doing something similar to what WT.annotate_iter is
2525
# doing, however it fails slightly because it doesn't know what
2526
# the *other* revision_id is, so it doesn't know how to give the
2527
# other as the origin for some lines, they all get
2528
# 'default_revision'
2529
# It would be nice to be able to use the new Annotator based
2530
# approach, as well.
2531
return annotate.reannotate([old_annotation],
2532
self.get_file(path).readlines(),
2535
def get_symlink_target(self, path):
2536
"""See Tree.get_symlink_target"""
2537
file_id = self.path2id(path)
2538
if not self._content_change(file_id):
2539
return self._transform._tree.get_symlink_target(path)
2540
trans_id = self._path2trans_id(path)
2541
name = self._transform._limbo_name(trans_id)
2542
return osutils.readlink(name)
2544
def walkdirs(self, prefix=''):
2545
pending = [self._transform.root]
2546
while len(pending) > 0:
2547
parent_id = pending.pop()
2550
prefix = prefix.rstrip('/')
2551
parent_path = self._final_paths.get_path(parent_id)
2552
parent_file_id = self._transform.final_file_id(parent_id)
2553
for child_id in self._all_children(parent_id):
2554
path_from_root = self._final_paths.get_path(child_id)
2555
basename = self._transform.final_name(child_id)
2556
file_id = self._transform.final_file_id(child_id)
2557
kind = self._transform.final_kind(child_id)
2558
if kind is not None:
2559
versioned_kind = kind
2562
versioned_kind = self._transform._tree.stored_kind(
2563
self._transform._tree.id2path(file_id))
2564
if versioned_kind == 'directory':
2565
subdirs.append(child_id)
2566
children.append((path_from_root, basename, kind, None,
2567
file_id, versioned_kind))
2569
if parent_path.startswith(prefix):
2570
yield (parent_path, parent_file_id), children
2571
pending.extend(sorted(subdirs, key=self._final_paths.get_path,
2574
def get_parent_ids(self):
2575
return self._parent_ids
2577
def set_parent_ids(self, parent_ids):
2578
self._parent_ids = parent_ids
2580
def get_revision_tree(self, revision_id):
2581
return self._transform._tree.get_revision_tree(revision_id)
1784
raise NotImplementedError(self.apply)
2584
1787
def joinpath(parent, child):