Commit Diff


commit - 1f03b8da6be221784414918f66390ccb0eb67908
commit + 41b0de1256a7a8a9ed3d4c0d66809ebfcbf1a58d
blob - 869f13d9c5e81da78233fd60e855d60bb5c769be
blob + 9b734106ce0c02a0c08f7dceeb7d5ac834988511
--- got/got.1
+++ got/got.1
@@ -136,7 +136,7 @@ follows the globbing rules documented in
 .It Cm im
 Short alias for
 .Cm import .
-.It Cm clone Oo Fl a Oc Oo Fl b Ar branch Oc Oo Fl q Oc Oo Fl v Oc Ar repository-URL Op Ar directory
+.It Cm clone Oo Fl a Oc Oo Fl b Ar branch Oc Oo Fl l Oc Oo Fl m Oc Oo Fl q Oc Oo Fl v Oc Ar repository-URL Op Ar directory
 Clone a Git repository at the specified
 .Ar repository-URL
 into the specified
@@ -222,6 +222,11 @@ repository's HEAD reference will be fetched.
 Cannot be used together with the
 .Fl a
 option.
+.It Fl l
+List branches and tags available for cloning from the remote repository
+and exit immediately.
+Cannot be used together with any of the other options except
+.Fl v .
 .It Fl m
 Create the cloned repository as a mirror of the original repository.
 This is useful if the cloned repository will not be used to store
@@ -267,7 +272,7 @@ The maximum is 3.
 .It Cm cl
 Short alias for
 .Cm clone .
-.It Cm fetch Oo Fl a Oc Oo Fl b Ar branch Oc Oo Fl r Ar repository-path Oc Oo Fl q Oc Oo Fl v Oc Op Ar remote-repository
+.It Cm fetch Oo Fl a Oc Oo Fl b Ar branch Oc Oo Fl l Oc Oo Fl r Ar repository-path Oc Oo Fl q Oc Oo Fl v Oc Op Ar remote-repository
 Fetch new changes from a remote repository.
 If no
 .Ar remote-repository
@@ -328,6 +333,13 @@ repository's HEAD reference will be fetched.
 Cannot be used together with the
 .Fl a
 option.
+.It Fl l
+List branches and tags available for fetching from the remote repository
+and exit immediately.
+Cannot be used together with any of the other options except
+.Fl v
+and
+.Fl r .
 .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
blob - d0e6d7f2e6b486b9103d7efb0dd24322773ab138
blob + a1aea8a8405c27c3618d0bb30e4faaf8d51d9c7a
--- got/got.c
+++ got/got.c
@@ -900,6 +900,35 @@ create_head_ref(struct got_reference *target_ref, stru
 }
 
 static const struct got_error *
