143
142
oldtmpf.close() # and delete
147
@deprecated_function(zero_eight)
148
def show_diff(b, from_spec, specific_files, external_diff_options=None,
149
revision2=None, output=None, b2=None):
150
"""Shortcut for showing the diff to the working tree.
152
Please use show_diff_trees instead.
158
None for 'basis tree', or otherwise the old revision to compare against.
160
The more general form is show_diff_trees(), where the caller
161
supplies any two trees.
167
if from_spec is None:
168
old_tree = b.bzrdir.open_workingtree()
170
old_tree = old_tree = old_tree.basis_tree()
172
old_tree = b.repository.revision_tree(from_spec.in_history(b).rev_id)
174
if revision2 is None:
176
new_tree = b.bzrdir.open_workingtree()
178
new_tree = b2.bzrdir.open_workingtree()
180
new_tree = b.repository.revision_tree(revision2.in_history(b).rev_id)
182
return show_diff_trees(old_tree, new_tree, output, specific_files,
183
external_diff_options)
186
def diff_cmd_helper(tree, specific_files, external_diff_options,
187
old_revision_spec=None, new_revision_spec=None):
188
"""Helper for cmd_diff.
194
The specific files to compare, or None
196
external_diff_options
197
If non-None, run an external diff, and pass it these options
200
If None, use basis tree as old revision, otherwise use the tree for
201
the specified revision.
204
If None, use working tree as new revision, otherwise use the tree for
205
the specified revision.
207
The more general form is show_diff_trees(), where the caller
208
supplies any two trees.
147
def show_diff(b, revision, specific_files, external_diff_options=None):
213
revision_id = spec.in_store(tree.branch).rev_id
214
return tree.branch.repository.revision_tree(revision_id)
215
if old_revision_spec is None:
216
old_tree = tree.basis_tree()
218
old_tree = spec_tree(old_revision_spec)
220
if new_revision_spec is None:
223
new_tree = spec_tree(new_revision_spec)
225
return show_diff_trees(old_tree, new_tree, sys.stdout, specific_files,
226
external_diff_options)
151
old_tree = b.basis_tree()
153
old_tree = b.revision_tree(b.lookup_revision(revision))
155
new_tree = b.working_tree()
157
show_diff_trees(old_tree, new_tree, sys.stdout, specific_files,
158
external_diff_options)
229
162
def show_diff_trees(old_tree, new_tree, to_file, specific_files=None,
275
191
diff_file = internal_diff
277
194
delta = compare_trees(old_tree, new_tree, want_unchanged=False,
278
195
specific_files=specific_files)
281
197
for path, file_id, kind in delta.removed:
283
print >>to_file, '=== removed %s %r' % (kind, old_label + path)
284
old_tree.inventory[file_id].diff(diff_file, old_label + path, old_tree,
285
DEVNULL, None, None, to_file)
198
print '*** removed %s %r' % (kind, path)
200
diff_file(old_label + path,
201
old_tree.get_file(file_id).readlines(),
286
206
for path, file_id, kind in delta.added:
288
print >>to_file, '=== added %s %r' % (kind, new_label + path)
289
new_tree.inventory[file_id].diff(diff_file, new_label + path, new_tree,
290
DEVNULL, None, None, to_file,
292
for (old_path, new_path, file_id, kind,
293
text_modified, meta_modified) in delta.renamed:
295
prop_str = get_prop_change(meta_modified)
296
print >>to_file, '=== renamed %s %r => %r%s' % (
297
kind, old_label + old_path, new_label + new_path, prop_str)
298
_maybe_diff_file_or_symlink(old_label, old_path, old_tree, file_id,
299
new_label, new_path, new_tree,
300
text_modified, kind, to_file, diff_file)
301
for path, file_id, kind, text_modified, meta_modified in delta.modified:
303
prop_str = get_prop_change(meta_modified)
304
print >>to_file, '=== modified %s %r%s' % (kind, old_label + path,
207
print '*** added %s %r' % (kind, path)
212
new_tree.get_file(file_id).readlines(),
215
for old_path, new_path, file_id, kind, text_modified in delta.renamed:
216
print '*** renamed %s %r => %r' % (kind, old_path, new_path)
306
217
if text_modified:
307
_maybe_diff_file_or_symlink(old_label, path, old_tree, file_id,
308
new_label, path, new_tree,
309
True, kind, to_file, diff_file)
314
def _raise_if_doubly_unversioned(specific_files, old_tree, new_tree):
315
"""Complain if paths are not versioned in either tree."""
316
if not specific_files:
318
old_unversioned = old_tree.filter_unversioned_files(specific_files)
319
new_unversioned = new_tree.filter_unversioned_files(specific_files)
320
unversioned = old_unversioned.intersection(new_unversioned)
322
raise errors.PathsNotVersionedError(sorted(unversioned))
218
diff_file(old_label + old_path,
219
old_tree.get_file(file_id).readlines(),
220
new_label + new_path,
221
new_tree.get_file(file_id).readlines(),
224
for path, file_id, kind in delta.modified:
225
print '*** modified %s %r' % (kind, path)
227
diff_file(old_label + path,
228
old_tree.get_file(file_id).readlines(),
230
new_tree.get_file(file_id).readlines(),
235
class TreeDelta(object):
236
"""Describes changes from one tree to another.
245
(oldpath, newpath, id, kind, text_modified)
251
Each id is listed only once.
253
Files that are both modified and renamed are listed only in
254
renamed, with the text_modified flag true.
256
The lists are normally sorted when the delta is created.
266
def touches_file_id(self, file_id):
267
"""Return True if file_id is modified by this delta."""
268
for l in self.added, self.removed, self.modified:
272
for v in self.renamed:
278
def show(self, to_file, show_ids=False, show_unchanged=False):
279
def show_list(files):
280
for path, fid, kind in files:
281
if kind == 'directory':
283
elif kind == 'symlink':
287
print >>to_file, ' %-30s %s' % (path, fid)
289
print >>to_file, ' ', path
292
print >>to_file, 'removed:'
293
show_list(self.removed)
296
print >>to_file, 'added:'
297
show_list(self.added)
300
print >>to_file, 'renamed:'
301
for oldpath, newpath, fid, kind, text_modified in self.renamed:
303
print >>to_file, ' %s => %s %s' % (oldpath, newpath, fid)
305
print >>to_file, ' %s => %s' % (oldpath, newpath)
308
print >>to_file, 'modified:'
309
show_list(self.modified)
311
if show_unchanged and self.unchanged:
312
print >>to_file, 'unchanged:'
313
show_list(self.unchanged)
317
def compare_trees(old_tree, new_tree, want_unchanged, specific_files=None):
318
"""Describe changes from one tree to another.
320
Returns a TreeDelta with details of added, modified, renamed, and
323
The root entry is specifically exempt.
325
This only considers versioned files.
328
If true, also list files unchanged from one version to
332
If true, only check for changes to specified names or
336
from osutils import is_inside_any
325
def get_prop_change(meta_modified):
327
return " (properties changed)"
332
def _maybe_diff_file_or_symlink(old_label, old_path, old_tree, file_id,
333
new_label, new_path, new_tree, text_modified,
334
kind, to_file, diff_file):
336
new_entry = new_tree.inventory[file_id]
337
old_tree.inventory[file_id].diff(diff_file,
338
old_label + old_path, old_tree,
339
new_label + new_path, new_entry,
338
old_inv = old_tree.inventory
339
new_inv = new_tree.inventory
341
mutter('start compare_trees')
343
# TODO: match for specific files can be rather smarter by finding
344
# the IDs of those files up front and then considering only that.
346
for file_id in old_tree:
347
if file_id in new_tree:
348
kind = old_inv.get_file_kind(file_id)
349
assert kind == new_inv.get_file_kind(file_id)
351
assert kind in ('file', 'directory', 'symlink', 'root_directory'), \
352
'invalid file kind %r' % kind
354
if kind == 'root_directory':
357
old_path = old_inv.id2path(file_id)
358
new_path = new_inv.id2path(file_id)
361
if (not is_inside_any(specific_files, old_path)
362
and not is_inside_any(specific_files, new_path)):
366
old_sha1 = old_tree.get_file_sha1(file_id)
367
new_sha1 = new_tree.get_file_sha1(file_id)
368
text_modified = (old_sha1 != new_sha1)
370
## mutter("no text to check for %r %r" % (file_id, kind))
371
text_modified = False
373
# TODO: Can possibly avoid calculating path strings if the
374
# two files are unchanged and their names and parents are
375
# the same and the parents are unchanged all the way up.
376
# May not be worthwhile.
378
if old_path != new_path:
379
delta.renamed.append((old_path, new_path, file_id, kind,
382
delta.modified.append((new_path, file_id, kind))
384
delta.unchanged.append((new_path, file_id, kind))
386
kind = old_inv.get_file_kind(file_id)
387
old_path = old_inv.id2path(file_id)
389
if not is_inside_any(specific_files, old_path):
391
delta.removed.append((old_path, file_id, kind))
393
mutter('start looking for new files')
394
for file_id in new_inv:
395
if file_id in old_inv:
397
new_path = new_inv.id2path(file_id)
399
if not is_inside_any(specific_files, new_path):
401
kind = new_inv.get_file_kind(file_id)
402
delta.added.append((new_path, file_id, kind))
407
delta.modified.sort()
408
delta.unchanged.sort()