]> arthur.barton.de Git - bup.git/blob - cmd-midx.py
cmd-midx: a command for merging multiple .idx files into one.
[bup.git] / cmd-midx.py
1 #!/usr/bin/env python
2 import sys, math, struct
3 import options, git
4 from helpers import *
5
6 PAGE_SIZE=4096
7 SHA_PER_PAGE=PAGE_SIZE/200.
8
9
10 def next(it):
11     try:
12         return it.next()
13     except StopIteration:
14         return None
15     
16     
17 optspec = """
18 bup midx -o outfile.midx <idxnames...>
19 --
20 o,output=  output midx file name
21 """
22 o = options.Options('bup midx', optspec)
23 (opt, flags, extra) = o.parse(sys.argv[1:])
24
25 if not extra:
26     log("bup midx: no input filenames given\n")
27     o.usage()
28 if not opt.output:
29     log("bup midx: no output filename given\n")
30     o.usage()
31     
32 inp = []
33 total = 0
34 for name in extra:
35     ix = git.PackIndex(name)
36     inp.append(ix)
37     total += len(ix)
38     
39 log('total objects expected: %d\n' % total)
40 pages = total/SHA_PER_PAGE
41 log('pages: %d\n' % pages)
42 bits = int(math.ceil(math.log(pages, 2)))
43 log('table bits: %d\n' % bits)
44 entries = 2**bits
45 log('table entries: %d\n' % entries)
46 log('table size: %d\n' % (entries*8))
47
48 table = [0]*entries
49
50 def merge(idxlist):
51     iters = [iter(i) for i in inp]
52     iters = [[next(it), it] for it in iters]
53     count = 0
54     while iters:
55         if (count % (total/100)) == 0:
56             log('\rMerging: %d%%' % (count*100/total))
57         e = min(iters)  # FIXME: very slow for long lists
58         assert(e[0])
59         yield e[0]
60         count += 1
61         prefix = git.extract_bits(e[0], bits)
62         table[prefix] = count
63         e[0] = next(e[1])
64         iters = filter(lambda x: x[0], iters)
65     log('\rMerging: done.\n')
66
67 f = open(opt.output, 'w+')
68 f.write('MIDX\0\0\0\1')
69 f.write(struct.pack('!I', bits))
70 assert(f.tell() == 12)
71 f.write('\0'*8*entries)
72
73 for e in merge(inp):
74     f.write(e)
75
76 f.write('\0'.join([os.path.basename(p) for p in extra]))
77
78 f.seek(12)
79 f.write(struct.pack('!%dQ' % entries, *table))
80 f.close()
81
82 # this is just for testing
83 if 1:
84     p = git.PackMidx(opt.output)
85     assert(len(p.idxnames) == len(extra))
86     print p.idxnames
87     assert(len(p) == total)
88     pi = iter(p)
89     for i in merge(inp):
90         assert(i == pi.next())
91         assert(p.exists(i))