Commit b8adfa5529205d58ab21c9c8fc76b50068ebbaa2

Stefan Sperling 2020-09-25T21:35:10

add "branch" keyword to got.conf which specifies a list of branches to fetch ok tracey

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
diff --git a/got/got.c b/got/got.c
index fd46852..d21f191 100644
--- a/got/got.c
+++ b/got/got.c
@@ -2096,6 +2096,14 @@ cmd_fetch(int argc, char *argv[])
 		goto done;
 	}
 
+	if (TAILQ_EMPTY(&wanted_branches) && remote->nbranches > 0) {
+		for (i = 0; i < remote->nbranches; i++) {
+			got_pathlist_append(&wanted_branches,
+			    remote->branches[i], NULL);
+		}
+
+	}
+
 	error = got_fetch_parse_uri(&proto, &host, &port, &server_path,
 	    &repo_name, remote->url);
 	if (error)
diff --git a/got/got.conf.5 b/got/got.conf.5
index f1c3eb0..4995cf2 100644
--- a/got/got.conf.5
+++ b/got/got.conf.5
@@ -105,6 +105,15 @@ for details.
 If not specified, the default port of the specified
 .Cm protocol
 will be used.
+.It Ic branch Brq Ar branch ...
+Specify one or more branches which
+.Cm got fetch
+should fetch from the remote repository by default.
+The list of branches specified here can be overridden at the
+.Cm got fetch
+command line with the
+.Fl b
+option.
 .It Ic mirror-references Ar yes | no
 This option controls the behaviour of
 .Cm got fetch
@@ -138,6 +147,7 @@ remote "origin" {
 	server git.gameoftrees.org
 	protocol git
 	repository got
+	branch { "main" }
 }
 .Ed
 .Pp
diff --git a/include/got_repository.h b/include/got_repository.h
index 2b10479..20f873a 100644
--- a/include/got_repository.h
+++ b/include/got_repository.h
@@ -51,15 +51,25 @@ const char *got_repo_get_gitconfig_owner(struct got_repository *);
 struct got_remote_repo {
 	char *name;
 	char *url;
-	
+
 	/*
 	 * If set, references are mirrored 1:1 into the local repository.
 	 * If not set, references are mapped into "refs/remotes/$name/".
 	 */
 	int mirror_references;
+
+	/* Branches to fetch by default. */
+	int nbranches;
+	char **branches;
 };
 
-/* Obtain the list of remote repositories parsed from gitconfig. */ 
+/*
+ * Free data allocated for the specified remote repository.
+ * Do not free the remote_repo pointer itself.
+ */
+void got_repo_free_remote_repo_data(struct got_remote_repo *);
+
+/* Obtain the list of remote repositories parsed from gitconfig. */
 void got_repo_get_gitconfig_remotes(int *, const struct got_remote_repo **,
     struct got_repository *);
 
