~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/revfile.py

  • Committer: Martin Pool
  • Date: 2005-09-09 09:44:03 UTC
  • Revision ID: mbp@sourcefrog.net-20050909094403-ddad5896b0b12c68
- weave commit records per-file ancestors

 - commits of merges are currently forbidden

 - files that existed in the previous revision are recorded with that 
   parent

 'weave annotate' on woven files now gives the correct result!

Show diffs side-by-side

added added

removed removed

Lines of Context:
89
89
# if there are gaps and that can happen if we're interrupted while
90
90
# writing to the datafile.  Overlapping would be very bad though.
91
91
 
92
 
 
 
92
# TODO: Shouldn't need to lock if we always write in append mode and
 
93
# then ftell after writing to see where it went.  In any case we
 
94
# assume the whole branch is protected by a lock.
93
95
 
94
96
import sys, zlib, struct, mdiff, stat, os, sha
95
97
from binascii import hexlify, unhexlify
96
98
 
97
 
factor = 10
98
 
 
99
99
_RECORDSIZE = 48
100
100
 
101
101
_HEADER = "bzr revfile v1\n"
112
112
FL_GZIP = 1
113
113
 
114
114
# maximum number of patches in a row before recording a whole text.
115
 
CHAIN_LIMIT = 50
 
115
CHAIN_LIMIT = 10
116
116
 
117
117
 
118
118
class RevfileError(Exception):
121
121
class LimitHitException(Exception):
122
122
    pass
123
123
 
124
 
class Revfile:
 
124
class Revfile(object):
125
125
    def __init__(self, basename, mode):
126
126
        # TODO: Lock file  while open
127
127
 
149
149
            self.idxfile = open(idxname, 'w+b')
150
150
            self.datafile = open(dataname, 'w+b')
151
151
            
152
 
            print 'init empty file'
153
152
            self.idxfile.write(_HEADER)
154
153
            self.idxfile.flush()
155
154
        else:
243
242
        return self._add_compressed(text_sha, text, _NO_RECORD, compress)
244
243
 
245
244
 
 
245
    # NOT USED
 
246
    def _choose_base(self, seed, base):
 
247
        while seed & 3 == 3:
 
248
            if base == _NO_RECORD:
 
249
                return _NO_RECORD
 
250
            idxrec = self[base]
 
251
            if idxrec[I_BASE] == _NO_RECORD:
 
252
                return base
 
253
 
 
254
            base = idxrec[I_BASE]
 
255
            seed >>= 2
 
256
                
 
257
        return base        # relative to this full text
 
258
        
 
259
 
 
260
 
246
261
    def _add_delta(self, text, text_sha, base, compress):
247
262
        """Add a text stored relative to a previous text."""
248
263
        self._check_index(base)
249
 
        
 
264
 
250
265
        try:
251
 
            base_text = self.get(base, recursion_limit=CHAIN_LIMIT)
 
266
            base_text = self.get(base, CHAIN_LIMIT)
252
267
        except LimitHitException:
253
268
            return self._add_full_text(text, text_sha, compress)
254
269
        
255
270
        data = mdiff.bdiff(base_text, text)
 
271
 
 
272
 
 
273
        if True: # paranoid early check for bad diff
 
274
            result = mdiff.bpatch(base_text, data)
 
275
            assert result == text
 
276
            
256
277
        
257
278
        # If the delta is larger than the text, we might as well just
258
279
        # store the text.  (OK, the delta might be more compressible,
264
285
            return self._add_compressed(text_sha, data, base, compress)
265
286
 
266
287
 
267
 
    def add(self, text, base=_NO_RECORD, compress=True):
 
288
    def add(self, text, base=None, compress=True):
268
289
        """Add a new text to the revfile.
269
290
 
270
291
        If the text is already present them its existing id is
278
299
        only be used if it would be a size win and if the existing
279
300
        base is not at too long of a delta chain already.
280
301
        """
 
302
        if base == None:
 
303
            base = _NO_RECORD
 
304
        
281
305
        self._check_write()
282
306
        
283
307
        text_sha = sha.new(text).digest()
288
312
            # it's the same, in case someone ever breaks SHA-1.
289
313
            return idx                  # already present
290
314
        
 
315
        # base = self._choose_base(ord(text_sha[0]), base)
 
316
 
291
317
        if base == _NO_RECORD:
292
318
            return self._add_full_text(text, text_sha, compress)
293
319
        else:
312
338
            text = self._get_patched(idx, idxrec, recursion_limit)
313
339
 
314
340
        if sha.new(text).digest() != idxrec[I_SHA]:
315
 
            raise RevfileError("corrupt SHA-1 digest on record %d"
316
 
                               % idx)
 
341
            raise RevfileError("corrupt SHA-1 digest on record %d in %s"
 
342
                               % (idx, self.basename))
317
343
 
