]> arthur.barton.de Git - bup.git/blob - cmd-save.py
Write git pack files instead of loose object files.
[bup.git] / cmd-save.py
1 #!/usr/bin/env python
2 import sys, re, errno
3 import hashsplit, git, options
4 from helpers import *
5
6 saved_errors = []
7
8 def add_error(e):
9     saved_errors.append(e)
10     log('%s\n' % e)
11
12
13 def direxpand(names):
14     for n in names:
15         log('%s\n' % n)
16         try:
17             for sub in os.listdir(n):
18                 subfull = os.path.join(n, sub)
19                 for sub2 in direxpand([subfull]):
20                     yield sub2
21         except OSError, e:
22             if e.errno == errno.ENOTDIR:
23                 yield n
24             elif e.errno in [errno.ENOENT, errno.EPERM, errno.EACCES]:
25                 add_error(e)
26             else:
27                 raise
28             
29
30 def _normpath(dir):
31     p = os.path.normpath(dir)
32     return (p != '.') and p or ''
33
34
35 class Tree:
36     def __init__(self, parent, name):
37         assert(name != '.')
38         assert(not (parent and not name))
39         self.parent = parent
40         self.name = name
41         self.sha = None
42         self.children = {}
43         if self.parent:
44             self.parent.children[self.name] = self
45     
46     def fullpath(self):
47         if self.parent:
48             return os.path.join(self.parent.fullpath(), self.name)
49         else:
50             return self.name
51         
52     def gettop(self):
53         p = self
54         while p.parent:
55             p = p.parent
56         return p
57         
58     def getdir(self, dir):
59         # FIXME: deal with '..' somehow
60         if dir.startswith('/'):
61             dir = dir[1:]
62         top = self.gettop()
63         if not dir:
64             return top
65         for part in _normpath(dir).split('/'):
66             sub = top.children.get(part)
67             if not sub:
68                 sub = top.children[part] = Tree(top, part)
69             top = sub
70         return top
71     
72     def addfile(self, mode, fullname, id):
73         (dir, name) = os.path.split(fullname)
74         self.getdir(dir).children[name] = (mode, name, id)
75         
76     def shalist(self):
77         for c in self.children.values():
78             if isinstance(c, tuple):  # sha1 entry for a file
79                 yield c
80             else:  # tree
81                 t = ('40000', c.name, c.gen_tree())
82                 yield t
83         
84     def gen_tree(self):
85         if not self.sha:
86             self.sha = git.gen_tree(self.shalist())
87         return self.sha
88
89
90 optspec = """
91 bup save [-tc] [-n name] <filenames...>
92 --
93 t,tree     output a tree id
94 c,commit   output a commit id
95 n,name=    name of backup set to update (if any)
96 """
97 o = options.Options('bup save', optspec)
98 (opt, flags, extra) = o.parse(sys.argv[1:])
99
100 if not (opt.tree or opt.commit or opt.name):
101     log("bup save: use one or more of -t, -c, -n\n")
102     o.usage()
103
104 root = Tree(None, '')
105 for fn in direxpand(extra):
106     try:
107         # FIXME: symlinks etc.
108         f = open(fn)
109     except IOError, e:
110         add_error(e)
111         continue
112     except OSError, e:
113         add_error(e)
114         continue
115     (mode, id) = hashsplit.split_to_blob_or_tree([f])
116     root.addfile(mode, fn, id)
117 tree = root.gen_tree()
118 if opt.tree:
119     print tree
120 if opt.commit or opt.name:
121     msg = 'bup save\n\nGenerated by command:\n%r' % sys.argv
122     ref = opt.name and ('refs/heads/%s' % opt.name) or None
123     commit = git.gen_commit_easy(ref, tree, msg)
124     if opt.commit:
125         print commit
126
127 git.flush_pack()
128
129 if saved_errors:
130     log('WARNING: %d errors encountered while saving.\n' % len(saved_errors))