229
213
delta = compare_trees(old_tree, new_tree, want_unchanged=False,
230
214
specific_files=specific_files)
233
216
for path, file_id, kind in delta.removed:
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)
217
print >>to_file, '*** removed %s %r' % (kind, path)
219
diff_file(old_label + path,
220
old_tree.get_file(file_id).readlines(),
238
225
for path, file_id, kind in delta.added:
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)
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)
257
236
if text_modified:
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)
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
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,
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
kind = old_inv.get_file_kind(file_id)
393
assert kind == new_inv.get_file_kind(file_id)
395
assert kind in ('file', 'directory', 'symlink', 'root_directory'), \
396
'invalid file kind %r' % kind
398
if kind == 'root_directory':
401
old_path = old_inv.id2path(file_id)
402
new_path = new_inv.id2path(file_id)
404
old_ie = old_inv[file_id]
405
new_ie = new_inv[file_id]
408
if (not is_inside_any(specific_files, old_path)
409
and not is_inside_any(specific_files, new_path)):
413
old_sha1 = old_tree.get_file_sha1(file_id)
414
new_sha1 = new_tree.get_file_sha1(file_id)
415
text_modified = (old_sha1 != new_sha1)
417
## mutter("no text to check for %r %r" % (file_id, kind))
418
text_modified = False
420
# TODO: Can possibly avoid calculating path strings if the
421
# two files are unchanged and their names and parents are
422
# the same and the parents are unchanged all the way up.
423
# May not be worthwhile.
425
if (old_ie.name != new_ie.name
426
or old_ie.parent_id != new_ie.parent_id):
427
delta.renamed.append((old_path, new_path, file_id, kind,
430
delta.modified.append((new_path, file_id, kind))
432
delta.unchanged.append((new_path, file_id, kind))
434
kind = old_inv.get_file_kind(file_id)
435
old_path = old_inv.id2path(file_id)
437
if not is_inside_any(specific_files, old_path):
439
delta.removed.append((old_path, file_id, kind))
441
mutter('start looking for new files')
442
for file_id in new_inv:
443
if file_id in old_inv:
445
new_path = new_inv.id2path(file_id)
447
if not is_inside_any(specific_files, new_path):
449
kind = new_inv.get_file_kind(file_id)
450
delta.added.append((new_path, file_id, kind))
455
delta.modified.sort()
456
delta.unchanged.sort()