Commit Diff


commit - f8ab0c604a8e2fe1adea15c39721de37dba9bcb8
commit + 4ba1413314ad741dd3eec3b6672b127f2e03428e
blob - 21ccb8afed1387e1042cc6d41ec3d99f9d36ff8f
blob + 869f13d9c5e81da78233fd60e855d60bb5c769be
--- 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 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 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
@@ -203,8 +203,25 @@ are as follows:
 .Bl -tag -width Ds
 .It Fl a
 Fetch all branches from the remote repository.
-If this option is not specified, a branch resolved via the repository's HEAD
-reference will be fetched.
+If this option is not specified, a branch resolved via the remote
+repository's HEAD reference will be fetched.
+Cannot be used together with the
+.Fl b
+option.
+.It Fl b Ar branch
+Fetch the specified
+.Ar branch
+from the remote repository.
+This option may be specified multiple times to build a list of branches
+to fetch.
+If the branch corresponding to the remote repository's HEAD reference is not
+in this list, the cloned repository's HEAD reference will be set to the first
+branch which was fetched.
+If this option is not specified, a branch resolved via the remote
+repository's HEAD reference will be fetched.
+Cannot be used together with the
+.Fl a
+option.
 .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
@@ -250,11 +267,11 @@ The maximum is 3.
 .It Cm cl
 Short alias for
 .Cm clone .
-.It Cm fetch Oo Fl a Oc Oo Fl r Ar repository-path Oc Oo Fl q Oc Oo Fl v Oc Op Ar remote-repository-name
+.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
 Fetch new changes from a remote repository.
 If no
-.Ar remote-repository-name
-is specified the name
+.Ar remote-repository
+is specified,
 .Dq origin
 will be used.
 The remote repository's URL is obtained from the corresponding entry in the
@@ -295,8 +312,22 @@ are as follows:
 .Bl -tag -width Ds
 .It Fl a
 Fetch all branches from the remote repository.
-If this option is not specified, a branch resolved via the repository's HEAD
-reference will be fetched.
+If this option is not specified, a branch resolved via the remote
+repository's HEAD reference will be fetched.
+Cannot be used together with the
+.Fl b
+option.
+.It Fl b Ar branch
+Fetch the specified
+.Ar branch
+from the remote repository.
+This option may be specified multiple times to build a list of branches
+to fetch.
+If this option is not specified, a branch resolved via the remote
+repository's HEAD reference will be fetched.
+Cannot be used together with the
+.Fl a
+option.
 .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 - c73a14ba3c012dc9cd1e9bbd417ae85ee77f4542
blob + 4ba89fc33d869f104e24016016d6818deca6517d
--- got/got.c
+++ got/got.c
@@ -811,8 +811,8 @@ done:
 __dead static void
 usage_clone(void)
 {
-	fprintf(stderr, "usage: %s clone [-a] [-m] [-q] [-v] repository-url "
-	    "[directory]\n", getprogname());
+	fprintf(stderr, "usage: %s clone [-a] [-b branch] [-m] [-q] [-v] "
+	    "repository-url [directory]\n", getprogname());
 	exit(1);
 }
 
@@ -885,6 +885,21 @@ fetch_progress(void *arg, const char *message, off_t p
 }
 
 static const struct got_error *
