346
349
extra_trees=extra_trees)
352
def _get_trees_to_diff(path_list, revision_specs, old_url, new_url):
353
"""Get the trees and specific files to diff given a list of paths.
355
This method works out the trees to be diff'ed and the files of
356
interest within those trees.
359
the list of arguments passed to the diff command
360
:param revision_specs:
361
Zero, one or two RevisionSpecs from the diff command line,
362
saying what revisions to compare.
364
The url of the old branch or tree. If None, the tree to use is
365
taken from the first path, if any, or the current working tree.
367
The url of the new branch or tree. If None, the tree to use is
368
taken from the first path, if any, or the current working tree.
370
a tuple of (old_tree, new_tree, specific_files, extra_trees) where
371
extra_trees is a sequence of additional trees to search in for
374
# Get the old and new revision specs
375
old_revision_spec = None
376
new_revision_spec = None
377
if revision_specs is not None:
378
if len(revision_specs) > 0:
379
old_revision_spec = revision_specs[0]
381
old_url = old_revision_spec.get_branch()
382
if len(revision_specs) > 1:
383
new_revision_spec = revision_specs[1]
385
new_url = new_revision_spec.get_branch()
388
make_paths_wt_relative = True
389
if path_list is None or len(path_list) == 0:
390
# If no path is given, assume the current directory
391
default_location = u'.'
392
elif old_url is not None and new_url is not None:
393
other_paths = path_list
394
make_paths_wt_relative = False
396
default_location = path_list[0]
397
other_paths = path_list[1:]
399
# Get the old location
402
old_url = default_location
403
working_tree, branch, relpath = \
404
bzrdir.BzrDir.open_containing_tree_or_branch(old_url)
406
specific_files.append(relpath)
407
old_tree = _get_tree_to_diff(old_revision_spec, working_tree, branch)
409
# Get the new location
411
new_url = default_location
412
if new_url != old_url:
413
working_tree, branch, relpath = \
414
bzrdir.BzrDir.open_containing_tree_or_branch(new_url)
416
specific_files.append(relpath)
417
new_tree = _get_tree_to_diff(new_revision_spec, working_tree, branch,
418
basis_is_default=working_tree is None)
420
# Get the specific files (all files is None, no files is [])
421
if make_paths_wt_relative and working_tree is not None:
422
other_paths = _relative_paths_in_tree(working_tree, other_paths)
423
specific_files.extend(other_paths)
424
if len(specific_files) == 0:
425
specific_files = None
427
# Get extra trees that ought to be searched for file-ids
429
if working_tree is not None and working_tree not in (old_tree, new_tree):
430
extra_trees = (working_tree,)
431
return old_tree, new_tree, specific_files, extra_trees
434
def _get_tree_to_diff(spec, tree=None, branch=None, basis_is_default=True):
435
if branch is None and tree is not None:
437
if spec is None or spec.spec is None:
440
return tree.basis_tree()
442
return branch.basis_tree()
445
revision = spec.in_store(branch)
446
revision_id = revision.rev_id
447
rev_branch = revision.branch
448
return rev_branch.repository.revision_tree(revision_id)
451
def _relative_paths_in_tree(tree, paths):
452
"""Get the relative paths within a working tree.
454
Each path may be either an absolute path or a path relative to the
455
current working directory.
458
for filename in paths:
460
result.append(tree.relpath(osutils.dereference_path(filename)))
461
except errors.PathNotChild:
462
raise errors.BzrCommandError("Files are in different branches")
349
466
def show_diff_trees(old_tree, new_tree, to_file, specific_files=None,
350
467
external_diff_options=None,
351
468
old_label='a/', new_label='b/',
694
814
# TODO: Generation of pseudo-diffs for added/deleted files could
695
815
# be usefully made into a much faster special case.
697
delta = self.new_tree.changes_from(self.old_tree,
698
specific_files=specific_files,
699
extra_trees=extra_trees, require_versioned=True)
816
iterator = self.new_tree._iter_changes(self.old_tree,
817
specific_files=specific_files,
818
extra_trees=extra_trees,
819
require_versioned=True)
702
for path, file_id, kind in delta.removed:
704
path_encoded = path.encode(self.path_encoding, "replace")
705
self.to_file.write("=== removed %s '%s'\n" % (kind, path_encoded))
706
self.diff(file_id, path, path)
708
for path, file_id, kind in delta.added:
710
path_encoded = path.encode(self.path_encoding, "replace")
711
self.to_file.write("=== added %s '%s'\n" % (kind, path_encoded))
712
self.diff(file_id, path, path)
713
for (old_path, new_path, file_id, kind,
714
text_modified, meta_modified) in delta.renamed:
716
prop_str = get_prop_change(meta_modified)
717
oldpath_encoded = old_path.encode(self.path_encoding, "replace")
718
newpath_encoded = new_path.encode(self.path_encoding, "replace")
719
self.to_file.write("=== renamed %s '%s' => '%s'%s\n" % (kind,
720
oldpath_encoded, newpath_encoded, prop_str))
722
self.diff(file_id, old_path, new_path)
723
for path, file_id, kind, text_modified, meta_modified in\
726
prop_str = get_prop_change(meta_modified)
727
path_encoded = path.encode(self.path_encoding, "replace")
728
self.to_file.write("=== modified %s '%s'%s\n" % (kind,
729
path_encoded, prop_str))
730
# The file may be in a different location in the old tree (because
731
# the containing dir was renamed, but the file itself was not)
733
old_path = self.old_tree.id2path(file_id)
734
self.diff(file_id, old_path, path)
821
def changes_key(change):
822
old_path, new_path = change[1]
827
def get_encoded_path(path):
829
return path.encode(self.path_encoding, "replace")
830
for (file_id, paths, changed_content, versioned, parent, name, kind,
831
executable) in sorted(iterator, key=changes_key):
832
if parent == (None, None):
834
oldpath, newpath = paths
835
oldpath_encoded = get_encoded_path(paths[0])
836
newpath_encoded = get_encoded_path(paths[1])
837
old_present = (kind[0] is not None and versioned[0])
838
new_present = (kind[1] is not None and versioned[1])
839
renamed = (parent[0], name[0]) != (parent[1], name[1])
840
prop_str = get_prop_change(executable[0] != executable[1])
841
if (old_present, new_present) == (True, False):
842
self.to_file.write("=== removed %s '%s'\n" %
843
(kind[0], oldpath_encoded))
845
elif (old_present, new_present) == (False, True):
846
self.to_file.write("=== added %s '%s'\n" %
847
(kind[1], newpath_encoded))
850
self.to_file.write("=== renamed %s '%s' => '%s'%s\n" %
851
(kind[0], oldpath_encoded, newpath_encoded, prop_str))
853
# if it was produced by _iter_changes, it must be
854
# modified *somehow*, either content or execute bit.
855
self.to_file.write("=== modified %s '%s'%s\n" % (kind[0],
856
newpath_encoded, prop_str))
858
self.diff(file_id, oldpath, newpath)
735
862
return has_changes
737
864
def diff(self, file_id, old_path, new_path):