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: