Commit Diff


commit - 1255c02f3e117fa4fd07f9cc6fbcf62383755e5d
commit + 99495ddb79841fe89b0746d35a49bb81e9220096
blob - 17b5f0720dd626a7af6459cf23be329bb9b96e21
blob + 4a92790860d73b08d6ba18a5daf45eed2583c62f
--- got/got.1
+++ got/got.1
@@ -193,8 +193,10 @@ and
 .Pa config
 files of the cloned repository to store the
 .Ar repository-url
-and
+and any
 .Ar branch
+or
+.Ar reference
 arguments for future use by
 .Cm got fetch
 or
blob - 53829fbd5cf6cd49d4f93619417cf7218d2bbd1e
blob + 588f144e15210cef646e4d3b143f8cb74e7e5abe
--- got/got.c
+++ got/got.c
@@ -927,6 +927,7 @@ struct got_fetch_progress_arg {
 	struct {
 		struct got_pathlist_head *symrefs;
 		struct got_pathlist_head *wanted_branches;
+		struct got_pathlist_head *wanted_refs;
 		const char *proto;
 		const char *host;
 		const char *port;
@@ -942,7 +943,8 @@ static const struct got_error *
 create_config_files(const char *proto, const char *host, const char *port,
     const char *remote_repo_path, const char *git_url, int fetch_all_branches,
     int mirror_references, struct got_pathlist_head *symrefs,
-    struct got_pathlist_head *wanted_branches, struct got_repository *repo);
+    struct got_pathlist_head *wanted_branches,
+    struct got_pathlist_head *wanted_refs, struct got_repository *repo);
 
 static const struct got_error *
 fetch_progress(void *arg, const char *message, off_t packfile_size,
@@ -969,7 +971,8 @@ fetch_progress(void *arg, const char *message, off_t p
 		    a->config_info.fetch_all_branches,
 		    a->config_info.mirror_references,
 		    a->config_info.symrefs,
-		    a->config_info.wanted_branches, a->repo);
+		    a->config_info.wanted_branches,
+		    a->config_info.wanted_refs, a->repo);
 		if (err)
 			return err;
 		a->configs_created = 1;
@@ -1165,14 +1168,15 @@ static const struct got_error *
 create_gotconfig(const char *proto, const char *host, const char *port,
     const char *remote_repo_path, const char *default_branch,
     int fetch_all_branches, struct got_pathlist_head *wanted_branches,
-    int mirror_references, struct got_repository *repo)
+    struct got_pathlist_head *wanted_refs, int mirror_references,
+    struct got_repository *repo)
 {
 	const struct got_error *err = NULL;
 	char *gotconfig_path = NULL;
 	char *gotconfig = NULL;
 	FILE *gotconfig_file = NULL;
 	const char *branchname = NULL;
-	char *branches = NULL;
+	char *branches = NULL, *refs = NULL;
 	ssize_t n;
 
 	if (!fetch_all_branches && !TAILQ_EMPTY(wanted_branches)) {
@@ -1197,6 +1201,22 @@ create_gotconfig(const char *proto, const char *host, 
 		if (asprintf(&branches, "\"%s\" ", branchname) == -1) {
 			err = got_error_from_errno("asprintf");
 			goto done;
+		}
+	}
+	if (!TAILQ_EMPTY(wanted_refs)) {
+		struct got_pathlist_entry *pe;
+		TAILQ_FOREACH(pe, wanted_refs, entry) {
+			char *s;
+			const char *refname = pe->path;
+			if (strncmp(refname, "refs/", 5) == 0)
+				branchname += 5;
+			if (asprintf(&s, "%s\"%s\" ",
+			    refs ? refs : "", refname) == -1) {
+				err = got_error_from_errno("asprintf");
+				goto done;
+			}
+			free(refs);
+			refs = s;
 		}
 	}
 
@@ -1218,6 +1238,7 @@ create_gotconfig(const char *proto, const char *host, 
 	    "%s%s%s"
 	    "\trepository \"%s\"\n"
 	    "%s%s%s"
+	    "%s%s%s"
 	    "%s"
 	    "%s"
 	    "}\n",
@@ -1225,6 +1246,7 @@ create_gotconfig(const char *proto, const char *host, 
 	    port ? "\tport " : "", port ? port : "", port ? "\n" : "",
 	    remote_repo_path, branches ? "\tbranch { " : "",
 	    branches ? branches : "", branches ? "}\n" : "", 
+	    refs ? "\treference { " : "", refs ? refs : "", refs ? "}\n" : "", 
 	    mirror_references ? "\tmirror-references yes\n" : "",
 	    fetch_all_branches ? "\tfetch-all-branches yes\n" : "") == -1) {
 		err = got_error_from_errno("asprintf");
@@ -1247,13 +1269,14 @@ done:
 static const struct got_error *
 create_gitconfig(const char *git_url, const char *default_branch,
     int fetch_all_branches, struct got_pathlist_head *wanted_branches,
-    int mirror_references, struct got_repository *repo)
+    struct got_pathlist_head *wanted_refs, int mirror_references,
+    struct got_repository *repo)
 {
 	const struct got_error *err = NULL;
 	char *gitconfig_path = NULL;
 	char *gitconfig = NULL;
 	FILE *gitconfig_file = NULL;
-	char *branches = NULL;
+	char *branches = NULL, *refs = NULL;
 	const char *branchname, *mirror = NULL;
 	ssize_t n;
 
@@ -1317,15 +1340,36 @@ create_gitconfig(const char *git_url, const char *defa
 		    branchname) == -1) {
 			err = got_error_from_errno("asprintf");
 			goto done;
+		}
+	}
+	if (!mirror_references && !TAILQ_EMPTY(wanted_refs)) {
+		struct got_pathlist_entry *pe;
+		TAILQ_FOREACH(pe, wanted_refs, entry) {
+			char *s;
+			const char *refname = pe->path;
+			if (strncmp(refname, "refs/", 5) == 0)
+				refname += 5;
+			if (asprintf(&s,
+			    "%s\tfetch = +refs/%s:refs/remotes/%s/%s\n",
+			    refs ? refs : "",
+			    refname, GOT_FETCH_DEFAULT_REMOTE_NAME,
+			    refname) == -1) {
+				err = got_error_from_errno("asprintf");
+				goto done;
+			}
+			free(refs);
+			refs = s;
 		}
 	}
+
 	if (asprintf(&gitconfig,
 	    "[remote \"%s\"]\n"
 	    "\turl = %s\n"
 	    "%s"
+	    "%s"
 	    "%s",
 	    GOT_FETCH_DEFAULT_REMOTE_NAME, git_url, branches ? branches : "",
-	    mirror ? mirror : "") == -1) {
+	    refs ? refs : "", mirror ? mirror : "") == -1) {
 		err = got_error_from_errno("asprintf");
 		goto done;
 	}