+create_head_ref(struct got_reference *target_ref, struct got_repository *repo)
+{
+	const struct got_error *err;
+	struct got_reference *head_symref;
+
+	err = got_ref_alloc_symref(&head_symref, GOT_REF_HEAD, target_ref);
+	if (err)
+		return err;
+
+	err = got_ref_write(head_symref, repo);
+	got_ref_close(head_symref);
+	return err;
+}
+
+static const struct got_error *
 cmd_clone(int argc, char *argv[])
 {
 	const struct got_error *error = NULL;
@@ -893,7 +908,7 @@ cmd_clone(int argc, char *argv[])
 	char *default_destdir = NULL, *id_str = NULL;
 	const char *repo_path;
 	struct got_repository *repo = NULL;
-	struct got_pathlist_head refs, symrefs;
+	struct got_pathlist_head refs, symrefs, wanted_branches;
 	struct got_pathlist_entry *pe;
 	struct got_object_id *pack_hash = NULL;
 	int ch, fetchfd = -1;
@@ -908,11 +923,18 @@ cmd_clone(int argc, char *argv[])
 
 	TAILQ_INIT(&refs);
 	TAILQ_INIT(&symrefs);
+	TAILQ_INIT(&wanted_branches);
 
-	while ((ch = getopt(argc, argv, "amvq")) != -1) {
+	while ((ch = getopt(argc, argv, "ab:mvq")) != -1) {
 		switch (ch) {
 		case 'a':
 			fetch_all_branches = 1;
+			break;
+		case 'b':
+			error = got_pathlist_append(&wanted_branches,
+			    optarg, NULL);
+			if (error)
+				return error;
 			break;
 		case 'm':
 			mirror_references = 1;
@@ -934,6 +956,9 @@ cmd_clone(int argc, char *argv[])
 	argc -= optind;
 	argv += optind;
 
+	if (fetch_all_branches && !TAILQ_EMPTY(&wanted_branches))
+		errx(1, "-a and -b options are mutually exclusive\n");
+
 	uri = argv[0];
 
 	if (argc == 1)
@@ -1023,7 +1048,8 @@ 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, fetchfd, repo, fetch_progress, &fpa);
+	    fetch_all_branches, &wanted_branches, fetchfd,
+	    repo, fetch_progress, &fpa);
 	if (error)
 		goto done;
 
@@ -1088,19 +1114,41 @@ cmd_clone(int argc, char *argv[])
 			goto done;
 		}
 
-		error = got_ref_alloc_symref(&head_symref,
-		    GOT_REF_HEAD, target_ref);
+		if (verbosity >= 0)
+			printf("Setting %s to %s\n", refname, target);
+		error = create_head_ref(target_ref, repo);
 		got_ref_close(target_ref);
 		if (error)
 			goto done;
-
-		if (verbosity >= 0)
-			printf("Setting %s to %s\n", GOT_REF_HEAD,
-			    got_ref_get_symref_target(head_symref));
+	}
+	if (pe == NULL) {
+		/*
+		 * We failed to set the HEAD reference. If we asked for
+		 * a set of wanted branches use the first of one of those
+		 * which could be fetched instead.
+		 */
+		 TAILQ_FOREACH(pe, &wanted_branches, entry) {
+			const char *target = pe->path;
+			struct got_reference *target_ref;
 
-		error = got_ref_write(head_symref, repo);
-		if (error)
-			goto done;
+			error = got_ref_open(&target_ref, repo, target, 0);
+			if (error) {
+				if (error->code == GOT_ERR_NOT_REF) {
+					error = NULL;
+					continue;
+				}
+				goto done;
+			}
+
+			if (verbosity >= 0)
+				printf("Setting %s to %s\n", GOT_REF_HEAD,
+				    got_ref_get_name(target_ref));
+			error = create_head_ref(target_ref, repo);
+			got_ref_close(target_ref);
+			if (error)
+				goto done;
+			break;
+		}
 	}
 
 	/* Create a config file git-fetch(1) can understand. */
@@ -1187,6 +1235,7 @@ done:
 		free(pe->data);
 	}
 	got_pathlist_free(&symrefs);
+	got_pathlist_free(&wanted_branches);
 	free(pack_hash);
 	free(proto);
 	free(host);
@@ -1268,8 +1317,8 @@ done:
 __dead static void
 usage_fetch(void)
 {
-	fprintf(stderr, "usage: %s fetch [-a] [-r repository-path] [-q] [-v] "
-	    "[remote-repository-name]\n", getprogname());
+	fprintf(stderr, "usage: %s fetch [-a] [-b branch] [-r repository-path] "
+	    "[-q] [-v] [remote-repository-name]\n", getprogname());
 	exit(1);
 }
 
