475
488
file_id = osutils.safe_file_id(file_id)
476
489
basis = self.basis_tree()
477
changes = self._iter_changes(basis, True, [file_id]).next()
478
changed_content, kind = changes[2], changes[6]
479
if not changed_content:
480
return basis.annotate_iter(file_id)
484
if kind[0] != 'file':
487
old_lines = list(basis.annotate_iter(file_id))
489
for tree in self.branch.repository.revision_trees(
490
self.get_parent_ids()[1:]):
491
if file_id not in tree:
493
old.append(list(tree.annotate_iter(file_id)))
494
return annotate.reannotate(old, self.get_file(file_id).readlines(),
492
changes = self._iter_changes(basis, True, [self.id2path(file_id)],
493
require_versioned=True).next()
494
changed_content, kind = changes[2], changes[6]
495
if not changed_content:
496
return basis.annotate_iter(file_id)
500
if kind[0] != 'file':
503
old_lines = list(basis.annotate_iter(file_id))
505
for tree in self.branch.repository.revision_trees(
506
self.get_parent_ids()[1:]):
507
if file_id not in tree:
509
old.append(list(tree.annotate_iter(file_id)))
510
return annotate.reannotate(old, self.get_file(file_id).readlines(),
497
515
def get_parent_ids(self):
498
516
"""See Tree.get_parent_ids.
876
896
def get_symlink_target(self, file_id):
897
file_id = osutils.safe_file_id(file_id)
877
898
return os.readlink(self.id2abspath(file_id))
879
def file_class(self, filename):
880
if self.path2id(filename):
882
elif self.is_ignored(filename):
901
def subsume(self, other_tree):
902
def add_children(inventory, entry):
903
for child_entry in entry.children.values():
904
inventory._byid[child_entry.file_id] = child_entry
905
if child_entry.kind == 'directory':
906
add_children(inventory, child_entry)
907
if other_tree.get_root_id() == self.get_root_id():
908
raise errors.BadSubsumeSource(self, other_tree,
909
'Trees have the same root')
911
other_tree_path = self.relpath(other_tree.basedir)
912
except errors.PathNotChild:
913
raise errors.BadSubsumeSource(self, other_tree,
914
'Tree is not contained by the other')
915
new_root_parent = self.path2id(osutils.dirname(other_tree_path))
916
if new_root_parent is None:
917
raise errors.BadSubsumeSource(self, other_tree,
918
'Parent directory is not versioned.')
919
# We need to ensure that the result of a fetch will have a
920
# versionedfile for the other_tree root, and only fetching into
921
# RepositoryKnit2 guarantees that.
922
if not self.branch.repository.supports_rich_root():
923
raise errors.SubsumeTargetNeedsUpgrade(other_tree)
924
other_tree.lock_tree_write()
926
new_parents = other_tree.get_parent_ids()
927
other_root = other_tree.inventory.root
928
other_root.parent_id = new_root_parent
929
other_root.name = osutils.basename(other_tree_path)
930
self.inventory.add(other_root)
931
add_children(self.inventory, other_root)
932
self._write_inventory(self.inventory)
933
# normally we don't want to fetch whole repositories, but i think
934
# here we really do want to consolidate the whole thing.
935
for parent_id in other_tree.get_parent_ids():
936
self.branch.fetch(other_tree.branch, parent_id)
937
self.add_parent_tree_id(parent_id)
940
other_tree.bzrdir.retire_bzrdir()
942
@needs_tree_write_lock
943
def extract(self, file_id, format=None):
944
"""Extract a subtree from this tree.
946
A new branch will be created, relative to the path for this tree.
950
segments = osutils.splitpath(path)
951
transport = self.branch.bzrdir.root_transport
952
for name in segments:
953
transport = transport.clone(name)
956
except errors.FileExists:
960
sub_path = self.id2path(file_id)
961
branch_transport = mkdirs(sub_path)
963
format = bzrdir.format_registry.make_bzrdir('dirstate-with-subtree')
965
branch_transport.mkdir('.')
966
except errors.FileExists:
968
branch_bzrdir = format.initialize_on_transport(branch_transport)
970
repo = branch_bzrdir.find_repository()
971
except errors.NoRepositoryPresent:
972
repo = branch_bzrdir.create_repository()
973
assert repo.supports_rich_root()
975
if not repo.supports_rich_root():
976
raise errors.RootNotRich()
977
new_branch = branch_bzrdir.create_branch()
978
new_branch.pull(self.branch)
979
for parent_id in self.get_parent_ids():
980
new_branch.fetch(self.branch, parent_id)
981
tree_transport = self.bzrdir.root_transport.clone(sub_path)
982
if tree_transport.base != branch_transport.base:
983
tree_bzrdir = format.initialize_on_transport(tree_transport)
984
branch.BranchReferenceFormat().initialize(tree_bzrdir, new_branch)
986
tree_bzrdir = branch_bzrdir
987
wt = tree_bzrdir.create_workingtree(NULL_REVISION)
988
wt.set_parent_ids(self.get_parent_ids())
989
my_inv = self.inventory
990
child_inv = Inventory(root_id=None)
991
new_root = my_inv[file_id]
992
my_inv.remove_recursive_id(file_id)
993
new_root.parent_id = None
994
child_inv.add(new_root)
995
self._write_inventory(my_inv)
996
wt._write_inventory(child_inv)
999
def _serialize(self, inventory, out_file):
1000
xml5.serializer_v5.write_inventory(self._inventory, out_file)
1002
def _deserialize(selt, in_file):
1003
return xml5.serializer_v5.read_inventory(in_file)
887
1005
def flush(self):
888
1006
"""Write the in memory inventory to disk."""
1940
2122
file_id=self.path2id(conflicted)))
1941
2123
return conflicts
2125
def walkdirs(self, prefix=""):
2126
"""Walk the directories of this tree.
2128
This API returns a generator, which is only valid during the current
2129
tree transaction - within a single lock_read or lock_write duration.
2131
If the tree is not locked, it may cause an error to be raised, depending
2132
on the tree implementation.
2134
disk_top = self.abspath(prefix)
2135
if disk_top.endswith('/'):
2136
disk_top = disk_top[:-1]
2137
top_strip_len = len(disk_top) + 1
2138
inventory_iterator = self._walkdirs(prefix)
2139
disk_iterator = osutils.walkdirs(disk_top, prefix)
2141
current_disk = disk_iterator.next()
2142
disk_finished = False
2144
if e.errno != errno.ENOENT:
2147
disk_finished = True
2149
current_inv = inventory_iterator.next()
2150
inv_finished = False
2151
except StopIteration:
2154
while not inv_finished or not disk_finished:
2155
if not disk_finished:
2156
# strip out .bzr dirs
2157
if current_disk[0][1][top_strip_len:] == '':
2158
# osutils.walkdirs can be made nicer -
2159
# yield the path-from-prefix rather than the pathjoined
2161
bzrdir_loc = bisect_left(current_disk[1], ('.bzr', '.bzr'))
2162
if current_disk[1][bzrdir_loc][0] == '.bzr':
2163
# we dont yield the contents of, or, .bzr itself.
2164
del current_disk[1][bzrdir_loc]
2166
# everything is unknown
2169
# everything is missing
2172
direction = cmp(current_inv[0][0], current_disk[0][0])
2174
# disk is before inventory - unknown
2175
dirblock = [(relpath, basename, kind, stat, None, None) for
2176
relpath, basename, kind, stat, top_path in current_disk[1]]
2177
yield (current_disk[0][0], None), dirblock
2179
current_disk = disk_iterator.next()
2180
except StopIteration:
2181
disk_finished = True
2183
# inventory is before disk - missing.
2184
dirblock = [(relpath, basename, 'unknown', None, fileid, kind)
2185
for relpath, basename, dkind, stat, fileid, kind in
2187
yield (current_inv[0][0], current_inv[0][1]), dirblock
2189
current_inv = inventory_iterator.next()
2190
except StopIteration:
2193
# versioned present directory
2194
# merge the inventory and disk data together
2196
for relpath, subiterator in itertools.groupby(sorted(
2197
current_inv[1] + current_disk[1], key=operator.itemgetter(0)), operator.itemgetter(1)):
2198
path_elements = list(subiterator)
2199
if len(path_elements) == 2:
2200
inv_row, disk_row = path_elements
2201
# versioned, present file
2202
dirblock.append((inv_row[0],
2203
inv_row[1], disk_row[2],
2204
disk_row[3], inv_row[4],
2206
elif len(path_elements[0]) == 5:
2208
dirblock.append((path_elements[0][0],
2209
path_elements[0][1], path_elements[0][2],
2210
path_elements[0][3], None, None))
2211
elif len(path_elements[0]) == 6:
2212
# versioned, absent file.
2213
dirblock.append((path_elements[0][0],
2214
path_elements[0][1], 'unknown', None,
2215
path_elements[0][4], path_elements[0][5]))
2217
raise NotImplementedError('unreachable code')
2218
yield current_inv[0], dirblock
2220
current_inv = inventory_iterator.next()
2221
except StopIteration:
2224
current_disk = disk_iterator.next()
2225
except StopIteration:
2226
disk_finished = True
2228
def _walkdirs(self, prefix=""):
2229
_directory = 'directory'
2230
# get the root in the inventory
2231
inv = self.inventory
2232
top_id = inv.path2id(prefix)
2236
pending = [(prefix, '', _directory, None, top_id, None)]
2239
currentdir = pending.pop()
2240
# 0 - relpath, 1- basename, 2- kind, 3- stat, 4-id, 5-kind
2241
top_id = currentdir[4]
2243
relroot = currentdir[0] + '/'
2246
# FIXME: stash the node in pending
2248
for name, child in entry.sorted_children():
2249
dirblock.append((relroot + name, name, child.kind, None,
2250
child.file_id, child.kind
2252
yield (currentdir[0], entry.file_id), dirblock
2253
# push the user specified dirs from dirblock
2254
for dir in reversed(dirblock):
2255
if dir[2] == _directory:
2258
@needs_tree_write_lock
2259
def auto_resolve(self):
2260
"""Automatically resolve text conflicts according to contents.
2262
Only text conflicts are auto_resolvable. Files with no conflict markers
2263
are considered 'resolved', because bzr always puts conflict markers
2264
into files that have text conflicts. The corresponding .THIS .BASE and
2265
.OTHER files are deleted, as per 'resolve'.
2266
:return: a tuple of ConflictLists: (un_resolved, resolved).
2268
un_resolved = _mod_conflicts.ConflictList()
2269
resolved = _mod_conflicts.ConflictList()
2270
conflict_re = re.compile('^(<{7}|={7}|>{7})')
2271
for conflict in self.conflicts():
2272
if (conflict.typestring != 'text conflict' or
2273
self.kind(conflict.file_id) != 'file'):
2274
un_resolved.append(conflict)
2276
my_file = open(self.id2abspath(conflict.file_id), 'rb')
2278
for line in my_file:
2279
if conflict_re.search(line):
2280
un_resolved.append(conflict)
2283
resolved.append(conflict)
2286
resolved.remove_files(self)
2287
self.set_conflicts(un_resolved)
2288
return un_resolved, resolved
1944
2291
class WorkingTree2(WorkingTree):
1945
2292
"""This is the Format 2 working tree.