]> arthur.barton.de Git - bup.git/blob - cmd/split-cmd.py
Refuse branch/tag names that start with a dot
[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 if opt.name and opt.name.startswith('.'):
82     o.fatal("'%s' is not a valid branch name." % opt.name)
83 refname = opt.name and 'refs/heads/%s' % opt.name or None
84 if opt.noop or opt.copy:
85     cli = pack_writer = oldref = None
86 elif opt.remote or is_reverse:
87     cli = client.Client(opt.remote)
88     oldref = refname and cli.read_ref(refname) or None
89     pack_writer = cli.new_packwriter()
90 else:
91     cli = None
92     oldref = refname and git.read_ref(refname) or None
93     pack_writer = git.PackWriter()
94
95 if opt.git_ids:
96     # the input is actually a series of git object ids that we should retrieve
97     # and split.
98     #
99     # This is a bit messy, but basically it converts from a series of
100     # CatPipe.get() iterators into a series of file-type objects.
101     # It would be less ugly if either CatPipe.get() returned a file-like object
102     # (not very efficient), or split_to_shalist() expected an iterator instead
103     # of a file.
104     cp = git.CatPipe()
105     class IterToFile:
106         def __init__(self, it):
107             self.it = iter(it)
108         def read(self, size):
109             v = next(self.it)
110             return v or ''
111     def read_ids():
112         while 1:
113             line = sys.stdin.readline()
114             if not line:
115                 break
116             if line:
117                 line = line.strip()
118             try:
119                 it = cp.get(line.strip())
120                 next(it)  # skip the file type
121             except KeyError, e:
122                 add_error('error: %s' % e)
123                 continue
124             yield IterToFile(it)
125     files = read_ids()
126 else:
127     # the input either comes from a series of files or from stdin.
128     files = extra and (open(fn) for fn in extra) or [sys.stdin]
129
130 if pack_writer:
131     shalist = hashsplit.split_to_shalist(pack_writer, files,
132                                          keep_boundaries=opt.keep_boundaries,
133                                          progress=prog)
134     tree = pack_writer.new_tree(shalist)
135 else:
136     last = 0
137     for (blob, bits) in hashsplit.hashsplit_iter(files,
138                                     keep_boundaries=opt.keep_boundaries,
139                                     progress=prog):
140         hashsplit.total_split += len(blob)
141         if opt.copy:
142             sys.stdout.write(str(blob))
143         megs = hashsplit.total_split/1024/1024
144         if not opt.quiet and last != megs:
145             progress('%d Mbytes read\r' % megs)
146             last = megs
147     progress('%d Mbytes read, done.\n' % megs)
148
149 if opt.verbose:
150     log('\n')
151 if opt.blobs:
152     for (mode,name,bin) in shalist:
153         print bin.encode('hex')
154 if opt.tree:
155     print tree.encode('hex')
156 if opt.commit or opt.name:
157     msg = 'bup split\n\nGenerated by command:\n%r' % sys.argv
158     ref = opt.name and ('refs/heads/%s' % opt.name) or None
159     commit = pack_writer.new_commit(oldref, tree, date, msg)
160     if opt.commit:
161         print commit.encode('hex')
162
163 if pack_writer:
164     pack_writer.close()  # must close before we can update the ref
165
166 if opt.name:
167     if cli:
168         cli.update_ref(refname, commit, oldref)
169     else:
170         git.update_ref(refname, commit, oldref)
171
172 if cli:
173     cli.close()
174
175 secs = time.time() - start_time
176 size = hashsplit.total_split
177 if opt.bench:
178     log('\nbup: %.2fkbytes in %.2f secs = %.2f kbytes/sec\n'
179         % (size/1024., secs, size/1024./secs))
180
181 if saved_errors:
182     log('WARNING: %d errors encountered while saving.\n' % len(saved_errors))
183     sys.exit(1)