]> arthur.barton.de Git - bup.git/blob - cmd/split-cmd.py
Merge branch 'master' into meta
[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 <-t|-c|-b|-n name|--copy|--noop> [--bench] [filenames...]
9 --
10  Modes:
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=    save the result under the given name
15 noop       split the input, but throw away the result
16 copy       split the input, copy it to stdout, don't save to repo
17  Options:
18 r,remote=  remote repository path
19 d,date=    date for the commit (seconds since the epoch)
20 q,quiet    don't print progress messages
21 v,verbose  increase log output (can be used more than once)
22 git-ids    read a list of git object ids from stdin and split their contents
23 keep-boundaries  don't let one chunk span two input files
24 bench      print benchmark timings to stderr
25 max-pack-size=  maximum bytes in a single pack
26 max-pack-objects=  maximum number of objects in a single pack
27 fanout=    maximum number of blobs in a single tree
28 bwlimit=   maximum bytes/sec to transmit to server
29 """
30 o = options.Options(optspec)
31 (opt, flags, extra) = o.parse(sys.argv[1:])
32
33 handle_ctrl_c()
34 git.check_repo_or_die()
35 if not (opt.blobs or opt.tree or opt.commit or opt.name or
36         opt.noop or opt.copy):
37     o.fatal("use one or more of -b, -t, -c, -n, -N, --copy")
38 if (opt.noop or opt.copy) and (opt.blobs or opt.tree or 
39                                opt.commit or opt.name):
40     o.fatal('-N and --copy are incompatible with -b, -t, -c, -n')
41 if opt.blobs and (opt.tree or opt.commit or opt.name):
42     o.fatal('-b is incompatible with -t, -c, -n')
43 if extra and opt.git_ids:
44     o.fatal("don't provide filenames when using --git-ids")
45
46 if opt.verbose >= 2:
47     git.verbose = opt.verbose - 1
48     opt.bench = 1
49 if opt.max_pack_size:
50     git.max_pack_size = parse_num(opt.max_pack_size)
51 if opt.max_pack_objects:
52     git.max_pack_objects = parse_num(opt.max_pack_objects)
53 if opt.fanout:
54     hashsplit.fanout = parse_num(opt.fanout)
55 if opt.blobs:
56     hashsplit.fanout = 0
57 if opt.bwlimit:
58     client.bwlimit = parse_num(opt.bwlimit)
59 if opt.date:
60     date = parse_date_or_fatal(opt.date, o.fatal)
61 else:
62     date = time.time()
63
64
65 total_bytes = 0
66 def prog(filenum, nbytes):
67     global total_bytes
68     total_bytes += nbytes
69     if filenum > 0:
70         qprogress('Splitting: file #%d, %d kbytes\r'
71                   % (filenum+1, total_bytes/1024))
72     else:
73         qprogress('Splitting: %d kbytes\r' % (total_bytes/1024))
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 and opt.blobs:
131     shalist = hashsplit.split_to_blobs(pack_writer.new_blob, files,
132                                        keep_boundaries=opt.keep_boundaries,
133                                        progress=prog)
134     for (sha, size, level) in shalist:
135         print sha.encode('hex')
136         reprogress()
137 elif pack_writer:  # tree or commit or name
138     shalist = hashsplit.split_to_shalist(pack_writer.new_blob,
139                                          pack_writer.new_tree,
140                                          files,
141                                          keep_boundaries=opt.keep_boundaries,
142                                          progress=prog)
143     tree = pack_writer.new_tree(shalist)
144 else:
145     last = 0
146     it = hashsplit.hashsplit_iter(files,
147                                   keep_boundaries=opt.keep_boundaries,
148                                   progress=prog)
149     for (blob, level) in it:
150         hashsplit.total_split += len(blob)
151         if opt.copy:
152             sys.stdout.write(str(blob))
153         megs = hashsplit.total_split/1024/1024
154         if not opt.quiet and last != megs:
155             last = megs
156
157 if opt.verbose:
158     log('\n')
159 if opt.tree:
160     print tree.encode('hex')
161 if opt.commit or opt.name:
162     msg = 'bup split\n\nGenerated by command:\n%r' % sys.argv
163     ref = opt.name and ('refs/heads/%s' % opt.name) or None
164     commit = pack_writer.new_commit(oldref, tree, date, msg)
165     if opt.commit:
166         print commit.encode('hex')
167
168 if pack_writer:
169     pack_writer.close()  # must close before we can update the ref
170
171 if opt.name:
172     if cli:
173         cli.update_ref(refname, commit, oldref)
174     else:
175         git.update_ref(refname, commit, oldref)
176
177 if cli:
178     cli.close()
179
180 secs = time.time() - start_time
181 size = hashsplit.total_split
182 if opt.bench:
183     log('bup: %.2fkbytes in %.2f secs = %.2f kbytes/sec\n'
184         % (size/1024., secs, size/1024./secs))
185
186 if saved_errors:
187     log('WARNING: %d errors encountered while saving.\n' % len(saved_errors))
188     sys.exit(1)