2 import sys, stat, time, os
3 from bup import options, git, index, drecurse, hlinkdb
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 and start must be epoch nanoseconds.
53 tmax = (time.time() - 1) * 10**9
54 ri = index.Reader(indexfile)
55 wi = index.Writer(indexfile, tmax)
56 rig = IterHelper(ri.iter(name=top))
57 tstart = int(time.time()) * 10**9
59 hlinks = hlinkdb.HLinkDB(indexfile + '.hlink')
64 return (GIT_MODE_FILE, index.FAKE_SHA)
67 bup_dir = os.path.abspath(git.repo())
68 for (path,pst) in drecurse.recursive_dirlist([top], xdev=opt.xdev,
70 excluded_paths=excluded_paths):
71 if opt.verbose>=2 or (opt.verbose==1 and stat.S_ISDIR(pst.st_mode)):
72 sys.stdout.write('%s\n' % path)
74 qprogress('Indexing: %d\r' % total)
75 elif not (total % 128):
76 qprogress('Indexing: %d\r' % total)
78 while rig.cur and rig.cur.name > path: # deleted paths
82 if rig.cur.nlink > 1 and not stat.S_ISDIR(rig.cur.mode):
83 hlinks.del_path(rig.cur.name)
85 if rig.cur and rig.cur.name == path: # paths that already existed
86 if not stat.S_ISDIR(rig.cur.mode) and rig.cur.nlink > 1:
87 hlinks.del_path(rig.cur.name)
88 if not stat.S_ISDIR(pst.st_mode) and pst.st_nlink > 1:
89 hlinks.add_path(path, pst.st_dev, pst.st_ino)
90 rig.cur.from_stat(pst, tstart)
91 if not (rig.cur.flags & index.IX_HASHVALID):
93 (rig.cur.gitmode, rig.cur.sha) = hashgen(path)
94 rig.cur.flags |= index.IX_HASHVALID
100 wi.add(path, pst, hashgen = hashgen)
101 if not stat.S_ISDIR(pst.st_mode) and pst.st_nlink > 1:
102 hlinks.add_path(path, pst.st_dev, pst.st_ino)
104 progress('Indexing: %d, done.\n' % total)
106 hlinks.prepare_save()
114 log('check: before merging: oldfile\n')
116 log('check: before merging: newfile\n')
118 mi = index.Writer(indexfile, tmax)
120 for e in index.merge(ri, wr):
121 # FIXME: shouldn't we remove deleted entries eventually? When?
135 bup index <-p|m|s|u> [options...] <filenames...>
138 p,print print the index entries for the given names (also works with -u)
139 m,modified print only added/deleted/modified files (implies -p)
140 s,status print each filename with a status char (A/M/D) (implies -p)
141 u,update recursively update the index entries for the given file/dir names (default if no mode is specified)
142 check carefully check index file integrity
144 H,hash print the hash for each object next to its name
145 l,long print more information about each file
146 fake-valid mark all index entries as up-to-date even if they aren't
147 fake-invalid mark all index entries as invalid
148 f,indexfile= the name of the index file (normally BUP_DIR/bupindex)
149 exclude= a path to exclude from the backup (can be used more than once)
150 exclude-from= a file that contains exclude paths (can be used more than once)
151 v,verbose increase log output (can be used more than once)
152 x,xdev,one-file-system don't cross filesystem boundaries
154 o = options.Options(optspec)
155 (opt, flags, extra) = o.parse(sys.argv[1:])
157 if not (opt.modified or opt['print'] or opt.status or opt.update or opt.check):
159 if (opt.fake_valid or opt.fake_invalid) and not opt.update:
160 o.fatal('--fake-{in,}valid are meaningless without -u')
161 if opt.fake_valid and opt.fake_invalid:
162 o.fatal('--fake-valid is incompatible with --fake-invalid')
164 # FIXME: remove this once we account for timestamp races, i.e. index;
165 # touch new-file; index. It's possible for this to happen quickly
166 # enough that new-file ends up with the same timestamp as the first
167 # index, and then bup will ignore it.
168 tick_start = time.time()
169 time.sleep(1 - (tick_start - int(tick_start)))
171 git.check_repo_or_die()
172 indexfile = opt.indexfile or git.repo('bupindex')
177 log('check: starting initial check.\n')
178 check_index(index.Reader(indexfile))
180 excluded_paths = drecurse.parse_excludes(flags)
182 paths = index.reduce_paths(extra)
186 o.fatal('update mode (-u) requested but no paths given')
187 for (rp,path) in paths:
188 update_index(rp, excluded_paths)
190 if opt['print'] or opt.status or opt.modified:
191 for (name, ent) in index.Reader(indexfile).filter(extra or ['']):
193 and (ent.is_valid() or ent.is_deleted() or not ent.mode)):
199 elif not ent.is_valid():
200 if ent.sha == index.EMPTY_SHA:
207 line += ent.sha.encode('hex') + ' '
209 line += "%7s %7s " % (oct(ent.mode), oct(ent.gitmode))
210 print line + (name or './')
212 if opt.check and (opt['print'] or opt.status or opt.modified or opt.update):
213 log('check: starting final check.\n')
214 check_index(index.Reader(indexfile))
217 log('WARNING: %d errors encountered.\n' % len(saved_errors))