]> arthur.barton.de Git - bup.git/blob - cmd-save.py
Makefile: work with cygwin on different windows versions.
[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         log('\n%s %s ' % (status, ent.name))
86
87     if not exists:
88         continue
89
90     assert(dir.startswith('/'))
91     dirp = dir.split('/')
92     while parts > dirp:
93         _pop()
94     if dir != '/':
95         for part in dirp[len(parts):]:
96             _push(part)
97
98     if not file:
99         # directory already handled.
100         # FIXME: not using the indexed tree sha1's for anything, which is
101         # a waste.  That's a potential optimization...
102         continue  
103
104     id = None
105     if hashvalid:
106         mode = '%o' % ent.mode
107         id = ent.sha
108         shalists[-1].append((mode, file, id))
109     else:
110         try:
111             if stat.S_ISREG(ent.mode):
112                 f = open(ent.name)
113                 (mode, id) = hashsplit.split_to_blob_or_tree(w, [f])
114             elif stat.S_ISDIR(ent.mode):
115                 assert(0)  # handled above
116             elif stat.S_ISLNK(ent.mode):
117                 (mode, id) = ('120000', w.new_blob(os.readlink(ent.name)))
118             else:
119                 add_error(Exception('skipping special file "%s"' % ent.name))
120         except IOError, e:
121             add_error(e)
122         except OSError, e:
123             add_error(e)
124         if id:
125             ent.validate(id)
126             ent.repack()
127             shalists[-1].append((mode, file, id))
128 #log('parts out: %r\n' % parts)
129 #log('stk out: %r\n' % shalists)
130 while len(parts) > 1:
131     _pop()
132 #log('parts out: %r\n' % parts)
133 #log('stk out: %r\n' % shalists)
134 assert(len(shalists) == 1)
135 tree = w.new_tree(shalists[-1])
136 if opt.verbose:
137     log('\n')
138 if opt.tree:
139     print tree.encode('hex')
140 if opt.commit or opt.name:
141     msg = 'bup save\n\nGenerated by command:\n%r' % sys.argv
142     ref = opt.name and ('refs/heads/%s' % opt.name) or None
143     commit = w.new_commit(oldref, tree, msg)
144     if opt.commit:
145         if opt.verbose:
146             log('\n')
147         print commit.encode('hex')
148
149 w.close()  # must close before we can update the ref
150         
151 if opt.name:
152     if cli:
153         cli.update_ref(refname, commit, oldref)
154     else:
155         git.update_ref(refname, commit, oldref)
156
157 if cli:
158     cli.close()
159
160 if saved_errors:
161     log('WARNING: %d errors encountered while saving.\n' % len(saved_errors))