15
15
# along with this program; if not, write to the Free Software
16
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
from bzrlib.trace import mutter
19
from bzrlib.errors import BzrError
20
from bzrlib.delta import compare_trees
18
from trace import mutter
19
from errors import BzrError
22
# TODO: Rather than building a changeset object, we should probably
23
# invoke callbacks on an object. That object can either accumulate a
24
# list, write them out directly, etc etc.
26
22
def internal_diff(old_label, oldlines, new_label, newlines, to_file):
146
def show_diff(b, revision, specific_files, external_diff_options=None,
148
"""Shortcut for showing the diff to the working tree.
154
None for each, or otherwise the old revision to compare against.
156
The more general form is show_diff_trees(), where the caller
157
supplies any two trees.
150
def show_diff(b, revision, specific_files, external_diff_options=None):
161
153
if revision == None:
162
154
old_tree = b.basis_tree()
164
156
old_tree = b.revision_tree(b.lookup_revision(revision))
166
if revision2 == None:
167
new_tree = b.working_tree()
169
new_tree = b.revision_tree(b.lookup_revision(revision2))
158
new_tree = b.working_tree()
171
160
show_diff_trees(old_tree, new_tree, sys.stdout, specific_files,
172
161
external_diff_options)
238
class TreeDelta(object):
239
"""Describes changes from one tree to another.
248
(oldpath, newpath, id, kind, text_modified)
254
Each id is listed only once.
256
Files that are both modified and renamed are listed only in
257
renamed, with the text_modified flag true.
259
The lists are normally sorted when the delta is created.
269
def touches_file_id(self, file_id):
270
"""Return True if file_id is modified by this delta."""
271
for l in self.added, self.removed, self.modified:
275
for v in self.renamed:
281
def show(self, to_file, show_ids=False, show_unchanged=False):
282
def show_list(files):
283
for path, fid, kind in files:
284
if kind == 'directory':
286
elif kind == 'symlink':
290
print >>to_file, ' %-30s %s' % (path, fid)
292
print >>to_file, ' ', path
295
print >>to_file, 'removed:'
296
show_list(self.removed)
299
print >>to_file, 'added:'
300
show_list(self.added)
303
print >>to_file, 'renamed:'
304
for oldpath, newpath, fid, kind, text_modified in self.renamed:
306
print >>to_file, ' %s => %s %s' % (oldpath, newpath, fid)
308
print >>to_file, ' %s => %s' % (oldpath, newpath)
311
print >>to_file, 'modified:'
312
show_list(self.modified)
314
if show_unchanged and self.unchanged:
315
print >>to_file, 'unchanged:'
316
show_list(self.unchanged)
320
def compare_trees(old_tree, new_tree, want_unchanged, specific_files=None):
321
"""Describe changes from one tree to another.
323
Returns a TreeDelta with details of added, modified, renamed, and
326
The root entry is specifically exempt.
328
This only considers versioned files.
331
If true, also list files unchanged from one version to
335
If true, only check for changes to specified names or
339
from osutils import is_inside_any
341
old_inv = old_tree.inventory
342
new_inv = new_tree.inventory
344
mutter('start compare_trees')
346
# TODO: match for specific files can be rather smarter by finding
347
# the IDs of those files up front and then considering only that.
349
for file_id in old_tree:
350
if file_id in new_tree:
351
kind = old_inv.get_file_kind(file_id)
352
assert kind == new_inv.get_file_kind(file_id)
354
assert kind in ('file', 'directory', 'symlink', 'root_directory'), \
355
'invalid file kind %r' % kind
357
if kind == 'root_directory':
360
old_path = old_inv.id2path(file_id)
361
new_path = new_inv.id2path(file_id)
364
if (not is_inside_any(specific_files, old_path)
365
and not is_inside_any(specific_files, new_path)):
369
old_sha1 = old_tree.get_file_sha1(file_id)
370
new_sha1 = new_tree.get_file_sha1(file_id)
371
text_modified = (old_sha1 != new_sha1)
373
## mutter("no text to check for %r %r" % (file_id, kind))
374
text_modified = False
376
# TODO: Can possibly avoid calculating path strings if the
377
# two files are unchanged and their names and parents are
378
# the same and the parents are unchanged all the way up.
379
# May not be worthwhile.
381
if old_path != new_path:
382
delta.renamed.append((old_path, new_path, file_id, kind,
385
delta.modified.append((new_path, file_id, kind))
387
delta.unchanged.append((new_path, file_id, kind))
389
kind = old_inv.get_file_kind(file_id)
390
old_path = old_inv.id2path(file_id)
392
if not is_inside_any(specific_files, old_path):
394
delta.removed.append((old_path, file_id, kind))
396
mutter('start looking for new files')
397
for file_id in new_inv:
398
if file_id in old_inv:
400
new_path = new_inv.id2path(file_id)
402
if not is_inside_any(specific_files, new_path):
404
kind = new_inv.get_file_kind(file_id)
405
delta.added.append((new_path, file_id, kind))
410
delta.modified.sort()
411
delta.unchanged.sort()