commit - 44392932de0367a4f50fdeb8ecb541cda02a86f8
commit + 896e9b6f30150bb0dfff1345dc5935a4b65b6838
blob - 3697535d7f7ebd36d8a374d195ebe42d1c658497
blob + e4ba15352f9f9e0ff9b741ca38676a601434bcde
--- got/got.1
+++ got/got.1
.It Cm ug
Short alias for
.Cm unstage .
-.It Cm cat Oo Fl r Ar repository-path Oc Ar object ...
-Parse and print contents of specified objects to standard output
-in a line-based text format.
-Treat each argument as a reference, a tag name, or an object ID SHA1 hash.
+.It Cm cat Oo Fl c Ar commit Oc Oo Fl r Ar repository-path Oc Ar arg ...
+Parse and print contents of objects to standard output in a line-based
+text format.
+Content of commit, tree, and tag objects is printed in a way similar
+to the actual content stored in such objects.
+Blob object contents are printed as they would appear in files on disk.
+.Pp
+Attempt to interpret each argument as a reference, a tag name, or
+an object ID SHA1 hash.
References will be resolved to an object ID.
Tag names will resolved to a tag object.
An abbreviated hash argument will be expanded to a full SHA1 hash
automatically, provided the abbreviation is unique.
.Pp
+If none of the above interpretations produce a valid result, or if the
+.Fl P
+option is used, attempt to interpret the argument as a path which will
+be resolved to the ID of an object found at this path in the repository.
+.Pp
The options for
.Cm got cat
are as follows:
.Bl -tag -width Ds
+.It Fl c Ar commit
+Look up paths in the specified
+.Ar commit .
+If this option is not used, paths are looked up in the commit resolved
+via the repository's HEAD reference.
+The expected argument is a commit ID SHA1 hash or an existing reference
+or tag name which will be resolved to a commit ID.
+An abbreviated hash argument will be expanded to a full SHA1 hash
+automatically, provided the abbreviation is unique.
.It Fl r Ar repository-path
Use the repository at the specified path.
If not specified, assume the repository is located at or above the current
If this directory is a
.Nm
work tree, use the repository path associated with this 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.
.El
.El
.Sh ENVIRONMENT
blob - 178ad9f5321f4259fd91762efec539efe3a0683c
blob + 54e235accacaac5767caaa2b55bc299d21a1353e
--- got/got.c
+++ got/got.c
__dead static void
usage_cat(void)
{
- fprintf(stderr, "usage: %s cat [-r repository ] object1 "
- "[object2 ...]\n", getprogname());
+ fprintf(stderr, "usage: %s cat [-r repository ] [ -c commit ] [ -P ] "
+ "arg1 [arg2 ...]\n", getprogname());
exit(1);
}
struct got_repository *repo = NULL;
struct got_worktree *worktree = NULL;
char *cwd = NULL, *repo_path = NULL, *label = NULL;
- struct got_object_id *id = NULL;
- int ch, obj_type, i;
+ const char *commit_id_str = NULL;
+ struct got_object_id *id = NULL, *commit_id = NULL;
+ int ch, obj_type, i, force_path = 0;
#ifndef PROFILE
if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
err(1, "pledge");
#endif
- while ((ch = getopt(argc, argv, "r:")) != -1) {
+ while ((ch = getopt(argc, argv, "c:r:P")) != -1) {
switch (ch) {
+ case 'c':
+ commit_id_str = optarg;
+ break;
case 'r':
repo_path = realpath(optarg, NULL);
if (repo_path == NULL)
err(1, "-r option");
got_path_strip_trailing_slashes(repo_path);
break;
+ case 'P':
+ force_path = 1;
+ break;
default:
usage_cat();
/* NOTREACHED */
if (error)
goto done;
+ if (commit_id_str == NULL)
+ commit_id_str = GOT_REF_HEAD;
+ error = resolve_commit_arg(&commit_id, commit_id_str, repo);
+ if (error)
+ goto done;
+
for (i = 0; i < argc; i++) {
- error = match_object_id(&id, &label, argv[i],
- GOT_OBJ_TYPE_ANY, 0, repo);
- if (error)
- break;
+ if (force_path) {
+ error = got_object_id_by_path(&id, repo, commit_id,
+ argv[i]);
+ if (error)
+ break;
+ } else {
+ error = match_object_id(&id, &label, argv[i],
+ GOT_OBJ_TYPE_ANY, 0, repo);
+ if (error) {
+ if (error->code != GOT_ERR_BAD_OBJ_ID_STR &&
+ error->code != GOT_ERR_NOT_REF)
+ break;
+ error = got_object_id_by_path(&id, repo,
+ commit_id, argv[i]);
+ if (error)
+ break;
+ }
+ }
error = got_object_get_type(&obj_type, repo, id);
if (error)
done:
free(label);
free(id);
+ free(commit_id);
if (worktree)
got_worktree_close(worktree);
if (repo) {
blob - ff28334685c4012d71ab8d57303196127b851018
blob + e94f00225a6d92aed87516ccb71ed5c81a284b32
--- regress/cmdline/cat.sh
+++ regress/cmdline/cat.sh
echo >> $testroot/stdout.expected
echo "numparents 0" >> $testroot/stdout.expected
echo "author $GOT_AUTHOR $author_time +0000" >> $testroot/stdout.expected
- echo "committer Flan Hacker <flan_hacker@openbsd.org> $author_time +0000" >> $testroot/stdout.expected
+ echo "committer $GOT_AUTHOR $author_time +0000" \
+ >> $testroot/stdout.expected
echo "messagelen 22" >> $testroot/stdout.expected
printf "\nadding the test tree\n" >> $testroot/stdout.expected
}
+function test_cat_path {
+ local testroot=`test_init cat_path`
+ local commit_id=`git_show_head $testroot/repo`
+ local author_time=`git_show_author_time $testroot/repo`
+ local alpha_id=`got tree -r $testroot/repo -i | grep 'alpha$' | cut -d' ' -f 1`
+ local gamma_id=`got tree -r $testroot/repo -i | grep 'gamma/$' | cut -d' ' -f 1`
+ local delta_id=`got tree -r $testroot/repo -i gamma | grep 'delta$' | cut -d' ' -f 1`
+
+ # cat blob by path
+ echo "alpha" > $testroot/stdout.expected
+ got cat -r $testroot/repo alpha > $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
+
+ # cat tree by path
+ echo "$delta_id 0100644 delta" > $testroot/stdout.expected
+ got cat -r $testroot/repo gamma > $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
+
+ (cd $testroot && got checkout repo wt > /dev/null)
+ echo "modified alpha" > $testroot/wt/alpha
+ (cd $testroot/wt && got commit -m "changed alpha" > /dev/null)
+ local commit_id2=`git_show_head $testroot/repo`
+ local author_time2=`git_show_author_time $testroot/repo`
+ local tree_commit2=`git_show_tree $testroot/repo`
+
+ # cat blob by path in specific commit
+ echo "alpha" > $testroot/stdout.expected
+ got cat -r $testroot/repo -c $commit_id alpha > $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
+ echo "modified alpha" > $testroot/stdout.expected
+ got cat -r $testroot/repo -c $commit_id2 alpha > $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
+
+ # resolve ambiguities between paths and other arguments
+ echo "new file called master" > $testroot/wt/master
+ echo "new file called $commit_id2" > $testroot/wt/$commit_id2
+ (cd $testroot/wt && got add master $commit_id2 > /dev/null)
+ (cd $testroot/wt && got commit -m "added clashing paths" > /dev/null)
+ local commit_id3=`git_show_head $testroot/repo`
+ local author_time3=`git_show_author_time $testroot/repo`
+
+ # references and object IDs override paths:
+ echo -n "tree " > $testroot/stdout.expected
+ git_show_tree $testroot/repo >> $testroot/stdout.expected
+ echo >> $testroot/stdout.expected
+ echo "numparents 1" >> $testroot/stdout.expected
+ echo "parent $commit_id2" >> $testroot/stdout.expected
+ echo "author $GOT_AUTHOR $author_time3 +0000" >> $testroot/stdout.expected
+ echo "committer $GOT_AUTHOR $author_time3 +0000" \
+ >> $testroot/stdout.expected
+ echo "messagelen 22" >> $testroot/stdout.expected
+ printf "\nadded clashing paths\n" >> $testroot/stdout.expected
+
+ for arg in master $commit_id3; do
+ got cat -r $testroot/repo $arg > $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
+ done
+
+ echo "tree $tree_commit2" > $testroot/stdout.expected
+ echo "numparents 1" >> $testroot/stdout.expected
+ echo "parent $commit_id" >> $testroot/stdout.expected
+ echo "author $GOT_AUTHOR $author_time2 +0000" >> $testroot/stdout.expected
+ echo "committer $GOT_AUTHOR $author_time2 +0000" \
+ >> $testroot/stdout.expected
+ echo "messagelen 15" >> $testroot/stdout.expected
+ printf "\nchanged alpha\n" >> $testroot/stdout.expected
+
+ got cat -r $testroot/repo $commit_id2 > $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
+
+ # force resolution of path 'master'
+ echo "new file called master" > $testroot/stdout.expected
+ got cat -r $testroot/repo -P master > $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
+
+ # force resolution of path "$commit_id2"
+ echo "new file called $commit_id2" > $testroot/stdout.expected
+ got cat -r $testroot/repo -P $commit_id2 > $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
+ test_done "$testroot" "$ret"
+}
+
run_test test_cat_basic
+run_test test_cat_path