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