-#!/usr/bin/env python
-import sys, re, errno
+#!/usr/bin/env python2.5
+import sys, re, errno, stat
import hashsplit, git, options
from helpers import *
-saved_errors = []
+saved_errors = []
def add_error(e):
saved_errors.append(e)
- log('%s\n' % e)
+ log('\n%s\n' % e)
+
+
+def _direxpand(name):
+ st = os.lstat(name)
+ try:
+ if stat.S_ISDIR(st.st_mode):
+ for sub in os.listdir(name):
+ subfull = os.path.join(name, sub)
+ for fn_st in _direxpand(subfull):
+ yield fn_st
+ else:
+ yield (name,st)
+ except OSError, e:
+ if e.errno in [errno.ENOENT, errno.EPERM, errno.EACCES]:
+ add_error(e)
+ else:
+ raise
def direxpand(names):
for n in names:
- log('%s\n' % n)
- try:
- for sub in os.listdir(n):
- subfull = os.path.join(n, sub)
- for sub2 in direxpand([subfull]):
- yield sub2
- except OSError, e:
- if e.errno == errno.ENOTDIR:
- yield n
- elif e.errno in [errno.ENOENT, errno.EPERM, errno.EACCES]:
- add_error(e)
- else:
- raise
+ for fn_st in _direxpand(n):
+ yield fn_st
def _normpath(dir):
return p
def getdir(self, dir):
- # FIXME: deal with '..' somehow
+ # FIXME: deal with '..' somehow (look at how tar does it)
+ dir = _normpath(dir)
if dir.startswith('/'):
dir = dir[1:]
top = self.gettop()
if not dir:
return top
- for part in _normpath(dir).split('/'):
+ for part in dir.split('/'):
sub = top.children.get(part)
if not sub:
sub = top.children[part] = Tree(top, part)
(dir, name) = os.path.split(fullname)
self.getdir(dir).children[name] = (mode, name, id)
- def shalist(self):
+ def shalist(self, w):
for c in self.children.values():
if isinstance(c, tuple): # sha1 entry for a file
yield c
else: # tree
- t = ('40000', c.name, c.gen_tree())
+ t = ('40000', c.name, c.gen_tree(w))
yield t
- def gen_tree(self):
+ def gen_tree(self, w):
if not self.sha:
- self.sha = git.gen_tree(self.shalist())
+ self.sha = w.new_tree(self.shalist(w))
return self.sha
t,tree output a tree id
c,commit output a commit id
n,name= name of backup set to update (if any)
+v,verbose increase log output (can be used more than once)
"""
o = options.Options('bup save', optspec)
(opt, flags, extra) = o.parse(sys.argv[1:])
+git.check_repo_or_die()
if not (opt.tree or opt.commit or opt.name):
log("bup save: use one or more of -t, -c, -n\n")
o.usage()
+if opt.verbose >= 2:
+ git.verbose = opt.verbose - 1
+ hashsplit.split_verbosely = opt.verbose - 1
+
+w = git.PackWriter()
root = Tree(None, '')
-for fn in direxpand(extra):
+for (fn,st) in direxpand(extra):
+ if opt.verbose:
+ log('\n%s ' % fn)
try:
- # FIXME: symlinks etc.
- f = open(fn)
+ if stat.S_ISREG(st.st_mode): # regular file
+ f = open(fn)
+ (mode, id) = hashsplit.split_to_blob_or_tree(w, [f])
+ elif stat.S_ISLNK(st.st_mode): # symlink
+ (mode, id) = ('120000', w.new_blob(os.readlink(fn)))
+ else:
+ add_error(Exception('skipping special file "%s"' % fn))
except IOError, e:
add_error(e)
- continue
except OSError, e:
add_error(e)
- continue
- (mode, id) = hashsplit.split_to_blob_or_tree([f])
- root.addfile(mode, fn, id)
-tree = root.gen_tree()
+ else:
+ root.addfile(mode, fn, id)
+tree = root.gen_tree(w)
+if opt.verbose:
+ log('\n')
if opt.tree:
- print tree
+ print tree.encode('hex')
if opt.commit or opt.name:
msg = 'bup save\n\nGenerated by command:\n%r' % sys.argv
ref = opt.name and ('refs/heads/%s' % opt.name) or None
- commit = git.gen_commit_easy(ref, tree, msg)
+ commit = w.new_commit(ref, tree, msg)
if opt.commit:
- print commit
+ if opt.verbose:
+ log('\n')
+ print commit.encode('hex')
+
+w.close()
if saved_errors:
log('WARNING: %d errors encountered while saving.\n' % len(saved_errors))