File: | got/got.c |
Warning: | line 717, column 13 Potential leak of memory pointed to by 'logmsg' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* | |||
2 | * Copyright (c) 2017 Martin Pieuchot <mpi@openbsd.org> | |||
3 | * Copyright (c) 2018, 2019, 2020 Stefan Sperling <stsp@openbsd.org> | |||
4 | * Copyright (c) 2020 Ori Bernstein <ori@openbsd.org> | |||
5 | * | |||
6 | * Permission to use, copy, modify, and distribute this software for any | |||
7 | * purpose with or without fee is hereby granted, provided that the above | |||
8 | * copyright notice and this permission notice appear in all copies. | |||
9 | * | |||
10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
17 | */ | |||
18 | ||||
19 | #include <sys/queue.h> | |||
20 | #include <sys/types.h> | |||
21 | #include <sys/stat.h> | |||
22 | #include <sys/param.h> | |||
23 | #include <sys/wait.h> | |||
24 | ||||
25 | #include <err.h> | |||
26 | #include <errno(*__errno()).h> | |||
27 | #include <fcntl.h> | |||
28 | #include <limits.h> | |||
29 | #include <locale.h> | |||
30 | #include <ctype.h> | |||
31 | #include <signal.h> | |||
32 | #include <stdio.h> | |||
33 | #include <stdlib.h> | |||
34 | #include <string.h> | |||
35 | #include <unistd.h> | |||
36 | #include <libgen.h> | |||
37 | #include <time.h> | |||
38 | #include <paths.h> | |||
39 | #include <regex.h> | |||
40 | #include <getopt.h> | |||
41 | #include <util.h> | |||
42 | ||||
43 | #include "got_version.h" | |||
44 | #include "got_error.h" | |||
45 | #include "got_object.h" | |||
46 | #include "got_reference.h" | |||
47 | #include "got_repository.h" | |||
48 | #include "got_path.h" | |||
49 | #include "got_cancel.h" | |||
50 | #include "got_worktree.h" | |||
51 | #include "got_diff.h" | |||
52 | #include "got_commit_graph.h" | |||
53 | #include "got_fetch.h" | |||
54 | #include "got_blame.h" | |||
55 | #include "got_privsep.h" | |||
56 | #include "got_opentemp.h" | |||
57 | #include "got_gotconfig.h" | |||
58 | ||||
59 | #ifndef nitems | |||
60 | #define nitems(_a)(sizeof((_a)) / sizeof((_a)[0])) (sizeof((_a)) / sizeof((_a)[0])) | |||
61 | #endif | |||
62 | ||||
63 | static volatile sig_atomic_t sigint_received; | |||
64 | static volatile sig_atomic_t sigpipe_received; | |||
65 | ||||
66 | static void | |||
67 | catch_sigint(int signo) | |||
68 | { | |||
69 | sigint_received = 1; | |||
70 | } | |||
71 | ||||
72 | static void | |||
73 | catch_sigpipe(int signo) | |||
74 | { | |||
75 | sigpipe_received = 1; | |||
76 | } | |||
77 | ||||
78 | ||||
79 | struct got_cmd { | |||
80 | const char *cmd_name; | |||
81 | const struct got_error *(*cmd_main)(int, char *[]); | |||
82 | void (*cmd_usage)(void); | |||
83 | const char *cmd_alias; | |||
84 | }; | |||
85 | ||||
86 | __dead__attribute__((__noreturn__)) static void usage(int, int); | |||
87 | __dead__attribute__((__noreturn__)) static void usage_init(void); | |||
88 | __dead__attribute__((__noreturn__)) static void usage_import(void); | |||
89 | __dead__attribute__((__noreturn__)) static void usage_clone(void); | |||
90 | __dead__attribute__((__noreturn__)) static void usage_fetch(void); | |||
91 | __dead__attribute__((__noreturn__)) static void usage_checkout(void); | |||
92 | __dead__attribute__((__noreturn__)) static void usage_update(void); | |||
93 | __dead__attribute__((__noreturn__)) static void usage_log(void); | |||
94 | __dead__attribute__((__noreturn__)) static void usage_diff(void); | |||
95 | __dead__attribute__((__noreturn__)) static void usage_blame(void); | |||
96 | __dead__attribute__((__noreturn__)) static void usage_tree(void); | |||
97 | __dead__attribute__((__noreturn__)) static void usage_status(void); | |||
98 | __dead__attribute__((__noreturn__)) static void usage_ref(void); | |||
99 | __dead__attribute__((__noreturn__)) static void usage_branch(void); | |||
100 | __dead__attribute__((__noreturn__)) static void usage_tag(void); | |||
101 | __dead__attribute__((__noreturn__)) static void usage_add(void); | |||
102 | __dead__attribute__((__noreturn__)) static void usage_remove(void); | |||
103 | __dead__attribute__((__noreturn__)) static void usage_revert(void); | |||
104 | __dead__attribute__((__noreturn__)) static void usage_commit(void); | |||
105 | __dead__attribute__((__noreturn__)) static void usage_cherrypick(void); | |||
106 | __dead__attribute__((__noreturn__)) static void usage_backout(void); | |||
107 | __dead__attribute__((__noreturn__)) static void usage_rebase(void); | |||
108 | __dead__attribute__((__noreturn__)) static void usage_histedit(void); | |||
109 | __dead__attribute__((__noreturn__)) static void usage_integrate(void); | |||
110 | __dead__attribute__((__noreturn__)) static void usage_stage(void); | |||
111 | __dead__attribute__((__noreturn__)) static void usage_unstage(void); | |||
112 | __dead__attribute__((__noreturn__)) static void usage_cat(void); | |||
113 | __dead__attribute__((__noreturn__)) static void usage_info(void); | |||
114 | ||||
115 | static const struct got_error* cmd_init(int, char *[]); | |||
116 | static const struct got_error* cmd_import(int, char *[]); | |||
117 | static const struct got_error* cmd_clone(int, char *[]); | |||
118 | static const struct got_error* cmd_fetch(int, char *[]); | |||
119 | static const struct got_error* cmd_checkout(int, char *[]); | |||
120 | static const struct got_error* cmd_update(int, char *[]); | |||
121 | static const struct got_error* cmd_log(int, char *[]); | |||
122 | static const struct got_error* cmd_diff(int, char *[]); | |||
123 | static const struct got_error* cmd_blame(int, char *[]); | |||
124 | static const struct got_error* cmd_tree(int, char *[]); | |||
125 | static const struct got_error* cmd_status(int, char *[]); | |||
126 | static const struct got_error* cmd_ref(int, char *[]); | |||
127 | static const struct got_error* cmd_branch(int, char *[]); | |||
128 | static const struct got_error* cmd_tag(int, char *[]); | |||
129 | static const struct got_error* cmd_add(int, char *[]); | |||
130 | static const struct got_error* cmd_remove(int, char *[]); | |||
131 | static const struct got_error* cmd_revert(int, char *[]); | |||
132 | static const struct got_error* cmd_commit(int, char *[]); | |||
133 | static const struct got_error* cmd_cherrypick(int, char *[]); | |||
134 | static const struct got_error* cmd_backout(int, char *[]); | |||
135 | static const struct got_error* cmd_rebase(int, char *[]); | |||
136 | static const struct got_error* cmd_histedit(int, char *[]); | |||
137 | static const struct got_error* cmd_integrate(int, char *[]); | |||
138 | static const struct got_error* cmd_stage(int, char *[]); | |||
139 | static const struct got_error* cmd_unstage(int, char *[]); | |||
140 | static const struct got_error* cmd_cat(int, char *[]); | |||
141 | static const struct got_error* cmd_info(int, char *[]); | |||
142 | ||||
143 | static struct got_cmd got_commands[] = { | |||
144 | { "init", cmd_init, usage_init, "" }, | |||
145 | { "import", cmd_import, usage_import, "im" }, | |||
146 | { "clone", cmd_clone, usage_clone, "cl" }, | |||
147 | { "fetch", cmd_fetch, usage_fetch, "fe" }, | |||
148 | { "checkout", cmd_checkout, usage_checkout, "co" }, | |||
149 | { "update", cmd_update, usage_update, "up" }, | |||
150 | { "log", cmd_log, usage_log, "" }, | |||
151 | { "diff", cmd_diff, usage_diff, "di" }, | |||
152 | { "blame", cmd_blame, usage_blame, "bl" }, | |||
153 | { "tree", cmd_tree, usage_tree, "tr" }, | |||
154 | { "status", cmd_status, usage_status, "st" }, | |||
155 | { "ref", cmd_ref, usage_ref, "" }, | |||
156 | { "branch", cmd_branch, usage_branch, "br" }, | |||
157 | { "tag", cmd_tag, usage_tag, "" }, | |||
158 | { "add", cmd_add, usage_add, "" }, | |||
159 | { "remove", cmd_remove, usage_remove, "rm" }, | |||
160 | { "revert", cmd_revert, usage_revert, "rv" }, | |||
161 | { "commit", cmd_commit, usage_commit, "ci" }, | |||
162 | { "cherrypick", cmd_cherrypick, usage_cherrypick, "cy" }, | |||
163 | { "backout", cmd_backout, usage_backout, "bo" }, | |||
164 | { "rebase", cmd_rebase, usage_rebase, "rb" }, | |||
165 | { "histedit", cmd_histedit, usage_histedit, "he" }, | |||
166 | { "integrate", cmd_integrate, usage_integrate,"ig" }, | |||
167 | { "stage", cmd_stage, usage_stage, "sg" }, | |||
168 | { "unstage", cmd_unstage, usage_unstage, "ug" }, | |||
169 | { "cat", cmd_cat, usage_cat, "" }, | |||
170 | { "info", cmd_info, usage_info, "" }, | |||
171 | }; | |||
172 | ||||
173 | static void | |||
174 | list_commands(FILE *fp) | |||
175 | { | |||
176 | size_t i; | |||
177 | ||||
178 | fprintf(fp, "commands:"); | |||
179 | for (i = 0; i < nitems(got_commands)(sizeof((got_commands)) / sizeof((got_commands)[0])); i++) { | |||
180 | struct got_cmd *cmd = &got_commands[i]; | |||
181 | fprintf(fp, " %s", cmd->cmd_name); | |||
182 | } | |||
183 | fputc('\n', fp); | |||
184 | } | |||
185 | ||||
186 | __dead__attribute__((__noreturn__)) static void | |||
187 | option_conflict(char a, char b) | |||
188 | { | |||
189 | errx(1, "-%c and -%c options are mutually exclusive", a, b); | |||
190 | } | |||
191 | ||||
192 | int | |||
193 | main(int argc, char *argv[]) | |||
194 | { | |||
195 | struct got_cmd *cmd; | |||
196 | size_t i; | |||
197 | int ch; | |||
198 | int hflag = 0, Vflag = 0; | |||
199 | static struct option longopts[] = { | |||
200 | { "version", no_argument0, NULL((void *)0), 'V' }, | |||
201 | { NULL((void *)0), 0, NULL((void *)0), 0 } | |||
202 | }; | |||
203 | ||||
204 | setlocale(LC_CTYPE2, ""); | |||
205 | ||||
206 | while ((ch = getopt_long(argc, argv, "+hV", longopts, NULL((void *)0))) != -1) { | |||
207 | switch (ch) { | |||
208 | case 'h': | |||
209 | hflag = 1; | |||
210 | break; | |||
211 | case 'V': | |||
212 | Vflag = 1; | |||
213 | break; | |||
214 | default: | |||
215 | usage(hflag, 1); | |||
216 | /* NOTREACHED */ | |||
217 | } | |||
218 | } | |||
219 | ||||
220 | argc -= optind; | |||
221 | argv += optind; | |||
222 | optind = 1; | |||
223 | optreset = 1; | |||
224 | ||||
225 | if (Vflag) { | |||
226 | got_version_print_str(); | |||
227 | return 0; | |||
228 | } | |||
229 | ||||
230 | if (argc <= 0) | |||
231 | usage(hflag, hflag ? 0 : 1); | |||
232 | ||||
233 | signal(SIGINT2, catch_sigint); | |||
234 | signal(SIGPIPE13, catch_sigpipe); | |||
235 | ||||
236 | for (i = 0; i < nitems(got_commands)(sizeof((got_commands)) / sizeof((got_commands)[0])); i++) { | |||
237 | const struct got_error *error; | |||
238 | ||||
239 | cmd = &got_commands[i]; | |||
240 | ||||
241 | if (strcmp(cmd->cmd_name, argv[0]) != 0 && | |||
242 | strcmp(cmd->cmd_alias, argv[0]) != 0) | |||
243 | continue; | |||
244 | ||||
245 | if (hflag) | |||
246 | got_commands[i].cmd_usage(); | |||
247 | ||||
248 | error = got_commands[i].cmd_main(argc, argv); | |||
249 | if (error && error->code != GOT_ERR_CANCELLED49 && | |||
250 | error->code != GOT_ERR_PRIVSEP_EXIT41 && | |||
251 | !(sigpipe_received && | |||
252 | error->code == GOT_ERR_ERRNO1 && errno(*__errno()) == EPIPE32) && | |||
253 | !(sigint_received && | |||
254 | error->code == GOT_ERR_ERRNO1 && errno(*__errno()) == EINTR4)) { | |||
255 | fprintf(stderr(&__sF[2]), "%s: %s\n", getprogname(), error->msg); | |||
256 | return 1; | |||
257 | } | |||
258 | ||||
259 | return 0; | |||
260 | } | |||
261 | ||||
262 | fprintf(stderr(&__sF[2]), "%s: unknown command '%s'\n", getprogname(), argv[0]); | |||
263 | list_commands(stderr(&__sF[2])); | |||
264 | return 1; | |||
265 | } | |||
266 | ||||
267 | __dead__attribute__((__noreturn__)) static void | |||
268 | usage(int hflag, int status) | |||
269 | { | |||
270 | FILE *fp = (status == 0) ? stdout(&__sF[1]) : stderr(&__sF[2]); | |||
271 | ||||
272 | fprintf(fp, "usage: %s [-h] [-V | --version] command [arg ...]\n", | |||
273 | getprogname()); | |||
274 | if (hflag) | |||
275 | list_commands(fp); | |||
276 | exit(status); | |||
277 | } | |||
278 | ||||
279 | static const struct got_error * | |||
280 | get_editor(char **abspath) | |||
281 | { | |||
282 | const struct got_error *err = NULL((void *)0); | |||
283 | const char *editor; | |||
284 | ||||
285 | *abspath = NULL((void *)0); | |||
286 | ||||
287 | editor = getenv("VISUAL"); | |||
288 | if (editor == NULL((void *)0)) | |||
289 | editor = getenv("EDITOR"); | |||
290 | ||||
291 | if (editor) { | |||
292 | err = got_path_find_prog(abspath, editor); | |||
293 | if (err) | |||
294 | return err; | |||
295 | } | |||
296 | ||||
297 | if (*abspath == NULL((void *)0)) { | |||
298 | *abspath = strdup("/bin/ed"); | |||
299 | if (*abspath == NULL((void *)0)) | |||
300 | return got_error_from_errno("strdup"); | |||
301 | } | |||
302 | ||||
303 | return NULL((void *)0); | |||
304 | } | |||
305 | ||||
306 | static const struct got_error * | |||
307 | apply_unveil(const char *repo_path, int repo_read_only, | |||
308 | const char *worktree_path) | |||
309 | { | |||
310 | const struct got_error *err; | |||
311 | ||||
312 | #ifdef PROFILE | |||
313 | if (unveil("gmon.out", "rwc") != 0) | |||
314 | return got_error_from_errno2("unveil", "gmon.out"); | |||
315 | #endif | |||
316 | if (repo_path && unveil(repo_path, repo_read_only ? "r" : "rwc") != 0) | |||
317 | return got_error_from_errno2("unveil", repo_path); | |||
318 | ||||
319 | if (worktree_path && unveil(worktree_path, "rwc") != 0) | |||
320 | return got_error_from_errno2("unveil", worktree_path); | |||
321 | ||||
322 | if (unveil(GOT_TMPDIR_STR"/tmp", "rwc") != 0) | |||
323 | return got_error_from_errno2("unveil", GOT_TMPDIR_STR"/tmp"); | |||
324 | ||||
325 | err = got_privsep_unveil_exec_helpers(); | |||
326 | if (err != NULL((void *)0)) | |||
327 | return err; | |||
328 | ||||
329 | if (unveil(NULL((void *)0), NULL((void *)0)) != 0) | |||
330 | return got_error_from_errno("unveil"); | |||
331 | ||||
332 | return NULL((void *)0); | |||
333 | } | |||
334 | ||||
335 | __dead__attribute__((__noreturn__)) static void | |||
336 | usage_init(void) | |||
337 | { | |||
338 | fprintf(stderr(&__sF[2]), "usage: %s init repository-path\n", getprogname()); | |||
339 | exit(1); | |||
340 | } | |||
341 | ||||
342 | static const struct got_error * | |||
343 | cmd_init(int argc, char *argv[]) | |||
344 | { | |||
345 | const struct got_error *error = NULL((void *)0); | |||
346 | char *repo_path = NULL((void *)0); | |||
347 | int ch; | |||
348 | ||||
349 | while ((ch = getopt(argc, argv, "")) != -1) { | |||
350 | switch (ch) { | |||
351 | default: | |||
352 | usage_init(); | |||
353 | /* NOTREACHED */ | |||
354 | } | |||
355 | } | |||
356 | ||||
357 | argc -= optind; | |||
358 | argv += optind; | |||
359 | ||||
360 | #ifndef PROFILE | |||
361 | if (pledge("stdio rpath wpath cpath unveil", NULL((void *)0)) == -1) | |||
362 | err(1, "pledge"); | |||
363 | #endif | |||
364 | if (argc != 1) | |||
365 | usage_init(); | |||
366 | ||||
367 | repo_path = strdup(argv[0]); | |||
368 | if (repo_path == NULL((void *)0)) | |||
369 | return got_error_from_errno("strdup"); | |||
370 | ||||
371 | got_path_strip_trailing_slashes(repo_path); | |||
372 | ||||
373 | error = got_path_mkdir(repo_path); | |||
374 | if (error && | |||
375 | !(error->code == GOT_ERR_ERRNO1 && errno(*__errno()) == EEXIST17)) | |||
376 | goto done; | |||
377 | ||||
378 | error = apply_unveil(repo_path, 0, NULL((void *)0)); | |||
379 | if (error) | |||
380 | goto done; | |||
381 | ||||
382 | error = got_repo_init(repo_path); | |||
383 | done: | |||
384 | free(repo_path); | |||
385 | return error; | |||
386 | } | |||
387 | ||||
388 | __dead__attribute__((__noreturn__)) static void | |||
389 | usage_import(void) | |||
390 | { | |||
391 | fprintf(stderr(&__sF[2]), "usage: %s import [-b branch] [-m message] " | |||
392 | "[-r repository-path] [-I pattern] path\n", getprogname()); | |||
393 | exit(1); | |||
394 | } | |||
395 | ||||
396 | int | |||
397 | spawn_editor(const char *editor, const char *file) | |||
398 | { | |||
399 | pid_t pid; | |||
400 | sig_t sighup, sigint, sigquit; | |||
401 | int st = -1; | |||
402 | ||||
403 | sighup = signal(SIGHUP1, SIG_IGN(void (*)(int))1); | |||
404 | sigint = signal(SIGINT2, SIG_IGN(void (*)(int))1); | |||
405 | sigquit = signal(SIGQUIT3, SIG_IGN(void (*)(int))1); | |||
406 | ||||
407 | switch (pid = fork()) { | |||
408 | case -1: | |||
409 | goto doneediting; | |||
410 | case 0: | |||
411 | execl(editor, editor, file, (char *)NULL((void *)0)); | |||
412 | _exit(127); | |||
413 | } | |||
414 | ||||
415 | while (waitpid(pid, &st, 0) == -1) | |||
416 | if (errno(*__errno()) != EINTR4) | |||
417 | break; | |||
418 | ||||
419 | doneediting: | |||
420 | (void)signal(SIGHUP1, sighup); | |||
421 | (void)signal(SIGINT2, sigint); | |||
422 | (void)signal(SIGQUIT3, sigquit); | |||
423 | ||||
424 | if (!WIFEXITED(st)(((st) & 0177) == 0)) { | |||
425 | errno(*__errno()) = EINTR4; | |||
426 | return -1; | |||
427 | } | |||
428 | ||||
429 | return WEXITSTATUS(st)(int)(((unsigned)(st) >> 8) & 0xff); | |||
430 | } | |||
431 | ||||
432 | static const struct got_error * | |||
433 | edit_logmsg(char **logmsg, const char *editor, const char *logmsg_path, | |||
434 | const char *initial_content, size_t initial_content_len, | |||
435 | int require_modification) | |||
436 | { | |||
437 | const struct got_error *err = NULL((void *)0); | |||
438 | char *line = NULL((void *)0); | |||
439 | size_t linesize = 0; | |||
440 | ssize_t linelen; | |||
441 | struct stat st, st2; | |||
442 | FILE *fp = NULL((void *)0); | |||
443 | size_t len, logmsg_len; | |||
444 | char *initial_content_stripped = NULL((void *)0), *buf = NULL((void *)0), *s; | |||
445 | ||||
446 | *logmsg = NULL((void *)0); | |||
447 | ||||
448 | if (stat(logmsg_path, &st) == -1) | |||
449 | return got_error_from_errno2("stat", logmsg_path); | |||
450 | ||||
451 | if (spawn_editor(editor, logmsg_path) == -1) | |||
452 | return got_error_from_errno("failed spawning editor"); | |||
453 | ||||
454 | if (stat(logmsg_path, &st2) == -1) | |||
455 | return got_error_from_errno("stat"); | |||
456 | ||||
457 | if (require_modification && | |||
458 | st.st_mtimest_mtim.tv_sec == st2.st_mtimest_mtim.tv_sec && st.st_size == st2.st_size) | |||
459 | return got_error_msg(GOT_ERR_COMMIT_MSG_EMPTY74, | |||
460 | "no changes made to commit message, aborting"); | |||
461 | ||||
462 | /* | |||
463 | * Set up a stripped version of the initial content without comments | |||
464 | * and blank lines. We need this in order to check if the message | |||
465 | * has in fact been edited. | |||
466 | */ | |||
467 | initial_content_stripped = malloc(initial_content_len + 1); | |||
468 | if (initial_content_stripped == NULL((void *)0)) | |||
469 | return got_error_from_errno("malloc"); | |||
470 | initial_content_stripped[0] = '\0'; | |||
471 | ||||
472 | buf = strdup(initial_content); | |||
473 | if (buf == NULL((void *)0)) { | |||
474 | err = got_error_from_errno("strdup"); | |||
475 | goto done; | |||
476 | } | |||
477 | s = buf; | |||
478 | len = 0; | |||
479 | while ((line = strsep(&s, "\n")) != NULL((void *)0)) { | |||
480 | if ((line[0] == '#' || (len == 0 && line[0] == '\n'))) | |||
481 | continue; /* remove comments and leading empty lines */ | |||
482 | len = strlcat(initial_content_stripped, line, | |||
483 | initial_content_len + 1); | |||
484 | if (len >= initial_content_len + 1) { | |||
485 | err = got_error(GOT_ERR_NO_SPACE9); | |||
486 | goto done; | |||
487 | } | |||
488 | } | |||
489 | while (len > 0 && initial_content_stripped[len - 1] == '\n') { | |||
490 | initial_content_stripped[len - 1] = '\0'; | |||
491 | len--; | |||
492 | } | |||
493 | ||||
494 | logmsg_len = st2.st_size; | |||
495 | *logmsg = malloc(logmsg_len + 1); | |||
496 | if (*logmsg == NULL((void *)0)) | |||
497 | return got_error_from_errno("malloc"); | |||
498 | (*logmsg)[0] = '\0'; | |||
499 | ||||
500 | fp = fopen(logmsg_path, "r"); | |||
501 | if (fp == NULL((void *)0)) { | |||
502 | err = got_error_from_errno("fopen"); | |||
503 | goto done; | |||
504 | } | |||
505 | ||||
506 | len = 0; | |||
507 | while ((linelen = getline(&line, &linesize, fp)) != -1) { | |||
508 | if ((line[0] == '#' || (len == 0 && line[0] == '\n'))) | |||
509 | continue; /* remove comments and leading empty lines */ | |||
510 | len = strlcat(*logmsg, line, logmsg_len + 1); | |||
511 | if (len >= logmsg_len + 1) { | |||
512 | err = got_error(GOT_ERR_NO_SPACE9); | |||
513 | goto done; | |||
514 | } | |||
515 | } | |||
516 | free(line); | |||
517 | if (ferror(fp)(!__isthreaded ? (((fp)->_flags & 0x0040) != 0) : (ferror )(fp))) { | |||
518 | err = got_ferror(fp, GOT_ERR_IO6); | |||
519 | goto done; | |||
520 | } | |||
521 | while (len > 0 && (*logmsg)[len - 1] == '\n') { | |||
522 | (*logmsg)[len - 1] = '\0'; | |||
523 | len--; | |||
524 | } | |||
525 | ||||
526 | if (len == 0) { | |||
527 | err = got_error_msg(GOT_ERR_COMMIT_MSG_EMPTY74, | |||
528 | "commit message cannot be empty, aborting"); | |||
529 | goto done; | |||
530 | } | |||
531 | if (require_modification && | |||
532 | strcmp(*logmsg, initial_content_stripped) == 0) | |||
533 | err = got_error_msg(GOT_ERR_COMMIT_MSG_EMPTY74, | |||
534 | "no changes made to commit message, aborting"); | |||
535 | done: | |||
536 | free(initial_content_stripped); | |||
537 | free(buf); | |||
538 | if (fp && fclose(fp) == EOF(-1) && err == NULL((void *)0)) | |||
539 | err = got_error_from_errno("fclose"); | |||
540 | if (err) { | |||
541 | free(*logmsg); | |||
542 | *logmsg = NULL((void *)0); | |||
543 | } | |||
544 | return err; | |||
545 | } | |||
546 | ||||
547 | static const struct got_error * | |||
548 | collect_import_msg(char **logmsg, char **logmsg_path, const char *editor, | |||
549 | const char *path_dir, const char *branch_name) | |||
550 | { | |||
551 | char *initial_content = NULL((void *)0); | |||
552 | const struct got_error *err = NULL((void *)0); | |||
553 | int initial_content_len; | |||
554 | int fd = -1; | |||
555 | ||||
556 | initial_content_len = asprintf(&initial_content, | |||
557 | "\n# %s to be imported to branch %s\n", path_dir, | |||
558 | branch_name); | |||
559 | if (initial_content_len == -1) | |||
560 | return got_error_from_errno("asprintf"); | |||
561 | ||||
562 | err = got_opentemp_named_fd(logmsg_path, &fd, | |||
563 | GOT_TMPDIR_STR"/tmp" "/got-importmsg"); | |||
564 | if (err) | |||
565 | goto done; | |||
566 | ||||
567 | if (write(fd, initial_content, initial_content_len) == -1) { | |||
568 | err = got_error_from_errno2("write", *logmsg_path); | |||
569 | goto done; | |||
570 | } | |||
571 | ||||
572 | err = edit_logmsg(logmsg, editor, *logmsg_path, initial_content, | |||
573 | initial_content_len, 1); | |||
574 | done: | |||
575 | if (fd != -1 && close(fd) == -1 && err == NULL((void *)0)) | |||
576 | err = got_error_from_errno2("close", *logmsg_path); | |||
577 | free(initial_content); | |||
578 | if (err) { | |||
579 | free(*logmsg_path); | |||
580 | *logmsg_path = NULL((void *)0); | |||
581 | } | |||
582 | return err; | |||
583 | } | |||
584 | ||||
585 | static const struct got_error * | |||
586 | import_progress(void *arg, const char *path) | |||
587 | { | |||
588 | printf("A %s\n", path); | |||
589 | return NULL((void *)0); | |||
590 | } | |||
591 | ||||
592 | static const struct got_error * | |||
593 | get_author(char **author, struct got_repository *repo, | |||
594 | struct got_worktree *worktree) | |||
595 | { | |||
596 | const struct got_error *err = NULL((void *)0); | |||
597 | const char *got_author = NULL((void *)0), *name, *email; | |||
598 | const struct got_gotconfig *worktree_conf = NULL((void *)0), *repo_conf = NULL((void *)0); | |||
599 | ||||
600 | *author = NULL((void *)0); | |||
601 | ||||
602 | if (worktree) | |||
603 | worktree_conf = got_worktree_get_gotconfig(worktree); | |||
604 | repo_conf = got_repo_get_gotconfig(repo); | |||
605 | ||||
606 | /* | |||
607 | * Priority of potential author information sources, from most | |||
608 | * significant to least significant: | |||
609 | * 1) work tree's .got/got.conf file | |||
610 | * 2) repository's got.conf file | |||
611 | * 3) repository's git config file | |||
612 | * 4) environment variables | |||
613 | * 5) global git config files (in user's home directory or /etc) | |||
614 | */ | |||
615 | ||||
616 | if (worktree_conf) | |||
617 | got_author = got_gotconfig_get_author(worktree_conf); | |||
618 | if (got_author == NULL((void *)0)) | |||
619 | got_author = got_gotconfig_get_author(repo_conf); | |||
620 | if (got_author == NULL((void *)0)) { | |||
621 | name = got_repo_get_gitconfig_author_name(repo); | |||
622 | email = got_repo_get_gitconfig_author_email(repo); | |||
623 | if (name && email) { | |||
624 | if (asprintf(author, "%s <%s>", name, email) == -1) | |||
625 | return got_error_from_errno("asprintf"); | |||
626 | return NULL((void *)0); | |||
627 | } | |||
628 | ||||
629 | got_author = getenv("GOT_AUTHOR"); | |||
630 | if (got_author == NULL((void *)0)) { | |||
631 | name = got_repo_get_global_gitconfig_author_name(repo); | |||
632 | email = got_repo_get_global_gitconfig_author_email( | |||
633 | repo); | |||
634 | if (name && email) { | |||
635 | if (asprintf(author, "%s <%s>", name, email) | |||
636 | == -1) | |||
637 | return got_error_from_errno("asprintf"); | |||
638 | return NULL((void *)0); | |||
639 | } | |||
640 | /* TODO: Look up user in password database? */ | |||
641 | return got_error(GOT_ERR_COMMIT_NO_AUTHOR71); | |||
642 | } | |||
643 | } | |||
644 | ||||
645 | *author = strdup(got_author); | |||
646 | if (*author == NULL((void *)0)) | |||
647 | return got_error_from_errno("strdup"); | |||
648 | ||||
649 | /* | |||
650 | * Really dumb email address check; we're only doing this to | |||
651 | * avoid git's object parser breaking on commits we create. | |||
652 | */ | |||
653 | while (*got_author && *got_author != '<') | |||
654 | got_author++; | |||
655 | if (*got_author != '<') { | |||
656 | err = got_error(GOT_ERR_COMMIT_NO_EMAIL108); | |||
657 | goto done; | |||
658 | } | |||
659 | while (*got_author && *got_author != '@') | |||
660 | got_author++; | |||
661 | if (*got_author != '@') { | |||
662 | err = got_error(GOT_ERR_COMMIT_NO_EMAIL108); | |||
663 | goto done; | |||
664 | } | |||
665 | while (*got_author && *got_author != '>') | |||
666 | got_author++; | |||
667 | if (*got_author != '>') | |||
668 | err = got_error(GOT_ERR_COMMIT_NO_EMAIL108); | |||
669 | done: | |||
670 | if (err) { | |||
671 | free(*author); | |||
672 | *author = NULL((void *)0); | |||
673 | } | |||
674 | return err; | |||
675 | } | |||
676 | ||||
677 | static const struct got_error * | |||
678 | get_gitconfig_path(char **gitconfig_path) | |||
679 | { | |||
680 | const char *homedir = getenv("HOME"); | |||
681 | ||||
682 | *gitconfig_path = NULL((void *)0); | |||
683 | if (homedir) { | |||
684 | if (asprintf(gitconfig_path, "%s/.gitconfig", homedir) == -1) | |||
685 | return got_error_from_errno("asprintf"); | |||
686 | ||||
687 | } | |||
688 | return NULL((void *)0); | |||
689 | } | |||
690 | ||||
691 | static const struct got_error * | |||
692 | cmd_import(int argc, char *argv[]) | |||
693 | { | |||
694 | const struct got_error *error = NULL((void *)0); | |||
695 | char *path_dir = NULL((void *)0), *repo_path = NULL((void *)0), *logmsg = NULL((void *)0); | |||
696 | char *gitconfig_path = NULL((void *)0), *editor = NULL((void *)0), *author = NULL((void *)0); | |||
697 | const char *branch_name = "main"; | |||
698 | char *refname = NULL((void *)0), *id_str = NULL((void *)0), *logmsg_path = NULL((void *)0); | |||
699 | struct got_repository *repo = NULL((void *)0); | |||
700 | struct got_reference *branch_ref = NULL((void *)0), *head_ref = NULL((void *)0); | |||
701 | struct got_object_id *new_commit_id = NULL((void *)0); | |||
702 | int ch; | |||
703 | struct got_pathlist_head ignores; | |||
704 | struct got_pathlist_entry *pe; | |||
705 | int preserve_logmsg = 0; | |||
706 | ||||
707 | TAILQ_INIT(&ignores)do { (&ignores)->tqh_first = ((void *)0); (&ignores )->tqh_last = &(&ignores)->tqh_first; } while ( 0); | |||
| ||||
708 | ||||
709 | while ((ch = getopt(argc, argv, "b:m:r:I:")) != -1) { | |||
710 | switch (ch) { | |||
711 | case 'b': | |||
712 | branch_name = optarg; | |||
713 | break; | |||
714 | case 'm': | |||
715 | logmsg = strdup(optarg); | |||
716 | if (logmsg == NULL((void *)0)) { | |||
717 | error = got_error_from_errno("strdup"); | |||
| ||||
718 | goto done; | |||
719 | } | |||
720 | break; | |||
721 | case 'r': | |||
722 | repo_path = realpath(optarg, NULL((void *)0)); | |||
723 | if (repo_path == NULL((void *)0)) { | |||
724 | error = got_error_from_errno2("realpath", | |||
725 | optarg); | |||
726 | goto done; | |||
727 | } | |||
728 | break; | |||
729 | case 'I': | |||
730 | if (optarg[0] == '\0') | |||
731 | break; | |||
732 | error = got_pathlist_insert(&pe, &ignores, optarg, | |||
733 | NULL((void *)0)); | |||
734 | if (error) | |||
735 | goto done; | |||
736 | break; | |||
737 | default: | |||
738 | usage_import(); | |||
739 | /* NOTREACHED */ | |||
740 | } | |||
741 | } | |||
742 | ||||
743 | argc -= optind; | |||
744 | argv += optind; | |||
745 | ||||
746 | #ifndef PROFILE | |||
747 | if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd " | |||
748 | "unveil", | |||
749 | NULL((void *)0)) == -1) | |||
750 | err(1, "pledge"); | |||
751 | #endif | |||
752 | if (argc != 1) | |||
753 | usage_import(); | |||
754 | ||||
755 | if (repo_path == NULL((void *)0)) { | |||
756 | repo_path = getcwd(NULL((void *)0), 0); | |||
757 | if (repo_path == NULL((void *)0)) | |||
758 | return got_error_from_errno("getcwd"); | |||
759 | } | |||
760 | got_path_strip_trailing_slashes(repo_path); | |||
761 | error = get_gitconfig_path(&gitconfig_path); | |||
762 | if (error) | |||
763 | goto done; | |||
764 | error = got_repo_open(&repo, repo_path, gitconfig_path); | |||
765 | if (error) | |||
766 | goto done; | |||
767 | ||||
768 | error = get_author(&author, repo, NULL((void *)0)); | |||
769 | if (error) | |||
770 | return error; | |||
771 | ||||
772 | /* | |||
773 | * Don't let the user create a branch name with a leading '-'. | |||
774 | * While technically a valid reference name, this case is usually | |||
775 | * an unintended typo. | |||
776 | */ | |||
777 | if (branch_name[0] == '-') | |||
778 | return got_error_path(branch_name, GOT_ERR_REF_NAME_MINUS113); | |||
779 | ||||
780 | if (asprintf(&refname, "refs/heads/%s", branch_name) == -1) { | |||
781 | error = got_error_from_errno("asprintf"); | |||
782 | goto done; | |||
783 | } | |||
784 | ||||
785 | error = got_ref_open(&branch_ref, repo, refname, 0); | |||
786 | if (error) { | |||
787 | if (error->code != GOT_ERR_NOT_REF5) | |||
788 | goto done; | |||
789 | } else { | |||
790 | error = got_error_msg(GOT_ERR_BRANCH_EXISTS83, | |||
791 | "import target branch already exists"); | |||
792 | goto done; | |||
793 | } | |||
794 | ||||
795 | path_dir = realpath(argv[0], NULL((void *)0)); | |||
796 | if (path_dir == NULL((void *)0)) { | |||
797 | error = got_error_from_errno2("realpath", argv[0]); | |||
798 | goto done; | |||
799 | } | |||
800 | got_path_strip_trailing_slashes(path_dir); | |||
801 | ||||
802 | /* | |||
803 | * unveil(2) traverses exec(2); if an editor is used we have | |||
804 | * to apply unveil after the log message has been written. | |||
805 | */ | |||
806 | if (logmsg == NULL((void *)0) || strlen(logmsg) == 0) { | |||
807 | error = get_editor(&editor); | |||
808 | if (error) | |||
809 | goto done; | |||
810 | free(logmsg); | |||
811 | error = collect_import_msg(&logmsg, &logmsg_path, editor, | |||
812 | path_dir, refname); | |||
813 | if (error) { | |||
814 | if (error->code != GOT_ERR_COMMIT_MSG_EMPTY74 && | |||
815 | logmsg_path != NULL((void *)0)) | |||
816 | preserve_logmsg = 1; | |||
817 | goto done; | |||
818 | } | |||
819 | } | |||
820 | ||||
821 | if (unveil(path_dir, "r") != 0) { | |||
822 | error = got_error_from_errno2("unveil", path_dir); | |||
823 | if (logmsg_path) | |||
824 | preserve_logmsg = 1; | |||
825 | goto done; | |||
826 | } | |||
827 | ||||
828 | error = apply_unveil(got_repo_get_path(repo), 0, NULL((void *)0)); | |||
829 | if (error) { | |||
830 | if (logmsg_path) | |||
831 | preserve_logmsg = 1; | |||
832 | goto done; | |||
833 | } | |||
834 | ||||
835 | error = got_repo_import(&new_commit_id, path_dir, logmsg, | |||
836 | author, &ignores, repo, import_progress, NULL((void *)0)); | |||
837 | if (error) { | |||
838 | if (logmsg_path) | |||
839 | preserve_logmsg = 1; | |||
840 | goto done; | |||
841 | } | |||
842 | ||||
843 | error = got_ref_alloc(&branch_ref, refname, new_commit_id); | |||
844 | if (error) { | |||
845 | if (logmsg_path) | |||
846 | preserve_logmsg = 1; | |||
847 | goto done; | |||
848 | } | |||
849 | ||||
850 | error = got_ref_write(branch_ref, repo); | |||
851 | if (error) { | |||
852 | if (logmsg_path) | |||
853 | preserve_logmsg = 1; | |||
854 | goto done; | |||
855 | } | |||
856 | ||||
857 | error = got_object_id_str(&id_str, new_commit_id); | |||
858 | if (error) { | |||
859 | if (logmsg_path) | |||
860 | preserve_logmsg = 1; | |||
861 | goto done; | |||
862 | } | |||
863 | ||||
864 | error = got_ref_open(&head_ref, repo, GOT_REF_HEAD"HEAD", 0); | |||
865 | if (error) { | |||
866 | if (error->code != GOT_ERR_NOT_REF5) { | |||
867 | if (logmsg_path) | |||
868 | preserve_logmsg = 1; | |||
869 | goto done; | |||
870 | } | |||
871 | ||||
872 | error = got_ref_alloc_symref(&head_ref, GOT_REF_HEAD"HEAD", | |||
873 | branch_ref); | |||
874 | if (error) { | |||
875 | if (logmsg_path) | |||
876 | preserve_logmsg = 1; | |||
877 | goto done; | |||
878 | } | |||
879 | ||||
880 | error = got_ref_write(head_ref, repo); | |||
881 | if (error) { | |||
882 | if (logmsg_path) | |||
883 | preserve_logmsg = 1; | |||
884 | goto done; | |||
885 | } | |||
886 | } | |||
887 | ||||
888 | printf("Created branch %s with commit %s\n", | |||
889 | got_ref_get_name(branch_ref), id_str); | |||
890 | done: | |||
891 | if (preserve_logmsg) { | |||
892 | fprintf(stderr(&__sF[2]), "%s: log message preserved in %s\n", | |||
893 | getprogname(), logmsg_path); | |||
894 | } else if (logmsg_path && unlink(logmsg_path) == -1 && error == NULL((void *)0)) | |||
895 | error = got_error_from_errno2("unlink", logmsg_path); | |||
896 | free(logmsg); | |||
897 | free(logmsg_path); | |||
898 | free(repo_path); | |||
899 | free(editor); | |||
900 | free(refname); | |||
901 | free(new_commit_id); | |||
902 | free(id_str); | |||
903 | free(author); | |||
904 | free(gitconfig_path); | |||
905 | if (branch_ref) | |||
906 | got_ref_close(branch_ref); | |||
907 | if (head_ref) | |||
908 | got_ref_close(head_ref); | |||
909 | return error; | |||
910 | } | |||
911 | ||||
912 | __dead__attribute__((__noreturn__)) static void | |||
913 | usage_clone(void) | |||
914 | { | |||
915 | fprintf(stderr(&__sF[2]), "usage: %s clone [-a] [-b branch] [-l] [-m] [-q] [-v] " | |||
916 | "[-R reference] repository-url [directory]\n", getprogname()); | |||
917 | exit(1); | |||
918 | } | |||
919 | ||||
920 | struct got_fetch_progress_arg { | |||
921 | char last_scaled_size[FMT_SCALED_STRSIZE7]; | |||
922 | int last_p_indexed; | |||
923 | int last_p_resolved; | |||
924 | int verbosity; | |||
925 | ||||
926 | struct got_repository *repo; | |||
927 | ||||
928 | int create_configs; | |||
929 | int configs_created; | |||
930 | struct { | |||
931 | struct got_pathlist_head *symrefs; | |||
932 | struct got_pathlist_head *wanted_branches; | |||
933 | struct got_pathlist_head *wanted_refs; | |||
934 | const char *proto; | |||
935 | const char *host; | |||
936 | const char *port; | |||
937 | const char *remote_repo_path; | |||
938 | const char *git_url; | |||
939 | int fetch_all_branches; | |||
940 | int mirror_references; | |||
941 | } config_info; | |||
942 | }; | |||
943 | ||||
944 | /* XXX forward declaration */ | |||
945 | static const struct got_error * | |||
946 | create_config_files(const char *proto, const char *host, const char *port, | |||
947 | const char *remote_repo_path, const char *git_url, int fetch_all_branches, | |||
948 | int mirror_references, struct got_pathlist_head *symrefs, | |||
949 | struct got_pathlist_head *wanted_branches, | |||
950 | struct got_pathlist_head *wanted_refs, struct got_repository *repo); | |||
951 | ||||
952 | static const struct got_error * | |||
953 | fetch_progress(void *arg, const char *message, off_t packfile_size, | |||
954 | int nobj_total, int nobj_indexed, int nobj_loose, int nobj_resolved) | |||
955 | { | |||
956 | const struct got_error *err = NULL((void *)0); | |||
957 | struct got_fetch_progress_arg *a = arg; | |||
958 | char scaled_size[FMT_SCALED_STRSIZE7]; | |||
959 | int p_indexed, p_resolved; | |||
960 | int print_size = 0, print_indexed = 0, print_resolved = 0; | |||
961 | ||||
962 | /* | |||
963 | * In order to allow a failed clone to be resumed with 'got fetch' | |||
964 | * we try to create configuration files as soon as possible. | |||
965 | * Once the server has sent information about its default branch | |||
966 | * we have all required information. | |||
967 | */ | |||
968 | if (a->create_configs && !a->configs_created && | |||
969 | !TAILQ_EMPTY(a->config_info.symrefs)(((a->config_info.symrefs)->tqh_first) == ((void *)0))) { | |||
970 | err = create_config_files(a->config_info.proto, | |||
971 | a->config_info.host, a->config_info.port, | |||
972 | a->config_info.remote_repo_path, | |||
973 | a->config_info.git_url, | |||
974 | a->config_info.fetch_all_branches, | |||
975 | a->config_info.mirror_references, | |||
976 | a->config_info.symrefs, | |||
977 | a->config_info.wanted_branches, | |||
978 | a->config_info.wanted_refs, a->repo); | |||
979 | if (err) | |||
980 | return err; | |||
981 | a->configs_created = 1; | |||
982 | } | |||
983 | ||||
984 | if (a->verbosity < 0) | |||
985 | return NULL((void *)0); | |||
986 | ||||
987 | if (message && message[0] != '\0') { | |||
988 | printf("\rserver: %s", message); | |||
989 | fflush(stdout(&__sF[1])); | |||
990 | return NULL((void *)0); | |||
991 | } | |||
992 | ||||
993 | if (packfile_size > 0 || nobj_indexed > 0) { | |||
994 | if (fmt_scaled(packfile_size, scaled_size) == 0 && | |||
995 | (a->last_scaled_size[0] == '\0' || | |||
996 | strcmp(scaled_size, a->last_scaled_size)) != 0) { | |||
997 | print_size = 1; | |||
998 | if (strlcpy(a->last_scaled_size, scaled_size, | |||
999 | FMT_SCALED_STRSIZE7) >= FMT_SCALED_STRSIZE7) | |||
1000 | return got_error(GOT_ERR_NO_SPACE9); | |||
1001 | } | |||
1002 | if (nobj_indexed > 0) { | |||
1003 | p_indexed = (nobj_indexed * 100) / nobj_total; | |||
1004 | if (p_indexed != a->last_p_indexed) { | |||
1005 | a->last_p_indexed = p_indexed; | |||
1006 | print_indexed = 1; | |||
1007 | print_size = 1; | |||
1008 | } | |||
1009 | } | |||
1010 | if (nobj_resolved > 0) { | |||
1011 | p_resolved = (nobj_resolved * 100) / | |||
1012 | (nobj_total - nobj_loose); | |||
1013 | if (p_resolved != a->last_p_resolved) { | |||
1014 | a->last_p_resolved = p_resolved; | |||
1015 | print_resolved = 1; | |||
1016 | print_indexed = 1; | |||
1017 | print_size = 1; | |||
1018 | } | |||
1019 | } | |||
1020 | ||||
1021 | } | |||
1022 | if (print_size || print_indexed || print_resolved) | |||
1023 | printf("\r"); | |||
1024 | if (print_size) | |||
1025 | printf("%*s fetched", FMT_SCALED_STRSIZE7, scaled_size); | |||
1026 | if (print_indexed) | |||
1027 | printf("; indexing %d%%", p_indexed); | |||
1028 | if (print_resolved) | |||
1029 | printf("; resolving deltas %d%%", p_resolved); | |||
1030 | if (print_size || print_indexed || print_resolved) | |||
1031 | fflush(stdout(&__sF[1])); | |||
1032 | ||||
1033 | return NULL((void *)0); | |||
1034 | } | |||
1035 | ||||
1036 | static const struct got_error * | |||
1037 | create_symref(const char *refname, struct got_reference *target_ref, | |||
1038 | int verbosity, struct got_repository *repo) | |||
1039 | { | |||
1040 | const struct got_error *err; | |||
1041 | struct got_reference *head_symref; | |||
1042 | ||||
1043 | err = got_ref_alloc_symref(&head_symref, refname, target_ref); | |||
1044 | if (err) | |||
1045 | return err; | |||
1046 | ||||
1047 | err = got_ref_write(head_symref, repo); | |||
1048 | if (err == NULL((void *)0) && verbosity > 0) { | |||
1049 | printf("Created reference %s: %s\n", GOT_REF_HEAD"HEAD", | |||
1050 | got_ref_get_name(target_ref)); | |||
1051 | } | |||
1052 | got_ref_close(head_symref); | |||
1053 | return err; | |||
1054 | } | |||
1055 | ||||
1056 | static const struct got_error * | |||
1057 | list_remote_refs(struct got_pathlist_head *symrefs, | |||
1058 | struct got_pathlist_head *refs) | |||
1059 | { | |||
1060 | const struct got_error *err; | |||
1061 | struct got_pathlist_entry *pe; | |||
1062 | ||||
1063 | TAILQ_FOREACH(pe, symrefs, entry)for((pe) = ((symrefs)->tqh_first); (pe) != ((void *)0); (pe ) = ((pe)->entry.tqe_next)) { | |||
1064 | const char *refname = pe->path; | |||
1065 | const char *targetref = pe->data; | |||
1066 | ||||
1067 | printf("%s: %s\n", refname, targetref); | |||
1068 | } | |||
1069 | ||||
1070 | TAILQ_FOREACH(pe, refs, entry)for((pe) = ((refs)->tqh_first); (pe) != ((void *)0); (pe) = ((pe)->entry.tqe_next)) { | |||
1071 | const char *refname = pe->path; | |||
1072 | struct got_object_id *id = pe->data; | |||
1073 | char *id_str; | |||
1074 | ||||
1075 | err = got_object_id_str(&id_str, id); | |||
1076 | if (err) | |||
1077 | return err; | |||
1078 | printf("%s: %s\n", refname, id_str); | |||
1079 | free(id_str); | |||
1080 | } | |||
1081 | ||||
1082 | return NULL((void *)0); | |||
1083 | } | |||
1084 | ||||
1085 | static const struct got_error * | |||
1086 | create_ref(const char *refname, struct got_object_id *id, | |||
1087 | int verbosity, struct got_repository *repo) | |||
1088 | { | |||
1089 | const struct got_error *err = NULL((void *)0); | |||
1090 | struct got_reference *ref; | |||
1091 | char *id_str; | |||
1092 | ||||
1093 | err = got_object_id_str(&id_str, id); | |||
1094 | if (err) | |||
1095 | return err; | |||
1096 | ||||
1097 | err = got_ref_alloc(&ref, refname, id); | |||
1098 | if (err) | |||
1099 | goto done; | |||
1100 | ||||
1101 | err = got_ref_write(ref, repo); | |||
1102 | got_ref_close(ref); | |||
1103 | ||||
1104 | if (err == NULL((void *)0) && verbosity >= 0) | |||
1105 | printf("Created reference %s: %s\n", refname, id_str); | |||
1106 | done: | |||
1107 | free(id_str); | |||
1108 | return err; | |||
1109 | } | |||
1110 | ||||
1111 | static int | |||
1112 | match_wanted_ref(const char *refname, const char *wanted_ref) | |||
1113 | { | |||
1114 | if (strncmp(refname, "refs/", 5) != 0) | |||
1115 | return 0; | |||
1116 | refname += 5; | |||
1117 | ||||
1118 | /* | |||
1119 | * Prevent fetching of references that won't make any | |||
1120 | * sense outside of the remote repository's context. | |||
1121 | */ | |||
1122 | if (strncmp(refname, "got/", 4) == 0) | |||
1123 | return 0; | |||
1124 | if (strncmp(refname, "remotes/", 8) == 0) | |||
1125 | return 0; | |||
1126 | ||||
1127 | if (strncmp(wanted_ref, "refs/", 5) == 0) | |||
1128 | wanted_ref += 5; | |||
1129 | ||||
1130 | /* Allow prefix match. */ | |||
1131 | if (got_path_is_child(refname, wanted_ref, strlen(wanted_ref))) | |||
1132 | return 1; | |||
1133 | ||||
1134 | /* Allow exact match. */ | |||
1135 | return (strcmp(refname, wanted_ref) == 0); | |||
1136 | } | |||
1137 | ||||
1138 | static int | |||
1139 | is_wanted_ref(struct got_pathlist_head *wanted_refs, const char *refname) | |||
1140 | { | |||
1141 | struct got_pathlist_entry *pe; | |||
1142 | ||||
1143 | TAILQ_FOREACH(pe, wanted_refs, entry)for((pe) = ((wanted_refs)->tqh_first); (pe) != ((void *)0) ; (pe) = ((pe)->entry.tqe_next)) { | |||
1144 | if (match_wanted_ref(refname, pe->path)) | |||
1145 | return 1; | |||
1146 | } | |||
1147 | ||||
1148 | return 0; | |||
1149 | } | |||
1150 | ||||
1151 | static const struct got_error * | |||
1152 | create_wanted_ref(const char *refname, struct got_object_id *id, | |||
1153 | const char *remote_repo_name, int verbosity, struct got_repository *repo) | |||
1154 | { | |||
1155 | const struct got_error *err; | |||
1156 | char *remote_refname; | |||
1157 | ||||
1158 | if (strncmp("refs/", refname, 5) == 0) | |||
1159 | refname += 5; | |||
1160 | ||||
1161 | if (asprintf(&remote_refname, "refs/remotes/%s/%s", | |||
1162 | remote_repo_name, refname) == -1) | |||
1163 | return got_error_from_errno("asprintf"); | |||
1164 | ||||
1165 | err = create_ref(remote_refname, id, verbosity, repo); | |||
1166 | free(remote_refname); | |||
1167 | return err; | |||
1168 | } | |||
1169 | ||||
1170 | static const struct got_error * | |||
1171 | create_gotconfig(const char *proto, const char *host, const char *port, | |||
1172 | const char *remote_repo_path, const char *default_branch, | |||
1173 | int fetch_all_branches, struct got_pathlist_head *wanted_branches, | |||
1174 | struct got_pathlist_head *wanted_refs, int mirror_references, | |||
1175 | struct got_repository *repo) | |||
1176 | { | |||
1177 | const struct got_error *err = NULL((void *)0); | |||
1178 | char *gotconfig_path = NULL((void *)0); | |||
1179 | char *gotconfig = NULL((void *)0); | |||
1180 | FILE *gotconfig_file = NULL((void *)0); | |||
1181 | const char *branchname = NULL((void *)0); | |||
1182 | char *branches = NULL((void *)0), *refs = NULL((void *)0); | |||
1183 | ssize_t n; | |||
1184 | ||||
1185 | if (!fetch_all_branches && !TAILQ_EMPTY(wanted_branches)(((wanted_branches)->tqh_first) == ((void *)0))) { | |||
1186 | struct got_pathlist_entry *pe; | |||
1187 | TAILQ_FOREACH(pe, wanted_branches, entry)for((pe) = ((wanted_branches)->tqh_first); (pe) != ((void * )0); (pe) = ((pe)->entry.tqe_next)) { | |||
1188 | char *s; | |||
1189 | branchname = pe->path; | |||
1190 | if (strncmp(branchname, "refs/heads/", 11) == 0) | |||
1191 | branchname += 11; | |||
1192 | if (asprintf(&s, "%s\"%s\" ", | |||
1193 | branches ? branches : "", branchname) == -1) { | |||
1194 | err = got_error_from_errno("asprintf"); | |||
1195 | goto done; | |||
1196 | } | |||
1197 | free(branches); | |||
1198 | branches = s; | |||
1199 | } | |||
1200 | } else if (!fetch_all_branches && default_branch) { | |||
1201 | branchname = default_branch; | |||
1202 | if (strncmp(branchname, "refs/heads/", 11) == 0) | |||
1203 | branchname += 11; | |||
1204 | if (asprintf(&branches, "\"%s\" ", branchname) == -1) { | |||
1205 | err = got_error_from_errno("asprintf"); | |||
1206 | goto done; | |||
1207 | } | |||
1208 | } | |||
1209 | if (!TAILQ_EMPTY(wanted_refs)(((wanted_refs)->tqh_first) == ((void *)0))) { | |||
1210 | struct got_pathlist_entry *pe; | |||
1211 | TAILQ_FOREACH(pe, wanted_refs, entry)for((pe) = ((wanted_refs)->tqh_first); (pe) != ((void *)0) ; (pe) = ((pe)->entry.tqe_next)) { | |||
1212 | char *s; | |||
1213 | const char *refname = pe->path; | |||
1214 | if (strncmp(refname, "refs/", 5) == 0) | |||
1215 | branchname += 5; | |||
1216 | if (asprintf(&s, "%s\"%s\" ", | |||
1217 | refs ? refs : "", refname) == -1) { | |||
1218 | err = got_error_from_errno("asprintf"); | |||
1219 | goto done; | |||
1220 | } | |||
1221 | free(refs); | |||
1222 | refs = s; | |||
1223 | } | |||
1224 | } | |||
1225 | ||||
1226 | /* Create got.conf(5). */ | |||
1227 | gotconfig_path = got_repo_get_path_gotconfig(repo); | |||
1228 | if (gotconfig_path == NULL((void *)0)) { | |||
1229 | err = got_error_from_errno("got_repo_get_path_gotconfig"); | |||
1230 | goto done; | |||
1231 | } | |||
1232 | gotconfig_file = fopen(gotconfig_path, "a"); | |||
1233 | if (gotconfig_file == NULL((void *)0)) { | |||
1234 | err = got_error_from_errno2("fopen", gotconfig_path); | |||
1235 | goto done; | |||
1236 | } | |||
1237 | if (asprintf(&gotconfig, | |||
1238 | "remote \"%s\" {\n" | |||
1239 | "\tserver %s\n" | |||
1240 | "\tprotocol %s\n" | |||
1241 | "%s%s%s" | |||
1242 | "\trepository \"%s\"\n" | |||
1243 | "%s%s%s" | |||
1244 | "%s%s%s" | |||
1245 | "%s" | |||
1246 | "%s" | |||
1247 | "}\n", | |||
1248 | GOT_FETCH_DEFAULT_REMOTE_NAME"origin", host, proto, | |||
1249 | port ? "\tport " : "", port ? port : "", port ? "\n" : "", | |||
1250 | remote_repo_path, branches ? "\tbranch { " : "", | |||
1251 | branches ? branches : "", branches ? "}\n" : "", | |||
1252 | refs ? "\treference { " : "", refs ? refs : "", refs ? "}\n" : "", | |||
1253 | mirror_references ? "\tmirror-references yes\n" : "", | |||
1254 | fetch_all_branches ? "\tfetch-all-branches yes\n" : "") == -1) { | |||
1255 | err = got_error_from_errno("asprintf"); | |||
1256 | goto done; | |||
1257 | } | |||
1258 | n = fwrite(gotconfig, 1, strlen(gotconfig), gotconfig_file); | |||
1259 | if (n != strlen(gotconfig)) { | |||
1260 | err = got_ferror(gotconfig_file, GOT_ERR_IO6); | |||
1261 | goto done; | |||
1262 | } | |||
1263 | ||||
1264 | done: | |||
1265 | if (gotconfig_file && fclose(gotconfig_file) == EOF(-1) && err == NULL((void *)0)) | |||
1266 | err = got_error_from_errno2("fclose", gotconfig_path); | |||
1267 | free(gotconfig_path); | |||
1268 | free(branches); | |||
1269 | return err; | |||
1270 | } | |||
1271 | ||||
1272 | static const struct got_error * | |||
1273 | create_gitconfig(const char *git_url, const char *default_branch, | |||
1274 | int fetch_all_branches, struct got_pathlist_head *wanted_branches, | |||
1275 | struct got_pathlist_head *wanted_refs, int mirror_references, | |||
1276 | struct got_repository *repo) | |||
1277 | { | |||
1278 | const struct got_error *err = NULL((void *)0); | |||
1279 | char *gitconfig_path = NULL((void *)0); | |||
1280 | char *gitconfig = NULL((void *)0); | |||
1281 | FILE *gitconfig_file = NULL((void *)0); | |||
1282 | char *branches = NULL((void *)0), *refs = NULL((void *)0); | |||
1283 | const char *branchname; | |||
1284 | ssize_t n; | |||
1285 | ||||
1286 | /* Create a config file Git can understand. */ | |||
1287 | gitconfig_path = got_repo_get_path_gitconfig(repo); | |||
1288 | if (gitconfig_path == NULL((void *)0)) { | |||
1289 | err = got_error_from_errno("got_repo_get_path_gitconfig"); | |||
1290 | goto done; | |||
1291 | } | |||
1292 | gitconfig_file = fopen(gitconfig_path, "a"); | |||
1293 | if (gitconfig_file == NULL((void *)0)) { | |||
1294 | err = got_error_from_errno2("fopen", gitconfig_path); | |||
1295 | goto done; | |||
1296 | } | |||
1297 | if (fetch_all_branches) { | |||
1298 | if (mirror_references) { | |||
1299 | if (asprintf(&branches, | |||
1300 | "\tfetch = refs/heads/*:refs/heads/*\n") == -1) { | |||
1301 | err = got_error_from_errno("asprintf"); | |||
1302 | goto done; | |||
1303 | } | |||
1304 | } else if (asprintf(&branches, | |||
1305 | "\tfetch = refs/heads/*:refs/remotes/%s/*\n", | |||
1306 | GOT_FETCH_DEFAULT_REMOTE_NAME"origin") == -1) { | |||
1307 | err = got_error_from_errno("asprintf"); | |||
1308 | goto done; | |||
1309 | } | |||
1310 | } else if (!TAILQ_EMPTY(wanted_branches)(((wanted_branches)->tqh_first) == ((void *)0))) { | |||
1311 | struct got_pathlist_entry *pe; | |||
1312 | TAILQ_FOREACH(pe, wanted_branches, entry)for((pe) = ((wanted_branches)->tqh_first); (pe) != ((void * )0); (pe) = ((pe)->entry.tqe_next)) { | |||
1313 | char *s; | |||
1314 | branchname = pe->path; | |||
1315 | if (strncmp(branchname, "refs/heads/", 11) == 0) | |||
1316 | branchname += 11; | |||
1317 | if (mirror_references) { | |||
1318 | if (asprintf(&s, | |||
1319 | "%s\tfetch = refs/heads/%s:refs/heads/%s\n", | |||
1320 | branches ? branches : "", | |||
1321 | branchname, branchname) == -1) { | |||
1322 | err = got_error_from_errno("asprintf"); | |||
1323 | goto done; | |||
1324 | } | |||
1325 | } else if (asprintf(&s, | |||
1326 | "%s\tfetch = refs/heads/%s:refs/remotes/%s/%s\n", | |||
1327 | branches ? branches : "", | |||
1328 | branchname, GOT_FETCH_DEFAULT_REMOTE_NAME"origin", | |||
1329 | branchname) == -1) { | |||
1330 | err = got_error_from_errno("asprintf"); | |||
1331 | goto done; | |||
1332 | } | |||
1333 | free(branches); | |||
1334 | branches = s; | |||
1335 | } | |||
1336 | } else { | |||
1337 | /* | |||
1338 | * If the server specified a default branch, use just that one. | |||
1339 | * Otherwise fall back to fetching all branches on next fetch. | |||
1340 | */ | |||
1341 | if (default_branch) { | |||
1342 | branchname = default_branch; | |||
1343 | if (strncmp(branchname, "refs/heads/", 11) == 0) | |||
1344 | branchname += 11; | |||
1345 | } else | |||
1346 | branchname = "*"; /* fall back to all branches */ | |||
1347 | if (mirror_references) { | |||
1348 | if (asprintf(&branches, | |||
1349 | "\tfetch = refs/heads/%s:refs/heads/%s\n", | |||
1350 | branchname, branchname) == -1) { | |||
1351 | err = got_error_from_errno("asprintf"); | |||
1352 | goto done; | |||
1353 | } | |||
1354 | } else if (asprintf(&branches, | |||
1355 | "\tfetch = refs/heads/%s:refs/remotes/%s/%s\n", | |||
1356 | branchname, GOT_FETCH_DEFAULT_REMOTE_NAME"origin", | |||
1357 | branchname) == -1) { | |||
1358 | err = got_error_from_errno("asprintf"); | |||
1359 | goto done; | |||
1360 | } | |||
1361 | } | |||
1362 | if (!TAILQ_EMPTY(wanted_refs)(((wanted_refs)->tqh_first) == ((void *)0))) { | |||
1363 | struct got_pathlist_entry *pe; | |||
1364 | TAILQ_FOREACH(pe, wanted_refs, entry)for((pe) = ((wanted_refs)->tqh_first); (pe) != ((void *)0) ; (pe) = ((pe)->entry.tqe_next)) { | |||
1365 | char *s; | |||
1366 | const char *refname = pe->path; | |||
1367 | if (strncmp(refname, "refs/", 5) == 0) | |||
1368 | refname += 5; | |||
1369 | if (mirror_references) { | |||
1370 | if (asprintf(&s, | |||
1371 | "%s\tfetch = refs/%s:refs/%s\n", | |||
1372 | refs ? refs : "", refname, refname) == -1) { | |||
1373 | err = got_error_from_errno("asprintf"); | |||
1374 | goto done; | |||
1375 | } | |||
1376 | } else if (asprintf(&s, | |||
1377 | "%s\tfetch = refs/%s:refs/remotes/%s/%s\n", | |||
1378 | refs ? refs : "", | |||
1379 | refname, GOT_FETCH_DEFAULT_REMOTE_NAME"origin", | |||
1380 | refname) == -1) { | |||
1381 | err = got_error_from_errno("asprintf"); | |||
1382 | goto done; | |||
1383 | } | |||
1384 | free(refs); | |||
1385 | refs = s; | |||
1386 | } | |||
1387 | } | |||
1388 | ||||
1389 | if (asprintf(&gitconfig, | |||
1390 | "[remote \"%s\"]\n" | |||
1391 | "\turl = %s\n" | |||
1392 | "%s" | |||
1393 | "%s" | |||
1394 | "\tfetch = refs/tags/*:refs/tags/*\n", | |||
1395 | GOT_FETCH_DEFAULT_REMOTE_NAME"origin", git_url, branches ? branches : "", | |||
1396 | refs ? refs : "") == -1) { | |||
1397 | err = got_error_from_errno("asprintf"); | |||
1398 | goto done; | |||
1399 | } | |||
1400 | n = fwrite(gitconfig, 1, strlen(gitconfig), gitconfig_file); | |||
1401 | if (n != strlen(gitconfig)) { | |||
1402 | err = got_ferror(gitconfig_file, GOT_ERR_IO6); | |||
1403 | goto done; | |||
1404 | } | |||
1405 | done: | |||
1406 | if (gitconfig_file && fclose(gitconfig_file) == EOF(-1) && err == NULL((void *)0)) | |||
1407 | err = got_error_from_errno2("fclose", gitconfig_path); | |||
1408 | free(gitconfig_path); | |||
1409 | free(branches); | |||
1410 | return err; | |||
1411 | } | |||
1412 | ||||
1413 | static const struct got_error * | |||
1414 | create_config_files(const char *proto, const char *host, const char *port, | |||
1415 | const char *remote_repo_path, const char *git_url, int fetch_all_branches, | |||
1416 | int mirror_references, struct got_pathlist_head *symrefs, | |||
1417 | struct got_pathlist_head *wanted_branches, | |||
1418 | struct got_pathlist_head *wanted_refs, struct got_repository *repo) | |||
1419 | { | |||
1420 | const struct got_error *err = NULL((void *)0); | |||
1421 | const char *default_branch = NULL((void *)0); | |||
1422 | struct got_pathlist_entry *pe; | |||
1423 | ||||
1424 | /* | |||
1425 | * If we asked for a set of wanted branches then use the first | |||
1426 | * one of those. | |||
1427 | */ | |||
1428 | if (!TAILQ_EMPTY(wanted_branches)(((wanted_branches)->tqh_first) == ((void *)0))) { | |||
1429 | pe = TAILQ_FIRST(wanted_branches)((wanted_branches)->tqh_first); | |||
1430 | default_branch = pe->path; | |||
1431 | } else { | |||
1432 | /* First HEAD ref listed by server is the default branch. */ | |||
1433 | TAILQ_FOREACH(pe, symrefs, entry)for((pe) = ((symrefs)->tqh_first); (pe) != ((void *)0); (pe ) = ((pe)->entry.tqe_next)) { | |||
1434 | const char *refname = pe->path; | |||
1435 | const char *target = pe->data; | |||
1436 | ||||
1437 | if (strcmp(refname, GOT_REF_HEAD"HEAD") != 0) | |||
1438 | continue; | |||
1439 | ||||
1440 | default_branch = target; | |||
1441 | break; | |||
1442 | } | |||
1443 | } | |||
1444 | ||||
1445 | /* Create got.conf(5). */ | |||
1446 | err = create_gotconfig(proto, host, port, remote_repo_path, | |||
1447 | default_branch, fetch_all_branches, wanted_branches, | |||
1448 | wanted_refs, mirror_references, repo); | |||
1449 | if (err) | |||
1450 | return err; | |||
1451 | ||||
1452 | /* Create a config file Git can understand. */ | |||
1453 | return create_gitconfig(git_url, default_branch, fetch_all_branches, | |||
1454 | wanted_branches, wanted_refs, mirror_references, repo); | |||
1455 | } | |||
1456 | ||||
1457 | static const struct got_error * | |||
1458 | cmd_clone(int argc, char *argv[]) | |||
1459 | { | |||
1460 | const struct got_error *error = NULL((void *)0); | |||
1461 | const char *uri, *dirname; | |||
1462 | char *proto, *host, *port, *repo_name, *server_path; | |||
1463 | char *default_destdir = NULL((void *)0), *id_str = NULL((void *)0); | |||
1464 | const char *repo_path; | |||
1465 | struct got_repository *repo = NULL((void *)0); | |||
1466 | struct got_pathlist_head refs, symrefs, wanted_branches, wanted_refs; | |||
1467 | struct got_pathlist_entry *pe; | |||
1468 | struct got_object_id *pack_hash = NULL((void *)0); | |||
1469 | int ch, fetchfd = -1, fetchstatus; | |||
1470 | pid_t fetchpid = -1; | |||
1471 | struct got_fetch_progress_arg fpa; | |||
1472 | char *git_url = NULL((void *)0); | |||
1473 | int verbosity = 0, fetch_all_branches = 0, mirror_references = 0; | |||
1474 | int list_refs_only = 0; | |||
1475 | ||||
1476 | TAILQ_INIT(&refs)do { (&refs)->tqh_first = ((void *)0); (&refs)-> tqh_last = &(&refs)->tqh_first; } while (0); | |||
1477 | TAILQ_INIT(&symrefs)do { (&symrefs)->tqh_first = ((void *)0); (&symrefs )->tqh_last = &(&symrefs)->tqh_first; } while ( 0); | |||
1478 | TAILQ_INIT(&wanted_branches)do { (&wanted_branches)->tqh_first = ((void *)0); (& wanted_branches)->tqh_last = &(&wanted_branches)-> tqh_first; } while (0); | |||
1479 | TAILQ_INIT(&wanted_refs)do { (&wanted_refs)->tqh_first = ((void *)0); (&wanted_refs )->tqh_last = &(&wanted_refs)->tqh_first; } while (0); | |||
1480 | ||||
1481 | while ((ch = getopt(argc, argv, "ab:lmvqR:")) != -1) { | |||
1482 | switch (ch) { | |||
1483 | case 'a': | |||
1484 | fetch_all_branches = 1; | |||
1485 | break; | |||
1486 | case 'b': | |||
1487 | error = got_pathlist_append(&wanted_branches, | |||
1488 | optarg, NULL((void *)0)); | |||
1489 | if (error) | |||
1490 | return error; | |||
1491 | break; | |||
1492 | case 'l': | |||
1493 | list_refs_only = 1; | |||
1494 | break; | |||
1495 | case 'm': | |||
1496 | mirror_references = 1; | |||
1497 | break; | |||
1498 | case 'v': | |||
1499 | if (verbosity < 0) | |||
1500 | verbosity = 0; | |||
1501 | else if (verbosity < 3) | |||
1502 | verbosity++; | |||
1503 | break; | |||
1504 | case 'q': | |||
1505 | verbosity = -1; | |||
1506 | break; | |||
1507 | case 'R': | |||
1508 | error = got_pathlist_append(&wanted_refs, | |||
1509 | optarg, NULL((void *)0)); | |||
1510 | if (error) | |||
1511 | return error; | |||
1512 | break; | |||
1513 | default: | |||
1514 | usage_clone(); | |||
1515 | break; | |||
1516 | } | |||
1517 | } | |||
1518 | argc -= optind; | |||
1519 | argv += optind; | |||
1520 | ||||
1521 | if (fetch_all_branches && !TAILQ_EMPTY(&wanted_branches)(((&wanted_branches)->tqh_first) == ((void *)0))) | |||
1522 | option_conflict('a', 'b'); | |||
1523 | if (list_refs_only) { | |||
1524 | if (!TAILQ_EMPTY(&wanted_branches)(((&wanted_branches)->tqh_first) == ((void *)0))) | |||
1525 | option_conflict('l', 'b'); | |||
1526 | if (fetch_all_branches) | |||
1527 | option_conflict('l', 'a'); | |||
1528 | if (mirror_references) | |||
1529 | option_conflict('l', 'm'); | |||
1530 | if (!TAILQ_EMPTY(&wanted_refs)(((&wanted_refs)->tqh_first) == ((void *)0))) | |||
1531 | option_conflict('l', 'R'); | |||
1532 | } | |||
1533 | ||||
1534 | uri = argv[0]; | |||
1535 | ||||
1536 | if (argc == 1) | |||
1537 | dirname = NULL((void *)0); | |||
1538 | else if (argc == 2) | |||
1539 | dirname = argv[1]; | |||
1540 | else | |||
1541 | usage_clone(); | |||
1542 | ||||
1543 | error = got_fetch_parse_uri(&proto, &host, &port, &server_path, | |||
1544 | &repo_name, uri); | |||
1545 | if (error) | |||
1546 | goto done; | |||
1547 | ||||
1548 | if (asprintf(&git_url, "%s://%s%s%s%s%s", proto, | |||
1549 | host, port ? ":" : "", port ? port : "", | |||
1550 | server_path[0] != '/' ? "/" : "", server_path) == -1) { | |||
1551 | error = got_error_from_errno("asprintf"); | |||
1552 | goto done; | |||
1553 | } | |||
1554 | ||||
1555 | if (strcmp(proto, "git") == 0) { | |||
1556 | #ifndef PROFILE | |||
1557 | if (pledge("stdio rpath wpath cpath fattr flock proc exec " | |||
1558 | "sendfd dns inet unveil", NULL((void *)0)) == -1) | |||
1559 | err(1, "pledge"); | |||
1560 | #endif | |||
1561 | } else if (strcmp(proto, "git+ssh") == 0 || | |||
1562 | strcmp(proto, "ssh") == 0) { | |||
1563 | #ifndef PROFILE | |||
1564 | if (pledge("stdio rpath wpath cpath fattr flock proc exec " | |||
1565 | "sendfd unveil", NULL((void *)0)) == -1) | |||
1566 | err(1, "pledge"); | |||
1567 | #endif | |||
1568 | } else if (strcmp(proto, "http") == 0 || | |||
1569 | strcmp(proto, "git+http") == 0) { | |||
1570 | error = got_error_path(proto, GOT_ERR_NOT_IMPL18); | |||
1571 | goto done; | |||
1572 | } else { | |||
1573 | error = got_error_path(proto, GOT_ERR_BAD_PROTO120); | |||
1574 | goto done; | |||
1575 | } | |||
1576 | if (dirname == NULL((void *)0)) { | |||
1577 | if (asprintf(&default_destdir, "%s.git", repo_name) == -1) { | |||
1578 | error = got_error_from_errno("asprintf"); | |||
1579 | goto done; | |||
1580 | } | |||
1581 | repo_path = default_destdir; | |||
1582 | } else | |||
1583 | repo_path = dirname; | |||
1584 | ||||
1585 | if (!list_refs_only) { | |||
1586 | error = got_path_mkdir(repo_path); | |||
1587 | if (error && | |||
1588 | (!(error->code == GOT_ERR_ERRNO1 && errno(*__errno()) == EISDIR21) && | |||
1589 | !(error->code == GOT_ERR_ERRNO1 && errno(*__errno()) == EEXIST17))) | |||
1590 | goto done; | |||
1591 | if (!got_path_dir_is_empty(repo_path)) { | |||
1592 | error = got_error_path(repo_path, | |||
1593 | GOT_ERR_DIR_NOT_EMPTY75); | |||
1594 | goto done; | |||
1595 | } | |||
1596 | } | |||
1597 | ||||
1598 | if (strcmp(proto, "git+ssh") == 0 || strcmp(proto, "ssh") == 0) { | |||
1599 | if (unveil(GOT_FETCH_PATH_SSH"/usr/bin/ssh", "x") != 0) { | |||
1600 | error = got_error_from_errno2("unveil", | |||
1601 | GOT_FETCH_PATH_SSH"/usr/bin/ssh"); | |||
1602 | goto done; | |||
1603 | } | |||
1604 | } | |||
1605 | error = apply_unveil(repo_path, 0, NULL((void *)0)); | |||
1606 | if (error) | |||
1607 | goto done; | |||
1608 | ||||
1609 | if (verbosity >= 0) | |||
1610 | printf("Connecting to %s%s%s\n", host, | |||
1611 | port ? ":" : "", port ? port : ""); | |||
1612 | ||||
1613 | error = got_fetch_connect(&fetchpid, &fetchfd, proto, host, port, | |||
1614 | server_path, verbosity); | |||
1615 | if (error) | |||
1616 | goto done; | |||
1617 | ||||
1618 | if (!list_refs_only) { | |||
1619 | error = got_repo_init(repo_path); | |||
1620 | if (error) | |||
1621 | goto done; | |||
1622 | error = got_repo_open(&repo, repo_path, NULL((void *)0)); | |||
1623 | if (error) | |||
1624 | goto done; | |||
1625 | } | |||
1626 | ||||
1627 | fpa.last_scaled_size[0] = '\0'; | |||
1628 | fpa.last_p_indexed = -1; | |||
1629 | fpa.last_p_resolved = -1; | |||
1630 | fpa.verbosity = verbosity; | |||
1631 | fpa.create_configs = 1; | |||
1632 | fpa.configs_created = 0; | |||
1633 | fpa.repo = repo; | |||
1634 | fpa.config_info.symrefs = &symrefs; | |||
1635 | fpa.config_info.wanted_branches = &wanted_branches; | |||
1636 | fpa.config_info.wanted_refs = &wanted_refs; | |||
1637 | fpa.config_info.proto = proto; | |||
1638 | fpa.config_info.host = host; | |||
1639 | fpa.config_info.port = port; | |||
1640 | fpa.config_info.remote_repo_path = server_path; | |||
1641 | fpa.config_info.git_url = git_url; | |||
1642 | fpa.config_info.fetch_all_branches = fetch_all_branches; | |||
1643 | fpa.config_info.mirror_references = mirror_references; | |||
1644 | error = got_fetch_pack(&pack_hash, &refs, &symrefs, | |||
1645 | GOT_FETCH_DEFAULT_REMOTE_NAME"origin", mirror_references, | |||
1646 | fetch_all_branches, &wanted_branches, &wanted_refs, | |||
1647 | list_refs_only, verbosity, fetchfd, repo, | |||
1648 | fetch_progress, &fpa); | |||
1649 | if (error) | |||
1650 | goto done; | |||
1651 | ||||
1652 | if (list_refs_only) { | |||
1653 | error = list_remote_refs(&symrefs, &refs); | |||
1654 | goto done; | |||
1655 | } | |||
1656 | ||||
1657 | error = got_object_id_str(&id_str, pack_hash); | |||
1658 | if (error) | |||
1659 | goto done; | |||
1660 | if (verbosity >= 0) | |||
1661 | printf("\nFetched %s.pack\n", id_str); | |||
1662 | free(id_str); | |||
1663 | ||||
1664 | /* Set up references provided with the pack file. */ | |||
1665 | TAILQ_FOREACH(pe, &refs, entry)for((pe) = ((&refs)->tqh_first); (pe) != ((void *)0); ( pe) = ((pe)->entry.tqe_next)) { | |||
1666 | const char *refname = pe->path; | |||
1667 | struct got_object_id *id = pe->data; | |||
1668 | char *remote_refname; | |||
1669 | ||||
1670 | if (is_wanted_ref(&wanted_refs, refname) && | |||
1671 | !mirror_references) { | |||
1672 | error = create_wanted_ref(refname, id, | |||
1673 | GOT_FETCH_DEFAULT_REMOTE_NAME"origin", | |||
1674 | verbosity - 1, repo); | |||
1675 | if (error) | |||
1676 | goto done; | |||
1677 | continue; | |||
1678 | } | |||
1679 | ||||
1680 | error = create_ref(refname, id, verbosity - 1, repo); | |||
1681 | if (error) | |||
1682 | goto done; | |||
1683 | ||||
1684 | if (mirror_references) | |||
1685 | continue; | |||
1686 | ||||
1687 | if (strncmp("refs/heads/", refname, 11) != 0) | |||
1688 | continue; | |||
1689 | ||||
1690 | if (asprintf(&remote_refname, | |||
1691 | "refs/remotes/%s/%s", GOT_FETCH_DEFAULT_REMOTE_NAME"origin", | |||
1692 | refname + 11) == -1) { | |||
1693 | error = got_error_from_errno("asprintf"); | |||
1694 | goto done; | |||
1695 | } | |||
1696 | error = create_ref(remote_refname, id, verbosity - 1, repo); | |||
1697 | free(remote_refname); | |||
1698 | if (error) | |||
1699 | goto done; | |||
1700 | } | |||
1701 | ||||
1702 | /* Set the HEAD reference if the server provided one. */ | |||
1703 | TAILQ_FOREACH(pe, &symrefs, entry)for((pe) = ((&symrefs)->tqh_first); (pe) != ((void *)0 ); (pe) = ((pe)->entry.tqe_next)) { | |||
1704 | struct got_reference *target_ref; | |||
1705 | const char *refname = pe->path; | |||
1706 | const char *target = pe->data; | |||
1707 | char *remote_refname = NULL((void *)0), *remote_target = NULL((void *)0); | |||
1708 | ||||
1709 | if (strcmp(refname, GOT_REF_HEAD"HEAD") != 0) | |||
1710 | continue; | |||
1711 | ||||
1712 | error = got_ref_open(&target_ref, repo, target, 0); | |||
1713 | if (error) { | |||
1714 | if (error->code == GOT_ERR_NOT_REF5) { | |||
1715 | error = NULL((void *)0); | |||
1716 | continue; | |||
1717 | } | |||
1718 | goto done; | |||
1719 | } | |||
1720 | ||||
1721 | error = create_symref(refname, target_ref, verbosity, repo); | |||
1722 | got_ref_close(target_ref); | |||
1723 | if (error) | |||
1724 | goto done; | |||
1725 | ||||
1726 | if (mirror_references) | |||
1727 | continue; | |||
1728 | ||||
1729 | if (strncmp("refs/heads/", target, 11) != 0) | |||
1730 | continue; | |||
1731 | ||||
1732 | if (asprintf(&remote_refname, | |||
1733 | "refs/remotes/%s/%s", GOT_FETCH_DEFAULT_REMOTE_NAME"origin", | |||
1734 | refname) == -1) { | |||
1735 | error = got_error_from_errno("asprintf"); | |||
1736 | goto done; | |||
1737 | } | |||
1738 | if (asprintf(&remote_target, | |||
1739 | "refs/remotes/%s/%s", GOT_FETCH_DEFAULT_REMOTE_NAME"origin", | |||
1740 | target + 11) == -1) { | |||
1741 | error = got_error_from_errno("asprintf"); | |||
1742 | free(remote_refname); | |||
1743 | goto done; | |||
1744 | } | |||
1745 | error = got_ref_open(&target_ref, repo, remote_target, 0); | |||
1746 | if (error) { | |||
1747 | free(remote_refname); | |||
1748 | free(remote_target); | |||
1749 | if (error->code == GOT_ERR_NOT_REF5) { | |||
1750 | error = NULL((void *)0); | |||
1751 | continue; | |||
1752 | } | |||
1753 | goto done; | |||
1754 | } | |||
1755 | error = create_symref(remote_refname, target_ref, | |||
1756 | verbosity - 1, repo); | |||
1757 | free(remote_refname); | |||
1758 | free(remote_target); | |||
1759 | got_ref_close(target_ref); | |||
1760 | if (error) | |||
1761 | goto done; | |||
1762 | } | |||
1763 | if (pe == NULL((void *)0)) { | |||
1764 | /* | |||
1765 | * We failed to set the HEAD reference. If we asked for | |||
1766 | * a set of wanted branches use the first of one of those | |||
1767 | * which could be fetched instead. | |||
1768 | */ | |||
1769 | TAILQ_FOREACH(pe, &wanted_branches, entry)for((pe) = ((&wanted_branches)->tqh_first); (pe) != (( void *)0); (pe) = ((pe)->entry.tqe_next)) { | |||
1770 | const char *target = pe->path; | |||
1771 | struct got_reference *target_ref; | |||
1772 | ||||
1773 | error = got_ref_open(&target_ref, repo, target, 0); | |||
1774 | if (error) { | |||
1775 | if (error->code == GOT_ERR_NOT_REF5) { | |||
1776 | error = NULL((void *)0); | |||
1777 | continue; | |||
1778 | } | |||
1779 | goto done; | |||
1780 | } | |||
1781 | ||||
1782 | error = create_symref(GOT_REF_HEAD"HEAD", target_ref, | |||
1783 | verbosity, repo); | |||
1784 | got_ref_close(target_ref); | |||
1785 | if (error) | |||
1786 | goto done; | |||
1787 | break; | |||
1788 | } | |||
1789 | } | |||
1790 | ||||
1791 | if (verbosity >= 0) | |||
1792 | printf("Created %s repository '%s'\n", | |||
1793 | mirror_references ? "mirrored" : "cloned", repo_path); | |||
1794 | done: | |||
1795 | if (fetchpid > 0) { | |||
1796 | if (kill(fetchpid, SIGTERM15) == -1) | |||
1797 | error = got_error_from_errno("kill"); | |||
1798 | if (waitpid(fetchpid, &fetchstatus, 0) == -1 && error == NULL((void *)0)) | |||
1799 | error = got_error_from_errno("waitpid"); | |||
1800 | } | |||
1801 | if (fetchfd != -1 && close(fetchfd) == -1 && error == NULL((void *)0)) | |||
1802 | error = got_error_from_errno("close"); | |||
1803 | if (repo) | |||
1804 | got_repo_close(repo); | |||
1805 | TAILQ_FOREACH(pe, &refs, entry)for((pe) = ((&refs)->tqh_first); (pe) != ((void *)0); ( pe) = ((pe)->entry.tqe_next)) { | |||
1806 | free((void *)pe->path); | |||
1807 | free(pe->data); | |||
1808 | } | |||
1809 | got_pathlist_free(&refs); | |||
1810 | TAILQ_FOREACH(pe, &symrefs, entry)for((pe) = ((&symrefs)->tqh_first); (pe) != ((void *)0 ); (pe) = ((pe)->entry.tqe_next)) { | |||
1811 | free((void *)pe->path); | |||
1812 | free(pe->data); | |||
1813 | } | |||
1814 | got_pathlist_free(&symrefs); | |||
1815 | got_pathlist_free(&wanted_branches); | |||
1816 | got_pathlist_free(&wanted_refs); | |||
1817 | free(pack_hash); | |||
1818 | free(proto); | |||
1819 | free(host); | |||
1820 | free(port); | |||
1821 | free(server_path); | |||
1822 | free(repo_name); | |||
1823 | free(default_destdir); | |||
1824 | free(git_url); | |||
1825 | return error; | |||
1826 | } | |||
1827 | ||||
1828 | static const struct got_error * | |||
1829 | update_ref(struct got_reference *ref, struct got_object_id *new_id, | |||
1830 | int replace_tags, int verbosity, struct got_repository *repo) | |||
1831 | { | |||
1832 | const struct got_error *err = NULL((void *)0); | |||
1833 | char *new_id_str = NULL((void *)0); | |||
1834 | struct got_object_id *old_id = NULL((void *)0); | |||
1835 | ||||
1836 | err = got_object_id_str(&new_id_str, new_id); | |||
1837 | if (err) | |||
1838 | goto done; | |||
1839 | ||||
1840 | if (!replace_tags && | |||
1841 | strncmp(got_ref_get_name(ref), "refs/tags/", 10) == 0) { | |||
1842 | err = got_ref_resolve(&old_id, repo, ref); | |||
1843 | if (err) | |||
1844 | goto done; | |||
1845 | if (got_object_id_cmp(old_id, new_id) == 0) | |||
1846 | goto done; | |||
1847 | if (verbosity >= 0) { | |||
1848 | printf("Rejecting update of existing tag %s: %s\n", | |||
1849 | got_ref_get_name(ref), new_id_str); | |||
1850 | } | |||
1851 | goto done; | |||
1852 | } | |||
1853 | ||||
1854 | if (got_ref_is_symbolic(ref)) { | |||
1855 | if (verbosity >= 0) { | |||
1856 | printf("Replacing reference %s: %s\n", | |||
1857 | got_ref_get_name(ref), | |||
1858 | got_ref_get_symref_target(ref)); | |||
1859 | } | |||
1860 | err = got_ref_change_symref_to_ref(ref, new_id); | |||
1861 | if (err) | |||
1862 | goto done; | |||
1863 | err = got_ref_write(ref, repo); | |||
1864 | if (err) | |||
1865 | goto done; | |||
1866 | } else { | |||
1867 | err = got_ref_resolve(&old_id, repo, ref); | |||
1868 | if (err) | |||
1869 | goto done; | |||
1870 | if (got_object_id_cmp(old_id, new_id) == 0) | |||
1871 | goto done; | |||
1872 | ||||
1873 | err = got_ref_change_ref(ref, new_id); | |||
1874 | if (err) | |||
1875 | goto done; | |||
1876 | err = got_ref_write(ref, repo); | |||
1877 | if (err) | |||
1878 | goto done; | |||
1879 | } | |||
1880 | ||||
1881 | if (verbosity >= 0) | |||
1882 | printf("Updated %s: %s\n", got_ref_get_name(ref), | |||
1883 | new_id_str); | |||
1884 | done: | |||
1885 | free(old_id); | |||
1886 | free(new_id_str); | |||
1887 | return err; | |||
1888 | } | |||
1889 | ||||
1890 | static const struct got_error * | |||
1891 | update_symref(const char *refname, struct got_reference *target_ref, | |||
1892 | int verbosity, struct got_repository *repo) | |||
1893 | { | |||
1894 | const struct got_error *err = NULL((void *)0), *unlock_err; | |||
1895 | struct got_reference *symref; | |||
1896 | int symref_is_locked = 0; | |||
1897 | ||||
1898 | err = got_ref_open(&symref, repo, refname, 1); | |||
1899 | if (err) { | |||
1900 | if (err->code != GOT_ERR_NOT_REF5) | |||
1901 | return err; | |||
1902 | err = got_ref_alloc_symref(&symref, refname, target_ref); | |||
1903 | if (err) | |||
1904 | goto done; | |||
1905 | ||||
1906 | err = got_ref_write(symref, repo); | |||
1907 | if (err) | |||
1908 | goto done; | |||
1909 | ||||
1910 | if (verbosity >= 0) | |||
1911 | printf("Created reference %s: %s\n", | |||
1912 | got_ref_get_name(symref), | |||
1913 | got_ref_get_symref_target(symref)); | |||
1914 | } else { | |||
1915 | symref_is_locked = 1; | |||
1916 | ||||
1917 | if (strcmp(got_ref_get_symref_target(symref), | |||
1918 | got_ref_get_name(target_ref)) == 0) | |||
1919 | goto done; | |||
1920 | ||||
1921 | err = got_ref_change_symref(symref, | |||
1922 | got_ref_get_name(target_ref)); | |||
1923 | if (err) | |||
1924 | goto done; | |||
1925 | ||||
1926 | err = got_ref_write(symref, repo); | |||
1927 | if (err) | |||
1928 | goto done; | |||
1929 | ||||
1930 | if (verbosity >= 0) | |||
1931 | printf("Updated %s: %s\n", got_ref_get_name(symref), | |||
1932 | got_ref_get_symref_target(symref)); | |||
1933 | ||||
1934 | } | |||
1935 | done: | |||
1936 | if (symref_is_locked) { | |||
1937 | unlock_err = got_ref_unlock(symref); | |||
1938 | if (unlock_err && err == NULL((void *)0)) | |||
1939 | err = unlock_err; | |||
1940 | } | |||
1941 | got_ref_close(symref); | |||
1942 | return err; | |||
1943 | } | |||
1944 | ||||
1945 | __dead__attribute__((__noreturn__)) static void | |||
1946 | usage_fetch(void) | |||
1947 | { | |||
1948 | fprintf(stderr(&__sF[2]), "usage: %s fetch [-a] [-b branch] [-d] [-l] " | |||
1949 | "[-r repository-path] [-t] [-q] [-v] [-R reference] " | |||
1950 | "[remote-repository-name]\n", | |||
1951 | getprogname()); | |||
1952 | exit(1); | |||
1953 | } | |||
1954 | ||||
1955 | static const struct got_error * | |||
1956 | delete_missing_ref(struct got_reference *ref, | |||
1957 | int verbosity, struct got_repository *repo) | |||
1958 | { | |||
1959 | const struct got_error *err = NULL((void *)0); | |||
1960 | struct got_object_id *id = NULL((void *)0); | |||
1961 | char *id_str = NULL((void *)0); | |||
1962 | ||||
1963 | if (got_ref_is_symbolic(ref)) { | |||
1964 | err = got_ref_delete(ref, repo); | |||
1965 | if (err) | |||
1966 | return err; | |||
1967 | if (verbosity >= 0) { | |||
1968 | printf("Deleted reference %s: %s\n", | |||
1969 | got_ref_get_name(ref), | |||
1970 | got_ref_get_symref_target(ref)); | |||
1971 | } | |||
1972 | } else { | |||
1973 | err = got_ref_resolve(&id, repo, ref); | |||
1974 | if (err) | |||
1975 | return err; | |||
1976 | err = got_object_id_str(&id_str, id); | |||
1977 | if (err) | |||
1978 | goto done; | |||
1979 | ||||
1980 | err = got_ref_delete(ref, repo); | |||
1981 | if (err) | |||
1982 | goto done; | |||
1983 | if (verbosity >= 0) { | |||
1984 | printf("Deleted reference %s: %s\n", | |||
1985 | got_ref_get_name(ref), id_str); | |||
1986 | } | |||
1987 | } | |||
1988 | done: | |||
1989 | free(id); | |||
1990 | free(id_str); | |||
1991 | return NULL((void *)0); | |||
1992 | } | |||
1993 | ||||
1994 | static const struct got_error * | |||
1995 | delete_missing_refs(struct got_pathlist_head *their_refs, | |||
1996 | struct got_pathlist_head *their_symrefs, | |||
1997 | const struct got_remote_repo *remote, | |||
1998 | int verbosity, struct got_repository *repo) | |||
1999 | { | |||
2000 | const struct got_error *err = NULL((void *)0), *unlock_err; | |||
2001 | struct got_reflist_head my_refs; | |||
2002 | struct got_reflist_entry *re; | |||
2003 | struct got_pathlist_entry *pe; | |||
2004 | char *remote_namespace = NULL((void *)0); | |||
2005 | char *local_refname = NULL((void *)0); | |||
2006 | ||||
2007 | TAILQ_INIT(&my_refs)do { (&my_refs)->tqh_first = ((void *)0); (&my_refs )->tqh_last = &(&my_refs)->tqh_first; } while ( 0); | |||
2008 | ||||
2009 | if (asprintf(&remote_namespace, "refs/remotes/%s/", remote->name) | |||
2010 | == -1) | |||
2011 | return got_error_from_errno("asprintf"); | |||
2012 | ||||
2013 | err = got_ref_list(&my_refs, repo, NULL((void *)0), got_ref_cmp_by_name, NULL((void *)0)); | |||
2014 | if (err) | |||
2015 | goto done; | |||
2016 | ||||
2017 | TAILQ_FOREACH(re, &my_refs, entry)for((re) = ((&my_refs)->tqh_first); (re) != ((void *)0 ); (re) = ((re)->entry.tqe_next)) { | |||
2018 | const char *refname = got_ref_get_name(re->ref); | |||
2019 | ||||
2020 | if (!remote->mirror_references) { | |||
2021 | if (strncmp(refname, remote_namespace, | |||
2022 | strlen(remote_namespace)) == 0) { | |||
2023 | if (strcmp(refname + strlen(remote_namespace), | |||
2024 | GOT_REF_HEAD"HEAD") == 0) | |||
2025 | continue; | |||
2026 | if (asprintf(&local_refname, "refs/heads/%s", | |||
2027 | refname + strlen(remote_namespace)) == -1) { | |||
2028 | err = got_error_from_errno("asprintf"); | |||
2029 | goto done; | |||
2030 | } | |||
2031 | } else if (strncmp(refname, "refs/tags/", 10) != 0) | |||
2032 | continue; | |||
2033 | } | |||
2034 | ||||
2035 | TAILQ_FOREACH(pe, their_refs, entry)for((pe) = ((their_refs)->tqh_first); (pe) != ((void *)0); (pe) = ((pe)->entry.tqe_next)) { | |||
2036 | if (strcmp(local_refname, pe->path) == 0) | |||
2037 | break; | |||
2038 | } | |||
2039 | if (pe != NULL((void *)0)) | |||
2040 | continue; | |||
2041 | ||||
2042 | TAILQ_FOREACH(pe, their_symrefs, entry)for((pe) = ((their_symrefs)->tqh_first); (pe) != ((void *) 0); (pe) = ((pe)->entry.tqe_next)) { | |||
2043 | if (strcmp(local_refname, pe->path) == 0) | |||
2044 | break; | |||
2045 | } | |||
2046 | if (pe != NULL((void *)0)) | |||
2047 | continue; | |||
2048 | ||||
2049 | err = delete_missing_ref(re->ref, verbosity, repo); | |||
2050 | if (err) | |||
2051 | break; | |||
2052 | ||||
2053 | if (local_refname) { | |||
2054 | struct got_reference *ref; | |||
2055 | err = got_ref_open(&ref, repo, local_refname, 1); | |||
2056 | if (err) { | |||
2057 | if (err->code != GOT_ERR_NOT_REF5) | |||
2058 | break; | |||
2059 | free(local_refname); | |||
2060 | local_refname = NULL((void *)0); | |||
2061 | continue; | |||
2062 | } | |||
2063 | err = delete_missing_ref(ref, verbosity, repo); | |||
2064 | if (err) | |||
2065 | break; | |||
2066 | unlock_err = got_ref_unlock(ref); | |||
2067 | got_ref_close(ref); | |||
2068 | if (unlock_err && err == NULL((void *)0)) { | |||
2069 | err = unlock_err; | |||
2070 | break; | |||
2071 | } | |||
2072 | ||||
2073 | free(local_refname); | |||
2074 | local_refname = NULL((void *)0); | |||
2075 | } | |||
2076 | } | |||
2077 | done: | |||
2078 | free(remote_namespace); | |||
2079 | free(local_refname); | |||
2080 | return err; | |||
2081 | } | |||
2082 | ||||
2083 | static const struct got_error * | |||
2084 | update_wanted_ref(const char *refname, struct got_object_id *id, | |||
2085 | const char *remote_repo_name, int verbosity, struct got_repository *repo) | |||
2086 | { | |||
2087 | const struct got_error *err, *unlock_err; | |||
2088 | char *remote_refname; | |||
2089 | struct got_reference *ref; | |||
2090 | ||||
2091 | if (strncmp("refs/", refname, 5) == 0) | |||
2092 | refname += 5; | |||
2093 | ||||
2094 | if (asprintf(&remote_refname, "refs/remotes/%s/%s", | |||
2095 | remote_repo_name, refname) == -1) | |||
2096 | return got_error_from_errno("asprintf"); | |||
2097 | ||||
2098 | err = got_ref_open(&ref, repo, remote_refname, 1); | |||
2099 | if (err) { | |||
2100 | if (err->code != GOT_ERR_NOT_REF5) | |||
2101 | goto done; | |||
2102 | err = create_ref(remote_refname, id, verbosity, repo); | |||
2103 | } else { | |||
2104 | err = update_ref(ref, id, 0, verbosity, repo); | |||
2105 | unlock_err = got_ref_unlock(ref); | |||
2106 | if (unlock_err && err == NULL((void *)0)) | |||
2107 | err = unlock_err; | |||
2108 | got_ref_close(ref); | |||
2109 | } | |||
2110 | done: | |||
2111 | free(remote_refname); | |||
2112 | return err; | |||
2113 | } | |||
2114 | ||||
2115 | static const struct got_error * | |||
2116 | cmd_fetch(int argc, char *argv[]) | |||
2117 | { | |||
2118 | const struct got_error *error = NULL((void *)0), *unlock_err; | |||
2119 | char *cwd = NULL((void *)0), *repo_path = NULL((void *)0); | |||
2120 | const char *remote_name; | |||
2121 | char *proto = NULL((void *)0), *host = NULL((void *)0), *port = NULL((void *)0); | |||
2122 | char *repo_name = NULL((void *)0), *server_path = NULL((void *)0); | |||
2123 | const struct got_remote_repo *remotes, *remote = NULL((void *)0); | |||
2124 | int nremotes; | |||
2125 | char *id_str = NULL((void *)0); | |||
2126 | struct got_repository *repo = NULL((void *)0); | |||
2127 | struct got_worktree *worktree = NULL((void *)0); | |||
2128 | const struct got_gotconfig *repo_conf = NULL((void *)0), *worktree_conf = NULL((void *)0); | |||
2129 | struct got_pathlist_head refs, symrefs, wanted_branches, wanted_refs; | |||
2130 | struct got_pathlist_entry *pe; | |||
2131 | struct got_object_id *pack_hash = NULL((void *)0); | |||
2132 | int i, ch, fetchfd = -1, fetchstatus; | |||
2133 | pid_t fetchpid = -1; | |||
2134 | struct got_fetch_progress_arg fpa; | |||
2135 | int verbosity = 0, fetch_all_branches = 0, list_refs_only = 0; | |||
2136 | int delete_refs = 0, replace_tags = 0; | |||
2137 | ||||
2138 | TAILQ_INIT(&refs)do { (&refs)->tqh_first = ((void *)0); (&refs)-> tqh_last = &(&refs)->tqh_first; } while (0); | |||
2139 | TAILQ_INIT(&symrefs)do { (&symrefs)->tqh_first = ((void *)0); (&symrefs )->tqh_last = &(&symrefs)->tqh_first; } while ( 0); | |||
2140 | TAILQ_INIT(&wanted_branches)do { (&wanted_branches)->tqh_first = ((void *)0); (& wanted_branches)->tqh_last = &(&wanted_branches)-> tqh_first; } while (0); | |||
2141 | TAILQ_INIT(&wanted_refs)do { (&wanted_refs)->tqh_first = ((void *)0); (&wanted_refs )->tqh_last = &(&wanted_refs)->tqh_first; } while (0); | |||
2142 | ||||
2143 | while ((ch = getopt(argc, argv, "ab:dlr:tvqR:")) != -1) { | |||
2144 | switch (ch) { | |||
2145 | case 'a': | |||
2146 | fetch_all_branches = 1; | |||
2147 | break; | |||
2148 | case 'b': | |||
2149 | error = got_pathlist_append(&wanted_branches, | |||
2150 | optarg, NULL((void *)0)); | |||
2151 | if (error) | |||
2152 | return error; | |||
2153 | break; | |||
2154 | case 'd': | |||
2155 | delete_refs = 1; | |||
2156 | break; | |||
2157 | case 'l': | |||
2158 | list_refs_only = 1; | |||
2159 | break; | |||
2160 | case 'r': | |||
2161 | repo_path = realpath(optarg, NULL((void *)0)); | |||
2162 | if (repo_path == NULL((void *)0)) | |||
2163 | return got_error_from_errno2("realpath", | |||
2164 | optarg); | |||
2165 | got_path_strip_trailing_slashes(repo_path); | |||
2166 | break; | |||
2167 | case 't': | |||
2168 | replace_tags = 1; | |||
2169 | break; | |||
2170 | case 'v': | |||
2171 | if (verbosity < 0) | |||
2172 | verbosity = 0; | |||
2173 | else if (verbosity < 3) | |||
2174 | verbosity++; | |||
2175 | break; | |||
2176 | case 'q': | |||
2177 | verbosity = -1; | |||
2178 | break; | |||
2179 | case 'R': | |||
2180 | error = got_pathlist_append(&wanted_refs, | |||
2181 | optarg, NULL((void *)0)); | |||
2182 | if (error) | |||
2183 | return error; | |||
2184 | break; | |||
2185 | default: | |||
2186 | usage_fetch(); | |||
2187 | break; | |||
2188 | } | |||
2189 | } | |||
2190 | argc -= optind; | |||
2191 | argv += optind; | |||
2192 | ||||
2193 | if (fetch_all_branches && !TAILQ_EMPTY(&wanted_branches)(((&wanted_branches)->tqh_first) == ((void *)0))) | |||
2194 | option_conflict('a', 'b'); | |||
2195 | if (list_refs_only) { | |||
2196 | if (!TAILQ_EMPTY(&wanted_branches)(((&wanted_branches)->tqh_first) == ((void *)0))) | |||
2197 | option_conflict('l', 'b'); | |||
2198 | if (fetch_all_branches) | |||
2199 | option_conflict('l', 'a'); | |||
2200 | if (delete_refs) | |||
2201 | option_conflict('l', 'd'); | |||
2202 | } | |||
2203 | ||||
2204 | if (argc == 0) | |||
2205 | remote_name = GOT_FETCH_DEFAULT_REMOTE_NAME"origin"; | |||
2206 | else if (argc == 1) | |||
2207 | remote_name = argv[0]; | |||
2208 | else | |||
2209 | usage_fetch(); | |||
2210 | ||||
2211 | cwd = getcwd(NULL((void *)0), 0); | |||
2212 | if (cwd == NULL((void *)0)) { | |||
2213 | error = got_error_from_errno("getcwd"); | |||
2214 | goto done; | |||
2215 | } | |||
2216 | ||||
2217 | if (repo_path == NULL((void *)0)) { | |||
2218 | error = got_worktree_open(&worktree, cwd); | |||
2219 | if (error && error->code != GOT_ERR_NOT_WORKTREE60) | |||
2220 | goto done; | |||
2221 | else | |||
2222 | error = NULL((void *)0); | |||
2223 | if (worktree) { | |||
2224 | repo_path = | |||
2225 | strdup(got_worktree_get_repo_path(worktree)); | |||
2226 | if (repo_path == NULL((void *)0)) | |||
2227 | error = got_error_from_errno("strdup"); | |||
2228 | if (error) | |||
2229 | goto done; | |||
2230 | } else { | |||
2231 | repo_path = strdup(cwd); | |||
2232 | if (repo_path == NULL((void *)0)) { | |||
2233 | error = got_error_from_errno("strdup"); | |||
2234 | goto done; | |||
2235 | } | |||
2236 | } | |||
2237 | } | |||
2238 | ||||
2239 | error = got_repo_open(&repo, repo_path, NULL((void *)0)); | |||
2240 | if (error) | |||
2241 | goto done; | |||
2242 | ||||
2243 | if (worktree) { | |||
2244 | worktree_conf = got_worktree_get_gotconfig(worktree); | |||
2245 | if (worktree_conf) { | |||
2246 | got_gotconfig_get_remotes(&nremotes, &remotes, | |||
2247 | worktree_conf); | |||
2248 | for (i = 0; i < nremotes; i++) { | |||
2249 | if (strcmp(remotes[i].name, remote_name) == 0) { | |||
2250 | remote = &remotes[i]; | |||
2251 | break; | |||
2252 | } | |||
2253 | } | |||
2254 | } | |||
2255 | } | |||
2256 | if (remote == NULL((void *)0)) { | |||
2257 | repo_conf = got_repo_get_gotconfig(repo); | |||
2258 | if (repo_conf) { | |||
2259 | got_gotconfig_get_remotes(&nremotes, &remotes, | |||
2260 | repo_conf); | |||
2261 | for (i = 0; i < nremotes; i++) { | |||
2262 | if (strcmp(remotes[i].name, remote_name) == 0) { | |||
2263 | remote = &remotes[i]; | |||
2264 | break; | |||
2265 | } | |||
2266 | } | |||
2267 | } | |||
2268 | } | |||
2269 | if (remote == NULL((void *)0)) { | |||
2270 | got_repo_get_gitconfig_remotes(&nremotes, &remotes, repo); | |||
2271 | for (i = 0; i < nremotes; i++) { | |||
2272 | if (strcmp(remotes[i].name, remote_name) == 0) { | |||
2273 | remote = &remotes[i]; | |||
2274 | break; | |||
2275 | } | |||
2276 | } | |||
2277 | } | |||
2278 | if (remote == NULL((void *)0)) { | |||
2279 | error = got_error_path(remote_name, GOT_ERR_NO_REMOTE123); | |||
2280 | goto done; | |||
2281 | } | |||
2282 | ||||
2283 | if (TAILQ_EMPTY(&wanted_branches)(((&wanted_branches)->tqh_first) == ((void *)0))) { | |||
2284 | if (!fetch_all_branches) | |||
2285 | fetch_all_branches = remote->fetch_all_branches; | |||
2286 | for (i = 0; i < remote->nbranches; i++) { | |||
2287 | got_pathlist_append(&wanted_branches, | |||
2288 | remote->branches[i], NULL((void *)0)); | |||
2289 | } | |||
2290 | } | |||
2291 | if (TAILQ_EMPTY(&wanted_refs)(((&wanted_refs)->tqh_first) == ((void *)0))) { | |||
2292 | for (i = 0; i < remote->nrefs; i++) { | |||
2293 | got_pathlist_append(&wanted_refs, | |||
2294 | remote->refs[i], NULL((void *)0)); | |||
2295 | } | |||
2296 | } | |||
2297 | ||||
2298 | error = got_fetch_parse_uri(&proto, &host, &port, &server_path, | |||
2299 | &repo_name, remote->url); | |||
2300 | if (error) | |||
2301 | goto done; | |||
2302 | ||||
2303 | if (strcmp(proto, "git") == 0) { | |||
2304 | #ifndef PROFILE | |||
2305 | if (pledge("stdio rpath wpath cpath fattr flock proc exec " | |||
2306 | "sendfd dns inet unveil", NULL((void *)0)) == -1) | |||
2307 | err(1, "pledge"); | |||
2308 | #endif | |||
2309 | } else if (strcmp(proto, "git+ssh") == 0 || | |||
2310 | strcmp(proto, "ssh") == 0) { | |||
2311 | #ifndef PROFILE | |||
2312 | if (pledge("stdio rpath wpath cpath fattr flock proc exec " | |||
2313 | "sendfd unveil", NULL((void *)0)) == -1) | |||
2314 | err(1, "pledge"); | |||
2315 | #endif | |||
2316 | } else if (strcmp(proto, "http") == 0 || | |||
2317 | strcmp(proto, "git+http") == 0) { | |||
2318 | error = got_error_path(proto, GOT_ERR_NOT_IMPL18); | |||
2319 | goto done; | |||
2320 | } else { | |||
2321 | error = got_error_path(proto, GOT_ERR_BAD_PROTO120); | |||
2322 | goto done; | |||
2323 | } | |||
2324 | ||||
2325 | if (strcmp(proto, "git+ssh") == 0 || strcmp(proto, "ssh") == 0) { | |||
2326 | if (unveil(GOT_FETCH_PATH_SSH"/usr/bin/ssh", "x") != 0) { | |||
2327 | error = got_error_from_errno2("unveil", | |||
2328 | GOT_FETCH_PATH_SSH"/usr/bin/ssh"); | |||
2329 | goto done; | |||
2330 | } | |||
2331 | } | |||
2332 | error = apply_unveil(got_repo_get_path(repo), 0, NULL((void *)0)); | |||
2333 | if (error) | |||
2334 | goto done; | |||
2335 | ||||
2336 | if (verbosity >= 0) | |||
2337 | printf("Connecting to \"%s\" %s%s%s\n", remote->name, host, | |||
2338 | port ? ":" : "", port ? port : ""); | |||
2339 | ||||
2340 | error = got_fetch_connect(&fetchpid, &fetchfd, proto, host, port, | |||
2341 | server_path, verbosity); | |||
2342 | if (error) | |||
2343 | goto done; | |||
2344 | ||||
2345 | fpa.last_scaled_size[0] = '\0'; | |||
2346 | fpa.last_p_indexed = -1; | |||
2347 | fpa.last_p_resolved = -1; | |||
2348 | fpa.verbosity = verbosity; | |||
2349 | fpa.repo = repo; | |||
2350 | fpa.create_configs = 0; | |||
2351 | fpa.configs_created = 0; | |||
2352 | memset(&fpa.config_info, 0, sizeof(fpa.config_info)); | |||
2353 | error = got_fetch_pack(&pack_hash, &refs, &symrefs, remote->name, | |||
2354 | remote->mirror_references, fetch_all_branches, &wanted_branches, | |||
2355 | &wanted_refs, list_refs_only, verbosity, fetchfd, repo, | |||
2356 | fetch_progress, &fpa); | |||
2357 | if (error) | |||
2358 | goto done; | |||
2359 | ||||
2360 | if (list_refs_only) { | |||
2361 | error = list_remote_refs(&symrefs, &refs); | |||
2362 | goto done; | |||
2363 | } | |||
2364 | ||||
2365 | if (pack_hash == NULL((void *)0)) { | |||
2366 | if (verbosity >= 0) | |||
2367 | printf("Already up-to-date\n"); | |||
2368 | } else if (verbosity >= 0) { | |||
2369 | error = got_object_id_str(&id_str, pack_hash); | |||
2370 | if (error) | |||
2371 | goto done; | |||
2372 | printf("\nFetched %s.pack\n", id_str); | |||
2373 | free(id_str); | |||
2374 | id_str = NULL((void *)0); | |||
2375 | } | |||
2376 | ||||
2377 | /* Update references provided with the pack file. */ | |||
2378 | TAILQ_FOREACH(pe, &refs, entry)for((pe) = ((&refs)->tqh_first); (pe) != ((void *)0); ( pe) = ((pe)->entry.tqe_next)) { | |||
2379 | const char *refname = pe->path; | |||
2380 | struct got_object_id *id = pe->data; | |||
2381 | struct got_reference *ref; | |||
2382 | char *remote_refname; | |||
2383 | ||||
2384 | if (is_wanted_ref(&wanted_refs, refname) && | |||
2385 | !remote->mirror_references) { | |||
2386 | error = update_wanted_ref(refname, id, | |||
2387 | remote->name, verbosity, repo); | |||
2388 | if (error) | |||
2389 | goto done; | |||
2390 | continue; | |||
2391 | } | |||
2392 | ||||
2393 | if (remote->mirror_references || | |||
2394 | strncmp("refs/tags/", refname, 10) == 0) { | |||
2395 | error = got_ref_open(&ref, repo, refname, 1); | |||
2396 | if (error) { | |||
2397 | if (error->code != GOT_ERR_NOT_REF5) | |||
2398 | goto done; | |||
2399 | error = create_ref(refname, id, verbosity, | |||
2400 | repo); | |||
2401 | if (error) | |||
2402 | goto done; | |||
2403 | } else { | |||
2404 | error = update_ref(ref, id, replace_tags, | |||
2405 | verbosity, repo); | |||
2406 | unlock_err = got_ref_unlock(ref); | |||
2407 | if (unlock_err && error == NULL((void *)0)) | |||
2408 | error = unlock_err; | |||
2409 | got_ref_close(ref); | |||
2410 | if (error) | |||
2411 | goto done; | |||
2412 | } | |||
2413 | } else if (strncmp("refs/heads/", refname, 11) == 0) { | |||
2414 | if (asprintf(&remote_refname, "refs/remotes/%s/%s", | |||
2415 | remote_name, refname + 11) == -1) { | |||
2416 | error = got_error_from_errno("asprintf"); | |||
2417 | goto done; | |||
2418 | } | |||
2419 | ||||
2420 | error = got_ref_open(&ref, repo, remote_refname, 1); | |||
2421 | if (error) { | |||
2422 | if (error->code != GOT_ERR_NOT_REF5) | |||
2423 | goto done; | |||
2424 | error = create_ref(remote_refname, id, | |||
2425 | verbosity, repo); | |||
2426 | if (error) | |||
2427 | goto done; | |||
2428 | } else { | |||
2429 | error = update_ref(ref, id, replace_tags, | |||
2430 | verbosity, repo); | |||
2431 | unlock_err = got_ref_unlock(ref); | |||
2432 | if (unlock_err && error == NULL((void *)0)) | |||
2433 | error = unlock_err; | |||
2434 | got_ref_close(ref); | |||
2435 | if (error) | |||
2436 | goto done; | |||
2437 | } | |||
2438 | ||||
2439 | /* Also create a local branch if none exists yet. */ | |||
2440 | error = got_ref_open(&ref, repo, refname, 1); | |||
2441 | if (error) { | |||
2442 | if (error->code != GOT_ERR_NOT_REF5) | |||
2443 | goto done; | |||
2444 | error = create_ref(refname, id, verbosity, | |||
2445 | repo); | |||
2446 | if (error) | |||
2447 | goto done; | |||
2448 | } else { | |||
2449 | unlock_err = got_ref_unlock(ref); | |||
2450 | if (unlock_err && error == NULL((void *)0)) | |||
2451 | error = unlock_err; | |||
2452 | got_ref_close(ref); | |||
2453 | } | |||
2454 | } | |||
2455 | } | |||
2456 | if (delete_refs) { | |||
2457 | error = delete_missing_refs(&refs, &symrefs, remote, | |||
2458 | verbosity, repo); | |||
2459 | if (error) | |||
2460 | goto done; | |||
2461 | } | |||
2462 | ||||
2463 | if (!remote->mirror_references) { | |||
2464 | /* Update remote HEAD reference if the server provided one. */ | |||
2465 | TAILQ_FOREACH(pe, &symrefs, entry)for((pe) = ((&symrefs)->tqh_first); (pe) != ((void *)0 ); (pe) = ((pe)->entry.tqe_next)) { | |||
2466 | struct got_reference *target_ref; | |||
2467 | const char *refname = pe->path; | |||
2468 | const char *target = pe->data; | |||
2469 | char *remote_refname = NULL((void *)0), *remote_target = NULL((void *)0); | |||
2470 | ||||
2471 | if (strcmp(refname, GOT_REF_HEAD"HEAD") != 0) | |||
2472 | continue; | |||
2473 | ||||
2474 | if (strncmp("refs/heads/", target, 11) != 0) | |||
2475 | continue; | |||
2476 | ||||
2477 | if (asprintf(&remote_refname, "refs/remotes/%s/%s", | |||
2478 | remote->name, refname) == -1) { | |||
2479 | error = got_error_from_errno("asprintf"); | |||
2480 | goto done; | |||
2481 | } | |||
2482 | if (asprintf(&remote_target, "refs/remotes/%s/%s", | |||
2483 | remote->name, target + 11) == -1) { | |||
2484 | error = got_error_from_errno("asprintf"); | |||
2485 | free(remote_refname); | |||
2486 | goto done; | |||
2487 | } | |||
2488 | ||||
2489 | error = got_ref_open(&target_ref, repo, remote_target, | |||
2490 | 0); | |||
2491 | if (error) { | |||
2492 | free(remote_refname); | |||
2493 | free(remote_target); | |||
2494 | if (error->code == GOT_ERR_NOT_REF5) { | |||
2495 | error = NULL((void *)0); | |||
2496 | continue; | |||
2497 | } | |||
2498 | goto done; | |||
2499 | } | |||
2500 | error = update_symref(remote_refname, target_ref, | |||
2501 | verbosity, repo); | |||
2502 | free(remote_refname); | |||
2503 | free(remote_target); | |||
2504 | got_ref_close(target_ref); | |||
2505 | if (error) | |||
2506 | goto done; | |||
2507 | } | |||
2508 | } | |||
2509 | done: | |||
2510 | if (fetchpid > 0) { | |||
2511 | if (kill(fetchpid, SIGTERM15) == -1) | |||
2512 | error = got_error_from_errno("kill"); | |||
2513 | if (waitpid(fetchpid, &fetchstatus, 0) == -1 && error == NULL((void *)0)) | |||
2514 | error = got_error_from_errno("waitpid"); | |||
2515 | } | |||
2516 | if (fetchfd != -1 && close(fetchfd) == -1 && error == NULL((void *)0)) | |||
2517 | error = got_error_from_errno("close"); | |||
2518 | if (repo) | |||
2519 | got_repo_close(repo); | |||
2520 | if (worktree) | |||
2521 | got_worktree_close(worktree); | |||
2522 | TAILQ_FOREACH(pe, &refs, entry)for((pe) = ((&refs)->tqh_first); (pe) != ((void *)0); ( pe) = ((pe)->entry.tqe_next)) { | |||
2523 | free((void *)pe->path); | |||
2524 | free(pe->data); | |||
2525 | } | |||
2526 | got_pathlist_free(&refs); | |||
2527 | TAILQ_FOREACH(pe, &symrefs, entry)for((pe) = ((&symrefs)->tqh_first); (pe) != ((void *)0 ); (pe) = ((pe)->entry.tqe_next)) { | |||
2528 | free((void *)pe->path); | |||
2529 | free(pe->data); | |||
2530 | } | |||
2531 | got_pathlist_free(&symrefs); | |||
2532 | got_pathlist_free(&wanted_branches); | |||
2533 | got_pathlist_free(&wanted_refs); | |||
2534 | free(id_str); | |||
2535 | free(cwd); | |||
2536 | free(repo_path); | |||
2537 | free(pack_hash); | |||
2538 | free(proto); | |||
2539 | free(host); | |||
2540 | free(port); | |||
2541 | free(server_path); | |||
2542 | free(repo_name); | |||
2543 | return error; | |||
2544 | } | |||
2545 | ||||
2546 | ||||
2547 | __dead__attribute__((__noreturn__)) static void | |||
2548 | usage_checkout(void) | |||
2549 | { | |||
2550 | fprintf(stderr(&__sF[2]), "usage: %s checkout [-E] [-b branch] [-c commit] " | |||
2551 | "[-p prefix] repository-path [worktree-path]\n", getprogname()); | |||
2552 | exit(1); | |||
2553 | } | |||
2554 | ||||
2555 | static void | |||
2556 | show_worktree_base_ref_warning(void) | |||
2557 | { | |||
2558 | fprintf(stderr(&__sF[2]), "%s: warning: could not create a reference " | |||
2559 | "to the work tree's base commit; the commit could be " | |||
2560 | "garbage-collected by Git; making the repository " | |||
2561 | "writable and running 'got update' will prevent this\n", | |||
2562 | getprogname()); | |||
2563 | } | |||
2564 | ||||
2565 | struct got_checkout_progress_arg { | |||
2566 | const char *worktree_path; | |||
2567 | int had_base_commit_ref_error; | |||
2568 | }; | |||
2569 | ||||
2570 | static const struct got_error * | |||
2571 | checkout_progress(void *arg, unsigned char status, const char *path) | |||
2572 | { | |||
2573 | struct got_checkout_progress_arg *a = arg; | |||
2574 | ||||
2575 | /* Base commit bump happens silently. */ | |||
2576 | if (status == GOT_STATUS_BUMP_BASE'b') | |||
2577 | return NULL((void *)0); | |||
2578 | ||||
2579 | if (status == GOT_STATUS_BASE_REF_ERR'B') { | |||
2580 | a->had_base_commit_ref_error = 1; | |||
2581 | return NULL((void *)0); | |||
2582 | } | |||
2583 | ||||
2584 | while (path[0] == '/') | |||
2585 | path++; | |||
2586 | ||||
2587 | printf("%c %s/%s\n", status, a->worktree_path, path); | |||
2588 | return NULL((void *)0); | |||
2589 | } | |||
2590 | ||||
2591 | static const struct got_error * | |||
2592 | check_cancelled(void *arg) | |||
2593 | { | |||
2594 | if (sigint_received || sigpipe_received) | |||
2595 | return got_error(GOT_ERR_CANCELLED49); | |||
2596 | return NULL((void *)0); | |||
2597 | } | |||
2598 | ||||
2599 | static const struct got_error * | |||
2600 | check_linear_ancestry(struct got_object_id *commit_id, | |||
2601 | struct got_object_id *base_commit_id, int allow_forwards_in_time_only, | |||
2602 | struct got_repository *repo) | |||
2603 | { | |||
2604 | const struct got_error *err = NULL((void *)0); | |||
2605 | struct got_object_id *yca_id; | |||
2606 | ||||
2607 | err = got_commit_graph_find_youngest_common_ancestor(&yca_id, | |||
2608 | commit_id, base_commit_id, repo, check_cancelled, NULL((void *)0)); | |||
2609 | if (err) | |||
2610 | return err; | |||
2611 | ||||
2612 | if (yca_id == NULL((void *)0)) | |||
2613 | return got_error(GOT_ERR_ANCESTRY55); | |||
2614 | ||||
2615 | /* | |||
2616 | * Require a straight line of history between the target commit | |||
2617 | * and the work tree's base commit. | |||
2618 | * | |||
2619 | * Non-linear situations such as this require a rebase: | |||
2620 | * | |||
2621 | * (commit) D F (base_commit) | |||
2622 | * \ / | |||
2623 | * C E | |||
2624 | * \ / | |||
2625 | * B (yca) | |||
2626 | * | | |||
2627 | * A | |||
2628 | * | |||
2629 | * 'got update' only handles linear cases: | |||
2630 | * Update forwards in time: A (base/yca) - B - C - D (commit) | |||
2631 | * Update backwards in time: D (base) - C - B - A (commit/yca) | |||
2632 | */ | |||
2633 | if (allow_forwards_in_time_only) { | |||
2634 | if (got_object_id_cmp(base_commit_id, yca_id) != 0) | |||
2635 | return got_error(GOT_ERR_ANCESTRY55); | |||
2636 | } else if (got_object_id_cmp(commit_id, yca_id) != 0 && | |||
2637 | got_object_id_cmp(base_commit_id, yca_id) != 0) | |||
2638 | return got_error(GOT_ERR_ANCESTRY55); | |||
2639 | ||||
2640 | free(yca_id); | |||
2641 | return NULL((void *)0); | |||
2642 | } | |||
2643 | ||||
2644 | static const struct got_error * | |||
2645 | check_same_branch(struct got_object_id *commit_id, | |||
2646 | struct got_reference *head_ref, struct got_object_id *yca_id, | |||
2647 | struct got_repository *repo) | |||
2648 | { | |||
2649 | const struct got_error *err = NULL((void *)0); | |||
2650 | struct got_commit_graph *graph = NULL((void *)0); | |||
2651 | struct got_object_id *head_commit_id = NULL((void *)0); | |||
2652 | int is_same_branch = 0; | |||
2653 | ||||
2654 | err = got_ref_resolve(&head_commit_id, repo, head_ref); | |||
2655 | if (err) | |||
2656 | goto done; | |||
2657 | ||||
2658 | if (got_object_id_cmp(head_commit_id, commit_id) == 0) { | |||
2659 | is_same_branch = 1; | |||
2660 | goto done; | |||
2661 | } | |||
2662 | if (yca_id && got_object_id_cmp(commit_id, yca_id) == 0) { | |||
2663 | is_same_branch = 1; | |||
2664 | goto done; | |||
2665 | } | |||
2666 | ||||
2667 | err = got_commit_graph_open(&graph, "/", 1); | |||
2668 | if (err) | |||
2669 | goto done; | |||
2670 | ||||
2671 | err = got_commit_graph_iter_start(graph, head_commit_id, repo, | |||
2672 | check_cancelled, NULL((void *)0)); | |||
2673 | if (err) | |||
2674 | goto done; | |||
2675 | ||||
2676 | for (;;) { | |||
2677 | struct got_object_id *id; | |||
2678 | err = got_commit_graph_iter_next(&id, graph, repo, | |||
2679 | check_cancelled, NULL((void *)0)); | |||
2680 | if (err) { | |||
2681 | if (err->code == GOT_ERR_ITER_COMPLETED46) | |||
2682 | err = NULL((void *)0); | |||
2683 | break; | |||
2684 | } | |||
2685 | ||||
2686 | if (id) { | |||
2687 | if (yca_id && got_object_id_cmp(id, yca_id) == 0) | |||
2688 | break; | |||
2689 | if (got_object_id_cmp(id, commit_id) == 0) { | |||
2690 | is_same_branch = 1; | |||
2691 | break; | |||
2692 | } | |||
2693 | } | |||
2694 | } | |||
2695 | done: | |||
2696 | if (graph) | |||
2697 | got_commit_graph_close(graph); | |||
2698 | free(head_commit_id); | |||
2699 | if (!err && !is_same_branch) | |||
2700 | err = got_error(GOT_ERR_ANCESTRY55); | |||
2701 | return err; | |||
2702 | } | |||
2703 | ||||
2704 | static const struct got_error * | |||
2705 | checkout_ancestry_error(struct got_reference *ref, const char *commit_id_str) | |||
2706 | { | |||
2707 | static char msg[512]; | |||
2708 | const char *branch_name; | |||
2709 | ||||
2710 | if (got_ref_is_symbolic(ref)) | |||
2711 | branch_name = got_ref_get_symref_target(ref); | |||
2712 | else | |||
2713 | branch_name = got_ref_get_name(ref); | |||
2714 | ||||
2715 | if (strncmp("refs/heads/", branch_name, 11) == 0) | |||
2716 | branch_name += 11; | |||
2717 | ||||
2718 | snprintf(msg, sizeof(msg), | |||
2719 | "target commit is not contained in branch '%s'; " | |||
2720 | "the branch to use must be specified with -b; " | |||
2721 | "if necessary a new branch can be created for " | |||
2722 | "this commit with 'got branch -c %s BRANCH_NAME'", | |||
2723 | branch_name, commit_id_str); | |||
2724 | ||||
2725 | return got_error_msg(GOT_ERR_ANCESTRY55, msg); | |||
2726 | } | |||
2727 | ||||
2728 | static const struct got_error * | |||
2729 | cmd_checkout(int argc, char *argv[]) | |||
2730 | { | |||
2731 | const struct got_error *error = NULL((void *)0); | |||
2732 | struct got_repository *repo = NULL((void *)0); | |||
2733 | struct got_reference *head_ref = NULL((void *)0); | |||
2734 | struct got_worktree *worktree = NULL((void *)0); | |||
2735 | char *repo_path = NULL((void *)0); | |||
2736 | char *worktree_path = NULL((void *)0); | |||
2737 | const char *path_prefix = ""; | |||
2738 | const char *branch_name = GOT_REF_HEAD"HEAD"; | |||
2739 | char *commit_id_str = NULL((void *)0); | |||
2740 | char *cwd = NULL((void *)0); | |||
2741 | int ch, same_path_prefix, allow_nonempty = 0; | |||
2742 | struct got_pathlist_head paths; | |||
2743 | struct got_checkout_progress_arg cpa; | |||
2744 | ||||
2745 | TAILQ_INIT(&paths)do { (&paths)->tqh_first = ((void *)0); (&paths)-> tqh_last = &(&paths)->tqh_first; } while (0); | |||
2746 | ||||
2747 | while ((ch = getopt(argc, argv, "b:c:Ep:")) != -1) { | |||
2748 | switch (ch) { | |||
2749 | case 'b': | |||
2750 | branch_name = optarg; | |||
2751 | break; | |||
2752 | case 'c': | |||
2753 | commit_id_str = strdup(optarg); | |||
2754 | if (commit_id_str == NULL((void *)0)) | |||
2755 | return got_error_from_errno("strdup"); | |||
2756 | break; | |||
2757 | case 'E': | |||
2758 | allow_nonempty = 1; | |||
2759 | break; | |||
2760 | case 'p': | |||
2761 | path_prefix = optarg; | |||
2762 | break; | |||
2763 | default: | |||
2764 | usage_checkout(); | |||
2765 | /* NOTREACHED */ | |||
2766 | } | |||
2767 | } | |||
2768 | ||||
2769 | argc -= optind; | |||
2770 | argv += optind; | |||
2771 | ||||
2772 | #ifndef PROFILE | |||
2773 | if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd " | |||
2774 | "unveil", NULL((void *)0)) == -1) | |||
2775 | err(1, "pledge"); | |||
2776 | #endif | |||
2777 | if (argc == 1) { | |||
2778 | char *base, *dotgit; | |||
2779 | const char *path; | |||
2780 | repo_path = realpath(argv[0], NULL((void *)0)); | |||
2781 | if (repo_path == NULL((void *)0)) | |||
2782 | return got_error_from_errno2("realpath", argv[0]); | |||
2783 | cwd = getcwd(NULL((void *)0), 0); | |||
2784 | if (cwd == NULL((void *)0)) { | |||
2785 | error = got_error_from_errno("getcwd"); | |||
2786 | goto done; | |||
2787 | } | |||
2788 | if (path_prefix[0]) | |||
2789 | path = path_prefix; | |||
2790 | else | |||
2791 | path = repo_path; | |||
2792 | error = got_path_basename(&base, path); | |||
2793 | if (error) | |||
2794 | goto done; | |||
2795 | dotgit = strstr(base, ".git"); | |||
2796 | if (dotgit) | |||
2797 | *dotgit = '\0'; | |||
2798 | if (asprintf(&worktree_path, "%s/%s", cwd, base) == -1) { | |||
2799 | error = got_error_from_errno("asprintf"); | |||
2800 | free(base); | |||
2801 | goto done; | |||
2802 | } | |||
2803 | free(base); | |||
2804 | } else if (argc == 2) { | |||
2805 | repo_path = realpath(argv[0], NULL((void *)0)); | |||
2806 | if (repo_path == NULL((void *)0)) { | |||
2807 | error = got_error_from_errno2("realpath", argv[0]); | |||
2808 | goto done; | |||
2809 | } | |||
2810 | worktree_path = realpath(argv[1], NULL((void *)0)); | |||
2811 | if (worktree_path == NULL((void *)0)) { | |||
2812 | if (errno(*__errno()) != ENOENT2) { | |||
2813 | error = got_error_from_errno2("realpath", | |||
2814 | argv[1]); | |||
2815 | goto done; | |||
2816 | } | |||
2817 | worktree_path = strdup(argv[1]); | |||
2818 | if (worktree_path == NULL((void *)0)) { | |||
2819 | error = got_error_from_errno("strdup"); | |||
2820 | goto done; | |||
2821 | } | |||
2822 | } | |||
2823 | } else | |||
2824 | usage_checkout(); | |||
2825 | ||||
2826 | got_path_strip_trailing_slashes(repo_path); | |||
2827 | got_path_strip_trailing_slashes(worktree_path); | |||
2828 | ||||
2829 | error = got_repo_open(&repo, repo_path, NULL((void *)0)); | |||
2830 | if (error != NULL((void *)0)) | |||
2831 | goto done; | |||
2832 | ||||
2833 | /* Pre-create work tree path for unveil(2) */ | |||
2834 | error = got_path_mkdir(worktree_path); | |||
2835 | if (error) { | |||
2836 | if (!(error->code == GOT_ERR_ERRNO1 && errno(*__errno()) == EISDIR21) && | |||
2837 | !(error->code == GOT_ERR_ERRNO1 && errno(*__errno()) == EEXIST17)) | |||
2838 | goto done; | |||
2839 | if (!allow_nonempty && | |||
2840 | !got_path_dir_is_empty(worktree_path)) { | |||
2841 | error = got_error_path(worktree_path, | |||
2842 | GOT_ERR_DIR_NOT_EMPTY75); | |||
2843 | goto done; | |||
2844 | } | |||
2845 | } | |||
2846 | ||||
2847 | error = apply_unveil(got_repo_get_path(repo), 0, worktree_path); | |||
2848 | if (error) | |||
2849 | goto done; | |||
2850 | ||||
2851 | error = got_ref_open(&head_ref, repo, branch_name, 0); | |||
2852 | if (error != NULL((void *)0)) | |||
2853 | goto done; | |||
2854 | ||||
2855 | error = got_worktree_init(worktree_path, head_ref, path_prefix, repo); | |||
2856 | if (error != NULL((void *)0) && !(error->code == GOT_ERR_ERRNO1 && errno(*__errno()) == EEXIST17)) | |||
2857 | goto done; | |||
2858 | ||||
2859 | error = got_worktree_open(&worktree, worktree_path); | |||
2860 | if (error != NULL((void *)0)) | |||
2861 | goto done; | |||
2862 | ||||
2863 | error = got_worktree_match_path_prefix(&same_path_prefix, worktree, | |||
2864 | path_prefix); | |||
2865 | if (error != NULL((void *)0)) | |||
2866 | goto done; | |||
2867 | if (!same_path_prefix) { | |||
2868 | error = got_error(GOT_ERR_PATH_PREFIX54); | |||
2869 | goto done; | |||
2870 | } | |||
2871 | ||||
2872 | if (commit_id_str) { | |||
2873 | struct got_object_id *commit_id; | |||
2874 | struct got_reflist_head refs; | |||
2875 | TAILQ_INIT(&refs)do { (&refs)->tqh_first = ((void *)0); (&refs)-> tqh_last = &(&refs)->tqh_first; } while (0); | |||
2876 | error = got_ref_list(&refs, repo, NULL((void *)0), got_ref_cmp_by_name, | |||
2877 | NULL((void *)0)); | |||
2878 | if (error) | |||
2879 | goto done; | |||
2880 | error = got_repo_match_object_id(&commit_id, NULL((void *)0), | |||
2881 | commit_id_str, GOT_OBJ_TYPE_COMMIT1, &refs, repo); | |||
2882 | got_ref_list_free(&refs); | |||
2883 | if (error) | |||
2884 | goto done; | |||
2885 | error = check_linear_ancestry(commit_id, | |||
2886 | got_worktree_get_base_commit_id(worktree), 0, repo); | |||
2887 | if (error != NULL((void *)0)) { | |||
2888 | free(commit_id); | |||
2889 | if (error->code == GOT_ERR_ANCESTRY55) { | |||
2890 | error = checkout_ancestry_error( | |||
2891 | head_ref, commit_id_str); | |||
2892 | } | |||
2893 | goto done; | |||
2894 | } | |||
2895 | error = check_same_branch(commit_id, head_ref, NULL((void *)0), repo); | |||
2896 | if (error) { | |||
2897 | if (error->code == GOT_ERR_ANCESTRY55) { | |||
2898 | error = checkout_ancestry_error( | |||
2899 | head_ref, commit_id_str); | |||
2900 | } | |||
2901 | goto done; | |||
2902 | } | |||
2903 | error = got_worktree_set_base_commit_id(worktree, repo, | |||
2904 | commit_id); | |||
2905 | free(commit_id); | |||
2906 | if (error) | |||
2907 | goto done; | |||
2908 | } | |||
2909 | ||||
2910 | error = got_pathlist_append(&paths, "", NULL((void *)0)); | |||
2911 | if (error) | |||
2912 | goto done; | |||
2913 | cpa.worktree_path = worktree_path; | |||
2914 | cpa.had_base_commit_ref_error = 0; | |||
2915 | error = got_worktree_checkout_files(worktree, &paths, repo, | |||
2916 | checkout_progress, &cpa, check_cancelled, NULL((void *)0)); | |||
2917 | if (error != NULL((void *)0)) | |||
2918 | goto done; | |||
2919 | ||||
2920 | printf("Now shut up and hack\n"); | |||
2921 | if (cpa.had_base_commit_ref_error) | |||
2922 | show_worktree_base_ref_warning(); | |||
2923 | done: | |||
2924 | got_pathlist_free(&paths); | |||
2925 | free(commit_id_str); | |||
2926 | free(repo_path); | |||
2927 | free(worktree_path); | |||
2928 | free(cwd); | |||
2929 | return error; | |||
2930 | } | |||
2931 | ||||
2932 | struct got_update_progress_arg { | |||
2933 | int did_something; | |||
2934 | int conflicts; | |||
2935 | int obstructed; | |||
2936 | int not_updated; | |||
2937 | }; | |||
2938 | ||||
2939 | void | |||
2940 | print_update_progress_stats(struct got_update_progress_arg *upa) | |||
2941 | { | |||
2942 | if (!upa->did_something) | |||
2943 | return; | |||
2944 | ||||
2945 | if (upa->conflicts > 0) | |||
2946 | printf("Files with new merge conflicts: %d\n", upa->conflicts); | |||
2947 | if (upa->obstructed > 0) | |||
2948 | printf("File paths obstructed by a non-regular file: %d\n", | |||
2949 | upa->obstructed); | |||
2950 | if (upa->not_updated > 0) | |||
2951 | printf("Files not updated because of existing merge " | |||
2952 | "conflicts: %d\n", upa->not_updated); | |||
2953 | } | |||
2954 | ||||
2955 | __dead__attribute__((__noreturn__)) static void | |||
2956 | usage_update(void) | |||
2957 | { | |||
2958 | fprintf(stderr(&__sF[2]), "usage: %s update [-b branch] [-c commit] [path ...]\n", | |||
2959 | getprogname()); | |||
2960 | exit(1); | |||
2961 | } | |||
2962 | ||||
2963 | static const struct got_error * | |||
2964 | update_progress(void *arg, unsigned char status, const char *path) | |||
2965 | { | |||
2966 | struct got_update_progress_arg *upa = arg; | |||
2967 | ||||
2968 | if (status == GOT_STATUS_EXISTS'E' || | |||
2969 | status == GOT_STATUS_BASE_REF_ERR'B') | |||
2970 | return NULL((void *)0); | |||
2971 | ||||
2972 | upa->did_something = 1; | |||
2973 | ||||
2974 | /* Base commit bump happens silently. */ | |||
2975 | if (status == GOT_STATUS_BUMP_BASE'b') | |||
2976 | return NULL((void *)0); | |||
2977 | ||||
2978 | if (status == GOT_STATUS_CONFLICT'C') | |||
2979 | upa->conflicts++; | |||
2980 | if (status == GOT_STATUS_OBSTRUCTED'~') | |||
2981 | upa->obstructed++; | |||
2982 | if (status == GOT_STATUS_CANNOT_UPDATE'#') | |||
2983 | upa->not_updated++; | |||
2984 | ||||
2985 | while (path[0] == '/') | |||
2986 | path++; | |||
2987 | printf("%c %s\n", status, path); | |||
2988 | return NULL((void *)0); | |||
2989 | } | |||
2990 | ||||
2991 | static const struct got_error * | |||
2992 | switch_head_ref(struct got_reference *head_ref, | |||
2993 | struct got_object_id *commit_id, struct got_worktree *worktree, | |||
2994 | struct got_repository *repo) | |||
2995 | { | |||
2996 | const struct got_error *err = NULL((void *)0); | |||
2997 | char *base_id_str; | |||
2998 | int ref_has_moved = 0; | |||
2999 | ||||
3000 | /* Trivial case: switching between two different references. */ | |||
3001 | if (strcmp(got_ref_get_name(head_ref), | |||
3002 | got_worktree_get_head_ref_name(worktree)) != 0) { | |||
3003 | printf("Switching work tree from %s to %s\n", | |||
3004 | got_worktree_get_head_ref_name(worktree), | |||
3005 | got_ref_get_name(head_ref)); | |||
3006 | return got_worktree_set_head_ref(worktree, head_ref); | |||
3007 | } | |||
3008 | ||||
3009 | err = check_linear_ancestry(commit_id, | |||
3010 | got_worktree_get_base_commit_id(worktree), 0, repo); | |||
3011 | if (err) { | |||
3012 | if (err->code != GOT_ERR_ANCESTRY55) | |||
3013 | return err; | |||
3014 | ref_has_moved = 1; | |||
3015 | } | |||
3016 | if (!ref_has_moved) | |||
3017 | return NULL((void *)0); | |||
3018 | ||||
3019 | /* Switching to a rebased branch with the same reference name. */ | |||
3020 | err = got_object_id_str(&base_id_str, | |||
3021 | got_worktree_get_base_commit_id(worktree)); | |||
3022 | if (err) | |||
3023 | return err; | |||
3024 | printf("Reference %s now points at a different branch\n", | |||
3025 | got_worktree_get_head_ref_name(worktree)); | |||
3026 | printf("Switching work tree from %s to %s\n", base_id_str, | |||
3027 | got_worktree_get_head_ref_name(worktree)); | |||
3028 | return NULL((void *)0); | |||
3029 | } | |||
3030 | ||||
3031 | static const struct got_error * | |||
3032 | check_rebase_or_histedit_in_progress(struct got_worktree *worktree) | |||
3033 | { | |||
3034 | const struct got_error *err; | |||
3035 | int in_progress; | |||
3036 | ||||
3037 | err = got_worktree_rebase_in_progress(&in_progress, worktree); | |||
3038 | if (err) | |||
3039 | return err; | |||
3040 | if (in_progress) | |||
3041 | return got_error(GOT_ERR_REBASING88); | |||
3042 | ||||
3043 | err = got_worktree_histedit_in_progress(&in_progress, worktree); | |||
3044 | if (err) | |||
3045 | return err; | |||
3046 | if (in_progress) | |||
3047 | return got_error(GOT_ERR_HISTEDIT_BUSY96); | |||
3048 | ||||
3049 | return NULL((void *)0); | |||
3050 | } | |||
3051 | ||||
3052 | static const struct got_error * | |||
3053 | get_worktree_paths_from_argv(struct got_pathlist_head *paths, int argc, | |||
3054 | char *argv[], struct got_worktree *worktree) | |||
3055 | { | |||
3056 | const struct got_error *err = NULL((void *)0); | |||
3057 | char *path; | |||
3058 | int i; | |||
3059 | ||||
3060 | if (argc == 0) { | |||
3061 | path = strdup(""); | |||
3062 | if (path == NULL((void *)0)) | |||
3063 | return got_error_from_errno("strdup"); | |||
3064 | return got_pathlist_append(paths, path, NULL((void *)0)); | |||
3065 | } | |||
3066 | ||||
3067 | for (i = 0; i < argc; i++) { | |||
3068 | err = got_worktree_resolve_path(&path, worktree, argv[i]); | |||
3069 | if (err) | |||
3070 | break; | |||
3071 | err = got_pathlist_append(paths, path, NULL((void *)0)); | |||
3072 | if (err) { | |||
3073 | free(path); | |||
3074 | break; | |||
3075 | } | |||
3076 | } | |||
3077 | ||||
3078 | return err; | |||
3079 | } | |||
3080 | ||||
3081 | static const struct got_error * | |||
3082 | wrap_not_worktree_error(const struct got_error *orig_err, | |||
3083 | const char *cmdname, const char *path) | |||
3084 | { | |||
3085 | const struct got_error *err; | |||
3086 | struct got_repository *repo; | |||
3087 | static char msg[512]; | |||
3088 | ||||
3089 | err = got_repo_open(&repo, path, NULL((void *)0)); | |||
3090 | if (err) | |||
3091 | return orig_err; | |||
3092 | ||||
3093 | snprintf(msg, sizeof(msg), | |||
3094 | "'got %s' needs a work tree in addition to a git repository\n" | |||
3095 | "Work trees can be checked out from this Git repository with " | |||
3096 | "'got checkout'.\n" | |||
3097 | "The got(1) manual page contains more information.", cmdname); | |||
3098 | err = got_error_msg(GOT_ERR_NOT_WORKTREE60, msg); | |||
3099 | got_repo_close(repo); | |||
3100 | return err; | |||
3101 | } | |||
3102 | ||||
3103 | static const struct got_error * | |||
3104 | cmd_update(int argc, char *argv[]) | |||
3105 | { | |||
3106 | const struct got_error *error = NULL((void *)0); | |||
3107 | struct got_repository *repo = NULL((void *)0); | |||
3108 | struct got_worktree *worktree = NULL((void *)0); | |||
3109 | char *worktree_path = NULL((void *)0); | |||
3110 | struct got_object_id *commit_id = NULL((void *)0); | |||
3111 | char *commit_id_str = NULL((void *)0); | |||
3112 | const char *branch_name = NULL((void *)0); | |||
3113 | struct got_reference *head_ref = NULL((void *)0); | |||
3114 | struct got_pathlist_head paths; | |||
3115 | struct got_pathlist_entry *pe; | |||
3116 | int ch; | |||
3117 | struct got_update_progress_arg upa; | |||
3118 | ||||
3119 | TAILQ_INIT(&paths)do { (&paths)->tqh_first = ((void *)0); (&paths)-> tqh_last = &(&paths)->tqh_first; } while (0); | |||
3120 | ||||
3121 | while ((ch = getopt(argc, argv, "b:c:")) != -1) { | |||
3122 | switch (ch) { | |||
3123 | case 'b': | |||
3124 | branch_name = optarg; | |||
3125 | break; | |||
3126 | case 'c': | |||
3127 | commit_id_str = strdup(optarg); | |||
3128 | if (commit_id_str == NULL((void *)0)) | |||
3129 | return got_error_from_errno("strdup"); | |||
3130 | break; | |||
3131 | default: | |||
3132 | usage_update(); | |||
3133 | /* NOTREACHED */ | |||
3134 | } | |||
3135 | } | |||
3136 | ||||
3137 | argc -= optind; | |||
3138 | argv += optind; | |||
3139 | ||||
3140 | #ifndef PROFILE | |||
3141 | if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd " | |||
3142 | "unveil", NULL((void *)0)) == -1) | |||
3143 | err(1, "pledge"); | |||
3144 | #endif | |||
3145 | worktree_path = getcwd(NULL((void *)0), 0); | |||
3146 | if (worktree_path == NULL((void *)0)) { | |||
3147 | error = got_error_from_errno("getcwd"); | |||
3148 | goto done; | |||
3149 | } | |||
3150 | error = got_worktree_open(&worktree, worktree_path); | |||
3151 | if (error) { | |||
3152 | if (error->code == GOT_ERR_NOT_WORKTREE60) | |||
3153 | error = wrap_not_worktree_error(error, "update", | |||
3154 | worktree_path); | |||
3155 | goto done; | |||
3156 | } | |||
3157 | ||||
3158 | error = check_rebase_or_histedit_in_progress(worktree); | |||
3159 | if (error) | |||
3160 | goto done; | |||
3161 | ||||
3162 | error = got_repo_open(&repo, got_worktree_get_repo_path(worktree), | |||
3163 | NULL((void *)0)); | |||
3164 | if (error != NULL((void *)0)) | |||
3165 | goto done; | |||
3166 | ||||
3167 | error = apply_unveil(got_repo_get_path(repo), 0, | |||
3168 | got_worktree_get_root_path(worktree)); | |||
3169 | if (error) | |||
3170 | goto done; | |||
3171 | ||||
3172 | error = get_worktree_paths_from_argv(&paths, argc, argv, worktree); | |||
3173 | if (error) | |||
3174 | goto done; | |||
3175 | ||||
3176 | error = got_ref_open(&head_ref, repo, branch_name ? branch_name : | |||
3177 | got_worktree_get_head_ref_name(worktree), 0); | |||
3178 | if (error != NULL((void *)0)) | |||
3179 | goto done; | |||
3180 | if (commit_id_str == NULL((void *)0)) { | |||
3181 | error = got_ref_resolve(&commit_id, repo, head_ref); | |||
3182 | if (error != NULL((void *)0)) | |||
3183 | goto done; | |||
3184 | error = got_object_id_str(&commit_id_str, commit_id); | |||
3185 | if (error != NULL((void *)0)) | |||
3186 | goto done; | |||
3187 | } else { | |||
3188 | struct got_reflist_head refs; | |||
3189 | TAILQ_INIT(&refs)do { (&refs)->tqh_first = ((void *)0); (&refs)-> tqh_last = &(&refs)->tqh_first; } while (0); | |||
3190 | error = got_ref_list(&refs, repo, NULL((void *)0), got_ref_cmp_by_name, | |||
3191 | NULL((void *)0)); | |||
3192 | if (error) | |||
3193 | goto done; | |||
3194 | error = got_repo_match_object_id(&commit_id, NULL((void *)0), | |||
3195 | commit_id_str, GOT_OBJ_TYPE_COMMIT1, &refs, repo); | |||
3196 | got_ref_list_free(&refs); | |||
3197 | free(commit_id_str); | |||
3198 | commit_id_str = NULL((void *)0); | |||
3199 | if (error) | |||
3200 | goto done; | |||
3201 | error = got_object_id_str(&commit_id_str, commit_id); | |||
3202 | if (error) | |||
3203 | goto done; | |||
3204 | } | |||
3205 | ||||
3206 | if (branch_name) { | |||
3207 | struct got_object_id *head_commit_id; | |||
3208 | TAILQ_FOREACH(pe, &paths, entry)for((pe) = ((&paths)->tqh_first); (pe) != ((void *)0); (pe) = ((pe)->entry.tqe_next)) { | |||
3209 | if (pe->path_len == 0) | |||
3210 | continue; | |||
3211 | error = got_error_msg(GOT_ERR_BAD_PATH4, | |||
3212 | "switching between branches requires that " | |||
3213 | "the entire work tree gets updated"); | |||
3214 | goto done; | |||
3215 | } | |||
3216 | error = got_ref_resolve(&head_commit_id, repo, head_ref); | |||
3217 | if (error) | |||
3218 | goto done; | |||
3219 | error = check_linear_ancestry(commit_id, head_commit_id, 0, | |||
3220 | repo); | |||
3221 | free(head_commit_id); | |||
3222 | if (error != NULL((void *)0)) | |||
3223 | goto done; | |||
3224 | error = check_same_branch(commit_id, head_ref, NULL((void *)0), repo); | |||
3225 | if (error) | |||
3226 | goto done; | |||
3227 | error = switch_head_ref(head_ref, commit_id, worktree, repo); | |||
3228 | if (error) | |||
3229 | goto done; | |||
3230 | } else { | |||
3231 | error = check_linear_ancestry(commit_id, | |||
3232 | got_worktree_get_base_commit_id(worktree), 0, repo); | |||
3233 | if (error != NULL((void *)0)) { | |||
3234 | if (error->code == GOT_ERR_ANCESTRY55) | |||
3235 | error = got_error(GOT_ERR_BRANCH_MOVED77); | |||
3236 | goto done; | |||
3237 | } | |||
3238 | error = check_same_branch(commit_id, head_ref, NULL((void *)0), repo); | |||
3239 | if (error) | |||
3240 | goto done; | |||
3241 | } | |||
3242 | ||||
3243 | if (got_object_id_cmp(got_worktree_get_base_commit_id(worktree), | |||
3244 | commit_id) != 0) { | |||
3245 | error = got_worktree_set_base_commit_id(worktree, repo, | |||
3246 | commit_id); | |||
3247 | if (error) | |||
3248 | goto done; | |||
3249 | } | |||
3250 | ||||
3251 | memset(&upa, 0, sizeof(upa)); | |||
3252 | error = got_worktree_checkout_files(worktree, &paths, repo, | |||
3253 | update_progress, &upa, check_cancelled, NULL((void *)0)); | |||
3254 | if (error != NULL((void *)0)) | |||
3255 | goto done; | |||
3256 | ||||
3257 | if (upa.did_something) | |||
3258 | printf("Updated to commit %s\n", commit_id_str); | |||
3259 | else | |||
3260 | printf("Already up-to-date\n"); | |||
3261 | print_update_progress_stats(&upa); | |||
3262 | done: | |||
3263 | free(worktree_path); | |||
3264 | TAILQ_FOREACH(pe, &paths, entry)for((pe) = ((&paths)->tqh_first); (pe) != ((void *)0); (pe) = ((pe)->entry.tqe_next)) | |||
3265 | free((char *)pe->path); | |||
3266 | got_pathlist_free(&paths); | |||
3267 | free(commit_id); | |||
3268 | free(commit_id_str); | |||
3269 | return error; | |||
3270 | } | |||
3271 | ||||
3272 | static const struct got_error * | |||
3273 | diff_blobs(struct got_object_id *blob_id1, struct got_object_id *blob_id2, | |||
3274 | const char *path, int diff_context, int ignore_whitespace, | |||
3275 | int force_text_diff, struct got_repository *repo) | |||
3276 | { | |||
3277 | const struct got_error *err = NULL((void *)0); | |||
3278 | struct got_blob_object *blob1 = NULL((void *)0), *blob2 = NULL((void *)0); | |||
3279 | ||||
3280 | if (blob_id1) { | |||
3281 | err = got_object_open_as_blob(&blob1, repo, blob_id1, 8192); | |||
3282 | if (err) | |||
3283 | goto done; | |||
3284 | } | |||
3285 | ||||
3286 | err = got_object_open_as_blob(&blob2, repo, blob_id2, 8192); | |||
3287 | if (err) | |||
3288 | goto done; | |||
3289 | ||||
3290 | while (path[0] == '/') | |||
3291 | path++; | |||
3292 | err = got_diff_blob(NULL((void *)0), NULL((void *)0), blob1, blob2, path, path, | |||
3293 | diff_context, ignore_whitespace, force_text_diff, stdout(&__sF[1])); | |||
3294 | done: | |||
3295 | if (blob1) | |||
3296 | got_object_blob_close(blob1); | |||
3297 | got_object_blob_close(blob2); | |||
3298 | return err; | |||
3299 | } | |||
3300 | ||||
3301 | static const struct got_error * | |||
3302 | diff_trees(struct got_object_id *tree_id1, struct got_object_id *tree_id2, | |||
3303 | const char *path, int diff_context, int ignore_whitespace, | |||
3304 | int force_text_diff, struct got_repository *repo) | |||
3305 | { | |||
3306 | const struct got_error *err = NULL((void *)0); | |||
3307 | struct got_tree_object *tree1 = NULL((void *)0), *tree2 = NULL((void *)0); | |||
3308 | struct got_diff_blob_output_unidiff_arg arg; | |||
3309 | ||||
3310 | if (tree_id1) { | |||
3311 | err = got_object_open_as_tree(&tree1, repo, tree_id1); | |||
3312 | if (err) | |||
3313 | goto done; | |||
3314 | } | |||
3315 | ||||
3316 | err = got_object_open_as_tree(&tree2, repo, tree_id2); | |||
3317 | if (err) | |||
3318 | goto done; | |||
3319 | ||||
3320 | arg.diff_context = diff_context; | |||
3321 | arg.ignore_whitespace = ignore_whitespace; | |||
3322 | arg.force_text_diff = force_text_diff; | |||
3323 | arg.outfile = stdout(&__sF[1]); | |||
3324 | arg.line_offsets = NULL((void *)0); | |||
3325 | arg.nlines = 0; | |||
3326 | while (path[0] == '/') | |||
3327 | path++; | |||
3328 | err = got_diff_tree(tree1, tree2, path, path, repo, | |||
3329 | got_diff_blob_output_unidiff, &arg, 1); | |||
3330 | done: | |||
3331 | if (tree1) | |||
3332 | got_object_tree_close(tree1); | |||
3333 | if (tree2) | |||
3334 | got_object_tree_close(tree2); | |||
3335 | return err; | |||
3336 | } | |||
3337 | ||||
3338 | static const struct got_error * | |||
3339 | get_changed_paths(struct got_pathlist_head *paths, | |||
3340 | struct got_commit_object *commit, struct got_repository *repo) | |||
3341 | { | |||
3342 | const struct got_error *err = NULL((void *)0); | |||
3343 | struct got_object_id *tree_id1 = NULL((void *)0), *tree_id2 = NULL((void *)0); | |||
3344 | struct got_tree_object *tree1 = NULL((void *)0), *tree2 = NULL((void *)0); | |||
3345 | struct got_object_qid *qid; | |||
3346 | ||||
3347 | qid = SIMPLEQ_FIRST(got_object_commit_get_parent_ids(commit))((got_object_commit_get_parent_ids(commit))->sqh_first); | |||
3348 | if (qid != NULL((void *)0)) { | |||
3349 | struct got_commit_object *pcommit; | |||
3350 | err = got_object_open_as_commit(&pcommit, repo, | |||
3351 | qid->id); | |||
3352 | if (err) | |||
3353 | return err; | |||
3354 | ||||
3355 | tree_id1 = got_object_commit_get_tree_id(pcommit); | |||
3356 | got_object_commit_close(pcommit); | |||
3357 | ||||
3358 | } | |||
3359 | ||||
3360 | if (tree_id1) { | |||
3361 | err = got_object_open_as_tree(&tree1, repo, tree_id1); | |||
3362 | if (err) | |||
3363 | goto done; | |||
3364 | } | |||
3365 | ||||
3366 | tree_id2 = got_object_commit_get_tree_id(commit); | |||
3367 | err = got_object_open_as_tree(&tree2, repo, tree_id2); | |||
3368 | if (err) | |||
3369 | goto done; | |||
3370 | ||||
3371 | err = got_diff_tree(tree1, tree2, "", "", repo, | |||
3372 | got_diff_tree_collect_changed_paths, paths, 0); | |||
3373 | done: | |||
3374 | if (tree1) | |||
3375 | got_object_tree_close(tree1); | |||
3376 | if (tree2) | |||
3377 | got_object_tree_close(tree2); | |||
3378 | return err; | |||
3379 | } | |||
3380 | ||||
3381 | static const struct got_error * | |||
3382 | print_patch(struct got_commit_object *commit, struct got_object_id *id, | |||
3383 | const char *path, int diff_context, struct got_repository *repo) | |||
3384 | { | |||
3385 | const struct got_error *err = NULL((void *)0); | |||
3386 | struct got_commit_object *pcommit = NULL((void *)0); | |||
3387 | char *id_str1 = NULL((void *)0), *id_str2 = NULL((void *)0); | |||
3388 | struct got_object_id *obj_id1 = NULL((void *)0), *obj_id2 = NULL((void *)0); | |||
3389 | struct got_object_qid *qid; | |||
3390 | ||||
3391 | qid = SIMPLEQ_FIRST(got_object_commit_get_parent_ids(commit))((got_object_commit_get_parent_ids(commit))->sqh_first); | |||
3392 | if (qid != NULL((void *)0)) { | |||
3393 | err = got_object_open_as_commit(&pcommit, repo, | |||
3394 | qid->id); | |||
3395 | if (err) | |||
3396 | return err; | |||
3397 | } | |||
3398 | ||||
3399 | if (path && path[0] != '\0') { | |||
3400 | int obj_type; | |||
3401 | err = got_object_id_by_path(&obj_id2, repo, id, path); | |||
3402 | if (err) | |||
3403 | goto done; | |||
3404 | err = got_object_id_str(&id_str2, obj_id2); | |||
3405 | if (err) { | |||
3406 | free(obj_id2); | |||
3407 | goto done; | |||
3408 | } | |||
3409 | if (pcommit) { | |||
3410 | err = got_object_id_by_path(&obj_id1, repo, | |||
3411 | qid->id, path); | |||
3412 | if (err) { | |||
3413 | if (err->code != GOT_ERR_NO_TREE_ENTRY50) { | |||
3414 | free(obj_id2); | |||
3415 | goto done; | |||
3416 | } | |||
3417 | } else { | |||
3418 | err = got_object_id_str(&id_str1, obj_id1); | |||
3419 | if (err) { | |||
3420 | free(obj_id2); | |||
3421 | goto done; | |||
3422 | } | |||
3423 | } | |||
3424 | } | |||
3425 | err = got_object_get_type(&obj_type, repo, obj_id2); | |||
3426 | if (err) { | |||
3427 | free(obj_id2); | |||
3428 | goto done; | |||
3429 | } | |||
3430 | printf("diff %s %s\n", id_str1 ? id_str1 : "/dev/null", id_str2); | |||
3431 | switch (obj_type) { | |||
3432 | case GOT_OBJ_TYPE_BLOB3: | |||
3433 | err = diff_blobs(obj_id1, obj_id2, path, diff_context, | |||
3434 | 0, 0, repo); | |||
3435 | break; | |||
3436 | case GOT_OBJ_TYPE_TREE2: | |||
3437 | err = diff_trees(obj_id1, obj_id2, path, diff_context, | |||
3438 | 0, 0, repo); | |||
3439 | break; | |||
3440 | default: | |||
3441 | err = got_error(GOT_ERR_OBJ_TYPE11); | |||
3442 | break; | |||
3443 | } | |||
3444 | free(obj_id1); | |||
3445 | free(obj_id2); | |||
3446 | } else { | |||
3447 | obj_id2 = got_object_commit_get_tree_id(commit); | |||
3448 | err = got_object_id_str(&id_str2, obj_id2); | |||
3449 | if (err) | |||
3450 | goto done; | |||
3451 | if (pcommit) { | |||
3452 | obj_id1 = got_object_commit_get_tree_id(pcommit); | |||
3453 | err = got_object_id_str(&id_str1, obj_id1); | |||
3454 | if (err) | |||
3455 | goto done; | |||
3456 | } | |||
3457 | printf("diff %s %s\n", id_str1 ? id_str1 : "/dev/null", | |||
3458 | id_str2); | |||
3459 | err = diff_trees(obj_id1, obj_id2, "", diff_context, 0, 0, | |||
3460 | repo); | |||
3461 | } | |||
3462 | done: | |||
3463 | free(id_str1); | |||
3464 | free(id_str2); | |||
3465 | if (pcommit) | |||
3466 | got_object_commit_close(pcommit); | |||
3467 | return err; | |||
3468 | } | |||
3469 | ||||
3470 | static char * | |||
3471 | get_datestr(time_t *time, char *datebuf) | |||
3472 | { | |||
3473 | struct tm mytm, *tm; | |||
3474 | char *p, *s; | |||
3475 | ||||
3476 | tm = gmtime_r(time, &mytm); | |||
3477 | if (tm == NULL((void *)0)) | |||
3478 | return NULL((void *)0); | |||
3479 | s = asctime_r(tm, datebuf); | |||
3480 | if (s == NULL((void *)0)) | |||
3481 | return NULL((void *)0); | |||
3482 | p = strchr(s, '\n'); | |||
3483 | if (p) | |||
3484 | *p = '\0'; | |||
3485 | return s; | |||
3486 | } | |||
3487 | ||||
3488 | static const struct got_error * | |||
3489 | match_logmsg(int *have_match, struct got_object_id *id, | |||
3490 | struct got_commit_object *commit, regex_t *regex) | |||
3491 | { | |||
3492 | const struct got_error *err = NULL((void *)0); | |||
3493 | regmatch_t regmatch; | |||
3494 | char *id_str = NULL((void *)0), *logmsg = NULL((void *)0); | |||
3495 | ||||
3496 | *have_match = 0; | |||
3497 | ||||
3498 | err = got_object_id_str(&id_str, id); | |||
3499 | if (err) | |||
3500 | return err; | |||
3501 | ||||
3502 | err = got_object_commit_get_logmsg(&logmsg, commit); | |||
3503 | if (err) | |||
3504 | goto done; | |||
3505 | ||||
3506 | if (regexec(regex, logmsg, 1, ®match, 0) == 0) | |||
3507 | *have_match = 1; | |||
3508 | done: | |||
3509 | free(id_str); | |||
3510 | free(logmsg); | |||
3511 | return err; | |||
3512 | } | |||
3513 | ||||
3514 | static void | |||
3515 | match_changed_paths(int *have_match, struct got_pathlist_head *changed_paths, | |||
3516 | regex_t *regex) | |||
3517 | { | |||
3518 | regmatch_t regmatch; | |||
3519 | struct got_pathlist_entry *pe; | |||
3520 | ||||
3521 | *have_match = 0; | |||
3522 | ||||
3523 | TAILQ_FOREACH(pe, changed_paths, entry)for((pe) = ((changed_paths)->tqh_first); (pe) != ((void *) 0); (pe) = ((pe)->entry.tqe_next)) { | |||
3524 | if (regexec(regex, pe->path, 1, ®match, 0) == 0) { | |||
3525 | *have_match = 1; | |||
3526 | break; | |||
3527 | } | |||
3528 | } | |||
3529 | } | |||
3530 | ||||
3531 | #define GOT_COMMIT_SEP_STR"-----------------------------------------------\n" "-----------------------------------------------\n" | |||
3532 | ||||
3533 | static const struct got_error* | |||
3534 | build_refs_str(char **refs_str, struct got_reflist_head *refs, | |||
3535 | struct got_object_id *id, struct got_repository *repo) | |||
3536 | { | |||
3537 | static const struct got_error *err = NULL((void *)0); | |||
3538 | struct got_reflist_entry *re; | |||
3539 | char *s; | |||
3540 | const char *name; | |||
3541 | ||||
3542 | *refs_str = NULL((void *)0); | |||
3543 | ||||
3544 | TAILQ_FOREACH(re, refs, entry)for((re) = ((refs)->tqh_first); (re) != ((void *)0); (re) = ((re)->entry.tqe_next)) { | |||
3545 | struct got_tag_object *tag = NULL((void *)0); | |||
3546 | struct got_object_id *ref_id; | |||
3547 | int cmp; | |||
3548 | ||||
3549 | name = got_ref_get_name(re->ref); | |||
3550 | if (strcmp(name, GOT_REF_HEAD"HEAD") == 0) | |||
3551 | continue; | |||
3552 | if (strncmp(name, "refs/", 5) == 0) | |||
3553 | name += 5; | |||
3554 | if (strncmp(name, "got/", 4) == 0) | |||
3555 | continue; | |||
3556 | if (strncmp(name, "heads/", 6) == 0) | |||
3557 | name += 6; | |||
3558 | if (strncmp(name, "remotes/", 8) == 0) { | |||
3559 | name += 8; | |||
3560 | s = strstr(name, "/" GOT_REF_HEAD"HEAD"); | |||
3561 | if (s != NULL((void *)0) && s[strlen(s)] == '\0') | |||
3562 | continue; | |||
3563 | } | |||
3564 | err = got_ref_resolve(&ref_id, repo, re->ref); | |||
3565 | if (err) | |||
3566 | break; | |||
3567 | if (strncmp(name, "tags/", 5) == 0) { | |||
3568 | err = got_object_open_as_tag(&tag, repo, ref_id); | |||
3569 | if (err) { | |||
3570 | if (err->code != GOT_ERR_OBJ_TYPE11) { | |||
3571 | free(ref_id); | |||
3572 | break; | |||
3573 | } | |||
3574 | /* Ref points at something other than a tag. */ | |||
3575 | err = NULL((void *)0); | |||
3576 | tag = NULL((void *)0); | |||
3577 | } | |||
3578 | } | |||
3579 | cmp = got_object_id_cmp(tag ? | |||
3580 | got_object_tag_get_object_id(tag) : ref_id, id); | |||
3581 | free(ref_id); | |||
3582 | if (tag) | |||
3583 | got_object_tag_close(tag); | |||
3584 | if (cmp != 0) | |||
3585 | continue; | |||
3586 | s = *refs_str; | |||
3587 | if (asprintf(refs_str, "%s%s%s", s ? s : "", | |||
3588 | s ? ", " : "", name) == -1) { | |||
3589 | err = got_error_from_errno("asprintf"); | |||
3590 | free(s); | |||
3591 | *refs_str = NULL((void *)0); | |||
3592 | break; | |||
3593 | } | |||
3594 | free(s); | |||
3595 | } | |||
3596 | ||||
3597 | return err; | |||
3598 | } | |||
3599 | ||||
3600 | static const struct got_error * | |||
3601 | print_commit(struct got_commit_object *commit, struct got_object_id *id, | |||
3602 | struct got_repository *repo, const char *path, | |||
3603 | struct got_pathlist_head *changed_paths, int show_patch, | |||
3604 | int diff_context, struct got_reflist_object_id_map *refs_idmap, | |||
3605 | const char *custom_refs_str) | |||
3606 | { | |||
3607 | const struct got_error *err = NULL((void *)0); | |||
3608 | char *id_str, *datestr, *logmsg0, *logmsg, *line; | |||
3609 | char datebuf[26]; | |||
3610 | time_t committer_time; | |||
3611 | const char *author, *committer; | |||
3612 | char *refs_str = NULL((void *)0); | |||
3613 | ||||
3614 | err = got_object_id_str(&id_str, id); | |||
3615 | if (err) | |||
3616 | return err; | |||
3617 | ||||
3618 | if (custom_refs_str == NULL((void *)0)) { | |||
3619 | struct got_reflist_head *refs; | |||
3620 | refs = got_reflist_object_id_map_lookup(refs_idmap, id); | |||
3621 | if (refs) { | |||
3622 | err = build_refs_str(&refs_str, refs, id, repo); | |||
3623 | if (err) | |||
3624 | goto done; | |||
3625 | } | |||
3626 | } | |||
3627 | ||||
3628 | printf(GOT_COMMIT_SEP_STR"-----------------------------------------------\n"); | |||
3629 | if (custom_refs_str) | |||
3630 | printf("commit %s (%s)\n", id_str, custom_refs_str); | |||
3631 | else | |||
3632 | printf("commit %s%s%s%s\n", id_str, refs_str ? " (" : "", | |||
3633 | refs_str ? refs_str : "", refs_str ? ")" : ""); | |||
3634 | free(id_str); | |||
3635 | id_str = NULL((void *)0); | |||
3636 | free(refs_str); | |||
3637 | refs_str = NULL((void *)0); | |||
3638 | printf("from: %s\n", got_object_commit_get_author(commit)); | |||
3639 | committer_time = got_object_commit_get_committer_time(commit); | |||
3640 | datestr = get_datestr(&committer_time, datebuf); | |||
3641 | if (datestr) | |||
3642 | printf("date: %s UTC\n", datestr); | |||
3643 | author = got_object_commit_get_author(commit); | |||
3644 | committer = got_object_commit_get_committer(commit); | |||
3645 | if (strcmp(author, committer) != 0) | |||
3646 | printf("via: %s\n", committer); | |||
3647 | if (got_object_commit_get_nparents(commit) > 1) { | |||
3648 | const struct got_object_id_queue *parent_ids; | |||
3649 | struct got_object_qid *qid; | |||
3650 | int n = 1; | |||
3651 | parent_ids = got_object_commit_get_parent_ids(commit); | |||
3652 | SIMPLEQ_FOREACH(qid, parent_ids, entry)for((qid) = ((parent_ids)->sqh_first); (qid) != ((void *)0 ); (qid) = ((qid)->entry.sqe_next)) { | |||
3653 | err = got_object_id_str(&id_str, qid->id); | |||
3654 | if (err) | |||
3655 | goto done; | |||
3656 | printf("parent %d: %s\n", n++, id_str); | |||
3657 | free(id_str); | |||
3658 | id_str = NULL((void *)0); | |||
3659 | } | |||
3660 | } | |||
3661 | ||||
3662 | err = got_object_commit_get_logmsg(&logmsg0, commit); | |||
3663 | if (err) | |||
3664 | goto done; | |||
3665 | ||||
3666 | logmsg = logmsg0; | |||
3667 | do { | |||
3668 | line = strsep(&logmsg, "\n"); | |||
3669 | if (line) | |||
3670 | printf(" %s\n", line); | |||
3671 | } while (line); | |||
3672 | free(logmsg0); | |||
3673 | ||||
3674 | if (changed_paths) { | |||
3675 | struct got_pathlist_entry *pe; | |||
3676 | TAILQ_FOREACH(pe, changed_paths, entry)for((pe) = ((changed_paths)->tqh_first); (pe) != ((void *) 0); (pe) = ((pe)->entry.tqe_next)) { | |||
3677 | struct got_diff_changed_path *cp = pe->data; | |||
3678 | printf(" %c %s\n", cp->status, pe->path); | |||
3679 | } | |||
3680 | printf("\n"); | |||
3681 | } | |||
3682 | if (show_patch) { | |||
3683 | err = print_patch(commit, id, path, diff_context, repo); | |||
3684 | if (err == 0) | |||
3685 | printf("\n"); | |||
3686 | } | |||
3687 | ||||
3688 | if (fflush(stdout(&__sF[1])) != 0 && err == NULL((void *)0)) | |||
3689 | err = got_error_from_errno("fflush"); | |||
3690 | done: | |||
3691 | free(id_str); | |||
3692 | free(refs_str); | |||
3693 | return err; | |||
3694 | } | |||
3695 | ||||
3696 | static const struct got_error * | |||
3697 | print_commits(struct got_object_id *root_id, struct got_object_id *end_id, | |||
3698 | struct got_repository *repo, const char *path, int show_changed_paths, | |||
3699 | int show_patch, const char *search_pattern, int diff_context, int limit, | |||
3700 | int log_branches, int reverse_display_order, | |||
3701 | struct got_reflist_object_id_map *refs_idmap) | |||
3702 | { | |||
3703 | const struct got_error *err; | |||
3704 | struct got_commit_graph *graph; | |||
3705 | regex_t regex; | |||
3706 | int have_match; | |||
3707 | struct got_object_id_queue reversed_commits; | |||
3708 | struct got_object_qid *qid; | |||
3709 | struct got_commit_object *commit; | |||
3710 | struct got_pathlist_head changed_paths; | |||
3711 | struct got_pathlist_entry *pe; | |||
3712 | ||||
3713 | SIMPLEQ_INIT(&reversed_commits)do { (&reversed_commits)->sqh_first = ((void *)0); (& reversed_commits)->sqh_last = &(&reversed_commits) ->sqh_first; } while (0); | |||
3714 | TAILQ_INIT(&changed_paths)do { (&changed_paths)->tqh_first = ((void *)0); (& changed_paths)->tqh_last = &(&changed_paths)->tqh_first ; } while (0); | |||
3715 | ||||
3716 | if (search_pattern && regcomp(®ex, search_pattern, | |||
3717 | REG_EXTENDED0001 | REG_NOSUB0004 | REG_NEWLINE0010)) | |||
3718 | return got_error_msg(GOT_ERR_REGEX112, search_pattern); | |||
3719 | ||||
3720 | err = got_commit_graph_open(&graph, path, !log_branches); | |||
3721 | if (err) | |||
3722 | return err; | |||
3723 | err = got_commit_graph_iter_start(graph, root_id, repo, | |||
3724 | check_cancelled, NULL((void *)0)); | |||
3725 | if (err) | |||
3726 | goto done; | |||
3727 | for (;;) { | |||
3728 | struct got_object_id *id; | |||
3729 | ||||
3730 | if (sigint_received || sigpipe_received) | |||
3731 | break; | |||
3732 | ||||
3733 | err = got_commit_graph_iter_next(&id, graph, repo, | |||
3734 | check_cancelled, NULL((void *)0)); | |||
3735 | if (err) { | |||
3736 | if (err->code == GOT_ERR_ITER_COMPLETED46) | |||
3737 | err = NULL((void *)0); | |||
3738 | break; | |||
3739 | } | |||
3740 | if (id == NULL((void *)0)) | |||
3741 | break; | |||
3742 | ||||
3743 | err = got_object_open_as_commit(&commit, repo, id); | |||
3744 | if (err) | |||
3745 | break; | |||
3746 | ||||
3747 | if (show_changed_paths && !reverse_display_order) { | |||
3748 | err = get_changed_paths(&changed_paths, commit, repo); | |||
3749 | if (err) | |||
3750 | break; | |||
3751 | } | |||
3752 | ||||
3753 | if (search_pattern) { | |||
3754 | err = match_logmsg(&have_match, id, commit, ®ex); | |||
3755 | if (err) { | |||
3756 | got_object_commit_close(commit); | |||
3757 | break; | |||
3758 | } | |||
3759 | if (have_match == 0 && show_changed_paths) | |||
3760 | match_changed_paths(&have_match, | |||
3761 | &changed_paths, ®ex); | |||
3762 | if (have_match == 0) { | |||
3763 | got_object_commit_close(commit); | |||
3764 | TAILQ_FOREACH(pe, &changed_paths, entry)for((pe) = ((&changed_paths)->tqh_first); (pe) != ((void *)0); (pe) = ((pe)->entry.tqe_next)) { | |||
3765 | free((char *)pe->path); | |||
3766 | free(pe->data); | |||
3767 | } | |||
3768 | got_pathlist_free(&changed_paths); | |||
3769 | continue; | |||
3770 | } | |||
3771 | } | |||
3772 | ||||
3773 | if (reverse_display_order) { | |||
3774 | err = got_object_qid_alloc(&qid, id); | |||
3775 | if (err) | |||
3776 | break; | |||
3777 | SIMPLEQ_INSERT_HEAD(&reversed_commits, qid, entry)do { if (((qid)->entry.sqe_next = (&reversed_commits)-> sqh_first) == ((void *)0)) (&reversed_commits)->sqh_last = &(qid)->entry.sqe_next; (&reversed_commits)-> sqh_first = (qid); } while (0); | |||
3778 | got_object_commit_close(commit); | |||
3779 | } else { | |||
3780 | err = print_commit(commit, id, repo, path, | |||
3781 | show_changed_paths ? &changed_paths : NULL((void *)0), | |||
3782 | show_patch, diff_context, refs_idmap, NULL((void *)0)); | |||
3783 | got_object_commit_close(commit); | |||
3784 | if (err) | |||
3785 | break; | |||
3786 | } | |||
3787 | if ((limit && --limit == 0) || | |||
3788 | (end_id && got_object_id_cmp(id, end_id) == 0)) | |||
3789 | break; | |||
3790 | ||||
3791 | TAILQ_FOREACH(pe, &changed_paths, entry)for((pe) = ((&changed_paths)->tqh_first); (pe) != ((void *)0); (pe) = ((pe)->entry.tqe_next)) { | |||
3792 | free((char *)pe->path); | |||
3793 | free(pe->data); | |||
3794 | } | |||
3795 | got_pathlist_free(&changed_paths); | |||
3796 | } | |||
3797 | if (reverse_display_order) { | |||
3798 | SIMPLEQ_FOREACH(qid, &reversed_commits, entry)for((qid) = ((&reversed_commits)->sqh_first); (qid) != ((void *)0); (qid) = ((qid)->entry.sqe_next)) { | |||
3799 | err = got_object_open_as_commit(&commit, repo, qid->id); | |||
3800 | if (err) | |||
3801 | break; | |||
3802 | if (show_changed_paths) { | |||
3803 | err = get_changed_paths(&changed_paths, | |||
3804 | commit, repo); | |||
3805 | if (err) | |||
3806 | break; | |||
3807 | } | |||
3808 | err = print_commit(commit, qid->id, repo, path, | |||
3809 | show_changed_paths ? &changed_paths : NULL((void *)0), | |||
3810 | show_patch, diff_context, refs_idmap, NULL((void *)0)); | |||
3811 | got_object_commit_close(commit); | |||
3812 | if (err) | |||
3813 | break; | |||
3814 | TAILQ_FOREACH(pe, &changed_paths, entry)for((pe) = ((&changed_paths)->tqh_first); (pe) != ((void *)0); (pe) = ((pe)->entry.tqe_next)) { | |||
3815 | free((char *)pe->path); | |||
3816 | free(pe->data); | |||
3817 | } | |||
3818 | got_pathlist_free(&changed_paths); | |||
3819 | } | |||
3820 | } | |||
3821 | done: | |||
3822 | while (!SIMPLEQ_EMPTY(&reversed_commits)(((&reversed_commits)->sqh_first) == ((void *)0))) { | |||
3823 | qid = SIMPLEQ_FIRST(&reversed_commits)((&reversed_commits)->sqh_first); | |||
3824 | SIMPLEQ_REMOVE_HEAD(&reversed_commits, entry)do { if (((&reversed_commits)->sqh_first = (&reversed_commits )->sqh_first->entry.sqe_next) == ((void *)0)) (&reversed_commits )->sqh_last = &(&reversed_commits)->sqh_first; } while (0); | |||
3825 | got_object_qid_free(qid); | |||
3826 | } | |||
3827 | TAILQ_FOREACH(pe, &changed_paths, entry)for((pe) = ((&changed_paths)->tqh_first); (pe) != ((void *)0); (pe) = ((pe)->entry.tqe_next)) { | |||
3828 | free((char *)pe->path); | |||
3829 | free(pe->data); | |||
3830 | } | |||
3831 | got_pathlist_free(&changed_paths); | |||
3832 | if (search_pattern) | |||
3833 | regfree(®ex); | |||
3834 | got_commit_graph_close(graph); | |||
3835 | return err; | |||
3836 | } | |||
3837 | ||||
3838 | __dead__attribute__((__noreturn__)) static void | |||
3839 | usage_log(void) | |||
3840 | { | |||
3841 | fprintf(stderr(&__sF[2]), "usage: %s log [-b] [-c commit] [-C number] [ -l N ] " | |||
3842 | "[-p] [-P] [-x commit] [-s search-pattern] [-r repository-path] " | |||
3843 | "[-R] [path]\n", getprogname()); | |||
3844 | exit(1); | |||
3845 | } | |||
3846 | ||||
3847 | static int | |||
3848 | get_default_log_limit(void) | |||
3849 | { | |||
3850 | const char *got_default_log_limit; | |||
3851 | long long n; | |||
3852 | const char *errstr; | |||
3853 | ||||
3854 | got_default_log_limit = getenv("GOT_LOG_DEFAULT_LIMIT"); | |||
3855 | if (got_default_log_limit == NULL((void *)0)) | |||
3856 | return 0; | |||
3857 | n = strtonum(got_default_log_limit, 0, INT_MAX2147483647, &errstr); | |||
3858 | if (errstr != NULL((void *)0)) | |||
3859 | return 0; | |||
3860 | return n; | |||
3861 | } | |||
3862 | ||||
3863 | static const struct got_error * | |||
3864 | cmd_log(int argc, char *argv[]) | |||
3865 | { | |||
3866 | const struct got_error *error; | |||
3867 | struct got_repository *repo = NULL((void *)0); | |||
3868 | struct got_worktree *worktree = NULL((void *)0); | |||
3869 | struct got_object_id *start_id = NULL((void *)0), *end_id = NULL((void *)0); | |||
3870 | char *repo_path = NULL((void *)0), *path = NULL((void *)0), *cwd = NULL((void *)0), *in_repo_path = NULL((void *)0); | |||
3871 | const char *start_commit = NULL((void *)0), *end_commit = NULL((void *)0); | |||
3872 | const char *search_pattern = NULL((void *)0); | |||
3873 | int diff_context = -1, ch; | |||
3874 | int show_changed_paths = 0, show_patch = 0, limit = 0, log_branches = 0; | |||
3875 | int reverse_display_order = 0; | |||
3876 | const char *errstr; | |||
3877 | struct got_reflist_head refs; | |||
3878 | struct got_reflist_object_id_map *refs_idmap = NULL((void *)0); | |||
3879 | ||||
3880 | TAILQ_INIT(&refs)do { (&refs)->tqh_first = ((void *)0); (&refs)-> tqh_last = &(&refs)->tqh_first; } while (0); | |||
3881 | ||||
3882 | #ifndef PROFILE | |||
3883 | if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil", | |||
3884 | NULL((void *)0)) | |||
3885 | == -1) | |||
3886 | err(1, "pledge"); | |||
3887 | #endif | |||
3888 | ||||
3889 | limit = get_default_log_limit(); | |||
3890 | ||||
3891 | while ((ch = getopt(argc, argv, "bpPc:C:l:r:Rs:x:")) != -1) { | |||
3892 | switch (ch) { | |||
3893 | case 'p': | |||
3894 | show_patch = 1; | |||
3895 | break; | |||
3896 | case 'P': | |||
3897 | show_changed_paths = 1; | |||
3898 | break; | |||
3899 | case 'c': | |||
3900 | start_commit = optarg; | |||
3901 | break; | |||
3902 | case 'C': | |||
3903 | diff_context = strtonum(optarg, 0, GOT_DIFF_MAX_CONTEXT64, | |||
3904 | &errstr); | |||
3905 | if (errstr != NULL((void *)0)) | |||
3906 | err(1, "-C option %s", errstr); | |||
3907 | break; | |||
3908 | case 'l': | |||
3909 | limit = strtonum(optarg, 0, INT_MAX2147483647, &errstr); | |||
3910 | if (errstr != NULL((void *)0)) | |||
3911 | err(1, "-l option %s", errstr); | |||
3912 | break; | |||
3913 | case 'b': | |||
3914 | log_branches = 1; | |||
3915 | break; | |||
3916 | case 'r': | |||
3917 | repo_path = realpath(optarg, NULL((void *)0)); | |||
3918 | if (repo_path == NULL((void *)0)) | |||
3919 | return got_error_from_errno2("realpath", | |||
3920 | optarg); | |||
3921 | got_path_strip_trailing_slashes(repo_path); | |||
3922 | break; | |||
3923 | case 'R': | |||
3924 | reverse_display_order = 1; | |||
3925 | break; | |||
3926 | case 's': | |||
3927 | search_pattern = optarg; | |||
3928 | break; | |||
3929 | case 'x': | |||
3930 | end_commit = optarg; | |||
3931 | break; | |||
3932 | default: | |||
3933 | usage_log(); | |||
3934 | /* NOTREACHED */ | |||
3935 | } | |||
3936 | } | |||
3937 | ||||
3938 | argc -= optind; | |||
3939 | argv += optind; | |||
3940 | ||||
3941 | if (diff_context == -1) | |||
3942 | diff_context = 3; | |||
3943 | else if (!show_patch) | |||
3944 | errx(1, "-C requires -p"); | |||
3945 | ||||
3946 | cwd = getcwd(NULL((void *)0), 0); | |||
3947 | if (cwd == NULL((void *)0)) { | |||
3948 | error = got_error_from_errno("getcwd"); | |||
3949 | goto done; | |||
3950 | } | |||
3951 | ||||
3952 | if (repo_path == NULL((void *)0)) { | |||
3953 | error = got_worktree_open(&worktree, cwd); | |||
3954 | if (error && error->code != GOT_ERR_NOT_WORKTREE60) | |||
3955 | goto done; | |||
3956 | error = NULL((void *)0); | |||
3957 | } | |||
3958 | ||||
3959 | if (argc == 1) { | |||
3960 | if (worktree) { | |||
3961 | error = got_worktree_resolve_path(&path, worktree, | |||
3962 | argv[0]); | |||
3963 | if (error) | |||
3964 | goto done; | |||
3965 | } else { | |||
3966 | path = strdup(argv[0]); | |||
3967 | if (path == NULL((void *)0)) { | |||
3968 | error = got_error_from_errno("strdup"); | |||
3969 | goto done; | |||
3970 | } | |||
3971 | } | |||
3972 | } else if (argc != 0) | |||
3973 | usage_log(); | |||
3974 | ||||
3975 | if (repo_path == NULL((void *)0)) { | |||
3976 | repo_path = worktree ? | |||
3977 | strdup(got_worktree_get_repo_path(worktree)) : strdup(cwd); | |||
3978 | } | |||
3979 | if (repo_path == NULL((void *)0)) { | |||
3980 | error = got_error_from_errno("strdup"); | |||
3981 | goto done; | |||
3982 | } | |||
3983 | ||||
3984 | error = got_repo_open(&repo, repo_path, NULL((void *)0)); | |||
3985 | if (error != NULL((void *)0)) | |||
3986 | goto done; | |||
3987 | ||||
3988 | error = apply_unveil(got_repo_get_path(repo), 1, | |||
3989 | worktree ? got_worktree_get_root_path(worktree) : NULL((void *)0)); | |||
3990 | if (error) | |||
3991 | goto done; | |||
3992 | ||||
3993 | error = got_ref_list(&refs, repo, NULL((void *)0), got_ref_cmp_by_name, NULL((void *)0)); | |||
3994 | if (error) | |||
3995 | goto done; | |||
3996 | ||||
3997 | error = got_reflist_object_id_map_create(&refs_idmap, &refs, repo); | |||
3998 | if (error) | |||
3999 | goto done; | |||
4000 | ||||
4001 | if (start_commit == NULL((void *)0)) { | |||
4002 | struct got_reference *head_ref; | |||
4003 | struct got_commit_object *commit = NULL((void *)0); | |||
4004 | error = got_ref_open(&head_ref, repo, | |||
4005 | worktree ? got_worktree_get_head_ref_name(worktree) | |||
4006 | : GOT_REF_HEAD"HEAD", 0); | |||
4007 | if (error != NULL((void *)0)) | |||
4008 | goto done; | |||
4009 | error = got_ref_resolve(&start_id, repo, head_ref); | |||
4010 | got_ref_close(head_ref); | |||
4011 | if (error != NULL((void *)0)) | |||
4012 | goto done; | |||
4013 | error = got_object_open_as_commit(&commit, repo, | |||
4014 | start_id); | |||
4015 | if (error != NULL((void *)0)) | |||
4016 | goto done; | |||
4017 | got_object_commit_close(commit); | |||
4018 | } else { | |||
4019 | error = got_repo_match_object_id(&start_id, NULL((void *)0), | |||
4020 | start_commit, GOT_OBJ_TYPE_COMMIT1, &refs, repo); | |||
4021 | if (error != NULL((void *)0)) | |||
4022 | goto done; | |||
4023 | } | |||
4024 | if (end_commit != NULL((void *)0)) { | |||
4025 | error = got_repo_match_object_id(&end_id, NULL((void *)0), | |||
4026 | end_commit, GOT_OBJ_TYPE_COMMIT1, &refs, repo); | |||
4027 | if (error != NULL((void *)0)) | |||
4028 | goto done; | |||
4029 | } | |||
4030 | ||||
4031 | if (worktree) { | |||
4032 | /* | |||
4033 | * If a path was specified on the command line it was resolved | |||
4034 | * to a path in the work tree above. Prepend the work tree's | |||
4035 | * path prefix to obtain the corresponding in-repository path. | |||
4036 | */ | |||
4037 | if (path) { | |||
4038 | const char *prefix; | |||
4039 | prefix = got_worktree_get_path_prefix(worktree); | |||
4040 | if (asprintf(&in_repo_path, "%s%s%s", prefix, | |||
4041 | (path[0] != '\0') ? "/" : "", path) == -1) { | |||
4042 | error = got_error_from_errno("asprintf"); | |||
4043 | goto done; | |||
4044 | } | |||
4045 | } | |||
4046 | } else | |||
4047 | error = got_repo_map_path(&in_repo_path, repo, | |||
4048 | path ? path : ""); | |||
4049 | if (error != NULL((void *)0)) | |||
4050 | goto done; | |||
4051 | if (in_repo_path) { | |||
4052 | free(path); | |||
4053 | path = in_repo_path; | |||
4054 | } | |||
4055 | ||||
4056 | error = print_commits(start_id, end_id, repo, path ? path : "", | |||
4057 | show_changed_paths, show_patch, search_pattern, diff_context, | |||
4058 | limit, log_branches, reverse_display_order, refs_idmap); | |||
4059 | done: | |||
4060 | free(path); | |||
4061 | free(repo_path); | |||
4062 | free(cwd); | |||
4063 | if (worktree) | |||
4064 | got_worktree_close(worktree); | |||
4065 | if (repo) { | |||
4066 | const struct got_error *repo_error; | |||
4067 | repo_error = got_repo_close(repo); | |||
4068 | if (error == NULL((void *)0)) | |||
4069 | error = repo_error; | |||
4070 | } | |||
4071 | if (refs_idmap) | |||
4072 | got_reflist_object_id_map_free(refs_idmap); | |||
4073 | got_ref_list_free(&refs); | |||
4074 | return error; | |||
4075 | } | |||
4076 | ||||
4077 | __dead__attribute__((__noreturn__)) static void | |||
4078 | usage_diff(void) | |||
4079 | { | |||
4080 | fprintf(stderr(&__sF[2]), "usage: %s diff [-a] [-C number] [-r repository-path] " | |||
4081 | "[-s] [-w] [object1 object2 | path]\n", getprogname()); | |||
4082 | exit(1); | |||
4083 | } | |||
4084 | ||||
4085 | struct print_diff_arg { | |||
4086 | struct got_repository *repo; | |||
4087 | struct got_worktree *worktree; | |||
4088 | int diff_context; | |||
4089 | const char *id_str; | |||
4090 | int header_shown; | |||
4091 | int diff_staged; | |||
4092 | int ignore_whitespace; | |||
4093 | int force_text_diff; | |||
4094 | }; | |||
4095 | ||||
4096 | /* | |||
4097 | * Create a file which contains the target path of a symlink so we can feed | |||
4098 | * it as content to the diff engine. | |||
4099 | */ | |||
4100 | static const struct got_error * | |||
4101 | get_symlink_target_file(int *fd, int dirfd, const char *de_name, | |||
4102 | const char *abspath) | |||
4103 | { | |||
4104 | const struct got_error *err = NULL((void *)0); | |||
4105 | char target_path[PATH_MAX1024]; | |||
4106 | ssize_t target_len, outlen; | |||
4107 | ||||
4108 | *fd = -1; | |||
4109 | ||||
4110 | if (dirfd != -1) { | |||
4111 | target_len = readlinkat(dirfd, de_name, target_path, PATH_MAX1024); | |||
4112 | if (target_len == -1) | |||
4113 | return got_error_from_errno2("readlinkat", abspath); | |||
4114 | } else { | |||
4115 | target_len = readlink(abspath, target_path, PATH_MAX1024); | |||
4116 | if (target_len == -1) | |||
4117 | return got_error_from_errno2("readlink", abspath); | |||
4118 | } | |||
4119 | ||||
4120 | *fd = got_opentempfd(); | |||
4121 | if (*fd == -1) | |||
4122 | return got_error_from_errno("got_opentempfd"); | |||
4123 | ||||
4124 | outlen = write(*fd, target_path, target_len); | |||
4125 | if (outlen == -1) { | |||
4126 | err = got_error_from_errno("got_opentempfd"); | |||
4127 | goto done; | |||
4128 | } | |||
4129 | ||||
4130 | if (lseek(*fd, 0, SEEK_SET0) == -1) { | |||
4131 | err = got_error_from_errno2("lseek", abspath); | |||
4132 | goto done; | |||
4133 | } | |||
4134 | done: | |||
4135 | if (err) { | |||
4136 | close(*fd); | |||
4137 | *fd = -1; | |||
4138 | } | |||
4139 | return err; | |||
4140 | } | |||
4141 | ||||
4142 | static const struct got_error * | |||
4143 | print_diff(void *arg, unsigned char status, unsigned char staged_status, | |||
4144 | const char *path, struct got_object_id *blob_id, | |||
4145 | struct got_object_id *staged_blob_id, struct got_object_id *commit_id, | |||
4146 | int dirfd, const char *de_name) | |||
4147 | { | |||
4148 | struct print_diff_arg *a = arg; | |||
4149 | const struct got_error *err = NULL((void *)0); | |||
4150 | struct got_blob_object *blob1 = NULL((void *)0); | |||
4151 | int fd = -1; | |||
4152 | FILE *f2 = NULL((void *)0); | |||
4153 | char *abspath = NULL((void *)0), *label1 = NULL((void *)0); | |||
4154 | struct stat sb; | |||
4155 | ||||
4156 | if (a->diff_staged) { | |||
4157 | if (staged_status != GOT_STATUS_MODIFY'M' && | |||
4158 | staged_status != GOT_STATUS_ADD'A' && | |||
4159 | staged_status != GOT_STATUS_DELETE'D') | |||
4160 | return NULL((void *)0); | |||
4161 | } else { | |||
4162 | if (staged_status == GOT_STATUS_DELETE'D') | |||
4163 | return NULL((void *)0); | |||
4164 | if (status == GOT_STATUS_NONEXISTENT'N') | |||
4165 | return got_error_set_errno(ENOENT2, path); | |||
4166 | if (status != GOT_STATUS_MODIFY'M' && | |||
4167 | status != GOT_STATUS_ADD'A' && | |||
4168 | status != GOT_STATUS_DELETE'D' && | |||
4169 | status != GOT_STATUS_CONFLICT'C') | |||
4170 | return NULL((void *)0); | |||
4171 | } | |||
4172 | ||||
4173 | if (!a->header_shown) { | |||
4174 | printf("diff %s %s%s\n", a->id_str, | |||
4175 | got_worktree_get_root_path(a->worktree), | |||
4176 | a->diff_staged ? " (staged changes)" : ""); | |||
4177 | a->header_shown = 1; | |||
4178 | } | |||
4179 | ||||
4180 | if (a->diff_staged) { | |||
4181 | const char *label1 = NULL((void *)0), *label2 = NULL((void *)0); | |||
4182 | switch (staged_status) { | |||
4183 | case GOT_STATUS_MODIFY'M': | |||
4184 | label1 = path; | |||
4185 | label2 = path; | |||
4186 | break; | |||
4187 | case GOT_STATUS_ADD'A': | |||
4188 | label2 = path; | |||
4189 | break; | |||
4190 | case GOT_STATUS_DELETE'D': | |||
4191 | label1 = path; | |||
4192 | break; | |||
4193 | default: | |||
4194 | return got_error(GOT_ERR_FILE_STATUS68); | |||
4195 | } | |||
4196 | return got_diff_objects_as_blobs(NULL((void *)0), NULL((void *)0), blob_id, | |||
4197 | staged_blob_id, label1, label2, a->diff_context, | |||
4198 | a->ignore_whitespace, a->force_text_diff, a->repo, stdout(&__sF[1])); | |||
4199 | } | |||
4200 | ||||
4201 | if (staged_status == GOT_STATUS_ADD'A' || | |||
4202 | staged_status == GOT_STATUS_MODIFY'M') { | |||
4203 | char *id_str; | |||
4204 | err = got_object_open_as_blob(&blob1, a->repo, staged_blob_id, | |||
4205 | 8192); | |||
4206 | if (err) | |||
4207 | goto done; | |||
4208 | err = got_object_id_str(&id_str, staged_blob_id); | |||
4209 | if (err) | |||
4210 | goto done; | |||
4211 | if (asprintf(&label1, "%s (staged)", id_str) == -1) { | |||
4212 | err = got_error_from_errno("asprintf"); | |||
4213 | free(id_str); | |||
4214 | goto done; | |||
4215 | } | |||
4216 | free(id_str); | |||
4217 | } else if (status != GOT_STATUS_ADD'A') { | |||
4218 | err = got_object_open_as_blob(&blob1, a->repo, blob_id, 8192); | |||
4219 | if (err) | |||
4220 | goto done; | |||
4221 | } | |||
4222 | ||||
4223 | if (status != GOT_STATUS_DELETE'D') { | |||
4224 | if (asprintf(&abspath, "%s/%s", | |||
4225 | got_worktree_get_root_path(a->worktree), path) == -1) { | |||
4226 | err = got_error_from_errno("asprintf"); | |||
4227 | goto done; | |||
4228 | } | |||
4229 | ||||
4230 | if (dirfd != -1) { | |||
4231 | fd = openat(dirfd, de_name, O_RDONLY0x0000 | O_NOFOLLOW0x0100); | |||
4232 | if (fd == -1) { | |||
4233 | if (errno(*__errno()) != ELOOP62) { | |||
4234 | err = got_error_from_errno2("openat", | |||
4235 | abspath); | |||
4236 | goto done; | |||
4237 | } | |||
4238 | err = get_symlink_target_file(&fd, dirfd, | |||
4239 | de_name, abspath); | |||
4240 | if (err) | |||
4241 | goto done; | |||
4242 | } | |||
4243 | } else { | |||
4244 | fd = open(abspath, O_RDONLY0x0000 | O_NOFOLLOW0x0100); | |||
4245 | if (fd == -1) { | |||
4246 | if (errno(*__errno()) != ELOOP62) { | |||
4247 | err = got_error_from_errno2("open", | |||
4248 | abspath); | |||
4249 | goto done; | |||
4250 | } | |||
4251 | err = get_symlink_target_file(&fd, dirfd, | |||
4252 | de_name, abspath); | |||
4253 | if (err) | |||
4254 | goto done; | |||
4255 | } | |||
4256 | } | |||
4257 | if (fstat(fd, &sb) == -1) { | |||
4258 | err = got_error_from_errno2("fstat", abspath); | |||
4259 | goto done; | |||
4260 | } | |||
4261 | f2 = fdopen(fd, "r"); | |||
4262 | if (f2 == NULL((void *)0)) { | |||
4263 | err = got_error_from_errno2("fdopen", abspath); | |||
4264 | goto done; | |||
4265 | } | |||
4266 | fd = -1; | |||
4267 | } else | |||
4268 | sb.st_size = 0; | |||
4269 | ||||
4270 | err = got_diff_blob_file(blob1, label1, f2, sb.st_size, path, | |||
4271 | a->diff_context, a->ignore_whitespace, a->force_text_diff, stdout(&__sF[1])); | |||
4272 | done: | |||
4273 | if (blob1) | |||
4274 | got_object_blob_close(blob1); | |||
4275 | if (f2 && fclose(f2) == EOF(-1) && err == NULL((void *)0)) | |||
4276 | err = got_error_from_errno("fclose"); | |||
4277 | if (fd != -1 && close(fd) == -1 && err == NULL((void *)0)) | |||
4278 | err = got_error_from_errno("close"); | |||
4279 | free(abspath); | |||
4280 | return err; | |||
4281 | } | |||
4282 | ||||
4283 | static const struct got_error * | |||
4284 | cmd_diff(int argc, char *argv[]) | |||
4285 | { | |||
4286 | const struct got_error *error; | |||
4287 | struct got_repository *repo = NULL((void *)0); | |||
4288 | struct got_worktree *worktree = NULL((void *)0); | |||
4289 | char *cwd = NULL((void *)0), *repo_path = NULL((void *)0); | |||
4290 | struct got_object_id *id1 = NULL((void *)0), *id2 = NULL((void *)0); | |||
4291 | const char *id_str1 = NULL((void *)0), *id_str2 = NULL((void *)0); | |||
4292 | char *label1 = NULL((void *)0), *label2 = NULL((void *)0); | |||
4293 | int type1, type2; | |||
4294 | int diff_context = 3, diff_staged = 0, ignore_whitespace = 0, ch; | |||
4295 | int force_text_diff = 0; | |||
4296 | const char *errstr; | |||
4297 | char *path = NULL((void *)0); | |||
4298 | struct got_reflist_head refs; | |||
4299 | ||||
4300 | TAILQ_INIT(&refs)do { (&refs)->tqh_first = ((void *)0); (&refs)-> tqh_last = &(&refs)->tqh_first; } while (0); | |||
4301 | ||||
4302 | #ifndef PROFILE | |||
4303 | if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil", | |||
4304 | NULL((void *)0)) == -1) | |||
4305 | err(1, "pledge"); | |||
4306 | #endif | |||
4307 | ||||
4308 | while ((ch = getopt(argc, argv, "aC:r:sw")) != -1) { | |||
4309 | switch (ch) { | |||
4310 | case 'a': | |||
4311 | force_text_diff = 1; | |||
4312 | break; | |||
4313 | case 'C': | |||
4314 | diff_context = strtonum(optarg, 0, GOT_DIFF_MAX_CONTEXT64, | |||
4315 | &errstr); | |||
4316 | if (errstr != NULL((void *)0)) | |||
4317 | err(1, "-C option %s", errstr); | |||
4318 | break; | |||
4319 | case 'r': | |||
4320 | repo_path = realpath(optarg, NULL((void *)0)); | |||
4321 | if (repo_path == NULL((void *)0)) | |||
4322 | return got_error_from_errno2("realpath", | |||
4323 | optarg); | |||
4324 | got_path_strip_trailing_slashes(repo_path); | |||
4325 | break; | |||
4326 | case 's': | |||
4327 | diff_staged = 1; | |||
4328 | break; | |||
4329 | case 'w': | |||
4330 | ignore_whitespace = 1; | |||
4331 | break; | |||
4332 | default: | |||
4333 | usage_diff(); | |||
4334 | /* NOTREACHED */ | |||
4335 | } | |||
4336 | } | |||
4337 | ||||
4338 | argc -= optind; | |||
4339 | argv += optind; | |||
4340 | ||||
4341 | cwd = getcwd(NULL((void *)0), 0); | |||
4342 | if (cwd == NULL((void *)0)) { | |||
4343 | error = got_error_from_errno("getcwd"); | |||
4344 | goto done; | |||
4345 | } | |||
4346 | if (argc <= 1) { | |||
4347 | if (repo_path) | |||
4348 | errx(1, | |||
4349 | "-r option can't be used when diffing a work tree"); | |||
4350 | error = got_worktree_open(&worktree, cwd); | |||
4351 | if (error) { | |||
4352 | if (error->code == GOT_ERR_NOT_WORKTREE60) | |||
4353 | error = wrap_not_worktree_error(error, "diff", | |||
4354 | cwd); | |||
4355 | goto done; | |||
4356 | } | |||
4357 | repo_path = strdup(got_worktree_get_repo_path(worktree)); | |||
4358 | if (repo_path == NULL((void *)0)) { | |||
4359 | error = got_error_from_errno("strdup"); | |||
4360 | goto done; | |||
4361 | } | |||
4362 | if (argc == 1) { | |||
4363 | error = got_worktree_resolve_path(&path, worktree, | |||
4364 | argv[0]); | |||
4365 | if (error) | |||
4366 | goto done; | |||
4367 | } else { | |||
4368 | path = strdup(""); | |||
4369 | if (path == NULL((void *)0)) { | |||
4370 | error = got_error_from_errno("strdup"); | |||
4371 | goto done; | |||
4372 | } | |||
4373 | } | |||
4374 | } else if (argc == 2) { | |||
4375 | if (diff_staged) | |||
4376 | errx(1, "-s option can't be used when diffing " | |||
4377 | "objects in repository"); | |||
4378 | id_str1 = argv[0]; | |||
4379 | id_str2 = argv[1]; | |||
4380 | if (repo_path == NULL((void *)0)) { | |||
4381 | error = got_worktree_open(&worktree, cwd); | |||
4382 | if (error && error->code != GOT_ERR_NOT_WORKTREE60) | |||
4383 | goto done; | |||
4384 | repo_path = strdup(worktree ? | |||
4385 | got_worktree_get_repo_path(worktree) : cwd); | |||
4386 | if (repo_path == NULL((void *)0)) { | |||
4387 | error = got_error_from_errno("strdup"); | |||
4388 | goto done; | |||
4389 | } | |||
4390 | } | |||
4391 | } else | |||
4392 | usage_diff(); | |||
4393 | ||||
4394 | error = got_repo_open(&repo, repo_path, NULL((void *)0)); | |||
4395 | free(repo_path); | |||
4396 | if (error != NULL((void *)0)) | |||
4397 | goto done; | |||
4398 | ||||
4399 | error = apply_unveil(got_repo_get_path(repo), 1, | |||
4400 | worktree ? got_worktree_get_root_path(worktree) : NULL((void *)0)); | |||
4401 | if (error) | |||
4402 | goto done; | |||
4403 | ||||
4404 | if (argc <= 1) { | |||
4405 | struct print_diff_arg arg; | |||
4406 | struct got_pathlist_head paths; | |||
4407 | char *id_str; | |||
4408 | ||||
4409 | TAILQ_INIT(&paths)do { (&paths)->tqh_first = ((void *)0); (&paths)-> tqh_last = &(&paths)->tqh_first; } while (0); | |||
4410 | ||||
4411 | error = got_object_id_str(&id_str, | |||
4412 | got_worktree_get_base_commit_id(worktree)); | |||
4413 | if (error) | |||
4414 | goto done; | |||
4415 | arg.repo = repo; | |||
4416 | arg.worktree = worktree; | |||
4417 | arg.diff_context = diff_context; | |||
4418 | arg.id_str = id_str; | |||
4419 | arg.header_shown = 0; | |||
4420 | arg.diff_staged = diff_staged; | |||
4421 | arg.ignore_whitespace = ignore_whitespace; | |||
4422 | arg.force_text_diff = force_text_diff; | |||
4423 | ||||
4424 | error = got_pathlist_append(&paths, path, NULL((void *)0)); | |||
4425 | if (error) | |||
4426 | goto done; | |||
4427 | ||||
4428 | error = got_worktree_status(worktree, &paths, repo, print_diff, | |||
4429 | &arg, check_cancelled, NULL((void *)0)); | |||
4430 | free(id_str); | |||
4431 | got_pathlist_free(&paths); | |||
4432 | goto done; | |||
4433 | } | |||
4434 | ||||
4435 | error = got_ref_list(&refs, repo, NULL((void *)0), got_ref_cmp_by_name, NULL((void *)0)); | |||
4436 | if (error) | |||
4437 | return error; | |||
4438 | ||||
4439 | error = got_repo_match_object_id(&id1, &label1, id_str1, | |||
4440 | GOT_OBJ_TYPE_ANY0, &refs, repo); | |||
4441 | if (error) | |||
4442 | goto done; | |||
4443 | ||||
4444 | error = got_repo_match_object_id(&id2, &label2, id_str2, | |||
4445 | GOT_OBJ_TYPE_ANY0, &refs, repo); | |||
4446 | if (error) | |||
4447 | goto done; | |||
4448 | ||||
4449 | error = got_object_get_type(&type1, repo, id1); | |||
4450 | if (error) | |||
4451 | goto done; | |||
4452 | ||||
4453 | error = got_object_get_type(&type2, repo, id2); | |||
4454 | if (error) | |||
4455 | goto done; | |||
4456 | ||||
4457 | if (type1 != type2) { | |||
4458 | error = got_error(GOT_ERR_OBJ_TYPE11); | |||
4459 | goto done; | |||
4460 | } | |||
4461 | ||||
4462 | switch (type1) { | |||
4463 | case GOT_OBJ_TYPE_BLOB3: | |||
4464 | error = got_diff_objects_as_blobs(NULL((void *)0), NULL((void *)0), id1, id2, | |||
4465 | NULL((void *)0), NULL((void *)0), diff_context, ignore_whitespace, | |||
4466 | force_text_diff, repo, stdout(&__sF[1])); | |||
4467 | break; | |||
4468 | case GOT_OBJ_TYPE_TREE2: | |||
4469 | error = got_diff_objects_as_trees(NULL((void *)0), NULL((void *)0), id1, id2, | |||
4470 | "", "", diff_context, ignore_whitespace, force_text_diff, | |||
4471 | repo, stdout(&__sF[1])); | |||
4472 | break; | |||
4473 | case GOT_OBJ_TYPE_COMMIT1: | |||
4474 | printf("diff %s %s\n", label1, label2); | |||
4475 | error = got_diff_objects_as_commits(NULL((void *)0), NULL((void *)0), id1, id2, | |||
4476 | diff_context, ignore_whitespace, force_text_diff, repo, | |||
4477 | stdout(&__sF[1])); | |||
4478 | break; | |||
4479 | default: | |||
4480 | error = got_error(GOT_ERR_OBJ_TYPE11); | |||
4481 | } | |||
4482 | done: | |||
4483 | free(label1); | |||
4484 | free(label2); | |||
4485 | free(id1); | |||
4486 | free(id2); | |||
4487 | free(path); | |||
4488 | if (worktree) | |||
4489 | got_worktree_close(worktree); | |||
4490 | if (repo) { | |||
4491 | const struct got_error *repo_error; | |||
4492 | repo_error = got_repo_close(repo); | |||
4493 | if (error == NULL((void *)0)) | |||
4494 | error = repo_error; | |||
4495 | } | |||
4496 | got_ref_list_free(&refs); | |||
4497 | return error; | |||
4498 | } | |||
4499 | ||||
4500 | __dead__attribute__((__noreturn__)) static void | |||
4501 | usage_blame(void) | |||
4502 | { | |||
4503 | fprintf(stderr(&__sF[2]), | |||
4504 | "usage: %s blame [-c commit] [-r repository-path] path\n", | |||
4505 | getprogname()); | |||
4506 | exit(1); | |||
4507 | } | |||
4508 | ||||
4509 | struct blame_line { | |||
4510 | int annotated; | |||
4511 | char *id_str; | |||
4512 | char *committer; | |||
4513 | char datebuf[11]; /* YYYY-MM-DD + NUL */ | |||
4514 | }; | |||
4515 | ||||
4516 | struct blame_cb_args { | |||
4517 | struct blame_line *lines; | |||
4518 | int nlines; | |||
4519 | int nlines_prec; | |||
4520 | int lineno_cur; | |||
4521 | off_t *line_offsets; | |||
4522 | FILE *f; | |||
4523 | struct got_repository *repo; | |||
4524 | }; | |||
4525 | ||||
4526 | static const struct got_error * | |||
4527 | blame_cb(void *arg, int nlines, int lineno, struct got_object_id *id) | |||
4528 | { | |||
4529 | const struct got_error *err = NULL((void *)0); | |||
4530 | struct blame_cb_args *a = arg; | |||
4531 | struct blame_line *bline; | |||
4532 | char *line = NULL((void *)0); | |||
4533 | size_t linesize = 0; | |||
4534 | struct got_commit_object *commit = NULL((void *)0); | |||
4535 | off_t offset; | |||
4536 | struct tm tm; | |||
4537 | time_t committer_time; | |||
4538 | ||||
4539 | if (nlines != a->nlines || | |||
4540 | (lineno != -1 && lineno < 1) || lineno > a->nlines) | |||
4541 | return got_error(GOT_ERR_RANGE47); | |||
4542 | ||||
4543 | if (sigint_received) | |||
4544 | return got_error(GOT_ERR_ITER_COMPLETED46); | |||
4545 | ||||
4546 | if (lineno == -1) | |||
4547 | return NULL((void *)0); /* no change in this commit */ | |||
4548 | ||||
4549 | /* Annotate this line. */ | |||
4550 | bline = &a->lines[lineno - 1]; | |||
4551 | if (bline->annotated) | |||
4552 | return NULL((void *)0); | |||
4553 | err = got_object_id_str(&bline->id_str, id); | |||
4554 | if (err) | |||
4555 | return err; | |||
4556 | ||||
4557 | err = got_object_open_as_commit(&commit, a->repo, id); | |||
4558 | if (err) | |||
4559 | goto done; | |||
4560 | ||||
4561 | bline->committer = strdup(got_object_commit_get_committer(commit)); | |||
4562 | if (bline->committer == NULL((void *)0)) { | |||
4563 | err = got_error_from_errno("strdup"); | |||
4564 | goto done; | |||
4565 | } | |||
4566 | ||||
4567 | committer_time = got_object_commit_get_committer_time(commit); | |||
4568 | if (localtime_r(&committer_time, &tm) == NULL((void *)0)) | |||
4569 | return got_error_from_errno("localtime_r"); | |||
4570 | if (strftime(bline->datebuf, sizeof(bline->datebuf), "%G-%m-%d", | |||
4571 | &tm) == 0) { | |||
4572 | err = got_error(GOT_ERR_NO_SPACE9); | |||
4573 | goto done; | |||
4574 | } | |||
4575 | bline->annotated = 1; | |||
4576 | ||||
4577 | /* Print lines annotated so far. */ | |||
4578 | bline = &a->lines[a->lineno_cur - 1]; | |||
4579 | if (!bline->annotated) | |||
4580 | goto done; | |||
4581 | ||||
4582 | offset = a->line_offsets[a->lineno_cur - 1]; | |||
4583 | if (fseeko(a->f, offset, SEEK_SET0) == -1) { | |||
4584 | err = got_error_from_errno("fseeko"); | |||
4585 | goto done; | |||
4586 | } | |||
4587 | ||||
4588 | while (bline->annotated) { | |||
4589 | char *smallerthan, *at, *nl, *committer; | |||
4590 | size_t len; | |||
4591 | ||||
4592 | if (getline(&line, &linesize, a->f) == -1) { | |||
4593 | if (ferror(a->f)(!__isthreaded ? (((a->f)->_flags & 0x0040) != 0) : (ferror)(a->f))) | |||
4594 | err = got_error_from_errno("getline"); | |||
4595 | break; | |||
4596 | } | |||
4597 | ||||
4598 | committer = bline->committer; | |||
4599 | smallerthan = strchr(committer, '<'); | |||
4600 | if (smallerthan && smallerthan[1] != '\0') | |||
4601 | committer = smallerthan + 1; | |||
4602 | at = strchr(committer, '@'); | |||
4603 | if (at) | |||
4604 | *at = '\0'; | |||
4605 | len = strlen(committer); | |||
4606 | if (len >= 9) | |||
4607 | committer[8] = '\0'; | |||
4608 | ||||
4609 | nl = strchr(line, '\n'); | |||
4610 | if (nl) | |||
4611 | *nl = '\0'; | |||
4612 | printf("%.*d) %.8s %s %-8s %s\n", a->nlines_prec, a->lineno_cur, | |||
4613 | bline->id_str, bline->datebuf, committer, line); | |||
4614 | ||||
4615 | a->lineno_cur++; | |||
4616 | bline = &a->lines[a->lineno_cur - 1]; | |||
4617 | } | |||
4618 | done: | |||
4619 | if (commit) | |||
4620 | got_object_commit_close(commit); | |||
4621 | free(line); | |||
4622 | return err; | |||
4623 | } | |||
4624 | ||||
4625 | static const struct got_error * | |||
4626 | cmd_blame(int argc, char *argv[]) | |||
4627 | { | |||
4628 | const struct got_error *error; | |||
4629 | struct got_repository *repo = NULL((void *)0); | |||
4630 | struct got_worktree *worktree = NULL((void *)0); | |||
4631 | char *path, *cwd = NULL((void *)0), *repo_path = NULL((void *)0), *in_repo_path = NULL((void *)0); | |||
4632 | char *link_target = NULL((void *)0); | |||
4633 | struct got_object_id *obj_id = NULL((void *)0); | |||
4634 | struct got_object_id *commit_id = NULL((void *)0); | |||
4635 | struct got_blob_object *blob = NULL((void *)0); | |||
4636 | char *commit_id_str = NULL((void *)0); | |||
4637 | struct blame_cb_args bca; | |||
4638 | int ch, obj_type, i; | |||
4639 | off_t filesize; | |||
4640 | ||||
4641 | memset(&bca, 0, sizeof(bca)); | |||
4642 | ||||
4643 | #ifndef PROFILE | |||
4644 | if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil", | |||
4645 | NULL((void *)0)) == -1) | |||
4646 | err(1, "pledge"); | |||
4647 | #endif | |||
4648 | ||||
4649 | while ((ch = getopt(argc, argv, "c:r:")) != -1) { | |||
4650 | switch (ch) { | |||
4651 | case 'c': | |||
4652 | commit_id_str = optarg; | |||
4653 | break; | |||
4654 | case 'r': | |||
4655 | repo_path = realpath(optarg, NULL((void *)0)); | |||
4656 | if (repo_path == NULL((void *)0)) | |||
4657 | return got_error_from_errno2("realpath", | |||
4658 | optarg); | |||
4659 | got_path_strip_trailing_slashes(repo_path); | |||
4660 | break; | |||
4661 | default: | |||
4662 | usage_blame(); | |||
4663 | /* NOTREACHED */ | |||
4664 | } | |||
4665 | } | |||
4666 | ||||
4667 | argc -= optind; | |||
4668 | argv += optind; | |||
4669 | ||||
4670 | if (argc == 1) | |||
4671 | path = argv[0]; | |||
4672 | else | |||
4673 | usage_blame(); | |||
4674 | ||||
4675 | cwd = getcwd(NULL((void *)0), 0); | |||
4676 | if (cwd == NULL((void *)0)) { | |||
4677 | error = got_error_from_errno("getcwd"); | |||
4678 | goto done; | |||
4679 | } | |||
4680 | if (repo_path == NULL((void *)0)) { | |||
4681 | error = got_worktree_open(&worktree, cwd); | |||
4682 | if (error && error->code != GOT_ERR_NOT_WORKTREE60) | |||
4683 | goto done; | |||
4684 | else | |||
4685 | error = NULL((void *)0); | |||
4686 | if (worktree) { | |||
4687 | repo_path = | |||
4688 | strdup(got_worktree_get_repo_path(worktree)); | |||
4689 | if (repo_path == NULL((void *)0)) { | |||
4690 | error = got_error_from_errno("strdup"); | |||
4691 | if (error) | |||
4692 | goto done; | |||
4693 | } | |||
4694 | } else { | |||
4695 | repo_path = strdup(cwd); | |||
4696 | if (repo_path == NULL((void *)0)) { | |||
4697 | error = got_error_from_errno("strdup"); | |||
4698 | goto done; | |||
4699 | } | |||
4700 | } | |||
4701 | } | |||
4702 | ||||
4703 | error = got_repo_open(&repo, repo_path, NULL((void *)0)); | |||
4704 | if (error != NULL((void *)0)) | |||
4705 | goto done; | |||
4706 | ||||
4707 | if (worktree) { | |||
4708 | const char *prefix = got_worktree_get_path_prefix(worktree); | |||
4709 | char *p; | |||
4710 | ||||
4711 | error = got_worktree_resolve_path(&p, worktree, path); | |||
4712 | if (error) | |||
4713 | goto done; | |||
4714 | if (asprintf(&in_repo_path, "%s%s%s", prefix, | |||
4715 | (p[0] != '\0' && !got_path_is_root_dir(prefix)) ? "/" : "", | |||
4716 | p) == -1) { | |||
4717 | error = got_error_from_errno("asprintf"); | |||
4718 | free(p); | |||
4719 | goto done; | |||
4720 | } | |||
4721 | free(p); | |||
4722 | error = apply_unveil(got_repo_get_path(repo), 1, NULL((void *)0)); | |||
4723 | } else { | |||
4724 | error = apply_unveil(got_repo_get_path(repo), 1, NULL((void *)0)); | |||
4725 | if (error) | |||
4726 | goto done; | |||
4727 | error = got_repo_map_path(&in_repo_path, repo, path); | |||
4728 | } | |||
4729 | if (error) | |||
4730 | goto done; | |||
4731 | ||||
4732 | if (commit_id_str == NULL((void *)0)) { | |||
4733 | struct got_reference *head_ref; | |||
4734 | error = got_ref_open(&head_ref, repo, worktree ? | |||
4735 | got_worktree_get_head_ref_name(worktree) : GOT_REF_HEAD"HEAD", 0); | |||
4736 | if (error != NULL((void *)0)) | |||
4737 | goto done; | |||
4738 | error = got_ref_resolve(&commit_id, repo, head_ref); | |||
4739 | got_ref_close(head_ref); | |||
4740 | if (error != NULL((void *)0)) | |||
4741 | goto done; | |||
4742 | } else { | |||
4743 | struct got_reflist_head refs; | |||
4744 | TAILQ_INIT(&refs)do { (&refs)->tqh_first = ((void *)0); (&refs)-> tqh_last = &(&refs)->tqh_first; } while (0); | |||
4745 | error = got_ref_list(&refs, repo, NULL((void *)0), got_ref_cmp_by_name, | |||
4746 | NULL((void *)0)); | |||
4747 | if (error) | |||
4748 | goto done; | |||
4749 | error = got_repo_match_object_id(&commit_id, NULL((void *)0), | |||
4750 | commit_id_str, GOT_OBJ_TYPE_COMMIT1, &refs, repo); | |||
4751 | got_ref_list_free(&refs); | |||
4752 | if (error) | |||
4753 | goto done; | |||
4754 | } | |||
4755 | ||||
4756 | error = got_object_resolve_symlinks(&link_target, in_repo_path, | |||
4757 | commit_id, repo); | |||
4758 | if (error) | |||
4759 | goto done; | |||
4760 | ||||
4761 | error = got_object_id_by_path(&obj_id, repo, commit_id, | |||
4762 | link_target ? link_target : in_repo_path); | |||
4763 | if (error) | |||
4764 | goto done; | |||
4765 | ||||
4766 | error = got_object_get_type(&obj_type, repo, obj_id); | |||
4767 | if (error) | |||
4768 | goto done; | |||
4769 | ||||
4770 | if (obj_type != GOT_OBJ_TYPE_BLOB3) { | |||
4771 | error = got_error_path(link_target ? link_target : in_repo_path, | |||
4772 | GOT_ERR_OBJ_TYPE11); | |||
4773 | goto done; | |||
4774 | } | |||
4775 | ||||
4776 | error = got_object_open_as_blob(&blob, repo, obj_id, 8192); | |||
4777 | if (error) | |||
4778 | goto done; | |||
4779 | bca.f = got_opentemp(); | |||
4780 | if (bca.f == NULL((void *)0)) { | |||
4781 | error = got_error_from_errno("got_opentemp"); | |||
4782 | goto done; | |||
4783 | } | |||
4784 | error = got_object_blob_dump_to_file(&filesize, &bca.nlines, | |||
4785 | &bca.line_offsets, bca.f, blob); | |||
4786 | if (error || bca.nlines == 0) | |||
4787 | goto done; | |||
4788 | ||||
4789 | /* Don't include \n at EOF in the blame line count. */ | |||
4790 | if (bca.line_offsets[bca.nlines - 1] == filesize) | |||
4791 | bca.nlines--; | |||
4792 | ||||
4793 | bca.lines = calloc(bca.nlines, sizeof(*bca.lines)); | |||
4794 | if (bca.lines == NULL((void *)0)) { | |||
4795 | error = got_error_from_errno("calloc"); | |||
4796 | goto done; | |||
4797 | } | |||
4798 | bca.lineno_cur = 1; | |||
4799 | bca.nlines_prec = 0; | |||
4800 | i = bca.nlines; | |||
4801 | while (i > 0) { | |||
4802 | i /= 10; | |||
4803 | bca.nlines_prec++; | |||
4804 | } | |||
4805 | bca.repo = repo; | |||
4806 | ||||
4807 | error = got_blame(link_target ? link_target : in_repo_path, commit_id, | |||
4808 | repo, blame_cb, &bca, check_cancelled, NULL((void *)0)); | |||
4809 | done: | |||
4810 | free(in_repo_path); | |||
4811 | free(link_target); | |||
4812 | free(repo_path); | |||
4813 | free(cwd); | |||
4814 | free(commit_id); | |||
4815 | free(obj_id); | |||
4816 | if (blob) | |||
4817 | got_object_blob_close(blob); | |||
4818 | if (worktree) | |||
4819 | got_worktree_close(worktree); | |||
4820 | if (repo) { | |||
4821 | const struct got_error *repo_error; | |||
4822 | repo_error = got_repo_close(repo); | |||
4823 | if (error == NULL((void *)0)) | |||
4824 | error = repo_error; | |||
4825 | } | |||
4826 | if (bca.lines) { | |||
4827 | for (i = 0; i < bca.nlines; i++) { | |||
4828 | struct blame_line *bline = &bca.lines[i]; | |||
4829 | free(bline->id_str); | |||
4830 | free(bline->committer); | |||
4831 | } | |||
4832 | free(bca.lines); | |||
4833 | } | |||
4834 | free(bca.line_offsets); | |||
4835 | if (bca.f && fclose(bca.f) == EOF(-1) && error == NULL((void *)0)) | |||
4836 | error = got_error_from_errno("fclose"); | |||
4837 | return error; | |||
4838 | } | |||
4839 | ||||
4840 | __dead__attribute__((__noreturn__)) static void | |||
4841 | usage_tree(void) | |||
4842 | { | |||
4843 | fprintf(stderr(&__sF[2]), | |||
4844 | "usage: %s tree [-c commit] [-r repository-path] [-iR] [path]\n", | |||
4845 | getprogname()); | |||
4846 | exit(1); | |||
4847 | } | |||
4848 | ||||
4849 | static const struct got_error * | |||
4850 | print_entry(struct got_tree_entry *te, const char *id, const char *path, | |||
4851 | const char *root_path, struct got_repository *repo) | |||
4852 | { | |||
4853 | const struct got_error *err = NULL((void *)0); | |||
4854 | int is_root_path = (strcmp(path, root_path) == 0); | |||
4855 | const char *modestr = ""; | |||
4856 | mode_t mode = got_tree_entry_get_mode(te); | |||
4857 | char *link_target = NULL((void *)0); | |||
4858 | ||||
4859 | path += strlen(root_path); | |||
4860 | while (path[0] == '/') | |||
4861 | path++; | |||
4862 | ||||
4863 | if (got_object_tree_entry_is_submodule(te)) | |||
4864 | modestr = "$"; | |||
4865 | else if (S_ISLNK(mode)((mode & 0170000) == 0120000)) { | |||
4866 | int i; | |||
4867 | ||||
4868 | err = got_tree_entry_get_symlink_target(&link_target, te, repo); | |||
4869 | if (err) | |||
4870 | return err; | |||
4871 | for (i = 0; i < strlen(link_target); i++) { | |||
4872 | if (!isprint((unsigned char)link_target[i])) | |||
4873 | link_target[i] = '?'; | |||
4874 | } | |||
4875 | ||||
4876 | modestr = "@"; | |||
4877 | } | |||
4878 | else if (S_ISDIR(mode)((mode & 0170000) == 0040000)) | |||
4879 | modestr = "/"; | |||
4880 | else if (mode & S_IXUSR0000100) | |||
4881 | modestr = "*"; | |||
4882 | ||||
4883 | printf("%s%s%s%s%s%s%s\n", id ? id : "", path, | |||
4884 | is_root_path ? "" : "/", got_tree_entry_get_name(te), modestr, | |||
4885 | link_target ? " -> ": "", link_target ? link_target : ""); | |||
4886 | ||||
4887 | free(link_target); | |||
4888 | return NULL((void *)0); | |||
4889 | } | |||
4890 | ||||
4891 | static const struct got_error * | |||
4892 | print_tree(const char *path, struct got_object_id *commit_id, | |||
4893 | int show_ids, int recurse, const char *root_path, | |||
4894 | struct got_repository *repo) | |||
4895 | { | |||
4896 | const struct got_error *err = NULL((void *)0); | |||
4897 | struct got_object_id *tree_id = NULL((void *)0); | |||
4898 | struct got_tree_object *tree = NULL((void *)0); | |||
4899 | int nentries, i; | |||
4900 | ||||
4901 | err = got_object_id_by_path(&tree_id, repo, commit_id, path); | |||
4902 | if (err) | |||
4903 | goto done; | |||
4904 | ||||
4905 | err = got_object_open_as_tree(&tree, repo, tree_id); | |||
4906 | if (err) | |||
4907 | goto done; | |||
4908 | nentries = got_object_tree_get_nentries(tree); | |||
4909 | for (i = 0; i < nentries; i++) { | |||
4910 | struct got_tree_entry *te; | |||
4911 | char *id = NULL((void *)0); | |||
4912 | ||||
4913 | if (sigint_received || sigpipe_received) | |||
4914 | break; | |||
4915 | ||||
4916 | te = got_object_tree_get_entry(tree, i); | |||
4917 | if (show_ids) { | |||
4918 | char *id_str; | |||
4919 | err = got_object_id_str(&id_str, | |||
4920 | got_tree_entry_get_id(te)); | |||
4921 | if (err) | |||
4922 | goto done; | |||
4923 | if (asprintf(&id, "%s ", id_str) == -1) { | |||
4924 | err = got_error_from_errno("asprintf"); | |||
4925 | free(id_str); | |||
4926 | goto done; | |||
4927 | } | |||
4928 | free(id_str); | |||
4929 | } | |||
4930 | err = print_entry(te, id, path, root_path, repo); | |||
4931 | free(id); | |||
4932 | if (err) | |||
4933 | goto done; | |||
4934 | ||||
4935 | if (recurse && S_ISDIR(got_tree_entry_get_mode(te))((got_tree_entry_get_mode(te) & 0170000) == 0040000)) { | |||
4936 | char *child_path; | |||
4937 | if (asprintf(&child_path, "%s%s%s", path, | |||
4938 | path[0] == '/' && path[1] == '\0' ? "" : "/", | |||
4939 | got_tree_entry_get_name(te)) == -1) { | |||
4940 | err = got_error_from_errno("asprintf"); | |||
4941 | goto done; | |||
4942 | } | |||
4943 | err = print_tree(child_path, commit_id, show_ids, 1, | |||
4944 | root_path, repo); | |||
4945 | free(child_path); | |||
4946 | if (err) | |||
4947 | goto done; | |||
4948 | } | |||
4949 | } | |||
4950 | done: | |||
4951 | if (tree) | |||
4952 | got_object_tree_close(tree); | |||
4953 | free(tree_id); | |||
4954 | return err; | |||
4955 | } | |||
4956 | ||||
4957 | static const struct got_error * | |||
4958 | cmd_tree(int argc, char *argv[]) | |||
4959 | { | |||
4960 | const struct got_error *error; | |||
4961 | struct got_repository *repo = NULL((void *)0); | |||
4962 | struct got_worktree *worktree = NULL((void *)0); | |||
4963 | const char *path, *refname = NULL((void *)0); | |||
4964 | char *cwd = NULL((void *)0), *repo_path = NULL((void *)0), *in_repo_path = NULL((void *)0); | |||
4965 | struct got_object_id *commit_id = NULL((void *)0); | |||
4966 | char *commit_id_str = NULL((void *)0); | |||
4967 | int show_ids = 0, recurse = 0; | |||
4968 | int ch; | |||
4969 | ||||
4970 | #ifndef PROFILE | |||
4971 | if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil", | |||
4972 | NULL((void *)0)) == -1) | |||
4973 | err(1, "pledge"); | |||
4974 | #endif | |||
4975 | ||||
4976 | while ((ch = getopt(argc, argv, "c:r:iR")) != -1) { | |||
4977 | switch (ch) { | |||
4978 | case 'c': | |||
4979 | commit_id_str = optarg; | |||
4980 | break; | |||
4981 | case 'r': | |||
4982 | repo_path = realpath(optarg, NULL((void *)0)); | |||
4983 | if (repo_path == NULL((void *)0)) | |||
4984 | return got_error_from_errno2("realpath", | |||
4985 | optarg); | |||
4986 | got_path_strip_trailing_slashes(repo_path); | |||
4987 | break; | |||
4988 | case 'i': | |||
4989 | show_ids = 1; | |||
4990 | break; | |||
4991 | case 'R': | |||
4992 | recurse = 1; | |||
4993 | break; | |||
4994 | default: | |||
4995 | usage_tree(); | |||
4996 | /* NOTREACHED */ | |||
4997 | } | |||
4998 | } | |||
4999 | ||||
5000 | argc -= optind; | |||
5001 | argv += optind; | |||
5002 | ||||
5003 | if (argc == 1) | |||
5004 | path = argv[0]; | |||
5005 | else if (argc > 1) | |||
5006 | usage_tree(); | |||
5007 | else | |||
5008 | path = NULL((void *)0); | |||
5009 | ||||
5010 | cwd = getcwd(NULL((void *)0), 0); | |||
5011 | if (cwd == NULL((void *)0)) { | |||
5012 | error = got_error_from_errno("getcwd"); | |||
5013 | goto done; | |||
5014 | } | |||
5015 | if (repo_path == NULL((void *)0)) { | |||
5016 | error = got_worktree_open(&worktree, cwd); | |||
5017 | if (error && error->code != GOT_ERR_NOT_WORKTREE60) | |||
5018 | goto done; | |||
5019 | else | |||
5020 | error = NULL((void *)0); | |||
5021 | if (worktree) { | |||
5022 | repo_path = | |||
5023 | strdup(got_worktree_get_repo_path(worktree)); | |||
5024 | if (repo_path == NULL((void *)0)) | |||
5025 | error = got_error_from_errno("strdup"); | |||
5026 | if (error) | |||
5027 | goto done; | |||
5028 | } else { | |||
5029 | repo_path = strdup(cwd); | |||
5030 | if (repo_path == NULL((void *)0)) { | |||
5031 | error = got_error_from_errno("strdup"); | |||
5032 | goto done; | |||
5033 | } | |||
5034 | } | |||
5035 | } | |||
5036 | ||||
5037 | error = got_repo_open(&repo, repo_path, NULL((void *)0)); | |||
5038 | if (error != NULL((void *)0)) | |||
5039 | goto done; | |||
5040 | ||||
5041 | if (worktree) { | |||
5042 | const char *prefix = got_worktree_get_path_prefix(worktree); | |||
5043 | char *p; | |||
5044 | ||||
5045 | if (path == NULL((void *)0)) | |||
5046 | path = ""; | |||
5047 | error = got_worktree_resolve_path(&p, worktree, path); | |||
5048 | if (error) | |||
5049 | goto done; | |||
5050 | if (asprintf(&in_repo_path, "%s%s%s", prefix, | |||
5051 | (p[0] != '\0' && !got_path_is_root_dir(prefix)) ? "/" : "", | |||
5052 | p) == -1) { | |||
5053 | error = got_error_from_errno("asprintf"); | |||
5054 | free(p); | |||
5055 | goto done; | |||
5056 | } | |||
5057 | free(p); | |||
5058 | error = apply_unveil(got_repo_get_path(repo), 1, NULL((void *)0)); | |||
5059 | if (error) | |||
5060 | goto done; | |||
5061 | } else { | |||
5062 | error = apply_unveil(got_repo_get_path(repo), 1, NULL((void *)0)); | |||
5063 | if (error) | |||
5064 | goto done; | |||
5065 | if (path == NULL((void *)0)) | |||
5066 | path = "/"; | |||
5067 | error = got_repo_map_path(&in_repo_path, repo, path); | |||
5068 | if (error != NULL((void *)0)) | |||
5069 | goto done; | |||
5070 | } | |||
5071 | ||||
5072 | if (commit_id_str == NULL((void *)0)) { | |||
5073 | struct got_reference *head_ref; | |||
5074 | if (worktree) | |||
5075 | refname = got_worktree_get_head_ref_name(worktree); | |||
5076 | else | |||
5077 | refname = GOT_REF_HEAD"HEAD"; | |||
5078 | error = got_ref_open(&head_ref, repo, refname, 0); | |||
5079 | if (error != NULL((void *)0)) | |||
5080 | goto done; | |||
5081 | error = got_ref_resolve(&commit_id, repo, head_ref); | |||
5082 | got_ref_close(head_ref); | |||
5083 | if (error != NULL((void *)0)) | |||
5084 | goto done; | |||
5085 | } else { | |||
5086 | struct got_reflist_head refs; | |||
5087 | TAILQ_INIT(&refs)do { (&refs)->tqh_first = ((void *)0); (&refs)-> tqh_last = &(&refs)->tqh_first; } while (0); | |||
5088 | error = got_ref_list(&refs, repo, NULL((void *)0), got_ref_cmp_by_name, | |||
5089 | NULL((void *)0)); | |||
5090 | if (error) | |||
5091 | goto done; | |||
5092 | error = got_repo_match_object_id(&commit_id, NULL((void *)0), | |||
5093 | commit_id_str, GOT_OBJ_TYPE_COMMIT1, &refs, repo); | |||
5094 | got_ref_list_free(&refs); | |||
5095 | if (error) | |||
5096 | goto done; | |||
5097 | } | |||
5098 | ||||
5099 | error = print_tree(in_repo_path, commit_id, show_ids, recurse, | |||
5100 | in_repo_path, repo); | |||
5101 | done: | |||
5102 | free(in_repo_path); | |||
5103 | free(repo_path); | |||
5104 | free(cwd); | |||
5105 | free(commit_id); | |||
5106 | if (worktree) | |||
5107 | got_worktree_close(worktree); | |||
5108 | if (repo) { | |||
5109 | const struct got_error *repo_error; | |||
5110 | repo_error = got_repo_close(repo); | |||
5111 | if (error == NULL((void *)0)) | |||
5112 | error = repo_error; | |||
5113 | } | |||
5114 | return error; | |||
5115 | } | |||
5116 | ||||
5117 | __dead__attribute__((__noreturn__)) static void | |||
5118 | usage_status(void) | |||
5119 | { | |||
5120 | fprintf(stderr(&__sF[2]), "usage: %s status [-s status-codes ] [path ...]\n", | |||
5121 | getprogname()); | |||
5122 | exit(1); | |||
5123 | } | |||
5124 | ||||
5125 | static const struct got_error * | |||
5126 | print_status(void *arg, unsigned char status, unsigned char staged_status, | |||
5127 | const char *path, struct got_object_id *blob_id, | |||
5128 | struct got_object_id *staged_blob_id, struct got_object_id *commit_id, | |||
5129 | int dirfd, const char *de_name) | |||
5130 | { | |||
5131 | if (status == staged_status && (status == GOT_STATUS_DELETE'D')) | |||
5132 | status = GOT_STATUS_NO_CHANGE' '; | |||
5133 | if (arg) { | |||
5134 | char *status_codes = arg; | |||
5135 | size_t ncodes = strlen(status_codes); | |||
5136 | int i; | |||
5137 | for (i = 0; i < ncodes ; i++) { | |||
5138 | if (status == status_codes[i] || | |||
5139 | staged_status == status_codes[i]) | |||
5140 | break; | |||
5141 | } | |||
5142 | if (i == ncodes) | |||
5143 | return NULL((void *)0); | |||
5144 | } | |||
5145 | printf("%c%c %s\n", status, staged_status, path); | |||
5146 | return NULL((void *)0); | |||
5147 | } | |||
5148 | ||||
5149 | static const struct got_error * | |||
5150 | cmd_status(int argc, char *argv[]) | |||
5151 | { | |||
5152 | const struct got_error *error = NULL((void *)0); | |||
5153 | struct got_repository *repo = NULL((void *)0); | |||
5154 | struct got_worktree *worktree = NULL((void *)0); | |||
5155 | char *cwd = NULL((void *)0), *status_codes = NULL((void *)0);; | |||
5156 | struct got_pathlist_head paths; | |||
5157 | struct got_pathlist_entry *pe; | |||
5158 | int ch, i; | |||
5159 | ||||
5160 | TAILQ_INIT(&paths)do { (&paths)->tqh_first = ((void *)0); (&paths)-> tqh_last = &(&paths)->tqh_first; } while (0); | |||
5161 | ||||
5162 | while ((ch = getopt(argc, argv, "s:")) != -1) { | |||
5163 | switch (ch) { | |||
5164 | case 's': | |||
5165 | for (i = 0; i < strlen(optarg); i++) { | |||
5166 | switch (optarg[i]) { | |||
5167 | case GOT_STATUS_MODIFY'M': | |||
5168 | case GOT_STATUS_ADD'A': | |||
5169 | case GOT_STATUS_DELETE'D': | |||
5170 | case GOT_STATUS_CONFLICT'C': | |||
5171 | case GOT_STATUS_MISSING'!': | |||
5172 | case GOT_STATUS_OBSTRUCTED'~': | |||
5173 | case GOT_STATUS_UNVERSIONED'?': | |||
5174 | case GOT_STATUS_MODE_CHANGE'm': | |||
5175 | case GOT_STATUS_NONEXISTENT'N': | |||
5176 | break; | |||
5177 | default: | |||
5178 | errx(1, "invalid status code '%c'", | |||
5179 | optarg[i]); | |||
5180 | } | |||
5181 | } | |||
5182 | status_codes = optarg; | |||
5183 | break; | |||
5184 | default: | |||
5185 | usage_status(); | |||
5186 | /* NOTREACHED */ | |||
5187 | } | |||
5188 | } | |||
5189 | ||||
5190 | argc -= optind; | |||
5191 | argv += optind; | |||
5192 | ||||
5193 | #ifndef PROFILE | |||
5194 | if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil", | |||
5195 | NULL((void *)0)) == -1) | |||
5196 | err(1, "pledge"); | |||
5197 | #endif | |||
5198 | cwd = getcwd(NULL((void *)0), 0); | |||
5199 | if (cwd == NULL((void *)0)) { | |||
5200 | error = got_error_from_errno("getcwd"); | |||
5201 | goto done; | |||
5202 | } | |||
5203 | ||||
5204 | error = got_worktree_open(&worktree, cwd); | |||
5205 | if (error) { | |||
5206 | if (error->code == GOT_ERR_NOT_WORKTREE60) | |||
5207 | error = wrap_not_worktree_error(error, "status", cwd); | |||
5208 | goto done; | |||
5209 | } | |||
5210 | ||||
5211 | error = got_repo_open(&repo, got_worktree_get_repo_path(worktree), | |||
5212 | NULL((void *)0)); | |||
5213 | if (error != NULL((void *)0)) | |||
5214 | goto done; | |||
5215 | ||||
5216 | error = apply_unveil(got_repo_get_path(repo), 1, | |||
5217 | got_worktree_get_root_path(worktree)); | |||
5218 | if (error) | |||
5219 | goto done; | |||
5220 | ||||
5221 | error = get_worktree_paths_from_argv(&paths, argc, argv, worktree); | |||
5222 | if (error) | |||
5223 | goto done; | |||
5224 | ||||
5225 | error = got_worktree_status(worktree, &paths, repo, print_status, | |||
5226 | status_codes, check_cancelled, NULL((void *)0)); | |||
5227 | done: | |||
5228 | TAILQ_FOREACH(pe, &paths, entry)for((pe) = ((&paths)->tqh_first); (pe) != ((void *)0); (pe) = ((pe)->entry.tqe_next)) | |||
5229 | free((char *)pe->path); | |||
5230 | got_pathlist_free(&paths); | |||
5231 | free(cwd); | |||
5232 | return error; | |||
5233 | } | |||
5234 | ||||
5235 | __dead__attribute__((__noreturn__)) static void | |||
5236 | usage_ref(void) | |||
5237 | { | |||
5238 | fprintf(stderr(&__sF[2]), | |||
5239 | "usage: %s ref [-r repository] [-l] [-c object] [-s reference] " | |||
5240 | "[-d] [name]\n", | |||
5241 | getprogname()); | |||
5242 | exit(1); | |||
5243 | } | |||
5244 | ||||
5245 | static const struct got_error * | |||
5246 | list_refs(struct got_repository *repo, const char *refname) | |||
5247 | { | |||
5248 | static const struct got_error *err = NULL((void *)0); | |||
5249 | struct got_reflist_head refs; | |||
5250 | struct got_reflist_entry *re; | |||
5251 | ||||
5252 | TAILQ_INIT(&refs)do { (&refs)->tqh_first = ((void *)0); (&refs)-> tqh_last = &(&refs)->tqh_first; } while (0); | |||
5253 | err = got_ref_list(&refs, repo, refname, got_ref_cmp_by_name, NULL((void *)0)); | |||
5254 | if (err) | |||
5255 | return err; | |||
5256 | ||||
5257 | TAILQ_FOREACH(re, &refs, entry)for((re) = ((&refs)->tqh_first); (re) != ((void *)0); ( re) = ((re)->entry.tqe_next)) { | |||
5258 | char *refstr; | |||
5259 | refstr = got_ref_to_str(re->ref); | |||
5260 | if (refstr == NULL((void *)0)) | |||
5261 | return got_error_from_errno("got_ref_to_str"); | |||
5262 | printf("%s: %s\n", got_ref_get_name(re->ref), refstr); | |||
5263 | free(refstr); | |||
5264 | } | |||
5265 | ||||
5266 | got_ref_list_free(&refs); | |||
5267 | return NULL((void *)0); | |||
5268 | } | |||
5269 | ||||
5270 | static const struct got_error * | |||
5271 | delete_ref(struct got_repository *repo, const char *refname) | |||
5272 | { | |||
5273 | const struct got_error *err = NULL((void *)0); | |||
5274 | struct got_reference *ref; | |||
5275 | ||||
5276 | err = got_ref_open(&ref, repo, refname, 0); | |||
5277 | if (err) | |||
5278 | return err; | |||
5279 | ||||
5280 | err = got_ref_delete(ref, repo); | |||
5281 | got_ref_close(ref); | |||
5282 | return err; | |||
5283 | } | |||
5284 | ||||
5285 | static const struct got_error * | |||
5286 | add_ref(struct got_repository *repo, const char *refname, const char *target) | |||
5287 | { | |||
5288 | const struct got_error *err = NULL((void *)0); | |||
5289 | struct got_object_id *id; | |||
5290 | struct got_reference *ref = NULL((void *)0); | |||
5291 | ||||
5292 | /* | |||
5293 | * Don't let the user create a reference name with a leading '-'. | |||
5294 | * While technically a valid reference name, this case is usually | |||
5295 | * an unintended typo. | |||
5296 | */ | |||
5297 | if (refname[0] == '-') | |||
5298 | return got_error_path(refname, GOT_ERR_REF_NAME_MINUS113); | |||
5299 | ||||
5300 | err = got_repo_match_object_id_prefix(&id, target, GOT_OBJ_TYPE_ANY0, | |||
5301 | repo); | |||
5302 | if (err) { | |||
5303 | struct got_reference *target_ref; | |||
5304 | ||||
5305 | if (err->code != GOT_ERR_BAD_OBJ_ID_STR23) | |||
5306 | return err; | |||
5307 | err = got_ref_open(&target_ref, repo, target, 0); | |||
5308 | if (err) | |||
5309 | return err; | |||
5310 | err = got_ref_resolve(&id, repo, target_ref); | |||
5311 | got_ref_close(target_ref); | |||
5312 | if (err) | |||
5313 | return err; | |||
5314 | } | |||
5315 | ||||
5316 | err = got_ref_alloc(&ref, refname, id); | |||
5317 | if (err) | |||
5318 | goto done; | |||
5319 | ||||
5320 | err = got_ref_write(ref, repo); | |||
5321 | done: | |||
5322 | if (ref) | |||
5323 | got_ref_close(ref); | |||
5324 | free(id); | |||
5325 | return err; | |||
5326 | } | |||
5327 | ||||
5328 | static const struct got_error * | |||
5329 | add_symref(struct got_repository *repo, const char *refname, const char *target) | |||
5330 | { | |||
5331 | const struct got_error *err = NULL((void *)0); | |||
5332 | struct got_reference *ref = NULL((void *)0); | |||
5333 | struct got_reference *target_ref = NULL((void *)0); | |||
5334 | ||||
5335 | /* | |||
5336 | * Don't let the user create a reference name with a leading '-'. | |||
5337 | * While technically a valid reference name, this case is usually | |||
5338 | * an unintended typo. | |||
5339 | */ | |||
5340 | if (refname[0] == '-') | |||
5341 | return got_error_path(refname, GOT_ERR_REF_NAME_MINUS113); | |||
5342 | ||||
5343 | err = got_ref_open(&target_ref, repo, target, 0); | |||
5344 | if (err) | |||
5345 | return err; | |||
5346 | ||||
5347 | err = got_ref_alloc_symref(&ref, refname, target_ref); | |||
5348 | if (err) | |||
5349 | goto done; | |||
5350 | ||||
5351 | err = got_ref_write(ref, repo); | |||
5352 | done: | |||
5353 | if (target_ref) | |||
5354 | got_ref_close(target_ref); | |||
5355 | if (ref) | |||
5356 | got_ref_close(ref); | |||
5357 | return err; | |||
5358 | } | |||
5359 | ||||
5360 | static const struct got_error * | |||
5361 | cmd_ref(int argc, char *argv[]) | |||
5362 | { | |||
5363 | const struct got_error *error = NULL((void *)0); | |||
5364 | struct got_repository *repo = NULL((void *)0); | |||
5365 | struct got_worktree *worktree = NULL((void *)0); | |||
5366 | char *cwd = NULL((void *)0), *repo_path = NULL((void *)0); | |||
5367 | int ch, do_list = 0, do_delete = 0; | |||
5368 | const char *obj_arg = NULL((void *)0), *symref_target= NULL((void *)0); | |||
5369 | char *refname = NULL((void *)0); | |||
5370 | ||||
5371 | while ((ch = getopt(argc, argv, "c:dr:ls:")) != -1) { | |||
5372 | switch (ch) { | |||
5373 | case 'c': | |||
5374 | obj_arg = optarg; | |||
5375 | break; | |||
5376 | case 'd': | |||
5377 | do_delete = 1; | |||
5378 | break; | |||
5379 | case 'r': | |||
5380 | repo_path = realpath(optarg, NULL((void *)0)); | |||
5381 | if (repo_path == NULL((void *)0)) | |||
5382 | return got_error_from_errno2("realpath", | |||
5383 | optarg); | |||
5384 | got_path_strip_trailing_slashes(repo_path); | |||
5385 | break; | |||
5386 | case 'l': | |||
5387 | do_list = 1; | |||
5388 | break; | |||
5389 | case 's': | |||
5390 | symref_target = optarg; | |||
5391 | break; | |||
5392 | default: | |||
5393 | usage_ref(); | |||
5394 | /* NOTREACHED */ | |||
5395 | } | |||
5396 | } | |||
5397 | ||||
5398 | if (obj_arg && do_list) | |||
5399 | option_conflict('c', 'l'); | |||
5400 | if (obj_arg && do_delete) | |||
5401 | option_conflict('c', 'd'); | |||
5402 | if (obj_arg && symref_target) | |||
5403 | option_conflict('c', 's'); | |||
5404 | if (symref_target && do_delete) | |||
5405 | option_conflict('s', 'd'); | |||
5406 | if (symref_target && do_list) | |||
5407 | option_conflict('s', 'l'); | |||
5408 | if (do_delete && do_list) | |||
5409 | option_conflict('d', 'l'); | |||
5410 | ||||
5411 | argc -= optind; | |||
5412 | argv += optind; | |||
5413 | ||||
5414 | if (do_list) { | |||
5415 | if (argc != 0 && argc != 1) | |||
5416 | usage_ref(); | |||
5417 | if (argc == 1) { | |||
5418 | refname = strdup(argv[0]); | |||
5419 | if (refname == NULL((void *)0)) { | |||
5420 | error = got_error_from_errno("strdup"); | |||
5421 | goto done; | |||
5422 | } | |||
5423 | } | |||
5424 | } else { | |||
5425 | if (argc != 1) | |||
5426 | usage_ref(); | |||
5427 | refname = strdup(argv[0]); | |||
5428 | if (refname == NULL((void *)0)) { | |||
5429 | error = got_error_from_errno("strdup"); | |||
5430 | goto done; | |||
5431 | } | |||
5432 | } | |||
5433 | ||||
5434 | if (refname) | |||
5435 | got_path_strip_trailing_slashes(refname); | |||
5436 | ||||
5437 | #ifndef PROFILE | |||
5438 | if (do_list) { | |||
5439 | if (pledge("stdio rpath wpath flock proc exec sendfd unveil", | |||
5440 | NULL((void *)0)) == -1) | |||
5441 | err(1, "pledge"); | |||
5442 | } else { | |||
5443 | if (pledge("stdio rpath wpath cpath fattr flock proc exec " | |||
5444 | "sendfd unveil", NULL((void *)0)) == -1) | |||
5445 | err(1, "pledge"); | |||
5446 | } | |||
5447 | #endif | |||
5448 | cwd = getcwd(NULL((void *)0), 0); | |||
5449 | if (cwd == NULL((void *)0)) { | |||
5450 | error = got_error_from_errno("getcwd"); | |||
5451 | goto done; | |||
5452 | } | |||
5453 | ||||
5454 | if (repo_path == NULL((void *)0)) { | |||
5455 | error = got_worktree_open(&worktree, cwd); | |||
5456 | if (error && error->code != GOT_ERR_NOT_WORKTREE60) | |||
5457 | goto done; | |||
5458 | else | |||
5459 | error = NULL((void *)0); | |||
5460 | if (worktree) { | |||
5461 | repo_path = | |||
5462 | strdup(got_worktree_get_repo_path(worktree)); | |||
5463 | if (repo_path == NULL((void *)0)) | |||
5464 | error = got_error_from_errno("strdup"); | |||
5465 | if (error) | |||
5466 | goto done; | |||
5467 | } else { | |||
5468 | repo_path = strdup(cwd); | |||
5469 | if (repo_path == NULL((void *)0)) { | |||
5470 | error = got_error_from_errno("strdup"); | |||
5471 | goto done; | |||
5472 | } | |||
5473 | } | |||
5474 | } | |||
5475 | ||||
5476 | error = got_repo_open(&repo, repo_path, NULL((void *)0)); | |||
5477 | if (error != NULL((void *)0)) | |||
5478 | goto done; | |||
5479 | ||||
5480 | error = apply_unveil(got_repo_get_path(repo), do_list, | |||
5481 | worktree ? got_worktree_get_root_path(worktree) : NULL((void *)0)); | |||
5482 | if (error) | |||
5483 | goto done; | |||
5484 | ||||
5485 | if (do_list) | |||
5486 | error = list_refs(repo, refname); | |||
5487 | else if (do_delete) | |||
5488 | error = delete_ref(repo, refname); | |||
5489 | else if (symref_target) | |||
5490 | error = add_symref(repo, refname, symref_target); | |||
5491 | else { | |||
5492 | if (obj_arg == NULL((void *)0)) | |||
5493 | usage_ref(); | |||
5494 | error = add_ref(repo, refname, obj_arg); | |||
5495 | } | |||
5496 | done: | |||
5497 | free(refname); | |||
5498 | if (repo) | |||
5499 | got_repo_close(repo); | |||
5500 | if (worktree) | |||
5501 | got_worktree_close(worktree); | |||
5502 | free(cwd); | |||
5503 | free(repo_path); | |||
5504 | return error; | |||
5505 | } | |||
5506 | ||||
5507 | __dead__attribute__((__noreturn__)) static void | |||
5508 | usage_branch(void) | |||
5509 | { | |||
5510 | fprintf(stderr(&__sF[2]), | |||
5511 | "usage: %s branch [-c commit] [-d] [-r repository] [-l] [-n] " | |||
5512 | "[name]\n", getprogname()); | |||
5513 | exit(1); | |||
5514 | } | |||
5515 | ||||
5516 | static const struct got_error * | |||
5517 | list_branch(struct got_repository *repo, struct got_worktree *worktree, | |||
5518 | struct got_reference *ref) | |||
5519 | { | |||
5520 | const struct got_error *err = NULL((void *)0); | |||
5521 | const char *refname, *marker = " "; | |||
5522 | char *refstr; | |||
5523 | ||||
5524 | refname = got_ref_get_name(ref); | |||
5525 | if (worktree && strcmp(refname, | |||
5526 | got_worktree_get_head_ref_name(worktree)) == 0) { | |||
5527 | struct got_object_id *id = NULL((void *)0); | |||
5528 | ||||
5529 | err = got_ref_resolve(&id, repo, ref); | |||
5530 | if (err) | |||
5531 | return err; | |||
5532 | if (got_object_id_cmp(id, | |||
5533 | got_worktree_get_base_commit_id(worktree)) == 0) | |||
5534 | marker = "* "; | |||
5535 | else | |||
5536 | marker = "~ "; | |||
5537 | free(id); | |||
5538 | } | |||
5539 | ||||
5540 | if (strncmp(refname, "refs/heads/", 11) == 0) | |||
5541 | refname += 11; | |||
5542 | if (strncmp(refname, "refs/got/worktree/", 18) == 0) | |||
5543 | refname += 18; | |||
5544 | if (strncmp(refname, "refs/remotes/", 13) == 0) | |||
5545 | refname += 13; | |||
5546 | ||||
5547 | refstr = got_ref_to_str(ref); | |||
5548 | if (refstr == NULL((void *)0)) | |||
5549 | return got_error_from_errno("got_ref_to_str"); | |||
5550 | ||||
5551 | printf("%s%s: %s\n", marker, refname, refstr); | |||
5552 | free(refstr); | |||
5553 | return NULL((void *)0); | |||
5554 | } | |||
5555 | ||||
5556 | static const struct got_error * | |||
5557 | show_current_branch(struct got_repository *repo, struct got_worktree *worktree) | |||
5558 | { | |||
5559 | const char *refname; | |||
5560 | ||||
5561 | if (worktree == NULL((void *)0)) | |||
5562 | return got_error(GOT_ERR_NOT_WORKTREE60); | |||
5563 | ||||
5564 | refname = got_worktree_get_head_ref_name(worktree); | |||
5565 | ||||
5566 | if (strncmp(refname, "refs/heads/", 11) == 0) | |||
5567 | refname += 11; | |||
5568 | if (strncmp(refname, "refs/got/worktree/", 18) == 0) | |||
5569 | refname += 18; | |||
5570 | ||||
5571 | printf("%s\n", refname); | |||
5572 | ||||
5573 | return NULL((void *)0); | |||
5574 | } | |||
5575 | ||||
5576 | static const struct got_error * | |||
5577 | list_branches(struct got_repository *repo, struct got_worktree *worktree) | |||
5578 | { | |||
5579 | static const struct got_error *err = NULL((void *)0); | |||
5580 | struct got_reflist_head refs; | |||
5581 | struct got_reflist_entry *re; | |||
5582 | struct got_reference *temp_ref = NULL((void *)0); | |||
5583 | int rebase_in_progress, histedit_in_progress; | |||
5584 | ||||
5585 | TAILQ_INIT(&refs)do { (&refs)->tqh_first = ((void *)0); (&refs)-> tqh_last = &(&refs)->tqh_first; } while (0); | |||
5586 | ||||
5587 | if (worktree) { | |||
5588 | err = got_worktree_rebase_in_progress(&rebase_in_progress, | |||
5589 | worktree); | |||
5590 | if (err) | |||
5591 | return err; | |||
5592 | ||||
5593 | err = got_worktree_histedit_in_progress(&histedit_in_progress, | |||
5594 | worktree); | |||
5595 | if (err) | |||
5596 | return err; | |||
5597 | ||||
5598 | if (rebase_in_progress || histedit_in_progress) { | |||
5599 | err = got_ref_open(&temp_ref, repo, | |||
5600 | got_worktree_get_head_ref_name(worktree), 0); | |||
5601 | if (err) | |||
5602 | return err; | |||
5603 | list_branch(repo, worktree, temp_ref); | |||
5604 | got_ref_close(temp_ref); | |||
5605 | } | |||
5606 | } | |||
5607 | ||||
5608 | err = got_ref_list(&refs, repo, "refs/heads", | |||
5609 | got_ref_cmp_by_name, NULL((void *)0)); | |||
5610 | if (err) | |||
5611 | return err; | |||
5612 | ||||
5613 | TAILQ_FOREACH(re, &refs, entry)for((re) = ((&refs)->tqh_first); (re) != ((void *)0); ( re) = ((re)->entry.tqe_next)) | |||
5614 | list_branch(repo, worktree, re->ref); | |||
5615 | ||||
5616 | got_ref_list_free(&refs); | |||
5617 | ||||
5618 | err = got_ref_list(&refs, repo, "refs/remotes", | |||
5619 | got_ref_cmp_by_name, NULL((void *)0)); | |||
5620 | if (err) | |||
5621 | return err; | |||
5622 | ||||
5623 | TAILQ_FOREACH(re, &refs, entry)for((re) = ((&refs)->tqh_first); (re) != ((void *)0); ( re) = ((re)->entry.tqe_next)) | |||
5624 | list_branch(repo, worktree, re->ref); | |||
5625 | ||||
5626 | got_ref_list_free(&refs); | |||
5627 | ||||
5628 | return NULL((void *)0); | |||
5629 | } | |||
5630 | ||||
5631 | static const struct got_error * | |||
5632 | delete_branch(struct got_repository *repo, struct got_worktree *worktree, | |||
5633 | const char *branch_name) | |||
5634 | { | |||
5635 | const struct got_error *err = NULL((void *)0); | |||
5636 | struct got_reference *ref = NULL((void *)0); | |||
5637 | char *refname; | |||
5638 | ||||
5639 | if (asprintf(&refname, "refs/heads/%s", branch_name) == -1) | |||
5640 | return got_error_from_errno("asprintf"); | |||
5641 | ||||
5642 | err = got_ref_open(&ref, repo, refname, 0); | |||
5643 | if (err) | |||
5644 | goto done; | |||
5645 | ||||
5646 | if (worktree && | |||
5647 | strcmp(got_worktree_get_head_ref_name(worktree), | |||
5648 | got_ref_get_name(ref)) == 0) { | |||
5649 | err = got_error_msg(GOT_ERR_SAME_BRANCH79, | |||
5650 | "will not delete this work tree's current branch"); | |||
5651 | goto done; | |||
5652 | } | |||
5653 | ||||
5654 | err = got_ref_delete(ref, repo); | |||
5655 | done: | |||
5656 | if (ref) | |||
5657 | got_ref_close(ref); | |||
5658 | free(refname); | |||
5659 | return err; | |||
5660 | } | |||
5661 | ||||
5662 | static const struct got_error * | |||
5663 | add_branch(struct got_repository *repo, const char *branch_name, | |||
5664 | struct got_object_id *base_commit_id) | |||
5665 | { | |||
5666 | const struct got_error *err = NULL((void *)0); | |||
5667 | struct got_reference *ref = NULL((void *)0); | |||
5668 | char *base_refname = NULL((void *)0), *refname = NULL((void *)0); | |||
5669 | ||||
5670 | /* | |||
5671 | * Don't let the user create a branch name with a leading '-'. | |||
5672 | * While technically a valid reference name, this case is usually | |||
5673 | * an unintended typo. | |||
5674 | */ | |||
5675 | if (branch_name[0] == '-') | |||
5676 | return got_error_path(branch_name, GOT_ERR_REF_NAME_MINUS113); | |||
5677 | ||||
5678 | if (asprintf(&refname, "refs/heads/%s", branch_name) == -1) { | |||
5679 | err = got_error_from_errno("asprintf"); | |||
5680 | goto done; | |||
5681 | } | |||
5682 | ||||
5683 | err = got_ref_open(&ref, repo, refname, 0); | |||
5684 | if (err == NULL((void *)0)) { | |||
5685 | err = got_error(GOT_ERR_BRANCH_EXISTS83); | |||
5686 | goto done; | |||
5687 | } else if (err->code != GOT_ERR_NOT_REF5) | |||
5688 | goto done; | |||
5689 | ||||
5690 | err = got_ref_alloc(&ref, refname, base_commit_id); | |||
5691 | if (err) | |||
5692 | goto done; | |||
5693 | ||||
5694 | err = got_ref_write(ref, repo); | |||
5695 | done: | |||
5696 | if (ref) | |||
5697 | got_ref_close(ref); | |||
5698 | free(base_refname); | |||
5699 | free(refname); | |||
5700 | return err; | |||
5701 | } | |||
5702 | ||||
5703 | static const struct got_error * | |||
5704 | cmd_branch(int argc, char *argv[]) | |||
5705 | { | |||
5706 | const struct got_error *error = NULL((void *)0); | |||
5707 | struct got_repository *repo = NULL((void *)0); | |||
5708 | struct got_worktree *worktree = NULL((void *)0); | |||
5709 | char *cwd = NULL((void *)0), *repo_path = NULL((void *)0); | |||
5710 | int ch, do_list = 0, do_show = 0, do_update = 1; | |||
5711 | const char *delref = NULL((void *)0), *commit_id_arg = NULL((void *)0); | |||
5712 | struct got_reference *ref = NULL((void *)0); | |||
5713 | struct got_pathlist_head paths; | |||
5714 | struct got_pathlist_entry *pe; | |||
5715 | struct got_object_id *commit_id = NULL((void *)0); | |||
5716 | char *commit_id_str = NULL((void *)0); | |||
5717 | ||||
5718 | TAILQ_INIT(&paths)do { (&paths)->tqh_first = ((void *)0); (&paths)-> tqh_last = &(&paths)->tqh_first; } while (0); | |||
5719 | ||||
5720 | while ((ch = getopt(argc, argv, "c:d:r:ln")) != -1) { | |||
5721 | switch (ch) { | |||
5722 | case 'c': | |||
5723 | commit_id_arg = optarg; | |||
5724 | break; | |||
5725 | case 'd': | |||
5726 | delref = optarg; | |||
5727 | break; | |||
5728 | case 'r': | |||
5729 | repo_path = realpath(optarg, NULL((void *)0)); | |||
5730 | if (repo_path == NULL((void *)0)) | |||
5731 | return got_error_from_errno2("realpath", | |||
5732 | optarg); | |||
5733 | got_path_strip_trailing_slashes(repo_path); | |||
5734 | break; | |||
5735 | case 'l': | |||
5736 | do_list = 1; | |||
5737 | break; | |||
5738 | case 'n': | |||
5739 | do_update = 0; | |||
5740 | break; | |||
5741 | default: | |||
5742 | usage_branch(); | |||
5743 | /* NOTREACHED */ | |||
5744 | } | |||
5745 | } | |||
5746 | ||||
5747 | if (do_list && delref) | |||
5748 | option_conflict('l', 'd'); | |||
5749 | ||||
5750 | argc -= optind; | |||
5751 | argv += optind; | |||
5752 | ||||
5753 | if (!do_list && !delref && argc == 0) | |||
5754 | do_show = 1; | |||
5755 | ||||
5756 | if ((do_list || delref || do_show) && commit_id_arg != NULL((void *)0)) | |||
5757 | errx(1, "-c option can only be used when creating a branch"); | |||
5758 | ||||
5759 | if (do_list || delref) { | |||
5760 | if (argc > 0) | |||
5761 | usage_branch(); | |||
5762 | } else if (!do_show && argc != 1) | |||
5763 | usage_branch(); | |||
5764 | ||||
5765 | #ifndef PROFILE | |||
5766 | if (do_list || do_show) { | |||
5767 | if (pledge("stdio rpath wpath flock proc exec sendfd unveil", | |||
5768 | NULL((void *)0)) == -1) | |||
5769 | err(1, "pledge"); | |||
5770 | } else { | |||
5771 | if (pledge("stdio rpath wpath cpath fattr flock proc exec " | |||
5772 | "sendfd unveil", NULL((void *)0)) == -1) | |||
5773 | err(1, "pledge"); | |||
5774 | } | |||
5775 | #endif | |||
5776 | cwd = getcwd(NULL((void *)0), 0); | |||
5777 | if (cwd == NULL((void *)0)) { | |||
5778 | error = got_error_from_errno("getcwd"); | |||
5779 | goto done; | |||
5780 | } | |||
5781 | ||||
5782 | if (repo_path == NULL((void *)0)) { | |||
5783 | error = got_worktree_open(&worktree, cwd); | |||
5784 | if (error && error->code != GOT_ERR_NOT_WORKTREE60) | |||
5785 | goto done; | |||
5786 | else | |||
5787 | error = NULL((void *)0); | |||
5788 | if (worktree) { | |||
5789 | repo_path = | |||
5790 | strdup(got_worktree_get_repo_path(worktree)); | |||
5791 | if (repo_path == NULL((void *)0)) | |||
5792 | error = got_error_from_errno("strdup"); | |||
5793 | if (error) | |||
5794 | goto done; | |||
5795 | } else { | |||
5796 | repo_path = strdup(cwd); | |||
5797 | if (repo_path == NULL((void *)0)) { | |||
5798 | error = got_error_from_errno("strdup"); | |||
5799 | goto done; | |||
5800 | } | |||
5801 | } | |||
5802 | } | |||
5803 | ||||
5804 | error = got_repo_open(&repo, repo_path, NULL((void *)0)); | |||
5805 | if (error != NULL((void *)0)) | |||
5806 | goto done; | |||
5807 | ||||
5808 | error = apply_unveil(got_repo_get_path(repo), do_list, | |||
5809 | worktree ? got_worktree_get_root_path(worktree) : NULL((void *)0)); | |||
5810 | if (error) | |||
5811 | goto done; | |||
5812 | ||||
5813 | if (do_show) | |||
5814 | error = show_current_branch(repo, worktree); | |||
5815 | else if (do_list) | |||
5816 | error = list_branches(repo, worktree); | |||
5817 | else if (delref) | |||
5818 | error = delete_branch(repo, worktree, delref); | |||
5819 | else { | |||
5820 | struct got_reflist_head refs; | |||
5821 | TAILQ_INIT(&refs)do { (&refs)->tqh_first = ((void *)0); (&refs)-> tqh_last = &(&refs)->tqh_first; } while (0); | |||
5822 | error = got_ref_list(&refs, repo, NULL((void *)0), got_ref_cmp_by_name, | |||
5823 | NULL((void *)0)); | |||
5824 | if (error) | |||
5825 | goto done; | |||
5826 | if (commit_id_arg == NULL((void *)0)) | |||
5827 | commit_id_arg = worktree ? | |||
5828 | got_worktree_get_head_ref_name(worktree) : | |||
5829 | GOT_REF_HEAD"HEAD"; | |||
5830 | error = got_repo_match_object_id(&commit_id, NULL((void *)0), | |||
5831 | commit_id_arg, GOT_OBJ_TYPE_COMMIT1, &refs, repo); | |||
5832 | got_ref_list_free(&refs); | |||
5833 | if (error) | |||
5834 | goto done; | |||
5835 | error = add_branch(repo, argv[0], commit_id); | |||
5836 | if (error) | |||
5837 | goto done; | |||
5838 | if (worktree && do_update) { | |||
5839 | struct got_update_progress_arg upa; | |||
5840 | char *branch_refname = NULL((void *)0); | |||
5841 | ||||
5842 | error = got_object_id_str(&commit_id_str, commit_id); | |||
5843 | if (error) | |||
5844 | goto done; | |||
5845 | error = get_worktree_paths_from_argv(&paths, 0, NULL((void *)0), | |||
5846 | worktree); | |||
5847 | if (error) | |||
5848 | goto done; | |||
5849 | if (asprintf(&branch_refname, "refs/heads/%s", argv[0]) | |||
5850 | == -1) { | |||
5851 | error = got_error_from_errno("asprintf"); | |||
5852 | goto done; | |||
5853 | } | |||
5854 | error = got_ref_open(&ref, repo, branch_refname, 0); | |||
5855 | free(branch_refname); | |||
5856 | if (error) | |||
5857 | goto done; | |||
5858 | error = switch_head_ref(ref, commit_id, worktree, | |||
5859 | repo); | |||
5860 | if (error) | |||
5861 | goto done; | |||
5862 | error = got_worktree_set_base_commit_id(worktree, repo, | |||
5863 | commit_id); | |||
5864 | if (error) | |||
5865 | goto done; | |||
5866 | memset(&upa, 0, sizeof(upa)); | |||
5867 | error = got_worktree_checkout_files(worktree, &paths, | |||
5868 | repo, update_progress, &upa, check_cancelled, | |||
5869 | NULL((void *)0)); | |||
5870 | if (error) | |||
5871 | goto done; | |||
5872 | if (upa.did_something) | |||
5873 | printf("Updated to commit %s\n", commit_id_str); | |||
5874 | print_update_progress_stats(&upa); | |||
5875 | } | |||
5876 | } | |||
5877 | done: | |||
5878 | if (ref) | |||
5879 | got_ref_close(ref); | |||
5880 | if (repo) | |||
5881 | got_repo_close(repo); | |||
5882 | if (worktree) | |||
5883 | got_worktree_close(worktree); | |||
5884 | free(cwd); | |||
5885 | free(repo_path); | |||
5886 | free(commit_id); | |||
5887 | free(commit_id_str); | |||
5888 | TAILQ_FOREACH(pe, &paths, entry)for((pe) = ((&paths)->tqh_first); (pe) != ((void *)0); (pe) = ((pe)->entry.tqe_next)) | |||
5889 | free((char *)pe->path); | |||
5890 | got_pathlist_free(&paths); | |||
5891 | return error; | |||
5892 | } | |||
5893 | ||||
5894 | ||||
5895 | __dead__attribute__((__noreturn__)) static void | |||
5896 | usage_tag(void) | |||
5897 | { | |||
5898 | fprintf(stderr(&__sF[2]), | |||
5899 | "usage: %s tag [-c commit] [-r repository] [-l] " | |||
5900 | "[-m message] name\n", getprogname()); | |||
5901 | exit(1); | |||
5902 | } | |||
5903 | ||||
5904 | #if 0 | |||
5905 | static const struct got_error * | |||
5906 | sort_tags(struct got_reflist_head *sorted, struct got_reflist_head *tags) | |||
5907 | { | |||
5908 | const struct got_error *err = NULL((void *)0); | |||
5909 | struct got_reflist_entry *re, *se, *new; | |||
5910 | struct got_object_id *re_id, *se_id; | |||
5911 | struct got_tag_object *re_tag, *se_tag; | |||
5912 | time_t re_time, se_time; | |||
5913 | ||||
5914 | SIMPLEQ_FOREACH(re, tags, entry)for((re) = ((tags)->sqh_first); (re) != ((void *)0); (re) = ((re)->entry.sqe_next)) { | |||
5915 | se = SIMPLEQ_FIRST(sorted)((sorted)->sqh_first); | |||
5916 | if (se == NULL((void *)0)) { | |||
5917 | err = got_reflist_entry_dup(&new, re); | |||
5918 | if (err) | |||
5919 | return err; | |||
5920 | SIMPLEQ_INSERT_HEAD(sorted, new, entry)do { if (((new)->entry.sqe_next = (sorted)->sqh_first) == ((void *)0)) (sorted)->sqh_last = &(new)->entry.sqe_next ; (sorted)->sqh_first = (new); } while (0); | |||
5921 | continue; | |||
5922 | } else { | |||
5923 | err = got_ref_resolve(&re_id, repo, re->ref); | |||
5924 | if (err) | |||
5925 | break; | |||
5926 | err = got_object_open_as_tag(&re_tag, repo, re_id); | |||
5927 | free(re_id); | |||
5928 | if (err) | |||
5929 | break; | |||
5930 | re_time = got_object_tag_get_tagger_time(re_tag); | |||
5931 | got_object_tag_close(re_tag); | |||
5932 | } | |||
5933 | ||||
5934 | while (se) { | |||
5935 | err = got_ref_resolve(&se_id, repo, re->ref); | |||
5936 | if (err) | |||
5937 | break; | |||
5938 | err = got_object_open_as_tag(&se_tag, repo, se_id); | |||
5939 | free(se_id); | |||
5940 | if (err) | |||
5941 | break; | |||
5942 | se_time = got_object_tag_get_tagger_time(se_tag); | |||
5943 | got_object_tag_close(se_tag); | |||
5944 | ||||
5945 | if (se_time > re_time) { | |||
5946 | err = got_reflist_entry_dup(&new, re); | |||
5947 | if (err) | |||
5948 | return err; | |||
5949 | SIMPLEQ_INSERT_AFTER(sorted, se, new, entry)do { if (((new)->entry.sqe_next = (se)->entry.sqe_next) == ((void *)0)) (sorted)->sqh_last = &(new)->entry .sqe_next; (se)->entry.sqe_next = (new); } while (0); | |||
5950 | break; | |||
5951 | } | |||
5952 | se = SIMPLEQ_NEXT(se, entry)((se)->entry.sqe_next); | |||
5953 | continue; | |||
5954 | } | |||
5955 | } | |||
5956 | done: | |||
5957 | return err; | |||
5958 | } | |||
5959 | #endif | |||
5960 | ||||
5961 | static const struct got_error * | |||
5962 | list_tags(struct got_repository *repo, struct got_worktree *worktree) | |||
5963 | { | |||
5964 | static const struct got_error *err = NULL((void *)0); | |||
5965 | struct got_reflist_head refs; | |||
5966 | struct got_reflist_entry *re; | |||
5967 | ||||
5968 | TAILQ_INIT(&refs)do { (&refs)->tqh_first = ((void *)0); (&refs)-> tqh_last = &(&refs)->tqh_first; } while (0); | |||
5969 | ||||
5970 | err = got_ref_list(&refs, repo, "refs/tags", got_ref_cmp_tags, repo); | |||
5971 | if (err) | |||
5972 | return err; | |||
5973 | ||||
5974 | TAILQ_FOREACH(re, &refs, entry)for((re) = ((&refs)->tqh_first); (re) != ((void *)0); ( re) = ((re)->entry.tqe_next)) { | |||
5975 | const char *refname; | |||
5976 | char *refstr, *tagmsg0, *tagmsg, *line, *id_str, *datestr; | |||
5977 | char datebuf[26]; | |||
5978 | const char *tagger; | |||
5979 | time_t tagger_time; | |||
5980 | struct got_object_id *id; | |||
5981 | struct got_tag_object *tag; | |||
5982 | struct got_commit_object *commit = NULL((void *)0); | |||
5983 | ||||
5984 | refname = got_ref_get_name(re->ref); | |||
5985 | if (strncmp(refname, "refs/tags/", 10) != 0) | |||
5986 | continue; | |||
5987 | refname += 10; | |||
5988 | refstr = got_ref_to_str(re->ref); | |||
5989 | if (refstr == NULL((void *)0)) { | |||
5990 | err = got_error_from_errno("got_ref_to_str"); | |||
5991 | break; | |||
5992 | } | |||
5993 | printf("%stag %s %s\n", GOT_COMMIT_SEP_STR"-----------------------------------------------\n", refname, refstr); | |||
5994 | free(refstr); | |||
5995 | ||||
5996 | err = got_ref_resolve(&id, repo, re->ref); | |||
5997 | if (err) | |||
5998 | break; | |||
5999 | err = got_object_open_as_tag(&tag, repo, id); | |||
6000 | if (err) { | |||
6001 | if (err->code != GOT_ERR_OBJ_TYPE11) { | |||
6002 | free(id); | |||
6003 | break; | |||
6004 | } | |||
6005 | /* "lightweight" tag */ | |||
6006 | err = got_object_open_as_commit(&commit, repo, id); | |||
6007 | if (err) { | |||
6008 | free(id); | |||
6009 | break; | |||
6010 | } | |||
6011 | tagger = got_object_commit_get_committer(commit); | |||
6012 | tagger_time = | |||
6013 | got_object_commit_get_committer_time(commit); | |||
6014 | err = got_object_id_str(&id_str, id); | |||
6015 | free(id); | |||
6016 | if (err) | |||
6017 | break; | |||
6018 | } else { | |||
6019 | free(id); | |||
6020 | tagger = got_object_tag_get_tagger(tag); | |||
6021 | tagger_time = got_object_tag_get_tagger_time(tag); | |||
6022 | err = got_object_id_str(&id_str, | |||
6023 | got_object_tag_get_object_id(tag)); | |||
6024 | if (err) | |||
6025 | break; | |||
6026 | } | |||
6027 | printf("from: %s\n", tagger); | |||
6028 | datestr = get_datestr(&tagger_time, datebuf); | |||
6029 | if (datestr) | |||
6030 | printf("date: %s UTC\n", datestr); | |||
6031 | if (commit) | |||
6032 | printf("object: %s %s\n", GOT_OBJ_LABEL_COMMIT"commit", id_str); | |||
6033 | else { | |||
6034 | switch (got_object_tag_get_object_type(tag)) { | |||
6035 | case GOT_OBJ_TYPE_BLOB3: | |||
6036 | printf("object: %s %s\n", GOT_OBJ_LABEL_BLOB"blob", | |||
6037 | id_str); | |||
6038 | break; | |||
6039 | case GOT_OBJ_TYPE_TREE2: | |||
6040 | printf("object: %s %s\n", GOT_OBJ_LABEL_TREE"tree", | |||
6041 | id_str); | |||
6042 | break; | |||
6043 | case GOT_OBJ_TYPE_COMMIT1: | |||
6044 | printf("object: %s %s\n", GOT_OBJ_LABEL_COMMIT"commit", | |||
6045 | id_str); | |||
6046 | break; | |||
6047 | case GOT_OBJ_TYPE_TAG4: | |||
6048 | printf("object: %s %s\n", GOT_OBJ_LABEL_TAG"tag", | |||
6049 | id_str); | |||
6050 | break; | |||
6051 | default: | |||
6052 | break; | |||
6053 | } | |||
6054 | } | |||
6055 | free(id_str); | |||
6056 | if (commit) { | |||
6057 | err = got_object_commit_get_logmsg(&tagmsg0, commit); | |||
6058 | if (err) | |||
6059 | break; | |||
6060 | got_object_commit_close(commit); | |||
6061 | } else { | |||
6062 | tagmsg0 = strdup(got_object_tag_get_message(tag)); | |||
6063 | got_object_tag_close(tag); | |||
6064 | if (tagmsg0 == NULL((void *)0)) { | |||
6065 | err = got_error_from_errno("strdup"); | |||
6066 | break; | |||
6067 | } | |||
6068 | } | |||
6069 | ||||
6070 | tagmsg = tagmsg0; | |||
6071 | do { | |||
6072 | line = strsep(&tagmsg, "\n"); | |||
6073 | if (line) | |||
6074 | printf(" %s\n", line); | |||
6075 | } while (line); | |||
6076 | free(tagmsg0); | |||
6077 | } | |||
6078 | ||||
6079 | got_ref_list_free(&refs); | |||
6080 | return NULL((void *)0); | |||
6081 | } | |||
6082 | ||||
6083 | static const struct got_error * | |||
6084 | get_tag_message(char **tagmsg, char **tagmsg_path, const char *commit_id_str, | |||
6085 | const char *tag_name, const char *repo_path) | |||
6086 | { | |||
6087 | const struct got_error *err = NULL((void *)0); | |||
6088 | char *template = NULL((void *)0), *initial_content = NULL((void *)0); | |||
6089 | char *editor = NULL((void *)0); | |||
6090 | int initial_content_len; | |||
6091 | int fd = -1; | |||
6092 | ||||
6093 | if (asprintf(&template, GOT_TMPDIR_STR"/tmp" "/got-tagmsg") == -1) { | |||
6094 | err = got_error_from_errno("asprintf"); | |||
6095 | goto done; | |||
6096 | } | |||
6097 | ||||
6098 | initial_content_len = asprintf(&initial_content, | |||
6099 | "\n# tagging commit %s as %s\n", | |||
6100 | commit_id_str, tag_name); | |||
6101 | if (initial_content_len == -1) { | |||
6102 | err = got_error_from_errno("asprintf"); | |||
6103 | goto done; | |||
6104 | } | |||
6105 | ||||
6106 | err = got_opentemp_named_fd(tagmsg_path, &fd, template); | |||
6107 | if (err) | |||
6108 | goto done; | |||
6109 | ||||
6110 | if (write(fd, initial_content, initial_content_len) == -1) { | |||
6111 | err = got_error_from_errno2("write", *tagmsg_path); | |||
6112 | goto done; | |||
6113 | } | |||
6114 | ||||
6115 | err = get_editor(&editor); | |||
6116 | if (err) | |||
6117 | goto done; | |||
6118 | err = edit_logmsg(tagmsg, editor, *tagmsg_path, initial_content, | |||
6119 | initial_content_len, 1); | |||
6120 | done: | |||
6121 | free(initial_content); | |||
6122 | free(template); | |||
6123 | free(editor); | |||
6124 | ||||
6125 | if (fd != -1 && close(fd) == -1 && err == NULL((void *)0)) | |||
6126 | err = got_error_from_errno2("close", *tagmsg_path); | |||
6127 | ||||
6128 | /* Editor is done; we can now apply unveil(2) */ | |||
6129 | if (err == NULL((void *)0)) | |||
6130 | err = apply_unveil(repo_path, 0, NULL((void *)0)); | |||
6131 | if (err) { | |||
6132 | free(*tagmsg); | |||
6133 | *tagmsg = NULL((void *)0); | |||
6134 | } | |||
6135 | return err; | |||
6136 | } | |||
6137 | ||||
6138 | static const struct got_error * | |||
6139 | add_tag(struct got_repository *repo, struct got_worktree *worktree, | |||
6140 | const char *tag_name, const char *commit_arg, const char *tagmsg_arg) | |||
6141 | { | |||
6142 | const struct got_error *err = NULL((void *)0); | |||
6143 | struct got_object_id *commit_id = NULL((void *)0), *tag_id = NULL((void *)0); | |||
6144 | char *label = NULL((void *)0), *commit_id_str = NULL((void *)0); | |||
6145 | struct got_reference *ref = NULL((void *)0); | |||
6146 | char *refname = NULL((void *)0), *tagmsg = NULL((void *)0), *tagger = NULL((void *)0); | |||
6147 | char *tagmsg_path = NULL((void *)0), *tag_id_str = NULL((void *)0); | |||
6148 | int preserve_tagmsg = 0; | |||
6149 | struct got_reflist_head refs; | |||
6150 | ||||
6151 | TAILQ_INIT(&refs)do { (&refs)->tqh_first = ((void *)0); (&refs)-> tqh_last = &(&refs)->tqh_first; } while (0); | |||
6152 | ||||
6153 | /* | |||
6154 | * Don't let the user create a tag name with a leading '-'. | |||
6155 | * While technically a valid reference name, this case is usually | |||
6156 | * an unintended typo. | |||
6157 | */ | |||
6158 | if (tag_name[0] == '-') | |||
6159 | return got_error_path(tag_name, GOT_ERR_REF_NAME_MINUS113); | |||
6160 | ||||
6161 | err = get_author(&tagger, repo, worktree); | |||
6162 | if (err |