~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/revfile.py

  • Committer: Martin Pool
  • Date: 2005-05-02 04:24:33 UTC
  • Revision ID: mbp@sourcefrog.net-20050502042433-c825a7f7235f6b15
doc: notes on merge

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