~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/revfile.py

  • Committer: Martin Pool
  • Date: 2005-04-28 07:24:55 UTC
  • Revision ID: mbp@sourcefrog.net-20050428072453-7b99afa993a1e549
todo

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
 
# 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.
 
92
 
95
93
 
96
94
import sys, zlib, struct, mdiff, stat, os, sha
97
95
from binascii import hexlify, unhexlify
98
96
 
 
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 = 10
 
115
CHAIN_LIMIT = 50
116
116
 
117
117
 
118
118
class RevfileError(Exception):
121
121
class LimitHitException(Exception):
122
122
    pass
123
123
 
124
 
class Revfile(object):
 
124
class Revfile:
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'
152
153
            self.idxfile.write(_HEADER)
153
154
            self.idxfile.flush()
154
155
        else:
242
243
        return self._add_compressed(text_sha, text, _NO_RECORD, compress)
243
244
 
244
245
 
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
 
 
261
246
    def _add_delta(self, text, text_sha, base, compress):
262
247
        """Add a text stored relative to a previous text."""
263
248
        self._check_index(base)
264
 
 
 
249
        
265
250
        try:
266
 
            base_text = self.get(base, CHAIN_LIMIT)
 
251
            base_text = self.get(base, recursion_limit=CHAIN_LIMIT)
267
252
        except LimitHitException:
268
253
            return self._add_full_text(text, text_sha, compress)
269
254
        
270
255
        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
 
            
277
256
        
278
257
        # If the delta is larger than the text, we might as well just
279
258
        # store the text.  (OK, the delta might be more compressible,
285
264
            return self._add_compressed(text_sha, data, base, compress)
286
265
 
287
266
 
288
 
    def add(self, text, base=None, compress=True):
 
267
    def add(self, text, base=_NO_RECORD, compress=True):
289
268
        """Add a new text to the revfile.
290
269
 
291
270
        If the text is already present them its existing id is
299
278
        only be used if it would be a size win and if the existing
300
279
        base is not at too long of a delta chain already.
301
280
        """
302
 
        if base == None:
303
 
            base = _NO_RECORD
304
 
        
305
281
        self._check_write()
306
282
        
307
283
        text_sha = sha.new(text).digest()
312
288
            # it's the same, in case someone ever breaks SHA-1.
313
289
            return idx                  # already present
314
290
        
315
 
        # base = self._choose_base(ord(text_sha[0]), base)
316
 
 
317
291
        if base == _NO_RECORD:
318
292
            return self._add_full_text(text, text_sha, compress)
319
293
        else:
338
312
            text = self._get_patched(idx, idxrec, recursion_limit)
339
313
 
340
314
        if sha.new(text).digest() != idxrec[I_SHA]:
341
 
            raise RevfileError("corrupt SHA-1 digest on record %d in %s"
342
 
                               % (idx, self.basename))
 
315
            raise RevfileError("corrupt SHA-1 digest on record %d"
 
316
                               % idx)
343
317
 
344
318
        return text
345
319
 
414
388
        self._seek_index(idx)
415
389
        idxrec = self._read_next_index()
416
390
        if idxrec == None:
417
 
            raise IndexError("no index %d" % idx)
 
391
            raise IndexError()
418
392
        else:
419
393
            return idxrec
420
394
 
430
404
        """Read back all index records.
431
405
 
432
406
        Do not seek the index file while this is underway!"""
433
 
        ## sys.stderr.write(" ** iter called ** \n")
 
407
        sys.stderr.write(" ** iter called ** \n")
434
408
        self._seek_index(0)
435
409
        while True:
436
410
            idxrec = self._read_next_index()
478
452
        for idx in range(len(self)):
479
453
            t += len(self.get(idx))
480
454
        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()
493
455
        
494
456
 
495
457
 
496
458
def main(argv):
497
459
    try:
498
460
        cmd = argv[1]
499
 
        filename = argv[2]
500
461
    except IndexError:
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")
 
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")
509
469
        return 1
510
470
 
511
 
    if filename.endswith('.drev') or filename.endswith('.irev'):
512
 
        filename = filename[:-5]
513
 
 
514
471
    def rw():
515
 
        return Revfile(filename, 'w')
 
472
        return Revfile('testrev', 'w')
516
473
 
517
474
    def ro():
518
 
        return Revfile(filename, 'r')
 
475
        return Revfile('testrev', 'r')
519
476
 
520
477
    if cmd == 'add':
521
478
        print rw().add(sys.stdin.read())
522
479
    elif cmd == 'add-delta':
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)
 
480
        print rw().add(sys.stdin.read(), int(argv[2]))
530
481
    elif cmd == 'dump':
531
482
        ro().dump()
532
483
    elif cmd == 'get':
533
484
        try:
534
 
            idx = int(argv[3])
 
485
            idx = int(argv[2])
535
486
        except IndexError:
536
 
            sys.stderr.write("usage: revfile get FILE IDX\n")
 
487
            sys.stderr.write("usage: revfile get IDX\n")
537
488
            return 1
538
489
 
539
 
        r = ro()
540
 
 
541
490
        if idx < 0 or idx >= len(r):
542
491
            sys.stderr.write("invalid index %r\n" % idx)
543
492
            return 1
544
493
 
545
 
        sys.stdout.write(r.get(idx))
 
494
        sys.stdout.write(ro().get(idx))
546
495
    elif cmd == 'find-sha':
547
496
        try:
548
 
            s = unhexlify(argv[3])
 
497
            s = unhexlify(argv[2])
549
498
        except IndexError:
550
 
            sys.stderr.write("usage: revfile find-sha FILE HEX\n")
 
499
            sys.stderr.write("usage: revfile find-sha HEX\n")
551
500
            return 1
552
501
 
553
502
        idx = ro().find_sha(s)
560
509
        print ro().total_text_size()
561
510
    elif cmd == 'last':
562
511
        print len(ro())-1
563
 
    elif cmd == 'check':
564
 
        import bzrlib.progress
565
 
        pb = bzrlib.progress.ProgressBar()
566
 
        ro().check(pb)
567
512
    else:
568
513
        sys.stderr.write("unknown command %r\n" % cmd)
569
514
        return 1