@@ -1346,7 +1390,8 @@ static const struct got_error *
 create_config_files(const char *proto, const char *host, const char *port,
     const char *remote_repo_path, const char *git_url, int fetch_all_branches,
     int mirror_references, struct got_pathlist_head *symrefs,
-    struct got_pathlist_head *wanted_branches, struct got_repository *repo)
+    struct got_pathlist_head *wanted_branches,
+    struct got_pathlist_head *wanted_refs, struct got_repository *repo)
 {
 	const struct got_error *err = NULL;
 	const char *default_branch = NULL;
@@ -1376,13 +1421,13 @@ create_config_files(const char *proto, const char *hos
 	/* Create got.conf(5). */
 	err = create_gotconfig(proto, host, port, remote_repo_path,
 	    default_branch, fetch_all_branches, wanted_branches,
-	    mirror_references, repo);
+	    wanted_refs, mirror_references, repo);
 	if (err)
 		return err;
 
 	/* Create a config file Git can understand. */
 	return create_gitconfig(git_url, default_branch, fetch_all_branches,
-	    wanted_branches, mirror_references, repo);
+	    wanted_branches, wanted_refs, mirror_references, repo);
 }
 
 static const struct got_error *
@@ -1566,6 +1611,7 @@ cmd_clone(int argc, char *argv[])
 	fpa.repo = repo;
 	fpa.config_info.symrefs = &symrefs;
 	fpa.config_info.wanted_branches = &wanted_branches;
+	fpa.config_info.wanted_refs = &wanted_refs;
 	fpa.config_info.proto = proto;
 	fpa.config_info.host = host;
 	fpa.config_info.port = port;
@@ -2218,6 +2264,12 @@ cmd_fetch(int argc, char *argv[])
 		for (i = 0; i < remote->nbranches; i++) {
 			got_pathlist_append(&wanted_branches,
 			    remote->branches[i], NULL);
+		}
+	}
+	if (TAILQ_EMPTY(&wanted_refs)) {
+		for (i = 0; i < remote->nrefs; i++) {
+			got_pathlist_append(&wanted_refs,
+			    remote->refs[i], NULL);
 		}
 	}
 
