275
def _get_trees_to_diff(path_list, revision_specs, old_url, new_url,
277
"""Get the trees and specific files to diff given a list of paths.
279
This method works out the trees to be diff'ed and the files of
280
interest within those trees.
283
the list of arguments passed to the diff command
284
:param revision_specs:
285
Zero, one or two RevisionSpecs from the diff command line,
286
saying what revisions to compare.
288
The url of the old branch or tree. If None, the tree to use is
289
taken from the first path, if any, or the current working tree.
291
The url of the new branch or tree. If None, the tree to use is
292
taken from the first path, if any, or the current working tree.
294
if True and a view is set, apply the view or check that the paths
297
a tuple of (old_tree, new_tree, specific_files, extra_trees) where
298
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
301
358
# Get the old and new revision specs
302
359
old_revision_spec = None
325
382
default_location = path_list[0]
326
383
other_paths = path_list[1:]
385
def lock_tree_or_branch(wt, br):
388
add_cleanup(wt.unlock)
391
add_cleanup(br.unlock)
328
393
# Get the old location
329
394
specific_files = []
330
395
if old_url is None:
331
396
old_url = default_location
332
397
working_tree, branch, relpath = \
333
398
bzrdir.BzrDir.open_containing_tree_or_branch(old_url)
399
lock_tree_or_branch(working_tree, branch)
334
400
if consider_relpath and relpath != '':
335
401
if working_tree is not None and apply_view:
336
402
views.check_path_in_view(working_tree, relpath)
337
403
specific_files.append(relpath)
338
404
old_tree = _get_tree_to_diff(old_revision_spec, working_tree, branch)
340
407
# Get the new location
341
408
if new_url is None:
343
410
if new_url != old_url:
344
411
working_tree, branch, relpath = \
345
412
bzrdir.BzrDir.open_containing_tree_or_branch(new_url)
413
lock_tree_or_branch(working_tree, branch)
346
414
if consider_relpath and relpath != '':
347
415
if working_tree is not None and apply_view:
348
416
views.check_path_in_view(working_tree, relpath)
349
417
specific_files.append(relpath)
350
418
new_tree = _get_tree_to_diff(new_revision_spec, working_tree, branch,
351
419
basis_is_default=working_tree is None)
353
422
# Get the specific files (all files is None, no files is [])
354
423
if make_paths_wt_relative and working_tree is not None:
368
437
specific_files = view_files
369
438
view_str = views.view_display_str(view_files)
370
note("*** ignoring files outside view: %s" % view_str)
439
note("*** Ignoring files outside view. View is %s" % view_str)
372
441
# Get extra trees that ought to be searched for file-ids
373
442
extra_trees = None
374
443
if working_tree is not None and working_tree not in (old_tree, new_tree):
375
444
extra_trees = (working_tree,)
376
return old_tree, new_tree, specific_files, extra_trees
445
return old_tree, new_tree, old_branch, new_branch, specific_files, extra_trees
378
448
def _get_tree_to_diff(spec, tree=None, branch=None, basis_is_default=True):
379
449
if branch is None and tree is not None:
394
464
old_label='a/', new_label='b/',
395
465
extra_trees=None,
396
466
path_encoding='utf8',
398
469
"""Show in text form the changes from one tree to another.
404
Include only changes to these files - None for all changes.
406
external_diff_options
407
If set, use an external GNU diff and pass these options.
410
If set, more Trees to use for looking up file ids
413
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
416
483
old_tree.lock_read()
418
485
if extra_trees is not None:
620
690
return self.CANNOT_DIFF
621
691
from_label = '%s%s\t%s' % (self.old_label, old_path, old_date)
622
692
to_label = '%s%s\t%s' % (self.new_label, new_path, new_date)
623
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,
625
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):
626
698
"""Diff the content of given files in two trees
628
700
:param from_file_id: The id of the file in the from tree. If None,
630
702
:param to_file_id: The id of the file in the to tree. This may refer
631
703
to a different file from from_file_id. If None,
632
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.
634
def _get_text(tree, file_id):
708
def _get_text(tree, file_id, path):
635
709
if file_id is not None:
636
return tree.get_file(file_id).readlines()
710
return tree.get_file(file_id, path).readlines()
640
from_text = _get_text(self.old_tree, from_file_id)
641
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)
642
716
self.text_differ(from_label, from_text, to_label, to_text,
644
718
except errors.BinaryFile:
702
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))
703
783
full_path = osutils.pathjoin(self._root, prefix, relpath)
704
if self._try_symlink_root(tree, prefix):
784
if not force_temp and self._try_symlink_root(tree, prefix):
706
786
parent_dir = osutils.dirname(full_path)
721
osutils.make_readonly(full_path)
722
mtime = tree.get_file_mtime(file_id)
723
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)
726
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):
727
813
old_disk_path = self._write_file(file_id, self.old_tree, 'old',
814
old_path, force_temp)
729
815
new_disk_path = self._write_file(file_id, self.new_tree, 'new',
816
new_path, force_temp,
817
allow_write=allow_write_new)
731
818
return old_disk_path, new_disk_path
733
820
def finish(self):
734
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))
736
828
def diff(self, file_id, old_path, new_path, old_kind, new_kind):
737
829
if (old_kind, new_kind) != ('file', 'file'):
738
830
return DiffPath.CANNOT_DIFF
739
self._prepare_files(file_id, old_path, new_path)
740
self._execute(osutils.pathjoin('old', old_path),
741
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()
744
859
class DiffTree(object):
903
1018
new_kind = self.new_tree.kind(file_id)
904
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):
907
1025
result = DiffPath._diff_many(self.differs, file_id, old_path,
908
1026
new_path, old_kind, new_kind)
909
1027
if result is DiffPath.CANNOT_DIFF: