]> arthur.barton.de Git - bup.git/blob - cmd/split-cmd.py
Configure python, use it, and embed during install
[bup.git] / cmd / split-cmd.py
1 #!/bin/sh
2 """": # -*-python-*-
3 bup_python="$(dirname "$0")/bup-python" || exit $?
4 exec "$bup_python" "$0" ${1+"$@"}
5 """
6 # end of bup preamble
7 import os, sys, time
8 from bup import hashsplit, git, options, client
9 from bup.helpers import *
10
11
12 optspec = """
13 bup split [-t] [-c] [-n name] OPTIONS [--git-ids | filenames...]
14 bup split -b OPTIONS [--git-ids | filenames...]
15 bup split <--noop [--copy]|--copy>  OPTIONS [--git-ids | filenames...]
16 --
17  Modes:
18 b,blobs    output a series of blob ids.  Implies --fanout=0.
19 t,tree     output a tree id
20 c,commit   output a commit id
21 n,name=    save the result under the given name
22 noop       split the input, but throw away the result
23 copy       split the input, copy it to stdout, don't save to repo
24  Options:
25 r,remote=  remote repository path
26 d,date=    date for the commit (seconds since the epoch)
27 q,quiet    don't print progress messages
28 v,verbose  increase log output (can be used more than once)
29 git-ids    read a list of git object ids from stdin and split their contents
30 keep-boundaries  don't let one chunk span two input files
31 bench      print benchmark timings to stderr
32 max-pack-size=  maximum bytes in a single pack
33 max-pack-objects=  maximum number of objects in a single pack
34 fanout=    average number of blobs in a single tree
35 bwlimit=   maximum bytes/sec to transmit to server
36 #,compress=  set compression level to # (0-9, 9 is highest) [1]
37 """
38 o = options.Options(optspec)
39 (opt, flags, extra) = o.parse(sys.argv[1:])
40
41 handle_ctrl_c()
42 git.check_repo_or_die()
43 if not (opt.blobs or opt.tree or opt.commit or opt.name or
44         opt.noop or opt.copy):
45     o.fatal("use one or more of -b, -t, -c, -n, --noop, --copy")
46 if (opt.noop or opt.copy) and (opt.blobs or opt.tree or
47                                opt.commit or opt.name):
48     o.fatal('--noop and --copy are incompatible with -b, -t, -c, -n')
49 if opt.blobs and (opt.tree or opt.commit or opt.name):
50     o.fatal('-b is incompatible with -t, -c, -n')
51 if extra and opt.git_ids:
52     o.fatal("don't provide filenames when using --git-ids")
53
54 if opt.verbose >= 2:
55     git.verbose = opt.verbose - 1
56     opt.bench = 1
57 if opt.max_pack_size:
58     git.max_pack_size = parse_num(opt.max_pack_size)
59 if opt.max_pack_objects:
60     git.max_pack_objects = parse_num(opt.max_pack_objects)
61 if opt.fanout:
62     hashsplit.fanout = parse_num(opt.fanout)
63 if opt.blobs:
64     hashsplit.fanout = 0
65 if opt.bwlimit:
66     client.bwlimit = parse_num(opt.bwlimit)
67 if opt.date:
68     date = parse_date_or_fatal(opt.date, o.fatal)
69 else:
70     date = time.time()
71
72 total_bytes = 0
73 def prog(filenum, nbytes):
74     global total_bytes
75     total_bytes += nbytes
76     if filenum > 0:
77         qprogress('Splitting: file #%d, %d kbytes\r'
78                   % (filenum+1, total_bytes/1024))
79     else:
80         qprogress('Splitting: %d kbytes\r' % (total_bytes/1024))
81
82
83 is_reverse = os.environ.get('BUP_SERVER_REVERSE')
84 if is_reverse and opt.remote:
85     o.fatal("don't use -r in reverse mode; it's automatic")
86 start_time = time.time()
87
88 if opt.name and opt.name.startswith('.'):
89     o.fatal("'%s' is not a valid branch name." % opt.name)
90 refname = opt.name and 'refs/heads/%s' % opt.name or None
91 if opt.noop or opt.copy:
92     cli = pack_writer = oldref = None
93 elif opt.remote or is_reverse:
94     cli = client.Client(opt.remote)
95     oldref = refname and cli.read_ref(refname) or None
96     pack_writer = cli.new_packwriter(compression_level=opt.compress)
97 else:
98     cli = None
99     oldref = refname and git.read_ref(refname) or None
100     pack_writer = git.PackWriter(compression_level=opt.compress)
101
102 if opt.git_ids:
103     # the input is actually a series of git object ids that we should retrieve
104     # and split.
105     #
106     # This is a bit messy, but basically it converts from a series of
107     # CatPipe.get() iterators into a series of file-type objects.
108     # It would be less ugly if either CatPipe.get() returned a file-like object
109     # (not very efficient), or split_to_shalist() expected an iterator instead
110     # of a file.
111     cp = git.CatPipe()
112     class IterToFile:
113         def __init__(self, it):
114             self.it = iter(it)
115         def read(self, size):
116             v = next(self.it, None)
117             return v or ''
118     def read_ids():
119         while 1:
120             line = sys.stdin.readline()
121             if not line:
122                 break
123             if line:
124                 line = line.strip()
125             try:
126                 it = cp.get(line.strip())
127                 next(it, None)  # skip the file type
128             except KeyError, e:
129                 add_error('error: %s' % e)
130                 continue
131             yield IterToFile(it)
132     files = read_ids()
133 else:
134     # the input either comes from a series of files or from stdin.
135     files = extra and (open(fn) for fn in extra) or [sys.stdin]
136
137 if pack_writer and opt.blobs:
138     shalist = hashsplit.split_to_blobs(pack_writer.new_blob, files,
139                                        keep_boundaries=opt.keep_boundaries,
140                                        progress=prog)
141     for (sha, size, level) in shalist:
142         print sha.encode('hex')
143         reprogress()
144 elif pack_writer:  # tree or commit or name
145     if opt.name: # insert dummy_name which may be used as a restore target
146         mode, sha = \
147             hashsplit.split_to_blob_or_tree(pack_writer.new_blob,
148                                             pack_writer.new_tree,
149                                             files,
150                                             keep_boundaries=opt.keep_boundaries,
151                                             progress=prog)
152         splitfile_name = git.mangle_name('data', hashsplit.GIT_MODE_FILE, mode)
153         shalist = [(mode, splitfile_name, sha)]
154     else:
155         shalist = hashsplit.split_to_shalist(
156                       pack_writer.new_blob, pack_writer.new_tree, files,
157                       keep_boundaries=opt.keep_boundaries, progress=prog)
158     tree = pack_writer.new_tree(shalist)
159 else:
160     last = 0
161     it = hashsplit.hashsplit_iter(files,
162                                   keep_boundaries=opt.keep_boundaries,
163                                   progress=prog)
164     for (blob, level) in it:
165         hashsplit.total_split += len(blob)
166         if opt.copy:
167             sys.stdout.write(str(blob))
168         megs = hashsplit.total_split/1024/1024
169         if not opt.quiet and last != megs:
170             last = megs
171
172 if opt.verbose:
173     log('\n')
174 if opt.tree:
175     print tree.encode('hex')
176 if opt.commit or opt.name:
177     msg = 'bup split\n\nGenerated by command:\n%r\n' % sys.argv
178     ref = opt.name and ('refs/heads/%s' % opt.name) or None
179     commit = pack_writer.new_commit(oldref, tree, date, msg)
180     if opt.commit:
181         print commit.encode('hex')
182
183 if pack_writer:
184     pack_writer.close()  # must close before we can update the ref
185
186 if opt.name:
187     if cli:
188         cli.update_ref(refname, commit, oldref)
189     else:
190         git.update_ref(refname, commit, oldref)
191
192 if cli:
193     cli.close()
194
195 secs = time.time() - start_time
196 size = hashsplit.total_split
197 if opt.bench:
198     log('bup: %.2fkbytes in %.2f secs = %.2f kbytes/sec\n'
199         % (size/1024., secs, size/1024./secs))
200
201 if saved_errors:
202     log('WARNING: %d errors encountered while saving.\n' % len(saved_errors))
203     sys.exit(1)