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
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, tmax)
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 # FIXME: remove this once we account for timestamp races, i.e. index;
149 # touch new-file; index. It's possible for this to happen quickly
150 # enough that new-file ends up with the same timestamp as the first
151 # index, and then bup will ignore it.
152 tick_start = time.time()
153 time.sleep(1 - (tick_start - int(tick_start)))
155 git.check_repo_or_die()
156 indexfile = opt.indexfile or git.repo('bupindex')
161 log('check: starting initial check.\n')
162 check_index(index.Reader(indexfile))
164 excluded_paths = drecurse.parse_excludes(flags)
166 paths = index.reduce_paths(extra)
170 o.fatal('update mode (-u) requested but no paths given')
171 for (rp,path) in paths:
172 update_index(rp, excluded_paths)
174 if opt['print'] or opt.status or opt.modified:
175 for (name, ent) in index.Reader(indexfile).filter(extra or ['']):
177 and (ent.is_valid() or ent.is_deleted() or not ent.mode)):
183 elif not ent.is_valid():
184 if ent.sha == index.EMPTY_SHA:
191 line += ent.sha.encode('hex') + ' '
193 line += "%7s %7s " % (oct(ent.mode), oct(ent.gitmode))
194 print line + (name or './')
196 if opt.check and (opt['print'] or opt.status or opt.modified or opt.update):
197 log('check: starting final check.\n')
198 check_index(index.Reader(indexfile))
201 log('WARNING: %d errors encountered.\n' % len(saved_errors))