143
145
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
def show_diff(b, revision, specific_files, external_diff_options=None):
150
151
"""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.
157
None for each, or otherwise the old revision to compare against.
207
159
The more general form is show_diff_trees(), where the caller
208
160
supplies any two trees.
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)
165
old_tree = b.basis_tree()
167
old_tree = b.revision_tree(b.lookup_revision(revision))
169
new_tree = b.working_tree()
171
show_diff_trees(old_tree, new_tree, sys.stdout, specific_files,
172
external_diff_options)
229
176
def show_diff_trees(old_tree, new_tree, to_file, specific_files=None,
275
205
diff_file = internal_diff
277
208
delta = compare_trees(old_tree, new_tree, want_unchanged=False,
278
209
specific_files=specific_files)
281
211
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)
212
print '*** removed %s %r' % (kind, path)
214
diff_file(old_label + path,
215
old_tree.get_file(file_id).readlines(),
286
220
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,
221
print '*** added %s %r' % (kind, path)
226
new_tree.get_file(file_id).readlines(),
229
for old_path, new_path, file_id, kind, text_modified in delta.renamed:
230
print '*** renamed %s %r => %r' % (kind, old_path, new_path)
306
231
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))
232
diff_file(old_label + old_path,
233
old_tree.get_file(file_id).readlines(),
234
new_label + new_path,
235
new_tree.get_file(file_id).readlines(),
238
for path, file_id, kind in delta.modified:
239
print '*** modified %s %r' % (kind, path)
241
diff_file(old_label + path,
242
old_tree.get_file(file_id).readlines(),
244
new_tree.get_file(file_id).readlines(),
249
class TreeDelta(object):
250
"""Describes changes from one tree to another.
259
(oldpath, newpath, id, kind, text_modified)
265
Each id is listed only once.
267
Files that are both modified and renamed are listed only in
268
renamed, with the text_modified flag true.
270
The lists are normally sorted when the delta is created.
280
def touches_file_id(self, file_id):
281
"""Return True if file_id is modified by this delta."""
282
for l in self.added, self.removed, self.modified:
286
for v in self.renamed:
292
def show(self, to_file, show_ids=False, show_unchanged=False):
293
def show_list(files):
294
for path, fid, kind in files:
295
if kind == 'directory':
297
elif kind == 'symlink':
301
print >>to_file, ' %-30s %s' % (path, fid)
303
print >>to_file, ' ', path
306
print >>to_file, 'removed:'
307
show_list(self.removed)
310
print >>to_file, 'added:'
311
show_list(self.added)
314
print >>to_file, 'renamed:'
315
for oldpath, newpath, fid, kind, text_modified in self.renamed:
317
print >>to_file, ' %s => %s %s' % (oldpath, newpath, fid)
319
print >>to_file, ' %s => %s' % (oldpath, newpath)
322
print >>to_file, 'modified:'
323
show_list(self.modified)
325
if show_unchanged and self.unchanged:
326
print >>to_file, 'unchanged:'
327
show_list(self.unchanged)
331
def compare_trees(old_tree, new_tree, want_unchanged, specific_files=None):
332
"""Describe changes from one tree to another.
334
Returns a TreeDelta with details of added, modified, renamed, and
337
The root entry is specifically exempt.
339
This only considers versioned files.
342
If true, also list files unchanged from one version to
346
If true, only check for changes to specified names or
350
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,
352
old_inv = old_tree.inventory
353
new_inv = new_tree.inventory
355
mutter('start compare_trees')
357
# TODO: match for specific files can be rather smarter by finding
358
# the IDs of those files up front and then considering only that.
360
for file_id in old_tree:
361
if file_id in new_tree:
362
kind = old_inv.get_file_kind(file_id)
363
assert kind == new_inv.get_file_kind(file_id)
365
assert kind in ('file', 'directory', 'symlink', 'root_directory'), \
366
'invalid file kind %r' % kind
368
if kind == 'root_directory':
371
old_path = old_inv.id2path(file_id)
372
new_path = new_inv.id2path(file_id)
375
if (not is_inside_any(specific_files, old_path)
376
and not is_inside_any(specific_files, new_path)):
380
old_sha1 = old_tree.get_file_sha1(file_id)
381
new_sha1 = new_tree.get_file_sha1(file_id)
382
text_modified = (old_sha1 != new_sha1)
384
## mutter("no text to check for %r %r" % (file_id, kind))
385
text_modified = False
387
# TODO: Can possibly avoid calculating path strings if the
388
# two files are unchanged and their names and parents are
389
# the same and the parents are unchanged all the way up.
390
# May not be worthwhile.
392
if old_path != new_path:
393
delta.renamed.append((old_path, new_path, file_id, kind,
396
delta.modified.append((new_path, file_id, kind))
398
delta.unchanged.append((new_path, file_id, kind))
400
kind = old_inv.get_file_kind(file_id)
401
old_path = old_inv.id2path(file_id)
403
if not is_inside_any(specific_files, old_path):
405
delta.removed.append((old_path, file_id, kind))
407
mutter('start looking for new files')
408
for file_id in new_inv:
409
if file_id in old_inv:
411
new_path = new_inv.id2path(file_id)
413
if not is_inside_any(specific_files, new_path):
415
kind = new_inv.get_file_kind(file_id)
416
delta.added.append((new_path, file_id, kind))
421
delta.modified.sort()
422
delta.unchanged.sort()