blob - b4fe08c8227db5b27b1d37237b58cef45adbbdb8
blob + 846205a43f056559a960ab6d0c0ebb5076e6ba20
--- got/got.conf.5
+++ got/got.conf.5
@@ -125,6 +125,26 @@ command line with the
 option, and any
 .Cm branch
 configuration settings for this remote repository will be ignored.
+.It Ic reference Brq Ar reference ...
+Specify one or more arbitrary references which
+.Cm got fetch
+should fetch by default, in addition to the branches and tags that will
+be fetched.
+The list of references specified here can be overridden at the
+.Cm got fetch
+command line with the
+.Fl R
+option.
+.Cm got fetch
+will refuse to fetch references from the remote repository's
+.Dq refs/remotes/
+or
+.Dq refs/got/
+namespace.
+In any case, references in the
+.Dq refs/tags/
+namespace will always be fetched and mapped directly to local references
+in the same namespace.
 .It Ic mirror-references Ar yes | no
 This option controls the behaviour of
 .Cm got fetch
blob - 8cc337b5791e1261b077f0b81458c6a6fa7a3672
blob + 56d5bf0fafeca5450bd82577d1eb163c53c74a9e
--- include/got_repository.h
+++ include/got_repository.h
@@ -70,6 +70,10 @@ struct got_remote_repo {
 	/* Branches to fetch by default. */
 	int nbranches;
 	char **branches;
+
+	/* Other arbitrary references to fetch by default. */
+	int nrefs;
+	char **refs;
 };
 
 /*
blob - fba84c449fe93dd2034e7f391c26ffcc869e1bd9
blob + 8dbb8d0ba06da245e90c69b5db067658d496e0b0
--- lib/got_lib_privsep.h
+++ lib/got_lib_privsep.h
@@ -380,9 +380,11 @@ struct got_imsg_remote {
 	int mirror_references;
 	int fetch_all_branches;
 	int nbranches;
+	int nrefs;
 
 	/* Followed by name_len + url_len data bytes. */
 	/* Followed by nbranches GOT_IMSG_GITCONFIG_STR_VAL messages. */
