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