]> arthur.barton.de Git - bup.git/blob - cmd/restore-cmd.py
Restore any metadata during "bup restore"; add "bup meta --edit".
[bup.git] / cmd / restore-cmd.py
1 #!/usr/bin/env python
2 import sys, stat
3 from bup import options, git, metadata, vfs
4 from bup.helpers import *
5
6 optspec = """
7 bup restore [-C outdir] </branch/revision/path/to/dir ...>
8 --
9 C,outdir=   change to given outdir before extracting files
10 numeric-ids restore numeric IDs (user, group, etc.) rather than names
11 v,verbose   increase log output (can be used more than once)
12 q,quiet     don't show progress meter
13 """
14
15 total_restored = 0
16
17
18 def verbose1(s):
19     if opt.verbose >= 1:
20         print s
21
22
23 def verbose2(s):
24     if opt.verbose >= 2:
25         print s
26
27
28 def plog(s):
29     if opt.quiet:
30         return
31     qprogress(s)
32
33
34 def print_info(n, fullname):
35     if stat.S_ISDIR(n.mode):
36         verbose1('%s/' % fullname)
37     elif stat.S_ISLNK(n.mode):
38         verbose2('%s@ -> %s' % (fullname, n.readlink()))
39     else:
40         verbose2(fullname)
41
42
43 def create_path(n, fullname, meta):
44     if meta:
45         meta.create_path(fullname)
46     else:
47         # These fallbacks are important -- meta could be null if, for
48         # example, save created a "fake" item, i.e. a new strip/graft
49         # path element, etc.  You can find cases like that by
50         # searching for "Metadata()".
51         unlink(fullname)
52         if stat.S_ISDIR(n.mode):
53             mkdirp(fullname)
54         elif stat.S_ISLNK(n.mode):
55             os.symlink(n.readlink(), fullname)
56
57
58 def do_node(top, n, meta=None):
59     # meta will be None for dirs, and when there is no .bupm (i.e. no metadata)
60     global total_restored, opt
61     meta_stream = None
62     try:
63         fullname = n.fullname(stop_at=top)
64         # If this is a directory, its metadata is the first entry in
65         # any .bupm file inside the directory.  Get it.
66         if(stat.S_ISDIR(n.mode)):
67             mfile = n.metadata_file() # VFS file -- cannot close().
68             if mfile:
69                 meta_stream = mfile.open()
70                 meta = metadata.Metadata.read(meta_stream)
71         print_info(n, fullname)
72         create_path(n, fullname, meta)
73
74         # Write content if appropriate (only regular files have content).
75         plain_file = False
76         if meta:
77             plain_file = stat.S_ISREG(meta.mode)
78         else:
79             plain_file = stat.S_ISREG(n.mode)
80
81         if plain_file:
82             outf = open(fullname, 'wb')
83             try:
84                 for b in chunkyreader(n.open()):
85                     outf.write(b)
86             finally:
87                 outf.close()
88
89         total_restored += 1
90         plog('Restoring: %d\r' % total_restored)
91         for sub in n:
92             m = None
93             # Don't get metadata if this is a dir -- handled in sub do_node().
94             if meta_stream and not stat.S_ISDIR(sub.mode):
95                 m = metadata.Metadata.read(meta_stream)
96             do_node(top, sub, m)
97         if meta:
98             meta.apply_to_path(fullname,
99                                restore_numeric_ids=opt.numeric_ids)
100     finally:
101         if meta_stream:
102             meta_stream.close()
103
104 handle_ctrl_c()
105
106 o = options.Options(optspec)
107 (opt, flags, extra) = o.parse(sys.argv[1:])
108
109 git.check_repo_or_die()
110 top = vfs.RefList(None)
111
112 if not extra:
113     o.fatal('must specify at least one filename to restore')
114     
115 if opt.outdir:
116     mkdirp(opt.outdir)
117     os.chdir(opt.outdir)
118
119 ret = 0
120 for d in extra:
121     path,name = os.path.split(d)
122     try:
123         n = top.lresolve(d)
124     except vfs.NodeError, e:
125         add_error(e)
126         continue
127     isdir = stat.S_ISDIR(n.mode)
128     if not name or name == '.':
129         # trailing slash: extract children to cwd
130         if not isdir:
131             add_error('%r: not a directory' % d)
132         else:
133             for sub in n:
134                 do_node(n, sub)
135     else:
136         # no trailing slash: extract node and its children to cwd
137         do_node(n.parent, n)
138
139 if not opt.quiet:
140     progress('Restoring: %d, done.\n' % total_restored)
141
142 if saved_errors:
143     log('WARNING: %d errors encountered while restoring.\n' % len(saved_errors))
144     sys.exit(1)