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>
43 #include "got_version.h"
44 #include "got_error.h"
45 #include "got_object.h"
46 #include "got_reference.h"
47 #include "got_repository.h"
49 #include "got_cancel.h"
50 #include "got_worktree.h"
52 #include "got_commit_graph.h"
53 #include "got_fetch.h"
54 #include "got_blame.h"
55 #include "got_privsep.h"
56 #include "got_opentemp.h"
57 #include "got_gotconfig.h"
60 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
63 static volatile sig_atomic_t sigint_received;
64 static volatile sig_atomic_t sigpipe_received;
67 catch_sigint(int signo)
73 catch_sigpipe(int signo)
81 const struct got_error *(*cmd_main)(int, char *[]);
82 void (*cmd_usage)(void);
83 const char *cmd_alias;
86 __dead static void usage(int);
87 __dead static void usage_init(void);
88 __dead static void usage_import(void);
89 __dead static void usage_clone(void);
90 __dead static void usage_fetch(void);
91 __dead static void usage_checkout(void);
92 __dead static void usage_update(void);
93 __dead static void usage_log(void);
94 __dead static void usage_diff(void);
95 __dead static void usage_blame(void);
96 __dead static void usage_tree(void);
97 __dead static void usage_status(void);
98 __dead static void usage_ref(void);
99 __dead static void usage_branch(void);
100 __dead static void usage_tag(void);
101 __dead static void usage_add(void);
102 __dead static void usage_remove(void);
103 __dead static void usage_revert(void);
104 __dead static void usage_commit(void);
105 __dead static void usage_cherrypick(void);
106 __dead static void usage_backout(void);
107 __dead static void usage_rebase(void);
108 __dead static void usage_histedit(void);
109 __dead static void usage_integrate(void);
110 __dead static void usage_stage(void);
111 __dead static void usage_unstage(void);
112 __dead static void usage_cat(void);
113 __dead static void usage_info(void);
115 static const struct got_error* cmd_init(int, char *[]);
116 static const struct got_error* cmd_import(int, char *[]);
117 static const struct got_error* cmd_clone(int, char *[]);
118 static const struct got_error* cmd_fetch(int, char *[]);
119 static const struct got_error* cmd_checkout(int, char *[]);
120 static const struct got_error* cmd_update(int, char *[]);
121 static const struct got_error* cmd_log(int, char *[]);
122 static const struct got_error* cmd_diff(int, char *[]);
123 static const struct got_error* cmd_blame(int, char *[]);
124 static const struct got_error* cmd_tree(int, char *[]);
125 static const struct got_error* cmd_status(int, char *[]);
126 static const struct got_error* cmd_ref(int, char *[]);
127 static const struct got_error* cmd_branch(int, char *[]);
128 static const struct got_error* cmd_tag(int, char *[]);
129 static const struct got_error* cmd_add(int, char *[]);
130 static const struct got_error* cmd_remove(int, char *[]);
131 static const struct got_error* cmd_revert(int, char *[]);
132 static const struct got_error* cmd_commit(int, char *[]);
133 static const struct got_error* cmd_cherrypick(int, char *[]);
134 static const struct got_error* cmd_backout(int, char *[]);
135 static const struct got_error* cmd_rebase(int, char *[]);
136 static const struct got_error* cmd_histedit(int, char *[]);
137 static const struct got_error* cmd_integrate(int, char *[]);
138 static const struct got_error* cmd_stage(int, char *[]);
139 static const struct got_error* cmd_unstage(int, char *[]);
140 static const struct got_error* cmd_cat(int, char *[]);
141 static const struct got_error* cmd_info(int, char *[]);
143 static struct got_cmd got_commands[] = {
144 { "init", cmd_init, usage_init, "" },
145 { "import", cmd_import, usage_import, "im" },
146 { "clone", cmd_clone, usage_clone, "cl" },
147 { "fetch", cmd_fetch, usage_fetch, "fe" },
148 { "checkout", cmd_checkout, usage_checkout, "co" },
149 { "update", cmd_update, usage_update, "up" },
150 { "log", cmd_log, usage_log, "" },
151 { "diff", cmd_diff, usage_diff, "di" },
152 { "blame", cmd_blame, usage_blame, "bl" },
153 { "tree", cmd_tree, usage_tree, "tr" },
154 { "status", cmd_status, usage_status, "st" },
155 { "ref", cmd_ref, usage_ref, "" },
156 { "branch", cmd_branch, usage_branch, "br" },
157 { "tag", cmd_tag, usage_tag, "" },
158 { "add", cmd_add, usage_add, "" },
159 { "remove", cmd_remove, usage_remove, "rm" },
160 { "revert", cmd_revert, usage_revert, "rv" },
161 { "commit", cmd_commit, usage_commit, "ci" },
162 { "cherrypick", cmd_cherrypick, usage_cherrypick, "cy" },
163 { "backout", cmd_backout, usage_backout, "bo" },
164 { "rebase", cmd_rebase, usage_rebase, "rb" },
165 { "histedit", cmd_histedit, usage_histedit, "he" },
166 { "integrate", cmd_integrate, usage_integrate,"ig" },
167 { "stage", cmd_stage, usage_stage, "sg" },
168 { "unstage", cmd_unstage, usage_unstage, "ug" },
169 { "cat", cmd_cat, usage_cat, "" },
170 { "info", cmd_info, usage_info, "" },
178 fprintf(stderr, "commands:");
179 for (i = 0; i < nitems(got_commands); i++) {
180 struct got_cmd *cmd = &got_commands[i];
181 fprintf(stderr, " %s", cmd->cmd_name);
187 main(int argc, char *argv[])
192 int hflag = 0, Vflag = 0;
193 static struct option longopts[] = {
194 { "version", no_argument, NULL, 'V' },
198 setlocale(LC_CTYPE, "");
200 while ((ch = getopt_long(argc, argv, "+hV", longopts, NULL)) != -1) {
219 got_version_print_str();
226 signal(SIGINT, catch_sigint);
227 signal(SIGPIPE, catch_sigpipe);
229 for (i = 0; i < nitems(got_commands); i++) {
230 const struct got_error *error;
232 cmd = &got_commands[i];
234 if (strcmp(cmd->cmd_name, argv[0]) != 0 &&
235 strcmp(cmd->cmd_alias, argv[0]) != 0)
239 got_commands[i].cmd_usage();
241 error = got_commands[i].cmd_main(argc, argv);
242 if (error && error->code != GOT_ERR_CANCELLED &&
243 error->code != GOT_ERR_PRIVSEP_EXIT &&
244 !(sigpipe_received &&
245 error->code == GOT_ERR_ERRNO && errno == EPIPE) &&
247 error->code == GOT_ERR_ERRNO && errno == EINTR)) {
248 fprintf(stderr, "%s: %s\n", getprogname(), error->msg);
255 fprintf(stderr, "%s: unknown command '%s'\n", getprogname(), argv[0]);
263 fprintf(stderr, "usage: %s [-h] [-V | --version] command [arg ...]\n",
270 static const struct got_error *
271 get_editor(char **abspath)
273 const struct got_error *err = NULL;
278 editor = getenv("VISUAL");
280 editor = getenv("EDITOR");
283 err = got_path_find_prog(abspath, editor);
288 if (*abspath == NULL) {
289 *abspath = strdup("/bin/ed");
290 if (*abspath == NULL)
291 return got_error_from_errno("strdup");
297 static const struct got_error *
298 apply_unveil(const char *repo_path, int repo_read_only,
299 const char *worktree_path)
301 const struct got_error *err;
304 if (unveil("gmon.out", "rwc") != 0)
305 return got_error_from_errno2("unveil", "gmon.out");
307 if (repo_path && unveil(repo_path, repo_read_only ? "r" : "rwc") != 0)
308 return got_error_from_errno2("unveil", repo_path);
310 if (worktree_path && unveil(worktree_path, "rwc") != 0)
311 return got_error_from_errno2("unveil", worktree_path);
313 if (unveil(GOT_TMPDIR_STR, "rwc") != 0)
314 return got_error_from_errno2("unveil", GOT_TMPDIR_STR);
316 err = got_privsep_unveil_exec_helpers();
320 if (unveil(NULL, NULL) != 0)
321 return got_error_from_errno("unveil");
329 fprintf(stderr, "usage: %s init repository-path\n", getprogname());
333 static const struct got_error *
334 cmd_init(int argc, char *argv[])
336 const struct got_error *error = NULL;
337 char *repo_path = NULL;
340 while ((ch = getopt(argc, argv, "")) != -1) {
352 if (pledge("stdio rpath wpath cpath unveil", NULL) == -1)
358 repo_path = strdup(argv[0]);
359 if (repo_path == NULL)
360 return got_error_from_errno("strdup");
362 got_path_strip_trailing_slashes(repo_path);
364 error = got_path_mkdir(repo_path);
366 !(error->code == GOT_ERR_ERRNO && errno == EEXIST))
369 error = apply_unveil(repo_path, 0, NULL);
373 error = got_repo_init(repo_path);
382 fprintf(stderr, "usage: %s import [-b branch] [-m message] "
383 "[-r repository-path] [-I pattern] path\n", getprogname());
388 spawn_editor(const char *editor, const char *file)
391 sig_t sighup, sigint, sigquit;
394 sighup = signal(SIGHUP, SIG_IGN);
395 sigint = signal(SIGINT, SIG_IGN);
396 sigquit = signal(SIGQUIT, SIG_IGN);
398 switch (pid = fork()) {
402 execl(editor, editor, file, (char *)NULL);
406 while (waitpid(pid, &st, 0) == -1)
411 (void)signal(SIGHUP, sighup);
412 (void)signal(SIGINT, sigint);
413 (void)signal(SIGQUIT, sigquit);
415 if (!WIFEXITED(st)) {
420 return WEXITSTATUS(st);
423 static const struct got_error *
424 edit_logmsg(char **logmsg, const char *editor, const char *logmsg_path,
425 const char *initial_content)
427 const struct got_error *err = NULL;
431 int content_changed = 0;
436 if (stat(logmsg_path, &st) == -1)
437 return got_error_from_errno2("stat", logmsg_path);
439 if (spawn_editor(editor, logmsg_path) == -1)
440 return got_error_from_errno("failed spawning editor");
442 if (stat(logmsg_path, &st2) == -1)
443 return got_error_from_errno("stat");
445 if (st.st_mtime == st2.st_mtime && st.st_size == st2.st_size)
446 return got_error_msg(GOT_ERR_COMMIT_MSG_EMPTY,
447 "no changes made to commit message, aborting");
449 *logmsg = malloc(st2.st_size + 1);
451 return got_error_from_errno("malloc");
455 fp = fopen(logmsg_path, "r");
457 err = got_error_from_errno("fopen");
460 while (fgets(buf, sizeof(buf), fp) != NULL) {
461 if (!content_changed && strcmp(buf, initial_content) != 0)
463 if (buf[0] == '#' || (len == 0 && buf[0] == '\n'))
464 continue; /* remove comments and leading empty lines */
465 len = strlcat(*logmsg, buf, st2.st_size);
469 while (len > 0 && (*logmsg)[len - 1] == '\n') {
470 (*logmsg)[len - 1] = '\0';
474 if (len == 0 || !content_changed)
475 err = got_error_msg(GOT_ERR_COMMIT_MSG_EMPTY,
476 "commit message cannot be empty, aborting");
485 static const struct got_error *
486 collect_import_msg(char **logmsg, char **logmsg_path, const char *editor,
487 const char *path_dir, const char *branch_name)
489 char *initial_content = NULL;
490 const struct got_error *err = NULL;
491 int initial_content_len;
494 initial_content_len = asprintf(&initial_content,
495 "\n# %s to be imported to branch %s\n", path_dir,
497 if (initial_content_len == -1)
498 return got_error_from_errno("asprintf");
500 err = got_opentemp_named_fd(logmsg_path, &fd,
501 GOT_TMPDIR_STR "/got-importmsg");
505 write(fd, initial_content, initial_content_len);
508 err = edit_logmsg(logmsg, editor, *logmsg_path, initial_content);
510 free(initial_content);
514 static const struct got_error *
515 import_progress(void *arg, const char *path)
517 printf("A %s\n", path);
521 static const struct got_error *
522 get_author(char **author, struct got_repository *repo,
523 struct got_worktree *worktree)
525 const struct got_error *err = NULL;
526 const char *got_author = NULL, *name, *email;
527 const struct got_gotconfig *worktree_conf = NULL, *repo_conf = NULL;
532 worktree_conf = got_worktree_get_gotconfig(worktree);
533 repo_conf = got_repo_get_gotconfig(repo);
536 * Priority of potential author information sources, from most
537 * significant to least significant:
538 * 1) work tree's .got/got.conf file
539 * 2) repository's got.conf file
540 * 3) repository's git config file
541 * 4) environment variables
542 * 5) global git config files (in user's home directory or /etc)
546 got_author = got_gotconfig_get_author(worktree_conf);
547 if (got_author == NULL)
548 got_author = got_gotconfig_get_author(repo_conf);
549 if (got_author == NULL) {
550 name = got_repo_get_gitconfig_author_name(repo);
551 email = got_repo_get_gitconfig_author_email(repo);
553 if (asprintf(author, "%s <%s>", name, email) == -1)
554 return got_error_from_errno("asprintf");
558 got_author = getenv("GOT_AUTHOR");
559 if (got_author == NULL) {
560 name = got_repo_get_global_gitconfig_author_name(repo);
561 email = got_repo_get_global_gitconfig_author_email(
564 if (asprintf(author, "%s <%s>", name, email)
566 return got_error_from_errno("asprintf");
569 /* TODO: Look up user in password database? */
570 return got_error(GOT_ERR_COMMIT_NO_AUTHOR);
574 *author = strdup(got_author);
576 return got_error_from_errno("strdup");
579 * Really dumb email address check; we're only doing this to
580 * avoid git's object parser breaking on commits we create.
582 while (*got_author && *got_author != '<')
584 if (*got_author != '<') {
585 err = got_error(GOT_ERR_COMMIT_NO_EMAIL);
588 while (*got_author && *got_author != '@')
590 if (*got_author != '@') {
591 err = got_error(GOT_ERR_COMMIT_NO_EMAIL);
594 while (*got_author && *got_author != '>')
596 if (*got_author != '>')
597 err = got_error(GOT_ERR_COMMIT_NO_EMAIL);
606 static const struct got_error *
607 get_gitconfig_path(char **gitconfig_path)
609 const char *homedir = getenv("HOME");
611 *gitconfig_path = NULL;
613 if (asprintf(gitconfig_path, "%s/.gitconfig", homedir) == -1)
614 return got_error_from_errno("asprintf");
620 static const struct got_error *
621 cmd_import(int argc, char *argv[])
623 const struct got_error *error = NULL;
624 char *path_dir = NULL, *repo_path = NULL, *logmsg = NULL;
625 char *gitconfig_path = NULL, *editor = NULL, *author = NULL;
626 const char *branch_name = "main";
627 char *refname = NULL, *id_str = NULL, *logmsg_path = NULL;
628 struct got_repository *repo = NULL;
629 struct got_reference *branch_ref = NULL, *head_ref = NULL;
630 struct got_object_id *new_commit_id = NULL;
632 struct got_pathlist_head ignores;
633 struct got_pathlist_entry *pe;
634 int preserve_logmsg = 0;
636 TAILQ_INIT(&ignores);
638 while ((ch = getopt(argc, argv, "b:m:r:I:")) != -1) {
641 branch_name = optarg;
644 logmsg = strdup(optarg);
645 if (logmsg == NULL) {
646 error = got_error_from_errno("strdup");
651 repo_path = realpath(optarg, NULL);
652 if (repo_path == NULL) {
653 error = got_error_from_errno2("realpath",
659 if (optarg[0] == '\0')
661 error = got_pathlist_insert(&pe, &ignores, optarg,
676 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
684 if (repo_path == NULL) {
685 repo_path = getcwd(NULL, 0);
686 if (repo_path == NULL)
687 return got_error_from_errno("getcwd");
689 got_path_strip_trailing_slashes(repo_path);
690 error = get_gitconfig_path(&gitconfig_path);
693 error = got_repo_open(&repo, repo_path, gitconfig_path);
697 error = get_author(&author, repo, NULL);
702 * Don't let the user create a branch name with a leading '-'.
703 * While technically a valid reference name, this case is usually
704 * an unintended typo.
706 if (branch_name[0] == '-')
707 return got_error_path(branch_name, GOT_ERR_REF_NAME_MINUS);
709 if (asprintf(&refname, "refs/heads/%s", branch_name) == -1) {
710 error = got_error_from_errno("asprintf");
714 error = got_ref_open(&branch_ref, repo, refname, 0);
716 if (error->code != GOT_ERR_NOT_REF)
719 error = got_error_msg(GOT_ERR_BRANCH_EXISTS,
720 "import target branch already exists");
724 path_dir = realpath(argv[0], NULL);
725 if (path_dir == NULL) {
726 error = got_error_from_errno2("realpath", argv[0]);
729 got_path_strip_trailing_slashes(path_dir);
732 * unveil(2) traverses exec(2); if an editor is used we have
733 * to apply unveil after the log message has been written.
735 if (logmsg == NULL || strlen(logmsg) == 0) {
736 error = get_editor(&editor);
740 error = collect_import_msg(&logmsg, &logmsg_path, editor,
743 if (error->code != GOT_ERR_COMMIT_MSG_EMPTY &&
750 if (unveil(path_dir, "r") != 0) {
751 error = got_error_from_errno2("unveil", path_dir);
757 error = apply_unveil(got_repo_get_path(repo), 0, NULL);
764 error = got_repo_import(&new_commit_id, path_dir, logmsg,
765 author, &ignores, repo, import_progress, NULL);
772 error = got_ref_alloc(&branch_ref, refname, new_commit_id);
779 error = got_ref_write(branch_ref, repo);
786 error = got_object_id_str(&id_str, new_commit_id);
793 error = got_ref_open(&head_ref, repo, GOT_REF_HEAD, 0);
795 if (error->code != GOT_ERR_NOT_REF) {
801 error = got_ref_alloc_symref(&head_ref, GOT_REF_HEAD,
809 error = got_ref_write(head_ref, repo);
817 printf("Created branch %s with commit %s\n",
818 got_ref_get_name(branch_ref), id_str);
820 if (preserve_logmsg) {
821 fprintf(stderr, "%s: log message preserved in %s\n",
822 getprogname(), logmsg_path);
823 } else if (logmsg_path && unlink(logmsg_path) == -1 && error == NULL)
824 error = got_error_from_errno2("unlink", logmsg_path);
833 free(gitconfig_path);
835 got_ref_close(branch_ref);
837 got_ref_close(head_ref);
844 fprintf(stderr, "usage: %s clone [-a] [-b branch] [-l] [-m] [-q] [-v] "
845 "[-R reference] repository-url [directory]\n", getprogname());
849 struct got_fetch_progress_arg {
850 char last_scaled_size[FMT_SCALED_STRSIZE];
856 static const struct got_error *
857 fetch_progress(void *arg, const char *message, off_t packfile_size,
858 int nobj_total, int nobj_indexed, int nobj_loose, int nobj_resolved)
860 struct got_fetch_progress_arg *a = arg;
861 char scaled_size[FMT_SCALED_STRSIZE];
862 int p_indexed, p_resolved;
863 int print_size = 0, print_indexed = 0, print_resolved = 0;
865 if (a->verbosity < 0)
868 if (message && message[0] != '\0') {
869 printf("\rserver: %s", message);
874 if (packfile_size > 0 || nobj_indexed > 0) {
875 if (fmt_scaled(packfile_size, scaled_size) == 0 &&
876 (a->last_scaled_size[0] == '\0' ||
877 strcmp(scaled_size, a->last_scaled_size)) != 0) {
879 if (strlcpy(a->last_scaled_size, scaled_size,
880 FMT_SCALED_STRSIZE) >= FMT_SCALED_STRSIZE)
881 return got_error(GOT_ERR_NO_SPACE);
883 if (nobj_indexed > 0) {
884 p_indexed = (nobj_indexed * 100) / nobj_total;
885 if (p_indexed != a->last_p_indexed) {
886 a->last_p_indexed = p_indexed;
891 if (nobj_resolved > 0) {
892 p_resolved = (nobj_resolved * 100) /
893 (nobj_total - nobj_loose);
894 if (p_resolved != a->last_p_resolved) {
895 a->last_p_resolved = p_resolved;
903 if (print_size || print_indexed || print_resolved)
906 printf("%*s fetched", FMT_SCALED_STRSIZE, scaled_size);
908 printf("; indexing %d%%", p_indexed);
910 printf("; resolving deltas %d%%", p_resolved);
911 if (print_size || print_indexed || print_resolved)
917 static const struct got_error *
918 create_symref(const char *refname, struct got_reference *target_ref,
919 int verbosity, struct got_repository *repo)
921 const struct got_error *err;
922 struct got_reference *head_symref;
924 err = got_ref_alloc_symref(&head_symref, refname, target_ref);
928 err = got_ref_write(head_symref, repo);
929 got_ref_close(head_symref);
930 if (err == NULL && verbosity > 0) {
931 printf("Created reference %s: %s\n", GOT_REF_HEAD,
932 got_ref_get_name(target_ref));
937 static const struct got_error *
938 list_remote_refs(struct got_pathlist_head *symrefs,
939 struct got_pathlist_head *refs)
941 const struct got_error *err;
942 struct got_pathlist_entry *pe;
944 TAILQ_FOREACH(pe, symrefs, entry) {
945 const char *refname = pe->path;
946 const char *targetref = pe->data;
948 printf("%s: %s\n", refname, targetref);
951 TAILQ_FOREACH(pe, refs, entry) {
952 const char *refname = pe->path;
953 struct got_object_id *id = pe->data;
956 err = got_object_id_str(&id_str, id);
959 printf("%s: %s\n", refname, id_str);
966 static const struct got_error *
967 create_ref(const char *refname, struct got_object_id *id,
968 int verbosity, struct got_repository *repo)
970 const struct got_error *err = NULL;
971 struct got_reference *ref;
974 err = got_object_id_str(&id_str, id);
978 err = got_ref_alloc(&ref, refname, id);
982 err = got_ref_write(ref, repo);
985 if (err == NULL && verbosity >= 0)
986 printf("Created reference %s: %s\n", refname, id_str);
993 match_wanted_ref(const char *refname, const char *wanted_ref)
995 if (strncmp(refname, "refs/", 5) != 0)
1000 * Prevent fetching of references that won't make any
1001 * sense outside of the remote repository's context.
1003 if (strncmp(refname, "got/", 4) == 0)
1005 if (strncmp(refname, "remotes/", 8) == 0)
1008 if (strncmp(wanted_ref, "refs/", 5) == 0)
1011 /* Allow prefix match. */
1012 if (got_path_is_child(refname, wanted_ref, strlen(wanted_ref)))
1015 /* Allow exact match. */
1016 return (strcmp(refname, wanted_ref) == 0);
1020 is_wanted_ref(struct got_pathlist_head *wanted_refs, const char *refname)
1022 struct got_pathlist_entry *pe;
1024 TAILQ_FOREACH(pe, wanted_refs, entry) {
1025 if (match_wanted_ref(refname, pe->path))
1032 static const struct got_error *
1033 create_wanted_ref(const char *refname, struct got_object_id *id,
1034 const char *remote_repo_name, int verbosity, struct got_repository *repo)
1036 const struct got_error *err;
1037 char *remote_refname;
1039 if (strncmp("refs/", refname, 5) == 0)
1042 if (asprintf(&remote_refname, "refs/remotes/%s/%s",
1043 remote_repo_name, refname) == -1)
1044 return got_error_from_errno("asprintf");
1046 err = create_ref(remote_refname, id, verbosity, repo);
1047 free(remote_refname);
1051 static const struct got_error *
1052 cmd_clone(int argc, char *argv[])
1054 const struct got_error *error = NULL;
1055 const char *uri, *dirname;
1056 char *proto, *host, *port, *repo_name, *server_path;
1057 char *default_destdir = NULL, *id_str = NULL;
1058 const char *repo_path;
1059 struct got_repository *repo = NULL;
1060 struct got_pathlist_head refs, symrefs, wanted_branches, wanted_refs;
1061 struct got_pathlist_entry *pe;
1062 struct got_object_id *pack_hash = NULL;
1063 int ch, fetchfd = -1, fetchstatus;
1064 pid_t fetchpid = -1;
1065 struct got_fetch_progress_arg fpa;
1066 char *git_url = NULL;
1067 char *gitconfig_path = NULL, *gotconfig_path = NULL;
1068 char *gitconfig = NULL, *gotconfig = NULL;
1069 FILE *gitconfig_file = NULL, *gotconfig_file = NULL;
1071 int verbosity = 0, fetch_all_branches = 0, mirror_references = 0;
1072 int list_refs_only = 0;
1073 struct got_reference *head_symref = NULL;
1076 TAILQ_INIT(&symrefs);
1077 TAILQ_INIT(&wanted_branches);
1078 TAILQ_INIT(&wanted_refs);
1080 while ((ch = getopt(argc, argv, "ab:lmvqR:")) != -1) {
1083 fetch_all_branches = 1;
1086 error = got_pathlist_append(&wanted_branches,
1095 mirror_references = 1;
1100 else if (verbosity < 3)
1107 error = got_pathlist_append(&wanted_refs,
1120 if (fetch_all_branches && !TAILQ_EMPTY(&wanted_branches))
1121 errx(1, "-a and -b options are mutually exclusive");
1122 if (list_refs_only) {
1123 if (!TAILQ_EMPTY(&wanted_branches))
1124 errx(1, "-l and -b options are mutually exclusive");
1125 if (fetch_all_branches)
1126 errx(1, "-l and -a options are mutually exclusive");
1127 if (mirror_references)
1128 errx(1, "-l and -m options are mutually exclusive");
1129 if (verbosity == -1)
1130 errx(1, "-l and -q options are mutually exclusive");
1131 if (!TAILQ_EMPTY(&wanted_refs))
1132 errx(1, "-l and -R options are mutually exclusive");
1144 error = got_fetch_parse_uri(&proto, &host, &port, &server_path,
1149 if (asprintf(&git_url, "%s://%s%s%s%s%s", proto,
1150 host, port ? ":" : "", port ? port : "",
1151 server_path[0] != '/' ? "/" : "", server_path) == -1) {
1152 error = got_error_from_errno("asprintf");
1156 if (strcmp(proto, "git") == 0) {
1158 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
1159 "sendfd dns inet unveil", NULL) == -1)
1162 } else if (strcmp(proto, "git+ssh") == 0 ||
1163 strcmp(proto, "ssh") == 0) {
1165 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
1166 "sendfd unveil", NULL) == -1)
1169 } else if (strcmp(proto, "http") == 0 ||
1170 strcmp(proto, "git+http") == 0) {
1171 error = got_error_path(proto, GOT_ERR_NOT_IMPL);
1174 error = got_error_path(proto, GOT_ERR_BAD_PROTO);
1177 if (dirname == NULL) {
1178 if (asprintf(&default_destdir, "%s.git", repo_name) == -1) {
1179 error = got_error_from_errno("asprintf");
1182 repo_path = default_destdir;
1184 repo_path = dirname;
1186 if (!list_refs_only) {
1187 error = got_path_mkdir(repo_path);
1191 error = got_repo_init(repo_path);
1194 error = got_repo_open(&repo, repo_path, NULL);
1199 if (strcmp(proto, "git+ssh") == 0 || strcmp(proto, "ssh") == 0) {
1200 if (unveil(GOT_FETCH_PATH_SSH, "x") != 0) {
1201 error = got_error_from_errno2("unveil",
1202 GOT_FETCH_PATH_SSH);
1206 error = apply_unveil(repo ? got_repo_get_path(repo) : NULL, 0, NULL);
1211 printf("Connecting to %s%s%s\n", host,
1212 port ? ":" : "", port ? port : "");
1214 error = got_fetch_connect(&fetchpid, &fetchfd, proto, host, port,
1215 server_path, verbosity);
1219 fpa.last_scaled_size[0] = '\0';
1220 fpa.last_p_indexed = -1;
1221 fpa.last_p_resolved = -1;
1222 fpa.verbosity = verbosity;
1223 error = got_fetch_pack(&pack_hash, &refs, &symrefs,
1224 GOT_FETCH_DEFAULT_REMOTE_NAME, mirror_references,
1225 fetch_all_branches, &wanted_branches, &wanted_refs,
1226 list_refs_only, verbosity, fetchfd, repo,
1227 fetch_progress, &fpa);
1231 if (list_refs_only) {
1232 error = list_remote_refs(&symrefs, &refs);
1236 error = got_object_id_str(&id_str, pack_hash);
1240 printf("\nFetched %s.pack\n", id_str);
1243 /* Set up references provided with the pack file. */
1244 TAILQ_FOREACH(pe, &refs, entry) {
1245 const char *refname = pe->path;
1246 struct got_object_id *id = pe->data;
1247 char *remote_refname;
1249 if (is_wanted_ref(&wanted_refs, refname) &&
1250 !mirror_references) {
1251 error = create_wanted_ref(refname, id,
1252 GOT_FETCH_DEFAULT_REMOTE_NAME,
1253 verbosity - 1, repo);
1259 error = create_ref(refname, id, verbosity - 1, repo);
1263 if (mirror_references)
1266 if (strncmp("refs/heads/", refname, 11) != 0)
1269 if (asprintf(&remote_refname,
1270 "refs/remotes/%s/%s", GOT_FETCH_DEFAULT_REMOTE_NAME,
1271 refname + 11) == -1) {
1272 error = got_error_from_errno("asprintf");
1275 error = create_ref(remote_refname, id, verbosity - 1, repo);
1276 free(remote_refname);
1281 /* Set the HEAD reference if the server provided one. */
1282 TAILQ_FOREACH(pe, &symrefs, entry) {
1283 struct got_reference *target_ref;
1284 const char *refname = pe->path;
1285 const char *target = pe->data;
1286 char *remote_refname = NULL, *remote_target = NULL;
1288 if (strcmp(refname, GOT_REF_HEAD) != 0)
1291 error = got_ref_open(&target_ref, repo, target, 0);
1293 if (error->code == GOT_ERR_NOT_REF) {
1300 error = create_symref(refname, target_ref, verbosity, repo);
1301 got_ref_close(target_ref);
1305 if (mirror_references)
1308 if (strncmp("refs/heads/", target, 11) != 0)
1311 if (asprintf(&remote_refname,
1312 "refs/remotes/%s/%s", GOT_FETCH_DEFAULT_REMOTE_NAME,
1314 error = got_error_from_errno("asprintf");
1317 if (asprintf(&remote_target,
1318 "refs/remotes/%s/%s", GOT_FETCH_DEFAULT_REMOTE_NAME,
1319 target + 11) == -1) {
1320 error = got_error_from_errno("asprintf");
1321 free(remote_refname);
1324 error = got_ref_open(&target_ref, repo, remote_target, 0);
1326 free(remote_refname);
1327 free(remote_target);
1328 if (error->code == GOT_ERR_NOT_REF) {
1334 error = create_symref(remote_refname, target_ref,
1335 verbosity - 1, repo);
1336 free(remote_refname);
1337 free(remote_target);
1338 got_ref_close(target_ref);
1344 * We failed to set the HEAD reference. If we asked for
1345 * a set of wanted branches use the first of one of those
1346 * which could be fetched instead.
1348 TAILQ_FOREACH(pe, &wanted_branches, entry) {
1349 const char *target = pe->path;
1350 struct got_reference *target_ref;
1352 error = got_ref_open(&target_ref, repo, target, 0);
1354 if (error->code == GOT_ERR_NOT_REF) {
1361 error = create_symref(GOT_REF_HEAD, target_ref,
1363 got_ref_close(target_ref);
1370 /* Create got.conf(5). */
1371 gotconfig_path = got_repo_get_path_gotconfig(repo);
1372 if (gotconfig_path == NULL) {
1373 error = got_error_from_errno("got_repo_get_path_gotconfig");
1376 gotconfig_file = fopen(gotconfig_path, "a");
1377 if (gotconfig_file == NULL) {
1378 error = got_error_from_errno2("fopen", gotconfig_path);
1381 got_path_strip_trailing_slashes(server_path);
1382 if (asprintf(&gotconfig,
1387 "\trepository \"%s\"\n"
1390 GOT_FETCH_DEFAULT_REMOTE_NAME, host, proto,
1391 port ? "\tport " : "", port ? port : "", port ? "\n" : "",
1393 mirror_references ? "\tmirror-references yes\n" : "") == -1) {
1394 error = got_error_from_errno("asprintf");
1397 n = fwrite(gotconfig, 1, strlen(gotconfig), gotconfig_file);
1398 if (n != strlen(gotconfig)) {
1399 error = got_ferror(gotconfig_file, GOT_ERR_IO);
1403 /* Create a config file Git can understand. */
1404 gitconfig_path = got_repo_get_path_gitconfig(repo);
1405 if (gitconfig_path == NULL) {
1406 error = got_error_from_errno("got_repo_get_path_gitconfig");
1409 gitconfig_file = fopen(gitconfig_path, "a");
1410 if (gitconfig_file == NULL) {
1411 error = got_error_from_errno2("fopen", gitconfig_path);
1414 if (mirror_references) {
1415 if (asprintf(&gitconfig,
1418 "\tfetch = +refs/*:refs/*\n"
1419 "\tmirror = true\n",
1420 GOT_FETCH_DEFAULT_REMOTE_NAME, git_url) == -1) {
1421 error = got_error_from_errno("asprintf");
1424 } else if (fetch_all_branches) {
1425 if (asprintf(&gitconfig,
1428 "\tfetch = +refs/heads/*:refs/remotes/%s/*\n",
1429 GOT_FETCH_DEFAULT_REMOTE_NAME, git_url,
1430 GOT_FETCH_DEFAULT_REMOTE_NAME) == -1) {
1431 error = got_error_from_errno("asprintf");
1435 const char *branchname;
1438 * If the server specified a default branch, use just that one.
1439 * Otherwise fall back to fetching all branches on next fetch.
1442 branchname = got_ref_get_symref_target(head_symref);
1443 if (strncmp(branchname, "refs/heads/", 11) == 0)
1446 branchname = "*"; /* fall back to all branches */
1447 if (asprintf(&gitconfig,
1450 "\tfetch = +refs/heads/%s:refs/remotes/%s/%s\n",
1451 GOT_FETCH_DEFAULT_REMOTE_NAME, git_url,
1452 branchname, GOT_FETCH_DEFAULT_REMOTE_NAME,
1453 branchname) == -1) {
1454 error = got_error_from_errno("asprintf");
1458 n = fwrite(gitconfig, 1, strlen(gitconfig), gitconfig_file);
1459 if (n != strlen(gitconfig)) {
1460 error = got_ferror(gitconfig_file, GOT_ERR_IO);
1465 printf("Created %s repository '%s'\n",
1466 mirror_references ? "mirrored" : "cloned", repo_path);
1469 if (kill(fetchpid, SIGTERM) == -1)
1470 error = got_error_from_errno("kill");
1471 if (waitpid(fetchpid, &fetchstatus, 0) == -1 && error == NULL)
1472 error = got_error_from_errno("waitpid");
1474 if (fetchfd != -1 && close(fetchfd) == -1 && error == NULL)
1475 error = got_error_from_errno("close");
1476 if (gotconfig_file && fclose(gotconfig_file) == EOF && error == NULL)
1477 error = got_error_from_errno("fclose");
1478 if (gitconfig_file && fclose(gitconfig_file) == EOF && error == NULL)
1479 error = got_error_from_errno("fclose");
1481 got_repo_close(repo);
1483 got_ref_close(head_symref);
1484 TAILQ_FOREACH(pe, &refs, entry) {
1485 free((void *)pe->path);
1488 got_pathlist_free(&refs);
1489 TAILQ_FOREACH(pe, &symrefs, entry) {
1490 free((void *)pe->path);
1493 got_pathlist_free(&symrefs);
1494 got_pathlist_free(&wanted_branches);
1495 got_pathlist_free(&wanted_refs);
1502 free(default_destdir);
1505 free(gotconfig_path);
1506 free(gitconfig_path);
1511 static const struct got_error *
1512 update_ref(struct got_reference *ref, struct got_object_id *new_id,
1513 int replace_tags, int verbosity, struct got_repository *repo)
1515 const struct got_error *err = NULL;
1516 char *new_id_str = NULL;
1517 struct got_object_id *old_id = NULL;
1519 err = got_object_id_str(&new_id_str, new_id);
1523 if (!replace_tags &&
1524 strncmp(got_ref_get_name(ref), "refs/tags/", 10) == 0) {
1525 err = got_ref_resolve(&old_id, repo, ref);
1528 if (got_object_id_cmp(old_id, new_id) == 0)
1530 if (verbosity >= 0) {
1531 printf("Rejecting update of existing tag %s: %s\n",
1532 got_ref_get_name(ref), new_id_str);
1537 if (got_ref_is_symbolic(ref)) {
1538 if (verbosity >= 0) {
1539 printf("Replacing reference %s: %s\n",
1540 got_ref_get_name(ref),
1541 got_ref_get_symref_target(ref));
1543 err = got_ref_change_symref_to_ref(ref, new_id);
1546 err = got_ref_write(ref, repo);
1550 err = got_ref_resolve(&old_id, repo, ref);
1553 if (got_object_id_cmp(old_id, new_id) == 0)
1556 err = got_ref_change_ref(ref, new_id);
1559 err = got_ref_write(ref, repo);
1565 printf("Updated %s: %s\n", got_ref_get_name(ref),
1573 static const struct got_error *
1574 update_symref(const char *refname, struct got_reference *target_ref,
1575 int verbosity, struct got_repository *repo)
1577 const struct got_error *err = NULL, *unlock_err;
1578 struct got_reference *symref;
1579 int symref_is_locked = 0;
1581 err = got_ref_open(&symref, repo, refname, 1);
1583 if (err->code != GOT_ERR_NOT_REF)
1585 err = got_ref_alloc_symref(&symref, refname, target_ref);
1589 err = got_ref_write(symref, repo);
1594 printf("Created reference %s: %s\n",
1595 got_ref_get_name(symref),
1596 got_ref_get_symref_target(symref));
1598 symref_is_locked = 1;
1600 if (strcmp(got_ref_get_symref_target(symref),
1601 got_ref_get_name(target_ref)) == 0)
1604 err = got_ref_change_symref(symref,
1605 got_ref_get_name(target_ref));
1609 err = got_ref_write(symref, repo);
1614 printf("Updated %s: %s\n", got_ref_get_name(symref),
1615 got_ref_get_symref_target(symref));
1619 if (symref_is_locked) {
1620 unlock_err = got_ref_unlock(symref);
1621 if (unlock_err && err == NULL)
1624 got_ref_close(symref);
1631 fprintf(stderr, "usage: %s fetch [-a] [-b branch] [-d] [-l] "
1632 "[-r repository-path] [-t] [-q] [-v] [-R reference] "
1633 "[remote-repository-name]\n",
1638 static const struct got_error *
1639 delete_missing_ref(struct got_reference *ref,
1640 int verbosity, struct got_repository *repo)
1642 const struct got_error *err = NULL;
1643 struct got_object_id *id = NULL;
1644 char *id_str = NULL;
1646 if (got_ref_is_symbolic(ref)) {
1647 err = got_ref_delete(ref, repo);
1650 if (verbosity >= 0) {
1651 printf("Deleted reference %s: %s\n",
1652 got_ref_get_name(ref),
1653 got_ref_get_symref_target(ref));
1656 err = got_ref_resolve(&id, repo, ref);
1659 err = got_object_id_str(&id_str, id);
1663 err = got_ref_delete(ref, repo);
1666 if (verbosity >= 0) {
1667 printf("Deleted reference %s: %s\n",
1668 got_ref_get_name(ref), id_str);
1677 static const struct got_error *
1678 delete_missing_refs(struct got_pathlist_head *their_refs,
1679 struct got_pathlist_head *their_symrefs,
1680 const struct got_remote_repo *remote,
1681 int verbosity, struct got_repository *repo)
1683 const struct got_error *err = NULL, *unlock_err;
1684 struct got_reflist_head my_refs;
1685 struct got_reflist_entry *re;
1686 struct got_pathlist_entry *pe;
1687 char *remote_namespace = NULL;
1688 char *local_refname = NULL;
1690 SIMPLEQ_INIT(&my_refs);
1692 if (asprintf(&remote_namespace, "refs/remotes/%s/", remote->name)
1694 return got_error_from_errno("asprintf");
1696 err = got_ref_list(&my_refs, repo, NULL, got_ref_cmp_by_name, NULL);
1700 SIMPLEQ_FOREACH(re, &my_refs, entry) {
1701 const char *refname = got_ref_get_name(re->ref);
1703 if (!remote->mirror_references) {
1704 if (strncmp(refname, remote_namespace,
1705 strlen(remote_namespace)) == 0) {
1706 if (strcmp(refname + strlen(remote_namespace),
1709 if (asprintf(&local_refname, "refs/heads/%s",
1710 refname + strlen(remote_namespace)) == -1) {
1711 err = got_error_from_errno("asprintf");
1714 } else if (strncmp(refname, "refs/tags/", 10) != 0)
1718 TAILQ_FOREACH(pe, their_refs, entry) {
1719 if (strcmp(local_refname, pe->path) == 0)
1725 TAILQ_FOREACH(pe, their_symrefs, entry) {
1726 if (strcmp(local_refname, pe->path) == 0)
1732 err = delete_missing_ref(re->ref, verbosity, repo);
1736 if (local_refname) {
1737 struct got_reference *ref;
1738 err = got_ref_open(&ref, repo, local_refname, 1);
1740 if (err->code != GOT_ERR_NOT_REF)
1742 free(local_refname);
1743 local_refname = NULL;
1746 err = delete_missing_ref(ref, verbosity, repo);
1749 unlock_err = got_ref_unlock(ref);
1751 if (unlock_err && err == NULL) {
1756 free(local_refname);
1757 local_refname = NULL;
1761 free(remote_namespace);
1762 free(local_refname);
1766 static const struct got_error *
1767 update_wanted_ref(const char *refname, struct got_object_id *id,
1768 const char *remote_repo_name, int verbosity, struct got_repository *repo)
1770 const struct got_error *err, *unlock_err;
1771 char *remote_refname;
1772 struct got_reference *ref;
1774 if (strncmp("refs/", refname, 5) == 0)
1777 if (asprintf(&remote_refname, "refs/remotes/%s/%s",
1778 remote_repo_name, refname) == -1)
1779 return got_error_from_errno("asprintf");
1781 err = got_ref_open(&ref, repo, remote_refname, 1);
1783 if (err->code != GOT_ERR_NOT_REF)
1785 err = create_ref(remote_refname, id, verbosity, repo);
1787 err = update_ref(ref, id, 0, verbosity, repo);
1788 unlock_err = got_ref_unlock(ref);
1789 if (unlock_err && err == NULL)
1794 free(remote_refname);
1798 static const struct got_error *
1799 cmd_fetch(int argc, char *argv[])
1801 const struct got_error *error = NULL, *unlock_err;
1802 char *cwd = NULL, *repo_path = NULL;
1803 const char *remote_name;
1804 char *proto = NULL, *host = NULL, *port = NULL;
1805 char *repo_name = NULL, *server_path = NULL;
1806 const struct got_remote_repo *remotes, *remote = NULL;
1808 char *id_str = NULL;
1809 struct got_repository *repo = NULL;
1810 struct got_worktree *worktree = NULL;
1811 const struct got_gotconfig *repo_conf = NULL, *worktree_conf = NULL;
1812 struct got_pathlist_head refs, symrefs, wanted_branches, wanted_refs;
1813 struct got_pathlist_entry *pe;
1814 struct got_object_id *pack_hash = NULL;
1815 int i, ch, fetchfd = -1, fetchstatus;
1816 pid_t fetchpid = -1;
1817 struct got_fetch_progress_arg fpa;
1818 int verbosity = 0, fetch_all_branches = 0, list_refs_only = 0;
1819 int delete_refs = 0, replace_tags = 0;
1822 TAILQ_INIT(&symrefs);
1823 TAILQ_INIT(&wanted_branches);
1824 TAILQ_INIT(&wanted_refs);
1826 while ((ch = getopt(argc, argv, "ab:dlr:tvqR:")) != -1) {
1829 fetch_all_branches = 1;
1832 error = got_pathlist_append(&wanted_branches,
1844 repo_path = realpath(optarg, NULL);
1845 if (repo_path == NULL)
1846 return got_error_from_errno2("realpath",
1848 got_path_strip_trailing_slashes(repo_path);
1856 else if (verbosity < 3)
1863 error = got_pathlist_append(&wanted_refs,
1876 if (fetch_all_branches && !TAILQ_EMPTY(&wanted_branches))
1877 errx(1, "-a and -b options are mutually exclusive");
1878 if (list_refs_only) {
1879 if (!TAILQ_EMPTY(&wanted_branches))
1880 errx(1, "-l and -b options are mutually exclusive");
1881 if (fetch_all_branches)
1882 errx(1, "-l and -a options are mutually exclusive");
1884 errx(1, "-l and -d options are mutually exclusive");
1885 if (verbosity == -1)
1886 errx(1, "-l and -q options are mutually exclusive");
1890 remote_name = GOT_FETCH_DEFAULT_REMOTE_NAME;
1892 remote_name = argv[0];
1896 cwd = getcwd(NULL, 0);
1898 error = got_error_from_errno("getcwd");
1902 if (repo_path == NULL) {
1903 error = got_worktree_open(&worktree, cwd);
1904 if (error && error->code != GOT_ERR_NOT_WORKTREE)
1910 strdup(got_worktree_get_repo_path(worktree));
1911 if (repo_path == NULL)
1912 error = got_error_from_errno("strdup");
1916 repo_path = strdup(cwd);
1917 if (repo_path == NULL) {
1918 error = got_error_from_errno("strdup");
1924 error = got_repo_open(&repo, repo_path, NULL);
1929 worktree_conf = got_worktree_get_gotconfig(worktree);
1930 if (worktree_conf) {
1931 got_gotconfig_get_remotes(&nremotes, &remotes,
1933 for (i = 0; i < nremotes; i++) {
1934 remote = &remotes[i];
1935 if (strcmp(remote->name, remote_name) == 0)
1940 if (remote == NULL) {
1941 repo_conf = got_repo_get_gotconfig(repo);
1943 got_gotconfig_get_remotes(&nremotes, &remotes,
1945 for (i = 0; i < nremotes; i++) {
1946 remote = &remotes[i];
1947 if (strcmp(remote->name, remote_name) == 0)
1952 if (remote == NULL) {
1953 got_repo_get_gitconfig_remotes(&nremotes, &remotes, repo);
1954 for (i = 0; i < nremotes; i++) {
1955 remote = &remotes[i];
1956 if (strcmp(remote->name, remote_name) == 0)
1960 if (remote == NULL) {
1961 error = got_error_path(remote_name, GOT_ERR_NO_REMOTE);
1965 error = got_fetch_parse_uri(&proto, &host, &port, &server_path,
1966 &repo_name, remote->url);
1970 if (strcmp(proto, "git") == 0) {
1972 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
1973 "sendfd dns inet unveil", NULL) == -1)
1976 } else if (strcmp(proto, "git+ssh") == 0 ||
1977 strcmp(proto, "ssh") == 0) {
1979 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
1980 "sendfd unveil", NULL) == -1)
1983 } else if (strcmp(proto, "http") == 0 ||
1984 strcmp(proto, "git+http") == 0) {
1985 error = got_error_path(proto, GOT_ERR_NOT_IMPL);
1988 error = got_error_path(proto, GOT_ERR_BAD_PROTO);
1992 if (strcmp(proto, "git+ssh") == 0 || strcmp(proto, "ssh") == 0) {
1993 if (unveil(GOT_FETCH_PATH_SSH, "x") != 0) {
1994 error = got_error_from_errno2("unveil",
1995 GOT_FETCH_PATH_SSH);
1999 error = apply_unveil(got_repo_get_path(repo), 0, NULL);
2004 printf("Connecting to \"%s\" %s%s%s\n", remote->name, host,
2005 port ? ":" : "", port ? port : "");
2007 error = got_fetch_connect(&fetchpid, &fetchfd, proto, host, port,
2008 server_path, verbosity);
2012 fpa.last_scaled_size[0] = '\0';
2013 fpa.last_p_indexed = -1;
2014 fpa.last_p_resolved = -1;
2015 fpa.verbosity = verbosity;
2016 error = got_fetch_pack(&pack_hash, &refs, &symrefs, remote->name,
2017 remote->mirror_references, fetch_all_branches, &wanted_branches,
2018 &wanted_refs, list_refs_only, verbosity, fetchfd, repo,
2019 fetch_progress, &fpa);
2023 if (list_refs_only) {
2024 error = list_remote_refs(&symrefs, &refs);
2028 if (pack_hash == NULL) {
2030 printf("Already up-to-date\n");
2031 } else if (verbosity >= 0) {
2032 error = got_object_id_str(&id_str, pack_hash);
2035 printf("\nFetched %s.pack\n", id_str);
2040 /* Update references provided with the pack file. */
2041 TAILQ_FOREACH(pe, &refs, entry) {
2042 const char *refname = pe->path;
2043 struct got_object_id *id = pe->data;
2044 struct got_reference *ref;
2045 char *remote_refname;
2047 if (is_wanted_ref(&wanted_refs, refname) &&
2048 !remote->mirror_references) {
2049 error = update_wanted_ref(refname, id,
2050 remote->name, verbosity, repo);
2056 if (remote->mirror_references ||
2057 strncmp("refs/tags/", refname, 10) == 0) {
2058 error = got_ref_open(&ref, repo, refname, 1);
2060 if (error->code != GOT_ERR_NOT_REF)
2062 error = create_ref(refname, id, verbosity,
2067 error = update_ref(ref, id, replace_tags,
2069 unlock_err = got_ref_unlock(ref);
2070 if (unlock_err && error == NULL)
2076 } else if (strncmp("refs/heads/", refname, 11) == 0) {
2077 if (asprintf(&remote_refname, "refs/remotes/%s/%s",
2078 remote_name, refname + 11) == -1) {
2079 error = got_error_from_errno("asprintf");
2083 error = got_ref_open(&ref, repo, remote_refname, 1);
2085 if (error->code != GOT_ERR_NOT_REF)
2087 error = create_ref(remote_refname, id,
2092 error = update_ref(ref, id, replace_tags,
2094 unlock_err = got_ref_unlock(ref);
2095 if (unlock_err && error == NULL)
2102 /* Also create a local branch if none exists yet. */
2103 error = got_ref_open(&ref, repo, refname, 1);
2105 if (error->code != GOT_ERR_NOT_REF)
2107 error = create_ref(refname, id, verbosity,
2112 unlock_err = got_ref_unlock(ref);
2113 if (unlock_err && error == NULL)
2120 error = delete_missing_refs(&refs, &symrefs, remote,
2126 if (!remote->mirror_references) {
2127 /* Update remote HEAD reference if the server provided one. */
2128 TAILQ_FOREACH(pe, &symrefs, entry) {
2129 struct got_reference *target_ref;
2130 const char *refname = pe->path;
2131 const char *target = pe->data;
2132 char *remote_refname = NULL, *remote_target = NULL;
2134 if (strcmp(refname, GOT_REF_HEAD) != 0)
2137 if (strncmp("refs/heads/", target, 11) != 0)
2140 if (asprintf(&remote_refname, "refs/remotes/%s/%s",
2141 remote->name, refname) == -1) {
2142 error = got_error_from_errno("asprintf");
2145 if (asprintf(&remote_target, "refs/remotes/%s/%s",
2146 remote->name, target + 11) == -1) {
2147 error = got_error_from_errno("asprintf");
2148 free(remote_refname);
2152 error = got_ref_open(&target_ref, repo, remote_target,
2155 free(remote_refname);
2156 free(remote_target);
2157 if (error->code == GOT_ERR_NOT_REF) {
2163 error = update_symref(remote_refname, target_ref,
2165 free(remote_refname);
2166 free(remote_target);
2167 got_ref_close(target_ref);
2174 if (kill(fetchpid, SIGTERM) == -1)
2175 error = got_error_from_errno("kill");
2176 if (waitpid(fetchpid, &fetchstatus, 0) == -1 && error == NULL)
2177 error = got_error_from_errno("waitpid");
2179 if (fetchfd != -1 && close(fetchfd) == -1 && error == NULL)
2180 error = got_error_from_errno("close");
2182 got_repo_close(repo);
2184 got_worktree_close(worktree);
2185 TAILQ_FOREACH(pe, &refs, entry) {
2186 free((void *)pe->path);
2189 got_pathlist_free(&refs);
2190 TAILQ_FOREACH(pe, &symrefs, entry) {
2191 free((void *)pe->path);
2194 got_pathlist_free(&symrefs);
2195 got_pathlist_free(&wanted_branches);
2196 got_pathlist_free(&wanted_refs);
2211 usage_checkout(void)
2213 fprintf(stderr, "usage: %s checkout [-E] [-b branch] [-c commit] "
2214 "[-p prefix] repository-path [worktree-path]\n", getprogname());
2219 show_worktree_base_ref_warning(void)
2221 fprintf(stderr, "%s: warning: could not create a reference "
2222 "to the work tree's base commit; the commit could be "
2223 "garbage-collected by Git; making the repository "
2224 "writable and running 'got update' will prevent this\n",
2228 struct got_checkout_progress_arg {
2229 const char *worktree_path;
2230 int had_base_commit_ref_error;
2233 static const struct got_error *
2234 checkout_progress(void *arg, unsigned char status, const char *path)
2236 struct got_checkout_progress_arg *a = arg;
2238 /* Base commit bump happens silently. */
2239 if (status == GOT_STATUS_BUMP_BASE)
2242 if (status == GOT_STATUS_BASE_REF_ERR) {
2243 a->had_base_commit_ref_error = 1;
2247 while (path[0] == '/')
2250 printf("%c %s/%s\n", status, a->worktree_path, path);
2254 static const struct got_error *
2255 check_cancelled(void *arg)
2257 if (sigint_received || sigpipe_received)
2258 return got_error(GOT_ERR_CANCELLED);
2262 static const struct got_error *
2263 check_linear_ancestry(struct got_object_id *commit_id,
2264 struct got_object_id *base_commit_id, int allow_forwards_in_time_only,
2265 struct got_repository *repo)
2267 const struct got_error *err = NULL;
2268 struct got_object_id *yca_id;
2270 err = got_commit_graph_find_youngest_common_ancestor(&yca_id,
2271 commit_id, base_commit_id, repo, check_cancelled, NULL);
2276 return got_error(GOT_ERR_ANCESTRY);
2279 * Require a straight line of history between the target commit
2280 * and the work tree's base commit.
2282 * Non-linear situations such as this require a rebase:
2284 * (commit) D F (base_commit)
2292 * 'got update' only handles linear cases:
2293 * Update forwards in time: A (base/yca) - B - C - D (commit)
2294 * Update backwards in time: D (base) - C - B - A (commit/yca)
2296 if (allow_forwards_in_time_only) {
2297 if (got_object_id_cmp(base_commit_id, yca_id) != 0)
2298 return got_error(GOT_ERR_ANCESTRY);
2299 } else if (got_object_id_cmp(commit_id, yca_id) != 0 &&
2300 got_object_id_cmp(base_commit_id, yca_id) != 0)
2301 return got_error(GOT_ERR_ANCESTRY);
2307 static const struct got_error *
2308 check_same_branch(struct got_object_id *commit_id,
2309 struct got_reference *head_ref, struct got_object_id *yca_id,
2310 struct got_repository *repo)
2312 const struct got_error *err = NULL;
2313 struct got_commit_graph *graph = NULL;
2314 struct got_object_id *head_commit_id = NULL;
2315 int is_same_branch = 0;
2317 err = got_ref_resolve(&head_commit_id, repo, head_ref);
2321 if (got_object_id_cmp(head_commit_id, commit_id) == 0) {
2325 if (yca_id && got_object_id_cmp(commit_id, yca_id) == 0) {
2330 err = got_commit_graph_open(&graph, "/", 1);
2334 err = got_commit_graph_iter_start(graph, head_commit_id, repo,
2335 check_cancelled, NULL);
2340 struct got_object_id *id;
2341 err = got_commit_graph_iter_next(&id, graph, repo,
2342 check_cancelled, NULL);
2344 if (err->code == GOT_ERR_ITER_COMPLETED)
2350 if (yca_id && got_object_id_cmp(id, yca_id) == 0)
2352 if (got_object_id_cmp(id, commit_id) == 0) {
2360 got_commit_graph_close(graph);
2361 free(head_commit_id);
2362 if (!err && !is_same_branch)
2363 err = got_error(GOT_ERR_ANCESTRY);
2367 static const struct got_error *
2368 checkout_ancestry_error(struct got_reference *ref, const char *commit_id_str)
2370 static char msg[512];
2371 const char *branch_name;
2373 if (got_ref_is_symbolic(ref))
2374 branch_name = got_ref_get_symref_target(ref);
2376 branch_name = got_ref_get_name(ref);
2378 if (strncmp("refs/heads/", branch_name, 11) == 0)
2381 snprintf(msg, sizeof(msg),
2382 "target commit is not contained in branch '%s'; "
2383 "the branch to use must be specified with -b; "
2384 "if necessary a new branch can be created for "
2385 "this commit with 'got branch -c %s BRANCH_NAME'",
2386 branch_name, commit_id_str);
2388 return got_error_msg(GOT_ERR_ANCESTRY, msg);
2391 static const struct got_error *
2392 cmd_checkout(int argc, char *argv[])
2394 const struct got_error *error = NULL;
2395 struct got_repository *repo = NULL;
2396 struct got_reference *head_ref = NULL;
2397 struct got_worktree *worktree = NULL;
2398 char *repo_path = NULL;
2399 char *worktree_path = NULL;
2400 const char *path_prefix = "";
2401 const char *branch_name = GOT_REF_HEAD;
2402 char *commit_id_str = NULL;
2403 int ch, same_path_prefix, allow_nonempty = 0;
2404 struct got_pathlist_head paths;
2405 struct got_checkout_progress_arg cpa;
2409 while ((ch = getopt(argc, argv, "b:c:Ep:")) != -1) {
2412 branch_name = optarg;
2415 commit_id_str = strdup(optarg);
2416 if (commit_id_str == NULL)
2417 return got_error_from_errno("strdup");
2423 path_prefix = optarg;
2435 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
2436 "unveil", NULL) == -1)
2440 char *cwd, *base, *dotgit;
2441 repo_path = realpath(argv[0], NULL);
2442 if (repo_path == NULL)
2443 return got_error_from_errno2("realpath", argv[0]);
2444 cwd = getcwd(NULL, 0);
2446 error = got_error_from_errno("getcwd");
2449 if (path_prefix[0]) {
2450 base = basename(path_prefix);
2452 error = got_error_from_errno2("basename",
2457 base = basename(repo_path);
2459 error = got_error_from_errno2("basename",
2464 dotgit = strstr(base, ".git");
2467 if (asprintf(&worktree_path, "%s/%s", cwd, base) == -1) {
2468 error = got_error_from_errno("asprintf");
2473 } else if (argc == 2) {
2474 repo_path = realpath(argv[0], NULL);
2475 if (repo_path == NULL) {
2476 error = got_error_from_errno2("realpath", argv[0]);
2479 worktree_path = realpath(argv[1], NULL);
2480 if (worktree_path == NULL) {
2481 if (errno != ENOENT) {
2482 error = got_error_from_errno2("realpath",
2486 worktree_path = strdup(argv[1]);
2487 if (worktree_path == NULL) {
2488 error = got_error_from_errno("strdup");
2495 got_path_strip_trailing_slashes(repo_path);
2496 got_path_strip_trailing_slashes(worktree_path);
2498 error = got_repo_open(&repo, repo_path, NULL);
2502 /* Pre-create work tree path for unveil(2) */
2503 error = got_path_mkdir(worktree_path);
2505 if (!(error->code == GOT_ERR_ERRNO && errno == EISDIR) &&
2506 !(error->code == GOT_ERR_ERRNO && errno == EEXIST))
2508 if (!allow_nonempty &&
2509 !got_path_dir_is_empty(worktree_path)) {
2510 error = got_error_path(worktree_path,
2511 GOT_ERR_DIR_NOT_EMPTY);
2516 error = apply_unveil(got_repo_get_path(repo), 0, worktree_path);
2520 error = got_ref_open(&head_ref, repo, branch_name, 0);
2524 error = got_worktree_init(worktree_path, head_ref, path_prefix, repo);
2525 if (error != NULL && !(error->code == GOT_ERR_ERRNO && errno == EEXIST))
2528 error = got_worktree_open(&worktree, worktree_path);
2532 error = got_worktree_match_path_prefix(&same_path_prefix, worktree,
2536 if (!same_path_prefix) {
2537 error = got_error(GOT_ERR_PATH_PREFIX);
2541 if (commit_id_str) {
2542 struct got_object_id *commit_id;
2543 error = got_repo_match_object_id(&commit_id, NULL,
2544 commit_id_str, GOT_OBJ_TYPE_COMMIT, 1, repo);
2547 error = check_linear_ancestry(commit_id,
2548 got_worktree_get_base_commit_id(worktree), 0, repo);
2549 if (error != NULL) {
2551 if (error->code == GOT_ERR_ANCESTRY) {
2552 error = checkout_ancestry_error(
2553 head_ref, commit_id_str);
2557 error = check_same_branch(commit_id, head_ref, NULL, repo);
2559 if (error->code == GOT_ERR_ANCESTRY) {
2560 error = checkout_ancestry_error(
2561 head_ref, commit_id_str);
2565 error = got_worktree_set_base_commit_id(worktree, repo,
2572 error = got_pathlist_append(&paths, "", NULL);
2575 cpa.worktree_path = worktree_path;
2576 cpa.had_base_commit_ref_error = 0;
2577 error = got_worktree_checkout_files(worktree, &paths, repo,
2578 checkout_progress, &cpa, check_cancelled, NULL);
2582 printf("Now shut up and hack\n");
2583 if (cpa.had_base_commit_ref_error)
2584 show_worktree_base_ref_warning();
2586 got_pathlist_free(&paths);
2587 free(commit_id_str);
2589 free(worktree_path);
2593 struct got_update_progress_arg {
2601 print_update_progress_stats(struct got_update_progress_arg *upa)
2603 if (!upa->did_something)
2606 if (upa->conflicts > 0)
2607 printf("Files with new merge conflicts: %d\n", upa->conflicts);
2608 if (upa->obstructed > 0)
2609 printf("File paths obstructed by a non-regular file: %d\n",
2611 if (upa->not_updated > 0)
2612 printf("Files not updated because of existing merge "
2613 "conflicts: %d\n", upa->not_updated);
2619 fprintf(stderr, "usage: %s update [-b branch] [-c commit] [path ...]\n",
2624 static const struct got_error *
2625 update_progress(void *arg, unsigned char status, const char *path)
2627 struct got_update_progress_arg *upa = arg;
2629 if (status == GOT_STATUS_EXISTS ||
2630 status == GOT_STATUS_BASE_REF_ERR)
2633 upa->did_something = 1;
2635 /* Base commit bump happens silently. */
2636 if (status == GOT_STATUS_BUMP_BASE)
2639 if (status == GOT_STATUS_CONFLICT)
2641 if (status == GOT_STATUS_OBSTRUCTED)
2643 if (status == GOT_STATUS_CANNOT_UPDATE)
2646 while (path[0] == '/')
2648 printf("%c %s\n", status, path);
2652 static const struct got_error *
2653 switch_head_ref(struct got_reference *head_ref,
2654 struct got_object_id *commit_id, struct got_worktree *worktree,
2655 struct got_repository *repo)
2657 const struct got_error *err = NULL;
2659 int ref_has_moved = 0;
2661 /* Trivial case: switching between two different references. */
2662 if (strcmp(got_ref_get_name(head_ref),
2663 got_worktree_get_head_ref_name(worktree)) != 0) {
2664 printf("Switching work tree from %s to %s\n",
2665 got_worktree_get_head_ref_name(worktree),
2666 got_ref_get_name(head_ref));
2667 return got_worktree_set_head_ref(worktree, head_ref);
2670 err = check_linear_ancestry(commit_id,
2671 got_worktree_get_base_commit_id(worktree), 0, repo);
2673 if (err->code != GOT_ERR_ANCESTRY)
2680 /* Switching to a rebased branch with the same reference name. */
2681 err = got_object_id_str(&base_id_str,
2682 got_worktree_get_base_commit_id(worktree));
2685 printf("Reference %s now points at a different branch\n",
2686 got_worktree_get_head_ref_name(worktree));
2687 printf("Switching work tree from %s to %s\n", base_id_str,
2688 got_worktree_get_head_ref_name(worktree));
2692 static const struct got_error *
2693 check_rebase_or_histedit_in_progress(struct got_worktree *worktree)
2695 const struct got_error *err;
2698 err = got_worktree_rebase_in_progress(&in_progress, worktree);
2702 return got_error(GOT_ERR_REBASING);
2704 err = got_worktree_histedit_in_progress(&in_progress, worktree);
2708 return got_error(GOT_ERR_HISTEDIT_BUSY);
2713 static const struct got_error *
2714 get_worktree_paths_from_argv(struct got_pathlist_head *paths, int argc,
2715 char *argv[], struct got_worktree *worktree)
2717 const struct got_error *err = NULL;
2724 return got_error_from_errno("strdup");
2725 return got_pathlist_append(paths, path, NULL);
2728 for (i = 0; i < argc; i++) {
2729 err = got_worktree_resolve_path(&path, worktree, argv[i]);
2732 err = got_pathlist_append(paths, path, NULL);
2742 static const struct got_error *
2743 wrap_not_worktree_error(const struct got_error *orig_err,
2744 const char *cmdname, const char *path)
2746 const struct got_error *err;
2747 struct got_repository *repo;
2748 static char msg[512];
2750 err = got_repo_open(&repo, path, NULL);
2754 snprintf(msg, sizeof(msg),
2755 "'got %s' needs a work tree in addition to a git repository\n"
2756 "Work trees can be checked out from this Git repository with "
2758 "The got(1) manual page contains more information.", cmdname);
2759 err = got_error_msg(GOT_ERR_NOT_WORKTREE, msg);
2760 got_repo_close(repo);
2764 static const struct got_error *
2765 cmd_update(int argc, char *argv[])
2767 const struct got_error *error = NULL;
2768 struct got_repository *repo = NULL;
2769 struct got_worktree *worktree = NULL;
2770 char *worktree_path = NULL;
2771 struct got_object_id *commit_id = NULL;
2772 char *commit_id_str = NULL;
2773 const char *branch_name = NULL;
2774 struct got_reference *head_ref = NULL;
2775 struct got_pathlist_head paths;
2776 struct got_pathlist_entry *pe;
2778 struct got_update_progress_arg upa;
2782 while ((ch = getopt(argc, argv, "b:c:")) != -1) {
2785 branch_name = optarg;
2788 commit_id_str = strdup(optarg);
2789 if (commit_id_str == NULL)
2790 return got_error_from_errno("strdup");
2802 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
2803 "unveil", NULL) == -1)
2806 worktree_path = getcwd(NULL, 0);
2807 if (worktree_path == NULL) {
2808 error = got_error_from_errno("getcwd");
2811 error = got_worktree_open(&worktree, worktree_path);
2813 if (error->code == GOT_ERR_NOT_WORKTREE)
2814 error = wrap_not_worktree_error(error, "update",
2819 error = check_rebase_or_histedit_in_progress(worktree);
2823 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
2828 error = apply_unveil(got_repo_get_path(repo), 0,
2829 got_worktree_get_root_path(worktree));
2833 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
2837 error = got_ref_open(&head_ref, repo, branch_name ? branch_name :
2838 got_worktree_get_head_ref_name(worktree), 0);
2841 if (commit_id_str == NULL) {
2842 error = got_ref_resolve(&commit_id, repo, head_ref);
2845 error = got_object_id_str(&commit_id_str, commit_id);
2849 error = got_repo_match_object_id(&commit_id, NULL,
2850 commit_id_str, GOT_OBJ_TYPE_COMMIT, 1, repo);
2851 free(commit_id_str);
2852 commit_id_str = NULL;
2855 error = got_object_id_str(&commit_id_str, commit_id);
2861 struct got_object_id *head_commit_id;
2862 TAILQ_FOREACH(pe, &paths, entry) {
2863 if (pe->path_len == 0)
2865 error = got_error_msg(GOT_ERR_BAD_PATH,
2866 "switching between branches requires that "
2867 "the entire work tree gets updated");
2870 error = got_ref_resolve(&head_commit_id, repo, head_ref);
2873 error = check_linear_ancestry(commit_id, head_commit_id, 0,
2875 free(head_commit_id);
2878 error = check_same_branch(commit_id, head_ref, NULL, repo);
2881 error = switch_head_ref(head_ref, commit_id, worktree, repo);
2885 error = check_linear_ancestry(commit_id,
2886 got_worktree_get_base_commit_id(worktree), 0, repo);
2887 if (error != NULL) {
2888 if (error->code == GOT_ERR_ANCESTRY)
2889 error = got_error(GOT_ERR_BRANCH_MOVED);
2892 error = check_same_branch(commit_id, head_ref, NULL, repo);
2897 if (got_object_id_cmp(got_worktree_get_base_commit_id(worktree),
2899 error = got_worktree_set_base_commit_id(worktree, repo,
2905 memset(&upa, 0, sizeof(upa));
2906 error = got_worktree_checkout_files(worktree, &paths, repo,
2907 update_progress, &upa, check_cancelled, NULL);
2911 if (upa.did_something)
2912 printf("Updated to commit %s\n", commit_id_str);
2914 printf("Already up-to-date\n");
2915 print_update_progress_stats(&upa);
2917 free(worktree_path);
2918 TAILQ_FOREACH(pe, &paths, entry)
2919 free((char *)pe->path);
2920 got_pathlist_free(&paths);
2922 free(commit_id_str);
2926 static const struct got_error *
2927 diff_blobs(struct got_object_id *blob_id1, struct got_object_id *blob_id2,
2928 const char *path, int diff_context, int ignore_whitespace,
2929 struct got_repository *repo)
2931 const struct got_error *err = NULL;
2932 struct got_blob_object *blob1 = NULL, *blob2 = NULL;
2935 err = got_object_open_as_blob(&blob1, repo, blob_id1, 8192);
2940 err = got_object_open_as_blob(&blob2, repo, blob_id2, 8192);
2944 while (path[0] == '/')
2946 err = got_diff_blob(blob1, blob2, path, path, diff_context,
2947 ignore_whitespace, stdout);
2950 got_object_blob_close(blob1);
2951 got_object_blob_close(blob2);
2955 static const struct got_error *
2956 diff_trees(struct got_object_id *tree_id1, struct got_object_id *tree_id2,
2957 const char *path, int diff_context, int ignore_whitespace,
2958 struct got_repository *repo)
2960 const struct got_error *err = NULL;
2961 struct got_tree_object *tree1 = NULL, *tree2 = NULL;
2962 struct got_diff_blob_output_unidiff_arg arg;
2965 err = got_object_open_as_tree(&tree1, repo, tree_id1);
2970 err = got_object_open_as_tree(&tree2, repo, tree_id2);
2974 arg.diff_context = diff_context;
2975 arg.ignore_whitespace = ignore_whitespace;
2976 arg.outfile = stdout;
2977 while (path[0] == '/')
2979 err = got_diff_tree(tree1, tree2, path, path, repo,
2980 got_diff_blob_output_unidiff, &arg, 1);
2983 got_object_tree_close(tree1);
2985 got_object_tree_close(tree2);
2989 static const struct got_error *
2990 get_changed_paths(struct got_pathlist_head *paths,
2991 struct got_commit_object *commit, struct got_repository *repo)
2993 const struct got_error *err = NULL;
2994 struct got_object_id *tree_id1 = NULL, *tree_id2 = NULL;
2995 struct got_tree_object *tree1 = NULL, *tree2 = NULL;
2996 struct got_object_qid *qid;
2998 qid = SIMPLEQ_FIRST(got_object_commit_get_parent_ids(commit));
3000 struct got_commit_object *pcommit;
3001 err = got_object_open_as_commit(&pcommit, repo,
3006 tree_id1 = got_object_commit_get_tree_id(pcommit);
3007 got_object_commit_close(pcommit);
3012 err = got_object_open_as_tree(&tree1, repo, tree_id1);
3017 tree_id2 = got_object_commit_get_tree_id(commit);
3018 err = got_object_open_as_tree(&tree2, repo, tree_id2);
3022 err = got_diff_tree(tree1, tree2, "", "", repo,
3023 got_diff_tree_collect_changed_paths, paths, 0);
3026 got_object_tree_close(tree1);
3028 got_object_tree_close(tree2);
3032 static const struct got_error *
3033 print_patch(struct got_commit_object *commit, struct got_object_id *id,
3034 const char *path, int diff_context, struct got_repository *repo)
3036 const struct got_error *err = NULL;
3037 struct got_commit_object *pcommit = NULL;
3038 char *id_str1 = NULL, *id_str2 = NULL;
3039 struct got_object_id *obj_id1 = NULL, *obj_id2 = NULL;
3040 struct got_object_qid *qid;
3042 qid = SIMPLEQ_FIRST(got_object_commit_get_parent_ids(commit));
3044 err = got_object_open_as_commit(&pcommit, repo,
3050 if (path && path[0] != '\0') {
3052 err = got_object_id_by_path(&obj_id2, repo, id, path);
3055 err = got_object_id_str(&id_str2, obj_id2);
3061 err = got_object_id_by_path(&obj_id1, repo,
3064 if (err->code != GOT_ERR_NO_TREE_ENTRY) {
3069 err = got_object_id_str(&id_str1, obj_id1);
3076 err = got_object_get_type(&obj_type, repo, obj_id2);
3081 printf("diff %s %s\n", id_str1 ? id_str1 : "/dev/null", id_str2);
3083 case GOT_OBJ_TYPE_BLOB:
3084 err = diff_blobs(obj_id1, obj_id2, path, diff_context,
3087 case GOT_OBJ_TYPE_TREE:
3088 err = diff_trees(obj_id1, obj_id2, path, diff_context,
3092 err = got_error(GOT_ERR_OBJ_TYPE);
3098 obj_id2 = got_object_commit_get_tree_id(commit);
3099 err = got_object_id_str(&id_str2, obj_id2);
3102 obj_id1 = got_object_commit_get_tree_id(pcommit);
3103 err = got_object_id_str(&id_str1, obj_id1);
3106 printf("diff %s %s\n", id_str1 ? id_str1 : "/dev/null", id_str2);
3107 err = diff_trees(obj_id1, obj_id2, "", diff_context, 0, repo);
3113 got_object_commit_close(pcommit);
3118 get_datestr(time_t *time, char *datebuf)
3120 struct tm mytm, *tm;
3123 tm = gmtime_r(time, &mytm);
3126 s = asctime_r(tm, datebuf);
3129 p = strchr(s, '\n');
3135 static const struct got_error *
3136 match_logmsg(int *have_match, struct got_object_id *id,
3137 struct got_commit_object *commit, regex_t *regex)
3139 const struct got_error *err = NULL;
3140 regmatch_t regmatch;
3141 char *id_str = NULL, *logmsg = NULL;
3145 err = got_object_id_str(&id_str, id);
3149 err = got_object_commit_get_logmsg(&logmsg, commit);
3153 if (regexec(regex, logmsg, 1, ®match, 0) == 0)
3162 match_changed_paths(int *have_match, struct got_pathlist_head *changed_paths,
3165 regmatch_t regmatch;
3166 struct got_pathlist_entry *pe;
3170 TAILQ_FOREACH(pe, changed_paths, entry) {
3171 if (regexec(regex, pe->path, 1, ®match, 0) == 0) {
3178 #define GOT_COMMIT_SEP_STR "-----------------------------------------------\n"
3180 static const struct got_error *
3181 print_commit(struct got_commit_object *commit, struct got_object_id *id,
3182 struct got_repository *repo, const char *path,
3183 struct got_pathlist_head *changed_paths, int show_patch,
3184 int diff_context, struct got_reflist_head *refs)
3186 const struct got_error *err = NULL;
3187 char *id_str, *datestr, *logmsg0, *logmsg, *line;
3189 time_t committer_time;
3190 const char *author, *committer;
3191 char *refs_str = NULL;
3192 struct got_reflist_entry *re;
3194 SIMPLEQ_FOREACH(re, refs, entry) {
3197 struct got_tag_object *tag = NULL;
3200 name = got_ref_get_name(re->ref);
3201 if (strcmp(name, GOT_REF_HEAD) == 0)
3203 if (strncmp(name, "refs/", 5) == 0)
3205 if (strncmp(name, "got/", 4) == 0)
3207 if (strncmp(name, "heads/", 6) == 0)
3209 if (strncmp(name, "remotes/", 8) == 0) {
3211 s = strstr(name, "/" GOT_REF_HEAD);
3212 if (s != NULL && s[strlen(s)] == '\0')
3215 if (strncmp(name, "tags/", 5) == 0) {
3216 err = got_object_open_as_tag(&tag, repo, re->id);
3218 if (err->code != GOT_ERR_OBJ_TYPE)
3220 /* Ref points at something other than a tag. */
3225 cmp = got_object_id_cmp(tag ?
3226 got_object_tag_get_object_id(tag) : re->id, id);
3228 got_object_tag_close(tag);
3232 if (asprintf(&refs_str, "%s%s%s", s ? s : "", s ? ", " : "",
3234 err = got_error_from_errno("asprintf");
3240 err = got_object_id_str(&id_str, id);
3244 printf(GOT_COMMIT_SEP_STR);
3245 printf("commit %s%s%s%s\n", id_str, refs_str ? " (" : "",
3246 refs_str ? refs_str : "", refs_str ? ")" : "");
3251 printf("from: %s\n", got_object_commit_get_author(commit));
3252 committer_time = got_object_commit_get_committer_time(commit);
3253 datestr = get_datestr(&committer_time, datebuf);
3255 printf("date: %s UTC\n", datestr);
3256 author = got_object_commit_get_author(commit);
3257 committer = got_object_commit_get_committer(commit);
3258 if (strcmp(author, committer) != 0)
3259 printf("via: %s\n", committer);
3260 if (got_object_commit_get_nparents(commit) > 1) {
3261 const struct got_object_id_queue *parent_ids;
3262 struct got_object_qid *qid;
3264 parent_ids = got_object_commit_get_parent_ids(commit);
3265 SIMPLEQ_FOREACH(qid, parent_ids, entry) {
3266 err = got_object_id_str(&id_str, qid->id);
3269 printf("parent %d: %s\n", n++, id_str);
3274 err = got_object_commit_get_logmsg(&logmsg0, commit);
3280 line = strsep(&logmsg, "\n");
3282 printf(" %s\n", line);
3286 if (changed_paths) {
3287 struct got_pathlist_entry *pe;
3288 TAILQ_FOREACH(pe, changed_paths, entry) {
3289 struct got_diff_changed_path *cp = pe->data;
3290 printf(" %c %s\n", cp->status, pe->path);
3295 err = print_patch(commit, id, path, diff_context, repo);
3300 if (fflush(stdout) != 0 && err == NULL)
3301 err = got_error_from_errno("fflush");
3305 static const struct got_error *
3306 print_commits(struct got_object_id *root_id, struct got_object_id *end_id,
3307 struct got_repository *repo, const char *path, int show_changed_paths,
3308 int show_patch, const char *search_pattern, int diff_context, int limit,
3309 int log_branches, int reverse_display_order, struct got_reflist_head *refs)
3311 const struct got_error *err;
3312 struct got_commit_graph *graph;
3315 struct got_object_id_queue reversed_commits;
3316 struct got_object_qid *qid;
3317 struct got_commit_object *commit;
3318 struct got_pathlist_head changed_paths;
3319 struct got_pathlist_entry *pe;
3321 SIMPLEQ_INIT(&reversed_commits);
3322 TAILQ_INIT(&changed_paths);
3324 if (search_pattern && regcomp(®ex, search_pattern,
3325 REG_EXTENDED | REG_NOSUB | REG_NEWLINE))
3326 return got_error_msg(GOT_ERR_REGEX, search_pattern);
3328 err = got_commit_graph_open(&graph, path, !log_branches);
3331 err = got_commit_graph_iter_start(graph, root_id, repo,
3332 check_cancelled, NULL);
3336 struct got_object_id *id;
3338 if (sigint_received || sigpipe_received)
3341 err = got_commit_graph_iter_next(&id, graph, repo,
3342 check_cancelled, NULL);
3344 if (err->code == GOT_ERR_ITER_COMPLETED)
3351 err = got_object_open_as_commit(&commit, repo, id);
3355 if (show_changed_paths && !reverse_display_order) {
3356 err = get_changed_paths(&changed_paths, commit, repo);
3361 if (search_pattern) {
3362 err = match_logmsg(&have_match, id, commit, ®ex);
3364 got_object_commit_close(commit);
3367 if (have_match == 0 && show_changed_paths)
3368 match_changed_paths(&have_match,
3369 &changed_paths, ®ex);
3370 if (have_match == 0) {
3371 got_object_commit_close(commit);
3372 TAILQ_FOREACH(pe, &changed_paths, entry) {
3373 free((char *)pe->path);
3376 got_pathlist_free(&changed_paths);
3381 if (reverse_display_order) {
3382 err = got_object_qid_alloc(&qid, id);
3385 SIMPLEQ_INSERT_HEAD(&reversed_commits, qid, entry);
3386 got_object_commit_close(commit);
3388 err = print_commit(commit, id, repo, path,
3389 show_changed_paths ? &changed_paths : NULL,
3390 show_patch, diff_context, refs);
3391 got_object_commit_close(commit);
3395 if ((limit && --limit == 0) ||
3396 (end_id && got_object_id_cmp(id, end_id) == 0))
3399 TAILQ_FOREACH(pe, &changed_paths, entry) {
3400 free((char *)pe->path);
3403 got_pathlist_free(&changed_paths);
3405 if (reverse_display_order) {
3406 SIMPLEQ_FOREACH(qid, &reversed_commits, entry) {
3407 err = got_object_open_as_commit(&commit, repo, qid->id);
3410 if (show_changed_paths) {
3411 err = get_changed_paths(&changed_paths,
3416 err = print_commit(commit, qid->id, repo, path,
3417 show_changed_paths ? &changed_paths : NULL,
3418 show_patch, diff_context, refs);
3419 got_object_commit_close(commit);
3422 TAILQ_FOREACH(pe, &changed_paths, entry) {
3423 free((char *)pe->path);
3426 got_pathlist_free(&changed_paths);
3430 while (!SIMPLEQ_EMPTY(&reversed_commits)) {
3431 qid = SIMPLEQ_FIRST(&reversed_commits);
3432 SIMPLEQ_REMOVE_HEAD(&reversed_commits, entry);
3433 got_object_qid_free(qid);
3435 TAILQ_FOREACH(pe, &changed_paths, entry) {
3436 free((char *)pe->path);
3439 got_pathlist_free(&changed_paths);
3442 got_commit_graph_close(graph);
3449 fprintf(stderr, "usage: %s log [-b] [-c commit] [-C number] [ -l N ] "
3450 "[-p] [-P] [-x commit] [-s search-pattern] [-r repository-path] "
3451 "[-R] [path]\n", getprogname());
3456 get_default_log_limit(void)
3458 const char *got_default_log_limit;
3462 got_default_log_limit = getenv("GOT_LOG_DEFAULT_LIMIT");
3463 if (got_default_log_limit == NULL)
3465 n = strtonum(got_default_log_limit, 0, INT_MAX, &errstr);
3471 static const struct got_error *
3472 resolve_commit_arg(struct got_object_id **id, const char *commit_arg,
3473 struct got_repository *repo)
3475 const struct got_error *err = NULL;
3476 struct got_reference *ref;
3480 err = got_ref_open(&ref, repo, commit_arg, 0);
3483 err = got_ref_resolve(id, repo, ref);
3487 err = got_object_get_type(&obj_type, repo, *id);
3490 if (obj_type == GOT_OBJ_TYPE_TAG) {
3491 struct got_tag_object *tag;
3492 err = got_object_open_as_tag(&tag, repo, *id);
3495 if (got_object_tag_get_object_type(tag) !=
3496 GOT_OBJ_TYPE_COMMIT) {
3497 got_object_tag_close(tag);
3498 return got_error(GOT_ERR_OBJ_TYPE);
3501 *id = got_object_id_dup(
3502 got_object_tag_get_object_id(tag));
3504 err = got_error_from_errno(
3505 "got_object_id_dup");
3506 got_object_tag_close(tag);
3509 } else if (obj_type != GOT_OBJ_TYPE_COMMIT)
3510 return got_error(GOT_ERR_OBJ_TYPE);
3512 err = got_repo_match_object_id_prefix(id, commit_arg,
3513 GOT_OBJ_TYPE_COMMIT, repo);
3519 static const struct got_error *
3520 cmd_log(int argc, char *argv[])
3522 const struct got_error *error;
3523 struct got_repository *repo = NULL;
3524 struct got_worktree *worktree = NULL;
3525 struct got_object_id *start_id = NULL, *end_id = NULL;
3526 char *repo_path = NULL, *path = NULL, *cwd = NULL, *in_repo_path = NULL;
3527 const char *start_commit = NULL, *end_commit = NULL;
3528 const char *search_pattern = NULL;
3529 int diff_context = -1, ch;
3530 int show_changed_paths = 0, show_patch = 0, limit = 0, log_branches = 0;
3531 int reverse_display_order = 0;
3533 struct got_reflist_head refs;
3535 SIMPLEQ_INIT(&refs);
3538 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
3544 limit = get_default_log_limit();
3546 while ((ch = getopt(argc, argv, "bpPc:C:l:r:Rs:x:")) != -1) {
3552 show_changed_paths = 1;
3555 start_commit = optarg;
3558 diff_context = strtonum(optarg, 0, GOT_DIFF_MAX_CONTEXT,
3561 err(1, "-C option %s", errstr);
3564 limit = strtonum(optarg, 0, INT_MAX, &errstr);
3566 err(1, "-l option %s", errstr);
3572 repo_path = realpath(optarg, NULL);
3573 if (repo_path == NULL)
3574 return got_error_from_errno2("realpath",
3576 got_path_strip_trailing_slashes(repo_path);
3579 reverse_display_order = 1;
3582 search_pattern = optarg;
3585 end_commit = optarg;
3596 if (diff_context == -1)
3598 else if (!show_patch)
3599 errx(1, "-C reguires -p");
3601 cwd = getcwd(NULL, 0);
3603 error = got_error_from_errno("getcwd");
3607 if (repo_path == NULL) {
3608 error = got_worktree_open(&worktree, cwd);
3609 if (error && error->code != GOT_ERR_NOT_WORKTREE)
3617 error = got_error_from_errno("strdup");
3620 } else if (argc == 1) {
3622 error = got_worktree_resolve_path(&path, worktree,
3627 path = strdup(argv[0]);
3629 error = got_error_from_errno("strdup");
3636 if (repo_path == NULL) {
3637 repo_path = worktree ?
3638 strdup(got_worktree_get_repo_path(worktree)) : strdup(cwd);
3640 if (repo_path == NULL) {
3641 error = got_error_from_errno("strdup");
3645 error = got_repo_open(&repo, repo_path, NULL);
3649 error = apply_unveil(got_repo_get_path(repo), 1,
3650 worktree ? got_worktree_get_root_path(worktree) : NULL);
3654 if (start_commit == NULL) {
3655 struct got_reference *head_ref;
3656 struct got_commit_object *commit = NULL;
3657 error = got_ref_open(&head_ref, repo,
3658 worktree ? got_worktree_get_head_ref_name(worktree)
3662 error = got_ref_resolve(&start_id, repo, head_ref);
3663 got_ref_close(head_ref);
3666 error = got_object_open_as_commit(&commit, repo,
3670 got_object_commit_close(commit);
3672 error = resolve_commit_arg(&start_id, start_commit, repo);
3676 if (end_commit != NULL) {
3677 error = resolve_commit_arg(&end_id, end_commit, repo);
3683 const char *prefix = got_worktree_get_path_prefix(worktree);
3685 if (asprintf(&p, "%s%s%s", prefix,
3686 (strcmp(prefix, "/") != 0) ? "/" : "", path) == -1) {
3687 error = got_error_from_errno("asprintf");
3690 error = got_repo_map_path(&in_repo_path, repo, p, 0);
3693 error = got_repo_map_path(&in_repo_path, repo, path, 1);
3698 path = in_repo_path;
3701 error = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name, NULL);
3705 error = print_commits(start_id, end_id, repo, path, show_changed_paths,
3706 show_patch, search_pattern, diff_context, limit, log_branches,
3707 reverse_display_order, &refs);
3713 got_worktree_close(worktree);
3715 const struct got_error *repo_error;
3716 repo_error = got_repo_close(repo);
3720 got_ref_list_free(&refs);
3727 fprintf(stderr, "usage: %s diff [-C number] [-r repository-path] [-s] "
3728 "[-w] [object1 object2 | path]\n", getprogname());
3732 struct print_diff_arg {
3733 struct got_repository *repo;
3734 struct got_worktree *worktree;
3739 int ignore_whitespace;
3743 * Create a file which contains the target path of a symlink so we can feed
3744 * it as content to the diff engine.
3746 static const struct got_error *
3747 get_symlink_target_file(int *fd, int dirfd, const char *de_name,
3748 const char *abspath)
3750 const struct got_error *err = NULL;
3751 char target_path[PATH_MAX];
3752 ssize_t target_len, outlen;
3757 target_len = readlinkat(dirfd, de_name, target_path, PATH_MAX);
3758 if (target_len == -1)
3759 return got_error_from_errno2("readlinkat", abspath);
3761 target_len = readlink(abspath, target_path, PATH_MAX);
3762 if (target_len == -1)
3763 return got_error_from_errno2("readlink", abspath);
3766 *fd = got_opentempfd();
3768 return got_error_from_errno("got_opentempfd");
3770 outlen = write(*fd, target_path, target_len);
3772 err = got_error_from_errno("got_opentempfd");
3776 if (lseek(*fd, 0, SEEK_SET) == -1) {
3777 err = got_error_from_errno2("lseek", abspath);
3788 static const struct got_error *
3789 print_diff(void *arg, unsigned char status, unsigned char staged_status,
3790 const char *path, struct got_object_id *blob_id,
3791 struct got_object_id *staged_blob_id, struct got_object_id *commit_id,
3792 int dirfd, const char *de_name)
3794 struct print_diff_arg *a = arg;
3795 const struct got_error *err = NULL;
3796 struct got_blob_object *blob1 = NULL;
3799 char *abspath = NULL, *label1 = NULL;
3802 if (a->diff_staged) {
3803 if (staged_status != GOT_STATUS_MODIFY &&
3804 staged_status != GOT_STATUS_ADD &&
3805 staged_status != GOT_STATUS_DELETE)
3808 if (staged_status == GOT_STATUS_DELETE)
3810 if (status == GOT_STATUS_NONEXISTENT)
3811 return got_error_set_errno(ENOENT, path);
3812 if (status != GOT_STATUS_MODIFY &&
3813 status != GOT_STATUS_ADD &&
3814 status != GOT_STATUS_DELETE &&
3815 status != GOT_STATUS_CONFLICT)
3819 if (!a->header_shown) {
3820 printf("diff %s %s%s\n", a->id_str,
3821 got_worktree_get_root_path(a->worktree),
3822 a->diff_staged ? " (staged changes)" : "");
3823 a->header_shown = 1;
3826 if (a->diff_staged) {
3827 const char *label1 = NULL, *label2 = NULL;
3828 switch (staged_status) {
3829 case GOT_STATUS_MODIFY:
3833 case GOT_STATUS_ADD:
3836 case GOT_STATUS_DELETE:
3840 return got_error(GOT_ERR_FILE_STATUS);
3842 return got_diff_objects_as_blobs(blob_id, staged_blob_id,
3843 label1, label2, a->diff_context, a->ignore_whitespace,
3847 if (staged_status == GOT_STATUS_ADD ||
3848 staged_status == GOT_STATUS_MODIFY) {
3850 err = got_object_open_as_blob(&blob1, a->repo, staged_blob_id,
3854 err = got_object_id_str(&id_str, staged_blob_id);
3857 if (asprintf(&label1, "%s (staged)", id_str) == -1) {
3858 err = got_error_from_errno("asprintf");
3863 } else if (status != GOT_STATUS_ADD) {
3864 err = got_object_open_as_blob(&blob1, a->repo, blob_id, 8192);
3869 if (status != GOT_STATUS_DELETE) {
3870 if (asprintf(&abspath, "%s/%s",
3871 got_worktree_get_root_path(a->worktree), path) == -1) {
3872 err = got_error_from_errno("asprintf");
3877 fd = openat(dirfd, de_name, O_RDONLY | O_NOFOLLOW);
3879 if (errno != ELOOP) {
3880 err = got_error_from_errno2("openat",
3884 err = get_symlink_target_file(&fd, dirfd,
3890 fd = open(abspath, O_RDONLY | O_NOFOLLOW);
3892 if (errno != ELOOP) {
3893 err = got_error_from_errno2("open",
3897 err = get_symlink_target_file(&fd, dirfd,
3903 if (fstat(fd, &sb) == -1) {
3904 err = got_error_from_errno2("fstat", abspath);
3907 f2 = fdopen(fd, "r");
3909 err = got_error_from_errno2("fdopen", abspath);
3916 err = got_diff_blob_file(blob1, label1, f2, sb.st_size, path,
3917 a->diff_context, a->ignore_whitespace, stdout);
3920 got_object_blob_close(blob1);
3921 if (f2 && fclose(f2) == EOF && err == NULL)
3922 err = got_error_from_errno("fclose");
3923 if (fd != -1 && close(fd) == -1 && err == NULL)
3924 err = got_error_from_errno("close");
3929 static const struct got_error *
3930 cmd_diff(int argc, char *argv[])
3932 const struct got_error *error;
3933 struct got_repository *repo = NULL;
3934 struct got_worktree *worktree = NULL;
3935 char *cwd = NULL, *repo_path = NULL;
3936 struct got_object_id *id1 = NULL, *id2 = NULL;
3937 const char *id_str1 = NULL, *id_str2 = NULL;
3938 char *label1 = NULL, *label2 = NULL;
3940 int diff_context = 3, diff_staged = 0, ignore_whitespace = 0, ch;
3945 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
3950 while ((ch = getopt(argc, argv, "C:r:sw")) != -1) {
3953 diff_context = strtonum(optarg, 0, GOT_DIFF_MAX_CONTEXT,
3956 err(1, "-C option %s", errstr);
3959 repo_path = realpath(optarg, NULL);
3960 if (repo_path == NULL)
3961 return got_error_from_errno2("realpath",
3963 got_path_strip_trailing_slashes(repo_path);
3969 ignore_whitespace = 1;
3980 cwd = getcwd(NULL, 0);
3982 error = got_error_from_errno("getcwd");
3988 "-r option can't be used when diffing a work tree");
3989 error = got_worktree_open(&worktree, cwd);
3991 if (error->code == GOT_ERR_NOT_WORKTREE)
3992 error = wrap_not_worktree_error(error, "diff",
3996 repo_path = strdup(got_worktree_get_repo_path(worktree));
3997 if (repo_path == NULL) {
3998 error = got_error_from_errno("strdup");
4002 error = got_worktree_resolve_path(&path, worktree,
4009 error = got_error_from_errno("strdup");
4013 } else if (argc == 2) {
4015 errx(1, "-s option can't be used when diffing "
4016 "objects in repository");
4019 if (repo_path == NULL) {
4020 error = got_worktree_open(&worktree, cwd);
4021 if (error && error->code != GOT_ERR_NOT_WORKTREE)
4025 got_worktree_get_repo_path(worktree));
4026 if (repo_path == NULL) {
4027 error = got_error_from_errno("strdup");
4031 repo_path = strdup(cwd);
4032 if (repo_path == NULL) {
4033 error = got_error_from_errno("strdup");
4041 error = got_repo_open(&repo, repo_path, NULL);
4046 error = apply_unveil(got_repo_get_path(repo), 1,
4047 worktree ? got_worktree_get_root_path(worktree) : NULL);
4052 struct print_diff_arg arg;
4053 struct got_pathlist_head paths;
4058 error = got_object_id_str(&id_str,
4059 got_worktree_get_base_commit_id(worktree));
4063 arg.worktree = worktree;
4064 arg.diff_context = diff_context;
4065 arg.id_str = id_str;
4066 arg.header_shown = 0;
4067 arg.diff_staged = diff_staged;
4068 arg.ignore_whitespace = ignore_whitespace;
4070 error = got_pathlist_append(&paths, path, NULL);
4074 error = got_worktree_status(worktree, &paths, repo, print_diff,
4075 &arg, check_cancelled, NULL);
4077 got_pathlist_free(&paths);
4081 error = got_repo_match_object_id(&id1, &label1, id_str1,
4082 GOT_OBJ_TYPE_ANY, 1, repo);
4086 error = got_repo_match_object_id(&id2, &label2, id_str2,
4087 GOT_OBJ_TYPE_ANY, 1, repo);
4091 error = got_object_get_type(&type1, repo, id1);
4095 error = got_object_get_type(&type2, repo, id2);
4099 if (type1 != type2) {
4100 error = got_error(GOT_ERR_OBJ_TYPE);
4105 case GOT_OBJ_TYPE_BLOB:
4106 error = got_diff_objects_as_blobs(id1, id2, NULL, NULL,
4107 diff_context, ignore_whitespace, repo, stdout);
4109 case GOT_OBJ_TYPE_TREE:
4110 error = got_diff_objects_as_trees(id1, id2, "", "",
4111 diff_context, ignore_whitespace, repo, stdout);
4113 case GOT_OBJ_TYPE_COMMIT:
4114 printf("diff %s %s\n", label1, label2);
4115 error = got_diff_objects_as_commits(id1, id2, diff_context,
4116 ignore_whitespace, repo, stdout);
4119 error = got_error(GOT_ERR_OBJ_TYPE);
4128 got_worktree_close(worktree);
4130 const struct got_error *repo_error;
4131 repo_error = got_repo_close(repo);
4142 "usage: %s blame [-c commit] [-r repository-path] path\n",
4151 char datebuf[11]; /* YYYY-MM-DD + NUL */
4154 struct blame_cb_args {
4155 struct blame_line *lines;
4159 off_t *line_offsets;
4161 struct got_repository *repo;
4164 static const struct got_error *
4165 blame_cb(void *arg, int nlines, int lineno, struct got_object_id *id)
4167 const struct got_error *err = NULL;
4168 struct blame_cb_args *a = arg;
4169 struct blame_line *bline;
4171 size_t linesize = 0;
4172 struct got_commit_object *commit = NULL;
4175 time_t committer_time;
4177 if (nlines != a->nlines ||
4178 (lineno != -1 && lineno < 1) || lineno > a->nlines)
4179 return got_error(GOT_ERR_RANGE);
4181 if (sigint_received)
4182 return got_error(GOT_ERR_ITER_COMPLETED);
4185 return NULL; /* no change in this commit */
4187 /* Annotate this line. */
4188 bline = &a->lines[lineno - 1];
4189 if (bline->annotated)
4191 err = got_object_id_str(&bline->id_str, id);
4195 err = got_object_open_as_commit(&commit, a->repo, id);
4199 bline->committer = strdup(got_object_commit_get_committer(commit));
4200 if (bline->committer == NULL) {
4201 err = got_error_from_errno("strdup");
4205 committer_time = got_object_commit_get_committer_time(commit);
4206 if (localtime_r(&committer_time, &tm) == NULL)
4207 return got_error_from_errno("localtime_r");
4208 if (strftime(bline->datebuf, sizeof(bline->datebuf), "%G-%m-%d",
4209 &tm) >= sizeof(bline->datebuf)) {
4210 err = got_error(GOT_ERR_NO_SPACE);
4213 bline->annotated = 1;
4215 /* Print lines annotated so far. */
4216 bline = &a->lines[a->lineno_cur - 1];
4217 if (!bline->annotated)
4220 offset = a->line_offsets[a->lineno_cur - 1];
4221 if (fseeko(a->f, offset, SEEK_SET) == -1) {
4222 err = got_error_from_errno("fseeko");
4226 while (bline->annotated) {
4227 char *smallerthan, *at, *nl, *committer;
4230 if (getline(&line, &linesize, a->f) == -1) {
4232 err = got_error_from_errno("getline");
4236 committer = bline->committer;
4237 smallerthan = strchr(committer, '<');
4238 if (smallerthan && smallerthan[1] != '\0')
4239 committer = smallerthan + 1;
4240 at = strchr(committer, '@');
4243 len = strlen(committer);
4245 committer[8] = '\0';
4247 nl = strchr(line, '\n');
4250 printf("%.*d) %.8s %s %-8s %s\n", a->nlines_prec, a->lineno_cur,
4251 bline->id_str, bline->datebuf, committer, line);
4254 bline = &a->lines[a->lineno_cur - 1];
4258 got_object_commit_close(commit);
4263 static const struct got_error *
4264 cmd_blame(int argc, char *argv[])
4266 const struct got_error *error;
4267 struct got_repository *repo = NULL;
4268 struct got_worktree *worktree = NULL;
4269 char *path, *cwd = NULL, *repo_path = NULL, *in_repo_path = NULL;
4270 char *link_target = NULL;
4271 struct got_object_id *obj_id = NULL;
4272 struct got_object_id *commit_id = NULL;
4273 struct got_blob_object *blob = NULL;
4274 char *commit_id_str = NULL;
4275 struct blame_cb_args bca;
4276 int ch, obj_type, i;
4279 memset(&bca, 0, sizeof(bca));
4282 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
4287 while ((ch = getopt(argc, argv, "c:r:")) != -1) {
4290 commit_id_str = optarg;
4293 repo_path = realpath(optarg, NULL);
4294 if (repo_path == NULL)
4295 return got_error_from_errno2("realpath",
4297 got_path_strip_trailing_slashes(repo_path);
4313 cwd = getcwd(NULL, 0);
4315 error = got_error_from_errno("getcwd");
4318 if (repo_path == NULL) {
4319 error = got_worktree_open(&worktree, cwd);
4320 if (error && error->code != GOT_ERR_NOT_WORKTREE)
4326 strdup(got_worktree_get_repo_path(worktree));
4327 if (repo_path == NULL) {
4328 error = got_error_from_errno("strdup");
4333 repo_path = strdup(cwd);
4334 if (repo_path == NULL) {
4335 error = got_error_from_errno("strdup");
4341 error = got_repo_open(&repo, repo_path, NULL);
4345 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
4350 const char *prefix = got_worktree_get_path_prefix(worktree);
4351 char *p, *worktree_subdir = cwd +
4352 strlen(got_worktree_get_root_path(worktree));
4353 if (asprintf(&p, "%s%s%s%s%s",
4354 prefix, (strcmp(prefix, "/") != 0) ? "/" : "",
4355 worktree_subdir, worktree_subdir[0] ? "/" : "",
4357 error = got_error_from_errno("asprintf");
4360 error = got_repo_map_path(&in_repo_path, repo, p, 0);
4363 error = got_repo_map_path(&in_repo_path, repo, path, 1);
4368 if (commit_id_str == NULL) {
4369 struct got_reference *head_ref;
4370 error = got_ref_open(&head_ref, repo, worktree ?
4371 got_worktree_get_head_ref_name(worktree) : GOT_REF_HEAD, 0);
4374 error = got_ref_resolve(&commit_id, repo, head_ref);
4375 got_ref_close(head_ref);
4379 error = got_repo_match_object_id(&commit_id, NULL,
4380 commit_id_str, GOT_OBJ_TYPE_COMMIT, 1, repo);
4385 error = got_object_resolve_symlinks(&link_target, in_repo_path,
4390 error = got_object_id_by_path(&obj_id, repo, commit_id,
4391 link_target ? link_target : in_repo_path);
4395 error = got_object_get_type(&obj_type, repo, obj_id);
4399 if (obj_type != GOT_OBJ_TYPE_BLOB) {
4400 error = got_error_path(link_target ? link_target : in_repo_path,
4405 error = got_object_open_as_blob(&blob, repo, obj_id, 8192);
4408 bca.f = got_opentemp();
4409 if (bca.f == NULL) {
4410 error = got_error_from_errno("got_opentemp");
4413 error = got_object_blob_dump_to_file(&filesize, &bca.nlines,
4414 &bca.line_offsets, bca.f, blob);
4415 if (error || bca.nlines == 0)
4418 /* Don't include \n at EOF in the blame line count. */
4419 if (bca.line_offsets[bca.nlines - 1] == filesize)
4422 bca.lines = calloc(bca.nlines, sizeof(*bca.lines));
4423 if (bca.lines == NULL) {
4424 error = got_error_from_errno("calloc");
4428 bca.nlines_prec = 0;
4436 error = got_blame(link_target ? link_target : in_repo_path, commit_id,
4437 repo, blame_cb, &bca, check_cancelled, NULL);
4446 got_object_blob_close(blob);
4448 got_worktree_close(worktree);
4450 const struct got_error *repo_error;
4451 repo_error = got_repo_close(repo);
4456 for (i = 0; i < bca.nlines; i++) {
4457 struct blame_line *bline = &bca.lines[i];
4458 free(bline->id_str);
4459 free(bline->committer);
4463 free(bca.line_offsets);
4464 if (bca.f && fclose(bca.f) == EOF && error == NULL)
4465 error = got_error_from_errno("fclose");
4473 "usage: %s tree [-c commit] [-r repository-path] [-iR] [path]\n",
4478 static const struct got_error *
4479 print_entry(struct got_tree_entry *te, const char *id, const char *path,
4480 const char *root_path, struct got_repository *repo)
4482 const struct got_error *err = NULL;
4483 int is_root_path = (strcmp(path, root_path) == 0);
4484 const char *modestr = "";
4485 mode_t mode = got_tree_entry_get_mode(te);
4486 char *link_target = NULL;
4488 path += strlen(root_path);
4489 while (path[0] == '/')
4492 if (got_object_tree_entry_is_submodule(te))
4494 else if (S_ISLNK(mode)) {
4497 err = got_tree_entry_get_symlink_target(&link_target, te, repo);
4500 for (i = 0; i < strlen(link_target); i++) {
4501 if (!isprint((unsigned char)link_target[i]))
4502 link_target[i] = '?';
4507 else if (S_ISDIR(mode))
4509 else if (mode & S_IXUSR)
4512 printf("%s%s%s%s%s%s%s\n", id ? id : "", path,
4513 is_root_path ? "" : "/", got_tree_entry_get_name(te), modestr,
4514 link_target ? " -> ": "", link_target ? link_target : "");
4520 static const struct got_error *
4521 print_tree(const char *path, struct got_object_id *commit_id,
4522 int show_ids, int recurse, const char *root_path,
4523 struct got_repository *repo)
4525 const struct got_error *err = NULL;
4526 struct got_object_id *tree_id = NULL;
4527 struct got_tree_object *tree = NULL;
4530 err = got_object_id_by_path(&tree_id, repo, commit_id, path);
4534 err = got_object_open_as_tree(&tree, repo, tree_id);
4537 nentries = got_object_tree_get_nentries(tree);
4538 for (i = 0; i < nentries; i++) {
4539 struct got_tree_entry *te;
4542 if (sigint_received || sigpipe_received)
4545 te = got_object_tree_get_entry(tree, i);
4548 err = got_object_id_str(&id_str,
4549 got_tree_entry_get_id(te));
4552 if (asprintf(&id, "%s ", id_str) == -1) {
4553 err = got_error_from_errno("asprintf");
4559 err = print_entry(te, id, path, root_path, repo);
4564 if (recurse && S_ISDIR(got_tree_entry_get_mode(te))) {
4566 if (asprintf(&child_path, "%s%s%s", path,
4567 path[0] == '/' && path[1] == '\0' ? "" : "/",
4568 got_tree_entry_get_name(te)) == -1) {
4569 err = got_error_from_errno("asprintf");
4572 err = print_tree(child_path, commit_id, show_ids, 1,
4581 got_object_tree_close(tree);
4586 static const struct got_error *
4587 cmd_tree(int argc, char *argv[])
4589 const struct got_error *error;
4590 struct got_repository *repo = NULL;
4591 struct got_worktree *worktree = NULL;
4592 const char *path, *refname = NULL;
4593 char *cwd = NULL, *repo_path = NULL, *in_repo_path = NULL;
4594 struct got_object_id *commit_id = NULL;
4595 char *commit_id_str = NULL;
4596 int show_ids = 0, recurse = 0;
4600 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
4605 while ((ch = getopt(argc, argv, "c:r:iR")) != -1) {
4608 commit_id_str = optarg;
4611 repo_path = realpath(optarg, NULL);
4612 if (repo_path == NULL)
4613 return got_error_from_errno2("realpath",
4615 got_path_strip_trailing_slashes(repo_path);
4639 cwd = getcwd(NULL, 0);
4641 error = got_error_from_errno("getcwd");
4644 if (repo_path == NULL) {
4645 error = got_worktree_open(&worktree, cwd);
4646 if (error && error->code != GOT_ERR_NOT_WORKTREE)
4652 strdup(got_worktree_get_repo_path(worktree));
4653 if (repo_path == NULL)
4654 error = got_error_from_errno("strdup");
4658 repo_path = strdup(cwd);
4659 if (repo_path == NULL) {
4660 error = got_error_from_errno("strdup");
4666 error = got_repo_open(&repo, repo_path, NULL);
4670 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
4676 char *p, *worktree_subdir = cwd +
4677 strlen(got_worktree_get_root_path(worktree));
4678 if (asprintf(&p, "%s/%s",
4679 got_worktree_get_path_prefix(worktree),
4680 worktree_subdir) == -1) {
4681 error = got_error_from_errno("asprintf");
4684 error = got_repo_map_path(&in_repo_path, repo, p, 0);
4691 if (in_repo_path == NULL) {
4692 error = got_repo_map_path(&in_repo_path, repo, path, 1);
4697 if (commit_id_str == NULL) {
4698 struct got_reference *head_ref;
4700 refname = got_worktree_get_head_ref_name(worktree);
4702 refname = GOT_REF_HEAD;
4703 error = got_ref_open(&head_ref, repo, refname, 0);
4706 error = got_ref_resolve(&commit_id, repo, head_ref);
4707 got_ref_close(head_ref);
4711 error = got_repo_match_object_id(&commit_id, NULL,
4712 commit_id_str, GOT_OBJ_TYPE_COMMIT, 1, repo);
4717 error = print_tree(in_repo_path, commit_id, show_ids, recurse,
4718 in_repo_path, repo);
4725 got_worktree_close(worktree);
4727 const struct got_error *repo_error;
4728 repo_error = got_repo_close(repo);
4738 fprintf(stderr, "usage: %s status [-s status-codes ] [path ...]\n",
4743 static const struct got_error *
4744 print_status(void *arg, unsigned char status, unsigned char staged_status,
4745 const char *path, struct got_object_id *blob_id,
4746 struct got_object_id *staged_blob_id, struct got_object_id *commit_id,
4747 int dirfd, const char *de_name)
4749 if (status == staged_status && (status == GOT_STATUS_DELETE))
4750 status = GOT_STATUS_NO_CHANGE;
4752 char *status_codes = arg;
4753 size_t ncodes = strlen(status_codes);
4755 for (i = 0; i < ncodes ; i++) {
4756 if (status == status_codes[i] ||
4757 staged_status == status_codes[i])
4763 printf("%c%c %s\n", status, staged_status, path);
4767 static const struct got_error *
4768 cmd_status(int argc, char *argv[])
4770 const struct got_error *error = NULL;
4771 struct got_repository *repo = NULL;
4772 struct got_worktree *worktree = NULL;
4773 char *cwd = NULL, *status_codes = NULL;;
4774 struct got_pathlist_head paths;
4775 struct got_pathlist_entry *pe;
4780 while ((ch = getopt(argc, argv, "s:")) != -1) {
4783 for (i = 0; i < strlen(optarg); i++) {
4784 switch (optarg[i]) {
4785 case GOT_STATUS_MODIFY:
4786 case GOT_STATUS_ADD:
4787 case GOT_STATUS_DELETE:
4788 case GOT_STATUS_CONFLICT:
4789 case GOT_STATUS_MISSING:
4790 case GOT_STATUS_OBSTRUCTED:
4791 case GOT_STATUS_UNVERSIONED:
4792 case GOT_STATUS_MODE_CHANGE:
4793 case GOT_STATUS_NONEXISTENT:
4796 errx(1, "invalid status code '%c'",
4800 status_codes = optarg;
4812 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
4816 cwd = getcwd(NULL, 0);
4818 error = got_error_from_errno("getcwd");
4822 error = got_worktree_open(&worktree, cwd);
4824 if (error->code == GOT_ERR_NOT_WORKTREE)
4825 error = wrap_not_worktree_error(error, "status", cwd);
4829 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
4834 error = apply_unveil(got_repo_get_path(repo), 1,
4835 got_worktree_get_root_path(worktree));
4839 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
4843 error = got_worktree_status(worktree, &paths, repo, print_status,
4844 status_codes, check_cancelled, NULL);
4846 TAILQ_FOREACH(pe, &paths, entry)
4847 free((char *)pe->path);
4848 got_pathlist_free(&paths);
4857 "usage: %s ref [-r repository] [-l] [-c object] [-s reference] "
4863 static const struct got_error *
4864 list_refs(struct got_repository *repo, const char *refname)
4866 static const struct got_error *err = NULL;
4867 struct got_reflist_head refs;
4868 struct got_reflist_entry *re;
4870 SIMPLEQ_INIT(&refs);
4871 err = got_ref_list(&refs, repo, refname, got_ref_cmp_by_name, NULL);
4875 SIMPLEQ_FOREACH(re, &refs, entry) {
4877 refstr = got_ref_to_str(re->ref);
4879 return got_error_from_errno("got_ref_to_str");
4880 printf("%s: %s\n", got_ref_get_name(re->ref), refstr);
4884 got_ref_list_free(&refs);
4888 static const struct got_error *
4889 delete_ref(struct got_repository *repo, const char *refname)
4891 const struct got_error *err = NULL;
4892 struct got_reference *ref;
4894 err = got_ref_open(&ref, repo, refname, 0);
4898 err = got_ref_delete(ref, repo);
4903 static const struct got_error *
4904 add_ref(struct got_repository *repo, const char *refname, const char *target)
4906 const struct got_error *err = NULL;
4907 struct got_object_id *id;
4908 struct got_reference *ref = NULL;
4911 * Don't let the user create a reference name with a leading '-'.
4912 * While technically a valid reference name, this case is usually
4913 * an unintended typo.
4915 if (refname[0] == '-')
4916 return got_error_path(refname, GOT_ERR_REF_NAME_MINUS);
4918 err = got_repo_match_object_id_prefix(&id, target, GOT_OBJ_TYPE_ANY,
4921 struct got_reference *target_ref;
4923 if (err->code != GOT_ERR_BAD_OBJ_ID_STR)
4925 err = got_ref_open(&target_ref, repo, target, 0);
4928 err = got_ref_resolve(&id, repo, target_ref);
4929 got_ref_close(target_ref);
4934 err = got_ref_alloc(&ref, refname, id);
4938 err = got_ref_write(ref, repo);
4946 static const struct got_error *
4947 add_symref(struct got_repository *repo, const char *refname, const char *target)
4949 const struct got_error *err = NULL;
4950 struct got_reference *ref = NULL;
4951 struct got_reference *target_ref = NULL;
4954 * Don't let the user create a reference name with a leading '-'.
4955 * While technically a valid reference name, this case is usually
4956 * an unintended typo.
4958 if (refname[0] == '-')
4959 return got_error_path(refname, GOT_ERR_REF_NAME_MINUS);
4961 err = got_ref_open(&target_ref, repo, target, 0);
4965 err = got_ref_alloc_symref(&ref, refname, target_ref);
4969 err = got_ref_write(ref, repo);
4972 got_ref_close(target_ref);
4978 static const struct got_error *
4979 cmd_ref(int argc, char *argv[])
4981 const struct got_error *error = NULL;
4982 struct got_repository *repo = NULL;
4983 struct got_worktree *worktree = NULL;
4984 char *cwd = NULL, *repo_path = NULL;
4985 int ch, do_list = 0, do_delete = 0;
4986 const char *obj_arg = NULL, *symref_target= NULL;
4987 char *refname = NULL;
4989 while ((ch = getopt(argc, argv, "c:dr:ls:")) != -1) {
4998 repo_path = realpath(optarg, NULL);
4999 if (repo_path == NULL)
5000 return got_error_from_errno2("realpath",
5002 got_path_strip_trailing_slashes(repo_path);
5008 symref_target = optarg;
5016 if (obj_arg && do_list)
5017 errx(1, "-c and -l options are mutually exclusive");
5018 if (obj_arg && do_delete)
5019 errx(1, "-c and -d options are mutually exclusive");
5020 if (obj_arg && symref_target)
5021 errx(1, "-c and -s options are mutually exclusive");
5022 if (symref_target && do_delete)
5023 errx(1, "-s and -d options are mutually exclusive");
5024 if (symref_target && do_list)
5025 errx(1, "-s and -l options are mutually exclusive");
5026 if (do_delete && do_list)
5027 errx(1, "-d and -l options are mutually exclusive");
5033 if (argc != 0 && argc != 1)
5036 refname = strdup(argv[0]);
5037 if (refname == NULL) {
5038 error = got_error_from_errno("strdup");
5045 refname = strdup(argv[0]);
5046 if (refname == NULL) {
5047 error = got_error_from_errno("strdup");
5053 got_path_strip_trailing_slashes(refname);
5057 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
5061 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
5062 "sendfd unveil", NULL) == -1)
5066 cwd = getcwd(NULL, 0);
5068 error = got_error_from_errno("getcwd");
5072 if (repo_path == NULL) {
5073 error = got_worktree_open(&worktree, cwd);
5074 if (error && error->code != GOT_ERR_NOT_WORKTREE)
5080 strdup(got_worktree_get_repo_path(worktree));
5081 if (repo_path == NULL)
5082 error = got_error_from_errno("strdup");
5086 repo_path = strdup(cwd);
5087 if (repo_path == NULL) {
5088 error = got_error_from_errno("strdup");
5094 error = got_repo_open(&repo, repo_path, NULL);
5098 error = apply_unveil(got_repo_get_path(repo), do_list,
5099 worktree ? got_worktree_get_root_path(worktree) : NULL);
5104 error = list_refs(repo, refname);
5106 error = delete_ref(repo, refname);
5107 else if (symref_target)
5108 error = add_symref(repo, refname, symref_target);
5110 if (obj_arg == NULL)
5112 error = add_ref(repo, refname, obj_arg);
5117 got_repo_close(repo);
5119 got_worktree_close(worktree);
5129 "usage: %s branch [-c commit] [-d] [-r repository] [-l] [-n] "
5130 "[name]\n", getprogname());
5134 static const struct got_error *
5135 list_branch(struct got_repository *repo, struct got_worktree *worktree,
5136 struct got_reference *ref)
5138 const struct got_error *err = NULL;
5139 const char *refname, *marker = " ";
5142 refname = got_ref_get_name(ref);
5143 if (worktree && strcmp(refname,
5144 got_worktree_get_head_ref_name(worktree)) == 0) {
5145 struct got_object_id *id = NULL;
5147 err = got_ref_resolve(&id, repo, ref);
5150 if (got_object_id_cmp(id,
5151 got_worktree_get_base_commit_id(worktree)) == 0)
5158 if (strncmp(refname, "refs/heads/", 11) == 0)
5160 if (strncmp(refname, "refs/got/worktree/", 18) == 0)
5163 refstr = got_ref_to_str(ref);
5165 return got_error_from_errno("got_ref_to_str");
5167 printf("%s%s: %s\n", marker, refname, refstr);
5172 static const struct got_error *
5173 show_current_branch(struct got_repository *repo, struct got_worktree *worktree)
5175 const char *refname;
5177 if (worktree == NULL)
5178 return got_error(GOT_ERR_NOT_WORKTREE);
5180 refname = got_worktree_get_head_ref_name(worktree);
5182 if (strncmp(refname, "refs/heads/", 11) == 0)
5184 if (strncmp(refname, "refs/got/worktree/", 18) == 0)
5187 printf("%s\n", refname);
5192 static const struct got_error *
5193 list_branches(struct got_repository *repo, struct got_worktree *worktree)
5195 static const struct got_error *err = NULL;
5196 struct got_reflist_head refs;
5197 struct got_reflist_entry *re;
5198 struct got_reference *temp_ref = NULL;
5199 int rebase_in_progress, histedit_in_progress;
5201 SIMPLEQ_INIT(&refs);
5204 err = got_worktree_rebase_in_progress(&rebase_in_progress,
5209 err = got_worktree_histedit_in_progress(&histedit_in_progress,
5214 if (rebase_in_progress || histedit_in_progress) {
5215 err = got_ref_open(&temp_ref, repo,
5216 got_worktree_get_head_ref_name(worktree), 0);
5219 list_branch(repo, worktree, temp_ref);
5220 got_ref_close(temp_ref);
5224 err = got_ref_list(&refs, repo, "refs/heads",
5225 got_ref_cmp_by_name, NULL);
5229 SIMPLEQ_FOREACH(re, &refs, entry)
5230 list_branch(repo, worktree, re->ref);
5232 got_ref_list_free(&refs);
5236 static const struct got_error *
5237 delete_branch(struct got_repository *repo, struct got_worktree *worktree,
5238 const char *branch_name)
5240 const struct got_error *err = NULL;
5241 struct got_reference *ref = NULL;
5244 if (asprintf(&refname, "refs/heads/%s", branch_name) == -1)
5245 return got_error_from_errno("asprintf");
5247 err = got_ref_open(&ref, repo, refname, 0);
5252 strcmp(got_worktree_get_head_ref_name(worktree),
5253 got_ref_get_name(ref)) == 0) {
5254 err = got_error_msg(GOT_ERR_SAME_BRANCH,
5255 "will not delete this work tree's current branch");
5259 err = got_ref_delete(ref, repo);
5267 static const struct got_error *
5268 add_branch(struct got_repository *repo, const char *branch_name,
5269 struct got_object_id *base_commit_id)
5271 const struct got_error *err = NULL;
5272 struct got_reference *ref = NULL;
5273 char *base_refname = NULL, *refname = NULL;
5276 * Don't let the user create a branch name with a leading '-'.
5277 * While technically a valid reference name, this case is usually
5278 * an unintended typo.
5280 if (branch_name[0] == '-')
5281 return got_error_path(branch_name, GOT_ERR_REF_NAME_MINUS);
5283 if (asprintf(&refname, "refs/heads/%s", branch_name) == -1) {
5284 err = got_error_from_errno("asprintf");
5288 err = got_ref_open(&ref, repo, refname, 0);
5290 err = got_error(GOT_ERR_BRANCH_EXISTS);
5292 } else if (err->code != GOT_ERR_NOT_REF)
5295 err = got_ref_alloc(&ref, refname, base_commit_id);
5299 err = got_ref_write(ref, repo);
5308 static const struct got_error *
5309 cmd_branch(int argc, char *argv[])
5311 const struct got_error *error = NULL;
5312 struct got_repository *repo = NULL;
5313 struct got_worktree *worktree = NULL;
5314 char *cwd = NULL, *repo_path = NULL;
5315 int ch, do_list = 0, do_show = 0, do_update = 1;
5316 const char *delref = NULL, *commit_id_arg = NULL;
5317 struct got_reference *ref = NULL;
5318 struct got_pathlist_head paths;
5319 struct got_pathlist_entry *pe;
5320 struct got_object_id *commit_id = NULL;
5321 char *commit_id_str = NULL;
5325 while ((ch = getopt(argc, argv, "c:d:r:ln")) != -1) {
5328 commit_id_arg = optarg;
5334 repo_path = realpath(optarg, NULL);
5335 if (repo_path == NULL)
5336 return got_error_from_errno2("realpath",
5338 got_path_strip_trailing_slashes(repo_path);
5352 if (do_list && delref)
5353 errx(1, "-l and -d options are mutually exclusive");
5358 if (!do_list && !delref && argc == 0)
5361 if ((do_list || delref || do_show) && commit_id_arg != NULL)
5362 errx(1, "-c option can only be used when creating a branch");
5364 if (do_list || delref) {
5367 } else if (!do_show && argc != 1)
5371 if (do_list || do_show) {
5372 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
5376 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
5377 "sendfd unveil", NULL) == -1)
5381 cwd = getcwd(NULL, 0);
5383 error = got_error_from_errno("getcwd");
5387 if (repo_path == NULL) {
5388 error = got_worktree_open(&worktree, cwd);
5389 if (error && error->code != GOT_ERR_NOT_WORKTREE)
5395 strdup(got_worktree_get_repo_path(worktree));
5396 if (repo_path == NULL)
5397 error = got_error_from_errno("strdup");
5401 repo_path = strdup(cwd);
5402 if (repo_path == NULL) {
5403 error = got_error_from_errno("strdup");
5409 error = got_repo_open(&repo, repo_path, NULL);
5413 error = apply_unveil(got_repo_get_path(repo), do_list,
5414 worktree ? got_worktree_get_root_path(worktree) : NULL);
5419 error = show_current_branch(repo, worktree);
5421 error = list_branches(repo, worktree);
5423 error = delete_branch(repo, worktree, delref);
5425 if (commit_id_arg == NULL)
5426 commit_id_arg = worktree ?
5427 got_worktree_get_head_ref_name(worktree) :
5429 error = got_repo_match_object_id(&commit_id, NULL,
5430 commit_id_arg, GOT_OBJ_TYPE_COMMIT, 1, repo);
5433 error = add_branch(repo, argv[0], commit_id);
5436 if (worktree && do_update) {
5437 struct got_update_progress_arg upa;
5438 char *branch_refname = NULL;
5440 error = got_object_id_str(&commit_id_str, commit_id);
5443 error = get_worktree_paths_from_argv(&paths, 0, NULL,
5447 if (asprintf(&branch_refname, "refs/heads/%s", argv[0])
5449 error = got_error_from_errno("asprintf");
5452 error = got_ref_open(&ref, repo, branch_refname, 0);
5453 free(branch_refname);
5456 error = switch_head_ref(ref, commit_id, worktree,
5460 error = got_worktree_set_base_commit_id(worktree, repo,
5464 memset(&upa, 0, sizeof(upa));
5465 error = got_worktree_checkout_files(worktree, &paths,
5466 repo, update_progress, &upa, check_cancelled,
5470 if (upa.did_something)
5471 printf("Updated to commit %s\n", commit_id_str);
5472 print_update_progress_stats(&upa);
5479 got_repo_close(repo);
5481 got_worktree_close(worktree);
5485 free(commit_id_str);
5486 TAILQ_FOREACH(pe, &paths, entry)
5487 free((char *)pe->path);
5488 got_pathlist_free(&paths);
5497 "usage: %s tag [-c commit] [-r repository] [-l] "
5498 "[-m message] name\n", getprogname());
5503 static const struct got_error *
5504 sort_tags(struct got_reflist_head *sorted, struct got_reflist_head *tags)
5506 const struct got_error *err = NULL;
5507 struct got_reflist_entry *re, *se, *new;
5508 struct got_object_id *re_id, *se_id;
5509 struct got_tag_object *re_tag, *se_tag;
5510 time_t re_time, se_time;
5512 SIMPLEQ_FOREACH(re, tags, entry) {
5513 se = SIMPLEQ_FIRST(sorted);
5515 err = got_reflist_entry_dup(&new, re);
5518 SIMPLEQ_INSERT_HEAD(sorted, new, entry);
5521 err = got_ref_resolve(&re_id, repo, re->ref);
5524 err = got_object_open_as_tag(&re_tag, repo, re_id);
5528 re_time = got_object_tag_get_tagger_time(re_tag);
5529 got_object_tag_close(re_tag);
5533 err = got_ref_resolve(&se_id, repo, re->ref);
5536 err = got_object_open_as_tag(&se_tag, repo, se_id);
5540 se_time = got_object_tag_get_tagger_time(se_tag);
5541 got_object_tag_close(se_tag);
5543 if (se_time > re_time) {
5544 err = got_reflist_entry_dup(&new, re);
5547 SIMPLEQ_INSERT_AFTER(sorted, se, new, entry);
5550 se = SIMPLEQ_NEXT(se, entry);
5559 static const struct got_error *
5560 list_tags(struct got_repository *repo, struct got_worktree *worktree)
5562 static const struct got_error *err = NULL;
5563 struct got_reflist_head refs;
5564 struct got_reflist_entry *re;
5566 SIMPLEQ_INIT(&refs);
5568 err = got_ref_list(&refs, repo, "refs/tags", got_ref_cmp_tags, repo);
5572 SIMPLEQ_FOREACH(re, &refs, entry) {
5573 const char *refname;
5574 char *refstr, *tagmsg0, *tagmsg, *line, *id_str, *datestr;
5578 struct got_object_id *id;
5579 struct got_tag_object *tag;
5580 struct got_commit_object *commit = NULL;
5582 refname = got_ref_get_name(re->ref);
5583 if (strncmp(refname, "refs/tags/", 10) != 0)
5586 refstr = got_ref_to_str(re->ref);
5587 if (refstr == NULL) {
5588 err = got_error_from_errno("got_ref_to_str");
5591 printf("%stag %s %s\n", GOT_COMMIT_SEP_STR, refname, refstr);
5594 err = got_ref_resolve(&id, repo, re->ref);
5597 err = got_object_open_as_tag(&tag, repo, id);
5599 if (err->code != GOT_ERR_OBJ_TYPE) {
5603 /* "lightweight" tag */
5604 err = got_object_open_as_commit(&commit, repo, id);
5609 tagger = got_object_commit_get_committer(commit);
5611 got_object_commit_get_committer_time(commit);
5612 err = got_object_id_str(&id_str, id);
5618 tagger = got_object_tag_get_tagger(tag);
5619 tagger_time = got_object_tag_get_tagger_time(tag);
5620 err = got_object_id_str(&id_str,
5621 got_object_tag_get_object_id(tag));
5625 printf("from: %s\n", tagger);
5626 datestr = get_datestr(&tagger_time, datebuf);
5628 printf("date: %s UTC\n", datestr);
5630 printf("object: %s %s\n", GOT_OBJ_LABEL_COMMIT, id_str);
5632 switch (got_object_tag_get_object_type(tag)) {
5633 case GOT_OBJ_TYPE_BLOB:
5634 printf("object: %s %s\n", GOT_OBJ_LABEL_BLOB,
5637 case GOT_OBJ_TYPE_TREE:
5638 printf("object: %s %s\n", GOT_OBJ_LABEL_TREE,
5641 case GOT_OBJ_TYPE_COMMIT:
5642 printf("object: %s %s\n", GOT_OBJ_LABEL_COMMIT,
5645 case GOT_OBJ_TYPE_TAG:
5646 printf("object: %s %s\n", GOT_OBJ_LABEL_TAG,
5655 err = got_object_commit_get_logmsg(&tagmsg0, commit);
5658 got_object_commit_close(commit);
5660 tagmsg0 = strdup(got_object_tag_get_message(tag));
5661 got_object_tag_close(tag);
5662 if (tagmsg0 == NULL) {
5663 err = got_error_from_errno("strdup");
5670 line = strsep(&tagmsg, "\n");
5672 printf(" %s\n", line);
5677 got_ref_list_free(&refs);
5681 static const struct got_error *
5682 get_tag_message(char **tagmsg, char **tagmsg_path, const char *commit_id_str,
5683 const char *tag_name, const char *repo_path)
5685 const struct got_error *err = NULL;
5686 char *template = NULL, *initial_content = NULL;
5687 char *editor = NULL;
5688 int initial_content_len;
5691 if (asprintf(&template, GOT_TMPDIR_STR "/got-tagmsg") == -1) {
5692 err = got_error_from_errno("asprintf");
5696 initial_content_len = asprintf(&initial_content,
5697 "\n# tagging commit %s as %s\n",
5698 commit_id_str, tag_name);
5699 if (initial_content_len == -1) {
5700 err = got_error_from_errno("asprintf");
5704 err = got_opentemp_named_fd(tagmsg_path, &fd, template);
5708 write(fd, initial_content, initial_content_len);
5711 err = get_editor(&editor);
5714 err = edit_logmsg(tagmsg, editor, *tagmsg_path, initial_content);
5716 free(initial_content);
5720 /* Editor is done; we can now apply unveil(2) */
5722 err = apply_unveil(repo_path, 0, NULL);
5731 static const struct got_error *
5732 add_tag(struct got_repository *repo, struct got_worktree *worktree,
5733 const char *tag_name, const char *commit_arg, const char *tagmsg_arg)
5735 const struct got_error *err = NULL;
5736 struct got_object_id *commit_id = NULL, *tag_id = NULL;
5737 char *label = NULL, *commit_id_str = NULL;
5738 struct got_reference *ref = NULL;
5739 char *refname = NULL, *tagmsg = NULL, *tagger = NULL;
5740 char *tagmsg_path = NULL, *tag_id_str = NULL;
5741 int preserve_tagmsg = 0;
5744 * Don't let the user create a tag name with a leading '-'.
5745 * While technically a valid reference name, this case is usually
5746 * an unintended typo.
5748 if (tag_name[0] == '-')
5749 return got_error_path(tag_name, GOT_ERR_REF_NAME_MINUS);
5751 err = get_author(&tagger, repo, worktree);
5755 err = got_repo_match_object_id(&commit_id, &label, commit_arg,
5756 GOT_OBJ_TYPE_COMMIT, 1, repo);
5760 err = got_object_id_str(&commit_id_str, commit_id);
5764 if (strncmp("refs/tags/", tag_name, 10) == 0) {
5765 refname = strdup(tag_name);
5766 if (refname == NULL) {
5767 err = got_error_from_errno("strdup");
5771 } else if (asprintf(&refname, "refs/tags/%s", tag_name) == -1) {
5772 err = got_error_from_errno("asprintf");
5776 err = got_ref_open(&ref, repo, refname, 0);
5778 err = got_error(GOT_ERR_TAG_EXISTS);
5780 } else if (err->code != GOT_ERR_NOT_REF)
5783 if (tagmsg_arg == NULL) {
5784 err = get_tag_message(&tagmsg, &tagmsg_path, commit_id_str,
5785 tag_name, got_repo_get_path(repo));
5787 if (err->code != GOT_ERR_COMMIT_MSG_EMPTY &&
5788 tagmsg_path != NULL)
5789 preserve_tagmsg = 1;
5794 err = got_object_tag_create(&tag_id, tag_name, commit_id,
5795 tagger, time(NULL), tagmsg ? tagmsg : tagmsg_arg, repo);
5798 preserve_tagmsg = 1;
5802 err = got_ref_alloc(&ref, refname, tag_id);
5805 preserve_tagmsg = 1;
5809 err = got_ref_write(ref, repo);
5812 preserve_tagmsg = 1;
5816 err = got_object_id_str(&tag_id_str, tag_id);
5819 preserve_tagmsg = 1;
5822 printf("Created tag %s\n", tag_id_str);
5824 if (preserve_tagmsg) {
5825 fprintf(stderr, "%s: tag message preserved in %s\n",
5826 getprogname(), tagmsg_path);
5827 } else if (tagmsg_path && unlink(tagmsg_path) == -1 && err == NULL)
5828 err = got_error_from_errno2("unlink", tagmsg_path);
5833 free(commit_id_str);
5841 static const struct got_error *
5842 cmd_tag(int argc, char *argv[])
5844 const struct got_error *error = NULL;
5845 struct got_repository *repo = NULL;
5846 struct got_worktree *worktree = NULL;
5847 char *cwd = NULL, *repo_path = NULL, *commit_id_str = NULL;
5848 char *gitconfig_path = NULL;
5849 const char *tag_name, *commit_id_arg = NULL, *tagmsg = NULL;
5850 int ch, do_list = 0;
5852 while ((ch = getopt(argc, argv, "c:m:r:l")) != -1) {
5855 commit_id_arg = optarg;
5861 repo_path = realpath(optarg, NULL);
5862 if (repo_path == NULL)
5863 return got_error_from_errno2("realpath",
5865 got_path_strip_trailing_slashes(repo_path);
5880 if (commit_id_arg != NULL)
5882 "-c option can only be used when creating a tag");
5884 errx(1, "-l and -m options are mutually exclusive");
5887 } else if (argc != 1)
5894 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
5898 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
5899 "sendfd unveil", NULL) == -1)
5903 cwd = getcwd(NULL, 0);
5905 error = got_error_from_errno("getcwd");
5909 if (repo_path == NULL) {
5910 error = got_worktree_open(&worktree, cwd);
5911 if (error && error->code != GOT_ERR_NOT_WORKTREE)
5917 strdup(got_worktree_get_repo_path(worktree));
5918 if (repo_path == NULL)
5919 error = got_error_from_errno("strdup");
5923 repo_path = strdup(cwd);
5924 if (repo_path == NULL) {
5925 error = got_error_from_errno("strdup");
5932 error = got_repo_open(&repo, repo_path, NULL);
5935 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
5938 error = list_tags(repo, worktree);
5940 error = get_gitconfig_path(&gitconfig_path);
5943 error = got_repo_open(&repo, repo_path, gitconfig_path);
5948 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
5953 if (commit_id_arg == NULL) {
5954 struct got_reference *head_ref;
5955 struct got_object_id *commit_id;
5956 error = got_ref_open(&head_ref, repo,
5957 worktree ? got_worktree_get_head_ref_name(worktree)
5961 error = got_ref_resolve(&commit_id, repo, head_ref);
5962 got_ref_close(head_ref);
5965 error = got_object_id_str(&commit_id_str, commit_id);
5971 error = add_tag(repo, worktree, tag_name,
5972 commit_id_str ? commit_id_str : commit_id_arg, tagmsg);
5976 got_repo_close(repo);
5978 got_worktree_close(worktree);
5981 free(gitconfig_path);
5982 free(commit_id_str);
5989 fprintf(stderr, "usage: %s add [-R] [-I] path ...\n",
5994 static const struct got_error *
5995 add_progress(void *arg, unsigned char status, const char *path)
5997 while (path[0] == '/')
5999 printf("%c %s\n", status, path);
6003 static const struct got_error *
6004 cmd_add(int argc, char *argv[])
6006 const struct got_error *error = NULL;
6007 struct got_repository *repo = NULL;
6008 struct got_worktree *worktree = NULL;
6010 struct got_pathlist_head paths;
6011 struct got_pathlist_entry *pe;
6012 int ch, can_recurse = 0, no_ignores = 0;
6016 while ((ch = getopt(argc, argv, "IR")) != -1) {
6034 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
6041 cwd = getcwd(NULL, 0);
6043 error = got_error_from_errno("getcwd");
6047 error = got_worktree_open(&worktree, cwd);
6049 if (error->code == GOT_ERR_NOT_WORKTREE)
6050 error = wrap_not_worktree_error(error, "add", cwd);
6054 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
6059 error = apply_unveil(got_repo_get_path(repo), 1,
6060 got_worktree_get_root_path(worktree));
6064 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
6068 if (!can_recurse && no_ignores) {
6069 error = got_error_msg(GOT_ERR_BAD_PATH,
6070 "disregarding ignores requires -R option");
6078 TAILQ_FOREACH(pe, &paths, entry) {
6079 if (asprintf(&ondisk_path, "%s/%s",
6080 got_worktree_get_root_path(worktree),
6082 error = got_error_from_errno("asprintf");
6085 if (lstat(ondisk_path, &sb) == -1) {
6086 if (errno == ENOENT) {
6090 error = got_error_from_errno2("lstat",
6096 if (S_ISDIR(sb.st_mode)) {
6097 error = got_error_msg(GOT_ERR_BAD_PATH,
6098 "adding directories requires -R option");
6104 error = got_worktree_schedule_add(worktree, &paths, add_progress,
6105 NULL, repo, no_ignores);
6108 got_repo_close(repo);
6110 got_worktree_close(worktree);
6111 TAILQ_FOREACH(pe, &paths, entry)
6112 free((char *)pe->path);
6113 got_pathlist_free(&paths);
6121 fprintf(stderr, "usage: %s remove [-f] [-k] [-R] [-s status-codes] "
6122 "path ...\n", getprogname());
6126 static const struct got_error *
6127 print_remove_status(void *arg, unsigned char status,
6128 unsigned char staged_status, const char *path)
6130 while (path[0] == '/')
6132 if (status == GOT_STATUS_NONEXISTENT)
6134 if (status == staged_status && (status == GOT_STATUS_DELETE))
6135 status = GOT_STATUS_NO_CHANGE;
6136 printf("%c%c %s\n", status, staged_status, path);
6140 static const struct got_error *
6141 cmd_remove(int argc, char *argv[])
6143 const struct got_error *error = NULL;
6144 struct got_worktree *worktree = NULL;
6145 struct got_repository *repo = NULL;
6146 const char *status_codes = NULL;
6148 struct got_pathlist_head paths;
6149 struct got_pathlist_entry *pe;
6150 int ch, delete_local_mods = 0, can_recurse = 0, keep_on_disk = 0, i;
6154 while ((ch = getopt(argc, argv, "fkRs:")) != -1) {
6157 delete_local_mods = 1;
6166 for (i = 0; i < strlen(optarg); i++) {
6167 switch (optarg[i]) {
6168 case GOT_STATUS_MODIFY:
6169 delete_local_mods = 1;
6171 case GOT_STATUS_MISSING:
6174 errx(1, "invalid status code '%c'",
6178 status_codes = optarg;
6190 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
6197 cwd = getcwd(NULL, 0);
6199 error = got_error_from_errno("getcwd");
6202 error = got_worktree_open(&worktree, cwd);
6204 if (error->code == GOT_ERR_NOT_WORKTREE)
6205 error = wrap_not_worktree_error(error, "remove", cwd);
6209 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
6214 error = apply_unveil(got_repo_get_path(repo), 1,
6215 got_worktree_get_root_path(worktree));
6219 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
6226 TAILQ_FOREACH(pe, &paths, entry) {
6227 if (asprintf(&ondisk_path, "%s/%s",
6228 got_worktree_get_root_path(worktree),
6230 error = got_error_from_errno("asprintf");
6233 if (lstat(ondisk_path, &sb) == -1) {
6234 if (errno == ENOENT) {
6238 error = got_error_from_errno2("lstat",
6244 if (S_ISDIR(sb.st_mode)) {
6245 error = got_error_msg(GOT_ERR_BAD_PATH,
6246 "removing directories requires -R option");
6252 error = got_worktree_schedule_delete(worktree, &paths,
6253 delete_local_mods, status_codes, print_remove_status, NULL,
6254 repo, keep_on_disk);
6257 got_repo_close(repo);
6259 got_worktree_close(worktree);
6260 TAILQ_FOREACH(pe, &paths, entry)
6261 free((char *)pe->path);
6262 got_pathlist_free(&paths);
6270 fprintf(stderr, "usage: %s revert [-p] [-F response-script] [-R] "
6271 "path ...\n", getprogname());
6275 static const struct got_error *
6276 revert_progress(void *arg, unsigned char status, const char *path)
6278 if (status == GOT_STATUS_UNVERSIONED)
6281 while (path[0] == '/')
6283 printf("%c %s\n", status, path);
6287 struct choose_patch_arg {
6288 FILE *patch_script_file;
6292 static const struct got_error *
6293 show_change(unsigned char status, const char *path, FILE *patch_file, int n,
6294 int nchanges, const char *action)
6297 size_t linesize = 0;
6301 case GOT_STATUS_ADD:
6302 printf("A %s\n%s this addition? [y/n] ", path, action);
6304 case GOT_STATUS_DELETE:
6305 printf("D %s\n%s this deletion? [y/n] ", path, action);
6307 case GOT_STATUS_MODIFY:
6308 if (fseek(patch_file, 0L, SEEK_SET) == -1)
6309 return got_error_from_errno("fseek");
6310 printf(GOT_COMMIT_SEP_STR);
6311 while ((linelen = getline(&line, &linesize, patch_file)) != -1)
6313 if (ferror(patch_file))
6314 return got_error_from_errno("getline");
6315 printf(GOT_COMMIT_SEP_STR);
6316 printf("M %s (change %d of %d)\n%s this change? [y/n/q] ",
6317 path, n, nchanges, action);
6320 return got_error_path(path, GOT_ERR_FILE_STATUS);
6326 static const struct got_error *
6327 choose_patch(int *choice, void *arg, unsigned char status, const char *path,
6328 FILE *patch_file, int n, int nchanges)
6330 const struct got_error *err = NULL;
6332 size_t linesize = 0;
6335 struct choose_patch_arg *a = arg;
6337 *choice = GOT_PATCH_CHOICE_NONE;
6339 if (a->patch_script_file) {
6341 err = show_change(status, path, patch_file, n, nchanges,
6345 linelen = getline(&line, &linesize, a->patch_script_file);
6346 if (linelen == -1) {
6347 if (ferror(a->patch_script_file))
6348 return got_error_from_errno("getline");
6351 nl = strchr(line, '\n');
6354 if (strcmp(line, "y") == 0) {
6355 *choice = GOT_PATCH_CHOICE_YES;
6357 } else if (strcmp(line, "n") == 0) {
6358 *choice = GOT_PATCH_CHOICE_NO;
6360 } else if (strcmp(line, "q") == 0 &&
6361 status == GOT_STATUS_MODIFY) {
6362 *choice = GOT_PATCH_CHOICE_QUIT;
6365 printf("invalid response '%s'\n", line);
6370 while (resp != 'y' && resp != 'n' && resp != 'q') {
6371 err = show_change(status, path, patch_file, n, nchanges,
6378 if (status == GOT_STATUS_MODIFY) {
6379 if (resp != 'y' && resp != 'n' && resp != 'q') {
6380 printf("invalid response '%c'\n", resp);
6383 } else if (resp != 'y' && resp != 'n') {
6384 printf("invalid response '%c'\n", resp);
6390 *choice = GOT_PATCH_CHOICE_YES;
6391 else if (resp == 'n')
6392 *choice = GOT_PATCH_CHOICE_NO;
6393 else if (resp == 'q' && status == GOT_STATUS_MODIFY)
6394 *choice = GOT_PATCH_CHOICE_QUIT;
6400 static const struct got_error *
6401 cmd_revert(int argc, char *argv[])
6403 const struct got_error *error = NULL;
6404 struct got_worktree *worktree = NULL;
6405 struct got_repository *repo = NULL;
6406 char *cwd = NULL, *path = NULL;
6407 struct got_pathlist_head paths;
6408 struct got_pathlist_entry *pe;
6409 int ch, can_recurse = 0, pflag = 0;
6410 FILE *patch_script_file = NULL;
6411 const char *patch_script_path = NULL;
6412 struct choose_patch_arg cpa;
6416 while ((ch = getopt(argc, argv, "pF:R")) != -1) {
6422 patch_script_path = optarg;
6437 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
6438 "unveil", NULL) == -1)
6443 if (patch_script_path && !pflag)
6444 errx(1, "-F option can only be used together with -p option");
6446 cwd = getcwd(NULL, 0);
6448 error = got_error_from_errno("getcwd");
6451 error = got_worktree_open(&worktree, cwd);
6453 if (error->code == GOT_ERR_NOT_WORKTREE)
6454 error = wrap_not_worktree_error(error, "revert", cwd);
6458 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
6463 if (patch_script_path) {
6464 patch_script_file = fopen(patch_script_path, "r");
6465 if (patch_script_file == NULL) {
6466 error = got_error_from_errno2("fopen",
6471 error = apply_unveil(got_repo_get_path(repo), 1,
6472 got_worktree_get_root_path(worktree));
6476 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
6483 TAILQ_FOREACH(pe, &paths, entry) {
6484 if (asprintf(&ondisk_path, "%s/%s",
6485 got_worktree_get_root_path(worktree),
6487 error = got_error_from_errno("asprintf");
6490 if (lstat(ondisk_path, &sb) == -1) {
6491 if (errno == ENOENT) {
6495 error = got_error_from_errno2("lstat",
6501 if (S_ISDIR(sb.st_mode)) {
6502 error = got_error_msg(GOT_ERR_BAD_PATH,
6503 "reverting directories requires -R option");
6509 cpa.patch_script_file = patch_script_file;
6510 cpa.action = "revert";
6511 error = got_worktree_revert(worktree, &paths, revert_progress, NULL,
6512 pflag ? choose_patch : NULL, &cpa, repo);
6514 if (patch_script_file && fclose(patch_script_file) == EOF &&
6516 error = got_error_from_errno2("fclose", patch_script_path);
6518 got_repo_close(repo);
6520 got_worktree_close(worktree);
6529 fprintf(stderr, "usage: %s commit [-m msg] [-S] [path ...]\n",
6534 struct collect_commit_logmsg_arg {
6535 const char *cmdline_log;
6537 const char *worktree_path;
6538 const char *branch_name;
6539 const char *repo_path;
6544 static const struct got_error *
6545 collect_commit_logmsg(struct got_pathlist_head *commitable_paths, char **logmsg,
6548 char *initial_content = NULL;
6549 struct got_pathlist_entry *pe;
6550 const struct got_error *err = NULL;
6551 char *template = NULL;
6552 struct collect_commit_logmsg_arg *a = arg;
6553 int initial_content_len;
6557 /* if a message was specified on the command line, just use it */
6558 if (a->cmdline_log != NULL && strlen(a->cmdline_log) != 0) {
6559 len = strlen(a->cmdline_log) + 1;
6560 *logmsg = malloc(len + 1);
6561 if (*logmsg == NULL)
6562 return got_error_from_errno("malloc");
6563 strlcpy(*logmsg, a->cmdline_log, len);
6567 if (asprintf(&template, "%s/logmsg", a->worktree_path) == -1)
6568 return got_error_from_errno("asprintf");
6570 initial_content_len = asprintf(&initial_content,
6571 "\n# changes to be committed on branch %s:\n",
6573 if (initial_content_len == -1)
6574 return got_error_from_errno("asprintf");
6576 err = got_opentemp_named_fd(&a->logmsg_path, &fd, template);
6580 write(fd, initial_content, initial_content_len);
6582 TAILQ_FOREACH(pe, commitable_paths, entry) {
6583 struct got_commitable *ct = pe->data;
6584 dprintf(fd, "# %c %s\n",
6585 got_commitable_get_status(ct),
6586 got_commitable_get_path(ct));
6590 err = edit_logmsg(logmsg, a->editor, a->logmsg_path, initial_content);
6592 free(initial_content);
6595 /* Editor is done; we can now apply unveil(2) */
6597 err = apply_unveil(a->repo_path, 0, a->worktree_path);
6606 static const struct got_error *
6607 cmd_commit(int argc, char *argv[])
6609 const struct got_error *error = NULL;
6610 struct got_worktree *worktree = NULL;
6611 struct got_repository *repo = NULL;
6612 char *cwd = NULL, *id_str = NULL;
6613 struct got_object_id *id = NULL;
6614 const char *logmsg = NULL;
6615 struct collect_commit_logmsg_arg cl_arg;
6616 char *gitconfig_path = NULL, *editor = NULL, *author = NULL;
6617 int ch, rebase_in_progress, histedit_in_progress, preserve_logmsg = 0;
6618 int allow_bad_symlinks = 0;
6619 struct got_pathlist_head paths;
6622 cl_arg.logmsg_path = NULL;
6624 while ((ch = getopt(argc, argv, "m:S")) != -1) {
6630 allow_bad_symlinks = 1;
6642 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
6643 "unveil", NULL) == -1)
6646 cwd = getcwd(NULL, 0);
6648 error = got_error_from_errno("getcwd");
6651 error = got_worktree_open(&worktree, cwd);
6653 if (error->code == GOT_ERR_NOT_WORKTREE)
6654 error = wrap_not_worktree_error(error, "commit", cwd);
6658 error = got_worktree_rebase_in_progress(&rebase_in_progress, worktree);
6661 if (rebase_in_progress) {
6662 error = got_error(GOT_ERR_REBASING);
6666 error = got_worktree_histedit_in_progress(&histedit_in_progress,
6671 error = get_gitconfig_path(&gitconfig_path);
6674 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
6679 error = get_author(&author, repo, worktree);
6684 * unveil(2) traverses exec(2); if an editor is used we have
6685 * to apply unveil after the log message has been written.
6687 if (logmsg == NULL || strlen(logmsg) == 0)
6688 error = get_editor(&editor);
6690 error = apply_unveil(got_repo_get_path(repo), 0,
6691 got_worktree_get_root_path(worktree));
6695 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
6699 cl_arg.editor = editor;
6700 cl_arg.cmdline_log = logmsg;
6701 cl_arg.worktree_path = got_worktree_get_root_path(worktree);
6702 cl_arg.branch_name = got_worktree_get_head_ref_name(worktree);
6703 if (!histedit_in_progress) {
6704 if (strncmp(cl_arg.branch_name, "refs/heads/", 11) != 0) {
6705 error = got_error(GOT_ERR_COMMIT_BRANCH);
6708 cl_arg.branch_name += 11;
6710 cl_arg.repo_path = got_repo_get_path(repo);
6711 error = got_worktree_commit(&id, worktree, &paths, author, NULL,
6712 allow_bad_symlinks, collect_commit_logmsg, &cl_arg,
6713 print_status, NULL, repo);
6715 if (error->code != GOT_ERR_COMMIT_MSG_EMPTY &&
6716 cl_arg.logmsg_path != NULL)
6717 preserve_logmsg = 1;
6721 error = got_object_id_str(&id_str, id);
6724 printf("Created commit %s\n", id_str);
6726 if (preserve_logmsg) {
6727 fprintf(stderr, "%s: log message preserved in %s\n",
6728 getprogname(), cl_arg.logmsg_path);
6729 } else if (cl_arg.logmsg_path && unlink(cl_arg.logmsg_path) == -1 &&
6731 error = got_error_from_errno2("unlink", cl_arg.logmsg_path);
6732 free(cl_arg.logmsg_path);
6734 got_repo_close(repo);
6736 got_worktree_close(worktree);
6739 free(gitconfig_path);
6746 usage_cherrypick(void)
6748 fprintf(stderr, "usage: %s cherrypick commit-id\n", getprogname());
6752 static const struct got_error *
6753 cmd_cherrypick(int argc, char *argv[])
6755 const struct got_error *error = NULL;
6756 struct got_worktree *worktree = NULL;
6757 struct got_repository *repo = NULL;
6758 char *cwd = NULL, *commit_id_str = NULL;
6759 struct got_object_id *commit_id = NULL;
6760 struct got_commit_object *commit = NULL;
6761 struct got_object_qid *pid;
6762 struct got_reference *head_ref = NULL;
6764 struct got_update_progress_arg upa;
6766 while ((ch = getopt(argc, argv, "")) != -1) {
6778 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
6779 "unveil", NULL) == -1)
6785 cwd = getcwd(NULL, 0);
6787 error = got_error_from_errno("getcwd");
6790 error = got_worktree_open(&worktree, cwd);
6792 if (error->code == GOT_ERR_NOT_WORKTREE)
6793 error = wrap_not_worktree_error(error, "cherrypick",
6798 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
6803 error = apply_unveil(got_repo_get_path(repo), 0,
6804 got_worktree_get_root_path(worktree));
6808 error = got_repo_match_object_id_prefix(&commit_id, argv[0],
6809 GOT_OBJ_TYPE_COMMIT, repo);
6810 if (error != NULL) {
6811 struct got_reference *ref;
6812 if (error->code != GOT_ERR_BAD_OBJ_ID_STR)
6814 error = got_ref_open(&ref, repo, argv[0], 0);
6817 error = got_ref_resolve(&commit_id, repo, ref);
6822 error = got_object_id_str(&commit_id_str, commit_id);
6826 error = got_ref_open(&head_ref, repo,
6827 got_worktree_get_head_ref_name(worktree), 0);
6831 error = check_same_branch(commit_id, head_ref, NULL, repo);
6833 if (error->code != GOT_ERR_ANCESTRY)
6837 error = got_error(GOT_ERR_SAME_BRANCH);
6841 error = got_object_open_as_commit(&commit, repo, commit_id);
6844 pid = SIMPLEQ_FIRST(got_object_commit_get_parent_ids(commit));
6845 memset(&upa, 0, sizeof(upa));
6846 error = got_worktree_merge_files(worktree, pid ? pid->id : NULL,
6847 commit_id, repo, update_progress, &upa, check_cancelled,
6852 if (upa.did_something)
6853 printf("Merged commit %s\n", commit_id_str);
6854 print_update_progress_stats(&upa);
6857 got_object_commit_close(commit);
6858 free(commit_id_str);
6860 got_ref_close(head_ref);
6862 got_worktree_close(worktree);
6864 got_repo_close(repo);
6871 fprintf(stderr, "usage: %s backout commit-id\n", getprogname());
6875 static const struct got_error *
6876 cmd_backout(int argc, char *argv[])
6878 const struct got_error *error = NULL;
6879 struct got_worktree *worktree = NULL;
6880 struct got_repository *repo = NULL;
6881 char *cwd = NULL, *commit_id_str = NULL;
6882 struct got_object_id *commit_id = NULL;
6883 struct got_commit_object *commit = NULL;
6884 struct got_object_qid *pid;
6885 struct got_reference *head_ref = NULL;
6887 struct got_update_progress_arg upa;
6889 while ((ch = getopt(argc, argv, "")) != -1) {
6901 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
6902 "unveil", NULL) == -1)
6908 cwd = getcwd(NULL, 0);
6910 error = got_error_from_errno("getcwd");
6913 error = got_worktree_open(&worktree, cwd);
6915 if (error->code == GOT_ERR_NOT_WORKTREE)
6916 error = wrap_not_worktree_error(error, "backout", cwd);
6920 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
6925 error = apply_unveil(got_repo_get_path(repo), 0,
6926 got_worktree_get_root_path(worktree));
6930 error = got_repo_match_object_id_prefix(&commit_id, argv[0],
6931 GOT_OBJ_TYPE_COMMIT, repo);
6932 if (error != NULL) {
6933 struct got_reference *ref;
6934 if (error->code != GOT_ERR_BAD_OBJ_ID_STR)
6936 error = got_ref_open(&ref, repo, argv[0], 0);
6939 error = got_ref_resolve(&commit_id, repo, ref);
6944 error = got_object_id_str(&commit_id_str, commit_id);
6948 error = got_ref_open(&head_ref, repo,
6949 got_worktree_get_head_ref_name(worktree), 0);
6953 error = check_same_branch(commit_id, head_ref, NULL, repo);
6957 error = got_object_open_as_commit(&commit, repo, commit_id);
6960 pid = SIMPLEQ_FIRST(got_object_commit_get_parent_ids(commit));
6962 error = got_error(GOT_ERR_ROOT_COMMIT);
6966 memset(&upa, 0, sizeof(upa));
6967 error = got_worktree_merge_files(worktree, commit_id, pid->id, repo,
6968 update_progress, &upa, check_cancelled, NULL);
6972 if (upa.did_something)
6973 printf("Backed out commit %s\n", commit_id_str);
6974 print_update_progress_stats(&upa);
6977 got_object_commit_close(commit);
6978 free(commit_id_str);
6980 got_ref_close(head_ref);
6982 got_worktree_close(worktree);
6984 got_repo_close(repo);
6991 fprintf(stderr, "usage: %s rebase [-a] | [-c] | branch\n",
6997 trim_logmsg(char *logmsg, int limit)
7002 len = strlen(logmsg);
7006 nl = strchr(logmsg, '\n');
7011 static const struct got_error *
7012 get_short_logmsg(char **logmsg, int limit, struct got_commit_object *commit)
7014 const struct got_error *err;
7015 char *logmsg0 = NULL;
7018 err = got_object_commit_get_logmsg(&logmsg0, commit);
7023 while (isspace((unsigned char)s[0]))
7026 *logmsg = strdup(s);
7027 if (*logmsg == NULL) {
7028 err = got_error_from_errno("strdup");
7032 trim_logmsg(*logmsg, limit);
7038 static const struct got_error *
7039 show_rebase_merge_conflict(struct got_object_id *id, struct got_repository *repo)
7041 const struct got_error *err;
7042 struct got_commit_object *commit = NULL;
7043 char *id_str = NULL, *logmsg = NULL;
7045 err = got_object_open_as_commit(&commit, repo, id);
7049 err = got_object_id_str(&id_str, id);
7055 err = get_short_logmsg(&logmsg, 42, commit);
7059 printf("%s -> merge conflict: %s\n", id_str, logmsg);
7062 got_object_commit_close(commit);
7067 static const struct got_error *
7068 show_rebase_progress(struct got_commit_object *commit,
7069 struct got_object_id *old_id, struct got_object_id *new_id)
7071 const struct got_error *err;
7072 char *old_id_str = NULL, *new_id_str = NULL, *logmsg = NULL;
7074 err = got_object_id_str(&old_id_str, old_id);
7079 err = got_object_id_str(&new_id_str, new_id);
7084 old_id_str[12] = '\0';
7086 new_id_str[12] = '\0';
7088 err = get_short_logmsg(&logmsg, 42, commit);
7092 printf("%s -> %s: %s\n", old_id_str,
7093 new_id_str ? new_id_str : "no-op change", logmsg);
7101 static const struct got_error *
7102 rebase_complete(struct got_worktree *worktree, struct got_fileindex *fileindex,
7103 struct got_reference *branch, struct got_reference *new_base_branch,
7104 struct got_reference *tmp_branch, struct got_repository *repo)
7106 printf("Switching work tree to %s\n", got_ref_get_name(branch));
7107 return got_worktree_rebase_complete(worktree, fileindex,
7108 new_base_branch, tmp_branch, branch, repo);
7111 static const struct got_error *
7112 rebase_commit(struct got_pathlist_head *merged_paths,
7113 struct got_worktree *worktree, struct got_fileindex *fileindex,
7114 struct got_reference *tmp_branch,
7115 struct got_object_id *commit_id, struct got_repository *repo)
7117 const struct got_error *error;
7118 struct got_commit_object *commit;
7119 struct got_object_id *new_commit_id;
7121 error = got_object_open_as_commit(&commit, repo, commit_id);
7125 error = got_worktree_rebase_commit(&new_commit_id, merged_paths,
7126 worktree, fileindex, tmp_branch, commit, commit_id, repo);
7128 if (error->code != GOT_ERR_COMMIT_NO_CHANGES)
7130 error = show_rebase_progress(commit, commit_id, NULL);
7132 error = show_rebase_progress(commit, commit_id, new_commit_id);
7133 free(new_commit_id);
7136 got_object_commit_close(commit);
7140 struct check_path_prefix_arg {
7141 const char *path_prefix;
7146 static const struct got_error *
7147 check_path_prefix_in_diff(void *arg, struct got_blob_object *blob1,
7148 struct got_blob_object *blob2, struct got_object_id *id1,
7149 struct got_object_id *id2, const char *path1, const char *path2,
7150 mode_t mode1, mode_t mode2, struct got_repository *repo)
7152 struct check_path_prefix_arg *a = arg;
7154 if ((path1 && !got_path_is_child(path1, a->path_prefix, a->len)) ||
7155 (path2 && !got_path_is_child(path2, a->path_prefix, a->len)))
7156 return got_error(a->errcode);
7161 static const struct got_error *
7162 check_path_prefix(struct got_object_id *parent_id,
7163 struct got_object_id *commit_id, const char *path_prefix,
7164 int errcode, struct got_repository *repo)
7166 const struct got_error *err;
7167 struct got_tree_object *tree1 = NULL, *tree2 = NULL;
7168 struct got_commit_object *commit = NULL, *parent_commit = NULL;
7169 struct check_path_prefix_arg cpp_arg;
7171 if (got_path_is_root_dir(path_prefix))
7174 err = got_object_open_as_commit(&commit, repo, commit_id);
7178 err = got_object_open_as_commit(&parent_commit, repo, parent_id);
7182 err = got_object_open_as_tree(&tree1, repo,
7183 got_object_commit_get_tree_id(parent_commit));
7187 err = got_object_open_as_tree(&tree2, repo,
7188 got_object_commit_get_tree_id(commit));
7192 cpp_arg.path_prefix = path_prefix;
7193 while (cpp_arg.path_prefix[0] == '/')
7194 cpp_arg.path_prefix++;
7195 cpp_arg.len = strlen(cpp_arg.path_prefix);
7196 cpp_arg.errcode = errcode;
7197 err = got_diff_tree(tree1, tree2, "", "", repo,
7198 check_path_prefix_in_diff, &cpp_arg, 0);
7201 got_object_tree_close(tree1);
7203 got_object_tree_close(tree2);
7205 got_object_commit_close(commit);
7207 got_object_commit_close(parent_commit);
7211 static const struct got_error *
7212 collect_commits(struct got_object_id_queue *commits,
7213 struct got_object_id *initial_commit_id,
7214 struct got_object_id *iter_start_id, struct got_object_id *iter_stop_id,
7215 const char *path_prefix, int path_prefix_errcode,
7216 struct got_repository *repo)
7218 const struct got_error *err = NULL;
7219 struct got_commit_graph *graph = NULL;
7220 struct got_object_id *parent_id = NULL;
7221 struct got_object_qid *qid;
7222 struct got_object_id *commit_id = initial_commit_id;
7224 err = got_commit_graph_open(&graph, "/", 1);
7228 err = got_commit_graph_iter_start(graph, iter_start_id, repo,
7229 check_cancelled, NULL);
7232 while (got_object_id_cmp(commit_id, iter_stop_id) != 0) {
7233 err = got_commit_graph_iter_next(&parent_id, graph, repo,
7234 check_cancelled, NULL);
7236 if (err->code == GOT_ERR_ITER_COMPLETED) {
7237 err = got_error_msg(GOT_ERR_ANCESTRY,
7238 "ran out of commits to rebase before "
7239 "youngest common ancestor commit has "
7244 err = check_path_prefix(parent_id, commit_id,
7245 path_prefix, path_prefix_errcode, repo);
7249 err = got_object_qid_alloc(&qid, commit_id);
7252 SIMPLEQ_INSERT_HEAD(commits, qid, entry);
7253 commit_id = parent_id;
7257 got_commit_graph_close(graph);
7261 static const struct got_error *
7262 cmd_rebase(int argc, char *argv[])
7264 const struct got_error *error = NULL;
7265 struct got_worktree *worktree = NULL;
7266 struct got_repository *repo = NULL;
7267 struct got_fileindex *fileindex = NULL;
7269 struct got_reference *branch = NULL;
7270 struct got_reference *new_base_branch = NULL, *tmp_branch = NULL;
7271 struct got_object_id *commit_id = NULL, *parent_id = NULL;
7272 struct got_object_id *resume_commit_id = NULL;
7273 struct got_object_id *branch_head_commit_id = NULL, *yca_id = NULL;
7274 struct got_commit_object *commit = NULL;
7275 int ch, rebase_in_progress = 0, abort_rebase = 0, continue_rebase = 0;
7276 int histedit_in_progress = 0;
7277 unsigned char rebase_status = GOT_STATUS_NO_CHANGE;
7278 struct got_object_id_queue commits;
7279 struct got_pathlist_head merged_paths;
7280 const struct got_object_id_queue *parent_ids;
7281 struct got_object_qid *qid, *pid;
7283 SIMPLEQ_INIT(&commits);
7284 TAILQ_INIT(&merged_paths);
7286 while ((ch = getopt(argc, argv, "ac")) != -1) {
7292 continue_rebase = 1;
7304 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
7305 "unveil", NULL) == -1)
7308 if (abort_rebase && continue_rebase)
7310 else if (abort_rebase || continue_rebase) {
7313 } else if (argc != 1)
7316 cwd = getcwd(NULL, 0);
7318 error = got_error_from_errno("getcwd");
7321 error = got_worktree_open(&worktree, cwd);
7323 if (error->code == GOT_ERR_NOT_WORKTREE)
7324 error = wrap_not_worktree_error(error, "rebase", cwd);
7328 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
7333 error = apply_unveil(got_repo_get_path(repo), 0,
7334 got_worktree_get_root_path(worktree));
7338 error = got_worktree_histedit_in_progress(&histedit_in_progress,
7342 if (histedit_in_progress) {
7343 error = got_error(GOT_ERR_HISTEDIT_BUSY);
7347 error = got_worktree_rebase_in_progress(&rebase_in_progress, worktree);
7352 struct got_update_progress_arg upa;
7353 if (!rebase_in_progress) {
7354 error = got_error(GOT_ERR_NOT_REBASING);
7357 error = got_worktree_rebase_continue(&resume_commit_id,
7358 &new_base_branch, &tmp_branch, &branch, &fileindex,
7362 printf("Switching work tree to %s\n",
7363 got_ref_get_symref_target(new_base_branch));
7364 memset(&upa, 0, sizeof(upa));
7365 error = got_worktree_rebase_abort(worktree, fileindex, repo,
7366 new_base_branch, update_progress, &upa);
7369 printf("Rebase of %s aborted\n", got_ref_get_name(branch));
7370 print_update_progress_stats(&upa);
7371 goto done; /* nothing else to do */
7374 if (continue_rebase) {
7375 if (!rebase_in_progress) {
7376 error = got_error(GOT_ERR_NOT_REBASING);
7379 error = got_worktree_rebase_continue(&resume_commit_id,
7380 &new_base_branch, &tmp_branch, &branch, &fileindex,
7385 error = rebase_commit(NULL, worktree, fileindex, tmp_branch,
7386 resume_commit_id, repo);
7390 yca_id = got_object_id_dup(resume_commit_id);
7391 if (yca_id == NULL) {
7392 error = got_error_from_errno("got_object_id_dup");
7396 error = got_ref_open(&branch, repo, argv[0], 0);
7401 error = got_ref_resolve(&branch_head_commit_id, repo, branch);
7405 if (!continue_rebase) {
7406 struct got_object_id *base_commit_id;
7408 base_commit_id = got_worktree_get_base_commit_id(worktree);
7409 error = got_commit_graph_find_youngest_common_ancestor(&yca_id,
7410 base_commit_id, branch_head_commit_id, repo,
7411 check_cancelled, NULL);
7414 if (yca_id == NULL) {
7415 error = got_error_msg(GOT_ERR_ANCESTRY,
7416 "specified branch shares no common ancestry "
7417 "with work tree's branch");
7421 error = check_same_branch(base_commit_id, branch, yca_id, repo);
7423 if (error->code != GOT_ERR_ANCESTRY)
7427 error = got_error_msg(GOT_ERR_SAME_BRANCH,
7428 "specified branch resolves to a commit which "
7429 "is already contained in work tree's branch");
7432 error = got_worktree_rebase_prepare(&new_base_branch,
7433 &tmp_branch, &fileindex, worktree, branch, repo);
7438 commit_id = branch_head_commit_id;
7439 error = got_object_open_as_commit(&commit, repo, commit_id);
7443 parent_ids = got_object_commit_get_parent_ids(commit);
7444 pid = SIMPLEQ_FIRST(parent_ids);
7446 if (!continue_rebase) {
7447 struct got_update_progress_arg upa;
7448 memset(&upa, 0, sizeof(upa));
7449 error = got_worktree_rebase_abort(worktree, fileindex,
7450 repo, new_base_branch, update_progress, &upa);
7453 printf("Rebase of %s aborted\n",
7454 got_ref_get_name(branch));
7455 print_update_progress_stats(&upa);
7458 error = got_error(GOT_ERR_EMPTY_REBASE);
7461 error = collect_commits(&commits, commit_id, pid->id,
7462 yca_id, got_worktree_get_path_prefix(worktree),
7463 GOT_ERR_REBASE_PATH, repo);
7464 got_object_commit_close(commit);
7469 if (SIMPLEQ_EMPTY(&commits)) {
7470 if (continue_rebase) {
7471 error = rebase_complete(worktree, fileindex,
7472 branch, new_base_branch, tmp_branch, repo);
7475 /* Fast-forward the reference of the branch. */
7476 struct got_object_id *new_head_commit_id;
7478 error = got_ref_resolve(&new_head_commit_id, repo,
7482 error = got_object_id_str(&id_str, new_head_commit_id);
7483 printf("Forwarding %s to commit %s\n",
7484 got_ref_get_name(branch), id_str);
7486 error = got_ref_change_ref(branch,
7487 new_head_commit_id);
7494 SIMPLEQ_FOREACH(qid, &commits, entry) {
7495 struct got_update_progress_arg upa;
7497 commit_id = qid->id;
7498 parent_id = pid ? pid->id : yca_id;
7501 memset(&upa, 0, sizeof(upa));
7502 error = got_worktree_rebase_merge_files(&merged_paths,
7503 worktree, fileindex, parent_id, commit_id, repo,
7504 update_progress, &upa, check_cancelled, NULL);
7508 print_update_progress_stats(&upa);
7509 if (upa.conflicts > 0)
7510 rebase_status = GOT_STATUS_CONFLICT;
7512 if (rebase_status == GOT_STATUS_CONFLICT) {
7513 error = show_rebase_merge_conflict(qid->id, repo);
7516 got_worktree_rebase_pathlist_free(&merged_paths);
7520 error = rebase_commit(&merged_paths, worktree, fileindex,
7521 tmp_branch, commit_id, repo);
7522 got_worktree_rebase_pathlist_free(&merged_paths);
7527 if (rebase_status == GOT_STATUS_CONFLICT) {
7528 error = got_worktree_rebase_postpone(worktree, fileindex);
7531 error = got_error_msg(GOT_ERR_CONFLICTS,
7532 "conflicts must be resolved before rebasing can continue");
7534 error = rebase_complete(worktree, fileindex, branch,
7535 new_base_branch, tmp_branch, repo);
7537 got_object_id_queue_free(&commits);
7538 free(branch_head_commit_id);
7539 free(resume_commit_id);
7542 got_object_commit_close(commit);
7544 got_ref_close(branch);
7545 if (new_base_branch)
7546 got_ref_close(new_base_branch);
7548 got_ref_close(tmp_branch);
7550 got_worktree_close(worktree);
7552 got_repo_close(repo);
7557 usage_histedit(void)
7559 fprintf(stderr, "usage: %s histedit [-a] [-c] [-F histedit-script] [-m]\n",
7564 #define GOT_HISTEDIT_PICK 'p'
7565 #define GOT_HISTEDIT_EDIT 'e'
7566 #define GOT_HISTEDIT_FOLD 'f'
7567 #define GOT_HISTEDIT_DROP 'd'
7568 #define GOT_HISTEDIT_MESG 'm'
7570 static struct got_histedit_cmd {
7574 } got_histedit_cmds[] = {
7575 { GOT_HISTEDIT_PICK, "pick", "use commit" },
7576 { GOT_HISTEDIT_EDIT, "edit", "use commit but stop for amending" },
7577 { GOT_HISTEDIT_FOLD, "fold", "combine with next commit that will "
7579 { GOT_HISTEDIT_DROP, "drop", "remove commit from history" },
7580 { GOT_HISTEDIT_MESG, "mesg",
7581 "single-line log message for commit above (open editor if empty)" },
7584 struct got_histedit_list_entry {
7585 TAILQ_ENTRY(got_histedit_list_entry) entry;
7586 struct got_object_id *commit_id;
7587 const struct got_histedit_cmd *cmd;
7590 TAILQ_HEAD(got_histedit_list, got_histedit_list_entry);
7592 static const struct got_error *
7593 histedit_write_commit(struct got_object_id *commit_id, const char *cmdname,
7594 FILE *f, struct got_repository *repo)
7596 const struct got_error *err = NULL;
7597 char *logmsg = NULL, *id_str = NULL;
7598 struct got_commit_object *commit = NULL;
7601 err = got_object_open_as_commit(&commit, repo, commit_id);
7605 err = get_short_logmsg(&logmsg, 34, commit);
7609 err = got_object_id_str(&id_str, commit_id);
7613 n = fprintf(f, "%s %s %s\n", cmdname, id_str, logmsg);
7615 err = got_ferror(f, GOT_ERR_IO);
7618 got_object_commit_close(commit);
7624 static const struct got_error *
7625 histedit_write_commit_list(struct got_object_id_queue *commits,
7626 FILE *f, int edit_logmsg_only, struct got_repository *repo)
7628 const struct got_error *err = NULL;
7629 struct got_object_qid *qid;
7631 if (SIMPLEQ_EMPTY(commits))
7632 return got_error(GOT_ERR_EMPTY_HISTEDIT);
7634 SIMPLEQ_FOREACH(qid, commits, entry) {
7635 err = histedit_write_commit(qid->id, got_histedit_cmds[0].name,
7639 if (edit_logmsg_only) {
7640 int n = fprintf(f, "%c\n", GOT_HISTEDIT_MESG);
7642 err = got_ferror(f, GOT_ERR_IO);
7651 static const struct got_error *
7652 write_cmd_list(FILE *f, const char *branch_name,
7653 struct got_object_id_queue *commits)
7655 const struct got_error *err = NULL;
7658 struct got_object_qid *qid;
7660 qid = SIMPLEQ_FIRST(commits);
7661 err = got_object_id_str(&id_str, qid->id);
7666 "# Editing the history of branch '%s' starting at\n"
7668 "# Commits will be processed in order from top to "
7669 "bottom of this file.\n", branch_name, id_str);
7671 err = got_ferror(f, GOT_ERR_IO);
7675 n = fprintf(f, "# Available histedit commands:\n");
7677 err = got_ferror(f, GOT_ERR_IO);
7681 for (i = 0; i < nitems(got_histedit_cmds); i++) {
7682 struct got_histedit_cmd *cmd = &got_histedit_cmds[i];
7683 n = fprintf(f, "# %s (%c): %s\n", cmd->name, cmd->code,
7686 err = got_ferror(f, GOT_ERR_IO);
7695 static const struct got_error *
7696 histedit_syntax_error(int lineno)
7698 static char msg[42];
7701 ret = snprintf(msg, sizeof(msg), "histedit syntax error on line %d",
7703 if (ret == -1 || ret >= sizeof(msg))
7704 return got_error(GOT_ERR_HISTEDIT_SYNTAX);
7706 return got_error_msg(GOT_ERR_HISTEDIT_SYNTAX, msg);
7709 static const struct got_error *
7710 append_folded_commit_msg(char **new_msg, struct got_histedit_list_entry *hle,
7711 char *logmsg, struct got_repository *repo)
7713 const struct got_error *err;
7714 struct got_commit_object *folded_commit = NULL;
7715 char *id_str, *folded_logmsg = NULL;
7717 err = got_object_id_str(&id_str, hle->commit_id);
7721 err = got_object_open_as_commit(&folded_commit, repo, hle->commit_id);
7725 err = got_object_commit_get_logmsg(&folded_logmsg, folded_commit);
7728 if (asprintf(new_msg, "%s%s# log message of folded commit %s: %s",
7729 logmsg ? logmsg : "", logmsg ? "\n" : "", id_str,
7730 folded_logmsg) == -1) {
7731 err = got_error_from_errno("asprintf");
7735 got_object_commit_close(folded_commit);
7737 free(folded_logmsg);
7741 static struct got_histedit_list_entry *
7742 get_folded_commits(struct got_histedit_list_entry *hle)
7744 struct got_histedit_list_entry *prev, *folded = NULL;
7746 prev = TAILQ_PREV(hle, got_histedit_list, entry);
7747 while (prev && (prev->cmd->code == GOT_HISTEDIT_FOLD ||
7748 prev->cmd->code == GOT_HISTEDIT_DROP)) {
7749 if (prev->cmd->code == GOT_HISTEDIT_FOLD)
7751 prev = TAILQ_PREV(prev, got_histedit_list, entry);
7757 static const struct got_error *
7758 histedit_edit_logmsg(struct got_histedit_list_entry *hle,
7759 struct got_repository *repo)
7761 char *logmsg_path = NULL, *id_str = NULL, *orig_logmsg = NULL;
7762 char *logmsg = NULL, *new_msg = NULL, *editor = NULL;
7763 const struct got_error *err = NULL;
7764 struct got_commit_object *commit = NULL;
7767 struct got_histedit_list_entry *folded = NULL;
7769 err = got_object_open_as_commit(&commit, repo, hle->commit_id);
7773 folded = get_folded_commits(hle);
7775 while (folded != hle) {
7776 if (folded->cmd->code == GOT_HISTEDIT_DROP) {
7777 folded = TAILQ_NEXT(folded, entry);
7780 err = append_folded_commit_msg(&new_msg, folded,
7786 folded = TAILQ_NEXT(folded, entry);
7790 err = got_object_id_str(&id_str, hle->commit_id);
7793 err = got_object_commit_get_logmsg(&orig_logmsg, commit);
7796 logmsg_len = asprintf(&new_msg,
7797 "%s\n# original log message of commit %s: %s",
7798 logmsg ? logmsg : "", id_str, orig_logmsg);
7799 if (logmsg_len == -1) {
7800 err = got_error_from_errno("asprintf");
7806 err = got_object_id_str(&id_str, hle->commit_id);
7810 err = got_opentemp_named_fd(&logmsg_path, &fd,
7811 GOT_TMPDIR_STR "/got-logmsg");
7815 write(fd, logmsg, logmsg_len);
7818 err = get_editor(&editor);
7822 err = edit_logmsg(&hle->logmsg, editor, logmsg_path, logmsg);
7824 if (err->code != GOT_ERR_COMMIT_MSG_EMPTY)
7826 err = got_object_commit_get_logmsg(&hle->logmsg, commit);
7829 if (logmsg_path && unlink(logmsg_path) != 0 && err == NULL)
7830 err = got_error_from_errno2("unlink", logmsg_path);
7836 got_object_commit_close(commit);
7840 static const struct got_error *
7841 histedit_parse_list(struct got_histedit_list *histedit_cmds,
7842 FILE *f, struct got_repository *repo)
7844 const struct got_error *err = NULL;
7845 char *line = NULL, *p, *end;
7849 const struct got_histedit_cmd *cmd;
7850 struct got_object_id *commit_id = NULL;
7851 struct got_histedit_list_entry *hle = NULL;
7854 len = getline(&line, &size, f);
7856 const struct got_error *getline_err;
7859 getline_err = got_error_from_errno("getline");
7860 err = got_ferror(f, getline_err->code);
7865 while (isspace((unsigned char)p[0]))
7867 if (p[0] == '#' || p[0] == '\0') {
7873 for (i = 0; i < nitems(got_histedit_cmds); i++) {
7874 cmd = &got_histedit_cmds[i];
7875 if (strncmp(cmd->name, p, strlen(cmd->name)) == 0 &&
7876 isspace((unsigned char)p[strlen(cmd->name)])) {
7877 p += strlen(cmd->name);
7880 if (p[0] == cmd->code && isspace((unsigned char)p[1])) {
7885 if (i == nitems(got_histedit_cmds)) {
7886 err = histedit_syntax_error(lineno);
7889 while (isspace((unsigned char)p[0]))
7891 if (cmd->code == GOT_HISTEDIT_MESG) {
7892 if (hle == NULL || hle->logmsg != NULL) {
7893 err = got_error(GOT_ERR_HISTEDIT_CMD);
7897 err = histedit_edit_logmsg(hle, repo);
7901 hle->logmsg = strdup(p);
7902 if (hle->logmsg == NULL) {
7903 err = got_error_from_errno("strdup");
7912 while (end[0] && !isspace((unsigned char)end[0]))
7916 err = got_object_resolve_id_str(&commit_id, repo, p);
7918 /* override error code */
7919 err = histedit_syntax_error(lineno);
7923 hle = malloc(sizeof(*hle));
7925 err = got_error_from_errno("malloc");
7929 hle->commit_id = commit_id;
7934 TAILQ_INSERT_TAIL(histedit_cmds, hle, entry);
7942 static const struct got_error *
7943 histedit_check_script(struct got_histedit_list *histedit_cmds,
7944 struct got_object_id_queue *commits, struct got_repository *repo)
7946 const struct got_error *err = NULL;
7947 struct got_object_qid *qid;
7948 struct got_histedit_list_entry *hle;
7949 static char msg[92];
7952 if (TAILQ_EMPTY(histedit_cmds))
7953 return got_error_msg(GOT_ERR_EMPTY_HISTEDIT,
7954 "histedit script contains no commands");
7955 if (SIMPLEQ_EMPTY(commits))
7956 return got_error(GOT_ERR_EMPTY_HISTEDIT);
7958 TAILQ_FOREACH(hle, histedit_cmds, entry) {
7959 struct got_histedit_list_entry *hle2;
7960 TAILQ_FOREACH(hle2, histedit_cmds, entry) {
7963 if (got_object_id_cmp(hle->commit_id,
7964 hle2->commit_id) != 0)
7966 err = got_object_id_str(&id_str, hle->commit_id);
7969 snprintf(msg, sizeof(msg), "commit %s is listed "
7970 "more than once in histedit script", id_str);
7972 return got_error_msg(GOT_ERR_HISTEDIT_CMD, msg);
7976 SIMPLEQ_FOREACH(qid, commits, entry) {
7977 TAILQ_FOREACH(hle, histedit_cmds, entry) {
7978 if (got_object_id_cmp(qid->id, hle->commit_id) == 0)
7982 err = got_object_id_str(&id_str, qid->id);
7985 snprintf(msg, sizeof(msg),
7986 "commit %s missing from histedit script", id_str);
7988 return got_error_msg(GOT_ERR_HISTEDIT_CMD, msg);
7992 hle = TAILQ_LAST(histedit_cmds, got_histedit_list);
7993 if (hle && hle->cmd->code == GOT_HISTEDIT_FOLD)
7994 return got_error_msg(GOT_ERR_HISTEDIT_CMD,
7995 "last commit in histedit script cannot be folded");
8000 static const struct got_error *
8001 histedit_run_editor(struct got_histedit_list *histedit_cmds,
8002 const char *path, struct got_object_id_queue *commits,
8003 struct got_repository *repo)
8005 const struct got_error *err = NULL;
8009 err = get_editor(&editor);
8013 if (spawn_editor(editor, path) == -1) {
8014 err = got_error_from_errno("failed spawning editor");
8018 f = fopen(path, "r");
8020 err = got_error_from_errno("fopen");
8023 err = histedit_parse_list(histedit_cmds, f, repo);
8027 err = histedit_check_script(histedit_cmds, commits, repo);
8029 if (f && fclose(f) != 0 && err == NULL)
8030 err = got_error_from_errno("fclose");
8035 static const struct got_error *
8036 histedit_edit_list_retry(struct got_histedit_list *, const struct got_error *,
8037 struct got_object_id_queue *, const char *, const char *,
8038 struct got_repository *);
8040 static const struct got_error *
8041 histedit_edit_script(struct got_histedit_list *histedit_cmds,
8042 struct got_object_id_queue *commits, const char *branch_name,
8043 int edit_logmsg_only, struct got_repository *repo)
8045 const struct got_error *err;
8049 err = got_opentemp_named(&path, &f, "got-histedit");
8053 err = write_cmd_list(f, branch_name, commits);
8057 err = histedit_write_commit_list(commits, f, edit_logmsg_only, repo);
8061 if (edit_logmsg_only) {
8063 err = histedit_parse_list(histedit_cmds, f, repo);
8065 if (fclose(f) != 0) {
8066 err = got_error_from_errno("fclose");
8070 err = histedit_run_editor(histedit_cmds, path, commits, repo);
8072 if (err->code != GOT_ERR_HISTEDIT_SYNTAX &&
8073 err->code != GOT_ERR_HISTEDIT_CMD)
8075 err = histedit_edit_list_retry(histedit_cmds, err,
8076 commits, path, branch_name, repo);
8080 if (f && fclose(f) != 0 && err == NULL)
8081 err = got_error_from_errno("fclose");
8082 if (path && unlink(path) != 0 && err == NULL)
8083 err = got_error_from_errno2("unlink", path);
8088 static const struct got_error *
8089 histedit_save_list(struct got_histedit_list *histedit_cmds,
8090 struct got_worktree *worktree, struct got_repository *repo)
8092 const struct got_error *err = NULL;
8095 struct got_histedit_list_entry *hle;
8096 struct got_commit_object *commit = NULL;
8098 err = got_worktree_get_histedit_script_path(&path, worktree);
8102 f = fopen(path, "w");
8104 err = got_error_from_errno2("fopen", path);
8107 TAILQ_FOREACH(hle, histedit_cmds, entry) {
8108 err = histedit_write_commit(hle->commit_id, hle->cmd->name, f,
8114 int n = fprintf(f, "%c %s\n",
8115 GOT_HISTEDIT_MESG, hle->logmsg);
8117 err = got_ferror(f, GOT_ERR_IO);
8123 if (f && fclose(f) != 0 && err == NULL)
8124 err = got_error_from_errno("fclose");
8127 got_object_commit_close(commit);
8132 histedit_free_list(struct got_histedit_list *histedit_cmds)
8134 struct got_histedit_list_entry *hle;
8136 while ((hle = TAILQ_FIRST(histedit_cmds))) {
8137 TAILQ_REMOVE(histedit_cmds, hle, entry);
8142 static const struct got_error *
8143 histedit_load_list(struct got_histedit_list *histedit_cmds,
8144 const char *path, struct got_repository *repo)
8146 const struct got_error *err = NULL;
8149 f = fopen(path, "r");
8151 err = got_error_from_errno2("fopen", path);
8155 err = histedit_parse_list(histedit_cmds, f, repo);
8157 if (f && fclose(f) != 0 && err == NULL)
8158 err = got_error_from_errno("fclose");
8162 static const struct got_error *
8163 histedit_edit_list_retry(struct got_histedit_list *histedit_cmds,
8164 const struct got_error *edit_err, struct got_object_id_queue *commits,
8165 const char *path, const char *branch_name, struct got_repository *repo)
8167 const struct got_error *err = NULL, *prev_err = edit_err;
8170 while (resp != 'c' && resp != 'r' && resp != 'a') {
8171 printf("%s: %s\n(c)ontinue editing, (r)estart editing, "
8172 "or (a)bort: ", getprogname(), prev_err->msg);
8177 histedit_free_list(histedit_cmds);
8178 err = histedit_run_editor(histedit_cmds, path, commits,
8181 if (err->code != GOT_ERR_HISTEDIT_SYNTAX &&
8182 err->code != GOT_ERR_HISTEDIT_CMD)
8189 } else if (resp == 'r') {
8190 histedit_free_list(histedit_cmds);
8191 err = histedit_edit_script(histedit_cmds,
8192 commits, branch_name, 0, repo);
8194 if (err->code != GOT_ERR_HISTEDIT_SYNTAX &&
8195 err->code != GOT_ERR_HISTEDIT_CMD)
8202 } else if (resp == 'a') {
8203 err = got_error(GOT_ERR_HISTEDIT_CANCEL);
8206 printf("invalid response '%c'\n", resp);
8212 static const struct got_error *
8213 histedit_complete(struct got_worktree *worktree,
8214 struct got_fileindex *fileindex, struct got_reference *tmp_branch,
8215 struct got_reference *branch, struct got_repository *repo)
8217 printf("Switching work tree to %s\n",
8218 got_ref_get_symref_target(branch));
8219 return got_worktree_histedit_complete(worktree, fileindex, tmp_branch,
8223 static const struct got_error *
8224 show_histedit_progress(struct got_commit_object *commit,
8225 struct got_histedit_list_entry *hle, struct got_object_id *new_id)
8227 const struct got_error *err;
8228 char *old_id_str = NULL, *new_id_str = NULL, *logmsg = NULL;
8230 err = got_object_id_str(&old_id_str, hle->commit_id);
8235 err = got_object_id_str(&new_id_str, new_id);
8240 old_id_str[12] = '\0';
8242 new_id_str[12] = '\0';
8245 logmsg = strdup(hle->logmsg);
8246 if (logmsg == NULL) {
8247 err = got_error_from_errno("strdup");
8250 trim_logmsg(logmsg, 42);
8252 err = get_short_logmsg(&logmsg, 42, commit);
8257 switch (hle->cmd->code) {
8258 case GOT_HISTEDIT_PICK:
8259 case GOT_HISTEDIT_EDIT:
8260 printf("%s -> %s: %s\n", old_id_str,
8261 new_id_str ? new_id_str : "no-op change", logmsg);
8263 case GOT_HISTEDIT_DROP:
8264 case GOT_HISTEDIT_FOLD:
8265 printf("%s -> %s commit: %s\n", old_id_str, hle->cmd->name,
8277 static const struct got_error *
8278 histedit_commit(struct got_pathlist_head *merged_paths,
8279 struct got_worktree *worktree, struct got_fileindex *fileindex,
8280 struct got_reference *tmp_branch, struct got_histedit_list_entry *hle,
8281 struct got_repository *repo)
8283 const struct got_error *err;
8284 struct got_commit_object *commit;
8285 struct got_object_id *new_commit_id;
8287 if ((hle->cmd->code == GOT_HISTEDIT_EDIT || get_folded_commits(hle))
8288 && hle->logmsg == NULL) {
8289 err = histedit_edit_logmsg(hle, repo);
8294 err = got_object_open_as_commit(&commit, repo, hle->commit_id);
8298 err = got_worktree_histedit_commit(&new_commit_id, merged_paths,
8299 worktree, fileindex, tmp_branch, commit, hle->commit_id,
8302 if (err->code != GOT_ERR_COMMIT_NO_CHANGES)
8304 err = show_histedit_progress(commit, hle, NULL);
8306 err = show_histedit_progress(commit, hle, new_commit_id);
8307 free(new_commit_id);
8310 got_object_commit_close(commit);
8314 static const struct got_error *
8315 histedit_skip_commit(struct got_histedit_list_entry *hle,
8316 struct got_worktree *worktree, struct got_repository *repo)
8318 const struct got_error *error;
8319 struct got_commit_object *commit;
8321 error = got_worktree_histedit_skip_commit(worktree, hle->commit_id,
8326 error = got_object_open_as_commit(&commit, repo, hle->commit_id);
8330 error = show_histedit_progress(commit, hle, NULL);
8331 got_object_commit_close(commit);
8335 static const struct got_error *
8336 check_local_changes(void *arg, unsigned char status,
8337 unsigned char staged_status, const char *path,
8338 struct got_object_id *blob_id, struct got_object_id *staged_blob_id,
8339 struct got_object_id *commit_id, int dirfd, const char *de_name)
8341 int *have_local_changes = arg;
8344 case GOT_STATUS_ADD:
8345 case GOT_STATUS_DELETE:
8346 case GOT_STATUS_MODIFY:
8347 case GOT_STATUS_CONFLICT:
8348 *have_local_changes = 1;
8349 return got_error(GOT_ERR_CANCELLED);
8354 switch (staged_status) {
8355 case GOT_STATUS_ADD:
8356 case GOT_STATUS_DELETE:
8357 case GOT_STATUS_MODIFY:
8358 *have_local_changes = 1;
8359 return got_error(GOT_ERR_CANCELLED);
8367 static const struct got_error *
8368 cmd_histedit(int argc, char *argv[])
8370 const struct got_error *error = NULL;
8371 struct got_worktree *worktree = NULL;
8372 struct got_fileindex *fileindex = NULL;
8373 struct got_repository *repo = NULL;
8375 struct got_reference *branch = NULL;
8376 struct got_reference *tmp_branch = NULL;
8377 struct got_object_id *resume_commit_id = NULL;
8378 struct got_object_id *base_commit_id = NULL;
8379 struct got_object_id *head_commit_id = NULL;
8380 struct got_commit_object *commit = NULL;
8381 int ch, rebase_in_progress = 0;
8382 struct got_update_progress_arg upa;
8383 int edit_in_progress = 0, abort_edit = 0, continue_edit = 0;
8384 int edit_logmsg_only = 0;
8385 const char *edit_script_path = NULL;
8386 unsigned char rebase_status = GOT_STATUS_NO_CHANGE;
8387 struct got_object_id_queue commits;
8388 struct got_pathlist_head merged_paths;
8389 const struct got_object_id_queue *parent_ids;
8390 struct got_object_qid *pid;
8391 struct got_histedit_list histedit_cmds;
8392 struct got_histedit_list_entry *hle;
8394 SIMPLEQ_INIT(&commits);
8395 TAILQ_INIT(&histedit_cmds);
8396 TAILQ_INIT(&merged_paths);
8397 memset(&upa, 0, sizeof(upa));
8399 while ((ch = getopt(argc, argv, "acF:m")) != -1) {
8408 edit_script_path = optarg;
8411 edit_logmsg_only = 1;
8423 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
8424 "unveil", NULL) == -1)
8427 if (abort_edit && continue_edit)
8428 errx(1, "histedit's -a and -c options are mutually exclusive");
8429 if (edit_script_path && edit_logmsg_only)
8430 errx(1, "histedit's -F and -m options are mutually exclusive");
8431 if (abort_edit && edit_logmsg_only)
8432 errx(1, "histedit's -a and -m options are mutually exclusive");
8433 if (continue_edit && edit_logmsg_only)
8434 errx(1, "histedit's -c and -m options are mutually exclusive");
8439 * This command cannot apply unveil(2) in all cases because the
8440 * user may choose to run an editor to edit the histedit script
8441 * and to edit individual commit log messages.
8442 * unveil(2) traverses exec(2); if an editor is used we have to
8443 * apply unveil after edit script and log messages have been written.
8444 * XXX TODO: Make use of unveil(2) where possible.
8447 cwd = getcwd(NULL, 0);
8449 error = got_error_from_errno("getcwd");
8452 error = got_worktree_open(&worktree, cwd);
8454 if (error->code == GOT_ERR_NOT_WORKTREE)
8455 error = wrap_not_worktree_error(error, "histedit", cwd);
8459 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
8464 error = got_worktree_rebase_in_progress(&rebase_in_progress, worktree);
8467 if (rebase_in_progress) {
8468 error = got_error(GOT_ERR_REBASING);
8472 error = got_worktree_histedit_in_progress(&edit_in_progress, worktree);
8476 if (edit_in_progress && edit_logmsg_only) {
8477 error = got_error_msg(GOT_ERR_HISTEDIT_BUSY,
8478 "histedit operation is in progress in this "
8479 "work tree and must be continued or aborted "
8480 "before the -m option can be used");
8484 if (edit_in_progress && abort_edit) {
8485 error = got_worktree_histedit_continue(&resume_commit_id,
8486 &tmp_branch, &branch, &base_commit_id, &fileindex,
8490 printf("Switching work tree to %s\n",
8491 got_ref_get_symref_target(branch));
8492 error = got_worktree_histedit_abort(worktree, fileindex, repo,
8493 branch, base_commit_id, update_progress, &upa);
8496 printf("Histedit of %s aborted\n",
8497 got_ref_get_symref_target(branch));
8498 print_update_progress_stats(&upa);
8499 goto done; /* nothing else to do */
8500 } else if (abort_edit) {
8501 error = got_error(GOT_ERR_NOT_HISTEDIT);
8505 if (continue_edit) {
8508 if (!edit_in_progress) {
8509 error = got_error(GOT_ERR_NOT_HISTEDIT);
8513 error = got_worktree_get_histedit_script_path(&path, worktree);
8517 error = histedit_load_list(&histedit_cmds, path, repo);
8522 error = got_worktree_histedit_continue(&resume_commit_id,
8523 &tmp_branch, &branch, &base_commit_id, &fileindex,
8528 error = got_ref_resolve(&head_commit_id, repo, branch);
8532 error = got_object_open_as_commit(&commit, repo,
8536 parent_ids = got_object_commit_get_parent_ids(commit);
8537 pid = SIMPLEQ_FIRST(parent_ids);
8539 error = got_error(GOT_ERR_EMPTY_HISTEDIT);
8542 error = collect_commits(&commits, head_commit_id, pid->id,
8543 base_commit_id, got_worktree_get_path_prefix(worktree),
8544 GOT_ERR_HISTEDIT_PATH, repo);
8545 got_object_commit_close(commit);
8550 if (edit_in_progress) {
8551 error = got_error(GOT_ERR_HISTEDIT_BUSY);
8555 error = got_ref_open(&branch, repo,
8556 got_worktree_get_head_ref_name(worktree), 0);
8560 if (strncmp(got_ref_get_name(branch), "refs/heads/", 11) != 0) {
8561 error = got_error_msg(GOT_ERR_COMMIT_BRANCH,
8562 "will not edit commit history of a branch outside "
8563 "the \"refs/heads/\" reference namespace");
8567 error = got_ref_resolve(&head_commit_id, repo, branch);
8568 got_ref_close(branch);
8573 error = got_object_open_as_commit(&commit, repo,
8577 parent_ids = got_object_commit_get_parent_ids(commit);
8578 pid = SIMPLEQ_FIRST(parent_ids);
8580 error = got_error(GOT_ERR_EMPTY_HISTEDIT);
8583 error = collect_commits(&commits, head_commit_id, pid->id,
8584 got_worktree_get_base_commit_id(worktree),
8585 got_worktree_get_path_prefix(worktree),
8586 GOT_ERR_HISTEDIT_PATH, repo);
8587 got_object_commit_close(commit);
8592 if (SIMPLEQ_EMPTY(&commits)) {
8593 error = got_error(GOT_ERR_EMPTY_HISTEDIT);
8597 error = got_worktree_histedit_prepare(&tmp_branch, &branch,
8598 &base_commit_id, &fileindex, worktree, repo);
8602 if (edit_script_path) {
8603 error = histedit_load_list(&histedit_cmds,
8604 edit_script_path, repo);
8606 got_worktree_histedit_abort(worktree, fileindex,
8607 repo, branch, base_commit_id,
8608 update_progress, &upa);
8609 print_update_progress_stats(&upa);
8613 const char *branch_name;
8614 branch_name = got_ref_get_symref_target(branch);
8615 if (strncmp(branch_name, "refs/heads/", 11) == 0)
8617 error = histedit_edit_script(&histedit_cmds, &commits,
8618 branch_name, edit_logmsg_only, repo);
8620 got_worktree_histedit_abort(worktree, fileindex,
8621 repo, branch, base_commit_id,
8622 update_progress, &upa);
8623 print_update_progress_stats(&upa);
8629 error = histedit_save_list(&histedit_cmds, worktree,
8632 got_worktree_histedit_abort(worktree, fileindex,
8633 repo, branch, base_commit_id,
8634 update_progress, &upa);
8635 print_update_progress_stats(&upa);
8641 error = histedit_check_script(&histedit_cmds, &commits, repo);
8645 TAILQ_FOREACH(hle, &histedit_cmds, entry) {
8646 if (resume_commit_id) {
8647 if (got_object_id_cmp(hle->commit_id,
8648 resume_commit_id) != 0)
8651 resume_commit_id = NULL;
8652 if (hle->cmd->code == GOT_HISTEDIT_DROP ||
8653 hle->cmd->code == GOT_HISTEDIT_FOLD) {
8654 error = histedit_skip_commit(hle, worktree,
8659 struct got_pathlist_head paths;
8660 int have_changes = 0;
8663 error = got_pathlist_append(&paths, "", NULL);
8666 error = got_worktree_status(worktree, &paths,
8667 repo, check_local_changes, &have_changes,
8668 check_cancelled, NULL);
8669 got_pathlist_free(&paths);
8671 if (error->code != GOT_ERR_CANCELLED)
8673 if (sigint_received || sigpipe_received)
8677 error = histedit_commit(NULL, worktree,
8678 fileindex, tmp_branch, hle, repo);
8682 error = got_object_open_as_commit(
8683 &commit, repo, hle->commit_id);
8686 error = show_histedit_progress(commit,
8688 got_object_commit_close(commit);
8697 if (hle->cmd->code == GOT_HISTEDIT_DROP) {
8698 error = histedit_skip_commit(hle, worktree, repo);
8704 error = got_object_open_as_commit(&commit, repo,
8708 parent_ids = got_object_commit_get_parent_ids(commit);
8709 pid = SIMPLEQ_FIRST(parent_ids);
8711 error = got_worktree_histedit_merge_files(&merged_paths,
8712 worktree, fileindex, pid->id, hle->commit_id, repo,
8713 update_progress, &upa, check_cancelled, NULL);
8716 got_object_commit_close(commit);
8719 print_update_progress_stats(&upa);
8720 if (upa.conflicts > 0)
8721 rebase_status = GOT_STATUS_CONFLICT;
8723 if (rebase_status == GOT_STATUS_CONFLICT) {
8724 error = show_rebase_merge_conflict(hle->commit_id,
8728 got_worktree_rebase_pathlist_free(&merged_paths);
8732 if (hle->cmd->code == GOT_HISTEDIT_EDIT) {
8734 error = got_object_id_str(&id_str, hle->commit_id);
8737 printf("Stopping histedit for amending commit %s\n",
8740 got_worktree_rebase_pathlist_free(&merged_paths);
8741 error = got_worktree_histedit_postpone(worktree,
8746 if (hle->cmd->code == GOT_HISTEDIT_FOLD) {
8747 error = histedit_skip_commit(hle, worktree, repo);
8753 error = histedit_commit(&merged_paths, worktree, fileindex,
8754 tmp_branch, hle, repo);
8755 got_worktree_rebase_pathlist_free(&merged_paths);
8760 if (rebase_status == GOT_STATUS_CONFLICT) {
8761 error = got_worktree_histedit_postpone(worktree, fileindex);
8764 error = got_error_msg(GOT_ERR_CONFLICTS,
8765 "conflicts must be resolved before histedit can continue");
8767 error = histedit_complete(worktree, fileindex, tmp_branch,
8770 got_object_id_queue_free(&commits);
8771 histedit_free_list(&histedit_cmds);
8772 free(head_commit_id);
8773 free(base_commit_id);
8774 free(resume_commit_id);
8776 got_object_commit_close(commit);
8778 got_ref_close(branch);
8780 got_ref_close(tmp_branch);
8782 got_worktree_close(worktree);
8784 got_repo_close(repo);
8789 usage_integrate(void)
8791 fprintf(stderr, "usage: %s integrate branch\n", getprogname());
8795 static const struct got_error *
8796 cmd_integrate(int argc, char *argv[])
8798 const struct got_error *error = NULL;
8799 struct got_repository *repo = NULL;
8800 struct got_worktree *worktree = NULL;
8801 char *cwd = NULL, *refname = NULL, *base_refname = NULL;
8802 const char *branch_arg = NULL;
8803 struct got_reference *branch_ref = NULL, *base_branch_ref = NULL;
8804 struct got_fileindex *fileindex = NULL;
8805 struct got_object_id *commit_id = NULL, *base_commit_id = NULL;
8807 struct got_update_progress_arg upa;
8809 while ((ch = getopt(argc, argv, "")) != -1) {
8822 branch_arg = argv[0];
8824 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
8825 "unveil", NULL) == -1)
8828 cwd = getcwd(NULL, 0);
8830 error = got_error_from_errno("getcwd");
8834 error = got_worktree_open(&worktree, cwd);
8836 if (error->code == GOT_ERR_NOT_WORKTREE)
8837 error = wrap_not_worktree_error(error, "integrate",
8842 error = check_rebase_or_histedit_in_progress(worktree);
8846 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
8851 error = apply_unveil(got_repo_get_path(repo), 0,
8852 got_worktree_get_root_path(worktree));
8856 if (asprintf(&refname, "refs/heads/%s", branch_arg) == -1) {
8857 error = got_error_from_errno("asprintf");
8861 error = got_worktree_integrate_prepare(&fileindex, &branch_ref,
8862 &base_branch_ref, worktree, refname, repo);
8866 refname = strdup(got_ref_get_name(branch_ref));
8867 if (refname == NULL) {
8868 error = got_error_from_errno("strdup");
8869 got_worktree_integrate_abort(worktree, fileindex, repo,
8870 branch_ref, base_branch_ref);
8873 base_refname = strdup(got_ref_get_name(base_branch_ref));
8874 if (base_refname == NULL) {
8875 error = got_error_from_errno("strdup");
8876 got_worktree_integrate_abort(worktree, fileindex, repo,
8877 branch_ref, base_branch_ref);
8881 error = got_ref_resolve(&commit_id, repo, branch_ref);
8885 error = got_ref_resolve(&base_commit_id, repo, base_branch_ref);
8889 if (got_object_id_cmp(commit_id, base_commit_id) == 0) {
8890 error = got_error_msg(GOT_ERR_SAME_BRANCH,
8891 "specified branch has already been integrated");
8892 got_worktree_integrate_abort(worktree, fileindex, repo,
8893 branch_ref, base_branch_ref);
8897 error = check_linear_ancestry(commit_id, base_commit_id, 1, repo);
8899 if (error->code == GOT_ERR_ANCESTRY)
8900 error = got_error(GOT_ERR_REBASE_REQUIRED);
8901 got_worktree_integrate_abort(worktree, fileindex, repo,
8902 branch_ref, base_branch_ref);
8906 memset(&upa, 0, sizeof(upa));
8907 error = got_worktree_integrate_continue(worktree, fileindex, repo,
8908 branch_ref, base_branch_ref, update_progress, &upa,
8909 check_cancelled, NULL);
8913 printf("Integrated %s into %s\n", refname, base_refname);
8914 print_update_progress_stats(&upa);
8917 got_repo_close(repo);
8919 got_worktree_close(worktree);
8921 free(base_commit_id);
8931 fprintf(stderr, "usage: %s stage [-l] | [-p] [-F response-script] "
8932 "[-S] [file-path ...]\n",
8937 static const struct got_error *
8938 print_stage(void *arg, unsigned char status, unsigned char staged_status,
8939 const char *path, struct got_object_id *blob_id,
8940 struct got_object_id *staged_blob_id, struct got_object_id *commit_id,
8941 int dirfd, const char *de_name)
8943 const struct got_error *err = NULL;
8944 char *id_str = NULL;
8946 if (staged_status != GOT_STATUS_ADD &&
8947 staged_status != GOT_STATUS_MODIFY &&
8948 staged_status != GOT_STATUS_DELETE)
8951 if (staged_status == GOT_STATUS_ADD ||
8952 staged_status == GOT_STATUS_MODIFY)
8953 err = got_object_id_str(&id_str, staged_blob_id);
8955 err = got_object_id_str(&id_str, blob_id);
8959 printf("%s %c %s\n", id_str, staged_status, path);
8964 static const struct got_error *
8965 cmd_stage(int argc, char *argv[])
8967 const struct got_error *error = NULL;
8968 struct got_repository *repo = NULL;
8969 struct got_worktree *worktree = NULL;
8971 struct got_pathlist_head paths;
8972 struct got_pathlist_entry *pe;
8973 int ch, list_stage = 0, pflag = 0, allow_bad_symlinks = 0;
8974 FILE *patch_script_file = NULL;
8975 const char *patch_script_path = NULL;
8976 struct choose_patch_arg cpa;
8980 while ((ch = getopt(argc, argv, "lpF:S")) != -1) {
8989 patch_script_path = optarg;
8992 allow_bad_symlinks = 1;
9004 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
9005 "unveil", NULL) == -1)
9008 if (list_stage && (pflag || patch_script_path))
9009 errx(1, "-l option cannot be used with other options");
9010 if (patch_script_path && !pflag)
9011 errx(1, "-F option can only be used together with -p option");
9013 cwd = getcwd(NULL, 0);
9015 error = got_error_from_errno("getcwd");
9019 error = got_worktree_open(&worktree, cwd);
9021 if (error->code == GOT_ERR_NOT_WORKTREE)
9022 error = wrap_not_worktree_error(error, "stage", cwd);
9026 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
9031 if (patch_script_path) {
9032 patch_script_file = fopen(patch_script_path, "r");
9033 if (patch_script_file == NULL) {
9034 error = got_error_from_errno2("fopen",
9039 error = apply_unveil(got_repo_get_path(repo), 0,
9040 got_worktree_get_root_path(worktree));
9044 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
9049 error = got_worktree_status(worktree, &paths, repo,
9050 print_stage, NULL, check_cancelled, NULL);
9052 cpa.patch_script_file = patch_script_file;
9053 cpa.action = "stage";
9054 error = got_worktree_stage(worktree, &paths,
9055 pflag ? NULL : print_status, NULL,
9056 pflag ? choose_patch : NULL, &cpa,
9057 allow_bad_symlinks, repo);
9060 if (patch_script_file && fclose(patch_script_file) == EOF &&
9062 error = got_error_from_errno2("fclose", patch_script_path);
9064 got_repo_close(repo);
9066 got_worktree_close(worktree);
9067 TAILQ_FOREACH(pe, &paths, entry)
9068 free((char *)pe->path);
9069 got_pathlist_free(&paths);
9077 fprintf(stderr, "usage: %s unstage [-p] [-F response-script] "
9078 "[file-path ...]\n",
9084 static const struct got_error *
9085 cmd_unstage(int argc, char *argv[])
9087 const struct got_error *error = NULL;
9088 struct got_repository *repo = NULL;
9089 struct got_worktree *worktree = NULL;
9091 struct got_pathlist_head paths;
9092 struct got_pathlist_entry *pe;
9094 struct got_update_progress_arg upa;
9095 FILE *patch_script_file = NULL;
9096 const char *patch_script_path = NULL;
9097 struct choose_patch_arg cpa;
9101 while ((ch = getopt(argc, argv, "pF:")) != -1) {
9107 patch_script_path = optarg;
9119 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
9120 "unveil", NULL) == -1)
9123 if (patch_script_path && !pflag)
9124 errx(1, "-F option can only be used together with -p option");
9126 cwd = getcwd(NULL, 0);
9128 error = got_error_from_errno("getcwd");
9132 error = got_worktree_open(&worktree, cwd);
9134 if (error->code == GOT_ERR_NOT_WORKTREE)
9135 error = wrap_not_worktree_error(error, "unstage", cwd);
9139 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
9144 if (patch_script_path) {
9145 patch_script_file = fopen(patch_script_path, "r");
9146 if (patch_script_file == NULL) {
9147 error = got_error_from_errno2("fopen",
9153 error = apply_unveil(got_repo_get_path(repo), 0,
9154 got_worktree_get_root_path(worktree));
9158 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
9162 cpa.patch_script_file = patch_script_file;
9163 cpa.action = "unstage";
9164 memset(&upa, 0, sizeof(upa));
9165 error = got_worktree_unstage(worktree, &paths, update_progress,
9166 &upa, pflag ? choose_patch : NULL, &cpa, repo);
9168 print_update_progress_stats(&upa);
9170 if (patch_script_file && fclose(patch_script_file) == EOF &&
9172 error = got_error_from_errno2("fclose", patch_script_path);
9174 got_repo_close(repo);
9176 got_worktree_close(worktree);
9177 TAILQ_FOREACH(pe, &paths, entry)
9178 free((char *)pe->path);
9179 got_pathlist_free(&paths);
9187 fprintf(stderr, "usage: %s cat [-r repository ] [ -c commit ] [ -P ] "
9188 "arg1 [arg2 ...]\n", getprogname());
9192 static const struct got_error *
9193 cat_blob(struct got_object_id *id, struct got_repository *repo, FILE *outfile)
9195 const struct got_error *err;
9196 struct got_blob_object *blob;
9198 err = got_object_open_as_blob(&blob, repo, id, 8192);
9202 err = got_object_blob_dump_to_file(NULL, NULL, NULL, outfile, blob);
9203 got_object_blob_close(blob);
9207 static const struct got_error *
9208 cat_tree(struct got_object_id *id, struct got_repository *repo, FILE *outfile)
9210 const struct got_error *err;
9211 struct got_tree_object *tree;
9214 err = got_object_open_as_tree(&tree, repo, id);
9218 nentries = got_object_tree_get_nentries(tree);
9219 for (i = 0; i < nentries; i++) {
9220 struct got_tree_entry *te;
9222 if (sigint_received || sigpipe_received)
9224 te = got_object_tree_get_entry(tree, i);
9225 err = got_object_id_str(&id_str, got_tree_entry_get_id(te));
9228 fprintf(outfile, "%s %.7o %s\n", id_str,
9229 got_tree_entry_get_mode(te),
9230 got_tree_entry_get_name(te));
9234 got_object_tree_close(tree);
9238 static const struct got_error *
9239 cat_commit(struct got_object_id *id, struct got_repository *repo, FILE *outfile)
9241 const struct got_error *err;
9242 struct got_commit_object *commit;
9243 const struct got_object_id_queue *parent_ids;
9244 struct got_object_qid *pid;
9245 char *id_str = NULL;
9246 const char *logmsg = NULL;
9248 err = got_object_open_as_commit(&commit, repo, id);
9252 err = got_object_id_str(&id_str, got_object_commit_get_tree_id(commit));
9256 fprintf(outfile, "%s%s\n", GOT_COMMIT_LABEL_TREE, id_str);
9257 parent_ids = got_object_commit_get_parent_ids(commit);
9258 fprintf(outfile, "numparents %d\n",
9259 got_object_commit_get_nparents(commit));
9260 SIMPLEQ_FOREACH(pid, parent_ids, entry) {
9262 err = got_object_id_str(&pid_str, pid->id);
9265 fprintf(outfile, "%s%s\n", GOT_COMMIT_LABEL_PARENT, pid_str);
9268 fprintf(outfile, "%s%s %lld +0000\n", GOT_COMMIT_LABEL_AUTHOR,
9269 got_object_commit_get_author(commit),
9270 got_object_commit_get_author_time(commit));
9272 fprintf(outfile, "%s%s %lld +0000\n", GOT_COMMIT_LABEL_COMMITTER,
9273 got_object_commit_get_author(commit),
9274 got_object_commit_get_committer_time(commit));
9276 logmsg = got_object_commit_get_logmsg_raw(commit);
9277 fprintf(outfile, "messagelen %zd\n", strlen(logmsg));
9278 fprintf(outfile, "%s", logmsg);
9281 got_object_commit_close(commit);
9285 static const struct got_error *
9286 cat_tag(struct got_object_id *id, struct got_repository *repo, FILE *outfile)
9288 const struct got_error *err;
9289 struct got_tag_object *tag;
9290 char *id_str = NULL;
9291 const char *tagmsg = NULL;
9293 err = got_object_open_as_tag(&tag, repo, id);
9297 err = got_object_id_str(&id_str, got_object_tag_get_object_id(tag));
9301 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_OBJECT, id_str);
9303 switch (got_object_tag_get_object_type(tag)) {
9304 case GOT_OBJ_TYPE_BLOB:
9305 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TYPE,
9306 GOT_OBJ_LABEL_BLOB);
9308 case GOT_OBJ_TYPE_TREE:
9309 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TYPE,
9310 GOT_OBJ_LABEL_TREE);
9312 case GOT_OBJ_TYPE_COMMIT:
9313 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TYPE,
9314 GOT_OBJ_LABEL_COMMIT);
9316 case GOT_OBJ_TYPE_TAG:
9317 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TYPE,
9324 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TAG,
9325 got_object_tag_get_name(tag));
9327 fprintf(outfile, "%s%s %lld +0000\n", GOT_TAG_LABEL_TAGGER,
9328 got_object_tag_get_tagger(tag),
9329 got_object_tag_get_tagger_time(tag));
9331 tagmsg = got_object_tag_get_message(tag);
9332 fprintf(outfile, "messagelen %zd\n", strlen(tagmsg));
9333 fprintf(outfile, "%s", tagmsg);
9336 got_object_tag_close(tag);
9340 static const struct got_error *
9341 cmd_cat(int argc, char *argv[])
9343 const struct got_error *error;
9344 struct got_repository *repo = NULL;
9345 struct got_worktree *worktree = NULL;
9346 char *cwd = NULL, *repo_path = NULL, *label = NULL;
9347 const char *commit_id_str = NULL;
9348 struct got_object_id *id = NULL, *commit_id = NULL;
9349 int ch, obj_type, i, force_path = 0;
9352 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
9357 while ((ch = getopt(argc, argv, "c:r:P")) != -1) {
9360 commit_id_str = optarg;
9363 repo_path = realpath(optarg, NULL);
9364 if (repo_path == NULL)
9365 return got_error_from_errno2("realpath",
9367 got_path_strip_trailing_slashes(repo_path);
9381 cwd = getcwd(NULL, 0);
9383 error = got_error_from_errno("getcwd");
9386 error = got_worktree_open(&worktree, cwd);
9387 if (error && error->code != GOT_ERR_NOT_WORKTREE)
9390 if (repo_path == NULL) {
9392 got_worktree_get_repo_path(worktree));
9393 if (repo_path == NULL) {
9394 error = got_error_from_errno("strdup");
9400 if (repo_path == NULL) {
9401 repo_path = getcwd(NULL, 0);
9402 if (repo_path == NULL)
9403 return got_error_from_errno("getcwd");
9406 error = got_repo_open(&repo, repo_path, NULL);
9411 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
9415 if (commit_id_str == NULL)
9416 commit_id_str = GOT_REF_HEAD;
9417 error = got_repo_match_object_id(&commit_id, NULL,
9418 commit_id_str, GOT_OBJ_TYPE_COMMIT, 1, repo);
9422 for (i = 0; i < argc; i++) {
9424 error = got_object_id_by_path(&id, repo, commit_id,
9429 error = got_repo_match_object_id(&id, &label, argv[i],
9430 GOT_OBJ_TYPE_ANY, 0, repo);
9432 if (error->code != GOT_ERR_BAD_OBJ_ID_STR &&
9433 error->code != GOT_ERR_NOT_REF)
9435 error = got_object_id_by_path(&id, repo,
9436 commit_id, argv[i]);
9442 error = got_object_get_type(&obj_type, repo, id);
9447 case GOT_OBJ_TYPE_BLOB:
9448 error = cat_blob(id, repo, stdout);
9450 case GOT_OBJ_TYPE_TREE:
9451 error = cat_tree(id, repo, stdout);
9453 case GOT_OBJ_TYPE_COMMIT:
9454 error = cat_commit(id, repo, stdout);
9456 case GOT_OBJ_TYPE_TAG:
9457 error = cat_tag(id, repo, stdout);
9460 error = got_error(GOT_ERR_OBJ_TYPE);
9475 got_worktree_close(worktree);
9477 const struct got_error *repo_error;
9478 repo_error = got_repo_close(repo);
9488 fprintf(stderr, "usage: %s info [path ...]\n",
9493 static const struct got_error *
9494 print_path_info(void *arg, const char *path, mode_t mode, time_t mtime,
9495 struct got_object_id *blob_id, struct got_object_id *staged_blob_id,
9496 struct got_object_id *commit_id)
9498 const struct got_error *err = NULL;
9499 char *id_str = NULL;
9501 struct tm mytm, *tm;
9502 struct got_pathlist_head *paths = arg;
9503 struct got_pathlist_entry *pe;
9506 * Clear error indication from any of the path arguments which
9507 * would cause this file index entry to be displayed.
9509 TAILQ_FOREACH(pe, paths, entry) {
9510 if (got_path_cmp(path, pe->path, strlen(path),
9511 pe->path_len) == 0 ||
9512 got_path_is_child(path, pe->path, pe->path_len))
9513 pe->data = NULL; /* no error */
9516 printf(GOT_COMMIT_SEP_STR);
9518 printf("symlink: %s\n", path);
9519 else if (S_ISREG(mode)) {
9520 printf("file: %s\n", path);
9521 printf("mode: %o\n", mode & (S_IRWXU | S_IRWXG | S_IRWXO));
9522 } else if (S_ISDIR(mode))
9523 printf("directory: %s\n", path);
9525 printf("something: %s\n", path);
9527 tm = localtime_r(&mtime, &mytm);
9530 if (strftime(datebuf, sizeof(datebuf), "%c %Z", tm) >= sizeof(datebuf))
9531 return got_error(GOT_ERR_NO_SPACE);
9532 printf("timestamp: %s\n", datebuf);
9535 err = got_object_id_str(&id_str, blob_id);
9538 printf("based on blob: %s\n", id_str);
9542 if (staged_blob_id) {
9543 err = got_object_id_str(&id_str, staged_blob_id);
9546 printf("based on staged blob: %s\n", id_str);
9551 err = got_object_id_str(&id_str, commit_id);
9554 printf("based on commit: %s\n", id_str);
9561 static const struct got_error *
9562 cmd_info(int argc, char *argv[])
9564 const struct got_error *error = NULL;
9565 struct got_worktree *worktree = NULL;
9566 char *cwd = NULL, *id_str = NULL;
9567 struct got_pathlist_head paths;
9568 struct got_pathlist_entry *pe;
9569 char *uuidstr = NULL;
9570 int ch, show_files = 0;
9574 while ((ch = getopt(argc, argv, "")) != -1) {
9586 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
9590 cwd = getcwd(NULL, 0);
9592 error = got_error_from_errno("getcwd");
9596 error = got_worktree_open(&worktree, cwd);
9598 if (error->code == GOT_ERR_NOT_WORKTREE)
9599 error = wrap_not_worktree_error(error, "status", cwd);
9603 error = apply_unveil(NULL, 0, got_worktree_get_root_path(worktree));
9608 error = get_worktree_paths_from_argv(&paths, argc, argv,
9615 error = got_object_id_str(&id_str,
9616 got_worktree_get_base_commit_id(worktree));
9620 error = got_worktree_get_uuid(&uuidstr, worktree);
9624 printf("work tree: %s\n", got_worktree_get_root_path(worktree));
9625 printf("work tree base commit: %s\n", id_str);
9626 printf("work tree path prefix: %s\n",
9627 got_worktree_get_path_prefix(worktree));
9628 printf("work tree branch reference: %s\n",
9629 got_worktree_get_head_ref_name(worktree));
9630 printf("work tree UUID: %s\n", uuidstr);
9631 printf("repository: %s\n", got_worktree_get_repo_path(worktree));
9634 struct got_pathlist_entry *pe;
9635 TAILQ_FOREACH(pe, &paths, entry) {
9636 if (pe->path_len == 0)
9639 * Assume this path will fail. This will be corrected
9640 * in print_path_info() in case the path does suceeed.
9642 pe->data = (void *)got_error_path(pe->path,
9645 error = got_worktree_path_info(worktree, &paths,
9646 print_path_info, &paths, check_cancelled, NULL);
9649 TAILQ_FOREACH(pe, &paths, entry) {
9650 if (pe->data != NULL) {
9651 error = pe->data; /* bad path */
9657 TAILQ_FOREACH(pe, &paths, entry)
9658 free((char *)pe->path);
9659 got_pathlist_free(&paths);