2 import sys, stat, time, os
3 from bup import options, git, index, drecurse
4 from bup.helpers import *
5 from bup.hashsplit import GIT_MODE_TREE, GIT_MODE_FILE
15 self.cur = self.i.next()
21 def check_index(reader):
23 log('check: checking forward iteration...\n')
26 for e in reader.forward_iter():
29 log('%08x+%-4d %r\n' % (e.children_ofs, e.children_n,
31 assert(e.children_ofs)
32 assert(e.name.endswith('/'))
33 assert(not d.get(e.children_ofs))
35 if e.flags & index.IX_HASHVALID:
36 assert(e.sha != index.EMPTY_SHA)
38 assert(not e or e.name == '/') # last entry is *always* /
39 log('check: checking normal iteration...\n')
46 log('index error! at %r\n' % e)
48 log('check: passed.\n')
51 def update_index(top, excluded_paths):
52 ri = index.Reader(indexfile)
53 wi = index.Writer(indexfile)
54 rig = IterHelper(ri.iter(name=top))
55 tstart = int(time.time())
60 return (GIT_MODE_FILE, index.FAKE_SHA)
63 bup_dir = os.path.abspath(git.repo())
64 for (path,pst) in drecurse.recursive_dirlist([top], xdev=opt.xdev,
66 excluded_paths=excluded_paths):
67 if opt.verbose>=2 or (opt.verbose==1 and stat.S_ISDIR(pst.st_mode)):
68 sys.stdout.write('%s\n' % path)
70 qprogress('Indexing: %d\r' % total)
71 elif not (total % 128):
72 qprogress('Indexing: %d\r' % total)
74 while rig.cur and rig.cur.name > path: # deleted paths
79 if rig.cur and rig.cur.name == path: # paths that already existed
81 rig.cur.from_stat(pst, tstart)
82 if not (rig.cur.flags & index.IX_HASHVALID):
84 (rig.cur.gitmode, rig.cur.sha) = hashgen(path)
85 rig.cur.flags |= index.IX_HASHVALID
91 wi.add(path, pst, hashgen = hashgen)
92 progress('Indexing: %d, done.\n' % total)
100 log('check: before merging: oldfile\n')
102 log('check: before merging: newfile\n')
104 mi = index.Writer(indexfile)
106 for e in index.merge(ri, wr):
107 # FIXME: shouldn't we remove deleted entries eventually? When?
119 bup index <-p|m|s|u> [options...] <filenames...>
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 u,update recursively update the index entries for the given file/dir names (default if no mode is specified)
126 check carefully check index file integrity
128 H,hash print the hash for each object next to its name
129 l,long print more information about each file
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 f,indexfile= the name of the index file (normally BUP_DIR/bupindex)
133 exclude= a path to exclude from the backup (can be used more than once)
134 exclude-from= a file that contains exclude paths (can be used more than once)
135 v,verbose increase log output (can be used more than once)
136 x,xdev,one-file-system don't cross filesystem boundaries
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):
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 mode (-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))