/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/diff.py

  • Committer: Andrew Bennetts
  • Date: 2007-12-21 05:52:38 UTC
  • mfrom: (3137 +trunk)
  • mto: This revision was merged to the branch mainline in revision 3138.
  • Revision ID: andrew.bennetts@canonical.com-20071221055238-0wjubfut943uzovf
Merge from bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
27
27
import time
28
28
 
29
29
from bzrlib import (
 
30
    bzrdir,
30
31
    errors,
31
32
    osutils,
32
33
    patiencediff,
37
38
 
38
39
from bzrlib.symbol_versioning import (
39
40
        deprecated_function,
 
41
        one_zero,
40
42
        )
41
43
from bzrlib.trace import mutter, warning
42
44
 
270
272
                        new_abspath, e)
271
273
 
272
274
 
 
275
@deprecated_function(one_zero)
273
276
def diff_cmd_helper(tree, specific_files, external_diff_options, 
274
277
                    old_revision_spec=None, new_revision_spec=None,
275
278
                    revision_specs=None,
346
349
                           extra_trees=extra_trees)
347
350
 
348
351
 
 
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.
 
354
 
 
355
    This method works out the trees to be diff'ed and the files of
 
356
    interest within those trees.
 
357
 
 
358
    :param path_list:
 
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.
 
363
    :param old_url:
 
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.
 
366
    :param new_url:
 
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.
 
369
    :returns:
 
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
 
372
        file-ids.
 
373
    """
 
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]
 
380
            if old_url is None:
 
381
                old_url = old_revision_spec.get_branch()
 
382
        if len(revision_specs) > 1:
 
383
            new_revision_spec = revision_specs[1]
 
384
            if new_url is None:
 
385
                new_url = new_revision_spec.get_branch()
 
386
 
 
387
    other_paths = []
 
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
 
395
    else:
 
396
        default_location = path_list[0]
 
397
        other_paths = path_list[1:]
 
398
 
 
399
    # Get the old location
 
400
    specific_files = []
 
401
    if old_url is None:
 
402
        old_url = default_location
 
403
    working_tree, branch, relpath = \
 
404
        bzrdir.BzrDir.open_containing_tree_or_branch(old_url)
 
405
    if relpath != '':
 
406
        specific_files.append(relpath)
 
407
    old_tree = _get_tree_to_diff(old_revision_spec, working_tree, branch)
 
408
 
 
409
    # Get the new location
 
410
    if new_url is None:
 
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)
 
415
        if relpath != '':
 
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)
 
419
 
 
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
 
426
 
 
427
    # Get extra trees that ought to be searched for file-ids
 
428
    extra_trees = None
 
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
 
432
 
 
433
 
 
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:
 
436
        branch = tree.branch
 
437
    if spec is None or spec.spec is None:
 
438
        if basis_is_default:
 
439
            if tree is not None:
 
440
                return tree.basis_tree()
 
441
            else:
 
442
                return branch.basis_tree()
 
443
        else:
 
444
            return 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)
 
449
 
 
450
 
 
451
def _relative_paths_in_tree(tree, paths):
 
452
    """Get the relative paths within a working tree.
 
453
 
 
454
    Each path may be either an absolute path or a path relative to the
 
455
    current working directory.
 
456
    """
 
457
    result = []
 
458
    for filename in paths:
 
459
        try:
 
460
            result.append(tree.relpath(osutils.dereference_path(filename)))
 
461
        except errors.PathNotChild:
 
462
            raise errors.BzrCommandError("Files are in different branches")
 
463
    return result
 
464
 
 
465
 
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/',
353
470
                    path_encoding='utf8'):
354
471
    """Show in text form the changes from one tree to another.
355
472
 
356
 
    to_files
357
 
        If set, include only changes to these files.
 
473
    to_file
 
474
        The output stream.
 
475
 
 
476
    specific_files
 
477
        Include only changes to these files - None for all changes.
358
478
 
359
479
    external_diff_options
360
480
        If set, use an external GNU diff and pass these options.
693
813
        """
694
814
        # TODO: Generation of pseudo-diffs for added/deleted files could
695
815
        # be usefully made into a much faster special case.
696
 
 
697
 
        delta = self.new_tree.changes_from(self.old_tree,
698
 
            specific_files=specific_files,
699
 
            extra_trees=extra_trees, require_versioned=True)
700
 
 
 
816
        iterator = self.new_tree._iter_changes(self.old_tree,
 
817
                                               specific_files=specific_files,
 
818
                                               extra_trees=extra_trees,
 
819
                                               require_versioned=True)
701
820
        has_changes = 0
702
 
        for path, file_id, kind in delta.removed:
703
 
            has_changes = 1
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)
707
 
 
708
 
        for path, file_id, kind in delta.added:
709
 
            has_changes = 1
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:
715
 
            has_changes = 1
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))
721
 
            if text_modified:
722
 
                self.diff(file_id, old_path, new_path)
723
 
        for path, file_id, kind, text_modified, meta_modified in\
724
 
            delta.modified:
725
 
            has_changes = 1
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)
732
 
            if text_modified:
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]
 
823
            path = new_path
 
824
            if path is None:
 
825
                path = old_path
 
826
            return path
 
827
        def get_encoded_path(path):
 
828
            if path is not None:
 
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):
 
833
                continue
 
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))
 
844
                newpath = oldpath
 
845
            elif (old_present, new_present) == (False, True):
 
846
                self.to_file.write("=== added %s '%s'\n" %
 
847
                                   (kind[1], newpath_encoded))
 
848
                oldpath = newpath
 
849
            elif renamed:
 
850
                self.to_file.write("=== renamed %s '%s' => '%s'%s\n" %
 
851
                    (kind[0], oldpath_encoded, newpath_encoded, prop_str))
 
852
            else:
 
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))
 
857
            if changed_content:
 
858
                self.diff(file_id, oldpath, newpath)
 
859
                has_changes = 1
 
860
            if renamed:
 
861
                has_changes = 1
735
862
        return has_changes
736
863
 
737
864
    def diff(self, file_id, old_path, new_path):