2 import sys, stat, time, os
3 from bup import options, git, index, drecurse
4 from bup.helpers import *
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?
14 def __init__(self, l):
21 self.cur = self.i.next()
27 def check_index(reader):
29 log('check: checking forward iteration...\n')
32 for e in reader.forward_iter():
35 log('%08x+%-4d %r\n' % (e.children_ofs, e.children_n,
37 assert(e.children_ofs)
38 assert(e.name.endswith('/'))
39 assert(not d.get(e.children_ofs))
41 if e.flags & index.IX_HASHVALID:
42 assert(e.sha != index.EMPTY_SHA)
44 assert(not e or e.name == '/') # last entry is *always* /
45 log('check: checking normal iteration...\n')
52 log('index error! at %r\n' % e)
54 log('check: passed.\n')
57 def update_index(top, excluded_paths):
58 ri = index.Reader(indexfile)
59 wi = index.Writer(indexfile)
60 rig = IterHelper(ri.iter(name=top))
61 tstart = int(time.time())
66 return (0100644, index.FAKE_SHA)
69 bup_dir = os.path.abspath(git.repo())
70 for (path,pst) in drecurse.recursive_dirlist([top], xdev=opt.xdev,
72 excluded_paths=excluded_paths):
73 if opt.verbose>=2 or (opt.verbose==1 and stat.S_ISDIR(pst.st_mode)):
74 sys.stdout.write('%s\n' % path)
76 progress('Indexing: %d\r' % total)
77 elif not (total % 128):
78 progress('Indexing: %d\r' % total)
80 while rig.cur and rig.cur.name > path: # deleted paths
85 if rig.cur and rig.cur.name == path: # paths that already existed
87 rig.cur.from_stat(pst, tstart)
88 if not (rig.cur.flags & index.IX_HASHVALID):
90 (rig.cur.gitmode, rig.cur.sha) = hashgen(path)
91 rig.cur.flags |= index.IX_HASHVALID
97 wi.add(path, pst, hashgen = hashgen)
98 progress('Indexing: %d, done.\n' % total)
106 log('check: before merging: oldfile\n')
108 log('check: before merging: newfile\n')
110 mi = index.Writer(indexfile)
111 merge_indexes(mi, ri, wr)
121 bup index <-p|m|u> [options...] <filenames...>
123 p,print print the index entries for the given names (also works with -u)
124 m,modified print only added/deleted/modified files (implies -p)
125 s,status print each filename with a status char (A/M/D) (implies -p)
126 H,hash print the hash for each object next to its name (implies -p)
127 l,long print more information about each file
128 u,update (recursively) update the index entries for the given filenames
129 x,xdev,one-file-system don't cross filesystem boundaries
130 fake-valid mark all index entries as up-to-date even if they aren't
131 fake-invalid mark all index entries as invalid
132 check carefully check index file integrity
133 f,indexfile= the name of the index file (normally BUP_DIR/bupindex)
134 exclude= a path to exclude from the backup (can be used more than once)
135 exclude-from= a file that contains exclude paths (can be used more than once)
136 v,verbose increase log output (can be used more than once)
138 o = options.Options(optspec)
139 (opt, flags, extra) = o.parse(sys.argv[1:])
141 if not (opt.modified or opt['print'] or opt.status or opt.update or opt.check):
142 o.fatal('supply one or more of -p, -s, -m, -u, or --check')
143 if (opt.fake_valid or opt.fake_invalid) and not opt.update:
144 o.fatal('--fake-{in,}valid are meaningless without -u')
145 if opt.fake_valid and opt.fake_invalid:
146 o.fatal('--fake-valid is incompatible with --fake-invalid')
148 git.check_repo_or_die()
149 indexfile = opt.indexfile or git.repo('bupindex')
154 log('check: starting initial check.\n')
155 check_index(index.Reader(indexfile))
157 excluded_paths = drecurse.parse_excludes(flags)
159 paths = index.reduce_paths(extra)
163 o.fatal('update (-u) requested but no paths given')
164 for (rp,path) in paths:
165 update_index(rp, excluded_paths)
167 if opt['print'] or opt.status or opt.modified:
168 for (name, ent) in index.Reader(indexfile).filter(extra or ['']):
170 and (ent.is_valid() or ent.is_deleted() or not ent.mode)):
176 elif not ent.is_valid():
177 if ent.sha == index.EMPTY_SHA:
184 line += ent.sha.encode('hex') + ' '
186 line += "%7s %7s " % (oct(ent.mode), oct(ent.gitmode))
187 print line + (name or './')
189 if opt.check and (opt['print'] or opt.status or opt.modified or opt.update):
190 log('check: starting final check.\n')
191 check_index(index.Reader(indexfile))
194 log('WARNING: %d errors encountered.\n' % len(saved_errors))