]> arthur.barton.de Git - bup.git/blob - cmd-save.py
529ad14f0b672146f87d8d31236fa4b6a29b5ad9
[bup.git] / cmd-save.py
1 #!/usr/bin/env python2.5
2 import sys, re, errno, stat, client
3 import hashsplit, git, options, index
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 optspec = """
14 bup save [-tc] [-n name] <filenames...>
15 --
16 r,remote=  remote repository path
17 t,tree     output a tree id
18 c,commit   output a commit id
19 n,name=    name of backup set to update (if any)
20 v,verbose  increase log output (can be used more than once)
21 """
22 o = options.Options('bup save', optspec)
23 (opt, flags, extra) = o.parse(sys.argv[1:])
24
25 git.check_repo_or_die()
26 if not (opt.tree or opt.commit or opt.name):
27     log("bup save: use one or more of -t, -c, -n\n")
28     o.usage()
29 if not extra:
30     log("bup save: no filenames given.\n")
31     o.usage()
32
33 if opt.verbose >= 2:
34     git.verbose = opt.verbose - 1
35     hashsplit.split_verbosely = opt.verbose - 1
36
37 refname = opt.name and 'refs/heads/%s' % opt.name or None
38 if opt.remote:
39     cli = client.Client(opt.remote)
40     oldref = refname and cli.read_ref(refname) or None
41     w = cli.new_packwriter()
42 else:
43     cli = None
44     oldref = refname and git.read_ref(refname) or None
45     w = git.PackWriter()
46
47
48 def eatslash(dir):
49     if dir.endswith('/'):
50         return dir[:-1]
51     else:
52         return dir
53
54
55 parts = ['']
56 shalists = [[]]
57
58 def _push(part):
59     assert(part)
60     parts.append(part)
61     shalists.append([])
62
63 def _pop():
64     assert(len(parts) > 1)
65     part = parts.pop()
66     shalist = shalists.pop()
67     tree = w.new_tree(shalist)
68     shalists[-1].append(('40000', part, tree))
69
70
71 for (transname,ent) in index.Reader(git.repo('bupindex')).filter(extra):
72     (dir, file) = os.path.split(ent.name)
73     exists = (ent.flags & index.IX_EXISTS)
74     hashvalid = (ent.flags & index.IX_HASHVALID) and w.exists(ent.sha)
75     if opt.verbose:
76         if not exists:
77             status = 'D'
78         elif not hashvalid:
79             if ent.sha == index.EMPTY_SHA:
80                 status = 'A'
81             else:
82                 status = 'M'
83         else:
84             status = ' '
85         if opt.verbose >= 2 or (status in ['A','M'] 
86                                 and not stat.S_ISDIR(ent.mode)):
87             log('\n%s %s ' % (status, ent.name))
88
89     if not exists:
90         continue
91
92     assert(dir.startswith('/'))
93     dirp = dir.split('/')
94     while parts > dirp:
95         _pop()
96     if dir != '/':
97         for part in dirp[len(parts):]:
98             _push(part)
99
100     if not file:
101         # directory already handled.
102         # FIXME: not using the indexed tree sha1's for anything, which is
103         # a waste.  That's a potential optimization...
104         continue  
105
106     id = None
107     if hashvalid:
108         mode = '%o' % ent.mode
109         id = ent.sha
110         shalists[-1].append((mode, file, id))
111     else:
112         try:
113             if stat.S_ISREG(ent.mode):
114                 f = open(ent.name)
115                 (mode, id) = hashsplit.split_to_blob_or_tree(w, [f])
116             elif stat.S_ISDIR(ent.mode):
117                 assert(0)  # handled above
118             elif stat.S_ISLNK(ent.mode):
119                 (mode, id) = ('120000', w.new_blob(os.readlink(ent.name)))
120             else:
121                 add_error(Exception('skipping special file "%s"' % ent.name))
122         except IOError, e:
123             add_error(e)
124         except OSError, e:
125             add_error(e)
126         if id:
127             ent.validate(id)
128             ent.repack()
129             shalists[-1].append((mode, file, id))
130 #log('parts out: %r\n' % parts)
131 #log('stk out: %r\n' % shalists)
132 while len(parts) > 1:
133     _pop()
134 #log('parts out: %r\n' % parts)
135 #log('stk out: %r\n' % shalists)
136 assert(len(shalists) == 1)
137 tree = w.new_tree(shalists[-1])
138 if opt.verbose:
139     log('\n')
140 if opt.tree:
141     print tree.encode('hex')
142 if opt.commit or opt.name:
143     msg = 'bup save\n\nGenerated by command:\n%r' % sys.argv
144     ref = opt.name and ('refs/heads/%s' % opt.name) or None
145     commit = w.new_commit(oldref, tree, msg)
146     if opt.commit:
147         if opt.verbose:
148             log('\n')
149         print commit.encode('hex')
150
151 w.close()  # must close before we can update the ref
152         
153 if opt.name:
154     if cli:
155         cli.update_ref(refname, commit, oldref)
156     else:
157         git.update_ref(refname, commit, oldref)
158
159 if cli:
160     cli.close()
161
162 if saved_errors:
163     log('WARNING: %d errors encountered while saving.\n' % len(saved_errors))