278
def _get_trees_to_diff(path_list, revision_specs, old_url, new_url):
279
"""Get the trees and specific files to diff given a list of paths.
281
This method works out the trees to be diff'ed and the files of
282
interest within those trees.
285
the list of arguments passed to the diff command
286
:param revision_specs:
287
Zero, one or two RevisionSpecs from the diff command line,
288
saying what revisions to compare.
290
The url of the old branch or tree. If None, the tree to use is
291
taken from the first path, if any, or the current working tree.
293
The url of the new branch or tree. If None, the tree to use is
294
taken from the first path, if any, or the current working tree.
296
a tuple of (old_tree, new_tree, specific_files, extra_trees) where
297
extra_trees is a sequence of additional trees to search in for
294
@deprecated_function(deprecated_in((2, 2, 0)))
295
def get_trees_and_branches_to_diff(path_list, revision_specs, old_url, new_url,
297
"""Get the trees and specific files to diff given a list of paths.
299
This method works out the trees to be diff'ed and the files of
300
interest within those trees.
303
the list of arguments passed to the diff command
304
:param revision_specs:
305
Zero, one or two RevisionSpecs from the diff command line,
306
saying what revisions to compare.
308
The url of the old branch or tree. If None, the tree to use is
309
taken from the first path, if any, or the current working tree.
311
The url of the new branch or tree. If None, the tree to use is
312
taken from the first path, if any, or the current working tree.
314
if True and a view is set, apply the view or check that the paths
317
a tuple of (old_tree, new_tree, old_branch, new_branch,
318
specific_files, extra_trees) where extra_trees is a sequence of
319
additional trees to search in for file-ids. The trees and branches
322
op = cleanup.OperationWithCleanups(get_trees_and_branches_to_diff_locked)
323
return op.run_simple(path_list, revision_specs, old_url, new_url,
324
op.add_cleanup, apply_view=apply_view)
327
def get_trees_and_branches_to_diff_locked(
328
path_list, revision_specs, old_url, new_url, add_cleanup, apply_view=True):
329
"""Get the trees and specific files to diff given a list of paths.
331
This method works out the trees to be diff'ed and the files of
332
interest within those trees.
335
the list of arguments passed to the diff command
336
:param revision_specs:
337
Zero, one or two RevisionSpecs from the diff command line,
338
saying what revisions to compare.
340
The url of the old branch or tree. If None, the tree to use is
341
taken from the first path, if any, or the current working tree.
343
The url of the new branch or tree. If None, the tree to use is
344
taken from the first path, if any, or the current working tree.
346
a callable like Command.add_cleanup. get_trees_and_branches_to_diff
347
will register cleanups that must be run to unlock the trees, etc.
349
if True and a view is set, apply the view or check that the paths
352
a tuple of (old_tree, new_tree, old_branch, new_branch,
353
specific_files, extra_trees) where extra_trees is a sequence of
354
additional trees to search in for file-ids. The trees and branches
355
will be read-locked until the cleanups registered via the add_cleanup
300
358
# Get the old and new revision specs
301
359
old_revision_spec = None
324
382
default_location = path_list[0]
325
383
other_paths = path_list[1:]
385
def lock_tree_or_branch(wt, br):
388
add_cleanup(wt.unlock)
391
add_cleanup(br.unlock)
327
393
# Get the old location
328
394
specific_files = []
329
395
if old_url is None:
330
396
old_url = default_location
331
397
working_tree, branch, relpath = \
332
398
bzrdir.BzrDir.open_containing_tree_or_branch(old_url)
399
lock_tree_or_branch(working_tree, branch)
333
400
if consider_relpath and relpath != '':
401
if working_tree is not None and apply_view:
402
views.check_path_in_view(working_tree, relpath)
334
403
specific_files.append(relpath)
335
404
old_tree = _get_tree_to_diff(old_revision_spec, working_tree, branch)
337
407
# Get the new location
338
408
if new_url is None:
340
410
if new_url != old_url:
341
411
working_tree, branch, relpath = \
342
412
bzrdir.BzrDir.open_containing_tree_or_branch(new_url)
413
lock_tree_or_branch(working_tree, branch)
343
414
if consider_relpath and relpath != '':
415
if working_tree is not None and apply_view:
416
views.check_path_in_view(working_tree, relpath)
344
417
specific_files.append(relpath)
345
418
new_tree = _get_tree_to_diff(new_revision_spec, working_tree, branch,
346
419
basis_is_default=working_tree is None)
348
422
# Get the specific files (all files is None, no files is [])
349
423
if make_paths_wt_relative and working_tree is not None:
350
other_paths = _relative_paths_in_tree(working_tree, other_paths)
425
from bzrlib.builtins import safe_relpath_files
426
other_paths = safe_relpath_files(working_tree, other_paths,
427
apply_view=apply_view)
428
except errors.FileInWrongBranch:
429
raise errors.BzrCommandError("Files are in different branches")
351
430
specific_files.extend(other_paths)
352
431
if len(specific_files) == 0:
353
432
specific_files = None
433
if (working_tree is not None and working_tree.supports_views()
435
view_files = working_tree.views.lookup_view()
437
specific_files = view_files
438
view_str = views.view_display_str(view_files)
439
note("*** Ignoring files outside view. View is %s" % view_str)
355
441
# Get extra trees that ought to be searched for file-ids
356
442
extra_trees = None
357
443
if working_tree is not None and working_tree not in (old_tree, new_tree):
358
444
extra_trees = (working_tree,)
359
return old_tree, new_tree, specific_files, extra_trees
445
return old_tree, new_tree, old_branch, new_branch, specific_files, extra_trees
362
448
def _get_tree_to_diff(spec, tree=None, branch=None, basis_is_default=True):
373
459
return spec.as_tree(branch)
376
def _relative_paths_in_tree(tree, paths):
377
"""Get the relative paths within a working tree.
379
Each path may be either an absolute path or a path relative to the
380
current working directory.
383
for filename in paths:
385
result.append(tree.relpath(osutils.dereference_path(filename)))
386
except errors.PathNotChild:
387
raise errors.BzrCommandError("Files are in different branches")
391
462
def show_diff_trees(old_tree, new_tree, to_file, specific_files=None,
392
463
external_diff_options=None,
393
464
old_label='a/', new_label='b/',
394
465
extra_trees=None,
395
466
path_encoding='utf8',
397
469
"""Show in text form the changes from one tree to another.
403
Include only changes to these files - None for all changes.
405
external_diff_options
406
If set, use an external GNU diff and pass these options.
409
If set, more Trees to use for looking up file ids
412
If set, the path will be encoded as specified, otherwise is supposed
471
:param to_file: The output stream.
472
:param specific_files:Include only changes to these files - None for all
474
:param external_diff_options: If set, use an external GNU diff and pass
476
:param extra_trees: If set, more Trees to use for looking up file ids
477
:param path_encoding: If set, the path will be encoded as specified,
478
otherwise is supposed to be utf8
479
:param format_cls: Formatter class (DiffTree subclass)
481
if format_cls is None:
482
format_cls = DiffTree
415
483
old_tree.lock_read()
417
485
if extra_trees is not None:
436
504
def _patch_header_date(tree, file_id, path):
437
505
"""Returns a timestamp suitable for use in a patch header."""
438
mtime = tree.get_file_mtime(file_id, path)
507
mtime = tree.get_file_mtime(file_id, path)
508
except errors.FileTimestampUnavailable:
439
510
return timestamp.format_patch_date(mtime)
442
def _raise_if_nonexistent(paths, old_tree, new_tree):
443
"""Complain if paths are not in either inventory or tree.
445
It's OK with the files exist in either tree's inventory, or
446
if they exist in the tree but are not versioned.
448
This can be used by operations such as bzr status that can accept
449
unknown or ignored files.
451
mutter("check paths: %r", paths)
454
s = old_tree.filter_unversioned_files(paths)
455
s = new_tree.filter_unversioned_files(s)
456
s = [path for path in s if not new_tree.has_filename(path)]
458
raise errors.PathsDoNotExist(sorted(s))
461
@deprecated_function(one_three)
462
def get_prop_change(meta_modified):
464
return " (properties changed)"
468
513
def get_executable_change(old_is_x, new_is_x):
469
514
descr = { True:"+x", False:"-x", None:"??" }
470
515
if old_is_x != new_is_x:
645
690
return self.CANNOT_DIFF
646
691
from_label = '%s%s\t%s' % (self.old_label, old_path, old_date)
647
692
to_label = '%s%s\t%s' % (self.new_label, new_path, new_date)
648
return self.diff_text(from_file_id, to_file_id, from_label, to_label)
693
return self.diff_text(from_file_id, to_file_id, from_label, to_label,
650
def diff_text(self, from_file_id, to_file_id, from_label, to_label):
696
def diff_text(self, from_file_id, to_file_id, from_label, to_label,
697
from_path=None, to_path=None):
651
698
"""Diff the content of given files in two trees
653
700
:param from_file_id: The id of the file in the from tree. If None,
655
702
:param to_file_id: The id of the file in the to tree. This may refer
656
703
to a different file from from_file_id. If None,
657
704
the file is not present in the to tree.
705
:param from_path: The path in the from tree or None if unknown.
706
:param to_path: The path in the to tree or None if unknown.
659
def _get_text(tree, file_id):
708
def _get_text(tree, file_id, path):
660
709
if file_id is not None:
661
return tree.get_file(file_id).readlines()
710
return tree.get_file(file_id, path).readlines()
665
from_text = _get_text(self.old_tree, from_file_id)
666
to_text = _get_text(self.new_tree, to_file_id)
714
from_text = _get_text(self.old_tree, from_file_id, from_path)
715
to_text = _get_text(self.new_tree, to_file_id, to_path)
667
716
self.text_differ(from_label, from_text, to_label, to_text,
669
718
except errors.BinaryFile:
727
def _write_file(self, file_id, tree, prefix, relpath):
778
def _write_file(self, file_id, tree, prefix, relpath, force_temp=False,
780
if not force_temp and isinstance(tree, WorkingTree):
781
return tree.abspath(tree.id2path(file_id))
728
783
full_path = osutils.pathjoin(self._root, prefix, relpath)
729
if self._try_symlink_root(tree, prefix):
784
if not force_temp and self._try_symlink_root(tree, prefix):
731
786
parent_dir = osutils.dirname(full_path)
746
osutils.make_readonly(full_path)
747
mtime = tree.get_file_mtime(file_id)
748
os.utime(full_path, (mtime, mtime))
802
mtime = tree.get_file_mtime(file_id)
803
except errors.FileTimestampUnavailable:
806
os.utime(full_path, (mtime, mtime))
808
osutils.make_readonly(full_path)
751
def _prepare_files(self, file_id, old_path, new_path):
811
def _prepare_files(self, file_id, old_path, new_path, force_temp=False,
812
allow_write_new=False):
752
813
old_disk_path = self._write_file(file_id, self.old_tree, 'old',
814
old_path, force_temp)
754
815
new_disk_path = self._write_file(file_id, self.new_tree, 'new',
816
new_path, force_temp,
817
allow_write=allow_write_new)
756
818
return old_disk_path, new_disk_path
758
820
def finish(self):
759
osutils.rmtree(self._root)
822
osutils.rmtree(self._root)
824
if e.errno != errno.ENOENT:
825
mutter("The temporary directory \"%s\" was not "
826
"cleanly removed: %s." % (self._root, e))
761
828
def diff(self, file_id, old_path, new_path, old_kind, new_kind):
762
829
if (old_kind, new_kind) != ('file', 'file'):
763
830
return DiffPath.CANNOT_DIFF
764
self._prepare_files(file_id, old_path, new_path)
765
self._execute(osutils.pathjoin('old', old_path),
766
osutils.pathjoin('new', new_path))
831
(old_disk_path, new_disk_path) = self._prepare_files(
832
file_id, old_path, new_path)
833
self._execute(old_disk_path, new_disk_path)
835
def edit_file(self, file_id):
836
"""Use this tool to edit a file.
838
A temporary copy will be edited, and the new contents will be
841
:param file_id: The id of the file to edit.
842
:return: The new contents of the file.
844
old_path = self.old_tree.id2path(file_id)
845
new_path = self.new_tree.id2path(file_id)
846
new_abs_path = self._prepare_files(file_id, old_path, new_path,
847
allow_write_new=True,
849
command = self._get_command(osutils.pathjoin('old', old_path),
850
osutils.pathjoin('new', new_path))
851
subprocess.call(command, cwd=self._root)
852
new_file = open(new_abs_path, 'r')
854
return new_file.read()
769
859
class DiffTree(object):
928
1018
new_kind = self.new_tree.kind(file_id)
929
1019
except (errors.NoSuchId, errors.NoSuchFile):
1021
self._diff(file_id, old_path, new_path, old_kind, new_kind)
1024
def _diff(self, file_id, old_path, new_path, old_kind, new_kind):
932
1025
result = DiffPath._diff_many(self.differs, file_id, old_path,
933
1026
new_path, old_kind, new_kind)
934
1027
if result is DiffPath.CANNOT_DIFF: