commit b2070a3f25a75399baa9a402542a60326197a053 from: Stefan Sperling date: Sun Mar 22 14:21:07 2020 UTC allow for limiting output of 'got ref -l' to a single ref or a namespace commit - e31abbf21f99a7312bdfd392f33ace285feadfe5 commit + b2070a3f25a75399baa9a402542a60326197a053 blob - 59cc0bc74ca2f04ba3df6a2dc55756f8e29edde5 blob + 99bf3e8ae0503062b3ca312d1f3880d3861a5bf7 --- got/got.1 +++ got/got.1 @@ -843,7 +843,15 @@ If this directory is a .Nm work tree, use the repository path associated with this work tree. .It Fl l -List all existing references in the repository. +List references in the repository. +If no +.Ar name +is specified, list all existing references in the repository. +If +.Ar name +is a reference namespace, list all references in this namespace. +Otherwise, show only the reference with the given +.Ar name . Cannot be used together with any other options except .Fl r . .It Fl c Ar object blob - b27ba046f77b825e22827ab64329341daa4b767b blob + a10c0e212fc2d7195e321950605e7a96f3f8c95a --- got/got.c +++ got/got.c @@ -4182,14 +4182,14 @@ usage_ref(void) } static const struct got_error * -list_refs(struct got_repository *repo) +list_refs(struct got_repository *repo, const char *refname) { static const struct got_error *err = NULL; struct got_reflist_head refs; struct got_reflist_entry *re; SIMPLEQ_INIT(&refs); - err = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name, NULL); + err = got_ref_list(&refs, repo, refname, got_ref_cmp_by_name, NULL); if (err) return err; @@ -4304,7 +4304,8 @@ cmd_ref(int argc, char *argv[]) struct got_worktree *worktree = NULL; char *cwd = NULL, *repo_path = NULL; int ch, do_list = 0, do_delete = 0; - const char *refname = NULL, *obj_arg = NULL, *symref_target= NULL; + const char *obj_arg = NULL, *symref_target= NULL; + char *refname = NULL; while ((ch = getopt(argc, argv, "c:dr:ls:")) != -1) { switch (ch) { @@ -4350,13 +4351,27 @@ cmd_ref(int argc, char *argv[]) argv += optind; if (do_list) { - if (argc > 0) + if (argc != 0 && argc != 1) usage_ref(); + if (argc == 1) { + refname = strdup(argv[0]); + if (refname == NULL) { + error = got_error_from_errno("strdup"); + goto done; + } + } } else { if (argc != 1) usage_ref(); - refname = argv[0]; + refname = strdup(argv[0]); + if (refname == NULL) { + error = got_error_from_errno("strdup"); + goto done; + } } + + if (refname) + got_path_strip_trailing_slashes(refname); #ifndef PROFILE if (do_list) { @@ -4407,7 +4422,7 @@ cmd_ref(int argc, char *argv[]) goto done; if (do_list) - error = list_refs(repo); + error = list_refs(repo, refname); else if (do_delete) error = delete_ref(repo, refname); else if (symref_target) @@ -4418,6 +4433,7 @@ cmd_ref(int argc, char *argv[]) error = add_ref(repo, refname, obj_arg); } done: + free(refname); if (repo) got_repo_close(repo); if (worktree) blob - 86409d0ebedc1d35c6333f13f4f8267d21581671 blob + f74cc9ecacc8b745cdb5c73db7f2c60cd9e918ef --- lib/reference.c +++ lib/reference.c @@ -162,7 +162,7 @@ parse_ref_line(struct got_reference **ref, const char static const struct got_error * parse_ref_file(struct got_reference **ref, const char *name, - const char *abspath, int lock) + const char *absname, const char *abspath, int lock) { const struct got_error *err = NULL; FILE *f; @@ -175,24 +175,34 @@ parse_ref_file(struct got_reference **ref, const char err = got_lockfile_lock(&lf, abspath); if (err) { if (err->code == GOT_ERR_ERRNO && errno == ENOENT) - err = got_error(GOT_ERR_NOT_REF); + err = got_error_not_ref(name); return err; } } f = fopen(abspath, "rb"); if (f == NULL) { + if (errno != ENOTDIR && errno != ENOENT) + err = got_error_from_errno2("fopen", abspath); + else + err = got_error_not_ref(name); if (lock) got_lockfile_unlock(lf); - return NULL; + return err; } linelen = getline(&line, &linesize, f); if (linelen == -1) { if (feof(f)) err = NULL; /* ignore empty files (could be locks) */ - else - err = got_error_from_errno2("getline", abspath); + else { + if (errno == EISDIR) + err = got_error(GOT_ERR_NOT_REF); + else if (ferror(f)) + err = got_ferror(f, GOT_ERR_IO); + else + err = got_error_from_errno2("getline", abspath); + } if (lock) got_lockfile_unlock(lf); goto done; @@ -202,7 +212,7 @@ parse_ref_file(struct got_reference **ref, const char linelen--; } - err = parse_ref_line(ref, name, line); + err = parse_ref_line(ref, absname, line); if (lock) { if (err) got_lockfile_unlock(lf); @@ -411,7 +421,7 @@ open_ref(struct got_reference **ref, const char *path_ } } - err = parse_ref_file(ref, absname, path, lock); + err = parse_ref_file(ref, name, absname, path, lock); done: if (!ref_is_absolute && !ref_is_well_known) free(absname); @@ -449,7 +459,7 @@ got_ref_open(struct got_reference **ref, struct got_re for (i = 0; i < nitems(subdirs); i++) { err = open_ref(ref, path_refs, subdirs[i], refname, lock); - if (err || *ref) + if ((err && err->code != GOT_ERR_NOT_REF) || *ref) goto done; } @@ -801,6 +811,9 @@ gather_on_disk_refs(struct got_reflist_head *refs, con const struct got_error *err = NULL; DIR *d = NULL; char *path_subdir; + + while (subdir[0] == '/') + subdir++; if (asprintf(&path_subdir, "%s/%s", path_refs, subdir) == -1) return got_error_from_errno("asprintf"); @@ -865,7 +878,8 @@ got_ref_list(struct got_reflist_head *refs, struct got { const struct got_error *err; char *packed_refs_path, *path_refs = NULL; - const char *ondisk_ref_namespace = NULL; + char *abs_namespace = NULL; + char *buf = NULL, *ondisk_ref_namespace = NULL; FILE *f = NULL; struct got_reference *ref; struct got_reflist_entry *new; @@ -885,11 +899,52 @@ got_ref_list(struct got_reflist_head *refs, struct got got_ref_close(ref); if (err && err->code != GOT_ERR_NOT_REF) goto done; + } else { + /* Try listing a single reference. */ + const char *refname = ref_namespace; + path_refs = get_refs_dir_path(repo, refname); + if (path_refs == NULL) { + err = got_error_from_errno("get_refs_dir_path"); + goto done; + } + err = open_ref(&ref, path_refs, "", refname, 0); + if (err) { + if (err->code != GOT_ERR_NOT_REF) + goto done; + /* Try to look up references in a given namespace. */ + } else { + err = insert_ref(&new, refs, ref, repo, + cmp_cb, cmp_arg); + if (err || new == NULL /* duplicate */) + got_ref_close(ref); + return err; + } } - ondisk_ref_namespace = ref_namespace; - if (ref_namespace && strncmp(ref_namespace, "refs/", 5) == 0) - ondisk_ref_namespace += 5; + if (ref_namespace) { + size_t len; + /* Canonicalize the path to eliminate double-slashes if any. */ + if (asprintf(&abs_namespace, "/%s", ref_namespace) == -1) { + err = got_error_from_errno("asprintf"); + goto done; + } + len = strlen(abs_namespace) + 1; + buf = malloc(len); + if (buf == NULL) { + err = got_error_from_errno("malloc"); + goto done; + } + err = got_canonpath(abs_namespace, buf, len); + if (err) + goto done; + ondisk_ref_namespace = buf; + while (ondisk_ref_namespace[0] == '/') + ondisk_ref_namespace++; + if (strncmp(ondisk_ref_namespace, "refs/", 5) == 0) + ondisk_ref_namespace += 5; + else if (strcmp(ondisk_ref_namespace, "refs") == 0) + ondisk_ref_namespace = ""; + } /* Gather on-disk refs before parsing packed-refs. */ free(path_refs); @@ -936,8 +991,9 @@ got_ref_list(struct got_reflist_head *refs, struct got if (ref_namespace) { const char *name; name = got_ref_get_name(ref); - if (strncmp(name, ref_namespace, - strlen(ref_namespace)) != 0) { + if (!got_path_is_child(name, + ref_namespace, + strlen(ref_namespace))) { got_ref_close(ref); continue; } @@ -952,6 +1008,8 @@ got_ref_list(struct got_reflist_head *refs, struct got } } done: + free(abs_namespace); + free(buf); free(path_refs); if (f && fclose(f) != 0 && err == NULL) err = got_error_from_errno("fclose"); blob - 94736443ecd38ab37cc59e7464129a48092a6a72 blob + 8457642808883458905c4ddef16686f54b7b7fd2 --- regress/cmdline/ref.sh +++ regress/cmdline/ref.sh @@ -238,9 +238,139 @@ function test_ref_delete { ret="$?" if [ "$ret" != "0" ]; then diff -u $testroot/stderr.expected $testroot/stderr + fi + test_done "$testroot" "$ret" +} + +function test_ref_list { + local testroot=`test_init ref_list` + local commit_id=`git_show_head $testroot/repo` + + # Create a tag pointing at a commit ID + got tag -r $testroot/repo -c $commit_id -m "1.0" "1.0" >/dev/null + ret="$?" + if [ "$ret" != "0" ]; then + echo "got tag command failed unexpectedly" + test_done "$testroot" "$ret" + return 1 + fi + local tag_id=`got ref -r $testroot/repo -l \ + | grep "^refs/tags/$tag" | tr -d ' ' | cut -d: -f2` + + # Create a ref based on repository's HEAD reference + got ref -r $testroot/repo -c HEAD refs/foo/zoo + ret="$?" + if [ "$ret" != "0" ]; then + echo "got ref command failed unexpectedly" + test_done "$testroot" "$ret" + return 1 + fi + + # Create a head ref based on another specific ref + (cd $testroot/repo && got ref -c refs/heads/master refs/foo/bar/baz) + ret="$?" + if [ "$ret" != "0" ]; then + test_done "$testroot" "$ret" + return 1 + fi + + got ref -r $testroot/repo -l > $testroot/stdout + + echo "HEAD: refs/heads/master" > $testroot/stdout.expected + echo "refs/foo/bar/baz: $commit_id" >> $testroot/stdout.expected + echo "refs/foo/zoo: $commit_id" >> $testroot/stdout.expected + echo "refs/heads/master: $commit_id" >> $testroot/stdout.expected + echo "refs/tags/1.0: $tag_id" >> $testroot/stdout.expected + cmp -s $testroot/stdout $testroot/stdout.expected + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stdout.expected $testroot/stdout + test_done "$testroot" "$ret" + return 1 + fi + + got ref -r $testroot/repo -l refs > $testroot/stdout + + echo "refs/foo/bar/baz: $commit_id" > $testroot/stdout.expected + echo "refs/foo/zoo: $commit_id" >> $testroot/stdout.expected + echo "refs/heads/master: $commit_id" >> $testroot/stdout.expected + echo "refs/tags/1.0: $tag_id" >> $testroot/stdout.expected + cmp -s $testroot/stdout $testroot/stdout.expected + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stdout.expected $testroot/stdout + test_done "$testroot" "$ret" + return 1 + fi + + got ref -r $testroot/repo -l refs/tags > $testroot/stdout + + echo "refs/tags/1.0: $tag_id" > $testroot/stdout.expected + cmp -s $testroot/stdout $testroot/stdout.expected + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stdout.expected $testroot/stdout + test_done "$testroot" "$ret" + return 1 fi + + for r in refs/foo/bar/baz refs/foo/bar/baz foo/bar/baz foo/bar; do + got ref -r $testroot/repo -l $r > $testroot/stdout + + echo "refs/foo/bar/baz: $commit_id" > $testroot/stdout.expected + cmp -s $testroot/stdout $testroot/stdout.expected + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stdout.expected $testroot/stdout + test_done "$testroot" "$ret" + return 1 + fi + done + + for r in refs/foo foo; do + got ref -r $testroot/repo -l $r > $testroot/stdout + + echo "refs/foo/bar/baz: $commit_id" > $testroot/stdout.expected + echo "refs/foo/zoo: $commit_id" >> $testroot/stdout.expected + cmp -s $testroot/stdout $testroot/stdout.expected + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stdout.expected $testroot/stdout + test_done "$testroot" "$ret" + return 1 + fi + done + + for r in refs//foo/bar refs//foo//bar refs////////foo//bar; do + got ref -r $testroot/repo -l $r > $testroot/stdout + + echo "refs/foo/bar/baz: $commit_id" > $testroot/stdout.expected + cmp -s $testroot/stdout $testroot/stdout.expected + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stdout.expected $testroot/stdout + test_done "$testroot" "$ret" + return 1 + fi + done + + # attempt to list non-existing references + for r in refs/fo bar baz moo riffs /refs/abc refs/foo/bar/baz/moo; do + got ref -r $testroot/repo -l $r > $testroot/stdout + + echo -n > $testroot/stdout.expected + cmp -s $testroot/stdout $testroot/stdout.expected + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stdout.expected $testroot/stdout + test_done "$testroot" "$ret" + return 1 + fi + done + test_done "$testroot" "$ret" } run_test test_ref_create run_test test_ref_delete +run_test test_ref_list