2
 
# -*- coding: UTF-8 -*-
 
4
 
# This program is free software; you can redistribute it and/or modify
 
5
 
# it under the terms of the GNU General Public License as published by
 
6
 
# the Free Software Foundation; either version 2 of the License, or
 
7
 
# (at your option) any later version.
 
9
 
# This program is distributed in the hope that it will be useful,
 
10
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
 
# GNU General Public License for more details.
 
14
 
# You should have received a copy of the GNU General Public License
 
15
 
# along with this program; if not, write to the Free Software
 
16
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
20
 
from trace import mutter
 
27
 
def diff_trees(old_tree, new_tree):
 
28
 
    """Compute diff between two trees.
 
30
 
    They may be in different branches and may be working or historical
 
33
 
    Yields a sequence of (state, id, old_name, new_name, kind).
 
34
 
    Each filename and each id is listed only once.
 
37
 
    ## TODO: Compare files before diffing; only mention those that have changed
 
39
 
    ## TODO: Set nice names in the headers, maybe include diffstat
 
41
 
    ## TODO: Perhaps make this a generator rather than using
 
44
 
    ## TODO: Allow specifying a list of files to compare, rather than
 
45
 
    ## doing the whole tree?  (Not urgent.)
 
47
 
    ## TODO: Allow diffing any two inventories, not just the
 
48
 
    ## current one against one.  We mgiht need to specify two
 
49
 
    ## stores to look for the files if diffing two branches.  That
 
50
 
    ## might imply this shouldn't be primarily a Branch method.
 
52
 
    ## XXX: This doesn't report on unknown files; that can be done
 
53
 
    ## from a separate method.
 
55
 
    old_it = old_tree.list_files()
 
56
 
    new_it = new_tree.list_files()
 
64
 
    old_item = next(old_it)
 
65
 
    new_item = next(new_it)
 
67
 
    # We step through the two sorted iterators in parallel, trying to
 
70
 
    while (old_item != None) or (new_item != None):
 
71
 
        # OK, we still have some remaining on both, but they may be
 
74
 
            old_name, old_class, old_kind, old_id = old_item
 
79
 
            new_name, new_class, new_kind, new_id = new_item
 
83
 
        mutter("   diff pairwise %r" % (old_item,))
 
84
 
        mutter("                 %r" % (new_item,))
 
87
 
            # can't handle the old tree being a WorkingTree
 
88
 
            assert old_class == 'V'
 
90
 
        if new_item and (new_class != 'V'):
 
91
 
            yield new_class, None, None, new_name, new_kind
 
92
 
            new_item = next(new_it)
 
93
 
        elif (not new_item) or (old_item and (old_name < new_name)):
 
94
 
            mutter("     extra entry in old-tree sequence")
 
95
 
            if new_tree.has_id(old_id):
 
96
 
                # will be mentioned as renamed under new name
 
99
 
                yield 'D', old_id, old_name, None, old_kind
 
100
 
            old_item = next(old_it)
 
101
 
        elif (not old_item) or (new_item and (new_name < old_name)):
 
102
 
            mutter("     extra entry in new-tree sequence")
 
103
 
            if old_tree.has_id(new_id):
 
104
 
                yield 'R', new_id, old_tree.id2path(new_id), new_name, new_kind
 
106
 
                yield 'A', new_id, None, new_name, new_kind
 
107
 
            new_item = next(new_it)
 
108
 
        elif old_id != new_id:
 
109
 
            assert old_name == new_name
 
110
 
            # both trees have a file of this name, but it is not the
 
111
 
            # same file.  in other words, the old filename has been
 
112
 
            # overwritten by either a newly-added or a renamed file.
 
113
 
            # (should we return something about the overwritten file?)
 
114
 
            if old_tree.has_id(new_id):
 
115
 
                # renaming, overlying a deleted file
 
116
 
                yield 'R', new_id, old_tree.id2path(new_id), new_name, new_kind
 
118
 
                yield 'A', new_id, None, new_name, new_kind
 
120
 
            new_item = next(new_it)
 
121
 
            old_item = next(old_it)
 
123
 
            assert old_id == new_id
 
124
 
            assert old_name == new_name
 
125
 
            assert old_kind == new_kind
 
127
 
            if old_kind == 'directory':
 
128
 
                yield '.', new_id, old_name, new_name, new_kind
 
129
 
            elif old_tree.get_file_size(old_id) != new_tree.get_file_size(old_id):
 
130
 
                mutter("    file size has changed, must be different")
 
131
 
                yield 'M', new_id, old_name, new_name, new_kind
 
132
 
            elif old_tree.get_file_sha1(old_id) == new_tree.get_file_sha1(old_id):
 
133
 
                mutter("      SHA1 indicates they're identical")
 
134
 
                ## assert compare_files(old_tree.get_file(i), new_tree.get_file(i))
 
135
 
                yield '.', new_id, old_name, new_name, new_kind
 
137
 
                mutter("      quick compare shows different")
 
138
 
                yield 'M', new_id, old_name, new_name, new_kind
 
140
 
            new_item = next(new_it)
 
141
 
            old_item = next(old_it)