diff --git a/lib/got_lib_privsep.h b/lib/got_lib_privsep.h
index 51acfb2..6c638e9 100644
--- a/lib/got_lib_privsep.h
+++ b/lib/got_lib_privsep.h
@@ -377,8 +377,10 @@ struct got_imsg_remote {
 	size_t name_len;
 	size_t url_len;
 	int mirror_references;
+	int nbranches;
 
 	/* Followed by name_len + url_len data bytes. */
+	/* Followed by nbranches GOT_IMSG_GITCONFIG_STR_VAL messages. */
 } __attribute__((__packed__));
 
 /*
diff --git a/lib/gotconfig.c b/lib/gotconfig.c
index d3c83b3..7133ec5 100644
--- a/lib/gotconfig.c
+++ b/lib/gotconfig.c
@@ -138,10 +138,8 @@ got_gotconfig_free(struct got_gotconfig *conf)
 
 	free(conf->author);
 
-	for (i = 0; i < conf->nremotes; i++) {
-		free(conf->remotes[i].name);
-		free(conf->remotes[i].url);
-	}
+	for (i = 0; i < conf->nremotes; i++)
+		got_repo_free_remote_repo_data(&conf->remotes[i]);
 	free(conf->remotes);
 	free(conf);
 }
diff --git a/lib/privsep.c b/lib/privsep.c
index 77debd8..0cacbdd 100644
--- a/lib/privsep.c
+++ b/lib/privsep.c
@@ -1785,6 +1785,18 @@ got_privsep_recv_gitconfig_int(int *val, struct imsgbuf *ibuf)
 	return err;
 }
 
+static void
+free_remote_data(struct got_remote_repo *remote)
+{
+	int i;
+
+	free(remote->name);
+	free(remote->url);
+	for (i = 0; i < remote->nbranches; i++)
+		free(remote->branches[i]);
+	free(remote->branches);
+}
+
 const struct got_error *
 got_privsep_recv_gitconfig_remotes(struct got_remote_repo **remotes,
     int *nremotes, struct imsgbuf *ibuf)
@@ -1838,6 +1850,7 @@ got_privsep_recv_gitconfig_remotes(struct got_remote_repo **remotes,
 		switch (imsg.hdr.type) {
 		case GOT_IMSG_GITCONFIG_REMOTE:
 			remote = &(*remotes)[*nremotes];
+			memset(remote, 0, sizeof(*remote));
 			if (datalen < sizeof(iremote)) {
 				err = got_error(GOT_ERR_PRIVSEP_LEN);
 				break;
@@ -1859,10 +1872,12 @@ got_privsep_recv_gitconfig_remotes(struct got_remote_repo **remotes,
 			    iremote.name_len, iremote.url_len);
 			if (remote->url == NULL) {
 				err = got_error_from_errno("strndup");
-				free(remote->name);
+				free_remote_data(remote);
 				break;
 			}
 			remote->mirror_references = iremote.mirror_references;
+			remote->nbranches = 0;
+			remote->branches = NULL;
 			(*nremotes)++;
 			break;
 		default:
@@ -1877,10 +1892,8 @@ got_privsep_recv_gitconfig_remotes(struct got_remote_repo **remotes,
 
 	if (err) {
 		int i;
-		for (i = 0; i < *nremotes; i++) {
-			free((*remotes)[i].name);
-			free((*remotes)[i].url);
-		}
+		for (i = 0; i < *nremotes; i++)
+			free_remote_data(&(*remotes)[i]);
 		free(*remotes);
 		*remotes = NULL;
 		*nremotes = 0;
@@ -1970,6 +1983,7 @@ got_privsep_recv_gotconfig_str(char **str, struct imsgbuf *ibuf)
 	return err;
 }
 
+
 const struct got_error *
 got_privsep_recv_gotconfig_remotes(struct got_remote_repo **remotes,
     int *nremotes, struct imsgbuf *ibuf)
@@ -2025,6 +2039,7 @@ got_privsep_recv_gotconfig_remotes(struct got_remote_repo **remotes,
 		struct got_remote_repo *remote;
 		const size_t min_datalen =
 		    MIN(sizeof(struct got_imsg_error), sizeof(iremote));
+		int i;
 
 		err = got_privsep_recv_imsg(&imsg, ibuf, min_datalen);
 		if (err)
@@ -2041,6 +2056,7 @@ got_privsep_recv_gotconfig_remotes(struct got_remote_repo **remotes,
 			break;
 		case GOT_IMSG_GOTCONFIG_REMOTE:
 			remote = &(*remotes)[*nremotes];
+			memset(remote, 0, sizeof(*remote));
 			if (datalen < sizeof(iremote)) {
 				err = got_error(GOT_ERR_PRIVSEP_LEN);
 				break;
@@ -2062,10 +2078,31 @@ got_privsep_recv_gotconfig_remotes(struct got_remote_repo **remotes,
 			    iremote.name_len, iremote.url_len);
 			if (remote->url == NULL) {
 				err = got_error_from_errno("strndup");
-				free(remote->name);
+				free_remote_data(remote);
 				break;
 			}
 			remote->mirror_references = iremote.mirror_references;
+			if (iremote.nbranches > 0) {
+				remote->branches = recallocarray(NULL, 0,
+				    iremote.nbranches, sizeof(char *));
+				if (remote->branches == NULL) {
+					err = got_error_from_errno("calloc");
+					free_remote_data(remote);
+					break;
+				}
+			}
+			remote->nbranches = 0;
+			for (i = 0; i < iremote.nbranches; i++) {
+				char *branch;
+				err = got_privsep_recv_gotconfig_str(&branch,
+				    ibuf);
+				if (err) {
+					free_remote_data(remote);
+					goto done;
+				}
+				remote->branches[i] = branch;
+				remote->nbranches++;
+			}
 			(*nremotes)++;
 			break;
 		default:
@@ -2077,13 +2114,11 @@ got_privsep_recv_gotconfig_remotes(struct got_remote_repo **remotes,
 		if (err)
 			break;
 	}
-
+done:
 	if (err) {
 		int i;
-		for (i = 0; i < *nremotes; i++) {
-			free((*remotes)[i].name);
-			free((*remotes)[i].url);
-		}
+		for (i = 0; i < *nremotes; i++)
+			free_remote_data(&(*remotes)[i]);
 		free(*remotes);
 		*remotes = NULL;
 		*nremotes = 0;
diff --git a/lib/repository.c b/lib/repository.c
index 4c90acd..5fe931f 100644
--- a/lib/repository.c
+++ b/lib/repository.c
@@ -681,16 +681,30 @@ got_repo_close(struct got_repository *repo)
 		got_gotconfig_free(repo->gotconfig);
 	free(repo->gitconfig_author_name);
 	free(repo->gitconfig_author_email);
-	for (i = 0; i < repo->ngitconfig_remotes; i++) {
-		free(repo->gitconfig_remotes[i].name);
-		free(repo->gitconfig_remotes[i].url);
-	}
+	for (i = 0; i < repo->ngitconfig_remotes; i++)
+		got_repo_free_remote_repo_data(&repo->gitconfig_remotes[i]);
 	free(repo->gitconfig_remotes);
 	free(repo);
 
 	return err;
 }
 
+void
+got_repo_free_remote_repo_data(struct got_remote_repo *repo)
+{
+	int i;
+
+	free(repo->name);
+	repo->name = NULL;
+	free(repo->url);
+	repo->url = NULL;
+	for (i = 0; i < repo->nbranches; i++)
+		free(repo->branches[i]);
+	free(repo->branches);
+	repo->branches = NULL;
+	repo->nbranches = 0;
+}
+
 const struct got_error *
 got_repo_map_path(char **in_repo_path, struct got_repository *repo,
     const char *input_path, int check_disk)
diff --git a/libexec/got-read-gotconfig/got-read-gotconfig.c b/libexec/got-read-gotconfig/got-read-gotconfig.c
index e7234d4..631e112 100644
--- a/libexec/got-read-gotconfig/got-read-gotconfig.c
+++ b/libexec/got-read-gotconfig/got-read-gotconfig.c
@@ -142,7 +142,16 @@ send_gotconfig_remotes(struct imsgbuf *ibuf,
 		struct got_imsg_remote iremote;
 		size_t len = sizeof(iremote);
 		struct ibuf *wbuf;
+		struct node_branch *branch;
+		int nbranches = 0;
 
+		branch = repo->branch;
+		while (branch) {
+			branch = branch->next;
+			nbranches++;
+		}
+
+		iremote.nbranches = nbranches;
 		iremote.mirror_references = repo->mirror_references;
 
 		iremote.name_len = strlen(repo->name);
@@ -189,6 +198,14 @@ send_gotconfig_remotes(struct imsgbuf *ibuf,
 
 		free(url);
 		url = NULL;
+
+		branch = repo->branch;
+		while (branch) {
+			err = send_gotconfig_str(ibuf, branch->branch_name);
+			if (err)
+				break;
+			branch = branch->next;
+		}
 	}
 
 	free(url);
diff --git a/libexec/got-read-gotconfig/gotconfig.h b/libexec/got-read-gotconfig/gotconfig.h
index ab55bd3..a909f14 100644
--- a/libexec/got-read-gotconfig/gotconfig.h
+++ b/libexec/got-read-gotconfig/gotconfig.h
@@ -15,6 +15,12 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+struct node_branch {
+	char *branch_name;
+	struct node_branch *next;
+	struct node_branch *tail;
+};
+
 struct gotconfig_remote_repo {
 	TAILQ_ENTRY(gotconfig_remote_repo) entry;
 	char	*name;
@@ -23,6 +29,7 @@ struct gotconfig_remote_repo {
 	char	*protocol;
 	int	port;
 	int	mirror_references;
+	struct	node_branch *branch;
 };
 TAILQ_HEAD(gotconfig_remote_repo_list, gotconfig_remote_repo);
 
diff --git a/libexec/got-read-gotconfig/parse.y b/libexec/got-read-gotconfig/parse.y
index aa7193a..2ae503c 100644
--- a/libexec/got-read-gotconfig/parse.y
+++ b/libexec/got-read-gotconfig/parse.y
@@ -86,6 +86,7 @@ typedef struct {
 	union {
 		int64_t		 number;
 		char		*string;
+		struct node_branch *branch;
 	} v;
 	int lineno;
 } YYSTYPE;
@@ -93,11 +94,13 @@ typedef struct {
 %}
 
 %token	ERROR
-%token	REMOTE REPOSITORY SERVER PORT PROTOCOL MIRROR_REFERENCES AUTHOR
+%token	REMOTE REPOSITORY SERVER PORT PROTOCOL MIRROR_REFERENCES BRANCH
+%token	AUTHOR
 %token	<v.string>	STRING
 %token	<v.number>	NUMBER
 %type	<v.number>	boolean portplain
 %type	<v.string>	numberstring
+%type	<v.branch>	branch xbranch branch_list
 
 %%
 
@@ -139,6 +142,28 @@ portplain	: numberstring	{
 			free($1);
 		}
 		;
+branch		: /* empty */				{ $$ = NULL; }
+		| xbranch			{ $$ = $1; }
+		| '{' optnl branch_list '}'	{ $$ = $3; }
+		;
+xbranch		: STRING {
+			$$ = calloc(1, sizeof(struct node_branch));
+			if ($$ == NULL) {
+				yyerror("calloc");
+				YYERROR;
+			}
+			$$->branch_name = $1;
+			$$->tail = $$;
+		}
+		;
+branch_list	: xbranch optnl			{ $$ = $1; }
+		| branch_list comma xbranch optnl {
+			$1->tail->next = $3;
+			$1->tail = $3;
+			$$ = $1;
+		}
+		;
+
 remoteopts2	: remoteopts2 remoteopts1 nl
 	   	| remoteopts1 optnl
 		;
@@ -175,6 +200,9 @@ remoteopts1	: REPOSITORY STRING {
 		| PORT portplain {
 			remote->port = $2;
 		}
+		| BRANCH branch {
+			remote->branch = $2;
+		}
 	   	;
 remote		: REMOTE STRING {
 			static const struct got_error* error;
@@ -212,6 +240,9 @@ optnl		: '\n' optnl
 		;
 nl		: '\n' optnl
 		;
+comma		: ','
+		| /* empty */
+		;
 %%
 
 struct keywords {
@@ -254,6 +285,7 @@ lookup(char *s)
 	/* This has to be sorted always. */
 	static const struct keywords keywords[] = {
 		{"author",		AUTHOR},
+		{"branch",		BRANCH},
 		{"mirror-references",	MIRROR_REFERENCES},
 		{"port",		PORT},
 		{"protocol",		PROTOCOL},