Blob


1 /*
2 * Copyright (c) 2017 Martin Pieuchot <mpi@openbsd.org>
3 * Copyright (c) 2018, 2019 Stefan Sperling <stsp@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
18 #include <sys/queue.h>
19 #include <sys/limits.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 <locale.h>
28 #include <signal.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <libgen.h>
34 #include <time.h>
35 #include <paths.h>
37 #include "got_error.h"
38 #include "got_object.h"
39 #include "got_reference.h"
40 #include "got_repository.h"
41 #include "got_path.h"
42 #include "got_worktree.h"
43 #include "got_diff.h"
44 #include "got_commit_graph.h"
45 #include "got_blame.h"
46 #include "got_privsep.h"
48 #ifndef nitems
49 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
50 #endif
52 static volatile sig_atomic_t sigint_received;
53 static volatile sig_atomic_t sigpipe_received;
55 static void
56 catch_sigint(int signo)
57 {
58 sigint_received = 1;
59 }
61 static void
62 catch_sigpipe(int signo)
63 {
64 sigpipe_received = 1;
65 }
68 struct cmd {
69 const char *cmd_name;
70 const struct got_error *(*cmd_main)(int, char *[]);
71 void (*cmd_usage)(void);
72 const char *cmd_descr;
73 };
75 __dead static void usage(void);
76 __dead static void usage_checkout(void);
77 __dead static void usage_update(void);
78 __dead static void usage_log(void);
79 __dead static void usage_diff(void);
80 __dead static void usage_blame(void);
81 __dead static void usage_tree(void);
82 __dead static void usage_status(void);
83 __dead static void usage_ref(void);
84 __dead static void usage_add(void);
85 __dead static void usage_rm(void);
86 __dead static void usage_revert(void);
87 __dead static void usage_commit(void);
89 static const struct got_error* cmd_checkout(int, char *[]);
90 static const struct got_error* cmd_update(int, char *[]);
91 static const struct got_error* cmd_log(int, char *[]);
92 static const struct got_error* cmd_diff(int, char *[]);
93 static const struct got_error* cmd_blame(int, char *[]);
94 static const struct got_error* cmd_tree(int, char *[]);
95 static const struct got_error* cmd_status(int, char *[]);
96 static const struct got_error* cmd_ref(int, char *[]);
97 static const struct got_error* cmd_add(int, char *[]);
98 static const struct got_error* cmd_rm(int, char *[]);
99 static const struct got_error* cmd_revert(int, char *[]);
100 static const struct got_error* cmd_commit(int, char *[]);
102 static struct cmd got_commands[] = {
103 { "checkout", cmd_checkout, usage_checkout,
104 "check out a new work tree from a repository" },
105 { "update", cmd_update, usage_update,
106 "update a work tree to a different commit" },
107 { "log", cmd_log, usage_log,
108 "show repository history" },
109 { "diff", cmd_diff, usage_diff,
110 "compare files and directories" },
111 { "blame", cmd_blame, usage_blame,
112 "show when lines in a file were changed" },
113 { "tree", cmd_tree, usage_tree,
114 "list files and directories in repository" },
115 { "status", cmd_status, usage_status,
116 "show modification status of files" },
117 { "ref", cmd_ref, usage_ref,
118 "manage references in repository" },
119 { "add", cmd_add, usage_add,
120 "add new files to version control" },
121 { "rm", cmd_rm, usage_rm,
122 "remove a versioned file" },
123 { "revert", cmd_revert, usage_revert,
124 "revert uncommitted changes" },
125 { "commit", cmd_commit, usage_commit,
126 "write changes from work tree to repository" },
127 };
129 int
130 main(int argc, char *argv[])
132 struct cmd *cmd;
133 unsigned int i;
134 int ch;
135 int hflag = 0;
137 setlocale(LC_CTYPE, "");
139 while ((ch = getopt(argc, argv, "h")) != -1) {
140 switch (ch) {
141 case 'h':
142 hflag = 1;
143 break;
144 default:
145 usage();
146 /* NOTREACHED */
150 argc -= optind;
151 argv += optind;
152 optind = 0;
154 if (argc <= 0)
155 usage();
157 signal(SIGINT, catch_sigint);
158 signal(SIGPIPE, catch_sigpipe);
160 for (i = 0; i < nitems(got_commands); i++) {
161 const struct got_error *error;
163 cmd = &got_commands[i];
165 if (strncmp(cmd->cmd_name, argv[0], strlen(argv[0])))
166 continue;
168 if (hflag)
169 got_commands[i].cmd_usage();
171 error = got_commands[i].cmd_main(argc, argv);
172 if (error && !(sigint_received || sigpipe_received)) {
173 fprintf(stderr, "%s: %s\n", getprogname(), error->msg);
174 return 1;
177 return 0;
180 fprintf(stderr, "%s: unknown command '%s'\n", getprogname(), argv[0]);
181 return 1;
184 __dead static void
185 usage(void)
187 int i;
189 fprintf(stderr, "usage: %s [-h] command [arg ...]\n\n"
190 "Available commands:\n", getprogname());
191 for (i = 0; i < nitems(got_commands); i++) {
192 struct cmd *cmd = &got_commands[i];
193 fprintf(stderr, " %s: %s\n", cmd->cmd_name, cmd->cmd_descr);
195 exit(1);
198 static const struct got_error *
199 apply_unveil(const char *repo_path, int repo_read_only,
200 const char *worktree_path, int create_worktree)
202 const struct got_error *error;
203 static char err_msg[MAXPATHLEN + 36];
205 if (create_worktree) {
206 /* Pre-create work tree path to avoid unveiling its parents. */
207 error = got_path_mkdir(worktree_path);
209 if (errno == EEXIST) {
210 if (got_path_dir_is_empty(worktree_path)) {
211 errno = 0;
212 error = NULL;
213 } else {
214 snprintf(err_msg, sizeof(err_msg),
215 "%s: directory exists but is not empty",
216 worktree_path);
217 error = got_error_msg(GOT_ERR_BAD_PATH,
218 err_msg);
222 if (error && (error->code != GOT_ERR_ERRNO || errno != EISDIR))
223 return error;
226 if (repo_path && unveil(repo_path, repo_read_only ? "r" : "rwc") != 0)
227 return got_error_prefix_errno2("unveil", repo_path);
229 if (worktree_path && unveil(worktree_path, "rwc") != 0)
230 return got_error_prefix_errno2("unveil", worktree_path);
232 if (unveil("/tmp", "rwc") != 0)
233 return got_error_prefix_errno2("unveil", "/tmp");
235 error = got_privsep_unveil_exec_helpers();
236 if (error != NULL)
237 return error;
239 if (unveil(NULL, NULL) != 0)
240 return got_error_prefix_errno("unveil");
242 return NULL;
245 __dead static void
246 usage_checkout(void)
248 fprintf(stderr, "usage: %s checkout [-p prefix] repository-path "
249 "[worktree-path]\n", getprogname());
250 exit(1);
253 static void
254 checkout_progress(void *arg, unsigned char status, const char *path)
256 char *worktree_path = arg;
258 while (path[0] == '/')
259 path++;
261 printf("%c %s/%s\n", status, worktree_path, path);
264 static const struct got_error *
265 check_cancelled(void *arg)
267 if (sigint_received || sigpipe_received)
268 return got_error(GOT_ERR_CANCELLED);
269 return NULL;
272 static const struct got_error *
273 check_ancestry(struct got_worktree *worktree, struct got_object_id *commit_id,
274 struct got_repository *repo)
276 const struct got_error *err;
277 struct got_reference *head_ref = NULL;
278 struct got_object_id *head_commit_id = NULL;
279 struct got_commit_graph *graph = NULL;
281 err = got_ref_open(&head_ref, repo,
282 got_worktree_get_head_ref_name(worktree), 0);
283 if (err)
284 return err;
286 /* TODO: Check the reflog. The head ref may have been rebased. */
287 err = got_ref_resolve(&head_commit_id, repo, head_ref);
288 if (err)
289 goto done;
291 err = got_commit_graph_open(&graph, head_commit_id, "/", 1, repo);
292 if (err)
293 goto done;
295 err = got_commit_graph_iter_start(graph, head_commit_id, repo);
296 if (err)
297 goto done;
298 for (;;) {
299 struct got_object_id *id;
301 if (sigint_received || sigpipe_received)
302 break;
304 err = got_commit_graph_iter_next(&id, graph);
305 if (err) {
306 if (err->code == GOT_ERR_ITER_COMPLETED) {
307 err = got_error(GOT_ERR_ANCESTRY);
308 break;
310 if (err->code != GOT_ERR_ITER_NEED_MORE)
311 break;
312 err = got_commit_graph_fetch_commits(graph, 1, repo);
313 if (err)
314 break;
315 else
316 continue;
318 if (id == NULL)
319 break;
320 if (got_object_id_cmp(id, commit_id) == 0)
321 break;
323 done:
324 if (head_ref)
325 got_ref_close(head_ref);
326 if (graph)
327 got_commit_graph_close(graph);
328 return err;
332 static const struct got_error *
333 cmd_checkout(int argc, char *argv[])
335 const struct got_error *error = NULL;
336 struct got_repository *repo = NULL;
337 struct got_reference *head_ref = NULL;
338 struct got_worktree *worktree = NULL;
339 char *repo_path = NULL;
340 char *worktree_path = NULL;
341 const char *path_prefix = "";
342 char *commit_id_str = NULL;
343 int ch, same_path_prefix;
345 while ((ch = getopt(argc, argv, "c:p:")) != -1) {
346 switch (ch) {
347 case 'c':
348 commit_id_str = strdup(optarg);
349 if (commit_id_str == NULL)
350 return got_error_prefix_errno("strdup");
351 break;
352 case 'p':
353 path_prefix = optarg;
354 break;
355 default:
356 usage_checkout();
357 /* NOTREACHED */
361 argc -= optind;
362 argv += optind;
364 #ifndef PROFILE
365 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
366 "unveil", NULL) == -1)
367 err(1, "pledge");
368 #endif
369 if (argc == 1) {
370 char *cwd, *base, *dotgit;
371 repo_path = realpath(argv[0], NULL);
372 if (repo_path == NULL)
373 return got_error_prefix_errno2("realpath", argv[0]);
374 cwd = getcwd(NULL, 0);
375 if (cwd == NULL) {
376 error = got_error_prefix_errno("getcwd");
377 goto done;
379 if (path_prefix[0]) {
380 base = basename(path_prefix);
381 if (base == NULL) {
382 error = got_error_prefix_errno2("basename",
383 path_prefix);
384 goto done;
386 } else {
387 base = basename(repo_path);
388 if (base == NULL) {
389 error = got_error_prefix_errno2("basename",
390 repo_path);
391 goto done;
394 dotgit = strstr(base, ".git");
395 if (dotgit)
396 *dotgit = '\0';
397 if (asprintf(&worktree_path, "%s/%s", cwd, base) == -1) {
398 error = got_error_prefix_errno("asprintf");
399 free(cwd);
400 goto done;
402 free(cwd);
403 } else if (argc == 2) {
404 repo_path = realpath(argv[0], NULL);
405 if (repo_path == NULL) {
406 error = got_error_prefix_errno2("realpath", argv[0]);
407 goto done;
409 worktree_path = realpath(argv[1], NULL);
410 if (worktree_path == NULL) {
411 error = got_error_prefix_errno2("realpath", argv[1]);
412 goto done;
414 } else
415 usage_checkout();
417 got_path_strip_trailing_slashes(repo_path);
418 got_path_strip_trailing_slashes(worktree_path);
420 error = got_repo_open(&repo, repo_path);
421 if (error != NULL)
422 goto done;
424 error = apply_unveil(got_repo_get_path(repo), 0, worktree_path, 1);
425 if (error)
426 goto done;
428 error = got_ref_open(&head_ref, repo, GOT_REF_HEAD, 0);
429 if (error != NULL)
430 goto done;
432 error = got_worktree_init(worktree_path, head_ref, path_prefix, repo);
433 if (error != NULL && !(error->code == GOT_ERR_ERRNO && errno == EEXIST))
434 goto done;
436 error = got_worktree_open(&worktree, worktree_path);
437 if (error != NULL)
438 goto done;
440 error = got_worktree_match_path_prefix(&same_path_prefix, worktree,
441 path_prefix);
442 if (error != NULL)
443 goto done;
444 if (!same_path_prefix) {
445 error = got_error(GOT_ERR_PATH_PREFIX);
446 goto done;
449 if (commit_id_str) {
450 struct got_object_id *commit_id;
451 error = got_object_resolve_id_str(&commit_id, repo,
452 commit_id_str);
453 if (error != NULL)
454 goto done;
455 error = check_ancestry(worktree, commit_id, repo);
456 if (error != NULL) {
457 free(commit_id);
458 goto done;
460 error = got_worktree_set_base_commit_id(worktree, repo,
461 commit_id);
462 free(commit_id);
463 if (error)
464 goto done;
467 error = got_worktree_checkout_files(worktree, "", repo,
468 checkout_progress, worktree_path, check_cancelled, NULL);
469 if (error != NULL)
470 goto done;
472 printf("Now shut up and hack\n");
474 done:
475 free(commit_id_str);
476 free(repo_path);
477 free(worktree_path);
478 return error;
481 __dead static void
482 usage_update(void)
484 fprintf(stderr, "usage: %s update [-c commit] [path]\n",
485 getprogname());
486 exit(1);
489 static void
490 update_progress(void *arg, unsigned char status, const char *path)
492 int *did_something = arg;
494 if (status == GOT_STATUS_EXISTS)
495 return;
497 *did_something = 1;
498 while (path[0] == '/')
499 path++;
500 printf("%c %s\n", status, path);
503 static const struct got_error *
504 cmd_update(int argc, char *argv[])
506 const struct got_error *error = NULL;
507 struct got_repository *repo = NULL;
508 struct got_worktree *worktree = NULL;
509 char *worktree_path = NULL, *path = NULL;
510 struct got_object_id *commit_id = NULL;
511 char *commit_id_str = NULL;
512 int ch, did_something = 0;
514 while ((ch = getopt(argc, argv, "c:")) != -1) {
515 switch (ch) {
516 case 'c':
517 commit_id_str = strdup(optarg);
518 if (commit_id_str == NULL)
519 return got_error_prefix_errno("strdup");
520 break;
521 default:
522 usage_update();
523 /* NOTREACHED */
527 argc -= optind;
528 argv += optind;
530 #ifndef PROFILE
531 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
532 "unveil", NULL) == -1)
533 err(1, "pledge");
534 #endif
535 worktree_path = getcwd(NULL, 0);
536 if (worktree_path == NULL) {
537 error = got_error_prefix_errno("getcwd");
538 goto done;
540 error = got_worktree_open(&worktree, worktree_path);
541 if (error)
542 goto done;
544 if (argc == 0) {
545 path = strdup("");
546 if (path == NULL) {
547 error = got_error_prefix_errno("strdup");
548 goto done;
550 } else if (argc == 1) {
551 error = got_worktree_resolve_path(&path, worktree, argv[0]);
552 if (error)
553 goto done;
554 } else
555 usage_update();
557 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree));
558 if (error != NULL)
559 goto done;
561 error = apply_unveil(got_repo_get_path(repo), 0,
562 got_worktree_get_root_path(worktree), 0);
563 if (error)
564 goto done;
566 if (commit_id_str == NULL) {
567 struct got_reference *head_ref;
568 error = got_ref_open(&head_ref, repo, GOT_REF_HEAD, 0);
569 if (error != NULL)
570 goto done;
571 error = got_ref_resolve(&commit_id, repo, head_ref);
572 if (error != NULL)
573 goto done;
574 error = got_object_id_str(&commit_id_str, commit_id);
575 if (error != NULL)
576 goto done;
577 } else {
578 error = got_object_resolve_id_str(&commit_id, repo,
579 commit_id_str);
580 if (error != NULL)
581 goto done;
584 error = check_ancestry(worktree, commit_id, repo);
585 if (error != NULL)
586 goto done;
588 if (got_object_id_cmp(got_worktree_get_base_commit_id(worktree),
589 commit_id) != 0) {
590 error = got_worktree_set_base_commit_id(worktree, repo,
591 commit_id);
592 if (error)
593 goto done;
596 error = got_worktree_checkout_files(worktree, path, repo,
597 update_progress, &did_something, check_cancelled, NULL);
598 if (error != NULL)
599 goto done;
601 if (did_something)
602 printf("Updated to commit %s\n", commit_id_str);
603 else
604 printf("Already up-to-date\n");
605 done:
606 free(worktree_path);
607 free(path);
608 free(commit_id);
609 free(commit_id_str);
610 return error;
613 static const struct got_error *
614 print_patch(struct got_commit_object *commit, struct got_object_id *id,
615 int diff_context, struct got_repository *repo)
617 const struct got_error *err = NULL;
618 struct got_tree_object *tree1 = NULL, *tree2;
619 struct got_object_qid *qid;
620 char *id_str1 = NULL, *id_str2;
622 err = got_object_open_as_tree(&tree2, repo,
623 got_object_commit_get_tree_id(commit));
624 if (err)
625 return err;
627 qid = SIMPLEQ_FIRST(got_object_commit_get_parent_ids(commit));
628 if (qid != NULL) {
629 struct got_commit_object *pcommit;
631 err = got_object_open_as_commit(&pcommit, repo, qid->id);
632 if (err)
633 return err;
635 err = got_object_open_as_tree(&tree1, repo,
636 got_object_commit_get_tree_id(pcommit));
637 got_object_commit_close(pcommit);
638 if (err)
639 return err;
641 err = got_object_id_str(&id_str1, qid->id);
642 if (err)
643 return err;
646 err = got_object_id_str(&id_str2, id);
647 if (err)
648 goto done;
650 printf("diff %s %s\n", id_str1 ? id_str1 : "/dev/null", id_str2);
651 err = got_diff_tree(tree1, tree2, "", "", diff_context, repo, stdout);
652 done:
653 if (tree1)
654 got_object_tree_close(tree1);
655 got_object_tree_close(tree2);
656 free(id_str1);
657 free(id_str2);
658 return err;
661 static char *
662 get_datestr(time_t *time, char *datebuf)
664 char *p, *s = ctime_r(time, datebuf);
665 p = strchr(s, '\n');
666 if (p)
667 *p = '\0';
668 return s;
671 static const struct got_error *
672 print_commit(struct got_commit_object *commit, struct got_object_id *id,
673 struct got_repository *repo, int show_patch, int diff_context,
674 struct got_reflist_head *refs)
676 const struct got_error *err = NULL;
677 char *id_str, *datestr, *logmsg0, *logmsg, *line;
678 char datebuf[26];
679 time_t committer_time;
680 const char *author, *committer;
681 char *refs_str = NULL;
682 struct got_reflist_entry *re;
684 SIMPLEQ_FOREACH(re, refs, entry) {
685 char *s;
686 const char *name;
687 if (got_object_id_cmp(re->id, id) != 0)
688 continue;
689 name = got_ref_get_name(re->ref);
690 if (strcmp(name, GOT_REF_HEAD) == 0)
691 continue;
692 if (strncmp(name, "refs/", 5) == 0)
693 name += 5;
694 if (strncmp(name, "got/", 4) == 0)
695 continue;
696 if (strncmp(name, "heads/", 6) == 0)
697 name += 6;
698 if (strncmp(name, "remotes/", 8) == 0)
699 name += 8;
700 s = refs_str;
701 if (asprintf(&refs_str, "%s%s%s", s ? s : "", s ? ", " : "",
702 name) == -1) {
703 err = got_error_prefix_errno("asprintf");
704 free(s);
705 break;
707 free(s);
709 err = got_object_id_str(&id_str, id);
710 if (err)
711 return err;
713 printf("-----------------------------------------------\n");
714 printf("commit %s%s%s%s\n", id_str, refs_str ? " (" : "",
715 refs_str ? refs_str : "", refs_str ? ")" : "");
716 free(id_str);
717 id_str = NULL;
718 free(refs_str);
719 refs_str = NULL;
720 printf("from: %s\n", got_object_commit_get_author(commit));
721 committer_time = got_object_commit_get_committer_time(commit);
722 datestr = get_datestr(&committer_time, datebuf);
723 printf("date: %s UTC\n", datestr);
724 author = got_object_commit_get_author(commit);
725 committer = got_object_commit_get_committer(commit);
726 if (strcmp(author, committer) != 0)
727 printf("via: %s\n", committer);
728 if (got_object_commit_get_nparents(commit) > 1) {
729 const struct got_object_id_queue *parent_ids;
730 struct got_object_qid *qid;
731 int n = 1;
732 parent_ids = got_object_commit_get_parent_ids(commit);
733 SIMPLEQ_FOREACH(qid, parent_ids, entry) {
734 err = got_object_id_str(&id_str, qid->id);
735 if (err)
736 return err;
737 printf("parent %d: %s\n", n++, id_str);
738 free(id_str);
742 logmsg0 = strdup(got_object_commit_get_logmsg(commit));
743 if (logmsg0 == NULL)
744 return got_error_prefix_errno("strdup");
746 logmsg = logmsg0;
747 do {
748 line = strsep(&logmsg, "\n");
749 if (line)
750 printf(" %s\n", line);
751 } while (line);
752 free(logmsg0);
754 if (show_patch) {
755 err = print_patch(commit, id, diff_context, repo);
756 if (err == 0)
757 printf("\n");
760 if (fflush(stdout) != 0 && err == NULL)
761 err = got_error_prefix_errno("fflush");
762 return err;
765 static const struct got_error *
766 print_commits(struct got_object_id *root_id, struct got_repository *repo,
767 char *path, int show_patch, int diff_context, int limit,
768 int first_parent_traversal, struct got_reflist_head *refs)
770 const struct got_error *err;
771 struct got_commit_graph *graph;
773 err = got_commit_graph_open(&graph, root_id, path,
774 first_parent_traversal, repo);
775 if (err)
776 return err;
777 err = got_commit_graph_iter_start(graph, root_id, repo);
778 if (err)
779 goto done;
780 for (;;) {
781 struct got_commit_object *commit;
782 struct got_object_id *id;
784 if (sigint_received || sigpipe_received)
785 break;
787 err = got_commit_graph_iter_next(&id, graph);
788 if (err) {
789 if (err->code == GOT_ERR_ITER_COMPLETED) {
790 err = NULL;
791 break;
793 if (err->code != GOT_ERR_ITER_NEED_MORE)
794 break;
795 err = got_commit_graph_fetch_commits(graph, 1, repo);
796 if (err)
797 break;
798 else
799 continue;
801 if (id == NULL)
802 break;
804 err = got_object_open_as_commit(&commit, repo, id);
805 if (err)
806 break;
807 err = print_commit(commit, id, repo, show_patch, diff_context,
808 refs);
809 got_object_commit_close(commit);
810 if (err || (limit && --limit == 0))
811 break;
813 done:
814 got_commit_graph_close(graph);
815 return err;
818 __dead static void
819 usage_log(void)
821 fprintf(stderr, "usage: %s log [-c commit] [-C number] [-f] [ -l N ] [-p] "
822 "[-r repository-path] [path]\n", getprogname());
823 exit(1);
826 static const struct got_error *
827 cmd_log(int argc, char *argv[])
829 const struct got_error *error;
830 struct got_repository *repo = NULL;
831 struct got_worktree *worktree = NULL;
832 struct got_commit_object *commit = NULL;
833 struct got_object_id *id = NULL;
834 char *repo_path = NULL, *path = NULL, *cwd = NULL, *in_repo_path = NULL;
835 char *start_commit = NULL;
836 int diff_context = 3, ch;
837 int show_patch = 0, limit = 0, first_parent_traversal = 0;
838 const char *errstr;
839 struct got_reflist_head refs;
841 SIMPLEQ_INIT(&refs);
843 #ifndef PROFILE
844 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
845 NULL)
846 == -1)
847 err(1, "pledge");
848 #endif
850 while ((ch = getopt(argc, argv, "pc:C:l:fr:")) != -1) {
851 switch (ch) {
852 case 'p':
853 show_patch = 1;
854 break;
855 case 'c':
856 start_commit = optarg;
857 break;
858 case 'C':
859 diff_context = strtonum(optarg, 0, GOT_DIFF_MAX_CONTEXT,
860 &errstr);
861 if (errstr != NULL)
862 err(1, "-C option %s", errstr);
863 break;
864 case 'l':
865 limit = strtonum(optarg, 1, INT_MAX, &errstr);
866 if (errstr != NULL)
867 err(1, "-l option %s", errstr);
868 break;
869 case 'f':
870 first_parent_traversal = 1;
871 break;
872 case 'r':
873 repo_path = realpath(optarg, NULL);
874 if (repo_path == NULL)
875 err(1, "-r option");
876 got_path_strip_trailing_slashes(repo_path);
877 break;
878 default:
879 usage_log();
880 /* NOTREACHED */
884 argc -= optind;
885 argv += optind;
887 cwd = getcwd(NULL, 0);
888 if (cwd == NULL) {
889 error = got_error_prefix_errno("getcwd");
890 goto done;
893 error = got_worktree_open(&worktree, cwd);
894 if (error && error->code != GOT_ERR_NOT_WORKTREE)
895 goto done;
896 error = NULL;
898 if (argc == 0) {
899 path = strdup("");
900 if (path == NULL) {
901 error = got_error_prefix_errno("strdup");
902 goto done;
904 } else if (argc == 1) {
905 if (worktree) {
906 error = got_worktree_resolve_path(&path, worktree,
907 argv[0]);
908 if (error)
909 goto done;
910 } else {
911 path = strdup(argv[0]);
912 if (path == NULL) {
913 error = got_error_prefix_errno("strdup");
914 goto done;
917 } else
918 usage_log();
920 if (repo_path == NULL) {
921 repo_path = worktree ?
922 strdup(got_worktree_get_repo_path(worktree)) : strdup(cwd);
924 if (repo_path == NULL) {
925 error = got_error_prefix_errno("strdup");
926 goto done;
929 error = got_repo_open(&repo, repo_path);
930 if (error != NULL)
931 goto done;
933 error = apply_unveil(got_repo_get_path(repo), 1,
934 worktree ? got_worktree_get_root_path(worktree) : NULL, 0);
935 if (error)
936 goto done;
938 if (start_commit == NULL) {
939 struct got_reference *head_ref;
940 error = got_ref_open(&head_ref, repo, GOT_REF_HEAD, 0);
941 if (error != NULL)
942 return error;
943 error = got_ref_resolve(&id, repo, head_ref);
944 got_ref_close(head_ref);
945 if (error != NULL)
946 return error;
947 error = got_object_open_as_commit(&commit, repo, id);
948 } else {
949 struct got_reference *ref;
950 error = got_ref_open(&ref, repo, start_commit, 0);
951 if (error == NULL) {
952 int obj_type;
953 error = got_ref_resolve(&id, repo, ref);
954 got_ref_close(ref);
955 if (error != NULL)
956 goto done;
957 error = got_object_get_type(&obj_type, repo, id);
958 if (error != NULL)
959 goto done;
960 if (obj_type == GOT_OBJ_TYPE_TAG) {
961 struct got_tag_object *tag;
962 error = got_object_open_as_tag(&tag, repo, id);
963 if (error != NULL)
964 goto done;
965 if (got_object_tag_get_object_type(tag) !=
966 GOT_OBJ_TYPE_COMMIT) {
967 got_object_tag_close(tag);
968 error = got_error(GOT_ERR_OBJ_TYPE);
969 goto done;
971 free(id);
972 id = got_object_id_dup(
973 got_object_tag_get_object_id(tag));
974 if (id == NULL)
975 error = got_error_prefix_errno(
976 "got_object_id_dup");
977 got_object_tag_close(tag);
978 if (error)
979 goto done;
980 } else if (obj_type != GOT_OBJ_TYPE_COMMIT) {
981 error = got_error(GOT_ERR_OBJ_TYPE);
982 goto done;
984 error = got_object_open_as_commit(&commit, repo, id);
985 if (error != NULL)
986 goto done;
988 if (commit == NULL) {
989 error = got_object_resolve_id_str(&id, repo,
990 start_commit);
991 if (error != NULL)
992 return error;
995 if (error != NULL)
996 goto done;
998 error = got_repo_map_path(&in_repo_path, repo, path, 1);
999 if (error != NULL)
1000 goto done;
1001 if (in_repo_path) {
1002 free(path);
1003 path = in_repo_path;
1006 error = got_ref_list(&refs, repo);
1007 if (error)
1008 goto done;
1010 error = print_commits(id, repo, path, show_patch,
1011 diff_context, limit, first_parent_traversal, &refs);
1012 done:
1013 free(path);
1014 free(repo_path);
1015 free(cwd);
1016 free(id);
1017 if (worktree)
1018 got_worktree_close(worktree);
1019 if (repo) {
1020 const struct got_error *repo_error;
1021 repo_error = got_repo_close(repo);
1022 if (error == NULL)
1023 error = repo_error;
1025 got_ref_list_free(&refs);
1026 return error;
1029 __dead static void
1030 usage_diff(void)
1032 fprintf(stderr, "usage: %s diff [-C number] [-r repository-path] "
1033 "[object1 object2 | path]\n", getprogname());
1034 exit(1);
1037 struct print_diff_arg {
1038 struct got_repository *repo;
1039 struct got_worktree *worktree;
1040 int diff_context;
1041 const char *id_str;
1042 int header_shown;
1045 static const struct got_error *
1046 print_diff(void *arg, unsigned char status, const char *path,
1047 struct got_object_id *id)
1049 struct print_diff_arg *a = arg;
1050 const struct got_error *err = NULL;
1051 struct got_blob_object *blob1 = NULL;
1052 FILE *f2 = NULL;
1053 char *abspath = NULL;
1054 struct stat sb;
1056 if (status != GOT_STATUS_MODIFY && status != GOT_STATUS_ADD &&
1057 status != GOT_STATUS_DELETE && status != GOT_STATUS_CONFLICT)
1058 return NULL;
1060 if (!a->header_shown) {
1061 printf("diff %s %s\n", a->id_str,
1062 got_worktree_get_root_path(a->worktree));
1063 a->header_shown = 1;
1066 if (status != GOT_STATUS_ADD) {
1067 err = got_object_open_as_blob(&blob1, a->repo, id, 8192);
1068 if (err)
1069 goto done;
1073 if (status != GOT_STATUS_DELETE) {
1074 if (asprintf(&abspath, "%s/%s",
1075 got_worktree_get_root_path(a->worktree), path) == -1) {
1076 err = got_error_prefix_errno("asprintf");
1077 goto done;
1080 f2 = fopen(abspath, "r");
1081 if (f2 == NULL) {
1082 err = got_error_prefix_errno2("fopen", abspath);
1083 goto done;
1085 if (lstat(abspath, &sb) == -1) {
1086 err = got_error_prefix_errno2("lstat", abspath);
1087 goto done;
1089 } else
1090 sb.st_size = 0;
1092 err = got_diff_blob_file(blob1, f2, sb.st_size, path, a->diff_context,
1093 stdout);
1094 done:
1095 if (blob1)
1096 got_object_blob_close(blob1);
1097 if (f2 && fclose(f2) != 0 && err == NULL)
1098 err = got_error_prefix_errno("fclose");
1099 free(abspath);
1100 return err;
1103 static const struct got_error *
1104 cmd_diff(int argc, char *argv[])
1106 const struct got_error *error;
1107 struct got_repository *repo = NULL;
1108 struct got_worktree *worktree = NULL;
1109 char *cwd = NULL, *repo_path = NULL;
1110 struct got_object_id *id1 = NULL, *id2 = NULL;
1111 char *id_str1 = NULL, *id_str2 = NULL;
1112 int type1, type2;
1113 int diff_context = 3, ch;
1114 const char *errstr;
1115 char *path = NULL;
1117 #ifndef PROFILE
1118 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
1119 NULL) == -1)
1120 err(1, "pledge");
1121 #endif
1123 while ((ch = getopt(argc, argv, "C:r:")) != -1) {
1124 switch (ch) {
1125 case 'C':
1126 diff_context = strtonum(optarg, 1, INT_MAX, &errstr);
1127 if (errstr != NULL)
1128 err(1, "-C option %s", errstr);
1129 break;
1130 case 'r':
1131 repo_path = realpath(optarg, NULL);
1132 if (repo_path == NULL)
1133 err(1, "-r option");
1134 got_path_strip_trailing_slashes(repo_path);
1135 break;
1136 default:
1137 usage_diff();
1138 /* NOTREACHED */
1142 argc -= optind;
1143 argv += optind;
1145 cwd = getcwd(NULL, 0);
1146 if (cwd == NULL) {
1147 error = got_error_prefix_errno("getcwd");
1148 goto done;
1150 error = got_worktree_open(&worktree, cwd);
1151 if (error && error->code != GOT_ERR_NOT_WORKTREE)
1152 goto done;
1153 if (argc <= 1) {
1154 if (worktree == NULL) {
1155 error = got_error(GOT_ERR_NOT_WORKTREE);
1156 goto done;
1158 if (repo_path)
1159 errx(1,
1160 "-r option can't be used when diffing a work tree");
1161 repo_path = strdup(got_worktree_get_repo_path(worktree));
1162 if (repo_path == NULL) {
1163 error = got_error_prefix_errno("strdup");
1164 goto done;
1166 if (argc == 1) {
1167 error = got_worktree_resolve_path(&path, worktree,
1168 argv[0]);
1169 if (error)
1170 goto done;
1171 } else {
1172 path = strdup("");
1173 if (path == NULL) {
1174 error = got_error_prefix_errno("strdup");
1175 goto done;
1178 } else if (argc == 2) {
1179 id_str1 = argv[0];
1180 id_str2 = argv[1];
1181 } else
1182 usage_diff();
1184 if (repo_path == NULL) {
1185 repo_path = getcwd(NULL, 0);
1186 if (repo_path == NULL)
1187 return got_error_prefix_errno("getcwd");
1190 error = got_repo_open(&repo, repo_path);
1191 free(repo_path);
1192 if (error != NULL)
1193 goto done;
1195 error = apply_unveil(got_repo_get_path(repo), 1,
1196 worktree ? got_worktree_get_root_path(worktree) : NULL, 0);
1197 if (error)
1198 goto done;
1200 if (worktree) {
1201 struct print_diff_arg arg;
1202 char *id_str;
1203 error = got_object_id_str(&id_str,
1204 got_worktree_get_base_commit_id(worktree));
1205 if (error)
1206 goto done;
1207 arg.repo = repo;
1208 arg.worktree = worktree;
1209 arg.diff_context = diff_context;
1210 arg.id_str = id_str;
1211 arg.header_shown = 0;
1213 error = got_worktree_status(worktree, path, repo, print_diff,
1214 &arg, check_cancelled, NULL);
1215 free(id_str);
1216 goto done;
1219 error = got_object_resolve_id_str(&id1, repo, id_str1);
1220 if (error)
1221 goto done;
1223 error = got_object_resolve_id_str(&id2, repo, id_str2);
1224 if (error)
1225 goto done;
1227 error = got_object_get_type(&type1, repo, id1);
1228 if (error)
1229 goto done;
1231 error = got_object_get_type(&type2, repo, id2);
1232 if (error)
1233 goto done;
1235 if (type1 != type2) {
1236 error = got_error(GOT_ERR_OBJ_TYPE);
1237 goto done;
1240 switch (type1) {
1241 case GOT_OBJ_TYPE_BLOB:
1242 error = got_diff_objects_as_blobs(id1, id2, NULL, NULL,
1243 diff_context, repo, stdout);
1244 break;
1245 case GOT_OBJ_TYPE_TREE:
1246 error = got_diff_objects_as_trees(id1, id2, "", "",
1247 diff_context, repo, stdout);
1248 break;
1249 case GOT_OBJ_TYPE_COMMIT:
1250 printf("diff %s %s\n", id_str1 ? id_str1 : "/dev/null",
1251 id_str2);
1252 error = got_diff_objects_as_commits(id1, id2, diff_context,
1253 repo, stdout);
1254 break;
1255 default:
1256 error = got_error(GOT_ERR_OBJ_TYPE);
1259 done:
1260 free(id1);
1261 free(id2);
1262 free(path);
1263 if (worktree)
1264 got_worktree_close(worktree);
1265 if (repo) {
1266 const struct got_error *repo_error;
1267 repo_error = got_repo_close(repo);
1268 if (error == NULL)
1269 error = repo_error;
1271 return error;
1274 __dead static void
1275 usage_blame(void)
1277 fprintf(stderr,
1278 "usage: %s blame [-c commit] [-r repository-path] path\n",
1279 getprogname());
1280 exit(1);
1283 static const struct got_error *
1284 cmd_blame(int argc, char *argv[])
1286 const struct got_error *error;
1287 struct got_repository *repo = NULL;
1288 struct got_worktree *worktree = NULL;
1289 char *path, *cwd = NULL, *repo_path = NULL, *in_repo_path = NULL;
1290 struct got_object_id *commit_id = NULL;
1291 char *commit_id_str = NULL;
1292 int ch;
1294 #ifndef PROFILE
1295 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
1296 NULL) == -1)
1297 err(1, "pledge");
1298 #endif
1300 while ((ch = getopt(argc, argv, "c:r:")) != -1) {
1301 switch (ch) {
1302 case 'c':
1303 commit_id_str = optarg;
1304 break;
1305 case 'r':
1306 repo_path = realpath(optarg, NULL);
1307 if (repo_path == NULL)
1308 err(1, "-r option");
1309 got_path_strip_trailing_slashes(repo_path);
1310 break;
1311 default:
1312 usage_blame();
1313 /* NOTREACHED */
1317 argc -= optind;
1318 argv += optind;
1320 if (argc == 1)
1321 path = argv[0];
1322 else
1323 usage_blame();
1325 cwd = getcwd(NULL, 0);
1326 if (cwd == NULL) {
1327 error = got_error_prefix_errno("getcwd");
1328 goto done;
1330 if (repo_path == NULL) {
1331 error = got_worktree_open(&worktree, cwd);
1332 if (error && error->code != GOT_ERR_NOT_WORKTREE)
1333 goto done;
1334 else
1335 error = NULL;
1336 if (worktree) {
1337 repo_path =
1338 strdup(got_worktree_get_repo_path(worktree));
1339 if (repo_path == NULL)
1340 error = got_error_prefix_errno("strdup");
1341 if (error)
1342 goto done;
1343 } else {
1344 repo_path = strdup(cwd);
1345 if (repo_path == NULL) {
1346 error = got_error_prefix_errno("strdup");
1347 goto done;
1352 error = got_repo_open(&repo, repo_path);
1353 if (error != NULL)
1354 goto done;
1356 error = apply_unveil(got_repo_get_path(repo), 1, NULL, 0);
1357 if (error)
1358 goto done;
1360 if (worktree) {
1361 const char *prefix = got_worktree_get_path_prefix(worktree);
1362 char *p, *worktree_subdir = cwd +
1363 strlen(got_worktree_get_root_path(worktree));
1364 if (asprintf(&p, "%s%s%s%s%s",
1365 prefix, (strcmp(prefix, "/") != 0) ? "/" : "",
1366 worktree_subdir, worktree_subdir[0] ? "/" : "",
1367 path) == -1) {
1368 error = got_error_prefix_errno("asprintf");
1369 goto done;
1371 error = got_repo_map_path(&in_repo_path, repo, p, 0);
1372 free(p);
1373 } else {
1374 error = got_repo_map_path(&in_repo_path, repo, path, 1);
1376 if (error)
1377 goto done;
1379 if (commit_id_str == NULL) {
1380 struct got_reference *head_ref;
1381 error = got_ref_open(&head_ref, repo, GOT_REF_HEAD, 0);
1382 if (error != NULL)
1383 goto done;
1384 error = got_ref_resolve(&commit_id, repo, head_ref);
1385 got_ref_close(head_ref);
1386 if (error != NULL)
1387 goto done;
1388 } else {
1389 error = got_object_resolve_id_str(&commit_id, repo,
1390 commit_id_str);
1391 if (error != NULL)
1392 goto done;
1395 error = got_blame(in_repo_path, commit_id, repo, stdout);
1396 done:
1397 free(in_repo_path);
1398 free(repo_path);
1399 free(cwd);
1400 free(commit_id);
1401 if (worktree)
1402 got_worktree_close(worktree);
1403 if (repo) {
1404 const struct got_error *repo_error;
1405 repo_error = got_repo_close(repo);
1406 if (error == NULL)
1407 error = repo_error;
1409 return error;
1412 __dead static void
1413 usage_tree(void)
1415 fprintf(stderr,
1416 "usage: %s tree [-c commit] [-r repository-path] [-iR] path\n",
1417 getprogname());
1418 exit(1);
1421 static void
1422 print_entry(struct got_tree_entry *te, const char *id, const char *path,
1423 const char *root_path)
1425 int is_root_path = (strcmp(path, root_path) == 0);
1427 path += strlen(root_path);
1428 while (path[0] == '/')
1429 path++;
1431 printf("%s%s%s%s%s\n", id ? id : "", path,
1432 is_root_path ? "" : "/", te->name,
1433 S_ISDIR(te->mode) ? "/" : ((te->mode & S_IXUSR) ? "*" : ""));
1436 static const struct got_error *
1437 print_tree(const char *path, struct got_object_id *commit_id,
1438 int show_ids, int recurse, const char *root_path,
1439 struct got_repository *repo)
1441 const struct got_error *err = NULL;
1442 struct got_object_id *tree_id = NULL;
1443 struct got_tree_object *tree = NULL;
1444 const struct got_tree_entries *entries;
1445 struct got_tree_entry *te;
1447 err = got_object_id_by_path(&tree_id, repo, commit_id, path);
1448 if (err)
1449 goto done;
1451 err = got_object_open_as_tree(&tree, repo, tree_id);
1452 if (err)
1453 goto done;
1454 entries = got_object_tree_get_entries(tree);
1455 te = SIMPLEQ_FIRST(&entries->head);
1456 while (te) {
1457 char *id = NULL;
1459 if (sigint_received || sigpipe_received)
1460 break;
1462 if (show_ids) {
1463 char *id_str;
1464 err = got_object_id_str(&id_str, te->id);
1465 if (err)
1466 goto done;
1467 if (asprintf(&id, "%s ", id_str) == -1) {
1468 err = got_error_prefix_errno("asprintf");
1469 free(id_str);
1470 goto done;
1472 free(id_str);
1474 print_entry(te, id, path, root_path);
1475 free(id);
1477 if (recurse && S_ISDIR(te->mode)) {
1478 char *child_path;
1479 if (asprintf(&child_path, "%s%s%s", path,
1480 path[0] == '/' && path[1] == '\0' ? "" : "/",
1481 te->name) == -1) {
1482 err = got_error_prefix_errno("asprintf");
1483 goto done;
1485 err = print_tree(child_path, commit_id, show_ids, 1,
1486 root_path, repo);
1487 free(child_path);
1488 if (err)
1489 goto done;
1492 te = SIMPLEQ_NEXT(te, entry);
1494 done:
1495 if (tree)
1496 got_object_tree_close(tree);
1497 free(tree_id);
1498 return err;
1501 static const struct got_error *
1502 cmd_tree(int argc, char *argv[])
1504 const struct got_error *error;
1505 struct got_repository *repo = NULL;
1506 struct got_worktree *worktree = NULL;
1507 const char *path;
1508 char *cwd = NULL, *repo_path = NULL, *in_repo_path = NULL;
1509 struct got_object_id *commit_id = NULL;
1510 char *commit_id_str = NULL;
1511 int show_ids = 0, recurse = 0;
1512 int ch;
1514 #ifndef PROFILE
1515 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
1516 NULL) == -1)
1517 err(1, "pledge");
1518 #endif
1520 while ((ch = getopt(argc, argv, "c:r:iR")) != -1) {
1521 switch (ch) {
1522 case 'c':
1523 commit_id_str = optarg;
1524 break;
1525 case 'r':
1526 repo_path = realpath(optarg, NULL);
1527 if (repo_path == NULL)
1528 err(1, "-r option");
1529 got_path_strip_trailing_slashes(repo_path);
1530 break;
1531 case 'i':
1532 show_ids = 1;
1533 break;
1534 case 'R':
1535 recurse = 1;
1536 break;
1537 default:
1538 usage_tree();
1539 /* NOTREACHED */
1543 argc -= optind;
1544 argv += optind;
1546 if (argc == 1)
1547 path = argv[0];
1548 else if (argc > 1)
1549 usage_tree();
1550 else
1551 path = NULL;
1553 cwd = getcwd(NULL, 0);
1554 if (cwd == NULL) {
1555 error = got_error_prefix_errno("getcwd");
1556 goto done;
1558 if (repo_path == NULL) {
1559 error = got_worktree_open(&worktree, cwd);
1560 if (error && error->code != GOT_ERR_NOT_WORKTREE)
1561 goto done;
1562 else
1563 error = NULL;
1564 if (worktree) {
1565 repo_path =
1566 strdup(got_worktree_get_repo_path(worktree));
1567 if (repo_path == NULL)
1568 error = got_error_prefix_errno("strdup");
1569 if (error)
1570 goto done;
1571 } else {
1572 repo_path = strdup(cwd);
1573 if (repo_path == NULL) {
1574 error = got_error_prefix_errno("strdup");
1575 goto done;
1580 error = got_repo_open(&repo, repo_path);
1581 if (error != NULL)
1582 goto done;
1584 error = apply_unveil(got_repo_get_path(repo), 1, NULL, 0);
1585 if (error)
1586 goto done;
1588 if (path == NULL) {
1589 if (worktree) {
1590 char *p, *worktree_subdir = cwd +
1591 strlen(got_worktree_get_root_path(worktree));
1592 if (asprintf(&p, "%s/%s",
1593 got_worktree_get_path_prefix(worktree),
1594 worktree_subdir) == -1) {
1595 error = got_error_prefix_errno("asprintf");
1596 goto done;
1598 error = got_repo_map_path(&in_repo_path, repo, p, 1);
1599 free(p);
1600 if (error)
1601 goto done;
1602 } else
1603 path = "/";
1605 if (in_repo_path == NULL) {
1606 error = got_repo_map_path(&in_repo_path, repo, path, 1);
1607 if (error != NULL)
1608 goto done;
1611 if (commit_id_str == NULL) {
1612 struct got_reference *head_ref;
1613 error = got_ref_open(&head_ref, repo, GOT_REF_HEAD, 0);
1614 if (error != NULL)
1615 goto done;
1616 error = got_ref_resolve(&commit_id, repo, head_ref);
1617 got_ref_close(head_ref);
1618 if (error != NULL)
1619 goto done;
1620 } else {
1621 error = got_object_resolve_id_str(&commit_id, repo,
1622 commit_id_str);
1623 if (error != NULL)
1624 goto done;
1627 error = print_tree(in_repo_path, commit_id, show_ids, recurse,
1628 in_repo_path, repo);
1629 done:
1630 free(in_repo_path);
1631 free(repo_path);
1632 free(cwd);
1633 free(commit_id);
1634 if (worktree)
1635 got_worktree_close(worktree);
1636 if (repo) {
1637 const struct got_error *repo_error;
1638 repo_error = got_repo_close(repo);
1639 if (error == NULL)
1640 error = repo_error;
1642 return error;
1645 __dead static void
1646 usage_status(void)
1648 fprintf(stderr, "usage: %s status [path]\n", getprogname());
1649 exit(1);
1652 static const struct got_error *
1653 print_status(void *arg, unsigned char status, const char *path,
1654 struct got_object_id *id)
1656 printf("%c %s\n", status, path);
1657 return NULL;
1660 static const struct got_error *
1661 cmd_status(int argc, char *argv[])
1663 const struct got_error *error = NULL;
1664 struct got_repository *repo = NULL;
1665 struct got_worktree *worktree = NULL;
1666 char *cwd = NULL, *path = NULL;
1667 int ch;
1669 while ((ch = getopt(argc, argv, "")) != -1) {
1670 switch (ch) {
1671 default:
1672 usage_status();
1673 /* NOTREACHED */
1677 argc -= optind;
1678 argv += optind;
1680 #ifndef PROFILE
1681 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
1682 NULL) == -1)
1683 err(1, "pledge");
1684 #endif
1685 cwd = getcwd(NULL, 0);
1686 if (cwd == NULL) {
1687 error = got_error_prefix_errno("getcwd");
1688 goto done;
1691 error = got_worktree_open(&worktree, cwd);
1692 if (error != NULL)
1693 goto done;
1695 if (argc == 0) {
1696 path = strdup("");
1697 if (path == NULL) {
1698 error = got_error_prefix_errno("strdup");
1699 goto done;
1701 } else if (argc == 1) {
1702 error = got_worktree_resolve_path(&path, worktree, argv[0]);
1703 if (error)
1704 goto done;
1705 } else
1706 usage_status();
1708 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree));
1709 if (error != NULL)
1710 goto done;
1712 error = apply_unveil(got_repo_get_path(repo), 1,
1713 got_worktree_get_root_path(worktree), 0);
1714 if (error)
1715 goto done;
1717 error = got_worktree_status(worktree, path, repo, print_status, NULL,
1718 check_cancelled, NULL);
1719 done:
1720 free(cwd);
1721 free(path);
1722 return error;
1725 __dead static void
1726 usage_ref(void)
1728 fprintf(stderr,
1729 "usage: %s ref [-r repository] -l | -d name | name object\n",
1730 getprogname());
1731 exit(1);
1734 static const struct got_error *
1735 list_refs(struct got_repository *repo)
1737 static const struct got_error *err = NULL;
1738 struct got_reflist_head refs;
1739 struct got_reflist_entry *re;
1741 SIMPLEQ_INIT(&refs);
1742 err = got_ref_list(&refs, repo);
1743 if (err)
1744 return err;
1746 SIMPLEQ_FOREACH(re, &refs, entry) {
1747 char *refstr;
1748 refstr = got_ref_to_str(re->ref);
1749 if (refstr == NULL)
1750 return got_error_prefix_errno("got_ref_to_str");
1751 printf("%s: %s\n", got_ref_get_name(re->ref), refstr);
1752 free(refstr);
1755 got_ref_list_free(&refs);
1756 return NULL;
1759 static const struct got_error *
1760 delete_ref(struct got_repository *repo, const char *refname)
1762 const struct got_error *err = NULL;
1763 struct got_reference *ref;
1765 err = got_ref_open(&ref, repo, refname, 0);
1766 if (err)
1767 return err;
1769 err = got_ref_delete(ref, repo);
1770 got_ref_close(ref);
1771 return err;
1774 static const struct got_error *
1775 add_ref(struct got_repository *repo, const char *refname, const char *id_str)
1777 const struct got_error *err = NULL;
1778 struct got_object_id *id;
1779 struct got_reference *ref = NULL;
1781 err = got_object_resolve_id_str(&id, repo, id_str);
1782 if (err)
1783 return err;
1785 err = got_ref_alloc(&ref, refname, id);
1786 if (err)
1787 goto done;
1789 err = got_ref_write(ref, repo);
1790 done:
1791 if (ref)
1792 got_ref_close(ref);
1793 free(id);
1794 return err;
1797 static const struct got_error *
1798 cmd_ref(int argc, char *argv[])
1800 const struct got_error *error = NULL;
1801 struct got_repository *repo = NULL;
1802 struct got_worktree *worktree = NULL;
1803 char *cwd = NULL, *repo_path = NULL;
1804 int ch, do_list = 0;
1805 const char *delref = NULL;
1807 /* TODO: Add -s option for adding symbolic references. */
1808 while ((ch = getopt(argc, argv, "d:r:l")) != -1) {
1809 switch (ch) {
1810 case 'd':
1811 delref = optarg;
1812 break;
1813 case 'r':
1814 repo_path = realpath(optarg, NULL);
1815 if (repo_path == NULL)
1816 err(1, "-r option");
1817 got_path_strip_trailing_slashes(repo_path);
1818 break;
1819 case 'l':
1820 do_list = 1;
1821 break;
1822 default:
1823 usage_ref();
1824 /* NOTREACHED */
1828 if (do_list && delref)
1829 errx(1, "-l and -d options are mutually exclusive\n");
1831 argc -= optind;
1832 argv += optind;
1834 if (do_list || delref) {
1835 if (argc > 0)
1836 usage_ref();
1837 } else if (argc != 2)
1838 usage_ref();
1840 #ifndef PROFILE
1841 if (do_list) {
1842 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
1843 NULL) == -1)
1844 err(1, "pledge");
1845 } else {
1846 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
1847 "sendfd unveil", NULL) == -1)
1848 err(1, "pledge");
1850 #endif
1851 cwd = getcwd(NULL, 0);
1852 if (cwd == NULL) {
1853 error = got_error_prefix_errno("getcwd");
1854 goto done;
1857 if (repo_path == NULL) {
1858 error = got_worktree_open(&worktree, cwd);
1859 if (error && error->code != GOT_ERR_NOT_WORKTREE)
1860 goto done;
1861 else
1862 error = NULL;
1863 if (worktree) {
1864 repo_path =
1865 strdup(got_worktree_get_repo_path(worktree));
1866 if (repo_path == NULL)
1867 error = got_error_prefix_errno("strdup");
1868 if (error)
1869 goto done;
1870 } else {
1871 repo_path = strdup(cwd);
1872 if (repo_path == NULL) {
1873 error = got_error_prefix_errno("strdup");
1874 goto done;
1879 error = got_repo_open(&repo, repo_path);
1880 if (error != NULL)
1881 goto done;
1883 error = apply_unveil(got_repo_get_path(repo), do_list,
1884 worktree ? got_worktree_get_root_path(worktree) : NULL, 0);
1885 if (error)
1886 goto done;
1888 if (do_list)
1889 error = list_refs(repo);
1890 else if (delref)
1891 error = delete_ref(repo, delref);
1892 else
1893 error = add_ref(repo, argv[0], argv[1]);
1894 done:
1895 if (repo)
1896 got_repo_close(repo);
1897 if (worktree)
1898 got_worktree_close(worktree);
1899 free(cwd);
1900 free(repo_path);
1901 return error;
1904 __dead static void
1905 usage_add(void)
1907 fprintf(stderr, "usage: %s add file-path ...\n", getprogname());
1908 exit(1);
1911 static const struct got_error *
1912 cmd_add(int argc, char *argv[])
1914 const struct got_error *error = NULL;
1915 struct got_repository *repo = NULL;
1916 struct got_worktree *worktree = NULL;
1917 char *cwd = NULL;
1918 struct got_pathlist_head paths;
1919 struct got_pathlist_entry *pe;
1920 int ch, x;
1922 TAILQ_INIT(&paths);
1924 while ((ch = getopt(argc, argv, "")) != -1) {
1925 switch (ch) {
1926 default:
1927 usage_add();
1928 /* NOTREACHED */
1932 argc -= optind;
1933 argv += optind;
1935 if (argc < 1)
1936 usage_add();
1938 /* make sure each file exists before doing anything halfway */
1939 for (x = 0; x < argc; x++) {
1940 char *path = realpath(argv[x], NULL);
1941 if (path == NULL) {
1942 error = got_error_prefix_errno2("realpath", argv[x]);
1943 goto done;
1945 free(path);
1948 cwd = getcwd(NULL, 0);
1949 if (cwd == NULL) {
1950 error = got_error_prefix_errno("getcwd");
1951 goto done;
1954 error = got_worktree_open(&worktree, cwd);
1955 if (error)
1956 goto done;
1958 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree));
1959 if (error != NULL)
1960 goto done;
1962 error = apply_unveil(got_repo_get_path(repo), 1,
1963 got_worktree_get_root_path(worktree), 0);
1964 if (error)
1965 goto done;
1967 for (x = 0; x < argc; x++) {
1968 char *path = realpath(argv[x], NULL);
1969 if (path == NULL) {
1970 error = got_error_prefix_errno2("realpath", argv[x]);
1971 goto done;
1974 got_path_strip_trailing_slashes(path);
1975 error = got_pathlist_insert(&pe, &paths, path, NULL);
1976 if (error) {
1977 free(path);
1978 goto done;
1981 error = got_worktree_schedule_add(worktree, &paths, print_status,
1982 NULL, repo);
1983 done:
1984 if (repo)
1985 got_repo_close(repo);
1986 if (worktree)
1987 got_worktree_close(worktree);
1988 TAILQ_FOREACH(pe, &paths, entry)
1989 free((char *)pe->path);
1990 got_pathlist_free(&paths);
1991 free(cwd);
1992 return error;
1995 __dead static void
1996 usage_rm(void)
1998 fprintf(stderr, "usage: %s rm [-f] file-path\n", getprogname());
1999 exit(1);
2002 static const struct got_error *
2003 cmd_rm(int argc, char *argv[])
2005 const struct got_error *error = NULL;
2006 struct got_worktree *worktree = NULL;
2007 struct got_repository *repo = NULL;
2008 char *cwd = NULL, *path = NULL;
2009 int ch, delete_local_mods = 0;
2011 while ((ch = getopt(argc, argv, "f")) != -1) {
2012 switch (ch) {
2013 case 'f':
2014 delete_local_mods = 1;
2015 break;
2016 default:
2017 usage_add();
2018 /* NOTREACHED */
2022 argc -= optind;
2023 argv += optind;
2025 if (argc != 1)
2026 usage_rm();
2028 path = realpath(argv[0], NULL);
2029 if (path == NULL) {
2030 error = got_error_prefix_errno2("realpath", argv[0]);
2031 goto done;
2033 got_path_strip_trailing_slashes(path);
2035 cwd = getcwd(NULL, 0);
2036 if (cwd == NULL) {
2037 error = got_error_prefix_errno("getcwd");
2038 goto done;
2040 error = got_worktree_open(&worktree, cwd);
2041 if (error)
2042 goto done;
2044 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree));
2045 if (error)
2046 goto done;
2048 error = apply_unveil(got_repo_get_path(repo), 1,
2049 got_worktree_get_root_path(worktree), 0);
2050 if (error)
2051 goto done;
2053 error = got_worktree_schedule_delete(worktree, path, delete_local_mods,
2054 print_status, NULL, repo);
2055 if (error)
2056 goto done;
2057 done:
2058 if (repo)
2059 got_repo_close(repo);
2060 if (worktree)
2061 got_worktree_close(worktree);
2062 free(path);
2063 free(cwd);
2064 return error;
2067 __dead static void
2068 usage_revert(void)
2070 fprintf(stderr, "usage: %s revert file-path\n", getprogname());
2071 exit(1);
2074 static void
2075 revert_progress(void *arg, unsigned char status, const char *path)
2077 while (path[0] == '/')
2078 path++;
2079 printf("%c %s\n", status, path);
2082 static const struct got_error *
2083 cmd_revert(int argc, char *argv[])
2085 const struct got_error *error = NULL;
2086 struct got_worktree *worktree = NULL;
2087 struct got_repository *repo = NULL;
2088 char *cwd = NULL, *path = NULL;
2089 int ch;
2091 while ((ch = getopt(argc, argv, "")) != -1) {
2092 switch (ch) {
2093 default:
2094 usage_revert();
2095 /* NOTREACHED */
2099 argc -= optind;
2100 argv += optind;
2102 if (argc != 1)
2103 usage_revert();
2105 path = realpath(argv[0], NULL);
2106 if (path == NULL) {
2107 error = got_error_prefix_errno2("realpath", argv[0]);
2108 goto done;
2110 got_path_strip_trailing_slashes(path);
2112 cwd = getcwd(NULL, 0);
2113 if (cwd == NULL) {
2114 error = got_error_prefix_errno("getcwd");
2115 goto done;
2117 error = got_worktree_open(&worktree, cwd);
2118 if (error)
2119 goto done;
2121 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree));
2122 if (error != NULL)
2123 goto done;
2125 error = apply_unveil(got_repo_get_path(repo), 1,
2126 got_worktree_get_root_path(worktree), 0);
2127 if (error)
2128 goto done;
2130 error = got_worktree_revert(worktree, path,
2131 revert_progress, NULL, repo);
2132 if (error)
2133 goto done;
2134 done:
2135 if (repo)
2136 got_repo_close(repo);
2137 if (worktree)
2138 got_worktree_close(worktree);
2139 free(path);
2140 free(cwd);
2141 return error;
2144 __dead static void
2145 usage_commit(void)
2147 fprintf(stderr, "usage: %s commit [-m msg] file-path\n", getprogname());
2148 exit(1);
2151 int
2152 spawn_editor(const char *file)
2154 char *argp[] = { "sh", "-c", NULL, NULL };
2155 char *editor, *editp;
2156 pid_t pid;
2157 sig_t sighup, sigint, sigquit;
2158 int st = -1;
2160 editor = getenv("VISUAL");
2161 if (editor == NULL)
2162 editor = getenv("EDITOR");
2163 if (editor == NULL)
2164 editor = "ed";
2166 if (asprintf(&editp, "%s %s", editor, file) == -1)
2167 return -1;
2169 argp[2] = editp;
2171 sighup = signal(SIGHUP, SIG_IGN);
2172 sigint = signal(SIGINT, SIG_IGN);
2173 sigquit = signal(SIGQUIT, SIG_IGN);
2175 switch (pid = fork()) {
2176 case -1:
2177 goto doneediting;
2178 case 0:
2179 execv(_PATH_BSHELL, argp);
2180 _exit(127);
2183 free(editp);
2185 while (waitpid(pid, &st, 0) == -1)
2186 if (errno != EINTR)
2187 break;
2189 doneediting:
2190 (void)signal(SIGHUP, sighup);
2191 (void)signal(SIGINT, sigint);
2192 (void)signal(SIGQUIT, sigquit);
2194 if (!WIFEXITED(st)) {
2195 errno = EINTR;
2196 return -1;
2199 return WEXITSTATUS(st);
2202 static const struct got_error *
2203 collect_commit_logmsg(struct got_pathlist_head *commitable_paths, char **logmsg,
2204 void *arg)
2206 struct got_pathlist_entry *pe;
2207 const struct got_error *err = NULL;
2208 char *tmppath = NULL;
2209 char *tmpfile = NULL;
2210 char *cmdline_log = (char *)arg;
2211 char buf[1024];
2212 struct stat st, st2;
2213 FILE *fp;
2214 size_t len;
2215 int fd;
2217 /* if a message was specified on the command line, just use it */
2218 if (cmdline_log != NULL && strlen(cmdline_log) != 0) {
2219 len = strlen(cmdline_log) + 1;
2220 *logmsg = malloc(len + 1);
2221 strlcpy(*logmsg, cmdline_log, len);
2222 return NULL;
2225 tmppath = getenv("TMPDIR");
2226 if (tmppath == NULL || strlen(tmppath) == 0)
2227 tmppath = "/tmp";
2229 if (asprintf(&tmpfile, "%s/got-XXXXXXXXXX", tmppath) == -1) {
2230 err = got_error_prefix_errno("asprintf");
2231 return err;
2234 fd = mkstemp(tmpfile);
2235 if (fd < 0) {
2236 err = got_error_prefix_errno("mktemp");
2237 free(tmpfile);
2238 return err;
2241 dprintf(fd, "\n"
2242 "# changes to be committed:\n");
2244 TAILQ_FOREACH(pe, commitable_paths, entry) {
2245 struct got_commitable *ct = pe->data;
2246 dprintf(fd, "# %c %s\n", ct->status, pe->path);
2248 close(fd);
2250 if (stat(tmpfile, &st) == -1) {
2251 err = got_error_prefix_errno2("stat", tmpfile);
2252 goto done;
2255 if (spawn_editor(tmpfile) == -1) {
2256 err = got_error_prefix_errno("failed spawning editor");
2257 goto done;
2260 if (stat(tmpfile, &st2) == -1) {
2261 err = got_error_prefix_errno("stat");
2262 goto done;
2265 if (st.st_mtime == st2.st_mtime && st.st_size == st2.st_size) {
2266 err = got_error_msg(GOT_ERR_COMMIT_MSG_EMPTY,
2267 "no changes made to commit message, aborting");
2268 goto done;
2271 /* remove comments */
2272 *logmsg = malloc(st2.st_size + 1);
2273 len = 0;
2275 fp = fopen(tmpfile, "r");
2276 while (fgets(buf, sizeof(buf), fp) != NULL) {
2277 if (buf[0] == '#' || (len == 0 && buf[0] == '\n'))
2278 continue;
2279 len = strlcat(*logmsg, buf, st2.st_size);
2281 fclose(fp);
2283 while (len > 0 && (*logmsg)[len - 1] == '\n') {
2284 (*logmsg)[len - 1] = '\0';
2285 len--;
2288 if (len == 0) {
2289 err = got_error_msg(GOT_ERR_COMMIT_MSG_EMPTY,
2290 "commit message cannot be empty, aborting");
2291 goto done;
2294 goto done;
2296 done:
2297 if (tmpfile) {
2298 unlink(tmpfile);
2299 free(tmpfile);
2302 return err;
2305 static const struct got_error *
2306 cmd_commit(int argc, char *argv[])
2308 const struct got_error *error = NULL;
2309 struct got_worktree *worktree = NULL;
2310 struct got_repository *repo = NULL;
2311 char *cwd = NULL, *path = NULL, *id_str = NULL;
2312 struct got_object_id *id = NULL;
2313 const char *logmsg = NULL;
2314 const char *got_author = getenv("GOT_AUTHOR");
2315 int ch;
2317 while ((ch = getopt(argc, argv, "m:")) != -1) {
2318 switch (ch) {
2319 case 'm':
2320 logmsg = optarg;
2321 break;
2322 default:
2323 usage_commit();
2324 /* NOTREACHED */
2328 argc -= optind;
2329 argv += optind;
2331 if (argc == 1) {
2332 path = realpath(argv[0], NULL);
2333 if (path == NULL) {
2334 error = got_error_prefix_errno2("realpath", argv[0]);
2335 goto done;
2337 got_path_strip_trailing_slashes(path);
2338 } else if (argc != 0)
2339 usage_commit();
2341 if (got_author == NULL) {
2342 /* TODO: Look current user up in password database */
2343 error = got_error(GOT_ERR_COMMIT_NO_AUTHOR);
2344 goto done;
2347 cwd = getcwd(NULL, 0);
2348 if (cwd == NULL) {
2349 error = got_error_prefix_errno("getcwd");
2350 goto done;
2352 error = got_worktree_open(&worktree, cwd);
2353 if (error)
2354 goto done;
2356 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree));
2357 if (error != NULL)
2358 goto done;
2360 #if 0
2361 error = apply_unveil(got_repo_get_path(repo), 0,
2362 got_worktree_get_root_path(worktree), 0);
2363 if (error)
2364 goto done;
2365 #endif
2367 error = got_worktree_commit(&id, worktree, path, got_author, NULL,
2368 collect_commit_logmsg, (void *)logmsg, print_status, NULL, repo);
2369 if (error)
2370 goto done;
2372 error = got_object_id_str(&id_str, id);
2373 if (error)
2374 goto done;
2375 printf("created commit %s\n", id_str);
2376 done:
2377 if (repo)
2378 got_repo_close(repo);
2379 if (worktree)
2380 got_worktree_close(worktree);
2381 free(path);
2382 free(cwd);
2383 free(id_str);
2384 return error;