~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/revfile.py

  • Committer: Robert Collins
  • Date: 2005-10-11 01:05:24 UTC
  • mto: This revision was merged to the branch mainline in revision 1438.
  • Revision ID: robertc@robertcollins.net-20051011010524-e258bc8d051cc9d2
add a cache bound to Transactions, and a precious facility, so that we keep inventory.weave in memory, but can discard weaves for other such files.

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
 
 
93
 
 
94
 
import sys, zlib, struct, mdiff, stat, os, sha
 
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.
 
95
 
 
96
import os
 
97
import sha
 
98
import stat
 
99
import struct
 
100
import sys
 
101
import zlib
95
102
from binascii import hexlify, unhexlify
96
103
 
97
 
factor = 10
 
104
import bzrlib.mdiff as mdiff
 
105
 
98
106
 
99
107
_RECORDSIZE = 48
100
108
 
112
120
FL_GZIP = 1
113
121
 
114
122
# maximum number of patches in a row before recording a whole text.
115
 
CHAIN_LIMIT = 50
 
123
CHAIN_LIMIT = 10
116
124
 
117
125
 
118
126
class RevfileError(Exception):
121
129
class LimitHitException(Exception):
122
130
    pass
123
131
 
124
 
class Revfile:
 
132
class Revfile(object):
125
133
    def __init__(self, basename, mode):
126
134
        # TODO: Lock file  while open
127
135
 
149
157
            self.idxfile = open(idxname, 'w+b')
150
158
            self.datafile = open(dataname, 'w+b')
151
159
            
152
 
            print 'init empty file'
153
160
            self.idxfile.write(_HEADER)
154
161
            self.idxfile.flush()
155
162
        else:
243
250
        return self._add_compressed(text_sha, text, _NO_RECORD, compress)
244
251
 
245
252
 
 
253
    # NOT USED
 
254
    def _choose_base(self, seed, base):
 
255
        while seed & 3 == 3:
 
256
            if base == _NO_RECORD:
 
257
                return _NO_RECORD
 
258
            idxrec = self[base]
 
259
            if idxrec[I_BASE] == _NO_RECORD:
 
260
                return base
 
261
 
 
262
            base = idxrec[I_BASE]
 
263
            seed >>= 2
 
264
                
 
265
        return base        # relative to this full text
 
266
        
 
267
 
 
268
 
246
269
    def _add_delta(self, text, text_sha, base, compress):
247
270
        """Add a text stored relative to a previous text."""
248
271
        self._check_index(base)
249
 
        
 
272
 
250
273
        try:
251
 
            base_text = self.get(base, recursion_limit=CHAIN_LIMIT)
 
274
            base_text = self.get(base, CHAIN_LIMIT)
252
275
        except LimitHitException:
253
276
            return self._add_full_text(text, text_sha, compress)
254
277
        
255
278
        data = mdiff.bdiff(base_text, text)
 
279
 
 
280
 
 
281
        if True: # paranoid early check for bad diff
 
282
            result = mdiff.bpatch(base_text, data)
 
283
            assert result == text
 
284
            
256
285
        
257
286
        # If the delta is larger than the text, we might as well just
258
287
        # store the text.  (OK, the delta might be more compressible,
264
293
            return self._add_compressed(text_sha, data, base, compress)
265
294
 
266
295
 
267
 
    def add(self, text, base=_NO_RECORD, compress=True):
 
296
    def add(self, text, base=None, compress=True):
268
297
        """Add a new text to the revfile.
269
298
 
270
299
        If the text is already present them its existing id is
278
307
        only be used if it would be a size win and if the existing
279
308
        base is not at too long of a delta chain already.
280
309
        """
 
310
        if base == None:
 
311
            base = _NO_RECORD
 
312
        
281
313
        self._check_write()
282
314
        
283
315
        text_sha = sha.new(text).digest()
288
320
            # it's the same, in case someone ever breaks SHA-1.
289
321
            return idx                  # already present
290
322
        
 
323
        # base = self._choose_base(ord(text_sha[0]), base)
 
324
 
291
325
        if base == _NO_RECORD:
292
326
            return self._add_full_text(text, text_sha, compress)
293
327
        else:
312
346
            text = self._get_patched(idx, idxrec, recursion_limit)
313
347
 
314
348
        if sha.new(text).digest() != idxrec[I_SHA]:
315
 
            raise RevfileError("corrupt SHA-1 digest on record %d"
316
 
                               % idx)
 
349
            raise RevfileError("corrupt SHA-1 digest on record %d in %s"
 
350
                               % (idx, self.basename))
