Blob


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 */
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>
25 #include <err.h>
26 #include <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>
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"
59 #ifndef nitems
60 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
61 #endif
63 static volatile sig_atomic_t sigint_received;
64 static volatile sig_atomic_t sigpipe_received;
66 static void
67 catch_sigint(int signo)
68 {
69 sigint_received = 1;
70 }
72 static void
73 catch_sigpipe(int signo)
74 {
75 sigpipe_received = 1;
76 }
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 };
86 __dead static void usage(int);
87 __dead static void usage_init(void);
88 __dead static void usage_import(void);
89 __dead static void usage_clone(void);
90 __dead static void usage_fetch(void);
91 __dead static void usage_checkout(void);
92 __dead static void usage_update(void);
93 __dead static void usage_log(void);
94 __dead static void usage_diff(void);
95 __dead static void usage_blame(void);
96 __dead static void usage_tree(void);
97 __dead static void usage_status(void);
98 __dead static void usage_ref(void);
99 __dead static void usage_branch(void);
100 __dead static void usage_tag(void);
101 __dead static void usage_add(void);
102 __dead static void usage_remove(void);
103 __dead static void usage_revert(void);
104 __dead static void usage_commit(void);
105 __dead static void usage_cherrypick(void);
106 __dead static void usage_backout(void);
107 __dead static void usage_rebase(void);
108 __dead static void usage_histedit(void);
109 __dead static void usage_integrate(void);
110 __dead static void usage_stage(void);
111 __dead static void usage_unstage(void);
112 __dead static void usage_cat(void);
113 __dead static void usage_info(void);
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 *[]);
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 };
173 static void
174 list_commands(void)
176 int i;
178 fprintf(stderr, "commands:");
179 for (i = 0; i < nitems(got_commands); i++) {
180 struct got_cmd *cmd = &got_commands[i];
181 fprintf(stderr, " %s", cmd->cmd_name);
183 fputc('\n', stderr);
186 int
187 main(int argc, char *argv[])
189 struct got_cmd *cmd;
190 unsigned int i;
191 int ch;
192 int hflag = 0, Vflag = 0;
193 static struct option longopts[] = {
194 { "version", no_argument, NULL, 'V' },
195 { NULL, 0, NULL, 0}
196 };
198 setlocale(LC_CTYPE, "");
200 while ((ch = getopt_long(argc, argv, "+hV", longopts, NULL)) != -1) {
201 switch (ch) {
202 case 'h':
203 hflag = 1;
204 break;
205 case 'V':
206 Vflag = 1;
207 break;
208 default:
209 usage(hflag);
210 /* NOTREACHED */
214 argc -= optind;
215 argv += optind;
216 optind = 0;
218 if (Vflag) {
219 got_version_print_str();
220 return 1;
223 if (argc <= 0)
224 usage(hflag);
226 signal(SIGINT, catch_sigint);
227 signal(SIGPIPE, catch_sigpipe);
229 for (i = 0; i < nitems(got_commands); i++) {
230 const struct got_error *error;
232 cmd = &got_commands[i];
234 if (strcmp(cmd->cmd_name, argv[0]) != 0 &&
235 strcmp(cmd->cmd_alias, argv[0]) != 0)
236 continue;
238 if (hflag)
239 got_commands[i].cmd_usage();
241 error = got_commands[i].cmd_main(argc, argv);
242 if (error && error->code != GOT_ERR_CANCELLED &&
243 error->code != GOT_ERR_PRIVSEP_EXIT &&
244 !(sigpipe_received &&
245 error->code == GOT_ERR_ERRNO && errno == EPIPE) &&
246 !(sigint_received &&
247 error->code == GOT_ERR_ERRNO && errno == EINTR)) {
248 fprintf(stderr, "%s: %s\n", getprogname(), error->msg);
249 return 1;
252 return 0;
255 fprintf(stderr, "%s: unknown command '%s'\n", getprogname(), argv[0]);
256 list_commands();
257 return 1;
260 __dead static void
261 usage(int hflag)
263 fprintf(stderr, "usage: %s [-h] [-V | --version] command [arg ...]\n",
264 getprogname());
265 if (hflag)
266 list_commands();
267 exit(1);
270 static const struct got_error *
271 get_editor(char **abspath)
273 const struct got_error *err = NULL;
274 const char *editor;
276 *abspath = NULL;
278 editor = getenv("VISUAL");
279 if (editor == NULL)
280 editor = getenv("EDITOR");
282 if (editor) {
283 err = got_path_find_prog(abspath, editor);
284 if (err)
285 return err;
288 if (*abspath == NULL) {
289 *abspath = strdup("/bin/ed");
290 if (*abspath == NULL)
291 return got_error_from_errno("strdup");
294 return NULL;
297 static const struct got_error *
298 apply_unveil(const char *repo_path, int repo_read_only,
299 const char *worktree_path)
301 const struct got_error *err;
303 #ifdef PROFILE
304 if (unveil("gmon.out", "rwc") != 0)
305 return got_error_from_errno2("unveil", "gmon.out");
306 #endif
307 if (repo_path && unveil(repo_path, repo_read_only ? "r" : "rwc") != 0)
308 return got_error_from_errno2("unveil", repo_path);
310 if (worktree_path && unveil(worktree_path, "rwc") != 0)
311 return got_error_from_errno2("unveil", worktree_path);
313 if (unveil(GOT_TMPDIR_STR, "rwc") != 0)
314 return got_error_from_errno2("unveil", GOT_TMPDIR_STR);
316 err = got_privsep_unveil_exec_helpers();
317 if (err != NULL)
318 return err;
320 if (unveil(NULL, NULL) != 0)
321 return got_error_from_errno("unveil");
323 return NULL;
326 __dead static void
327 usage_init(void)
329 fprintf(stderr, "usage: %s init repository-path\n", getprogname());
330 exit(1);
333 static const struct got_error *
334 cmd_init(int argc, char *argv[])
336 const struct got_error *error = NULL;
337 char *repo_path = NULL;
338 int ch;
340 while ((ch = getopt(argc, argv, "")) != -1) {
341 switch (ch) {
342 default:
343 usage_init();
344 /* NOTREACHED */
348 argc -= optind;
349 argv += optind;
351 #ifndef PROFILE
352 if (pledge("stdio rpath wpath cpath unveil", NULL) == -1)
353 err(1, "pledge");
354 #endif
355 if (argc != 1)
356 usage_init();
358 repo_path = strdup(argv[0]);
359 if (repo_path == NULL)
360 return got_error_from_errno("strdup");
362 got_path_strip_trailing_slashes(repo_path);
364 error = got_path_mkdir(repo_path);
365 if (error &&
366 !(error->code == GOT_ERR_ERRNO && errno == EEXIST))
367 goto done;
369 error = apply_unveil(repo_path, 0, NULL);
370 if (error)
371 goto done;
373 error = got_repo_init(repo_path);
374 done:
375 free(repo_path);
376 return error;
379 __dead static void
380 usage_import(void)
382 fprintf(stderr, "usage: %s import [-b branch] [-m message] "
383 "[-r repository-path] [-I pattern] path\n", getprogname());
384 exit(1);
387 int
388 spawn_editor(const char *editor, const char *file)
390 pid_t pid;
391 sig_t sighup, sigint, sigquit;
392 int st = -1;
394 sighup = signal(SIGHUP, SIG_IGN);
395 sigint = signal(SIGINT, SIG_IGN);
396 sigquit = signal(SIGQUIT, SIG_IGN);
398 switch (pid = fork()) {
399 case -1:
400 goto doneediting;
401 case 0:
402 execl(editor, editor, file, (char *)NULL);
403 _exit(127);
406 while (waitpid(pid, &st, 0) == -1)
407 if (errno != EINTR)
408 break;
410 doneediting:
411 (void)signal(SIGHUP, sighup);
412 (void)signal(SIGINT, sigint);
413 (void)signal(SIGQUIT, sigquit);
415 if (!WIFEXITED(st)) {
416 errno = EINTR;
417 return -1;
420 return WEXITSTATUS(st);
423 static const struct got_error *
424 edit_logmsg(char **logmsg, const char *editor, const char *logmsg_path,
425 const char *initial_content)
427 const struct got_error *err = NULL;
428 char buf[1024];
429 struct stat st, st2;
430 FILE *fp;
431 int content_changed = 0;
432 size_t len;
434 *logmsg = NULL;
436 if (stat(logmsg_path, &st) == -1)
437 return got_error_from_errno2("stat", logmsg_path);
439 if (spawn_editor(editor, logmsg_path) == -1)
440 return got_error_from_errno("failed spawning editor");
442 if (stat(logmsg_path, &st2) == -1)
443 return got_error_from_errno("stat");
445 if (st.st_mtime == st2.st_mtime && st.st_size == st2.st_size)
446 return got_error_msg(GOT_ERR_COMMIT_MSG_EMPTY,
447 "no changes made to commit message, aborting");
449 *logmsg = malloc(st2.st_size + 1);
450 if (*logmsg == NULL)
451 return got_error_from_errno("malloc");
452 (*logmsg)[0] = '\0';
453 len = 0;
455 fp = fopen(logmsg_path, "r");
456 if (fp == NULL) {
457 err = got_error_from_errno("fopen");
458 goto done;
460 while (fgets(buf, sizeof(buf), fp) != NULL) {
461 if (!content_changed && strcmp(buf, initial_content) != 0)
462 content_changed = 1;
463 if (buf[0] == '#' || (len == 0 && buf[0] == '\n'))
464 continue; /* remove comments and leading empty lines */
465 len = strlcat(*logmsg, buf, st2.st_size);
467 fclose(fp);
469 while (len > 0 && (*logmsg)[len - 1] == '\n') {
470 (*logmsg)[len - 1] = '\0';
471 len--;
474 if (len == 0 || !content_changed)
475 err = got_error_msg(GOT_ERR_COMMIT_MSG_EMPTY,
476 "commit message cannot be empty, aborting");
477 done:
478 if (err) {
479 free(*logmsg);
480 *logmsg = NULL;
482 return err;
485 static const struct got_error *
486 collect_import_msg(char **logmsg, char **logmsg_path, const char *editor,
487 const char *path_dir, const char *branch_name)
489 char *initial_content = NULL;
490 const struct got_error *err = NULL;
491 int initial_content_len;
492 int fd;
494 initial_content_len = asprintf(&initial_content,
495 "\n# %s to be imported to branch %s\n", path_dir,
496 branch_name);
497 if (initial_content_len == -1)
498 return got_error_from_errno("asprintf");
500 err = got_opentemp_named_fd(logmsg_path, &fd,
501 GOT_TMPDIR_STR "/got-importmsg");
502 if (err)
503 goto done;
505 write(fd, initial_content, initial_content_len);
506 close(fd);
508 err = edit_logmsg(logmsg, editor, *logmsg_path, initial_content);
509 done:
510 free(initial_content);
511 return err;
514 static const struct got_error *
515 import_progress(void *arg, const char *path)
517 printf("A %s\n", path);
518 return NULL;
521 static const struct got_error *
522 get_author(char **author, struct got_repository *repo,
523 struct got_worktree *worktree)
525 const struct got_error *err = NULL;
526 const char *got_author = NULL, *name, *email;
527 const struct got_gotconfig *worktree_conf = NULL, *repo_conf = NULL;
529 *author = NULL;
531 if (worktree)
532 worktree_conf = got_worktree_get_gotconfig(worktree);
533 repo_conf = got_repo_get_gotconfig(repo);
535 /*
536 * Priority of potential author information sources, from most
537 * significant to least significant:
538 * 1) work tree's .got/got.conf file
539 * 2) repository's got.conf file
540 * 3) repository's git config file
541 * 4) environment variables
542 * 5) global git config files (in user's home directory or /etc)
543 */
545 if (worktree_conf)
546 got_author = got_gotconfig_get_author(worktree_conf);
547 if (got_author == NULL)
548 got_author = got_gotconfig_get_author(repo_conf);
549 if (got_author == NULL) {
550 name = got_repo_get_gitconfig_author_name(repo);
551 email = got_repo_get_gitconfig_author_email(repo);
552 if (name && email) {
553 if (asprintf(author, "%s <%s>", name, email) == -1)
554 return got_error_from_errno("asprintf");
555 return NULL;
558 got_author = getenv("GOT_AUTHOR");
559 if (got_author == NULL) {
560 name = got_repo_get_global_gitconfig_author_name(repo);
561 email = got_repo_get_global_gitconfig_author_email(
562 repo);
563 if (name && email) {
564 if (asprintf(author, "%s <%s>", name, email)
565 == -1)
566 return got_error_from_errno("asprintf");
567 return NULL;
569 /* TODO: Look up user in password database? */
570 return got_error(GOT_ERR_COMMIT_NO_AUTHOR);
574 *author = strdup(got_author);
575 if (*author == NULL)
576 return got_error_from_errno("strdup");
578 /*
579 * Really dumb email address check; we're only doing this to
580 * avoid git's object parser breaking on commits we create.
581 */
582 while (*got_author && *got_author != '<')
583 got_author++;
584 if (*got_author != '<') {
585 err = got_error(GOT_ERR_COMMIT_NO_EMAIL);
586 goto done;
588 while (*got_author && *got_author != '@')
589 got_author++;
590 if (*got_author != '@') {
591 err = got_error(GOT_ERR_COMMIT_NO_EMAIL);
592 goto done;
594 while (*got_author && *got_author != '>')
595 got_author++;
596 if (*got_author != '>')
597 err = got_error(GOT_ERR_COMMIT_NO_EMAIL);
598 done:
599 if (err) {
600 free(*author);
601 *author = NULL;
603 return err;
606 static const struct got_error *
607 get_gitconfig_path(char **gitconfig_path)
609 const char *homedir = getenv("HOME");
611 *gitconfig_path = NULL;
612 if (homedir) {
613 if (asprintf(gitconfig_path, "%s/.gitconfig", homedir) == -1)
614 return got_error_from_errno("asprintf");
617 return NULL;
620 static const struct got_error *
621 cmd_import(int argc, char *argv[])
623 const struct got_error *error = NULL;
624 char *path_dir = NULL, *repo_path = NULL, *logmsg = NULL;
625 char *gitconfig_path = NULL, *editor = NULL, *author = NULL;
626 const char *branch_name = "main";
627 char *refname = NULL, *id_str = NULL, *logmsg_path = NULL;
628 struct got_repository *repo = NULL;
629 struct got_reference *branch_ref = NULL, *head_ref = NULL;
630 struct got_object_id *new_commit_id = NULL;
631 int ch;
632 struct got_pathlist_head ignores;
633 struct got_pathlist_entry *pe;
634 int preserve_logmsg = 0;
636 TAILQ_INIT(&ignores);
638 while ((ch = getopt(argc, argv, "b:m:r:I:")) != -1) {
639 switch (ch) {
640 case 'b':
641 branch_name = optarg;
642 break;
643 case 'm':
644 logmsg = strdup(optarg);
645 if (logmsg == NULL) {
646 error = got_error_from_errno("strdup");
647 goto done;
649 break;
650 case 'r':
651 repo_path = realpath(optarg, NULL);
652 if (repo_path == NULL) {
653 error = got_error_from_errno2("realpath",
654 optarg);
655 goto done;
657 break;
658 case 'I':
659 if (optarg[0] == '\0')
660 break;
661 error = got_pathlist_insert(&pe, &ignores, optarg,
662 NULL);
663 if (error)
664 goto done;
665 break;
666 default:
667 usage_import();
668 /* NOTREACHED */
672 argc -= optind;
673 argv += optind;
675 #ifndef PROFILE
676 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
677 "unveil",
678 NULL) == -1)
679 err(1, "pledge");
680 #endif
681 if (argc != 1)
682 usage_import();
684 if (repo_path == NULL) {
685 repo_path = getcwd(NULL, 0);
686 if (repo_path == NULL)
687 return got_error_from_errno("getcwd");
689 got_path_strip_trailing_slashes(repo_path);
690 error = get_gitconfig_path(&gitconfig_path);
691 if (error)
692 goto done;
693 error = got_repo_open(&repo, repo_path, gitconfig_path);
694 if (error)
695 goto done;
697 error = get_author(&author, repo, NULL);
698 if (error)
699 return error;
701 /*
702 * Don't let the user create a branch name with a leading '-'.
703 * While technically a valid reference name, this case is usually
704 * an unintended typo.
705 */
706 if (branch_name[0] == '-')
707 return got_error_path(branch_name, GOT_ERR_REF_NAME_MINUS);
709 if (asprintf(&refname, "refs/heads/%s", branch_name) == -1) {
710 error = got_error_from_errno("asprintf");
711 goto done;
714 error = got_ref_open(&branch_ref, repo, refname, 0);
715 if (error) {
716 if (error->code != GOT_ERR_NOT_REF)
717 goto done;
718 } else {
719 error = got_error_msg(GOT_ERR_BRANCH_EXISTS,
720 "import target branch already exists");
721 goto done;
724 path_dir = realpath(argv[0], NULL);
725 if (path_dir == NULL) {
726 error = got_error_from_errno2("realpath", argv[0]);
727 goto done;
729 got_path_strip_trailing_slashes(path_dir);
731 /*
732 * unveil(2) traverses exec(2); if an editor is used we have
733 * to apply unveil after the log message has been written.
734 */
735 if (logmsg == NULL || strlen(logmsg) == 0) {
736 error = get_editor(&editor);
737 if (error)
738 goto done;
739 free(logmsg);
740 error = collect_import_msg(&logmsg, &logmsg_path, editor,
741 path_dir, refname);
742 if (error) {
743 if (error->code != GOT_ERR_COMMIT_MSG_EMPTY &&
744 logmsg_path != NULL)
745 preserve_logmsg = 1;
746 goto done;
750 if (unveil(path_dir, "r") != 0) {
751 error = got_error_from_errno2("unveil", path_dir);
752 if (logmsg_path)
753 preserve_logmsg = 1;
754 goto done;
757 error = apply_unveil(got_repo_get_path(repo), 0, NULL);
758 if (error) {
759 if (logmsg_path)
760 preserve_logmsg = 1;
761 goto done;
764 error = got_repo_import(&new_commit_id, path_dir, logmsg,
765 author, &ignores, repo, import_progress, NULL);
766 if (error) {
767 if (logmsg_path)
768 preserve_logmsg = 1;
769 goto done;
772 error = got_ref_alloc(&branch_ref, refname, new_commit_id);
773 if (error) {
774 if (logmsg_path)
775 preserve_logmsg = 1;
776 goto done;
779 error = got_ref_write(branch_ref, repo);
780 if (error) {
781 if (logmsg_path)
782 preserve_logmsg = 1;
783 goto done;
786 error = got_object_id_str(&id_str, new_commit_id);
787 if (error) {
788 if (logmsg_path)
789 preserve_logmsg = 1;
790 goto done;
793 error = got_ref_open(&head_ref, repo, GOT_REF_HEAD, 0);
794 if (error) {
795 if (error->code != GOT_ERR_NOT_REF) {
796 if (logmsg_path)
797 preserve_logmsg = 1;
798 goto done;
801 error = got_ref_alloc_symref(&head_ref, GOT_REF_HEAD,
802 branch_ref);
803 if (error) {
804 if (logmsg_path)
805 preserve_logmsg = 1;
806 goto done;
809 error = got_ref_write(head_ref, repo);
810 if (error) {
811 if (logmsg_path)
812 preserve_logmsg = 1;
813 goto done;
817 printf("Created branch %s with commit %s\n",
818 got_ref_get_name(branch_ref), id_str);
819 done:
820 if (preserve_logmsg) {
821 fprintf(stderr, "%s: log message preserved in %s\n",
822 getprogname(), logmsg_path);
823 } else if (logmsg_path && unlink(logmsg_path) == -1 && error == NULL)
824 error = got_error_from_errno2("unlink", logmsg_path);
825 free(logmsg);
826 free(logmsg_path);
827 free(repo_path);
828 free(editor);
829 free(refname);
830 free(new_commit_id);
831 free(id_str);
832 free(author);
833 free(gitconfig_path);
834 if (branch_ref)
835 got_ref_close(branch_ref);
836 if (head_ref)
837 got_ref_close(head_ref);
838 return error;
841 __dead static void
842 usage_clone(void)
844 fprintf(stderr, "usage: %s clone [-a] [-b branch] [-l] [-m] [-q] [-v] "
845 "[-R reference] repository-url [directory]\n", getprogname());
846 exit(1);
849 struct got_fetch_progress_arg {
850 char last_scaled_size[FMT_SCALED_STRSIZE];
851 int last_p_indexed;
852 int last_p_resolved;
853 int verbosity;
854 };
856 static const struct got_error *
857 fetch_progress(void *arg, const char *message, off_t packfile_size,
858 int nobj_total, int nobj_indexed, int nobj_loose, int nobj_resolved)
860 struct got_fetch_progress_arg *a = arg;
861 char scaled_size[FMT_SCALED_STRSIZE];
862 int p_indexed, p_resolved;
863 int print_size = 0, print_indexed = 0, print_resolved = 0;
865 if (a->verbosity < 0)
866 return NULL;
868 if (message && message[0] != '\0') {
869 printf("\rserver: %s", message);
870 fflush(stdout);
871 return NULL;
874 if (packfile_size > 0 || nobj_indexed > 0) {
875 if (fmt_scaled(packfile_size, scaled_size) == 0 &&
876 (a->last_scaled_size[0] == '\0' ||
877 strcmp(scaled_size, a->last_scaled_size)) != 0) {
878 print_size = 1;
879 if (strlcpy(a->last_scaled_size, scaled_size,
880 FMT_SCALED_STRSIZE) >= FMT_SCALED_STRSIZE)
881 return got_error(GOT_ERR_NO_SPACE);
883 if (nobj_indexed > 0) {
884 p_indexed = (nobj_indexed * 100) / nobj_total;
885 if (p_indexed != a->last_p_indexed) {
886 a->last_p_indexed = p_indexed;
887 print_indexed = 1;
888 print_size = 1;
891 if (nobj_resolved > 0) {
892 p_resolved = (nobj_resolved * 100) /
893 (nobj_total - nobj_loose);
894 if (p_resolved != a->last_p_resolved) {
895 a->last_p_resolved = p_resolved;
896 print_resolved = 1;
897 print_indexed = 1;
898 print_size = 1;
903 if (print_size || print_indexed || print_resolved)
904 printf("\r");
905 if (print_size)
906 printf("%*s fetched", FMT_SCALED_STRSIZE, scaled_size);
907 if (print_indexed)
908 printf("; indexing %d%%", p_indexed);
909 if (print_resolved)
910 printf("; resolving deltas %d%%", p_resolved);
911 if (print_size || print_indexed || print_resolved)
912 fflush(stdout);
914 return NULL;
917 static const struct got_error *
918 create_symref(const char *refname, struct got_reference *target_ref,
919 int verbosity, struct got_repository *repo)
921 const struct got_error *err;
922 struct got_reference *head_symref;
924 err = got_ref_alloc_symref(&head_symref, refname, target_ref);
925 if (err)
926 return err;
928 err = got_ref_write(head_symref, repo);
929 got_ref_close(head_symref);
930 if (err == NULL && verbosity > 0) {
931 printf("Created reference %s: %s\n", GOT_REF_HEAD,
932 got_ref_get_name(target_ref));
934 return err;
937 static const struct got_error *
938 list_remote_refs(struct got_pathlist_head *symrefs,
939 struct got_pathlist_head *refs)
941 const struct got_error *err;
942 struct got_pathlist_entry *pe;
944 TAILQ_FOREACH(pe, symrefs, entry) {
945 const char *refname = pe->path;
946 const char *targetref = pe->data;
948 printf("%s: %s\n", refname, targetref);
951 TAILQ_FOREACH(pe, refs, entry) {
952 const char *refname = pe->path;
953 struct got_object_id *id = pe->data;
954 char *id_str;
956 err = got_object_id_str(&id_str, id);
957 if (err)
958 return err;
959 printf("%s: %s\n", refname, id_str);
960 free(id_str);
963 return NULL;
966 static const struct got_error *
967 create_ref(const char *refname, struct got_object_id *id,
968 int verbosity, struct got_repository *repo)
970 const struct got_error *err = NULL;
971 struct got_reference *ref;
972 char *id_str;
974 err = got_object_id_str(&id_str, id);
975 if (err)
976 return err;
978 err = got_ref_alloc(&ref, refname, id);
979 if (err)
980 goto done;
982 err = got_ref_write(ref, repo);
983 got_ref_close(ref);
985 if (err == NULL && verbosity >= 0)
986 printf("Created reference %s: %s\n", refname, id_str);
987 done:
988 free(id_str);
989 return err;
992 static int
993 match_wanted_ref(const char *refname, const char *wanted_ref)
995 if (strncmp(refname, "refs/", 5) != 0)
996 return 0;
997 refname += 5;
999 /*
1000 * Prevent fetching of references that won't make any
1001 * sense outside of the remote repository's context.
1003 if (strncmp(refname, "got/", 4) == 0)
1004 return 0;
1005 if (strncmp(refname, "remotes/", 8) == 0)
1006 return 0;
1008 if (strncmp(wanted_ref, "refs/", 5) == 0)
1009 wanted_ref += 5;
1011 /* Allow prefix match. */
1012 if (got_path_is_child(refname, wanted_ref, strlen(wanted_ref)))
1013 return 1;
1015 /* Allow exact match. */
1016 return (strcmp(refname, wanted_ref) == 0);
1019 static int
1020 is_wanted_ref(struct got_pathlist_head *wanted_refs, const char *refname)
1022 struct got_pathlist_entry *pe;
1024 TAILQ_FOREACH(pe, wanted_refs, entry) {
1025 if (match_wanted_ref(refname, pe->path))
1026 return 1;
1029 return 0;
1032 static const struct got_error *
1033 create_wanted_ref(const char *refname, struct got_object_id *id,
1034 const char *remote_repo_name, int verbosity, struct got_repository *repo)
1036 const struct got_error *err;
1037 char *remote_refname;
1039 if (strncmp("refs/", refname, 5) == 0)
1040 refname += 5;
1042 if (asprintf(&remote_refname, "refs/remotes/%s/%s",
1043 remote_repo_name, refname) == -1)
1044 return got_error_from_errno("asprintf");
1046 err = create_ref(remote_refname, id, verbosity, repo);
1047 free(remote_refname);
1048 return err;
1051 static const struct got_error *
1052 cmd_clone(int argc, char *argv[])
1054 const struct got_error *error = NULL;
1055 const char *uri, *dirname;
1056 char *proto, *host, *port, *repo_name, *server_path;
1057 char *default_destdir = NULL, *id_str = NULL;
1058 const char *repo_path;
1059 struct got_repository *repo = NULL;
1060 struct got_pathlist_head refs, symrefs, wanted_branches, wanted_refs;
1061 struct got_pathlist_entry *pe;
1062 struct got_object_id *pack_hash = NULL;
1063 int ch, fetchfd = -1, fetchstatus;
1064 pid_t fetchpid = -1;
1065 struct got_fetch_progress_arg fpa;
1066 char *git_url = NULL;
1067 char *gitconfig_path = NULL, *gotconfig_path = NULL;
1068 char *gitconfig = NULL, *gotconfig = NULL;
1069 FILE *gitconfig_file = NULL, *gotconfig_file = NULL;
1070 ssize_t n;
1071 int verbosity = 0, fetch_all_branches = 0, mirror_references = 0;
1072 int list_refs_only = 0;
1073 struct got_reference *head_symref = NULL;
1075 TAILQ_INIT(&refs);
1076 TAILQ_INIT(&symrefs);
1077 TAILQ_INIT(&wanted_branches);
1078 TAILQ_INIT(&wanted_refs);
1080 while ((ch = getopt(argc, argv, "ab:lmvqR:")) != -1) {
1081 switch (ch) {
1082 case 'a':
1083 fetch_all_branches = 1;
1084 break;
1085 case 'b':
1086 error = got_pathlist_append(&wanted_branches,
1087 optarg, NULL);
1088 if (error)
1089 return error;
1090 break;
1091 case 'l':
1092 list_refs_only = 1;
1093 break;
1094 case 'm':
1095 mirror_references = 1;
1096 break;
1097 case 'v':
1098 if (verbosity < 0)
1099 verbosity = 0;
1100 else if (verbosity < 3)
1101 verbosity++;
1102 break;
1103 case 'q':
1104 verbosity = -1;
1105 break;
1106 case 'R':
1107 error = got_pathlist_append(&wanted_refs,
1108 optarg, NULL);
1109 if (error)
1110 return error;
1111 break;
1112 default:
1113 usage_clone();
1114 break;
1117 argc -= optind;
1118 argv += optind;
1120 if (fetch_all_branches && !TAILQ_EMPTY(&wanted_branches))
1121 errx(1, "-a and -b options are mutually exclusive");
1122 if (list_refs_only) {
1123 if (!TAILQ_EMPTY(&wanted_branches))
1124 errx(1, "-l and -b options are mutually exclusive");
1125 if (fetch_all_branches)
1126 errx(1, "-l and -a options are mutually exclusive");
1127 if (mirror_references)
1128 errx(1, "-l and -m options are mutually exclusive");
1129 if (verbosity == -1)
1130 errx(1, "-l and -q options are mutually exclusive");
1131 if (!TAILQ_EMPTY(&wanted_refs))
1132 errx(1, "-l and -R options are mutually exclusive");
1135 uri = argv[0];
1137 if (argc == 1)
1138 dirname = NULL;
1139 else if (argc == 2)
1140 dirname = argv[1];
1141 else
1142 usage_clone();
1144 error = got_fetch_parse_uri(&proto, &host, &port, &server_path,
1145 &repo_name, uri);
1146 if (error)
1147 goto done;
1149 if (asprintf(&git_url, "%s://%s%s%s%s%s", proto,
1150 host, port ? ":" : "", port ? port : "",
1151 server_path[0] != '/' ? "/" : "", server_path) == -1) {
1152 error = got_error_from_errno("asprintf");
1153 goto done;
1156 if (strcmp(proto, "git") == 0) {
1157 #ifndef PROFILE
1158 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
1159 "sendfd dns inet unveil", NULL) == -1)
1160 err(1, "pledge");
1161 #endif
1162 } else if (strcmp(proto, "git+ssh") == 0 ||
1163 strcmp(proto, "ssh") == 0) {
1164 #ifndef PROFILE
1165 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
1166 "sendfd unveil", NULL) == -1)
1167 err(1, "pledge");
1168 #endif
1169 } else if (strcmp(proto, "http") == 0 ||
1170 strcmp(proto, "git+http") == 0) {
1171 error = got_error_path(proto, GOT_ERR_NOT_IMPL);
1172 goto done;
1173 } else {
1174 error = got_error_path(proto, GOT_ERR_BAD_PROTO);
1175 goto done;
1177 if (dirname == NULL) {
1178 if (asprintf(&default_destdir, "%s.git", repo_name) == -1) {
1179 error = got_error_from_errno("asprintf");
1180 goto done;
1182 repo_path = default_destdir;
1183 } else
1184 repo_path = dirname;
1186 if (!list_refs_only) {
1187 error = got_path_mkdir(repo_path);
1188 if (error)
1189 goto done;
1191 error = got_repo_init(repo_path);
1192 if (error)
1193 goto done;
1194 error = got_repo_open(&repo, repo_path, NULL);
1195 if (error)
1196 goto done;
1199 if (strcmp(proto, "git+ssh") == 0 || strcmp(proto, "ssh") == 0) {
1200 if (unveil(GOT_FETCH_PATH_SSH, "x") != 0) {
1201 error = got_error_from_errno2("unveil",
1202 GOT_FETCH_PATH_SSH);
1203 goto done;
1206 error = apply_unveil(repo ? got_repo_get_path(repo) : NULL, 0, NULL);
1207 if (error)
1208 goto done;
1210 if (verbosity >= 0)
1211 printf("Connecting to %s%s%s\n", host,
1212 port ? ":" : "", port ? port : "");
1214 error = got_fetch_connect(&fetchpid, &fetchfd, proto, host, port,
1215 server_path, verbosity);
1216 if (error)
1217 goto done;
1219 fpa.last_scaled_size[0] = '\0';
1220 fpa.last_p_indexed = -1;
1221 fpa.last_p_resolved = -1;
1222 fpa.verbosity = verbosity;
1223 error = got_fetch_pack(&pack_hash, &refs, &symrefs,
1224 GOT_FETCH_DEFAULT_REMOTE_NAME, mirror_references,
1225 fetch_all_branches, &wanted_branches, &wanted_refs,
1226 list_refs_only, verbosity, fetchfd, repo,
1227 fetch_progress, &fpa);
1228 if (error)
1229 goto done;
1231 if (list_refs_only) {
1232 error = list_remote_refs(&symrefs, &refs);
1233 goto done;
1236 error = got_object_id_str(&id_str, pack_hash);
1237 if (error)
1238 goto done;
1239 if (verbosity >= 0)
1240 printf("\nFetched %s.pack\n", id_str);
1241 free(id_str);
1243 /* Set up references provided with the pack file. */
1244 TAILQ_FOREACH(pe, &refs, entry) {
1245 const char *refname = pe->path;
1246 struct got_object_id *id = pe->data;
1247 char *remote_refname;
1249 if (is_wanted_ref(&wanted_refs, refname) &&
1250 !mirror_references) {
1251 error = create_wanted_ref(refname, id,
1252 GOT_FETCH_DEFAULT_REMOTE_NAME,
1253 verbosity - 1, repo);
1254 if (error)
1255 goto done;
1256 continue;
1259 error = create_ref(refname, id, verbosity - 1, repo);
1260 if (error)
1261 goto done;
1263 if (mirror_references)
1264 continue;
1266 if (strncmp("refs/heads/", refname, 11) != 0)
1267 continue;
1269 if (asprintf(&remote_refname,
1270 "refs/remotes/%s/%s", GOT_FETCH_DEFAULT_REMOTE_NAME,
1271 refname + 11) == -1) {
1272 error = got_error_from_errno("asprintf");
1273 goto done;
1275 error = create_ref(remote_refname, id, verbosity - 1, repo);
1276 free(remote_refname);
1277 if (error)
1278 goto done;
1281 /* Set the HEAD reference if the server provided one. */
1282 TAILQ_FOREACH(pe, &symrefs, entry) {
1283 struct got_reference *target_ref;
1284 const char *refname = pe->path;
1285 const char *target = pe->data;
1286 char *remote_refname = NULL, *remote_target = NULL;
1288 if (strcmp(refname, GOT_REF_HEAD) != 0)
1289 continue;
1291 error = got_ref_open(&target_ref, repo, target, 0);
1292 if (error) {
1293 if (error->code == GOT_ERR_NOT_REF) {
1294 error = NULL;
1295 continue;
1297 goto done;
1300 error = create_symref(refname, target_ref, verbosity, repo);
1301 got_ref_close(target_ref);
1302 if (error)
1303 goto done;
1305 if (mirror_references)
1306 continue;
1308 if (strncmp("refs/heads/", target, 11) != 0)
1309 continue;
1311 if (asprintf(&remote_refname,
1312 "refs/remotes/%s/%s", GOT_FETCH_DEFAULT_REMOTE_NAME,
1313 refname) == -1) {
1314 error = got_error_from_errno("asprintf");
1315 goto done;
1317 if (asprintf(&remote_target,
1318 "refs/remotes/%s/%s", GOT_FETCH_DEFAULT_REMOTE_NAME,
1319 target + 11) == -1) {
1320 error = got_error_from_errno("asprintf");
1321 free(remote_refname);
1322 goto done;
1324 error = got_ref_open(&target_ref, repo, remote_target, 0);
1325 if (error) {
1326 free(remote_refname);
1327 free(remote_target);
1328 if (error->code == GOT_ERR_NOT_REF) {
1329 error = NULL;
1330 continue;
1332 goto done;
1334 error = create_symref(remote_refname, target_ref,
1335 verbosity - 1, repo);
1336 free(remote_refname);
1337 free(remote_target);
1338 got_ref_close(target_ref);
1339 if (error)
1340 goto done;
1342 if (pe == NULL) {
1344 * We failed to set the HEAD reference. If we asked for
1345 * a set of wanted branches use the first of one of those
1346 * which could be fetched instead.
1348 TAILQ_FOREACH(pe, &wanted_branches, entry) {
1349 const char *target = pe->path;
1350 struct got_reference *target_ref;
1352 error = got_ref_open(&target_ref, repo, target, 0);
1353 if (error) {
1354 if (error->code == GOT_ERR_NOT_REF) {
1355 error = NULL;
1356 continue;
1358 goto done;
1361 error = create_symref(GOT_REF_HEAD, target_ref,
1362 verbosity, repo);
1363 got_ref_close(target_ref);
1364 if (error)
1365 goto done;
1366 break;
1370 /* Create got.conf(5). */
1371 gotconfig_path = got_repo_get_path_gotconfig(repo);
1372 if (gotconfig_path == NULL) {
1373 error = got_error_from_errno("got_repo_get_path_gotconfig");
1374 goto done;
1376 gotconfig_file = fopen(gotconfig_path, "a");
1377 if (gotconfig_file == NULL) {
1378 error = got_error_from_errno2("fopen", gotconfig_path);
1379 goto done;
1381 got_path_strip_trailing_slashes(server_path);
1382 if (asprintf(&gotconfig,
1383 "remote \"%s\" {\n"
1384 "\tserver %s\n"
1385 "\tprotocol %s\n"
1386 "%s%s%s"
1387 "\trepository \"%s\"\n"
1388 "%s"
1389 "}\n",
1390 GOT_FETCH_DEFAULT_REMOTE_NAME, host, proto,
1391 port ? "\tport " : "", port ? port : "", port ? "\n" : "",
1392 server_path,
1393 mirror_references ? "\tmirror-references yes\n" : "") == -1) {
1394 error = got_error_from_errno("asprintf");
1395 goto done;
1397 n = fwrite(gotconfig, 1, strlen(gotconfig), gotconfig_file);
1398 if (n != strlen(gotconfig)) {
1399 error = got_ferror(gotconfig_file, GOT_ERR_IO);
1400 goto done;
1403 /* Create a config file Git can understand. */
1404 gitconfig_path = got_repo_get_path_gitconfig(repo);
1405 if (gitconfig_path == NULL) {
1406 error = got_error_from_errno("got_repo_get_path_gitconfig");
1407 goto done;
1409 gitconfig_file = fopen(gitconfig_path, "a");
1410 if (gitconfig_file == NULL) {
1411 error = got_error_from_errno2("fopen", gitconfig_path);
1412 goto done;
1414 if (mirror_references) {
1415 if (asprintf(&gitconfig,
1416 "[remote \"%s\"]\n"
1417 "\turl = %s\n"
1418 "\tfetch = +refs/*:refs/*\n"
1419 "\tmirror = true\n",
1420 GOT_FETCH_DEFAULT_REMOTE_NAME, git_url) == -1) {
1421 error = got_error_from_errno("asprintf");
1422 goto done;
1424 } else if (fetch_all_branches) {
1425 if (asprintf(&gitconfig,
1426 "[remote \"%s\"]\n"
1427 "\turl = %s\n"
1428 "\tfetch = +refs/heads/*:refs/remotes/%s/*\n",
1429 GOT_FETCH_DEFAULT_REMOTE_NAME, git_url,
1430 GOT_FETCH_DEFAULT_REMOTE_NAME) == -1) {
1431 error = got_error_from_errno("asprintf");
1432 goto done;
1434 } else {
1435 const char *branchname;
1438 * If the server specified a default branch, use just that one.
1439 * Otherwise fall back to fetching all branches on next fetch.
1441 if (head_symref) {
1442 branchname = got_ref_get_symref_target(head_symref);
1443 if (strncmp(branchname, "refs/heads/", 11) == 0)
1444 branchname += 11;
1445 } else
1446 branchname = "*"; /* fall back to all branches */
1447 if (asprintf(&gitconfig,
1448 "[remote \"%s\"]\n"
1449 "\turl = %s\n"
1450 "\tfetch = +refs/heads/%s:refs/remotes/%s/%s\n",
1451 GOT_FETCH_DEFAULT_REMOTE_NAME, git_url,
1452 branchname, GOT_FETCH_DEFAULT_REMOTE_NAME,
1453 branchname) == -1) {
1454 error = got_error_from_errno("asprintf");
1455 goto done;
1458 n = fwrite(gitconfig, 1, strlen(gitconfig), gitconfig_file);
1459 if (n != strlen(gitconfig)) {
1460 error = got_ferror(gitconfig_file, GOT_ERR_IO);
1461 goto done;
1464 if (verbosity >= 0)
1465 printf("Created %s repository '%s'\n",
1466 mirror_references ? "mirrored" : "cloned", repo_path);
1467 done:
1468 if (fetchpid > 0) {
1469 if (kill(fetchpid, SIGTERM) == -1)
1470 error = got_error_from_errno("kill");
1471 if (waitpid(fetchpid, &fetchstatus, 0) == -1 && error == NULL)
1472 error = got_error_from_errno("waitpid");
1474 if (fetchfd != -1 && close(fetchfd) == -1 && error == NULL)
1475 error = got_error_from_errno("close");
1476 if (gotconfig_file && fclose(gotconfig_file) == EOF && error == NULL)
1477 error = got_error_from_errno("fclose");
1478 if (gitconfig_file && fclose(gitconfig_file) == EOF && error == NULL)
1479 error = got_error_from_errno("fclose");
1480 if (repo)
1481 got_repo_close(repo);
1482 if (head_symref)
1483 got_ref_close(head_symref);
1484 TAILQ_FOREACH(pe, &refs, entry) {
1485 free((void *)pe->path);
1486 free(pe->data);
1488 got_pathlist_free(&refs);
1489 TAILQ_FOREACH(pe, &symrefs, entry) {
1490 free((void *)pe->path);
1491 free(pe->data);
1493 got_pathlist_free(&symrefs);
1494 got_pathlist_free(&wanted_branches);
1495 got_pathlist_free(&wanted_refs);
1496 free(pack_hash);
1497 free(proto);
1498 free(host);
1499 free(port);
1500 free(server_path);
1501 free(repo_name);
1502 free(default_destdir);
1503 free(gotconfig);
1504 free(gitconfig);
1505 free(gotconfig_path);
1506 free(gitconfig_path);
1507 free(git_url);
1508 return error;
1511 static const struct got_error *
1512 update_ref(struct got_reference *ref, struct got_object_id *new_id,
1513 int replace_tags, int verbosity, struct got_repository *repo)
1515 const struct got_error *err = NULL;
1516 char *new_id_str = NULL;
1517 struct got_object_id *old_id = NULL;
1519 err = got_object_id_str(&new_id_str, new_id);
1520 if (err)
1521 goto done;
1523 if (!replace_tags &&
1524 strncmp(got_ref_get_name(ref), "refs/tags/", 10) == 0) {
1525 err = got_ref_resolve(&old_id, repo, ref);
1526 if (err)
1527 goto done;
1528 if (got_object_id_cmp(old_id, new_id) == 0)
1529 goto done;
1530 if (verbosity >= 0) {
1531 printf("Rejecting update of existing tag %s: %s\n",
1532 got_ref_get_name(ref), new_id_str);
1534 goto done;
1537 if (got_ref_is_symbolic(ref)) {
1538 if (verbosity >= 0) {
1539 printf("Replacing reference %s: %s\n",
1540 got_ref_get_name(ref),
1541 got_ref_get_symref_target(ref));
1543 err = got_ref_change_symref_to_ref(ref, new_id);
1544 if (err)
1545 goto done;
1546 err = got_ref_write(ref, repo);
1547 if (err)
1548 goto done;
1549 } else {
1550 err = got_ref_resolve(&old_id, repo, ref);
1551 if (err)
1552 goto done;
1553 if (got_object_id_cmp(old_id, new_id) == 0)
1554 goto done;
1556 err = got_ref_change_ref(ref, new_id);
1557 if (err)
1558 goto done;
1559 err = got_ref_write(ref, repo);
1560 if (err)
1561 goto done;
1564 if (verbosity >= 0)
1565 printf("Updated %s: %s\n", got_ref_get_name(ref),
1566 new_id_str);
1567 done:
1568 free(old_id);
1569 free(new_id_str);
1570 return err;
1573 static const struct got_error *
1574 update_symref(const char *refname, struct got_reference *target_ref,
1575 int verbosity, struct got_repository *repo)
1577 const struct got_error *err = NULL, *unlock_err;
1578 struct got_reference *symref;
1579 int symref_is_locked = 0;
1581 err = got_ref_open(&symref, repo, refname, 1);
1582 if (err) {
1583 if (err->code != GOT_ERR_NOT_REF)
1584 return err;
1585 err = got_ref_alloc_symref(&symref, refname, target_ref);
1586 if (err)
1587 goto done;
1589 err = got_ref_write(symref, repo);
1590 if (err)
1591 goto done;
1593 if (verbosity >= 0)
1594 printf("Created reference %s: %s\n",
1595 got_ref_get_name(symref),
1596 got_ref_get_symref_target(symref));
1597 } else {
1598 symref_is_locked = 1;
1600 if (strcmp(got_ref_get_symref_target(symref),
1601 got_ref_get_name(target_ref)) == 0)
1602 goto done;
1604 err = got_ref_change_symref(symref,
1605 got_ref_get_name(target_ref));
1606 if (err)
1607 goto done;
1609 err = got_ref_write(symref, repo);
1610 if (err)
1611 goto done;
1613 if (verbosity >= 0)
1614 printf("Updated %s: %s\n", got_ref_get_name(symref),
1615 got_ref_get_symref_target(symref));
1618 done:
1619 if (symref_is_locked) {
1620 unlock_err = got_ref_unlock(symref);
1621 if (unlock_err && err == NULL)
1622 err = unlock_err;
1624 got_ref_close(symref);
1625 return err;
1628 __dead static void
1629 usage_fetch(void)
1631 fprintf(stderr, "usage: %s fetch [-a] [-b branch] [-d] [-l] "
1632 "[-r repository-path] [-t] [-q] [-v] [-R reference] "
1633 "[remote-repository-name]\n",
1634 getprogname());
1635 exit(1);
1638 static const struct got_error *
1639 delete_missing_ref(struct got_reference *ref,
1640 int verbosity, struct got_repository *repo)
1642 const struct got_error *err = NULL;
1643 struct got_object_id *id = NULL;
1644 char *id_str = NULL;
1646 if (got_ref_is_symbolic(ref)) {
1647 err = got_ref_delete(ref, repo);
1648 if (err)
1649 return err;
1650 if (verbosity >= 0) {
1651 printf("Deleted reference %s: %s\n",
1652 got_ref_get_name(ref),
1653 got_ref_get_symref_target(ref));
1655 } else {
1656 err = got_ref_resolve(&id, repo, ref);
1657 if (err)
1658 return err;
1659 err = got_object_id_str(&id_str, id);
1660 if (err)
1661 goto done;
1663 err = got_ref_delete(ref, repo);
1664 if (err)
1665 goto done;
1666 if (verbosity >= 0) {
1667 printf("Deleted reference %s: %s\n",
1668 got_ref_get_name(ref), id_str);
1671 done:
1672 free(id);
1673 free(id_str);
1674 return NULL;
1677 static const struct got_error *
1678 delete_missing_refs(struct got_pathlist_head *their_refs,
1679 struct got_pathlist_head *their_symrefs,
1680 const struct got_remote_repo *remote,
1681 int verbosity, struct got_repository *repo)
1683 const struct got_error *err = NULL, *unlock_err;
1684 struct got_reflist_head my_refs;
1685 struct got_reflist_entry *re;
1686 struct got_pathlist_entry *pe;
1687 char *remote_namespace = NULL;
1688 char *local_refname = NULL;
1690 SIMPLEQ_INIT(&my_refs);
1692 if (asprintf(&remote_namespace, "refs/remotes/%s/", remote->name)
1693 == -1)
1694 return got_error_from_errno("asprintf");
1696 err = got_ref_list(&my_refs, repo, NULL, got_ref_cmp_by_name, NULL);
1697 if (err)
1698 goto done;
1700 SIMPLEQ_FOREACH(re, &my_refs, entry) {
1701 const char *refname = got_ref_get_name(re->ref);
1703 if (!remote->mirror_references) {
1704 if (strncmp(refname, remote_namespace,
1705 strlen(remote_namespace)) == 0) {
1706 if (strcmp(refname + strlen(remote_namespace),
1707 GOT_REF_HEAD) == 0)
1708 continue;
1709 if (asprintf(&local_refname, "refs/heads/%s",
1710 refname + strlen(remote_namespace)) == -1) {
1711 err = got_error_from_errno("asprintf");
1712 goto done;
1714 } else if (strncmp(refname, "refs/tags/", 10) != 0)
1715 continue;
1718 TAILQ_FOREACH(pe, their_refs, entry) {
1719 if (strcmp(local_refname, pe->path) == 0)
1720 break;
1722 if (pe != NULL)
1723 continue;
1725 TAILQ_FOREACH(pe, their_symrefs, entry) {
1726 if (strcmp(local_refname, pe->path) == 0)
1727 break;
1729 if (pe != NULL)
1730 continue;
1732 err = delete_missing_ref(re->ref, verbosity, repo);
1733 if (err)
1734 break;
1736 if (local_refname) {
1737 struct got_reference *ref;
1738 err = got_ref_open(&ref, repo, local_refname, 1);
1739 if (err) {
1740 if (err->code != GOT_ERR_NOT_REF)
1741 break;
1742 free(local_refname);
1743 local_refname = NULL;
1744 continue;
1746 err = delete_missing_ref(ref, verbosity, repo);
1747 if (err)
1748 break;
1749 unlock_err = got_ref_unlock(ref);
1750 got_ref_close(ref);
1751 if (unlock_err && err == NULL) {
1752 err = unlock_err;
1753 break;
1756 free(local_refname);
1757 local_refname = NULL;
1760 done:
1761 free(remote_namespace);
1762 free(local_refname);
1763 return err;
1766 static const struct got_error *
1767 update_wanted_ref(const char *refname, struct got_object_id *id,
1768 const char *remote_repo_name, int verbosity, struct got_repository *repo)
1770 const struct got_error *err, *unlock_err;
1771 char *remote_refname;
1772 struct got_reference *ref;
1774 if (strncmp("refs/", refname, 5) == 0)
1775 refname += 5;
1777 if (asprintf(&remote_refname, "refs/remotes/%s/%s",
1778 remote_repo_name, refname) == -1)
1779 return got_error_from_errno("asprintf");
1781 err = got_ref_open(&ref, repo, remote_refname, 1);
1782 if (err) {
1783 if (err->code != GOT_ERR_NOT_REF)
1784 goto done;
1785 err = create_ref(remote_refname, id, verbosity, repo);
1786 } else {
1787 err = update_ref(ref, id, 0, verbosity, repo);
1788 unlock_err = got_ref_unlock(ref);
1789 if (unlock_err && err == NULL)
1790 err = unlock_err;
1791 got_ref_close(ref);
1793 done:
1794 free(remote_refname);
1795 return err;
1798 static const struct got_error *
1799 cmd_fetch(int argc, char *argv[])
1801 const struct got_error *error = NULL, *unlock_err;
1802 char *cwd = NULL, *repo_path = NULL;
1803 const char *remote_name;
1804 char *proto = NULL, *host = NULL, *port = NULL;
1805 char *repo_name = NULL, *server_path = NULL;
1806 const struct got_remote_repo *remotes, *remote = NULL;
1807 int nremotes;
1808 char *id_str = NULL;
1809 struct got_repository *repo = NULL;
1810 struct got_worktree *worktree = NULL;
1811 const struct got_gotconfig *repo_conf = NULL, *worktree_conf = NULL;
1812 struct got_pathlist_head refs, symrefs, wanted_branches, wanted_refs;
1813 struct got_pathlist_entry *pe;
1814 struct got_object_id *pack_hash = NULL;
1815 int i, ch, fetchfd = -1, fetchstatus;
1816 pid_t fetchpid = -1;
1817 struct got_fetch_progress_arg fpa;
1818 int verbosity = 0, fetch_all_branches = 0, list_refs_only = 0;
1819 int delete_refs = 0, replace_tags = 0;
1821 TAILQ_INIT(&refs);
1822 TAILQ_INIT(&symrefs);
1823 TAILQ_INIT(&wanted_branches);
1824 TAILQ_INIT(&wanted_refs);
1826 while ((ch = getopt(argc, argv, "ab:dlr:tvqR:")) != -1) {
1827 switch (ch) {
1828 case 'a':
1829 fetch_all_branches = 1;
1830 break;
1831 case 'b':
1832 error = got_pathlist_append(&wanted_branches,
1833 optarg, NULL);
1834 if (error)
1835 return error;
1836 break;
1837 case 'd':
1838 delete_refs = 1;
1839 break;
1840 case 'l':
1841 list_refs_only = 1;
1842 break;
1843 case 'r':
1844 repo_path = realpath(optarg, NULL);
1845 if (repo_path == NULL)
1846 return got_error_from_errno2("realpath",
1847 optarg);
1848 got_path_strip_trailing_slashes(repo_path);
1849 break;
1850 case 't':
1851 replace_tags = 1;
1852 break;
1853 case 'v':
1854 if (verbosity < 0)
1855 verbosity = 0;
1856 else if (verbosity < 3)
1857 verbosity++;
1858 break;
1859 case 'q':
1860 verbosity = -1;
1861 break;
1862 case 'R':
1863 error = got_pathlist_append(&wanted_refs,
1864 optarg, NULL);
1865 if (error)
1866 return error;
1867 break;
1868 default:
1869 usage_fetch();
1870 break;
1873 argc -= optind;
1874 argv += optind;
1876 if (fetch_all_branches && !TAILQ_EMPTY(&wanted_branches))
1877 errx(1, "-a and -b options are mutually exclusive");
1878 if (list_refs_only) {
1879 if (!TAILQ_EMPTY(&wanted_branches))
1880 errx(1, "-l and -b options are mutually exclusive");
1881 if (fetch_all_branches)
1882 errx(1, "-l and -a options are mutually exclusive");
1883 if (delete_refs)
1884 errx(1, "-l and -d options are mutually exclusive");
1885 if (verbosity == -1)
1886 errx(1, "-l and -q options are mutually exclusive");
1889 if (argc == 0)
1890 remote_name = GOT_FETCH_DEFAULT_REMOTE_NAME;
1891 else if (argc == 1)
1892 remote_name = argv[0];
1893 else
1894 usage_fetch();
1896 cwd = getcwd(NULL, 0);
1897 if (cwd == NULL) {
1898 error = got_error_from_errno("getcwd");
1899 goto done;
1902 if (repo_path == NULL) {
1903 error = got_worktree_open(&worktree, cwd);
1904 if (error && error->code != GOT_ERR_NOT_WORKTREE)
1905 goto done;
1906 else
1907 error = NULL;
1908 if (worktree) {
1909 repo_path =
1910 strdup(got_worktree_get_repo_path(worktree));
1911 if (repo_path == NULL)
1912 error = got_error_from_errno("strdup");
1913 if (error)
1914 goto done;
1915 } else {
1916 repo_path = strdup(cwd);
1917 if (repo_path == NULL) {
1918 error = got_error_from_errno("strdup");
1919 goto done;
1924 error = got_repo_open(&repo, repo_path, NULL);
1925 if (error)
1926 goto done;
1928 if (worktree) {
1929 worktree_conf = got_worktree_get_gotconfig(worktree);
1930 if (worktree_conf) {
1931 got_gotconfig_get_remotes(&nremotes, &remotes,
1932 worktree_conf);
1933 for (i = 0; i < nremotes; i++) {
1934 remote = &remotes[i];
1935 if (strcmp(remote->name, remote_name) == 0)
1936 break;
1940 if (remote == NULL) {
1941 repo_conf = got_repo_get_gotconfig(repo);
1942 if (repo_conf) {
1943 got_gotconfig_get_remotes(&nremotes, &remotes,
1944 repo_conf);
1945 for (i = 0; i < nremotes; i++) {
1946 remote = &remotes[i];
1947 if (strcmp(remote->name, remote_name) == 0)
1948 break;
1952 if (remote == NULL) {
1953 got_repo_get_gitconfig_remotes(&nremotes, &remotes, repo);
1954 for (i = 0; i < nremotes; i++) {
1955 remote = &remotes[i];
1956 if (strcmp(remote->name, remote_name) == 0)
1957 break;
1960 if (remote == NULL) {
1961 error = got_error_path(remote_name, GOT_ERR_NO_REMOTE);
1962 goto done;
1965 error = got_fetch_parse_uri(&proto, &host, &port, &server_path,
1966 &repo_name, remote->url);
1967 if (error)
1968 goto done;
1970 if (strcmp(proto, "git") == 0) {
1971 #ifndef PROFILE
1972 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
1973 "sendfd dns inet unveil", NULL) == -1)
1974 err(1, "pledge");
1975 #endif
1976 } else if (strcmp(proto, "git+ssh") == 0 ||
1977 strcmp(proto, "ssh") == 0) {
1978 #ifndef PROFILE
1979 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
1980 "sendfd unveil", NULL) == -1)
1981 err(1, "pledge");
1982 #endif
1983 } else if (strcmp(proto, "http") == 0 ||
1984 strcmp(proto, "git+http") == 0) {
1985 error = got_error_path(proto, GOT_ERR_NOT_IMPL);
1986 goto done;
1987 } else {
1988 error = got_error_path(proto, GOT_ERR_BAD_PROTO);
1989 goto done;
1992 if (strcmp(proto, "git+ssh") == 0 || strcmp(proto, "ssh") == 0) {
1993 if (unveil(GOT_FETCH_PATH_SSH, "x") != 0) {
1994 error = got_error_from_errno2("unveil",
1995 GOT_FETCH_PATH_SSH);
1996 goto done;
1999 error = apply_unveil(got_repo_get_path(repo), 0, NULL);
2000 if (error)
2001 goto done;
2003 if (verbosity >= 0)
2004 printf("Connecting to \"%s\" %s%s%s\n", remote->name, host,
2005 port ? ":" : "", port ? port : "");
2007 error = got_fetch_connect(&fetchpid, &fetchfd, proto, host, port,
2008 server_path, verbosity);
2009 if (error)
2010 goto done;
2012 fpa.last_scaled_size[0] = '\0';
2013 fpa.last_p_indexed = -1;
2014 fpa.last_p_resolved = -1;
2015 fpa.verbosity = verbosity;
2016 error = got_fetch_pack(&pack_hash, &refs, &symrefs, remote->name,
2017 remote->mirror_references, fetch_all_branches, &wanted_branches,
2018 &wanted_refs, list_refs_only, verbosity, fetchfd, repo,
2019 fetch_progress, &fpa);
2020 if (error)
2021 goto done;
2023 if (list_refs_only) {
2024 error = list_remote_refs(&symrefs, &refs);
2025 goto done;
2028 if (pack_hash == NULL) {
2029 if (verbosity >= 0)
2030 printf("Already up-to-date\n");
2031 } else if (verbosity >= 0) {
2032 error = got_object_id_str(&id_str, pack_hash);
2033 if (error)
2034 goto done;
2035 printf("\nFetched %s.pack\n", id_str);
2036 free(id_str);
2037 id_str = NULL;
2040 /* Update references provided with the pack file. */
2041 TAILQ_FOREACH(pe, &refs, entry) {
2042 const char *refname = pe->path;
2043 struct got_object_id *id = pe->data;
2044 struct got_reference *ref;
2045 char *remote_refname;
2047 if (is_wanted_ref(&wanted_refs, refname) &&
2048 !remote->mirror_references) {
2049 error = update_wanted_ref(refname, id,
2050 remote->name, verbosity, repo);
2051 if (error)
2052 goto done;
2053 continue;
2056 if (remote->mirror_references ||
2057 strncmp("refs/tags/", refname, 10) == 0) {
2058 error = got_ref_open(&ref, repo, refname, 1);
2059 if (error) {
2060 if (error->code != GOT_ERR_NOT_REF)
2061 goto done;
2062 error = create_ref(refname, id, verbosity,
2063 repo);
2064 if (error)
2065 goto done;
2066 } else {
2067 error = update_ref(ref, id, replace_tags,
2068 verbosity, repo);
2069 unlock_err = got_ref_unlock(ref);
2070 if (unlock_err && error == NULL)
2071 error = unlock_err;
2072 got_ref_close(ref);
2073 if (error)
2074 goto done;
2076 } else if (strncmp("refs/heads/", refname, 11) == 0) {
2077 if (asprintf(&remote_refname, "refs/remotes/%s/%s",
2078 remote_name, refname + 11) == -1) {
2079 error = got_error_from_errno("asprintf");
2080 goto done;
2083 error = got_ref_open(&ref, repo, remote_refname, 1);
2084 if (error) {
2085 if (error->code != GOT_ERR_NOT_REF)
2086 goto done;
2087 error = create_ref(remote_refname, id,
2088 verbosity, repo);
2089 if (error)
2090 goto done;
2091 } else {
2092 error = update_ref(ref, id, replace_tags,
2093 verbosity, repo);
2094 unlock_err = got_ref_unlock(ref);
2095 if (unlock_err && error == NULL)
2096 error = unlock_err;
2097 got_ref_close(ref);
2098 if (error)
2099 goto done;
2102 /* Also create a local branch if none exists yet. */
2103 error = got_ref_open(&ref, repo, refname, 1);
2104 if (error) {
2105 if (error->code != GOT_ERR_NOT_REF)
2106 goto done;
2107 error = create_ref(refname, id, verbosity,
2108 repo);
2109 if (error)
2110 goto done;
2111 } else {
2112 unlock_err = got_ref_unlock(ref);
2113 if (unlock_err && error == NULL)
2114 error = unlock_err;
2115 got_ref_close(ref);
2119 if (delete_refs) {
2120 error = delete_missing_refs(&refs, &symrefs, remote,
2121 verbosity, repo);
2122 if (error)
2123 goto done;
2126 if (!remote->mirror_references) {
2127 /* Update remote HEAD reference if the server provided one. */
2128 TAILQ_FOREACH(pe, &symrefs, entry) {
2129 struct got_reference *target_ref;
2130 const char *refname = pe->path;
2131 const char *target = pe->data;
2132 char *remote_refname = NULL, *remote_target = NULL;
2134 if (strcmp(refname, GOT_REF_HEAD) != 0)
2135 continue;
2137 if (strncmp("refs/heads/", target, 11) != 0)
2138 continue;
2140 if (asprintf(&remote_refname, "refs/remotes/%s/%s",
2141 remote->name, refname) == -1) {
2142 error = got_error_from_errno("asprintf");
2143 goto done;
2145 if (asprintf(&remote_target, "refs/remotes/%s/%s",
2146 remote->name, target + 11) == -1) {
2147 error = got_error_from_errno("asprintf");
2148 free(remote_refname);
2149 goto done;
2152 error = got_ref_open(&target_ref, repo, remote_target,
2153 0);
2154 if (error) {
2155 free(remote_refname);
2156 free(remote_target);
2157 if (error->code == GOT_ERR_NOT_REF) {
2158 error = NULL;
2159 continue;
2161 goto done;
2163 error = update_symref(remote_refname, target_ref,
2164 verbosity, repo);
2165 free(remote_refname);
2166 free(remote_target);
2167 got_ref_close(target_ref);
2168 if (error)
2169 goto done;
2172 done:
2173 if (fetchpid > 0) {
2174 if (kill(fetchpid, SIGTERM) == -1)
2175 error = got_error_from_errno("kill");
2176 if (waitpid(fetchpid, &fetchstatus, 0) == -1 && error == NULL)
2177 error = got_error_from_errno("waitpid");
2179 if (fetchfd != -1 && close(fetchfd) == -1 && error == NULL)
2180 error = got_error_from_errno("close");
2181 if (repo)
2182 got_repo_close(repo);
2183 if (worktree)
2184 got_worktree_close(worktree);
2185 TAILQ_FOREACH(pe, &refs, entry) {
2186 free((void *)pe->path);
2187 free(pe->data);
2189 got_pathlist_free(&refs);
2190 TAILQ_FOREACH(pe, &symrefs, entry) {
2191 free((void *)pe->path);
2192 free(pe->data);
2194 got_pathlist_free(&symrefs);
2195 got_pathlist_free(&wanted_branches);
2196 got_pathlist_free(&wanted_refs);
2197 free(id_str);
2198 free(cwd);
2199 free(repo_path);
2200 free(pack_hash);
2201 free(proto);
2202 free(host);
2203 free(port);
2204 free(server_path);
2205 free(repo_name);
2206 return error;
2210 __dead static void
2211 usage_checkout(void)
2213 fprintf(stderr, "usage: %s checkout [-E] [-b branch] [-c commit] "
2214 "[-p prefix] repository-path [worktree-path]\n", getprogname());
2215 exit(1);
2218 static void
2219 show_worktree_base_ref_warning(void)
2221 fprintf(stderr, "%s: warning: could not create a reference "
2222 "to the work tree's base commit; the commit could be "
2223 "garbage-collected by Git; making the repository "
2224 "writable and running 'got update' will prevent this\n",
2225 getprogname());
2228 struct got_checkout_progress_arg {
2229 const char *worktree_path;
2230 int had_base_commit_ref_error;
2233 static const struct got_error *
2234 checkout_progress(void *arg, unsigned char status, const char *path)
2236 struct got_checkout_progress_arg *a = arg;
2238 /* Base commit bump happens silently. */
2239 if (status == GOT_STATUS_BUMP_BASE)
2240 return NULL;
2242 if (status == GOT_STATUS_BASE_REF_ERR) {
2243 a->had_base_commit_ref_error = 1;
2244 return NULL;
2247 while (path[0] == '/')
2248 path++;
2250 printf("%c %s/%s\n", status, a->worktree_path, path);
2251 return NULL;
2254 static const struct got_error *
2255 check_cancelled(void *arg)
2257 if (sigint_received || sigpipe_received)
2258 return got_error(GOT_ERR_CANCELLED);
2259 return NULL;
2262 static const struct got_error *
2263 check_linear_ancestry(struct got_object_id *commit_id,
2264 struct got_object_id *base_commit_id, int allow_forwards_in_time_only,
2265 struct got_repository *repo)
2267 const struct got_error *err = NULL;
2268 struct got_object_id *yca_id;
2270 err = got_commit_graph_find_youngest_common_ancestor(&yca_id,
2271 commit_id, base_commit_id, repo, check_cancelled, NULL);
2272 if (err)
2273 return err;
2275 if (yca_id == NULL)
2276 return got_error(GOT_ERR_ANCESTRY);
2279 * Require a straight line of history between the target commit
2280 * and the work tree's base commit.
2282 * Non-linear situations such as this require a rebase:
2284 * (commit) D F (base_commit)
2285 * \ /
2286 * C E
2287 * \ /
2288 * B (yca)
2289 * |
2290 * A
2292 * 'got update' only handles linear cases:
2293 * Update forwards in time: A (base/yca) - B - C - D (commit)
2294 * Update backwards in time: D (base) - C - B - A (commit/yca)
2296 if (allow_forwards_in_time_only) {
2297 if (got_object_id_cmp(base_commit_id, yca_id) != 0)
2298 return got_error(GOT_ERR_ANCESTRY);
2299 } else if (got_object_id_cmp(commit_id, yca_id) != 0 &&
2300 got_object_id_cmp(base_commit_id, yca_id) != 0)
2301 return got_error(GOT_ERR_ANCESTRY);
2303 free(yca_id);
2304 return NULL;
2307 static const struct got_error *
2308 check_same_branch(struct got_object_id *commit_id,
2309 struct got_reference *head_ref, struct got_object_id *yca_id,
2310 struct got_repository *repo)
2312 const struct got_error *err = NULL;
2313 struct got_commit_graph *graph = NULL;
2314 struct got_object_id *head_commit_id = NULL;
2315 int is_same_branch = 0;
2317 err = got_ref_resolve(&head_commit_id, repo, head_ref);
2318 if (err)
2319 goto done;
2321 if (got_object_id_cmp(head_commit_id, commit_id) == 0) {
2322 is_same_branch = 1;
2323 goto done;
2325 if (yca_id && got_object_id_cmp(commit_id, yca_id) == 0) {
2326 is_same_branch = 1;
2327 goto done;
2330 err = got_commit_graph_open(&graph, "/", 1);
2331 if (err)
2332 goto done;
2334 err = got_commit_graph_iter_start(graph, head_commit_id, repo,
2335 check_cancelled, NULL);
2336 if (err)
2337 goto done;
2339 for (;;) {
2340 struct got_object_id *id;
2341 err = got_commit_graph_iter_next(&id, graph, repo,
2342 check_cancelled, NULL);
2343 if (err) {
2344 if (err->code == GOT_ERR_ITER_COMPLETED)
2345 err = NULL;
2346 break;
2349 if (id) {
2350 if (yca_id && got_object_id_cmp(id, yca_id) == 0)
2351 break;
2352 if (got_object_id_cmp(id, commit_id) == 0) {
2353 is_same_branch = 1;
2354 break;
2358 done:
2359 if (graph)
2360 got_commit_graph_close(graph);
2361 free(head_commit_id);
2362 if (!err && !is_same_branch)
2363 err = got_error(GOT_ERR_ANCESTRY);
2364 return err;
2367 static const struct got_error *
2368 checkout_ancestry_error(struct got_reference *ref, const char *commit_id_str)
2370 static char msg[512];
2371 const char *branch_name;
2373 if (got_ref_is_symbolic(ref))
2374 branch_name = got_ref_get_symref_target(ref);
2375 else
2376 branch_name = got_ref_get_name(ref);
2378 if (strncmp("refs/heads/", branch_name, 11) == 0)
2379 branch_name += 11;
2381 snprintf(msg, sizeof(msg),
2382 "target commit is not contained in branch '%s'; "
2383 "the branch to use must be specified with -b; "
2384 "if necessary a new branch can be created for "
2385 "this commit with 'got branch -c %s BRANCH_NAME'",
2386 branch_name, commit_id_str);
2388 return got_error_msg(GOT_ERR_ANCESTRY, msg);
2391 static const struct got_error *
2392 cmd_checkout(int argc, char *argv[])
2394 const struct got_error *error = NULL;
2395 struct got_repository *repo = NULL;
2396 struct got_reference *head_ref = NULL;
2397 struct got_worktree *worktree = NULL;
2398 char *repo_path = NULL;
2399 char *worktree_path = NULL;
2400 const char *path_prefix = "";
2401 const char *branch_name = GOT_REF_HEAD;
2402 char *commit_id_str = NULL;
2403 int ch, same_path_prefix, allow_nonempty = 0;
2404 struct got_pathlist_head paths;
2405 struct got_checkout_progress_arg cpa;
2407 TAILQ_INIT(&paths);
2409 while ((ch = getopt(argc, argv, "b:c:Ep:")) != -1) {
2410 switch (ch) {
2411 case 'b':
2412 branch_name = optarg;
2413 break;
2414 case 'c':
2415 commit_id_str = strdup(optarg);
2416 if (commit_id_str == NULL)
2417 return got_error_from_errno("strdup");
2418 break;
2419 case 'E':
2420 allow_nonempty = 1;
2421 break;
2422 case 'p':
2423 path_prefix = optarg;
2424 break;
2425 default:
2426 usage_checkout();
2427 /* NOTREACHED */
2431 argc -= optind;
2432 argv += optind;
2434 #ifndef PROFILE
2435 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
2436 "unveil", NULL) == -1)
2437 err(1, "pledge");
2438 #endif
2439 if (argc == 1) {
2440 char *cwd, *base, *dotgit;
2441 repo_path = realpath(argv[0], NULL);
2442 if (repo_path == NULL)
2443 return got_error_from_errno2("realpath", argv[0]);
2444 cwd = getcwd(NULL, 0);
2445 if (cwd == NULL) {
2446 error = got_error_from_errno("getcwd");
2447 goto done;
2449 if (path_prefix[0]) {
2450 base = basename(path_prefix);
2451 if (base == NULL) {
2452 error = got_error_from_errno2("basename",
2453 path_prefix);
2454 goto done;
2456 } else {
2457 base = basename(repo_path);
2458 if (base == NULL) {
2459 error = got_error_from_errno2("basename",
2460 repo_path);
2461 goto done;
2464 dotgit = strstr(base, ".git");
2465 if (dotgit)
2466 *dotgit = '\0';
2467 if (asprintf(&worktree_path, "%s/%s", cwd, base) == -1) {
2468 error = got_error_from_errno("asprintf");
2469 free(cwd);
2470 goto done;
2472 free(cwd);
2473 } else if (argc == 2) {
2474 repo_path = realpath(argv[0], NULL);
2475 if (repo_path == NULL) {
2476 error = got_error_from_errno2("realpath", argv[0]);
2477 goto done;
2479 worktree_path = realpath(argv[1], NULL);
2480 if (worktree_path == NULL) {
2481 if (errno != ENOENT) {
2482 error = got_error_from_errno2("realpath",
2483 argv[1]);
2484 goto done;
2486 worktree_path = strdup(argv[1]);
2487 if (worktree_path == NULL) {
2488 error = got_error_from_errno("strdup");
2489 goto done;
2492 } else
2493 usage_checkout();
2495 got_path_strip_trailing_slashes(repo_path);
2496 got_path_strip_trailing_slashes(worktree_path);
2498 error = got_repo_open(&repo, repo_path, NULL);
2499 if (error != NULL)
2500 goto done;
2502 /* Pre-create work tree path for unveil(2) */
2503 error = got_path_mkdir(worktree_path);
2504 if (error) {
2505 if (!(error->code == GOT_ERR_ERRNO && errno == EISDIR) &&
2506 !(error->code == GOT_ERR_ERRNO && errno == EEXIST))
2507 goto done;
2508 if (!allow_nonempty &&
2509 !got_path_dir_is_empty(worktree_path)) {
2510 error = got_error_path(worktree_path,
2511 GOT_ERR_DIR_NOT_EMPTY);
2512 goto done;
2516 error = apply_unveil(got_repo_get_path(repo), 0, worktree_path);
2517 if (error)
2518 goto done;
2520 error = got_ref_open(&head_ref, repo, branch_name, 0);
2521 if (error != NULL)
2522 goto done;
2524 error = got_worktree_init(worktree_path, head_ref, path_prefix, repo);
2525 if (error != NULL && !(error->code == GOT_ERR_ERRNO && errno == EEXIST))
2526 goto done;
2528 error = got_worktree_open(&worktree, worktree_path);
2529 if (error != NULL)
2530 goto done;
2532 error = got_worktree_match_path_prefix(&same_path_prefix, worktree,
2533 path_prefix);
2534 if (error != NULL)
2535 goto done;
2536 if (!same_path_prefix) {
2537 error = got_error(GOT_ERR_PATH_PREFIX);
2538 goto done;
2541 if (commit_id_str) {
2542 struct got_object_id *commit_id;
2543 error = got_repo_match_object_id(&commit_id, NULL,
2544 commit_id_str, GOT_OBJ_TYPE_COMMIT, 1, repo);
2545 if (error)
2546 goto done;
2547 error = check_linear_ancestry(commit_id,
2548 got_worktree_get_base_commit_id(worktree), 0, repo);
2549 if (error != NULL) {
2550 free(commit_id);
2551 if (error->code == GOT_ERR_ANCESTRY) {
2552 error = checkout_ancestry_error(
2553 head_ref, commit_id_str);
2555 goto done;
2557 error = check_same_branch(commit_id, head_ref, NULL, repo);
2558 if (error) {
2559 if (error->code == GOT_ERR_ANCESTRY) {
2560 error = checkout_ancestry_error(
2561 head_ref, commit_id_str);
2563 goto done;
2565 error = got_worktree_set_base_commit_id(worktree, repo,
2566 commit_id);
2567 free(commit_id);
2568 if (error)
2569 goto done;
2572 error = got_pathlist_append(&paths, "", NULL);
2573 if (error)
2574 goto done;
2575 cpa.worktree_path = worktree_path;
2576 cpa.had_base_commit_ref_error = 0;
2577 error = got_worktree_checkout_files(worktree, &paths, repo,
2578 checkout_progress, &cpa, check_cancelled, NULL);
2579 if (error != NULL)
2580 goto done;
2582 printf("Now shut up and hack\n");
2583 if (cpa.had_base_commit_ref_error)
2584 show_worktree_base_ref_warning();
2585 done:
2586 got_pathlist_free(&paths);
2587 free(commit_id_str);
2588 free(repo_path);
2589 free(worktree_path);
2590 return error;
2593 struct got_update_progress_arg {
2594 int did_something;
2595 int conflicts;
2596 int obstructed;
2597 int not_updated;
2600 void
2601 print_update_progress_stats(struct got_update_progress_arg *upa)
2603 if (!upa->did_something)
2604 return;
2606 if (upa->conflicts > 0)
2607 printf("Files with new merge conflicts: %d\n", upa->conflicts);
2608 if (upa->obstructed > 0)
2609 printf("File paths obstructed by a non-regular file: %d\n",
2610 upa->obstructed);
2611 if (upa->not_updated > 0)
2612 printf("Files not updated because of existing merge "
2613 "conflicts: %d\n", upa->not_updated);
2616 __dead static void
2617 usage_update(void)
2619 fprintf(stderr, "usage: %s update [-b branch] [-c commit] [path ...]\n",
2620 getprogname());
2621 exit(1);
2624 static const struct got_error *
2625 update_progress(void *arg, unsigned char status, const char *path)
2627 struct got_update_progress_arg *upa = arg;
2629 if (status == GOT_STATUS_EXISTS ||
2630 status == GOT_STATUS_BASE_REF_ERR)
2631 return NULL;
2633 upa->did_something = 1;
2635 /* Base commit bump happens silently. */
2636 if (status == GOT_STATUS_BUMP_BASE)
2637 return NULL;
2639 if (status == GOT_STATUS_CONFLICT)
2640 upa->conflicts++;
2641 if (status == GOT_STATUS_OBSTRUCTED)
2642 upa->obstructed++;
2643 if (status == GOT_STATUS_CANNOT_UPDATE)
2644 upa->not_updated++;
2646 while (path[0] == '/')
2647 path++;
2648 printf("%c %s\n", status, path);
2649 return NULL;
2652 static const struct got_error *
2653 switch_head_ref(struct got_reference *head_ref,
2654 struct got_object_id *commit_id, struct got_worktree *worktree,
2655 struct got_repository *repo)
2657 const struct got_error *err = NULL;
2658 char *base_id_str;
2659 int ref_has_moved = 0;
2661 /* Trivial case: switching between two different references. */
2662 if (strcmp(got_ref_get_name(head_ref),
2663 got_worktree_get_head_ref_name(worktree)) != 0) {
2664 printf("Switching work tree from %s to %s\n",
2665 got_worktree_get_head_ref_name(worktree),
2666 got_ref_get_name(head_ref));
2667 return got_worktree_set_head_ref(worktree, head_ref);
2670 err = check_linear_ancestry(commit_id,
2671 got_worktree_get_base_commit_id(worktree), 0, repo);
2672 if (err) {
2673 if (err->code != GOT_ERR_ANCESTRY)
2674 return err;
2675 ref_has_moved = 1;
2677 if (!ref_has_moved)
2678 return NULL;
2680 /* Switching to a rebased branch with the same reference name. */
2681 err = got_object_id_str(&base_id_str,
2682 got_worktree_get_base_commit_id(worktree));
2683 if (err)
2684 return err;
2685 printf("Reference %s now points at a different branch\n",
2686 got_worktree_get_head_ref_name(worktree));
2687 printf("Switching work tree from %s to %s\n", base_id_str,
2688 got_worktree_get_head_ref_name(worktree));
2689 return NULL;
2692 static const struct got_error *
2693 check_rebase_or_histedit_in_progress(struct got_worktree *worktree)
2695 const struct got_error *err;
2696 int in_progress;
2698 err = got_worktree_rebase_in_progress(&in_progress, worktree);
2699 if (err)
2700 return err;
2701 if (in_progress)
2702 return got_error(GOT_ERR_REBASING);
2704 err = got_worktree_histedit_in_progress(&in_progress, worktree);
2705 if (err)
2706 return err;
2707 if (in_progress)
2708 return got_error(GOT_ERR_HISTEDIT_BUSY);
2710 return NULL;
2713 static const struct got_error *
2714 get_worktree_paths_from_argv(struct got_pathlist_head *paths, int argc,
2715 char *argv[], struct got_worktree *worktree)
2717 const struct got_error *err = NULL;
2718 char *path;
2719 int i;
2721 if (argc == 0) {
2722 path = strdup("");
2723 if (path == NULL)
2724 return got_error_from_errno("strdup");
2725 return got_pathlist_append(paths, path, NULL);
2728 for (i = 0; i < argc; i++) {
2729 err = got_worktree_resolve_path(&path, worktree, argv[i]);
2730 if (err)
2731 break;
2732 err = got_pathlist_append(paths, path, NULL);
2733 if (err) {
2734 free(path);
2735 break;
2739 return err;
2742 static const struct got_error *
2743 wrap_not_worktree_error(const struct got_error *orig_err,
2744 const char *cmdname, const char *path)
2746 const struct got_error *err;
2747 struct got_repository *repo;
2748 static char msg[512];
2750 err = got_repo_open(&repo, path, NULL);
2751 if (err)
2752 return orig_err;
2754 snprintf(msg, sizeof(msg),
2755 "'got %s' needs a work tree in addition to a git repository\n"
2756 "Work trees can be checked out from this Git repository with "
2757 "'got checkout'.\n"
2758 "The got(1) manual page contains more information.", cmdname);
2759 err = got_error_msg(GOT_ERR_NOT_WORKTREE, msg);
2760 got_repo_close(repo);
2761 return err;
2764 static const struct got_error *
2765 cmd_update(int argc, char *argv[])
2767 const struct got_error *error = NULL;
2768 struct got_repository *repo = NULL;
2769 struct got_worktree *worktree = NULL;
2770 char *worktree_path = NULL;
2771 struct got_object_id *commit_id = NULL;
2772 char *commit_id_str = NULL;
2773 const char *branch_name = NULL;
2774 struct got_reference *head_ref = NULL;
2775 struct got_pathlist_head paths;
2776 struct got_pathlist_entry *pe;
2777 int ch;
2778 struct got_update_progress_arg upa;
2780 TAILQ_INIT(&paths);
2782 while ((ch = getopt(argc, argv, "b:c:")) != -1) {
2783 switch (ch) {
2784 case 'b':
2785 branch_name = optarg;
2786 break;
2787 case 'c':
2788 commit_id_str = strdup(optarg);
2789 if (commit_id_str == NULL)
2790 return got_error_from_errno("strdup");
2791 break;
2792 default:
2793 usage_update();
2794 /* NOTREACHED */
2798 argc -= optind;
2799 argv += optind;
2801 #ifndef PROFILE
2802 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
2803 "unveil", NULL) == -1)
2804 err(1, "pledge");
2805 #endif
2806 worktree_path = getcwd(NULL, 0);
2807 if (worktree_path == NULL) {
2808 error = got_error_from_errno("getcwd");
2809 goto done;
2811 error = got_worktree_open(&worktree, worktree_path);
2812 if (error) {
2813 if (error->code == GOT_ERR_NOT_WORKTREE)
2814 error = wrap_not_worktree_error(error, "update",
2815 worktree_path);
2816 goto done;
2819 error = check_rebase_or_histedit_in_progress(worktree);
2820 if (error)
2821 goto done;
2823 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
2824 NULL);
2825 if (error != NULL)
2826 goto done;
2828 error = apply_unveil(got_repo_get_path(repo), 0,
2829 got_worktree_get_root_path(worktree));
2830 if (error)
2831 goto done;
2833 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
2834 if (error)
2835 goto done;
2837 error = got_ref_open(&head_ref, repo, branch_name ? branch_name :
2838 got_worktree_get_head_ref_name(worktree), 0);
2839 if (error != NULL)
2840 goto done;
2841 if (commit_id_str == NULL) {
2842 error = got_ref_resolve(&commit_id, repo, head_ref);
2843 if (error != NULL)
2844 goto done;
2845 error = got_object_id_str(&commit_id_str, commit_id);
2846 if (error != NULL)
2847 goto done;
2848 } else {
2849 error = got_repo_match_object_id(&commit_id, NULL,
2850 commit_id_str, GOT_OBJ_TYPE_COMMIT, 1, repo);
2851 free(commit_id_str);
2852 commit_id_str = NULL;
2853 if (error)
2854 goto done;
2855 error = got_object_id_str(&commit_id_str, commit_id);
2856 if (error)
2857 goto done;
2860 if (branch_name) {
2861 struct got_object_id *head_commit_id;
2862 TAILQ_FOREACH(pe, &paths, entry) {
2863 if (pe->path_len == 0)
2864 continue;
2865 error = got_error_msg(GOT_ERR_BAD_PATH,
2866 "switching between branches requires that "
2867 "the entire work tree gets updated");
2868 goto done;
2870 error = got_ref_resolve(&head_commit_id, repo, head_ref);
2871 if (error)
2872 goto done;
2873 error = check_linear_ancestry(commit_id, head_commit_id, 0,
2874 repo);
2875 free(head_commit_id);
2876 if (error != NULL)
2877 goto done;
2878 error = check_same_branch(commit_id, head_ref, NULL, repo);
2879 if (error)
2880 goto done;
2881 error = switch_head_ref(head_ref, commit_id, worktree, repo);
2882 if (error)
2883 goto done;
2884 } else {
2885 error = check_linear_ancestry(commit_id,
2886 got_worktree_get_base_commit_id(worktree), 0, repo);
2887 if (error != NULL) {
2888 if (error->code == GOT_ERR_ANCESTRY)
2889 error = got_error(GOT_ERR_BRANCH_MOVED);
2890 goto done;
2892 error = check_same_branch(commit_id, head_ref, NULL, repo);
2893 if (error)
2894 goto done;
2897 if (got_object_id_cmp(got_worktree_get_base_commit_id(worktree),
2898 commit_id) != 0) {
2899 error = got_worktree_set_base_commit_id(worktree, repo,
2900 commit_id);
2901 if (error)
2902 goto done;
2905 memset(&upa, 0, sizeof(upa));
2906 error = got_worktree_checkout_files(worktree, &paths, repo,
2907 update_progress, &upa, check_cancelled, NULL);
2908 if (error != NULL)
2909 goto done;
2911 if (upa.did_something)
2912 printf("Updated to commit %s\n", commit_id_str);
2913 else
2914 printf("Already up-to-date\n");
2915 print_update_progress_stats(&upa);
2916 done:
2917 free(worktree_path);
2918 TAILQ_FOREACH(pe, &paths, entry)
2919 free((char *)pe->path);
2920 got_pathlist_free(&paths);
2921 free(commit_id);
2922 free(commit_id_str);
2923 return error;
2926 static const struct got_error *
2927 diff_blobs(struct got_object_id *blob_id1, struct got_object_id *blob_id2,
2928 const char *path, int diff_context, int ignore_whitespace,
2929 struct got_repository *repo)
2931 const struct got_error *err = NULL;
2932 struct got_blob_object *blob1 = NULL, *blob2 = NULL;
2934 if (blob_id1) {
2935 err = got_object_open_as_blob(&blob1, repo, blob_id1, 8192);
2936 if (err)
2937 goto done;
2940 err = got_object_open_as_blob(&blob2, repo, blob_id2, 8192);
2941 if (err)
2942 goto done;
2944 while (path[0] == '/')
2945 path++;
2946 err = got_diff_blob(blob1, blob2, path, path, diff_context,
2947 ignore_whitespace, stdout);
2948 done:
2949 if (blob1)
2950 got_object_blob_close(blob1);
2951 got_object_blob_close(blob2);
2952 return err;
2955 static const struct got_error *
2956 diff_trees(struct got_object_id *tree_id1, struct got_object_id *tree_id2,
2957 const char *path, int diff_context, int ignore_whitespace,
2958 struct got_repository *repo)
2960 const struct got_error *err = NULL;
2961 struct got_tree_object *tree1 = NULL, *tree2 = NULL;
2962 struct got_diff_blob_output_unidiff_arg arg;
2964 if (tree_id1) {
2965 err = got_object_open_as_tree(&tree1, repo, tree_id1);
2966 if (err)
2967 goto done;
2970 err = got_object_open_as_tree(&tree2, repo, tree_id2);
2971 if (err)
2972 goto done;
2974 arg.diff_context = diff_context;
2975 arg.ignore_whitespace = ignore_whitespace;
2976 arg.outfile = stdout;
2977 while (path[0] == '/')
2978 path++;
2979 err = got_diff_tree(tree1, tree2, path, path, repo,
2980 got_diff_blob_output_unidiff, &arg, 1);
2981 done:
2982 if (tree1)
2983 got_object_tree_close(tree1);
2984 if (tree2)
2985 got_object_tree_close(tree2);
2986 return err;
2989 static const struct got_error *
2990 get_changed_paths(struct got_pathlist_head *paths,
2991 struct got_commit_object *commit, struct got_repository *repo)
2993 const struct got_error *err = NULL;
2994 struct got_object_id *tree_id1 = NULL, *tree_id2 = NULL;
2995 struct got_tree_object *tree1 = NULL, *tree2 = NULL;
2996 struct got_object_qid *qid;
2998 qid = SIMPLEQ_FIRST(got_object_commit_get_parent_ids(commit));
2999 if (qid != NULL) {
3000 struct got_commit_object *pcommit;
3001 err = got_object_open_as_commit(&pcommit, repo,
3002 qid->id);
3003 if (err)
3004 return err;
3006 tree_id1 = got_object_commit_get_tree_id(pcommit);
3007 got_object_commit_close(pcommit);
3011 if (tree_id1) {
3012 err = got_object_open_as_tree(&tree1, repo, tree_id1);
3013 if (err)
3014 goto done;
3017 tree_id2 = got_object_commit_get_tree_id(commit);
3018 err = got_object_open_as_tree(&tree2, repo, tree_id2);
3019 if (err)
3020 goto done;
3022 err = got_diff_tree(tree1, tree2, "", "", repo,
3023 got_diff_tree_collect_changed_paths, paths, 0);
3024 done:
3025 if (tree1)
3026 got_object_tree_close(tree1);
3027 if (tree2)
3028 got_object_tree_close(tree2);
3029 return err;
3032 static const struct got_error *
3033 print_patch(struct got_commit_object *commit, struct got_object_id *id,
3034 const char *path, int diff_context, struct got_repository *repo)
3036 const struct got_error *err = NULL;
3037 struct got_commit_object *pcommit = NULL;
3038 char *id_str1 = NULL, *id_str2 = NULL;
3039 struct got_object_id *obj_id1 = NULL, *obj_id2 = NULL;
3040 struct got_object_qid *qid;
3042 qid = SIMPLEQ_FIRST(got_object_commit_get_parent_ids(commit));
3043 if (qid != NULL) {
3044 err = got_object_open_as_commit(&pcommit, repo,
3045 qid->id);
3046 if (err)
3047 return err;
3050 if (path && path[0] != '\0') {
3051 int obj_type;
3052 err = got_object_id_by_path(&obj_id2, repo, id, path);
3053 if (err)
3054 goto done;
3055 err = got_object_id_str(&id_str2, obj_id2);
3056 if (err) {
3057 free(obj_id2);
3058 goto done;
3060 if (pcommit) {
3061 err = got_object_id_by_path(&obj_id1, repo,
3062 qid->id, path);
3063 if (err) {
3064 if (err->code != GOT_ERR_NO_TREE_ENTRY) {
3065 free(obj_id2);
3066 goto done;
3068 } else {
3069 err = got_object_id_str(&id_str1, obj_id1);
3070 if (err) {
3071 free(obj_id2);
3072 goto done;
3076 err = got_object_get_type(&obj_type, repo, obj_id2);
3077 if (err) {
3078 free(obj_id2);
3079 goto done;
3081 printf("diff %s %s\n", id_str1 ? id_str1 : "/dev/null", id_str2);
3082 switch (obj_type) {
3083 case GOT_OBJ_TYPE_BLOB:
3084 err = diff_blobs(obj_id1, obj_id2, path, diff_context,
3085 0, repo);
3086 break;
3087 case GOT_OBJ_TYPE_TREE:
3088 err = diff_trees(obj_id1, obj_id2, path, diff_context,
3089 0, repo);
3090 break;
3091 default:
3092 err = got_error(GOT_ERR_OBJ_TYPE);
3093 break;
3095 free(obj_id1);
3096 free(obj_id2);
3097 } else {
3098 obj_id2 = got_object_commit_get_tree_id(commit);
3099 err = got_object_id_str(&id_str2, obj_id2);
3100 if (err)
3101 goto done;
3102 obj_id1 = got_object_commit_get_tree_id(pcommit);
3103 err = got_object_id_str(&id_str1, obj_id1);
3104 if (err)
3105 goto done;
3106 printf("diff %s %s\n", id_str1 ? id_str1 : "/dev/null", id_str2);
3107 err = diff_trees(obj_id1, obj_id2, "", diff_context, 0, repo);
3109 done:
3110 free(id_str1);
3111 free(id_str2);
3112 if (pcommit)
3113 got_object_commit_close(pcommit);
3114 return err;
3117 static char *
3118 get_datestr(time_t *time, char *datebuf)
3120 struct tm mytm, *tm;
3121 char *p, *s;
3123 tm = gmtime_r(time, &mytm);
3124 if (tm == NULL)
3125 return NULL;
3126 s = asctime_r(tm, datebuf);
3127 if (s == NULL)
3128 return NULL;
3129 p = strchr(s, '\n');
3130 if (p)
3131 *p = '\0';
3132 return s;
3135 static const struct got_error *
3136 match_logmsg(int *have_match, struct got_object_id *id,
3137 struct got_commit_object *commit, regex_t *regex)
3139 const struct got_error *err = NULL;
3140 regmatch_t regmatch;
3141 char *id_str = NULL, *logmsg = NULL;
3143 *have_match = 0;
3145 err = got_object_id_str(&id_str, id);
3146 if (err)
3147 return err;
3149 err = got_object_commit_get_logmsg(&logmsg, commit);
3150 if (err)
3151 goto done;
3153 if (regexec(regex, logmsg, 1, &regmatch, 0) == 0)
3154 *have_match = 1;
3155 done:
3156 free(id_str);
3157 free(logmsg);
3158 return err;
3161 static void
3162 match_changed_paths(int *have_match, struct got_pathlist_head *changed_paths,
3163 regex_t *regex)
3165 regmatch_t regmatch;
3166 struct got_pathlist_entry *pe;
3168 *have_match = 0;
3170 TAILQ_FOREACH(pe, changed_paths, entry) {
3171 if (regexec(regex, pe->path, 1, &regmatch, 0) == 0) {
3172 *have_match = 1;
3173 break;
3178 #define GOT_COMMIT_SEP_STR "-----------------------------------------------\n"
3180 static const struct got_error *
3181 print_commit(struct got_commit_object *commit, struct got_object_id *id,
3182 struct got_repository *repo, const char *path,
3183 struct got_pathlist_head *changed_paths, int show_patch,
3184 int diff_context, struct got_reflist_head *refs)
3186 const struct got_error *err = NULL;
3187 char *id_str, *datestr, *logmsg0, *logmsg, *line;
3188 char datebuf[26];
3189 time_t committer_time;
3190 const char *author, *committer;
3191 char *refs_str = NULL;
3192 struct got_reflist_entry *re;
3194 SIMPLEQ_FOREACH(re, refs, entry) {
3195 char *s;
3196 const char *name;
3197 struct got_tag_object *tag = NULL;
3198 int cmp;
3200 name = got_ref_get_name(re->ref);
3201 if (strcmp(name, GOT_REF_HEAD) == 0)
3202 continue;
3203 if (strncmp(name, "refs/", 5) == 0)
3204 name += 5;
3205 if (strncmp(name, "got/", 4) == 0)
3206 continue;
3207 if (strncmp(name, "heads/", 6) == 0)
3208 name += 6;
3209 if (strncmp(name, "remotes/", 8) == 0) {
3210 name += 8;
3211 s = strstr(name, "/" GOT_REF_HEAD);
3212 if (s != NULL && s[strlen(s)] == '\0')
3213 continue;
3215 if (strncmp(name, "tags/", 5) == 0) {
3216 err = got_object_open_as_tag(&tag, repo, re->id);
3217 if (err) {
3218 if (err->code != GOT_ERR_OBJ_TYPE)
3219 return err;
3220 /* Ref points at something other than a tag. */
3221 err = NULL;
3222 tag = NULL;
3225 cmp = got_object_id_cmp(tag ?
3226 got_object_tag_get_object_id(tag) : re->id, id);
3227 if (tag)
3228 got_object_tag_close(tag);
3229 if (cmp != 0)
3230 continue;
3231 s = refs_str;
3232 if (asprintf(&refs_str, "%s%s%s", s ? s : "", s ? ", " : "",
3233 name) == -1) {
3234 err = got_error_from_errno("asprintf");
3235 free(s);
3236 return err;
3238 free(s);
3240 err = got_object_id_str(&id_str, id);
3241 if (err)
3242 return err;
3244 printf(GOT_COMMIT_SEP_STR);
3245 printf("commit %s%s%s%s\n", id_str, refs_str ? " (" : "",
3246 refs_str ? refs_str : "", refs_str ? ")" : "");
3247 free(id_str);
3248 id_str = NULL;
3249 free(refs_str);
3250 refs_str = NULL;
3251 printf("from: %s\n", got_object_commit_get_author(commit));
3252 committer_time = got_object_commit_get_committer_time(commit);
3253 datestr = get_datestr(&committer_time, datebuf);
3254 if (datestr)
3255 printf("date: %s UTC\n", datestr);
3256 author = got_object_commit_get_author(commit);
3257 committer = got_object_commit_get_committer(commit);
3258 if (strcmp(author, committer) != 0)
3259 printf("via: %s\n", committer);
3260 if (got_object_commit_get_nparents(commit) > 1) {
3261 const struct got_object_id_queue *parent_ids;
3262 struct got_object_qid *qid;
3263 int n = 1;
3264 parent_ids = got_object_commit_get_parent_ids(commit);
3265 SIMPLEQ_FOREACH(qid, parent_ids, entry) {
3266 err = got_object_id_str(&id_str, qid->id);
3267 if (err)
3268 return err;
3269 printf("parent %d: %s\n", n++, id_str);
3270 free(id_str);
3274 err = got_object_commit_get_logmsg(&logmsg0, commit);
3275 if (err)
3276 return err;
3278 logmsg = logmsg0;
3279 do {
3280 line = strsep(&logmsg, "\n");
3281 if (line)
3282 printf(" %s\n", line);
3283 } while (line);
3284 free(logmsg0);
3286 if (changed_paths) {
3287 struct got_pathlist_entry *pe;
3288 TAILQ_FOREACH(pe, changed_paths, entry) {
3289 struct got_diff_changed_path *cp = pe->data;
3290 printf(" %c %s\n", cp->status, pe->path);
3292 printf("\n");
3294 if (show_patch) {
3295 err = print_patch(commit, id, path, diff_context, repo);
3296 if (err == 0)
3297 printf("\n");
3300 if (fflush(stdout) != 0 && err == NULL)
3301 err = got_error_from_errno("fflush");
3302 return err;
3305 static const struct got_error *
3306 print_commits(struct got_object_id *root_id, struct got_object_id *end_id,
3307 struct got_repository *repo, const char *path, int show_changed_paths,
3308 int show_patch, const char *search_pattern, int diff_context, int limit,
3309 int log_branches, int reverse_display_order, struct got_reflist_head *refs)
3311 const struct got_error *err;
3312 struct got_commit_graph *graph;
3313 regex_t regex;
3314 int have_match;
3315 struct got_object_id_queue reversed_commits;
3316 struct got_object_qid *qid;
3317 struct got_commit_object *commit;
3318 struct got_pathlist_head changed_paths;
3319 struct got_pathlist_entry *pe;
3321 SIMPLEQ_INIT(&reversed_commits);
3322 TAILQ_INIT(&changed_paths);
3324 if (search_pattern && regcomp(&regex, search_pattern,
3325 REG_EXTENDED | REG_NOSUB | REG_NEWLINE))
3326 return got_error_msg(GOT_ERR_REGEX, search_pattern);
3328 err = got_commit_graph_open(&graph, path, !log_branches);
3329 if (err)
3330 return err;
3331 err = got_commit_graph_iter_start(graph, root_id, repo,
3332 check_cancelled, NULL);
3333 if (err)
3334 goto done;
3335 for (;;) {
3336 struct got_object_id *id;
3338 if (sigint_received || sigpipe_received)
3339 break;
3341 err = got_commit_graph_iter_next(&id, graph, repo,
3342 check_cancelled, NULL);
3343 if (err) {
3344 if (err->code == GOT_ERR_ITER_COMPLETED)
3345 err = NULL;
3346 break;
3348 if (id == NULL)
3349 break;
3351 err = got_object_open_as_commit(&commit, repo, id);
3352 if (err)
3353 break;
3355 if (show_changed_paths && !reverse_display_order) {
3356 err = get_changed_paths(&changed_paths, commit, repo);
3357 if (err)
3358 break;
3361 if (search_pattern) {
3362 err = match_logmsg(&have_match, id, commit, &regex);
3363 if (err) {
3364 got_object_commit_close(commit);
3365 break;
3367 if (have_match == 0 && show_changed_paths)
3368 match_changed_paths(&have_match,
3369 &changed_paths, &regex);
3370 if (have_match == 0) {
3371 got_object_commit_close(commit);
3372 TAILQ_FOREACH(pe, &changed_paths, entry) {
3373 free((char *)pe->path);
3374 free(pe->data);
3376 got_pathlist_free(&changed_paths);
3377 continue;
3381 if (reverse_display_order) {
3382 err = got_object_qid_alloc(&qid, id);
3383 if (err)
3384 break;
3385 SIMPLEQ_INSERT_HEAD(&reversed_commits, qid, entry);
3386 got_object_commit_close(commit);
3387 } else {
3388 err = print_commit(commit, id, repo, path,
3389 show_changed_paths ? &changed_paths : NULL,
3390 show_patch, diff_context, refs);
3391 got_object_commit_close(commit);
3392 if (err)
3393 break;
3395 if ((limit && --limit == 0) ||
3396 (end_id && got_object_id_cmp(id, end_id) == 0))
3397 break;
3399 TAILQ_FOREACH(pe, &changed_paths, entry) {
3400 free((char *)pe->path);
3401 free(pe->data);
3403 got_pathlist_free(&changed_paths);
3405 if (reverse_display_order) {
3406 SIMPLEQ_FOREACH(qid, &reversed_commits, entry) {
3407 err = got_object_open_as_commit(&commit, repo, qid->id);
3408 if (err)
3409 break;
3410 if (show_changed_paths) {
3411 err = get_changed_paths(&changed_paths,
3412 commit, repo);
3413 if (err)
3414 break;
3416 err = print_commit(commit, qid->id, repo, path,
3417 show_changed_paths ? &changed_paths : NULL,
3418 show_patch, diff_context, refs);
3419 got_object_commit_close(commit);
3420 if (err)
3421 break;
3422 TAILQ_FOREACH(pe, &changed_paths, entry) {
3423 free((char *)pe->path);
3424 free(pe->data);
3426 got_pathlist_free(&changed_paths);
3429 done:
3430 while (!SIMPLEQ_EMPTY(&reversed_commits)) {
3431 qid = SIMPLEQ_FIRST(&reversed_commits);
3432 SIMPLEQ_REMOVE_HEAD(&reversed_commits, entry);
3433 got_object_qid_free(qid);
3435 TAILQ_FOREACH(pe, &changed_paths, entry) {
3436 free((char *)pe->path);
3437 free(pe->data);
3439 got_pathlist_free(&changed_paths);
3440 if (search_pattern)
3441 regfree(&regex);
3442 got_commit_graph_close(graph);
3443 return err;
3446 __dead static void
3447 usage_log(void)
3449 fprintf(stderr, "usage: %s log [-b] [-c commit] [-C number] [ -l N ] "
3450 "[-p] [-P] [-x commit] [-s search-pattern] [-r repository-path] "
3451 "[-R] [path]\n", getprogname());
3452 exit(1);
3455 static int
3456 get_default_log_limit(void)
3458 const char *got_default_log_limit;
3459 long long n;
3460 const char *errstr;
3462 got_default_log_limit = getenv("GOT_LOG_DEFAULT_LIMIT");
3463 if (got_default_log_limit == NULL)
3464 return 0;
3465 n = strtonum(got_default_log_limit, 0, INT_MAX, &errstr);
3466 if (errstr != NULL)
3467 return 0;
3468 return n;
3471 static const struct got_error *
3472 resolve_commit_arg(struct got_object_id **id, const char *commit_arg,
3473 struct got_repository *repo)
3475 const struct got_error *err = NULL;
3476 struct got_reference *ref;
3478 *id = NULL;
3480 err = got_ref_open(&ref, repo, commit_arg, 0);
3481 if (err == NULL) {
3482 int obj_type;
3483 err = got_ref_resolve(id, repo, ref);
3484 got_ref_close(ref);
3485 if (err)
3486 return err;
3487 err = got_object_get_type(&obj_type, repo, *id);
3488 if (err)
3489 return err;
3490 if (obj_type == GOT_OBJ_TYPE_TAG) {
3491 struct got_tag_object *tag;
3492 err = got_object_open_as_tag(&tag, repo, *id);
3493 if (err)
3494 return err;
3495 if (got_object_tag_get_object_type(tag) !=
3496 GOT_OBJ_TYPE_COMMIT) {
3497 got_object_tag_close(tag);
3498 return got_error(GOT_ERR_OBJ_TYPE);
3500 free(*id);
3501 *id = got_object_id_dup(
3502 got_object_tag_get_object_id(tag));
3503 if (*id == NULL)
3504 err = got_error_from_errno(
3505 "got_object_id_dup");
3506 got_object_tag_close(tag);
3507 if (err)
3508 return err;
3509 } else if (obj_type != GOT_OBJ_TYPE_COMMIT)
3510 return got_error(GOT_ERR_OBJ_TYPE);
3511 } else {
3512 err = got_repo_match_object_id_prefix(id, commit_arg,
3513 GOT_OBJ_TYPE_COMMIT, repo);
3516 return err;
3519 static const struct got_error *
3520 cmd_log(int argc, char *argv[])
3522 const struct got_error *error;
3523 struct got_repository *repo = NULL;
3524 struct got_worktree *worktree = NULL;
3525 struct got_object_id *start_id = NULL, *end_id = NULL;
3526 char *repo_path = NULL, *path = NULL, *cwd = NULL, *in_repo_path = NULL;
3527 const char *start_commit = NULL, *end_commit = NULL;
3528 const char *search_pattern = NULL;
3529 int diff_context = -1, ch;
3530 int show_changed_paths = 0, show_patch = 0, limit = 0, log_branches = 0;
3531 int reverse_display_order = 0;
3532 const char *errstr;
3533 struct got_reflist_head refs;
3535 SIMPLEQ_INIT(&refs);
3537 #ifndef PROFILE
3538 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
3539 NULL)
3540 == -1)
3541 err(1, "pledge");
3542 #endif
3544 limit = get_default_log_limit();
3546 while ((ch = getopt(argc, argv, "bpPc:C:l:r:Rs:x:")) != -1) {
3547 switch (ch) {
3548 case 'p':
3549 show_patch = 1;
3550 break;
3551 case 'P':
3552 show_changed_paths = 1;
3553 break;
3554 case 'c':
3555 start_commit = optarg;
3556 break;
3557 case 'C':
3558 diff_context = strtonum(optarg, 0, GOT_DIFF_MAX_CONTEXT,
3559 &errstr);
3560 if (errstr != NULL)
3561 err(1, "-C option %s", errstr);
3562 break;
3563 case 'l':
3564 limit = strtonum(optarg, 0, INT_MAX, &errstr);
3565 if (errstr != NULL)
3566 err(1, "-l option %s", errstr);
3567 break;
3568 case 'b':
3569 log_branches = 1;
3570 break;
3571 case 'r':
3572 repo_path = realpath(optarg, NULL);
3573 if (repo_path == NULL)
3574 return got_error_from_errno2("realpath",
3575 optarg);
3576 got_path_strip_trailing_slashes(repo_path);
3577 break;
3578 case 'R':
3579 reverse_display_order = 1;
3580 break;
3581 case 's':
3582 search_pattern = optarg;
3583 break;
3584 case 'x':
3585 end_commit = optarg;
3586 break;
3587 default:
3588 usage_log();
3589 /* NOTREACHED */
3593 argc -= optind;
3594 argv += optind;
3596 if (diff_context == -1)
3597 diff_context = 3;
3598 else if (!show_patch)
3599 errx(1, "-C reguires -p");
3601 cwd = getcwd(NULL, 0);
3602 if (cwd == NULL) {
3603 error = got_error_from_errno("getcwd");
3604 goto done;
3607 if (repo_path == NULL) {
3608 error = got_worktree_open(&worktree, cwd);
3609 if (error && error->code != GOT_ERR_NOT_WORKTREE)
3610 goto done;
3611 error = NULL;
3614 if (argc == 0) {
3615 path = strdup("");
3616 if (path == NULL) {
3617 error = got_error_from_errno("strdup");
3618 goto done;
3620 } else if (argc == 1) {
3621 if (worktree) {
3622 error = got_worktree_resolve_path(&path, worktree,
3623 argv[0]);
3624 if (error)
3625 goto done;
3626 } else {
3627 path = strdup(argv[0]);
3628 if (path == NULL) {
3629 error = got_error_from_errno("strdup");
3630 goto done;
3633 } else
3634 usage_log();
3636 if (repo_path == NULL) {
3637 repo_path = worktree ?
3638 strdup(got_worktree_get_repo_path(worktree)) : strdup(cwd);
3640 if (repo_path == NULL) {
3641 error = got_error_from_errno("strdup");
3642 goto done;
3645 error = got_repo_open(&repo, repo_path, NULL);
3646 if (error != NULL)
3647 goto done;
3649 error = apply_unveil(got_repo_get_path(repo), 1,
3650 worktree ? got_worktree_get_root_path(worktree) : NULL);
3651 if (error)
3652 goto done;
3654 if (start_commit == NULL) {
3655 struct got_reference *head_ref;
3656 struct got_commit_object *commit = NULL;
3657 error = got_ref_open(&head_ref, repo,
3658 worktree ? got_worktree_get_head_ref_name(worktree)
3659 : GOT_REF_HEAD, 0);
3660 if (error != NULL)
3661 goto done;
3662 error = got_ref_resolve(&start_id, repo, head_ref);
3663 got_ref_close(head_ref);
3664 if (error != NULL)
3665 goto done;
3666 error = got_object_open_as_commit(&commit, repo,
3667 start_id);
3668 if (error != NULL)
3669 goto done;
3670 got_object_commit_close(commit);
3671 } else {
3672 error = resolve_commit_arg(&start_id, start_commit, repo);
3673 if (error != NULL)
3674 goto done;
3676 if (end_commit != NULL) {
3677 error = resolve_commit_arg(&end_id, end_commit, repo);
3678 if (error != NULL)
3679 goto done;
3682 if (worktree) {
3683 const char *prefix = got_worktree_get_path_prefix(worktree);
3684 char *p;
3685 if (asprintf(&p, "%s%s%s", prefix,
3686 (strcmp(prefix, "/") != 0) ? "/" : "", path) == -1) {
3687 error = got_error_from_errno("asprintf");
3688 goto done;
3690 error = got_repo_map_path(&in_repo_path, repo, p, 0);
3691 free(p);
3692 } else
3693 error = got_repo_map_path(&in_repo_path, repo, path, 1);
3694 if (error != NULL)
3695 goto done;
3696 if (in_repo_path) {
3697 free(path);
3698 path = in_repo_path;
3701 error = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name, NULL);
3702 if (error)
3703 goto done;
3705 error = print_commits(start_id, end_id, repo, path, show_changed_paths,
3706 show_patch, search_pattern, diff_context, limit, log_branches,
3707 reverse_display_order, &refs);
3708 done:
3709 free(path);
3710 free(repo_path);
3711 free(cwd);
3712 if (worktree)
3713 got_worktree_close(worktree);
3714 if (repo) {
3715 const struct got_error *repo_error;
3716 repo_error = got_repo_close(repo);
3717 if (error == NULL)
3718 error = repo_error;
3720 got_ref_list_free(&refs);
3721 return error;
3724 __dead static void
3725 usage_diff(void)
3727 fprintf(stderr, "usage: %s diff [-C number] [-r repository-path] [-s] "
3728 "[-w] [object1 object2 | path]\n", getprogname());
3729 exit(1);
3732 struct print_diff_arg {
3733 struct got_repository *repo;
3734 struct got_worktree *worktree;
3735 int diff_context;
3736 const char *id_str;
3737 int header_shown;
3738 int diff_staged;
3739 int ignore_whitespace;
3743 * Create a file which contains the target path of a symlink so we can feed
3744 * it as content to the diff engine.
3746 static const struct got_error *
3747 get_symlink_target_file(int *fd, int dirfd, const char *de_name,
3748 const char *abspath)
3750 const struct got_error *err = NULL;
3751 char target_path[PATH_MAX];
3752 ssize_t target_len, outlen;
3754 *fd = -1;
3756 if (dirfd != -1) {
3757 target_len = readlinkat(dirfd, de_name, target_path, PATH_MAX);
3758 if (target_len == -1)
3759 return got_error_from_errno2("readlinkat", abspath);
3760 } else {
3761 target_len = readlink(abspath, target_path, PATH_MAX);
3762 if (target_len == -1)
3763 return got_error_from_errno2("readlink", abspath);
3766 *fd = got_opentempfd();
3767 if (*fd == -1)
3768 return got_error_from_errno("got_opentempfd");
3770 outlen = write(*fd, target_path, target_len);
3771 if (outlen == -1) {
3772 err = got_error_from_errno("got_opentempfd");
3773 goto done;
3776 if (lseek(*fd, 0, SEEK_SET) == -1) {
3777 err = got_error_from_errno2("lseek", abspath);
3778 goto done;
3780 done:
3781 if (err) {
3782 close(*fd);
3783 *fd = -1;
3785 return err;
3788 static const struct got_error *
3789 print_diff(void *arg, unsigned char status, unsigned char staged_status,
3790 const char *path, struct got_object_id *blob_id,
3791 struct got_object_id *staged_blob_id, struct got_object_id *commit_id,
3792 int dirfd, const char *de_name)
3794 struct print_diff_arg *a = arg;
3795 const struct got_error *err = NULL;
3796 struct got_blob_object *blob1 = NULL;
3797 int fd = -1;
3798 FILE *f2 = NULL;
3799 char *abspath = NULL, *label1 = NULL;
3800 struct stat sb;
3802 if (a->diff_staged) {
3803 if (staged_status != GOT_STATUS_MODIFY &&
3804 staged_status != GOT_STATUS_ADD &&
3805 staged_status != GOT_STATUS_DELETE)
3806 return NULL;
3807 } else {
3808 if (staged_status == GOT_STATUS_DELETE)
3809 return NULL;
3810 if (status == GOT_STATUS_NONEXISTENT)
3811 return got_error_set_errno(ENOENT, path);
3812 if (status != GOT_STATUS_MODIFY &&
3813 status != GOT_STATUS_ADD &&
3814 status != GOT_STATUS_DELETE &&
3815 status != GOT_STATUS_CONFLICT)
3816 return NULL;
3819 if (!a->header_shown) {
3820 printf("diff %s %s%s\n", a->id_str,
3821 got_worktree_get_root_path(a->worktree),
3822 a->diff_staged ? " (staged changes)" : "");
3823 a->header_shown = 1;
3826 if (a->diff_staged) {
3827 const char *label1 = NULL, *label2 = NULL;
3828 switch (staged_status) {
3829 case GOT_STATUS_MODIFY:
3830 label1 = path;
3831 label2 = path;
3832 break;
3833 case GOT_STATUS_ADD:
3834 label2 = path;
3835 break;
3836 case GOT_STATUS_DELETE:
3837 label1 = path;
3838 break;
3839 default:
3840 return got_error(GOT_ERR_FILE_STATUS);
3842 return got_diff_objects_as_blobs(blob_id, staged_blob_id,
3843 label1, label2, a->diff_context, a->ignore_whitespace,
3844 a->repo, stdout);
3847 if (staged_status == GOT_STATUS_ADD ||
3848 staged_status == GOT_STATUS_MODIFY) {
3849 char *id_str;
3850 err = got_object_open_as_blob(&blob1, a->repo, staged_blob_id,
3851 8192);
3852 if (err)
3853 goto done;
3854 err = got_object_id_str(&id_str, staged_blob_id);
3855 if (err)
3856 goto done;
3857 if (asprintf(&label1, "%s (staged)", id_str) == -1) {
3858 err = got_error_from_errno("asprintf");
3859 free(id_str);
3860 goto done;
3862 free(id_str);
3863 } else if (status != GOT_STATUS_ADD) {
3864 err = got_object_open_as_blob(&blob1, a->repo, blob_id, 8192);
3865 if (err)
3866 goto done;
3869 if (status != GOT_STATUS_DELETE) {
3870 if (asprintf(&abspath, "%s/%s",
3871 got_worktree_get_root_path(a->worktree), path) == -1) {
3872 err = got_error_from_errno("asprintf");
3873 goto done;
3876 if (dirfd != -1) {
3877 fd = openat(dirfd, de_name, O_RDONLY | O_NOFOLLOW);
3878 if (fd == -1) {
3879 if (errno != ELOOP) {
3880 err = got_error_from_errno2("openat",
3881 abspath);
3882 goto done;
3884 err = get_symlink_target_file(&fd, dirfd,
3885 de_name, abspath);
3886 if (err)
3887 goto done;
3889 } else {
3890 fd = open(abspath, O_RDONLY | O_NOFOLLOW);
3891 if (fd == -1) {
3892 if (errno != ELOOP) {
3893 err = got_error_from_errno2("open",
3894 abspath);
3895 goto done;
3897 err = get_symlink_target_file(&fd, dirfd,
3898 de_name, abspath);
3899 if (err)
3900 goto done;
3903 if (fstat(fd, &sb) == -1) {
3904 err = got_error_from_errno2("fstat", abspath);
3905 goto done;
3907 f2 = fdopen(fd, "r");
3908 if (f2 == NULL) {
3909 err = got_error_from_errno2("fdopen", abspath);
3910 goto done;
3912 fd = -1;
3913 } else
3914 sb.st_size = 0;
3916 err = got_diff_blob_file(blob1, label1, f2, sb.st_size, path,
3917 a->diff_context, a->ignore_whitespace, stdout);
3918 done:
3919 if (blob1)
3920 got_object_blob_close(blob1);
3921 if (f2 && fclose(f2) == EOF && err == NULL)
3922 err = got_error_from_errno("fclose");
3923 if (fd != -1 && close(fd) == -1 && err == NULL)
3924 err = got_error_from_errno("close");
3925 free(abspath);
3926 return err;
3929 static const struct got_error *
3930 cmd_diff(int argc, char *argv[])
3932 const struct got_error *error;
3933 struct got_repository *repo = NULL;
3934 struct got_worktree *worktree = NULL;
3935 char *cwd = NULL, *repo_path = NULL;
3936 struct got_object_id *id1 = NULL, *id2 = NULL;
3937 const char *id_str1 = NULL, *id_str2 = NULL;
3938 char *label1 = NULL, *label2 = NULL;
3939 int type1, type2;
3940 int diff_context = 3, diff_staged = 0, ignore_whitespace = 0, ch;
3941 const char *errstr;
3942 char *path = NULL;
3944 #ifndef PROFILE
3945 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
3946 NULL) == -1)
3947 err(1, "pledge");
3948 #endif
3950 while ((ch = getopt(argc, argv, "C:r:sw")) != -1) {
3951 switch (ch) {
3952 case 'C':
3953 diff_context = strtonum(optarg, 0, GOT_DIFF_MAX_CONTEXT,
3954 &errstr);
3955 if (errstr != NULL)
3956 err(1, "-C option %s", errstr);
3957 break;
3958 case 'r':
3959 repo_path = realpath(optarg, NULL);
3960 if (repo_path == NULL)
3961 return got_error_from_errno2("realpath",
3962 optarg);
3963 got_path_strip_trailing_slashes(repo_path);
3964 break;
3965 case 's':
3966 diff_staged = 1;
3967 break;
3968 case 'w':
3969 ignore_whitespace = 1;
3970 break;
3971 default:
3972 usage_diff();
3973 /* NOTREACHED */
3977 argc -= optind;
3978 argv += optind;
3980 cwd = getcwd(NULL, 0);
3981 if (cwd == NULL) {
3982 error = got_error_from_errno("getcwd");
3983 goto done;
3985 if (argc <= 1) {
3986 if (repo_path)
3987 errx(1,
3988 "-r option can't be used when diffing a work tree");
3989 error = got_worktree_open(&worktree, cwd);
3990 if (error) {
3991 if (error->code == GOT_ERR_NOT_WORKTREE)
3992 error = wrap_not_worktree_error(error, "diff",
3993 cwd);
3994 goto done;
3996 repo_path = strdup(got_worktree_get_repo_path(worktree));
3997 if (repo_path == NULL) {
3998 error = got_error_from_errno("strdup");
3999 goto done;
4001 if (argc == 1) {
4002 error = got_worktree_resolve_path(&path, worktree,
4003 argv[0]);
4004 if (error)
4005 goto done;
4006 } else {
4007 path = strdup("");
4008 if (path == NULL) {
4009 error = got_error_from_errno("strdup");
4010 goto done;
4013 } else if (argc == 2) {
4014 if (diff_staged)
4015 errx(1, "-s option can't be used when diffing "
4016 "objects in repository");
4017 id_str1 = argv[0];
4018 id_str2 = argv[1];
4019 if (repo_path == NULL) {
4020 error = got_worktree_open(&worktree, cwd);
4021 if (error && error->code != GOT_ERR_NOT_WORKTREE)
4022 goto done;
4023 if (worktree) {
4024 repo_path = strdup(
4025 got_worktree_get_repo_path(worktree));
4026 if (repo_path == NULL) {
4027 error = got_error_from_errno("strdup");
4028 goto done;
4030 } else {
4031 repo_path = strdup(cwd);
4032 if (repo_path == NULL) {
4033 error = got_error_from_errno("strdup");
4034 goto done;
4038 } else
4039 usage_diff();
4041 error = got_repo_open(&repo, repo_path, NULL);
4042 free(repo_path);
4043 if (error != NULL)
4044 goto done;
4046 error = apply_unveil(got_repo_get_path(repo), 1,
4047 worktree ? got_worktree_get_root_path(worktree) : NULL);
4048 if (error)
4049 goto done;
4051 if (argc <= 1) {
4052 struct print_diff_arg arg;
4053 struct got_pathlist_head paths;
4054 char *id_str;
4056 TAILQ_INIT(&paths);
4058 error = got_object_id_str(&id_str,
4059 got_worktree_get_base_commit_id(worktree));
4060 if (error)
4061 goto done;
4062 arg.repo = repo;
4063 arg.worktree = worktree;
4064 arg.diff_context = diff_context;
4065 arg.id_str = id_str;
4066 arg.header_shown = 0;
4067 arg.diff_staged = diff_staged;
4068 arg.ignore_whitespace = ignore_whitespace;
4070 error = got_pathlist_append(&paths, path, NULL);
4071 if (error)
4072 goto done;
4074 error = got_worktree_status(worktree, &paths, repo, print_diff,
4075 &arg, check_cancelled, NULL);
4076 free(id_str);
4077 got_pathlist_free(&paths);
4078 goto done;
4081 error = got_repo_match_object_id(&id1, &label1, id_str1,
4082 GOT_OBJ_TYPE_ANY, 1, repo);
4083 if (error)
4084 goto done;
4086 error = got_repo_match_object_id(&id2, &label2, id_str2,
4087 GOT_OBJ_TYPE_ANY, 1, repo);
4088 if (error)
4089 goto done;
4091 error = got_object_get_type(&type1, repo, id1);
4092 if (error)
4093 goto done;
4095 error = got_object_get_type(&type2, repo, id2);
4096 if (error)
4097 goto done;
4099 if (type1 != type2) {
4100 error = got_error(GOT_ERR_OBJ_TYPE);
4101 goto done;
4104 switch (type1) {
4105 case GOT_OBJ_TYPE_BLOB:
4106 error = got_diff_objects_as_blobs(id1, id2, NULL, NULL,
4107 diff_context, ignore_whitespace, repo, stdout);
4108 break;
4109 case GOT_OBJ_TYPE_TREE:
4110 error = got_diff_objects_as_trees(id1, id2, "", "",
4111 diff_context, ignore_whitespace, repo, stdout);
4112 break;
4113 case GOT_OBJ_TYPE_COMMIT:
4114 printf("diff %s %s\n", label1, label2);
4115 error = got_diff_objects_as_commits(id1, id2, diff_context,
4116 ignore_whitespace, repo, stdout);
4117 break;
4118 default:
4119 error = got_error(GOT_ERR_OBJ_TYPE);
4121 done:
4122 free(label1);
4123 free(label2);
4124 free(id1);
4125 free(id2);
4126 free(path);
4127 if (worktree)
4128 got_worktree_close(worktree);
4129 if (repo) {
4130 const struct got_error *repo_error;
4131 repo_error = got_repo_close(repo);
4132 if (error == NULL)
4133 error = repo_error;
4135 return error;
4138 __dead static void
4139 usage_blame(void)
4141 fprintf(stderr,
4142 "usage: %s blame [-c commit] [-r repository-path] path\n",
4143 getprogname());
4144 exit(1);
4147 struct blame_line {
4148 int annotated;
4149 char *id_str;
4150 char *committer;
4151 char datebuf[11]; /* YYYY-MM-DD + NUL */
4154 struct blame_cb_args {
4155 struct blame_line *lines;
4156 int nlines;
4157 int nlines_prec;
4158 int lineno_cur;
4159 off_t *line_offsets;
4160 FILE *f;
4161 struct got_repository *repo;
4164 static const struct got_error *
4165 blame_cb(void *arg, int nlines, int lineno, struct got_object_id *id)
4167 const struct got_error *err = NULL;
4168 struct blame_cb_args *a = arg;
4169 struct blame_line *bline;
4170 char *line = NULL;
4171 size_t linesize = 0;
4172 struct got_commit_object *commit = NULL;
4173 off_t offset;
4174 struct tm tm;
4175 time_t committer_time;
4177 if (nlines != a->nlines ||
4178 (lineno != -1 && lineno < 1) || lineno > a->nlines)
4179 return got_error(GOT_ERR_RANGE);
4181 if (sigint_received)
4182 return got_error(GOT_ERR_ITER_COMPLETED);
4184 if (lineno == -1)
4185 return NULL; /* no change in this commit */
4187 /* Annotate this line. */
4188 bline = &a->lines[lineno - 1];
4189 if (bline->annotated)
4190 return NULL;
4191 err = got_object_id_str(&bline->id_str, id);
4192 if (err)
4193 return err;
4195 err = got_object_open_as_commit(&commit, a->repo, id);
4196 if (err)
4197 goto done;
4199 bline->committer = strdup(got_object_commit_get_committer(commit));
4200 if (bline->committer == NULL) {
4201 err = got_error_from_errno("strdup");
4202 goto done;
4205 committer_time = got_object_commit_get_committer_time(commit);
4206 if (localtime_r(&committer_time, &tm) == NULL)
4207 return got_error_from_errno("localtime_r");
4208 if (strftime(bline->datebuf, sizeof(bline->datebuf), "%G-%m-%d",
4209 &tm) >= sizeof(bline->datebuf)) {
4210 err = got_error(GOT_ERR_NO_SPACE);
4211 goto done;
4213 bline->annotated = 1;
4215 /* Print lines annotated so far. */
4216 bline = &a->lines[a->lineno_cur - 1];
4217 if (!bline->annotated)
4218 goto done;
4220 offset = a->line_offsets[a->lineno_cur - 1];
4221 if (fseeko(a->f, offset, SEEK_SET) == -1) {
4222 err = got_error_from_errno("fseeko");
4223 goto done;
4226 while (bline->annotated) {
4227 char *smallerthan, *at, *nl, *committer;
4228 size_t len;
4230 if (getline(&line, &linesize, a->f) == -1) {
4231 if (ferror(a->f))
4232 err = got_error_from_errno("getline");
4233 break;
4236 committer = bline->committer;
4237 smallerthan = strchr(committer, '<');
4238 if (smallerthan && smallerthan[1] != '\0')
4239 committer = smallerthan + 1;
4240 at = strchr(committer, '@');
4241 if (at)
4242 *at = '\0';
4243 len = strlen(committer);
4244 if (len >= 9)
4245 committer[8] = '\0';
4247 nl = strchr(line, '\n');
4248 if (nl)
4249 *nl = '\0';
4250 printf("%.*d) %.8s %s %-8s %s\n", a->nlines_prec, a->lineno_cur,
4251 bline->id_str, bline->datebuf, committer, line);
4253 a->lineno_cur++;
4254 bline = &a->lines[a->lineno_cur - 1];
4256 done:
4257 if (commit)
4258 got_object_commit_close(commit);
4259 free(line);
4260 return err;
4263 static const struct got_error *
4264 cmd_blame(int argc, char *argv[])
4266 const struct got_error *error;
4267 struct got_repository *repo = NULL;
4268 struct got_worktree *worktree = NULL;
4269 char *path, *cwd = NULL, *repo_path = NULL, *in_repo_path = NULL;
4270 char *link_target = NULL;
4271 struct got_object_id *obj_id = NULL;
4272 struct got_object_id *commit_id = NULL;
4273 struct got_blob_object *blob = NULL;
4274 char *commit_id_str = NULL;
4275 struct blame_cb_args bca;
4276 int ch, obj_type, i;
4277 size_t filesize;
4279 memset(&bca, 0, sizeof(bca));
4281 #ifndef PROFILE
4282 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
4283 NULL) == -1)
4284 err(1, "pledge");
4285 #endif
4287 while ((ch = getopt(argc, argv, "c:r:")) != -1) {
4288 switch (ch) {
4289 case 'c':
4290 commit_id_str = optarg;
4291 break;
4292 case 'r':
4293 repo_path = realpath(optarg, NULL);
4294 if (repo_path == NULL)
4295 return got_error_from_errno2("realpath",
4296 optarg);
4297 got_path_strip_trailing_slashes(repo_path);
4298 break;
4299 default:
4300 usage_blame();
4301 /* NOTREACHED */
4305 argc -= optind;
4306 argv += optind;
4308 if (argc == 1)
4309 path = argv[0];
4310 else
4311 usage_blame();
4313 cwd = getcwd(NULL, 0);
4314 if (cwd == NULL) {
4315 error = got_error_from_errno("getcwd");
4316 goto done;
4318 if (repo_path == NULL) {
4319 error = got_worktree_open(&worktree, cwd);
4320 if (error && error->code != GOT_ERR_NOT_WORKTREE)
4321 goto done;
4322 else
4323 error = NULL;
4324 if (worktree) {
4325 repo_path =
4326 strdup(got_worktree_get_repo_path(worktree));
4327 if (repo_path == NULL) {
4328 error = got_error_from_errno("strdup");
4329 if (error)
4330 goto done;
4332 } else {
4333 repo_path = strdup(cwd);
4334 if (repo_path == NULL) {
4335 error = got_error_from_errno("strdup");
4336 goto done;
4341 error = got_repo_open(&repo, repo_path, NULL);
4342 if (error != NULL)
4343 goto done;
4345 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
4346 if (error)
4347 goto done;
4349 if (worktree) {
4350 const char *prefix = got_worktree_get_path_prefix(worktree);
4351 char *p, *worktree_subdir = cwd +
4352 strlen(got_worktree_get_root_path(worktree));
4353 if (asprintf(&p, "%s%s%s%s%s",
4354 prefix, (strcmp(prefix, "/") != 0) ? "/" : "",
4355 worktree_subdir, worktree_subdir[0] ? "/" : "",
4356 path) == -1) {
4357 error = got_error_from_errno("asprintf");
4358 goto done;
4360 error = got_repo_map_path(&in_repo_path, repo, p, 0);
4361 free(p);
4362 } else {
4363 error = got_repo_map_path(&in_repo_path, repo, path, 1);
4365 if (error)
4366 goto done;
4368 if (commit_id_str == NULL) {
4369 struct got_reference *head_ref;
4370 error = got_ref_open(&head_ref, repo, worktree ?
4371 got_worktree_get_head_ref_name(worktree) : GOT_REF_HEAD, 0);
4372 if (error != NULL)
4373 goto done;
4374 error = got_ref_resolve(&commit_id, repo, head_ref);
4375 got_ref_close(head_ref);
4376 if (error != NULL)
4377 goto done;
4378 } else {
4379 error = got_repo_match_object_id(&commit_id, NULL,
4380 commit_id_str, GOT_OBJ_TYPE_COMMIT, 1, repo);
4381 if (error)
4382 goto done;
4385 error = got_object_resolve_symlinks(&link_target, in_repo_path,
4386 commit_id, repo);
4387 if (error)
4388 goto done;
4390 error = got_object_id_by_path(&obj_id, repo, commit_id,
4391 link_target ? link_target : in_repo_path);
4392 if (error)
4393 goto done;
4395 error = got_object_get_type(&obj_type, repo, obj_id);
4396 if (error)
4397 goto done;
4399 if (obj_type != GOT_OBJ_TYPE_BLOB) {
4400 error = got_error_path(link_target ? link_target : in_repo_path,
4401 GOT_ERR_OBJ_TYPE);
4402 goto done;
4405 error = got_object_open_as_blob(&blob, repo, obj_id, 8192);
4406 if (error)
4407 goto done;
4408 bca.f = got_opentemp();
4409 if (bca.f == NULL) {
4410 error = got_error_from_errno("got_opentemp");
4411 goto done;
4413 error = got_object_blob_dump_to_file(&filesize, &bca.nlines,
4414 &bca.line_offsets, bca.f, blob);
4415 if (error || bca.nlines == 0)
4416 goto done;
4418 /* Don't include \n at EOF in the blame line count. */
4419 if (bca.line_offsets[bca.nlines - 1] == filesize)
4420 bca.nlines--;
4422 bca.lines = calloc(bca.nlines, sizeof(*bca.lines));
4423 if (bca.lines == NULL) {
4424 error = got_error_from_errno("calloc");
4425 goto done;
4427 bca.lineno_cur = 1;
4428 bca.nlines_prec = 0;
4429 i = bca.nlines;
4430 while (i > 0) {
4431 i /= 10;
4432 bca.nlines_prec++;
4434 bca.repo = repo;
4436 error = got_blame(link_target ? link_target : in_repo_path, commit_id,
4437 repo, blame_cb, &bca, check_cancelled, NULL);
4438 done:
4439 free(in_repo_path);
4440 free(link_target);
4441 free(repo_path);
4442 free(cwd);
4443 free(commit_id);
4444 free(obj_id);
4445 if (blob)
4446 got_object_blob_close(blob);
4447 if (worktree)
4448 got_worktree_close(worktree);
4449 if (repo) {
4450 const struct got_error *repo_error;
4451 repo_error = got_repo_close(repo);
4452 if (error == NULL)
4453 error = repo_error;
4455 if (bca.lines) {
4456 for (i = 0; i < bca.nlines; i++) {
4457 struct blame_line *bline = &bca.lines[i];
4458 free(bline->id_str);
4459 free(bline->committer);
4461 free(bca.lines);
4463 free(bca.line_offsets);
4464 if (bca.f && fclose(bca.f) == EOF && error == NULL)
4465 error = got_error_from_errno("fclose");
4466 return error;
4469 __dead static void
4470 usage_tree(void)
4472 fprintf(stderr,
4473 "usage: %s tree [-c commit] [-r repository-path] [-iR] [path]\n",
4474 getprogname());
4475 exit(1);
4478 static const struct got_error *
4479 print_entry(struct got_tree_entry *te, const char *id, const char *path,
4480 const char *root_path, struct got_repository *repo)
4482 const struct got_error *err = NULL;
4483 int is_root_path = (strcmp(path, root_path) == 0);
4484 const char *modestr = "";
4485 mode_t mode = got_tree_entry_get_mode(te);
4486 char *link_target = NULL;
4488 path += strlen(root_path);
4489 while (path[0] == '/')
4490 path++;
4492 if (got_object_tree_entry_is_submodule(te))
4493 modestr = "$";
4494 else if (S_ISLNK(mode)) {
4495 int i;
4497 err = got_tree_entry_get_symlink_target(&link_target, te, repo);
4498 if (err)
4499 return err;
4500 for (i = 0; i < strlen(link_target); i++) {
4501 if (!isprint((unsigned char)link_target[i]))
4502 link_target[i] = '?';
4505 modestr = "@";
4507 else if (S_ISDIR(mode))
4508 modestr = "/";
4509 else if (mode & S_IXUSR)
4510 modestr = "*";
4512 printf("%s%s%s%s%s%s%s\n", id ? id : "", path,
4513 is_root_path ? "" : "/", got_tree_entry_get_name(te), modestr,
4514 link_target ? " -> ": "", link_target ? link_target : "");
4516 free(link_target);
4517 return NULL;
4520 static const struct got_error *
4521 print_tree(const char *path, struct got_object_id *commit_id,
4522 int show_ids, int recurse, const char *root_path,
4523 struct got_repository *repo)
4525 const struct got_error *err = NULL;
4526 struct got_object_id *tree_id = NULL;
4527 struct got_tree_object *tree = NULL;
4528 int nentries, i;
4530 err = got_object_id_by_path(&tree_id, repo, commit_id, path);
4531 if (err)
4532 goto done;
4534 err = got_object_open_as_tree(&tree, repo, tree_id);
4535 if (err)
4536 goto done;
4537 nentries = got_object_tree_get_nentries(tree);
4538 for (i = 0; i < nentries; i++) {
4539 struct got_tree_entry *te;
4540 char *id = NULL;
4542 if (sigint_received || sigpipe_received)
4543 break;
4545 te = got_object_tree_get_entry(tree, i);
4546 if (show_ids) {
4547 char *id_str;
4548 err = got_object_id_str(&id_str,
4549 got_tree_entry_get_id(te));
4550 if (err)
4551 goto done;
4552 if (asprintf(&id, "%s ", id_str) == -1) {
4553 err = got_error_from_errno("asprintf");
4554 free(id_str);
4555 goto done;
4557 free(id_str);
4559 err = print_entry(te, id, path, root_path, repo);
4560 free(id);
4561 if (err)
4562 goto done;
4564 if (recurse && S_ISDIR(got_tree_entry_get_mode(te))) {
4565 char *child_path;
4566 if (asprintf(&child_path, "%s%s%s", path,
4567 path[0] == '/' && path[1] == '\0' ? "" : "/",
4568 got_tree_entry_get_name(te)) == -1) {
4569 err = got_error_from_errno("asprintf");
4570 goto done;
4572 err = print_tree(child_path, commit_id, show_ids, 1,
4573 root_path, repo);
4574 free(child_path);
4575 if (err)
4576 goto done;
4579 done:
4580 if (tree)
4581 got_object_tree_close(tree);
4582 free(tree_id);
4583 return err;
4586 static const struct got_error *
4587 cmd_tree(int argc, char *argv[])
4589 const struct got_error *error;
4590 struct got_repository *repo = NULL;
4591 struct got_worktree *worktree = NULL;
4592 const char *path, *refname = NULL;
4593 char *cwd = NULL, *repo_path = NULL, *in_repo_path = NULL;
4594 struct got_object_id *commit_id = NULL;
4595 char *commit_id_str = NULL;
4596 int show_ids = 0, recurse = 0;
4597 int ch;
4599 #ifndef PROFILE
4600 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
4601 NULL) == -1)
4602 err(1, "pledge");
4603 #endif
4605 while ((ch = getopt(argc, argv, "c:r:iR")) != -1) {
4606 switch (ch) {
4607 case 'c':
4608 commit_id_str = optarg;
4609 break;
4610 case 'r':
4611 repo_path = realpath(optarg, NULL);
4612 if (repo_path == NULL)
4613 return got_error_from_errno2("realpath",
4614 optarg);
4615 got_path_strip_trailing_slashes(repo_path);
4616 break;
4617 case 'i':
4618 show_ids = 1;
4619 break;
4620 case 'R':
4621 recurse = 1;
4622 break;
4623 default:
4624 usage_tree();
4625 /* NOTREACHED */
4629 argc -= optind;
4630 argv += optind;
4632 if (argc == 1)
4633 path = argv[0];
4634 else if (argc > 1)
4635 usage_tree();
4636 else
4637 path = NULL;
4639 cwd = getcwd(NULL, 0);
4640 if (cwd == NULL) {
4641 error = got_error_from_errno("getcwd");
4642 goto done;
4644 if (repo_path == NULL) {
4645 error = got_worktree_open(&worktree, cwd);
4646 if (error && error->code != GOT_ERR_NOT_WORKTREE)
4647 goto done;
4648 else
4649 error = NULL;
4650 if (worktree) {
4651 repo_path =
4652 strdup(got_worktree_get_repo_path(worktree));
4653 if (repo_path == NULL)
4654 error = got_error_from_errno("strdup");
4655 if (error)
4656 goto done;
4657 } else {
4658 repo_path = strdup(cwd);
4659 if (repo_path == NULL) {
4660 error = got_error_from_errno("strdup");
4661 goto done;
4666 error = got_repo_open(&repo, repo_path, NULL);
4667 if (error != NULL)
4668 goto done;
4670 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
4671 if (error)
4672 goto done;
4674 if (path == NULL) {
4675 if (worktree) {
4676 char *p, *worktree_subdir = cwd +
4677 strlen(got_worktree_get_root_path(worktree));
4678 if (asprintf(&p, "%s/%s",
4679 got_worktree_get_path_prefix(worktree),
4680 worktree_subdir) == -1) {
4681 error = got_error_from_errno("asprintf");
4682 goto done;
4684 error = got_repo_map_path(&in_repo_path, repo, p, 0);
4685 free(p);
4686 if (error)
4687 goto done;
4688 } else
4689 path = "/";
4691 if (in_repo_path == NULL) {
4692 error = got_repo_map_path(&in_repo_path, repo, path, 1);
4693 if (error != NULL)
4694 goto done;
4697 if (commit_id_str == NULL) {
4698 struct got_reference *head_ref;
4699 if (worktree)
4700 refname = got_worktree_get_head_ref_name(worktree);
4701 else
4702 refname = GOT_REF_HEAD;
4703 error = got_ref_open(&head_ref, repo, refname, 0);
4704 if (error != NULL)
4705 goto done;
4706 error = got_ref_resolve(&commit_id, repo, head_ref);
4707 got_ref_close(head_ref);
4708 if (error != NULL)
4709 goto done;
4710 } else {
4711 error = got_repo_match_object_id(&commit_id, NULL,
4712 commit_id_str, GOT_OBJ_TYPE_COMMIT, 1, repo);
4713 if (error)
4714 goto done;
4717 error = print_tree(in_repo_path, commit_id, show_ids, recurse,
4718 in_repo_path, repo);
4719 done:
4720 free(in_repo_path);
4721 free(repo_path);
4722 free(cwd);
4723 free(commit_id);
4724 if (worktree)
4725 got_worktree_close(worktree);
4726 if (repo) {
4727 const struct got_error *repo_error;
4728 repo_error = got_repo_close(repo);
4729 if (error == NULL)
4730 error = repo_error;
4732 return error;
4735 __dead static void
4736 usage_status(void)
4738 fprintf(stderr, "usage: %s status [-s status-codes ] [path ...]\n",
4739 getprogname());
4740 exit(1);
4743 static const struct got_error *
4744 print_status(void *arg, unsigned char status, unsigned char staged_status,
4745 const char *path, struct got_object_id *blob_id,
4746 struct got_object_id *staged_blob_id, struct got_object_id *commit_id,
4747 int dirfd, const char *de_name)
4749 if (status == staged_status && (status == GOT_STATUS_DELETE))
4750 status = GOT_STATUS_NO_CHANGE;
4751 if (arg) {
4752 char *status_codes = arg;
4753 size_t ncodes = strlen(status_codes);
4754 int i;
4755 for (i = 0; i < ncodes ; i++) {
4756 if (status == status_codes[i] ||
4757 staged_status == status_codes[i])
4758 break;
4760 if (i == ncodes)
4761 return NULL;
4763 printf("%c%c %s\n", status, staged_status, path);
4764 return NULL;
4767 static const struct got_error *
4768 cmd_status(int argc, char *argv[])
4770 const struct got_error *error = NULL;
4771 struct got_repository *repo = NULL;
4772 struct got_worktree *worktree = NULL;
4773 char *cwd = NULL, *status_codes = NULL;;
4774 struct got_pathlist_head paths;
4775 struct got_pathlist_entry *pe;
4776 int ch, i;
4778 TAILQ_INIT(&paths);
4780 while ((ch = getopt(argc, argv, "s:")) != -1) {
4781 switch (ch) {
4782 case 's':
4783 for (i = 0; i < strlen(optarg); i++) {
4784 switch (optarg[i]) {
4785 case GOT_STATUS_MODIFY:
4786 case GOT_STATUS_ADD:
4787 case GOT_STATUS_DELETE:
4788 case GOT_STATUS_CONFLICT:
4789 case GOT_STATUS_MISSING:
4790 case GOT_STATUS_OBSTRUCTED:
4791 case GOT_STATUS_UNVERSIONED:
4792 case GOT_STATUS_MODE_CHANGE:
4793 case GOT_STATUS_NONEXISTENT:
4794 break;
4795 default:
4796 errx(1, "invalid status code '%c'",
4797 optarg[i]);
4800 status_codes = optarg;
4801 break;
4802 default:
4803 usage_status();
4804 /* NOTREACHED */
4808 argc -= optind;
4809 argv += optind;
4811 #ifndef PROFILE
4812 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
4813 NULL) == -1)
4814 err(1, "pledge");
4815 #endif
4816 cwd = getcwd(NULL, 0);
4817 if (cwd == NULL) {
4818 error = got_error_from_errno("getcwd");
4819 goto done;
4822 error = got_worktree_open(&worktree, cwd);
4823 if (error) {
4824 if (error->code == GOT_ERR_NOT_WORKTREE)
4825 error = wrap_not_worktree_error(error, "status", cwd);
4826 goto done;
4829 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
4830 NULL);
4831 if (error != NULL)
4832 goto done;
4834 error = apply_unveil(got_repo_get_path(repo), 1,
4835 got_worktree_get_root_path(worktree));
4836 if (error)
4837 goto done;
4839 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
4840 if (error)
4841 goto done;
4843 error = got_worktree_status(worktree, &paths, repo, print_status,
4844 status_codes, check_cancelled, NULL);
4845 done:
4846 TAILQ_FOREACH(pe, &paths, entry)
4847 free((char *)pe->path);
4848 got_pathlist_free(&paths);
4849 free(cwd);
4850 return error;
4853 __dead static void
4854 usage_ref(void)
4856 fprintf(stderr,
4857 "usage: %s ref [-r repository] [-l] [-c object] [-s reference] "
4858 "[-d] [name]\n",
4859 getprogname());
4860 exit(1);
4863 static const struct got_error *
4864 list_refs(struct got_repository *repo, const char *refname)
4866 static const struct got_error *err = NULL;
4867 struct got_reflist_head refs;
4868 struct got_reflist_entry *re;
4870 SIMPLEQ_INIT(&refs);
4871 err = got_ref_list(&refs, repo, refname, got_ref_cmp_by_name, NULL);
4872 if (err)
4873 return err;
4875 SIMPLEQ_FOREACH(re, &refs, entry) {
4876 char *refstr;
4877 refstr = got_ref_to_str(re->ref);
4878 if (refstr == NULL)
4879 return got_error_from_errno("got_ref_to_str");
4880 printf("%s: %s\n", got_ref_get_name(re->ref), refstr);
4881 free(refstr);
4884 got_ref_list_free(&refs);
4885 return NULL;
4888 static const struct got_error *
4889 delete_ref(struct got_repository *repo, const char *refname)
4891 const struct got_error *err = NULL;
4892 struct got_reference *ref;
4894 err = got_ref_open(&ref, repo, refname, 0);
4895 if (err)
4896 return err;
4898 err = got_ref_delete(ref, repo);
4899 got_ref_close(ref);
4900 return err;
4903 static const struct got_error *
4904 add_ref(struct got_repository *repo, const char *refname, const char *target)
4906 const struct got_error *err = NULL;
4907 struct got_object_id *id;
4908 struct got_reference *ref = NULL;
4911 * Don't let the user create a reference name with a leading '-'.
4912 * While technically a valid reference name, this case is usually
4913 * an unintended typo.
4915 if (refname[0] == '-')
4916 return got_error_path(refname, GOT_ERR_REF_NAME_MINUS);
4918 err = got_repo_match_object_id_prefix(&id, target, GOT_OBJ_TYPE_ANY,
4919 repo);
4920 if (err) {
4921 struct got_reference *target_ref;
4923 if (err->code != GOT_ERR_BAD_OBJ_ID_STR)
4924 return err;
4925 err = got_ref_open(&target_ref, repo, target, 0);
4926 if (err)
4927 return err;
4928 err = got_ref_resolve(&id, repo, target_ref);
4929 got_ref_close(target_ref);
4930 if (err)
4931 return err;
4934 err = got_ref_alloc(&ref, refname, id);
4935 if (err)
4936 goto done;
4938 err = got_ref_write(ref, repo);
4939 done:
4940 if (ref)
4941 got_ref_close(ref);
4942 free(id);
4943 return err;
4946 static const struct got_error *
4947 add_symref(struct got_repository *repo, const char *refname, const char *target)
4949 const struct got_error *err = NULL;
4950 struct got_reference *ref = NULL;
4951 struct got_reference *target_ref = NULL;
4954 * Don't let the user create a reference name with a leading '-'.
4955 * While technically a valid reference name, this case is usually
4956 * an unintended typo.
4958 if (refname[0] == '-')
4959 return got_error_path(refname, GOT_ERR_REF_NAME_MINUS);
4961 err = got_ref_open(&target_ref, repo, target, 0);
4962 if (err)
4963 return err;
4965 err = got_ref_alloc_symref(&ref, refname, target_ref);
4966 if (err)
4967 goto done;
4969 err = got_ref_write(ref, repo);
4970 done:
4971 if (target_ref)
4972 got_ref_close(target_ref);
4973 if (ref)
4974 got_ref_close(ref);
4975 return err;
4978 static const struct got_error *
4979 cmd_ref(int argc, char *argv[])
4981 const struct got_error *error = NULL;
4982 struct got_repository *repo = NULL;
4983 struct got_worktree *worktree = NULL;
4984 char *cwd = NULL, *repo_path = NULL;
4985 int ch, do_list = 0, do_delete = 0;
4986 const char *obj_arg = NULL, *symref_target= NULL;
4987 char *refname = NULL;
4989 while ((ch = getopt(argc, argv, "c:dr:ls:")) != -1) {
4990 switch (ch) {
4991 case 'c':
4992 obj_arg = optarg;
4993 break;
4994 case 'd':
4995 do_delete = 1;
4996 break;
4997 case 'r':
4998 repo_path = realpath(optarg, NULL);
4999 if (repo_path == NULL)
5000 return got_error_from_errno2("realpath",
5001 optarg);
5002 got_path_strip_trailing_slashes(repo_path);
5003 break;
5004 case 'l':
5005 do_list = 1;
5006 break;
5007 case 's':
5008 symref_target = optarg;
5009 break;
5010 default:
5011 usage_ref();
5012 /* NOTREACHED */
5016 if (obj_arg && do_list)
5017 errx(1, "-c and -l options are mutually exclusive");
5018 if (obj_arg && do_delete)
5019 errx(1, "-c and -d options are mutually exclusive");
5020 if (obj_arg && symref_target)
5021 errx(1, "-c and -s options are mutually exclusive");
5022 if (symref_target && do_delete)
5023 errx(1, "-s and -d options are mutually exclusive");
5024 if (symref_target && do_list)
5025 errx(1, "-s and -l options are mutually exclusive");
5026 if (do_delete && do_list)
5027 errx(1, "-d and -l options are mutually exclusive");
5029 argc -= optind;
5030 argv += optind;
5032 if (do_list) {
5033 if (argc != 0 && argc != 1)
5034 usage_ref();
5035 if (argc == 1) {
5036 refname = strdup(argv[0]);
5037 if (refname == NULL) {
5038 error = got_error_from_errno("strdup");
5039 goto done;
5042 } else {
5043 if (argc != 1)
5044 usage_ref();
5045 refname = strdup(argv[0]);
5046 if (refname == NULL) {
5047 error = got_error_from_errno("strdup");
5048 goto done;
5052 if (refname)
5053 got_path_strip_trailing_slashes(refname);
5055 #ifndef PROFILE
5056 if (do_list) {
5057 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
5058 NULL) == -1)
5059 err(1, "pledge");
5060 } else {
5061 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
5062 "sendfd unveil", NULL) == -1)
5063 err(1, "pledge");
5065 #endif
5066 cwd = getcwd(NULL, 0);
5067 if (cwd == NULL) {
5068 error = got_error_from_errno("getcwd");
5069 goto done;
5072 if (repo_path == NULL) {
5073 error = got_worktree_open(&worktree, cwd);
5074 if (error && error->code != GOT_ERR_NOT_WORKTREE)
5075 goto done;
5076 else
5077 error = NULL;
5078 if (worktree) {
5079 repo_path =
5080 strdup(got_worktree_get_repo_path(worktree));
5081 if (repo_path == NULL)
5082 error = got_error_from_errno("strdup");
5083 if (error)
5084 goto done;
5085 } else {
5086 repo_path = strdup(cwd);
5087 if (repo_path == NULL) {
5088 error = got_error_from_errno("strdup");
5089 goto done;
5094 error = got_repo_open(&repo, repo_path, NULL);
5095 if (error != NULL)
5096 goto done;
5098 error = apply_unveil(got_repo_get_path(repo), do_list,
5099 worktree ? got_worktree_get_root_path(worktree) : NULL);
5100 if (error)
5101 goto done;
5103 if (do_list)
5104 error = list_refs(repo, refname);
5105 else if (do_delete)
5106 error = delete_ref(repo, refname);
5107 else if (symref_target)
5108 error = add_symref(repo, refname, symref_target);
5109 else {
5110 if (obj_arg == NULL)
5111 usage_ref();
5112 error = add_ref(repo, refname, obj_arg);
5114 done:
5115 free(refname);
5116 if (repo)
5117 got_repo_close(repo);
5118 if (worktree)
5119 got_worktree_close(worktree);
5120 free(cwd);
5121 free(repo_path);
5122 return error;
5125 __dead static void
5126 usage_branch(void)
5128 fprintf(stderr,
5129 "usage: %s branch [-c commit] [-d] [-r repository] [-l] [-n] "
5130 "[name]\n", getprogname());
5131 exit(1);
5134 static const struct got_error *
5135 list_branch(struct got_repository *repo, struct got_worktree *worktree,
5136 struct got_reference *ref)
5138 const struct got_error *err = NULL;
5139 const char *refname, *marker = " ";
5140 char *refstr;
5142 refname = got_ref_get_name(ref);
5143 if (worktree && strcmp(refname,
5144 got_worktree_get_head_ref_name(worktree)) == 0) {
5145 struct got_object_id *id = NULL;
5147 err = got_ref_resolve(&id, repo, ref);
5148 if (err)
5149 return err;
5150 if (got_object_id_cmp(id,
5151 got_worktree_get_base_commit_id(worktree)) == 0)
5152 marker = "* ";
5153 else
5154 marker = "~ ";
5155 free(id);
5158 if (strncmp(refname, "refs/heads/", 11) == 0)
5159 refname += 11;
5160 if (strncmp(refname, "refs/got/worktree/", 18) == 0)
5161 refname += 18;
5163 refstr = got_ref_to_str(ref);
5164 if (refstr == NULL)
5165 return got_error_from_errno("got_ref_to_str");
5167 printf("%s%s: %s\n", marker, refname, refstr);
5168 free(refstr);
5169 return NULL;
5172 static const struct got_error *
5173 show_current_branch(struct got_repository *repo, struct got_worktree *worktree)
5175 const char *refname;
5177 if (worktree == NULL)
5178 return got_error(GOT_ERR_NOT_WORKTREE);
5180 refname = got_worktree_get_head_ref_name(worktree);
5182 if (strncmp(refname, "refs/heads/", 11) == 0)
5183 refname += 11;
5184 if (strncmp(refname, "refs/got/worktree/", 18) == 0)
5185 refname += 18;
5187 printf("%s\n", refname);
5189 return NULL;
5192 static const struct got_error *
5193 list_branches(struct got_repository *repo, struct got_worktree *worktree)
5195 static const struct got_error *err = NULL;
5196 struct got_reflist_head refs;
5197 struct got_reflist_entry *re;
5198 struct got_reference *temp_ref = NULL;
5199 int rebase_in_progress, histedit_in_progress;
5201 SIMPLEQ_INIT(&refs);
5203 if (worktree) {
5204 err = got_worktree_rebase_in_progress(&rebase_in_progress,
5205 worktree);
5206 if (err)
5207 return err;
5209 err = got_worktree_histedit_in_progress(&histedit_in_progress,
5210 worktree);
5211 if (err)
5212 return err;
5214 if (rebase_in_progress || histedit_in_progress) {
5215 err = got_ref_open(&temp_ref, repo,
5216 got_worktree_get_head_ref_name(worktree), 0);
5217 if (err)
5218 return err;
5219 list_branch(repo, worktree, temp_ref);
5220 got_ref_close(temp_ref);
5224 err = got_ref_list(&refs, repo, "refs/heads",
5225 got_ref_cmp_by_name, NULL);
5226 if (err)
5227 return err;
5229 SIMPLEQ_FOREACH(re, &refs, entry)
5230 list_branch(repo, worktree, re->ref);
5232 got_ref_list_free(&refs);
5233 return NULL;
5236 static const struct got_error *
5237 delete_branch(struct got_repository *repo, struct got_worktree *worktree,
5238 const char *branch_name)
5240 const struct got_error *err = NULL;
5241 struct got_reference *ref = NULL;
5242 char *refname;
5244 if (asprintf(&refname, "refs/heads/%s", branch_name) == -1)
5245 return got_error_from_errno("asprintf");
5247 err = got_ref_open(&ref, repo, refname, 0);
5248 if (err)
5249 goto done;
5251 if (worktree &&
5252 strcmp(got_worktree_get_head_ref_name(worktree),
5253 got_ref_get_name(ref)) == 0) {
5254 err = got_error_msg(GOT_ERR_SAME_BRANCH,
5255 "will not delete this work tree's current branch");
5256 goto done;
5259 err = got_ref_delete(ref, repo);
5260 done:
5261 if (ref)
5262 got_ref_close(ref);
5263 free(refname);
5264 return err;
5267 static const struct got_error *
5268 add_branch(struct got_repository *repo, const char *branch_name,
5269 struct got_object_id *base_commit_id)
5271 const struct got_error *err = NULL;
5272 struct got_reference *ref = NULL;
5273 char *base_refname = NULL, *refname = NULL;
5276 * Don't let the user create a branch name with a leading '-'.
5277 * While technically a valid reference name, this case is usually
5278 * an unintended typo.
5280 if (branch_name[0] == '-')
5281 return got_error_path(branch_name, GOT_ERR_REF_NAME_MINUS);
5283 if (asprintf(&refname, "refs/heads/%s", branch_name) == -1) {
5284 err = got_error_from_errno("asprintf");
5285 goto done;
5288 err = got_ref_open(&ref, repo, refname, 0);
5289 if (err == NULL) {
5290 err = got_error(GOT_ERR_BRANCH_EXISTS);
5291 goto done;
5292 } else if (err->code != GOT_ERR_NOT_REF)
5293 goto done;
5295 err = got_ref_alloc(&ref, refname, base_commit_id);
5296 if (err)
5297 goto done;
5299 err = got_ref_write(ref, repo);
5300 done:
5301 if (ref)
5302 got_ref_close(ref);
5303 free(base_refname);
5304 free(refname);
5305 return err;
5308 static const struct got_error *
5309 cmd_branch(int argc, char *argv[])
5311 const struct got_error *error = NULL;
5312 struct got_repository *repo = NULL;
5313 struct got_worktree *worktree = NULL;
5314 char *cwd = NULL, *repo_path = NULL;
5315 int ch, do_list = 0, do_show = 0, do_update = 1;
5316 const char *delref = NULL, *commit_id_arg = NULL;
5317 struct got_reference *ref = NULL;
5318 struct got_pathlist_head paths;
5319 struct got_pathlist_entry *pe;
5320 struct got_object_id *commit_id = NULL;
5321 char *commit_id_str = NULL;
5323 TAILQ_INIT(&paths);
5325 while ((ch = getopt(argc, argv, "c:d:r:ln")) != -1) {
5326 switch (ch) {
5327 case 'c':
5328 commit_id_arg = optarg;
5329 break;
5330 case 'd':
5331 delref = optarg;
5332 break;
5333 case 'r':
5334 repo_path = realpath(optarg, NULL);
5335 if (repo_path == NULL)
5336 return got_error_from_errno2("realpath",
5337 optarg);
5338 got_path_strip_trailing_slashes(repo_path);
5339 break;
5340 case 'l':
5341 do_list = 1;
5342 break;
5343 case 'n':
5344 do_update = 0;
5345 break;
5346 default:
5347 usage_branch();
5348 /* NOTREACHED */
5352 if (do_list && delref)
5353 errx(1, "-l and -d options are mutually exclusive");
5355 argc -= optind;
5356 argv += optind;
5358 if (!do_list && !delref && argc == 0)
5359 do_show = 1;
5361 if ((do_list || delref || do_show) && commit_id_arg != NULL)
5362 errx(1, "-c option can only be used when creating a branch");
5364 if (do_list || delref) {
5365 if (argc > 0)
5366 usage_branch();
5367 } else if (!do_show && argc != 1)
5368 usage_branch();
5370 #ifndef PROFILE
5371 if (do_list || do_show) {
5372 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
5373 NULL) == -1)
5374 err(1, "pledge");
5375 } else {
5376 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
5377 "sendfd unveil", NULL) == -1)
5378 err(1, "pledge");
5380 #endif
5381 cwd = getcwd(NULL, 0);
5382 if (cwd == NULL) {
5383 error = got_error_from_errno("getcwd");
5384 goto done;
5387 if (repo_path == NULL) {
5388 error = got_worktree_open(&worktree, cwd);
5389 if (error && error->code != GOT_ERR_NOT_WORKTREE)
5390 goto done;
5391 else
5392 error = NULL;
5393 if (worktree) {
5394 repo_path =
5395 strdup(got_worktree_get_repo_path(worktree));
5396 if (repo_path == NULL)
5397 error = got_error_from_errno("strdup");
5398 if (error)
5399 goto done;
5400 } else {
5401 repo_path = strdup(cwd);
5402 if (repo_path == NULL) {
5403 error = got_error_from_errno("strdup");
5404 goto done;
5409 error = got_repo_open(&repo, repo_path, NULL);
5410 if (error != NULL)
5411 goto done;
5413 error = apply_unveil(got_repo_get_path(repo), do_list,
5414 worktree ? got_worktree_get_root_path(worktree) : NULL);
5415 if (error)
5416 goto done;
5418 if (do_show)
5419 error = show_current_branch(repo, worktree);
5420 else if (do_list)
5421 error = list_branches(repo, worktree);
5422 else if (delref)
5423 error = delete_branch(repo, worktree, delref);
5424 else {
5425 if (commit_id_arg == NULL)
5426 commit_id_arg = worktree ?
5427 got_worktree_get_head_ref_name(worktree) :
5428 GOT_REF_HEAD;
5429 error = got_repo_match_object_id(&commit_id, NULL,
5430 commit_id_arg, GOT_OBJ_TYPE_COMMIT, 1, repo);
5431 if (error)
5432 goto done;
5433 error = add_branch(repo, argv[0], commit_id);
5434 if (error)
5435 goto done;
5436 if (worktree && do_update) {
5437 struct got_update_progress_arg upa;
5438 char *branch_refname = NULL;
5440 error = got_object_id_str(&commit_id_str, commit_id);
5441 if (error)
5442 goto done;
5443 error = get_worktree_paths_from_argv(&paths, 0, NULL,
5444 worktree);
5445 if (error)
5446 goto done;
5447 if (asprintf(&branch_refname, "refs/heads/%s", argv[0])
5448 == -1) {
5449 error = got_error_from_errno("asprintf");
5450 goto done;
5452 error = got_ref_open(&ref, repo, branch_refname, 0);
5453 free(branch_refname);
5454 if (error)
5455 goto done;
5456 error = switch_head_ref(ref, commit_id, worktree,
5457 repo);
5458 if (error)
5459 goto done;
5460 error = got_worktree_set_base_commit_id(worktree, repo,
5461 commit_id);
5462 if (error)
5463 goto done;
5464 memset(&upa, 0, sizeof(upa));
5465 error = got_worktree_checkout_files(worktree, &paths,
5466 repo, update_progress, &upa, check_cancelled,
5467 NULL);
5468 if (error)
5469 goto done;
5470 if (upa.did_something)
5471 printf("Updated to commit %s\n", commit_id_str);
5472 print_update_progress_stats(&upa);
5475 done:
5476 if (ref)
5477 got_ref_close(ref);
5478 if (repo)
5479 got_repo_close(repo);
5480 if (worktree)
5481 got_worktree_close(worktree);
5482 free(cwd);
5483 free(repo_path);
5484 free(commit_id);
5485 free(commit_id_str);
5486 TAILQ_FOREACH(pe, &paths, entry)
5487 free((char *)pe->path);
5488 got_pathlist_free(&paths);
5489 return error;
5493 __dead static void
5494 usage_tag(void)
5496 fprintf(stderr,
5497 "usage: %s tag [-c commit] [-r repository] [-l] "
5498 "[-m message] name\n", getprogname());
5499 exit(1);
5502 #if 0
5503 static const struct got_error *
5504 sort_tags(struct got_reflist_head *sorted, struct got_reflist_head *tags)
5506 const struct got_error *err = NULL;
5507 struct got_reflist_entry *re, *se, *new;
5508 struct got_object_id *re_id, *se_id;
5509 struct got_tag_object *re_tag, *se_tag;
5510 time_t re_time, se_time;
5512 SIMPLEQ_FOREACH(re, tags, entry) {
5513 se = SIMPLEQ_FIRST(sorted);
5514 if (se == NULL) {
5515 err = got_reflist_entry_dup(&new, re);
5516 if (err)
5517 return err;
5518 SIMPLEQ_INSERT_HEAD(sorted, new, entry);
5519 continue;
5520 } else {
5521 err = got_ref_resolve(&re_id, repo, re->ref);
5522 if (err)
5523 break;
5524 err = got_object_open_as_tag(&re_tag, repo, re_id);
5525 free(re_id);
5526 if (err)
5527 break;
5528 re_time = got_object_tag_get_tagger_time(re_tag);
5529 got_object_tag_close(re_tag);
5532 while (se) {
5533 err = got_ref_resolve(&se_id, repo, re->ref);
5534 if (err)
5535 break;
5536 err = got_object_open_as_tag(&se_tag, repo, se_id);
5537 free(se_id);
5538 if (err)
5539 break;
5540 se_time = got_object_tag_get_tagger_time(se_tag);
5541 got_object_tag_close(se_tag);
5543 if (se_time > re_time) {
5544 err = got_reflist_entry_dup(&new, re);
5545 if (err)
5546 return err;
5547 SIMPLEQ_INSERT_AFTER(sorted, se, new, entry);
5548 break;
5550 se = SIMPLEQ_NEXT(se, entry);
5551 continue;
5554 done:
5555 return err;
5557 #endif
5559 static const struct got_error *
5560 list_tags(struct got_repository *repo, struct got_worktree *worktree)
5562 static const struct got_error *err = NULL;
5563 struct got_reflist_head refs;
5564 struct got_reflist_entry *re;
5566 SIMPLEQ_INIT(&refs);
5568 err = got_ref_list(&refs, repo, "refs/tags", got_ref_cmp_tags, repo);
5569 if (err)
5570 return err;
5572 SIMPLEQ_FOREACH(re, &refs, entry) {
5573 const char *refname;
5574 char *refstr, *tagmsg0, *tagmsg, *line, *id_str, *datestr;
5575 char datebuf[26];
5576 const char *tagger;
5577 time_t tagger_time;
5578 struct got_object_id *id;
5579 struct got_tag_object *tag;
5580 struct got_commit_object *commit = NULL;
5582 refname = got_ref_get_name(re->ref);
5583 if (strncmp(refname, "refs/tags/", 10) != 0)
5584 continue;
5585 refname += 10;
5586 refstr = got_ref_to_str(re->ref);
5587 if (refstr == NULL) {
5588 err = got_error_from_errno("got_ref_to_str");
5589 break;
5591 printf("%stag %s %s\n", GOT_COMMIT_SEP_STR, refname, refstr);
5592 free(refstr);
5594 err = got_ref_resolve(&id, repo, re->ref);
5595 if (err)
5596 break;
5597 err = got_object_open_as_tag(&tag, repo, id);
5598 if (err) {
5599 if (err->code != GOT_ERR_OBJ_TYPE) {
5600 free(id);
5601 break;
5603 /* "lightweight" tag */
5604 err = got_object_open_as_commit(&commit, repo, id);
5605 if (err) {
5606 free(id);
5607 break;
5609 tagger = got_object_commit_get_committer(commit);
5610 tagger_time =
5611 got_object_commit_get_committer_time(commit);
5612 err = got_object_id_str(&id_str, id);
5613 free(id);
5614 if (err)
5615 break;
5616 } else {
5617 free(id);
5618 tagger = got_object_tag_get_tagger(tag);
5619 tagger_time = got_object_tag_get_tagger_time(tag);
5620 err = got_object_id_str(&id_str,
5621 got_object_tag_get_object_id(tag));
5622 if (err)
5623 break;
5625 printf("from: %s\n", tagger);
5626 datestr = get_datestr(&tagger_time, datebuf);
5627 if (datestr)
5628 printf("date: %s UTC\n", datestr);
5629 if (commit)
5630 printf("object: %s %s\n", GOT_OBJ_LABEL_COMMIT, id_str);
5631 else {
5632 switch (got_object_tag_get_object_type(tag)) {
5633 case GOT_OBJ_TYPE_BLOB:
5634 printf("object: %s %s\n", GOT_OBJ_LABEL_BLOB,
5635 id_str);
5636 break;
5637 case GOT_OBJ_TYPE_TREE:
5638 printf("object: %s %s\n", GOT_OBJ_LABEL_TREE,
5639 id_str);
5640 break;
5641 case GOT_OBJ_TYPE_COMMIT:
5642 printf("object: %s %s\n", GOT_OBJ_LABEL_COMMIT,
5643 id_str);
5644 break;
5645 case GOT_OBJ_TYPE_TAG:
5646 printf("object: %s %s\n", GOT_OBJ_LABEL_TAG,
5647 id_str);
5648 break;
5649 default:
5650 break;
5653 free(id_str);
5654 if (commit) {
5655 err = got_object_commit_get_logmsg(&tagmsg0, commit);
5656 if (err)
5657 break;
5658 got_object_commit_close(commit);
5659 } else {
5660 tagmsg0 = strdup(got_object_tag_get_message(tag));
5661 got_object_tag_close(tag);
5662 if (tagmsg0 == NULL) {
5663 err = got_error_from_errno("strdup");
5664 break;
5668 tagmsg = tagmsg0;
5669 do {
5670 line = strsep(&tagmsg, "\n");
5671 if (line)
5672 printf(" %s\n", line);
5673 } while (line);
5674 free(tagmsg0);
5677 got_ref_list_free(&refs);
5678 return NULL;
5681 static const struct got_error *
5682 get_tag_message(char **tagmsg, char **tagmsg_path, const char *commit_id_str,
5683 const char *tag_name, const char *repo_path)
5685 const struct got_error *err = NULL;
5686 char *template = NULL, *initial_content = NULL;
5687 char *editor = NULL;
5688 int initial_content_len;
5689 int fd = -1;
5691 if (asprintf(&template, GOT_TMPDIR_STR "/got-tagmsg") == -1) {
5692 err = got_error_from_errno("asprintf");
5693 goto done;
5696 initial_content_len = asprintf(&initial_content,
5697 "\n# tagging commit %s as %s\n",
5698 commit_id_str, tag_name);
5699 if (initial_content_len == -1) {
5700 err = got_error_from_errno("asprintf");
5701 goto done;
5704 err = got_opentemp_named_fd(tagmsg_path, &fd, template);
5705 if (err)
5706 goto done;
5708 write(fd, initial_content, initial_content_len);
5709 close(fd);
5711 err = get_editor(&editor);
5712 if (err)
5713 goto done;
5714 err = edit_logmsg(tagmsg, editor, *tagmsg_path, initial_content);
5715 done:
5716 free(initial_content);
5717 free(template);
5718 free(editor);
5720 /* Editor is done; we can now apply unveil(2) */
5721 if (err == NULL) {
5722 err = apply_unveil(repo_path, 0, NULL);
5723 if (err) {
5724 free(*tagmsg);
5725 *tagmsg = NULL;
5728 return err;
5731 static const struct got_error *
5732 add_tag(struct got_repository *repo, struct got_worktree *worktree,
5733 const char *tag_name, const char *commit_arg, const char *tagmsg_arg)
5735 const struct got_error *err = NULL;
5736 struct got_object_id *commit_id = NULL, *tag_id = NULL;
5737 char *label = NULL, *commit_id_str = NULL;
5738 struct got_reference *ref = NULL;
5739 char *refname = NULL, *tagmsg = NULL, *tagger = NULL;
5740 char *tagmsg_path = NULL, *tag_id_str = NULL;
5741 int preserve_tagmsg = 0;
5744 * Don't let the user create a tag name with a leading '-'.
5745 * While technically a valid reference name, this case is usually
5746 * an unintended typo.
5748 if (tag_name[0] == '-')
5749 return got_error_path(tag_name, GOT_ERR_REF_NAME_MINUS);
5751 err = get_author(&tagger, repo, worktree);
5752 if (err)
5753 return err;
5755 err = got_repo_match_object_id(&commit_id, &label, commit_arg,
5756 GOT_OBJ_TYPE_COMMIT, 1, repo);
5757 if (err)
5758 goto done;
5760 err = got_object_id_str(&commit_id_str, commit_id);
5761 if (err)
5762 goto done;
5764 if (strncmp("refs/tags/", tag_name, 10) == 0) {
5765 refname = strdup(tag_name);
5766 if (refname == NULL) {
5767 err = got_error_from_errno("strdup");
5768 goto done;
5770 tag_name += 10;
5771 } else if (asprintf(&refname, "refs/tags/%s", tag_name) == -1) {
5772 err = got_error_from_errno("asprintf");
5773 goto done;
5776 err = got_ref_open(&ref, repo, refname, 0);
5777 if (err == NULL) {
5778 err = got_error(GOT_ERR_TAG_EXISTS);
5779 goto done;
5780 } else if (err->code != GOT_ERR_NOT_REF)
5781 goto done;
5783 if (tagmsg_arg == NULL) {
5784 err = get_tag_message(&tagmsg, &tagmsg_path, commit_id_str,
5785 tag_name, got_repo_get_path(repo));
5786 if (err) {
5787 if (err->code != GOT_ERR_COMMIT_MSG_EMPTY &&
5788 tagmsg_path != NULL)
5789 preserve_tagmsg = 1;
5790 goto done;
5794 err = got_object_tag_create(&tag_id, tag_name, commit_id,
5795 tagger, time(NULL), tagmsg ? tagmsg : tagmsg_arg, repo);
5796 if (err) {
5797 if (tagmsg_path)
5798 preserve_tagmsg = 1;
5799 goto done;
5802 err = got_ref_alloc(&ref, refname, tag_id);
5803 if (err) {
5804 if (tagmsg_path)
5805 preserve_tagmsg = 1;
5806 goto done;
5809 err = got_ref_write(ref, repo);
5810 if (err) {
5811 if (tagmsg_path)
5812 preserve_tagmsg = 1;
5813 goto done;
5816 err = got_object_id_str(&tag_id_str, tag_id);
5817 if (err) {
5818 if (tagmsg_path)
5819 preserve_tagmsg = 1;
5820 goto done;
5822 printf("Created tag %s\n", tag_id_str);
5823 done:
5824 if (preserve_tagmsg) {
5825 fprintf(stderr, "%s: tag message preserved in %s\n",
5826 getprogname(), tagmsg_path);
5827 } else if (tagmsg_path && unlink(tagmsg_path) == -1 && err == NULL)
5828 err = got_error_from_errno2("unlink", tagmsg_path);
5829 free(tag_id_str);
5830 if (ref)
5831 got_ref_close(ref);
5832 free(commit_id);
5833 free(commit_id_str);
5834 free(refname);
5835 free(tagmsg);
5836 free(tagmsg_path);
5837 free(tagger);
5838 return err;
5841 static const struct got_error *
5842 cmd_tag(int argc, char *argv[])
5844 const struct got_error *error = NULL;
5845 struct got_repository *repo = NULL;
5846 struct got_worktree *worktree = NULL;
5847 char *cwd = NULL, *repo_path = NULL, *commit_id_str = NULL;
5848 char *gitconfig_path = NULL;
5849 const char *tag_name, *commit_id_arg = NULL, *tagmsg = NULL;
5850 int ch, do_list = 0;
5852 while ((ch = getopt(argc, argv, "c:m:r:l")) != -1) {
5853 switch (ch) {
5854 case 'c':
5855 commit_id_arg = optarg;
5856 break;
5857 case 'm':
5858 tagmsg = optarg;
5859 break;
5860 case 'r':
5861 repo_path = realpath(optarg, NULL);
5862 if (repo_path == NULL)
5863 return got_error_from_errno2("realpath",
5864 optarg);
5865 got_path_strip_trailing_slashes(repo_path);
5866 break;
5867 case 'l':
5868 do_list = 1;
5869 break;
5870 default:
5871 usage_tag();
5872 /* NOTREACHED */
5876 argc -= optind;
5877 argv += optind;
5879 if (do_list) {
5880 if (commit_id_arg != NULL)
5881 errx(1,
5882 "-c option can only be used when creating a tag");
5883 if (tagmsg)
5884 errx(1, "-l and -m options are mutually exclusive");
5885 if (argc > 0)
5886 usage_tag();
5887 } else if (argc != 1)
5888 usage_tag();
5890 tag_name = argv[0];
5892 #ifndef PROFILE
5893 if (do_list) {
5894 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
5895 NULL) == -1)
5896 err(1, "pledge");
5897 } else {
5898 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
5899 "sendfd unveil", NULL) == -1)
5900 err(1, "pledge");
5902 #endif
5903 cwd = getcwd(NULL, 0);
5904 if (cwd == NULL) {
5905 error = got_error_from_errno("getcwd");
5906 goto done;
5909 if (repo_path == NULL) {
5910 error = got_worktree_open(&worktree, cwd);
5911 if (error && error->code != GOT_ERR_NOT_WORKTREE)
5912 goto done;
5913 else
5914 error = NULL;
5915 if (worktree) {
5916 repo_path =
5917 strdup(got_worktree_get_repo_path(worktree));
5918 if (repo_path == NULL)
5919 error = got_error_from_errno("strdup");
5920 if (error)
5921 goto done;
5922 } else {
5923 repo_path = strdup(cwd);
5924 if (repo_path == NULL) {
5925 error = got_error_from_errno("strdup");
5926 goto done;
5931 if (do_list) {
5932 error = got_repo_open(&repo, repo_path, NULL);
5933 if (error != NULL)
5934 goto done;
5935 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
5936 if (error)
5937 goto done;
5938 error = list_tags(repo, worktree);
5939 } else {
5940 error = get_gitconfig_path(&gitconfig_path);
5941 if (error)
5942 goto done;
5943 error = got_repo_open(&repo, repo_path, gitconfig_path);
5944 if (error != NULL)
5945 goto done;
5947 if (tagmsg) {
5948 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
5949 if (error)
5950 goto done;
5953 if (commit_id_arg == NULL) {
5954 struct got_reference *head_ref;
5955 struct got_object_id *commit_id;
5956 error = got_ref_open(&head_ref, repo,
5957 worktree ? got_worktree_get_head_ref_name(worktree)
5958 : GOT_REF_HEAD, 0);
5959 if (error)
5960 goto done;
5961 error = got_ref_resolve(&commit_id, repo, head_ref);
5962 got_ref_close(head_ref);
5963 if (error)
5964 goto done;
5965 error = got_object_id_str(&commit_id_str, commit_id);
5966 free(commit_id);
5967 if (error)
5968 goto done;
5971 error = add_tag(repo, worktree, tag_name,
5972 commit_id_str ? commit_id_str : commit_id_arg, tagmsg);
5974 done:
5975 if (repo)
5976 got_repo_close(repo);
5977 if (worktree)
5978 got_worktree_close(worktree);
5979 free(cwd);
5980 free(repo_path);
5981 free(gitconfig_path);
5982 free(commit_id_str);
5983 return error;
5986 __dead static void
5987 usage_add(void)
5989 fprintf(stderr, "usage: %s add [-R] [-I] path ...\n",
5990 getprogname());
5991 exit(1);
5994 static const struct got_error *
5995 add_progress(void *arg, unsigned char status, const char *path)
5997 while (path[0] == '/')
5998 path++;
5999 printf("%c %s\n", status, path);
6000 return NULL;
6003 static const struct got_error *
6004 cmd_add(int argc, char *argv[])
6006 const struct got_error *error = NULL;
6007 struct got_repository *repo = NULL;
6008 struct got_worktree *worktree = NULL;
6009 char *cwd = NULL;
6010 struct got_pathlist_head paths;
6011 struct got_pathlist_entry *pe;
6012 int ch, can_recurse = 0, no_ignores = 0;
6014 TAILQ_INIT(&paths);
6016 while ((ch = getopt(argc, argv, "IR")) != -1) {
6017 switch (ch) {
6018 case 'I':
6019 no_ignores = 1;
6020 break;
6021 case 'R':
6022 can_recurse = 1;
6023 break;
6024 default:
6025 usage_add();
6026 /* NOTREACHED */
6030 argc -= optind;
6031 argv += optind;
6033 #ifndef PROFILE
6034 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
6035 NULL) == -1)
6036 err(1, "pledge");
6037 #endif
6038 if (argc < 1)
6039 usage_add();
6041 cwd = getcwd(NULL, 0);
6042 if (cwd == NULL) {
6043 error = got_error_from_errno("getcwd");
6044 goto done;
6047 error = got_worktree_open(&worktree, cwd);
6048 if (error) {
6049 if (error->code == GOT_ERR_NOT_WORKTREE)
6050 error = wrap_not_worktree_error(error, "add", cwd);
6051 goto done;
6054 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
6055 NULL);
6056 if (error != NULL)
6057 goto done;
6059 error = apply_unveil(got_repo_get_path(repo), 1,
6060 got_worktree_get_root_path(worktree));
6061 if (error)
6062 goto done;
6064 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
6065 if (error)
6066 goto done;
6068 if (!can_recurse && no_ignores) {
6069 error = got_error_msg(GOT_ERR_BAD_PATH,
6070 "disregarding ignores requires -R option");
6071 goto done;
6075 if (!can_recurse) {
6076 char *ondisk_path;
6077 struct stat sb;
6078 TAILQ_FOREACH(pe, &paths, entry) {
6079 if (asprintf(&ondisk_path, "%s/%s",
6080 got_worktree_get_root_path(worktree),
6081 pe->path) == -1) {
6082 error = got_error_from_errno("asprintf");
6083 goto done;
6085 if (lstat(ondisk_path, &sb) == -1) {
6086 if (errno == ENOENT) {
6087 free(ondisk_path);
6088 continue;
6090 error = got_error_from_errno2("lstat",
6091 ondisk_path);
6092 free(ondisk_path);
6093 goto done;
6095 free(ondisk_path);
6096 if (S_ISDIR(sb.st_mode)) {
6097 error = got_error_msg(GOT_ERR_BAD_PATH,
6098 "adding directories requires -R option");
6099 goto done;
6104 error = got_worktree_schedule_add(worktree, &paths, add_progress,
6105 NULL, repo, no_ignores);
6106 done:
6107 if (repo)
6108 got_repo_close(repo);
6109 if (worktree)
6110 got_worktree_close(worktree);
6111 TAILQ_FOREACH(pe, &paths, entry)
6112 free((char *)pe->path);
6113 got_pathlist_free(&paths);
6114 free(cwd);
6115 return error;
6118 __dead static void
6119 usage_remove(void)
6121 fprintf(stderr, "usage: %s remove [-f] [-k] [-R] [-s status-codes] "
6122 "path ...\n", getprogname());
6123 exit(1);
6126 static const struct got_error *
6127 print_remove_status(void *arg, unsigned char status,
6128 unsigned char staged_status, const char *path)
6130 while (path[0] == '/')
6131 path++;
6132 if (status == GOT_STATUS_NONEXISTENT)
6133 return NULL;
6134 if (status == staged_status && (status == GOT_STATUS_DELETE))
6135 status = GOT_STATUS_NO_CHANGE;
6136 printf("%c%c %s\n", status, staged_status, path);
6137 return NULL;
6140 static const struct got_error *
6141 cmd_remove(int argc, char *argv[])
6143 const struct got_error *error = NULL;
6144 struct got_worktree *worktree = NULL;
6145 struct got_repository *repo = NULL;
6146 const char *status_codes = NULL;
6147 char *cwd = NULL;
6148 struct got_pathlist_head paths;
6149 struct got_pathlist_entry *pe;
6150 int ch, delete_local_mods = 0, can_recurse = 0, keep_on_disk = 0, i;
6152 TAILQ_INIT(&paths);
6154 while ((ch = getopt(argc, argv, "fkRs:")) != -1) {
6155 switch (ch) {
6156 case 'f':
6157 delete_local_mods = 1;
6158 break;
6159 case 'k':
6160 keep_on_disk = 1;
6161 break;
6162 case 'R':
6163 can_recurse = 1;
6164 break;
6165 case 's':
6166 for (i = 0; i < strlen(optarg); i++) {
6167 switch (optarg[i]) {
6168 case GOT_STATUS_MODIFY:
6169 delete_local_mods = 1;
6170 break;
6171 case GOT_STATUS_MISSING:
6172 break;
6173 default:
6174 errx(1, "invalid status code '%c'",
6175 optarg[i]);
6178 status_codes = optarg;
6179 break;
6180 default:
6181 usage_remove();
6182 /* NOTREACHED */
6186 argc -= optind;
6187 argv += optind;
6189 #ifndef PROFILE
6190 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
6191 NULL) == -1)
6192 err(1, "pledge");
6193 #endif
6194 if (argc < 1)
6195 usage_remove();
6197 cwd = getcwd(NULL, 0);
6198 if (cwd == NULL) {
6199 error = got_error_from_errno("getcwd");
6200 goto done;
6202 error = got_worktree_open(&worktree, cwd);
6203 if (error) {
6204 if (error->code == GOT_ERR_NOT_WORKTREE)
6205 error = wrap_not_worktree_error(error, "remove", cwd);
6206 goto done;
6209 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
6210 NULL);
6211 if (error)
6212 goto done;
6214 error = apply_unveil(got_repo_get_path(repo), 1,
6215 got_worktree_get_root_path(worktree));
6216 if (error)
6217 goto done;
6219 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
6220 if (error)
6221 goto done;
6223 if (!can_recurse) {
6224 char *ondisk_path;
6225 struct stat sb;
6226 TAILQ_FOREACH(pe, &paths, entry) {
6227 if (asprintf(&ondisk_path, "%s/%s",
6228 got_worktree_get_root_path(worktree),
6229 pe->path) == -1) {
6230 error = got_error_from_errno("asprintf");
6231 goto done;
6233 if (lstat(ondisk_path, &sb) == -1) {
6234 if (errno == ENOENT) {
6235 free(ondisk_path);
6236 continue;
6238 error = got_error_from_errno2("lstat",
6239 ondisk_path);
6240 free(ondisk_path);
6241 goto done;
6243 free(ondisk_path);
6244 if (S_ISDIR(sb.st_mode)) {
6245 error = got_error_msg(GOT_ERR_BAD_PATH,
6246 "removing directories requires -R option");
6247 goto done;
6252 error = got_worktree_schedule_delete(worktree, &paths,
6253 delete_local_mods, status_codes, print_remove_status, NULL,
6254 repo, keep_on_disk);
6255 done:
6256 if (repo)
6257 got_repo_close(repo);
6258 if (worktree)
6259 got_worktree_close(worktree);
6260 TAILQ_FOREACH(pe, &paths, entry)
6261 free((char *)pe->path);
6262 got_pathlist_free(&paths);
6263 free(cwd);
6264 return error;
6267 __dead static void
6268 usage_revert(void)
6270 fprintf(stderr, "usage: %s revert [-p] [-F response-script] [-R] "
6271 "path ...\n", getprogname());
6272 exit(1);
6275 static const struct got_error *
6276 revert_progress(void *arg, unsigned char status, const char *path)
6278 if (status == GOT_STATUS_UNVERSIONED)
6279 return NULL;
6281 while (path[0] == '/')
6282 path++;
6283 printf("%c %s\n", status, path);
6284 return NULL;
6287 struct choose_patch_arg {
6288 FILE *patch_script_file;
6289 const char *action;
6292 static const struct got_error *
6293 show_change(unsigned char status, const char *path, FILE *patch_file, int n,
6294 int nchanges, const char *action)
6296 char *line = NULL;
6297 size_t linesize = 0;
6298 ssize_t linelen;
6300 switch (status) {
6301 case GOT_STATUS_ADD:
6302 printf("A %s\n%s this addition? [y/n] ", path, action);
6303 break;
6304 case GOT_STATUS_DELETE:
6305 printf("D %s\n%s this deletion? [y/n] ", path, action);
6306 break;
6307 case GOT_STATUS_MODIFY:
6308 if (fseek(patch_file, 0L, SEEK_SET) == -1)
6309 return got_error_from_errno("fseek");
6310 printf(GOT_COMMIT_SEP_STR);
6311 while ((linelen = getline(&line, &linesize, patch_file)) != -1)
6312 printf("%s", line);
6313 if (ferror(patch_file))
6314 return got_error_from_errno("getline");
6315 printf(GOT_COMMIT_SEP_STR);
6316 printf("M %s (change %d of %d)\n%s this change? [y/n/q] ",
6317 path, n, nchanges, action);
6318 break;
6319 default:
6320 return got_error_path(path, GOT_ERR_FILE_STATUS);
6323 return NULL;
6326 static const struct got_error *
6327 choose_patch(int *choice, void *arg, unsigned char status, const char *path,
6328 FILE *patch_file, int n, int nchanges)
6330 const struct got_error *err = NULL;
6331 char *line = NULL;
6332 size_t linesize = 0;
6333 ssize_t linelen;
6334 int resp = ' ';
6335 struct choose_patch_arg *a = arg;
6337 *choice = GOT_PATCH_CHOICE_NONE;
6339 if (a->patch_script_file) {
6340 char *nl;
6341 err = show_change(status, path, patch_file, n, nchanges,
6342 a->action);
6343 if (err)
6344 return err;
6345 linelen = getline(&line, &linesize, a->patch_script_file);
6346 if (linelen == -1) {
6347 if (ferror(a->patch_script_file))
6348 return got_error_from_errno("getline");
6349 return NULL;
6351 nl = strchr(line, '\n');
6352 if (nl)
6353 *nl = '\0';
6354 if (strcmp(line, "y") == 0) {
6355 *choice = GOT_PATCH_CHOICE_YES;
6356 printf("y\n");
6357 } else if (strcmp(line, "n") == 0) {
6358 *choice = GOT_PATCH_CHOICE_NO;
6359 printf("n\n");
6360 } else if (strcmp(line, "q") == 0 &&
6361 status == GOT_STATUS_MODIFY) {
6362 *choice = GOT_PATCH_CHOICE_QUIT;
6363 printf("q\n");
6364 } else
6365 printf("invalid response '%s'\n", line);
6366 free(line);
6367 return NULL;
6370 while (resp != 'y' && resp != 'n' && resp != 'q') {
6371 err = show_change(status, path, patch_file, n, nchanges,
6372 a->action);
6373 if (err)
6374 return err;
6375 resp = getchar();
6376 if (resp == '\n')
6377 resp = getchar();
6378 if (status == GOT_STATUS_MODIFY) {
6379 if (resp != 'y' && resp != 'n' && resp != 'q') {
6380 printf("invalid response '%c'\n", resp);
6381 resp = ' ';
6383 } else if (resp != 'y' && resp != 'n') {
6384 printf("invalid response '%c'\n", resp);
6385 resp = ' ';
6389 if (resp == 'y')
6390 *choice = GOT_PATCH_CHOICE_YES;
6391 else if (resp == 'n')
6392 *choice = GOT_PATCH_CHOICE_NO;
6393 else if (resp == 'q' && status == GOT_STATUS_MODIFY)
6394 *choice = GOT_PATCH_CHOICE_QUIT;
6396 return NULL;
6400 static const struct got_error *
6401 cmd_revert(int argc, char *argv[])
6403 const struct got_error *error = NULL;
6404 struct got_worktree *worktree = NULL;
6405 struct got_repository *repo = NULL;
6406 char *cwd = NULL, *path = NULL;
6407 struct got_pathlist_head paths;
6408 struct got_pathlist_entry *pe;
6409 int ch, can_recurse = 0, pflag = 0;
6410 FILE *patch_script_file = NULL;
6411 const char *patch_script_path = NULL;
6412 struct choose_patch_arg cpa;
6414 TAILQ_INIT(&paths);
6416 while ((ch = getopt(argc, argv, "pF:R")) != -1) {
6417 switch (ch) {
6418 case 'p':
6419 pflag = 1;
6420 break;
6421 case 'F':
6422 patch_script_path = optarg;
6423 break;
6424 case 'R':
6425 can_recurse = 1;
6426 break;
6427 default:
6428 usage_revert();
6429 /* NOTREACHED */
6433 argc -= optind;
6434 argv += optind;
6436 #ifndef PROFILE
6437 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
6438 "unveil", NULL) == -1)
6439 err(1, "pledge");
6440 #endif
6441 if (argc < 1)
6442 usage_revert();
6443 if (patch_script_path && !pflag)
6444 errx(1, "-F option can only be used together with -p option");
6446 cwd = getcwd(NULL, 0);
6447 if (cwd == NULL) {
6448 error = got_error_from_errno("getcwd");
6449 goto done;
6451 error = got_worktree_open(&worktree, cwd);
6452 if (error) {
6453 if (error->code == GOT_ERR_NOT_WORKTREE)
6454 error = wrap_not_worktree_error(error, "revert", cwd);
6455 goto done;
6458 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
6459 NULL);
6460 if (error != NULL)
6461 goto done;
6463 if (patch_script_path) {
6464 patch_script_file = fopen(patch_script_path, "r");
6465 if (patch_script_file == NULL) {
6466 error = got_error_from_errno2("fopen",
6467 patch_script_path);
6468 goto done;
6471 error = apply_unveil(got_repo_get_path(repo), 1,
6472 got_worktree_get_root_path(worktree));
6473 if (error)
6474 goto done;
6476 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
6477 if (error)
6478 goto done;
6480 if (!can_recurse) {
6481 char *ondisk_path;
6482 struct stat sb;
6483 TAILQ_FOREACH(pe, &paths, entry) {
6484 if (asprintf(&ondisk_path, "%s/%s",
6485 got_worktree_get_root_path(worktree),
6486 pe->path) == -1) {
6487 error = got_error_from_errno("asprintf");
6488 goto done;
6490 if (lstat(ondisk_path, &sb) == -1) {
6491 if (errno == ENOENT) {
6492 free(ondisk_path);
6493 continue;
6495 error = got_error_from_errno2("lstat",
6496 ondisk_path);
6497 free(ondisk_path);
6498 goto done;
6500 free(ondisk_path);
6501 if (S_ISDIR(sb.st_mode)) {
6502 error = got_error_msg(GOT_ERR_BAD_PATH,
6503 "reverting directories requires -R option");
6504 goto done;
6509 cpa.patch_script_file = patch_script_file;
6510 cpa.action = "revert";
6511 error = got_worktree_revert(worktree, &paths, revert_progress, NULL,
6512 pflag ? choose_patch : NULL, &cpa, repo);
6513 done:
6514 if (patch_script_file && fclose(patch_script_file) == EOF &&
6515 error == NULL)
6516 error = got_error_from_errno2("fclose", patch_script_path);
6517 if (repo)
6518 got_repo_close(repo);
6519 if (worktree)
6520 got_worktree_close(worktree);
6521 free(path);
6522 free(cwd);
6523 return error;
6526 __dead static void
6527 usage_commit(void)
6529 fprintf(stderr, "usage: %s commit [-m msg] [-S] [path ...]\n",
6530 getprogname());
6531 exit(1);
6534 struct collect_commit_logmsg_arg {
6535 const char *cmdline_log;
6536 const char *editor;
6537 const char *worktree_path;
6538 const char *branch_name;
6539 const char *repo_path;
6540 char *logmsg_path;
6544 static const struct got_error *
6545 collect_commit_logmsg(struct got_pathlist_head *commitable_paths, char **logmsg,
6546 void *arg)
6548 char *initial_content = NULL;
6549 struct got_pathlist_entry *pe;
6550 const struct got_error *err = NULL;
6551 char *template = NULL;
6552 struct collect_commit_logmsg_arg *a = arg;
6553 int initial_content_len;
6554 int fd;
6555 size_t len;
6557 /* if a message was specified on the command line, just use it */
6558 if (a->cmdline_log != NULL && strlen(a->cmdline_log) != 0) {
6559 len = strlen(a->cmdline_log) + 1;
6560 *logmsg = malloc(len + 1);
6561 if (*logmsg == NULL)
6562 return got_error_from_errno("malloc");
6563 strlcpy(*logmsg, a->cmdline_log, len);
6564 return NULL;
6567 if (asprintf(&template, "%s/logmsg", a->worktree_path) == -1)
6568 return got_error_from_errno("asprintf");
6570 initial_content_len = asprintf(&initial_content,
6571 "\n# changes to be committed on branch %s:\n",
6572 a->branch_name);
6573 if (initial_content_len == -1)
6574 return got_error_from_errno("asprintf");
6576 err = got_opentemp_named_fd(&a->logmsg_path, &fd, template);
6577 if (err)
6578 goto done;
6580 write(fd, initial_content, initial_content_len);
6582 TAILQ_FOREACH(pe, commitable_paths, entry) {
6583 struct got_commitable *ct = pe->data;
6584 dprintf(fd, "# %c %s\n",
6585 got_commitable_get_status(ct),
6586 got_commitable_get_path(ct));
6588 close(fd);
6590 err = edit_logmsg(logmsg, a->editor, a->logmsg_path, initial_content);
6591 done:
6592 free(initial_content);
6593 free(template);
6595 /* Editor is done; we can now apply unveil(2) */
6596 if (err == NULL) {
6597 err = apply_unveil(a->repo_path, 0, a->worktree_path);
6598 if (err) {
6599 free(*logmsg);
6600 *logmsg = NULL;
6603 return err;
6606 static const struct got_error *
6607 cmd_commit(int argc, char *argv[])
6609 const struct got_error *error = NULL;
6610 struct got_worktree *worktree = NULL;
6611 struct got_repository *repo = NULL;
6612 char *cwd = NULL, *id_str = NULL;
6613 struct got_object_id *id = NULL;
6614 const char *logmsg = NULL;
6615 struct collect_commit_logmsg_arg cl_arg;
6616 char *gitconfig_path = NULL, *editor = NULL, *author = NULL;
6617 int ch, rebase_in_progress, histedit_in_progress, preserve_logmsg = 0;
6618 int allow_bad_symlinks = 0;
6619 struct got_pathlist_head paths;
6621 TAILQ_INIT(&paths);
6622 cl_arg.logmsg_path = NULL;
6624 while ((ch = getopt(argc, argv, "m:S")) != -1) {
6625 switch (ch) {
6626 case 'm':
6627 logmsg = optarg;
6628 break;
6629 case 'S':
6630 allow_bad_symlinks = 1;
6631 break;
6632 default:
6633 usage_commit();
6634 /* NOTREACHED */
6638 argc -= optind;
6639 argv += optind;
6641 #ifndef PROFILE
6642 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
6643 "unveil", NULL) == -1)
6644 err(1, "pledge");
6645 #endif
6646 cwd = getcwd(NULL, 0);
6647 if (cwd == NULL) {
6648 error = got_error_from_errno("getcwd");
6649 goto done;
6651 error = got_worktree_open(&worktree, cwd);
6652 if (error) {
6653 if (error->code == GOT_ERR_NOT_WORKTREE)
6654 error = wrap_not_worktree_error(error, "commit", cwd);
6655 goto done;
6658 error = got_worktree_rebase_in_progress(&rebase_in_progress, worktree);
6659 if (error)
6660 goto done;
6661 if (rebase_in_progress) {
6662 error = got_error(GOT_ERR_REBASING);
6663 goto done;
6666 error = got_worktree_histedit_in_progress(&histedit_in_progress,
6667 worktree);
6668 if (error)
6669 goto done;
6671 error = get_gitconfig_path(&gitconfig_path);
6672 if (error)
6673 goto done;
6674 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
6675 gitconfig_path);
6676 if (error != NULL)
6677 goto done;
6679 error = get_author(&author, repo, worktree);
6680 if (error)
6681 return error;
6684 * unveil(2) traverses exec(2); if an editor is used we have
6685 * to apply unveil after the log message has been written.
6687 if (logmsg == NULL || strlen(logmsg) == 0)
6688 error = get_editor(&editor);
6689 else
6690 error = apply_unveil(got_repo_get_path(repo), 0,
6691 got_worktree_get_root_path(worktree));
6692 if (error)
6693 goto done;
6695 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
6696 if (error)
6697 goto done;
6699 cl_arg.editor = editor;
6700 cl_arg.cmdline_log = logmsg;
6701 cl_arg.worktree_path = got_worktree_get_root_path(worktree);
6702 cl_arg.branch_name = got_worktree_get_head_ref_name(worktree);
6703 if (!histedit_in_progress) {
6704 if (strncmp(cl_arg.branch_name, "refs/heads/", 11) != 0) {
6705 error = got_error(GOT_ERR_COMMIT_BRANCH);
6706 goto done;
6708 cl_arg.branch_name += 11;
6710 cl_arg.repo_path = got_repo_get_path(repo);
6711 error = got_worktree_commit(&id, worktree, &paths, author, NULL,
6712 allow_bad_symlinks, collect_commit_logmsg, &cl_arg,
6713 print_status, NULL, repo);
6714 if (error) {
6715 if (error->code != GOT_ERR_COMMIT_MSG_EMPTY &&
6716 cl_arg.logmsg_path != NULL)
6717 preserve_logmsg = 1;
6718 goto done;
6721 error = got_object_id_str(&id_str, id);
6722 if (error)
6723 goto done;
6724 printf("Created commit %s\n", id_str);
6725 done:
6726 if (preserve_logmsg) {
6727 fprintf(stderr, "%s: log message preserved in %s\n",
6728 getprogname(), cl_arg.logmsg_path);
6729 } else if (cl_arg.logmsg_path && unlink(cl_arg.logmsg_path) == -1 &&
6730 error == NULL)
6731 error = got_error_from_errno2("unlink", cl_arg.logmsg_path);
6732 free(cl_arg.logmsg_path);
6733 if (repo)
6734 got_repo_close(repo);
6735 if (worktree)
6736 got_worktree_close(worktree);
6737 free(cwd);
6738 free(id_str);
6739 free(gitconfig_path);
6740 free(editor);
6741 free(author);
6742 return error;
6745 __dead static void
6746 usage_cherrypick(void)
6748 fprintf(stderr, "usage: %s cherrypick commit-id\n", getprogname());
6749 exit(1);
6752 static const struct got_error *
6753 cmd_cherrypick(int argc, char *argv[])
6755 const struct got_error *error = NULL;
6756 struct got_worktree *worktree = NULL;
6757 struct got_repository *repo = NULL;
6758 char *cwd = NULL, *commit_id_str = NULL;
6759 struct got_object_id *commit_id = NULL;
6760 struct got_commit_object *commit = NULL;
6761 struct got_object_qid *pid;
6762 struct got_reference *head_ref = NULL;
6763 int ch;
6764 struct got_update_progress_arg upa;
6766 while ((ch = getopt(argc, argv, "")) != -1) {
6767 switch (ch) {
6768 default:
6769 usage_cherrypick();
6770 /* NOTREACHED */
6774 argc -= optind;
6775 argv += optind;
6777 #ifndef PROFILE
6778 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
6779 "unveil", NULL) == -1)
6780 err(1, "pledge");
6781 #endif
6782 if (argc != 1)
6783 usage_cherrypick();
6785 cwd = getcwd(NULL, 0);
6786 if (cwd == NULL) {
6787 error = got_error_from_errno("getcwd");
6788 goto done;
6790 error = got_worktree_open(&worktree, cwd);
6791 if (error) {
6792 if (error->code == GOT_ERR_NOT_WORKTREE)
6793 error = wrap_not_worktree_error(error, "cherrypick",
6794 cwd);
6795 goto done;
6798 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
6799 NULL);
6800 if (error != NULL)
6801 goto done;
6803 error = apply_unveil(got_repo_get_path(repo), 0,
6804 got_worktree_get_root_path(worktree));
6805 if (error)
6806 goto done;
6808 error = got_repo_match_object_id_prefix(&commit_id, argv[0],
6809 GOT_OBJ_TYPE_COMMIT, repo);
6810 if (error != NULL) {
6811 struct got_reference *ref;
6812 if (error->code != GOT_ERR_BAD_OBJ_ID_STR)
6813 goto done;
6814 error = got_ref_open(&ref, repo, argv[0], 0);
6815 if (error != NULL)
6816 goto done;
6817 error = got_ref_resolve(&commit_id, repo, ref);
6818 got_ref_close(ref);
6819 if (error != NULL)
6820 goto done;
6822 error = got_object_id_str(&commit_id_str, commit_id);
6823 if (error)
6824 goto done;
6826 error = got_ref_open(&head_ref, repo,
6827 got_worktree_get_head_ref_name(worktree), 0);
6828 if (error != NULL)
6829 goto done;
6831 error = check_same_branch(commit_id, head_ref, NULL, repo);
6832 if (error) {
6833 if (error->code != GOT_ERR_ANCESTRY)
6834 goto done;
6835 error = NULL;
6836 } else {
6837 error = got_error(GOT_ERR_SAME_BRANCH);
6838 goto done;
6841 error = got_object_open_as_commit(&commit, repo, commit_id);
6842 if (error)
6843 goto done;
6844 pid = SIMPLEQ_FIRST(got_object_commit_get_parent_ids(commit));
6845 memset(&upa, 0, sizeof(upa));
6846 error = got_worktree_merge_files(worktree, pid ? pid->id : NULL,
6847 commit_id, repo, update_progress, &upa, check_cancelled,
6848 NULL);
6849 if (error != NULL)
6850 goto done;
6852 if (upa.did_something)
6853 printf("Merged commit %s\n", commit_id_str);
6854 print_update_progress_stats(&upa);
6855 done:
6856 if (commit)
6857 got_object_commit_close(commit);
6858 free(commit_id_str);
6859 if (head_ref)
6860 got_ref_close(head_ref);
6861 if (worktree)
6862 got_worktree_close(worktree);
6863 if (repo)
6864 got_repo_close(repo);
6865 return error;
6868 __dead static void
6869 usage_backout(void)
6871 fprintf(stderr, "usage: %s backout commit-id\n", getprogname());
6872 exit(1);
6875 static const struct got_error *
6876 cmd_backout(int argc, char *argv[])
6878 const struct got_error *error = NULL;
6879 struct got_worktree *worktree = NULL;
6880 struct got_repository *repo = NULL;
6881 char *cwd = NULL, *commit_id_str = NULL;
6882 struct got_object_id *commit_id = NULL;
6883 struct got_commit_object *commit = NULL;
6884 struct got_object_qid *pid;
6885 struct got_reference *head_ref = NULL;
6886 int ch;
6887 struct got_update_progress_arg upa;
6889 while ((ch = getopt(argc, argv, "")) != -1) {
6890 switch (ch) {
6891 default:
6892 usage_backout();
6893 /* NOTREACHED */
6897 argc -= optind;
6898 argv += optind;
6900 #ifndef PROFILE
6901 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
6902 "unveil", NULL) == -1)
6903 err(1, "pledge");
6904 #endif
6905 if (argc != 1)
6906 usage_backout();
6908 cwd = getcwd(NULL, 0);
6909 if (cwd == NULL) {
6910 error = got_error_from_errno("getcwd");
6911 goto done;
6913 error = got_worktree_open(&worktree, cwd);
6914 if (error) {
6915 if (error->code == GOT_ERR_NOT_WORKTREE)
6916 error = wrap_not_worktree_error(error, "backout", cwd);
6917 goto done;
6920 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
6921 NULL);
6922 if (error != NULL)
6923 goto done;
6925 error = apply_unveil(got_repo_get_path(repo), 0,
6926 got_worktree_get_root_path(worktree));
6927 if (error)
6928 goto done;
6930 error = got_repo_match_object_id_prefix(&commit_id, argv[0],
6931 GOT_OBJ_TYPE_COMMIT, repo);
6932 if (error != NULL) {
6933 struct got_reference *ref;
6934 if (error->code != GOT_ERR_BAD_OBJ_ID_STR)
6935 goto done;
6936 error = got_ref_open(&ref, repo, argv[0], 0);
6937 if (error != NULL)
6938 goto done;
6939 error = got_ref_resolve(&commit_id, repo, ref);
6940 got_ref_close(ref);
6941 if (error != NULL)
6942 goto done;
6944 error = got_object_id_str(&commit_id_str, commit_id);
6945 if (error)
6946 goto done;
6948 error = got_ref_open(&head_ref, repo,
6949 got_worktree_get_head_ref_name(worktree), 0);
6950 if (error != NULL)
6951 goto done;
6953 error = check_same_branch(commit_id, head_ref, NULL, repo);
6954 if (error)
6955 goto done;
6957 error = got_object_open_as_commit(&commit, repo, commit_id);
6958 if (error)
6959 goto done;
6960 pid = SIMPLEQ_FIRST(got_object_commit_get_parent_ids(commit));
6961 if (pid == NULL) {
6962 error = got_error(GOT_ERR_ROOT_COMMIT);
6963 goto done;
6966 memset(&upa, 0, sizeof(upa));
6967 error = got_worktree_merge_files(worktree, commit_id, pid->id, repo,
6968 update_progress, &upa, check_cancelled, NULL);
6969 if (error != NULL)
6970 goto done;
6972 if (upa.did_something)
6973 printf("Backed out commit %s\n", commit_id_str);
6974 print_update_progress_stats(&upa);
6975 done:
6976 if (commit)
6977 got_object_commit_close(commit);
6978 free(commit_id_str);
6979 if (head_ref)
6980 got_ref_close(head_ref);
6981 if (worktree)
6982 got_worktree_close(worktree);
6983 if (repo)
6984 got_repo_close(repo);
6985 return error;
6988 __dead static void
6989 usage_rebase(void)
6991 fprintf(stderr, "usage: %s rebase [-a] | [-c] | branch\n",
6992 getprogname());
6993 exit(1);
6996 void
6997 trim_logmsg(char *logmsg, int limit)
6999 char *nl;
7000 size_t len;
7002 len = strlen(logmsg);
7003 if (len > limit)
7004 len = limit;
7005 logmsg[len] = '\0';
7006 nl = strchr(logmsg, '\n');
7007 if (nl)
7008 *nl = '\0';
7011 static const struct got_error *
7012 get_short_logmsg(char **logmsg, int limit, struct got_commit_object *commit)
7014 const struct got_error *err;
7015 char *logmsg0 = NULL;
7016 const char *s;
7018 err = got_object_commit_get_logmsg(&logmsg0, commit);
7019 if (err)
7020 return err;
7022 s = logmsg0;
7023 while (isspace((unsigned char)s[0]))
7024 s++;
7026 *logmsg = strdup(s);
7027 if (*logmsg == NULL) {
7028 err = got_error_from_errno("strdup");
7029 goto done;
7032 trim_logmsg(*logmsg, limit);
7033 done:
7034 free(logmsg0);
7035 return err;
7038 static const struct got_error *
7039 show_rebase_merge_conflict(struct got_object_id *id, struct got_repository *repo)
7041 const struct got_error *err;
7042 struct got_commit_object *commit = NULL;
7043 char *id_str = NULL, *logmsg = NULL;
7045 err = got_object_open_as_commit(&commit, repo, id);
7046 if (err)
7047 return err;
7049 err = got_object_id_str(&id_str, id);
7050 if (err)
7051 goto done;
7053 id_str[12] = '\0';
7055 err = get_short_logmsg(&logmsg, 42, commit);
7056 if (err)
7057 goto done;
7059 printf("%s -> merge conflict: %s\n", id_str, logmsg);
7060 done:
7061 free(id_str);
7062 got_object_commit_close(commit);
7063 free(logmsg);
7064 return err;
7067 static const struct got_error *
7068 show_rebase_progress(struct got_commit_object *commit,
7069 struct got_object_id *old_id, struct got_object_id *new_id)
7071 const struct got_error *err;
7072 char *old_id_str = NULL, *new_id_str = NULL, *logmsg = NULL;
7074 err = got_object_id_str(&old_id_str, old_id);
7075 if (err)
7076 goto done;
7078 if (new_id) {
7079 err = got_object_id_str(&new_id_str, new_id);
7080 if (err)
7081 goto done;
7084 old_id_str[12] = '\0';
7085 if (new_id_str)
7086 new_id_str[12] = '\0';
7088 err = get_short_logmsg(&logmsg, 42, commit);
7089 if (err)
7090 goto done;
7092 printf("%s -> %s: %s\n", old_id_str,
7093 new_id_str ? new_id_str : "no-op change", logmsg);
7094 done:
7095 free(old_id_str);
7096 free(new_id_str);
7097 free(logmsg);
7098 return err;
7101 static const struct got_error *
7102 rebase_complete(struct got_worktree *worktree, struct got_fileindex *fileindex,
7103 struct got_reference *branch, struct got_reference *new_base_branch,
7104 struct got_reference *tmp_branch, struct got_repository *repo)
7106 printf("Switching work tree to %s\n", got_ref_get_name(branch));
7107 return got_worktree_rebase_complete(worktree, fileindex,
7108 new_base_branch, tmp_branch, branch, repo);
7111 static const struct got_error *
7112 rebase_commit(struct got_pathlist_head *merged_paths,
7113 struct got_worktree *worktree, struct got_fileindex *fileindex,
7114 struct got_reference *tmp_branch,
7115 struct got_object_id *commit_id, struct got_repository *repo)
7117 const struct got_error *error;
7118 struct got_commit_object *commit;
7119 struct got_object_id *new_commit_id;
7121 error = got_object_open_as_commit(&commit, repo, commit_id);
7122 if (error)
7123 return error;
7125 error = got_worktree_rebase_commit(&new_commit_id, merged_paths,
7126 worktree, fileindex, tmp_branch, commit, commit_id, repo);
7127 if (error) {
7128 if (error->code != GOT_ERR_COMMIT_NO_CHANGES)
7129 goto done;
7130 error = show_rebase_progress(commit, commit_id, NULL);
7131 } else {
7132 error = show_rebase_progress(commit, commit_id, new_commit_id);
7133 free(new_commit_id);
7135 done:
7136 got_object_commit_close(commit);
7137 return error;
7140 struct check_path_prefix_arg {
7141 const char *path_prefix;
7142 size_t len;
7143 int errcode;
7146 static const struct got_error *
7147 check_path_prefix_in_diff(void *arg, struct got_blob_object *blob1,
7148 struct got_blob_object *blob2, struct got_object_id *id1,
7149 struct got_object_id *id2, const char *path1, const char *path2,
7150 mode_t mode1, mode_t mode2, struct got_repository *repo)
7152 struct check_path_prefix_arg *a = arg;
7154 if ((path1 && !got_path_is_child(path1, a->path_prefix, a->len)) ||
7155 (path2 && !got_path_is_child(path2, a->path_prefix, a->len)))
7156 return got_error(a->errcode);
7158 return NULL;
7161 static const struct got_error *
7162 check_path_prefix(struct got_object_id *parent_id,
7163 struct got_object_id *commit_id, const char *path_prefix,
7164 int errcode, struct got_repository *repo)
7166 const struct got_error *err;
7167 struct got_tree_object *tree1 = NULL, *tree2 = NULL;
7168 struct got_commit_object *commit = NULL, *parent_commit = NULL;
7169 struct check_path_prefix_arg cpp_arg;
7171 if (got_path_is_root_dir(path_prefix))
7172 return NULL;
7174 err = got_object_open_as_commit(&commit, repo, commit_id);
7175 if (err)
7176 goto done;
7178 err = got_object_open_as_commit(&parent_commit, repo, parent_id);
7179 if (err)
7180 goto done;
7182 err = got_object_open_as_tree(&tree1, repo,
7183 got_object_commit_get_tree_id(parent_commit));
7184 if (err)
7185 goto done;
7187 err = got_object_open_as_tree(&tree2, repo,
7188 got_object_commit_get_tree_id(commit));
7189 if (err)
7190 goto done;
7192 cpp_arg.path_prefix = path_prefix;
7193 while (cpp_arg.path_prefix[0] == '/')
7194 cpp_arg.path_prefix++;
7195 cpp_arg.len = strlen(cpp_arg.path_prefix);
7196 cpp_arg.errcode = errcode;
7197 err = got_diff_tree(tree1, tree2, "", "", repo,
7198 check_path_prefix_in_diff, &cpp_arg, 0);
7199 done:
7200 if (tree1)
7201 got_object_tree_close(tree1);
7202 if (tree2)
7203 got_object_tree_close(tree2);
7204 if (commit)
7205 got_object_commit_close(commit);
7206 if (parent_commit)
7207 got_object_commit_close(parent_commit);
7208 return err;
7211 static const struct got_error *
7212 collect_commits(struct got_object_id_queue *commits,
7213 struct got_object_id *initial_commit_id,
7214 struct got_object_id *iter_start_id, struct got_object_id *iter_stop_id,
7215 const char *path_prefix, int path_prefix_errcode,
7216 struct got_repository *repo)
7218 const struct got_error *err = NULL;
7219 struct got_commit_graph *graph = NULL;
7220 struct got_object_id *parent_id = NULL;
7221 struct got_object_qid *qid;
7222 struct got_object_id *commit_id = initial_commit_id;
7224 err = got_commit_graph_open(&graph, "/", 1);
7225 if (err)
7226 return err;
7228 err = got_commit_graph_iter_start(graph, iter_start_id, repo,
7229 check_cancelled, NULL);
7230 if (err)
7231 goto done;
7232 while (got_object_id_cmp(commit_id, iter_stop_id) != 0) {
7233 err = got_commit_graph_iter_next(&parent_id, graph, repo,
7234 check_cancelled, NULL);
7235 if (err) {
7236 if (err->code == GOT_ERR_ITER_COMPLETED) {
7237 err = got_error_msg(GOT_ERR_ANCESTRY,
7238 "ran out of commits to rebase before "
7239 "youngest common ancestor commit has "
7240 "been reached?!?");
7242 goto done;
7243 } else {
7244 err = check_path_prefix(parent_id, commit_id,
7245 path_prefix, path_prefix_errcode, repo);
7246 if (err)
7247 goto done;
7249 err = got_object_qid_alloc(&qid, commit_id);
7250 if (err)
7251 goto done;
7252 SIMPLEQ_INSERT_HEAD(commits, qid, entry);
7253 commit_id = parent_id;
7256 done:
7257 got_commit_graph_close(graph);
7258 return err;
7261 static const struct got_error *
7262 cmd_rebase(int argc, char *argv[])
7264 const struct got_error *error = NULL;
7265 struct got_worktree *worktree = NULL;
7266 struct got_repository *repo = NULL;
7267 struct got_fileindex *fileindex = NULL;
7268 char *cwd = NULL;
7269 struct got_reference *branch = NULL;
7270 struct got_reference *new_base_branch = NULL, *tmp_branch = NULL;
7271 struct got_object_id *commit_id = NULL, *parent_id = NULL;
7272 struct got_object_id *resume_commit_id = NULL;
7273 struct got_object_id *branch_head_commit_id = NULL, *yca_id = NULL;
7274 struct got_commit_object *commit = NULL;
7275 int ch, rebase_in_progress = 0, abort_rebase = 0, continue_rebase = 0;
7276 int histedit_in_progress = 0;
7277 unsigned char rebase_status = GOT_STATUS_NO_CHANGE;
7278 struct got_object_id_queue commits;
7279 struct got_pathlist_head merged_paths;
7280 const struct got_object_id_queue *parent_ids;
7281 struct got_object_qid *qid, *pid;
7283 SIMPLEQ_INIT(&commits);
7284 TAILQ_INIT(&merged_paths);
7286 while ((ch = getopt(argc, argv, "ac")) != -1) {
7287 switch (ch) {
7288 case 'a':
7289 abort_rebase = 1;
7290 break;
7291 case 'c':
7292 continue_rebase = 1;
7293 break;
7294 default:
7295 usage_rebase();
7296 /* NOTREACHED */
7300 argc -= optind;
7301 argv += optind;
7303 #ifndef PROFILE
7304 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
7305 "unveil", NULL) == -1)
7306 err(1, "pledge");
7307 #endif
7308 if (abort_rebase && continue_rebase)
7309 usage_rebase();
7310 else if (abort_rebase || continue_rebase) {
7311 if (argc != 0)
7312 usage_rebase();
7313 } else if (argc != 1)
7314 usage_rebase();
7316 cwd = getcwd(NULL, 0);
7317 if (cwd == NULL) {
7318 error = got_error_from_errno("getcwd");
7319 goto done;
7321 error = got_worktree_open(&worktree, cwd);
7322 if (error) {
7323 if (error->code == GOT_ERR_NOT_WORKTREE)
7324 error = wrap_not_worktree_error(error, "rebase", cwd);
7325 goto done;
7328 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
7329 NULL);
7330 if (error != NULL)
7331 goto done;
7333 error = apply_unveil(got_repo_get_path(repo), 0,
7334 got_worktree_get_root_path(worktree));
7335 if (error)
7336 goto done;
7338 error = got_worktree_histedit_in_progress(&histedit_in_progress,
7339 worktree);
7340 if (error)
7341 goto done;
7342 if (histedit_in_progress) {
7343 error = got_error(GOT_ERR_HISTEDIT_BUSY);
7344 goto done;
7347 error = got_worktree_rebase_in_progress(&rebase_in_progress, worktree);
7348 if (error)
7349 goto done;
7351 if (abort_rebase) {
7352 struct got_update_progress_arg upa;
7353 if (!rebase_in_progress) {
7354 error = got_error(GOT_ERR_NOT_REBASING);
7355 goto done;
7357 error = got_worktree_rebase_continue(&resume_commit_id,
7358 &new_base_branch, &tmp_branch, &branch, &fileindex,
7359 worktree, repo);
7360 if (error)
7361 goto done;
7362 printf("Switching work tree to %s\n",
7363 got_ref_get_symref_target(new_base_branch));
7364 memset(&upa, 0, sizeof(upa));
7365 error = got_worktree_rebase_abort(worktree, fileindex, repo,
7366 new_base_branch, update_progress, &upa);
7367 if (error)
7368 goto done;
7369 printf("Rebase of %s aborted\n", got_ref_get_name(branch));
7370 print_update_progress_stats(&upa);
7371 goto done; /* nothing else to do */
7374 if (continue_rebase) {
7375 if (!rebase_in_progress) {
7376 error = got_error(GOT_ERR_NOT_REBASING);
7377 goto done;
7379 error = got_worktree_rebase_continue(&resume_commit_id,
7380 &new_base_branch, &tmp_branch, &branch, &fileindex,
7381 worktree, repo);
7382 if (error)
7383 goto done;
7385 error = rebase_commit(NULL, worktree, fileindex, tmp_branch,
7386 resume_commit_id, repo);
7387 if (error)
7388 goto done;
7390 yca_id = got_object_id_dup(resume_commit_id);
7391 if (yca_id == NULL) {
7392 error = got_error_from_errno("got_object_id_dup");
7393 goto done;
7395 } else {
7396 error = got_ref_open(&branch, repo, argv[0], 0);
7397 if (error != NULL)
7398 goto done;
7401 error = got_ref_resolve(&branch_head_commit_id, repo, branch);
7402 if (error)
7403 goto done;
7405 if (!continue_rebase) {
7406 struct got_object_id *base_commit_id;
7408 base_commit_id = got_worktree_get_base_commit_id(worktree);
7409 error = got_commit_graph_find_youngest_common_ancestor(&yca_id,
7410 base_commit_id, branch_head_commit_id, repo,
7411 check_cancelled, NULL);
7412 if (error)
7413 goto done;
7414 if (yca_id == NULL) {
7415 error = got_error_msg(GOT_ERR_ANCESTRY,
7416 "specified branch shares no common ancestry "
7417 "with work tree's branch");
7418 goto done;
7421 error = check_same_branch(base_commit_id, branch, yca_id, repo);
7422 if (error) {
7423 if (error->code != GOT_ERR_ANCESTRY)
7424 goto done;
7425 error = NULL;
7426 } else {
7427 error = got_error_msg(GOT_ERR_SAME_BRANCH,
7428 "specified branch resolves to a commit which "
7429 "is already contained in work tree's branch");
7430 goto done;
7432 error = got_worktree_rebase_prepare(&new_base_branch,
7433 &tmp_branch, &fileindex, worktree, branch, repo);
7434 if (error)
7435 goto done;
7438 commit_id = branch_head_commit_id;
7439 error = got_object_open_as_commit(&commit, repo, commit_id);
7440 if (error)
7441 goto done;
7443 parent_ids = got_object_commit_get_parent_ids(commit);
7444 pid = SIMPLEQ_FIRST(parent_ids);
7445 if (pid == NULL) {
7446 if (!continue_rebase) {
7447 struct got_update_progress_arg upa;
7448 memset(&upa, 0, sizeof(upa));
7449 error = got_worktree_rebase_abort(worktree, fileindex,
7450 repo, new_base_branch, update_progress, &upa);
7451 if (error)
7452 goto done;
7453 printf("Rebase of %s aborted\n",
7454 got_ref_get_name(branch));
7455 print_update_progress_stats(&upa);
7458 error = got_error(GOT_ERR_EMPTY_REBASE);
7459 goto done;
7461 error = collect_commits(&commits, commit_id, pid->id,
7462 yca_id, got_worktree_get_path_prefix(worktree),
7463 GOT_ERR_REBASE_PATH, repo);
7464 got_object_commit_close(commit);
7465 commit = NULL;
7466 if (error)
7467 goto done;
7469 if (SIMPLEQ_EMPTY(&commits)) {
7470 if (continue_rebase) {
7471 error = rebase_complete(worktree, fileindex,
7472 branch, new_base_branch, tmp_branch, repo);
7473 goto done;
7474 } else {
7475 /* Fast-forward the reference of the branch. */
7476 struct got_object_id *new_head_commit_id;
7477 char *id_str;
7478 error = got_ref_resolve(&new_head_commit_id, repo,
7479 new_base_branch);
7480 if (error)
7481 goto done;
7482 error = got_object_id_str(&id_str, new_head_commit_id);
7483 printf("Forwarding %s to commit %s\n",
7484 got_ref_get_name(branch), id_str);
7485 free(id_str);
7486 error = got_ref_change_ref(branch,
7487 new_head_commit_id);
7488 if (error)
7489 goto done;
7493 pid = NULL;
7494 SIMPLEQ_FOREACH(qid, &commits, entry) {
7495 struct got_update_progress_arg upa;
7497 commit_id = qid->id;
7498 parent_id = pid ? pid->id : yca_id;
7499 pid = qid;
7501 memset(&upa, 0, sizeof(upa));
7502 error = got_worktree_rebase_merge_files(&merged_paths,
7503 worktree, fileindex, parent_id, commit_id, repo,
7504 update_progress, &upa, check_cancelled, NULL);
7505 if (error)
7506 goto done;
7508 print_update_progress_stats(&upa);
7509 if (upa.conflicts > 0)
7510 rebase_status = GOT_STATUS_CONFLICT;
7512 if (rebase_status == GOT_STATUS_CONFLICT) {
7513 error = show_rebase_merge_conflict(qid->id, repo);
7514 if (error)
7515 goto done;
7516 got_worktree_rebase_pathlist_free(&merged_paths);
7517 break;
7520 error = rebase_commit(&merged_paths, worktree, fileindex,
7521 tmp_branch, commit_id, repo);
7522 got_worktree_rebase_pathlist_free(&merged_paths);
7523 if (error)
7524 goto done;
7527 if (rebase_status == GOT_STATUS_CONFLICT) {
7528 error = got_worktree_rebase_postpone(worktree, fileindex);
7529 if (error)
7530 goto done;
7531 error = got_error_msg(GOT_ERR_CONFLICTS,
7532 "conflicts must be resolved before rebasing can continue");
7533 } else
7534 error = rebase_complete(worktree, fileindex, branch,
7535 new_base_branch, tmp_branch, repo);
7536 done:
7537 got_object_id_queue_free(&commits);
7538 free(branch_head_commit_id);
7539 free(resume_commit_id);
7540 free(yca_id);
7541 if (commit)
7542 got_object_commit_close(commit);
7543 if (branch)
7544 got_ref_close(branch);
7545 if (new_base_branch)
7546 got_ref_close(new_base_branch);
7547 if (tmp_branch)
7548 got_ref_close(tmp_branch);
7549 if (worktree)
7550 got_worktree_close(worktree);
7551 if (repo)
7552 got_repo_close(repo);
7553 return error;
7556 __dead static void
7557 usage_histedit(void)
7559 fprintf(stderr, "usage: %s histedit [-a] [-c] [-F histedit-script] [-m]\n",
7560 getprogname());
7561 exit(1);
7564 #define GOT_HISTEDIT_PICK 'p'
7565 #define GOT_HISTEDIT_EDIT 'e'
7566 #define GOT_HISTEDIT_FOLD 'f'
7567 #define GOT_HISTEDIT_DROP 'd'
7568 #define GOT_HISTEDIT_MESG 'm'
7570 static struct got_histedit_cmd {
7571 unsigned char code;
7572 const char *name;
7573 const char *desc;
7574 } got_histedit_cmds[] = {
7575 { GOT_HISTEDIT_PICK, "pick", "use commit" },
7576 { GOT_HISTEDIT_EDIT, "edit", "use commit but stop for amending" },
7577 { GOT_HISTEDIT_FOLD, "fold", "combine with next commit that will "
7578 "be used" },
7579 { GOT_HISTEDIT_DROP, "drop", "remove commit from history" },
7580 { GOT_HISTEDIT_MESG, "mesg",
7581 "single-line log message for commit above (open editor if empty)" },
7584 struct got_histedit_list_entry {
7585 TAILQ_ENTRY(got_histedit_list_entry) entry;
7586 struct got_object_id *commit_id;
7587 const struct got_histedit_cmd *cmd;
7588 char *logmsg;
7590 TAILQ_HEAD(got_histedit_list, got_histedit_list_entry);
7592 static const struct got_error *
7593 histedit_write_commit(struct got_object_id *commit_id, const char *cmdname,
7594 FILE *f, struct got_repository *repo)
7596 const struct got_error *err = NULL;
7597 char *logmsg = NULL, *id_str = NULL;
7598 struct got_commit_object *commit = NULL;
7599 int n;
7601 err = got_object_open_as_commit(&commit, repo, commit_id);
7602 if (err)
7603 goto done;
7605 err = get_short_logmsg(&logmsg, 34, commit);
7606 if (err)
7607 goto done;
7609 err = got_object_id_str(&id_str, commit_id);
7610 if (err)
7611 goto done;
7613 n = fprintf(f, "%s %s %s\n", cmdname, id_str, logmsg);
7614 if (n < 0)
7615 err = got_ferror(f, GOT_ERR_IO);
7616 done:
7617 if (commit)
7618 got_object_commit_close(commit);
7619 free(id_str);
7620 free(logmsg);
7621 return err;
7624 static const struct got_error *
7625 histedit_write_commit_list(struct got_object_id_queue *commits,
7626 FILE *f, int edit_logmsg_only, struct got_repository *repo)
7628 const struct got_error *err = NULL;
7629 struct got_object_qid *qid;
7631 if (SIMPLEQ_EMPTY(commits))
7632 return got_error(GOT_ERR_EMPTY_HISTEDIT);
7634 SIMPLEQ_FOREACH(qid, commits, entry) {
7635 err = histedit_write_commit(qid->id, got_histedit_cmds[0].name,
7636 f, repo);
7637 if (err)
7638 break;
7639 if (edit_logmsg_only) {
7640 int n = fprintf(f, "%c\n", GOT_HISTEDIT_MESG);
7641 if (n < 0) {
7642 err = got_ferror(f, GOT_ERR_IO);
7643 break;
7648 return err;
7651 static const struct got_error *
7652 write_cmd_list(FILE *f, const char *branch_name,
7653 struct got_object_id_queue *commits)
7655 const struct got_error *err = NULL;
7656 int n, i;
7657 char *id_str;
7658 struct got_object_qid *qid;
7660 qid = SIMPLEQ_FIRST(commits);
7661 err = got_object_id_str(&id_str, qid->id);
7662 if (err)
7663 return err;
7665 n = fprintf(f,
7666 "# Editing the history of branch '%s' starting at\n"
7667 "# commit %s\n"
7668 "# Commits will be processed in order from top to "
7669 "bottom of this file.\n", branch_name, id_str);
7670 if (n < 0) {
7671 err = got_ferror(f, GOT_ERR_IO);
7672 goto done;
7675 n = fprintf(f, "# Available histedit commands:\n");
7676 if (n < 0) {
7677 err = got_ferror(f, GOT_ERR_IO);
7678 goto done;
7681 for (i = 0; i < nitems(got_histedit_cmds); i++) {
7682 struct got_histedit_cmd *cmd = &got_histedit_cmds[i];
7683 n = fprintf(f, "# %s (%c): %s\n", cmd->name, cmd->code,
7684 cmd->desc);
7685 if (n < 0) {
7686 err = got_ferror(f, GOT_ERR_IO);
7687 break;
7690 done:
7691 free(id_str);
7692 return err;
7695 static const struct got_error *
7696 histedit_syntax_error(int lineno)
7698 static char msg[42];
7699 int ret;
7701 ret = snprintf(msg, sizeof(msg), "histedit syntax error on line %d",
7702 lineno);
7703 if (ret == -1 || ret >= sizeof(msg))
7704 return got_error(GOT_ERR_HISTEDIT_SYNTAX);
7706 return got_error_msg(GOT_ERR_HISTEDIT_SYNTAX, msg);
7709 static const struct got_error *
7710 append_folded_commit_msg(char **new_msg, struct got_histedit_list_entry *hle,
7711 char *logmsg, struct got_repository *repo)
7713 const struct got_error *err;
7714 struct got_commit_object *folded_commit = NULL;
7715 char *id_str, *folded_logmsg = NULL;
7717 err = got_object_id_str(&id_str, hle->commit_id);
7718 if (err)
7719 return err;
7721 err = got_object_open_as_commit(&folded_commit, repo, hle->commit_id);
7722 if (err)
7723 goto done;
7725 err = got_object_commit_get_logmsg(&folded_logmsg, folded_commit);
7726 if (err)
7727 goto done;
7728 if (asprintf(new_msg, "%s%s# log message of folded commit %s: %s",
7729 logmsg ? logmsg : "", logmsg ? "\n" : "", id_str,
7730 folded_logmsg) == -1) {
7731 err = got_error_from_errno("asprintf");
7733 done:
7734 if (folded_commit)
7735 got_object_commit_close(folded_commit);
7736 free(id_str);
7737 free(folded_logmsg);
7738 return err;
7741 static struct got_histedit_list_entry *
7742 get_folded_commits(struct got_histedit_list_entry *hle)
7744 struct got_histedit_list_entry *prev, *folded = NULL;
7746 prev = TAILQ_PREV(hle, got_histedit_list, entry);
7747 while (prev && (prev->cmd->code == GOT_HISTEDIT_FOLD ||
7748 prev->cmd->code == GOT_HISTEDIT_DROP)) {
7749 if (prev->cmd->code == GOT_HISTEDIT_FOLD)
7750 folded = prev;
7751 prev = TAILQ_PREV(prev, got_histedit_list, entry);
7754 return folded;
7757 static const struct got_error *
7758 histedit_edit_logmsg(struct got_histedit_list_entry *hle,
7759 struct got_repository *repo)
7761 char *logmsg_path = NULL, *id_str = NULL, *orig_logmsg = NULL;
7762 char *logmsg = NULL, *new_msg = NULL, *editor = NULL;
7763 const struct got_error *err = NULL;
7764 struct got_commit_object *commit = NULL;
7765 int logmsg_len;
7766 int fd;
7767 struct got_histedit_list_entry *folded = NULL;
7769 err = got_object_open_as_commit(&commit, repo, hle->commit_id);
7770 if (err)
7771 return err;
7773 folded = get_folded_commits(hle);
7774 if (folded) {
7775 while (folded != hle) {
7776 if (folded->cmd->code == GOT_HISTEDIT_DROP) {
7777 folded = TAILQ_NEXT(folded, entry);
7778 continue;
7780 err = append_folded_commit_msg(&new_msg, folded,
7781 logmsg, repo);
7782 if (err)
7783 goto done;
7784 free(logmsg);
7785 logmsg = new_msg;
7786 folded = TAILQ_NEXT(folded, entry);
7790 err = got_object_id_str(&id_str, hle->commit_id);
7791 if (err)
7792 goto done;
7793 err = got_object_commit_get_logmsg(&orig_logmsg, commit);
7794 if (err)
7795 goto done;
7796 logmsg_len = asprintf(&new_msg,
7797 "%s\n# original log message of commit %s: %s",
7798 logmsg ? logmsg : "", id_str, orig_logmsg);
7799 if (logmsg_len == -1) {
7800 err = got_error_from_errno("asprintf");
7801 goto done;
7803 free(logmsg);
7804 logmsg = new_msg;
7806 err = got_object_id_str(&id_str, hle->commit_id);
7807 if (err)
7808 goto done;
7810 err = got_opentemp_named_fd(&logmsg_path, &fd,
7811 GOT_TMPDIR_STR "/got-logmsg");
7812 if (err)
7813 goto done;
7815 write(fd, logmsg, logmsg_len);
7816 close(fd);
7818 err = get_editor(&editor);
7819 if (err)
7820 goto done;
7822 err = edit_logmsg(&hle->logmsg, editor, logmsg_path, logmsg);
7823 if (err) {
7824 if (err->code != GOT_ERR_COMMIT_MSG_EMPTY)
7825 goto done;
7826 err = got_object_commit_get_logmsg(&hle->logmsg, commit);
7828 done:
7829 if (logmsg_path && unlink(logmsg_path) != 0 && err == NULL)
7830 err = got_error_from_errno2("unlink", logmsg_path);
7831 free(logmsg_path);
7832 free(logmsg);
7833 free(orig_logmsg);
7834 free(editor);
7835 if (commit)
7836 got_object_commit_close(commit);
7837 return err;
7840 static const struct got_error *
7841 histedit_parse_list(struct got_histedit_list *histedit_cmds,
7842 FILE *f, struct got_repository *repo)
7844 const struct got_error *err = NULL;
7845 char *line = NULL, *p, *end;
7846 size_t size;
7847 ssize_t len;
7848 int lineno = 0, i;
7849 const struct got_histedit_cmd *cmd;
7850 struct got_object_id *commit_id = NULL;
7851 struct got_histedit_list_entry *hle = NULL;
7853 for (;;) {
7854 len = getline(&line, &size, f);
7855 if (len == -1) {
7856 const struct got_error *getline_err;
7857 if (feof(f))
7858 break;
7859 getline_err = got_error_from_errno("getline");
7860 err = got_ferror(f, getline_err->code);
7861 break;
7863 lineno++;
7864 p = line;
7865 while (isspace((unsigned char)p[0]))
7866 p++;
7867 if (p[0] == '#' || p[0] == '\0') {
7868 free(line);
7869 line = NULL;
7870 continue;
7872 cmd = NULL;
7873 for (i = 0; i < nitems(got_histedit_cmds); i++) {
7874 cmd = &got_histedit_cmds[i];
7875 if (strncmp(cmd->name, p, strlen(cmd->name)) == 0 &&
7876 isspace((unsigned char)p[strlen(cmd->name)])) {
7877 p += strlen(cmd->name);
7878 break;
7880 if (p[0] == cmd->code && isspace((unsigned char)p[1])) {
7881 p++;
7882 break;
7885 if (i == nitems(got_histedit_cmds)) {
7886 err = histedit_syntax_error(lineno);
7887 break;
7889 while (isspace((unsigned char)p[0]))
7890 p++;
7891 if (cmd->code == GOT_HISTEDIT_MESG) {
7892 if (hle == NULL || hle->logmsg != NULL) {
7893 err = got_error(GOT_ERR_HISTEDIT_CMD);
7894 break;
7896 if (p[0] == '\0') {
7897 err = histedit_edit_logmsg(hle, repo);
7898 if (err)
7899 break;
7900 } else {
7901 hle->logmsg = strdup(p);
7902 if (hle->logmsg == NULL) {
7903 err = got_error_from_errno("strdup");
7904 break;
7907 free(line);
7908 line = NULL;
7909 continue;
7910 } else {
7911 end = p;
7912 while (end[0] && !isspace((unsigned char)end[0]))
7913 end++;
7914 *end = '\0';
7916 err = got_object_resolve_id_str(&commit_id, repo, p);
7917 if (err) {
7918 /* override error code */
7919 err = histedit_syntax_error(lineno);
7920 break;
7923 hle = malloc(sizeof(*hle));
7924 if (hle == NULL) {
7925 err = got_error_from_errno("malloc");
7926 break;
7928 hle->cmd = cmd;
7929 hle->commit_id = commit_id;
7930 hle->logmsg = NULL;
7931 commit_id = NULL;
7932 free(line);
7933 line = NULL;
7934 TAILQ_INSERT_TAIL(histedit_cmds, hle, entry);
7937 free(line);
7938 free(commit_id);
7939 return err;
7942 static const struct got_error *
7943 histedit_check_script(struct got_histedit_list *histedit_cmds,
7944 struct got_object_id_queue *commits, struct got_repository *repo)
7946 const struct got_error *err = NULL;
7947 struct got_object_qid *qid;
7948 struct got_histedit_list_entry *hle;
7949 static char msg[92];
7950 char *id_str;
7952 if (TAILQ_EMPTY(histedit_cmds))
7953 return got_error_msg(GOT_ERR_EMPTY_HISTEDIT,
7954 "histedit script contains no commands");
7955 if (SIMPLEQ_EMPTY(commits))
7956 return got_error(GOT_ERR_EMPTY_HISTEDIT);
7958 TAILQ_FOREACH(hle, histedit_cmds, entry) {
7959 struct got_histedit_list_entry *hle2;
7960 TAILQ_FOREACH(hle2, histedit_cmds, entry) {
7961 if (hle == hle2)
7962 continue;
7963 if (got_object_id_cmp(hle->commit_id,
7964 hle2->commit_id) != 0)
7965 continue;
7966 err = got_object_id_str(&id_str, hle->commit_id);
7967 if (err)
7968 return err;
7969 snprintf(msg, sizeof(msg), "commit %s is listed "
7970 "more than once in histedit script", id_str);
7971 free(id_str);
7972 return got_error_msg(GOT_ERR_HISTEDIT_CMD, msg);
7976 SIMPLEQ_FOREACH(qid, commits, entry) {
7977 TAILQ_FOREACH(hle, histedit_cmds, entry) {
7978 if (got_object_id_cmp(qid->id, hle->commit_id) == 0)
7979 break;
7981 if (hle == NULL) {
7982 err = got_object_id_str(&id_str, qid->id);
7983 if (err)
7984 return err;
7985 snprintf(msg, sizeof(msg),
7986 "commit %s missing from histedit script", id_str);
7987 free(id_str);
7988 return got_error_msg(GOT_ERR_HISTEDIT_CMD, msg);
7992 hle = TAILQ_LAST(histedit_cmds, got_histedit_list);
7993 if (hle && hle->cmd->code == GOT_HISTEDIT_FOLD)
7994 return got_error_msg(GOT_ERR_HISTEDIT_CMD,
7995 "last commit in histedit script cannot be folded");
7997 return NULL;
8000 static const struct got_error *
8001 histedit_run_editor(struct got_histedit_list *histedit_cmds,
8002 const char *path, struct got_object_id_queue *commits,
8003 struct got_repository *repo)
8005 const struct got_error *err = NULL;
8006 char *editor;
8007 FILE *f = NULL;
8009 err = get_editor(&editor);
8010 if (err)
8011 return err;
8013 if (spawn_editor(editor, path) == -1) {
8014 err = got_error_from_errno("failed spawning editor");
8015 goto done;
8018 f = fopen(path, "r");
8019 if (f == NULL) {
8020 err = got_error_from_errno("fopen");
8021 goto done;
8023 err = histedit_parse_list(histedit_cmds, f, repo);
8024 if (err)
8025 goto done;
8027 err = histedit_check_script(histedit_cmds, commits, repo);
8028 done:
8029 if (f && fclose(f) != 0 && err == NULL)
8030 err = got_error_from_errno("fclose");
8031 free(editor);
8032 return err;
8035 static const struct got_error *
8036 histedit_edit_list_retry(struct got_histedit_list *, const struct got_error *,
8037 struct got_object_id_queue *, const char *, const char *,
8038 struct got_repository *);
8040 static const struct got_error *
8041 histedit_edit_script(struct got_histedit_list *histedit_cmds,
8042 struct got_object_id_queue *commits, const char *branch_name,
8043 int edit_logmsg_only, struct got_repository *repo)
8045 const struct got_error *err;
8046 FILE *f = NULL;
8047 char *path = NULL;
8049 err = got_opentemp_named(&path, &f, "got-histedit");
8050 if (err)
8051 return err;
8053 err = write_cmd_list(f, branch_name, commits);
8054 if (err)
8055 goto done;
8057 err = histedit_write_commit_list(commits, f, edit_logmsg_only, repo);
8058 if (err)
8059 goto done;
8061 if (edit_logmsg_only) {
8062 rewind(f);
8063 err = histedit_parse_list(histedit_cmds, f, repo);
8064 } else {
8065 if (fclose(f) != 0) {
8066 err = got_error_from_errno("fclose");
8067 goto done;
8069 f = NULL;
8070 err = histedit_run_editor(histedit_cmds, path, commits, repo);
8071 if (err) {
8072 if (err->code != GOT_ERR_HISTEDIT_SYNTAX &&
8073 err->code != GOT_ERR_HISTEDIT_CMD)
8074 goto done;
8075 err = histedit_edit_list_retry(histedit_cmds, err,
8076 commits, path, branch_name, repo);
8079 done:
8080 if (f && fclose(f) != 0 && err == NULL)
8081 err = got_error_from_errno("fclose");
8082 if (path && unlink(path) != 0 && err == NULL)
8083 err = got_error_from_errno2("unlink", path);
8084 free(path);
8085 return err;
8088 static const struct got_error *
8089 histedit_save_list(struct got_histedit_list *histedit_cmds,
8090 struct got_worktree *worktree, struct got_repository *repo)
8092 const struct got_error *err = NULL;
8093 char *path = NULL;
8094 FILE *f = NULL;
8095 struct got_histedit_list_entry *hle;
8096 struct got_commit_object *commit = NULL;
8098 err = got_worktree_get_histedit_script_path(&path, worktree);
8099 if (err)
8100 return err;
8102 f = fopen(path, "w");
8103 if (f == NULL) {
8104 err = got_error_from_errno2("fopen", path);
8105 goto done;
8107 TAILQ_FOREACH(hle, histedit_cmds, entry) {
8108 err = histedit_write_commit(hle->commit_id, hle->cmd->name, f,
8109 repo);
8110 if (err)
8111 break;
8113 if (hle->logmsg) {
8114 int n = fprintf(f, "%c %s\n",
8115 GOT_HISTEDIT_MESG, hle->logmsg);
8116 if (n < 0) {
8117 err = got_ferror(f, GOT_ERR_IO);
8118 break;
8122 done:
8123 if (f && fclose(f) != 0 && err == NULL)
8124 err = got_error_from_errno("fclose");
8125 free(path);
8126 if (commit)
8127 got_object_commit_close(commit);
8128 return err;
8131 void
8132 histedit_free_list(struct got_histedit_list *histedit_cmds)
8134 struct got_histedit_list_entry *hle;
8136 while ((hle = TAILQ_FIRST(histedit_cmds))) {
8137 TAILQ_REMOVE(histedit_cmds, hle, entry);
8138 free(hle);
8142 static const struct got_error *
8143 histedit_load_list(struct got_histedit_list *histedit_cmds,
8144 const char *path, struct got_repository *repo)
8146 const struct got_error *err = NULL;
8147 FILE *f = NULL;
8149 f = fopen(path, "r");
8150 if (f == NULL) {
8151 err = got_error_from_errno2("fopen", path);
8152 goto done;
8155 err = histedit_parse_list(histedit_cmds, f, repo);
8156 done:
8157 if (f && fclose(f) != 0 && err == NULL)
8158 err = got_error_from_errno("fclose");
8159 return err;
8162 static const struct got_error *
8163 histedit_edit_list_retry(struct got_histedit_list *histedit_cmds,
8164 const struct got_error *edit_err, struct got_object_id_queue *commits,
8165 const char *path, const char *branch_name, struct got_repository *repo)
8167 const struct got_error *err = NULL, *prev_err = edit_err;
8168 int resp = ' ';
8170 while (resp != 'c' && resp != 'r' && resp != 'a') {
8171 printf("%s: %s\n(c)ontinue editing, (r)estart editing, "
8172 "or (a)bort: ", getprogname(), prev_err->msg);
8173 resp = getchar();
8174 if (resp == '\n')
8175 resp = getchar();
8176 if (resp == 'c') {
8177 histedit_free_list(histedit_cmds);
8178 err = histedit_run_editor(histedit_cmds, path, commits,
8179 repo);
8180 if (err) {
8181 if (err->code != GOT_ERR_HISTEDIT_SYNTAX &&
8182 err->code != GOT_ERR_HISTEDIT_CMD)
8183 break;
8184 prev_err = err;
8185 resp = ' ';
8186 continue;
8188 break;
8189 } else if (resp == 'r') {
8190 histedit_free_list(histedit_cmds);
8191 err = histedit_edit_script(histedit_cmds,
8192 commits, branch_name, 0, repo);
8193 if (err) {
8194 if (err->code != GOT_ERR_HISTEDIT_SYNTAX &&
8195 err->code != GOT_ERR_HISTEDIT_CMD)
8196 break;
8197 prev_err = err;
8198 resp = ' ';
8199 continue;
8201 break;
8202 } else if (resp == 'a') {
8203 err = got_error(GOT_ERR_HISTEDIT_CANCEL);
8204 break;
8205 } else
8206 printf("invalid response '%c'\n", resp);
8209 return err;
8212 static const struct got_error *
8213 histedit_complete(struct got_worktree *worktree,
8214 struct got_fileindex *fileindex, struct got_reference *tmp_branch,
8215 struct got_reference *branch, struct got_repository *repo)
8217 printf("Switching work tree to %s\n",
8218 got_ref_get_symref_target(branch));
8219 return got_worktree_histedit_complete(worktree, fileindex, tmp_branch,
8220 branch, repo);
8223 static const struct got_error *
8224 show_histedit_progress(struct got_commit_object *commit,
8225 struct got_histedit_list_entry *hle, struct got_object_id *new_id)
8227 const struct got_error *err;
8228 char *old_id_str = NULL, *new_id_str = NULL, *logmsg = NULL;
8230 err = got_object_id_str(&old_id_str, hle->commit_id);
8231 if (err)
8232 goto done;
8234 if (new_id) {
8235 err = got_object_id_str(&new_id_str, new_id);
8236 if (err)
8237 goto done;
8240 old_id_str[12] = '\0';
8241 if (new_id_str)
8242 new_id_str[12] = '\0';
8244 if (hle->logmsg) {
8245 logmsg = strdup(hle->logmsg);
8246 if (logmsg == NULL) {
8247 err = got_error_from_errno("strdup");
8248 goto done;
8250 trim_logmsg(logmsg, 42);
8251 } else {
8252 err = get_short_logmsg(&logmsg, 42, commit);
8253 if (err)
8254 goto done;
8257 switch (hle->cmd->code) {
8258 case GOT_HISTEDIT_PICK:
8259 case GOT_HISTEDIT_EDIT:
8260 printf("%s -> %s: %s\n", old_id_str,
8261 new_id_str ? new_id_str : "no-op change", logmsg);
8262 break;
8263 case GOT_HISTEDIT_DROP:
8264 case GOT_HISTEDIT_FOLD:
8265 printf("%s -> %s commit: %s\n", old_id_str, hle->cmd->name,
8266 logmsg);
8267 break;
8268 default:
8269 break;
8271 done:
8272 free(old_id_str);
8273 free(new_id_str);
8274 return err;
8277 static const struct got_error *
8278 histedit_commit(struct got_pathlist_head *merged_paths,
8279 struct got_worktree *worktree, struct got_fileindex *fileindex,
8280 struct got_reference *tmp_branch, struct got_histedit_list_entry *hle,
8281 struct got_repository *repo)
8283 const struct got_error *err;
8284 struct got_commit_object *commit;
8285 struct got_object_id *new_commit_id;
8287 if ((hle->cmd->code == GOT_HISTEDIT_EDIT || get_folded_commits(hle))
8288 && hle->logmsg == NULL) {
8289 err = histedit_edit_logmsg(hle, repo);
8290 if (err)
8291 return err;
8294 err = got_object_open_as_commit(&commit, repo, hle->commit_id);
8295 if (err)
8296 return err;
8298 err = got_worktree_histedit_commit(&new_commit_id, merged_paths,
8299 worktree, fileindex, tmp_branch, commit, hle->commit_id,
8300 hle->logmsg, repo);
8301 if (err) {
8302 if (err->code != GOT_ERR_COMMIT_NO_CHANGES)
8303 goto done;
8304 err = show_histedit_progress(commit, hle, NULL);
8305 } else {
8306 err = show_histedit_progress(commit, hle, new_commit_id);
8307 free(new_commit_id);
8309 done:
8310 got_object_commit_close(commit);
8311 return err;
8314 static const struct got_error *
8315 histedit_skip_commit(struct got_histedit_list_entry *hle,
8316 struct got_worktree *worktree, struct got_repository *repo)
8318 const struct got_error *error;
8319 struct got_commit_object *commit;
8321 error = got_worktree_histedit_skip_commit(worktree, hle->commit_id,
8322 repo);
8323 if (error)
8324 return error;
8326 error = got_object_open_as_commit(&commit, repo, hle->commit_id);
8327 if (error)
8328 return error;
8330 error = show_histedit_progress(commit, hle, NULL);
8331 got_object_commit_close(commit);
8332 return error;
8335 static const struct got_error *
8336 check_local_changes(void *arg, unsigned char status,
8337 unsigned char staged_status, const char *path,
8338 struct got_object_id *blob_id, struct got_object_id *staged_blob_id,
8339 struct got_object_id *commit_id, int dirfd, const char *de_name)
8341 int *have_local_changes = arg;
8343 switch (status) {
8344 case GOT_STATUS_ADD:
8345 case GOT_STATUS_DELETE:
8346 case GOT_STATUS_MODIFY:
8347 case GOT_STATUS_CONFLICT:
8348 *have_local_changes = 1;
8349 return got_error(GOT_ERR_CANCELLED);
8350 default:
8351 break;
8354 switch (staged_status) {
8355 case GOT_STATUS_ADD:
8356 case GOT_STATUS_DELETE:
8357 case GOT_STATUS_MODIFY:
8358 *have_local_changes = 1;
8359 return got_error(GOT_ERR_CANCELLED);
8360 default:
8361 break;
8364 return NULL;
8367 static const struct got_error *
8368 cmd_histedit(int argc, char *argv[])
8370 const struct got_error *error = NULL;
8371 struct got_worktree *worktree = NULL;
8372 struct got_fileindex *fileindex = NULL;
8373 struct got_repository *repo = NULL;
8374 char *cwd = NULL;
8375 struct got_reference *branch = NULL;
8376 struct got_reference *tmp_branch = NULL;
8377 struct got_object_id *resume_commit_id = NULL;
8378 struct got_object_id *base_commit_id = NULL;
8379 struct got_object_id *head_commit_id = NULL;
8380 struct got_commit_object *commit = NULL;
8381 int ch, rebase_in_progress = 0;
8382 struct got_update_progress_arg upa;
8383 int edit_in_progress = 0, abort_edit = 0, continue_edit = 0;
8384 int edit_logmsg_only = 0;
8385 const char *edit_script_path = NULL;
8386 unsigned char rebase_status = GOT_STATUS_NO_CHANGE;
8387 struct got_object_id_queue commits;
8388 struct got_pathlist_head merged_paths;
8389 const struct got_object_id_queue *parent_ids;
8390 struct got_object_qid *pid;
8391 struct got_histedit_list histedit_cmds;
8392 struct got_histedit_list_entry *hle;
8394 SIMPLEQ_INIT(&commits);
8395 TAILQ_INIT(&histedit_cmds);
8396 TAILQ_INIT(&merged_paths);
8397 memset(&upa, 0, sizeof(upa));
8399 while ((ch = getopt(argc, argv, "acF:m")) != -1) {
8400 switch (ch) {
8401 case 'a':
8402 abort_edit = 1;
8403 break;
8404 case 'c':
8405 continue_edit = 1;
8406 break;
8407 case 'F':
8408 edit_script_path = optarg;
8409 break;
8410 case 'm':
8411 edit_logmsg_only = 1;
8412 break;
8413 default:
8414 usage_histedit();
8415 /* NOTREACHED */
8419 argc -= optind;
8420 argv += optind;
8422 #ifndef PROFILE
8423 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
8424 "unveil", NULL) == -1)
8425 err(1, "pledge");
8426 #endif
8427 if (abort_edit && continue_edit)
8428 errx(1, "histedit's -a and -c options are mutually exclusive");
8429 if (edit_script_path && edit_logmsg_only)
8430 errx(1, "histedit's -F and -m options are mutually exclusive");
8431 if (abort_edit && edit_logmsg_only)
8432 errx(1, "histedit's -a and -m options are mutually exclusive");
8433 if (continue_edit && edit_logmsg_only)
8434 errx(1, "histedit's -c and -m options are mutually exclusive");
8435 if (argc != 0)
8436 usage_histedit();
8439 * This command cannot apply unveil(2) in all cases because the
8440 * user may choose to run an editor to edit the histedit script
8441 * and to edit individual commit log messages.
8442 * unveil(2) traverses exec(2); if an editor is used we have to
8443 * apply unveil after edit script and log messages have been written.
8444 * XXX TODO: Make use of unveil(2) where possible.
8447 cwd = getcwd(NULL, 0);
8448 if (cwd == NULL) {
8449 error = got_error_from_errno("getcwd");
8450 goto done;
8452 error = got_worktree_open(&worktree, cwd);
8453 if (error) {
8454 if (error->code == GOT_ERR_NOT_WORKTREE)
8455 error = wrap_not_worktree_error(error, "histedit", cwd);
8456 goto done;
8459 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
8460 NULL);
8461 if (error != NULL)
8462 goto done;
8464 error = got_worktree_rebase_in_progress(&rebase_in_progress, worktree);
8465 if (error)
8466 goto done;
8467 if (rebase_in_progress) {
8468 error = got_error(GOT_ERR_REBASING);
8469 goto done;
8472 error = got_worktree_histedit_in_progress(&edit_in_progress, worktree);
8473 if (error)
8474 goto done;
8476 if (edit_in_progress && edit_logmsg_only) {
8477 error = got_error_msg(GOT_ERR_HISTEDIT_BUSY,
8478 "histedit operation is in progress in this "
8479 "work tree and must be continued or aborted "
8480 "before the -m option can be used");
8481 goto done;
8484 if (edit_in_progress && abort_edit) {
8485 error = got_worktree_histedit_continue(&resume_commit_id,
8486 &tmp_branch, &branch, &base_commit_id, &fileindex,
8487 worktree, repo);
8488 if (error)
8489 goto done;
8490 printf("Switching work tree to %s\n",
8491 got_ref_get_symref_target(branch));
8492 error = got_worktree_histedit_abort(worktree, fileindex, repo,
8493 branch, base_commit_id, update_progress, &upa);
8494 if (error)
8495 goto done;
8496 printf("Histedit of %s aborted\n",
8497 got_ref_get_symref_target(branch));
8498 print_update_progress_stats(&upa);
8499 goto done; /* nothing else to do */
8500 } else if (abort_edit) {
8501 error = got_error(GOT_ERR_NOT_HISTEDIT);
8502 goto done;
8505 if (continue_edit) {
8506 char *path;
8508 if (!edit_in_progress) {
8509 error = got_error(GOT_ERR_NOT_HISTEDIT);
8510 goto done;
8513 error = got_worktree_get_histedit_script_path(&path, worktree);
8514 if (error)
8515 goto done;
8517 error = histedit_load_list(&histedit_cmds, path, repo);
8518 free(path);
8519 if (error)
8520 goto done;
8522 error = got_worktree_histedit_continue(&resume_commit_id,
8523 &tmp_branch, &branch, &base_commit_id, &fileindex,
8524 worktree, repo);
8525 if (error)
8526 goto done;
8528 error = got_ref_resolve(&head_commit_id, repo, branch);
8529 if (error)
8530 goto done;
8532 error = got_object_open_as_commit(&commit, repo,
8533 head_commit_id);
8534 if (error)
8535 goto done;
8536 parent_ids = got_object_commit_get_parent_ids(commit);
8537 pid = SIMPLEQ_FIRST(parent_ids);
8538 if (pid == NULL) {
8539 error = got_error(GOT_ERR_EMPTY_HISTEDIT);
8540 goto done;
8542 error = collect_commits(&commits, head_commit_id, pid->id,
8543 base_commit_id, got_worktree_get_path_prefix(worktree),
8544 GOT_ERR_HISTEDIT_PATH, repo);
8545 got_object_commit_close(commit);
8546 commit = NULL;
8547 if (error)
8548 goto done;
8549 } else {
8550 if (edit_in_progress) {
8551 error = got_error(GOT_ERR_HISTEDIT_BUSY);
8552 goto done;
8555 error = got_ref_open(&branch, repo,
8556 got_worktree_get_head_ref_name(worktree), 0);
8557 if (error != NULL)
8558 goto done;
8560 if (strncmp(got_ref_get_name(branch), "refs/heads/", 11) != 0) {
8561 error = got_error_msg(GOT_ERR_COMMIT_BRANCH,
8562 "will not edit commit history of a branch outside "
8563 "the \"refs/heads/\" reference namespace");
8564 goto done;
8567 error = got_ref_resolve(&head_commit_id, repo, branch);
8568 got_ref_close(branch);
8569 branch = NULL;
8570 if (error)
8571 goto done;
8573 error = got_object_open_as_commit(&commit, repo,
8574 head_commit_id);
8575 if (error)
8576 goto done;
8577 parent_ids = got_object_commit_get_parent_ids(commit);
8578 pid = SIMPLEQ_FIRST(parent_ids);
8579 if (pid == NULL) {
8580 error = got_error(GOT_ERR_EMPTY_HISTEDIT);
8581 goto done;
8583 error = collect_commits(&commits, head_commit_id, pid->id,
8584 got_worktree_get_base_commit_id(worktree),
8585 got_worktree_get_path_prefix(worktree),
8586 GOT_ERR_HISTEDIT_PATH, repo);
8587 got_object_commit_close(commit);
8588 commit = NULL;
8589 if (error)
8590 goto done;
8592 if (SIMPLEQ_EMPTY(&commits)) {
8593 error = got_error(GOT_ERR_EMPTY_HISTEDIT);
8594 goto done;
8597 error = got_worktree_histedit_prepare(&tmp_branch, &branch,
8598 &base_commit_id, &fileindex, worktree, repo);
8599 if (error)
8600 goto done;
8602 if (edit_script_path) {
8603 error = histedit_load_list(&histedit_cmds,
8604 edit_script_path, repo);
8605 if (error) {
8606 got_worktree_histedit_abort(worktree, fileindex,
8607 repo, branch, base_commit_id,
8608 update_progress, &upa);
8609 print_update_progress_stats(&upa);
8610 goto done;
8612 } else {
8613 const char *branch_name;
8614 branch_name = got_ref_get_symref_target(branch);
8615 if (strncmp(branch_name, "refs/heads/", 11) == 0)
8616 branch_name += 11;
8617 error = histedit_edit_script(&histedit_cmds, &commits,
8618 branch_name, edit_logmsg_only, repo);
8619 if (error) {
8620 got_worktree_histedit_abort(worktree, fileindex,
8621 repo, branch, base_commit_id,
8622 update_progress, &upa);
8623 print_update_progress_stats(&upa);
8624 goto done;
8629 error = histedit_save_list(&histedit_cmds, worktree,
8630 repo);
8631 if (error) {
8632 got_worktree_histedit_abort(worktree, fileindex,
8633 repo, branch, base_commit_id,
8634 update_progress, &upa);
8635 print_update_progress_stats(&upa);
8636 goto done;
8641 error = histedit_check_script(&histedit_cmds, &commits, repo);
8642 if (error)
8643 goto done;
8645 TAILQ_FOREACH(hle, &histedit_cmds, entry) {
8646 if (resume_commit_id) {
8647 if (got_object_id_cmp(hle->commit_id,
8648 resume_commit_id) != 0)
8649 continue;
8651 resume_commit_id = NULL;
8652 if (hle->cmd->code == GOT_HISTEDIT_DROP ||
8653 hle->cmd->code == GOT_HISTEDIT_FOLD) {
8654 error = histedit_skip_commit(hle, worktree,
8655 repo);
8656 if (error)
8657 goto done;
8658 } else {
8659 struct got_pathlist_head paths;
8660 int have_changes = 0;
8662 TAILQ_INIT(&paths);
8663 error = got_pathlist_append(&paths, "", NULL);
8664 if (error)
8665 goto done;
8666 error = got_worktree_status(worktree, &paths,
8667 repo, check_local_changes, &have_changes,
8668 check_cancelled, NULL);
8669 got_pathlist_free(&paths);
8670 if (error) {
8671 if (error->code != GOT_ERR_CANCELLED)
8672 goto done;
8673 if (sigint_received || sigpipe_received)
8674 goto done;
8676 if (have_changes) {
8677 error = histedit_commit(NULL, worktree,
8678 fileindex, tmp_branch, hle, repo);
8679 if (error)
8680 goto done;
8681 } else {
8682 error = got_object_open_as_commit(
8683 &commit, repo, hle->commit_id);
8684 if (error)
8685 goto done;
8686 error = show_histedit_progress(commit,
8687 hle, NULL);
8688 got_object_commit_close(commit);
8689 commit = NULL;
8690 if (error)
8691 goto done;
8694 continue;
8697 if (hle->cmd->code == GOT_HISTEDIT_DROP) {
8698 error = histedit_skip_commit(hle, worktree, repo);
8699 if (error)
8700 goto done;
8701 continue;
8704 error = got_object_open_as_commit(&commit, repo,
8705 hle->commit_id);
8706 if (error)
8707 goto done;
8708 parent_ids = got_object_commit_get_parent_ids(commit);
8709 pid = SIMPLEQ_FIRST(parent_ids);
8711 error = got_worktree_histedit_merge_files(&merged_paths,
8712 worktree, fileindex, pid->id, hle->commit_id, repo,
8713 update_progress, &upa, check_cancelled, NULL);
8714 if (error)
8715 goto done;
8716 got_object_commit_close(commit);
8717 commit = NULL;
8719 print_update_progress_stats(&upa);
8720 if (upa.conflicts > 0)
8721 rebase_status = GOT_STATUS_CONFLICT;
8723 if (rebase_status == GOT_STATUS_CONFLICT) {
8724 error = show_rebase_merge_conflict(hle->commit_id,
8725 repo);
8726 if (error)
8727 goto done;
8728 got_worktree_rebase_pathlist_free(&merged_paths);
8729 break;
8732 if (hle->cmd->code == GOT_HISTEDIT_EDIT) {
8733 char *id_str;
8734 error = got_object_id_str(&id_str, hle->commit_id);
8735 if (error)
8736 goto done;
8737 printf("Stopping histedit for amending commit %s\n",
8738 id_str);
8739 free(id_str);
8740 got_worktree_rebase_pathlist_free(&merged_paths);
8741 error = got_worktree_histedit_postpone(worktree,
8742 fileindex);
8743 goto done;
8746 if (hle->cmd->code == GOT_HISTEDIT_FOLD) {
8747 error = histedit_skip_commit(hle, worktree, repo);
8748 if (error)
8749 goto done;
8750 continue;
8753 error = histedit_commit(&merged_paths, worktree, fileindex,
8754 tmp_branch, hle, repo);
8755 got_worktree_rebase_pathlist_free(&merged_paths);
8756 if (error)
8757 goto done;
8760 if (rebase_status == GOT_STATUS_CONFLICT) {
8761 error = got_worktree_histedit_postpone(worktree, fileindex);
8762 if (error)
8763 goto done;
8764 error = got_error_msg(GOT_ERR_CONFLICTS,
8765 "conflicts must be resolved before histedit can continue");
8766 } else
8767 error = histedit_complete(worktree, fileindex, tmp_branch,
8768 branch, repo);
8769 done:
8770 got_object_id_queue_free(&commits);
8771 histedit_free_list(&histedit_cmds);
8772 free(head_commit_id);
8773 free(base_commit_id);
8774 free(resume_commit_id);
8775 if (commit)
8776 got_object_commit_close(commit);
8777 if (branch)
8778 got_ref_close(branch);
8779 if (tmp_branch)
8780 got_ref_close(tmp_branch);
8781 if (worktree)
8782 got_worktree_close(worktree);
8783 if (repo)
8784 got_repo_close(repo);
8785 return error;
8788 __dead static void
8789 usage_integrate(void)
8791 fprintf(stderr, "usage: %s integrate branch\n", getprogname());
8792 exit(1);
8795 static const struct got_error *
8796 cmd_integrate(int argc, char *argv[])
8798 const struct got_error *error = NULL;
8799 struct got_repository *repo = NULL;
8800 struct got_worktree *worktree = NULL;
8801 char *cwd = NULL, *refname = NULL, *base_refname = NULL;
8802 const char *branch_arg = NULL;
8803 struct got_reference *branch_ref = NULL, *base_branch_ref = NULL;
8804 struct got_fileindex *fileindex = NULL;
8805 struct got_object_id *commit_id = NULL, *base_commit_id = NULL;
8806 int ch;
8807 struct got_update_progress_arg upa;
8809 while ((ch = getopt(argc, argv, "")) != -1) {
8810 switch (ch) {
8811 default:
8812 usage_integrate();
8813 /* NOTREACHED */
8817 argc -= optind;
8818 argv += optind;
8820 if (argc != 1)
8821 usage_integrate();
8822 branch_arg = argv[0];
8824 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
8825 "unveil", NULL) == -1)
8826 err(1, "pledge");
8828 cwd = getcwd(NULL, 0);
8829 if (cwd == NULL) {
8830 error = got_error_from_errno("getcwd");
8831 goto done;
8834 error = got_worktree_open(&worktree, cwd);
8835 if (error) {
8836 if (error->code == GOT_ERR_NOT_WORKTREE)
8837 error = wrap_not_worktree_error(error, "integrate",
8838 cwd);
8839 goto done;
8842 error = check_rebase_or_histedit_in_progress(worktree);
8843 if (error)
8844 goto done;
8846 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
8847 NULL);
8848 if (error != NULL)
8849 goto done;
8851 error = apply_unveil(got_repo_get_path(repo), 0,
8852 got_worktree_get_root_path(worktree));
8853 if (error)
8854 goto done;
8856 if (asprintf(&refname, "refs/heads/%s", branch_arg) == -1) {
8857 error = got_error_from_errno("asprintf");
8858 goto done;
8861 error = got_worktree_integrate_prepare(&fileindex, &branch_ref,
8862 &base_branch_ref, worktree, refname, repo);
8863 if (error)
8864 goto done;
8866 refname = strdup(got_ref_get_name(branch_ref));
8867 if (refname == NULL) {
8868 error = got_error_from_errno("strdup");
8869 got_worktree_integrate_abort(worktree, fileindex, repo,
8870 branch_ref, base_branch_ref);
8871 goto done;
8873 base_refname = strdup(got_ref_get_name(base_branch_ref));
8874 if (base_refname == NULL) {
8875 error = got_error_from_errno("strdup");
8876 got_worktree_integrate_abort(worktree, fileindex, repo,
8877 branch_ref, base_branch_ref);
8878 goto done;
8881 error = got_ref_resolve(&commit_id, repo, branch_ref);
8882 if (error)
8883 goto done;
8885 error = got_ref_resolve(&base_commit_id, repo, base_branch_ref);
8886 if (error)
8887 goto done;
8889 if (got_object_id_cmp(commit_id, base_commit_id) == 0) {
8890 error = got_error_msg(GOT_ERR_SAME_BRANCH,
8891 "specified branch has already been integrated");
8892 got_worktree_integrate_abort(worktree, fileindex, repo,
8893 branch_ref, base_branch_ref);
8894 goto done;
8897 error = check_linear_ancestry(commit_id, base_commit_id, 1, repo);
8898 if (error) {
8899 if (error->code == GOT_ERR_ANCESTRY)
8900 error = got_error(GOT_ERR_REBASE_REQUIRED);
8901 got_worktree_integrate_abort(worktree, fileindex, repo,
8902 branch_ref, base_branch_ref);
8903 goto done;
8906 memset(&upa, 0, sizeof(upa));
8907 error = got_worktree_integrate_continue(worktree, fileindex, repo,
8908 branch_ref, base_branch_ref, update_progress, &upa,
8909 check_cancelled, NULL);
8910 if (error)
8911 goto done;
8913 printf("Integrated %s into %s\n", refname, base_refname);
8914 print_update_progress_stats(&upa);
8915 done:
8916 if (repo)
8917 got_repo_close(repo);
8918 if (worktree)
8919 got_worktree_close(worktree);
8920 free(cwd);
8921 free(base_commit_id);
8922 free(commit_id);
8923 free(refname);
8924 free(base_refname);
8925 return error;
8928 __dead static void
8929 usage_stage(void)
8931 fprintf(stderr, "usage: %s stage [-l] | [-p] [-F response-script] "
8932 "[-S] [file-path ...]\n",
8933 getprogname());
8934 exit(1);
8937 static const struct got_error *
8938 print_stage(void *arg, unsigned char status, unsigned char staged_status,
8939 const char *path, struct got_object_id *blob_id,
8940 struct got_object_id *staged_blob_id, struct got_object_id *commit_id,
8941 int dirfd, const char *de_name)
8943 const struct got_error *err = NULL;
8944 char *id_str = NULL;
8946 if (staged_status != GOT_STATUS_ADD &&
8947 staged_status != GOT_STATUS_MODIFY &&
8948 staged_status != GOT_STATUS_DELETE)
8949 return NULL;
8951 if (staged_status == GOT_STATUS_ADD ||
8952 staged_status == GOT_STATUS_MODIFY)
8953 err = got_object_id_str(&id_str, staged_blob_id);
8954 else
8955 err = got_object_id_str(&id_str, blob_id);
8956 if (err)
8957 return err;
8959 printf("%s %c %s\n", id_str, staged_status, path);
8960 free(id_str);
8961 return NULL;
8964 static const struct got_error *
8965 cmd_stage(int argc, char *argv[])
8967 const struct got_error *error = NULL;
8968 struct got_repository *repo = NULL;
8969 struct got_worktree *worktree = NULL;
8970 char *cwd = NULL;
8971 struct got_pathlist_head paths;
8972 struct got_pathlist_entry *pe;
8973 int ch, list_stage = 0, pflag = 0, allow_bad_symlinks = 0;
8974 FILE *patch_script_file = NULL;
8975 const char *patch_script_path = NULL;
8976 struct choose_patch_arg cpa;
8978 TAILQ_INIT(&paths);
8980 while ((ch = getopt(argc, argv, "lpF:S")) != -1) {
8981 switch (ch) {
8982 case 'l':
8983 list_stage = 1;
8984 break;
8985 case 'p':
8986 pflag = 1;
8987 break;
8988 case 'F':
8989 patch_script_path = optarg;
8990 break;
8991 case 'S':
8992 allow_bad_symlinks = 1;
8993 break;
8994 default:
8995 usage_stage();
8996 /* NOTREACHED */
9000 argc -= optind;
9001 argv += optind;
9003 #ifndef PROFILE
9004 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
9005 "unveil", NULL) == -1)
9006 err(1, "pledge");
9007 #endif
9008 if (list_stage && (pflag || patch_script_path))
9009 errx(1, "-l option cannot be used with other options");
9010 if (patch_script_path && !pflag)
9011 errx(1, "-F option can only be used together with -p option");
9013 cwd = getcwd(NULL, 0);
9014 if (cwd == NULL) {
9015 error = got_error_from_errno("getcwd");
9016 goto done;
9019 error = got_worktree_open(&worktree, cwd);
9020 if (error) {
9021 if (error->code == GOT_ERR_NOT_WORKTREE)
9022 error = wrap_not_worktree_error(error, "stage", cwd);
9023 goto done;
9026 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
9027 NULL);
9028 if (error != NULL)
9029 goto done;
9031 if (patch_script_path) {
9032 patch_script_file = fopen(patch_script_path, "r");
9033 if (patch_script_file == NULL) {
9034 error = got_error_from_errno2("fopen",
9035 patch_script_path);
9036 goto done;
9039 error = apply_unveil(got_repo_get_path(repo), 0,
9040 got_worktree_get_root_path(worktree));
9041 if (error)
9042 goto done;
9044 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
9045 if (error)
9046 goto done;
9048 if (list_stage)
9049 error = got_worktree_status(worktree, &paths, repo,
9050 print_stage, NULL, check_cancelled, NULL);
9051 else {
9052 cpa.patch_script_file = patch_script_file;
9053 cpa.action = "stage";
9054 error = got_worktree_stage(worktree, &paths,
9055 pflag ? NULL : print_status, NULL,
9056 pflag ? choose_patch : NULL, &cpa,
9057 allow_bad_symlinks, repo);
9059 done:
9060 if (patch_script_file && fclose(patch_script_file) == EOF &&
9061 error == NULL)
9062 error = got_error_from_errno2("fclose", patch_script_path);
9063 if (repo)
9064 got_repo_close(repo);
9065 if (worktree)
9066 got_worktree_close(worktree);
9067 TAILQ_FOREACH(pe, &paths, entry)
9068 free((char *)pe->path);
9069 got_pathlist_free(&paths);
9070 free(cwd);
9071 return error;
9074 __dead static void
9075 usage_unstage(void)
9077 fprintf(stderr, "usage: %s unstage [-p] [-F response-script] "
9078 "[file-path ...]\n",
9079 getprogname());
9080 exit(1);
9084 static const struct got_error *
9085 cmd_unstage(int argc, char *argv[])
9087 const struct got_error *error = NULL;
9088 struct got_repository *repo = NULL;
9089 struct got_worktree *worktree = NULL;
9090 char *cwd = NULL;
9091 struct got_pathlist_head paths;
9092 struct got_pathlist_entry *pe;
9093 int ch, pflag = 0;
9094 struct got_update_progress_arg upa;
9095 FILE *patch_script_file = NULL;
9096 const char *patch_script_path = NULL;
9097 struct choose_patch_arg cpa;
9099 TAILQ_INIT(&paths);
9101 while ((ch = getopt(argc, argv, "pF:")) != -1) {
9102 switch (ch) {
9103 case 'p':
9104 pflag = 1;
9105 break;
9106 case 'F':
9107 patch_script_path = optarg;
9108 break;
9109 default:
9110 usage_unstage();
9111 /* NOTREACHED */
9115 argc -= optind;
9116 argv += optind;
9118 #ifndef PROFILE
9119 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
9120 "unveil", NULL) == -1)
9121 err(1, "pledge");
9122 #endif
9123 if (patch_script_path && !pflag)
9124 errx(1, "-F option can only be used together with -p option");
9126 cwd = getcwd(NULL, 0);
9127 if (cwd == NULL) {
9128 error = got_error_from_errno("getcwd");
9129 goto done;
9132 error = got_worktree_open(&worktree, cwd);
9133 if (error) {
9134 if (error->code == GOT_ERR_NOT_WORKTREE)
9135 error = wrap_not_worktree_error(error, "unstage", cwd);
9136 goto done;
9139 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
9140 NULL);
9141 if (error != NULL)
9142 goto done;
9144 if (patch_script_path) {
9145 patch_script_file = fopen(patch_script_path, "r");
9146 if (patch_script_file == NULL) {
9147 error = got_error_from_errno2("fopen",
9148 patch_script_path);
9149 goto done;
9153 error = apply_unveil(got_repo_get_path(repo), 0,
9154 got_worktree_get_root_path(worktree));
9155 if (error)
9156 goto done;
9158 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
9159 if (error)
9160 goto done;
9162 cpa.patch_script_file = patch_script_file;
9163 cpa.action = "unstage";
9164 memset(&upa, 0, sizeof(upa));
9165 error = got_worktree_unstage(worktree, &paths, update_progress,
9166 &upa, pflag ? choose_patch : NULL, &cpa, repo);
9167 if (!error)
9168 print_update_progress_stats(&upa);
9169 done:
9170 if (patch_script_file && fclose(patch_script_file) == EOF &&
9171 error == NULL)
9172 error = got_error_from_errno2("fclose", patch_script_path);
9173 if (repo)
9174 got_repo_close(repo);
9175 if (worktree)
9176 got_worktree_close(worktree);
9177 TAILQ_FOREACH(pe, &paths, entry)
9178 free((char *)pe->path);
9179 got_pathlist_free(&paths);
9180 free(cwd);
9181 return error;
9184 __dead static void
9185 usage_cat(void)
9187 fprintf(stderr, "usage: %s cat [-r repository ] [ -c commit ] [ -P ] "
9188 "arg1 [arg2 ...]\n", getprogname());
9189 exit(1);
9192 static const struct got_error *
9193 cat_blob(struct got_object_id *id, struct got_repository *repo, FILE *outfile)
9195 const struct got_error *err;
9196 struct got_blob_object *blob;
9198 err = got_object_open_as_blob(&blob, repo, id, 8192);
9199 if (err)
9200 return err;
9202 err = got_object_blob_dump_to_file(NULL, NULL, NULL, outfile, blob);
9203 got_object_blob_close(blob);
9204 return err;
9207 static const struct got_error *
9208 cat_tree(struct got_object_id *id, struct got_repository *repo, FILE *outfile)
9210 const struct got_error *err;
9211 struct got_tree_object *tree;
9212 int nentries, i;
9214 err = got_object_open_as_tree(&tree, repo, id);
9215 if (err)
9216 return err;
9218 nentries = got_object_tree_get_nentries(tree);
9219 for (i = 0; i < nentries; i++) {
9220 struct got_tree_entry *te;
9221 char *id_str;
9222 if (sigint_received || sigpipe_received)
9223 break;
9224 te = got_object_tree_get_entry(tree, i);
9225 err = got_object_id_str(&id_str, got_tree_entry_get_id(te));
9226 if (err)
9227 break;
9228 fprintf(outfile, "%s %.7o %s\n", id_str,
9229 got_tree_entry_get_mode(te),
9230 got_tree_entry_get_name(te));
9231 free(id_str);
9234 got_object_tree_close(tree);
9235 return err;
9238 static const struct got_error *
9239 cat_commit(struct got_object_id *id, struct got_repository *repo, FILE *outfile)
9241 const struct got_error *err;
9242 struct got_commit_object *commit;
9243 const struct got_object_id_queue *parent_ids;
9244 struct got_object_qid *pid;
9245 char *id_str = NULL;
9246 const char *logmsg = NULL;
9248 err = got_object_open_as_commit(&commit, repo, id);
9249 if (err)
9250 return err;
9252 err = got_object_id_str(&id_str, got_object_commit_get_tree_id(commit));
9253 if (err)
9254 goto done;
9256 fprintf(outfile, "%s%s\n", GOT_COMMIT_LABEL_TREE, id_str);
9257 parent_ids = got_object_commit_get_parent_ids(commit);
9258 fprintf(outfile, "numparents %d\n",
9259 got_object_commit_get_nparents(commit));
9260 SIMPLEQ_FOREACH(pid, parent_ids, entry) {
9261 char *pid_str;
9262 err = got_object_id_str(&pid_str, pid->id);
9263 if (err)
9264 goto done;
9265 fprintf(outfile, "%s%s\n", GOT_COMMIT_LABEL_PARENT, pid_str);
9266 free(pid_str);
9268 fprintf(outfile, "%s%s %lld +0000\n", GOT_COMMIT_LABEL_AUTHOR,
9269 got_object_commit_get_author(commit),
9270 got_object_commit_get_author_time(commit));
9272 fprintf(outfile, "%s%s %lld +0000\n", GOT_COMMIT_LABEL_COMMITTER,
9273 got_object_commit_get_author(commit),
9274 got_object_commit_get_committer_time(commit));
9276 logmsg = got_object_commit_get_logmsg_raw(commit);
9277 fprintf(outfile, "messagelen %zd\n", strlen(logmsg));
9278 fprintf(outfile, "%s", logmsg);
9279 done:
9280 free(id_str);
9281 got_object_commit_close(commit);
9282 return err;
9285 static const struct got_error *
9286 cat_tag(struct got_object_id *id, struct got_repository *repo, FILE *outfile)
9288 const struct got_error *err;
9289 struct got_tag_object *tag;
9290 char *id_str = NULL;
9291 const char *tagmsg = NULL;
9293 err = got_object_open_as_tag(&tag, repo, id);
9294 if (err)
9295 return err;
9297 err = got_object_id_str(&id_str, got_object_tag_get_object_id(tag));
9298 if (err)
9299 goto done;
9301 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_OBJECT, id_str);
9303 switch (got_object_tag_get_object_type(tag)) {
9304 case GOT_OBJ_TYPE_BLOB:
9305 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TYPE,
9306 GOT_OBJ_LABEL_BLOB);
9307 break;
9308 case GOT_OBJ_TYPE_TREE:
9309 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TYPE,
9310 GOT_OBJ_LABEL_TREE);
9311 break;
9312 case GOT_OBJ_TYPE_COMMIT:
9313 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TYPE,
9314 GOT_OBJ_LABEL_COMMIT);
9315 break;
9316 case GOT_OBJ_TYPE_TAG:
9317 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TYPE,
9318 GOT_OBJ_LABEL_TAG);
9319 break;
9320 default:
9321 break;
9324 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TAG,
9325 got_object_tag_get_name(tag));
9327 fprintf(outfile, "%s%s %lld +0000\n", GOT_TAG_LABEL_TAGGER,
9328 got_object_tag_get_tagger(tag),
9329 got_object_tag_get_tagger_time(tag));
9331 tagmsg = got_object_tag_get_message(tag);
9332 fprintf(outfile, "messagelen %zd\n", strlen(tagmsg));
9333 fprintf(outfile, "%s", tagmsg);
9334 done:
9335 free(id_str);
9336 got_object_tag_close(tag);
9337 return err;
9340 static const struct got_error *
9341 cmd_cat(int argc, char *argv[])
9343 const struct got_error *error;
9344 struct got_repository *repo = NULL;
9345 struct got_worktree *worktree = NULL;
9346 char *cwd = NULL, *repo_path = NULL, *label = NULL;
9347 const char *commit_id_str = NULL;
9348 struct got_object_id *id = NULL, *commit_id = NULL;
9349 int ch, obj_type, i, force_path = 0;
9351 #ifndef PROFILE
9352 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
9353 NULL) == -1)
9354 err(1, "pledge");
9355 #endif
9357 while ((ch = getopt(argc, argv, "c:r:P")) != -1) {
9358 switch (ch) {
9359 case 'c':
9360 commit_id_str = optarg;
9361 break;
9362 case 'r':
9363 repo_path = realpath(optarg, NULL);
9364 if (repo_path == NULL)
9365 return got_error_from_errno2("realpath",
9366 optarg);
9367 got_path_strip_trailing_slashes(repo_path);
9368 break;
9369 case 'P':
9370 force_path = 1;
9371 break;
9372 default:
9373 usage_cat();
9374 /* NOTREACHED */
9378 argc -= optind;
9379 argv += optind;
9381 cwd = getcwd(NULL, 0);
9382 if (cwd == NULL) {
9383 error = got_error_from_errno("getcwd");
9384 goto done;
9386 error = got_worktree_open(&worktree, cwd);
9387 if (error && error->code != GOT_ERR_NOT_WORKTREE)
9388 goto done;
9389 if (worktree) {
9390 if (repo_path == NULL) {
9391 repo_path = strdup(
9392 got_worktree_get_repo_path(worktree));
9393 if (repo_path == NULL) {
9394 error = got_error_from_errno("strdup");
9395 goto done;
9400 if (repo_path == NULL) {
9401 repo_path = getcwd(NULL, 0);
9402 if (repo_path == NULL)
9403 return got_error_from_errno("getcwd");
9406 error = got_repo_open(&repo, repo_path, NULL);
9407 free(repo_path);
9408 if (error != NULL)
9409 goto done;
9411 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
9412 if (error)
9413 goto done;
9415 if (commit_id_str == NULL)
9416 commit_id_str = GOT_REF_HEAD;
9417 error = got_repo_match_object_id(&commit_id, NULL,
9418 commit_id_str, GOT_OBJ_TYPE_COMMIT, 1, repo);
9419 if (error)
9420 goto done;
9422 for (i = 0; i < argc; i++) {
9423 if (force_path) {
9424 error = got_object_id_by_path(&id, repo, commit_id,
9425 argv[i]);
9426 if (error)
9427 break;
9428 } else {
9429 error = got_repo_match_object_id(&id, &label, argv[i],
9430 GOT_OBJ_TYPE_ANY, 0, repo);
9431 if (error) {
9432 if (error->code != GOT_ERR_BAD_OBJ_ID_STR &&
9433 error->code != GOT_ERR_NOT_REF)
9434 break;
9435 error = got_object_id_by_path(&id, repo,
9436 commit_id, argv[i]);
9437 if (error)
9438 break;
9442 error = got_object_get_type(&obj_type, repo, id);
9443 if (error)
9444 break;
9446 switch (obj_type) {
9447 case GOT_OBJ_TYPE_BLOB:
9448 error = cat_blob(id, repo, stdout);
9449 break;
9450 case GOT_OBJ_TYPE_TREE:
9451 error = cat_tree(id, repo, stdout);
9452 break;
9453 case GOT_OBJ_TYPE_COMMIT:
9454 error = cat_commit(id, repo, stdout);
9455 break;
9456 case GOT_OBJ_TYPE_TAG:
9457 error = cat_tag(id, repo, stdout);
9458 break;
9459 default:
9460 error = got_error(GOT_ERR_OBJ_TYPE);
9461 break;
9463 if (error)
9464 break;
9465 free(label);
9466 label = NULL;
9467 free(id);
9468 id = NULL;
9470 done:
9471 free(label);
9472 free(id);
9473 free(commit_id);
9474 if (worktree)
9475 got_worktree_close(worktree);
9476 if (repo) {
9477 const struct got_error *repo_error;
9478 repo_error = got_repo_close(repo);
9479 if (error == NULL)
9480 error = repo_error;
9482 return error;
9485 __dead static void
9486 usage_info(void)
9488 fprintf(stderr, "usage: %s info [path ...]\n",
9489 getprogname());
9490 exit(1);
9493 static const struct got_error *
9494 print_path_info(void *arg, const char *path, mode_t mode, time_t mtime,
9495 struct got_object_id *blob_id, struct got_object_id *staged_blob_id,
9496 struct got_object_id *commit_id)
9498 const struct got_error *err = NULL;
9499 char *id_str = NULL;
9500 char datebuf[128];
9501 struct tm mytm, *tm;
9502 struct got_pathlist_head *paths = arg;
9503 struct got_pathlist_entry *pe;
9506 * Clear error indication from any of the path arguments which
9507 * would cause this file index entry to be displayed.
9509 TAILQ_FOREACH(pe, paths, entry) {
9510 if (got_path_cmp(path, pe->path, strlen(path),
9511 pe->path_len) == 0 ||
9512 got_path_is_child(path, pe->path, pe->path_len))
9513 pe->data = NULL; /* no error */
9516 printf(GOT_COMMIT_SEP_STR);
9517 if (S_ISLNK(mode))
9518 printf("symlink: %s\n", path);
9519 else if (S_ISREG(mode)) {
9520 printf("file: %s\n", path);
9521 printf("mode: %o\n", mode & (S_IRWXU | S_IRWXG | S_IRWXO));
9522 } else if (S_ISDIR(mode))
9523 printf("directory: %s\n", path);
9524 else
9525 printf("something: %s\n", path);
9527 tm = localtime_r(&mtime, &mytm);
9528 if (tm == NULL)
9529 return NULL;
9530 if (strftime(datebuf, sizeof(datebuf), "%c %Z", tm) >= sizeof(datebuf))
9531 return got_error(GOT_ERR_NO_SPACE);
9532 printf("timestamp: %s\n", datebuf);
9534 if (blob_id) {
9535 err = got_object_id_str(&id_str, blob_id);
9536 if (err)
9537 return err;
9538 printf("based on blob: %s\n", id_str);
9539 free(id_str);
9542 if (staged_blob_id) {
9543 err = got_object_id_str(&id_str, staged_blob_id);
9544 if (err)
9545 return err;
9546 printf("based on staged blob: %s\n", id_str);
9547 free(id_str);
9550 if (commit_id) {
9551 err = got_object_id_str(&id_str, commit_id);
9552 if (err)
9553 return err;
9554 printf("based on commit: %s\n", id_str);
9555 free(id_str);
9558 return NULL;
9561 static const struct got_error *
9562 cmd_info(int argc, char *argv[])
9564 const struct got_error *error = NULL;
9565 struct got_worktree *worktree = NULL;
9566 char *cwd = NULL, *id_str = NULL;
9567 struct got_pathlist_head paths;
9568 struct got_pathlist_entry *pe;
9569 char *uuidstr = NULL;
9570 int ch, show_files = 0;
9572 TAILQ_INIT(&paths);
9574 while ((ch = getopt(argc, argv, "")) != -1) {
9575 switch (ch) {
9576 default:
9577 usage_info();
9578 /* NOTREACHED */
9582 argc -= optind;
9583 argv += optind;
9585 #ifndef PROFILE
9586 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
9587 NULL) == -1)
9588 err(1, "pledge");
9589 #endif
9590 cwd = getcwd(NULL, 0);
9591 if (cwd == NULL) {
9592 error = got_error_from_errno("getcwd");
9593 goto done;
9596 error = got_worktree_open(&worktree, cwd);
9597 if (error) {
9598 if (error->code == GOT_ERR_NOT_WORKTREE)
9599 error = wrap_not_worktree_error(error, "status", cwd);
9600 goto done;
9603 error = apply_unveil(NULL, 0, got_worktree_get_root_path(worktree));
9604 if (error)
9605 goto done;
9607 if (argc >= 1) {
9608 error = get_worktree_paths_from_argv(&paths, argc, argv,
9609 worktree);
9610 if (error)
9611 goto done;
9612 show_files = 1;
9615 error = got_object_id_str(&id_str,
9616 got_worktree_get_base_commit_id(worktree));
9617 if (error)
9618 goto done;
9620 error = got_worktree_get_uuid(&uuidstr, worktree);
9621 if (error)
9622 goto done;
9624 printf("work tree: %s\n", got_worktree_get_root_path(worktree));
9625 printf("work tree base commit: %s\n", id_str);
9626 printf("work tree path prefix: %s\n",
9627 got_worktree_get_path_prefix(worktree));
9628 printf("work tree branch reference: %s\n",
9629 got_worktree_get_head_ref_name(worktree));
9630 printf("work tree UUID: %s\n", uuidstr);
9631 printf("repository: %s\n", got_worktree_get_repo_path(worktree));
9633 if (show_files) {
9634 struct got_pathlist_entry *pe;
9635 TAILQ_FOREACH(pe, &paths, entry) {
9636 if (pe->path_len == 0)
9637 continue;
9639 * Assume this path will fail. This will be corrected
9640 * in print_path_info() in case the path does suceeed.
9642 pe->data = (void *)got_error_path(pe->path,
9643 GOT_ERR_BAD_PATH);
9645 error = got_worktree_path_info(worktree, &paths,
9646 print_path_info, &paths, check_cancelled, NULL);
9647 if (error)
9648 goto done;
9649 TAILQ_FOREACH(pe, &paths, entry) {
9650 if (pe->data != NULL) {
9651 error = pe->data; /* bad path */
9652 break;
9656 done:
9657 TAILQ_FOREACH(pe, &paths, entry)
9658 free((char *)pe->path);
9659 got_pathlist_free(&paths);
9660 free(cwd);
9661 free(id_str);
9662 free(uuidstr);
9663 return error;