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) | |||
6163 | return err; | |||
6164 | ||||
6165 | err = got_ref_list(&refs, repo, NULL((void *)0), got_ref_cmp_by_name, NULL((void *)0)); | |||
6166 | if (err) | |||
6167 | goto done; | |||
6168 | ||||
6169 | err = got_repo_match_object_id(&commit_id, &label, commit_arg, | |||
6170 | GOT_OBJ_TYPE_COMMIT1, &refs, repo); | |||
6171 | if (err) | |||
6172 | goto done; | |||
6173 | ||||
6174 | err = got_object_id_str(&commit_id_str, commit_id); | |||
6175 | if (err) | |||
6176 | goto done; | |||
6177 | ||||
6178 | if (strncmp("refs/tags/", tag_name, 10) == 0) { | |||
6179 | refname = strdup(tag_name); | |||
6180 | if (refname == NULL((void *)0)) { | |||
6181 | err = got_error_from_errno("strdup"); | |||
6182 | goto done; | |||
6183 | } | |||
6184 | tag_name += 10; | |||
6185 | } else if (asprintf(&refname, "refs/tags/%s", tag_name) == -1) { | |||
6186 | err = got_error_from_errno("asprintf"); | |||
6187 | goto done; | |||
6188 | } | |||
6189 | ||||
6190 | err = got_ref_open(&ref, repo, refname, 0); | |||
6191 | if (err == NULL((void *)0)) { | |||
6192 | err = got_error(GOT_ERR_TAG_EXISTS109); | |||
6193 | goto done; | |||
6194 | } else if (err->code != GOT_ERR_NOT_REF5) | |||
6195 | goto done; | |||
6196 | ||||
6197 | if (tagmsg_arg == NULL((void *)0)) { | |||
6198 | err = get_tag_message(&tagmsg, &tagmsg_path, commit_id_str, | |||
6199 | tag_name, got_repo_get_path(repo)); | |||
6200 | if (err) { | |||
6201 | if (err->code != GOT_ERR_COMMIT_MSG_EMPTY74 && | |||
6202 | tagmsg_path != NULL((void *)0)) | |||
6203 | preserve_tagmsg = 1; | |||
6204 | goto done; | |||
6205 | } | |||
6206 | } | |||
6207 | ||||
6208 | err = got_object_tag_create(&tag_id, tag_name, commit_id, | |||
6209 | tagger, time(NULL((void *)0)), tagmsg ? tagmsg : tagmsg_arg, repo); | |||
6210 | if (err) { | |||
6211 | if (tagmsg_path) | |||
6212 | preserve_tagmsg = 1; | |||
6213 | goto done; | |||
6214 | } | |||
6215 | ||||
6216 | err = got_ref_alloc(&ref, refname, tag_id); | |||
6217 | if (err) { | |||
6218 | if (tagmsg_path) | |||
6219 | preserve_tagmsg = 1; | |||
6220 | goto done; | |||
6221 | } | |||
6222 | ||||
6223 | err = got_ref_write(ref, repo); | |||
6224 | if (err) { | |||
6225 | if (tagmsg_path) | |||
6226 | preserve_tagmsg = 1; | |||
6227 | goto done; | |||
6228 | } | |||
6229 | ||||
6230 | err = got_object_id_str(&tag_id_str, tag_id); | |||
6231 | if (err) { | |||
6232 | if (tagmsg_path) | |||
6233 | preserve_tagmsg = 1; | |||
6234 | goto done; | |||
6235 | } | |||
6236 | printf("Created tag %s\n", tag_id_str); | |||
6237 | done: | |||
6238 | if (preserve_tagmsg) { | |||
6239 | fprintf(stderr(&__sF[2]), "%s: tag message preserved in %s\n", | |||
6240 | getprogname(), tagmsg_path); | |||
6241 | } else if (tagmsg_path && unlink(tagmsg_path) == -1 && err == NULL((void *)0)) | |||
6242 | err = got_error_from_errno2("unlink", tagmsg_path); | |||
6243 | free(tag_id_str); | |||
6244 | if (ref) | |||
6245 | got_ref_close(ref); | |||
6246 | free(commit_id); | |||
6247 | free(commit_id_str); | |||
6248 | free(refname); | |||
6249 | free(tagmsg); | |||
6250 | free(tagmsg_path); | |||
6251 | free(tagger); | |||
6252 | got_ref_list_free(&refs); | |||
6253 | return err; | |||
6254 | } | |||
6255 | ||||
6256 | static const struct got_error * | |||
6257 | cmd_tag(int argc, char *argv[]) | |||
6258 | { | |||
6259 | const struct got_error *error = NULL((void *)0); | |||
6260 | struct got_repository *repo = NULL((void *)0); | |||
6261 | struct got_worktree *worktree = NULL((void *)0); | |||
6262 | char *cwd = NULL((void *)0), *repo_path = NULL((void *)0), *commit_id_str = NULL((void *)0); | |||
6263 | char *gitconfig_path = NULL((void *)0); | |||
6264 | const char *tag_name, *commit_id_arg = NULL((void *)0), *tagmsg = NULL((void *)0); | |||
6265 | int ch, do_list = 0; | |||
6266 | ||||
6267 | while ((ch = getopt(argc, argv, "c:m:r:l")) != -1) { | |||
6268 | switch (ch) { | |||
6269 | case 'c': | |||
6270 | commit_id_arg = optarg; | |||
6271 | break; | |||
6272 | case 'm': | |||
6273 | tagmsg = optarg; | |||
6274 | break; | |||
6275 | case 'r': | |||
6276 | repo_path = realpath(optarg, NULL((void *)0)); | |||
6277 | if (repo_path == NULL((void *)0)) | |||
6278 | return got_error_from_errno2("realpath", | |||
6279 | optarg); | |||
6280 | got_path_strip_trailing_slashes(repo_path); | |||
6281 | break; | |||
6282 | case 'l': | |||
6283 | do_list = 1; | |||
6284 | break; | |||
6285 | default: | |||
6286 | usage_tag(); | |||
6287 | /* NOTREACHED */ | |||
6288 | } | |||
6289 | } | |||
6290 | ||||
6291 | argc -= optind; | |||
6292 | argv += optind; | |||
6293 | ||||
6294 | if (do_list) { | |||
6295 | if (commit_id_arg != NULL((void *)0)) | |||
6296 | errx(1, | |||
6297 | "-c option can only be used when creating a tag"); | |||
6298 | if (tagmsg) | |||
6299 | option_conflict('l', 'm'); | |||
6300 | if (argc > 0) | |||
6301 | usage_tag(); | |||
6302 | } else if (argc != 1) | |||
6303 | usage_tag(); | |||
6304 | ||||
6305 | tag_name = argv[0]; | |||
6306 | ||||
6307 | #ifndef PROFILE | |||
6308 | if (do_list) { | |||
6309 | if (pledge("stdio rpath wpath flock proc exec sendfd unveil", | |||
6310 | NULL((void *)0)) == -1) | |||
6311 | err(1, "pledge"); | |||
6312 | } else { | |||
6313 | if (pledge("stdio rpath wpath cpath fattr flock proc exec " | |||
6314 | "sendfd unveil", NULL((void *)0)) == -1) | |||
6315 | err(1, "pledge"); | |||
6316 | } | |||
6317 | #endif | |||
6318 | cwd = getcwd(NULL((void *)0), 0); | |||
6319 | if (cwd == NULL((void *)0)) { | |||
6320 | error = got_error_from_errno("getcwd"); | |||
6321 | goto done; | |||
6322 | } | |||
6323 | ||||
6324 | if (repo_path == NULL((void *)0)) { | |||
6325 | error = got_worktree_open(&worktree, cwd); | |||
6326 | if (error && error->code != GOT_ERR_NOT_WORKTREE60) | |||
6327 | goto done; | |||
6328 | else | |||
6329 | error = NULL((void *)0); | |||
6330 | if (worktree) { | |||
6331 | repo_path = | |||
6332 | strdup(got_worktree_get_repo_path(worktree)); | |||
6333 | if (repo_path == NULL((void *)0)) | |||
6334 | error = got_error_from_errno("strdup"); | |||
6335 | if (error) | |||
6336 | goto done; | |||
6337 | } else { | |||
6338 | repo_path = strdup(cwd); | |||
6339 | if (repo_path == NULL((void *)0)) { | |||
6340 | error = got_error_from_errno("strdup"); | |||
6341 | goto done; | |||
6342 | } | |||
6343 | } | |||
6344 | } | |||
6345 | ||||
6346 | if (do_list) { | |||
6347 | error = got_repo_open(&repo, repo_path, NULL((void *)0)); | |||
6348 | if (error != NULL((void *)0)) | |||
6349 | goto done; | |||
6350 | error = apply_unveil(got_repo_get_path(repo), 1, NULL((void *)0)); | |||
6351 | if (error) | |||
6352 | goto done; | |||
6353 | error = list_tags(repo, worktree); | |||
6354 | } else { | |||
6355 | error = get_gitconfig_path(&gitconfig_path); | |||
6356 | if (error) | |||
6357 | goto done; | |||
6358 | error = got_repo_open(&repo, repo_path, gitconfig_path); | |||
6359 | if (error != NULL((void *)0)) | |||
6360 | goto done; | |||
6361 | ||||
6362 | if (tagmsg) { | |||
6363 | error = apply_unveil(got_repo_get_path(repo), 0, NULL((void *)0)); | |||
6364 | if (error) | |||
6365 | goto done; | |||
6366 | } | |||
6367 | ||||
6368 | if (commit_id_arg == NULL((void *)0)) { | |||
6369 | struct got_reference *head_ref; | |||
6370 | struct got_object_id *commit_id; | |||
6371 | error = got_ref_open(&head_ref, repo, | |||
6372 | worktree ? got_worktree_get_head_ref_name(worktree) | |||
6373 | : GOT_REF_HEAD"HEAD", 0); | |||
6374 | if (error) | |||
6375 | goto done; | |||
6376 | error = got_ref_resolve(&commit_id, repo, head_ref); | |||
6377 | got_ref_close(head_ref); | |||
6378 | if (error) | |||
6379 | goto done; | |||
6380 | error = got_object_id_str(&commit_id_str, commit_id); | |||
6381 | free(commit_id); | |||
6382 | if (error) | |||
6383 | goto done; | |||
6384 | } | |||
6385 | ||||
6386 | error = add_tag(repo, worktree, tag_name, | |||
6387 | commit_id_str ? commit_id_str : commit_id_arg, tagmsg); | |||
6388 | } | |||
6389 | done: | |||
6390 | if (repo) | |||
6391 | got_repo_close(repo); | |||
6392 | if (worktree) | |||
6393 | got_worktree_close(worktree); | |||
6394 | free(cwd); | |||
6395 | free(repo_path); | |||
6396 | free(gitconfig_path); | |||
6397 | free(commit_id_str); | |||
6398 | return error; | |||
6399 | } | |||
6400 | ||||
6401 | __dead__attribute__((__noreturn__)) static void | |||
6402 | usage_add(void) | |||
6403 | { | |||
6404 | fprintf(stderr(&__sF[2]), "usage: %s add [-R] [-I] path ...\n", | |||
6405 | getprogname()); | |||
6406 | exit(1); | |||
6407 | } | |||
6408 | ||||
6409 | static const struct got_error * | |||
6410 | add_progress(void *arg, unsigned char status, const char *path) | |||
6411 | { | |||
6412 | while (path[0] == '/') | |||
6413 | path++; | |||
6414 | printf("%c %s\n", status, path); | |||
6415 | return NULL((void *)0); | |||
6416 | } | |||
6417 | ||||
6418 | static const struct got_error * | |||
6419 | cmd_add(int argc, char *argv[]) | |||
6420 | { | |||
6421 | const struct got_error *error = NULL((void *)0); | |||
6422 | struct got_repository *repo = NULL((void *)0); | |||
6423 | struct got_worktree *worktree = NULL((void *)0); | |||
6424 | char *cwd = NULL((void *)0); | |||
6425 | struct got_pathlist_head paths; | |||
6426 | struct got_pathlist_entry *pe; | |||
6427 | int ch, can_recurse = 0, no_ignores = 0; | |||
6428 | ||||
6429 | TAILQ_INIT(&paths)do { (&paths)->tqh_first = ((void *)0); (&paths)-> tqh_last = &(&paths)->tqh_first; } while (0); | |||
6430 | ||||
6431 | while ((ch = getopt(argc, argv, "IR")) != -1) { | |||
6432 | switch (ch) { | |||
6433 | case 'I': | |||
6434 | no_ignores = 1; | |||
6435 | break; | |||
6436 | case 'R': | |||
6437 | can_recurse = 1; | |||
6438 | break; | |||
6439 | default: | |||
6440 | usage_add(); | |||
6441 | /* NOTREACHED */ | |||
6442 | } | |||
6443 | } | |||
6444 | ||||
6445 | argc -= optind; | |||
6446 | argv += optind; | |||
6447 | ||||
6448 | #ifndef PROFILE | |||
6449 | if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil", | |||
6450 | NULL((void *)0)) == -1) | |||
6451 | err(1, "pledge"); | |||
6452 | #endif | |||
6453 | if (argc < 1) | |||
6454 | usage_add(); | |||
6455 | ||||
6456 | cwd = getcwd(NULL((void *)0), 0); | |||
6457 | if (cwd == NULL((void *)0)) { | |||
6458 | error = got_error_from_errno("getcwd"); | |||
6459 | goto done; | |||
6460 | } | |||
6461 | ||||
6462 | error = got_worktree_open(&worktree, cwd); | |||
6463 | if (error) { | |||
6464 | if (error->code == GOT_ERR_NOT_WORKTREE60) | |||
6465 | error = wrap_not_worktree_error(error, "add", cwd); | |||
6466 | goto done; | |||
6467 | } | |||
6468 | ||||
6469 | error = got_repo_open(&repo, got_worktree_get_repo_path(worktree), | |||
6470 | NULL((void *)0)); | |||
6471 | if (error != NULL((void *)0)) | |||
6472 | goto done; | |||
6473 | ||||
6474 | error = apply_unveil(got_repo_get_path(repo), 1, | |||
6475 | got_worktree_get_root_path(worktree)); | |||
6476 | if (error) | |||
6477 | goto done; | |||
6478 | ||||
6479 | error = get_worktree_paths_from_argv(&paths, argc, argv, worktree); | |||
6480 | if (error) | |||
6481 | goto done; | |||
6482 | ||||
6483 | if (!can_recurse && no_ignores) { | |||
6484 | error = got_error_msg(GOT_ERR_BAD_PATH4, | |||
6485 | "disregarding ignores requires -R option"); | |||
6486 | goto done; | |||
6487 | ||||
6488 | } | |||
6489 | ||||
6490 | if (!can_recurse) { | |||
6491 | char *ondisk_path; | |||
6492 | struct stat sb; | |||
6493 | TAILQ_FOREACH(pe, &paths, entry)for((pe) = ((&paths)->tqh_first); (pe) != ((void *)0); (pe) = ((pe)->entry.tqe_next)) { | |||
6494 | if (asprintf(&ondisk_path, "%s/%s", | |||
6495 | got_worktree_get_root_path(worktree), | |||
6496 | pe->path) == -1) { | |||
6497 | error = got_error_from_errno("asprintf"); | |||
6498 | goto done; | |||
6499 | } | |||
6500 | if (lstat(ondisk_path, &sb) == -1) { | |||
6501 | if (errno(*__errno()) == ENOENT2) { | |||
6502 | free(ondisk_path); | |||
6503 | continue; | |||
6504 | } | |||
6505 | error = got_error_from_errno2("lstat", | |||
6506 | ondisk_path); | |||
6507 | free(ondisk_path); | |||
6508 | goto done; | |||
6509 | } | |||
6510 | free(ondisk_path); | |||
6511 | if (S_ISDIR(sb.st_mode)((sb.st_mode & 0170000) == 0040000)) { | |||
6512 | error = got_error_msg(GOT_ERR_BAD_PATH4, | |||
6513 | "adding directories requires -R option"); | |||
6514 | goto done; | |||
6515 | } | |||
6516 | } | |||
6517 | } | |||
6518 | ||||
6519 | error = got_worktree_schedule_add(worktree, &paths, add_progress, | |||
6520 | NULL((void *)0), repo, no_ignores); | |||
6521 | done: | |||
6522 | if (repo) | |||
6523 | got_repo_close(repo); | |||
6524 | if (worktree) | |||
6525 | got_worktree_close(worktree); | |||
6526 | TAILQ_FOREACH(pe, &paths, entry)for((pe) = ((&paths)->tqh_first); (pe) != ((void *)0); (pe) = ((pe)->entry.tqe_next)) | |||
6527 | free((char *)pe->path); | |||
6528 | got_pathlist_free(&paths); | |||
6529 | free(cwd); | |||
6530 | return error; | |||
6531 | } | |||
6532 | ||||
6533 | __dead__attribute__((__noreturn__)) static void | |||
6534 | usage_remove(void) | |||
6535 | { | |||
6536 | fprintf(stderr(&__sF[2]), "usage: %s remove [-f] [-k] [-R] [-s status-codes] " | |||
6537 | "path ...\n", getprogname()); | |||
6538 | exit(1); | |||
6539 | } | |||
6540 | ||||
6541 | static const struct got_error * | |||
6542 | print_remove_status(void *arg, unsigned char status, | |||
6543 | unsigned char staged_status, const char *path) | |||
6544 | { | |||
6545 | while (path[0] == '/') | |||
6546 | path++; | |||
6547 | if (status == GOT_STATUS_NONEXISTENT'N') | |||
6548 | return NULL((void *)0); | |||
6549 | if (status == staged_status && (status == GOT_STATUS_DELETE'D')) | |||
6550 | status = GOT_STATUS_NO_CHANGE' '; | |||
6551 | printf("%c%c %s\n", status, staged_status, path); | |||
6552 | return NULL((void *)0); | |||
6553 | } | |||
6554 | ||||
6555 | static const struct got_error * | |||
6556 | cmd_remove(int argc, char *argv[]) | |||
6557 | { | |||
6558 | const struct got_error *error = NULL((void *)0); | |||
6559 | struct got_worktree *worktree = NULL((void *)0); | |||
6560 | struct got_repository *repo = NULL((void *)0); | |||
6561 | const char *status_codes = NULL((void *)0); | |||
6562 | char *cwd = NULL((void *)0); | |||
6563 | struct got_pathlist_head paths; | |||
6564 | struct got_pathlist_entry *pe; | |||
6565 | int ch, delete_local_mods = 0, can_recurse = 0, keep_on_disk = 0, i; | |||
6566 | ||||
6567 | TAILQ_INIT(&paths)do { (&paths)->tqh_first = ((void *)0); (&paths)-> tqh_last = &(&paths)->tqh_first; } while (0); | |||
6568 | ||||
6569 | while ((ch = getopt(argc, argv, "fkRs:")) != -1) { | |||
6570 | switch (ch) { | |||
6571 | case 'f': | |||
6572 | delete_local_mods = 1; | |||
6573 | break; | |||
6574 | case 'k': | |||
6575 | keep_on_disk = 1; | |||
6576 | break; | |||
6577 | case 'R': | |||
6578 | can_recurse = 1; | |||
6579 | break; | |||
6580 | case 's': | |||
6581 | for (i = 0; i < strlen(optarg); i++) { | |||
6582 | switch (optarg[i]) { | |||
6583 | case GOT_STATUS_MODIFY'M': | |||
6584 | delete_local_mods = 1; | |||
6585 | break; | |||
6586 | case GOT_STATUS_MISSING'!': | |||
6587 | break; | |||
6588 | default: | |||
6589 | errx(1, "invalid status code '%c'", | |||
6590 | optarg[i]); | |||
6591 | } | |||
6592 | } | |||
6593 | status_codes = optarg; | |||
6594 | break; | |||
6595 | default: | |||
6596 | usage_remove(); | |||
6597 | /* NOTREACHED */ | |||
6598 | } | |||
6599 | } | |||
6600 | ||||
6601 | argc -= optind; | |||
6602 | argv += optind; | |||
6603 | ||||
6604 | #ifndef PROFILE | |||
6605 | if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil", | |||
6606 | NULL((void *)0)) == -1) | |||
6607 | err(1, "pledge"); | |||
6608 | #endif | |||
6609 | if (argc < 1) | |||
6610 | usage_remove(); | |||
6611 | ||||
6612 | cwd = getcwd(NULL((void *)0), 0); | |||
6613 | if (cwd == NULL((void *)0)) { | |||
6614 | error = got_error_from_errno("getcwd"); | |||
6615 | goto done; | |||
6616 | } | |||
6617 | error = got_worktree_open(&worktree, cwd); | |||
6618 | if (error) { | |||
6619 | if (error->code == GOT_ERR_NOT_WORKTREE60) | |||
6620 | error = wrap_not_worktree_error(error, "remove", cwd); | |||
6621 | goto done; | |||
6622 | } | |||
6623 | ||||
6624 | error = got_repo_open(&repo, got_worktree_get_repo_path(worktree), | |||
6625 | NULL((void *)0)); | |||
6626 | if (error) | |||
6627 | goto done; | |||
6628 | ||||
6629 | error = apply_unveil(got_repo_get_path(repo), 1, | |||
6630 | got_worktree_get_root_path(worktree)); | |||
6631 | if (error) | |||
6632 | goto done; | |||
6633 | ||||
6634 | error = get_worktree_paths_from_argv(&paths, argc, argv, worktree); | |||
6635 | if (error) | |||
6636 | goto done; | |||
6637 | ||||
6638 | if (!can_recurse) { | |||
6639 | char *ondisk_path; | |||
6640 | struct stat sb; | |||
6641 | TAILQ_FOREACH(pe, &paths, entry)for((pe) = ((&paths)->tqh_first); (pe) != ((void *)0); (pe) = ((pe)->entry.tqe_next)) { | |||
6642 | if (asprintf(&ondisk_path, "%s/%s", | |||
6643 | got_worktree_get_root_path(worktree), | |||
6644 | pe->path) == -1) { | |||
6645 | error = got_error_from_errno("asprintf"); | |||
6646 | goto done; | |||
6647 | } | |||
6648 | if (lstat(ondisk_path, &sb) == -1) { | |||
6649 | if (errno(*__errno()) == ENOENT2) { | |||
6650 | free(ondisk_path); | |||
6651 | continue; | |||
6652 | } | |||
6653 | error = got_error_from_errno2("lstat", | |||
6654 | ondisk_path); | |||
6655 | free(ondisk_path); | |||
6656 | goto done; | |||
6657 | } | |||
6658 | free(ondisk_path); | |||
6659 | if (S_ISDIR(sb.st_mode)((sb.st_mode & 0170000) == 0040000)) { | |||
6660 | error = got_error_msg(GOT_ERR_BAD_PATH4, | |||
6661 | "removing directories requires -R option"); | |||
6662 | goto done; | |||
6663 | } | |||
6664 | } | |||
6665 | } | |||
6666 | ||||
6667 | error = got_worktree_schedule_delete(worktree, &paths, | |||
6668 | delete_local_mods, status_codes, print_remove_status, NULL((void *)0), | |||
6669 | repo, keep_on_disk); | |||
6670 | done: | |||
6671 | if (repo) | |||
6672 | got_repo_close(repo); | |||
6673 | if (worktree) | |||
6674 | got_worktree_close(worktree); | |||
6675 | TAILQ_FOREACH(pe, &paths, entry)for((pe) = ((&paths)->tqh_first); (pe) != ((void *)0); (pe) = ((pe)->entry.tqe_next)) | |||
6676 | free((char *)pe->path); | |||
6677 | got_pathlist_free(&paths); | |||
6678 | free(cwd); | |||
6679 | return error; | |||
6680 | } | |||
6681 | ||||
6682 | __dead__attribute__((__noreturn__)) static void | |||
6683 | usage_revert(void) | |||
6684 | { | |||
6685 | fprintf(stderr(&__sF[2]), "usage: %s revert [-p] [-F response-script] [-R] " | |||
6686 | "path ...\n", getprogname()); | |||
6687 | exit(1); | |||
6688 | } | |||
6689 | ||||
6690 | static const struct got_error * | |||
6691 | revert_progress(void *arg, unsigned char status, const char *path) | |||
6692 | { | |||
6693 | if (status == GOT_STATUS_UNVERSIONED'?') | |||
6694 | return NULL((void *)0); | |||
6695 | ||||
6696 | while (path[0] == '/') | |||
6697 | path++; | |||
6698 | printf("%c %s\n", status, path); | |||
6699 | return NULL((void *)0); | |||
6700 | } | |||
6701 | ||||
6702 | struct choose_patch_arg { | |||
6703 | FILE *patch_script_file; | |||
6704 | const char *action; | |||
6705 | }; | |||
6706 | ||||
6707 | static const struct got_error * | |||
6708 | show_change(unsigned char status, const char *path, FILE *patch_file, int n, | |||
6709 | int nchanges, const char *action) | |||
6710 | { | |||
6711 | char *line = NULL((void *)0); | |||
6712 | size_t linesize = 0; | |||
6713 | ssize_t linelen; | |||
6714 | ||||
6715 | switch (status) { | |||
6716 | case GOT_STATUS_ADD'A': | |||
6717 | printf("A %s\n%s this addition? [y/n] ", path, action); | |||
6718 | break; | |||
6719 | case GOT_STATUS_DELETE'D': | |||
6720 | printf("D %s\n%s this deletion? [y/n] ", path, action); | |||
6721 | break; | |||
6722 | case GOT_STATUS_MODIFY'M': | |||
6723 | if (fseek(patch_file, 0L, SEEK_SET0) == -1) | |||
6724 | return got_error_from_errno("fseek"); | |||
6725 | printf(GOT_COMMIT_SEP_STR"-----------------------------------------------\n"); | |||
6726 | while ((linelen = getline(&line, &linesize, patch_file)) != -1) | |||
6727 | printf("%s", line); | |||
6728 | if (ferror(patch_file)(!__isthreaded ? (((patch_file)->_flags & 0x0040) != 0 ) : (ferror)(patch_file))) | |||
6729 | return got_error_from_errno("getline"); | |||
6730 | printf(GOT_COMMIT_SEP_STR"-----------------------------------------------\n"); | |||
6731 | printf("M %s (change %d of %d)\n%s this change? [y/n/q] ", | |||
6732 | path, n, nchanges, action); | |||
6733 | break; | |||
6734 | default: | |||
6735 | return got_error_path(path, GOT_ERR_FILE_STATUS68); | |||
6736 | } | |||
6737 | ||||
6738 | return NULL((void *)0); | |||
6739 | } | |||
6740 | ||||
6741 | static const struct got_error * | |||
6742 | choose_patch(int *choice, void *arg, unsigned char status, const char *path, | |||
6743 | FILE *patch_file, int n, int nchanges) | |||
6744 | { | |||
6745 | const struct got_error *err = NULL((void *)0); | |||
6746 | char *line = NULL((void *)0); | |||
6747 | size_t linesize = 0; | |||
6748 | ssize_t linelen; | |||
6749 | int resp = ' '; | |||
6750 | struct choose_patch_arg *a = arg; | |||
6751 | ||||
6752 | *choice = GOT_PATCH_CHOICE_NONE0; | |||
6753 | ||||
6754 | if (a->patch_script_file) { | |||
6755 | char *nl; | |||
6756 | err = show_change(status, path, patch_file, n, nchanges, | |||
6757 | a->action); | |||
6758 | if (err) | |||
6759 | return err; | |||
6760 | linelen = getline(&line, &linesize, a->patch_script_file); | |||
6761 | if (linelen == -1) { | |||
6762 | if (ferror(a->patch_script_file)(!__isthreaded ? (((a->patch_script_file)->_flags & 0x0040) != 0) : (ferror)(a->patch_script_file))) | |||
6763 | return got_error_from_errno("getline"); | |||
6764 | return NULL((void *)0); | |||
6765 | } | |||
6766 | nl = strchr(line, '\n'); | |||
6767 | if (nl) | |||
6768 | *nl = '\0'; | |||
6769 | if (strcmp(line, "y") == 0) { | |||
6770 | *choice = GOT_PATCH_CHOICE_YES1; | |||
6771 | printf("y\n"); | |||
6772 | } else if (strcmp(line, "n") == 0) { | |||
6773 | *choice = GOT_PATCH_CHOICE_NO2; | |||
6774 | printf("n\n"); | |||
6775 | } else if (strcmp(line, "q") == 0 && | |||
6776 | status == GOT_STATUS_MODIFY'M') { | |||
6777 | *choice = GOT_PATCH_CHOICE_QUIT3; | |||
6778 | printf("q\n"); | |||
6779 | } else | |||
6780 | printf("invalid response '%s'\n", line); | |||
6781 | free(line); | |||
6782 | return NULL((void *)0); | |||
6783 | } | |||
6784 | ||||
6785 | while (resp != 'y' && resp != 'n' && resp != 'q') { | |||
6786 | err = show_change(status, path, patch_file, n, nchanges, | |||
6787 | a->action); | |||
6788 | if (err) | |||
6789 | return err; | |||
6790 | resp = getchar()(!__isthreaded ? (--((&__sF[0]))->_r < 0 ? __srget( (&__sF[0])) : (int)(*((&__sF[0]))->_p++)) : (getc) ((&__sF[0]))); | |||
6791 | if (resp == '\n') | |||
6792 | resp = getchar()(!__isthreaded ? (--((&__sF[0]))->_r < 0 ? __srget( (&__sF[0])) : (int)(*((&__sF[0]))->_p++)) : (getc) ((&__sF[0]))); | |||
6793 | if (status == GOT_STATUS_MODIFY'M') { | |||
6794 | if (resp != 'y' && resp != 'n' && resp != 'q') { | |||
6795 | printf("invalid response '%c'\n", resp); | |||
6796 | resp = ' '; | |||
6797 | } | |||
6798 | } else if (resp != 'y' && resp != 'n') { | |||
6799 | printf("invalid response '%c'\n", resp); | |||
6800 | resp = ' '; | |||
6801 | } | |||
6802 | } | |||
6803 | ||||
6804 | if (resp == 'y') | |||
6805 | *choice = GOT_PATCH_CHOICE_YES1; | |||
6806 | else if (resp == 'n') | |||
6807 | *choice = GOT_PATCH_CHOICE_NO2; | |||
6808 | else if (resp == 'q' && status == GOT_STATUS_MODIFY'M') | |||
6809 | *choice = GOT_PATCH_CHOICE_QUIT3; | |||
6810 | ||||
6811 | return NULL((void *)0); | |||
6812 | } | |||
6813 | ||||
6814 | ||||
6815 | static const struct got_error * | |||
6816 | cmd_revert(int argc, char *argv[]) | |||
6817 | { | |||
6818 | const struct got_error *error = NULL((void *)0); | |||
6819 | struct got_worktree *worktree = NULL((void *)0); | |||
6820 | struct got_repository *repo = NULL((void *)0); | |||
6821 | char *cwd = NULL((void *)0), *path = NULL((void *)0); | |||
6822 | struct got_pathlist_head paths; | |||
6823 | struct got_pathlist_entry *pe; | |||
6824 | int ch, can_recurse = 0, pflag = 0; | |||
6825 | FILE *patch_script_file = NULL((void *)0); | |||
6826 | const char *patch_script_path = NULL((void *)0); | |||
6827 | struct choose_patch_arg cpa; | |||
6828 | ||||
6829 | TAILQ_INIT(&paths)do { (&paths)->tqh_first = ((void *)0); (&paths)-> tqh_last = &(&paths)->tqh_first; } while (0); | |||
6830 | ||||
6831 | while ((ch = getopt(argc, argv, "pF:R")) != -1) { | |||
6832 | switch (ch) { | |||
6833 | case 'p': | |||
6834 | pflag = 1; | |||
6835 | break; | |||
6836 | case 'F': | |||
6837 | patch_script_path = optarg; | |||
6838 | break; | |||
6839 | case 'R': | |||
6840 | can_recurse = 1; | |||
6841 | break; | |||
6842 | default: | |||
6843 | usage_revert(); | |||
6844 | /* NOTREACHED */ | |||
6845 | } | |||
6846 | } | |||
6847 | ||||
6848 | argc -= optind; | |||
6849 | argv += optind; | |||
6850 | ||||
6851 | #ifndef PROFILE | |||
6852 | if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd " | |||
6853 | "unveil", NULL((void *)0)) == -1) | |||
6854 | err(1, "pledge"); | |||
6855 | #endif | |||
6856 | if (argc < 1) | |||
6857 | usage_revert(); | |||
6858 | if (patch_script_path && !pflag) | |||
6859 | errx(1, "-F option can only be used together with -p option"); | |||
6860 | ||||
6861 | cwd = getcwd(NULL((void *)0), 0); | |||
6862 | if (cwd == NULL((void *)0)) { | |||
6863 | error = got_error_from_errno("getcwd"); | |||
6864 | goto done; | |||
6865 | } | |||
6866 | error = got_worktree_open(&worktree, cwd); | |||
6867 | if (error) { | |||
6868 | if (error->code == GOT_ERR_NOT_WORKTREE60) | |||
6869 | error = wrap_not_worktree_error(error, "revert", cwd); | |||
6870 | goto done; | |||
6871 | } | |||
6872 | ||||
6873 | error = got_repo_open(&repo, got_worktree_get_repo_path(worktree), | |||
6874 | NULL((void *)0)); | |||
6875 | if (error != NULL((void *)0)) | |||
6876 | goto done; | |||
6877 | ||||
6878 | if (patch_script_path) { | |||
6879 | patch_script_file = fopen(patch_script_path, "r"); | |||
6880 | if (patch_script_file == NULL((void *)0)) { | |||
6881 | error = got_error_from_errno2("fopen", | |||
6882 | patch_script_path); | |||
6883 | goto done; | |||
6884 | } | |||
6885 | } | |||
6886 | error = apply_unveil(got_repo_get_path(repo), 1, | |||
6887 | got_worktree_get_root_path(worktree)); | |||
6888 | if (error) | |||
6889 | goto done; | |||
6890 | ||||
6891 | error = get_worktree_paths_from_argv(&paths, argc, argv, worktree); | |||
6892 | if (error) | |||
6893 | goto done; | |||
6894 | ||||
6895 | if (!can_recurse) { | |||
6896 | char *ondisk_path; | |||
6897 | struct stat sb; | |||
6898 | TAILQ_FOREACH(pe, &paths, entry)for((pe) = ((&paths)->tqh_first); (pe) != ((void *)0); (pe) = ((pe)->entry.tqe_next)) { | |||
6899 | if (asprintf(&ondisk_path, "%s/%s", | |||
6900 | got_worktree_get_root_path(worktree), | |||
6901 | pe->path) == -1) { | |||
6902 | error = got_error_from_errno("asprintf"); | |||
6903 | goto done; | |||
6904 | } | |||
6905 | if (lstat(ondisk_path, &sb) == -1) { | |||
6906 | if (errno(*__errno()) == ENOENT2) { | |||
6907 | free(ondisk_path); | |||
6908 | continue; | |||
6909 | } | |||
6910 | error = got_error_from_errno2("lstat", | |||
6911 | ondisk_path); | |||
6912 | free(ondisk_path); | |||
6913 | goto done; | |||
6914 | } | |||
6915 | free(ondisk_path); | |||
6916 | if (S_ISDIR(sb.st_mode)((sb.st_mode & 0170000) == 0040000)) { | |||
6917 | error = got_error_msg(GOT_ERR_BAD_PATH4, | |||
6918 | "reverting directories requires -R option"); | |||
6919 | goto done; | |||
6920 | } | |||
6921 | } | |||
6922 | } | |||
6923 | ||||
6924 | cpa.patch_script_file = patch_script_file; | |||
6925 | cpa.action = "revert"; | |||
6926 | error = got_worktree_revert(worktree, &paths, revert_progress, NULL((void *)0), | |||
6927 | pflag ? choose_patch : NULL((void *)0), &cpa, repo); | |||
6928 | done: | |||
6929 | if (patch_script_file && fclose(patch_script_file) == EOF(-1) && | |||
6930 | error == NULL((void *)0)) | |||
6931 | error = got_error_from_errno2("fclose", patch_script_path); | |||
6932 | if (repo) | |||
6933 | got_repo_close(repo); | |||
6934 | if (worktree) | |||
6935 | got_worktree_close(worktree); | |||
6936 | free(path); | |||
6937 | free(cwd); | |||
6938 | return error; | |||
6939 | } | |||
6940 | ||||
6941 | __dead__attribute__((__noreturn__)) static void | |||
6942 | usage_commit(void) | |||
6943 | { | |||
6944 | fprintf(stderr(&__sF[2]), "usage: %s commit [-F path] [-m msg] [-N] [-S] " | |||
6945 | "[path ...]\n", getprogname()); | |||
6946 | exit(1); | |||
6947 | } | |||
6948 | ||||
6949 | struct collect_commit_logmsg_arg { | |||
6950 | const char *cmdline_log; | |||
6951 | const char *prepared_log; | |||
6952 | int non_interactive; | |||
6953 | const char *editor; | |||
6954 | const char *worktree_path; | |||
6955 | const char *branch_name; | |||
6956 | const char *repo_path; | |||
6957 | char *logmsg_path; | |||
6958 | ||||
6959 | }; | |||
6960 | ||||
6961 | static const struct got_error * | |||
6962 | read_prepared_logmsg(char **logmsg, const char *path) | |||
6963 | { | |||
6964 | const struct got_error *err = NULL((void *)0); | |||
6965 | FILE *f = NULL((void *)0); | |||
6966 | struct stat sb; | |||
6967 | size_t r; | |||
6968 | ||||
6969 | *logmsg = NULL((void *)0); | |||
6970 | memset(&sb, 0, sizeof(sb)); | |||
6971 | ||||
6972 | f = fopen(path, "r"); | |||
6973 | if (f == NULL((void *)0)) | |||
6974 | return got_error_from_errno2("fopen", path); | |||
6975 | ||||
6976 | if (fstat(fileno(f)(!__isthreaded ? ((f)->_file) : (fileno)(f)), &sb) == -1) { | |||
6977 | err = got_error_from_errno2("fstat", path); | |||
6978 | goto done; | |||
6979 | } | |||
6980 | if (sb.st_size == 0) { | |||
6981 | err = got_error(GOT_ERR_COMMIT_MSG_EMPTY74); | |||
6982 | goto done; | |||
6983 | } | |||
6984 | ||||
6985 | *logmsg = malloc(sb.st_size + 1); | |||
6986 | if (*logmsg == NULL((void *)0)) { | |||
6987 | err = got_error_from_errno("malloc"); | |||
6988 | goto done; | |||
6989 | } | |||
6990 | ||||
6991 | r = fread(*logmsg, 1, sb.st_size, f); | |||
6992 | if (r != sb.st_size) { | |||
6993 | if (ferror(f)(!__isthreaded ? (((f)->_flags & 0x0040) != 0) : (ferror )(f))) | |||
6994 | err = got_error_from_errno2("fread", path); | |||
6995 | else | |||
6996 | err = got_error(GOT_ERR_IO6); | |||
6997 | goto done; | |||
6998 | } | |||
6999 | (*logmsg)[sb.st_size] = '\0'; | |||
7000 | done: | |||
7001 | if (fclose(f) == EOF(-1) && err == NULL((void *)0)) | |||
7002 | err = got_error_from_errno2("fclose", path); | |||
7003 | if (err) { | |||
7004 | free(*logmsg); | |||
7005 | *logmsg = NULL((void *)0); | |||
7006 | } | |||
7007 | return err; | |||
7008 | ||||
7009 | } | |||
7010 | ||||
7011 | static const struct got_error * | |||
7012 | collect_commit_logmsg(struct got_pathlist_head *commitable_paths, char **logmsg, | |||
7013 | void *arg) | |||
7014 | { | |||
7015 | char *initial_content = NULL((void *)0); | |||
7016 | struct got_pathlist_entry *pe; | |||
7017 | const struct got_error *err = NULL((void *)0); | |||
7018 | char *template = NULL((void *)0); | |||
7019 | struct collect_commit_logmsg_arg *a = arg; | |||
7020 | int initial_content_len; | |||
7021 | int fd = -1; | |||
7022 | size_t len; | |||
7023 | ||||
7024 | /* if a message was specified on the command line, just use it */ | |||
7025 | if (a->cmdline_log != NULL((void *)0) && strlen(a->cmdline_log) != 0) { | |||
7026 | len = strlen(a->cmdline_log) + 1; | |||
7027 | *logmsg = malloc(len + 1); | |||
7028 | if (*logmsg == NULL((void *)0)) | |||
7029 | return got_error_from_errno("malloc"); | |||
7030 | strlcpy(*logmsg, a->cmdline_log, len); | |||
7031 | return NULL((void *)0); | |||
7032 | } else if (a->prepared_log != NULL((void *)0) && a->non_interactive) | |||
7033 | return read_prepared_logmsg(logmsg, a->prepared_log); | |||
7034 | ||||
7035 | if (asprintf(&template, "%s/logmsg", a->worktree_path) == -1) | |||
7036 | return got_error_from_errno("asprintf"); | |||
7037 | ||||
7038 | err = got_opentemp_named_fd(&a->logmsg_path, &fd, template); | |||
7039 | if (err) | |||
7040 | goto done; | |||
7041 | ||||
7042 | if (a->prepared_log) { | |||
7043 | char *msg; | |||
7044 | err = read_prepared_logmsg(&msg, a->prepared_log); | |||
7045 | if (err) | |||
7046 | goto done; | |||
7047 | if (write(fd, msg, strlen(msg)) == -1) { | |||
7048 | err = got_error_from_errno2("write", a->logmsg_path); | |||
7049 | free(msg); | |||
7050 | goto done; | |||
7051 | } | |||
7052 | free(msg); | |||
7053 | } | |||
7054 | ||||
7055 | initial_content_len = asprintf(&initial_content, | |||
7056 | "\n# changes to be committed on branch %s:\n", | |||
7057 | a->branch_name); | |||
7058 | if (initial_content_len == -1) { | |||
7059 | err = got_error_from_errno("asprintf"); | |||
7060 | goto done; | |||
7061 | } | |||
7062 | ||||
7063 | if (write(fd, initial_content, initial_content_len) == -1) { | |||
7064 | err = got_error_from_errno2("write", a->logmsg_path); | |||
7065 | goto done; | |||
7066 | } | |||
7067 | ||||
7068 | TAILQ_FOREACH(pe, commitable_paths, entry)for((pe) = ((commitable_paths)->tqh_first); (pe) != ((void *)0); (pe) = ((pe)->entry.tqe_next)) { | |||
7069 | struct got_commitable *ct = pe->data; | |||
7070 | dprintf(fd, "# %c %s\n", | |||
7071 | got_commitable_get_status(ct), | |||
7072 | got_commitable_get_path(ct)); | |||
7073 | } | |||
7074 | ||||
7075 | err = edit_logmsg(logmsg, a->editor, a->logmsg_path, initial_content, | |||
7076 | initial_content_len, a->prepared_log ? 0 : 1); | |||
7077 | done: | |||
7078 | free(initial_content); | |||
7079 | free(template); | |||
7080 | ||||
7081 | if (fd != -1 && close(fd) == -1 && err == NULL((void *)0)) | |||
7082 | err = got_error_from_errno2("close", a->logmsg_path); | |||
7083 | ||||
7084 | /* Editor is done; we can now apply unveil(2) */ | |||
7085 | if (err == NULL((void *)0)) | |||
7086 | err = apply_unveil(a->repo_path, 0, a->worktree_path); | |||
7087 | if (err) { | |||
7088 | free(*logmsg); | |||
7089 | *logmsg = NULL((void *)0); | |||
7090 | } | |||
7091 | return err; | |||
7092 | } | |||
7093 | ||||
7094 | static const struct got_error * | |||
7095 | cmd_commit(int argc, char *argv[]) | |||
7096 | { | |||
7097 | const struct got_error *error = NULL((void *)0); | |||
7098 | struct got_worktree *worktree = NULL((void *)0); | |||
7099 | struct got_repository *repo = NULL((void *)0); | |||
7100 | char *cwd = NULL((void *)0), *id_str = NULL((void *)0); | |||
7101 | struct got_object_id *id = NULL((void *)0); | |||
7102 | const char *logmsg = NULL((void *)0); | |||
7103 | char *prepared_logmsg = NULL((void *)0); | |||
7104 | struct collect_commit_logmsg_arg cl_arg; | |||
7105 | char *gitconfig_path = NULL((void *)0), *editor = NULL((void *)0), *author = NULL((void *)0); | |||
7106 | int ch, rebase_in_progress, histedit_in_progress, preserve_logmsg = 0; | |||
7107 | int allow_bad_symlinks = 0, non_interactive = 0; | |||
7108 | struct got_pathlist_head paths; | |||
7109 | ||||
7110 | TAILQ_INIT(&paths)do { (&paths)->tqh_first = ((void *)0); (&paths)-> tqh_last = &(&paths)->tqh_first; } while (0); | |||
7111 | cl_arg.logmsg_path = NULL((void *)0); | |||
7112 | ||||
7113 | while ((ch = getopt(argc, argv, "F:m:NS")) != -1) { | |||
7114 | switch (ch) { | |||
7115 | case 'F': | |||
7116 | if (logmsg != NULL((void *)0)) | |||
7117 | option_conflict('F', 'm'); | |||
7118 | prepared_logmsg = realpath(optarg, NULL((void *)0)); | |||
7119 | if (prepared_logmsg == NULL((void *)0)) | |||
7120 | return got_error_from_errno2("realpath", | |||
7121 | optarg); | |||
7122 | break; | |||
7123 | case 'm': | |||
7124 | if (prepared_logmsg) | |||
7125 | option_conflict('m', 'F'); | |||
7126 | logmsg = optarg; | |||
7127 | break; | |||
7128 | case 'N': | |||
7129 | non_interactive = 1; | |||
7130 | break; | |||
7131 | case 'S': | |||
7132 | allow_bad_symlinks = 1; | |||
7133 | break; | |||
7134 | default: | |||
7135 | usage_commit(); | |||
7136 | /* NOTREACHED */ | |||
7137 | } | |||
7138 | } | |||
7139 | ||||
7140 | argc -= optind; | |||
7141 | argv += optind; | |||
7142 | ||||
7143 | #ifndef PROFILE | |||
7144 | if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd " | |||
7145 | "unveil", NULL((void *)0)) == -1) | |||
7146 | err(1, "pledge"); | |||
7147 | #endif | |||
7148 | cwd = getcwd(NULL((void *)0), 0); | |||
7149 | if (cwd == NULL((void *)0)) { | |||
7150 | error = got_error_from_errno("getcwd"); | |||
7151 | goto done; | |||
7152 | } | |||
7153 | error = got_worktree_open(&worktree, cwd); | |||
7154 | if (error) { | |||
7155 | if (error->code == GOT_ERR_NOT_WORKTREE60) | |||
7156 | error = wrap_not_worktree_error(error, "commit", cwd); | |||
7157 | goto done; | |||
7158 | } | |||
7159 | ||||
7160 | error = got_worktree_rebase_in_progress(&rebase_in_progress, worktree); | |||
7161 | if (error) | |||
7162 | goto done; | |||
7163 | if (rebase_in_progress) { | |||
7164 | error = got_error(GOT_ERR_REBASING88); | |||
7165 | goto done; | |||
7166 | } | |||
7167 | ||||
7168 | error = got_worktree_histedit_in_progress(&histedit_in_progress, | |||
7169 | worktree); | |||
7170 | if (error) | |||
7171 | goto done; | |||
7172 | ||||
7173 | error = get_gitconfig_path(&gitconfig_path); | |||
7174 | if (error) | |||
7175 | goto done; | |||
7176 | error = got_repo_open(&repo, got_worktree_get_repo_path(worktree), | |||
7177 | gitconfig_path); | |||
7178 | if (error != NULL((void *)0)) | |||
7179 | goto done; | |||
7180 | ||||
7181 | error = get_author(&author, repo, worktree); | |||
7182 | if (error) | |||
7183 | return error; | |||
7184 | ||||
7185 | /* | |||
7186 | * unveil(2) traverses exec(2); if an editor is used we have | |||
7187 | * to apply unveil after the log message has been written. | |||
7188 | */ | |||
7189 | if (logmsg == NULL((void *)0) || strlen(logmsg) == 0) | |||
7190 | error = get_editor(&editor); | |||
7191 | else | |||
7192 | error = apply_unveil(got_repo_get_path(repo), 0, | |||
7193 | got_worktree_get_root_path(worktree)); | |||
7194 | if (error) | |||
7195 | goto done; | |||
7196 | ||||
7197 | error = get_worktree_paths_from_argv(&paths, argc, argv, worktree); | |||
7198 | if (error) | |||
7199 | goto done; | |||
7200 | ||||
7201 | cl_arg.editor = editor; | |||
7202 | cl_arg.cmdline_log = logmsg; | |||
7203 | cl_arg.prepared_log = prepared_logmsg; | |||
7204 | cl_arg.non_interactive = non_interactive; | |||
7205 | cl_arg.worktree_path = got_worktree_get_root_path(worktree); | |||
7206 | cl_arg.branch_name = got_worktree_get_head_ref_name(worktree); | |||
7207 | if (!histedit_in_progress) { | |||
7208 | if (strncmp(cl_arg.branch_name, "refs/heads/", 11) != 0) { | |||
7209 | error = got_error(GOT_ERR_COMMIT_BRANCH100); | |||
7210 | goto done; | |||
7211 | } | |||
7212 | cl_arg.branch_name += 11; | |||
7213 | } | |||
7214 | cl_arg.repo_path = got_repo_get_path(repo); | |||
7215 | error = got_worktree_commit(&id, worktree, &paths, author, NULL((void *)0), | |||
7216 | allow_bad_symlinks, collect_commit_logmsg, &cl_arg, | |||
7217 | print_status, NULL((void *)0), repo); | |||
7218 | if (error) { | |||
7219 | if (error->code != GOT_ERR_COMMIT_MSG_EMPTY74 && | |||
7220 | cl_arg.logmsg_path != NULL((void *)0)) | |||
7221 | preserve_logmsg = 1; | |||
7222 | goto done; | |||
7223 | } | |||
7224 | ||||
7225 | error = got_object_id_str(&id_str, id); | |||
7226 | if (error) | |||
7227 | goto done; | |||
7228 | printf("Created commit %s\n", id_str); | |||
7229 | done: | |||
7230 | if (preserve_logmsg) { | |||
7231 | fprintf(stderr(&__sF[2]), "%s: log message preserved in %s\n", | |||
7232 | getprogname(), cl_arg.logmsg_path); | |||
7233 | } else if (cl_arg.logmsg_path && unlink(cl_arg.logmsg_path) == -1 && | |||
7234 | error == NULL((void *)0)) | |||
7235 | error = got_error_from_errno2("unlink", cl_arg.logmsg_path); | |||
7236 | free(cl_arg.logmsg_path); | |||
7237 | if (repo) | |||
7238 | got_repo_close(repo); | |||
7239 | if (worktree) | |||
7240 | got_worktree_close(worktree); | |||
7241 | free(cwd); | |||
7242 | free(id_str); | |||
7243 | free(gitconfig_path); | |||
7244 | free(editor); | |||
7245 | free(author); | |||
7246 | free(prepared_logmsg); | |||
7247 | return error; | |||
7248 | } | |||
7249 | ||||
7250 | __dead__attribute__((__noreturn__)) static void | |||
7251 | usage_cherrypick(void) | |||
7252 | { | |||
7253 | fprintf(stderr(&__sF[2]), "usage: %s cherrypick commit-id\n", getprogname()); | |||
7254 | exit(1); | |||
7255 | } | |||
7256 | ||||
7257 | static const struct got_error * | |||
7258 | cmd_cherrypick(int argc, char *argv[]) | |||
7259 | { | |||
7260 | const struct got_error *error = NULL((void *)0); | |||
7261 | struct got_worktree *worktree = NULL((void *)0); | |||
7262 | struct got_repository *repo = NULL((void *)0); | |||
7263 | char *cwd = NULL((void *)0), *commit_id_str = NULL((void *)0); | |||
7264 | struct got_object_id *commit_id = NULL((void *)0); | |||
7265 | struct got_commit_object *commit = NULL((void *)0); | |||
7266 | struct got_object_qid *pid; | |||
7267 | struct got_reference *head_ref = NULL((void *)0); | |||
7268 | int ch; | |||
7269 | struct got_update_progress_arg upa; | |||
7270 | ||||
7271 | while ((ch = getopt(argc, argv, "")) != -1) { | |||
7272 | switch (ch) { | |||
7273 | default: | |||
7274 | usage_cherrypick(); | |||
7275 | /* NOTREACHED */ | |||
7276 | } | |||
7277 | } | |||
7278 | ||||
7279 | argc -= optind; | |||
7280 | argv += optind; | |||
7281 | ||||
7282 | #ifndef PROFILE | |||
7283 | if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd " | |||
7284 | "unveil", NULL((void *)0)) == -1) | |||
7285 | err(1, "pledge"); | |||
7286 | #endif | |||
7287 | if (argc != 1) | |||
7288 | usage_cherrypick(); | |||
7289 | ||||
7290 | cwd = getcwd(NULL((void *)0), 0); | |||
7291 | if (cwd == NULL((void *)0)) { | |||
7292 | error = got_error_from_errno("getcwd"); | |||
7293 | goto done; | |||
7294 | } | |||
7295 | error = got_worktree_open(&worktree, cwd); | |||
7296 | if (error) { | |||
7297 | if (error->code == GOT_ERR_NOT_WORKTREE60) | |||
7298 | error = wrap_not_worktree_error(error, "cherrypick", | |||
7299 | cwd); | |||
7300 | goto done; | |||
7301 | } | |||
7302 | ||||
7303 | error = got_repo_open(&repo, got_worktree_get_repo_path(worktree), | |||
7304 | NULL((void *)0)); | |||
7305 | if (error != NULL((void *)0)) | |||
7306 | goto done; | |||
7307 | ||||
7308 | error = apply_unveil(got_repo_get_path(repo), 0, | |||
7309 | got_worktree_get_root_path(worktree)); | |||
7310 | if (error) | |||
7311 | goto done; | |||
7312 | ||||
7313 | error = got_repo_match_object_id_prefix(&commit_id, argv[0], | |||
7314 | GOT_OBJ_TYPE_COMMIT1, repo); | |||
7315 | if (error != NULL((void *)0)) { | |||
7316 | struct got_reference *ref; | |||
7317 | if (error->code != GOT_ERR_BAD_OBJ_ID_STR23) | |||
7318 | goto done; | |||
7319 | error = got_ref_open(&ref, repo, argv[0], 0); | |||
7320 | if (error != NULL((void *)0)) | |||
7321 | goto done; | |||
7322 | error = got_ref_resolve(&commit_id, repo, ref); | |||
7323 | got_ref_close(ref); | |||
7324 | if (error != NULL((void *)0)) | |||
7325 | goto done; | |||
7326 | } | |||
7327 | error = got_object_id_str(&commit_id_str, commit_id); | |||
7328 | if (error) | |||
7329 | goto done; | |||
7330 | ||||
7331 | error = got_ref_open(&head_ref, repo, | |||
7332 | got_worktree_get_head_ref_name(worktree), 0); | |||
7333 | if (error != NULL((void *)0)) | |||
7334 | goto done; | |||
7335 | ||||
7336 | error = check_same_branch(commit_id, head_ref, NULL((void *)0), repo); | |||
7337 | if (error) { | |||
7338 | if (error->code != GOT_ERR_ANCESTRY55) | |||
7339 | goto done; | |||
7340 | error = NULL((void *)0); | |||
7341 | } else { | |||
7342 | error = got_error(GOT_ERR_SAME_BRANCH79); | |||
7343 | goto done; | |||
7344 | } | |||
7345 | ||||
7346 | error = got_object_open_as_commit(&commit, repo, commit_id); | |||
7347 | if (error) | |||
7348 | goto done; | |||
7349 | pid = SIMPLEQ_FIRST(got_object_commit_get_parent_ids(commit))((got_object_commit_get_parent_ids(commit))->sqh_first); | |||
7350 | memset(&upa, 0, sizeof(upa)); | |||
7351 | error = got_worktree_merge_files(worktree, pid ? pid->id : NULL((void *)0), | |||
7352 | commit_id, repo, update_progress, &upa, check_cancelled, | |||
7353 | NULL((void *)0)); | |||
7354 | if (error != NULL((void *)0)) | |||
7355 | goto done; | |||
7356 | ||||
7357 | if (upa.did_something) | |||
7358 | printf("Merged commit %s\n", commit_id_str); | |||
7359 | print_update_progress_stats(&upa); | |||
7360 | done: | |||
7361 | if (commit) | |||
7362 | got_object_commit_close(commit); | |||
7363 | free(commit_id_str); | |||
7364 | if (head_ref) | |||
7365 | got_ref_close(head_ref); | |||
7366 | if (worktree) | |||
7367 | got_worktree_close(worktree); | |||
7368 | if (repo) | |||
7369 | got_repo_close(repo); | |||
7370 | return error; | |||
7371 | } | |||
7372 | ||||
7373 | __dead__attribute__((__noreturn__)) static void | |||
7374 | usage_backout(void) | |||
7375 | { | |||
7376 | fprintf(stderr(&__sF[2]), "usage: %s backout commit-id\n", getprogname()); | |||
7377 | exit(1); | |||
7378 | } | |||
7379 | ||||
7380 | static const struct got_error * | |||
7381 | cmd_backout(int argc, char *argv[]) | |||
7382 | { | |||
7383 | const struct got_error *error = NULL((void *)0); | |||
7384 | struct got_worktree *worktree = NULL((void *)0); | |||
7385 | struct got_repository *repo = NULL((void *)0); | |||
7386 | char *cwd = NULL((void *)0), *commit_id_str = NULL((void *)0); | |||
7387 | struct got_object_id *commit_id = NULL((void *)0); | |||
7388 | struct got_commit_object *commit = NULL((void *)0); | |||
7389 | struct got_object_qid *pid; | |||
7390 | struct got_reference *head_ref = NULL((void *)0); | |||
7391 | int ch; | |||
7392 | struct got_update_progress_arg upa; | |||
7393 | ||||
7394 | while ((ch = getopt(argc, argv, "")) != -1) { | |||
7395 | switch (ch) { | |||
7396 | default: | |||
7397 | usage_backout(); | |||
7398 | /* NOTREACHED */ | |||
7399 | } | |||
7400 | } | |||
7401 | ||||
7402 | argc -= optind; | |||
7403 | argv += optind; | |||
7404 | ||||
7405 | #ifndef PROFILE | |||
7406 | if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd " | |||
7407 | "unveil", NULL((void *)0)) == -1) | |||
7408 | err(1, "pledge"); | |||
7409 | #endif | |||
7410 | if (argc != 1) | |||
7411 | usage_backout(); | |||
7412 | ||||
7413 | cwd = getcwd(NULL((void *)0), 0); | |||
7414 | if (cwd == NULL((void *)0)) { | |||
7415 | error = got_error_from_errno("getcwd"); | |||
7416 | goto done; | |||
7417 | } | |||
7418 | error = got_worktree_open(&worktree, cwd); | |||
7419 | if (error) { | |||
7420 | if (error->code == GOT_ERR_NOT_WORKTREE60) | |||
7421 | error = wrap_not_worktree_error(error, "backout", cwd); | |||
7422 | goto done; | |||
7423 | } | |||
7424 | ||||
7425 | error = got_repo_open(&repo, got_worktree_get_repo_path(worktree), | |||
7426 | NULL((void *)0)); | |||
7427 | if (error != NULL((void *)0)) | |||
7428 | goto done; | |||
7429 | ||||
7430 | error = apply_unveil(got_repo_get_path(repo), 0, | |||
7431 | got_worktree_get_root_path(worktree)); | |||
7432 | if (error) | |||
7433 | goto done; | |||
7434 | ||||
7435 | error = got_repo_match_object_id_prefix(&commit_id, argv[0], | |||
7436 | GOT_OBJ_TYPE_COMMIT1, repo); | |||
7437 | if (error != NULL((void *)0)) { | |||
7438 | struct got_reference *ref; | |||
7439 | if (error->code != GOT_ERR_BAD_OBJ_ID_STR23) | |||
7440 | goto done; | |||
7441 | error = got_ref_open(&ref, repo, argv[0], 0); | |||
7442 | if (error != NULL((void *)0)) | |||
7443 | goto done; | |||
7444 | error = got_ref_resolve(&commit_id, repo, ref); | |||
7445 | got_ref_close(ref); | |||
7446 | if (error != NULL((void *)0)) | |||
7447 | goto done; | |||
7448 | } | |||
7449 | error = got_object_id_str(&commit_id_str, commit_id); | |||
7450 | if (error) | |||
7451 | goto done; | |||
7452 | ||||
7453 | error = got_ref_open(&head_ref, repo, | |||
7454 | got_worktree_get_head_ref_name(worktree), 0); | |||
7455 | if (error != NULL((void *)0)) | |||
7456 | goto done; | |||
7457 | ||||
7458 | error = check_same_branch(commit_id, head_ref, NULL((void *)0), repo); | |||
7459 | if (error) | |||
7460 | goto done; | |||
7461 | ||||
7462 | error = got_object_open_as_commit(&commit, repo, commit_id); | |||
7463 | if (error) | |||
7464 | goto done; | |||
7465 | pid = SIMPLEQ_FIRST(got_object_commit_get_parent_ids(commit))((got_object_commit_get_parent_ids(commit))->sqh_first); | |||
7466 | if (pid == NULL((void *)0)) { | |||
7467 | error = got_error(GOT_ERR_ROOT_COMMIT80); | |||
7468 | goto done; | |||
7469 | } | |||
7470 | ||||
7471 | memset(&upa, 0, sizeof(upa)); | |||
7472 | error = got_worktree_merge_files(worktree, commit_id, pid->id, repo, | |||
7473 | update_progress, &upa, check_cancelled, NULL((void *)0)); | |||
7474 | if (error != NULL((void *)0)) | |||
7475 | goto done; | |||
7476 | ||||
7477 | if (upa.did_something) | |||
7478 | printf("Backed out commit %s\n", commit_id_str); | |||
7479 | print_update_progress_stats(&upa); | |||
7480 | done: | |||
7481 | if (commit) | |||
7482 | got_object_commit_close(commit); | |||
7483 | free(commit_id_str); | |||
7484 | if (head_ref) | |||
7485 | got_ref_close(head_ref); | |||
7486 | if (worktree) | |||
7487 | got_worktree_close(worktree); | |||
7488 | if (repo) | |||
7489 | got_repo_close(repo); | |||
7490 | return error; | |||
7491 | } | |||
7492 | ||||
7493 | __dead__attribute__((__noreturn__)) static void | |||
7494 | usage_rebase(void) | |||
7495 | { | |||
7496 | fprintf(stderr(&__sF[2]), "usage: %s rebase [-a] [-c] [-l] [branch]\n", | |||
7497 | getprogname()); | |||
7498 | exit(1); | |||
7499 | } | |||
7500 | ||||
7501 | void | |||
7502 | trim_logmsg(char *logmsg, int limit) | |||
7503 | { | |||
7504 | char *nl; | |||
7505 | size_t len; | |||
7506 | ||||
7507 | len = strlen(logmsg); | |||
7508 | if (len > limit) | |||
7509 | len = limit; | |||
7510 | logmsg[len] = '\0'; | |||
7511 | nl = strchr(logmsg, '\n'); | |||
7512 | if (nl) | |||
7513 | *nl = '\0'; | |||
7514 | } | |||
7515 | ||||
7516 | static const struct got_error * | |||
7517 | get_short_logmsg(char **logmsg, int limit, struct got_commit_object *commit) | |||
7518 | { | |||
7519 | const struct got_error *err; | |||
7520 | char *logmsg0 = NULL((void *)0); | |||
7521 | const char *s; | |||
7522 | ||||
7523 | err = got_object_commit_get_logmsg(&logmsg0, commit); | |||
7524 | if (err) | |||
7525 | return err; | |||
7526 | ||||
7527 | s = logmsg0; | |||
7528 | while (isspace((unsigned char)s[0])) | |||
7529 | s++; | |||
7530 | ||||
7531 | *logmsg = strdup(s); | |||
7532 | if (*logmsg == NULL((void *)0)) { | |||
7533 | err = got_error_from_errno("strdup"); | |||
7534 | goto done; | |||
7535 | } | |||
7536 | ||||
7537 | trim_logmsg(*logmsg, limit); | |||
7538 | done: | |||
7539 | free(logmsg0); | |||
7540 | return err; | |||
7541 | } | |||
7542 | ||||
7543 | static const struct got_error * | |||
7544 | show_rebase_merge_conflict(struct got_object_id *id, struct got_repository *repo) | |||
7545 | { | |||
7546 | const struct got_error *err; | |||
7547 | struct got_commit_object *commit = NULL((void *)0); | |||
7548 | char *id_str = NULL((void *)0), *logmsg = NULL((void *)0); | |||
7549 | ||||
7550 | err = got_object_open_as_commit(&commit, repo, id); | |||
7551 | if (err) | |||
7552 | return err; | |||
7553 | ||||
7554 | err = got_object_id_str(&id_str, id); | |||
7555 | if (err) | |||
7556 | goto done; | |||
7557 | ||||
7558 | id_str[12] = '\0'; | |||
7559 | ||||
7560 | err = get_short_logmsg(&logmsg, 42, commit); | |||
7561 | if (err) | |||
7562 | goto done; | |||
7563 | ||||
7564 | printf("%s -> merge conflict: %s\n", id_str, logmsg); | |||
7565 | done: | |||
7566 | free(id_str); | |||
7567 | got_object_commit_close(commit); | |||
7568 | free(logmsg); | |||
7569 | return err; | |||
7570 | } | |||
7571 | ||||
7572 | static const struct got_error * | |||
7573 | show_rebase_progress(struct got_commit_object *commit, | |||
7574 | struct got_object_id *old_id, struct got_object_id *new_id) | |||
7575 | { | |||
7576 | const struct got_error *err; | |||
7577 | char *old_id_str = NULL((void *)0), *new_id_str = NULL((void *)0), *logmsg = NULL((void *)0); | |||
7578 | ||||
7579 | err = got_object_id_str(&old_id_str, old_id); | |||
7580 | if (err) | |||
7581 | goto done; | |||
7582 | ||||
7583 | if (new_id) { | |||
7584 | err = got_object_id_str(&new_id_str, new_id); | |||
7585 | if (err) | |||
7586 | goto done; | |||
7587 | } | |||
7588 | ||||
7589 | old_id_str[12] = '\0'; | |||
7590 | if (new_id_str) | |||
7591 | new_id_str[12] = '\0'; | |||
7592 | ||||
7593 | err = get_short_logmsg(&logmsg, 42, commit); | |||
7594 | if (err) | |||
7595 | goto done; | |||
7596 | ||||
7597 | printf("%s -> %s: %s\n", old_id_str, | |||
7598 | new_id_str ? new_id_str : "no-op change", logmsg); | |||
7599 | done: | |||
7600 | free(old_id_str); | |||
7601 | free(new_id_str); | |||
7602 | free(logmsg); | |||
7603 | return err; | |||
7604 | } | |||
7605 | ||||
7606 | static const struct got_error * | |||
7607 | rebase_complete(struct got_worktree *worktree, struct got_fileindex *fileindex, | |||
7608 | struct got_reference *branch, struct got_reference *new_base_branch, | |||
7609 | struct got_reference *tmp_branch, struct got_repository *repo, | |||
7610 | int create_backup) | |||
7611 | { | |||
7612 | printf("Switching work tree to %s\n", got_ref_get_name(branch)); | |||
7613 | return got_worktree_rebase_complete(worktree, fileindex, | |||
7614 | new_base_branch, tmp_branch, branch, repo, create_backup); | |||
7615 | } | |||
7616 | ||||
7617 | static const struct got_error * | |||
7618 | rebase_commit(struct got_pathlist_head *merged_paths, | |||
7619 | struct got_worktree *worktree, struct got_fileindex *fileindex, | |||
7620 | struct got_reference *tmp_branch, | |||
7621 | struct got_object_id *commit_id, struct got_repository *repo) | |||
7622 | { | |||
7623 | const struct got_error *error; | |||
7624 | struct got_commit_object *commit; | |||
7625 | struct got_object_id *new_commit_id; | |||
7626 | ||||
7627 | error = got_object_open_as_commit(&commit, repo, commit_id); | |||
7628 | if (error) | |||
7629 | return error; | |||
7630 | ||||
7631 | error = got_worktree_rebase_commit(&new_commit_id, merged_paths, | |||
7632 | worktree, fileindex, tmp_branch, commit, commit_id, repo); | |||
7633 | if (error) { | |||
7634 | if (error->code != GOT_ERR_COMMIT_NO_CHANGES76) | |||
7635 | goto done; | |||
7636 | error = show_rebase_progress(commit, commit_id, NULL((void *)0)); | |||
7637 | } else { | |||
7638 | error = show_rebase_progress(commit, commit_id, new_commit_id); | |||
7639 | free(new_commit_id); | |||
7640 | } | |||
7641 | done: | |||
7642 | got_object_commit_close(commit); | |||
7643 | return error; | |||
7644 | } | |||
7645 | ||||
7646 | struct check_path_prefix_arg { | |||
7647 | const char *path_prefix; | |||
7648 | size_t len; | |||
7649 | int errcode; | |||
7650 | }; | |||
7651 | ||||
7652 | static const struct got_error * | |||
7653 | check_path_prefix_in_diff(void *arg, struct got_blob_object *blob1, | |||
7654 | struct got_blob_object *blob2, struct got_object_id *id1, | |||
7655 | struct got_object_id *id2, const char *path1, const char *path2, | |||
7656 | mode_t mode1, mode_t mode2, struct got_repository *repo) | |||
7657 | { | |||
7658 | struct check_path_prefix_arg *a = arg; | |||
7659 | ||||
7660 | if ((path1 && !got_path_is_child(path1, a->path_prefix, a->len)) || | |||
7661 | (path2 && !got_path_is_child(path2, a->path_prefix, a->len))) | |||
7662 | return got_error(a->errcode); | |||
7663 | ||||
7664 | return NULL((void *)0); | |||
7665 | } | |||
7666 | ||||
7667 | static const struct got_error * | |||
7668 | check_path_prefix(struct got_object_id *parent_id, | |||
7669 | struct got_object_id *commit_id, const char *path_prefix, | |||
7670 | int errcode, struct got_repository *repo) | |||
7671 | { | |||
7672 | const struct got_error *err; | |||
7673 | struct got_tree_object *tree1 = NULL((void *)0), *tree2 = NULL((void *)0); | |||
7674 | struct got_commit_object *commit = NULL((void *)0), *parent_commit = NULL((void *)0); | |||
7675 | struct check_path_prefix_arg cpp_arg; | |||
7676 | ||||
7677 | if (got_path_is_root_dir(path_prefix)) | |||
7678 | return NULL((void *)0); | |||
7679 | ||||
7680 | err = got_object_open_as_commit(&commit, repo, commit_id); | |||
7681 | if (err) | |||
7682 | goto done; | |||
7683 | ||||
7684 | err = got_object_open_as_commit(&parent_commit, repo, parent_id); | |||
7685 | if (err) | |||
7686 | goto done; | |||
7687 | ||||
7688 | err = got_object_open_as_tree(&tree1, repo, | |||
7689 | got_object_commit_get_tree_id(parent_commit)); | |||
7690 | if (err) | |||
7691 | goto done; | |||
7692 | ||||
7693 | err = got_object_open_as_tree(&tree2, repo, | |||
7694 | got_object_commit_get_tree_id(commit)); | |||
7695 | if (err) | |||
7696 | goto done; | |||
7697 | ||||
7698 | cpp_arg.path_prefix = path_prefix; | |||
7699 | while (cpp_arg.path_prefix[0] == '/') | |||
7700 | cpp_arg.path_prefix++; | |||
7701 | cpp_arg.len = strlen(cpp_arg.path_prefix); | |||
7702 | cpp_arg.errcode = errcode; | |||
7703 | err = got_diff_tree(tree1, tree2, "", "", repo, | |||
7704 | check_path_prefix_in_diff, &cpp_arg, 0); | |||
7705 | done: | |||
7706 | if (tree1) | |||
7707 | got_object_tree_close(tree1); | |||
7708 | if (tree2) | |||
7709 | got_object_tree_close(tree2); | |||
7710 | if (commit) | |||
7711 | got_object_commit_close(commit); | |||
7712 | if (parent_commit) | |||
7713 | got_object_commit_close(parent_commit); | |||
7714 | return err; | |||
7715 | } | |||
7716 | ||||
7717 | static const struct got_error * | |||
7718 | collect_commits(struct got_object_id_queue *commits, | |||
7719 | struct got_object_id *initial_commit_id, | |||
7720 | struct got_object_id *iter_start_id, struct got_object_id *iter_stop_id, | |||
7721 | const char *path_prefix, int path_prefix_errcode, | |||
7722 | struct got_repository *repo) | |||
7723 | { | |||
7724 | const struct got_error *err = NULL((void *)0); | |||
7725 | struct got_commit_graph *graph = NULL((void *)0); | |||
7726 | struct got_object_id *parent_id = NULL((void *)0); | |||
7727 | struct got_object_qid *qid; | |||
7728 | struct got_object_id *commit_id = initial_commit_id; | |||
7729 | ||||
7730 | err = got_commit_graph_open(&graph, "/", 1); | |||
7731 | if (err) | |||
7732 | return err; | |||
7733 | ||||
7734 | err = got_commit_graph_iter_start(graph, iter_start_id, repo, | |||
7735 | check_cancelled, NULL((void *)0)); | |||
7736 | if (err) | |||
7737 | goto done; | |||
7738 | while (got_object_id_cmp(commit_id, iter_stop_id) != 0) { | |||
7739 | err = got_commit_graph_iter_next(&parent_id, graph, repo, | |||
7740 | check_cancelled, NULL((void *)0)); | |||
7741 | if (err) { | |||
7742 | if (err->code == GOT_ERR_ITER_COMPLETED46) { | |||
7743 | err = got_error_msg(GOT_ERR_ANCESTRY55, | |||
7744 | "ran out of commits to rebase before " | |||
7745 | "youngest common ancestor commit has " | |||
7746 | "been reached?!?"); | |||
7747 | } | |||
7748 | goto done; | |||
7749 | } else { | |||
7750 | err = check_path_prefix(parent_id, commit_id, | |||
7751 | path_prefix, path_prefix_errcode, repo); | |||
7752 | if (err) | |||
7753 | goto done; | |||
7754 | ||||
7755 | err = got_object_qid_alloc(&qid, commit_id); | |||
7756 | if (err) | |||
7757 | goto done; | |||
7758 | SIMPLEQ_INSERT_HEAD(commits, qid, entry)do { if (((qid)->entry.sqe_next = (commits)->sqh_first) == ((void *)0)) (commits)->sqh_last = &(qid)->entry .sqe_next; (commits)->sqh_first = (qid); } while (0); | |||
7759 | commit_id = parent_id; | |||
7760 | } | |||
7761 | } | |||
7762 | done: | |||
7763 | got_commit_graph_close(graph); | |||
7764 | return err; | |||
7765 | } | |||
7766 | ||||
7767 | static const struct got_error * | |||
7768 | get_commit_brief_str(char **brief_str, struct got_commit_object *commit) | |||
7769 | { | |||
7770 | const struct got_error *err = NULL((void *)0); | |||
7771 | time_t committer_time; | |||
7772 | struct tm tm; | |||
7773 | char datebuf[11]; /* YYYY-MM-DD + NUL */ | |||
7774 | char *author0 = NULL((void *)0), *author, *smallerthan; | |||
7775 | char *logmsg0 = NULL((void *)0), *logmsg, *newline; | |||
7776 | ||||
7777 | committer_time = got_object_commit_get_committer_time(commit); | |||
7778 | if (localtime_r(&committer_time, &tm) == NULL((void *)0)) | |||
7779 | return got_error_from_errno("localtime_r"); | |||
7780 | if (strftime(datebuf, sizeof(datebuf), "%G-%m-%d", &tm) == 0) | |||
7781 | return got_error(GOT_ERR_NO_SPACE9); | |||
7782 | ||||
7783 | author0 = strdup(got_object_commit_get_author(commit)); | |||
7784 | if (author0 == NULL((void *)0)) | |||
7785 | return got_error_from_errno("strdup"); | |||
7786 | author = author0; | |||
7787 | smallerthan = strchr(author, '<'); | |||
7788 | if (smallerthan && smallerthan[1] != '\0') | |||
7789 | author = smallerthan + 1; | |||
7790 | author[strcspn(author, "@>")] = '\0'; | |||
7791 | ||||
7792 | err = got_object_commit_get_logmsg(&logmsg0, commit); | |||
7793 | if (err) | |||
7794 | goto done; | |||
7795 | logmsg = logmsg0; | |||
7796 | while (*logmsg == '\n') | |||
7797 | logmsg++; | |||
7798 | newline = strchr(logmsg, '\n'); | |||
7799 | if (newline) | |||
7800 | *newline = '\0'; | |||
7801 | ||||
7802 | if (asprintf(brief_str, "%s %s %s", | |||
7803 | datebuf, author, logmsg) == -1) | |||
7804 | err = got_error_from_errno("asprintf"); | |||
7805 | done: | |||
7806 | free(author0); | |||
7807 | free(logmsg0); | |||
7808 | return err; | |||
7809 | } | |||
7810 | ||||
7811 | static const struct got_error * | |||
7812 | print_backup_ref(const char *branch_name, const char *new_id_str, | |||
7813 | struct got_object_id *old_commit_id, struct got_commit_object *old_commit, | |||
7814 | struct got_reflist_object_id_map *refs_idmap, | |||
7815 | struct got_repository *repo) | |||
7816 | { | |||
7817 | const struct got_error *err = NULL((void *)0); | |||
7818 | struct got_reflist_head *refs; | |||
7819 | char *refs_str = NULL((void *)0); | |||
7820 | struct got_object_id *new_commit_id = NULL((void *)0); | |||
7821 | struct got_commit_object *new_commit = NULL((void *)0); | |||
7822 | char *new_commit_brief_str = NULL((void *)0); | |||
7823 | struct got_object_id *yca_id = NULL((void *)0); | |||
7824 | struct got_commit_object *yca_commit = NULL((void *)0); | |||
7825 | char *yca_id_str = NULL((void *)0), *yca_brief_str = NULL((void *)0); | |||
7826 | char *custom_refs_str; | |||
7827 | ||||
7828 | if (asprintf(&custom_refs_str, "formerly %s", branch_name) == -1) | |||
7829 | return got_error_from_errno("asprintf"); | |||
7830 | ||||
7831 | err = print_commit(old_commit, old_commit_id, repo, NULL((void *)0), NULL((void *)0), | |||
7832 | 0, 0, refs_idmap, custom_refs_str); | |||
7833 | if (err) | |||
7834 | goto done; | |||
7835 | ||||
7836 | err = got_object_resolve_id_str(&new_commit_id, repo, new_id_str); | |||
7837 | if (err) | |||
7838 | goto done; | |||
7839 | ||||
7840 | refs = got_reflist_object_id_map_lookup(refs_idmap, new_commit_id); | |||
7841 | if (refs) { | |||
7842 | err = build_refs_str(&refs_str, refs, new_commit_id, repo); | |||
7843 | if (err) | |||
7844 | goto done; | |||
7845 | } | |||
7846 | ||||
7847 | err = got_object_open_as_commit(&new_commit, repo, new_commit_id); | |||
7848 | if (err) | |||
7849 | goto done; | |||
7850 | ||||
7851 | err = get_commit_brief_str(&new_commit_brief_str, new_commit); | |||
7852 | if (err) | |||
7853 | goto done; | |||
7854 | ||||
7855 | err = got_commit_graph_find_youngest_common_ancestor(&yca_id, | |||
7856 | old_commit_id, new_commit_id, repo, check_cancelled, NULL((void *)0)); | |||
7857 | if (err) | |||
7858 | goto done; | |||
7859 | ||||
7860 | printf("has become commit %s%s%s%s\n %s\n", new_id_str, | |||
7861 | refs_str ? " (" : "", refs_str ? refs_str : "", | |||
7862 | refs_str ? ")" : "", new_commit_brief_str); | |||
7863 | if (yca_id && got_object_id_cmp(yca_id, new_commit_id) != 0 && | |||
7864 | got_object_id_cmp(yca_id, old_commit_id) != 0) { | |||
7865 | free(refs_str); | |||
7866 | refs_str = NULL((void *)0); | |||
7867 | ||||
7868 | err = got_object_open_as_commit(&yca_commit, repo, yca_id); | |||
7869 | if (err) | |||
7870 | goto done; | |||
7871 | ||||
7872 | err = get_commit_brief_str(&yca_brief_str, yca_commit); | |||
7873 | if (err) | |||
7874 | goto done; | |||
7875 | ||||
7876 | err = got_object_id_str(&yca_id_str, yca_id); | |||
7877 | if (err) | |||
7878 | goto done; | |||
7879 | ||||
7880 | refs = got_reflist_object_id_map_lookup(refs_idmap, yca_id); | |||
7881 | if (refs) { | |||
7882 | err = build_refs_str(&refs_str, refs, yca_id, repo); | |||
7883 | if (err) | |||
7884 | goto done; | |||
7885 | } | |||
7886 | printf("history forked at %s%s%s%s\n %s\n", | |||
7887 | yca_id_str, | |||
7888 | refs_str ? " (" : "", refs_str ? refs_str : "", | |||
7889 | refs_str ? ")" : "", yca_brief_str); | |||
7890 | } | |||
7891 | done: | |||
7892 | free(custom_refs_str); | |||
7893 | free(new_commit_id); | |||
7894 | free(refs_str); | |||
7895 | free(yca_id); | |||
7896 | free(yca_id_str); | |||
7897 | free(yca_brief_str); | |||
7898 | if (new_commit) | |||
7899 | got_object_commit_close(new_commit); | |||
7900 | if (yca_commit) | |||
7901 | got_object_commit_close(yca_commit); | |||
7902 | ||||
7903 | return NULL((void *)0); | |||
7904 | } | |||
7905 | ||||
7906 | static const struct got_error * | |||
7907 | list_backup_refs(const char *backup_ref_prefix, const char *wanted_branch_name, | |||
7908 | struct got_repository *repo) | |||
7909 | { | |||
7910 | const struct got_error *err; | |||
7911 | struct got_reflist_head refs, backup_refs; | |||
7912 | struct got_reflist_entry *re; | |||
7913 | const size_t backup_ref_prefix_len = strlen(backup_ref_prefix); | |||
7914 | struct got_object_id *old_commit_id = NULL((void *)0); | |||
7915 | char *branch_name = NULL((void *)0); | |||
7916 | struct got_commit_object *old_commit = NULL((void *)0); | |||
7917 | struct got_reflist_object_id_map *refs_idmap = NULL((void *)0); | |||
7918 | int wanted_branch_found = 0; | |||
7919 | ||||
7920 | TAILQ_INIT(&refs)do { (&refs)->tqh_first = ((void *)0); (&refs)-> tqh_last = &(&refs)->tqh_first; } while (0); | |||
7921 | TAILQ_INIT(&backup_refs)do { (&backup_refs)->tqh_first = ((void *)0); (&backup_refs )->tqh_last = &(&backup_refs)->tqh_first; } while (0); | |||
7922 | ||||
7923 | err = got_ref_list(&refs, repo, NULL((void *)0), got_ref_cmp_by_name, NULL((void *)0)); | |||
7924 | if (err) | |||
7925 | return err; | |||
7926 | ||||
7927 | err = got_reflist_object_id_map_create(&refs_idmap, &refs, repo); | |||
7928 | if (err) | |||
7929 | goto done; | |||
7930 | ||||
7931 | if (wanted_branch_name) { | |||
7932 | if (strncmp(wanted_branch_name, "refs/heads/", 11) == 0) | |||
7933 | wanted_branch_name += 11; | |||
7934 | } | |||
7935 | ||||
7936 | err = got_ref_list(&backup_refs, repo, backup_ref_prefix, | |||
7937 | got_ref_cmp_by_commit_timestamp_descending, repo); | |||
7938 | if (err) | |||
7939 | goto done; | |||
7940 | ||||
7941 | TAILQ_FOREACH(re, &backup_refs, entry)for((re) = ((&backup_refs)->tqh_first); (re) != ((void *)0); (re) = ((re)->entry.tqe_next)) { | |||
7942 | const char *refname = got_ref_get_name(re->ref); | |||
7943 | char *slash; | |||
7944 | ||||
7945 | err = got_ref_resolve(&old_commit_id, repo, re->ref); | |||
7946 | if (err) | |||
7947 | break; | |||
7948 | ||||
7949 | err = got_object_open_as_commit(&old_commit, repo, | |||
7950 | old_commit_id); | |||
7951 | if (err) | |||
7952 | break; | |||
7953 | ||||
7954 | if (strncmp(backup_ref_prefix, refname, | |||
7955 | backup_ref_prefix_len) == 0) | |||
7956 | refname += backup_ref_prefix_len; | |||
7957 | ||||
7958 | while (refname[0] == '/') | |||
7959 | refname++; | |||
7960 | ||||
7961 | branch_name = strdup(refname); | |||
7962 | if (branch_name == NULL((void *)0)) { | |||
7963 | err = got_error_from_errno("strdup"); | |||
7964 | break; | |||
7965 | } | |||
7966 | slash = strrchr(branch_name, '/'); | |||
7967 | if (slash) { | |||
7968 | *slash = '\0'; | |||
7969 | refname += strlen(branch_name) + 1; | |||
7970 | } | |||
7971 | ||||
7972 | if (wanted_branch_name == NULL((void *)0) || | |||
7973 | strcmp(wanted_branch_name, branch_name) == 0) { | |||
7974 | wanted_branch_found = 1; | |||
7975 | err = print_backup_ref(branch_name, refname, | |||
7976 | old_commit_id, old_commit, refs_idmap, repo); | |||
7977 | if (err) | |||
7978 | break; | |||
7979 | } | |||
7980 | ||||
7981 | free(old_commit_id); | |||
7982 | old_commit_id = NULL((void *)0); | |||
7983 | free(branch_name); | |||
7984 | branch_name = NULL((void *)0); | |||
7985 | got_object_commit_close(old_commit); | |||
7986 | old_commit = NULL((void *)0); | |||
7987 | } | |||
7988 | ||||
7989 | if (wanted_branch_name && !wanted_branch_found) { | |||
7990 | err = got_error_fmt(GOT_ERR_NOT_REF5, | |||
7991 | "%s/%s/", backup_ref_prefix, wanted_branch_name); | |||
7992 | } | |||
7993 | done: | |||
7994 | if (refs_idmap) | |||
7995 | got_reflist_object_id_map_free(refs_idmap); | |||
7996 | got_ref_list_free(&refs); | |||
7997 | got_ref_list_free(&backup_refs); | |||
7998 | free(old_commit_id); | |||
7999 | free(branch_name); | |||
8000 | if (old_commit) | |||
8001 | got_object_commit_close(old_commit); | |||
8002 | return err; | |||
8003 | } | |||
8004 | ||||
8005 | static const struct got_error * | |||
8006 | cmd_rebase(int argc, char *argv[]) | |||
8007 | { | |||
8008 | const struct got_error *error = NULL((void *)0); | |||
8009 | struct got_worktree *worktree = NULL((void *)0); | |||
8010 | struct got_repository *repo = NULL((void *)0); | |||
8011 | struct got_fileindex *fileindex = NULL((void *)0); | |||
8012 | char *cwd = NULL((void *)0); | |||
8013 | struct got_reference *branch = NULL((void *)0); | |||
8014 | struct got_reference *new_base_branch = NULL((void *)0), *tmp_branch = NULL((void *)0); | |||
8015 | struct got_object_id *commit_id = NULL((void *)0), *parent_id = NULL((void *)0); | |||
8016 | struct got_object_id *resume_commit_id = NULL((void *)0); | |||
8017 | struct got_object_id *branch_head_commit_id = NULL((void *)0), *yca_id = NULL((void *)0); | |||
8018 | struct got_commit_object *commit = NULL((void *)0); | |||
8019 | int ch, rebase_in_progress = 0, abort_rebase = 0, continue_rebase = 0; | |||
8020 | int histedit_in_progress = 0, create_backup = 1, list_backups = 0; | |||
8021 | unsigned char rebase_status = GOT_STATUS_NO_CHANGE' '; | |||
8022 | struct got_object_id_queue commits; | |||
8023 | struct got_pathlist_head merged_paths; | |||
8024 | const struct got_object_id_queue *parent_ids; | |||
8025 | struct got_object_qid *qid, *pid; | |||
8026 | ||||
8027 | SIMPLEQ_INIT(&commits)do { (&commits)->sqh_first = ((void *)0); (&commits )->sqh_last = &(&commits)->sqh_first; } while ( 0); | |||
8028 | TAILQ_INIT(&merged_paths)do { (&merged_paths)->tqh_first = ((void *)0); (&merged_paths )->tqh_last = &(&merged_paths)->tqh_first; } while (0); | |||
8029 | ||||
8030 | while ((ch = getopt(argc, argv, "acl")) != -1) { | |||
8031 | switch (ch) { | |||
8032 | case 'a': | |||
8033 | abort_rebase = 1; | |||
8034 | break; | |||
8035 | case 'c': | |||
8036 | continue_rebase = 1; | |||
8037 | break; | |||
8038 | case 'l': | |||
8039 | list_backups = 1; | |||
8040 | break; | |||
8041 | default: | |||
8042 | usage_rebase(); | |||
8043 | /* NOTREACHED */ | |||
8044 | } | |||
8045 | } | |||
8046 | ||||
8047 | argc -= optind; | |||
8048 | argv += optind; | |||
8049 | ||||
8050 | #ifndef PROFILE | |||
8051 | if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd " | |||
8052 | "unveil", NULL((void *)0)) == -1) | |||
8053 | err(1, "pledge"); | |||
8054 | #endif | |||
8055 | if (list_backups) { | |||
8056 | if (abort_rebase) | |||
8057 | option_conflict('l', 'a'); | |||
8058 | if (continue_rebase) | |||
8059 | option_conflict('l', 'c'); | |||
8060 | if (argc != 0 && argc != 1) | |||
8061 | usage_rebase(); | |||
8062 | } else { | |||
8063 | if (abort_rebase && continue_rebase) | |||
8064 | usage_rebase(); | |||
8065 | else if (abort_rebase || continue_rebase) { | |||
8066 | if (argc != 0) | |||
8067 | usage_rebase(); | |||
8068 | } else if (argc != 1) | |||
8069 | usage_rebase(); | |||
8070 | } | |||
8071 | ||||
8072 | cwd = getcwd(NULL((void *)0), 0); | |||
8073 | if (cwd == NULL((void *)0)) { | |||
8074 | error = got_error_from_errno("getcwd"); | |||
8075 | goto done; | |||
8076 | } | |||
8077 | error = got_worktree_open(&worktree, cwd); | |||
8078 | if (error) { | |||
8079 | if (list_backups) { | |||
8080 | if (error->code != GOT_ERR_NOT_WORKTREE60) | |||
8081 | goto done; | |||
8082 | } else { | |||
8083 | if (error->code == GOT_ERR_NOT_WORKTREE60) | |||
8084 | error = wrap_not_worktree_error(error, | |||
8085 | "rebase", cwd); | |||
8086 | goto done; | |||
8087 | } | |||
8088 | } | |||
8089 | ||||
8090 | error = got_repo_open(&repo, | |||
8091 | worktree ? got_worktree_get_repo_path(worktree) : cwd, NULL((void *)0)); | |||
8092 | if (error != NULL((void *)0)) | |||
8093 | goto done; | |||
8094 | ||||
8095 | error = apply_unveil(got_repo_get_path(repo), 0, | |||
8096 | worktree ? got_worktree_get_root_path(worktree) : NULL((void *)0)); | |||
8097 | if (error) | |||
8098 | goto done; | |||
8099 | ||||
8100 | if (list_backups) { | |||
8101 | error = list_backup_refs(GOT_WORKTREE_REBASE_BACKUP_REF_PREFIX"refs/got/backup/rebase", | |||
8102 | argc == 1 ? argv[0] : NULL((void *)0), repo); | |||
8103 | goto done; /* nothing else to do */ | |||
8104 | } | |||
8105 | ||||
8106 | error = got_worktree_histedit_in_progress(&histedit_in_progress, | |||
8107 | worktree); | |||
8108 | if (error) | |||
8109 | goto done; | |||
8110 | if (histedit_in_progress) { | |||
8111 | error = got_error(GOT_ERR_HISTEDIT_BUSY96); | |||
8112 | goto done; | |||
8113 | } | |||
8114 | ||||
8115 | error = got_worktree_rebase_in_progress(&rebase_in_progress, worktree); | |||
8116 | if (error) | |||
8117 | goto done; | |||
8118 | ||||
8119 | if (abort_rebase) { | |||
8120 | struct got_update_progress_arg upa; | |||
8121 | if (!rebase_in_progress) { | |||
8122 | error = got_error(GOT_ERR_NOT_REBASING85); | |||
8123 | goto done; | |||
8124 | } | |||
8125 | error = got_worktree_rebase_continue(&resume_commit_id, | |||
8126 | &new_base_branch, &tmp_branch, &branch, &fileindex, | |||
8127 | worktree, repo); | |||
8128 | if (error) | |||
8129 | goto done; | |||
8130 | printf("Switching work tree to %s\n", | |||
8131 | got_ref_get_symref_target(new_base_branch)); | |||
8132 | memset(&upa, 0, sizeof(upa)); | |||
8133 | error = got_worktree_rebase_abort(worktree, fileindex, repo, | |||
8134 | new_base_branch, update_progress, &upa); | |||
8135 | if (error) | |||
8136 | goto done; | |||
8137 | printf("Rebase of %s aborted\n", got_ref_get_name(branch)); | |||
8138 | print_update_progress_stats(&upa); | |||
8139 | goto done; /* nothing else to do */ | |||
8140 | } | |||
8141 | ||||
8142 | if (continue_rebase) { | |||
8143 | if (!rebase_in_progress) { | |||
8144 | error = got_error(GOT_ERR_NOT_REBASING85); | |||
8145 | goto done; | |||
8146 | } | |||
8147 | error = got_worktree_rebase_continue(&resume_commit_id, | |||
8148 | &new_base_branch, &tmp_branch, &branch, &fileindex, | |||
8149 | worktree, repo); | |||
8150 | if (error) | |||
8151 | goto done; | |||
8152 | ||||
8153 | error = rebase_commit(NULL((void *)0), worktree, fileindex, tmp_branch, | |||
8154 | resume_commit_id, repo); | |||
8155 | if (error) | |||
8156 | goto done; | |||
8157 | ||||
8158 | yca_id = got_object_id_dup(resume_commit_id); | |||
8159 | if (yca_id == NULL((void *)0)) { | |||
8160 | error = got_error_from_errno("got_object_id_dup"); | |||
8161 | goto done; | |||
8162 | } | |||
8163 | } else { | |||
8164 | error = got_ref_open(&branch, repo, argv[0], 0); | |||
8165 | if (error != NULL((void *)0)) | |||
8166 | goto done; | |||
8167 | } | |||
8168 | ||||
8169 | error = got_ref_resolve(&branch_head_commit_id, repo, branch); | |||
8170 | if (error) | |||
8171 | goto done; | |||
8172 | ||||
8173 | if (!continue_rebase) { | |||
8174 | struct got_object_id *base_commit_id; | |||
8175 | ||||
8176 | base_commit_id = got_worktree_get_base_commit_id(worktree); | |||
8177 | error = got_commit_graph_find_youngest_common_ancestor(&yca_id, | |||
8178 | base_commit_id, branch_head_commit_id, repo, | |||
8179 | check_cancelled, NULL((void *)0)); | |||
8180 | if (error) | |||
8181 | goto done; | |||
8182 | if (yca_id == NULL((void *)0)) { | |||
8183 | error = got_error_msg(GOT_ERR_ANCESTRY55, | |||
8184 | "specified branch shares no common ancestry " | |||
8185 | "with work tree's branch"); | |||
8186 | goto done; | |||
8187 | } | |||
8188 | ||||
8189 | error = check_same_branch(base_commit_id, branch, yca_id, repo); | |||
8190 | if (error) { | |||
8191 | if (error->code != GOT_ERR_ANCESTRY55) | |||
8192 | goto done; | |||
8193 | error = NULL((void *)0); | |||
8194 | } else { | |||
8195 | static char msg[128]; | |||
8196 | snprintf(msg, sizeof(msg), | |||
8197 | "%s is already based on %s", | |||
8198 | got_ref_get_name(branch), | |||
8199 | got_worktree_get_head_ref_name(worktree)); | |||
8200 | error = got_error_msg(GOT_ERR_SAME_BRANCH79, msg); | |||
8201 | goto done; | |||
8202 | } | |||
8203 | error = got_worktree_rebase_prepare(&new_base_branch, | |||
8204 | &tmp_branch, &fileindex, worktree, branch, repo); | |||
8205 | if (error) | |||
8206 | goto done; | |||
8207 | } | |||
8208 | ||||
8209 | commit_id = branch_head_commit_id; | |||
8210 | error = got_object_open_as_commit(&commit, repo, commit_id); | |||
8211 | if (error) | |||
8212 | goto done; | |||
8213 | ||||
8214 | parent_ids = got_object_commit_get_parent_ids(commit); | |||
8215 | pid = SIMPLEQ_FIRST(parent_ids)((parent_ids)->sqh_first); | |||
8216 | if (pid == NULL((void *)0)) { | |||
8217 | if (!continue_rebase) { | |||
8218 | struct got_update_progress_arg upa; | |||
8219 | memset(&upa, 0, sizeof(upa)); | |||
8220 | error = got_worktree_rebase_abort(worktree, fileindex, | |||
8221 | repo, new_base_branch, update_progress, &upa); | |||
8222 | if (error) | |||
8223 | goto done; | |||
8224 | printf("Rebase of %s aborted\n", | |||
8225 | got_ref_get_name(branch)); | |||
8226 | print_update_progress_stats(&upa); | |||
8227 | ||||
8228 | } | |||
8229 | error = got_error(GOT_ERR_EMPTY_REBASE86); | |||
8230 | goto done; | |||
8231 | } | |||
8232 | error = collect_commits(&commits, commit_id, pid->id, | |||
8233 | yca_id, got_worktree_get_path_prefix(worktree), | |||
8234 | GOT_ERR_REBASE_PATH89, repo); | |||
8235 | got_object_commit_close(commit); | |||
8236 | commit = NULL((void *)0); | |||
8237 | if (error) | |||
8238 | goto done; | |||
8239 | ||||
8240 | if (SIMPLEQ_EMPTY(&commits)(((&commits)->sqh_first) == ((void *)0))) { | |||
8241 | if (continue_rebase) { | |||
8242 | error = rebase_complete(worktree, fileindex, | |||
8243 | branch, new_base_branch, tmp_branch, repo, | |||
8244 | create_backup); | |||
8245 | goto done; | |||
8246 | } else { | |||
8247 | /* Fast-forward the reference of the branch. */ | |||
8248 | struct got_object_id *new_head_commit_id; | |||
8249 | char *id_str; | |||
8250 | error = got_ref_resolve(&new_head_commit_id, repo, | |||
8251 | new_base_branch); | |||
8252 | if (error) | |||
8253 | goto done; | |||
8254 | error = got_object_id_str(&id_str, new_head_commit_id); | |||
8255 | printf("Forwarding %s to commit %s\n", | |||
8256 | got_ref_get_name(branch), id_str); | |||
8257 | free(id_str); | |||
8258 | error = got_ref_change_ref(branch, | |||
8259 | new_head_commit_id); | |||
8260 | if (error) | |||
8261 | goto done; | |||
8262 | /* No backup needed since objects did not change. */ | |||
8263 | create_backup = 0; | |||
8264 | } | |||
8265 | } | |||
8266 | ||||
8267 | pid = NULL((void *)0); | |||
8268 | SIMPLEQ_FOREACH(qid, &commits, entry)for((qid) = ((&commits)->sqh_first); (qid) != ((void * )0); (qid) = ((qid)->entry.sqe_next)) { | |||
8269 | struct got_update_progress_arg upa; | |||
8270 | ||||
8271 | commit_id = qid->id; | |||
8272 | parent_id = pid ? pid->id : yca_id; | |||
8273 | pid = qid; | |||
8274 | ||||
8275 | memset(&upa, 0, sizeof(upa)); | |||
8276 | error = got_worktree_rebase_merge_files(&merged_paths, | |||
8277 | worktree, fileindex, parent_id, commit_id, repo, | |||
8278 | update_progress, &upa, check_cancelled, NULL((void *)0)); | |||
8279 | if (error) | |||
8280 | goto done; | |||
8281 | ||||
8282 | print_update_progress_stats(&upa); | |||
8283 | if (upa.conflicts > 0) | |||
8284 | rebase_status = GOT_STATUS_CONFLICT'C'; | |||
8285 | ||||
8286 | if (rebase_status == GOT_STATUS_CONFLICT'C') { | |||
8287 | error = show_rebase_merge_conflict(qid->id, repo); | |||
8288 | if (error) | |||
8289 | goto done; | |||
8290 | got_worktree_rebase_pathlist_free(&merged_paths); | |||
8291 | break; | |||
8292 | } | |||
8293 | ||||
8294 | error = rebase_commit(&merged_paths, worktree, fileindex, | |||
8295 | tmp_branch, commit_id, repo); | |||
8296 | got_worktree_rebase_pathlist_free(&merged_paths); | |||
8297 | if (error) | |||
8298 | goto done; | |||
8299 | } | |||
8300 | ||||
8301 | if (rebase_status == GOT_STATUS_CONFLICT'C') { | |||
8302 | error = got_worktree_rebase_postpone(worktree, fileindex); | |||
8303 | if (error) | |||
8304 | goto done; | |||
8305 | error = got_error_msg(GOT_ERR_CONFLICTS82, | |||
8306 | "conflicts must be resolved before rebasing can continue"); | |||
8307 | } else | |||
8308 | error = rebase_complete(worktree, fileindex, branch, | |||
8309 | new_base_branch, tmp_branch, repo, create_backup); | |||
8310 | done: | |||
8311 | got_object_id_queue_free(&commits); | |||
8312 | free(branch_head_commit_id); | |||
8313 | free(resume_commit_id); | |||
8314 | free(yca_id); | |||
8315 | if (commit) | |||
8316 | got_object_commit_close(commit); | |||
8317 | if (branch) | |||
8318 | got_ref_close(branch); | |||
8319 | if (new_base_branch) | |||
8320 | got_ref_close(new_base_branch); | |||
8321 | if (tmp_branch) | |||
8322 | got_ref_close(tmp_branch); | |||
8323 | if (worktree) | |||
8324 | got_worktree_close(worktree); | |||
8325 | if (repo) | |||
8326 | got_repo_close(repo); | |||
8327 | return error; | |||
8328 | } | |||
8329 | ||||
8330 | __dead__attribute__((__noreturn__)) static void | |||
8331 | usage_histedit(void) | |||
8332 | { | |||
8333 | fprintf(stderr(&__sF[2]), "usage: %s histedit [-a] [-c] [-f] " | |||
8334 | "[-F histedit-script] [-m] [-l] [branch]\n", getprogname()); | |||
8335 | exit(1); | |||
8336 | } | |||
8337 | ||||
8338 | #define GOT_HISTEDIT_PICK'p' 'p' | |||
8339 | #define GOT_HISTEDIT_EDIT'e' 'e' | |||
8340 | #define GOT_HISTEDIT_FOLD'f' 'f' | |||
8341 | #define GOT_HISTEDIT_DROP'd' 'd' | |||
8342 | #define GOT_HISTEDIT_MESG'm' 'm' | |||
8343 | ||||
8344 | static struct got_histedit_cmd { | |||
8345 | unsigned char code; | |||
8346 | const char *name; | |||
8347 | const char *desc; | |||
8348 | } got_histedit_cmds[] = { | |||
8349 | { GOT_HISTEDIT_PICK'p', "pick", "use commit" }, | |||
8350 | { GOT_HISTEDIT_EDIT'e', "edit", "use commit but stop for amending" }, | |||
8351 | { GOT_HISTEDIT_FOLD'f', "fold", "combine with next commit that will " | |||
8352 | "be used" }, | |||
8353 | { GOT_HISTEDIT_DROP'd', "drop", "remove commit from history" }, | |||
8354 | { GOT_HISTEDIT_MESG'm', "mesg", | |||
8355 | "single-line log message for commit above (open editor if empty)" }, | |||
8356 | }; | |||
8357 | ||||
8358 | struct got_histedit_list_entry { | |||
8359 | TAILQ_ENTRY(got_histedit_list_entry)struct { struct got_histedit_list_entry *tqe_next; struct got_histedit_list_entry **tqe_prev; } entry; | |||
8360 | struct got_object_id *commit_id; | |||
8361 | const struct got_histedit_cmd *cmd; | |||
8362 | char *logmsg; | |||
8363 | }; | |||
8364 | TAILQ_HEAD(got_histedit_list, got_histedit_list_entry)struct got_histedit_list { struct got_histedit_list_entry *tqh_first ; struct got_histedit_list_entry **tqh_last; }; | |||
8365 | ||||
8366 | static const struct got_error * | |||
8367 | histedit_write_commit(struct got_object_id *commit_id, const char *cmdname, | |||
8368 | FILE *f, struct got_repository *repo) | |||
8369 | { | |||
8370 | const struct got_error *err = NULL((void *)0); | |||
8371 | char *logmsg = NULL((void *)0), *id_str = NULL((void *)0); | |||
8372 | struct got_commit_object *commit = NULL((void *)0); | |||
8373 | int n; | |||
8374 | ||||
8375 | err = got_object_open_as_commit(&commit, repo, commit_id); | |||
8376 | if (err) | |||
8377 | goto done; | |||
8378 | ||||
8379 | err = get_short_logmsg(&logmsg, 34, commit); | |||
8380 | if (err) | |||
8381 | goto done; | |||
8382 | ||||
8383 | err = got_object_id_str(&id_str, commit_id); | |||
8384 | if (err) | |||
8385 | goto done; | |||
8386 | ||||
8387 | n = fprintf(f, "%s %s %s\n", cmdname, id_str, logmsg); | |||
8388 | if (n < 0) | |||
8389 | err = got_ferror(f, GOT_ERR_IO6); | |||
8390 | done: | |||
8391 | if (commit) | |||
8392 | got_object_commit_close(commit); | |||
8393 | free(id_str); | |||
8394 | free(logmsg); | |||
8395 | return err; | |||
8396 | } | |||
8397 | ||||
8398 | static const struct got_error * | |||
8399 | histedit_write_commit_list(struct got_object_id_queue *commits, | |||
8400 | FILE *f, int edit_logmsg_only, int fold_only, struct got_repository *repo) | |||
8401 | { | |||
8402 | const struct got_error *err = NULL((void *)0); | |||
8403 | struct got_object_qid *qid; | |||
8404 | const char *histedit_cmd = NULL((void *)0); | |||
8405 | ||||
8406 | if (SIMPLEQ_EMPTY(commits)(((commits)->sqh_first) == ((void *)0))) | |||
8407 | return got_error(GOT_ERR_EMPTY_HISTEDIT91); | |||
8408 | ||||
8409 | SIMPLEQ_FOREACH(qid, commits, entry)for((qid) = ((commits)->sqh_first); (qid) != ((void *)0); ( qid) = ((qid)->entry.sqe_next)) { | |||
8410 | histedit_cmd = got_histedit_cmds[0].name; | |||
8411 | if (fold_only && SIMPLEQ_NEXT(qid, entry)((qid)->entry.sqe_next) != NULL((void *)0)) | |||
8412 | histedit_cmd = "fold"; | |||
8413 | err = histedit_write_commit(qid->id, histedit_cmd, f, repo); | |||
8414 | if (err) | |||
8415 | break; | |||
8416 | if (edit_logmsg_only) { | |||
8417 | int n = fprintf(f, "%c\n", GOT_HISTEDIT_MESG'm'); | |||
8418 | if (n < 0) { | |||
8419 | err = got_ferror(f, GOT_ERR_IO6); | |||
8420 | break; | |||
8421 | } | |||
8422 | } | |||
8423 | } | |||
8424 | ||||
8425 | return err; | |||
8426 | } | |||
8427 | ||||
8428 | static const struct got_error * | |||
8429 | write_cmd_list(FILE *f, const char *branch_name, | |||
8430 | struct got_object_id_queue *commits) | |||
8431 | { | |||
8432 | const struct got_error *err = NULL((void *)0); | |||
8433 | size_t i; | |||
8434 | int n; | |||
8435 | char *id_str; | |||
8436 | struct got_object_qid *qid; | |||
8437 | ||||
8438 | qid = SIMPLEQ_FIRST(commits)((commits)->sqh_first); | |||
8439 | err = got_object_id_str(&id_str, qid->id); | |||
8440 | if (err) | |||
8441 | return err; | |||
8442 | ||||
8443 | n = fprintf(f, | |||
8444 | "# Editing the history of branch '%s' starting at\n" | |||
8445 | "# commit %s\n" | |||
8446 | "# Commits will be processed in order from top to " | |||
8447 | "bottom of this file.\n", branch_name, id_str); | |||
8448 | if (n < 0) { | |||
8449 | err = got_ferror(f, GOT_ERR_IO6); | |||
8450 | goto done; | |||
8451 | } | |||
8452 | ||||
8453 | n = fprintf(f, "# Available histedit commands:\n"); | |||
8454 | if (n < 0) { | |||
8455 | err = got_ferror(f, GOT_ERR_IO6); | |||
8456 | goto done; | |||
8457 | } | |||
8458 | ||||
8459 | for (i = 0; i < nitems(got_histedit_cmds)(sizeof((got_histedit_cmds)) / sizeof((got_histedit_cmds)[0]) ); i++) { | |||
8460 | struct got_histedit_cmd *cmd = &got_histedit_cmds[i]; | |||
8461 | n = fprintf(f, "# %s (%c): %s\n", cmd->name, cmd->code, | |||
8462 | cmd->desc); | |||
8463 | if (n < 0) { | |||
8464 | err = got_ferror(f, GOT_ERR_IO6); | |||
8465 | break; | |||
8466 | } | |||
8467 | } | |||
8468 | done: | |||
8469 | free(id_str); | |||
8470 | return err; | |||
8471 | } | |||
8472 | ||||
8473 | static const struct got_error * | |||
8474 | histedit_syntax_error(int lineno) | |||
8475 | { | |||
8476 | static char msg[42]; | |||
8477 | int ret; | |||
8478 | ||||
8479 | ret = snprintf(msg, sizeof(msg), "histedit syntax error on line %d", | |||
8480 | lineno); | |||
8481 | if (ret == -1 || ret >= sizeof(msg)) | |||
8482 | return got_error(GOT_ERR_HISTEDIT_SYNTAX93); | |||
8483 | ||||
8484 | return got_error_msg(GOT_ERR_HISTEDIT_SYNTAX93, msg); | |||
8485 | } | |||
8486 | ||||
8487 | static const struct got_error * | |||
8488 | append_folded_commit_msg(char **new_msg, struct got_histedit_list_entry *hle, | |||
8489 | char *logmsg, struct got_repository *repo) | |||
8490 | { | |||
8491 | const struct got_error *err; | |||
8492 | struct got_commit_object *folded_commit = NULL((void *)0); | |||
8493 | char *id_str, *folded_logmsg = NULL((void *)0); | |||
8494 | ||||
8495 | err = got_object_id_str(&id_str, hle->commit_id); | |||
8496 | if (err) | |||
8497 | return err; | |||
8498 | ||||
8499 | err = got_object_open_as_commit(&folded_commit, repo, hle->commit_id); | |||
8500 | if (err) | |||
8501 | goto done; | |||
8502 | ||||
8503 | err = got_object_commit_get_logmsg(&folded_logmsg, folded_commit); | |||
8504 | if (err) | |||
8505 | goto done; | |||
8506 | if (asprintf(new_msg, "%s%s# log message of folded commit %s: %s", | |||
8507 | logmsg ? logmsg : "", logmsg ? "\n" : "", id_str, | |||
8508 | folded_logmsg) == -1) { | |||
8509 | err = got_error_from_errno("asprintf"); | |||
8510 | } | |||
8511 | done: | |||
8512 | if (folded_commit) | |||
8513 | got_object_commit_close(folded_commit); | |||
8514 | free(id_str); | |||
8515 | free(folded_logmsg); | |||
8516 | return err; | |||
8517 | } | |||
8518 | ||||
8519 | static struct got_histedit_list_entry * | |||
8520 | get_folded_commits(struct got_histedit_list_entry *hle) | |||
8521 | { | |||
8522 | struct got_histedit_list_entry *prev, *folded = NULL((void *)0); | |||
8523 | ||||
8524 | prev = TAILQ_PREV(hle, got_histedit_list, entry)(*(((struct got_histedit_list *)((hle)->entry.tqe_prev))-> tqh_last)); | |||
8525 | while (prev && (prev->cmd->code == GOT_HISTEDIT_FOLD'f' || | |||
8526 | prev->cmd->code == GOT_HISTEDIT_DROP'd')) { | |||
8527 | if (prev->cmd->code == GOT_HISTEDIT_FOLD'f') | |||
8528 | folded = prev; | |||
8529 | prev = TAILQ_PREV(prev, got_histedit_list, entry)(*(((struct got_histedit_list *)((prev)->entry.tqe_prev))-> tqh_last)); | |||
8530 | } | |||
8531 | ||||
8532 | return folded; | |||
8533 | } | |||
8534 | ||||
8535 | static const struct got_error * | |||
8536 | histedit_edit_logmsg(struct got_histedit_list_entry *hle, | |||
8537 | struct got_repository *repo) | |||
8538 | { | |||
8539 | char *logmsg_path = NULL((void *)0), *id_str = NULL((void *)0), *orig_logmsg = NULL((void *)0); | |||
8540 | char *logmsg = NULL((void *)0), *new_msg = NULL((void *)0), *editor = NULL((void *)0); | |||
8541 | const struct got_error *err = NULL((void *)0); | |||
8542 | struct got_commit_object *commit = NULL((void *)0); | |||
8543 | int logmsg_len; | |||
8544 | int fd; | |||
8545 | struct got_histedit_list_entry *folded = NULL((void *)0); | |||
8546 | ||||
8547 | err = got_object_open_as_commit(&commit, repo, hle->commit_id); | |||
8548 | if (err) | |||
8549 | return err; | |||
8550 | ||||
8551 | folded = get_folded_commits(hle); | |||
8552 | if (folded) { | |||
8553 | while (folded != hle) { | |||
8554 | if (folded->cmd->code == GOT_HISTEDIT_DROP'd') { | |||
8555 | folded = TAILQ_NEXT(folded, entry)((folded)->entry.tqe_next); | |||
8556 | continue; | |||
8557 | } | |||
8558 | err = append_folded_commit_msg(&new_msg, folded, | |||
8559 | logmsg, repo); | |||
8560 | if (err) | |||
8561 | goto done; | |||
8562 | free(logmsg); | |||
8563 | logmsg = new_msg; | |||
8564 | folded = TAILQ_NEXT(folded, entry)((folded)->entry.tqe_next); | |||
8565 | } | |||
8566 | } | |||
8567 | ||||
8568 | err = got_object_id_str(&id_str, hle->commit_id); | |||
8569 | if (err) | |||
8570 | goto done; | |||
8571 | err = got_object_commit_get_logmsg(&orig_logmsg, commit); | |||
8572 | if (err) | |||
8573 | goto done; | |||
8574 | logmsg_len = asprintf(&new_msg, | |||
8575 | "%s\n# original log message of commit %s: %s", | |||
8576 | logmsg ? logmsg : "", id_str, orig_logmsg); | |||
8577 | if (logmsg_len == -1) { | |||
8578 | err = got_error_from_errno("asprintf"); | |||
8579 | goto done; | |||
8580 | } | |||
8581 | free(logmsg); | |||
8582 | logmsg = new_msg; | |||
8583 | ||||
8584 | err = got_object_id_str(&id_str, hle->commit_id); | |||
8585 | if (err) | |||
8586 | goto done; | |||
8587 | ||||
8588 | err = got_opentemp_named_fd(&logmsg_path, &fd, | |||
8589 | GOT_TMPDIR_STR"/tmp" "/got-logmsg"); | |||
8590 | if (err) | |||
8591 | goto done; | |||
8592 | ||||
8593 | write(fd, logmsg, logmsg_len); | |||
8594 | close(fd); | |||
8595 | ||||
8596 | err = get_editor(&editor); | |||
8597 | if (err) | |||
8598 | goto done; | |||
8599 | ||||
8600 | err = edit_logmsg(&hle->logmsg, editor, logmsg_path, logmsg, | |||
8601 | logmsg_len, 0); | |||
8602 | if (err) { | |||
8603 | if (err->code != GOT_ERR_COMMIT_MSG_EMPTY74) | |||
8604 | goto done; | |||
8605 | err = NULL((void *)0); | |||
8606 | hle->logmsg = strdup(new_msg); | |||
8607 | if (hle->logmsg == NULL((void *)0)) | |||
8608 | err = got_error_from_errno("strdup"); | |||
8609 | } | |||
8610 | done: | |||
8611 | if (logmsg_path && unlink(logmsg_path) != 0 && err == NULL((void *)0)) | |||
8612 | err = got_error_from_errno2("unlink", logmsg_path); | |||
8613 | free(logmsg_path); | |||
8614 | free(logmsg); | |||
8615 | free(orig_logmsg); | |||
8616 | free(editor); | |||
8617 | if (commit) | |||
8618 | got_object_commit_close(commit); | |||
8619 | return err; | |||
8620 | } | |||
8621 | ||||
8622 | static const struct got_error * | |||
8623 | histedit_parse_list(struct got_histedit_list *histedit_cmds, | |||
8624 | FILE *f, struct got_repository *repo) | |||
8625 | { | |||
8626 | const struct got_error *err = NULL((void *)0); | |||
8627 | char *line = NULL((void *)0), *p, *end; | |||
8628 | size_t i, size; | |||
8629 | ssize_t len; | |||
8630 | int lineno = 0; | |||
8631 | const struct got_histedit_cmd *cmd; | |||
8632 | struct got_object_id *commit_id = NULL((void *)0); | |||
8633 | struct got_histedit_list_entry *hle = NULL((void *)0); | |||
8634 | ||||
8635 | for (;;) { | |||
8636 | len = getline(&line, &size, f); | |||
8637 | if (len == -1) { | |||
8638 | const struct got_error *getline_err; | |||
8639 | if (feof(f)(!__isthreaded ? (((f)->_flags & 0x0020) != 0) : (feof )(f))) | |||
8640 | break; | |||
8641 | getline_err = got_error_from_errno("getline"); | |||
8642 | err = got_ferror(f, getline_err->code); | |||
8643 | break; | |||
8644 | } | |||
8645 | lineno++; | |||
8646 | p = line; | |||
8647 | while (isspace((unsigned char)p[0])) | |||
8648 | p++; | |||
8649 | if (p[0] == '#' || p[0] == '\0') { | |||
8650 | free(line); | |||
8651 | line = NULL((void *)0); | |||
8652 | continue; | |||
8653 | } | |||
8654 | cmd = NULL((void *)0); | |||
8655 | for (i = 0; i < nitems(got_histedit_cmds)(sizeof((got_histedit_cmds)) / sizeof((got_histedit_cmds)[0]) ); i++) { | |||
8656 | cmd = &got_histedit_cmds[i]; | |||
8657 | if (strncmp(cmd->name, p, strlen(cmd->name)) == 0 && | |||
8658 | isspace((unsigned char)p[strlen(cmd->name)])) { | |||
8659 | p += strlen(cmd->name); | |||
8660 | break; | |||
8661 | } | |||
8662 | if (p[0] == cmd->code && isspace((unsigned char)p[1])) { | |||
8663 | p++; | |||
8664 | break; | |||
8665 | } | |||
8666 | } | |||
8667 | if (i == nitems(got_histedit_cmds)(sizeof((got_histedit_cmds)) / sizeof((got_histedit_cmds)[0]) )) { | |||
8668 | err = histedit_syntax_error(lineno); | |||
8669 | break; | |||
8670 | } | |||
8671 | while (isspace((unsigned char)p[0])) | |||
8672 | p++; | |||
8673 | if (cmd->code == GOT_HISTEDIT_MESG'm') { | |||
8674 | if (hle == NULL((void *)0) || hle->logmsg != NULL((void *)0)) { | |||
8675 | err = got_error(GOT_ERR_HISTEDIT_CMD97); | |||
8676 | break; | |||
8677 | } | |||
8678 | if (p[0] == '\0') { | |||
8679 | err = histedit_edit_logmsg(hle, repo); | |||
8680 | if (err) | |||
8681 | break; | |||
8682 | } else { | |||
8683 | hle->logmsg = strdup(p); | |||
8684 | if (hle->logmsg == NULL((void *)0)) { | |||
8685 | err = got_error_from_errno("strdup"); | |||
8686 | break; | |||
8687 | } | |||
8688 | } | |||
8689 | free(line); | |||
8690 | line = NULL((void *)0); | |||
8691 | continue; | |||
8692 | } else { | |||
8693 | end = p; | |||
8694 | while (end[0] && !isspace((unsigned char)end[0])) | |||
8695 | end++; | |||
8696 | *end = '\0'; | |||
8697 | ||||
8698 | err = got_object_resolve_id_str(&commit_id, repo, p); | |||
8699 | if (err) { | |||
8700 | /* override error code */ | |||
8701 | err = histedit_syntax_error(lineno); | |||
8702 | break; | |||
8703 | } | |||
8704 | } | |||
8705 | hle = malloc(sizeof(*hle)); | |||
8706 | if (hle == NULL((void *)0)) { | |||
8707 | err = got_error_from_errno("malloc"); | |||
8708 | break; | |||
8709 | } | |||
8710 | hle->cmd = cmd; | |||
8711 | hle->commit_id = commit_id; | |||
8712 | hle->logmsg = NULL((void *)0); | |||
8713 | commit_id = NULL((void *)0); | |||
8714 | free(line); | |||
8715 | line = NULL((void *)0); | |||
8716 | TAILQ_INSERT_TAIL(histedit_cmds, hle, entry)do { (hle)->entry.tqe_next = ((void *)0); (hle)->entry. tqe_prev = (histedit_cmds)->tqh_last; *(histedit_cmds)-> tqh_last = (hle); (histedit_cmds)->tqh_last = &(hle)-> entry.tqe_next; } while (0); | |||
8717 | } | |||
8718 | ||||
8719 | free(line); | |||
8720 | free(commit_id); | |||
8721 | return err; | |||
8722 | } | |||
8723 | ||||
8724 | static const struct got_error * | |||
8725 | histedit_check_script(struct got_histedit_list *histedit_cmds, | |||
8726 | struct got_object_id_queue *commits, struct got_repository *repo) | |||
8727 | { | |||
8728 | const struct got_error *err = NULL((void *)0); | |||
8729 | struct got_object_qid *qid; | |||
8730 | struct got_histedit_list_entry *hle; | |||
8731 | static char msg[92]; | |||
8732 | char *id_str; | |||
8733 | ||||
8734 | if (TAILQ_EMPTY(histedit_cmds)(((histedit_cmds)->tqh_first) == ((void *)0))) | |||
8735 | return got_error_msg(GOT_ERR_EMPTY_HISTEDIT91, | |||
8736 | "histedit script contains no commands"); | |||
8737 | if (SIMPLEQ_EMPTY(commits)(((commits)->sqh_first) == ((void *)0))) | |||
8738 | return got_error(GOT_ERR_EMPTY_HISTEDIT91); | |||
8739 | ||||
8740 | TAILQ_FOREACH(hle, histedit_cmds, entry)for((hle) = ((histedit_cmds)->tqh_first); (hle) != ((void * )0); (hle) = ((hle)->entry.tqe_next)) { | |||
8741 | struct got_histedit_list_entry *hle2; | |||
8742 | TAILQ_FOREACH(hle2, histedit_cmds, entry)for((hle2) = ((histedit_cmds)->tqh_first); (hle2) != ((void *)0); (hle2) = ((hle2)->entry.tqe_next)) { | |||
8743 | if (hle == hle2) | |||
8744 | continue; | |||
8745 | if (got_object_id_cmp(hle->commit_id, | |||
8746 | hle2->commit_id) != 0) | |||
8747 | continue; | |||
8748 | err = got_object_id_str(&id_str, hle->commit_id); | |||
8749 | if (err) | |||
8750 | return err; | |||
8751 | snprintf(msg, sizeof(msg), "commit %s is listed " | |||
8752 | "more than once in histedit script", id_str); | |||
8753 | free(id_str); | |||
8754 | return got_error_msg(GOT_ERR_HISTEDIT_CMD97, msg); | |||
8755 | } | |||
8756 | } | |||
8757 | ||||
8758 | SIMPLEQ_FOREACH(qid, commits, entry)for((qid) = ((commits)->sqh_first); (qid) != ((void *)0); ( qid) = ((qid)->entry.sqe_next)) { | |||
8759 | TAILQ_FOREACH(hle, histedit_cmds, entry)for((hle) = ((histedit_cmds)->tqh_first); (hle) != ((void * )0); (hle) = ((hle)->entry.tqe_next)) { | |||
8760 | if (got_object_id_cmp(qid->id, hle->commit_id) == 0) | |||
8761 | break; | |||
8762 | } | |||
8763 | if (hle == NULL((void *)0)) { | |||
8764 | err = got_object_id_str(&id_str, qid->id); | |||
8765 | if (err) | |||
8766 | return err; | |||
8767 | snprintf(msg, sizeof(msg), | |||
8768 | "commit %s missing from histedit script", id_str); | |||
8769 | free(id_str); | |||
8770 | return got_error_msg(GOT_ERR_HISTEDIT_CMD97, msg); | |||
8771 | } | |||
8772 | } | |||
8773 | ||||
8774 | hle = TAILQ_LAST(histedit_cmds, got_histedit_list)(*(((struct got_histedit_list *)((histedit_cmds)->tqh_last ))->tqh_last)); | |||
8775 | if (hle && hle->cmd->code == GOT_HISTEDIT_FOLD'f') | |||
8776 | return got_error_msg(GOT_ERR_HISTEDIT_CMD97, | |||
8777 | "last commit in histedit script cannot be folded"); | |||
8778 | ||||
8779 | return NULL((void *)0); | |||
8780 | } | |||
8781 | ||||
8782 | static const struct got_error * | |||
8783 | histedit_run_editor(struct got_histedit_list *histedit_cmds, | |||
8784 | const char *path, struct got_object_id_queue *commits, | |||
8785 | struct got_repository *repo) | |||
8786 | { | |||
8787 | const struct got_error *err = NULL((void *)0); | |||
8788 | char *editor; | |||
8789 | FILE *f = NULL((void *)0); | |||
8790 | ||||
8791 | err = get_editor(&editor); | |||
8792 | if (err) | |||
8793 | return err; | |||
8794 | ||||
8795 | if (spawn_editor(editor, path) == -1) { | |||
8796 | err = got_error_from_errno("failed spawning editor"); | |||
8797 | goto done; | |||
8798 | } | |||
8799 | ||||
8800 | f = fopen(path, "r"); | |||
8801 | if (f == NULL((void *)0)) { | |||
8802 | err = got_error_from_errno("fopen"); | |||
8803 | goto done; | |||
8804 | } | |||
8805 | err = histedit_parse_list(histedit_cmds, f, repo); | |||
8806 | if (err) | |||
8807 | goto done; | |||
8808 | ||||
8809 | err = histedit_check_script(histedit_cmds, commits, repo); | |||
8810 | done: | |||
8811 | if (f && fclose(f) == EOF(-1) && err == NULL((void *)0)) | |||
8812 | err = got_error_from_errno("fclose"); | |||
8813 | free(editor); | |||
8814 | return err; | |||
8815 | } | |||
8816 | ||||
8817 | static const struct got_error * | |||
8818 | histedit_edit_list_retry(struct got_histedit_list *, const struct got_error *, | |||
8819 | struct got_object_id_queue *, const char *, const char *, | |||
8820 | struct got_repository *); | |||
8821 | ||||
8822 | static const struct got_error * | |||
8823 | histedit_edit_script(struct got_histedit_list *histedit_cmds, | |||
8824 | struct got_object_id_queue *commits, const char *branch_name, | |||
8825 | int edit_logmsg_only, int fold_only, struct got_repository *repo) | |||
8826 | { | |||
8827 | const struct got_error *err; | |||
8828 | FILE *f = NULL((void *)0); | |||
8829 | char *path = NULL((void *)0); | |||
8830 | ||||
8831 | err = got_opentemp_named(&path, &f, "got-histedit"); | |||
8832 | if (err) | |||
8833 | return err; | |||
8834 | ||||
8835 | err = write_cmd_list(f, branch_name, commits); | |||
8836 | if (err) | |||
8837 | goto done; | |||
8838 | ||||
8839 | err = histedit_write_commit_list(commits, f, edit_logmsg_only, | |||
8840 | fold_only, repo); | |||
8841 | if (err) | |||
8842 | goto done; | |||
8843 | ||||
8844 | if (edit_logmsg_only || fold_only) { | |||
8845 | rewind(f); | |||
8846 | err = histedit_parse_list(histedit_cmds, f, repo); | |||
8847 | } else { | |||
8848 | if (fclose(f) == EOF(-1)) { | |||
8849 | err = got_error_from_errno("fclose"); | |||
8850 | goto done; | |||
8851 | } | |||
8852 | f = NULL((void *)0); | |||
8853 | err = histedit_run_editor(histedit_cmds, path, commits, repo); | |||
8854 | if (err) { | |||
8855 | if (err->code != GOT_ERR_HISTEDIT_SYNTAX93 && | |||
8856 | err->code != GOT_ERR_HISTEDIT_CMD97) | |||
8857 | goto done; | |||
8858 | err = histedit_edit_list_retry(histedit_cmds, err, | |||
8859 | commits, path, branch_name, repo); | |||
8860 | } | |||
8861 | } | |||
8862 | done: | |||
8863 | if (f && fclose(f) == EOF(-1) && err == NULL((void *)0)) | |||
8864 | err = got_error_from_errno("fclose"); | |||
8865 | if (path && unlink(path) != 0 && err == NULL((void *)0)) | |||
8866 | err = got_error_from_errno2("unlink", path); | |||
8867 | free(path); | |||
8868 | return err; | |||
8869 | } | |||
8870 | ||||
8871 | static const struct got_error * | |||
8872 | histedit_save_list(struct got_histedit_list *histedit_cmds, | |||
8873 | struct got_worktree *worktree, struct got_repository *repo) | |||
8874 | { | |||
8875 | const struct got_error *err = NULL((void *)0); | |||
8876 | char *path = NULL((void *)0); | |||
8877 | FILE *f = NULL((void *)0); | |||
8878 | struct got_histedit_list_entry *hle; | |||
8879 | struct got_commit_object *commit = NULL((void *)0); | |||
8880 | ||||
8881 | err = got_worktree_get_histedit_script_path(&path, worktree); | |||
8882 | if (err) | |||
8883 | return err; | |||
8884 | ||||
8885 | f = fopen(path, "w"); | |||
8886 | if (f == NULL((void *)0)) { | |||
8887 | err = got_error_from_errno2("fopen", path); | |||
8888 | goto done; | |||
8889 | } | |||
8890 | TAILQ_FOREACH(hle, histedit_cmds, entry)for((hle) = ((histedit_cmds)->tqh_first); (hle) != ((void * )0); (hle) = ((hle)->entry.tqe_next)) { | |||
8891 | err = histedit_write_commit(hle->commit_id, hle->cmd->name, f, | |||
8892 | repo); | |||
8893 | if (err) | |||
8894 | break; | |||
8895 | ||||
8896 | if (hle->logmsg) { | |||
8897 | int n = fprintf(f, "%c %s\n", | |||
8898 | GOT_HISTEDIT_MESG'm', hle->logmsg); | |||
8899 | if (n < 0) { | |||
8900 | err = got_ferror(f, GOT_ERR_IO6); | |||
8901 | break; | |||
8902 | } | |||
8903 | } | |||
8904 | } | |||
8905 | done: | |||
8906 | if (f && fclose(f) == EOF(-1) && err == NULL((void *)0)) | |||
8907 | err = got_error_from_errno("fclose"); | |||
8908 | free(path); | |||
8909 | if (commit) | |||
8910 | got_object_commit_close(commit); | |||
8911 | return err; | |||
8912 | } | |||
8913 | ||||
8914 | void | |||
8915 | histedit_free_list(struct got_histedit_list *histedit_cmds) | |||
8916 | { | |||
8917 | struct got_histedit_list_entry *hle; | |||
8918 | ||||
8919 | while ((hle = TAILQ_FIRST(histedit_cmds)((histedit_cmds)->tqh_first))) { | |||
8920 | TAILQ_REMOVE(histedit_cmds, hle, entry)do { if (((hle)->entry.tqe_next) != ((void *)0)) (hle)-> entry.tqe_next->entry.tqe_prev = (hle)->entry.tqe_prev; else (histedit_cmds)->tqh_last = (hle)->entry.tqe_prev ; *(hle)->entry.tqe_prev = (hle)->entry.tqe_next; ; ; } while (0); | |||
8921 | free(hle); | |||
8922 | } | |||
8923 | } | |||
8924 | ||||
8925 | static const struct got_error * | |||
8926 | histedit_load_list(struct got_histedit_list *histedit_cmds, | |||
8927 | const char *path, struct got_repository *repo) | |||
8928 | { | |||
8929 | const struct got_error *err = NULL((void *)0); | |||
8930 | FILE *f = NULL((void *)0); | |||
8931 | ||||
8932 | f = fopen(path, "r"); | |||
8933 | if (f == NULL((void *)0)) { | |||
8934 | err = got_error_from_errno2("fopen", path); | |||
8935 | goto done; | |||
8936 | } | |||
8937 | ||||
8938 | err = histedit_parse_list(histedit_cmds, f, repo); | |||
8939 | done: | |||
8940 | if (f && fclose(f) == EOF(-1) && err == NULL((void *)0)) | |||
8941 | err = got_error_from_errno("fclose"); | |||
8942 | return err; | |||
8943 | } | |||
8944 | ||||
8945 | static const struct got_error * | |||
8946 | histedit_edit_list_retry(struct got_histedit_list *histedit_cmds, | |||
8947 | const struct got_error *edit_err, struct got_object_id_queue *commits, | |||
8948 | const char *path, const char *branch_name, struct got_repository *repo) | |||
8949 | { | |||
8950 | const struct got_error *err = NULL((void *)0), *prev_err = edit_err; | |||
8951 | int resp = ' '; | |||
8952 | ||||
8953 | while (resp != 'c' && resp != 'r' && resp != 'a') { | |||
8954 | printf("%s: %s\n(c)ontinue editing, (r)estart editing, " | |||
8955 | "or (a)bort: ", getprogname(), prev_err->msg); | |||
8956 | resp = getchar()(!__isthreaded ? (--((&__sF[0]))->_r < 0 ? __srget( (&__sF[0])) : (int)(*((&__sF[0]))->_p++)) : (getc) ((&__sF[0]))); | |||
8957 | if (resp == '\n') | |||
8958 | resp = getchar()(!__isthreaded ? (--((&__sF[0]))->_r < 0 ? __srget( (&__sF[0])) : (int)(*((&__sF[0]))->_p++)) : (getc) ((&__sF[0]))); | |||
8959 | if (resp == 'c') { | |||
8960 | histedit_free_list(histedit_cmds); | |||
8961 | err = histedit_run_editor(histedit_cmds, path, commits, | |||
8962 | repo); | |||
8963 | if (err) { | |||
8964 | if (err->code != GOT_ERR_HISTEDIT_SYNTAX93 && | |||
8965 | err->code != GOT_ERR_HISTEDIT_CMD97) | |||
8966 | break; | |||
8967 | prev_err = err; | |||
8968 | resp = ' '; | |||
8969 | continue; | |||
8970 | } | |||
8971 | break; | |||
8972 | } else if (resp == 'r') { | |||
8973 | histedit_free_list(histedit_cmds); | |||
8974 | err = histedit_edit_script(histedit_cmds, | |||
8975 | commits, branch_name, 0, 0, repo); | |||
8976 | if (err) { | |||
8977 | if (err->code != GOT_ERR_HISTEDIT_SYNTAX93 && | |||
8978 | err->code != GOT_ERR_HISTEDIT_CMD97) | |||
8979 | break; | |||
8980 | prev_err = err; | |||
8981 | resp = ' '; | |||
8982 | continue; | |||
8983 | } | |||
8984 | break; | |||
8985 | } else if (resp == 'a') { | |||
8986 | err = got_error(GOT_ERR_HISTEDIT_CANCEL94); | |||
8987 | break; | |||
8988 | } else | |||
8989 | printf("invalid response '%c'\n", resp); | |||
8990 | } | |||
8991 | ||||
8992 | return err; | |||
8993 | } | |||
8994 | ||||
8995 | static const struct got_error * | |||
8996 | histedit_complete(struct got_worktree *worktree, | |||
8997 | struct got_fileindex *fileindex, struct got_reference *tmp_branch, | |||
8998 | struct got_reference *branch, struct got_repository *repo) | |||
8999 | { | |||
9000 | printf("Switching work tree to %s\n", | |||
9001 | got_ref_get_symref_target(branch)); | |||
9002 | return got_worktree_histedit_complete(worktree, fileindex, tmp_branch, | |||
9003 | branch, repo); | |||
9004 | } | |||
9005 | ||||
9006 | static const struct got_error * | |||
9007 | show_histedit_progress(struct got_commit_object *commit, | |||
9008 | struct got_histedit_list_entry *hle, struct got_object_id *new_id) | |||
9009 | { | |||
9010 | const struct got_error *err; | |||
9011 | char *old_id_str = NULL((void *)0), *new_id_str = NULL((void *)0), *logmsg = NULL((void *)0); | |||
9012 | ||||
9013 | err = got_object_id_str(&old_id_str, hle->commit_id); | |||
9014 | if (err) | |||
9015 | goto done; | |||
9016 | ||||
9017 | if (new_id) { | |||
9018 | err = got_object_id_str(&new_id_str, new_id); | |||
9019 | if (err) | |||
9020 | goto done; | |||
9021 | } | |||
9022 | ||||
9023 | old_id_str[12] = '\0'; | |||
9024 | if (new_id_str) | |||
9025 | new_id_str[12] = '\0'; | |||
9026 | ||||
9027 | if (hle->logmsg) { | |||
9028 | logmsg = strdup(hle->logmsg); | |||
9029 | if (logmsg == NULL((void *)0)) { | |||
9030 | err = got_error_from_errno("strdup"); | |||
9031 | goto done; | |||
9032 | } | |||
9033 | trim_logmsg(logmsg, 42); | |||
9034 | } else { | |||
9035 | err = get_short_logmsg(&logmsg, 42, commit); | |||
9036 | if (err) | |||
9037 | goto done; | |||
9038 | } | |||
9039 | ||||
9040 | switch (hle->cmd->code) { | |||
9041 | case GOT_HISTEDIT_PICK'p': | |||
9042 | case GOT_HISTEDIT_EDIT'e': | |||
9043 | printf("%s -> %s: %s\n", old_id_str, | |||
9044 | new_id_str ? new_id_str : "no-op change", logmsg); | |||
9045 | break; | |||
9046 | case GOT_HISTEDIT_DROP'd': | |||
9047 | case GOT_HISTEDIT_FOLD'f': | |||
9048 | printf("%s -> %s commit: %s\n", old_id_str, hle->cmd->name, | |||
9049 | logmsg); | |||
9050 | break; | |||
9051 | default: | |||
9052 | break; | |||
9053 | } | |||
9054 | done: | |||
9055 | free(old_id_str); | |||
9056 | free(new_id_str); | |||
9057 | return err; | |||
9058 | } | |||
9059 | ||||
9060 | static const struct got_error * | |||
9061 | histedit_commit(struct got_pathlist_head *merged_paths, | |||
9062 | struct got_worktree *worktree, struct got_fileindex *fileindex, | |||
9063 | struct got_reference *tmp_branch, struct got_histedit_list_entry *hle, | |||
9064 | struct got_repository *repo) | |||
9065 | { | |||
9066 | const struct got_error *err; | |||
9067 | struct got_commit_object *commit; | |||
9068 | struct got_object_id *new_commit_id; | |||
9069 | ||||
9070 | if ((hle->cmd->code == GOT_HISTEDIT_EDIT'e' || get_folded_commits(hle)) | |||
9071 | && hle->logmsg == NULL((void *)0)) { | |||
9072 | err = histedit_edit_logmsg(hle, repo); | |||
9073 | if (err) | |||
9074 | return err; | |||
9075 | } | |||
9076 | ||||
9077 | err = got_object_open_as_commit(&commit, repo, hle->commit_id); | |||
9078 | if (err) | |||
9079 | return err; | |||
9080 | ||||
9081 | err = got_worktree_histedit_commit(&new_commit_id, merged_paths, | |||
9082 | worktree, fileindex, tmp_branch, commit, hle->commit_id, | |||
9083 | hle->logmsg, repo); | |||
9084 | if (err) { | |||
9085 | if (err->code != GOT_ERR_COMMIT_NO_CHANGES76) | |||
9086 | goto done; | |||
9087 | err = show_histedit_progress(commit, hle, NULL((void *)0)); | |||
9088 | } else { | |||
9089 | err = show_histedit_progress(commit, hle, new_commit_id); | |||
9090 | free(new_commit_id); | |||
9091 | } | |||
9092 | done: | |||
9093 | got_object_commit_close(commit); | |||
9094 | return err; | |||
9095 | } | |||
9096 | ||||
9097 | static const struct got_error * | |||
9098 | histedit_skip_commit(struct got_histedit_list_entry *hle, | |||
9099 | struct got_worktree *worktree, struct got_repository *repo) | |||
9100 | { | |||
9101 | const struct got_error *error; | |||
9102 | struct got_commit_object *commit; | |||
9103 | ||||
9104 | error = got_worktree_histedit_skip_commit(worktree, hle->commit_id, | |||
9105 | repo); | |||
9106 | if (error) | |||
9107 | return error; | |||
9108 | ||||
9109 | error = got_object_open_as_commit(&commit, repo, hle->commit_id); | |||
9110 | if (error) | |||
9111 | return error; | |||
9112 | ||||
9113 | error = show_histedit_progress(commit, hle, NULL((void *)0)); | |||
9114 | got_object_commit_close(commit); | |||
9115 | return error; | |||
9116 | } | |||
9117 | ||||
9118 | static const struct got_error * | |||
9119 | check_local_changes(void *arg, unsigned char status, | |||
9120 | unsigned char staged_status, const char *path, | |||
9121 | struct got_object_id *blob_id, struct got_object_id *staged_blob_id, | |||
9122 | struct got_object_id *commit_id, int dirfd, const char *de_name) | |||
9123 | { | |||
9124 | int *have_local_changes = arg; | |||
9125 | ||||
9126 | switch (status) { | |||
9127 | case GOT_STATUS_ADD'A': | |||
9128 | case GOT_STATUS_DELETE'D': | |||
9129 | case GOT_STATUS_MODIFY'M': | |||
9130 | case GOT_STATUS_CONFLICT'C': | |||
9131 | *have_local_changes = 1; | |||
9132 | return got_error(GOT_ERR_CANCELLED49); | |||
9133 | default: | |||
9134 | break; | |||
9135 | } | |||
9136 | ||||
9137 | switch (staged_status) { | |||
9138 | case GOT_STATUS_ADD'A': | |||
9139 | case GOT_STATUS_DELETE'D': | |||
9140 | case GOT_STATUS_MODIFY'M': | |||
9141 | *have_local_changes = 1; | |||
9142 | return got_error(GOT_ERR_CANCELLED49); | |||
9143 | default: | |||
9144 | break; | |||
9145 | } | |||
9146 | ||||
9147 | return NULL((void *)0); | |||
9148 | } | |||
9149 | ||||
9150 | static const struct got_error * | |||
9151 | cmd_histedit(int argc, char *argv[]) | |||
9152 | { | |||
9153 | const struct got_error *error = NULL((void *)0); | |||
9154 | struct got_worktree *worktree = NULL((void *)0); | |||
9155 | struct got_fileindex *fileindex = NULL((void *)0); | |||
9156 | struct got_repository *repo = NULL((void *)0); | |||
9157 | char *cwd = NULL((void *)0); | |||
9158 | struct got_reference *branch = NULL((void *)0); | |||
9159 | struct got_reference *tmp_branch = NULL((void *)0); | |||
9160 | struct got_object_id *resume_commit_id = NULL((void *)0); | |||
9161 | struct got_object_id *base_commit_id = NULL((void *)0); | |||
9162 | struct got_object_id *head_commit_id = NULL((void *)0); | |||
9163 | struct got_commit_object *commit = NULL((void *)0); | |||
9164 | int ch, rebase_in_progress = 0; | |||
9165 | struct got_update_progress_arg upa; | |||
9166 | int edit_in_progress = 0, abort_edit = 0, continue_edit = 0; | |||
9167 | int edit_logmsg_only = 0, fold_only = 0; | |||
9168 | int list_backups = 0; | |||
9169 | const char *edit_script_path = NULL((void *)0); | |||
9170 | unsigned char rebase_status = GOT_STATUS_NO_CHANGE' '; | |||
9171 | struct got_object_id_queue commits; | |||
9172 | struct got_pathlist_head merged_paths; | |||
9173 | const struct got_object_id_queue *parent_ids; | |||
9174 | struct got_object_qid *pid; | |||
9175 | struct got_histedit_list histedit_cmds; | |||
9176 | struct got_histedit_list_entry *hle; | |||
9177 | ||||
9178 | SIMPLEQ_INIT(&commits)do { (&commits)->sqh_first = ((void *)0); (&commits )->sqh_last = &(&commits)->sqh_first; } while ( 0); | |||
9179 | TAILQ_INIT(&histedit_cmds)do { (&histedit_cmds)->tqh_first = ((void *)0); (& histedit_cmds)->tqh_last = &(&histedit_cmds)->tqh_first ; } while (0); | |||
9180 | TAILQ_INIT(&merged_paths)do { (&merged_paths)->tqh_first = ((void *)0); (&merged_paths )->tqh_last = &(&merged_paths)->tqh_first; } while (0); | |||
9181 | memset(&upa, 0, sizeof(upa)); | |||
9182 | ||||
9183 | while ((ch = getopt(argc, argv, "acfF:ml")) != -1) { | |||
9184 | switch (ch) { | |||
9185 | case 'a': | |||
9186 | abort_edit = 1; | |||
9187 | break; | |||
9188 | case 'c': | |||
9189 | continue_edit = 1; | |||
9190 | break; | |||
9191 | case 'f': | |||
9192 | fold_only = 1; | |||
9193 | break; | |||
9194 | case 'F': | |||
9195 | edit_script_path = optarg; | |||
9196 | break; | |||
9197 | case 'm': | |||
9198 | edit_logmsg_only = 1; | |||
9199 | break; | |||
9200 | case 'l': | |||
9201 | list_backups = 1; | |||
9202 | break; | |||
9203 | default: | |||
9204 | usage_histedit(); | |||
9205 | /* NOTREACHED */ | |||
9206 | } | |||
9207 | } | |||
9208 | ||||
9209 | argc -= optind; | |||
9210 | argv += optind; | |||
9211 | ||||
9212 | #ifndef PROFILE | |||
9213 | if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd " | |||
9214 | "unveil", NULL((void *)0)) == -1) | |||
9215 | err(1, "pledge"); | |||
9216 | #endif | |||
9217 | if (abort_edit && continue_edit) | |||
9218 | option_conflict('a', 'c'); | |||
9219 | if (edit_script_path && edit_logmsg_only) | |||
9220 | option_conflict('F', 'm'); | |||
9221 | if (abort_edit && edit_logmsg_only) | |||
9222 | option_conflict('a', 'm'); | |||
9223 | if (continue_edit && edit_logmsg_only) | |||
9224 | option_conflict('c', 'm'); | |||
9225 | if (abort_edit && fold_only) | |||
9226 | option_conflict('a', 'f'); | |||
9227 | if (continue_edit && fold_only) | |||
9228 | option_conflict('c', 'f'); | |||
9229 | if (fold_only && edit_logmsg_only) | |||
9230 | option_conflict('f', 'm'); | |||
9231 | if (edit_script_path && fold_only) | |||
9232 | option_conflict('F', 'f'); | |||
9233 | if (list_backups) { | |||
9234 | if (abort_edit) | |||
9235 | option_conflict('l', 'a'); | |||
9236 | if (continue_edit) | |||
9237 | option_conflict('l', 'c'); | |||
9238 | if (edit_script_path) | |||
9239 | option_conflict('l', 'F'); | |||
9240 | if (edit_logmsg_only) | |||
9241 | option_conflict('l', 'm'); | |||
9242 | if (fold_only) | |||
9243 | option_conflict('l', 'f'); | |||
9244 | if (argc != 0 && argc != 1) | |||
9245 | usage_histedit(); | |||
9246 | } else if (argc != 0) | |||
9247 | usage_histedit(); | |||
9248 | ||||
9249 | /* | |||
9250 | * This command cannot apply unveil(2) in all cases because the | |||
9251 | * user may choose to run an editor to edit the histedit script | |||
9252 | * and to edit individual commit log messages. | |||
9253 | * unveil(2) traverses exec(2); if an editor is used we have to | |||
9254 | * apply unveil after edit script and log messages have been written. | |||
9255 | * XXX TODO: Make use of unveil(2) where possible. | |||
9256 | */ | |||
9257 | ||||
9258 | cwd = getcwd(NULL((void *)0), 0); | |||
9259 | if (cwd == NULL((void *)0)) { | |||
9260 | error = got_error_from_errno("getcwd"); | |||
9261 | goto done; | |||
9262 | } | |||
9263 | error = got_worktree_open(&worktree, cwd); | |||
9264 | if (error) { | |||
9265 | if (list_backups) { | |||
9266 | if (error->code != GOT_ERR_NOT_WORKTREE60) | |||
9267 | goto done; | |||
9268 | } else { | |||
9269 | if (error->code == GOT_ERR_NOT_WORKTREE60) | |||
9270 | error = wrap_not_worktree_error(error, | |||
9271 | "histedit", cwd); | |||
9272 | goto done; | |||
9273 | } | |||
9274 | } | |||
9275 | ||||
9276 | if (list_backups) { | |||
9277 | error = got_repo_open(&repo, | |||
9278 | worktree ? got_worktree_get_repo_path(worktree) : cwd, | |||
9279 | NULL((void *)0)); | |||
9280 | if (error != NULL((void *)0)) | |||
9281 | goto done; | |||
9282 | error = apply_unveil(got_repo_get_path(repo), 0, | |||
9283 | worktree ? got_worktree_get_root_path(worktree) : NULL((void *)0)); | |||
9284 | if (error) | |||
9285 | goto done; | |||
9286 | error = list_backup_refs( | |||
9287 | GOT_WORKTREE_HISTEDIT_BACKUP_REF_PREFIX"refs/got/backup/histedit", | |||
9288 | argc == 1 ? argv[0] : NULL((void *)0), repo); | |||
9289 | goto done; /* nothing else to do */ | |||
9290 | } | |||
9291 | ||||
9292 | error = got_repo_open(&repo, got_worktree_get_repo_path(worktree), | |||
9293 | NULL((void *)0)); | |||
9294 | if (error != NULL((void *)0)) | |||
9295 | goto done; | |||
9296 | ||||
9297 | error = got_worktree_rebase_in_progress(&rebase_in_progress, worktree); | |||
9298 | if (error) | |||
9299 | goto done; | |||
9300 | if (rebase_in_progress) { | |||
9301 | error = got_error(GOT_ERR_REBASING88); | |||
9302 | goto done; | |||
9303 | } | |||
9304 | ||||
9305 | error = got_worktree_histedit_in_progress(&edit_in_progress, worktree); | |||
9306 | if (error) | |||
9307 | goto done; | |||
9308 | ||||
9309 | if (edit_in_progress && edit_logmsg_only) { | |||
9310 | error = got_error_msg(GOT_ERR_HISTEDIT_BUSY96, | |||
9311 | "histedit operation is in progress in this " | |||
9312 | "work tree and must be continued or aborted " | |||
9313 | "before the -m option can be used"); | |||
9314 | goto done; | |||
9315 | } | |||
9316 | if (edit_in_progress && fold_only) { | |||
9317 | error = got_error_msg(GOT_ERR_HISTEDIT_BUSY96, | |||
9318 | "histedit operation is in progress in this " | |||
9319 | "work tree and must be continued or aborted " | |||
9320 | "before the -f option can be used"); | |||
9321 | goto done; | |||
9322 | } | |||
9323 | ||||
9324 | if (edit_in_progress && abort_edit) { | |||
9325 | error = got_worktree_histedit_continue(&resume_commit_id, | |||
9326 | &tmp_branch, &branch, &base_commit_id, &fileindex, | |||
9327 | worktree, repo); | |||
9328 | if (error) | |||
9329 | goto done; | |||
9330 | printf("Switching work tree to %s\n", | |||
9331 | got_ref_get_symref_target(branch)); | |||
9332 | error = got_worktree_histedit_abort(worktree, fileindex, repo, | |||
9333 | branch, base_commit_id, update_progress, &upa); | |||
9334 | if (error) | |||
9335 | goto done; | |||
9336 | printf("Histedit of %s aborted\n", | |||
9337 | got_ref_get_symref_target(branch)); | |||
9338 | print_update_progress_stats(&upa); | |||
9339 | goto done; /* nothing else to do */ | |||
9340 | } else if (abort_edit) { | |||
9341 | error = got_error(GOT_ERR_NOT_HISTEDIT90); | |||
9342 | goto done; | |||
9343 | } | |||
9344 | ||||
9345 | if (continue_edit) { | |||
9346 | char *path; | |||
9347 | ||||
9348 | if (!edit_in_progress) { | |||
9349 | error = got_error(GOT_ERR_NOT_HISTEDIT90); | |||
9350 | goto done; | |||
9351 | } | |||
9352 | ||||
9353 | error = got_worktree_get_histedit_script_path(&path, worktree); | |||
9354 | if (error) | |||
9355 | goto done; | |||
9356 | ||||
9357 | error = histedit_load_list(&histedit_cmds, path, repo); | |||
9358 | free(path); | |||
9359 | if (error) | |||
9360 | goto done; | |||
9361 | ||||
9362 | error = got_worktree_histedit_continue(&resume_commit_id, | |||
9363 | &tmp_branch, &branch, &base_commit_id, &fileindex, | |||
9364 | worktree, repo); | |||
9365 | if (error) | |||
9366 | goto done; | |||
9367 | ||||
9368 | error = got_ref_resolve(&head_commit_id, repo, branch); | |||
9369 | if (error) | |||
9370 | goto done; | |||
9371 | ||||
9372 | error = got_object_open_as_commit(&commit, repo, | |||
9373 | head_commit_id); | |||
9374 | if (error) | |||
9375 | goto done; | |||
9376 | parent_ids = got_object_commit_get_parent_ids(commit); | |||
9377 | pid = SIMPLEQ_FIRST(parent_ids)((parent_ids)->sqh_first); | |||
9378 | if (pid == NULL((void *)0)) { | |||
9379 | error = got_error(GOT_ERR_EMPTY_HISTEDIT91); | |||
9380 | goto done; | |||
9381 | } | |||
9382 | error = collect_commits(&commits, head_commit_id, pid->id, | |||
9383 | base_commit_id, got_worktree_get_path_prefix(worktree), | |||
9384 | GOT_ERR_HISTEDIT_PATH98, repo); | |||
9385 | got_object_commit_close(commit); | |||
9386 | commit = NULL((void *)0); | |||
9387 | if (error) | |||
9388 | goto done; | |||
9389 | } else { | |||
9390 | if (edit_in_progress) { | |||
9391 | error = got_error(GOT_ERR_HISTEDIT_BUSY96); | |||
9392 | goto done; | |||
9393 | } | |||
9394 | ||||
9395 | error = got_ref_open(&branch, repo, | |||
9396 | got_worktree_get_head_ref_name(worktree), 0); | |||
9397 | if (error != NULL((void *)0)) | |||
9398 | goto done; | |||
9399 | ||||
9400 | if (strncmp(got_ref_get_name(branch), "refs/heads/", 11) != 0) { | |||
9401 | error = got_error_msg(GOT_ERR_COMMIT_BRANCH100, | |||
9402 | "will not edit commit history of a branch outside " | |||
9403 | "the \"refs/heads/\" reference namespace"); | |||
9404 | goto done; | |||
9405 | } | |||
9406 | ||||
9407 | error = got_ref_resolve(&head_commit_id, repo, branch); | |||
9408 | got_ref_close(branch); | |||
9409 | branch = NULL((void *)0); | |||
9410 | if (error) | |||
9411 | goto done; | |||
9412 | ||||
9413 | error = got_object_open_as_commit(&commit, repo, | |||
9414 | head_commit_id); | |||
9415 | if (error) | |||
9416 | goto done; | |||
9417 | parent_ids = got_object_commit_get_parent_ids(commit); | |||
9418 | pid = SIMPLEQ_FIRST(parent_ids)((parent_ids)->sqh_first); | |||
9419 | if (pid == NULL((void *)0)) { | |||
9420 | error = got_error(GOT_ERR_EMPTY_HISTEDIT91); | |||
9421 | goto done; | |||
9422 | } | |||
9423 | error = collect_commits(&commits, head_commit_id, pid->id, | |||
9424 | got_worktree_get_base_commit_id(worktree), | |||
9425 | got_worktree_get_path_prefix(worktree), | |||
9426 | GOT_ERR_HISTEDIT_PATH98, repo); | |||
9427 | got_object_commit_close(commit); | |||
9428 | commit = NULL((void *)0); | |||
9429 | if (error) | |||
9430 | goto done; | |||
9431 | ||||
9432 | if (SIMPLEQ_EMPTY(&commits)(((&commits)->sqh_first) == ((void *)0))) { | |||
9433 | error = got_error(GOT_ERR_EMPTY_HISTEDIT91); | |||
9434 | goto done; | |||
9435 | } | |||
9436 | ||||
9437 | error = got_worktree_histedit_prepare(&tmp_branch, &branch, | |||
9438 | &base_commit_id, &fileindex, worktree, repo); | |||
9439 | if (error) | |||
9440 | goto done; | |||
9441 | ||||
9442 | if (edit_script_path) { | |||
9443 | error = histedit_load_list(&histedit_cmds, | |||
9444 | edit_script_path, repo); | |||
9445 | if (error) { | |||
9446 | got_worktree_histedit_abort(worktree, fileindex, | |||
9447 | repo, branch, base_commit_id, | |||
9448 | update_progress, &upa); | |||
9449 | print_update_progress_stats(&upa); | |||
9450 | goto done; | |||
9451 | } | |||
9452 | } else { | |||
9453 | const char *branch_name; | |||
9454 | branch_name = got_ref_get_symref_target(branch); | |||
9455 | if (strncmp(branch_name, "refs/heads/", 11) == 0) | |||
9456 | branch_name += 11; | |||
9457 | error = histedit_edit_script(&histedit_cmds, &commits, | |||
9458 | branch_name, edit_logmsg_only, fold_only, repo); | |||
9459 | if (error) { | |||
9460 | got_worktree_histedit_abort(worktree, fileindex, | |||
9461 | repo, branch, base_commit_id, | |||
9462 | update_progress, &upa); | |||
9463 | print_update_progress_stats(&upa); | |||
9464 | goto done; | |||
9465 | } | |||
9466 | ||||
9467 | } | |||
9468 | ||||
9469 | error = histedit_save_list(&histedit_cmds, worktree, | |||
9470 | repo); | |||
9471 | if (error) { | |||
9472 | got_worktree_histedit_abort(worktree, fileindex, | |||
9473 | repo, branch, base_commit_id, | |||
9474 | update_progress, &upa); | |||
9475 | print_update_progress_stats(&upa); | |||
9476 | goto done; | |||
9477 | } | |||
9478 | ||||
9479 | } | |||
9480 | ||||
9481 | error = histedit_check_script(&histedit_cmds, &commits, repo); | |||
9482 | if (error) | |||
9483 | goto done; | |||
9484 | ||||
9485 | TAILQ_FOREACH(hle, &histedit_cmds, entry)for((hle) = ((&histedit_cmds)->tqh_first); (hle) != (( void *)0); (hle) = ((hle)->entry.tqe_next)) { | |||
9486 | if (resume_commit_id) { | |||
9487 | if (got_object_id_cmp(hle->commit_id, | |||
9488 | resume_commit_id) != 0) | |||
9489 | continue; | |||
9490 | ||||
9491 | resume_commit_id = NULL((void *)0); | |||
9492 | if (hle->cmd->code == GOT_HISTEDIT_DROP'd' || | |||
9493 | hle->cmd->code == GOT_HISTEDIT_FOLD'f') { | |||
9494 | error = histedit_skip_commit(hle, worktree, | |||
9495 | repo); | |||
9496 | if (error) | |||
9497 | goto done; | |||
9498 | } else { | |||
9499 | struct got_pathlist_head paths; | |||
9500 | int have_changes = 0; | |||
9501 | ||||
9502 | TAILQ_INIT(&paths)do { (&paths)->tqh_first = ((void *)0); (&paths)-> tqh_last = &(&paths)->tqh_first; } while (0); | |||
9503 | error = got_pathlist_append(&paths, "", NULL((void *)0)); | |||
9504 | if (error) | |||
9505 | goto done; | |||
9506 | error = got_worktree_status(worktree, &paths, | |||
9507 | repo, check_local_changes, &have_changes, | |||
9508 | check_cancelled, NULL((void *)0)); | |||
9509 | got_pathlist_free(&paths); | |||
9510 | if (error) { | |||
9511 | if (error->code != GOT_ERR_CANCELLED49) | |||
9512 | goto done; | |||
9513 | if (sigint_received || sigpipe_received) | |||
9514 | goto done; | |||
9515 | } | |||
9516 | if (have_changes) { | |||
9517 | error = histedit_commit(NULL((void *)0), worktree, | |||
9518 | fileindex, tmp_branch, hle, repo); | |||
9519 | if (error) | |||
9520 | goto done; | |||
9521 | } else { | |||
9522 | error = got_object_open_as_commit( | |||
9523 | &commit, repo, hle->commit_id); | |||
9524 | if (error) | |||
9525 | goto done; | |||
9526 | error = show_histedit_progress(commit, | |||
9527 | hle, NULL((void *)0)); | |||
9528 | got_object_commit_close(commit); | |||
9529 | commit = NULL((void *)0); | |||
9530 | if (error) | |||
9531 | goto done; | |||
9532 | } | |||
9533 | } | |||
9534 | continue; | |||
9535 | } | |||
9536 | ||||
9537 | if (hle->cmd->code == GOT_HISTEDIT_DROP'd') { | |||
9538 | error = histedit_skip_commit(hle, worktree, repo); | |||
9539 | if (error) | |||
9540 | goto done; | |||
9541 | continue; | |||
9542 | } | |||
9543 | ||||
9544 | error = got_object_open_as_commit(&commit, repo, | |||
9545 | hle->commit_id); | |||
9546 | if (error) | |||
9547 | goto done; | |||
9548 | parent_ids = got_object_commit_get_parent_ids(commit); | |||
9549 | pid = SIMPLEQ_FIRST(parent_ids)((parent_ids)->sqh_first); | |||
9550 | ||||
9551 | error = got_worktree_histedit_merge_files(&merged_paths, | |||
9552 | worktree, fileindex, pid->id, hle->commit_id, repo, | |||
9553 | update_progress, &upa, check_cancelled, NULL((void *)0)); | |||
9554 | if (error) | |||
9555 | goto done; | |||
9556 | got_object_commit_close(commit); | |||
9557 | commit = NULL((void *)0); | |||
9558 | ||||
9559 | print_update_progress_stats(&upa); | |||
9560 | if (upa.conflicts > 0) | |||
9561 | rebase_status = GOT_STATUS_CONFLICT'C'; | |||
9562 | ||||
9563 | if (rebase_status == GOT_STATUS_CONFLICT'C') { | |||
9564 | error = show_rebase_merge_conflict(hle->commit_id, | |||
9565 | repo); | |||
9566 | if (error) | |||
9567 | goto done; | |||
9568 | got_worktree_rebase_pathlist_free(&merged_paths); | |||
9569 | break; | |||
9570 | } | |||
9571 | ||||
9572 | if (hle->cmd->code == GOT_HISTEDIT_EDIT'e') { | |||
9573 | char *id_str; | |||
9574 | error = got_object_id_str(&id_str, hle->commit_id); | |||
9575 | if (error) | |||
9576 | goto done; | |||
9577 | printf("Stopping histedit for amending commit %s\n", | |||
9578 | id_str); | |||
9579 | free(id_str); | |||
9580 | got_worktree_rebase_pathlist_free(&merged_paths); | |||
9581 | error = got_worktree_histedit_postpone(worktree, | |||
9582 | fileindex); | |||
9583 | goto done; | |||
9584 | } | |||
9585 | ||||
9586 | if (hle->cmd->code == GOT_HISTEDIT_FOLD'f') { | |||
9587 | error = histedit_skip_commit(hle, worktree, repo); | |||
9588 | if (error) | |||
9589 | goto done; | |||
9590 | continue; | |||
9591 | } | |||
9592 | ||||
9593 | error = histedit_commit(&merged_paths, worktree, fileindex, | |||
9594 | tmp_branch, hle, repo); | |||
9595 | got_worktree_rebase_pathlist_free(&merged_paths); | |||
9596 | if (error) | |||
9597 | goto done; | |||
9598 | } | |||
9599 | ||||
9600 | if (rebase_status == GOT_STATUS_CONFLICT'C') { | |||
9601 | error = got_worktree_histedit_postpone(worktree, fileindex); | |||
9602 | if (error) | |||
9603 | goto done; | |||
9604 | error = got_error_msg(GOT_ERR_CONFLICTS82, | |||
9605 | "conflicts must be resolved before histedit can continue"); | |||
9606 | } else | |||
9607 | error = histedit_complete(worktree, fileindex, tmp_branch, | |||
9608 | branch, repo); | |||
9609 | done: | |||
9610 | got_object_id_queue_free(&commits); | |||
9611 | histedit_free_list(&histedit_cmds); | |||
9612 | free(head_commit_id); | |||
9613 | free(base_commit_id); | |||
9614 | free(resume_commit_id); | |||
9615 | if (commit) | |||
9616 | got_object_commit_close(commit); | |||
9617 | if (branch) | |||
9618 | got_ref_close(branch); | |||
9619 | if (tmp_branch) | |||
9620 | got_ref_close(tmp_branch); | |||
9621 | if (worktree) | |||
9622 | got_worktree_close(worktree); | |||
9623 | if (repo) | |||
9624 | got_repo_close(repo); | |||
9625 | return error; | |||
9626 | } | |||
9627 | ||||
9628 | __dead__attribute__((__noreturn__)) static void | |||
9629 | usage_integrate(void) | |||
9630 | { | |||
9631 | fprintf(stderr(&__sF[2]), "usage: %s integrate branch\n", getprogname()); | |||
9632 | exit(1); | |||
9633 | } | |||
9634 | ||||
9635 | static const struct got_error * | |||
9636 | cmd_integrate(int argc, char *argv[]) | |||
9637 | { | |||
9638 | const struct got_error *error = NULL((void *)0); | |||
9639 | struct got_repository *repo = NULL((void *)0); | |||
9640 | struct got_worktree *worktree = NULL((void *)0); | |||
9641 | char *cwd = NULL((void *)0), *refname = NULL((void *)0), *base_refname = NULL((void *)0); | |||
9642 | const char *branch_arg = NULL((void *)0); | |||
9643 | struct got_reference *branch_ref = NULL((void *)0), *base_branch_ref = NULL((void *)0); | |||
9644 | struct got_fileindex *fileindex = NULL((void *)0); | |||
9645 | struct got_object_id *commit_id = NULL((void *)0), *base_commit_id = NULL((void *)0); | |||
9646 | int ch; | |||
9647 | struct got_update_progress_arg upa; | |||
9648 | ||||
9649 | while ((ch = getopt(argc, argv, "")) != -1) { | |||
9650 | switch (ch) { | |||
9651 | default: | |||
9652 | usage_integrate(); | |||
9653 | /* NOTREACHED */ | |||
9654 | } | |||
9655 | } | |||
9656 | ||||
9657 | argc -= optind; | |||
9658 | argv += optind; | |||
9659 | ||||
9660 | if (argc != 1) | |||
9661 | usage_integrate(); | |||
9662 | branch_arg = argv[0]; | |||
9663 | #ifndef PROFILE | |||
9664 | if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd " | |||
9665 | "unveil", NULL((void *)0)) == -1) | |||
9666 | err(1, "pledge"); | |||
9667 | #endif | |||
9668 | cwd = getcwd(NULL((void *)0), 0); | |||
9669 | if (cwd == NULL((void *)0)) { | |||
9670 | error = got_error_from_errno("getcwd"); | |||
9671 | goto done; | |||
9672 | } | |||
9673 | ||||
9674 | error = got_worktree_open(&worktree, cwd); | |||
9675 | if (error) { | |||
9676 | if (error->code == GOT_ERR_NOT_WORKTREE60) | |||
9677 | error = wrap_not_worktree_error(error, "integrate", | |||
9678 | cwd); | |||
9679 | goto done; | |||
9680 | } | |||
9681 | ||||
9682 | error = check_rebase_or_histedit_in_progress(worktree); | |||
9683 | if (error) | |||
9684 | goto done; | |||
9685 | ||||
9686 | error = got_repo_open(&repo, got_worktree_get_repo_path(worktree), | |||
9687 | NULL((void *)0)); | |||
9688 | if (error != NULL((void *)0)) | |||
9689 | goto done; | |||
9690 | ||||
9691 | error = apply_unveil(got_repo_get_path(repo), 0, | |||
9692 | got_worktree_get_root_path(worktree)); | |||
9693 | if (error) | |||
9694 | goto done; | |||
9695 | ||||
9696 | if (asprintf(&refname, "refs/heads/%s", branch_arg) == -1) { | |||
9697 | error = got_error_from_errno("asprintf"); | |||
9698 | goto done; | |||
9699 | } | |||
9700 | ||||
9701 | error = got_worktree_integrate_prepare(&fileindex, &branch_ref, | |||
9702 | &base_branch_ref, worktree, refname, repo); | |||
9703 | if (error) | |||
9704 | goto done; | |||
9705 | ||||
9706 | refname = strdup(got_ref_get_name(branch_ref)); | |||
9707 | if (refname == NULL((void *)0)) { | |||
9708 | error = got_error_from_errno("strdup"); | |||
9709 | got_worktree_integrate_abort(worktree, fileindex, repo, | |||
9710 | branch_ref, base_branch_ref); | |||
9711 | goto done; | |||
9712 | } | |||
9713 | base_refname = strdup(got_ref_get_name(base_branch_ref)); | |||
9714 | if (base_refname == NULL((void *)0)) { | |||
9715 | error = got_error_from_errno("strdup"); | |||
9716 | got_worktree_integrate_abort(worktree, fileindex, repo, | |||
9717 | branch_ref, base_branch_ref); | |||
9718 | goto done; | |||
9719 | } | |||
9720 | ||||
9721 | error = got_ref_resolve(&commit_id, repo, branch_ref); | |||
9722 | if (error) | |||
9723 | goto done; | |||
9724 | ||||
9725 | error = got_ref_resolve(&base_commit_id, repo, base_branch_ref); | |||
9726 | if (error) | |||
9727 | goto done; | |||
9728 | ||||
9729 | if (got_object_id_cmp(commit_id, base_commit_id) == 0) { | |||
9730 | error = got_error_msg(GOT_ERR_SAME_BRANCH79, | |||
9731 | "specified branch has already been integrated"); | |||
9732 | got_worktree_integrate_abort(worktree, fileindex, repo, | |||
9733 | branch_ref, base_branch_ref); | |||
9734 | goto done; | |||
9735 | } | |||
9736 | ||||
9737 | error = check_linear_ancestry(commit_id, base_commit_id, 1, repo); | |||
9738 | if (error) { | |||
9739 | if (error->code == GOT_ERR_ANCESTRY55) | |||
9740 | error = got_error(GOT_ERR_REBASE_REQUIRED111); | |||
9741 | got_worktree_integrate_abort(worktree, fileindex, repo, | |||
9742 | branch_ref, base_branch_ref); | |||
9743 | goto done; | |||
9744 | } | |||
9745 | ||||
9746 | memset(&upa, 0, sizeof(upa)); | |||
9747 | error = got_worktree_integrate_continue(worktree, fileindex, repo, | |||
9748 | branch_ref, base_branch_ref, update_progress, &upa, | |||
9749 | check_cancelled, NULL((void *)0)); | |||
9750 | if (error) | |||
9751 | goto done; | |||
9752 | ||||
9753 | printf("Integrated %s into %s\n", refname, base_refname); | |||
9754 | print_update_progress_stats(&upa); | |||
9755 | done: | |||
9756 | if (repo) | |||
9757 | got_repo_close(repo); | |||
9758 | if (worktree) | |||
9759 | got_worktree_close(worktree); | |||
9760 | free(cwd); | |||
9761 | free(base_commit_id); | |||
9762 | free(commit_id); | |||
9763 | free(refname); | |||
9764 | free(base_refname); | |||
9765 | return error; | |||
9766 | } | |||
9767 | ||||
9768 | __dead__attribute__((__noreturn__)) static void | |||
9769 | usage_stage(void) | |||
9770 | { | |||
9771 | fprintf(stderr(&__sF[2]), "usage: %s stage [-l] | [-p] [-F response-script] " | |||
9772 | "[-S] [file-path ...]\n", | |||
9773 | getprogname()); | |||
9774 | exit(1); | |||
9775 | } | |||
9776 | ||||
9777 | static const struct got_error * | |||
9778 | print_stage(void *arg, unsigned char status, unsigned char staged_status, | |||
9779 | const char *path, struct got_object_id *blob_id, | |||
9780 | struct got_object_id *staged_blob_id, struct got_object_id *commit_id, | |||
9781 | int dirfd, const char *de_name) | |||
9782 | { | |||
9783 | const struct got_error *err = NULL((void *)0); | |||
9784 | char *id_str = NULL((void *)0); | |||
9785 | ||||
9786 | if (staged_status != GOT_STATUS_ADD'A' && | |||
9787 | staged_status != GOT_STATUS_MODIFY'M' && | |||
9788 | staged_status != GOT_STATUS_DELETE'D') | |||
9789 | return NULL((void *)0); | |||
9790 | ||||
9791 | if (staged_status == GOT_STATUS_ADD'A' || | |||
9792 | staged_status == GOT_STATUS_MODIFY'M') | |||
9793 | err = got_object_id_str(&id_str, staged_blob_id); | |||
9794 | else | |||
9795 | err = got_object_id_str(&id_str, blob_id); | |||
9796 | if (err) | |||
9797 | return err; | |||
9798 | ||||
9799 | printf("%s %c %s\n", id_str, staged_status, path); | |||
9800 | free(id_str); | |||
9801 | return NULL((void *)0); | |||
9802 | } | |||
9803 | ||||
9804 | static const struct got_error * | |||
9805 | cmd_stage(int argc, char *argv[]) | |||
9806 | { | |||
9807 | const struct got_error *error = NULL((void *)0); | |||
9808 | struct got_repository *repo = NULL((void *)0); | |||
9809 | struct got_worktree *worktree = NULL((void *)0); | |||
9810 | char *cwd = NULL((void *)0); | |||
9811 | struct got_pathlist_head paths; | |||
9812 | struct got_pathlist_entry *pe; | |||
9813 | int ch, list_stage = 0, pflag = 0, allow_bad_symlinks = 0; | |||
9814 | FILE *patch_script_file = NULL((void *)0); | |||
9815 | const char *patch_script_path = NULL((void *)0); | |||
9816 | struct choose_patch_arg cpa; | |||
9817 | ||||
9818 | TAILQ_INIT(&paths)do { (&paths)->tqh_first = ((void *)0); (&paths)-> tqh_last = &(&paths)->tqh_first; } while (0); | |||
9819 | ||||
9820 | while ((ch = getopt(argc, argv, "lpF:S")) != -1) { | |||
9821 | switch (ch) { | |||
9822 | case 'l': | |||
9823 | list_stage = 1; | |||
9824 | break; | |||
9825 | case 'p': | |||
9826 | pflag = 1; | |||
9827 | break; | |||
9828 | case 'F': | |||
9829 | patch_script_path = optarg; | |||
9830 | break; | |||
9831 | case 'S': | |||
9832 | allow_bad_symlinks = 1; | |||
9833 | break; | |||
9834 | default: | |||
9835 | usage_stage(); | |||
9836 | /* NOTREACHED */ | |||
9837 | } | |||
9838 | } | |||
9839 | ||||
9840 | argc -= optind; | |||
9841 | argv += optind; | |||
9842 | ||||
9843 | #ifndef PROFILE | |||
9844 | if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd " | |||
9845 | "unveil", NULL((void *)0)) == -1) | |||
9846 | err(1, "pledge"); | |||
9847 | #endif | |||
9848 | if (list_stage && (pflag || patch_script_path)) | |||
9849 | errx(1, "-l option cannot be used with other options"); | |||
9850 | if (patch_script_path && !pflag) | |||
9851 | errx(1, "-F option can only be used together with -p option"); | |||
9852 | ||||
9853 | cwd = getcwd(NULL((void *)0), 0); | |||
9854 | if (cwd == NULL((void *)0)) { | |||
9855 | error = got_error_from_errno("getcwd"); | |||
9856 | goto done; | |||
9857 | } | |||
9858 | ||||
9859 | error = got_worktree_open(&worktree, cwd); | |||
9860 | if (error) { | |||
9861 | if (error->code == GOT_ERR_NOT_WORKTREE60) | |||
9862 | error = wrap_not_worktree_error(error, "stage", cwd); | |||
9863 | goto done; | |||
9864 | } | |||
9865 | ||||
9866 | error = got_repo_open(&repo, got_worktree_get_repo_path(worktree), | |||
9867 | NULL((void *)0)); | |||
9868 | if (error != NULL((void *)0)) | |||
9869 | goto done; | |||
9870 | ||||
9871 | if (patch_script_path) { | |||
9872 | patch_script_file = fopen(patch_script_path, "r"); | |||
9873 | if (patch_script_file == NULL((void *)0)) { | |||
9874 | error = got_error_from_errno2("fopen", | |||
9875 | patch_script_path); | |||
9876 | goto done; | |||
9877 | } | |||
9878 | } | |||
9879 | error = apply_unveil(got_repo_get_path(repo), 0, | |||
9880 | got_worktree_get_root_path(worktree)); | |||
9881 | if (error) | |||
9882 | goto done; | |||
9883 | ||||
9884 | error = get_worktree_paths_from_argv(&paths, argc, argv, worktree); | |||
9885 | if (error) | |||
9886 | goto done; | |||
9887 | ||||
9888 | if (list_stage) | |||
9889 | error = got_worktree_status(worktree, &paths, repo, | |||
9890 | print_stage, NULL((void *)0), check_cancelled, NULL((void *)0)); | |||
9891 | else { | |||
9892 | cpa.patch_script_file = patch_script_file; | |||
9893 | cpa.action = "stage"; | |||
9894 | error = got_worktree_stage(worktree, &paths, | |||
9895 | pflag ? NULL((void *)0) : print_status, NULL((void *)0), | |||
9896 | pflag ? choose_patch : NULL((void *)0), &cpa, | |||
9897 | allow_bad_symlinks, repo); | |||
9898 | } | |||
9899 | done: | |||
9900 | if (patch_script_file && fclose(patch_script_file) == EOF(-1) && | |||
9901 | error == NULL((void *)0)) | |||
9902 | error = got_error_from_errno2("fclose", patch_script_path); | |||
9903 | if (repo) | |||
9904 | got_repo_close(repo); | |||
9905 | if (worktree) | |||
9906 | got_worktree_close(worktree); | |||
9907 | TAILQ_FOREACH(pe, &paths, entry)for((pe) = ((&paths)->tqh_first); (pe) != ((void *)0); (pe) = ((pe)->entry.tqe_next)) | |||
9908 | free((char *)pe->path); | |||
9909 | got_pathlist_free(&paths); | |||
9910 | free(cwd); | |||
9911 | return error; | |||
9912 | } | |||
9913 | ||||
9914 | __dead__attribute__((__noreturn__)) static void | |||
9915 | usage_unstage(void) | |||
9916 | { | |||
9917 | fprintf(stderr(&__sF[2]), "usage: %s unstage [-p] [-F response-script] " | |||
9918 | "[file-path ...]\n", | |||
9919 | getprogname()); | |||
9920 | exit(1); | |||
9921 | } | |||
9922 | ||||
9923 | ||||
9924 | static const struct got_error * | |||
9925 | cmd_unstage(int argc, char *argv[]) | |||
9926 | { | |||
9927 | const struct got_error *error = NULL((void *)0); | |||
9928 | struct got_repository *repo = NULL((void *)0); | |||
9929 | struct got_worktree *worktree = NULL((void *)0); | |||
9930 | char *cwd = NULL((void *)0); | |||
9931 | struct got_pathlist_head paths; | |||
9932 | struct got_pathlist_entry *pe; | |||
9933 | int ch, pflag = 0; | |||
9934 | struct got_update_progress_arg upa; | |||
9935 | FILE *patch_script_file = NULL((void *)0); | |||
9936 | const char *patch_script_path = NULL((void *)0); | |||
9937 | struct choose_patch_arg cpa; | |||
9938 | ||||
9939 | TAILQ_INIT(&paths)do { (&paths)->tqh_first = ((void *)0); (&paths)-> tqh_last = &(&paths)->tqh_first; } while (0); | |||
9940 | ||||
9941 | while ((ch = getopt(argc, argv, "pF:")) != -1) { | |||
9942 | switch (ch) { | |||
9943 | case 'p': | |||
9944 | pflag = 1; | |||
9945 | break; | |||
9946 | case 'F': | |||
9947 | patch_script_path = optarg; | |||
9948 | break; | |||
9949 | default: | |||
9950 | usage_unstage(); | |||
9951 | /* NOTREACHED */ | |||
9952 | } | |||
9953 | } | |||
9954 | ||||
9955 | argc -= optind; | |||
9956 | argv += optind; | |||
9957 | ||||
9958 | #ifndef PROFILE | |||
9959 | if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd " | |||
9960 | "unveil", NULL((void *)0)) == -1) | |||
9961 | err(1, "pledge"); | |||
9962 | #endif | |||
9963 | if (patch_script_path && !pflag) | |||
9964 | errx(1, "-F option can only be used together with -p option"); | |||
9965 | ||||
9966 | cwd = getcwd(NULL((void *)0), 0); | |||
9967 | if (cwd == NULL((void *)0)) { | |||
9968 | error = got_error_from_errno("getcwd"); | |||
9969 | goto done; | |||
9970 | } | |||
9971 | ||||
9972 | error = got_worktree_open(&worktree, cwd); | |||
9973 | if (error) { | |||
9974 | if (error->code == GOT_ERR_NOT_WORKTREE60) | |||
9975 | error = wrap_not_worktree_error(error, "unstage", cwd); | |||
9976 | goto done; | |||
9977 | } | |||
9978 | ||||
9979 | error = got_repo_open(&repo, got_worktree_get_repo_path(worktree), | |||
9980 | NULL((void *)0)); | |||
9981 | if (error != NULL((void *)0)) | |||
9982 | goto done; | |||
9983 | ||||
9984 | if (patch_script_path) { | |||
9985 | patch_script_file = fopen(patch_script_path, "r"); | |||
9986 | if (patch_script_file == NULL((void *)0)) { | |||
9987 | error = got_error_from_errno2("fopen", | |||
9988 | patch_script_path); | |||
9989 | goto done; | |||
9990 | } | |||
9991 | } | |||
9992 | ||||
9993 | error = apply_unveil(got_repo_get_path(repo), 0, | |||
9994 | got_worktree_get_root_path(worktree)); | |||
9995 | if (error) | |||
9996 | goto done; | |||
9997 | ||||
9998 | error = get_worktree_paths_from_argv(&paths, argc, argv, worktree); | |||
9999 | if (error) | |||
10000 | goto done; | |||
10001 | ||||
10002 | cpa.patch_script_file = patch_script_file; | |||
10003 | cpa.action = "unstage"; | |||
10004 | memset(&upa, 0, sizeof(upa)); | |||
10005 | error = got_worktree_unstage(worktree, &paths, update_progress, | |||
10006 | &upa, pflag ? choose_patch : NULL((void *)0), &cpa, repo); | |||
10007 | if (!error) | |||
10008 | print_update_progress_stats(&upa); | |||
10009 | done: | |||
10010 | if (patch_script_file && fclose(patch_script_file) == EOF(-1) && | |||
10011 | error == NULL((void *)0)) | |||
10012 | error = got_error_from_errno2("fclose", patch_script_path); | |||
10013 | if (repo) | |||
10014 | got_repo_close(repo); | |||
10015 | if (worktree) | |||
10016 | got_worktree_close(worktree); | |||
10017 | TAILQ_FOREACH(pe, &paths, entry)for((pe) = ((&paths)->tqh_first); (pe) != ((void *)0); (pe) = ((pe)->entry.tqe_next)) | |||
10018 | free((char *)pe->path); | |||
10019 | got_pathlist_free(&paths); | |||
10020 | free(cwd); | |||
10021 | return error; | |||
10022 | } | |||
10023 | ||||
10024 | __dead__attribute__((__noreturn__)) static void | |||
10025 | usage_cat(void) | |||
10026 | { | |||
10027 | fprintf(stderr(&__sF[2]), "usage: %s cat [-r repository ] [ -c commit ] [ -P ] " | |||
10028 | "arg1 [arg2 ...]\n", getprogname()); | |||
10029 | exit(1); | |||
10030 | } | |||
10031 | ||||
10032 | static const struct got_error * | |||
10033 | cat_blob(struct got_object_id *id, struct got_repository *repo, FILE *outfile) | |||
10034 | { | |||
10035 | const struct got_error *err; | |||
10036 | struct got_blob_object *blob; | |||
10037 | ||||
10038 | err = got_object_open_as_blob(&blob, repo, id, 8192); | |||
10039 | if (err) | |||
10040 | return err; | |||
10041 | ||||
10042 | err = got_object_blob_dump_to_file(NULL((void *)0), NULL((void *)0), NULL((void *)0), outfile, blob); | |||
10043 | got_object_blob_close(blob); | |||
10044 | return err; | |||
10045 | } | |||
10046 | ||||
10047 | static const struct got_error * | |||
10048 | cat_tree(struct got_object_id *id, struct got_repository *repo, FILE *outfile) | |||
10049 | { | |||
10050 | const struct got_error *err; | |||
10051 | struct got_tree_object *tree; | |||
10052 | int nentries, i; | |||
10053 | ||||
10054 | err = got_object_open_as_tree(&tree, repo, id); | |||
10055 | if (err) | |||
10056 | return err; | |||
10057 | ||||
10058 | nentries = got_object_tree_get_nentries(tree); | |||
10059 | for (i = 0; i < nentries; i++) { | |||
10060 | struct got_tree_entry *te; | |||
10061 | char *id_str; | |||
10062 | if (sigint_received || sigpipe_received) | |||
10063 | break; | |||
10064 | te = got_object_tree_get_entry(tree, i); | |||
10065 | err = got_object_id_str(&id_str, got_tree_entry_get_id(te)); | |||
10066 | if (err) | |||
10067 | break; | |||
10068 | fprintf(outfile, "%s %.7o %s\n", id_str, | |||
10069 | got_tree_entry_get_mode(te), | |||
10070 | got_tree_entry_get_name(te)); | |||
10071 | free(id_str); | |||
10072 | } | |||
10073 | ||||
10074 | got_object_tree_close(tree); | |||
10075 | return err; | |||
10076 | } | |||
10077 | ||||
10078 | static const struct got_error * | |||
10079 | cat_commit(struct got_object_id *id, struct got_repository *repo, FILE *outfile) | |||
10080 | { | |||
10081 | const struct got_error *err; | |||
10082 | struct got_commit_object *commit; | |||
10083 | const struct got_object_id_queue *parent_ids; | |||
10084 | struct got_object_qid *pid; | |||
10085 | char *id_str = NULL((void *)0); | |||
10086 | const char *logmsg = NULL((void *)0); | |||
10087 | ||||
10088 | err = got_object_open_as_commit(&commit, repo, id); | |||
10089 | if (err) | |||
10090 | return err; | |||
10091 | ||||
10092 | err = got_object_id_str(&id_str, got_object_commit_get_tree_id(commit)); | |||
10093 | if (err) | |||
10094 | goto done; | |||
10095 | ||||
10096 | fprintf(outfile, "%s%s\n", GOT_COMMIT_LABEL_TREE"tree ", id_str); | |||
10097 | parent_ids = got_object_commit_get_parent_ids(commit); | |||
10098 | fprintf(outfile, "numparents %d\n", | |||
10099 | got_object_commit_get_nparents(commit)); | |||
10100 | SIMPLEQ_FOREACH(pid, parent_ids, entry)for((pid) = ((parent_ids)->sqh_first); (pid) != ((void *)0 ); (pid) = ((pid)->entry.sqe_next)) { | |||
10101 | char *pid_str; | |||
10102 | err = got_object_id_str(&pid_str, pid->id); | |||
10103 | if (err) | |||
10104 | goto done; | |||
10105 | fprintf(outfile, "%s%s\n", GOT_COMMIT_LABEL_PARENT"parent ", pid_str); | |||
10106 | free(pid_str); | |||
10107 | } | |||
10108 | fprintf(outfile, "%s%s %lld +0000\n", GOT_COMMIT_LABEL_AUTHOR"author ", | |||
10109 | got_object_commit_get_author(commit), | |||
10110 | (long long)got_object_commit_get_author_time(commit)); | |||
10111 | ||||
10112 | fprintf(outfile, "%s%s %lld +0000\n", GOT_COMMIT_LABEL_COMMITTER"committer ", | |||
10113 | got_object_commit_get_author(commit), | |||
10114 | (long long)got_object_commit_get_committer_time(commit)); | |||
10115 | ||||
10116 | logmsg = got_object_commit_get_logmsg_raw(commit); | |||
10117 | fprintf(outfile, "messagelen %zd\n", strlen(logmsg)); | |||
10118 | fprintf(outfile, "%s", logmsg); | |||
10119 | done: | |||
10120 | free(id_str); | |||
10121 | got_object_commit_close(commit); | |||
10122 | return err; | |||
10123 | } | |||
10124 | ||||
10125 | static const struct got_error * | |||
10126 | cat_tag(struct got_object_id *id, struct got_repository *repo, FILE *outfile) | |||
10127 | { | |||
10128 | const struct got_error *err; | |||
10129 | struct got_tag_object *tag; | |||
10130 | char *id_str = NULL((void *)0); | |||
10131 | const char *tagmsg = NULL((void *)0); | |||
10132 | ||||
10133 | err = got_object_open_as_tag(&tag, repo, id); | |||
10134 | if (err) | |||
10135 | return err; | |||
10136 | ||||
10137 | err = got_object_id_str(&id_str, got_object_tag_get_object_id(tag)); | |||
10138 | if (err) | |||
10139 | goto done; | |||
10140 | ||||
10141 | fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_OBJECT"object ", id_str); | |||
10142 | ||||
10143 | switch (got_object_tag_get_object_type(tag)) { | |||
10144 | case GOT_OBJ_TYPE_BLOB3: | |||
10145 | fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TYPE"type ", | |||
10146 | GOT_OBJ_LABEL_BLOB"blob"); | |||
10147 | break; | |||
10148 | case GOT_OBJ_TYPE_TREE2: | |||
10149 | fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TYPE"type ", | |||
10150 | GOT_OBJ_LABEL_TREE"tree"); | |||
10151 | break; | |||
10152 | case GOT_OBJ_TYPE_COMMIT1: | |||
10153 | fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TYPE"type ", | |||
10154 | GOT_OBJ_LABEL_COMMIT"commit"); | |||
10155 | break; | |||
10156 | case GOT_OBJ_TYPE_TAG4: | |||
10157 | fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TYPE"type ", | |||
10158 | GOT_OBJ_LABEL_TAG"tag"); | |||
10159 | break; | |||
10160 | default: | |||
10161 | break; | |||
10162 | } | |||
10163 | ||||
10164 | fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TAG"tag ", | |||
10165 | got_object_tag_get_name(tag)); | |||
10166 | ||||
10167 | fprintf(outfile, "%s%s %lld +0000\n", GOT_TAG_LABEL_TAGGER"tagger ", | |||
10168 | got_object_tag_get_tagger(tag), | |||
10169 | (long long)got_object_tag_get_tagger_time(tag)); | |||
10170 | ||||
10171 | tagmsg = got_object_tag_get_message(tag); | |||
10172 | fprintf(outfile, "messagelen %zd\n", strlen(tagmsg)); | |||
10173 | fprintf(outfile, "%s", tagmsg); | |||
10174 | done: | |||
10175 | free(id_str); | |||
10176 | got_object_tag_close(tag); | |||
10177 | return err; | |||
10178 | } | |||
10179 | ||||
10180 | static const struct got_error * | |||
10181 | cmd_cat(int argc, char *argv[]) | |||
10182 | { | |||
10183 | const struct got_error *error; | |||
10184 | struct got_repository *repo = NULL((void *)0); | |||
10185 | struct got_worktree *worktree = NULL((void *)0); | |||
10186 | char *cwd = NULL((void *)0), *repo_path = NULL((void *)0), *label = NULL((void *)0); | |||
10187 | const char *commit_id_str = NULL((void *)0); | |||
10188 | struct got_object_id *id = NULL((void *)0), *commit_id = NULL((void *)0); | |||
10189 | int ch, obj_type, i, force_path = 0; | |||
10190 | struct got_reflist_head refs; | |||
10191 | ||||
10192 | TAILQ_INIT(&refs)do { (&refs)->tqh_first = ((void *)0); (&refs)-> tqh_last = &(&refs)->tqh_first; } while (0); | |||
10193 | ||||
10194 | #ifndef PROFILE | |||
10195 | if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil", | |||
10196 | NULL((void *)0)) == -1) | |||
10197 | err(1, "pledge"); | |||
10198 | #endif | |||
10199 | ||||
10200 | while ((ch = getopt(argc, argv, "c:r:P")) != -1) { | |||
10201 | switch (ch) { | |||
10202 | case 'c': | |||
10203 | commit_id_str = optarg; | |||
10204 | break; | |||
10205 | case 'r': | |||
10206 | repo_path = realpath(optarg, NULL((void *)0)); | |||
10207 | if (repo_path == NULL((void *)0)) | |||
10208 | return got_error_from_errno2("realpath", | |||
10209 | optarg); | |||
10210 | got_path_strip_trailing_slashes(repo_path); | |||
10211 | break; | |||
10212 | case 'P': | |||
10213 | force_path = 1; | |||
10214 | break; | |||
10215 | default: | |||
10216 | usage_cat(); | |||
10217 | /* NOTREACHED */ | |||
10218 | } | |||
10219 | } | |||
10220 | ||||
10221 | argc -= optind; | |||
10222 | argv += optind; | |||
10223 | ||||
10224 | cwd = getcwd(NULL((void *)0), 0); | |||
10225 | if (cwd == NULL((void *)0)) { | |||
10226 | error = got_error_from_errno("getcwd"); | |||
10227 | goto done; | |||
10228 | } | |||
10229 | error = got_worktree_open(&worktree, cwd); | |||
10230 | if (error && error->code != GOT_ERR_NOT_WORKTREE60) | |||
10231 | goto done; | |||
10232 | if (worktree) { | |||
10233 | if (repo_path == NULL((void *)0)) { | |||
10234 | repo_path = strdup( | |||
10235 | got_worktree_get_repo_path(worktree)); | |||
10236 | if (repo_path == NULL((void *)0)) { | |||
10237 | error = got_error_from_errno("strdup"); | |||
10238 | goto done; | |||
10239 | } | |||
10240 | } | |||
10241 | } | |||
10242 | ||||
10243 | if (repo_path == NULL((void *)0)) { | |||
10244 | repo_path = getcwd(NULL((void *)0), 0); | |||
10245 | if (repo_path == NULL((void *)0)) | |||
10246 | return got_error_from_errno("getcwd"); | |||
10247 | } | |||
10248 | ||||
10249 | error = got_repo_open(&repo, repo_path, NULL((void *)0)); | |||
10250 | free(repo_path); | |||
10251 | if (error != NULL((void *)0)) | |||
10252 | goto done; | |||
10253 | ||||
10254 | error = apply_unveil(got_repo_get_path(repo), 1, NULL((void *)0)); | |||
10255 | if (error) | |||
10256 | goto done; | |||
10257 | ||||
10258 | error = got_ref_list(&refs, repo, NULL((void *)0), got_ref_cmp_by_name, NULL((void *)0)); | |||
10259 | if (error) | |||
10260 | goto done; | |||
10261 | ||||
10262 | if (commit_id_str == NULL((void *)0)) | |||
10263 | commit_id_str = GOT_REF_HEAD"HEAD"; | |||
10264 | error = got_repo_match_object_id(&commit_id, NULL((void *)0), | |||
10265 | commit_id_str, GOT_OBJ_TYPE_COMMIT1, &refs, repo); | |||
10266 | if (error) | |||
10267 | goto done; | |||
10268 | ||||
10269 | for (i = 0; i < argc; i++) { | |||
10270 | if (force_path) { | |||
10271 | error = got_object_id_by_path(&id, repo, commit_id, | |||
10272 | argv[i]); | |||
10273 | if (error) | |||
10274 | break; | |||
10275 | } else { | |||
10276 | error = got_repo_match_object_id(&id, &label, argv[i], | |||
10277 | GOT_OBJ_TYPE_ANY0, NULL((void *)0) /* do not resolve tags */, | |||
10278 | repo); | |||
10279 | if (error) { | |||
10280 | if (error->code != GOT_ERR_BAD_OBJ_ID_STR23 && | |||
10281 | error->code != GOT_ERR_NOT_REF5) | |||
10282 | break; | |||
10283 | error = got_object_id_by_path(&id, repo, | |||
10284 | commit_id, argv[i]); | |||
10285 | if (error) | |||
10286 | break; | |||
10287 | } | |||
10288 | } | |||
10289 | ||||
10290 | error = got_object_get_type(&obj_type, repo, id); | |||
10291 | if (error) | |||
10292 | break; | |||
10293 | ||||
10294 | switch (obj_type) { | |||
10295 | case GOT_OBJ_TYPE_BLOB3: | |||
10296 | error = cat_blob(id, repo, stdout(&__sF[1])); | |||
10297 | break; | |||
10298 | case GOT_OBJ_TYPE_TREE2: | |||
10299 | error = cat_tree(id, repo, stdout(&__sF[1])); | |||
10300 | break; | |||
10301 | case GOT_OBJ_TYPE_COMMIT1: | |||
10302 | error = cat_commit(id, repo, stdout(&__sF[1])); | |||
10303 | break; | |||
10304 | case GOT_OBJ_TYPE_TAG4: | |||
10305 | error = cat_tag(id, repo, stdout(&__sF[1])); | |||
10306 | break; | |||
10307 | default: | |||
10308 | error = got_error(GOT_ERR_OBJ_TYPE11); | |||
10309 | break; | |||
10310 | } | |||
10311 | if (error) | |||
10312 | break; | |||
10313 | free(label); | |||
10314 | label = NULL((void *)0); | |||
10315 | free(id); | |||
10316 | id = NULL((void *)0); | |||
10317 | } | |||
10318 | done: | |||
10319 | free(label); | |||
10320 | free(id); | |||
10321 | free(commit_id); | |||
10322 | if (worktree) | |||
10323 | got_worktree_close(worktree); | |||
10324 | if (repo) { | |||
10325 | const struct got_error *repo_error; | |||
10326 | repo_error = got_repo_close(repo); | |||
10327 | if (error == NULL((void *)0)) | |||
10328 | error = repo_error; | |||
10329 | } | |||
10330 | got_ref_list_free(&refs); | |||
10331 | return error; | |||
10332 | } | |||
10333 | ||||
10334 | __dead__attribute__((__noreturn__)) static void | |||
10335 | usage_info(void) | |||
10336 | { | |||
10337 | fprintf(stderr(&__sF[2]), "usage: %s info [path ...]\n", | |||
10338 | getprogname()); | |||
10339 | exit(1); | |||
10340 | } | |||
10341 | ||||
10342 | static const struct got_error * | |||
10343 | print_path_info(void *arg, const char *path, mode_t mode, time_t mtime, | |||
10344 | struct got_object_id *blob_id, struct got_object_id *staged_blob_id, | |||
10345 | struct got_object_id *commit_id) | |||
10346 | { | |||
10347 | const struct got_error *err = NULL((void *)0); | |||
10348 | char *id_str = NULL((void *)0); | |||
10349 | char datebuf[128]; | |||
10350 | struct tm mytm, *tm; | |||
10351 | struct got_pathlist_head *paths = arg; | |||
10352 | struct got_pathlist_entry *pe; | |||
10353 | ||||
10354 | /* | |||
10355 | * Clear error indication from any of the path arguments which | |||
10356 | * would cause this file index entry to be displayed. | |||
10357 | */ | |||
10358 | TAILQ_FOREACH(pe, paths, entry)for((pe) = ((paths)->tqh_first); (pe) != ((void *)0); (pe) = ((pe)->entry.tqe_next)) { | |||
10359 | if (got_path_cmp(path, pe->path, strlen(path), | |||
10360 | pe->path_len) == 0 || | |||
10361 | got_path_is_child(path, pe->path, pe->path_len)) | |||
10362 | pe->data = NULL((void *)0); /* no error */ | |||
10363 | } | |||
10364 | ||||
10365 | printf(GOT_COMMIT_SEP_STR"-----------------------------------------------\n"); | |||
10366 | if (S_ISLNK(mode)((mode & 0170000) == 0120000)) | |||
10367 | printf("symlink: %s\n", path); | |||
10368 | else if (S_ISREG(mode)((mode & 0170000) == 0100000)) { | |||
10369 | printf("file: %s\n", path); | |||
10370 | printf("mode: %o\n", mode & (S_IRWXU0000700 | S_IRWXG0000070 | S_IRWXO0000007)); | |||
10371 | } else if (S_ISDIR(mode)((mode & 0170000) == 0040000)) | |||
10372 | printf("directory: %s\n", path); | |||
10373 | else | |||
10374 | printf("something: %s\n", path); | |||
10375 | ||||
10376 | tm = localtime_r(&mtime, &mytm); | |||
10377 | if (tm == NULL((void *)0)) | |||
10378 | return NULL((void *)0); | |||
10379 | if (strftime(datebuf, sizeof(datebuf), "%c %Z", tm) == 0) | |||
10380 | return got_error(GOT_ERR_NO_SPACE9); | |||
10381 | printf("timestamp: %s\n", datebuf); | |||
10382 | ||||
10383 | if (blob_id) { | |||
10384 | err = got_object_id_str(&id_str, blob_id); | |||
10385 | if (err) | |||
10386 | return err; | |||
10387 | printf("based on blob: %s\n", id_str); | |||
10388 | free(id_str); | |||
10389 | } | |||
10390 | ||||
10391 | if (staged_blob_id) { | |||
10392 | err = got_object_id_str(&id_str, staged_blob_id); | |||
10393 | if (err) | |||
10394 | return err; | |||
10395 | printf("based on staged blob: %s\n", id_str); | |||
10396 | free(id_str); | |||
10397 | } | |||
10398 | ||||
10399 | if (commit_id) { | |||
10400 | err = got_object_id_str(&id_str, commit_id); | |||
10401 | if (err) | |||
10402 | return err; | |||
10403 | printf("based on commit: %s\n", id_str); | |||
10404 | free(id_str); | |||
10405 | } | |||
10406 | ||||
10407 | return NULL((void *)0); | |||
10408 | } | |||
10409 | ||||
10410 | static const struct got_error * | |||
10411 | cmd_info(int argc, char *argv[]) | |||
10412 | { | |||
10413 | const struct got_error *error = NULL((void *)0); | |||
10414 | struct got_worktree *worktree = NULL((void *)0); | |||
10415 | char *cwd = NULL((void *)0), *id_str = NULL((void *)0); | |||
10416 | struct got_pathlist_head paths; | |||
10417 | struct got_pathlist_entry *pe; | |||
10418 | char *uuidstr = NULL((void *)0); | |||
10419 | int ch, show_files = 0; | |||
10420 | ||||
10421 | TAILQ_INIT(&paths)do { (&paths)->tqh_first = ((void *)0); (&paths)-> tqh_last = &(&paths)->tqh_first; } while (0); | |||
10422 | ||||
10423 | while ((ch = getopt(argc, argv, "")) != -1) { | |||
10424 | switch (ch) { | |||
10425 | default: | |||
10426 | usage_info(); | |||
10427 | /* NOTREACHED */ | |||
10428 | } | |||
10429 | } | |||
10430 | ||||
10431 | argc -= optind; | |||
10432 | argv += optind; | |||
10433 | ||||
10434 | #ifndef PROFILE | |||
10435 | if (pledge("stdio rpath wpath flock proc exec sendfd unveil", | |||
10436 | NULL((void *)0)) == -1) | |||
10437 | err(1, "pledge"); | |||
10438 | #endif | |||
10439 | cwd = getcwd(NULL((void *)0), 0); | |||
10440 | if (cwd == NULL((void *)0)) { | |||
10441 | error = got_error_from_errno("getcwd"); | |||
10442 | goto done; | |||
10443 | } | |||
10444 | ||||
10445 | error = got_worktree_open(&worktree, cwd); | |||
10446 | if (error) { | |||
10447 | if (error->code == GOT_ERR_NOT_WORKTREE60) | |||
10448 | error = wrap_not_worktree_error(error, "info", cwd); | |||
10449 | goto done; | |||
10450 | } | |||
10451 | ||||
10452 | error = apply_unveil(NULL((void *)0), 0, got_worktree_get_root_path(worktree)); | |||
10453 | if (error) | |||
10454 | goto done; | |||
10455 | ||||
10456 | if (argc >= 1) { | |||
10457 | error = get_worktree_paths_from_argv(&paths, argc, argv, | |||
10458 | worktree); | |||
10459 | if (error) | |||
10460 | goto done; | |||
10461 | show_files = 1; | |||
10462 | } | |||
10463 | ||||
10464 | error = got_object_id_str(&id_str, | |||
10465 | got_worktree_get_base_commit_id(worktree)); | |||
10466 | if (error) | |||
10467 | goto done; | |||
10468 | ||||
10469 | error = got_worktree_get_uuid(&uuidstr, worktree); | |||
10470 | if (error) | |||
10471 | goto done; | |||
10472 | ||||
10473 | printf("work tree: %s\n", got_worktree_get_root_path(worktree)); | |||
10474 | printf("work tree base commit: %s\n", id_str); | |||
10475 | printf("work tree path prefix: %s\n", | |||
10476 | got_worktree_get_path_prefix(worktree)); | |||
10477 | printf("work tree branch reference: %s\n", | |||
10478 | got_worktree_get_head_ref_name(worktree)); | |||
10479 | printf("work tree UUID: %s\n", uuidstr); | |||
10480 | printf("repository: %s\n", got_worktree_get_repo_path(worktree)); | |||
10481 | ||||
10482 | if (show_files) { | |||
10483 | struct got_pathlist_entry *pe; | |||
10484 | TAILQ_FOREACH(pe, &paths, entry)for((pe) = ((&paths)->tqh_first); (pe) != ((void *)0); (pe) = ((pe)->entry.tqe_next)) { | |||
10485 | if (pe->path_len == 0) | |||
10486 | continue; | |||
10487 | /* | |||
10488 | * Assume this path will fail. This will be corrected | |||
10489 | * in print_path_info() in case the path does suceeed. | |||
10490 | */ | |||
10491 | pe->data = (void *)got_error_path(pe->path, | |||
10492 | GOT_ERR_BAD_PATH4); | |||
10493 | } | |||
10494 | error = got_worktree_path_info(worktree, &paths, | |||
10495 | print_path_info, &paths, check_cancelled, NULL((void *)0)); | |||
10496 | if (error) | |||
10497 | goto done; | |||
10498 | TAILQ_FOREACH(pe, &paths, entry)for((pe) = ((&paths)->tqh_first); (pe) != ((void *)0); (pe) = ((pe)->entry.tqe_next)) { | |||
10499 | if (pe->data != NULL((void *)0)) { | |||
10500 | error = pe->data; /* bad path */ | |||
10501 | break; | |||
10502 | } | |||
10503 | } | |||
10504 | } | |||
10505 | done: | |||
10506 | TAILQ_FOREACH(pe, &paths, entry)for((pe) = ((&paths)->tqh_first); (pe) != ((void *)0); (pe) = ((pe)->entry.tqe_next)) | |||
10507 | free((char *)pe->path); | |||
10508 | got_pathlist_free(&paths); | |||
10509 | free(cwd); | |||
10510 | free(id_str); | |||
10511 | free(uuidstr); | |||
10512 | return error; | |||
10513 | } |