Blob


1 /*
2 * Copyright (c) 2017 Martin Pieuchot <mpi@openbsd.org>
3 * Copyright (c) 2018, 2019, 2020 Stefan Sperling <stsp@openbsd.org>
4 * Copyright (c) 2020 Ori Bernstein <ori@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
19 #include <sys/queue.h>
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <sys/wait.h>
24 #include <err.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <limits.h>
28 #include <locale.h>
29 #include <ctype.h>
30 #include <sha1.h>
31 #include <signal.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <libgen.h>
37 #include <time.h>
38 #include <paths.h>
39 #include <regex.h>
40 #include <getopt.h>
41 #include <util.h>
43 #include "got_version.h"
44 #include "got_error.h"
45 #include "got_object.h"
46 #include "got_reference.h"
47 #include "got_repository.h"
48 #include "got_path.h"
49 #include "got_cancel.h"
50 #include "got_worktree.h"
51 #include "got_diff.h"
52 #include "got_commit_graph.h"
53 #include "got_fetch.h"
54 #include "got_send.h"
55 #include "got_blame.h"
56 #include "got_privsep.h"
57 #include "got_opentemp.h"
58 #include "got_gotconfig.h"
59 #include "got_dial.h"
60 #include "got_patch.h"
62 #ifndef nitems
63 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
64 #endif
66 static volatile sig_atomic_t sigint_received;
67 static volatile sig_atomic_t sigpipe_received;
69 static void
70 catch_sigint(int signo)
71 {
72 sigint_received = 1;
73 }
75 static void
76 catch_sigpipe(int signo)
77 {
78 sigpipe_received = 1;
79 }
82 struct got_cmd {
83 const char *cmd_name;
84 const struct got_error *(*cmd_main)(int, char *[]);
85 void (*cmd_usage)(void);
86 const char *cmd_alias;
87 };
89 __dead static void usage(int, int);
90 __dead static void usage_init(void);
91 __dead static void usage_import(void);
92 __dead static void usage_clone(void);
93 __dead static void usage_fetch(void);
94 __dead static void usage_checkout(void);
95 __dead static void usage_update(void);
96 __dead static void usage_log(void);
97 __dead static void usage_diff(void);
98 __dead static void usage_blame(void);
99 __dead static void usage_tree(void);
100 __dead static void usage_status(void);
101 __dead static void usage_ref(void);
102 __dead static void usage_branch(void);
103 __dead static void usage_tag(void);
104 __dead static void usage_add(void);
105 __dead static void usage_remove(void);
106 __dead static void usage_patch(void);
107 __dead static void usage_revert(void);
108 __dead static void usage_commit(void);
109 __dead static void usage_send(void);
110 __dead static void usage_cherrypick(void);
111 __dead static void usage_backout(void);
112 __dead static void usage_rebase(void);
113 __dead static void usage_histedit(void);
114 __dead static void usage_integrate(void);
115 __dead static void usage_merge(void);
116 __dead static void usage_stage(void);
117 __dead static void usage_unstage(void);
118 __dead static void usage_cat(void);
119 __dead static void usage_info(void);
121 static const struct got_error* cmd_init(int, char *[]);
122 static const struct got_error* cmd_import(int, char *[]);
123 static const struct got_error* cmd_clone(int, char *[]);
124 static const struct got_error* cmd_fetch(int, char *[]);
125 static const struct got_error* cmd_checkout(int, char *[]);
126 static const struct got_error* cmd_update(int, char *[]);
127 static const struct got_error* cmd_log(int, char *[]);
128 static const struct got_error* cmd_diff(int, char *[]);
129 static const struct got_error* cmd_blame(int, char *[]);
130 static const struct got_error* cmd_tree(int, char *[]);
131 static const struct got_error* cmd_status(int, char *[]);
132 static const struct got_error* cmd_ref(int, char *[]);
133 static const struct got_error* cmd_branch(int, char *[]);
134 static const struct got_error* cmd_tag(int, char *[]);
135 static const struct got_error* cmd_add(int, char *[]);
136 static const struct got_error* cmd_remove(int, char *[]);
137 static const struct got_error* cmd_patch(int, char *[]);
138 static const struct got_error* cmd_revert(int, char *[]);
139 static const struct got_error* cmd_commit(int, char *[]);
140 static const struct got_error* cmd_send(int, char *[]);
141 static const struct got_error* cmd_cherrypick(int, char *[]);
142 static const struct got_error* cmd_backout(int, char *[]);
143 static const struct got_error* cmd_rebase(int, char *[]);
144 static const struct got_error* cmd_histedit(int, char *[]);
145 static const struct got_error* cmd_integrate(int, char *[]);
146 static const struct got_error* cmd_merge(int, char *[]);
147 static const struct got_error* cmd_stage(int, char *[]);
148 static const struct got_error* cmd_unstage(int, char *[]);
149 static const struct got_error* cmd_cat(int, char *[]);
150 static const struct got_error* cmd_info(int, char *[]);
152 static const struct got_cmd got_commands[] = {
153 { "init", cmd_init, usage_init, "" },
154 { "import", cmd_import, usage_import, "im" },
155 { "clone", cmd_clone, usage_clone, "cl" },
156 { "fetch", cmd_fetch, usage_fetch, "fe" },
157 { "checkout", cmd_checkout, usage_checkout, "co" },
158 { "update", cmd_update, usage_update, "up" },
159 { "log", cmd_log, usage_log, "" },
160 { "diff", cmd_diff, usage_diff, "di" },
161 { "blame", cmd_blame, usage_blame, "bl" },
162 { "tree", cmd_tree, usage_tree, "tr" },
163 { "status", cmd_status, usage_status, "st" },
164 { "ref", cmd_ref, usage_ref, "" },
165 { "branch", cmd_branch, usage_branch, "br" },
166 { "tag", cmd_tag, usage_tag, "" },
167 { "add", cmd_add, usage_add, "" },
168 { "remove", cmd_remove, usage_remove, "rm" },
169 { "patch", cmd_patch, usage_patch, "pa" },
170 { "revert", cmd_revert, usage_revert, "rv" },
171 { "commit", cmd_commit, usage_commit, "ci" },
172 { "send", cmd_send, usage_send, "se" },
173 { "cherrypick", cmd_cherrypick, usage_cherrypick, "cy" },
174 { "backout", cmd_backout, usage_backout, "bo" },
175 { "rebase", cmd_rebase, usage_rebase, "rb" },
176 { "histedit", cmd_histedit, usage_histedit, "he" },
177 { "integrate", cmd_integrate, usage_integrate,"ig" },
178 { "merge", cmd_merge, usage_merge, "mg" },
179 { "stage", cmd_stage, usage_stage, "sg" },
180 { "unstage", cmd_unstage, usage_unstage, "ug" },
181 { "cat", cmd_cat, usage_cat, "" },
182 { "info", cmd_info, usage_info, "" },
183 };
185 static void
186 list_commands(FILE *fp)
188 size_t i;
190 fprintf(fp, "commands:");
191 for (i = 0; i < nitems(got_commands); i++) {
192 const struct got_cmd *cmd = &got_commands[i];
193 fprintf(fp, " %s", cmd->cmd_name);
195 fputc('\n', fp);
198 __dead static void
199 option_conflict(char a, char b)
201 errx(1, "-%c and -%c options are mutually exclusive", a, b);
204 int
205 main(int argc, char *argv[])
207 const struct got_cmd *cmd;
208 size_t i;
209 int ch;
210 int hflag = 0, Vflag = 0;
211 static const struct option longopts[] = {
212 { "version", no_argument, NULL, 'V' },
213 { NULL, 0, NULL, 0 }
214 };
216 setlocale(LC_CTYPE, "");
218 while ((ch = getopt_long(argc, argv, "+hV", longopts, NULL)) != -1) {
219 switch (ch) {
220 case 'h':
221 hflag = 1;
222 break;
223 case 'V':
224 Vflag = 1;
225 break;
226 default:
227 usage(hflag, 1);
228 /* NOTREACHED */
232 argc -= optind;
233 argv += optind;
234 optind = 1;
235 optreset = 1;
237 if (Vflag) {
238 got_version_print_str();
239 return 0;
242 if (argc <= 0)
243 usage(hflag, hflag ? 0 : 1);
245 signal(SIGINT, catch_sigint);
246 signal(SIGPIPE, catch_sigpipe);
248 for (i = 0; i < nitems(got_commands); i++) {
249 const struct got_error *error;
251 cmd = &got_commands[i];
253 if (strcmp(cmd->cmd_name, argv[0]) != 0 &&
254 strcmp(cmd->cmd_alias, argv[0]) != 0)
255 continue;
257 if (hflag)
258 cmd->cmd_usage();
260 error = cmd->cmd_main(argc, argv);
261 if (error && error->code != GOT_ERR_CANCELLED &&
262 error->code != GOT_ERR_PRIVSEP_EXIT &&
263 !(sigpipe_received &&
264 error->code == GOT_ERR_ERRNO && errno == EPIPE) &&
265 !(sigint_received &&
266 error->code == GOT_ERR_ERRNO && errno == EINTR)) {
267 fprintf(stderr, "%s: %s\n", getprogname(), error->msg);
268 return 1;
271 return 0;
274 fprintf(stderr, "%s: unknown command '%s'\n", getprogname(), argv[0]);
275 list_commands(stderr);
276 return 1;
279 __dead static void
280 usage(int hflag, int status)
282 FILE *fp = (status == 0) ? stdout : stderr;
284 fprintf(fp, "usage: %s [-h] [-V | --version] command [arg ...]\n",
285 getprogname());
286 if (hflag)
287 list_commands(fp);
288 exit(status);
291 static const struct got_error *
292 get_editor(char **abspath)
294 const struct got_error *err = NULL;
295 const char *editor;
297 *abspath = NULL;
299 editor = getenv("VISUAL");
300 if (editor == NULL)
301 editor = getenv("EDITOR");
303 if (editor) {
304 err = got_path_find_prog(abspath, editor);
305 if (err)
306 return err;
309 if (*abspath == NULL) {
310 *abspath = strdup("/bin/ed");
311 if (*abspath == NULL)
312 return got_error_from_errno("strdup");
315 return NULL;
318 static const struct got_error *
319 apply_unveil(const char *repo_path, int repo_read_only,
320 const char *worktree_path)
322 const struct got_error *err;
324 #ifdef PROFILE
325 if (unveil("gmon.out", "rwc") != 0)
326 return got_error_from_errno2("unveil", "gmon.out");
327 #endif
328 if (repo_path && unveil(repo_path, repo_read_only ? "r" : "rwc") != 0)
329 return got_error_from_errno2("unveil", repo_path);
331 if (worktree_path && unveil(worktree_path, "rwc") != 0)
332 return got_error_from_errno2("unveil", worktree_path);
334 if (unveil(GOT_TMPDIR_STR, "rwc") != 0)
335 return got_error_from_errno2("unveil", GOT_TMPDIR_STR);
337 err = got_privsep_unveil_exec_helpers();
338 if (err != NULL)
339 return err;
341 if (unveil(NULL, NULL) != 0)
342 return got_error_from_errno("unveil");
344 return NULL;
347 __dead static void
348 usage_init(void)
350 fprintf(stderr, "usage: %s init repository-path\n", getprogname());
351 exit(1);
354 static const struct got_error *
355 cmd_init(int argc, char *argv[])
357 const struct got_error *error = NULL;
358 char *repo_path = NULL;
359 int ch;
361 while ((ch = getopt(argc, argv, "")) != -1) {
362 switch (ch) {
363 default:
364 usage_init();
365 /* NOTREACHED */
369 argc -= optind;
370 argv += optind;
372 #ifndef PROFILE
373 if (pledge("stdio rpath wpath cpath unveil", NULL) == -1)
374 err(1, "pledge");
375 #endif
376 if (argc != 1)
377 usage_init();
379 repo_path = strdup(argv[0]);
380 if (repo_path == NULL)
381 return got_error_from_errno("strdup");
383 got_path_strip_trailing_slashes(repo_path);
385 error = got_path_mkdir(repo_path);
386 if (error &&
387 !(error->code == GOT_ERR_ERRNO && errno == EEXIST))
388 goto done;
390 error = apply_unveil(repo_path, 0, NULL);
391 if (error)
392 goto done;
394 error = got_repo_init(repo_path);
395 done:
396 free(repo_path);
397 return error;
400 __dead static void
401 usage_import(void)
403 fprintf(stderr, "usage: %s import [-b branch] [-m message] "
404 "[-r repository-path] [-I pattern] path\n", getprogname());
405 exit(1);
408 int
409 spawn_editor(const char *editor, const char *file)
411 pid_t pid;
412 sig_t sighup, sigint, sigquit;
413 int st = -1;
415 sighup = signal(SIGHUP, SIG_IGN);
416 sigint = signal(SIGINT, SIG_IGN);
417 sigquit = signal(SIGQUIT, SIG_IGN);
419 switch (pid = fork()) {
420 case -1:
421 goto doneediting;
422 case 0:
423 execl(editor, editor, file, (char *)NULL);
424 _exit(127);
427 while (waitpid(pid, &st, 0) == -1)
428 if (errno != EINTR)
429 break;
431 doneediting:
432 (void)signal(SIGHUP, sighup);
433 (void)signal(SIGINT, sigint);
434 (void)signal(SIGQUIT, sigquit);
436 if (!WIFEXITED(st)) {
437 errno = EINTR;
438 return -1;
441 return WEXITSTATUS(st);
444 static const struct got_error *
445 edit_logmsg(char **logmsg, const char *editor, const char *logmsg_path,
446 const char *initial_content, size_t initial_content_len,
447 int require_modification)
449 const struct got_error *err = NULL;
450 char *line = NULL;
451 size_t linesize = 0;
452 ssize_t linelen;
453 struct stat st, st2;
454 FILE *fp = NULL;
455 size_t len, logmsg_len;
456 char *initial_content_stripped = NULL, *buf = NULL, *s;
458 *logmsg = NULL;
460 if (stat(logmsg_path, &st) == -1)
461 return got_error_from_errno2("stat", logmsg_path);
463 if (spawn_editor(editor, logmsg_path) == -1)
464 return got_error_from_errno("failed spawning editor");
466 if (stat(logmsg_path, &st2) == -1)
467 return got_error_from_errno("stat");
469 if (require_modification &&
470 st.st_mtime == st2.st_mtime && st.st_size == st2.st_size)
471 return got_error_msg(GOT_ERR_COMMIT_MSG_EMPTY,
472 "no changes made to commit message, aborting");
474 /*
475 * Set up a stripped version of the initial content without comments
476 * and blank lines. We need this in order to check if the message
477 * has in fact been edited.
478 */
479 initial_content_stripped = malloc(initial_content_len + 1);
480 if (initial_content_stripped == NULL)
481 return got_error_from_errno("malloc");
482 initial_content_stripped[0] = '\0';
484 buf = strdup(initial_content);
485 if (buf == NULL) {
486 err = got_error_from_errno("strdup");
487 goto done;
489 s = buf;
490 len = 0;
491 while ((line = strsep(&s, "\n")) != NULL) {
492 if ((line[0] == '#' || (len == 0 && line[0] == '\n')))
493 continue; /* remove comments and leading empty lines */
494 len = strlcat(initial_content_stripped, line,
495 initial_content_len + 1);
496 if (len >= initial_content_len + 1) {
497 err = got_error(GOT_ERR_NO_SPACE);
498 goto done;
501 while (len > 0 && initial_content_stripped[len - 1] == '\n') {
502 initial_content_stripped[len - 1] = '\0';
503 len--;
506 logmsg_len = st2.st_size;
507 *logmsg = malloc(logmsg_len + 1);
508 if (*logmsg == NULL)
509 return got_error_from_errno("malloc");
510 (*logmsg)[0] = '\0';
512 fp = fopen(logmsg_path, "re");
513 if (fp == NULL) {
514 err = got_error_from_errno("fopen");
515 goto done;
518 len = 0;
519 while ((linelen = getline(&line, &linesize, fp)) != -1) {
520 if ((line[0] == '#' || (len == 0 && line[0] == '\n')))
521 continue; /* remove comments and leading empty lines */
522 len = strlcat(*logmsg, line, logmsg_len + 1);
523 if (len >= logmsg_len + 1) {
524 err = got_error(GOT_ERR_NO_SPACE);
525 goto done;
528 free(line);
529 if (ferror(fp)) {
530 err = got_ferror(fp, GOT_ERR_IO);
531 goto done;
533 while (len > 0 && (*logmsg)[len - 1] == '\n') {
534 (*logmsg)[len - 1] = '\0';
535 len--;
538 if (len == 0) {
539 err = got_error_msg(GOT_ERR_COMMIT_MSG_EMPTY,
540 "commit message cannot be empty, aborting");
541 goto done;
543 if (require_modification &&
544 strcmp(*logmsg, initial_content_stripped) == 0)
545 err = got_error_msg(GOT_ERR_COMMIT_MSG_EMPTY,
546 "no changes made to commit message, aborting");
547 done:
548 free(initial_content_stripped);
549 free(buf);
550 if (fp && fclose(fp) == EOF && err == NULL)
551 err = got_error_from_errno("fclose");
552 if (err) {
553 free(*logmsg);
554 *logmsg = NULL;
556 return err;
559 static const struct got_error *
560 collect_import_msg(char **logmsg, char **logmsg_path, const char *editor,
561 const char *path_dir, const char *branch_name)
563 char *initial_content = NULL;
564 const struct got_error *err = NULL;
565 int initial_content_len;
566 int fd = -1;
568 initial_content_len = asprintf(&initial_content,
569 "\n# %s to be imported to branch %s\n", path_dir,
570 branch_name);
571 if (initial_content_len == -1)
572 return got_error_from_errno("asprintf");
574 err = got_opentemp_named_fd(logmsg_path, &fd,
575 GOT_TMPDIR_STR "/got-importmsg");
576 if (err)
577 goto done;
579 if (write(fd, initial_content, initial_content_len) == -1) {
580 err = got_error_from_errno2("write", *logmsg_path);
581 goto done;
584 err = edit_logmsg(logmsg, editor, *logmsg_path, initial_content,
585 initial_content_len, 1);
586 done:
587 if (fd != -1 && close(fd) == -1 && err == NULL)
588 err = got_error_from_errno2("close", *logmsg_path);
589 free(initial_content);
590 if (err) {
591 free(*logmsg_path);
592 *logmsg_path = NULL;
594 return err;
597 static const struct got_error *
598 import_progress(void *arg, const char *path)
600 printf("A %s\n", path);
601 return NULL;
604 static int
605 valid_author(const char *author)
607 /*
608 * Really dumb email address check; we're only doing this to
609 * avoid git's object parser breaking on commits we create.
610 */
611 while (*author && *author != '<')
612 author++;
613 if (*author != '<')
614 return 0;
615 while (*author && *author != '@')
616 author++;
617 if (*author != '@')
618 return 0;
619 while (*author && *author != '>')
620 author++;
621 return *author == '>';
624 static const struct got_error *
625 get_author(char **author, struct got_repository *repo,
626 struct got_worktree *worktree)
628 const struct got_error *err = NULL;
629 const char *got_author = NULL, *name, *email;
630 const struct got_gotconfig *worktree_conf = NULL, *repo_conf = NULL;
632 *author = NULL;
634 if (worktree)
635 worktree_conf = got_worktree_get_gotconfig(worktree);
636 repo_conf = got_repo_get_gotconfig(repo);
638 /*
639 * Priority of potential author information sources, from most
640 * significant to least significant:
641 * 1) work tree's .got/got.conf file
642 * 2) repository's got.conf file
643 * 3) repository's git config file
644 * 4) environment variables
645 * 5) global git config files (in user's home directory or /etc)
646 */
648 if (worktree_conf)
649 got_author = got_gotconfig_get_author(worktree_conf);
650 if (got_author == NULL)
651 got_author = got_gotconfig_get_author(repo_conf);
652 if (got_author == NULL) {
653 name = got_repo_get_gitconfig_author_name(repo);
654 email = got_repo_get_gitconfig_author_email(repo);
655 if (name && email) {
656 if (asprintf(author, "%s <%s>", name, email) == -1)
657 return got_error_from_errno("asprintf");
658 return NULL;
661 got_author = getenv("GOT_AUTHOR");
662 if (got_author == NULL) {
663 name = got_repo_get_global_gitconfig_author_name(repo);
664 email = got_repo_get_global_gitconfig_author_email(
665 repo);
666 if (name && email) {
667 if (asprintf(author, "%s <%s>", name, email)
668 == -1)
669 return got_error_from_errno("asprintf");
670 return NULL;
672 /* TODO: Look up user in password database? */
673 return got_error(GOT_ERR_COMMIT_NO_AUTHOR);
677 *author = strdup(got_author);
678 if (*author == NULL)
679 return got_error_from_errno("strdup");
681 if (!valid_author(*author)) {
682 err = got_error_fmt(GOT_ERR_COMMIT_NO_EMAIL, "%s", *author);
683 free(*author);
684 *author = NULL;
686 return err;
689 static const struct got_error *
690 get_gitconfig_path(char **gitconfig_path)
692 const char *homedir = getenv("HOME");
694 *gitconfig_path = NULL;
695 if (homedir) {
696 if (asprintf(gitconfig_path, "%s/.gitconfig", homedir) == -1)
697 return got_error_from_errno("asprintf");
700 return NULL;
703 static const struct got_error *
704 cmd_import(int argc, char *argv[])
706 const struct got_error *error = NULL;
707 char *path_dir = NULL, *repo_path = NULL, *logmsg = NULL;
708 char *gitconfig_path = NULL, *editor = NULL, *author = NULL;
709 const char *branch_name = "main";
710 char *refname = NULL, *id_str = NULL, *logmsg_path = NULL;
711 struct got_repository *repo = NULL;
712 struct got_reference *branch_ref = NULL, *head_ref = NULL;
713 struct got_object_id *new_commit_id = NULL;
714 int ch;
715 struct got_pathlist_head ignores;
716 struct got_pathlist_entry *pe;
717 int preserve_logmsg = 0;
718 int *pack_fds = NULL;
720 TAILQ_INIT(&ignores);
722 while ((ch = getopt(argc, argv, "b:m:r:I:")) != -1) {
723 switch (ch) {
724 case 'b':
725 branch_name = optarg;
726 break;
727 case 'm':
728 logmsg = strdup(optarg);
729 if (logmsg == NULL) {
730 error = got_error_from_errno("strdup");
731 goto done;
733 break;
734 case 'r':
735 repo_path = realpath(optarg, NULL);
736 if (repo_path == NULL) {
737 error = got_error_from_errno2("realpath",
738 optarg);
739 goto done;
741 break;
742 case 'I':
743 if (optarg[0] == '\0')
744 break;
745 error = got_pathlist_insert(&pe, &ignores, optarg,
746 NULL);
747 if (error)
748 goto done;
749 break;
750 default:
751 usage_import();
752 /* NOTREACHED */
756 argc -= optind;
757 argv += optind;
759 #ifndef PROFILE
760 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
761 "unveil",
762 NULL) == -1)
763 err(1, "pledge");
764 #endif
765 if (argc != 1)
766 usage_import();
768 if (repo_path == NULL) {
769 repo_path = getcwd(NULL, 0);
770 if (repo_path == NULL)
771 return got_error_from_errno("getcwd");
773 got_path_strip_trailing_slashes(repo_path);
774 error = get_gitconfig_path(&gitconfig_path);
775 if (error)
776 goto done;
777 error = got_repo_pack_fds_open(&pack_fds);
778 if (error != NULL)
779 goto done;
780 error = got_repo_open(&repo, repo_path, gitconfig_path, pack_fds);
781 if (error)
782 goto done;
784 error = get_author(&author, repo, NULL);
785 if (error)
786 return error;
788 /*
789 * Don't let the user create a branch name with a leading '-'.
790 * While technically a valid reference name, this case is usually
791 * an unintended typo.
792 */
793 if (branch_name[0] == '-')
794 return got_error_path(branch_name, GOT_ERR_REF_NAME_MINUS);
796 if (asprintf(&refname, "refs/heads/%s", branch_name) == -1) {
797 error = got_error_from_errno("asprintf");
798 goto done;
801 error = got_ref_open(&branch_ref, repo, refname, 0);
802 if (error) {
803 if (error->code != GOT_ERR_NOT_REF)
804 goto done;
805 } else {
806 error = got_error_msg(GOT_ERR_BRANCH_EXISTS,
807 "import target branch already exists");
808 goto done;
811 path_dir = realpath(argv[0], NULL);
812 if (path_dir == NULL) {
813 error = got_error_from_errno2("realpath", argv[0]);
814 goto done;
816 got_path_strip_trailing_slashes(path_dir);
818 /*
819 * unveil(2) traverses exec(2); if an editor is used we have
820 * to apply unveil after the log message has been written.
821 */
822 if (logmsg == NULL || strlen(logmsg) == 0) {
823 error = get_editor(&editor);
824 if (error)
825 goto done;
826 free(logmsg);
827 error = collect_import_msg(&logmsg, &logmsg_path, editor,
828 path_dir, refname);
829 if (error) {
830 if (error->code != GOT_ERR_COMMIT_MSG_EMPTY &&
831 logmsg_path != NULL)
832 preserve_logmsg = 1;
833 goto done;
837 if (unveil(path_dir, "r") != 0) {
838 error = got_error_from_errno2("unveil", path_dir);
839 if (logmsg_path)
840 preserve_logmsg = 1;
841 goto done;
844 error = apply_unveil(got_repo_get_path(repo), 0, NULL);
845 if (error) {
846 if (logmsg_path)
847 preserve_logmsg = 1;
848 goto done;
851 error = got_repo_import(&new_commit_id, path_dir, logmsg,
852 author, &ignores, repo, import_progress, NULL);
853 if (error) {
854 if (logmsg_path)
855 preserve_logmsg = 1;
856 goto done;
859 error = got_ref_alloc(&branch_ref, refname, new_commit_id);
860 if (error) {
861 if (logmsg_path)
862 preserve_logmsg = 1;
863 goto done;
866 error = got_ref_write(branch_ref, repo);
867 if (error) {
868 if (logmsg_path)
869 preserve_logmsg = 1;
870 goto done;
873 error = got_object_id_str(&id_str, new_commit_id);
874 if (error) {
875 if (logmsg_path)
876 preserve_logmsg = 1;
877 goto done;
880 error = got_ref_open(&head_ref, repo, GOT_REF_HEAD, 0);
881 if (error) {
882 if (error->code != GOT_ERR_NOT_REF) {
883 if (logmsg_path)
884 preserve_logmsg = 1;
885 goto done;
888 error = got_ref_alloc_symref(&head_ref, GOT_REF_HEAD,
889 branch_ref);
890 if (error) {
891 if (logmsg_path)
892 preserve_logmsg = 1;
893 goto done;
896 error = got_ref_write(head_ref, repo);
897 if (error) {
898 if (logmsg_path)
899 preserve_logmsg = 1;
900 goto done;
904 printf("Created branch %s with commit %s\n",
905 got_ref_get_name(branch_ref), id_str);
906 done:
907 if (pack_fds) {
908 const struct got_error *pack_err =
909 got_repo_pack_fds_close(pack_fds);
910 if (error == NULL)
911 error = pack_err;
913 if (preserve_logmsg) {
914 fprintf(stderr, "%s: log message preserved in %s\n",
915 getprogname(), logmsg_path);
916 } else if (logmsg_path && unlink(logmsg_path) == -1 && error == NULL)
917 error = got_error_from_errno2("unlink", logmsg_path);
918 free(logmsg);
919 free(logmsg_path);
920 free(repo_path);
921 free(editor);
922 free(refname);
923 free(new_commit_id);
924 free(id_str);
925 free(author);
926 free(gitconfig_path);
927 if (branch_ref)
928 got_ref_close(branch_ref);
929 if (head_ref)
930 got_ref_close(head_ref);
931 return error;
934 __dead static void
935 usage_clone(void)
937 fprintf(stderr, "usage: %s clone [-a] [-b branch] [-l] [-m] [-q] [-v] "
938 "[-R reference] repository-url [directory]\n", getprogname());
939 exit(1);
942 struct got_fetch_progress_arg {
943 char last_scaled_size[FMT_SCALED_STRSIZE];
944 int last_p_indexed;
945 int last_p_resolved;
946 int verbosity;
948 struct got_repository *repo;
950 int create_configs;
951 int configs_created;
952 struct {
953 struct got_pathlist_head *symrefs;
954 struct got_pathlist_head *wanted_branches;
955 struct got_pathlist_head *wanted_refs;
956 const char *proto;
957 const char *host;
958 const char *port;
959 const char *remote_repo_path;
960 const char *git_url;
961 int fetch_all_branches;
962 int mirror_references;
963 } config_info;
964 };
966 /* XXX forward declaration */
967 static const struct got_error *
968 create_config_files(const char *proto, const char *host, const char *port,
969 const char *remote_repo_path, const char *git_url, int fetch_all_branches,
970 int mirror_references, struct got_pathlist_head *symrefs,
971 struct got_pathlist_head *wanted_branches,
972 struct got_pathlist_head *wanted_refs, struct got_repository *repo);
974 static const struct got_error *
975 fetch_progress(void *arg, const char *message, off_t packfile_size,
976 int nobj_total, int nobj_indexed, int nobj_loose, int nobj_resolved)
978 const struct got_error *err = NULL;
979 struct got_fetch_progress_arg *a = arg;
980 char scaled_size[FMT_SCALED_STRSIZE];
981 int p_indexed, p_resolved;
982 int print_size = 0, print_indexed = 0, print_resolved = 0;
984 /*
985 * In order to allow a failed clone to be resumed with 'got fetch'
986 * we try to create configuration files as soon as possible.
987 * Once the server has sent information about its default branch
988 * we have all required information.
989 */
990 if (a->create_configs && !a->configs_created &&
991 !TAILQ_EMPTY(a->config_info.symrefs)) {
992 err = create_config_files(a->config_info.proto,
993 a->config_info.host, a->config_info.port,
994 a->config_info.remote_repo_path,
995 a->config_info.git_url,
996 a->config_info.fetch_all_branches,
997 a->config_info.mirror_references,
998 a->config_info.symrefs,
999 a->config_info.wanted_branches,
1000 a->config_info.wanted_refs, a->repo);
1001 if (err)
1002 return err;
1003 a->configs_created = 1;
1006 if (a->verbosity < 0)
1007 return NULL;
1009 if (message && message[0] != '\0') {
1010 printf("\rserver: %s", message);
1011 fflush(stdout);
1012 return NULL;
1015 if (packfile_size > 0 || nobj_indexed > 0) {
1016 if (fmt_scaled(packfile_size, scaled_size) == 0 &&
1017 (a->last_scaled_size[0] == '\0' ||
1018 strcmp(scaled_size, a->last_scaled_size)) != 0) {
1019 print_size = 1;
1020 if (strlcpy(a->last_scaled_size, scaled_size,
1021 FMT_SCALED_STRSIZE) >= FMT_SCALED_STRSIZE)
1022 return got_error(GOT_ERR_NO_SPACE);
1024 if (nobj_indexed > 0) {
1025 p_indexed = (nobj_indexed * 100) / nobj_total;
1026 if (p_indexed != a->last_p_indexed) {
1027 a->last_p_indexed = p_indexed;
1028 print_indexed = 1;
1029 print_size = 1;
1032 if (nobj_resolved > 0) {
1033 p_resolved = (nobj_resolved * 100) /
1034 (nobj_total - nobj_loose);
1035 if (p_resolved != a->last_p_resolved) {
1036 a->last_p_resolved = p_resolved;
1037 print_resolved = 1;
1038 print_indexed = 1;
1039 print_size = 1;
1044 if (print_size || print_indexed || print_resolved)
1045 printf("\r");
1046 if (print_size)
1047 printf("%*s fetched", FMT_SCALED_STRSIZE - 2, scaled_size);
1048 if (print_indexed)
1049 printf("; indexing %d%%", p_indexed);
1050 if (print_resolved)
1051 printf("; resolving deltas %d%%", p_resolved);
1052 if (print_size || print_indexed || print_resolved)
1053 fflush(stdout);
1055 return NULL;
1058 static const struct got_error *
1059 create_symref(const char *refname, struct got_reference *target_ref,
1060 int verbosity, struct got_repository *repo)
1062 const struct got_error *err;
1063 struct got_reference *head_symref;
1065 err = got_ref_alloc_symref(&head_symref, refname, target_ref);
1066 if (err)
1067 return err;
1069 err = got_ref_write(head_symref, repo);
1070 if (err == NULL && verbosity > 0) {
1071 printf("Created reference %s: %s\n", GOT_REF_HEAD,
1072 got_ref_get_name(target_ref));
1074 got_ref_close(head_symref);
1075 return err;
1078 static const struct got_error *
1079 list_remote_refs(struct got_pathlist_head *symrefs,
1080 struct got_pathlist_head *refs)
1082 const struct got_error *err;
1083 struct got_pathlist_entry *pe;
1085 TAILQ_FOREACH(pe, symrefs, entry) {
1086 const char *refname = pe->path;
1087 const char *targetref = pe->data;
1089 printf("%s: %s\n", refname, targetref);
1092 TAILQ_FOREACH(pe, refs, entry) {
1093 const char *refname = pe->path;
1094 struct got_object_id *id = pe->data;
1095 char *id_str;
1097 err = got_object_id_str(&id_str, id);
1098 if (err)
1099 return err;
1100 printf("%s: %s\n", refname, id_str);
1101 free(id_str);
1104 return NULL;
1107 static const struct got_error *
1108 create_ref(const char *refname, struct got_object_id *id,
1109 int verbosity, struct got_repository *repo)
1111 const struct got_error *err = NULL;
1112 struct got_reference *ref;
1113 char *id_str;
1115 err = got_object_id_str(&id_str, id);
1116 if (err)
1117 return err;
1119 err = got_ref_alloc(&ref, refname, id);
1120 if (err)
1121 goto done;
1123 err = got_ref_write(ref, repo);
1124 got_ref_close(ref);
1126 if (err == NULL && verbosity >= 0)
1127 printf("Created reference %s: %s\n", refname, id_str);
1128 done:
1129 free(id_str);
1130 return err;
1133 static int
1134 match_wanted_ref(const char *refname, const char *wanted_ref)
1136 if (strncmp(refname, "refs/", 5) != 0)
1137 return 0;
1138 refname += 5;
1141 * Prevent fetching of references that won't make any
1142 * sense outside of the remote repository's context.
1144 if (strncmp(refname, "got/", 4) == 0)
1145 return 0;
1146 if (strncmp(refname, "remotes/", 8) == 0)
1147 return 0;
1149 if (strncmp(wanted_ref, "refs/", 5) == 0)
1150 wanted_ref += 5;
1152 /* Allow prefix match. */
1153 if (got_path_is_child(refname, wanted_ref, strlen(wanted_ref)))
1154 return 1;
1156 /* Allow exact match. */
1157 return (strcmp(refname, wanted_ref) == 0);
1160 static int
1161 is_wanted_ref(struct got_pathlist_head *wanted_refs, const char *refname)
1163 struct got_pathlist_entry *pe;
1165 TAILQ_FOREACH(pe, wanted_refs, entry) {
1166 if (match_wanted_ref(refname, pe->path))
1167 return 1;
1170 return 0;
1173 static const struct got_error *
1174 create_wanted_ref(const char *refname, struct got_object_id *id,
1175 const char *remote_repo_name, int verbosity, struct got_repository *repo)
1177 const struct got_error *err;
1178 char *remote_refname;
1180 if (strncmp("refs/", refname, 5) == 0)
1181 refname += 5;
1183 if (asprintf(&remote_refname, "refs/remotes/%s/%s",
1184 remote_repo_name, refname) == -1)
1185 return got_error_from_errno("asprintf");
1187 err = create_ref(remote_refname, id, verbosity, repo);
1188 free(remote_refname);
1189 return err;
1192 static const struct got_error *
1193 create_gotconfig(const char *proto, const char *host, const char *port,
1194 const char *remote_repo_path, const char *default_branch,
1195 int fetch_all_branches, struct got_pathlist_head *wanted_branches,
1196 struct got_pathlist_head *wanted_refs, int mirror_references,
1197 struct got_repository *repo)
1199 const struct got_error *err = NULL;
1200 char *gotconfig_path = NULL;
1201 char *gotconfig = NULL;
1202 FILE *gotconfig_file = NULL;
1203 const char *branchname = NULL;
1204 char *branches = NULL, *refs = NULL;
1205 ssize_t n;
1207 if (!fetch_all_branches && !TAILQ_EMPTY(wanted_branches)) {
1208 struct got_pathlist_entry *pe;
1209 TAILQ_FOREACH(pe, wanted_branches, entry) {
1210 char *s;
1211 branchname = pe->path;
1212 if (strncmp(branchname, "refs/heads/", 11) == 0)
1213 branchname += 11;
1214 if (asprintf(&s, "%s\"%s\" ",
1215 branches ? branches : "", branchname) == -1) {
1216 err = got_error_from_errno("asprintf");
1217 goto done;
1219 free(branches);
1220 branches = s;
1222 } else if (!fetch_all_branches && default_branch) {
1223 branchname = default_branch;
1224 if (strncmp(branchname, "refs/heads/", 11) == 0)
1225 branchname += 11;
1226 if (asprintf(&branches, "\"%s\" ", branchname) == -1) {
1227 err = got_error_from_errno("asprintf");
1228 goto done;
1231 if (!TAILQ_EMPTY(wanted_refs)) {
1232 struct got_pathlist_entry *pe;
1233 TAILQ_FOREACH(pe, wanted_refs, entry) {
1234 char *s;
1235 const char *refname = pe->path;
1236 if (strncmp(refname, "refs/", 5) == 0)
1237 branchname += 5;
1238 if (asprintf(&s, "%s\"%s\" ",
1239 refs ? refs : "", refname) == -1) {
1240 err = got_error_from_errno("asprintf");
1241 goto done;
1243 free(refs);
1244 refs = s;
1248 /* Create got.conf(5). */
1249 gotconfig_path = got_repo_get_path_gotconfig(repo);
1250 if (gotconfig_path == NULL) {
1251 err = got_error_from_errno("got_repo_get_path_gotconfig");
1252 goto done;
1254 gotconfig_file = fopen(gotconfig_path, "ae");
1255 if (gotconfig_file == NULL) {
1256 err = got_error_from_errno2("fopen", gotconfig_path);
1257 goto done;
1259 if (asprintf(&gotconfig,
1260 "remote \"%s\" {\n"
1261 "\tserver %s\n"
1262 "\tprotocol %s\n"
1263 "%s%s%s"
1264 "\trepository \"%s\"\n"
1265 "%s%s%s"
1266 "%s%s%s"
1267 "%s"
1268 "%s"
1269 "}\n",
1270 GOT_FETCH_DEFAULT_REMOTE_NAME, host, proto,
1271 port ? "\tport " : "", port ? port : "", port ? "\n" : "",
1272 remote_repo_path, branches ? "\tbranch { " : "",
1273 branches ? branches : "", branches ? "}\n" : "",
1274 refs ? "\treference { " : "", refs ? refs : "", refs ? "}\n" : "",
1275 mirror_references ? "\tmirror-references yes\n" : "",
1276 fetch_all_branches ? "\tfetch-all-branches yes\n" : "") == -1) {
1277 err = got_error_from_errno("asprintf");
1278 goto done;
1280 n = fwrite(gotconfig, 1, strlen(gotconfig), gotconfig_file);
1281 if (n != strlen(gotconfig)) {
1282 err = got_ferror(gotconfig_file, GOT_ERR_IO);
1283 goto done;
1286 done:
1287 if (gotconfig_file && fclose(gotconfig_file) == EOF && err == NULL)
1288 err = got_error_from_errno2("fclose", gotconfig_path);
1289 free(gotconfig_path);
1290 free(branches);
1291 return err;
1294 static const struct got_error *
1295 create_gitconfig(const char *git_url, const char *default_branch,
1296 int fetch_all_branches, struct got_pathlist_head *wanted_branches,
1297 struct got_pathlist_head *wanted_refs, int mirror_references,
1298 struct got_repository *repo)
1300 const struct got_error *err = NULL;
1301 char *gitconfig_path = NULL;
1302 char *gitconfig = NULL;
1303 FILE *gitconfig_file = NULL;
1304 char *branches = NULL, *refs = NULL;
1305 const char *branchname;
1306 ssize_t n;
1308 /* Create a config file Git can understand. */
1309 gitconfig_path = got_repo_get_path_gitconfig(repo);
1310 if (gitconfig_path == NULL) {
1311 err = got_error_from_errno("got_repo_get_path_gitconfig");
1312 goto done;
1314 gitconfig_file = fopen(gitconfig_path, "ae");
1315 if (gitconfig_file == NULL) {
1316 err = got_error_from_errno2("fopen", gitconfig_path);
1317 goto done;
1319 if (fetch_all_branches) {
1320 if (mirror_references) {
1321 if (asprintf(&branches,
1322 "\tfetch = refs/heads/*:refs/heads/*\n") == -1) {
1323 err = got_error_from_errno("asprintf");
1324 goto done;
1326 } else if (asprintf(&branches,
1327 "\tfetch = refs/heads/*:refs/remotes/%s/*\n",
1328 GOT_FETCH_DEFAULT_REMOTE_NAME) == -1) {
1329 err = got_error_from_errno("asprintf");
1330 goto done;
1332 } else if (!TAILQ_EMPTY(wanted_branches)) {
1333 struct got_pathlist_entry *pe;
1334 TAILQ_FOREACH(pe, wanted_branches, entry) {
1335 char *s;
1336 branchname = pe->path;
1337 if (strncmp(branchname, "refs/heads/", 11) == 0)
1338 branchname += 11;
1339 if (mirror_references) {
1340 if (asprintf(&s,
1341 "%s\tfetch = refs/heads/%s:refs/heads/%s\n",
1342 branches ? branches : "",
1343 branchname, branchname) == -1) {
1344 err = got_error_from_errno("asprintf");
1345 goto done;
1347 } else if (asprintf(&s,
1348 "%s\tfetch = refs/heads/%s:refs/remotes/%s/%s\n",
1349 branches ? branches : "",
1350 branchname, GOT_FETCH_DEFAULT_REMOTE_NAME,
1351 branchname) == -1) {
1352 err = got_error_from_errno("asprintf");
1353 goto done;
1355 free(branches);
1356 branches = s;
1358 } else {
1360 * If the server specified a default branch, use just that one.
1361 * Otherwise fall back to fetching all branches on next fetch.
1363 if (default_branch) {
1364 branchname = default_branch;
1365 if (strncmp(branchname, "refs/heads/", 11) == 0)
1366 branchname += 11;
1367 } else
1368 branchname = "*"; /* fall back to all branches */
1369 if (mirror_references) {
1370 if (asprintf(&branches,
1371 "\tfetch = refs/heads/%s:refs/heads/%s\n",
1372 branchname, branchname) == -1) {
1373 err = got_error_from_errno("asprintf");
1374 goto done;
1376 } else if (asprintf(&branches,
1377 "\tfetch = refs/heads/%s:refs/remotes/%s/%s\n",
1378 branchname, GOT_FETCH_DEFAULT_REMOTE_NAME,
1379 branchname) == -1) {
1380 err = got_error_from_errno("asprintf");
1381 goto done;
1384 if (!TAILQ_EMPTY(wanted_refs)) {
1385 struct got_pathlist_entry *pe;
1386 TAILQ_FOREACH(pe, wanted_refs, entry) {
1387 char *s;
1388 const char *refname = pe->path;
1389 if (strncmp(refname, "refs/", 5) == 0)
1390 refname += 5;
1391 if (mirror_references) {
1392 if (asprintf(&s,
1393 "%s\tfetch = refs/%s:refs/%s\n",
1394 refs ? refs : "", refname, refname) == -1) {
1395 err = got_error_from_errno("asprintf");
1396 goto done;
1398 } else if (asprintf(&s,
1399 "%s\tfetch = refs/%s:refs/remotes/%s/%s\n",
1400 refs ? refs : "",
1401 refname, GOT_FETCH_DEFAULT_REMOTE_NAME,
1402 refname) == -1) {
1403 err = got_error_from_errno("asprintf");
1404 goto done;
1406 free(refs);
1407 refs = s;
1411 if (asprintf(&gitconfig,
1412 "[remote \"%s\"]\n"
1413 "\turl = %s\n"
1414 "%s"
1415 "%s"
1416 "\tfetch = refs/tags/*:refs/tags/*\n",
1417 GOT_FETCH_DEFAULT_REMOTE_NAME, git_url, branches ? branches : "",
1418 refs ? refs : "") == -1) {
1419 err = got_error_from_errno("asprintf");
1420 goto done;
1422 n = fwrite(gitconfig, 1, strlen(gitconfig), gitconfig_file);
1423 if (n != strlen(gitconfig)) {
1424 err = got_ferror(gitconfig_file, GOT_ERR_IO);
1425 goto done;
1427 done:
1428 if (gitconfig_file && fclose(gitconfig_file) == EOF && err == NULL)
1429 err = got_error_from_errno2("fclose", gitconfig_path);
1430 free(gitconfig_path);
1431 free(branches);
1432 return err;
1435 static const struct got_error *
1436 create_config_files(const char *proto, const char *host, const char *port,
1437 const char *remote_repo_path, const char *git_url, int fetch_all_branches,
1438 int mirror_references, struct got_pathlist_head *symrefs,
1439 struct got_pathlist_head *wanted_branches,
1440 struct got_pathlist_head *wanted_refs, struct got_repository *repo)
1442 const struct got_error *err = NULL;
1443 const char *default_branch = NULL;
1444 struct got_pathlist_entry *pe;
1447 * If we asked for a set of wanted branches then use the first
1448 * one of those.
1450 if (!TAILQ_EMPTY(wanted_branches)) {
1451 pe = TAILQ_FIRST(wanted_branches);
1452 default_branch = pe->path;
1453 } else {
1454 /* First HEAD ref listed by server is the default branch. */
1455 TAILQ_FOREACH(pe, symrefs, entry) {
1456 const char *refname = pe->path;
1457 const char *target = pe->data;
1459 if (strcmp(refname, GOT_REF_HEAD) != 0)
1460 continue;
1462 default_branch = target;
1463 break;
1467 /* Create got.conf(5). */
1468 err = create_gotconfig(proto, host, port, remote_repo_path,
1469 default_branch, fetch_all_branches, wanted_branches,
1470 wanted_refs, mirror_references, repo);
1471 if (err)
1472 return err;
1474 /* Create a config file Git can understand. */
1475 return create_gitconfig(git_url, default_branch, fetch_all_branches,
1476 wanted_branches, wanted_refs, mirror_references, repo);
1479 static const struct got_error *
1480 cmd_clone(int argc, char *argv[])
1482 const struct got_error *error = NULL;
1483 const char *uri, *dirname;
1484 char *proto, *host, *port, *repo_name, *server_path;
1485 char *default_destdir = NULL, *id_str = NULL;
1486 const char *repo_path;
1487 struct got_repository *repo = NULL;
1488 struct got_pathlist_head refs, symrefs, wanted_branches, wanted_refs;
1489 struct got_pathlist_entry *pe;
1490 struct got_object_id *pack_hash = NULL;
1491 int ch, fetchfd = -1, fetchstatus;
1492 pid_t fetchpid = -1;
1493 struct got_fetch_progress_arg fpa;
1494 char *git_url = NULL;
1495 int verbosity = 0, fetch_all_branches = 0, mirror_references = 0;
1496 int list_refs_only = 0;
1497 int *pack_fds = NULL;
1499 TAILQ_INIT(&refs);
1500 TAILQ_INIT(&symrefs);
1501 TAILQ_INIT(&wanted_branches);
1502 TAILQ_INIT(&wanted_refs);
1504 while ((ch = getopt(argc, argv, "ab:lmvqR:")) != -1) {
1505 switch (ch) {
1506 case 'a':
1507 fetch_all_branches = 1;
1508 break;
1509 case 'b':
1510 error = got_pathlist_append(&wanted_branches,
1511 optarg, NULL);
1512 if (error)
1513 return error;
1514 break;
1515 case 'l':
1516 list_refs_only = 1;
1517 break;
1518 case 'm':
1519 mirror_references = 1;
1520 break;
1521 case 'v':
1522 if (verbosity < 0)
1523 verbosity = 0;
1524 else if (verbosity < 3)
1525 verbosity++;
1526 break;
1527 case 'q':
1528 verbosity = -1;
1529 break;
1530 case 'R':
1531 error = got_pathlist_append(&wanted_refs,
1532 optarg, NULL);
1533 if (error)
1534 return error;
1535 break;
1536 default:
1537 usage_clone();
1538 break;
1541 argc -= optind;
1542 argv += optind;
1544 if (fetch_all_branches && !TAILQ_EMPTY(&wanted_branches))
1545 option_conflict('a', 'b');
1546 if (list_refs_only) {
1547 if (!TAILQ_EMPTY(&wanted_branches))
1548 option_conflict('l', 'b');
1549 if (fetch_all_branches)
1550 option_conflict('l', 'a');
1551 if (mirror_references)
1552 option_conflict('l', 'm');
1553 if (!TAILQ_EMPTY(&wanted_refs))
1554 option_conflict('l', 'R');
1557 uri = argv[0];
1559 if (argc == 1)
1560 dirname = NULL;
1561 else if (argc == 2)
1562 dirname = argv[1];
1563 else
1564 usage_clone();
1566 error = got_dial_parse_uri(&proto, &host, &port, &server_path,
1567 &repo_name, uri);
1568 if (error)
1569 goto done;
1571 if (asprintf(&git_url, "%s://%s%s%s%s%s", proto,
1572 host, port ? ":" : "", port ? port : "",
1573 server_path[0] != '/' ? "/" : "", server_path) == -1) {
1574 error = got_error_from_errno("asprintf");
1575 goto done;
1578 if (strcmp(proto, "git") == 0) {
1579 #ifndef PROFILE
1580 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
1581 "sendfd dns inet unveil", NULL) == -1)
1582 err(1, "pledge");
1583 #endif
1584 } else if (strcmp(proto, "git+ssh") == 0 ||
1585 strcmp(proto, "ssh") == 0) {
1586 #ifndef PROFILE
1587 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
1588 "sendfd unveil", NULL) == -1)
1589 err(1, "pledge");
1590 #endif
1591 } else if (strcmp(proto, "http") == 0 ||
1592 strcmp(proto, "git+http") == 0) {
1593 error = got_error_path(proto, GOT_ERR_NOT_IMPL);
1594 goto done;
1595 } else {
1596 error = got_error_path(proto, GOT_ERR_BAD_PROTO);
1597 goto done;
1599 if (dirname == NULL) {
1600 if (asprintf(&default_destdir, "%s.git", repo_name) == -1) {
1601 error = got_error_from_errno("asprintf");
1602 goto done;
1604 repo_path = default_destdir;
1605 } else
1606 repo_path = dirname;
1608 if (!list_refs_only) {
1609 error = got_path_mkdir(repo_path);
1610 if (error &&
1611 (!(error->code == GOT_ERR_ERRNO && errno == EISDIR) &&
1612 !(error->code == GOT_ERR_ERRNO && errno == EEXIST)))
1613 goto done;
1614 if (!got_path_dir_is_empty(repo_path)) {
1615 error = got_error_path(repo_path,
1616 GOT_ERR_DIR_NOT_EMPTY);
1617 goto done;
1621 error = got_dial_apply_unveil(proto);
1622 if (error)
1623 goto done;
1625 error = apply_unveil(repo_path, 0, NULL);
1626 if (error)
1627 goto done;
1629 if (verbosity >= 0)
1630 printf("Connecting to %s%s%s\n", host,
1631 port ? ":" : "", port ? port : "");
1633 error = got_fetch_connect(&fetchpid, &fetchfd, proto, host, port,
1634 server_path, verbosity);
1635 if (error)
1636 goto done;
1638 if (!list_refs_only) {
1639 error = got_repo_init(repo_path);
1640 if (error)
1641 goto done;
1642 error = got_repo_pack_fds_open(&pack_fds);
1643 if (error != NULL)
1644 goto done;
1645 error = got_repo_open(&repo, repo_path, NULL, pack_fds);
1646 if (error)
1647 goto done;
1650 fpa.last_scaled_size[0] = '\0';
1651 fpa.last_p_indexed = -1;
1652 fpa.last_p_resolved = -1;
1653 fpa.verbosity = verbosity;
1654 fpa.create_configs = 1;
1655 fpa.configs_created = 0;
1656 fpa.repo = repo;
1657 fpa.config_info.symrefs = &symrefs;
1658 fpa.config_info.wanted_branches = &wanted_branches;
1659 fpa.config_info.wanted_refs = &wanted_refs;
1660 fpa.config_info.proto = proto;
1661 fpa.config_info.host = host;
1662 fpa.config_info.port = port;
1663 fpa.config_info.remote_repo_path = server_path;
1664 fpa.config_info.git_url = git_url;
1665 fpa.config_info.fetch_all_branches = fetch_all_branches;
1666 fpa.config_info.mirror_references = mirror_references;
1667 error = got_fetch_pack(&pack_hash, &refs, &symrefs,
1668 GOT_FETCH_DEFAULT_REMOTE_NAME, mirror_references,
1669 fetch_all_branches, &wanted_branches, &wanted_refs,
1670 list_refs_only, verbosity, fetchfd, repo,
1671 fetch_progress, &fpa);
1672 if (error)
1673 goto done;
1675 if (list_refs_only) {
1676 error = list_remote_refs(&symrefs, &refs);
1677 goto done;
1680 if (pack_hash == NULL) {
1681 error = got_error_fmt(GOT_ERR_FETCH_FAILED, "%s",
1682 "server sent an empty pack file");
1683 goto done;
1685 error = got_object_id_str(&id_str, pack_hash);
1686 if (error)
1687 goto done;
1688 if (verbosity >= 0)
1689 printf("\nFetched %s.pack\n", id_str);
1690 free(id_str);
1692 /* Set up references provided with the pack file. */
1693 TAILQ_FOREACH(pe, &refs, entry) {
1694 const char *refname = pe->path;
1695 struct got_object_id *id = pe->data;
1696 char *remote_refname;
1698 if (is_wanted_ref(&wanted_refs, refname) &&
1699 !mirror_references) {
1700 error = create_wanted_ref(refname, id,
1701 GOT_FETCH_DEFAULT_REMOTE_NAME,
1702 verbosity - 1, repo);
1703 if (error)
1704 goto done;
1705 continue;
1708 error = create_ref(refname, id, verbosity - 1, repo);
1709 if (error)
1710 goto done;
1712 if (mirror_references)
1713 continue;
1715 if (strncmp("refs/heads/", refname, 11) != 0)
1716 continue;
1718 if (asprintf(&remote_refname,
1719 "refs/remotes/%s/%s", GOT_FETCH_DEFAULT_REMOTE_NAME,
1720 refname + 11) == -1) {
1721 error = got_error_from_errno("asprintf");
1722 goto done;
1724 error = create_ref(remote_refname, id, verbosity - 1, repo);
1725 free(remote_refname);
1726 if (error)
1727 goto done;
1730 /* Set the HEAD reference if the server provided one. */
1731 TAILQ_FOREACH(pe, &symrefs, entry) {
1732 struct got_reference *target_ref;
1733 const char *refname = pe->path;
1734 const char *target = pe->data;
1735 char *remote_refname = NULL, *remote_target = NULL;
1737 if (strcmp(refname, GOT_REF_HEAD) != 0)
1738 continue;
1740 error = got_ref_open(&target_ref, repo, target, 0);
1741 if (error) {
1742 if (error->code == GOT_ERR_NOT_REF) {
1743 error = NULL;
1744 continue;
1746 goto done;
1749 error = create_symref(refname, target_ref, verbosity, repo);
1750 got_ref_close(target_ref);
1751 if (error)
1752 goto done;
1754 if (mirror_references)
1755 continue;
1757 if (strncmp("refs/heads/", target, 11) != 0)
1758 continue;
1760 if (asprintf(&remote_refname,
1761 "refs/remotes/%s/%s", GOT_FETCH_DEFAULT_REMOTE_NAME,
1762 refname) == -1) {
1763 error = got_error_from_errno("asprintf");
1764 goto done;
1766 if (asprintf(&remote_target,
1767 "refs/remotes/%s/%s", GOT_FETCH_DEFAULT_REMOTE_NAME,
1768 target + 11) == -1) {
1769 error = got_error_from_errno("asprintf");
1770 free(remote_refname);
1771 goto done;
1773 error = got_ref_open(&target_ref, repo, remote_target, 0);
1774 if (error) {
1775 free(remote_refname);
1776 free(remote_target);
1777 if (error->code == GOT_ERR_NOT_REF) {
1778 error = NULL;
1779 continue;
1781 goto done;
1783 error = create_symref(remote_refname, target_ref,
1784 verbosity - 1, repo);
1785 free(remote_refname);
1786 free(remote_target);
1787 got_ref_close(target_ref);
1788 if (error)
1789 goto done;
1791 if (pe == NULL) {
1793 * We failed to set the HEAD reference. If we asked for
1794 * a set of wanted branches use the first of one of those
1795 * which could be fetched instead.
1797 TAILQ_FOREACH(pe, &wanted_branches, entry) {
1798 const char *target = pe->path;
1799 struct got_reference *target_ref;
1801 error = got_ref_open(&target_ref, repo, target, 0);
1802 if (error) {
1803 if (error->code == GOT_ERR_NOT_REF) {
1804 error = NULL;
1805 continue;
1807 goto done;
1810 error = create_symref(GOT_REF_HEAD, target_ref,
1811 verbosity, repo);
1812 got_ref_close(target_ref);
1813 if (error)
1814 goto done;
1815 break;
1819 if (verbosity >= 0)
1820 printf("Created %s repository '%s'\n",
1821 mirror_references ? "mirrored" : "cloned", repo_path);
1822 done:
1823 if (pack_fds) {
1824 const struct got_error *pack_err =
1825 got_repo_pack_fds_close(pack_fds);
1826 if (error == NULL)
1827 error = pack_err;
1829 if (fetchpid > 0) {
1830 if (kill(fetchpid, SIGTERM) == -1)
1831 error = got_error_from_errno("kill");
1832 if (waitpid(fetchpid, &fetchstatus, 0) == -1 && error == NULL)
1833 error = got_error_from_errno("waitpid");
1835 if (fetchfd != -1 && close(fetchfd) == -1 && error == NULL)
1836 error = got_error_from_errno("close");
1837 if (repo) {
1838 const struct got_error *close_err = got_repo_close(repo);
1839 if (error == NULL)
1840 error = close_err;
1842 TAILQ_FOREACH(pe, &refs, entry) {
1843 free((void *)pe->path);
1844 free(pe->data);
1846 got_pathlist_free(&refs);
1847 TAILQ_FOREACH(pe, &symrefs, entry) {
1848 free((void *)pe->path);
1849 free(pe->data);
1851 got_pathlist_free(&symrefs);
1852 got_pathlist_free(&wanted_branches);
1853 got_pathlist_free(&wanted_refs);
1854 free(pack_hash);
1855 free(proto);
1856 free(host);
1857 free(port);
1858 free(server_path);
1859 free(repo_name);
1860 free(default_destdir);
1861 free(git_url);
1862 return error;
1865 static const struct got_error *
1866 update_ref(struct got_reference *ref, struct got_object_id *new_id,
1867 int replace_tags, int verbosity, struct got_repository *repo)
1869 const struct got_error *err = NULL;
1870 char *new_id_str = NULL;
1871 struct got_object_id *old_id = NULL;
1873 err = got_object_id_str(&new_id_str, new_id);
1874 if (err)
1875 goto done;
1877 if (!replace_tags &&
1878 strncmp(got_ref_get_name(ref), "refs/tags/", 10) == 0) {
1879 err = got_ref_resolve(&old_id, repo, ref);
1880 if (err)
1881 goto done;
1882 if (got_object_id_cmp(old_id, new_id) == 0)
1883 goto done;
1884 if (verbosity >= 0) {
1885 printf("Rejecting update of existing tag %s: %s\n",
1886 got_ref_get_name(ref), new_id_str);
1888 goto done;
1891 if (got_ref_is_symbolic(ref)) {
1892 if (verbosity >= 0) {
1893 printf("Replacing reference %s: %s\n",
1894 got_ref_get_name(ref),
1895 got_ref_get_symref_target(ref));
1897 err = got_ref_change_symref_to_ref(ref, new_id);
1898 if (err)
1899 goto done;
1900 err = got_ref_write(ref, repo);
1901 if (err)
1902 goto done;
1903 } else {
1904 err = got_ref_resolve(&old_id, repo, ref);
1905 if (err)
1906 goto done;
1907 if (got_object_id_cmp(old_id, new_id) == 0)
1908 goto done;
1910 err = got_ref_change_ref(ref, new_id);
1911 if (err)
1912 goto done;
1913 err = got_ref_write(ref, repo);
1914 if (err)
1915 goto done;
1918 if (verbosity >= 0)
1919 printf("Updated %s: %s\n", got_ref_get_name(ref),
1920 new_id_str);
1921 done:
1922 free(old_id);
1923 free(new_id_str);
1924 return err;
1927 static const struct got_error *
1928 update_symref(const char *refname, struct got_reference *target_ref,
1929 int verbosity, struct got_repository *repo)
1931 const struct got_error *err = NULL, *unlock_err;
1932 struct got_reference *symref;
1933 int symref_is_locked = 0;
1935 err = got_ref_open(&symref, repo, refname, 1);
1936 if (err) {
1937 if (err->code != GOT_ERR_NOT_REF)
1938 return err;
1939 err = got_ref_alloc_symref(&symref, refname, target_ref);
1940 if (err)
1941 goto done;
1943 err = got_ref_write(symref, repo);
1944 if (err)
1945 goto done;
1947 if (verbosity >= 0)
1948 printf("Created reference %s: %s\n",
1949 got_ref_get_name(symref),
1950 got_ref_get_symref_target(symref));
1951 } else {
1952 symref_is_locked = 1;
1954 if (strcmp(got_ref_get_symref_target(symref),
1955 got_ref_get_name(target_ref)) == 0)
1956 goto done;
1958 err = got_ref_change_symref(symref,
1959 got_ref_get_name(target_ref));
1960 if (err)
1961 goto done;
1963 err = got_ref_write(symref, repo);
1964 if (err)
1965 goto done;
1967 if (verbosity >= 0)
1968 printf("Updated %s: %s\n", got_ref_get_name(symref),
1969 got_ref_get_symref_target(symref));
1972 done:
1973 if (symref_is_locked) {
1974 unlock_err = got_ref_unlock(symref);
1975 if (unlock_err && err == NULL)
1976 err = unlock_err;
1978 got_ref_close(symref);
1979 return err;
1982 __dead static void
1983 usage_fetch(void)
1985 fprintf(stderr, "usage: %s fetch [-a] [-b branch] [-d] [-l] "
1986 "[-r repository-path] [-t] [-q] [-v] [-R reference] [-X] "
1987 "[remote-repository-name]\n",
1988 getprogname());
1989 exit(1);
1992 static const struct got_error *
1993 delete_missing_ref(struct got_reference *ref,
1994 int verbosity, struct got_repository *repo)
1996 const struct got_error *err = NULL;
1997 struct got_object_id *id = NULL;
1998 char *id_str = NULL;
2000 if (got_ref_is_symbolic(ref)) {
2001 err = got_ref_delete(ref, repo);
2002 if (err)
2003 return err;
2004 if (verbosity >= 0) {
2005 printf("Deleted %s: %s\n",
2006 got_ref_get_name(ref),
2007 got_ref_get_symref_target(ref));
2009 } else {
2010 err = got_ref_resolve(&id, repo, ref);
2011 if (err)
2012 return err;
2013 err = got_object_id_str(&id_str, id);
2014 if (err)
2015 goto done;
2017 err = got_ref_delete(ref, repo);
2018 if (err)
2019 goto done;
2020 if (verbosity >= 0) {
2021 printf("Deleted %s: %s\n",
2022 got_ref_get_name(ref), id_str);
2025 done:
2026 free(id);
2027 free(id_str);
2028 return NULL;
2031 static const struct got_error *
2032 delete_missing_refs(struct got_pathlist_head *their_refs,
2033 struct got_pathlist_head *their_symrefs,
2034 const struct got_remote_repo *remote,
2035 int verbosity, struct got_repository *repo)
2037 const struct got_error *err = NULL, *unlock_err;
2038 struct got_reflist_head my_refs;
2039 struct got_reflist_entry *re;
2040 struct got_pathlist_entry *pe;
2041 char *remote_namespace = NULL;
2042 char *local_refname = NULL;
2044 TAILQ_INIT(&my_refs);
2046 if (asprintf(&remote_namespace, "refs/remotes/%s/", remote->name)
2047 == -1)
2048 return got_error_from_errno("asprintf");
2050 err = got_ref_list(&my_refs, repo, NULL, got_ref_cmp_by_name, NULL);
2051 if (err)
2052 goto done;
2054 TAILQ_FOREACH(re, &my_refs, entry) {
2055 const char *refname = got_ref_get_name(re->ref);
2056 const char *their_refname;
2058 if (remote->mirror_references) {
2059 their_refname = refname;
2060 } else {
2061 if (strncmp(refname, remote_namespace,
2062 strlen(remote_namespace)) == 0) {
2063 if (strcmp(refname + strlen(remote_namespace),
2064 GOT_REF_HEAD) == 0)
2065 continue;
2066 if (asprintf(&local_refname, "refs/heads/%s",
2067 refname + strlen(remote_namespace)) == -1) {
2068 err = got_error_from_errno("asprintf");
2069 goto done;
2071 } else if (strncmp(refname, "refs/tags/", 10) != 0)
2072 continue;
2074 their_refname = local_refname;
2077 TAILQ_FOREACH(pe, their_refs, entry) {
2078 if (strcmp(their_refname, pe->path) == 0)
2079 break;
2081 if (pe != NULL)
2082 continue;
2084 TAILQ_FOREACH(pe, their_symrefs, entry) {
2085 if (strcmp(their_refname, pe->path) == 0)
2086 break;
2088 if (pe != NULL)
2089 continue;
2091 err = delete_missing_ref(re->ref, verbosity, repo);
2092 if (err)
2093 break;
2095 if (local_refname) {
2096 struct got_reference *ref;
2097 err = got_ref_open(&ref, repo, local_refname, 1);
2098 if (err) {
2099 if (err->code != GOT_ERR_NOT_REF)
2100 break;
2101 free(local_refname);
2102 local_refname = NULL;
2103 continue;
2105 err = delete_missing_ref(ref, verbosity, repo);
2106 if (err)
2107 break;
2108 unlock_err = got_ref_unlock(ref);
2109 got_ref_close(ref);
2110 if (unlock_err && err == NULL) {
2111 err = unlock_err;
2112 break;
2115 free(local_refname);
2116 local_refname = NULL;
2119 done:
2120 free(remote_namespace);
2121 free(local_refname);
2122 return err;
2125 static const struct got_error *
2126 update_wanted_ref(const char *refname, struct got_object_id *id,
2127 const char *remote_repo_name, int verbosity, struct got_repository *repo)
2129 const struct got_error *err, *unlock_err;
2130 char *remote_refname;
2131 struct got_reference *ref;
2133 if (strncmp("refs/", refname, 5) == 0)
2134 refname += 5;
2136 if (asprintf(&remote_refname, "refs/remotes/%s/%s",
2137 remote_repo_name, refname) == -1)
2138 return got_error_from_errno("asprintf");
2140 err = got_ref_open(&ref, repo, remote_refname, 1);
2141 if (err) {
2142 if (err->code != GOT_ERR_NOT_REF)
2143 goto done;
2144 err = create_ref(remote_refname, id, verbosity, repo);
2145 } else {
2146 err = update_ref(ref, id, 0, verbosity, repo);
2147 unlock_err = got_ref_unlock(ref);
2148 if (unlock_err && err == NULL)
2149 err = unlock_err;
2150 got_ref_close(ref);
2152 done:
2153 free(remote_refname);
2154 return err;
2157 static const struct got_error *
2158 delete_ref(struct got_repository *repo, struct got_reference *ref)
2160 const struct got_error *err = NULL;
2161 struct got_object_id *id = NULL;
2162 char *id_str = NULL;
2163 const char *target;
2165 if (got_ref_is_symbolic(ref)) {
2166 target = got_ref_get_symref_target(ref);
2167 } else {
2168 err = got_ref_resolve(&id, repo, ref);
2169 if (err)
2170 goto done;
2171 err = got_object_id_str(&id_str, id);
2172 if (err)
2173 goto done;
2174 target = id_str;
2177 err = got_ref_delete(ref, repo);
2178 if (err)
2179 goto done;
2181 printf("Deleted %s: %s\n", got_ref_get_name(ref), target);
2182 done:
2183 free(id);
2184 free(id_str);
2185 return err;
2188 static const struct got_error *
2189 delete_refs_for_remote(struct got_repository *repo, const char *remote_name)
2191 const struct got_error *err = NULL;
2192 struct got_reflist_head refs;
2193 struct got_reflist_entry *re;
2194 char *prefix;
2196 TAILQ_INIT(&refs);
2198 if (asprintf(&prefix, "refs/remotes/%s", remote_name) == -1) {
2199 err = got_error_from_errno("asprintf");
2200 goto done;
2202 err = got_ref_list(&refs, repo, prefix, got_ref_cmp_by_name, NULL);
2203 if (err)
2204 goto done;
2206 TAILQ_FOREACH(re, &refs, entry)
2207 delete_ref(repo, re->ref);
2208 done:
2209 got_ref_list_free(&refs);
2210 return err;
2213 static const struct got_error *
2214 cmd_fetch(int argc, char *argv[])
2216 const struct got_error *error = NULL, *unlock_err;
2217 char *cwd = NULL, *repo_path = NULL;
2218 const char *remote_name;
2219 char *proto = NULL, *host = NULL, *port = NULL;
2220 char *repo_name = NULL, *server_path = NULL;
2221 const struct got_remote_repo *remotes, *remote = NULL;
2222 int nremotes;
2223 char *id_str = NULL;
2224 struct got_repository *repo = NULL;
2225 struct got_worktree *worktree = NULL;
2226 const struct got_gotconfig *repo_conf = NULL, *worktree_conf = NULL;
2227 struct got_pathlist_head refs, symrefs, wanted_branches, wanted_refs;
2228 struct got_pathlist_entry *pe;
2229 struct got_object_id *pack_hash = NULL;
2230 int i, ch, fetchfd = -1, fetchstatus;
2231 pid_t fetchpid = -1;
2232 struct got_fetch_progress_arg fpa;
2233 int verbosity = 0, fetch_all_branches = 0, list_refs_only = 0;
2234 int delete_refs = 0, replace_tags = 0, delete_remote = 0;
2235 int *pack_fds = NULL;
2237 TAILQ_INIT(&refs);
2238 TAILQ_INIT(&symrefs);
2239 TAILQ_INIT(&wanted_branches);
2240 TAILQ_INIT(&wanted_refs);
2242 while ((ch = getopt(argc, argv, "ab:dlr:tvqR:X")) != -1) {
2243 switch (ch) {
2244 case 'a':
2245 fetch_all_branches = 1;
2246 break;
2247 case 'b':
2248 error = got_pathlist_append(&wanted_branches,
2249 optarg, NULL);
2250 if (error)
2251 return error;
2252 break;
2253 case 'd':
2254 delete_refs = 1;
2255 break;
2256 case 'l':
2257 list_refs_only = 1;
2258 break;
2259 case 'r':
2260 repo_path = realpath(optarg, NULL);
2261 if (repo_path == NULL)
2262 return got_error_from_errno2("realpath",
2263 optarg);
2264 got_path_strip_trailing_slashes(repo_path);
2265 break;
2266 case 't':
2267 replace_tags = 1;
2268 break;
2269 case 'v':
2270 if (verbosity < 0)
2271 verbosity = 0;
2272 else if (verbosity < 3)
2273 verbosity++;
2274 break;
2275 case 'q':
2276 verbosity = -1;
2277 break;
2278 case 'R':
2279 error = got_pathlist_append(&wanted_refs,
2280 optarg, NULL);
2281 if (error)
2282 return error;
2283 break;
2284 case 'X':
2285 delete_remote = 1;
2286 break;
2287 default:
2288 usage_fetch();
2289 break;
2292 argc -= optind;
2293 argv += optind;
2295 if (fetch_all_branches && !TAILQ_EMPTY(&wanted_branches))
2296 option_conflict('a', 'b');
2297 if (list_refs_only) {
2298 if (!TAILQ_EMPTY(&wanted_branches))
2299 option_conflict('l', 'b');
2300 if (fetch_all_branches)
2301 option_conflict('l', 'a');
2302 if (delete_refs)
2303 option_conflict('l', 'd');
2304 if (delete_remote)
2305 option_conflict('l', 'X');
2307 if (delete_remote) {
2308 if (fetch_all_branches)
2309 option_conflict('X', 'a');
2310 if (!TAILQ_EMPTY(&wanted_branches))
2311 option_conflict('X', 'b');
2312 if (delete_refs)
2313 option_conflict('X', 'd');
2314 if (replace_tags)
2315 option_conflict('X', 't');
2316 if (!TAILQ_EMPTY(&wanted_refs))
2317 option_conflict('X', 'R');
2320 if (argc == 0) {
2321 if (delete_remote)
2322 errx(1, "-X option requires a remote name");
2323 remote_name = GOT_FETCH_DEFAULT_REMOTE_NAME;
2324 } else if (argc == 1)
2325 remote_name = argv[0];
2326 else
2327 usage_fetch();
2329 cwd = getcwd(NULL, 0);
2330 if (cwd == NULL) {
2331 error = got_error_from_errno("getcwd");
2332 goto done;
2335 error = got_repo_pack_fds_open(&pack_fds);
2336 if (error != NULL)
2337 goto done;
2339 if (repo_path == NULL) {
2340 error = got_worktree_open(&worktree, cwd);
2341 if (error && error->code != GOT_ERR_NOT_WORKTREE)
2342 goto done;
2343 else
2344 error = NULL;
2345 if (worktree) {
2346 repo_path =
2347 strdup(got_worktree_get_repo_path(worktree));
2348 if (repo_path == NULL)
2349 error = got_error_from_errno("strdup");
2350 if (error)
2351 goto done;
2352 } else {
2353 repo_path = strdup(cwd);
2354 if (repo_path == NULL) {
2355 error = got_error_from_errno("strdup");
2356 goto done;
2361 error = got_repo_open(&repo, repo_path, NULL, pack_fds);
2362 if (error)
2363 goto done;
2365 if (delete_remote) {
2366 error = delete_refs_for_remote(repo, remote_name);
2367 goto done; /* nothing else to do */
2370 if (worktree) {
2371 worktree_conf = got_worktree_get_gotconfig(worktree);
2372 if (worktree_conf) {
2373 got_gotconfig_get_remotes(&nremotes, &remotes,
2374 worktree_conf);
2375 for (i = 0; i < nremotes; i++) {
2376 if (strcmp(remotes[i].name, remote_name) == 0) {
2377 remote = &remotes[i];
2378 break;
2383 if (remote == NULL) {
2384 repo_conf = got_repo_get_gotconfig(repo);
2385 if (repo_conf) {
2386 got_gotconfig_get_remotes(&nremotes, &remotes,
2387 repo_conf);
2388 for (i = 0; i < nremotes; i++) {
2389 if (strcmp(remotes[i].name, remote_name) == 0) {
2390 remote = &remotes[i];
2391 break;
2396 if (remote == NULL) {
2397 got_repo_get_gitconfig_remotes(&nremotes, &remotes, repo);
2398 for (i = 0; i < nremotes; i++) {
2399 if (strcmp(remotes[i].name, remote_name) == 0) {
2400 remote = &remotes[i];
2401 break;
2405 if (remote == NULL) {
2406 error = got_error_path(remote_name, GOT_ERR_NO_REMOTE);
2407 goto done;
2410 if (TAILQ_EMPTY(&wanted_branches)) {
2411 if (!fetch_all_branches)
2412 fetch_all_branches = remote->fetch_all_branches;
2413 for (i = 0; i < remote->nfetch_branches; i++) {
2414 got_pathlist_append(&wanted_branches,
2415 remote->fetch_branches[i], NULL);
2418 if (TAILQ_EMPTY(&wanted_refs)) {
2419 for (i = 0; i < remote->nfetch_refs; i++) {
2420 got_pathlist_append(&wanted_refs,
2421 remote->fetch_refs[i], NULL);
2425 error = got_dial_parse_uri(&proto, &host, &port, &server_path,
2426 &repo_name, remote->fetch_url);
2427 if (error)
2428 goto done;
2430 if (strcmp(proto, "git") == 0) {
2431 #ifndef PROFILE
2432 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
2433 "sendfd dns inet unveil", NULL) == -1)
2434 err(1, "pledge");
2435 #endif
2436 } else if (strcmp(proto, "git+ssh") == 0 ||
2437 strcmp(proto, "ssh") == 0) {
2438 #ifndef PROFILE
2439 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
2440 "sendfd unveil", NULL) == -1)
2441 err(1, "pledge");
2442 #endif
2443 } else if (strcmp(proto, "http") == 0 ||
2444 strcmp(proto, "git+http") == 0) {
2445 error = got_error_path(proto, GOT_ERR_NOT_IMPL);
2446 goto done;
2447 } else {
2448 error = got_error_path(proto, GOT_ERR_BAD_PROTO);
2449 goto done;
2452 error = got_dial_apply_unveil(proto);
2453 if (error)
2454 goto done;
2456 error = apply_unveil(got_repo_get_path(repo), 0, NULL);
2457 if (error)
2458 goto done;
2460 if (verbosity >= 0)
2461 printf("Connecting to \"%s\" %s%s%s\n", remote->name, host,
2462 port ? ":" : "", port ? port : "");
2464 error = got_fetch_connect(&fetchpid, &fetchfd, proto, host, port,
2465 server_path, verbosity);
2466 if (error)
2467 goto done;
2469 fpa.last_scaled_size[0] = '\0';
2470 fpa.last_p_indexed = -1;
2471 fpa.last_p_resolved = -1;
2472 fpa.verbosity = verbosity;
2473 fpa.repo = repo;
2474 fpa.create_configs = 0;
2475 fpa.configs_created = 0;
2476 memset(&fpa.config_info, 0, sizeof(fpa.config_info));
2477 error = got_fetch_pack(&pack_hash, &refs, &symrefs, remote->name,
2478 remote->mirror_references, fetch_all_branches, &wanted_branches,
2479 &wanted_refs, list_refs_only, verbosity, fetchfd, repo,
2480 fetch_progress, &fpa);
2481 if (error)
2482 goto done;
2484 if (list_refs_only) {
2485 error = list_remote_refs(&symrefs, &refs);
2486 goto done;
2489 if (pack_hash == NULL) {
2490 if (verbosity >= 0)
2491 printf("Already up-to-date\n");
2492 } else if (verbosity >= 0) {
2493 error = got_object_id_str(&id_str, pack_hash);
2494 if (error)
2495 goto done;
2496 printf("\nFetched %s.pack\n", id_str);
2497 free(id_str);
2498 id_str = NULL;
2501 /* Update references provided with the pack file. */
2502 TAILQ_FOREACH(pe, &refs, entry) {
2503 const char *refname = pe->path;
2504 struct got_object_id *id = pe->data;
2505 struct got_reference *ref;
2506 char *remote_refname;
2508 if (is_wanted_ref(&wanted_refs, refname) &&
2509 !remote->mirror_references) {
2510 error = update_wanted_ref(refname, id,
2511 remote->name, verbosity, repo);
2512 if (error)
2513 goto done;
2514 continue;
2517 if (remote->mirror_references ||
2518 strncmp("refs/tags/", refname, 10) == 0) {
2519 error = got_ref_open(&ref, repo, refname, 1);
2520 if (error) {
2521 if (error->code != GOT_ERR_NOT_REF)
2522 goto done;
2523 error = create_ref(refname, id, verbosity,
2524 repo);
2525 if (error)
2526 goto done;
2527 } else {
2528 error = update_ref(ref, id, replace_tags,
2529 verbosity, repo);
2530 unlock_err = got_ref_unlock(ref);
2531 if (unlock_err && error == NULL)
2532 error = unlock_err;
2533 got_ref_close(ref);
2534 if (error)
2535 goto done;
2537 } else if (strncmp("refs/heads/", refname, 11) == 0) {
2538 if (asprintf(&remote_refname, "refs/remotes/%s/%s",
2539 remote_name, refname + 11) == -1) {
2540 error = got_error_from_errno("asprintf");
2541 goto done;
2544 error = got_ref_open(&ref, repo, remote_refname, 1);
2545 if (error) {
2546 if (error->code != GOT_ERR_NOT_REF)
2547 goto done;
2548 error = create_ref(remote_refname, id,
2549 verbosity, repo);
2550 if (error)
2551 goto done;
2552 } else {
2553 error = update_ref(ref, id, replace_tags,
2554 verbosity, repo);
2555 unlock_err = got_ref_unlock(ref);
2556 if (unlock_err && error == NULL)
2557 error = unlock_err;
2558 got_ref_close(ref);
2559 if (error)
2560 goto done;
2563 /* Also create a local branch if none exists yet. */
2564 error = got_ref_open(&ref, repo, refname, 1);
2565 if (error) {
2566 if (error->code != GOT_ERR_NOT_REF)
2567 goto done;
2568 error = create_ref(refname, id, verbosity,
2569 repo);
2570 if (error)
2571 goto done;
2572 } else {
2573 unlock_err = got_ref_unlock(ref);
2574 if (unlock_err && error == NULL)
2575 error = unlock_err;
2576 got_ref_close(ref);
2580 if (delete_refs) {
2581 error = delete_missing_refs(&refs, &symrefs, remote,
2582 verbosity, repo);
2583 if (error)
2584 goto done;
2587 if (!remote->mirror_references) {
2588 /* Update remote HEAD reference if the server provided one. */
2589 TAILQ_FOREACH(pe, &symrefs, entry) {
2590 struct got_reference *target_ref;
2591 const char *refname = pe->path;
2592 const char *target = pe->data;
2593 char *remote_refname = NULL, *remote_target = NULL;
2595 if (strcmp(refname, GOT_REF_HEAD) != 0)
2596 continue;
2598 if (strncmp("refs/heads/", target, 11) != 0)
2599 continue;
2601 if (asprintf(&remote_refname, "refs/remotes/%s/%s",
2602 remote->name, refname) == -1) {
2603 error = got_error_from_errno("asprintf");
2604 goto done;
2606 if (asprintf(&remote_target, "refs/remotes/%s/%s",
2607 remote->name, target + 11) == -1) {
2608 error = got_error_from_errno("asprintf");
2609 free(remote_refname);
2610 goto done;
2613 error = got_ref_open(&target_ref, repo, remote_target,
2614 0);
2615 if (error) {
2616 free(remote_refname);
2617 free(remote_target);
2618 if (error->code == GOT_ERR_NOT_REF) {
2619 error = NULL;
2620 continue;
2622 goto done;
2624 error = update_symref(remote_refname, target_ref,
2625 verbosity, repo);
2626 free(remote_refname);
2627 free(remote_target);
2628 got_ref_close(target_ref);
2629 if (error)
2630 goto done;
2633 done:
2634 if (fetchpid > 0) {
2635 if (kill(fetchpid, SIGTERM) == -1)
2636 error = got_error_from_errno("kill");
2637 if (waitpid(fetchpid, &fetchstatus, 0) == -1 && error == NULL)
2638 error = got_error_from_errno("waitpid");
2640 if (fetchfd != -1 && close(fetchfd) == -1 && error == NULL)
2641 error = got_error_from_errno("close");
2642 if (repo) {
2643 const struct got_error *close_err = got_repo_close(repo);
2644 if (error == NULL)
2645 error = close_err;
2647 if (worktree)
2648 got_worktree_close(worktree);
2649 if (pack_fds) {
2650 const struct got_error *pack_err =
2651 got_repo_pack_fds_close(pack_fds);
2652 if (error == NULL)
2653 error = pack_err;
2655 TAILQ_FOREACH(pe, &refs, entry) {
2656 free((void *)pe->path);
2657 free(pe->data);
2659 got_pathlist_free(&refs);
2660 TAILQ_FOREACH(pe, &symrefs, entry) {
2661 free((void *)pe->path);
2662 free(pe->data);
2664 got_pathlist_free(&symrefs);
2665 got_pathlist_free(&wanted_branches);
2666 got_pathlist_free(&wanted_refs);
2667 free(id_str);
2668 free(cwd);
2669 free(repo_path);
2670 free(pack_hash);
2671 free(proto);
2672 free(host);
2673 free(port);
2674 free(server_path);
2675 free(repo_name);
2676 return error;
2680 __dead static void
2681 usage_checkout(void)
2683 fprintf(stderr, "usage: %s checkout [-E] [-b branch] [-c commit] "
2684 "[-p prefix] [-q] repository-path [worktree-path]\n",
2685 getprogname());
2686 exit(1);
2689 static void
2690 show_worktree_base_ref_warning(void)
2692 fprintf(stderr, "%s: warning: could not create a reference "
2693 "to the work tree's base commit; the commit could be "
2694 "garbage-collected by Git or 'gotadmin cleanup'; making the "
2695 "repository writable and running 'got update' will prevent this\n",
2696 getprogname());
2699 struct got_checkout_progress_arg {
2700 const char *worktree_path;
2701 int had_base_commit_ref_error;
2702 int verbosity;
2705 static const struct got_error *
2706 checkout_progress(void *arg, unsigned char status, const char *path)
2708 struct got_checkout_progress_arg *a = arg;
2710 /* Base commit bump happens silently. */
2711 if (status == GOT_STATUS_BUMP_BASE)
2712 return NULL;
2714 if (status == GOT_STATUS_BASE_REF_ERR) {
2715 a->had_base_commit_ref_error = 1;
2716 return NULL;
2719 while (path[0] == '/')
2720 path++;
2722 if (a->verbosity >= 0)
2723 printf("%c %s/%s\n", status, a->worktree_path, path);
2725 return NULL;
2728 static const struct got_error *
2729 check_cancelled(void *arg)
2731 if (sigint_received || sigpipe_received)
2732 return got_error(GOT_ERR_CANCELLED);
2733 return NULL;
2736 static const struct got_error *
2737 check_linear_ancestry(struct got_object_id *commit_id,
2738 struct got_object_id *base_commit_id, int allow_forwards_in_time_only,
2739 struct got_repository *repo)
2741 const struct got_error *err = NULL;
2742 struct got_object_id *yca_id;
2744 err = got_commit_graph_find_youngest_common_ancestor(&yca_id,
2745 commit_id, base_commit_id, 1, repo, check_cancelled, NULL);
2746 if (err)
2747 return err;
2749 if (yca_id == NULL)
2750 return got_error(GOT_ERR_ANCESTRY);
2753 * Require a straight line of history between the target commit
2754 * and the work tree's base commit.
2756 * Non-linear situations such as this require a rebase:
2758 * (commit) D F (base_commit)
2759 * \ /
2760 * C E
2761 * \ /
2762 * B (yca)
2763 * |
2764 * A
2766 * 'got update' only handles linear cases:
2767 * Update forwards in time: A (base/yca) - B - C - D (commit)
2768 * Update backwards in time: D (base) - C - B - A (commit/yca)
2770 if (allow_forwards_in_time_only) {
2771 if (got_object_id_cmp(base_commit_id, yca_id) != 0)
2772 return got_error(GOT_ERR_ANCESTRY);
2773 } else if (got_object_id_cmp(commit_id, yca_id) != 0 &&
2774 got_object_id_cmp(base_commit_id, yca_id) != 0)
2775 return got_error(GOT_ERR_ANCESTRY);
2777 free(yca_id);
2778 return NULL;
2781 static const struct got_error *
2782 check_same_branch(struct got_object_id *commit_id,
2783 struct got_reference *head_ref, struct got_object_id *yca_id,
2784 struct got_repository *repo)
2786 const struct got_error *err = NULL;
2787 struct got_commit_graph *graph = NULL;
2788 struct got_object_id *head_commit_id = NULL;
2789 int is_same_branch = 0;
2791 err = got_ref_resolve(&head_commit_id, repo, head_ref);
2792 if (err)
2793 goto done;
2795 if (got_object_id_cmp(head_commit_id, commit_id) == 0) {
2796 is_same_branch = 1;
2797 goto done;
2799 if (yca_id && got_object_id_cmp(commit_id, yca_id) == 0) {
2800 is_same_branch = 1;
2801 goto done;
2804 err = got_commit_graph_open(&graph, "/", 1);
2805 if (err)
2806 goto done;
2808 err = got_commit_graph_iter_start(graph, head_commit_id, repo,
2809 check_cancelled, NULL);
2810 if (err)
2811 goto done;
2813 for (;;) {
2814 struct got_object_id *id;
2815 err = got_commit_graph_iter_next(&id, graph, repo,
2816 check_cancelled, NULL);
2817 if (err) {
2818 if (err->code == GOT_ERR_ITER_COMPLETED)
2819 err = NULL;
2820 break;
2823 if (id) {
2824 if (yca_id && got_object_id_cmp(id, yca_id) == 0)
2825 break;
2826 if (got_object_id_cmp(id, commit_id) == 0) {
2827 is_same_branch = 1;
2828 break;
2832 done:
2833 if (graph)
2834 got_commit_graph_close(graph);
2835 free(head_commit_id);
2836 if (!err && !is_same_branch)
2837 err = got_error(GOT_ERR_ANCESTRY);
2838 return err;
2841 static const struct got_error *
2842 checkout_ancestry_error(struct got_reference *ref, const char *commit_id_str)
2844 static char msg[512];
2845 const char *branch_name;
2847 if (got_ref_is_symbolic(ref))
2848 branch_name = got_ref_get_symref_target(ref);
2849 else
2850 branch_name = got_ref_get_name(ref);
2852 if (strncmp("refs/heads/", branch_name, 11) == 0)
2853 branch_name += 11;
2855 snprintf(msg, sizeof(msg),
2856 "target commit is not contained in branch '%s'; "
2857 "the branch to use must be specified with -b; "
2858 "if necessary a new branch can be created for "
2859 "this commit with 'got branch -c %s BRANCH_NAME'",
2860 branch_name, commit_id_str);
2862 return got_error_msg(GOT_ERR_ANCESTRY, msg);
2865 static const struct got_error *
2866 cmd_checkout(int argc, char *argv[])
2868 const struct got_error *error = NULL;
2869 struct got_repository *repo = NULL;
2870 struct got_reference *head_ref = NULL, *ref = NULL;
2871 struct got_worktree *worktree = NULL;
2872 char *repo_path = NULL;
2873 char *worktree_path = NULL;
2874 const char *path_prefix = "";
2875 const char *branch_name = GOT_REF_HEAD, *refname = NULL;
2876 char *commit_id_str = NULL;
2877 struct got_object_id *commit_id = NULL;
2878 char *cwd = NULL;
2879 int ch, same_path_prefix, allow_nonempty = 0, verbosity = 0;
2880 struct got_pathlist_head paths;
2881 struct got_checkout_progress_arg cpa;
2882 int *pack_fds = NULL;
2884 TAILQ_INIT(&paths);
2886 while ((ch = getopt(argc, argv, "b:c:Ep:q")) != -1) {
2887 switch (ch) {
2888 case 'b':
2889 branch_name = optarg;
2890 break;
2891 case 'c':
2892 commit_id_str = strdup(optarg);
2893 if (commit_id_str == NULL)
2894 return got_error_from_errno("strdup");
2895 break;
2896 case 'E':
2897 allow_nonempty = 1;
2898 break;
2899 case 'p':
2900 path_prefix = optarg;
2901 break;
2902 case 'q':
2903 verbosity = -1;
2904 break;
2905 default:
2906 usage_checkout();
2907 /* NOTREACHED */
2911 argc -= optind;
2912 argv += optind;
2914 #ifndef PROFILE
2915 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
2916 "unveil", NULL) == -1)
2917 err(1, "pledge");
2918 #endif
2919 if (argc == 1) {
2920 char *base, *dotgit;
2921 const char *path;
2922 repo_path = realpath(argv[0], NULL);
2923 if (repo_path == NULL)
2924 return got_error_from_errno2("realpath", argv[0]);
2925 cwd = getcwd(NULL, 0);
2926 if (cwd == NULL) {
2927 error = got_error_from_errno("getcwd");
2928 goto done;
2930 if (path_prefix[0])
2931 path = path_prefix;
2932 else
2933 path = repo_path;
2934 error = got_path_basename(&base, path);
2935 if (error)
2936 goto done;
2937 dotgit = strstr(base, ".git");
2938 if (dotgit)
2939 *dotgit = '\0';
2940 if (asprintf(&worktree_path, "%s/%s", cwd, base) == -1) {
2941 error = got_error_from_errno("asprintf");
2942 free(base);
2943 goto done;
2945 free(base);
2946 } else if (argc == 2) {
2947 repo_path = realpath(argv[0], NULL);
2948 if (repo_path == NULL) {
2949 error = got_error_from_errno2("realpath", argv[0]);
2950 goto done;
2952 worktree_path = realpath(argv[1], NULL);
2953 if (worktree_path == NULL) {
2954 if (errno != ENOENT) {
2955 error = got_error_from_errno2("realpath",
2956 argv[1]);
2957 goto done;
2959 worktree_path = strdup(argv[1]);
2960 if (worktree_path == NULL) {
2961 error = got_error_from_errno("strdup");
2962 goto done;
2965 } else
2966 usage_checkout();
2968 got_path_strip_trailing_slashes(repo_path);
2969 got_path_strip_trailing_slashes(worktree_path);
2971 error = got_repo_pack_fds_open(&pack_fds);
2972 if (error != NULL)
2973 goto done;
2975 error = got_repo_open(&repo, repo_path, NULL, pack_fds);
2976 if (error != NULL)
2977 goto done;
2979 /* Pre-create work tree path for unveil(2) */
2980 error = got_path_mkdir(worktree_path);
2981 if (error) {
2982 if (!(error->code == GOT_ERR_ERRNO && errno == EISDIR) &&
2983 !(error->code == GOT_ERR_ERRNO && errno == EEXIST))
2984 goto done;
2985 if (!allow_nonempty &&
2986 !got_path_dir_is_empty(worktree_path)) {
2987 error = got_error_path(worktree_path,
2988 GOT_ERR_DIR_NOT_EMPTY);
2989 goto done;
2993 error = apply_unveil(got_repo_get_path(repo), 0, worktree_path);
2994 if (error)
2995 goto done;
2997 error = got_ref_open(&head_ref, repo, branch_name, 0);
2998 if (error != NULL)
2999 goto done;
3001 error = got_worktree_init(worktree_path, head_ref, path_prefix, repo);
3002 if (error != NULL && !(error->code == GOT_ERR_ERRNO && errno == EEXIST))
3003 goto done;
3005 error = got_worktree_open(&worktree, worktree_path);
3006 if (error != NULL)
3007 goto done;
3009 error = got_worktree_match_path_prefix(&same_path_prefix, worktree,
3010 path_prefix);
3011 if (error != NULL)
3012 goto done;
3013 if (!same_path_prefix) {
3014 error = got_error(GOT_ERR_PATH_PREFIX);
3015 goto done;
3018 if (commit_id_str) {
3019 struct got_reflist_head refs;
3020 TAILQ_INIT(&refs);
3021 error = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name,
3022 NULL);
3023 if (error)
3024 goto done;
3025 error = got_repo_match_object_id(&commit_id, NULL,
3026 commit_id_str, GOT_OBJ_TYPE_COMMIT, &refs, repo);
3027 got_ref_list_free(&refs);
3028 if (error)
3029 goto done;
3030 error = check_linear_ancestry(commit_id,
3031 got_worktree_get_base_commit_id(worktree), 0, repo);
3032 if (error != NULL) {
3033 if (error->code == GOT_ERR_ANCESTRY) {
3034 error = checkout_ancestry_error(
3035 head_ref, commit_id_str);
3037 goto done;
3039 error = check_same_branch(commit_id, head_ref, NULL, repo);
3040 if (error) {
3041 if (error->code == GOT_ERR_ANCESTRY) {
3042 error = checkout_ancestry_error(
3043 head_ref, commit_id_str);
3045 goto done;
3047 error = got_worktree_set_base_commit_id(worktree, repo,
3048 commit_id);
3049 if (error)
3050 goto done;
3051 /* Expand potentially abbreviated commit ID string. */
3052 free(commit_id_str);
3053 error = got_object_id_str(&commit_id_str, commit_id);
3054 if (error)
3055 goto done;
3056 } else {
3057 commit_id = got_object_id_dup(
3058 got_worktree_get_base_commit_id(worktree));
3059 if (commit_id == NULL) {
3060 error = got_error_from_errno("got_object_id_dup");
3061 goto done;
3063 error = got_object_id_str(&commit_id_str, commit_id);
3064 if (error)
3065 goto done;
3068 error = got_pathlist_append(&paths, "", NULL);
3069 if (error)
3070 goto done;
3071 cpa.worktree_path = worktree_path;
3072 cpa.had_base_commit_ref_error = 0;
3073 cpa.verbosity = verbosity;
3074 error = got_worktree_checkout_files(worktree, &paths, repo,
3075 checkout_progress, &cpa, check_cancelled, NULL);
3076 if (error != NULL)
3077 goto done;
3079 if (got_ref_is_symbolic(head_ref)) {
3080 error = got_ref_resolve_symbolic(&ref, repo, head_ref);
3081 if (error)
3082 goto done;
3083 refname = got_ref_get_name(ref);
3084 } else
3085 refname = got_ref_get_name(head_ref);
3086 printf("Checked out %s: %s\n", refname, commit_id_str);
3087 printf("Now shut up and hack\n");
3088 if (cpa.had_base_commit_ref_error)
3089 show_worktree_base_ref_warning();
3090 done:
3091 if (pack_fds) {
3092 const struct got_error *pack_err =
3093 got_repo_pack_fds_close(pack_fds);
3094 if (error == NULL)
3095 error = pack_err;
3097 if (head_ref)
3098 got_ref_close(head_ref);
3099 if (ref)
3100 got_ref_close(ref);
3101 got_pathlist_free(&paths);
3102 free(commit_id_str);
3103 free(commit_id);
3104 free(repo_path);
3105 free(worktree_path);
3106 free(cwd);
3107 return error;
3110 struct got_update_progress_arg {
3111 int did_something;
3112 int conflicts;
3113 int obstructed;
3114 int not_updated;
3115 int missing;
3116 int not_deleted;
3117 int unversioned;
3118 int verbosity;
3121 void
3122 print_update_progress_stats(struct got_update_progress_arg *upa)
3124 if (!upa->did_something)
3125 return;
3127 if (upa->conflicts > 0)
3128 printf("Files with new merge conflicts: %d\n", upa->conflicts);
3129 if (upa->obstructed > 0)
3130 printf("File paths obstructed by a non-regular file: %d\n",
3131 upa->obstructed);
3132 if (upa->not_updated > 0)
3133 printf("Files not updated because of existing merge "
3134 "conflicts: %d\n", upa->not_updated);
3138 * The meaning of some status codes differs between merge-style operations and
3139 * update operations. For example, the ! status code means "file was missing"
3140 * if changes were merged into the work tree, and "missing file was restored"
3141 * if the work tree was updated. This function should be used by any operation
3142 * which merges changes into the work tree without updating the work tree.
3144 void
3145 print_merge_progress_stats(struct got_update_progress_arg *upa)
3147 if (!upa->did_something)
3148 return;
3150 if (upa->conflicts > 0)
3151 printf("Files with new merge conflicts: %d\n", upa->conflicts);
3152 if (upa->obstructed > 0)
3153 printf("File paths obstructed by a non-regular file: %d\n",
3154 upa->obstructed);
3155 if (upa->missing > 0)
3156 printf("Files which had incoming changes but could not be "
3157 "found in the work tree: %d\n", upa->missing);
3158 if (upa->not_deleted > 0)
3159 printf("Files not deleted due to differences in deleted "
3160 "content: %d\n", upa->not_deleted);
3161 if (upa->unversioned > 0)
3162 printf("Files not merged because an unversioned file was "
3163 "found in the work tree: %d\n", upa->unversioned);
3166 __dead static void
3167 usage_update(void)
3169 fprintf(stderr, "usage: %s update [-b branch] [-c commit] [-q] "
3170 "[path ...]\n",
3171 getprogname());
3172 exit(1);
3175 static const struct got_error *
3176 update_progress(void *arg, unsigned char status, const char *path)
3178 struct got_update_progress_arg *upa = arg;
3180 if (status == GOT_STATUS_EXISTS ||
3181 status == GOT_STATUS_BASE_REF_ERR)
3182 return NULL;
3184 upa->did_something = 1;
3186 /* Base commit bump happens silently. */
3187 if (status == GOT_STATUS_BUMP_BASE)
3188 return NULL;
3190 if (status == GOT_STATUS_CONFLICT)
3191 upa->conflicts++;
3192 if (status == GOT_STATUS_OBSTRUCTED)
3193 upa->obstructed++;
3194 if (status == GOT_STATUS_CANNOT_UPDATE)
3195 upa->not_updated++;
3196 if (status == GOT_STATUS_MISSING)
3197 upa->missing++;
3198 if (status == GOT_STATUS_CANNOT_DELETE)
3199 upa->not_deleted++;
3200 if (status == GOT_STATUS_UNVERSIONED)
3201 upa->unversioned++;
3203 while (path[0] == '/')
3204 path++;
3205 if (upa->verbosity >= 0)
3206 printf("%c %s\n", status, path);
3208 return NULL;
3211 static const struct got_error *
3212 switch_head_ref(struct got_reference *head_ref,
3213 struct got_object_id *commit_id, struct got_worktree *worktree,
3214 struct got_repository *repo)
3216 const struct got_error *err = NULL;
3217 char *base_id_str;
3218 int ref_has_moved = 0;
3220 /* Trivial case: switching between two different references. */
3221 if (strcmp(got_ref_get_name(head_ref),
3222 got_worktree_get_head_ref_name(worktree)) != 0) {
3223 printf("Switching work tree from %s to %s\n",
3224 got_worktree_get_head_ref_name(worktree),
3225 got_ref_get_name(head_ref));
3226 return got_worktree_set_head_ref(worktree, head_ref);
3229 err = check_linear_ancestry(commit_id,
3230 got_worktree_get_base_commit_id(worktree), 0, repo);
3231 if (err) {
3232 if (err->code != GOT_ERR_ANCESTRY)
3233 return err;
3234 ref_has_moved = 1;
3236 if (!ref_has_moved)
3237 return NULL;
3239 /* Switching to a rebased branch with the same reference name. */
3240 err = got_object_id_str(&base_id_str,
3241 got_worktree_get_base_commit_id(worktree));
3242 if (err)
3243 return err;
3244 printf("Reference %s now points at a different branch\n",
3245 got_worktree_get_head_ref_name(worktree));
3246 printf("Switching work tree from %s to %s\n", base_id_str,
3247 got_worktree_get_head_ref_name(worktree));
3248 return NULL;
3251 static const struct got_error *
3252 check_rebase_or_histedit_in_progress(struct got_worktree *worktree)
3254 const struct got_error *err;
3255 int in_progress;
3257 err = got_worktree_rebase_in_progress(&in_progress, worktree);
3258 if (err)
3259 return err;
3260 if (in_progress)
3261 return got_error(GOT_ERR_REBASING);
3263 err = got_worktree_histedit_in_progress(&in_progress, worktree);
3264 if (err)
3265 return err;
3266 if (in_progress)
3267 return got_error(GOT_ERR_HISTEDIT_BUSY);
3269 return NULL;
3272 static const struct got_error *
3273 check_merge_in_progress(struct got_worktree *worktree,
3274 struct got_repository *repo)
3276 const struct got_error *err;
3277 int in_progress;
3279 err = got_worktree_merge_in_progress(&in_progress, worktree, repo);
3280 if (err)
3281 return err;
3282 if (in_progress)
3283 return got_error(GOT_ERR_MERGE_BUSY);
3285 return NULL;
3288 static const struct got_error *
3289 get_worktree_paths_from_argv(struct got_pathlist_head *paths, int argc,
3290 char *argv[], struct got_worktree *worktree)
3292 const struct got_error *err = NULL;
3293 char *path;
3294 struct got_pathlist_entry *new;
3295 int i;
3297 if (argc == 0) {
3298 path = strdup("");
3299 if (path == NULL)
3300 return got_error_from_errno("strdup");
3301 return got_pathlist_append(paths, path, NULL);
3304 for (i = 0; i < argc; i++) {
3305 err = got_worktree_resolve_path(&path, worktree, argv[i]);
3306 if (err)
3307 break;
3308 err = got_pathlist_insert(&new, paths, path, NULL);
3309 if (err || new == NULL /* duplicate */) {
3310 free(path);
3311 if (err)
3312 break;
3316 return err;
3319 static const struct got_error *
3320 wrap_not_worktree_error(const struct got_error *orig_err,
3321 const char *cmdname, const char *path)
3323 const struct got_error *err;
3324 struct got_repository *repo;
3325 static char msg[512];
3326 int *pack_fds = NULL;
3328 err = got_repo_pack_fds_open(&pack_fds);
3329 if (err)
3330 return err;
3332 err = got_repo_open(&repo, path, NULL, pack_fds);
3333 if (err)
3334 return orig_err;
3336 snprintf(msg, sizeof(msg),
3337 "'got %s' needs a work tree in addition to a git repository\n"
3338 "Work trees can be checked out from this Git repository with "
3339 "'got checkout'.\n"
3340 "The got(1) manual page contains more information.", cmdname);
3341 err = got_error_msg(GOT_ERR_NOT_WORKTREE, msg);
3342 got_repo_close(repo);
3343 if (pack_fds) {
3344 const struct got_error *pack_err =
3345 got_repo_pack_fds_close(pack_fds);
3346 if (err == NULL)
3347 err = pack_err;
3349 return err;
3352 static const struct got_error *
3353 cmd_update(int argc, char *argv[])
3355 const struct got_error *error = NULL;
3356 struct got_repository *repo = NULL;
3357 struct got_worktree *worktree = NULL;
3358 char *worktree_path = NULL;
3359 struct got_object_id *commit_id = NULL;
3360 char *commit_id_str = NULL;
3361 const char *branch_name = NULL;
3362 struct got_reference *head_ref = NULL;
3363 struct got_pathlist_head paths;
3364 struct got_pathlist_entry *pe;
3365 int ch, verbosity = 0;
3366 struct got_update_progress_arg upa;
3367 int *pack_fds = NULL;
3369 TAILQ_INIT(&paths);
3371 while ((ch = getopt(argc, argv, "b:c:q")) != -1) {
3372 switch (ch) {
3373 case 'b':
3374 branch_name = optarg;
3375 break;
3376 case 'c':
3377 commit_id_str = strdup(optarg);
3378 if (commit_id_str == NULL)
3379 return got_error_from_errno("strdup");
3380 break;
3381 case 'q':
3382 verbosity = -1;
3383 break;
3384 default:
3385 usage_update();
3386 /* NOTREACHED */
3390 argc -= optind;
3391 argv += optind;
3393 #ifndef PROFILE
3394 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
3395 "unveil", NULL) == -1)
3396 err(1, "pledge");
3397 #endif
3398 worktree_path = getcwd(NULL, 0);
3399 if (worktree_path == NULL) {
3400 error = got_error_from_errno("getcwd");
3401 goto done;
3404 error = got_repo_pack_fds_open(&pack_fds);
3405 if (error != NULL)
3406 goto done;
3408 error = got_worktree_open(&worktree, worktree_path);
3409 if (error) {
3410 if (error->code == GOT_ERR_NOT_WORKTREE)
3411 error = wrap_not_worktree_error(error, "update",
3412 worktree_path);
3413 goto done;
3416 error = check_rebase_or_histedit_in_progress(worktree);
3417 if (error)
3418 goto done;
3420 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
3421 NULL, pack_fds);
3422 if (error != NULL)
3423 goto done;
3425 error = apply_unveil(got_repo_get_path(repo), 0,
3426 got_worktree_get_root_path(worktree));
3427 if (error)
3428 goto done;
3430 error = check_merge_in_progress(worktree, repo);
3431 if (error)
3432 goto done;
3434 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
3435 if (error)
3436 goto done;
3438 error = got_ref_open(&head_ref, repo, branch_name ? branch_name :
3439 got_worktree_get_head_ref_name(worktree), 0);
3440 if (error != NULL)
3441 goto done;
3442 if (commit_id_str == NULL) {
3443 error = got_ref_resolve(&commit_id, repo, head_ref);
3444 if (error != NULL)
3445 goto done;
3446 error = got_object_id_str(&commit_id_str, commit_id);
3447 if (error != NULL)
3448 goto done;
3449 } else {
3450 struct got_reflist_head refs;
3451 TAILQ_INIT(&refs);
3452 error = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name,
3453 NULL);
3454 if (error)
3455 goto done;
3456 error = got_repo_match_object_id(&commit_id, NULL,
3457 commit_id_str, GOT_OBJ_TYPE_COMMIT, &refs, repo);
3458 got_ref_list_free(&refs);
3459 free(commit_id_str);
3460 commit_id_str = NULL;
3461 if (error)
3462 goto done;
3463 error = got_object_id_str(&commit_id_str, commit_id);
3464 if (error)
3465 goto done;
3468 if (branch_name) {
3469 struct got_object_id *head_commit_id;
3470 TAILQ_FOREACH(pe, &paths, entry) {
3471 if (pe->path_len == 0)
3472 continue;
3473 error = got_error_msg(GOT_ERR_BAD_PATH,
3474 "switching between branches requires that "
3475 "the entire work tree gets updated");
3476 goto done;
3478 error = got_ref_resolve(&head_commit_id, repo, head_ref);
3479 if (error)
3480 goto done;
3481 error = check_linear_ancestry(commit_id, head_commit_id, 0,
3482 repo);
3483 free(head_commit_id);
3484 if (error != NULL)
3485 goto done;
3486 error = check_same_branch(commit_id, head_ref, NULL, repo);
3487 if (error)
3488 goto done;
3489 error = switch_head_ref(head_ref, commit_id, worktree, repo);
3490 if (error)
3491 goto done;
3492 } else {
3493 error = check_linear_ancestry(commit_id,
3494 got_worktree_get_base_commit_id(worktree), 0, repo);
3495 if (error != NULL) {
3496 if (error->code == GOT_ERR_ANCESTRY)
3497 error = got_error(GOT_ERR_BRANCH_MOVED);
3498 goto done;
3500 error = check_same_branch(commit_id, head_ref, NULL, repo);
3501 if (error)
3502 goto done;
3505 if (got_object_id_cmp(got_worktree_get_base_commit_id(worktree),
3506 commit_id) != 0) {
3507 error = got_worktree_set_base_commit_id(worktree, repo,
3508 commit_id);
3509 if (error)
3510 goto done;
3513 memset(&upa, 0, sizeof(upa));
3514 upa.verbosity = verbosity;
3515 error = got_worktree_checkout_files(worktree, &paths, repo,
3516 update_progress, &upa, check_cancelled, NULL);
3517 if (error != NULL)
3518 goto done;
3520 if (upa.did_something) {
3521 printf("Updated to %s: %s\n",
3522 got_worktree_get_head_ref_name(worktree), commit_id_str);
3523 } else
3524 printf("Already up-to-date\n");
3526 print_update_progress_stats(&upa);
3527 done:
3528 if (pack_fds) {
3529 const struct got_error *pack_err =
3530 got_repo_pack_fds_close(pack_fds);
3531 if (error == NULL)
3532 error = pack_err;
3534 free(worktree_path);
3535 TAILQ_FOREACH(pe, &paths, entry)
3536 free((char *)pe->path);
3537 got_pathlist_free(&paths);
3538 free(commit_id);
3539 free(commit_id_str);
3540 return error;
3543 static const struct got_error *
3544 diff_blobs(struct got_object_id *blob_id1, struct got_object_id *blob_id2,
3545 const char *path, int diff_context, int ignore_whitespace,
3546 int force_text_diff, struct got_repository *repo, FILE *outfile)
3548 const struct got_error *err = NULL;
3549 struct got_blob_object *blob1 = NULL, *blob2 = NULL;
3550 FILE *f1 = NULL, *f2 = NULL;
3552 if (blob_id1) {
3553 err = got_object_open_as_blob(&blob1, repo, blob_id1, 8192);
3554 if (err)
3555 goto done;
3556 f1 = got_opentemp();
3557 if (f1 == NULL) {
3558 err = got_error_from_errno("got_opentemp");
3559 goto done;
3563 err = got_object_open_as_blob(&blob2, repo, blob_id2, 8192);
3564 if (err)
3565 goto done;
3567 f2 = got_opentemp();
3568 if (f2 == NULL) {
3569 err = got_error_from_errno("got_opentemp");
3570 goto done;
3573 while (path[0] == '/')
3574 path++;
3575 err = got_diff_blob(NULL, NULL, blob1, blob2, f1, f2, path, path,
3576 diff_context, ignore_whitespace, force_text_diff, outfile);
3577 done:
3578 if (blob1)
3579 got_object_blob_close(blob1);
3580 got_object_blob_close(blob2);
3581 if (f1 && fclose(f1) == EOF && err == NULL)
3582 err = got_error_from_errno("fclose");
3583 if (f2 && fclose(f2) == EOF && err == NULL)
3584 err = got_error_from_errno("fclose");
3585 return err;
3588 static const struct got_error *
3589 diff_trees(struct got_object_id *tree_id1, struct got_object_id *tree_id2,
3590 const char *path, int diff_context, int ignore_whitespace,
3591 int force_text_diff, struct got_repository *repo, FILE *outfile)
3593 const struct got_error *err = NULL;
3594 struct got_tree_object *tree1 = NULL, *tree2 = NULL;
3595 struct got_diff_blob_output_unidiff_arg arg;
3596 FILE *f1 = NULL, *f2 = NULL;
3598 if (tree_id1) {
3599 err = got_object_open_as_tree(&tree1, repo, tree_id1);
3600 if (err)
3601 goto done;
3602 f1 = got_opentemp();
3603 if (f1 == NULL) {
3604 err = got_error_from_errno("got_opentemp");
3605 goto done;
3609 err = got_object_open_as_tree(&tree2, repo, tree_id2);
3610 if (err)
3611 goto done;
3613 f2 = got_opentemp();
3614 if (f2 == NULL) {
3615 err = got_error_from_errno("got_opentemp");
3616 goto done;
3619 arg.diff_context = diff_context;
3620 arg.ignore_whitespace = ignore_whitespace;
3621 arg.force_text_diff = force_text_diff;
3622 arg.outfile = outfile;
3623 arg.line_offsets = NULL;
3624 arg.nlines = 0;
3625 while (path[0] == '/')
3626 path++;
3627 err = got_diff_tree(tree1, tree2, f1, f2, path, path, repo,
3628 got_diff_blob_output_unidiff, &arg, 1);
3629 done:
3630 if (tree1)
3631 got_object_tree_close(tree1);
3632 if (tree2)
3633 got_object_tree_close(tree2);
3634 if (f1 && fclose(f1) == EOF && err == NULL)
3635 err = got_error_from_errno("fclose");
3636 if (f2 && fclose(f2) == EOF && err == NULL)
3637 err = got_error_from_errno("fclose");
3638 return err;
3641 static const struct got_error *
3642 get_changed_paths(struct got_pathlist_head *paths,
3643 struct got_commit_object *commit, struct got_repository *repo)
3645 const struct got_error *err = NULL;
3646 struct got_object_id *tree_id1 = NULL, *tree_id2 = NULL;
3647 struct got_tree_object *tree1 = NULL, *tree2 = NULL;
3648 struct got_object_qid *qid;
3650 qid = STAILQ_FIRST(got_object_commit_get_parent_ids(commit));
3651 if (qid != NULL) {
3652 struct got_commit_object *pcommit;
3653 err = got_object_open_as_commit(&pcommit, repo,
3654 &qid->id);
3655 if (err)
3656 return err;
3658 tree_id1 = got_object_id_dup(
3659 got_object_commit_get_tree_id(pcommit));
3660 if (tree_id1 == NULL) {
3661 got_object_commit_close(pcommit);
3662 return got_error_from_errno("got_object_id_dup");
3664 got_object_commit_close(pcommit);
3668 if (tree_id1) {
3669 err = got_object_open_as_tree(&tree1, repo, tree_id1);
3670 if (err)
3671 goto done;
3674 tree_id2 = got_object_commit_get_tree_id(commit);
3675 err = got_object_open_as_tree(&tree2, repo, tree_id2);
3676 if (err)
3677 goto done;
3679 err = got_diff_tree(tree1, tree2, NULL, NULL, "", "", repo,
3680 got_diff_tree_collect_changed_paths, paths, 0);
3681 done:
3682 if (tree1)
3683 got_object_tree_close(tree1);
3684 if (tree2)
3685 got_object_tree_close(tree2);
3686 free(tree_id1);
3687 return err;
3690 static const struct got_error *
3691 print_patch(struct got_commit_object *commit, struct got_object_id *id,
3692 const char *path, int diff_context, struct got_repository *repo,
3693 FILE *outfile)
3695 const struct got_error *err = NULL;
3696 struct got_commit_object *pcommit = NULL;
3697 char *id_str1 = NULL, *id_str2 = NULL;
3698 struct got_object_id *obj_id1 = NULL, *obj_id2 = NULL;
3699 struct got_object_qid *qid;
3701 qid = STAILQ_FIRST(got_object_commit_get_parent_ids(commit));
3702 if (qid != NULL) {
3703 err = got_object_open_as_commit(&pcommit, repo,
3704 &qid->id);
3705 if (err)
3706 return err;
3709 if (path && path[0] != '\0') {
3710 int obj_type;
3711 err = got_object_id_by_path(&obj_id2, repo, commit, path);
3712 if (err)
3713 goto done;
3714 err = got_object_id_str(&id_str2, obj_id2);
3715 if (err) {
3716 free(obj_id2);
3717 goto done;
3719 if (pcommit) {
3720 err = got_object_id_by_path(&obj_id1, repo,
3721 pcommit, path);
3722 if (err) {
3723 if (err->code != GOT_ERR_NO_TREE_ENTRY) {
3724 free(obj_id2);
3725 goto done;
3727 } else {
3728 err = got_object_id_str(&id_str1, obj_id1);
3729 if (err) {
3730 free(obj_id2);
3731 goto done;
3735 err = got_object_get_type(&obj_type, repo, obj_id2);
3736 if (err) {
3737 free(obj_id2);
3738 goto done;
3740 fprintf(outfile,
3741 "diff %s %s\n", id_str1 ? id_str1 : "/dev/null", id_str2);
3742 switch (obj_type) {
3743 case GOT_OBJ_TYPE_BLOB:
3744 err = diff_blobs(obj_id1, obj_id2, path, diff_context,
3745 0, 0, repo, outfile);
3746 break;
3747 case GOT_OBJ_TYPE_TREE:
3748 err = diff_trees(obj_id1, obj_id2, path, diff_context,
3749 0, 0, repo, outfile);
3750 break;
3751 default:
3752 err = got_error(GOT_ERR_OBJ_TYPE);
3753 break;
3755 free(obj_id1);
3756 free(obj_id2);
3757 } else {
3758 obj_id2 = got_object_commit_get_tree_id(commit);
3759 err = got_object_id_str(&id_str2, obj_id2);
3760 if (err)
3761 goto done;
3762 if (pcommit) {
3763 obj_id1 = got_object_commit_get_tree_id(pcommit);
3764 err = got_object_id_str(&id_str1, obj_id1);
3765 if (err)
3766 goto done;
3768 fprintf(outfile,
3769 "diff %s %s\n", id_str1 ? id_str1 : "/dev/null", id_str2);
3770 err = diff_trees(obj_id1, obj_id2, "", diff_context, 0, 0,
3771 repo, outfile);
3773 done:
3774 free(id_str1);
3775 free(id_str2);
3776 if (pcommit)
3777 got_object_commit_close(pcommit);
3778 return err;
3781 static char *
3782 get_datestr(time_t *time, char *datebuf)
3784 struct tm mytm, *tm;
3785 char *p, *s;
3787 tm = gmtime_r(time, &mytm);
3788 if (tm == NULL)
3789 return NULL;
3790 s = asctime_r(tm, datebuf);
3791 if (s == NULL)
3792 return NULL;
3793 p = strchr(s, '\n');
3794 if (p)
3795 *p = '\0';
3796 return s;
3799 static const struct got_error *
3800 match_commit(int *have_match, struct got_object_id *id,
3801 struct got_commit_object *commit, regex_t *regex)
3803 const struct got_error *err = NULL;
3804 regmatch_t regmatch;
3805 char *id_str = NULL, *logmsg = NULL;
3807 *have_match = 0;
3809 err = got_object_id_str(&id_str, id);
3810 if (err)
3811 return err;
3813 err = got_object_commit_get_logmsg(&logmsg, commit);
3814 if (err)
3815 goto done;
3817 if (regexec(regex, got_object_commit_get_author(commit), 1,
3818 &regmatch, 0) == 0 ||
3819 regexec(regex, got_object_commit_get_committer(commit), 1,
3820 &regmatch, 0) == 0 ||
3821 regexec(regex, id_str, 1, &regmatch, 0) == 0 ||
3822 regexec(regex, logmsg, 1, &regmatch, 0) == 0)
3823 *have_match = 1;
3824 done:
3825 free(id_str);
3826 free(logmsg);
3827 return err;
3830 static void
3831 match_changed_paths(int *have_match, struct got_pathlist_head *changed_paths,
3832 regex_t *regex)
3834 regmatch_t regmatch;
3835 struct got_pathlist_entry *pe;
3837 *have_match = 0;
3839 TAILQ_FOREACH(pe, changed_paths, entry) {
3840 if (regexec(regex, pe->path, 1, &regmatch, 0) == 0) {
3841 *have_match = 1;
3842 break;
3847 static const struct got_error *
3848 match_patch(int *have_match, struct got_commit_object *commit,
3849 struct got_object_id *id, const char *path, int diff_context,
3850 struct got_repository *repo, regex_t *regex, FILE *f)
3852 const struct got_error *err = NULL;
3853 char *line = NULL;
3854 size_t linesize = 0;
3855 ssize_t linelen;
3856 regmatch_t regmatch;
3858 *have_match = 0;
3860 err = got_opentemp_truncate(f);
3861 if (err)
3862 return err;
3864 err = print_patch(commit, id, path, diff_context, repo, f);
3865 if (err)
3866 goto done;
3868 if (fseeko(f, 0L, SEEK_SET) == -1) {
3869 err = got_error_from_errno("fseeko");
3870 goto done;
3873 while ((linelen = getline(&line, &linesize, f)) != -1) {
3874 if (regexec(regex, line, 1, &regmatch, 0) == 0) {
3875 *have_match = 1;
3876 break;
3879 done:
3880 free(line);
3881 return err;
3884 #define GOT_COMMIT_SEP_STR "-----------------------------------------------\n"
3886 static const struct got_error*
3887 build_refs_str(char **refs_str, struct got_reflist_head *refs,
3888 struct got_object_id *id, struct got_repository *repo,
3889 int local_only)
3891 static const struct got_error *err = NULL;
3892 struct got_reflist_entry *re;
3893 char *s;
3894 const char *name;
3896 *refs_str = NULL;
3898 TAILQ_FOREACH(re, refs, entry) {
3899 struct got_tag_object *tag = NULL;
3900 struct got_object_id *ref_id;
3901 int cmp;
3903 name = got_ref_get_name(re->ref);
3904 if (strcmp(name, GOT_REF_HEAD) == 0)
3905 continue;
3906 if (strncmp(name, "refs/", 5) == 0)
3907 name += 5;
3908 if (strncmp(name, "got/", 4) == 0)
3909 continue;
3910 if (strncmp(name, "heads/", 6) == 0)
3911 name += 6;
3912 if (strncmp(name, "remotes/", 8) == 0) {
3913 if (local_only)
3914 continue;
3915 name += 8;
3916 s = strstr(name, "/" GOT_REF_HEAD);
3917 if (s != NULL && s[strlen(s)] == '\0')
3918 continue;
3920 err = got_ref_resolve(&ref_id, repo, re->ref);
3921 if (err)
3922 break;
3923 if (strncmp(name, "tags/", 5) == 0) {
3924 err = got_object_open_as_tag(&tag, repo, ref_id);
3925 if (err) {
3926 if (err->code != GOT_ERR_OBJ_TYPE) {
3927 free(ref_id);
3928 break;
3930 /* Ref points at something other than a tag. */
3931 err = NULL;
3932 tag = NULL;
3935 cmp = got_object_id_cmp(tag ?
3936 got_object_tag_get_object_id(tag) : ref_id, id);
3937 free(ref_id);
3938 if (tag)
3939 got_object_tag_close(tag);
3940 if (cmp != 0)
3941 continue;
3942 s = *refs_str;
3943 if (asprintf(refs_str, "%s%s%s", s ? s : "",
3944 s ? ", " : "", name) == -1) {
3945 err = got_error_from_errno("asprintf");
3946 free(s);
3947 *refs_str = NULL;
3948 break;
3950 free(s);
3953 return err;
3956 static const struct got_error *
3957 print_commit_oneline(struct got_commit_object *commit, struct got_object_id *id,
3958 struct got_repository *repo, struct got_reflist_object_id_map *refs_idmap)
3960 const struct got_error *err = NULL;
3961 char *ref_str = NULL, *id_str = NULL, *logmsg0 = NULL;
3962 char *comma, *s, *nl;
3963 struct got_reflist_head *refs;
3964 char datebuf[12]; /* YYYY-MM-DD + SPACE + NUL */
3965 struct tm tm;
3966 time_t committer_time;
3968 refs = got_reflist_object_id_map_lookup(refs_idmap, id);
3969 if (refs) {
3970 err = build_refs_str(&ref_str, refs, id, repo, 1);
3971 if (err)
3972 return err;
3974 /* Display the first matching ref only. */
3975 if (ref_str && (comma = strchr(ref_str, ',')) != NULL)
3976 *comma = '\0';
3979 if (ref_str == NULL) {
3980 err = got_object_id_str(&id_str, id);
3981 if (err)
3982 return err;
3985 committer_time = got_object_commit_get_committer_time(commit);
3986 if (gmtime_r(&committer_time, &tm) == NULL) {
3987 err = got_error_from_errno("gmtime_r");
3988 goto done;
3990 if (strftime(datebuf, sizeof(datebuf), "%G-%m-%d ", &tm) == 0) {
3991 err = got_error(GOT_ERR_NO_SPACE);
3992 goto done;
3995 err = got_object_commit_get_logmsg(&logmsg0, commit);
3996 if (err)
3997 goto done;
3999 s = logmsg0;
4000 while (isspace((unsigned char)s[0]))
4001 s++;
4003 nl = strchr(s, '\n');
4004 if (nl) {
4005 *nl = '\0';
4008 if (ref_str)
4009 printf("%s%-7s %s\n", datebuf, ref_str, s);
4010 else
4011 printf("%s%.7s %s\n", datebuf, id_str, s);
4013 if (fflush(stdout) != 0 && err == NULL)
4014 err = got_error_from_errno("fflush");
4015 done:
4016 free(id_str);
4017 free(ref_str);
4018 free(logmsg0);
4019 return err;
4022 static const struct got_error *
4023 print_commit(struct got_commit_object *commit, struct got_object_id *id,
4024 struct got_repository *repo, const char *path,
4025 struct got_pathlist_head *changed_paths, int show_patch,
4026 int diff_context, struct got_reflist_object_id_map *refs_idmap,
4027 const char *custom_refs_str)
4029 const struct got_error *err = NULL;
4030 char *id_str, *datestr, *logmsg0, *logmsg, *line;
4031 char datebuf[26];
4032 time_t committer_time;
4033 const char *author, *committer;
4034 char *refs_str = NULL;
4036 err = got_object_id_str(&id_str, id);
4037 if (err)
4038 return err;
4040 if (custom_refs_str == NULL) {
4041 struct got_reflist_head *refs;
4042 refs = got_reflist_object_id_map_lookup(refs_idmap, id);
4043 if (refs) {
4044 err = build_refs_str(&refs_str, refs, id, repo, 0);
4045 if (err)
4046 goto done;
4050 printf(GOT_COMMIT_SEP_STR);
4051 if (custom_refs_str)
4052 printf("commit %s (%s)\n", id_str, custom_refs_str);
4053 else
4054 printf("commit %s%s%s%s\n", id_str, refs_str ? " (" : "",
4055 refs_str ? refs_str : "", refs_str ? ")" : "");
4056 free(id_str);
4057 id_str = NULL;
4058 free(refs_str);
4059 refs_str = NULL;
4060 printf("from: %s\n", got_object_commit_get_author(commit));
4061 committer_time = got_object_commit_get_committer_time(commit);
4062 datestr = get_datestr(&committer_time, datebuf);
4063 if (datestr)
4064 printf("date: %s UTC\n", datestr);
4065 author = got_object_commit_get_author(commit);
4066 committer = got_object_commit_get_committer(commit);
4067 if (strcmp(author, committer) != 0)
4068 printf("via: %s\n", committer);
4069 if (got_object_commit_get_nparents(commit) > 1) {
4070 const struct got_object_id_queue *parent_ids;
4071 struct got_object_qid *qid;
4072 int n = 1;
4073 parent_ids = got_object_commit_get_parent_ids(commit);
4074 STAILQ_FOREACH(qid, parent_ids, entry) {
4075 err = got_object_id_str(&id_str, &qid->id);
4076 if (err)
4077 goto done;
4078 printf("parent %d: %s\n", n++, id_str);
4079 free(id_str);
4080 id_str = NULL;
4084 err = got_object_commit_get_logmsg(&logmsg0, commit);
4085 if (err)
4086 goto done;
4088 logmsg = logmsg0;
4089 do {
4090 line = strsep(&logmsg, "\n");
4091 if (line)
4092 printf(" %s\n", line);
4093 } while (line);
4094 free(logmsg0);
4096 if (changed_paths) {
4097 struct got_pathlist_entry *pe;
4098 TAILQ_FOREACH(pe, changed_paths, entry) {
4099 struct got_diff_changed_path *cp = pe->data;
4100 printf(" %c %s\n", cp->status, pe->path);
4102 printf("\n");
4104 if (show_patch) {
4105 err = print_patch(commit, id, path, diff_context, repo, stdout);
4106 if (err == 0)
4107 printf("\n");
4110 if (fflush(stdout) != 0 && err == NULL)
4111 err = got_error_from_errno("fflush");
4112 done:
4113 free(id_str);
4114 free(refs_str);
4115 return err;
4118 static const struct got_error *
4119 print_commits(struct got_object_id *root_id, struct got_object_id *end_id,
4120 struct got_repository *repo, const char *path, int show_changed_paths,
4121 int show_patch, const char *search_pattern, int diff_context, int limit,
4122 int log_branches, int reverse_display_order,
4123 struct got_reflist_object_id_map *refs_idmap, int one_line,
4124 FILE *tmpfile)
4126 const struct got_error *err;
4127 struct got_commit_graph *graph;
4128 regex_t regex;
4129 int have_match;
4130 struct got_object_id_queue reversed_commits;
4131 struct got_object_qid *qid;
4132 struct got_commit_object *commit;
4133 struct got_pathlist_head changed_paths;
4134 struct got_pathlist_entry *pe;
4136 STAILQ_INIT(&reversed_commits);
4137 TAILQ_INIT(&changed_paths);
4139 if (search_pattern && regcomp(&regex, search_pattern,
4140 REG_EXTENDED | REG_NOSUB | REG_NEWLINE))
4141 return got_error_msg(GOT_ERR_REGEX, search_pattern);
4143 err = got_commit_graph_open(&graph, path, !log_branches);
4144 if (err)
4145 return err;
4146 err = got_commit_graph_iter_start(graph, root_id, repo,
4147 check_cancelled, NULL);
4148 if (err)
4149 goto done;
4150 for (;;) {
4151 struct got_object_id *id;
4153 if (sigint_received || sigpipe_received)
4154 break;
4156 err = got_commit_graph_iter_next(&id, graph, repo,
4157 check_cancelled, NULL);
4158 if (err) {
4159 if (err->code == GOT_ERR_ITER_COMPLETED)
4160 err = NULL;
4161 break;
4163 if (id == NULL)
4164 break;
4166 err = got_object_open_as_commit(&commit, repo, id);
4167 if (err)
4168 break;
4170 if (show_changed_paths && !reverse_display_order) {
4171 err = get_changed_paths(&changed_paths, commit, repo);
4172 if (err)
4173 break;
4176 if (search_pattern) {
4177 err = match_commit(&have_match, id, commit, &regex);
4178 if (err) {
4179 got_object_commit_close(commit);
4180 break;
4182 if (have_match == 0 && show_changed_paths)
4183 match_changed_paths(&have_match,
4184 &changed_paths, &regex);
4185 if (have_match == 0 && show_patch) {
4186 err = match_patch(&have_match, commit, id,
4187 path, diff_context, repo, &regex,
4188 tmpfile);
4189 if (err)
4190 break;
4192 if (have_match == 0) {
4193 got_object_commit_close(commit);
4194 TAILQ_FOREACH(pe, &changed_paths, entry) {
4195 free((char *)pe->path);
4196 free(pe->data);
4198 got_pathlist_free(&changed_paths);
4199 continue;
4203 if (reverse_display_order) {
4204 err = got_object_qid_alloc(&qid, id);
4205 if (err)
4206 break;
4207 STAILQ_INSERT_HEAD(&reversed_commits, qid, entry);
4208 got_object_commit_close(commit);
4209 } else {
4210 if (one_line)
4211 err = print_commit_oneline(commit, id,
4212 repo, refs_idmap);
4213 else
4214 err = print_commit(commit, id, repo, path,
4215 show_changed_paths ? &changed_paths : NULL,
4216 show_patch, diff_context, refs_idmap, NULL);
4217 got_object_commit_close(commit);
4218 if (err)
4219 break;
4221 if ((limit && --limit == 0) ||
4222 (end_id && got_object_id_cmp(id, end_id) == 0))
4223 break;
4225 TAILQ_FOREACH(pe, &changed_paths, entry) {
4226 free((char *)pe->path);
4227 free(pe->data);
4229 got_pathlist_free(&changed_paths);
4231 if (reverse_display_order) {
4232 STAILQ_FOREACH(qid, &reversed_commits, entry) {
4233 err = got_object_open_as_commit(&commit, repo,
4234 &qid->id);
4235 if (err)
4236 break;
4237 if (show_changed_paths) {
4238 err = get_changed_paths(&changed_paths,
4239 commit, repo);
4240 if (err)
4241 break;
4243 if (one_line)
4244 err = print_commit_oneline(commit, &qid->id,
4245 repo, refs_idmap);
4246 else
4247 err = print_commit(commit, &qid->id, repo, path,
4248 show_changed_paths ? &changed_paths : NULL,
4249 show_patch, diff_context, refs_idmap, NULL);
4250 got_object_commit_close(commit);
4251 if (err)
4252 break;
4253 TAILQ_FOREACH(pe, &changed_paths, entry) {
4254 free((char *)pe->path);
4255 free(pe->data);
4257 got_pathlist_free(&changed_paths);
4260 done:
4261 while (!STAILQ_EMPTY(&reversed_commits)) {
4262 qid = STAILQ_FIRST(&reversed_commits);
4263 STAILQ_REMOVE_HEAD(&reversed_commits, entry);
4264 got_object_qid_free(qid);
4266 TAILQ_FOREACH(pe, &changed_paths, entry) {
4267 free((char *)pe->path);
4268 free(pe->data);
4270 got_pathlist_free(&changed_paths);
4271 if (search_pattern)
4272 regfree(&regex);
4273 got_commit_graph_close(graph);
4274 return err;
4277 __dead static void
4278 usage_log(void)
4280 fprintf(stderr, "usage: %s log [-b] [-p] [-P] [-s] [-c commit] "
4281 "[-C number] [ -l N ] [-x commit] [-S search-pattern] "
4282 "[-r repository-path] [-R] [path]\n", getprogname());
4283 exit(1);
4286 static int
4287 get_default_log_limit(void)
4289 const char *got_default_log_limit;
4290 long long n;
4291 const char *errstr;
4293 got_default_log_limit = getenv("GOT_LOG_DEFAULT_LIMIT");
4294 if (got_default_log_limit == NULL)
4295 return 0;
4296 n = strtonum(got_default_log_limit, 0, INT_MAX, &errstr);
4297 if (errstr != NULL)
4298 return 0;
4299 return n;
4302 static const struct got_error *
4303 cmd_log(int argc, char *argv[])
4305 const struct got_error *error;
4306 struct got_repository *repo = NULL;
4307 struct got_worktree *worktree = NULL;
4308 struct got_object_id *start_id = NULL, *end_id = NULL;
4309 char *repo_path = NULL, *path = NULL, *cwd = NULL, *in_repo_path = NULL;
4310 const char *start_commit = NULL, *end_commit = NULL;
4311 const char *search_pattern = NULL;
4312 int diff_context = -1, ch;
4313 int show_changed_paths = 0, show_patch = 0, limit = 0, log_branches = 0;
4314 int reverse_display_order = 0, one_line = 0;
4315 const char *errstr;
4316 struct got_reflist_head refs;
4317 struct got_reflist_object_id_map *refs_idmap = NULL;
4318 FILE *tmpfile = NULL;
4319 int *pack_fds = NULL;
4321 TAILQ_INIT(&refs);
4323 #ifndef PROFILE
4324 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
4325 NULL)
4326 == -1)
4327 err(1, "pledge");
4328 #endif
4330 limit = get_default_log_limit();
4332 while ((ch = getopt(argc, argv, "bpPc:C:l:r:RsS:x:")) != -1) {
4333 switch (ch) {
4334 case 'p':
4335 show_patch = 1;
4336 break;
4337 case 'P':
4338 show_changed_paths = 1;
4339 break;
4340 case 'c':
4341 start_commit = optarg;
4342 break;
4343 case 'C':
4344 diff_context = strtonum(optarg, 0, GOT_DIFF_MAX_CONTEXT,
4345 &errstr);
4346 if (errstr != NULL)
4347 errx(1, "number of context lines is %s: %s",
4348 errstr, optarg);
4349 break;
4350 case 'l':
4351 limit = strtonum(optarg, 0, INT_MAX, &errstr);
4352 if (errstr != NULL)
4353 errx(1, "number of commits is %s: %s",
4354 errstr, optarg);
4355 break;
4356 case 'b':
4357 log_branches = 1;
4358 break;
4359 case 'r':
4360 repo_path = realpath(optarg, NULL);
4361 if (repo_path == NULL)
4362 return got_error_from_errno2("realpath",
4363 optarg);
4364 got_path_strip_trailing_slashes(repo_path);
4365 break;
4366 case 'R':
4367 reverse_display_order = 1;
4368 break;
4369 case 's':
4370 one_line = 1;
4371 break;
4372 case 'S':
4373 search_pattern = optarg;
4374 break;
4375 case 'x':
4376 end_commit = optarg;
4377 break;
4378 default:
4379 usage_log();
4380 /* NOTREACHED */
4384 argc -= optind;
4385 argv += optind;
4387 if (diff_context == -1)
4388 diff_context = 3;
4389 else if (!show_patch)
4390 errx(1, "-C requires -p");
4392 if (one_line && (show_patch || show_changed_paths))
4393 errx(1, "cannot use -s with -p or -P");
4395 cwd = getcwd(NULL, 0);
4396 if (cwd == NULL) {
4397 error = got_error_from_errno("getcwd");
4398 goto done;
4401 error = got_repo_pack_fds_open(&pack_fds);
4402 if (error != NULL)
4403 goto done;
4405 if (repo_path == NULL) {
4406 error = got_worktree_open(&worktree, cwd);
4407 if (error && error->code != GOT_ERR_NOT_WORKTREE)
4408 goto done;
4409 error = NULL;
4412 if (argc == 1) {
4413 if (worktree) {
4414 error = got_worktree_resolve_path(&path, worktree,
4415 argv[0]);
4416 if (error)
4417 goto done;
4418 } else {
4419 path = strdup(argv[0]);
4420 if (path == NULL) {
4421 error = got_error_from_errno("strdup");
4422 goto done;
4425 } else if (argc != 0)
4426 usage_log();
4428 if (repo_path == NULL) {
4429 repo_path = worktree ?
4430 strdup(got_worktree_get_repo_path(worktree)) : strdup(cwd);
4432 if (repo_path == NULL) {
4433 error = got_error_from_errno("strdup");
4434 goto done;
4437 error = got_repo_open(&repo, repo_path, NULL, pack_fds);
4438 if (error != NULL)
4439 goto done;
4441 error = apply_unveil(got_repo_get_path(repo), 1,
4442 worktree ? got_worktree_get_root_path(worktree) : NULL);
4443 if (error)
4444 goto done;
4446 error = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name, NULL);
4447 if (error)
4448 goto done;
4450 error = got_reflist_object_id_map_create(&refs_idmap, &refs, repo);
4451 if (error)
4452 goto done;
4454 if (start_commit == NULL) {
4455 struct got_reference *head_ref;
4456 struct got_commit_object *commit = NULL;
4457 error = got_ref_open(&head_ref, repo,
4458 worktree ? got_worktree_get_head_ref_name(worktree)
4459 : GOT_REF_HEAD, 0);
4460 if (error != NULL)
4461 goto done;
4462 error = got_ref_resolve(&start_id, repo, head_ref);
4463 got_ref_close(head_ref);
4464 if (error != NULL)
4465 goto done;
4466 error = got_object_open_as_commit(&commit, repo,
4467 start_id);
4468 if (error != NULL)
4469 goto done;
4470 got_object_commit_close(commit);
4471 } else {
4472 error = got_repo_match_object_id(&start_id, NULL,
4473 start_commit, GOT_OBJ_TYPE_COMMIT, &refs, repo);
4474 if (error != NULL)
4475 goto done;
4477 if (end_commit != NULL) {
4478 error = got_repo_match_object_id(&end_id, NULL,
4479 end_commit, GOT_OBJ_TYPE_COMMIT, &refs, repo);
4480 if (error != NULL)
4481 goto done;
4484 if (worktree) {
4486 * If a path was specified on the command line it was resolved
4487 * to a path in the work tree above. Prepend the work tree's
4488 * path prefix to obtain the corresponding in-repository path.
4490 if (path) {
4491 const char *prefix;
4492 prefix = got_worktree_get_path_prefix(worktree);
4493 if (asprintf(&in_repo_path, "%s%s%s", prefix,
4494 (path[0] != '\0') ? "/" : "", path) == -1) {
4495 error = got_error_from_errno("asprintf");
4496 goto done;
4499 } else
4500 error = got_repo_map_path(&in_repo_path, repo,
4501 path ? path : "");
4502 if (error != NULL)
4503 goto done;
4504 if (in_repo_path) {
4505 free(path);
4506 path = in_repo_path;
4509 if (worktree) {
4510 /* Release work tree lock. */
4511 got_worktree_close(worktree);
4512 worktree = NULL;
4515 if (search_pattern && show_patch) {
4516 tmpfile = got_opentemp();
4517 if (tmpfile == NULL) {
4518 error = got_error_from_errno("got_opentemp");
4519 goto done;
4523 error = print_commits(start_id, end_id, repo, path ? path : "",
4524 show_changed_paths, show_patch, search_pattern, diff_context,
4525 limit, log_branches, reverse_display_order, refs_idmap, one_line,
4526 tmpfile);
4527 done:
4528 free(path);
4529 free(repo_path);
4530 free(cwd);
4531 if (worktree)
4532 got_worktree_close(worktree);
4533 if (repo) {
4534 const struct got_error *close_err = got_repo_close(repo);
4535 if (error == NULL)
4536 error = close_err;
4538 if (pack_fds) {
4539 const struct got_error *pack_err =
4540 got_repo_pack_fds_close(pack_fds);
4541 if (error == NULL)
4542 error = pack_err;
4544 if (refs_idmap)
4545 got_reflist_object_id_map_free(refs_idmap);
4546 if (tmpfile && fclose(tmpfile) == EOF && error == NULL)
4547 error = got_error_from_errno("fclose");
4548 got_ref_list_free(&refs);
4549 return error;
4552 __dead static void
4553 usage_diff(void)
4555 fprintf(stderr, "usage: %s diff [-a] [-c commit] [-C number] "
4556 "[-r repository-path] [-s] [-w] [-P] "
4557 "[object1 object2 | path ...]\n", getprogname());
4558 exit(1);
4561 struct print_diff_arg {
4562 struct got_repository *repo;
4563 struct got_worktree *worktree;
4564 int diff_context;
4565 const char *id_str;
4566 int header_shown;
4567 int diff_staged;
4568 int ignore_whitespace;
4569 int force_text_diff;
4573 * Create a file which contains the target path of a symlink so we can feed
4574 * it as content to the diff engine.
4576 static const struct got_error *
4577 get_symlink_target_file(int *fd, int dirfd, const char *de_name,
4578 const char *abspath)
4580 const struct got_error *err = NULL;
4581 char target_path[PATH_MAX];
4582 ssize_t target_len, outlen;
4584 *fd = -1;
4586 if (dirfd != -1) {
4587 target_len = readlinkat(dirfd, de_name, target_path, PATH_MAX);
4588 if (target_len == -1)
4589 return got_error_from_errno2("readlinkat", abspath);
4590 } else {
4591 target_len = readlink(abspath, target_path, PATH_MAX);
4592 if (target_len == -1)
4593 return got_error_from_errno2("readlink", abspath);
4596 *fd = got_opentempfd();
4597 if (*fd == -1)
4598 return got_error_from_errno("got_opentempfd");
4600 outlen = write(*fd, target_path, target_len);
4601 if (outlen == -1) {
4602 err = got_error_from_errno("got_opentempfd");
4603 goto done;
4606 if (lseek(*fd, 0, SEEK_SET) == -1) {
4607 err = got_error_from_errno2("lseek", abspath);
4608 goto done;
4610 done:
4611 if (err) {
4612 close(*fd);
4613 *fd = -1;
4615 return err;
4618 static const struct got_error *
4619 print_diff(void *arg, unsigned char status, unsigned char staged_status,
4620 const char *path, struct got_object_id *blob_id,
4621 struct got_object_id *staged_blob_id, struct got_object_id *commit_id,
4622 int dirfd, const char *de_name)
4624 struct print_diff_arg *a = arg;
4625 const struct got_error *err = NULL;
4626 struct got_blob_object *blob1 = NULL;
4627 int fd = -1;
4628 FILE *f1 = NULL, *f2 = NULL;
4629 char *abspath = NULL, *label1 = NULL;
4630 struct stat sb;
4631 off_t size1 = 0;
4633 if (a->diff_staged) {
4634 if (staged_status != GOT_STATUS_MODIFY &&
4635 staged_status != GOT_STATUS_ADD &&
4636 staged_status != GOT_STATUS_DELETE)
4637 return NULL;
4638 } else {
4639 if (staged_status == GOT_STATUS_DELETE)
4640 return NULL;
4641 if (status == GOT_STATUS_NONEXISTENT)
4642 return got_error_set_errno(ENOENT, path);
4643 if (status != GOT_STATUS_MODIFY &&
4644 status != GOT_STATUS_ADD &&
4645 status != GOT_STATUS_DELETE &&
4646 status != GOT_STATUS_CONFLICT)
4647 return NULL;
4650 if (!a->header_shown) {
4651 printf("diff %s %s%s\n", a->id_str,
4652 got_worktree_get_root_path(a->worktree),
4653 a->diff_staged ? " (staged changes)" : "");
4654 a->header_shown = 1;
4657 if (a->diff_staged) {
4658 const char *label1 = NULL, *label2 = NULL;
4659 switch (staged_status) {
4660 case GOT_STATUS_MODIFY:
4661 label1 = path;
4662 label2 = path;
4663 break;
4664 case GOT_STATUS_ADD:
4665 label2 = path;
4666 break;
4667 case GOT_STATUS_DELETE:
4668 label1 = path;
4669 break;
4670 default:
4671 return got_error(GOT_ERR_FILE_STATUS);
4673 f1 = got_opentemp();
4674 if (f1 == NULL) {
4675 err = got_error_from_errno("got_opentemp");
4676 goto done;
4678 f2 = got_opentemp();
4679 if (f2 == NULL) {
4680 err = got_error_from_errno("got_opentemp");
4681 goto done;
4683 err = got_diff_objects_as_blobs(NULL, NULL, f1, f2,
4684 blob_id, staged_blob_id, label1, label2, a->diff_context,
4685 a->ignore_whitespace, a->force_text_diff, a->repo, stdout);
4686 goto done;
4689 if (staged_status == GOT_STATUS_ADD ||
4690 staged_status == GOT_STATUS_MODIFY) {
4691 char *id_str;
4692 err = got_object_open_as_blob(&blob1, a->repo, staged_blob_id,
4693 8192);
4694 if (err)
4695 goto done;
4696 err = got_object_id_str(&id_str, staged_blob_id);
4697 if (err)
4698 goto done;
4699 if (asprintf(&label1, "%s (staged)", id_str) == -1) {
4700 err = got_error_from_errno("asprintf");
4701 free(id_str);
4702 goto done;
4704 free(id_str);
4705 } else if (status != GOT_STATUS_ADD) {
4706 err = got_object_open_as_blob(&blob1, a->repo, blob_id, 8192);
4707 if (err)
4708 goto done;
4711 if (status != GOT_STATUS_DELETE) {
4712 if (asprintf(&abspath, "%s/%s",
4713 got_worktree_get_root_path(a->worktree), path) == -1) {
4714 err = got_error_from_errno("asprintf");
4715 goto done;
4718 if (dirfd != -1) {
4719 fd = openat(dirfd, de_name,
4720 O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
4721 if (fd == -1) {
4722 if (!got_err_open_nofollow_on_symlink()) {
4723 err = got_error_from_errno2("openat",
4724 abspath);
4725 goto done;
4727 err = get_symlink_target_file(&fd, dirfd,
4728 de_name, abspath);
4729 if (err)
4730 goto done;
4732 } else {
4733 fd = open(abspath, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
4734 if (fd == -1) {
4735 if (!got_err_open_nofollow_on_symlink()) {
4736 err = got_error_from_errno2("open",
4737 abspath);
4738 goto done;
4740 err = get_symlink_target_file(&fd, dirfd,
4741 de_name, abspath);
4742 if (err)
4743 goto done;
4746 if (fstat(fd, &sb) == -1) {
4747 err = got_error_from_errno2("fstat", abspath);
4748 goto done;
4750 f2 = fdopen(fd, "r");
4751 if (f2 == NULL) {
4752 err = got_error_from_errno2("fdopen", abspath);
4753 goto done;
4755 fd = -1;
4756 } else
4757 sb.st_size = 0;
4759 if (blob1) {
4760 f1 = got_opentemp();
4761 if (f1 == NULL) {
4762 err = got_error_from_errno("got_opentemp");
4763 goto done;
4765 err = got_object_blob_dump_to_file(&size1, NULL, NULL, f1,
4766 blob1);
4767 if (err)
4768 goto done;
4771 err = got_diff_blob_file(blob1, f1, size1, label1, f2, sb.st_size,
4772 path, a->diff_context, a->ignore_whitespace, a->force_text_diff,
4773 stdout);
4774 done:
4775 if (blob1)
4776 got_object_blob_close(blob1);
4777 if (f1 && fclose(f1) == EOF && err == NULL)
4778 err = got_error_from_errno("fclose");
4779 if (f2 && fclose(f2) == EOF && err == NULL)
4780 err = got_error_from_errno("fclose");
4781 if (fd != -1 && close(fd) == -1 && err == NULL)
4782 err = got_error_from_errno("close");
4783 free(abspath);
4784 return err;
4787 static const struct got_error *
4788 cmd_diff(int argc, char *argv[])
4790 const struct got_error *error;
4791 struct got_repository *repo = NULL;
4792 struct got_worktree *worktree = NULL;
4793 char *cwd = NULL, *repo_path = NULL;
4794 const char *commit_args[2] = { NULL, NULL };
4795 int ncommit_args = 0;
4796 struct got_object_id *ids[2] = { NULL, NULL };
4797 char *labels[2] = { NULL, NULL };
4798 int type1 = GOT_OBJ_TYPE_ANY, type2 = GOT_OBJ_TYPE_ANY;
4799 int diff_context = 3, diff_staged = 0, ignore_whitespace = 0, ch, i;
4800 int force_text_diff = 0, force_path = 0, rflag = 0;
4801 const char *errstr;
4802 struct got_reflist_head refs;
4803 struct got_pathlist_head paths;
4804 struct got_pathlist_entry *pe;
4805 FILE *f1 = NULL, *f2 = NULL;
4806 int *pack_fds = NULL;
4808 TAILQ_INIT(&refs);
4809 TAILQ_INIT(&paths);
4811 #ifndef PROFILE
4812 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
4813 NULL) == -1)
4814 err(1, "pledge");
4815 #endif
4817 while ((ch = getopt(argc, argv, "ac:C:r:swP")) != -1) {
4818 switch (ch) {
4819 case 'a':
4820 force_text_diff = 1;
4821 break;
4822 case 'c':
4823 if (ncommit_args >= 2)
4824 errx(1, "too many -c options used");
4825 commit_args[ncommit_args++] = optarg;
4826 break;
4827 case 'C':
4828 diff_context = strtonum(optarg, 0, GOT_DIFF_MAX_CONTEXT,
4829 &errstr);
4830 if (errstr != NULL)
4831 errx(1, "number of context lines is %s: %s",
4832 errstr, optarg);
4833 break;
4834 case 'r':
4835 repo_path = realpath(optarg, NULL);
4836 if (repo_path == NULL)
4837 return got_error_from_errno2("realpath",
4838 optarg);
4839 got_path_strip_trailing_slashes(repo_path);
4840 rflag = 1;
4841 break;
4842 case 's':
4843 diff_staged = 1;
4844 break;
4845 case 'w':
4846 ignore_whitespace = 1;
4847 break;
4848 case 'P':
4849 force_path = 1;
4850 break;
4851 default:
4852 usage_diff();
4853 /* NOTREACHED */
4857 argc -= optind;
4858 argv += optind;
4860 cwd = getcwd(NULL, 0);
4861 if (cwd == NULL) {
4862 error = got_error_from_errno("getcwd");
4863 goto done;
4866 error = got_repo_pack_fds_open(&pack_fds);
4867 if (error != NULL)
4868 goto done;
4870 if (repo_path == NULL) {
4871 error = got_worktree_open(&worktree, cwd);
4872 if (error && error->code != GOT_ERR_NOT_WORKTREE)
4873 goto done;
4874 else
4875 error = NULL;
4876 if (worktree) {
4877 repo_path =
4878 strdup(got_worktree_get_repo_path(worktree));
4879 if (repo_path == NULL) {
4880 error = got_error_from_errno("strdup");
4881 goto done;
4883 } else {
4884 repo_path = strdup(cwd);
4885 if (repo_path == NULL) {
4886 error = got_error_from_errno("strdup");
4887 goto done;
4892 error = got_repo_open(&repo, repo_path, NULL, pack_fds);
4893 free(repo_path);
4894 if (error != NULL)
4895 goto done;
4897 if (rflag || worktree == NULL || ncommit_args > 0) {
4898 if (force_path) {
4899 error = got_error_msg(GOT_ERR_NOT_IMPL,
4900 "-P option can only be used when diffing "
4901 "a work tree");
4902 goto done;
4904 if (diff_staged) {
4905 error = got_error_msg(GOT_ERR_NOT_IMPL,
4906 "-s option can only be used when diffing "
4907 "a work tree");
4908 goto done;
4912 error = apply_unveil(got_repo_get_path(repo), 1,
4913 worktree ? got_worktree_get_root_path(worktree) : NULL);
4914 if (error)
4915 goto done;
4917 if ((!force_path && argc == 2) || ncommit_args > 0) {
4918 int obj_type = (ncommit_args > 0 ?
4919 GOT_OBJ_TYPE_COMMIT : GOT_OBJ_TYPE_ANY);
4920 error = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name,
4921 NULL);
4922 if (error)
4923 goto done;
4924 for (i = 0; i < (ncommit_args > 0 ? ncommit_args : argc); i++) {
4925 const char *arg;
4926 if (ncommit_args > 0)
4927 arg = commit_args[i];
4928 else
4929 arg = argv[i];
4930 error = got_repo_match_object_id(&ids[i], &labels[i],
4931 arg, obj_type, &refs, repo);
4932 if (error) {
4933 if (error->code != GOT_ERR_NOT_REF &&
4934 error->code != GOT_ERR_NO_OBJ)
4935 goto done;
4936 if (ncommit_args > 0)
4937 goto done;
4938 error = NULL;
4939 break;
4944 if (ncommit_args == 0 && (ids[0] == NULL || ids[1] == NULL)) {
4945 struct print_diff_arg arg;
4946 char *id_str;
4948 if (worktree == NULL) {
4949 if (argc == 2 && ids[0] == NULL) {
4950 error = got_error_path(argv[0], GOT_ERR_NO_OBJ);
4951 goto done;
4952 } else if (argc == 2 && ids[1] == NULL) {
4953 error = got_error_path(argv[1], GOT_ERR_NO_OBJ);
4954 goto done;
4955 } else if (argc > 0) {
4956 error = got_error_fmt(GOT_ERR_NOT_WORKTREE,
4957 "%s", "specified paths cannot be resolved");
4958 goto done;
4959 } else {
4960 error = got_error(GOT_ERR_NOT_WORKTREE);
4961 goto done;
4965 error = get_worktree_paths_from_argv(&paths, argc, argv,
4966 worktree);
4967 if (error)
4968 goto done;
4970 error = got_object_id_str(&id_str,
4971 got_worktree_get_base_commit_id(worktree));
4972 if (error)
4973 goto done;
4974 arg.repo = repo;
4975 arg.worktree = worktree;
4976 arg.diff_context = diff_context;
4977 arg.id_str = id_str;
4978 arg.header_shown = 0;
4979 arg.diff_staged = diff_staged;
4980 arg.ignore_whitespace = ignore_whitespace;
4981 arg.force_text_diff = force_text_diff;
4983 error = got_worktree_status(worktree, &paths, repo, 0,
4984 print_diff, &arg, check_cancelled, NULL);
4985 free(id_str);
4986 goto done;
4989 if (ncommit_args == 1) {
4990 struct got_commit_object *commit;
4991 error = got_object_open_as_commit(&commit, repo, ids[0]);
4992 if (error)
4993 goto done;
4995 labels[1] = labels[0];
4996 ids[1] = ids[0];
4997 if (got_object_commit_get_nparents(commit) > 0) {
4998 const struct got_object_id_queue *pids;
4999 struct got_object_qid *pid;
5000 pids = got_object_commit_get_parent_ids(commit);
5001 pid = STAILQ_FIRST(pids);
5002 ids[0] = got_object_id_dup(&pid->id);
5003 if (ids[0] == NULL) {
5004 error = got_error_from_errno(
5005 "got_object_id_dup");
5006 got_object_commit_close(commit);
5007 goto done;
5009 error = got_object_id_str(&labels[0], ids[0]);
5010 if (error) {
5011 got_object_commit_close(commit);
5012 goto done;
5014 } else {
5015 ids[0] = NULL;
5016 labels[0] = strdup("/dev/null");
5017 if (labels[0] == NULL) {
5018 error = got_error_from_errno("strdup");
5019 got_object_commit_close(commit);
5020 goto done;
5024 got_object_commit_close(commit);
5027 if (ncommit_args == 0 && argc > 2) {
5028 error = got_error_msg(GOT_ERR_BAD_PATH,
5029 "path arguments cannot be used when diffing two objects");
5030 goto done;
5033 if (ids[0]) {
5034 error = got_object_get_type(&type1, repo, ids[0]);
5035 if (error)
5036 goto done;
5039 error = got_object_get_type(&type2, repo, ids[1]);
5040 if (error)
5041 goto done;
5042 if (type1 != GOT_OBJ_TYPE_ANY && type1 != type2) {
5043 error = got_error(GOT_ERR_OBJ_TYPE);
5044 goto done;
5046 if (type1 == GOT_OBJ_TYPE_BLOB && argc > 0) {
5047 error = got_error_msg(GOT_ERR_OBJ_TYPE,
5048 "path arguments cannot be used when diffing blobs");
5049 goto done;
5052 for (i = 0; ncommit_args > 0 && i < argc; i++) {
5053 char *in_repo_path;
5054 struct got_pathlist_entry *new;
5055 if (worktree) {
5056 const char *prefix;
5057 char *p;
5058 error = got_worktree_resolve_path(&p, worktree,
5059 argv[i]);
5060 if (error)
5061 goto done;
5062 prefix = got_worktree_get_path_prefix(worktree);
5063 while (prefix[0] == '/')
5064 prefix++;
5065 if (asprintf(&in_repo_path, "%s%s%s", prefix,
5066 (p[0] != '\0' && prefix[0] != '\0') ? "/" : "",
5067 p) == -1) {
5068 error = got_error_from_errno("asprintf");
5069 free(p);
5070 goto done;
5072 free(p);
5073 } else {
5074 char *mapped_path, *s;
5075 error = got_repo_map_path(&mapped_path, repo, argv[i]);
5076 if (error)
5077 goto done;
5078 s = mapped_path;
5079 while (s[0] == '/')
5080 s++;
5081 in_repo_path = strdup(s);
5082 if (in_repo_path == NULL) {
5083 error = got_error_from_errno("asprintf");
5084 free(mapped_path);
5085 goto done;
5087 free(mapped_path);
5090 error = got_pathlist_insert(&new, &paths, in_repo_path, NULL);
5091 if (error || new == NULL /* duplicate */)
5092 free(in_repo_path);
5093 if (error)
5094 goto done;
5097 if (worktree) {
5098 /* Release work tree lock. */
5099 got_worktree_close(worktree);
5100 worktree = NULL;
5103 f1 = got_opentemp();
5104 if (f1 == NULL) {
5105 error = got_error_from_errno("got_opentemp");
5106 goto done;
5109 f2 = got_opentemp();
5110 if (f2 == NULL) {
5111 error = got_error_from_errno("got_opentemp");
5112 goto done;
5115 switch (type1 == GOT_OBJ_TYPE_ANY ? type2 : type1) {
5116 case GOT_OBJ_TYPE_BLOB:
5117 error = got_diff_objects_as_blobs(NULL, NULL, f1, f2,
5118 ids[0], ids[1], NULL, NULL, diff_context,
5119 ignore_whitespace, force_text_diff, repo, stdout);
5120 break;
5121 case GOT_OBJ_TYPE_TREE:
5122 error = got_diff_objects_as_trees(NULL, NULL, f1, f2,
5123 ids[0], ids[1], &paths, "", "", diff_context,
5124 ignore_whitespace, force_text_diff, repo, stdout);
5125 break;
5126 case GOT_OBJ_TYPE_COMMIT:
5127 printf("diff %s %s\n", labels[0], labels[1]);
5128 error = got_diff_objects_as_commits(NULL, NULL, f1, f2,
5129 ids[0], ids[1], &paths, diff_context, ignore_whitespace,
5130 force_text_diff, repo, stdout);
5131 break;
5132 default:
5133 error = got_error(GOT_ERR_OBJ_TYPE);
5135 done:
5136 free(labels[0]);
5137 free(labels[1]);
5138 free(ids[0]);
5139 free(ids[1]);
5140 if (worktree)
5141 got_worktree_close(worktree);
5142 if (repo) {
5143 const struct got_error *close_err = got_repo_close(repo);
5144 if (error == NULL)
5145 error = close_err;
5147 if (pack_fds) {
5148 const struct got_error *pack_err =
5149 got_repo_pack_fds_close(pack_fds);
5150 if (error == NULL)
5151 error = pack_err;
5153 TAILQ_FOREACH(pe, &paths, entry)
5154 free((char *)pe->path);
5155 got_pathlist_free(&paths);
5156 got_ref_list_free(&refs);
5157 if (f1 && fclose(f1) == EOF && error == NULL)
5158 error = got_error_from_errno("fclose");
5159 if (f2 && fclose(f2) == EOF && error == NULL)
5160 error = got_error_from_errno("fclose");
5161 return error;
5164 __dead static void
5165 usage_blame(void)
5167 fprintf(stderr,
5168 "usage: %s blame [-c commit] [-r repository-path] path\n",
5169 getprogname());
5170 exit(1);
5173 struct blame_line {
5174 int annotated;
5175 char *id_str;
5176 char *committer;
5177 char datebuf[11]; /* YYYY-MM-DD + NUL */
5180 struct blame_cb_args {
5181 struct blame_line *lines;
5182 int nlines;
5183 int nlines_prec;
5184 int lineno_cur;
5185 off_t *line_offsets;
5186 FILE *f;
5187 struct got_repository *repo;
5190 static const struct got_error *
5191 blame_cb(void *arg, int nlines, int lineno,
5192 struct got_commit_object *commit, struct got_object_id *id)
5194 const struct got_error *err = NULL;
5195 struct blame_cb_args *a = arg;
5196 struct blame_line *bline;
5197 char *line = NULL;
5198 size_t linesize = 0;
5199 off_t offset;
5200 struct tm tm;
5201 time_t committer_time;
5203 if (nlines != a->nlines ||
5204 (lineno != -1 && lineno < 1) || lineno > a->nlines)
5205 return got_error(GOT_ERR_RANGE);
5207 if (sigint_received)
5208 return got_error(GOT_ERR_ITER_COMPLETED);
5210 if (lineno == -1)
5211 return NULL; /* no change in this commit */
5213 /* Annotate this line. */
5214 bline = &a->lines[lineno - 1];
5215 if (bline->annotated)
5216 return NULL;
5217 err = got_object_id_str(&bline->id_str, id);
5218 if (err)
5219 return err;
5221 bline->committer = strdup(got_object_commit_get_committer(commit));
5222 if (bline->committer == NULL) {
5223 err = got_error_from_errno("strdup");
5224 goto done;
5227 committer_time = got_object_commit_get_committer_time(commit);
5228 if (gmtime_r(&committer_time, &tm) == NULL)
5229 return got_error_from_errno("gmtime_r");
5230 if (strftime(bline->datebuf, sizeof(bline->datebuf), "%G-%m-%d",
5231 &tm) == 0) {
5232 err = got_error(GOT_ERR_NO_SPACE);
5233 goto done;
5235 bline->annotated = 1;
5237 /* Print lines annotated so far. */
5238 bline = &a->lines[a->lineno_cur - 1];
5239 if (!bline->annotated)
5240 goto done;
5242 offset = a->line_offsets[a->lineno_cur - 1];
5243 if (fseeko(a->f, offset, SEEK_SET) == -1) {
5244 err = got_error_from_errno("fseeko");
5245 goto done;
5248 while (bline->annotated) {
5249 char *smallerthan, *at, *nl, *committer;
5250 size_t len;
5252 if (getline(&line, &linesize, a->f) == -1) {
5253 if (ferror(a->f))
5254 err = got_error_from_errno("getline");
5255 break;
5258 committer = bline->committer;
5259 smallerthan = strchr(committer, '<');
5260 if (smallerthan && smallerthan[1] != '\0')
5261 committer = smallerthan + 1;
5262 at = strchr(committer, '@');
5263 if (at)
5264 *at = '\0';
5265 len = strlen(committer);
5266 if (len >= 9)
5267 committer[8] = '\0';
5269 nl = strchr(line, '\n');
5270 if (nl)
5271 *nl = '\0';
5272 printf("%.*d) %.8s %s %-8s %s\n", a->nlines_prec, a->lineno_cur,
5273 bline->id_str, bline->datebuf, committer, line);
5275 a->lineno_cur++;
5276 bline = &a->lines[a->lineno_cur - 1];
5278 done:
5279 free(line);
5280 return err;
5283 static const struct got_error *
5284 cmd_blame(int argc, char *argv[])
5286 const struct got_error *error;
5287 struct got_repository *repo = NULL;
5288 struct got_worktree *worktree = NULL;
5289 char *path, *cwd = NULL, *repo_path = NULL, *in_repo_path = NULL;
5290 char *link_target = NULL;
5291 struct got_object_id *obj_id = NULL;
5292 struct got_object_id *commit_id = NULL;
5293 struct got_commit_object *commit = NULL;
5294 struct got_blob_object *blob = NULL;
5295 char *commit_id_str = NULL;
5296 struct blame_cb_args bca;
5297 int ch, obj_type, i;
5298 off_t filesize;
5299 int *pack_fds = NULL;
5301 memset(&bca, 0, sizeof(bca));
5303 #ifndef PROFILE
5304 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
5305 NULL) == -1)
5306 err(1, "pledge");
5307 #endif
5309 while ((ch = getopt(argc, argv, "c:r:")) != -1) {
5310 switch (ch) {
5311 case 'c':
5312 commit_id_str = optarg;
5313 break;
5314 case 'r':
5315 repo_path = realpath(optarg, NULL);
5316 if (repo_path == NULL)
5317 return got_error_from_errno2("realpath",
5318 optarg);
5319 got_path_strip_trailing_slashes(repo_path);
5320 break;
5321 default:
5322 usage_blame();
5323 /* NOTREACHED */
5327 argc -= optind;
5328 argv += optind;
5330 if (argc == 1)
5331 path = argv[0];
5332 else
5333 usage_blame();
5335 cwd = getcwd(NULL, 0);
5336 if (cwd == NULL) {
5337 error = got_error_from_errno("getcwd");
5338 goto done;
5341 error = got_repo_pack_fds_open(&pack_fds);
5342 if (error != NULL)
5343 goto done;
5345 if (repo_path == NULL) {
5346 error = got_worktree_open(&worktree, cwd);
5347 if (error && error->code != GOT_ERR_NOT_WORKTREE)
5348 goto done;
5349 else
5350 error = NULL;
5351 if (worktree) {
5352 repo_path =
5353 strdup(got_worktree_get_repo_path(worktree));
5354 if (repo_path == NULL) {
5355 error = got_error_from_errno("strdup");
5356 if (error)
5357 goto done;
5359 } else {
5360 repo_path = strdup(cwd);
5361 if (repo_path == NULL) {
5362 error = got_error_from_errno("strdup");
5363 goto done;
5368 error = got_repo_open(&repo, repo_path, NULL, pack_fds);
5369 if (error != NULL)
5370 goto done;
5372 if (worktree) {
5373 const char *prefix = got_worktree_get_path_prefix(worktree);
5374 char *p;
5376 error = got_worktree_resolve_path(&p, worktree, path);
5377 if (error)
5378 goto done;
5379 if (asprintf(&in_repo_path, "%s%s%s", prefix,
5380 (p[0] != '\0' && !got_path_is_root_dir(prefix)) ? "/" : "",
5381 p) == -1) {
5382 error = got_error_from_errno("asprintf");
5383 free(p);
5384 goto done;
5386 free(p);
5387 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
5388 } else {
5389 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
5390 if (error)
5391 goto done;
5392 error = got_repo_map_path(&in_repo_path, repo, path);
5394 if (error)
5395 goto done;
5397 if (commit_id_str == NULL) {
5398 struct got_reference *head_ref;
5399 error = got_ref_open(&head_ref, repo, worktree ?
5400 got_worktree_get_head_ref_name(worktree) : GOT_REF_HEAD, 0);
5401 if (error != NULL)
5402 goto done;
5403 error = got_ref_resolve(&commit_id, repo, head_ref);
5404 got_ref_close(head_ref);
5405 if (error != NULL)
5406 goto done;
5407 } else {
5408 struct got_reflist_head refs;
5409 TAILQ_INIT(&refs);
5410 error = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name,
5411 NULL);
5412 if (error)
5413 goto done;
5414 error = got_repo_match_object_id(&commit_id, NULL,
5415 commit_id_str, GOT_OBJ_TYPE_COMMIT, &refs, repo);
5416 got_ref_list_free(&refs);
5417 if (error)
5418 goto done;
5421 if (worktree) {
5422 /* Release work tree lock. */
5423 got_worktree_close(worktree);
5424 worktree = NULL;
5427 error = got_object_open_as_commit(&commit, repo, commit_id);
5428 if (error)
5429 goto done;
5431 error = got_object_resolve_symlinks(&link_target, in_repo_path,
5432 commit, repo);
5433 if (error)
5434 goto done;
5436 error = got_object_id_by_path(&obj_id, repo, commit,
5437 link_target ? link_target : in_repo_path);
5438 if (error)
5439 goto done;
5441 error = got_object_get_type(&obj_type, repo, obj_id);
5442 if (error)
5443 goto done;
5445 if (obj_type != GOT_OBJ_TYPE_BLOB) {
5446 error = got_error_path(link_target ? link_target : in_repo_path,
5447 GOT_ERR_OBJ_TYPE);
5448 goto done;
5451 error = got_object_open_as_blob(&blob, repo, obj_id, 8192);
5452 if (error)
5453 goto done;
5454 bca.f = got_opentemp();
5455 if (bca.f == NULL) {
5456 error = got_error_from_errno("got_opentemp");
5457 goto done;
5459 error = got_object_blob_dump_to_file(&filesize, &bca.nlines,
5460 &bca.line_offsets, bca.f, blob);
5461 if (error || bca.nlines == 0)
5462 goto done;
5464 /* Don't include \n at EOF in the blame line count. */
5465 if (bca.line_offsets[bca.nlines - 1] == filesize)
5466 bca.nlines--;
5468 bca.lines = calloc(bca.nlines, sizeof(*bca.lines));
5469 if (bca.lines == NULL) {
5470 error = got_error_from_errno("calloc");
5471 goto done;
5473 bca.lineno_cur = 1;
5474 bca.nlines_prec = 0;
5475 i = bca.nlines;
5476 while (i > 0) {
5477 i /= 10;
5478 bca.nlines_prec++;
5480 bca.repo = repo;
5482 error = got_blame(link_target ? link_target : in_repo_path, commit_id,
5483 repo, blame_cb, &bca, check_cancelled, NULL);
5484 done:
5485 free(in_repo_path);
5486 free(link_target);
5487 free(repo_path);
5488 free(cwd);
5489 free(commit_id);
5490 free(obj_id);
5491 if (commit)
5492 got_object_commit_close(commit);
5493 if (blob)
5494 got_object_blob_close(blob);
5495 if (worktree)
5496 got_worktree_close(worktree);
5497 if (repo) {
5498 const struct got_error *close_err = got_repo_close(repo);
5499 if (error == NULL)
5500 error = close_err;
5502 if (pack_fds) {
5503 const struct got_error *pack_err =
5504 got_repo_pack_fds_close(pack_fds);
5505 if (error == NULL)
5506 error = pack_err;
5508 if (bca.lines) {
5509 for (i = 0; i < bca.nlines; i++) {
5510 struct blame_line *bline = &bca.lines[i];
5511 free(bline->id_str);
5512 free(bline->committer);
5514 free(bca.lines);
5516 free(bca.line_offsets);
5517 if (bca.f && fclose(bca.f) == EOF && error == NULL)
5518 error = got_error_from_errno("fclose");
5519 return error;
5522 __dead static void
5523 usage_tree(void)
5525 fprintf(stderr,
5526 "usage: %s tree [-c commit] [-r repository-path] [-iR] [path]\n",
5527 getprogname());
5528 exit(1);
5531 static const struct got_error *
5532 print_entry(struct got_tree_entry *te, const char *id, const char *path,
5533 const char *root_path, struct got_repository *repo)
5535 const struct got_error *err = NULL;
5536 int is_root_path = (strcmp(path, root_path) == 0);
5537 const char *modestr = "";
5538 mode_t mode = got_tree_entry_get_mode(te);
5539 char *link_target = NULL;
5541 path += strlen(root_path);
5542 while (path[0] == '/')
5543 path++;
5545 if (got_object_tree_entry_is_submodule(te))
5546 modestr = "$";
5547 else if (S_ISLNK(mode)) {
5548 int i;
5550 err = got_tree_entry_get_symlink_target(&link_target, te, repo);
5551 if (err)
5552 return err;
5553 for (i = 0; i < strlen(link_target); i++) {
5554 if (!isprint((unsigned char)link_target[i]))
5555 link_target[i] = '?';
5558 modestr = "@";
5560 else if (S_ISDIR(mode))
5561 modestr = "/";
5562 else if (mode & S_IXUSR)
5563 modestr = "*";
5565 printf("%s%s%s%s%s%s%s\n", id ? id : "", path,
5566 is_root_path ? "" : "/", got_tree_entry_get_name(te), modestr,
5567 link_target ? " -> ": "", link_target ? link_target : "");
5569 free(link_target);
5570 return NULL;
5573 static const struct got_error *
5574 print_tree(const char *path, struct got_commit_object *commit,
5575 int show_ids, int recurse, const char *root_path,
5576 struct got_repository *repo)
5578 const struct got_error *err = NULL;
5579 struct got_object_id *tree_id = NULL;
5580 struct got_tree_object *tree = NULL;
5581 int nentries, i;
5583 err = got_object_id_by_path(&tree_id, repo, commit, path);
5584 if (err)
5585 goto done;
5587 err = got_object_open_as_tree(&tree, repo, tree_id);
5588 if (err)
5589 goto done;
5590 nentries = got_object_tree_get_nentries(tree);
5591 for (i = 0; i < nentries; i++) {
5592 struct got_tree_entry *te;
5593 char *id = NULL;
5595 if (sigint_received || sigpipe_received)
5596 break;
5598 te = got_object_tree_get_entry(tree, i);
5599 if (show_ids) {
5600 char *id_str;
5601 err = got_object_id_str(&id_str,
5602 got_tree_entry_get_id(te));
5603 if (err)
5604 goto done;
5605 if (asprintf(&id, "%s ", id_str) == -1) {
5606 err = got_error_from_errno("asprintf");
5607 free(id_str);
5608 goto done;
5610 free(id_str);
5612 err = print_entry(te, id, path, root_path, repo);
5613 free(id);
5614 if (err)
5615 goto done;
5617 if (recurse && S_ISDIR(got_tree_entry_get_mode(te))) {
5618 char *child_path;
5619 if (asprintf(&child_path, "%s%s%s", path,
5620 path[0] == '/' && path[1] == '\0' ? "" : "/",
5621 got_tree_entry_get_name(te)) == -1) {
5622 err = got_error_from_errno("asprintf");
5623 goto done;
5625 err = print_tree(child_path, commit, show_ids, 1,
5626 root_path, repo);
5627 free(child_path);
5628 if (err)
5629 goto done;
5632 done:
5633 if (tree)
5634 got_object_tree_close(tree);
5635 free(tree_id);
5636 return err;
5639 static const struct got_error *
5640 cmd_tree(int argc, char *argv[])
5642 const struct got_error *error;
5643 struct got_repository *repo = NULL;
5644 struct got_worktree *worktree = NULL;
5645 const char *path, *refname = NULL;
5646 char *cwd = NULL, *repo_path = NULL, *in_repo_path = NULL;
5647 struct got_object_id *commit_id = NULL;
5648 struct got_commit_object *commit = NULL;
5649 char *commit_id_str = NULL;
5650 int show_ids = 0, recurse = 0;
5651 int ch;
5652 int *pack_fds = NULL;
5654 #ifndef PROFILE
5655 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
5656 NULL) == -1)
5657 err(1, "pledge");
5658 #endif
5660 while ((ch = getopt(argc, argv, "c:r:iR")) != -1) {
5661 switch (ch) {
5662 case 'c':
5663 commit_id_str = optarg;
5664 break;
5665 case 'r':
5666 repo_path = realpath(optarg, NULL);
5667 if (repo_path == NULL)
5668 return got_error_from_errno2("realpath",
5669 optarg);
5670 got_path_strip_trailing_slashes(repo_path);
5671 break;
5672 case 'i':
5673 show_ids = 1;
5674 break;
5675 case 'R':
5676 recurse = 1;
5677 break;
5678 default:
5679 usage_tree();
5680 /* NOTREACHED */
5684 argc -= optind;
5685 argv += optind;
5687 if (argc == 1)
5688 path = argv[0];
5689 else if (argc > 1)
5690 usage_tree();
5691 else
5692 path = NULL;
5694 cwd = getcwd(NULL, 0);
5695 if (cwd == NULL) {
5696 error = got_error_from_errno("getcwd");
5697 goto done;
5700 error = got_repo_pack_fds_open(&pack_fds);
5701 if (error != NULL)
5702 goto done;
5704 if (repo_path == NULL) {
5705 error = got_worktree_open(&worktree, cwd);
5706 if (error && error->code != GOT_ERR_NOT_WORKTREE)
5707 goto done;
5708 else
5709 error = NULL;
5710 if (worktree) {
5711 repo_path =
5712 strdup(got_worktree_get_repo_path(worktree));
5713 if (repo_path == NULL)
5714 error = got_error_from_errno("strdup");
5715 if (error)
5716 goto done;
5717 } else {
5718 repo_path = strdup(cwd);
5719 if (repo_path == NULL) {
5720 error = got_error_from_errno("strdup");
5721 goto done;
5726 error = got_repo_open(&repo, repo_path, NULL, pack_fds);
5727 if (error != NULL)
5728 goto done;
5730 if (worktree) {
5731 const char *prefix = got_worktree_get_path_prefix(worktree);
5732 char *p;
5734 if (path == NULL)
5735 path = "";
5736 error = got_worktree_resolve_path(&p, worktree, path);
5737 if (error)
5738 goto done;
5739 if (asprintf(&in_repo_path, "%s%s%s", prefix,
5740 (p[0] != '\0' && !got_path_is_root_dir(prefix)) ? "/" : "",
5741 p) == -1) {
5742 error = got_error_from_errno("asprintf");
5743 free(p);
5744 goto done;
5746 free(p);
5747 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
5748 if (error)
5749 goto done;
5750 } else {
5751 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
5752 if (error)
5753 goto done;
5754 if (path == NULL)
5755 path = "/";
5756 error = got_repo_map_path(&in_repo_path, repo, path);
5757 if (error != NULL)
5758 goto done;
5761 if (commit_id_str == NULL) {
5762 struct got_reference *head_ref;
5763 if (worktree)
5764 refname = got_worktree_get_head_ref_name(worktree);
5765 else
5766 refname = GOT_REF_HEAD;
5767 error = got_ref_open(&head_ref, repo, refname, 0);
5768 if (error != NULL)
5769 goto done;
5770 error = got_ref_resolve(&commit_id, repo, head_ref);
5771 got_ref_close(head_ref);
5772 if (error != NULL)
5773 goto done;
5774 } else {
5775 struct got_reflist_head refs;
5776 TAILQ_INIT(&refs);
5777 error = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name,
5778 NULL);
5779 if (error)
5780 goto done;
5781 error = got_repo_match_object_id(&commit_id, NULL,
5782 commit_id_str, GOT_OBJ_TYPE_COMMIT, &refs, repo);
5783 got_ref_list_free(&refs);
5784 if (error)
5785 goto done;
5788 if (worktree) {
5789 /* Release work tree lock. */
5790 got_worktree_close(worktree);
5791 worktree = NULL;
5794 error = got_object_open_as_commit(&commit, repo, commit_id);
5795 if (error)
5796 goto done;
5798 error = print_tree(in_repo_path, commit, show_ids, recurse,
5799 in_repo_path, repo);
5800 done:
5801 free(in_repo_path);
5802 free(repo_path);
5803 free(cwd);
5804 free(commit_id);
5805 if (commit)
5806 got_object_commit_close(commit);
5807 if (worktree)
5808 got_worktree_close(worktree);
5809 if (repo) {
5810 const struct got_error *close_err = got_repo_close(repo);
5811 if (error == NULL)
5812 error = close_err;
5814 if (pack_fds) {
5815 const struct got_error *pack_err =
5816 got_repo_pack_fds_close(pack_fds);
5817 if (error == NULL)
5818 error = pack_err;
5820 return error;
5823 __dead static void
5824 usage_status(void)
5826 fprintf(stderr, "usage: %s status [-I] [-s status-codes ] "
5827 "[-S status-codes] [path ...]\n", getprogname());
5828 exit(1);
5831 struct got_status_arg {
5832 char *status_codes;
5833 int suppress;
5836 static const struct got_error *
5837 print_status(void *arg, unsigned char status, unsigned char staged_status,
5838 const char *path, struct got_object_id *blob_id,
5839 struct got_object_id *staged_blob_id, struct got_object_id *commit_id,
5840 int dirfd, const char *de_name)
5842 struct got_status_arg *st = arg;
5844 if (status == staged_status && (status == GOT_STATUS_DELETE))
5845 status = GOT_STATUS_NO_CHANGE;
5846 if (st != NULL && st->status_codes) {
5847 size_t ncodes = strlen(st->status_codes);
5848 int i, j = 0;
5850 for (i = 0; i < ncodes ; i++) {
5851 if (st->suppress) {
5852 if (status == st->status_codes[i] ||
5853 staged_status == st->status_codes[i]) {
5854 j++;
5855 continue;
5857 } else {
5858 if (status == st->status_codes[i] ||
5859 staged_status == st->status_codes[i])
5860 break;
5864 if (st->suppress && j == 0)
5865 goto print;
5867 if (i == ncodes)
5868 return NULL;
5870 print:
5871 printf("%c%c %s\n", status, staged_status, path);
5872 return NULL;
5875 static const struct got_error *
5876 cmd_status(int argc, char *argv[])
5878 const struct got_error *error = NULL;
5879 struct got_repository *repo = NULL;
5880 struct got_worktree *worktree = NULL;
5881 struct got_status_arg st;
5882 char *cwd = NULL;
5883 struct got_pathlist_head paths;
5884 struct got_pathlist_entry *pe;
5885 int ch, i, no_ignores = 0;
5886 int *pack_fds = NULL;
5888 TAILQ_INIT(&paths);
5890 memset(&st, 0, sizeof(st));
5891 st.status_codes = NULL;
5892 st.suppress = 0;
5894 while ((ch = getopt(argc, argv, "Is:S:")) != -1) {
5895 switch (ch) {
5896 case 'I':
5897 no_ignores = 1;
5898 break;
5899 case 'S':
5900 if (st.status_codes != NULL && st.suppress == 0)
5901 option_conflict('S', 's');
5902 st.suppress = 1;
5903 /* fallthrough */
5904 case 's':
5905 for (i = 0; i < strlen(optarg); i++) {
5906 switch (optarg[i]) {
5907 case GOT_STATUS_MODIFY:
5908 case GOT_STATUS_ADD:
5909 case GOT_STATUS_DELETE:
5910 case GOT_STATUS_CONFLICT:
5911 case GOT_STATUS_MISSING:
5912 case GOT_STATUS_OBSTRUCTED:
5913 case GOT_STATUS_UNVERSIONED:
5914 case GOT_STATUS_MODE_CHANGE:
5915 case GOT_STATUS_NONEXISTENT:
5916 break;
5917 default:
5918 errx(1, "invalid status code '%c'",
5919 optarg[i]);
5922 if (ch == 's' && st.suppress)
5923 option_conflict('s', 'S');
5924 st.status_codes = optarg;
5925 break;
5926 default:
5927 usage_status();
5928 /* NOTREACHED */
5932 argc -= optind;
5933 argv += optind;
5935 #ifndef PROFILE
5936 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
5937 NULL) == -1)
5938 err(1, "pledge");
5939 #endif
5940 cwd = getcwd(NULL, 0);
5941 if (cwd == NULL) {
5942 error = got_error_from_errno("getcwd");
5943 goto done;
5946 error = got_repo_pack_fds_open(&pack_fds);
5947 if (error != NULL)
5948 goto done;
5950 error = got_worktree_open(&worktree, cwd);
5951 if (error) {
5952 if (error->code == GOT_ERR_NOT_WORKTREE)
5953 error = wrap_not_worktree_error(error, "status", cwd);
5954 goto done;
5957 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
5958 NULL, pack_fds);
5959 if (error != NULL)
5960 goto done;
5962 error = apply_unveil(got_repo_get_path(repo), 1,
5963 got_worktree_get_root_path(worktree));
5964 if (error)
5965 goto done;
5967 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
5968 if (error)
5969 goto done;
5971 error = got_worktree_status(worktree, &paths, repo, no_ignores,
5972 print_status, &st, check_cancelled, NULL);
5973 done:
5974 if (pack_fds) {
5975 const struct got_error *pack_err =
5976 got_repo_pack_fds_close(pack_fds);
5977 if (error == NULL)
5978 error = pack_err;
5981 TAILQ_FOREACH(pe, &paths, entry)
5982 free((char *)pe->path);
5983 got_pathlist_free(&paths);
5984 free(cwd);
5985 return error;
5988 __dead static void
5989 usage_ref(void)
5991 fprintf(stderr,
5992 "usage: %s ref [-r repository] [-l] [-t] [-c object] "
5993 "[-s reference] [-d] [name]\n",
5994 getprogname());
5995 exit(1);
5998 static const struct got_error *
5999 list_refs(struct got_repository *repo, const char *refname, int sort_by_time)
6001 static const struct got_error *err = NULL;
6002 struct got_reflist_head refs;
6003 struct got_reflist_entry *re;
6005 TAILQ_INIT(&refs);
6006 err = got_ref_list(&refs, repo, refname, sort_by_time ?
6007 got_ref_cmp_by_commit_timestamp_descending : got_ref_cmp_by_name,
6008 repo);
6009 if (err)
6010 return err;
6012 TAILQ_FOREACH(re, &refs, entry) {
6013 char *refstr;
6014 refstr = got_ref_to_str(re->ref);
6015 if (refstr == NULL) {
6016 err = got_error_from_errno("got_ref_to_str");
6017 break;
6019 printf("%s: %s\n", got_ref_get_name(re->ref), refstr);
6020 free(refstr);
6023 got_ref_list_free(&refs);
6024 return err;
6027 static const struct got_error *
6028 delete_ref_by_name(struct got_repository *repo, const char *refname)
6030 const struct got_error *err;
6031 struct got_reference *ref;
6033 err = got_ref_open(&ref, repo, refname, 0);
6034 if (err)
6035 return err;
6037 err = delete_ref(repo, ref);
6038 got_ref_close(ref);
6039 return err;
6042 static const struct got_error *
6043 add_ref(struct got_repository *repo, const char *refname, const char *target)
6045 const struct got_error *err = NULL;
6046 struct got_object_id *id = NULL;
6047 struct got_reference *ref = NULL;
6048 struct got_reflist_head refs;
6051 * Don't let the user create a reference name with a leading '-'.
6052 * While technically a valid reference name, this case is usually
6053 * an unintended typo.
6055 if (refname[0] == '-')
6056 return got_error_path(refname, GOT_ERR_REF_NAME_MINUS);
6058 TAILQ_INIT(&refs);
6059 err = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name, NULL);
6060 if (err)
6061 goto done;
6062 err = got_repo_match_object_id(&id, NULL, target, GOT_OBJ_TYPE_ANY,
6063 &refs, repo);
6064 got_ref_list_free(&refs);
6065 if (err)
6066 goto done;
6068 err = got_ref_alloc(&ref, refname, id);
6069 if (err)
6070 goto done;
6072 err = got_ref_write(ref, repo);
6073 done:
6074 if (ref)
6075 got_ref_close(ref);
6076 free(id);
6077 return err;
6080 static const struct got_error *
6081 add_symref(struct got_repository *repo, const char *refname, const char *target)
6083 const struct got_error *err = NULL;
6084 struct got_reference *ref = NULL;
6085 struct got_reference *target_ref = NULL;
6088 * Don't let the user create a reference name with a leading '-'.
6089 * While technically a valid reference name, this case is usually
6090 * an unintended typo.
6092 if (refname[0] == '-')
6093 return got_error_path(refname, GOT_ERR_REF_NAME_MINUS);
6095 err = got_ref_open(&target_ref, repo, target, 0);
6096 if (err)
6097 return err;
6099 err = got_ref_alloc_symref(&ref, refname, target_ref);
6100 if (err)
6101 goto done;
6103 err = got_ref_write(ref, repo);
6104 done:
6105 if (target_ref)
6106 got_ref_close(target_ref);
6107 if (ref)
6108 got_ref_close(ref);
6109 return err;
6112 static const struct got_error *
6113 cmd_ref(int argc, char *argv[])
6115 const struct got_error *error = NULL;
6116 struct got_repository *repo = NULL;
6117 struct got_worktree *worktree = NULL;
6118 char *cwd = NULL, *repo_path = NULL;
6119 int ch, do_list = 0, do_delete = 0, sort_by_time = 0;
6120 const char *obj_arg = NULL, *symref_target= NULL;
6121 char *refname = NULL;
6122 int *pack_fds = NULL;
6124 while ((ch = getopt(argc, argv, "c:dr:ls:t")) != -1) {
6125 switch (ch) {
6126 case 'c':
6127 obj_arg = optarg;
6128 break;
6129 case 'd':
6130 do_delete = 1;
6131 break;
6132 case 'r':
6133 repo_path = realpath(optarg, NULL);
6134 if (repo_path == NULL)
6135 return got_error_from_errno2("realpath",
6136 optarg);
6137 got_path_strip_trailing_slashes(repo_path);
6138 break;
6139 case 'l':
6140 do_list = 1;
6141 break;
6142 case 's':
6143 symref_target = optarg;
6144 break;
6145 case 't':
6146 sort_by_time = 1;
6147 break;
6148 default:
6149 usage_ref();
6150 /* NOTREACHED */
6154 if (obj_arg && do_list)
6155 option_conflict('c', 'l');
6156 if (obj_arg && do_delete)
6157 option_conflict('c', 'd');
6158 if (obj_arg && symref_target)
6159 option_conflict('c', 's');
6160 if (symref_target && do_delete)
6161 option_conflict('s', 'd');
6162 if (symref_target && do_list)
6163 option_conflict('s', 'l');
6164 if (do_delete && do_list)
6165 option_conflict('d', 'l');
6166 if (sort_by_time && !do_list)
6167 errx(1, "-t option requires -l option");
6169 argc -= optind;
6170 argv += optind;
6172 if (do_list) {
6173 if (argc != 0 && argc != 1)
6174 usage_ref();
6175 if (argc == 1) {
6176 refname = strdup(argv[0]);
6177 if (refname == NULL) {
6178 error = got_error_from_errno("strdup");
6179 goto done;
6182 } else {
6183 if (argc != 1)
6184 usage_ref();
6185 refname = strdup(argv[0]);
6186 if (refname == NULL) {
6187 error = got_error_from_errno("strdup");
6188 goto done;
6192 if (refname)
6193 got_path_strip_trailing_slashes(refname);
6195 #ifndef PROFILE
6196 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
6197 "sendfd unveil", NULL) == -1)
6198 err(1, "pledge");
6199 #endif
6200 cwd = getcwd(NULL, 0);
6201 if (cwd == NULL) {
6202 error = got_error_from_errno("getcwd");
6203 goto done;
6206 error = got_repo_pack_fds_open(&pack_fds);
6207 if (error != NULL)
6208 goto done;
6210 if (repo_path == NULL) {
6211 error = got_worktree_open(&worktree, cwd);
6212 if (error && error->code != GOT_ERR_NOT_WORKTREE)
6213 goto done;
6214 else
6215 error = NULL;
6216 if (worktree) {
6217 repo_path =
6218 strdup(got_worktree_get_repo_path(worktree));
6219 if (repo_path == NULL)
6220 error = got_error_from_errno("strdup");
6221 if (error)
6222 goto done;
6223 } else {
6224 repo_path = strdup(cwd);
6225 if (repo_path == NULL) {
6226 error = got_error_from_errno("strdup");
6227 goto done;
6232 error = got_repo_open(&repo, repo_path, NULL, pack_fds);
6233 if (error != NULL)
6234 goto done;
6236 #ifndef PROFILE
6237 if (do_list) {
6238 /* Remove "cpath" promise. */
6239 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
6240 NULL) == -1)
6241 err(1, "pledge");
6243 #endif
6245 error = apply_unveil(got_repo_get_path(repo), do_list,
6246 worktree ? got_worktree_get_root_path(worktree) : NULL);
6247 if (error)
6248 goto done;
6250 if (do_list)
6251 error = list_refs(repo, refname, sort_by_time);
6252 else if (do_delete)
6253 error = delete_ref_by_name(repo, refname);
6254 else if (symref_target)
6255 error = add_symref(repo, refname, symref_target);
6256 else {
6257 if (obj_arg == NULL)
6258 usage_ref();
6259 error = add_ref(repo, refname, obj_arg);
6261 done:
6262 free(refname);
6263 if (repo) {
6264 const struct got_error *close_err = got_repo_close(repo);
6265 if (error == NULL)
6266 error = close_err;
6268 if (worktree)
6269 got_worktree_close(worktree);
6270 if (pack_fds) {
6271 const struct got_error *pack_err =
6272 got_repo_pack_fds_close(pack_fds);
6273 if (error == NULL)
6274 error = pack_err;
6276 free(cwd);
6277 free(repo_path);
6278 return error;
6281 __dead static void
6282 usage_branch(void)
6284 fprintf(stderr,
6285 "usage: %s branch [-c commit] [-d] [-r repository] [-l] [-t] "
6286 "[-n] [name]\n", getprogname());
6287 exit(1);
6290 static const struct got_error *
6291 list_branch(struct got_repository *repo, struct got_worktree *worktree,
6292 struct got_reference *ref)
6294 const struct got_error *err = NULL;
6295 const char *refname, *marker = " ";
6296 char *refstr;
6298 refname = got_ref_get_name(ref);
6299 if (worktree && strcmp(refname,
6300 got_worktree_get_head_ref_name(worktree)) == 0) {
6301 struct got_object_id *id = NULL;
6303 err = got_ref_resolve(&id, repo, ref);
6304 if (err)
6305 return err;
6306 if (got_object_id_cmp(id,
6307 got_worktree_get_base_commit_id(worktree)) == 0)
6308 marker = "* ";
6309 else
6310 marker = "~ ";
6311 free(id);
6314 if (strncmp(refname, "refs/heads/", 11) == 0)
6315 refname += 11;
6316 if (strncmp(refname, "refs/got/worktree/", 18) == 0)
6317 refname += 18;
6318 if (strncmp(refname, "refs/remotes/", 13) == 0)
6319 refname += 13;
6321 refstr = got_ref_to_str(ref);
6322 if (refstr == NULL)
6323 return got_error_from_errno("got_ref_to_str");
6325 printf("%s%s: %s\n", marker, refname, refstr);
6326 free(refstr);
6327 return NULL;
6330 static const struct got_error *
6331 show_current_branch(struct got_repository *repo, struct got_worktree *worktree)
6333 const char *refname;
6335 if (worktree == NULL)
6336 return got_error(GOT_ERR_NOT_WORKTREE);
6338 refname = got_worktree_get_head_ref_name(worktree);
6340 if (strncmp(refname, "refs/heads/", 11) == 0)
6341 refname += 11;
6342 if (strncmp(refname, "refs/got/worktree/", 18) == 0)
6343 refname += 18;
6345 printf("%s\n", refname);
6347 return NULL;
6350 static const struct got_error *
6351 list_branches(struct got_repository *repo, struct got_worktree *worktree,
6352 int sort_by_time)
6354 static const struct got_error *err = NULL;
6355 struct got_reflist_head refs;
6356 struct got_reflist_entry *re;
6357 struct got_reference *temp_ref = NULL;
6358 int rebase_in_progress, histedit_in_progress;
6360 TAILQ_INIT(&refs);
6362 if (worktree) {
6363 err = got_worktree_rebase_in_progress(&rebase_in_progress,
6364 worktree);
6365 if (err)
6366 return err;
6368 err = got_worktree_histedit_in_progress(&histedit_in_progress,
6369 worktree);
6370 if (err)
6371 return err;
6373 if (rebase_in_progress || histedit_in_progress) {
6374 err = got_ref_open(&temp_ref, repo,
6375 got_worktree_get_head_ref_name(worktree), 0);
6376 if (err)
6377 return err;
6378 list_branch(repo, worktree, temp_ref);
6379 got_ref_close(temp_ref);
6383 err = got_ref_list(&refs, repo, "refs/heads", sort_by_time ?
6384 got_ref_cmp_by_commit_timestamp_descending : got_ref_cmp_by_name,
6385 repo);
6386 if (err)
6387 return err;
6389 TAILQ_FOREACH(re, &refs, entry)
6390 list_branch(repo, worktree, re->ref);
6392 got_ref_list_free(&refs);
6394 err = got_ref_list(&refs, repo, "refs/remotes", sort_by_time ?
6395 got_ref_cmp_by_commit_timestamp_descending : got_ref_cmp_by_name,
6396 repo);
6397 if (err)
6398 return err;
6400 TAILQ_FOREACH(re, &refs, entry)
6401 list_branch(repo, worktree, re->ref);
6403 got_ref_list_free(&refs);
6405 return NULL;
6408 static const struct got_error *
6409 delete_branch(struct got_repository *repo, struct got_worktree *worktree,
6410 const char *branch_name)
6412 const struct got_error *err = NULL;
6413 struct got_reference *ref = NULL;
6414 char *refname, *remote_refname = NULL;
6416 if (strncmp(branch_name, "refs/", 5) == 0)
6417 branch_name += 5;
6418 if (strncmp(branch_name, "heads/", 6) == 0)
6419 branch_name += 6;
6420 else if (strncmp(branch_name, "remotes/", 8) == 0)
6421 branch_name += 8;
6423 if (asprintf(&refname, "refs/heads/%s", branch_name) == -1)
6424 return got_error_from_errno("asprintf");
6426 if (asprintf(&remote_refname, "refs/remotes/%s",
6427 branch_name) == -1) {
6428 err = got_error_from_errno("asprintf");
6429 goto done;
6432 err = got_ref_open(&ref, repo, refname, 0);
6433 if (err) {
6434 const struct got_error *err2;
6435 if (err->code != GOT_ERR_NOT_REF)
6436 goto done;
6438 * Keep 'err' intact such that if neither branch exists
6439 * we report "refs/heads" rather than "refs/remotes" in
6440 * our error message.
6442 err2 = got_ref_open(&ref, repo, remote_refname, 0);
6443 if (err2)
6444 goto done;
6445 err = NULL;
6448 if (worktree &&
6449 strcmp(got_worktree_get_head_ref_name(worktree),
6450 got_ref_get_name(ref)) == 0) {
6451 err = got_error_msg(GOT_ERR_SAME_BRANCH,
6452 "will not delete this work tree's current branch");
6453 goto done;
6456 err = delete_ref(repo, ref);
6457 done:
6458 if (ref)
6459 got_ref_close(ref);
6460 free(refname);
6461 free(remote_refname);
6462 return err;
6465 static const struct got_error *
6466 add_branch(struct got_repository *repo, const char *branch_name,
6467 struct got_object_id *base_commit_id)
6469 const struct got_error *err = NULL;
6470 struct got_reference *ref = NULL;
6471 char *base_refname = NULL, *refname = NULL;
6474 * Don't let the user create a branch name with a leading '-'.
6475 * While technically a valid reference name, this case is usually
6476 * an unintended typo.
6478 if (branch_name[0] == '-')
6479 return got_error_path(branch_name, GOT_ERR_REF_NAME_MINUS);
6481 if (strncmp(branch_name, "refs/heads/", 11) == 0)
6482 branch_name += 11;
6484 if (asprintf(&refname, "refs/heads/%s", branch_name) == -1) {
6485 err = got_error_from_errno("asprintf");
6486 goto done;
6489 err = got_ref_open(&ref, repo, refname, 0);
6490 if (err == NULL) {
6491 err = got_error(GOT_ERR_BRANCH_EXISTS);
6492 goto done;
6493 } else if (err->code != GOT_ERR_NOT_REF)
6494 goto done;
6496 err = got_ref_alloc(&ref, refname, base_commit_id);
6497 if (err)
6498 goto done;
6500 err = got_ref_write(ref, repo);
6501 done:
6502 if (ref)
6503 got_ref_close(ref);
6504 free(base_refname);
6505 free(refname);
6506 return err;
6509 static const struct got_error *
6510 cmd_branch(int argc, char *argv[])
6512 const struct got_error *error = NULL;
6513 struct got_repository *repo = NULL;
6514 struct got_worktree *worktree = NULL;
6515 char *cwd = NULL, *repo_path = NULL;
6516 int ch, do_list = 0, do_show = 0, do_update = 1, sort_by_time = 0;
6517 const char *delref = NULL, *commit_id_arg = NULL;
6518 struct got_reference *ref = NULL;
6519 struct got_pathlist_head paths;
6520 struct got_pathlist_entry *pe;
6521 struct got_object_id *commit_id = NULL;
6522 char *commit_id_str = NULL;
6523 int *pack_fds = NULL;
6525 TAILQ_INIT(&paths);
6527 while ((ch = getopt(argc, argv, "c:d:r:lnt")) != -1) {
6528 switch (ch) {
6529 case 'c':
6530 commit_id_arg = optarg;
6531 break;
6532 case 'd':
6533 delref = optarg;
6534 break;
6535 case 'r':
6536 repo_path = realpath(optarg, NULL);
6537 if (repo_path == NULL)
6538 return got_error_from_errno2("realpath",
6539 optarg);
6540 got_path_strip_trailing_slashes(repo_path);
6541 break;
6542 case 'l':
6543 do_list = 1;
6544 break;
6545 case 'n':
6546 do_update = 0;
6547 break;
6548 case 't':
6549 sort_by_time = 1;
6550 break;
6551 default:
6552 usage_branch();
6553 /* NOTREACHED */
6557 if (do_list && delref)
6558 option_conflict('l', 'd');
6559 if (sort_by_time && !do_list)
6560 errx(1, "-t option requires -l option");
6562 argc -= optind;
6563 argv += optind;
6565 if (!do_list && !delref && argc == 0)
6566 do_show = 1;
6568 if ((do_list || delref || do_show) && commit_id_arg != NULL)
6569 errx(1, "-c option can only be used when creating a branch");
6571 if (do_list || delref) {
6572 if (argc > 0)
6573 usage_branch();
6574 } else if (!do_show && argc != 1)
6575 usage_branch();
6577 #ifndef PROFILE
6578 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
6579 "sendfd unveil", NULL) == -1)
6580 err(1, "pledge");
6581 #endif
6582 cwd = getcwd(NULL, 0);
6583 if (cwd == NULL) {
6584 error = got_error_from_errno("getcwd");
6585 goto done;
6588 error = got_repo_pack_fds_open(&pack_fds);
6589 if (error != NULL)
6590 goto done;
6592 if (repo_path == NULL) {
6593 error = got_worktree_open(&worktree, cwd);
6594 if (error && error->code != GOT_ERR_NOT_WORKTREE)
6595 goto done;
6596 else
6597 error = NULL;
6598 if (worktree) {
6599 repo_path =
6600 strdup(got_worktree_get_repo_path(worktree));
6601 if (repo_path == NULL)
6602 error = got_error_from_errno("strdup");
6603 if (error)
6604 goto done;
6605 } else {
6606 repo_path = strdup(cwd);
6607 if (repo_path == NULL) {
6608 error = got_error_from_errno("strdup");
6609 goto done;
6614 error = got_repo_open(&repo, repo_path, NULL, pack_fds);
6615 if (error != NULL)
6616 goto done;
6618 #ifndef PROFILE
6619 if (do_list || do_show) {
6620 /* Remove "cpath" promise. */
6621 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
6622 NULL) == -1)
6623 err(1, "pledge");
6625 #endif
6627 error = apply_unveil(got_repo_get_path(repo), do_list,
6628 worktree ? got_worktree_get_root_path(worktree) : NULL);
6629 if (error)
6630 goto done;
6632 if (do_show)
6633 error = show_current_branch(repo, worktree);
6634 else if (do_list)
6635 error = list_branches(repo, worktree, sort_by_time);
6636 else if (delref)
6637 error = delete_branch(repo, worktree, delref);
6638 else {
6639 struct got_reflist_head refs;
6640 TAILQ_INIT(&refs);
6641 error = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name,
6642 NULL);
6643 if (error)
6644 goto done;
6645 if (commit_id_arg == NULL)
6646 commit_id_arg = worktree ?
6647 got_worktree_get_head_ref_name(worktree) :
6648 GOT_REF_HEAD;
6649 error = got_repo_match_object_id(&commit_id, NULL,
6650 commit_id_arg, GOT_OBJ_TYPE_COMMIT, &refs, repo);
6651 got_ref_list_free(&refs);
6652 if (error)
6653 goto done;
6654 error = add_branch(repo, argv[0], commit_id);
6655 if (error)
6656 goto done;
6657 if (worktree && do_update) {
6658 struct got_update_progress_arg upa;
6659 char *branch_refname = NULL;
6661 error = got_object_id_str(&commit_id_str, commit_id);
6662 if (error)
6663 goto done;
6664 error = get_worktree_paths_from_argv(&paths, 0, NULL,
6665 worktree);
6666 if (error)
6667 goto done;
6668 if (asprintf(&branch_refname, "refs/heads/%s", argv[0])
6669 == -1) {
6670 error = got_error_from_errno("asprintf");
6671 goto done;
6673 error = got_ref_open(&ref, repo, branch_refname, 0);
6674 free(branch_refname);
6675 if (error)
6676 goto done;
6677 error = switch_head_ref(ref, commit_id, worktree,
6678 repo);
6679 if (error)
6680 goto done;
6681 error = got_worktree_set_base_commit_id(worktree, repo,
6682 commit_id);
6683 if (error)
6684 goto done;
6685 memset(&upa, 0, sizeof(upa));
6686 error = got_worktree_checkout_files(worktree, &paths,
6687 repo, update_progress, &upa, check_cancelled,
6688 NULL);
6689 if (error)
6690 goto done;
6691 if (upa.did_something) {
6692 printf("Updated to %s: %s\n",
6693 got_worktree_get_head_ref_name(worktree),
6694 commit_id_str);
6696 print_update_progress_stats(&upa);
6699 done:
6700 if (ref)
6701 got_ref_close(ref);
6702 if (repo) {
6703 const struct got_error *close_err = got_repo_close(repo);
6704 if (error == NULL)
6705 error = close_err;
6707 if (worktree)
6708 got_worktree_close(worktree);
6709 if (pack_fds) {
6710 const struct got_error *pack_err =
6711 got_repo_pack_fds_close(pack_fds);
6712 if (error == NULL)
6713 error = pack_err;
6715 free(cwd);
6716 free(repo_path);
6717 free(commit_id);
6718 free(commit_id_str);
6719 TAILQ_FOREACH(pe, &paths, entry)
6720 free((char *)pe->path);
6721 got_pathlist_free(&paths);
6722 return error;
6726 __dead static void
6727 usage_tag(void)
6729 fprintf(stderr,
6730 "usage: %s tag [-c commit] [-r repository] [-l] "
6731 "[-m message] name\n", getprogname());
6732 exit(1);
6735 #if 0
6736 static const struct got_error *
6737 sort_tags(struct got_reflist_head *sorted, struct got_reflist_head *tags)
6739 const struct got_error *err = NULL;
6740 struct got_reflist_entry *re, *se, *new;
6741 struct got_object_id *re_id, *se_id;
6742 struct got_tag_object *re_tag, *se_tag;
6743 time_t re_time, se_time;
6745 STAILQ_FOREACH(re, tags, entry) {
6746 se = STAILQ_FIRST(sorted);
6747 if (se == NULL) {
6748 err = got_reflist_entry_dup(&new, re);
6749 if (err)
6750 return err;
6751 STAILQ_INSERT_HEAD(sorted, new, entry);
6752 continue;
6753 } else {
6754 err = got_ref_resolve(&re_id, repo, re->ref);
6755 if (err)
6756 break;
6757 err = got_object_open_as_tag(&re_tag, repo, re_id);
6758 free(re_id);
6759 if (err)
6760 break;
6761 re_time = got_object_tag_get_tagger_time(re_tag);
6762 got_object_tag_close(re_tag);
6765 while (se) {
6766 err = got_ref_resolve(&se_id, repo, re->ref);
6767 if (err)
6768 break;
6769 err = got_object_open_as_tag(&se_tag, repo, se_id);
6770 free(se_id);
6771 if (err)
6772 break;
6773 se_time = got_object_tag_get_tagger_time(se_tag);
6774 got_object_tag_close(se_tag);
6776 if (se_time > re_time) {
6777 err = got_reflist_entry_dup(&new, re);
6778 if (err)
6779 return err;
6780 STAILQ_INSERT_AFTER(sorted, se, new, entry);
6781 break;
6783 se = STAILQ_NEXT(se, entry);
6784 continue;
6787 done:
6788 return err;
6790 #endif
6792 static const struct got_error *
6793 list_tags(struct got_repository *repo)
6795 static const struct got_error *err = NULL;
6796 struct got_reflist_head refs;
6797 struct got_reflist_entry *re;
6799 TAILQ_INIT(&refs);
6801 err = got_ref_list(&refs, repo, "refs/tags", got_ref_cmp_tags, repo);
6802 if (err)
6803 return err;
6805 TAILQ_FOREACH(re, &refs, entry) {
6806 const char *refname;
6807 char *refstr, *tagmsg0, *tagmsg, *line, *id_str, *datestr;
6808 char datebuf[26];
6809 const char *tagger;
6810 time_t tagger_time;
6811 struct got_object_id *id;
6812 struct got_tag_object *tag;
6813 struct got_commit_object *commit = NULL;
6815 refname = got_ref_get_name(re->ref);
6816 if (strncmp(refname, "refs/tags/", 10) != 0)
6817 continue;
6818 refname += 10;
6819 refstr = got_ref_to_str(re->ref);
6820 if (refstr == NULL) {
6821 err = got_error_from_errno("got_ref_to_str");
6822 break;
6824 printf("%stag %s %s\n", GOT_COMMIT_SEP_STR, refname, refstr);
6825 free(refstr);
6827 err = got_ref_resolve(&id, repo, re->ref);
6828 if (err)
6829 break;
6830 err = got_object_open_as_tag(&tag, repo, id);
6831 if (err) {
6832 if (err->code != GOT_ERR_OBJ_TYPE) {
6833 free(id);
6834 break;
6836 /* "lightweight" tag */
6837 err = got_object_open_as_commit(&commit, repo, id);
6838 if (err) {
6839 free(id);
6840 break;
6842 tagger = got_object_commit_get_committer(commit);
6843 tagger_time =
6844 got_object_commit_get_committer_time(commit);
6845 err = got_object_id_str(&id_str, id);
6846 free(id);
6847 if (err)
6848 break;
6849 } else {
6850 free(id);
6851 tagger = got_object_tag_get_tagger(tag);
6852 tagger_time = got_object_tag_get_tagger_time(tag);
6853 err = got_object_id_str(&id_str,
6854 got_object_tag_get_object_id(tag));
6855 if (err)
6856 break;
6858 printf("from: %s\n", tagger);
6859 datestr = get_datestr(&tagger_time, datebuf);
6860 if (datestr)
6861 printf("date: %s UTC\n", datestr);
6862 if (commit)
6863 printf("object: %s %s\n", GOT_OBJ_LABEL_COMMIT, id_str);
6864 else {
6865 switch (got_object_tag_get_object_type(tag)) {
6866 case GOT_OBJ_TYPE_BLOB:
6867 printf("object: %s %s\n", GOT_OBJ_LABEL_BLOB,
6868 id_str);
6869 break;
6870 case GOT_OBJ_TYPE_TREE:
6871 printf("object: %s %s\n", GOT_OBJ_LABEL_TREE,
6872 id_str);
6873 break;
6874 case GOT_OBJ_TYPE_COMMIT:
6875 printf("object: %s %s\n", GOT_OBJ_LABEL_COMMIT,
6876 id_str);
6877 break;
6878 case GOT_OBJ_TYPE_TAG:
6879 printf("object: %s %s\n", GOT_OBJ_LABEL_TAG,
6880 id_str);
6881 break;
6882 default:
6883 break;
6886 free(id_str);
6887 if (commit) {
6888 err = got_object_commit_get_logmsg(&tagmsg0, commit);
6889 if (err)
6890 break;
6891 got_object_commit_close(commit);
6892 } else {
6893 tagmsg0 = strdup(got_object_tag_get_message(tag));
6894 got_object_tag_close(tag);
6895 if (tagmsg0 == NULL) {
6896 err = got_error_from_errno("strdup");
6897 break;
6901 tagmsg = tagmsg0;
6902 do {
6903 line = strsep(&tagmsg, "\n");
6904 if (line)
6905 printf(" %s\n", line);
6906 } while (line);
6907 free(tagmsg0);
6910 got_ref_list_free(&refs);
6911 return NULL;
6914 static const struct got_error *
6915 get_tag_message(char **tagmsg, char **tagmsg_path, const char *commit_id_str,
6916 const char *tag_name, const char *repo_path)
6918 const struct got_error *err = NULL;
6919 char *template = NULL, *initial_content = NULL;
6920 char *editor = NULL;
6921 int initial_content_len;
6922 int fd = -1;
6924 if (asprintf(&template, GOT_TMPDIR_STR "/got-tagmsg") == -1) {
6925 err = got_error_from_errno("asprintf");
6926 goto done;
6929 initial_content_len = asprintf(&initial_content,
6930 "\n# tagging commit %s as %s\n",
6931 commit_id_str, tag_name);
6932 if (initial_content_len == -1) {
6933 err = got_error_from_errno("asprintf");
6934 goto done;
6937 err = got_opentemp_named_fd(tagmsg_path, &fd, template);
6938 if (err)
6939 goto done;
6941 if (write(fd, initial_content, initial_content_len) == -1) {
6942 err = got_error_from_errno2("write", *tagmsg_path);
6943 goto done;
6946 err = get_editor(&editor);
6947 if (err)
6948 goto done;
6949 err = edit_logmsg(tagmsg, editor, *tagmsg_path, initial_content,
6950 initial_content_len, 1);
6951 done:
6952 free(initial_content);
6953 free(template);
6954 free(editor);
6956 if (fd != -1 && close(fd) == -1 && err == NULL)
6957 err = got_error_from_errno2("close", *tagmsg_path);
6959 /* Editor is done; we can now apply unveil(2) */
6960 if (err == NULL)
6961 err = apply_unveil(repo_path, 0, NULL);
6962 if (err) {
6963 free(*tagmsg);
6964 *tagmsg = NULL;
6966 return err;
6969 static const struct got_error *
6970 add_tag(struct got_repository *repo, const char *tagger,
6971 const char *tag_name, const char *commit_arg, const char *tagmsg_arg)
6973 const struct got_error *err = NULL;
6974 struct got_object_id *commit_id = NULL, *tag_id = NULL;
6975 char *label = NULL, *commit_id_str = NULL;
6976 struct got_reference *ref = NULL;
6977 char *refname = NULL, *tagmsg = NULL;
6978 char *tagmsg_path = NULL, *tag_id_str = NULL;
6979 int preserve_tagmsg = 0;
6980 struct got_reflist_head refs;
6982 TAILQ_INIT(&refs);
6985 * Don't let the user create a tag name with a leading '-'.
6986 * While technically a valid reference name, this case is usually
6987 * an unintended typo.
6989 if (tag_name[0] == '-')
6990 return got_error_path(tag_name, GOT_ERR_REF_NAME_MINUS);
6992 err = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name, NULL);
6993 if (err)
6994 goto done;
6996 err = got_repo_match_object_id(&commit_id, &label, commit_arg,
6997 GOT_OBJ_TYPE_COMMIT, &refs, repo);
6998 if (err)
6999 goto done;
7001 err = got_object_id_str(&commit_id_str, commit_id);
7002 if (err)
7003 goto done;
7005 if (strncmp("refs/tags/", tag_name, 10) == 0) {
7006 refname = strdup(tag_name);
7007 if (refname == NULL) {
7008 err = got_error_from_errno("strdup");
7009 goto done;
7011 tag_name += 10;
7012 } else if (asprintf(&refname, "refs/tags/%s", tag_name) == -1) {
7013 err = got_error_from_errno("asprintf");
7014 goto done;
7017 err = got_ref_open(&ref, repo, refname, 0);
7018 if (err == NULL) {
7019 err = got_error(GOT_ERR_TAG_EXISTS);
7020 goto done;
7021 } else if (err->code != GOT_ERR_NOT_REF)
7022 goto done;
7024 if (tagmsg_arg == NULL) {
7025 err = get_tag_message(&tagmsg, &tagmsg_path, commit_id_str,
7026 tag_name, got_repo_get_path(repo));
7027 if (err) {
7028 if (err->code != GOT_ERR_COMMIT_MSG_EMPTY &&
7029 tagmsg_path != NULL)
7030 preserve_tagmsg = 1;
7031 goto done;
7035 err = got_object_tag_create(&tag_id, tag_name, commit_id,
7036 tagger, time(NULL), tagmsg ? tagmsg : tagmsg_arg, repo);
7037 if (err) {
7038 if (tagmsg_path)
7039 preserve_tagmsg = 1;
7040 goto done;
7043 err = got_ref_alloc(&ref, refname, tag_id);
7044 if (err) {
7045 if (tagmsg_path)
7046 preserve_tagmsg = 1;
7047 goto done;
7050 err = got_ref_write(ref, repo);
7051 if (err) {
7052 if (tagmsg_path)
7053 preserve_tagmsg = 1;
7054 goto done;
7057 err = got_object_id_str(&tag_id_str, tag_id);
7058 if (err) {
7059 if (tagmsg_path)
7060 preserve_tagmsg = 1;
7061 goto done;
7063 printf("Created tag %s\n", tag_id_str);
7064 done:
7065 if (preserve_tagmsg) {
7066 fprintf(stderr, "%s: tag message preserved in %s\n",
7067 getprogname(), tagmsg_path);
7068 } else if (tagmsg_path && unlink(tagmsg_path) == -1 && err == NULL)
7069 err = got_error_from_errno2("unlink", tagmsg_path);
7070 free(tag_id_str);
7071 if (ref)
7072 got_ref_close(ref);
7073 free(commit_id);
7074 free(commit_id_str);
7075 free(refname);
7076 free(tagmsg);
7077 free(tagmsg_path);
7078 got_ref_list_free(&refs);
7079 return err;
7082 static const struct got_error *
7083 cmd_tag(int argc, char *argv[])
7085 const struct got_error *error = NULL;
7086 struct got_repository *repo = NULL;
7087 struct got_worktree *worktree = NULL;
7088 char *cwd = NULL, *repo_path = NULL, *commit_id_str = NULL;
7089 char *gitconfig_path = NULL, *tagger = NULL;
7090 const char *tag_name, *commit_id_arg = NULL, *tagmsg = NULL;
7091 int ch, do_list = 0;
7092 int *pack_fds = NULL;
7094 while ((ch = getopt(argc, argv, "c:m:r:l")) != -1) {
7095 switch (ch) {
7096 case 'c':
7097 commit_id_arg = optarg;
7098 break;
7099 case 'm':
7100 tagmsg = optarg;
7101 break;
7102 case 'r':
7103 repo_path = realpath(optarg, NULL);
7104 if (repo_path == NULL)
7105 return got_error_from_errno2("realpath",
7106 optarg);
7107 got_path_strip_trailing_slashes(repo_path);
7108 break;
7109 case 'l':
7110 do_list = 1;
7111 break;
7112 default:
7113 usage_tag();
7114 /* NOTREACHED */
7118 argc -= optind;
7119 argv += optind;
7121 if (do_list) {
7122 if (commit_id_arg != NULL)
7123 errx(1,
7124 "-c option can only be used when creating a tag");
7125 if (tagmsg)
7126 option_conflict('l', 'm');
7127 if (argc > 0)
7128 usage_tag();
7129 } else if (argc != 1)
7130 usage_tag();
7132 tag_name = argv[0];
7134 #ifndef PROFILE
7135 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
7136 "sendfd unveil", NULL) == -1)
7137 err(1, "pledge");
7138 #endif
7139 cwd = getcwd(NULL, 0);
7140 if (cwd == NULL) {
7141 error = got_error_from_errno("getcwd");
7142 goto done;
7145 error = got_repo_pack_fds_open(&pack_fds);
7146 if (error != NULL)
7147 goto done;
7149 if (repo_path == NULL) {
7150 error = got_worktree_open(&worktree, cwd);
7151 if (error && error->code != GOT_ERR_NOT_WORKTREE)
7152 goto done;
7153 else
7154 error = NULL;
7155 if (worktree) {
7156 repo_path =
7157 strdup(got_worktree_get_repo_path(worktree));
7158 if (repo_path == NULL)
7159 error = got_error_from_errno("strdup");
7160 if (error)
7161 goto done;
7162 } else {
7163 repo_path = strdup(cwd);
7164 if (repo_path == NULL) {
7165 error = got_error_from_errno("strdup");
7166 goto done;
7171 if (do_list) {
7172 if (worktree) {
7173 /* Release work tree lock. */
7174 got_worktree_close(worktree);
7175 worktree = NULL;
7177 error = got_repo_open(&repo, repo_path, NULL, pack_fds);
7178 if (error != NULL)
7179 goto done;
7181 #ifndef PROFILE
7182 /* Remove "cpath" promise. */
7183 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
7184 NULL) == -1)
7185 err(1, "pledge");
7186 #endif
7187 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
7188 if (error)
7189 goto done;
7190 error = list_tags(repo);
7191 } else {
7192 error = get_gitconfig_path(&gitconfig_path);
7193 if (error)
7194 goto done;
7195 error = got_repo_open(&repo, repo_path, gitconfig_path,
7196 pack_fds);
7197 if (error != NULL)
7198 goto done;
7200 error = get_author(&tagger, repo, worktree);
7201 if (error)
7202 goto done;
7203 if (worktree) {
7204 /* Release work tree lock. */
7205 got_worktree_close(worktree);
7206 worktree = NULL;
7209 if (tagmsg) {
7210 error = apply_unveil(got_repo_get_path(repo), 0, NULL);
7211 if (error)
7212 goto done;
7215 if (commit_id_arg == NULL) {
7216 struct got_reference *head_ref;
7217 struct got_object_id *commit_id;
7218 error = got_ref_open(&head_ref, repo,
7219 worktree ? got_worktree_get_head_ref_name(worktree)
7220 : GOT_REF_HEAD, 0);
7221 if (error)
7222 goto done;
7223 error = got_ref_resolve(&commit_id, repo, head_ref);
7224 got_ref_close(head_ref);
7225 if (error)
7226 goto done;
7227 error = got_object_id_str(&commit_id_str, commit_id);
7228 free(commit_id);
7229 if (error)
7230 goto done;
7233 error = add_tag(repo, tagger, tag_name,
7234 commit_id_str ? commit_id_str : commit_id_arg, tagmsg);
7236 done:
7237 if (repo) {
7238 const struct got_error *close_err = got_repo_close(repo);
7239 if (error == NULL)
7240 error = close_err;
7242 if (worktree)
7243 got_worktree_close(worktree);
7244 if (pack_fds) {
7245 const struct got_error *pack_err =
7246 got_repo_pack_fds_close(pack_fds);
7247 if (error == NULL)
7248 error = pack_err;
7250 free(cwd);
7251 free(repo_path);
7252 free(gitconfig_path);
7253 free(commit_id_str);
7254 free(tagger);
7255 return error;
7258 __dead static void
7259 usage_add(void)
7261 fprintf(stderr, "usage: %s add [-R] [-I] path ...\n",
7262 getprogname());
7263 exit(1);
7266 static const struct got_error *
7267 add_progress(void *arg, unsigned char status, const char *path)
7269 while (path[0] == '/')
7270 path++;
7271 printf("%c %s\n", status, path);
7272 return NULL;
7275 static const struct got_error *
7276 cmd_add(int argc, char *argv[])
7278 const struct got_error *error = NULL;
7279 struct got_repository *repo = NULL;
7280 struct got_worktree *worktree = NULL;
7281 char *cwd = NULL;
7282 struct got_pathlist_head paths;
7283 struct got_pathlist_entry *pe;
7284 int ch, can_recurse = 0, no_ignores = 0;
7285 int *pack_fds = NULL;
7287 TAILQ_INIT(&paths);
7289 while ((ch = getopt(argc, argv, "IR")) != -1) {
7290 switch (ch) {
7291 case 'I':
7292 no_ignores = 1;
7293 break;
7294 case 'R':
7295 can_recurse = 1;
7296 break;
7297 default:
7298 usage_add();
7299 /* NOTREACHED */
7303 argc -= optind;
7304 argv += optind;
7306 #ifndef PROFILE
7307 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
7308 NULL) == -1)
7309 err(1, "pledge");
7310 #endif
7311 if (argc < 1)
7312 usage_add();
7314 cwd = getcwd(NULL, 0);
7315 if (cwd == NULL) {
7316 error = got_error_from_errno("getcwd");
7317 goto done;
7320 error = got_repo_pack_fds_open(&pack_fds);
7321 if (error != NULL)
7322 goto done;
7324 error = got_worktree_open(&worktree, cwd);
7325 if (error) {
7326 if (error->code == GOT_ERR_NOT_WORKTREE)
7327 error = wrap_not_worktree_error(error, "add", cwd);
7328 goto done;
7331 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
7332 NULL, pack_fds);
7333 if (error != NULL)
7334 goto done;
7336 error = apply_unveil(got_repo_get_path(repo), 1,
7337 got_worktree_get_root_path(worktree));
7338 if (error)
7339 goto done;
7341 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
7342 if (error)
7343 goto done;
7345 if (!can_recurse) {
7346 char *ondisk_path;
7347 struct stat sb;
7348 TAILQ_FOREACH(pe, &paths, entry) {
7349 if (asprintf(&ondisk_path, "%s/%s",
7350 got_worktree_get_root_path(worktree),
7351 pe->path) == -1) {
7352 error = got_error_from_errno("asprintf");
7353 goto done;
7355 if (lstat(ondisk_path, &sb) == -1) {
7356 if (errno == ENOENT) {
7357 free(ondisk_path);
7358 continue;
7360 error = got_error_from_errno2("lstat",
7361 ondisk_path);
7362 free(ondisk_path);
7363 goto done;
7365 free(ondisk_path);
7366 if (S_ISDIR(sb.st_mode)) {
7367 error = got_error_msg(GOT_ERR_BAD_PATH,
7368 "adding directories requires -R option");
7369 goto done;
7374 error = got_worktree_schedule_add(worktree, &paths, add_progress,
7375 NULL, repo, no_ignores);
7376 done:
7377 if (repo) {
7378 const struct got_error *close_err = got_repo_close(repo);
7379 if (error == NULL)
7380 error = close_err;
7382 if (worktree)
7383 got_worktree_close(worktree);
7384 if (pack_fds) {
7385 const struct got_error *pack_err =
7386 got_repo_pack_fds_close(pack_fds);
7387 if (error == NULL)
7388 error = pack_err;
7390 TAILQ_FOREACH(pe, &paths, entry)
7391 free((char *)pe->path);
7392 got_pathlist_free(&paths);
7393 free(cwd);
7394 return error;
7397 __dead static void
7398 usage_remove(void)
7400 fprintf(stderr, "usage: %s remove [-f] [-k] [-R] [-s status-codes] "
7401 "path ...\n", getprogname());
7402 exit(1);
7405 static const struct got_error *
7406 print_remove_status(void *arg, unsigned char status,
7407 unsigned char staged_status, const char *path)
7409 while (path[0] == '/')
7410 path++;
7411 if (status == GOT_STATUS_NONEXISTENT)
7412 return NULL;
7413 if (status == staged_status && (status == GOT_STATUS_DELETE))
7414 status = GOT_STATUS_NO_CHANGE;
7415 printf("%c%c %s\n", status, staged_status, path);
7416 return NULL;
7419 static const struct got_error *
7420 cmd_remove(int argc, char *argv[])
7422 const struct got_error *error = NULL;
7423 struct got_worktree *worktree = NULL;
7424 struct got_repository *repo = NULL;
7425 const char *status_codes = NULL;
7426 char *cwd = NULL;
7427 struct got_pathlist_head paths;
7428 struct got_pathlist_entry *pe;
7429 int ch, delete_local_mods = 0, can_recurse = 0, keep_on_disk = 0, i;
7430 int ignore_missing_paths = 0;
7431 int *pack_fds = NULL;
7433 TAILQ_INIT(&paths);
7435 while ((ch = getopt(argc, argv, "fkRs:")) != -1) {
7436 switch (ch) {
7437 case 'f':
7438 delete_local_mods = 1;
7439 ignore_missing_paths = 1;
7440 break;
7441 case 'k':
7442 keep_on_disk = 1;
7443 break;
7444 case 'R':
7445 can_recurse = 1;
7446 break;
7447 case 's':
7448 for (i = 0; i < strlen(optarg); i++) {
7449 switch (optarg[i]) {
7450 case GOT_STATUS_MODIFY:
7451 delete_local_mods = 1;
7452 break;
7453 case GOT_STATUS_MISSING:
7454 ignore_missing_paths = 1;
7455 break;
7456 default:
7457 errx(1, "invalid status code '%c'",
7458 optarg[i]);
7461 status_codes = optarg;
7462 break;
7463 default:
7464 usage_remove();
7465 /* NOTREACHED */
7469 argc -= optind;
7470 argv += optind;
7472 #ifndef PROFILE
7473 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
7474 NULL) == -1)
7475 err(1, "pledge");
7476 #endif
7477 if (argc < 1)
7478 usage_remove();
7480 cwd = getcwd(NULL, 0);
7481 if (cwd == NULL) {
7482 error = got_error_from_errno("getcwd");
7483 goto done;
7486 error = got_repo_pack_fds_open(&pack_fds);
7487 if (error != NULL)
7488 goto done;
7490 error = got_worktree_open(&worktree, cwd);
7491 if (error) {
7492 if (error->code == GOT_ERR_NOT_WORKTREE)
7493 error = wrap_not_worktree_error(error, "remove", cwd);
7494 goto done;
7497 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
7498 NULL, pack_fds);
7499 if (error)
7500 goto done;
7502 error = apply_unveil(got_repo_get_path(repo), 1,
7503 got_worktree_get_root_path(worktree));
7504 if (error)
7505 goto done;
7507 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
7508 if (error)
7509 goto done;
7511 if (!can_recurse) {
7512 char *ondisk_path;
7513 struct stat sb;
7514 TAILQ_FOREACH(pe, &paths, entry) {
7515 if (asprintf(&ondisk_path, "%s/%s",
7516 got_worktree_get_root_path(worktree),
7517 pe->path) == -1) {
7518 error = got_error_from_errno("asprintf");
7519 goto done;
7521 if (lstat(ondisk_path, &sb) == -1) {
7522 if (errno == ENOENT) {
7523 free(ondisk_path);
7524 continue;
7526 error = got_error_from_errno2("lstat",
7527 ondisk_path);
7528 free(ondisk_path);
7529 goto done;
7531 free(ondisk_path);
7532 if (S_ISDIR(sb.st_mode)) {
7533 error = got_error_msg(GOT_ERR_BAD_PATH,
7534 "removing directories requires -R option");
7535 goto done;
7540 error = got_worktree_schedule_delete(worktree, &paths,
7541 delete_local_mods, status_codes, print_remove_status, NULL,
7542 repo, keep_on_disk, ignore_missing_paths);
7543 done:
7544 if (repo) {
7545 const struct got_error *close_err = got_repo_close(repo);
7546 if (error == NULL)
7547 error = close_err;
7549 if (worktree)
7550 got_worktree_close(worktree);
7551 if (pack_fds) {
7552 const struct got_error *pack_err =
7553 got_repo_pack_fds_close(pack_fds);
7554 if (error == NULL)
7555 error = pack_err;
7557 TAILQ_FOREACH(pe, &paths, entry)
7558 free((char *)pe->path);
7559 got_pathlist_free(&paths);
7560 free(cwd);
7561 return error;
7564 __dead static void
7565 usage_patch(void)
7567 fprintf(stderr, "usage: %s patch [-n] [-p strip-count] "
7568 "[-R] [patchfile]\n", getprogname());
7569 exit(1);
7572 static const struct got_error *
7573 patch_from_stdin(int *patchfd)
7575 const struct got_error *err = NULL;
7576 ssize_t r;
7577 char *path, buf[BUFSIZ];
7578 sig_t sighup, sigint, sigquit;
7580 err = got_opentemp_named_fd(&path, patchfd,
7581 GOT_TMPDIR_STR "/got-patch");
7582 if (err)
7583 return err;
7584 unlink(path);
7585 free(path);
7587 sighup = signal(SIGHUP, SIG_DFL);
7588 sigint = signal(SIGINT, SIG_DFL);
7589 sigquit = signal(SIGQUIT, SIG_DFL);
7591 for (;;) {
7592 r = read(0, buf, sizeof(buf));
7593 if (r == -1) {
7594 err = got_error_from_errno("read");
7595 break;
7597 if (r == 0)
7598 break;
7599 if (write(*patchfd, buf, r) == -1) {
7600 err = got_error_from_errno("write");
7601 break;
7605 signal(SIGHUP, sighup);
7606 signal(SIGINT, sigint);
7607 signal(SIGQUIT, sigquit);
7609 if (err == NULL && lseek(*patchfd, 0, SEEK_SET) == -1)
7610 err = got_error_from_errno("lseek");
7612 if (err != NULL) {
7613 close(*patchfd);
7614 *patchfd = -1;
7617 return err;
7620 static const struct got_error *
7621 patch_progress(void *arg, const char *old, const char *new,
7622 unsigned char status, const struct got_error *error, long old_from,
7623 long old_lines, long new_from, long new_lines, long offset,
7624 const struct got_error *hunk_err)
7626 const char *path = new == NULL ? old : new;
7628 while (*path == '/')
7629 path++;
7631 if (status != 0)
7632 printf("%c %s\n", status, path);
7634 if (error != NULL)
7635 fprintf(stderr, "%s: %s\n", getprogname(), error->msg);
7637 if (offset != 0 || hunk_err != NULL) {
7638 printf("@@ -%ld,%ld +%ld,%ld @@ ", old_from,
7639 old_lines, new_from, new_lines);
7640 if (hunk_err != NULL)
7641 printf("%s\n", hunk_err->msg);
7642 else
7643 printf("applied with offset %ld\n", offset);
7646 return NULL;
7649 static const struct got_error *
7650 cmd_patch(int argc, char *argv[])
7652 const struct got_error *error = NULL, *close_error = NULL;
7653 struct got_worktree *worktree = NULL;
7654 struct got_repository *repo = NULL;
7655 const char *errstr;
7656 char *cwd = NULL;
7657 int ch, nop = 0, strip = -1, reverse = 0;
7658 int patchfd;
7659 int *pack_fds = NULL;
7661 while ((ch = getopt(argc, argv, "np:R")) != -1) {
7662 switch (ch) {
7663 case 'n':
7664 nop = 1;
7665 break;
7666 case 'p':
7667 strip = strtonum(optarg, 0, INT_MAX, &errstr);
7668 if (errstr != NULL)
7669 errx(1, "pathname strip count is %s: %s",
7670 errstr, optarg);
7671 break;
7672 case 'R':
7673 reverse = 1;
7674 break;
7675 default:
7676 usage_patch();
7677 /* NOTREACHED */
7681 argc -= optind;
7682 argv += optind;
7684 if (argc == 0) {
7685 error = patch_from_stdin(&patchfd);
7686 if (error)
7687 return error;
7688 } else if (argc == 1) {
7689 patchfd = open(argv[0], O_RDONLY);
7690 if (patchfd == -1) {
7691 error = got_error_from_errno2("open", argv[0]);
7692 return error;
7694 } else
7695 usage_patch();
7697 if ((cwd = getcwd(NULL, 0)) == NULL) {
7698 error = got_error_from_errno("getcwd");
7699 goto done;
7702 error = got_repo_pack_fds_open(&pack_fds);
7703 if (error != NULL)
7704 goto done;
7706 error = got_worktree_open(&worktree, cwd);
7707 if (error != NULL)
7708 goto done;
7710 const char *repo_path = got_worktree_get_repo_path(worktree);
7711 error = got_repo_open(&repo, repo_path, NULL, pack_fds);
7712 if (error != NULL)
7713 goto done;
7715 error = apply_unveil(got_repo_get_path(repo), 0,
7716 got_worktree_get_root_path(worktree));
7717 if (error != NULL)
7718 goto done;
7720 #ifndef PROFILE
7721 if (pledge("stdio rpath wpath cpath fattr proc exec sendfd flock",
7722 NULL) == -1)
7723 err(1, "pledge");
7724 #endif
7726 error = got_patch(patchfd, worktree, repo, nop, strip, reverse,
7727 &patch_progress, NULL, check_cancelled, NULL);
7729 done:
7730 if (repo) {
7731 close_error = got_repo_close(repo);
7732 if (error == NULL)
7733 error = close_error;
7735 if (worktree != NULL) {
7736 close_error = got_worktree_close(worktree);
7737 if (error == NULL)
7738 error = close_error;
7740 if (pack_fds) {
7741 const struct got_error *pack_err =
7742 got_repo_pack_fds_close(pack_fds);
7743 if (error == NULL)
7744 error = pack_err;
7746 free(cwd);
7747 return error;
7750 __dead static void
7751 usage_revert(void)
7753 fprintf(stderr, "usage: %s revert [-p] [-F response-script] [-R] "
7754 "path ...\n", getprogname());
7755 exit(1);
7758 static const struct got_error *
7759 revert_progress(void *arg, unsigned char status, const char *path)
7761 if (status == GOT_STATUS_UNVERSIONED)
7762 return NULL;
7764 while (path[0] == '/')
7765 path++;
7766 printf("%c %s\n", status, path);
7767 return NULL;
7770 struct choose_patch_arg {
7771 FILE *patch_script_file;
7772 const char *action;
7775 static const struct got_error *
7776 show_change(unsigned char status, const char *path, FILE *patch_file, int n,
7777 int nchanges, const char *action)
7779 const struct got_error *err;
7780 char *line = NULL;
7781 size_t linesize = 0;
7782 ssize_t linelen;
7784 switch (status) {
7785 case GOT_STATUS_ADD:
7786 printf("A %s\n%s this addition? [y/n] ", path, action);
7787 break;
7788 case GOT_STATUS_DELETE:
7789 printf("D %s\n%s this deletion? [y/n] ", path, action);
7790 break;
7791 case GOT_STATUS_MODIFY:
7792 if (fseek(patch_file, 0L, SEEK_SET) == -1)
7793 return got_error_from_errno("fseek");
7794 printf(GOT_COMMIT_SEP_STR);
7795 while ((linelen = getline(&line, &linesize, patch_file)) != -1)
7796 printf("%s", line);
7797 if (linelen == -1 && ferror(patch_file)) {
7798 err = got_error_from_errno("getline");
7799 free(line);
7800 return err;
7802 free(line);
7803 printf(GOT_COMMIT_SEP_STR);
7804 printf("M %s (change %d of %d)\n%s this change? [y/n/q] ",
7805 path, n, nchanges, action);
7806 break;
7807 default:
7808 return got_error_path(path, GOT_ERR_FILE_STATUS);
7811 return NULL;
7814 static const struct got_error *
7815 choose_patch(int *choice, void *arg, unsigned char status, const char *path,
7816 FILE *patch_file, int n, int nchanges)
7818 const struct got_error *err = NULL;
7819 char *line = NULL;
7820 size_t linesize = 0;
7821 ssize_t linelen;
7822 int resp = ' ';
7823 struct choose_patch_arg *a = arg;
7825 *choice = GOT_PATCH_CHOICE_NONE;
7827 if (a->patch_script_file) {
7828 char *nl;
7829 err = show_change(status, path, patch_file, n, nchanges,
7830 a->action);
7831 if (err)
7832 return err;
7833 linelen = getline(&line, &linesize, a->patch_script_file);
7834 if (linelen == -1) {
7835 if (ferror(a->patch_script_file))
7836 return got_error_from_errno("getline");
7837 return NULL;
7839 nl = strchr(line, '\n');
7840 if (nl)
7841 *nl = '\0';
7842 if (strcmp(line, "y") == 0) {
7843 *choice = GOT_PATCH_CHOICE_YES;
7844 printf("y\n");
7845 } else if (strcmp(line, "n") == 0) {
7846 *choice = GOT_PATCH_CHOICE_NO;
7847 printf("n\n");
7848 } else if (strcmp(line, "q") == 0 &&
7849 status == GOT_STATUS_MODIFY) {
7850 *choice = GOT_PATCH_CHOICE_QUIT;
7851 printf("q\n");
7852 } else
7853 printf("invalid response '%s'\n", line);
7854 free(line);
7855 return NULL;
7858 while (resp != 'y' && resp != 'n' && resp != 'q') {
7859 err = show_change(status, path, patch_file, n, nchanges,
7860 a->action);
7861 if (err)
7862 return err;
7863 resp = getchar();
7864 if (resp == '\n')
7865 resp = getchar();
7866 if (status == GOT_STATUS_MODIFY) {
7867 if (resp != 'y' && resp != 'n' && resp != 'q') {
7868 printf("invalid response '%c'\n", resp);
7869 resp = ' ';
7871 } else if (resp != 'y' && resp != 'n') {
7872 printf("invalid response '%c'\n", resp);
7873 resp = ' ';
7877 if (resp == 'y')
7878 *choice = GOT_PATCH_CHOICE_YES;
7879 else if (resp == 'n')
7880 *choice = GOT_PATCH_CHOICE_NO;
7881 else if (resp == 'q' && status == GOT_STATUS_MODIFY)
7882 *choice = GOT_PATCH_CHOICE_QUIT;
7884 return NULL;
7887 static const struct got_error *
7888 cmd_revert(int argc, char *argv[])
7890 const struct got_error *error = NULL;
7891 struct got_worktree *worktree = NULL;
7892 struct got_repository *repo = NULL;
7893 char *cwd = NULL, *path = NULL;
7894 struct got_pathlist_head paths;
7895 struct got_pathlist_entry *pe;
7896 int ch, can_recurse = 0, pflag = 0;
7897 FILE *patch_script_file = NULL;
7898 const char *patch_script_path = NULL;
7899 struct choose_patch_arg cpa;
7900 int *pack_fds = NULL;
7902 TAILQ_INIT(&paths);
7904 while ((ch = getopt(argc, argv, "pF:R")) != -1) {
7905 switch (ch) {
7906 case 'p':
7907 pflag = 1;
7908 break;
7909 case 'F':
7910 patch_script_path = optarg;
7911 break;
7912 case 'R':
7913 can_recurse = 1;
7914 break;
7915 default:
7916 usage_revert();
7917 /* NOTREACHED */
7921 argc -= optind;
7922 argv += optind;
7924 #ifndef PROFILE
7925 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
7926 "unveil", NULL) == -1)
7927 err(1, "pledge");
7928 #endif
7929 if (argc < 1)
7930 usage_revert();
7931 if (patch_script_path && !pflag)
7932 errx(1, "-F option can only be used together with -p option");
7934 cwd = getcwd(NULL, 0);
7935 if (cwd == NULL) {
7936 error = got_error_from_errno("getcwd");
7937 goto done;
7940 error = got_repo_pack_fds_open(&pack_fds);
7941 if (error != NULL)
7942 goto done;
7944 error = got_worktree_open(&worktree, cwd);
7945 if (error) {
7946 if (error->code == GOT_ERR_NOT_WORKTREE)
7947 error = wrap_not_worktree_error(error, "revert", cwd);
7948 goto done;
7951 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
7952 NULL, pack_fds);
7953 if (error != NULL)
7954 goto done;
7956 if (patch_script_path) {
7957 patch_script_file = fopen(patch_script_path, "re");
7958 if (patch_script_file == NULL) {
7959 error = got_error_from_errno2("fopen",
7960 patch_script_path);
7961 goto done;
7964 error = apply_unveil(got_repo_get_path(repo), 1,
7965 got_worktree_get_root_path(worktree));
7966 if (error)
7967 goto done;
7969 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
7970 if (error)
7971 goto done;
7973 if (!can_recurse) {
7974 char *ondisk_path;
7975 struct stat sb;
7976 TAILQ_FOREACH(pe, &paths, entry) {
7977 if (asprintf(&ondisk_path, "%s/%s",
7978 got_worktree_get_root_path(worktree),
7979 pe->path) == -1) {
7980 error = got_error_from_errno("asprintf");
7981 goto done;
7983 if (lstat(ondisk_path, &sb) == -1) {
7984 if (errno == ENOENT) {
7985 free(ondisk_path);
7986 continue;
7988 error = got_error_from_errno2("lstat",
7989 ondisk_path);
7990 free(ondisk_path);
7991 goto done;
7993 free(ondisk_path);
7994 if (S_ISDIR(sb.st_mode)) {
7995 error = got_error_msg(GOT_ERR_BAD_PATH,
7996 "reverting directories requires -R option");
7997 goto done;
8002 cpa.patch_script_file = patch_script_file;
8003 cpa.action = "revert";
8004 error = got_worktree_revert(worktree, &paths, revert_progress, NULL,
8005 pflag ? choose_patch : NULL, &cpa, repo);
8006 done:
8007 if (patch_script_file && fclose(patch_script_file) == EOF &&
8008 error == NULL)
8009 error = got_error_from_errno2("fclose", patch_script_path);
8010 if (repo) {
8011 const struct got_error *close_err = got_repo_close(repo);
8012 if (error == NULL)
8013 error = close_err;
8015 if (worktree)
8016 got_worktree_close(worktree);
8017 if (pack_fds) {
8018 const struct got_error *pack_err =
8019 got_repo_pack_fds_close(pack_fds);
8020 if (error == NULL)
8021 error = pack_err;
8023 free(path);
8024 free(cwd);
8025 return error;
8028 __dead static void
8029 usage_commit(void)
8031 fprintf(stderr, "usage: %s commit [-F path] [-m msg] [-N] [-S] "
8032 "[path ...]\n", getprogname());
8033 exit(1);
8036 struct collect_commit_logmsg_arg {
8037 const char *cmdline_log;
8038 const char *prepared_log;
8039 int non_interactive;
8040 const char *editor;
8041 const char *worktree_path;
8042 const char *branch_name;
8043 const char *repo_path;
8044 char *logmsg_path;
8048 static const struct got_error *
8049 read_prepared_logmsg(char **logmsg, const char *path)
8051 const struct got_error *err = NULL;
8052 FILE *f = NULL;
8053 struct stat sb;
8054 size_t r;
8056 *logmsg = NULL;
8057 memset(&sb, 0, sizeof(sb));
8059 f = fopen(path, "re");
8060 if (f == NULL)
8061 return got_error_from_errno2("fopen", path);
8063 if (fstat(fileno(f), &sb) == -1) {
8064 err = got_error_from_errno2("fstat", path);
8065 goto done;
8067 if (sb.st_size == 0) {
8068 err = got_error(GOT_ERR_COMMIT_MSG_EMPTY);
8069 goto done;
8072 *logmsg = malloc(sb.st_size + 1);
8073 if (*logmsg == NULL) {
8074 err = got_error_from_errno("malloc");
8075 goto done;
8078 r = fread(*logmsg, 1, sb.st_size, f);
8079 if (r != sb.st_size) {
8080 if (ferror(f))
8081 err = got_error_from_errno2("fread", path);
8082 else
8083 err = got_error(GOT_ERR_IO);
8084 goto done;
8086 (*logmsg)[sb.st_size] = '\0';
8087 done:
8088 if (fclose(f) == EOF && err == NULL)
8089 err = got_error_from_errno2("fclose", path);
8090 if (err) {
8091 free(*logmsg);
8092 *logmsg = NULL;
8094 return err;
8098 static const struct got_error *
8099 collect_commit_logmsg(struct got_pathlist_head *commitable_paths, char **logmsg,
8100 void *arg)
8102 char *initial_content = NULL;
8103 struct got_pathlist_entry *pe;
8104 const struct got_error *err = NULL;
8105 char *template = NULL;
8106 struct collect_commit_logmsg_arg *a = arg;
8107 int initial_content_len;
8108 int fd = -1;
8109 size_t len;
8111 /* if a message was specified on the command line, just use it */
8112 if (a->cmdline_log != NULL && strlen(a->cmdline_log) != 0) {
8113 len = strlen(a->cmdline_log) + 1;
8114 *logmsg = malloc(len + 1);
8115 if (*logmsg == NULL)
8116 return got_error_from_errno("malloc");
8117 strlcpy(*logmsg, a->cmdline_log, len);
8118 return NULL;
8119 } else if (a->prepared_log != NULL && a->non_interactive)
8120 return read_prepared_logmsg(logmsg, a->prepared_log);
8122 if (asprintf(&template, "%s/logmsg", a->worktree_path) == -1)
8123 return got_error_from_errno("asprintf");
8125 err = got_opentemp_named_fd(&a->logmsg_path, &fd, template);
8126 if (err)
8127 goto done;
8129 if (a->prepared_log) {
8130 char *msg;
8131 err = read_prepared_logmsg(&msg, a->prepared_log);
8132 if (err)
8133 goto done;
8134 if (write(fd, msg, strlen(msg)) == -1) {
8135 err = got_error_from_errno2("write", a->logmsg_path);
8136 free(msg);
8137 goto done;
8139 free(msg);
8142 initial_content_len = asprintf(&initial_content,
8143 "\n# changes to be committed on branch %s:\n",
8144 a->branch_name);
8145 if (initial_content_len == -1) {
8146 err = got_error_from_errno("asprintf");
8147 goto done;
8150 if (write(fd, initial_content, initial_content_len) == -1) {
8151 err = got_error_from_errno2("write", a->logmsg_path);
8152 goto done;
8155 TAILQ_FOREACH(pe, commitable_paths, entry) {
8156 struct got_commitable *ct = pe->data;
8157 dprintf(fd, "# %c %s\n",
8158 got_commitable_get_status(ct),
8159 got_commitable_get_path(ct));
8162 err = edit_logmsg(logmsg, a->editor, a->logmsg_path, initial_content,
8163 initial_content_len, a->prepared_log ? 0 : 1);
8164 done:
8165 free(initial_content);
8166 free(template);
8168 if (fd != -1 && close(fd) == -1 && err == NULL)
8169 err = got_error_from_errno2("close", a->logmsg_path);
8171 /* Editor is done; we can now apply unveil(2) */
8172 if (err == NULL)
8173 err = apply_unveil(a->repo_path, 0, a->worktree_path);
8174 if (err) {
8175 free(*logmsg);
8176 *logmsg = NULL;
8178 return err;
8181 static const struct got_error *
8182 cmd_commit(int argc, char *argv[])
8184 const struct got_error *error = NULL;
8185 struct got_worktree *worktree = NULL;
8186 struct got_repository *repo = NULL;
8187 char *cwd = NULL, *id_str = NULL;
8188 struct got_object_id *id = NULL;
8189 const char *logmsg = NULL;
8190 char *prepared_logmsg = NULL;
8191 struct collect_commit_logmsg_arg cl_arg;
8192 char *gitconfig_path = NULL, *editor = NULL, *author = NULL;
8193 int ch, rebase_in_progress, histedit_in_progress, preserve_logmsg = 0;
8194 int allow_bad_symlinks = 0, non_interactive = 0, merge_in_progress = 0;
8195 struct got_pathlist_head paths;
8196 int *pack_fds = NULL;
8198 TAILQ_INIT(&paths);
8199 cl_arg.logmsg_path = NULL;
8201 while ((ch = getopt(argc, argv, "F:m:NS")) != -1) {
8202 switch (ch) {
8203 case 'F':
8204 if (logmsg != NULL)
8205 option_conflict('F', 'm');
8206 prepared_logmsg = realpath(optarg, NULL);
8207 if (prepared_logmsg == NULL)
8208 return got_error_from_errno2("realpath",
8209 optarg);
8210 break;
8211 case 'm':
8212 if (prepared_logmsg)
8213 option_conflict('m', 'F');
8214 logmsg = optarg;
8215 break;
8216 case 'N':
8217 non_interactive = 1;
8218 break;
8219 case 'S':
8220 allow_bad_symlinks = 1;
8221 break;
8222 default:
8223 usage_commit();
8224 /* NOTREACHED */
8228 argc -= optind;
8229 argv += optind;
8231 #ifndef PROFILE
8232 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
8233 "unveil", NULL) == -1)
8234 err(1, "pledge");
8235 #endif
8236 cwd = getcwd(NULL, 0);
8237 if (cwd == NULL) {
8238 error = got_error_from_errno("getcwd");
8239 goto done;
8242 error = got_repo_pack_fds_open(&pack_fds);
8243 if (error != NULL)
8244 goto done;
8246 error = got_worktree_open(&worktree, cwd);
8247 if (error) {
8248 if (error->code == GOT_ERR_NOT_WORKTREE)
8249 error = wrap_not_worktree_error(error, "commit", cwd);
8250 goto done;
8253 error = got_worktree_rebase_in_progress(&rebase_in_progress, worktree);
8254 if (error)
8255 goto done;
8256 if (rebase_in_progress) {
8257 error = got_error(GOT_ERR_REBASING);
8258 goto done;
8261 error = got_worktree_histedit_in_progress(&histedit_in_progress,
8262 worktree);
8263 if (error)
8264 goto done;
8266 error = get_gitconfig_path(&gitconfig_path);
8267 if (error)
8268 goto done;
8269 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
8270 gitconfig_path, pack_fds);
8271 if (error != NULL)
8272 goto done;
8274 error = got_worktree_merge_in_progress(&merge_in_progress, worktree, repo);
8275 if (error)
8276 goto done;
8277 if (merge_in_progress) {
8278 error = got_error(GOT_ERR_MERGE_BUSY);
8279 goto done;
8282 error = get_author(&author, repo, worktree);
8283 if (error)
8284 return error;
8287 * unveil(2) traverses exec(2); if an editor is used we have
8288 * to apply unveil after the log message has been written.
8290 if (logmsg == NULL || strlen(logmsg) == 0)
8291 error = get_editor(&editor);
8292 else
8293 error = apply_unveil(got_repo_get_path(repo), 0,
8294 got_worktree_get_root_path(worktree));
8295 if (error)
8296 goto done;
8298 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
8299 if (error)
8300 goto done;
8302 cl_arg.editor = editor;
8303 cl_arg.cmdline_log = logmsg;
8304 cl_arg.prepared_log = prepared_logmsg;
8305 cl_arg.non_interactive = non_interactive;
8306 cl_arg.worktree_path = got_worktree_get_root_path(worktree);
8307 cl_arg.branch_name = got_worktree_get_head_ref_name(worktree);
8308 if (!histedit_in_progress) {
8309 if (strncmp(cl_arg.branch_name, "refs/heads/", 11) != 0) {
8310 error = got_error(GOT_ERR_COMMIT_BRANCH);
8311 goto done;
8313 cl_arg.branch_name += 11;
8315 cl_arg.repo_path = got_repo_get_path(repo);
8316 error = got_worktree_commit(&id, worktree, &paths, author, NULL,
8317 allow_bad_symlinks, collect_commit_logmsg, &cl_arg,
8318 print_status, NULL, repo);
8319 if (error) {
8320 if (error->code != GOT_ERR_COMMIT_MSG_EMPTY &&
8321 cl_arg.logmsg_path != NULL)
8322 preserve_logmsg = 1;
8323 goto done;
8326 error = got_object_id_str(&id_str, id);
8327 if (error)
8328 goto done;
8329 printf("Created commit %s\n", id_str);
8330 done:
8331 if (preserve_logmsg) {
8332 fprintf(stderr, "%s: log message preserved in %s\n",
8333 getprogname(), cl_arg.logmsg_path);
8334 } else if (cl_arg.logmsg_path && unlink(cl_arg.logmsg_path) == -1 &&
8335 error == NULL)
8336 error = got_error_from_errno2("unlink", cl_arg.logmsg_path);
8337 free(cl_arg.logmsg_path);
8338 if (repo) {
8339 const struct got_error *close_err = got_repo_close(repo);
8340 if (error == NULL)
8341 error = close_err;
8343 if (worktree)
8344 got_worktree_close(worktree);
8345 if (pack_fds) {
8346 const struct got_error *pack_err =
8347 got_repo_pack_fds_close(pack_fds);
8348 if (error == NULL)
8349 error = pack_err;
8351 free(cwd);
8352 free(id_str);
8353 free(gitconfig_path);
8354 free(editor);
8355 free(author);
8356 free(prepared_logmsg);
8357 return error;
8360 __dead static void
8361 usage_send(void)
8363 fprintf(stderr, "usage: %s send [-a] [-b branch] [-d branch] [-f] "
8364 "[-r repository-path] [-t tag] [-T] [-q] [-v] "
8365 "[remote-repository]\n", getprogname());
8366 exit(1);
8369 static void
8370 print_load_info(int print_colored, int print_found, int print_trees,
8371 int ncolored, int nfound, int ntrees)
8373 if (print_colored) {
8374 printf("%d commit%s colored", ncolored,
8375 ncolored == 1 ? "" : "s");
8377 if (print_found) {
8378 printf("%s%d object%s found",
8379 ncolored > 0 ? "; " : "",
8380 nfound, nfound == 1 ? "" : "s");
8382 if (print_trees) {
8383 printf("; %d tree%s scanned", ntrees,
8384 ntrees == 1 ? "" : "s");
8388 struct got_send_progress_arg {
8389 char last_scaled_packsize[FMT_SCALED_STRSIZE];
8390 int verbosity;
8391 int last_ncolored;
8392 int last_nfound;
8393 int last_ntrees;
8394 int loading_done;
8395 int last_ncommits;
8396 int last_nobj_total;
8397 int last_p_deltify;
8398 int last_p_written;
8399 int last_p_sent;
8400 int printed_something;
8401 int sent_something;
8402 struct got_pathlist_head *delete_branches;
8405 static const struct got_error *
8406 send_progress(void *arg, int ncolored, int nfound, int ntrees,
8407 off_t packfile_size, int ncommits, int nobj_total, int nobj_deltify,
8408 int nobj_written, off_t bytes_sent, const char *refname, int success)
8410 struct got_send_progress_arg *a = arg;
8411 char scaled_packsize[FMT_SCALED_STRSIZE];
8412 char scaled_sent[FMT_SCALED_STRSIZE];
8413 int p_deltify = 0, p_written = 0, p_sent = 0;
8414 int print_colored = 0, print_found = 0, print_trees = 0;
8415 int print_searching = 0, print_total = 0;
8416 int print_deltify = 0, print_written = 0, print_sent = 0;
8418 if (a->verbosity < 0)
8419 return NULL;
8421 if (refname) {
8422 const char *status = success ? "accepted" : "rejected";
8424 if (success) {
8425 struct got_pathlist_entry *pe;
8426 TAILQ_FOREACH(pe, a->delete_branches, entry) {
8427 const char *branchname = pe->path;
8428 if (got_path_cmp(branchname, refname,
8429 strlen(branchname), strlen(refname)) == 0) {
8430 status = "deleted";
8431 a->sent_something = 1;
8432 break;
8437 if (a->printed_something)
8438 putchar('\n');
8439 printf("Server has %s %s", status, refname);
8440 a->printed_something = 1;
8441 return NULL;
8444 if (a->last_ncolored != ncolored) {
8445 print_colored = 1;
8446 a->last_ncolored = ncolored;
8449 if (a->last_nfound != nfound) {
8450 print_colored = 1;
8451 print_found = 1;
8452 a->last_nfound = nfound;
8455 if (a->last_ntrees != ntrees) {
8456 print_colored = 1;
8457 print_found = 1;
8458 print_trees = 1;
8459 a->last_ntrees = ntrees;
8462 if ((print_colored || print_found || print_trees) &&
8463 !a->loading_done) {
8464 printf("\r");
8465 print_load_info(print_colored, print_found, print_trees,
8466 ncolored, nfound, ntrees);
8467 a->printed_something = 1;
8468 fflush(stdout);
8469 return NULL;
8470 } else if (!a->loading_done) {
8471 printf("\r");
8472 print_load_info(1, 1, 1, ncolored, nfound, ntrees);
8473 printf("\n");
8474 a->loading_done = 1;
8477 if (fmt_scaled(packfile_size, scaled_packsize) == -1)
8478 return got_error_from_errno("fmt_scaled");
8479 if (fmt_scaled(bytes_sent, scaled_sent) == -1)
8480 return got_error_from_errno("fmt_scaled");
8482 if (a->last_ncommits != ncommits) {
8483 print_searching = 1;
8484 a->last_ncommits = ncommits;
8487 if (a->last_nobj_total != nobj_total) {
8488 print_searching = 1;
8489 print_total = 1;
8490 a->last_nobj_total = nobj_total;
8493 if (packfile_size > 0 && (a->last_scaled_packsize[0] == '\0' ||
8494 strcmp(scaled_packsize, a->last_scaled_packsize)) != 0) {
8495 if (strlcpy(a->last_scaled_packsize, scaled_packsize,
8496 FMT_SCALED_STRSIZE) >= FMT_SCALED_STRSIZE)
8497 return got_error(GOT_ERR_NO_SPACE);
8500 if (nobj_deltify > 0 || nobj_written > 0) {
8501 if (nobj_deltify > 0) {
8502 p_deltify = (nobj_deltify * 100) / nobj_total;
8503 if (p_deltify != a->last_p_deltify) {
8504 a->last_p_deltify = p_deltify;
8505 print_searching = 1;
8506 print_total = 1;
8507 print_deltify = 1;
8510 if (nobj_written > 0) {
8511 p_written = (nobj_written * 100) / nobj_total;
8512 if (p_written != a->last_p_written) {
8513 a->last_p_written = p_written;
8514 print_searching = 1;
8515 print_total = 1;
8516 print_deltify = 1;
8517 print_written = 1;
8522 if (bytes_sent > 0) {
8523 p_sent = (bytes_sent * 100) / packfile_size;
8524 if (p_sent != a->last_p_sent) {
8525 a->last_p_sent = p_sent;
8526 print_searching = 1;
8527 print_total = 1;
8528 print_deltify = 1;
8529 print_written = 1;
8530 print_sent = 1;
8532 a->sent_something = 1;
8535 if (print_searching || print_total || print_deltify || print_written ||
8536 print_sent)
8537 printf("\r");
8538 if (print_searching)
8539 printf("packing %d reference%s", ncommits,
8540 ncommits == 1 ? "" : "s");
8541 if (print_total)
8542 printf("; %d object%s", nobj_total,
8543 nobj_total == 1 ? "" : "s");
8544 if (print_deltify)
8545 printf("; deltify: %d%%", p_deltify);
8546 if (print_sent)
8547 printf("; uploading pack: %*s %d%%", FMT_SCALED_STRSIZE - 2,
8548 scaled_packsize, p_sent);
8549 else if (print_written)
8550 printf("; writing pack: %*s %d%%", FMT_SCALED_STRSIZE - 2,
8551 scaled_packsize, p_written);
8552 if (print_searching || print_total || print_deltify ||
8553 print_written || print_sent) {
8554 a->printed_something = 1;
8555 fflush(stdout);
8557 return NULL;
8560 static const struct got_error *
8561 cmd_send(int argc, char *argv[])
8563 const struct got_error *error = NULL;
8564 char *cwd = NULL, *repo_path = NULL;
8565 const char *remote_name;
8566 char *proto = NULL, *host = NULL, *port = NULL;
8567 char *repo_name = NULL, *server_path = NULL;
8568 const struct got_remote_repo *remotes, *remote = NULL;
8569 int nremotes, nbranches = 0, ntags = 0, ndelete_branches = 0;
8570 struct got_repository *repo = NULL;
8571 struct got_worktree *worktree = NULL;
8572 const struct got_gotconfig *repo_conf = NULL, *worktree_conf = NULL;
8573 struct got_pathlist_head branches;
8574 struct got_pathlist_head tags;
8575 struct got_reflist_head all_branches;
8576 struct got_reflist_head all_tags;
8577 struct got_pathlist_head delete_args;
8578 struct got_pathlist_head delete_branches;
8579 struct got_reflist_entry *re;
8580 struct got_pathlist_entry *pe;
8581 int i, ch, sendfd = -1, sendstatus;
8582 pid_t sendpid = -1;
8583 struct got_send_progress_arg spa;
8584 int verbosity = 0, overwrite_refs = 0;
8585 int send_all_branches = 0, send_all_tags = 0;
8586 struct got_reference *ref = NULL;
8587 int *pack_fds = NULL;
8589 TAILQ_INIT(&branches);
8590 TAILQ_INIT(&tags);
8591 TAILQ_INIT(&all_branches);
8592 TAILQ_INIT(&all_tags);
8593 TAILQ_INIT(&delete_args);
8594 TAILQ_INIT(&delete_branches);
8596 while ((ch = getopt(argc, argv, "ab:d:fr:t:Tvq")) != -1) {
8597 switch (ch) {
8598 case 'a':
8599 send_all_branches = 1;
8600 break;
8601 case 'b':
8602 error = got_pathlist_append(&branches, optarg, NULL);
8603 if (error)
8604 return error;
8605 nbranches++;
8606 break;
8607 case 'd':
8608 error = got_pathlist_append(&delete_args, optarg, NULL);
8609 if (error)
8610 return error;
8611 break;
8612 case 'f':
8613 overwrite_refs = 1;
8614 break;
8615 case 'r':
8616 repo_path = realpath(optarg, NULL);
8617 if (repo_path == NULL)
8618 return got_error_from_errno2("realpath",
8619 optarg);
8620 got_path_strip_trailing_slashes(repo_path);
8621 break;
8622 case 't':
8623 error = got_pathlist_append(&tags, optarg, NULL);
8624 if (error)
8625 return error;
8626 ntags++;
8627 break;
8628 case 'T':
8629 send_all_tags = 1;
8630 break;
8631 case 'v':
8632 if (verbosity < 0)
8633 verbosity = 0;
8634 else if (verbosity < 3)
8635 verbosity++;
8636 break;
8637 case 'q':
8638 verbosity = -1;
8639 break;
8640 default:
8641 usage_send();
8642 /* NOTREACHED */
8645 argc -= optind;
8646 argv += optind;
8648 if (send_all_branches && !TAILQ_EMPTY(&branches))
8649 option_conflict('a', 'b');
8650 if (send_all_tags && !TAILQ_EMPTY(&tags))
8651 option_conflict('T', 't');
8654 if (argc == 0)
8655 remote_name = GOT_SEND_DEFAULT_REMOTE_NAME;
8656 else if (argc == 1)
8657 remote_name = argv[0];
8658 else
8659 usage_send();
8661 cwd = getcwd(NULL, 0);
8662 if (cwd == NULL) {
8663 error = got_error_from_errno("getcwd");
8664 goto done;
8667 error = got_repo_pack_fds_open(&pack_fds);
8668 if (error != NULL)
8669 goto done;
8671 if (repo_path == NULL) {
8672 error = got_worktree_open(&worktree, cwd);
8673 if (error && error->code != GOT_ERR_NOT_WORKTREE)
8674 goto done;
8675 else
8676 error = NULL;
8677 if (worktree) {
8678 repo_path =
8679 strdup(got_worktree_get_repo_path(worktree));
8680 if (repo_path == NULL)
8681 error = got_error_from_errno("strdup");
8682 if (error)
8683 goto done;
8684 } else {
8685 repo_path = strdup(cwd);
8686 if (repo_path == NULL) {
8687 error = got_error_from_errno("strdup");
8688 goto done;
8693 error = got_repo_open(&repo, repo_path, NULL, pack_fds);
8694 if (error)
8695 goto done;
8697 if (worktree) {
8698 worktree_conf = got_worktree_get_gotconfig(worktree);
8699 if (worktree_conf) {
8700 got_gotconfig_get_remotes(&nremotes, &remotes,
8701 worktree_conf);
8702 for (i = 0; i < nremotes; i++) {
8703 if (strcmp(remotes[i].name, remote_name) == 0) {
8704 remote = &remotes[i];
8705 break;
8710 if (remote == NULL) {
8711 repo_conf = got_repo_get_gotconfig(repo);
8712 if (repo_conf) {
8713 got_gotconfig_get_remotes(&nremotes, &remotes,
8714 repo_conf);
8715 for (i = 0; i < nremotes; i++) {
8716 if (strcmp(remotes[i].name, remote_name) == 0) {
8717 remote = &remotes[i];
8718 break;
8723 if (remote == NULL) {
8724 got_repo_get_gitconfig_remotes(&nremotes, &remotes, repo);
8725 for (i = 0; i < nremotes; i++) {
8726 if (strcmp(remotes[i].name, remote_name) == 0) {
8727 remote = &remotes[i];
8728 break;
8732 if (remote == NULL) {
8733 error = got_error_path(remote_name, GOT_ERR_NO_REMOTE);
8734 goto done;
8737 error = got_dial_parse_uri(&proto, &host, &port, &server_path,
8738 &repo_name, remote->send_url);
8739 if (error)
8740 goto done;
8742 if (strcmp(proto, "git") == 0) {
8743 #ifndef PROFILE
8744 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
8745 "sendfd dns inet unveil", NULL) == -1)
8746 err(1, "pledge");
8747 #endif
8748 } else if (strcmp(proto, "git+ssh") == 0 ||
8749 strcmp(proto, "ssh") == 0) {
8750 #ifndef PROFILE
8751 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
8752 "sendfd unveil", NULL) == -1)
8753 err(1, "pledge");
8754 #endif
8755 } else if (strcmp(proto, "http") == 0 ||
8756 strcmp(proto, "git+http") == 0) {
8757 error = got_error_path(proto, GOT_ERR_NOT_IMPL);
8758 goto done;
8759 } else {
8760 error = got_error_path(proto, GOT_ERR_BAD_PROTO);
8761 goto done;
8764 error = got_dial_apply_unveil(proto);
8765 if (error)
8766 goto done;
8768 error = apply_unveil(got_repo_get_path(repo), 0, NULL);
8769 if (error)
8770 goto done;
8772 if (send_all_branches) {
8773 error = got_ref_list(&all_branches, repo, "refs/heads",
8774 got_ref_cmp_by_name, NULL);
8775 if (error)
8776 goto done;
8777 TAILQ_FOREACH(re, &all_branches, entry) {
8778 const char *branchname = got_ref_get_name(re->ref);
8779 error = got_pathlist_append(&branches,
8780 branchname, NULL);
8781 if (error)
8782 goto done;
8783 nbranches++;
8785 } else if (nbranches == 0) {
8786 for (i = 0; i < remote->nsend_branches; i++) {
8787 got_pathlist_append(&branches,
8788 remote->send_branches[i], NULL);
8792 if (send_all_tags) {
8793 error = got_ref_list(&all_tags, repo, "refs/tags",
8794 got_ref_cmp_by_name, NULL);
8795 if (error)
8796 goto done;
8797 TAILQ_FOREACH(re, &all_tags, entry) {
8798 const char *tagname = got_ref_get_name(re->ref);
8799 error = got_pathlist_append(&tags,
8800 tagname, NULL);
8801 if (error)
8802 goto done;
8803 ntags++;
8808 * To prevent accidents only branches in refs/heads/ can be deleted
8809 * with 'got send -d'.
8810 * Deleting anything else requires local repository access or Git.
8812 TAILQ_FOREACH(pe, &delete_args, entry) {
8813 const char *branchname = pe->path;
8814 char *s;
8815 struct got_pathlist_entry *new;
8816 if (strncmp(branchname, "refs/heads/", 11) == 0) {
8817 s = strdup(branchname);
8818 if (s == NULL) {
8819 error = got_error_from_errno("strdup");
8820 goto done;
8822 } else {
8823 if (asprintf(&s, "refs/heads/%s", branchname) == -1) {
8824 error = got_error_from_errno("asprintf");
8825 goto done;
8828 error = got_pathlist_insert(&new, &delete_branches, s, NULL);
8829 if (error || new == NULL /* duplicate */)
8830 free(s);
8831 if (error)
8832 goto done;
8833 ndelete_branches++;
8836 if (nbranches == 0 && ndelete_branches == 0) {
8837 struct got_reference *head_ref;
8838 if (worktree)
8839 error = got_ref_open(&head_ref, repo,
8840 got_worktree_get_head_ref_name(worktree), 0);
8841 else
8842 error = got_ref_open(&head_ref, repo, GOT_REF_HEAD, 0);
8843 if (error)
8844 goto done;
8845 if (got_ref_is_symbolic(head_ref)) {
8846 error = got_ref_resolve_symbolic(&ref, repo, head_ref);
8847 got_ref_close(head_ref);
8848 if (error)
8849 goto done;
8850 } else
8851 ref = head_ref;
8852 error = got_pathlist_append(&branches, got_ref_get_name(ref),
8853 NULL);
8854 if (error)
8855 goto done;
8856 nbranches++;
8859 if (verbosity >= 0)
8860 printf("Connecting to \"%s\" %s%s%s\n", remote->name, host,
8861 port ? ":" : "", port ? port : "");
8863 error = got_send_connect(&sendpid, &sendfd, proto, host, port,
8864 server_path, verbosity);
8865 if (error)
8866 goto done;
8868 memset(&spa, 0, sizeof(spa));
8869 spa.last_scaled_packsize[0] = '\0';
8870 spa.last_p_deltify = -1;
8871 spa.last_p_written = -1;
8872 spa.verbosity = verbosity;
8873 spa.delete_branches = &delete_branches;
8874 error = got_send_pack(remote_name, &branches, &tags, &delete_branches,
8875 verbosity, overwrite_refs, sendfd, repo, send_progress, &spa,
8876 check_cancelled, NULL);
8877 if (spa.printed_something)
8878 putchar('\n');
8879 if (error)
8880 goto done;
8881 if (!spa.sent_something && verbosity >= 0)
8882 printf("Already up-to-date\n");
8883 done:
8884 if (sendpid > 0) {
8885 if (kill(sendpid, SIGTERM) == -1)
8886 error = got_error_from_errno("kill");
8887 if (waitpid(sendpid, &sendstatus, 0) == -1 && error == NULL)
8888 error = got_error_from_errno("waitpid");
8890 if (sendfd != -1 && close(sendfd) == -1 && error == NULL)
8891 error = got_error_from_errno("close");
8892 if (repo) {
8893 const struct got_error *close_err = got_repo_close(repo);
8894 if (error == NULL)
8895 error = close_err;
8897 if (worktree)
8898 got_worktree_close(worktree);
8899 if (pack_fds) {
8900 const struct got_error *pack_err =
8901 got_repo_pack_fds_close(pack_fds);
8902 if (error == NULL)
8903 error = pack_err;
8905 if (ref)
8906 got_ref_close(ref);
8907 got_pathlist_free(&branches);
8908 got_pathlist_free(&tags);
8909 got_ref_list_free(&all_branches);
8910 got_ref_list_free(&all_tags);
8911 got_pathlist_free(&delete_args);
8912 TAILQ_FOREACH(pe, &delete_branches, entry)
8913 free((char *)pe->path);
8914 got_pathlist_free(&delete_branches);
8915 free(cwd);
8916 free(repo_path);
8917 free(proto);
8918 free(host);
8919 free(port);
8920 free(server_path);
8921 free(repo_name);
8922 return error;
8925 __dead static void
8926 usage_cherrypick(void)
8928 fprintf(stderr, "usage: %s cherrypick commit-id\n", getprogname());
8929 exit(1);
8932 static const struct got_error *
8933 cmd_cherrypick(int argc, char *argv[])
8935 const struct got_error *error = NULL;
8936 struct got_worktree *worktree = NULL;
8937 struct got_repository *repo = NULL;
8938 char *cwd = NULL, *commit_id_str = NULL;
8939 struct got_object_id *commit_id = NULL;
8940 struct got_commit_object *commit = NULL;
8941 struct got_object_qid *pid;
8942 int ch;
8943 struct got_update_progress_arg upa;
8944 int *pack_fds = NULL;
8946 while ((ch = getopt(argc, argv, "")) != -1) {
8947 switch (ch) {
8948 default:
8949 usage_cherrypick();
8950 /* NOTREACHED */
8954 argc -= optind;
8955 argv += optind;
8957 #ifndef PROFILE
8958 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
8959 "unveil", NULL) == -1)
8960 err(1, "pledge");
8961 #endif
8962 if (argc != 1)
8963 usage_cherrypick();
8965 cwd = getcwd(NULL, 0);
8966 if (cwd == NULL) {
8967 error = got_error_from_errno("getcwd");
8968 goto done;
8971 error = got_repo_pack_fds_open(&pack_fds);
8972 if (error != NULL)
8973 goto done;
8975 error = got_worktree_open(&worktree, cwd);
8976 if (error) {
8977 if (error->code == GOT_ERR_NOT_WORKTREE)
8978 error = wrap_not_worktree_error(error, "cherrypick",
8979 cwd);
8980 goto done;
8983 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
8984 NULL, pack_fds);
8985 if (error != NULL)
8986 goto done;
8988 error = apply_unveil(got_repo_get_path(repo), 0,
8989 got_worktree_get_root_path(worktree));
8990 if (error)
8991 goto done;
8993 error = got_repo_match_object_id(&commit_id, NULL, argv[0],
8994 GOT_OBJ_TYPE_COMMIT, NULL, repo);
8995 if (error)
8996 goto done;
8997 error = got_object_id_str(&commit_id_str, commit_id);
8998 if (error)
8999 goto done;
9001 error = got_object_open_as_commit(&commit, repo, commit_id);
9002 if (error)
9003 goto done;
9004 pid = STAILQ_FIRST(got_object_commit_get_parent_ids(commit));
9005 memset(&upa, 0, sizeof(upa));
9006 error = got_worktree_merge_files(worktree, pid ? &pid->id : NULL,
9007 commit_id, repo, update_progress, &upa, check_cancelled,
9008 NULL);
9009 if (error != NULL)
9010 goto done;
9012 if (upa.did_something)
9013 printf("Merged commit %s\n", commit_id_str);
9014 print_merge_progress_stats(&upa);
9015 done:
9016 if (commit)
9017 got_object_commit_close(commit);
9018 free(commit_id_str);
9019 if (worktree)
9020 got_worktree_close(worktree);
9021 if (repo) {
9022 const struct got_error *close_err = got_repo_close(repo);
9023 if (error == NULL)
9024 error = close_err;
9026 if (pack_fds) {
9027 const struct got_error *pack_err =
9028 got_repo_pack_fds_close(pack_fds);
9029 if (error == NULL)
9030 error = pack_err;
9033 return error;
9036 __dead static void
9037 usage_backout(void)
9039 fprintf(stderr, "usage: %s backout commit-id\n", getprogname());
9040 exit(1);
9043 static const struct got_error *
9044 cmd_backout(int argc, char *argv[])
9046 const struct got_error *error = NULL;
9047 struct got_worktree *worktree = NULL;
9048 struct got_repository *repo = NULL;
9049 char *cwd = NULL, *commit_id_str = NULL;
9050 struct got_object_id *commit_id = NULL;
9051 struct got_commit_object *commit = NULL;
9052 struct got_object_qid *pid;
9053 int ch;
9054 struct got_update_progress_arg upa;
9055 int *pack_fds = NULL;
9057 while ((ch = getopt(argc, argv, "")) != -1) {
9058 switch (ch) {
9059 default:
9060 usage_backout();
9061 /* NOTREACHED */
9065 argc -= optind;
9066 argv += optind;
9068 #ifndef PROFILE
9069 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
9070 "unveil", NULL) == -1)
9071 err(1, "pledge");
9072 #endif
9073 if (argc != 1)
9074 usage_backout();
9076 cwd = getcwd(NULL, 0);
9077 if (cwd == NULL) {
9078 error = got_error_from_errno("getcwd");
9079 goto done;
9082 error = got_repo_pack_fds_open(&pack_fds);
9083 if (error != NULL)
9084 goto done;
9086 error = got_worktree_open(&worktree, cwd);
9087 if (error) {
9088 if (error->code == GOT_ERR_NOT_WORKTREE)
9089 error = wrap_not_worktree_error(error, "backout", cwd);
9090 goto done;
9093 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
9094 NULL, pack_fds);
9095 if (error != NULL)
9096 goto done;
9098 error = apply_unveil(got_repo_get_path(repo), 0,
9099 got_worktree_get_root_path(worktree));
9100 if (error)
9101 goto done;
9103 error = got_repo_match_object_id(&commit_id, NULL, argv[0],
9104 GOT_OBJ_TYPE_COMMIT, NULL, repo);
9105 if (error)
9106 goto done;
9107 error = got_object_id_str(&commit_id_str, commit_id);
9108 if (error)
9109 goto done;
9111 error = got_object_open_as_commit(&commit, repo, commit_id);
9112 if (error)
9113 goto done;
9114 pid = STAILQ_FIRST(got_object_commit_get_parent_ids(commit));
9115 if (pid == NULL) {
9116 error = got_error(GOT_ERR_ROOT_COMMIT);
9117 goto done;
9120 memset(&upa, 0, sizeof(upa));
9121 error = got_worktree_merge_files(worktree, commit_id, &pid->id,
9122 repo, update_progress, &upa, check_cancelled, NULL);
9123 if (error != NULL)
9124 goto done;
9126 if (upa.did_something)
9127 printf("Backed out commit %s\n", commit_id_str);
9128 print_merge_progress_stats(&upa);
9129 done:
9130 if (commit)
9131 got_object_commit_close(commit);
9132 free(commit_id_str);
9133 if (worktree)
9134 got_worktree_close(worktree);
9135 if (repo) {
9136 const struct got_error *close_err = got_repo_close(repo);
9137 if (error == NULL)
9138 error = close_err;
9140 if (pack_fds) {
9141 const struct got_error *pack_err =
9142 got_repo_pack_fds_close(pack_fds);
9143 if (error == NULL)
9144 error = pack_err;
9146 return error;
9149 __dead static void
9150 usage_rebase(void)
9152 fprintf(stderr, "usage: %s rebase [-a] [-c] [-l] [-X] [branch]\n",
9153 getprogname());
9154 exit(1);
9157 void
9158 trim_logmsg(char *logmsg, int limit)
9160 char *nl;
9161 size_t len;
9163 len = strlen(logmsg);
9164 if (len > limit)
9165 len = limit;
9166 logmsg[len] = '\0';
9167 nl = strchr(logmsg, '\n');
9168 if (nl)
9169 *nl = '\0';
9172 static const struct got_error *
9173 get_short_logmsg(char **logmsg, int limit, struct got_commit_object *commit)
9175 const struct got_error *err;
9176 char *logmsg0 = NULL;
9177 const char *s;
9179 err = got_object_commit_get_logmsg(&logmsg0, commit);
9180 if (err)
9181 return err;
9183 s = logmsg0;
9184 while (isspace((unsigned char)s[0]))
9185 s++;
9187 *logmsg = strdup(s);
9188 if (*logmsg == NULL) {
9189 err = got_error_from_errno("strdup");
9190 goto done;
9193 trim_logmsg(*logmsg, limit);
9194 done:
9195 free(logmsg0);
9196 return err;
9199 static const struct got_error *
9200 show_rebase_merge_conflict(struct got_object_id *id,
9201 struct got_repository *repo)
9203 const struct got_error *err;
9204 struct got_commit_object *commit = NULL;
9205 char *id_str = NULL, *logmsg = NULL;
9207 err = got_object_open_as_commit(&commit, repo, id);
9208 if (err)
9209 return err;
9211 err = got_object_id_str(&id_str, id);
9212 if (err)
9213 goto done;
9215 id_str[12] = '\0';
9217 err = get_short_logmsg(&logmsg, 42, commit);
9218 if (err)
9219 goto done;
9221 printf("%s -> merge conflict: %s\n", id_str, logmsg);
9222 done:
9223 free(id_str);
9224 got_object_commit_close(commit);
9225 free(logmsg);
9226 return err;
9229 static const struct got_error *
9230 show_rebase_progress(struct got_commit_object *commit,
9231 struct got_object_id *old_id, struct got_object_id *new_id)
9233 const struct got_error *err;
9234 char *old_id_str = NULL, *new_id_str = NULL, *logmsg = NULL;
9236 err = got_object_id_str(&old_id_str, old_id);
9237 if (err)
9238 goto done;
9240 if (new_id) {
9241 err = got_object_id_str(&new_id_str, new_id);
9242 if (err)
9243 goto done;
9246 old_id_str[12] = '\0';
9247 if (new_id_str)
9248 new_id_str[12] = '\0';
9250 err = get_short_logmsg(&logmsg, 42, commit);
9251 if (err)
9252 goto done;
9254 printf("%s -> %s: %s\n", old_id_str,
9255 new_id_str ? new_id_str : "no-op change", logmsg);
9256 done:
9257 free(old_id_str);
9258 free(new_id_str);
9259 free(logmsg);
9260 return err;
9263 static const struct got_error *
9264 rebase_complete(struct got_worktree *worktree, struct got_fileindex *fileindex,
9265 struct got_reference *branch, struct got_reference *new_base_branch,
9266 struct got_reference *tmp_branch, struct got_repository *repo,
9267 int create_backup)
9269 printf("Switching work tree to %s\n", got_ref_get_name(branch));
9270 return got_worktree_rebase_complete(worktree, fileindex,
9271 new_base_branch, tmp_branch, branch, repo, create_backup);
9274 static const struct got_error *
9275 rebase_commit(struct got_pathlist_head *merged_paths,
9276 struct got_worktree *worktree, struct got_fileindex *fileindex,
9277 struct got_reference *tmp_branch,
9278 struct got_object_id *commit_id, struct got_repository *repo)
9280 const struct got_error *error;
9281 struct got_commit_object *commit;
9282 struct got_object_id *new_commit_id;
9284 error = got_object_open_as_commit(&commit, repo, commit_id);
9285 if (error)
9286 return error;
9288 error = got_worktree_rebase_commit(&new_commit_id, merged_paths,
9289 worktree, fileindex, tmp_branch, commit, commit_id, repo);
9290 if (error) {
9291 if (error->code != GOT_ERR_COMMIT_NO_CHANGES)
9292 goto done;
9293 error = show_rebase_progress(commit, commit_id, NULL);
9294 } else {
9295 error = show_rebase_progress(commit, commit_id, new_commit_id);
9296 free(new_commit_id);
9298 done:
9299 got_object_commit_close(commit);
9300 return error;
9303 struct check_path_prefix_arg {
9304 const char *path_prefix;
9305 size_t len;
9306 int errcode;
9309 static const struct got_error *
9310 check_path_prefix_in_diff(void *arg, struct got_blob_object *blob1,
9311 struct got_blob_object *blob2, FILE *f1, FILE *f2,
9312 struct got_object_id *id1, struct got_object_id *id2,
9313 const char *path1, const char *path2,
9314 mode_t mode1, mode_t mode2, struct got_repository *repo)
9316 struct check_path_prefix_arg *a = arg;
9318 if ((path1 && !got_path_is_child(path1, a->path_prefix, a->len)) ||
9319 (path2 && !got_path_is_child(path2, a->path_prefix, a->len)))
9320 return got_error(a->errcode);
9322 return NULL;
9325 static const struct got_error *
9326 check_path_prefix(struct got_object_id *parent_id,
9327 struct got_object_id *commit_id, const char *path_prefix,
9328 int errcode, struct got_repository *repo)
9330 const struct got_error *err;
9331 struct got_tree_object *tree1 = NULL, *tree2 = NULL;
9332 struct got_commit_object *commit = NULL, *parent_commit = NULL;
9333 struct check_path_prefix_arg cpp_arg;
9335 if (got_path_is_root_dir(path_prefix))
9336 return NULL;
9338 err = got_object_open_as_commit(&commit, repo, commit_id);
9339 if (err)
9340 goto done;
9342 err = got_object_open_as_commit(&parent_commit, repo, parent_id);
9343 if (err)
9344 goto done;
9346 err = got_object_open_as_tree(&tree1, repo,
9347 got_object_commit_get_tree_id(parent_commit));
9348 if (err)
9349 goto done;
9351 err = got_object_open_as_tree(&tree2, repo,
9352 got_object_commit_get_tree_id(commit));
9353 if (err)
9354 goto done;
9356 cpp_arg.path_prefix = path_prefix;
9357 while (cpp_arg.path_prefix[0] == '/')
9358 cpp_arg.path_prefix++;
9359 cpp_arg.len = strlen(cpp_arg.path_prefix);
9360 cpp_arg.errcode = errcode;
9361 err = got_diff_tree(tree1, tree2, NULL, NULL, "", "", repo,
9362 check_path_prefix_in_diff, &cpp_arg, 0);
9363 done:
9364 if (tree1)
9365 got_object_tree_close(tree1);
9366 if (tree2)
9367 got_object_tree_close(tree2);
9368 if (commit)
9369 got_object_commit_close(commit);
9370 if (parent_commit)
9371 got_object_commit_close(parent_commit);
9372 return err;
9375 static const struct got_error *
9376 collect_commits(struct got_object_id_queue *commits,
9377 struct got_object_id *initial_commit_id,
9378 struct got_object_id *iter_start_id, struct got_object_id *iter_stop_id,
9379 const char *path_prefix, int path_prefix_errcode,
9380 struct got_repository *repo)
9382 const struct got_error *err = NULL;
9383 struct got_commit_graph *graph = NULL;
9384 struct got_object_id *parent_id = NULL;
9385 struct got_object_qid *qid;
9386 struct got_object_id *commit_id = initial_commit_id;
9388 err = got_commit_graph_open(&graph, "/", 1);
9389 if (err)
9390 return err;
9392 err = got_commit_graph_iter_start(graph, iter_start_id, repo,
9393 check_cancelled, NULL);
9394 if (err)
9395 goto done;
9396 while (got_object_id_cmp(commit_id, iter_stop_id) != 0) {
9397 err = got_commit_graph_iter_next(&parent_id, graph, repo,
9398 check_cancelled, NULL);
9399 if (err) {
9400 if (err->code == GOT_ERR_ITER_COMPLETED) {
9401 err = got_error_msg(GOT_ERR_ANCESTRY,
9402 "ran out of commits to rebase before "
9403 "youngest common ancestor commit has "
9404 "been reached?!?");
9406 goto done;
9407 } else {
9408 err = check_path_prefix(parent_id, commit_id,
9409 path_prefix, path_prefix_errcode, repo);
9410 if (err)
9411 goto done;
9413 err = got_object_qid_alloc(&qid, commit_id);
9414 if (err)
9415 goto done;
9416 STAILQ_INSERT_HEAD(commits, qid, entry);
9417 commit_id = parent_id;
9420 done:
9421 got_commit_graph_close(graph);
9422 return err;
9425 static const struct got_error *
9426 get_commit_brief_str(char **brief_str, struct got_commit_object *commit)
9428 const struct got_error *err = NULL;
9429 time_t committer_time;
9430 struct tm tm;
9431 char datebuf[11]; /* YYYY-MM-DD + NUL */
9432 char *author0 = NULL, *author, *smallerthan;
9433 char *logmsg0 = NULL, *logmsg, *newline;
9435 committer_time = got_object_commit_get_committer_time(commit);
9436 if (gmtime_r(&committer_time, &tm) == NULL)
9437 return got_error_from_errno("gmtime_r");
9438 if (strftime(datebuf, sizeof(datebuf), "%G-%m-%d", &tm) == 0)
9439 return got_error(GOT_ERR_NO_SPACE);
9441 author0 = strdup(got_object_commit_get_author(commit));
9442 if (author0 == NULL)
9443 return got_error_from_errno("strdup");
9444 author = author0;
9445 smallerthan = strchr(author, '<');
9446 if (smallerthan && smallerthan[1] != '\0')
9447 author = smallerthan + 1;
9448 author[strcspn(author, "@>")] = '\0';
9450 err = got_object_commit_get_logmsg(&logmsg0, commit);
9451 if (err)
9452 goto done;
9453 logmsg = logmsg0;
9454 while (*logmsg == '\n')
9455 logmsg++;
9456 newline = strchr(logmsg, '\n');
9457 if (newline)
9458 *newline = '\0';
9460 if (asprintf(brief_str, "%s %s %s",
9461 datebuf, author, logmsg) == -1)
9462 err = got_error_from_errno("asprintf");
9463 done:
9464 free(author0);
9465 free(logmsg0);
9466 return err;
9469 static const struct got_error *
9470 delete_backup_ref(struct got_reference *ref, struct got_object_id *id,
9471 struct got_repository *repo)
9473 const struct got_error *err;
9474 char *id_str;
9476 err = got_object_id_str(&id_str, id);
9477 if (err)
9478 return err;
9480 err = got_ref_delete(ref, repo);
9481 if (err)
9482 goto done;
9484 printf("Deleted %s: %s\n", got_ref_get_name(ref), id_str);
9485 done:
9486 free(id_str);
9487 return err;
9490 static const struct got_error *
9491 print_backup_ref(const char *branch_name, const char *new_id_str,
9492 struct got_object_id *old_commit_id, struct got_commit_object *old_commit,
9493 struct got_reflist_object_id_map *refs_idmap,
9494 struct got_repository *repo)
9496 const struct got_error *err = NULL;
9497 struct got_reflist_head *refs;
9498 char *refs_str = NULL;
9499 struct got_object_id *new_commit_id = NULL;
9500 struct got_commit_object *new_commit = NULL;
9501 char *new_commit_brief_str = NULL;
9502 struct got_object_id *yca_id = NULL;
9503 struct got_commit_object *yca_commit = NULL;
9504 char *yca_id_str = NULL, *yca_brief_str = NULL;
9505 char *custom_refs_str;
9507 if (asprintf(&custom_refs_str, "formerly %s", branch_name) == -1)
9508 return got_error_from_errno("asprintf");
9510 err = print_commit(old_commit, old_commit_id, repo, NULL, NULL,
9511 0, 0, refs_idmap, custom_refs_str);
9512 if (err)
9513 goto done;
9515 err = got_object_resolve_id_str(&new_commit_id, repo, new_id_str);
9516 if (err)
9517 goto done;
9519 refs = got_reflist_object_id_map_lookup(refs_idmap, new_commit_id);
9520 if (refs) {
9521 err = build_refs_str(&refs_str, refs, new_commit_id, repo, 0);
9522 if (err)
9523 goto done;
9526 err = got_object_open_as_commit(&new_commit, repo, new_commit_id);
9527 if (err)
9528 goto done;
9530 err = get_commit_brief_str(&new_commit_brief_str, new_commit);
9531 if (err)
9532 goto done;
9534 err = got_commit_graph_find_youngest_common_ancestor(&yca_id,
9535 old_commit_id, new_commit_id, 1, repo, check_cancelled, NULL);
9536 if (err)
9537 goto done;
9539 printf("has become commit %s%s%s%s\n %s\n", new_id_str,
9540 refs_str ? " (" : "", refs_str ? refs_str : "",
9541 refs_str ? ")" : "", new_commit_brief_str);
9542 if (yca_id && got_object_id_cmp(yca_id, new_commit_id) != 0 &&
9543 got_object_id_cmp(yca_id, old_commit_id) != 0) {
9544 free(refs_str);
9545 refs_str = NULL;
9547 err = got_object_open_as_commit(&yca_commit, repo, yca_id);
9548 if (err)
9549 goto done;
9551 err = get_commit_brief_str(&yca_brief_str, yca_commit);
9552 if (err)
9553 goto done;
9555 err = got_object_id_str(&yca_id_str, yca_id);
9556 if (err)
9557 goto done;
9559 refs = got_reflist_object_id_map_lookup(refs_idmap, yca_id);
9560 if (refs) {
9561 err = build_refs_str(&refs_str, refs, yca_id, repo, 0);
9562 if (err)
9563 goto done;
9565 printf("history forked at %s%s%s%s\n %s\n",
9566 yca_id_str,
9567 refs_str ? " (" : "", refs_str ? refs_str : "",
9568 refs_str ? ")" : "", yca_brief_str);
9570 done:
9571 free(custom_refs_str);
9572 free(new_commit_id);
9573 free(refs_str);
9574 free(yca_id);
9575 free(yca_id_str);
9576 free(yca_brief_str);
9577 if (new_commit)
9578 got_object_commit_close(new_commit);
9579 if (yca_commit)
9580 got_object_commit_close(yca_commit);
9582 return NULL;
9585 static const struct got_error *
9586 process_backup_refs(const char *backup_ref_prefix,
9587 const char *wanted_branch_name,
9588 int delete, struct got_repository *repo)
9590 const struct got_error *err;
9591 struct got_reflist_head refs, backup_refs;
9592 struct got_reflist_entry *re;
9593 const size_t backup_ref_prefix_len = strlen(backup_ref_prefix);
9594 struct got_object_id *old_commit_id = NULL;
9595 char *branch_name = NULL;
9596 struct got_commit_object *old_commit = NULL;
9597 struct got_reflist_object_id_map *refs_idmap = NULL;
9598 int wanted_branch_found = 0;
9600 TAILQ_INIT(&refs);
9601 TAILQ_INIT(&backup_refs);
9603 err = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name, NULL);
9604 if (err)
9605 return err;
9607 err = got_reflist_object_id_map_create(&refs_idmap, &refs, repo);
9608 if (err)
9609 goto done;
9611 if (wanted_branch_name) {
9612 if (strncmp(wanted_branch_name, "refs/heads/", 11) == 0)
9613 wanted_branch_name += 11;
9616 err = got_ref_list(&backup_refs, repo, backup_ref_prefix,
9617 got_ref_cmp_by_commit_timestamp_descending, repo);
9618 if (err)
9619 goto done;
9621 TAILQ_FOREACH(re, &backup_refs, entry) {
9622 const char *refname = got_ref_get_name(re->ref);
9623 char *slash;
9625 err = check_cancelled(NULL);
9626 if (err)
9627 break;
9629 err = got_ref_resolve(&old_commit_id, repo, re->ref);
9630 if (err)
9631 break;
9633 err = got_object_open_as_commit(&old_commit, repo,
9634 old_commit_id);
9635 if (err)
9636 break;
9638 if (strncmp(backup_ref_prefix, refname,
9639 backup_ref_prefix_len) == 0)
9640 refname += backup_ref_prefix_len;
9642 while (refname[0] == '/')
9643 refname++;
9645 branch_name = strdup(refname);
9646 if (branch_name == NULL) {
9647 err = got_error_from_errno("strdup");
9648 break;
9650 slash = strrchr(branch_name, '/');
9651 if (slash) {
9652 *slash = '\0';
9653 refname += strlen(branch_name) + 1;
9656 if (wanted_branch_name == NULL ||
9657 strcmp(wanted_branch_name, branch_name) == 0) {
9658 wanted_branch_found = 1;
9659 if (delete) {
9660 err = delete_backup_ref(re->ref,
9661 old_commit_id, repo);
9662 } else {
9663 err = print_backup_ref(branch_name, refname,
9664 old_commit_id, old_commit, refs_idmap,
9665 repo);
9667 if (err)
9668 break;
9671 free(old_commit_id);
9672 old_commit_id = NULL;
9673 free(branch_name);
9674 branch_name = NULL;
9675 got_object_commit_close(old_commit);
9676 old_commit = NULL;
9679 if (wanted_branch_name && !wanted_branch_found) {
9680 err = got_error_fmt(GOT_ERR_NOT_REF,
9681 "%s/%s/", backup_ref_prefix, wanted_branch_name);
9683 done:
9684 if (refs_idmap)
9685 got_reflist_object_id_map_free(refs_idmap);
9686 got_ref_list_free(&refs);
9687 got_ref_list_free(&backup_refs);
9688 free(old_commit_id);
9689 free(branch_name);
9690 if (old_commit)
9691 got_object_commit_close(old_commit);
9692 return err;
9695 static const struct got_error *
9696 abort_progress(void *arg, unsigned char status, const char *path)
9699 * Unversioned files should not clutter progress output when
9700 * an operation is aborted.
9702 if (status == GOT_STATUS_UNVERSIONED)
9703 return NULL;
9705 return update_progress(arg, status, path);
9708 static const struct got_error *
9709 cmd_rebase(int argc, char *argv[])
9711 const struct got_error *error = NULL;
9712 struct got_worktree *worktree = NULL;
9713 struct got_repository *repo = NULL;
9714 struct got_fileindex *fileindex = NULL;
9715 char *cwd = NULL;
9716 struct got_reference *branch = NULL;
9717 struct got_reference *new_base_branch = NULL, *tmp_branch = NULL;
9718 struct got_object_id *commit_id = NULL, *parent_id = NULL;
9719 struct got_object_id *resume_commit_id = NULL;
9720 struct got_object_id *branch_head_commit_id = NULL, *yca_id = NULL;
9721 struct got_commit_object *commit = NULL;
9722 int ch, rebase_in_progress = 0, abort_rebase = 0, continue_rebase = 0;
9723 int histedit_in_progress = 0, merge_in_progress = 0;
9724 int create_backup = 1, list_backups = 0, delete_backups = 0;
9725 struct got_object_id_queue commits;
9726 struct got_pathlist_head merged_paths;
9727 const struct got_object_id_queue *parent_ids;
9728 struct got_object_qid *qid, *pid;
9729 struct got_update_progress_arg upa;
9730 int *pack_fds = NULL;
9732 STAILQ_INIT(&commits);
9733 TAILQ_INIT(&merged_paths);
9734 memset(&upa, 0, sizeof(upa));
9736 while ((ch = getopt(argc, argv, "aclX")) != -1) {
9737 switch (ch) {
9738 case 'a':
9739 abort_rebase = 1;
9740 break;
9741 case 'c':
9742 continue_rebase = 1;
9743 break;
9744 case 'l':
9745 list_backups = 1;
9746 break;
9747 case 'X':
9748 delete_backups = 1;
9749 break;
9750 default:
9751 usage_rebase();
9752 /* NOTREACHED */
9756 argc -= optind;
9757 argv += optind;
9759 #ifndef PROFILE
9760 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
9761 "unveil", NULL) == -1)
9762 err(1, "pledge");
9763 #endif
9764 if (list_backups) {
9765 if (abort_rebase)
9766 option_conflict('l', 'a');
9767 if (continue_rebase)
9768 option_conflict('l', 'c');
9769 if (delete_backups)
9770 option_conflict('l', 'X');
9771 if (argc != 0 && argc != 1)
9772 usage_rebase();
9773 } else if (delete_backups) {
9774 if (abort_rebase)
9775 option_conflict('X', 'a');
9776 if (continue_rebase)
9777 option_conflict('X', 'c');
9778 if (list_backups)
9779 option_conflict('l', 'X');
9780 if (argc != 0 && argc != 1)
9781 usage_rebase();
9782 } else {
9783 if (abort_rebase && continue_rebase)
9784 usage_rebase();
9785 else if (abort_rebase || continue_rebase) {
9786 if (argc != 0)
9787 usage_rebase();
9788 } else if (argc != 1)
9789 usage_rebase();
9792 cwd = getcwd(NULL, 0);
9793 if (cwd == NULL) {
9794 error = got_error_from_errno("getcwd");
9795 goto done;
9798 error = got_repo_pack_fds_open(&pack_fds);
9799 if (error != NULL)
9800 goto done;
9802 error = got_worktree_open(&worktree, cwd);
9803 if (error) {
9804 if (list_backups || delete_backups) {
9805 if (error->code != GOT_ERR_NOT_WORKTREE)
9806 goto done;
9807 } else {
9808 if (error->code == GOT_ERR_NOT_WORKTREE)
9809 error = wrap_not_worktree_error(error,
9810 "rebase", cwd);
9811 goto done;
9815 error = got_repo_open(&repo,
9816 worktree ? got_worktree_get_repo_path(worktree) : cwd, NULL,
9817 pack_fds);
9818 if (error != NULL)
9819 goto done;
9821 error = apply_unveil(got_repo_get_path(repo), 0,
9822 worktree ? got_worktree_get_root_path(worktree) : NULL);
9823 if (error)
9824 goto done;
9826 if (list_backups || delete_backups) {
9827 error = process_backup_refs(
9828 GOT_WORKTREE_REBASE_BACKUP_REF_PREFIX,
9829 argc == 1 ? argv[0] : NULL, delete_backups, repo);
9830 goto done; /* nothing else to do */
9833 error = got_worktree_histedit_in_progress(&histedit_in_progress,
9834 worktree);
9835 if (error)
9836 goto done;
9837 if (histedit_in_progress) {
9838 error = got_error(GOT_ERR_HISTEDIT_BUSY);
9839 goto done;
9842 error = got_worktree_merge_in_progress(&merge_in_progress,
9843 worktree, repo);
9844 if (error)
9845 goto done;
9846 if (merge_in_progress) {
9847 error = got_error(GOT_ERR_MERGE_BUSY);
9848 goto done;
9851 error = got_worktree_rebase_in_progress(&rebase_in_progress, worktree);
9852 if (error)
9853 goto done;
9855 if (abort_rebase) {
9856 if (!rebase_in_progress) {
9857 error = got_error(GOT_ERR_NOT_REBASING);
9858 goto done;
9860 error = got_worktree_rebase_continue(&resume_commit_id,
9861 &new_base_branch, &tmp_branch, &branch, &fileindex,
9862 worktree, repo);
9863 if (error)
9864 goto done;
9865 printf("Switching work tree to %s\n",
9866 got_ref_get_symref_target(new_base_branch));
9867 error = got_worktree_rebase_abort(worktree, fileindex, repo,
9868 new_base_branch, abort_progress, &upa);
9869 if (error)
9870 goto done;
9871 printf("Rebase of %s aborted\n", got_ref_get_name(branch));
9872 print_merge_progress_stats(&upa);
9873 goto done; /* nothing else to do */
9876 if (continue_rebase) {
9877 if (!rebase_in_progress) {
9878 error = got_error(GOT_ERR_NOT_REBASING);
9879 goto done;
9881 error = got_worktree_rebase_continue(&resume_commit_id,
9882 &new_base_branch, &tmp_branch, &branch, &fileindex,
9883 worktree, repo);
9884 if (error)
9885 goto done;
9887 error = rebase_commit(NULL, worktree, fileindex, tmp_branch,
9888 resume_commit_id, repo);
9889 if (error)
9890 goto done;
9892 yca_id = got_object_id_dup(resume_commit_id);
9893 if (yca_id == NULL) {
9894 error = got_error_from_errno("got_object_id_dup");
9895 goto done;
9897 } else {
9898 error = got_ref_open(&branch, repo, argv[0], 0);
9899 if (error != NULL)
9900 goto done;
9903 error = got_ref_resolve(&branch_head_commit_id, repo, branch);
9904 if (error)
9905 goto done;
9907 if (!continue_rebase) {
9908 struct got_object_id *base_commit_id;
9910 base_commit_id = got_worktree_get_base_commit_id(worktree);
9911 error = got_commit_graph_find_youngest_common_ancestor(&yca_id,
9912 base_commit_id, branch_head_commit_id, 1, repo,
9913 check_cancelled, NULL);
9914 if (error)
9915 goto done;
9916 if (yca_id == NULL) {
9917 error = got_error_msg(GOT_ERR_ANCESTRY,
9918 "specified branch shares no common ancestry "
9919 "with work tree's branch");
9920 goto done;
9923 error = check_same_branch(base_commit_id, branch, yca_id, repo);
9924 if (error) {
9925 if (error->code != GOT_ERR_ANCESTRY)
9926 goto done;
9927 error = NULL;
9928 } else {
9929 struct got_pathlist_head paths;
9930 printf("%s is already based on %s\n",
9931 got_ref_get_name(branch),
9932 got_worktree_get_head_ref_name(worktree));
9933 error = switch_head_ref(branch, branch_head_commit_id,
9934 worktree, repo);
9935 if (error)
9936 goto done;
9937 error = got_worktree_set_base_commit_id(worktree, repo,
9938 branch_head_commit_id);
9939 if (error)
9940 goto done;
9941 TAILQ_INIT(&paths);
9942 error = got_pathlist_append(&paths, "", NULL);
9943 if (error)
9944 goto done;
9945 error = got_worktree_checkout_files(worktree,
9946 &paths, repo, update_progress, &upa,
9947 check_cancelled, NULL);
9948 got_pathlist_free(&paths);
9949 if (error)
9950 goto done;
9951 if (upa.did_something) {
9952 char *id_str;
9953 error = got_object_id_str(&id_str,
9954 branch_head_commit_id);
9955 if (error)
9956 goto done;
9957 printf("Updated to %s: %s\n",
9958 got_worktree_get_head_ref_name(worktree),
9959 id_str);
9960 free(id_str);
9961 } else
9962 printf("Already up-to-date\n");
9963 print_update_progress_stats(&upa);
9964 goto done;
9968 commit_id = branch_head_commit_id;
9969 error = got_object_open_as_commit(&commit, repo, commit_id);
9970 if (error)
9971 goto done;
9973 parent_ids = got_object_commit_get_parent_ids(commit);
9974 pid = STAILQ_FIRST(parent_ids);
9975 if (pid == NULL) {
9976 error = got_error(GOT_ERR_EMPTY_REBASE);
9977 goto done;
9979 error = collect_commits(&commits, commit_id, &pid->id,
9980 yca_id, got_worktree_get_path_prefix(worktree),
9981 GOT_ERR_REBASE_PATH, repo);
9982 got_object_commit_close(commit);
9983 commit = NULL;
9984 if (error)
9985 goto done;
9987 if (!continue_rebase) {
9988 error = got_worktree_rebase_prepare(&new_base_branch,
9989 &tmp_branch, &fileindex, worktree, branch, repo);
9990 if (error)
9991 goto done;
9994 if (STAILQ_EMPTY(&commits)) {
9995 if (continue_rebase) {
9996 error = rebase_complete(worktree, fileindex,
9997 branch, new_base_branch, tmp_branch, repo,
9998 create_backup);
9999 goto done;
10000 } else {
10001 /* Fast-forward the reference of the branch. */
10002 struct got_object_id *new_head_commit_id;
10003 char *id_str;
10004 error = got_ref_resolve(&new_head_commit_id, repo,
10005 new_base_branch);
10006 if (error)
10007 goto done;
10008 error = got_object_id_str(&id_str, new_head_commit_id);
10009 printf("Forwarding %s to commit %s\n",
10010 got_ref_get_name(branch), id_str);
10011 free(id_str);
10012 error = got_ref_change_ref(branch,
10013 new_head_commit_id);
10014 if (error)
10015 goto done;
10016 /* No backup needed since objects did not change. */
10017 create_backup = 0;
10021 pid = NULL;
10022 STAILQ_FOREACH(qid, &commits, entry) {
10024 commit_id = &qid->id;
10025 parent_id = pid ? &pid->id : yca_id;
10026 pid = qid;
10028 memset(&upa, 0, sizeof(upa));
10029 error = got_worktree_rebase_merge_files(&merged_paths,
10030 worktree, fileindex, parent_id, commit_id, repo,
10031 update_progress, &upa, check_cancelled, NULL);
10032 if (error)
10033 goto done;
10035 print_merge_progress_stats(&upa);
10036 if (upa.conflicts > 0 || upa.missing > 0 ||
10037 upa.not_deleted > 0 || upa.unversioned > 0) {
10038 if (upa.conflicts > 0) {
10039 error = show_rebase_merge_conflict(&qid->id,
10040 repo);
10041 if (error)
10042 goto done;
10044 got_worktree_rebase_pathlist_free(&merged_paths);
10045 break;
10048 error = rebase_commit(&merged_paths, worktree, fileindex,
10049 tmp_branch, commit_id, repo);
10050 got_worktree_rebase_pathlist_free(&merged_paths);
10051 if (error)
10052 goto done;
10055 if (upa.conflicts > 0 || upa.missing > 0 ||
10056 upa.not_deleted > 0 || upa.unversioned > 0) {
10057 error = got_worktree_rebase_postpone(worktree, fileindex);
10058 if (error)
10059 goto done;
10060 if (upa.conflicts > 0 && upa.missing == 0 &&
10061 upa.not_deleted == 0 && upa.unversioned == 0) {
10062 error = got_error_msg(GOT_ERR_CONFLICTS,
10063 "conflicts must be resolved before rebasing "
10064 "can continue");
10065 } else if (upa.conflicts > 0) {
10066 error = got_error_msg(GOT_ERR_CONFLICTS,
10067 "conflicts must be resolved before rebasing "
10068 "can continue; changes destined for some "
10069 "files were not yet merged and should be "
10070 "merged manually if required before the "
10071 "rebase operation is continued");
10072 } else {
10073 error = got_error_msg(GOT_ERR_CONFLICTS,
10074 "changes destined for some files were not "
10075 "yet merged and should be merged manually "
10076 "if required before the rebase operation "
10077 "is continued");
10079 } else
10080 error = rebase_complete(worktree, fileindex, branch,
10081 new_base_branch, tmp_branch, repo, create_backup);
10082 done:
10083 got_object_id_queue_free(&commits);
10084 free(branch_head_commit_id);
10085 free(resume_commit_id);
10086 free(yca_id);
10087 if (commit)
10088 got_object_commit_close(commit);
10089 if (branch)
10090 got_ref_close(branch);
10091 if (new_base_branch)
10092 got_ref_close(new_base_branch);
10093 if (tmp_branch)
10094 got_ref_close(tmp_branch);
10095 if (worktree)
10096 got_worktree_close(worktree);
10097 if (repo) {
10098 const struct got_error *close_err = got_repo_close(repo);
10099 if (error == NULL)
10100 error = close_err;
10102 if (pack_fds) {
10103 const struct got_error *pack_err =
10104 got_repo_pack_fds_close(pack_fds);
10105 if (error == NULL)
10106 error = pack_err;
10108 return error;
10111 __dead static void
10112 usage_histedit(void)
10114 fprintf(stderr, "usage: %s histedit [-a] [-c] [-e] [-f] "
10115 "[-F histedit-script] [-m] [-l] [-X] [branch]\n",
10116 getprogname());
10117 exit(1);
10120 #define GOT_HISTEDIT_PICK 'p'
10121 #define GOT_HISTEDIT_EDIT 'e'
10122 #define GOT_HISTEDIT_FOLD 'f'
10123 #define GOT_HISTEDIT_DROP 'd'
10124 #define GOT_HISTEDIT_MESG 'm'
10126 static const struct got_histedit_cmd {
10127 unsigned char code;
10128 const char *name;
10129 const char *desc;
10130 } got_histedit_cmds[] = {
10131 { GOT_HISTEDIT_PICK, "pick", "use commit" },
10132 { GOT_HISTEDIT_EDIT, "edit", "use commit but stop for amending" },
10133 { GOT_HISTEDIT_FOLD, "fold", "combine with next commit that will "
10134 "be used" },
10135 { GOT_HISTEDIT_DROP, "drop", "remove commit from history" },
10136 { GOT_HISTEDIT_MESG, "mesg",
10137 "single-line log message for commit above (open editor if empty)" },
10140 struct got_histedit_list_entry {
10141 TAILQ_ENTRY(got_histedit_list_entry) entry;
10142 struct got_object_id *commit_id;
10143 const struct got_histedit_cmd *cmd;
10144 char *logmsg;
10146 TAILQ_HEAD(got_histedit_list, got_histedit_list_entry);
10148 static const struct got_error *
10149 histedit_write_commit(struct got_object_id *commit_id, const char *cmdname,
10150 FILE *f, struct got_repository *repo)
10152 const struct got_error *err = NULL;
10153 char *logmsg = NULL, *id_str = NULL;
10154 struct got_commit_object *commit = NULL;
10155 int n;
10157 err = got_object_open_as_commit(&commit, repo, commit_id);
10158 if (err)
10159 goto done;
10161 err = get_short_logmsg(&logmsg, 34, commit);
10162 if (err)
10163 goto done;
10165 err = got_object_id_str(&id_str, commit_id);
10166 if (err)
10167 goto done;
10169 n = fprintf(f, "%s %s %s\n", cmdname, id_str, logmsg);
10170 if (n < 0)
10171 err = got_ferror(f, GOT_ERR_IO);
10172 done:
10173 if (commit)
10174 got_object_commit_close(commit);
10175 free(id_str);
10176 free(logmsg);
10177 return err;
10180 static const struct got_error *
10181 histedit_write_commit_list(struct got_object_id_queue *commits,
10182 FILE *f, int edit_logmsg_only, int fold_only, int edit_only,
10183 struct got_repository *repo)
10185 const struct got_error *err = NULL;
10186 struct got_object_qid *qid;
10187 const char *histedit_cmd = NULL;
10189 if (STAILQ_EMPTY(commits))
10190 return got_error(GOT_ERR_EMPTY_HISTEDIT);
10192 STAILQ_FOREACH(qid, commits, entry) {
10193 histedit_cmd = got_histedit_cmds[0].name;
10194 if (edit_only)
10195 histedit_cmd = "edit";
10196 else if (fold_only && STAILQ_NEXT(qid, entry) != NULL)
10197 histedit_cmd = "fold";
10198 err = histedit_write_commit(&qid->id, histedit_cmd, f, repo);
10199 if (err)
10200 break;
10201 if (edit_logmsg_only) {
10202 int n = fprintf(f, "%c\n", GOT_HISTEDIT_MESG);
10203 if (n < 0) {
10204 err = got_ferror(f, GOT_ERR_IO);
10205 break;
10210 return err;
10213 static const struct got_error *
10214 write_cmd_list(FILE *f, const char *branch_name,
10215 struct got_object_id_queue *commits)
10217 const struct got_error *err = NULL;
10218 size_t i;
10219 int n;
10220 char *id_str;
10221 struct got_object_qid *qid;
10223 qid = STAILQ_FIRST(commits);
10224 err = got_object_id_str(&id_str, &qid->id);
10225 if (err)
10226 return err;
10228 n = fprintf(f,
10229 "# Editing the history of branch '%s' starting at\n"
10230 "# commit %s\n"
10231 "# Commits will be processed in order from top to "
10232 "bottom of this file.\n", branch_name, id_str);
10233 if (n < 0) {
10234 err = got_ferror(f, GOT_ERR_IO);
10235 goto done;
10238 n = fprintf(f, "# Available histedit commands:\n");
10239 if (n < 0) {
10240 err = got_ferror(f, GOT_ERR_IO);
10241 goto done;
10244 for (i = 0; i < nitems(got_histedit_cmds); i++) {
10245 const struct got_histedit_cmd *cmd = &got_histedit_cmds[i];
10246 n = fprintf(f, "# %s (%c): %s\n", cmd->name, cmd->code,
10247 cmd->desc);
10248 if (n < 0) {
10249 err = got_ferror(f, GOT_ERR_IO);
10250 break;
10253 done:
10254 free(id_str);
10255 return err;
10258 static const struct got_error *
10259 histedit_syntax_error(int lineno)
10261 static char msg[42];
10262 int ret;
10264 ret = snprintf(msg, sizeof(msg), "histedit syntax error on line %d",
10265 lineno);
10266 if (ret == -1 || ret >= sizeof(msg))
10267 return got_error(GOT_ERR_HISTEDIT_SYNTAX);
10269 return got_error_msg(GOT_ERR_HISTEDIT_SYNTAX, msg);
10272 static const struct got_error *
10273 append_folded_commit_msg(char **new_msg, struct got_histedit_list_entry *hle,
10274 char *logmsg, struct got_repository *repo)
10276 const struct got_error *err;
10277 struct got_commit_object *folded_commit = NULL;
10278 char *id_str, *folded_logmsg = NULL;
10280 err = got_object_id_str(&id_str, hle->commit_id);
10281 if (err)
10282 return err;
10284 err = got_object_open_as_commit(&folded_commit, repo, hle->commit_id);
10285 if (err)
10286 goto done;
10288 err = got_object_commit_get_logmsg(&folded_logmsg, folded_commit);
10289 if (err)
10290 goto done;
10291 if (asprintf(new_msg, "%s%s# log message of folded commit %s: %s",
10292 logmsg ? logmsg : "", logmsg ? "\n" : "", id_str,
10293 folded_logmsg) == -1) {
10294 err = got_error_from_errno("asprintf");
10296 done:
10297 if (folded_commit)
10298 got_object_commit_close(folded_commit);
10299 free(id_str);
10300 free(folded_logmsg);
10301 return err;
10304 static struct got_histedit_list_entry *
10305 get_folded_commits(struct got_histedit_list_entry *hle)
10307 struct got_histedit_list_entry *prev, *folded = NULL;
10309 prev = TAILQ_PREV(hle, got_histedit_list, entry);
10310 while (prev && (prev->cmd->code == GOT_HISTEDIT_FOLD ||
10311 prev->cmd->code == GOT_HISTEDIT_DROP)) {
10312 if (prev->cmd->code == GOT_HISTEDIT_FOLD)
10313 folded = prev;
10314 prev = TAILQ_PREV(prev, got_histedit_list, entry);
10317 return folded;
10320 static const struct got_error *
10321 histedit_edit_logmsg(struct got_histedit_list_entry *hle,
10322 struct got_repository *repo)
10324 char *logmsg_path = NULL, *id_str = NULL, *orig_logmsg = NULL;
10325 char *logmsg = NULL, *new_msg = NULL, *editor = NULL;
10326 const struct got_error *err = NULL;
10327 struct got_commit_object *commit = NULL;
10328 int logmsg_len;
10329 int fd;
10330 struct got_histedit_list_entry *folded = NULL;
10332 err = got_object_open_as_commit(&commit, repo, hle->commit_id);
10333 if (err)
10334 return err;
10336 folded = get_folded_commits(hle);
10337 if (folded) {
10338 while (folded != hle) {
10339 if (folded->cmd->code == GOT_HISTEDIT_DROP) {
10340 folded = TAILQ_NEXT(folded, entry);
10341 continue;
10343 err = append_folded_commit_msg(&new_msg, folded,
10344 logmsg, repo);
10345 if (err)
10346 goto done;
10347 free(logmsg);
10348 logmsg = new_msg;
10349 folded = TAILQ_NEXT(folded, entry);
10353 err = got_object_id_str(&id_str, hle->commit_id);
10354 if (err)
10355 goto done;
10356 err = got_object_commit_get_logmsg(&orig_logmsg, commit);
10357 if (err)
10358 goto done;
10359 logmsg_len = asprintf(&new_msg,
10360 "%s\n# original log message of commit %s: %s",
10361 logmsg ? logmsg : "", id_str, orig_logmsg);
10362 if (logmsg_len == -1) {
10363 err = got_error_from_errno("asprintf");
10364 goto done;
10366 free(logmsg);
10367 logmsg = new_msg;
10369 err = got_object_id_str(&id_str, hle->commit_id);
10370 if (err)
10371 goto done;
10373 err = got_opentemp_named_fd(&logmsg_path, &fd,
10374 GOT_TMPDIR_STR "/got-logmsg");
10375 if (err)
10376 goto done;
10378 write(fd, logmsg, logmsg_len);
10379 close(fd);
10381 err = get_editor(&editor);
10382 if (err)
10383 goto done;
10385 err = edit_logmsg(&hle->logmsg, editor, logmsg_path, logmsg,
10386 logmsg_len, 0);
10387 if (err) {
10388 if (err->code != GOT_ERR_COMMIT_MSG_EMPTY)
10389 goto done;
10390 err = NULL;
10391 hle->logmsg = strdup(new_msg);
10392 if (hle->logmsg == NULL)
10393 err = got_error_from_errno("strdup");
10395 done:
10396 if (logmsg_path && unlink(logmsg_path) != 0 && err == NULL)
10397 err = got_error_from_errno2("unlink", logmsg_path);
10398 free(logmsg_path);
10399 free(logmsg);
10400 free(orig_logmsg);
10401 free(editor);
10402 if (commit)
10403 got_object_commit_close(commit);
10404 return err;
10407 static const struct got_error *
10408 histedit_parse_list(struct got_histedit_list *histedit_cmds,
10409 FILE *f, struct got_repository *repo)
10411 const struct got_error *err = NULL;
10412 char *line = NULL, *p, *end;
10413 size_t i, size;
10414 ssize_t len;
10415 int lineno = 0;
10416 const struct got_histedit_cmd *cmd;
10417 struct got_object_id *commit_id = NULL;
10418 struct got_histedit_list_entry *hle = NULL;
10420 for (;;) {
10421 len = getline(&line, &size, f);
10422 if (len == -1) {
10423 const struct got_error *getline_err;
10424 if (feof(f))
10425 break;
10426 getline_err = got_error_from_errno("getline");
10427 err = got_ferror(f, getline_err->code);
10428 break;
10430 lineno++;
10431 p = line;
10432 while (isspace((unsigned char)p[0]))
10433 p++;
10434 if (p[0] == '#' || p[0] == '\0') {
10435 free(line);
10436 line = NULL;
10437 continue;
10439 cmd = NULL;
10440 for (i = 0; i < nitems(got_histedit_cmds); i++) {
10441 cmd = &got_histedit_cmds[i];
10442 if (strncmp(cmd->name, p, strlen(cmd->name)) == 0 &&
10443 isspace((unsigned char)p[strlen(cmd->name)])) {
10444 p += strlen(cmd->name);
10445 break;
10447 if (p[0] == cmd->code && isspace((unsigned char)p[1])) {
10448 p++;
10449 break;
10452 if (i == nitems(got_histedit_cmds)) {
10453 err = histedit_syntax_error(lineno);
10454 break;
10456 while (isspace((unsigned char)p[0]))
10457 p++;
10458 if (cmd->code == GOT_HISTEDIT_MESG) {
10459 if (hle == NULL || hle->logmsg != NULL) {
10460 err = got_error(GOT_ERR_HISTEDIT_CMD);
10461 break;
10463 if (p[0] == '\0') {
10464 err = histedit_edit_logmsg(hle, repo);
10465 if (err)
10466 break;
10467 } else {
10468 hle->logmsg = strdup(p);
10469 if (hle->logmsg == NULL) {
10470 err = got_error_from_errno("strdup");
10471 break;
10474 free(line);
10475 line = NULL;
10476 continue;
10477 } else {
10478 end = p;
10479 while (end[0] && !isspace((unsigned char)end[0]))
10480 end++;
10481 *end = '\0';
10483 err = got_object_resolve_id_str(&commit_id, repo, p);
10484 if (err) {
10485 /* override error code */
10486 err = histedit_syntax_error(lineno);
10487 break;
10490 hle = malloc(sizeof(*hle));
10491 if (hle == NULL) {
10492 err = got_error_from_errno("malloc");
10493 break;
10495 hle->cmd = cmd;
10496 hle->commit_id = commit_id;
10497 hle->logmsg = NULL;
10498 commit_id = NULL;
10499 free(line);
10500 line = NULL;
10501 TAILQ_INSERT_TAIL(histedit_cmds, hle, entry);
10504 free(line);
10505 free(commit_id);
10506 return err;
10509 static const struct got_error *
10510 histedit_check_script(struct got_histedit_list *histedit_cmds,
10511 struct got_object_id_queue *commits, struct got_repository *repo)
10513 const struct got_error *err = NULL;
10514 struct got_object_qid *qid;
10515 struct got_histedit_list_entry *hle;
10516 static char msg[92];
10517 char *id_str;
10519 if (TAILQ_EMPTY(histedit_cmds))
10520 return got_error_msg(GOT_ERR_EMPTY_HISTEDIT,
10521 "histedit script contains no commands");
10522 if (STAILQ_EMPTY(commits))
10523 return got_error(GOT_ERR_EMPTY_HISTEDIT);
10525 TAILQ_FOREACH(hle, histedit_cmds, entry) {
10526 struct got_histedit_list_entry *hle2;
10527 TAILQ_FOREACH(hle2, histedit_cmds, entry) {
10528 if (hle == hle2)
10529 continue;
10530 if (got_object_id_cmp(hle->commit_id,
10531 hle2->commit_id) != 0)
10532 continue;
10533 err = got_object_id_str(&id_str, hle->commit_id);
10534 if (err)
10535 return err;
10536 snprintf(msg, sizeof(msg), "commit %s is listed "
10537 "more than once in histedit script", id_str);
10538 free(id_str);
10539 return got_error_msg(GOT_ERR_HISTEDIT_CMD, msg);
10543 STAILQ_FOREACH(qid, commits, entry) {
10544 TAILQ_FOREACH(hle, histedit_cmds, entry) {
10545 if (got_object_id_cmp(&qid->id, hle->commit_id) == 0)
10546 break;
10548 if (hle == NULL) {
10549 err = got_object_id_str(&id_str, &qid->id);
10550 if (err)
10551 return err;
10552 snprintf(msg, sizeof(msg),
10553 "commit %s missing from histedit script", id_str);
10554 free(id_str);
10555 return got_error_msg(GOT_ERR_HISTEDIT_CMD, msg);
10559 hle = TAILQ_LAST(histedit_cmds, got_histedit_list);
10560 if (hle && hle->cmd->code == GOT_HISTEDIT_FOLD)
10561 return got_error_msg(GOT_ERR_HISTEDIT_CMD,
10562 "last commit in histedit script cannot be folded");
10564 return NULL;
10567 static const struct got_error *
10568 histedit_run_editor(struct got_histedit_list *histedit_cmds,
10569 const char *path, struct got_object_id_queue *commits,
10570 struct got_repository *repo)
10572 const struct got_error *err = NULL;
10573 char *editor;
10574 FILE *f = NULL;
10576 err = get_editor(&editor);
10577 if (err)
10578 return err;
10580 if (spawn_editor(editor, path) == -1) {
10581 err = got_error_from_errno("failed spawning editor");
10582 goto done;
10585 f = fopen(path, "re");
10586 if (f == NULL) {
10587 err = got_error_from_errno("fopen");
10588 goto done;
10590 err = histedit_parse_list(histedit_cmds, f, repo);
10591 if (err)
10592 goto done;
10594 err = histedit_check_script(histedit_cmds, commits, repo);
10595 done:
10596 if (f && fclose(f) == EOF && err == NULL)
10597 err = got_error_from_errno("fclose");
10598 free(editor);
10599 return err;
10602 static const struct got_error *
10603 histedit_edit_list_retry(struct got_histedit_list *, const struct got_error *,
10604 struct got_object_id_queue *, const char *, const char *,
10605 struct got_repository *);
10607 static const struct got_error *
10608 histedit_edit_script(struct got_histedit_list *histedit_cmds,
10609 struct got_object_id_queue *commits, const char *branch_name,
10610 int edit_logmsg_only, int fold_only, int edit_only,
10611 struct got_repository *repo)
10613 const struct got_error *err;
10614 FILE *f = NULL;
10615 char *path = NULL;
10617 err = got_opentemp_named(&path, &f, "got-histedit");
10618 if (err)
10619 return err;
10621 err = write_cmd_list(f, branch_name, commits);
10622 if (err)
10623 goto done;
10625 err = histedit_write_commit_list(commits, f, edit_logmsg_only,
10626 fold_only, edit_only, repo);
10627 if (err)
10628 goto done;
10630 if (edit_logmsg_only || fold_only || edit_only) {
10631 rewind(f);
10632 err = histedit_parse_list(histedit_cmds, f, repo);
10633 } else {
10634 if (fclose(f) == EOF) {
10635 err = got_error_from_errno("fclose");
10636 goto done;
10638 f = NULL;
10639 err = histedit_run_editor(histedit_cmds, path, commits, repo);
10640 if (err) {
10641 if (err->code != GOT_ERR_HISTEDIT_SYNTAX &&
10642 err->code != GOT_ERR_HISTEDIT_CMD)
10643 goto done;
10644 err = histedit_edit_list_retry(histedit_cmds, err,
10645 commits, path, branch_name, repo);
10648 done:
10649 if (f && fclose(f) == EOF && err == NULL)
10650 err = got_error_from_errno("fclose");
10651 if (path && unlink(path) != 0 && err == NULL)
10652 err = got_error_from_errno2("unlink", path);
10653 free(path);
10654 return err;
10657 static const struct got_error *
10658 histedit_save_list(struct got_histedit_list *histedit_cmds,
10659 struct got_worktree *worktree, struct got_repository *repo)
10661 const struct got_error *err = NULL;
10662 char *path = NULL;
10663 FILE *f = NULL;
10664 struct got_histedit_list_entry *hle;
10665 struct got_commit_object *commit = NULL;
10667 err = got_worktree_get_histedit_script_path(&path, worktree);
10668 if (err)
10669 return err;
10671 f = fopen(path, "we");
10672 if (f == NULL) {
10673 err = got_error_from_errno2("fopen", path);
10674 goto done;
10676 TAILQ_FOREACH(hle, histedit_cmds, entry) {
10677 err = histedit_write_commit(hle->commit_id, hle->cmd->name, f,
10678 repo);
10679 if (err)
10680 break;
10682 if (hle->logmsg) {
10683 int n = fprintf(f, "%c %s\n",
10684 GOT_HISTEDIT_MESG, hle->logmsg);
10685 if (n < 0) {
10686 err = got_ferror(f, GOT_ERR_IO);
10687 break;
10691 done:
10692 if (f && fclose(f) == EOF && err == NULL)
10693 err = got_error_from_errno("fclose");
10694 free(path);
10695 if (commit)
10696 got_object_commit_close(commit);
10697 return err;
10700 void
10701 histedit_free_list(struct got_histedit_list *histedit_cmds)
10703 struct got_histedit_list_entry *hle;
10705 while ((hle = TAILQ_FIRST(histedit_cmds))) {
10706 TAILQ_REMOVE(histedit_cmds, hle, entry);
10707 free(hle);
10711 static const struct got_error *
10712 histedit_load_list(struct got_histedit_list *histedit_cmds,
10713 const char *path, struct got_repository *repo)
10715 const struct got_error *err = NULL;
10716 FILE *f = NULL;
10718 f = fopen(path, "re");
10719 if (f == NULL) {
10720 err = got_error_from_errno2("fopen", path);
10721 goto done;
10724 err = histedit_parse_list(histedit_cmds, f, repo);
10725 done:
10726 if (f && fclose(f) == EOF && err == NULL)
10727 err = got_error_from_errno("fclose");
10728 return err;
10731 static const struct got_error *
10732 histedit_edit_list_retry(struct got_histedit_list *histedit_cmds,
10733 const struct got_error *edit_err, struct got_object_id_queue *commits,
10734 const char *path, const char *branch_name, struct got_repository *repo)
10736 const struct got_error *err = NULL, *prev_err = edit_err;
10737 int resp = ' ';
10739 while (resp != 'c' && resp != 'r' && resp != 'a') {
10740 printf("%s: %s\n(c)ontinue editing, (r)estart editing, "
10741 "or (a)bort: ", getprogname(), prev_err->msg);
10742 resp = getchar();
10743 if (resp == '\n')
10744 resp = getchar();
10745 if (resp == 'c') {
10746 histedit_free_list(histedit_cmds);
10747 err = histedit_run_editor(histedit_cmds, path, commits,
10748 repo);
10749 if (err) {
10750 if (err->code != GOT_ERR_HISTEDIT_SYNTAX &&
10751 err->code != GOT_ERR_HISTEDIT_CMD)
10752 break;
10753 prev_err = err;
10754 resp = ' ';
10755 continue;
10757 break;
10758 } else if (resp == 'r') {
10759 histedit_free_list(histedit_cmds);
10760 err = histedit_edit_script(histedit_cmds,
10761 commits, branch_name, 0, 0, 0, repo);
10762 if (err) {
10763 if (err->code != GOT_ERR_HISTEDIT_SYNTAX &&
10764 err->code != GOT_ERR_HISTEDIT_CMD)
10765 break;
10766 prev_err = err;
10767 resp = ' ';
10768 continue;
10770 break;
10771 } else if (resp == 'a') {
10772 err = got_error(GOT_ERR_HISTEDIT_CANCEL);
10773 break;
10774 } else
10775 printf("invalid response '%c'\n", resp);
10778 return err;
10781 static const struct got_error *
10782 histedit_complete(struct got_worktree *worktree,
10783 struct got_fileindex *fileindex, struct got_reference *tmp_branch,
10784 struct got_reference *branch, struct got_repository *repo)
10786 printf("Switching work tree to %s\n",
10787 got_ref_get_symref_target(branch));
10788 return got_worktree_histedit_complete(worktree, fileindex, tmp_branch,
10789 branch, repo);
10792 static const struct got_error *
10793 show_histedit_progress(struct got_commit_object *commit,
10794 struct got_histedit_list_entry *hle, struct got_object_id *new_id)
10796 const struct got_error *err;
10797 char *old_id_str = NULL, *new_id_str = NULL, *logmsg = NULL;
10799 err = got_object_id_str(&old_id_str, hle->commit_id);
10800 if (err)
10801 goto done;
10803 if (new_id) {
10804 err = got_object_id_str(&new_id_str, new_id);
10805 if (err)
10806 goto done;
10809 old_id_str[12] = '\0';
10810 if (new_id_str)
10811 new_id_str[12] = '\0';
10813 if (hle->logmsg) {
10814 logmsg = strdup(hle->logmsg);
10815 if (logmsg == NULL) {
10816 err = got_error_from_errno("strdup");
10817 goto done;
10819 trim_logmsg(logmsg, 42);
10820 } else {
10821 err = get_short_logmsg(&logmsg, 42, commit);
10822 if (err)
10823 goto done;
10826 switch (hle->cmd->code) {
10827 case GOT_HISTEDIT_PICK:
10828 case GOT_HISTEDIT_EDIT:
10829 printf("%s -> %s: %s\n", old_id_str,
10830 new_id_str ? new_id_str : "no-op change", logmsg);
10831 break;
10832 case GOT_HISTEDIT_DROP:
10833 case GOT_HISTEDIT_FOLD:
10834 printf("%s -> %s commit: %s\n", old_id_str, hle->cmd->name,
10835 logmsg);
10836 break;
10837 default:
10838 break;
10840 done:
10841 free(old_id_str);
10842 free(new_id_str);
10843 return err;
10846 static const struct got_error *
10847 histedit_commit(struct got_pathlist_head *merged_paths,
10848 struct got_worktree *worktree, struct got_fileindex *fileindex,
10849 struct got_reference *tmp_branch, struct got_histedit_list_entry *hle,
10850 struct got_repository *repo)
10852 const struct got_error *err;
10853 struct got_commit_object *commit;
10854 struct got_object_id *new_commit_id;
10856 if ((hle->cmd->code == GOT_HISTEDIT_EDIT || get_folded_commits(hle))
10857 && hle->logmsg == NULL) {
10858 err = histedit_edit_logmsg(hle, repo);
10859 if (err)
10860 return err;
10863 err = got_object_open_as_commit(&commit, repo, hle->commit_id);
10864 if (err)
10865 return err;
10867 err = got_worktree_histedit_commit(&new_commit_id, merged_paths,
10868 worktree, fileindex, tmp_branch, commit, hle->commit_id,
10869 hle->logmsg, repo);
10870 if (err) {
10871 if (err->code != GOT_ERR_COMMIT_NO_CHANGES)
10872 goto done;
10873 err = show_histedit_progress(commit, hle, NULL);
10874 } else {
10875 err = show_histedit_progress(commit, hle, new_commit_id);
10876 free(new_commit_id);
10878 done:
10879 got_object_commit_close(commit);
10880 return err;
10883 static const struct got_error *
10884 histedit_skip_commit(struct got_histedit_list_entry *hle,
10885 struct got_worktree *worktree, struct got_repository *repo)
10887 const struct got_error *error;
10888 struct got_commit_object *commit;
10890 error = got_worktree_histedit_skip_commit(worktree, hle->commit_id,
10891 repo);
10892 if (error)
10893 return error;
10895 error = got_object_open_as_commit(&commit, repo, hle->commit_id);
10896 if (error)
10897 return error;
10899 error = show_histedit_progress(commit, hle, NULL);
10900 got_object_commit_close(commit);
10901 return error;
10904 static const struct got_error *
10905 check_local_changes(void *arg, unsigned char status,
10906 unsigned char staged_status, const char *path,
10907 struct got_object_id *blob_id, struct got_object_id *staged_blob_id,
10908 struct got_object_id *commit_id, int dirfd, const char *de_name)
10910 int *have_local_changes = arg;
10912 switch (status) {
10913 case GOT_STATUS_ADD:
10914 case GOT_STATUS_DELETE:
10915 case GOT_STATUS_MODIFY:
10916 case GOT_STATUS_CONFLICT:
10917 *have_local_changes = 1;
10918 return got_error(GOT_ERR_CANCELLED);
10919 default:
10920 break;
10923 switch (staged_status) {
10924 case GOT_STATUS_ADD:
10925 case GOT_STATUS_DELETE:
10926 case GOT_STATUS_MODIFY:
10927 *have_local_changes = 1;
10928 return got_error(GOT_ERR_CANCELLED);
10929 default:
10930 break;
10933 return NULL;
10936 static const struct got_error *
10937 cmd_histedit(int argc, char *argv[])
10939 const struct got_error *error = NULL;
10940 struct got_worktree *worktree = NULL;
10941 struct got_fileindex *fileindex = NULL;
10942 struct got_repository *repo = NULL;
10943 char *cwd = NULL;
10944 struct got_reference *branch = NULL;
10945 struct got_reference *tmp_branch = NULL;
10946 struct got_object_id *resume_commit_id = NULL;
10947 struct got_object_id *base_commit_id = NULL;
10948 struct got_object_id *head_commit_id = NULL;
10949 struct got_commit_object *commit = NULL;
10950 int ch, rebase_in_progress = 0, merge_in_progress = 0;
10951 struct got_update_progress_arg upa;
10952 int edit_in_progress = 0, abort_edit = 0, continue_edit = 0;
10953 int edit_logmsg_only = 0, fold_only = 0, edit_only = 0;
10954 int list_backups = 0, delete_backups = 0;
10955 const char *edit_script_path = NULL;
10956 struct got_object_id_queue commits;
10957 struct got_pathlist_head merged_paths;
10958 const struct got_object_id_queue *parent_ids;
10959 struct got_object_qid *pid;
10960 struct got_histedit_list histedit_cmds;
10961 struct got_histedit_list_entry *hle;
10962 int *pack_fds = NULL;
10964 STAILQ_INIT(&commits);
10965 TAILQ_INIT(&histedit_cmds);
10966 TAILQ_INIT(&merged_paths);
10967 memset(&upa, 0, sizeof(upa));
10969 while ((ch = getopt(argc, argv, "acefF:mlX")) != -1) {
10970 switch (ch) {
10971 case 'a':
10972 abort_edit = 1;
10973 break;
10974 case 'c':
10975 continue_edit = 1;
10976 break;
10977 case 'e':
10978 edit_only = 1;
10979 break;
10980 case 'f':
10981 fold_only = 1;
10982 break;
10983 case 'F':
10984 edit_script_path = optarg;
10985 break;
10986 case 'm':
10987 edit_logmsg_only = 1;
10988 break;
10989 case 'l':
10990 list_backups = 1;
10991 break;
10992 case 'X':
10993 delete_backups = 1;
10994 break;
10995 default:
10996 usage_histedit();
10997 /* NOTREACHED */
11001 argc -= optind;
11002 argv += optind;
11004 #ifndef PROFILE
11005 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
11006 "unveil", NULL) == -1)
11007 err(1, "pledge");
11008 #endif
11009 if (abort_edit && continue_edit)
11010 option_conflict('a', 'c');
11011 if (edit_script_path && edit_logmsg_only)
11012 option_conflict('F', 'm');
11013 if (abort_edit && edit_logmsg_only)
11014 option_conflict('a', 'm');
11015 if (continue_edit && edit_logmsg_only)
11016 option_conflict('c', 'm');
11017 if (abort_edit && fold_only)
11018 option_conflict('a', 'f');
11019 if (continue_edit && fold_only)
11020 option_conflict('c', 'f');
11021 if (fold_only && edit_logmsg_only)
11022 option_conflict('f', 'm');
11023 if (edit_script_path && fold_only)
11024 option_conflict('F', 'f');
11025 if (abort_edit && edit_only)
11026 option_conflict('a', 'e');
11027 if (continue_edit && edit_only)
11028 option_conflict('c', 'e');
11029 if (edit_only && edit_logmsg_only)
11030 option_conflict('e', 'm');
11031 if (edit_script_path && edit_only)
11032 option_conflict('F', 'e');
11033 if (list_backups) {
11034 if (abort_edit)
11035 option_conflict('l', 'a');
11036 if (continue_edit)
11037 option_conflict('l', 'c');
11038 if (edit_script_path)
11039 option_conflict('l', 'F');
11040 if (edit_logmsg_only)
11041 option_conflict('l', 'm');
11042 if (fold_only)
11043 option_conflict('l', 'f');
11044 if (edit_only)
11045 option_conflict('l', 'e');
11046 if (delete_backups)
11047 option_conflict('l', 'X');
11048 if (argc != 0 && argc != 1)
11049 usage_histedit();
11050 } else if (delete_backups) {
11051 if (abort_edit)
11052 option_conflict('X', 'a');
11053 if (continue_edit)
11054 option_conflict('X', 'c');
11055 if (edit_script_path)
11056 option_conflict('X', 'F');
11057 if (edit_logmsg_only)
11058 option_conflict('X', 'm');
11059 if (fold_only)
11060 option_conflict('X', 'f');
11061 if (edit_only)
11062 option_conflict('X', 'e');
11063 if (list_backups)
11064 option_conflict('X', 'l');
11065 if (argc != 0 && argc != 1)
11066 usage_histedit();
11067 } else if (argc != 0)
11068 usage_histedit();
11071 * This command cannot apply unveil(2) in all cases because the
11072 * user may choose to run an editor to edit the histedit script
11073 * and to edit individual commit log messages.
11074 * unveil(2) traverses exec(2); if an editor is used we have to
11075 * apply unveil after edit script and log messages have been written.
11076 * XXX TODO: Make use of unveil(2) where possible.
11079 cwd = getcwd(NULL, 0);
11080 if (cwd == NULL) {
11081 error = got_error_from_errno("getcwd");
11082 goto done;
11085 error = got_repo_pack_fds_open(&pack_fds);
11086 if (error != NULL)
11087 goto done;
11089 error = got_worktree_open(&worktree, cwd);
11090 if (error) {
11091 if (list_backups || delete_backups) {
11092 if (error->code != GOT_ERR_NOT_WORKTREE)
11093 goto done;
11094 } else {
11095 if (error->code == GOT_ERR_NOT_WORKTREE)
11096 error = wrap_not_worktree_error(error,
11097 "histedit", cwd);
11098 goto done;
11102 if (list_backups || delete_backups) {
11103 error = got_repo_open(&repo,
11104 worktree ? got_worktree_get_repo_path(worktree) : cwd,
11105 NULL, pack_fds);
11106 if (error != NULL)
11107 goto done;
11108 error = apply_unveil(got_repo_get_path(repo), 0,
11109 worktree ? got_worktree_get_root_path(worktree) : NULL);
11110 if (error)
11111 goto done;
11112 error = process_backup_refs(
11113 GOT_WORKTREE_HISTEDIT_BACKUP_REF_PREFIX,
11114 argc == 1 ? argv[0] : NULL, delete_backups, repo);
11115 goto done; /* nothing else to do */
11118 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
11119 NULL, pack_fds);
11120 if (error != NULL)
11121 goto done;
11123 error = got_worktree_rebase_in_progress(&rebase_in_progress, worktree);
11124 if (error)
11125 goto done;
11126 if (rebase_in_progress) {
11127 error = got_error(GOT_ERR_REBASING);
11128 goto done;
11131 error = got_worktree_merge_in_progress(&merge_in_progress, worktree,
11132 repo);
11133 if (error)
11134 goto done;
11135 if (merge_in_progress) {
11136 error = got_error(GOT_ERR_MERGE_BUSY);
11137 goto done;
11140 error = got_worktree_histedit_in_progress(&edit_in_progress, worktree);
11141 if (error)
11142 goto done;
11144 if (edit_in_progress && edit_logmsg_only) {
11145 error = got_error_msg(GOT_ERR_HISTEDIT_BUSY,
11146 "histedit operation is in progress in this "
11147 "work tree and must be continued or aborted "
11148 "before the -m option can be used");
11149 goto done;
11151 if (edit_in_progress && fold_only) {
11152 error = got_error_msg(GOT_ERR_HISTEDIT_BUSY,
11153 "histedit operation is in progress in this "
11154 "work tree and must be continued or aborted "
11155 "before the -f option can be used");
11156 goto done;
11158 if (edit_in_progress && edit_only) {
11159 error = got_error_msg(GOT_ERR_HISTEDIT_BUSY,
11160 "histedit operation is in progress in this "
11161 "work tree and must be continued or aborted "
11162 "before the -e option can be used");
11163 goto done;
11166 if (edit_in_progress && abort_edit) {
11167 error = got_worktree_histedit_continue(&resume_commit_id,
11168 &tmp_branch, &branch, &base_commit_id, &fileindex,
11169 worktree, repo);
11170 if (error)
11171 goto done;
11172 printf("Switching work tree to %s\n",
11173 got_ref_get_symref_target(branch));
11174 error = got_worktree_histedit_abort(worktree, fileindex, repo,
11175 branch, base_commit_id, abort_progress, &upa);
11176 if (error)
11177 goto done;
11178 printf("Histedit of %s aborted\n",
11179 got_ref_get_symref_target(branch));
11180 print_merge_progress_stats(&upa);
11181 goto done; /* nothing else to do */
11182 } else if (abort_edit) {
11183 error = got_error(GOT_ERR_NOT_HISTEDIT);
11184 goto done;
11187 if (continue_edit) {
11188 char *path;
11190 if (!edit_in_progress) {
11191 error = got_error(GOT_ERR_NOT_HISTEDIT);
11192 goto done;
11195 error = got_worktree_get_histedit_script_path(&path, worktree);
11196 if (error)
11197 goto done;
11199 error = histedit_load_list(&histedit_cmds, path, repo);
11200 free(path);
11201 if (error)
11202 goto done;
11204 error = got_worktree_histedit_continue(&resume_commit_id,
11205 &tmp_branch, &branch, &base_commit_id, &fileindex,
11206 worktree, repo);
11207 if (error)
11208 goto done;
11210 error = got_ref_resolve(&head_commit_id, repo, branch);
11211 if (error)
11212 goto done;
11214 error = got_object_open_as_commit(&commit, repo,
11215 head_commit_id);
11216 if (error)
11217 goto done;
11218 parent_ids = got_object_commit_get_parent_ids(commit);
11219 pid = STAILQ_FIRST(parent_ids);
11220 if (pid == NULL) {
11221 error = got_error(GOT_ERR_EMPTY_HISTEDIT);
11222 goto done;
11224 error = collect_commits(&commits, head_commit_id, &pid->id,
11225 base_commit_id, got_worktree_get_path_prefix(worktree),
11226 GOT_ERR_HISTEDIT_PATH, repo);
11227 got_object_commit_close(commit);
11228 commit = NULL;
11229 if (error)
11230 goto done;
11231 } else {
11232 if (edit_in_progress) {
11233 error = got_error(GOT_ERR_HISTEDIT_BUSY);
11234 goto done;
11237 error = got_ref_open(&branch, repo,
11238 got_worktree_get_head_ref_name(worktree), 0);
11239 if (error != NULL)
11240 goto done;
11242 if (strncmp(got_ref_get_name(branch), "refs/heads/", 11) != 0) {
11243 error = got_error_msg(GOT_ERR_COMMIT_BRANCH,
11244 "will not edit commit history of a branch outside "
11245 "the \"refs/heads/\" reference namespace");
11246 goto done;
11249 error = got_ref_resolve(&head_commit_id, repo, branch);
11250 got_ref_close(branch);
11251 branch = NULL;
11252 if (error)
11253 goto done;
11255 error = got_object_open_as_commit(&commit, repo,
11256 head_commit_id);
11257 if (error)
11258 goto done;
11259 parent_ids = got_object_commit_get_parent_ids(commit);
11260 pid = STAILQ_FIRST(parent_ids);
11261 if (pid == NULL) {
11262 error = got_error(GOT_ERR_EMPTY_HISTEDIT);
11263 goto done;
11265 error = collect_commits(&commits, head_commit_id, &pid->id,
11266 got_worktree_get_base_commit_id(worktree),
11267 got_worktree_get_path_prefix(worktree),
11268 GOT_ERR_HISTEDIT_PATH, repo);
11269 got_object_commit_close(commit);
11270 commit = NULL;
11271 if (error)
11272 goto done;
11274 if (STAILQ_EMPTY(&commits)) {
11275 error = got_error(GOT_ERR_EMPTY_HISTEDIT);
11276 goto done;
11279 error = got_worktree_histedit_prepare(&tmp_branch, &branch,
11280 &base_commit_id, &fileindex, worktree, repo);
11281 if (error)
11282 goto done;
11284 if (edit_script_path) {
11285 error = histedit_load_list(&histedit_cmds,
11286 edit_script_path, repo);
11287 if (error) {
11288 got_worktree_histedit_abort(worktree, fileindex,
11289 repo, branch, base_commit_id,
11290 abort_progress, &upa);
11291 print_merge_progress_stats(&upa);
11292 goto done;
11294 } else {
11295 const char *branch_name;
11296 branch_name = got_ref_get_symref_target(branch);
11297 if (strncmp(branch_name, "refs/heads/", 11) == 0)
11298 branch_name += 11;
11299 error = histedit_edit_script(&histedit_cmds, &commits,
11300 branch_name, edit_logmsg_only, fold_only,
11301 edit_only, repo);
11302 if (error) {
11303 got_worktree_histedit_abort(worktree, fileindex,
11304 repo, branch, base_commit_id,
11305 abort_progress, &upa);
11306 print_merge_progress_stats(&upa);
11307 goto done;
11312 error = histedit_save_list(&histedit_cmds, worktree,
11313 repo);
11314 if (error) {
11315 got_worktree_histedit_abort(worktree, fileindex,
11316 repo, branch, base_commit_id,
11317 abort_progress, &upa);
11318 print_merge_progress_stats(&upa);
11319 goto done;
11324 error = histedit_check_script(&histedit_cmds, &commits, repo);
11325 if (error)
11326 goto done;
11328 TAILQ_FOREACH(hle, &histedit_cmds, entry) {
11329 if (resume_commit_id) {
11330 if (got_object_id_cmp(hle->commit_id,
11331 resume_commit_id) != 0)
11332 continue;
11334 resume_commit_id = NULL;
11335 if (hle->cmd->code == GOT_HISTEDIT_DROP ||
11336 hle->cmd->code == GOT_HISTEDIT_FOLD) {
11337 error = histedit_skip_commit(hle, worktree,
11338 repo);
11339 if (error)
11340 goto done;
11341 } else {
11342 struct got_pathlist_head paths;
11343 int have_changes = 0;
11345 TAILQ_INIT(&paths);
11346 error = got_pathlist_append(&paths, "", NULL);
11347 if (error)
11348 goto done;
11349 error = got_worktree_status(worktree, &paths,
11350 repo, 0, check_local_changes, &have_changes,
11351 check_cancelled, NULL);
11352 got_pathlist_free(&paths);
11353 if (error) {
11354 if (error->code != GOT_ERR_CANCELLED)
11355 goto done;
11356 if (sigint_received || sigpipe_received)
11357 goto done;
11359 if (have_changes) {
11360 error = histedit_commit(NULL, worktree,
11361 fileindex, tmp_branch, hle, repo);
11362 if (error)
11363 goto done;
11364 } else {
11365 error = got_object_open_as_commit(
11366 &commit, repo, hle->commit_id);
11367 if (error)
11368 goto done;
11369 error = show_histedit_progress(commit,
11370 hle, NULL);
11371 got_object_commit_close(commit);
11372 commit = NULL;
11373 if (error)
11374 goto done;
11377 continue;
11380 if (hle->cmd->code == GOT_HISTEDIT_DROP) {
11381 error = histedit_skip_commit(hle, worktree, repo);
11382 if (error)
11383 goto done;
11384 continue;
11387 error = got_object_open_as_commit(&commit, repo,
11388 hle->commit_id);
11389 if (error)
11390 goto done;
11391 parent_ids = got_object_commit_get_parent_ids(commit);
11392 pid = STAILQ_FIRST(parent_ids);
11394 error = got_worktree_histedit_merge_files(&merged_paths,
11395 worktree, fileindex, &pid->id, hle->commit_id, repo,
11396 update_progress, &upa, check_cancelled, NULL);
11397 if (error)
11398 goto done;
11399 got_object_commit_close(commit);
11400 commit = NULL;
11402 print_merge_progress_stats(&upa);
11403 if (upa.conflicts > 0 || upa.missing > 0 ||
11404 upa.not_deleted > 0 || upa.unversioned > 0) {
11405 if (upa.conflicts > 0) {
11406 error = show_rebase_merge_conflict(
11407 hle->commit_id, repo);
11408 if (error)
11409 goto done;
11411 got_worktree_rebase_pathlist_free(&merged_paths);
11412 break;
11415 if (hle->cmd->code == GOT_HISTEDIT_EDIT) {
11416 char *id_str;
11417 error = got_object_id_str(&id_str, hle->commit_id);
11418 if (error)
11419 goto done;
11420 printf("Stopping histedit for amending commit %s\n",
11421 id_str);
11422 free(id_str);
11423 got_worktree_rebase_pathlist_free(&merged_paths);
11424 error = got_worktree_histedit_postpone(worktree,
11425 fileindex);
11426 goto done;
11429 if (hle->cmd->code == GOT_HISTEDIT_FOLD) {
11430 error = histedit_skip_commit(hle, worktree, repo);
11431 if (error)
11432 goto done;
11433 continue;
11436 error = histedit_commit(&merged_paths, worktree, fileindex,
11437 tmp_branch, hle, repo);
11438 got_worktree_rebase_pathlist_free(&merged_paths);
11439 if (error)
11440 goto done;
11443 if (upa.conflicts > 0 || upa.missing > 0 ||
11444 upa.not_deleted > 0 || upa.unversioned > 0) {
11445 error = got_worktree_histedit_postpone(worktree, fileindex);
11446 if (error)
11447 goto done;
11448 if (upa.conflicts > 0 && upa.missing == 0 &&
11449 upa.not_deleted == 0 && upa.unversioned == 0) {
11450 error = got_error_msg(GOT_ERR_CONFLICTS,
11451 "conflicts must be resolved before histedit "
11452 "can continue");
11453 } else if (upa.conflicts > 0) {
11454 error = got_error_msg(GOT_ERR_CONFLICTS,
11455 "conflicts must be resolved before histedit "
11456 "can continue; changes destined for some "
11457 "files were not yet merged and should be "
11458 "merged manually if required before the "
11459 "histedit operation is continued");
11460 } else {
11461 error = got_error_msg(GOT_ERR_CONFLICTS,
11462 "changes destined for some files were not "
11463 "yet merged and should be merged manually "
11464 "if required before the histedit operation "
11465 "is continued");
11467 } else
11468 error = histedit_complete(worktree, fileindex, tmp_branch,
11469 branch, repo);
11470 done:
11471 got_object_id_queue_free(&commits);
11472 histedit_free_list(&histedit_cmds);
11473 free(head_commit_id);
11474 free(base_commit_id);
11475 free(resume_commit_id);
11476 if (commit)
11477 got_object_commit_close(commit);
11478 if (branch)
11479 got_ref_close(branch);
11480 if (tmp_branch)
11481 got_ref_close(tmp_branch);
11482 if (worktree)
11483 got_worktree_close(worktree);
11484 if (repo) {
11485 const struct got_error *close_err = got_repo_close(repo);
11486 if (error == NULL)
11487 error = close_err;
11489 if (pack_fds) {
11490 const struct got_error *pack_err =
11491 got_repo_pack_fds_close(pack_fds);
11492 if (error == NULL)
11493 error = pack_err;
11495 return error;
11498 __dead static void
11499 usage_integrate(void)
11501 fprintf(stderr, "usage: %s integrate branch\n", getprogname());
11502 exit(1);
11505 static const struct got_error *
11506 cmd_integrate(int argc, char *argv[])
11508 const struct got_error *error = NULL;
11509 struct got_repository *repo = NULL;
11510 struct got_worktree *worktree = NULL;
11511 char *cwd = NULL, *refname = NULL, *base_refname = NULL;
11512 const char *branch_arg = NULL;
11513 struct got_reference *branch_ref = NULL, *base_branch_ref = NULL;
11514 struct got_fileindex *fileindex = NULL;
11515 struct got_object_id *commit_id = NULL, *base_commit_id = NULL;
11516 int ch;
11517 struct got_update_progress_arg upa;
11518 int *pack_fds = NULL;
11520 while ((ch = getopt(argc, argv, "")) != -1) {
11521 switch (ch) {
11522 default:
11523 usage_integrate();
11524 /* NOTREACHED */
11528 argc -= optind;
11529 argv += optind;
11531 if (argc != 1)
11532 usage_integrate();
11533 branch_arg = argv[0];
11534 #ifndef PROFILE
11535 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
11536 "unveil", NULL) == -1)
11537 err(1, "pledge");
11538 #endif
11539 cwd = getcwd(NULL, 0);
11540 if (cwd == NULL) {
11541 error = got_error_from_errno("getcwd");
11542 goto done;
11545 error = got_repo_pack_fds_open(&pack_fds);
11546 if (error != NULL)
11547 goto done;
11549 error = got_worktree_open(&worktree, cwd);
11550 if (error) {
11551 if (error->code == GOT_ERR_NOT_WORKTREE)
11552 error = wrap_not_worktree_error(error, "integrate",
11553 cwd);
11554 goto done;
11557 error = check_rebase_or_histedit_in_progress(worktree);
11558 if (error)
11559 goto done;
11561 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
11562 NULL, pack_fds);
11563 if (error != NULL)
11564 goto done;
11566 error = apply_unveil(got_repo_get_path(repo), 0,
11567 got_worktree_get_root_path(worktree));
11568 if (error)
11569 goto done;
11571 error = check_merge_in_progress(worktree, repo);
11572 if (error)
11573 goto done;
11575 if (asprintf(&refname, "refs/heads/%s", branch_arg) == -1) {
11576 error = got_error_from_errno("asprintf");
11577 goto done;
11580 error = got_worktree_integrate_prepare(&fileindex, &branch_ref,
11581 &base_branch_ref, worktree, refname, repo);
11582 if (error)
11583 goto done;
11585 refname = strdup(got_ref_get_name(branch_ref));
11586 if (refname == NULL) {
11587 error = got_error_from_errno("strdup");
11588 got_worktree_integrate_abort(worktree, fileindex, repo,
11589 branch_ref, base_branch_ref);
11590 goto done;
11592 base_refname = strdup(got_ref_get_name(base_branch_ref));
11593 if (base_refname == NULL) {
11594 error = got_error_from_errno("strdup");
11595 got_worktree_integrate_abort(worktree, fileindex, repo,
11596 branch_ref, base_branch_ref);
11597 goto done;
11600 error = got_ref_resolve(&commit_id, repo, branch_ref);
11601 if (error)
11602 goto done;
11604 error = got_ref_resolve(&base_commit_id, repo, base_branch_ref);
11605 if (error)
11606 goto done;
11608 if (got_object_id_cmp(commit_id, base_commit_id) == 0) {
11609 error = got_error_msg(GOT_ERR_SAME_BRANCH,
11610 "specified branch has already been integrated");
11611 got_worktree_integrate_abort(worktree, fileindex, repo,
11612 branch_ref, base_branch_ref);
11613 goto done;
11616 error = check_linear_ancestry(commit_id, base_commit_id, 1, repo);
11617 if (error) {
11618 if (error->code == GOT_ERR_ANCESTRY)
11619 error = got_error(GOT_ERR_REBASE_REQUIRED);
11620 got_worktree_integrate_abort(worktree, fileindex, repo,
11621 branch_ref, base_branch_ref);
11622 goto done;
11625 memset(&upa, 0, sizeof(upa));
11626 error = got_worktree_integrate_continue(worktree, fileindex, repo,
11627 branch_ref, base_branch_ref, update_progress, &upa,
11628 check_cancelled, NULL);
11629 if (error)
11630 goto done;
11632 printf("Integrated %s into %s\n", refname, base_refname);
11633 print_update_progress_stats(&upa);
11634 done:
11635 if (repo) {
11636 const struct got_error *close_err = got_repo_close(repo);
11637 if (error == NULL)
11638 error = close_err;
11640 if (worktree)
11641 got_worktree_close(worktree);
11642 if (pack_fds) {
11643 const struct got_error *pack_err =
11644 got_repo_pack_fds_close(pack_fds);
11645 if (error == NULL)
11646 error = pack_err;
11648 free(cwd);
11649 free(base_commit_id);
11650 free(commit_id);
11651 free(refname);
11652 free(base_refname);
11653 return error;
11656 __dead static void
11657 usage_merge(void)
11659 fprintf(stderr, "usage: %s merge [-a] [-c] [-n] [branch]\n",
11660 getprogname());
11661 exit(1);
11664 static const struct got_error *
11665 cmd_merge(int argc, char *argv[])
11667 const struct got_error *error = NULL;
11668 struct got_worktree *worktree = NULL;
11669 struct got_repository *repo = NULL;
11670 struct got_fileindex *fileindex = NULL;
11671 char *cwd = NULL, *id_str = NULL, *author = NULL;
11672 struct got_reference *branch = NULL, *wt_branch = NULL;
11673 struct got_object_id *branch_tip = NULL, *yca_id = NULL;
11674 struct got_object_id *wt_branch_tip = NULL;
11675 int ch, merge_in_progress = 0, abort_merge = 0, continue_merge = 0;
11676 int interrupt_merge = 0;
11677 struct got_update_progress_arg upa;
11678 struct got_object_id *merge_commit_id = NULL;
11679 char *branch_name = NULL;
11680 int *pack_fds = NULL;
11682 memset(&upa, 0, sizeof(upa));
11684 while ((ch = getopt(argc, argv, "acn")) != -1) {
11685 switch (ch) {
11686 case 'a':
11687 abort_merge = 1;
11688 break;
11689 case 'c':
11690 continue_merge = 1;
11691 break;
11692 case 'n':
11693 interrupt_merge = 1;
11694 break;
11695 default:
11696 usage_rebase();
11697 /* NOTREACHED */
11701 argc -= optind;
11702 argv += optind;
11704 #ifndef PROFILE
11705 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
11706 "unveil", NULL) == -1)
11707 err(1, "pledge");
11708 #endif
11710 if (abort_merge && continue_merge)
11711 option_conflict('a', 'c');
11712 if (abort_merge || continue_merge) {
11713 if (argc != 0)
11714 usage_merge();
11715 } else if (argc != 1)
11716 usage_merge();
11718 cwd = getcwd(NULL, 0);
11719 if (cwd == NULL) {
11720 error = got_error_from_errno("getcwd");
11721 goto done;
11724 error = got_repo_pack_fds_open(&pack_fds);
11725 if (error != NULL)
11726 goto done;
11728 error = got_worktree_open(&worktree, cwd);
11729 if (error) {
11730 if (error->code == GOT_ERR_NOT_WORKTREE)
11731 error = wrap_not_worktree_error(error,
11732 "merge", cwd);
11733 goto done;
11736 error = got_repo_open(&repo,
11737 worktree ? got_worktree_get_repo_path(worktree) : cwd, NULL,
11738 pack_fds);
11739 if (error != NULL)
11740 goto done;
11742 error = apply_unveil(got_repo_get_path(repo), 0,
11743 worktree ? got_worktree_get_root_path(worktree) : NULL);
11744 if (error)
11745 goto done;
11747 error = check_rebase_or_histedit_in_progress(worktree);
11748 if (error)
11749 goto done;
11751 error = got_worktree_merge_in_progress(&merge_in_progress, worktree,
11752 repo);
11753 if (error)
11754 goto done;
11756 if (abort_merge) {
11757 if (!merge_in_progress) {
11758 error = got_error(GOT_ERR_NOT_MERGING);
11759 goto done;
11761 error = got_worktree_merge_continue(&branch_name,
11762 &branch_tip, &fileindex, worktree, repo);
11763 if (error)
11764 goto done;
11765 error = got_worktree_merge_abort(worktree, fileindex, repo,
11766 abort_progress, &upa);
11767 if (error)
11768 goto done;
11769 printf("Merge of %s aborted\n", branch_name);
11770 goto done; /* nothing else to do */
11773 error = get_author(&author, repo, worktree);
11774 if (error)
11775 goto done;
11777 if (continue_merge) {
11778 if (!merge_in_progress) {
11779 error = got_error(GOT_ERR_NOT_MERGING);
11780 goto done;
11782 error = got_worktree_merge_continue(&branch_name,
11783 &branch_tip, &fileindex, worktree, repo);
11784 if (error)
11785 goto done;
11786 } else {
11787 error = got_ref_open(&branch, repo, argv[0], 0);
11788 if (error != NULL)
11789 goto done;
11790 branch_name = strdup(got_ref_get_name(branch));
11791 if (branch_name == NULL) {
11792 error = got_error_from_errno("strdup");
11793 goto done;
11795 error = got_ref_resolve(&branch_tip, repo, branch);
11796 if (error)
11797 goto done;
11800 error = got_ref_open(&wt_branch, repo,
11801 got_worktree_get_head_ref_name(worktree), 0);
11802 if (error)
11803 goto done;
11804 error = got_ref_resolve(&wt_branch_tip, repo, wt_branch);
11805 if (error)
11806 goto done;
11807 error = got_commit_graph_find_youngest_common_ancestor(&yca_id,
11808 wt_branch_tip, branch_tip, 0, repo,
11809 check_cancelled, NULL);
11810 if (error && error->code != GOT_ERR_ANCESTRY)
11811 goto done;
11813 if (!continue_merge) {
11814 error = check_path_prefix(wt_branch_tip, branch_tip,
11815 got_worktree_get_path_prefix(worktree),
11816 GOT_ERR_MERGE_PATH, repo);
11817 if (error)
11818 goto done;
11819 if (yca_id) {
11820 error = check_same_branch(wt_branch_tip, branch,
11821 yca_id, repo);
11822 if (error) {
11823 if (error->code != GOT_ERR_ANCESTRY)
11824 goto done;
11825 error = NULL;
11826 } else {
11827 static char msg[512];
11828 snprintf(msg, sizeof(msg),
11829 "cannot create a merge commit because "
11830 "%s is based on %s; %s can be integrated "
11831 "with 'got integrate' instead", branch_name,
11832 got_worktree_get_head_ref_name(worktree),
11833 branch_name);
11834 error = got_error_msg(GOT_ERR_SAME_BRANCH, msg);
11835 goto done;
11838 error = got_worktree_merge_prepare(&fileindex, worktree,
11839 branch, repo);
11840 if (error)
11841 goto done;
11843 error = got_worktree_merge_branch(worktree, fileindex,
11844 yca_id, branch_tip, repo, update_progress, &upa,
11845 check_cancelled, NULL);
11846 if (error)
11847 goto done;
11848 print_merge_progress_stats(&upa);
11849 if (!upa.did_something) {
11850 error = got_worktree_merge_abort(worktree, fileindex,
11851 repo, abort_progress, &upa);
11852 if (error)
11853 goto done;
11854 printf("Already up-to-date\n");
11855 goto done;
11859 if (interrupt_merge) {
11860 error = got_worktree_merge_postpone(worktree, fileindex);
11861 if (error)
11862 goto done;
11863 printf("Merge of %s interrupted on request\n", branch_name);
11864 } else if (upa.conflicts > 0 || upa.missing > 0 ||
11865 upa.not_deleted > 0 || upa.unversioned > 0) {
11866 error = got_worktree_merge_postpone(worktree, fileindex);
11867 if (error)
11868 goto done;
11869 if (upa.conflicts > 0 && upa.missing == 0 &&
11870 upa.not_deleted == 0 && upa.unversioned == 0) {
11871 error = got_error_msg(GOT_ERR_CONFLICTS,
11872 "conflicts must be resolved before merging "
11873 "can continue");
11874 } else if (upa.conflicts > 0) {
11875 error = got_error_msg(GOT_ERR_CONFLICTS,
11876 "conflicts must be resolved before merging "
11877 "can continue; changes destined for some "
11878 "files were not yet merged and "
11879 "should be merged manually if required before the "
11880 "merge operation is continued");
11881 } else {
11882 error = got_error_msg(GOT_ERR_CONFLICTS,
11883 "changes destined for some "
11884 "files were not yet merged and should be "
11885 "merged manually if required before the "
11886 "merge operation is continued");
11888 goto done;
11889 } else {
11890 error = got_worktree_merge_commit(&merge_commit_id, worktree,
11891 fileindex, author, NULL, 1, branch_tip, branch_name,
11892 repo, continue_merge ? print_status : NULL, NULL);
11893 if (error)
11894 goto done;
11895 error = got_worktree_merge_complete(worktree, fileindex, repo);
11896 if (error)
11897 goto done;
11898 error = got_object_id_str(&id_str, merge_commit_id);
11899 if (error)
11900 goto done;
11901 printf("Merged %s into %s: %s\n", branch_name,
11902 got_worktree_get_head_ref_name(worktree),
11903 id_str);
11906 done:
11907 free(id_str);
11908 free(merge_commit_id);
11909 free(author);
11910 free(branch_tip);
11911 free(branch_name);
11912 free(yca_id);
11913 if (branch)
11914 got_ref_close(branch);
11915 if (wt_branch)
11916 got_ref_close(wt_branch);
11917 if (worktree)
11918 got_worktree_close(worktree);
11919 if (repo) {
11920 const struct got_error *close_err = got_repo_close(repo);
11921 if (error == NULL)
11922 error = close_err;
11924 if (pack_fds) {
11925 const struct got_error *pack_err =
11926 got_repo_pack_fds_close(pack_fds);
11927 if (error == NULL)
11928 error = pack_err;
11930 return error;
11933 __dead static void
11934 usage_stage(void)
11936 fprintf(stderr, "usage: %s stage [-l] | [-p] [-F response-script] "
11937 "[-S] [file-path ...]\n",
11938 getprogname());
11939 exit(1);
11942 static const struct got_error *
11943 print_stage(void *arg, unsigned char status, unsigned char staged_status,
11944 const char *path, struct got_object_id *blob_id,
11945 struct got_object_id *staged_blob_id, struct got_object_id *commit_id,
11946 int dirfd, const char *de_name)
11948 const struct got_error *err = NULL;
11949 char *id_str = NULL;
11951 if (staged_status != GOT_STATUS_ADD &&
11952 staged_status != GOT_STATUS_MODIFY &&
11953 staged_status != GOT_STATUS_DELETE)
11954 return NULL;
11956 if (staged_status == GOT_STATUS_ADD ||
11957 staged_status == GOT_STATUS_MODIFY)
11958 err = got_object_id_str(&id_str, staged_blob_id);
11959 else
11960 err = got_object_id_str(&id_str, blob_id);
11961 if (err)
11962 return err;
11964 printf("%s %c %s\n", id_str, staged_status, path);
11965 free(id_str);
11966 return NULL;
11969 static const struct got_error *
11970 cmd_stage(int argc, char *argv[])
11972 const struct got_error *error = NULL;
11973 struct got_repository *repo = NULL;
11974 struct got_worktree *worktree = NULL;
11975 char *cwd = NULL;
11976 struct got_pathlist_head paths;
11977 struct got_pathlist_entry *pe;
11978 int ch, list_stage = 0, pflag = 0, allow_bad_symlinks = 0;
11979 FILE *patch_script_file = NULL;
11980 const char *patch_script_path = NULL;
11981 struct choose_patch_arg cpa;
11982 int *pack_fds = NULL;
11984 TAILQ_INIT(&paths);
11986 while ((ch = getopt(argc, argv, "lpF:S")) != -1) {
11987 switch (ch) {
11988 case 'l':
11989 list_stage = 1;
11990 break;
11991 case 'p':
11992 pflag = 1;
11993 break;
11994 case 'F':
11995 patch_script_path = optarg;
11996 break;
11997 case 'S':
11998 allow_bad_symlinks = 1;
11999 break;
12000 default:
12001 usage_stage();
12002 /* NOTREACHED */
12006 argc -= optind;
12007 argv += optind;
12009 #ifndef PROFILE
12010 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
12011 "unveil", NULL) == -1)
12012 err(1, "pledge");
12013 #endif
12014 if (list_stage && (pflag || patch_script_path))
12015 errx(1, "-l option cannot be used with other options");
12016 if (patch_script_path && !pflag)
12017 errx(1, "-F option can only be used together with -p option");
12019 cwd = getcwd(NULL, 0);
12020 if (cwd == NULL) {
12021 error = got_error_from_errno("getcwd");
12022 goto done;
12025 error = got_repo_pack_fds_open(&pack_fds);
12026 if (error != NULL)
12027 goto done;
12029 error = got_worktree_open(&worktree, cwd);
12030 if (error) {
12031 if (error->code == GOT_ERR_NOT_WORKTREE)
12032 error = wrap_not_worktree_error(error, "stage", cwd);
12033 goto done;
12036 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
12037 NULL, pack_fds);
12038 if (error != NULL)
12039 goto done;
12041 if (patch_script_path) {
12042 patch_script_file = fopen(patch_script_path, "re");
12043 if (patch_script_file == NULL) {
12044 error = got_error_from_errno2("fopen",
12045 patch_script_path);
12046 goto done;
12049 error = apply_unveil(got_repo_get_path(repo), 0,
12050 got_worktree_get_root_path(worktree));
12051 if (error)
12052 goto done;
12054 error = check_merge_in_progress(worktree, repo);
12055 if (error)
12056 goto done;
12058 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
12059 if (error)
12060 goto done;
12062 if (list_stage)
12063 error = got_worktree_status(worktree, &paths, repo, 0,
12064 print_stage, NULL, check_cancelled, NULL);
12065 else {
12066 cpa.patch_script_file = patch_script_file;
12067 cpa.action = "stage";
12068 error = got_worktree_stage(worktree, &paths,
12069 pflag ? NULL : print_status, NULL,
12070 pflag ? choose_patch : NULL, &cpa,
12071 allow_bad_symlinks, repo);
12073 done:
12074 if (patch_script_file && fclose(patch_script_file) == EOF &&
12075 error == NULL)
12076 error = got_error_from_errno2("fclose", patch_script_path);
12077 if (repo) {
12078 const struct got_error *close_err = got_repo_close(repo);
12079 if (error == NULL)
12080 error = close_err;
12082 if (worktree)
12083 got_worktree_close(worktree);
12084 if (pack_fds) {
12085 const struct got_error *pack_err =
12086 got_repo_pack_fds_close(pack_fds);
12087 if (error == NULL)
12088 error = pack_err;
12090 TAILQ_FOREACH(pe, &paths, entry)
12091 free((char *)pe->path);
12092 got_pathlist_free(&paths);
12093 free(cwd);
12094 return error;
12097 __dead static void
12098 usage_unstage(void)
12100 fprintf(stderr, "usage: %s unstage [-p] [-F response-script] "
12101 "[file-path ...]\n",
12102 getprogname());
12103 exit(1);
12107 static const struct got_error *
12108 cmd_unstage(int argc, char *argv[])
12110 const struct got_error *error = NULL;
12111 struct got_repository *repo = NULL;
12112 struct got_worktree *worktree = NULL;
12113 char *cwd = NULL;
12114 struct got_pathlist_head paths;
12115 struct got_pathlist_entry *pe;
12116 int ch, pflag = 0;
12117 struct got_update_progress_arg upa;
12118 FILE *patch_script_file = NULL;
12119 const char *patch_script_path = NULL;
12120 struct choose_patch_arg cpa;
12121 int *pack_fds = NULL;
12123 TAILQ_INIT(&paths);
12125 while ((ch = getopt(argc, argv, "pF:")) != -1) {
12126 switch (ch) {
12127 case 'p':
12128 pflag = 1;
12129 break;
12130 case 'F':
12131 patch_script_path = optarg;
12132 break;
12133 default:
12134 usage_unstage();
12135 /* NOTREACHED */
12139 argc -= optind;
12140 argv += optind;
12142 #ifndef PROFILE
12143 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
12144 "unveil", NULL) == -1)
12145 err(1, "pledge");
12146 #endif
12147 if (patch_script_path && !pflag)
12148 errx(1, "-F option can only be used together with -p option");
12150 cwd = getcwd(NULL, 0);
12151 if (cwd == NULL) {
12152 error = got_error_from_errno("getcwd");
12153 goto done;
12156 error = got_repo_pack_fds_open(&pack_fds);
12157 if (error != NULL)
12158 goto done;
12160 error = got_worktree_open(&worktree, cwd);
12161 if (error) {
12162 if (error->code == GOT_ERR_NOT_WORKTREE)
12163 error = wrap_not_worktree_error(error, "unstage", cwd);
12164 goto done;
12167 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
12168 NULL, pack_fds);
12169 if (error != NULL)
12170 goto done;
12172 if (patch_script_path) {
12173 patch_script_file = fopen(patch_script_path, "re");
12174 if (patch_script_file == NULL) {
12175 error = got_error_from_errno2("fopen",
12176 patch_script_path);
12177 goto done;
12181 error = apply_unveil(got_repo_get_path(repo), 0,
12182 got_worktree_get_root_path(worktree));
12183 if (error)
12184 goto done;
12186 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
12187 if (error)
12188 goto done;
12190 cpa.patch_script_file = patch_script_file;
12191 cpa.action = "unstage";
12192 memset(&upa, 0, sizeof(upa));
12193 error = got_worktree_unstage(worktree, &paths, update_progress,
12194 &upa, pflag ? choose_patch : NULL, &cpa, repo);
12195 if (!error)
12196 print_merge_progress_stats(&upa);
12197 done:
12198 if (patch_script_file && fclose(patch_script_file) == EOF &&
12199 error == NULL)
12200 error = got_error_from_errno2("fclose", patch_script_path);
12201 if (repo) {
12202 const struct got_error *close_err = got_repo_close(repo);
12203 if (error == NULL)
12204 error = close_err;
12206 if (worktree)
12207 got_worktree_close(worktree);
12208 if (pack_fds) {
12209 const struct got_error *pack_err =
12210 got_repo_pack_fds_close(pack_fds);
12211 if (error == NULL)
12212 error = pack_err;
12214 TAILQ_FOREACH(pe, &paths, entry)
12215 free((char *)pe->path);
12216 got_pathlist_free(&paths);
12217 free(cwd);
12218 return error;
12221 __dead static void
12222 usage_cat(void)
12224 fprintf(stderr, "usage: %s cat [-r repository ] [ -c commit ] [ -P ] "
12225 "arg1 [arg2 ...]\n", getprogname());
12226 exit(1);
12229 static const struct got_error *
12230 cat_blob(struct got_object_id *id, struct got_repository *repo, FILE *outfile)
12232 const struct got_error *err;
12233 struct got_blob_object *blob;
12235 err = got_object_open_as_blob(&blob, repo, id, 8192);
12236 if (err)
12237 return err;
12239 err = got_object_blob_dump_to_file(NULL, NULL, NULL, outfile, blob);
12240 got_object_blob_close(blob);
12241 return err;
12244 static const struct got_error *
12245 cat_tree(struct got_object_id *id, struct got_repository *repo, FILE *outfile)
12247 const struct got_error *err;
12248 struct got_tree_object *tree;
12249 int nentries, i;
12251 err = got_object_open_as_tree(&tree, repo, id);
12252 if (err)
12253 return err;
12255 nentries = got_object_tree_get_nentries(tree);
12256 for (i = 0; i < nentries; i++) {
12257 struct got_tree_entry *te;
12258 char *id_str;
12259 if (sigint_received || sigpipe_received)
12260 break;
12261 te = got_object_tree_get_entry(tree, i);
12262 err = got_object_id_str(&id_str, got_tree_entry_get_id(te));
12263 if (err)
12264 break;
12265 fprintf(outfile, "%s %.7o %s\n", id_str,
12266 got_tree_entry_get_mode(te),
12267 got_tree_entry_get_name(te));
12268 free(id_str);
12271 got_object_tree_close(tree);
12272 return err;
12275 static void
12276 format_gmtoff(char *buf, size_t sz, time_t gmtoff)
12278 long long h, m;
12279 char sign = '+';
12281 if (gmtoff < 0) {
12282 sign = '-';
12283 gmtoff = -gmtoff;
12286 h = (long long)gmtoff / 3600;
12287 m = ((long long)gmtoff - h*3600) / 60;
12288 snprintf(buf, sz, "%c%02lld%02lld", sign, h, m);
12291 static const struct got_error *
12292 cat_commit(struct got_object_id *id, struct got_repository *repo, FILE *outfile)
12294 const struct got_error *err;
12295 struct got_commit_object *commit;
12296 const struct got_object_id_queue *parent_ids;
12297 struct got_object_qid *pid;
12298 char *id_str = NULL;
12299 const char *logmsg = NULL;
12300 char gmtoff[6];
12302 err = got_object_open_as_commit(&commit, repo, id);
12303 if (err)
12304 return err;
12306 err = got_object_id_str(&id_str, got_object_commit_get_tree_id(commit));
12307 if (err)
12308 goto done;
12310 fprintf(outfile, "%s%s\n", GOT_COMMIT_LABEL_TREE, id_str);
12311 parent_ids = got_object_commit_get_parent_ids(commit);
12312 fprintf(outfile, "numparents %d\n",
12313 got_object_commit_get_nparents(commit));
12314 STAILQ_FOREACH(pid, parent_ids, entry) {
12315 char *pid_str;
12316 err = got_object_id_str(&pid_str, &pid->id);
12317 if (err)
12318 goto done;
12319 fprintf(outfile, "%s%s\n", GOT_COMMIT_LABEL_PARENT, pid_str);
12320 free(pid_str);
12322 format_gmtoff(gmtoff, sizeof(gmtoff),
12323 got_object_commit_get_author_gmtoff(commit));
12324 fprintf(outfile, "%s%s %lld %s\n", GOT_COMMIT_LABEL_AUTHOR,
12325 got_object_commit_get_author(commit),
12326 (long long)got_object_commit_get_author_time(commit),
12327 gmtoff);
12329 format_gmtoff(gmtoff, sizeof(gmtoff),
12330 got_object_commit_get_committer_gmtoff(commit));
12331 fprintf(outfile, "%s%s %lld %s\n", GOT_COMMIT_LABEL_COMMITTER,
12332 got_object_commit_get_author(commit),
12333 (long long)got_object_commit_get_committer_time(commit),
12334 gmtoff);
12336 logmsg = got_object_commit_get_logmsg_raw(commit);
12337 fprintf(outfile, "messagelen %zd\n", strlen(logmsg));
12338 fprintf(outfile, "%s", logmsg);
12339 done:
12340 free(id_str);
12341 got_object_commit_close(commit);
12342 return err;
12345 static const struct got_error *
12346 cat_tag(struct got_object_id *id, struct got_repository *repo, FILE *outfile)
12348 const struct got_error *err;
12349 struct got_tag_object *tag;
12350 char *id_str = NULL;
12351 const char *tagmsg = NULL;
12352 char gmtoff[6];
12354 err = got_object_open_as_tag(&tag, repo, id);
12355 if (err)
12356 return err;
12358 err = got_object_id_str(&id_str, got_object_tag_get_object_id(tag));
12359 if (err)
12360 goto done;
12362 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_OBJECT, id_str);
12364 switch (got_object_tag_get_object_type(tag)) {
12365 case GOT_OBJ_TYPE_BLOB:
12366 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TYPE,
12367 GOT_OBJ_LABEL_BLOB);
12368 break;
12369 case GOT_OBJ_TYPE_TREE:
12370 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TYPE,
12371 GOT_OBJ_LABEL_TREE);
12372 break;
12373 case GOT_OBJ_TYPE_COMMIT:
12374 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TYPE,
12375 GOT_OBJ_LABEL_COMMIT);
12376 break;
12377 case GOT_OBJ_TYPE_TAG:
12378 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TYPE,
12379 GOT_OBJ_LABEL_TAG);
12380 break;
12381 default:
12382 break;
12385 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TAG,
12386 got_object_tag_get_name(tag));
12388 format_gmtoff(gmtoff, sizeof(gmtoff),
12389 got_object_tag_get_tagger_gmtoff(tag));
12390 fprintf(outfile, "%s%s %lld %s\n", GOT_TAG_LABEL_TAGGER,
12391 got_object_tag_get_tagger(tag),
12392 (long long)got_object_tag_get_tagger_time(tag),
12393 gmtoff);
12395 tagmsg = got_object_tag_get_message(tag);
12396 fprintf(outfile, "messagelen %zd\n", strlen(tagmsg));
12397 fprintf(outfile, "%s", tagmsg);
12398 done:
12399 free(id_str);
12400 got_object_tag_close(tag);
12401 return err;
12404 static const struct got_error *
12405 cmd_cat(int argc, char *argv[])
12407 const struct got_error *error;
12408 struct got_repository *repo = NULL;
12409 struct got_worktree *worktree = NULL;
12410 char *cwd = NULL, *repo_path = NULL, *label = NULL;
12411 const char *commit_id_str = NULL;
12412 struct got_object_id *id = NULL, *commit_id = NULL;
12413 struct got_commit_object *commit = NULL;
12414 int ch, obj_type, i, force_path = 0;
12415 struct got_reflist_head refs;
12416 int *pack_fds = NULL;
12418 TAILQ_INIT(&refs);
12420 #ifndef PROFILE
12421 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
12422 NULL) == -1)
12423 err(1, "pledge");
12424 #endif
12426 while ((ch = getopt(argc, argv, "c:r:P")) != -1) {
12427 switch (ch) {
12428 case 'c':
12429 commit_id_str = optarg;
12430 break;
12431 case 'r':
12432 repo_path = realpath(optarg, NULL);
12433 if (repo_path == NULL)
12434 return got_error_from_errno2("realpath",
12435 optarg);
12436 got_path_strip_trailing_slashes(repo_path);
12437 break;
12438 case 'P':
12439 force_path = 1;
12440 break;
12441 default:
12442 usage_cat();
12443 /* NOTREACHED */
12447 argc -= optind;
12448 argv += optind;
12450 cwd = getcwd(NULL, 0);
12451 if (cwd == NULL) {
12452 error = got_error_from_errno("getcwd");
12453 goto done;
12456 error = got_repo_pack_fds_open(&pack_fds);
12457 if (error != NULL)
12458 goto done;
12460 if (repo_path == NULL) {
12461 error = got_worktree_open(&worktree, cwd);
12462 if (error && error->code != GOT_ERR_NOT_WORKTREE)
12463 goto done;
12464 if (worktree) {
12465 repo_path = strdup(
12466 got_worktree_get_repo_path(worktree));
12467 if (repo_path == NULL) {
12468 error = got_error_from_errno("strdup");
12469 goto done;
12472 /* Release work tree lock. */
12473 got_worktree_close(worktree);
12474 worktree = NULL;
12478 if (repo_path == NULL) {
12479 repo_path = strdup(cwd);
12480 if (repo_path == NULL)
12481 return got_error_from_errno("strdup");
12484 error = got_repo_open(&repo, repo_path, NULL, pack_fds);
12485 free(repo_path);
12486 if (error != NULL)
12487 goto done;
12489 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
12490 if (error)
12491 goto done;
12493 error = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name, NULL);
12494 if (error)
12495 goto done;
12497 if (commit_id_str == NULL)
12498 commit_id_str = GOT_REF_HEAD;
12499 error = got_repo_match_object_id(&commit_id, NULL,
12500 commit_id_str, GOT_OBJ_TYPE_COMMIT, &refs, repo);
12501 if (error)
12502 goto done;
12504 error = got_object_open_as_commit(&commit, repo, commit_id);
12505 if (error)
12506 goto done;
12508 for (i = 0; i < argc; i++) {
12509 if (force_path) {
12510 error = got_object_id_by_path(&id, repo, commit,
12511 argv[i]);
12512 if (error)
12513 break;
12514 } else {
12515 error = got_repo_match_object_id(&id, &label, argv[i],
12516 GOT_OBJ_TYPE_ANY, NULL /* do not resolve tags */,
12517 repo);
12518 if (error) {
12519 if (error->code != GOT_ERR_BAD_OBJ_ID_STR &&
12520 error->code != GOT_ERR_NOT_REF)
12521 break;
12522 error = got_object_id_by_path(&id, repo,
12523 commit, argv[i]);
12524 if (error)
12525 break;
12529 error = got_object_get_type(&obj_type, repo, id);
12530 if (error)
12531 break;
12533 switch (obj_type) {
12534 case GOT_OBJ_TYPE_BLOB:
12535 error = cat_blob(id, repo, stdout);
12536 break;
12537 case GOT_OBJ_TYPE_TREE:
12538 error = cat_tree(id, repo, stdout);
12539 break;
12540 case GOT_OBJ_TYPE_COMMIT:
12541 error = cat_commit(id, repo, stdout);
12542 break;
12543 case GOT_OBJ_TYPE_TAG:
12544 error = cat_tag(id, repo, stdout);
12545 break;
12546 default:
12547 error = got_error(GOT_ERR_OBJ_TYPE);
12548 break;
12550 if (error)
12551 break;
12552 free(label);
12553 label = NULL;
12554 free(id);
12555 id = NULL;
12557 done:
12558 free(label);
12559 free(id);
12560 free(commit_id);
12561 if (commit)
12562 got_object_commit_close(commit);
12563 if (worktree)
12564 got_worktree_close(worktree);
12565 if (repo) {
12566 const struct got_error *close_err = got_repo_close(repo);
12567 if (error == NULL)
12568 error = close_err;
12570 if (pack_fds) {
12571 const struct got_error *pack_err =
12572 got_repo_pack_fds_close(pack_fds);
12573 if (error == NULL)
12574 error = pack_err;
12577 got_ref_list_free(&refs);
12578 return error;
12581 __dead static void
12582 usage_info(void)
12584 fprintf(stderr, "usage: %s info [path ...]\n",
12585 getprogname());
12586 exit(1);
12589 static const struct got_error *
12590 print_path_info(void *arg, const char *path, mode_t mode, time_t mtime,
12591 struct got_object_id *blob_id, struct got_object_id *staged_blob_id,
12592 struct got_object_id *commit_id)
12594 const struct got_error *err = NULL;
12595 char *id_str = NULL;
12596 char datebuf[128];
12597 struct tm mytm, *tm;
12598 struct got_pathlist_head *paths = arg;
12599 struct got_pathlist_entry *pe;
12602 * Clear error indication from any of the path arguments which
12603 * would cause this file index entry to be displayed.
12605 TAILQ_FOREACH(pe, paths, entry) {
12606 if (got_path_cmp(path, pe->path, strlen(path),
12607 pe->path_len) == 0 ||
12608 got_path_is_child(path, pe->path, pe->path_len))
12609 pe->data = NULL; /* no error */
12612 printf(GOT_COMMIT_SEP_STR);
12613 if (S_ISLNK(mode))
12614 printf("symlink: %s\n", path);
12615 else if (S_ISREG(mode)) {
12616 printf("file: %s\n", path);
12617 printf("mode: %o\n", mode & (S_IRWXU | S_IRWXG | S_IRWXO));
12618 } else if (S_ISDIR(mode))
12619 printf("directory: %s\n", path);
12620 else
12621 printf("something: %s\n", path);
12623 tm = localtime_r(&mtime, &mytm);
12624 if (tm == NULL)
12625 return NULL;
12626 if (strftime(datebuf, sizeof(datebuf), "%c %Z", tm) == 0)
12627 return got_error(GOT_ERR_NO_SPACE);
12628 printf("timestamp: %s\n", datebuf);
12630 if (blob_id) {
12631 err = got_object_id_str(&id_str, blob_id);
12632 if (err)
12633 return err;
12634 printf("based on blob: %s\n", id_str);
12635 free(id_str);
12638 if (staged_blob_id) {
12639 err = got_object_id_str(&id_str, staged_blob_id);
12640 if (err)
12641 return err;
12642 printf("based on staged blob: %s\n", id_str);
12643 free(id_str);
12646 if (commit_id) {
12647 err = got_object_id_str(&id_str, commit_id);
12648 if (err)
12649 return err;
12650 printf("based on commit: %s\n", id_str);
12651 free(id_str);
12654 return NULL;
12657 static const struct got_error *
12658 cmd_info(int argc, char *argv[])
12660 const struct got_error *error = NULL;
12661 struct got_worktree *worktree = NULL;
12662 char *cwd = NULL, *id_str = NULL;
12663 struct got_pathlist_head paths;
12664 struct got_pathlist_entry *pe;
12665 char *uuidstr = NULL;
12666 int ch, show_files = 0;
12667 int *pack_fds = NULL;
12669 TAILQ_INIT(&paths);
12671 while ((ch = getopt(argc, argv, "")) != -1) {
12672 switch (ch) {
12673 default:
12674 usage_info();
12675 /* NOTREACHED */
12679 argc -= optind;
12680 argv += optind;
12682 #ifndef PROFILE
12683 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
12684 NULL) == -1)
12685 err(1, "pledge");
12686 #endif
12687 cwd = getcwd(NULL, 0);
12688 if (cwd == NULL) {
12689 error = got_error_from_errno("getcwd");
12690 goto done;
12693 error = got_repo_pack_fds_open(&pack_fds);
12694 if (error != NULL)
12695 goto done;
12697 error = got_worktree_open(&worktree, cwd);
12698 if (error) {
12699 if (error->code == GOT_ERR_NOT_WORKTREE)
12700 error = wrap_not_worktree_error(error, "info", cwd);
12701 goto done;
12704 #ifndef PROFILE
12705 /* Remove "cpath" promise. */
12706 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
12707 NULL) == -1)
12708 err(1, "pledge");
12709 #endif
12710 error = apply_unveil(NULL, 0, got_worktree_get_root_path(worktree));
12711 if (error)
12712 goto done;
12714 if (argc >= 1) {
12715 error = get_worktree_paths_from_argv(&paths, argc, argv,
12716 worktree);
12717 if (error)
12718 goto done;
12719 show_files = 1;
12722 error = got_object_id_str(&id_str,
12723 got_worktree_get_base_commit_id(worktree));
12724 if (error)
12725 goto done;
12727 error = got_worktree_get_uuid(&uuidstr, worktree);
12728 if (error)
12729 goto done;
12731 printf("work tree: %s\n", got_worktree_get_root_path(worktree));
12732 printf("work tree base commit: %s\n", id_str);
12733 printf("work tree path prefix: %s\n",
12734 got_worktree_get_path_prefix(worktree));
12735 printf("work tree branch reference: %s\n",
12736 got_worktree_get_head_ref_name(worktree));
12737 printf("work tree UUID: %s\n", uuidstr);
12738 printf("repository: %s\n", got_worktree_get_repo_path(worktree));
12740 if (show_files) {
12741 struct got_pathlist_entry *pe;
12742 TAILQ_FOREACH(pe, &paths, entry) {
12743 if (pe->path_len == 0)
12744 continue;
12746 * Assume this path will fail. This will be corrected
12747 * in print_path_info() in case the path does suceeed.
12749 pe->data = (void *)got_error_path(pe->path,
12750 GOT_ERR_BAD_PATH);
12752 error = got_worktree_path_info(worktree, &paths,
12753 print_path_info, &paths, check_cancelled, NULL);
12754 if (error)
12755 goto done;
12756 TAILQ_FOREACH(pe, &paths, entry) {
12757 if (pe->data != NULL) {
12758 error = pe->data; /* bad path */
12759 break;
12763 done:
12764 if (pack_fds) {
12765 const struct got_error *pack_err =
12766 got_repo_pack_fds_close(pack_fds);
12767 if (error == NULL)
12768 error = pack_err;
12770 TAILQ_FOREACH(pe, &paths, entry)
12771 free((char *)pe->path);
12772 got_pathlist_free(&paths);
12773 free(cwd);
12774 free(id_str);
12775 free(uuidstr);
12776 return error;