]> arthur.barton.de Git - bup.git/blob - cmd/index-cmd.py
cmd/index: fix documented default value for --indexfile.
[bup.git] / cmd / index-cmd.py
1 #!/usr/bin/env python
2 import sys, stat, time, os
3 from bup import options, git, index, drecurse
4 from bup.helpers import *
5
6
7 def merge_indexes(out, r1, r2):
8     for e in index.MergeIter([r1, r2]):
9         # FIXME: shouldn't we remove deleted entries eventually?  When?
10         out.add_ixentry(e)
11
12
13 class IterHelper:
14     def __init__(self, l):
15         self.i = iter(l)
16         self.cur = None
17         self.next()
18
19     def next(self):
20         try:
21             self.cur = self.i.next()
22         except StopIteration:
23             self.cur = None
24         return self.cur
25
26
27 def check_index(reader):
28     try:
29         log('check: checking forward iteration...\n')
30         e = None
31         d = {}
32         for e in reader.forward_iter():
33             if e.children_n:
34                 if opt.verbose:
35                     log('%08x+%-4d %r\n' % (e.children_ofs, e.children_n,
36                                             e.name))
37                 assert(e.children_ofs)
38                 assert(e.name.endswith('/'))
39                 assert(not d.get(e.children_ofs))
40                 d[e.children_ofs] = 1
41             if e.flags & index.IX_HASHVALID:
42                 assert(e.sha != index.EMPTY_SHA)
43                 assert(e.gitmode)
44         assert(not e or e.name == '/')  # last entry is *always* /
45         log('check: checking normal iteration...\n')
46         last = None
47         for e in reader:
48             if last:
49                 assert(last > e.name)
50             last = e.name
51     except:
52         log('index error! at %r\n' % e)
53         raise
54     log('check: passed.\n')
55
56
57 def update_index(top):
58     ri = index.Reader(indexfile)
59     wi = index.Writer(indexfile)
60     rig = IterHelper(ri.iter(name=top))
61     tstart = int(time.time())
62
63     hashgen = None
64     if opt.fake_valid:
65         def hashgen(name):
66             return (0100644, index.FAKE_SHA)
67
68     total = 0
69     bup_dir = os.path.abspath(git.repo())
70     for (path,pst) in drecurse.recursive_dirlist([top], xdev=opt.xdev,
71                                                  bup_dir=bup_dir):
72         if opt.verbose>=2 or (opt.verbose==1 and stat.S_ISDIR(pst.st_mode)):
73             sys.stdout.write('%s\n' % path)
74             sys.stdout.flush()
75             progress('Indexing: %d\r' % total)
76         elif not (total % 128):
77             progress('Indexing: %d\r' % total)
78         total += 1
79         while rig.cur and rig.cur.name > path:  # deleted paths
80             if rig.cur.exists():
81                 rig.cur.set_deleted()
82                 rig.cur.repack()
83             rig.next()
84         if rig.cur and rig.cur.name == path:    # paths that already existed
85             if pst:
86                 rig.cur.from_stat(pst, tstart)
87             if not (rig.cur.flags & index.IX_HASHVALID):
88                 if hashgen:
89                     (rig.cur.gitmode, rig.cur.sha) = hashgen(path)
90                     rig.cur.flags |= index.IX_HASHVALID
91             if opt.fake_invalid:
92                 rig.cur.invalidate()
93             rig.cur.repack()
94             rig.next()
95         else:  # new paths
96             wi.add(path, pst, hashgen = hashgen)
97     progress('Indexing: %d, done.\n' % total)
98     
99     if ri.exists():
100         ri.save()
101         wi.flush()
102         if wi.count:
103             wr = wi.new_reader()
104             if opt.check:
105                 log('check: before merging: oldfile\n')
106                 check_index(ri)
107                 log('check: before merging: newfile\n')
108                 check_index(wr)
109             mi = index.Writer(indexfile)
110             merge_indexes(mi, ri, wr)
111             ri.close()
112             mi.close()
113             wr.close()
114         wi.abort()
115     else:
116         wi.close()
117
118
119 optspec = """
120 bup index <-p|m|u> [options...] <filenames...>
121 --
122 p,print    print the index entries for the given names (also works with -u)
123 m,modified print only added/deleted/modified files (implies -p)
124 s,status   print each filename with a status char (A/M/D) (implies -p)
125 H,hash     print the hash for each object next to its name (implies -p)
126 l,long     print more information about each file
127 u,update   (recursively) update the index entries for the given filenames
128 x,xdev,one-file-system  don't cross filesystem boundaries
129 fake-valid mark all index entries as up-to-date even if they aren't
130 fake-invalid mark all index entries as invalid
131 check      carefully check index file integrity
132 f,indexfile=  the name of the index file (normally BUP_DIR/bupindex)
133 v,verbose  increase log output (can be used more than once)
134 """
135 o = options.Options('bup index', optspec)
136 (opt, flags, extra) = o.parse(sys.argv[1:])
137
138 if not (opt.modified or opt['print'] or opt.status or opt.update or opt.check):
139     o.fatal('supply one or more of -p, -s, -m, -u, or --check')
140 if (opt.fake_valid or opt.fake_invalid) and not opt.update:
141     o.fatal('--fake-{in,}valid are meaningless without -u')
142 if opt.fake_valid and opt.fake_invalid:
143     o.fatal('--fake-valid is incompatible with --fake-invalid')
144
145 git.check_repo_or_die()
146 indexfile = opt.indexfile or git.repo('bupindex')
147
148 handle_ctrl_c()
149
150 if opt.check:
151     log('check: starting initial check.\n')
152     check_index(index.Reader(indexfile))
153
154 paths = index.reduce_paths(extra)
155
156 if opt.update:
157     if not extra:
158         o.fatal('update (-u) requested but no paths given')
159     for (rp,path) in paths:
160         update_index(rp)
161
162 if opt['print'] or opt.status or opt.modified:
163     for (name, ent) in index.Reader(indexfile).filter(extra or ['']):
164         if (opt.modified 
165             and (ent.is_valid() or ent.is_deleted() or not ent.mode)):
166             continue
167         line = ''
168         if opt.status:
169             if ent.is_deleted():
170                 line += 'D '
171             elif not ent.is_valid():
172                 if ent.sha == index.EMPTY_SHA:
173                     line += 'A '
174                 else:
175                     line += 'M '
176             else:
177                 line += '  '
178         if opt.hash:
179             line += ent.sha.encode('hex') + ' '
180         if opt.long:
181             line += "%7s %7s " % (oct(ent.mode), oct(ent.gitmode))
182         print line + (name or './')
183
184 if opt.check and (opt['print'] or opt.status or opt.modified or opt.update):
185     log('check: starting final check.\n')
186     check_index(index.Reader(indexfile))
187
188 if saved_errors:
189     log('WARNING: %d errors encountered.\n' % len(saved_errors))
190     sys.exit(1)