/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/delta.py

  • Committer: Aaron Bentley
  • Date: 2006-09-20 14:03:05 UTC
  • mto: This revision was merged to the branch mainline in revision 2162.
  • Revision ID: abentley@panoramicfeedback.com-20060920140305-7726fe3eb92690b1
Get tree._iter_changed down to ~ 1 stat per file

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005, 2006 Canonical
 
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 import errors
 
18
from bzrlib.inventory import InventoryEntry
 
19
from bzrlib.trace import mutter
 
20
from bzrlib.symbol_versioning import deprecated_function, zero_nine
 
21
 
 
22
 
 
23
class TreeDelta(object):
 
24
    """Describes changes from one tree to another.
 
25
 
 
26
    Contains four lists:
 
27
 
 
28
    added
 
29
        (path, id, kind)
 
30
    removed
 
31
        (path, id, kind)
 
32
    renamed
 
33
        (oldpath, newpath, id, kind, text_modified, meta_modified)
 
34
    modified
 
35
        (path, id, kind, text_modified, meta_modified)
 
36
    unchanged
 
37
        (path, id, kind)
 
38
 
 
39
    Each id is listed only once.
 
40
 
 
41
    Files that are both modified and renamed are listed only in
 
42
    renamed, with the text_modified flag true. The text_modified
 
43
    applies either to the the content of the file or the target of the
 
44
    symbolic link, depending of the kind of file.
 
45
 
 
46
    Files are only considered renamed if their name has changed or
 
47
    their parent directory has changed.  Renaming a directory
 
48
    does not count as renaming all its contents.
 
49
 
 
50
    The lists are normally sorted when the delta is created.
 
51
    """
 
52
    def __init__(self):
 
53
        self.added = []
 
54
        self.removed = []
 
55
        self.renamed = []
 
56
        self.modified = []
 
57
        self.unchanged = []
 
58
 
 
59
    def __eq__(self, other):
 
60
        if not isinstance(other, TreeDelta):
 
61
            return False
 
62
        return self.added == other.added \
 
63
               and self.removed == other.removed \
 
64
               and self.renamed == other.renamed \
 
65
               and self.modified == other.modified \
 
66
               and self.unchanged == other.unchanged
 
67
 
 
68
    def __ne__(self, other):
 
69
        return not (self == other)
 
70
 
 
71
    def __repr__(self):
 
72
        return "TreeDelta(added=%r, removed=%r, renamed=%r, modified=%r," \
 
73
            " unchanged=%r)" % (self.added, self.removed, self.renamed,
 
74
            self.modified, self.unchanged)
 
75
 
 
76
    def has_changed(self):
 
77
        return bool(self.modified
 
78
                    or self.added
 
79
                    or self.removed
 
80
                    or self.renamed)
 
81
 
 
82
    def touches_file_id(self, file_id):
 
83
        """Return True if file_id is modified by this delta."""
 
84
        for l in self.added, self.removed, self.modified:
 
85
            for v in l:
 
86
                if v[1] == file_id:
 
87
                    return True
 
88
        for v in self.renamed:
 
89
            if v[2] == file_id:
 
90
                return True
 
91
        return False
 
92
            
 
93
 
 
94
    def show(self, to_file, show_ids=False, show_unchanged=False):
 
95
        """output this delta in status-like form to to_file."""
 
96
        def show_list(files):
 
97
            for item in files:
 
98
                path, fid, kind = item[:3]
 
99
 
 
100
                if kind == 'directory':
 
101
                    path += '/'
 
102
                elif kind == 'symlink':
 
103
                    path += '@'
 
104
 
 
105
                if len(item) == 5 and item[4]:
 
106
                    path += '*'
 
107
 
 
108
                if show_ids:
 
109
                    print >>to_file, '  %-30s %s' % (path, fid)
 
110
                else:
 
111
                    print >>to_file, ' ', path
 
112
            
 
113
        if self.removed:
 
114
            print >>to_file, 'removed:'
 
115
            show_list(self.removed)
 
116
                
 
117
        if self.added:
 
118
            print >>to_file, 'added:'
 
