]> arthur.barton.de Git - bup.git/blob - cmd-save.py
Fix some problems running on older Debian.
[bup.git] / cmd-save.py
1 #!/usr/bin/env python2.5
2 import sys, re, errno, stat
3 import hashsplit, git, options
4 from helpers import *
5
6
7 saved_errors = []
8 def add_error(e):
9     saved_errors.append(e)
10     log('\n%s\n' % e)
11
12
13 def _direxpand(name):
14     st = os.lstat(name)
15     try:
16         if stat.S_ISDIR(st.st_mode):
17             for sub in os.listdir(name):
18                 subfull = os.path.join(name, sub)
19                 for fn_st in _direxpand(subfull):
20                     yield fn_st
21         else:
22             yield (name,st)
23     except OSError, e:
24         if e.errno in [errno.ENOENT, errno.EPERM, errno.EACCES]:
25             add_error(e)
26         else:
27             raise
28
29
30 def direxpand(names):
31     for n in names:
32         for fn_st in _direxpand(n):
33             yield fn_st
34             
35
36 def _normpath(dir):
37     p = os.path.normpath(dir)
38     return (p != '.') and p or ''
39
40
41 class Tree:
42     def __init__(self, parent, name):
43         assert(name != '.')
44         assert(not (parent and not name))
45         self.parent = parent
46         self.name = name
47         self.sha = None
48         self.children = {}
49         if self.parent:
50             self.parent.children[self.name] = self
51     
52     def fullpath(self):
53         if self.parent:
54             return os.path.join(self.parent.fullpath(), self.name)
55         else:
56             return self.name
57         
58     def gettop(self):
59         p = self
60         while p.parent:
61             p = p.parent
62         return p
63         
64     def getdir(self, dir):
65         # FIXME: deal with '..' somehow (look at how tar does it)
66         dir = _normpath(dir)
67         if dir.startswith('/'):
68             dir = dir[1:]
69         top = self.gettop()
70         if not dir:
71             return top
72         for part in dir.split('/'):
73             sub = top.children.get(part)
74             if not sub:
75                 sub = top.children[part] = Tree(top, part)
76             top = sub
77         return top
78     
79     def addfile(self, mode, fullname, id):
80         (dir, name) = os.path.split(fullname)
81         self.getdir(dir).children[name] = (mode, name, id)
82         
83     def shalist(self, w):
84         for c in self.children.values():
85             if isinstance(c, tuple):  # sha1 entry for a file
86                 yield c
87             else:  # tree
88                 t = ('40000', c.name, c.gen_tree(w))
89                 yield t
90         
91     def gen_tree(self, w):
92         if not self.sha:
93             self.sha = w.new_tree(self.shalist(w))
94         return self.sha
95
96
97 optspec = """
98 bup save [-tc] [-n name] <filenames...>
99 --
100 t,tree     output a tree id
101 c,commit   output a commit id
102 n,name=    name of backup set to update (if any)
103 v,verbose  increase log output (can be used more than once)
104 """
105 o = options.Options('bup save', optspec)
106 (opt, flags, extra) = o.parse(sys.argv[1:])
107
108 git.check_repo_or_die()
109 if not (opt.tree or opt.commit or opt.name):
110     log("bup save: use one or more of -t, -c, -n\n")
111     o.usage()
112
113 if opt.verbose >= 2:
114     git.verbose = opt.verbose - 1
115     hashsplit.split_verbosely = opt.verbose - 1
116
117 w = git.PackWriter()
118 root = Tree(None, '')
119 for (fn,st) in direxpand(extra):
120     if opt.verbose:
121         log('\n%s ' % fn)
122     try:
123         if stat.S_ISREG(st.st_mode):  # regular file
124             f = open(fn)
125             (mode, id) = hashsplit.split_to_blob_or_tree(w, [f])
126         elif stat.S_ISLNK(st.st_mode):  # symlink
127             (mode, id) = ('120000', w.new_blob(os.readlink(fn)))
128         else:
129             add_error(Exception('skipping special file "%s"' % fn))
130     except IOError, e:
131         add_error(e)
132     except OSError, e:
133         add_error(e)
134     else:
135         root.addfile(mode, fn, id)
136 tree = root.gen_tree(w)
137 if opt.verbose:
138     log('\n')
139 if opt.tree:
140     print tree.encode('hex')
141 if opt.commit or opt.name:
142     msg = 'bup save\n\nGenerated by command:\n%r' % sys.argv
143     ref = opt.name and ('refs/heads/%s' % opt.name) or None
144     commit = w.new_commit(ref, tree, msg)
145     if opt.commit:
146         if opt.verbose:
147             log('\n')
148         print commit.encode('hex')
149
150 w.close()
151
152 if saved_errors:
153     log('WARNING: %d errors encountered while saving.\n' % len(saved_errors))