]> arthur.barton.de Git - bup.git/blob - t/test-get
cirrus: allow failures on macos with python 2
[bup.git] / t / test-get
1 #!/bin/sh
2 """": # -*-python-*-
3 # https://sourceware.org/bugzilla/show_bug.cgi?id=26034
4 export "BUP_ARGV_0"="$0"
5 arg_i=1
6 for arg in "$@"; do
7     export "BUP_ARGV_${arg_i}"="$arg"
8     shift
9     arg_i=$((arg_i + 1))
10 done
11 bup_python="$(dirname "$0")/../dev/bup-python" || exit $?
12 exec "$bup_python" "$0"
13 """
14 # end of bup preamble
15
16 from __future__ import print_function
17 from errno import ENOENT
18 from os import chdir, mkdir, rename
19 from os.path import abspath, dirname
20 from shutil import rmtree
21 from subprocess import PIPE
22 import os, re, sys
23
24 # For buptest, wvtest, ...
25 sys.path[:0] = (abspath(os.path.dirname(__file__) + '/..'),)
26 sys.path[:0] = [os.path.dirname(os.path.realpath(__file__)) + '/../lib']
27
28
29 from bup import compat, path
30 from bup.compat import environ, getcwd, items
31 from bup.helpers import bquote, merge_dict, unlink
32 from bup.io import byte_stream
33 from buptest import ex, exo, test_tempdir
34 from wvtest import wvcheck, wvfail, wvmsg, wvpass, wvpasseq, wvpassne, wvstart
35 import bup.path
36
37 sys.stdout.flush()
38 stdout = byte_stream(sys.stdout)
39
40 # FIXME: per-test function
41 environ[b'GIT_AUTHOR_NAME'] = b'bup test-get'
42 environ[b'GIT_COMMITTER_NAME'] = b'bup test-get'
43 environ[b'GIT_AUTHOR_EMAIL'] = b'bup@85430dcca2b611e4b2c3-8f5691723476'
44 environ[b'GIT_COMMITTER_EMAIL'] = b'bup@85430dcca2b611e4b2c3-8f5691723476'
45
46 # The clean-repo test can probably be applied more broadly.  It was
47 # initially just applied to test-pick to catch a bug.
48
49 top = getcwd()
50 bup_cmd = bup.path.exe()
51
52 def rmrf(path):
53     err = []  # because python's scoping mess...
54     def onerror(function, path, excinfo):
55         err.append((function, path, excinfo))
56     rmtree(path, onerror=onerror)
57     if err:
58         function, path, excinfo = err[0]
59         ex_type, ex, traceback = excinfo
60         if (not isinstance(ex, OSError)) or ex.errno != ENOENT:
61             raise ex
62
63 def verify_trees_match(path1, path2):
64     global top
65     exr = exo((top + b'/t/compare-trees', b'-c', path1, path2), check=False)
66     stdout.write(exr.out)
67     sys.stdout.flush()
68     wvcheck(exr.rc == 0, 'process exit %d == 0' % exr.rc)
69
70 def verify_rcz(cmd, **kwargs):
71     assert not kwargs.get('check')
72     kwargs['check'] = False
73     result = exo(cmd, **kwargs)
74     stdout.write(result.out)
75     rc = result.proc.returncode
76     wvcheck(rc == 0, 'process exit %d == 0' % rc)
77     return result
78
79 # FIXME: multline, or allow opts generally?
80
81 def verify_rx(rx, string):
82     wvcheck(re.search(rx, string), 'rx %r matches %r' % (rx, string))
83
84 def verify_nrx(rx, string):
85     wvcheck(not re.search(rx, string), "rx %r doesn't match %r" % (rx, string))
86
87 def validate_clean_repo():
88     out = verify_rcz((b'git', b'--git-dir', b'get-dest', b'fsck')).out
89     verify_nrx(br'dangling|mismatch|missing|unreachable', out)
90     
91 def validate_blob(src_id, dest_id):
92     global top
93     rmrf(b'restore-src')
94     rmrf(b'restore-dest')
95     cat_tree = top + b'/t/git-cat-tree'
96     src_blob = verify_rcz((cat_tree, b'--git-dir', b'get-src', src_id)).out
97     dest_blob = verify_rcz((cat_tree, b'--git-dir', b'get-src', src_id)).out
98     wvpasseq(src_blob, dest_blob)
99
100 def validate_tree(src_id, dest_id):
101
102     rmrf(b'restore-src')
103     rmrf(b'restore-dest')
104     mkdir(b'restore-src')
105     mkdir(b'restore-dest')
106     
107     commit_env = merge_dict(environ, {b'GIT_COMMITTER_DATE': b'2014-01-01 01:01'})
108
109     # Create a commit so the archive contents will have matching timestamps.
110     src_c = exo((b'git', b'--git-dir', b'get-src',
111                  b'commit-tree', b'-m', b'foo', src_id),
112                 env=commit_env).out.strip()
113     dest_c = exo((b'git', b'--git-dir', b'get-dest',
114                   b'commit-tree', b'-m', b'foo', dest_id),
115                  env=commit_env).out.strip()
116     exr = verify_rcz(b'git --git-dir get-src archive %s | tar xvf - -C restore-src'
117                      % bquote(src_c),
118                      shell=True)
119     if exr.rc != 0: return False
120     exr = verify_rcz(b'git --git-dir get-dest archive %s | tar xvf - -C restore-dest'
121                      % bquote(dest_c),
122                      shell=True)
123     if exr.rc != 0: return False
124     
125     # git archive doesn't include an entry for ./.
126     unlink(b'restore-src/pax_global_header')
127     unlink(b'restore-dest/pax_global_header')
128     ex((b'touch', b'-r', b'restore-src', b'restore-dest'))
129     verify_trees_match(b'restore-src/', b'restore-dest/')
130     rmrf(b'restore-src')
131     rmrf(b'restore-dest')
132
133 def validate_commit(src_id, dest_id):
134     exr = verify_rcz((b'git', b'--git-dir', b'get-src', b'cat-file', b'commit', src_id))
135     if exr.rc != 0: return False
136     src_cat = exr.out
137     exr = verify_rcz((b'git', b'--git-dir', b'get-dest', b'cat-file', b'commit', dest_id))
138     if exr.rc != 0: return False
139     dest_cat = exr.out
140     wvpasseq(src_cat, dest_cat)
141     if src_cat != dest_cat: return False
142     
143     rmrf(b'restore-src')
144     rmrf(b'restore-dest')
145     mkdir(b'restore-src')
146     mkdir(b'restore-dest')
147     qsrc = bquote(src_id)
148     qdest = bquote(dest_id)
149     exr = verify_rcz((b'git --git-dir get-src archive ' + qsrc
150                       + b' | tar xf - -C restore-src'),
151                      shell=True)
152     if exr.rc != 0: return False
153     exr = verify_rcz((b'git --git-dir get-dest archive ' + qdest +
154                       b' | tar xf - -C restore-dest'),
155                      shell=True)
156     if exr.rc != 0: return False
157     
158     # git archive doesn't include an entry for ./.
159     ex((b'touch', b'-r', b'restore-src', b'restore-dest'))
160     verify_trees_match(b'restore-src/', b'restore-dest/')
161     rmrf(b'restore-src')
162     rmrf(b'restore-dest')
163
164 def _validate_save(orig_dir, save_path, commit_id, tree_id):
165     global bup_cmd
166     rmrf(b'restore')
167     exr = verify_rcz((bup_cmd, b'-d', b'get-dest',
168                       b'restore', b'-C', b'restore', save_path + b'/.'))
169     if exr.rc: return False
170     verify_trees_match(orig_dir + b'/', b'restore/')
171     if tree_id:
172         # FIXME: double check that get-dest is correct
173         exr = verify_rcz((b'git', b'--git-dir', b'get-dest', b'ls-tree', tree_id))
174         if exr.rc: return False
175         cat = verify_rcz((b'git', b'--git-dir', b'get-dest',
176                           b'cat-file', b'commit', commit_id))
177         if cat.rc: return False
178         wvpasseq(b'tree ' + tree_id, cat.out.splitlines()[0])
179
180 # FIXME: re-merge save and new_save?
181         
182 def validate_save(dest_name, restore_subpath, commit_id, tree_id, orig_value,
183                   get_out):
184     out = get_out.splitlines()
185     print('blarg: out', repr(out), file=sys.stderr)
186     wvpasseq(2, len(out))
187     get_tree_id = out[0]
188     get_commit_id = out[1]
189     wvpasseq(tree_id, get_tree_id)
190     wvpasseq(commit_id, get_commit_id)
191     _validate_save(orig_value, dest_name + restore_subpath, commit_id, tree_id)
192
193 def validate_new_save(dest_name, restore_subpath, commit_id, tree_id, orig_value,
194                       get_out):
195     out = get_out.splitlines()
196     wvpasseq(2, len(out))
197     get_tree_id = out[0]
198     get_commit_id = out[1]
199     wvpasseq(tree_id, get_tree_id)
200     wvpassne(commit_id, get_commit_id)
201     _validate_save(orig_value, dest_name + restore_subpath, get_commit_id, tree_id)
202         
203 def validate_tagged_save(tag_name, restore_subpath,
204                          commit_id, tree_id, orig_value, get_out):
205     out = get_out.splitlines()
206     wvpasseq(1, len(out))
207     get_tag_id = out[0]
208     wvpasseq(commit_id, get_tag_id)
209     # Make sure tmp doesn't already exist.
210     exr = exo((b'git', b'--git-dir', b'get-dest', b'show-ref', b'tmp-branch-for-tag'),
211               check=False)
212     wvpasseq(1, exr.rc)
213
214     ex((b'git', b'--git-dir', b'get-dest', b'branch', b'tmp-branch-for-tag',
215         b'refs/tags/' + tag_name))
216     _validate_save(orig_value, b'tmp-branch-for-tag/latest' + restore_subpath,
217                    commit_id, tree_id)
218     ex((b'git', b'--git-dir', b'get-dest', b'branch', b'-D', b'tmp-branch-for-tag'))
219
220 def validate_new_tagged_commit(tag_name, commit_id, tree_id, get_out):
221     out = get_out.splitlines()
222     wvpasseq(1, len(out))
223     get_tag_id = out[0]
224     wvpassne(commit_id, get_tag_id)
225     validate_tree(tree_id, tag_name + b':')
226
227
228 get_cases_tested = 0
229
230 def _run_get(disposition, method, what):
231     print('run_get:', repr((disposition, method, what)), file=sys.stderr)
232     global bup_cmd
233
234     if disposition == 'get':
235         get_cmd = (bup_cmd, b'-d', b'get-dest',
236                    b'get', b'-vvct', b'--print-tags', b'-s', b'get-src')
237     elif disposition == 'get-on':
238         get_cmd = (bup_cmd, b'-d', b'get-dest',
239                    b'on', b'-', b'get', b'-vvct', b'--print-tags', b'-s', b'get-src')
240     elif disposition == 'get-to':
241         get_cmd = (bup_cmd, b'-d', b'get-dest',
242                    b'get', b'-vvct', b'--print-tags', b'-s', b'get-src',
243                    b'-r', b'-:' + getcwd() + b'/get-dest')
244     else:
245         raise Exception('error: unexpected get disposition ' + repr(disposition))
246     
247     global get_cases_tested
248     if isinstance(what, bytes):
249         cmd = get_cmd + (method, what)
250     else:
251         assert not isinstance(what, str)  # python 3 sanity check
252         if method in (b'--ff', b'--append', b'--pick', b'--force-pick', b'--new-tag',
253                       b'--replace'):
254             method += b':'
255         src, dest = what
256         cmd = get_cmd + (method, src, dest)
257     result = exo(cmd, check=False, stderr=PIPE)
258     get_cases_tested += 1
259     fsck = ex((bup_cmd, b'-d', b'get-dest', b'fsck'), check=False)
260     wvpasseq(0, fsck.rc)
261     return result
262
263 def run_get(disposition, method, what=None, given=None):
264     global bup_cmd
265     rmrf(b'get-dest')
266     ex((bup_cmd, b'-d', b'get-dest', b'init'))
267
268     if given:
269         # FIXME: replace bup-get with independent commands as is feasible
270         exr = _run_get(disposition, b'--replace', given)
271         assert not exr.rc
272     return _run_get(disposition, method, what)
273
274 def test_universal_behaviors(get_disposition):
275     methods = (b'--ff', b'--append', b'--pick', b'--force-pick', b'--new-tag',
276                b'--replace', b'--unnamed')
277     for method in methods:
278         mmsg = method.decode('ascii')
279         wvstart(get_disposition + ' ' + mmsg + ', missing source, fails')
280         exr = run_get(get_disposition, method, b'not-there')
281         wvpassne(0, exr.rc)
282         verify_rx(br'cannot find source', exr.err)
283     for method in methods:
284         mmsg = method.decode('ascii')
285         wvstart(get_disposition + ' ' + mmsg + ' / fails')
286         exr = run_get(get_disposition, method, b'/')
287         wvpassne(0, exr.rc)
288         verify_rx(b'cannot fetch entire repository', exr.err)
289
290 def verify_only_refs(**kwargs):
291     for kind, refs in items(kwargs):
292         if kind == 'heads':
293             abs_refs = [b'refs/heads/' + ref for ref in refs]
294             karg = b'--heads'
295         elif kind == 'tags':
296             abs_refs = [b'refs/tags/' + ref for ref in refs]
297             karg = b'--tags'
298         else:
299             raise TypeError('unexpected keyword argument %r' % kind)
300         if abs_refs:
301             verify_rcz([b'git', b'--git-dir', b'get-dest',
302                         b'show-ref', b'--verify', karg] + abs_refs)
303             exr = exo((b'git', b'--git-dir', b'get-dest', b'show-ref', karg),
304                       check=False)
305             wvpasseq(0, exr.rc)
306             expected_refs = sorted(abs_refs)
307             repo_refs = sorted([x.split()[1] for x in exr.out.splitlines()])
308             wvpasseq(expected_refs, repo_refs)
309         else:
310             # FIXME: can we just check "git show-ref --heads == ''"?
311             exr = exo((b'git', b'--git-dir', b'get-dest', b'show-ref', karg),
312                       check=False)
313             wvpasseq(1, exr.rc)
314             wvpasseq(b'', exr.out.strip())
315
316 def test_replace(get_disposition, src_info):
317     print('blarg:', repr(src_info), file=sys.stderr)
318
319     wvstart(get_disposition + ' --replace to root fails')
320     for item in (b'.tag/tinyfile',
321                  b'src/latest' + src_info['tinyfile-path'],
322                  b'.tag/subtree',
323                  b'src/latest' + src_info['subtree-vfs-path'],
324                  b'.tag/commit-1',
325                  b'src/latest',
326                  b'src'):
327         exr = run_get(get_disposition, b'--replace', (item, b'/'))
328         wvpassne(0, exr.rc)
329         verify_rx(br'impossible; can only overwrite branch or tag', exr.err)
330
331     tinyfile_id = src_info['tinyfile-id']
332     tinyfile_path = src_info['tinyfile-path']
333     subtree_vfs_path = src_info['subtree-vfs-path']
334     subtree_id = src_info['subtree-id']
335     commit_2_id = src_info['commit-2-id']
336     tree_2_id = src_info['tree-2-id']
337
338     # Anything to tag
339     existing_items = {'nothing' : None,
340                       'blob' : (b'.tag/tinyfile', b'.tag/obj'),
341                       'tree' : (b'.tag/tree-1', b'.tag/obj'),
342                       'commit': (b'.tag/commit-1', b'.tag/obj')}
343     for ex_type, ex_ref in items(existing_items):
344         wvstart(get_disposition + ' --replace ' + ex_type + ' with blob tag')
345         for item in (b'.tag/tinyfile', b'src/latest' + tinyfile_path):
346             exr = run_get(get_disposition, b'--replace', (item ,b'.tag/obj'),
347                           given=ex_ref)
348             wvpasseq(0, exr.rc)        
349             validate_blob(tinyfile_id, tinyfile_id)
350             verify_only_refs(heads=[], tags=(b'obj',))
351         wvstart(get_disposition + ' --replace ' + ex_type + ' with tree tag')
352         for item in (b'.tag/subtree',  b'src/latest' + subtree_vfs_path):
353             exr = run_get(get_disposition, b'--replace', (item, b'.tag/obj'),
354                           given=ex_ref)
355             validate_tree(subtree_id, subtree_id)
356             verify_only_refs(heads=[], tags=(b'obj',))
357         wvstart(get_disposition + ' --replace ' + ex_type + ' with commitish tag')
358         for item in (b'.tag/commit-2', b'src/latest', b'src'):
359             exr = run_get(get_disposition, b'--replace', (item, b'.tag/obj'),
360                           given=ex_ref)
361             validate_tagged_save(b'obj', getcwd() + b'/src',
362                                  commit_2_id, tree_2_id, b'src-2', exr.out)
363             verify_only_refs(heads=[], tags=(b'obj',))
364
365         # Committish to branch.
366         existing_items = (('nothing', None),
367                           ('branch', (b'.tag/commit-1', b'obj')))
368         for ex_type, ex_ref in existing_items:
369             for item_type, item in (('commit', b'.tag/commit-2'),
370                                     ('save', b'src/latest'),
371                                     ('branch', b'src')):
372                 wvstart(get_disposition + ' --replace '
373                         + ex_type + ' with ' + item_type)
374                 exr = run_get(get_disposition, b'--replace', (item, b'obj'),
375                               given=ex_ref)
376                 validate_save(b'obj/latest', getcwd() + b'/src',
377                               commit_2_id, tree_2_id, b'src-2', exr.out)
378                 verify_only_refs(heads=(b'obj',), tags=[])
379
380         # Not committish to branch
381         existing_items = (('nothing', None),
382                           ('branch', (b'.tag/commit-1', b'obj')))
383         for ex_type, ex_ref in existing_items:
384             for item_type, item in (('blob', b'.tag/tinyfile'),
385                                     ('blob', b'src/latest' + tinyfile_path),
386                                     ('tree', b'.tag/subtree'),
387                                     ('tree', b'src/latest' + subtree_vfs_path)):
388                 wvstart(get_disposition + ' --replace branch with '
389                         + item_type + ' given ' + ex_type + ' fails')
390
391                 exr = run_get(get_disposition, b'--replace', (item, b'obj'),
392                               given=ex_ref)
393                 wvpassne(0, exr.rc)
394                 verify_rx(br'cannot overwrite branch with .+ for', exr.err)
395
396         wvstart(get_disposition + ' --replace, implicit destinations')
397
398         exr = run_get(get_disposition, b'--replace', b'src')
399         validate_save(b'src/latest', getcwd() + b'/src',
400                       commit_2_id, tree_2_id, b'src-2', exr.out)
401         verify_only_refs(heads=(b'src',), tags=[])
402
403         exr = run_get(get_disposition, b'--replace', b'.tag/commit-2')
404         validate_tagged_save(b'commit-2', getcwd() + b'/src',
405                              commit_2_id, tree_2_id, b'src-2', exr.out)
406         verify_only_refs(heads=[], tags=(b'commit-2',))
407
408 def test_ff(get_disposition, src_info):
409
410     wvstart(get_disposition + ' --ff to root fails')
411     tinyfile_path = src_info['tinyfile-path']
412     for item in (b'.tag/tinyfile', b'src/latest' + tinyfile_path):
413         exr = run_get(get_disposition, b'--ff', (item, b'/'))
414         wvpassne(0, exr.rc)
415         verify_rx(br'source for .+ must be a branch, save, or commit', exr.err)
416     subtree_vfs_path = src_info['subtree-vfs-path']
417     for item in (b'.tag/subtree', b'src/latest' + subtree_vfs_path):
418         exr = run_get(get_disposition, b'--ff', (item, b'/'))
419         wvpassne(0, exr.rc)
420         verify_rx(br'is impossible; can only --append a tree to a branch',
421                   exr.err)    
422     for item in (b'.tag/commit-1', b'src/latest', b'src'):
423         exr = run_get(get_disposition, b'--ff', (item, b'/'))
424         wvpassne(0, exr.rc)
425         verify_rx(br'destination for .+ is a root, not a branch', exr.err)
426
427     wvstart(get_disposition + ' --ff of not-committish fails')
428     for src in (b'.tag/tinyfile', b'src/latest' + tinyfile_path):
429         # FIXME: use get_item elsewhere?
430         for given, get_item in ((None, (src, b'obj')),
431                                 (None, (src, b'.tag/obj')),
432                                 ((b'.tag/tinyfile', b'.tag/obj'), (src, b'.tag/obj')),
433                                 ((b'.tag/tree-1', b'.tag/obj'), (src, b'.tag/obj')),
434                                 ((b'.tag/commit-1', b'.tag/obj'), (src, b'.tag/obj')),
435                                 ((b'.tag/commit-1', b'obj'), (src, b'obj'))):
436             exr = run_get(get_disposition, b'--ff', get_item, given=given)
437             wvpassne(0, exr.rc)
438             verify_rx(br'must be a branch, save, or commit', exr.err)
439     for src in (b'.tag/subtree', b'src/latest' + subtree_vfs_path):
440         for given, get_item in ((None, (src, b'obj')),
441                                 (None, (src, b'.tag/obj')),
442                                 ((b'.tag/tinyfile', b'.tag/obj'), (src, b'.tag/obj')),
443                                 ((b'.tag/tree-1', b'.tag/obj'), (src, b'.tag/obj')),
444                                 ((b'.tag/commit-1', b'.tag/obj'), (src, b'.tag/obj')),
445                                 ((b'.tag/commit-1', b'obj'), (src, b'obj'))):
446             exr = run_get(get_disposition, b'--ff', get_item, given=given)
447             wvpassne(0, exr.rc)
448             verify_rx(br'can only --append a tree to a branch', exr.err)
449
450     wvstart(get_disposition + ' --ff committish, ff possible')
451     save_2 = src_info['save-2']
452     for src in (b'.tag/commit-2', b'src/' + save_2, b'src'):
453         for given, get_item, complaint in \
454             ((None, (src, b'.tag/obj'),
455               br'destination .+ must be a valid branch name'),
456              ((b'.tag/tinyfile', b'.tag/obj'), (src, b'.tag/obj'),
457               br'destination .+ is a blob, not a branch'),
458              ((b'.tag/tree-1', b'.tag/obj'), (src, b'.tag/obj'),
459               br'destination .+ is a tree, not a branch'),
460              ((b'.tag/commit-1', b'.tag/obj'), (src, b'.tag/obj'),
461               br'destination .+ is a tagged commit, not a branch'),
462              ((b'.tag/commit-2', b'.tag/obj'), (src, b'.tag/obj'),
463               br'destination .+ is a tagged commit, not a branch')):
464             exr = run_get(get_disposition, b'--ff', get_item, given=given)
465             wvpassne(0, exr.rc)
466             verify_rx(complaint, exr.err)
467     # FIXME: use src or item and given or existing consistently in loops...
468     commit_2_id = src_info['commit-2-id']
469     tree_2_id = src_info['tree-2-id']
470     for src in (b'.tag/commit-2', b'src/' + save_2, b'src'):
471         for given in (None, (b'.tag/commit-1', b'obj'), (b'.tag/commit-2', b'obj')):
472             exr = run_get(get_disposition, b'--ff', (src, b'obj'), given=given)
473             wvpasseq(0, exr.rc)
474             validate_save(b'obj/latest', getcwd() + b'/src',
475                           commit_2_id, tree_2_id, b'src-2', exr.out)
476             verify_only_refs(heads=(b'obj',), tags=[])
477             
478     wvstart(get_disposition + ' --ff, implicit destinations')
479     for item in (b'src', b'src/latest'):
480         exr = run_get(get_disposition, b'--ff', item)
481         wvpasseq(0, exr.rc)
482
483         ex((b'find', b'get-dest/refs'))
484         ex((bup_cmd, b'-d', b'get-dest', b'ls'))
485
486         validate_save(b'src/latest', getcwd() + b'/src',
487                      commit_2_id, tree_2_id, b'src-2', exr.out)
488         #verify_only_refs(heads=('src',), tags=[])
489
490     wvstart(get_disposition + ' --ff, ff impossible')
491     for given, get_item in (((b'unrelated-branch', b'src'), b'src'),
492                             ((b'.tag/commit-2', b'src'), (b'.tag/commit-1', b'src'))):
493         exr = run_get(get_disposition, b'--ff', get_item, given=given)
494         wvpassne(0, exr.rc)
495         verify_rx(br'destination is not an ancestor of source', exr.err)
496
497 def test_append(get_disposition, src_info):
498     tinyfile_path = src_info['tinyfile-path']
499     subtree_vfs_path = src_info['subtree-vfs-path']
500
501     wvstart(get_disposition + ' --append to root fails')
502     for item in (b'.tag/tinyfile', b'src/latest' + tinyfile_path):
503         exr = run_get(get_disposition, b'--append', (item, b'/'))
504         wvpassne(0, exr.rc)
505         verify_rx(br'source for .+ must be a branch, save, commit, or tree',
506                   exr.err)
507     for item in (b'.tag/subtree', b'src/latest' + subtree_vfs_path,
508                  b'.tag/commit-1', b'src/latest', b'src'):
509         exr = run_get(get_disposition, b'--append', (item, b'/'))
510         wvpassne(0, exr.rc)
511         verify_rx(br'destination for .+ is a root, not a branch', exr.err)
512
513     wvstart(get_disposition + ' --append of not-treeish fails')
514     for src in (b'.tag/tinyfile', b'src/latest' + tinyfile_path):
515         for given, item in ((None, (src, b'obj')),
516                             (None, (src, b'.tag/obj')),
517                             ((b'.tag/tinyfile', b'.tag/obj'), (src, b'.tag/obj')),
518                             ((b'.tag/tree-1', b'.tag/obj'), (src, b'.tag/obj')),
519                             ((b'.tag/commit-1', b'.tag/obj'), (src, b'.tag/obj')),
520                             ((b'.tag/commit-1', b'obj'), (src, b'obj'))):
521             exr = run_get(get_disposition, b'--append', item, given=given)
522             wvpassne(0, exr.rc)
523             verify_rx(br'must be a branch, save, commit, or tree', exr.err)
524
525     wvstart(get_disposition + ' --append committish failure cases')
526     save_2 = src_info['save-2']
527     for src in (b'.tag/subtree', b'src/latest' + subtree_vfs_path,
528                 b'.tag/commit-2', b'src/' + save_2, b'src'):
529         for given, item, complaint in \
530             ((None, (src, b'.tag/obj'),
531               br'destination .+ must be a valid branch name'),
532              ((b'.tag/tinyfile', b'.tag/obj'), (src, b'.tag/obj'),
533               br'destination .+ is a blob, not a branch'),
534              ((b'.tag/tree-1', b'.tag/obj'), (src, b'.tag/obj'),
535               br'destination .+ is a tree, not a branch'),
536              ((b'.tag/commit-1', b'.tag/obj'), (src, b'.tag/obj'),
537               br'destination .+ is a tagged commit, not a branch'),
538              ((b'.tag/commit-2', b'.tag/obj'), (src, b'.tag/obj'),
539               br'destination .+ is a tagged commit, not a branch')):
540             exr = run_get(get_disposition, b'--append', item, given=given)
541             wvpassne(0, exr.rc)
542             verify_rx(complaint, exr.err)
543
544     wvstart(get_disposition + ' --append committish')
545     commit_2_id = src_info['commit-2-id']
546     tree_2_id = src_info['tree-2-id']
547     for item in (b'.tag/commit-2', b'src/' + save_2, b'src'):
548         for existing in (None, (b'.tag/commit-1', b'obj'),
549                          (b'.tag/commit-2', b'obj'),
550                          (b'unrelated-branch', b'obj')):
551             exr = run_get(get_disposition, b'--append', (item, b'obj'),
552                           given=existing)
553             wvpasseq(0, exr.rc)
554             validate_new_save(b'obj/latest', getcwd() + b'/src',
555                               commit_2_id, tree_2_id, b'src-2', exr.out)
556             verify_only_refs(heads=(b'obj',), tags=[])
557     # Append ancestor
558     save_1 = src_info['save-1']
559     commit_1_id = src_info['commit-1-id']
560     tree_1_id = src_info['tree-1-id']
561     for item in (b'.tag/commit-1',  b'src/' + save_1, b'src-1'):
562         exr = run_get(get_disposition, b'--append', (item, b'obj'),
563                       given=(b'.tag/commit-2', b'obj'))
564         wvpasseq(0, exr.rc)
565         validate_new_save(b'obj/latest', getcwd() + b'/src',
566                           commit_1_id, tree_1_id, b'src-1', exr.out)
567         verify_only_refs(heads=(b'obj',), tags=[])
568
569     wvstart(get_disposition + ' --append tree')
570     subtree_path = src_info['subtree-path']
571     subtree_id = src_info['subtree-id']
572     for item in (b'.tag/subtree', b'src/latest' + subtree_vfs_path):
573         for existing in (None,
574                          (b'.tag/commit-1', b'obj'),
575                          (b'.tag/commit-2', b'obj')):
576             exr = run_get(get_disposition, b'--append', (item, b'obj'),
577                           given=existing)
578             wvpasseq(0, exr.rc)
579             validate_new_save(b'obj/latest', b'/', None, subtree_id, subtree_path,
580                               exr.out)
581             verify_only_refs(heads=(b'obj',), tags=[])
582
583     wvstart(get_disposition + ' --append, implicit destinations')
584
585     for item in (b'src', b'src/latest'):
586         exr = run_get(get_disposition, b'--append', item)
587         wvpasseq(0, exr.rc)
588         validate_new_save(b'src/latest', getcwd() + b'/src', commit_2_id, tree_2_id,
589                           b'src-2', exr.out)
590         verify_only_refs(heads=(b'src',), tags=[])
591
592 def test_pick(get_disposition, src_info, force=False):
593     flavor = b'--force-pick' if force else b'--pick'
594     flavormsg = flavor.decode('ascii')
595     tinyfile_path = src_info['tinyfile-path']
596     subtree_vfs_path = src_info['subtree-vfs-path']
597     
598     wvstart(get_disposition + ' ' + flavormsg + ' to root fails')
599     for item in (b'.tag/tinyfile', b'src/latest' + tinyfile_path, b'src'):
600         exr = run_get(get_disposition, flavor, (item, b'/'))
601         wvpassne(0, exr.rc)
602         verify_rx(br'can only pick a commit or save', exr.err)
603     for item in (b'.tag/commit-1', b'src/latest'):
604         exr = run_get(get_disposition, flavor, (item, b'/'))
605         wvpassne(0, exr.rc)
606         verify_rx(br'destination is not a tag or branch', exr.err)
607     for item in (b'.tag/subtree', b'src/latest' + subtree_vfs_path):
608         exr = run_get(get_disposition, flavor, (item, b'/'))
609         wvpassne(0, exr.rc)
610         verify_rx(br'is impossible; can only --append a tree', exr.err)
611
612     wvstart(get_disposition + ' ' + flavormsg + ' of blob or branch fails')
613     for item in (b'.tag/tinyfile', b'src/latest' + tinyfile_path, b'src'):
614         for given, get_item in ((None, (item, b'obj')),
615                                 (None, (item, b'.tag/obj')),
616                                 ((b'.tag/tinyfile', b'.tag/obj'), (item, b'.tag/obj')),
617                                 ((b'.tag/tree-1', b'.tag/obj'), (item, b'.tag/obj')),
618                                 ((b'.tag/commit-1', b'.tag/obj'), (item, b'.tag/obj')),
619                                 ((b'.tag/commit-1', b'obj'), (item, b'obj'))):
620             exr = run_get(get_disposition, flavor, get_item, given=given)
621             wvpassne(0, exr.rc)
622             verify_rx(br'impossible; can only pick a commit or save', exr.err)
623
624     wvstart(get_disposition + ' ' + flavormsg + ' of tree fails')
625     for item in (b'.tag/subtree', b'src/latest' + subtree_vfs_path):
626         for given, get_item in ((None, (item, b'obj')),
627                                 (None, (item, b'.tag/obj')),
628                                 ((b'.tag/tinyfile', b'.tag/obj'), (item, b'.tag/obj')),
629                                 ((b'.tag/tree-1', b'.tag/obj'), (item, b'.tag/obj')),
630                                 ((b'.tag/commit-1', b'.tag/obj'), (item, b'.tag/obj')),
631                                 ((b'.tag/commit-1', b'obj'), (item, b'obj'))):
632             exr = run_get(get_disposition, flavor, get_item, given=given)
633             wvpassne(0, exr.rc)
634             verify_rx(br'impossible; can only --append a tree', exr.err)
635
636     save_2 = src_info['save-2']
637     commit_2_id = src_info['commit-2-id']
638     tree_2_id = src_info['tree-2-id']
639     # FIXME: these two wvstart texts?
640     if force:
641         wvstart(get_disposition + ' ' + flavormsg + ' commit/save to existing tag')
642         for item in (b'.tag/commit-2', b'src/' + save_2):
643             for given in ((b'.tag/tinyfile', b'.tag/obj'),
644                           (b'.tag/tree-1', b'.tag/obj'),
645                           (b'.tag/commit-1', b'.tag/obj')):
646                 exr = run_get(get_disposition, flavor, (item, b'.tag/obj'),
647                               given=given)
648                 wvpasseq(0, exr.rc)
649                 validate_new_tagged_commit(b'obj', commit_2_id, tree_2_id,
650                                            exr.out)
651                 verify_only_refs(heads=[], tags=(b'obj',))
652     else: # --pick
653         wvstart(get_disposition + ' ' + flavormsg
654                 + ' commit/save to existing tag fails')
655         for item in (b'.tag/commit-2', b'src/' + save_2):
656             for given in ((b'.tag/tinyfile', b'.tag/obj'),
657                           (b'.tag/tree-1', b'.tag/obj'),
658                           (b'.tag/commit-1', b'.tag/obj')):
659                 exr = run_get(get_disposition, flavor, (item, b'.tag/obj'), given=given)
660                 wvpassne(0, exr.rc)
661                 verify_rx(br'cannot overwrite existing tag', exr.err)
662             
663     wvstart(get_disposition + ' ' + flavormsg + ' commit/save to tag')
664     for item in (b'.tag/commit-2', b'src/' + save_2):
665         exr = run_get(get_disposition, flavor, (item, b'.tag/obj'))
666         wvpasseq(0, exr.rc)
667         validate_clean_repo()
668         validate_new_tagged_commit(b'obj', commit_2_id, tree_2_id, exr.out)
669         verify_only_refs(heads=[], tags=(b'obj',))
670          
671     wvstart(get_disposition + ' ' + flavormsg + ' commit/save to branch')
672     for item in (b'.tag/commit-2', b'src/' + save_2):
673         for given in (None, (b'.tag/commit-1', b'obj'), (b'.tag/commit-2', b'obj')):
674             exr = run_get(get_disposition, flavor, (item, b'obj'), given=given)
675             wvpasseq(0, exr.rc)
676             validate_clean_repo()
677             validate_new_save(b'obj/latest', getcwd() + b'/src',
678                               commit_2_id, tree_2_id, b'src-2', exr.out)
679             verify_only_refs(heads=(b'obj',), tags=[])
680
681     wvstart(get_disposition + ' ' + flavormsg
682             + ' commit/save unrelated commit to branch')
683     for item in(b'.tag/commit-2', b'src/' + save_2):
684         exr = run_get(get_disposition, flavor, (item, b'obj'),
685                       given=(b'unrelated-branch', b'obj'))
686         wvpasseq(0, exr.rc)
687         validate_clean_repo()
688         validate_new_save(b'obj/latest', getcwd() + b'/src',
689                           commit_2_id, tree_2_id, b'src-2', exr.out)
690         verify_only_refs(heads=(b'obj',), tags=[])
691
692     wvstart(get_disposition + ' ' + flavormsg + ' commit/save ancestor to branch')
693     save_1 = src_info['save-1']
694     commit_1_id = src_info['commit-1-id']
695     tree_1_id = src_info['tree-1-id']
696     for item in (b'.tag/commit-1', b'src/' + save_1):
697         exr = run_get(get_disposition, flavor, (item, b'obj'),
698                       given=(b'.tag/commit-2', b'obj'))
699         wvpasseq(0, exr.rc)
700         validate_clean_repo()
701         validate_new_save(b'obj/latest', getcwd() + b'/src',
702                           commit_1_id, tree_1_id, b'src-1', exr.out)
703         verify_only_refs(heads=(b'obj',), tags=[])
704
705
706     wvstart(get_disposition + ' ' + flavormsg + ', implicit destinations')
707     exr = run_get(get_disposition, flavor, b'.tag/commit-2')
708     wvpasseq(0, exr.rc)
709     validate_clean_repo()
710     validate_new_tagged_commit(b'commit-2', commit_2_id, tree_2_id, exr.out)
711     verify_only_refs(heads=[], tags=(b'commit-2',))
712
713     exr = run_get(get_disposition, flavor, b'src/latest')
714     wvpasseq(0, exr.rc)
715     validate_clean_repo()
716     validate_new_save(b'src/latest', getcwd() + b'/src',
717                       commit_2_id, tree_2_id, b'src-2', exr.out)
718     verify_only_refs(heads=(b'src',), tags=[])
719
720 def test_new_tag(get_disposition, src_info):
721     tinyfile_id = src_info['tinyfile-id']
722     tinyfile_path = src_info['tinyfile-path']
723     commit_2_id = src_info['commit-2-id']
724     tree_2_id = src_info['tree-2-id']
725     subtree_id = src_info['subtree-id']
726     subtree_vfs_path = src_info['subtree-vfs-path']
727
728     wvstart(get_disposition + ' --new-tag to root fails')
729     for item in (b'.tag/tinyfile',
730                  b'src/latest' + tinyfile_path,
731                  b'.tag/subtree',
732                  b'src/latest' + subtree_vfs_path,
733                  b'.tag/commit-1',
734                  b'src/latest',
735                  b'src'):
736         exr = run_get(get_disposition, b'--new-tag', (item, b'/'))
737         wvpassne(0, exr.rc)
738         verify_rx(br'destination for .+ must be a VFS tag', exr.err)
739
740     # Anything to new tag.
741     wvstart(get_disposition + ' --new-tag, blob tag')
742     for item in (b'.tag/tinyfile', b'src/latest' + tinyfile_path):
743         exr = run_get(get_disposition, b'--new-tag', (item, b'.tag/obj'))
744         wvpasseq(0, exr.rc)        
745         validate_blob(tinyfile_id, tinyfile_id)
746         verify_only_refs(heads=[], tags=(b'obj',))
747
748     wvstart(get_disposition + ' --new-tag, tree tag')
749     for item in (b'.tag/subtree', b'src/latest' + subtree_vfs_path):
750         exr = run_get(get_disposition, b'--new-tag', (item, b'.tag/obj'))
751         wvpasseq(0, exr.rc)        
752         validate_tree(subtree_id, subtree_id)
753         verify_only_refs(heads=[], tags=(b'obj',))
754         
755     wvstart(get_disposition + ' --new-tag, committish tag')
756     for item in (b'.tag/commit-2', b'src/latest', b'src'):
757         exr = run_get(get_disposition, b'--new-tag', (item, b'.tag/obj'))
758         wvpasseq(0, exr.rc)        
759         validate_tagged_save(b'obj', getcwd() + b'/src/', commit_2_id, tree_2_id,
760                              b'src-2', exr.out)
761         verify_only_refs(heads=[], tags=(b'obj',))
762
763     # Anything to existing tag (fails).
764     for ex_type, ex_tag in (('blob', (b'.tag/tinyfile', b'.tag/obj')),
765                             ('tree', (b'.tag/tree-1', b'.tag/obj')),
766                             ('commit', (b'.tag/commit-1', b'.tag/obj'))):
767         for item_type, item in (('blob tag', b'.tag/tinyfile'),
768                                 ('blob path', b'src/latest' + tinyfile_path),
769                                 ('tree tag', b'.tag/subtree'),
770                                 ('tree path', b'src/latest' + subtree_vfs_path),
771                                 ('commit tag', b'.tag/commit-2'),
772                                 ('save', b'src/latest'),
773                                 ('branch', b'src')):
774             wvstart(get_disposition + ' --new-tag of ' + item_type
775                     + ', given existing ' + ex_type + ' tag, fails')
776             exr = run_get(get_disposition, b'--new-tag', (item, b'.tag/obj'),
777                           given=ex_tag)
778             wvpassne(0, exr.rc)
779             verify_rx(br'cannot overwrite existing tag .* \(requires --replace\)',
780                       exr.err)
781
782     # Anything to branch (fails).
783     for ex_type, ex_tag in (('nothing', None),
784                             ('blob', (b'.tag/tinyfile', b'.tag/obj')),
785                             ('tree', (b'.tag/tree-1', b'.tag/obj')),
786                             ('commit', (b'.tag/commit-1', b'.tag/obj'))):
787         for item_type, item in (('blob tag', b'.tag/tinyfile'),
788                 ('blob path', b'src/latest' + tinyfile_path),
789                 ('tree tag', b'.tag/subtree'),
790                 ('tree path', b'src/latest' + subtree_vfs_path),
791                 ('commit tag', b'.tag/commit-2'),
792                 ('save', b'src/latest'),
793                 ('branch', b'src')):
794             wvstart(get_disposition + ' --new-tag to branch of ' + item_type
795                     + ', given existing ' + ex_type + ' tag, fails')
796             exr = run_get(get_disposition, b'--new-tag', (item, b'obj'),
797                           given=ex_tag)
798             wvpassne(0, exr.rc)
799             verify_rx(br'destination for .+ must be a VFS tag', exr.err)
800
801     wvstart(get_disposition + ' --new-tag, implicit destinations')
802     exr = run_get(get_disposition, b'--new-tag', b'.tag/commit-2')
803     wvpasseq(0, exr.rc)        
804     validate_tagged_save(b'commit-2', getcwd() + b'/src/', commit_2_id, tree_2_id,
805                          b'src-2', exr.out)
806     verify_only_refs(heads=[], tags=(b'commit-2',))
807
808 def test_unnamed(get_disposition, src_info):
809     tinyfile_id = src_info['tinyfile-id']
810     tinyfile_path = src_info['tinyfile-path']
811     subtree_vfs_path = src_info['subtree-vfs-path']
812     wvstart(get_disposition + ' --unnamed to root fails')
813     for item in (b'.tag/tinyfile',
814                  b'src/latest' + tinyfile_path,
815                  b'.tag/subtree',
816                  b'src/latest' + subtree_vfs_path,
817                  b'.tag/commit-1',
818                  b'src/latest',
819                  b'src'):
820         for ex_ref in (None, (item, b'.tag/obj')):
821             exr = run_get(get_disposition, b'--unnamed', (item, b'/'),
822                           given=ex_ref)
823             wvpassne(0, exr.rc)
824             verify_rx(br'usage: bup get ', exr.err)
825
826     wvstart(get_disposition + ' --unnamed file')
827     for item in (b'.tag/tinyfile', b'src/latest' + tinyfile_path):
828         exr = run_get(get_disposition, b'--unnamed', item)
829         wvpasseq(0, exr.rc)        
830         validate_blob(tinyfile_id, tinyfile_id)
831         verify_only_refs(heads=[], tags=[])
832
833         exr = run_get(get_disposition, b'--unnamed', item,
834                       given=(item, b'.tag/obj'))
835         wvpasseq(0, exr.rc)        
836         validate_blob(tinyfile_id, tinyfile_id)
837         verify_only_refs(heads=[], tags=(b'obj',))
838
839     wvstart(get_disposition + ' --unnamed tree')
840     subtree_id = src_info['subtree-id']
841     for item in (b'.tag/subtree', b'src/latest' + subtree_vfs_path):
842         exr = run_get(get_disposition, b'--unnamed', item)
843         wvpasseq(0, exr.rc)        
844         validate_tree(subtree_id, subtree_id)
845         verify_only_refs(heads=[], tags=[])
846         
847         exr = run_get(get_disposition, b'--unnamed', item,
848                       given=(item, b'.tag/obj'))
849         wvpasseq(0, exr.rc)        
850         validate_tree(subtree_id, subtree_id)
851         verify_only_refs(heads=[], tags=(b'obj',))
852         
853     wvstart(get_disposition + ' --unnamed committish')
854     save_2 = src_info['save-2']
855     commit_2_id = src_info['commit-2-id']
856     for item in (b'.tag/commit-2', b'src/' + save_2, b'src'):
857         exr = run_get(get_disposition, b'--unnamed', item)
858         wvpasseq(0, exr.rc)        
859         validate_commit(commit_2_id, commit_2_id)
860         verify_only_refs(heads=[], tags=[])
861
862         exr = run_get(get_disposition, b'--unnamed', item,
863                       given=(item, b'.tag/obj'))
864         wvpasseq(0, exr.rc)        
865         validate_commit(commit_2_id, commit_2_id)
866         verify_only_refs(heads=[], tags=(b'obj',))
867
868 def create_get_src():
869     global bup_cmd, src_info
870     wvstart('preparing')
871     ex((bup_cmd, b'-d', b'get-src', b'init'))
872
873     mkdir(b'src')
874     open(b'src/unrelated', 'a').close()
875     ex((bup_cmd, b'-d', b'get-src', b'index', b'src'))
876     ex((bup_cmd, b'-d', b'get-src', b'save', b'-tcn', b'unrelated-branch', b'src'))
877
878     ex((bup_cmd, b'-d', b'get-src', b'index', b'--clear'))
879     rmrf(b'src')
880     mkdir(b'src')
881     open(b'src/zero', 'a').close()
882     ex((bup_cmd, b'-d', b'get-src', b'index', b'src'))
883     exr = exo((bup_cmd, b'-d', b'get-src', b'save', b'-tcn', b'src', b'src'))
884     out = exr.out.splitlines()
885     tree_0_id = out[0]
886     commit_0_id = out[-1]
887     exr = exo((bup_cmd, b'-d', b'get-src', b'ls', b'src'))
888     save_0 = exr.out.splitlines()[0]
889     ex((b'git', b'--git-dir', b'get-src', b'branch', b'src-0', b'src'))
890     ex((b'cp', b'-RPp', b'src', b'src-0'))
891     
892     rmrf(b'src')
893     mkdir(b'src')
894     mkdir(b'src/x')
895     mkdir(b'src/x/y')
896     ex((bup_cmd + b' -d get-src random 1k > src/1'), shell=True)
897     ex((bup_cmd + b' -d get-src random 1k > src/x/2'), shell=True)
898     ex((bup_cmd, b'-d', b'get-src', b'index', b'src'))
899     exr = exo((bup_cmd, b'-d', b'get-src', b'save', b'-tcn', b'src', b'src'))
900     out = exr.out.splitlines()
901     tree_1_id = out[0]
902     commit_1_id = out[-1]
903     exr = exo((bup_cmd, b'-d', b'get-src', b'ls', b'src'))
904     save_1 = exr.out.splitlines()[1]
905     ex((b'git', b'--git-dir', b'get-src', b'branch', b'src-1', b'src'))
906     ex((b'cp', b'-RPp', b'src', b'src-1'))
907     
908     # Make a copy the current state of src so we'll have an ancestor.
909     ex((b'cp', b'-RPp',
910          b'get-src/refs/heads/src', b'get-src/refs/heads/src-ancestor'))
911
912     with open(b'src/tiny-file', 'ab') as f: f.write(b'xyzzy')
913     ex((bup_cmd, b'-d', b'get-src', b'index', b'src'))
914     ex((bup_cmd, b'-d', b'get-src', b'tick'))  # Ensure the save names differ
915     exr = exo((bup_cmd, b'-d', b'get-src', b'save', b'-tcn', b'src', b'src'))
916     out = exr.out.splitlines()
917     tree_2_id = out[0]
918     commit_2_id = out[-1]
919     exr = exo((bup_cmd, b'-d', b'get-src', b'ls', b'src'))
920     save_2 = exr.out.splitlines()[2]
921     rename(b'src', b'src-2')
922
923     src_root = getcwd() + b'/src'
924
925     subtree_path = b'src-2/x'
926     subtree_vfs_path = src_root + b'/x'
927
928     # No support for "ls -d", so grep...
929     exr = exo((bup_cmd, b'-d', b'get-src', b'ls', b'-s', b'src/latest' + src_root))
930     out = exr.out.splitlines()
931     subtree_id = None
932     for line in out:
933         if b'x' in line:
934             subtree_id = line.split()[0]
935     assert(subtree_id)
936
937     # With a tiny file, we'll get a single blob, not a chunked tree
938     tinyfile_path = src_root + b'/tiny-file'
939     exr = exo((bup_cmd, b'-d', b'get-src', b'ls', b'-s', b'src/latest' + tinyfile_path))
940     tinyfile_id = exr.out.splitlines()[0].split()[0]
941
942     ex((bup_cmd, b'-d', b'get-src', b'tag', b'tinyfile', tinyfile_id))
943     ex((bup_cmd, b'-d', b'get-src', b'tag', b'subtree', subtree_id))
944     ex((bup_cmd, b'-d', b'get-src', b'tag', b'tree-0', tree_0_id))
945     ex((bup_cmd, b'-d', b'get-src', b'tag', b'tree-1', tree_1_id))
946     ex((bup_cmd, b'-d', b'get-src', b'tag', b'tree-2', tree_2_id))
947     ex((bup_cmd, b'-d', b'get-src', b'tag', b'commit-0', commit_0_id))
948     ex((bup_cmd, b'-d', b'get-src', b'tag', b'commit-1', commit_1_id))
949     ex((bup_cmd, b'-d', b'get-src', b'tag', b'commit-2', commit_2_id))
950     ex((b'git', b'--git-dir', b'get-src', b'branch', b'commit-1', commit_1_id))
951     ex((b'git', b'--git-dir', b'get-src', b'branch', b'commit-2', commit_2_id))
952
953     return {'tinyfile-path' : tinyfile_path,
954             'tinyfile-id' : tinyfile_id,
955             'subtree-id' : subtree_id,
956             'tree-0-id' : tree_0_id,
957             'tree-1-id' : tree_1_id,
958             'tree-2-id' : tree_2_id,
959             'commit-0-id' : commit_0_id,
960             'commit-1-id' : commit_1_id,
961             'commit-2-id' : commit_2_id,
962             'save-1' : save_1,
963             'save-2' : save_2,
964             'subtree-path' : subtree_path,
965             'subtree-vfs-path' : subtree_vfs_path}
966     
967 # FIXME: this fails in a strange way:
968 #   WVPASS given nothing get --ff not-there
969
970 dispositions_to_test = ('get',)
971
972 if int(environ.get(b'BUP_TEST_LEVEL', b'0')) >= 11:
973     dispositions_to_test += ('get-on', 'get-to')
974
975 if len(compat.argv) == 1:
976     categories = ('replace', 'universal', 'ff', 'append', 'pick', 'new-tag',
977              'unnamed')
978 else:
979     categories = compat.argv[1:]
980     
981 with test_tempdir(b'get-') as tmpdir:
982     chdir(tmpdir)
983     try:
984         src_info = create_get_src()
985         for category in categories:
986             for disposition in dispositions_to_test:
987                 # given=FOO depends on --replace, so test it early
988                 if category == 'replace':
989                     test_replace(disposition, src_info)
990                 elif category == 'universal':
991                     test_universal_behaviors(disposition)
992                 elif category == 'ff':
993                     test_ff(disposition, src_info)
994                 elif category == 'append':
995                     test_append(disposition, src_info)
996                 elif category == 'pick':
997                     test_pick(disposition, src_info, force=False)
998                     test_pick(disposition, src_info, force=True)
999                 elif category == 'new-tag':
1000                     test_new_tag(disposition, src_info)
1001                 elif category == 'unnamed':
1002                     test_unnamed(disposition, src_info)
1003                 else:
1004                     raise Exception('unrecognized get test category')
1005     except Exception as ex:
1006         chdir(top)
1007         raise
1008     chdir(top)
1009
1010 wvmsg('checked %d cases' % get_cases_tested)