@@ -1286,7 +1335,7 @@ cmd_fetch(int argc, char *argv[])
 	char *id_str = NULL;
 	struct got_repository *repo = NULL;
 	struct got_worktree *worktree = NULL;
-	struct got_pathlist_head refs, symrefs;
+	struct got_pathlist_head refs, symrefs, wanted_branches;
 	struct got_pathlist_entry *pe;
 	struct got_object_id *pack_hash = NULL;
 	int i, ch, fetchfd = -1;
@@ -1295,11 +1344,18 @@ cmd_fetch(int argc, char *argv[])
 
 	TAILQ_INIT(&refs);
 	TAILQ_INIT(&symrefs);
+	TAILQ_INIT(&wanted_branches);
 
-	while ((ch = getopt(argc, argv, "ar:vq")) != -1) {
+	while ((ch = getopt(argc, argv, "ab:r:vq")) != -1) {
 		switch (ch) {
 		case 'a':
 			fetch_all_branches = 1;
+			break;
+		case 'b':
+			error = got_pathlist_append(&wanted_branches,
+			    optarg, NULL);
+			if (error)
+				return error;
 			break;
 		case 'r':
 			repo_path = realpath(optarg, NULL);
@@ -1325,6 +1381,9 @@ cmd_fetch(int argc, char *argv[])
 	argc -= optind;
 	argv += optind;
 
+	if (fetch_all_branches && !TAILQ_EMPTY(&wanted_branches))
+		errx(1, "-a and -b options are mutually exclusive\n");
+
 	if (argc == 0)
 		remote_name = GOT_FETCH_DEFAULT_REMOTE_NAME;
 	else if (argc == 1)
@@ -1427,8 +1486,8 @@ cmd_fetch(int argc, char *argv[])
 	fpa.last_p_resolved = -1;
 	fpa.verbosity = verbosity;
 	error = got_fetch_pack(&pack_hash, &refs, &symrefs, remote->name,
-	    remote->mirror_references, fetch_all_branches, fetchfd, repo,
-	    fetch_progress, &fpa);
+	    remote->mirror_references, fetch_all_branches, &wanted_branches,
+	    fetchfd, repo, fetch_progress, &fpa);
 	if (error)
 		goto done;
 
@@ -1526,6 +1585,7 @@ done:
 		free(pe->data);
 	}
 	got_pathlist_free(&symrefs);
+	got_pathlist_free(&wanted_branches);
 	free(id_str);
 	free(cwd);
 	free(repo_path);
blob - 14f29c4e58a7a424ab01ef02f0e6205fbdbf016a
blob + a7b9590dcef74fceec8004075319634cc5f5e462
--- include/got_fetch.h
+++ include/got_fetch.h
@@ -61,4 +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, int, struct got_repository *, got_fetch_progress_cb, void *);
+	int, int, struct got_pathlist_head *, int, struct got_repository *,
+	got_fetch_progress_cb, void *);
blob - e36d5b846fbbe85dbec5aaf078f073bde3b2ef11
blob + d9e55b8853c1065604fa45460938e09c2444b97b
--- lib/fetch.c
+++ lib/fetch.c
@@ -387,7 +387,8 @@ check_pack_hash(int fd, size_t sz, uint8_t *hcomp)
 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, int fetchfd,
+    int mirror_references, int fetch_all_branches,
+    struct got_pathlist_head *wanted_branches, int fetchfd,
     struct got_repository *repo,
     got_fetch_progress_cb progress_cb, void *progress_arg)
 {
@@ -554,7 +555,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);
+	    fetch_all_branches, wanted_branches);
 	if (err != NULL)
 		goto done;
 	nfetchfd = -1;
