1
# Copyright (C) 2005, 2006 Canonical Ltd
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.
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.
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
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
23
class TreeDelta(object):
24
"""Describes changes from one tree to another.
33
(oldpath, newpath, id, kind, text_modified, meta_modified)
35
(path, id, kind, text_modified, meta_modified)
39
Each id is listed only once.
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.
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.
50
The lists are normally sorted when the delta is created.
59
def __eq__(self, other):
60
if not isinstance(other, TreeDelta):
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
68
def __ne__(self, other):
69
return not (self == other)
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)
76
def has_changed(self):
77
return bool(self.modified
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:
88
for v in self.renamed:
94
def show(self, to_file, show_ids=False, show_unchanged=False, short_status=False):
95
"""output this delta in status-like form to to_file."""
96
def show_list(files, short_status_letter=''):
98
path, fid, kind = item[:3]
100
if kind == 'directory':
102
elif kind == 'symlink':
105
if len(item) == 5 and item[4]:
109
print >>to_file, '%s %-30s %s' % (short_status_letter, path, fid)
111
print >>to_file, '%s %s' % (short_status_letter, path)
115
print >>to_file, 'removed:'
116
show_list(self.removed)
118
show_list(self.removed, 'D')
122
print >>to_file, 'added:'
123
show_list(self.added)
125
show_list(self.added, 'A')
130
short_status_letter = 'R'
132
print >>to_file, 'renamed:'
133
short_status_letter = ''
134
for (oldpath, newpath, fid, kind,
135
text_modified, meta_modified) in self.renamed:
136
if text_modified or meta_modified:
137
extra_modified.append((newpath, fid, kind,
138
text_modified, meta_modified))
142
print >>to_file, '%s %s => %s %s' % (short_status_letter,
143
oldpath, newpath, fid)
145
print >>to_file, '%s %s => %s' % (short_status_letter,
148
if self.modified or extra_modified:
149
short_status_letter = 'M'
151
print >>to_file, 'modified:'
152
short_status_letter = ''
153
show_list(self.modified, short_status_letter)
154
show_list(extra_modified, short_status_letter)
156
if show_unchanged and self.unchanged:
158
print >>to_file, 'unchanged:'
159
show_list(self.unchanged)
161
show_list(self.unchanged, 'S')
164
@deprecated_function(zero_nine)
165
def compare_trees(old_tree, new_tree, want_unchanged=False,
166
specific_files=None, extra_trees=None,
167
require_versioned=False):
168
"""compare_trees was deprecated in 0.10. Please see Tree.changes_from."""
169
return new_tree.changes_from(old_tree,
170
want_unchanged=want_unchanged,
171
specific_files=specific_files,
172
extra_trees=extra_trees,
173
require_versioned=require_versioned,
177
def _compare_trees(old_tree, new_tree, want_unchanged, specific_file_ids,
180
# mutter('start compare_trees')
182
for (file_id, path, content_change, versioned, parent_id, name, kind,
183
executable) in new_tree._iter_changes(old_tree, want_unchanged,
185
if not include_root and (None, None) == parent_id:
187
assert kind[0] == kind[1] or None in kind
188
# the only 'kind change' permitted is creation/deletion
189
fully_present = tuple((versioned[x] and kind[x] is not None) for
191
if fully_present[0] != fully_present[1]:
192
if fully_present[1] is True:
193
delta.added.append((path, file_id, kind[1]))
195
assert fully_present[0] is True
196
old_path = old_tree.id2path(file_id)
197
delta.removed.append((old_path, file_id, kind[0]))
198
elif fully_present[0] is False:
200
elif name[0] != name[1] or parent_id[0] != parent_id[1]:
201
# If the name changes, or the parent_id changes, we have a rename
202
# (if we move a parent, that doesn't count as a rename for the
204
old_path = old_tree.id2path(file_id)
205
delta.renamed.append((old_path,
210
(executable[0] != executable[1])))
211
elif content_change is True or executable[0] != executable[1]:
212
delta.modified.append((path, file_id, kind[1],
214
(executable[0] != executable[1])))
216
delta.unchanged.append((path, file_id, kind[1]))
221
# TODO: jam 20060529 These lists shouldn't need to be sorted
222
# since we added them in alphabetical order.
223
delta.modified.sort()
224
delta.unchanged.sort()