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