]> arthur.barton.de Git - bup.git/blobdiff - cmd-save.py
executable files: don't assume python2.5.
[bup.git] / cmd-save.py
index 7da0429503cff90286f0b680249151db51590aab..8c7ec9ef7ca60c8c6f239d41c8ca4ad98e9a6648 100755 (executable)
@@ -1,6 +1,6 @@
-#!/usr/bin/env python2.5
+#!/usr/bin/env python
 import sys, re, errno, stat, client
-import hashsplit, git, options
+import hashsplit, git, options, index
 from helpers import *
 
 
@@ -10,90 +10,6 @@ def add_error(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:
-        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 = """
 bup save [-tc] [-n name] <filenames...>
 --
@@ -110,6 +26,9 @@ 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 not extra:
+    log("bup save: no filenames given.\n")
+    o.usage()
 
 if opt.verbose >= 2:
     git.verbose = opt.verbose - 1
@@ -124,26 +43,98 @@ else:
     cli = None
     oldref = refname and git.read_ref(refname) or None
     w = git.PackWriter()
-    
-root = Tree(None, '')
-for (fn,st) in direxpand(extra):
+
+
+def eatslash(dir):
+    if dir.endswith('/'):
+        return dir[:-1]
+    else:
+        return dir
+
+
+parts = ['']
+shalists = [[]]
+
+def _push(part):
+    assert(part)
+    parts.append(part)
+    shalists.append([])
+
+def _pop():
+    assert(len(parts) > 1)
+    part = parts.pop()
+    shalist = shalists.pop()
+    tree = w.new_tree(shalist)
+    shalists[-1].append(('40000', part, tree))
+
+
+for (transname,ent) in index.Reader(git.repo('bupindex')).filter(extra):
+    (dir, file) = os.path.split(ent.name)
+    exists = (ent.flags & index.IX_EXISTS)
+    hashvalid = (ent.flags & index.IX_HASHVALID) and w.exists(ent.sha)
     if opt.verbose:
-        log('\n%s ' % fn)
-    try:
-        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)))
+        if not exists:
+            status = 'D'
+        elif not hashvalid:
+            if ent.sha == index.EMPTY_SHA:
+                status = 'A'
+            else:
+                status = 'M'
         else:
-            add_error(Exception('skipping special file "%s"' % fn))
-    except IOError, e:
-        add_error(e)
-    except OSError, e:
-        add_error(e)
+            status = ' '
+        if opt.verbose >= 2 or (status in ['A','M'] 
+                                and not stat.S_ISDIR(ent.mode)):
+            log('\n%s %s ' % (status, ent.name))
+
+    if not exists:
+        continue
+
+    assert(dir.startswith('/'))
+    dirp = dir.split('/')
+    while parts > dirp:
+        _pop()
+    if dir != '/':
+        for part in dirp[len(parts):]:
+            _push(part)
+
+    if not file:
+        # directory already handled.
+        # FIXME: not using the indexed tree sha1's for anything, which is
+        # a waste.  That's a potential optimization...
+        continue  
+
+    id = None
+    if hashvalid:
+        mode = '%o' % ent.mode
+        id = ent.sha
+        shalists[-1].append((mode, file, id))
     else:
-        root.addfile(mode, fn, id)
-tree = root.gen_tree(w)
+        try:
+            if stat.S_ISREG(ent.mode):
+                f = open(ent.name)
+                (mode, id) = hashsplit.split_to_blob_or_tree(w, [f])
+            elif stat.S_ISDIR(ent.mode):
+                assert(0)  # handled above
+            elif stat.S_ISLNK(ent.mode):
+                (mode, id) = ('120000', w.new_blob(os.readlink(ent.name)))
+            else:
+                add_error(Exception('skipping special file "%s"' % ent.name))
+        except IOError, e:
+            add_error(e)
+        except OSError, e:
+            add_error(e)
+        if id:
+            ent.validate(id)
+            ent.repack()
+            shalists[-1].append((mode, file, id))
+#log('parts out: %r\n' % parts)
+#log('stk out: %r\n' % shalists)
+while len(parts) > 1:
+    _pop()
+#log('parts out: %r\n' % parts)
+#log('stk out: %r\n' % shalists)
+assert(len(shalists) == 1)
+tree = w.new_tree(shalists[-1])
 if opt.verbose:
     log('\n')
 if opt.tree: