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"
58 #ifndef nitems
59 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
60 #endif
62 static volatile sig_atomic_t sigint_received;
63 static volatile sig_atomic_t sigpipe_received;
65 static void
66 catch_sigint(int signo)
67 {
68 sigint_received = 1;
69 }
71 static void
72 catch_sigpipe(int signo)
73 {
74 sigpipe_received = 1;
75 }
78 struct got_cmd {
79 const char *cmd_name;
80 const struct got_error *(*cmd_main)(int, char *[]);
81 void (*cmd_usage)(void);
82 const char *cmd_alias;
83 };
85 __dead static void usage(int);
86 __dead static void usage_init(void);
87 __dead static void usage_import(void);
88 __dead static void usage_checkout(void);
89 __dead static void usage_clone(void);
90 __dead static void usage_update(void);
91 __dead static void usage_log(void);
92 __dead static void usage_diff(void);
93 __dead static void usage_blame(void);
94 __dead static void usage_tree(void);
95 __dead static void usage_status(void);
96 __dead static void usage_ref(void);
97 __dead static void usage_branch(void);
98 __dead static void usage_tag(void);
99 __dead static void usage_add(void);
100 __dead static void usage_remove(void);
101 __dead static void usage_revert(void);
102 __dead static void usage_commit(void);
103 __dead static void usage_cherrypick(void);
104 __dead static void usage_backout(void);
105 __dead static void usage_rebase(void);
106 __dead static void usage_histedit(void);
107 __dead static void usage_integrate(void);
108 __dead static void usage_stage(void);
109 __dead static void usage_unstage(void);
110 __dead static void usage_cat(void);
112 static const struct got_error* cmd_init(int, char *[]);
113 static const struct got_error* cmd_import(int, char *[]);
114 static const struct got_error* cmd_clone(int, char *[]);
115 static const struct got_error* cmd_checkout(int, char *[]);
116 static const struct got_error* cmd_update(int, char *[]);
117 static const struct got_error* cmd_log(int, char *[]);
118 static const struct got_error* cmd_diff(int, char *[]);
119 static const struct got_error* cmd_blame(int, char *[]);
120 static const struct got_error* cmd_tree(int, char *[]);
121 static const struct got_error* cmd_status(int, char *[]);
122 static const struct got_error* cmd_ref(int, char *[]);
123 static const struct got_error* cmd_branch(int, char *[]);
124 static const struct got_error* cmd_tag(int, char *[]);
125 static const struct got_error* cmd_add(int, char *[]);
126 static const struct got_error* cmd_remove(int, char *[]);
127 static const struct got_error* cmd_revert(int, char *[]);
128 static const struct got_error* cmd_commit(int, char *[]);
129 static const struct got_error* cmd_cherrypick(int, char *[]);
130 static const struct got_error* cmd_backout(int, char *[]);
131 static const struct got_error* cmd_rebase(int, char *[]);
132 static const struct got_error* cmd_histedit(int, char *[]);
133 static const struct got_error* cmd_integrate(int, char *[]);
134 static const struct got_error* cmd_stage(int, char *[]);
135 static const struct got_error* cmd_unstage(int, char *[]);
136 static const struct got_error* cmd_cat(int, char *[]);
138 static struct got_cmd got_commands[] = {
139 { "init", cmd_init, usage_init, "in" },
140 { "import", cmd_import, usage_import, "im" },
141 { "checkout", cmd_checkout, usage_checkout, "co" },
142 { "clone", cmd_clone, usage_clone, "cl" },
143 { "update", cmd_update, usage_update, "up" },
144 { "log", cmd_log, usage_log, "" },
145 { "diff", cmd_diff, usage_diff, "di" },
146 { "blame", cmd_blame, usage_blame, "bl" },
147 { "tree", cmd_tree, usage_tree, "tr" },
148 { "status", cmd_status, usage_status, "st" },
149 { "ref", cmd_ref, usage_ref, "" },
150 { "branch", cmd_branch, usage_branch, "br" },
151 { "tag", cmd_tag, usage_tag, "" },
152 { "add", cmd_add, usage_add, "" },
153 { "remove", cmd_remove, usage_remove, "rm" },
154 { "revert", cmd_revert, usage_revert, "rv" },
155 { "commit", cmd_commit, usage_commit, "ci" },
156 { "cherrypick", cmd_cherrypick, usage_cherrypick, "cy" },
157 { "backout", cmd_backout, usage_backout, "bo" },
158 { "rebase", cmd_rebase, usage_rebase, "rb" },
159 { "histedit", cmd_histedit, usage_histedit, "he" },
160 { "integrate", cmd_integrate, usage_integrate,"ig" },
161 { "stage", cmd_stage, usage_stage, "sg" },
162 { "unstage", cmd_unstage, usage_unstage, "ug" },
163 { "cat", cmd_cat, usage_cat, "" },
164 };
166 static void
167 list_commands(void)
169 int i;
171 fprintf(stderr, "commands:");
172 for (i = 0; i < nitems(got_commands); i++) {
173 struct got_cmd *cmd = &got_commands[i];
174 fprintf(stderr, " %s", cmd->cmd_name);
176 fputc('\n', stderr);
179 int
180 main(int argc, char *argv[])
182 struct got_cmd *cmd;
183 unsigned int i;
184 int ch;
185 int hflag = 0, Vflag = 0;
186 static struct option longopts[] = {
187 { "version", no_argument, NULL, 'V' },
188 { NULL, 0, NULL, 0}
189 };
191 setlocale(LC_CTYPE, "");
193 while ((ch = getopt_long(argc, argv, "+hV", longopts, NULL)) != -1) {
194 switch (ch) {
195 case 'h':
196 hflag = 1;
197 break;
198 case 'V':
199 Vflag = 1;
200 break;
201 default:
202 usage(hflag);
203 /* NOTREACHED */
207 argc -= optind;
208 argv += optind;
209 optind = 0;
211 if (Vflag) {
212 got_version_print_str();
213 return 1;
216 if (argc <= 0)
217 usage(hflag);
219 signal(SIGINT, catch_sigint);
220 signal(SIGPIPE, catch_sigpipe);
222 for (i = 0; i < nitems(got_commands); i++) {
223 const struct got_error *error;
225 cmd = &got_commands[i];
227 if (strcmp(cmd->cmd_name, argv[0]) != 0 &&
228 strcmp(cmd->cmd_alias, argv[0]) != 0)
229 continue;
231 if (hflag)
232 got_commands[i].cmd_usage();
234 error = got_commands[i].cmd_main(argc, argv);
235 if (error && error->code != GOT_ERR_CANCELLED &&
236 error->code != GOT_ERR_PRIVSEP_EXIT &&
237 !(sigpipe_received &&
238 error->code == GOT_ERR_ERRNO && errno == EPIPE) &&
239 !(sigint_received &&
240 error->code == GOT_ERR_ERRNO && errno == EINTR)) {
241 fprintf(stderr, "%s: %s\n", getprogname(), error->msg);
242 return 1;
245 return 0;
248 fprintf(stderr, "%s: unknown command '%s'\n", getprogname(), argv[0]);
249 list_commands();
250 return 1;
253 __dead static void
254 usage(int hflag)
256 fprintf(stderr, "usage: %s [-h] [-V | --version] command [arg ...]\n",
257 getprogname());
258 if (hflag)
259 list_commands();
260 exit(1);
263 static const struct got_error *
264 get_editor(char **abspath)
266 const struct got_error *err = NULL;
267 const char *editor;
269 *abspath = NULL;
271 editor = getenv("VISUAL");
272 if (editor == NULL)
273 editor = getenv("EDITOR");
275 if (editor) {
276 err = got_path_find_prog(abspath, editor);
277 if (err)
278 return err;
281 if (*abspath == NULL) {
282 *abspath = strdup("/bin/ed");
283 if (*abspath == NULL)
284 return got_error_from_errno("strdup");
287 return NULL;
290 static const struct got_error *
291 apply_unveil(const char *repo_path, int repo_read_only,
292 const char *worktree_path)
294 const struct got_error *err;
296 #ifdef PROFILE
297 if (unveil("gmon.out", "rwc") != 0)
298 return got_error_from_errno2("unveil", "gmon.out");
299 #endif
300 if (repo_path && unveil(repo_path, repo_read_only ? "r" : "rwc") != 0)
301 return got_error_from_errno2("unveil", repo_path);
303 if (worktree_path && unveil(worktree_path, "rwc") != 0)
304 return got_error_from_errno2("unveil", worktree_path);
306 if (unveil(GOT_TMPDIR_STR, "rwc") != 0)
307 return got_error_from_errno2("unveil", GOT_TMPDIR_STR);
309 err = got_privsep_unveil_exec_helpers();
310 if (err != NULL)
311 return err;
313 if (unveil(NULL, NULL) != 0)
314 return got_error_from_errno("unveil");
316 return NULL;
319 __dead static void
320 usage_init(void)
322 fprintf(stderr, "usage: %s init repository-path\n", getprogname());
323 exit(1);
326 static const struct got_error *
327 cmd_init(int argc, char *argv[])
329 const struct got_error *error = NULL;
330 char *repo_path = NULL;
331 int ch;
333 while ((ch = getopt(argc, argv, "")) != -1) {
334 switch (ch) {
335 default:
336 usage_init();
337 /* NOTREACHED */
341 argc -= optind;
342 argv += optind;
344 #ifndef PROFILE
345 if (pledge("stdio rpath wpath cpath unveil", NULL) == -1)
346 err(1, "pledge");
347 #endif
348 if (argc != 1)
349 usage_init();
351 repo_path = strdup(argv[0]);
352 if (repo_path == NULL)
353 return got_error_from_errno("strdup");
355 got_path_strip_trailing_slashes(repo_path);
357 error = got_path_mkdir(repo_path);
358 if (error &&
359 !(error->code == GOT_ERR_ERRNO && errno == EEXIST))
360 goto done;
362 error = apply_unveil(repo_path, 0, NULL);
363 if (error)
364 goto done;
366 error = got_repo_init(repo_path);
367 done:
368 free(repo_path);
369 return error;
372 __dead static void
373 usage_import(void)
375 fprintf(stderr, "usage: %s import [-b branch] [-m message] "
376 "[-r repository-path] [-I pattern] path\n", getprogname());
377 exit(1);
380 int
381 spawn_editor(const char *editor, const char *file)
383 pid_t pid;
384 sig_t sighup, sigint, sigquit;
385 int st = -1;
387 sighup = signal(SIGHUP, SIG_IGN);
388 sigint = signal(SIGINT, SIG_IGN);
389 sigquit = signal(SIGQUIT, SIG_IGN);
391 switch (pid = fork()) {
392 case -1:
393 goto doneediting;
394 case 0:
395 execl(editor, editor, file, (char *)NULL);
396 _exit(127);
399 while (waitpid(pid, &st, 0) == -1)
400 if (errno != EINTR)
401 break;
403 doneediting:
404 (void)signal(SIGHUP, sighup);
405 (void)signal(SIGINT, sigint);
406 (void)signal(SIGQUIT, sigquit);
408 if (!WIFEXITED(st)) {
409 errno = EINTR;
410 return -1;
413 return WEXITSTATUS(st);
416 static const struct got_error *
417 edit_logmsg(char **logmsg, const char *editor, const char *logmsg_path,
418 const char *initial_content)
420 const struct got_error *err = NULL;
421 char buf[1024];
422 struct stat st, st2;
423 FILE *fp;
424 int content_changed = 0;
425 size_t len;
427 *logmsg = NULL;
429 if (stat(logmsg_path, &st) == -1)
430 return got_error_from_errno2("stat", logmsg_path);
432 if (spawn_editor(editor, logmsg_path) == -1)
433 return got_error_from_errno("failed spawning editor");
435 if (stat(logmsg_path, &st2) == -1)
436 return got_error_from_errno("stat");
438 if (st.st_mtime == st2.st_mtime && st.st_size == st2.st_size)
439 return got_error_msg(GOT_ERR_COMMIT_MSG_EMPTY,
440 "no changes made to commit message, aborting");
442 *logmsg = malloc(st2.st_size + 1);
443 if (*logmsg == NULL)
444 return got_error_from_errno("malloc");
445 (*logmsg)[0] = '\0';
446 len = 0;
448 fp = fopen(logmsg_path, "r");
449 if (fp == NULL) {
450 err = got_error_from_errno("fopen");
451 goto done;
453 while (fgets(buf, sizeof(buf), fp) != NULL) {
454 if (!content_changed && strcmp(buf, initial_content) != 0)
455 content_changed = 1;
456 if (buf[0] == '#' || (len == 0 && buf[0] == '\n'))
457 continue; /* remove comments and leading empty lines */
458 len = strlcat(*logmsg, buf, st2.st_size);
460 fclose(fp);
462 while (len > 0 && (*logmsg)[len - 1] == '\n') {
463 (*logmsg)[len - 1] = '\0';
464 len--;
467 if (len == 0 || !content_changed)
468 err = got_error_msg(GOT_ERR_COMMIT_MSG_EMPTY,
469 "commit message cannot be empty, aborting");
470 done:
471 if (err) {
472 free(*logmsg);
473 *logmsg = NULL;
475 return err;
478 static const struct got_error *
479 collect_import_msg(char **logmsg, char **logmsg_path, const char *editor,
480 const char *path_dir, const char *branch_name)
482 char *initial_content = NULL;
483 const struct got_error *err = NULL;
484 int fd;
486 if (asprintf(&initial_content,
487 "\n# %s to be imported to branch %s\n", path_dir,
488 branch_name) == -1)
489 return got_error_from_errno("asprintf");
491 err = got_opentemp_named_fd(logmsg_path, &fd,
492 GOT_TMPDIR_STR "/got-importmsg");
493 if (err)
494 goto done;
496 dprintf(fd, initial_content);
497 close(fd);
499 err = edit_logmsg(logmsg, editor, *logmsg_path, initial_content);
500 done:
501 free(initial_content);
502 return err;
505 static const struct got_error *
506 import_progress(void *arg, const char *path)
508 printf("A %s\n", path);
509 return NULL;
512 static const struct got_error *
513 get_author(char **author, struct got_repository *repo)
515 const struct got_error *err = NULL;
516 const char *got_author, *name, *email;
518 *author = NULL;
520 name = got_repo_get_gitconfig_author_name(repo);
521 email = got_repo_get_gitconfig_author_email(repo);
522 if (name && email) {
523 if (asprintf(author, "%s <%s>", name, email) == -1)
524 return got_error_from_errno("asprintf");
525 return NULL;
528 got_author = getenv("GOT_AUTHOR");
529 if (got_author == NULL) {
530 name = got_repo_get_global_gitconfig_author_name(repo);
531 email = got_repo_get_global_gitconfig_author_email(repo);
532 if (name && email) {
533 if (asprintf(author, "%s <%s>", name, email) == -1)
534 return got_error_from_errno("asprintf");
535 return NULL;
537 /* TODO: Look up user in password database? */
538 return got_error(GOT_ERR_COMMIT_NO_AUTHOR);
541 *author = strdup(got_author);
542 if (*author == NULL)
543 return got_error_from_errno("strdup");
545 /*
546 * Really dumb email address check; we're only doing this to
547 * avoid git's object parser breaking on commits we create.
548 */
549 while (*got_author && *got_author != '<')
550 got_author++;
551 if (*got_author != '<') {
552 err = got_error(GOT_ERR_COMMIT_NO_EMAIL);
553 goto done;
555 while (*got_author && *got_author != '@')
556 got_author++;
557 if (*got_author != '@') {
558 err = got_error(GOT_ERR_COMMIT_NO_EMAIL);
559 goto done;
561 while (*got_author && *got_author != '>')
562 got_author++;
563 if (*got_author != '>')
564 err = got_error(GOT_ERR_COMMIT_NO_EMAIL);
565 done:
566 if (err) {
567 free(*author);
568 *author = NULL;
570 return err;
573 static const struct got_error *
574 get_gitconfig_path(char **gitconfig_path)
576 const char *homedir = getenv("HOME");
578 *gitconfig_path = NULL;
579 if (homedir) {
580 if (asprintf(gitconfig_path, "%s/.gitconfig", homedir) == -1)
581 return got_error_from_errno("asprintf");
584 return NULL;
587 static const struct got_error *
588 cmd_import(int argc, char *argv[])
590 const struct got_error *error = NULL;
591 char *path_dir = NULL, *repo_path = NULL, *logmsg = NULL;
592 char *gitconfig_path = NULL, *editor = NULL, *author = NULL;
593 const char *branch_name = "main";
594 char *refname = NULL, *id_str = NULL, *logmsg_path = NULL;
595 struct got_repository *repo = NULL;
596 struct got_reference *branch_ref = NULL, *head_ref = NULL;
597 struct got_object_id *new_commit_id = NULL;
598 int ch;
599 struct got_pathlist_head ignores;
600 struct got_pathlist_entry *pe;
601 int preserve_logmsg = 0;
603 TAILQ_INIT(&ignores);
605 while ((ch = getopt(argc, argv, "b:m:r:I:")) != -1) {
606 switch (ch) {
607 case 'b':
608 branch_name = optarg;
609 break;
610 case 'm':
611 logmsg = strdup(optarg);
612 if (logmsg == NULL) {
613 error = got_error_from_errno("strdup");
614 goto done;
616 break;
617 case 'r':
618 repo_path = realpath(optarg, NULL);
619 if (repo_path == NULL) {
620 error = got_error_from_errno2("realpath",
621 optarg);
622 goto done;
624 break;
625 case 'I':
626 if (optarg[0] == '\0')
627 break;
628 error = got_pathlist_insert(&pe, &ignores, optarg,
629 NULL);
630 if (error)
631 goto done;
632 break;
633 default:
634 usage_import();
635 /* NOTREACHED */
639 argc -= optind;
640 argv += optind;
642 #ifndef PROFILE
643 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
644 "unveil",
645 NULL) == -1)
646 err(1, "pledge");
647 #endif
648 if (argc != 1)
649 usage_import();
651 if (repo_path == NULL) {
652 repo_path = getcwd(NULL, 0);
653 if (repo_path == NULL)
654 return got_error_from_errno("getcwd");
656 got_path_strip_trailing_slashes(repo_path);
657 error = get_gitconfig_path(&gitconfig_path);
658 if (error)
659 goto done;
660 error = got_repo_open(&repo, repo_path, gitconfig_path);
661 if (error)
662 goto done;
664 error = get_author(&author, repo);
665 if (error)
666 return error;
668 /*
669 * Don't let the user create a branch name with a leading '-'.
670 * While technically a valid reference name, this case is usually
671 * an unintended typo.
672 */
673 if (branch_name[0] == '-')
674 return got_error_path(branch_name, GOT_ERR_REF_NAME_MINUS);
676 if (asprintf(&refname, "refs/heads/%s", branch_name) == -1) {
677 error = got_error_from_errno("asprintf");
678 goto done;
681 error = got_ref_open(&branch_ref, repo, refname, 0);
682 if (error) {
683 if (error->code != GOT_ERR_NOT_REF)
684 goto done;
685 } else {
686 error = got_error_msg(GOT_ERR_BRANCH_EXISTS,
687 "import target branch already exists");
688 goto done;
691 path_dir = realpath(argv[0], NULL);
692 if (path_dir == NULL) {
693 error = got_error_from_errno2("realpath", argv[0]);
694 goto done;
696 got_path_strip_trailing_slashes(path_dir);
698 /*
699 * unveil(2) traverses exec(2); if an editor is used we have
700 * to apply unveil after the log message has been written.
701 */
702 if (logmsg == NULL || strlen(logmsg) == 0) {
703 error = get_editor(&editor);
704 if (error)
705 goto done;
706 free(logmsg);
707 error = collect_import_msg(&logmsg, &logmsg_path, editor,
708 path_dir, refname);
709 if (error) {
710 if (error->code != GOT_ERR_COMMIT_MSG_EMPTY &&
711 logmsg_path != NULL)
712 preserve_logmsg = 1;
713 goto done;
717 if (unveil(path_dir, "r") != 0) {
718 error = got_error_from_errno2("unveil", path_dir);
719 if (logmsg_path)
720 preserve_logmsg = 1;
721 goto done;
724 error = apply_unveil(got_repo_get_path(repo), 0, NULL);
725 if (error) {
726 if (logmsg_path)
727 preserve_logmsg = 1;
728 goto done;
731 error = got_repo_import(&new_commit_id, path_dir, logmsg,
732 author, &ignores, repo, import_progress, NULL);
733 if (error) {
734 if (logmsg_path)
735 preserve_logmsg = 1;
736 goto done;
739 error = got_ref_alloc(&branch_ref, refname, new_commit_id);
740 if (error) {
741 if (logmsg_path)
742 preserve_logmsg = 1;
743 goto done;
746 error = got_ref_write(branch_ref, repo);
747 if (error) {
748 if (logmsg_path)
749 preserve_logmsg = 1;
750 goto done;
753 error = got_object_id_str(&id_str, new_commit_id);
754 if (error) {
755 if (logmsg_path)
756 preserve_logmsg = 1;
757 goto done;
760 error = got_ref_open(&head_ref, repo, GOT_REF_HEAD, 0);
761 if (error) {
762 if (error->code != GOT_ERR_NOT_REF) {
763 if (logmsg_path)
764 preserve_logmsg = 1;
765 goto done;
768 error = got_ref_alloc_symref(&head_ref, GOT_REF_HEAD,
769 branch_ref);
770 if (error) {
771 if (logmsg_path)
772 preserve_logmsg = 1;
773 goto done;
776 error = got_ref_write(head_ref, repo);
777 if (error) {
778 if (logmsg_path)
779 preserve_logmsg = 1;
780 goto done;
784 printf("Created branch %s with commit %s\n",
785 got_ref_get_name(branch_ref), id_str);
786 done:
787 if (preserve_logmsg) {
788 fprintf(stderr, "%s: log message preserved in %s\n",
789 getprogname(), logmsg_path);
790 } else if (logmsg_path && unlink(logmsg_path) == -1 && error == NULL)
791 error = got_error_from_errno2("unlink", logmsg_path);
792 free(logmsg);
793 free(logmsg_path);
794 free(repo_path);
795 free(editor);
796 free(refname);
797 free(new_commit_id);
798 free(id_str);
799 free(author);
800 free(gitconfig_path);
801 if (branch_ref)
802 got_ref_close(branch_ref);
803 if (head_ref)
804 got_ref_close(head_ref);
805 return error;
808 __dead static void
809 usage_clone(void)
811 fprintf(stderr, "usage: %s clone repo-url\n", getprogname());
812 exit(1);
815 __dead static void
816 usage_checkout(void)
818 fprintf(stderr, "usage: %s checkout [-E] [-b branch] [-c commit] "
819 "[-p prefix] repository-path [worktree-path]\n", getprogname());
820 exit(1);
823 static void
824 show_worktree_base_ref_warning(void)
826 fprintf(stderr, "%s: warning: could not create a reference "
827 "to the work tree's base commit; the commit could be "
828 "garbage-collected by Git; making the repository "
829 "writable and running 'got update' will prevent this\n",
830 getprogname());
833 struct got_checkout_progress_arg {
834 const char *worktree_path;
835 int had_base_commit_ref_error;
836 };
838 static const struct got_error *
839 checkout_progress(void *arg, unsigned char status, const char *path)
841 struct got_checkout_progress_arg *a = arg;
843 /* Base commit bump happens silently. */
844 if (status == GOT_STATUS_BUMP_BASE)
845 return NULL;
847 if (status == GOT_STATUS_BASE_REF_ERR) {
848 a->had_base_commit_ref_error = 1;
849 return NULL;
852 while (path[0] == '/')
853 path++;
855 printf("%c %s/%s\n", status, a->worktree_path, path);
856 return NULL;
859 static const struct got_error *
860 check_cancelled(void *arg)
862 if (sigint_received || sigpipe_received)
863 return got_error(GOT_ERR_CANCELLED);
864 return NULL;
867 static const struct got_error *
868 check_linear_ancestry(struct got_object_id *commit_id,
869 struct got_object_id *base_commit_id, int allow_forwards_in_time_only,
870 struct got_repository *repo)
872 const struct got_error *err = NULL;
873 struct got_object_id *yca_id;
875 err = got_commit_graph_find_youngest_common_ancestor(&yca_id,
876 commit_id, base_commit_id, repo, check_cancelled, NULL);
877 if (err)
878 return err;
880 if (yca_id == NULL)
881 return got_error(GOT_ERR_ANCESTRY);
883 /*
884 * Require a straight line of history between the target commit
885 * and the work tree's base commit.
887 * Non-linear situations such as this require a rebase:
889 * (commit) D F (base_commit)
890 * \ /
891 * C E
892 * \ /
893 * B (yca)
894 * |
895 * A
897 * 'got update' only handles linear cases:
898 * Update forwards in time: A (base/yca) - B - C - D (commit)
899 * Update backwards in time: D (base) - C - B - A (commit/yca)
900 */
901 if (allow_forwards_in_time_only) {
902 if (got_object_id_cmp(base_commit_id, yca_id) != 0)
903 return got_error(GOT_ERR_ANCESTRY);
904 } else if (got_object_id_cmp(commit_id, yca_id) != 0 &&
905 got_object_id_cmp(base_commit_id, yca_id) != 0)
906 return got_error(GOT_ERR_ANCESTRY);
908 free(yca_id);
909 return NULL;
912 static const struct got_error *
913 check_same_branch(struct got_object_id *commit_id,
914 struct got_reference *head_ref, struct got_object_id *yca_id,
915 struct got_repository *repo)
917 const struct got_error *err = NULL;
918 struct got_commit_graph *graph = NULL;
919 struct got_object_id *head_commit_id = NULL;
920 int is_same_branch = 0;
922 err = got_ref_resolve(&head_commit_id, repo, head_ref);
923 if (err)
924 goto done;
926 if (got_object_id_cmp(head_commit_id, commit_id) == 0) {
927 is_same_branch = 1;
928 goto done;
930 if (yca_id && got_object_id_cmp(commit_id, yca_id) == 0) {
931 is_same_branch = 1;
932 goto done;
935 err = got_commit_graph_open(&graph, "/", 1);
936 if (err)
937 goto done;
939 err = got_commit_graph_iter_start(graph, head_commit_id, repo,
940 check_cancelled, NULL);
941 if (err)
942 goto done;
944 for (;;) {
945 struct got_object_id *id;
946 err = got_commit_graph_iter_next(&id, graph, repo,
947 check_cancelled, NULL);
948 if (err) {
949 if (err->code == GOT_ERR_ITER_COMPLETED)
950 err = NULL;
951 break;
954 if (id) {
955 if (yca_id && got_object_id_cmp(id, yca_id) == 0)
956 break;
957 if (got_object_id_cmp(id, commit_id) == 0) {
958 is_same_branch = 1;
959 break;
963 done:
964 if (graph)
965 got_commit_graph_close(graph);
966 free(head_commit_id);
967 if (!err && !is_same_branch)
968 err = got_error(GOT_ERR_ANCESTRY);
969 return err;
972 static const struct got_error *
973 fetch_progress(void *arg, const char *message, off_t packfile_size,
974 int nobj_total, int nobj_indexed, int nobj_loose, int nobj_resolved)
976 int p;
977 char scaled[FMT_SCALED_STRSIZE];
979 if (message && message[0] != '\0' && message[0] != '\n') {
980 printf("\rserver: %s", message);
981 if (strchr(message, '\n') == 0)
982 printf("\n");
985 if (packfile_size > 0 || nobj_indexed > 0) {
986 printf("\r");
987 if (fmt_scaled(packfile_size, scaled) == 0)
988 printf(" %*s fetched", FMT_SCALED_STRSIZE, scaled);
989 if (nobj_indexed > 0) {
990 p = (nobj_indexed * 100) / nobj_total;
991 printf("; indexing %d%%", p);
993 if (nobj_resolved > 0) {
994 p = (nobj_resolved * 100) / (nobj_total - nobj_loose);
995 printf("; resolving deltas %d%%", p);
997 if (nobj_indexed > 0 && nobj_indexed == nobj_total &&
998 nobj_resolved == nobj_total - nobj_loose)
999 printf("\nWriting pack index...\n");
1002 fflush(stdout);
1003 return NULL;
1006 static const struct got_error *
1007 cmd_clone(int argc, char *argv[])
1009 const struct got_error *err = NULL;
1010 const char *uri, *branch_filter, *dirname;
1011 char *proto, *host, *port, *repo_name, *server_path;
1012 char *default_destdir = NULL, *id_str = NULL;
1013 const char *repo_path;
1014 struct got_repository *repo = NULL;
1015 struct got_pathlist_head refs, symrefs;
1016 struct got_pathlist_entry *pe;
1017 struct got_object_id *pack_hash = NULL;
1018 int ch, fetchfd = -1;
1020 TAILQ_INIT(&refs);
1021 TAILQ_INIT(&symrefs);
1023 while ((ch = getopt(argc, argv, "b:")) != -1) {
1024 switch (ch) {
1025 case 'b':
1026 branch_filter = optarg;
1027 break;
1028 default:
1029 usage_clone();
1030 break;
1033 argc -= optind;
1034 argv += optind;
1035 uri = argv[0];
1036 if(argc == 1)
1037 dirname = NULL;
1038 else if(argc == 2)
1039 dirname = argv[1];
1040 else
1041 usage_clone();
1043 err = got_fetch_parse_uri(&proto, &host, &port, &server_path,
1044 &repo_name, argv[0]);
1045 if (err)
1046 goto done;
1048 if (dirname == NULL) {
1049 if (asprintf(&default_destdir, "%s.git", repo_name) == -1) {
1050 err = got_error_from_errno("asprintf");
1051 goto done;
1053 repo_path = default_destdir;
1054 } else
1055 repo_path = dirname;
1057 err = got_path_mkdir(repo_path);
1058 if (err)
1059 goto done;
1061 err = got_repo_init(repo_path);
1062 if (err)
1063 goto done;
1065 err = got_repo_open(&repo, repo_path, NULL);
1066 if (err)
1067 goto done;
1069 err = got_fetch_connect(&fetchfd, proto, host, port, server_path);
1070 if (err)
1071 goto done;
1073 printf("Connected to %s:%s\n", host, port);
1075 err = got_fetch_pack(&pack_hash, &refs, &symrefs, fetchfd,
1076 repo, fetch_progress, NULL);
1077 if (err)
1078 goto done;
1080 err = got_object_id_str(&id_str, pack_hash);
1081 if (err)
1082 goto done;
1083 printf("Fetched %s.pack\n", id_str);
1084 free(id_str);
1086 /* Set up references provided with the pack file. */
1087 TAILQ_FOREACH(pe, &refs, entry) {
1088 const char *refname = pe->path;
1089 struct got_object_id *id = pe->data;
1090 struct got_reference *ref;
1093 err = got_ref_alloc(&ref, refname, id);
1094 if (err)
1095 goto done;
1097 #if 0
1098 err = got_object_id_str(&id_str, id);
1099 if (err)
1100 goto done;
1101 printf("%s: %s\n", got_ref_get_name(ref), id_str);
1102 free(id_str);
1103 #endif
1104 err = got_ref_write(ref, repo);
1105 got_ref_close(ref);
1106 if (err)
1107 goto done;
1110 /* Set the HEAD reference if the server provided one. */
1111 TAILQ_FOREACH(pe, &symrefs, entry) {
1112 struct got_reference *symref, *target_ref;
1113 const char *refname = pe->path;
1114 const char *target = pe->data;
1116 if (strcmp(refname, GOT_REF_HEAD) != 0)
1117 continue;
1119 err = got_ref_open(&target_ref, repo, target, 0);
1120 if (err) {
1121 if (err->code == GOT_ERR_NOT_REF)
1122 continue;
1123 goto done;
1126 err = got_ref_alloc_symref(&symref, GOT_REF_HEAD, target_ref);
1127 got_ref_close(target_ref);
1128 if (err)
1129 goto done;
1131 printf("Setting %s to %s\n", GOT_REF_HEAD,
1132 got_ref_get_symref_target(symref));
1134 err = got_ref_write(symref, repo);
1135 got_ref_close(symref);
1136 break;
1139 done:
1140 if (fetchfd != -1 && close(fetchfd) == -1 && err == NULL)
1141 err = got_error_from_errno("close");
1142 if (repo)
1143 got_repo_close(repo);
1144 TAILQ_FOREACH(pe, &refs, entry) {
1145 free((void *)pe->path);
1146 free(pe->data);
1148 got_pathlist_free(&refs);
1149 TAILQ_FOREACH(pe, &symrefs, entry) {
1150 free((void *)pe->path);
1151 free(pe->data);
1153 got_pathlist_free(&symrefs);
1154 free(pack_hash);
1155 free(proto);
1156 free(host);
1157 free(port);
1158 free(server_path);
1159 free(repo_name);
1160 free(default_destdir);
1161 return err;
1164 static const struct got_error *
1165 checkout_ancestry_error(struct got_reference *ref, const char *commit_id_str)
1167 static char msg[512];
1168 const char *branch_name;
1170 if (got_ref_is_symbolic(ref))
1171 branch_name = got_ref_get_symref_target(ref);
1172 else
1173 branch_name = got_ref_get_name(ref);
1175 if (strncmp("refs/heads/", branch_name, 11) == 0)
1176 branch_name += 11;
1178 snprintf(msg, sizeof(msg),
1179 "target commit is not contained in branch '%s'; "
1180 "the branch to use must be specified with -b; "
1181 "if necessary a new branch can be created for "
1182 "this commit with 'got branch -c %s BRANCH_NAME'",
1183 branch_name, commit_id_str);
1185 return got_error_msg(GOT_ERR_ANCESTRY, msg);
1188 static const struct got_error *
1189 cmd_checkout(int argc, char *argv[])
1191 const struct got_error *error = NULL;
1192 struct got_repository *repo = NULL;
1193 struct got_reference *head_ref = NULL;
1194 struct got_worktree *worktree = NULL;
1195 char *repo_path = NULL;
1196 char *worktree_path = NULL;
1197 const char *path_prefix = "";
1198 const char *branch_name = GOT_REF_HEAD;
1199 char *commit_id_str = NULL;
1200 int ch, same_path_prefix, allow_nonempty = 0;
1201 struct got_pathlist_head paths;
1202 struct got_checkout_progress_arg cpa;
1204 TAILQ_INIT(&paths);
1206 while ((ch = getopt(argc, argv, "b:c:Ep:")) != -1) {
1207 switch (ch) {
1208 case 'b':
1209 branch_name = optarg;
1210 break;
1211 case 'c':
1212 commit_id_str = strdup(optarg);
1213 if (commit_id_str == NULL)
1214 return got_error_from_errno("strdup");
1215 break;
1216 case 'E':
1217 allow_nonempty = 1;
1218 break;
1219 case 'p':
1220 path_prefix = optarg;
1221 break;
1222 default:
1223 usage_checkout();
1224 /* NOTREACHED */
1228 argc -= optind;
1229 argv += optind;
1231 #ifndef PROFILE
1232 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
1233 "unveil", NULL) == -1)
1234 err(1, "pledge");
1235 #endif
1236 if (argc == 1) {
1237 char *cwd, *base, *dotgit;
1238 repo_path = realpath(argv[0], NULL);
1239 if (repo_path == NULL)
1240 return got_error_from_errno2("realpath", argv[0]);
1241 cwd = getcwd(NULL, 0);
1242 if (cwd == NULL) {
1243 error = got_error_from_errno("getcwd");
1244 goto done;
1246 if (path_prefix[0]) {
1247 base = basename(path_prefix);
1248 if (base == NULL) {
1249 error = got_error_from_errno2("basename",
1250 path_prefix);
1251 goto done;
1253 } else {
1254 base = basename(repo_path);
1255 if (base == NULL) {
1256 error = got_error_from_errno2("basename",
1257 repo_path);
1258 goto done;
1261 dotgit = strstr(base, ".git");
1262 if (dotgit)
1263 *dotgit = '\0';
1264 if (asprintf(&worktree_path, "%s/%s", cwd, base) == -1) {
1265 error = got_error_from_errno("asprintf");
1266 free(cwd);
1267 goto done;
1269 free(cwd);
1270 } else if (argc == 2) {
1271 repo_path = realpath(argv[0], NULL);
1272 if (repo_path == NULL) {
1273 error = got_error_from_errno2("realpath", argv[0]);
1274 goto done;
1276 worktree_path = realpath(argv[1], NULL);
1277 if (worktree_path == NULL) {
1278 if (errno != ENOENT) {
1279 error = got_error_from_errno2("realpath",
1280 argv[1]);
1281 goto done;
1283 worktree_path = strdup(argv[1]);
1284 if (worktree_path == NULL) {
1285 error = got_error_from_errno("strdup");
1286 goto done;
1289 } else
1290 usage_checkout();
1292 got_path_strip_trailing_slashes(repo_path);
1293 got_path_strip_trailing_slashes(worktree_path);
1295 error = got_repo_open(&repo, repo_path, NULL);
1296 if (error != NULL)
1297 goto done;
1299 /* Pre-create work tree path for unveil(2) */
1300 error = got_path_mkdir(worktree_path);
1301 if (error) {
1302 if (!(error->code == GOT_ERR_ERRNO && errno == EISDIR) &&
1303 !(error->code == GOT_ERR_ERRNO && errno == EEXIST))
1304 goto done;
1305 if (!allow_nonempty &&
1306 !got_path_dir_is_empty(worktree_path)) {
1307 error = got_error_path(worktree_path,
1308 GOT_ERR_DIR_NOT_EMPTY);
1309 goto done;
1313 error = apply_unveil(got_repo_get_path(repo), 0, worktree_path);
1314 if (error)
1315 goto done;
1317 error = got_ref_open(&head_ref, repo, branch_name, 0);
1318 if (error != NULL)
1319 goto done;
1321 error = got_worktree_init(worktree_path, head_ref, path_prefix, repo);
1322 if (error != NULL && !(error->code == GOT_ERR_ERRNO && errno == EEXIST))
1323 goto done;
1325 error = got_worktree_open(&worktree, worktree_path);
1326 if (error != NULL)
1327 goto done;
1329 error = got_worktree_match_path_prefix(&same_path_prefix, worktree,
1330 path_prefix);
1331 if (error != NULL)
1332 goto done;
1333 if (!same_path_prefix) {
1334 error = got_error(GOT_ERR_PATH_PREFIX);
1335 goto done;
1338 if (commit_id_str) {
1339 struct got_object_id *commit_id;
1340 error = got_repo_match_object_id(&commit_id, NULL,
1341 commit_id_str, GOT_OBJ_TYPE_COMMIT, 1, repo);
1342 if (error)
1343 goto done;
1344 error = check_linear_ancestry(commit_id,
1345 got_worktree_get_base_commit_id(worktree), 0, repo);
1346 if (error != NULL) {
1347 free(commit_id);
1348 if (error->code == GOT_ERR_ANCESTRY) {
1349 error = checkout_ancestry_error(
1350 head_ref, commit_id_str);
1352 goto done;
1354 error = check_same_branch(commit_id, head_ref, NULL, repo);
1355 if (error) {
1356 if (error->code == GOT_ERR_ANCESTRY) {
1357 error = checkout_ancestry_error(
1358 head_ref, commit_id_str);
1360 goto done;
1362 error = got_worktree_set_base_commit_id(worktree, repo,
1363 commit_id);
1364 free(commit_id);
1365 if (error)
1366 goto done;
1369 error = got_pathlist_append(&paths, "", NULL);
1370 if (error)
1371 goto done;
1372 cpa.worktree_path = worktree_path;
1373 cpa.had_base_commit_ref_error = 0;
1374 error = got_worktree_checkout_files(worktree, &paths, repo,
1375 checkout_progress, &cpa, check_cancelled, NULL);
1376 if (error != NULL)
1377 goto done;
1379 printf("Now shut up and hack\n");
1380 if (cpa.had_base_commit_ref_error)
1381 show_worktree_base_ref_warning();
1382 done:
1383 got_pathlist_free(&paths);
1384 free(commit_id_str);
1385 free(repo_path);
1386 free(worktree_path);
1387 return error;
1390 __dead static void
1391 usage_update(void)
1393 fprintf(stderr, "usage: %s update [-b branch] [-c commit] [path ...]\n",
1394 getprogname());
1395 exit(1);
1398 static const struct got_error *
1399 update_progress(void *arg, unsigned char status, const char *path)
1401 int *did_something = arg;
1403 if (status == GOT_STATUS_EXISTS ||
1404 status == GOT_STATUS_BASE_REF_ERR)
1405 return NULL;
1407 *did_something = 1;
1409 /* Base commit bump happens silently. */
1410 if (status == GOT_STATUS_BUMP_BASE)
1411 return NULL;
1413 while (path[0] == '/')
1414 path++;
1415 printf("%c %s\n", status, path);
1416 return NULL;
1419 static const struct got_error *
1420 switch_head_ref(struct got_reference *head_ref,
1421 struct got_object_id *commit_id, struct got_worktree *worktree,
1422 struct got_repository *repo)
1424 const struct got_error *err = NULL;
1425 char *base_id_str;
1426 int ref_has_moved = 0;
1428 /* Trivial case: switching between two different references. */
1429 if (strcmp(got_ref_get_name(head_ref),
1430 got_worktree_get_head_ref_name(worktree)) != 0) {
1431 printf("Switching work tree from %s to %s\n",
1432 got_worktree_get_head_ref_name(worktree),
1433 got_ref_get_name(head_ref));
1434 return got_worktree_set_head_ref(worktree, head_ref);
1437 err = check_linear_ancestry(commit_id,
1438 got_worktree_get_base_commit_id(worktree), 0, repo);
1439 if (err) {
1440 if (err->code != GOT_ERR_ANCESTRY)
1441 return err;
1442 ref_has_moved = 1;
1444 if (!ref_has_moved)
1445 return NULL;
1447 /* Switching to a rebased branch with the same reference name. */
1448 err = got_object_id_str(&base_id_str,
1449 got_worktree_get_base_commit_id(worktree));
1450 if (err)
1451 return err;
1452 printf("Reference %s now points at a different branch\n",
1453 got_worktree_get_head_ref_name(worktree));
1454 printf("Switching work tree from %s to %s\n", base_id_str,
1455 got_worktree_get_head_ref_name(worktree));
1456 return NULL;
1459 static const struct got_error *
1460 check_rebase_or_histedit_in_progress(struct got_worktree *worktree)
1462 const struct got_error *err;
1463 int in_progress;
1465 err = got_worktree_rebase_in_progress(&in_progress, worktree);
1466 if (err)
1467 return err;
1468 if (in_progress)
1469 return got_error(GOT_ERR_REBASING);
1471 err = got_worktree_histedit_in_progress(&in_progress, worktree);
1472 if (err)
1473 return err;
1474 if (in_progress)
1475 return got_error(GOT_ERR_HISTEDIT_BUSY);
1477 return NULL;
1480 static const struct got_error *
1481 get_worktree_paths_from_argv(struct got_pathlist_head *paths, int argc,
1482 char *argv[], struct got_worktree *worktree)
1484 const struct got_error *err = NULL;
1485 char *path;
1486 int i;
1488 if (argc == 0) {
1489 path = strdup("");
1490 if (path == NULL)
1491 return got_error_from_errno("strdup");
1492 return got_pathlist_append(paths, path, NULL);
1495 for (i = 0; i < argc; i++) {
1496 err = got_worktree_resolve_path(&path, worktree, argv[i]);
1497 if (err)
1498 break;
1499 err = got_pathlist_append(paths, path, NULL);
1500 if (err) {
1501 free(path);
1502 break;
1506 return err;
1509 static const struct got_error *
1510 cmd_update(int argc, char *argv[])
1512 const struct got_error *error = NULL;
1513 struct got_repository *repo = NULL;
1514 struct got_worktree *worktree = NULL;
1515 char *worktree_path = NULL;
1516 struct got_object_id *commit_id = NULL;
1517 char *commit_id_str = NULL;
1518 const char *branch_name = NULL;
1519 struct got_reference *head_ref = NULL;
1520 struct got_pathlist_head paths;
1521 struct got_pathlist_entry *pe;
1522 int ch, did_something = 0;
1524 TAILQ_INIT(&paths);
1526 while ((ch = getopt(argc, argv, "b:c:")) != -1) {
1527 switch (ch) {
1528 case 'b':
1529 branch_name = optarg;
1530 break;
1531 case 'c':
1532 commit_id_str = strdup(optarg);
1533 if (commit_id_str == NULL)
1534 return got_error_from_errno("strdup");
1535 break;
1536 default:
1537 usage_update();
1538 /* NOTREACHED */
1542 argc -= optind;
1543 argv += optind;
1545 #ifndef PROFILE
1546 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
1547 "unveil", NULL) == -1)
1548 err(1, "pledge");
1549 #endif
1550 worktree_path = getcwd(NULL, 0);
1551 if (worktree_path == NULL) {
1552 error = got_error_from_errno("getcwd");
1553 goto done;
1555 error = got_worktree_open(&worktree, worktree_path);
1556 if (error)
1557 goto done;
1559 error = check_rebase_or_histedit_in_progress(worktree);
1560 if (error)
1561 goto done;
1563 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
1564 NULL);
1565 if (error != NULL)
1566 goto done;
1568 error = apply_unveil(got_repo_get_path(repo), 0,
1569 got_worktree_get_root_path(worktree));
1570 if (error)
1571 goto done;
1573 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
1574 if (error)
1575 goto done;
1577 error = got_ref_open(&head_ref, repo, branch_name ? branch_name :
1578 got_worktree_get_head_ref_name(worktree), 0);
1579 if (error != NULL)
1580 goto done;
1581 if (commit_id_str == NULL) {
1582 error = got_ref_resolve(&commit_id, repo, head_ref);
1583 if (error != NULL)
1584 goto done;
1585 error = got_object_id_str(&commit_id_str, commit_id);
1586 if (error != NULL)
1587 goto done;
1588 } else {
1589 error = got_repo_match_object_id(&commit_id, NULL,
1590 commit_id_str, GOT_OBJ_TYPE_COMMIT, 1, repo);
1591 free(commit_id_str);
1592 commit_id_str = NULL;
1593 if (error)
1594 goto done;
1595 error = got_object_id_str(&commit_id_str, commit_id);
1596 if (error)
1597 goto done;
1600 if (branch_name) {
1601 struct got_object_id *head_commit_id;
1602 TAILQ_FOREACH(pe, &paths, entry) {
1603 if (pe->path_len == 0)
1604 continue;
1605 error = got_error_msg(GOT_ERR_BAD_PATH,
1606 "switching between branches requires that "
1607 "the entire work tree gets updated");
1608 goto done;
1610 error = got_ref_resolve(&head_commit_id, repo, head_ref);
1611 if (error)
1612 goto done;
1613 error = check_linear_ancestry(commit_id, head_commit_id, 0,
1614 repo);
1615 free(head_commit_id);
1616 if (error != NULL)
1617 goto done;
1618 error = check_same_branch(commit_id, head_ref, NULL, repo);
1619 if (error)
1620 goto done;
1621 error = switch_head_ref(head_ref, commit_id, worktree, repo);
1622 if (error)
1623 goto done;
1624 } else {
1625 error = check_linear_ancestry(commit_id,
1626 got_worktree_get_base_commit_id(worktree), 0, repo);
1627 if (error != NULL) {
1628 if (error->code == GOT_ERR_ANCESTRY)
1629 error = got_error(GOT_ERR_BRANCH_MOVED);
1630 goto done;
1632 error = check_same_branch(commit_id, head_ref, NULL, repo);
1633 if (error)
1634 goto done;
1637 if (got_object_id_cmp(got_worktree_get_base_commit_id(worktree),
1638 commit_id) != 0) {
1639 error = got_worktree_set_base_commit_id(worktree, repo,
1640 commit_id);
1641 if (error)
1642 goto done;
1645 error = got_worktree_checkout_files(worktree, &paths, repo,
1646 update_progress, &did_something, check_cancelled, NULL);
1647 if (error != NULL)
1648 goto done;
1650 if (did_something)
1651 printf("Updated to commit %s\n", commit_id_str);
1652 else
1653 printf("Already up-to-date\n");
1654 done:
1655 free(worktree_path);
1656 TAILQ_FOREACH(pe, &paths, entry)
1657 free((char *)pe->path);
1658 got_pathlist_free(&paths);
1659 free(commit_id);
1660 free(commit_id_str);
1661 return error;
1664 static const struct got_error *
1665 diff_blobs(struct got_object_id *blob_id1, struct got_object_id *blob_id2,
1666 const char *path, int diff_context, int ignore_whitespace,
1667 struct got_repository *repo)
1669 const struct got_error *err = NULL;
1670 struct got_blob_object *blob1 = NULL, *blob2 = NULL;
1672 if (blob_id1) {
1673 err = got_object_open_as_blob(&blob1, repo, blob_id1, 8192);
1674 if (err)
1675 goto done;
1678 err = got_object_open_as_blob(&blob2, repo, blob_id2, 8192);
1679 if (err)
1680 goto done;
1682 while (path[0] == '/')
1683 path++;
1684 err = got_diff_blob(blob1, blob2, path, path, diff_context,
1685 ignore_whitespace, stdout);
1686 done:
1687 if (blob1)
1688 got_object_blob_close(blob1);
1689 got_object_blob_close(blob2);
1690 return err;
1693 static const struct got_error *
1694 diff_trees(struct got_object_id *tree_id1, struct got_object_id *tree_id2,
1695 const char *path, int diff_context, int ignore_whitespace,
1696 struct got_repository *repo)
1698 const struct got_error *err = NULL;
1699 struct got_tree_object *tree1 = NULL, *tree2 = NULL;
1700 struct got_diff_blob_output_unidiff_arg arg;
1702 if (tree_id1) {
1703 err = got_object_open_as_tree(&tree1, repo, tree_id1);
1704 if (err)
1705 goto done;
1708 err = got_object_open_as_tree(&tree2, repo, tree_id2);
1709 if (err)
1710 goto done;
1712 arg.diff_context = diff_context;
1713 arg.ignore_whitespace = ignore_whitespace;
1714 arg.outfile = stdout;
1715 while (path[0] == '/')
1716 path++;
1717 err = got_diff_tree(tree1, tree2, path, path, repo,
1718 got_diff_blob_output_unidiff, &arg, 1);
1719 done:
1720 if (tree1)
1721 got_object_tree_close(tree1);
1722 if (tree2)
1723 got_object_tree_close(tree2);
1724 return err;
1727 static const struct got_error *
1728 print_patch(struct got_commit_object *commit, struct got_object_id *id,
1729 const char *path, int diff_context, struct got_repository *repo)
1731 const struct got_error *err = NULL;
1732 struct got_commit_object *pcommit = NULL;
1733 char *id_str1 = NULL, *id_str2 = NULL;
1734 struct got_object_id *obj_id1 = NULL, *obj_id2 = NULL;
1735 struct got_object_qid *qid;
1737 qid = SIMPLEQ_FIRST(got_object_commit_get_parent_ids(commit));
1738 if (qid != NULL) {
1739 err = got_object_open_as_commit(&pcommit, repo,
1740 qid->id);
1741 if (err)
1742 return err;
1745 if (path && path[0] != '\0') {
1746 int obj_type;
1747 err = got_object_id_by_path(&obj_id2, repo, id, path);
1748 if (err)
1749 goto done;
1750 err = got_object_id_str(&id_str2, obj_id2);
1751 if (err) {
1752 free(obj_id2);
1753 goto done;
1755 if (pcommit) {
1756 err = got_object_id_by_path(&obj_id1, repo,
1757 qid->id, path);
1758 if (err) {
1759 free(obj_id2);
1760 goto done;
1762 err = got_object_id_str(&id_str1, obj_id1);
1763 if (err) {
1764 free(obj_id2);
1765 goto done;
1768 err = got_object_get_type(&obj_type, repo, obj_id2);
1769 if (err) {
1770 free(obj_id2);
1771 goto done;
1773 printf("diff %s %s\n", id_str1 ? id_str1 : "/dev/null", id_str2);
1774 switch (obj_type) {
1775 case GOT_OBJ_TYPE_BLOB:
1776 err = diff_blobs(obj_id1, obj_id2, path, diff_context,
1777 0, repo);
1778 break;
1779 case GOT_OBJ_TYPE_TREE:
1780 err = diff_trees(obj_id1, obj_id2, path, diff_context,
1781 0, repo);
1782 break;
1783 default:
1784 err = got_error(GOT_ERR_OBJ_TYPE);
1785 break;
1787 free(obj_id1);
1788 free(obj_id2);
1789 } else {
1790 obj_id2 = got_object_commit_get_tree_id(commit);
1791 err = got_object_id_str(&id_str2, obj_id2);
1792 if (err)
1793 goto done;
1794 obj_id1 = got_object_commit_get_tree_id(pcommit);
1795 err = got_object_id_str(&id_str1, obj_id1);
1796 if (err)
1797 goto done;
1798 printf("diff %s %s\n", id_str1 ? id_str1 : "/dev/null", id_str2);
1799 err = diff_trees(obj_id1, obj_id2, "", diff_context, 0, repo);
1801 done:
1802 free(id_str1);
1803 free(id_str2);
1804 if (pcommit)
1805 got_object_commit_close(pcommit);
1806 return err;
1809 static char *
1810 get_datestr(time_t *time, char *datebuf)
1812 struct tm mytm, *tm;
1813 char *p, *s;
1815 tm = gmtime_r(time, &mytm);
1816 if (tm == NULL)
1817 return NULL;
1818 s = asctime_r(tm, datebuf);
1819 if (s == NULL)
1820 return NULL;
1821 p = strchr(s, '\n');
1822 if (p)
1823 *p = '\0';
1824 return s;
1827 static const struct got_error *
1828 match_logmsg(int *have_match, struct got_object_id *id,
1829 struct got_commit_object *commit, regex_t *regex)
1831 const struct got_error *err = NULL;
1832 regmatch_t regmatch;
1833 char *id_str = NULL, *logmsg = NULL;
1835 *have_match = 0;
1837 err = got_object_id_str(&id_str, id);
1838 if (err)
1839 return err;
1841 err = got_object_commit_get_logmsg(&logmsg, commit);
1842 if (err)
1843 goto done;
1845 if (regexec(regex, logmsg, 1, &regmatch, 0) == 0)
1846 *have_match = 1;
1847 done:
1848 free(id_str);
1849 free(logmsg);
1850 return err;
1853 #define GOT_COMMIT_SEP_STR "-----------------------------------------------\n"
1855 static const struct got_error *
1856 print_commit(struct got_commit_object *commit, struct got_object_id *id,
1857 struct got_repository *repo, const char *path, int show_patch,
1858 int diff_context, struct got_reflist_head *refs)
1860 const struct got_error *err = NULL;
1861 char *id_str, *datestr, *logmsg0, *logmsg, *line;
1862 char datebuf[26];
1863 time_t committer_time;
1864 const char *author, *committer;
1865 char *refs_str = NULL;
1866 struct got_reflist_entry *re;
1868 SIMPLEQ_FOREACH(re, refs, entry) {
1869 char *s;
1870 const char *name;
1871 struct got_tag_object *tag = NULL;
1872 int cmp;
1874 name = got_ref_get_name(re->ref);
1875 if (strcmp(name, GOT_REF_HEAD) == 0)
1876 continue;
1877 if (strncmp(name, "refs/", 5) == 0)
1878 name += 5;
1879 if (strncmp(name, "got/", 4) == 0)
1880 continue;
1881 if (strncmp(name, "heads/", 6) == 0)
1882 name += 6;
1883 if (strncmp(name, "remotes/", 8) == 0)
1884 name += 8;
1885 if (strncmp(name, "tags/", 5) == 0) {
1886 err = got_object_open_as_tag(&tag, repo, re->id);
1887 if (err) {
1888 if (err->code != GOT_ERR_OBJ_TYPE)
1889 return err;
1890 /* Ref points at something other than a tag. */
1891 err = NULL;
1892 tag = NULL;
1895 cmp = got_object_id_cmp(tag ?
1896 got_object_tag_get_object_id(tag) : re->id, id);
1897 if (tag)
1898 got_object_tag_close(tag);
1899 if (cmp != 0)
1900 continue;
1901 s = refs_str;
1902 if (asprintf(&refs_str, "%s%s%s", s ? s : "", s ? ", " : "",
1903 name) == -1) {
1904 err = got_error_from_errno("asprintf");
1905 free(s);
1906 return err;
1908 free(s);
1910 err = got_object_id_str(&id_str, id);
1911 if (err)
1912 return err;
1914 printf(GOT_COMMIT_SEP_STR);
1915 printf("commit %s%s%s%s\n", id_str, refs_str ? " (" : "",
1916 refs_str ? refs_str : "", refs_str ? ")" : "");
1917 free(id_str);
1918 id_str = NULL;
1919 free(refs_str);
1920 refs_str = NULL;
1921 printf("from: %s\n", got_object_commit_get_author(commit));
1922 committer_time = got_object_commit_get_committer_time(commit);
1923 datestr = get_datestr(&committer_time, datebuf);
1924 if (datestr)
1925 printf("date: %s UTC\n", datestr);
1926 author = got_object_commit_get_author(commit);
1927 committer = got_object_commit_get_committer(commit);
1928 if (strcmp(author, committer) != 0)
1929 printf("via: %s\n", committer);
1930 if (got_object_commit_get_nparents(commit) > 1) {
1931 const struct got_object_id_queue *parent_ids;
1932 struct got_object_qid *qid;
1933 int n = 1;
1934 parent_ids = got_object_commit_get_parent_ids(commit);
1935 SIMPLEQ_FOREACH(qid, parent_ids, entry) {
1936 err = got_object_id_str(&id_str, qid->id);
1937 if (err)
1938 return err;
1939 printf("parent %d: %s\n", n++, id_str);
1940 free(id_str);
1944 err = got_object_commit_get_logmsg(&logmsg0, commit);
1945 if (err)
1946 return err;
1948 logmsg = logmsg0;
1949 do {
1950 line = strsep(&logmsg, "\n");
1951 if (line)
1952 printf(" %s\n", line);
1953 } while (line);
1954 free(logmsg0);
1956 if (show_patch) {
1957 err = print_patch(commit, id, path, diff_context, repo);
1958 if (err == 0)
1959 printf("\n");
1962 if (fflush(stdout) != 0 && err == NULL)
1963 err = got_error_from_errno("fflush");
1964 return err;
1967 static const struct got_error *
1968 print_commits(struct got_object_id *root_id, struct got_repository *repo,
1969 const char *path, int show_patch, const char *search_pattern,
1970 int diff_context, int limit, int log_branches,
1971 struct got_reflist_head *refs)
1973 const struct got_error *err;
1974 struct got_commit_graph *graph;
1975 regex_t regex;
1976 int have_match;
1978 if (search_pattern &&
1979 regcomp(&regex, search_pattern, REG_EXTENDED | REG_NOSUB | REG_NEWLINE))
1980 return got_error_msg(GOT_ERR_REGEX, search_pattern);
1982 err = got_commit_graph_open(&graph, path, !log_branches);
1983 if (err)
1984 return err;
1985 err = got_commit_graph_iter_start(graph, root_id, repo,
1986 check_cancelled, NULL);
1987 if (err)
1988 goto done;
1989 for (;;) {
1990 struct got_commit_object *commit;
1991 struct got_object_id *id;
1993 if (sigint_received || sigpipe_received)
1994 break;
1996 err = got_commit_graph_iter_next(&id, graph, repo,
1997 check_cancelled, NULL);
1998 if (err) {
1999 if (err->code == GOT_ERR_ITER_COMPLETED)
2000 err = NULL;
2001 break;
2003 if (id == NULL)
2004 break;
2006 err = got_object_open_as_commit(&commit, repo, id);
2007 if (err)
2008 break;
2010 if (search_pattern) {
2011 err = match_logmsg(&have_match, id, commit, &regex);
2012 if (err) {
2013 got_object_commit_close(commit);
2014 break;
2016 if (have_match == 0) {
2017 got_object_commit_close(commit);
2018 continue;
2022 err = print_commit(commit, id, repo, path, show_patch,
2023 diff_context, refs);
2024 got_object_commit_close(commit);
2025 if (err || (limit && --limit == 0))
2026 break;
2028 done:
2029 if (search_pattern)
2030 regfree(&regex);
2031 got_commit_graph_close(graph);
2032 return err;
2035 __dead static void
2036 usage_log(void)
2038 fprintf(stderr, "usage: %s log [-b] [-c commit] [-C number] [ -l N ] [-p] "
2039 "[-s search-pattern] [-r repository-path] [path]\n", getprogname());
2040 exit(1);
2043 static int
2044 get_default_log_limit(void)
2046 const char *got_default_log_limit;
2047 long long n;
2048 const char *errstr;
2050 got_default_log_limit = getenv("GOT_LOG_DEFAULT_LIMIT");
2051 if (got_default_log_limit == NULL)
2052 return 0;
2053 n = strtonum(got_default_log_limit, 0, INT_MAX, &errstr);
2054 if (errstr != NULL)
2055 return 0;
2056 return n;
2059 static const struct got_error *
2060 cmd_log(int argc, char *argv[])
2062 const struct got_error *error;
2063 struct got_repository *repo = NULL;
2064 struct got_worktree *worktree = NULL;
2065 struct got_commit_object *commit = NULL;
2066 struct got_object_id *id = NULL;
2067 char *repo_path = NULL, *path = NULL, *cwd = NULL, *in_repo_path = NULL;
2068 const char *start_commit = NULL, *search_pattern = NULL;
2069 int diff_context = -1, ch;
2070 int show_patch = 0, limit = 0, log_branches = 0;
2071 const char *errstr;
2072 struct got_reflist_head refs;
2074 SIMPLEQ_INIT(&refs);
2076 #ifndef PROFILE
2077 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
2078 NULL)
2079 == -1)
2080 err(1, "pledge");
2081 #endif
2083 limit = get_default_log_limit();
2085 while ((ch = getopt(argc, argv, "bpc:C:l:r:s:")) != -1) {
2086 switch (ch) {
2087 case 'p':
2088 show_patch = 1;
2089 break;
2090 case 'c':
2091 start_commit = optarg;
2092 break;
2093 case 'C':
2094 diff_context = strtonum(optarg, 0, GOT_DIFF_MAX_CONTEXT,
2095 &errstr);
2096 if (errstr != NULL)
2097 err(1, "-C option %s", errstr);
2098 break;
2099 case 'l':
2100 limit = strtonum(optarg, 0, INT_MAX, &errstr);
2101 if (errstr != NULL)
2102 err(1, "-l option %s", errstr);
2103 break;
2104 case 'b':
2105 log_branches = 1;
2106 break;
2107 case 'r':
2108 repo_path = realpath(optarg, NULL);
2109 if (repo_path == NULL)
2110 return got_error_from_errno2("realpath",
2111 optarg);
2112 got_path_strip_trailing_slashes(repo_path);
2113 break;
2114 case 's':
2115 search_pattern = optarg;
2116 break;
2117 default:
2118 usage_log();
2119 /* NOTREACHED */
2123 argc -= optind;
2124 argv += optind;
2126 if (diff_context == -1)
2127 diff_context = 3;
2128 else if (!show_patch)
2129 errx(1, "-C reguires -p");
2131 cwd = getcwd(NULL, 0);
2132 if (cwd == NULL) {
2133 error = got_error_from_errno("getcwd");
2134 goto done;
2137 error = got_worktree_open(&worktree, cwd);
2138 if (error && error->code != GOT_ERR_NOT_WORKTREE)
2139 goto done;
2140 error = NULL;
2142 if (argc == 0) {
2143 path = strdup("");
2144 if (path == NULL) {
2145 error = got_error_from_errno("strdup");
2146 goto done;
2148 } else if (argc == 1) {
2149 if (worktree) {
2150 error = got_worktree_resolve_path(&path, worktree,
2151 argv[0]);
2152 if (error)
2153 goto done;
2154 } else {
2155 path = strdup(argv[0]);
2156 if (path == NULL) {
2157 error = got_error_from_errno("strdup");
2158 goto done;
2161 } else
2162 usage_log();
2164 if (repo_path == NULL) {
2165 repo_path = worktree ?
2166 strdup(got_worktree_get_repo_path(worktree)) : strdup(cwd);
2168 if (repo_path == NULL) {
2169 error = got_error_from_errno("strdup");
2170 goto done;
2173 error = got_repo_open(&repo, repo_path, NULL);
2174 if (error != NULL)
2175 goto done;
2177 error = apply_unveil(got_repo_get_path(repo), 1,
2178 worktree ? got_worktree_get_root_path(worktree) : NULL);
2179 if (error)
2180 goto done;
2182 if (start_commit == NULL) {
2183 struct got_reference *head_ref;
2184 error = got_ref_open(&head_ref, repo,
2185 worktree ? got_worktree_get_head_ref_name(worktree)
2186 : GOT_REF_HEAD, 0);
2187 if (error != NULL)
2188 return error;
2189 error = got_ref_resolve(&id, repo, head_ref);
2190 got_ref_close(head_ref);
2191 if (error != NULL)
2192 return error;
2193 error = got_object_open_as_commit(&commit, repo, id);
2194 } else {
2195 struct got_reference *ref;
2196 error = got_ref_open(&ref, repo, start_commit, 0);
2197 if (error == NULL) {
2198 int obj_type;
2199 error = got_ref_resolve(&id, repo, ref);
2200 got_ref_close(ref);
2201 if (error != NULL)
2202 goto done;
2203 error = got_object_get_type(&obj_type, repo, id);
2204 if (error != NULL)
2205 goto done;
2206 if (obj_type == GOT_OBJ_TYPE_TAG) {
2207 struct got_tag_object *tag;
2208 error = got_object_open_as_tag(&tag, repo, id);
2209 if (error != NULL)
2210 goto done;
2211 if (got_object_tag_get_object_type(tag) !=
2212 GOT_OBJ_TYPE_COMMIT) {
2213 got_object_tag_close(tag);
2214 error = got_error(GOT_ERR_OBJ_TYPE);
2215 goto done;
2217 free(id);
2218 id = got_object_id_dup(
2219 got_object_tag_get_object_id(tag));
2220 if (id == NULL)
2221 error = got_error_from_errno(
2222 "got_object_id_dup");
2223 got_object_tag_close(tag);
2224 if (error)
2225 goto done;
2226 } else if (obj_type != GOT_OBJ_TYPE_COMMIT) {
2227 error = got_error(GOT_ERR_OBJ_TYPE);
2228 goto done;
2230 error = got_object_open_as_commit(&commit, repo, id);
2231 if (error != NULL)
2232 goto done;
2234 if (commit == NULL) {
2235 error = got_repo_match_object_id_prefix(&id,
2236 start_commit, GOT_OBJ_TYPE_COMMIT, repo);
2237 if (error != NULL)
2238 return error;
2241 if (error != NULL)
2242 goto done;
2244 if (worktree) {
2245 const char *prefix = got_worktree_get_path_prefix(worktree);
2246 char *p;
2247 if (asprintf(&p, "%s%s%s", prefix,
2248 (strcmp(prefix, "/") != 0) ? "/" : "", path) == -1) {
2249 error = got_error_from_errno("asprintf");
2250 goto done;
2252 error = got_repo_map_path(&in_repo_path, repo, p, 0);
2253 free(p);
2254 } else
2255 error = got_repo_map_path(&in_repo_path, repo, path, 1);
2256 if (error != NULL)
2257 goto done;
2258 if (in_repo_path) {
2259 free(path);
2260 path = in_repo_path;
2263 error = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name, NULL);
2264 if (error)
2265 goto done;
2267 error = print_commits(id, repo, path, show_patch, search_pattern,
2268 diff_context, limit, log_branches, &refs);
2269 done:
2270 free(path);
2271 free(repo_path);
2272 free(cwd);
2273 free(id);
2274 if (worktree)
2275 got_worktree_close(worktree);
2276 if (repo) {
2277 const struct got_error *repo_error;
2278 repo_error = got_repo_close(repo);
2279 if (error == NULL)
2280 error = repo_error;
2282 got_ref_list_free(&refs);
2283 return error;
2286 __dead static void
2287 usage_diff(void)
2289 fprintf(stderr, "usage: %s diff [-C number] [-r repository-path] [-s] "
2290 "[-w] [object1 object2 | path]\n", getprogname());
2291 exit(1);
2294 struct print_diff_arg {
2295 struct got_repository *repo;
2296 struct got_worktree *worktree;
2297 int diff_context;
2298 const char *id_str;
2299 int header_shown;
2300 int diff_staged;
2301 int ignore_whitespace;
2304 static const struct got_error *
2305 print_diff(void *arg, unsigned char status, unsigned char staged_status,
2306 const char *path, struct got_object_id *blob_id,
2307 struct got_object_id *staged_blob_id, struct got_object_id *commit_id,
2308 int dirfd, const char *de_name)
2310 struct print_diff_arg *a = arg;
2311 const struct got_error *err = NULL;
2312 struct got_blob_object *blob1 = NULL;
2313 int fd = -1;
2314 FILE *f2 = NULL;
2315 char *abspath = NULL, *label1 = NULL;
2316 struct stat sb;
2318 if (a->diff_staged) {
2319 if (staged_status != GOT_STATUS_MODIFY &&
2320 staged_status != GOT_STATUS_ADD &&
2321 staged_status != GOT_STATUS_DELETE)
2322 return NULL;
2323 } else {
2324 if (staged_status == GOT_STATUS_DELETE)
2325 return NULL;
2326 if (status == GOT_STATUS_NONEXISTENT)
2327 return got_error_set_errno(ENOENT, path);
2328 if (status != GOT_STATUS_MODIFY &&
2329 status != GOT_STATUS_ADD &&
2330 status != GOT_STATUS_DELETE &&
2331 status != GOT_STATUS_CONFLICT)
2332 return NULL;
2335 if (!a->header_shown) {
2336 printf("diff %s %s%s\n", a->id_str,
2337 got_worktree_get_root_path(a->worktree),
2338 a->diff_staged ? " (staged changes)" : "");
2339 a->header_shown = 1;
2342 if (a->diff_staged) {
2343 const char *label1 = NULL, *label2 = NULL;
2344 switch (staged_status) {
2345 case GOT_STATUS_MODIFY:
2346 label1 = path;
2347 label2 = path;
2348 break;
2349 case GOT_STATUS_ADD:
2350 label2 = path;
2351 break;
2352 case GOT_STATUS_DELETE:
2353 label1 = path;
2354 break;
2355 default:
2356 return got_error(GOT_ERR_FILE_STATUS);
2358 return got_diff_objects_as_blobs(blob_id, staged_blob_id,
2359 label1, label2, a->diff_context, a->ignore_whitespace,
2360 a->repo, stdout);
2363 if (staged_status == GOT_STATUS_ADD ||
2364 staged_status == GOT_STATUS_MODIFY) {
2365 char *id_str;
2366 err = got_object_open_as_blob(&blob1, a->repo, staged_blob_id,
2367 8192);
2368 if (err)
2369 goto done;
2370 err = got_object_id_str(&id_str, staged_blob_id);
2371 if (err)
2372 goto done;
2373 if (asprintf(&label1, "%s (staged)", id_str) == -1) {
2374 err = got_error_from_errno("asprintf");
2375 free(id_str);
2376 goto done;
2378 free(id_str);
2379 } else if (status != GOT_STATUS_ADD) {
2380 err = got_object_open_as_blob(&blob1, a->repo, blob_id, 8192);
2381 if (err)
2382 goto done;
2385 if (status != GOT_STATUS_DELETE) {
2386 if (asprintf(&abspath, "%s/%s",
2387 got_worktree_get_root_path(a->worktree), path) == -1) {
2388 err = got_error_from_errno("asprintf");
2389 goto done;
2392 if (dirfd != -1) {
2393 fd = openat(dirfd, de_name, O_RDONLY | O_NOFOLLOW);
2394 if (fd == -1) {
2395 err = got_error_from_errno2("openat", abspath);
2396 goto done;
2398 } else {
2399 fd = open(abspath, O_RDONLY | O_NOFOLLOW);
2400 if (fd == -1) {
2401 err = got_error_from_errno2("open", abspath);
2402 goto done;
2405 if (fstat(fd, &sb) == -1) {
2406 err = got_error_from_errno2("fstat", abspath);
2407 goto done;
2409 f2 = fdopen(fd, "r");
2410 if (f2 == NULL) {
2411 err = got_error_from_errno2("fdopen", abspath);
2412 goto done;
2414 fd = -1;
2415 } else
2416 sb.st_size = 0;
2418 err = got_diff_blob_file(blob1, label1, f2, sb.st_size, path,
2419 a->diff_context, a->ignore_whitespace, stdout);
2420 done:
2421 if (blob1)
2422 got_object_blob_close(blob1);
2423 if (f2 && fclose(f2) == EOF && err == NULL)
2424 err = got_error_from_errno("fclose");
2425 if (fd != -1 && close(fd) == -1 && err == NULL)
2426 err = got_error_from_errno("close");
2427 free(abspath);
2428 return err;
2431 static const struct got_error *
2432 cmd_diff(int argc, char *argv[])
2434 const struct got_error *error;
2435 struct got_repository *repo = NULL;
2436 struct got_worktree *worktree = NULL;
2437 char *cwd = NULL, *repo_path = NULL;
2438 struct got_object_id *id1 = NULL, *id2 = NULL;
2439 const char *id_str1 = NULL, *id_str2 = NULL;
2440 char *label1 = NULL, *label2 = NULL;
2441 int type1, type2;
2442 int diff_context = 3, diff_staged = 0, ignore_whitespace = 0, ch;
2443 const char *errstr;
2444 char *path = NULL;
2446 #ifndef PROFILE
2447 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
2448 NULL) == -1)
2449 err(1, "pledge");
2450 #endif
2452 while ((ch = getopt(argc, argv, "C:r:sw")) != -1) {
2453 switch (ch) {
2454 case 'C':
2455 diff_context = strtonum(optarg, 0, GOT_DIFF_MAX_CONTEXT,
2456 &errstr);
2457 if (errstr != NULL)
2458 err(1, "-C option %s", errstr);
2459 break;
2460 case 'r':
2461 repo_path = realpath(optarg, NULL);
2462 if (repo_path == NULL)
2463 return got_error_from_errno2("realpath",
2464 optarg);
2465 got_path_strip_trailing_slashes(repo_path);
2466 break;
2467 case 's':
2468 diff_staged = 1;
2469 break;
2470 case 'w':
2471 ignore_whitespace = 1;
2472 break;
2473 default:
2474 usage_diff();
2475 /* NOTREACHED */
2479 argc -= optind;
2480 argv += optind;
2482 cwd = getcwd(NULL, 0);
2483 if (cwd == NULL) {
2484 error = got_error_from_errno("getcwd");
2485 goto done;
2487 error = got_worktree_open(&worktree, cwd);
2488 if (error && error->code != GOT_ERR_NOT_WORKTREE)
2489 goto done;
2490 if (argc <= 1) {
2491 if (worktree == NULL) {
2492 error = got_error(GOT_ERR_NOT_WORKTREE);
2493 goto done;
2495 if (repo_path)
2496 errx(1,
2497 "-r option can't be used when diffing a work tree");
2498 repo_path = strdup(got_worktree_get_repo_path(worktree));
2499 if (repo_path == NULL) {
2500 error = got_error_from_errno("strdup");
2501 goto done;
2503 if (argc == 1) {
2504 error = got_worktree_resolve_path(&path, worktree,
2505 argv[0]);
2506 if (error)
2507 goto done;
2508 } else {
2509 path = strdup("");
2510 if (path == NULL) {
2511 error = got_error_from_errno("strdup");
2512 goto done;
2515 } else if (argc == 2) {
2516 if (diff_staged)
2517 errx(1, "-s option can't be used when diffing "
2518 "objects in repository");
2519 id_str1 = argv[0];
2520 id_str2 = argv[1];
2521 if (worktree && repo_path == NULL) {
2522 repo_path =
2523 strdup(got_worktree_get_repo_path(worktree));
2524 if (repo_path == NULL) {
2525 error = got_error_from_errno("strdup");
2526 goto done;
2529 } else
2530 usage_diff();
2532 if (repo_path == NULL) {
2533 repo_path = getcwd(NULL, 0);
2534 if (repo_path == NULL)
2535 return got_error_from_errno("getcwd");
2538 error = got_repo_open(&repo, repo_path, NULL);
2539 free(repo_path);
2540 if (error != NULL)
2541 goto done;
2543 error = apply_unveil(got_repo_get_path(repo), 1,
2544 worktree ? got_worktree_get_root_path(worktree) : NULL);
2545 if (error)
2546 goto done;
2548 if (argc <= 1) {
2549 struct print_diff_arg arg;
2550 struct got_pathlist_head paths;
2551 char *id_str;
2553 TAILQ_INIT(&paths);
2555 error = got_object_id_str(&id_str,
2556 got_worktree_get_base_commit_id(worktree));
2557 if (error)
2558 goto done;
2559 arg.repo = repo;
2560 arg.worktree = worktree;
2561 arg.diff_context = diff_context;
2562 arg.id_str = id_str;
2563 arg.header_shown = 0;
2564 arg.diff_staged = diff_staged;
2565 arg.ignore_whitespace = ignore_whitespace;
2567 error = got_pathlist_append(&paths, path, NULL);
2568 if (error)
2569 goto done;
2571 error = got_worktree_status(worktree, &paths, repo, print_diff,
2572 &arg, check_cancelled, NULL);
2573 free(id_str);
2574 got_pathlist_free(&paths);
2575 goto done;
2578 error = got_repo_match_object_id(&id1, &label1, id_str1,
2579 GOT_OBJ_TYPE_ANY, 1, repo);
2580 if (error)
2581 goto done;
2583 error = got_repo_match_object_id(&id2, &label2, id_str2,
2584 GOT_OBJ_TYPE_ANY, 1, repo);
2585 if (error)
2586 goto done;
2588 error = got_object_get_type(&type1, repo, id1);
2589 if (error)
2590 goto done;
2592 error = got_object_get_type(&type2, repo, id2);
2593 if (error)
2594 goto done;
2596 if (type1 != type2) {
2597 error = got_error(GOT_ERR_OBJ_TYPE);
2598 goto done;
2601 switch (type1) {
2602 case GOT_OBJ_TYPE_BLOB:
2603 error = got_diff_objects_as_blobs(id1, id2, NULL, NULL,
2604 diff_context, ignore_whitespace, repo, stdout);
2605 break;
2606 case GOT_OBJ_TYPE_TREE:
2607 error = got_diff_objects_as_trees(id1, id2, "", "",
2608 diff_context, ignore_whitespace, repo, stdout);
2609 break;
2610 case GOT_OBJ_TYPE_COMMIT:
2611 printf("diff %s %s\n", label1, label2);
2612 error = got_diff_objects_as_commits(id1, id2, diff_context,
2613 ignore_whitespace, repo, stdout);
2614 break;
2615 default:
2616 error = got_error(GOT_ERR_OBJ_TYPE);
2618 done:
2619 free(label1);
2620 free(label2);
2621 free(id1);
2622 free(id2);
2623 free(path);
2624 if (worktree)
2625 got_worktree_close(worktree);
2626 if (repo) {
2627 const struct got_error *repo_error;
2628 repo_error = got_repo_close(repo);
2629 if (error == NULL)
2630 error = repo_error;
2632 return error;
2635 __dead static void
2636 usage_blame(void)
2638 fprintf(stderr,
2639 "usage: %s blame [-c commit] [-r repository-path] path\n",
2640 getprogname());
2641 exit(1);
2644 struct blame_line {
2645 int annotated;
2646 char *id_str;
2647 char *committer;
2648 char datebuf[11]; /* YYYY-MM-DD + NUL */
2651 struct blame_cb_args {
2652 struct blame_line *lines;
2653 int nlines;
2654 int nlines_prec;
2655 int lineno_cur;
2656 off_t *line_offsets;
2657 FILE *f;
2658 struct got_repository *repo;
2661 static const struct got_error *
2662 blame_cb(void *arg, int nlines, int lineno, struct got_object_id *id)
2664 const struct got_error *err = NULL;
2665 struct blame_cb_args *a = arg;
2666 struct blame_line *bline;
2667 char *line = NULL;
2668 size_t linesize = 0;
2669 struct got_commit_object *commit = NULL;
2670 off_t offset;
2671 struct tm tm;
2672 time_t committer_time;
2674 if (nlines != a->nlines ||
2675 (lineno != -1 && lineno < 1) || lineno > a->nlines)
2676 return got_error(GOT_ERR_RANGE);
2678 if (sigint_received)
2679 return got_error(GOT_ERR_ITER_COMPLETED);
2681 if (lineno == -1)
2682 return NULL; /* no change in this commit */
2684 /* Annotate this line. */
2685 bline = &a->lines[lineno - 1];
2686 if (bline->annotated)
2687 return NULL;
2688 err = got_object_id_str(&bline->id_str, id);
2689 if (err)
2690 return err;
2692 err = got_object_open_as_commit(&commit, a->repo, id);
2693 if (err)
2694 goto done;
2696 bline->committer = strdup(got_object_commit_get_committer(commit));
2697 if (bline->committer == NULL) {
2698 err = got_error_from_errno("strdup");
2699 goto done;
2702 committer_time = got_object_commit_get_committer_time(commit);
2703 if (localtime_r(&committer_time, &tm) == NULL)
2704 return got_error_from_errno("localtime_r");
2705 if (strftime(bline->datebuf, sizeof(bline->datebuf), "%G-%m-%d",
2706 &tm) >= sizeof(bline->datebuf)) {
2707 err = got_error(GOT_ERR_NO_SPACE);
2708 goto done;
2710 bline->annotated = 1;
2712 /* Print lines annotated so far. */
2713 bline = &a->lines[a->lineno_cur - 1];
2714 if (!bline->annotated)
2715 goto done;
2717 offset = a->line_offsets[a->lineno_cur - 1];
2718 if (fseeko(a->f, offset, SEEK_SET) == -1) {
2719 err = got_error_from_errno("fseeko");
2720 goto done;
2723 while (bline->annotated) {
2724 char *smallerthan, *at, *nl, *committer;
2725 size_t len;
2727 if (getline(&line, &linesize, a->f) == -1) {
2728 if (ferror(a->f))
2729 err = got_error_from_errno("getline");
2730 break;
2733 committer = bline->committer;
2734 smallerthan = strchr(committer, '<');
2735 if (smallerthan && smallerthan[1] != '\0')
2736 committer = smallerthan + 1;
2737 at = strchr(committer, '@');
2738 if (at)
2739 *at = '\0';
2740 len = strlen(committer);
2741 if (len >= 9)
2742 committer[8] = '\0';
2744 nl = strchr(line, '\n');
2745 if (nl)
2746 *nl = '\0';
2747 printf("%.*d) %.8s %s %-8s %s\n", a->nlines_prec, a->lineno_cur,
2748 bline->id_str, bline->datebuf, committer, line);
2750 a->lineno_cur++;
2751 bline = &a->lines[a->lineno_cur - 1];
2753 done:
2754 if (commit)
2755 got_object_commit_close(commit);
2756 free(line);
2757 return err;
2760 static const struct got_error *
2761 cmd_blame(int argc, char *argv[])
2763 const struct got_error *error;
2764 struct got_repository *repo = NULL;
2765 struct got_worktree *worktree = NULL;
2766 char *path, *cwd = NULL, *repo_path = NULL, *in_repo_path = NULL;
2767 struct got_object_id *obj_id = NULL;
2768 struct got_object_id *commit_id = NULL;
2769 struct got_blob_object *blob = NULL;
2770 char *commit_id_str = NULL;
2771 struct blame_cb_args bca;
2772 int ch, obj_type, i;
2773 size_t filesize;
2775 memset(&bca, 0, sizeof(bca));
2777 #ifndef PROFILE
2778 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
2779 NULL) == -1)
2780 err(1, "pledge");
2781 #endif
2783 while ((ch = getopt(argc, argv, "c:r:")) != -1) {
2784 switch (ch) {
2785 case 'c':
2786 commit_id_str = optarg;
2787 break;
2788 case 'r':
2789 repo_path = realpath(optarg, NULL);
2790 if (repo_path == NULL)
2791 return got_error_from_errno2("realpath",
2792 optarg);
2793 got_path_strip_trailing_slashes(repo_path);
2794 break;
2795 default:
2796 usage_blame();
2797 /* NOTREACHED */
2801 argc -= optind;
2802 argv += optind;
2804 if (argc == 1)
2805 path = argv[0];
2806 else
2807 usage_blame();
2809 cwd = getcwd(NULL, 0);
2810 if (cwd == NULL) {
2811 error = got_error_from_errno("getcwd");
2812 goto done;
2814 if (repo_path == NULL) {
2815 error = got_worktree_open(&worktree, cwd);
2816 if (error && error->code != GOT_ERR_NOT_WORKTREE)
2817 goto done;
2818 else
2819 error = NULL;
2820 if (worktree) {
2821 repo_path =
2822 strdup(got_worktree_get_repo_path(worktree));
2823 if (repo_path == NULL)
2824 error = got_error_from_errno("strdup");
2825 if (error)
2826 goto done;
2827 } else {
2828 repo_path = strdup(cwd);
2829 if (repo_path == NULL) {
2830 error = got_error_from_errno("strdup");
2831 goto done;
2836 error = got_repo_open(&repo, repo_path, NULL);
2837 if (error != NULL)
2838 goto done;
2840 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
2841 if (error)
2842 goto done;
2844 if (worktree) {
2845 const char *prefix = got_worktree_get_path_prefix(worktree);
2846 char *p, *worktree_subdir = cwd +
2847 strlen(got_worktree_get_root_path(worktree));
2848 if (asprintf(&p, "%s%s%s%s%s",
2849 prefix, (strcmp(prefix, "/") != 0) ? "/" : "",
2850 worktree_subdir, worktree_subdir[0] ? "/" : "",
2851 path) == -1) {
2852 error = got_error_from_errno("asprintf");
2853 goto done;
2855 error = got_repo_map_path(&in_repo_path, repo, p, 0);
2856 free(p);
2857 } else {
2858 error = got_repo_map_path(&in_repo_path, repo, path, 1);
2860 if (error)
2861 goto done;
2863 if (commit_id_str == NULL) {
2864 struct got_reference *head_ref;
2865 error = got_ref_open(&head_ref, repo, worktree ?
2866 got_worktree_get_head_ref_name(worktree) : GOT_REF_HEAD, 0);
2867 if (error != NULL)
2868 goto done;
2869 error = got_ref_resolve(&commit_id, repo, head_ref);
2870 got_ref_close(head_ref);
2871 if (error != NULL)
2872 goto done;
2873 } else {
2874 error = got_repo_match_object_id(&commit_id, NULL,
2875 commit_id_str, GOT_OBJ_TYPE_COMMIT, 1, repo);
2876 if (error)
2877 goto done;
2880 error = got_object_id_by_path(&obj_id, repo, commit_id, in_repo_path);
2881 if (error)
2882 goto done;
2884 error = got_object_get_type(&obj_type, repo, obj_id);
2885 if (error)
2886 goto done;
2888 if (obj_type != GOT_OBJ_TYPE_BLOB) {
2889 error = got_error(GOT_ERR_OBJ_TYPE);
2890 goto done;
2893 error = got_object_open_as_blob(&blob, repo, obj_id, 8192);
2894 if (error)
2895 goto done;
2896 bca.f = got_opentemp();
2897 if (bca.f == NULL) {
2898 error = got_error_from_errno("got_opentemp");
2899 goto done;
2901 error = got_object_blob_dump_to_file(&filesize, &bca.nlines,
2902 &bca.line_offsets, bca.f, blob);
2903 if (error || bca.nlines == 0)
2904 goto done;
2906 /* Don't include \n at EOF in the blame line count. */
2907 if (bca.line_offsets[bca.nlines - 1] == filesize)
2908 bca.nlines--;
2910 bca.lines = calloc(bca.nlines, sizeof(*bca.lines));
2911 if (bca.lines == NULL) {
2912 error = got_error_from_errno("calloc");
2913 goto done;
2915 bca.lineno_cur = 1;
2916 bca.nlines_prec = 0;
2917 i = bca.nlines;
2918 while (i > 0) {
2919 i /= 10;
2920 bca.nlines_prec++;
2922 bca.repo = repo;
2924 error = got_blame(in_repo_path, commit_id, repo, blame_cb, &bca,
2925 check_cancelled, NULL);
2926 done:
2927 free(in_repo_path);
2928 free(repo_path);
2929 free(cwd);
2930 free(commit_id);
2931 free(obj_id);
2932 if (blob)
2933 got_object_blob_close(blob);
2934 if (worktree)
2935 got_worktree_close(worktree);
2936 if (repo) {
2937 const struct got_error *repo_error;
2938 repo_error = got_repo_close(repo);
2939 if (error == NULL)
2940 error = repo_error;
2942 if (bca.lines) {
2943 for (i = 0; i < bca.nlines; i++) {
2944 struct blame_line *bline = &bca.lines[i];
2945 free(bline->id_str);
2946 free(bline->committer);
2948 free(bca.lines);
2950 free(bca.line_offsets);
2951 if (bca.f && fclose(bca.f) == EOF && error == NULL)
2952 error = got_error_from_errno("fclose");
2953 return error;
2956 __dead static void
2957 usage_tree(void)
2959 fprintf(stderr,
2960 "usage: %s tree [-c commit] [-r repository-path] [-iR] path\n",
2961 getprogname());
2962 exit(1);
2965 static void
2966 print_entry(struct got_tree_entry *te, const char *id, const char *path,
2967 const char *root_path)
2969 int is_root_path = (strcmp(path, root_path) == 0);
2970 const char *modestr = "";
2971 mode_t mode = got_tree_entry_get_mode(te);
2973 path += strlen(root_path);
2974 while (path[0] == '/')
2975 path++;
2977 if (got_object_tree_entry_is_submodule(te))
2978 modestr = "$";
2979 else if (S_ISLNK(mode))
2980 modestr = "@";
2981 else if (S_ISDIR(mode))
2982 modestr = "/";
2983 else if (mode & S_IXUSR)
2984 modestr = "*";
2986 printf("%s%s%s%s%s\n", id ? id : "", path,
2987 is_root_path ? "" : "/", got_tree_entry_get_name(te), modestr);
2990 static const struct got_error *
2991 print_tree(const char *path, struct got_object_id *commit_id,
2992 int show_ids, int recurse, const char *root_path,
2993 struct got_repository *repo)
2995 const struct got_error *err = NULL;
2996 struct got_object_id *tree_id = NULL;
2997 struct got_tree_object *tree = NULL;
2998 int nentries, i;
3000 err = got_object_id_by_path(&tree_id, repo, commit_id, path);
3001 if (err)
3002 goto done;
3004 err = got_object_open_as_tree(&tree, repo, tree_id);
3005 if (err)
3006 goto done;
3007 nentries = got_object_tree_get_nentries(tree);
3008 for (i = 0; i < nentries; i++) {
3009 struct got_tree_entry *te;
3010 char *id = NULL;
3012 if (sigint_received || sigpipe_received)
3013 break;
3015 te = got_object_tree_get_entry(tree, i);
3016 if (show_ids) {
3017 char *id_str;
3018 err = got_object_id_str(&id_str,
3019 got_tree_entry_get_id(te));
3020 if (err)
3021 goto done;
3022 if (asprintf(&id, "%s ", id_str) == -1) {
3023 err = got_error_from_errno("asprintf");
3024 free(id_str);
3025 goto done;
3027 free(id_str);
3029 print_entry(te, id, path, root_path);
3030 free(id);
3032 if (recurse && S_ISDIR(got_tree_entry_get_mode(te))) {
3033 char *child_path;
3034 if (asprintf(&child_path, "%s%s%s", path,
3035 path[0] == '/' && path[1] == '\0' ? "" : "/",
3036 got_tree_entry_get_name(te)) == -1) {
3037 err = got_error_from_errno("asprintf");
3038 goto done;
3040 err = print_tree(child_path, commit_id, show_ids, 1,
3041 root_path, repo);
3042 free(child_path);
3043 if (err)
3044 goto done;
3047 done:
3048 if (tree)
3049 got_object_tree_close(tree);
3050 free(tree_id);
3051 return err;
3054 static const struct got_error *
3055 cmd_tree(int argc, char *argv[])
3057 const struct got_error *error;
3058 struct got_repository *repo = NULL;
3059 struct got_worktree *worktree = NULL;
3060 const char *path;
3061 char *cwd = NULL, *repo_path = NULL, *in_repo_path = NULL;
3062 struct got_object_id *commit_id = NULL;
3063 char *commit_id_str = NULL;
3064 int show_ids = 0, recurse = 0;
3065 int ch;
3067 #ifndef PROFILE
3068 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
3069 NULL) == -1)
3070 err(1, "pledge");
3071 #endif
3073 while ((ch = getopt(argc, argv, "c:r:iR")) != -1) {
3074 switch (ch) {
3075 case 'c':
3076 commit_id_str = optarg;
3077 break;
3078 case 'r':
3079 repo_path = realpath(optarg, NULL);
3080 if (repo_path == NULL)
3081 return got_error_from_errno2("realpath",
3082 optarg);
3083 got_path_strip_trailing_slashes(repo_path);
3084 break;
3085 case 'i':
3086 show_ids = 1;
3087 break;
3088 case 'R':
3089 recurse = 1;
3090 break;
3091 default:
3092 usage_tree();
3093 /* NOTREACHED */
3097 argc -= optind;
3098 argv += optind;
3100 if (argc == 1)
3101 path = argv[0];
3102 else if (argc > 1)
3103 usage_tree();
3104 else
3105 path = NULL;
3107 cwd = getcwd(NULL, 0);
3108 if (cwd == NULL) {
3109 error = got_error_from_errno("getcwd");
3110 goto done;
3112 if (repo_path == NULL) {
3113 error = got_worktree_open(&worktree, cwd);
3114 if (error && error->code != GOT_ERR_NOT_WORKTREE)
3115 goto done;
3116 else
3117 error = NULL;
3118 if (worktree) {
3119 repo_path =
3120 strdup(got_worktree_get_repo_path(worktree));
3121 if (repo_path == NULL)
3122 error = got_error_from_errno("strdup");
3123 if (error)
3124 goto done;
3125 } else {
3126 repo_path = strdup(cwd);
3127 if (repo_path == NULL) {
3128 error = got_error_from_errno("strdup");
3129 goto done;
3134 error = got_repo_open(&repo, repo_path, NULL);
3135 if (error != NULL)
3136 goto done;
3138 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
3139 if (error)
3140 goto done;
3142 if (path == NULL) {
3143 if (worktree) {
3144 char *p, *worktree_subdir = cwd +
3145 strlen(got_worktree_get_root_path(worktree));
3146 if (asprintf(&p, "%s/%s",
3147 got_worktree_get_path_prefix(worktree),
3148 worktree_subdir) == -1) {
3149 error = got_error_from_errno("asprintf");
3150 goto done;
3152 error = got_repo_map_path(&in_repo_path, repo, p, 0);
3153 free(p);
3154 if (error)
3155 goto done;
3156 } else
3157 path = "/";
3159 if (in_repo_path == NULL) {
3160 error = got_repo_map_path(&in_repo_path, repo, path, 1);
3161 if (error != NULL)
3162 goto done;
3165 if (commit_id_str == NULL) {
3166 struct got_reference *head_ref;
3167 error = got_ref_open(&head_ref, repo, GOT_REF_HEAD, 0);
3168 if (error != NULL)
3169 goto done;
3170 error = got_ref_resolve(&commit_id, repo, head_ref);
3171 got_ref_close(head_ref);
3172 if (error != NULL)
3173 goto done;
3174 } else {
3175 error = got_repo_match_object_id(&commit_id, NULL,
3176 commit_id_str, GOT_OBJ_TYPE_COMMIT, 1, repo);
3177 if (error)
3178 goto done;
3181 error = print_tree(in_repo_path, commit_id, show_ids, recurse,
3182 in_repo_path, repo);
3183 done:
3184 free(in_repo_path);
3185 free(repo_path);
3186 free(cwd);
3187 free(commit_id);
3188 if (worktree)
3189 got_worktree_close(worktree);
3190 if (repo) {
3191 const struct got_error *repo_error;
3192 repo_error = got_repo_close(repo);
3193 if (error == NULL)
3194 error = repo_error;
3196 return error;
3199 __dead static void
3200 usage_status(void)
3202 fprintf(stderr, "usage: %s status [path ...]\n", getprogname());
3203 exit(1);
3206 static const struct got_error *
3207 print_status(void *arg, unsigned char status, unsigned char staged_status,
3208 const char *path, struct got_object_id *blob_id,
3209 struct got_object_id *staged_blob_id, struct got_object_id *commit_id,
3210 int dirfd, const char *de_name)
3212 if (status == staged_status && (status == GOT_STATUS_DELETE))
3213 status = GOT_STATUS_NO_CHANGE;
3214 printf("%c%c %s\n", status, staged_status, path);
3215 return NULL;
3218 static const struct got_error *
3219 cmd_status(int argc, char *argv[])
3221 const struct got_error *error = NULL;
3222 struct got_repository *repo = NULL;
3223 struct got_worktree *worktree = NULL;
3224 char *cwd = NULL;
3225 struct got_pathlist_head paths;
3226 struct got_pathlist_entry *pe;
3227 int ch;
3229 TAILQ_INIT(&paths);
3231 while ((ch = getopt(argc, argv, "")) != -1) {
3232 switch (ch) {
3233 default:
3234 usage_status();
3235 /* NOTREACHED */
3239 argc -= optind;
3240 argv += optind;
3242 #ifndef PROFILE
3243 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
3244 NULL) == -1)
3245 err(1, "pledge");
3246 #endif
3247 cwd = getcwd(NULL, 0);
3248 if (cwd == NULL) {
3249 error = got_error_from_errno("getcwd");
3250 goto done;
3253 error = got_worktree_open(&worktree, cwd);
3254 if (error != NULL)
3255 goto done;
3257 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
3258 NULL);
3259 if (error != NULL)
3260 goto done;
3262 error = apply_unveil(got_repo_get_path(repo), 1,
3263 got_worktree_get_root_path(worktree));
3264 if (error)
3265 goto done;
3267 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
3268 if (error)
3269 goto done;
3271 error = got_worktree_status(worktree, &paths, repo, print_status, NULL,
3272 check_cancelled, NULL);
3273 done:
3274 TAILQ_FOREACH(pe, &paths, entry)
3275 free((char *)pe->path);
3276 got_pathlist_free(&paths);
3277 free(cwd);
3278 return error;
3281 __dead static void
3282 usage_ref(void)
3284 fprintf(stderr,
3285 "usage: %s ref [-r repository] -l | -d name | [-s] name target\n",
3286 getprogname());
3287 exit(1);
3290 static const struct got_error *
3291 list_refs(struct got_repository *repo)
3293 static const struct got_error *err = NULL;
3294 struct got_reflist_head refs;
3295 struct got_reflist_entry *re;
3297 SIMPLEQ_INIT(&refs);
3298 err = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name, NULL);
3299 if (err)
3300 return err;
3302 SIMPLEQ_FOREACH(re, &refs, entry) {
3303 char *refstr;
3304 refstr = got_ref_to_str(re->ref);
3305 if (refstr == NULL)
3306 return got_error_from_errno("got_ref_to_str");
3307 printf("%s: %s\n", got_ref_get_name(re->ref), refstr);
3308 free(refstr);
3311 got_ref_list_free(&refs);
3312 return NULL;
3315 static const struct got_error *
3316 delete_ref(struct got_repository *repo, const char *refname)
3318 const struct got_error *err = NULL;
3319 struct got_reference *ref;
3321 err = got_ref_open(&ref, repo, refname, 0);
3322 if (err)
3323 return err;
3325 err = got_ref_delete(ref, repo);
3326 got_ref_close(ref);
3327 return err;
3330 static const struct got_error *
3331 add_ref(struct got_repository *repo, const char *refname, const char *target)
3333 const struct got_error *err = NULL;
3334 struct got_object_id *id;
3335 struct got_reference *ref = NULL;
3338 * Don't let the user create a reference name with a leading '-'.
3339 * While technically a valid reference name, this case is usually
3340 * an unintended typo.
3342 if (refname[0] == '-')
3343 return got_error_path(refname, GOT_ERR_REF_NAME_MINUS);
3345 err = got_repo_match_object_id_prefix(&id, target, GOT_OBJ_TYPE_ANY,
3346 repo);
3347 if (err) {
3348 struct got_reference *target_ref;
3350 if (err->code != GOT_ERR_BAD_OBJ_ID_STR)
3351 return err;
3352 err = got_ref_open(&target_ref, repo, target, 0);
3353 if (err)
3354 return err;
3355 err = got_ref_resolve(&id, repo, target_ref);
3356 got_ref_close(target_ref);
3357 if (err)
3358 return err;
3361 err = got_ref_alloc(&ref, refname, id);
3362 if (err)
3363 goto done;
3365 err = got_ref_write(ref, repo);
3366 done:
3367 if (ref)
3368 got_ref_close(ref);
3369 free(id);
3370 return err;
3373 static const struct got_error *
3374 add_symref(struct got_repository *repo, const char *refname, const char *target)
3376 const struct got_error *err = NULL;
3377 struct got_reference *ref = NULL;
3378 struct got_reference *target_ref = NULL;
3381 * Don't let the user create a reference name with a leading '-'.
3382 * While technically a valid reference name, this case is usually
3383 * an unintended typo.
3385 if (refname[0] == '-')
3386 return got_error_path(refname, GOT_ERR_REF_NAME_MINUS);
3388 err = got_ref_open(&target_ref, repo, target, 0);
3389 if (err)
3390 return err;
3392 err = got_ref_alloc_symref(&ref, refname, target_ref);
3393 if (err)
3394 goto done;
3396 err = got_ref_write(ref, repo);
3397 done:
3398 if (target_ref)
3399 got_ref_close(target_ref);
3400 if (ref)
3401 got_ref_close(ref);
3402 return err;
3405 static const struct got_error *
3406 cmd_ref(int argc, char *argv[])
3408 const struct got_error *error = NULL;
3409 struct got_repository *repo = NULL;
3410 struct got_worktree *worktree = NULL;
3411 char *cwd = NULL, *repo_path = NULL;
3412 int ch, do_list = 0, create_symref = 0;
3413 const char *delref = NULL;
3415 /* TODO: Add -s option for adding symbolic references. */
3416 while ((ch = getopt(argc, argv, "d:r:ls")) != -1) {
3417 switch (ch) {
3418 case 'd':
3419 delref = optarg;
3420 break;
3421 case 'r':
3422 repo_path = realpath(optarg, NULL);
3423 if (repo_path == NULL)
3424 return got_error_from_errno2("realpath",
3425 optarg);
3426 got_path_strip_trailing_slashes(repo_path);
3427 break;
3428 case 'l':
3429 do_list = 1;
3430 break;
3431 case 's':
3432 create_symref = 1;
3433 break;
3434 default:
3435 usage_ref();
3436 /* NOTREACHED */
3440 if (do_list && delref)
3441 errx(1, "-l and -d options are mutually exclusive\n");
3443 argc -= optind;
3444 argv += optind;
3446 if (do_list || delref) {
3447 if (create_symref)
3448 errx(1, "-s option cannot be used together with the "
3449 "-l or -d options");
3450 if (argc > 0)
3451 usage_ref();
3452 } else if (argc != 2)
3453 usage_ref();
3455 #ifndef PROFILE
3456 if (do_list) {
3457 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
3458 NULL) == -1)
3459 err(1, "pledge");
3460 } else {
3461 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
3462 "sendfd unveil", NULL) == -1)
3463 err(1, "pledge");
3465 #endif
3466 cwd = getcwd(NULL, 0);
3467 if (cwd == NULL) {
3468 error = got_error_from_errno("getcwd");
3469 goto done;
3472 if (repo_path == NULL) {
3473 error = got_worktree_open(&worktree, cwd);
3474 if (error && error->code != GOT_ERR_NOT_WORKTREE)
3475 goto done;
3476 else
3477 error = NULL;
3478 if (worktree) {
3479 repo_path =
3480 strdup(got_worktree_get_repo_path(worktree));
3481 if (repo_path == NULL)
3482 error = got_error_from_errno("strdup");
3483 if (error)
3484 goto done;
3485 } else {
3486 repo_path = strdup(cwd);
3487 if (repo_path == NULL) {
3488 error = got_error_from_errno("strdup");
3489 goto done;
3494 error = got_repo_open(&repo, repo_path, NULL);
3495 if (error != NULL)
3496 goto done;
3498 error = apply_unveil(got_repo_get_path(repo), do_list,
3499 worktree ? got_worktree_get_root_path(worktree) : NULL);
3500 if (error)
3501 goto done;
3503 if (do_list)
3504 error = list_refs(repo);
3505 else if (delref)
3506 error = delete_ref(repo, delref);
3507 else if (create_symref)
3508 error = add_symref(repo, argv[0], argv[1]);
3509 else
3510 error = add_ref(repo, argv[0], argv[1]);
3511 done:
3512 if (repo)
3513 got_repo_close(repo);
3514 if (worktree)
3515 got_worktree_close(worktree);
3516 free(cwd);
3517 free(repo_path);
3518 return error;
3521 __dead static void
3522 usage_branch(void)
3524 fprintf(stderr,
3525 "usage: %s branch [-c commit] [-d] [-r repository] [-l] [-n] "
3526 "[name]\n", getprogname());
3527 exit(1);
3530 static const struct got_error *
3531 list_branch(struct got_repository *repo, struct got_worktree *worktree,
3532 struct got_reference *ref)
3534 const struct got_error *err = NULL;
3535 const char *refname, *marker = " ";
3536 char *refstr;
3538 refname = got_ref_get_name(ref);
3539 if (worktree && strcmp(refname,
3540 got_worktree_get_head_ref_name(worktree)) == 0) {
3541 struct got_object_id *id = NULL;
3543 err = got_ref_resolve(&id, repo, ref);
3544 if (err)
3545 return err;
3546 if (got_object_id_cmp(id,
3547 got_worktree_get_base_commit_id(worktree)) == 0)
3548 marker = "* ";
3549 else
3550 marker = "~ ";
3551 free(id);
3554 if (strncmp(refname, "refs/heads/", 11) == 0)
3555 refname += 11;
3556 if (strncmp(refname, "refs/got/worktree/", 18) == 0)
3557 refname += 18;
3559 refstr = got_ref_to_str(ref);
3560 if (refstr == NULL)
3561 return got_error_from_errno("got_ref_to_str");
3563 printf("%s%s: %s\n", marker, refname, refstr);
3564 free(refstr);
3565 return NULL;
3568 static const struct got_error *
3569 show_current_branch(struct got_repository *repo, struct got_worktree *worktree)
3571 const char *refname;
3573 if (worktree == NULL)
3574 return got_error(GOT_ERR_NOT_WORKTREE);
3576 refname = got_worktree_get_head_ref_name(worktree);
3578 if (strncmp(refname, "refs/heads/", 11) == 0)
3579 refname += 11;
3580 if (strncmp(refname, "refs/got/worktree/", 18) == 0)
3581 refname += 18;
3583 printf("%s\n", refname);
3585 return NULL;
3588 static const struct got_error *
3589 list_branches(struct got_repository *repo, struct got_worktree *worktree)
3591 static const struct got_error *err = NULL;
3592 struct got_reflist_head refs;
3593 struct got_reflist_entry *re;
3594 struct got_reference *temp_ref = NULL;
3595 int rebase_in_progress, histedit_in_progress;
3597 SIMPLEQ_INIT(&refs);
3599 if (worktree) {
3600 err = got_worktree_rebase_in_progress(&rebase_in_progress,
3601 worktree);
3602 if (err)
3603 return err;
3605 err = got_worktree_histedit_in_progress(&histedit_in_progress,
3606 worktree);
3607 if (err)
3608 return err;
3610 if (rebase_in_progress || histedit_in_progress) {
3611 err = got_ref_open(&temp_ref, repo,
3612 got_worktree_get_head_ref_name(worktree), 0);
3613 if (err)
3614 return err;
3615 list_branch(repo, worktree, temp_ref);
3616 got_ref_close(temp_ref);
3620 err = got_ref_list(&refs, repo, "refs/heads",
3621 got_ref_cmp_by_name, NULL);
3622 if (err)
3623 return err;
3625 SIMPLEQ_FOREACH(re, &refs, entry)
3626 list_branch(repo, worktree, re->ref);
3628 got_ref_list_free(&refs);
3629 return NULL;
3632 static const struct got_error *
3633 delete_branch(struct got_repository *repo, struct got_worktree *worktree,
3634 const char *branch_name)
3636 const struct got_error *err = NULL;
3637 struct got_reference *ref = NULL;
3638 char *refname;
3640 if (asprintf(&refname, "refs/heads/%s", branch_name) == -1)
3641 return got_error_from_errno("asprintf");
3643 err = got_ref_open(&ref, repo, refname, 0);
3644 if (err)
3645 goto done;
3647 if (worktree &&
3648 strcmp(got_worktree_get_head_ref_name(worktree),
3649 got_ref_get_name(ref)) == 0) {
3650 err = got_error_msg(GOT_ERR_SAME_BRANCH,
3651 "will not delete this work tree's current branch");
3652 goto done;
3655 err = got_ref_delete(ref, repo);
3656 done:
3657 if (ref)
3658 got_ref_close(ref);
3659 free(refname);
3660 return err;
3663 static const struct got_error *
3664 add_branch(struct got_repository *repo, const char *branch_name,
3665 struct got_object_id *base_commit_id)
3667 const struct got_error *err = NULL;
3668 struct got_reference *ref = NULL;
3669 char *base_refname = NULL, *refname = NULL;
3672 * Don't let the user create a branch name with a leading '-'.
3673 * While technically a valid reference name, this case is usually
3674 * an unintended typo.
3676 if (branch_name[0] == '-')
3677 return got_error_path(branch_name, GOT_ERR_REF_NAME_MINUS);
3679 if (asprintf(&refname, "refs/heads/%s", branch_name) == -1) {
3680 err = got_error_from_errno("asprintf");
3681 goto done;
3684 err = got_ref_open(&ref, repo, refname, 0);
3685 if (err == NULL) {
3686 err = got_error(GOT_ERR_BRANCH_EXISTS);
3687 goto done;
3688 } else if (err->code != GOT_ERR_NOT_REF)
3689 goto done;
3691 err = got_ref_alloc(&ref, refname, base_commit_id);
3692 if (err)
3693 goto done;
3695 err = got_ref_write(ref, repo);
3696 done:
3697 if (ref)
3698 got_ref_close(ref);
3699 free(base_refname);
3700 free(refname);
3701 return err;
3704 static const struct got_error *
3705 cmd_branch(int argc, char *argv[])
3707 const struct got_error *error = NULL;
3708 struct got_repository *repo = NULL;
3709 struct got_worktree *worktree = NULL;
3710 char *cwd = NULL, *repo_path = NULL;
3711 int ch, do_list = 0, do_show = 0, do_update = 1;
3712 const char *delref = NULL, *commit_id_arg = NULL;
3713 struct got_reference *ref = NULL;
3714 struct got_pathlist_head paths;
3715 struct got_pathlist_entry *pe;
3716 struct got_object_id *commit_id = NULL;
3717 char *commit_id_str = NULL;
3719 TAILQ_INIT(&paths);
3721 while ((ch = getopt(argc, argv, "c:d:r:ln")) != -1) {
3722 switch (ch) {
3723 case 'c':
3724 commit_id_arg = optarg;
3725 break;
3726 case 'd':
3727 delref = optarg;
3728 break;
3729 case 'r':
3730 repo_path = realpath(optarg, NULL);
3731 if (repo_path == NULL)
3732 return got_error_from_errno2("realpath",
3733 optarg);
3734 got_path_strip_trailing_slashes(repo_path);
3735 break;
3736 case 'l':
3737 do_list = 1;
3738 break;
3739 case 'n':
3740 do_update = 0;
3741 break;
3742 default:
3743 usage_branch();
3744 /* NOTREACHED */
3748 if (do_list && delref)
3749 errx(1, "-l and -d options are mutually exclusive\n");
3751 argc -= optind;
3752 argv += optind;
3754 if (!do_list && !delref && argc == 0)
3755 do_show = 1;
3757 if ((do_list || delref || do_show) && commit_id_arg != NULL)
3758 errx(1, "-c option can only be used when creating a branch");
3760 if (do_list || delref) {
3761 if (argc > 0)
3762 usage_branch();
3763 } else if (!do_show && argc != 1)
3764 usage_branch();
3766 #ifndef PROFILE
3767 if (do_list || do_show) {
3768 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
3769 NULL) == -1)
3770 err(1, "pledge");
3771 } else {
3772 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
3773 "sendfd unveil", NULL) == -1)
3774 err(1, "pledge");
3776 #endif
3777 cwd = getcwd(NULL, 0);
3778 if (cwd == NULL) {
3779 error = got_error_from_errno("getcwd");
3780 goto done;
3783 if (repo_path == NULL) {
3784 error = got_worktree_open(&worktree, cwd);
3785 if (error && error->code != GOT_ERR_NOT_WORKTREE)
3786 goto done;
3787 else
3788 error = NULL;
3789 if (worktree) {
3790 repo_path =
3791 strdup(got_worktree_get_repo_path(worktree));
3792 if (repo_path == NULL)
3793 error = got_error_from_errno("strdup");
3794 if (error)
3795 goto done;
3796 } else {
3797 repo_path = strdup(cwd);
3798 if (repo_path == NULL) {
3799 error = got_error_from_errno("strdup");
3800 goto done;
3805 error = got_repo_open(&repo, repo_path, NULL);
3806 if (error != NULL)
3807 goto done;
3809 error = apply_unveil(got_repo_get_path(repo), do_list,
3810 worktree ? got_worktree_get_root_path(worktree) : NULL);
3811 if (error)
3812 goto done;
3814 if (do_show)
3815 error = show_current_branch(repo, worktree);
3816 else if (do_list)
3817 error = list_branches(repo, worktree);
3818 else if (delref)
3819 error = delete_branch(repo, worktree, delref);
3820 else {
3821 if (commit_id_arg == NULL)
3822 commit_id_arg = worktree ?
3823 got_worktree_get_head_ref_name(worktree) :
3824 GOT_REF_HEAD;
3825 error = got_repo_match_object_id(&commit_id, NULL,
3826 commit_id_arg, GOT_OBJ_TYPE_COMMIT, 1, repo);
3827 if (error)
3828 goto done;
3829 error = add_branch(repo, argv[0], commit_id);
3830 if (error)
3831 goto done;
3832 if (worktree && do_update) {
3833 int did_something = 0;
3834 char *branch_refname = NULL;
3836 error = got_object_id_str(&commit_id_str, commit_id);
3837 if (error)
3838 goto done;
3839 error = get_worktree_paths_from_argv(&paths, 0, NULL,
3840 worktree);
3841 if (error)
3842 goto done;
3843 if (asprintf(&branch_refname, "refs/heads/%s", argv[0])
3844 == -1) {
3845 error = got_error_from_errno("asprintf");
3846 goto done;
3848 error = got_ref_open(&ref, repo, branch_refname, 0);
3849 free(branch_refname);
3850 if (error)
3851 goto done;
3852 error = switch_head_ref(ref, commit_id, worktree,
3853 repo);
3854 if (error)
3855 goto done;
3856 error = got_worktree_set_base_commit_id(worktree, repo,
3857 commit_id);
3858 if (error)
3859 goto done;
3860 error = got_worktree_checkout_files(worktree, &paths,
3861 repo, update_progress, &did_something,
3862 check_cancelled, NULL);
3863 if (error)
3864 goto done;
3865 if (did_something)
3866 printf("Updated to commit %s\n", commit_id_str);
3869 done:
3870 if (ref)
3871 got_ref_close(ref);
3872 if (repo)
3873 got_repo_close(repo);
3874 if (worktree)
3875 got_worktree_close(worktree);
3876 free(cwd);
3877 free(repo_path);
3878 free(commit_id);
3879 free(commit_id_str);
3880 TAILQ_FOREACH(pe, &paths, entry)
3881 free((char *)pe->path);
3882 got_pathlist_free(&paths);
3883 return error;
3887 __dead static void
3888 usage_tag(void)
3890 fprintf(stderr,
3891 "usage: %s tag [-c commit] [-r repository] [-l] "
3892 "[-m message] name\n", getprogname());
3893 exit(1);
3896 #if 0
3897 static const struct got_error *
3898 sort_tags(struct got_reflist_head *sorted, struct got_reflist_head *tags)
3900 const struct got_error *err = NULL;
3901 struct got_reflist_entry *re, *se, *new;
3902 struct got_object_id *re_id, *se_id;
3903 struct got_tag_object *re_tag, *se_tag;
3904 time_t re_time, se_time;
3906 SIMPLEQ_FOREACH(re, tags, entry) {
3907 se = SIMPLEQ_FIRST(sorted);
3908 if (se == NULL) {
3909 err = got_reflist_entry_dup(&new, re);
3910 if (err)
3911 return err;
3912 SIMPLEQ_INSERT_HEAD(sorted, new, entry);
3913 continue;
3914 } else {
3915 err = got_ref_resolve(&re_id, repo, re->ref);
3916 if (err)
3917 break;
3918 err = got_object_open_as_tag(&re_tag, repo, re_id);
3919 free(re_id);
3920 if (err)
3921 break;
3922 re_time = got_object_tag_get_tagger_time(re_tag);
3923 got_object_tag_close(re_tag);
3926 while (se) {
3927 err = got_ref_resolve(&se_id, repo, re->ref);
3928 if (err)
3929 break;
3930 err = got_object_open_as_tag(&se_tag, repo, se_id);
3931 free(se_id);
3932 if (err)
3933 break;
3934 se_time = got_object_tag_get_tagger_time(se_tag);
3935 got_object_tag_close(se_tag);
3937 if (se_time > re_time) {
3938 err = got_reflist_entry_dup(&new, re);
3939 if (err)
3940 return err;
3941 SIMPLEQ_INSERT_AFTER(sorted, se, new, entry);
3942 break;
3944 se = SIMPLEQ_NEXT(se, entry);
3945 continue;
3948 done:
3949 return err;
3951 #endif
3953 static const struct got_error *
3954 list_tags(struct got_repository *repo, struct got_worktree *worktree)
3956 static const struct got_error *err = NULL;
3957 struct got_reflist_head refs;
3958 struct got_reflist_entry *re;
3960 SIMPLEQ_INIT(&refs);
3962 err = got_ref_list(&refs, repo, "refs/tags", got_ref_cmp_tags, repo);
3963 if (err)
3964 return err;
3966 SIMPLEQ_FOREACH(re, &refs, entry) {
3967 const char *refname;
3968 char *refstr, *tagmsg0, *tagmsg, *line, *id_str, *datestr;
3969 char datebuf[26];
3970 const char *tagger;
3971 time_t tagger_time;
3972 struct got_object_id *id;
3973 struct got_tag_object *tag;
3974 struct got_commit_object *commit = NULL;
3976 refname = got_ref_get_name(re->ref);
3977 if (strncmp(refname, "refs/tags/", 10) != 0)
3978 continue;
3979 refname += 10;
3980 refstr = got_ref_to_str(re->ref);
3981 if (refstr == NULL) {
3982 err = got_error_from_errno("got_ref_to_str");
3983 break;
3985 printf("%stag %s %s\n", GOT_COMMIT_SEP_STR, refname, refstr);
3986 free(refstr);
3988 err = got_ref_resolve(&id, repo, re->ref);
3989 if (err)
3990 break;
3991 err = got_object_open_as_tag(&tag, repo, id);
3992 if (err) {
3993 if (err->code != GOT_ERR_OBJ_TYPE) {
3994 free(id);
3995 break;
3997 /* "lightweight" tag */
3998 err = got_object_open_as_commit(&commit, repo, id);
3999 if (err) {
4000 free(id);
4001 break;
4003 tagger = got_object_commit_get_committer(commit);
4004 tagger_time =
4005 got_object_commit_get_committer_time(commit);
4006 err = got_object_id_str(&id_str, id);
4007 free(id);
4008 if (err)
4009 break;
4010 } else {
4011 free(id);
4012 tagger = got_object_tag_get_tagger(tag);
4013 tagger_time = got_object_tag_get_tagger_time(tag);
4014 err = got_object_id_str(&id_str,
4015 got_object_tag_get_object_id(tag));
4016 if (err)
4017 break;
4019 printf("from: %s\n", tagger);
4020 datestr = get_datestr(&tagger_time, datebuf);
4021 if (datestr)
4022 printf("date: %s UTC\n", datestr);
4023 if (commit)
4024 printf("object: %s %s\n", GOT_OBJ_LABEL_COMMIT, id_str);
4025 else {
4026 switch (got_object_tag_get_object_type(tag)) {
4027 case GOT_OBJ_TYPE_BLOB:
4028 printf("object: %s %s\n", GOT_OBJ_LABEL_BLOB,
4029 id_str);
4030 break;
4031 case GOT_OBJ_TYPE_TREE:
4032 printf("object: %s %s\n", GOT_OBJ_LABEL_TREE,
4033 id_str);
4034 break;
4035 case GOT_OBJ_TYPE_COMMIT:
4036 printf("object: %s %s\n", GOT_OBJ_LABEL_COMMIT,
4037 id_str);
4038 break;
4039 case GOT_OBJ_TYPE_TAG:
4040 printf("object: %s %s\n", GOT_OBJ_LABEL_TAG,
4041 id_str);
4042 break;
4043 default:
4044 break;
4047 free(id_str);
4048 if (commit) {
4049 err = got_object_commit_get_logmsg(&tagmsg0, commit);
4050 if (err)
4051 break;
4052 got_object_commit_close(commit);
4053 } else {
4054 tagmsg0 = strdup(got_object_tag_get_message(tag));
4055 got_object_tag_close(tag);
4056 if (tagmsg0 == NULL) {
4057 err = got_error_from_errno("strdup");
4058 break;
4062 tagmsg = tagmsg0;
4063 do {
4064 line = strsep(&tagmsg, "\n");
4065 if (line)
4066 printf(" %s\n", line);
4067 } while (line);
4068 free(tagmsg0);
4071 got_ref_list_free(&refs);
4072 return NULL;
4075 static const struct got_error *
4076 get_tag_message(char **tagmsg, char **tagmsg_path, const char *commit_id_str,
4077 const char *tag_name, const char *repo_path)
4079 const struct got_error *err = NULL;
4080 char *template = NULL, *initial_content = NULL;
4081 char *editor = NULL;
4082 int fd = -1;
4084 if (asprintf(&template, GOT_TMPDIR_STR "/got-tagmsg") == -1) {
4085 err = got_error_from_errno("asprintf");
4086 goto done;
4089 if (asprintf(&initial_content, "\n# tagging commit %s as %s\n",
4090 commit_id_str, tag_name) == -1) {
4091 err = got_error_from_errno("asprintf");
4092 goto done;
4095 err = got_opentemp_named_fd(tagmsg_path, &fd, template);
4096 if (err)
4097 goto done;
4099 dprintf(fd, initial_content);
4100 close(fd);
4102 err = get_editor(&editor);
4103 if (err)
4104 goto done;
4105 err = edit_logmsg(tagmsg, editor, *tagmsg_path, initial_content);
4106 done:
4107 free(initial_content);
4108 free(template);
4109 free(editor);
4111 /* Editor is done; we can now apply unveil(2) */
4112 if (err == NULL) {
4113 err = apply_unveil(repo_path, 0, NULL);
4114 if (err) {
4115 free(*tagmsg);
4116 *tagmsg = NULL;
4119 return err;
4122 static const struct got_error *
4123 add_tag(struct got_repository *repo, const char *tag_name,
4124 const char *commit_arg, const char *tagmsg_arg)
4126 const struct got_error *err = NULL;
4127 struct got_object_id *commit_id = NULL, *tag_id = NULL;
4128 char *label = NULL, *commit_id_str = NULL;
4129 struct got_reference *ref = NULL;
4130 char *refname = NULL, *tagmsg = NULL, *tagger = NULL;
4131 char *tagmsg_path = NULL, *tag_id_str = NULL;
4132 int preserve_tagmsg = 0;
4135 * Don't let the user create a tag name with a leading '-'.
4136 * While technically a valid reference name, this case is usually
4137 * an unintended typo.
4139 if (tag_name[0] == '-')
4140 return got_error_path(tag_name, GOT_ERR_REF_NAME_MINUS);
4142 err = get_author(&tagger, repo);
4143 if (err)
4144 return err;
4146 err = got_repo_match_object_id(&commit_id, &label, commit_arg,
4147 GOT_OBJ_TYPE_COMMIT, 1, repo);
4148 if (err)
4149 goto done;
4151 err = got_object_id_str(&commit_id_str, commit_id);
4152 if (err)
4153 goto done;
4155 if (strncmp("refs/tags/", tag_name, 10) == 0) {
4156 refname = strdup(tag_name);
4157 if (refname == NULL) {
4158 err = got_error_from_errno("strdup");
4159 goto done;
4161 tag_name += 10;
4162 } else if (asprintf(&refname, "refs/tags/%s", tag_name) == -1) {
4163 err = got_error_from_errno("asprintf");
4164 goto done;
4167 err = got_ref_open(&ref, repo, refname, 0);
4168 if (err == NULL) {
4169 err = got_error(GOT_ERR_TAG_EXISTS);
4170 goto done;
4171 } else if (err->code != GOT_ERR_NOT_REF)
4172 goto done;
4174 if (tagmsg_arg == NULL) {
4175 err = get_tag_message(&tagmsg, &tagmsg_path, commit_id_str,
4176 tag_name, got_repo_get_path(repo));
4177 if (err) {
4178 if (err->code != GOT_ERR_COMMIT_MSG_EMPTY &&
4179 tagmsg_path != NULL)
4180 preserve_tagmsg = 1;
4181 goto done;
4185 err = got_object_tag_create(&tag_id, tag_name, commit_id,
4186 tagger, time(NULL), tagmsg ? tagmsg : tagmsg_arg, repo);
4187 if (err) {
4188 if (tagmsg_path)
4189 preserve_tagmsg = 1;
4190 goto done;
4193 err = got_ref_alloc(&ref, refname, tag_id);
4194 if (err) {
4195 if (tagmsg_path)
4196 preserve_tagmsg = 1;
4197 goto done;
4200 err = got_ref_write(ref, repo);
4201 if (err) {
4202 if (tagmsg_path)
4203 preserve_tagmsg = 1;
4204 goto done;
4207 err = got_object_id_str(&tag_id_str, tag_id);
4208 if (err) {
4209 if (tagmsg_path)
4210 preserve_tagmsg = 1;
4211 goto done;
4213 printf("Created tag %s\n", tag_id_str);
4214 done:
4215 if (preserve_tagmsg) {
4216 fprintf(stderr, "%s: tag message preserved in %s\n",
4217 getprogname(), tagmsg_path);
4218 } else if (tagmsg_path && unlink(tagmsg_path) == -1 && err == NULL)
4219 err = got_error_from_errno2("unlink", tagmsg_path);
4220 free(tag_id_str);
4221 if (ref)
4222 got_ref_close(ref);
4223 free(commit_id);
4224 free(commit_id_str);
4225 free(refname);
4226 free(tagmsg);
4227 free(tagmsg_path);
4228 free(tagger);
4229 return err;
4232 static const struct got_error *
4233 cmd_tag(int argc, char *argv[])
4235 const struct got_error *error = NULL;
4236 struct got_repository *repo = NULL;
4237 struct got_worktree *worktree = NULL;
4238 char *cwd = NULL, *repo_path = NULL, *commit_id_str = NULL;
4239 char *gitconfig_path = NULL;
4240 const char *tag_name, *commit_id_arg = NULL, *tagmsg = NULL;
4241 int ch, do_list = 0;
4243 while ((ch = getopt(argc, argv, "c:m:r:l")) != -1) {
4244 switch (ch) {
4245 case 'c':
4246 commit_id_arg = optarg;
4247 break;
4248 case 'm':
4249 tagmsg = optarg;
4250 break;
4251 case 'r':
4252 repo_path = realpath(optarg, NULL);
4253 if (repo_path == NULL)
4254 return got_error_from_errno2("realpath",
4255 optarg);
4256 got_path_strip_trailing_slashes(repo_path);
4257 break;
4258 case 'l':
4259 do_list = 1;
4260 break;
4261 default:
4262 usage_tag();
4263 /* NOTREACHED */
4267 argc -= optind;
4268 argv += optind;
4270 if (do_list) {
4271 if (commit_id_arg != NULL)
4272 errx(1, "-c option can only be used when creating a tag");
4273 if (tagmsg)
4274 errx(1, "-l and -m options are mutually exclusive");
4275 if (argc > 0)
4276 usage_tag();
4277 } else if (argc != 1)
4278 usage_tag();
4280 tag_name = argv[0];
4282 #ifndef PROFILE
4283 if (do_list) {
4284 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
4285 NULL) == -1)
4286 err(1, "pledge");
4287 } else {
4288 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
4289 "sendfd unveil", NULL) == -1)
4290 err(1, "pledge");
4292 #endif
4293 cwd = getcwd(NULL, 0);
4294 if (cwd == NULL) {
4295 error = got_error_from_errno("getcwd");
4296 goto done;
4299 if (repo_path == NULL) {
4300 error = got_worktree_open(&worktree, cwd);
4301 if (error && error->code != GOT_ERR_NOT_WORKTREE)
4302 goto done;
4303 else
4304 error = NULL;
4305 if (worktree) {
4306 repo_path =
4307 strdup(got_worktree_get_repo_path(worktree));
4308 if (repo_path == NULL)
4309 error = got_error_from_errno("strdup");
4310 if (error)
4311 goto done;
4312 } else {
4313 repo_path = strdup(cwd);
4314 if (repo_path == NULL) {
4315 error = got_error_from_errno("strdup");
4316 goto done;
4321 if (do_list) {
4322 error = got_repo_open(&repo, repo_path, NULL);
4323 if (error != NULL)
4324 goto done;
4325 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
4326 if (error)
4327 goto done;
4328 error = list_tags(repo, worktree);
4329 } else {
4330 error = get_gitconfig_path(&gitconfig_path);
4331 if (error)
4332 goto done;
4333 error = got_repo_open(&repo, repo_path, gitconfig_path);
4334 if (error != NULL)
4335 goto done;
4337 if (tagmsg) {
4338 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
4339 if (error)
4340 goto done;
4343 if (commit_id_arg == NULL) {
4344 struct got_reference *head_ref;
4345 struct got_object_id *commit_id;
4346 error = got_ref_open(&head_ref, repo,
4347 worktree ? got_worktree_get_head_ref_name(worktree)
4348 : GOT_REF_HEAD, 0);
4349 if (error)
4350 goto done;
4351 error = got_ref_resolve(&commit_id, repo, head_ref);
4352 got_ref_close(head_ref);
4353 if (error)
4354 goto done;
4355 error = got_object_id_str(&commit_id_str, commit_id);
4356 free(commit_id);
4357 if (error)
4358 goto done;
4361 error = add_tag(repo, tag_name,
4362 commit_id_str ? commit_id_str : commit_id_arg, tagmsg);
4364 done:
4365 if (repo)
4366 got_repo_close(repo);
4367 if (worktree)
4368 got_worktree_close(worktree);
4369 free(cwd);
4370 free(repo_path);
4371 free(gitconfig_path);
4372 free(commit_id_str);
4373 return error;
4376 __dead static void
4377 usage_add(void)
4379 fprintf(stderr, "usage: %s add [-R] [-I] path ...\n",
4380 getprogname());
4381 exit(1);
4384 static const struct got_error *
4385 add_progress(void *arg, unsigned char status, const char *path)
4387 while (path[0] == '/')
4388 path++;
4389 printf("%c %s\n", status, path);
4390 return NULL;
4393 static const struct got_error *
4394 cmd_add(int argc, char *argv[])
4396 const struct got_error *error = NULL;
4397 struct got_repository *repo = NULL;
4398 struct got_worktree *worktree = NULL;
4399 char *cwd = NULL;
4400 struct got_pathlist_head paths;
4401 struct got_pathlist_entry *pe;
4402 int ch, can_recurse = 0, no_ignores = 0;
4404 TAILQ_INIT(&paths);
4406 while ((ch = getopt(argc, argv, "IR")) != -1) {
4407 switch (ch) {
4408 case 'I':
4409 no_ignores = 1;
4410 break;
4411 case 'R':
4412 can_recurse = 1;
4413 break;
4414 default:
4415 usage_add();
4416 /* NOTREACHED */
4420 argc -= optind;
4421 argv += optind;
4423 #ifndef PROFILE
4424 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
4425 NULL) == -1)
4426 err(1, "pledge");
4427 #endif
4428 if (argc < 1)
4429 usage_add();
4431 cwd = getcwd(NULL, 0);
4432 if (cwd == NULL) {
4433 error = got_error_from_errno("getcwd");
4434 goto done;
4437 error = got_worktree_open(&worktree, cwd);
4438 if (error)
4439 goto done;
4441 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
4442 NULL);
4443 if (error != NULL)
4444 goto done;
4446 error = apply_unveil(got_repo_get_path(repo), 1,
4447 got_worktree_get_root_path(worktree));
4448 if (error)
4449 goto done;
4451 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
4452 if (error)
4453 goto done;
4455 if (!can_recurse && no_ignores) {
4456 error = got_error_msg(GOT_ERR_BAD_PATH,
4457 "disregarding ignores requires -R option");
4458 goto done;
4462 if (!can_recurse) {
4463 char *ondisk_path;
4464 struct stat sb;
4465 TAILQ_FOREACH(pe, &paths, entry) {
4466 if (asprintf(&ondisk_path, "%s/%s",
4467 got_worktree_get_root_path(worktree),
4468 pe->path) == -1) {
4469 error = got_error_from_errno("asprintf");
4470 goto done;
4472 if (lstat(ondisk_path, &sb) == -1) {
4473 if (errno == ENOENT) {
4474 free(ondisk_path);
4475 continue;
4477 error = got_error_from_errno2("lstat",
4478 ondisk_path);
4479 free(ondisk_path);
4480 goto done;
4482 free(ondisk_path);
4483 if (S_ISDIR(sb.st_mode)) {
4484 error = got_error_msg(GOT_ERR_BAD_PATH,
4485 "adding directories requires -R option");
4486 goto done;
4491 error = got_worktree_schedule_add(worktree, &paths, add_progress,
4492 NULL, repo, no_ignores);
4493 done:
4494 if (repo)
4495 got_repo_close(repo);
4496 if (worktree)
4497 got_worktree_close(worktree);
4498 TAILQ_FOREACH(pe, &paths, entry)
4499 free((char *)pe->path);
4500 got_pathlist_free(&paths);
4501 free(cwd);
4502 return error;
4505 __dead static void
4506 usage_remove(void)
4508 fprintf(stderr, "usage: %s remove [-f] [-k] [-R] path ...\n",
4509 getprogname());
4510 exit(1);
4513 static const struct got_error *
4514 print_remove_status(void *arg, unsigned char status,
4515 unsigned char staged_status, const char *path)
4517 while (path[0] == '/')
4518 path++;
4519 if (status == GOT_STATUS_NONEXISTENT)
4520 return NULL;
4521 if (status == staged_status && (status == GOT_STATUS_DELETE))
4522 status = GOT_STATUS_NO_CHANGE;
4523 printf("%c%c %s\n", status, staged_status, path);
4524 return NULL;
4527 static const struct got_error *
4528 cmd_remove(int argc, char *argv[])
4530 const struct got_error *error = NULL;
4531 struct got_worktree *worktree = NULL;
4532 struct got_repository *repo = NULL;
4533 char *cwd = NULL;
4534 struct got_pathlist_head paths;
4535 struct got_pathlist_entry *pe;
4536 int ch, delete_local_mods = 0, can_recurse = 0, keep_on_disk = 0;
4538 TAILQ_INIT(&paths);
4540 while ((ch = getopt(argc, argv, "fkR")) != -1) {
4541 switch (ch) {
4542 case 'f':
4543 delete_local_mods = 1;
4544 break;
4545 case 'k':
4546 keep_on_disk = 1;
4547 break;
4548 case 'R':
4549 can_recurse = 1;
4550 break;
4551 default:
4552 usage_remove();
4553 /* NOTREACHED */
4557 argc -= optind;
4558 argv += optind;
4560 #ifndef PROFILE
4561 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
4562 NULL) == -1)
4563 err(1, "pledge");
4564 #endif
4565 if (argc < 1)
4566 usage_remove();
4568 cwd = getcwd(NULL, 0);
4569 if (cwd == NULL) {
4570 error = got_error_from_errno("getcwd");
4571 goto done;
4573 error = got_worktree_open(&worktree, cwd);
4574 if (error)
4575 goto done;
4577 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
4578 NULL);
4579 if (error)
4580 goto done;
4582 error = apply_unveil(got_repo_get_path(repo), 1,
4583 got_worktree_get_root_path(worktree));
4584 if (error)
4585 goto done;
4587 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
4588 if (error)
4589 goto done;
4591 if (!can_recurse) {
4592 char *ondisk_path;
4593 struct stat sb;
4594 TAILQ_FOREACH(pe, &paths, entry) {
4595 if (asprintf(&ondisk_path, "%s/%s",
4596 got_worktree_get_root_path(worktree),
4597 pe->path) == -1) {
4598 error = got_error_from_errno("asprintf");
4599 goto done;
4601 if (lstat(ondisk_path, &sb) == -1) {
4602 if (errno == ENOENT) {
4603 free(ondisk_path);
4604 continue;
4606 error = got_error_from_errno2("lstat",
4607 ondisk_path);
4608 free(ondisk_path);
4609 goto done;
4611 free(ondisk_path);
4612 if (S_ISDIR(sb.st_mode)) {
4613 error = got_error_msg(GOT_ERR_BAD_PATH,
4614 "removing directories requires -R option");
4615 goto done;
4620 error = got_worktree_schedule_delete(worktree, &paths,
4621 delete_local_mods, print_remove_status, NULL, repo, keep_on_disk);
4622 done:
4623 if (repo)
4624 got_repo_close(repo);
4625 if (worktree)
4626 got_worktree_close(worktree);
4627 TAILQ_FOREACH(pe, &paths, entry)
4628 free((char *)pe->path);
4629 got_pathlist_free(&paths);
4630 free(cwd);
4631 return error;
4634 __dead static void
4635 usage_revert(void)
4637 fprintf(stderr, "usage: %s revert [-p] [-F response-script] [-R] "
4638 "path ...\n", getprogname());
4639 exit(1);
4642 static const struct got_error *
4643 revert_progress(void *arg, unsigned char status, const char *path)
4645 if (status == GOT_STATUS_UNVERSIONED)
4646 return NULL;
4648 while (path[0] == '/')
4649 path++;
4650 printf("%c %s\n", status, path);
4651 return NULL;
4654 struct choose_patch_arg {
4655 FILE *patch_script_file;
4656 const char *action;
4659 static const struct got_error *
4660 show_change(unsigned char status, const char *path, FILE *patch_file, int n,
4661 int nchanges, const char *action)
4663 char *line = NULL;
4664 size_t linesize = 0;
4665 ssize_t linelen;
4667 switch (status) {
4668 case GOT_STATUS_ADD:
4669 printf("A %s\n%s this addition? [y/n] ", path, action);
4670 break;
4671 case GOT_STATUS_DELETE:
4672 printf("D %s\n%s this deletion? [y/n] ", path, action);
4673 break;
4674 case GOT_STATUS_MODIFY:
4675 if (fseek(patch_file, 0L, SEEK_SET) == -1)
4676 return got_error_from_errno("fseek");
4677 printf(GOT_COMMIT_SEP_STR);
4678 while ((linelen = getline(&line, &linesize, patch_file)) != -1)
4679 printf("%s", line);
4680 if (ferror(patch_file))
4681 return got_error_from_errno("getline");
4682 printf(GOT_COMMIT_SEP_STR);
4683 printf("M %s (change %d of %d)\n%s this change? [y/n/q] ",
4684 path, n, nchanges, action);
4685 break;
4686 default:
4687 return got_error_path(path, GOT_ERR_FILE_STATUS);
4690 return NULL;
4693 static const struct got_error *
4694 choose_patch(int *choice, void *arg, unsigned char status, const char *path,
4695 FILE *patch_file, int n, int nchanges)
4697 const struct got_error *err = NULL;
4698 char *line = NULL;
4699 size_t linesize = 0;
4700 ssize_t linelen;
4701 int resp = ' ';
4702 struct choose_patch_arg *a = arg;
4704 *choice = GOT_PATCH_CHOICE_NONE;
4706 if (a->patch_script_file) {
4707 char *nl;
4708 err = show_change(status, path, patch_file, n, nchanges,
4709 a->action);
4710 if (err)
4711 return err;
4712 linelen = getline(&line, &linesize, a->patch_script_file);
4713 if (linelen == -1) {
4714 if (ferror(a->patch_script_file))
4715 return got_error_from_errno("getline");
4716 return NULL;
4718 nl = strchr(line, '\n');
4719 if (nl)
4720 *nl = '\0';
4721 if (strcmp(line, "y") == 0) {
4722 *choice = GOT_PATCH_CHOICE_YES;
4723 printf("y\n");
4724 } else if (strcmp(line, "n") == 0) {
4725 *choice = GOT_PATCH_CHOICE_NO;
4726 printf("n\n");
4727 } else if (strcmp(line, "q") == 0 &&
4728 status == GOT_STATUS_MODIFY) {
4729 *choice = GOT_PATCH_CHOICE_QUIT;
4730 printf("q\n");
4731 } else
4732 printf("invalid response '%s'\n", line);
4733 free(line);
4734 return NULL;
4737 while (resp != 'y' && resp != 'n' && resp != 'q') {
4738 err = show_change(status, path, patch_file, n, nchanges,
4739 a->action);
4740 if (err)
4741 return err;
4742 resp = getchar();
4743 if (resp == '\n')
4744 resp = getchar();
4745 if (status == GOT_STATUS_MODIFY) {
4746 if (resp != 'y' && resp != 'n' && resp != 'q') {
4747 printf("invalid response '%c'\n", resp);
4748 resp = ' ';
4750 } else if (resp != 'y' && resp != 'n') {
4751 printf("invalid response '%c'\n", resp);
4752 resp = ' ';
4756 if (resp == 'y')
4757 *choice = GOT_PATCH_CHOICE_YES;
4758 else if (resp == 'n')
4759 *choice = GOT_PATCH_CHOICE_NO;
4760 else if (resp == 'q' && status == GOT_STATUS_MODIFY)
4761 *choice = GOT_PATCH_CHOICE_QUIT;
4763 return NULL;
4767 static const struct got_error *
4768 cmd_revert(int argc, char *argv[])
4770 const struct got_error *error = NULL;
4771 struct got_worktree *worktree = NULL;
4772 struct got_repository *repo = NULL;
4773 char *cwd = NULL, *path = NULL;
4774 struct got_pathlist_head paths;
4775 struct got_pathlist_entry *pe;
4776 int ch, can_recurse = 0, pflag = 0;
4777 FILE *patch_script_file = NULL;
4778 const char *patch_script_path = NULL;
4779 struct choose_patch_arg cpa;
4781 TAILQ_INIT(&paths);
4783 while ((ch = getopt(argc, argv, "pF:R")) != -1) {
4784 switch (ch) {
4785 case 'p':
4786 pflag = 1;
4787 break;
4788 case 'F':
4789 patch_script_path = optarg;
4790 break;
4791 case 'R':
4792 can_recurse = 1;
4793 break;
4794 default:
4795 usage_revert();
4796 /* NOTREACHED */
4800 argc -= optind;
4801 argv += optind;
4803 #ifndef PROFILE
4804 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
4805 "unveil", NULL) == -1)
4806 err(1, "pledge");
4807 #endif
4808 if (argc < 1)
4809 usage_revert();
4810 if (patch_script_path && !pflag)
4811 errx(1, "-F option can only be used together with -p option");
4813 cwd = getcwd(NULL, 0);
4814 if (cwd == NULL) {
4815 error = got_error_from_errno("getcwd");
4816 goto done;
4818 error = got_worktree_open(&worktree, cwd);
4819 if (error)
4820 goto done;
4822 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
4823 NULL);
4824 if (error != NULL)
4825 goto done;
4827 if (patch_script_path) {
4828 patch_script_file = fopen(patch_script_path, "r");
4829 if (patch_script_file == NULL) {
4830 error = got_error_from_errno2("fopen",
4831 patch_script_path);
4832 goto done;
4835 error = apply_unveil(got_repo_get_path(repo), 1,
4836 got_worktree_get_root_path(worktree));
4837 if (error)
4838 goto done;
4840 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
4841 if (error)
4842 goto done;
4844 if (!can_recurse) {
4845 char *ondisk_path;
4846 struct stat sb;
4847 TAILQ_FOREACH(pe, &paths, entry) {
4848 if (asprintf(&ondisk_path, "%s/%s",
4849 got_worktree_get_root_path(worktree),
4850 pe->path) == -1) {
4851 error = got_error_from_errno("asprintf");
4852 goto done;
4854 if (lstat(ondisk_path, &sb) == -1) {
4855 if (errno == ENOENT) {
4856 free(ondisk_path);
4857 continue;
4859 error = got_error_from_errno2("lstat",
4860 ondisk_path);
4861 free(ondisk_path);
4862 goto done;
4864 free(ondisk_path);
4865 if (S_ISDIR(sb.st_mode)) {
4866 error = got_error_msg(GOT_ERR_BAD_PATH,
4867 "reverting directories requires -R option");
4868 goto done;
4873 cpa.patch_script_file = patch_script_file;
4874 cpa.action = "revert";
4875 error = got_worktree_revert(worktree, &paths, revert_progress, NULL,
4876 pflag ? choose_patch : NULL, &cpa, repo);
4877 done:
4878 if (patch_script_file && fclose(patch_script_file) == EOF &&
4879 error == NULL)
4880 error = got_error_from_errno2("fclose", patch_script_path);
4881 if (repo)
4882 got_repo_close(repo);
4883 if (worktree)
4884 got_worktree_close(worktree);
4885 free(path);
4886 free(cwd);
4887 return error;
4890 __dead static void
4891 usage_commit(void)
4893 fprintf(stderr, "usage: %s commit [-m msg] [path ...]\n",
4894 getprogname());
4895 exit(1);
4898 struct collect_commit_logmsg_arg {
4899 const char *cmdline_log;
4900 const char *editor;
4901 const char *worktree_path;
4902 const char *branch_name;
4903 const char *repo_path;
4904 char *logmsg_path;
4908 static const struct got_error *
4909 collect_commit_logmsg(struct got_pathlist_head *commitable_paths, char **logmsg,
4910 void *arg)
4912 char *initial_content = NULL;
4913 struct got_pathlist_entry *pe;
4914 const struct got_error *err = NULL;
4915 char *template = NULL;
4916 struct collect_commit_logmsg_arg *a = arg;
4917 int fd;
4918 size_t len;
4920 /* if a message was specified on the command line, just use it */
4921 if (a->cmdline_log != NULL && strlen(a->cmdline_log) != 0) {
4922 len = strlen(a->cmdline_log) + 1;
4923 *logmsg = malloc(len + 1);
4924 if (*logmsg == NULL)
4925 return got_error_from_errno("malloc");
4926 strlcpy(*logmsg, a->cmdline_log, len);
4927 return NULL;
4930 if (asprintf(&template, "%s/logmsg", a->worktree_path) == -1)
4931 return got_error_from_errno("asprintf");
4933 if (asprintf(&initial_content,
4934 "\n# changes to be committed on branch %s:\n",
4935 a->branch_name) == -1)
4936 return got_error_from_errno("asprintf");
4938 err = got_opentemp_named_fd(&a->logmsg_path, &fd, template);
4939 if (err)
4940 goto done;
4942 dprintf(fd, initial_content);
4944 TAILQ_FOREACH(pe, commitable_paths, entry) {
4945 struct got_commitable *ct = pe->data;
4946 dprintf(fd, "# %c %s\n",
4947 got_commitable_get_status(ct),
4948 got_commitable_get_path(ct));
4950 close(fd);
4952 err = edit_logmsg(logmsg, a->editor, a->logmsg_path, initial_content);
4953 done:
4954 free(initial_content);
4955 free(template);
4957 /* Editor is done; we can now apply unveil(2) */
4958 if (err == NULL) {
4959 err = apply_unveil(a->repo_path, 0, a->worktree_path);
4960 if (err) {
4961 free(*logmsg);
4962 *logmsg = NULL;
4965 return err;
4968 static const struct got_error *
4969 cmd_commit(int argc, char *argv[])
4971 const struct got_error *error = NULL;
4972 struct got_worktree *worktree = NULL;
4973 struct got_repository *repo = NULL;
4974 char *cwd = NULL, *id_str = NULL;
4975 struct got_object_id *id = NULL;
4976 const char *logmsg = NULL;
4977 struct collect_commit_logmsg_arg cl_arg;
4978 char *gitconfig_path = NULL, *editor = NULL, *author = NULL;
4979 int ch, rebase_in_progress, histedit_in_progress, preserve_logmsg = 0;
4980 struct got_pathlist_head paths;
4982 TAILQ_INIT(&paths);
4983 cl_arg.logmsg_path = NULL;
4985 while ((ch = getopt(argc, argv, "m:")) != -1) {
4986 switch (ch) {
4987 case 'm':
4988 logmsg = optarg;
4989 break;
4990 default:
4991 usage_commit();
4992 /* NOTREACHED */
4996 argc -= optind;
4997 argv += optind;
4999 #ifndef PROFILE
5000 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
5001 "unveil", NULL) == -1)
5002 err(1, "pledge");
5003 #endif
5004 cwd = getcwd(NULL, 0);
5005 if (cwd == NULL) {
5006 error = got_error_from_errno("getcwd");
5007 goto done;
5009 error = got_worktree_open(&worktree, cwd);
5010 if (error)
5011 goto done;
5013 error = got_worktree_rebase_in_progress(&rebase_in_progress, worktree);
5014 if (error)
5015 goto done;
5016 if (rebase_in_progress) {
5017 error = got_error(GOT_ERR_REBASING);
5018 goto done;
5021 error = got_worktree_histedit_in_progress(&histedit_in_progress,
5022 worktree);
5023 if (error)
5024 goto done;
5026 error = get_gitconfig_path(&gitconfig_path);
5027 if (error)
5028 goto done;
5029 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
5030 gitconfig_path);
5031 if (error != NULL)
5032 goto done;
5034 error = get_author(&author, repo);
5035 if (error)
5036 return error;
5039 * unveil(2) traverses exec(2); if an editor is used we have
5040 * to apply unveil after the log message has been written.
5042 if (logmsg == NULL || strlen(logmsg) == 0)
5043 error = get_editor(&editor);
5044 else
5045 error = apply_unveil(got_repo_get_path(repo), 0,
5046 got_worktree_get_root_path(worktree));
5047 if (error)
5048 goto done;
5050 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
5051 if (error)
5052 goto done;
5054 cl_arg.editor = editor;
5055 cl_arg.cmdline_log = logmsg;
5056 cl_arg.worktree_path = got_worktree_get_root_path(worktree);
5057 cl_arg.branch_name = got_worktree_get_head_ref_name(worktree);
5058 if (!histedit_in_progress) {
5059 if (strncmp(cl_arg.branch_name, "refs/heads/", 11) != 0) {
5060 error = got_error(GOT_ERR_COMMIT_BRANCH);
5061 goto done;
5063 cl_arg.branch_name += 11;
5065 cl_arg.repo_path = got_repo_get_path(repo);
5066 error = got_worktree_commit(&id, worktree, &paths, author, NULL,
5067 collect_commit_logmsg, &cl_arg, print_status, NULL, repo);
5068 if (error) {
5069 if (error->code != GOT_ERR_COMMIT_MSG_EMPTY &&
5070 cl_arg.logmsg_path != NULL)
5071 preserve_logmsg = 1;
5072 goto done;
5075 error = got_object_id_str(&id_str, id);
5076 if (error)
5077 goto done;
5078 printf("Created commit %s\n", id_str);
5079 done:
5080 if (preserve_logmsg) {
5081 fprintf(stderr, "%s: log message preserved in %s\n",
5082 getprogname(), cl_arg.logmsg_path);
5083 } else if (cl_arg.logmsg_path && unlink(cl_arg.logmsg_path) == -1 &&
5084 error == NULL)
5085 error = got_error_from_errno2("unlink", cl_arg.logmsg_path);
5086 free(cl_arg.logmsg_path);
5087 if (repo)
5088 got_repo_close(repo);
5089 if (worktree)
5090 got_worktree_close(worktree);
5091 free(cwd);
5092 free(id_str);
5093 free(gitconfig_path);
5094 free(editor);
5095 free(author);
5096 return error;
5099 __dead static void
5100 usage_cherrypick(void)
5102 fprintf(stderr, "usage: %s cherrypick commit-id\n", getprogname());
5103 exit(1);
5106 static const struct got_error *
5107 cmd_cherrypick(int argc, char *argv[])
5109 const struct got_error *error = NULL;
5110 struct got_worktree *worktree = NULL;
5111 struct got_repository *repo = NULL;
5112 char *cwd = NULL, *commit_id_str = NULL;
5113 struct got_object_id *commit_id = NULL;
5114 struct got_commit_object *commit = NULL;
5115 struct got_object_qid *pid;
5116 struct got_reference *head_ref = NULL;
5117 int ch, did_something = 0;
5119 while ((ch = getopt(argc, argv, "")) != -1) {
5120 switch (ch) {
5121 default:
5122 usage_cherrypick();
5123 /* NOTREACHED */
5127 argc -= optind;
5128 argv += optind;
5130 #ifndef PROFILE
5131 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
5132 "unveil", NULL) == -1)
5133 err(1, "pledge");
5134 #endif
5135 if (argc != 1)
5136 usage_cherrypick();
5138 cwd = getcwd(NULL, 0);
5139 if (cwd == NULL) {
5140 error = got_error_from_errno("getcwd");
5141 goto done;
5143 error = got_worktree_open(&worktree, cwd);
5144 if (error)
5145 goto done;
5147 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
5148 NULL);
5149 if (error != NULL)
5150 goto done;
5152 error = apply_unveil(got_repo_get_path(repo), 0,
5153 got_worktree_get_root_path(worktree));
5154 if (error)
5155 goto done;
5157 error = got_repo_match_object_id_prefix(&commit_id, argv[0],
5158 GOT_OBJ_TYPE_COMMIT, repo);
5159 if (error != NULL) {
5160 struct got_reference *ref;
5161 if (error->code != GOT_ERR_BAD_OBJ_ID_STR)
5162 goto done;
5163 error = got_ref_open(&ref, repo, argv[0], 0);
5164 if (error != NULL)
5165 goto done;
5166 error = got_ref_resolve(&commit_id, repo, ref);
5167 got_ref_close(ref);
5168 if (error != NULL)
5169 goto done;
5171 error = got_object_id_str(&commit_id_str, commit_id);
5172 if (error)
5173 goto done;
5175 error = got_ref_open(&head_ref, repo,
5176 got_worktree_get_head_ref_name(worktree), 0);
5177 if (error != NULL)
5178 goto done;
5180 error = check_same_branch(commit_id, head_ref, NULL, repo);
5181 if (error) {
5182 if (error->code != GOT_ERR_ANCESTRY)
5183 goto done;
5184 error = NULL;
5185 } else {
5186 error = got_error(GOT_ERR_SAME_BRANCH);
5187 goto done;
5190 error = got_object_open_as_commit(&commit, repo, commit_id);
5191 if (error)
5192 goto done;
5193 pid = SIMPLEQ_FIRST(got_object_commit_get_parent_ids(commit));
5194 error = got_worktree_merge_files(worktree, pid ? pid->id : NULL,
5195 commit_id, repo, update_progress, &did_something, check_cancelled,
5196 NULL);
5197 if (error != NULL)
5198 goto done;
5200 if (did_something)
5201 printf("Merged commit %s\n", commit_id_str);
5202 done:
5203 if (commit)
5204 got_object_commit_close(commit);
5205 free(commit_id_str);
5206 if (head_ref)
5207 got_ref_close(head_ref);
5208 if (worktree)
5209 got_worktree_close(worktree);
5210 if (repo)
5211 got_repo_close(repo);
5212 return error;
5215 __dead static void
5216 usage_backout(void)
5218 fprintf(stderr, "usage: %s backout commit-id\n", getprogname());
5219 exit(1);
5222 static const struct got_error *
5223 cmd_backout(int argc, char *argv[])
5225 const struct got_error *error = NULL;
5226 struct got_worktree *worktree = NULL;
5227 struct got_repository *repo = NULL;
5228 char *cwd = NULL, *commit_id_str = NULL;
5229 struct got_object_id *commit_id = NULL;
5230 struct got_commit_object *commit = NULL;
5231 struct got_object_qid *pid;
5232 struct got_reference *head_ref = NULL;
5233 int ch, did_something = 0;
5235 while ((ch = getopt(argc, argv, "")) != -1) {
5236 switch (ch) {
5237 default:
5238 usage_backout();
5239 /* NOTREACHED */
5243 argc -= optind;
5244 argv += optind;
5246 #ifndef PROFILE
5247 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
5248 "unveil", NULL) == -1)
5249 err(1, "pledge");
5250 #endif
5251 if (argc != 1)
5252 usage_backout();
5254 cwd = getcwd(NULL, 0);
5255 if (cwd == NULL) {
5256 error = got_error_from_errno("getcwd");
5257 goto done;
5259 error = got_worktree_open(&worktree, cwd);
5260 if (error)
5261 goto done;
5263 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
5264 NULL);
5265 if (error != NULL)
5266 goto done;
5268 error = apply_unveil(got_repo_get_path(repo), 0,
5269 got_worktree_get_root_path(worktree));
5270 if (error)
5271 goto done;
5273 error = got_repo_match_object_id_prefix(&commit_id, argv[0],
5274 GOT_OBJ_TYPE_COMMIT, repo);
5275 if (error != NULL) {
5276 struct got_reference *ref;
5277 if (error->code != GOT_ERR_BAD_OBJ_ID_STR)
5278 goto done;
5279 error = got_ref_open(&ref, repo, argv[0], 0);
5280 if (error != NULL)
5281 goto done;
5282 error = got_ref_resolve(&commit_id, repo, ref);
5283 got_ref_close(ref);
5284 if (error != NULL)
5285 goto done;
5287 error = got_object_id_str(&commit_id_str, commit_id);
5288 if (error)
5289 goto done;
5291 error = got_ref_open(&head_ref, repo,
5292 got_worktree_get_head_ref_name(worktree), 0);
5293 if (error != NULL)
5294 goto done;
5296 error = check_same_branch(commit_id, head_ref, NULL, repo);
5297 if (error)
5298 goto done;
5300 error = got_object_open_as_commit(&commit, repo, commit_id);
5301 if (error)
5302 goto done;
5303 pid = SIMPLEQ_FIRST(got_object_commit_get_parent_ids(commit));
5304 if (pid == NULL) {
5305 error = got_error(GOT_ERR_ROOT_COMMIT);
5306 goto done;
5309 error = got_worktree_merge_files(worktree, commit_id, pid->id, repo,
5310 update_progress, &did_something, check_cancelled, NULL);
5311 if (error != NULL)
5312 goto done;
5314 if (did_something)
5315 printf("Backed out commit %s\n", commit_id_str);
5316 done:
5317 if (commit)
5318 got_object_commit_close(commit);
5319 free(commit_id_str);
5320 if (head_ref)
5321 got_ref_close(head_ref);
5322 if (worktree)
5323 got_worktree_close(worktree);
5324 if (repo)
5325 got_repo_close(repo);
5326 return error;
5329 __dead static void
5330 usage_rebase(void)
5332 fprintf(stderr, "usage: %s rebase [-a] | [-c] | branch\n",
5333 getprogname());
5334 exit(1);
5337 void
5338 trim_logmsg(char *logmsg, int limit)
5340 char *nl;
5341 size_t len;
5343 len = strlen(logmsg);
5344 if (len > limit)
5345 len = limit;
5346 logmsg[len] = '\0';
5347 nl = strchr(logmsg, '\n');
5348 if (nl)
5349 *nl = '\0';
5352 static const struct got_error *
5353 get_short_logmsg(char **logmsg, int limit, struct got_commit_object *commit)
5355 const struct got_error *err;
5356 char *logmsg0 = NULL;
5357 const char *s;
5359 err = got_object_commit_get_logmsg(&logmsg0, commit);
5360 if (err)
5361 return err;
5363 s = logmsg0;
5364 while (isspace((unsigned char)s[0]))
5365 s++;
5367 *logmsg = strdup(s);
5368 if (*logmsg == NULL) {
5369 err = got_error_from_errno("strdup");
5370 goto done;
5373 trim_logmsg(*logmsg, limit);
5374 done:
5375 free(logmsg0);
5376 return err;
5379 static const struct got_error *
5380 show_rebase_merge_conflict(struct got_object_id *id, struct got_repository *repo)
5382 const struct got_error *err;
5383 struct got_commit_object *commit = NULL;
5384 char *id_str = NULL, *logmsg = NULL;
5386 err = got_object_open_as_commit(&commit, repo, id);
5387 if (err)
5388 return err;
5390 err = got_object_id_str(&id_str, id);
5391 if (err)
5392 goto done;
5394 id_str[12] = '\0';
5396 err = get_short_logmsg(&logmsg, 42, commit);
5397 if (err)
5398 goto done;
5400 printf("%s -> merge conflict: %s\n", id_str, logmsg);
5401 done:
5402 free(id_str);
5403 got_object_commit_close(commit);
5404 free(logmsg);
5405 return err;
5408 static const struct got_error *
5409 show_rebase_progress(struct got_commit_object *commit,
5410 struct got_object_id *old_id, struct got_object_id *new_id)
5412 const struct got_error *err;
5413 char *old_id_str = NULL, *new_id_str = NULL, *logmsg = NULL;
5415 err = got_object_id_str(&old_id_str, old_id);
5416 if (err)
5417 goto done;
5419 if (new_id) {
5420 err = got_object_id_str(&new_id_str, new_id);
5421 if (err)
5422 goto done;
5425 old_id_str[12] = '\0';
5426 if (new_id_str)
5427 new_id_str[12] = '\0';
5429 err = get_short_logmsg(&logmsg, 42, commit);
5430 if (err)
5431 goto done;
5433 printf("%s -> %s: %s\n", old_id_str,
5434 new_id_str ? new_id_str : "no-op change", logmsg);
5435 done:
5436 free(old_id_str);
5437 free(new_id_str);
5438 free(logmsg);
5439 return err;
5442 static const struct got_error *
5443 rebase_progress(void *arg, unsigned char status, const char *path)
5445 unsigned char *rebase_status = arg;
5447 while (path[0] == '/')
5448 path++;
5449 printf("%c %s\n", status, path);
5451 if (*rebase_status == GOT_STATUS_CONFLICT)
5452 return NULL;
5453 if (status == GOT_STATUS_CONFLICT || status == GOT_STATUS_MERGE)
5454 *rebase_status = status;
5455 return NULL;
5458 static const struct got_error *
5459 rebase_complete(struct got_worktree *worktree, struct got_fileindex *fileindex,
5460 struct got_reference *branch, struct got_reference *new_base_branch,
5461 struct got_reference *tmp_branch, struct got_repository *repo)
5463 printf("Switching work tree to %s\n", got_ref_get_name(branch));
5464 return got_worktree_rebase_complete(worktree, fileindex,
5465 new_base_branch, tmp_branch, branch, repo);
5468 static const struct got_error *
5469 rebase_commit(struct got_pathlist_head *merged_paths,
5470 struct got_worktree *worktree, struct got_fileindex *fileindex,
5471 struct got_reference *tmp_branch,
5472 struct got_object_id *commit_id, struct got_repository *repo)
5474 const struct got_error *error;
5475 struct got_commit_object *commit;
5476 struct got_object_id *new_commit_id;
5478 error = got_object_open_as_commit(&commit, repo, commit_id);
5479 if (error)
5480 return error;
5482 error = got_worktree_rebase_commit(&new_commit_id, merged_paths,
5483 worktree, fileindex, tmp_branch, commit, commit_id, repo);
5484 if (error) {
5485 if (error->code != GOT_ERR_COMMIT_NO_CHANGES)
5486 goto done;
5487 error = show_rebase_progress(commit, commit_id, NULL);
5488 } else {
5489 error = show_rebase_progress(commit, commit_id, new_commit_id);
5490 free(new_commit_id);
5492 done:
5493 got_object_commit_close(commit);
5494 return error;
5497 struct check_path_prefix_arg {
5498 const char *path_prefix;
5499 size_t len;
5500 int errcode;
5503 static const struct got_error *
5504 check_path_prefix_in_diff(void *arg, struct got_blob_object *blob1,
5505 struct got_blob_object *blob2, struct got_object_id *id1,
5506 struct got_object_id *id2, const char *path1, const char *path2,
5507 mode_t mode1, mode_t mode2, struct got_repository *repo)
5509 struct check_path_prefix_arg *a = arg;
5511 if ((path1 && !got_path_is_child(path1, a->path_prefix, a->len)) ||
5512 (path2 && !got_path_is_child(path2, a->path_prefix, a->len)))
5513 return got_error(a->errcode);
5515 return NULL;
5518 static const struct got_error *
5519 check_path_prefix(struct got_object_id *parent_id,
5520 struct got_object_id *commit_id, const char *path_prefix,
5521 int errcode, struct got_repository *repo)
5523 const struct got_error *err;
5524 struct got_tree_object *tree1 = NULL, *tree2 = NULL;
5525 struct got_commit_object *commit = NULL, *parent_commit = NULL;
5526 struct check_path_prefix_arg cpp_arg;
5528 if (got_path_is_root_dir(path_prefix))
5529 return NULL;
5531 err = got_object_open_as_commit(&commit, repo, commit_id);
5532 if (err)
5533 goto done;
5535 err = got_object_open_as_commit(&parent_commit, repo, parent_id);
5536 if (err)
5537 goto done;
5539 err = got_object_open_as_tree(&tree1, repo,
5540 got_object_commit_get_tree_id(parent_commit));
5541 if (err)
5542 goto done;
5544 err = got_object_open_as_tree(&tree2, repo,
5545 got_object_commit_get_tree_id(commit));
5546 if (err)
5547 goto done;
5549 cpp_arg.path_prefix = path_prefix;
5550 while (cpp_arg.path_prefix[0] == '/')
5551 cpp_arg.path_prefix++;
5552 cpp_arg.len = strlen(cpp_arg.path_prefix);
5553 cpp_arg.errcode = errcode;
5554 err = got_diff_tree(tree1, tree2, "", "", repo,
5555 check_path_prefix_in_diff, &cpp_arg, 0);
5556 done:
5557 if (tree1)
5558 got_object_tree_close(tree1);
5559 if (tree2)
5560 got_object_tree_close(tree2);
5561 if (commit)
5562 got_object_commit_close(commit);
5563 if (parent_commit)
5564 got_object_commit_close(parent_commit);
5565 return err;
5568 static const struct got_error *
5569 collect_commits(struct got_object_id_queue *commits,
5570 struct got_object_id *initial_commit_id,
5571 struct got_object_id *iter_start_id, struct got_object_id *iter_stop_id,
5572 const char *path_prefix, int path_prefix_errcode,
5573 struct got_repository *repo)
5575 const struct got_error *err = NULL;
5576 struct got_commit_graph *graph = NULL;
5577 struct got_object_id *parent_id = NULL;
5578 struct got_object_qid *qid;
5579 struct got_object_id *commit_id = initial_commit_id;
5581 err = got_commit_graph_open(&graph, "/", 1);
5582 if (err)
5583 return err;
5585 err = got_commit_graph_iter_start(graph, iter_start_id, repo,
5586 check_cancelled, NULL);
5587 if (err)
5588 goto done;
5589 while (got_object_id_cmp(commit_id, iter_stop_id) != 0) {
5590 err = got_commit_graph_iter_next(&parent_id, graph, repo,
5591 check_cancelled, NULL);
5592 if (err) {
5593 if (err->code == GOT_ERR_ITER_COMPLETED) {
5594 err = got_error_msg(GOT_ERR_ANCESTRY,
5595 "ran out of commits to rebase before "
5596 "youngest common ancestor commit has "
5597 "been reached?!?");
5599 goto done;
5600 } else {
5601 err = check_path_prefix(parent_id, commit_id,
5602 path_prefix, path_prefix_errcode, repo);
5603 if (err)
5604 goto done;
5606 err = got_object_qid_alloc(&qid, commit_id);
5607 if (err)
5608 goto done;
5609 SIMPLEQ_INSERT_HEAD(commits, qid, entry);
5610 commit_id = parent_id;
5613 done:
5614 got_commit_graph_close(graph);
5615 return err;
5618 static const struct got_error *
5619 cmd_rebase(int argc, char *argv[])
5621 const struct got_error *error = NULL;
5622 struct got_worktree *worktree = NULL;
5623 struct got_repository *repo = NULL;
5624 struct got_fileindex *fileindex = NULL;
5625 char *cwd = NULL;
5626 struct got_reference *branch = NULL;
5627 struct got_reference *new_base_branch = NULL, *tmp_branch = NULL;
5628 struct got_object_id *commit_id = NULL, *parent_id = NULL;
5629 struct got_object_id *resume_commit_id = NULL;
5630 struct got_object_id *branch_head_commit_id = NULL, *yca_id = NULL;
5631 struct got_commit_object *commit = NULL;
5632 int ch, rebase_in_progress = 0, abort_rebase = 0, continue_rebase = 0;
5633 int histedit_in_progress = 0;
5634 unsigned char rebase_status = GOT_STATUS_NO_CHANGE;
5635 struct got_object_id_queue commits;
5636 struct got_pathlist_head merged_paths;
5637 const struct got_object_id_queue *parent_ids;
5638 struct got_object_qid *qid, *pid;
5640 SIMPLEQ_INIT(&commits);
5641 TAILQ_INIT(&merged_paths);
5643 while ((ch = getopt(argc, argv, "ac")) != -1) {
5644 switch (ch) {
5645 case 'a':
5646 abort_rebase = 1;
5647 break;
5648 case 'c':
5649 continue_rebase = 1;
5650 break;
5651 default:
5652 usage_rebase();
5653 /* NOTREACHED */
5657 argc -= optind;
5658 argv += optind;
5660 #ifndef PROFILE
5661 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
5662 "unveil", NULL) == -1)
5663 err(1, "pledge");
5664 #endif
5665 if (abort_rebase && continue_rebase)
5666 usage_rebase();
5667 else if (abort_rebase || continue_rebase) {
5668 if (argc != 0)
5669 usage_rebase();
5670 } else if (argc != 1)
5671 usage_rebase();
5673 cwd = getcwd(NULL, 0);
5674 if (cwd == NULL) {
5675 error = got_error_from_errno("getcwd");
5676 goto done;
5678 error = got_worktree_open(&worktree, cwd);
5679 if (error)
5680 goto done;
5682 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
5683 NULL);
5684 if (error != NULL)
5685 goto done;
5687 error = apply_unveil(got_repo_get_path(repo), 0,
5688 got_worktree_get_root_path(worktree));
5689 if (error)
5690 goto done;
5692 error = got_worktree_histedit_in_progress(&histedit_in_progress,
5693 worktree);
5694 if (error)
5695 goto done;
5696 if (histedit_in_progress) {
5697 error = got_error(GOT_ERR_HISTEDIT_BUSY);
5698 goto done;
5701 error = got_worktree_rebase_in_progress(&rebase_in_progress, worktree);
5702 if (error)
5703 goto done;
5705 if (abort_rebase) {
5706 int did_something;
5707 if (!rebase_in_progress) {
5708 error = got_error(GOT_ERR_NOT_REBASING);
5709 goto done;
5711 error = got_worktree_rebase_continue(&resume_commit_id,
5712 &new_base_branch, &tmp_branch, &branch, &fileindex,
5713 worktree, repo);
5714 if (error)
5715 goto done;
5716 printf("Switching work tree to %s\n",
5717 got_ref_get_symref_target(new_base_branch));
5718 error = got_worktree_rebase_abort(worktree, fileindex, repo,
5719 new_base_branch, update_progress, &did_something);
5720 if (error)
5721 goto done;
5722 printf("Rebase of %s aborted\n", got_ref_get_name(branch));
5723 goto done; /* nothing else to do */
5726 if (continue_rebase) {
5727 if (!rebase_in_progress) {
5728 error = got_error(GOT_ERR_NOT_REBASING);
5729 goto done;
5731 error = got_worktree_rebase_continue(&resume_commit_id,
5732 &new_base_branch, &tmp_branch, &branch, &fileindex,
5733 worktree, repo);
5734 if (error)
5735 goto done;
5737 error = rebase_commit(NULL, worktree, fileindex, tmp_branch,
5738 resume_commit_id, repo);
5739 if (error)
5740 goto done;
5742 yca_id = got_object_id_dup(resume_commit_id);
5743 if (yca_id == NULL) {
5744 error = got_error_from_errno("got_object_id_dup");
5745 goto done;
5747 } else {
5748 error = got_ref_open(&branch, repo, argv[0], 0);
5749 if (error != NULL)
5750 goto done;
5753 error = got_ref_resolve(&branch_head_commit_id, repo, branch);
5754 if (error)
5755 goto done;
5757 if (!continue_rebase) {
5758 struct got_object_id *base_commit_id;
5760 base_commit_id = got_worktree_get_base_commit_id(worktree);
5761 error = got_commit_graph_find_youngest_common_ancestor(&yca_id,
5762 base_commit_id, branch_head_commit_id, repo,
5763 check_cancelled, NULL);
5764 if (error)
5765 goto done;
5766 if (yca_id == NULL) {
5767 error = got_error_msg(GOT_ERR_ANCESTRY,
5768 "specified branch shares no common ancestry "
5769 "with work tree's branch");
5770 goto done;
5773 error = check_same_branch(base_commit_id, branch, yca_id, repo);
5774 if (error) {
5775 if (error->code != GOT_ERR_ANCESTRY)
5776 goto done;
5777 error = NULL;
5778 } else {
5779 error = got_error_msg(GOT_ERR_SAME_BRANCH,
5780 "specified branch resolves to a commit which "
5781 "is already contained in work tree's branch");
5782 goto done;
5784 error = got_worktree_rebase_prepare(&new_base_branch,
5785 &tmp_branch, &fileindex, worktree, branch, repo);
5786 if (error)
5787 goto done;
5790 commit_id = branch_head_commit_id;
5791 error = got_object_open_as_commit(&commit, repo, commit_id);
5792 if (error)
5793 goto done;
5795 parent_ids = got_object_commit_get_parent_ids(commit);
5796 pid = SIMPLEQ_FIRST(parent_ids);
5797 if (pid == NULL) {
5798 if (!continue_rebase) {
5799 int did_something;
5800 error = got_worktree_rebase_abort(worktree, fileindex,
5801 repo, new_base_branch, update_progress,
5802 &did_something);
5803 if (error)
5804 goto done;
5805 printf("Rebase of %s aborted\n",
5806 got_ref_get_name(branch));
5808 error = got_error(GOT_ERR_EMPTY_REBASE);
5809 goto done;
5811 error = collect_commits(&commits, commit_id, pid->id,
5812 yca_id, got_worktree_get_path_prefix(worktree),
5813 GOT_ERR_REBASE_PATH, repo);
5814 got_object_commit_close(commit);
5815 commit = NULL;
5816 if (error)
5817 goto done;
5819 if (SIMPLEQ_EMPTY(&commits)) {
5820 if (continue_rebase) {
5821 error = rebase_complete(worktree, fileindex,
5822 branch, new_base_branch, tmp_branch, repo);
5823 goto done;
5824 } else {
5825 /* Fast-forward the reference of the branch. */
5826 struct got_object_id *new_head_commit_id;
5827 char *id_str;
5828 error = got_ref_resolve(&new_head_commit_id, repo,
5829 new_base_branch);
5830 if (error)
5831 goto done;
5832 error = got_object_id_str(&id_str, new_head_commit_id);
5833 printf("Forwarding %s to commit %s\n",
5834 got_ref_get_name(branch), id_str);
5835 free(id_str);
5836 error = got_ref_change_ref(branch,
5837 new_head_commit_id);
5838 if (error)
5839 goto done;
5843 pid = NULL;
5844 SIMPLEQ_FOREACH(qid, &commits, entry) {
5845 commit_id = qid->id;
5846 parent_id = pid ? pid->id : yca_id;
5847 pid = qid;
5849 error = got_worktree_rebase_merge_files(&merged_paths,
5850 worktree, fileindex, parent_id, commit_id, repo,
5851 rebase_progress, &rebase_status, check_cancelled, NULL);
5852 if (error)
5853 goto done;
5855 if (rebase_status == GOT_STATUS_CONFLICT) {
5856 error = show_rebase_merge_conflict(qid->id, repo);
5857 if (error)
5858 goto done;
5859 got_worktree_rebase_pathlist_free(&merged_paths);
5860 break;
5863 error = rebase_commit(&merged_paths, worktree, fileindex,
5864 tmp_branch, commit_id, repo);
5865 got_worktree_rebase_pathlist_free(&merged_paths);
5866 if (error)
5867 goto done;
5870 if (rebase_status == GOT_STATUS_CONFLICT) {
5871 error = got_worktree_rebase_postpone(worktree, fileindex);
5872 if (error)
5873 goto done;
5874 error = got_error_msg(GOT_ERR_CONFLICTS,
5875 "conflicts must be resolved before rebasing can continue");
5876 } else
5877 error = rebase_complete(worktree, fileindex, branch,
5878 new_base_branch, tmp_branch, repo);
5879 done:
5880 got_object_id_queue_free(&commits);
5881 free(branch_head_commit_id);
5882 free(resume_commit_id);
5883 free(yca_id);
5884 if (commit)
5885 got_object_commit_close(commit);
5886 if (branch)
5887 got_ref_close(branch);
5888 if (new_base_branch)
5889 got_ref_close(new_base_branch);
5890 if (tmp_branch)
5891 got_ref_close(tmp_branch);
5892 if (worktree)
5893 got_worktree_close(worktree);
5894 if (repo)
5895 got_repo_close(repo);
5896 return error;
5899 __dead static void
5900 usage_histedit(void)
5902 fprintf(stderr, "usage: %s histedit [-a] [-c] [-F histedit-script] [-m]\n",
5903 getprogname());
5904 exit(1);
5907 #define GOT_HISTEDIT_PICK 'p'
5908 #define GOT_HISTEDIT_EDIT 'e'
5909 #define GOT_HISTEDIT_FOLD 'f'
5910 #define GOT_HISTEDIT_DROP 'd'
5911 #define GOT_HISTEDIT_MESG 'm'
5913 static struct got_histedit_cmd {
5914 unsigned char code;
5915 const char *name;
5916 const char *desc;
5917 } got_histedit_cmds[] = {
5918 { GOT_HISTEDIT_PICK, "pick", "use commit" },
5919 { GOT_HISTEDIT_EDIT, "edit", "use commit but stop for amending" },
5920 { GOT_HISTEDIT_FOLD, "fold", "combine with next commit that will "
5921 "be used" },
5922 { GOT_HISTEDIT_DROP, "drop", "remove commit from history" },
5923 { GOT_HISTEDIT_MESG, "mesg",
5924 "single-line log message for commit above (open editor if empty)" },
5927 struct got_histedit_list_entry {
5928 TAILQ_ENTRY(got_histedit_list_entry) entry;
5929 struct got_object_id *commit_id;
5930 const struct got_histedit_cmd *cmd;
5931 char *logmsg;
5933 TAILQ_HEAD(got_histedit_list, got_histedit_list_entry);
5935 static const struct got_error *
5936 histedit_write_commit(struct got_object_id *commit_id, const char *cmdname,
5937 FILE *f, struct got_repository *repo)
5939 const struct got_error *err = NULL;
5940 char *logmsg = NULL, *id_str = NULL;
5941 struct got_commit_object *commit = NULL;
5942 int n;
5944 err = got_object_open_as_commit(&commit, repo, commit_id);
5945 if (err)
5946 goto done;
5948 err = get_short_logmsg(&logmsg, 34, commit);
5949 if (err)
5950 goto done;
5952 err = got_object_id_str(&id_str, commit_id);
5953 if (err)
5954 goto done;
5956 n = fprintf(f, "%s %s %s\n", cmdname, id_str, logmsg);
5957 if (n < 0)
5958 err = got_ferror(f, GOT_ERR_IO);
5959 done:
5960 if (commit)
5961 got_object_commit_close(commit);
5962 free(id_str);
5963 free(logmsg);
5964 return err;
5967 static const struct got_error *
5968 histedit_write_commit_list(struct got_object_id_queue *commits,
5969 FILE *f, int edit_logmsg_only, struct got_repository *repo)
5971 const struct got_error *err = NULL;
5972 struct got_object_qid *qid;
5974 if (SIMPLEQ_EMPTY(commits))
5975 return got_error(GOT_ERR_EMPTY_HISTEDIT);
5977 SIMPLEQ_FOREACH(qid, commits, entry) {
5978 err = histedit_write_commit(qid->id, got_histedit_cmds[0].name,
5979 f, repo);
5980 if (err)
5981 break;
5982 if (edit_logmsg_only) {
5983 int n = fprintf(f, "%c\n", GOT_HISTEDIT_MESG);
5984 if (n < 0) {
5985 err = got_ferror(f, GOT_ERR_IO);
5986 break;
5991 return err;
5994 static const struct got_error *
5995 write_cmd_list(FILE *f, const char *branch_name,
5996 struct got_object_id_queue *commits)
5998 const struct got_error *err = NULL;
5999 int n, i;
6000 char *id_str;
6001 struct got_object_qid *qid;
6003 qid = SIMPLEQ_FIRST(commits);
6004 err = got_object_id_str(&id_str, qid->id);
6005 if (err)
6006 return err;
6008 n = fprintf(f,
6009 "# Editing the history of branch '%s' starting at\n"
6010 "# commit %s\n"
6011 "# Commits will be processed in order from top to "
6012 "bottom of this file.\n", branch_name, id_str);
6013 if (n < 0) {
6014 err = got_ferror(f, GOT_ERR_IO);
6015 goto done;
6018 n = fprintf(f, "# Available histedit commands:\n");
6019 if (n < 0) {
6020 err = got_ferror(f, GOT_ERR_IO);
6021 goto done;
6024 for (i = 0; i < nitems(got_histedit_cmds); i++) {
6025 struct got_histedit_cmd *cmd = &got_histedit_cmds[i];
6026 n = fprintf(f, "# %s (%c): %s\n", cmd->name, cmd->code,
6027 cmd->desc);
6028 if (n < 0) {
6029 err = got_ferror(f, GOT_ERR_IO);
6030 break;
6033 done:
6034 free(id_str);
6035 return err;
6038 static const struct got_error *
6039 histedit_syntax_error(int lineno)
6041 static char msg[42];
6042 int ret;
6044 ret = snprintf(msg, sizeof(msg), "histedit syntax error on line %d",
6045 lineno);
6046 if (ret == -1 || ret >= sizeof(msg))
6047 return got_error(GOT_ERR_HISTEDIT_SYNTAX);
6049 return got_error_msg(GOT_ERR_HISTEDIT_SYNTAX, msg);
6052 static const struct got_error *
6053 append_folded_commit_msg(char **new_msg, struct got_histedit_list_entry *hle,
6054 char *logmsg, struct got_repository *repo)
6056 const struct got_error *err;
6057 struct got_commit_object *folded_commit = NULL;
6058 char *id_str, *folded_logmsg = NULL;
6060 err = got_object_id_str(&id_str, hle->commit_id);
6061 if (err)
6062 return err;
6064 err = got_object_open_as_commit(&folded_commit, repo, hle->commit_id);
6065 if (err)
6066 goto done;
6068 err = got_object_commit_get_logmsg(&folded_logmsg, folded_commit);
6069 if (err)
6070 goto done;
6071 if (asprintf(new_msg, "%s%s# log message of folded commit %s: %s",
6072 logmsg ? logmsg : "", logmsg ? "\n" : "", id_str,
6073 folded_logmsg) == -1) {
6074 err = got_error_from_errno("asprintf");
6076 done:
6077 if (folded_commit)
6078 got_object_commit_close(folded_commit);
6079 free(id_str);
6080 free(folded_logmsg);
6081 return err;
6084 static struct got_histedit_list_entry *
6085 get_folded_commits(struct got_histedit_list_entry *hle)
6087 struct got_histedit_list_entry *prev, *folded = NULL;
6089 prev = TAILQ_PREV(hle, got_histedit_list, entry);
6090 while (prev && (prev->cmd->code == GOT_HISTEDIT_FOLD ||
6091 prev->cmd->code == GOT_HISTEDIT_DROP)) {
6092 if (prev->cmd->code == GOT_HISTEDIT_FOLD)
6093 folded = prev;
6094 prev = TAILQ_PREV(prev, got_histedit_list, entry);
6097 return folded;
6100 static const struct got_error *
6101 histedit_edit_logmsg(struct got_histedit_list_entry *hle,
6102 struct got_repository *repo)
6104 char *logmsg_path = NULL, *id_str = NULL, *orig_logmsg = NULL;
6105 char *logmsg = NULL, *new_msg = NULL, *editor = NULL;
6106 const struct got_error *err = NULL;
6107 struct got_commit_object *commit = NULL;
6108 int fd;
6109 struct got_histedit_list_entry *folded = NULL;
6111 err = got_object_open_as_commit(&commit, repo, hle->commit_id);
6112 if (err)
6113 return err;
6115 folded = get_folded_commits(hle);
6116 if (folded) {
6117 while (folded != hle) {
6118 if (folded->cmd->code == GOT_HISTEDIT_DROP) {
6119 folded = TAILQ_NEXT(folded, entry);
6120 continue;
6122 err = append_folded_commit_msg(&new_msg, folded,
6123 logmsg, repo);
6124 if (err)
6125 goto done;
6126 free(logmsg);
6127 logmsg = new_msg;
6128 folded = TAILQ_NEXT(folded, entry);
6132 err = got_object_id_str(&id_str, hle->commit_id);
6133 if (err)
6134 goto done;
6135 err = got_object_commit_get_logmsg(&orig_logmsg, commit);
6136 if (err)
6137 goto done;
6138 if (asprintf(&new_msg,
6139 "%s\n# original log message of commit %s: %s",
6140 logmsg ? logmsg : "", id_str, orig_logmsg) == -1) {
6141 err = got_error_from_errno("asprintf");
6142 goto done;
6144 free(logmsg);
6145 logmsg = new_msg;
6147 err = got_object_id_str(&id_str, hle->commit_id);
6148 if (err)
6149 goto done;
6151 err = got_opentemp_named_fd(&logmsg_path, &fd,
6152 GOT_TMPDIR_STR "/got-logmsg");
6153 if (err)
6154 goto done;
6156 dprintf(fd, logmsg);
6157 close(fd);
6159 err = get_editor(&editor);
6160 if (err)
6161 goto done;
6163 err = edit_logmsg(&hle->logmsg, editor, logmsg_path, logmsg);
6164 if (err) {
6165 if (err->code != GOT_ERR_COMMIT_MSG_EMPTY)
6166 goto done;
6167 err = got_object_commit_get_logmsg(&hle->logmsg, commit);
6169 done:
6170 if (logmsg_path && unlink(logmsg_path) != 0 && err == NULL)
6171 err = got_error_from_errno2("unlink", logmsg_path);
6172 free(logmsg_path);
6173 free(logmsg);
6174 free(orig_logmsg);
6175 free(editor);
6176 if (commit)
6177 got_object_commit_close(commit);
6178 return err;
6181 static const struct got_error *
6182 histedit_parse_list(struct got_histedit_list *histedit_cmds,
6183 FILE *f, struct got_repository *repo)
6185 const struct got_error *err = NULL;
6186 char *line = NULL, *p, *end;
6187 size_t size;
6188 ssize_t len;
6189 int lineno = 0, i;
6190 const struct got_histedit_cmd *cmd;
6191 struct got_object_id *commit_id = NULL;
6192 struct got_histedit_list_entry *hle = NULL;
6194 for (;;) {
6195 len = getline(&line, &size, f);
6196 if (len == -1) {
6197 const struct got_error *getline_err;
6198 if (feof(f))
6199 break;
6200 getline_err = got_error_from_errno("getline");
6201 err = got_ferror(f, getline_err->code);
6202 break;
6204 lineno++;
6205 p = line;
6206 while (isspace((unsigned char)p[0]))
6207 p++;
6208 if (p[0] == '#' || p[0] == '\0') {
6209 free(line);
6210 line = NULL;
6211 continue;
6213 cmd = NULL;
6214 for (i = 0; i < nitems(got_histedit_cmds); i++) {
6215 cmd = &got_histedit_cmds[i];
6216 if (strncmp(cmd->name, p, strlen(cmd->name)) == 0 &&
6217 isspace((unsigned char)p[strlen(cmd->name)])) {
6218 p += strlen(cmd->name);
6219 break;
6221 if (p[0] == cmd->code && isspace((unsigned char)p[1])) {
6222 p++;
6223 break;
6226 if (i == nitems(got_histedit_cmds)) {
6227 err = histedit_syntax_error(lineno);
6228 break;
6230 while (isspace((unsigned char)p[0]))
6231 p++;
6232 if (cmd->code == GOT_HISTEDIT_MESG) {
6233 if (hle == NULL || hle->logmsg != NULL) {
6234 err = got_error(GOT_ERR_HISTEDIT_CMD);
6235 break;
6237 if (p[0] == '\0') {
6238 err = histedit_edit_logmsg(hle, repo);
6239 if (err)
6240 break;
6241 } else {
6242 hle->logmsg = strdup(p);
6243 if (hle->logmsg == NULL) {
6244 err = got_error_from_errno("strdup");
6245 break;
6248 free(line);
6249 line = NULL;
6250 continue;
6251 } else {
6252 end = p;
6253 while (end[0] && !isspace((unsigned char)end[0]))
6254 end++;
6255 *end = '\0';
6257 err = got_object_resolve_id_str(&commit_id, repo, p);
6258 if (err) {
6259 /* override error code */
6260 err = histedit_syntax_error(lineno);
6261 break;
6264 hle = malloc(sizeof(*hle));
6265 if (hle == NULL) {
6266 err = got_error_from_errno("malloc");
6267 break;
6269 hle->cmd = cmd;
6270 hle->commit_id = commit_id;
6271 hle->logmsg = NULL;
6272 commit_id = NULL;
6273 free(line);
6274 line = NULL;
6275 TAILQ_INSERT_TAIL(histedit_cmds, hle, entry);
6278 free(line);
6279 free(commit_id);
6280 return err;
6283 static const struct got_error *
6284 histedit_check_script(struct got_histedit_list *histedit_cmds,
6285 struct got_object_id_queue *commits, struct got_repository *repo)
6287 const struct got_error *err = NULL;
6288 struct got_object_qid *qid;
6289 struct got_histedit_list_entry *hle;
6290 static char msg[92];
6291 char *id_str;
6293 if (TAILQ_EMPTY(histedit_cmds))
6294 return got_error_msg(GOT_ERR_EMPTY_HISTEDIT,
6295 "histedit script contains no commands");
6296 if (SIMPLEQ_EMPTY(commits))
6297 return got_error(GOT_ERR_EMPTY_HISTEDIT);
6299 TAILQ_FOREACH(hle, histedit_cmds, entry) {
6300 struct got_histedit_list_entry *hle2;
6301 TAILQ_FOREACH(hle2, histedit_cmds, entry) {
6302 if (hle == hle2)
6303 continue;
6304 if (got_object_id_cmp(hle->commit_id,
6305 hle2->commit_id) != 0)
6306 continue;
6307 err = got_object_id_str(&id_str, hle->commit_id);
6308 if (err)
6309 return err;
6310 snprintf(msg, sizeof(msg), "commit %s is listed "
6311 "more than once in histedit script", id_str);
6312 free(id_str);
6313 return got_error_msg(GOT_ERR_HISTEDIT_CMD, msg);
6317 SIMPLEQ_FOREACH(qid, commits, entry) {
6318 TAILQ_FOREACH(hle, histedit_cmds, entry) {
6319 if (got_object_id_cmp(qid->id, hle->commit_id) == 0)
6320 break;
6322 if (hle == NULL) {
6323 err = got_object_id_str(&id_str, qid->id);
6324 if (err)
6325 return err;
6326 snprintf(msg, sizeof(msg),
6327 "commit %s missing from histedit script", id_str);
6328 free(id_str);
6329 return got_error_msg(GOT_ERR_HISTEDIT_CMD, msg);
6333 hle = TAILQ_LAST(histedit_cmds, got_histedit_list);
6334 if (hle && hle->cmd->code == GOT_HISTEDIT_FOLD)
6335 return got_error_msg(GOT_ERR_HISTEDIT_CMD,
6336 "last commit in histedit script cannot be folded");
6338 return NULL;
6341 static const struct got_error *
6342 histedit_run_editor(struct got_histedit_list *histedit_cmds,
6343 const char *path, struct got_object_id_queue *commits,
6344 struct got_repository *repo)
6346 const struct got_error *err = NULL;
6347 char *editor;
6348 FILE *f = NULL;
6350 err = get_editor(&editor);
6351 if (err)
6352 return err;
6354 if (spawn_editor(editor, path) == -1) {
6355 err = got_error_from_errno("failed spawning editor");
6356 goto done;
6359 f = fopen(path, "r");
6360 if (f == NULL) {
6361 err = got_error_from_errno("fopen");
6362 goto done;
6364 err = histedit_parse_list(histedit_cmds, f, repo);
6365 if (err)
6366 goto done;
6368 err = histedit_check_script(histedit_cmds, commits, repo);
6369 done:
6370 if (f && fclose(f) != 0 && err == NULL)
6371 err = got_error_from_errno("fclose");
6372 free(editor);
6373 return err;
6376 static const struct got_error *
6377 histedit_edit_list_retry(struct got_histedit_list *, const struct got_error *,
6378 struct got_object_id_queue *, const char *, const char *,
6379 struct got_repository *);
6381 static const struct got_error *
6382 histedit_edit_script(struct got_histedit_list *histedit_cmds,
6383 struct got_object_id_queue *commits, const char *branch_name,
6384 int edit_logmsg_only, struct got_repository *repo)
6386 const struct got_error *err;
6387 FILE *f = NULL;
6388 char *path = NULL;
6390 err = got_opentemp_named(&path, &f, "got-histedit");
6391 if (err)
6392 return err;
6394 err = write_cmd_list(f, branch_name, commits);
6395 if (err)
6396 goto done;
6398 err = histedit_write_commit_list(commits, f, edit_logmsg_only, repo);
6399 if (err)
6400 goto done;
6402 if (edit_logmsg_only) {
6403 rewind(f);
6404 err = histedit_parse_list(histedit_cmds, f, repo);
6405 } else {
6406 if (fclose(f) != 0) {
6407 err = got_error_from_errno("fclose");
6408 goto done;
6410 f = NULL;
6411 err = histedit_run_editor(histedit_cmds, path, commits, repo);
6412 if (err) {
6413 if (err->code != GOT_ERR_HISTEDIT_SYNTAX &&
6414 err->code != GOT_ERR_HISTEDIT_CMD)
6415 goto done;
6416 err = histedit_edit_list_retry(histedit_cmds, err,
6417 commits, path, branch_name, repo);
6420 done:
6421 if (f && fclose(f) != 0 && err == NULL)
6422 err = got_error_from_errno("fclose");
6423 if (path && unlink(path) != 0 && err == NULL)
6424 err = got_error_from_errno2("unlink", path);
6425 free(path);
6426 return err;
6429 static const struct got_error *
6430 histedit_save_list(struct got_histedit_list *histedit_cmds,
6431 struct got_worktree *worktree, struct got_repository *repo)
6433 const struct got_error *err = NULL;
6434 char *path = NULL;
6435 FILE *f = NULL;
6436 struct got_histedit_list_entry *hle;
6437 struct got_commit_object *commit = NULL;
6439 err = got_worktree_get_histedit_script_path(&path, worktree);
6440 if (err)
6441 return err;
6443 f = fopen(path, "w");
6444 if (f == NULL) {
6445 err = got_error_from_errno2("fopen", path);
6446 goto done;
6448 TAILQ_FOREACH(hle, histedit_cmds, entry) {
6449 err = histedit_write_commit(hle->commit_id, hle->cmd->name, f,
6450 repo);
6451 if (err)
6452 break;
6454 if (hle->logmsg) {
6455 int n = fprintf(f, "%c %s\n",
6456 GOT_HISTEDIT_MESG, hle->logmsg);
6457 if (n < 0) {
6458 err = got_ferror(f, GOT_ERR_IO);
6459 break;
6463 done:
6464 if (f && fclose(f) != 0 && err == NULL)
6465 err = got_error_from_errno("fclose");
6466 free(path);
6467 if (commit)
6468 got_object_commit_close(commit);
6469 return err;
6472 void
6473 histedit_free_list(struct got_histedit_list *histedit_cmds)
6475 struct got_histedit_list_entry *hle;
6477 while ((hle = TAILQ_FIRST(histedit_cmds))) {
6478 TAILQ_REMOVE(histedit_cmds, hle, entry);
6479 free(hle);
6483 static const struct got_error *
6484 histedit_load_list(struct got_histedit_list *histedit_cmds,
6485 const char *path, struct got_repository *repo)
6487 const struct got_error *err = NULL;
6488 FILE *f = NULL;
6490 f = fopen(path, "r");
6491 if (f == NULL) {
6492 err = got_error_from_errno2("fopen", path);
6493 goto done;
6496 err = histedit_parse_list(histedit_cmds, f, repo);
6497 done:
6498 if (f && fclose(f) != 0 && err == NULL)
6499 err = got_error_from_errno("fclose");
6500 return err;
6503 static const struct got_error *
6504 histedit_edit_list_retry(struct got_histedit_list *histedit_cmds,
6505 const struct got_error *edit_err, struct got_object_id_queue *commits,
6506 const char *path, const char *branch_name, struct got_repository *repo)
6508 const struct got_error *err = NULL, *prev_err = edit_err;
6509 int resp = ' ';
6511 while (resp != 'c' && resp != 'r' && resp != 'a') {
6512 printf("%s: %s\n(c)ontinue editing, (r)estart editing, "
6513 "or (a)bort: ", getprogname(), prev_err->msg);
6514 resp = getchar();
6515 if (resp == '\n')
6516 resp = getchar();
6517 if (resp == 'c') {
6518 histedit_free_list(histedit_cmds);
6519 err = histedit_run_editor(histedit_cmds, path, commits,
6520 repo);
6521 if (err) {
6522 if (err->code != GOT_ERR_HISTEDIT_SYNTAX &&
6523 err->code != GOT_ERR_HISTEDIT_CMD)
6524 break;
6525 prev_err = err;
6526 resp = ' ';
6527 continue;
6529 break;
6530 } else if (resp == 'r') {
6531 histedit_free_list(histedit_cmds);
6532 err = histedit_edit_script(histedit_cmds,
6533 commits, branch_name, 0, repo);
6534 if (err) {
6535 if (err->code != GOT_ERR_HISTEDIT_SYNTAX &&
6536 err->code != GOT_ERR_HISTEDIT_CMD)
6537 break;
6538 prev_err = err;
6539 resp = ' ';
6540 continue;
6542 break;
6543 } else if (resp == 'a') {
6544 err = got_error(GOT_ERR_HISTEDIT_CANCEL);
6545 break;
6546 } else
6547 printf("invalid response '%c'\n", resp);
6550 return err;
6553 static const struct got_error *
6554 histedit_complete(struct got_worktree *worktree,
6555 struct got_fileindex *fileindex, struct got_reference *tmp_branch,
6556 struct got_reference *branch, struct got_repository *repo)
6558 printf("Switching work tree to %s\n",
6559 got_ref_get_symref_target(branch));
6560 return got_worktree_histedit_complete(worktree, fileindex, tmp_branch,
6561 branch, repo);
6564 static const struct got_error *
6565 show_histedit_progress(struct got_commit_object *commit,
6566 struct got_histedit_list_entry *hle, struct got_object_id *new_id)
6568 const struct got_error *err;
6569 char *old_id_str = NULL, *new_id_str = NULL, *logmsg = NULL;
6571 err = got_object_id_str(&old_id_str, hle->commit_id);
6572 if (err)
6573 goto done;
6575 if (new_id) {
6576 err = got_object_id_str(&new_id_str, new_id);
6577 if (err)
6578 goto done;
6581 old_id_str[12] = '\0';
6582 if (new_id_str)
6583 new_id_str[12] = '\0';
6585 if (hle->logmsg) {
6586 logmsg = strdup(hle->logmsg);
6587 if (logmsg == NULL) {
6588 err = got_error_from_errno("strdup");
6589 goto done;
6591 trim_logmsg(logmsg, 42);
6592 } else {
6593 err = get_short_logmsg(&logmsg, 42, commit);
6594 if (err)
6595 goto done;
6598 switch (hle->cmd->code) {
6599 case GOT_HISTEDIT_PICK:
6600 case GOT_HISTEDIT_EDIT:
6601 printf("%s -> %s: %s\n", old_id_str,
6602 new_id_str ? new_id_str : "no-op change", logmsg);
6603 break;
6604 case GOT_HISTEDIT_DROP:
6605 case GOT_HISTEDIT_FOLD:
6606 printf("%s -> %s commit: %s\n", old_id_str, hle->cmd->name,
6607 logmsg);
6608 break;
6609 default:
6610 break;
6612 done:
6613 free(old_id_str);
6614 free(new_id_str);
6615 return err;
6618 static const struct got_error *
6619 histedit_commit(struct got_pathlist_head *merged_paths,
6620 struct got_worktree *worktree, struct got_fileindex *fileindex,
6621 struct got_reference *tmp_branch, struct got_histedit_list_entry *hle,
6622 struct got_repository *repo)
6624 const struct got_error *err;
6625 struct got_commit_object *commit;
6626 struct got_object_id *new_commit_id;
6628 if ((hle->cmd->code == GOT_HISTEDIT_EDIT || get_folded_commits(hle))
6629 && hle->logmsg == NULL) {
6630 err = histedit_edit_logmsg(hle, repo);
6631 if (err)
6632 return err;
6635 err = got_object_open_as_commit(&commit, repo, hle->commit_id);
6636 if (err)
6637 return err;
6639 err = got_worktree_histedit_commit(&new_commit_id, merged_paths,
6640 worktree, fileindex, tmp_branch, commit, hle->commit_id,
6641 hle->logmsg, repo);
6642 if (err) {
6643 if (err->code != GOT_ERR_COMMIT_NO_CHANGES)
6644 goto done;
6645 err = show_histedit_progress(commit, hle, NULL);
6646 } else {
6647 err = show_histedit_progress(commit, hle, new_commit_id);
6648 free(new_commit_id);
6650 done:
6651 got_object_commit_close(commit);
6652 return err;
6655 static const struct got_error *
6656 histedit_skip_commit(struct got_histedit_list_entry *hle,
6657 struct got_worktree *worktree, struct got_repository *repo)
6659 const struct got_error *error;
6660 struct got_commit_object *commit;
6662 error = got_worktree_histedit_skip_commit(worktree, hle->commit_id,
6663 repo);
6664 if (error)
6665 return error;
6667 error = got_object_open_as_commit(&commit, repo, hle->commit_id);
6668 if (error)
6669 return error;
6671 error = show_histedit_progress(commit, hle, NULL);
6672 got_object_commit_close(commit);
6673 return error;
6676 static const struct got_error *
6677 check_local_changes(void *arg, unsigned char status,
6678 unsigned char staged_status, const char *path,
6679 struct got_object_id *blob_id, struct got_object_id *staged_blob_id,
6680 struct got_object_id *commit_id, int dirfd, const char *de_name)
6682 int *have_local_changes = arg;
6684 switch (status) {
6685 case GOT_STATUS_ADD:
6686 case GOT_STATUS_DELETE:
6687 case GOT_STATUS_MODIFY:
6688 case GOT_STATUS_CONFLICT:
6689 *have_local_changes = 1;
6690 return got_error(GOT_ERR_CANCELLED);
6691 default:
6692 break;
6695 switch (staged_status) {
6696 case GOT_STATUS_ADD:
6697 case GOT_STATUS_DELETE:
6698 case GOT_STATUS_MODIFY:
6699 *have_local_changes = 1;
6700 return got_error(GOT_ERR_CANCELLED);
6701 default:
6702 break;
6705 return NULL;
6708 static const struct got_error *
6709 cmd_histedit(int argc, char *argv[])
6711 const struct got_error *error = NULL;
6712 struct got_worktree *worktree = NULL;
6713 struct got_fileindex *fileindex = NULL;
6714 struct got_repository *repo = NULL;
6715 char *cwd = NULL;
6716 struct got_reference *branch = NULL;
6717 struct got_reference *tmp_branch = NULL;
6718 struct got_object_id *resume_commit_id = NULL;
6719 struct got_object_id *base_commit_id = NULL;
6720 struct got_object_id *head_commit_id = NULL;
6721 struct got_commit_object *commit = NULL;
6722 int ch, rebase_in_progress = 0, did_something;
6723 int edit_in_progress = 0, abort_edit = 0, continue_edit = 0;
6724 int edit_logmsg_only = 0;
6725 const char *edit_script_path = NULL;
6726 unsigned char rebase_status = GOT_STATUS_NO_CHANGE;
6727 struct got_object_id_queue commits;
6728 struct got_pathlist_head merged_paths;
6729 const struct got_object_id_queue *parent_ids;
6730 struct got_object_qid *pid;
6731 struct got_histedit_list histedit_cmds;
6732 struct got_histedit_list_entry *hle;
6734 SIMPLEQ_INIT(&commits);
6735 TAILQ_INIT(&histedit_cmds);
6736 TAILQ_INIT(&merged_paths);
6738 while ((ch = getopt(argc, argv, "acF:m")) != -1) {
6739 switch (ch) {
6740 case 'a':
6741 abort_edit = 1;
6742 break;
6743 case 'c':
6744 continue_edit = 1;
6745 break;
6746 case 'F':
6747 edit_script_path = optarg;
6748 break;
6749 case 'm':
6750 edit_logmsg_only = 1;
6751 break;
6752 default:
6753 usage_histedit();
6754 /* NOTREACHED */
6758 argc -= optind;
6759 argv += optind;
6761 #ifndef PROFILE
6762 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
6763 "unveil", NULL) == -1)
6764 err(1, "pledge");
6765 #endif
6766 if (abort_edit && continue_edit)
6767 errx(1, "histedit's -a and -c options are mutually exclusive");
6768 if (edit_script_path && edit_logmsg_only)
6769 errx(1, "histedit's -F and -m options are mutually exclusive");
6770 if (abort_edit && edit_logmsg_only)
6771 errx(1, "histedit's -a and -m options are mutually exclusive");
6772 if (continue_edit && edit_logmsg_only)
6773 errx(1, "histedit's -c and -m options are mutually exclusive");
6774 if (argc != 0)
6775 usage_histedit();
6778 * This command cannot apply unveil(2) in all cases because the
6779 * user may choose to run an editor to edit the histedit script
6780 * and to edit individual commit log messages.
6781 * unveil(2) traverses exec(2); if an editor is used we have to
6782 * apply unveil after edit script and log messages have been written.
6783 * XXX TODO: Make use of unveil(2) where possible.
6786 cwd = getcwd(NULL, 0);
6787 if (cwd == NULL) {
6788 error = got_error_from_errno("getcwd");
6789 goto done;
6791 error = got_worktree_open(&worktree, cwd);
6792 if (error)
6793 goto done;
6795 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
6796 NULL);
6797 if (error != NULL)
6798 goto done;
6800 error = got_worktree_rebase_in_progress(&rebase_in_progress, worktree);
6801 if (error)
6802 goto done;
6803 if (rebase_in_progress) {
6804 error = got_error(GOT_ERR_REBASING);
6805 goto done;
6808 error = got_worktree_histedit_in_progress(&edit_in_progress, worktree);
6809 if (error)
6810 goto done;
6812 if (edit_in_progress && edit_logmsg_only) {
6813 error = got_error_msg(GOT_ERR_HISTEDIT_BUSY,
6814 "histedit operation is in progress in this "
6815 "work tree and must be continued or aborted "
6816 "before the -m option can be used");
6817 goto done;
6820 if (edit_in_progress && abort_edit) {
6821 error = got_worktree_histedit_continue(&resume_commit_id,
6822 &tmp_branch, &branch, &base_commit_id, &fileindex,
6823 worktree, repo);
6824 if (error)
6825 goto done;
6826 printf("Switching work tree to %s\n",
6827 got_ref_get_symref_target(branch));
6828 error = got_worktree_histedit_abort(worktree, fileindex, repo,
6829 branch, base_commit_id, update_progress, &did_something);
6830 if (error)
6831 goto done;
6832 printf("Histedit of %s aborted\n",
6833 got_ref_get_symref_target(branch));
6834 goto done; /* nothing else to do */
6835 } else if (abort_edit) {
6836 error = got_error(GOT_ERR_NOT_HISTEDIT);
6837 goto done;
6840 if (continue_edit) {
6841 char *path;
6843 if (!edit_in_progress) {
6844 error = got_error(GOT_ERR_NOT_HISTEDIT);
6845 goto done;
6848 error = got_worktree_get_histedit_script_path(&path, worktree);
6849 if (error)
6850 goto done;
6852 error = histedit_load_list(&histedit_cmds, path, repo);
6853 free(path);
6854 if (error)
6855 goto done;
6857 error = got_worktree_histedit_continue(&resume_commit_id,
6858 &tmp_branch, &branch, &base_commit_id, &fileindex,
6859 worktree, repo);
6860 if (error)
6861 goto done;
6863 error = got_ref_resolve(&head_commit_id, repo, branch);
6864 if (error)
6865 goto done;
6867 error = got_object_open_as_commit(&commit, repo,
6868 head_commit_id);
6869 if (error)
6870 goto done;
6871 parent_ids = got_object_commit_get_parent_ids(commit);
6872 pid = SIMPLEQ_FIRST(parent_ids);
6873 if (pid == NULL) {
6874 error = got_error(GOT_ERR_EMPTY_HISTEDIT);
6875 goto done;
6877 error = collect_commits(&commits, head_commit_id, pid->id,
6878 base_commit_id, got_worktree_get_path_prefix(worktree),
6879 GOT_ERR_HISTEDIT_PATH, repo);
6880 got_object_commit_close(commit);
6881 commit = NULL;
6882 if (error)
6883 goto done;
6884 } else {
6885 if (edit_in_progress) {
6886 error = got_error(GOT_ERR_HISTEDIT_BUSY);
6887 goto done;
6890 error = got_ref_open(&branch, repo,
6891 got_worktree_get_head_ref_name(worktree), 0);
6892 if (error != NULL)
6893 goto done;
6895 if (strncmp(got_ref_get_name(branch), "refs/heads/", 11) != 0) {
6896 error = got_error_msg(GOT_ERR_COMMIT_BRANCH,
6897 "will not edit commit history of a branch outside "
6898 "the \"refs/heads/\" reference namespace");
6899 goto done;
6902 error = got_ref_resolve(&head_commit_id, repo, branch);
6903 got_ref_close(branch);
6904 branch = NULL;
6905 if (error)
6906 goto done;
6908 error = got_object_open_as_commit(&commit, repo,
6909 head_commit_id);
6910 if (error)
6911 goto done;
6912 parent_ids = got_object_commit_get_parent_ids(commit);
6913 pid = SIMPLEQ_FIRST(parent_ids);
6914 if (pid == NULL) {
6915 error = got_error(GOT_ERR_EMPTY_HISTEDIT);
6916 goto done;
6918 error = collect_commits(&commits, head_commit_id, pid->id,
6919 got_worktree_get_base_commit_id(worktree),
6920 got_worktree_get_path_prefix(worktree),
6921 GOT_ERR_HISTEDIT_PATH, repo);
6922 got_object_commit_close(commit);
6923 commit = NULL;
6924 if (error)
6925 goto done;
6927 if (SIMPLEQ_EMPTY(&commits)) {
6928 error = got_error(GOT_ERR_EMPTY_HISTEDIT);
6929 goto done;
6932 error = got_worktree_histedit_prepare(&tmp_branch, &branch,
6933 &base_commit_id, &fileindex, worktree, repo);
6934 if (error)
6935 goto done;
6937 if (edit_script_path) {
6938 error = histedit_load_list(&histedit_cmds,
6939 edit_script_path, repo);
6940 if (error) {
6941 got_worktree_histedit_abort(worktree, fileindex,
6942 repo, branch, base_commit_id,
6943 update_progress, &did_something);
6944 goto done;
6946 } else {
6947 const char *branch_name;
6948 branch_name = got_ref_get_symref_target(branch);
6949 if (strncmp(branch_name, "refs/heads/", 11) == 0)
6950 branch_name += 11;
6951 error = histedit_edit_script(&histedit_cmds, &commits,
6952 branch_name, edit_logmsg_only, repo);
6953 if (error) {
6954 got_worktree_histedit_abort(worktree, fileindex,
6955 repo, branch, base_commit_id,
6956 update_progress, &did_something);
6957 goto done;
6962 error = histedit_save_list(&histedit_cmds, worktree,
6963 repo);
6964 if (error) {
6965 got_worktree_histedit_abort(worktree, fileindex,
6966 repo, branch, base_commit_id,
6967 update_progress, &did_something);
6968 goto done;
6973 error = histedit_check_script(&histedit_cmds, &commits, repo);
6974 if (error)
6975 goto done;
6977 TAILQ_FOREACH(hle, &histedit_cmds, entry) {
6978 if (resume_commit_id) {
6979 if (got_object_id_cmp(hle->commit_id,
6980 resume_commit_id) != 0)
6981 continue;
6983 resume_commit_id = NULL;
6984 if (hle->cmd->code == GOT_HISTEDIT_DROP ||
6985 hle->cmd->code == GOT_HISTEDIT_FOLD) {
6986 error = histedit_skip_commit(hle, worktree,
6987 repo);
6988 if (error)
6989 goto done;
6990 } else {
6991 struct got_pathlist_head paths;
6992 int have_changes = 0;
6994 TAILQ_INIT(&paths);
6995 error = got_pathlist_append(&paths, "", NULL);
6996 if (error)
6997 goto done;
6998 error = got_worktree_status(worktree, &paths,
6999 repo, check_local_changes, &have_changes,
7000 check_cancelled, NULL);
7001 got_pathlist_free(&paths);
7002 if (error) {
7003 if (error->code != GOT_ERR_CANCELLED)
7004 goto done;
7005 if (sigint_received || sigpipe_received)
7006 goto done;
7008 if (have_changes) {
7009 error = histedit_commit(NULL, worktree,
7010 fileindex, tmp_branch, hle, repo);
7011 if (error)
7012 goto done;
7013 } else {
7014 error = got_object_open_as_commit(
7015 &commit, repo, hle->commit_id);
7016 if (error)
7017 goto done;
7018 error = show_histedit_progress(commit,
7019 hle, NULL);
7020 got_object_commit_close(commit);
7021 commit = NULL;
7022 if (error)
7023 goto done;
7026 continue;
7029 if (hle->cmd->code == GOT_HISTEDIT_DROP) {
7030 error = histedit_skip_commit(hle, worktree, repo);
7031 if (error)
7032 goto done;
7033 continue;
7036 error = got_object_open_as_commit(&commit, repo,
7037 hle->commit_id);
7038 if (error)
7039 goto done;
7040 parent_ids = got_object_commit_get_parent_ids(commit);
7041 pid = SIMPLEQ_FIRST(parent_ids);
7043 error = got_worktree_histedit_merge_files(&merged_paths,
7044 worktree, fileindex, pid->id, hle->commit_id, repo,
7045 rebase_progress, &rebase_status, check_cancelled, NULL);
7046 if (error)
7047 goto done;
7048 got_object_commit_close(commit);
7049 commit = NULL;
7051 if (rebase_status == GOT_STATUS_CONFLICT) {
7052 error = show_rebase_merge_conflict(hle->commit_id,
7053 repo);
7054 if (error)
7055 goto done;
7056 got_worktree_rebase_pathlist_free(&merged_paths);
7057 break;
7060 if (hle->cmd->code == GOT_HISTEDIT_EDIT) {
7061 char *id_str;
7062 error = got_object_id_str(&id_str, hle->commit_id);
7063 if (error)
7064 goto done;
7065 printf("Stopping histedit for amending commit %s\n",
7066 id_str);
7067 free(id_str);
7068 got_worktree_rebase_pathlist_free(&merged_paths);
7069 error = got_worktree_histedit_postpone(worktree,
7070 fileindex);
7071 goto done;
7074 if (hle->cmd->code == GOT_HISTEDIT_FOLD) {
7075 error = histedit_skip_commit(hle, worktree, repo);
7076 if (error)
7077 goto done;
7078 continue;
7081 error = histedit_commit(&merged_paths, worktree, fileindex,
7082 tmp_branch, hle, repo);
7083 got_worktree_rebase_pathlist_free(&merged_paths);
7084 if (error)
7085 goto done;
7088 if (rebase_status == GOT_STATUS_CONFLICT) {
7089 error = got_worktree_histedit_postpone(worktree, fileindex);
7090 if (error)
7091 goto done;
7092 error = got_error_msg(GOT_ERR_CONFLICTS,
7093 "conflicts must be resolved before histedit can continue");
7094 } else
7095 error = histedit_complete(worktree, fileindex, tmp_branch,
7096 branch, repo);
7097 done:
7098 got_object_id_queue_free(&commits);
7099 histedit_free_list(&histedit_cmds);
7100 free(head_commit_id);
7101 free(base_commit_id);
7102 free(resume_commit_id);
7103 if (commit)
7104 got_object_commit_close(commit);
7105 if (branch)
7106 got_ref_close(branch);
7107 if (tmp_branch)
7108 got_ref_close(tmp_branch);
7109 if (worktree)
7110 got_worktree_close(worktree);
7111 if (repo)
7112 got_repo_close(repo);
7113 return error;
7116 __dead static void
7117 usage_integrate(void)
7119 fprintf(stderr, "usage: %s integrate branch\n", getprogname());
7120 exit(1);
7123 static const struct got_error *
7124 cmd_integrate(int argc, char *argv[])
7126 const struct got_error *error = NULL;
7127 struct got_repository *repo = NULL;
7128 struct got_worktree *worktree = NULL;
7129 char *cwd = NULL, *refname = NULL, *base_refname = NULL;
7130 const char *branch_arg = NULL;
7131 struct got_reference *branch_ref = NULL, *base_branch_ref = NULL;
7132 struct got_fileindex *fileindex = NULL;
7133 struct got_object_id *commit_id = NULL, *base_commit_id = NULL;
7134 int ch, did_something = 0;
7136 while ((ch = getopt(argc, argv, "")) != -1) {
7137 switch (ch) {
7138 default:
7139 usage_integrate();
7140 /* NOTREACHED */
7144 argc -= optind;
7145 argv += optind;
7147 if (argc != 1)
7148 usage_integrate();
7149 branch_arg = argv[0];
7151 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
7152 "unveil", NULL) == -1)
7153 err(1, "pledge");
7155 cwd = getcwd(NULL, 0);
7156 if (cwd == NULL) {
7157 error = got_error_from_errno("getcwd");
7158 goto done;
7161 error = got_worktree_open(&worktree, cwd);
7162 if (error)
7163 goto done;
7165 error = check_rebase_or_histedit_in_progress(worktree);
7166 if (error)
7167 goto done;
7169 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
7170 NULL);
7171 if (error != NULL)
7172 goto done;
7174 error = apply_unveil(got_repo_get_path(repo), 0,
7175 got_worktree_get_root_path(worktree));
7176 if (error)
7177 goto done;
7179 if (asprintf(&refname, "refs/heads/%s", branch_arg) == -1) {
7180 error = got_error_from_errno("asprintf");
7181 goto done;
7184 error = got_worktree_integrate_prepare(&fileindex, &branch_ref,
7185 &base_branch_ref, worktree, refname, repo);
7186 if (error)
7187 goto done;
7189 refname = strdup(got_ref_get_name(branch_ref));
7190 if (refname == NULL) {
7191 error = got_error_from_errno("strdup");
7192 got_worktree_integrate_abort(worktree, fileindex, repo,
7193 branch_ref, base_branch_ref);
7194 goto done;
7196 base_refname = strdup(got_ref_get_name(base_branch_ref));
7197 if (base_refname == NULL) {
7198 error = got_error_from_errno("strdup");
7199 got_worktree_integrate_abort(worktree, fileindex, repo,
7200 branch_ref, base_branch_ref);
7201 goto done;
7204 error = got_ref_resolve(&commit_id, repo, branch_ref);
7205 if (error)
7206 goto done;
7208 error = got_ref_resolve(&base_commit_id, repo, base_branch_ref);
7209 if (error)
7210 goto done;
7212 if (got_object_id_cmp(commit_id, base_commit_id) == 0) {
7213 error = got_error_msg(GOT_ERR_SAME_BRANCH,
7214 "specified branch has already been integrated");
7215 got_worktree_integrate_abort(worktree, fileindex, repo,
7216 branch_ref, base_branch_ref);
7217 goto done;
7220 error = check_linear_ancestry(commit_id, base_commit_id, 1, repo);
7221 if (error) {
7222 if (error->code == GOT_ERR_ANCESTRY)
7223 error = got_error(GOT_ERR_REBASE_REQUIRED);
7224 got_worktree_integrate_abort(worktree, fileindex, repo,
7225 branch_ref, base_branch_ref);
7226 goto done;
7229 error = got_worktree_integrate_continue(worktree, fileindex, repo,
7230 branch_ref, base_branch_ref, update_progress, &did_something,
7231 check_cancelled, NULL);
7232 if (error)
7233 goto done;
7235 printf("Integrated %s into %s\n", refname, base_refname);
7236 done:
7237 if (repo)
7238 got_repo_close(repo);
7239 if (worktree)
7240 got_worktree_close(worktree);
7241 free(cwd);
7242 free(base_commit_id);
7243 free(commit_id);
7244 free(refname);
7245 free(base_refname);
7246 return error;
7249 __dead static void
7250 usage_stage(void)
7252 fprintf(stderr, "usage: %s stage [-l] | [-p] [-F response-script] "
7253 "[file-path ...]\n",
7254 getprogname());
7255 exit(1);
7258 static const struct got_error *
7259 print_stage(void *arg, unsigned char status, unsigned char staged_status,
7260 const char *path, struct got_object_id *blob_id,
7261 struct got_object_id *staged_blob_id, struct got_object_id *commit_id,
7262 int dirfd, const char *de_name)
7264 const struct got_error *err = NULL;
7265 char *id_str = NULL;
7267 if (staged_status != GOT_STATUS_ADD &&
7268 staged_status != GOT_STATUS_MODIFY &&
7269 staged_status != GOT_STATUS_DELETE)
7270 return NULL;
7272 if (staged_status == GOT_STATUS_ADD ||
7273 staged_status == GOT_STATUS_MODIFY)
7274 err = got_object_id_str(&id_str, staged_blob_id);
7275 else
7276 err = got_object_id_str(&id_str, blob_id);
7277 if (err)
7278 return err;
7280 printf("%s %c %s\n", id_str, staged_status, path);
7281 free(id_str);
7282 return NULL;
7285 static const struct got_error *
7286 cmd_stage(int argc, char *argv[])
7288 const struct got_error *error = NULL;
7289 struct got_repository *repo = NULL;
7290 struct got_worktree *worktree = NULL;
7291 char *cwd = NULL;
7292 struct got_pathlist_head paths;
7293 struct got_pathlist_entry *pe;
7294 int ch, list_stage = 0, pflag = 0;
7295 FILE *patch_script_file = NULL;
7296 const char *patch_script_path = NULL;
7297 struct choose_patch_arg cpa;
7299 TAILQ_INIT(&paths);
7301 while ((ch = getopt(argc, argv, "lpF:")) != -1) {
7302 switch (ch) {
7303 case 'l':
7304 list_stage = 1;
7305 break;
7306 case 'p':
7307 pflag = 1;
7308 break;
7309 case 'F':
7310 patch_script_path = optarg;
7311 break;
7312 default:
7313 usage_stage();
7314 /* NOTREACHED */
7318 argc -= optind;
7319 argv += optind;
7321 #ifndef PROFILE
7322 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
7323 "unveil", NULL) == -1)
7324 err(1, "pledge");
7325 #endif
7326 if (list_stage && (pflag || patch_script_path))
7327 errx(1, "-l option cannot be used with other options");
7328 if (patch_script_path && !pflag)
7329 errx(1, "-F option can only be used together with -p option");
7331 cwd = getcwd(NULL, 0);
7332 if (cwd == NULL) {
7333 error = got_error_from_errno("getcwd");
7334 goto done;
7337 error = got_worktree_open(&worktree, cwd);
7338 if (error)
7339 goto done;
7341 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
7342 NULL);
7343 if (error != NULL)
7344 goto done;
7346 if (patch_script_path) {
7347 patch_script_file = fopen(patch_script_path, "r");
7348 if (patch_script_file == NULL) {
7349 error = got_error_from_errno2("fopen",
7350 patch_script_path);
7351 goto done;
7354 error = apply_unveil(got_repo_get_path(repo), 0,
7355 got_worktree_get_root_path(worktree));
7356 if (error)
7357 goto done;
7359 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
7360 if (error)
7361 goto done;
7363 if (list_stage)
7364 error = got_worktree_status(worktree, &paths, repo,
7365 print_stage, NULL, check_cancelled, NULL);
7366 else {
7367 cpa.patch_script_file = patch_script_file;
7368 cpa.action = "stage";
7369 error = got_worktree_stage(worktree, &paths,
7370 pflag ? NULL : print_status, NULL,
7371 pflag ? choose_patch : NULL, &cpa, repo);
7373 done:
7374 if (patch_script_file && fclose(patch_script_file) == EOF &&
7375 error == NULL)
7376 error = got_error_from_errno2("fclose", patch_script_path);
7377 if (repo)
7378 got_repo_close(repo);
7379 if (worktree)
7380 got_worktree_close(worktree);
7381 TAILQ_FOREACH(pe, &paths, entry)
7382 free((char *)pe->path);
7383 got_pathlist_free(&paths);
7384 free(cwd);
7385 return error;
7388 __dead static void
7389 usage_unstage(void)
7391 fprintf(stderr, "usage: %s unstage [-p] [-F response-script] "
7392 "[file-path ...]\n",
7393 getprogname());
7394 exit(1);
7398 static const struct got_error *
7399 cmd_unstage(int argc, char *argv[])
7401 const struct got_error *error = NULL;
7402 struct got_repository *repo = NULL;
7403 struct got_worktree *worktree = NULL;
7404 char *cwd = NULL;
7405 struct got_pathlist_head paths;
7406 struct got_pathlist_entry *pe;
7407 int ch, did_something = 0, pflag = 0;
7408 FILE *patch_script_file = NULL;
7409 const char *patch_script_path = NULL;
7410 struct choose_patch_arg cpa;
7412 TAILQ_INIT(&paths);
7414 while ((ch = getopt(argc, argv, "pF:")) != -1) {
7415 switch (ch) {
7416 case 'p':
7417 pflag = 1;
7418 break;
7419 case 'F':
7420 patch_script_path = optarg;
7421 break;
7422 default:
7423 usage_unstage();
7424 /* NOTREACHED */
7428 argc -= optind;
7429 argv += optind;
7431 #ifndef PROFILE
7432 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
7433 "unveil", NULL) == -1)
7434 err(1, "pledge");
7435 #endif
7436 if (patch_script_path && !pflag)
7437 errx(1, "-F option can only be used together with -p option");
7439 cwd = getcwd(NULL, 0);
7440 if (cwd == NULL) {
7441 error = got_error_from_errno("getcwd");
7442 goto done;
7445 error = got_worktree_open(&worktree, cwd);
7446 if (error)
7447 goto done;
7449 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
7450 NULL);
7451 if (error != NULL)
7452 goto done;
7454 if (patch_script_path) {
7455 patch_script_file = fopen(patch_script_path, "r");
7456 if (patch_script_file == NULL) {
7457 error = got_error_from_errno2("fopen",
7458 patch_script_path);
7459 goto done;
7463 error = apply_unveil(got_repo_get_path(repo), 0,
7464 got_worktree_get_root_path(worktree));
7465 if (error)
7466 goto done;
7468 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
7469 if (error)
7470 goto done;
7472 cpa.patch_script_file = patch_script_file;
7473 cpa.action = "unstage";
7474 error = got_worktree_unstage(worktree, &paths, update_progress,
7475 &did_something, pflag ? choose_patch : NULL, &cpa, repo);
7476 done:
7477 if (patch_script_file && fclose(patch_script_file) == EOF &&
7478 error == NULL)
7479 error = got_error_from_errno2("fclose", patch_script_path);
7480 if (repo)
7481 got_repo_close(repo);
7482 if (worktree)
7483 got_worktree_close(worktree);
7484 TAILQ_FOREACH(pe, &paths, entry)
7485 free((char *)pe->path);
7486 got_pathlist_free(&paths);
7487 free(cwd);
7488 return error;
7491 __dead static void
7492 usage_cat(void)
7494 fprintf(stderr, "usage: %s cat [-r repository ] [ -c commit ] [ -P ] "
7495 "arg1 [arg2 ...]\n", getprogname());
7496 exit(1);
7499 static const struct got_error *
7500 cat_blob(struct got_object_id *id, struct got_repository *repo, FILE *outfile)
7502 const struct got_error *err;
7503 struct got_blob_object *blob;
7505 err = got_object_open_as_blob(&blob, repo, id, 8192);
7506 if (err)
7507 return err;
7509 err = got_object_blob_dump_to_file(NULL, NULL, NULL, outfile, blob);
7510 got_object_blob_close(blob);
7511 return err;
7514 static const struct got_error *
7515 cat_tree(struct got_object_id *id, struct got_repository *repo, FILE *outfile)
7517 const struct got_error *err;
7518 struct got_tree_object *tree;
7519 int nentries, i;
7521 err = got_object_open_as_tree(&tree, repo, id);
7522 if (err)
7523 return err;
7525 nentries = got_object_tree_get_nentries(tree);
7526 for (i = 0; i < nentries; i++) {
7527 struct got_tree_entry *te;
7528 char *id_str;
7529 if (sigint_received || sigpipe_received)
7530 break;
7531 te = got_object_tree_get_entry(tree, i);
7532 err = got_object_id_str(&id_str, got_tree_entry_get_id(te));
7533 if (err)
7534 break;
7535 fprintf(outfile, "%s %.7o %s\n", id_str,
7536 got_tree_entry_get_mode(te),
7537 got_tree_entry_get_name(te));
7538 free(id_str);
7541 got_object_tree_close(tree);
7542 return err;
7545 static const struct got_error *
7546 cat_commit(struct got_object_id *id, struct got_repository *repo, FILE *outfile)
7548 const struct got_error *err;
7549 struct got_commit_object *commit;
7550 const struct got_object_id_queue *parent_ids;
7551 struct got_object_qid *pid;
7552 char *id_str = NULL;
7553 const char *logmsg = NULL;
7555 err = got_object_open_as_commit(&commit, repo, id);
7556 if (err)
7557 return err;
7559 err = got_object_id_str(&id_str, got_object_commit_get_tree_id(commit));
7560 if (err)
7561 goto done;
7563 fprintf(outfile, "%s%s\n", GOT_COMMIT_LABEL_TREE, id_str);
7564 parent_ids = got_object_commit_get_parent_ids(commit);
7565 fprintf(outfile, "numparents %d\n",
7566 got_object_commit_get_nparents(commit));
7567 SIMPLEQ_FOREACH(pid, parent_ids, entry) {
7568 char *pid_str;
7569 err = got_object_id_str(&pid_str, pid->id);
7570 if (err)
7571 goto done;
7572 fprintf(outfile, "%s%s\n", GOT_COMMIT_LABEL_PARENT, pid_str);
7573 free(pid_str);
7575 fprintf(outfile, "%s%s %lld +0000\n", GOT_COMMIT_LABEL_AUTHOR,
7576 got_object_commit_get_author(commit),
7577 got_object_commit_get_author_time(commit));
7579 fprintf(outfile, "%s%s %lld +0000\n", GOT_COMMIT_LABEL_COMMITTER,
7580 got_object_commit_get_author(commit),
7581 got_object_commit_get_committer_time(commit));
7583 logmsg = got_object_commit_get_logmsg_raw(commit);
7584 fprintf(outfile, "messagelen %zd\n", strlen(logmsg));
7585 fprintf(outfile, "%s", logmsg);
7586 done:
7587 free(id_str);
7588 got_object_commit_close(commit);
7589 return err;
7592 static const struct got_error *
7593 cat_tag(struct got_object_id *id, struct got_repository *repo, FILE *outfile)
7595 const struct got_error *err;
7596 struct got_tag_object *tag;
7597 char *id_str = NULL;
7598 const char *tagmsg = NULL;
7600 err = got_object_open_as_tag(&tag, repo, id);
7601 if (err)
7602 return err;
7604 err = got_object_id_str(&id_str, got_object_tag_get_object_id(tag));
7605 if (err)
7606 goto done;
7608 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_OBJECT, id_str);
7610 switch (got_object_tag_get_object_type(tag)) {
7611 case GOT_OBJ_TYPE_BLOB:
7612 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TYPE,
7613 GOT_OBJ_LABEL_BLOB);
7614 break;
7615 case GOT_OBJ_TYPE_TREE:
7616 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TYPE,
7617 GOT_OBJ_LABEL_TREE);
7618 break;
7619 case GOT_OBJ_TYPE_COMMIT:
7620 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TYPE,
7621 GOT_OBJ_LABEL_COMMIT);
7622 break;
7623 case GOT_OBJ_TYPE_TAG:
7624 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TYPE,
7625 GOT_OBJ_LABEL_TAG);
7626 break;
7627 default:
7628 break;
7631 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TAG,
7632 got_object_tag_get_name(tag));
7634 fprintf(outfile, "%s%s %lld +0000\n", GOT_TAG_LABEL_TAGGER,
7635 got_object_tag_get_tagger(tag),
7636 got_object_tag_get_tagger_time(tag));
7638 tagmsg = got_object_tag_get_message(tag);
7639 fprintf(outfile, "messagelen %zd\n", strlen(tagmsg));
7640 fprintf(outfile, "%s", tagmsg);
7641 done:
7642 free(id_str);
7643 got_object_tag_close(tag);
7644 return err;
7647 static const struct got_error *
7648 cmd_cat(int argc, char *argv[])
7650 const struct got_error *error;
7651 struct got_repository *repo = NULL;
7652 struct got_worktree *worktree = NULL;
7653 char *cwd = NULL, *repo_path = NULL, *label = NULL;
7654 const char *commit_id_str = NULL;
7655 struct got_object_id *id = NULL, *commit_id = NULL;
7656 int ch, obj_type, i, force_path = 0;
7658 #ifndef PROFILE
7659 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
7660 NULL) == -1)
7661 err(1, "pledge");
7662 #endif
7664 while ((ch = getopt(argc, argv, "c:r:P")) != -1) {
7665 switch (ch) {
7666 case 'c':
7667 commit_id_str = optarg;
7668 break;
7669 case 'r':
7670 repo_path = realpath(optarg, NULL);
7671 if (repo_path == NULL)
7672 return got_error_from_errno2("realpath",
7673 optarg);
7674 got_path_strip_trailing_slashes(repo_path);
7675 break;
7676 case 'P':
7677 force_path = 1;
7678 break;
7679 default:
7680 usage_cat();
7681 /* NOTREACHED */
7685 argc -= optind;
7686 argv += optind;
7688 cwd = getcwd(NULL, 0);
7689 if (cwd == NULL) {
7690 error = got_error_from_errno("getcwd");
7691 goto done;
7693 error = got_worktree_open(&worktree, cwd);
7694 if (error && error->code != GOT_ERR_NOT_WORKTREE)
7695 goto done;
7696 if (worktree) {
7697 if (repo_path == NULL) {
7698 repo_path = strdup(
7699 got_worktree_get_repo_path(worktree));
7700 if (repo_path == NULL) {
7701 error = got_error_from_errno("strdup");
7702 goto done;
7707 if (repo_path == NULL) {
7708 repo_path = getcwd(NULL, 0);
7709 if (repo_path == NULL)
7710 return got_error_from_errno("getcwd");
7713 error = got_repo_open(&repo, repo_path, NULL);
7714 free(repo_path);
7715 if (error != NULL)
7716 goto done;
7718 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
7719 if (error)
7720 goto done;
7722 if (commit_id_str == NULL)
7723 commit_id_str = GOT_REF_HEAD;
7724 error = got_repo_match_object_id(&commit_id, NULL,
7725 commit_id_str, GOT_OBJ_TYPE_COMMIT, 1, repo);
7726 if (error)
7727 goto done;
7729 for (i = 0; i < argc; i++) {
7730 if (force_path) {
7731 error = got_object_id_by_path(&id, repo, commit_id,
7732 argv[i]);
7733 if (error)
7734 break;
7735 } else {
7736 error = got_repo_match_object_id(&id, &label, argv[i],
7737 GOT_OBJ_TYPE_ANY, 0, repo);
7738 if (error) {
7739 if (error->code != GOT_ERR_BAD_OBJ_ID_STR &&
7740 error->code != GOT_ERR_NOT_REF)
7741 break;
7742 error = got_object_id_by_path(&id, repo,
7743 commit_id, argv[i]);
7744 if (error)
7745 break;
7749 error = got_object_get_type(&obj_type, repo, id);
7750 if (error)
7751 break;
7753 switch (obj_type) {
7754 case GOT_OBJ_TYPE_BLOB:
7755 error = cat_blob(id, repo, stdout);
7756 break;
7757 case GOT_OBJ_TYPE_TREE:
7758 error = cat_tree(id, repo, stdout);
7759 break;
7760 case GOT_OBJ_TYPE_COMMIT:
7761 error = cat_commit(id, repo, stdout);
7762 break;
7763 case GOT_OBJ_TYPE_TAG:
7764 error = cat_tag(id, repo, stdout);
7765 break;
7766 default:
7767 error = got_error(GOT_ERR_OBJ_TYPE);
7768 break;
7770 if (error)
7771 break;
7772 free(label);
7773 label = NULL;
7774 free(id);
7775 id = NULL;
7777 done:
7778 free(label);
7779 free(id);
7780 free(commit_id);
7781 if (worktree)
7782 got_worktree_close(worktree);
7783 if (repo) {
7784 const struct got_error *repo_error;
7785 repo_error = got_repo_close(repo);
7786 if (error == NULL)
7787 error = repo_error;
7789 return error;