317
351
 
318
352
        return text
319
353
 
388
422
        self._seek_index(idx)
389
423
        idxrec = self._read_next_index()
390
424
        if idxrec == None:
391
 
            raise IndexError()
 
425
            raise IndexError("no index %d" % idx)
392
426
        else:
393
427
            return idxrec
394
428
 
404
438
        """Read back all index records.
405
439
 
406
440
        Do not seek the index file while this is underway!"""
407
 
        sys.stderr.write(" ** iter called ** \n")
 
441
        ## sys.stderr.write(" ** iter called ** \n")
408
442
        self._seek_index(0)
409
443
        while True:
410
444
            idxrec = self._read_next_index()
452
486
        for idx in range(len(self)):
453
487
            t += len(self.get(idx))
454
488
        return t
 
489
 
 
490
 
 
491
    def check(self, pb=None):
 
492
        """Extract every version and check its hash."""
 
493
        total = len(self)
 
494
        for i in range(total):
 
495
            if pb:
 
496
                pb.update("check revision", i, total)
 
497
            # the get method implicitly checks the SHA-1
 
498
            self.get(i)
 
499
        if pb:
 
500
            pb.clear()
455
501
        
456
502
 
457
503
 
458
504
def main(argv):
459
505
    try:
460
506
        cmd = argv[1]
 
507
        filename = argv[2]
461
508
    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")
 
509
        sys.stderr.write("usage: revfile dump REVFILE\n"
 
510
                         "       revfile add REVFILE < INPUT\n"
 
511
                         "       revfile add-delta REVFILE BASE < INPUT\n"
 
512
                         "       revfile add-series REVFILE BASE FILE...\n"
 
513
                         "       revfile get REVFILE IDX\n"
 
514
                         "       revfile find-sha REVFILE HEX\n"
 
515
                         "       revfile total-text-size REVFILE\n"
 
516
                         "       revfile last REVFILE\n")
469
517
        return 1
470
518
 
 
519
    if filename.endswith('.drev') or filename.endswith('.irev'):
 
520
        filename = filename[:-5]
 
521
 
471
522
    def rw():
472
 
        return Revfile('testrev', 'w')
 
523
        return Revfile(filename, 'w')
473
524
 
474
525
    def ro():
475
 
        return Revfile('testrev', 'r')
 
526
        return Revfile(filename, 'r')
476
527
 
477
528
    if cmd == 'add':
478
529
        print rw().add(sys.stdin.read())
479
530
    elif cmd == 'add-delta':
480
 
        print rw().add(sys.stdin.read(), int(argv[2]))
 
531
        print rw().add(sys.stdin.read(), int(argv[3]))
 
532
    elif cmd == 'add-series':
 
533
        r = rw()
 
534
        rev = int(argv[3])
 
535
        for fn in argv[4:]:
 
536
            print rev
 
537
            rev = r.add(file(fn).read(), rev)
481
538
    elif cmd == 'dump':
482
539
        ro().dump()
483
540
    elif cmd == 'get':
484
541
        try:
485
 
            idx = int(argv[2])
 
542
            idx = int(argv[3])
486
543
        except IndexError:
487
 
            sys.stderr.write("usage: revfile get IDX\n")
 
544
            sys.stderr.write("usage: revfile get FILE IDX\n")
488
545
            return 1
489
546
 
 
547
        r = ro()
 
548
 
490
549
        if idx < 0 or idx >= len(r):
491
550
            sys.stderr.write("invalid index %r\n" % idx)
492
551
            return 1
493
552
 
494
 
        sys.stdout.write(ro().get(idx))
 
553
        sys.stdout.write(r.get(idx))
495
554
    elif cmd == 'find-sha':
496
555
        try:
497
 
            s = unhexlify(argv[2])
 
556
            s = unhexlify(argv[3])
498
557
        except IndexError:
499
 
            sys.stderr.write("usage: revfile find-sha HEX\n")
 
558
            sys.stderr.write("usage: revfile find-sha FILE HEX\n")
500
559
            return 1
501
560
 
502
561
        idx = ro().find_sha(s)
509
568
        print ro().total_text_size()
510
569
    elif cmd == 'last':
511
570
        print len(ro())-1
 
571
    elif cmd == 'check':
 
572
        import bzrlib.progress
 
573
        pb = bzrlib.progress.ProgressBar()
 
574
        ro().check(pb)
512
575
    else:
513
576
        sys.stderr.write("unknown command %r\n" % cmd)
514
577
        return 1