/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/diff.py

  • Committer: Martin Pool
  • Date: 2005-06-28 03:02:31 UTC
  • Revision ID: mbp@sourcefrog.net-20050628030231-d311e4ebcd467ef4
Merge John's import-speedup branch:

                                                                                         
  777 John Arbash Meinel <john@arbash-meinel.com>       Sun 2005-06-26 22:20:32 -0500
      revision-id: john@arbash-meinel.com-20050627032031-e82a50db3863b18e
      bzr selftest was not using the correct bzr

  776 John Arbash Meinel <john@arbash-meinel.com>       Sun 2005-06-26 22:20:22 -0500
      revision-id: john@arbash-meinel.com-20050627032021-c9f21fde989ddaee
      Add was using an old mutter

  775 John Arbash Meinel <john@arbash-meinel.com>       Sun 2005-06-26 22:02:33 -0500
      revision-id: john@arbash-meinel.com-20050627030233-9165cfe98fc63298
      Cleaned up to be less different

  774 John Arbash Meinel <john@arbash-meinel.com>       Sun 2005-06-26 21:54:53 -0500
      revision-id: john@arbash-meinel.com-20050627025452-4260d0e744edef43
      Allow BZR_PLUGIN_PATH='' to negate plugin loading.

  773 John Arbash Meinel <john@arbash-meinel.com>       Sun 2005-06-26 21:49:34 -0500
      revision-id: john@arbash-meinel.com-20050627024933-b7158f67b7b9eae5
      Finished the previous cleanup (allowing load_plugins to be called twice)

  772 John Arbash Meinel <john@arbash-meinel.com>       Sun 2005-06-26 21:45:08 -0500
      revision-id: john@arbash-meinel.com-20050627024508-723b1df510d196fc
      Work on making the tests pass. versioning.py is calling run_cmd directly, but plugins have been loaded.

  771 John Arbash Meinel <john@arbash-meinel.com>       Sun 2005-06-26 21:32:29 -0500
      revision-id: john@arbash-meinel.com-20050627023228-79972744d7c53e15
      Got it down a little bit more by removing import of tree and inventory.

  770 John Arbash Meinel <john@arbash-meinel.com>       Sun 2005-06-26 21:26:05 -0500
      revision-id: john@arbash-meinel.com-20050627022604-350b9773ef622f95
      Reducing the number of import from bzrlib/__init__.py and bzrlib/branch.py

  769 John Arbash Meinel <john@arbash-meinel.com>       Sun 2005-06-26 20:32:25 -0500
      revision-id: john@arbash-meinel.com-20050627013225-32dd044f10d23948
      Updated revision.py and xml.py to include SubElement.

  768 John Arbash Meinel <john@arbash-meinel.com>       Sun 2005-06-26 20:03:56 -0500
      revision-id: john@arbash-meinel.com-20050627010356-ee66919e1c377faf
      Minor typo

  767 John Arbash Meinel <john@arbash-meinel.com>       Sun 2005-06-26 20:03:13 -0500
      revision-id: john@arbash-meinel.com-20050627010312-40d024007eb85051
      Caching the import

  766 John Arbash Meinel <john@arbash-meinel.com>       Sun 2005-06-26 19:51:47 -0500
      revision-id: john@arbash-meinel.com-20050627005147-5281c99e48ed1834
      Created wrapper functions for lazy import of ElementTree

  765 John Arbash Meinel <john@arbash-meinel.com>       Sun 2005-06-26 19:46:37 -0500
      revision-id: john@arbash-meinel.com-20050627004636-bf432902004a94c5
      Removed all of the test imports of cElementTree

  764 John Arbash Meinel <john@arbash-meinel.com>       Sun 2005-06-26 19:43:59 -0500
      revision-id: john@arbash-meinel.com-20050627004358-d137fbe9570dd71b
      Trying to make bzr startup faster.

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
17
17
 
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
 
20
 
21
21
 
22
22
# TODO: Rather than building a changeset object, we should probably
23
23
# invoke callbacks on an object.  That object can either accumulate a
42
42
    if not oldlines and not newlines:
43
43
        return
44
44
 
 
45
    nonl = False
 
46
 
 
47
    if oldlines and (oldlines[-1][-1] != '\n'):
 
48
        oldlines[-1] += '\n'
 
49
        nonl = True
 
50
    if newlines and (newlines[-1][-1] != '\n'):
 
