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 tmax = time.time() - 1
53 ri = index.Reader(indexfile)
54 wi = index.Writer(indexfile, tmax)
55 rig = IterHelper(ri.iter(name=top))
56 tstart = int(time.time())
61 return (GIT_MODE_FILE, index.FAKE_SHA)
64 bup_dir = os.path.abspath(git.repo())
65 for (path,pst) in drecurse.recursive_dirlist([top], xdev=opt.xdev,
67 excluded_paths=excluded_paths):
68 if opt.verbose>=2 or (opt.verbose==1 and stat.S_ISDIR(pst.st_mode)):
69 sys.stdout.write('%s\n' % path)
71 qprogress('Indexing: %d\r' % total)
72 elif not (total % 128):
73 qprogress('Indexing: %d\r' % total)
75 while rig.cur and rig.cur.name > path: # deleted paths
80 if rig.cur and rig.cur.name == path: # paths that already existed
82 rig.cur.from_stat(pst, tstart)
83 if not (rig.cur.flags & index.IX_HASHVALID):
85 (rig.cur.gitmode, rig.cur.sha) = hashgen(path)
86 rig.cur.flags |= index.IX_HASHVALID
92 wi.add(path, pst, hashgen = hashgen)
93 progress('Indexing: %d, done.\n' % total)
101 log('check: before merging: oldfile\n')
103 log('check: before merging: newfile\n')
105 mi = index.Writer(indexfile, tmax)
107 for e in index.merge(ri, wr):
108 # FIXME: shouldn't we remove deleted entries eventually? When?
120 bup index <-p|m|s|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 u,update recursively update the index entries for the given file/dir names (default if no mode is specified)
127 check carefully check index file integrity
129 H,hash print the hash for each object next to its name
130 l,long print more information about each file
131 fake-valid mark all index entries as up-to-date even if they aren't
132 fake-invalid mark all index entries as invalid
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)
137 x,xdev,one-file-system don't cross filesystem boundaries
139 o = options.Options(optspec)
140 (opt, flags, extra) = o.parse(sys.argv[1:])
142 if not (opt.modified or opt['print'] or opt.status or opt.update or opt.check):
144 if (opt.fake_valid or opt.fake_invalid) and not opt.update:
145 o.fatal('--fake-{in,}valid are meaningless without -u')
146 if opt.fake_valid and opt.fake_invalid:
147 o.fatal('--fake-valid is incompatible with --fake-invalid')
149 # FIXME: remove this once we account for timestamp races, i.e. index;
150 # touch new-file; index. It's possible for this to happen quickly
151 # enough that new-file ends up with the same timestamp as the first
152 # index, and then bup will ignore it.
153 tick_start = time.time()
154 time.sleep(1 - (tick_start - int(tick_start)))
156 git.check_repo_or_die()
157 indexfile = opt.indexfile or git.repo('bupindex')
162 log('check: starting initial check.\n')
163 check_index(index.Reader(indexfile))
165 excluded_paths = drecurse.parse_excludes(flags)
167 paths = index.reduce_paths(extra)
171 o.fatal('update mode (-u) requested but no paths given')
172 for (rp,path) in paths:
173 update_index(rp, excluded_paths)
175 if opt['print'] or opt.status or opt.modified:
176 for (name, ent) in index.Reader(indexfile).filter(extra or ['']):
178 and (ent.is_valid() or ent.is_deleted() or not ent.mode)):
184 elif not ent.is_valid():
185 if ent.sha == index.EMPTY_SHA:
192 line += ent.sha.encode('hex') + ' '
194 line += "%7s %7s " % (oct(ent.mode), oct(ent.gitmode))
195 print line + (name or './')
197 if opt.check and (opt['print'] or opt.status or opt.modified or opt.update):
198 log('check: starting final check.\n')
199 check_index(index.Reader(indexfile))
202 log('WARNING: %d errors encountered.\n' % len(saved_errors))