119
            show_list(self.added)
 
120
 
 
121
        extra_modified = []
 
122
 
 
123
        if self.renamed:
 
124
            print >>to_file, 'renamed:'
 
125
            for (oldpath, newpath, fid, kind,
 
126
                 text_modified, meta_modified) in self.renamed:
 
127
                if text_modified or meta_modified:
 
128
                    extra_modified.append((newpath, fid, kind,
 
129
                                           text_modified, meta_modified))
 
130
                if meta_modified:
 
131
                    newpath += '*'
 
132
                if show_ids:
 
133
                    print >>to_file, '  %s => %s %s' % (oldpath, newpath, fid)
 
134
                else:
 
135
                    print >>to_file, '  %s => %s' % (oldpath, newpath)
 
136
                    
 
137
        if self.modified or extra_modified:
 
138
            print >>to_file, 'modified:'
 
139
            show_list(self.modified)
 
140
            show_list(extra_modified)
 
141
            
 
142
        if show_unchanged and self.unchanged:
 
143
            print >>to_file, 'unchanged:'
 
144
            show_list(self.unchanged)
 
145
 
 
146
 
 
147
@deprecated_function(zero_nine)
 
148
def compare_trees(old_tree, new_tree, want_unchanged=False,
 
149
                  specific_files=None, extra_trees=None,
 
150
                  require_versioned=False):
 
151
    """compare_trees was deprecated in 0.10. Please see Tree.changes_from."""
 
152
    return new_tree.changes_from(old_tree,
 
153
        want_unchanged=want_unchanged,
 
154
        specific_files=specific_files,
 
155
        extra_trees=extra_trees,
 
156
        require_versioned=require_versioned)
 
157
 
 
158
 
 
159
def _compare_trees(old_tree, new_tree, want_unchanged, specific_file_ids):
 
160
 
 
161
    delta = TreeDelta()
 
162
    # mutter('start compare_trees')
 
163
 
 
164
    root_id = new_tree.inventory.root.file_id
 
165
    for (file_id, path, content_change, versioned, parent_id, name, kind,
 
166
         executable) in new_tree.iter_changes(old_tree, want_unchanged, 
 
167
                                              specific_file_ids):
 
168
        if file_id == root_id:
 
169
            continue
 
170
        assert kind[0] == kind[1] or None in kind
 
171
        # the only 'kind change' permitted is creation/deletion
 
172
        fully_present = tuple((versioned[x] and kind[x] is not None) for
 
173
                              x in range(2))
 
174
        if fully_present[0] != fully_present[1]:
 
175
            if fully_present[1] is True:
 
176
                delta.added.append((path, file_id, kind[1]))
 
177
            else:
 
178
                assert fully_present[0] is True
 
179
                old_path = old_tree.id2path(file_id)
 
180
                delta.removed.append((old_path, file_id, kind[0]))
 
181
        elif fully_present[0] is False:
 
182
            continue
 
183
        elif name[0] != name[1] or parent_id[0] != parent_id[1]:
 
184
            # If the name changes, or the parent_id changes, we have a rename
 
185
            # (if we move a parent, that doesn't count as a rename for the
 
186
            # file)
 
187
            old_path = old_tree.id2path(file_id)
 
188
            delta.renamed.append((old_path,
 
189
                                  path,
 
190
                                  file_id, 
 
191
                                  kind[1],
 
192
                                  content_change, 
 
193
                                  (executable[0] != executable[1])))
 
194
        elif content_change is True or executable[0] != executable[1]:
 
195
            delta.modified.append((path, file_id, kind[1],
 
196
                                   content_change, 
 
197
                                   (executable[0] != executable[1])))
 
198
        else:
 
199
            delta.unchanged.append((path, file_id, kind[1]))
 
200
 
 
201
    delta.removed.sort()
 
202
    delta.added.sort()
 
203
    delta.renamed.sort()
 
204
    # TODO: jam 20060529 These lists shouldn't need to be sorted
 
205
    #       since we added them in alphabetical order.
 
206
    delta.modified.sort()
 
207
    delta.unchanged.sort()
 
208
 
 
209
    return delta