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 = 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())
58 hlinks = hlinkdb.HLinkDB(indexfile + '.hlink')
63 return (GIT_MODE_FILE, index.FAKE_SHA)
66 bup_dir = os.path.abspath(git.repo())
67 for (path,pst) in drecurse.recursive_dirlist([top], xdev=opt.xdev,
69 excluded_paths=excluded_paths):
70 if opt.verbose>=2 or (opt.verbose==1 and stat.S_ISDIR(pst.st_mode)):
71 sys.stdout.write('%s\n' % path)
73 qprogress('Indexing: %d\r' % total)
74 elif not (total % 128):
75 qprogress('Indexing: %d\r' % total)
77 while rig.cur and rig.cur.name > path: # deleted paths
81 if rig.cur.nlink > 1 and not stat.S_ISDIR(rig.cur.mode):
82 hlinks.del_path(rig.cur.name)
84 if rig.cur and rig.cur.name == path: # paths that already existed
85 if not stat.S_ISDIR(rig.cur.mode) and rig.cur.nlink > 1:
86 hlinks.del_path(rig.cur.name)
87 if not stat.S_ISDIR(pst.st_mode) and pst.st_nlink > 1:
88 hlinks.add_path(path, pst.st_dev, pst.st_ino)
89 rig.cur.from_stat(pst, tstart)
90 if not (rig.cur.flags & index.IX_HASHVALID):
92 (rig.cur.gitmode, rig.cur.sha) = hashgen(path)
93 rig.cur.flags |= index.IX_HASHVALID
99 wi.add(path, pst, hashgen = hashgen)
100 if not stat.S_ISDIR(pst.st_mode) and pst.st_nlink > 1:
101 hlinks.add_path(path, pst.st_dev, pst.st_ino)
103 progress('Indexing: %d, done.\n' % total)
105 hlinks.prepare_save()
113 log('check: before merging: oldfile\n')
115 log('check: before merging: newfile\n')
117 mi = index.Writer(indexfile, tmax)
119 for e in index.merge(ri, wr):
120 # FIXME: shouldn't we remove deleted entries eventually? When?
134 bup index <-p|m|s|u> [options...] <filenames...>
137 p,print print the index entries for the given names (also works with -u)
138 m,modified print only added/deleted/modified files (implies -p)
139 s,status print each filename with a status char (A/M/D) (implies -p)
140 u,update recursively update the index entries for the given file/dir names (default if no mode is specified)
141 check carefully check index file integrity
143 H,hash print the hash for each object next to its name
144 l,long print more information about each file
145 fake-valid mark all index entries as up-to-date even if they aren't
146 fake-invalid mark all index entries as invalid
147 f,indexfile= the name of the index file (normally BUP_DIR/bupindex)
148 exclude= a path to exclude from the backup (can be used more than once)
149 exclude-from= a file that contains exclude paths (can be used more than once)
150 v,verbose increase log output (can be used more than once)
151 x,xdev,one-file-system don't cross filesystem boundaries
153 o = options.Options(optspec)
154 (opt, flags, extra) = o.parse(sys.argv[1:])
156 if not (opt.modified or opt['print'] or opt.status or opt.update or opt.check):
158 if (opt.fake_valid or opt.fake_invalid) and not opt.update:
159 o.fatal('--fake-{in,}valid are meaningless without -u')
160 if opt.fake_valid and opt.fake_invalid:
161 o.fatal('--fake-valid is incompatible with --fake-invalid')
163 # FIXME: remove this once we account for timestamp races, i.e. index;
164 # touch new-file; index. It's possible for this to happen quickly
165 # enough that new-file ends up with the same timestamp as the first
166 # index, and then bup will ignore it.
167 tick_start = time.time()
168 time.sleep(1 - (tick_start - int(tick_start)))
170 git.check_repo_or_die()
171 indexfile = opt.indexfile or git.repo('bupindex')
176 log('check: starting initial check.\n')
177 check_index(index.Reader(indexfile))
179 excluded_paths = drecurse.parse_excludes(flags)
181 paths = index.reduce_paths(extra)
185 o.fatal('update mode (-u) requested but no paths given')
186 for (rp,path) in paths:
187 update_index(rp, excluded_paths)
189 if opt['print'] or opt.status or opt.modified:
190 for (name, ent) in index.Reader(indexfile).filter(extra or ['']):
192 and (ent.is_valid() or ent.is_deleted() or not ent.mode)):
198 elif not ent.is_valid():
199 if ent.sha == index.EMPTY_SHA:
206 line += ent.sha.encode('hex') + ' '
208 line += "%7s %7s " % (oct(ent.mode), oct(ent.gitmode))
209 print line + (name or './')
211 if opt.check and (opt['print'] or opt.status or opt.modified or opt.update):
212 log('check: starting final check.\n')
213 check_index(index.Reader(indexfile))
216 log('WARNING: %d errors encountered.\n' % len(saved_errors))