51
        newlines[-1] += '\n'
 
52
        nonl = True
 
53
 
45
54
    ud = difflib.unified_diff(oldlines, newlines,
46
55
                              fromfile=old_label, tofile=new_label)
47
56
 
54
63
        ud = list(ud)
55
64
        ud[2] = ud[2].replace('+1,0', '+0,0')
56
65
 
57
 
    for line in ud:
58
 
        to_file.write(line)
59
 
        if not line.endswith('\n'):
60
 
            to_file.write("\n\\ No newline at end of file\n")
 
66
    to_file.writelines(ud)
 
67
    if nonl:
 
68
        print >>to_file, "\\ No newline at end of file"
61
69
    print >>to_file
62
70
 
63
71
 
242
250
 
243
251
 
244
252
 
245
 
 
246
 
 
 
253
class TreeDelta(object):
 
254
    """Describes changes from one tree to another.
 
255
 
 
256
    Contains four lists:
 
257
 
 
258
    added
 
259
        (path, id, kind)
 
260
    removed
 
261
        (path, id, kind)
 
262
    renamed
 
263
        (oldpath, newpath, id, kind, text_modified)
 
264
    modified
 
265
        (path, id, kind)
 
266
    unchanged
 
267
        (path, id, kind)
 
268
 
 
269
    Each id is listed only once.
 
270
 
 
271
    Files that are both modified and renamed are listed only in
 
272
    renamed, with the text_modified flag true.
 
273
 
 
274
    Files are only considered renamed if their name has changed or
 
275
    their parent directory has changed.  Renaming a directory
 
276
    does not count as renaming all its contents.
 
277
 
 
278
    The lists are normally sorted when the delta is created.
 
279
    """
 
280
    def __init__(self):
 
281
        self.added = []
 
282
        self.removed = []
 
283
        self.renamed = []
 
284
        self.modified = []
 
285
        self.unchanged = []
 
286
 
 
287
    def __eq__(self, other):
 
288
        if not isinstance(other, TreeDelta):
 
289
            return False
 
290
        return self.added == other.added \
 
291
               and self.removed == other.removed \
 
292
               and self.renamed == other.renamed \
 
293
               and self.modified == other.modified \
 
294
               and self.unchanged == other.unchanged
 
295
 
 
296
    def __ne__(self, other):
 
297
        return not (self == other)
 
298
 
 
299
    def __repr__(self):
 
300
        return "TreeDelta(added=%r, removed=%r, renamed=%r, modified=%r," \
 
301
            " unchanged=%r)" % (self.added, self.removed, self.renamed,
 
302
            self.modified, self.unchanged)
 
303
 
 
304
    def has_changed(self):
 
305
        changes = len(self.added) + len(self.removed) + len(self.renamed)
 
306
        changes += len(self.modified) 
 
307
        return (changes != 0)
 
308
 
 
309
    def touches_file_id(self, file_id):
 
310
        """Return True if file_id is modified by this delta."""
 
311
        for l in self.added, self.removed, self.modified:
 
312
            for v in l:
 
313
                if v[1] == file_id:
 
314
                    return True
 
315
        for v in self.renamed:
 
316
            if v[2] == file_id:
 
317
                return True
 
318
        return False
 
319
            
 
320
 
 
321
    def show(self, to_file, show_ids=False, show_unchanged=False):
 
322
        def show_list(files):
 
323
            for path, fid, kind in files:
 
324
                if kind == 'directory':
 
325
                    path += '/'
 
326
                elif kind == 'symlink':
 
327
                    path += '@'
 
328
                    
 
329
                if show_ids:
 
330
                    print >>to_file, '  %-30s %s' % (path, fid)
 
331
                else:
 
332
                    print >>to_file, ' ', path
 
333
            
 
334
        if self.removed:
 
335
            print >>to_file, 'removed:'
 
336
            show_list(self.removed)
 
337
                
 
338
        if self.added:
 
339
            print >>to_file, 'added:'
 
340
            show_list(self.added)
 
341
 
 
342
        if self.renamed:
 
343
            print >>to_file, 'renamed:'
 
344
            for oldpath, newpath, fid, kind, text_modified in self.renamed:
 
345
                if show_ids:
 
346
                    print >>to_file, '  %s => %s %s' % (oldpath, newpath, fid)
 
347
                else:
 
348
                    print >>to_file, '  %s => %s' % (oldpath, newpath)
 
