213
229
delta = compare_trees(old_tree, new_tree, want_unchanged=False,
214
230
specific_files=specific_files)
216
233
for path, file_id, kind in delta.removed:
217
print >>to_file, '*** removed %s %r' % (kind, path)
219
diff_file(old_label + path,
220
old_tree.get_file(file_id).readlines(),
235
print >>to_file, '=== removed %s %r' % (kind, path)
236
old_tree.inventory[file_id].diff(diff_file, old_label + path, old_tree,
237
DEVNULL, None, None, to_file)
225
238
for path, file_id, kind in delta.added:
226
print >>to_file, '*** added %s %r' % (kind, path)
231
new_tree.get_file(file_id).readlines(),
234
for old_path, new_path, file_id, kind, text_modified in delta.renamed:
235
print >>to_file, '*** renamed %s %r => %r' % (kind, old_path, new_path)
240
print >>to_file, '=== added %s %r' % (kind, path)
241
new_tree.inventory[file_id].diff(diff_file, new_label + path, new_tree,
242
DEVNULL, None, None, to_file,
244
for (old_path, new_path, file_id, kind,
245
text_modified, meta_modified) in delta.renamed:
247
prop_str = get_prop_change(meta_modified)
248
print >>to_file, '=== renamed %s %r => %r%s' % (
249
kind, old_path, new_path, prop_str)
250
_maybe_diff_file_or_symlink(old_label, old_path, old_tree, file_id,
251
new_label, new_path, new_tree,
252
text_modified, kind, to_file, diff_file)
253
for path, file_id, kind, text_modified, meta_modified in delta.modified:
255
prop_str = get_prop_change(meta_modified)
256
print >>to_file, '=== modified %s %r%s' % (kind, path, prop_str)
236
257
if text_modified:
237
diff_file(old_label + old_path,
238
old_tree.get_file(file_id).readlines(),
239
new_label + new_path,
240
new_tree.get_file(file_id).readlines(),
243
for path, file_id, kind in delta.modified:
244
print >>to_file, '*** modified %s %r' % (kind, path)
246
diff_file(old_label + path,
247
old_tree.get_file(file_id).readlines(),
249
new_tree.get_file(file_id).readlines(),
254
class TreeDelta(object):
255
"""Describes changes from one tree to another.
264
(oldpath, newpath, id, kind, text_modified)
270
Each id is listed only once.
272
Files that are both modified and renamed are listed only in
273
renamed, with the text_modified flag true.
275
Files are only considered renamed if their name has changed or
276
their parent directory has changed. Renaming a directory
277
does not count as renaming all its contents.
279
The lists are normally sorted when the delta is created.
288
def __eq__(self, other):
289
if not isinstance(other, TreeDelta):
291
return self.added == other.added \
292
and self.removed == other.removed \
293
and self.renamed == other.renamed \
294
and self.modified == other.modified \
295
and self.unchanged == other.unchanged
297
def __ne__(self, other):
298
return not (self == other)
301
return "TreeDelta(added=%r, removed=%r, renamed=%r, modified=%r," \
302
" unchanged=%r)" % (self.added, self.removed, self.renamed,
303
self.modified, self.unchanged)
305
def has_changed(self):
306
changes = len(self.added) + len(self.removed) + len(self.renamed)
307
changes += len(self.modified)
308
return (changes != 0)
310
def touches_file_id(self, file_id):
311
"""Return True if file_id is modified by this delta."""
312
for l in self.added, self.removed, self.modified:
316
for v in self.renamed:
322
def show(self, to_file, show_ids=False, show_unchanged=False):
323
def show_list(files):
324
for path, fid, kind in files:
325
if kind == 'directory':
327
elif kind == 'symlink':
331
print >>to_file, ' %-30s %s' % (path, fid)
333
print >>to_file, ' ', path
336
print >>to_file, 'removed:'
337
show_list(self.removed)
340
print >>to_file, 'added:'
341
show_list(self.added)
344
print >>to_file, 'renamed:'
345
for oldpath, newpath, fid, kind, text_modified in self.renamed:
347
print >>to_file, ' %s => %s %s' % (oldpath, newpath, fid)
349
print >>to_file, ' %s => %s' % (oldpath, newpath)
352
print >>to_file, 'modified:'
353
show_list(self.modified)
355
if show_unchanged and self.unchanged:
356
print >>to_file, 'unchanged:'
357
show_list(self.unchanged)
361
def compare_trees(old_tree, new_tree, want_unchanged=False, specific_files=None):
362
"""Describe changes from one tree to another.
364
Returns a TreeDelta with details of added, modified, renamed, and
367
The root entry is specifically exempt.
369
This only considers versioned files.
372
If true, also list files unchanged from one version to
376
If true, only check for changes to specified names or
380
from osutils import is_inside_any
258
_maybe_diff_file_or_symlink(old_label, path, old_tree, file_id,
259
new_label, path, new_tree,
260
True, kind, to_file, diff_file)
382
old_inv = old_tree.inventory
383
new_inv = new_tree.inventory
385
mutter('start compare_trees')
387
# TODO: match for specific files can be rather smarter by finding
388
# the IDs of those files up front and then considering only that.
390
for file_id in old_tree:
391
if file_id in new_tree:
392
old_ie = old_inv[file_id]
393
new_ie = new_inv[file_id]
396
assert kind == new_ie.kind
398
assert kind in ('file', 'directory', 'symlink', 'root_directory'), \
399
'invalid file kind %r' % kind
401
if kind == 'root_directory':
405
if (not is_inside_any(specific_files, old_inv.id2path(file_id))
406
and not is_inside_any(specific_files, new_inv.id2path(file_id))):
410
old_sha1 = old_tree.get_file_sha1(file_id)
411
new_sha1 = new_tree.get_file_sha1(file_id)
412
text_modified = (old_sha1 != new_sha1)
414
## mutter("no text to check for %r %r" % (file_id, kind))
415
text_modified = False
417
# TODO: Can possibly avoid calculating path strings if the
418
# two files are unchanged and their names and parents are
419
# the same and the parents are unchanged all the way up.
420
# May not be worthwhile.
422
if (old_ie.name != new_ie.name
423
or old_ie.parent_id != new_ie.parent_id):
424
delta.renamed.append((old_inv.id2path(file_id),
425
new_inv.id2path(file_id),
429
delta.modified.append((new_inv.id2path(file_id), file_id, kind))
431
delta.unchanged.append((new_inv.id2path(file_id), file_id, kind))
433
kind = old_inv.get_file_kind(file_id)
434
old_path = old_inv.id2path(file_id)
436
if not is_inside_any(specific_files, old_path):
438
delta.removed.append((old_path, file_id, kind))
440
mutter('start looking for new files')
441
for file_id in new_inv:
442
if file_id in old_inv:
444
new_path = new_inv.id2path(file_id)
446
if not is_inside_any(specific_files, new_path):
448
kind = new_inv.get_file_kind(file_id)
449
delta.added.append((new_path, file_id, kind))
454
delta.modified.sort()
455
delta.unchanged.sort()
264
def get_prop_change(meta_modified):
266
return " (properties changed)"
271
def _maybe_diff_file_or_symlink(old_label, old_path, old_tree, file_id,
272
new_label, new_path, new_tree, text_modified,
273
kind, to_file, diff_file):
275
new_entry = new_tree.inventory[file_id]
276
old_tree.inventory[file_id].diff(diff_file,
277
old_label + old_path, old_tree,
278
new_label + new_path, new_entry,