+	/* Followed by nrefs GOT_IMSG_GITCONFIG_STR_VAL messages. */
 } __attribute__((__packed__));
 
 /*
blob - a4c526b34cd96016e55eeea3f87de869709e3eed
blob + 4fb6f24032dafb92991f1671b8252d6b045fa81b
--- lib/privsep.c
+++ lib/privsep.c
@@ -1808,6 +1808,9 @@ free_remote_data(struct got_remote_repo *remote)
 	for (i = 0; i < remote->nbranches; i++)
 		free(remote->branches[i]);
 	free(remote->branches);
+	for (i = 0; i < remote->nrefs; i++)
+		free(remote->refs[i]);
+	free(remote->refs);
 }
 
 const struct got_error *
@@ -1892,6 +1895,8 @@ got_privsep_recv_gitconfig_remotes(struct got_remote_r
 			remote->fetch_all_branches = iremote.fetch_all_branches;
 			remote->nbranches = 0;
 			remote->branches = NULL;
+			remote->nrefs = 0;
+			remote->refs = NULL;
 			(*nremotes)++;
 			break;
 		default:
@@ -2117,7 +2122,28 @@ got_privsep_recv_gotconfig_remotes(struct got_remote_r
 				}
 				remote->branches[i] = branch;
 				remote->nbranches++;
+			}
+			if (iremote.nrefs > 0) {
+				remote->refs = recallocarray(NULL, 0,
+				    iremote.nrefs, sizeof(char *));
+				if (remote->refs == NULL) {
+					err = got_error_from_errno("calloc");
+					free_remote_data(remote);
+					break;
+				}
 			}
+			remote->nrefs = 0;
+			for (i = 0; i < iremote.nrefs; i++) {
+				char *ref;
+				err = got_privsep_recv_gotconfig_str(&ref,
+				    ibuf);
+				if (err) {
+					free_remote_data(remote);
+					goto done;
+				}
+				remote->refs[i] = ref;
+				remote->nrefs++;
+			}
 			(*nremotes)++;
 			break;
 		default:
blob - 70e993212984e95be5ad134c51a5705fd98e7417
blob + 2fe537dc6e1f300194d9d5d74a0b2e170c9a0010
--- libexec/got-read-gotconfig/got-read-gotconfig.c
+++ libexec/got-read-gotconfig/got-read-gotconfig.c
@@ -143,15 +143,23 @@ send_gotconfig_remotes(struct imsgbuf *ibuf,
 		size_t len = sizeof(iremote);
 		struct ibuf *wbuf;
 		struct node_branch *branch;
-		int nbranches = 0;
+		struct node_ref *ref;
+		int nbranches = 0, nrefs = 0;
 
 		branch = repo->branch;
 		while (branch) {
 			branch = branch->next;
 			nbranches++;
+		}
+
+		ref = repo->ref;
+		while (ref) {
+			ref = ref->next;
+			nrefs++;
 		}
 
 		iremote.nbranches = nbranches;
+		iremote.nrefs = nrefs;
 		iremote.mirror_references = repo->mirror_references;
 		iremote.fetch_all_branches = repo->fetch_all_branches;
 
@@ -207,6 +215,14 @@ send_gotconfig_remotes(struct imsgbuf *ibuf,
 				break;
 			branch = branch->next;
 		}
+
+		ref = repo->ref;
+		while (ref) {
+			err = send_gotconfig_str(ibuf, ref->ref_name);
+			if (err)
+				break;
+			ref = ref->next;
+		}
 	}
 
 	free(url);
blob - 212b87b75f0b8d817b88f63b9c5d417d6499a93c
blob + 5f1429f97e35fc3f76e9a1054a203794f783e4c7
--- libexec/got-read-gotconfig/gotconfig.h
+++ libexec/got-read-gotconfig/gotconfig.h
@@ -21,6 +21,12 @@ struct node_branch {
 	struct node_branch *tail;
 };
 
+struct node_ref {
+	char *ref_name;
+	struct node_ref *next;
+	struct node_ref *tail;
+};
+
 struct gotconfig_remote_repo {
 	TAILQ_ENTRY(gotconfig_remote_repo) entry;
 	char	*name;
@@ -31,6 +37,7 @@ struct gotconfig_remote_repo {
 	int	mirror_references;
 	int	fetch_all_branches;
 	struct	node_branch *branch;
+	struct	node_ref *ref;
 };
 TAILQ_HEAD(gotconfig_remote_repo_list, gotconfig_remote_repo);
 
blob - 07d4556e67f6c9a4b929b7ada11df90e97e2ebd7
blob + 821eff3050b8f45ad0cd9b5475874eca2aa1117d
--- libexec/got-read-gotconfig/parse.y
+++ libexec/got-read-gotconfig/parse.y
@@ -87,6 +87,7 @@ typedef struct {
 		long long	 number;
 		char		*string;
 		struct node_branch *branch;
+		struct node_ref *ref;
 	} v;
 	int lineno;
 } YYSTYPE;
@@ -95,12 +96,13 @@ typedef struct {
 
 %token	ERROR
 %token	REMOTE REPOSITORY SERVER PORT PROTOCOL MIRROR_REFERENCES BRANCH
-%token	AUTHOR FETCH_ALL_BRANCHES
+%token	AUTHOR FETCH_ALL_BRANCHES REFERENCE
 %token	<v.string>	STRING
 %token	<v.number>	NUMBER
 %type	<v.number>	boolean portplain
 %type	<v.string>	numberstring
 %type	<v.branch>	branch xbranch branch_list
+%type	<v.ref>		ref xref ref_list
 
 %%
 
@@ -163,7 +165,27 @@ branch_list	: xbranch optnl			{ $$ = $1; }
 			$$ = $1;
 		}
 		;
-
+ref		: /* empty */		{ $$ = NULL; }
+		| xref			{ $$ = $1; }
+		| '{' optnl ref_list '}'	{ $$ = $3; }
+		;
+xref		: STRING {
+			$$ = calloc(1, sizeof(struct node_ref));
+			if ($$ == NULL) {
+				yyerror("calloc");
+				YYERROR;
+			}
+			$$->ref_name = $1;
+			$$->tail = $$;
+		}
+		;
+ref_list	: xref optnl			{ $$ = $1; }
+		| ref_list comma xref optnl {
+			$1->tail->next = $3;
+			$1->tail = $3;
+			$$ = $1;
+		}
+		;
 remoteopts2	: remoteopts2 remoteopts1 nl
 	   	| remoteopts1 optnl
 		;
