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>
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.
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.
19 #include <sys/queue.h>
20 #include <sys/types.h>
22 #include <sys/param.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"
48 #include "got_cancel.h"
49 #include "got_worktree.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"
58 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
61 static volatile sig_atomic_t sigint_received;
62 static volatile sig_atomic_t sigpipe_received;
65 catch_sigint(int signo)
71 catch_sigpipe(int signo)
79 const struct got_error *(*cmd_main)(int, char *[]);
80 void (*cmd_usage)(void);
81 const char *cmd_alias;
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, "" },
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);
179 main(int argc, char *argv[])
184 int hflag = 0, Vflag = 0;
185 static struct option longopts[] = {
186 { "version", no_argument, NULL, 'V' },
190 setlocale(LC_CTYPE, "");
192 while ((ch = getopt_long(argc, argv, "+hV", longopts, NULL)) != -1) {
211 got_version_print_str();
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)
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) &&
239 error->code == GOT_ERR_ERRNO && errno == EINTR)) {
240 fprintf(stderr, "%s: %s\n", getprogname(), error->msg);
247 fprintf(stderr, "%s: unknown command '%s'\n", getprogname(), argv[0]);
255 fprintf(stderr, "usage: %s [-h] [-V | --version] command [arg ...]\n",
262 static const struct got_error *
263 get_editor(char **abspath)
265 const struct got_error *err = NULL;
270 editor = getenv("VISUAL");
272 editor = getenv("EDITOR");
275 err = got_path_find_prog(abspath, editor);
280 if (*abspath == NULL) {
281 *abspath = strdup("/bin/ed");
282 if (*abspath == NULL)
283 return got_error_from_errno("strdup");
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;
296 if (unveil("gmon.out", "rwc") != 0)
297 return got_error_from_errno2("unveil", "gmon.out");
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();
312 if (unveil(NULL, NULL) != 0)
313 return got_error_from_errno("unveil");
321 fprintf(stderr, "usage: %s init repository-path\n", getprogname());
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;
332 while ((ch = getopt(argc, argv, "")) != -1) {
344 if (pledge("stdio rpath wpath cpath unveil", NULL) == -1)
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);
358 !(error->code == GOT_ERR_ERRNO && errno == EEXIST))
361 error = apply_unveil(repo_path, 0, NULL);
365 error = got_repo_init(repo_path);
374 fprintf(stderr, "usage: %s import [-b branch] [-m message] "
375 "[-r repository-path] [-I pattern] path\n", getprogname());
380 spawn_editor(const char *editor, const char *file)
383 sig_t sighup, sigint, sigquit;
386 sighup = signal(SIGHUP, SIG_IGN);
387 sigint = signal(SIGINT, SIG_IGN);
388 sigquit = signal(SIGQUIT, SIG_IGN);
390 switch (pid = fork()) {
394 execl(editor, editor, file, (char *)NULL);
398 while (waitpid(pid, &st, 0) == -1)
403 (void)signal(SIGHUP, sighup);
404 (void)signal(SIGINT, sigint);
405 (void)signal(SIGQUIT, sigquit);
407 if (!WIFEXITED(st)) {
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;
423 int content_changed = 0;
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);
443 return got_error_from_errno("malloc");
447 fp = fopen(logmsg_path, "r");
449 err = got_error_from_errno("fopen");
452 while (fgets(buf, sizeof(buf), fp) != NULL) {
453 if (!content_changed && strcmp(buf, initial_content) != 0)
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);
461 while (len > 0 && (*logmsg)[len - 1] == '\n') {
462 (*logmsg)[len - 1] = '\0';
466 if (len == 0 || !content_changed)
467 err = got_error_msg(GOT_ERR_COMMIT_MSG_EMPTY,
468 "commit message cannot be empty, aborting");
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;
485 if (asprintf(&initial_content,
486 "\n# %s to be imported to branch %s\n", path_dir,
488 return got_error_from_errno("asprintf");
490 err = got_opentemp_named_fd(logmsg_path, &fd,
491 GOT_TMPDIR_STR "/got-importmsg");
495 dprintf(fd, initial_content);
498 err = edit_logmsg(logmsg, editor, *logmsg_path, initial_content);
500 free(initial_content);
504 static const struct got_error *
505 import_progress(void *arg, const char *path)
507 printf("A %s\n", path);
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;
519 name = got_repo_get_gitconfig_author_name(repo);
520 email = got_repo_get_gitconfig_author_email(repo);
522 if (asprintf(author, "%s <%s>", name, email) == -1)
523 return got_error_from_errno("asprintf");
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);
532 if (asprintf(author, "%s <%s>", name, email) == -1)
533 return got_error_from_errno("asprintf");
536 /* TODO: Look up user in password database? */
537 return got_error(GOT_ERR_COMMIT_NO_AUTHOR);
540 *author = strdup(got_author);
542 return got_error_from_errno("strdup");
545 * Really dumb email address check; we're only doing this to
546 * avoid git's object parser breaking on commits we create.
548 while (*got_author && *got_author != '<')
550 if (*got_author != '<') {
551 err = got_error(GOT_ERR_COMMIT_NO_EMAIL);
554 while (*got_author && *got_author != '@')
556 if (*got_author != '@') {
557 err = got_error(GOT_ERR_COMMIT_NO_EMAIL);
560 while (*got_author && *got_author != '>')
562 if (*got_author != '>')
563 err = got_error(GOT_ERR_COMMIT_NO_EMAIL);
572 static const struct got_error *
573 get_gitconfig_path(char **gitconfig_path)
575 const char *homedir = getenv("HOME");
577 *gitconfig_path = NULL;
579 if (asprintf(gitconfig_path, "%s/.gitconfig", homedir) == -1)
580 return got_error_from_errno("asprintf");
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;
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) {
607 branch_name = optarg;
610 logmsg = strdup(optarg);
611 if (logmsg == NULL) {
612 error = got_error_from_errno("strdup");
617 repo_path = realpath(optarg, NULL);
618 if (repo_path == NULL) {
619 error = got_error_from_errno2("realpath",
625 if (optarg[0] == '\0')
627 error = got_pathlist_insert(&pe, &ignores, optarg,
642 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
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);
659 error = got_repo_open(&repo, repo_path, gitconfig_path);
663 error = get_author(&author, repo);
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.
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");
680 error = got_ref_open(&branch_ref, repo, refname, 0);
682 if (error->code != GOT_ERR_NOT_REF)
685 error = got_error_msg(GOT_ERR_BRANCH_EXISTS,
686 "import target branch already exists");
690 path_dir = realpath(argv[0], NULL);
691 if (path_dir == NULL) {
692 error = got_error_from_errno2("realpath", argv[0]);
695 got_path_strip_trailing_slashes(path_dir);
698 * unveil(2) traverses exec(2); if an editor is used we have
699 * to apply unveil after the log message has been written.
701 if (logmsg == NULL || strlen(logmsg) == 0) {
702 error = get_editor(&editor);
706 error = collect_import_msg(&logmsg, &logmsg_path, editor,
709 if (error->code != GOT_ERR_COMMIT_MSG_EMPTY &&
716 if (unveil(path_dir, "r") != 0) {
717 error = got_error_from_errno2("unveil", path_dir);
723 error = apply_unveil(got_repo_get_path(repo), 0, NULL);
730 error = got_repo_import(&new_commit_id, path_dir, logmsg,
731 author, &ignores, repo, import_progress, NULL);
738 error = got_ref_alloc(&branch_ref, refname, new_commit_id);
745 error = got_ref_write(branch_ref, repo);
752 error = got_object_id_str(&id_str, new_commit_id);
759 error = got_ref_open(&head_ref, repo, GOT_REF_HEAD, 0);
761 if (error->code != GOT_ERR_NOT_REF) {
767 error = got_ref_alloc_symref(&head_ref, GOT_REF_HEAD,
775 error = got_ref_write(head_ref, repo);
783 printf("Created branch %s with commit %s\n",
784 got_ref_get_name(branch_ref), id_str);
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);
799 free(gitconfig_path);
801 got_ref_close(branch_ref);
803 got_ref_close(head_ref);
810 fprintf(stderr, "usage: %s clone repo-url\n", getprogname());
817 fprintf(stderr, "usage: %s checkout [-E] [-b branch] [-c commit] "
818 "[-p prefix] repository-path [worktree-path]\n", getprogname());
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",
832 struct got_checkout_progress_arg {
833 const char *worktree_path;
834 int had_base_commit_ref_error;
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)
846 if (status == GOT_STATUS_BASE_REF_ERR) {
847 a->had_base_commit_ref_error = 1;
851 while (path[0] == '/')
854 printf("%c %s/%s\n", status, a->worktree_path, path);
858 static const struct got_error *
859 check_cancelled(void *arg)
861 if (sigint_received || sigpipe_received)
862 return got_error(GOT_ERR_CANCELLED);
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);
880 return got_error(GOT_ERR_ANCESTRY);
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)
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)
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);
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);
925 if (got_object_id_cmp(head_commit_id, commit_id) == 0) {
929 if (yca_id && got_object_id_cmp(commit_id, yca_id) == 0) {
934 err = got_commit_graph_open(&graph, "/", 1);
938 err = got_commit_graph_iter_start(graph, head_commit_id, repo,
939 check_cancelled, NULL);
944 struct got_object_id *id;
945 err = got_commit_graph_iter_next(&id, graph, repo,
946 check_cancelled, NULL);
948 if (err->code == GOT_ERR_ITER_COMPLETED)
954 if (yca_id && got_object_id_cmp(id, yca_id) == 0)
956 if (got_object_id_cmp(id, commit_id) == 0) {
964 got_commit_graph_close(graph);
965 free(head_commit_id);
966 if (!err && !is_same_branch)
967 err = got_error(GOT_ERR_ANCESTRY);
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;
982 while ((ch = getopt(argc, argv, "b:")) != -1) {
985 branch_filter = optarg;
1002 err = got_fetch_parse_uri(&proto, &host, &port, &server_path,
1003 &repo_name, argv[0]);
1007 if (dirname == NULL) {
1008 if (asprintf(&default_destdir, "%s.git", repo_name) == -1) {
1009 err = got_error_from_errno("asprintf");
1012 repo_path = default_destdir;
1014 repo_path = dirname;
1016 err = got_path_mkdir(repo_path);
1020 err = got_repo_init(repo_path);
1024 err = got_repo_open(&repo, repo_path, NULL);
1028 err = got_fetch(proto, host, port, server_path, repo_name,
1029 branch_filter, repo);
1032 got_repo_close(repo);
1038 free(default_destdir);
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);
1051 branch_name = got_ref_get_name(ref);
1053 if (strncmp("refs/heads/", branch_name, 11) == 0)
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;
1084 while ((ch = getopt(argc, argv, "b:c:Ep:")) != -1) {
1087 branch_name = optarg;
1090 commit_id_str = strdup(optarg);
1091 if (commit_id_str == NULL)
1092 return got_error_from_errno("strdup");
1098 path_prefix = optarg;
1110 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
1111 "unveil", NULL) == -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);
1121 error = got_error_from_errno("getcwd");
1124 if (path_prefix[0]) {
1125 base = basename(path_prefix);
1127 error = got_error_from_errno2("basename",
1132 base = basename(repo_path);
1134 error = got_error_from_errno2("basename",
1139 dotgit = strstr(base, ".git");
1142 if (asprintf(&worktree_path, "%s/%s", cwd, base) == -1) {
1143 error = got_error_from_errno("asprintf");
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]);
1154 worktree_path = realpath(argv[1], NULL);
1155 if (worktree_path == NULL) {
1156 if (errno != ENOENT) {
1157 error = got_error_from_errno2("realpath",
1161 worktree_path = strdup(argv[1]);
1162 if (worktree_path == NULL) {
1163 error = got_error_from_errno("strdup");
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);
1177 /* Pre-create work tree path for unveil(2) */
1178 error = got_path_mkdir(worktree_path);
1180 if (!(error->code == GOT_ERR_ERRNO && errno == EISDIR) &&
1181 !(error->code == GOT_ERR_ERRNO && errno == EEXIST))
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);
1191 error = apply_unveil(got_repo_get_path(repo), 0, worktree_path);
1195 error = got_ref_open(&head_ref, repo, branch_name, 0);
1199 error = got_worktree_init(worktree_path, head_ref, path_prefix, repo);
1200 if (error != NULL && !(error->code == GOT_ERR_ERRNO && errno == EEXIST))
1203 error = got_worktree_open(&worktree, worktree_path);
1207 error = got_worktree_match_path_prefix(&same_path_prefix, worktree,
1211 if (!same_path_prefix) {
1212 error = got_error(GOT_ERR_PATH_PREFIX);
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);
1222 error = check_linear_ancestry(commit_id,
1223 got_worktree_get_base_commit_id(worktree), 0, repo);
1224 if (error != NULL) {
1226 if (error->code == GOT_ERR_ANCESTRY) {
1227 error = checkout_ancestry_error(
1228 head_ref, commit_id_str);
1232 error = check_same_branch(commit_id, head_ref, NULL, repo);
1234 if (error->code == GOT_ERR_ANCESTRY) {
1235 error = checkout_ancestry_error(
1236 head_ref, commit_id_str);
1240 error = got_worktree_set_base_commit_id(worktree, repo,
1247 error = got_pathlist_append(&paths, "", NULL);
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);
1257 printf("Now shut up and hack\n");
1258 if (cpa.had_base_commit_ref_error)
1259 show_worktree_base_ref_warning();
1261 got_pathlist_free(&paths);
1262 free(commit_id_str);
1264 free(worktree_path);
1271 fprintf(stderr, "usage: %s update [-b branch] [-c commit] [path ...]\n",
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)
1287 /* Base commit bump happens silently. */
1288 if (status == GOT_STATUS_BUMP_BASE)
1291 while (path[0] == '/')
1293 printf("%c %s\n", status, path);
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;
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);
1318 if (err->code != GOT_ERR_ANCESTRY)
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));
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));
1337 static const struct got_error *
1338 check_rebase_or_histedit_in_progress(struct got_worktree *worktree)
1340 const struct got_error *err;
1343 err = got_worktree_rebase_in_progress(&in_progress, worktree);
1347 return got_error(GOT_ERR_REBASING);
1349 err = got_worktree_histedit_in_progress(&in_progress, worktree);
1353 return got_error(GOT_ERR_HISTEDIT_BUSY);
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;
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]);
1377 err = got_pathlist_append(paths, path, NULL);
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;
1404 while ((ch = getopt(argc, argv, "b:c:")) != -1) {
1407 branch_name = optarg;
1410 commit_id_str = strdup(optarg);
1411 if (commit_id_str == NULL)
1412 return got_error_from_errno("strdup");
1424 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
1425 "unveil", NULL) == -1)
1428 worktree_path = getcwd(NULL, 0);
1429 if (worktree_path == NULL) {
1430 error = got_error_from_errno("getcwd");
1433 error = got_worktree_open(&worktree, worktree_path);
1437 error = check_rebase_or_histedit_in_progress(worktree);
1441 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
1446 error = apply_unveil(got_repo_get_path(repo), 0,
1447 got_worktree_get_root_path(worktree));
1451 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
1455 error = got_ref_open(&head_ref, repo, branch_name ? branch_name :
1456 got_worktree_get_head_ref_name(worktree), 0);
1459 if (commit_id_str == NULL) {
1460 error = got_ref_resolve(&commit_id, repo, head_ref);
1463 error = got_object_id_str(&commit_id_str, commit_id);
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;
1473 error = got_object_id_str(&commit_id_str, commit_id);
1479 struct got_object_id *head_commit_id;
1480 TAILQ_FOREACH(pe, &paths, entry) {
1481 if (pe->path_len == 0)
1483 error = got_error_msg(GOT_ERR_BAD_PATH,
1484 "switching between branches requires that "
1485 "the entire work tree gets updated");
1488 error = got_ref_resolve(&head_commit_id, repo, head_ref);
1491 error = check_linear_ancestry(commit_id, head_commit_id, 0,
1493 free(head_commit_id);
1496 error = check_same_branch(commit_id, head_ref, NULL, repo);
1499 error = switch_head_ref(head_ref, commit_id, worktree, repo);
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);
1510 error = check_same_branch(commit_id, head_ref, NULL, repo);
1515 if (got_object_id_cmp(got_worktree_get_base_commit_id(worktree),
1517 error = got_worktree_set_base_commit_id(worktree, repo,
1523 error = got_worktree_checkout_files(worktree, &paths, repo,
1524 update_progress, &did_something, check_cancelled, NULL);
1529 printf("Updated to commit %s\n", commit_id_str);
1531 printf("Already up-to-date\n");
1533 free(worktree_path);
1534 TAILQ_FOREACH(pe, &paths, entry)
1535 free((char *)pe->path);
1536 got_pathlist_free(&paths);
1538 free(commit_id_str);
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;
1551 err = got_object_open_as_blob(&blob1, repo, blob_id1, 8192);
1556 err = got_object_open_as_blob(&blob2, repo, blob_id2, 8192);
1560 while (path[0] == '/')
1562 err = got_diff_blob(blob1, blob2, path, path, diff_context,
1563 ignore_whitespace, stdout);
1566 got_object_blob_close(blob1);
1567 got_object_blob_close(blob2);
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;
1581 err = got_object_open_as_tree(&tree1, repo, tree_id1);
1586 err = got_object_open_as_tree(&tree2, repo, tree_id2);
1590 arg.diff_context = diff_context;
1591 arg.ignore_whitespace = ignore_whitespace;
1592 arg.outfile = stdout;
1593 while (path[0] == '/')
1595 err = got_diff_tree(tree1, tree2, path, path, repo,
1596 got_diff_blob_output_unidiff, &arg, 1);
1599 got_object_tree_close(tree1);
1601 got_object_tree_close(tree2);
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));
1617 err = got_object_open_as_commit(&pcommit, repo,
1623 if (path && path[0] != '\0') {
1625 err = got_object_id_by_path(&obj_id2, repo, id, path);
1628 err = got_object_id_str(&id_str2, obj_id2);
1634 err = got_object_id_by_path(&obj_id1, repo,
1640 err = got_object_id_str(&id_str1, obj_id1);
1646 err = got_object_get_type(&obj_type, repo, obj_id2);
1651 printf("diff %s %s\n", id_str1 ? id_str1 : "/dev/null", id_str2);
1653 case GOT_OBJ_TYPE_BLOB:
1654 err = diff_blobs(obj_id1, obj_id2, path, diff_context,
1657 case GOT_OBJ_TYPE_TREE:
1658 err = diff_trees(obj_id1, obj_id2, path, diff_context,
1662 err = got_error(GOT_ERR_OBJ_TYPE);
1668 obj_id2 = got_object_commit_get_tree_id(commit);
1669 err = got_object_id_str(&id_str2, obj_id2);
1672 obj_id1 = got_object_commit_get_tree_id(pcommit);
1673 err = got_object_id_str(&id_str1, obj_id1);
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);
1683 got_object_commit_close(pcommit);
1688 get_datestr(time_t *time, char *datebuf)
1690 struct tm mytm, *tm;
1693 tm = gmtime_r(time, &mytm);
1696 s = asctime_r(tm, datebuf);
1699 p = strchr(s, '\n');
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;
1715 err = got_object_id_str(&id_str, id);
1719 err = got_object_commit_get_logmsg(&logmsg, commit);
1723 if (regexec(regex, logmsg, 1, ®match, 0) == 0)
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;
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) {
1749 struct got_tag_object *tag = NULL;
1752 name = got_ref_get_name(re->ref);
1753 if (strcmp(name, GOT_REF_HEAD) == 0)
1755 if (strncmp(name, "refs/", 5) == 0)
1757 if (strncmp(name, "got/", 4) == 0)
1759 if (strncmp(name, "heads/", 6) == 0)
1761 if (strncmp(name, "remotes/", 8) == 0)
1763 if (strncmp(name, "tags/", 5) == 0) {
1764 err = got_object_open_as_tag(&tag, repo, re->id);
1766 if (err->code != GOT_ERR_OBJ_TYPE)
1768 /* Ref points at something other than a tag. */
1773 cmp = got_object_id_cmp(tag ?
1774 got_object_tag_get_object_id(tag) : re->id, id);
1776 got_object_tag_close(tag);
1780 if (asprintf(&refs_str, "%s%s%s", s ? s : "", s ? ", " : "",
1782 err = got_error_from_errno("asprintf");
1788 err = got_object_id_str(&id_str, id);
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 ? ")" : "");
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);
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;
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);
1817 printf("parent %d: %s\n", n++, id_str);
1822 err = got_object_commit_get_logmsg(&logmsg0, commit);
1828 line = strsep(&logmsg, "\n");
1830 printf(" %s\n", line);
1835 err = print_patch(commit, id, path, diff_context, repo);
1840 if (fflush(stdout) != 0 && err == NULL)
1841 err = got_error_from_errno("fflush");
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;
1856 if (search_pattern &&
1857 regcomp(®ex, 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);
1863 err = got_commit_graph_iter_start(graph, root_id, repo,
1864 check_cancelled, NULL);
1868 struct got_commit_object *commit;
1869 struct got_object_id *id;
1871 if (sigint_received || sigpipe_received)
1874 err = got_commit_graph_iter_next(&id, graph, repo,
1875 check_cancelled, NULL);
1877 if (err->code == GOT_ERR_ITER_COMPLETED)
1884 err = got_object_open_as_commit(&commit, repo, id);
1888 if (search_pattern) {
1889 err = match_logmsg(&have_match, id, commit, ®ex);
1891 got_object_commit_close(commit);
1894 if (have_match == 0) {
1895 got_object_commit_close(commit);
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))
1909 got_commit_graph_close(graph);
1916 fprintf(stderr, "usage: %s log [-b] [-c commit] [-C number] [ -l N ] [-p] "
1917 "[-s search-pattern] [-r repository-path] [path]\n", getprogname());
1922 get_default_log_limit(void)
1924 const char *got_default_log_limit;
1928 got_default_log_limit = getenv("GOT_LOG_DEFAULT_LIMIT");
1929 if (got_default_log_limit == NULL)
1931 n = strtonum(got_default_log_limit, 0, INT_MAX, &errstr);
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;
1950 struct got_reflist_head refs;
1952 SIMPLEQ_INIT(&refs);
1955 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
1961 limit = get_default_log_limit();
1963 while ((ch = getopt(argc, argv, "bpc:C:l:r:s:")) != -1) {
1969 start_commit = optarg;
1972 diff_context = strtonum(optarg, 0, GOT_DIFF_MAX_CONTEXT,
1975 err(1, "-C option %s", errstr);
1978 limit = strtonum(optarg, 0, INT_MAX, &errstr);
1980 err(1, "-l option %s", errstr);
1986 repo_path = realpath(optarg, NULL);
1987 if (repo_path == NULL)
1988 return got_error_from_errno2("realpath",
1990 got_path_strip_trailing_slashes(repo_path);
1993 search_pattern = optarg;
2004 if (diff_context == -1)
2006 else if (!show_patch)
2007 errx(1, "-C reguires -p");
2009 cwd = getcwd(NULL, 0);
2011 error = got_error_from_errno("getcwd");
2015 error = got_worktree_open(&worktree, cwd);
2016 if (error && error->code != GOT_ERR_NOT_WORKTREE)
2023 error = got_error_from_errno("strdup");
2026 } else if (argc == 1) {
2028 error = got_worktree_resolve_path(&path, worktree,
2033 path = strdup(argv[0]);
2035 error = got_error_from_errno("strdup");
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");
2051 error = got_repo_open(&repo, repo_path, NULL);
2055 error = apply_unveil(got_repo_get_path(repo), 1,
2056 worktree ? got_worktree_get_root_path(worktree) : NULL);
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)
2067 error = got_ref_resolve(&id, repo, head_ref);
2068 got_ref_close(head_ref);
2071 error = got_object_open_as_commit(&commit, repo, id);
2073 struct got_reference *ref;
2074 error = got_ref_open(&ref, repo, start_commit, 0);
2075 if (error == NULL) {
2077 error = got_ref_resolve(&id, repo, ref);
2081 error = got_object_get_type(&obj_type, repo, id);
2084 if (obj_type == GOT_OBJ_TYPE_TAG) {
2085 struct got_tag_object *tag;
2086 error = got_object_open_as_tag(&tag, repo, id);
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);
2096 id = got_object_id_dup(
2097 got_object_tag_get_object_id(tag));
2099 error = got_error_from_errno(
2100 "got_object_id_dup");
2101 got_object_tag_close(tag);
2104 } else if (obj_type != GOT_OBJ_TYPE_COMMIT) {
2105 error = got_error(GOT_ERR_OBJ_TYPE);
2108 error = got_object_open_as_commit(&commit, repo, id);
2112 if (commit == NULL) {
2113 error = got_repo_match_object_id_prefix(&id,
2114 start_commit, GOT_OBJ_TYPE_COMMIT, repo);
2123 const char *prefix = got_worktree_get_path_prefix(worktree);
2125 if (asprintf(&p, "%s%s%s", prefix,
2126 (strcmp(prefix, "/") != 0) ? "/" : "", path) == -1) {
2127 error = got_error_from_errno("asprintf");
2130 error = got_repo_map_path(&in_repo_path, repo, p, 0);
2133 error = got_repo_map_path(&in_repo_path, repo, path, 1);
2138 path = in_repo_path;
2141 error = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name, NULL);
2145 error = print_commits(id, repo, path, show_patch, search_pattern,
2146 diff_context, limit, log_branches, &refs);
2153 got_worktree_close(worktree);
2155 const struct got_error *repo_error;
2156 repo_error = got_repo_close(repo);
2160 got_ref_list_free(&refs);
2167 fprintf(stderr, "usage: %s diff [-C number] [-r repository-path] [-s] "
2168 "[-w] [object1 object2 | path]\n", getprogname());
2172 struct print_diff_arg {
2173 struct got_repository *repo;
2174 struct got_worktree *worktree;
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;
2193 char *abspath = NULL, *label1 = NULL;
2196 if (a->diff_staged) {
2197 if (staged_status != GOT_STATUS_MODIFY &&
2198 staged_status != GOT_STATUS_ADD &&
2199 staged_status != GOT_STATUS_DELETE)
2202 if (staged_status == GOT_STATUS_DELETE)
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)
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:
2227 case GOT_STATUS_ADD:
2230 case GOT_STATUS_DELETE:
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,
2241 if (staged_status == GOT_STATUS_ADD ||
2242 staged_status == GOT_STATUS_MODIFY) {
2244 err = got_object_open_as_blob(&blob1, a->repo, staged_blob_id,
2248 err = got_object_id_str(&id_str, staged_blob_id);
2251 if (asprintf(&label1, "%s (staged)", id_str) == -1) {
2252 err = got_error_from_errno("asprintf");
2257 } else if (status != GOT_STATUS_ADD) {
2258 err = got_object_open_as_blob(&blob1, a->repo, blob_id, 8192);
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");
2271 fd = openat(dirfd, de_name, O_RDONLY | O_NOFOLLOW);
2273 err = got_error_from_errno2("openat", abspath);
2277 fd = open(abspath, O_RDONLY | O_NOFOLLOW);
2279 err = got_error_from_errno2("open", abspath);
2283 if (fstat(fd, &sb) == -1) {
2284 err = got_error_from_errno2("fstat", abspath);
2287 f2 = fdopen(fd, "r");
2289 err = got_error_from_errno2("fdopen", abspath);
2296 err = got_diff_blob_file(blob1, label1, f2, sb.st_size, path,
2297 a->diff_context, a->ignore_whitespace, stdout);
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");
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;
2320 int diff_context = 3, diff_staged = 0, ignore_whitespace = 0, ch;
2325 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
2330 while ((ch = getopt(argc, argv, "C:r:sw")) != -1) {
2333 diff_context = strtonum(optarg, 0, GOT_DIFF_MAX_CONTEXT,
2336 err(1, "-C option %s", errstr);
2339 repo_path = realpath(optarg, NULL);
2340 if (repo_path == NULL)
2341 return got_error_from_errno2("realpath",
2343 got_path_strip_trailing_slashes(repo_path);
2349 ignore_whitespace = 1;
2360 cwd = getcwd(NULL, 0);
2362 error = got_error_from_errno("getcwd");
2365 error = got_worktree_open(&worktree, cwd);
2366 if (error && error->code != GOT_ERR_NOT_WORKTREE)
2369 if (worktree == NULL) {
2370 error = got_error(GOT_ERR_NOT_WORKTREE);
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");
2382 error = got_worktree_resolve_path(&path, worktree,
2389 error = got_error_from_errno("strdup");
2393 } else if (argc == 2) {
2395 errx(1, "-s option can't be used when diffing "
2396 "objects in repository");
2399 if (worktree && repo_path == NULL) {
2401 strdup(got_worktree_get_repo_path(worktree));
2402 if (repo_path == NULL) {
2403 error = got_error_from_errno("strdup");
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);
2421 error = apply_unveil(got_repo_get_path(repo), 1,
2422 worktree ? got_worktree_get_root_path(worktree) : NULL);
2427 struct print_diff_arg arg;
2428 struct got_pathlist_head paths;
2433 error = got_object_id_str(&id_str,
2434 got_worktree_get_base_commit_id(worktree));
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);
2449 error = got_worktree_status(worktree, &paths, repo, print_diff,
2450 &arg, check_cancelled, NULL);
2452 got_pathlist_free(&paths);
2456 error = got_repo_match_object_id(&id1, &label1, id_str1,
2457 GOT_OBJ_TYPE_ANY, 1, repo);
2461 error = got_repo_match_object_id(&id2, &label2, id_str2,
2462 GOT_OBJ_TYPE_ANY, 1, repo);
2466 error = got_object_get_type(&type1, repo, id1);
2470 error = got_object_get_type(&type2, repo, id2);
2474 if (type1 != type2) {
2475 error = got_error(GOT_ERR_OBJ_TYPE);
2480 case GOT_OBJ_TYPE_BLOB:
2481 error = got_diff_objects_as_blobs(id1, id2, NULL, NULL,
2482 diff_context, ignore_whitespace, repo, stdout);
2484 case GOT_OBJ_TYPE_TREE:
2485 error = got_diff_objects_as_trees(id1, id2, "", "",
2486 diff_context, ignore_whitespace, repo, stdout);
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);
2494 error = got_error(GOT_ERR_OBJ_TYPE);
2503 got_worktree_close(worktree);
2505 const struct got_error *repo_error;
2506 repo_error = got_repo_close(repo);
2517 "usage: %s blame [-c commit] [-r repository-path] path\n",
2526 char datebuf[11]; /* YYYY-MM-DD + NUL */
2529 struct blame_cb_args {
2530 struct blame_line *lines;
2534 off_t *line_offsets;
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;
2546 size_t linesize = 0;
2547 struct got_commit_object *commit = NULL;
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);
2560 return NULL; /* no change in this commit */
2562 /* Annotate this line. */
2563 bline = &a->lines[lineno - 1];
2564 if (bline->annotated)
2566 err = got_object_id_str(&bline->id_str, id);
2570 err = got_object_open_as_commit(&commit, a->repo, id);
2574 bline->committer = strdup(got_object_commit_get_committer(commit));
2575 if (bline->committer == NULL) {
2576 err = got_error_from_errno("strdup");
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);
2588 bline->annotated = 1;
2590 /* Print lines annotated so far. */
2591 bline = &a->lines[a->lineno_cur - 1];
2592 if (!bline->annotated)
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");
2601 while (bline->annotated) {
2602 char *smallerthan, *at, *nl, *committer;
2605 if (getline(&line, &linesize, a->f) == -1) {
2607 err = got_error_from_errno("getline");
2611 committer = bline->committer;
2612 smallerthan = strchr(committer, '<');
2613 if (smallerthan && smallerthan[1] != '\0')
2614 committer = smallerthan + 1;
2615 at = strchr(committer, '@');
2618 len = strlen(committer);
2620 committer[8] = '\0';
2622 nl = strchr(line, '\n');
2625 printf("%.*d) %.8s %s %-8s %s\n", a->nlines_prec, a->lineno_cur,
2626 bline->id_str, bline->datebuf, committer, line);
2629 bline = &a->lines[a->lineno_cur - 1];
2633 got_object_commit_close(commit);
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;
2653 memset(&bca, 0, sizeof(bca));
2656 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
2661 while ((ch = getopt(argc, argv, "c:r:")) != -1) {
2664 commit_id_str = optarg;
2667 repo_path = realpath(optarg, NULL);
2668 if (repo_path == NULL)
2669 return got_error_from_errno2("realpath",
2671 got_path_strip_trailing_slashes(repo_path);
2687 cwd = getcwd(NULL, 0);
2689 error = got_error_from_errno("getcwd");
2692 if (repo_path == NULL) {
2693 error = got_worktree_open(&worktree, cwd);
2694 if (error && error->code != GOT_ERR_NOT_WORKTREE)
2700 strdup(got_worktree_get_repo_path(worktree));
2701 if (repo_path == NULL)
2702 error = got_error_from_errno("strdup");
2706 repo_path = strdup(cwd);
2707 if (repo_path == NULL) {
2708 error = got_error_from_errno("strdup");
2714 error = got_repo_open(&repo, repo_path, NULL);
2718 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
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] ? "/" : "",
2730 error = got_error_from_errno("asprintf");
2733 error = got_repo_map_path(&in_repo_path, repo, p, 0);
2736 error = got_repo_map_path(&in_repo_path, repo, path, 1);
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);
2747 error = got_ref_resolve(&commit_id, repo, head_ref);
2748 got_ref_close(head_ref);
2752 error = got_repo_match_object_id(&commit_id, NULL,
2753 commit_id_str, GOT_OBJ_TYPE_COMMIT, 1, repo);
2758 error = got_object_id_by_path(&obj_id, repo, commit_id, in_repo_path);
2762 error = got_object_get_type(&obj_type, repo, obj_id);
2766 if (obj_type != GOT_OBJ_TYPE_BLOB) {
2767 error = got_error(GOT_ERR_OBJ_TYPE);
2771 error = got_object_open_as_blob(&blob, repo, obj_id, 8192);
2774 bca.f = got_opentemp();
2775 if (bca.f == NULL) {
2776 error = got_error_from_errno("got_opentemp");
2779 error = got_object_blob_dump_to_file(&filesize, &bca.nlines,
2780 &bca.line_offsets, bca.f, blob);
2781 if (error || bca.nlines == 0)
2784 /* Don't include \n at EOF in the blame line count. */
2785 if (bca.line_offsets[bca.nlines - 1] == filesize)
2788 bca.lines = calloc(bca.nlines, sizeof(*bca.lines));
2789 if (bca.lines == NULL) {
2790 error = got_error_from_errno("calloc");
2794 bca.nlines_prec = 0;
2802 error = got_blame(in_repo_path, commit_id, repo, blame_cb, &bca,
2803 check_cancelled, NULL);
2811 got_object_blob_close(blob);
2813 got_worktree_close(worktree);
2815 const struct got_error *repo_error;
2816 repo_error = got_repo_close(repo);
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);
2828 free(bca.line_offsets);
2829 if (bca.f && fclose(bca.f) == EOF && error == NULL)
2830 error = got_error_from_errno("fclose");
2838 "usage: %s tree [-c commit] [-r repository-path] [-iR] path\n",
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] == '/')
2855 if (got_object_tree_entry_is_submodule(te))
2857 else if (S_ISLNK(mode))
2859 else if (S_ISDIR(mode))
2861 else if (mode & S_IXUSR)
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;
2878 err = got_object_id_by_path(&tree_id, repo, commit_id, path);
2882 err = got_object_open_as_tree(&tree, repo, tree_id);
2885 nentries = got_object_tree_get_nentries(tree);
2886 for (i = 0; i < nentries; i++) {
2887 struct got_tree_entry *te;
2890 if (sigint_received || sigpipe_received)
2893 te = got_object_tree_get_entry(tree, i);
2896 err = got_object_id_str(&id_str,
2897 got_tree_entry_get_id(te));
2900 if (asprintf(&id, "%s ", id_str) == -1) {
2901 err = got_error_from_errno("asprintf");
2907 print_entry(te, id, path, root_path);
2910 if (recurse && S_ISDIR(got_tree_entry_get_mode(te))) {
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");
2918 err = print_tree(child_path, commit_id, show_ids, 1,
2927 got_object_tree_close(tree);
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;
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;
2946 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
2951 while ((ch = getopt(argc, argv, "c:r:iR")) != -1) {
2954 commit_id_str = optarg;
2957 repo_path = realpath(optarg, NULL);
2958 if (repo_path == NULL)
2959 return got_error_from_errno2("realpath",
2961 got_path_strip_trailing_slashes(repo_path);
2985 cwd = getcwd(NULL, 0);
2987 error = got_error_from_errno("getcwd");
2990 if (repo_path == NULL) {
2991 error = got_worktree_open(&worktree, cwd);
2992 if (error && error->code != GOT_ERR_NOT_WORKTREE)
2998 strdup(got_worktree_get_repo_path(worktree));
2999 if (repo_path == NULL)
3000 error = got_error_from_errno("strdup");
3004 repo_path = strdup(cwd);
3005 if (repo_path == NULL) {
3006 error = got_error_from_errno("strdup");
3012 error = got_repo_open(&repo, repo_path, NULL);
3016 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
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");
3030 error = got_repo_map_path(&in_repo_path, repo, p, 0);
3037 if (in_repo_path == NULL) {
3038 error = got_repo_map_path(&in_repo_path, repo, path, 1);
3043 if (commit_id_str == NULL) {
3044 struct got_reference *head_ref;
3045 error = got_ref_open(&head_ref, repo, GOT_REF_HEAD, 0);
3048 error = got_ref_resolve(&commit_id, repo, head_ref);
3049 got_ref_close(head_ref);
3053 error = got_repo_match_object_id(&commit_id, NULL,
3054 commit_id_str, GOT_OBJ_TYPE_COMMIT, 1, repo);
3059 error = print_tree(in_repo_path, commit_id, show_ids, recurse,
3060 in_repo_path, repo);
3067 got_worktree_close(worktree);
3069 const struct got_error *repo_error;
3070 repo_error = got_repo_close(repo);
3080 fprintf(stderr, "usage: %s status [path ...]\n", getprogname());
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);
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;
3103 struct got_pathlist_head paths;
3104 struct got_pathlist_entry *pe;
3109 while ((ch = getopt(argc, argv, "")) != -1) {
3121 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
3125 cwd = getcwd(NULL, 0);
3127 error = got_error_from_errno("getcwd");
3131 error = got_worktree_open(&worktree, cwd);
3135 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
3140 error = apply_unveil(got_repo_get_path(repo), 1,
3141 got_worktree_get_root_path(worktree));
3145 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
3149 error = got_worktree_status(worktree, &paths, repo, print_status, NULL,
3150 check_cancelled, NULL);
3152 TAILQ_FOREACH(pe, &paths, entry)
3153 free((char *)pe->path);
3154 got_pathlist_free(&paths);
3163 "usage: %s ref [-r repository] -l | -d name | [-s] name target\n",
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);
3180 SIMPLEQ_FOREACH(re, &refs, entry) {
3182 refstr = got_ref_to_str(re->ref);
3184 return got_error_from_errno("got_ref_to_str");
3185 printf("%s: %s\n", got_ref_get_name(re->ref), refstr);
3189 got_ref_list_free(&refs);
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);
3203 err = got_ref_delete(ref, repo);
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,
3226 struct got_reference *target_ref;
3228 if (err->code != GOT_ERR_BAD_OBJ_ID_STR)
3230 err = got_ref_open(&target_ref, repo, target, 0);
3233 err = got_ref_resolve(&id, repo, target_ref);
3234 got_ref_close(target_ref);
3239 err = got_ref_alloc(&ref, refname, id);
3243 err = got_ref_write(ref, repo);
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);
3270 err = got_ref_alloc_symref(&ref, refname, target_ref);
3274 err = got_ref_write(ref, repo);
3277 got_ref_close(target_ref);
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) {
3300 repo_path = realpath(optarg, NULL);
3301 if (repo_path == NULL)
3302 return got_error_from_errno2("realpath",
3304 got_path_strip_trailing_slashes(repo_path);
3318 if (do_list && delref)
3319 errx(1, "-l and -d options are mutually exclusive\n");
3324 if (do_list || delref) {
3326 errx(1, "-s option cannot be used together with the "
3327 "-l or -d options");
3330 } else if (argc != 2)
3335 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
3339 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
3340 "sendfd unveil", NULL) == -1)
3344 cwd = getcwd(NULL, 0);
3346 error = got_error_from_errno("getcwd");
3350 if (repo_path == NULL) {
3351 error = got_worktree_open(&worktree, cwd);
3352 if (error && error->code != GOT_ERR_NOT_WORKTREE)
3358 strdup(got_worktree_get_repo_path(worktree));
3359 if (repo_path == NULL)
3360 error = got_error_from_errno("strdup");
3364 repo_path = strdup(cwd);
3365 if (repo_path == NULL) {
3366 error = got_error_from_errno("strdup");
3372 error = got_repo_open(&repo, repo_path, NULL);
3376 error = apply_unveil(got_repo_get_path(repo), do_list,
3377 worktree ? got_worktree_get_root_path(worktree) : NULL);
3382 error = list_refs(repo);
3384 error = delete_ref(repo, delref);
3385 else if (create_symref)
3386 error = add_symref(repo, argv[0], argv[1]);
3388 error = add_ref(repo, argv[0], argv[1]);
3391 got_repo_close(repo);
3393 got_worktree_close(worktree);
3403 "usage: %s branch [-c commit] [-d] [-r repository] [-l] [-n] "
3404 "[name]\n", getprogname());
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 = " ";
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);
3424 if (got_object_id_cmp(id,
3425 got_worktree_get_base_commit_id(worktree)) == 0)
3432 if (strncmp(refname, "refs/heads/", 11) == 0)
3434 if (strncmp(refname, "refs/got/worktree/", 18) == 0)
3437 refstr = got_ref_to_str(ref);
3439 return got_error_from_errno("got_ref_to_str");
3441 printf("%s%s: %s\n", marker, refname, refstr);
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)
3458 if (strncmp(refname, "refs/got/worktree/", 18) == 0)
3461 printf("%s\n", refname);
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);
3478 err = got_worktree_rebase_in_progress(&rebase_in_progress,
3483 err = got_worktree_histedit_in_progress(&histedit_in_progress,
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);
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);
3503 SIMPLEQ_FOREACH(re, &refs, entry)
3504 list_branch(repo, worktree, re->ref);
3506 got_ref_list_free(&refs);
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;
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);
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");
3533 err = got_ref_delete(ref, repo);
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");
3562 err = got_ref_open(&ref, repo, refname, 0);
3564 err = got_error(GOT_ERR_BRANCH_EXISTS);
3566 } else if (err->code != GOT_ERR_NOT_REF)
3569 err = got_ref_alloc(&ref, refname, base_commit_id);
3573 err = got_ref_write(ref, repo);
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;
3599 while ((ch = getopt(argc, argv, "c:d:r:ln")) != -1) {
3602 commit_id_arg = optarg;
3608 repo_path = realpath(optarg, NULL);
3609 if (repo_path == NULL)
3610 return got_error_from_errno2("realpath",
3612 got_path_strip_trailing_slashes(repo_path);
3626 if (do_list && delref)
3627 errx(1, "-l and -d options are mutually exclusive\n");
3632 if (!do_list && !delref && argc == 0)
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) {
3641 } else if (!do_show && argc != 1)
3645 if (do_list || do_show) {
3646 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
3650 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
3651 "sendfd unveil", NULL) == -1)
3655 cwd = getcwd(NULL, 0);
3657 error = got_error_from_errno("getcwd");
3661 if (repo_path == NULL) {
3662 error = got_worktree_open(&worktree, cwd);
3663 if (error && error->code != GOT_ERR_NOT_WORKTREE)
3669 strdup(got_worktree_get_repo_path(worktree));
3670 if (repo_path == NULL)
3671 error = got_error_from_errno("strdup");
3675 repo_path = strdup(cwd);
3676 if (repo_path == NULL) {
3677 error = got_error_from_errno("strdup");
3683 error = got_repo_open(&repo, repo_path, NULL);
3687 error = apply_unveil(got_repo_get_path(repo), do_list,
3688 worktree ? got_worktree_get_root_path(worktree) : NULL);
3693 error = show_current_branch(repo, worktree);
3695 error = list_branches(repo, worktree);
3697 error = delete_branch(repo, worktree, delref);
3699 if (commit_id_arg == NULL)
3700 commit_id_arg = worktree ?
3701 got_worktree_get_head_ref_name(worktree) :
3703 error = got_repo_match_object_id(&commit_id, NULL,
3704 commit_id_arg, GOT_OBJ_TYPE_COMMIT, 1, repo);
3707 error = add_branch(repo, argv[0], commit_id);
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);
3717 error = get_worktree_paths_from_argv(&paths, 0, NULL,
3721 if (asprintf(&branch_refname, "refs/heads/%s", argv[0])
3723 error = got_error_from_errno("asprintf");
3726 error = got_ref_open(&ref, repo, branch_refname, 0);
3727 free(branch_refname);
3730 error = switch_head_ref(ref, commit_id, worktree,
3734 error = got_worktree_set_base_commit_id(worktree, repo,
3738 error = got_worktree_checkout_files(worktree, &paths,
3739 repo, update_progress, &did_something,
3740 check_cancelled, NULL);
3744 printf("Updated to commit %s\n", commit_id_str);
3751 got_repo_close(repo);
3753 got_worktree_close(worktree);
3757 free(commit_id_str);
3758 TAILQ_FOREACH(pe, &paths, entry)
3759 free((char *)pe->path);
3760 got_pathlist_free(&paths);
3769 "usage: %s tag [-c commit] [-r repository] [-l] "
3770 "[-m message] name\n", getprogname());
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);
3787 err = got_reflist_entry_dup(&new, re);
3790 SIMPLEQ_INSERT_HEAD(sorted, new, entry);
3793 err = got_ref_resolve(&re_id, repo, re->ref);
3796 err = got_object_open_as_tag(&re_tag, repo, re_id);
3800 re_time = got_object_tag_get_tagger_time(re_tag);
3801 got_object_tag_close(re_tag);
3805 err = got_ref_resolve(&se_id, repo, re->ref);
3808 err = got_object_open_as_tag(&se_tag, repo, se_id);
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);
3819 SIMPLEQ_INSERT_AFTER(sorted, se, new, entry);
3822 se = SIMPLEQ_NEXT(se, entry);
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);
3844 SIMPLEQ_FOREACH(re, &refs, entry) {
3845 const char *refname;
3846 char *refstr, *tagmsg0, *tagmsg, *line, *id_str, *datestr;
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)
3858 refstr = got_ref_to_str(re->ref);
3859 if (refstr == NULL) {
3860 err = got_error_from_errno("got_ref_to_str");
3863 printf("%stag %s %s\n", GOT_COMMIT_SEP_STR, refname, refstr);
3866 err = got_ref_resolve(&id, repo, re->ref);
3869 err = got_object_open_as_tag(&tag, repo, id);
3871 if (err->code != GOT_ERR_OBJ_TYPE) {
3875 /* "lightweight" tag */
3876 err = got_object_open_as_commit(&commit, repo, id);
3881 tagger = got_object_commit_get_committer(commit);
3883 got_object_commit_get_committer_time(commit);
3884 err = got_object_id_str(&id_str, 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));
3897 printf("from: %s\n", tagger);
3898 datestr = get_datestr(&tagger_time, datebuf);
3900 printf("date: %s UTC\n", datestr);
3902 printf("object: %s %s\n", GOT_OBJ_LABEL_COMMIT, id_str);
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,
3909 case GOT_OBJ_TYPE_TREE:
3910 printf("object: %s %s\n", GOT_OBJ_LABEL_TREE,
3913 case GOT_OBJ_TYPE_COMMIT:
3914 printf("object: %s %s\n", GOT_OBJ_LABEL_COMMIT,
3917 case GOT_OBJ_TYPE_TAG:
3918 printf("object: %s %s\n", GOT_OBJ_LABEL_TAG,
3927 err = got_object_commit_get_logmsg(&tagmsg0, commit);
3930 got_object_commit_close(commit);
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");
3942 line = strsep(&tagmsg, "\n");
3944 printf(" %s\n", line);
3949 got_ref_list_free(&refs);
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;
3962 if (asprintf(&template, GOT_TMPDIR_STR "/got-tagmsg") == -1) {
3963 err = got_error_from_errno("asprintf");
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");
3973 err = got_opentemp_named_fd(tagmsg_path, &fd, template);
3977 dprintf(fd, initial_content);
3980 err = get_editor(&editor);
3983 err = edit_logmsg(tagmsg, editor, *tagmsg_path, initial_content);
3985 free(initial_content);
3989 /* Editor is done; we can now apply unveil(2) */
3991 err = apply_unveil(repo_path, 0, NULL);
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);
4024 err = got_repo_match_object_id(&commit_id, &label, commit_arg,
4025 GOT_OBJ_TYPE_COMMIT, 1, repo);
4029 err = got_object_id_str(&commit_id_str, commit_id);
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");
4040 } else if (asprintf(&refname, "refs/tags/%s", tag_name) == -1) {
4041 err = got_error_from_errno("asprintf");
4045 err = got_ref_open(&ref, repo, refname, 0);
4047 err = got_error(GOT_ERR_TAG_EXISTS);
4049 } else if (err->code != GOT_ERR_NOT_REF)
4052 if (tagmsg_arg == NULL) {
4053 err = get_tag_message(&tagmsg, &tagmsg_path, commit_id_str,
4054 tag_name, got_repo_get_path(repo));
4056 if (err->code != GOT_ERR_COMMIT_MSG_EMPTY &&
4057 tagmsg_path != NULL)
4058 preserve_tagmsg = 1;
4063 err = got_object_tag_create(&tag_id, tag_name, commit_id,
4064 tagger, time(NULL), tagmsg ? tagmsg : tagmsg_arg, repo);
4067 preserve_tagmsg = 1;
4071 err = got_ref_alloc(&ref, refname, tag_id);
4074 preserve_tagmsg = 1;
4078 err = got_ref_write(ref, repo);
4081 preserve_tagmsg = 1;
4085 err = got_object_id_str(&tag_id_str, tag_id);
4088 preserve_tagmsg = 1;
4091 printf("Created tag %s\n", tag_id_str);
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);
4102 free(commit_id_str);
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) {
4124 commit_id_arg = optarg;
4130 repo_path = realpath(optarg, NULL);
4131 if (repo_path == NULL)
4132 return got_error_from_errno2("realpath",
4134 got_path_strip_trailing_slashes(repo_path);
4149 if (commit_id_arg != NULL)
4150 errx(1, "-c option can only be used when creating a tag");
4152 errx(1, "-l and -m options are mutually exclusive");
4155 } else if (argc != 1)
4162 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
4166 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
4167 "sendfd unveil", NULL) == -1)
4171 cwd = getcwd(NULL, 0);
4173 error = got_error_from_errno("getcwd");
4177 if (repo_path == NULL) {
4178 error = got_worktree_open(&worktree, cwd);
4179 if (error && error->code != GOT_ERR_NOT_WORKTREE)
4185 strdup(got_worktree_get_repo_path(worktree));
4186 if (repo_path == NULL)
4187 error = got_error_from_errno("strdup");
4191 repo_path = strdup(cwd);
4192 if (repo_path == NULL) {
4193 error = got_error_from_errno("strdup");
4200 error = got_repo_open(&repo, repo_path, NULL);
4203 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
4206 error = list_tags(repo, worktree);
4208 error = get_gitconfig_path(&gitconfig_path);
4211 error = got_repo_open(&repo, repo_path, gitconfig_path);
4216 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
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)
4229 error = got_ref_resolve(&commit_id, repo, head_ref);
4230 got_ref_close(head_ref);
4233 error = got_object_id_str(&commit_id_str, commit_id);
4239 error = add_tag(repo, tag_name,
4240 commit_id_str ? commit_id_str : commit_id_arg, tagmsg);
4244 got_repo_close(repo);
4246 got_worktree_close(worktree);
4249 free(gitconfig_path);
4250 free(commit_id_str);
4257 fprintf(stderr, "usage: %s add [-R] [-I] path ...\n",
4262 static const struct got_error *
4263 add_progress(void *arg, unsigned char status, const char *path)
4265 while (path[0] == '/')
4267 printf("%c %s\n", status, path);
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;
4278 struct got_pathlist_head paths;
4279 struct got_pathlist_entry *pe;
4280 int ch, can_recurse = 0, no_ignores = 0;
4284 while ((ch = getopt(argc, argv, "IR")) != -1) {
4302 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
4309 cwd = getcwd(NULL, 0);
4311 error = got_error_from_errno("getcwd");
4315 error = got_worktree_open(&worktree, cwd);
4319 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
4324 error = apply_unveil(got_repo_get_path(repo), 1,
4325 got_worktree_get_root_path(worktree));
4329 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
4333 if (!can_recurse && no_ignores) {
4334 error = got_error_msg(GOT_ERR_BAD_PATH,
4335 "disregarding ignores requires -R option");
4343 TAILQ_FOREACH(pe, &paths, entry) {
4344 if (asprintf(&ondisk_path, "%s/%s",
4345 got_worktree_get_root_path(worktree),
4347 error = got_error_from_errno("asprintf");
4350 if (lstat(ondisk_path, &sb) == -1) {
4351 if (errno == ENOENT) {
4355 error = got_error_from_errno2("lstat",
4361 if (S_ISDIR(sb.st_mode)) {
4362 error = got_error_msg(GOT_ERR_BAD_PATH,
4363 "adding directories requires -R option");
4369 error = got_worktree_schedule_add(worktree, &paths, add_progress,
4370 NULL, repo, no_ignores);
4373 got_repo_close(repo);
4375 got_worktree_close(worktree);
4376 TAILQ_FOREACH(pe, &paths, entry)
4377 free((char *)pe->path);
4378 got_pathlist_free(&paths);
4386 fprintf(stderr, "usage: %s remove [-f] [-k] [-R] path ...\n",
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] == '/')
4397 if (status == GOT_STATUS_NONEXISTENT)
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);
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;
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;
4418 while ((ch = getopt(argc, argv, "fkR")) != -1) {
4421 delete_local_mods = 1;
4439 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
4446 cwd = getcwd(NULL, 0);
4448 error = got_error_from_errno("getcwd");
4451 error = got_worktree_open(&worktree, cwd);
4455 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
4460 error = apply_unveil(got_repo_get_path(repo), 1,
4461 got_worktree_get_root_path(worktree));
4465 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
4472 TAILQ_FOREACH(pe, &paths, entry) {
4473 if (asprintf(&ondisk_path, "%s/%s",
4474 got_worktree_get_root_path(worktree),
4476 error = got_error_from_errno("asprintf");
4479 if (lstat(ondisk_path, &sb) == -1) {
4480 if (errno == ENOENT) {
4484 error = got_error_from_errno2("lstat",
4490 if (S_ISDIR(sb.st_mode)) {
4491 error = got_error_msg(GOT_ERR_BAD_PATH,
4492 "removing directories requires -R option");
4498 error = got_worktree_schedule_delete(worktree, &paths,
4499 delete_local_mods, print_remove_status, NULL, repo, keep_on_disk);
4502 got_repo_close(repo);
4504 got_worktree_close(worktree);
4505 TAILQ_FOREACH(pe, &paths, entry)
4506 free((char *)pe->path);
4507 got_pathlist_free(&paths);
4515 fprintf(stderr, "usage: %s revert [-p] [-F response-script] [-R] "
4516 "path ...\n", getprogname());
4520 static const struct got_error *
4521 revert_progress(void *arg, unsigned char status, const char *path)
4523 if (status == GOT_STATUS_UNVERSIONED)
4526 while (path[0] == '/')
4528 printf("%c %s\n", status, path);
4532 struct choose_patch_arg {
4533 FILE *patch_script_file;
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)
4542 size_t linesize = 0;
4546 case GOT_STATUS_ADD:
4547 printf("A %s\n%s this addition? [y/n] ", path, action);
4549 case GOT_STATUS_DELETE:
4550 printf("D %s\n%s this deletion? [y/n] ", path, action);
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)
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);
4565 return got_error_path(path, GOT_ERR_FILE_STATUS);
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;
4577 size_t linesize = 0;
4580 struct choose_patch_arg *a = arg;
4582 *choice = GOT_PATCH_CHOICE_NONE;
4584 if (a->patch_script_file) {
4586 err = show_change(status, path, patch_file, n, nchanges,
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");
4596 nl = strchr(line, '\n');
4599 if (strcmp(line, "y") == 0) {
4600 *choice = GOT_PATCH_CHOICE_YES;
4602 } else if (strcmp(line, "n") == 0) {
4603 *choice = GOT_PATCH_CHOICE_NO;
4605 } else if (strcmp(line, "q") == 0 &&
4606 status == GOT_STATUS_MODIFY) {
4607 *choice = GOT_PATCH_CHOICE_QUIT;
4610 printf("invalid response '%s'\n", line);
4615 while (resp != 'y' && resp != 'n' && resp != 'q') {
4616 err = show_change(status, path, patch_file, n, nchanges,
4623 if (status == GOT_STATUS_MODIFY) {
4624 if (resp != 'y' && resp != 'n' && resp != 'q') {
4625 printf("invalid response '%c'\n", resp);
4628 } else if (resp != 'y' && resp != 'n') {
4629 printf("invalid response '%c'\n", resp);
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;
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;
4661 while ((ch = getopt(argc, argv, "pF:R")) != -1) {
4667 patch_script_path = optarg;
4682 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
4683 "unveil", NULL) == -1)
4688 if (patch_script_path && !pflag)
4689 errx(1, "-F option can only be used together with -p option");
4691 cwd = getcwd(NULL, 0);
4693 error = got_error_from_errno("getcwd");
4696 error = got_worktree_open(&worktree, cwd);
4700 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
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",
4713 error = apply_unveil(got_repo_get_path(repo), 1,
4714 got_worktree_get_root_path(worktree));
4718 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
4725 TAILQ_FOREACH(pe, &paths, entry) {
4726 if (asprintf(&ondisk_path, "%s/%s",
4727 got_worktree_get_root_path(worktree),
4729 error = got_error_from_errno("asprintf");
4732 if (lstat(ondisk_path, &sb) == -1) {
4733 if (errno == ENOENT) {
4737 error = got_error_from_errno2("lstat",
4743 if (S_ISDIR(sb.st_mode)) {
4744 error = got_error_msg(GOT_ERR_BAD_PATH,
4745 "reverting directories requires -R option");
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);
4756 if (patch_script_file && fclose(patch_script_file) == EOF &&
4758 error = got_error_from_errno2("fclose", patch_script_path);
4760 got_repo_close(repo);
4762 got_worktree_close(worktree);
4771 fprintf(stderr, "usage: %s commit [-m msg] [path ...]\n",
4776 struct collect_commit_logmsg_arg {
4777 const char *cmdline_log;
4779 const char *worktree_path;
4780 const char *branch_name;
4781 const char *repo_path;
4786 static const struct got_error *
4787 collect_commit_logmsg(struct got_pathlist_head *commitable_paths, char **logmsg,
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;
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);
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);
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));
4830 err = edit_logmsg(logmsg, a->editor, a->logmsg_path, initial_content);
4832 free(initial_content);
4835 /* Editor is done; we can now apply unveil(2) */
4837 err = apply_unveil(a->repo_path, 0, a->worktree_path);
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;
4861 cl_arg.logmsg_path = NULL;
4863 while ((ch = getopt(argc, argv, "m:")) != -1) {
4878 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
4879 "unveil", NULL) == -1)
4882 cwd = getcwd(NULL, 0);
4884 error = got_error_from_errno("getcwd");
4887 error = got_worktree_open(&worktree, cwd);
4891 error = got_worktree_rebase_in_progress(&rebase_in_progress, worktree);
4894 if (rebase_in_progress) {
4895 error = got_error(GOT_ERR_REBASING);
4899 error = got_worktree_histedit_in_progress(&histedit_in_progress,
4904 error = get_gitconfig_path(&gitconfig_path);
4907 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
4912 error = get_author(&author, repo);
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);
4923 error = apply_unveil(got_repo_get_path(repo), 0,
4924 got_worktree_get_root_path(worktree));
4928 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
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);
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);
4947 if (error->code != GOT_ERR_COMMIT_MSG_EMPTY &&
4948 cl_arg.logmsg_path != NULL)
4949 preserve_logmsg = 1;
4953 error = got_object_id_str(&id_str, id);
4956 printf("Created commit %s\n", id_str);
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 &&
4963 error = got_error_from_errno2("unlink", cl_arg.logmsg_path);
4964 free(cl_arg.logmsg_path);
4966 got_repo_close(repo);
4968 got_worktree_close(worktree);
4971 free(gitconfig_path);
4978 usage_cherrypick(void)
4980 fprintf(stderr, "usage: %s cherrypick commit-id\n", getprogname());
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) {
5009 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
5010 "unveil", NULL) == -1)
5016 cwd = getcwd(NULL, 0);
5018 error = got_error_from_errno("getcwd");
5021 error = got_worktree_open(&worktree, cwd);
5025 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
5030 error = apply_unveil(got_repo_get_path(repo), 0,
5031 got_worktree_get_root_path(worktree));
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)
5041 error = got_ref_open(&ref, repo, argv[0], 0);
5044 error = got_ref_resolve(&commit_id, repo, ref);
5049 error = got_object_id_str(&commit_id_str, commit_id);
5053 error = got_ref_open(&head_ref, repo,
5054 got_worktree_get_head_ref_name(worktree), 0);
5058 error = check_same_branch(commit_id, head_ref, NULL, repo);
5060 if (error->code != GOT_ERR_ANCESTRY)
5064 error = got_error(GOT_ERR_SAME_BRANCH);
5068 error = got_object_open_as_commit(&commit, repo, commit_id);
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,
5079 printf("Merged commit %s\n", commit_id_str);
5082 got_object_commit_close(commit);
5083 free(commit_id_str);
5085 got_ref_close(head_ref);
5087 got_worktree_close(worktree);
5089 got_repo_close(repo);
5096 fprintf(stderr, "usage: %s backout commit-id\n", getprogname());
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) {
5125 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
5126 "unveil", NULL) == -1)
5132 cwd = getcwd(NULL, 0);
5134 error = got_error_from_errno("getcwd");
5137 error = got_worktree_open(&worktree, cwd);
5141 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
5146 error = apply_unveil(got_repo_get_path(repo), 0,
5147 got_worktree_get_root_path(worktree));
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)
5157 error = got_ref_open(&ref, repo, argv[0], 0);
5160 error = got_ref_resolve(&commit_id, repo, ref);
5165 error = got_object_id_str(&commit_id_str, commit_id);
5169 error = got_ref_open(&head_ref, repo,
5170 got_worktree_get_head_ref_name(worktree), 0);
5174 error = check_same_branch(commit_id, head_ref, NULL, repo);
5178 error = got_object_open_as_commit(&commit, repo, commit_id);
5181 pid = SIMPLEQ_FIRST(got_object_commit_get_parent_ids(commit));
5183 error = got_error(GOT_ERR_ROOT_COMMIT);
5187 error = got_worktree_merge_files(worktree, commit_id, pid->id, repo,
5188 update_progress, &did_something, check_cancelled, NULL);
5193 printf("Backed out commit %s\n", commit_id_str);
5196 got_object_commit_close(commit);
5197 free(commit_id_str);
5199 got_ref_close(head_ref);
5201 got_worktree_close(worktree);
5203 got_repo_close(repo);
5210 fprintf(stderr, "usage: %s rebase [-a] | [-c] | branch\n",
5216 trim_logmsg(char *logmsg, int limit)
5221 len = strlen(logmsg);
5225 nl = strchr(logmsg, '\n');
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;
5237 err = got_object_commit_get_logmsg(&logmsg0, commit);
5242 while (isspace((unsigned char)s[0]))
5245 *logmsg = strdup(s);
5246 if (*logmsg == NULL) {
5247 err = got_error_from_errno("strdup");
5251 trim_logmsg(*logmsg, limit);
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);
5268 err = got_object_id_str(&id_str, id);
5274 err = get_short_logmsg(&logmsg, 42, commit);
5278 printf("%s -> merge conflict: %s\n", id_str, logmsg);
5281 got_object_commit_close(commit);
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);
5298 err = got_object_id_str(&new_id_str, new_id);
5303 old_id_str[12] = '\0';
5305 new_id_str[12] = '\0';
5307 err = get_short_logmsg(&logmsg, 42, commit);
5311 printf("%s -> %s: %s\n", old_id_str,
5312 new_id_str ? new_id_str : "no-op change", logmsg);
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] == '/')
5327 printf("%c %s\n", status, path);
5329 if (*rebase_status == GOT_STATUS_CONFLICT)
5331 if (status == GOT_STATUS_CONFLICT || status == GOT_STATUS_MERGE)
5332 *rebase_status = status;
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);
5360 error = got_worktree_rebase_commit(&new_commit_id, merged_paths,
5361 worktree, fileindex, tmp_branch, commit, commit_id, repo);
5363 if (error->code != GOT_ERR_COMMIT_NO_CHANGES)
5365 error = show_rebase_progress(commit, commit_id, NULL);
5367 error = show_rebase_progress(commit, commit_id, new_commit_id);
5368 free(new_commit_id);
5371 got_object_commit_close(commit);
5375 struct check_path_prefix_arg {
5376 const char *path_prefix;
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);
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))
5409 err = got_object_open_as_commit(&commit, repo, commit_id);
5413 err = got_object_open_as_commit(&parent_commit, repo, parent_id);
5417 err = got_object_open_as_tree(&tree1, repo,
5418 got_object_commit_get_tree_id(parent_commit));
5422 err = got_object_open_as_tree(&tree2, repo,
5423 got_object_commit_get_tree_id(commit));
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);
5436 got_object_tree_close(tree1);
5438 got_object_tree_close(tree2);
5440 got_object_commit_close(commit);
5442 got_object_commit_close(parent_commit);
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);
5463 err = got_commit_graph_iter_start(graph, iter_start_id, repo,
5464 check_cancelled, NULL);
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);
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 "
5479 err = check_path_prefix(parent_id, commit_id,
5480 path_prefix, path_prefix_errcode, repo);
5484 err = got_object_qid_alloc(&qid, commit_id);
5487 SIMPLEQ_INSERT_HEAD(commits, qid, entry);
5488 commit_id = parent_id;
5492 got_commit_graph_close(graph);
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;
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) {
5527 continue_rebase = 1;
5539 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
5540 "unveil", NULL) == -1)
5543 if (abort_rebase && continue_rebase)
5545 else if (abort_rebase || continue_rebase) {
5548 } else if (argc != 1)
5551 cwd = getcwd(NULL, 0);
5553 error = got_error_from_errno("getcwd");
5556 error = got_worktree_open(&worktree, cwd);
5560 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
5565 error = apply_unveil(got_repo_get_path(repo), 0,
5566 got_worktree_get_root_path(worktree));
5570 error = got_worktree_histedit_in_progress(&histedit_in_progress,
5574 if (histedit_in_progress) {
5575 error = got_error(GOT_ERR_HISTEDIT_BUSY);
5579 error = got_worktree_rebase_in_progress(&rebase_in_progress, worktree);
5585 if (!rebase_in_progress) {
5586 error = got_error(GOT_ERR_NOT_REBASING);
5589 error = got_worktree_rebase_continue(&resume_commit_id,
5590 &new_base_branch, &tmp_branch, &branch, &fileindex,
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);
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);
5609 error = got_worktree_rebase_continue(&resume_commit_id,
5610 &new_base_branch, &tmp_branch, &branch, &fileindex,
5615 error = rebase_commit(NULL, worktree, fileindex, tmp_branch,
5616 resume_commit_id, repo);
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");
5626 error = got_ref_open(&branch, repo, argv[0], 0);
5631 error = got_ref_resolve(&branch_head_commit_id, repo, branch);
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);
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");
5651 error = check_same_branch(base_commit_id, branch, yca_id, repo);
5653 if (error->code != GOT_ERR_ANCESTRY)
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");
5662 error = got_worktree_rebase_prepare(&new_base_branch,
5663 &tmp_branch, &fileindex, worktree, branch, repo);
5668 commit_id = branch_head_commit_id;
5669 error = got_object_open_as_commit(&commit, repo, commit_id);
5673 parent_ids = got_object_commit_get_parent_ids(commit);
5674 pid = SIMPLEQ_FIRST(parent_ids);
5676 if (!continue_rebase) {
5678 error = got_worktree_rebase_abort(worktree, fileindex,
5679 repo, new_base_branch, update_progress,
5683 printf("Rebase of %s aborted\n",
5684 got_ref_get_name(branch));
5686 error = got_error(GOT_ERR_EMPTY_REBASE);
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);
5697 if (SIMPLEQ_EMPTY(&commits)) {
5698 if (continue_rebase) {
5699 error = rebase_complete(worktree, fileindex,
5700 branch, new_base_branch, tmp_branch, repo);
5703 /* Fast-forward the reference of the branch. */
5704 struct got_object_id *new_head_commit_id;
5706 error = got_ref_resolve(&new_head_commit_id, repo,
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);
5714 error = got_ref_change_ref(branch,
5715 new_head_commit_id);
5722 SIMPLEQ_FOREACH(qid, &commits, entry) {
5723 commit_id = qid->id;
5724 parent_id = pid ? pid->id : yca_id;
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);
5733 if (rebase_status == GOT_STATUS_CONFLICT) {
5734 error = show_rebase_merge_conflict(qid->id, repo);
5737 got_worktree_rebase_pathlist_free(&merged_paths);
5741 error = rebase_commit(&merged_paths, worktree, fileindex,
5742 tmp_branch, commit_id, repo);
5743 got_worktree_rebase_pathlist_free(&merged_paths);
5748 if (rebase_status == GOT_STATUS_CONFLICT) {
5749 error = got_worktree_rebase_postpone(worktree, fileindex);
5752 error = got_error_msg(GOT_ERR_CONFLICTS,
5753 "conflicts must be resolved before rebasing can continue");
5755 error = rebase_complete(worktree, fileindex, branch,
5756 new_base_branch, tmp_branch, repo);
5758 got_object_id_queue_free(&commits);
5759 free(branch_head_commit_id);
5760 free(resume_commit_id);
5763 got_object_commit_close(commit);
5765 got_ref_close(branch);
5766 if (new_base_branch)
5767 got_ref_close(new_base_branch);
5769 got_ref_close(tmp_branch);
5771 got_worktree_close(worktree);
5773 got_repo_close(repo);
5778 usage_histedit(void)
5780 fprintf(stderr, "usage: %s histedit [-a] [-c] [-F histedit-script] [-m]\n",
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 {
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 "
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;
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;
5822 err = got_object_open_as_commit(&commit, repo, commit_id);
5826 err = get_short_logmsg(&logmsg, 34, commit);
5830 err = got_object_id_str(&id_str, commit_id);
5834 n = fprintf(f, "%s %s %s\n", cmdname, id_str, logmsg);
5836 err = got_ferror(f, GOT_ERR_IO);
5839 got_object_commit_close(commit);
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,
5860 if (edit_logmsg_only) {
5861 int n = fprintf(f, "%c\n", GOT_HISTEDIT_MESG);
5863 err = got_ferror(f, GOT_ERR_IO);
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;
5879 struct got_object_qid *qid;
5881 qid = SIMPLEQ_FIRST(commits);
5882 err = got_object_id_str(&id_str, qid->id);
5887 "# Editing the history of branch '%s' starting at\n"
5889 "# Commits will be processed in order from top to "
5890 "bottom of this file.\n", branch_name, id_str);
5892 err = got_ferror(f, GOT_ERR_IO);
5896 n = fprintf(f, "# Available histedit commands:\n");
5898 err = got_ferror(f, GOT_ERR_IO);
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,
5907 err = got_ferror(f, GOT_ERR_IO);
5916 static const struct got_error *
5917 histedit_syntax_error(int lineno)
5919 static char msg[42];
5922 ret = snprintf(msg, sizeof(msg), "histedit syntax error on line %d",
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);
5942 err = got_object_open_as_commit(&folded_commit, repo, hle->commit_id);
5946 err = got_object_commit_get_logmsg(&folded_logmsg, folded_commit);
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");
5956 got_object_commit_close(folded_commit);
5958 free(folded_logmsg);
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)
5972 prev = TAILQ_PREV(prev, got_histedit_list, entry);
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;
5987 struct got_histedit_list_entry *folded = NULL;
5989 err = got_object_open_as_commit(&commit, repo, hle->commit_id);
5993 folded = get_folded_commits(hle);
5995 while (folded != hle) {
5996 if (folded->cmd->code == GOT_HISTEDIT_DROP) {
5997 folded = TAILQ_NEXT(folded, entry);
6000 err = append_folded_commit_msg(&new_msg, folded,
6006 folded = TAILQ_NEXT(folded, entry);
6010 err = got_object_id_str(&id_str, hle->commit_id);
6013 err = got_object_commit_get_logmsg(&orig_logmsg, commit);
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");
6025 err = got_object_id_str(&id_str, hle->commit_id);
6029 err = got_opentemp_named_fd(&logmsg_path, &fd,
6030 GOT_TMPDIR_STR "/got-logmsg");
6034 dprintf(fd, logmsg);
6037 err = get_editor(&editor);
6041 err = edit_logmsg(&hle->logmsg, editor, logmsg_path, logmsg);
6043 if (err->code != GOT_ERR_COMMIT_MSG_EMPTY)
6045 err = got_object_commit_get_logmsg(&hle->logmsg, commit);
6048 if (logmsg_path && unlink(logmsg_path) != 0 && err == NULL)
6049 err = got_error_from_errno2("unlink", logmsg_path);
6055 got_object_commit_close(commit);
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;
6068 const struct got_histedit_cmd *cmd;
6069 struct got_object_id *commit_id = NULL;
6070 struct got_histedit_list_entry *hle = NULL;
6073 len = getline(&line, &size, f);
6075 const struct got_error *getline_err;
6078 getline_err = got_error_from_errno("getline");
6079 err = got_ferror(f, getline_err->code);
6084 while (isspace((unsigned char)p[0]))
6086 if (p[0] == '#' || p[0] == '\0') {
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);
6099 if (p[0] == cmd->code && isspace((unsigned char)p[1])) {
6104 if (i == nitems(got_histedit_cmds)) {
6105 err = histedit_syntax_error(lineno);
6108 while (isspace((unsigned char)p[0]))
6110 if (cmd->code == GOT_HISTEDIT_MESG) {
6111 if (hle == NULL || hle->logmsg != NULL) {
6112 err = got_error(GOT_ERR_HISTEDIT_CMD);
6116 err = histedit_edit_logmsg(hle, repo);
6120 hle->logmsg = strdup(p);
6121 if (hle->logmsg == NULL) {
6122 err = got_error_from_errno("strdup");
6131 while (end[0] && !isspace((unsigned char)end[0]))
6135 err = got_object_resolve_id_str(&commit_id, repo, p);
6137 /* override error code */
6138 err = histedit_syntax_error(lineno);
6142 hle = malloc(sizeof(*hle));
6144 err = got_error_from_errno("malloc");
6148 hle->commit_id = commit_id;
6153 TAILQ_INSERT_TAIL(histedit_cmds, hle, entry);
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];
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) {
6182 if (got_object_id_cmp(hle->commit_id,
6183 hle2->commit_id) != 0)
6185 err = got_object_id_str(&id_str, hle->commit_id);
6188 snprintf(msg, sizeof(msg), "commit %s is listed "
6189 "more than once in histedit script", 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)
6201 err = got_object_id_str(&id_str, qid->id);
6204 snprintf(msg, sizeof(msg),
6205 "commit %s missing from histedit script", 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");
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;
6228 err = get_editor(&editor);
6232 if (spawn_editor(editor, path) == -1) {
6233 err = got_error_from_errno("failed spawning editor");
6237 f = fopen(path, "r");
6239 err = got_error_from_errno("fopen");
6242 err = histedit_parse_list(histedit_cmds, f, repo);
6246 err = histedit_check_script(histedit_cmds, commits, repo);
6248 if (f && fclose(f) != 0 && err == NULL)
6249 err = got_error_from_errno("fclose");
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;
6268 err = got_opentemp_named(&path, &f, "got-histedit");
6272 err = write_cmd_list(f, branch_name, commits);
6276 err = histedit_write_commit_list(commits, f, edit_logmsg_only, repo);
6280 if (edit_logmsg_only) {
6282 err = histedit_parse_list(histedit_cmds, f, repo);
6284 if (fclose(f) != 0) {
6285 err = got_error_from_errno("fclose");
6289 err = histedit_run_editor(histedit_cmds, path, commits, repo);
6291 if (err->code != GOT_ERR_HISTEDIT_SYNTAX &&
6292 err->code != GOT_ERR_HISTEDIT_CMD)
6294 err = histedit_edit_list_retry(histedit_cmds, err,
6295 commits, path, branch_name, repo);
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);
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;
6314 struct got_histedit_list_entry *hle;
6315 struct got_commit_object *commit = NULL;
6317 err = got_worktree_get_histedit_script_path(&path, worktree);
6321 f = fopen(path, "w");
6323 err = got_error_from_errno2("fopen", path);
6326 TAILQ_FOREACH(hle, histedit_cmds, entry) {
6327 err = histedit_write_commit(hle->commit_id, hle->cmd->name, f,
6333 int n = fprintf(f, "%c %s\n",
6334 GOT_HISTEDIT_MESG, hle->logmsg);
6336 err = got_ferror(f, GOT_ERR_IO);
6342 if (f && fclose(f) != 0 && err == NULL)
6343 err = got_error_from_errno("fclose");
6346 got_object_commit_close(commit);
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);
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;
6368 f = fopen(path, "r");
6370 err = got_error_from_errno2("fopen", path);
6374 err = histedit_parse_list(histedit_cmds, f, repo);
6376 if (f && fclose(f) != 0 && err == NULL)
6377 err = got_error_from_errno("fclose");
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;
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);
6396 histedit_free_list(histedit_cmds);
6397 err = histedit_run_editor(histedit_cmds, path, commits,
6400 if (err->code != GOT_ERR_HISTEDIT_SYNTAX &&
6401 err->code != GOT_ERR_HISTEDIT_CMD)
6408 } else if (resp == 'r') {
6409 histedit_free_list(histedit_cmds);
6410 err = histedit_edit_script(histedit_cmds,
6411 commits, branch_name, 0, repo);
6413 if (err->code != GOT_ERR_HISTEDIT_SYNTAX &&
6414 err->code != GOT_ERR_HISTEDIT_CMD)
6421 } else if (resp == 'a') {
6422 err = got_error(GOT_ERR_HISTEDIT_CANCEL);
6425 printf("invalid response '%c'\n", resp);
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,
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);
6454 err = got_object_id_str(&new_id_str, new_id);
6459 old_id_str[12] = '\0';
6461 new_id_str[12] = '\0';
6464 logmsg = strdup(hle->logmsg);
6465 if (logmsg == NULL) {
6466 err = got_error_from_errno("strdup");
6469 trim_logmsg(logmsg, 42);
6471 err = get_short_logmsg(&logmsg, 42, commit);
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);
6482 case GOT_HISTEDIT_DROP:
6483 case GOT_HISTEDIT_FOLD:
6484 printf("%s -> %s commit: %s\n", old_id_str, hle->cmd->name,
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);
6513 err = got_object_open_as_commit(&commit, repo, hle->commit_id);
6517 err = got_worktree_histedit_commit(&new_commit_id, merged_paths,
6518 worktree, fileindex, tmp_branch, commit, hle->commit_id,
6521 if (err->code != GOT_ERR_COMMIT_NO_CHANGES)
6523 err = show_histedit_progress(commit, hle, NULL);
6525 err = show_histedit_progress(commit, hle, new_commit_id);
6526 free(new_commit_id);
6529 got_object_commit_close(commit);
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,
6545 error = got_object_open_as_commit(&commit, repo, hle->commit_id);
6549 error = show_histedit_progress(commit, hle, NULL);
6550 got_object_commit_close(commit);
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;
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);
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);
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;
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) {
6625 edit_script_path = optarg;
6628 edit_logmsg_only = 1;
6640 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
6641 "unveil", NULL) == -1)
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");
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);
6666 error = got_error_from_errno("getcwd");
6669 error = got_worktree_open(&worktree, cwd);
6673 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
6678 error = got_worktree_rebase_in_progress(&rebase_in_progress, worktree);
6681 if (rebase_in_progress) {
6682 error = got_error(GOT_ERR_REBASING);
6686 error = got_worktree_histedit_in_progress(&edit_in_progress, worktree);
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");
6698 if (edit_in_progress && abort_edit) {
6699 error = got_worktree_histedit_continue(&resume_commit_id,
6700 &tmp_branch, &branch, &base_commit_id, &fileindex,
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);
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);
6718 if (continue_edit) {
6721 if (!edit_in_progress) {
6722 error = got_error(GOT_ERR_NOT_HISTEDIT);
6726 error = got_worktree_get_histedit_script_path(&path, worktree);
6730 error = histedit_load_list(&histedit_cmds, path, repo);
6735 error = got_worktree_histedit_continue(&resume_commit_id,
6736 &tmp_branch, &branch, &base_commit_id, &fileindex,
6741 error = got_ref_resolve(&head_commit_id, repo, branch);
6745 error = got_object_open_as_commit(&commit, repo,
6749 parent_ids = got_object_commit_get_parent_ids(commit);
6750 pid = SIMPLEQ_FIRST(parent_ids);
6752 error = got_error(GOT_ERR_EMPTY_HISTEDIT);
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);
6763 if (edit_in_progress) {
6764 error = got_error(GOT_ERR_HISTEDIT_BUSY);
6768 error = got_ref_open(&branch, repo,
6769 got_worktree_get_head_ref_name(worktree), 0);
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");
6780 error = got_ref_resolve(&head_commit_id, repo, branch);
6781 got_ref_close(branch);
6786 error = got_object_open_as_commit(&commit, repo,
6790 parent_ids = got_object_commit_get_parent_ids(commit);
6791 pid = SIMPLEQ_FIRST(parent_ids);
6793 error = got_error(GOT_ERR_EMPTY_HISTEDIT);
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);
6805 if (SIMPLEQ_EMPTY(&commits)) {
6806 error = got_error(GOT_ERR_EMPTY_HISTEDIT);
6810 error = got_worktree_histedit_prepare(&tmp_branch, &branch,
6811 &base_commit_id, &fileindex, worktree, repo);
6815 if (edit_script_path) {
6816 error = histedit_load_list(&histedit_cmds,
6817 edit_script_path, repo);
6819 got_worktree_histedit_abort(worktree, fileindex,
6820 repo, branch, base_commit_id,
6821 update_progress, &did_something);
6825 const char *branch_name;
6826 branch_name = got_ref_get_symref_target(branch);
6827 if (strncmp(branch_name, "refs/heads/", 11) == 0)
6829 error = histedit_edit_script(&histedit_cmds, &commits,
6830 branch_name, edit_logmsg_only, repo);
6832 got_worktree_histedit_abort(worktree, fileindex,
6833 repo, branch, base_commit_id,
6834 update_progress, &did_something);
6840 error = histedit_save_list(&histedit_cmds, worktree,
6843 got_worktree_histedit_abort(worktree, fileindex,
6844 repo, branch, base_commit_id,
6845 update_progress, &did_something);
6851 error = histedit_check_script(&histedit_cmds, &commits, repo);
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)
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,
6869 struct got_pathlist_head paths;
6870 int have_changes = 0;
6873 error = got_pathlist_append(&paths, "", NULL);
6876 error = got_worktree_status(worktree, &paths,
6877 repo, check_local_changes, &have_changes,
6878 check_cancelled, NULL);
6879 got_pathlist_free(&paths);
6881 if (error->code != GOT_ERR_CANCELLED)
6883 if (sigint_received || sigpipe_received)
6887 error = histedit_commit(NULL, worktree,
6888 fileindex, tmp_branch, hle, repo);
6892 error = got_object_open_as_commit(
6893 &commit, repo, hle->commit_id);
6896 error = show_histedit_progress(commit,
6898 got_object_commit_close(commit);
6907 if (hle->cmd->code == GOT_HISTEDIT_DROP) {
6908 error = histedit_skip_commit(hle, worktree, repo);
6914 error = got_object_open_as_commit(&commit, repo,
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);
6926 got_object_commit_close(commit);
6929 if (rebase_status == GOT_STATUS_CONFLICT) {
6930 error = show_rebase_merge_conflict(hle->commit_id,
6934 got_worktree_rebase_pathlist_free(&merged_paths);
6938 if (hle->cmd->code == GOT_HISTEDIT_EDIT) {
6940 error = got_object_id_str(&id_str, hle->commit_id);
6943 printf("Stopping histedit for amending commit %s\n",
6946 got_worktree_rebase_pathlist_free(&merged_paths);
6947 error = got_worktree_histedit_postpone(worktree,
6952 if (hle->cmd->code == GOT_HISTEDIT_FOLD) {
6953 error = histedit_skip_commit(hle, worktree, repo);
6959 error = histedit_commit(&merged_paths, worktree, fileindex,
6960 tmp_branch, hle, repo);
6961 got_worktree_rebase_pathlist_free(&merged_paths);
6966 if (rebase_status == GOT_STATUS_CONFLICT) {
6967 error = got_worktree_histedit_postpone(worktree, fileindex);
6970 error = got_error_msg(GOT_ERR_CONFLICTS,
6971 "conflicts must be resolved before histedit can continue");
6973 error = histedit_complete(worktree, fileindex, tmp_branch,
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);
6982 got_object_commit_close(commit);
6984 got_ref_close(branch);
6986 got_ref_close(tmp_branch);
6988 got_worktree_close(worktree);
6990 got_repo_close(repo);
6995 usage_integrate(void)
6997 fprintf(stderr, "usage: %s integrate branch\n", getprogname());
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) {
7027 branch_arg = argv[0];
7029 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
7030 "unveil", NULL) == -1)
7033 cwd = getcwd(NULL, 0);
7035 error = got_error_from_errno("getcwd");
7039 error = got_worktree_open(&worktree, cwd);
7043 error = check_rebase_or_histedit_in_progress(worktree);
7047 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
7052 error = apply_unveil(got_repo_get_path(repo), 0,
7053 got_worktree_get_root_path(worktree));
7057 if (asprintf(&refname, "refs/heads/%s", branch_arg) == -1) {
7058 error = got_error_from_errno("asprintf");
7062 error = got_worktree_integrate_prepare(&fileindex, &branch_ref,
7063 &base_branch_ref, worktree, refname, repo);
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);
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);
7082 error = got_ref_resolve(&commit_id, repo, branch_ref);
7086 error = got_ref_resolve(&base_commit_id, repo, base_branch_ref);
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);
7098 error = check_linear_ancestry(commit_id, base_commit_id, 1, repo);
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);
7107 error = got_worktree_integrate_continue(worktree, fileindex, repo,
7108 branch_ref, base_branch_ref, update_progress, &did_something,
7109 check_cancelled, NULL);
7113 printf("Integrated %s into %s\n", refname, base_refname);
7116 got_repo_close(repo);
7118 got_worktree_close(worktree);
7120 free(base_commit_id);
7130 fprintf(stderr, "usage: %s stage [-l] | [-p] [-F response-script] "
7131 "[file-path ...]\n",
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)
7150 if (staged_status == GOT_STATUS_ADD ||
7151 staged_status == GOT_STATUS_MODIFY)
7152 err = got_object_id_str(&id_str, staged_blob_id);
7154 err = got_object_id_str(&id_str, blob_id);
7158 printf("%s %c %s\n", id_str, staged_status, path);
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;
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;
7179 while ((ch = getopt(argc, argv, "lpF:")) != -1) {
7188 patch_script_path = optarg;
7200 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
7201 "unveil", NULL) == -1)
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);
7211 error = got_error_from_errno("getcwd");
7215 error = got_worktree_open(&worktree, cwd);
7219 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
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",
7232 error = apply_unveil(got_repo_get_path(repo), 0,
7233 got_worktree_get_root_path(worktree));
7237 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
7242 error = got_worktree_status(worktree, &paths, repo,
7243 print_stage, NULL, check_cancelled, NULL);
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);
7252 if (patch_script_file && fclose(patch_script_file) == EOF &&
7254 error = got_error_from_errno2("fclose", patch_script_path);
7256 got_repo_close(repo);
7258 got_worktree_close(worktree);
7259 TAILQ_FOREACH(pe, &paths, entry)
7260 free((char *)pe->path);
7261 got_pathlist_free(&paths);
7269 fprintf(stderr, "usage: %s unstage [-p] [-F response-script] "
7270 "[file-path ...]\n",
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;
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;
7292 while ((ch = getopt(argc, argv, "pF:")) != -1) {
7298 patch_script_path = optarg;
7310 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
7311 "unveil", NULL) == -1)
7314 if (patch_script_path && !pflag)
7315 errx(1, "-F option can only be used together with -p option");
7317 cwd = getcwd(NULL, 0);
7319 error = got_error_from_errno("getcwd");
7323 error = got_worktree_open(&worktree, cwd);
7327 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
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",
7341 error = apply_unveil(got_repo_get_path(repo), 0,
7342 got_worktree_get_root_path(worktree));
7346 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
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);
7355 if (patch_script_file && fclose(patch_script_file) == EOF &&
7357 error = got_error_from_errno2("fclose", patch_script_path);
7359 got_repo_close(repo);
7361 got_worktree_close(worktree);
7362 TAILQ_FOREACH(pe, &paths, entry)
7363 free((char *)pe->path);
7364 got_pathlist_free(&paths);
7372 fprintf(stderr, "usage: %s cat [-r repository ] [ -c commit ] [ -P ] "
7373 "arg1 [arg2 ...]\n", getprogname());
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);
7387 err = got_object_blob_dump_to_file(NULL, NULL, NULL, outfile, blob);
7388 got_object_blob_close(blob);
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;
7399 err = got_object_open_as_tree(&tree, repo, id);
7403 nentries = got_object_tree_get_nentries(tree);
7404 for (i = 0; i < nentries; i++) {
7405 struct got_tree_entry *te;
7407 if (sigint_received || sigpipe_received)
7409 te = got_object_tree_get_entry(tree, i);
7410 err = got_object_id_str(&id_str, got_tree_entry_get_id(te));
7413 fprintf(outfile, "%s %.7o %s\n", id_str,
7414 got_tree_entry_get_mode(te),
7415 got_tree_entry_get_name(te));
7419 got_object_tree_close(tree);
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);
7437 err = got_object_id_str(&id_str, got_object_commit_get_tree_id(commit));
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) {
7447 err = got_object_id_str(&pid_str, pid->id);
7450 fprintf(outfile, "%s%s\n", GOT_COMMIT_LABEL_PARENT, 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);
7466 got_object_commit_close(commit);
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);
7482 err = got_object_id_str(&id_str, got_object_tag_get_object_id(tag));
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);
7493 case GOT_OBJ_TYPE_TREE:
7494 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TYPE,
7495 GOT_OBJ_LABEL_TREE);
7497 case GOT_OBJ_TYPE_COMMIT:
7498 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TYPE,
7499 GOT_OBJ_LABEL_COMMIT);
7501 case GOT_OBJ_TYPE_TAG:
7502 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TYPE,
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);
7521 got_object_tag_close(tag);
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;
7537 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
7542 while ((ch = getopt(argc, argv, "c:r:P")) != -1) {
7545 commit_id_str = optarg;
7548 repo_path = realpath(optarg, NULL);
7549 if (repo_path == NULL)
7550 return got_error_from_errno2("realpath",
7552 got_path_strip_trailing_slashes(repo_path);
7566 cwd = getcwd(NULL, 0);
7568 error = got_error_from_errno("getcwd");
7571 error = got_worktree_open(&worktree, cwd);
7572 if (error && error->code != GOT_ERR_NOT_WORKTREE)
7575 if (repo_path == NULL) {
7577 got_worktree_get_repo_path(worktree));
7578 if (repo_path == NULL) {
7579 error = got_error_from_errno("strdup");
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);
7596 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
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);
7607 for (i = 0; i < argc; i++) {
7609 error = got_object_id_by_path(&id, repo, commit_id,
7614 error = got_repo_match_object_id(&id, &label, argv[i],
7615 GOT_OBJ_TYPE_ANY, 0, repo);
7617 if (error->code != GOT_ERR_BAD_OBJ_ID_STR &&
7618 error->code != GOT_ERR_NOT_REF)
7620 error = got_object_id_by_path(&id, repo,
7621 commit_id, argv[i]);
7627 error = got_object_get_type(&obj_type, repo, id);
7632 case GOT_OBJ_TYPE_BLOB:
7633 error = cat_blob(id, repo, stdout);
7635 case GOT_OBJ_TYPE_TREE:
7636 error = cat_tree(id, repo, stdout);
7638 case GOT_OBJ_TYPE_COMMIT:
7639 error = cat_commit(id, repo, stdout);
7641 case GOT_OBJ_TYPE_TAG:
7642 error = cat_tag(id, repo, stdout);
7645 error = got_error(GOT_ERR_OBJ_TYPE);
7660 got_worktree_close(worktree);
7662 const struct got_error *repo_error;
7663 repo_error = got_repo_close(repo);