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>
42 #include "got_version.h"
43 #include "got_error.h"
44 #include "got_object.h"
45 #include "got_reference.h"
46 #include "got_repository.h"
47 #include "got_path.h"
48 #include "got_cancel.h"
49 #include "got_worktree.h"
50 #include "got_diff.h"
51 #include "got_commit_graph.h"
52 #include "got_fetch.h"
53 #include "got_blame.h"
54 #include "got_privsep.h"
55 #include "got_opentemp.h"
57 #ifndef nitems
58 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
59 #endif
61 static volatile sig_atomic_t sigint_received;
62 static volatile sig_atomic_t sigpipe_received;
64 static void
65 catch_sigint(int signo)
66 {
67 sigint_received = 1;
68 }
70 static void
71 catch_sigpipe(int signo)
72 {
73 sigpipe_received = 1;
74 }
77 struct got_cmd {
78 const char *cmd_name;
79 const struct got_error *(*cmd_main)(int, char *[]);
80 void (*cmd_usage)(void);
81 const char *cmd_alias;
82 };
84 __dead static void usage(int);
85 __dead static void usage_init(void);
86 __dead static void usage_import(void);
87 __dead static void usage_checkout(void);
88 __dead static void usage_clone(void);
89 __dead static void usage_update(void);
90 __dead static void usage_log(void);
91 __dead static void usage_diff(void);
92 __dead static void usage_blame(void);
93 __dead static void usage_tree(void);
94 __dead static void usage_status(void);
95 __dead static void usage_ref(void);
96 __dead static void usage_branch(void);
97 __dead static void usage_tag(void);
98 __dead static void usage_add(void);
99 __dead static void usage_remove(void);
100 __dead static void usage_revert(void);
101 __dead static void usage_commit(void);
102 __dead static void usage_cherrypick(void);
103 __dead static void usage_backout(void);
104 __dead static void usage_rebase(void);
105 __dead static void usage_histedit(void);
106 __dead static void usage_integrate(void);
107 __dead static void usage_stage(void);
108 __dead static void usage_unstage(void);
109 __dead static void usage_cat(void);
111 static const struct got_error* cmd_init(int, char *[]);
112 static const struct got_error* cmd_import(int, char *[]);
113 static const struct got_error* cmd_clone(int, char *[]);
114 static const struct got_error* cmd_checkout(int, char *[]);
115 static const struct got_error* cmd_update(int, char *[]);
116 static const struct got_error* cmd_log(int, char *[]);
117 static const struct got_error* cmd_diff(int, char *[]);
118 static const struct got_error* cmd_blame(int, char *[]);
119 static const struct got_error* cmd_tree(int, char *[]);
120 static const struct got_error* cmd_status(int, char *[]);
121 static const struct got_error* cmd_ref(int, char *[]);
122 static const struct got_error* cmd_branch(int, char *[]);
123 static const struct got_error* cmd_tag(int, char *[]);
124 static const struct got_error* cmd_add(int, char *[]);
125 static const struct got_error* cmd_remove(int, char *[]);
126 static const struct got_error* cmd_revert(int, char *[]);
127 static const struct got_error* cmd_commit(int, char *[]);
128 static const struct got_error* cmd_cherrypick(int, char *[]);
129 static const struct got_error* cmd_backout(int, char *[]);
130 static const struct got_error* cmd_rebase(int, char *[]);
131 static const struct got_error* cmd_histedit(int, char *[]);
132 static const struct got_error* cmd_integrate(int, char *[]);
133 static const struct got_error* cmd_stage(int, char *[]);
134 static const struct got_error* cmd_unstage(int, char *[]);
135 static const struct got_error* cmd_cat(int, char *[]);
137 static struct got_cmd got_commands[] = {
138 { "init", cmd_init, usage_init, "in" },
139 { "import", cmd_import, usage_import, "im" },
140 { "checkout", cmd_checkout, usage_checkout, "co" },
141 { "clone", cmd_clone, usage_clone, "cl" },
142 { "update", cmd_update, usage_update, "up" },
143 { "log", cmd_log, usage_log, "" },
144 { "diff", cmd_diff, usage_diff, "di" },
145 { "blame", cmd_blame, usage_blame, "bl" },
146 { "tree", cmd_tree, usage_tree, "tr" },
147 { "status", cmd_status, usage_status, "st" },
148 { "ref", cmd_ref, usage_ref, "" },
149 { "branch", cmd_branch, usage_branch, "br" },
150 { "tag", cmd_tag, usage_tag, "" },
151 { "add", cmd_add, usage_add, "" },
152 { "remove", cmd_remove, usage_remove, "rm" },
153 { "revert", cmd_revert, usage_revert, "rv" },
154 { "commit", cmd_commit, usage_commit, "ci" },
155 { "cherrypick", cmd_cherrypick, usage_cherrypick, "cy" },
156 { "backout", cmd_backout, usage_backout, "bo" },
157 { "rebase", cmd_rebase, usage_rebase, "rb" },
158 { "histedit", cmd_histedit, usage_histedit, "he" },
159 { "integrate", cmd_integrate, usage_integrate,"ig" },
160 { "stage", cmd_stage, usage_stage, "sg" },
161 { "unstage", cmd_unstage, usage_unstage, "ug" },
162 { "cat", cmd_cat, usage_cat, "" },
163 };
165 static void
166 list_commands(void)
168 int i;
170 fprintf(stderr, "commands:");
171 for (i = 0; i < nitems(got_commands); i++) {
172 struct got_cmd *cmd = &got_commands[i];
173 fprintf(stderr, " %s", cmd->cmd_name);
175 fputc('\n', stderr);
178 int
179 main(int argc, char *argv[])
181 struct got_cmd *cmd;
182 unsigned int i;
183 int ch;
184 int hflag = 0, Vflag = 0;
185 static struct option longopts[] = {
186 { "version", no_argument, NULL, 'V' },
187 { NULL, 0, NULL, 0}
188 };
190 setlocale(LC_CTYPE, "");
192 while ((ch = getopt_long(argc, argv, "+hV", longopts, NULL)) != -1) {
193 switch (ch) {
194 case 'h':
195 hflag = 1;
196 break;
197 case 'V':
198 Vflag = 1;
199 break;
200 default:
201 usage(hflag);
202 /* NOTREACHED */
206 argc -= optind;
207 argv += optind;
208 optind = 0;
210 if (Vflag) {
211 got_version_print_str();
212 return 1;
215 if (argc <= 0)
216 usage(hflag);
218 signal(SIGINT, catch_sigint);
219 signal(SIGPIPE, catch_sigpipe);
221 for (i = 0; i < nitems(got_commands); i++) {
222 const struct got_error *error;
224 cmd = &got_commands[i];
226 if (strcmp(cmd->cmd_name, argv[0]) != 0 &&
227 strcmp(cmd->cmd_alias, argv[0]) != 0)
228 continue;
230 if (hflag)
231 got_commands[i].cmd_usage();
233 error = got_commands[i].cmd_main(argc, argv);
234 if (error && error->code != GOT_ERR_CANCELLED &&
235 error->code != GOT_ERR_PRIVSEP_EXIT &&
236 !(sigpipe_received &&
237 error->code == GOT_ERR_ERRNO && errno == EPIPE) &&
238 !(sigint_received &&
239 error->code == GOT_ERR_ERRNO && errno == EINTR)) {
240 fprintf(stderr, "%s: %s\n", getprogname(), error->msg);
241 return 1;
244 return 0;
247 fprintf(stderr, "%s: unknown command '%s'\n", getprogname(), argv[0]);
248 list_commands();
249 return 1;
252 __dead static void
253 usage(int hflag)
255 fprintf(stderr, "usage: %s [-h] [-V | --version] command [arg ...]\n",
256 getprogname());
257 if (hflag)
258 list_commands();
259 exit(1);
262 static const struct got_error *
263 get_editor(char **abspath)
265 const struct got_error *err = NULL;
266 const char *editor;
268 *abspath = NULL;
270 editor = getenv("VISUAL");
271 if (editor == NULL)
272 editor = getenv("EDITOR");
274 if (editor) {
275 err = got_path_find_prog(abspath, editor);
276 if (err)
277 return err;
280 if (*abspath == NULL) {
281 *abspath = strdup("/bin/ed");
282 if (*abspath == NULL)
283 return got_error_from_errno("strdup");
286 return NULL;
289 static const struct got_error *
290 apply_unveil(const char *repo_path, int repo_read_only,
291 const char *worktree_path)
293 const struct got_error *err;
295 #ifdef PROFILE
296 if (unveil("gmon.out", "rwc") != 0)
297 return got_error_from_errno2("unveil", "gmon.out");
298 #endif
299 if (repo_path && unveil(repo_path, repo_read_only ? "r" : "rwc") != 0)
300 return got_error_from_errno2("unveil", repo_path);
302 if (worktree_path && unveil(worktree_path, "rwc") != 0)
303 return got_error_from_errno2("unveil", worktree_path);
305 if (unveil(GOT_TMPDIR_STR, "rwc") != 0)
306 return got_error_from_errno2("unveil", GOT_TMPDIR_STR);
308 err = got_privsep_unveil_exec_helpers();
309 if (err != NULL)
310 return err;
312 if (unveil(NULL, NULL) != 0)
313 return got_error_from_errno("unveil");
315 return NULL;
318 __dead static void
319 usage_init(void)
321 fprintf(stderr, "usage: %s init repository-path\n", getprogname());
322 exit(1);
325 static const struct got_error *
326 cmd_init(int argc, char *argv[])
328 const struct got_error *error = NULL;
329 char *repo_path = NULL;
330 int ch;
332 while ((ch = getopt(argc, argv, "")) != -1) {
333 switch (ch) {
334 default:
335 usage_init();
336 /* NOTREACHED */
340 argc -= optind;
341 argv += optind;
343 #ifndef PROFILE
344 if (pledge("stdio rpath wpath cpath unveil", NULL) == -1)
345 err(1, "pledge");
346 #endif
347 if (argc != 1)
348 usage_init();
350 repo_path = strdup(argv[0]);
351 if (repo_path == NULL)
352 return got_error_from_errno("strdup");
354 got_path_strip_trailing_slashes(repo_path);
356 error = got_path_mkdir(repo_path);
357 if (error &&
358 !(error->code == GOT_ERR_ERRNO && errno == EEXIST))
359 goto done;
361 error = apply_unveil(repo_path, 0, NULL);
362 if (error)
363 goto done;
365 error = got_repo_init(repo_path);
366 done:
367 free(repo_path);
368 return error;
371 __dead static void
372 usage_import(void)
374 fprintf(stderr, "usage: %s import [-b branch] [-m message] "
375 "[-r repository-path] [-I pattern] path\n", getprogname());
376 exit(1);
379 int
380 spawn_editor(const char *editor, const char *file)
382 pid_t pid;
383 sig_t sighup, sigint, sigquit;
384 int st = -1;
386 sighup = signal(SIGHUP, SIG_IGN);
387 sigint = signal(SIGINT, SIG_IGN);
388 sigquit = signal(SIGQUIT, SIG_IGN);
390 switch (pid = fork()) {
391 case -1:
392 goto doneediting;
393 case 0:
394 execl(editor, editor, file, (char *)NULL);
395 _exit(127);
398 while (waitpid(pid, &st, 0) == -1)
399 if (errno != EINTR)
400 break;
402 doneediting:
403 (void)signal(SIGHUP, sighup);
404 (void)signal(SIGINT, sigint);
405 (void)signal(SIGQUIT, sigquit);
407 if (!WIFEXITED(st)) {
408 errno = EINTR;
409 return -1;
412 return WEXITSTATUS(st);
415 static const struct got_error *
416 edit_logmsg(char **logmsg, const char *editor, const char *logmsg_path,
417 const char *initial_content)
419 const struct got_error *err = NULL;
420 char buf[1024];
421 struct stat st, st2;
422 FILE *fp;
423 int content_changed = 0;
424 size_t len;
426 *logmsg = NULL;
428 if (stat(logmsg_path, &st) == -1)
429 return got_error_from_errno2("stat", logmsg_path);
431 if (spawn_editor(editor, logmsg_path) == -1)
432 return got_error_from_errno("failed spawning editor");
434 if (stat(logmsg_path, &st2) == -1)
435 return got_error_from_errno("stat");
437 if (st.st_mtime == st2.st_mtime && st.st_size == st2.st_size)
438 return got_error_msg(GOT_ERR_COMMIT_MSG_EMPTY,
439 "no changes made to commit message, aborting");
441 *logmsg = malloc(st2.st_size + 1);
442 if (*logmsg == NULL)
443 return got_error_from_errno("malloc");
444 (*logmsg)[0] = '\0';
445 len = 0;
447 fp = fopen(logmsg_path, "r");
448 if (fp == NULL) {
449 err = got_error_from_errno("fopen");
450 goto done;
452 while (fgets(buf, sizeof(buf), fp) != NULL) {
453 if (!content_changed && strcmp(buf, initial_content) != 0)
454 content_changed = 1;
455 if (buf[0] == '#' || (len == 0 && buf[0] == '\n'))
456 continue; /* remove comments and leading empty lines */
457 len = strlcat(*logmsg, buf, st2.st_size);
459 fclose(fp);
461 while (len > 0 && (*logmsg)[len - 1] == '\n') {
462 (*logmsg)[len - 1] = '\0';
463 len--;
466 if (len == 0 || !content_changed)
467 err = got_error_msg(GOT_ERR_COMMIT_MSG_EMPTY,
468 "commit message cannot be empty, aborting");
469 done:
470 if (err) {
471 free(*logmsg);
472 *logmsg = NULL;
474 return err;
477 static const struct got_error *
478 collect_import_msg(char **logmsg, char **logmsg_path, const char *editor,
479 const char *path_dir, const char *branch_name)
481 char *initial_content = NULL;
482 const struct got_error *err = NULL;
483 int fd;
485 if (asprintf(&initial_content,
486 "\n# %s to be imported to branch %s\n", path_dir,
487 branch_name) == -1)
488 return got_error_from_errno("asprintf");
490 err = got_opentemp_named_fd(logmsg_path, &fd,
491 GOT_TMPDIR_STR "/got-importmsg");
492 if (err)
493 goto done;
495 dprintf(fd, initial_content);
496 close(fd);
498 err = edit_logmsg(logmsg, editor, *logmsg_path, initial_content);
499 done:
500 free(initial_content);
501 return err;
504 static const struct got_error *
505 import_progress(void *arg, const char *path)
507 printf("A %s\n", path);
508 return NULL;
511 static const struct got_error *
512 get_author(char **author, struct got_repository *repo)
514 const struct got_error *err = NULL;
515 const char *got_author, *name, *email;
517 *author = NULL;
519 name = got_repo_get_gitconfig_author_name(repo);
520 email = got_repo_get_gitconfig_author_email(repo);
521 if (name && email) {
522 if (asprintf(author, "%s <%s>", name, email) == -1)
523 return got_error_from_errno("asprintf");
524 return NULL;
527 got_author = getenv("GOT_AUTHOR");
528 if (got_author == NULL) {
529 name = got_repo_get_global_gitconfig_author_name(repo);
530 email = got_repo_get_global_gitconfig_author_email(repo);
531 if (name && email) {
532 if (asprintf(author, "%s <%s>", name, email) == -1)
533 return got_error_from_errno("asprintf");
534 return NULL;
536 /* TODO: Look up user in password database? */
537 return got_error(GOT_ERR_COMMIT_NO_AUTHOR);
540 *author = strdup(got_author);
541 if (*author == NULL)
542 return got_error_from_errno("strdup");
544 /*
545 * Really dumb email address check; we're only doing this to
546 * avoid git's object parser breaking on commits we create.
547 */
548 while (*got_author && *got_author != '<')
549 got_author++;
550 if (*got_author != '<') {
551 err = got_error(GOT_ERR_COMMIT_NO_EMAIL);
552 goto done;
554 while (*got_author && *got_author != '@')
555 got_author++;
556 if (*got_author != '@') {
557 err = got_error(GOT_ERR_COMMIT_NO_EMAIL);
558 goto done;
560 while (*got_author && *got_author != '>')
561 got_author++;
562 if (*got_author != '>')
563 err = got_error(GOT_ERR_COMMIT_NO_EMAIL);
564 done:
565 if (err) {
566 free(*author);
567 *author = NULL;
569 return err;
572 static const struct got_error *
573 get_gitconfig_path(char **gitconfig_path)
575 const char *homedir = getenv("HOME");
577 *gitconfig_path = NULL;
578 if (homedir) {
579 if (asprintf(gitconfig_path, "%s/.gitconfig", homedir) == -1)
580 return got_error_from_errno("asprintf");
583 return NULL;
586 static const struct got_error *
587 cmd_import(int argc, char *argv[])
589 const struct got_error *error = NULL;
590 char *path_dir = NULL, *repo_path = NULL, *logmsg = NULL;
591 char *gitconfig_path = NULL, *editor = NULL, *author = NULL;
592 const char *branch_name = "main";
593 char *refname = NULL, *id_str = NULL, *logmsg_path = NULL;
594 struct got_repository *repo = NULL;
595 struct got_reference *branch_ref = NULL, *head_ref = NULL;
596 struct got_object_id *new_commit_id = NULL;
597 int ch;
598 struct got_pathlist_head ignores;
599 struct got_pathlist_entry *pe;
600 int preserve_logmsg = 0;
602 TAILQ_INIT(&ignores);
604 while ((ch = getopt(argc, argv, "b:m:r:I:")) != -1) {
605 switch (ch) {
606 case 'b':
607 branch_name = optarg;
608 break;
609 case 'm':
610 logmsg = strdup(optarg);
611 if (logmsg == NULL) {
612 error = got_error_from_errno("strdup");
613 goto done;
615 break;
616 case 'r':
617 repo_path = realpath(optarg, NULL);
618 if (repo_path == NULL) {
619 error = got_error_from_errno2("realpath",
620 optarg);
621 goto done;
623 break;
624 case 'I':
625 if (optarg[0] == '\0')
626 break;
627 error = got_pathlist_insert(&pe, &ignores, optarg,
628 NULL);
629 if (error)
630 goto done;
631 break;
632 default:
633 usage_import();
634 /* NOTREACHED */
638 argc -= optind;
639 argv += optind;
641 #ifndef PROFILE
642 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
643 "unveil",
644 NULL) == -1)
645 err(1, "pledge");
646 #endif
647 if (argc != 1)
648 usage_import();
650 if (repo_path == NULL) {
651 repo_path = getcwd(NULL, 0);
652 if (repo_path == NULL)
653 return got_error_from_errno("getcwd");
655 got_path_strip_trailing_slashes(repo_path);
656 error = get_gitconfig_path(&gitconfig_path);
657 if (error)
658 goto done;
659 error = got_repo_open(&repo, repo_path, gitconfig_path);
660 if (error)
661 goto done;
663 error = get_author(&author, repo);
664 if (error)
665 return error;
667 /*
668 * Don't let the user create a branch name with a leading '-'.
669 * While technically a valid reference name, this case is usually
670 * an unintended typo.
671 */
672 if (branch_name[0] == '-')
673 return got_error_path(branch_name, GOT_ERR_REF_NAME_MINUS);
675 if (asprintf(&refname, "refs/heads/%s", branch_name) == -1) {
676 error = got_error_from_errno("asprintf");
677 goto done;
680 error = got_ref_open(&branch_ref, repo, refname, 0);
681 if (error) {
682 if (error->code != GOT_ERR_NOT_REF)
683 goto done;
684 } else {
685 error = got_error_msg(GOT_ERR_BRANCH_EXISTS,
686 "import target branch already exists");
687 goto done;
690 path_dir = realpath(argv[0], NULL);
691 if (path_dir == NULL) {
692 error = got_error_from_errno2("realpath", argv[0]);
693 goto done;
695 got_path_strip_trailing_slashes(path_dir);
697 /*
698 * unveil(2) traverses exec(2); if an editor is used we have
699 * to apply unveil after the log message has been written.
700 */
701 if (logmsg == NULL || strlen(logmsg) == 0) {
702 error = get_editor(&editor);
703 if (error)
704 goto done;
705 free(logmsg);
706 error = collect_import_msg(&logmsg, &logmsg_path, editor,
707 path_dir, refname);
708 if (error) {
709 if (error->code != GOT_ERR_COMMIT_MSG_EMPTY &&
710 logmsg_path != NULL)
711 preserve_logmsg = 1;
712 goto done;
716 if (unveil(path_dir, "r") != 0) {
717 error = got_error_from_errno2("unveil", path_dir);
718 if (logmsg_path)
719 preserve_logmsg = 1;
720 goto done;
723 error = apply_unveil(got_repo_get_path(repo), 0, NULL);
724 if (error) {
725 if (logmsg_path)
726 preserve_logmsg = 1;
727 goto done;
730 error = got_repo_import(&new_commit_id, path_dir, logmsg,
731 author, &ignores, repo, import_progress, NULL);
732 if (error) {
733 if (logmsg_path)
734 preserve_logmsg = 1;
735 goto done;
738 error = got_ref_alloc(&branch_ref, refname, new_commit_id);
739 if (error) {
740 if (logmsg_path)
741 preserve_logmsg = 1;
742 goto done;
745 error = got_ref_write(branch_ref, repo);
746 if (error) {
747 if (logmsg_path)
748 preserve_logmsg = 1;
749 goto done;
752 error = got_object_id_str(&id_str, new_commit_id);
753 if (error) {
754 if (logmsg_path)
755 preserve_logmsg = 1;
756 goto done;
759 error = got_ref_open(&head_ref, repo, GOT_REF_HEAD, 0);
760 if (error) {
761 if (error->code != GOT_ERR_NOT_REF) {
762 if (logmsg_path)
763 preserve_logmsg = 1;
764 goto done;
767 error = got_ref_alloc_symref(&head_ref, GOT_REF_HEAD,
768 branch_ref);
769 if (error) {
770 if (logmsg_path)
771 preserve_logmsg = 1;
772 goto done;
775 error = got_ref_write(head_ref, repo);
776 if (error) {
777 if (logmsg_path)
778 preserve_logmsg = 1;
779 goto done;
783 printf("Created branch %s with commit %s\n",
784 got_ref_get_name(branch_ref), id_str);
785 done:
786 if (preserve_logmsg) {
787 fprintf(stderr, "%s: log message preserved in %s\n",
788 getprogname(), logmsg_path);
789 } else if (logmsg_path && unlink(logmsg_path) == -1 && error == NULL)
790 error = got_error_from_errno2("unlink", logmsg_path);
791 free(logmsg);
792 free(logmsg_path);
793 free(repo_path);
794 free(editor);
795 free(refname);
796 free(new_commit_id);
797 free(id_str);
798 free(author);
799 free(gitconfig_path);
800 if (branch_ref)
801 got_ref_close(branch_ref);
802 if (head_ref)
803 got_ref_close(head_ref);
804 return error;
807 __dead static void
808 usage_clone(void)
810 fprintf(stderr, "usage: %s clone repo-url\n", getprogname());
811 exit(1);
814 __dead static void
815 usage_checkout(void)
817 fprintf(stderr, "usage: %s checkout [-E] [-b branch] [-c commit] "
818 "[-p prefix] repository-path [worktree-path]\n", getprogname());
819 exit(1);
822 static void
823 show_worktree_base_ref_warning(void)
825 fprintf(stderr, "%s: warning: could not create a reference "
826 "to the work tree's base commit; the commit could be "
827 "garbage-collected by Git; making the repository "
828 "writable and running 'got update' will prevent this\n",
829 getprogname());
832 struct got_checkout_progress_arg {
833 const char *worktree_path;
834 int had_base_commit_ref_error;
835 };
837 static const struct got_error *
838 checkout_progress(void *arg, unsigned char status, const char *path)
840 struct got_checkout_progress_arg *a = arg;
842 /* Base commit bump happens silently. */
843 if (status == GOT_STATUS_BUMP_BASE)
844 return NULL;
846 if (status == GOT_STATUS_BASE_REF_ERR) {
847 a->had_base_commit_ref_error = 1;
848 return NULL;
851 while (path[0] == '/')
852 path++;
854 printf("%c %s/%s\n", status, a->worktree_path, path);
855 return NULL;
858 static const struct got_error *
859 check_cancelled(void *arg)
861 if (sigint_received || sigpipe_received)
862 return got_error(GOT_ERR_CANCELLED);
863 return NULL;
866 static const struct got_error *
867 check_linear_ancestry(struct got_object_id *commit_id,
868 struct got_object_id *base_commit_id, int allow_forwards_in_time_only,
869 struct got_repository *repo)
871 const struct got_error *err = NULL;
872 struct got_object_id *yca_id;
874 err = got_commit_graph_find_youngest_common_ancestor(&yca_id,
875 commit_id, base_commit_id, repo, check_cancelled, NULL);
876 if (err)
877 return err;
879 if (yca_id == NULL)
880 return got_error(GOT_ERR_ANCESTRY);
882 /*
883 * Require a straight line of history between the target commit
884 * and the work tree's base commit.
886 * Non-linear situations such as this require a rebase:
888 * (commit) D F (base_commit)
889 * \ /
890 * C E
891 * \ /
892 * B (yca)
893 * |
894 * A
896 * 'got update' only handles linear cases:
897 * Update forwards in time: A (base/yca) - B - C - D (commit)
898 * Update backwards in time: D (base) - C - B - A (commit/yca)
899 */
900 if (allow_forwards_in_time_only) {
901 if (got_object_id_cmp(base_commit_id, yca_id) != 0)
902 return got_error(GOT_ERR_ANCESTRY);
903 } else if (got_object_id_cmp(commit_id, yca_id) != 0 &&
904 got_object_id_cmp(base_commit_id, yca_id) != 0)
905 return got_error(GOT_ERR_ANCESTRY);
907 free(yca_id);
908 return NULL;
911 static const struct got_error *
912 check_same_branch(struct got_object_id *commit_id,
913 struct got_reference *head_ref, struct got_object_id *yca_id,
914 struct got_repository *repo)
916 const struct got_error *err = NULL;
917 struct got_commit_graph *graph = NULL;
918 struct got_object_id *head_commit_id = NULL;
919 int is_same_branch = 0;
921 err = got_ref_resolve(&head_commit_id, repo, head_ref);
922 if (err)
923 goto done;
925 if (got_object_id_cmp(head_commit_id, commit_id) == 0) {
926 is_same_branch = 1;
927 goto done;
929 if (yca_id && got_object_id_cmp(commit_id, yca_id) == 0) {
930 is_same_branch = 1;
931 goto done;
934 err = got_commit_graph_open(&graph, "/", 1);
935 if (err)
936 goto done;
938 err = got_commit_graph_iter_start(graph, head_commit_id, repo,
939 check_cancelled, NULL);
940 if (err)
941 goto done;
943 for (;;) {
944 struct got_object_id *id;
945 err = got_commit_graph_iter_next(&id, graph, repo,
946 check_cancelled, NULL);
947 if (err) {
948 if (err->code == GOT_ERR_ITER_COMPLETED)
949 err = NULL;
950 break;
953 if (id) {
954 if (yca_id && got_object_id_cmp(id, yca_id) == 0)
955 break;
956 if (got_object_id_cmp(id, commit_id) == 0) {
957 is_same_branch = 1;
958 break;
962 done:
963 if (graph)
964 got_commit_graph_close(graph);
965 free(head_commit_id);
966 if (!err && !is_same_branch)
967 err = got_error(GOT_ERR_ANCESTRY);
968 return err;
971 static const struct got_error *
972 cmd_clone(int argc, char *argv[])
974 const struct got_error *err = NULL;
975 const char *uri, *branch_filter, *dirname;
976 char *proto, *host, *port, *repo_name, *server_path;
977 char *default_destdir = NULL;
978 const char *repo_path;
979 struct got_repository *repo = NULL;
980 int ch;
982 while ((ch = getopt(argc, argv, "b:")) != -1) {
983 switch (ch) {
984 case 'b':
985 branch_filter = optarg;
986 break;
987 default:
988 usage_clone();
989 break;
992 argc -= optind;
993 argv += optind;
994 uri = argv[0];
995 if(argc == 1)
996 dirname = NULL;
997 else if(argc == 2)
998 dirname = argv[1];
999 else
1000 usage_clone();
1002 err = got_fetch_parse_uri(&proto, &host, &port, &server_path,
1003 &repo_name, argv[0]);
1004 if (err)
1005 goto done;
1007 if (dirname == NULL) {
1008 if (asprintf(&default_destdir, "%s.git", repo_name) == -1) {
1009 err = got_error_from_errno("asprintf");
1010 goto done;
1012 repo_path = default_destdir;
1013 } else
1014 repo_path = dirname;
1016 err = got_path_mkdir(repo_path);
1017 if (err)
1018 goto done;
1020 err = got_repo_init(repo_path);
1021 if (err != NULL)
1022 goto done;
1024 err = got_repo_open(&repo, repo_path, NULL);
1025 if (err)
1026 goto done;
1028 err = got_fetch(proto, host, port, server_path, repo_name,
1029 branch_filter, repo);
1030 done:
1031 if (repo)
1032 got_repo_close(repo);
1033 free(proto);
1034 free(host);
1035 free(port);
1036 free(server_path);
1037 free(repo_name);
1038 free(default_destdir);
1039 return err;
1042 static const struct got_error *
1043 checkout_ancestry_error(struct got_reference *ref, const char *commit_id_str)
1045 static char msg[512];
1046 const char *branch_name;
1048 if (got_ref_is_symbolic(ref))
1049 branch_name = got_ref_get_symref_target(ref);
1050 else
1051 branch_name = got_ref_get_name(ref);
1053 if (strncmp("refs/heads/", branch_name, 11) == 0)
1054 branch_name += 11;
1056 snprintf(msg, sizeof(msg),
1057 "target commit is not contained in branch '%s'; "
1058 "the branch to use must be specified with -b; "
1059 "if necessary a new branch can be created for "
1060 "this commit with 'got branch -c %s BRANCH_NAME'",
1061 branch_name, commit_id_str);
1063 return got_error_msg(GOT_ERR_ANCESTRY, msg);
1066 static const struct got_error *
1067 cmd_checkout(int argc, char *argv[])
1069 const struct got_error *error = NULL;
1070 struct got_repository *repo = NULL;
1071 struct got_reference *head_ref = NULL;
1072 struct got_worktree *worktree = NULL;
1073 char *repo_path = NULL;
1074 char *worktree_path = NULL;
1075 const char *path_prefix = "";
1076 const char *branch_name = GOT_REF_HEAD;
1077 char *commit_id_str = NULL;
1078 int ch, same_path_prefix, allow_nonempty = 0;
1079 struct got_pathlist_head paths;
1080 struct got_checkout_progress_arg cpa;
1082 TAILQ_INIT(&paths);
1084 while ((ch = getopt(argc, argv, "b:c:Ep:")) != -1) {
1085 switch (ch) {
1086 case 'b':
1087 branch_name = optarg;
1088 break;
1089 case 'c':
1090 commit_id_str = strdup(optarg);
1091 if (commit_id_str == NULL)
1092 return got_error_from_errno("strdup");
1093 break;
1094 case 'E':
1095 allow_nonempty = 1;
1096 break;
1097 case 'p':
1098 path_prefix = optarg;
1099 break;
1100 default:
1101 usage_checkout();
1102 /* NOTREACHED */
1106 argc -= optind;
1107 argv += optind;
1109 #ifndef PROFILE
1110 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
1111 "unveil", NULL) == -1)
1112 err(1, "pledge");
1113 #endif
1114 if (argc == 1) {
1115 char *cwd, *base, *dotgit;
1116 repo_path = realpath(argv[0], NULL);
1117 if (repo_path == NULL)
1118 return got_error_from_errno2("realpath", argv[0]);
1119 cwd = getcwd(NULL, 0);
1120 if (cwd == NULL) {
1121 error = got_error_from_errno("getcwd");
1122 goto done;
1124 if (path_prefix[0]) {
1125 base = basename(path_prefix);
1126 if (base == NULL) {
1127 error = got_error_from_errno2("basename",
1128 path_prefix);
1129 goto done;
1131 } else {
1132 base = basename(repo_path);
1133 if (base == NULL) {
1134 error = got_error_from_errno2("basename",
1135 repo_path);
1136 goto done;
1139 dotgit = strstr(base, ".git");
1140 if (dotgit)
1141 *dotgit = '\0';
1142 if (asprintf(&worktree_path, "%s/%s", cwd, base) == -1) {
1143 error = got_error_from_errno("asprintf");
1144 free(cwd);
1145 goto done;
1147 free(cwd);
1148 } else if (argc == 2) {
1149 repo_path = realpath(argv[0], NULL);
1150 if (repo_path == NULL) {
1151 error = got_error_from_errno2("realpath", argv[0]);
1152 goto done;
1154 worktree_path = realpath(argv[1], NULL);
1155 if (worktree_path == NULL) {
1156 if (errno != ENOENT) {
1157 error = got_error_from_errno2("realpath",
1158 argv[1]);
1159 goto done;
1161 worktree_path = strdup(argv[1]);
1162 if (worktree_path == NULL) {
1163 error = got_error_from_errno("strdup");
1164 goto done;
1167 } else
1168 usage_checkout();
1170 got_path_strip_trailing_slashes(repo_path);
1171 got_path_strip_trailing_slashes(worktree_path);
1173 error = got_repo_open(&repo, repo_path, NULL);
1174 if (error != NULL)
1175 goto done;
1177 /* Pre-create work tree path for unveil(2) */
1178 error = got_path_mkdir(worktree_path);
1179 if (error) {
1180 if (!(error->code == GOT_ERR_ERRNO && errno == EISDIR) &&
1181 !(error->code == GOT_ERR_ERRNO && errno == EEXIST))
1182 goto done;
1183 if (!allow_nonempty &&
1184 !got_path_dir_is_empty(worktree_path)) {
1185 error = got_error_path(worktree_path,
1186 GOT_ERR_DIR_NOT_EMPTY);
1187 goto done;
1191 error = apply_unveil(got_repo_get_path(repo), 0, worktree_path);
1192 if (error)
1193 goto done;
1195 error = got_ref_open(&head_ref, repo, branch_name, 0);
1196 if (error != NULL)
1197 goto done;
1199 error = got_worktree_init(worktree_path, head_ref, path_prefix, repo);
1200 if (error != NULL && !(error->code == GOT_ERR_ERRNO && errno == EEXIST))
1201 goto done;
1203 error = got_worktree_open(&worktree, worktree_path);
1204 if (error != NULL)
1205 goto done;
1207 error = got_worktree_match_path_prefix(&same_path_prefix, worktree,
1208 path_prefix);
1209 if (error != NULL)
1210 goto done;
1211 if (!same_path_prefix) {
1212 error = got_error(GOT_ERR_PATH_PREFIX);
1213 goto done;
1216 if (commit_id_str) {
1217 struct got_object_id *commit_id;
1218 error = got_repo_match_object_id(&commit_id, NULL,
1219 commit_id_str, GOT_OBJ_TYPE_COMMIT, 1, repo);
1220 if (error)
1221 goto done;
1222 error = check_linear_ancestry(commit_id,
1223 got_worktree_get_base_commit_id(worktree), 0, repo);
1224 if (error != NULL) {
1225 free(commit_id);
1226 if (error->code == GOT_ERR_ANCESTRY) {
1227 error = checkout_ancestry_error(
1228 head_ref, commit_id_str);
1230 goto done;
1232 error = check_same_branch(commit_id, head_ref, NULL, repo);
1233 if (error) {
1234 if (error->code == GOT_ERR_ANCESTRY) {
1235 error = checkout_ancestry_error(
1236 head_ref, commit_id_str);
1238 goto done;
1240 error = got_worktree_set_base_commit_id(worktree, repo,
1241 commit_id);
1242 free(commit_id);
1243 if (error)
1244 goto done;
1247 error = got_pathlist_append(&paths, "", NULL);
1248 if (error)
1249 goto done;
1250 cpa.worktree_path = worktree_path;
1251 cpa.had_base_commit_ref_error = 0;
1252 error = got_worktree_checkout_files(worktree, &paths, repo,
1253 checkout_progress, &cpa, check_cancelled, NULL);
1254 if (error != NULL)
1255 goto done;
1257 printf("Now shut up and hack\n");
1258 if (cpa.had_base_commit_ref_error)
1259 show_worktree_base_ref_warning();
1260 done:
1261 got_pathlist_free(&paths);
1262 free(commit_id_str);
1263 free(repo_path);
1264 free(worktree_path);
1265 return error;
1268 __dead static void
1269 usage_update(void)
1271 fprintf(stderr, "usage: %s update [-b branch] [-c commit] [path ...]\n",
1272 getprogname());
1273 exit(1);
1276 static const struct got_error *
1277 update_progress(void *arg, unsigned char status, const char *path)
1279 int *did_something = arg;
1281 if (status == GOT_STATUS_EXISTS ||
1282 status == GOT_STATUS_BASE_REF_ERR)
1283 return NULL;
1285 *did_something = 1;
1287 /* Base commit bump happens silently. */
1288 if (status == GOT_STATUS_BUMP_BASE)
1289 return NULL;
1291 while (path[0] == '/')
1292 path++;
1293 printf("%c %s\n", status, path);
1294 return NULL;
1297 static const struct got_error *
1298 switch_head_ref(struct got_reference *head_ref,
1299 struct got_object_id *commit_id, struct got_worktree *worktree,
1300 struct got_repository *repo)
1302 const struct got_error *err = NULL;
1303 char *base_id_str;
1304 int ref_has_moved = 0;
1306 /* Trivial case: switching between two different references. */
1307 if (strcmp(got_ref_get_name(head_ref),
1308 got_worktree_get_head_ref_name(worktree)) != 0) {
1309 printf("Switching work tree from %s to %s\n",
1310 got_worktree_get_head_ref_name(worktree),
1311 got_ref_get_name(head_ref));
1312 return got_worktree_set_head_ref(worktree, head_ref);
1315 err = check_linear_ancestry(commit_id,
1316 got_worktree_get_base_commit_id(worktree), 0, repo);
1317 if (err) {
1318 if (err->code != GOT_ERR_ANCESTRY)
1319 return err;
1320 ref_has_moved = 1;
1322 if (!ref_has_moved)
1323 return NULL;
1325 /* Switching to a rebased branch with the same reference name. */
1326 err = got_object_id_str(&base_id_str,
1327 got_worktree_get_base_commit_id(worktree));
1328 if (err)
1329 return err;
1330 printf("Reference %s now points at a different branch\n",
1331 got_worktree_get_head_ref_name(worktree));
1332 printf("Switching work tree from %s to %s\n", base_id_str,
1333 got_worktree_get_head_ref_name(worktree));
1334 return NULL;
1337 static const struct got_error *
1338 check_rebase_or_histedit_in_progress(struct got_worktree *worktree)
1340 const struct got_error *err;
1341 int in_progress;
1343 err = got_worktree_rebase_in_progress(&in_progress, worktree);
1344 if (err)
1345 return err;
1346 if (in_progress)
1347 return got_error(GOT_ERR_REBASING);
1349 err = got_worktree_histedit_in_progress(&in_progress, worktree);
1350 if (err)
1351 return err;
1352 if (in_progress)
1353 return got_error(GOT_ERR_HISTEDIT_BUSY);
1355 return NULL;
1358 static const struct got_error *
1359 get_worktree_paths_from_argv(struct got_pathlist_head *paths, int argc,
1360 char *argv[], struct got_worktree *worktree)
1362 const struct got_error *err = NULL;
1363 char *path;
1364 int i;
1366 if (argc == 0) {
1367 path = strdup("");
1368 if (path == NULL)
1369 return got_error_from_errno("strdup");
1370 return got_pathlist_append(paths, path, NULL);
1373 for (i = 0; i < argc; i++) {
1374 err = got_worktree_resolve_path(&path, worktree, argv[i]);
1375 if (err)
1376 break;
1377 err = got_pathlist_append(paths, path, NULL);
1378 if (err) {
1379 free(path);
1380 break;
1384 return err;
1387 static const struct got_error *
1388 cmd_update(int argc, char *argv[])
1390 const struct got_error *error = NULL;
1391 struct got_repository *repo = NULL;
1392 struct got_worktree *worktree = NULL;
1393 char *worktree_path = NULL;
1394 struct got_object_id *commit_id = NULL;
1395 char *commit_id_str = NULL;
1396 const char *branch_name = NULL;
1397 struct got_reference *head_ref = NULL;
1398 struct got_pathlist_head paths;
1399 struct got_pathlist_entry *pe;
1400 int ch, did_something = 0;
1402 TAILQ_INIT(&paths);
1404 while ((ch = getopt(argc, argv, "b:c:")) != -1) {
1405 switch (ch) {
1406 case 'b':
1407 branch_name = optarg;
1408 break;
1409 case 'c':
1410 commit_id_str = strdup(optarg);
1411 if (commit_id_str == NULL)
1412 return got_error_from_errno("strdup");
1413 break;
1414 default:
1415 usage_update();
1416 /* NOTREACHED */
1420 argc -= optind;
1421 argv += optind;
1423 #ifndef PROFILE
1424 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
1425 "unveil", NULL) == -1)
1426 err(1, "pledge");
1427 #endif
1428 worktree_path = getcwd(NULL, 0);
1429 if (worktree_path == NULL) {
1430 error = got_error_from_errno("getcwd");
1431 goto done;
1433 error = got_worktree_open(&worktree, worktree_path);
1434 if (error)
1435 goto done;
1437 error = check_rebase_or_histedit_in_progress(worktree);
1438 if (error)
1439 goto done;
1441 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
1442 NULL);
1443 if (error != NULL)
1444 goto done;
1446 error = apply_unveil(got_repo_get_path(repo), 0,
1447 got_worktree_get_root_path(worktree));
1448 if (error)
1449 goto done;
1451 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
1452 if (error)
1453 goto done;
1455 error = got_ref_open(&head_ref, repo, branch_name ? branch_name :
1456 got_worktree_get_head_ref_name(worktree), 0);
1457 if (error != NULL)
1458 goto done;
1459 if (commit_id_str == NULL) {
1460 error = got_ref_resolve(&commit_id, repo, head_ref);
1461 if (error != NULL)
1462 goto done;
1463 error = got_object_id_str(&commit_id_str, commit_id);
1464 if (error != NULL)
1465 goto done;
1466 } else {
1467 error = got_repo_match_object_id(&commit_id, NULL,
1468 commit_id_str, GOT_OBJ_TYPE_COMMIT, 1, repo);
1469 free(commit_id_str);
1470 commit_id_str = NULL;
1471 if (error)
1472 goto done;
1473 error = got_object_id_str(&commit_id_str, commit_id);
1474 if (error)
1475 goto done;
1478 if (branch_name) {
1479 struct got_object_id *head_commit_id;
1480 TAILQ_FOREACH(pe, &paths, entry) {
1481 if (pe->path_len == 0)
1482 continue;
1483 error = got_error_msg(GOT_ERR_BAD_PATH,
1484 "switching between branches requires that "
1485 "the entire work tree gets updated");
1486 goto done;
1488 error = got_ref_resolve(&head_commit_id, repo, head_ref);
1489 if (error)
1490 goto done;
1491 error = check_linear_ancestry(commit_id, head_commit_id, 0,
1492 repo);
1493 free(head_commit_id);
1494 if (error != NULL)
1495 goto done;
1496 error = check_same_branch(commit_id, head_ref, NULL, repo);
1497 if (error)
1498 goto done;
1499 error = switch_head_ref(head_ref, commit_id, worktree, repo);
1500 if (error)
1501 goto done;
1502 } else {
1503 error = check_linear_ancestry(commit_id,
1504 got_worktree_get_base_commit_id(worktree), 0, repo);
1505 if (error != NULL) {
1506 if (error->code == GOT_ERR_ANCESTRY)
1507 error = got_error(GOT_ERR_BRANCH_MOVED);
1508 goto done;
1510 error = check_same_branch(commit_id, head_ref, NULL, repo);
1511 if (error)
1512 goto done;
1515 if (got_object_id_cmp(got_worktree_get_base_commit_id(worktree),
1516 commit_id) != 0) {
1517 error = got_worktree_set_base_commit_id(worktree, repo,
1518 commit_id);
1519 if (error)
1520 goto done;
1523 error = got_worktree_checkout_files(worktree, &paths, repo,
1524 update_progress, &did_something, check_cancelled, NULL);
1525 if (error != NULL)
1526 goto done;
1528 if (did_something)
1529 printf("Updated to commit %s\n", commit_id_str);
1530 else
1531 printf("Already up-to-date\n");
1532 done:
1533 free(worktree_path);
1534 TAILQ_FOREACH(pe, &paths, entry)
1535 free((char *)pe->path);
1536 got_pathlist_free(&paths);
1537 free(commit_id);
1538 free(commit_id_str);
1539 return error;
1542 static const struct got_error *
1543 diff_blobs(struct got_object_id *blob_id1, struct got_object_id *blob_id2,
1544 const char *path, int diff_context, int ignore_whitespace,
1545 struct got_repository *repo)
1547 const struct got_error *err = NULL;
1548 struct got_blob_object *blob1 = NULL, *blob2 = NULL;
1550 if (blob_id1) {
1551 err = got_object_open_as_blob(&blob1, repo, blob_id1, 8192);
1552 if (err)
1553 goto done;
1556 err = got_object_open_as_blob(&blob2, repo, blob_id2, 8192);
1557 if (err)
1558 goto done;
1560 while (path[0] == '/')
1561 path++;
1562 err = got_diff_blob(blob1, blob2, path, path, diff_context,
1563 ignore_whitespace, stdout);
1564 done:
1565 if (blob1)
1566 got_object_blob_close(blob1);
1567 got_object_blob_close(blob2);
1568 return err;
1571 static const struct got_error *
1572 diff_trees(struct got_object_id *tree_id1, struct got_object_id *tree_id2,
1573 const char *path, int diff_context, int ignore_whitespace,
1574 struct got_repository *repo)
1576 const struct got_error *err = NULL;
1577 struct got_tree_object *tree1 = NULL, *tree2 = NULL;
1578 struct got_diff_blob_output_unidiff_arg arg;
1580 if (tree_id1) {
1581 err = got_object_open_as_tree(&tree1, repo, tree_id1);
1582 if (err)
1583 goto done;
1586 err = got_object_open_as_tree(&tree2, repo, tree_id2);
1587 if (err)
1588 goto done;
1590 arg.diff_context = diff_context;
1591 arg.ignore_whitespace = ignore_whitespace;
1592 arg.outfile = stdout;
1593 while (path[0] == '/')
1594 path++;
1595 err = got_diff_tree(tree1, tree2, path, path, repo,
1596 got_diff_blob_output_unidiff, &arg, 1);
1597 done:
1598 if (tree1)
1599 got_object_tree_close(tree1);
1600 if (tree2)
1601 got_object_tree_close(tree2);
1602 return err;
1605 static const struct got_error *
1606 print_patch(struct got_commit_object *commit, struct got_object_id *id,
1607 const char *path, int diff_context, struct got_repository *repo)
1609 const struct got_error *err = NULL;
1610 struct got_commit_object *pcommit = NULL;
1611 char *id_str1 = NULL, *id_str2 = NULL;
1612 struct got_object_id *obj_id1 = NULL, *obj_id2 = NULL;
1613 struct got_object_qid *qid;
1615 qid = SIMPLEQ_FIRST(got_object_commit_get_parent_ids(commit));
1616 if (qid != NULL) {
1617 err = got_object_open_as_commit(&pcommit, repo,
1618 qid->id);
1619 if (err)
1620 return err;
1623 if (path && path[0] != '\0') {
1624 int obj_type;
1625 err = got_object_id_by_path(&obj_id2, repo, id, path);
1626 if (err)
1627 goto done;
1628 err = got_object_id_str(&id_str2, obj_id2);
1629 if (err) {
1630 free(obj_id2);
1631 goto done;
1633 if (pcommit) {
1634 err = got_object_id_by_path(&obj_id1, repo,
1635 qid->id, path);
1636 if (err) {
1637 free(obj_id2);
1638 goto done;
1640 err = got_object_id_str(&id_str1, obj_id1);
1641 if (err) {
1642 free(obj_id2);
1643 goto done;
1646 err = got_object_get_type(&obj_type, repo, obj_id2);
1647 if (err) {
1648 free(obj_id2);
1649 goto done;
1651 printf("diff %s %s\n", id_str1 ? id_str1 : "/dev/null", id_str2);
1652 switch (obj_type) {
1653 case GOT_OBJ_TYPE_BLOB:
1654 err = diff_blobs(obj_id1, obj_id2, path, diff_context,
1655 0, repo);
1656 break;
1657 case GOT_OBJ_TYPE_TREE:
1658 err = diff_trees(obj_id1, obj_id2, path, diff_context,
1659 0, repo);
1660 break;
1661 default:
1662 err = got_error(GOT_ERR_OBJ_TYPE);
1663 break;
1665 free(obj_id1);
1666 free(obj_id2);
1667 } else {
1668 obj_id2 = got_object_commit_get_tree_id(commit);
1669 err = got_object_id_str(&id_str2, obj_id2);
1670 if (err)
1671 goto done;
1672 obj_id1 = got_object_commit_get_tree_id(pcommit);
1673 err = got_object_id_str(&id_str1, obj_id1);
1674 if (err)
1675 goto done;
1676 printf("diff %s %s\n", id_str1 ? id_str1 : "/dev/null", id_str2);
1677 err = diff_trees(obj_id1, obj_id2, "", diff_context, 0, repo);
1679 done:
1680 free(id_str1);
1681 free(id_str2);
1682 if (pcommit)
1683 got_object_commit_close(pcommit);
1684 return err;
1687 static char *
1688 get_datestr(time_t *time, char *datebuf)
1690 struct tm mytm, *tm;
1691 char *p, *s;
1693 tm = gmtime_r(time, &mytm);
1694 if (tm == NULL)
1695 return NULL;
1696 s = asctime_r(tm, datebuf);
1697 if (s == NULL)
1698 return NULL;
1699 p = strchr(s, '\n');
1700 if (p)
1701 *p = '\0';
1702 return s;
1705 static const struct got_error *
1706 match_logmsg(int *have_match, struct got_object_id *id,
1707 struct got_commit_object *commit, regex_t *regex)
1709 const struct got_error *err = NULL;
1710 regmatch_t regmatch;
1711 char *id_str = NULL, *logmsg = NULL;
1713 *have_match = 0;
1715 err = got_object_id_str(&id_str, id);
1716 if (err)
1717 return err;
1719 err = got_object_commit_get_logmsg(&logmsg, commit);
1720 if (err)
1721 goto done;
1723 if (regexec(regex, logmsg, 1, &regmatch, 0) == 0)
1724 *have_match = 1;
1725 done:
1726 free(id_str);
1727 free(logmsg);
1728 return err;
1731 #define GOT_COMMIT_SEP_STR "-----------------------------------------------\n"
1733 static const struct got_error *
1734 print_commit(struct got_commit_object *commit, struct got_object_id *id,
1735 struct got_repository *repo, const char *path, int show_patch,
1736 int diff_context, struct got_reflist_head *refs)
1738 const struct got_error *err = NULL;
1739 char *id_str, *datestr, *logmsg0, *logmsg, *line;
1740 char datebuf[26];
1741 time_t committer_time;
1742 const char *author, *committer;
1743 char *refs_str = NULL;
1744 struct got_reflist_entry *re;
1746 SIMPLEQ_FOREACH(re, refs, entry) {
1747 char *s;
1748 const char *name;
1749 struct got_tag_object *tag = NULL;
1750 int cmp;
1752 name = got_ref_get_name(re->ref);
1753 if (strcmp(name, GOT_REF_HEAD) == 0)
1754 continue;
1755 if (strncmp(name, "refs/", 5) == 0)
1756 name += 5;
1757 if (strncmp(name, "got/", 4) == 0)
1758 continue;
1759 if (strncmp(name, "heads/", 6) == 0)
1760 name += 6;
1761 if (strncmp(name, "remotes/", 8) == 0)
1762 name += 8;
1763 if (strncmp(name, "tags/", 5) == 0) {
1764 err = got_object_open_as_tag(&tag, repo, re->id);
1765 if (err) {
1766 if (err->code != GOT_ERR_OBJ_TYPE)
1767 return err;
1768 /* Ref points at something other than a tag. */
1769 err = NULL;
1770 tag = NULL;
1773 cmp = got_object_id_cmp(tag ?
1774 got_object_tag_get_object_id(tag) : re->id, id);
1775 if (tag)
1776 got_object_tag_close(tag);
1777 if (cmp != 0)
1778 continue;
1779 s = refs_str;
1780 if (asprintf(&refs_str, "%s%s%s", s ? s : "", s ? ", " : "",
1781 name) == -1) {
1782 err = got_error_from_errno("asprintf");
1783 free(s);
1784 return err;
1786 free(s);
1788 err = got_object_id_str(&id_str, id);
1789 if (err)
1790 return err;
1792 printf(GOT_COMMIT_SEP_STR);
1793 printf("commit %s%s%s%s\n", id_str, refs_str ? " (" : "",
1794 refs_str ? refs_str : "", refs_str ? ")" : "");
1795 free(id_str);
1796 id_str = NULL;
1797 free(refs_str);
1798 refs_str = NULL;
1799 printf("from: %s\n", got_object_commit_get_author(commit));
1800 committer_time = got_object_commit_get_committer_time(commit);
1801 datestr = get_datestr(&committer_time, datebuf);
1802 if (datestr)
1803 printf("date: %s UTC\n", datestr);
1804 author = got_object_commit_get_author(commit);
1805 committer = got_object_commit_get_committer(commit);
1806 if (strcmp(author, committer) != 0)
1807 printf("via: %s\n", committer);
1808 if (got_object_commit_get_nparents(commit) > 1) {
1809 const struct got_object_id_queue *parent_ids;
1810 struct got_object_qid *qid;
1811 int n = 1;
1812 parent_ids = got_object_commit_get_parent_ids(commit);
1813 SIMPLEQ_FOREACH(qid, parent_ids, entry) {
1814 err = got_object_id_str(&id_str, qid->id);
1815 if (err)
1816 return err;
1817 printf("parent %d: %s\n", n++, id_str);
1818 free(id_str);
1822 err = got_object_commit_get_logmsg(&logmsg0, commit);
1823 if (err)
1824 return err;
1826 logmsg = logmsg0;
1827 do {
1828 line = strsep(&logmsg, "\n");
1829 if (line)
1830 printf(" %s\n", line);
1831 } while (line);
1832 free(logmsg0);
1834 if (show_patch) {
1835 err = print_patch(commit, id, path, diff_context, repo);
1836 if (err == 0)
1837 printf("\n");
1840 if (fflush(stdout) != 0 && err == NULL)
1841 err = got_error_from_errno("fflush");
1842 return err;
1845 static const struct got_error *
1846 print_commits(struct got_object_id *root_id, struct got_repository *repo,
1847 const char *path, int show_patch, const char *search_pattern,
1848 int diff_context, int limit, int log_branches,
1849 struct got_reflist_head *refs)
1851 const struct got_error *err;
1852 struct got_commit_graph *graph;
1853 regex_t regex;
1854 int have_match;
1856 if (search_pattern &&
1857 regcomp(&regex, search_pattern, REG_EXTENDED | REG_NOSUB | REG_NEWLINE))
1858 return got_error_msg(GOT_ERR_REGEX, search_pattern);
1860 err = got_commit_graph_open(&graph, path, !log_branches);
1861 if (err)
1862 return err;
1863 err = got_commit_graph_iter_start(graph, root_id, repo,
1864 check_cancelled, NULL);
1865 if (err)
1866 goto done;
1867 for (;;) {
1868 struct got_commit_object *commit;
1869 struct got_object_id *id;
1871 if (sigint_received || sigpipe_received)
1872 break;
1874 err = got_commit_graph_iter_next(&id, graph, repo,
1875 check_cancelled, NULL);
1876 if (err) {
1877 if (err->code == GOT_ERR_ITER_COMPLETED)
1878 err = NULL;
1879 break;
1881 if (id == NULL)
1882 break;
1884 err = got_object_open_as_commit(&commit, repo, id);
1885 if (err)
1886 break;
1888 if (search_pattern) {
1889 err = match_logmsg(&have_match, id, commit, &regex);
1890 if (err) {
1891 got_object_commit_close(commit);
1892 break;
1894 if (have_match == 0) {
1895 got_object_commit_close(commit);
1896 continue;
1900 err = print_commit(commit, id, repo, path, show_patch,
1901 diff_context, refs);
1902 got_object_commit_close(commit);
1903 if (err || (limit && --limit == 0))
1904 break;
1906 done:
1907 if (search_pattern)
1908 regfree(&regex);
1909 got_commit_graph_close(graph);
1910 return err;
1913 __dead static void
1914 usage_log(void)
1916 fprintf(stderr, "usage: %s log [-b] [-c commit] [-C number] [ -l N ] [-p] "
1917 "[-s search-pattern] [-r repository-path] [path]\n", getprogname());
1918 exit(1);
1921 static int
1922 get_default_log_limit(void)
1924 const char *got_default_log_limit;
1925 long long n;
1926 const char *errstr;
1928 got_default_log_limit = getenv("GOT_LOG_DEFAULT_LIMIT");
1929 if (got_default_log_limit == NULL)
1930 return 0;
1931 n = strtonum(got_default_log_limit, 0, INT_MAX, &errstr);
1932 if (errstr != NULL)
1933 return 0;
1934 return n;
1937 static const struct got_error *
1938 cmd_log(int argc, char *argv[])
1940 const struct got_error *error;
1941 struct got_repository *repo = NULL;
1942 struct got_worktree *worktree = NULL;
1943 struct got_commit_object *commit = NULL;
1944 struct got_object_id *id = NULL;
1945 char *repo_path = NULL, *path = NULL, *cwd = NULL, *in_repo_path = NULL;
1946 const char *start_commit = NULL, *search_pattern = NULL;
1947 int diff_context = -1, ch;
1948 int show_patch = 0, limit = 0, log_branches = 0;
1949 const char *errstr;
1950 struct got_reflist_head refs;
1952 SIMPLEQ_INIT(&refs);
1954 #ifndef PROFILE
1955 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
1956 NULL)
1957 == -1)
1958 err(1, "pledge");
1959 #endif
1961 limit = get_default_log_limit();
1963 while ((ch = getopt(argc, argv, "bpc:C:l:r:s:")) != -1) {
1964 switch (ch) {
1965 case 'p':
1966 show_patch = 1;
1967 break;
1968 case 'c':
1969 start_commit = optarg;
1970 break;
1971 case 'C':
1972 diff_context = strtonum(optarg, 0, GOT_DIFF_MAX_CONTEXT,
1973 &errstr);
1974 if (errstr != NULL)
1975 err(1, "-C option %s", errstr);
1976 break;
1977 case 'l':
1978 limit = strtonum(optarg, 0, INT_MAX, &errstr);
1979 if (errstr != NULL)
1980 err(1, "-l option %s", errstr);
1981 break;
1982 case 'b':
1983 log_branches = 1;
1984 break;
1985 case 'r':
1986 repo_path = realpath(optarg, NULL);
1987 if (repo_path == NULL)
1988 return got_error_from_errno2("realpath",
1989 optarg);
1990 got_path_strip_trailing_slashes(repo_path);
1991 break;
1992 case 's':
1993 search_pattern = optarg;
1994 break;
1995 default:
1996 usage_log();
1997 /* NOTREACHED */
2001 argc -= optind;
2002 argv += optind;
2004 if (diff_context == -1)
2005 diff_context = 3;
2006 else if (!show_patch)
2007 errx(1, "-C reguires -p");
2009 cwd = getcwd(NULL, 0);
2010 if (cwd == NULL) {
2011 error = got_error_from_errno("getcwd");
2012 goto done;
2015 error = got_worktree_open(&worktree, cwd);
2016 if (error && error->code != GOT_ERR_NOT_WORKTREE)
2017 goto done;
2018 error = NULL;
2020 if (argc == 0) {
2021 path = strdup("");
2022 if (path == NULL) {
2023 error = got_error_from_errno("strdup");
2024 goto done;
2026 } else if (argc == 1) {
2027 if (worktree) {
2028 error = got_worktree_resolve_path(&path, worktree,
2029 argv[0]);
2030 if (error)
2031 goto done;
2032 } else {
2033 path = strdup(argv[0]);
2034 if (path == NULL) {
2035 error = got_error_from_errno("strdup");
2036 goto done;
2039 } else
2040 usage_log();
2042 if (repo_path == NULL) {
2043 repo_path = worktree ?
2044 strdup(got_worktree_get_repo_path(worktree)) : strdup(cwd);
2046 if (repo_path == NULL) {
2047 error = got_error_from_errno("strdup");
2048 goto done;
2051 error = got_repo_open(&repo, repo_path, NULL);
2052 if (error != NULL)
2053 goto done;
2055 error = apply_unveil(got_repo_get_path(repo), 1,
2056 worktree ? got_worktree_get_root_path(worktree) : NULL);
2057 if (error)
2058 goto done;
2060 if (start_commit == NULL) {
2061 struct got_reference *head_ref;
2062 error = got_ref_open(&head_ref, repo,
2063 worktree ? got_worktree_get_head_ref_name(worktree)
2064 : GOT_REF_HEAD, 0);
2065 if (error != NULL)
2066 return error;
2067 error = got_ref_resolve(&id, repo, head_ref);
2068 got_ref_close(head_ref);
2069 if (error != NULL)
2070 return error;
2071 error = got_object_open_as_commit(&commit, repo, id);
2072 } else {
2073 struct got_reference *ref;
2074 error = got_ref_open(&ref, repo, start_commit, 0);
2075 if (error == NULL) {
2076 int obj_type;
2077 error = got_ref_resolve(&id, repo, ref);
2078 got_ref_close(ref);
2079 if (error != NULL)
2080 goto done;
2081 error = got_object_get_type(&obj_type, repo, id);
2082 if (error != NULL)
2083 goto done;
2084 if (obj_type == GOT_OBJ_TYPE_TAG) {
2085 struct got_tag_object *tag;
2086 error = got_object_open_as_tag(&tag, repo, id);
2087 if (error != NULL)
2088 goto done;
2089 if (got_object_tag_get_object_type(tag) !=
2090 GOT_OBJ_TYPE_COMMIT) {
2091 got_object_tag_close(tag);
2092 error = got_error(GOT_ERR_OBJ_TYPE);
2093 goto done;
2095 free(id);
2096 id = got_object_id_dup(
2097 got_object_tag_get_object_id(tag));
2098 if (id == NULL)
2099 error = got_error_from_errno(
2100 "got_object_id_dup");
2101 got_object_tag_close(tag);
2102 if (error)
2103 goto done;
2104 } else if (obj_type != GOT_OBJ_TYPE_COMMIT) {
2105 error = got_error(GOT_ERR_OBJ_TYPE);
2106 goto done;
2108 error = got_object_open_as_commit(&commit, repo, id);
2109 if (error != NULL)
2110 goto done;
2112 if (commit == NULL) {
2113 error = got_repo_match_object_id_prefix(&id,
2114 start_commit, GOT_OBJ_TYPE_COMMIT, repo);
2115 if (error != NULL)
2116 return error;
2119 if (error != NULL)
2120 goto done;
2122 if (worktree) {
2123 const char *prefix = got_worktree_get_path_prefix(worktree);
2124 char *p;
2125 if (asprintf(&p, "%s%s%s", prefix,
2126 (strcmp(prefix, "/") != 0) ? "/" : "", path) == -1) {
2127 error = got_error_from_errno("asprintf");
2128 goto done;
2130 error = got_repo_map_path(&in_repo_path, repo, p, 0);
2131 free(p);
2132 } else
2133 error = got_repo_map_path(&in_repo_path, repo, path, 1);
2134 if (error != NULL)
2135 goto done;
2136 if (in_repo_path) {
2137 free(path);
2138 path = in_repo_path;
2141 error = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name, NULL);
2142 if (error)
2143 goto done;
2145 error = print_commits(id, repo, path, show_patch, search_pattern,
2146 diff_context, limit, log_branches, &refs);
2147 done:
2148 free(path);
2149 free(repo_path);
2150 free(cwd);
2151 free(id);
2152 if (worktree)
2153 got_worktree_close(worktree);
2154 if (repo) {
2155 const struct got_error *repo_error;
2156 repo_error = got_repo_close(repo);
2157 if (error == NULL)
2158 error = repo_error;
2160 got_ref_list_free(&refs);
2161 return error;
2164 __dead static void
2165 usage_diff(void)
2167 fprintf(stderr, "usage: %s diff [-C number] [-r repository-path] [-s] "
2168 "[-w] [object1 object2 | path]\n", getprogname());
2169 exit(1);
2172 struct print_diff_arg {
2173 struct got_repository *repo;
2174 struct got_worktree *worktree;
2175 int diff_context;
2176 const char *id_str;
2177 int header_shown;
2178 int diff_staged;
2179 int ignore_whitespace;
2182 static const struct got_error *
2183 print_diff(void *arg, unsigned char status, unsigned char staged_status,
2184 const char *path, struct got_object_id *blob_id,
2185 struct got_object_id *staged_blob_id, struct got_object_id *commit_id,
2186 int dirfd, const char *de_name)
2188 struct print_diff_arg *a = arg;
2189 const struct got_error *err = NULL;
2190 struct got_blob_object *blob1 = NULL;
2191 int fd = -1;
2192 FILE *f2 = NULL;
2193 char *abspath = NULL, *label1 = NULL;
2194 struct stat sb;
2196 if (a->diff_staged) {
2197 if (staged_status != GOT_STATUS_MODIFY &&
2198 staged_status != GOT_STATUS_ADD &&
2199 staged_status != GOT_STATUS_DELETE)
2200 return NULL;
2201 } else {
2202 if (staged_status == GOT_STATUS_DELETE)
2203 return NULL;
2204 if (status == GOT_STATUS_NONEXISTENT)
2205 return got_error_set_errno(ENOENT, path);
2206 if (status != GOT_STATUS_MODIFY &&
2207 status != GOT_STATUS_ADD &&
2208 status != GOT_STATUS_DELETE &&
2209 status != GOT_STATUS_CONFLICT)
2210 return NULL;
2213 if (!a->header_shown) {
2214 printf("diff %s %s%s\n", a->id_str,
2215 got_worktree_get_root_path(a->worktree),
2216 a->diff_staged ? " (staged changes)" : "");
2217 a->header_shown = 1;
2220 if (a->diff_staged) {
2221 const char *label1 = NULL, *label2 = NULL;
2222 switch (staged_status) {
2223 case GOT_STATUS_MODIFY:
2224 label1 = path;
2225 label2 = path;
2226 break;
2227 case GOT_STATUS_ADD:
2228 label2 = path;
2229 break;
2230 case GOT_STATUS_DELETE:
2231 label1 = path;
2232 break;
2233 default:
2234 return got_error(GOT_ERR_FILE_STATUS);
2236 return got_diff_objects_as_blobs(blob_id, staged_blob_id,
2237 label1, label2, a->diff_context, a->ignore_whitespace,
2238 a->repo, stdout);
2241 if (staged_status == GOT_STATUS_ADD ||
2242 staged_status == GOT_STATUS_MODIFY) {
2243 char *id_str;
2244 err = got_object_open_as_blob(&blob1, a->repo, staged_blob_id,
2245 8192);
2246 if (err)
2247 goto done;
2248 err = got_object_id_str(&id_str, staged_blob_id);
2249 if (err)
2250 goto done;
2251 if (asprintf(&label1, "%s (staged)", id_str) == -1) {
2252 err = got_error_from_errno("asprintf");
2253 free(id_str);
2254 goto done;
2256 free(id_str);
2257 } else if (status != GOT_STATUS_ADD) {
2258 err = got_object_open_as_blob(&blob1, a->repo, blob_id, 8192);
2259 if (err)
2260 goto done;
2263 if (status != GOT_STATUS_DELETE) {
2264 if (asprintf(&abspath, "%s/%s",
2265 got_worktree_get_root_path(a->worktree), path) == -1) {
2266 err = got_error_from_errno("asprintf");
2267 goto done;
2270 if (dirfd != -1) {
2271 fd = openat(dirfd, de_name, O_RDONLY | O_NOFOLLOW);
2272 if (fd == -1) {
2273 err = got_error_from_errno2("openat", abspath);
2274 goto done;
2276 } else {
2277 fd = open(abspath, O_RDONLY | O_NOFOLLOW);
2278 if (fd == -1) {
2279 err = got_error_from_errno2("open", abspath);
2280 goto done;
2283 if (fstat(fd, &sb) == -1) {
2284 err = got_error_from_errno2("fstat", abspath);
2285 goto done;
2287 f2 = fdopen(fd, "r");
2288 if (f2 == NULL) {
2289 err = got_error_from_errno2("fdopen", abspath);
2290 goto done;
2292 fd = -1;
2293 } else
2294 sb.st_size = 0;
2296 err = got_diff_blob_file(blob1, label1, f2, sb.st_size, path,
2297 a->diff_context, a->ignore_whitespace, stdout);
2298 done:
2299 if (blob1)
2300 got_object_blob_close(blob1);
2301 if (f2 && fclose(f2) == EOF && err == NULL)
2302 err = got_error_from_errno("fclose");
2303 if (fd != -1 && close(fd) == -1 && err == NULL)
2304 err = got_error_from_errno("close");
2305 free(abspath);
2306 return err;
2309 static const struct got_error *
2310 cmd_diff(int argc, char *argv[])
2312 const struct got_error *error;
2313 struct got_repository *repo = NULL;
2314 struct got_worktree *worktree = NULL;
2315 char *cwd = NULL, *repo_path = NULL;
2316 struct got_object_id *id1 = NULL, *id2 = NULL;
2317 const char *id_str1 = NULL, *id_str2 = NULL;
2318 char *label1 = NULL, *label2 = NULL;
2319 int type1, type2;
2320 int diff_context = 3, diff_staged = 0, ignore_whitespace = 0, ch;
2321 const char *errstr;
2322 char *path = NULL;
2324 #ifndef PROFILE
2325 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
2326 NULL) == -1)
2327 err(1, "pledge");
2328 #endif
2330 while ((ch = getopt(argc, argv, "C:r:sw")) != -1) {
2331 switch (ch) {
2332 case 'C':
2333 diff_context = strtonum(optarg, 0, GOT_DIFF_MAX_CONTEXT,
2334 &errstr);
2335 if (errstr != NULL)
2336 err(1, "-C option %s", errstr);
2337 break;
2338 case 'r':
2339 repo_path = realpath(optarg, NULL);
2340 if (repo_path == NULL)
2341 return got_error_from_errno2("realpath",
2342 optarg);
2343 got_path_strip_trailing_slashes(repo_path);
2344 break;
2345 case 's':
2346 diff_staged = 1;
2347 break;
2348 case 'w':
2349 ignore_whitespace = 1;
2350 break;
2351 default:
2352 usage_diff();
2353 /* NOTREACHED */
2357 argc -= optind;
2358 argv += optind;
2360 cwd = getcwd(NULL, 0);
2361 if (cwd == NULL) {
2362 error = got_error_from_errno("getcwd");
2363 goto done;
2365 error = got_worktree_open(&worktree, cwd);
2366 if (error && error->code != GOT_ERR_NOT_WORKTREE)
2367 goto done;
2368 if (argc <= 1) {
2369 if (worktree == NULL) {
2370 error = got_error(GOT_ERR_NOT_WORKTREE);
2371 goto done;
2373 if (repo_path)
2374 errx(1,
2375 "-r option can't be used when diffing a work tree");
2376 repo_path = strdup(got_worktree_get_repo_path(worktree));
2377 if (repo_path == NULL) {
2378 error = got_error_from_errno("strdup");
2379 goto done;
2381 if (argc == 1) {
2382 error = got_worktree_resolve_path(&path, worktree,
2383 argv[0]);
2384 if (error)
2385 goto done;
2386 } else {
2387 path = strdup("");
2388 if (path == NULL) {
2389 error = got_error_from_errno("strdup");
2390 goto done;
2393 } else if (argc == 2) {
2394 if (diff_staged)
2395 errx(1, "-s option can't be used when diffing "
2396 "objects in repository");
2397 id_str1 = argv[0];
2398 id_str2 = argv[1];
2399 if (worktree && repo_path == NULL) {
2400 repo_path =
2401 strdup(got_worktree_get_repo_path(worktree));
2402 if (repo_path == NULL) {
2403 error = got_error_from_errno("strdup");
2404 goto done;
2407 } else
2408 usage_diff();
2410 if (repo_path == NULL) {
2411 repo_path = getcwd(NULL, 0);
2412 if (repo_path == NULL)
2413 return got_error_from_errno("getcwd");
2416 error = got_repo_open(&repo, repo_path, NULL);
2417 free(repo_path);
2418 if (error != NULL)
2419 goto done;
2421 error = apply_unveil(got_repo_get_path(repo), 1,
2422 worktree ? got_worktree_get_root_path(worktree) : NULL);
2423 if (error)
2424 goto done;
2426 if (argc <= 1) {
2427 struct print_diff_arg arg;
2428 struct got_pathlist_head paths;
2429 char *id_str;
2431 TAILQ_INIT(&paths);
2433 error = got_object_id_str(&id_str,
2434 got_worktree_get_base_commit_id(worktree));
2435 if (error)
2436 goto done;
2437 arg.repo = repo;
2438 arg.worktree = worktree;
2439 arg.diff_context = diff_context;
2440 arg.id_str = id_str;
2441 arg.header_shown = 0;
2442 arg.diff_staged = diff_staged;
2443 arg.ignore_whitespace = ignore_whitespace;
2445 error = got_pathlist_append(&paths, path, NULL);
2446 if (error)
2447 goto done;
2449 error = got_worktree_status(worktree, &paths, repo, print_diff,
2450 &arg, check_cancelled, NULL);
2451 free(id_str);
2452 got_pathlist_free(&paths);
2453 goto done;
2456 error = got_repo_match_object_id(&id1, &label1, id_str1,
2457 GOT_OBJ_TYPE_ANY, 1, repo);
2458 if (error)
2459 goto done;
2461 error = got_repo_match_object_id(&id2, &label2, id_str2,
2462 GOT_OBJ_TYPE_ANY, 1, repo);
2463 if (error)
2464 goto done;
2466 error = got_object_get_type(&type1, repo, id1);
2467 if (error)
2468 goto done;
2470 error = got_object_get_type(&type2, repo, id2);
2471 if (error)
2472 goto done;
2474 if (type1 != type2) {
2475 error = got_error(GOT_ERR_OBJ_TYPE);
2476 goto done;
2479 switch (type1) {
2480 case GOT_OBJ_TYPE_BLOB:
2481 error = got_diff_objects_as_blobs(id1, id2, NULL, NULL,
2482 diff_context, ignore_whitespace, repo, stdout);
2483 break;
2484 case GOT_OBJ_TYPE_TREE:
2485 error = got_diff_objects_as_trees(id1, id2, "", "",
2486 diff_context, ignore_whitespace, repo, stdout);
2487 break;
2488 case GOT_OBJ_TYPE_COMMIT:
2489 printf("diff %s %s\n", label1, label2);
2490 error = got_diff_objects_as_commits(id1, id2, diff_context,
2491 ignore_whitespace, repo, stdout);
2492 break;
2493 default:
2494 error = got_error(GOT_ERR_OBJ_TYPE);
2496 done:
2497 free(label1);
2498 free(label2);
2499 free(id1);
2500 free(id2);
2501 free(path);
2502 if (worktree)
2503 got_worktree_close(worktree);
2504 if (repo) {
2505 const struct got_error *repo_error;
2506 repo_error = got_repo_close(repo);
2507 if (error == NULL)
2508 error = repo_error;
2510 return error;
2513 __dead static void
2514 usage_blame(void)
2516 fprintf(stderr,
2517 "usage: %s blame [-c commit] [-r repository-path] path\n",
2518 getprogname());
2519 exit(1);
2522 struct blame_line {
2523 int annotated;
2524 char *id_str;
2525 char *committer;
2526 char datebuf[11]; /* YYYY-MM-DD + NUL */
2529 struct blame_cb_args {
2530 struct blame_line *lines;
2531 int nlines;
2532 int nlines_prec;
2533 int lineno_cur;
2534 off_t *line_offsets;
2535 FILE *f;
2536 struct got_repository *repo;
2539 static const struct got_error *
2540 blame_cb(void *arg, int nlines, int lineno, struct got_object_id *id)
2542 const struct got_error *err = NULL;
2543 struct blame_cb_args *a = arg;
2544 struct blame_line *bline;
2545 char *line = NULL;
2546 size_t linesize = 0;
2547 struct got_commit_object *commit = NULL;
2548 off_t offset;
2549 struct tm tm;
2550 time_t committer_time;
2552 if (nlines != a->nlines ||
2553 (lineno != -1 && lineno < 1) || lineno > a->nlines)
2554 return got_error(GOT_ERR_RANGE);
2556 if (sigint_received)
2557 return got_error(GOT_ERR_ITER_COMPLETED);
2559 if (lineno == -1)
2560 return NULL; /* no change in this commit */
2562 /* Annotate this line. */
2563 bline = &a->lines[lineno - 1];
2564 if (bline->annotated)
2565 return NULL;
2566 err = got_object_id_str(&bline->id_str, id);
2567 if (err)
2568 return err;
2570 err = got_object_open_as_commit(&commit, a->repo, id);
2571 if (err)
2572 goto done;
2574 bline->committer = strdup(got_object_commit_get_committer(commit));
2575 if (bline->committer == NULL) {
2576 err = got_error_from_errno("strdup");
2577 goto done;
2580 committer_time = got_object_commit_get_committer_time(commit);
2581 if (localtime_r(&committer_time, &tm) == NULL)
2582 return got_error_from_errno("localtime_r");
2583 if (strftime(bline->datebuf, sizeof(bline->datebuf), "%G-%m-%d",
2584 &tm) >= sizeof(bline->datebuf)) {
2585 err = got_error(GOT_ERR_NO_SPACE);
2586 goto done;
2588 bline->annotated = 1;
2590 /* Print lines annotated so far. */
2591 bline = &a->lines[a->lineno_cur - 1];
2592 if (!bline->annotated)
2593 goto done;
2595 offset = a->line_offsets[a->lineno_cur - 1];
2596 if (fseeko(a->f, offset, SEEK_SET) == -1) {
2597 err = got_error_from_errno("fseeko");
2598 goto done;
2601 while (bline->annotated) {
2602 char *smallerthan, *at, *nl, *committer;
2603 size_t len;
2605 if (getline(&line, &linesize, a->f) == -1) {
2606 if (ferror(a->f))
2607 err = got_error_from_errno("getline");
2608 break;
2611 committer = bline->committer;
2612 smallerthan = strchr(committer, '<');
2613 if (smallerthan && smallerthan[1] != '\0')
2614 committer = smallerthan + 1;
2615 at = strchr(committer, '@');
2616 if (at)
2617 *at = '\0';
2618 len = strlen(committer);
2619 if (len >= 9)
2620 committer[8] = '\0';
2622 nl = strchr(line, '\n');
2623 if (nl)
2624 *nl = '\0';
2625 printf("%.*d) %.8s %s %-8s %s\n", a->nlines_prec, a->lineno_cur,
2626 bline->id_str, bline->datebuf, committer, line);
2628 a->lineno_cur++;
2629 bline = &a->lines[a->lineno_cur - 1];
2631 done:
2632 if (commit)
2633 got_object_commit_close(commit);
2634 free(line);
2635 return err;
2638 static const struct got_error *
2639 cmd_blame(int argc, char *argv[])
2641 const struct got_error *error;
2642 struct got_repository *repo = NULL;
2643 struct got_worktree *worktree = NULL;
2644 char *path, *cwd = NULL, *repo_path = NULL, *in_repo_path = NULL;
2645 struct got_object_id *obj_id = NULL;
2646 struct got_object_id *commit_id = NULL;
2647 struct got_blob_object *blob = NULL;
2648 char *commit_id_str = NULL;
2649 struct blame_cb_args bca;
2650 int ch, obj_type, i;
2651 size_t filesize;
2653 memset(&bca, 0, sizeof(bca));
2655 #ifndef PROFILE
2656 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
2657 NULL) == -1)
2658 err(1, "pledge");
2659 #endif
2661 while ((ch = getopt(argc, argv, "c:r:")) != -1) {
2662 switch (ch) {
2663 case 'c':
2664 commit_id_str = optarg;
2665 break;
2666 case 'r':
2667 repo_path = realpath(optarg, NULL);
2668 if (repo_path == NULL)
2669 return got_error_from_errno2("realpath",
2670 optarg);
2671 got_path_strip_trailing_slashes(repo_path);
2672 break;
2673 default:
2674 usage_blame();
2675 /* NOTREACHED */
2679 argc -= optind;
2680 argv += optind;
2682 if (argc == 1)
2683 path = argv[0];
2684 else
2685 usage_blame();
2687 cwd = getcwd(NULL, 0);
2688 if (cwd == NULL) {
2689 error = got_error_from_errno("getcwd");
2690 goto done;
2692 if (repo_path == NULL) {
2693 error = got_worktree_open(&worktree, cwd);
2694 if (error && error->code != GOT_ERR_NOT_WORKTREE)
2695 goto done;
2696 else
2697 error = NULL;
2698 if (worktree) {
2699 repo_path =
2700 strdup(got_worktree_get_repo_path(worktree));
2701 if (repo_path == NULL)
2702 error = got_error_from_errno("strdup");
2703 if (error)
2704 goto done;
2705 } else {
2706 repo_path = strdup(cwd);
2707 if (repo_path == NULL) {
2708 error = got_error_from_errno("strdup");
2709 goto done;
2714 error = got_repo_open(&repo, repo_path, NULL);
2715 if (error != NULL)
2716 goto done;
2718 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
2719 if (error)
2720 goto done;
2722 if (worktree) {
2723 const char *prefix = got_worktree_get_path_prefix(worktree);
2724 char *p, *worktree_subdir = cwd +
2725 strlen(got_worktree_get_root_path(worktree));
2726 if (asprintf(&p, "%s%s%s%s%s",
2727 prefix, (strcmp(prefix, "/") != 0) ? "/" : "",
2728 worktree_subdir, worktree_subdir[0] ? "/" : "",
2729 path) == -1) {
2730 error = got_error_from_errno("asprintf");
2731 goto done;
2733 error = got_repo_map_path(&in_repo_path, repo, p, 0);
2734 free(p);
2735 } else {
2736 error = got_repo_map_path(&in_repo_path, repo, path, 1);
2738 if (error)
2739 goto done;
2741 if (commit_id_str == NULL) {
2742 struct got_reference *head_ref;
2743 error = got_ref_open(&head_ref, repo, worktree ?
2744 got_worktree_get_head_ref_name(worktree) : GOT_REF_HEAD, 0);
2745 if (error != NULL)
2746 goto done;
2747 error = got_ref_resolve(&commit_id, repo, head_ref);
2748 got_ref_close(head_ref);
2749 if (error != NULL)
2750 goto done;
2751 } else {
2752 error = got_repo_match_object_id(&commit_id, NULL,
2753 commit_id_str, GOT_OBJ_TYPE_COMMIT, 1, repo);
2754 if (error)
2755 goto done;
2758 error = got_object_id_by_path(&obj_id, repo, commit_id, in_repo_path);
2759 if (error)
2760 goto done;
2762 error = got_object_get_type(&obj_type, repo, obj_id);
2763 if (error)
2764 goto done;
2766 if (obj_type != GOT_OBJ_TYPE_BLOB) {
2767 error = got_error(GOT_ERR_OBJ_TYPE);
2768 goto done;
2771 error = got_object_open_as_blob(&blob, repo, obj_id, 8192);
2772 if (error)
2773 goto done;
2774 bca.f = got_opentemp();
2775 if (bca.f == NULL) {
2776 error = got_error_from_errno("got_opentemp");
2777 goto done;
2779 error = got_object_blob_dump_to_file(&filesize, &bca.nlines,
2780 &bca.line_offsets, bca.f, blob);
2781 if (error || bca.nlines == 0)
2782 goto done;
2784 /* Don't include \n at EOF in the blame line count. */
2785 if (bca.line_offsets[bca.nlines - 1] == filesize)
2786 bca.nlines--;
2788 bca.lines = calloc(bca.nlines, sizeof(*bca.lines));
2789 if (bca.lines == NULL) {
2790 error = got_error_from_errno("calloc");
2791 goto done;
2793 bca.lineno_cur = 1;
2794 bca.nlines_prec = 0;
2795 i = bca.nlines;
2796 while (i > 0) {
2797 i /= 10;
2798 bca.nlines_prec++;
2800 bca.repo = repo;
2802 error = got_blame(in_repo_path, commit_id, repo, blame_cb, &bca,
2803 check_cancelled, NULL);
2804 done:
2805 free(in_repo_path);
2806 free(repo_path);
2807 free(cwd);
2808 free(commit_id);
2809 free(obj_id);
2810 if (blob)
2811 got_object_blob_close(blob);
2812 if (worktree)
2813 got_worktree_close(worktree);
2814 if (repo) {
2815 const struct got_error *repo_error;
2816 repo_error = got_repo_close(repo);
2817 if (error == NULL)
2818 error = repo_error;
2820 if (bca.lines) {
2821 for (i = 0; i < bca.nlines; i++) {
2822 struct blame_line *bline = &bca.lines[i];
2823 free(bline->id_str);
2824 free(bline->committer);
2826 free(bca.lines);
2828 free(bca.line_offsets);
2829 if (bca.f && fclose(bca.f) == EOF && error == NULL)
2830 error = got_error_from_errno("fclose");
2831 return error;
2834 __dead static void
2835 usage_tree(void)
2837 fprintf(stderr,
2838 "usage: %s tree [-c commit] [-r repository-path] [-iR] path\n",
2839 getprogname());
2840 exit(1);
2843 static void
2844 print_entry(struct got_tree_entry *te, const char *id, const char *path,
2845 const char *root_path)
2847 int is_root_path = (strcmp(path, root_path) == 0);
2848 const char *modestr = "";
2849 mode_t mode = got_tree_entry_get_mode(te);
2851 path += strlen(root_path);
2852 while (path[0] == '/')
2853 path++;
2855 if (got_object_tree_entry_is_submodule(te))
2856 modestr = "$";
2857 else if (S_ISLNK(mode))
2858 modestr = "@";
2859 else if (S_ISDIR(mode))
2860 modestr = "/";
2861 else if (mode & S_IXUSR)
2862 modestr = "*";
2864 printf("%s%s%s%s%s\n", id ? id : "", path,
2865 is_root_path ? "" : "/", got_tree_entry_get_name(te), modestr);
2868 static const struct got_error *
2869 print_tree(const char *path, struct got_object_id *commit_id,
2870 int show_ids, int recurse, const char *root_path,
2871 struct got_repository *repo)
2873 const struct got_error *err = NULL;
2874 struct got_object_id *tree_id = NULL;
2875 struct got_tree_object *tree = NULL;
2876 int nentries, i;
2878 err = got_object_id_by_path(&tree_id, repo, commit_id, path);
2879 if (err)
2880 goto done;
2882 err = got_object_open_as_tree(&tree, repo, tree_id);
2883 if (err)
2884 goto done;
2885 nentries = got_object_tree_get_nentries(tree);
2886 for (i = 0; i < nentries; i++) {
2887 struct got_tree_entry *te;
2888 char *id = NULL;
2890 if (sigint_received || sigpipe_received)
2891 break;
2893 te = got_object_tree_get_entry(tree, i);
2894 if (show_ids) {
2895 char *id_str;
2896 err = got_object_id_str(&id_str,
2897 got_tree_entry_get_id(te));
2898 if (err)
2899 goto done;
2900 if (asprintf(&id, "%s ", id_str) == -1) {
2901 err = got_error_from_errno("asprintf");
2902 free(id_str);
2903 goto done;
2905 free(id_str);
2907 print_entry(te, id, path, root_path);
2908 free(id);
2910 if (recurse && S_ISDIR(got_tree_entry_get_mode(te))) {
2911 char *child_path;
2912 if (asprintf(&child_path, "%s%s%s", path,
2913 path[0] == '/' && path[1] == '\0' ? "" : "/",
2914 got_tree_entry_get_name(te)) == -1) {
2915 err = got_error_from_errno("asprintf");
2916 goto done;
2918 err = print_tree(child_path, commit_id, show_ids, 1,
2919 root_path, repo);
2920 free(child_path);
2921 if (err)
2922 goto done;
2925 done:
2926 if (tree)
2927 got_object_tree_close(tree);
2928 free(tree_id);
2929 return err;
2932 static const struct got_error *
2933 cmd_tree(int argc, char *argv[])
2935 const struct got_error *error;
2936 struct got_repository *repo = NULL;
2937 struct got_worktree *worktree = NULL;
2938 const char *path;
2939 char *cwd = NULL, *repo_path = NULL, *in_repo_path = NULL;
2940 struct got_object_id *commit_id = NULL;
2941 char *commit_id_str = NULL;
2942 int show_ids = 0, recurse = 0;
2943 int ch;
2945 #ifndef PROFILE
2946 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
2947 NULL) == -1)
2948 err(1, "pledge");
2949 #endif
2951 while ((ch = getopt(argc, argv, "c:r:iR")) != -1) {
2952 switch (ch) {
2953 case 'c':
2954 commit_id_str = optarg;
2955 break;
2956 case 'r':
2957 repo_path = realpath(optarg, NULL);
2958 if (repo_path == NULL)
2959 return got_error_from_errno2("realpath",
2960 optarg);
2961 got_path_strip_trailing_slashes(repo_path);
2962 break;
2963 case 'i':
2964 show_ids = 1;
2965 break;
2966 case 'R':
2967 recurse = 1;
2968 break;
2969 default:
2970 usage_tree();
2971 /* NOTREACHED */
2975 argc -= optind;
2976 argv += optind;
2978 if (argc == 1)
2979 path = argv[0];
2980 else if (argc > 1)
2981 usage_tree();
2982 else
2983 path = NULL;
2985 cwd = getcwd(NULL, 0);
2986 if (cwd == NULL) {
2987 error = got_error_from_errno("getcwd");
2988 goto done;
2990 if (repo_path == NULL) {
2991 error = got_worktree_open(&worktree, cwd);
2992 if (error && error->code != GOT_ERR_NOT_WORKTREE)
2993 goto done;
2994 else
2995 error = NULL;
2996 if (worktree) {
2997 repo_path =
2998 strdup(got_worktree_get_repo_path(worktree));
2999 if (repo_path == NULL)
3000 error = got_error_from_errno("strdup");
3001 if (error)
3002 goto done;
3003 } else {
3004 repo_path = strdup(cwd);
3005 if (repo_path == NULL) {
3006 error = got_error_from_errno("strdup");
3007 goto done;
3012 error = got_repo_open(&repo, repo_path, NULL);
3013 if (error != NULL)
3014 goto done;
3016 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
3017 if (error)
3018 goto done;
3020 if (path == NULL) {
3021 if (worktree) {
3022 char *p, *worktree_subdir = cwd +
3023 strlen(got_worktree_get_root_path(worktree));
3024 if (asprintf(&p, "%s/%s",
3025 got_worktree_get_path_prefix(worktree),
3026 worktree_subdir) == -1) {
3027 error = got_error_from_errno("asprintf");
3028 goto done;
3030 error = got_repo_map_path(&in_repo_path, repo, p, 0);
3031 free(p);
3032 if (error)
3033 goto done;
3034 } else
3035 path = "/";
3037 if (in_repo_path == NULL) {
3038 error = got_repo_map_path(&in_repo_path, repo, path, 1);
3039 if (error != NULL)
3040 goto done;
3043 if (commit_id_str == NULL) {
3044 struct got_reference *head_ref;
3045 error = got_ref_open(&head_ref, repo, GOT_REF_HEAD, 0);
3046 if (error != NULL)
3047 goto done;
3048 error = got_ref_resolve(&commit_id, repo, head_ref);
3049 got_ref_close(head_ref);
3050 if (error != NULL)
3051 goto done;
3052 } else {
3053 error = got_repo_match_object_id(&commit_id, NULL,
3054 commit_id_str, GOT_OBJ_TYPE_COMMIT, 1, repo);
3055 if (error)
3056 goto done;
3059 error = print_tree(in_repo_path, commit_id, show_ids, recurse,
3060 in_repo_path, repo);
3061 done:
3062 free(in_repo_path);
3063 free(repo_path);
3064 free(cwd);
3065 free(commit_id);
3066 if (worktree)
3067 got_worktree_close(worktree);
3068 if (repo) {
3069 const struct got_error *repo_error;
3070 repo_error = got_repo_close(repo);
3071 if (error == NULL)
3072 error = repo_error;
3074 return error;
3077 __dead static void
3078 usage_status(void)
3080 fprintf(stderr, "usage: %s status [path ...]\n", getprogname());
3081 exit(1);
3084 static const struct got_error *
3085 print_status(void *arg, unsigned char status, unsigned char staged_status,
3086 const char *path, struct got_object_id *blob_id,
3087 struct got_object_id *staged_blob_id, struct got_object_id *commit_id,
3088 int dirfd, const char *de_name)
3090 if (status == staged_status && (status == GOT_STATUS_DELETE))
3091 status = GOT_STATUS_NO_CHANGE;
3092 printf("%c%c %s\n", status, staged_status, path);
3093 return NULL;
3096 static const struct got_error *
3097 cmd_status(int argc, char *argv[])
3099 const struct got_error *error = NULL;
3100 struct got_repository *repo = NULL;
3101 struct got_worktree *worktree = NULL;
3102 char *cwd = NULL;
3103 struct got_pathlist_head paths;
3104 struct got_pathlist_entry *pe;
3105 int ch;
3107 TAILQ_INIT(&paths);
3109 while ((ch = getopt(argc, argv, "")) != -1) {
3110 switch (ch) {
3111 default:
3112 usage_status();
3113 /* NOTREACHED */
3117 argc -= optind;
3118 argv += optind;
3120 #ifndef PROFILE
3121 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
3122 NULL) == -1)
3123 err(1, "pledge");
3124 #endif
3125 cwd = getcwd(NULL, 0);
3126 if (cwd == NULL) {
3127 error = got_error_from_errno("getcwd");
3128 goto done;
3131 error = got_worktree_open(&worktree, cwd);
3132 if (error != NULL)
3133 goto done;
3135 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
3136 NULL);
3137 if (error != NULL)
3138 goto done;
3140 error = apply_unveil(got_repo_get_path(repo), 1,
3141 got_worktree_get_root_path(worktree));
3142 if (error)
3143 goto done;
3145 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
3146 if (error)
3147 goto done;
3149 error = got_worktree_status(worktree, &paths, repo, print_status, NULL,
3150 check_cancelled, NULL);
3151 done:
3152 TAILQ_FOREACH(pe, &paths, entry)
3153 free((char *)pe->path);
3154 got_pathlist_free(&paths);
3155 free(cwd);
3156 return error;
3159 __dead static void
3160 usage_ref(void)
3162 fprintf(stderr,
3163 "usage: %s ref [-r repository] -l | -d name | [-s] name target\n",
3164 getprogname());
3165 exit(1);
3168 static const struct got_error *
3169 list_refs(struct got_repository *repo)
3171 static const struct got_error *err = NULL;
3172 struct got_reflist_head refs;
3173 struct got_reflist_entry *re;
3175 SIMPLEQ_INIT(&refs);
3176 err = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name, NULL);
3177 if (err)
3178 return err;
3180 SIMPLEQ_FOREACH(re, &refs, entry) {
3181 char *refstr;
3182 refstr = got_ref_to_str(re->ref);
3183 if (refstr == NULL)
3184 return got_error_from_errno("got_ref_to_str");
3185 printf("%s: %s\n", got_ref_get_name(re->ref), refstr);
3186 free(refstr);
3189 got_ref_list_free(&refs);
3190 return NULL;
3193 static const struct got_error *
3194 delete_ref(struct got_repository *repo, const char *refname)
3196 const struct got_error *err = NULL;
3197 struct got_reference *ref;
3199 err = got_ref_open(&ref, repo, refname, 0);
3200 if (err)
3201 return err;
3203 err = got_ref_delete(ref, repo);
3204 got_ref_close(ref);
3205 return err;
3208 static const struct got_error *
3209 add_ref(struct got_repository *repo, const char *refname, const char *target)
3211 const struct got_error *err = NULL;
3212 struct got_object_id *id;
3213 struct got_reference *ref = NULL;
3216 * Don't let the user create a reference name with a leading '-'.
3217 * While technically a valid reference name, this case is usually
3218 * an unintended typo.
3220 if (refname[0] == '-')
3221 return got_error_path(refname, GOT_ERR_REF_NAME_MINUS);
3223 err = got_repo_match_object_id_prefix(&id, target, GOT_OBJ_TYPE_ANY,
3224 repo);
3225 if (err) {
3226 struct got_reference *target_ref;
3228 if (err->code != GOT_ERR_BAD_OBJ_ID_STR)
3229 return err;
3230 err = got_ref_open(&target_ref, repo, target, 0);
3231 if (err)
3232 return err;
3233 err = got_ref_resolve(&id, repo, target_ref);
3234 got_ref_close(target_ref);
3235 if (err)
3236 return err;
3239 err = got_ref_alloc(&ref, refname, id);
3240 if (err)
3241 goto done;
3243 err = got_ref_write(ref, repo);
3244 done:
3245 if (ref)
3246 got_ref_close(ref);
3247 free(id);
3248 return err;
3251 static const struct got_error *
3252 add_symref(struct got_repository *repo, const char *refname, const char *target)
3254 const struct got_error *err = NULL;
3255 struct got_reference *ref = NULL;
3256 struct got_reference *target_ref = NULL;
3259 * Don't let the user create a reference name with a leading '-'.
3260 * While technically a valid reference name, this case is usually
3261 * an unintended typo.
3263 if (refname[0] == '-')
3264 return got_error_path(refname, GOT_ERR_REF_NAME_MINUS);
3266 err = got_ref_open(&target_ref, repo, target, 0);
3267 if (err)
3268 return err;
3270 err = got_ref_alloc_symref(&ref, refname, target_ref);
3271 if (err)
3272 goto done;
3274 err = got_ref_write(ref, repo);
3275 done:
3276 if (target_ref)
3277 got_ref_close(target_ref);
3278 if (ref)
3279 got_ref_close(ref);
3280 return err;
3283 static const struct got_error *
3284 cmd_ref(int argc, char *argv[])
3286 const struct got_error *error = NULL;
3287 struct got_repository *repo = NULL;
3288 struct got_worktree *worktree = NULL;
3289 char *cwd = NULL, *repo_path = NULL;
3290 int ch, do_list = 0, create_symref = 0;
3291 const char *delref = NULL;
3293 /* TODO: Add -s option for adding symbolic references. */
3294 while ((ch = getopt(argc, argv, "d:r:ls")) != -1) {
3295 switch (ch) {
3296 case 'd':
3297 delref = optarg;
3298 break;
3299 case 'r':
3300 repo_path = realpath(optarg, NULL);
3301 if (repo_path == NULL)
3302 return got_error_from_errno2("realpath",
3303 optarg);
3304 got_path_strip_trailing_slashes(repo_path);
3305 break;
3306 case 'l':
3307 do_list = 1;
3308 break;
3309 case 's':
3310 create_symref = 1;
3311 break;
3312 default:
3313 usage_ref();
3314 /* NOTREACHED */
3318 if (do_list && delref)
3319 errx(1, "-l and -d options are mutually exclusive\n");
3321 argc -= optind;
3322 argv += optind;
3324 if (do_list || delref) {
3325 if (create_symref)
3326 errx(1, "-s option cannot be used together with the "
3327 "-l or -d options");
3328 if (argc > 0)
3329 usage_ref();
3330 } else if (argc != 2)
3331 usage_ref();
3333 #ifndef PROFILE
3334 if (do_list) {
3335 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
3336 NULL) == -1)
3337 err(1, "pledge");
3338 } else {
3339 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
3340 "sendfd unveil", NULL) == -1)
3341 err(1, "pledge");
3343 #endif
3344 cwd = getcwd(NULL, 0);
3345 if (cwd == NULL) {
3346 error = got_error_from_errno("getcwd");
3347 goto done;
3350 if (repo_path == NULL) {
3351 error = got_worktree_open(&worktree, cwd);
3352 if (error && error->code != GOT_ERR_NOT_WORKTREE)
3353 goto done;
3354 else
3355 error = NULL;
3356 if (worktree) {
3357 repo_path =
3358 strdup(got_worktree_get_repo_path(worktree));
3359 if (repo_path == NULL)
3360 error = got_error_from_errno("strdup");
3361 if (error)
3362 goto done;
3363 } else {
3364 repo_path = strdup(cwd);
3365 if (repo_path == NULL) {
3366 error = got_error_from_errno("strdup");
3367 goto done;
3372 error = got_repo_open(&repo, repo_path, NULL);
3373 if (error != NULL)
3374 goto done;
3376 error = apply_unveil(got_repo_get_path(repo), do_list,
3377 worktree ? got_worktree_get_root_path(worktree) : NULL);
3378 if (error)
3379 goto done;
3381 if (do_list)
3382 error = list_refs(repo);
3383 else if (delref)
3384 error = delete_ref(repo, delref);
3385 else if (create_symref)
3386 error = add_symref(repo, argv[0], argv[1]);
3387 else
3388 error = add_ref(repo, argv[0], argv[1]);
3389 done:
3390 if (repo)
3391 got_repo_close(repo);
3392 if (worktree)
3393 got_worktree_close(worktree);
3394 free(cwd);
3395 free(repo_path);
3396 return error;
3399 __dead static void
3400 usage_branch(void)
3402 fprintf(stderr,
3403 "usage: %s branch [-c commit] [-d] [-r repository] [-l] [-n] "
3404 "[name]\n", getprogname());
3405 exit(1);
3408 static const struct got_error *
3409 list_branch(struct got_repository *repo, struct got_worktree *worktree,
3410 struct got_reference *ref)
3412 const struct got_error *err = NULL;
3413 const char *refname, *marker = " ";
3414 char *refstr;
3416 refname = got_ref_get_name(ref);
3417 if (worktree && strcmp(refname,
3418 got_worktree_get_head_ref_name(worktree)) == 0) {
3419 struct got_object_id *id = NULL;
3421 err = got_ref_resolve(&id, repo, ref);
3422 if (err)
3423 return err;
3424 if (got_object_id_cmp(id,
3425 got_worktree_get_base_commit_id(worktree)) == 0)
3426 marker = "* ";
3427 else
3428 marker = "~ ";
3429 free(id);
3432 if (strncmp(refname, "refs/heads/", 11) == 0)
3433 refname += 11;
3434 if (strncmp(refname, "refs/got/worktree/", 18) == 0)
3435 refname += 18;
3437 refstr = got_ref_to_str(ref);
3438 if (refstr == NULL)
3439 return got_error_from_errno("got_ref_to_str");
3441 printf("%s%s: %s\n", marker, refname, refstr);
3442 free(refstr);
3443 return NULL;
3446 static const struct got_error *
3447 show_current_branch(struct got_repository *repo, struct got_worktree *worktree)
3449 const char *refname;
3451 if (worktree == NULL)
3452 return got_error(GOT_ERR_NOT_WORKTREE);
3454 refname = got_worktree_get_head_ref_name(worktree);
3456 if (strncmp(refname, "refs/heads/", 11) == 0)
3457 refname += 11;
3458 if (strncmp(refname, "refs/got/worktree/", 18) == 0)
3459 refname += 18;
3461 printf("%s\n", refname);
3463 return NULL;
3466 static const struct got_error *
3467 list_branches(struct got_repository *repo, struct got_worktree *worktree)
3469 static const struct got_error *err = NULL;
3470 struct got_reflist_head refs;
3471 struct got_reflist_entry *re;
3472 struct got_reference *temp_ref = NULL;
3473 int rebase_in_progress, histedit_in_progress;
3475 SIMPLEQ_INIT(&refs);
3477 if (worktree) {
3478 err = got_worktree_rebase_in_progress(&rebase_in_progress,
3479 worktree);
3480 if (err)
3481 return err;
3483 err = got_worktree_histedit_in_progress(&histedit_in_progress,
3484 worktree);
3485 if (err)
3486 return err;
3488 if (rebase_in_progress || histedit_in_progress) {
3489 err = got_ref_open(&temp_ref, repo,
3490 got_worktree_get_head_ref_name(worktree), 0);
3491 if (err)
3492 return err;
3493 list_branch(repo, worktree, temp_ref);
3494 got_ref_close(temp_ref);
3498 err = got_ref_list(&refs, repo, "refs/heads",
3499 got_ref_cmp_by_name, NULL);
3500 if (err)
3501 return err;
3503 SIMPLEQ_FOREACH(re, &refs, entry)
3504 list_branch(repo, worktree, re->ref);
3506 got_ref_list_free(&refs);
3507 return NULL;
3510 static const struct got_error *
3511 delete_branch(struct got_repository *repo, struct got_worktree *worktree,
3512 const char *branch_name)
3514 const struct got_error *err = NULL;
3515 struct got_reference *ref = NULL;
3516 char *refname;
3518 if (asprintf(&refname, "refs/heads/%s", branch_name) == -1)
3519 return got_error_from_errno("asprintf");
3521 err = got_ref_open(&ref, repo, refname, 0);
3522 if (err)
3523 goto done;
3525 if (worktree &&
3526 strcmp(got_worktree_get_head_ref_name(worktree),
3527 got_ref_get_name(ref)) == 0) {
3528 err = got_error_msg(GOT_ERR_SAME_BRANCH,
3529 "will not delete this work tree's current branch");
3530 goto done;
3533 err = got_ref_delete(ref, repo);
3534 done:
3535 if (ref)
3536 got_ref_close(ref);
3537 free(refname);
3538 return err;
3541 static const struct got_error *
3542 add_branch(struct got_repository *repo, const char *branch_name,
3543 struct got_object_id *base_commit_id)
3545 const struct got_error *err = NULL;
3546 struct got_reference *ref = NULL;
3547 char *base_refname = NULL, *refname = NULL;
3550 * Don't let the user create a branch name with a leading '-'.
3551 * While technically a valid reference name, this case is usually
3552 * an unintended typo.
3554 if (branch_name[0] == '-')
3555 return got_error_path(branch_name, GOT_ERR_REF_NAME_MINUS);
3557 if (asprintf(&refname, "refs/heads/%s", branch_name) == -1) {
3558 err = got_error_from_errno("asprintf");
3559 goto done;
3562 err = got_ref_open(&ref, repo, refname, 0);
3563 if (err == NULL) {
3564 err = got_error(GOT_ERR_BRANCH_EXISTS);
3565 goto done;
3566 } else if (err->code != GOT_ERR_NOT_REF)
3567 goto done;
3569 err = got_ref_alloc(&ref, refname, base_commit_id);
3570 if (err)
3571 goto done;
3573 err = got_ref_write(ref, repo);
3574 done:
3575 if (ref)
3576 got_ref_close(ref);
3577 free(base_refname);
3578 free(refname);
3579 return err;
3582 static const struct got_error *
3583 cmd_branch(int argc, char *argv[])
3585 const struct got_error *error = NULL;
3586 struct got_repository *repo = NULL;
3587 struct got_worktree *worktree = NULL;
3588 char *cwd = NULL, *repo_path = NULL;
3589 int ch, do_list = 0, do_show = 0, do_update = 1;
3590 const char *delref = NULL, *commit_id_arg = NULL;
3591 struct got_reference *ref = NULL;
3592 struct got_pathlist_head paths;
3593 struct got_pathlist_entry *pe;
3594 struct got_object_id *commit_id = NULL;
3595 char *commit_id_str = NULL;
3597 TAILQ_INIT(&paths);
3599 while ((ch = getopt(argc, argv, "c:d:r:ln")) != -1) {
3600 switch (ch) {
3601 case 'c':
3602 commit_id_arg = optarg;
3603 break;
3604 case 'd':
3605 delref = optarg;
3606 break;
3607 case 'r':
3608 repo_path = realpath(optarg, NULL);
3609 if (repo_path == NULL)
3610 return got_error_from_errno2("realpath",
3611 optarg);
3612 got_path_strip_trailing_slashes(repo_path);
3613 break;
3614 case 'l':
3615 do_list = 1;
3616 break;
3617 case 'n':
3618 do_update = 0;
3619 break;
3620 default:
3621 usage_branch();
3622 /* NOTREACHED */
3626 if (do_list && delref)
3627 errx(1, "-l and -d options are mutually exclusive\n");
3629 argc -= optind;
3630 argv += optind;
3632 if (!do_list && !delref && argc == 0)
3633 do_show = 1;
3635 if ((do_list || delref || do_show) && commit_id_arg != NULL)
3636 errx(1, "-c option can only be used when creating a branch");
3638 if (do_list || delref) {
3639 if (argc > 0)
3640 usage_branch();
3641 } else if (!do_show && argc != 1)
3642 usage_branch();
3644 #ifndef PROFILE
3645 if (do_list || do_show) {
3646 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
3647 NULL) == -1)
3648 err(1, "pledge");
3649 } else {
3650 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
3651 "sendfd unveil", NULL) == -1)
3652 err(1, "pledge");
3654 #endif
3655 cwd = getcwd(NULL, 0);
3656 if (cwd == NULL) {
3657 error = got_error_from_errno("getcwd");
3658 goto done;
3661 if (repo_path == NULL) {
3662 error = got_worktree_open(&worktree, cwd);
3663 if (error && error->code != GOT_ERR_NOT_WORKTREE)
3664 goto done;
3665 else
3666 error = NULL;
3667 if (worktree) {
3668 repo_path =
3669 strdup(got_worktree_get_repo_path(worktree));
3670 if (repo_path == NULL)
3671 error = got_error_from_errno("strdup");
3672 if (error)
3673 goto done;
3674 } else {
3675 repo_path = strdup(cwd);
3676 if (repo_path == NULL) {
3677 error = got_error_from_errno("strdup");
3678 goto done;
3683 error = got_repo_open(&repo, repo_path, NULL);
3684 if (error != NULL)
3685 goto done;
3687 error = apply_unveil(got_repo_get_path(repo), do_list,
3688 worktree ? got_worktree_get_root_path(worktree) : NULL);
3689 if (error)
3690 goto done;
3692 if (do_show)
3693 error = show_current_branch(repo, worktree);
3694 else if (do_list)
3695 error = list_branches(repo, worktree);
3696 else if (delref)
3697 error = delete_branch(repo, worktree, delref);
3698 else {
3699 if (commit_id_arg == NULL)
3700 commit_id_arg = worktree ?
3701 got_worktree_get_head_ref_name(worktree) :
3702 GOT_REF_HEAD;
3703 error = got_repo_match_object_id(&commit_id, NULL,
3704 commit_id_arg, GOT_OBJ_TYPE_COMMIT, 1, repo);
3705 if (error)
3706 goto done;
3707 error = add_branch(repo, argv[0], commit_id);
3708 if (error)
3709 goto done;
3710 if (worktree && do_update) {
3711 int did_something = 0;
3712 char *branch_refname = NULL;
3714 error = got_object_id_str(&commit_id_str, commit_id);
3715 if (error)
3716 goto done;
3717 error = get_worktree_paths_from_argv(&paths, 0, NULL,
3718 worktree);
3719 if (error)
3720 goto done;
3721 if (asprintf(&branch_refname, "refs/heads/%s", argv[0])
3722 == -1) {
3723 error = got_error_from_errno("asprintf");
3724 goto done;
3726 error = got_ref_open(&ref, repo, branch_refname, 0);
3727 free(branch_refname);
3728 if (error)
3729 goto done;
3730 error = switch_head_ref(ref, commit_id, worktree,
3731 repo);
3732 if (error)
3733 goto done;
3734 error = got_worktree_set_base_commit_id(worktree, repo,
3735 commit_id);
3736 if (error)
3737 goto done;
3738 error = got_worktree_checkout_files(worktree, &paths,
3739 repo, update_progress, &did_something,
3740 check_cancelled, NULL);
3741 if (error)
3742 goto done;
3743 if (did_something)
3744 printf("Updated to commit %s\n", commit_id_str);
3747 done:
3748 if (ref)
3749 got_ref_close(ref);
3750 if (repo)
3751 got_repo_close(repo);
3752 if (worktree)
3753 got_worktree_close(worktree);
3754 free(cwd);
3755 free(repo_path);
3756 free(commit_id);
3757 free(commit_id_str);
3758 TAILQ_FOREACH(pe, &paths, entry)
3759 free((char *)pe->path);
3760 got_pathlist_free(&paths);
3761 return error;
3765 __dead static void
3766 usage_tag(void)
3768 fprintf(stderr,
3769 "usage: %s tag [-c commit] [-r repository] [-l] "
3770 "[-m message] name\n", getprogname());
3771 exit(1);
3774 #if 0
3775 static const struct got_error *
3776 sort_tags(struct got_reflist_head *sorted, struct got_reflist_head *tags)
3778 const struct got_error *err = NULL;
3779 struct got_reflist_entry *re, *se, *new;
3780 struct got_object_id *re_id, *se_id;
3781 struct got_tag_object *re_tag, *se_tag;
3782 time_t re_time, se_time;
3784 SIMPLEQ_FOREACH(re, tags, entry) {
3785 se = SIMPLEQ_FIRST(sorted);
3786 if (se == NULL) {
3787 err = got_reflist_entry_dup(&new, re);
3788 if (err)
3789 return err;
3790 SIMPLEQ_INSERT_HEAD(sorted, new, entry);
3791 continue;
3792 } else {
3793 err = got_ref_resolve(&re_id, repo, re->ref);
3794 if (err)
3795 break;
3796 err = got_object_open_as_tag(&re_tag, repo, re_id);
3797 free(re_id);
3798 if (err)
3799 break;
3800 re_time = got_object_tag_get_tagger_time(re_tag);
3801 got_object_tag_close(re_tag);
3804 while (se) {
3805 err = got_ref_resolve(&se_id, repo, re->ref);
3806 if (err)
3807 break;
3808 err = got_object_open_as_tag(&se_tag, repo, se_id);
3809 free(se_id);
3810 if (err)
3811 break;
3812 se_time = got_object_tag_get_tagger_time(se_tag);
3813 got_object_tag_close(se_tag);
3815 if (se_time > re_time) {
3816 err = got_reflist_entry_dup(&new, re);
3817 if (err)
3818 return err;
3819 SIMPLEQ_INSERT_AFTER(sorted, se, new, entry);
3820 break;
3822 se = SIMPLEQ_NEXT(se, entry);
3823 continue;
3826 done:
3827 return err;
3829 #endif
3831 static const struct got_error *
3832 list_tags(struct got_repository *repo, struct got_worktree *worktree)
3834 static const struct got_error *err = NULL;
3835 struct got_reflist_head refs;
3836 struct got_reflist_entry *re;
3838 SIMPLEQ_INIT(&refs);
3840 err = got_ref_list(&refs, repo, "refs/tags", got_ref_cmp_tags, repo);
3841 if (err)
3842 return err;
3844 SIMPLEQ_FOREACH(re, &refs, entry) {
3845 const char *refname;
3846 char *refstr, *tagmsg0, *tagmsg, *line, *id_str, *datestr;
3847 char datebuf[26];
3848 const char *tagger;
3849 time_t tagger_time;
3850 struct got_object_id *id;
3851 struct got_tag_object *tag;
3852 struct got_commit_object *commit = NULL;
3854 refname = got_ref_get_name(re->ref);
3855 if (strncmp(refname, "refs/tags/", 10) != 0)
3856 continue;
3857 refname += 10;
3858 refstr = got_ref_to_str(re->ref);
3859 if (refstr == NULL) {
3860 err = got_error_from_errno("got_ref_to_str");
3861 break;
3863 printf("%stag %s %s\n", GOT_COMMIT_SEP_STR, refname, refstr);
3864 free(refstr);
3866 err = got_ref_resolve(&id, repo, re->ref);
3867 if (err)
3868 break;
3869 err = got_object_open_as_tag(&tag, repo, id);
3870 if (err) {
3871 if (err->code != GOT_ERR_OBJ_TYPE) {
3872 free(id);
3873 break;
3875 /* "lightweight" tag */
3876 err = got_object_open_as_commit(&commit, repo, id);
3877 if (err) {
3878 free(id);
3879 break;
3881 tagger = got_object_commit_get_committer(commit);
3882 tagger_time =
3883 got_object_commit_get_committer_time(commit);
3884 err = got_object_id_str(&id_str, id);
3885 free(id);
3886 if (err)
3887 break;
3888 } else {
3889 free(id);
3890 tagger = got_object_tag_get_tagger(tag);
3891 tagger_time = got_object_tag_get_tagger_time(tag);
3892 err = got_object_id_str(&id_str,
3893 got_object_tag_get_object_id(tag));
3894 if (err)
3895 break;
3897 printf("from: %s\n", tagger);
3898 datestr = get_datestr(&tagger_time, datebuf);
3899 if (datestr)
3900 printf("date: %s UTC\n", datestr);
3901 if (commit)
3902 printf("object: %s %s\n", GOT_OBJ_LABEL_COMMIT, id_str);
3903 else {
3904 switch (got_object_tag_get_object_type(tag)) {
3905 case GOT_OBJ_TYPE_BLOB:
3906 printf("object: %s %s\n", GOT_OBJ_LABEL_BLOB,
3907 id_str);
3908 break;
3909 case GOT_OBJ_TYPE_TREE:
3910 printf("object: %s %s\n", GOT_OBJ_LABEL_TREE,
3911 id_str);
3912 break;
3913 case GOT_OBJ_TYPE_COMMIT:
3914 printf("object: %s %s\n", GOT_OBJ_LABEL_COMMIT,
3915 id_str);
3916 break;
3917 case GOT_OBJ_TYPE_TAG:
3918 printf("object: %s %s\n", GOT_OBJ_LABEL_TAG,
3919 id_str);
3920 break;
3921 default:
3922 break;
3925 free(id_str);
3926 if (commit) {
3927 err = got_object_commit_get_logmsg(&tagmsg0, commit);
3928 if (err)
3929 break;
3930 got_object_commit_close(commit);
3931 } else {
3932 tagmsg0 = strdup(got_object_tag_get_message(tag));
3933 got_object_tag_close(tag);
3934 if (tagmsg0 == NULL) {
3935 err = got_error_from_errno("strdup");
3936 break;
3940 tagmsg = tagmsg0;
3941 do {
3942 line = strsep(&tagmsg, "\n");
3943 if (line)
3944 printf(" %s\n", line);
3945 } while (line);
3946 free(tagmsg0);
3949 got_ref_list_free(&refs);
3950 return NULL;
3953 static const struct got_error *
3954 get_tag_message(char **tagmsg, char **tagmsg_path, const char *commit_id_str,
3955 const char *tag_name, const char *repo_path)
3957 const struct got_error *err = NULL;
3958 char *template = NULL, *initial_content = NULL;
3959 char *editor = NULL;
3960 int fd = -1;
3962 if (asprintf(&template, GOT_TMPDIR_STR "/got-tagmsg") == -1) {
3963 err = got_error_from_errno("asprintf");
3964 goto done;
3967 if (asprintf(&initial_content, "\n# tagging commit %s as %s\n",
3968 commit_id_str, tag_name) == -1) {
3969 err = got_error_from_errno("asprintf");
3970 goto done;
3973 err = got_opentemp_named_fd(tagmsg_path, &fd, template);
3974 if (err)
3975 goto done;
3977 dprintf(fd, initial_content);
3978 close(fd);
3980 err = get_editor(&editor);
3981 if (err)
3982 goto done;
3983 err = edit_logmsg(tagmsg, editor, *tagmsg_path, initial_content);
3984 done:
3985 free(initial_content);
3986 free(template);
3987 free(editor);
3989 /* Editor is done; we can now apply unveil(2) */
3990 if (err == NULL) {
3991 err = apply_unveil(repo_path, 0, NULL);
3992 if (err) {
3993 free(*tagmsg);
3994 *tagmsg = NULL;
3997 return err;
4000 static const struct got_error *
4001 add_tag(struct got_repository *repo, const char *tag_name,
4002 const char *commit_arg, const char *tagmsg_arg)
4004 const struct got_error *err = NULL;
4005 struct got_object_id *commit_id = NULL, *tag_id = NULL;
4006 char *label = NULL, *commit_id_str = NULL;
4007 struct got_reference *ref = NULL;
4008 char *refname = NULL, *tagmsg = NULL, *tagger = NULL;
4009 char *tagmsg_path = NULL, *tag_id_str = NULL;
4010 int preserve_tagmsg = 0;
4013 * Don't let the user create a tag name with a leading '-'.
4014 * While technically a valid reference name, this case is usually
4015 * an unintended typo.
4017 if (tag_name[0] == '-')
4018 return got_error_path(tag_name, GOT_ERR_REF_NAME_MINUS);
4020 err = get_author(&tagger, repo);
4021 if (err)
4022 return err;
4024 err = got_repo_match_object_id(&commit_id, &label, commit_arg,
4025 GOT_OBJ_TYPE_COMMIT, 1, repo);
4026 if (err)
4027 goto done;
4029 err = got_object_id_str(&commit_id_str, commit_id);
4030 if (err)
4031 goto done;
4033 if (strncmp("refs/tags/", tag_name, 10) == 0) {
4034 refname = strdup(tag_name);
4035 if (refname == NULL) {
4036 err = got_error_from_errno("strdup");
4037 goto done;
4039 tag_name += 10;
4040 } else if (asprintf(&refname, "refs/tags/%s", tag_name) == -1) {
4041 err = got_error_from_errno("asprintf");
4042 goto done;
4045 err = got_ref_open(&ref, repo, refname, 0);
4046 if (err == NULL) {
4047 err = got_error(GOT_ERR_TAG_EXISTS);
4048 goto done;
4049 } else if (err->code != GOT_ERR_NOT_REF)
4050 goto done;
4052 if (tagmsg_arg == NULL) {
4053 err = get_tag_message(&tagmsg, &tagmsg_path, commit_id_str,
4054 tag_name, got_repo_get_path(repo));
4055 if (err) {
4056 if (err->code != GOT_ERR_COMMIT_MSG_EMPTY &&
4057 tagmsg_path != NULL)
4058 preserve_tagmsg = 1;
4059 goto done;
4063 err = got_object_tag_create(&tag_id, tag_name, commit_id,
4064 tagger, time(NULL), tagmsg ? tagmsg : tagmsg_arg, repo);
4065 if (err) {
4066 if (tagmsg_path)
4067 preserve_tagmsg = 1;
4068 goto done;
4071 err = got_ref_alloc(&ref, refname, tag_id);
4072 if (err) {
4073 if (tagmsg_path)
4074 preserve_tagmsg = 1;
4075 goto done;
4078 err = got_ref_write(ref, repo);
4079 if (err) {
4080 if (tagmsg_path)
4081 preserve_tagmsg = 1;
4082 goto done;
4085 err = got_object_id_str(&tag_id_str, tag_id);
4086 if (err) {
4087 if (tagmsg_path)
4088 preserve_tagmsg = 1;
4089 goto done;
4091 printf("Created tag %s\n", tag_id_str);
4092 done:
4093 if (preserve_tagmsg) {
4094 fprintf(stderr, "%s: tag message preserved in %s\n",
4095 getprogname(), tagmsg_path);
4096 } else if (tagmsg_path && unlink(tagmsg_path) == -1 && err == NULL)
4097 err = got_error_from_errno2("unlink", tagmsg_path);
4098 free(tag_id_str);
4099 if (ref)
4100 got_ref_close(ref);
4101 free(commit_id);
4102 free(commit_id_str);
4103 free(refname);
4104 free(tagmsg);
4105 free(tagmsg_path);
4106 free(tagger);
4107 return err;
4110 static const struct got_error *
4111 cmd_tag(int argc, char *argv[])
4113 const struct got_error *error = NULL;
4114 struct got_repository *repo = NULL;
4115 struct got_worktree *worktree = NULL;
4116 char *cwd = NULL, *repo_path = NULL, *commit_id_str = NULL;
4117 char *gitconfig_path = NULL;
4118 const char *tag_name, *commit_id_arg = NULL, *tagmsg = NULL;
4119 int ch, do_list = 0;
4121 while ((ch = getopt(argc, argv, "c:m:r:l")) != -1) {
4122 switch (ch) {
4123 case 'c':
4124 commit_id_arg = optarg;
4125 break;
4126 case 'm':
4127 tagmsg = optarg;
4128 break;
4129 case 'r':
4130 repo_path = realpath(optarg, NULL);
4131 if (repo_path == NULL)
4132 return got_error_from_errno2("realpath",
4133 optarg);
4134 got_path_strip_trailing_slashes(repo_path);
4135 break;
4136 case 'l':
4137 do_list = 1;
4138 break;
4139 default:
4140 usage_tag();
4141 /* NOTREACHED */
4145 argc -= optind;
4146 argv += optind;
4148 if (do_list) {
4149 if (commit_id_arg != NULL)
4150 errx(1, "-c option can only be used when creating a tag");
4151 if (tagmsg)
4152 errx(1, "-l and -m options are mutually exclusive");
4153 if (argc > 0)
4154 usage_tag();
4155 } else if (argc != 1)
4156 usage_tag();
4158 tag_name = argv[0];
4160 #ifndef PROFILE
4161 if (do_list) {
4162 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
4163 NULL) == -1)
4164 err(1, "pledge");
4165 } else {
4166 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
4167 "sendfd unveil", NULL) == -1)
4168 err(1, "pledge");
4170 #endif
4171 cwd = getcwd(NULL, 0);
4172 if (cwd == NULL) {
4173 error = got_error_from_errno("getcwd");
4174 goto done;
4177 if (repo_path == NULL) {
4178 error = got_worktree_open(&worktree, cwd);
4179 if (error && error->code != GOT_ERR_NOT_WORKTREE)
4180 goto done;
4181 else
4182 error = NULL;
4183 if (worktree) {
4184 repo_path =
4185 strdup(got_worktree_get_repo_path(worktree));
4186 if (repo_path == NULL)
4187 error = got_error_from_errno("strdup");
4188 if (error)
4189 goto done;
4190 } else {
4191 repo_path = strdup(cwd);
4192 if (repo_path == NULL) {
4193 error = got_error_from_errno("strdup");
4194 goto done;
4199 if (do_list) {
4200 error = got_repo_open(&repo, repo_path, NULL);
4201 if (error != NULL)
4202 goto done;
4203 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
4204 if (error)
4205 goto done;
4206 error = list_tags(repo, worktree);
4207 } else {
4208 error = get_gitconfig_path(&gitconfig_path);
4209 if (error)
4210 goto done;
4211 error = got_repo_open(&repo, repo_path, gitconfig_path);
4212 if (error != NULL)
4213 goto done;
4215 if (tagmsg) {
4216 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
4217 if (error)
4218 goto done;
4221 if (commit_id_arg == NULL) {
4222 struct got_reference *head_ref;
4223 struct got_object_id *commit_id;
4224 error = got_ref_open(&head_ref, repo,
4225 worktree ? got_worktree_get_head_ref_name(worktree)
4226 : GOT_REF_HEAD, 0);
4227 if (error)
4228 goto done;
4229 error = got_ref_resolve(&commit_id, repo, head_ref);
4230 got_ref_close(head_ref);
4231 if (error)
4232 goto done;
4233 error = got_object_id_str(&commit_id_str, commit_id);
4234 free(commit_id);
4235 if (error)
4236 goto done;
4239 error = add_tag(repo, tag_name,
4240 commit_id_str ? commit_id_str : commit_id_arg, tagmsg);
4242 done:
4243 if (repo)
4244 got_repo_close(repo);
4245 if (worktree)
4246 got_worktree_close(worktree);
4247 free(cwd);
4248 free(repo_path);
4249 free(gitconfig_path);
4250 free(commit_id_str);
4251 return error;
4254 __dead static void
4255 usage_add(void)
4257 fprintf(stderr, "usage: %s add [-R] [-I] path ...\n",
4258 getprogname());
4259 exit(1);
4262 static const struct got_error *
4263 add_progress(void *arg, unsigned char status, const char *path)
4265 while (path[0] == '/')
4266 path++;
4267 printf("%c %s\n", status, path);
4268 return NULL;
4271 static const struct got_error *
4272 cmd_add(int argc, char *argv[])
4274 const struct got_error *error = NULL;
4275 struct got_repository *repo = NULL;
4276 struct got_worktree *worktree = NULL;
4277 char *cwd = NULL;
4278 struct got_pathlist_head paths;
4279 struct got_pathlist_entry *pe;
4280 int ch, can_recurse = 0, no_ignores = 0;
4282 TAILQ_INIT(&paths);
4284 while ((ch = getopt(argc, argv, "IR")) != -1) {
4285 switch (ch) {
4286 case 'I':
4287 no_ignores = 1;
4288 break;
4289 case 'R':
4290 can_recurse = 1;
4291 break;
4292 default:
4293 usage_add();
4294 /* NOTREACHED */
4298 argc -= optind;
4299 argv += optind;
4301 #ifndef PROFILE
4302 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
4303 NULL) == -1)
4304 err(1, "pledge");
4305 #endif
4306 if (argc < 1)
4307 usage_add();
4309 cwd = getcwd(NULL, 0);
4310 if (cwd == NULL) {
4311 error = got_error_from_errno("getcwd");
4312 goto done;
4315 error = got_worktree_open(&worktree, cwd);
4316 if (error)
4317 goto done;
4319 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
4320 NULL);
4321 if (error != NULL)
4322 goto done;
4324 error = apply_unveil(got_repo_get_path(repo), 1,
4325 got_worktree_get_root_path(worktree));
4326 if (error)
4327 goto done;
4329 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
4330 if (error)
4331 goto done;
4333 if (!can_recurse && no_ignores) {
4334 error = got_error_msg(GOT_ERR_BAD_PATH,
4335 "disregarding ignores requires -R option");
4336 goto done;
4340 if (!can_recurse) {
4341 char *ondisk_path;
4342 struct stat sb;
4343 TAILQ_FOREACH(pe, &paths, entry) {
4344 if (asprintf(&ondisk_path, "%s/%s",
4345 got_worktree_get_root_path(worktree),
4346 pe->path) == -1) {
4347 error = got_error_from_errno("asprintf");
4348 goto done;
4350 if (lstat(ondisk_path, &sb) == -1) {
4351 if (errno == ENOENT) {
4352 free(ondisk_path);
4353 continue;
4355 error = got_error_from_errno2("lstat",
4356 ondisk_path);
4357 free(ondisk_path);
4358 goto done;
4360 free(ondisk_path);
4361 if (S_ISDIR(sb.st_mode)) {
4362 error = got_error_msg(GOT_ERR_BAD_PATH,
4363 "adding directories requires -R option");
4364 goto done;
4369 error = got_worktree_schedule_add(worktree, &paths, add_progress,
4370 NULL, repo, no_ignores);
4371 done:
4372 if (repo)
4373 got_repo_close(repo);
4374 if (worktree)
4375 got_worktree_close(worktree);
4376 TAILQ_FOREACH(pe, &paths, entry)
4377 free((char *)pe->path);
4378 got_pathlist_free(&paths);
4379 free(cwd);
4380 return error;
4383 __dead static void
4384 usage_remove(void)
4386 fprintf(stderr, "usage: %s remove [-f] [-k] [-R] path ...\n",
4387 getprogname());
4388 exit(1);
4391 static const struct got_error *
4392 print_remove_status(void *arg, unsigned char status,
4393 unsigned char staged_status, const char *path)
4395 while (path[0] == '/')
4396 path++;
4397 if (status == GOT_STATUS_NONEXISTENT)
4398 return NULL;
4399 if (status == staged_status && (status == GOT_STATUS_DELETE))
4400 status = GOT_STATUS_NO_CHANGE;
4401 printf("%c%c %s\n", status, staged_status, path);
4402 return NULL;
4405 static const struct got_error *
4406 cmd_remove(int argc, char *argv[])
4408 const struct got_error *error = NULL;
4409 struct got_worktree *worktree = NULL;
4410 struct got_repository *repo = NULL;
4411 char *cwd = NULL;
4412 struct got_pathlist_head paths;
4413 struct got_pathlist_entry *pe;
4414 int ch, delete_local_mods = 0, can_recurse = 0, keep_on_disk = 0;
4416 TAILQ_INIT(&paths);
4418 while ((ch = getopt(argc, argv, "fkR")) != -1) {
4419 switch (ch) {
4420 case 'f':
4421 delete_local_mods = 1;
4422 break;
4423 case 'k':
4424 keep_on_disk = 1;
4425 break;
4426 case 'R':
4427 can_recurse = 1;
4428 break;
4429 default:
4430 usage_remove();
4431 /* NOTREACHED */
4435 argc -= optind;
4436 argv += optind;
4438 #ifndef PROFILE
4439 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
4440 NULL) == -1)
4441 err(1, "pledge");
4442 #endif
4443 if (argc < 1)
4444 usage_remove();
4446 cwd = getcwd(NULL, 0);
4447 if (cwd == NULL) {
4448 error = got_error_from_errno("getcwd");
4449 goto done;
4451 error = got_worktree_open(&worktree, cwd);
4452 if (error)
4453 goto done;
4455 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
4456 NULL);
4457 if (error)
4458 goto done;
4460 error = apply_unveil(got_repo_get_path(repo), 1,
4461 got_worktree_get_root_path(worktree));
4462 if (error)
4463 goto done;
4465 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
4466 if (error)
4467 goto done;
4469 if (!can_recurse) {
4470 char *ondisk_path;
4471 struct stat sb;
4472 TAILQ_FOREACH(pe, &paths, entry) {
4473 if (asprintf(&ondisk_path, "%s/%s",
4474 got_worktree_get_root_path(worktree),
4475 pe->path) == -1) {
4476 error = got_error_from_errno("asprintf");
4477 goto done;
4479 if (lstat(ondisk_path, &sb) == -1) {
4480 if (errno == ENOENT) {
4481 free(ondisk_path);
4482 continue;
4484 error = got_error_from_errno2("lstat",
4485 ondisk_path);
4486 free(ondisk_path);
4487 goto done;
4489 free(ondisk_path);
4490 if (S_ISDIR(sb.st_mode)) {
4491 error = got_error_msg(GOT_ERR_BAD_PATH,
4492 "removing directories requires -R option");
4493 goto done;
4498 error = got_worktree_schedule_delete(worktree, &paths,
4499 delete_local_mods, print_remove_status, NULL, repo, keep_on_disk);
4500 done:
4501 if (repo)
4502 got_repo_close(repo);
4503 if (worktree)
4504 got_worktree_close(worktree);
4505 TAILQ_FOREACH(pe, &paths, entry)
4506 free((char *)pe->path);
4507 got_pathlist_free(&paths);
4508 free(cwd);
4509 return error;
4512 __dead static void
4513 usage_revert(void)
4515 fprintf(stderr, "usage: %s revert [-p] [-F response-script] [-R] "
4516 "path ...\n", getprogname());
4517 exit(1);
4520 static const struct got_error *
4521 revert_progress(void *arg, unsigned char status, const char *path)
4523 if (status == GOT_STATUS_UNVERSIONED)
4524 return NULL;
4526 while (path[0] == '/')
4527 path++;
4528 printf("%c %s\n", status, path);
4529 return NULL;
4532 struct choose_patch_arg {
4533 FILE *patch_script_file;
4534 const char *action;
4537 static const struct got_error *
4538 show_change(unsigned char status, const char *path, FILE *patch_file, int n,
4539 int nchanges, const char *action)
4541 char *line = NULL;
4542 size_t linesize = 0;
4543 ssize_t linelen;
4545 switch (status) {
4546 case GOT_STATUS_ADD:
4547 printf("A %s\n%s this addition? [y/n] ", path, action);
4548 break;
4549 case GOT_STATUS_DELETE:
4550 printf("D %s\n%s this deletion? [y/n] ", path, action);
4551 break;
4552 case GOT_STATUS_MODIFY:
4553 if (fseek(patch_file, 0L, SEEK_SET) == -1)
4554 return got_error_from_errno("fseek");
4555 printf(GOT_COMMIT_SEP_STR);
4556 while ((linelen = getline(&line, &linesize, patch_file)) != -1)
4557 printf("%s", line);
4558 if (ferror(patch_file))
4559 return got_error_from_errno("getline");
4560 printf(GOT_COMMIT_SEP_STR);
4561 printf("M %s (change %d of %d)\n%s this change? [y/n/q] ",
4562 path, n, nchanges, action);
4563 break;
4564 default:
4565 return got_error_path(path, GOT_ERR_FILE_STATUS);
4568 return NULL;
4571 static const struct got_error *
4572 choose_patch(int *choice, void *arg, unsigned char status, const char *path,
4573 FILE *patch_file, int n, int nchanges)
4575 const struct got_error *err = NULL;
4576 char *line = NULL;
4577 size_t linesize = 0;
4578 ssize_t linelen;
4579 int resp = ' ';
4580 struct choose_patch_arg *a = arg;
4582 *choice = GOT_PATCH_CHOICE_NONE;
4584 if (a->patch_script_file) {
4585 char *nl;
4586 err = show_change(status, path, patch_file, n, nchanges,
4587 a->action);
4588 if (err)
4589 return err;
4590 linelen = getline(&line, &linesize, a->patch_script_file);
4591 if (linelen == -1) {
4592 if (ferror(a->patch_script_file))
4593 return got_error_from_errno("getline");
4594 return NULL;
4596 nl = strchr(line, '\n');
4597 if (nl)
4598 *nl = '\0';
4599 if (strcmp(line, "y") == 0) {
4600 *choice = GOT_PATCH_CHOICE_YES;
4601 printf("y\n");
4602 } else if (strcmp(line, "n") == 0) {
4603 *choice = GOT_PATCH_CHOICE_NO;
4604 printf("n\n");
4605 } else if (strcmp(line, "q") == 0 &&
4606 status == GOT_STATUS_MODIFY) {
4607 *choice = GOT_PATCH_CHOICE_QUIT;
4608 printf("q\n");
4609 } else
4610 printf("invalid response '%s'\n", line);
4611 free(line);
4612 return NULL;
4615 while (resp != 'y' && resp != 'n' && resp != 'q') {
4616 err = show_change(status, path, patch_file, n, nchanges,
4617 a->action);
4618 if (err)
4619 return err;
4620 resp = getchar();
4621 if (resp == '\n')
4622 resp = getchar();
4623 if (status == GOT_STATUS_MODIFY) {
4624 if (resp != 'y' && resp != 'n' && resp != 'q') {
4625 printf("invalid response '%c'\n", resp);
4626 resp = ' ';
4628 } else if (resp != 'y' && resp != 'n') {
4629 printf("invalid response '%c'\n", resp);
4630 resp = ' ';
4634 if (resp == 'y')
4635 *choice = GOT_PATCH_CHOICE_YES;
4636 else if (resp == 'n')
4637 *choice = GOT_PATCH_CHOICE_NO;
4638 else if (resp == 'q' && status == GOT_STATUS_MODIFY)
4639 *choice = GOT_PATCH_CHOICE_QUIT;
4641 return NULL;
4645 static const struct got_error *
4646 cmd_revert(int argc, char *argv[])
4648 const struct got_error *error = NULL;
4649 struct got_worktree *worktree = NULL;
4650 struct got_repository *repo = NULL;
4651 char *cwd = NULL, *path = NULL;
4652 struct got_pathlist_head paths;
4653 struct got_pathlist_entry *pe;
4654 int ch, can_recurse = 0, pflag = 0;
4655 FILE *patch_script_file = NULL;
4656 const char *patch_script_path = NULL;
4657 struct choose_patch_arg cpa;
4659 TAILQ_INIT(&paths);
4661 while ((ch = getopt(argc, argv, "pF:R")) != -1) {
4662 switch (ch) {
4663 case 'p':
4664 pflag = 1;
4665 break;
4666 case 'F':
4667 patch_script_path = optarg;
4668 break;
4669 case 'R':
4670 can_recurse = 1;
4671 break;
4672 default:
4673 usage_revert();
4674 /* NOTREACHED */
4678 argc -= optind;
4679 argv += optind;
4681 #ifndef PROFILE
4682 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
4683 "unveil", NULL) == -1)
4684 err(1, "pledge");
4685 #endif
4686 if (argc < 1)
4687 usage_revert();
4688 if (patch_script_path && !pflag)
4689 errx(1, "-F option can only be used together with -p option");
4691 cwd = getcwd(NULL, 0);
4692 if (cwd == NULL) {
4693 error = got_error_from_errno("getcwd");
4694 goto done;
4696 error = got_worktree_open(&worktree, cwd);
4697 if (error)
4698 goto done;
4700 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
4701 NULL);
4702 if (error != NULL)
4703 goto done;
4705 if (patch_script_path) {
4706 patch_script_file = fopen(patch_script_path, "r");
4707 if (patch_script_file == NULL) {
4708 error = got_error_from_errno2("fopen",
4709 patch_script_path);
4710 goto done;
4713 error = apply_unveil(got_repo_get_path(repo), 1,
4714 got_worktree_get_root_path(worktree));
4715 if (error)
4716 goto done;
4718 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
4719 if (error)
4720 goto done;
4722 if (!can_recurse) {
4723 char *ondisk_path;
4724 struct stat sb;
4725 TAILQ_FOREACH(pe, &paths, entry) {
4726 if (asprintf(&ondisk_path, "%s/%s",
4727 got_worktree_get_root_path(worktree),
4728 pe->path) == -1) {
4729 error = got_error_from_errno("asprintf");
4730 goto done;
4732 if (lstat(ondisk_path, &sb) == -1) {
4733 if (errno == ENOENT) {
4734 free(ondisk_path);
4735 continue;
4737 error = got_error_from_errno2("lstat",
4738 ondisk_path);
4739 free(ondisk_path);
4740 goto done;
4742 free(ondisk_path);
4743 if (S_ISDIR(sb.st_mode)) {
4744 error = got_error_msg(GOT_ERR_BAD_PATH,
4745 "reverting directories requires -R option");
4746 goto done;
4751 cpa.patch_script_file = patch_script_file;
4752 cpa.action = "revert";
4753 error = got_worktree_revert(worktree, &paths, revert_progress, NULL,
4754 pflag ? choose_patch : NULL, &cpa, repo);
4755 done:
4756 if (patch_script_file && fclose(patch_script_file) == EOF &&
4757 error == NULL)
4758 error = got_error_from_errno2("fclose", patch_script_path);
4759 if (repo)
4760 got_repo_close(repo);
4761 if (worktree)
4762 got_worktree_close(worktree);
4763 free(path);
4764 free(cwd);
4765 return error;
4768 __dead static void
4769 usage_commit(void)
4771 fprintf(stderr, "usage: %s commit [-m msg] [path ...]\n",
4772 getprogname());
4773 exit(1);
4776 struct collect_commit_logmsg_arg {
4777 const char *cmdline_log;
4778 const char *editor;
4779 const char *worktree_path;
4780 const char *branch_name;
4781 const char *repo_path;
4782 char *logmsg_path;
4786 static const struct got_error *
4787 collect_commit_logmsg(struct got_pathlist_head *commitable_paths, char **logmsg,
4788 void *arg)
4790 char *initial_content = NULL;
4791 struct got_pathlist_entry *pe;
4792 const struct got_error *err = NULL;
4793 char *template = NULL;
4794 struct collect_commit_logmsg_arg *a = arg;
4795 int fd;
4796 size_t len;
4798 /* if a message was specified on the command line, just use it */
4799 if (a->cmdline_log != NULL && strlen(a->cmdline_log) != 0) {
4800 len = strlen(a->cmdline_log) + 1;
4801 *logmsg = malloc(len + 1);
4802 if (*logmsg == NULL)
4803 return got_error_from_errno("malloc");
4804 strlcpy(*logmsg, a->cmdline_log, len);
4805 return NULL;
4808 if (asprintf(&template, "%s/logmsg", a->worktree_path) == -1)
4809 return got_error_from_errno("asprintf");
4811 if (asprintf(&initial_content,
4812 "\n# changes to be committed on branch %s:\n",
4813 a->branch_name) == -1)
4814 return got_error_from_errno("asprintf");
4816 err = got_opentemp_named_fd(&a->logmsg_path, &fd, template);
4817 if (err)
4818 goto done;
4820 dprintf(fd, initial_content);
4822 TAILQ_FOREACH(pe, commitable_paths, entry) {
4823 struct got_commitable *ct = pe->data;
4824 dprintf(fd, "# %c %s\n",
4825 got_commitable_get_status(ct),
4826 got_commitable_get_path(ct));
4828 close(fd);
4830 err = edit_logmsg(logmsg, a->editor, a->logmsg_path, initial_content);
4831 done:
4832 free(initial_content);
4833 free(template);
4835 /* Editor is done; we can now apply unveil(2) */
4836 if (err == NULL) {
4837 err = apply_unveil(a->repo_path, 0, a->worktree_path);
4838 if (err) {
4839 free(*logmsg);
4840 *logmsg = NULL;
4843 return err;
4846 static const struct got_error *
4847 cmd_commit(int argc, char *argv[])
4849 const struct got_error *error = NULL;
4850 struct got_worktree *worktree = NULL;
4851 struct got_repository *repo = NULL;
4852 char *cwd = NULL, *id_str = NULL;
4853 struct got_object_id *id = NULL;
4854 const char *logmsg = NULL;
4855 struct collect_commit_logmsg_arg cl_arg;
4856 char *gitconfig_path = NULL, *editor = NULL, *author = NULL;
4857 int ch, rebase_in_progress, histedit_in_progress, preserve_logmsg = 0;
4858 struct got_pathlist_head paths;
4860 TAILQ_INIT(&paths);
4861 cl_arg.logmsg_path = NULL;
4863 while ((ch = getopt(argc, argv, "m:")) != -1) {
4864 switch (ch) {
4865 case 'm':
4866 logmsg = optarg;
4867 break;
4868 default:
4869 usage_commit();
4870 /* NOTREACHED */
4874 argc -= optind;
4875 argv += optind;
4877 #ifndef PROFILE
4878 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
4879 "unveil", NULL) == -1)
4880 err(1, "pledge");
4881 #endif
4882 cwd = getcwd(NULL, 0);
4883 if (cwd == NULL) {
4884 error = got_error_from_errno("getcwd");
4885 goto done;
4887 error = got_worktree_open(&worktree, cwd);
4888 if (error)
4889 goto done;
4891 error = got_worktree_rebase_in_progress(&rebase_in_progress, worktree);
4892 if (error)
4893 goto done;
4894 if (rebase_in_progress) {
4895 error = got_error(GOT_ERR_REBASING);
4896 goto done;
4899 error = got_worktree_histedit_in_progress(&histedit_in_progress,
4900 worktree);
4901 if (error)
4902 goto done;
4904 error = get_gitconfig_path(&gitconfig_path);
4905 if (error)
4906 goto done;
4907 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
4908 gitconfig_path);
4909 if (error != NULL)
4910 goto done;
4912 error = get_author(&author, repo);
4913 if (error)
4914 return error;
4917 * unveil(2) traverses exec(2); if an editor is used we have
4918 * to apply unveil after the log message has been written.
4920 if (logmsg == NULL || strlen(logmsg) == 0)
4921 error = get_editor(&editor);
4922 else
4923 error = apply_unveil(got_repo_get_path(repo), 0,
4924 got_worktree_get_root_path(worktree));
4925 if (error)
4926 goto done;
4928 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
4929 if (error)
4930 goto done;
4932 cl_arg.editor = editor;
4933 cl_arg.cmdline_log = logmsg;
4934 cl_arg.worktree_path = got_worktree_get_root_path(worktree);
4935 cl_arg.branch_name = got_worktree_get_head_ref_name(worktree);
4936 if (!histedit_in_progress) {
4937 if (strncmp(cl_arg.branch_name, "refs/heads/", 11) != 0) {
4938 error = got_error(GOT_ERR_COMMIT_BRANCH);
4939 goto done;
4941 cl_arg.branch_name += 11;
4943 cl_arg.repo_path = got_repo_get_path(repo);
4944 error = got_worktree_commit(&id, worktree, &paths, author, NULL,
4945 collect_commit_logmsg, &cl_arg, print_status, NULL, repo);
4946 if (error) {
4947 if (error->code != GOT_ERR_COMMIT_MSG_EMPTY &&
4948 cl_arg.logmsg_path != NULL)
4949 preserve_logmsg = 1;
4950 goto done;
4953 error = got_object_id_str(&id_str, id);
4954 if (error)
4955 goto done;
4956 printf("Created commit %s\n", id_str);
4957 done:
4958 if (preserve_logmsg) {
4959 fprintf(stderr, "%s: log message preserved in %s\n",
4960 getprogname(), cl_arg.logmsg_path);
4961 } else if (cl_arg.logmsg_path && unlink(cl_arg.logmsg_path) == -1 &&
4962 error == NULL)
4963 error = got_error_from_errno2("unlink", cl_arg.logmsg_path);
4964 free(cl_arg.logmsg_path);
4965 if (repo)
4966 got_repo_close(repo);
4967 if (worktree)
4968 got_worktree_close(worktree);
4969 free(cwd);
4970 free(id_str);
4971 free(gitconfig_path);
4972 free(editor);
4973 free(author);
4974 return error;
4977 __dead static void
4978 usage_cherrypick(void)
4980 fprintf(stderr, "usage: %s cherrypick commit-id\n", getprogname());
4981 exit(1);
4984 static const struct got_error *
4985 cmd_cherrypick(int argc, char *argv[])
4987 const struct got_error *error = NULL;
4988 struct got_worktree *worktree = NULL;
4989 struct got_repository *repo = NULL;
4990 char *cwd = NULL, *commit_id_str = NULL;
4991 struct got_object_id *commit_id = NULL;
4992 struct got_commit_object *commit = NULL;
4993 struct got_object_qid *pid;
4994 struct got_reference *head_ref = NULL;
4995 int ch, did_something = 0;
4997 while ((ch = getopt(argc, argv, "")) != -1) {
4998 switch (ch) {
4999 default:
5000 usage_cherrypick();
5001 /* NOTREACHED */
5005 argc -= optind;
5006 argv += optind;
5008 #ifndef PROFILE
5009 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
5010 "unveil", NULL) == -1)
5011 err(1, "pledge");
5012 #endif
5013 if (argc != 1)
5014 usage_cherrypick();
5016 cwd = getcwd(NULL, 0);
5017 if (cwd == NULL) {
5018 error = got_error_from_errno("getcwd");
5019 goto done;
5021 error = got_worktree_open(&worktree, cwd);
5022 if (error)
5023 goto done;
5025 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
5026 NULL);
5027 if (error != NULL)
5028 goto done;
5030 error = apply_unveil(got_repo_get_path(repo), 0,
5031 got_worktree_get_root_path(worktree));
5032 if (error)
5033 goto done;
5035 error = got_repo_match_object_id_prefix(&commit_id, argv[0],
5036 GOT_OBJ_TYPE_COMMIT, repo);
5037 if (error != NULL) {
5038 struct got_reference *ref;
5039 if (error->code != GOT_ERR_BAD_OBJ_ID_STR)
5040 goto done;
5041 error = got_ref_open(&ref, repo, argv[0], 0);
5042 if (error != NULL)
5043 goto done;
5044 error = got_ref_resolve(&commit_id, repo, ref);
5045 got_ref_close(ref);
5046 if (error != NULL)
5047 goto done;
5049 error = got_object_id_str(&commit_id_str, commit_id);
5050 if (error)
5051 goto done;
5053 error = got_ref_open(&head_ref, repo,
5054 got_worktree_get_head_ref_name(worktree), 0);
5055 if (error != NULL)
5056 goto done;
5058 error = check_same_branch(commit_id, head_ref, NULL, repo);
5059 if (error) {
5060 if (error->code != GOT_ERR_ANCESTRY)
5061 goto done;
5062 error = NULL;
5063 } else {
5064 error = got_error(GOT_ERR_SAME_BRANCH);
5065 goto done;
5068 error = got_object_open_as_commit(&commit, repo, commit_id);
5069 if (error)
5070 goto done;
5071 pid = SIMPLEQ_FIRST(got_object_commit_get_parent_ids(commit));
5072 error = got_worktree_merge_files(worktree, pid ? pid->id : NULL,
5073 commit_id, repo, update_progress, &did_something, check_cancelled,
5074 NULL);
5075 if (error != NULL)
5076 goto done;
5078 if (did_something)
5079 printf("Merged commit %s\n", commit_id_str);
5080 done:
5081 if (commit)
5082 got_object_commit_close(commit);
5083 free(commit_id_str);
5084 if (head_ref)
5085 got_ref_close(head_ref);
5086 if (worktree)
5087 got_worktree_close(worktree);
5088 if (repo)
5089 got_repo_close(repo);
5090 return error;
5093 __dead static void
5094 usage_backout(void)
5096 fprintf(stderr, "usage: %s backout commit-id\n", getprogname());
5097 exit(1);
5100 static const struct got_error *
5101 cmd_backout(int argc, char *argv[])
5103 const struct got_error *error = NULL;
5104 struct got_worktree *worktree = NULL;
5105 struct got_repository *repo = NULL;
5106 char *cwd = NULL, *commit_id_str = NULL;
5107 struct got_object_id *commit_id = NULL;
5108 struct got_commit_object *commit = NULL;
5109 struct got_object_qid *pid;
5110 struct got_reference *head_ref = NULL;
5111 int ch, did_something = 0;
5113 while ((ch = getopt(argc, argv, "")) != -1) {
5114 switch (ch) {
5115 default:
5116 usage_backout();
5117 /* NOTREACHED */
5121 argc -= optind;
5122 argv += optind;
5124 #ifndef PROFILE
5125 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
5126 "unveil", NULL) == -1)
5127 err(1, "pledge");
5128 #endif
5129 if (argc != 1)
5130 usage_backout();
5132 cwd = getcwd(NULL, 0);
5133 if (cwd == NULL) {
5134 error = got_error_from_errno("getcwd");
5135 goto done;
5137 error = got_worktree_open(&worktree, cwd);
5138 if (error)
5139 goto done;
5141 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
5142 NULL);
5143 if (error != NULL)
5144 goto done;
5146 error = apply_unveil(got_repo_get_path(repo), 0,
5147 got_worktree_get_root_path(worktree));
5148 if (error)
5149 goto done;
5151 error = got_repo_match_object_id_prefix(&commit_id, argv[0],
5152 GOT_OBJ_TYPE_COMMIT, repo);
5153 if (error != NULL) {
5154 struct got_reference *ref;
5155 if (error->code != GOT_ERR_BAD_OBJ_ID_STR)
5156 goto done;
5157 error = got_ref_open(&ref, repo, argv[0], 0);
5158 if (error != NULL)
5159 goto done;
5160 error = got_ref_resolve(&commit_id, repo, ref);
5161 got_ref_close(ref);
5162 if (error != NULL)
5163 goto done;
5165 error = got_object_id_str(&commit_id_str, commit_id);
5166 if (error)
5167 goto done;
5169 error = got_ref_open(&head_ref, repo,
5170 got_worktree_get_head_ref_name(worktree), 0);
5171 if (error != NULL)
5172 goto done;
5174 error = check_same_branch(commit_id, head_ref, NULL, repo);
5175 if (error)
5176 goto done;
5178 error = got_object_open_as_commit(&commit, repo, commit_id);
5179 if (error)
5180 goto done;
5181 pid = SIMPLEQ_FIRST(got_object_commit_get_parent_ids(commit));
5182 if (pid == NULL) {
5183 error = got_error(GOT_ERR_ROOT_COMMIT);
5184 goto done;
5187 error = got_worktree_merge_files(worktree, commit_id, pid->id, repo,
5188 update_progress, &did_something, check_cancelled, NULL);
5189 if (error != NULL)
5190 goto done;
5192 if (did_something)
5193 printf("Backed out commit %s\n", commit_id_str);
5194 done:
5195 if (commit)
5196 got_object_commit_close(commit);
5197 free(commit_id_str);
5198 if (head_ref)
5199 got_ref_close(head_ref);
5200 if (worktree)
5201 got_worktree_close(worktree);
5202 if (repo)
5203 got_repo_close(repo);
5204 return error;
5207 __dead static void
5208 usage_rebase(void)
5210 fprintf(stderr, "usage: %s rebase [-a] | [-c] | branch\n",
5211 getprogname());
5212 exit(1);
5215 void
5216 trim_logmsg(char *logmsg, int limit)
5218 char *nl;
5219 size_t len;
5221 len = strlen(logmsg);
5222 if (len > limit)
5223 len = limit;
5224 logmsg[len] = '\0';
5225 nl = strchr(logmsg, '\n');
5226 if (nl)
5227 *nl = '\0';
5230 static const struct got_error *
5231 get_short_logmsg(char **logmsg, int limit, struct got_commit_object *commit)
5233 const struct got_error *err;
5234 char *logmsg0 = NULL;
5235 const char *s;
5237 err = got_object_commit_get_logmsg(&logmsg0, commit);
5238 if (err)
5239 return err;
5241 s = logmsg0;
5242 while (isspace((unsigned char)s[0]))
5243 s++;
5245 *logmsg = strdup(s);
5246 if (*logmsg == NULL) {
5247 err = got_error_from_errno("strdup");
5248 goto done;
5251 trim_logmsg(*logmsg, limit);
5252 done:
5253 free(logmsg0);
5254 return err;
5257 static const struct got_error *
5258 show_rebase_merge_conflict(struct got_object_id *id, struct got_repository *repo)
5260 const struct got_error *err;
5261 struct got_commit_object *commit = NULL;
5262 char *id_str = NULL, *logmsg = NULL;
5264 err = got_object_open_as_commit(&commit, repo, id);
5265 if (err)
5266 return err;
5268 err = got_object_id_str(&id_str, id);
5269 if (err)
5270 goto done;
5272 id_str[12] = '\0';
5274 err = get_short_logmsg(&logmsg, 42, commit);
5275 if (err)
5276 goto done;
5278 printf("%s -> merge conflict: %s\n", id_str, logmsg);
5279 done:
5280 free(id_str);
5281 got_object_commit_close(commit);
5282 free(logmsg);
5283 return err;
5286 static const struct got_error *
5287 show_rebase_progress(struct got_commit_object *commit,
5288 struct got_object_id *old_id, struct got_object_id *new_id)
5290 const struct got_error *err;
5291 char *old_id_str = NULL, *new_id_str = NULL, *logmsg = NULL;
5293 err = got_object_id_str(&old_id_str, old_id);
5294 if (err)
5295 goto done;
5297 if (new_id) {
5298 err = got_object_id_str(&new_id_str, new_id);
5299 if (err)
5300 goto done;
5303 old_id_str[12] = '\0';
5304 if (new_id_str)
5305 new_id_str[12] = '\0';
5307 err = get_short_logmsg(&logmsg, 42, commit);
5308 if (err)
5309 goto done;
5311 printf("%s -> %s: %s\n", old_id_str,
5312 new_id_str ? new_id_str : "no-op change", logmsg);
5313 done:
5314 free(old_id_str);
5315 free(new_id_str);
5316 free(logmsg);
5317 return err;
5320 static const struct got_error *
5321 rebase_progress(void *arg, unsigned char status, const char *path)
5323 unsigned char *rebase_status = arg;
5325 while (path[0] == '/')
5326 path++;
5327 printf("%c %s\n", status, path);
5329 if (*rebase_status == GOT_STATUS_CONFLICT)
5330 return NULL;
5331 if (status == GOT_STATUS_CONFLICT || status == GOT_STATUS_MERGE)
5332 *rebase_status = status;
5333 return NULL;
5336 static const struct got_error *
5337 rebase_complete(struct got_worktree *worktree, struct got_fileindex *fileindex,
5338 struct got_reference *branch, struct got_reference *new_base_branch,
5339 struct got_reference *tmp_branch, struct got_repository *repo)
5341 printf("Switching work tree to %s\n", got_ref_get_name(branch));
5342 return got_worktree_rebase_complete(worktree, fileindex,
5343 new_base_branch, tmp_branch, branch, repo);
5346 static const struct got_error *
5347 rebase_commit(struct got_pathlist_head *merged_paths,
5348 struct got_worktree *worktree, struct got_fileindex *fileindex,
5349 struct got_reference *tmp_branch,
5350 struct got_object_id *commit_id, struct got_repository *repo)
5352 const struct got_error *error;
5353 struct got_commit_object *commit;
5354 struct got_object_id *new_commit_id;
5356 error = got_object_open_as_commit(&commit, repo, commit_id);
5357 if (error)
5358 return error;
5360 error = got_worktree_rebase_commit(&new_commit_id, merged_paths,
5361 worktree, fileindex, tmp_branch, commit, commit_id, repo);
5362 if (error) {
5363 if (error->code != GOT_ERR_COMMIT_NO_CHANGES)
5364 goto done;
5365 error = show_rebase_progress(commit, commit_id, NULL);
5366 } else {
5367 error = show_rebase_progress(commit, commit_id, new_commit_id);
5368 free(new_commit_id);
5370 done:
5371 got_object_commit_close(commit);
5372 return error;
5375 struct check_path_prefix_arg {
5376 const char *path_prefix;
5377 size_t len;
5378 int errcode;
5381 static const struct got_error *
5382 check_path_prefix_in_diff(void *arg, struct got_blob_object *blob1,
5383 struct got_blob_object *blob2, struct got_object_id *id1,
5384 struct got_object_id *id2, const char *path1, const char *path2,
5385 mode_t mode1, mode_t mode2, struct got_repository *repo)
5387 struct check_path_prefix_arg *a = arg;
5389 if ((path1 && !got_path_is_child(path1, a->path_prefix, a->len)) ||
5390 (path2 && !got_path_is_child(path2, a->path_prefix, a->len)))
5391 return got_error(a->errcode);
5393 return NULL;
5396 static const struct got_error *
5397 check_path_prefix(struct got_object_id *parent_id,
5398 struct got_object_id *commit_id, const char *path_prefix,
5399 int errcode, struct got_repository *repo)
5401 const struct got_error *err;
5402 struct got_tree_object *tree1 = NULL, *tree2 = NULL;
5403 struct got_commit_object *commit = NULL, *parent_commit = NULL;
5404 struct check_path_prefix_arg cpp_arg;
5406 if (got_path_is_root_dir(path_prefix))
5407 return NULL;
5409 err = got_object_open_as_commit(&commit, repo, commit_id);
5410 if (err)
5411 goto done;
5413 err = got_object_open_as_commit(&parent_commit, repo, parent_id);
5414 if (err)
5415 goto done;
5417 err = got_object_open_as_tree(&tree1, repo,
5418 got_object_commit_get_tree_id(parent_commit));
5419 if (err)
5420 goto done;
5422 err = got_object_open_as_tree(&tree2, repo,
5423 got_object_commit_get_tree_id(commit));
5424 if (err)
5425 goto done;
5427 cpp_arg.path_prefix = path_prefix;
5428 while (cpp_arg.path_prefix[0] == '/')
5429 cpp_arg.path_prefix++;
5430 cpp_arg.len = strlen(cpp_arg.path_prefix);
5431 cpp_arg.errcode = errcode;
5432 err = got_diff_tree(tree1, tree2, "", "", repo,
5433 check_path_prefix_in_diff, &cpp_arg, 0);
5434 done:
5435 if (tree1)
5436 got_object_tree_close(tree1);
5437 if (tree2)
5438 got_object_tree_close(tree2);
5439 if (commit)
5440 got_object_commit_close(commit);
5441 if (parent_commit)
5442 got_object_commit_close(parent_commit);
5443 return err;
5446 static const struct got_error *
5447 collect_commits(struct got_object_id_queue *commits,
5448 struct got_object_id *initial_commit_id,
5449 struct got_object_id *iter_start_id, struct got_object_id *iter_stop_id,
5450 const char *path_prefix, int path_prefix_errcode,
5451 struct got_repository *repo)
5453 const struct got_error *err = NULL;
5454 struct got_commit_graph *graph = NULL;
5455 struct got_object_id *parent_id = NULL;
5456 struct got_object_qid *qid;
5457 struct got_object_id *commit_id = initial_commit_id;
5459 err = got_commit_graph_open(&graph, "/", 1);
5460 if (err)
5461 return err;
5463 err = got_commit_graph_iter_start(graph, iter_start_id, repo,
5464 check_cancelled, NULL);
5465 if (err)
5466 goto done;
5467 while (got_object_id_cmp(commit_id, iter_stop_id) != 0) {
5468 err = got_commit_graph_iter_next(&parent_id, graph, repo,
5469 check_cancelled, NULL);
5470 if (err) {
5471 if (err->code == GOT_ERR_ITER_COMPLETED) {
5472 err = got_error_msg(GOT_ERR_ANCESTRY,
5473 "ran out of commits to rebase before "
5474 "youngest common ancestor commit has "
5475 "been reached?!?");
5477 goto done;
5478 } else {
5479 err = check_path_prefix(parent_id, commit_id,
5480 path_prefix, path_prefix_errcode, repo);
5481 if (err)
5482 goto done;
5484 err = got_object_qid_alloc(&qid, commit_id);
5485 if (err)
5486 goto done;
5487 SIMPLEQ_INSERT_HEAD(commits, qid, entry);
5488 commit_id = parent_id;
5491 done:
5492 got_commit_graph_close(graph);
5493 return err;
5496 static const struct got_error *
5497 cmd_rebase(int argc, char *argv[])
5499 const struct got_error *error = NULL;
5500 struct got_worktree *worktree = NULL;
5501 struct got_repository *repo = NULL;
5502 struct got_fileindex *fileindex = NULL;
5503 char *cwd = NULL;
5504 struct got_reference *branch = NULL;
5505 struct got_reference *new_base_branch = NULL, *tmp_branch = NULL;
5506 struct got_object_id *commit_id = NULL, *parent_id = NULL;
5507 struct got_object_id *resume_commit_id = NULL;
5508 struct got_object_id *branch_head_commit_id = NULL, *yca_id = NULL;
5509 struct got_commit_object *commit = NULL;
5510 int ch, rebase_in_progress = 0, abort_rebase = 0, continue_rebase = 0;
5511 int histedit_in_progress = 0;
5512 unsigned char rebase_status = GOT_STATUS_NO_CHANGE;
5513 struct got_object_id_queue commits;
5514 struct got_pathlist_head merged_paths;
5515 const struct got_object_id_queue *parent_ids;
5516 struct got_object_qid *qid, *pid;
5518 SIMPLEQ_INIT(&commits);
5519 TAILQ_INIT(&merged_paths);
5521 while ((ch = getopt(argc, argv, "ac")) != -1) {
5522 switch (ch) {
5523 case 'a':
5524 abort_rebase = 1;
5525 break;
5526 case 'c':
5527 continue_rebase = 1;
5528 break;
5529 default:
5530 usage_rebase();
5531 /* NOTREACHED */
5535 argc -= optind;
5536 argv += optind;
5538 #ifndef PROFILE
5539 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
5540 "unveil", NULL) == -1)
5541 err(1, "pledge");
5542 #endif
5543 if (abort_rebase && continue_rebase)
5544 usage_rebase();
5545 else if (abort_rebase || continue_rebase) {
5546 if (argc != 0)
5547 usage_rebase();
5548 } else if (argc != 1)
5549 usage_rebase();
5551 cwd = getcwd(NULL, 0);
5552 if (cwd == NULL) {
5553 error = got_error_from_errno("getcwd");
5554 goto done;
5556 error = got_worktree_open(&worktree, cwd);
5557 if (error)
5558 goto done;
5560 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
5561 NULL);
5562 if (error != NULL)
5563 goto done;
5565 error = apply_unveil(got_repo_get_path(repo), 0,
5566 got_worktree_get_root_path(worktree));
5567 if (error)
5568 goto done;
5570 error = got_worktree_histedit_in_progress(&histedit_in_progress,
5571 worktree);
5572 if (error)
5573 goto done;
5574 if (histedit_in_progress) {
5575 error = got_error(GOT_ERR_HISTEDIT_BUSY);
5576 goto done;
5579 error = got_worktree_rebase_in_progress(&rebase_in_progress, worktree);
5580 if (error)
5581 goto done;
5583 if (abort_rebase) {
5584 int did_something;
5585 if (!rebase_in_progress) {
5586 error = got_error(GOT_ERR_NOT_REBASING);
5587 goto done;
5589 error = got_worktree_rebase_continue(&resume_commit_id,
5590 &new_base_branch, &tmp_branch, &branch, &fileindex,
5591 worktree, repo);
5592 if (error)
5593 goto done;
5594 printf("Switching work tree to %s\n",
5595 got_ref_get_symref_target(new_base_branch));
5596 error = got_worktree_rebase_abort(worktree, fileindex, repo,
5597 new_base_branch, update_progress, &did_something);
5598 if (error)
5599 goto done;
5600 printf("Rebase of %s aborted\n", got_ref_get_name(branch));
5601 goto done; /* nothing else to do */
5604 if (continue_rebase) {
5605 if (!rebase_in_progress) {
5606 error = got_error(GOT_ERR_NOT_REBASING);
5607 goto done;
5609 error = got_worktree_rebase_continue(&resume_commit_id,
5610 &new_base_branch, &tmp_branch, &branch, &fileindex,
5611 worktree, repo);
5612 if (error)
5613 goto done;
5615 error = rebase_commit(NULL, worktree, fileindex, tmp_branch,
5616 resume_commit_id, repo);
5617 if (error)
5618 goto done;
5620 yca_id = got_object_id_dup(resume_commit_id);
5621 if (yca_id == NULL) {
5622 error = got_error_from_errno("got_object_id_dup");
5623 goto done;
5625 } else {
5626 error = got_ref_open(&branch, repo, argv[0], 0);
5627 if (error != NULL)
5628 goto done;
5631 error = got_ref_resolve(&branch_head_commit_id, repo, branch);
5632 if (error)
5633 goto done;
5635 if (!continue_rebase) {
5636 struct got_object_id *base_commit_id;
5638 base_commit_id = got_worktree_get_base_commit_id(worktree);
5639 error = got_commit_graph_find_youngest_common_ancestor(&yca_id,
5640 base_commit_id, branch_head_commit_id, repo,
5641 check_cancelled, NULL);
5642 if (error)
5643 goto done;
5644 if (yca_id == NULL) {
5645 error = got_error_msg(GOT_ERR_ANCESTRY,
5646 "specified branch shares no common ancestry "
5647 "with work tree's branch");
5648 goto done;
5651 error = check_same_branch(base_commit_id, branch, yca_id, repo);
5652 if (error) {
5653 if (error->code != GOT_ERR_ANCESTRY)
5654 goto done;
5655 error = NULL;
5656 } else {
5657 error = got_error_msg(GOT_ERR_SAME_BRANCH,
5658 "specified branch resolves to a commit which "
5659 "is already contained in work tree's branch");
5660 goto done;
5662 error = got_worktree_rebase_prepare(&new_base_branch,
5663 &tmp_branch, &fileindex, worktree, branch, repo);
5664 if (error)
5665 goto done;
5668 commit_id = branch_head_commit_id;
5669 error = got_object_open_as_commit(&commit, repo, commit_id);
5670 if (error)
5671 goto done;
5673 parent_ids = got_object_commit_get_parent_ids(commit);
5674 pid = SIMPLEQ_FIRST(parent_ids);
5675 if (pid == NULL) {
5676 if (!continue_rebase) {
5677 int did_something;
5678 error = got_worktree_rebase_abort(worktree, fileindex,
5679 repo, new_base_branch, update_progress,
5680 &did_something);
5681 if (error)
5682 goto done;
5683 printf("Rebase of %s aborted\n",
5684 got_ref_get_name(branch));
5686 error = got_error(GOT_ERR_EMPTY_REBASE);
5687 goto done;
5689 error = collect_commits(&commits, commit_id, pid->id,
5690 yca_id, got_worktree_get_path_prefix(worktree),
5691 GOT_ERR_REBASE_PATH, repo);
5692 got_object_commit_close(commit);
5693 commit = NULL;
5694 if (error)
5695 goto done;
5697 if (SIMPLEQ_EMPTY(&commits)) {
5698 if (continue_rebase) {
5699 error = rebase_complete(worktree, fileindex,
5700 branch, new_base_branch, tmp_branch, repo);
5701 goto done;
5702 } else {
5703 /* Fast-forward the reference of the branch. */
5704 struct got_object_id *new_head_commit_id;
5705 char *id_str;
5706 error = got_ref_resolve(&new_head_commit_id, repo,
5707 new_base_branch);
5708 if (error)
5709 goto done;
5710 error = got_object_id_str(&id_str, new_head_commit_id);
5711 printf("Forwarding %s to commit %s\n",
5712 got_ref_get_name(branch), id_str);
5713 free(id_str);
5714 error = got_ref_change_ref(branch,
5715 new_head_commit_id);
5716 if (error)
5717 goto done;
5721 pid = NULL;
5722 SIMPLEQ_FOREACH(qid, &commits, entry) {
5723 commit_id = qid->id;
5724 parent_id = pid ? pid->id : yca_id;
5725 pid = qid;
5727 error = got_worktree_rebase_merge_files(&merged_paths,
5728 worktree, fileindex, parent_id, commit_id, repo,
5729 rebase_progress, &rebase_status, check_cancelled, NULL);
5730 if (error)
5731 goto done;
5733 if (rebase_status == GOT_STATUS_CONFLICT) {
5734 error = show_rebase_merge_conflict(qid->id, repo);
5735 if (error)
5736 goto done;
5737 got_worktree_rebase_pathlist_free(&merged_paths);
5738 break;
5741 error = rebase_commit(&merged_paths, worktree, fileindex,
5742 tmp_branch, commit_id, repo);
5743 got_worktree_rebase_pathlist_free(&merged_paths);
5744 if (error)
5745 goto done;
5748 if (rebase_status == GOT_STATUS_CONFLICT) {
5749 error = got_worktree_rebase_postpone(worktree, fileindex);
5750 if (error)
5751 goto done;
5752 error = got_error_msg(GOT_ERR_CONFLICTS,
5753 "conflicts must be resolved before rebasing can continue");
5754 } else
5755 error = rebase_complete(worktree, fileindex, branch,
5756 new_base_branch, tmp_branch, repo);
5757 done:
5758 got_object_id_queue_free(&commits);
5759 free(branch_head_commit_id);
5760 free(resume_commit_id);
5761 free(yca_id);
5762 if (commit)
5763 got_object_commit_close(commit);
5764 if (branch)
5765 got_ref_close(branch);
5766 if (new_base_branch)
5767 got_ref_close(new_base_branch);
5768 if (tmp_branch)
5769 got_ref_close(tmp_branch);
5770 if (worktree)
5771 got_worktree_close(worktree);
5772 if (repo)
5773 got_repo_close(repo);
5774 return error;
5777 __dead static void
5778 usage_histedit(void)
5780 fprintf(stderr, "usage: %s histedit [-a] [-c] [-F histedit-script] [-m]\n",
5781 getprogname());
5782 exit(1);
5785 #define GOT_HISTEDIT_PICK 'p'
5786 #define GOT_HISTEDIT_EDIT 'e'
5787 #define GOT_HISTEDIT_FOLD 'f'
5788 #define GOT_HISTEDIT_DROP 'd'
5789 #define GOT_HISTEDIT_MESG 'm'
5791 static struct got_histedit_cmd {
5792 unsigned char code;
5793 const char *name;
5794 const char *desc;
5795 } got_histedit_cmds[] = {
5796 { GOT_HISTEDIT_PICK, "pick", "use commit" },
5797 { GOT_HISTEDIT_EDIT, "edit", "use commit but stop for amending" },
5798 { GOT_HISTEDIT_FOLD, "fold", "combine with next commit that will "
5799 "be used" },
5800 { GOT_HISTEDIT_DROP, "drop", "remove commit from history" },
5801 { GOT_HISTEDIT_MESG, "mesg",
5802 "single-line log message for commit above (open editor if empty)" },
5805 struct got_histedit_list_entry {
5806 TAILQ_ENTRY(got_histedit_list_entry) entry;
5807 struct got_object_id *commit_id;
5808 const struct got_histedit_cmd *cmd;
5809 char *logmsg;
5811 TAILQ_HEAD(got_histedit_list, got_histedit_list_entry);
5813 static const struct got_error *
5814 histedit_write_commit(struct got_object_id *commit_id, const char *cmdname,
5815 FILE *f, struct got_repository *repo)
5817 const struct got_error *err = NULL;
5818 char *logmsg = NULL, *id_str = NULL;
5819 struct got_commit_object *commit = NULL;
5820 int n;
5822 err = got_object_open_as_commit(&commit, repo, commit_id);
5823 if (err)
5824 goto done;
5826 err = get_short_logmsg(&logmsg, 34, commit);
5827 if (err)
5828 goto done;
5830 err = got_object_id_str(&id_str, commit_id);
5831 if (err)
5832 goto done;
5834 n = fprintf(f, "%s %s %s\n", cmdname, id_str, logmsg);
5835 if (n < 0)
5836 err = got_ferror(f, GOT_ERR_IO);
5837 done:
5838 if (commit)
5839 got_object_commit_close(commit);
5840 free(id_str);
5841 free(logmsg);
5842 return err;
5845 static const struct got_error *
5846 histedit_write_commit_list(struct got_object_id_queue *commits,
5847 FILE *f, int edit_logmsg_only, struct got_repository *repo)
5849 const struct got_error *err = NULL;
5850 struct got_object_qid *qid;
5852 if (SIMPLEQ_EMPTY(commits))
5853 return got_error(GOT_ERR_EMPTY_HISTEDIT);
5855 SIMPLEQ_FOREACH(qid, commits, entry) {
5856 err = histedit_write_commit(qid->id, got_histedit_cmds[0].name,
5857 f, repo);
5858 if (err)
5859 break;
5860 if (edit_logmsg_only) {
5861 int n = fprintf(f, "%c\n", GOT_HISTEDIT_MESG);
5862 if (n < 0) {
5863 err = got_ferror(f, GOT_ERR_IO);
5864 break;
5869 return err;
5872 static const struct got_error *
5873 write_cmd_list(FILE *f, const char *branch_name,
5874 struct got_object_id_queue *commits)
5876 const struct got_error *err = NULL;
5877 int n, i;
5878 char *id_str;
5879 struct got_object_qid *qid;
5881 qid = SIMPLEQ_FIRST(commits);
5882 err = got_object_id_str(&id_str, qid->id);
5883 if (err)
5884 return err;
5886 n = fprintf(f,
5887 "# Editing the history of branch '%s' starting at\n"
5888 "# commit %s\n"
5889 "# Commits will be processed in order from top to "
5890 "bottom of this file.\n", branch_name, id_str);
5891 if (n < 0) {
5892 err = got_ferror(f, GOT_ERR_IO);
5893 goto done;
5896 n = fprintf(f, "# Available histedit commands:\n");
5897 if (n < 0) {
5898 err = got_ferror(f, GOT_ERR_IO);
5899 goto done;
5902 for (i = 0; i < nitems(got_histedit_cmds); i++) {
5903 struct got_histedit_cmd *cmd = &got_histedit_cmds[i];
5904 n = fprintf(f, "# %s (%c): %s\n", cmd->name, cmd->code,
5905 cmd->desc);
5906 if (n < 0) {
5907 err = got_ferror(f, GOT_ERR_IO);
5908 break;
5911 done:
5912 free(id_str);
5913 return err;
5916 static const struct got_error *
5917 histedit_syntax_error(int lineno)
5919 static char msg[42];
5920 int ret;
5922 ret = snprintf(msg, sizeof(msg), "histedit syntax error on line %d",
5923 lineno);
5924 if (ret == -1 || ret >= sizeof(msg))
5925 return got_error(GOT_ERR_HISTEDIT_SYNTAX);
5927 return got_error_msg(GOT_ERR_HISTEDIT_SYNTAX, msg);
5930 static const struct got_error *
5931 append_folded_commit_msg(char **new_msg, struct got_histedit_list_entry *hle,
5932 char *logmsg, struct got_repository *repo)
5934 const struct got_error *err;
5935 struct got_commit_object *folded_commit = NULL;
5936 char *id_str, *folded_logmsg = NULL;
5938 err = got_object_id_str(&id_str, hle->commit_id);
5939 if (err)
5940 return err;
5942 err = got_object_open_as_commit(&folded_commit, repo, hle->commit_id);
5943 if (err)
5944 goto done;
5946 err = got_object_commit_get_logmsg(&folded_logmsg, folded_commit);
5947 if (err)
5948 goto done;
5949 if (asprintf(new_msg, "%s%s# log message of folded commit %s: %s",
5950 logmsg ? logmsg : "", logmsg ? "\n" : "", id_str,
5951 folded_logmsg) == -1) {
5952 err = got_error_from_errno("asprintf");
5954 done:
5955 if (folded_commit)
5956 got_object_commit_close(folded_commit);
5957 free(id_str);
5958 free(folded_logmsg);
5959 return err;
5962 static struct got_histedit_list_entry *
5963 get_folded_commits(struct got_histedit_list_entry *hle)
5965 struct got_histedit_list_entry *prev, *folded = NULL;
5967 prev = TAILQ_PREV(hle, got_histedit_list, entry);
5968 while (prev && (prev->cmd->code == GOT_HISTEDIT_FOLD ||
5969 prev->cmd->code == GOT_HISTEDIT_DROP)) {
5970 if (prev->cmd->code == GOT_HISTEDIT_FOLD)
5971 folded = prev;
5972 prev = TAILQ_PREV(prev, got_histedit_list, entry);
5975 return folded;
5978 static const struct got_error *
5979 histedit_edit_logmsg(struct got_histedit_list_entry *hle,
5980 struct got_repository *repo)
5982 char *logmsg_path = NULL, *id_str = NULL, *orig_logmsg = NULL;
5983 char *logmsg = NULL, *new_msg = NULL, *editor = NULL;
5984 const struct got_error *err = NULL;
5985 struct got_commit_object *commit = NULL;
5986 int fd;
5987 struct got_histedit_list_entry *folded = NULL;
5989 err = got_object_open_as_commit(&commit, repo, hle->commit_id);
5990 if (err)
5991 return err;
5993 folded = get_folded_commits(hle);
5994 if (folded) {
5995 while (folded != hle) {
5996 if (folded->cmd->code == GOT_HISTEDIT_DROP) {
5997 folded = TAILQ_NEXT(folded, entry);
5998 continue;
6000 err = append_folded_commit_msg(&new_msg, folded,
6001 logmsg, repo);
6002 if (err)
6003 goto done;
6004 free(logmsg);
6005 logmsg = new_msg;
6006 folded = TAILQ_NEXT(folded, entry);
6010 err = got_object_id_str(&id_str, hle->commit_id);
6011 if (err)
6012 goto done;
6013 err = got_object_commit_get_logmsg(&orig_logmsg, commit);
6014 if (err)
6015 goto done;
6016 if (asprintf(&new_msg,
6017 "%s\n# original log message of commit %s: %s",
6018 logmsg ? logmsg : "", id_str, orig_logmsg) == -1) {
6019 err = got_error_from_errno("asprintf");
6020 goto done;
6022 free(logmsg);
6023 logmsg = new_msg;
6025 err = got_object_id_str(&id_str, hle->commit_id);
6026 if (err)
6027 goto done;
6029 err = got_opentemp_named_fd(&logmsg_path, &fd,
6030 GOT_TMPDIR_STR "/got-logmsg");
6031 if (err)
6032 goto done;
6034 dprintf(fd, logmsg);
6035 close(fd);
6037 err = get_editor(&editor);
6038 if (err)
6039 goto done;
6041 err = edit_logmsg(&hle->logmsg, editor, logmsg_path, logmsg);
6042 if (err) {
6043 if (err->code != GOT_ERR_COMMIT_MSG_EMPTY)
6044 goto done;
6045 err = got_object_commit_get_logmsg(&hle->logmsg, commit);
6047 done:
6048 if (logmsg_path && unlink(logmsg_path) != 0 && err == NULL)
6049 err = got_error_from_errno2("unlink", logmsg_path);
6050 free(logmsg_path);
6051 free(logmsg);
6052 free(orig_logmsg);
6053 free(editor);
6054 if (commit)
6055 got_object_commit_close(commit);
6056 return err;
6059 static const struct got_error *
6060 histedit_parse_list(struct got_histedit_list *histedit_cmds,
6061 FILE *f, struct got_repository *repo)
6063 const struct got_error *err = NULL;
6064 char *line = NULL, *p, *end;
6065 size_t size;
6066 ssize_t len;
6067 int lineno = 0, i;
6068 const struct got_histedit_cmd *cmd;
6069 struct got_object_id *commit_id = NULL;
6070 struct got_histedit_list_entry *hle = NULL;
6072 for (;;) {
6073 len = getline(&line, &size, f);
6074 if (len == -1) {
6075 const struct got_error *getline_err;
6076 if (feof(f))
6077 break;
6078 getline_err = got_error_from_errno("getline");
6079 err = got_ferror(f, getline_err->code);
6080 break;
6082 lineno++;
6083 p = line;
6084 while (isspace((unsigned char)p[0]))
6085 p++;
6086 if (p[0] == '#' || p[0] == '\0') {
6087 free(line);
6088 line = NULL;
6089 continue;
6091 cmd = NULL;
6092 for (i = 0; i < nitems(got_histedit_cmds); i++) {
6093 cmd = &got_histedit_cmds[i];
6094 if (strncmp(cmd->name, p, strlen(cmd->name)) == 0 &&
6095 isspace((unsigned char)p[strlen(cmd->name)])) {
6096 p += strlen(cmd->name);
6097 break;
6099 if (p[0] == cmd->code && isspace((unsigned char)p[1])) {
6100 p++;
6101 break;
6104 if (i == nitems(got_histedit_cmds)) {
6105 err = histedit_syntax_error(lineno);
6106 break;
6108 while (isspace((unsigned char)p[0]))
6109 p++;
6110 if (cmd->code == GOT_HISTEDIT_MESG) {
6111 if (hle == NULL || hle->logmsg != NULL) {
6112 err = got_error(GOT_ERR_HISTEDIT_CMD);
6113 break;
6115 if (p[0] == '\0') {
6116 err = histedit_edit_logmsg(hle, repo);
6117 if (err)
6118 break;
6119 } else {
6120 hle->logmsg = strdup(p);
6121 if (hle->logmsg == NULL) {
6122 err = got_error_from_errno("strdup");
6123 break;
6126 free(line);
6127 line = NULL;
6128 continue;
6129 } else {
6130 end = p;
6131 while (end[0] && !isspace((unsigned char)end[0]))
6132 end++;
6133 *end = '\0';
6135 err = got_object_resolve_id_str(&commit_id, repo, p);
6136 if (err) {
6137 /* override error code */
6138 err = histedit_syntax_error(lineno);
6139 break;
6142 hle = malloc(sizeof(*hle));
6143 if (hle == NULL) {
6144 err = got_error_from_errno("malloc");
6145 break;
6147 hle->cmd = cmd;
6148 hle->commit_id = commit_id;
6149 hle->logmsg = NULL;
6150 commit_id = NULL;
6151 free(line);
6152 line = NULL;
6153 TAILQ_INSERT_TAIL(histedit_cmds, hle, entry);
6156 free(line);
6157 free(commit_id);
6158 return err;
6161 static const struct got_error *
6162 histedit_check_script(struct got_histedit_list *histedit_cmds,
6163 struct got_object_id_queue *commits, struct got_repository *repo)
6165 const struct got_error *err = NULL;
6166 struct got_object_qid *qid;
6167 struct got_histedit_list_entry *hle;
6168 static char msg[92];
6169 char *id_str;
6171 if (TAILQ_EMPTY(histedit_cmds))
6172 return got_error_msg(GOT_ERR_EMPTY_HISTEDIT,
6173 "histedit script contains no commands");
6174 if (SIMPLEQ_EMPTY(commits))
6175 return got_error(GOT_ERR_EMPTY_HISTEDIT);
6177 TAILQ_FOREACH(hle, histedit_cmds, entry) {
6178 struct got_histedit_list_entry *hle2;
6179 TAILQ_FOREACH(hle2, histedit_cmds, entry) {
6180 if (hle == hle2)
6181 continue;
6182 if (got_object_id_cmp(hle->commit_id,
6183 hle2->commit_id) != 0)
6184 continue;
6185 err = got_object_id_str(&id_str, hle->commit_id);
6186 if (err)
6187 return err;
6188 snprintf(msg, sizeof(msg), "commit %s is listed "
6189 "more than once in histedit script", id_str);
6190 free(id_str);
6191 return got_error_msg(GOT_ERR_HISTEDIT_CMD, msg);
6195 SIMPLEQ_FOREACH(qid, commits, entry) {
6196 TAILQ_FOREACH(hle, histedit_cmds, entry) {
6197 if (got_object_id_cmp(qid->id, hle->commit_id) == 0)
6198 break;
6200 if (hle == NULL) {
6201 err = got_object_id_str(&id_str, qid->id);
6202 if (err)
6203 return err;
6204 snprintf(msg, sizeof(msg),
6205 "commit %s missing from histedit script", id_str);
6206 free(id_str);
6207 return got_error_msg(GOT_ERR_HISTEDIT_CMD, msg);
6211 hle = TAILQ_LAST(histedit_cmds, got_histedit_list);
6212 if (hle && hle->cmd->code == GOT_HISTEDIT_FOLD)
6213 return got_error_msg(GOT_ERR_HISTEDIT_CMD,
6214 "last commit in histedit script cannot be folded");
6216 return NULL;
6219 static const struct got_error *
6220 histedit_run_editor(struct got_histedit_list *histedit_cmds,
6221 const char *path, struct got_object_id_queue *commits,
6222 struct got_repository *repo)
6224 const struct got_error *err = NULL;
6225 char *editor;
6226 FILE *f = NULL;
6228 err = get_editor(&editor);
6229 if (err)
6230 return err;
6232 if (spawn_editor(editor, path) == -1) {
6233 err = got_error_from_errno("failed spawning editor");
6234 goto done;
6237 f = fopen(path, "r");
6238 if (f == NULL) {
6239 err = got_error_from_errno("fopen");
6240 goto done;
6242 err = histedit_parse_list(histedit_cmds, f, repo);
6243 if (err)
6244 goto done;
6246 err = histedit_check_script(histedit_cmds, commits, repo);
6247 done:
6248 if (f && fclose(f) != 0 && err == NULL)
6249 err = got_error_from_errno("fclose");
6250 free(editor);
6251 return err;
6254 static const struct got_error *
6255 histedit_edit_list_retry(struct got_histedit_list *, const struct got_error *,
6256 struct got_object_id_queue *, const char *, const char *,
6257 struct got_repository *);
6259 static const struct got_error *
6260 histedit_edit_script(struct got_histedit_list *histedit_cmds,
6261 struct got_object_id_queue *commits, const char *branch_name,
6262 int edit_logmsg_only, struct got_repository *repo)
6264 const struct got_error *err;
6265 FILE *f = NULL;
6266 char *path = NULL;
6268 err = got_opentemp_named(&path, &f, "got-histedit");
6269 if (err)
6270 return err;
6272 err = write_cmd_list(f, branch_name, commits);
6273 if (err)
6274 goto done;
6276 err = histedit_write_commit_list(commits, f, edit_logmsg_only, repo);
6277 if (err)
6278 goto done;
6280 if (edit_logmsg_only) {
6281 rewind(f);
6282 err = histedit_parse_list(histedit_cmds, f, repo);
6283 } else {
6284 if (fclose(f) != 0) {
6285 err = got_error_from_errno("fclose");
6286 goto done;
6288 f = NULL;
6289 err = histedit_run_editor(histedit_cmds, path, commits, repo);
6290 if (err) {
6291 if (err->code != GOT_ERR_HISTEDIT_SYNTAX &&
6292 err->code != GOT_ERR_HISTEDIT_CMD)
6293 goto done;
6294 err = histedit_edit_list_retry(histedit_cmds, err,
6295 commits, path, branch_name, repo);
6298 done:
6299 if (f && fclose(f) != 0 && err == NULL)
6300 err = got_error_from_errno("fclose");
6301 if (path && unlink(path) != 0 && err == NULL)
6302 err = got_error_from_errno2("unlink", path);
6303 free(path);
6304 return err;
6307 static const struct got_error *
6308 histedit_save_list(struct got_histedit_list *histedit_cmds,
6309 struct got_worktree *worktree, struct got_repository *repo)
6311 const struct got_error *err = NULL;
6312 char *path = NULL;
6313 FILE *f = NULL;
6314 struct got_histedit_list_entry *hle;
6315 struct got_commit_object *commit = NULL;
6317 err = got_worktree_get_histedit_script_path(&path, worktree);
6318 if (err)
6319 return err;
6321 f = fopen(path, "w");
6322 if (f == NULL) {
6323 err = got_error_from_errno2("fopen", path);
6324 goto done;
6326 TAILQ_FOREACH(hle, histedit_cmds, entry) {
6327 err = histedit_write_commit(hle->commit_id, hle->cmd->name, f,
6328 repo);
6329 if (err)
6330 break;
6332 if (hle->logmsg) {
6333 int n = fprintf(f, "%c %s\n",
6334 GOT_HISTEDIT_MESG, hle->logmsg);
6335 if (n < 0) {
6336 err = got_ferror(f, GOT_ERR_IO);
6337 break;
6341 done:
6342 if (f && fclose(f) != 0 && err == NULL)
6343 err = got_error_from_errno("fclose");
6344 free(path);
6345 if (commit)
6346 got_object_commit_close(commit);
6347 return err;
6350 void
6351 histedit_free_list(struct got_histedit_list *histedit_cmds)
6353 struct got_histedit_list_entry *hle;
6355 while ((hle = TAILQ_FIRST(histedit_cmds))) {
6356 TAILQ_REMOVE(histedit_cmds, hle, entry);
6357 free(hle);
6361 static const struct got_error *
6362 histedit_load_list(struct got_histedit_list *histedit_cmds,
6363 const char *path, struct got_repository *repo)
6365 const struct got_error *err = NULL;
6366 FILE *f = NULL;
6368 f = fopen(path, "r");
6369 if (f == NULL) {
6370 err = got_error_from_errno2("fopen", path);
6371 goto done;
6374 err = histedit_parse_list(histedit_cmds, f, repo);
6375 done:
6376 if (f && fclose(f) != 0 && err == NULL)
6377 err = got_error_from_errno("fclose");
6378 return err;
6381 static const struct got_error *
6382 histedit_edit_list_retry(struct got_histedit_list *histedit_cmds,
6383 const struct got_error *edit_err, struct got_object_id_queue *commits,
6384 const char *path, const char *branch_name, struct got_repository *repo)
6386 const struct got_error *err = NULL, *prev_err = edit_err;
6387 int resp = ' ';
6389 while (resp != 'c' && resp != 'r' && resp != 'a') {
6390 printf("%s: %s\n(c)ontinue editing, (r)estart editing, "
6391 "or (a)bort: ", getprogname(), prev_err->msg);
6392 resp = getchar();
6393 if (resp == '\n')
6394 resp = getchar();
6395 if (resp == 'c') {
6396 histedit_free_list(histedit_cmds);
6397 err = histedit_run_editor(histedit_cmds, path, commits,
6398 repo);
6399 if (err) {
6400 if (err->code != GOT_ERR_HISTEDIT_SYNTAX &&
6401 err->code != GOT_ERR_HISTEDIT_CMD)
6402 break;
6403 prev_err = err;
6404 resp = ' ';
6405 continue;
6407 break;
6408 } else if (resp == 'r') {
6409 histedit_free_list(histedit_cmds);
6410 err = histedit_edit_script(histedit_cmds,
6411 commits, branch_name, 0, repo);
6412 if (err) {
6413 if (err->code != GOT_ERR_HISTEDIT_SYNTAX &&
6414 err->code != GOT_ERR_HISTEDIT_CMD)
6415 break;
6416 prev_err = err;
6417 resp = ' ';
6418 continue;
6420 break;
6421 } else if (resp == 'a') {
6422 err = got_error(GOT_ERR_HISTEDIT_CANCEL);
6423 break;
6424 } else
6425 printf("invalid response '%c'\n", resp);
6428 return err;
6431 static const struct got_error *
6432 histedit_complete(struct got_worktree *worktree,
6433 struct got_fileindex *fileindex, struct got_reference *tmp_branch,
6434 struct got_reference *branch, struct got_repository *repo)
6436 printf("Switching work tree to %s\n",
6437 got_ref_get_symref_target(branch));
6438 return got_worktree_histedit_complete(worktree, fileindex, tmp_branch,
6439 branch, repo);
6442 static const struct got_error *
6443 show_histedit_progress(struct got_commit_object *commit,
6444 struct got_histedit_list_entry *hle, struct got_object_id *new_id)
6446 const struct got_error *err;
6447 char *old_id_str = NULL, *new_id_str = NULL, *logmsg = NULL;
6449 err = got_object_id_str(&old_id_str, hle->commit_id);
6450 if (err)
6451 goto done;
6453 if (new_id) {
6454 err = got_object_id_str(&new_id_str, new_id);
6455 if (err)
6456 goto done;
6459 old_id_str[12] = '\0';
6460 if (new_id_str)
6461 new_id_str[12] = '\0';
6463 if (hle->logmsg) {
6464 logmsg = strdup(hle->logmsg);
6465 if (logmsg == NULL) {
6466 err = got_error_from_errno("strdup");
6467 goto done;
6469 trim_logmsg(logmsg, 42);
6470 } else {
6471 err = get_short_logmsg(&logmsg, 42, commit);
6472 if (err)
6473 goto done;
6476 switch (hle->cmd->code) {
6477 case GOT_HISTEDIT_PICK:
6478 case GOT_HISTEDIT_EDIT:
6479 printf("%s -> %s: %s\n", old_id_str,
6480 new_id_str ? new_id_str : "no-op change", logmsg);
6481 break;
6482 case GOT_HISTEDIT_DROP:
6483 case GOT_HISTEDIT_FOLD:
6484 printf("%s -> %s commit: %s\n", old_id_str, hle->cmd->name,
6485 logmsg);
6486 break;
6487 default:
6488 break;
6490 done:
6491 free(old_id_str);
6492 free(new_id_str);
6493 return err;
6496 static const struct got_error *
6497 histedit_commit(struct got_pathlist_head *merged_paths,
6498 struct got_worktree *worktree, struct got_fileindex *fileindex,
6499 struct got_reference *tmp_branch, struct got_histedit_list_entry *hle,
6500 struct got_repository *repo)
6502 const struct got_error *err;
6503 struct got_commit_object *commit;
6504 struct got_object_id *new_commit_id;
6506 if ((hle->cmd->code == GOT_HISTEDIT_EDIT || get_folded_commits(hle))
6507 && hle->logmsg == NULL) {
6508 err = histedit_edit_logmsg(hle, repo);
6509 if (err)
6510 return err;
6513 err = got_object_open_as_commit(&commit, repo, hle->commit_id);
6514 if (err)
6515 return err;
6517 err = got_worktree_histedit_commit(&new_commit_id, merged_paths,
6518 worktree, fileindex, tmp_branch, commit, hle->commit_id,
6519 hle->logmsg, repo);
6520 if (err) {
6521 if (err->code != GOT_ERR_COMMIT_NO_CHANGES)
6522 goto done;
6523 err = show_histedit_progress(commit, hle, NULL);
6524 } else {
6525 err = show_histedit_progress(commit, hle, new_commit_id);
6526 free(new_commit_id);
6528 done:
6529 got_object_commit_close(commit);
6530 return err;
6533 static const struct got_error *
6534 histedit_skip_commit(struct got_histedit_list_entry *hle,
6535 struct got_worktree *worktree, struct got_repository *repo)
6537 const struct got_error *error;
6538 struct got_commit_object *commit;
6540 error = got_worktree_histedit_skip_commit(worktree, hle->commit_id,
6541 repo);
6542 if (error)
6543 return error;
6545 error = got_object_open_as_commit(&commit, repo, hle->commit_id);
6546 if (error)
6547 return error;
6549 error = show_histedit_progress(commit, hle, NULL);
6550 got_object_commit_close(commit);
6551 return error;
6554 static const struct got_error *
6555 check_local_changes(void *arg, unsigned char status,
6556 unsigned char staged_status, const char *path,
6557 struct got_object_id *blob_id, struct got_object_id *staged_blob_id,
6558 struct got_object_id *commit_id, int dirfd, const char *de_name)
6560 int *have_local_changes = arg;
6562 switch (status) {
6563 case GOT_STATUS_ADD:
6564 case GOT_STATUS_DELETE:
6565 case GOT_STATUS_MODIFY:
6566 case GOT_STATUS_CONFLICT:
6567 *have_local_changes = 1;
6568 return got_error(GOT_ERR_CANCELLED);
6569 default:
6570 break;
6573 switch (staged_status) {
6574 case GOT_STATUS_ADD:
6575 case GOT_STATUS_DELETE:
6576 case GOT_STATUS_MODIFY:
6577 *have_local_changes = 1;
6578 return got_error(GOT_ERR_CANCELLED);
6579 default:
6580 break;
6583 return NULL;
6586 static const struct got_error *
6587 cmd_histedit(int argc, char *argv[])
6589 const struct got_error *error = NULL;
6590 struct got_worktree *worktree = NULL;
6591 struct got_fileindex *fileindex = NULL;
6592 struct got_repository *repo = NULL;
6593 char *cwd = NULL;
6594 struct got_reference *branch = NULL;
6595 struct got_reference *tmp_branch = NULL;
6596 struct got_object_id *resume_commit_id = NULL;
6597 struct got_object_id *base_commit_id = NULL;
6598 struct got_object_id *head_commit_id = NULL;
6599 struct got_commit_object *commit = NULL;
6600 int ch, rebase_in_progress = 0, did_something;
6601 int edit_in_progress = 0, abort_edit = 0, continue_edit = 0;
6602 int edit_logmsg_only = 0;
6603 const char *edit_script_path = NULL;
6604 unsigned char rebase_status = GOT_STATUS_NO_CHANGE;
6605 struct got_object_id_queue commits;
6606 struct got_pathlist_head merged_paths;
6607 const struct got_object_id_queue *parent_ids;
6608 struct got_object_qid *pid;
6609 struct got_histedit_list histedit_cmds;
6610 struct got_histedit_list_entry *hle;
6612 SIMPLEQ_INIT(&commits);
6613 TAILQ_INIT(&histedit_cmds);
6614 TAILQ_INIT(&merged_paths);
6616 while ((ch = getopt(argc, argv, "acF:m")) != -1) {
6617 switch (ch) {
6618 case 'a':
6619 abort_edit = 1;
6620 break;
6621 case 'c':
6622 continue_edit = 1;
6623 break;
6624 case 'F':
6625 edit_script_path = optarg;
6626 break;
6627 case 'm':
6628 edit_logmsg_only = 1;
6629 break;
6630 default:
6631 usage_histedit();
6632 /* NOTREACHED */
6636 argc -= optind;
6637 argv += optind;
6639 #ifndef PROFILE
6640 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
6641 "unveil", NULL) == -1)
6642 err(1, "pledge");
6643 #endif
6644 if (abort_edit && continue_edit)
6645 errx(1, "histedit's -a and -c options are mutually exclusive");
6646 if (edit_script_path && edit_logmsg_only)
6647 errx(1, "histedit's -F and -m options are mutually exclusive");
6648 if (abort_edit && edit_logmsg_only)
6649 errx(1, "histedit's -a and -m options are mutually exclusive");
6650 if (continue_edit && edit_logmsg_only)
6651 errx(1, "histedit's -c and -m options are mutually exclusive");
6652 if (argc != 0)
6653 usage_histedit();
6656 * This command cannot apply unveil(2) in all cases because the
6657 * user may choose to run an editor to edit the histedit script
6658 * and to edit individual commit log messages.
6659 * unveil(2) traverses exec(2); if an editor is used we have to
6660 * apply unveil after edit script and log messages have been written.
6661 * XXX TODO: Make use of unveil(2) where possible.
6664 cwd = getcwd(NULL, 0);
6665 if (cwd == NULL) {
6666 error = got_error_from_errno("getcwd");
6667 goto done;
6669 error = got_worktree_open(&worktree, cwd);
6670 if (error)
6671 goto done;
6673 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
6674 NULL);
6675 if (error != NULL)
6676 goto done;
6678 error = got_worktree_rebase_in_progress(&rebase_in_progress, worktree);
6679 if (error)
6680 goto done;
6681 if (rebase_in_progress) {
6682 error = got_error(GOT_ERR_REBASING);
6683 goto done;
6686 error = got_worktree_histedit_in_progress(&edit_in_progress, worktree);
6687 if (error)
6688 goto done;
6690 if (edit_in_progress && edit_logmsg_only) {
6691 error = got_error_msg(GOT_ERR_HISTEDIT_BUSY,
6692 "histedit operation is in progress in this "
6693 "work tree and must be continued or aborted "
6694 "before the -m option can be used");
6695 goto done;
6698 if (edit_in_progress && abort_edit) {
6699 error = got_worktree_histedit_continue(&resume_commit_id,
6700 &tmp_branch, &branch, &base_commit_id, &fileindex,
6701 worktree, repo);
6702 if (error)
6703 goto done;
6704 printf("Switching work tree to %s\n",
6705 got_ref_get_symref_target(branch));
6706 error = got_worktree_histedit_abort(worktree, fileindex, repo,
6707 branch, base_commit_id, update_progress, &did_something);
6708 if (error)
6709 goto done;
6710 printf("Histedit of %s aborted\n",
6711 got_ref_get_symref_target(branch));
6712 goto done; /* nothing else to do */
6713 } else if (abort_edit) {
6714 error = got_error(GOT_ERR_NOT_HISTEDIT);
6715 goto done;
6718 if (continue_edit) {
6719 char *path;
6721 if (!edit_in_progress) {
6722 error = got_error(GOT_ERR_NOT_HISTEDIT);
6723 goto done;
6726 error = got_worktree_get_histedit_script_path(&path, worktree);
6727 if (error)
6728 goto done;
6730 error = histedit_load_list(&histedit_cmds, path, repo);
6731 free(path);
6732 if (error)
6733 goto done;
6735 error = got_worktree_histedit_continue(&resume_commit_id,
6736 &tmp_branch, &branch, &base_commit_id, &fileindex,
6737 worktree, repo);
6738 if (error)
6739 goto done;
6741 error = got_ref_resolve(&head_commit_id, repo, branch);
6742 if (error)
6743 goto done;
6745 error = got_object_open_as_commit(&commit, repo,
6746 head_commit_id);
6747 if (error)
6748 goto done;
6749 parent_ids = got_object_commit_get_parent_ids(commit);
6750 pid = SIMPLEQ_FIRST(parent_ids);
6751 if (pid == NULL) {
6752 error = got_error(GOT_ERR_EMPTY_HISTEDIT);
6753 goto done;
6755 error = collect_commits(&commits, head_commit_id, pid->id,
6756 base_commit_id, got_worktree_get_path_prefix(worktree),
6757 GOT_ERR_HISTEDIT_PATH, repo);
6758 got_object_commit_close(commit);
6759 commit = NULL;
6760 if (error)
6761 goto done;
6762 } else {
6763 if (edit_in_progress) {
6764 error = got_error(GOT_ERR_HISTEDIT_BUSY);
6765 goto done;
6768 error = got_ref_open(&branch, repo,
6769 got_worktree_get_head_ref_name(worktree), 0);
6770 if (error != NULL)
6771 goto done;
6773 if (strncmp(got_ref_get_name(branch), "refs/heads/", 11) != 0) {
6774 error = got_error_msg(GOT_ERR_COMMIT_BRANCH,
6775 "will not edit commit history of a branch outside "
6776 "the \"refs/heads/\" reference namespace");
6777 goto done;
6780 error = got_ref_resolve(&head_commit_id, repo, branch);
6781 got_ref_close(branch);
6782 branch = NULL;
6783 if (error)
6784 goto done;
6786 error = got_object_open_as_commit(&commit, repo,
6787 head_commit_id);
6788 if (error)
6789 goto done;
6790 parent_ids = got_object_commit_get_parent_ids(commit);
6791 pid = SIMPLEQ_FIRST(parent_ids);
6792 if (pid == NULL) {
6793 error = got_error(GOT_ERR_EMPTY_HISTEDIT);
6794 goto done;
6796 error = collect_commits(&commits, head_commit_id, pid->id,
6797 got_worktree_get_base_commit_id(worktree),
6798 got_worktree_get_path_prefix(worktree),
6799 GOT_ERR_HISTEDIT_PATH, repo);
6800 got_object_commit_close(commit);
6801 commit = NULL;
6802 if (error)
6803 goto done;
6805 if (SIMPLEQ_EMPTY(&commits)) {
6806 error = got_error(GOT_ERR_EMPTY_HISTEDIT);
6807 goto done;
6810 error = got_worktree_histedit_prepare(&tmp_branch, &branch,
6811 &base_commit_id, &fileindex, worktree, repo);
6812 if (error)
6813 goto done;
6815 if (edit_script_path) {
6816 error = histedit_load_list(&histedit_cmds,
6817 edit_script_path, repo);
6818 if (error) {
6819 got_worktree_histedit_abort(worktree, fileindex,
6820 repo, branch, base_commit_id,
6821 update_progress, &did_something);
6822 goto done;
6824 } else {
6825 const char *branch_name;
6826 branch_name = got_ref_get_symref_target(branch);
6827 if (strncmp(branch_name, "refs/heads/", 11) == 0)
6828 branch_name += 11;
6829 error = histedit_edit_script(&histedit_cmds, &commits,
6830 branch_name, edit_logmsg_only, repo);
6831 if (error) {
6832 got_worktree_histedit_abort(worktree, fileindex,
6833 repo, branch, base_commit_id,
6834 update_progress, &did_something);
6835 goto done;
6840 error = histedit_save_list(&histedit_cmds, worktree,
6841 repo);
6842 if (error) {
6843 got_worktree_histedit_abort(worktree, fileindex,
6844 repo, branch, base_commit_id,
6845 update_progress, &did_something);
6846 goto done;
6851 error = histedit_check_script(&histedit_cmds, &commits, repo);
6852 if (error)
6853 goto done;
6855 TAILQ_FOREACH(hle, &histedit_cmds, entry) {
6856 if (resume_commit_id) {
6857 if (got_object_id_cmp(hle->commit_id,
6858 resume_commit_id) != 0)
6859 continue;
6861 resume_commit_id = NULL;
6862 if (hle->cmd->code == GOT_HISTEDIT_DROP ||
6863 hle->cmd->code == GOT_HISTEDIT_FOLD) {
6864 error = histedit_skip_commit(hle, worktree,
6865 repo);
6866 if (error)
6867 goto done;
6868 } else {
6869 struct got_pathlist_head paths;
6870 int have_changes = 0;
6872 TAILQ_INIT(&paths);
6873 error = got_pathlist_append(&paths, "", NULL);
6874 if (error)
6875 goto done;
6876 error = got_worktree_status(worktree, &paths,
6877 repo, check_local_changes, &have_changes,
6878 check_cancelled, NULL);
6879 got_pathlist_free(&paths);
6880 if (error) {
6881 if (error->code != GOT_ERR_CANCELLED)
6882 goto done;
6883 if (sigint_received || sigpipe_received)
6884 goto done;
6886 if (have_changes) {
6887 error = histedit_commit(NULL, worktree,
6888 fileindex, tmp_branch, hle, repo);
6889 if (error)
6890 goto done;
6891 } else {
6892 error = got_object_open_as_commit(
6893 &commit, repo, hle->commit_id);
6894 if (error)
6895 goto done;
6896 error = show_histedit_progress(commit,
6897 hle, NULL);
6898 got_object_commit_close(commit);
6899 commit = NULL;
6900 if (error)
6901 goto done;
6904 continue;
6907 if (hle->cmd->code == GOT_HISTEDIT_DROP) {
6908 error = histedit_skip_commit(hle, worktree, repo);
6909 if (error)
6910 goto done;
6911 continue;
6914 error = got_object_open_as_commit(&commit, repo,
6915 hle->commit_id);
6916 if (error)
6917 goto done;
6918 parent_ids = got_object_commit_get_parent_ids(commit);
6919 pid = SIMPLEQ_FIRST(parent_ids);
6921 error = got_worktree_histedit_merge_files(&merged_paths,
6922 worktree, fileindex, pid->id, hle->commit_id, repo,
6923 rebase_progress, &rebase_status, check_cancelled, NULL);
6924 if (error)
6925 goto done;
6926 got_object_commit_close(commit);
6927 commit = NULL;
6929 if (rebase_status == GOT_STATUS_CONFLICT) {
6930 error = show_rebase_merge_conflict(hle->commit_id,
6931 repo);
6932 if (error)
6933 goto done;
6934 got_worktree_rebase_pathlist_free(&merged_paths);
6935 break;
6938 if (hle->cmd->code == GOT_HISTEDIT_EDIT) {
6939 char *id_str;
6940 error = got_object_id_str(&id_str, hle->commit_id);
6941 if (error)
6942 goto done;
6943 printf("Stopping histedit for amending commit %s\n",
6944 id_str);
6945 free(id_str);
6946 got_worktree_rebase_pathlist_free(&merged_paths);
6947 error = got_worktree_histedit_postpone(worktree,
6948 fileindex);
6949 goto done;
6952 if (hle->cmd->code == GOT_HISTEDIT_FOLD) {
6953 error = histedit_skip_commit(hle, worktree, repo);
6954 if (error)
6955 goto done;
6956 continue;
6959 error = histedit_commit(&merged_paths, worktree, fileindex,
6960 tmp_branch, hle, repo);
6961 got_worktree_rebase_pathlist_free(&merged_paths);
6962 if (error)
6963 goto done;
6966 if (rebase_status == GOT_STATUS_CONFLICT) {
6967 error = got_worktree_histedit_postpone(worktree, fileindex);
6968 if (error)
6969 goto done;
6970 error = got_error_msg(GOT_ERR_CONFLICTS,
6971 "conflicts must be resolved before histedit can continue");
6972 } else
6973 error = histedit_complete(worktree, fileindex, tmp_branch,
6974 branch, repo);
6975 done:
6976 got_object_id_queue_free(&commits);
6977 histedit_free_list(&histedit_cmds);
6978 free(head_commit_id);
6979 free(base_commit_id);
6980 free(resume_commit_id);
6981 if (commit)
6982 got_object_commit_close(commit);
6983 if (branch)
6984 got_ref_close(branch);
6985 if (tmp_branch)
6986 got_ref_close(tmp_branch);
6987 if (worktree)
6988 got_worktree_close(worktree);
6989 if (repo)
6990 got_repo_close(repo);
6991 return error;
6994 __dead static void
6995 usage_integrate(void)
6997 fprintf(stderr, "usage: %s integrate branch\n", getprogname());
6998 exit(1);
7001 static const struct got_error *
7002 cmd_integrate(int argc, char *argv[])
7004 const struct got_error *error = NULL;
7005 struct got_repository *repo = NULL;
7006 struct got_worktree *worktree = NULL;
7007 char *cwd = NULL, *refname = NULL, *base_refname = NULL;
7008 const char *branch_arg = NULL;
7009 struct got_reference *branch_ref = NULL, *base_branch_ref = NULL;
7010 struct got_fileindex *fileindex = NULL;
7011 struct got_object_id *commit_id = NULL, *base_commit_id = NULL;
7012 int ch, did_something = 0;
7014 while ((ch = getopt(argc, argv, "")) != -1) {
7015 switch (ch) {
7016 default:
7017 usage_integrate();
7018 /* NOTREACHED */
7022 argc -= optind;
7023 argv += optind;
7025 if (argc != 1)
7026 usage_integrate();
7027 branch_arg = argv[0];
7029 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
7030 "unveil", NULL) == -1)
7031 err(1, "pledge");
7033 cwd = getcwd(NULL, 0);
7034 if (cwd == NULL) {
7035 error = got_error_from_errno("getcwd");
7036 goto done;
7039 error = got_worktree_open(&worktree, cwd);
7040 if (error)
7041 goto done;
7043 error = check_rebase_or_histedit_in_progress(worktree);
7044 if (error)
7045 goto done;
7047 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
7048 NULL);
7049 if (error != NULL)
7050 goto done;
7052 error = apply_unveil(got_repo_get_path(repo), 0,
7053 got_worktree_get_root_path(worktree));
7054 if (error)
7055 goto done;
7057 if (asprintf(&refname, "refs/heads/%s", branch_arg) == -1) {
7058 error = got_error_from_errno("asprintf");
7059 goto done;
7062 error = got_worktree_integrate_prepare(&fileindex, &branch_ref,
7063 &base_branch_ref, worktree, refname, repo);
7064 if (error)
7065 goto done;
7067 refname = strdup(got_ref_get_name(branch_ref));
7068 if (refname == NULL) {
7069 error = got_error_from_errno("strdup");
7070 got_worktree_integrate_abort(worktree, fileindex, repo,
7071 branch_ref, base_branch_ref);
7072 goto done;
7074 base_refname = strdup(got_ref_get_name(base_branch_ref));
7075 if (base_refname == NULL) {
7076 error = got_error_from_errno("strdup");
7077 got_worktree_integrate_abort(worktree, fileindex, repo,
7078 branch_ref, base_branch_ref);
7079 goto done;
7082 error = got_ref_resolve(&commit_id, repo, branch_ref);
7083 if (error)
7084 goto done;
7086 error = got_ref_resolve(&base_commit_id, repo, base_branch_ref);
7087 if (error)
7088 goto done;
7090 if (got_object_id_cmp(commit_id, base_commit_id) == 0) {
7091 error = got_error_msg(GOT_ERR_SAME_BRANCH,
7092 "specified branch has already been integrated");
7093 got_worktree_integrate_abort(worktree, fileindex, repo,
7094 branch_ref, base_branch_ref);
7095 goto done;
7098 error = check_linear_ancestry(commit_id, base_commit_id, 1, repo);
7099 if (error) {
7100 if (error->code == GOT_ERR_ANCESTRY)
7101 error = got_error(GOT_ERR_REBASE_REQUIRED);
7102 got_worktree_integrate_abort(worktree, fileindex, repo,
7103 branch_ref, base_branch_ref);
7104 goto done;
7107 error = got_worktree_integrate_continue(worktree, fileindex, repo,
7108 branch_ref, base_branch_ref, update_progress, &did_something,
7109 check_cancelled, NULL);
7110 if (error)
7111 goto done;
7113 printf("Integrated %s into %s\n", refname, base_refname);
7114 done:
7115 if (repo)
7116 got_repo_close(repo);
7117 if (worktree)
7118 got_worktree_close(worktree);
7119 free(cwd);
7120 free(base_commit_id);
7121 free(commit_id);
7122 free(refname);
7123 free(base_refname);
7124 return error;
7127 __dead static void
7128 usage_stage(void)
7130 fprintf(stderr, "usage: %s stage [-l] | [-p] [-F response-script] "
7131 "[file-path ...]\n",
7132 getprogname());
7133 exit(1);
7136 static const struct got_error *
7137 print_stage(void *arg, unsigned char status, unsigned char staged_status,
7138 const char *path, struct got_object_id *blob_id,
7139 struct got_object_id *staged_blob_id, struct got_object_id *commit_id,
7140 int dirfd, const char *de_name)
7142 const struct got_error *err = NULL;
7143 char *id_str = NULL;
7145 if (staged_status != GOT_STATUS_ADD &&
7146 staged_status != GOT_STATUS_MODIFY &&
7147 staged_status != GOT_STATUS_DELETE)
7148 return NULL;
7150 if (staged_status == GOT_STATUS_ADD ||
7151 staged_status == GOT_STATUS_MODIFY)
7152 err = got_object_id_str(&id_str, staged_blob_id);
7153 else
7154 err = got_object_id_str(&id_str, blob_id);
7155 if (err)
7156 return err;
7158 printf("%s %c %s\n", id_str, staged_status, path);
7159 free(id_str);
7160 return NULL;
7163 static const struct got_error *
7164 cmd_stage(int argc, char *argv[])
7166 const struct got_error *error = NULL;
7167 struct got_repository *repo = NULL;
7168 struct got_worktree *worktree = NULL;
7169 char *cwd = NULL;
7170 struct got_pathlist_head paths;
7171 struct got_pathlist_entry *pe;
7172 int ch, list_stage = 0, pflag = 0;
7173 FILE *patch_script_file = NULL;
7174 const char *patch_script_path = NULL;
7175 struct choose_patch_arg cpa;
7177 TAILQ_INIT(&paths);
7179 while ((ch = getopt(argc, argv, "lpF:")) != -1) {
7180 switch (ch) {
7181 case 'l':
7182 list_stage = 1;
7183 break;
7184 case 'p':
7185 pflag = 1;
7186 break;
7187 case 'F':
7188 patch_script_path = optarg;
7189 break;
7190 default:
7191 usage_stage();
7192 /* NOTREACHED */
7196 argc -= optind;
7197 argv += optind;
7199 #ifndef PROFILE
7200 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
7201 "unveil", NULL) == -1)
7202 err(1, "pledge");
7203 #endif
7204 if (list_stage && (pflag || patch_script_path))
7205 errx(1, "-l option cannot be used with other options");
7206 if (patch_script_path && !pflag)
7207 errx(1, "-F option can only be used together with -p option");
7209 cwd = getcwd(NULL, 0);
7210 if (cwd == NULL) {
7211 error = got_error_from_errno("getcwd");
7212 goto done;
7215 error = got_worktree_open(&worktree, cwd);
7216 if (error)
7217 goto done;
7219 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
7220 NULL);
7221 if (error != NULL)
7222 goto done;
7224 if (patch_script_path) {
7225 patch_script_file = fopen(patch_script_path, "r");
7226 if (patch_script_file == NULL) {
7227 error = got_error_from_errno2("fopen",
7228 patch_script_path);
7229 goto done;
7232 error = apply_unveil(got_repo_get_path(repo), 0,
7233 got_worktree_get_root_path(worktree));
7234 if (error)
7235 goto done;
7237 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
7238 if (error)
7239 goto done;
7241 if (list_stage)
7242 error = got_worktree_status(worktree, &paths, repo,
7243 print_stage, NULL, check_cancelled, NULL);
7244 else {
7245 cpa.patch_script_file = patch_script_file;
7246 cpa.action = "stage";
7247 error = got_worktree_stage(worktree, &paths,
7248 pflag ? NULL : print_status, NULL,
7249 pflag ? choose_patch : NULL, &cpa, repo);
7251 done:
7252 if (patch_script_file && fclose(patch_script_file) == EOF &&
7253 error == NULL)
7254 error = got_error_from_errno2("fclose", patch_script_path);
7255 if (repo)
7256 got_repo_close(repo);
7257 if (worktree)
7258 got_worktree_close(worktree);
7259 TAILQ_FOREACH(pe, &paths, entry)
7260 free((char *)pe->path);
7261 got_pathlist_free(&paths);
7262 free(cwd);
7263 return error;
7266 __dead static void
7267 usage_unstage(void)
7269 fprintf(stderr, "usage: %s unstage [-p] [-F response-script] "
7270 "[file-path ...]\n",
7271 getprogname());
7272 exit(1);
7276 static const struct got_error *
7277 cmd_unstage(int argc, char *argv[])
7279 const struct got_error *error = NULL;
7280 struct got_repository *repo = NULL;
7281 struct got_worktree *worktree = NULL;
7282 char *cwd = NULL;
7283 struct got_pathlist_head paths;
7284 struct got_pathlist_entry *pe;
7285 int ch, did_something = 0, pflag = 0;
7286 FILE *patch_script_file = NULL;
7287 const char *patch_script_path = NULL;
7288 struct choose_patch_arg cpa;
7290 TAILQ_INIT(&paths);
7292 while ((ch = getopt(argc, argv, "pF:")) != -1) {
7293 switch (ch) {
7294 case 'p':
7295 pflag = 1;
7296 break;
7297 case 'F':
7298 patch_script_path = optarg;
7299 break;
7300 default:
7301 usage_unstage();
7302 /* NOTREACHED */
7306 argc -= optind;
7307 argv += optind;
7309 #ifndef PROFILE
7310 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
7311 "unveil", NULL) == -1)
7312 err(1, "pledge");
7313 #endif
7314 if (patch_script_path && !pflag)
7315 errx(1, "-F option can only be used together with -p option");
7317 cwd = getcwd(NULL, 0);
7318 if (cwd == NULL) {
7319 error = got_error_from_errno("getcwd");
7320 goto done;
7323 error = got_worktree_open(&worktree, cwd);
7324 if (error)
7325 goto done;
7327 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
7328 NULL);
7329 if (error != NULL)
7330 goto done;
7332 if (patch_script_path) {
7333 patch_script_file = fopen(patch_script_path, "r");
7334 if (patch_script_file == NULL) {
7335 error = got_error_from_errno2("fopen",
7336 patch_script_path);
7337 goto done;
7341 error = apply_unveil(got_repo_get_path(repo), 0,
7342 got_worktree_get_root_path(worktree));
7343 if (error)
7344 goto done;
7346 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
7347 if (error)
7348 goto done;
7350 cpa.patch_script_file = patch_script_file;
7351 cpa.action = "unstage";
7352 error = got_worktree_unstage(worktree, &paths, update_progress,
7353 &did_something, pflag ? choose_patch : NULL, &cpa, repo);
7354 done:
7355 if (patch_script_file && fclose(patch_script_file) == EOF &&
7356 error == NULL)
7357 error = got_error_from_errno2("fclose", patch_script_path);
7358 if (repo)
7359 got_repo_close(repo);
7360 if (worktree)
7361 got_worktree_close(worktree);
7362 TAILQ_FOREACH(pe, &paths, entry)
7363 free((char *)pe->path);
7364 got_pathlist_free(&paths);
7365 free(cwd);
7366 return error;
7369 __dead static void
7370 usage_cat(void)
7372 fprintf(stderr, "usage: %s cat [-r repository ] [ -c commit ] [ -P ] "
7373 "arg1 [arg2 ...]\n", getprogname());
7374 exit(1);
7377 static const struct got_error *
7378 cat_blob(struct got_object_id *id, struct got_repository *repo, FILE *outfile)
7380 const struct got_error *err;
7381 struct got_blob_object *blob;
7383 err = got_object_open_as_blob(&blob, repo, id, 8192);
7384 if (err)
7385 return err;
7387 err = got_object_blob_dump_to_file(NULL, NULL, NULL, outfile, blob);
7388 got_object_blob_close(blob);
7389 return err;
7392 static const struct got_error *
7393 cat_tree(struct got_object_id *id, struct got_repository *repo, FILE *outfile)
7395 const struct got_error *err;
7396 struct got_tree_object *tree;
7397 int nentries, i;
7399 err = got_object_open_as_tree(&tree, repo, id);
7400 if (err)
7401 return err;
7403 nentries = got_object_tree_get_nentries(tree);
7404 for (i = 0; i < nentries; i++) {
7405 struct got_tree_entry *te;
7406 char *id_str;
7407 if (sigint_received || sigpipe_received)
7408 break;
7409 te = got_object_tree_get_entry(tree, i);
7410 err = got_object_id_str(&id_str, got_tree_entry_get_id(te));
7411 if (err)
7412 break;
7413 fprintf(outfile, "%s %.7o %s\n", id_str,
7414 got_tree_entry_get_mode(te),
7415 got_tree_entry_get_name(te));
7416 free(id_str);
7419 got_object_tree_close(tree);
7420 return err;
7423 static const struct got_error *
7424 cat_commit(struct got_object_id *id, struct got_repository *repo, FILE *outfile)
7426 const struct got_error *err;
7427 struct got_commit_object *commit;
7428 const struct got_object_id_queue *parent_ids;
7429 struct got_object_qid *pid;
7430 char *id_str = NULL;
7431 const char *logmsg = NULL;
7433 err = got_object_open_as_commit(&commit, repo, id);
7434 if (err)
7435 return err;
7437 err = got_object_id_str(&id_str, got_object_commit_get_tree_id(commit));
7438 if (err)
7439 goto done;
7441 fprintf(outfile, "%s%s\n", GOT_COMMIT_LABEL_TREE, id_str);
7442 parent_ids = got_object_commit_get_parent_ids(commit);
7443 fprintf(outfile, "numparents %d\n",
7444 got_object_commit_get_nparents(commit));
7445 SIMPLEQ_FOREACH(pid, parent_ids, entry) {
7446 char *pid_str;
7447 err = got_object_id_str(&pid_str, pid->id);
7448 if (err)
7449 goto done;
7450 fprintf(outfile, "%s%s\n", GOT_COMMIT_LABEL_PARENT, pid_str);
7451 free(pid_str);
7453 fprintf(outfile, "%s%s %lld +0000\n", GOT_COMMIT_LABEL_AUTHOR,
7454 got_object_commit_get_author(commit),
7455 got_object_commit_get_author_time(commit));
7457 fprintf(outfile, "%s%s %lld +0000\n", GOT_COMMIT_LABEL_COMMITTER,
7458 got_object_commit_get_author(commit),
7459 got_object_commit_get_committer_time(commit));
7461 logmsg = got_object_commit_get_logmsg_raw(commit);
7462 fprintf(outfile, "messagelen %zd\n", strlen(logmsg));
7463 fprintf(outfile, "%s", logmsg);
7464 done:
7465 free(id_str);
7466 got_object_commit_close(commit);
7467 return err;
7470 static const struct got_error *
7471 cat_tag(struct got_object_id *id, struct got_repository *repo, FILE *outfile)
7473 const struct got_error *err;
7474 struct got_tag_object *tag;
7475 char *id_str = NULL;
7476 const char *tagmsg = NULL;
7478 err = got_object_open_as_tag(&tag, repo, id);
7479 if (err)
7480 return err;
7482 err = got_object_id_str(&id_str, got_object_tag_get_object_id(tag));
7483 if (err)
7484 goto done;
7486 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_OBJECT, id_str);
7488 switch (got_object_tag_get_object_type(tag)) {
7489 case GOT_OBJ_TYPE_BLOB:
7490 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TYPE,
7491 GOT_OBJ_LABEL_BLOB);
7492 break;
7493 case GOT_OBJ_TYPE_TREE:
7494 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TYPE,
7495 GOT_OBJ_LABEL_TREE);
7496 break;
7497 case GOT_OBJ_TYPE_COMMIT:
7498 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TYPE,
7499 GOT_OBJ_LABEL_COMMIT);
7500 break;
7501 case GOT_OBJ_TYPE_TAG:
7502 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TYPE,
7503 GOT_OBJ_LABEL_TAG);
7504 break;
7505 default:
7506 break;
7509 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TAG,
7510 got_object_tag_get_name(tag));
7512 fprintf(outfile, "%s%s %lld +0000\n", GOT_TAG_LABEL_TAGGER,
7513 got_object_tag_get_tagger(tag),
7514 got_object_tag_get_tagger_time(tag));
7516 tagmsg = got_object_tag_get_message(tag);
7517 fprintf(outfile, "messagelen %zd\n", strlen(tagmsg));
7518 fprintf(outfile, "%s", tagmsg);
7519 done:
7520 free(id_str);
7521 got_object_tag_close(tag);
7522 return err;
7525 static const struct got_error *
7526 cmd_cat(int argc, char *argv[])
7528 const struct got_error *error;
7529 struct got_repository *repo = NULL;
7530 struct got_worktree *worktree = NULL;
7531 char *cwd = NULL, *repo_path = NULL, *label = NULL;
7532 const char *commit_id_str = NULL;
7533 struct got_object_id *id = NULL, *commit_id = NULL;
7534 int ch, obj_type, i, force_path = 0;
7536 #ifndef PROFILE
7537 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
7538 NULL) == -1)
7539 err(1, "pledge");
7540 #endif
7542 while ((ch = getopt(argc, argv, "c:r:P")) != -1) {
7543 switch (ch) {
7544 case 'c':
7545 commit_id_str = optarg;
7546 break;
7547 case 'r':
7548 repo_path = realpath(optarg, NULL);
7549 if (repo_path == NULL)
7550 return got_error_from_errno2("realpath",
7551 optarg);
7552 got_path_strip_trailing_slashes(repo_path);
7553 break;
7554 case 'P':
7555 force_path = 1;
7556 break;
7557 default:
7558 usage_cat();
7559 /* NOTREACHED */
7563 argc -= optind;
7564 argv += optind;
7566 cwd = getcwd(NULL, 0);
7567 if (cwd == NULL) {
7568 error = got_error_from_errno("getcwd");
7569 goto done;
7571 error = got_worktree_open(&worktree, cwd);
7572 if (error && error->code != GOT_ERR_NOT_WORKTREE)
7573 goto done;
7574 if (worktree) {
7575 if (repo_path == NULL) {
7576 repo_path = strdup(
7577 got_worktree_get_repo_path(worktree));
7578 if (repo_path == NULL) {
7579 error = got_error_from_errno("strdup");
7580 goto done;
7585 if (repo_path == NULL) {
7586 repo_path = getcwd(NULL, 0);
7587 if (repo_path == NULL)
7588 return got_error_from_errno("getcwd");
7591 error = got_repo_open(&repo, repo_path, NULL);
7592 free(repo_path);
7593 if (error != NULL)
7594 goto done;
7596 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
7597 if (error)
7598 goto done;
7600 if (commit_id_str == NULL)
7601 commit_id_str = GOT_REF_HEAD;
7602 error = got_repo_match_object_id(&commit_id, NULL,
7603 commit_id_str, GOT_OBJ_TYPE_COMMIT, 1, repo);
7604 if (error)
7605 goto done;
7607 for (i = 0; i < argc; i++) {
7608 if (force_path) {
7609 error = got_object_id_by_path(&id, repo, commit_id,
7610 argv[i]);
7611 if (error)
7612 break;
7613 } else {
7614 error = got_repo_match_object_id(&id, &label, argv[i],
7615 GOT_OBJ_TYPE_ANY, 0, repo);
7616 if (error) {
7617 if (error->code != GOT_ERR_BAD_OBJ_ID_STR &&
7618 error->code != GOT_ERR_NOT_REF)
7619 break;
7620 error = got_object_id_by_path(&id, repo,
7621 commit_id, argv[i]);
7622 if (error)
7623 break;
7627 error = got_object_get_type(&obj_type, repo, id);
7628 if (error)
7629 break;
7631 switch (obj_type) {
7632 case GOT_OBJ_TYPE_BLOB:
7633 error = cat_blob(id, repo, stdout);
7634 break;
7635 case GOT_OBJ_TYPE_TREE:
7636 error = cat_tree(id, repo, stdout);
7637 break;
7638 case GOT_OBJ_TYPE_COMMIT:
7639 error = cat_commit(id, repo, stdout);
7640 break;
7641 case GOT_OBJ_TYPE_TAG:
7642 error = cat_tag(id, repo, stdout);
7643 break;
7644 default:
7645 error = got_error(GOT_ERR_OBJ_TYPE);
7646 break;
7648 if (error)
7649 break;
7650 free(label);
7651 label = NULL;
7652 free(id);
7653 id = NULL;
7655 done:
7656 free(label);
7657 free(id);
7658 free(commit_id);
7659 if (worktree)
7660 got_worktree_close(worktree);
7661 if (repo) {
7662 const struct got_error *repo_error;
7663 repo_error = got_repo_close(repo);
7664 if (error == NULL)
7665 error = repo_error;
7667 return error;