318
344
        return text
319
345
 
388
414
        self._seek_index(idx)
389
415
        idxrec = self._read_next_index()
390
416
        if idxrec == None:
391
 
            raise IndexError()
 
417
            raise IndexError("no index %d" % idx)
392
418
        else:
393
419
            return idxrec
394
420
 
404
430
        """Read back all index records.
405
431
 
406
432
        Do not seek the index file while this is underway!"""
407
 
        sys.stderr.write(" ** iter called ** \n")
 
433
        ## sys.stderr.write(" ** iter called ** \n")
408
434
        self._seek_index(0)
409
435
        while True:
410
436
            idxrec = self._read_next_index()
452
478
        for idx in range(len(self)):
453
479
            t += len(self.get(idx))
454
480
        return t
 
481
 
 
482
 
 
483
    def check(self, pb=None):
 
484
        """Extract every version and check its hash."""
 
485
        total = len(self)
 
486
        for i in range(total):
 
487
            if pb:
 
488
                pb.update("check revision", i, total)
 
489
            # the get method implicitly checks the SHA-1
 
490
            self.get(i)
 
491
        if pb:
 
492
            pb.clear()
455
493
        
456
494
 
457
495
 
458
496
def main(argv):
459
497
    try:
460
498
        cmd = argv[1]
 
499
        filename = argv[2]
461
500
    except IndexError:
462
 
        sys.stderr.write("usage: revfile dump\n"
463
 
                         "       revfile add\n"
464
 
                         "       revfile add-delta BASE\n"
465
 
                         "       revfile get IDX\n"
466
 
                         "       revfile find-sha HEX\n"
467
 
                         "       revfile total-text-size\n"
468
 
                         "       revfile last\n")
 
501
        sys.stderr.write("usage: revfile dump REVFILE\n"
 
502
                         "       revfile add REVFILE < INPUT\n"
 
503
                         "       revfile add-delta REVFILE BASE < INPUT\n"
 
504
                         "       revfile add-series REVFILE BASE FILE...\n"
 
505
                         "       revfile get REVFILE IDX\n"
 
506
                         "       revfile find-sha REVFILE HEX\n"
 
507
                         "       revfile total-text-size REVFILE\n"
 
508
                         "       revfile last REVFILE\n")
469
509
        return 1
470
510
 
 
511
    if filename.endswith('.drev') or filename.endswith('.irev'):
 
512
        filename = filename[:-5]
 
513
 
471
514
    def rw():
472
 
        return Revfile('testrev', 'w')
 
515
        return Revfile(filename, 'w')
473
516
 
474
517
    def ro():
475
 
        return Revfile('testrev', 'r')
 
518
        return Revfile(filename, 'r')
476
519
 
477
520
    if cmd == 'add':
478
521
        print rw().add(sys.stdin.read())
479
522
    elif cmd == 'add-delta':
480
 
        print rw().add(sys.stdin.read(), int(argv[2]))
 
523
        print rw().add(sys.stdin.read(), int(argv[3]))
 
524
    elif cmd == 'add-series':
 
525
        r = rw()
 
526
        rev = int(argv[3])
 
527
        for fn in argv[4:]:
 
528
            print rev
 
529
            rev = r.add(file(fn).read(), rev)
481
530
    elif cmd == 'dump':
482
531
        ro().dump()
483
532
    elif cmd == 'get':
484
533
        try:
485
 
            idx = int(argv[2])
 
534
            idx = int(argv[3])
486
535
        except IndexError:
487
 
            sys.stderr.write("usage: revfile get IDX\n")
 
536
            sys.stderr.write("usage: revfile get FILE IDX\n")
488
537
            return 1
489
538
 
 
539
        r = ro()
 
540
 
490
541
        if idx < 0 or idx >= len(r):
491
542
            sys.stderr.write("invalid index %r\n" % idx)
492
543
            return 1
493
544
 
494
 
        sys.stdout.write(ro().get(idx))
 
545
        sys.stdout.write(r.get(idx))
495
546
    elif cmd == 'find-sha':
496
547
        try:
497
 
            s = unhexlify(argv[2])
 
548
            s = unhexlify(argv[3])
498
549
        except IndexError:
499
 
            sys.stderr.write("usage: revfile find-sha HEX\n")
 
550
            sys.stderr.write("usage: revfile find-sha FILE HEX\n")
500
551
            return 1
501
552
 
502
553
        idx = ro().find_sha(s)
509
560
        print ro().total_text_size()
510
561
    elif cmd == 'last':
511
562
        print len(ro())-1
 
563
    elif cmd == 'check':
 
564
        import bzrlib.progress
 
565
        pb = bzrlib.progress.ProgressBar()
 
566
        ro().check(pb)
512
567
    else:
513
568
        sys.stderr.write("unknown command %r\n" % cmd)
514
569
        return 1