1
# Copyright (C) 2005, 2006 Canonical
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):
95
"""output this delta in status-like form to to_file."""
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, ' %-30s %s' % (path, fid)
111
print >>to_file, ' ', path
114
print >>to_file, 'removed:'
115
show_list(self.removed)
118
print >>to_file, 'added:'
119
show_list(self.added)
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))
133
print >>to_file, ' %s => %s %s' % (oldpath, newpath, fid)
135
print >>to_file, ' %s => %s' % (oldpath, newpath)
137
if self.modified or extra_modified:
138
print >>to_file, 'modified:'
139
show_list(self.modified)
140
show_list(extra_modified)
142
if show_unchanged and self.unchanged:
143
print >>to_file, 'unchanged:'
144
show_list(self.unchanged)
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)
159
def _compare_trees(old_tree, new_tree, want_unchanged, specific_file_ids):
162
# mutter('start compare_trees')
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,
168
if file_id == root_id:
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
174
if fully_present[0] != fully_present[1]:
175
if fully_present[1] is True:
176
delta.added.append((path, file_id, kind[1]))
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:
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
187
old_path = old_tree.id2path(file_id)
188
delta.renamed.append((old_path,
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],
197
(executable[0] != executable[1])))
199
delta.unchanged.append((path, file_id, kind[1]))
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()