+list_remote_refs(struct got_pathlist_head *symrefs,
+    struct got_pathlist_head *refs)
+{
+	const struct got_error *err;
+	struct got_pathlist_entry *pe;
+
+	TAILQ_FOREACH(pe, symrefs, entry) {
+		const char *refname = pe->path;
+		const char *targetref = pe->data;
+
+		printf("%s: %s\n", refname, targetref);
+	}
+
+	TAILQ_FOREACH(pe, refs, entry) {
+		const char *refname = pe->path;
+		struct got_object_id *id = pe->data;
+		char *id_str;
+
+		err = got_object_id_str(&id_str, id);
+		if (err)
+			return err;
+		printf("%s: %s\n", refname, id_str);
+		free(id_str);
+	}
+
+	return NULL;
+}
+
+static const struct got_error *
 cmd_clone(int argc, char *argv[])
 {
 	const struct got_error *error = NULL;
@@ -919,13 +948,14 @@ cmd_clone(int argc, char *argv[])
 	FILE *gitconfig_file = NULL;
 	ssize_t n;
 	int verbosity = 0, fetch_all_branches = 0, mirror_references = 0;
+	int list_refs_only = 0;
 	struct got_reference *head_symref = NULL;
 
 	TAILQ_INIT(&refs);
 	TAILQ_INIT(&symrefs);
 	TAILQ_INIT(&wanted_branches);
 
-	while ((ch = getopt(argc, argv, "ab:mvq")) != -1) {
+	while ((ch = getopt(argc, argv, "ab:lmvq")) != -1) {
 		switch (ch) {
 		case 'a':
 			fetch_all_branches = 1;
@@ -936,6 +966,9 @@ cmd_clone(int argc, char *argv[])
 			if (error)
 				return error;
 			break;
+		case 'l':
+			list_refs_only = 1;
+			break;
 		case 'm':
 			mirror_references = 1;
 			break;
@@ -957,7 +990,17 @@ cmd_clone(int argc, char *argv[])
 	argv += optind;
 
 	if (fetch_all_branches && !TAILQ_EMPTY(&wanted_branches))
-		errx(1, "-a and -b options are mutually exclusive\n");
+		errx(1, "-a and -b options are mutually exclusive");
+	if (list_refs_only) {
+		if (!TAILQ_EMPTY(&wanted_branches))
+			errx(1, "-l and -b options are mutually exclusive");
+		if (fetch_all_branches)
+			errx(1, "-l and -a options are mutually exclusive");
+		if (mirror_references)
+			errx(1, "-l and -m options are mutually exclusive");
+		if (verbosity == -1)
+			errx(1, "-l and -q options are mutually exclusive");
+	}
 
 	uri = argv[0];
 
@@ -1010,18 +1053,19 @@ cmd_clone(int argc, char *argv[])
 	} else
 		repo_path = dirname;
 
-	error = got_path_mkdir(repo_path);
-	if (error)
-		goto done;
+	if (!list_refs_only) {
+		error = got_path_mkdir(repo_path);
+		if (error)
+			goto done;
 
-	error = got_repo_init(repo_path);
-	if (error)
-		goto done;
+		error = got_repo_init(repo_path);
+		if (error)
+			goto done;
+		error = got_repo_open(&repo, repo_path, NULL);
+		if (error)
+			goto done;
+	}
 
-	error = got_repo_open(&repo, repo_path, NULL);
-	if (error)
-		goto done;
-
 	if (strcmp(proto, "git+ssh") == 0 || strcmp(proto, "ssh") == 0) {
 		if (unveil(GOT_FETCH_PATH_SSH, "x") != 0) {
 			error = got_error_from_errno2("unveil",
@@ -1029,7 +1073,7 @@ cmd_clone(int argc, char *argv[])
 			goto done;
 		}
 	}
-	error = apply_unveil(got_repo_get_path(repo), 0, NULL);
+	error = apply_unveil(repo ? got_repo_get_path(repo) : NULL, 0, NULL);
 	if (error)
 		goto done;
 
@@ -1048,11 +1092,16 @@ cmd_clone(int argc, char *argv[])
 	fpa.verbosity = verbosity;
 	error = got_fetch_pack(&pack_hash, &refs, &symrefs,
 	    GOT_FETCH_DEFAULT_REMOTE_NAME, mirror_references,
-	    fetch_all_branches, &wanted_branches, fetchfd,
-	    repo, fetch_progress, &fpa);
+	    fetch_all_branches, &wanted_branches, list_refs_only,
+	    fetchfd, repo, fetch_progress, &fpa);
 	if (error)
 		goto done;
 
+	if (list_refs_only) {
+		error = list_remote_refs(&symrefs, &refs);
+		goto done;
+	}
+
 	error = got_object_id_str(&id_str, pack_hash);
 	if (error)
 		goto done;
@@ -1340,13 +1389,13 @@ cmd_fetch(int argc, char *argv[])
 	struct got_object_id *pack_hash = NULL;
 	int i, ch, fetchfd = -1;
 	struct got_fetch_progress_arg fpa;
-	int verbosity = 0, fetch_all_branches = 0;
+	int verbosity = 0, fetch_all_branches = 0, list_refs_only = 0;
 
 	TAILQ_INIT(&refs);
 	TAILQ_INIT(&symrefs);
 	TAILQ_INIT(&wanted_branches);
 
-	while ((ch = getopt(argc, argv, "ab:r:vq")) != -1) {
+	while ((ch = getopt(argc, argv, "ab:lr:vq")) != -1) {
 		switch (ch) {
 		case 'a':
 			fetch_all_branches = 1;
@@ -1356,6 +1405,9 @@ cmd_fetch(int argc, char *argv[])
 			    optarg, NULL);
 			if (error)
 				return error;
+			break;
+		case 'l':
+			list_refs_only = 1;
 			break;
 		case 'r':
 			repo_path = realpath(optarg, NULL);
@@ -1382,7 +1434,15 @@ cmd_fetch(int argc, char *argv[])
 	argv += optind;
 
 	if (fetch_all_branches && !TAILQ_EMPTY(&wanted_branches))
-		errx(1, "-a and -b options are mutually exclusive\n");
+		errx(1, "-a and -b options are mutually exclusive");
+	if (list_refs_only) {
+		if (!TAILQ_EMPTY(&wanted_branches))
+			errx(1, "-l and -b options are mutually exclusive");
+		if (fetch_all_branches)
+			errx(1, "-l and -a options are mutually exclusive");
+		if (verbosity == -1)
+			errx(1, "-l and -q options are mutually exclusive");
+	}
 
 	if (argc == 0)
 		remote_name = GOT_FETCH_DEFAULT_REMOTE_NAME;
@@ -1487,10 +1547,15 @@ cmd_fetch(int argc, char *argv[])
 	fpa.verbosity = verbosity;
 	error = got_fetch_pack(&pack_hash, &refs, &symrefs, remote->name,
 	    remote->mirror_references, fetch_all_branches, &wanted_branches,
-	    fetchfd, repo, fetch_progress, &fpa);
+	    list_refs_only, fetchfd, repo, fetch_progress, &fpa);
 	if (error)
 		goto done;
 
+	if (list_refs_only) {
+		error = list_remote_refs(&symrefs, &refs);
+		goto done;
+	}
+
 	if (pack_hash == NULL) {
 		if (verbosity >= 0)
 			printf("Already up-to-date\n");
blob - a7b9590dcef74fceec8004075319634cc5f5e462
blob + 9e26b62a0b5d1a4848d98980cd9a7b5bc73fd1aa
--- include/got_fetch.h
+++ include/got_fetch.h
@@ -61,5 +61,5 @@ typedef const struct got_error *(*got_fetch_progress_c
  */
 const struct got_error *got_fetch_pack(struct got_object_id **,
 	struct got_pathlist_head *, struct got_pathlist_head *, const char *,
-	int, int, struct got_pathlist_head *, int, struct got_repository *,
+	int, int, struct got_pathlist_head *, int, int, struct got_repository *,
 	got_fetch_progress_cb, void *);
blob - d9e55b8853c1065604fa45460938e09c2444b97b
blob + fcd9bb07bd25aa7e1ed09ad450f14bebbaf623b4
--- lib/fetch.c
+++ lib/fetch.c
@@ -388,8 +388,8 @@ const struct got_error*
 got_fetch_pack(struct got_object_id **pack_hash, struct got_pathlist_head *refs,
     struct got_pathlist_head *symrefs, const char *remote_name,
     int mirror_references, int fetch_all_branches,
-    struct got_pathlist_head *wanted_branches, int fetchfd,
-    struct got_repository *repo,
+    struct got_pathlist_head *wanted_branches, int list_refs_only,
+    int fetchfd, struct got_repository *repo,
     got_fetch_progress_cb progress_cb, void *progress_arg)
 {
 	int imsg_fetchfds[2], imsg_idxfds[2];
@@ -401,7 +401,7 @@ got_fetch_pack(struct got_object_id **pack_hash, struc
 	pid_t fetchpid, idxpid;
 	char *tmppackpath = NULL, *tmpidxpath = NULL;
 	char *packpath = NULL, *idxpath = NULL, *id_str = NULL;
-	const char *repo_path = got_repo_get_path_git_dir(repo);
+	const char *repo_path = NULL;
 	struct got_pathlist_head have_refs;
 	struct got_pathlist_entry *pe;
 	struct got_reflist_head my_refs;
@@ -411,6 +411,9 @@ got_fetch_pack(struct got_object_id **pack_hash, struc
 	size_t ref_prefixlen = 0;
 	char *path;
 	char *progress = NULL;
+
+	if (!list_refs_only)
+		repo_path = got_repo_get_path_git_dir(repo);
 
 	*pack_hash = NULL;
 	for (i = 0; i < nitems(tmpfds); i++)
@@ -426,9 +429,12 @@ got_fetch_pack(struct got_object_id **pack_hash, struc
 		ref_prefixlen = strlen(ref_prefix);
 	}
 
-	err = got_ref_list(&my_refs, repo, NULL, got_ref_cmp_by_name, NULL);
-	if (err)
-		goto done;
+	if (!list_refs_only) {
+		err = got_ref_list(&my_refs, repo, NULL,
+		    got_ref_cmp_by_name, NULL);
+		if (err)
+			goto done;
+	}
 
 	SIMPLEQ_FOREACH(re, &my_refs, entry) {
 		struct got_object_id *id;
@@ -493,29 +499,45 @@ got_fetch_pack(struct got_object_id **pack_hash, struc
 		}
 	}
 
-	if (asprintf(&path, "%s/%s/fetching.pack",
-	    repo_path, GOT_OBJECTS_PACK_DIR) == -1) {
-		err = got_error_from_errno("asprintf");
-		goto done;
+	if (list_refs_only) {
+		packfd = got_opentempfd();
+		if (packfd == -1) {
+			err = got_error_from_errno("got_opentempfd");
+			goto done;
+		}
+	} else {
+		if (asprintf(&path, "%s/%s/fetching.pack",
+		    repo_path, GOT_OBJECTS_PACK_DIR) == -1) {
+			err = got_error_from_errno("asprintf");
+			goto done;
+		}
+		err = got_opentemp_named_fd(&tmppackpath, &packfd, path);
+		free(path);
+		if (err)
+			goto done;
 	}
-	err = got_opentemp_named_fd(&tmppackpath, &packfd, path);
-	free(path);
-	if (err)
-		goto done;
 	npackfd = dup(packfd);
 	if (npackfd == -1) {
 		err = got_error_from_errno("dup");
 		goto done;
 	}
-	if (asprintf(&path, "%s/%s/fetching.idx",
-	    repo_path, GOT_OBJECTS_PACK_DIR) == -1) {
-		err = got_error_from_errno("asprintf");
-		goto done;
+	if (list_refs_only) {
+		idxfd = got_opentempfd();
+		if (idxfd == -1) {
+			err = got_error_from_errno("got_opentempfd");
+			goto done;
+		}
+	} else {
+		if (asprintf(&path, "%s/%s/fetching.idx",
+		    repo_path, GOT_OBJECTS_PACK_DIR) == -1) {
+			err = got_error_from_errno("asprintf");
+			goto done;
+		}
+		err = got_opentemp_named_fd(&tmpidxpath, &idxfd, path);
+		free(path);
+		if (err)
+			goto done;
 	}
-	err = got_opentemp_named_fd(&tmpidxpath, &idxfd, path);
-	free(path);
-	if (err)
-		goto done;
 	nidxfd = dup(idxfd);
 	if (nidxfd == -1) {
 		err = got_error_from_errno("dup");
@@ -555,7 +577,7 @@ got_fetch_pack(struct got_object_id **pack_hash, struc
 		goto done;
 	}
 	err = got_privsep_send_fetch_req(&fetchibuf, nfetchfd, &have_refs,
-	    fetch_all_branches, wanted_branches);
+	    fetch_all_branches, wanted_branches, list_refs_only);
 	if (err != NULL)
 		goto done;
 	nfetchfd = -1;
@@ -591,7 +613,7 @@ got_fetch_pack(struct got_object_id **pack_hash, struc
 			else
 				free(id);
 		} else if (refname && id) {
-			err = got_pathlist_append(refs, refname, id);
+			err = got_pathlist_insert(NULL, refs, refname, id);
 			if (err)
 				goto done;
 		} else if (server_progress) {
blob - ca92bcd3660a9b973c8c555f1cbb36a1c0fede5b
blob + b542cec86ba90e8bd23eabe37271fc2faf08ce72
--- lib/got_lib_privsep.h
+++ lib/got_lib_privsep.h
@@ -257,6 +257,7 @@ struct got_imsg_fetch_wanted_branch {
 /* Structure for GOT_IMSG_FETCH_REQUEST data. */
 struct got_imsg_fetch_request {
 	int fetch_all_branches;
+	int list_refs_only;
 	size_t n_have_refs;
 	size_t n_wanted_branches;
 	/* Followed by n_have_refs GOT_IMSG_FETCH_HAVE_REF messages. */
@@ -401,7 +402,7 @@ const struct got_error *got_privsep_send_index_pack_do
 const struct got_error *got_privsep_recv_index_progress(int *, int *, int *,
     int *, int *, struct imsgbuf *ibuf);
 const struct got_error *got_privsep_send_fetch_req(struct imsgbuf *, int,
-    struct got_pathlist_head *, int, struct got_pathlist_head *);
+    struct got_pathlist_head *, int, struct got_pathlist_head *, int);
 const struct got_error *got_privsep_send_fetch_outfd(struct imsgbuf *, int);
 const struct got_error *got_privsep_send_fetch_symrefs(struct imsgbuf *,
     struct got_pathlist_head *);
blob - 805dd3e8bcf919be36494e99aa5351e30d121d66
blob + 2baa585778c31396c55ca867abadd1baae6a09a8
--- lib/privsep.c
+++ lib/privsep.c
@@ -413,7 +413,7 @@ got_privsep_send_obj(struct imsgbuf *ibuf, struct got_
 const struct got_error *
 got_privsep_send_fetch_req(struct imsgbuf *ibuf, int fd,
    struct got_pathlist_head *have_refs, int fetch_all_branches,
-   struct got_pathlist_head *wanted_branches)
+   struct got_pathlist_head *wanted_branches, int list_refs_only)
 {
 	const struct got_error *err = NULL;
 	struct ibuf *wbuf;
@@ -423,6 +423,7 @@ got_privsep_send_fetch_req(struct imsgbuf *ibuf, int f
 
 	memset(&fetchreq, 0, sizeof(fetchreq));
 	fetchreq.fetch_all_branches = fetch_all_branches;
+	fetchreq.list_refs_only = list_refs_only;
 	TAILQ_FOREACH(pe, have_refs, entry)
 		fetchreq.n_have_refs++;
 	TAILQ_FOREACH(pe, wanted_branches, entry)
blob - cb84af601f60de1ffe71b64a1d15358acbcc11e8
blob + 123004c4d0700fee5e741fc9839cdcefe67d560b
--- libexec/got-fetch-pack/got-fetch-pack.c
+++ libexec/got-fetch-pack/got-fetch-pack.c
@@ -483,7 +483,8 @@ fetch_error(const char *buf, size_t len)
 static const struct got_error *
 fetch_pack(int fd, int packfd, struct got_object_id *packid,
     struct got_pathlist_head *have_refs, int fetch_all_branches,
-    struct got_pathlist_head *wanted_branches, struct imsgbuf *ibuf)
+    struct got_pathlist_head *wanted_branches, int list_refs_only,
+    struct imsgbuf *ibuf)
 {
 	const struct got_error *err = NULL;
 	char buf[GOT_FETCH_PKTMAX];
@@ -561,7 +562,7 @@ fetch_pack(int fd, int packfd, struct got_object_id *p
 			    getprogname(), refname);
 
 		if (strncmp(refname, "refs/heads/", 11) == 0) {
-			if (fetch_all_branches) {
+			if (fetch_all_branches || list_refs_only) {
 				found_branch = 1;
 			} else if (!TAILQ_EMPTY(wanted_branches)) {
 				TAILQ_FOREACH(pe, wanted_branches, entry) {
@@ -628,6 +629,9 @@ fetch_pack(int fd, int packfd, struct got_object_id *p
 		nref++;
 	}
 
+	if (list_refs_only)
+		goto done;
+
 	/* Abort if we haven't found any branch to fetch. */
 	if (!found_branch) {
 		err = got_error(GOT_ERR_FETCH_NO_BRANCH);
@@ -1006,7 +1010,8 @@ main(int argc, char **argv)
 	packfd = imsg.fd;
 
 	err = fetch_pack(fetchfd, packfd, &packid, &have_refs,
-	    fetch_req.fetch_all_branches, &wanted_branches, &ibuf);
+	    fetch_req.fetch_all_branches, &wanted_branches,
+	    fetch_req.list_refs_only, &ibuf);
 done:
 	TAILQ_FOREACH(pe, &have_refs, entry) {
 		free((char *)pe->path);