1 #!/usr/bin/env python2.5
3 import options, git, index
7 def __init__(self, path):
10 self.fd = os.open(path, os.O_RDONLY|os.O_LARGEFILE|os.O_NOFOLLOW)
11 except AttributeError:
12 self.fd = os.open(path, os.O_RDONLY|os.O_NOFOLLOW)
13 #self.st = os.fstat(self.fd)
27 saved_errors.append(e)
31 # the use of fchdir() and lstat() are for two reasons:
32 # - help out the kernel by not making it repeatedly look up the absolute path
33 # - avoid race conditions caused by doing listdir() on a changing symlink
34 def handle_path(ri, wi, dir, name, pst, xdev, can_delete_siblings):
38 return (0, index.FAKE_SHA)
42 #log('handle_path(%r,%r)\n' % (dir, name))
43 if stat.S_ISDIR(pst.st_mode):
44 if opt.verbose == 1: # log dirs only
45 sys.stdout.write('%s\n' % path)
50 add_error(Exception('in %s: %s' % (dir, str(e))))
55 #log('* %r: %r\n' % (name, ld))
57 add_error(Exception('in %s: %s' % (path, str(e))))
64 add_error(Exception('in %s: %s' % (path, str(e))))
66 if xdev != None and st.st_dev != xdev:
67 log('Skipping %r: different filesystem.\n'
70 if stat.S_ISDIR(st.st_mode):
73 for p,st in reversed(sorted(lds)):
74 dirty += handle_path(ri, wi, path, p, st, xdev,
75 can_delete_siblings = True)
78 #log('endloop: ri.cur:%r path:%r\n' % (ri.cur.name, path))
79 while ri.cur and ri.cur.name > path:
80 #log('ricur:%r path:%r\n' % (ri.cur, path))
81 if can_delete_siblings and dir and ri.cur.name.startswith(dir):
82 #log(' --- deleting\n')
83 ri.cur.flags &= ~(index.IX_EXISTS | index.IX_HASHVALID)
87 if ri.cur and ri.cur.name == path:
88 dirty += ri.cur.from_stat(pst)
89 if dirty or not (ri.cur.flags & index.IX_HASHVALID):
90 #log(' --- updating %r\n' % path)
92 (ri.cur.gitmode, ri.cur.sha) = hashgen(name)
93 ri.cur.flags |= index.IX_HASHVALID
97 wi.add(path, pst, hashgen = hashgen)
99 if opt.verbose > 1: # all files, not just dirs
100 sys.stdout.write('%s\n' % path)
105 def merge_indexes(out, r1, r2):
106 log('bup: merging indexes.\n')
107 for e in index._last_writer_wins_iter([r1, r2]):
108 #if e.flags & index.IX_EXISTS:
113 def __init__(self, l):
120 self.cur = self.i.next()
121 except StopIteration:
126 def update_index(path):
127 ri = index.Reader(indexfile)
128 wi = index.Writer(indexfile)
129 rig = MergeGetter(ri)
131 rpath = index.realpath(path)
140 (dir, name) = os.path.split(rpath)
141 dir = slashappend(dir)
142 if stat.S_ISDIR(st.st_mode) and (not rpath or rpath[-1] != '/'):
144 can_delete_siblings = True
146 can_delete_siblings = False
147 OsFile(dir or '/').fchdir()
148 dirty = handle_path(rig, wi, dir, name, st, xdev, can_delete_siblings)
150 # make sure all the parents of the updated path exist and are invalidated
153 (rpath, junk) = os.path.split(rpath)
160 while rig.cur and rig.cur.name > p:
161 #log('FINISHING: %r path=%r d=%r\n' % (rig.cur.name, p, dirty))
163 if rig.cur and rig.cur.name == p:
165 rig.cur.flags &= ~index.IX_HASHVALID
168 wi.add(p, os.lstat(p))
175 mi = index.Writer(indexfile)
176 merge_indexes(mi, ri, wi.new_reader())
183 bup index <-p|s|m|u> [options...] <filenames...>
185 p,print print the index entries for the given names (also works with -u)
186 m,modified print only added/deleted/modified files (implies -p)
187 s,status print each filename with a status char (A/M/D) (implies -p)
188 H,hash print the hash for each object next to its name (implies -p)
189 u,update (recursively) update the index entries for the given filenames
190 x,xdev,one-file-system don't cross filesystem boundaries
191 fake-valid mark all index entries as up-to-date even if they aren't
192 f,indexfile= the name of the index file (default 'index')
193 v,verbose increase log output (can be used more than once)
195 o = options.Options('bup index', optspec)
196 (opt, flags, extra) = o.parse(sys.argv[1:])
198 if not (opt.modified or opt['print'] or opt.status or opt.update):
199 log('bup index: you must supply one or more of -p, -s, -m, or -u\n')
201 if opt.fake_valid and not opt.update:
202 log('bup index: --fake-valid is meaningless without -u\n')
205 git.check_repo_or_die()
206 indexfile = opt.indexfile or git.repo('bupindex')
208 paths = index.reduce_paths(extra)
212 log('bup index: update (-u) requested but no paths given\n')
214 for (rp, path) in paths:
217 if opt['print'] or opt.status or opt.modified:
218 for (name, ent) in index.Reader(indexfile).filter(extra or ['']):
219 if opt.modified and ent.flags & index.IX_HASHVALID:
223 if not ent.flags & index.IX_EXISTS:
225 elif not ent.flags & index.IX_HASHVALID:
226 if ent.sha == index.EMPTY_SHA:
233 line += ent.sha.encode('hex') + ' '
234 print line + (name or './')
238 log('WARNING: %d errors encountered.\n' % len(saved_errors))