/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
1
# -*- coding: UTF-8 -*-
2
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
from bzrlib.trace import mutter
18
19
class TreeDelta(object):
20
    """Describes changes from one tree to another.
21
22
    Contains four lists:
23
24
    added
25
        (path, id, kind)
26
    removed
27
        (path, id, kind)
28
    renamed
29
        (oldpath, newpath, id, kind, text_modified)
30
    modified
31
        (path, id, kind)
32
    unchanged
33
        (path, id, kind)
34
35
    Each id is listed only once.
36
37
    Files that are both modified and renamed are listed only in
1092.2.6 by Robert Collins
symlink support updated to work
38
    renamed, with the text_modified flag true. The text_modified
39
    applies either to the the content of the file or the target of the
40
    symbolic link, depending of the kind of file.
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
41
42
    Files are only considered renamed if their name has changed or
43
    their parent directory has changed.  Renaming a directory
44
    does not count as renaming all its contents.
45
46
    The lists are normally sorted when the delta is created.
47
    """
48
    def __init__(self):
49
        self.added = []
50
        self.removed = []
51
        self.renamed = []
52
        self.modified = []
53
        self.unchanged = []
54
55
    def __eq__(self, other):
56
        if not isinstance(other, TreeDelta):
57
            return False
58
        return self.added == other.added \
59
               and self.removed == other.removed \
60
               and self.renamed == other.renamed \
61
               and self.modified == other.modified \
62
               and self.unchanged == other.unchanged
63
64
    def __ne__(self, other):
65
        return not (self == other)
66
67
    def __repr__(self):
68
        return "TreeDelta(added=%r, removed=%r, renamed=%r, modified=%r," \
69
            " unchanged=%r)" % (self.added, self.removed, self.renamed,
70
            self.modified, self.unchanged)
71
72
    def has_changed(self):
1189 by Martin Pool
- BROKEN: partial support for commit into weave
73
        return bool(self.modified
74
                    or self.added
75
                    or self.removed
76
                    or self.renamed)
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
77
78
    def touches_file_id(self, file_id):
79
        """Return True if file_id is modified by this delta."""
80
        for l in self.added, self.removed, self.modified:
81
            for v in l:
82
                if v[1] == file_id:
83
                    return True
84
        for v in self.renamed:
85
            if v[2] == file_id:
86
                return True
87
        return False
88
            
89
90
    def show(self, to_file, show_ids=False, show_unchanged=False):
91
        def show_list(files):
92
            for path, fid, kind in files:
93
                if kind == 'directory':
94
                    path += '/'
95
                elif kind == 'symlink':
96
                    path += '@'
97
                    
98
                if show_ids:
99
                    print >>to_file, '  %-30s %s' % (path, fid)
100
                else:
101
                    print >>to_file, ' ', path
102
            
103
        if self.removed:
104
            print >>to_file, 'removed:'
105
            show_list(self.removed)
106
                
107
        if self.added:
108
            print >>to_file, 'added:'
109
            show_list(self.added)
110
111
        if self.renamed:
112
            print >>to_file, 'renamed:'
113
            for oldpath, newpath, fid, kind, text_modified in self.renamed:
114
                if show_ids:
115
                    print >>to_file, '  %s => %s %s' % (oldpath, newpath, fid)
116
                else:
117
                    print >>to_file, '  %s => %s' % (oldpath, newpath)
118
                    
119
        if self.modified:
120
            print >>to_file, 'modified:'
121
            show_list(self.modified)
122
            
123
        if show_unchanged and self.unchanged:
124
            print >>to_file, 'unchanged:'
125
            show_list(self.unchanged)
126
127
128
129
def compare_trees(old_tree, new_tree, want_unchanged=False, specific_files=None):
130
    """Describe changes from one tree to another.
131
132
    Returns a TreeDelta with details of added, modified, renamed, and
133
    deleted entries.
134
135
    The root entry is specifically exempt.
136
137
    This only considers versioned files.
138
139
    want_unchanged
140
        If true, also list files unchanged from one version to
141
        the next.
142
143
    specific_files
144
        If true, only check for changes to specified names or
145
        files within them.
146
    """
147
148
    from osutils import is_inside_any
149
    
150
    old_inv = old_tree.inventory
151
    new_inv = new_tree.inventory
152
    delta = TreeDelta()
153
    mutter('start compare_trees')
154
155
    # TODO: match for specific files can be rather smarter by finding
156
    # the IDs of those files up front and then considering only that.
157
158
    for file_id in old_tree:
159
        if file_id in new_tree:
160
            old_ie = old_inv[file_id]
161
            new_ie = new_inv[file_id]
162
163
            kind = old_ie.kind
164
            assert kind == new_ie.kind
165
            
166
            assert kind in ('file', 'directory', 'symlink', 'root_directory'), \
167
                   'invalid file kind %r' % kind
168
169
            if kind == 'root_directory':
170
                continue
171
            
172
            if specific_files:
173
                if (not is_inside_any(specific_files, old_inv.id2path(file_id)) 
174
                    and not is_inside_any(specific_files, new_inv.id2path(file_id))):
175
                    continue
176
177
            if kind == 'file':
178
                old_sha1 = old_tree.get_file_sha1(file_id)
179
                new_sha1 = new_tree.get_file_sha1(file_id)
180
                text_modified = (old_sha1 != new_sha1)
1092.2.6 by Robert Collins
symlink support updated to work
181
            elif kind == 'symlink':
182
                t1 = old_tree.get_symlink_target(file_id)
183
                t2 = new_tree.get_symlink_target(file_id)
184
                if t1 != t2:
185
                    mutter("    symlink target changed")
186
                    text_modified = True
187
                else:
188
                    text_modified = False
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
189
            else:
190
                ## mutter("no text to check for %r %r" % (file_id, kind))
191
                text_modified = False
192
193
            # TODO: Can possibly avoid calculating path strings if the
194
            # two files are unchanged and their names and parents are
195
            # the same and the parents are unchanged all the way up.
196
            # May not be worthwhile.
197
            
198
            if (old_ie.name != new_ie.name
199
                or old_ie.parent_id != new_ie.parent_id):
200
                delta.renamed.append((old_inv.id2path(file_id),
201
                                      new_inv.id2path(file_id),
202
                                      file_id, kind,
203
                                      text_modified))
204
            elif text_modified:
205
                delta.modified.append((new_inv.id2path(file_id), file_id, kind))
206
            elif want_unchanged:
207
                delta.unchanged.append((new_inv.id2path(file_id), file_id, kind))
208
        else:
209
            kind = old_inv.get_file_kind(file_id)
210
            if kind == 'root_directory':
211
                continue
212
            old_path = old_inv.id2path(file_id)
213
            if specific_files:
214
                if not is_inside_any(specific_files, old_path):
215
                    continue
216
            delta.removed.append((old_path, file_id, kind))
217
218
    mutter('start looking for new files')
219
    for file_id in new_inv:
220
        if file_id in old_inv:
221
            continue
222
        kind = new_inv.get_file_kind(file_id)
223
        if kind == 'root_directory':
224
            continue
225
        new_path = new_inv.id2path(file_id)
226
        if specific_files:
227
            if not is_inside_any(specific_files, new_path):
228
                continue
229
        delta.added.append((new_path, file_id, kind))
230
            
231
    delta.removed.sort()
232
    delta.added.sort()
233
    delta.renamed.sort()
234
    delta.modified.sort()
235
    delta.unchanged.sort()
236
237
    return delta