blob - 3df98dcefa961785e3259c8141d5647f3648ccee
blob + ca92bcd3660a9b973c8c555f1cbb36a1c0fede5b
--- lib/got_lib_privsep.h
+++ lib/got_lib_privsep.h
@@ -110,6 +110,8 @@ enum got_imsg_type {
 
 	/* Messages related to networking. */
 	GOT_IMSG_FETCH_REQUEST,
+	GOT_IMSG_FETCH_HAVE_REF,
+	GOT_IMSG_FETCH_WANTED_BRANCH,
 	GOT_IMSG_FETCH_OUTFD,
 	GOT_IMSG_FETCH_SYMREFS,
 	GOT_IMSG_FETCH_REF,
@@ -239,17 +241,26 @@ struct got_imsg_tag_object {
 	 */
 } __attribute__((__packed__));
 
-/* Structures for GOT_IMSG_FETCH_REQUEST data. */
+/* Structure for GOT_IMSG_FETCH_HAVE_REF data. */
 struct got_imsg_fetch_have_ref {
 	uint8_t id[SHA1_DIGEST_LENGTH];
 	size_t name_len;
 	/* Followed by name_len data bytes. */
 } __attribute__((__packed__));
 
+/* Structure for GOT_IMSG_FETCH_WANTED_BRANCH data. */
+struct got_imsg_fetch_wanted_branch {
+	size_t name_len;
+	/* Followed by name_len data bytes. */
+} __attribute__((__packed__));
+
+/* Structure for GOT_IMSG_FETCH_REQUEST data. */
 struct got_imsg_fetch_request {
 	int fetch_all_branches;
 	size_t n_have_refs;
-	/* Followed by n_have_refs times of got_imsg_fetch_have_ref data. */
+	size_t n_wanted_branches;
+	/* Followed by n_have_refs GOT_IMSG_FETCH_HAVE_REF messages. */
+	/* Followed by n_wanted_branches times GOT_IMSG_FETCH_WANTED_BRANCH. */
 } __attribute__((__packed__));
 
 /* Structures for GOT_IMSG_FETCH_SYMREFS data. */
@@ -345,7 +356,7 @@ struct got_imsg_remote {
 	int mirror_references;
 
 	/* Followed by name_len + url_len data bytes. */
-};
+} __attribute__((__packed__));
 
 /*
  * Structure for GOT_IMSG_GITCONFIG_REMOTES data.
@@ -390,7 +401,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 *, int, struct got_pathlist_head *);
 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 - c03ab3df89453d5c30fe7cfe3f920f83564e86f7
blob + 805dd3e8bcf919be36494e99aa5351e30d121d66
--- lib/privsep.c
+++ lib/privsep.c
@@ -412,73 +412,107 @@ 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 *have_refs, int fetch_all_branches,
+   struct got_pathlist_head *wanted_branches)
 {
 	const struct got_error *err = NULL;
 	struct ibuf *wbuf;
-	size_t len, n_have_refs = 0;
+	size_t len;
 	struct got_pathlist_entry *pe;
+	struct got_imsg_fetch_request fetchreq;
 
+	memset(&fetchreq, 0, sizeof(fetchreq));
+	fetchreq.fetch_all_branches = fetch_all_branches;
+	TAILQ_FOREACH(pe, have_refs, entry)
+		fetchreq.n_have_refs++;
+	TAILQ_FOREACH(pe, wanted_branches, entry)
+		fetchreq.n_wanted_branches++;
 	len = sizeof(struct got_imsg_fetch_request);
-	TAILQ_FOREACH(pe, have_refs, entry) {
-		len += sizeof(struct got_imsg_fetch_have_ref) + pe->path_len;
-		n_have_refs++;
-	}
 	if (len >= MAX_IMSGSIZE - IMSG_HEADER_SIZE) {
 		close(fd);
 		return got_error(GOT_ERR_NO_SPACE);
 	}
 
-	wbuf = imsg_create(ibuf, GOT_IMSG_FETCH_REQUEST, 0, 0, len);
-	if (wbuf == NULL) {
-		close(fd);
-		return got_error_from_errno("imsg_create FETCH_REQUEST");
-	}
+	if (imsg_compose(ibuf, GOT_IMSG_FETCH_REQUEST, 0, 0, fd,
+	    &fetchreq, sizeof(fetchreq)) == -1)
+		return got_error_from_errno(
+		    "imsg_compose FETCH_SERVER_PROGRESS");
 
-	/* Keep in sync with struct got_imsg_fetch_request definition! */
-	if (imsg_add(wbuf, &fetch_all_branches, sizeof(fetch_all_branches))
-	    == -1) {
-		err = got_error_from_errno("imsg_add FETCH_REQUEST");
-		ibuf_free(wbuf);
+	err = flush_imsg(ibuf);
+	if (err) {
 		close(fd);
 		return err;
 	}
-	if (imsg_add(wbuf, &n_have_refs, sizeof(n_have_refs)) == -1) {
-		err = got_error_from_errno("imsg_add FETCH_REQUEST");
-		ibuf_free(wbuf);
-		close(fd);
-		return err;
-	}
+	fd = -1;
 
 	TAILQ_FOREACH(pe, have_refs, entry) {
 		const char *name = pe->path;
 		size_t name_len = pe->path_len;
 		struct got_object_id *id = pe->data;
 
+		len = sizeof(struct got_imsg_fetch_have_ref) + name_len;
+		wbuf = imsg_create(ibuf, GOT_IMSG_FETCH_HAVE_REF, 0, 0, len);
+		if (wbuf == NULL)
+			return got_error_from_errno("imsg_create FETCH_HAVE_REF");
+
 		/* Keep in sync with struct got_imsg_fetch_have_ref! */
 		if (imsg_add(wbuf, id->sha1, sizeof(id->sha1)) == -1) {
-			err = got_error_from_errno("imsg_add FETCH_REQUEST");
+			err = got_error_from_errno("imsg_add FETCH_HAVE_REF");
 			ibuf_free(wbuf);
-			close(fd);
 			return err;
 		}
 		if (imsg_add(wbuf, &name_len, sizeof(name_len)) == -1) {
-			err = got_error_from_errno("imsg_add FETCH_REQUEST");
+			err = got_error_from_errno("imsg_add FETCH_HAVE_REF");
 			ibuf_free(wbuf);
-			close(fd);
 			return err;
 		}
 		if (imsg_add(wbuf, name, name_len) == -1) {
-			err = got_error_from_errno("imsg_add FETCH_REQUEST");
+			err = got_error_from_errno("imsg_add FETCH_HAVE_REF");
 			ibuf_free(wbuf);
-			close(fd);
 			return err;
 		}
+
+		wbuf->fd = -1;
+		imsg_close(ibuf, wbuf);
+		err = flush_imsg(ibuf);
+		if (err)
+			return err;
 	}
 
-	wbuf->fd = fd;
-	imsg_close(ibuf, wbuf);
-	return flush_imsg(ibuf);
+	TAILQ_FOREACH(pe, wanted_branches, entry) {
+		const char *name = pe->path;
+		size_t name_len = pe->path_len;
+
+		len = sizeof(struct got_imsg_fetch_wanted_branch) + name_len;
+		wbuf = imsg_create(ibuf, GOT_IMSG_FETCH_WANTED_BRANCH, 0, 0,
+		    len);
+		if (wbuf == NULL)
+			return got_error_from_errno(
+			     "imsg_create FETCH_WANTED_BRANCH");
+
+		/* Keep in sync with struct got_imsg_fetch_wanted_branch! */
+		if (imsg_add(wbuf, &name_len, sizeof(name_len)) == -1) {
+			err = got_error_from_errno(
+			    "imsg_add FETCH_WANTED_BRANCH");
+			ibuf_free(wbuf);
+			return err;
+		}
+		if (imsg_add(wbuf, name, name_len) == -1) {
+			err = got_error_from_errno(
+			     "imsg_add FETCH_WANTED_BRANCH");
+			ibuf_free(wbuf);
+			return err;
+		}
+
+		wbuf->fd = -1;
+		imsg_close(ibuf, wbuf);
+		err = flush_imsg(ibuf);
+		if (err)
+			return err;
+	}
+
+	return NULL;
+
 }
 
 const struct got_error *
