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