470
485
incorrectly attributed to CURRENT_REVISION (but after committing, the
471
486
attribution will be correct).
488
file_id = osutils.safe_file_id(file_id)
473
489
basis = self.basis_tree()
474
changes = self._iter_changes(basis, True, [file_id]).next()
475
changed_content, kind = changes[2], changes[6]
476
if not changed_content:
477
return basis.annotate_iter(file_id)
481
if kind[0] != 'file':
484
old_lines = list(basis.annotate_iter(file_id))
486
for tree in self.branch.repository.revision_trees(
487
self.get_parent_ids()[1:]):
488
if file_id not in tree:
490
old.append(list(tree.annotate_iter(file_id)))
491
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(),
494
515
def get_parent_ids(self):
495
516
"""See Tree.get_parent_ids.
574
600
__contains__ = has_id
576
602
def get_file_size(self, file_id):
603
file_id = osutils.safe_file_id(file_id)
577
604
return os.path.getsize(self.id2abspath(file_id))
580
607
def get_file_sha1(self, file_id, path=None, stat_value=None):
608
file_id = osutils.safe_file_id(file_id)
582
610
path = self._inventory.id2path(file_id)
583
611
return self._hashcache.get_sha1(path, stat_value)
585
613
def get_file_mtime(self, file_id, path=None):
614
file_id = osutils.safe_file_id(file_id)
587
path = self._inventory.id2path(file_id)
616
path = self.inventory.id2path(file_id)
588
617
return os.lstat(self.abspath(path)).st_mtime
590
619
if not supports_executable():
591
620
def is_executable(self, file_id, path=None):
621
file_id = osutils.safe_file_id(file_id)
592
622
return self._inventory[file_id].executable
594
624
def is_executable(self, file_id, path=None):
596
path = self._inventory.id2path(file_id)
626
file_id = osutils.safe_file_id(file_id)
627
path = self.id2path(file_id)
597
628
mode = os.lstat(self.abspath(path)).st_mode
598
629
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
631
@needs_tree_write_lock
601
632
def _add(self, files, ids, kinds):
602
633
"""See MutableTree._add."""
603
634
# TODO: Re-adding a file that is removed in the working copy
850
896
def get_symlink_target(self, file_id):
897
file_id = osutils.safe_file_id(file_id)
851
898
return os.readlink(self.id2abspath(file_id))
853
def file_class(self, filename):
854
if self.path2id(filename):
856
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.
949
segments = osutils.splitpath(path)
950
transport = self.branch.bzrdir.root_transport
951
for name in segments:
952
transport = transport.clone(name)
955
except errors.FileExists:
959
sub_path = self.id2path(file_id)
960
branch_transport = mkdirs(sub_path)
962
format = bzrdir.format_registry.make_bzrdir('dirstate-with-subtree')
964
branch_transport.mkdir('.')
965
except errors.FileExists:
967
branch_bzrdir = format.initialize_on_transport(branch_transport)
969
repo = branch_bzrdir.find_repository()
970
except errors.NoRepositoryPresent:
971
repo = branch_bzrdir.create_repository()
972
assert repo.supports_rich_root()
974
if not repo.supports_rich_root():
975
raise errors.RootNotRich()
976
new_branch = branch_bzrdir.create_branch()
977
new_branch.pull(self.branch)
978
for parent_id in self.get_parent_ids():
979
new_branch.fetch(self.branch, parent_id)
980
tree_transport = self.bzrdir.root_transport.clone(sub_path)
981
if tree_transport.base != branch_transport.base:
982
tree_bzrdir = format.initialize_on_transport(tree_transport)
983
branch.BranchReferenceFormat().initialize(tree_bzrdir, new_branch)
985
tree_bzrdir = branch_bzrdir
986
wt = tree_bzrdir.create_workingtree(NULL_REVISION)
987
wt.set_parent_ids(self.get_parent_ids())
988
my_inv = self.inventory
989
child_inv = Inventory(root_id=None)
990
new_root = my_inv[file_id]
991
my_inv.remove_recursive_id(file_id)
992
new_root.parent_id = None
993
child_inv.add(new_root)
994
self._write_inventory(my_inv)
995
wt._write_inventory(child_inv)
998
def _serialize(self, inventory, out_file):
999
xml5.serializer_v5.write_inventory(self._inventory, out_file)
1001
def _deserialize(selt, in_file):
1002
return xml5.serializer_v5.read_inventory(in_file)
861
1004
def flush(self):
862
1005
"""Write the in memory inventory to disk."""
1902
2124
file_id=self.path2id(conflicted)))
1903
2125
return conflicts
2127
def walkdirs(self, prefix=""):
2128
"""Walk the directories of this tree.
2130
This API returns a generator, which is only valid during the current
2131
tree transaction - within a single lock_read or lock_write duration.
2133
If the tree is not locked, it may cause an error to be raised, depending
2134
on the tree implementation.
2136
disk_top = self.abspath(prefix)
2137
if disk_top.endswith('/'):
2138
disk_top = disk_top[:-1]
2139
top_strip_len = len(disk_top) + 1
2140
inventory_iterator = self._walkdirs(prefix)
2141
disk_iterator = osutils.walkdirs(disk_top, prefix)
2143
current_disk = disk_iterator.next()
2144
disk_finished = False
2146
if e.errno != errno.ENOENT:
2149
disk_finished = True
2151
current_inv = inventory_iterator.next()
2152
inv_finished = False
2153
except StopIteration:
2156
while not inv_finished or not disk_finished:
2157
if not disk_finished:
2158
# strip out .bzr dirs
2159
if current_disk[0][1][top_strip_len:] == '':
2160
# osutils.walkdirs can be made nicer -
2161
# yield the path-from-prefix rather than the pathjoined
2163
bzrdir_loc = bisect_left(current_disk[1], ('.bzr', '.bzr'))
2164
if current_disk[1][bzrdir_loc][0] == '.bzr':
2165
# we dont yield the contents of, or, .bzr itself.
2166
del current_disk[1][bzrdir_loc]
2168
# everything is unknown
2171
# everything is missing
2174
direction = cmp(current_inv[0][0], current_disk[0][0])
2176
# disk is before inventory - unknown
2177
dirblock = [(relpath, basename, kind, stat, None, None) for
2178
relpath, basename, kind, stat, top_path in current_disk[1]]
2179
yield (current_disk[0][0], None), dirblock
2181
current_disk = disk_iterator.next()
2182
except StopIteration:
2183
disk_finished = True
2185
# inventory is before disk - missing.
2186
dirblock = [(relpath, basename, 'unknown', None, fileid, kind)
2187
for relpath, basename, dkind, stat, fileid, kind in
2189
yield (current_inv[0][0], current_inv[0][1]), dirblock
2191
current_inv = inventory_iterator.next()
2192
except StopIteration:
2195
# versioned present directory
2196
# merge the inventory and disk data together
2198
for relpath, subiterator in itertools.groupby(sorted(
2199
current_inv[1] + current_disk[1], key=operator.itemgetter(0)), operator.itemgetter(1)):
2200
path_elements = list(subiterator)
2201
if len(path_elements) == 2:
2202
inv_row, disk_row = path_elements
2203
# versioned, present file
2204
dirblock.append((inv_row[0],
2205
inv_row[1], disk_row[2],
2206
disk_row[3], inv_row[4],
2208
elif len(path_elements[0]) == 5:
2210
dirblock.append((path_elements[0][0],
2211
path_elements[0][1], path_elements[0][2],
2212
path_elements[0][3], None, None))
2213
elif len(path_elements[0]) == 6:
2214
# versioned, absent file.
2215
dirblock.append((path_elements[0][0],
2216
path_elements[0][1], 'unknown', None,
2217
path_elements[0][4], path_elements[0][5]))
2219
raise NotImplementedError('unreachable code')
2220
yield current_inv[0], dirblock
2222
current_inv = inventory_iterator.next()
2223
except StopIteration:
2226
current_disk = disk_iterator.next()
2227
except StopIteration:
2228
disk_finished = True
2230
def _walkdirs(self, prefix=""):
2231
_directory = 'directory'
2232
# get the root in the inventory
2233
inv = self.inventory
2234
top_id = inv.path2id(prefix)
2238
pending = [(prefix, '', _directory, None, top_id, None)]
2241
currentdir = pending.pop()
2242
# 0 - relpath, 1- basename, 2- kind, 3- stat, 4-id, 5-kind
2243
top_id = currentdir[4]
2245
relroot = currentdir[0] + '/'
2248
# FIXME: stash the node in pending
2250
for name, child in entry.sorted_children():
2251
dirblock.append((relroot + name, name, child.kind, None,
2252
child.file_id, child.kind
2254
yield (currentdir[0], entry.file_id), dirblock
2255
# push the user specified dirs from dirblock
2256
for dir in reversed(dirblock):
2257
if dir[2] == _directory:
2260
@needs_tree_write_lock
2261
def auto_resolve(self):
2262
"""Automatically resolve text conflicts according to contents.
2264
Only text conflicts are auto_resolvable. Files with no conflict markers
2265
are considered 'resolved', because bzr always puts conflict markers
2266
into files that have text conflicts. The corresponding .THIS .BASE and
2267
.OTHER files are deleted, as per 'resolve'.
2268
:return: a tuple of ConflictLists: (un_resolved, resolved).
2270
un_resolved = _mod_conflicts.ConflictList()
2271
resolved = _mod_conflicts.ConflictList()
2272
conflict_re = re.compile('^(<{7}|={7}|>{7})')
2273
for conflict in self.conflicts():
2274
if (conflict.typestring != 'text conflict' or
2275
self.kind(conflict.file_id) != 'file'):
2276
un_resolved.append(conflict)
2278
my_file = open(self.id2abspath(conflict.file_id), 'rb')
2280
for line in my_file:
2281
if conflict_re.search(line):
2282
un_resolved.append(conflict)
2285
resolved.append(conflict)
2288
resolved.remove_files(self)
2289
self.set_conflicts(un_resolved)
2290
return un_resolved, resolved
1906
2293
class WorkingTree2(WorkingTree):
1907
2294
"""This is the Format 2 working tree.