]> arthur.barton.de Git - bup.git/blob - cmd-split.py
Support incremental backups to a remote server.
[bup.git] / cmd-split.py
1 #!/usr/bin/env python
2 import sys, time, re, struct
3 import hashsplit, git, options
4 from helpers import *
5
6 optspec = """
7 bup split [-tcb] [-n name] [--bench] [filenames...]
8 --
9 r,remote=  remote repository path
10 b,blobs    output a series of blob ids
11 t,tree     output a tree id
12 c,commit   output a commit id
13 n,name=    name of backup set to update (if any)
14 v,verbose  increase log output (can be used more than once)
15 bench      print benchmark timings to stderr
16 """
17 o = options.Options('bup split', optspec)
18 (opt, flags, extra) = o.parse(sys.argv[1:])
19
20 git.check_repo_or_die()
21 if not (opt.blobs or opt.tree or opt.commit or opt.name):
22     log("bup split: use one or more of -b, -t, -c, -n\n")
23     o.usage()
24
25 hashsplit.split_verbosely = opt.verbose
26 if opt.verbose >= 2:
27     git.verbose = opt.verbose - 1
28     opt.bench = 1
29
30 start_time = time.time()
31
32 def server_connect(remote):
33     rs = remote.split(':', 1)
34     if len(rs) == 1:
35         (host, dir) = ('NONE', remote)
36         p = subprocess.Popen(['bup', 'server'],
37                              stdin=subprocess.PIPE, stdout=subprocess.PIPE)
38     else:
39         (host, dir) = rs
40         p = subprocess.Popen(['ssh', host, '--', 'bup', 'server'],
41                              stdin=subprocess.PIPE, stdout=subprocess.PIPE)
42     conn = Conn(p.stdout, p.stdin)
43     dir = re.sub(r'[\r\n]', ' ', dir)
44     conn.write('set-dir %s\n' % dir)
45     conn.check_ok()
46     
47     conn.write('list-indexes\n')
48     cachedir = git.repo('index-cache/%s' % re.sub(r'[^@:\w]', '_',
49                                                   "%s:%s" % (host, dir)))
50     packdir = git.repo('objects/pack')
51     mkdirp(cachedir)
52     all = {}
53     needed = {}
54     for line in linereader(conn):
55         if not line:
56             break
57         all[line] = 1
58         assert(line.find('/') < 0)
59         if (not os.path.exists(os.path.join(cachedir, line)) and
60             not os.path.exists(os.path.join(packdir, line))):
61                 needed[line] = 1
62     conn.check_ok()
63                 
64     for f in os.listdir(cachedir):
65         if f.endswith('.idx') and not f in all:
66             log('pruning old index: %r\n' % f)
67             os.unlink(os.path.join(cachedir, f))
68             
69     # FIXME this should be pipelined: request multiple indexes at a time, or
70     # we waste lots of network turnarounds.
71     for name in needed.keys():
72         log('requesting %r\n' % name)
73         conn.write('send-index %s\n' % name)
74         n = struct.unpack('!I', conn.read(4))[0]
75         assert(n)
76         log('   expect %d bytes\n' % n)
77         fn = os.path.join(cachedir, name)
78         f = open(fn + '.tmp', 'w')
79         for b in chunkyreader(conn, n):
80             f.write(b)
81         conn.check_ok()
82         f.close()
83         os.rename(fn + '.tmp', fn)
84     return (p, conn, cachedir)
85
86 if opt.remote:
87     (p, conn, cachedir) = server_connect(opt.remote)
88     conn.write('receive-objects\n')
89     w = git.PackWriter_Remote(conn, objcache = git.MultiPackIndex(cachedir))
90 else:
91     w = git.PackWriter()
92     
93 (shalist,tree) = hashsplit.split_to_tree(w, hashsplit.autofiles(extra))
94
95 if opt.verbose:
96     log('\n')
97 if opt.blobs:
98     for (mode,name,bin) in shalist:
99         print bin.encode('hex')
100 if opt.tree:
101     print tree.encode('hex')
102 if opt.commit or opt.name:
103     msg = 'bup split\n\nGenerated by command:\n%r' % sys.argv
104     ref = opt.name and ('refs/heads/%s' % opt.name) or None
105     commit = w.new_commit(ref, tree, msg)
106     if opt.commit:
107         print commit.encode('hex')
108
109 if opt.remote:
110     w.close()
111     p.stdin.write('quit\n')
112     p.wait()
113
114 secs = time.time() - start_time
115 size = hashsplit.total_split
116 if opt.bench:
117     log('\nbup: %.2fkbytes in %.2f secs = %.2f kbytes/sec\n'
118         % (size/1024., secs, size/1024./secs))