@@ -205,6 +227,9 @@ remoteopts1	: REPOSITORY STRING {
 		}
 		| BRANCH branch {
 			remote->branch = $2;
+		}
+		| REFERENCE ref {
+			remote->ref = $2;
 		}
 	   	;
 remote		: REMOTE STRING {
@@ -293,6 +318,7 @@ lookup(char *s)
 		{"mirror-references",	MIRROR_REFERENCES},
 		{"port",		PORT},
 		{"protocol",		PROTOCOL},
+		{"reference",		REFERENCE},
 		{"remote",		REMOTE},
 		{"repository",		REPOSITORY},
 		{"server",		SERVER},
blob - b36a5f40a0b3db3547f0990d5fd2b2b947fc4821
blob + 44c5e030d3c0d68974cecd35b85f6edabc096ee3
--- regress/cmdline/clone.sh
+++ regress/cmdline/clone.sh
@@ -492,6 +492,7 @@ remote "origin" {
 	protocol ssh
 	repository "$testroot/repo"
 	branch { "master" }
+	reference { "hoo" }
 }
 EOF
 	cmp -s $testroot/repo-clone/got.conf $testroot/got.conf.expected
@@ -512,6 +513,7 @@ EOF
 [remote "origin"]
 	url = ssh://127.0.0.1$testroot/repo
 	fetch = +refs/heads/master:refs/remotes/origin/master
+	fetch = +refs/hoo:refs/remotes/origin/hoo
 EOF
 	cmp -s $testroot/repo-clone/config $testroot/config.expected
 	ret="$?"
@@ -565,6 +567,7 @@ remote "origin" {
 	protocol ssh
 	repository "$testroot/repo"
 	branch { "foo" }
+	reference { "hoo/boo/zoo" }
 }
 EOF
 	cmp -s $testroot/repo-clone/got.conf $testroot/got.conf.expected
@@ -585,6 +588,7 @@ EOF
 [remote "origin"]
 	url = ssh://127.0.0.1$testroot/repo
 	fetch = +refs/heads/foo:refs/remotes/origin/foo
+	fetch = +refs/hoo/boo/zoo:refs/remotes/origin/hoo/boo/zoo
 EOF
 	cmp -s $testroot/repo-clone/config $testroot/config.expected
 	ret="$?"
@@ -635,6 +639,7 @@ remote "origin" {
 	protocol ssh
 	repository "$testroot/repo"
 	branch { "master" }
+	reference { "hoo" }
 	mirror-references yes
 }
 EOF
blob - 0af6ce58eaded1d0d98284d9acdf7306a7405494
blob + fa4ff0524821f4d1b0e9bc984f084f50e656bdb0
--- regress/cmdline/fetch.sh
+++ regress/cmdline/fetch.sh
@@ -1050,11 +1050,46 @@ EOF
 	ret="$?"
 	if [ "$ret" != "0" ]; then
 		diff -u $testroot/stdout.expected $testroot/stdout
-	fi
-	test_done "$testroot" "$ret"
+		test_done "$testroot" "$ret"
+		return 1
+	fi
 
+cat > $testroot/repo-clone/got.conf <<EOF
+remote "origin" {
+	protocol ssh
+	server 127.0.0.1
+	repository "$testroot/repo"
+	branch { "foo" }
+	reference { "hoo/boo/zoo" }
 }
+EOF
+	(cd $testroot/repo-clone && got fetch -q > $testroot/stdout)
 
+	local tag_id=`got ref -r $testroot/repo -l \
+		| grep "^refs/tags/1.0" | tr -d ' ' | cut -d: -f2`
+	echo "HEAD: refs/heads/master" > $testroot/stdout.expected
+	echo "refs/heads/foo: $commit_id" >> $testroot/stdout.expected
+	echo "refs/heads/master: $commit_id" >> $testroot/stdout.expected
+	echo "refs/remotes/origin/HEAD: refs/remotes/origin/master" \
+		>> $testroot/stdout.expected
+	echo "refs/remotes/origin/foo: $commit_id" \
+		>> $testroot/stdout.expected
+	echo "refs/remotes/origin/hoo/boo/zoo: $commit_id" \
+		>> $testroot/stdout.expected
+	echo "refs/remotes/origin/master: $commit_id" \
+		>> $testroot/stdout.expected
+	echo "refs/tags/1.0: $tag_id" >> $testroot/stdout.expected
+
+	got ref -l -r $testroot/repo-clone > $testroot/stdout
+
+	cmp -s $testroot/stdout $testroot/stdout.expected
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/stdout.expected $testroot/stdout
+	fi
+	test_done "$testroot" "$ret"
+}
+
 test_parseargs "$@"
 run_test test_fetch_basic
 run_test test_fetch_list