349
                    
 
350
        if self.modified:
 
351
            print >>to_file, 'modified:'
 
352
            show_list(self.modified)
 
353
            
 
354
        if show_unchanged and self.unchanged:
 
355
            print >>to_file, 'unchanged:'
 
356
            show_list(self.unchanged)
 
357
 
 
358
 
 
359
 
 
360
def compare_trees(old_tree, new_tree, want_unchanged=False, specific_files=None):
 
361
    """Describe changes from one tree to another.
 
362
 
 
363
    Returns a TreeDelta with details of added, modified, renamed, and
 
364
    deleted entries.
 
365
 
 
366
    The root entry is specifically exempt.
 
367
 
 
368
    This only considers versioned files.
 
369
 
 
370
    want_unchanged
 
371
        If true, also list files unchanged from one version to
 
372
        the next.
 
373
 
 
374
    specific_files
 
375
        If true, only check for changes to specified names or
 
376
        files within them.
 
377
    """
 
378
 
 
379
    from osutils import is_inside_any
 
380
    
 
381
    old_inv = old_tree.inventory
 
382
    new_inv = new_tree.inventory
 
383
    delta = TreeDelta()
 
384
    mutter('start compare_trees')
 
385
 
 
386
    # TODO: match for specific files can be rather smarter by finding
 
387
    # the IDs of those files up front and then considering only that.
 
388
 
 
389
    for file_id in old_tree:
 
390
        if file_id in new_tree:
 
391
            kind = old_inv.get_file_kind(file_id)
 
392
            assert kind == new_inv.get_file_kind(file_id)
 
393
            
 
394
            assert kind in ('file', 'directory', 'symlink', 'root_directory'), \
 
395
                   'invalid file kind %r' % kind
 
396
 
 
397
            if kind == 'root_directory':
 
398
                continue
 
399
            
 
400
            old_path = old_inv.id2path(file_id)
 
401
            new_path = new_inv.id2path(file_id)
 
402
 
 
403
            old_ie = old_inv[file_id]
 
404
            new_ie = new_inv[file_id]
 
405
 
 
406
            if specific_files:
 
407
                if (not is_inside_any(specific_files, old_path) 
 
408
                    and not is_inside_any(specific_files, new_path)):
 
409
                    continue
 
410
 
 
411
            if kind == 'file':
 
412
                old_sha1 = old_tree.get_file_sha1(file_id)
 
413
                new_sha1 = new_tree.get_file_sha1(file_id)
 
414
                text_modified = (old_sha1 != new_sha1)
 
415
            else:
 
416
                ## mutter("no text to check for %r %r" % (file_id, kind))
 
417
                text_modified = False
 
418
 
 
419
            # TODO: Can possibly avoid calculating path strings if the
 
420
            # two files are unchanged and their names and parents are
 
421
            # the same and the parents are unchanged all the way up.
 
422
            # May not be worthwhile.
 
423
            
 
424
            if (old_ie.name != new_ie.name
 
425
                or old_ie.parent_id != new_ie.parent_id):
 
426
                delta.renamed.append((old_path, new_path, file_id, kind,
 
427
                                      text_modified))
 
428
            elif text_modified:
 
429
                delta.modified.append((new_path, file_id, kind))
 
430
            elif want_unchanged:
 
431
                delta.unchanged.append((new_path, file_id, kind))
 
432
        else:
 
433
            kind = old_inv.get_file_kind(file_id)
 
434
            old_path = old_inv.id2path(file_id)
 
435
            if specific_files:
 
436
                if not is_inside_any(specific_files, old_path):
 
437
                    continue
 
438
            delta.removed.append((old_path, file_id, kind))
 
439
 
 
440
    mutter('start looking for new files')
 
441
    for file_id in new_inv:
 
442
        if file_id in old_inv:
 
443
            continue
 
444
        new_path = new_inv.id2path(file_id)
 
445
        if specific_files:
 
446
            if not is_inside_any(specific_files, new_path):
 
447
                continue
 
448
        kind = new_inv.get_file_kind(file_id)
 
449
        delta.added.append((new_path, file_id, kind))
 
450
            
 
451
    delta.removed.sort()
 
452
    delta.added.sort()
 
453
    delta.renamed.sort()
 
454
    delta.modified.sort()
 
455
    delta.unchanged.sort()
 
456
 
 
457
    return delta