commit 30f6c0c64b5c08d79e22cc2c0bd1f1340288a7f5 from: Stefan Sperling via: Thomas Adam date: Fri Oct 08 12:21:56 2021 UTC add support for multiple path arguments to 'got diff' positive feedback from Omar and kn ok kn commit - 2539ccf28b5b0e6be13e9a7f2063a104efa7217b commit + 30f6c0c64b5c08d79e22cc2c0bd1f1340288a7f5 blob - 607d9e411094bc0ccc199c39ccb264406f3ef169 blob + bdded67d33703dcd1607e51d0887179126d00a83 --- got/got.1 +++ got/got.1 @@ -839,13 +839,13 @@ This option has no effect if the specified is never traversed. .El .Tg di -.It Cm diff Oo Fl a Oc Oo Fl C Ar number Oc Oo Fl r Ar repository-path Oc Oo Fl s Oc Oo Fl w Oc Op Ar object1 Ar object2 | Ar path +.It Cm diff Oo Fl a Oc Oo Fl C Ar number Oc Oo Fl r Ar repository-path Oc Oo Fl s Oc Oo Fl P Oc Oo Fl w Oc Op Ar object1 Ar object2 | Ar path ... .Dl (alias: Cm di ) -When invoked within a work tree with less than two arguments, display +When invoked within a work tree without any arguments, display all local changes in the work tree. -If a +If one or more .Ar path -is specified, only show changes within this path. +arguments are specified, only show changes within the specified paths. .Pp If two arguments are provided, treat each argument as a reference, a tag name, or an object ID SHA1 hash, and display differences between the @@ -853,6 +853,12 @@ corresponding objects. Both objects must be of the same type (blobs, trees, or commits). An abbreviated hash argument will be expanded to a full SHA1 hash automatically, provided the abbreviation is unique. +If none of these interpretations produce a valid result or if the +.Fl P +option is used, +and if +.Cm got diff +is running in a work tree, attempt to interpret the two arguments as paths. .Pp The options for .Cm got diff @@ -874,6 +880,13 @@ work tree, use the repository path associated with thi Show changes staged with .Cm got stage instead of showing local changes in the work tree. +This option is only valid when +.Cm got diff +is invoked in a work tree. +.It Fl P +Interpret all arguments as paths only. +This option can be used to resolve ambiguity in cases where paths +look like tag names, reference names, or object IDs. This option is only valid when .Cm got diff is invoked in a work tree. blob - 369d7d7369f7a373f6eef08f611035ae2b846fe5 blob + ad7b886219f18cc55190a65296d733c9de7a8214 --- got/got.c +++ got/got.c @@ -4277,7 +4277,7 @@ __dead static void usage_diff(void) { fprintf(stderr, "usage: %s diff [-a] [-C number] [-r repository-path] " - "[-s] [-w] [object1 object2 | path]\n", getprogname()); + "[-s] [-w] [-P] [object1 object2 | path ...]\n", getprogname()); exit(1); } @@ -4486,17 +4486,18 @@ cmd_diff(int argc, char *argv[]) struct got_repository *repo = NULL; struct got_worktree *worktree = NULL; char *cwd = NULL, *repo_path = NULL; - struct got_object_id *id1 = NULL, *id2 = NULL; - const char *id_str1 = NULL, *id_str2 = NULL; - char *label1 = NULL, *label2 = NULL; + struct got_object_id *ids[2] = { NULL, NULL }; + char *labels[2] = { NULL, NULL }; int type1, type2; - int diff_context = 3, diff_staged = 0, ignore_whitespace = 0, ch; - int force_text_diff = 0; + int diff_context = 3, diff_staged = 0, ignore_whitespace = 0, ch, i; + int force_text_diff = 0, force_path = 0, rflag = 0; const char *errstr; - char *path = NULL; struct got_reflist_head refs; + struct got_pathlist_head paths; + struct got_pathlist_entry *pe; TAILQ_INIT(&refs); + TAILQ_INIT(&paths); #ifndef PROFILE if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil", @@ -4504,7 +4505,7 @@ cmd_diff(int argc, char *argv[]) err(1, "pledge"); #endif - while ((ch = getopt(argc, argv, "aC:r:sw")) != -1) { + while ((ch = getopt(argc, argv, "aC:r:swP")) != -1) { switch (ch) { case 'a': force_text_diff = 1; @@ -4521,6 +4522,7 @@ cmd_diff(int argc, char *argv[]) return got_error_from_errno2("realpath", optarg); got_path_strip_trailing_slashes(repo_path); + rflag = 1; break; case 's': diff_staged = 1; @@ -4528,6 +4530,9 @@ cmd_diff(int argc, char *argv[]) case 'w': ignore_whitespace = 1; break; + case 'P': + force_path = 1; + break; default: usage_diff(); /* NOTREACHED */ @@ -4542,54 +4547,48 @@ cmd_diff(int argc, char *argv[]) error = got_error_from_errno("getcwd"); goto done; } - if (argc <= 1) { - if (repo_path) - errx(1, - "-r option can't be used when diffing a work tree"); + + if (repo_path == NULL) { error = got_worktree_open(&worktree, cwd); - if (error) { - if (error->code == GOT_ERR_NOT_WORKTREE) - error = wrap_not_worktree_error(error, "diff", - cwd); + if (error && error->code != GOT_ERR_NOT_WORKTREE) goto done; - } - repo_path = strdup(got_worktree_get_repo_path(worktree)); - if (repo_path == NULL) { - error = got_error_from_errno("strdup"); - goto done; - } - if (argc == 1) { - error = got_worktree_resolve_path(&path, worktree, - argv[0]); - if (error) + else + error = NULL; + if (worktree) { + repo_path = + strdup(got_worktree_get_repo_path(worktree)); + if (repo_path == NULL) { + error = got_error_from_errno("strdup"); goto done; + } } else { - path = strdup(""); - if (path == NULL) { + repo_path = strdup(cwd); + if (repo_path == NULL) { error = got_error_from_errno("strdup"); goto done; } } - } else if (argc == 2) { - if (diff_staged) - errx(1, "-s option can't be used when diffing " - "objects in repository"); - id_str1 = argv[0]; - id_str2 = argv[1]; + } + + if (worktree) { + repo_path = strdup(got_worktree_get_repo_path(worktree)); if (repo_path == NULL) { - error = got_worktree_open(&worktree, cwd); - if (error && error->code != GOT_ERR_NOT_WORKTREE) - goto done; - repo_path = strdup(worktree ? - got_worktree_get_repo_path(worktree) : cwd); + error = got_error_from_errno("strdup"); + goto done; + } + } else { + if (repo_path == NULL) { + repo_path = strdup(cwd); if (repo_path == NULL) { error = got_error_from_errno("strdup"); goto done; } } - } else - usage_diff(); + } + if (force_path && (rflag || worktree == NULL)) + errx(1, "-P option can only be used when diffing a work tree"); + error = got_repo_open(&repo, repo_path, NULL); free(repo_path); if (error != NULL) @@ -4600,13 +4599,35 @@ cmd_diff(int argc, char *argv[]) if (error) goto done; - if (argc <= 1) { + if (!force_path && argc == 2) { + error = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name, + NULL); + if (error) + goto done; + for (i = 0; i < argc; i++) { + error = got_repo_match_object_id(&ids[i], &labels[i], + argv[i], GOT_OBJ_TYPE_ANY, &refs, repo); + if (error) { + if (error->code != GOT_ERR_NOT_REF && + error->code != GOT_ERR_NO_OBJ) + goto done; + error = NULL; + break; + } + } + } + + if (worktree != NULL && (ids[0] == NULL || ids[1] == NULL)) { + error = get_worktree_paths_from_argv(&paths, + argc, argv, worktree); + if (error) + goto done; + } + + if (!TAILQ_EMPTY(&paths)) { struct print_diff_arg arg; - struct got_pathlist_head paths; char *id_str; - TAILQ_INIT(&paths); - error = got_object_id_str(&id_str, got_worktree_get_base_commit_id(worktree)); if (error) @@ -4620,39 +4641,34 @@ cmd_diff(int argc, char *argv[]) arg.ignore_whitespace = ignore_whitespace; arg.force_text_diff = force_text_diff; - error = got_pathlist_append(&paths, path, NULL); - if (error) - goto done; - error = got_worktree_status(worktree, &paths, repo, 0, print_diff, &arg, check_cancelled, NULL); free(id_str); - got_pathlist_free(&paths); goto done; } - error = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name, NULL); - if (error) - return error; - - error = got_repo_match_object_id(&id1, &label1, id_str1, - GOT_OBJ_TYPE_ANY, &refs, repo); - if (error) - goto done; + if (ids[0] == NULL || ids[1] == NULL) { + if (argc == 2) { + error = got_error_fmt(GOT_ERR_NO_OBJ, "%s", + ids[0] ? argv[1] : argv[0]); + goto done; + } if (worktree == NULL) { + error = got_error(GOT_ERR_NOT_WORKTREE); + goto done; + } else + usage_diff(); + } + if (diff_staged) + errx(1, "-s option can't be used when diffing " + "objects in repository"); - error = got_repo_match_object_id(&id2, &label2, id_str2, - GOT_OBJ_TYPE_ANY, &refs, repo); + error = got_object_get_type(&type1, repo, ids[0]); if (error) goto done; - error = got_object_get_type(&type1, repo, id1); + error = got_object_get_type(&type2, repo, ids[1]); if (error) goto done; - - error = got_object_get_type(&type2, repo, id2); - if (error) - goto done; - if (type1 != type2) { error = got_error(GOT_ERR_OBJ_TYPE); goto done; @@ -4660,18 +4676,18 @@ cmd_diff(int argc, char *argv[]) switch (type1) { case GOT_OBJ_TYPE_BLOB: - error = got_diff_objects_as_blobs(NULL, NULL, id1, id2, + error = got_diff_objects_as_blobs(NULL, NULL, ids[0], ids[1], NULL, NULL, diff_context, ignore_whitespace, force_text_diff, repo, stdout); break; case GOT_OBJ_TYPE_TREE: - error = got_diff_objects_as_trees(NULL, NULL, id1, id2, + error = got_diff_objects_as_trees(NULL, NULL, ids[0], ids[1], "", "", diff_context, ignore_whitespace, force_text_diff, repo, stdout); break; case GOT_OBJ_TYPE_COMMIT: - printf("diff %s %s\n", label1, label2); - error = got_diff_objects_as_commits(NULL, NULL, id1, id2, + printf("diff %s %s\n", labels[0], labels[1]); + error = got_diff_objects_as_commits(NULL, NULL, ids[0], ids[1], diff_context, ignore_whitespace, force_text_diff, repo, stdout); break; @@ -4679,11 +4695,10 @@ cmd_diff(int argc, char *argv[]) error = got_error(GOT_ERR_OBJ_TYPE); } done: - free(label1); - free(label2); - free(id1); - free(id2); - free(path); + free(labels[0]); + free(labels[1]); + free(ids[0]); + free(ids[1]); if (worktree) got_worktree_close(worktree); if (repo) { @@ -4691,6 +4706,9 @@ done: if (error == NULL) error = close_err; } + TAILQ_FOREACH(pe, &paths, entry) + free((char *)pe->path); + got_pathlist_free(&paths); got_ref_list_free(&refs); return error; } blob - 9987502cc2a56a00f9c9a0960fb32f8ba17770df blob + 7bc83f4d3575b51f65002d99ae9beedadfea7495 --- regress/cmdline/diff.sh +++ regress/cmdline/diff.sh @@ -66,6 +66,29 @@ test_diff_basic() { return 1 fi + # 'got diff' in a repository without any arguments is an error + (cd $testroot/repo && got diff 2> $testroot/stderr) + echo "got: no got work tree found" > $testroot/stderr.expected + cmp -s $testroot/stderr.expected $testroot/stderr + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stderr.expected $testroot/stderr + test_done "$testroot" "$ret" + return 1 + fi + + # 'got diff' in a repository with two arguments requires that + # both named objects exist + (cd $testroot/repo && got diff $head_rev foo 2> $testroot/stderr) + echo "got: foo: object not found" > $testroot/stderr.expected + cmp -s $testroot/stderr.expected $testroot/stderr + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stderr.expected $testroot/stderr + test_done "$testroot" "$ret" + return 1 + fi + # diff non-existent path (cd $testroot/wt && got diff nonexistent > $testroot/stdout \ 2> $testroot/stderr) @@ -80,11 +103,304 @@ test_diff_basic() { fi echo "got: nonexistent: No such file or directory" \ + > $testroot/stderr.expected + cmp -s $testroot/stderr.expected $testroot/stderr + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stderr.expected $testroot/stderr + test_done "$testroot" "$ret" + return 1 + fi + + echo "modified zeta" > $testroot/wt/epsilon/zeta + + # diff several paths in a work tree + echo "diff $head_rev $testroot/wt" > $testroot/stdout.expected + echo 'blob - /dev/null' >> $testroot/stdout.expected + echo 'file + new' >> $testroot/stdout.expected + echo '--- /dev/null' >> $testroot/stdout.expected + echo '+++ new' >> $testroot/stdout.expected + echo '@@ -0,0 +1 @@' >> $testroot/stdout.expected + echo '+new file' >> $testroot/stdout.expected + echo -n 'blob - ' >> $testroot/stdout.expected + got tree -r $testroot/repo -i | grep 'alpha$' | cut -d' ' -f 1 \ + >> $testroot/stdout.expected + echo 'file + alpha' >> $testroot/stdout.expected + echo '--- alpha' >> $testroot/stdout.expected + echo '+++ alpha' >> $testroot/stdout.expected + echo '@@ -1 +1 @@' >> $testroot/stdout.expected + echo '-alpha' >> $testroot/stdout.expected + echo '+modified alpha' >> $testroot/stdout.expected + echo -n 'blob - ' >> $testroot/stdout.expected + got tree -r $testroot/repo -i epsilon | grep 'zeta$' | cut -d' ' -f 1 \ + >> $testroot/stdout.expected + echo 'file + epsilon/zeta' >> $testroot/stdout.expected + echo '--- epsilon/zeta' >> $testroot/stdout.expected + echo '+++ epsilon/zeta' >> $testroot/stdout.expected + echo '@@ -1 +1 @@' >> $testroot/stdout.expected + echo '-zeta' >> $testroot/stdout.expected + echo '+modified zeta' >> $testroot/stdout.expected + echo -n 'blob - ' >> $testroot/stdout.expected + got tree -r $testroot/repo -i | grep 'beta$' | cut -d' ' -f 1 \ + >> $testroot/stdout.expected + echo 'file + /dev/null' >> $testroot/stdout.expected + echo '--- beta' >> $testroot/stdout.expected + echo '+++ /dev/null' >> $testroot/stdout.expected + echo '@@ -1 +0,0 @@' >> $testroot/stdout.expected + echo '-beta' >> $testroot/stdout.expected + + (cd $testroot/wt && got diff new alpha epsilon beta > $testroot/stdout) + cmp -s $testroot/stdout.expected $testroot/stdout + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stdout.expected $testroot/stdout + test_done "$testroot" "$ret" + return 1 + fi + + # a branch 'new' should not collide with path 'new' if more + # than two arguments are passed + got br -r $testroot/repo -c master new > /dev/null + (cd $testroot/wt && got diff new alpha epsilon beta \ + > $testroot/stdout 2> $testroot/stderr) + ret="$?" + if [ "$ret" != "0" ]; then + echo "diff failed unexpectedly" >&2 + test_done "$testroot" "1" + return 1 + fi + cmp -s $testroot/stdout.expected $testroot/stdout + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stdout.expected $testroot/stdout + test_done "$testroot" "$ret" + return 1 + fi + + # different order of arguments results in different output order + echo "diff $head_rev $testroot/wt" > $testroot/stdout.expected + echo -n 'blob - ' >> $testroot/stdout.expected + got tree -r $testroot/repo -i | grep 'alpha$' | cut -d' ' -f 1 \ + >> $testroot/stdout.expected + echo 'file + alpha' >> $testroot/stdout.expected + echo '--- alpha' >> $testroot/stdout.expected + echo '+++ alpha' >> $testroot/stdout.expected + echo '@@ -1 +1 @@' >> $testroot/stdout.expected + echo '-alpha' >> $testroot/stdout.expected + echo '+modified alpha' >> $testroot/stdout.expected + echo 'blob - /dev/null' >> $testroot/stdout.expected + echo 'file + new' >> $testroot/stdout.expected + echo '--- /dev/null' >> $testroot/stdout.expected + echo '+++ new' >> $testroot/stdout.expected + echo '@@ -0,0 +1 @@' >> $testroot/stdout.expected + echo '+new file' >> $testroot/stdout.expected + echo -n 'blob - ' >> $testroot/stdout.expected + got tree -r $testroot/repo -i epsilon | grep 'zeta$' | cut -d' ' -f 1 \ + >> $testroot/stdout.expected + echo 'file + epsilon/zeta' >> $testroot/stdout.expected + echo '--- epsilon/zeta' >> $testroot/stdout.expected + echo '+++ epsilon/zeta' >> $testroot/stdout.expected + echo '@@ -1 +1 @@' >> $testroot/stdout.expected + echo '-zeta' >> $testroot/stdout.expected + echo '+modified zeta' >> $testroot/stdout.expected + echo -n 'blob - ' >> $testroot/stdout.expected + got tree -r $testroot/repo -i | grep 'beta$' | cut -d' ' -f 1 \ + >> $testroot/stdout.expected + echo 'file + /dev/null' >> $testroot/stdout.expected + echo '--- beta' >> $testroot/stdout.expected + echo '+++ /dev/null' >> $testroot/stdout.expected + echo '@@ -1 +0,0 @@' >> $testroot/stdout.expected + echo '-beta' >> $testroot/stdout.expected + (cd $testroot/wt && got diff alpha new epsilon beta \ + > $testroot/stdout 2> $testroot/stderr) + ret="$?" + if [ "$ret" != "0" ]; then + echo "diff failed unexpectedly" >&2 + test_done "$testroot" "1" + return 1 + fi + cmp -s $testroot/stdout.expected $testroot/stdout + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stdout.expected $testroot/stdout + test_done "$testroot" "$ret" + return 1 + fi + + # Two arguments are interpreted as objects if a colliding path exists + echo master > $testroot/wt/master + (cd $testroot/wt && got add master > /dev/null) + (cd $testroot/wt && got diff master new > $testroot/stdout) + ret="$?" + if [ "$ret" != "0" ]; then + echo "diff failed unexpectedly" >&2 + test_done "$testroot" "1" + return 1 + fi + echo "diff refs/heads/master refs/heads/new" > $testroot/stdout.expected + # diff between the branches is empty + cmp -s $testroot/stdout.expected $testroot/stdout + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stdout.expected $testroot/stdout + test_done "$testroot" "$ret" + return 1 + fi + # same without a work tree + (cd $testroot/repo && got diff master new > $testroot/stdout) + ret="$?" + if [ "$ret" != "0" ]; then + echo "diff failed unexpectedly" >&2 + test_done "$testroot" "1" + return 1 + fi + echo "diff refs/heads/master refs/heads/new" > $testroot/stdout.expected + cmp -s $testroot/stdout.expected $testroot/stdout + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stdout.expected $testroot/stdout + test_done "$testroot" "$ret" + return 1 + fi + # same with -r argument + got diff -r $testroot/repo master new > $testroot/stdout + ret="$?" + if [ "$ret" != "0" ]; then + echo "diff failed unexpectedly" >&2 + test_done "$testroot" "1" + return 1 + fi + echo "diff refs/heads/master refs/heads/new" > $testroot/stdout.expected + cmp -s $testroot/stdout.expected $testroot/stdout + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stdout.expected $testroot/stdout + test_done "$testroot" "$ret" + return 1 + fi + + # -P can be used to force use of paths + (cd $testroot/wt && got diff -P new master > $testroot/stdout) + ret="$?" + if [ "$ret" != "0" ]; then + echo "diff failed unexpectedly" >&2 + test_done "$testroot" "1" + return 1 + fi + echo "diff $head_rev $testroot/wt" > $testroot/stdout.expected + echo 'blob - /dev/null' >> $testroot/stdout.expected + echo 'file + new' >> $testroot/stdout.expected + echo '--- /dev/null' >> $testroot/stdout.expected + echo '+++ new' >> $testroot/stdout.expected + echo '@@ -0,0 +1 @@' >> $testroot/stdout.expected + echo '+new file' >> $testroot/stdout.expected + echo 'blob - /dev/null' >> $testroot/stdout.expected + echo 'file + master' >> $testroot/stdout.expected + echo '--- /dev/null' >> $testroot/stdout.expected + echo '+++ master' >> $testroot/stdout.expected + echo '@@ -0,0 +1 @@' >> $testroot/stdout.expected + echo '+master' >> $testroot/stdout.expected + cmp -s $testroot/stdout.expected $testroot/stdout + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stdout.expected $testroot/stdout + test_done "$testroot" "$ret" + return 1 + fi + + # -P can only be used in a work tree + got diff -r $testroot/repo -P new master 2> $testroot/stderr + ret="$?" + if [ "$ret" == "0" ]; then + echo "diff succeeded unexpectedly" >&2 + test_done "$testroot" "1" + return 1 + fi + echo "got: -P option can only be used when diffing a work tree" \ > $testroot/stderr.expected cmp -s $testroot/stderr.expected $testroot/stderr ret="$?" if [ "$ret" != "0" ]; then diff -u $testroot/stderr.expected $testroot/stderr + test_done "$testroot" "$ret" + return 1 + fi + + # a single argument which can be resolved to a path is not ambiguous + echo "diff $head_rev $testroot/wt" > $testroot/stdout.expected + echo 'blob - /dev/null' >> $testroot/stdout.expected + echo 'file + new' >> $testroot/stdout.expected + echo '--- /dev/null' >> $testroot/stdout.expected + echo '+++ new' >> $testroot/stdout.expected + echo '@@ -0,0 +1 @@' >> $testroot/stdout.expected + echo '+new file' >> $testroot/stdout.expected + (cd $testroot/wt && got diff new > $testroot/stdout) + ret="$?" + if [ "$ret" != "0" ]; then + echo "diff failed unexpectedly" >&2 + test_done "$testroot" "1" + return 1 + fi + cmp -s $testroot/stdout.expected $testroot/stdout + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stdout.expected $testroot/stdout + test_done "$testroot" "$ret" + return 1 + fi + + # diff with just one object ID argument results in + # interpretation of argument as a path + (cd $testroot/wt && got diff $head_rev 2> $testroot/stderr) + ret="$?" + if [ "$ret" = "0" ]; then + echo "diff succeeded unexpectedly" >&2 + test_done "$testroot" "1" + return 1 + fi + echo "got: $head_rev: No such file or directory" \ + > $testroot/stderr.expected + cmp -s $testroot/stderr.expected $testroot/stderr + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stderr.expected $testroot/stderr + test_done "$testroot" "$ret" + return 1 + fi + + # diff with more than two object arguments results in + # interpretation of arguments as paths + (cd $testroot/wt && got diff new $head_rev master \ + > $testroot/stout 2> $testroot/stderr) + ret="$?" + if [ "$ret" = "0" ]; then + echo "diff succeeded unexpectedly" >&2 + test_done "$testroot" "1" + return 1 + fi + + echo "diff $head_rev $testroot/wt" > $testroot/stdout.expected + echo 'blob - /dev/null' >> $testroot/stdout.expected + echo 'file + new' >> $testroot/stdout.expected + echo '--- /dev/null' >> $testroot/stdout.expected + echo '+++ new' >> $testroot/stdout.expected + echo '@@ -0,0 +1 @@' >> $testroot/stdout.expected + echo '+new file' >> $testroot/stdout.expected + cmp -s $testroot/stdout.expected $testroot/stdout + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stdout.expected $testroot/stdout + test_done "$testroot" "$ret" + return 1 + fi + + echo "got: $head_rev: No such file or directory" \ + > $testroot/stderr.expected + cmp -s $testroot/stderr.expected $testroot/stderr + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stderr.expected $testroot/stderr + return 1 fi test_done "$testroot" "$ret" }