]> arthur.barton.de Git - bup.git/blobdiff - cmd-save.py
Fix some problems running on older Debian.
[bup.git] / cmd-save.py
index 8b32e5e18b121cd70b57e76229fc186940bbfa41..e68539c730a3e57ac6f1213fcd8c78c528bf4404 100755 (executable)
@@ -1,30 +1,97 @@
-#!/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):
+    p = os.path.normpath(dir)
+    return (p != '.') and p or ''
+
+
+class Tree:
+    def __init__(self, parent, name):
+        assert(name != '.')
+        assert(not (parent and not name))
+        self.parent = parent
+        self.name = name
+        self.sha = None
+        self.children = {}
+        if self.parent:
+            self.parent.children[self.name] = self
+    
+    def fullpath(self):
+        if self.parent:
+            return os.path.join(self.parent.fullpath(), self.name)
+        else:
+            return self.name
+        
+    def gettop(self):
+        p = self
+        while p.parent:
+            p = p.parent
+        return p
+        
+    def getdir(self, dir):
+        # 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 dir.split('/'):
+            sub = top.children.get(part)
+            if not sub:
+                sub = top.children[part] = Tree(top, part)
+            top = sub
+        return top
+    
+    def addfile(self, mode, fullname, id):
+        (dir, name) = os.path.split(fullname)
+        self.getdir(dir).children[name] = (mode, name, id)
+        
+    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(w))
+                yield t
+        
+    def gen_tree(self, w):
+        if not self.sha:
+            self.sha = w.new_tree(self.shalist(w))
+        return self.sha
 
 
 optspec = """
@@ -33,36 +100,54 @@ bup save [-tc] [-n name] <filenames...>
 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()
 
-shalist = []
-for fn in direxpand(extra):
+if opt.verbose >= 2:
+    git.verbose = opt.verbose - 1
+    hashsplit.split_verbosely = opt.verbose - 1
+
+w = git.PackWriter()
+root = Tree(None, '')
+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])
-    shalist.append((mode, re.sub(r'/', '_', fn), id))
-tree = git.gen_tree(shalist)
+    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 = 'Generated by command:\n%r' % sys.argv
+    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))