blob - 35e6b7739ac8c1477f7ec61d9e2524ce6c3f7f7b
blob + cb84af601f60de1ffe71b64a1d15358acbcc11e8
--- libexec/got-fetch-pack/got-fetch-pack.c
+++ libexec/got-fetch-pack/got-fetch-pack.c
@@ -483,7 +483,7 @@ 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 imsgbuf *ibuf)
+    struct got_pathlist_head *wanted_branches, struct imsgbuf *ibuf)
 {
 	const struct got_error *err = NULL;
 	char buf[GOT_FETCH_PKTMAX];
@@ -561,10 +561,21 @@ fetch_pack(int fd, int packfd, struct got_object_id *p
 			    getprogname(), refname);
 
 		if (strncmp(refname, "refs/heads/", 11) == 0) {
-			if (default_branch != NULL &&
-			    !match_branch(refname, default_branch))
-				continue;
-			found_branch = 1;
+			if (fetch_all_branches) {
+				found_branch = 1;
+			} else if (!TAILQ_EMPTY(wanted_branches)) {
+				TAILQ_FOREACH(pe, wanted_branches, entry) {
+					if (match_branch(refname, pe->path))
+						break;
+				}
+				if (pe == NULL)
+					continue;
+				found_branch = 1;
+			} else if (default_branch != NULL) {
+				if (!match_branch(refname, default_branch))
+					continue;
+				found_branch = 1;
+			}
 		} else if (strncmp(refname, "refs/tags/", 10) != 0) {
 			if (chattygot) {
 				fprintf(stderr, "%s: ignoring '%s' which is "
@@ -837,11 +848,12 @@ main(int argc, char **argv)
 	struct imsgbuf ibuf;
 	struct imsg imsg;
 	struct got_pathlist_head have_refs;
+	struct got_pathlist_head wanted_branches;
 	struct got_pathlist_entry *pe;
-	struct got_imsg_fetch_request *fetch_req = NULL;
+	struct got_imsg_fetch_request fetch_req;
 	struct got_imsg_fetch_have_ref href;
-	size_t datalen, remain;
-	size_t offset;
+	struct got_imsg_fetch_wanted_branch wbranch;
+	size_t datalen;
 #if 0
 	static int attached;
 	while (!attached)
@@ -849,6 +861,7 @@ main(int argc, char **argv)
 #endif
 
 	TAILQ_INIT(&have_refs);
+	TAILQ_INIT(&wanted_branches);
 
 	if (getenv("GOT_FETCH_DEBUG") != NULL) {
 		fprintf(stderr, "%s being chatty!\n", getprogname());
@@ -876,43 +889,46 @@ main(int argc, char **argv)
 		goto done;
 	}
 	datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
-	if (datalen < sizeof(struct got_imsg_fetch_request)) {
+	if (datalen < sizeof(fetch_req)) {
 		err = got_error(GOT_ERR_PRIVSEP_LEN);
 		goto done;
 	}
-	fetch_req = (struct got_imsg_fetch_request *)imsg.data;
-	if (datalen < sizeof(*fetch_req) +
-	    sizeof(struct got_imsg_fetch_have_ref) *
-	    fetch_req->n_have_refs) {
-		err = got_error(GOT_ERR_PRIVSEP_LEN);
-		goto done;
-	}
-	offset = sizeof(*fetch_req);
-	remain = datalen;
-	for (i = 0; i < fetch_req->n_have_refs; i++) {
+	memcpy(&fetch_req, imsg.data, sizeof(fetch_req));
+	fetchfd = imsg.fd;
+	imsg_free(&imsg);
+
+	for (i = 0; i < fetch_req.n_have_refs; i++) {
 		struct got_object_id *id;
 		char *refname;
 
-		if (remain < sizeof(href) || offset > datalen) {
+		if ((err = got_privsep_recv_imsg(&imsg, &ibuf, 0)) != 0) {
+			if (err->code == GOT_ERR_PRIVSEP_PIPE)
+				err = NULL;
+			goto done;
+		}
+		if (imsg.hdr.type == GOT_IMSG_STOP)
+			goto done;
+		if (imsg.hdr.type != GOT_IMSG_FETCH_HAVE_REF) {
+			err = got_error(GOT_ERR_PRIVSEP_MSG);
+			goto done;
+		}
+		datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
+		if (datalen < sizeof(href)) {
 			err = got_error(GOT_ERR_PRIVSEP_LEN);
 			goto done;
 		}
-		memcpy(&href, imsg.data + offset, sizeof(href));
-		remain -= sizeof(href);
-		if (remain < href.name_len) {
+		memcpy(&href, imsg.data, sizeof(href));
+		if (datalen - sizeof(href) < href.name_len) {
 			err = got_error(GOT_ERR_PRIVSEP_LEN);
 			goto done;
 		}
-		remain -= href.name_len;
 		refname = malloc(href.name_len + 1);
 		if (refname == NULL) {
 			err = got_error_from_errno("malloc");
 			goto done;
 		}
-		offset += sizeof(href);
-		memcpy(refname, imsg.data + offset, href.name_len);
+		memcpy(refname, imsg.data + sizeof(href), href.name_len);
 		refname[href.name_len] = '\0';
-		offset += href.name_len;
 
 		id = malloc(sizeof(*id));
 		if (id == NULL) {
@@ -927,9 +943,51 @@ main(int argc, char **argv)
 			free(id);
 			goto done;
 		}
+
+		imsg_free(&imsg);
 	}
-	fetchfd = imsg.fd;
 
+	for (i = 0; i < fetch_req.n_wanted_branches; i++) {
+		char *refname;
+
+		if ((err = got_privsep_recv_imsg(&imsg, &ibuf, 0)) != 0) {
+			if (err->code == GOT_ERR_PRIVSEP_PIPE)
+				err = NULL;
+			goto done;
+		}
+		if (imsg.hdr.type == GOT_IMSG_STOP)
+			goto done;
+		if (imsg.hdr.type != GOT_IMSG_FETCH_WANTED_BRANCH) {
+			err = got_error(GOT_ERR_PRIVSEP_MSG);
+			goto done;
+		}
+		datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
+		if (datalen < sizeof(wbranch)) {
+			err = got_error(GOT_ERR_PRIVSEP_LEN);
+			goto done;
+		}
+		memcpy(&wbranch, imsg.data, sizeof(wbranch));
+		if (datalen - sizeof(wbranch) < wbranch.name_len) {
+			err = got_error(GOT_ERR_PRIVSEP_LEN);
+			goto done;
+		}
+		refname = malloc(wbranch.name_len + 1);
+		if (refname == NULL) {
+			err = got_error_from_errno("malloc");
+			goto done;
+		}
+		memcpy(refname, imsg.data + sizeof(wbranch), wbranch.name_len);
+		refname[wbranch.name_len] = '\0';
+
+		err = got_pathlist_append(&wanted_branches, refname, NULL);
+		if (err) {
+			free(refname);
+			goto done;
+		}
+
+		imsg_free(&imsg);
+	}
+
 	if ((err = got_privsep_recv_imsg(&imsg, &ibuf, 0)) != 0) {
 		if (err->code == GOT_ERR_PRIVSEP_PIPE)
 			err = NULL;
@@ -948,13 +1006,16 @@ main(int argc, char **argv)
 	packfd = imsg.fd;
 
 	err = fetch_pack(fetchfd, packfd, &packid, &have_refs,
-	    fetch_req->fetch_all_branches, &ibuf);
+	    fetch_req.fetch_all_branches, &wanted_branches, &ibuf);
 done:
 	TAILQ_FOREACH(pe, &have_refs, entry) {
 		free((char *)pe->path);
 		free(pe->data);
 	}
 	got_pathlist_free(&have_refs);
+	TAILQ_FOREACH(pe, &wanted_branches, entry)
+		free((char *)pe->path);
+	got_pathlist_free(&wanted_branches);
 	if (packfd != -1 && close(packfd) == -1 && err == NULL)
 		err = got_error_from_errno("close");
 	if (err != NULL)