commit - e31abbf21f99a7312bdfd392f33ace285feadfe5
commit + b2070a3f25a75399baa9a402542a60326197a053
blob - 59cc0bc74ca2f04ba3df6a2dc55756f8e29edde5
blob + 99bf3e8ae0503062b3ca312d1f3880d3861a5bf7
--- got/got.1
+++ got/got.1
.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
}
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;
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) {
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) {
if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
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)
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
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;
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;
linelen--;
}
- err = parse_ref_line(ref, name, line);
+ err = parse_ref_line(ref, absname, line);
if (lock) {
if (err)
got_lockfile_unlock(lf);
}
}
- 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);
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;
}
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");
{
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;
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);
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;
}
}
}
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
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