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/time.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <sys/wait.h>
25 #include <err.h>
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <limits.h>
29 #include <locale.h>
30 #include <ctype.h>
31 #include <sha1.h>
32 #include <sha2.h>
33 #include <signal.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <libgen.h>
39 #include <time.h>
40 #include <paths.h>
41 #include <regex.h>
42 #include <getopt.h>
43 #include <util.h>
45 #include "got_version.h"
46 #include "got_error.h"
47 #include "got_object.h"
48 #include "got_reference.h"
49 #include "got_repository.h"
50 #include "got_path.h"
51 #include "got_cancel.h"
52 #include "got_worktree.h"
53 #include "got_diff.h"
54 #include "got_commit_graph.h"
55 #include "got_fetch.h"
56 #include "got_send.h"
57 #include "got_blame.h"
58 #include "got_privsep.h"
59 #include "got_opentemp.h"
60 #include "got_gotconfig.h"
61 #include "got_dial.h"
62 #include "got_patch.h"
63 #include "got_sigs.h"
64 #include "got_date.h"
66 #ifndef nitems
67 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
68 #endif
70 static volatile sig_atomic_t sigint_received;
71 static volatile sig_atomic_t sigpipe_received;
73 static void
74 catch_sigint(int signo)
75 {
76 sigint_received = 1;
77 }
79 static void
80 catch_sigpipe(int signo)
81 {
82 sigpipe_received = 1;
83 }
86 struct got_cmd {
87 const char *cmd_name;
88 const struct got_error *(*cmd_main)(int, char *[]);
89 void (*cmd_usage)(void);
90 const char *cmd_alias;
91 };
93 __dead static void usage(int, int);
94 __dead static void usage_import(void);
95 __dead static void usage_clone(void);
96 __dead static void usage_fetch(void);
97 __dead static void usage_checkout(void);
98 __dead static void usage_update(void);
99 __dead static void usage_log(void);
100 __dead static void usage_diff(void);
101 __dead static void usage_blame(void);
102 __dead static void usage_tree(void);
103 __dead static void usage_status(void);
104 __dead static void usage_ref(void);
105 __dead static void usage_branch(void);
106 __dead static void usage_tag(void);
107 __dead static void usage_add(void);
108 __dead static void usage_remove(void);
109 __dead static void usage_patch(void);
110 __dead static void usage_revert(void);
111 __dead static void usage_commit(void);
112 __dead static void usage_send(void);
113 __dead static void usage_cherrypick(void);
114 __dead static void usage_backout(void);
115 __dead static void usage_rebase(void);
116 __dead static void usage_histedit(void);
117 __dead static void usage_integrate(void);
118 __dead static void usage_merge(void);
119 __dead static void usage_stage(void);
120 __dead static void usage_unstage(void);
121 __dead static void usage_cat(void);
122 __dead static void usage_info(void);
124 static const struct got_error* cmd_import(int, char *[]);
125 static const struct got_error* cmd_clone(int, char *[]);
126 static const struct got_error* cmd_fetch(int, char *[]);
127 static const struct got_error* cmd_checkout(int, char *[]);
128 static const struct got_error* cmd_update(int, char *[]);
129 static const struct got_error* cmd_log(int, char *[]);
130 static const struct got_error* cmd_diff(int, char *[]);
131 static const struct got_error* cmd_blame(int, char *[]);
132 static const struct got_error* cmd_tree(int, char *[]);
133 static const struct got_error* cmd_status(int, char *[]);
134 static const struct got_error* cmd_ref(int, char *[]);
135 static const struct got_error* cmd_branch(int, char *[]);
136 static const struct got_error* cmd_tag(int, char *[]);
137 static const struct got_error* cmd_add(int, char *[]);
138 static const struct got_error* cmd_remove(int, char *[]);
139 static const struct got_error* cmd_patch(int, char *[]);
140 static const struct got_error* cmd_revert(int, char *[]);
141 static const struct got_error* cmd_commit(int, char *[]);
142 static const struct got_error* cmd_send(int, char *[]);
143 static const struct got_error* cmd_cherrypick(int, char *[]);
144 static const struct got_error* cmd_backout(int, char *[]);
145 static const struct got_error* cmd_rebase(int, char *[]);
146 static const struct got_error* cmd_histedit(int, char *[]);
147 static const struct got_error* cmd_integrate(int, char *[]);
148 static const struct got_error* cmd_merge(int, char *[]);
149 static const struct got_error* cmd_stage(int, char *[]);
150 static const struct got_error* cmd_unstage(int, char *[]);
151 static const struct got_error* cmd_cat(int, char *[]);
152 static const struct got_error* cmd_info(int, char *[]);
154 static const struct got_cmd got_commands[] = {
155 { "import", cmd_import, usage_import, "im" },
156 { "clone", cmd_clone, usage_clone, "cl" },
157 { "fetch", cmd_fetch, usage_fetch, "fe" },
158 { "checkout", cmd_checkout, usage_checkout, "co" },
159 { "update", cmd_update, usage_update, "up" },
160 { "log", cmd_log, usage_log, "" },
161 { "diff", cmd_diff, usage_diff, "di" },
162 { "blame", cmd_blame, usage_blame, "bl" },
163 { "tree", cmd_tree, usage_tree, "tr" },
164 { "status", cmd_status, usage_status, "st" },
165 { "ref", cmd_ref, usage_ref, "" },
166 { "branch", cmd_branch, usage_branch, "br" },
167 { "tag", cmd_tag, usage_tag, "" },
168 { "add", cmd_add, usage_add, "" },
169 { "remove", cmd_remove, usage_remove, "rm" },
170 { "patch", cmd_patch, usage_patch, "pa" },
171 { "revert", cmd_revert, usage_revert, "rv" },
172 { "commit", cmd_commit, usage_commit, "ci" },
173 { "send", cmd_send, usage_send, "se" },
174 { "cherrypick", cmd_cherrypick, usage_cherrypick, "cy" },
175 { "backout", cmd_backout, usage_backout, "bo" },
176 { "rebase", cmd_rebase, usage_rebase, "rb" },
177 { "histedit", cmd_histedit, usage_histedit, "he" },
178 { "integrate", cmd_integrate, usage_integrate,"ig" },
179 { "merge", cmd_merge, usage_merge, "mg" },
180 { "stage", cmd_stage, usage_stage, "sg" },
181 { "unstage", cmd_unstage, usage_unstage, "ug" },
182 { "cat", cmd_cat, usage_cat, "" },
183 { "info", cmd_info, usage_info, "" },
184 };
186 static void
187 list_commands(FILE *fp)
189 size_t i;
191 fprintf(fp, "commands:");
192 for (i = 0; i < nitems(got_commands); i++) {
193 const struct got_cmd *cmd = &got_commands[i];
194 fprintf(fp, " %s", cmd->cmd_name);
196 fputc('\n', fp);
199 __dead static void
200 option_conflict(char a, char b)
202 errx(1, "-%c and -%c options are mutually exclusive", a, b);
205 int
206 main(int argc, char *argv[])
208 const struct got_cmd *cmd;
209 size_t i;
210 int ch;
211 int hflag = 0, Vflag = 0;
212 static const struct option longopts[] = {
213 { "version", no_argument, NULL, 'V' },
214 { NULL, 0, NULL, 0 }
215 };
217 setlocale(LC_CTYPE, "");
219 while ((ch = getopt_long(argc, argv, "+hV", longopts, NULL)) != -1) {
220 switch (ch) {
221 case 'h':
222 hflag = 1;
223 break;
224 case 'V':
225 Vflag = 1;
226 break;
227 default:
228 usage(hflag, 1);
229 /* NOTREACHED */
233 argc -= optind;
234 argv += optind;
235 optind = 1;
236 optreset = 1;
238 if (Vflag) {
239 got_version_print_str();
240 return 0;
243 if (argc <= 0)
244 usage(hflag, hflag ? 0 : 1);
246 signal(SIGINT, catch_sigint);
247 signal(SIGPIPE, catch_sigpipe);
249 for (i = 0; i < nitems(got_commands); i++) {
250 const struct got_error *error;
252 cmd = &got_commands[i];
254 if (strcmp(cmd->cmd_name, argv[0]) != 0 &&
255 strcmp(cmd->cmd_alias, argv[0]) != 0)
256 continue;
258 if (hflag)
259 cmd->cmd_usage();
261 error = cmd->cmd_main(argc, argv);
262 if (error && error->code != GOT_ERR_CANCELLED &&
263 error->code != GOT_ERR_PRIVSEP_EXIT &&
264 !(sigpipe_received &&
265 error->code == GOT_ERR_ERRNO && errno == EPIPE) &&
266 !(sigint_received &&
267 error->code == GOT_ERR_ERRNO && errno == EINTR)) {
268 fprintf(stderr, "%s: %s\n", getprogname(), error->msg);
269 return 1;
272 return 0;
275 fprintf(stderr, "%s: unknown command '%s'\n", getprogname(), argv[0]);
276 list_commands(stderr);
277 return 1;
280 __dead static void
281 usage(int hflag, int status)
283 FILE *fp = (status == 0) ? stdout : stderr;
285 fprintf(fp, "usage: %s [-hV] command [arg ...]\n",
286 getprogname());
287 if (hflag)
288 list_commands(fp);
289 exit(status);
292 static const struct got_error *
293 get_editor(char **abspath)
295 const struct got_error *err = NULL;
296 const char *editor;
298 *abspath = NULL;
300 editor = getenv("VISUAL");
301 if (editor == NULL)
302 editor = getenv("EDITOR");
304 if (editor) {
305 err = got_path_find_prog(abspath, editor);
306 if (err)
307 return err;
310 if (*abspath == NULL) {
311 *abspath = strdup("/usr/bin/vi");
312 if (*abspath == NULL)
313 return got_error_from_errno("strdup");
316 return NULL;
319 static const struct got_error *
320 apply_unveil(const char *repo_path, int repo_read_only,
321 const char *worktree_path)
323 const struct got_error *err;
325 #ifdef PROFILE
326 if (unveil("gmon.out", "rwc") != 0)
327 return got_error_from_errno2("unveil", "gmon.out");
328 #endif
329 if (repo_path && unveil(repo_path, repo_read_only ? "r" : "rwc") != 0)
330 return got_error_from_errno2("unveil", repo_path);
332 if (worktree_path && unveil(worktree_path, "rwc") != 0)
333 return got_error_from_errno2("unveil", worktree_path);
335 if (unveil(GOT_TMPDIR_STR, "rwc") != 0)
336 return got_error_from_errno2("unveil", GOT_TMPDIR_STR);
338 err = got_privsep_unveil_exec_helpers();
339 if (err != NULL)
340 return err;
342 if (unveil(NULL, NULL) != 0)
343 return got_error_from_errno("unveil");
345 return NULL;
348 __dead static void
349 usage_import(void)
351 fprintf(stderr, "usage: %s import [-b branch] [-I pattern] [-m message] "
352 "[-r repository-path] directory\n", getprogname());
353 exit(1);
356 static int
357 spawn_editor(const char *editor, const char *file)
359 pid_t pid;
360 sig_t sighup, sigint, sigquit;
361 int st = -1;
363 sighup = signal(SIGHUP, SIG_IGN);
364 sigint = signal(SIGINT, SIG_IGN);
365 sigquit = signal(SIGQUIT, SIG_IGN);
367 switch (pid = fork()) {
368 case -1:
369 goto doneediting;
370 case 0:
371 execl(editor, editor, file, (char *)NULL);
372 _exit(127);
375 while (waitpid(pid, &st, 0) == -1)
376 if (errno != EINTR)
377 break;
379 doneediting:
380 (void)signal(SIGHUP, sighup);
381 (void)signal(SIGINT, sigint);
382 (void)signal(SIGQUIT, sigquit);
384 if (!WIFEXITED(st)) {
385 errno = EINTR;
386 return -1;
389 return WEXITSTATUS(st);
392 static const struct got_error *
393 read_logmsg(char **logmsg, size_t *len, FILE *fp, size_t filesize)
395 const struct got_error *err = NULL;
396 char *line = NULL;
397 size_t linesize = 0;
399 *logmsg = NULL;
400 *len = 0;
402 if (fseeko(fp, 0L, SEEK_SET) == -1)
403 return got_error_from_errno("fseeko");
405 *logmsg = malloc(filesize + 1);
406 if (*logmsg == NULL)
407 return got_error_from_errno("malloc");
408 (*logmsg)[0] = '\0';
410 while (getline(&line, &linesize, fp) != -1) {
411 if (line[0] == '#' || (*len == 0 && line[0] == '\n'))
412 continue; /* remove comments and leading empty lines */
413 *len = strlcat(*logmsg, line, filesize + 1);
414 if (*len >= filesize + 1) {
415 err = got_error(GOT_ERR_NO_SPACE);
416 goto done;
419 if (ferror(fp)) {
420 err = got_ferror(fp, GOT_ERR_IO);
421 goto done;
424 while (*len > 0 && (*logmsg)[*len - 1] == '\n') {
425 (*logmsg)[*len - 1] = '\0';
426 (*len)--;
428 done:
429 free(line);
430 if (err) {
431 free(*logmsg);
432 *logmsg = NULL;
433 *len = 0;
435 return err;
438 static const struct got_error *
439 edit_logmsg(char **logmsg, const char *editor, const char *logmsg_path,
440 const char *initial_content, size_t initial_content_len,
441 int require_modification)
443 const struct got_error *err = NULL;
444 struct stat st, st2;
445 FILE *fp = NULL;
446 size_t logmsg_len;
448 *logmsg = NULL;
450 if (stat(logmsg_path, &st) == -1)
451 return got_error_from_errno2("stat", logmsg_path);
453 if (spawn_editor(editor, logmsg_path) == -1)
454 return got_error_from_errno("failed spawning editor");
456 if (require_modification) {
457 struct timespec timeout;
459 timeout.tv_sec = 0;
460 timeout.tv_nsec = 1;
461 nanosleep(&timeout, NULL);
464 if (stat(logmsg_path, &st2) == -1)
465 return got_error_from_errno("stat");
467 if (require_modification && st.st_size == st2.st_size &&
468 timespeccmp(&st.st_mtim, &st2.st_mtim, ==))
469 return got_error_msg(GOT_ERR_COMMIT_MSG_EMPTY,
470 "no changes made to commit message, aborting");
472 fp = fopen(logmsg_path, "re");
473 if (fp == NULL) {
474 err = got_error_from_errno("fopen");
475 goto done;
478 /* strip comments and leading/trailing newlines */
479 err = read_logmsg(logmsg, &logmsg_len, fp, st2.st_size);
480 if (err)
481 goto done;
482 if (logmsg_len == 0) {
483 err = got_error_msg(GOT_ERR_COMMIT_MSG_EMPTY,
484 "commit message cannot be empty, aborting");
485 goto done;
487 done:
488 if (fp && fclose(fp) == EOF && err == NULL)
489 err = got_error_from_errno("fclose");
490 if (err) {
491 free(*logmsg);
492 *logmsg = NULL;
494 return err;
497 static const struct got_error *
498 collect_import_msg(char **logmsg, char **logmsg_path, const char *editor,
499 const char *path_dir, const char *branch_name)
501 char *initial_content = NULL;
502 const struct got_error *err = NULL;
503 int initial_content_len;
504 int fd = -1;
506 initial_content_len = asprintf(&initial_content,
507 "\n# %s to be imported to branch %s\n", path_dir,
508 branch_name);
509 if (initial_content_len == -1)
510 return got_error_from_errno("asprintf");
512 err = got_opentemp_named_fd(logmsg_path, &fd,
513 GOT_TMPDIR_STR "/got-importmsg", "");
514 if (err)
515 goto done;
517 if (write(fd, initial_content, initial_content_len) == -1) {
518 err = got_error_from_errno2("write", *logmsg_path);
519 goto done;
521 if (close(fd) == -1) {
522 err = got_error_from_errno2("close", *logmsg_path);
523 goto done;
525 fd = -1;
527 err = edit_logmsg(logmsg, editor, *logmsg_path, initial_content,
528 initial_content_len, 1);
529 done:
530 if (fd != -1 && close(fd) == -1 && err == NULL)
531 err = got_error_from_errno2("close", *logmsg_path);
532 free(initial_content);
533 if (err) {
534 free(*logmsg_path);
535 *logmsg_path = NULL;
537 return err;
540 static const struct got_error *
541 import_progress(void *arg, const char *path)
543 printf("A %s\n", path);
544 return NULL;
547 static const struct got_error *
548 valid_author(const char *author)
550 const char *email = author;
552 /*
553 * Git' expects the author (or committer) to be in the form
554 * "name <email>", which are mostly free form (see the
555 * "committer" description in git-fast-import(1)). We're only
556 * doing this to avoid git's object parser breaking on commits
557 * we create.
558 */
560 while (*author && *author != '\n' && *author != '<' && *author != '>')
561 author++;
562 if (author != email && *author == '<' && *(author - 1) != ' ')
563 return got_error_fmt(GOT_ERR_COMMIT_BAD_AUTHOR, "%s: space "
564 "between author name and email required", email);
565 if (*author++ != '<')
566 return got_error_fmt(GOT_ERR_COMMIT_NO_EMAIL, "%s", email);
567 while (*author && *author != '\n' && *author != '<' && *author != '>')
568 author++;
569 if (strcmp(author, ">") != 0)
570 return got_error_fmt(GOT_ERR_COMMIT_NO_EMAIL, "%s", email);
571 return NULL;
574 static const struct got_error *
575 get_author(char **author, struct got_repository *repo,
576 struct got_worktree *worktree)
578 const struct got_error *err = NULL;
579 const char *got_author = NULL, *name, *email;
580 const struct got_gotconfig *worktree_conf = NULL, *repo_conf = NULL;
582 *author = NULL;
584 if (worktree)
585 worktree_conf = got_worktree_get_gotconfig(worktree);
586 repo_conf = got_repo_get_gotconfig(repo);
588 /*
589 * Priority of potential author information sources, from most
590 * significant to least significant:
591 * 1) work tree's .got/got.conf file
592 * 2) repository's got.conf file
593 * 3) repository's git config file
594 * 4) environment variables
595 * 5) global git config files (in user's home directory or /etc)
596 */
598 if (worktree_conf)
599 got_author = got_gotconfig_get_author(worktree_conf);
600 if (got_author == NULL)
601 got_author = got_gotconfig_get_author(repo_conf);
602 if (got_author == NULL) {
603 name = got_repo_get_gitconfig_author_name(repo);
604 email = got_repo_get_gitconfig_author_email(repo);
605 if (name && email) {
606 if (asprintf(author, "%s <%s>", name, email) == -1)
607 return got_error_from_errno("asprintf");
608 return NULL;
611 got_author = getenv("GOT_AUTHOR");
612 if (got_author == NULL) {
613 name = got_repo_get_global_gitconfig_author_name(repo);
614 email = got_repo_get_global_gitconfig_author_email(
615 repo);
616 if (name && email) {
617 if (asprintf(author, "%s <%s>", name, email)
618 == -1)
619 return got_error_from_errno("asprintf");
620 return NULL;
622 /* TODO: Look up user in password database? */
623 return got_error(GOT_ERR_COMMIT_NO_AUTHOR);
627 *author = strdup(got_author);
628 if (*author == NULL)
629 return got_error_from_errno("strdup");
631 err = valid_author(*author);
632 if (err) {
633 free(*author);
634 *author = NULL;
636 return err;
639 static const struct got_error *
640 get_allowed_signers(char **allowed_signers, struct got_repository *repo,
641 struct got_worktree *worktree)
643 const char *got_allowed_signers = NULL;
644 const struct got_gotconfig *worktree_conf = NULL, *repo_conf = NULL;
646 *allowed_signers = NULL;
648 if (worktree)
649 worktree_conf = got_worktree_get_gotconfig(worktree);
650 repo_conf = got_repo_get_gotconfig(repo);
652 /*
653 * Priority of potential author information sources, from most
654 * significant to least significant:
655 * 1) work tree's .got/got.conf file
656 * 2) repository's got.conf file
657 */
659 if (worktree_conf)
660 got_allowed_signers = got_gotconfig_get_allowed_signers_file(
661 worktree_conf);
662 if (got_allowed_signers == NULL)
663 got_allowed_signers = got_gotconfig_get_allowed_signers_file(
664 repo_conf);
666 if (got_allowed_signers) {
667 *allowed_signers = strdup(got_allowed_signers);
668 if (*allowed_signers == NULL)
669 return got_error_from_errno("strdup");
671 return NULL;
674 static const struct got_error *
675 get_revoked_signers(char **revoked_signers, struct got_repository *repo,
676 struct got_worktree *worktree)
678 const char *got_revoked_signers = NULL;
679 const struct got_gotconfig *worktree_conf = NULL, *repo_conf = NULL;
681 *revoked_signers = NULL;
683 if (worktree)
684 worktree_conf = got_worktree_get_gotconfig(worktree);
685 repo_conf = got_repo_get_gotconfig(repo);
687 /*
688 * Priority of potential author information sources, from most
689 * significant to least significant:
690 * 1) work tree's .got/got.conf file
691 * 2) repository's got.conf file
692 */
694 if (worktree_conf)
695 got_revoked_signers = got_gotconfig_get_revoked_signers_file(
696 worktree_conf);
697 if (got_revoked_signers == NULL)
698 got_revoked_signers = got_gotconfig_get_revoked_signers_file(
699 repo_conf);
701 if (got_revoked_signers) {
702 *revoked_signers = strdup(got_revoked_signers);
703 if (*revoked_signers == NULL)
704 return got_error_from_errno("strdup");
706 return NULL;
709 static const char *
710 get_signer_id(struct got_repository *repo, struct got_worktree *worktree)
712 const char *got_signer_id = NULL;
713 const struct got_gotconfig *worktree_conf = NULL, *repo_conf = NULL;
715 if (worktree)
716 worktree_conf = got_worktree_get_gotconfig(worktree);
717 repo_conf = got_repo_get_gotconfig(repo);
719 /*
720 * Priority of potential author information sources, from most
721 * significant to least significant:
722 * 1) work tree's .got/got.conf file
723 * 2) repository's got.conf file
724 */
726 if (worktree_conf)
727 got_signer_id = got_gotconfig_get_signer_id(worktree_conf);
728 if (got_signer_id == NULL)
729 got_signer_id = got_gotconfig_get_signer_id(repo_conf);
731 return got_signer_id;
734 static const struct got_error *
735 get_gitconfig_path(char **gitconfig_path)
737 const char *homedir = getenv("HOME");
739 *gitconfig_path = NULL;
740 if (homedir) {
741 if (asprintf(gitconfig_path, "%s/.gitconfig", homedir) == -1)
742 return got_error_from_errno("asprintf");
745 return NULL;
748 static const struct got_error *
749 cmd_import(int argc, char *argv[])
751 const struct got_error *error = NULL;
752 char *path_dir = NULL, *repo_path = NULL, *logmsg = NULL;
753 char *gitconfig_path = NULL, *editor = NULL, *author = NULL;
754 const char *branch_name = NULL;
755 char *id_str = NULL, *logmsg_path = NULL;
756 char refname[PATH_MAX] = "refs/heads/";
757 struct got_repository *repo = NULL;
758 struct got_reference *branch_ref = NULL, *head_ref = NULL;
759 struct got_object_id *new_commit_id = NULL;
760 int ch, n = 0;
761 struct got_pathlist_head ignores;
762 struct got_pathlist_entry *pe;
763 int preserve_logmsg = 0;
764 int *pack_fds = NULL;
766 TAILQ_INIT(&ignores);
768 #ifndef PROFILE
769 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
770 "unveil",
771 NULL) == -1)
772 err(1, "pledge");
773 #endif
775 while ((ch = getopt(argc, argv, "b:I:m:r:")) != -1) {
776 switch (ch) {
777 case 'b':
778 branch_name = optarg;
779 break;
780 case 'I':
781 if (optarg[0] == '\0')
782 break;
783 error = got_pathlist_insert(&pe, &ignores, optarg,
784 NULL);
785 if (error)
786 goto done;
787 break;
788 case 'm':
789 logmsg = strdup(optarg);
790 if (logmsg == NULL) {
791 error = got_error_from_errno("strdup");
792 goto done;
794 break;
795 case 'r':
796 repo_path = realpath(optarg, NULL);
797 if (repo_path == NULL) {
798 error = got_error_from_errno2("realpath",
799 optarg);
800 goto done;
802 break;
803 default:
804 usage_import();
805 /* NOTREACHED */
809 argc -= optind;
810 argv += optind;
812 if (argc != 1)
813 usage_import();
815 if (repo_path == NULL) {
816 repo_path = getcwd(NULL, 0);
817 if (repo_path == NULL)
818 return got_error_from_errno("getcwd");
820 got_path_strip_trailing_slashes(repo_path);
821 error = get_gitconfig_path(&gitconfig_path);
822 if (error)
823 goto done;
824 error = got_repo_pack_fds_open(&pack_fds);
825 if (error != NULL)
826 goto done;
827 error = got_repo_open(&repo, repo_path, gitconfig_path, pack_fds);
828 if (error)
829 goto done;
831 error = get_author(&author, repo, NULL);
832 if (error)
833 return error;
835 /*
836 * Don't let the user create a branch name with a leading '-'.
837 * While technically a valid reference name, this case is usually
838 * an unintended typo.
839 */
840 if (branch_name && branch_name[0] == '-')
841 return got_error_path(branch_name, GOT_ERR_REF_NAME_MINUS);
843 error = got_ref_open(&head_ref, repo, GOT_REF_HEAD, 0);
844 if (error && error->code != GOT_ERR_NOT_REF)
845 goto done;
847 if (branch_name)
848 n = strlcat(refname, branch_name, sizeof(refname));
849 else if (head_ref && got_ref_is_symbolic(head_ref))
850 n = strlcpy(refname, got_ref_get_symref_target(head_ref),
851 sizeof(refname));
852 else
853 n = strlcat(refname, "main", sizeof(refname));
854 if (n >= sizeof(refname)) {
855 error = got_error(GOT_ERR_NO_SPACE);
856 goto done;
859 error = got_ref_open(&branch_ref, repo, refname, 0);
860 if (error) {
861 if (error->code != GOT_ERR_NOT_REF)
862 goto done;
863 } else {
864 error = got_error_msg(GOT_ERR_BRANCH_EXISTS,
865 "import target branch already exists");
866 goto done;
869 path_dir = realpath(argv[0], NULL);
870 if (path_dir == NULL) {
871 error = got_error_from_errno2("realpath", argv[0]);
872 goto done;
874 got_path_strip_trailing_slashes(path_dir);
876 /*
877 * unveil(2) traverses exec(2); if an editor is used we have
878 * to apply unveil after the log message has been written.
879 */
880 if (logmsg == NULL || strlen(logmsg) == 0) {
881 error = get_editor(&editor);
882 if (error)
883 goto done;
884 free(logmsg);
885 error = collect_import_msg(&logmsg, &logmsg_path, editor,
886 path_dir, refname);
887 if (error) {
888 if (error->code != GOT_ERR_COMMIT_MSG_EMPTY &&
889 logmsg_path != NULL)
890 preserve_logmsg = 1;
891 goto done;
895 if (unveil(path_dir, "r") != 0) {
896 error = got_error_from_errno2("unveil", path_dir);
897 if (logmsg_path)
898 preserve_logmsg = 1;
899 goto done;
902 error = apply_unveil(got_repo_get_path(repo), 0, NULL);
903 if (error) {
904 if (logmsg_path)
905 preserve_logmsg = 1;
906 goto done;
909 error = got_repo_import(&new_commit_id, path_dir, logmsg,
910 author, &ignores, repo, import_progress, NULL);
911 if (error) {
912 if (logmsg_path)
913 preserve_logmsg = 1;
914 goto done;
917 error = got_ref_alloc(&branch_ref, refname, new_commit_id);
918 if (error) {
919 if (logmsg_path)
920 preserve_logmsg = 1;
921 goto done;
924 error = got_ref_write(branch_ref, repo);
925 if (error) {
926 if (logmsg_path)
927 preserve_logmsg = 1;
928 goto done;
931 error = got_object_id_str(&id_str, new_commit_id);
932 if (error) {
933 if (logmsg_path)
934 preserve_logmsg = 1;
935 goto done;
938 error = got_ref_open(&head_ref, repo, GOT_REF_HEAD, 0);
939 if (error) {
940 if (error->code != GOT_ERR_NOT_REF) {
941 if (logmsg_path)
942 preserve_logmsg = 1;
943 goto done;
946 error = got_ref_alloc_symref(&head_ref, GOT_REF_HEAD,
947 branch_ref);
948 if (error) {
949 if (logmsg_path)
950 preserve_logmsg = 1;
951 goto done;
954 error = got_ref_write(head_ref, repo);
955 if (error) {
956 if (logmsg_path)
957 preserve_logmsg = 1;
958 goto done;
962 printf("Created branch %s with commit %s\n",
963 got_ref_get_name(branch_ref), id_str);
964 done:
965 if (pack_fds) {
966 const struct got_error *pack_err =
967 got_repo_pack_fds_close(pack_fds);
968 if (error == NULL)
969 error = pack_err;
971 if (preserve_logmsg) {
972 fprintf(stderr, "%s: log message preserved in %s\n",
973 getprogname(), logmsg_path);
974 } else if (logmsg_path && unlink(logmsg_path) == -1 && error == NULL)
975 error = got_error_from_errno2("unlink", logmsg_path);
976 free(logmsg);
977 free(logmsg_path);
978 free(repo_path);
979 free(editor);
980 free(new_commit_id);
981 free(id_str);
982 free(author);
983 free(gitconfig_path);
984 if (branch_ref)
985 got_ref_close(branch_ref);
986 if (head_ref)
987 got_ref_close(head_ref);
988 return error;
991 __dead static void
992 usage_clone(void)
994 fprintf(stderr, "usage: %s clone [-almqv] [-b branch] [-R reference] "
995 "repository-URL [directory]\n", getprogname());
996 exit(1);
999 struct got_fetch_progress_arg {
1000 char last_scaled_size[FMT_SCALED_STRSIZE];
1001 int last_p_indexed;
1002 int last_p_resolved;
1003 int verbosity;
1005 struct got_repository *repo;
1007 int create_configs;
1008 int configs_created;
1009 struct {
1010 struct got_pathlist_head *symrefs;
1011 struct got_pathlist_head *wanted_branches;
1012 struct got_pathlist_head *wanted_refs;
1013 const char *proto;
1014 const char *host;
1015 const char *port;
1016 const char *remote_repo_path;
1017 const char *git_url;
1018 int fetch_all_branches;
1019 int mirror_references;
1020 } config_info;
1023 /* XXX forward declaration */
1024 static const struct got_error *
1025 create_config_files(const char *proto, const char *host, const char *port,
1026 const char *remote_repo_path, const char *git_url, int fetch_all_branches,
1027 int mirror_references, struct got_pathlist_head *symrefs,
1028 struct got_pathlist_head *wanted_branches,
1029 struct got_pathlist_head *wanted_refs, struct got_repository *repo);
1031 static const struct got_error *
1032 fetch_progress(void *arg, const char *message, off_t packfile_size,
1033 int nobj_total, int nobj_indexed, int nobj_loose, int nobj_resolved)
1035 const struct got_error *err = NULL;
1036 struct got_fetch_progress_arg *a = arg;
1037 char scaled_size[FMT_SCALED_STRSIZE];
1038 int p_indexed, p_resolved;
1039 int print_size = 0, print_indexed = 0, print_resolved = 0;
1042 * In order to allow a failed clone to be resumed with 'got fetch'
1043 * we try to create configuration files as soon as possible.
1044 * Once the server has sent information about its default branch
1045 * we have all required information.
1047 if (a->create_configs && !a->configs_created &&
1048 !TAILQ_EMPTY(a->config_info.symrefs)) {
1049 err = create_config_files(a->config_info.proto,
1050 a->config_info.host, a->config_info.port,
1051 a->config_info.remote_repo_path,
1052 a->config_info.git_url,
1053 a->config_info.fetch_all_branches,
1054 a->config_info.mirror_references,
1055 a->config_info.symrefs,
1056 a->config_info.wanted_branches,
1057 a->config_info.wanted_refs, a->repo);
1058 if (err)
1059 return err;
1060 a->configs_created = 1;
1063 if (a->verbosity < 0)
1064 return NULL;
1066 if (message && message[0] != '\0') {
1067 printf("\rserver: %s", message);
1068 fflush(stdout);
1069 return NULL;
1072 if (packfile_size > 0 || nobj_indexed > 0) {
1073 if (fmt_scaled(packfile_size, scaled_size) == 0 &&
1074 (a->last_scaled_size[0] == '\0' ||
1075 strcmp(scaled_size, a->last_scaled_size)) != 0) {
1076 print_size = 1;
1077 if (strlcpy(a->last_scaled_size, scaled_size,
1078 FMT_SCALED_STRSIZE) >= FMT_SCALED_STRSIZE)
1079 return got_error(GOT_ERR_NO_SPACE);
1081 if (nobj_indexed > 0) {
1082 p_indexed = (nobj_indexed * 100) / nobj_total;
1083 if (p_indexed != a->last_p_indexed) {
1084 a->last_p_indexed = p_indexed;
1085 print_indexed = 1;
1086 print_size = 1;
1089 if (nobj_resolved > 0) {
1090 p_resolved = (nobj_resolved * 100) /
1091 (nobj_total - nobj_loose);
1092 if (p_resolved != a->last_p_resolved) {
1093 a->last_p_resolved = p_resolved;
1094 print_resolved = 1;
1095 print_indexed = 1;
1096 print_size = 1;
1101 if (print_size || print_indexed || print_resolved)
1102 printf("\r");
1103 if (print_size)
1104 printf("%*s fetched", FMT_SCALED_STRSIZE - 2, scaled_size);
1105 if (print_indexed)
1106 printf("; indexing %d%%", p_indexed);
1107 if (print_resolved)
1108 printf("; resolving deltas %d%%", p_resolved);
1109 if (print_size || print_indexed || print_resolved)
1110 fflush(stdout);
1112 return NULL;
1115 static const struct got_error *
1116 create_symref(const char *refname, struct got_reference *target_ref,
1117 int verbosity, struct got_repository *repo)
1119 const struct got_error *err;
1120 struct got_reference *head_symref;
1122 err = got_ref_alloc_symref(&head_symref, refname, target_ref);
1123 if (err)
1124 return err;
1126 err = got_ref_write(head_symref, repo);
1127 if (err == NULL && verbosity > 0) {
1128 printf("Created reference %s: %s\n", GOT_REF_HEAD,
1129 got_ref_get_name(target_ref));
1131 got_ref_close(head_symref);
1132 return err;
1135 static const struct got_error *
1136 list_remote_refs(struct got_pathlist_head *symrefs,
1137 struct got_pathlist_head *refs)
1139 const struct got_error *err;
1140 struct got_pathlist_entry *pe;
1142 TAILQ_FOREACH(pe, symrefs, entry) {
1143 const char *refname = pe->path;
1144 const char *targetref = pe->data;
1146 printf("%s: %s\n", refname, targetref);
1149 TAILQ_FOREACH(pe, refs, entry) {
1150 const char *refname = pe->path;
1151 struct got_object_id *id = pe->data;
1152 char *id_str;
1154 err = got_object_id_str(&id_str, id);
1155 if (err)
1156 return err;
1157 printf("%s: %s\n", refname, id_str);
1158 free(id_str);
1161 return NULL;
1164 static const struct got_error *
1165 create_ref(const char *refname, struct got_object_id *id,
1166 int verbosity, struct got_repository *repo)
1168 const struct got_error *err = NULL;
1169 struct got_reference *ref;
1170 char *id_str;
1172 err = got_object_id_str(&id_str, id);
1173 if (err)
1174 return err;
1176 err = got_ref_alloc(&ref, refname, id);
1177 if (err)
1178 goto done;
1180 err = got_ref_write(ref, repo);
1181 got_ref_close(ref);
1183 if (err == NULL && verbosity >= 0)
1184 printf("Created reference %s: %s\n", refname, id_str);
1185 done:
1186 free(id_str);
1187 return err;
1190 static int
1191 match_wanted_ref(const char *refname, const char *wanted_ref)
1193 if (strncmp(refname, "refs/", 5) != 0)
1194 return 0;
1195 refname += 5;
1198 * Prevent fetching of references that won't make any
1199 * sense outside of the remote repository's context.
1201 if (strncmp(refname, "got/", 4) == 0)
1202 return 0;
1203 if (strncmp(refname, "remotes/", 8) == 0)
1204 return 0;
1206 if (strncmp(wanted_ref, "refs/", 5) == 0)
1207 wanted_ref += 5;
1209 /* Allow prefix match. */
1210 if (got_path_is_child(refname, wanted_ref, strlen(wanted_ref)))
1211 return 1;
1213 /* Allow exact match. */
1214 return (strcmp(refname, wanted_ref) == 0);
1217 static int
1218 is_wanted_ref(struct got_pathlist_head *wanted_refs, const char *refname)
1220 struct got_pathlist_entry *pe;
1222 TAILQ_FOREACH(pe, wanted_refs, entry) {
1223 if (match_wanted_ref(refname, pe->path))
1224 return 1;
1227 return 0;
1230 static const struct got_error *
1231 create_wanted_ref(const char *refname, struct got_object_id *id,
1232 const char *remote_repo_name, int verbosity, struct got_repository *repo)
1234 const struct got_error *err;
1235 char *remote_refname;
1237 if (strncmp("refs/", refname, 5) == 0)
1238 refname += 5;
1240 if (asprintf(&remote_refname, "refs/remotes/%s/%s",
1241 remote_repo_name, refname) == -1)
1242 return got_error_from_errno("asprintf");
1244 err = create_ref(remote_refname, id, verbosity, repo);
1245 free(remote_refname);
1246 return err;
1249 static const struct got_error *
1250 create_gotconfig(const char *proto, const char *host, const char *port,
1251 const char *remote_repo_path, const char *default_branch,
1252 int fetch_all_branches, struct got_pathlist_head *wanted_branches,
1253 struct got_pathlist_head *wanted_refs, int mirror_references,
1254 struct got_repository *repo)
1256 const struct got_error *err = NULL;
1257 char *gotconfig_path = NULL;
1258 char *gotconfig = NULL;
1259 FILE *gotconfig_file = NULL;
1260 const char *branchname = NULL;
1261 char *branches = NULL, *refs = NULL;
1262 ssize_t n;
1264 if (!fetch_all_branches && !TAILQ_EMPTY(wanted_branches)) {
1265 struct got_pathlist_entry *pe;
1266 TAILQ_FOREACH(pe, wanted_branches, entry) {
1267 char *s;
1268 branchname = pe->path;
1269 if (strncmp(branchname, "refs/heads/", 11) == 0)
1270 branchname += 11;
1271 if (asprintf(&s, "%s\"%s\" ",
1272 branches ? branches : "", branchname) == -1) {
1273 err = got_error_from_errno("asprintf");
1274 goto done;
1276 free(branches);
1277 branches = s;
1279 } else if (!fetch_all_branches && default_branch) {
1280 branchname = default_branch;
1281 if (strncmp(branchname, "refs/heads/", 11) == 0)
1282 branchname += 11;
1283 if (asprintf(&branches, "\"%s\" ", branchname) == -1) {
1284 err = got_error_from_errno("asprintf");
1285 goto done;
1288 if (!TAILQ_EMPTY(wanted_refs)) {
1289 struct got_pathlist_entry *pe;
1290 TAILQ_FOREACH(pe, wanted_refs, entry) {
1291 char *s;
1292 const char *refname = pe->path;
1293 if (strncmp(refname, "refs/", 5) == 0)
1294 branchname += 5;
1295 if (asprintf(&s, "%s\"%s\" ",
1296 refs ? refs : "", refname) == -1) {
1297 err = got_error_from_errno("asprintf");
1298 goto done;
1300 free(refs);
1301 refs = s;
1305 /* Create got.conf(5). */
1306 gotconfig_path = got_repo_get_path_gotconfig(repo);
1307 if (gotconfig_path == NULL) {
1308 err = got_error_from_errno("got_repo_get_path_gotconfig");
1309 goto done;
1311 gotconfig_file = fopen(gotconfig_path, "ae");
1312 if (gotconfig_file == NULL) {
1313 err = got_error_from_errno2("fopen", gotconfig_path);
1314 goto done;
1316 if (asprintf(&gotconfig,
1317 "remote \"%s\" {\n"
1318 "\tserver %s\n"
1319 "\tprotocol %s\n"
1320 "%s%s%s"
1321 "\trepository \"%s\"\n"
1322 "%s%s%s"
1323 "%s%s%s"
1324 "%s"
1325 "%s"
1326 "}\n",
1327 GOT_FETCH_DEFAULT_REMOTE_NAME, host, proto,
1328 port ? "\tport " : "", port ? port : "", port ? "\n" : "",
1329 remote_repo_path, branches ? "\tbranch { " : "",
1330 branches ? branches : "", branches ? "}\n" : "",
1331 refs ? "\treference { " : "", refs ? refs : "", refs ? "}\n" : "",
1332 mirror_references ? "\tmirror_references yes\n" : "",
1333 fetch_all_branches ? "\tfetch_all_branches yes\n" : "") == -1) {
1334 err = got_error_from_errno("asprintf");
1335 goto done;
1337 n = fwrite(gotconfig, 1, strlen(gotconfig), gotconfig_file);
1338 if (n != strlen(gotconfig)) {
1339 err = got_ferror(gotconfig_file, GOT_ERR_IO);
1340 goto done;
1343 done:
1344 if (gotconfig_file && fclose(gotconfig_file) == EOF && err == NULL)
1345 err = got_error_from_errno2("fclose", gotconfig_path);
1346 free(gotconfig_path);
1347 free(branches);
1348 return err;
1351 static const struct got_error *
1352 create_gitconfig(const char *git_url, const char *default_branch,
1353 int fetch_all_branches, struct got_pathlist_head *wanted_branches,
1354 struct got_pathlist_head *wanted_refs, int mirror_references,
1355 struct got_repository *repo)
1357 const struct got_error *err = NULL;
1358 char *gitconfig_path = NULL;
1359 char *gitconfig = NULL;
1360 FILE *gitconfig_file = NULL;
1361 char *branches = NULL, *refs = NULL;
1362 const char *branchname;
1363 ssize_t n;
1365 /* Create a config file Git can understand. */
1366 gitconfig_path = got_repo_get_path_gitconfig(repo);
1367 if (gitconfig_path == NULL) {
1368 err = got_error_from_errno("got_repo_get_path_gitconfig");
1369 goto done;
1371 gitconfig_file = fopen(gitconfig_path, "ae");
1372 if (gitconfig_file == NULL) {
1373 err = got_error_from_errno2("fopen", gitconfig_path);
1374 goto done;
1376 if (fetch_all_branches) {
1377 if (mirror_references) {
1378 if (asprintf(&branches,
1379 "\tfetch = refs/heads/*:refs/heads/*\n") == -1) {
1380 err = got_error_from_errno("asprintf");
1381 goto done;
1383 } else if (asprintf(&branches,
1384 "\tfetch = refs/heads/*:refs/remotes/%s/*\n",
1385 GOT_FETCH_DEFAULT_REMOTE_NAME) == -1) {
1386 err = got_error_from_errno("asprintf");
1387 goto done;
1389 } else if (!TAILQ_EMPTY(wanted_branches)) {
1390 struct got_pathlist_entry *pe;
1391 TAILQ_FOREACH(pe, wanted_branches, entry) {
1392 char *s;
1393 branchname = pe->path;
1394 if (strncmp(branchname, "refs/heads/", 11) == 0)
1395 branchname += 11;
1396 if (mirror_references) {
1397 if (asprintf(&s,
1398 "%s\tfetch = refs/heads/%s:refs/heads/%s\n",
1399 branches ? branches : "",
1400 branchname, branchname) == -1) {
1401 err = got_error_from_errno("asprintf");
1402 goto done;
1404 } else if (asprintf(&s,
1405 "%s\tfetch = refs/heads/%s:refs/remotes/%s/%s\n",
1406 branches ? branches : "",
1407 branchname, GOT_FETCH_DEFAULT_REMOTE_NAME,
1408 branchname) == -1) {
1409 err = got_error_from_errno("asprintf");
1410 goto done;
1412 free(branches);
1413 branches = s;
1415 } else {
1417 * If the server specified a default branch, use just that one.
1418 * Otherwise fall back to fetching all branches on next fetch.
1420 if (default_branch) {
1421 branchname = default_branch;
1422 if (strncmp(branchname, "refs/heads/", 11) == 0)
1423 branchname += 11;
1424 } else
1425 branchname = "*"; /* fall back to all branches */
1426 if (mirror_references) {
1427 if (asprintf(&branches,
1428 "\tfetch = refs/heads/%s:refs/heads/%s\n",
1429 branchname, branchname) == -1) {
1430 err = got_error_from_errno("asprintf");
1431 goto done;
1433 } else if (asprintf(&branches,
1434 "\tfetch = refs/heads/%s:refs/remotes/%s/%s\n",
1435 branchname, GOT_FETCH_DEFAULT_REMOTE_NAME,
1436 branchname) == -1) {
1437 err = got_error_from_errno("asprintf");
1438 goto done;
1441 if (!TAILQ_EMPTY(wanted_refs)) {
1442 struct got_pathlist_entry *pe;
1443 TAILQ_FOREACH(pe, wanted_refs, entry) {
1444 char *s;
1445 const char *refname = pe->path;
1446 if (strncmp(refname, "refs/", 5) == 0)
1447 refname += 5;
1448 if (mirror_references) {
1449 if (asprintf(&s,
1450 "%s\tfetch = refs/%s:refs/%s\n",
1451 refs ? refs : "", refname, refname) == -1) {
1452 err = got_error_from_errno("asprintf");
1453 goto done;
1455 } else if (asprintf(&s,
1456 "%s\tfetch = refs/%s:refs/remotes/%s/%s\n",
1457 refs ? refs : "",
1458 refname, GOT_FETCH_DEFAULT_REMOTE_NAME,
1459 refname) == -1) {
1460 err = got_error_from_errno("asprintf");
1461 goto done;
1463 free(refs);
1464 refs = s;
1468 if (asprintf(&gitconfig,
1469 "[remote \"%s\"]\n"
1470 "\turl = %s\n"
1471 "%s"
1472 "%s"
1473 "\tfetch = refs/tags/*:refs/tags/*\n",
1474 GOT_FETCH_DEFAULT_REMOTE_NAME, git_url, branches ? branches : "",
1475 refs ? refs : "") == -1) {
1476 err = got_error_from_errno("asprintf");
1477 goto done;
1479 n = fwrite(gitconfig, 1, strlen(gitconfig), gitconfig_file);
1480 if (n != strlen(gitconfig)) {
1481 err = got_ferror(gitconfig_file, GOT_ERR_IO);
1482 goto done;
1484 done:
1485 if (gitconfig_file && fclose(gitconfig_file) == EOF && err == NULL)
1486 err = got_error_from_errno2("fclose", gitconfig_path);
1487 free(gitconfig_path);
1488 free(branches);
1489 return err;
1492 static const struct got_error *
1493 create_config_files(const char *proto, const char *host, const char *port,
1494 const char *remote_repo_path, const char *git_url, int fetch_all_branches,
1495 int mirror_references, struct got_pathlist_head *symrefs,
1496 struct got_pathlist_head *wanted_branches,
1497 struct got_pathlist_head *wanted_refs, struct got_repository *repo)
1499 const struct got_error *err = NULL;
1500 const char *default_branch = NULL;
1501 struct got_pathlist_entry *pe;
1504 * If we asked for a set of wanted branches then use the first
1505 * one of those.
1507 if (!TAILQ_EMPTY(wanted_branches)) {
1508 pe = TAILQ_FIRST(wanted_branches);
1509 default_branch = pe->path;
1510 } else {
1511 /* First HEAD ref listed by server is the default branch. */
1512 TAILQ_FOREACH(pe, symrefs, entry) {
1513 const char *refname = pe->path;
1514 const char *target = pe->data;
1516 if (strcmp(refname, GOT_REF_HEAD) != 0)
1517 continue;
1519 default_branch = target;
1520 break;
1524 /* Create got.conf(5). */
1525 err = create_gotconfig(proto, host, port, remote_repo_path,
1526 default_branch, fetch_all_branches, wanted_branches,
1527 wanted_refs, mirror_references, repo);
1528 if (err)
1529 return err;
1531 /* Create a config file Git can understand. */
1532 return create_gitconfig(git_url, default_branch, fetch_all_branches,
1533 wanted_branches, wanted_refs, mirror_references, repo);
1536 static const struct got_error *
1537 cmd_clone(int argc, char *argv[])
1539 const struct got_error *error = NULL;
1540 const char *uri, *dirname;
1541 char *proto, *host, *port, *repo_name, *server_path;
1542 char *default_destdir = NULL, *id_str = NULL;
1543 const char *repo_path;
1544 struct got_repository *repo = NULL;
1545 struct got_pathlist_head refs, symrefs, wanted_branches, wanted_refs;
1546 struct got_pathlist_entry *pe;
1547 struct got_object_id *pack_hash = NULL;
1548 int ch, fetchfd = -1, fetchstatus;
1549 pid_t fetchpid = -1;
1550 struct got_fetch_progress_arg fpa;
1551 char *git_url = NULL;
1552 int verbosity = 0, fetch_all_branches = 0, mirror_references = 0;
1553 int bflag = 0, list_refs_only = 0;
1554 int *pack_fds = NULL;
1556 TAILQ_INIT(&refs);
1557 TAILQ_INIT(&symrefs);
1558 TAILQ_INIT(&wanted_branches);
1559 TAILQ_INIT(&wanted_refs);
1561 while ((ch = getopt(argc, argv, "ab:lmqR:v")) != -1) {
1562 switch (ch) {
1563 case 'a':
1564 fetch_all_branches = 1;
1565 break;
1566 case 'b':
1567 error = got_pathlist_append(&wanted_branches,
1568 optarg, NULL);
1569 if (error)
1570 return error;
1571 bflag = 1;
1572 break;
1573 case 'l':
1574 list_refs_only = 1;
1575 break;
1576 case 'm':
1577 mirror_references = 1;
1578 break;
1579 case 'q':
1580 verbosity = -1;
1581 break;
1582 case 'R':
1583 error = got_pathlist_append(&wanted_refs,
1584 optarg, NULL);
1585 if (error)
1586 return error;
1587 break;
1588 case 'v':
1589 if (verbosity < 0)
1590 verbosity = 0;
1591 else if (verbosity < 3)
1592 verbosity++;
1593 break;
1594 default:
1595 usage_clone();
1596 break;
1599 argc -= optind;
1600 argv += optind;
1602 if (fetch_all_branches && !TAILQ_EMPTY(&wanted_branches))
1603 option_conflict('a', 'b');
1604 if (list_refs_only) {
1605 if (!TAILQ_EMPTY(&wanted_branches))
1606 option_conflict('l', 'b');
1607 if (fetch_all_branches)
1608 option_conflict('l', 'a');
1609 if (mirror_references)
1610 option_conflict('l', 'm');
1611 if (!TAILQ_EMPTY(&wanted_refs))
1612 option_conflict('l', 'R');
1615 uri = argv[0];
1617 if (argc == 1)
1618 dirname = NULL;
1619 else if (argc == 2)
1620 dirname = argv[1];
1621 else
1622 usage_clone();
1624 error = got_dial_parse_uri(&proto, &host, &port, &server_path,
1625 &repo_name, uri);
1626 if (error)
1627 goto done;
1629 if (asprintf(&git_url, "%s://%s%s%s%s%s", proto,
1630 host, port ? ":" : "", port ? port : "",
1631 server_path[0] != '/' ? "/" : "", server_path) == -1) {
1632 error = got_error_from_errno("asprintf");
1633 goto done;
1636 if (strcmp(proto, "git") == 0) {
1637 #ifndef PROFILE
1638 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
1639 "sendfd dns inet unveil", NULL) == -1)
1640 err(1, "pledge");
1641 #endif
1642 } else if (strcmp(proto, "git+ssh") == 0 ||
1643 strcmp(proto, "ssh") == 0) {
1644 #ifndef PROFILE
1645 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
1646 "sendfd unveil", NULL) == -1)
1647 err(1, "pledge");
1648 #endif
1649 } else if (strcmp(proto, "http") == 0 ||
1650 strcmp(proto, "git+http") == 0) {
1651 error = got_error_path(proto, GOT_ERR_NOT_IMPL);
1652 goto done;
1653 } else {
1654 error = got_error_path(proto, GOT_ERR_BAD_PROTO);
1655 goto done;
1657 if (dirname == NULL) {
1658 if (asprintf(&default_destdir, "%s.git", repo_name) == -1) {
1659 error = got_error_from_errno("asprintf");
1660 goto done;
1662 repo_path = default_destdir;
1663 } else
1664 repo_path = dirname;
1666 if (!list_refs_only) {
1667 error = got_path_mkdir(repo_path);
1668 if (error &&
1669 (!(error->code == GOT_ERR_ERRNO && errno == EISDIR) &&
1670 !(error->code == GOT_ERR_ERRNO && errno == EEXIST)))
1671 goto done;
1672 if (!got_path_dir_is_empty(repo_path)) {
1673 error = got_error_path(repo_path,
1674 GOT_ERR_DIR_NOT_EMPTY);
1675 goto done;
1679 error = got_dial_apply_unveil(proto);
1680 if (error)
1681 goto done;
1683 error = apply_unveil(repo_path, 0, NULL);
1684 if (error)
1685 goto done;
1687 if (verbosity >= 0)
1688 printf("Connecting to %s\n", git_url);
1690 error = got_fetch_connect(&fetchpid, &fetchfd, proto, host, port,
1691 server_path, verbosity);
1692 if (error)
1693 goto done;
1695 if (!list_refs_only) {
1696 error = got_repo_init(repo_path, NULL);
1697 if (error)
1698 goto done;
1699 error = got_repo_pack_fds_open(&pack_fds);
1700 if (error != NULL)
1701 goto done;
1702 error = got_repo_open(&repo, repo_path, NULL, pack_fds);
1703 if (error)
1704 goto done;
1707 fpa.last_scaled_size[0] = '\0';
1708 fpa.last_p_indexed = -1;
1709 fpa.last_p_resolved = -1;
1710 fpa.verbosity = verbosity;
1711 fpa.create_configs = 1;
1712 fpa.configs_created = 0;
1713 fpa.repo = repo;
1714 fpa.config_info.symrefs = &symrefs;
1715 fpa.config_info.wanted_branches = &wanted_branches;
1716 fpa.config_info.wanted_refs = &wanted_refs;
1717 fpa.config_info.proto = proto;
1718 fpa.config_info.host = host;
1719 fpa.config_info.port = port;
1720 fpa.config_info.remote_repo_path = server_path;
1721 fpa.config_info.git_url = git_url;
1722 fpa.config_info.fetch_all_branches = fetch_all_branches;
1723 fpa.config_info.mirror_references = mirror_references;
1724 error = got_fetch_pack(&pack_hash, &refs, &symrefs,
1725 GOT_FETCH_DEFAULT_REMOTE_NAME, mirror_references,
1726 fetch_all_branches, &wanted_branches, &wanted_refs,
1727 list_refs_only, verbosity, fetchfd, repo, NULL, NULL, bflag,
1728 fetch_progress, &fpa);
1729 if (error)
1730 goto done;
1732 if (list_refs_only) {
1733 error = list_remote_refs(&symrefs, &refs);
1734 goto done;
1737 if (pack_hash == NULL) {
1738 error = got_error_fmt(GOT_ERR_FETCH_FAILED, "%s",
1739 "server sent an empty pack file");
1740 goto done;
1742 error = got_object_id_str(&id_str, pack_hash);
1743 if (error)
1744 goto done;
1745 if (verbosity >= 0)
1746 printf("\nFetched %s.pack\n", id_str);
1747 free(id_str);
1749 /* Set up references provided with the pack file. */
1750 TAILQ_FOREACH(pe, &refs, entry) {
1751 const char *refname = pe->path;
1752 struct got_object_id *id = pe->data;
1753 char *remote_refname;
1755 if (is_wanted_ref(&wanted_refs, refname) &&
1756 !mirror_references) {
1757 error = create_wanted_ref(refname, id,
1758 GOT_FETCH_DEFAULT_REMOTE_NAME,
1759 verbosity - 1, repo);
1760 if (error)
1761 goto done;
1762 continue;
1765 error = create_ref(refname, id, verbosity - 1, repo);
1766 if (error)
1767 goto done;
1769 if (mirror_references)
1770 continue;
1772 if (strncmp("refs/heads/", refname, 11) != 0)
1773 continue;
1775 if (asprintf(&remote_refname,
1776 "refs/remotes/%s/%s", GOT_FETCH_DEFAULT_REMOTE_NAME,
1777 refname + 11) == -1) {
1778 error = got_error_from_errno("asprintf");
1779 goto done;
1781 error = create_ref(remote_refname, id, verbosity - 1, repo);
1782 free(remote_refname);
1783 if (error)
1784 goto done;
1787 /* Set the HEAD reference if the server provided one. */
1788 TAILQ_FOREACH(pe, &symrefs, entry) {
1789 struct got_reference *target_ref;
1790 const char *refname = pe->path;
1791 const char *target = pe->data;
1792 char *remote_refname = NULL, *remote_target = NULL;
1794 if (strcmp(refname, GOT_REF_HEAD) != 0)
1795 continue;
1797 error = got_ref_open(&target_ref, repo, target, 0);
1798 if (error) {
1799 if (error->code == GOT_ERR_NOT_REF) {
1800 error = NULL;
1801 continue;
1803 goto done;
1806 error = create_symref(refname, target_ref, verbosity, repo);
1807 got_ref_close(target_ref);
1808 if (error)
1809 goto done;
1811 if (mirror_references)
1812 continue;
1814 if (strncmp("refs/heads/", target, 11) != 0)
1815 continue;
1817 if (asprintf(&remote_refname,
1818 "refs/remotes/%s/%s", GOT_FETCH_DEFAULT_REMOTE_NAME,
1819 refname) == -1) {
1820 error = got_error_from_errno("asprintf");
1821 goto done;
1823 if (asprintf(&remote_target,
1824 "refs/remotes/%s/%s", GOT_FETCH_DEFAULT_REMOTE_NAME,
1825 target + 11) == -1) {
1826 error = got_error_from_errno("asprintf");
1827 free(remote_refname);
1828 goto done;
1830 error = got_ref_open(&target_ref, repo, remote_target, 0);
1831 if (error) {
1832 free(remote_refname);
1833 free(remote_target);
1834 if (error->code == GOT_ERR_NOT_REF) {
1835 error = NULL;
1836 continue;
1838 goto done;
1840 error = create_symref(remote_refname, target_ref,
1841 verbosity - 1, repo);
1842 free(remote_refname);
1843 free(remote_target);
1844 got_ref_close(target_ref);
1845 if (error)
1846 goto done;
1848 if (pe == NULL) {
1850 * We failed to set the HEAD reference. If we asked for
1851 * a set of wanted branches use the first of one of those
1852 * which could be fetched instead.
1854 TAILQ_FOREACH(pe, &wanted_branches, entry) {
1855 const char *target = pe->path;
1856 struct got_reference *target_ref;
1858 error = got_ref_open(&target_ref, repo, target, 0);
1859 if (error) {
1860 if (error->code == GOT_ERR_NOT_REF) {
1861 error = NULL;
1862 continue;
1864 goto done;
1867 error = create_symref(GOT_REF_HEAD, target_ref,
1868 verbosity, repo);
1869 got_ref_close(target_ref);
1870 if (error)
1871 goto done;
1872 break;
1875 if (!fpa.configs_created && pe != NULL) {
1876 error = create_config_files(fpa.config_info.proto,
1877 fpa.config_info.host, fpa.config_info.port,
1878 fpa.config_info.remote_repo_path,
1879 fpa.config_info.git_url,
1880 fpa.config_info.fetch_all_branches,
1881 fpa.config_info.mirror_references,
1882 fpa.config_info.symrefs,
1883 fpa.config_info.wanted_branches,
1884 fpa.config_info.wanted_refs, fpa.repo);
1885 if (error)
1886 goto done;
1890 if (verbosity >= 0)
1891 printf("Created %s repository '%s'\n",
1892 mirror_references ? "mirrored" : "cloned", repo_path);
1893 done:
1894 if (pack_fds) {
1895 const struct got_error *pack_err =
1896 got_repo_pack_fds_close(pack_fds);
1897 if (error == NULL)
1898 error = pack_err;
1900 if (fetchpid > 0) {
1901 if (kill(fetchpid, SIGTERM) == -1)
1902 error = got_error_from_errno("kill");
1903 if (waitpid(fetchpid, &fetchstatus, 0) == -1 && error == NULL)
1904 error = got_error_from_errno("waitpid");
1906 if (fetchfd != -1 && close(fetchfd) == -1 && error == NULL)
1907 error = got_error_from_errno("close");
1908 if (repo) {
1909 const struct got_error *close_err = got_repo_close(repo);
1910 if (error == NULL)
1911 error = close_err;
1913 got_pathlist_free(&refs, GOT_PATHLIST_FREE_ALL);
1914 got_pathlist_free(&symrefs, GOT_PATHLIST_FREE_ALL);
1915 got_pathlist_free(&wanted_branches, GOT_PATHLIST_FREE_NONE);
1916 got_pathlist_free(&wanted_refs, GOT_PATHLIST_FREE_NONE);
1917 free(pack_hash);
1918 free(proto);
1919 free(host);
1920 free(port);
1921 free(server_path);
1922 free(repo_name);
1923 free(default_destdir);
1924 free(git_url);
1925 return error;
1928 static const struct got_error *
1929 update_ref(struct got_reference *ref, struct got_object_id *new_id,
1930 int replace_tags, int verbosity, struct got_repository *repo)
1932 const struct got_error *err = NULL;
1933 char *new_id_str = NULL;
1934 struct got_object_id *old_id = NULL;
1936 err = got_object_id_str(&new_id_str, new_id);
1937 if (err)
1938 goto done;
1940 if (!replace_tags &&
1941 strncmp(got_ref_get_name(ref), "refs/tags/", 10) == 0) {
1942 err = got_ref_resolve(&old_id, repo, ref);
1943 if (err)
1944 goto done;
1945 if (got_object_id_cmp(old_id, new_id) == 0)
1946 goto done;
1947 if (verbosity >= 0) {
1948 printf("Rejecting update of existing tag %s: %s\n",
1949 got_ref_get_name(ref), new_id_str);
1951 goto done;
1954 if (got_ref_is_symbolic(ref)) {
1955 if (verbosity >= 0) {
1956 printf("Replacing reference %s: %s\n",
1957 got_ref_get_name(ref),
1958 got_ref_get_symref_target(ref));
1960 err = got_ref_change_symref_to_ref(ref, new_id);
1961 if (err)
1962 goto done;
1963 err = got_ref_write(ref, repo);
1964 if (err)
1965 goto done;
1966 } else {
1967 err = got_ref_resolve(&old_id, repo, ref);
1968 if (err)
1969 goto done;
1970 if (got_object_id_cmp(old_id, new_id) == 0)
1971 goto done;
1973 err = got_ref_change_ref(ref, new_id);
1974 if (err)
1975 goto done;
1976 err = got_ref_write(ref, repo);
1977 if (err)
1978 goto done;
1981 if (verbosity >= 0)
1982 printf("Updated %s: %s\n", got_ref_get_name(ref),
1983 new_id_str);
1984 done:
1985 free(old_id);
1986 free(new_id_str);
1987 return err;
1990 static const struct got_error *
1991 update_symref(const char *refname, struct got_reference *target_ref,
1992 int verbosity, struct got_repository *repo)
1994 const struct got_error *err = NULL, *unlock_err;
1995 struct got_reference *symref;
1996 int symref_is_locked = 0;
1998 err = got_ref_open(&symref, repo, refname, 1);
1999 if (err) {
2000 if (err->code != GOT_ERR_NOT_REF)
2001 return err;
2002 err = got_ref_alloc_symref(&symref, refname, target_ref);
2003 if (err)
2004 goto done;
2006 err = got_ref_write(symref, repo);
2007 if (err)
2008 goto done;
2010 if (verbosity >= 0)
2011 printf("Created reference %s: %s\n",
2012 got_ref_get_name(symref),
2013 got_ref_get_symref_target(symref));
2014 } else {
2015 symref_is_locked = 1;
2017 if (strcmp(got_ref_get_symref_target(symref),
2018 got_ref_get_name(target_ref)) == 0)
2019 goto done;
2021 err = got_ref_change_symref(symref,
2022 got_ref_get_name(target_ref));
2023 if (err)
2024 goto done;
2026 err = got_ref_write(symref, repo);
2027 if (err)
2028 goto done;
2030 if (verbosity >= 0)
2031 printf("Updated %s: %s\n", got_ref_get_name(symref),
2032 got_ref_get_symref_target(symref));
2035 done:
2036 if (symref_is_locked) {
2037 unlock_err = got_ref_unlock(symref);
2038 if (unlock_err && err == NULL)
2039 err = unlock_err;
2041 got_ref_close(symref);
2042 return err;
2045 __dead static void
2046 usage_fetch(void)
2048 fprintf(stderr, "usage: %s fetch [-adlqtvX] [-b branch] "
2049 "[-R reference] [-r repository-path] [remote-repository]\n",
2050 getprogname());
2051 exit(1);
2054 static const struct got_error *
2055 delete_missing_ref(struct got_reference *ref,
2056 int verbosity, struct got_repository *repo)
2058 const struct got_error *err = NULL;
2059 struct got_object_id *id = NULL;
2060 char *id_str = NULL;
2062 if (got_ref_is_symbolic(ref)) {
2063 err = got_ref_delete(ref, repo);
2064 if (err)
2065 return err;
2066 if (verbosity >= 0) {
2067 printf("Deleted %s: %s\n",
2068 got_ref_get_name(ref),
2069 got_ref_get_symref_target(ref));
2071 } else {
2072 err = got_ref_resolve(&id, repo, ref);
2073 if (err)
2074 return err;
2075 err = got_object_id_str(&id_str, id);
2076 if (err)
2077 goto done;
2079 err = got_ref_delete(ref, repo);
2080 if (err)
2081 goto done;
2082 if (verbosity >= 0) {
2083 printf("Deleted %s: %s\n",
2084 got_ref_get_name(ref), id_str);
2087 done:
2088 free(id);
2089 free(id_str);
2090 return err;
2093 static const struct got_error *
2094 delete_missing_refs(struct got_pathlist_head *their_refs,
2095 struct got_pathlist_head *their_symrefs,
2096 const struct got_remote_repo *remote,
2097 int verbosity, struct got_repository *repo)
2099 const struct got_error *err = NULL, *unlock_err;
2100 struct got_reflist_head my_refs;
2101 struct got_reflist_entry *re;
2102 struct got_pathlist_entry *pe;
2103 char *remote_namespace = NULL;
2104 char *local_refname = NULL;
2106 TAILQ_INIT(&my_refs);
2108 if (asprintf(&remote_namespace, "refs/remotes/%s/", remote->name)
2109 == -1)
2110 return got_error_from_errno("asprintf");
2112 err = got_ref_list(&my_refs, repo, NULL, got_ref_cmp_by_name, NULL);
2113 if (err)
2114 goto done;
2116 TAILQ_FOREACH(re, &my_refs, entry) {
2117 const char *refname = got_ref_get_name(re->ref);
2118 const char *their_refname;
2120 if (remote->mirror_references) {
2121 their_refname = refname;
2122 } else {
2123 if (strncmp(refname, remote_namespace,
2124 strlen(remote_namespace)) == 0) {
2125 if (strcmp(refname + strlen(remote_namespace),
2126 GOT_REF_HEAD) == 0)
2127 continue;
2128 if (asprintf(&local_refname, "refs/heads/%s",
2129 refname + strlen(remote_namespace)) == -1) {
2130 err = got_error_from_errno("asprintf");
2131 goto done;
2133 } else if (strncmp(refname, "refs/tags/", 10) != 0)
2134 continue;
2136 their_refname = local_refname;
2139 TAILQ_FOREACH(pe, their_refs, entry) {
2140 if (strcmp(their_refname, pe->path) == 0)
2141 break;
2143 if (pe != NULL)
2144 continue;
2146 TAILQ_FOREACH(pe, their_symrefs, entry) {
2147 if (strcmp(their_refname, pe->path) == 0)
2148 break;
2150 if (pe != NULL)
2151 continue;
2153 err = delete_missing_ref(re->ref, verbosity, repo);
2154 if (err)
2155 break;
2157 if (local_refname) {
2158 struct got_reference *ref;
2159 err = got_ref_open(&ref, repo, local_refname, 1);
2160 if (err) {
2161 if (err->code != GOT_ERR_NOT_REF)
2162 break;
2163 free(local_refname);
2164 local_refname = NULL;
2165 continue;
2167 err = delete_missing_ref(ref, verbosity, repo);
2168 if (err)
2169 break;
2170 unlock_err = got_ref_unlock(ref);
2171 got_ref_close(ref);
2172 if (unlock_err && err == NULL) {
2173 err = unlock_err;
2174 break;
2177 free(local_refname);
2178 local_refname = NULL;
2181 done:
2182 got_ref_list_free(&my_refs);
2183 free(remote_namespace);
2184 free(local_refname);
2185 return err;
2188 static const struct got_error *
2189 update_wanted_ref(const char *refname, struct got_object_id *id,
2190 const char *remote_repo_name, int verbosity, struct got_repository *repo)
2192 const struct got_error *err, *unlock_err;
2193 char *remote_refname;
2194 struct got_reference *ref;
2196 if (strncmp("refs/", refname, 5) == 0)
2197 refname += 5;
2199 if (asprintf(&remote_refname, "refs/remotes/%s/%s",
2200 remote_repo_name, refname) == -1)
2201 return got_error_from_errno("asprintf");
2203 err = got_ref_open(&ref, repo, remote_refname, 1);
2204 if (err) {
2205 if (err->code != GOT_ERR_NOT_REF)
2206 goto done;
2207 err = create_ref(remote_refname, id, verbosity, repo);
2208 } else {
2209 err = update_ref(ref, id, 0, verbosity, repo);
2210 unlock_err = got_ref_unlock(ref);
2211 if (unlock_err && err == NULL)
2212 err = unlock_err;
2213 got_ref_close(ref);
2215 done:
2216 free(remote_refname);
2217 return err;
2220 static const struct got_error *
2221 delete_ref(struct got_repository *repo, struct got_reference *ref)
2223 const struct got_error *err = NULL;
2224 struct got_object_id *id = NULL;
2225 char *id_str = NULL;
2226 const char *target;
2228 if (got_ref_is_symbolic(ref)) {
2229 target = got_ref_get_symref_target(ref);
2230 } else {
2231 err = got_ref_resolve(&id, repo, ref);
2232 if (err)
2233 goto done;
2234 err = got_object_id_str(&id_str, id);
2235 if (err)
2236 goto done;
2237 target = id_str;
2240 err = got_ref_delete(ref, repo);
2241 if (err)
2242 goto done;
2244 printf("Deleted %s: %s\n", got_ref_get_name(ref), target);
2245 done:
2246 free(id);
2247 free(id_str);
2248 return err;
2251 static const struct got_error *
2252 delete_refs_for_remote(struct got_repository *repo, const char *remote_name)
2254 const struct got_error *err = NULL;
2255 struct got_reflist_head refs;
2256 struct got_reflist_entry *re;
2257 char *prefix;
2259 TAILQ_INIT(&refs);
2261 if (asprintf(&prefix, "refs/remotes/%s", remote_name) == -1) {
2262 err = got_error_from_errno("asprintf");
2263 goto done;
2265 err = got_ref_list(&refs, repo, prefix, got_ref_cmp_by_name, NULL);
2266 if (err)
2267 goto done;
2269 TAILQ_FOREACH(re, &refs, entry)
2270 delete_ref(repo, re->ref);
2271 done:
2272 got_ref_list_free(&refs);
2273 return err;
2276 static const struct got_error *
2277 cmd_fetch(int argc, char *argv[])
2279 const struct got_error *error = NULL, *unlock_err;
2280 char *cwd = NULL, *repo_path = NULL;
2281 const char *remote_name;
2282 char *proto = NULL, *host = NULL, *port = NULL;
2283 char *repo_name = NULL, *server_path = NULL;
2284 const struct got_remote_repo *remotes, *remote = NULL;
2285 int nremotes;
2286 char *id_str = NULL;
2287 struct got_repository *repo = NULL;
2288 struct got_worktree *worktree = NULL;
2289 const struct got_gotconfig *repo_conf = NULL, *worktree_conf = NULL;
2290 struct got_pathlist_head refs, symrefs, wanted_branches, wanted_refs;
2291 struct got_pathlist_entry *pe;
2292 struct got_reflist_head remote_refs;
2293 struct got_reflist_entry *re;
2294 struct got_object_id *pack_hash = NULL;
2295 int i, ch, fetchfd = -1, fetchstatus;
2296 pid_t fetchpid = -1;
2297 struct got_fetch_progress_arg fpa;
2298 int verbosity = 0, fetch_all_branches = 0, list_refs_only = 0;
2299 int delete_refs = 0, replace_tags = 0, delete_remote = 0;
2300 int *pack_fds = NULL, have_bflag = 0;
2301 const char *remote_head = NULL, *worktree_branch = NULL;
2303 TAILQ_INIT(&refs);
2304 TAILQ_INIT(&symrefs);
2305 TAILQ_INIT(&remote_refs);
2306 TAILQ_INIT(&wanted_branches);
2307 TAILQ_INIT(&wanted_refs);
2309 while ((ch = getopt(argc, argv, "ab:dlqR:r:tvX")) != -1) {
2310 switch (ch) {
2311 case 'a':
2312 fetch_all_branches = 1;
2313 break;
2314 case 'b':
2315 error = got_pathlist_append(&wanted_branches,
2316 optarg, NULL);
2317 if (error)
2318 return error;
2319 have_bflag = 1;
2320 break;
2321 case 'd':
2322 delete_refs = 1;
2323 break;
2324 case 'l':
2325 list_refs_only = 1;
2326 break;
2327 case 'q':
2328 verbosity = -1;
2329 break;
2330 case 'R':
2331 error = got_pathlist_append(&wanted_refs,
2332 optarg, NULL);
2333 if (error)
2334 return error;
2335 break;
2336 case 'r':
2337 repo_path = realpath(optarg, NULL);
2338 if (repo_path == NULL)
2339 return got_error_from_errno2("realpath",
2340 optarg);
2341 got_path_strip_trailing_slashes(repo_path);
2342 break;
2343 case 't':
2344 replace_tags = 1;
2345 break;
2346 case 'v':
2347 if (verbosity < 0)
2348 verbosity = 0;
2349 else if (verbosity < 3)
2350 verbosity++;
2351 break;
2352 case 'X':
2353 delete_remote = 1;
2354 break;
2355 default:
2356 usage_fetch();
2357 break;
2360 argc -= optind;
2361 argv += optind;
2363 if (fetch_all_branches && !TAILQ_EMPTY(&wanted_branches))
2364 option_conflict('a', 'b');
2365 if (list_refs_only) {
2366 if (!TAILQ_EMPTY(&wanted_branches))
2367 option_conflict('l', 'b');
2368 if (fetch_all_branches)
2369 option_conflict('l', 'a');
2370 if (delete_refs)
2371 option_conflict('l', 'd');
2372 if (delete_remote)
2373 option_conflict('l', 'X');
2375 if (delete_remote) {
2376 if (fetch_all_branches)
2377 option_conflict('X', 'a');
2378 if (!TAILQ_EMPTY(&wanted_branches))
2379 option_conflict('X', 'b');
2380 if (delete_refs)
2381 option_conflict('X', 'd');
2382 if (replace_tags)
2383 option_conflict('X', 't');
2384 if (!TAILQ_EMPTY(&wanted_refs))
2385 option_conflict('X', 'R');
2388 if (argc == 0) {
2389 if (delete_remote)
2390 errx(1, "-X option requires a remote name");
2391 remote_name = GOT_FETCH_DEFAULT_REMOTE_NAME;
2392 } else if (argc == 1)
2393 remote_name = argv[0];
2394 else
2395 usage_fetch();
2397 cwd = getcwd(NULL, 0);
2398 if (cwd == NULL) {
2399 error = got_error_from_errno("getcwd");
2400 goto done;
2403 error = got_repo_pack_fds_open(&pack_fds);
2404 if (error != NULL)
2405 goto done;
2407 if (repo_path == NULL) {
2408 error = got_worktree_open(&worktree, cwd);
2409 if (error && error->code != GOT_ERR_NOT_WORKTREE)
2410 goto done;
2411 else
2412 error = NULL;
2413 if (worktree) {
2414 repo_path =
2415 strdup(got_worktree_get_repo_path(worktree));
2416 if (repo_path == NULL)
2417 error = got_error_from_errno("strdup");
2418 if (error)
2419 goto done;
2420 } else {
2421 repo_path = strdup(cwd);
2422 if (repo_path == NULL) {
2423 error = got_error_from_errno("strdup");
2424 goto done;
2429 error = got_repo_open(&repo, repo_path, NULL, pack_fds);
2430 if (error)
2431 goto done;
2433 if (delete_remote) {
2434 error = delete_refs_for_remote(repo, remote_name);
2435 goto done; /* nothing else to do */
2438 if (worktree) {
2439 worktree_conf = got_worktree_get_gotconfig(worktree);
2440 if (worktree_conf) {
2441 got_gotconfig_get_remotes(&nremotes, &remotes,
2442 worktree_conf);
2443 for (i = 0; i < nremotes; i++) {
2444 if (strcmp(remotes[i].name, remote_name) == 0) {
2445 remote = &remotes[i];
2446 break;
2451 if (remote == NULL) {
2452 repo_conf = got_repo_get_gotconfig(repo);
2453 if (repo_conf) {
2454 got_gotconfig_get_remotes(&nremotes, &remotes,
2455 repo_conf);
2456 for (i = 0; i < nremotes; i++) {
2457 if (strcmp(remotes[i].name, remote_name) == 0) {
2458 remote = &remotes[i];
2459 break;
2464 if (remote == NULL) {
2465 got_repo_get_gitconfig_remotes(&nremotes, &remotes, repo);
2466 for (i = 0; i < nremotes; i++) {
2467 if (strcmp(remotes[i].name, remote_name) == 0) {
2468 remote = &remotes[i];
2469 break;
2473 if (remote == NULL) {
2474 error = got_error_path(remote_name, GOT_ERR_NO_REMOTE);
2475 goto done;
2478 if (TAILQ_EMPTY(&wanted_branches)) {
2479 if (!fetch_all_branches)
2480 fetch_all_branches = remote->fetch_all_branches;
2481 for (i = 0; i < remote->nfetch_branches; i++) {
2482 error = got_pathlist_append(&wanted_branches,
2483 remote->fetch_branches[i], NULL);
2484 if (error)
2485 goto done;
2488 if (TAILQ_EMPTY(&wanted_refs)) {
2489 for (i = 0; i < remote->nfetch_refs; i++) {
2490 error = got_pathlist_append(&wanted_refs,
2491 remote->fetch_refs[i], NULL);
2492 if (error)
2493 goto done;
2497 error = got_dial_parse_uri(&proto, &host, &port, &server_path,
2498 &repo_name, remote->fetch_url);
2499 if (error)
2500 goto done;
2502 if (strcmp(proto, "git") == 0) {
2503 #ifndef PROFILE
2504 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
2505 "sendfd dns inet unveil", NULL) == -1)
2506 err(1, "pledge");
2507 #endif
2508 } else if (strcmp(proto, "git+ssh") == 0 ||
2509 strcmp(proto, "ssh") == 0) {
2510 #ifndef PROFILE
2511 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
2512 "sendfd unveil", NULL) == -1)
2513 err(1, "pledge");
2514 #endif
2515 } else if (strcmp(proto, "http") == 0 ||
2516 strcmp(proto, "git+http") == 0) {
2517 error = got_error_path(proto, GOT_ERR_NOT_IMPL);
2518 goto done;
2519 } else {
2520 error = got_error_path(proto, GOT_ERR_BAD_PROTO);
2521 goto done;
2524 error = got_dial_apply_unveil(proto);
2525 if (error)
2526 goto done;
2528 error = apply_unveil(got_repo_get_path(repo), 0, NULL);
2529 if (error)
2530 goto done;
2532 if (verbosity >= 0) {
2533 printf("Connecting to \"%s\" %s://%s%s%s%s%s\n",
2534 remote->name, proto, host,
2535 port ? ":" : "", port ? port : "",
2536 *server_path == '/' ? "" : "/", server_path);
2539 error = got_fetch_connect(&fetchpid, &fetchfd, proto, host, port,
2540 server_path, verbosity);
2541 if (error)
2542 goto done;
2544 if (!have_bflag) {
2546 * If set, get this remote's HEAD ref target so
2547 * if it has changed on the server we can fetch it.
2549 error = got_ref_list(&remote_refs, repo, "refs/remotes",
2550 got_ref_cmp_by_name, repo);
2551 if (error)
2552 goto done;
2554 TAILQ_FOREACH(re, &remote_refs, entry) {
2555 const char *remote_refname, *remote_target;
2556 size_t remote_name_len;
2558 if (!got_ref_is_symbolic(re->ref))
2559 continue;
2561 remote_name_len = strlen(remote->name);
2562 remote_refname = got_ref_get_name(re->ref);
2564 /* we only want refs/remotes/$remote->name/HEAD */
2565 if (strncmp(remote_refname + 13, remote->name,
2566 remote_name_len) != 0)
2567 continue;
2569 if (strcmp(remote_refname + remote_name_len + 14,
2570 GOT_REF_HEAD) != 0)
2571 continue;
2574 * Take the name itself because we already
2575 * only match with refs/heads/ in fetch_pack().
2577 remote_target = got_ref_get_symref_target(re->ref);
2578 remote_head = remote_target + remote_name_len + 14;
2579 break;
2582 if (worktree) {
2583 const char *refname;
2585 refname = got_worktree_get_head_ref_name(worktree);
2586 if (strncmp(refname, "refs/heads/", 11) == 0)
2587 worktree_branch = refname;
2591 fpa.last_scaled_size[0] = '\0';
2592 fpa.last_p_indexed = -1;
2593 fpa.last_p_resolved = -1;
2594 fpa.verbosity = verbosity;
2595 fpa.repo = repo;
2596 fpa.create_configs = 0;
2597 fpa.configs_created = 0;
2598 memset(&fpa.config_info, 0, sizeof(fpa.config_info));
2600 error = got_fetch_pack(&pack_hash, &refs, &symrefs, remote->name,
2601 remote->mirror_references, fetch_all_branches, &wanted_branches,
2602 &wanted_refs, list_refs_only, verbosity, fetchfd, repo,
2603 worktree_branch, remote_head, have_bflag, fetch_progress, &fpa);
2604 if (error)
2605 goto done;
2607 if (list_refs_only) {
2608 error = list_remote_refs(&symrefs, &refs);
2609 goto done;
2612 if (pack_hash == NULL) {
2613 if (verbosity >= 0)
2614 printf("Already up-to-date\n");
2615 } else if (verbosity >= 0) {
2616 error = got_object_id_str(&id_str, pack_hash);
2617 if (error)
2618 goto done;
2619 printf("\nFetched %s.pack\n", id_str);
2620 free(id_str);
2621 id_str = NULL;
2624 /* Update references provided with the pack file. */
2625 TAILQ_FOREACH(pe, &refs, entry) {
2626 const char *refname = pe->path;
2627 struct got_object_id *id = pe->data;
2628 struct got_reference *ref;
2629 char *remote_refname;
2631 if (is_wanted_ref(&wanted_refs, refname) &&
2632 !remote->mirror_references) {
2633 error = update_wanted_ref(refname, id,
2634 remote->name, verbosity, repo);
2635 if (error)
2636 goto done;
2637 continue;
2640 if (remote->mirror_references ||
2641 strncmp("refs/tags/", refname, 10) == 0) {
2642 error = got_ref_open(&ref, repo, refname, 1);
2643 if (error) {
2644 if (error->code != GOT_ERR_NOT_REF)
2645 goto done;
2646 error = create_ref(refname, id, verbosity,
2647 repo);
2648 if (error)
2649 goto done;
2650 } else {
2651 error = update_ref(ref, id, replace_tags,
2652 verbosity, repo);
2653 unlock_err = got_ref_unlock(ref);
2654 if (unlock_err && error == NULL)
2655 error = unlock_err;
2656 got_ref_close(ref);
2657 if (error)
2658 goto done;
2660 } else if (strncmp("refs/heads/", refname, 11) == 0) {
2661 if (asprintf(&remote_refname, "refs/remotes/%s/%s",
2662 remote_name, refname + 11) == -1) {
2663 error = got_error_from_errno("asprintf");
2664 goto done;
2667 error = got_ref_open(&ref, repo, remote_refname, 1);
2668 if (error) {
2669 if (error->code != GOT_ERR_NOT_REF)
2670 goto done;
2671 error = create_ref(remote_refname, id,
2672 verbosity, repo);
2673 if (error)
2674 goto done;
2675 } else {
2676 error = update_ref(ref, id, replace_tags,
2677 verbosity, repo);
2678 unlock_err = got_ref_unlock(ref);
2679 if (unlock_err && error == NULL)
2680 error = unlock_err;
2681 got_ref_close(ref);
2682 if (error)
2683 goto done;
2686 /* Also create a local branch if none exists yet. */
2687 error = got_ref_open(&ref, repo, refname, 1);
2688 if (error) {
2689 if (error->code != GOT_ERR_NOT_REF)
2690 goto done;
2691 error = create_ref(refname, id, verbosity,
2692 repo);
2693 if (error)
2694 goto done;
2695 } else {
2696 unlock_err = got_ref_unlock(ref);
2697 if (unlock_err && error == NULL)
2698 error = unlock_err;
2699 got_ref_close(ref);
2703 if (delete_refs) {
2704 error = delete_missing_refs(&refs, &symrefs, remote,
2705 verbosity, repo);
2706 if (error)
2707 goto done;
2710 if (!remote->mirror_references) {
2711 /* Update remote HEAD reference if the server provided one. */
2712 TAILQ_FOREACH(pe, &symrefs, entry) {
2713 struct got_reference *target_ref;
2714 const char *refname = pe->path;
2715 const char *target = pe->data;
2716 char *remote_refname = NULL, *remote_target = NULL;
2718 if (strcmp(refname, GOT_REF_HEAD) != 0)
2719 continue;
2721 if (strncmp("refs/heads/", target, 11) != 0)
2722 continue;
2724 if (asprintf(&remote_refname, "refs/remotes/%s/%s",
2725 remote->name, refname) == -1) {
2726 error = got_error_from_errno("asprintf");
2727 goto done;
2729 if (asprintf(&remote_target, "refs/remotes/%s/%s",
2730 remote->name, target + 11) == -1) {
2731 error = got_error_from_errno("asprintf");
2732 free(remote_refname);
2733 goto done;
2736 error = got_ref_open(&target_ref, repo, remote_target,
2737 0);
2738 if (error) {
2739 free(remote_refname);
2740 free(remote_target);
2741 if (error->code == GOT_ERR_NOT_REF) {
2742 error = NULL;
2743 continue;
2745 goto done;
2747 error = update_symref(remote_refname, target_ref,
2748 verbosity, repo);
2749 free(remote_refname);
2750 free(remote_target);
2751 got_ref_close(target_ref);
2752 if (error)
2753 goto done;
2756 done:
2757 if (fetchpid > 0) {
2758 if (kill(fetchpid, SIGTERM) == -1)
2759 error = got_error_from_errno("kill");
2760 if (waitpid(fetchpid, &fetchstatus, 0) == -1 && error == NULL)
2761 error = got_error_from_errno("waitpid");
2763 if (fetchfd != -1 && close(fetchfd) == -1 && error == NULL)
2764 error = got_error_from_errno("close");
2765 if (repo) {
2766 const struct got_error *close_err = got_repo_close(repo);
2767 if (error == NULL)
2768 error = close_err;
2770 if (worktree)
2771 got_worktree_close(worktree);
2772 if (pack_fds) {
2773 const struct got_error *pack_err =
2774 got_repo_pack_fds_close(pack_fds);
2775 if (error == NULL)
2776 error = pack_err;
2778 got_pathlist_free(&refs, GOT_PATHLIST_FREE_ALL);
2779 got_pathlist_free(&symrefs, GOT_PATHLIST_FREE_ALL);
2780 got_pathlist_free(&wanted_branches, GOT_PATHLIST_FREE_NONE);
2781 got_pathlist_free(&wanted_refs, GOT_PATHLIST_FREE_NONE);
2782 got_ref_list_free(&remote_refs);
2783 free(id_str);
2784 free(cwd);
2785 free(repo_path);
2786 free(pack_hash);
2787 free(proto);
2788 free(host);
2789 free(port);
2790 free(server_path);
2791 free(repo_name);
2792 return error;
2796 __dead static void
2797 usage_checkout(void)
2799 fprintf(stderr, "usage: %s checkout [-Eq] [-b branch] [-c commit] "
2800 "[-p path-prefix] repository-path [work-tree-path]\n",
2801 getprogname());
2802 exit(1);
2805 static void
2806 show_worktree_base_ref_warning(void)
2808 fprintf(stderr, "%s: warning: could not create a reference "
2809 "to the work tree's base commit; the commit could be "
2810 "garbage-collected by Git or 'gotadmin cleanup'; making the "
2811 "repository writable and running 'got update' will prevent this\n",
2812 getprogname());
2815 struct got_checkout_progress_arg {
2816 const char *worktree_path;
2817 int had_base_commit_ref_error;
2818 int verbosity;
2821 static const struct got_error *
2822 checkout_progress(void *arg, unsigned char status, const char *path)
2824 struct got_checkout_progress_arg *a = arg;
2826 /* Base commit bump happens silently. */
2827 if (status == GOT_STATUS_BUMP_BASE)
2828 return NULL;
2830 if (status == GOT_STATUS_BASE_REF_ERR) {
2831 a->had_base_commit_ref_error = 1;
2832 return NULL;
2835 while (path[0] == '/')
2836 path++;
2838 if (a->verbosity >= 0)
2839 printf("%c %s/%s\n", status, a->worktree_path, path);
2841 return NULL;
2844 static const struct got_error *
2845 check_cancelled(void *arg)
2847 if (sigint_received || sigpipe_received)
2848 return got_error(GOT_ERR_CANCELLED);
2849 return NULL;
2852 static const struct got_error *
2853 check_linear_ancestry(struct got_object_id *commit_id,
2854 struct got_object_id *base_commit_id, int allow_forwards_in_time_only,
2855 struct got_repository *repo)
2857 const struct got_error *err = NULL;
2858 struct got_object_id *yca_id;
2860 err = got_commit_graph_find_youngest_common_ancestor(&yca_id,
2861 commit_id, base_commit_id, 1, repo, check_cancelled, NULL);
2862 if (err)
2863 return err;
2865 if (yca_id == NULL)
2866 return got_error(GOT_ERR_ANCESTRY);
2869 * Require a straight line of history between the target commit
2870 * and the work tree's base commit.
2872 * Non-linear situations such as this require a rebase:
2874 * (commit) D F (base_commit)
2875 * \ /
2876 * C E
2877 * \ /
2878 * B (yca)
2879 * |
2880 * A
2882 * 'got update' only handles linear cases:
2883 * Update forwards in time: A (base/yca) - B - C - D (commit)
2884 * Update backwards in time: D (base) - C - B - A (commit/yca)
2886 if (allow_forwards_in_time_only) {
2887 if (got_object_id_cmp(base_commit_id, yca_id) != 0)
2888 return got_error(GOT_ERR_ANCESTRY);
2889 } else if (got_object_id_cmp(commit_id, yca_id) != 0 &&
2890 got_object_id_cmp(base_commit_id, yca_id) != 0)
2891 return got_error(GOT_ERR_ANCESTRY);
2893 free(yca_id);
2894 return NULL;
2897 static const struct got_error *
2898 check_same_branch(struct got_object_id *commit_id,
2899 struct got_reference *head_ref, struct got_object_id *yca_id,
2900 struct got_repository *repo)
2902 const struct got_error *err = NULL;
2903 struct got_commit_graph *graph = NULL;
2904 struct got_object_id *head_commit_id = NULL;
2905 int is_same_branch = 0;
2907 err = got_ref_resolve(&head_commit_id, repo, head_ref);
2908 if (err)
2909 goto done;
2911 if (got_object_id_cmp(head_commit_id, commit_id) == 0) {
2912 is_same_branch = 1;
2913 goto done;
2915 if (yca_id && got_object_id_cmp(commit_id, yca_id) == 0) {
2916 is_same_branch = 1;
2917 goto done;
2920 err = got_commit_graph_open(&graph, "/", 1);
2921 if (err)
2922 goto done;
2924 err = got_commit_graph_iter_start(graph, head_commit_id, repo,
2925 check_cancelled, NULL);
2926 if (err)
2927 goto done;
2929 for (;;) {
2930 struct got_object_id id;
2932 err = got_commit_graph_iter_next(&id, graph, repo,
2933 check_cancelled, NULL);
2934 if (err) {
2935 if (err->code == GOT_ERR_ITER_COMPLETED)
2936 err = NULL;
2937 break;
2940 if (yca_id && got_object_id_cmp(&id, yca_id) == 0)
2941 break;
2942 if (got_object_id_cmp(&id, commit_id) == 0) {
2943 is_same_branch = 1;
2944 break;
2947 done:
2948 if (graph)
2949 got_commit_graph_close(graph);
2950 free(head_commit_id);
2951 if (!err && !is_same_branch)
2952 err = got_error(GOT_ERR_ANCESTRY);
2953 return err;
2956 static const struct got_error *
2957 checkout_ancestry_error(struct got_reference *ref, const char *commit_id_str)
2959 static char msg[512];
2960 const char *branch_name;
2962 if (got_ref_is_symbolic(ref))
2963 branch_name = got_ref_get_symref_target(ref);
2964 else
2965 branch_name = got_ref_get_name(ref);
2967 if (strncmp("refs/heads/", branch_name, 11) == 0)
2968 branch_name += 11;
2970 snprintf(msg, sizeof(msg),
2971 "target commit is not contained in branch '%s'; "
2972 "the branch to use must be specified with -b; "
2973 "if necessary a new branch can be created for "
2974 "this commit with 'got branch -c %s BRANCH_NAME'",
2975 branch_name, commit_id_str);
2977 return got_error_msg(GOT_ERR_ANCESTRY, msg);
2980 static const struct got_error *
2981 cmd_checkout(int argc, char *argv[])
2983 const struct got_error *error = NULL;
2984 struct got_repository *repo = NULL;
2985 struct got_reference *head_ref = NULL, *ref = NULL;
2986 struct got_worktree *worktree = NULL;
2987 char *repo_path = NULL;
2988 char *worktree_path = NULL;
2989 const char *path_prefix = "";
2990 const char *branch_name = GOT_REF_HEAD, *refname = NULL;
2991 char *commit_id_str = NULL;
2992 struct got_object_id *commit_id = NULL;
2993 char *cwd = NULL;
2994 int ch, same_path_prefix, allow_nonempty = 0, verbosity = 0;
2995 struct got_pathlist_head paths;
2996 struct got_checkout_progress_arg cpa;
2997 int *pack_fds = NULL;
2999 TAILQ_INIT(&paths);
3001 #ifndef PROFILE
3002 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
3003 "unveil", NULL) == -1)
3004 err(1, "pledge");
3005 #endif
3007 while ((ch = getopt(argc, argv, "b:c:Ep:q")) != -1) {
3008 switch (ch) {
3009 case 'b':
3010 branch_name = optarg;
3011 break;
3012 case 'c':
3013 commit_id_str = strdup(optarg);
3014 if (commit_id_str == NULL)
3015 return got_error_from_errno("strdup");
3016 break;
3017 case 'E':
3018 allow_nonempty = 1;
3019 break;
3020 case 'p':
3021 path_prefix = optarg;
3022 break;
3023 case 'q':
3024 verbosity = -1;
3025 break;
3026 default:
3027 usage_checkout();
3028 /* NOTREACHED */
3032 argc -= optind;
3033 argv += optind;
3035 if (argc == 1) {
3036 char *base, *dotgit;
3037 const char *path;
3038 repo_path = realpath(argv[0], NULL);
3039 if (repo_path == NULL)
3040 return got_error_from_errno2("realpath", argv[0]);
3041 cwd = getcwd(NULL, 0);
3042 if (cwd == NULL) {
3043 error = got_error_from_errno("getcwd");
3044 goto done;
3046 if (path_prefix[0])
3047 path = path_prefix;
3048 else
3049 path = repo_path;
3050 error = got_path_basename(&base, path);
3051 if (error)
3052 goto done;
3053 dotgit = strstr(base, ".git");
3054 if (dotgit)
3055 *dotgit = '\0';
3056 if (asprintf(&worktree_path, "%s/%s", cwd, base) == -1) {
3057 error = got_error_from_errno("asprintf");
3058 free(base);
3059 goto done;
3061 free(base);
3062 } else if (argc == 2) {
3063 repo_path = realpath(argv[0], NULL);
3064 if (repo_path == NULL) {
3065 error = got_error_from_errno2("realpath", argv[0]);
3066 goto done;
3068 worktree_path = realpath(argv[1], NULL);
3069 if (worktree_path == NULL) {
3070 if (errno != ENOENT) {
3071 error = got_error_from_errno2("realpath",
3072 argv[1]);
3073 goto done;
3075 worktree_path = strdup(argv[1]);
3076 if (worktree_path == NULL) {
3077 error = got_error_from_errno("strdup");
3078 goto done;
3081 } else
3082 usage_checkout();
3084 got_path_strip_trailing_slashes(repo_path);
3085 got_path_strip_trailing_slashes(worktree_path);
3087 error = got_repo_pack_fds_open(&pack_fds);
3088 if (error != NULL)
3089 goto done;
3091 error = got_repo_open(&repo, repo_path, NULL, pack_fds);
3092 if (error != NULL)
3093 goto done;
3095 /* Pre-create work tree path for unveil(2) */
3096 error = got_path_mkdir(worktree_path);
3097 if (error) {
3098 if (!(error->code == GOT_ERR_ERRNO && errno == EISDIR) &&
3099 !(error->code == GOT_ERR_ERRNO && errno == EEXIST))
3100 goto done;
3101 if (!allow_nonempty &&
3102 !got_path_dir_is_empty(worktree_path)) {
3103 error = got_error_path(worktree_path,
3104 GOT_ERR_DIR_NOT_EMPTY);
3105 goto done;
3109 error = apply_unveil(got_repo_get_path(repo), 0, worktree_path);
3110 if (error)
3111 goto done;
3113 error = got_ref_open(&head_ref, repo, branch_name, 0);
3114 if (error != NULL)
3115 goto done;
3117 error = got_worktree_init(worktree_path, head_ref, path_prefix, repo);
3118 if (error != NULL && !(error->code == GOT_ERR_ERRNO && errno == EEXIST))
3119 goto done;
3121 error = got_worktree_open(&worktree, worktree_path);
3122 if (error != NULL)
3123 goto done;
3125 error = got_worktree_match_path_prefix(&same_path_prefix, worktree,
3126 path_prefix);
3127 if (error != NULL)
3128 goto done;
3129 if (!same_path_prefix) {
3130 error = got_error(GOT_ERR_PATH_PREFIX);
3131 goto done;
3134 if (commit_id_str) {
3135 struct got_reflist_head refs;
3136 TAILQ_INIT(&refs);
3137 error = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name,
3138 NULL);
3139 if (error)
3140 goto done;
3141 error = got_repo_match_object_id(&commit_id, NULL,
3142 commit_id_str, GOT_OBJ_TYPE_COMMIT, &refs, repo);
3143 got_ref_list_free(&refs);
3144 if (error)
3145 goto done;
3146 error = check_linear_ancestry(commit_id,
3147 got_worktree_get_base_commit_id(worktree), 0, repo);
3148 if (error != NULL) {
3149 if (error->code == GOT_ERR_ANCESTRY) {
3150 error = checkout_ancestry_error(
3151 head_ref, commit_id_str);
3153 goto done;
3155 error = check_same_branch(commit_id, head_ref, NULL, repo);
3156 if (error) {
3157 if (error->code == GOT_ERR_ANCESTRY) {
3158 error = checkout_ancestry_error(
3159 head_ref, commit_id_str);
3161 goto done;
3163 error = got_worktree_set_base_commit_id(worktree, repo,
3164 commit_id);
3165 if (error)
3166 goto done;
3167 /* Expand potentially abbreviated commit ID string. */
3168 free(commit_id_str);
3169 error = got_object_id_str(&commit_id_str, commit_id);
3170 if (error)
3171 goto done;
3172 } else {
3173 commit_id = got_object_id_dup(
3174 got_worktree_get_base_commit_id(worktree));
3175 if (commit_id == NULL) {
3176 error = got_error_from_errno("got_object_id_dup");
3177 goto done;
3179 error = got_object_id_str(&commit_id_str, commit_id);
3180 if (error)
3181 goto done;
3184 error = got_pathlist_append(&paths, "", NULL);
3185 if (error)
3186 goto done;
3187 cpa.worktree_path = worktree_path;
3188 cpa.had_base_commit_ref_error = 0;
3189 cpa.verbosity = verbosity;
3190 error = got_worktree_checkout_files(worktree, &paths, repo,
3191 checkout_progress, &cpa, check_cancelled, NULL);
3192 if (error != NULL)
3193 goto done;
3195 if (got_ref_is_symbolic(head_ref)) {
3196 error = got_ref_resolve_symbolic(&ref, repo, head_ref);
3197 if (error)
3198 goto done;
3199 refname = got_ref_get_name(ref);
3200 } else
3201 refname = got_ref_get_name(head_ref);
3202 printf("Checked out %s: %s\n", refname, commit_id_str);
3203 printf("Now shut up and hack\n");
3204 if (cpa.had_base_commit_ref_error)
3205 show_worktree_base_ref_warning();
3206 done:
3207 if (pack_fds) {
3208 const struct got_error *pack_err =
3209 got_repo_pack_fds_close(pack_fds);
3210 if (error == NULL)
3211 error = pack_err;
3213 if (head_ref)
3214 got_ref_close(head_ref);
3215 if (ref)
3216 got_ref_close(ref);
3217 got_pathlist_free(&paths, GOT_PATHLIST_FREE_NONE);
3218 free(commit_id_str);
3219 free(commit_id);
3220 free(repo_path);
3221 free(worktree_path);
3222 free(cwd);
3223 return error;
3226 struct got_update_progress_arg {
3227 int did_something;
3228 int conflicts;
3229 int obstructed;
3230 int not_updated;
3231 int missing;
3232 int not_deleted;
3233 int unversioned;
3234 int verbosity;
3237 static void
3238 print_update_progress_stats(struct got_update_progress_arg *upa)
3240 if (!upa->did_something)
3241 return;
3243 if (upa->conflicts > 0)
3244 printf("Files with new merge conflicts: %d\n", upa->conflicts);
3245 if (upa->obstructed > 0)
3246 printf("File paths obstructed by a non-regular file: %d\n",
3247 upa->obstructed);
3248 if (upa->not_updated > 0)
3249 printf("Files not updated because of existing merge "
3250 "conflicts: %d\n", upa->not_updated);
3254 * The meaning of some status codes differs between merge-style operations and
3255 * update operations. For example, the ! status code means "file was missing"
3256 * if changes were merged into the work tree, and "missing file was restored"
3257 * if the work tree was updated. This function should be used by any operation
3258 * which merges changes into the work tree without updating the work tree.
3260 static void
3261 print_merge_progress_stats(struct got_update_progress_arg *upa)
3263 if (!upa->did_something)
3264 return;
3266 if (upa->conflicts > 0)
3267 printf("Files with new merge conflicts: %d\n", upa->conflicts);
3268 if (upa->obstructed > 0)
3269 printf("File paths obstructed by a non-regular file: %d\n",
3270 upa->obstructed);
3271 if (upa->missing > 0)
3272 printf("Files which had incoming changes but could not be "
3273 "found in the work tree: %d\n", upa->missing);
3274 if (upa->not_deleted > 0)
3275 printf("Files not deleted due to differences in deleted "
3276 "content: %d\n", upa->not_deleted);
3277 if (upa->unversioned > 0)
3278 printf("Files not merged because an unversioned file was "
3279 "found in the work tree: %d\n", upa->unversioned);
3282 __dead static void
3283 usage_update(void)
3285 fprintf(stderr, "usage: %s update [-q] [-b branch] [-c commit] "
3286 "[path ...]\n", getprogname());
3287 exit(1);
3290 static const struct got_error *
3291 update_progress(void *arg, unsigned char status, const char *path)
3293 struct got_update_progress_arg *upa = arg;
3295 if (status == GOT_STATUS_EXISTS ||
3296 status == GOT_STATUS_BASE_REF_ERR)
3297 return NULL;
3299 upa->did_something = 1;
3301 /* Base commit bump happens silently. */
3302 if (status == GOT_STATUS_BUMP_BASE)
3303 return NULL;
3305 if (status == GOT_STATUS_CONFLICT)
3306 upa->conflicts++;
3307 if (status == GOT_STATUS_OBSTRUCTED)
3308 upa->obstructed++;
3309 if (status == GOT_STATUS_CANNOT_UPDATE)
3310 upa->not_updated++;
3311 if (status == GOT_STATUS_MISSING)
3312 upa->missing++;
3313 if (status == GOT_STATUS_CANNOT_DELETE)
3314 upa->not_deleted++;
3315 if (status == GOT_STATUS_UNVERSIONED)
3316 upa->unversioned++;
3318 while (path[0] == '/')
3319 path++;
3320 if (upa->verbosity >= 0)
3321 printf("%c %s\n", status, path);
3323 return NULL;
3326 static const struct got_error *
3327 switch_head_ref(struct got_reference *head_ref,
3328 struct got_object_id *commit_id, struct got_worktree *worktree,
3329 struct got_repository *repo)
3331 const struct got_error *err = NULL;
3332 char *base_id_str;
3333 int ref_has_moved = 0;
3335 /* Trivial case: switching between two different references. */
3336 if (strcmp(got_ref_get_name(head_ref),
3337 got_worktree_get_head_ref_name(worktree)) != 0) {
3338 printf("Switching work tree from %s to %s\n",
3339 got_worktree_get_head_ref_name(worktree),
3340 got_ref_get_name(head_ref));
3341 return got_worktree_set_head_ref(worktree, head_ref);
3344 err = check_linear_ancestry(commit_id,
3345 got_worktree_get_base_commit_id(worktree), 0, repo);
3346 if (err) {
3347 if (err->code != GOT_ERR_ANCESTRY)
3348 return err;
3349 ref_has_moved = 1;
3351 if (!ref_has_moved)
3352 return NULL;
3354 /* Switching to a rebased branch with the same reference name. */
3355 err = got_object_id_str(&base_id_str,
3356 got_worktree_get_base_commit_id(worktree));
3357 if (err)
3358 return err;
3359 printf("Reference %s now points at a different branch\n",
3360 got_worktree_get_head_ref_name(worktree));
3361 printf("Switching work tree from %s to %s\n", base_id_str,
3362 got_worktree_get_head_ref_name(worktree));
3363 return NULL;
3366 static const struct got_error *
3367 check_rebase_or_histedit_in_progress(struct got_worktree *worktree)
3369 const struct got_error *err;
3370 int in_progress;
3372 err = got_worktree_rebase_in_progress(&in_progress, worktree);
3373 if (err)
3374 return err;
3375 if (in_progress)
3376 return got_error(GOT_ERR_REBASING);
3378 err = got_worktree_histedit_in_progress(&in_progress, worktree);
3379 if (err)
3380 return err;
3381 if (in_progress)
3382 return got_error(GOT_ERR_HISTEDIT_BUSY);
3384 return NULL;
3387 static const struct got_error *
3388 check_merge_in_progress(struct got_worktree *worktree,
3389 struct got_repository *repo)
3391 const struct got_error *err;
3392 int in_progress;
3394 err = got_worktree_merge_in_progress(&in_progress, worktree, repo);
3395 if (err)
3396 return err;
3397 if (in_progress)
3398 return got_error(GOT_ERR_MERGE_BUSY);
3400 return NULL;
3403 static const struct got_error *
3404 get_worktree_paths_from_argv(struct got_pathlist_head *paths, int argc,
3405 char *argv[], struct got_worktree *worktree)
3407 const struct got_error *err = NULL;
3408 char *path;
3409 struct got_pathlist_entry *new;
3410 int i;
3412 if (argc == 0) {
3413 path = strdup("");
3414 if (path == NULL)
3415 return got_error_from_errno("strdup");
3416 return got_pathlist_append(paths, path, NULL);
3419 for (i = 0; i < argc; i++) {
3420 err = got_worktree_resolve_path(&path, worktree, argv[i]);
3421 if (err)
3422 break;
3423 err = got_pathlist_insert(&new, paths, path, NULL);
3424 if (err || new == NULL /* duplicate */) {
3425 free(path);
3426 if (err)
3427 break;
3431 return err;
3434 static const struct got_error *
3435 wrap_not_worktree_error(const struct got_error *orig_err,
3436 const char *cmdname, const char *path)
3438 const struct got_error *err;
3439 struct got_repository *repo;
3440 static char msg[512];
3441 int *pack_fds = NULL;
3443 err = got_repo_pack_fds_open(&pack_fds);
3444 if (err)
3445 return err;
3447 err = got_repo_open(&repo, path, NULL, pack_fds);
3448 if (err)
3449 return orig_err;
3451 snprintf(msg, sizeof(msg),
3452 "'got %s' needs a work tree in addition to a git repository\n"
3453 "Work trees can be checked out from this Git repository with "
3454 "'got checkout'.\n"
3455 "The got(1) manual page contains more information.", cmdname);
3456 err = got_error_msg(GOT_ERR_NOT_WORKTREE, msg);
3457 got_repo_close(repo);
3458 if (pack_fds) {
3459 const struct got_error *pack_err =
3460 got_repo_pack_fds_close(pack_fds);
3461 if (err == NULL)
3462 err = pack_err;
3464 return err;
3467 static const struct got_error *
3468 cmd_update(int argc, char *argv[])
3470 const struct got_error *error = NULL;
3471 struct got_repository *repo = NULL;
3472 struct got_worktree *worktree = NULL;
3473 char *worktree_path = NULL;
3474 struct got_object_id *commit_id = NULL;
3475 char *commit_id_str = NULL;
3476 const char *branch_name = NULL;
3477 struct got_reference *head_ref = NULL;
3478 struct got_pathlist_head paths;
3479 struct got_pathlist_entry *pe;
3480 int ch, verbosity = 0;
3481 struct got_update_progress_arg upa;
3482 int *pack_fds = NULL;
3484 TAILQ_INIT(&paths);
3486 #ifndef PROFILE
3487 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
3488 "unveil", NULL) == -1)
3489 err(1, "pledge");
3490 #endif
3492 while ((ch = getopt(argc, argv, "b:c:q")) != -1) {
3493 switch (ch) {
3494 case 'b':
3495 branch_name = optarg;
3496 break;
3497 case 'c':
3498 commit_id_str = strdup(optarg);
3499 if (commit_id_str == NULL)
3500 return got_error_from_errno("strdup");
3501 break;
3502 case 'q':
3503 verbosity = -1;
3504 break;
3505 default:
3506 usage_update();
3507 /* NOTREACHED */
3511 argc -= optind;
3512 argv += optind;
3514 worktree_path = getcwd(NULL, 0);
3515 if (worktree_path == NULL) {
3516 error = got_error_from_errno("getcwd");
3517 goto done;
3520 error = got_repo_pack_fds_open(&pack_fds);
3521 if (error != NULL)
3522 goto done;
3524 error = got_worktree_open(&worktree, worktree_path);
3525 if (error) {
3526 if (error->code == GOT_ERR_NOT_WORKTREE)
3527 error = wrap_not_worktree_error(error, "update",
3528 worktree_path);
3529 goto done;
3532 error = check_rebase_or_histedit_in_progress(worktree);
3533 if (error)
3534 goto done;
3536 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
3537 NULL, pack_fds);
3538 if (error != NULL)
3539 goto done;
3541 error = apply_unveil(got_repo_get_path(repo), 0,
3542 got_worktree_get_root_path(worktree));
3543 if (error)
3544 goto done;
3546 error = check_merge_in_progress(worktree, repo);
3547 if (error)
3548 goto done;
3550 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
3551 if (error)
3552 goto done;
3554 error = got_ref_open(&head_ref, repo, branch_name ? branch_name :
3555 got_worktree_get_head_ref_name(worktree), 0);
3556 if (error != NULL)
3557 goto done;
3558 if (commit_id_str == NULL) {
3559 error = got_ref_resolve(&commit_id, repo, head_ref);
3560 if (error != NULL)
3561 goto done;
3562 error = got_object_id_str(&commit_id_str, commit_id);
3563 if (error != NULL)
3564 goto done;
3565 } else {
3566 struct got_reflist_head refs;
3567 TAILQ_INIT(&refs);
3568 error = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name,
3569 NULL);
3570 if (error)
3571 goto done;
3572 error = got_repo_match_object_id(&commit_id, NULL,
3573 commit_id_str, GOT_OBJ_TYPE_COMMIT, &refs, repo);
3574 got_ref_list_free(&refs);
3575 free(commit_id_str);
3576 commit_id_str = NULL;
3577 if (error)
3578 goto done;
3579 error = got_object_id_str(&commit_id_str, commit_id);
3580 if (error)
3581 goto done;
3584 if (branch_name) {
3585 struct got_object_id *head_commit_id;
3586 TAILQ_FOREACH(pe, &paths, entry) {
3587 if (pe->path_len == 0)
3588 continue;
3589 error = got_error_msg(GOT_ERR_BAD_PATH,
3590 "switching between branches requires that "
3591 "the entire work tree gets updated");
3592 goto done;
3594 error = got_ref_resolve(&head_commit_id, repo, head_ref);
3595 if (error)
3596 goto done;
3597 error = check_linear_ancestry(commit_id, head_commit_id, 0,
3598 repo);
3599 free(head_commit_id);
3600 if (error != NULL)
3601 goto done;
3602 error = check_same_branch(commit_id, head_ref, NULL, repo);
3603 if (error)
3604 goto done;
3605 error = switch_head_ref(head_ref, commit_id, worktree, repo);
3606 if (error)
3607 goto done;
3608 } else {
3609 error = check_linear_ancestry(commit_id,
3610 got_worktree_get_base_commit_id(worktree), 0, repo);
3611 if (error != NULL) {
3612 if (error->code == GOT_ERR_ANCESTRY)
3613 error = got_error(GOT_ERR_BRANCH_MOVED);
3614 goto done;
3616 error = check_same_branch(commit_id, head_ref, NULL, repo);
3617 if (error)
3618 goto done;
3621 if (got_object_id_cmp(got_worktree_get_base_commit_id(worktree),
3622 commit_id) != 0) {
3623 error = got_worktree_set_base_commit_id(worktree, repo,
3624 commit_id);
3625 if (error)
3626 goto done;
3629 memset(&upa, 0, sizeof(upa));
3630 upa.verbosity = verbosity;
3631 error = got_worktree_checkout_files(worktree, &paths, repo,
3632 update_progress, &upa, check_cancelled, NULL);
3633 if (error != NULL)
3634 goto done;
3636 if (upa.did_something) {
3637 printf("Updated to %s: %s\n",
3638 got_worktree_get_head_ref_name(worktree), commit_id_str);
3639 } else
3640 printf("Already up-to-date\n");
3642 print_update_progress_stats(&upa);
3643 done:
3644 if (pack_fds) {
3645 const struct got_error *pack_err =
3646 got_repo_pack_fds_close(pack_fds);
3647 if (error == NULL)
3648 error = pack_err;
3650 free(worktree_path);
3651 got_pathlist_free(&paths, GOT_PATHLIST_FREE_PATH);
3652 free(commit_id);
3653 free(commit_id_str);
3654 return error;
3657 static const struct got_error *
3658 diff_blobs(struct got_object_id *blob_id1, struct got_object_id *blob_id2,
3659 const char *path, int diff_context, int ignore_whitespace,
3660 int force_text_diff, struct got_diffstat_cb_arg *dsa,
3661 struct got_repository *repo, FILE *outfile)
3663 const struct got_error *err = NULL;
3664 struct got_blob_object *blob1 = NULL, *blob2 = NULL;
3665 FILE *f1 = NULL, *f2 = NULL;
3666 int fd1 = -1, fd2 = -1;
3668 fd1 = got_opentempfd();
3669 if (fd1 == -1)
3670 return got_error_from_errno("got_opentempfd");
3671 fd2 = got_opentempfd();
3672 if (fd2 == -1) {
3673 err = got_error_from_errno("got_opentempfd");
3674 goto done;
3677 if (blob_id1) {
3678 err = got_object_open_as_blob(&blob1, repo, blob_id1, 8192,
3679 fd1);
3680 if (err)
3681 goto done;
3684 err = got_object_open_as_blob(&blob2, repo, blob_id2, 8192, fd2);
3685 if (err)
3686 goto done;
3688 f1 = got_opentemp();
3689 if (f1 == NULL) {
3690 err = got_error_from_errno("got_opentemp");
3691 goto done;
3693 f2 = got_opentemp();
3694 if (f2 == NULL) {
3695 err = got_error_from_errno("got_opentemp");
3696 goto done;
3699 while (path[0] == '/')
3700 path++;
3701 err = got_diff_blob(NULL, NULL, blob1, blob2, f1, f2, path, path,
3702 GOT_DIFF_ALGORITHM_PATIENCE, diff_context, ignore_whitespace,
3703 force_text_diff, dsa, outfile);
3704 done:
3705 if (fd1 != -1 && close(fd1) == -1 && err == NULL)
3706 err = got_error_from_errno("close");
3707 if (blob1)
3708 got_object_blob_close(blob1);
3709 if (fd2 != -1 && close(fd2) == -1 && err == NULL)
3710 err = got_error_from_errno("close");
3711 got_object_blob_close(blob2);
3712 if (f1 && fclose(f1) == EOF && err == NULL)
3713 err = got_error_from_errno("fclose");
3714 if (f2 && fclose(f2) == EOF && err == NULL)
3715 err = got_error_from_errno("fclose");
3716 return err;
3719 static const struct got_error *
3720 diff_trees(struct got_object_id *tree_id1, struct got_object_id *tree_id2,
3721 const char *path, int diff_context, int ignore_whitespace,
3722 int force_text_diff, struct got_diffstat_cb_arg *dsa,
3723 struct got_repository *repo, FILE *outfile)
3725 const struct got_error *err = NULL;
3726 struct got_tree_object *tree1 = NULL, *tree2 = NULL;
3727 struct got_diff_blob_output_unidiff_arg arg;
3728 FILE *f1 = NULL, *f2 = NULL;
3729 int fd1 = -1, fd2 = -1;
3731 if (tree_id1) {
3732 err = got_object_open_as_tree(&tree1, repo, tree_id1);
3733 if (err)
3734 goto done;
3735 fd1 = got_opentempfd();
3736 if (fd1 == -1) {
3737 err = got_error_from_errno("got_opentempfd");
3738 goto done;
3742 err = got_object_open_as_tree(&tree2, repo, tree_id2);
3743 if (err)
3744 goto done;
3746 f1 = got_opentemp();
3747 if (f1 == NULL) {
3748 err = got_error_from_errno("got_opentemp");
3749 goto done;
3752 f2 = got_opentemp();
3753 if (f2 == NULL) {
3754 err = got_error_from_errno("got_opentemp");
3755 goto done;
3757 fd2 = got_opentempfd();
3758 if (fd2 == -1) {
3759 err = got_error_from_errno("got_opentempfd");
3760 goto done;
3762 arg.diff_context = diff_context;
3763 arg.ignore_whitespace = ignore_whitespace;
3764 arg.force_text_diff = force_text_diff;
3765 arg.diffstat = dsa;
3766 arg.diff_algo = GOT_DIFF_ALGORITHM_PATIENCE;
3767 arg.outfile = outfile;
3768 arg.lines = NULL;
3769 arg.nlines = 0;
3770 while (path[0] == '/')
3771 path++;
3772 err = got_diff_tree(tree1, tree2, f1, f2, fd1, fd2, path, path, repo,
3773 got_diff_blob_output_unidiff, &arg, 1);
3774 done:
3775 if (tree1)
3776 got_object_tree_close(tree1);
3777 if (tree2)
3778 got_object_tree_close(tree2);
3779 if (f1 && fclose(f1) == EOF && err == NULL)
3780 err = got_error_from_errno("fclose");
3781 if (f2 && fclose(f2) == EOF && err == NULL)
3782 err = got_error_from_errno("fclose");
3783 if (fd1 != -1 && close(fd1) == -1 && err == NULL)
3784 err = got_error_from_errno("close");
3785 if (fd2 != -1 && close(fd2) == -1 && err == NULL)
3786 err = got_error_from_errno("close");
3787 return err;
3790 static const struct got_error *
3791 get_changed_paths(struct got_pathlist_head *paths,
3792 struct got_commit_object *commit, struct got_repository *repo,
3793 struct got_diffstat_cb_arg *dsa)
3795 const struct got_error *err = NULL;
3796 struct got_object_id *tree_id1 = NULL, *tree_id2 = NULL;
3797 struct got_tree_object *tree1 = NULL, *tree2 = NULL;
3798 struct got_object_qid *qid;
3799 got_diff_blob_cb cb = got_diff_tree_collect_changed_paths;
3800 FILE *f1 = NULL, *f2 = NULL;
3801 int fd1 = -1, fd2 = -1;
3803 if (dsa) {
3804 cb = got_diff_tree_compute_diffstat;
3806 f1 = got_opentemp();
3807 if (f1 == NULL) {
3808 err = got_error_from_errno("got_opentemp");
3809 goto done;
3811 f2 = got_opentemp();
3812 if (f2 == NULL) {
3813 err = got_error_from_errno("got_opentemp");
3814 goto done;
3816 fd1 = got_opentempfd();
3817 if (fd1 == -1) {
3818 err = got_error_from_errno("got_opentempfd");
3819 goto done;
3821 fd2 = got_opentempfd();
3822 if (fd2 == -1) {
3823 err = got_error_from_errno("got_opentempfd");
3824 goto done;
3828 qid = STAILQ_FIRST(got_object_commit_get_parent_ids(commit));
3829 if (qid != NULL) {
3830 struct got_commit_object *pcommit;
3831 err = got_object_open_as_commit(&pcommit, repo,
3832 &qid->id);
3833 if (err)
3834 return err;
3836 tree_id1 = got_object_id_dup(
3837 got_object_commit_get_tree_id(pcommit));
3838 if (tree_id1 == NULL) {
3839 got_object_commit_close(pcommit);
3840 return got_error_from_errno("got_object_id_dup");
3842 got_object_commit_close(pcommit);
3846 if (tree_id1) {
3847 err = got_object_open_as_tree(&tree1, repo, tree_id1);
3848 if (err)
3849 goto done;
3852 tree_id2 = got_object_commit_get_tree_id(commit);
3853 err = got_object_open_as_tree(&tree2, repo, tree_id2);
3854 if (err)
3855 goto done;
3857 err = got_diff_tree(tree1, tree2, f1, f2, fd1, fd2, "", "", repo,
3858 cb, dsa ? (void *)dsa : paths, dsa ? 1 : 0);
3859 done:
3860 if (tree1)
3861 got_object_tree_close(tree1);
3862 if (tree2)
3863 got_object_tree_close(tree2);
3864 if (fd1 != -1 && close(fd1) == -1 && err == NULL)
3865 err = got_error_from_errno("close");
3866 if (fd2 != -1 && close(fd2) == -1 && err == NULL)
3867 err = got_error_from_errno("close");
3868 if (f1 && fclose(f1) == EOF && err == NULL)
3869 err = got_error_from_errno("fclose");
3870 if (f2 && fclose(f2) == EOF && err == NULL)
3871 err = got_error_from_errno("fclose");
3872 free(tree_id1);
3873 return err;
3876 static const struct got_error *
3877 print_patch(struct got_commit_object *commit, struct got_object_id *id,
3878 const char *path, int diff_context, struct got_diffstat_cb_arg *dsa,
3879 struct got_repository *repo, FILE *outfile)
3881 const struct got_error *err = NULL;
3882 struct got_commit_object *pcommit = NULL;
3883 char *id_str1 = NULL, *id_str2 = NULL;
3884 struct got_object_id *obj_id1 = NULL, *obj_id2 = NULL;
3885 struct got_object_qid *qid;
3887 qid = STAILQ_FIRST(got_object_commit_get_parent_ids(commit));
3888 if (qid != NULL) {
3889 err = got_object_open_as_commit(&pcommit, repo,
3890 &qid->id);
3891 if (err)
3892 return err;
3893 err = got_object_id_str(&id_str1, &qid->id);
3894 if (err)
3895 goto done;
3898 err = got_object_id_str(&id_str2, id);
3899 if (err)
3900 goto done;
3902 if (path && path[0] != '\0') {
3903 int obj_type;
3904 err = got_object_id_by_path(&obj_id2, repo, commit, path);
3905 if (err)
3906 goto done;
3907 if (pcommit) {
3908 err = got_object_id_by_path(&obj_id1, repo,
3909 pcommit, path);
3910 if (err) {
3911 if (err->code != GOT_ERR_NO_TREE_ENTRY) {
3912 free(obj_id2);
3913 goto done;
3917 err = got_object_get_type(&obj_type, repo, obj_id2);
3918 if (err) {
3919 free(obj_id2);
3920 goto done;
3922 fprintf(outfile,
3923 "diff %s %s\n", id_str1 ? id_str1 : "/dev/null", id_str2);
3924 fprintf(outfile, "commit - %s\n",
3925 id_str1 ? id_str1 : "/dev/null");
3926 fprintf(outfile, "commit + %s\n", id_str2);
3927 switch (obj_type) {
3928 case GOT_OBJ_TYPE_BLOB:
3929 err = diff_blobs(obj_id1, obj_id2, path, diff_context,
3930 0, 0, dsa, repo, outfile);
3931 break;
3932 case GOT_OBJ_TYPE_TREE:
3933 err = diff_trees(obj_id1, obj_id2, path, diff_context,
3934 0, 0, dsa, repo, outfile);
3935 break;
3936 default:
3937 err = got_error(GOT_ERR_OBJ_TYPE);
3938 break;
3940 free(obj_id1);
3941 free(obj_id2);
3942 } else {
3943 obj_id2 = got_object_commit_get_tree_id(commit);
3944 if (pcommit)
3945 obj_id1 = got_object_commit_get_tree_id(pcommit);
3946 fprintf(outfile,
3947 "diff %s %s\n", id_str1 ? id_str1 : "/dev/null", id_str2);
3948 fprintf(outfile, "commit - %s\n",
3949 id_str1 ? id_str1 : "/dev/null");
3950 fprintf(outfile, "commit + %s\n", id_str2);
3951 err = diff_trees(obj_id1, obj_id2, "", diff_context, 0, 0,
3952 dsa, repo, outfile);
3954 done:
3955 free(id_str1);
3956 free(id_str2);
3957 if (pcommit)
3958 got_object_commit_close(pcommit);
3959 return err;
3962 static char *
3963 get_datestr(time_t *time, char *datebuf)
3965 struct tm mytm, *tm;
3966 char *p, *s;
3968 tm = gmtime_r(time, &mytm);
3969 if (tm == NULL)
3970 return NULL;
3971 s = asctime_r(tm, datebuf);
3972 if (s == NULL)
3973 return NULL;
3974 p = strchr(s, '\n');
3975 if (p)
3976 *p = '\0';
3977 return s;
3980 static const struct got_error *
3981 match_commit(int *have_match, struct got_object_id *id,
3982 struct got_commit_object *commit, regex_t *regex)
3984 const struct got_error *err = NULL;
3985 regmatch_t regmatch;
3986 char *id_str = NULL, *logmsg = NULL;
3988 *have_match = 0;
3990 err = got_object_id_str(&id_str, id);
3991 if (err)
3992 return err;
3994 err = got_object_commit_get_logmsg(&logmsg, commit);
3995 if (err)
3996 goto done;
3998 if (regexec(regex, got_object_commit_get_author(commit), 1,
3999 &regmatch, 0) == 0 ||
4000 regexec(regex, got_object_commit_get_committer(commit), 1,
4001 &regmatch, 0) == 0 ||
4002 regexec(regex, id_str, 1, &regmatch, 0) == 0 ||
4003 regexec(regex, logmsg, 1, &regmatch, 0) == 0)
4004 *have_match = 1;
4005 done:
4006 free(id_str);
4007 free(logmsg);
4008 return err;
4011 static void
4012 match_changed_paths(int *have_match, struct got_pathlist_head *changed_paths,
4013 regex_t *regex)
4015 regmatch_t regmatch;
4016 struct got_pathlist_entry *pe;
4018 *have_match = 0;
4020 TAILQ_FOREACH(pe, changed_paths, entry) {
4021 if (regexec(regex, pe->path, 1, &regmatch, 0) == 0) {
4022 *have_match = 1;
4023 break;
4028 static const struct got_error *
4029 match_patch(int *have_match, struct got_commit_object *commit,
4030 struct got_object_id *id, const char *path, int diff_context,
4031 struct got_repository *repo, regex_t *regex, FILE *f)
4033 const struct got_error *err = NULL;
4034 char *line = NULL;
4035 size_t linesize = 0;
4036 regmatch_t regmatch;
4038 *have_match = 0;
4040 err = got_opentemp_truncate(f);
4041 if (err)
4042 return err;
4044 err = print_patch(commit, id, path, diff_context, NULL, repo, f);
4045 if (err)
4046 goto done;
4048 if (fseeko(f, 0L, SEEK_SET) == -1) {
4049 err = got_error_from_errno("fseeko");
4050 goto done;
4053 while (getline(&line, &linesize, f) != -1) {
4054 if (regexec(regex, line, 1, &regmatch, 0) == 0) {
4055 *have_match = 1;
4056 break;
4059 done:
4060 free(line);
4061 return err;
4064 #define GOT_COMMIT_SEP_STR "-----------------------------------------------\n"
4066 static const struct got_error*
4067 build_refs_str(char **refs_str, struct got_reflist_head *refs,
4068 struct got_object_id *id, struct got_repository *repo,
4069 int local_only)
4071 static const struct got_error *err = NULL;
4072 struct got_reflist_entry *re;
4073 char *s;
4074 const char *name;
4076 *refs_str = NULL;
4078 TAILQ_FOREACH(re, refs, entry) {
4079 struct got_tag_object *tag = NULL;
4080 struct got_object_id *ref_id;
4081 int cmp;
4083 name = got_ref_get_name(re->ref);
4084 if (strcmp(name, GOT_REF_HEAD) == 0)
4085 continue;
4086 if (strncmp(name, "refs/", 5) == 0)
4087 name += 5;
4088 if (strncmp(name, "got/", 4) == 0)
4089 continue;
4090 if (strncmp(name, "heads/", 6) == 0)
4091 name += 6;
4092 if (strncmp(name, "remotes/", 8) == 0) {
4093 if (local_only)
4094 continue;
4095 name += 8;
4096 s = strstr(name, "/" GOT_REF_HEAD);
4097 if (s != NULL && s[strlen(s)] == '\0')
4098 continue;
4100 err = got_ref_resolve(&ref_id, repo, re->ref);
4101 if (err)
4102 break;
4103 if (strncmp(name, "tags/", 5) == 0) {
4104 err = got_object_open_as_tag(&tag, repo, ref_id);
4105 if (err) {
4106 if (err->code != GOT_ERR_OBJ_TYPE) {
4107 free(ref_id);
4108 break;
4110 /* Ref points at something other than a tag. */
4111 err = NULL;
4112 tag = NULL;
4115 cmp = got_object_id_cmp(tag ?
4116 got_object_tag_get_object_id(tag) : ref_id, id);
4117 free(ref_id);
4118 if (tag)
4119 got_object_tag_close(tag);
4120 if (cmp != 0)
4121 continue;
4122 s = *refs_str;
4123 if (asprintf(refs_str, "%s%s%s", s ? s : "",
4124 s ? ", " : "", name) == -1) {
4125 err = got_error_from_errno("asprintf");
4126 free(s);
4127 *refs_str = NULL;
4128 break;
4130 free(s);
4133 return err;
4136 static const struct got_error *
4137 print_commit_oneline(struct got_commit_object *commit, struct got_object_id *id,
4138 struct got_repository *repo, struct got_reflist_object_id_map *refs_idmap)
4140 const struct got_error *err = NULL;
4141 char *ref_str = NULL, *id_str = NULL, *logmsg0 = NULL;
4142 char *comma, *s, *nl;
4143 struct got_reflist_head *refs;
4144 char datebuf[12]; /* YYYY-MM-DD + SPACE + NUL */
4145 struct tm tm;
4146 time_t committer_time;
4148 refs = got_reflist_object_id_map_lookup(refs_idmap, id);
4149 if (refs) {
4150 err = build_refs_str(&ref_str, refs, id, repo, 1);
4151 if (err)
4152 return err;
4154 /* Display the first matching ref only. */
4155 if (ref_str && (comma = strchr(ref_str, ',')) != NULL)
4156 *comma = '\0';
4159 if (ref_str == NULL) {
4160 err = got_object_id_str(&id_str, id);
4161 if (err)
4162 return err;
4165 committer_time = got_object_commit_get_committer_time(commit);
4166 if (gmtime_r(&committer_time, &tm) == NULL) {
4167 err = got_error_from_errno("gmtime_r");
4168 goto done;
4170 if (strftime(datebuf, sizeof(datebuf), "%G-%m-%d ", &tm) == 0) {
4171 err = got_error(GOT_ERR_NO_SPACE);
4172 goto done;
4175 err = got_object_commit_get_logmsg(&logmsg0, commit);
4176 if (err)
4177 goto done;
4179 s = logmsg0;
4180 while (isspace((unsigned char)s[0]))
4181 s++;
4183 nl = strchr(s, '\n');
4184 if (nl) {
4185 *nl = '\0';
4188 if (ref_str)
4189 printf("%s%-7s %s\n", datebuf, ref_str, s);
4190 else
4191 printf("%s%.7s %s\n", datebuf, id_str, s);
4193 if (fflush(stdout) != 0 && err == NULL)
4194 err = got_error_from_errno("fflush");
4195 done:
4196 free(id_str);
4197 free(ref_str);
4198 free(logmsg0);
4199 return err;
4202 static const struct got_error *
4203 print_diffstat(struct got_diffstat_cb_arg *dsa, const char *header)
4205 struct got_pathlist_entry *pe;
4207 if (header != NULL)
4208 printf("%s\n", header);
4210 TAILQ_FOREACH(pe, dsa->paths, entry) {
4211 struct got_diff_changed_path *cp = pe->data;
4212 int pad = dsa->max_path_len - pe->path_len + 1;
4214 printf(" %c %s%*c | %*d+ %*d-\n", cp->status, pe->path, pad,
4215 ' ', dsa->add_cols + 1, cp->add, dsa->rm_cols + 1, cp->rm);
4217 printf("\n%d file%s changed, %d insertion%s(+), %d deletion%s(-)\n\n",
4218 dsa->nfiles, dsa->nfiles > 1 ? "s" : "", dsa->ins,
4219 dsa->ins != 1 ? "s" : "", dsa->del, dsa->del != 1 ? "s" : "");
4221 if (fflush(stdout) != 0)
4222 return got_error_from_errno("fflush");
4224 return NULL;
4227 static const struct got_error *
4228 printfile(FILE *f)
4230 char buf[8192];
4231 size_t r;
4233 if (fseeko(f, 0L, SEEK_SET) == -1)
4234 return got_error_from_errno("fseek");
4236 for (;;) {
4237 r = fread(buf, 1, sizeof(buf), f);
4238 if (r == 0) {
4239 if (ferror(f))
4240 return got_error_from_errno("fread");
4241 if (feof(f))
4242 break;
4244 if (fwrite(buf, 1, r, stdout) != r)
4245 return got_ferror(stdout, GOT_ERR_IO);
4248 return NULL;
4251 static const struct got_error *
4252 print_commit(struct got_commit_object *commit, struct got_object_id *id,
4253 struct got_repository *repo, const char *path,
4254 struct got_pathlist_head *changed_paths,
4255 struct got_diffstat_cb_arg *diffstat, int show_patch, int diff_context,
4256 struct got_reflist_object_id_map *refs_idmap, const char *custom_refs_str,
4257 const char *prefix)
4259 const struct got_error *err = NULL;
4260 FILE *f = NULL;
4261 char *id_str, *datestr, *logmsg0, *logmsg, *line;
4262 char datebuf[26];
4263 time_t committer_time;
4264 const char *author, *committer;
4265 char *refs_str = NULL;
4267 err = got_object_id_str(&id_str, id);
4268 if (err)
4269 return err;
4271 if (custom_refs_str == NULL) {
4272 struct got_reflist_head *refs;
4273 refs = got_reflist_object_id_map_lookup(refs_idmap, id);
4274 if (refs) {
4275 err = build_refs_str(&refs_str, refs, id, repo, 0);
4276 if (err)
4277 goto done;
4281 printf(GOT_COMMIT_SEP_STR);
4282 if (custom_refs_str)
4283 printf("%s %s (%s)\n", prefix ? prefix : "commit", id_str,
4284 custom_refs_str);
4285 else
4286 printf("%s %s%s%s%s\n", prefix ? prefix : "commit", id_str,
4287 refs_str ? " (" : "", refs_str ? refs_str : "",
4288 refs_str ? ")" : "");
4289 free(id_str);
4290 id_str = NULL;
4291 free(refs_str);
4292 refs_str = NULL;
4293 printf("from: %s\n", got_object_commit_get_author(commit));
4294 author = got_object_commit_get_author(commit);
4295 committer = got_object_commit_get_committer(commit);
4296 if (strcmp(author, committer) != 0)
4297 printf("via: %s\n", committer);
4298 committer_time = got_object_commit_get_committer_time(commit);
4299 datestr = get_datestr(&committer_time, datebuf);
4300 if (datestr)
4301 printf("date: %s UTC\n", datestr);
4302 if (got_object_commit_get_nparents(commit) > 1) {
4303 const struct got_object_id_queue *parent_ids;
4304 struct got_object_qid *qid;
4305 int n = 1;
4306 parent_ids = got_object_commit_get_parent_ids(commit);
4307 STAILQ_FOREACH(qid, parent_ids, entry) {
4308 err = got_object_id_str(&id_str, &qid->id);
4309 if (err)
4310 goto done;
4311 printf("parent %d: %s\n", n++, id_str);
4312 free(id_str);
4313 id_str = NULL;
4317 err = got_object_commit_get_logmsg(&logmsg0, commit);
4318 if (err)
4319 goto done;
4321 logmsg = logmsg0;
4322 do {
4323 line = strsep(&logmsg, "\n");
4324 if (line)
4325 printf(" %s\n", line);
4326 } while (line);
4327 free(logmsg0);
4329 if (changed_paths && diffstat == NULL) {
4330 struct got_pathlist_entry *pe;
4332 TAILQ_FOREACH(pe, changed_paths, entry) {
4333 struct got_diff_changed_path *cp = pe->data;
4335 printf(" %c %s\n", cp->status, pe->path);
4337 printf("\n");
4339 if (show_patch) {
4340 if (diffstat) {
4341 f = got_opentemp();
4342 if (f == NULL) {
4343 err = got_error_from_errno("got_opentemp");
4344 goto done;
4348 err = print_patch(commit, id, path, diff_context, diffstat,
4349 repo, diffstat == NULL ? stdout : f);
4350 if (err)
4351 goto done;
4353 if (diffstat) {
4354 err = print_diffstat(diffstat, NULL);
4355 if (err)
4356 goto done;
4357 if (show_patch) {
4358 err = printfile(f);
4359 if (err)
4360 goto done;
4363 if (show_patch)
4364 printf("\n");
4366 if (fflush(stdout) != 0 && err == NULL)
4367 err = got_error_from_errno("fflush");
4368 done:
4369 if (f && fclose(f) == EOF && err == NULL)
4370 err = got_error_from_errno("fclose");
4371 free(id_str);
4372 free(refs_str);
4373 return err;
4376 static const struct got_error *
4377 print_commits(struct got_object_id *root_id, struct got_object_id *end_id,
4378 struct got_repository *repo, const char *path, int show_changed_paths,
4379 int show_diffstat, int show_patch, const char *search_pattern,
4380 int diff_context, int limit, int log_branches, int reverse_display_order,
4381 struct got_reflist_object_id_map *refs_idmap, int one_line,
4382 FILE *tmpfile)
4384 const struct got_error *err;
4385 struct got_commit_graph *graph;
4386 regex_t regex;
4387 int have_match;
4388 struct got_object_id_queue reversed_commits;
4389 struct got_object_qid *qid;
4390 struct got_commit_object *commit;
4391 struct got_pathlist_head changed_paths;
4393 STAILQ_INIT(&reversed_commits);
4394 TAILQ_INIT(&changed_paths);
4396 if (search_pattern && regcomp(&regex, search_pattern,
4397 REG_EXTENDED | REG_NOSUB | REG_NEWLINE))
4398 return got_error_msg(GOT_ERR_REGEX, search_pattern);
4400 err = got_commit_graph_open(&graph, path, !log_branches);
4401 if (err)
4402 return err;
4403 err = got_commit_graph_iter_start(graph, root_id, repo,
4404 check_cancelled, NULL);
4405 if (err)
4406 goto done;
4407 for (;;) {
4408 struct got_object_id id;
4409 struct got_diffstat_cb_arg dsa = { 0, 0, 0, 0, 0, 0,
4410 &changed_paths, 0, 0, GOT_DIFF_ALGORITHM_PATIENCE };
4412 if (sigint_received || sigpipe_received)
4413 break;
4415 err = got_commit_graph_iter_next(&id, graph, repo,
4416 check_cancelled, NULL);
4417 if (err) {
4418 if (err->code == GOT_ERR_ITER_COMPLETED)
4419 err = NULL;
4420 break;
4423 err = got_object_open_as_commit(&commit, repo, &id);
4424 if (err)
4425 break;
4427 if ((show_changed_paths || (show_diffstat && !show_patch))
4428 && !reverse_display_order) {
4429 err = get_changed_paths(&changed_paths, commit, repo,
4430 show_diffstat ? &dsa : NULL);
4431 if (err)
4432 break;
4435 if (search_pattern) {
4436 err = match_commit(&have_match, &id, commit, &regex);
4437 if (err) {
4438 got_object_commit_close(commit);
4439 break;
4441 if (have_match == 0 && show_changed_paths)
4442 match_changed_paths(&have_match,
4443 &changed_paths, &regex);
4444 if (have_match == 0 && show_patch) {
4445 err = match_patch(&have_match, commit, &id,
4446 path, diff_context, repo, &regex, tmpfile);
4447 if (err)
4448 break;
4450 if (have_match == 0) {
4451 got_object_commit_close(commit);
4452 got_pathlist_free(&changed_paths,
4453 GOT_PATHLIST_FREE_ALL);
4454 continue;
4458 if (reverse_display_order) {
4459 err = got_object_qid_alloc(&qid, &id);
4460 if (err)
4461 break;
4462 STAILQ_INSERT_HEAD(&reversed_commits, qid, entry);
4463 got_object_commit_close(commit);
4464 } else {
4465 if (one_line)
4466 err = print_commit_oneline(commit, &id,
4467 repo, refs_idmap);
4468 else
4469 err = print_commit(commit, &id, repo, path,
4470 (show_changed_paths || show_diffstat) ?
4471 &changed_paths : NULL,
4472 show_diffstat ? &dsa : NULL, show_patch,
4473 diff_context, refs_idmap, NULL, NULL);
4474 got_object_commit_close(commit);
4475 if (err)
4476 break;
4478 if ((limit && --limit == 0) ||
4479 (end_id && got_object_id_cmp(&id, end_id) == 0))
4480 break;
4482 got_pathlist_free(&changed_paths, GOT_PATHLIST_FREE_ALL);
4484 if (reverse_display_order) {
4485 STAILQ_FOREACH(qid, &reversed_commits, entry) {
4486 struct got_diffstat_cb_arg dsa = { 0, 0, 0, 0, 0, 0,
4487 &changed_paths, 0, 0, GOT_DIFF_ALGORITHM_PATIENCE };
4489 err = got_object_open_as_commit(&commit, repo,
4490 &qid->id);
4491 if (err)
4492 break;
4493 if (show_changed_paths ||
4494 (show_diffstat && !show_patch)) {
4495 err = get_changed_paths(&changed_paths, commit,
4496 repo, show_diffstat ? &dsa : NULL);
4497 if (err)
4498 break;
4500 if (one_line)
4501 err = print_commit_oneline(commit, &qid->id,
4502 repo, refs_idmap);
4503 else
4504 err = print_commit(commit, &qid->id, repo, path,
4505 (show_changed_paths || show_diffstat) ?
4506 &changed_paths : NULL,
4507 show_diffstat ? &dsa : NULL, show_patch,
4508 diff_context, refs_idmap, NULL, NULL);
4509 got_object_commit_close(commit);
4510 if (err)
4511 break;
4512 got_pathlist_free(&changed_paths, GOT_PATHLIST_FREE_ALL);
4515 done:
4516 while (!STAILQ_EMPTY(&reversed_commits)) {
4517 qid = STAILQ_FIRST(&reversed_commits);
4518 STAILQ_REMOVE_HEAD(&reversed_commits, entry);
4519 got_object_qid_free(qid);
4521 got_pathlist_free(&changed_paths, GOT_PATHLIST_FREE_ALL);
4522 if (search_pattern)
4523 regfree(&regex);
4524 got_commit_graph_close(graph);
4525 return err;
4528 __dead static void
4529 usage_log(void)
4531 fprintf(stderr, "usage: %s log [-bdPpRs] [-C number] [-c commit] "
4532 "[-l N] [-r repository-path] [-S search-pattern] [-x commit] "
4533 "[path]\n", getprogname());
4534 exit(1);
4537 static int
4538 get_default_log_limit(void)
4540 const char *got_default_log_limit;
4541 long long n;
4542 const char *errstr;
4544 got_default_log_limit = getenv("GOT_LOG_DEFAULT_LIMIT");
4545 if (got_default_log_limit == NULL)
4546 return 0;
4547 n = strtonum(got_default_log_limit, 0, INT_MAX, &errstr);
4548 if (errstr != NULL)
4549 return 0;
4550 return n;
4553 static const struct got_error *
4554 cmd_log(int argc, char *argv[])
4556 const struct got_error *error;
4557 struct got_repository *repo = NULL;
4558 struct got_worktree *worktree = NULL;
4559 struct got_object_id *start_id = NULL, *end_id = NULL;
4560 char *repo_path = NULL, *path = NULL, *cwd = NULL, *in_repo_path = NULL;
4561 const char *start_commit = NULL, *end_commit = NULL;
4562 const char *search_pattern = NULL;
4563 int diff_context = -1, ch;
4564 int show_changed_paths = 0, show_patch = 0, limit = 0, log_branches = 0;
4565 int show_diffstat = 0, reverse_display_order = 0, one_line = 0;
4566 const char *errstr;
4567 struct got_reflist_head refs;
4568 struct got_reflist_object_id_map *refs_idmap = NULL;
4569 FILE *tmpfile = NULL;
4570 int *pack_fds = NULL;
4572 TAILQ_INIT(&refs);
4574 #ifndef PROFILE
4575 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
4576 NULL)
4577 == -1)
4578 err(1, "pledge");
4579 #endif
4581 limit = get_default_log_limit();
4583 while ((ch = getopt(argc, argv, "bC:c:dl:PpRr:S:sx:")) != -1) {
4584 switch (ch) {
4585 case 'b':
4586 log_branches = 1;
4587 break;
4588 case 'C':
4589 diff_context = strtonum(optarg, 0, GOT_DIFF_MAX_CONTEXT,
4590 &errstr);
4591 if (errstr != NULL)
4592 errx(1, "number of context lines is %s: %s",
4593 errstr, optarg);
4594 break;
4595 case 'c':
4596 start_commit = optarg;
4597 break;
4598 case 'd':
4599 show_diffstat = 1;
4600 break;
4601 case 'l':
4602 limit = strtonum(optarg, 0, INT_MAX, &errstr);
4603 if (errstr != NULL)
4604 errx(1, "number of commits is %s: %s",
4605 errstr, optarg);
4606 break;
4607 case 'P':
4608 show_changed_paths = 1;
4609 break;
4610 case 'p':
4611 show_patch = 1;
4612 break;
4613 case 'R':
4614 reverse_display_order = 1;
4615 break;
4616 case 'r':
4617 repo_path = realpath(optarg, NULL);
4618 if (repo_path == NULL)
4619 return got_error_from_errno2("realpath",
4620 optarg);
4621 got_path_strip_trailing_slashes(repo_path);
4622 break;
4623 case 'S':
4624 search_pattern = optarg;
4625 break;
4626 case 's':
4627 one_line = 1;
4628 break;
4629 case 'x':
4630 end_commit = optarg;
4631 break;
4632 default:
4633 usage_log();
4634 /* NOTREACHED */
4638 argc -= optind;
4639 argv += optind;
4641 if (diff_context == -1)
4642 diff_context = 3;
4643 else if (!show_patch)
4644 errx(1, "-C requires -p");
4646 if (one_line && (show_patch || show_changed_paths || show_diffstat))
4647 errx(1, "cannot use -s with -d, -p or -P");
4649 cwd = getcwd(NULL, 0);
4650 if (cwd == NULL) {
4651 error = got_error_from_errno("getcwd");
4652 goto done;
4655 error = got_repo_pack_fds_open(&pack_fds);
4656 if (error != NULL)
4657 goto done;
4659 if (repo_path == NULL) {
4660 error = got_worktree_open(&worktree, cwd);
4661 if (error && error->code != GOT_ERR_NOT_WORKTREE)
4662 goto done;
4663 error = NULL;
4666 if (argc == 1) {
4667 if (worktree) {
4668 error = got_worktree_resolve_path(&path, worktree,
4669 argv[0]);
4670 if (error)
4671 goto done;
4672 } else {
4673 path = strdup(argv[0]);
4674 if (path == NULL) {
4675 error = got_error_from_errno("strdup");
4676 goto done;
4679 } else if (argc != 0)
4680 usage_log();
4682 if (repo_path == NULL) {
4683 repo_path = worktree ?
4684 strdup(got_worktree_get_repo_path(worktree)) : strdup(cwd);
4686 if (repo_path == NULL) {
4687 error = got_error_from_errno("strdup");
4688 goto done;
4691 error = got_repo_open(&repo, repo_path, NULL, pack_fds);
4692 if (error != NULL)
4693 goto done;
4695 error = apply_unveil(got_repo_get_path(repo), 1,
4696 worktree ? got_worktree_get_root_path(worktree) : NULL);
4697 if (error)
4698 goto done;
4700 error = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name, NULL);
4701 if (error)
4702 goto done;
4704 error = got_reflist_object_id_map_create(&refs_idmap, &refs, repo);
4705 if (error)
4706 goto done;
4708 if (start_commit == NULL) {
4709 struct got_reference *head_ref;
4710 struct got_commit_object *commit = NULL;
4711 error = got_ref_open(&head_ref, repo,
4712 worktree ? got_worktree_get_head_ref_name(worktree)
4713 : GOT_REF_HEAD, 0);
4714 if (error != NULL)
4715 goto done;
4716 error = got_ref_resolve(&start_id, repo, head_ref);
4717 got_ref_close(head_ref);
4718 if (error != NULL)
4719 goto done;
4720 error = got_object_open_as_commit(&commit, repo,
4721 start_id);
4722 if (error != NULL)
4723 goto done;
4724 got_object_commit_close(commit);
4725 } else {
4726 error = got_repo_match_object_id(&start_id, NULL,
4727 start_commit, GOT_OBJ_TYPE_COMMIT, &refs, repo);
4728 if (error != NULL)
4729 goto done;
4731 if (end_commit != NULL) {
4732 error = got_repo_match_object_id(&end_id, NULL,
4733 end_commit, GOT_OBJ_TYPE_COMMIT, &refs, repo);
4734 if (error != NULL)
4735 goto done;
4738 if (worktree) {
4740 * If a path was specified on the command line it was resolved
4741 * to a path in the work tree above. Prepend the work tree's
4742 * path prefix to obtain the corresponding in-repository path.
4744 if (path) {
4745 const char *prefix;
4746 prefix = got_worktree_get_path_prefix(worktree);
4747 if (asprintf(&in_repo_path, "%s%s%s", prefix,
4748 (path[0] != '\0') ? "/" : "", path) == -1) {
4749 error = got_error_from_errno("asprintf");
4750 goto done;
4753 } else
4754 error = got_repo_map_path(&in_repo_path, repo,
4755 path ? path : "");
4756 if (error != NULL)
4757 goto done;
4758 if (in_repo_path) {
4759 free(path);
4760 path = in_repo_path;
4763 if (worktree) {
4764 /* Release work tree lock. */
4765 got_worktree_close(worktree);
4766 worktree = NULL;
4769 if (search_pattern && show_patch) {
4770 tmpfile = got_opentemp();
4771 if (tmpfile == NULL) {
4772 error = got_error_from_errno("got_opentemp");
4773 goto done;
4777 error = print_commits(start_id, end_id, repo, path ? path : "",
4778 show_changed_paths, show_diffstat, show_patch, search_pattern,
4779 diff_context, limit, log_branches, reverse_display_order,
4780 refs_idmap, one_line, tmpfile);
4781 done:
4782 free(path);
4783 free(repo_path);
4784 free(cwd);
4785 if (worktree)
4786 got_worktree_close(worktree);
4787 if (repo) {
4788 const struct got_error *close_err = got_repo_close(repo);
4789 if (error == NULL)
4790 error = close_err;
4792 if (pack_fds) {
4793 const struct got_error *pack_err =
4794 got_repo_pack_fds_close(pack_fds);
4795 if (error == NULL)
4796 error = pack_err;
4798 if (refs_idmap)
4799 got_reflist_object_id_map_free(refs_idmap);
4800 if (tmpfile && fclose(tmpfile) == EOF && error == NULL)
4801 error = got_error_from_errno("fclose");
4802 got_ref_list_free(&refs);
4803 return error;
4806 __dead static void
4807 usage_diff(void)
4809 fprintf(stderr, "usage: %s diff [-adPsw] [-C number] [-c commit] "
4810 "[-r repository-path] [object1 object2 | path ...]\n",
4811 getprogname());
4812 exit(1);
4815 struct print_diff_arg {
4816 struct got_repository *repo;
4817 struct got_worktree *worktree;
4818 struct got_diffstat_cb_arg *diffstat;
4819 int diff_context;
4820 const char *id_str;
4821 int header_shown;
4822 int diff_staged;
4823 enum got_diff_algorithm diff_algo;
4824 int ignore_whitespace;
4825 int force_text_diff;
4826 FILE *f1;
4827 FILE *f2;
4828 FILE *outfile;
4832 * Create a file which contains the target path of a symlink so we can feed
4833 * it as content to the diff engine.
4835 static const struct got_error *
4836 get_symlink_target_file(int *fd, int dirfd, const char *de_name,
4837 const char *abspath)
4839 const struct got_error *err = NULL;
4840 char target_path[PATH_MAX];
4841 ssize_t target_len, outlen;
4843 *fd = -1;
4845 if (dirfd != -1) {
4846 target_len = readlinkat(dirfd, de_name, target_path, PATH_MAX);
4847 if (target_len == -1)
4848 return got_error_from_errno2("readlinkat", abspath);
4849 } else {
4850 target_len = readlink(abspath, target_path, PATH_MAX);
4851 if (target_len == -1)
4852 return got_error_from_errno2("readlink", abspath);
4855 *fd = got_opentempfd();
4856 if (*fd == -1)
4857 return got_error_from_errno("got_opentempfd");
4859 outlen = write(*fd, target_path, target_len);
4860 if (outlen == -1) {
4861 err = got_error_from_errno("got_opentempfd");
4862 goto done;
4865 if (lseek(*fd, 0, SEEK_SET) == -1) {
4866 err = got_error_from_errno2("lseek", abspath);
4867 goto done;
4869 done:
4870 if (err) {
4871 close(*fd);
4872 *fd = -1;
4874 return err;
4877 static const struct got_error *
4878 print_diff(void *arg, unsigned char status, unsigned char staged_status,
4879 const char *path, struct got_object_id *blob_id,
4880 struct got_object_id *staged_blob_id, struct got_object_id *commit_id,
4881 int dirfd, const char *de_name)
4883 struct print_diff_arg *a = arg;
4884 const struct got_error *err = NULL;
4885 struct got_blob_object *blob1 = NULL;
4886 int fd = -1, fd1 = -1, fd2 = -1;
4887 FILE *f2 = NULL;
4888 char *abspath = NULL, *label1 = NULL;
4889 struct stat sb;
4890 off_t size1 = 0;
4891 int f2_exists = 0;
4893 memset(&sb, 0, sizeof(sb));
4895 if (a->diff_staged) {
4896 if (staged_status != GOT_STATUS_MODIFY &&
4897 staged_status != GOT_STATUS_ADD &&
4898 staged_status != GOT_STATUS_DELETE)
4899 return NULL;
4900 } else {
4901 if (staged_status == GOT_STATUS_DELETE)
4902 return NULL;
4903 if (status == GOT_STATUS_NONEXISTENT)
4904 return got_error_set_errno(ENOENT, path);
4905 if (status != GOT_STATUS_MODIFY &&
4906 status != GOT_STATUS_ADD &&
4907 status != GOT_STATUS_DELETE &&
4908 status != GOT_STATUS_CONFLICT)
4909 return NULL;
4912 err = got_opentemp_truncate(a->f1);
4913 if (err)
4914 return got_error_from_errno("got_opentemp_truncate");
4915 err = got_opentemp_truncate(a->f2);
4916 if (err)
4917 return got_error_from_errno("got_opentemp_truncate");
4919 if (!a->header_shown) {
4920 if (fprintf(a->outfile, "diff %s%s\n",
4921 a->diff_staged ? "-s " : "",
4922 got_worktree_get_root_path(a->worktree)) < 0) {
4923 err = got_error_from_errno("fprintf");
4924 goto done;
4926 if (fprintf(a->outfile, "commit - %s\n", a->id_str) < 0) {
4927 err = got_error_from_errno("fprintf");
4928 goto done;
4930 if (fprintf(a->outfile, "path + %s%s\n",
4931 got_worktree_get_root_path(a->worktree),
4932 a->diff_staged ? " (staged changes)" : "") < 0) {
4933 err = got_error_from_errno("fprintf");
4934 goto done;
4936 a->header_shown = 1;
4939 if (a->diff_staged) {
4940 const char *label1 = NULL, *label2 = NULL;
4941 switch (staged_status) {
4942 case GOT_STATUS_MODIFY:
4943 label1 = path;
4944 label2 = path;
4945 break;
4946 case GOT_STATUS_ADD:
4947 label2 = path;
4948 break;
4949 case GOT_STATUS_DELETE:
4950 label1 = path;
4951 break;
4952 default:
4953 return got_error(GOT_ERR_FILE_STATUS);
4955 fd1 = got_opentempfd();
4956 if (fd1 == -1) {
4957 err = got_error_from_errno("got_opentempfd");
4958 goto done;
4960 fd2 = got_opentempfd();
4961 if (fd2 == -1) {
4962 err = got_error_from_errno("got_opentempfd");
4963 goto done;
4965 err = got_diff_objects_as_blobs(NULL, NULL, a->f1, a->f2,
4966 fd1, fd2, blob_id, staged_blob_id, label1, label2,
4967 a->diff_algo, a->diff_context, a->ignore_whitespace,
4968 a->force_text_diff, a->diffstat, a->repo, a->outfile);
4969 goto done;
4972 fd1 = got_opentempfd();
4973 if (fd1 == -1) {
4974 err = got_error_from_errno("got_opentempfd");
4975 goto done;
4978 if (staged_status == GOT_STATUS_ADD ||
4979 staged_status == GOT_STATUS_MODIFY) {
4980 char *id_str;
4981 err = got_object_open_as_blob(&blob1, a->repo, staged_blob_id,
4982 8192, fd1);
4983 if (err)
4984 goto done;
4985 err = got_object_id_str(&id_str, staged_blob_id);
4986 if (err)
4987 goto done;
4988 if (asprintf(&label1, "%s (staged)", id_str) == -1) {
4989 err = got_error_from_errno("asprintf");
4990 free(id_str);
4991 goto done;
4993 free(id_str);
4994 } else if (status != GOT_STATUS_ADD) {
4995 err = got_object_open_as_blob(&blob1, a->repo, blob_id, 8192,
4996 fd1);
4997 if (err)
4998 goto done;
5001 if (status != GOT_STATUS_DELETE) {
5002 if (asprintf(&abspath, "%s/%s",
5003 got_worktree_get_root_path(a->worktree), path) == -1) {
5004 err = got_error_from_errno("asprintf");
5005 goto done;
5008 if (dirfd != -1) {
5009 fd = openat(dirfd, de_name,
5010 O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
5011 if (fd == -1) {
5012 if (!got_err_open_nofollow_on_symlink()) {
5013 err = got_error_from_errno2("openat",
5014 abspath);
5015 goto done;
5017 err = get_symlink_target_file(&fd, dirfd,
5018 de_name, abspath);
5019 if (err)
5020 goto done;
5022 } else {
5023 fd = open(abspath, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
5024 if (fd == -1) {
5025 if (!got_err_open_nofollow_on_symlink()) {
5026 err = got_error_from_errno2("open",
5027 abspath);
5028 goto done;
5030 err = get_symlink_target_file(&fd, dirfd,
5031 de_name, abspath);
5032 if (err)
5033 goto done;
5036 if (fstatat(fd, abspath, &sb, AT_SYMLINK_NOFOLLOW) == -1) {
5037 err = got_error_from_errno2("fstatat", abspath);
5038 goto done;
5040 f2 = fdopen(fd, "r");
5041 if (f2 == NULL) {
5042 err = got_error_from_errno2("fdopen", abspath);
5043 goto done;
5045 fd = -1;
5046 f2_exists = 1;
5049 if (blob1) {
5050 err = got_object_blob_dump_to_file(&size1, NULL, NULL,
5051 a->f1, blob1);
5052 if (err)
5053 goto done;
5056 err = got_diff_blob_file(blob1, a->f1, size1, label1, f2 ? f2 : a->f2,
5057 f2_exists, &sb, path, GOT_DIFF_ALGORITHM_PATIENCE, a->diff_context,
5058 a->ignore_whitespace, a->force_text_diff, a->diffstat, a->outfile);
5059 done:
5060 if (fd1 != -1 && close(fd1) == -1 && err == NULL)
5061 err = got_error_from_errno("close");
5062 if (fd2 != -1 && close(fd2) == -1 && err == NULL)
5063 err = got_error_from_errno("close");
5064 if (blob1)
5065 got_object_blob_close(blob1);
5066 if (fd != -1 && close(fd) == -1 && err == NULL)
5067 err = got_error_from_errno("close");
5068 if (f2 && fclose(f2) == EOF && err == NULL)
5069 err = got_error_from_errno("fclose");
5070 free(abspath);
5071 return err;
5074 static const struct got_error *
5075 cmd_diff(int argc, char *argv[])
5077 const struct got_error *error;
5078 struct got_repository *repo = NULL;
5079 struct got_worktree *worktree = NULL;
5080 char *cwd = NULL, *repo_path = NULL;
5081 const char *commit_args[2] = { NULL, NULL };
5082 int ncommit_args = 0;
5083 struct got_object_id *ids[2] = { NULL, NULL };
5084 char *labels[2] = { NULL, NULL };
5085 int type1 = GOT_OBJ_TYPE_ANY, type2 = GOT_OBJ_TYPE_ANY;
5086 int diff_context = 3, diff_staged = 0, ignore_whitespace = 0, ch, i;
5087 int force_text_diff = 0, force_path = 0, rflag = 0, show_diffstat = 0;
5088 const char *errstr;
5089 struct got_reflist_head refs;
5090 struct got_pathlist_head diffstat_paths, paths;
5091 FILE *f1 = NULL, *f2 = NULL, *outfile = NULL;
5092 int fd1 = -1, fd2 = -1;
5093 int *pack_fds = NULL;
5094 struct got_diffstat_cb_arg dsa;
5096 memset(&dsa, 0, sizeof(dsa));
5098 TAILQ_INIT(&refs);
5099 TAILQ_INIT(&paths);
5100 TAILQ_INIT(&diffstat_paths);
5102 #ifndef PROFILE
5103 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
5104 NULL) == -1)
5105 err(1, "pledge");
5106 #endif
5108 while ((ch = getopt(argc, argv, "aC:c:dPr:sw")) != -1) {
5109 switch (ch) {
5110 case 'a':
5111 force_text_diff = 1;
5112 break;
5113 case 'C':
5114 diff_context = strtonum(optarg, 0, GOT_DIFF_MAX_CONTEXT,
5115 &errstr);
5116 if (errstr != NULL)
5117 errx(1, "number of context lines is %s: %s",
5118 errstr, optarg);
5119 break;
5120 case 'c':
5121 if (ncommit_args >= 2)
5122 errx(1, "too many -c options used");
5123 commit_args[ncommit_args++] = optarg;
5124 break;
5125 case 'd':
5126 show_diffstat = 1;
5127 break;
5128 case 'P':
5129 force_path = 1;
5130 break;
5131 case 'r':
5132 repo_path = realpath(optarg, NULL);
5133 if (repo_path == NULL)
5134 return got_error_from_errno2("realpath",
5135 optarg);
5136 got_path_strip_trailing_slashes(repo_path);
5137 rflag = 1;
5138 break;
5139 case 's':
5140 diff_staged = 1;
5141 break;
5142 case 'w':
5143 ignore_whitespace = 1;
5144 break;
5145 default:
5146 usage_diff();
5147 /* NOTREACHED */
5151 argc -= optind;
5152 argv += optind;
5154 cwd = getcwd(NULL, 0);
5155 if (cwd == NULL) {
5156 error = got_error_from_errno("getcwd");
5157 goto done;
5160 error = got_repo_pack_fds_open(&pack_fds);
5161 if (error != NULL)
5162 goto done;
5164 if (repo_path == NULL) {
5165 error = got_worktree_open(&worktree, cwd);
5166 if (error && error->code != GOT_ERR_NOT_WORKTREE)
5167 goto done;
5168 else
5169 error = NULL;
5170 if (worktree) {
5171 repo_path =
5172 strdup(got_worktree_get_repo_path(worktree));
5173 if (repo_path == NULL) {
5174 error = got_error_from_errno("strdup");
5175 goto done;
5177 } else {
5178 repo_path = strdup(cwd);
5179 if (repo_path == NULL) {
5180 error = got_error_from_errno("strdup");
5181 goto done;
5186 error = got_repo_open(&repo, repo_path, NULL, pack_fds);
5187 free(repo_path);
5188 if (error != NULL)
5189 goto done;
5191 if (show_diffstat) {
5192 dsa.paths = &diffstat_paths;
5193 dsa.force_text = force_text_diff;
5194 dsa.ignore_ws = ignore_whitespace;
5195 dsa.diff_algo = GOT_DIFF_ALGORITHM_PATIENCE;
5198 if (rflag || worktree == NULL || ncommit_args > 0) {
5199 if (force_path) {
5200 error = got_error_msg(GOT_ERR_NOT_IMPL,
5201 "-P option can only be used when diffing "
5202 "a work tree");
5203 goto done;
5205 if (diff_staged) {
5206 error = got_error_msg(GOT_ERR_NOT_IMPL,
5207 "-s option can only be used when diffing "
5208 "a work tree");
5209 goto done;
5213 error = apply_unveil(got_repo_get_path(repo), 1,
5214 worktree ? got_worktree_get_root_path(worktree) : NULL);
5215 if (error)
5216 goto done;
5218 if ((!force_path && argc == 2) || ncommit_args > 0) {
5219 int obj_type = (ncommit_args > 0 ?
5220 GOT_OBJ_TYPE_COMMIT : GOT_OBJ_TYPE_ANY);
5221 error = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name,
5222 NULL);
5223 if (error)
5224 goto done;
5225 for (i = 0; i < (ncommit_args > 0 ? ncommit_args : argc); i++) {
5226 const char *arg;
5227 if (ncommit_args > 0)
5228 arg = commit_args[i];
5229 else
5230 arg = argv[i];
5231 error = got_repo_match_object_id(&ids[i], &labels[i],
5232 arg, obj_type, &refs, repo);
5233 if (error) {
5234 if (error->code != GOT_ERR_NOT_REF &&
5235 error->code != GOT_ERR_NO_OBJ)
5236 goto done;
5237 if (ncommit_args > 0)
5238 goto done;
5239 error = NULL;
5240 break;
5245 f1 = got_opentemp();
5246 if (f1 == NULL) {
5247 error = got_error_from_errno("got_opentemp");
5248 goto done;
5251 f2 = got_opentemp();
5252 if (f2 == NULL) {
5253 error = got_error_from_errno("got_opentemp");
5254 goto done;
5257 outfile = got_opentemp();
5258 if (outfile == NULL) {
5259 error = got_error_from_errno("got_opentemp");
5260 goto done;
5263 if (ncommit_args == 0 && (ids[0] == NULL || ids[1] == NULL)) {
5264 struct print_diff_arg arg;
5265 char *id_str;
5267 if (worktree == NULL) {
5268 if (argc == 2 && ids[0] == NULL) {
5269 error = got_error_path(argv[0], GOT_ERR_NO_OBJ);
5270 goto done;
5271 } else if (argc == 2 && ids[1] == NULL) {
5272 error = got_error_path(argv[1], GOT_ERR_NO_OBJ);
5273 goto done;
5274 } else if (argc > 0) {
5275 error = got_error_fmt(GOT_ERR_NOT_WORKTREE,
5276 "%s", "specified paths cannot be resolved");
5277 goto done;
5278 } else {
5279 error = got_error(GOT_ERR_NOT_WORKTREE);
5280 goto done;
5284 error = get_worktree_paths_from_argv(&paths, argc, argv,
5285 worktree);
5286 if (error)
5287 goto done;
5289 error = got_object_id_str(&id_str,
5290 got_worktree_get_base_commit_id(worktree));
5291 if (error)
5292 goto done;
5293 arg.repo = repo;
5294 arg.worktree = worktree;
5295 arg.diff_algo = GOT_DIFF_ALGORITHM_PATIENCE;
5296 arg.diff_context = diff_context;
5297 arg.id_str = id_str;
5298 arg.header_shown = 0;
5299 arg.diff_staged = diff_staged;
5300 arg.ignore_whitespace = ignore_whitespace;
5301 arg.force_text_diff = force_text_diff;
5302 arg.diffstat = show_diffstat ? &dsa : NULL;
5303 arg.f1 = f1;
5304 arg.f2 = f2;
5305 arg.outfile = outfile;
5307 error = got_worktree_status(worktree, &paths, repo, 0,
5308 print_diff, &arg, check_cancelled, NULL);
5309 free(id_str);
5310 if (error)
5311 goto done;
5313 if (show_diffstat && dsa.nfiles > 0) {
5314 char *header;
5316 if (asprintf(&header, "diffstat %s%s",
5317 diff_staged ? "-s " : "",
5318 got_worktree_get_root_path(worktree)) == -1) {
5319 error = got_error_from_errno("asprintf");
5320 goto done;
5323 error = print_diffstat(&dsa, header);
5324 free(header);
5325 if (error)
5326 goto done;
5329 error = printfile(outfile);
5330 goto done;
5333 if (ncommit_args == 1) {
5334 struct got_commit_object *commit;
5335 error = got_object_open_as_commit(&commit, repo, ids[0]);
5336 if (error)
5337 goto done;
5339 labels[1] = labels[0];
5340 ids[1] = ids[0];
5341 if (got_object_commit_get_nparents(commit) > 0) {
5342 const struct got_object_id_queue *pids;
5343 struct got_object_qid *pid;
5344 pids = got_object_commit_get_parent_ids(commit);
5345 pid = STAILQ_FIRST(pids);
5346 ids[0] = got_object_id_dup(&pid->id);
5347 if (ids[0] == NULL) {
5348 error = got_error_from_errno(
5349 "got_object_id_dup");
5350 got_object_commit_close(commit);
5351 goto done;
5353 error = got_object_id_str(&labels[0], ids[0]);
5354 if (error) {
5355 got_object_commit_close(commit);
5356 goto done;
5358 } else {
5359 ids[0] = NULL;
5360 labels[0] = strdup("/dev/null");
5361 if (labels[0] == NULL) {
5362 error = got_error_from_errno("strdup");
5363 got_object_commit_close(commit);
5364 goto done;
5368 got_object_commit_close(commit);
5371 if (ncommit_args == 0 && argc > 2) {
5372 error = got_error_msg(GOT_ERR_BAD_PATH,
5373 "path arguments cannot be used when diffing two objects");
5374 goto done;
5377 if (ids[0]) {
5378 error = got_object_get_type(&type1, repo, ids[0]);
5379 if (error)
5380 goto done;
5383 error = got_object_get_type(&type2, repo, ids[1]);
5384 if (error)
5385 goto done;
5386 if (type1 != GOT_OBJ_TYPE_ANY && type1 != type2) {
5387 error = got_error(GOT_ERR_OBJ_TYPE);
5388 goto done;
5390 if (type1 == GOT_OBJ_TYPE_BLOB && argc > 2) {
5391 error = got_error_msg(GOT_ERR_OBJ_TYPE,
5392 "path arguments cannot be used when diffing blobs");
5393 goto done;
5396 for (i = 0; ncommit_args > 0 && i < argc; i++) {
5397 char *in_repo_path;
5398 struct got_pathlist_entry *new;
5399 if (worktree) {
5400 const char *prefix;
5401 char *p;
5402 error = got_worktree_resolve_path(&p, worktree,
5403 argv[i]);
5404 if (error)
5405 goto done;
5406 prefix = got_worktree_get_path_prefix(worktree);
5407 while (prefix[0] == '/')
5408 prefix++;
5409 if (asprintf(&in_repo_path, "%s%s%s", prefix,
5410 (p[0] != '\0' && prefix[0] != '\0') ? "/" : "",
5411 p) == -1) {
5412 error = got_error_from_errno("asprintf");
5413 free(p);
5414 goto done;
5416 free(p);
5417 } else {
5418 char *mapped_path, *s;
5419 error = got_repo_map_path(&mapped_path, repo, argv[i]);
5420 if (error)
5421 goto done;
5422 s = mapped_path;
5423 while (s[0] == '/')
5424 s++;
5425 in_repo_path = strdup(s);
5426 if (in_repo_path == NULL) {
5427 error = got_error_from_errno("asprintf");
5428 free(mapped_path);
5429 goto done;
5431 free(mapped_path);
5434 error = got_pathlist_insert(&new, &paths, in_repo_path, NULL);
5435 if (error || new == NULL /* duplicate */)
5436 free(in_repo_path);
5437 if (error)
5438 goto done;
5441 if (worktree) {
5442 /* Release work tree lock. */
5443 got_worktree_close(worktree);
5444 worktree = NULL;
5447 fd1 = got_opentempfd();
5448 if (fd1 == -1) {
5449 error = got_error_from_errno("got_opentempfd");
5450 goto done;
5453 fd2 = got_opentempfd();
5454 if (fd2 == -1) {
5455 error = got_error_from_errno("got_opentempfd");
5456 goto done;
5459 switch (type1 == GOT_OBJ_TYPE_ANY ? type2 : type1) {
5460 case GOT_OBJ_TYPE_BLOB:
5461 error = got_diff_objects_as_blobs(NULL, NULL, f1, f2,
5462 fd1, fd2, ids[0], ids[1], NULL, NULL,
5463 GOT_DIFF_ALGORITHM_PATIENCE, diff_context,
5464 ignore_whitespace, force_text_diff,
5465 show_diffstat ? &dsa : NULL, repo, outfile);
5466 break;
5467 case GOT_OBJ_TYPE_TREE:
5468 error = got_diff_objects_as_trees(NULL, NULL, f1, f2, fd1, fd2,
5469 ids[0], ids[1], &paths, "", "",
5470 GOT_DIFF_ALGORITHM_PATIENCE, diff_context,
5471 ignore_whitespace, force_text_diff,
5472 show_diffstat ? &dsa : NULL, repo, outfile);
5473 break;
5474 case GOT_OBJ_TYPE_COMMIT:
5475 fprintf(outfile, "diff %s %s\n", labels[0], labels[1]);
5476 error = got_diff_objects_as_commits(NULL, NULL, f1, f2,
5477 fd1, fd2, ids[0], ids[1], &paths,
5478 GOT_DIFF_ALGORITHM_PATIENCE, diff_context,
5479 ignore_whitespace, force_text_diff,
5480 show_diffstat ? &dsa : NULL, repo, outfile);
5481 break;
5482 default:
5483 error = got_error(GOT_ERR_OBJ_TYPE);
5485 if (error)
5486 goto done;
5488 if (show_diffstat && dsa.nfiles > 0) {
5489 char *header = NULL;
5491 if (asprintf(&header, "diffstat %s %s",
5492 labels[0], labels[1]) == -1) {
5493 error = got_error_from_errno("asprintf");
5494 goto done;
5497 error = print_diffstat(&dsa, header);
5498 free(header);
5499 if (error)
5500 goto done;
5503 error = printfile(outfile);
5505 done:
5506 free(labels[0]);
5507 free(labels[1]);
5508 free(ids[0]);
5509 free(ids[1]);
5510 if (worktree)
5511 got_worktree_close(worktree);
5512 if (repo) {
5513 const struct got_error *close_err = got_repo_close(repo);
5514 if (error == NULL)
5515 error = close_err;
5517 if (pack_fds) {
5518 const struct got_error *pack_err =
5519 got_repo_pack_fds_close(pack_fds);
5520 if (error == NULL)
5521 error = pack_err;
5523 got_pathlist_free(&paths, GOT_PATHLIST_FREE_PATH);
5524 got_pathlist_free(&diffstat_paths, GOT_PATHLIST_FREE_ALL);
5525 got_ref_list_free(&refs);
5526 if (outfile && fclose(outfile) == EOF && error == NULL)
5527 error = got_error_from_errno("fclose");
5528 if (f1 && fclose(f1) == EOF && error == NULL)
5529 error = got_error_from_errno("fclose");
5530 if (f2 && fclose(f2) == EOF && error == NULL)
5531 error = got_error_from_errno("fclose");
5532 if (fd1 != -1 && close(fd1) == -1 && error == NULL)
5533 error = got_error_from_errno("close");
5534 if (fd2 != -1 && close(fd2) == -1 && error == NULL)
5535 error = got_error_from_errno("close");
5536 return error;
5539 __dead static void
5540 usage_blame(void)
5542 fprintf(stderr,
5543 "usage: %s blame [-c commit] [-r repository-path] path\n",
5544 getprogname());
5545 exit(1);
5548 struct blame_line {
5549 int annotated;
5550 char *id_str;
5551 char *committer;
5552 char datebuf[11]; /* YYYY-MM-DD + NUL */
5555 struct blame_cb_args {
5556 struct blame_line *lines;
5557 int nlines;
5558 int nlines_prec;
5559 int lineno_cur;
5560 off_t *line_offsets;
5561 FILE *f;
5562 struct got_repository *repo;
5565 static const struct got_error *
5566 blame_cb(void *arg, int nlines, int lineno,
5567 struct got_commit_object *commit, struct got_object_id *id)
5569 const struct got_error *err = NULL;
5570 struct blame_cb_args *a = arg;
5571 struct blame_line *bline;
5572 char *line = NULL;
5573 size_t linesize = 0;
5574 off_t offset;
5575 struct tm tm;
5576 time_t committer_time;
5578 if (nlines != a->nlines ||
5579 (lineno != -1 && lineno < 1) || lineno > a->nlines)
5580 return got_error(GOT_ERR_RANGE);
5582 if (sigint_received)
5583 return got_error(GOT_ERR_ITER_COMPLETED);
5585 if (lineno == -1)
5586 return NULL; /* no change in this commit */
5588 /* Annotate this line. */
5589 bline = &a->lines[lineno - 1];
5590 if (bline->annotated)
5591 return NULL;
5592 err = got_object_id_str(&bline->id_str, id);
5593 if (err)
5594 return err;
5596 bline->committer = strdup(got_object_commit_get_committer(commit));
5597 if (bline->committer == NULL) {
5598 err = got_error_from_errno("strdup");
5599 goto done;
5602 committer_time = got_object_commit_get_committer_time(commit);
5603 if (gmtime_r(&committer_time, &tm) == NULL)
5604 return got_error_from_errno("gmtime_r");
5605 if (strftime(bline->datebuf, sizeof(bline->datebuf), "%G-%m-%d",
5606 &tm) == 0) {
5607 err = got_error(GOT_ERR_NO_SPACE);
5608 goto done;
5610 bline->annotated = 1;
5612 /* Print lines annotated so far. */
5613 bline = &a->lines[a->lineno_cur - 1];
5614 if (!bline->annotated)
5615 goto done;
5617 offset = a->line_offsets[a->lineno_cur - 1];
5618 if (fseeko(a->f, offset, SEEK_SET) == -1) {
5619 err = got_error_from_errno("fseeko");
5620 goto done;
5623 while (a->lineno_cur <= a->nlines && bline->annotated) {
5624 char *smallerthan, *at, *nl, *committer;
5625 size_t len;
5627 if (getline(&line, &linesize, a->f) == -1) {
5628 if (ferror(a->f))
5629 err = got_error_from_errno("getline");
5630 break;
5633 committer = bline->committer;
5634 smallerthan = strchr(committer, '<');
5635 if (smallerthan && smallerthan[1] != '\0')
5636 committer = smallerthan + 1;
5637 at = strchr(committer, '@');
5638 if (at)
5639 *at = '\0';
5640 len = strlen(committer);
5641 if (len >= 9)
5642 committer[8] = '\0';
5644 nl = strchr(line, '\n');
5645 if (nl)
5646 *nl = '\0';
5647 printf("%.*d) %.8s %s %-8s %s\n", a->nlines_prec, a->lineno_cur,
5648 bline->id_str, bline->datebuf, committer, line);
5650 a->lineno_cur++;
5651 bline = &a->lines[a->lineno_cur - 1];
5653 done:
5654 free(line);
5655 return err;
5658 static const struct got_error *
5659 cmd_blame(int argc, char *argv[])
5661 const struct got_error *error;
5662 struct got_repository *repo = NULL;
5663 struct got_worktree *worktree = NULL;
5664 char *path, *cwd = NULL, *repo_path = NULL, *in_repo_path = NULL;
5665 char *link_target = NULL;
5666 struct got_object_id *obj_id = NULL;
5667 struct got_object_id *commit_id = NULL;
5668 struct got_commit_object *commit = NULL;
5669 struct got_blob_object *blob = NULL;
5670 char *commit_id_str = NULL;
5671 struct blame_cb_args bca;
5672 int ch, obj_type, i, fd1 = -1, fd2 = -1, fd3 = -1;
5673 off_t filesize;
5674 int *pack_fds = NULL;
5675 FILE *f1 = NULL, *f2 = NULL;
5677 fd1 = got_opentempfd();
5678 if (fd1 == -1)
5679 return got_error_from_errno("got_opentempfd");
5681 memset(&bca, 0, sizeof(bca));
5683 #ifndef PROFILE
5684 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
5685 NULL) == -1)
5686 err(1, "pledge");
5687 #endif
5689 while ((ch = getopt(argc, argv, "c:r:")) != -1) {
5690 switch (ch) {
5691 case 'c':
5692 commit_id_str = optarg;
5693 break;
5694 case 'r':
5695 repo_path = realpath(optarg, NULL);
5696 if (repo_path == NULL)
5697 return got_error_from_errno2("realpath",
5698 optarg);
5699 got_path_strip_trailing_slashes(repo_path);
5700 break;
5701 default:
5702 usage_blame();
5703 /* NOTREACHED */
5707 argc -= optind;
5708 argv += optind;
5710 if (argc == 1)
5711 path = argv[0];
5712 else
5713 usage_blame();
5715 cwd = getcwd(NULL, 0);
5716 if (cwd == NULL) {
5717 error = got_error_from_errno("getcwd");
5718 goto done;
5721 error = got_repo_pack_fds_open(&pack_fds);
5722 if (error != NULL)
5723 goto done;
5725 if (repo_path == NULL) {
5726 error = got_worktree_open(&worktree, cwd);
5727 if (error && error->code != GOT_ERR_NOT_WORKTREE)
5728 goto done;
5729 else
5730 error = NULL;
5731 if (worktree) {
5732 repo_path =
5733 strdup(got_worktree_get_repo_path(worktree));
5734 if (repo_path == NULL) {
5735 error = got_error_from_errno("strdup");
5736 if (error)
5737 goto done;
5739 } else {
5740 repo_path = strdup(cwd);
5741 if (repo_path == NULL) {
5742 error = got_error_from_errno("strdup");
5743 goto done;
5748 error = got_repo_open(&repo, repo_path, NULL, pack_fds);
5749 if (error != NULL)
5750 goto done;
5752 if (worktree) {
5753 const char *prefix = got_worktree_get_path_prefix(worktree);
5754 char *p;
5756 error = got_worktree_resolve_path(&p, worktree, path);
5757 if (error)
5758 goto done;
5759 if (asprintf(&in_repo_path, "%s%s%s", prefix,
5760 (p[0] != '\0' && !got_path_is_root_dir(prefix)) ? "/" : "",
5761 p) == -1) {
5762 error = got_error_from_errno("asprintf");
5763 free(p);
5764 goto done;
5766 free(p);
5767 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
5768 } else {
5769 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
5770 if (error)
5771 goto done;
5772 error = got_repo_map_path(&in_repo_path, repo, path);
5774 if (error)
5775 goto done;
5777 if (commit_id_str == NULL) {
5778 struct got_reference *head_ref;
5779 error = got_ref_open(&head_ref, repo, worktree ?
5780 got_worktree_get_head_ref_name(worktree) : GOT_REF_HEAD, 0);
5781 if (error != NULL)
5782 goto done;
5783 error = got_ref_resolve(&commit_id, repo, head_ref);
5784 got_ref_close(head_ref);
5785 if (error != NULL)
5786 goto done;
5787 } else {
5788 struct got_reflist_head refs;
5789 TAILQ_INIT(&refs);
5790 error = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name,
5791 NULL);
5792 if (error)
5793 goto done;
5794 error = got_repo_match_object_id(&commit_id, NULL,
5795 commit_id_str, GOT_OBJ_TYPE_COMMIT, &refs, repo);
5796 got_ref_list_free(&refs);
5797 if (error)
5798 goto done;
5801 if (worktree) {
5802 /* Release work tree lock. */
5803 got_worktree_close(worktree);
5804 worktree = NULL;
5807 error = got_object_open_as_commit(&commit, repo, commit_id);
5808 if (error)
5809 goto done;
5811 error = got_object_resolve_symlinks(&link_target, in_repo_path,
5812 commit, repo);
5813 if (error)
5814 goto done;
5816 error = got_object_id_by_path(&obj_id, repo, commit,
5817 link_target ? link_target : in_repo_path);
5818 if (error)
5819 goto done;
5821 error = got_object_get_type(&obj_type, repo, obj_id);
5822 if (error)
5823 goto done;
5825 if (obj_type != GOT_OBJ_TYPE_BLOB) {
5826 error = got_error_path(link_target ? link_target : in_repo_path,
5827 GOT_ERR_OBJ_TYPE);
5828 goto done;
5831 error = got_object_open_as_blob(&blob, repo, obj_id, 8192, fd1);
5832 if (error)
5833 goto done;
5834 bca.f = got_opentemp();
5835 if (bca.f == NULL) {
5836 error = got_error_from_errno("got_opentemp");
5837 goto done;
5839 error = got_object_blob_dump_to_file(&filesize, &bca.nlines,
5840 &bca.line_offsets, bca.f, blob);
5841 if (error || bca.nlines == 0)
5842 goto done;
5844 /* Don't include \n at EOF in the blame line count. */
5845 if (bca.line_offsets[bca.nlines - 1] == filesize)
5846 bca.nlines--;
5848 bca.lines = calloc(bca.nlines, sizeof(*bca.lines));
5849 if (bca.lines == NULL) {
5850 error = got_error_from_errno("calloc");
5851 goto done;
5853 bca.lineno_cur = 1;
5854 bca.nlines_prec = 0;
5855 i = bca.nlines;
5856 while (i > 0) {
5857 i /= 10;
5858 bca.nlines_prec++;
5860 bca.repo = repo;
5862 fd2 = got_opentempfd();
5863 if (fd2 == -1) {
5864 error = got_error_from_errno("got_opentempfd");
5865 goto done;
5867 fd3 = got_opentempfd();
5868 if (fd3 == -1) {
5869 error = got_error_from_errno("got_opentempfd");
5870 goto done;
5872 f1 = got_opentemp();
5873 if (f1 == NULL) {
5874 error = got_error_from_errno("got_opentemp");
5875 goto done;
5877 f2 = got_opentemp();
5878 if (f2 == NULL) {
5879 error = got_error_from_errno("got_opentemp");
5880 goto done;
5882 error = got_blame(link_target ? link_target : in_repo_path, commit_id,
5883 repo, GOT_DIFF_ALGORITHM_PATIENCE, blame_cb, &bca,
5884 check_cancelled, NULL, fd2, fd3, f1, f2);
5885 done:
5886 free(in_repo_path);
5887 free(link_target);
5888 free(repo_path);
5889 free(cwd);
5890 free(commit_id);
5891 free(obj_id);
5892 if (commit)
5893 got_object_commit_close(commit);
5895 if (fd1 != -1 && close(fd1) == -1 && error == NULL)
5896 error = got_error_from_errno("close");
5897 if (fd2 != -1 && close(fd2) == -1 && error == NULL)
5898 error = got_error_from_errno("close");
5899 if (fd3 != -1 && close(fd3) == -1 && error == NULL)
5900 error = got_error_from_errno("close");
5901 if (f1 && fclose(f1) == EOF && error == NULL)
5902 error = got_error_from_errno("fclose");
5903 if (f2 && fclose(f2) == EOF && error == NULL)
5904 error = got_error_from_errno("fclose");
5906 if (blob)
5907 got_object_blob_close(blob);
5908 if (worktree)
5909 got_worktree_close(worktree);
5910 if (repo) {
5911 const struct got_error *close_err = got_repo_close(repo);
5912 if (error == NULL)
5913 error = close_err;
5915 if (pack_fds) {
5916 const struct got_error *pack_err =
5917 got_repo_pack_fds_close(pack_fds);
5918 if (error == NULL)
5919 error = pack_err;
5921 if (bca.lines) {
5922 for (i = 0; i < bca.nlines; i++) {
5923 struct blame_line *bline = &bca.lines[i];
5924 free(bline->id_str);
5925 free(bline->committer);
5927 free(bca.lines);
5929 free(bca.line_offsets);
5930 if (bca.f && fclose(bca.f) == EOF && error == NULL)
5931 error = got_error_from_errno("fclose");
5932 return error;
5935 __dead static void
5936 usage_tree(void)
5938 fprintf(stderr, "usage: %s tree [-iR] [-c commit] [-r repository-path] "
5939 "[path]\n", getprogname());
5940 exit(1);
5943 static const struct got_error *
5944 print_entry(struct got_tree_entry *te, const char *id, const char *path,
5945 const char *root_path, struct got_repository *repo)
5947 const struct got_error *err = NULL;
5948 int is_root_path = (strcmp(path, root_path) == 0);
5949 const char *modestr = "";
5950 mode_t mode = got_tree_entry_get_mode(te);
5951 char *link_target = NULL;
5953 path += strlen(root_path);
5954 while (path[0] == '/')
5955 path++;
5957 if (got_object_tree_entry_is_submodule(te))
5958 modestr = "$";
5959 else if (S_ISLNK(mode)) {
5960 int i;
5962 err = got_tree_entry_get_symlink_target(&link_target, te, repo);
5963 if (err)
5964 return err;
5965 for (i = 0; i < strlen(link_target); i++) {
5966 if (!isprint((unsigned char)link_target[i]))
5967 link_target[i] = '?';
5970 modestr = "@";
5972 else if (S_ISDIR(mode))
5973 modestr = "/";
5974 else if (mode & S_IXUSR)
5975 modestr = "*";
5977 printf("%s%s%s%s%s%s%s\n", id ? id : "", path,
5978 is_root_path ? "" : "/", got_tree_entry_get_name(te), modestr,
5979 link_target ? " -> ": "", link_target ? link_target : "");
5981 free(link_target);
5982 return NULL;
5985 static const struct got_error *
5986 print_tree(const char *path, struct got_commit_object *commit,
5987 int show_ids, int recurse, const char *root_path,
5988 struct got_repository *repo)
5990 const struct got_error *err = NULL;
5991 struct got_object_id *tree_id = NULL;
5992 struct got_tree_object *tree = NULL;
5993 int nentries, i;
5995 err = got_object_id_by_path(&tree_id, repo, commit, path);
5996 if (err)
5997 goto done;
5999 err = got_object_open_as_tree(&tree, repo, tree_id);
6000 if (err)
6001 goto done;
6002 nentries = got_object_tree_get_nentries(tree);
6003 for (i = 0; i < nentries; i++) {
6004 struct got_tree_entry *te;
6005 char *id = NULL;
6007 if (sigint_received || sigpipe_received)
6008 break;
6010 te = got_object_tree_get_entry(tree, i);
6011 if (show_ids) {
6012 char *id_str;
6013 err = got_object_id_str(&id_str,
6014 got_tree_entry_get_id(te));
6015 if (err)
6016 goto done;
6017 if (asprintf(&id, "%s ", id_str) == -1) {
6018 err = got_error_from_errno("asprintf");
6019 free(id_str);
6020 goto done;
6022 free(id_str);
6024 err = print_entry(te, id, path, root_path, repo);
6025 free(id);
6026 if (err)
6027 goto done;
6029 if (recurse && S_ISDIR(got_tree_entry_get_mode(te))) {
6030 char *child_path;
6031 if (asprintf(&child_path, "%s%s%s", path,
6032 path[0] == '/' && path[1] == '\0' ? "" : "/",
6033 got_tree_entry_get_name(te)) == -1) {
6034 err = got_error_from_errno("asprintf");
6035 goto done;
6037 err = print_tree(child_path, commit, show_ids, 1,
6038 root_path, repo);
6039 free(child_path);
6040 if (err)
6041 goto done;
6044 done:
6045 if (tree)
6046 got_object_tree_close(tree);
6047 free(tree_id);
6048 return err;
6051 static const struct got_error *
6052 cmd_tree(int argc, char *argv[])
6054 const struct got_error *error;
6055 struct got_repository *repo = NULL;
6056 struct got_worktree *worktree = NULL;
6057 const char *path, *refname = NULL;
6058 char *cwd = NULL, *repo_path = NULL, *in_repo_path = NULL;
6059 struct got_object_id *commit_id = NULL;
6060 struct got_commit_object *commit = NULL;
6061 char *commit_id_str = NULL;
6062 int show_ids = 0, recurse = 0;
6063 int ch;
6064 int *pack_fds = NULL;
6066 #ifndef PROFILE
6067 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
6068 NULL) == -1)
6069 err(1, "pledge");
6070 #endif
6072 while ((ch = getopt(argc, argv, "c:iRr:")) != -1) {
6073 switch (ch) {
6074 case 'c':
6075 commit_id_str = optarg;
6076 break;
6077 case 'i':
6078 show_ids = 1;
6079 break;
6080 case 'R':
6081 recurse = 1;
6082 break;
6083 case 'r':
6084 repo_path = realpath(optarg, NULL);
6085 if (repo_path == NULL)
6086 return got_error_from_errno2("realpath",
6087 optarg);
6088 got_path_strip_trailing_slashes(repo_path);
6089 break;
6090 default:
6091 usage_tree();
6092 /* NOTREACHED */
6096 argc -= optind;
6097 argv += optind;
6099 if (argc == 1)
6100 path = argv[0];
6101 else if (argc > 1)
6102 usage_tree();
6103 else
6104 path = NULL;
6106 cwd = getcwd(NULL, 0);
6107 if (cwd == NULL) {
6108 error = got_error_from_errno("getcwd");
6109 goto done;
6112 error = got_repo_pack_fds_open(&pack_fds);
6113 if (error != NULL)
6114 goto done;
6116 if (repo_path == NULL) {
6117 error = got_worktree_open(&worktree, cwd);
6118 if (error && error->code != GOT_ERR_NOT_WORKTREE)
6119 goto done;
6120 else
6121 error = NULL;
6122 if (worktree) {
6123 repo_path =
6124 strdup(got_worktree_get_repo_path(worktree));
6125 if (repo_path == NULL)
6126 error = got_error_from_errno("strdup");
6127 if (error)
6128 goto done;
6129 } else {
6130 repo_path = strdup(cwd);
6131 if (repo_path == NULL) {
6132 error = got_error_from_errno("strdup");
6133 goto done;
6138 error = got_repo_open(&repo, repo_path, NULL, pack_fds);
6139 if (error != NULL)
6140 goto done;
6142 if (worktree) {
6143 const char *prefix = got_worktree_get_path_prefix(worktree);
6144 char *p;
6146 if (path == NULL)
6147 path = "";
6148 error = got_worktree_resolve_path(&p, worktree, path);
6149 if (error)
6150 goto done;
6151 if (asprintf(&in_repo_path, "%s%s%s", prefix,
6152 (p[0] != '\0' && !got_path_is_root_dir(prefix)) ? "/" : "",
6153 p) == -1) {
6154 error = got_error_from_errno("asprintf");
6155 free(p);
6156 goto done;
6158 free(p);
6159 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
6160 if (error)
6161 goto done;
6162 } else {
6163 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
6164 if (error)
6165 goto done;
6166 if (path == NULL)
6167 path = "/";
6168 error = got_repo_map_path(&in_repo_path, repo, path);
6169 if (error != NULL)
6170 goto done;
6173 if (commit_id_str == NULL) {
6174 struct got_reference *head_ref;
6175 if (worktree)
6176 refname = got_worktree_get_head_ref_name(worktree);
6177 else
6178 refname = GOT_REF_HEAD;
6179 error = got_ref_open(&head_ref, repo, refname, 0);
6180 if (error != NULL)
6181 goto done;
6182 error = got_ref_resolve(&commit_id, repo, head_ref);
6183 got_ref_close(head_ref);
6184 if (error != NULL)
6185 goto done;
6186 } else {
6187 struct got_reflist_head refs;
6188 TAILQ_INIT(&refs);
6189 error = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name,
6190 NULL);
6191 if (error)
6192 goto done;
6193 error = got_repo_match_object_id(&commit_id, NULL,
6194 commit_id_str, GOT_OBJ_TYPE_COMMIT, &refs, repo);
6195 got_ref_list_free(&refs);
6196 if (error)
6197 goto done;
6200 if (worktree) {
6201 /* Release work tree lock. */
6202 got_worktree_close(worktree);
6203 worktree = NULL;
6206 error = got_object_open_as_commit(&commit, repo, commit_id);
6207 if (error)
6208 goto done;
6210 error = print_tree(in_repo_path, commit, show_ids, recurse,
6211 in_repo_path, repo);
6212 done:
6213 free(in_repo_path);
6214 free(repo_path);
6215 free(cwd);
6216 free(commit_id);
6217 if (commit)
6218 got_object_commit_close(commit);
6219 if (worktree)
6220 got_worktree_close(worktree);
6221 if (repo) {
6222 const struct got_error *close_err = got_repo_close(repo);
6223 if (error == NULL)
6224 error = close_err;
6226 if (pack_fds) {
6227 const struct got_error *pack_err =
6228 got_repo_pack_fds_close(pack_fds);
6229 if (error == NULL)
6230 error = pack_err;
6232 return error;
6235 __dead static void
6236 usage_status(void)
6238 fprintf(stderr, "usage: %s status [-I] [-S status-codes] "
6239 "[-s status-codes] [path ...]\n", getprogname());
6240 exit(1);
6243 struct got_status_arg {
6244 char *status_codes;
6245 int suppress;
6248 static const struct got_error *
6249 print_status(void *arg, unsigned char status, unsigned char staged_status,
6250 const char *path, struct got_object_id *blob_id,
6251 struct got_object_id *staged_blob_id, struct got_object_id *commit_id,
6252 int dirfd, const char *de_name)
6254 struct got_status_arg *st = arg;
6256 if (status == staged_status && (status == GOT_STATUS_DELETE))
6257 status = GOT_STATUS_NO_CHANGE;
6258 if (st != NULL && st->status_codes) {
6259 size_t ncodes = strlen(st->status_codes);
6260 int i, j = 0;
6262 for (i = 0; i < ncodes ; i++) {
6263 if (st->suppress) {
6264 if (status == st->status_codes[i] ||
6265 staged_status == st->status_codes[i]) {
6266 j++;
6267 continue;
6269 } else {
6270 if (status == st->status_codes[i] ||
6271 staged_status == st->status_codes[i])
6272 break;
6276 if (st->suppress && j == 0)
6277 goto print;
6279 if (i == ncodes)
6280 return NULL;
6282 print:
6283 printf("%c%c %s\n", status, staged_status, path);
6284 return NULL;
6287 static const struct got_error *
6288 cmd_status(int argc, char *argv[])
6290 const struct got_error *error = NULL;
6291 struct got_repository *repo = NULL;
6292 struct got_worktree *worktree = NULL;
6293 struct got_status_arg st;
6294 char *cwd = NULL;
6295 struct got_pathlist_head paths;
6296 int ch, i, no_ignores = 0;
6297 int *pack_fds = NULL;
6299 TAILQ_INIT(&paths);
6301 memset(&st, 0, sizeof(st));
6302 st.status_codes = NULL;
6303 st.suppress = 0;
6305 #ifndef PROFILE
6306 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
6307 NULL) == -1)
6308 err(1, "pledge");
6309 #endif
6311 while ((ch = getopt(argc, argv, "IS:s:")) != -1) {
6312 switch (ch) {
6313 case 'I':
6314 no_ignores = 1;
6315 break;
6316 case 'S':
6317 if (st.status_codes != NULL && st.suppress == 0)
6318 option_conflict('S', 's');
6319 st.suppress = 1;
6320 /* fallthrough */
6321 case 's':
6322 for (i = 0; i < strlen(optarg); i++) {
6323 switch (optarg[i]) {
6324 case GOT_STATUS_MODIFY:
6325 case GOT_STATUS_ADD:
6326 case GOT_STATUS_DELETE:
6327 case GOT_STATUS_CONFLICT:
6328 case GOT_STATUS_MISSING:
6329 case GOT_STATUS_OBSTRUCTED:
6330 case GOT_STATUS_UNVERSIONED:
6331 case GOT_STATUS_MODE_CHANGE:
6332 case GOT_STATUS_NONEXISTENT:
6333 break;
6334 default:
6335 errx(1, "invalid status code '%c'",
6336 optarg[i]);
6339 if (ch == 's' && st.suppress)
6340 option_conflict('s', 'S');
6341 st.status_codes = optarg;
6342 break;
6343 default:
6344 usage_status();
6345 /* NOTREACHED */
6349 argc -= optind;
6350 argv += optind;
6352 cwd = getcwd(NULL, 0);
6353 if (cwd == NULL) {
6354 error = got_error_from_errno("getcwd");
6355 goto done;
6358 error = got_repo_pack_fds_open(&pack_fds);
6359 if (error != NULL)
6360 goto done;
6362 error = got_worktree_open(&worktree, cwd);
6363 if (error) {
6364 if (error->code == GOT_ERR_NOT_WORKTREE)
6365 error = wrap_not_worktree_error(error, "status", cwd);
6366 goto done;
6369 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
6370 NULL, pack_fds);
6371 if (error != NULL)
6372 goto done;
6374 error = apply_unveil(got_repo_get_path(repo), 1,
6375 got_worktree_get_root_path(worktree));
6376 if (error)
6377 goto done;
6379 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
6380 if (error)
6381 goto done;
6383 error = got_worktree_status(worktree, &paths, repo, no_ignores,
6384 print_status, &st, check_cancelled, NULL);
6385 done:
6386 if (pack_fds) {
6387 const struct got_error *pack_err =
6388 got_repo_pack_fds_close(pack_fds);
6389 if (error == NULL)
6390 error = pack_err;
6393 got_pathlist_free(&paths, GOT_PATHLIST_FREE_PATH);
6394 free(cwd);
6395 return error;
6398 __dead static void
6399 usage_ref(void)
6401 fprintf(stderr, "usage: %s ref [-dlt] [-c object] [-r repository-path] "
6402 "[-s reference] [name]\n", getprogname());
6403 exit(1);
6406 static const struct got_error *
6407 list_refs(struct got_repository *repo, const char *refname, int sort_by_time)
6409 static const struct got_error *err = NULL;
6410 struct got_reflist_head refs;
6411 struct got_reflist_entry *re;
6413 TAILQ_INIT(&refs);
6414 err = got_ref_list(&refs, repo, refname, sort_by_time ?
6415 got_ref_cmp_by_commit_timestamp_descending : got_ref_cmp_by_name,
6416 repo);
6417 if (err)
6418 return err;
6420 TAILQ_FOREACH(re, &refs, entry) {
6421 char *refstr;
6422 refstr = got_ref_to_str(re->ref);
6423 if (refstr == NULL) {
6424 err = got_error_from_errno("got_ref_to_str");
6425 break;
6427 printf("%s: %s\n", got_ref_get_name(re->ref), refstr);
6428 free(refstr);
6431 got_ref_list_free(&refs);
6432 return err;
6435 static const struct got_error *
6436 delete_ref_by_name(struct got_repository *repo, const char *refname)
6438 const struct got_error *err;
6439 struct got_reference *ref;
6441 err = got_ref_open(&ref, repo, refname, 0);
6442 if (err)
6443 return err;
6445 err = delete_ref(repo, ref);
6446 got_ref_close(ref);
6447 return err;
6450 static const struct got_error *
6451 add_ref(struct got_repository *repo, const char *refname, const char *target)
6453 const struct got_error *err = NULL;
6454 struct got_object_id *id = NULL;
6455 struct got_reference *ref = NULL;
6456 struct got_reflist_head refs;
6459 * Don't let the user create a reference name with a leading '-'.
6460 * While technically a valid reference name, this case is usually
6461 * an unintended typo.
6463 if (refname[0] == '-')
6464 return got_error_path(refname, GOT_ERR_REF_NAME_MINUS);
6466 TAILQ_INIT(&refs);
6467 err = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name, NULL);
6468 if (err)
6469 goto done;
6470 err = got_repo_match_object_id(&id, NULL, target, GOT_OBJ_TYPE_ANY,
6471 &refs, repo);
6472 got_ref_list_free(&refs);
6473 if (err)
6474 goto done;
6476 err = got_ref_alloc(&ref, refname, id);
6477 if (err)
6478 goto done;
6480 err = got_ref_write(ref, repo);
6481 done:
6482 if (ref)
6483 got_ref_close(ref);
6484 free(id);
6485 return err;
6488 static const struct got_error *
6489 add_symref(struct got_repository *repo, const char *refname, const char *target)
6491 const struct got_error *err = NULL;
6492 struct got_reference *ref = NULL;
6493 struct got_reference *target_ref = NULL;
6496 * Don't let the user create a reference name with a leading '-'.
6497 * While technically a valid reference name, this case is usually
6498 * an unintended typo.
6500 if (refname[0] == '-')
6501 return got_error_path(refname, GOT_ERR_REF_NAME_MINUS);
6503 err = got_ref_open(&target_ref, repo, target, 0);
6504 if (err)
6505 return err;
6507 err = got_ref_alloc_symref(&ref, refname, target_ref);
6508 if (err)
6509 goto done;
6511 err = got_ref_write(ref, repo);
6512 done:
6513 if (target_ref)
6514 got_ref_close(target_ref);
6515 if (ref)
6516 got_ref_close(ref);
6517 return err;
6520 static const struct got_error *
6521 cmd_ref(int argc, char *argv[])
6523 const struct got_error *error = NULL;
6524 struct got_repository *repo = NULL;
6525 struct got_worktree *worktree = NULL;
6526 char *cwd = NULL, *repo_path = NULL;
6527 int ch, do_list = 0, do_delete = 0, sort_by_time = 0;
6528 const char *obj_arg = NULL, *symref_target= NULL;
6529 char *refname = NULL;
6530 int *pack_fds = NULL;
6532 #ifndef PROFILE
6533 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
6534 "sendfd unveil", NULL) == -1)
6535 err(1, "pledge");
6536 #endif
6538 while ((ch = getopt(argc, argv, "c:dlr:s:t")) != -1) {
6539 switch (ch) {
6540 case 'c':
6541 obj_arg = optarg;
6542 break;
6543 case 'd':
6544 do_delete = 1;
6545 break;
6546 case 'l':
6547 do_list = 1;
6548 break;
6549 case 'r':
6550 repo_path = realpath(optarg, NULL);
6551 if (repo_path == NULL)
6552 return got_error_from_errno2("realpath",
6553 optarg);
6554 got_path_strip_trailing_slashes(repo_path);
6555 break;
6556 case 's':
6557 symref_target = optarg;
6558 break;
6559 case 't':
6560 sort_by_time = 1;
6561 break;
6562 default:
6563 usage_ref();
6564 /* NOTREACHED */
6568 if (obj_arg && do_list)
6569 option_conflict('c', 'l');
6570 if (obj_arg && do_delete)
6571 option_conflict('c', 'd');
6572 if (obj_arg && symref_target)
6573 option_conflict('c', 's');
6574 if (symref_target && do_delete)
6575 option_conflict('s', 'd');
6576 if (symref_target && do_list)
6577 option_conflict('s', 'l');
6578 if (do_delete && do_list)
6579 option_conflict('d', 'l');
6580 if (sort_by_time && !do_list)
6581 errx(1, "-t option requires -l option");
6583 argc -= optind;
6584 argv += optind;
6586 if (do_list) {
6587 if (argc != 0 && argc != 1)
6588 usage_ref();
6589 if (argc == 1) {
6590 refname = strdup(argv[0]);
6591 if (refname == NULL) {
6592 error = got_error_from_errno("strdup");
6593 goto done;
6596 } else {
6597 if (argc != 1)
6598 usage_ref();
6599 refname = strdup(argv[0]);
6600 if (refname == NULL) {
6601 error = got_error_from_errno("strdup");
6602 goto done;
6606 if (refname)
6607 got_path_strip_trailing_slashes(refname);
6609 cwd = getcwd(NULL, 0);
6610 if (cwd == NULL) {
6611 error = got_error_from_errno("getcwd");
6612 goto done;
6615 error = got_repo_pack_fds_open(&pack_fds);
6616 if (error != NULL)
6617 goto done;
6619 if (repo_path == NULL) {
6620 error = got_worktree_open(&worktree, cwd);
6621 if (error && error->code != GOT_ERR_NOT_WORKTREE)
6622 goto done;
6623 else
6624 error = NULL;
6625 if (worktree) {
6626 repo_path =
6627 strdup(got_worktree_get_repo_path(worktree));
6628 if (repo_path == NULL)
6629 error = got_error_from_errno("strdup");
6630 if (error)
6631 goto done;
6632 } else {
6633 repo_path = strdup(cwd);
6634 if (repo_path == NULL) {
6635 error = got_error_from_errno("strdup");
6636 goto done;
6641 error = got_repo_open(&repo, repo_path, NULL, pack_fds);
6642 if (error != NULL)
6643 goto done;
6645 #ifndef PROFILE
6646 if (do_list) {
6647 /* Remove "cpath" promise. */
6648 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
6649 NULL) == -1)
6650 err(1, "pledge");
6652 #endif
6654 error = apply_unveil(got_repo_get_path(repo), do_list,
6655 worktree ? got_worktree_get_root_path(worktree) : NULL);
6656 if (error)
6657 goto done;
6659 if (do_list)
6660 error = list_refs(repo, refname, sort_by_time);
6661 else if (do_delete)
6662 error = delete_ref_by_name(repo, refname);
6663 else if (symref_target)
6664 error = add_symref(repo, refname, symref_target);
6665 else {
6666 if (obj_arg == NULL)
6667 usage_ref();
6668 error = add_ref(repo, refname, obj_arg);
6670 done:
6671 free(refname);
6672 if (repo) {
6673 const struct got_error *close_err = got_repo_close(repo);
6674 if (error == NULL)
6675 error = close_err;
6677 if (worktree)
6678 got_worktree_close(worktree);
6679 if (pack_fds) {
6680 const struct got_error *pack_err =
6681 got_repo_pack_fds_close(pack_fds);
6682 if (error == NULL)
6683 error = pack_err;
6685 free(cwd);
6686 free(repo_path);
6687 return error;
6690 __dead static void
6691 usage_branch(void)
6693 fprintf(stderr, "usage: %s branch [-lnt] [-c commit] [-d name] "
6694 "[-r repository-path] [name]\n", getprogname());
6695 exit(1);
6698 static const struct got_error *
6699 list_branch(struct got_repository *repo, struct got_worktree *worktree,
6700 struct got_reference *ref)
6702 const struct got_error *err = NULL;
6703 const char *refname, *marker = " ";
6704 char *refstr;
6706 refname = got_ref_get_name(ref);
6707 if (worktree && strcmp(refname,
6708 got_worktree_get_head_ref_name(worktree)) == 0) {
6709 struct got_object_id *id = NULL;
6711 err = got_ref_resolve(&id, repo, ref);
6712 if (err)
6713 return err;
6714 if (got_object_id_cmp(id,
6715 got_worktree_get_base_commit_id(worktree)) == 0)
6716 marker = "* ";
6717 else
6718 marker = "~ ";
6719 free(id);
6722 if (strncmp(refname, "refs/heads/", 11) == 0)
6723 refname += 11;
6724 if (strncmp(refname, "refs/got/worktree/", 18) == 0)
6725 refname += 18;
6726 if (strncmp(refname, "refs/remotes/", 13) == 0)
6727 refname += 13;
6729 refstr = got_ref_to_str(ref);
6730 if (refstr == NULL)
6731 return got_error_from_errno("got_ref_to_str");
6733 printf("%s%s: %s\n", marker, refname, refstr);
6734 free(refstr);
6735 return NULL;
6738 static const struct got_error *
6739 show_current_branch(struct got_repository *repo, struct got_worktree *worktree)
6741 const char *refname;
6743 if (worktree == NULL)
6744 return got_error(GOT_ERR_NOT_WORKTREE);
6746 refname = got_worktree_get_head_ref_name(worktree);
6748 if (strncmp(refname, "refs/heads/", 11) == 0)
6749 refname += 11;
6750 if (strncmp(refname, "refs/got/worktree/", 18) == 0)
6751 refname += 18;
6753 printf("%s\n", refname);
6755 return NULL;
6758 static const struct got_error *
6759 list_branches(struct got_repository *repo, struct got_worktree *worktree,
6760 int sort_by_time)
6762 static const struct got_error *err = NULL;
6763 struct got_reflist_head refs;
6764 struct got_reflist_entry *re;
6765 struct got_reference *temp_ref = NULL;
6766 int rebase_in_progress, histedit_in_progress;
6768 TAILQ_INIT(&refs);
6770 if (worktree) {
6771 err = got_worktree_rebase_in_progress(&rebase_in_progress,
6772 worktree);
6773 if (err)
6774 return err;
6776 err = got_worktree_histedit_in_progress(&histedit_in_progress,
6777 worktree);
6778 if (err)
6779 return err;
6781 if (rebase_in_progress || histedit_in_progress) {
6782 err = got_ref_open(&temp_ref, repo,
6783 got_worktree_get_head_ref_name(worktree), 0);
6784 if (err)
6785 return err;
6786 list_branch(repo, worktree, temp_ref);
6787 got_ref_close(temp_ref);
6791 err = got_ref_list(&refs, repo, "refs/heads", sort_by_time ?
6792 got_ref_cmp_by_commit_timestamp_descending : got_ref_cmp_by_name,
6793 repo);
6794 if (err)
6795 return err;
6797 TAILQ_FOREACH(re, &refs, entry)
6798 list_branch(repo, worktree, re->ref);
6800 got_ref_list_free(&refs);
6802 err = got_ref_list(&refs, repo, "refs/remotes", sort_by_time ?
6803 got_ref_cmp_by_commit_timestamp_descending : got_ref_cmp_by_name,
6804 repo);
6805 if (err)
6806 return err;
6808 TAILQ_FOREACH(re, &refs, entry)
6809 list_branch(repo, worktree, re->ref);
6811 got_ref_list_free(&refs);
6813 return NULL;
6816 static const struct got_error *
6817 delete_branch(struct got_repository *repo, struct got_worktree *worktree,
6818 const char *branch_name)
6820 const struct got_error *err = NULL;
6821 struct got_reference *ref = NULL;
6822 char *refname, *remote_refname = NULL;
6824 if (strncmp(branch_name, "refs/", 5) == 0)
6825 branch_name += 5;
6826 if (strncmp(branch_name, "heads/", 6) == 0)
6827 branch_name += 6;
6828 else if (strncmp(branch_name, "remotes/", 8) == 0)
6829 branch_name += 8;
6831 if (asprintf(&refname, "refs/heads/%s", branch_name) == -1)
6832 return got_error_from_errno("asprintf");
6834 if (asprintf(&remote_refname, "refs/remotes/%s",
6835 branch_name) == -1) {
6836 err = got_error_from_errno("asprintf");
6837 goto done;
6840 err = got_ref_open(&ref, repo, refname, 0);
6841 if (err) {
6842 const struct got_error *err2;
6843 if (err->code != GOT_ERR_NOT_REF)
6844 goto done;
6846 * Keep 'err' intact such that if neither branch exists
6847 * we report "refs/heads" rather than "refs/remotes" in
6848 * our error message.
6850 err2 = got_ref_open(&ref, repo, remote_refname, 0);
6851 if (err2)
6852 goto done;
6853 err = NULL;
6856 if (worktree &&
6857 strcmp(got_worktree_get_head_ref_name(worktree),
6858 got_ref_get_name(ref)) == 0) {
6859 err = got_error_msg(GOT_ERR_SAME_BRANCH,
6860 "will not delete this work tree's current branch");
6861 goto done;
6864 err = delete_ref(repo, ref);
6865 done:
6866 if (ref)
6867 got_ref_close(ref);
6868 free(refname);
6869 free(remote_refname);
6870 return err;
6873 static const struct got_error *
6874 add_branch(struct got_repository *repo, const char *branch_name,
6875 struct got_object_id *base_commit_id)
6877 const struct got_error *err = NULL;
6878 struct got_reference *ref = NULL;
6879 char *refname = NULL;
6882 * Don't let the user create a branch name with a leading '-'.
6883 * While technically a valid reference name, this case is usually
6884 * an unintended typo.
6886 if (branch_name[0] == '-')
6887 return got_error_path(branch_name, GOT_ERR_REF_NAME_MINUS);
6889 if (strncmp(branch_name, "refs/heads/", 11) == 0)
6890 branch_name += 11;
6892 if (asprintf(&refname, "refs/heads/%s", branch_name) == -1) {
6893 err = got_error_from_errno("asprintf");
6894 goto done;
6897 err = got_ref_open(&ref, repo, refname, 0);
6898 if (err == NULL) {
6899 err = got_error(GOT_ERR_BRANCH_EXISTS);
6900 goto done;
6901 } else if (err->code != GOT_ERR_NOT_REF)
6902 goto done;
6904 err = got_ref_alloc(&ref, refname, base_commit_id);
6905 if (err)
6906 goto done;
6908 err = got_ref_write(ref, repo);
6909 done:
6910 if (ref)
6911 got_ref_close(ref);
6912 free(refname);
6913 return err;
6916 static const struct got_error *
6917 cmd_branch(int argc, char *argv[])
6919 const struct got_error *error = NULL;
6920 struct got_repository *repo = NULL;
6921 struct got_worktree *worktree = NULL;
6922 char *cwd = NULL, *repo_path = NULL;
6923 int ch, do_list = 0, do_show = 0, do_update = 1, sort_by_time = 0;
6924 const char *delref = NULL, *commit_id_arg = NULL;
6925 struct got_reference *ref = NULL;
6926 struct got_pathlist_head paths;
6927 struct got_object_id *commit_id = NULL;
6928 char *commit_id_str = NULL;
6929 int *pack_fds = NULL;
6931 TAILQ_INIT(&paths);
6933 #ifndef PROFILE
6934 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
6935 "sendfd unveil", NULL) == -1)
6936 err(1, "pledge");
6937 #endif
6939 while ((ch = getopt(argc, argv, "c:d:lnr:t")) != -1) {
6940 switch (ch) {
6941 case 'c':
6942 commit_id_arg = optarg;
6943 break;
6944 case 'd':
6945 delref = optarg;
6946 break;
6947 case 'l':
6948 do_list = 1;
6949 break;
6950 case 'n':
6951 do_update = 0;
6952 break;
6953 case 'r':
6954 repo_path = realpath(optarg, NULL);
6955 if (repo_path == NULL)
6956 return got_error_from_errno2("realpath",
6957 optarg);
6958 got_path_strip_trailing_slashes(repo_path);
6959 break;
6960 case 't':
6961 sort_by_time = 1;
6962 break;
6963 default:
6964 usage_branch();
6965 /* NOTREACHED */
6969 if (do_list && delref)
6970 option_conflict('l', 'd');
6971 if (sort_by_time && !do_list)
6972 errx(1, "-t option requires -l option");
6974 argc -= optind;
6975 argv += optind;
6977 if (!do_list && !delref && argc == 0)
6978 do_show = 1;
6980 if ((do_list || delref || do_show) && commit_id_arg != NULL)
6981 errx(1, "-c option can only be used when creating a branch");
6983 if (do_list || delref) {
6984 if (argc > 0)
6985 usage_branch();
6986 } else if (!do_show && argc != 1)
6987 usage_branch();
6989 cwd = getcwd(NULL, 0);
6990 if (cwd == NULL) {
6991 error = got_error_from_errno("getcwd");
6992 goto done;
6995 error = got_repo_pack_fds_open(&pack_fds);
6996 if (error != NULL)
6997 goto done;
6999 if (repo_path == NULL) {
7000 error = got_worktree_open(&worktree, cwd);
7001 if (error && error->code != GOT_ERR_NOT_WORKTREE)
7002 goto done;
7003 else
7004 error = NULL;
7005 if (worktree) {
7006 repo_path =
7007 strdup(got_worktree_get_repo_path(worktree));
7008 if (repo_path == NULL)
7009 error = got_error_from_errno("strdup");
7010 if (error)
7011 goto done;
7012 } else {
7013 repo_path = strdup(cwd);
7014 if (repo_path == NULL) {
7015 error = got_error_from_errno("strdup");
7016 goto done;
7021 error = got_repo_open(&repo, repo_path, NULL, pack_fds);
7022 if (error != NULL)
7023 goto done;
7025 #ifndef PROFILE
7026 if (do_list || do_show) {
7027 /* Remove "cpath" promise. */
7028 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
7029 NULL) == -1)
7030 err(1, "pledge");
7032 #endif
7034 error = apply_unveil(got_repo_get_path(repo), do_list,
7035 worktree ? got_worktree_get_root_path(worktree) : NULL);
7036 if (error)
7037 goto done;
7039 if (do_show)
7040 error = show_current_branch(repo, worktree);
7041 else if (do_list)
7042 error = list_branches(repo, worktree, sort_by_time);
7043 else if (delref)
7044 error = delete_branch(repo, worktree, delref);
7045 else {
7046 struct got_reflist_head refs;
7047 TAILQ_INIT(&refs);
7048 error = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name,
7049 NULL);
7050 if (error)
7051 goto done;
7052 if (commit_id_arg == NULL)
7053 commit_id_arg = worktree ?
7054 got_worktree_get_head_ref_name(worktree) :
7055 GOT_REF_HEAD;
7056 error = got_repo_match_object_id(&commit_id, NULL,
7057 commit_id_arg, GOT_OBJ_TYPE_COMMIT, &refs, repo);
7058 got_ref_list_free(&refs);
7059 if (error)
7060 goto done;
7061 error = add_branch(repo, argv[0], commit_id);
7062 if (error)
7063 goto done;
7064 if (worktree && do_update) {
7065 struct got_update_progress_arg upa;
7066 char *branch_refname = NULL;
7068 error = got_object_id_str(&commit_id_str, commit_id);
7069 if (error)
7070 goto done;
7071 error = get_worktree_paths_from_argv(&paths, 0, NULL,
7072 worktree);
7073 if (error)
7074 goto done;
7075 if (asprintf(&branch_refname, "refs/heads/%s", argv[0])
7076 == -1) {
7077 error = got_error_from_errno("asprintf");
7078 goto done;
7080 error = got_ref_open(&ref, repo, branch_refname, 0);
7081 free(branch_refname);
7082 if (error)
7083 goto done;
7084 error = switch_head_ref(ref, commit_id, worktree,
7085 repo);
7086 if (error)
7087 goto done;
7088 error = got_worktree_set_base_commit_id(worktree, repo,
7089 commit_id);
7090 if (error)
7091 goto done;
7092 memset(&upa, 0, sizeof(upa));
7093 error = got_worktree_checkout_files(worktree, &paths,
7094 repo, update_progress, &upa, check_cancelled,
7095 NULL);
7096 if (error)
7097 goto done;
7098 if (upa.did_something) {
7099 printf("Updated to %s: %s\n",
7100 got_worktree_get_head_ref_name(worktree),
7101 commit_id_str);
7103 print_update_progress_stats(&upa);
7106 done:
7107 if (ref)
7108 got_ref_close(ref);
7109 if (repo) {
7110 const struct got_error *close_err = got_repo_close(repo);
7111 if (error == NULL)
7112 error = close_err;
7114 if (worktree)
7115 got_worktree_close(worktree);
7116 if (pack_fds) {
7117 const struct got_error *pack_err =
7118 got_repo_pack_fds_close(pack_fds);
7119 if (error == NULL)
7120 error = pack_err;
7122 free(cwd);
7123 free(repo_path);
7124 free(commit_id);
7125 free(commit_id_str);
7126 got_pathlist_free(&paths, GOT_PATHLIST_FREE_PATH);
7127 return error;
7131 __dead static void
7132 usage_tag(void)
7134 fprintf(stderr, "usage: %s tag [-lVv] [-c commit] [-m message] "
7135 "[-r repository-path] [-s signer-id] name\n", getprogname());
7136 exit(1);
7139 #if 0
7140 static const struct got_error *
7141 sort_tags(struct got_reflist_head *sorted, struct got_reflist_head *tags)
7143 const struct got_error *err = NULL;
7144 struct got_reflist_entry *re, *se, *new;
7145 struct got_object_id *re_id, *se_id;
7146 struct got_tag_object *re_tag, *se_tag;
7147 time_t re_time, se_time;
7149 STAILQ_FOREACH(re, tags, entry) {
7150 se = STAILQ_FIRST(sorted);
7151 if (se == NULL) {
7152 err = got_reflist_entry_dup(&new, re);
7153 if (err)
7154 return err;
7155 STAILQ_INSERT_HEAD(sorted, new, entry);
7156 continue;
7157 } else {
7158 err = got_ref_resolve(&re_id, repo, re->ref);
7159 if (err)
7160 break;
7161 err = got_object_open_as_tag(&re_tag, repo, re_id);
7162 free(re_id);
7163 if (err)
7164 break;
7165 re_time = got_object_tag_get_tagger_time(re_tag);
7166 got_object_tag_close(re_tag);
7169 while (se) {
7170 err = got_ref_resolve(&se_id, repo, re->ref);
7171 if (err)
7172 break;
7173 err = got_object_open_as_tag(&se_tag, repo, se_id);
7174 free(se_id);
7175 if (err)
7176 break;
7177 se_time = got_object_tag_get_tagger_time(se_tag);
7178 got_object_tag_close(se_tag);
7180 if (se_time > re_time) {
7181 err = got_reflist_entry_dup(&new, re);
7182 if (err)
7183 return err;
7184 STAILQ_INSERT_AFTER(sorted, se, new, entry);
7185 break;
7187 se = STAILQ_NEXT(se, entry);
7188 continue;
7191 done:
7192 return err;
7194 #endif
7196 static const struct got_error *
7197 get_tag_refname(char **refname, const char *tag_name)
7199 const struct got_error *err;
7201 if (strncmp("refs/tags/", tag_name, 10) == 0) {
7202 *refname = strdup(tag_name);
7203 if (*refname == NULL)
7204 return got_error_from_errno("strdup");
7205 } else if (asprintf(refname, "refs/tags/%s", tag_name) == -1) {
7206 err = got_error_from_errno("asprintf");
7207 *refname = NULL;
7208 return err;
7211 return NULL;
7214 static const struct got_error *
7215 list_tags(struct got_repository *repo, const char *tag_name, int verify_tags,
7216 const char *allowed_signers, const char *revoked_signers, int verbosity)
7218 static const struct got_error *err = NULL;
7219 struct got_reflist_head refs;
7220 struct got_reflist_entry *re;
7221 char *wanted_refname = NULL;
7222 int bad_sigs = 0;
7224 TAILQ_INIT(&refs);
7226 err = got_ref_list(&refs, repo, "refs/tags", got_ref_cmp_tags, repo);
7227 if (err)
7228 return err;
7230 if (tag_name) {
7231 struct got_reference *ref;
7232 err = get_tag_refname(&wanted_refname, tag_name);
7233 if (err)
7234 goto done;
7235 /* Wanted tag reference should exist. */
7236 err = got_ref_open(&ref, repo, wanted_refname, 0);
7237 if (err)
7238 goto done;
7239 got_ref_close(ref);
7242 TAILQ_FOREACH(re, &refs, entry) {
7243 const char *refname;
7244 char *refstr, *tagmsg0, *tagmsg, *line, *id_str, *datestr;
7245 char datebuf[26];
7246 const char *tagger, *ssh_sig = NULL;
7247 char *sig_msg = NULL;
7248 time_t tagger_time;
7249 struct got_object_id *id;
7250 struct got_tag_object *tag;
7251 struct got_commit_object *commit = NULL;
7253 refname = got_ref_get_name(re->ref);
7254 if (strncmp(refname, "refs/tags/", 10) != 0 ||
7255 (wanted_refname && strcmp(refname, wanted_refname) != 0))
7256 continue;
7257 refname += 10;
7258 refstr = got_ref_to_str(re->ref);
7259 if (refstr == NULL) {
7260 err = got_error_from_errno("got_ref_to_str");
7261 break;
7264 err = got_ref_resolve(&id, repo, re->ref);
7265 if (err)
7266 break;
7267 err = got_object_open_as_tag(&tag, repo, id);
7268 if (err) {
7269 if (err->code != GOT_ERR_OBJ_TYPE) {
7270 free(id);
7271 break;
7273 /* "lightweight" tag */
7274 err = got_object_open_as_commit(&commit, repo, id);
7275 if (err) {
7276 free(id);
7277 break;
7279 tagger = got_object_commit_get_committer(commit);
7280 tagger_time =
7281 got_object_commit_get_committer_time(commit);
7282 err = got_object_id_str(&id_str, id);
7283 free(id);
7284 if (err)
7285 break;
7286 } else {
7287 free(id);
7288 tagger = got_object_tag_get_tagger(tag);
7289 tagger_time = got_object_tag_get_tagger_time(tag);
7290 err = got_object_id_str(&id_str,
7291 got_object_tag_get_object_id(tag));
7292 if (err)
7293 break;
7296 if (tag && verify_tags) {
7297 ssh_sig = got_sigs_get_tagmsg_ssh_signature(
7298 got_object_tag_get_message(tag));
7299 if (ssh_sig && allowed_signers == NULL) {
7300 err = got_error_msg(
7301 GOT_ERR_VERIFY_TAG_SIGNATURE,
7302 "SSH signature verification requires "
7303 "setting allowed_signers in "
7304 "got.conf(5)");
7305 break;
7309 printf("%stag %s %s\n", GOT_COMMIT_SEP_STR, refname, refstr);
7310 free(refstr);
7311 printf("from: %s\n", tagger);
7312 datestr = get_datestr(&tagger_time, datebuf);
7313 if (datestr)
7314 printf("date: %s UTC\n", datestr);
7315 if (commit)
7316 printf("object: %s %s\n", GOT_OBJ_LABEL_COMMIT, id_str);
7317 else {
7318 switch (got_object_tag_get_object_type(tag)) {
7319 case GOT_OBJ_TYPE_BLOB:
7320 printf("object: %s %s\n", GOT_OBJ_LABEL_BLOB,
7321 id_str);
7322 break;
7323 case GOT_OBJ_TYPE_TREE:
7324 printf("object: %s %s\n", GOT_OBJ_LABEL_TREE,
7325 id_str);
7326 break;
7327 case GOT_OBJ_TYPE_COMMIT:
7328 printf("object: %s %s\n", GOT_OBJ_LABEL_COMMIT,
7329 id_str);
7330 break;
7331 case GOT_OBJ_TYPE_TAG:
7332 printf("object: %s %s\n", GOT_OBJ_LABEL_TAG,
7333 id_str);
7334 break;
7335 default:
7336 break;
7339 free(id_str);
7341 if (ssh_sig) {
7342 err = got_sigs_verify_tag_ssh(&sig_msg, tag, ssh_sig,
7343 allowed_signers, revoked_signers, verbosity);
7344 if (err && err->code == GOT_ERR_BAD_TAG_SIGNATURE)
7345 bad_sigs = 1;
7346 else if (err)
7347 break;
7348 printf("signature: %s", sig_msg);
7349 free(sig_msg);
7350 sig_msg = NULL;
7353 if (commit) {
7354 err = got_object_commit_get_logmsg(&tagmsg0, commit);
7355 if (err)
7356 break;
7357 got_object_commit_close(commit);
7358 } else {
7359 tagmsg0 = strdup(got_object_tag_get_message(tag));
7360 got_object_tag_close(tag);
7361 if (tagmsg0 == NULL) {
7362 err = got_error_from_errno("strdup");
7363 break;
7367 tagmsg = tagmsg0;
7368 do {
7369 line = strsep(&tagmsg, "\n");
7370 if (line)
7371 printf(" %s\n", line);
7372 } while (line);
7373 free(tagmsg0);
7375 done:
7376 got_ref_list_free(&refs);
7377 free(wanted_refname);
7379 if (err == NULL && bad_sigs)
7380 err = got_error(GOT_ERR_BAD_TAG_SIGNATURE);
7381 return err;
7384 static const struct got_error *
7385 get_tag_message(char **tagmsg, char **tagmsg_path, const char *commit_id_str,
7386 const char *tag_name, const char *repo_path)
7388 const struct got_error *err = NULL;
7389 char *template = NULL, *initial_content = NULL;
7390 char *editor = NULL;
7391 int initial_content_len;
7392 int fd = -1;
7394 if (asprintf(&template, GOT_TMPDIR_STR "/got-tagmsg") == -1) {
7395 err = got_error_from_errno("asprintf");
7396 goto done;
7399 initial_content_len = asprintf(&initial_content,
7400 "\n# tagging commit %s as %s\n",
7401 commit_id_str, tag_name);
7402 if (initial_content_len == -1) {
7403 err = got_error_from_errno("asprintf");
7404 goto done;
7407 err = got_opentemp_named_fd(tagmsg_path, &fd, template, "");
7408 if (err)
7409 goto done;
7411 if (write(fd, initial_content, initial_content_len) == -1) {
7412 err = got_error_from_errno2("write", *tagmsg_path);
7413 goto done;
7415 if (close(fd) == -1) {
7416 err = got_error_from_errno2("close", *tagmsg_path);
7417 goto done;
7419 fd = -1;
7421 err = get_editor(&editor);
7422 if (err)
7423 goto done;
7424 err = edit_logmsg(tagmsg, editor, *tagmsg_path, initial_content,
7425 initial_content_len, 1);
7426 done:
7427 free(initial_content);
7428 free(template);
7429 free(editor);
7431 if (fd != -1 && close(fd) == -1 && err == NULL)
7432 err = got_error_from_errno2("close", *tagmsg_path);
7434 if (err) {
7435 free(*tagmsg);
7436 *tagmsg = NULL;
7438 return err;
7441 static const struct got_error *
7442 add_tag(struct got_repository *repo, const char *tagger,
7443 const char *tag_name, const char *commit_arg, const char *tagmsg_arg,
7444 const char *signer_id, int verbosity)
7446 const struct got_error *err = NULL;
7447 struct got_object_id *commit_id = NULL, *tag_id = NULL;
7448 char *label = NULL, *commit_id_str = NULL;
7449 struct got_reference *ref = NULL;
7450 char *refname = NULL, *tagmsg = NULL;
7451 char *tagmsg_path = NULL, *tag_id_str = NULL;
7452 int preserve_tagmsg = 0;
7453 struct got_reflist_head refs;
7455 TAILQ_INIT(&refs);
7458 * Don't let the user create a tag name with a leading '-'.
7459 * While technically a valid reference name, this case is usually
7460 * an unintended typo.
7462 if (tag_name[0] == '-')
7463 return got_error_path(tag_name, GOT_ERR_REF_NAME_MINUS);
7465 err = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name, NULL);
7466 if (err)
7467 goto done;
7469 err = got_repo_match_object_id(&commit_id, &label, commit_arg,
7470 GOT_OBJ_TYPE_COMMIT, &refs, repo);
7471 if (err)
7472 goto done;
7474 err = got_object_id_str(&commit_id_str, commit_id);
7475 if (err)
7476 goto done;
7478 err = get_tag_refname(&refname, tag_name);
7479 if (err)
7480 goto done;
7481 if (strncmp("refs/tags/", tag_name, 10) == 0)
7482 tag_name += 10;
7484 err = got_ref_open(&ref, repo, refname, 0);
7485 if (err == NULL) {
7486 err = got_error(GOT_ERR_TAG_EXISTS);
7487 goto done;
7488 } else if (err->code != GOT_ERR_NOT_REF)
7489 goto done;
7491 if (tagmsg_arg == NULL) {
7492 err = get_tag_message(&tagmsg, &tagmsg_path, commit_id_str,
7493 tag_name, got_repo_get_path(repo));
7494 if (err) {
7495 if (err->code != GOT_ERR_COMMIT_MSG_EMPTY &&
7496 tagmsg_path != NULL)
7497 preserve_tagmsg = 1;
7498 goto done;
7500 /* Editor is done; we can now apply unveil(2) */
7501 err = got_sigs_apply_unveil();
7502 if (err)
7503 goto done;
7504 err = apply_unveil(got_repo_get_path(repo), 0, NULL);
7505 if (err)
7506 goto done;
7509 err = got_object_tag_create(&tag_id, tag_name, commit_id,
7510 tagger, time(NULL), tagmsg ? tagmsg : tagmsg_arg, signer_id, repo,
7511 verbosity);
7512 if (err) {
7513 if (tagmsg_path)
7514 preserve_tagmsg = 1;
7515 goto done;
7518 err = got_ref_alloc(&ref, refname, tag_id);
7519 if (err) {
7520 if (tagmsg_path)
7521 preserve_tagmsg = 1;
7522 goto done;
7525 err = got_ref_write(ref, repo);
7526 if (err) {
7527 if (tagmsg_path)
7528 preserve_tagmsg = 1;
7529 goto done;
7532 err = got_object_id_str(&tag_id_str, tag_id);
7533 if (err) {
7534 if (tagmsg_path)
7535 preserve_tagmsg = 1;
7536 goto done;
7538 printf("Created tag %s\n", tag_id_str);
7539 done:
7540 if (preserve_tagmsg) {
7541 fprintf(stderr, "%s: tag message preserved in %s\n",
7542 getprogname(), tagmsg_path);
7543 } else if (tagmsg_path && unlink(tagmsg_path) == -1 && err == NULL)
7544 err = got_error_from_errno2("unlink", tagmsg_path);
7545 free(tag_id_str);
7546 if (ref)
7547 got_ref_close(ref);
7548 free(commit_id);
7549 free(commit_id_str);
7550 free(refname);
7551 free(tagmsg);
7552 free(tagmsg_path);
7553 got_ref_list_free(&refs);
7554 return err;
7557 static const struct got_error *
7558 cmd_tag(int argc, char *argv[])
7560 const struct got_error *error = NULL;
7561 struct got_repository *repo = NULL;
7562 struct got_worktree *worktree = NULL;
7563 char *cwd = NULL, *repo_path = NULL, *commit_id_str = NULL;
7564 char *gitconfig_path = NULL, *tagger = NULL;
7565 char *allowed_signers = NULL, *revoked_signers = NULL;
7566 const char *signer_id = NULL;
7567 const char *tag_name = NULL, *commit_id_arg = NULL, *tagmsg = NULL;
7568 int ch, do_list = 0, verify_tags = 0, verbosity = 0;
7569 int *pack_fds = NULL;
7571 #ifndef PROFILE
7572 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
7573 "sendfd unveil", NULL) == -1)
7574 err(1, "pledge");
7575 #endif
7577 while ((ch = getopt(argc, argv, "c:lm:r:s:Vv")) != -1) {
7578 switch (ch) {
7579 case 'c':
7580 commit_id_arg = optarg;
7581 break;
7582 case 'l':
7583 do_list = 1;
7584 break;
7585 case 'm':
7586 tagmsg = optarg;
7587 break;
7588 case 'r':
7589 repo_path = realpath(optarg, NULL);
7590 if (repo_path == NULL) {
7591 error = got_error_from_errno2("realpath",
7592 optarg);
7593 goto done;
7595 got_path_strip_trailing_slashes(repo_path);
7596 break;
7597 case 's':
7598 signer_id = optarg;
7599 break;
7600 case 'V':
7601 verify_tags = 1;
7602 break;
7603 case 'v':
7604 if (verbosity < 0)
7605 verbosity = 0;
7606 else if (verbosity < 3)
7607 verbosity++;
7608 break;
7609 default:
7610 usage_tag();
7611 /* NOTREACHED */
7615 argc -= optind;
7616 argv += optind;
7618 if (do_list || verify_tags) {
7619 if (commit_id_arg != NULL)
7620 errx(1,
7621 "-c option can only be used when creating a tag");
7622 if (tagmsg) {
7623 if (do_list)
7624 option_conflict('l', 'm');
7625 else
7626 option_conflict('V', 'm');
7628 if (signer_id) {
7629 if (do_list)
7630 option_conflict('l', 's');
7631 else
7632 option_conflict('V', 's');
7634 if (argc > 1)
7635 usage_tag();
7636 } else if (argc != 1)
7637 usage_tag();
7639 if (argc == 1)
7640 tag_name = argv[0];
7642 cwd = getcwd(NULL, 0);
7643 if (cwd == NULL) {
7644 error = got_error_from_errno("getcwd");
7645 goto done;
7648 error = got_repo_pack_fds_open(&pack_fds);
7649 if (error != NULL)
7650 goto done;
7652 if (repo_path == NULL) {
7653 error = got_worktree_open(&worktree, cwd);
7654 if (error && error->code != GOT_ERR_NOT_WORKTREE)
7655 goto done;
7656 else
7657 error = NULL;
7658 if (worktree) {
7659 repo_path =
7660 strdup(got_worktree_get_repo_path(worktree));
7661 if (repo_path == NULL)
7662 error = got_error_from_errno("strdup");
7663 if (error)
7664 goto done;
7665 } else {
7666 repo_path = strdup(cwd);
7667 if (repo_path == NULL) {
7668 error = got_error_from_errno("strdup");
7669 goto done;
7674 if (do_list || verify_tags) {
7675 error = got_repo_open(&repo, repo_path, NULL, pack_fds);
7676 if (error != NULL)
7677 goto done;
7678 error = get_allowed_signers(&allowed_signers, repo, worktree);
7679 if (error)
7680 goto done;
7681 error = get_revoked_signers(&revoked_signers, repo, worktree);
7682 if (error)
7683 goto done;
7684 if (worktree) {
7685 /* Release work tree lock. */
7686 got_worktree_close(worktree);
7687 worktree = NULL;
7691 * Remove "cpath" promise unless needed for signature tmpfile
7692 * creation.
7694 if (verify_tags)
7695 got_sigs_apply_unveil();
7696 else {
7697 #ifndef PROFILE
7698 if (pledge("stdio rpath wpath flock proc exec sendfd "
7699 "unveil", NULL) == -1)
7700 err(1, "pledge");
7701 #endif
7703 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
7704 if (error)
7705 goto done;
7706 error = list_tags(repo, tag_name, verify_tags, allowed_signers,
7707 revoked_signers, verbosity);
7708 } else {
7709 error = get_gitconfig_path(&gitconfig_path);
7710 if (error)
7711 goto done;
7712 error = got_repo_open(&repo, repo_path, gitconfig_path,
7713 pack_fds);
7714 if (error != NULL)
7715 goto done;
7717 error = get_author(&tagger, repo, worktree);
7718 if (error)
7719 goto done;
7720 if (signer_id == NULL)
7721 signer_id = get_signer_id(repo, worktree);
7723 if (tagmsg) {
7724 if (signer_id) {
7725 error = got_sigs_apply_unveil();
7726 if (error)
7727 goto done;
7729 error = apply_unveil(got_repo_get_path(repo), 0, NULL);
7730 if (error)
7731 goto done;
7734 if (commit_id_arg == NULL) {
7735 struct got_reference *head_ref;
7736 struct got_object_id *commit_id;
7737 error = got_ref_open(&head_ref, repo,
7738 worktree ? got_worktree_get_head_ref_name(worktree)
7739 : GOT_REF_HEAD, 0);
7740 if (error)
7741 goto done;
7742 error = got_ref_resolve(&commit_id, repo, head_ref);
7743 got_ref_close(head_ref);
7744 if (error)
7745 goto done;
7746 error = got_object_id_str(&commit_id_str, commit_id);
7747 free(commit_id);
7748 if (error)
7749 goto done;
7752 if (worktree) {
7753 /* Release work tree lock. */
7754 got_worktree_close(worktree);
7755 worktree = NULL;
7758 error = add_tag(repo, tagger, tag_name,
7759 commit_id_str ? commit_id_str : commit_id_arg, tagmsg,
7760 signer_id, verbosity);
7762 done:
7763 if (repo) {
7764 const struct got_error *close_err = got_repo_close(repo);
7765 if (error == NULL)
7766 error = close_err;
7768 if (worktree)
7769 got_worktree_close(worktree);
7770 if (pack_fds) {
7771 const struct got_error *pack_err =
7772 got_repo_pack_fds_close(pack_fds);
7773 if (error == NULL)
7774 error = pack_err;
7776 free(cwd);
7777 free(repo_path);
7778 free(gitconfig_path);
7779 free(commit_id_str);
7780 free(tagger);
7781 free(allowed_signers);
7782 free(revoked_signers);
7783 return error;
7786 __dead static void
7787 usage_add(void)
7789 fprintf(stderr, "usage: %s add [-IR] path ...\n", getprogname());
7790 exit(1);
7793 static const struct got_error *
7794 add_progress(void *arg, unsigned char status, const char *path)
7796 while (path[0] == '/')
7797 path++;
7798 printf("%c %s\n", status, path);
7799 return NULL;
7802 static const struct got_error *
7803 cmd_add(int argc, char *argv[])
7805 const struct got_error *error = NULL;
7806 struct got_repository *repo = NULL;
7807 struct got_worktree *worktree = NULL;
7808 char *cwd = NULL;
7809 struct got_pathlist_head paths;
7810 struct got_pathlist_entry *pe;
7811 int ch, can_recurse = 0, no_ignores = 0;
7812 int *pack_fds = NULL;
7814 TAILQ_INIT(&paths);
7816 #ifndef PROFILE
7817 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
7818 NULL) == -1)
7819 err(1, "pledge");
7820 #endif
7822 while ((ch = getopt(argc, argv, "IR")) != -1) {
7823 switch (ch) {
7824 case 'I':
7825 no_ignores = 1;
7826 break;
7827 case 'R':
7828 can_recurse = 1;
7829 break;
7830 default:
7831 usage_add();
7832 /* NOTREACHED */
7836 argc -= optind;
7837 argv += optind;
7839 if (argc < 1)
7840 usage_add();
7842 cwd = getcwd(NULL, 0);
7843 if (cwd == NULL) {
7844 error = got_error_from_errno("getcwd");
7845 goto done;
7848 error = got_repo_pack_fds_open(&pack_fds);
7849 if (error != NULL)
7850 goto done;
7852 error = got_worktree_open(&worktree, cwd);
7853 if (error) {
7854 if (error->code == GOT_ERR_NOT_WORKTREE)
7855 error = wrap_not_worktree_error(error, "add", cwd);
7856 goto done;
7859 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
7860 NULL, pack_fds);
7861 if (error != NULL)
7862 goto done;
7864 error = apply_unveil(got_repo_get_path(repo), 1,
7865 got_worktree_get_root_path(worktree));
7866 if (error)
7867 goto done;
7869 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
7870 if (error)
7871 goto done;
7873 if (!can_recurse) {
7874 char *ondisk_path;
7875 struct stat sb;
7876 TAILQ_FOREACH(pe, &paths, entry) {
7877 if (asprintf(&ondisk_path, "%s/%s",
7878 got_worktree_get_root_path(worktree),
7879 pe->path) == -1) {
7880 error = got_error_from_errno("asprintf");
7881 goto done;
7883 if (lstat(ondisk_path, &sb) == -1) {
7884 if (errno == ENOENT) {
7885 free(ondisk_path);
7886 continue;
7888 error = got_error_from_errno2("lstat",
7889 ondisk_path);
7890 free(ondisk_path);
7891 goto done;
7893 free(ondisk_path);
7894 if (S_ISDIR(sb.st_mode)) {
7895 error = got_error_msg(GOT_ERR_BAD_PATH,
7896 "adding directories requires -R option");
7897 goto done;
7902 error = got_worktree_schedule_add(worktree, &paths, add_progress,
7903 NULL, repo, no_ignores);
7904 done:
7905 if (repo) {
7906 const struct got_error *close_err = got_repo_close(repo);
7907 if (error == NULL)
7908 error = close_err;
7910 if (worktree)
7911 got_worktree_close(worktree);
7912 if (pack_fds) {
7913 const struct got_error *pack_err =
7914 got_repo_pack_fds_close(pack_fds);
7915 if (error == NULL)
7916 error = pack_err;
7918 got_pathlist_free(&paths, GOT_PATHLIST_FREE_PATH);
7919 free(cwd);
7920 return error;
7923 __dead static void
7924 usage_remove(void)
7926 fprintf(stderr, "usage: %s remove [-fkR] [-s status-codes] path ...\n",
7927 getprogname());
7928 exit(1);
7931 static const struct got_error *
7932 print_remove_status(void *arg, unsigned char status,
7933 unsigned char staged_status, const char *path)
7935 while (path[0] == '/')
7936 path++;
7937 if (status == GOT_STATUS_NONEXISTENT)
7938 return NULL;
7939 if (status == staged_status && (status == GOT_STATUS_DELETE))
7940 status = GOT_STATUS_NO_CHANGE;
7941 printf("%c%c %s\n", status, staged_status, path);
7942 return NULL;
7945 static const struct got_error *
7946 cmd_remove(int argc, char *argv[])
7948 const struct got_error *error = NULL;
7949 struct got_worktree *worktree = NULL;
7950 struct got_repository *repo = NULL;
7951 const char *status_codes = NULL;
7952 char *cwd = NULL;
7953 struct got_pathlist_head paths;
7954 struct got_pathlist_entry *pe;
7955 int ch, delete_local_mods = 0, can_recurse = 0, keep_on_disk = 0, i;
7956 int ignore_missing_paths = 0;
7957 int *pack_fds = NULL;
7959 TAILQ_INIT(&paths);
7961 #ifndef PROFILE
7962 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
7963 NULL) == -1)
7964 err(1, "pledge");
7965 #endif
7967 while ((ch = getopt(argc, argv, "fkRs:")) != -1) {
7968 switch (ch) {
7969 case 'f':
7970 delete_local_mods = 1;
7971 ignore_missing_paths = 1;
7972 break;
7973 case 'k':
7974 keep_on_disk = 1;
7975 break;
7976 case 'R':
7977 can_recurse = 1;
7978 break;
7979 case 's':
7980 for (i = 0; i < strlen(optarg); i++) {
7981 switch (optarg[i]) {
7982 case GOT_STATUS_MODIFY:
7983 delete_local_mods = 1;
7984 break;
7985 case GOT_STATUS_MISSING:
7986 ignore_missing_paths = 1;
7987 break;
7988 default:
7989 errx(1, "invalid status code '%c'",
7990 optarg[i]);
7993 status_codes = optarg;
7994 break;
7995 default:
7996 usage_remove();
7997 /* NOTREACHED */
8001 argc -= optind;
8002 argv += optind;
8004 if (argc < 1)
8005 usage_remove();
8007 cwd = getcwd(NULL, 0);
8008 if (cwd == NULL) {
8009 error = got_error_from_errno("getcwd");
8010 goto done;
8013 error = got_repo_pack_fds_open(&pack_fds);
8014 if (error != NULL)
8015 goto done;
8017 error = got_worktree_open(&worktree, cwd);
8018 if (error) {
8019 if (error->code == GOT_ERR_NOT_WORKTREE)
8020 error = wrap_not_worktree_error(error, "remove", cwd);
8021 goto done;
8024 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
8025 NULL, pack_fds);
8026 if (error)
8027 goto done;
8029 error = apply_unveil(got_repo_get_path(repo), 1,
8030 got_worktree_get_root_path(worktree));
8031 if (error)
8032 goto done;
8034 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
8035 if (error)
8036 goto done;
8038 if (!can_recurse) {
8039 char *ondisk_path;
8040 struct stat sb;
8041 TAILQ_FOREACH(pe, &paths, entry) {
8042 if (asprintf(&ondisk_path, "%s/%s",
8043 got_worktree_get_root_path(worktree),
8044 pe->path) == -1) {
8045 error = got_error_from_errno("asprintf");
8046 goto done;
8048 if (lstat(ondisk_path, &sb) == -1) {
8049 if (errno == ENOENT) {
8050 free(ondisk_path);
8051 continue;
8053 error = got_error_from_errno2("lstat",
8054 ondisk_path);
8055 free(ondisk_path);
8056 goto done;
8058 free(ondisk_path);
8059 if (S_ISDIR(sb.st_mode)) {
8060 error = got_error_msg(GOT_ERR_BAD_PATH,
8061 "removing directories requires -R option");
8062 goto done;
8067 error = got_worktree_schedule_delete(worktree, &paths,
8068 delete_local_mods, status_codes, print_remove_status, NULL,
8069 repo, keep_on_disk, ignore_missing_paths);
8070 done:
8071 if (repo) {
8072 const struct got_error *close_err = got_repo_close(repo);
8073 if (error == NULL)
8074 error = close_err;
8076 if (worktree)
8077 got_worktree_close(worktree);
8078 if (pack_fds) {
8079 const struct got_error *pack_err =
8080 got_repo_pack_fds_close(pack_fds);
8081 if (error == NULL)
8082 error = pack_err;
8084 got_pathlist_free(&paths, GOT_PATHLIST_FREE_PATH);
8085 free(cwd);
8086 return error;
8089 __dead static void
8090 usage_patch(void)
8092 fprintf(stderr, "usage: %s patch [-nR] [-c commit] [-p strip-count] "
8093 "[patchfile]\n", getprogname());
8094 exit(1);
8097 static const struct got_error *
8098 patch_from_stdin(int *patchfd)
8100 const struct got_error *err = NULL;
8101 ssize_t r;
8102 char buf[BUFSIZ];
8103 sig_t sighup, sigint, sigquit;
8105 *patchfd = got_opentempfd();
8106 if (*patchfd == -1)
8107 return got_error_from_errno("got_opentempfd");
8109 sighup = signal(SIGHUP, SIG_DFL);
8110 sigint = signal(SIGINT, SIG_DFL);
8111 sigquit = signal(SIGQUIT, SIG_DFL);
8113 for (;;) {
8114 r = read(0, buf, sizeof(buf));
8115 if (r == -1) {
8116 err = got_error_from_errno("read");
8117 break;
8119 if (r == 0)
8120 break;
8121 if (write(*patchfd, buf, r) == -1) {
8122 err = got_error_from_errno("write");
8123 break;
8127 signal(SIGHUP, sighup);
8128 signal(SIGINT, sigint);
8129 signal(SIGQUIT, sigquit);
8131 if (err == NULL && lseek(*patchfd, 0, SEEK_SET) == -1)
8132 err = got_error_from_errno("lseek");
8134 if (err != NULL) {
8135 close(*patchfd);
8136 *patchfd = -1;
8139 return err;
8142 static const struct got_error *
8143 patch_progress(void *arg, const char *old, const char *new,
8144 unsigned char status, const struct got_error *error, int old_from,
8145 int old_lines, int new_from, int new_lines, int offset,
8146 int ws_mangled, const struct got_error *hunk_err)
8148 const char *path = new == NULL ? old : new;
8150 while (*path == '/')
8151 path++;
8153 if (status != 0)
8154 printf("%c %s\n", status, path);
8156 if (error != NULL)
8157 fprintf(stderr, "%s: %s\n", getprogname(), error->msg);
8159 if (offset != 0 || hunk_err != NULL || ws_mangled) {
8160 printf("@@ -%d,%d +%d,%d @@ ", old_from,
8161 old_lines, new_from, new_lines);
8162 if (hunk_err != NULL)
8163 printf("%s\n", hunk_err->msg);
8164 else if (offset != 0)
8165 printf("applied with offset %d\n", offset);
8166 else
8167 printf("hunk contains mangled whitespace\n");
8170 return NULL;
8173 static const struct got_error *
8174 cmd_patch(int argc, char *argv[])
8176 const struct got_error *error = NULL, *close_error = NULL;
8177 struct got_worktree *worktree = NULL;
8178 struct got_repository *repo = NULL;
8179 struct got_reflist_head refs;
8180 struct got_object_id *commit_id = NULL;
8181 const char *commit_id_str = NULL;
8182 struct stat sb;
8183 const char *errstr;
8184 char *cwd = NULL;
8185 int ch, nop = 0, strip = -1, reverse = 0;
8186 int patchfd;
8187 int *pack_fds = NULL;
8189 TAILQ_INIT(&refs);
8191 #ifndef PROFILE
8192 if (pledge("stdio rpath wpath cpath fattr proc exec sendfd flock "
8193 "unveil", NULL) == -1)
8194 err(1, "pledge");
8195 #endif
8197 while ((ch = getopt(argc, argv, "c:np:R")) != -1) {
8198 switch (ch) {
8199 case 'c':
8200 commit_id_str = optarg;
8201 break;
8202 case 'n':
8203 nop = 1;
8204 break;
8205 case 'p':
8206 strip = strtonum(optarg, 0, INT_MAX, &errstr);
8207 if (errstr != NULL)
8208 errx(1, "pathname strip count is %s: %s",
8209 errstr, optarg);
8210 break;
8211 case 'R':
8212 reverse = 1;
8213 break;
8214 default:
8215 usage_patch();
8216 /* NOTREACHED */
8220 argc -= optind;
8221 argv += optind;
8223 if (argc == 0) {
8224 error = patch_from_stdin(&patchfd);
8225 if (error)
8226 return error;
8227 } else if (argc == 1) {
8228 patchfd = open(argv[0], O_RDONLY);
8229 if (patchfd == -1) {
8230 error = got_error_from_errno2("open", argv[0]);
8231 return error;
8233 if (fstat(patchfd, &sb) == -1) {
8234 error = got_error_from_errno2("fstat", argv[0]);
8235 goto done;
8237 if (!S_ISREG(sb.st_mode)) {
8238 error = got_error_path(argv[0], GOT_ERR_BAD_FILETYPE);
8239 goto done;
8241 } else
8242 usage_patch();
8244 if ((cwd = getcwd(NULL, 0)) == NULL) {
8245 error = got_error_from_errno("getcwd");
8246 goto done;
8249 error = got_repo_pack_fds_open(&pack_fds);
8250 if (error != NULL)
8251 goto done;
8253 error = got_worktree_open(&worktree, cwd);
8254 if (error != NULL)
8255 goto done;
8257 const char *repo_path = got_worktree_get_repo_path(worktree);
8258 error = got_repo_open(&repo, repo_path, NULL, pack_fds);
8259 if (error != NULL)
8260 goto done;
8262 error = apply_unveil(got_repo_get_path(repo), 0,
8263 got_worktree_get_root_path(worktree));
8264 if (error != NULL)
8265 goto done;
8267 error = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name, NULL);
8268 if (error)
8269 goto done;
8271 if (commit_id_str != NULL) {
8272 error = got_repo_match_object_id(&commit_id, NULL,
8273 commit_id_str, GOT_OBJ_TYPE_COMMIT, &refs, repo);
8274 if (error)
8275 goto done;
8278 error = got_patch(patchfd, worktree, repo, nop, strip, reverse,
8279 commit_id, &patch_progress, NULL, check_cancelled, NULL);
8281 done:
8282 got_ref_list_free(&refs);
8283 free(commit_id);
8284 if (repo) {
8285 close_error = got_repo_close(repo);
8286 if (error == NULL)
8287 error = close_error;
8289 if (worktree != NULL) {
8290 close_error = got_worktree_close(worktree);
8291 if (error == NULL)
8292 error = close_error;
8294 if (pack_fds) {
8295 const struct got_error *pack_err =
8296 got_repo_pack_fds_close(pack_fds);
8297 if (error == NULL)
8298 error = pack_err;
8300 free(cwd);
8301 return error;
8304 __dead static void
8305 usage_revert(void)
8307 fprintf(stderr, "usage: %s revert [-pR] [-F response-script] path ...\n",
8308 getprogname());
8309 exit(1);
8312 static const struct got_error *
8313 revert_progress(void *arg, unsigned char status, const char *path)
8315 if (status == GOT_STATUS_UNVERSIONED)
8316 return NULL;
8318 while (path[0] == '/')
8319 path++;
8320 printf("%c %s\n", status, path);
8321 return NULL;
8324 struct choose_patch_arg {
8325 FILE *patch_script_file;
8326 const char *action;
8329 static const struct got_error *
8330 show_change(unsigned char status, const char *path, FILE *patch_file, int n,
8331 int nchanges, const char *action)
8333 const struct got_error *err;
8334 char *line = NULL;
8335 size_t linesize = 0;
8336 ssize_t linelen;
8338 switch (status) {
8339 case GOT_STATUS_ADD:
8340 printf("A %s\n%s this addition? [y/n] ", path, action);
8341 break;
8342 case GOT_STATUS_DELETE:
8343 printf("D %s\n%s this deletion? [y/n] ", path, action);
8344 break;
8345 case GOT_STATUS_MODIFY:
8346 if (fseek(patch_file, 0L, SEEK_SET) == -1)
8347 return got_error_from_errno("fseek");
8348 printf(GOT_COMMIT_SEP_STR);
8349 while ((linelen = getline(&line, &linesize, patch_file)) != -1)
8350 printf("%s", line);
8351 if (linelen == -1 && ferror(patch_file)) {
8352 err = got_error_from_errno("getline");
8353 free(line);
8354 return err;
8356 free(line);
8357 printf(GOT_COMMIT_SEP_STR);
8358 printf("M %s (change %d of %d)\n%s this change? [y/n/q] ",
8359 path, n, nchanges, action);
8360 break;
8361 default:
8362 return got_error_path(path, GOT_ERR_FILE_STATUS);
8365 fflush(stdout);
8366 return NULL;
8369 static const struct got_error *
8370 choose_patch(int *choice, void *arg, unsigned char status, const char *path,
8371 FILE *patch_file, int n, int nchanges)
8373 const struct got_error *err = NULL;
8374 char *line = NULL;
8375 size_t linesize = 0;
8376 ssize_t linelen;
8377 int resp = ' ';
8378 struct choose_patch_arg *a = arg;
8380 *choice = GOT_PATCH_CHOICE_NONE;
8382 if (a->patch_script_file) {
8383 char *nl;
8384 err = show_change(status, path, patch_file, n, nchanges,
8385 a->action);
8386 if (err)
8387 return err;
8388 linelen = getline(&line, &linesize, a->patch_script_file);
8389 if (linelen == -1) {
8390 if (ferror(a->patch_script_file))
8391 return got_error_from_errno("getline");
8392 return NULL;
8394 nl = strchr(line, '\n');
8395 if (nl)
8396 *nl = '\0';
8397 if (strcmp(line, "y") == 0) {
8398 *choice = GOT_PATCH_CHOICE_YES;
8399 printf("y\n");
8400 } else if (strcmp(line, "n") == 0) {
8401 *choice = GOT_PATCH_CHOICE_NO;
8402 printf("n\n");
8403 } else if (strcmp(line, "q") == 0 &&
8404 status == GOT_STATUS_MODIFY) {
8405 *choice = GOT_PATCH_CHOICE_QUIT;
8406 printf("q\n");
8407 } else
8408 printf("invalid response '%s'\n", line);
8409 free(line);
8410 return NULL;
8413 while (resp != 'y' && resp != 'n' && resp != 'q') {
8414 err = show_change(status, path, patch_file, n, nchanges,
8415 a->action);
8416 if (err)
8417 return err;
8418 resp = getchar();
8419 if (resp == '\n')
8420 resp = getchar();
8421 if (status == GOT_STATUS_MODIFY) {
8422 if (resp != 'y' && resp != 'n' && resp != 'q') {
8423 printf("invalid response '%c'\n", resp);
8424 resp = ' ';
8426 } else if (resp != 'y' && resp != 'n') {
8427 printf("invalid response '%c'\n", resp);
8428 resp = ' ';
8432 if (resp == 'y')
8433 *choice = GOT_PATCH_CHOICE_YES;
8434 else if (resp == 'n')
8435 *choice = GOT_PATCH_CHOICE_NO;
8436 else if (resp == 'q' && status == GOT_STATUS_MODIFY)
8437 *choice = GOT_PATCH_CHOICE_QUIT;
8439 return NULL;
8442 struct wt_commitable_path_arg {
8443 struct got_pathlist_head *commit_paths;
8444 int *has_changes;
8448 * Shortcut work tree status callback to determine if the set of paths scanned
8449 * has at least one versioned path that is being modified and, if not NULL, is
8450 * in the arg->commit_paths list. Set arg and return GOT_ERR_FILE_MODIFIED as
8451 * soon as a path is passed with a status that satisfies this criteria.
8453 static const struct got_error *
8454 worktree_has_commitable_path(void *arg, unsigned char status,
8455 unsigned char staged_status, const char *path,
8456 struct got_object_id *blob_id, struct got_object_id *staged_blob_id,
8457 struct got_object_id *commit_id, int dirfd, const char *de_name)
8459 struct wt_commitable_path_arg *a = arg;
8461 if (status == staged_status && (status == GOT_STATUS_DELETE))
8462 status = GOT_STATUS_NO_CHANGE;
8464 if (!(status == GOT_STATUS_NO_CHANGE ||
8465 status == GOT_STATUS_UNVERSIONED) ||
8466 staged_status != GOT_STATUS_NO_CHANGE) {
8467 if (a->commit_paths != NULL) {
8468 struct got_pathlist_entry *pe;
8470 TAILQ_FOREACH(pe, a->commit_paths, entry) {
8471 if (strncmp(path, pe->path,
8472 pe->path_len) == 0) {
8473 *a->has_changes = 1;
8474 break;
8477 } else
8478 *a->has_changes = 1;
8480 if (*a->has_changes)
8481 return got_error(GOT_ERR_FILE_MODIFIED);
8484 return NULL;
8488 * Check that the changeset of the commit identified by id is
8489 * comprised of at least one modified path that is being committed.
8491 static const struct got_error *
8492 commit_path_changed_in_worktree(struct wt_commitable_path_arg *wcpa,
8493 struct got_object_id *id, struct got_worktree *worktree,
8494 struct got_repository *repo)
8496 const struct got_error *err;
8497 struct got_pathlist_head paths;
8498 struct got_commit_object *commit = NULL, *pcommit = NULL;
8499 struct got_tree_object *tree = NULL, *ptree = NULL;
8500 struct got_object_qid *pid;
8502 TAILQ_INIT(&paths);
8504 err = got_object_open_as_commit(&commit, repo, id);
8505 if (err)
8506 goto done;
8508 err = got_object_open_as_tree(&tree, repo,
8509 got_object_commit_get_tree_id(commit));
8510 if (err)
8511 goto done;
8513 pid = STAILQ_FIRST(got_object_commit_get_parent_ids(commit));
8514 if (pid != NULL) {
8515 err = got_object_open_as_commit(&pcommit, repo, &pid->id);
8516 if (err)
8517 goto done;
8519 err = got_object_open_as_tree(&ptree, repo,
8520 got_object_commit_get_tree_id(pcommit));
8521 if (err)
8522 goto done;
8525 err = got_diff_tree(ptree, tree, NULL, NULL, -1, -1, "", "", repo,
8526 got_diff_tree_collect_changed_paths, &paths, 0);
8527 if (err)
8528 goto done;
8530 err = got_worktree_status(worktree, &paths, repo, 0,
8531 worktree_has_commitable_path, wcpa, check_cancelled, NULL);
8532 if (err && err->code == GOT_ERR_FILE_MODIFIED) {
8534 * At least one changed path in the referenced commit is
8535 * modified in the work tree, that's all we need to know!
8537 err = NULL;
8540 done:
8541 got_pathlist_free(&paths, GOT_PATHLIST_FREE_ALL);
8542 if (commit)
8543 got_object_commit_close(commit);
8544 if (pcommit)
8545 got_object_commit_close(pcommit);
8546 if (tree)
8547 got_object_tree_close(tree);
8548 if (ptree)
8549 got_object_tree_close(ptree);
8550 return err;
8554 * Remove any "logmsg" reference comprised entirely of paths that have
8555 * been reverted in this work tree. If any path in the logmsg ref changeset
8556 * remains in a changed state in the worktree, do not remove the reference.
8558 static const struct got_error *
8559 rm_logmsg_ref(struct got_worktree *worktree, struct got_repository *repo)
8561 const struct got_error *err;
8562 struct got_reflist_head refs;
8563 struct got_reflist_entry *re;
8564 struct got_commit_object *commit = NULL;
8565 struct got_object_id *commit_id = NULL;
8566 struct wt_commitable_path_arg wcpa;
8567 char *uuidstr = NULL;
8569 TAILQ_INIT(&refs);
8571 err = got_worktree_get_uuid(&uuidstr, worktree);
8572 if (err)
8573 goto done;
8575 err = got_ref_list(&refs, repo, "refs/got/worktree",
8576 got_ref_cmp_by_name, repo);
8577 if (err)
8578 goto done;
8580 TAILQ_FOREACH(re, &refs, entry) {
8581 const char *refname;
8582 int has_changes = 0;
8584 refname = got_ref_get_name(re->ref);
8586 if (!strncmp(refname, GOT_WORKTREE_CHERRYPICK_REF_PREFIX,
8587 GOT_WORKTREE_CHERRYPICK_REF_PREFIX_LEN))
8588 refname += GOT_WORKTREE_CHERRYPICK_REF_PREFIX_LEN + 1;
8589 else if (!strncmp(refname, GOT_WORKTREE_BACKOUT_REF_PREFIX,
8590 GOT_WORKTREE_BACKOUT_REF_PREFIX_LEN))
8591 refname += GOT_WORKTREE_BACKOUT_REF_PREFIX_LEN + 1;
8592 else
8593 continue;
8595 if (strncmp(refname, uuidstr, GOT_WORKTREE_UUID_STRLEN) == 0)
8596 refname += GOT_WORKTREE_UUID_STRLEN + 1; /* skip '-' */
8597 else
8598 continue;
8600 err = got_repo_match_object_id(&commit_id, NULL, refname,
8601 GOT_OBJ_TYPE_COMMIT, NULL, repo);
8602 if (err)
8603 goto done;
8605 err = got_object_open_as_commit(&commit, repo, commit_id);
8606 if (err)
8607 goto done;
8609 wcpa.commit_paths = NULL;
8610 wcpa.has_changes = &has_changes;
8612 err = commit_path_changed_in_worktree(&wcpa, commit_id,
8613 worktree, repo);
8614 if (err)
8615 goto done;
8617 if (!has_changes) {
8618 err = got_ref_delete(re->ref, repo);
8619 if (err)
8620 goto done;
8623 got_object_commit_close(commit);
8624 commit = NULL;
8625 free(commit_id);
8626 commit_id = NULL;
8629 done:
8630 free(uuidstr);
8631 free(commit_id);
8632 got_ref_list_free(&refs);
8633 if (commit)
8634 got_object_commit_close(commit);
8635 return err;
8638 static const struct got_error *
8639 cmd_revert(int argc, char *argv[])
8641 const struct got_error *error = NULL;
8642 struct got_worktree *worktree = NULL;
8643 struct got_repository *repo = NULL;
8644 char *cwd = NULL, *path = NULL;
8645 struct got_pathlist_head paths;
8646 struct got_pathlist_entry *pe;
8647 int ch, can_recurse = 0, pflag = 0;
8648 FILE *patch_script_file = NULL;
8649 const char *patch_script_path = NULL;
8650 struct choose_patch_arg cpa;
8651 int *pack_fds = NULL;
8653 TAILQ_INIT(&paths);
8655 #ifndef PROFILE
8656 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
8657 "unveil", NULL) == -1)
8658 err(1, "pledge");
8659 #endif
8661 while ((ch = getopt(argc, argv, "F:pR")) != -1) {
8662 switch (ch) {
8663 case 'F':
8664 patch_script_path = optarg;
8665 break;
8666 case 'p':
8667 pflag = 1;
8668 break;
8669 case 'R':
8670 can_recurse = 1;
8671 break;
8672 default:
8673 usage_revert();
8674 /* NOTREACHED */
8678 argc -= optind;
8679 argv += optind;
8681 if (argc < 1)
8682 usage_revert();
8683 if (patch_script_path && !pflag)
8684 errx(1, "-F option can only be used together with -p option");
8686 cwd = getcwd(NULL, 0);
8687 if (cwd == NULL) {
8688 error = got_error_from_errno("getcwd");
8689 goto done;
8692 error = got_repo_pack_fds_open(&pack_fds);
8693 if (error != NULL)
8694 goto done;
8696 error = got_worktree_open(&worktree, cwd);
8697 if (error) {
8698 if (error->code == GOT_ERR_NOT_WORKTREE)
8699 error = wrap_not_worktree_error(error, "revert", cwd);
8700 goto done;
8703 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
8704 NULL, pack_fds);
8705 if (error != NULL)
8706 goto done;
8708 if (patch_script_path) {
8709 patch_script_file = fopen(patch_script_path, "re");
8710 if (patch_script_file == NULL) {
8711 error = got_error_from_errno2("fopen",
8712 patch_script_path);
8713 goto done;
8718 * XXX "c" perm needed on repo dir to delete merge references.
8720 error = apply_unveil(got_repo_get_path(repo), 0,
8721 got_worktree_get_root_path(worktree));
8722 if (error)
8723 goto done;
8725 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
8726 if (error)
8727 goto done;
8729 if (!can_recurse) {
8730 char *ondisk_path;
8731 struct stat sb;
8732 TAILQ_FOREACH(pe, &paths, entry) {
8733 if (asprintf(&ondisk_path, "%s/%s",
8734 got_worktree_get_root_path(worktree),
8735 pe->path) == -1) {
8736 error = got_error_from_errno("asprintf");
8737 goto done;
8739 if (lstat(ondisk_path, &sb) == -1) {
8740 if (errno == ENOENT) {
8741 free(ondisk_path);
8742 continue;
8744 error = got_error_from_errno2("lstat",
8745 ondisk_path);
8746 free(ondisk_path);
8747 goto done;
8749 free(ondisk_path);
8750 if (S_ISDIR(sb.st_mode)) {
8751 error = got_error_msg(GOT_ERR_BAD_PATH,
8752 "reverting directories requires -R option");
8753 goto done;
8758 cpa.patch_script_file = patch_script_file;
8759 cpa.action = "revert";
8760 error = got_worktree_revert(worktree, &paths, revert_progress, NULL,
8761 pflag ? choose_patch : NULL, &cpa, repo);
8763 error = rm_logmsg_ref(worktree, repo);
8764 done:
8765 if (patch_script_file && fclose(patch_script_file) == EOF &&
8766 error == NULL)
8767 error = got_error_from_errno2("fclose", patch_script_path);
8768 if (repo) {
8769 const struct got_error *close_err = got_repo_close(repo);
8770 if (error == NULL)
8771 error = close_err;
8773 if (worktree)
8774 got_worktree_close(worktree);
8775 if (pack_fds) {
8776 const struct got_error *pack_err =
8777 got_repo_pack_fds_close(pack_fds);
8778 if (error == NULL)
8779 error = pack_err;
8781 got_pathlist_free(&paths, GOT_PATHLIST_FREE_PATH);
8782 free(path);
8783 free(cwd);
8784 return error;
8787 __dead static void
8788 usage_commit(void)
8790 fprintf(stderr, "usage: %s commit [-CNnS] [-A author] [-F path] "
8791 "[-m message] [path ...]\n", getprogname());
8792 exit(1);
8795 struct collect_commit_logmsg_arg {
8796 const char *cmdline_log;
8797 const char *prepared_log;
8798 const char *merged_log;
8799 int non_interactive;
8800 const char *editor;
8801 const char *worktree_path;
8802 const char *branch_name;
8803 const char *repo_path;
8804 char *logmsg_path;
8808 static const struct got_error *
8809 read_prepared_logmsg(char **logmsg, const char *path)
8811 const struct got_error *err = NULL;
8812 FILE *f = NULL;
8813 struct stat sb;
8814 size_t r;
8816 *logmsg = NULL;
8817 memset(&sb, 0, sizeof(sb));
8819 f = fopen(path, "re");
8820 if (f == NULL)
8821 return got_error_from_errno2("fopen", path);
8823 if (fstat(fileno(f), &sb) == -1) {
8824 err = got_error_from_errno2("fstat", path);
8825 goto done;
8827 if (sb.st_size == 0) {
8828 err = got_error(GOT_ERR_COMMIT_MSG_EMPTY);
8829 goto done;
8832 *logmsg = malloc(sb.st_size + 1);
8833 if (*logmsg == NULL) {
8834 err = got_error_from_errno("malloc");
8835 goto done;
8838 r = fread(*logmsg, 1, sb.st_size, f);
8839 if (r != sb.st_size) {
8840 if (ferror(f))
8841 err = got_error_from_errno2("fread", path);
8842 else
8843 err = got_error(GOT_ERR_IO);
8844 goto done;
8846 (*logmsg)[sb.st_size] = '\0';
8847 done:
8848 if (fclose(f) == EOF && err == NULL)
8849 err = got_error_from_errno2("fclose", path);
8850 if (err) {
8851 free(*logmsg);
8852 *logmsg = NULL;
8854 return err;
8857 static const struct got_error *
8858 collect_commit_logmsg(struct got_pathlist_head *commitable_paths,
8859 const char *diff_path, char **logmsg, void *arg)
8861 char *initial_content = NULL;
8862 struct got_pathlist_entry *pe;
8863 const struct got_error *err = NULL;
8864 char *template = NULL;
8865 char *prepared_msg = NULL, *merged_msg = NULL;
8866 struct collect_commit_logmsg_arg *a = arg;
8867 int initial_content_len;
8868 int fd = -1;
8869 size_t len;
8871 /* if a message was specified on the command line, just use it */
8872 if (a->cmdline_log != NULL && strlen(a->cmdline_log) != 0) {
8873 len = strlen(a->cmdline_log) + 1;
8874 *logmsg = malloc(len + 1);
8875 if (*logmsg == NULL)
8876 return got_error_from_errno("malloc");
8877 strlcpy(*logmsg, a->cmdline_log, len);
8878 return NULL;
8879 } else if (a->prepared_log != NULL && a->non_interactive)
8880 return read_prepared_logmsg(logmsg, a->prepared_log);
8882 if (asprintf(&template, "%s/logmsg", a->worktree_path) == -1)
8883 return got_error_from_errno("asprintf");
8885 err = got_opentemp_named_fd(&a->logmsg_path, &fd, template, "");
8886 if (err)
8887 goto done;
8889 if (a->prepared_log) {
8890 err = read_prepared_logmsg(&prepared_msg, a->prepared_log);
8891 if (err)
8892 goto done;
8893 } else if (a->merged_log) {
8894 err = read_prepared_logmsg(&merged_msg, a->merged_log);
8895 if (err)
8896 goto done;
8899 initial_content_len = asprintf(&initial_content,
8900 "%s%s\n# changes to be committed on branch %s:\n",
8901 prepared_msg ? prepared_msg : "",
8902 merged_msg ? merged_msg : "", a->branch_name);
8903 if (initial_content_len == -1) {
8904 err = got_error_from_errno("asprintf");
8905 goto done;
8908 if (write(fd, initial_content, initial_content_len) == -1) {
8909 err = got_error_from_errno2("write", a->logmsg_path);
8910 goto done;
8913 TAILQ_FOREACH(pe, commitable_paths, entry) {
8914 struct got_commitable *ct = pe->data;
8915 dprintf(fd, "# %c %s\n",
8916 got_commitable_get_status(ct),
8917 got_commitable_get_path(ct));
8920 if (diff_path) {
8921 dprintf(fd, "# detailed changes can be viewed in %s\n",
8922 diff_path);
8925 if (close(fd) == -1) {
8926 err = got_error_from_errno2("close", a->logmsg_path);
8927 goto done;
8929 fd = -1;
8931 err = edit_logmsg(logmsg, a->editor, a->logmsg_path, initial_content,
8932 initial_content_len, a->prepared_log ? 0 : 1);
8933 done:
8934 free(initial_content);
8935 free(template);
8936 free(prepared_msg);
8937 free(merged_msg);
8939 if (fd != -1 && close(fd) == -1 && err == NULL)
8940 err = got_error_from_errno2("close", a->logmsg_path);
8942 /* Editor is done; we can now apply unveil(2) */
8943 if (err == NULL)
8944 err = apply_unveil(a->repo_path, 0, a->worktree_path);
8945 if (err) {
8946 free(*logmsg);
8947 *logmsg = NULL;
8949 return err;
8952 static const struct got_error *
8953 cat_logmsg(FILE *f, struct got_commit_object *commit, const char *idstr,
8954 const char *type, int has_content)
8956 const struct got_error *err = NULL;
8957 char *logmsg = NULL;
8959 err = got_object_commit_get_logmsg(&logmsg, commit);
8960 if (err)
8961 return err;
8963 if (fprintf(f, "%s# log message of %s commit %s:%s",
8964 has_content ? "\n" : "", type, idstr, logmsg) < 0)
8965 err = got_ferror(f, GOT_ERR_IO);
8967 free(logmsg);
8968 return err;
8972 * Lookup "logmsg" references of backed-out and cherrypicked commits
8973 * belonging to the current work tree. If found, and the worktree has
8974 * at least one modified file that was changed in the referenced commit,
8975 * add its log message to a new temporary file at *logmsg_path.
8976 * Add all refs found to matched_refs to be scheduled for removal on
8977 * successful commit.
8979 static const struct got_error *
8980 lookup_logmsg_ref(char **logmsg_path, struct got_pathlist_head *paths,
8981 struct got_reflist_head *matched_refs, struct got_worktree *worktree,
8982 struct got_repository *repo)
8984 const struct got_error *err;
8985 struct got_commit_object *commit = NULL;
8986 struct got_object_id *id = NULL;
8987 struct got_reflist_head refs;
8988 struct got_reflist_entry *re, *re_match;
8989 FILE *f = NULL;
8990 char *uuidstr = NULL;
8991 int added_logmsg = 0;
8993 TAILQ_INIT(&refs);
8995 *logmsg_path = NULL;
8997 err = got_worktree_get_uuid(&uuidstr, worktree);
8998 if (err)
8999 goto done;
9001 err = got_ref_list(&refs, repo, "refs/got/worktree",
9002 got_ref_cmp_by_name, repo);
9003 if (err)
9004 goto done;
9006 TAILQ_FOREACH(re, &refs, entry) {
9007 const char *refname, *type;
9008 struct wt_commitable_path_arg wcpa;
9009 int add_logmsg = 0;
9011 refname = got_ref_get_name(re->ref);
9013 if (strncmp(refname, GOT_WORKTREE_CHERRYPICK_REF_PREFIX,
9014 GOT_WORKTREE_CHERRYPICK_REF_PREFIX_LEN) == 0) {
9015 refname += GOT_WORKTREE_CHERRYPICK_REF_PREFIX_LEN + 1;
9016 type = "cherrypicked";
9017 } else if (strncmp(refname, GOT_WORKTREE_BACKOUT_REF_PREFIX,
9018 GOT_WORKTREE_BACKOUT_REF_PREFIX_LEN) == 0) {
9019 refname += GOT_WORKTREE_BACKOUT_REF_PREFIX_LEN + 1;
9020 type = "backed-out";
9021 } else
9022 continue;
9024 if (strncmp(refname, uuidstr, GOT_WORKTREE_UUID_STRLEN) == 0)
9025 refname += GOT_WORKTREE_UUID_STRLEN + 1; /* skip '-' */
9026 else
9027 continue;
9029 err = got_repo_match_object_id(&id, NULL, refname,
9030 GOT_OBJ_TYPE_COMMIT, NULL, repo);
9031 if (err)
9032 goto done;
9034 err = got_object_open_as_commit(&commit, repo, id);
9035 if (err)
9036 goto done;
9038 wcpa.commit_paths = paths;
9039 wcpa.has_changes = &add_logmsg;
9041 err = commit_path_changed_in_worktree(&wcpa, id,
9042 worktree, repo);
9043 if (err)
9044 goto done;
9046 if (add_logmsg) {
9047 if (f == NULL) {
9048 err = got_opentemp_named(logmsg_path, &f,
9049 "got-commit-logmsg", "");
9050 if (err)
9051 goto done;
9053 err = cat_logmsg(f, commit, refname, type,
9054 added_logmsg);
9055 if (err)
9056 goto done;
9057 if (!added_logmsg)
9058 ++added_logmsg;
9060 err = got_reflist_entry_dup(&re_match, re);
9061 if (err)
9062 goto done;
9063 TAILQ_INSERT_HEAD(matched_refs, re_match, entry);
9066 got_object_commit_close(commit);
9067 commit = NULL;
9068 free(id);
9069 id = NULL;
9072 done:
9073 free(id);
9074 free(uuidstr);
9075 got_ref_list_free(&refs);
9076 if (commit)
9077 got_object_commit_close(commit);
9078 if (f && fclose(f) == EOF && err == NULL)
9079 err = got_error_from_errno("fclose");
9080 if (!added_logmsg) {
9081 if (*logmsg_path && unlink(*logmsg_path) != 0 && err == NULL)
9082 err = got_error_from_errno2("unlink", *logmsg_path);
9083 *logmsg_path = NULL;
9085 return err;
9088 static const struct got_error *
9089 cmd_commit(int argc, char *argv[])
9091 const struct got_error *error = NULL;
9092 struct got_worktree *worktree = NULL;
9093 struct got_repository *repo = NULL;
9094 char *cwd = NULL, *id_str = NULL;
9095 struct got_object_id *id = NULL;
9096 const char *logmsg = NULL;
9097 char *prepared_logmsg = NULL, *merged_logmsg = NULL;
9098 struct collect_commit_logmsg_arg cl_arg;
9099 const char *author = NULL;
9100 char *gitconfig_path = NULL, *editor = NULL, *committer = NULL;
9101 int ch, rebase_in_progress, histedit_in_progress, preserve_logmsg = 0;
9102 int allow_bad_symlinks = 0, non_interactive = 0, merge_in_progress = 0;
9103 int show_diff = 1, commit_conflicts = 0;
9104 struct got_pathlist_head paths;
9105 struct got_reflist_head refs;
9106 struct got_reflist_entry *re;
9107 int *pack_fds = NULL;
9109 TAILQ_INIT(&refs);
9110 TAILQ_INIT(&paths);
9111 cl_arg.logmsg_path = NULL;
9113 #ifndef PROFILE
9114 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
9115 "unveil", NULL) == -1)
9116 err(1, "pledge");
9117 #endif
9119 while ((ch = getopt(argc, argv, "A:CF:m:NnS")) != -1) {
9120 switch (ch) {
9121 case 'A':
9122 author = optarg;
9123 error = valid_author(author);
9124 if (error)
9125 return error;
9126 break;
9127 case 'C':
9128 commit_conflicts = 1;
9129 break;
9130 case 'F':
9131 if (logmsg != NULL)
9132 option_conflict('F', 'm');
9133 prepared_logmsg = realpath(optarg, NULL);
9134 if (prepared_logmsg == NULL)
9135 return got_error_from_errno2("realpath",
9136 optarg);
9137 break;
9138 case 'm':
9139 if (prepared_logmsg)
9140 option_conflict('m', 'F');
9141 logmsg = optarg;
9142 break;
9143 case 'N':
9144 non_interactive = 1;
9145 break;
9146 case 'n':
9147 show_diff = 0;
9148 break;
9149 case 'S':
9150 allow_bad_symlinks = 1;
9151 break;
9152 default:
9153 usage_commit();
9154 /* NOTREACHED */
9158 argc -= optind;
9159 argv += optind;
9161 cwd = getcwd(NULL, 0);
9162 if (cwd == NULL) {
9163 error = got_error_from_errno("getcwd");
9164 goto done;
9167 error = got_repo_pack_fds_open(&pack_fds);
9168 if (error != NULL)
9169 goto done;
9171 error = got_worktree_open(&worktree, cwd);
9172 if (error) {
9173 if (error->code == GOT_ERR_NOT_WORKTREE)
9174 error = wrap_not_worktree_error(error, "commit", cwd);
9175 goto done;
9178 error = got_worktree_rebase_in_progress(&rebase_in_progress, worktree);
9179 if (error)
9180 goto done;
9181 if (rebase_in_progress) {
9182 error = got_error(GOT_ERR_REBASING);
9183 goto done;
9186 error = got_worktree_histedit_in_progress(&histedit_in_progress,
9187 worktree);
9188 if (error)
9189 goto done;
9191 error = get_gitconfig_path(&gitconfig_path);
9192 if (error)
9193 goto done;
9194 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
9195 gitconfig_path, pack_fds);
9196 if (error != NULL)
9197 goto done;
9199 error = got_worktree_merge_in_progress(&merge_in_progress, worktree, repo);
9200 if (error)
9201 goto done;
9202 if (merge_in_progress) {
9203 error = got_error(GOT_ERR_MERGE_BUSY);
9204 goto done;
9207 error = get_author(&committer, repo, worktree);
9208 if (error)
9209 goto done;
9211 if (author == NULL)
9212 author = committer;
9215 * unveil(2) traverses exec(2); if an editor is used we have
9216 * to apply unveil after the log message has been written.
9218 if (logmsg == NULL || strlen(logmsg) == 0)
9219 error = get_editor(&editor);
9220 else
9221 error = apply_unveil(got_repo_get_path(repo), 0,
9222 got_worktree_get_root_path(worktree));
9223 if (error)
9224 goto done;
9226 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
9227 if (error)
9228 goto done;
9230 if (prepared_logmsg == NULL) {
9231 error = lookup_logmsg_ref(&merged_logmsg,
9232 argc > 0 ? &paths : NULL, &refs, worktree, repo);
9233 if (error)
9234 goto done;
9237 cl_arg.editor = editor;
9238 cl_arg.cmdline_log = logmsg;
9239 cl_arg.prepared_log = prepared_logmsg;
9240 cl_arg.merged_log = merged_logmsg;
9241 cl_arg.non_interactive = non_interactive;
9242 cl_arg.worktree_path = got_worktree_get_root_path(worktree);
9243 cl_arg.branch_name = got_worktree_get_head_ref_name(worktree);
9244 if (!histedit_in_progress) {
9245 if (strncmp(cl_arg.branch_name, "refs/heads/", 11) != 0) {
9246 error = got_error(GOT_ERR_COMMIT_BRANCH);
9247 goto done;
9249 cl_arg.branch_name += 11;
9251 cl_arg.repo_path = got_repo_get_path(repo);
9252 error = got_worktree_commit(&id, worktree, &paths, author, committer,
9253 allow_bad_symlinks, show_diff, commit_conflicts,
9254 collect_commit_logmsg, &cl_arg, print_status, NULL, repo);
9255 if (error) {
9256 if (error->code != GOT_ERR_COMMIT_MSG_EMPTY &&
9257 cl_arg.logmsg_path != NULL)
9258 preserve_logmsg = 1;
9259 goto done;
9262 error = got_object_id_str(&id_str, id);
9263 if (error)
9264 goto done;
9265 printf("Created commit %s\n", id_str);
9267 TAILQ_FOREACH(re, &refs, entry) {
9268 error = got_ref_delete(re->ref, repo);
9269 if (error)
9270 goto done;
9273 done:
9274 if (preserve_logmsg) {
9275 fprintf(stderr, "%s: log message preserved in %s\n",
9276 getprogname(), cl_arg.logmsg_path);
9277 } else if (cl_arg.logmsg_path && unlink(cl_arg.logmsg_path) == -1 &&
9278 error == NULL)
9279 error = got_error_from_errno2("unlink", cl_arg.logmsg_path);
9280 free(cl_arg.logmsg_path);
9281 if (merged_logmsg && unlink(merged_logmsg) == -1 && error == NULL)
9282 error = got_error_from_errno2("unlink", merged_logmsg);
9283 free(merged_logmsg);
9284 if (repo) {
9285 const struct got_error *close_err = got_repo_close(repo);
9286 if (error == NULL)
9287 error = close_err;
9289 if (worktree)
9290 got_worktree_close(worktree);
9291 if (pack_fds) {
9292 const struct got_error *pack_err =
9293 got_repo_pack_fds_close(pack_fds);
9294 if (error == NULL)
9295 error = pack_err;
9297 got_ref_list_free(&refs);
9298 got_pathlist_free(&paths, GOT_PATHLIST_FREE_PATH);
9299 free(cwd);
9300 free(id_str);
9301 free(gitconfig_path);
9302 free(editor);
9303 free(committer);
9304 free(prepared_logmsg);
9305 return error;
9308 __dead static void
9309 usage_send(void)
9311 fprintf(stderr, "usage: %s send [-afqTv] [-b branch] [-d branch] "
9312 "[-r repository-path] [-t tag] [remote-repository]\n",
9313 getprogname());
9314 exit(1);
9317 static void
9318 print_load_info(int print_colored, int print_found, int print_trees,
9319 int ncolored, int nfound, int ntrees)
9321 if (print_colored) {
9322 printf("%d commit%s colored", ncolored,
9323 ncolored == 1 ? "" : "s");
9325 if (print_found) {
9326 printf("%s%d object%s found",
9327 ncolored > 0 ? "; " : "",
9328 nfound, nfound == 1 ? "" : "s");
9330 if (print_trees) {
9331 printf("; %d tree%s scanned", ntrees,
9332 ntrees == 1 ? "" : "s");
9336 struct got_send_progress_arg {
9337 char last_scaled_packsize[FMT_SCALED_STRSIZE];
9338 int verbosity;
9339 int last_ncolored;
9340 int last_nfound;
9341 int last_ntrees;
9342 int loading_done;
9343 int last_ncommits;
9344 int last_nobj_total;
9345 int last_p_deltify;
9346 int last_p_written;
9347 int last_p_sent;
9348 int printed_something;
9349 int sent_something;
9350 struct got_pathlist_head *delete_branches;
9353 static const struct got_error *
9354 send_progress(void *arg, int ncolored, int nfound, int ntrees,
9355 off_t packfile_size, int ncommits, int nobj_total, int nobj_deltify,
9356 int nobj_written, off_t bytes_sent, const char *refname,
9357 const char *errmsg, int success)
9359 struct got_send_progress_arg *a = arg;
9360 char scaled_packsize[FMT_SCALED_STRSIZE];
9361 char scaled_sent[FMT_SCALED_STRSIZE];
9362 int p_deltify = 0, p_written = 0, p_sent = 0;
9363 int print_colored = 0, print_found = 0, print_trees = 0;
9364 int print_searching = 0, print_total = 0;
9365 int print_deltify = 0, print_written = 0, print_sent = 0;
9367 if (a->verbosity < 0)
9368 return NULL;
9370 if (refname) {
9371 const char *status = success ? "accepted" : "rejected";
9373 if (success) {
9374 struct got_pathlist_entry *pe;
9375 TAILQ_FOREACH(pe, a->delete_branches, entry) {
9376 const char *branchname = pe->path;
9377 if (got_path_cmp(branchname, refname,
9378 strlen(branchname), strlen(refname)) == 0) {
9379 status = "deleted";
9380 a->sent_something = 1;
9381 break;
9386 if (a->printed_something)
9387 putchar('\n');
9388 printf("Server has %s %s", status, refname);
9389 if (errmsg)
9390 printf(": %s", errmsg);
9391 a->printed_something = 1;
9392 return NULL;
9395 if (a->last_ncolored != ncolored) {
9396 print_colored = 1;
9397 a->last_ncolored = ncolored;
9400 if (a->last_nfound != nfound) {
9401 print_colored = 1;
9402 print_found = 1;
9403 a->last_nfound = nfound;
9406 if (a->last_ntrees != ntrees) {
9407 print_colored = 1;
9408 print_found = 1;
9409 print_trees = 1;
9410 a->last_ntrees = ntrees;
9413 if ((print_colored || print_found || print_trees) &&
9414 !a->loading_done) {
9415 printf("\r");
9416 print_load_info(print_colored, print_found, print_trees,
9417 ncolored, nfound, ntrees);
9418 a->printed_something = 1;
9419 fflush(stdout);
9420 return NULL;
9421 } else if (!a->loading_done) {
9422 printf("\r");
9423 print_load_info(1, 1, 1, ncolored, nfound, ntrees);
9424 printf("\n");
9425 a->loading_done = 1;
9428 if (fmt_scaled(packfile_size, scaled_packsize) == -1)
9429 return got_error_from_errno("fmt_scaled");
9430 if (fmt_scaled(bytes_sent, scaled_sent) == -1)
9431 return got_error_from_errno("fmt_scaled");
9433 if (a->last_ncommits != ncommits) {
9434 print_searching = 1;
9435 a->last_ncommits = ncommits;
9438 if (a->last_nobj_total != nobj_total) {
9439 print_searching = 1;
9440 print_total = 1;
9441 a->last_nobj_total = nobj_total;
9444 if (packfile_size > 0 && (a->last_scaled_packsize[0] == '\0' ||
9445 strcmp(scaled_packsize, a->last_scaled_packsize)) != 0) {
9446 if (strlcpy(a->last_scaled_packsize, scaled_packsize,
9447 FMT_SCALED_STRSIZE) >= FMT_SCALED_STRSIZE)
9448 return got_error(GOT_ERR_NO_SPACE);
9451 if (nobj_deltify > 0 || nobj_written > 0) {
9452 if (nobj_deltify > 0) {
9453 p_deltify = (nobj_deltify * 100) / nobj_total;
9454 if (p_deltify != a->last_p_deltify) {
9455 a->last_p_deltify = p_deltify;
9456 print_searching = 1;
9457 print_total = 1;
9458 print_deltify = 1;
9461 if (nobj_written > 0) {
9462 p_written = (nobj_written * 100) / nobj_total;
9463 if (p_written != a->last_p_written) {
9464 a->last_p_written = p_written;
9465 print_searching = 1;
9466 print_total = 1;
9467 print_deltify = 1;
9468 print_written = 1;
9473 if (bytes_sent > 0) {
9474 p_sent = (bytes_sent * 100) / packfile_size;
9475 if (p_sent != a->last_p_sent) {
9476 a->last_p_sent = p_sent;
9477 print_searching = 1;
9478 print_total = 1;
9479 print_deltify = 1;
9480 print_written = 1;
9481 print_sent = 1;
9483 a->sent_something = 1;
9486 if (print_searching || print_total || print_deltify || print_written ||
9487 print_sent)
9488 printf("\r");
9489 if (print_searching)
9490 printf("packing %d reference%s", ncommits,
9491 ncommits == 1 ? "" : "s");
9492 if (print_total)
9493 printf("; %d object%s", nobj_total,
9494 nobj_total == 1 ? "" : "s");
9495 if (print_deltify)
9496 printf("; deltify: %d%%", p_deltify);
9497 if (print_sent)
9498 printf("; uploading pack: %*s %d%%", FMT_SCALED_STRSIZE - 2,
9499 scaled_packsize, p_sent);
9500 else if (print_written)
9501 printf("; writing pack: %*s %d%%", FMT_SCALED_STRSIZE - 2,
9502 scaled_packsize, p_written);
9503 if (print_searching || print_total || print_deltify ||
9504 print_written || print_sent) {
9505 a->printed_something = 1;
9506 fflush(stdout);
9508 return NULL;
9511 static const struct got_error *
9512 cmd_send(int argc, char *argv[])
9514 const struct got_error *error = NULL;
9515 char *cwd = NULL, *repo_path = NULL;
9516 const char *remote_name;
9517 char *proto = NULL, *host = NULL, *port = NULL;
9518 char *repo_name = NULL, *server_path = NULL;
9519 const struct got_remote_repo *remotes, *remote = NULL;
9520 int nremotes, nbranches = 0, ndelete_branches = 0;
9521 struct got_repository *repo = NULL;
9522 struct got_worktree *worktree = NULL;
9523 const struct got_gotconfig *repo_conf = NULL, *worktree_conf = NULL;
9524 struct got_pathlist_head branches;
9525 struct got_pathlist_head tags;
9526 struct got_reflist_head all_branches;
9527 struct got_reflist_head all_tags;
9528 struct got_pathlist_head delete_args;
9529 struct got_pathlist_head delete_branches;
9530 struct got_reflist_entry *re;
9531 struct got_pathlist_entry *pe;
9532 int i, ch, sendfd = -1, sendstatus;
9533 pid_t sendpid = -1;
9534 struct got_send_progress_arg spa;
9535 int verbosity = 0, overwrite_refs = 0;
9536 int send_all_branches = 0, send_all_tags = 0;
9537 struct got_reference *ref = NULL;
9538 int *pack_fds = NULL;
9540 TAILQ_INIT(&branches);
9541 TAILQ_INIT(&tags);
9542 TAILQ_INIT(&all_branches);
9543 TAILQ_INIT(&all_tags);
9544 TAILQ_INIT(&delete_args);
9545 TAILQ_INIT(&delete_branches);
9547 while ((ch = getopt(argc, argv, "ab:d:fqr:Tt:v")) != -1) {
9548 switch (ch) {
9549 case 'a':
9550 send_all_branches = 1;
9551 break;
9552 case 'b':
9553 error = got_pathlist_append(&branches, optarg, NULL);
9554 if (error)
9555 return error;
9556 nbranches++;
9557 break;
9558 case 'd':
9559 error = got_pathlist_append(&delete_args, optarg, NULL);
9560 if (error)
9561 return error;
9562 break;
9563 case 'f':
9564 overwrite_refs = 1;
9565 break;
9566 case 'q':
9567 verbosity = -1;
9568 break;
9569 case 'r':
9570 repo_path = realpath(optarg, NULL);
9571 if (repo_path == NULL)
9572 return got_error_from_errno2("realpath",
9573 optarg);
9574 got_path_strip_trailing_slashes(repo_path);
9575 break;
9576 case 'T':
9577 send_all_tags = 1;
9578 break;
9579 case 't':
9580 error = got_pathlist_append(&tags, optarg, NULL);
9581 if (error)
9582 return error;
9583 break;
9584 case 'v':
9585 if (verbosity < 0)
9586 verbosity = 0;
9587 else if (verbosity < 3)
9588 verbosity++;
9589 break;
9590 default:
9591 usage_send();
9592 /* NOTREACHED */
9595 argc -= optind;
9596 argv += optind;
9598 if (send_all_branches && !TAILQ_EMPTY(&branches))
9599 option_conflict('a', 'b');
9600 if (send_all_tags && !TAILQ_EMPTY(&tags))
9601 option_conflict('T', 't');
9604 if (argc == 0)
9605 remote_name = GOT_SEND_DEFAULT_REMOTE_NAME;
9606 else if (argc == 1)
9607 remote_name = argv[0];
9608 else
9609 usage_send();
9611 cwd = getcwd(NULL, 0);
9612 if (cwd == NULL) {
9613 error = got_error_from_errno("getcwd");
9614 goto done;
9617 error = got_repo_pack_fds_open(&pack_fds);
9618 if (error != NULL)
9619 goto done;
9621 if (repo_path == NULL) {
9622 error = got_worktree_open(&worktree, cwd);
9623 if (error && error->code != GOT_ERR_NOT_WORKTREE)
9624 goto done;
9625 else
9626 error = NULL;
9627 if (worktree) {
9628 repo_path =
9629 strdup(got_worktree_get_repo_path(worktree));
9630 if (repo_path == NULL)
9631 error = got_error_from_errno("strdup");
9632 if (error)
9633 goto done;
9634 } else {
9635 repo_path = strdup(cwd);
9636 if (repo_path == NULL) {
9637 error = got_error_from_errno("strdup");
9638 goto done;
9643 error = got_repo_open(&repo, repo_path, NULL, pack_fds);
9644 if (error)
9645 goto done;
9647 if (worktree) {
9648 worktree_conf = got_worktree_get_gotconfig(worktree);
9649 if (worktree_conf) {
9650 got_gotconfig_get_remotes(&nremotes, &remotes,
9651 worktree_conf);
9652 for (i = 0; i < nremotes; i++) {
9653 if (strcmp(remotes[i].name, remote_name) == 0) {
9654 remote = &remotes[i];
9655 break;
9660 if (remote == NULL) {
9661 repo_conf = got_repo_get_gotconfig(repo);
9662 if (repo_conf) {
9663 got_gotconfig_get_remotes(&nremotes, &remotes,
9664 repo_conf);
9665 for (i = 0; i < nremotes; i++) {
9666 if (strcmp(remotes[i].name, remote_name) == 0) {
9667 remote = &remotes[i];
9668 break;
9673 if (remote == NULL) {
9674 got_repo_get_gitconfig_remotes(&nremotes, &remotes, repo);
9675 for (i = 0; i < nremotes; i++) {
9676 if (strcmp(remotes[i].name, remote_name) == 0) {
9677 remote = &remotes[i];
9678 break;
9682 if (remote == NULL) {
9683 error = got_error_path(remote_name, GOT_ERR_NO_REMOTE);
9684 goto done;
9687 error = got_dial_parse_uri(&proto, &host, &port, &server_path,
9688 &repo_name, remote->send_url);
9689 if (error)
9690 goto done;
9692 if (strcmp(proto, "git") == 0) {
9693 #ifndef PROFILE
9694 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
9695 "sendfd dns inet unveil", NULL) == -1)
9696 err(1, "pledge");
9697 #endif
9698 } else if (strcmp(proto, "git+ssh") == 0 ||
9699 strcmp(proto, "ssh") == 0) {
9700 #ifndef PROFILE
9701 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
9702 "sendfd unveil", NULL) == -1)
9703 err(1, "pledge");
9704 #endif
9705 } else if (strcmp(proto, "http") == 0 ||
9706 strcmp(proto, "git+http") == 0) {
9707 error = got_error_path(proto, GOT_ERR_NOT_IMPL);
9708 goto done;
9709 } else {
9710 error = got_error_path(proto, GOT_ERR_BAD_PROTO);
9711 goto done;
9714 error = got_dial_apply_unveil(proto);
9715 if (error)
9716 goto done;
9718 error = apply_unveil(got_repo_get_path(repo), 0, NULL);
9719 if (error)
9720 goto done;
9722 if (send_all_branches) {
9723 error = got_ref_list(&all_branches, repo, "refs/heads",
9724 got_ref_cmp_by_name, NULL);
9725 if (error)
9726 goto done;
9727 TAILQ_FOREACH(re, &all_branches, entry) {
9728 const char *branchname = got_ref_get_name(re->ref);
9729 error = got_pathlist_append(&branches,
9730 branchname, NULL);
9731 if (error)
9732 goto done;
9733 nbranches++;
9735 } else if (nbranches == 0) {
9736 for (i = 0; i < remote->nsend_branches; i++) {
9737 error = got_pathlist_append(&branches,
9738 remote->send_branches[i], NULL);
9739 if (error)
9740 goto done;
9744 if (send_all_tags) {
9745 error = got_ref_list(&all_tags, repo, "refs/tags",
9746 got_ref_cmp_by_name, NULL);
9747 if (error)
9748 goto done;
9749 TAILQ_FOREACH(re, &all_tags, entry) {
9750 const char *tagname = got_ref_get_name(re->ref);
9751 error = got_pathlist_append(&tags,
9752 tagname, NULL);
9753 if (error)
9754 goto done;
9759 * To prevent accidents only branches in refs/heads/ can be deleted
9760 * with 'got send -d'.
9761 * Deleting anything else requires local repository access or Git.
9763 TAILQ_FOREACH(pe, &delete_args, entry) {
9764 const char *branchname = pe->path;
9765 char *s;
9766 struct got_pathlist_entry *new;
9767 if (strncmp(branchname, "refs/heads/", 11) == 0) {
9768 s = strdup(branchname);
9769 if (s == NULL) {
9770 error = got_error_from_errno("strdup");
9771 goto done;
9773 } else {
9774 if (asprintf(&s, "refs/heads/%s", branchname) == -1) {
9775 error = got_error_from_errno("asprintf");
9776 goto done;
9779 error = got_pathlist_insert(&new, &delete_branches, s, NULL);
9780 if (error || new == NULL /* duplicate */)
9781 free(s);
9782 if (error)
9783 goto done;
9784 ndelete_branches++;
9787 if (nbranches == 0 && ndelete_branches == 0) {
9788 struct got_reference *head_ref;
9789 if (worktree)
9790 error = got_ref_open(&head_ref, repo,
9791 got_worktree_get_head_ref_name(worktree), 0);
9792 else
9793 error = got_ref_open(&head_ref, repo, GOT_REF_HEAD, 0);
9794 if (error)
9795 goto done;
9796 if (got_ref_is_symbolic(head_ref)) {
9797 error = got_ref_resolve_symbolic(&ref, repo, head_ref);
9798 got_ref_close(head_ref);
9799 if (error)
9800 goto done;
9801 } else
9802 ref = head_ref;
9803 error = got_pathlist_append(&branches, got_ref_get_name(ref),
9804 NULL);
9805 if (error)
9806 goto done;
9807 nbranches++;
9810 if (verbosity >= 0) {
9811 printf("Connecting to \"%s\" %s://%s%s%s%s%s\n",
9812 remote->name, proto, host,
9813 port ? ":" : "", port ? port : "",
9814 *server_path == '/' ? "" : "/", server_path);
9817 error = got_send_connect(&sendpid, &sendfd, proto, host, port,
9818 server_path, verbosity);
9819 if (error)
9820 goto done;
9822 memset(&spa, 0, sizeof(spa));
9823 spa.last_scaled_packsize[0] = '\0';
9824 spa.last_p_deltify = -1;
9825 spa.last_p_written = -1;
9826 spa.verbosity = verbosity;
9827 spa.delete_branches = &delete_branches;
9828 error = got_send_pack(remote_name, &branches, &tags, &delete_branches,
9829 verbosity, overwrite_refs, sendfd, repo, send_progress, &spa,
9830 check_cancelled, NULL);
9831 if (spa.printed_something)
9832 putchar('\n');
9833 if (error)
9834 goto done;
9835 if (!spa.sent_something && verbosity >= 0)
9836 printf("Already up-to-date\n");
9837 done:
9838 if (sendpid > 0) {
9839 if (kill(sendpid, SIGTERM) == -1)
9840 error = got_error_from_errno("kill");
9841 if (waitpid(sendpid, &sendstatus, 0) == -1 && error == NULL)
9842 error = got_error_from_errno("waitpid");
9844 if (sendfd != -1 && close(sendfd) == -1 && error == NULL)
9845 error = got_error_from_errno("close");
9846 if (repo) {
9847 const struct got_error *close_err = got_repo_close(repo);
9848 if (error == NULL)
9849 error = close_err;
9851 if (worktree)
9852 got_worktree_close(worktree);
9853 if (pack_fds) {
9854 const struct got_error *pack_err =
9855 got_repo_pack_fds_close(pack_fds);
9856 if (error == NULL)
9857 error = pack_err;
9859 if (ref)
9860 got_ref_close(ref);
9861 got_pathlist_free(&branches, GOT_PATHLIST_FREE_NONE);
9862 got_pathlist_free(&tags, GOT_PATHLIST_FREE_NONE);
9863 got_ref_list_free(&all_branches);
9864 got_ref_list_free(&all_tags);
9865 got_pathlist_free(&delete_args, GOT_PATHLIST_FREE_NONE);
9866 got_pathlist_free(&delete_branches, GOT_PATHLIST_FREE_PATH);
9867 free(cwd);
9868 free(repo_path);
9869 free(proto);
9870 free(host);
9871 free(port);
9872 free(server_path);
9873 free(repo_name);
9874 return error;
9878 * Print and if delete is set delete all ref_prefix references.
9879 * If wanted_ref is not NULL, only print or delete this reference.
9881 static const struct got_error *
9882 process_logmsg_refs(const char *ref_prefix, size_t prefix_len,
9883 const char *wanted_ref, int delete, struct got_worktree *worktree,
9884 struct got_repository *repo)
9886 const struct got_error *err;
9887 struct got_pathlist_head paths;
9888 struct got_reflist_head refs;
9889 struct got_reflist_entry *re;
9890 struct got_reflist_object_id_map *refs_idmap = NULL;
9891 struct got_commit_object *commit = NULL;
9892 struct got_object_id *id = NULL;
9893 const char *header_prefix;
9894 char *uuidstr = NULL;
9895 int found = 0;
9897 TAILQ_INIT(&refs);
9898 TAILQ_INIT(&paths);
9900 err = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name, repo);
9901 if (err)
9902 goto done;
9904 err = got_reflist_object_id_map_create(&refs_idmap, &refs, repo);
9905 if (err)
9906 goto done;
9908 if (worktree != NULL) {
9909 err = got_worktree_get_uuid(&uuidstr, worktree);
9910 if (err)
9911 goto done;
9914 if (wanted_ref) {
9915 if (strncmp(wanted_ref, "refs/heads/", 11) == 0)
9916 wanted_ref += 11;
9919 if (strcmp(ref_prefix, GOT_WORKTREE_BACKOUT_REF_PREFIX) == 0)
9920 header_prefix = "backout";
9921 else
9922 header_prefix = "cherrypick";
9924 TAILQ_FOREACH(re, &refs, entry) {
9925 const char *refname, *wt;
9927 refname = got_ref_get_name(re->ref);
9929 err = check_cancelled(NULL);
9930 if (err)
9931 goto done;
9933 if (strncmp(refname, ref_prefix, prefix_len) == 0)
9934 refname += prefix_len + 1; /* skip '-' delimiter */
9935 else
9936 continue;
9938 wt = refname;
9940 if (worktree == NULL || strncmp(refname, uuidstr,
9941 GOT_WORKTREE_UUID_STRLEN) == 0)
9942 refname += GOT_WORKTREE_UUID_STRLEN + 1; /* skip '-' */
9943 else
9944 continue;
9946 err = got_repo_match_object_id(&id, NULL, refname,
9947 GOT_OBJ_TYPE_COMMIT, NULL, repo);
9948 if (err)
9949 goto done;
9951 err = got_object_open_as_commit(&commit, repo, id);
9952 if (err)
9953 goto done;
9955 if (wanted_ref)
9956 found = strncmp(wanted_ref, refname,
9957 strlen(wanted_ref)) == 0;
9958 if (wanted_ref && !found) {
9959 struct got_reflist_head *ci_refs;
9961 ci_refs = got_reflist_object_id_map_lookup(refs_idmap,
9962 id);
9964 if (ci_refs) {
9965 char *refs_str = NULL;
9966 char const *r = NULL;
9968 err = build_refs_str(&refs_str, ci_refs, id,
9969 repo, 1);
9970 if (err)
9971 goto done;
9973 r = refs_str;
9974 while (r) {
9975 if (strncmp(r, wanted_ref,
9976 strlen(wanted_ref)) == 0) {
9977 found = 1;
9978 break;
9980 r = strchr(r, ' ');
9981 if (r)
9982 ++r;
9984 free(refs_str);
9988 if (wanted_ref == NULL || found) {
9989 if (delete) {
9990 err = got_ref_delete(re->ref, repo);
9991 if (err)
9992 goto done;
9993 printf("Deleted: ");
9994 err = print_commit_oneline(commit, id, repo,
9995 refs_idmap);
9996 } else {
9998 * Print paths modified by commit to help
9999 * associate commits with worktree changes.
10001 err = get_changed_paths(&paths, commit,
10002 repo, NULL);
10003 if (err)
10004 goto done;
10006 err = print_commit(commit, id, repo, NULL,
10007 &paths, NULL, 0, 0, refs_idmap, NULL,
10008 header_prefix);
10009 got_pathlist_free(&paths,
10010 GOT_PATHLIST_FREE_ALL);
10012 if (worktree == NULL)
10013 printf("work tree: %.*s\n\n",
10014 GOT_WORKTREE_UUID_STRLEN, wt);
10016 if (err || found)
10017 goto done;
10020 got_object_commit_close(commit);
10021 commit = NULL;
10022 free(id);
10023 id = NULL;
10026 if (wanted_ref != NULL && !found)
10027 err = got_error_fmt(GOT_ERR_NOT_REF, "%s", wanted_ref);
10029 done:
10030 free(id);
10031 free(uuidstr);
10032 got_ref_list_free(&refs);
10033 got_pathlist_free(&paths, GOT_PATHLIST_FREE_ALL);
10034 if (refs_idmap)
10035 got_reflist_object_id_map_free(refs_idmap);
10036 if (commit)
10037 got_object_commit_close(commit);
10038 return err;
10042 * Create new temp "logmsg" ref of the backed-out or cherrypicked commit
10043 * identified by id for log messages to prepopulate the editor on commit.
10045 static const struct got_error *
10046 logmsg_ref(struct got_object_id *id, const char *prefix,
10047 struct got_worktree *worktree, struct got_repository *repo)
10049 const struct got_error *err = NULL;
10050 char *idstr, *ref = NULL, *refname = NULL;
10051 int histedit_in_progress;
10052 int rebase_in_progress, merge_in_progress;
10055 * Silenty refuse to create merge reference if any histedit, merge,
10056 * or rebase operation is in progress.
10058 err = got_worktree_histedit_in_progress(&histedit_in_progress,
10059 worktree);
10060 if (err)
10061 return err;
10062 if (histedit_in_progress)
10063 return NULL;
10065 err = got_worktree_rebase_in_progress(&rebase_in_progress, worktree);
10066 if (err)
10067 return err;
10068 if (rebase_in_progress)
10069 return NULL;
10071 err = got_worktree_merge_in_progress(&merge_in_progress, worktree,
10072 repo);
10073 if (err)
10074 return err;
10075 if (merge_in_progress)
10076 return NULL;
10078 err = got_object_id_str(&idstr, id);
10079 if (err)
10080 return err;
10082 err = got_worktree_get_logmsg_ref_name(&refname, worktree, prefix);
10083 if (err)
10084 goto done;
10086 if (asprintf(&ref, "%s-%s", refname, idstr) == -1) {
10087 err = got_error_from_errno("asprintf");
10088 goto done;
10091 err = create_ref(ref, got_worktree_get_base_commit_id(worktree),
10092 -1, repo);
10093 done:
10094 free(ref);
10095 free(idstr);
10096 free(refname);
10097 return err;
10100 __dead static void
10101 usage_cherrypick(void)
10103 fprintf(stderr, "usage: %s cherrypick [-lX] [commit-id]\n",
10104 getprogname());
10105 exit(1);
10108 static const struct got_error *
10109 cmd_cherrypick(int argc, char *argv[])
10111 const struct got_error *error = NULL;
10112 struct got_worktree *worktree = NULL;
10113 struct got_repository *repo = NULL;
10114 char *cwd = NULL, *commit_id_str = NULL;
10115 struct got_object_id *commit_id = NULL;
10116 struct got_commit_object *commit = NULL;
10117 struct got_object_qid *pid;
10118 int ch, list_refs = 0, remove_refs = 0;
10119 struct got_update_progress_arg upa;
10120 int *pack_fds = NULL;
10122 #ifndef PROFILE
10123 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
10124 "unveil", NULL) == -1)
10125 err(1, "pledge");
10126 #endif
10128 while ((ch = getopt(argc, argv, "lX")) != -1) {
10129 switch (ch) {
10130 case 'l':
10131 list_refs = 1;
10132 break;
10133 case 'X':
10134 remove_refs = 1;
10135 break;
10136 default:
10137 usage_cherrypick();
10138 /* NOTREACHED */
10142 argc -= optind;
10143 argv += optind;
10145 if (list_refs || remove_refs) {
10146 if (argc != 0 && argc != 1)
10147 usage_cherrypick();
10148 } else if (argc != 1)
10149 usage_cherrypick();
10150 if (list_refs && remove_refs)
10151 option_conflict('l', 'X');
10153 cwd = getcwd(NULL, 0);
10154 if (cwd == NULL) {
10155 error = got_error_from_errno("getcwd");
10156 goto done;
10159 error = got_repo_pack_fds_open(&pack_fds);
10160 if (error != NULL)
10161 goto done;
10163 error = got_worktree_open(&worktree, cwd);
10164 if (error) {
10165 if (list_refs || remove_refs) {
10166 if (error->code != GOT_ERR_NOT_WORKTREE)
10167 goto done;
10168 } else {
10169 if (error->code == GOT_ERR_NOT_WORKTREE)
10170 error = wrap_not_worktree_error(error,
10171 "cherrypick", cwd);
10172 goto done;
10176 error = got_repo_open(&repo,
10177 worktree ? got_worktree_get_repo_path(worktree) : cwd,
10178 NULL, pack_fds);
10179 if (error != NULL)
10180 goto done;
10182 error = apply_unveil(got_repo_get_path(repo), 0,
10183 worktree ? got_worktree_get_root_path(worktree) : NULL);
10184 if (error)
10185 goto done;
10187 if (list_refs || remove_refs) {
10188 error = process_logmsg_refs(GOT_WORKTREE_CHERRYPICK_REF_PREFIX,
10189 GOT_WORKTREE_CHERRYPICK_REF_PREFIX_LEN,
10190 argc == 1 ? argv[0] : NULL, remove_refs, worktree, repo);
10191 goto done;
10194 error = got_repo_match_object_id(&commit_id, NULL, argv[0],
10195 GOT_OBJ_TYPE_COMMIT, NULL, repo);
10196 if (error)
10197 goto done;
10198 error = got_object_id_str(&commit_id_str, commit_id);
10199 if (error)
10200 goto done;
10202 error = got_object_open_as_commit(&commit, repo, commit_id);
10203 if (error)
10204 goto done;
10205 pid = STAILQ_FIRST(got_object_commit_get_parent_ids(commit));
10206 memset(&upa, 0, sizeof(upa));
10207 error = got_worktree_merge_files(worktree, pid ? &pid->id : NULL,
10208 commit_id, repo, update_progress, &upa, check_cancelled,
10209 NULL);
10210 if (error != NULL)
10211 goto done;
10213 if (upa.did_something) {
10214 error = logmsg_ref(commit_id,
10215 GOT_WORKTREE_CHERRYPICK_REF_PREFIX, worktree, repo);
10216 if (error)
10217 goto done;
10218 printf("Merged commit %s\n", commit_id_str);
10220 print_merge_progress_stats(&upa);
10221 done:
10222 free(cwd);
10223 if (commit)
10224 got_object_commit_close(commit);
10225 free(commit_id_str);
10226 if (worktree)
10227 got_worktree_close(worktree);
10228 if (repo) {
10229 const struct got_error *close_err = got_repo_close(repo);
10230 if (error == NULL)
10231 error = close_err;
10233 if (pack_fds) {
10234 const struct got_error *pack_err =
10235 got_repo_pack_fds_close(pack_fds);
10236 if (error == NULL)
10237 error = pack_err;
10240 return error;
10243 __dead static void
10244 usage_backout(void)
10246 fprintf(stderr, "usage: %s backout [-lX] [commit-id]\n", getprogname());
10247 exit(1);
10250 static const struct got_error *
10251 cmd_backout(int argc, char *argv[])
10253 const struct got_error *error = NULL;
10254 struct got_worktree *worktree = NULL;
10255 struct got_repository *repo = NULL;
10256 char *cwd = NULL, *commit_id_str = NULL;
10257 struct got_object_id *commit_id = NULL;
10258 struct got_commit_object *commit = NULL;
10259 struct got_object_qid *pid;
10260 int ch, list_refs = 0, remove_refs = 0;
10261 struct got_update_progress_arg upa;
10262 int *pack_fds = NULL;
10264 #ifndef PROFILE
10265 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
10266 "unveil", NULL) == -1)
10267 err(1, "pledge");
10268 #endif
10270 while ((ch = getopt(argc, argv, "lX")) != -1) {
10271 switch (ch) {
10272 case 'l':
10273 list_refs = 1;
10274 break;
10275 case 'X':
10276 remove_refs = 1;
10277 break;
10278 default:
10279 usage_backout();
10280 /* NOTREACHED */
10284 argc -= optind;
10285 argv += optind;
10287 if (list_refs || remove_refs) {
10288 if (argc != 0 && argc != 1)
10289 usage_backout();
10290 } else if (argc != 1)
10291 usage_backout();
10292 if (list_refs && remove_refs)
10293 option_conflict('l', 'X');
10295 cwd = getcwd(NULL, 0);
10296 if (cwd == NULL) {
10297 error = got_error_from_errno("getcwd");
10298 goto done;
10301 error = got_repo_pack_fds_open(&pack_fds);
10302 if (error != NULL)
10303 goto done;
10305 error = got_worktree_open(&worktree, cwd);
10306 if (error) {
10307 if (list_refs || remove_refs) {
10308 if (error->code != GOT_ERR_NOT_WORKTREE)
10309 goto done;
10310 } else {
10311 if (error->code == GOT_ERR_NOT_WORKTREE)
10312 error = wrap_not_worktree_error(error,
10313 "backout", cwd);
10314 goto done;
10318 error = got_repo_open(&repo,
10319 worktree ? got_worktree_get_repo_path(worktree) : cwd,
10320 NULL, pack_fds);
10321 if (error != NULL)
10322 goto done;
10324 error = apply_unveil(got_repo_get_path(repo), 0,
10325 worktree ? got_worktree_get_root_path(worktree) : NULL);
10326 if (error)
10327 goto done;
10329 if (list_refs || remove_refs) {
10330 error = process_logmsg_refs(GOT_WORKTREE_BACKOUT_REF_PREFIX,
10331 GOT_WORKTREE_BACKOUT_REF_PREFIX_LEN,
10332 argc == 1 ? argv[0] : NULL, remove_refs, worktree, repo);
10333 goto done;
10336 error = got_repo_match_object_id(&commit_id, NULL, argv[0],
10337 GOT_OBJ_TYPE_COMMIT, NULL, repo);
10338 if (error)
10339 goto done;
10340 error = got_object_id_str(&commit_id_str, commit_id);
10341 if (error)
10342 goto done;
10344 error = got_object_open_as_commit(&commit, repo, commit_id);
10345 if (error)
10346 goto done;
10347 pid = STAILQ_FIRST(got_object_commit_get_parent_ids(commit));
10348 if (pid == NULL) {
10349 error = got_error(GOT_ERR_ROOT_COMMIT);
10350 goto done;
10353 memset(&upa, 0, sizeof(upa));
10354 error = got_worktree_merge_files(worktree, commit_id, &pid->id,
10355 repo, update_progress, &upa, check_cancelled, NULL);
10356 if (error != NULL)
10357 goto done;
10359 if (upa.did_something) {
10360 error = logmsg_ref(commit_id, GOT_WORKTREE_BACKOUT_REF_PREFIX,
10361 worktree, repo);
10362 if (error)
10363 goto done;
10364 printf("Backed out commit %s\n", commit_id_str);
10366 print_merge_progress_stats(&upa);
10367 done:
10368 free(cwd);
10369 if (commit)
10370 got_object_commit_close(commit);
10371 free(commit_id_str);
10372 if (worktree)
10373 got_worktree_close(worktree);
10374 if (repo) {
10375 const struct got_error *close_err = got_repo_close(repo);
10376 if (error == NULL)
10377 error = close_err;
10379 if (pack_fds) {
10380 const struct got_error *pack_err =
10381 got_repo_pack_fds_close(pack_fds);
10382 if (error == NULL)
10383 error = pack_err;
10385 return error;
10388 __dead static void
10389 usage_rebase(void)
10391 fprintf(stderr, "usage: %s rebase [-aCclX] [branch]\n", getprogname());
10392 exit(1);
10395 static void
10396 trim_logmsg(char *logmsg, int limit)
10398 char *nl;
10399 size_t len;
10401 len = strlen(logmsg);
10402 if (len > limit)
10403 len = limit;
10404 logmsg[len] = '\0';
10405 nl = strchr(logmsg, '\n');
10406 if (nl)
10407 *nl = '\0';
10410 static const struct got_error *
10411 get_short_logmsg(char **logmsg, int limit, struct got_commit_object *commit)
10413 const struct got_error *err;
10414 char *logmsg0 = NULL;
10415 const char *s;
10417 err = got_object_commit_get_logmsg(&logmsg0, commit);
10418 if (err)
10419 return err;
10421 s = logmsg0;
10422 while (isspace((unsigned char)s[0]))
10423 s++;
10425 *logmsg = strdup(s);
10426 if (*logmsg == NULL) {
10427 err = got_error_from_errno("strdup");
10428 goto done;
10431 trim_logmsg(*logmsg, limit);
10432 done:
10433 free(logmsg0);
10434 return err;
10437 static const struct got_error *
10438 show_rebase_merge_conflict(struct got_object_id *id,
10439 struct got_repository *repo)
10441 const struct got_error *err;
10442 struct got_commit_object *commit = NULL;
10443 char *id_str = NULL, *logmsg = NULL;
10445 err = got_object_open_as_commit(&commit, repo, id);
10446 if (err)
10447 return err;
10449 err = got_object_id_str(&id_str, id);
10450 if (err)
10451 goto done;
10453 id_str[12] = '\0';
10455 err = get_short_logmsg(&logmsg, 42, commit);
10456 if (err)
10457 goto done;
10459 printf("%s -> merge conflict: %s\n", id_str, logmsg);
10460 done:
10461 free(id_str);
10462 got_object_commit_close(commit);
10463 free(logmsg);
10464 return err;
10467 static const struct got_error *
10468 show_rebase_progress(struct got_commit_object *commit,
10469 struct got_object_id *old_id, struct got_object_id *new_id)
10471 const struct got_error *err;
10472 char *old_id_str = NULL, *new_id_str = NULL, *logmsg = NULL;
10474 err = got_object_id_str(&old_id_str, old_id);
10475 if (err)
10476 goto done;
10478 if (new_id) {
10479 err = got_object_id_str(&new_id_str, new_id);
10480 if (err)
10481 goto done;
10484 old_id_str[12] = '\0';
10485 if (new_id_str)
10486 new_id_str[12] = '\0';
10488 err = get_short_logmsg(&logmsg, 42, commit);
10489 if (err)
10490 goto done;
10492 printf("%s -> %s: %s\n", old_id_str,
10493 new_id_str ? new_id_str : "no-op change", logmsg);
10494 done:
10495 free(old_id_str);
10496 free(new_id_str);
10497 free(logmsg);
10498 return err;
10501 static const struct got_error *
10502 rebase_complete(struct got_worktree *worktree, struct got_fileindex *fileindex,
10503 struct got_reference *branch, struct got_reference *tmp_branch,
10504 struct got_repository *repo, int create_backup)
10506 printf("Switching work tree to %s\n", got_ref_get_name(branch));
10507 return got_worktree_rebase_complete(worktree, fileindex,
10508 tmp_branch, branch, repo, create_backup);
10511 static const struct got_error *
10512 rebase_commit(struct got_pathlist_head *merged_paths,
10513 struct got_worktree *worktree, struct got_fileindex *fileindex,
10514 struct got_reference *tmp_branch, const char *committer,
10515 struct got_object_id *commit_id, int allow_conflict,
10516 struct got_repository *repo)
10518 const struct got_error *error;
10519 struct got_commit_object *commit;
10520 struct got_object_id *new_commit_id;
10522 error = got_object_open_as_commit(&commit, repo, commit_id);
10523 if (error)
10524 return error;
10526 error = got_worktree_rebase_commit(&new_commit_id, merged_paths,
10527 worktree, fileindex, tmp_branch, committer, commit, commit_id,
10528 allow_conflict, repo);
10529 if (error) {
10530 if (error->code != GOT_ERR_COMMIT_NO_CHANGES)
10531 goto done;
10532 error = show_rebase_progress(commit, commit_id, NULL);
10533 } else {
10534 error = show_rebase_progress(commit, commit_id, new_commit_id);
10535 free(new_commit_id);
10537 done:
10538 got_object_commit_close(commit);
10539 return error;
10542 struct check_path_prefix_arg {
10543 const char *path_prefix;
10544 size_t len;
10545 int errcode;
10548 static const struct got_error *
10549 check_path_prefix_in_diff(void *arg, struct got_blob_object *blob1,
10550 struct got_blob_object *blob2, FILE *f1, FILE *f2,
10551 struct got_object_id *id1, struct got_object_id *id2,
10552 const char *path1, const char *path2,
10553 mode_t mode1, mode_t mode2, struct got_repository *repo)
10555 struct check_path_prefix_arg *a = arg;
10557 if ((path1 && !got_path_is_child(path1, a->path_prefix, a->len)) ||
10558 (path2 && !got_path_is_child(path2, a->path_prefix, a->len)))
10559 return got_error(a->errcode);
10561 return NULL;
10564 static const struct got_error *
10565 check_path_prefix(struct got_object_id *parent_id,
10566 struct got_object_id *commit_id, const char *path_prefix,
10567 int errcode, struct got_repository *repo)
10569 const struct got_error *err;
10570 struct got_tree_object *tree1 = NULL, *tree2 = NULL;
10571 struct got_commit_object *commit = NULL, *parent_commit = NULL;
10572 struct check_path_prefix_arg cpp_arg;
10574 if (got_path_is_root_dir(path_prefix))
10575 return NULL;
10577 err = got_object_open_as_commit(&commit, repo, commit_id);
10578 if (err)
10579 goto done;
10581 err = got_object_open_as_commit(&parent_commit, repo, parent_id);
10582 if (err)
10583 goto done;
10585 err = got_object_open_as_tree(&tree1, repo,
10586 got_object_commit_get_tree_id(parent_commit));
10587 if (err)
10588 goto done;
10590 err = got_object_open_as_tree(&tree2, repo,
10591 got_object_commit_get_tree_id(commit));
10592 if (err)
10593 goto done;
10595 cpp_arg.path_prefix = path_prefix;
10596 while (cpp_arg.path_prefix[0] == '/')
10597 cpp_arg.path_prefix++;
10598 cpp_arg.len = strlen(cpp_arg.path_prefix);
10599 cpp_arg.errcode = errcode;
10600 err = got_diff_tree(tree1, tree2, NULL, NULL, -1, -1, "", "", repo,
10601 check_path_prefix_in_diff, &cpp_arg, 0);
10602 done:
10603 if (tree1)
10604 got_object_tree_close(tree1);
10605 if (tree2)
10606 got_object_tree_close(tree2);
10607 if (commit)
10608 got_object_commit_close(commit);
10609 if (parent_commit)
10610 got_object_commit_close(parent_commit);
10611 return err;
10614 static const struct got_error *
10615 collect_commits(struct got_object_id_queue *commits,
10616 struct got_object_id *initial_commit_id,
10617 struct got_object_id *iter_start_id, struct got_object_id *iter_stop_id,
10618 const char *path_prefix, int path_prefix_errcode,
10619 struct got_repository *repo)
10621 const struct got_error *err = NULL;
10622 struct got_commit_graph *graph = NULL;
10623 struct got_object_id parent_id, commit_id;
10624 struct got_object_qid *qid;
10626 err = got_commit_graph_open(&graph, "/", 1);
10627 if (err)
10628 return err;
10630 err = got_commit_graph_iter_start(graph, iter_start_id, repo,
10631 check_cancelled, NULL);
10632 if (err)
10633 goto done;
10635 memcpy(&commit_id, initial_commit_id, sizeof(commit_id));
10636 while (got_object_id_cmp(&commit_id, iter_stop_id) != 0) {
10637 err = got_commit_graph_iter_next(&parent_id, graph, repo,
10638 check_cancelled, NULL);
10639 if (err) {
10640 if (err->code == GOT_ERR_ITER_COMPLETED) {
10641 err = got_error_msg(GOT_ERR_ANCESTRY,
10642 "ran out of commits to rebase before "
10643 "youngest common ancestor commit has "
10644 "been reached?!?");
10646 goto done;
10647 } else {
10648 err = check_path_prefix(&parent_id, &commit_id,
10649 path_prefix, path_prefix_errcode, repo);
10650 if (err)
10651 goto done;
10653 err = got_object_qid_alloc(&qid, &commit_id);
10654 if (err)
10655 goto done;
10656 STAILQ_INSERT_HEAD(commits, qid, entry);
10658 memcpy(&commit_id, &parent_id, sizeof(commit_id));
10661 done:
10662 got_commit_graph_close(graph);
10663 return err;
10666 static const struct got_error *
10667 get_commit_brief_str(char **brief_str, struct got_commit_object *commit)
10669 const struct got_error *err = NULL;
10670 time_t committer_time;
10671 struct tm tm;
10672 char datebuf[11]; /* YYYY-MM-DD + NUL */
10673 char *author0 = NULL, *author, *smallerthan;
10674 char *logmsg0 = NULL, *logmsg, *newline;
10676 committer_time = got_object_commit_get_committer_time(commit);
10677 if (gmtime_r(&committer_time, &tm) == NULL)
10678 return got_error_from_errno("gmtime_r");
10679 if (strftime(datebuf, sizeof(datebuf), "%G-%m-%d", &tm) == 0)
10680 return got_error(GOT_ERR_NO_SPACE);
10682 author0 = strdup(got_object_commit_get_author(commit));
10683 if (author0 == NULL)
10684 return got_error_from_errno("strdup");
10685 author = author0;
10686 smallerthan = strchr(author, '<');
10687 if (smallerthan && smallerthan[1] != '\0')
10688 author = smallerthan + 1;
10689 author[strcspn(author, "@>")] = '\0';
10691 err = got_object_commit_get_logmsg(&logmsg0, commit);
10692 if (err)
10693 goto done;
10694 logmsg = logmsg0;
10695 while (*logmsg == '\n')
10696 logmsg++;
10697 newline = strchr(logmsg, '\n');
10698 if (newline)
10699 *newline = '\0';
10701 if (asprintf(brief_str, "%s %s %s",
10702 datebuf, author, logmsg) == -1)
10703 err = got_error_from_errno("asprintf");
10704 done:
10705 free(author0);
10706 free(logmsg0);
10707 return err;
10710 static const struct got_error *
10711 delete_backup_ref(struct got_reference *ref, struct got_object_id *id,
10712 struct got_repository *repo)
10714 const struct got_error *err;
10715 char *id_str;
10717 err = got_object_id_str(&id_str, id);
10718 if (err)
10719 return err;
10721 err = got_ref_delete(ref, repo);
10722 if (err)
10723 goto done;
10725 printf("Deleted %s: %s\n", got_ref_get_name(ref), id_str);
10726 done:
10727 free(id_str);
10728 return err;
10731 static const struct got_error *
10732 print_backup_ref(const char *branch_name, const char *new_id_str,
10733 struct got_object_id *old_commit_id, struct got_commit_object *old_commit,
10734 struct got_reflist_object_id_map *refs_idmap,
10735 struct got_repository *repo)
10737 const struct got_error *err = NULL;
10738 struct got_reflist_head *refs;
10739 char *refs_str = NULL;
10740 struct got_object_id *new_commit_id = NULL;
10741 struct got_commit_object *new_commit = NULL;
10742 char *new_commit_brief_str = NULL;
10743 struct got_object_id *yca_id = NULL;
10744 struct got_commit_object *yca_commit = NULL;
10745 char *yca_id_str = NULL, *yca_brief_str = NULL;
10746 char *custom_refs_str;
10748 if (asprintf(&custom_refs_str, "formerly %s", branch_name) == -1)
10749 return got_error_from_errno("asprintf");
10751 err = print_commit(old_commit, old_commit_id, repo, NULL, NULL, NULL,
10752 0, 0, refs_idmap, custom_refs_str, NULL);
10753 if (err)
10754 goto done;
10756 err = got_object_resolve_id_str(&new_commit_id, repo, new_id_str);
10757 if (err)
10758 goto done;
10760 refs = got_reflist_object_id_map_lookup(refs_idmap, new_commit_id);
10761 if (refs) {
10762 err = build_refs_str(&refs_str, refs, new_commit_id, repo, 0);
10763 if (err)
10764 goto done;
10767 err = got_object_open_as_commit(&new_commit, repo, new_commit_id);
10768 if (err)
10769 goto done;
10771 err = get_commit_brief_str(&new_commit_brief_str, new_commit);
10772 if (err)
10773 goto done;
10775 err = got_commit_graph_find_youngest_common_ancestor(&yca_id,
10776 old_commit_id, new_commit_id, 1, repo, check_cancelled, NULL);
10777 if (err)
10778 goto done;
10780 printf("has become commit %s%s%s%s\n %s\n", new_id_str,
10781 refs_str ? " (" : "", refs_str ? refs_str : "",
10782 refs_str ? ")" : "", new_commit_brief_str);
10783 if (yca_id && got_object_id_cmp(yca_id, new_commit_id) != 0 &&
10784 got_object_id_cmp(yca_id, old_commit_id) != 0) {
10785 free(refs_str);
10786 refs_str = NULL;
10788 err = got_object_open_as_commit(&yca_commit, repo, yca_id);
10789 if (err)
10790 goto done;
10792 err = get_commit_brief_str(&yca_brief_str, yca_commit);
10793 if (err)
10794 goto done;
10796 err = got_object_id_str(&yca_id_str, yca_id);
10797 if (err)
10798 goto done;
10800 refs = got_reflist_object_id_map_lookup(refs_idmap, yca_id);
10801 if (refs) {
10802 err = build_refs_str(&refs_str, refs, yca_id, repo, 0);
10803 if (err)
10804 goto done;
10806 printf("history forked at %s%s%s%s\n %s\n",
10807 yca_id_str,
10808 refs_str ? " (" : "", refs_str ? refs_str : "",
10809 refs_str ? ")" : "", yca_brief_str);
10811 done:
10812 free(custom_refs_str);
10813 free(new_commit_id);
10814 free(refs_str);
10815 free(yca_id);
10816 free(yca_id_str);
10817 free(yca_brief_str);
10818 if (new_commit)
10819 got_object_commit_close(new_commit);
10820 if (yca_commit)
10821 got_object_commit_close(yca_commit);
10823 return err;
10826 static const struct got_error *
10827 worktree_has_logmsg_ref(const char *caller, struct got_worktree *worktree,
10828 struct got_repository *repo)
10830 const struct got_error *err;
10831 struct got_reflist_head refs;
10832 struct got_reflist_entry *re;
10833 char *uuidstr = NULL;
10834 static char msg[160];
10836 TAILQ_INIT(&refs);
10838 err = got_worktree_get_uuid(&uuidstr, worktree);
10839 if (err)
10840 goto done;
10842 err = got_ref_list(&refs, repo, "refs/got/worktree",
10843 got_ref_cmp_by_name, repo);
10844 if (err)
10845 goto done;
10847 TAILQ_FOREACH(re, &refs, entry) {
10848 const char *cmd, *refname, *type;
10850 refname = got_ref_get_name(re->ref);
10852 if (strncmp(refname, GOT_WORKTREE_CHERRYPICK_REF_PREFIX,
10853 GOT_WORKTREE_CHERRYPICK_REF_PREFIX_LEN) == 0) {
10854 refname += GOT_WORKTREE_CHERRYPICK_REF_PREFIX_LEN + 1;
10855 cmd = "cherrypick";
10856 type = "cherrypicked";
10857 } else if (strncmp(refname, GOT_WORKTREE_BACKOUT_REF_PREFIX,
10858 GOT_WORKTREE_BACKOUT_REF_PREFIX_LEN) == 0) {
10859 refname += GOT_WORKTREE_BACKOUT_REF_PREFIX_LEN + 1;
10860 cmd = "backout";
10861 type = "backed-out";
10862 } else
10863 continue;
10865 if (strncmp(refname, uuidstr, GOT_WORKTREE_UUID_STRLEN) != 0)
10866 continue;
10868 snprintf(msg, sizeof(msg),
10869 "work tree has references created by %s commits which "
10870 "must be removed with 'got %s -X' before running the %s "
10871 "command", type, cmd, caller);
10872 err = got_error_msg(GOT_ERR_WORKTREE_META, msg);
10873 goto done;
10876 done:
10877 free(uuidstr);
10878 got_ref_list_free(&refs);
10879 return err;
10882 static const struct got_error *
10883 process_backup_refs(const char *backup_ref_prefix,
10884 const char *wanted_branch_name,
10885 int delete, struct got_repository *repo)
10887 const struct got_error *err;
10888 struct got_reflist_head refs, backup_refs;
10889 struct got_reflist_entry *re;
10890 const size_t backup_ref_prefix_len = strlen(backup_ref_prefix);
10891 struct got_object_id *old_commit_id = NULL;
10892 char *branch_name = NULL;
10893 struct got_commit_object *old_commit = NULL;
10894 struct got_reflist_object_id_map *refs_idmap = NULL;
10895 int wanted_branch_found = 0;
10897 TAILQ_INIT(&refs);
10898 TAILQ_INIT(&backup_refs);
10900 err = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name, NULL);
10901 if (err)
10902 return err;
10904 err = got_reflist_object_id_map_create(&refs_idmap, &refs, repo);
10905 if (err)
10906 goto done;
10908 if (wanted_branch_name) {
10909 if (strncmp(wanted_branch_name, "refs/heads/", 11) == 0)
10910 wanted_branch_name += 11;
10913 err = got_ref_list(&backup_refs, repo, backup_ref_prefix,
10914 got_ref_cmp_by_commit_timestamp_descending, repo);
10915 if (err)
10916 goto done;
10918 TAILQ_FOREACH(re, &backup_refs, entry) {
10919 const char *refname = got_ref_get_name(re->ref);
10920 char *slash;
10922 err = check_cancelled(NULL);
10923 if (err)
10924 break;
10926 err = got_ref_resolve(&old_commit_id, repo, re->ref);
10927 if (err)
10928 break;
10930 err = got_object_open_as_commit(&old_commit, repo,
10931 old_commit_id);
10932 if (err)
10933 break;
10935 if (strncmp(backup_ref_prefix, refname,
10936 backup_ref_prefix_len) == 0)
10937 refname += backup_ref_prefix_len;
10939 while (refname[0] == '/')
10940 refname++;
10942 branch_name = strdup(refname);
10943 if (branch_name == NULL) {
10944 err = got_error_from_errno("strdup");
10945 break;
10947 slash = strrchr(branch_name, '/');
10948 if (slash) {
10949 *slash = '\0';
10950 refname += strlen(branch_name) + 1;
10953 if (wanted_branch_name == NULL ||
10954 strcmp(wanted_branch_name, branch_name) == 0) {
10955 wanted_branch_found = 1;
10956 if (delete) {
10957 err = delete_backup_ref(re->ref,
10958 old_commit_id, repo);
10959 } else {
10960 err = print_backup_ref(branch_name, refname,
10961 old_commit_id, old_commit, refs_idmap,
10962 repo);
10964 if (err)
10965 break;
10968 free(old_commit_id);
10969 old_commit_id = NULL;
10970 free(branch_name);
10971 branch_name = NULL;
10972 got_object_commit_close(old_commit);
10973 old_commit = NULL;
10976 if (wanted_branch_name && !wanted_branch_found) {
10977 err = got_error_fmt(GOT_ERR_NOT_REF,
10978 "%s/%s/", backup_ref_prefix, wanted_branch_name);
10980 done:
10981 if (refs_idmap)
10982 got_reflist_object_id_map_free(refs_idmap);
10983 got_ref_list_free(&refs);
10984 got_ref_list_free(&backup_refs);
10985 free(old_commit_id);
10986 free(branch_name);
10987 if (old_commit)
10988 got_object_commit_close(old_commit);
10989 return err;
10992 static const struct got_error *
10993 abort_progress(void *arg, unsigned char status, const char *path)
10996 * Unversioned files should not clutter progress output when
10997 * an operation is aborted.
10999 if (status == GOT_STATUS_UNVERSIONED)
11000 return NULL;
11002 return update_progress(arg, status, path);
11005 static const struct got_error *
11006 cmd_rebase(int argc, char *argv[])
11008 const struct got_error *error = NULL;
11009 struct got_worktree *worktree = NULL;
11010 struct got_repository *repo = NULL;
11011 struct got_fileindex *fileindex = NULL;
11012 char *cwd = NULL, *committer = NULL, *gitconfig_path = NULL;
11013 struct got_reference *branch = NULL;
11014 struct got_reference *new_base_branch = NULL, *tmp_branch = NULL;
11015 struct got_object_id *commit_id = NULL, *parent_id = NULL;
11016 struct got_object_id *resume_commit_id = NULL;
11017 struct got_object_id *branch_head_commit_id = NULL, *yca_id = NULL;
11018 struct got_object_id *head_commit_id = NULL;
11019 struct got_reference *head_ref = NULL;
11020 struct got_commit_object *commit = NULL;
11021 int ch, rebase_in_progress = 0, abort_rebase = 0, continue_rebase = 0;
11022 int histedit_in_progress = 0, merge_in_progress = 0;
11023 int create_backup = 1, list_backups = 0, delete_backups = 0;
11024 int allow_conflict = 0;
11025 struct got_object_id_queue commits;
11026 struct got_pathlist_head merged_paths;
11027 const struct got_object_id_queue *parent_ids;
11028 struct got_object_qid *qid, *pid;
11029 struct got_update_progress_arg upa;
11030 int *pack_fds = NULL;
11032 STAILQ_INIT(&commits);
11033 TAILQ_INIT(&merged_paths);
11034 memset(&upa, 0, sizeof(upa));
11036 #ifndef PROFILE
11037 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
11038 "unveil", NULL) == -1)
11039 err(1, "pledge");
11040 #endif
11042 while ((ch = getopt(argc, argv, "aCclX")) != -1) {
11043 switch (ch) {
11044 case 'a':
11045 abort_rebase = 1;
11046 break;
11047 case 'C':
11048 allow_conflict = 1;
11049 break;
11050 case 'c':
11051 continue_rebase = 1;
11052 break;
11053 case 'l':
11054 list_backups = 1;
11055 break;
11056 case 'X':
11057 delete_backups = 1;
11058 break;
11059 default:
11060 usage_rebase();
11061 /* NOTREACHED */
11065 argc -= optind;
11066 argv += optind;
11068 if (list_backups) {
11069 if (abort_rebase)
11070 option_conflict('l', 'a');
11071 if (allow_conflict)
11072 option_conflict('l', 'C');
11073 if (continue_rebase)
11074 option_conflict('l', 'c');
11075 if (delete_backups)
11076 option_conflict('l', 'X');
11077 if (argc != 0 && argc != 1)
11078 usage_rebase();
11079 } else if (delete_backups) {
11080 if (abort_rebase)
11081 option_conflict('X', 'a');
11082 if (allow_conflict)
11083 option_conflict('X', 'C');
11084 if (continue_rebase)
11085 option_conflict('X', 'c');
11086 if (list_backups)
11087 option_conflict('l', 'X');
11088 if (argc != 0 && argc != 1)
11089 usage_rebase();
11090 } else if (allow_conflict) {
11091 if (abort_rebase)
11092 option_conflict('C', 'a');
11093 if (!continue_rebase)
11094 errx(1, "-C option requires -c");
11095 } else {
11096 if (abort_rebase && continue_rebase)
11097 usage_rebase();
11098 else if (abort_rebase || continue_rebase) {
11099 if (argc != 0)
11100 usage_rebase();
11101 } else if (argc != 1)
11102 usage_rebase();
11105 cwd = getcwd(NULL, 0);
11106 if (cwd == NULL) {
11107 error = got_error_from_errno("getcwd");
11108 goto done;
11111 error = got_repo_pack_fds_open(&pack_fds);
11112 if (error != NULL)
11113 goto done;
11115 error = got_worktree_open(&worktree, cwd);
11116 if (error) {
11117 if (list_backups || delete_backups) {
11118 if (error->code != GOT_ERR_NOT_WORKTREE)
11119 goto done;
11120 } else {
11121 if (error->code == GOT_ERR_NOT_WORKTREE)
11122 error = wrap_not_worktree_error(error,
11123 "rebase", cwd);
11124 goto done;
11128 error = get_gitconfig_path(&gitconfig_path);
11129 if (error)
11130 goto done;
11131 error = got_repo_open(&repo,
11132 worktree ? got_worktree_get_repo_path(worktree) : cwd,
11133 gitconfig_path, pack_fds);
11134 if (error != NULL)
11135 goto done;
11137 if (worktree != NULL && !list_backups && !delete_backups) {
11138 error = worktree_has_logmsg_ref("rebase", worktree, repo);
11139 if (error)
11140 goto done;
11143 error = get_author(&committer, repo, worktree);
11144 if (error && error->code != GOT_ERR_COMMIT_NO_AUTHOR)
11145 goto done;
11147 error = apply_unveil(got_repo_get_path(repo), 0,
11148 worktree ? got_worktree_get_root_path(worktree) : NULL);
11149 if (error)
11150 goto done;
11152 if (list_backups || delete_backups) {
11153 error = process_backup_refs(
11154 GOT_WORKTREE_REBASE_BACKUP_REF_PREFIX,
11155 argc == 1 ? argv[0] : NULL, delete_backups, repo);
11156 goto done; /* nothing else to do */
11159 error = got_worktree_histedit_in_progress(&histedit_in_progress,
11160 worktree);
11161 if (error)
11162 goto done;
11163 if (histedit_in_progress) {
11164 error = got_error(GOT_ERR_HISTEDIT_BUSY);
11165 goto done;
11168 error = got_worktree_merge_in_progress(&merge_in_progress,
11169 worktree, repo);
11170 if (error)
11171 goto done;
11172 if (merge_in_progress) {
11173 error = got_error(GOT_ERR_MERGE_BUSY);
11174 goto done;
11177 error = got_worktree_rebase_in_progress(&rebase_in_progress, worktree);
11178 if (error)
11179 goto done;
11181 if (abort_rebase) {
11182 if (!rebase_in_progress) {
11183 error = got_error(GOT_ERR_NOT_REBASING);
11184 goto done;
11186 error = got_worktree_rebase_continue(&resume_commit_id,
11187 &new_base_branch, &tmp_branch, &branch, &fileindex,
11188 worktree, repo);
11189 if (error)
11190 goto done;
11191 printf("Switching work tree to %s\n",
11192 got_ref_get_symref_target(new_base_branch));
11193 error = got_worktree_rebase_abort(worktree, fileindex, repo,
11194 new_base_branch, abort_progress, &upa);
11195 if (error)
11196 goto done;
11197 printf("Rebase of %s aborted\n", got_ref_get_name(branch));
11198 print_merge_progress_stats(&upa);
11199 goto done; /* nothing else to do */
11202 if (continue_rebase) {
11203 if (!rebase_in_progress) {
11204 error = got_error(GOT_ERR_NOT_REBASING);
11205 goto done;
11207 error = got_worktree_rebase_continue(&resume_commit_id,
11208 &new_base_branch, &tmp_branch, &branch, &fileindex,
11209 worktree, repo);
11210 if (error)
11211 goto done;
11213 error = rebase_commit(NULL, worktree, fileindex, tmp_branch,
11214 committer, resume_commit_id, allow_conflict, repo);
11215 if (error)
11216 goto done;
11218 yca_id = got_object_id_dup(resume_commit_id);
11219 if (yca_id == NULL) {
11220 error = got_error_from_errno("got_object_id_dup");
11221 goto done;
11223 } else {
11224 error = got_ref_open(&branch, repo, argv[0], 0);
11225 if (error != NULL)
11226 goto done;
11227 if (strncmp(got_ref_get_name(branch), "refs/heads/", 11) != 0) {
11228 error = got_error_msg(GOT_ERR_COMMIT_BRANCH,
11229 "will not rebase a branch which lives outside "
11230 "the \"refs/heads/\" reference namespace");
11231 goto done;
11235 error = got_ref_resolve(&branch_head_commit_id, repo, branch);
11236 if (error)
11237 goto done;
11239 if (!continue_rebase) {
11240 struct got_object_id *base_commit_id;
11242 error = got_ref_open(&head_ref, repo,
11243 got_worktree_get_head_ref_name(worktree), 0);
11244 if (error)
11245 goto done;
11246 error = got_ref_resolve(&head_commit_id, repo, head_ref);
11247 if (error)
11248 goto done;
11249 base_commit_id = got_worktree_get_base_commit_id(worktree);
11250 if (got_object_id_cmp(base_commit_id, head_commit_id) != 0) {
11251 error = got_error(GOT_ERR_REBASE_OUT_OF_DATE);
11252 goto done;
11255 error = got_commit_graph_find_youngest_common_ancestor(&yca_id,
11256 base_commit_id, branch_head_commit_id, 1, repo,
11257 check_cancelled, NULL);
11258 if (error) {
11259 if (error->code == GOT_ERR_ANCESTRY) {
11260 error = got_error_msg(GOT_ERR_ANCESTRY,
11261 "specified branch shares no common "
11262 "ancestry with work tree's branch");
11264 goto done;
11267 error = check_same_branch(base_commit_id, branch, yca_id, repo);
11268 if (error) {
11269 if (error->code != GOT_ERR_ANCESTRY)
11270 goto done;
11271 error = NULL;
11272 } else {
11273 struct got_pathlist_head paths;
11274 printf("%s is already based on %s\n",
11275 got_ref_get_name(branch),
11276 got_worktree_get_head_ref_name(worktree));
11277 error = switch_head_ref(branch, branch_head_commit_id,
11278 worktree, repo);
11279 if (error)
11280 goto done;
11281 error = got_worktree_set_base_commit_id(worktree, repo,
11282 branch_head_commit_id);
11283 if (error)
11284 goto done;
11285 TAILQ_INIT(&paths);
11286 error = got_pathlist_append(&paths, "", NULL);
11287 if (error)
11288 goto done;
11289 error = got_worktree_checkout_files(worktree,
11290 &paths, repo, update_progress, &upa,
11291 check_cancelled, NULL);
11292 got_pathlist_free(&paths, GOT_PATHLIST_FREE_NONE);
11293 if (error)
11294 goto done;
11295 if (upa.did_something) {
11296 char *id_str;
11297 error = got_object_id_str(&id_str,
11298 branch_head_commit_id);
11299 if (error)
11300 goto done;
11301 printf("Updated to %s: %s\n",
11302 got_worktree_get_head_ref_name(worktree),
11303 id_str);
11304 free(id_str);
11305 } else
11306 printf("Already up-to-date\n");
11307 print_update_progress_stats(&upa);
11308 goto done;
11312 commit_id = branch_head_commit_id;
11313 error = got_object_open_as_commit(&commit, repo, commit_id);
11314 if (error)
11315 goto done;
11317 parent_ids = got_object_commit_get_parent_ids(commit);
11318 pid = STAILQ_FIRST(parent_ids);
11319 if (pid) {
11320 error = collect_commits(&commits, commit_id, &pid->id,
11321 yca_id, got_worktree_get_path_prefix(worktree),
11322 GOT_ERR_REBASE_PATH, repo);
11323 if (error)
11324 goto done;
11327 got_object_commit_close(commit);
11328 commit = NULL;
11330 if (!continue_rebase) {
11331 error = got_worktree_rebase_prepare(&new_base_branch,
11332 &tmp_branch, &fileindex, worktree, branch, repo);
11333 if (error)
11334 goto done;
11337 if (STAILQ_EMPTY(&commits)) {
11338 if (continue_rebase) {
11339 error = rebase_complete(worktree, fileindex,
11340 branch, tmp_branch, repo, create_backup);
11341 goto done;
11342 } else {
11343 /* Fast-forward the reference of the branch. */
11344 struct got_object_id *new_head_commit_id;
11345 char *id_str;
11346 error = got_ref_resolve(&new_head_commit_id, repo,
11347 new_base_branch);
11348 if (error)
11349 goto done;
11350 error = got_object_id_str(&id_str, new_head_commit_id);
11351 if (error)
11352 goto done;
11353 printf("Forwarding %s to commit %s\n",
11354 got_ref_get_name(branch), id_str);
11355 free(id_str);
11356 error = got_ref_change_ref(branch,
11357 new_head_commit_id);
11358 if (error)
11359 goto done;
11360 /* No backup needed since objects did not change. */
11361 create_backup = 0;
11365 pid = NULL;
11366 STAILQ_FOREACH(qid, &commits, entry) {
11368 commit_id = &qid->id;
11369 parent_id = pid ? &pid->id : yca_id;
11370 pid = qid;
11372 memset(&upa, 0, sizeof(upa));
11373 error = got_worktree_rebase_merge_files(&merged_paths,
11374 worktree, fileindex, parent_id, commit_id, repo,
11375 update_progress, &upa, check_cancelled, NULL);
11376 if (error)
11377 goto done;
11379 print_merge_progress_stats(&upa);
11380 if (upa.conflicts > 0 || upa.missing > 0 ||
11381 upa.not_deleted > 0 || upa.unversioned > 0) {
11382 if (upa.conflicts > 0) {
11383 error = show_rebase_merge_conflict(&qid->id,
11384 repo);
11385 if (error)
11386 goto done;
11388 got_pathlist_free(&merged_paths, GOT_PATHLIST_FREE_PATH);
11389 break;
11392 error = rebase_commit(&merged_paths, worktree, fileindex,
11393 tmp_branch, committer, commit_id, 0, repo);
11394 got_pathlist_free(&merged_paths, GOT_PATHLIST_FREE_PATH);
11395 if (error)
11396 goto done;
11399 if (upa.conflicts > 0 || upa.missing > 0 ||
11400 upa.not_deleted > 0 || upa.unversioned > 0) {
11401 error = got_worktree_rebase_postpone(worktree, fileindex);
11402 if (error)
11403 goto done;
11404 if (upa.conflicts > 0 && upa.missing == 0 &&
11405 upa.not_deleted == 0 && upa.unversioned == 0) {
11406 error = got_error_msg(GOT_ERR_CONFLICTS,
11407 "conflicts must be resolved before rebasing "
11408 "can continue");
11409 } else if (upa.conflicts > 0) {
11410 error = got_error_msg(GOT_ERR_CONFLICTS,
11411 "conflicts must be resolved before rebasing "
11412 "can continue; changes destined for some "
11413 "files were not yet merged and should be "
11414 "merged manually if required before the "
11415 "rebase operation is continued");
11416 } else {
11417 error = got_error_msg(GOT_ERR_CONFLICTS,
11418 "changes destined for some files were not "
11419 "yet merged and should be merged manually "
11420 "if required before the rebase operation "
11421 "is continued");
11423 } else
11424 error = rebase_complete(worktree, fileindex, branch,
11425 tmp_branch, repo, create_backup);
11426 done:
11427 free(cwd);
11428 free(committer);
11429 free(gitconfig_path);
11430 got_object_id_queue_free(&commits);
11431 free(branch_head_commit_id);
11432 free(resume_commit_id);
11433 free(head_commit_id);
11434 free(yca_id);
11435 if (commit)
11436 got_object_commit_close(commit);
11437 if (branch)
11438 got_ref_close(branch);
11439 if (new_base_branch)
11440 got_ref_close(new_base_branch);
11441 if (tmp_branch)
11442 got_ref_close(tmp_branch);
11443 if (head_ref)
11444 got_ref_close(head_ref);
11445 if (worktree)
11446 got_worktree_close(worktree);
11447 if (repo) {
11448 const struct got_error *close_err = got_repo_close(repo);
11449 if (error == NULL)
11450 error = close_err;
11452 if (pack_fds) {
11453 const struct got_error *pack_err =
11454 got_repo_pack_fds_close(pack_fds);
11455 if (error == NULL)
11456 error = pack_err;
11458 return error;
11461 __dead static void
11462 usage_histedit(void)
11464 fprintf(stderr, "usage: %s histedit [-aCcdeflmX] [-F histedit-script] "
11465 "[branch]\n", getprogname());
11466 exit(1);
11469 #define GOT_HISTEDIT_PICK 'p'
11470 #define GOT_HISTEDIT_EDIT 'e'
11471 #define GOT_HISTEDIT_FOLD 'f'
11472 #define GOT_HISTEDIT_DROP 'd'
11473 #define GOT_HISTEDIT_MESG 'm'
11475 static const struct got_histedit_cmd {
11476 unsigned char code;
11477 const char *name;
11478 const char *desc;
11479 } got_histedit_cmds[] = {
11480 { GOT_HISTEDIT_PICK, "pick", "use commit" },
11481 { GOT_HISTEDIT_EDIT, "edit", "use commit but stop for amending" },
11482 { GOT_HISTEDIT_FOLD, "fold", "combine with next commit that will "
11483 "be used" },
11484 { GOT_HISTEDIT_DROP, "drop", "remove commit from history" },
11485 { GOT_HISTEDIT_MESG, "mesg",
11486 "single-line log message for commit above (open editor if empty)" },
11489 struct got_histedit_list_entry {
11490 TAILQ_ENTRY(got_histedit_list_entry) entry;
11491 struct got_object_id *commit_id;
11492 const struct got_histedit_cmd *cmd;
11493 char *logmsg;
11495 TAILQ_HEAD(got_histedit_list, got_histedit_list_entry);
11497 static const struct got_error *
11498 histedit_write_commit(struct got_object_id *commit_id, const char *cmdname,
11499 FILE *f, struct got_repository *repo)
11501 const struct got_error *err = NULL;
11502 char *logmsg = NULL, *id_str = NULL;
11503 struct got_commit_object *commit = NULL;
11504 int n;
11506 err = got_object_open_as_commit(&commit, repo, commit_id);
11507 if (err)
11508 goto done;
11510 err = get_short_logmsg(&logmsg, 34, commit);
11511 if (err)
11512 goto done;
11514 err = got_object_id_str(&id_str, commit_id);
11515 if (err)
11516 goto done;
11518 n = fprintf(f, "%s %s %s\n", cmdname, id_str, logmsg);
11519 if (n < 0)
11520 err = got_ferror(f, GOT_ERR_IO);
11521 done:
11522 if (commit)
11523 got_object_commit_close(commit);
11524 free(id_str);
11525 free(logmsg);
11526 return err;
11529 static const struct got_error *
11530 histedit_write_commit_list(struct got_object_id_queue *commits,
11531 FILE *f, int edit_logmsg_only, int fold_only, int drop_only,
11532 int edit_only, struct got_repository *repo)
11534 const struct got_error *err = NULL;
11535 struct got_object_qid *qid;
11536 const char *histedit_cmd = NULL;
11538 if (STAILQ_EMPTY(commits))
11539 return got_error(GOT_ERR_EMPTY_HISTEDIT);
11541 STAILQ_FOREACH(qid, commits, entry) {
11542 histedit_cmd = got_histedit_cmds[0].name;
11543 if (drop_only)
11544 histedit_cmd = "drop";
11545 else if (edit_only)
11546 histedit_cmd = "edit";
11547 else if (fold_only && STAILQ_NEXT(qid, entry) != NULL)
11548 histedit_cmd = "fold";
11549 err = histedit_write_commit(&qid->id, histedit_cmd, f, repo);
11550 if (err)
11551 break;
11552 if (edit_logmsg_only) {
11553 int n = fprintf(f, "%c\n", GOT_HISTEDIT_MESG);
11554 if (n < 0) {
11555 err = got_ferror(f, GOT_ERR_IO);
11556 break;
11561 return err;
11564 static const struct got_error *
11565 write_cmd_list(FILE *f, const char *branch_name,
11566 struct got_object_id_queue *commits)
11568 const struct got_error *err = NULL;
11569 size_t i;
11570 int n;
11571 char *id_str;
11572 struct got_object_qid *qid;
11574 qid = STAILQ_FIRST(commits);
11575 err = got_object_id_str(&id_str, &qid->id);
11576 if (err)
11577 return err;
11579 n = fprintf(f,
11580 "# Editing the history of branch '%s' starting at\n"
11581 "# commit %s\n"
11582 "# Commits will be processed in order from top to "
11583 "bottom of this file.\n", branch_name, id_str);
11584 if (n < 0) {
11585 err = got_ferror(f, GOT_ERR_IO);
11586 goto done;
11589 n = fprintf(f, "# Available histedit commands:\n");
11590 if (n < 0) {
11591 err = got_ferror(f, GOT_ERR_IO);
11592 goto done;
11595 for (i = 0; i < nitems(got_histedit_cmds); i++) {
11596 const struct got_histedit_cmd *cmd = &got_histedit_cmds[i];
11597 n = fprintf(f, "# %s (%c): %s\n", cmd->name, cmd->code,
11598 cmd->desc);
11599 if (n < 0) {
11600 err = got_ferror(f, GOT_ERR_IO);
11601 break;
11604 done:
11605 free(id_str);
11606 return err;
11609 static const struct got_error *
11610 histedit_syntax_error(int lineno)
11612 static char msg[42];
11613 int ret;
11615 ret = snprintf(msg, sizeof(msg), "histedit syntax error on line %d",
11616 lineno);
11617 if (ret < 0 || (size_t)ret >= sizeof(msg))
11618 return got_error(GOT_ERR_HISTEDIT_SYNTAX);
11620 return got_error_msg(GOT_ERR_HISTEDIT_SYNTAX, msg);
11623 static const struct got_error *
11624 append_folded_commit_msg(char **new_msg, struct got_histedit_list_entry *hle,
11625 char *logmsg, struct got_repository *repo)
11627 const struct got_error *err;
11628 struct got_commit_object *folded_commit = NULL;
11629 char *id_str, *folded_logmsg = NULL;
11631 err = got_object_id_str(&id_str, hle->commit_id);
11632 if (err)
11633 return err;
11635 err = got_object_open_as_commit(&folded_commit, repo, hle->commit_id);
11636 if (err)
11637 goto done;
11639 err = got_object_commit_get_logmsg(&folded_logmsg, folded_commit);
11640 if (err)
11641 goto done;
11642 if (asprintf(new_msg, "%s%s# log message of folded commit %s: %s",
11643 logmsg ? logmsg : "", logmsg ? "\n" : "", id_str,
11644 folded_logmsg) == -1) {
11645 err = got_error_from_errno("asprintf");
11647 done:
11648 if (folded_commit)
11649 got_object_commit_close(folded_commit);
11650 free(id_str);
11651 free(folded_logmsg);
11652 return err;
11655 static struct got_histedit_list_entry *
11656 get_folded_commits(struct got_histedit_list_entry *hle)
11658 struct got_histedit_list_entry *prev, *folded = NULL;
11660 prev = TAILQ_PREV(hle, got_histedit_list, entry);
11661 while (prev && (prev->cmd->code == GOT_HISTEDIT_FOLD ||
11662 prev->cmd->code == GOT_HISTEDIT_DROP)) {
11663 if (prev->cmd->code == GOT_HISTEDIT_FOLD)
11664 folded = prev;
11665 prev = TAILQ_PREV(prev, got_histedit_list, entry);
11668 return folded;
11671 static const struct got_error *
11672 histedit_edit_logmsg(struct got_histedit_list_entry *hle,
11673 struct got_repository *repo)
11675 char *logmsg_path = NULL, *id_str = NULL, *orig_logmsg = NULL;
11676 char *logmsg = NULL, *new_msg = NULL, *editor = NULL;
11677 const struct got_error *err = NULL;
11678 struct got_commit_object *commit = NULL;
11679 int logmsg_len;
11680 int fd = -1;
11681 struct got_histedit_list_entry *folded = NULL;
11683 err = got_object_open_as_commit(&commit, repo, hle->commit_id);
11684 if (err)
11685 return err;
11687 folded = get_folded_commits(hle);
11688 if (folded) {
11689 while (folded != hle) {
11690 if (folded->cmd->code == GOT_HISTEDIT_DROP) {
11691 folded = TAILQ_NEXT(folded, entry);
11692 continue;
11694 err = append_folded_commit_msg(&new_msg, folded,
11695 logmsg, repo);
11696 if (err)
11697 goto done;
11698 free(logmsg);
11699 logmsg = new_msg;
11700 folded = TAILQ_NEXT(folded, entry);
11704 err = got_object_id_str(&id_str, hle->commit_id);
11705 if (err)
11706 goto done;
11707 err = got_object_commit_get_logmsg(&orig_logmsg, commit);
11708 if (err)
11709 goto done;
11710 logmsg_len = asprintf(&new_msg,
11711 "%s\n# original log message of commit %s: %s",
11712 logmsg ? logmsg : "", id_str, orig_logmsg);
11713 if (logmsg_len == -1) {
11714 err = got_error_from_errno("asprintf");
11715 goto done;
11717 free(logmsg);
11718 logmsg = new_msg;
11720 err = got_object_id_str(&id_str, hle->commit_id);
11721 if (err)
11722 goto done;
11724 err = got_opentemp_named_fd(&logmsg_path, &fd,
11725 GOT_TMPDIR_STR "/got-logmsg", "");
11726 if (err)
11727 goto done;
11729 if (write(fd, logmsg, logmsg_len) == -1) {
11730 err = got_error_from_errno2("write", logmsg_path);
11731 goto done;
11733 if (close(fd) == -1) {
11734 err = got_error_from_errno2("close", logmsg_path);
11735 goto done;
11737 fd = -1;
11739 err = get_editor(&editor);
11740 if (err)
11741 goto done;
11743 err = edit_logmsg(&hle->logmsg, editor, logmsg_path, logmsg,
11744 logmsg_len, 0);
11745 if (err) {
11746 if (err->code != GOT_ERR_COMMIT_MSG_EMPTY)
11747 goto done;
11748 err = NULL;
11749 hle->logmsg = strdup(new_msg);
11750 if (hle->logmsg == NULL)
11751 err = got_error_from_errno("strdup");
11753 done:
11754 if (fd != -1 && close(fd) == -1 && err == NULL)
11755 err = got_error_from_errno2("close", logmsg_path);
11756 if (logmsg_path && unlink(logmsg_path) != 0 && err == NULL)
11757 err = got_error_from_errno2("unlink", logmsg_path);
11758 free(logmsg_path);
11759 free(logmsg);
11760 free(orig_logmsg);
11761 free(editor);
11762 if (commit)
11763 got_object_commit_close(commit);
11764 return err;
11767 static const struct got_error *
11768 histedit_parse_list(struct got_histedit_list *histedit_cmds,
11769 FILE *f, struct got_repository *repo)
11771 const struct got_error *err = NULL;
11772 char *line = NULL, *p, *end;
11773 size_t i, linesize = 0;
11774 ssize_t linelen;
11775 int lineno = 0, lastcmd = -1;
11776 const struct got_histedit_cmd *cmd;
11777 struct got_object_id *commit_id = NULL;
11778 struct got_histedit_list_entry *hle = NULL;
11780 for (;;) {
11781 linelen = getline(&line, &linesize, f);
11782 if (linelen == -1) {
11783 const struct got_error *getline_err;
11784 if (feof(f))
11785 break;
11786 getline_err = got_error_from_errno("getline");
11787 err = got_ferror(f, getline_err->code);
11788 break;
11790 lineno++;
11791 p = line;
11792 while (isspace((unsigned char)p[0]))
11793 p++;
11794 if (p[0] == '#' || p[0] == '\0')
11795 continue;
11796 cmd = NULL;
11797 for (i = 0; i < nitems(got_histedit_cmds); i++) {
11798 cmd = &got_histedit_cmds[i];
11799 if (strncmp(cmd->name, p, strlen(cmd->name)) == 0 &&
11800 isspace((unsigned char)p[strlen(cmd->name)])) {
11801 p += strlen(cmd->name);
11802 break;
11804 if (p[0] == cmd->code && isspace((unsigned char)p[1])) {
11805 p++;
11806 break;
11809 if (i == nitems(got_histedit_cmds)) {
11810 err = histedit_syntax_error(lineno);
11811 break;
11813 while (isspace((unsigned char)p[0]))
11814 p++;
11815 if (cmd->code == GOT_HISTEDIT_MESG) {
11816 if (lastcmd != GOT_HISTEDIT_PICK &&
11817 lastcmd != GOT_HISTEDIT_EDIT) {
11818 err = got_error(GOT_ERR_HISTEDIT_CMD);
11819 break;
11821 if (p[0] == '\0') {
11822 err = histedit_edit_logmsg(hle, repo);
11823 if (err)
11824 break;
11825 } else {
11826 hle->logmsg = strdup(p);
11827 if (hle->logmsg == NULL) {
11828 err = got_error_from_errno("strdup");
11829 break;
11832 lastcmd = cmd->code;
11833 continue;
11834 } else {
11835 end = p;
11836 while (end[0] && !isspace((unsigned char)end[0]))
11837 end++;
11838 *end = '\0';
11840 err = got_object_resolve_id_str(&commit_id, repo, p);
11841 if (err) {
11842 /* override error code */
11843 err = histedit_syntax_error(lineno);
11844 break;
11847 hle = malloc(sizeof(*hle));
11848 if (hle == NULL) {
11849 err = got_error_from_errno("malloc");
11850 break;
11852 hle->cmd = cmd;
11853 hle->commit_id = commit_id;
11854 hle->logmsg = NULL;
11855 commit_id = NULL;
11856 TAILQ_INSERT_TAIL(histedit_cmds, hle, entry);
11857 lastcmd = cmd->code;
11860 free(line);
11861 free(commit_id);
11862 return err;
11865 static const struct got_error *
11866 histedit_check_script(struct got_histedit_list *histedit_cmds,
11867 struct got_object_id_queue *commits, struct got_repository *repo)
11869 const struct got_error *err = NULL;
11870 struct got_object_qid *qid;
11871 struct got_histedit_list_entry *hle;
11872 static char msg[92];
11873 char *id_str;
11875 if (TAILQ_EMPTY(histedit_cmds))
11876 return got_error_msg(GOT_ERR_EMPTY_HISTEDIT,
11877 "histedit script contains no commands");
11878 if (STAILQ_EMPTY(commits))
11879 return got_error(GOT_ERR_EMPTY_HISTEDIT);
11881 TAILQ_FOREACH(hle, histedit_cmds, entry) {
11882 struct got_histedit_list_entry *hle2;
11883 TAILQ_FOREACH(hle2, histedit_cmds, entry) {
11884 if (hle == hle2)
11885 continue;
11886 if (got_object_id_cmp(hle->commit_id,
11887 hle2->commit_id) != 0)
11888 continue;
11889 err = got_object_id_str(&id_str, hle->commit_id);
11890 if (err)
11891 return err;
11892 snprintf(msg, sizeof(msg), "commit %s is listed "
11893 "more than once in histedit script", id_str);
11894 free(id_str);
11895 return got_error_msg(GOT_ERR_HISTEDIT_CMD, msg);
11899 STAILQ_FOREACH(qid, commits, entry) {
11900 TAILQ_FOREACH(hle, histedit_cmds, entry) {
11901 if (got_object_id_cmp(&qid->id, hle->commit_id) == 0)
11902 break;
11904 if (hle == NULL) {
11905 err = got_object_id_str(&id_str, &qid->id);
11906 if (err)
11907 return err;
11908 snprintf(msg, sizeof(msg),
11909 "commit %s missing from histedit script", id_str);
11910 free(id_str);
11911 return got_error_msg(GOT_ERR_HISTEDIT_CMD, msg);
11915 hle = TAILQ_LAST(histedit_cmds, got_histedit_list);
11916 if (hle && hle->cmd->code == GOT_HISTEDIT_FOLD)
11917 return got_error_msg(GOT_ERR_HISTEDIT_CMD,
11918 "last commit in histedit script cannot be folded");
11920 return NULL;
11923 static const struct got_error *
11924 histedit_run_editor(struct got_histedit_list *histedit_cmds,
11925 const char *path, struct got_object_id_queue *commits,
11926 struct got_repository *repo)
11928 const struct got_error *err = NULL;
11929 char *editor;
11930 FILE *f = NULL;
11932 err = get_editor(&editor);
11933 if (err)
11934 return err;
11936 if (spawn_editor(editor, path) == -1) {
11937 err = got_error_from_errno("failed spawning editor");
11938 goto done;
11941 f = fopen(path, "re");
11942 if (f == NULL) {
11943 err = got_error_from_errno("fopen");
11944 goto done;
11946 err = histedit_parse_list(histedit_cmds, f, repo);
11947 if (err)
11948 goto done;
11950 err = histedit_check_script(histedit_cmds, commits, repo);
11951 done:
11952 if (f && fclose(f) == EOF && err == NULL)
11953 err = got_error_from_errno("fclose");
11954 free(editor);
11955 return err;
11958 static const struct got_error *
11959 histedit_edit_list_retry(struct got_histedit_list *, const struct got_error *,
11960 struct got_object_id_queue *, const char *, const char *,
11961 struct got_repository *);
11963 static const struct got_error *
11964 histedit_edit_script(struct got_histedit_list *histedit_cmds,
11965 struct got_object_id_queue *commits, const char *branch_name,
11966 int edit_logmsg_only, int fold_only, int drop_only, int edit_only,
11967 struct got_repository *repo)
11969 const struct got_error *err;
11970 FILE *f = NULL;
11971 char *path = NULL;
11973 err = got_opentemp_named(&path, &f, "got-histedit", "");
11974 if (err)
11975 return err;
11977 err = write_cmd_list(f, branch_name, commits);
11978 if (err)
11979 goto done;
11981 err = histedit_write_commit_list(commits, f, edit_logmsg_only,
11982 fold_only, drop_only, edit_only, repo);
11983 if (err)
11984 goto done;
11986 if (drop_only || edit_logmsg_only || fold_only || edit_only) {
11987 rewind(f);
11988 err = histedit_parse_list(histedit_cmds, f, repo);
11989 } else {
11990 if (fclose(f) == EOF) {
11991 err = got_error_from_errno("fclose");
11992 goto done;
11994 f = NULL;
11995 err = histedit_run_editor(histedit_cmds, path, commits, repo);
11996 if (err) {
11997 if (err->code != GOT_ERR_HISTEDIT_SYNTAX &&
11998 err->code != GOT_ERR_HISTEDIT_CMD)
11999 goto done;
12000 err = histedit_edit_list_retry(histedit_cmds, err,
12001 commits, path, branch_name, repo);
12004 done:
12005 if (f && fclose(f) == EOF && err == NULL)
12006 err = got_error_from_errno("fclose");
12007 if (path && unlink(path) != 0 && err == NULL)
12008 err = got_error_from_errno2("unlink", path);
12009 free(path);
12010 return err;
12013 static const struct got_error *
12014 histedit_save_list(struct got_histedit_list *histedit_cmds,
12015 struct got_worktree *worktree, struct got_repository *repo)
12017 const struct got_error *err = NULL;
12018 char *path = NULL;
12019 FILE *f = NULL;
12020 struct got_histedit_list_entry *hle;
12021 struct got_commit_object *commit = NULL;
12023 err = got_worktree_get_histedit_script_path(&path, worktree);
12024 if (err)
12025 return err;
12027 f = fopen(path, "we");
12028 if (f == NULL) {
12029 err = got_error_from_errno2("fopen", path);
12030 goto done;
12032 TAILQ_FOREACH(hle, histedit_cmds, entry) {
12033 err = histedit_write_commit(hle->commit_id, hle->cmd->name, f,
12034 repo);
12035 if (err)
12036 break;
12038 if (hle->logmsg) {
12039 int n = fprintf(f, "%c %s\n",
12040 GOT_HISTEDIT_MESG, hle->logmsg);
12041 if (n < 0) {
12042 err = got_ferror(f, GOT_ERR_IO);
12043 break;
12047 done:
12048 if (f && fclose(f) == EOF && err == NULL)
12049 err = got_error_from_errno("fclose");
12050 free(path);
12051 if (commit)
12052 got_object_commit_close(commit);
12053 return err;
12056 static void
12057 histedit_free_list(struct got_histedit_list *histedit_cmds)
12059 struct got_histedit_list_entry *hle;
12061 while ((hle = TAILQ_FIRST(histedit_cmds))) {
12062 TAILQ_REMOVE(histedit_cmds, hle, entry);
12063 free(hle);
12067 static const struct got_error *
12068 histedit_load_list(struct got_histedit_list *histedit_cmds,
12069 const char *path, struct got_repository *repo)
12071 const struct got_error *err = NULL;
12072 FILE *f = NULL;
12074 f = fopen(path, "re");
12075 if (f == NULL) {
12076 err = got_error_from_errno2("fopen", path);
12077 goto done;
12080 err = histedit_parse_list(histedit_cmds, f, repo);
12081 done:
12082 if (f && fclose(f) == EOF && err == NULL)
12083 err = got_error_from_errno("fclose");
12084 return err;
12087 static const struct got_error *
12088 histedit_edit_list_retry(struct got_histedit_list *histedit_cmds,
12089 const struct got_error *edit_err, struct got_object_id_queue *commits,
12090 const char *path, const char *branch_name, struct got_repository *repo)
12092 const struct got_error *err = NULL, *prev_err = edit_err;
12093 int resp = ' ';
12095 while (resp != 'c' && resp != 'r' && resp != 'a') {
12096 printf("%s: %s\n(c)ontinue editing, (r)estart editing, "
12097 "or (a)bort: ", getprogname(), prev_err->msg);
12098 resp = getchar();
12099 if (resp == '\n')
12100 resp = getchar();
12101 if (resp == 'c') {
12102 histedit_free_list(histedit_cmds);
12103 err = histedit_run_editor(histedit_cmds, path, commits,
12104 repo);
12105 if (err) {
12106 if (err->code != GOT_ERR_HISTEDIT_SYNTAX &&
12107 err->code != GOT_ERR_HISTEDIT_CMD)
12108 break;
12109 prev_err = err;
12110 resp = ' ';
12111 continue;
12113 break;
12114 } else if (resp == 'r') {
12115 histedit_free_list(histedit_cmds);
12116 err = histedit_edit_script(histedit_cmds,
12117 commits, branch_name, 0, 0, 0, 0, repo);
12118 if (err) {
12119 if (err->code != GOT_ERR_HISTEDIT_SYNTAX &&
12120 err->code != GOT_ERR_HISTEDIT_CMD)
12121 break;
12122 prev_err = err;
12123 resp = ' ';
12124 continue;
12126 break;
12127 } else if (resp == 'a') {
12128 err = got_error(GOT_ERR_HISTEDIT_CANCEL);
12129 break;
12130 } else
12131 printf("invalid response '%c'\n", resp);
12134 return err;
12137 static const struct got_error *
12138 histedit_complete(struct got_worktree *worktree,
12139 struct got_fileindex *fileindex, struct got_reference *tmp_branch,
12140 struct got_reference *branch, struct got_repository *repo)
12142 printf("Switching work tree to %s\n",
12143 got_ref_get_symref_target(branch));
12144 return got_worktree_histedit_complete(worktree, fileindex, tmp_branch,
12145 branch, repo);
12148 static const struct got_error *
12149 show_histedit_progress(struct got_commit_object *commit,
12150 struct got_histedit_list_entry *hle, struct got_object_id *new_id)
12152 const struct got_error *err;
12153 char *old_id_str = NULL, *new_id_str = NULL, *logmsg = NULL;
12155 err = got_object_id_str(&old_id_str, hle->commit_id);
12156 if (err)
12157 goto done;
12159 if (new_id) {
12160 err = got_object_id_str(&new_id_str, new_id);
12161 if (err)
12162 goto done;
12165 old_id_str[12] = '\0';
12166 if (new_id_str)
12167 new_id_str[12] = '\0';
12169 if (hle->logmsg) {
12170 logmsg = strdup(hle->logmsg);
12171 if (logmsg == NULL) {
12172 err = got_error_from_errno("strdup");
12173 goto done;
12175 trim_logmsg(logmsg, 42);
12176 } else {
12177 err = get_short_logmsg(&logmsg, 42, commit);
12178 if (err)
12179 goto done;
12182 switch (hle->cmd->code) {
12183 case GOT_HISTEDIT_PICK:
12184 case GOT_HISTEDIT_EDIT:
12185 printf("%s -> %s: %s\n", old_id_str,
12186 new_id_str ? new_id_str : "no-op change", logmsg);
12187 break;
12188 case GOT_HISTEDIT_DROP:
12189 case GOT_HISTEDIT_FOLD:
12190 printf("%s -> %s commit: %s\n", old_id_str, hle->cmd->name,
12191 logmsg);
12192 break;
12193 default:
12194 break;
12196 done:
12197 free(old_id_str);
12198 free(new_id_str);
12199 return err;
12202 static const struct got_error *
12203 histedit_commit(struct got_pathlist_head *merged_paths,
12204 struct got_worktree *worktree, struct got_fileindex *fileindex,
12205 struct got_reference *tmp_branch, struct got_histedit_list_entry *hle,
12206 const char *committer, int allow_conflict, struct got_repository *repo)
12208 const struct got_error *err;
12209 struct got_commit_object *commit;
12210 struct got_object_id *new_commit_id;
12212 if ((hle->cmd->code == GOT_HISTEDIT_EDIT || get_folded_commits(hle))
12213 && hle->logmsg == NULL) {
12214 err = histedit_edit_logmsg(hle, repo);
12215 if (err)
12216 return err;
12219 err = got_object_open_as_commit(&commit, repo, hle->commit_id);
12220 if (err)
12221 return err;
12223 err = got_worktree_histedit_commit(&new_commit_id, merged_paths,
12224 worktree, fileindex, tmp_branch, committer, commit, hle->commit_id,
12225 hle->logmsg, allow_conflict, repo);
12226 if (err) {
12227 if (err->code != GOT_ERR_COMMIT_NO_CHANGES)
12228 goto done;
12229 err = show_histedit_progress(commit, hle, NULL);
12230 } else {
12231 err = show_histedit_progress(commit, hle, new_commit_id);
12232 free(new_commit_id);
12234 done:
12235 got_object_commit_close(commit);
12236 return err;
12239 static const struct got_error *
12240 histedit_skip_commit(struct got_histedit_list_entry *hle,
12241 struct got_worktree *worktree, struct got_repository *repo)
12243 const struct got_error *error;
12244 struct got_commit_object *commit;
12246 error = got_worktree_histedit_skip_commit(worktree, hle->commit_id,
12247 repo);
12248 if (error)
12249 return error;
12251 error = got_object_open_as_commit(&commit, repo, hle->commit_id);
12252 if (error)
12253 return error;
12255 error = show_histedit_progress(commit, hle, NULL);
12256 got_object_commit_close(commit);
12257 return error;
12260 static const struct got_error *
12261 check_local_changes(void *arg, unsigned char status,
12262 unsigned char staged_status, const char *path,
12263 struct got_object_id *blob_id, struct got_object_id *staged_blob_id,
12264 struct got_object_id *commit_id, int dirfd, const char *de_name)
12266 int *have_local_changes = arg;
12268 switch (status) {
12269 case GOT_STATUS_ADD:
12270 case GOT_STATUS_DELETE:
12271 case GOT_STATUS_MODIFY:
12272 case GOT_STATUS_CONFLICT:
12273 *have_local_changes = 1;
12274 return got_error(GOT_ERR_CANCELLED);
12275 default:
12276 break;
12279 switch (staged_status) {
12280 case GOT_STATUS_ADD:
12281 case GOT_STATUS_DELETE:
12282 case GOT_STATUS_MODIFY:
12283 *have_local_changes = 1;
12284 return got_error(GOT_ERR_CANCELLED);
12285 default:
12286 break;
12289 return NULL;
12292 static const struct got_error *
12293 cmd_histedit(int argc, char *argv[])
12295 const struct got_error *error = NULL;
12296 struct got_worktree *worktree = NULL;
12297 struct got_fileindex *fileindex = NULL;
12298 struct got_repository *repo = NULL;
12299 char *cwd = NULL, *committer = NULL, *gitconfig_path = NULL;
12300 struct got_reference *branch = NULL;
12301 struct got_reference *tmp_branch = NULL;
12302 struct got_object_id *resume_commit_id = NULL;
12303 struct got_object_id *base_commit_id = NULL;
12304 struct got_object_id *head_commit_id = NULL;
12305 struct got_commit_object *commit = NULL;
12306 int ch, rebase_in_progress = 0, merge_in_progress = 0;
12307 struct got_update_progress_arg upa;
12308 int edit_in_progress = 0, abort_edit = 0, continue_edit = 0;
12309 int drop_only = 0, edit_logmsg_only = 0, fold_only = 0, edit_only = 0;
12310 int allow_conflict = 0, list_backups = 0, delete_backups = 0;
12311 const char *edit_script_path = NULL;
12312 struct got_object_id_queue commits;
12313 struct got_pathlist_head merged_paths;
12314 const struct got_object_id_queue *parent_ids;
12315 struct got_object_qid *pid;
12316 struct got_histedit_list histedit_cmds;
12317 struct got_histedit_list_entry *hle;
12318 int *pack_fds = NULL;
12320 STAILQ_INIT(&commits);
12321 TAILQ_INIT(&histedit_cmds);
12322 TAILQ_INIT(&merged_paths);
12323 memset(&upa, 0, sizeof(upa));
12325 #ifndef PROFILE
12326 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
12327 "unveil", NULL) == -1)
12328 err(1, "pledge");
12329 #endif
12331 while ((ch = getopt(argc, argv, "aCcdeF:flmX")) != -1) {
12332 switch (ch) {
12333 case 'a':
12334 abort_edit = 1;
12335 break;
12336 case 'C':
12337 allow_conflict = 1;
12338 break;
12339 case 'c':
12340 continue_edit = 1;
12341 break;
12342 case 'd':
12343 drop_only = 1;
12344 break;
12345 case 'e':
12346 edit_only = 1;
12347 break;
12348 case 'F':
12349 edit_script_path = optarg;
12350 break;
12351 case 'f':
12352 fold_only = 1;
12353 break;
12354 case 'l':
12355 list_backups = 1;
12356 break;
12357 case 'm':
12358 edit_logmsg_only = 1;
12359 break;
12360 case 'X':
12361 delete_backups = 1;
12362 break;
12363 default:
12364 usage_histedit();
12365 /* NOTREACHED */
12369 argc -= optind;
12370 argv += optind;
12372 if (abort_edit && allow_conflict)
12373 option_conflict('a', 'C');
12374 if (abort_edit && continue_edit)
12375 option_conflict('a', 'c');
12376 if (edit_script_path && allow_conflict)
12377 option_conflict('F', 'C');
12378 if (edit_script_path && edit_logmsg_only)
12379 option_conflict('F', 'm');
12380 if (abort_edit && edit_logmsg_only)
12381 option_conflict('a', 'm');
12382 if (edit_logmsg_only && allow_conflict)
12383 option_conflict('m', 'C');
12384 if (continue_edit && edit_logmsg_only)
12385 option_conflict('c', 'm');
12386 if (abort_edit && fold_only)
12387 option_conflict('a', 'f');
12388 if (fold_only && allow_conflict)
12389 option_conflict('f', 'C');
12390 if (continue_edit && fold_only)
12391 option_conflict('c', 'f');
12392 if (fold_only && edit_logmsg_only)
12393 option_conflict('f', 'm');
12394 if (edit_script_path && fold_only)
12395 option_conflict('F', 'f');
12396 if (abort_edit && edit_only)
12397 option_conflict('a', 'e');
12398 if (continue_edit && edit_only)
12399 option_conflict('c', 'e');
12400 if (edit_only && edit_logmsg_only)
12401 option_conflict('e', 'm');
12402 if (edit_script_path && edit_only)
12403 option_conflict('F', 'e');
12404 if (fold_only && edit_only)
12405 option_conflict('f', 'e');
12406 if (drop_only && abort_edit)
12407 option_conflict('d', 'a');
12408 if (drop_only && allow_conflict)
12409 option_conflict('d', 'C');
12410 if (drop_only && continue_edit)
12411 option_conflict('d', 'c');
12412 if (drop_only && edit_logmsg_only)
12413 option_conflict('d', 'm');
12414 if (drop_only && edit_only)
12415 option_conflict('d', 'e');
12416 if (drop_only && edit_script_path)
12417 option_conflict('d', 'F');
12418 if (drop_only && fold_only)
12419 option_conflict('d', 'f');
12420 if (list_backups) {
12421 if (abort_edit)
12422 option_conflict('l', 'a');
12423 if (allow_conflict)
12424 option_conflict('l', 'C');
12425 if (continue_edit)
12426 option_conflict('l', 'c');
12427 if (edit_script_path)
12428 option_conflict('l', 'F');
12429 if (edit_logmsg_only)
12430 option_conflict('l', 'm');
12431 if (drop_only)
12432 option_conflict('l', 'd');
12433 if (fold_only)
12434 option_conflict('l', 'f');
12435 if (edit_only)
12436 option_conflict('l', 'e');
12437 if (delete_backups)
12438 option_conflict('l', 'X');
12439 if (argc != 0 && argc != 1)
12440 usage_histedit();
12441 } else if (delete_backups) {
12442 if (abort_edit)
12443 option_conflict('X', 'a');
12444 if (allow_conflict)
12445 option_conflict('X', 'C');
12446 if (continue_edit)
12447 option_conflict('X', 'c');
12448 if (drop_only)
12449 option_conflict('X', 'd');
12450 if (edit_script_path)
12451 option_conflict('X', 'F');
12452 if (edit_logmsg_only)
12453 option_conflict('X', 'm');
12454 if (fold_only)
12455 option_conflict('X', 'f');
12456 if (edit_only)
12457 option_conflict('X', 'e');
12458 if (list_backups)
12459 option_conflict('X', 'l');
12460 if (argc != 0 && argc != 1)
12461 usage_histedit();
12462 } else if (allow_conflict && !continue_edit)
12463 errx(1, "-C option requires -c");
12464 else if (argc != 0)
12465 usage_histedit();
12468 * This command cannot apply unveil(2) in all cases because the
12469 * user may choose to run an editor to edit the histedit script
12470 * and to edit individual commit log messages.
12471 * unveil(2) traverses exec(2); if an editor is used we have to
12472 * apply unveil after edit script and log messages have been written.
12473 * XXX TODO: Make use of unveil(2) where possible.
12476 cwd = getcwd(NULL, 0);
12477 if (cwd == NULL) {
12478 error = got_error_from_errno("getcwd");
12479 goto done;
12482 error = got_repo_pack_fds_open(&pack_fds);
12483 if (error != NULL)
12484 goto done;
12486 error = got_worktree_open(&worktree, cwd);
12487 if (error) {
12488 if (list_backups || delete_backups) {
12489 if (error->code != GOT_ERR_NOT_WORKTREE)
12490 goto done;
12491 } else {
12492 if (error->code == GOT_ERR_NOT_WORKTREE)
12493 error = wrap_not_worktree_error(error,
12494 "histedit", cwd);
12495 goto done;
12499 if (list_backups || delete_backups) {
12500 error = got_repo_open(&repo,
12501 worktree ? got_worktree_get_repo_path(worktree) : cwd,
12502 NULL, pack_fds);
12503 if (error != NULL)
12504 goto done;
12505 error = apply_unveil(got_repo_get_path(repo), 0,
12506 worktree ? got_worktree_get_root_path(worktree) : NULL);
12507 if (error)
12508 goto done;
12509 error = process_backup_refs(
12510 GOT_WORKTREE_HISTEDIT_BACKUP_REF_PREFIX,
12511 argc == 1 ? argv[0] : NULL, delete_backups, repo);
12512 goto done; /* nothing else to do */
12515 error = get_gitconfig_path(&gitconfig_path);
12516 if (error)
12517 goto done;
12518 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
12519 gitconfig_path, pack_fds);
12520 if (error != NULL)
12521 goto done;
12523 if (worktree != NULL && !list_backups && !delete_backups) {
12524 error = worktree_has_logmsg_ref("histedit", worktree, repo);
12525 if (error)
12526 goto done;
12529 error = got_worktree_rebase_in_progress(&rebase_in_progress, worktree);
12530 if (error)
12531 goto done;
12532 if (rebase_in_progress) {
12533 error = got_error(GOT_ERR_REBASING);
12534 goto done;
12537 error = got_worktree_merge_in_progress(&merge_in_progress, worktree,
12538 repo);
12539 if (error)
12540 goto done;
12541 if (merge_in_progress) {
12542 error = got_error(GOT_ERR_MERGE_BUSY);
12543 goto done;
12546 error = got_worktree_histedit_in_progress(&edit_in_progress, worktree);
12547 if (error)
12548 goto done;
12550 if (edit_in_progress && edit_logmsg_only) {
12551 error = got_error_msg(GOT_ERR_HISTEDIT_BUSY,
12552 "histedit operation is in progress in this "
12553 "work tree and must be continued or aborted "
12554 "before the -m option can be used");
12555 goto done;
12557 if (edit_in_progress && drop_only) {
12558 error = got_error_msg(GOT_ERR_HISTEDIT_BUSY,
12559 "histedit operation is in progress in this "
12560 "work tree and must be continued or aborted "
12561 "before the -d option can be used");
12562 goto done;
12564 if (edit_in_progress && fold_only) {
12565 error = got_error_msg(GOT_ERR_HISTEDIT_BUSY,
12566 "histedit operation is in progress in this "
12567 "work tree and must be continued or aborted "
12568 "before the -f option can be used");
12569 goto done;
12571 if (edit_in_progress && edit_only) {
12572 error = got_error_msg(GOT_ERR_HISTEDIT_BUSY,
12573 "histedit operation is in progress in this "
12574 "work tree and must be continued or aborted "
12575 "before the -e option can be used");
12576 goto done;
12579 if (edit_in_progress && abort_edit) {
12580 error = got_worktree_histedit_continue(&resume_commit_id,
12581 &tmp_branch, &branch, &base_commit_id, &fileindex,
12582 worktree, repo);
12583 if (error)
12584 goto done;
12585 printf("Switching work tree to %s\n",
12586 got_ref_get_symref_target(branch));
12587 error = got_worktree_histedit_abort(worktree, fileindex, repo,
12588 branch, base_commit_id, abort_progress, &upa);
12589 if (error)
12590 goto done;
12591 printf("Histedit of %s aborted\n",
12592 got_ref_get_symref_target(branch));
12593 print_merge_progress_stats(&upa);
12594 goto done; /* nothing else to do */
12595 } else if (abort_edit) {
12596 error = got_error(GOT_ERR_NOT_HISTEDIT);
12597 goto done;
12600 error = get_author(&committer, repo, worktree);
12601 if (error)
12602 goto done;
12604 if (continue_edit) {
12605 char *path;
12607 if (!edit_in_progress) {
12608 error = got_error(GOT_ERR_NOT_HISTEDIT);
12609 goto done;
12612 error = got_worktree_get_histedit_script_path(&path, worktree);
12613 if (error)
12614 goto done;
12616 error = histedit_load_list(&histedit_cmds, path, repo);
12617 free(path);
12618 if (error)
12619 goto done;
12621 error = got_worktree_histedit_continue(&resume_commit_id,
12622 &tmp_branch, &branch, &base_commit_id, &fileindex,
12623 worktree, repo);
12624 if (error)
12625 goto done;
12627 error = got_ref_resolve(&head_commit_id, repo, branch);
12628 if (error)
12629 goto done;
12631 error = got_object_open_as_commit(&commit, repo,
12632 head_commit_id);
12633 if (error)
12634 goto done;
12635 parent_ids = got_object_commit_get_parent_ids(commit);
12636 pid = STAILQ_FIRST(parent_ids);
12637 if (pid == NULL) {
12638 error = got_error(GOT_ERR_EMPTY_HISTEDIT);
12639 goto done;
12641 error = collect_commits(&commits, head_commit_id, &pid->id,
12642 base_commit_id, got_worktree_get_path_prefix(worktree),
12643 GOT_ERR_HISTEDIT_PATH, repo);
12644 got_object_commit_close(commit);
12645 commit = NULL;
12646 if (error)
12647 goto done;
12648 } else {
12649 if (edit_in_progress) {
12650 error = got_error(GOT_ERR_HISTEDIT_BUSY);
12651 goto done;
12654 error = got_ref_open(&branch, repo,
12655 got_worktree_get_head_ref_name(worktree), 0);
12656 if (error != NULL)
12657 goto done;
12659 if (strncmp(got_ref_get_name(branch), "refs/heads/", 11) != 0) {
12660 error = got_error_msg(GOT_ERR_COMMIT_BRANCH,
12661 "will not edit commit history of a branch outside "
12662 "the \"refs/heads/\" reference namespace");
12663 goto done;
12666 error = got_ref_resolve(&head_commit_id, repo, branch);
12667 got_ref_close(branch);
12668 branch = NULL;
12669 if (error)
12670 goto done;
12672 error = got_object_open_as_commit(&commit, repo,
12673 head_commit_id);
12674 if (error)
12675 goto done;
12676 parent_ids = got_object_commit_get_parent_ids(commit);
12677 pid = STAILQ_FIRST(parent_ids);
12678 if (pid == NULL) {
12679 error = got_error(GOT_ERR_EMPTY_HISTEDIT);
12680 goto done;
12682 error = collect_commits(&commits, head_commit_id, &pid->id,
12683 got_worktree_get_base_commit_id(worktree),
12684 got_worktree_get_path_prefix(worktree),
12685 GOT_ERR_HISTEDIT_PATH, repo);
12686 got_object_commit_close(commit);
12687 commit = NULL;
12688 if (error)
12689 goto done;
12691 if (STAILQ_EMPTY(&commits)) {
12692 error = got_error(GOT_ERR_EMPTY_HISTEDIT);
12693 goto done;
12696 error = got_worktree_histedit_prepare(&tmp_branch, &branch,
12697 &base_commit_id, &fileindex, worktree, repo);
12698 if (error)
12699 goto done;
12701 if (edit_script_path) {
12702 error = histedit_load_list(&histedit_cmds,
12703 edit_script_path, repo);
12704 if (error) {
12705 got_worktree_histedit_abort(worktree, fileindex,
12706 repo, branch, base_commit_id,
12707 abort_progress, &upa);
12708 print_merge_progress_stats(&upa);
12709 goto done;
12711 } else {
12712 const char *branch_name;
12713 branch_name = got_ref_get_symref_target(branch);
12714 if (strncmp(branch_name, "refs/heads/", 11) == 0)
12715 branch_name += 11;
12716 error = histedit_edit_script(&histedit_cmds, &commits,
12717 branch_name, edit_logmsg_only, fold_only,
12718 drop_only, edit_only, repo);
12719 if (error) {
12720 got_worktree_histedit_abort(worktree, fileindex,
12721 repo, branch, base_commit_id,
12722 abort_progress, &upa);
12723 print_merge_progress_stats(&upa);
12724 goto done;
12729 error = histedit_save_list(&histedit_cmds, worktree,
12730 repo);
12731 if (error) {
12732 got_worktree_histedit_abort(worktree, fileindex,
12733 repo, branch, base_commit_id,
12734 abort_progress, &upa);
12735 print_merge_progress_stats(&upa);
12736 goto done;
12741 error = histedit_check_script(&histedit_cmds, &commits, repo);
12742 if (error)
12743 goto done;
12745 TAILQ_FOREACH(hle, &histedit_cmds, entry) {
12746 if (resume_commit_id) {
12747 if (got_object_id_cmp(hle->commit_id,
12748 resume_commit_id) != 0)
12749 continue;
12751 resume_commit_id = NULL;
12752 if (hle->cmd->code == GOT_HISTEDIT_DROP ||
12753 hle->cmd->code == GOT_HISTEDIT_FOLD) {
12754 error = histedit_skip_commit(hle, worktree,
12755 repo);
12756 if (error)
12757 goto done;
12758 } else {
12759 struct got_pathlist_head paths;
12760 int have_changes = 0;
12762 TAILQ_INIT(&paths);
12763 error = got_pathlist_append(&paths, "", NULL);
12764 if (error)
12765 goto done;
12766 error = got_worktree_status(worktree, &paths,
12767 repo, 0, check_local_changes, &have_changes,
12768 check_cancelled, NULL);
12769 got_pathlist_free(&paths,
12770 GOT_PATHLIST_FREE_NONE);
12771 if (error) {
12772 if (error->code != GOT_ERR_CANCELLED)
12773 goto done;
12774 if (sigint_received || sigpipe_received)
12775 goto done;
12777 if (have_changes) {
12778 error = histedit_commit(NULL, worktree,
12779 fileindex, tmp_branch, hle,
12780 committer, allow_conflict, repo);
12781 if (error)
12782 goto done;
12783 } else {
12784 error = got_object_open_as_commit(
12785 &commit, repo, hle->commit_id);
12786 if (error)
12787 goto done;
12788 error = show_histedit_progress(commit,
12789 hle, NULL);
12790 got_object_commit_close(commit);
12791 commit = NULL;
12792 if (error)
12793 goto done;
12796 continue;
12799 if (hle->cmd->code == GOT_HISTEDIT_DROP) {
12800 error = histedit_skip_commit(hle, worktree, repo);
12801 if (error)
12802 goto done;
12803 continue;
12806 error = got_object_open_as_commit(&commit, repo,
12807 hle->commit_id);
12808 if (error)
12809 goto done;
12810 parent_ids = got_object_commit_get_parent_ids(commit);
12811 pid = STAILQ_FIRST(parent_ids);
12813 error = got_worktree_histedit_merge_files(&merged_paths,
12814 worktree, fileindex, &pid->id, hle->commit_id, repo,
12815 update_progress, &upa, check_cancelled, NULL);
12816 if (error)
12817 goto done;
12818 got_object_commit_close(commit);
12819 commit = NULL;
12821 print_merge_progress_stats(&upa);
12822 if (upa.conflicts > 0 || upa.missing > 0 ||
12823 upa.not_deleted > 0 || upa.unversioned > 0) {
12824 if (upa.conflicts > 0) {
12825 error = show_rebase_merge_conflict(
12826 hle->commit_id, repo);
12827 if (error)
12828 goto done;
12830 got_pathlist_free(&merged_paths, GOT_PATHLIST_FREE_PATH);
12831 break;
12834 if (hle->cmd->code == GOT_HISTEDIT_EDIT) {
12835 char *id_str;
12836 error = got_object_id_str(&id_str, hle->commit_id);
12837 if (error)
12838 goto done;
12839 printf("Stopping histedit for amending commit %s\n",
12840 id_str);
12841 free(id_str);
12842 got_pathlist_free(&merged_paths, GOT_PATHLIST_FREE_PATH);
12843 error = got_worktree_histedit_postpone(worktree,
12844 fileindex);
12845 goto done;
12848 if (hle->cmd->code == GOT_HISTEDIT_FOLD) {
12849 error = histedit_skip_commit(hle, worktree, repo);
12850 if (error)
12851 goto done;
12852 continue;
12855 error = histedit_commit(&merged_paths, worktree, fileindex,
12856 tmp_branch, hle, committer, allow_conflict, repo);
12857 got_pathlist_free(&merged_paths, GOT_PATHLIST_FREE_PATH);
12858 if (error)
12859 goto done;
12862 if (upa.conflicts > 0 || upa.missing > 0 ||
12863 upa.not_deleted > 0 || upa.unversioned > 0) {
12864 error = got_worktree_histedit_postpone(worktree, fileindex);
12865 if (error)
12866 goto done;
12867 if (upa.conflicts > 0 && upa.missing == 0 &&
12868 upa.not_deleted == 0 && upa.unversioned == 0) {
12869 error = got_error_msg(GOT_ERR_CONFLICTS,
12870 "conflicts must be resolved before histedit "
12871 "can continue");
12872 } else if (upa.conflicts > 0) {
12873 error = got_error_msg(GOT_ERR_CONFLICTS,
12874 "conflicts must be resolved before histedit "
12875 "can continue; changes destined for some "
12876 "files were not yet merged and should be "
12877 "merged manually if required before the "
12878 "histedit operation is continued");
12879 } else {
12880 error = got_error_msg(GOT_ERR_CONFLICTS,
12881 "changes destined for some files were not "
12882 "yet merged and should be merged manually "
12883 "if required before the histedit operation "
12884 "is continued");
12886 } else
12887 error = histedit_complete(worktree, fileindex, tmp_branch,
12888 branch, repo);
12889 done:
12890 free(cwd);
12891 free(committer);
12892 free(gitconfig_path);
12893 got_object_id_queue_free(&commits);
12894 histedit_free_list(&histedit_cmds);
12895 free(head_commit_id);
12896 free(base_commit_id);
12897 free(resume_commit_id);
12898 if (commit)
12899 got_object_commit_close(commit);
12900 if (branch)
12901 got_ref_close(branch);
12902 if (tmp_branch)
12903 got_ref_close(tmp_branch);
12904 if (worktree)
12905 got_worktree_close(worktree);
12906 if (repo) {
12907 const struct got_error *close_err = got_repo_close(repo);
12908 if (error == NULL)
12909 error = close_err;
12911 if (pack_fds) {
12912 const struct got_error *pack_err =
12913 got_repo_pack_fds_close(pack_fds);
12914 if (error == NULL)
12915 error = pack_err;
12917 return error;
12920 __dead static void
12921 usage_integrate(void)
12923 fprintf(stderr, "usage: %s integrate branch\n", getprogname());
12924 exit(1);
12927 static const struct got_error *
12928 cmd_integrate(int argc, char *argv[])
12930 const struct got_error *error = NULL;
12931 struct got_repository *repo = NULL;
12932 struct got_worktree *worktree = NULL;
12933 char *cwd = NULL, *refname = NULL, *base_refname = NULL;
12934 const char *branch_arg = NULL;
12935 struct got_reference *branch_ref = NULL, *base_branch_ref = NULL;
12936 struct got_fileindex *fileindex = NULL;
12937 struct got_object_id *commit_id = NULL, *base_commit_id = NULL;
12938 int ch;
12939 struct got_update_progress_arg upa;
12940 int *pack_fds = NULL;
12942 #ifndef PROFILE
12943 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
12944 "unveil", NULL) == -1)
12945 err(1, "pledge");
12946 #endif
12948 while ((ch = getopt(argc, argv, "")) != -1) {
12949 switch (ch) {
12950 default:
12951 usage_integrate();
12952 /* NOTREACHED */
12956 argc -= optind;
12957 argv += optind;
12959 if (argc != 1)
12960 usage_integrate();
12961 branch_arg = argv[0];
12963 cwd = getcwd(NULL, 0);
12964 if (cwd == NULL) {
12965 error = got_error_from_errno("getcwd");
12966 goto done;
12969 error = got_repo_pack_fds_open(&pack_fds);
12970 if (error != NULL)
12971 goto done;
12973 error = got_worktree_open(&worktree, cwd);
12974 if (error) {
12975 if (error->code == GOT_ERR_NOT_WORKTREE)
12976 error = wrap_not_worktree_error(error, "integrate",
12977 cwd);
12978 goto done;
12981 error = check_rebase_or_histedit_in_progress(worktree);
12982 if (error)
12983 goto done;
12985 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
12986 NULL, pack_fds);
12987 if (error != NULL)
12988 goto done;
12990 error = apply_unveil(got_repo_get_path(repo), 0,
12991 got_worktree_get_root_path(worktree));
12992 if (error)
12993 goto done;
12995 error = check_merge_in_progress(worktree, repo);
12996 if (error)
12997 goto done;
12999 if (asprintf(&refname, "refs/heads/%s", branch_arg) == -1) {
13000 error = got_error_from_errno("asprintf");
13001 goto done;
13004 error = got_worktree_integrate_prepare(&fileindex, &branch_ref,
13005 &base_branch_ref, worktree, refname, repo);
13006 if (error)
13007 goto done;
13009 refname = strdup(got_ref_get_name(branch_ref));
13010 if (refname == NULL) {
13011 error = got_error_from_errno("strdup");
13012 got_worktree_integrate_abort(worktree, fileindex, repo,
13013 branch_ref, base_branch_ref);
13014 goto done;
13016 base_refname = strdup(got_ref_get_name(base_branch_ref));
13017 if (base_refname == NULL) {
13018 error = got_error_from_errno("strdup");
13019 got_worktree_integrate_abort(worktree, fileindex, repo,
13020 branch_ref, base_branch_ref);
13021 goto done;
13023 if (strncmp(base_refname, "refs/heads/", 11) != 0) {
13024 error = got_error(GOT_ERR_INTEGRATE_BRANCH);
13025 got_worktree_integrate_abort(worktree, fileindex, repo,
13026 branch_ref, base_branch_ref);
13027 goto done;
13030 error = got_ref_resolve(&commit_id, repo, branch_ref);
13031 if (error)
13032 goto done;
13034 error = got_ref_resolve(&base_commit_id, repo, base_branch_ref);
13035 if (error)
13036 goto done;
13038 if (got_object_id_cmp(commit_id, base_commit_id) == 0) {
13039 error = got_error_msg(GOT_ERR_SAME_BRANCH,
13040 "specified branch has already been integrated");
13041 got_worktree_integrate_abort(worktree, fileindex, repo,
13042 branch_ref, base_branch_ref);
13043 goto done;
13046 error = check_linear_ancestry(commit_id, base_commit_id, 1, repo);
13047 if (error) {
13048 if (error->code == GOT_ERR_ANCESTRY)
13049 error = got_error(GOT_ERR_REBASE_REQUIRED);
13050 got_worktree_integrate_abort(worktree, fileindex, repo,
13051 branch_ref, base_branch_ref);
13052 goto done;
13055 memset(&upa, 0, sizeof(upa));
13056 error = got_worktree_integrate_continue(worktree, fileindex, repo,
13057 branch_ref, base_branch_ref, update_progress, &upa,
13058 check_cancelled, NULL);
13059 if (error)
13060 goto done;
13062 printf("Integrated %s into %s\n", refname, base_refname);
13063 print_update_progress_stats(&upa);
13064 done:
13065 if (repo) {
13066 const struct got_error *close_err = got_repo_close(repo);
13067 if (error == NULL)
13068 error = close_err;
13070 if (worktree)
13071 got_worktree_close(worktree);
13072 if (pack_fds) {
13073 const struct got_error *pack_err =
13074 got_repo_pack_fds_close(pack_fds);
13075 if (error == NULL)
13076 error = pack_err;
13078 free(cwd);
13079 free(base_commit_id);
13080 free(commit_id);
13081 free(refname);
13082 free(base_refname);
13083 return error;
13086 __dead static void
13087 usage_merge(void)
13089 fprintf(stderr, "usage: %s merge [-aCcn] [branch]\n", getprogname());
13090 exit(1);
13093 static const struct got_error *
13094 cmd_merge(int argc, char *argv[])
13096 const struct got_error *error = NULL;
13097 struct got_worktree *worktree = NULL;
13098 struct got_repository *repo = NULL;
13099 struct got_fileindex *fileindex = NULL;
13100 char *cwd = NULL, *id_str = NULL, *author = NULL;
13101 char *gitconfig_path = NULL;
13102 struct got_reference *branch = NULL, *wt_branch = NULL;
13103 struct got_object_id *branch_tip = NULL, *yca_id = NULL;
13104 struct got_object_id *wt_branch_tip = NULL;
13105 int ch, merge_in_progress = 0, abort_merge = 0, continue_merge = 0;
13106 int allow_conflict = 0, interrupt_merge = 0;
13107 struct got_update_progress_arg upa;
13108 struct got_object_id *merge_commit_id = NULL;
13109 char *branch_name = NULL;
13110 int *pack_fds = NULL;
13112 memset(&upa, 0, sizeof(upa));
13114 #ifndef PROFILE
13115 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
13116 "unveil", NULL) == -1)
13117 err(1, "pledge");
13118 #endif
13120 while ((ch = getopt(argc, argv, "aCcn")) != -1) {
13121 switch (ch) {
13122 case 'a':
13123 abort_merge = 1;
13124 break;
13125 case 'C':
13126 allow_conflict = 1;
13127 case 'c':
13128 continue_merge = 1;
13129 break;
13130 case 'n':
13131 interrupt_merge = 1;
13132 break;
13133 default:
13134 usage_merge();
13135 /* NOTREACHED */
13139 argc -= optind;
13140 argv += optind;
13142 if (allow_conflict) {
13143 if (abort_merge)
13144 option_conflict('a', 'C');
13145 if (!continue_merge)
13146 errx(1, "-C option requires -c");
13148 if (abort_merge && continue_merge)
13149 option_conflict('a', 'c');
13150 if (abort_merge || continue_merge) {
13151 if (argc != 0)
13152 usage_merge();
13153 } else if (argc != 1)
13154 usage_merge();
13156 cwd = getcwd(NULL, 0);
13157 if (cwd == NULL) {
13158 error = got_error_from_errno("getcwd");
13159 goto done;
13162 error = got_repo_pack_fds_open(&pack_fds);
13163 if (error != NULL)
13164 goto done;
13166 error = got_worktree_open(&worktree, cwd);
13167 if (error) {
13168 if (error->code == GOT_ERR_NOT_WORKTREE)
13169 error = wrap_not_worktree_error(error,
13170 "merge", cwd);
13171 goto done;
13174 error = get_gitconfig_path(&gitconfig_path);
13175 if (error)
13176 goto done;
13177 error = got_repo_open(&repo,
13178 worktree ? got_worktree_get_repo_path(worktree) : cwd,
13179 gitconfig_path, pack_fds);
13180 if (error != NULL)
13181 goto done;
13183 if (worktree != NULL) {
13184 error = worktree_has_logmsg_ref("merge", worktree, repo);
13185 if (error)
13186 goto done;
13189 error = apply_unveil(got_repo_get_path(repo), 0,
13190 worktree ? got_worktree_get_root_path(worktree) : NULL);
13191 if (error)
13192 goto done;
13194 error = check_rebase_or_histedit_in_progress(worktree);
13195 if (error)
13196 goto done;
13198 error = got_worktree_merge_in_progress(&merge_in_progress, worktree,
13199 repo);
13200 if (error)
13201 goto done;
13203 if (abort_merge) {
13204 if (!merge_in_progress) {
13205 error = got_error(GOT_ERR_NOT_MERGING);
13206 goto done;
13208 error = got_worktree_merge_continue(&branch_name,
13209 &branch_tip, &fileindex, worktree, repo);
13210 if (error)
13211 goto done;
13212 error = got_worktree_merge_abort(worktree, fileindex, repo,
13213 abort_progress, &upa);
13214 if (error)
13215 goto done;
13216 printf("Merge of %s aborted\n", branch_name);
13217 goto done; /* nothing else to do */
13220 error = get_author(&author, repo, worktree);
13221 if (error)
13222 goto done;
13224 if (continue_merge) {
13225 if (!merge_in_progress) {
13226 error = got_error(GOT_ERR_NOT_MERGING);
13227 goto done;
13229 error = got_worktree_merge_continue(&branch_name,
13230 &branch_tip, &fileindex, worktree, repo);
13231 if (error)
13232 goto done;
13233 } else {
13234 error = got_ref_open(&branch, repo, argv[0], 0);
13235 if (error != NULL)
13236 goto done;
13237 branch_name = strdup(got_ref_get_name(branch));
13238 if (branch_name == NULL) {
13239 error = got_error_from_errno("strdup");
13240 goto done;
13242 error = got_ref_resolve(&branch_tip, repo, branch);
13243 if (error)
13244 goto done;
13247 error = got_ref_open(&wt_branch, repo,
13248 got_worktree_get_head_ref_name(worktree), 0);
13249 if (error)
13250 goto done;
13251 error = got_ref_resolve(&wt_branch_tip, repo, wt_branch);
13252 if (error)
13253 goto done;
13254 error = got_commit_graph_find_youngest_common_ancestor(&yca_id,
13255 wt_branch_tip, branch_tip, 0, repo,
13256 check_cancelled, NULL);
13257 if (error && error->code != GOT_ERR_ANCESTRY)
13258 goto done;
13260 if (!continue_merge) {
13261 error = check_path_prefix(wt_branch_tip, branch_tip,
13262 got_worktree_get_path_prefix(worktree),
13263 GOT_ERR_MERGE_PATH, repo);
13264 if (error)
13265 goto done;
13266 if (yca_id) {
13267 error = check_same_branch(wt_branch_tip, branch,
13268 yca_id, repo);
13269 if (error) {
13270 if (error->code != GOT_ERR_ANCESTRY)
13271 goto done;
13272 error = NULL;
13273 } else {
13274 static char msg[512];
13275 snprintf(msg, sizeof(msg),
13276 "cannot create a merge commit because "
13277 "%s is based on %s; %s can be integrated "
13278 "with 'got integrate' instead", branch_name,
13279 got_worktree_get_head_ref_name(worktree),
13280 branch_name);
13281 error = got_error_msg(GOT_ERR_SAME_BRANCH, msg);
13282 goto done;
13285 error = got_worktree_merge_prepare(&fileindex, worktree,
13286 branch, repo);
13287 if (error)
13288 goto done;
13290 error = got_worktree_merge_branch(worktree, fileindex,
13291 yca_id, branch_tip, repo, update_progress, &upa,
13292 check_cancelled, NULL);
13293 if (error)
13294 goto done;
13295 print_merge_progress_stats(&upa);
13296 if (!upa.did_something) {
13297 error = got_worktree_merge_abort(worktree, fileindex,
13298 repo, abort_progress, &upa);
13299 if (error)
13300 goto done;
13301 printf("Already up-to-date\n");
13302 goto done;
13306 if (interrupt_merge) {
13307 error = got_worktree_merge_postpone(worktree, fileindex);
13308 if (error)
13309 goto done;
13310 printf("Merge of %s interrupted on request\n", branch_name);
13311 } else if (upa.conflicts > 0 || upa.missing > 0 ||
13312 upa.not_deleted > 0 || upa.unversioned > 0) {
13313 error = got_worktree_merge_postpone(worktree, fileindex);
13314 if (error)
13315 goto done;
13316 if (upa.conflicts > 0 && upa.missing == 0 &&
13317 upa.not_deleted == 0 && upa.unversioned == 0) {
13318 error = got_error_msg(GOT_ERR_CONFLICTS,
13319 "conflicts must be resolved before merging "
13320 "can continue");
13321 } else if (upa.conflicts > 0) {
13322 error = got_error_msg(GOT_ERR_CONFLICTS,
13323 "conflicts must be resolved before merging "
13324 "can continue; changes destined for some "
13325 "files were not yet merged and "
13326 "should be merged manually if required before the "
13327 "merge operation is continued");
13328 } else {
13329 error = got_error_msg(GOT_ERR_CONFLICTS,
13330 "changes destined for some "
13331 "files were not yet merged and should be "
13332 "merged manually if required before the "
13333 "merge operation is continued");
13335 goto done;
13336 } else {
13337 error = got_worktree_merge_commit(&merge_commit_id, worktree,
13338 fileindex, author, NULL, 1, branch_tip, branch_name,
13339 allow_conflict, repo, continue_merge ? print_status : NULL,
13340 NULL);
13341 if (error)
13342 goto done;
13343 error = got_worktree_merge_complete(worktree, fileindex, repo);
13344 if (error)
13345 goto done;
13346 error = got_object_id_str(&id_str, merge_commit_id);
13347 if (error)
13348 goto done;
13349 printf("Merged %s into %s: %s\n", branch_name,
13350 got_worktree_get_head_ref_name(worktree),
13351 id_str);
13354 done:
13355 free(gitconfig_path);
13356 free(id_str);
13357 free(merge_commit_id);
13358 free(author);
13359 free(branch_tip);
13360 free(branch_name);
13361 free(yca_id);
13362 if (branch)
13363 got_ref_close(branch);
13364 if (wt_branch)
13365 got_ref_close(wt_branch);
13366 if (worktree)
13367 got_worktree_close(worktree);
13368 if (repo) {
13369 const struct got_error *close_err = got_repo_close(repo);
13370 if (error == NULL)
13371 error = close_err;
13373 if (pack_fds) {
13374 const struct got_error *pack_err =
13375 got_repo_pack_fds_close(pack_fds);
13376 if (error == NULL)
13377 error = pack_err;
13379 return error;
13382 __dead static void
13383 usage_stage(void)
13385 fprintf(stderr, "usage: %s stage [-lpS] [-F response-script] "
13386 "[path ...]\n", getprogname());
13387 exit(1);
13390 static const struct got_error *
13391 print_stage(void *arg, unsigned char status, unsigned char staged_status,
13392 const char *path, struct got_object_id *blob_id,
13393 struct got_object_id *staged_blob_id, struct got_object_id *commit_id,
13394 int dirfd, const char *de_name)
13396 const struct got_error *err = NULL;
13397 char *id_str = NULL;
13399 if (staged_status != GOT_STATUS_ADD &&
13400 staged_status != GOT_STATUS_MODIFY &&
13401 staged_status != GOT_STATUS_DELETE)
13402 return NULL;
13404 if (staged_status == GOT_STATUS_ADD ||
13405 staged_status == GOT_STATUS_MODIFY)
13406 err = got_object_id_str(&id_str, staged_blob_id);
13407 else
13408 err = got_object_id_str(&id_str, blob_id);
13409 if (err)
13410 return err;
13412 printf("%s %c %s\n", id_str, staged_status, path);
13413 free(id_str);
13414 return NULL;
13417 static const struct got_error *
13418 cmd_stage(int argc, char *argv[])
13420 const struct got_error *error = NULL;
13421 struct got_repository *repo = NULL;
13422 struct got_worktree *worktree = NULL;
13423 char *cwd = NULL;
13424 struct got_pathlist_head paths;
13425 int ch, list_stage = 0, pflag = 0, allow_bad_symlinks = 0;
13426 FILE *patch_script_file = NULL;
13427 const char *patch_script_path = NULL;
13428 struct choose_patch_arg cpa;
13429 int *pack_fds = NULL;
13431 TAILQ_INIT(&paths);
13433 #ifndef PROFILE
13434 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
13435 "unveil", NULL) == -1)
13436 err(1, "pledge");
13437 #endif
13439 while ((ch = getopt(argc, argv, "F:lpS")) != -1) {
13440 switch (ch) {
13441 case 'F':
13442 patch_script_path = optarg;
13443 break;
13444 case 'l':
13445 list_stage = 1;
13446 break;
13447 case 'p':
13448 pflag = 1;
13449 break;
13450 case 'S':
13451 allow_bad_symlinks = 1;
13452 break;
13453 default:
13454 usage_stage();
13455 /* NOTREACHED */
13459 argc -= optind;
13460 argv += optind;
13462 if (list_stage && (pflag || patch_script_path))
13463 errx(1, "-l option cannot be used with other options");
13464 if (patch_script_path && !pflag)
13465 errx(1, "-F option can only be used together with -p option");
13467 cwd = getcwd(NULL, 0);
13468 if (cwd == NULL) {
13469 error = got_error_from_errno("getcwd");
13470 goto done;
13473 error = got_repo_pack_fds_open(&pack_fds);
13474 if (error != NULL)
13475 goto done;
13477 error = got_worktree_open(&worktree, cwd);
13478 if (error) {
13479 if (error->code == GOT_ERR_NOT_WORKTREE)
13480 error = wrap_not_worktree_error(error, "stage", cwd);
13481 goto done;
13484 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
13485 NULL, pack_fds);
13486 if (error != NULL)
13487 goto done;
13489 if (patch_script_path) {
13490 patch_script_file = fopen(patch_script_path, "re");
13491 if (patch_script_file == NULL) {
13492 error = got_error_from_errno2("fopen",
13493 patch_script_path);
13494 goto done;
13497 error = apply_unveil(got_repo_get_path(repo), 0,
13498 got_worktree_get_root_path(worktree));
13499 if (error)
13500 goto done;
13502 error = check_merge_in_progress(worktree, repo);
13503 if (error)
13504 goto done;
13506 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
13507 if (error)
13508 goto done;
13510 if (list_stage)
13511 error = got_worktree_status(worktree, &paths, repo, 0,
13512 print_stage, NULL, check_cancelled, NULL);
13513 else {
13514 cpa.patch_script_file = patch_script_file;
13515 cpa.action = "stage";
13516 error = got_worktree_stage(worktree, &paths,
13517 pflag ? NULL : print_status, NULL,
13518 pflag ? choose_patch : NULL, &cpa,
13519 allow_bad_symlinks, repo);
13521 done:
13522 if (patch_script_file && fclose(patch_script_file) == EOF &&
13523 error == NULL)
13524 error = got_error_from_errno2("fclose", patch_script_path);
13525 if (repo) {
13526 const struct got_error *close_err = got_repo_close(repo);
13527 if (error == NULL)
13528 error = close_err;
13530 if (worktree)
13531 got_worktree_close(worktree);
13532 if (pack_fds) {
13533 const struct got_error *pack_err =
13534 got_repo_pack_fds_close(pack_fds);
13535 if (error == NULL)
13536 error = pack_err;
13538 got_pathlist_free(&paths, GOT_PATHLIST_FREE_PATH);
13539 free(cwd);
13540 return error;
13543 __dead static void
13544 usage_unstage(void)
13546 fprintf(stderr, "usage: %s unstage [-p] [-F response-script] "
13547 "[path ...]\n", getprogname());
13548 exit(1);
13552 static const struct got_error *
13553 cmd_unstage(int argc, char *argv[])
13555 const struct got_error *error = NULL;
13556 struct got_repository *repo = NULL;
13557 struct got_worktree *worktree = NULL;
13558 char *cwd = NULL;
13559 struct got_pathlist_head paths;
13560 int ch, pflag = 0;
13561 struct got_update_progress_arg upa;
13562 FILE *patch_script_file = NULL;
13563 const char *patch_script_path = NULL;
13564 struct choose_patch_arg cpa;
13565 int *pack_fds = NULL;
13567 TAILQ_INIT(&paths);
13569 #ifndef PROFILE
13570 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
13571 "unveil", NULL) == -1)
13572 err(1, "pledge");
13573 #endif
13575 while ((ch = getopt(argc, argv, "F:p")) != -1) {
13576 switch (ch) {
13577 case 'F':
13578 patch_script_path = optarg;
13579 break;
13580 case 'p':
13581 pflag = 1;
13582 break;
13583 default:
13584 usage_unstage();
13585 /* NOTREACHED */
13589 argc -= optind;
13590 argv += optind;
13592 if (patch_script_path && !pflag)
13593 errx(1, "-F option can only be used together with -p option");
13595 cwd = getcwd(NULL, 0);
13596 if (cwd == NULL) {
13597 error = got_error_from_errno("getcwd");
13598 goto done;
13601 error = got_repo_pack_fds_open(&pack_fds);
13602 if (error != NULL)
13603 goto done;
13605 error = got_worktree_open(&worktree, cwd);
13606 if (error) {
13607 if (error->code == GOT_ERR_NOT_WORKTREE)
13608 error = wrap_not_worktree_error(error, "unstage", cwd);
13609 goto done;
13612 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
13613 NULL, pack_fds);
13614 if (error != NULL)
13615 goto done;
13617 if (patch_script_path) {
13618 patch_script_file = fopen(patch_script_path, "re");
13619 if (patch_script_file == NULL) {
13620 error = got_error_from_errno2("fopen",
13621 patch_script_path);
13622 goto done;
13626 error = apply_unveil(got_repo_get_path(repo), 0,
13627 got_worktree_get_root_path(worktree));
13628 if (error)
13629 goto done;
13631 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
13632 if (error)
13633 goto done;
13635 cpa.patch_script_file = patch_script_file;
13636 cpa.action = "unstage";
13637 memset(&upa, 0, sizeof(upa));
13638 error = got_worktree_unstage(worktree, &paths, update_progress,
13639 &upa, pflag ? choose_patch : NULL, &cpa, repo);
13640 if (!error)
13641 print_merge_progress_stats(&upa);
13642 done:
13643 if (patch_script_file && fclose(patch_script_file) == EOF &&
13644 error == NULL)
13645 error = got_error_from_errno2("fclose", patch_script_path);
13646 if (repo) {
13647 const struct got_error *close_err = got_repo_close(repo);
13648 if (error == NULL)
13649 error = close_err;
13651 if (worktree)
13652 got_worktree_close(worktree);
13653 if (pack_fds) {
13654 const struct got_error *pack_err =
13655 got_repo_pack_fds_close(pack_fds);
13656 if (error == NULL)
13657 error = pack_err;
13659 got_pathlist_free(&paths, GOT_PATHLIST_FREE_PATH);
13660 free(cwd);
13661 return error;
13664 __dead static void
13665 usage_cat(void)
13667 fprintf(stderr, "usage: %s cat [-P] [-c commit] [-r repository-path] "
13668 "arg ...\n", getprogname());
13669 exit(1);
13672 static const struct got_error *
13673 cat_blob(struct got_object_id *id, struct got_repository *repo, FILE *outfile)
13675 const struct got_error *err;
13676 struct got_blob_object *blob;
13677 int fd = -1;
13679 fd = got_opentempfd();
13680 if (fd == -1)
13681 return got_error_from_errno("got_opentempfd");
13683 err = got_object_open_as_blob(&blob, repo, id, 8192, fd);
13684 if (err)
13685 goto done;
13687 err = got_object_blob_dump_to_file(NULL, NULL, NULL, outfile, blob);
13688 done:
13689 if (fd != -1 && close(fd) == -1 && err == NULL)
13690 err = got_error_from_errno("close");
13691 if (blob)
13692 got_object_blob_close(blob);
13693 return err;
13696 static const struct got_error *
13697 cat_tree(struct got_object_id *id, struct got_repository *repo, FILE *outfile)
13699 const struct got_error *err;
13700 struct got_tree_object *tree;
13701 int nentries, i;
13703 err = got_object_open_as_tree(&tree, repo, id);
13704 if (err)
13705 return err;
13707 nentries = got_object_tree_get_nentries(tree);
13708 for (i = 0; i < nentries; i++) {
13709 struct got_tree_entry *te;
13710 char *id_str;
13711 if (sigint_received || sigpipe_received)
13712 break;
13713 te = got_object_tree_get_entry(tree, i);
13714 err = got_object_id_str(&id_str, got_tree_entry_get_id(te));
13715 if (err)
13716 break;
13717 fprintf(outfile, "%s %.7o %s\n", id_str,
13718 got_tree_entry_get_mode(te),
13719 got_tree_entry_get_name(te));
13720 free(id_str);
13723 got_object_tree_close(tree);
13724 return err;
13727 static const struct got_error *
13728 cat_commit(struct got_object_id *id, struct got_repository *repo, FILE *outfile)
13730 const struct got_error *err;
13731 struct got_commit_object *commit;
13732 const struct got_object_id_queue *parent_ids;
13733 struct got_object_qid *pid;
13734 char *id_str = NULL;
13735 const char *logmsg = NULL;
13736 char gmtoff[6];
13738 err = got_object_open_as_commit(&commit, repo, id);
13739 if (err)
13740 return err;
13742 err = got_object_id_str(&id_str, got_object_commit_get_tree_id(commit));
13743 if (err)
13744 goto done;
13746 fprintf(outfile, "%s%s\n", GOT_COMMIT_LABEL_TREE, id_str);
13747 parent_ids = got_object_commit_get_parent_ids(commit);
13748 fprintf(outfile, "numparents %d\n",
13749 got_object_commit_get_nparents(commit));
13750 STAILQ_FOREACH(pid, parent_ids, entry) {
13751 char *pid_str;
13752 err = got_object_id_str(&pid_str, &pid->id);
13753 if (err)
13754 goto done;
13755 fprintf(outfile, "%s%s\n", GOT_COMMIT_LABEL_PARENT, pid_str);
13756 free(pid_str);
13758 got_date_format_gmtoff(gmtoff, sizeof(gmtoff),
13759 got_object_commit_get_author_gmtoff(commit));
13760 fprintf(outfile, "%s%s %lld %s\n", GOT_COMMIT_LABEL_AUTHOR,
13761 got_object_commit_get_author(commit),
13762 (long long)got_object_commit_get_author_time(commit),
13763 gmtoff);
13765 got_date_format_gmtoff(gmtoff, sizeof(gmtoff),
13766 got_object_commit_get_committer_gmtoff(commit));
13767 fprintf(outfile, "%s%s %lld %s\n", GOT_COMMIT_LABEL_COMMITTER,
13768 got_object_commit_get_committer(commit),
13769 (long long)got_object_commit_get_committer_time(commit),
13770 gmtoff);
13772 logmsg = got_object_commit_get_logmsg_raw(commit);
13773 fprintf(outfile, "messagelen %zd\n", strlen(logmsg));
13774 fprintf(outfile, "%s", logmsg);
13775 done:
13776 free(id_str);
13777 got_object_commit_close(commit);
13778 return err;
13781 static const struct got_error *
13782 cat_tag(struct got_object_id *id, struct got_repository *repo, FILE *outfile)
13784 const struct got_error *err;
13785 struct got_tag_object *tag;
13786 char *id_str = NULL;
13787 const char *tagmsg = NULL;
13788 char gmtoff[6];
13790 err = got_object_open_as_tag(&tag, repo, id);
13791 if (err)
13792 return err;
13794 err = got_object_id_str(&id_str, got_object_tag_get_object_id(tag));
13795 if (err)
13796 goto done;
13798 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_OBJECT, id_str);
13800 switch (got_object_tag_get_object_type(tag)) {
13801 case GOT_OBJ_TYPE_BLOB:
13802 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TYPE,
13803 GOT_OBJ_LABEL_BLOB);
13804 break;
13805 case GOT_OBJ_TYPE_TREE:
13806 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TYPE,
13807 GOT_OBJ_LABEL_TREE);
13808 break;
13809 case GOT_OBJ_TYPE_COMMIT:
13810 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TYPE,
13811 GOT_OBJ_LABEL_COMMIT);
13812 break;
13813 case GOT_OBJ_TYPE_TAG:
13814 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TYPE,
13815 GOT_OBJ_LABEL_TAG);
13816 break;
13817 default:
13818 break;
13821 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TAG,
13822 got_object_tag_get_name(tag));
13824 got_date_format_gmtoff(gmtoff, sizeof(gmtoff),
13825 got_object_tag_get_tagger_gmtoff(tag));
13826 fprintf(outfile, "%s%s %lld %s\n", GOT_TAG_LABEL_TAGGER,
13827 got_object_tag_get_tagger(tag),
13828 (long long)got_object_tag_get_tagger_time(tag),
13829 gmtoff);
13831 tagmsg = got_object_tag_get_message(tag);
13832 fprintf(outfile, "messagelen %zd\n", strlen(tagmsg));
13833 fprintf(outfile, "%s", tagmsg);
13834 done:
13835 free(id_str);
13836 got_object_tag_close(tag);
13837 return err;
13840 static const struct got_error *
13841 cmd_cat(int argc, char *argv[])
13843 const struct got_error *error;
13844 struct got_repository *repo = NULL;
13845 struct got_worktree *worktree = NULL;
13846 char *cwd = NULL, *repo_path = NULL, *label = NULL;
13847 const char *commit_id_str = NULL;
13848 struct got_object_id *id = NULL, *commit_id = NULL;
13849 struct got_commit_object *commit = NULL;
13850 int ch, obj_type, i, force_path = 0;
13851 struct got_reflist_head refs;
13852 int *pack_fds = NULL;
13854 TAILQ_INIT(&refs);
13856 #ifndef PROFILE
13857 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
13858 NULL) == -1)
13859 err(1, "pledge");
13860 #endif
13862 while ((ch = getopt(argc, argv, "c:Pr:")) != -1) {
13863 switch (ch) {
13864 case 'c':
13865 commit_id_str = optarg;
13866 break;
13867 case 'P':
13868 force_path = 1;
13869 break;
13870 case 'r':
13871 repo_path = realpath(optarg, NULL);
13872 if (repo_path == NULL)
13873 return got_error_from_errno2("realpath",
13874 optarg);
13875 got_path_strip_trailing_slashes(repo_path);
13876 break;
13877 default:
13878 usage_cat();
13879 /* NOTREACHED */
13883 argc -= optind;
13884 argv += optind;
13886 cwd = getcwd(NULL, 0);
13887 if (cwd == NULL) {
13888 error = got_error_from_errno("getcwd");
13889 goto done;
13892 error = got_repo_pack_fds_open(&pack_fds);
13893 if (error != NULL)
13894 goto done;
13896 if (repo_path == NULL) {
13897 error = got_worktree_open(&worktree, cwd);
13898 if (error && error->code != GOT_ERR_NOT_WORKTREE)
13899 goto done;
13900 if (worktree) {
13901 repo_path = strdup(
13902 got_worktree_get_repo_path(worktree));
13903 if (repo_path == NULL) {
13904 error = got_error_from_errno("strdup");
13905 goto done;
13908 /* Release work tree lock. */
13909 got_worktree_close(worktree);
13910 worktree = NULL;
13914 if (repo_path == NULL) {
13915 repo_path = strdup(cwd);
13916 if (repo_path == NULL)
13917 return got_error_from_errno("strdup");
13920 error = got_repo_open(&repo, repo_path, NULL, pack_fds);
13921 free(repo_path);
13922 if (error != NULL)
13923 goto done;
13925 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
13926 if (error)
13927 goto done;
13929 error = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name, NULL);
13930 if (error)
13931 goto done;
13933 if (commit_id_str == NULL)
13934 commit_id_str = GOT_REF_HEAD;
13935 error = got_repo_match_object_id(&commit_id, NULL,
13936 commit_id_str, GOT_OBJ_TYPE_COMMIT, &refs, repo);
13937 if (error)
13938 goto done;
13940 error = got_object_open_as_commit(&commit, repo, commit_id);
13941 if (error)
13942 goto done;
13944 for (i = 0; i < argc; i++) {
13945 if (force_path) {
13946 error = got_object_id_by_path(&id, repo, commit,
13947 argv[i]);
13948 if (error)
13949 break;
13950 } else {
13951 error = got_repo_match_object_id(&id, &label, argv[i],
13952 GOT_OBJ_TYPE_ANY, NULL /* do not resolve tags */,
13953 repo);
13954 if (error) {
13955 if (error->code != GOT_ERR_BAD_OBJ_ID_STR &&
13956 error->code != GOT_ERR_NOT_REF)
13957 break;
13958 error = got_object_id_by_path(&id, repo,
13959 commit, argv[i]);
13960 if (error)
13961 break;
13965 error = got_object_get_type(&obj_type, repo, id);
13966 if (error)
13967 break;
13969 switch (obj_type) {
13970 case GOT_OBJ_TYPE_BLOB:
13971 error = cat_blob(id, repo, stdout);
13972 break;
13973 case GOT_OBJ_TYPE_TREE:
13974 error = cat_tree(id, repo, stdout);
13975 break;
13976 case GOT_OBJ_TYPE_COMMIT:
13977 error = cat_commit(id, repo, stdout);
13978 break;
13979 case GOT_OBJ_TYPE_TAG:
13980 error = cat_tag(id, repo, stdout);
13981 break;
13982 default:
13983 error = got_error(GOT_ERR_OBJ_TYPE);
13984 break;
13986 if (error)
13987 break;
13988 free(label);
13989 label = NULL;
13990 free(id);
13991 id = NULL;
13993 done:
13994 free(label);
13995 free(id);
13996 free(commit_id);
13997 if (commit)
13998 got_object_commit_close(commit);
13999 if (worktree)
14000 got_worktree_close(worktree);
14001 if (repo) {
14002 const struct got_error *close_err = got_repo_close(repo);
14003 if (error == NULL)
14004 error = close_err;
14006 if (pack_fds) {
14007 const struct got_error *pack_err =
14008 got_repo_pack_fds_close(pack_fds);
14009 if (error == NULL)
14010 error = pack_err;
14013 got_ref_list_free(&refs);
14014 return error;
14017 __dead static void
14018 usage_info(void)
14020 fprintf(stderr, "usage: %s info [path ...]\n",
14021 getprogname());
14022 exit(1);
14025 static const struct got_error *
14026 print_path_info(void *arg, const char *path, mode_t mode, time_t mtime,
14027 struct got_object_id *blob_id, struct got_object_id *staged_blob_id,
14028 struct got_object_id *commit_id)
14030 const struct got_error *err = NULL;
14031 char *id_str = NULL;
14032 char datebuf[128];
14033 struct tm mytm, *tm;
14034 struct got_pathlist_head *paths = arg;
14035 struct got_pathlist_entry *pe;
14038 * Clear error indication from any of the path arguments which
14039 * would cause this file index entry to be displayed.
14041 TAILQ_FOREACH(pe, paths, entry) {
14042 if (got_path_cmp(path, pe->path, strlen(path),
14043 pe->path_len) == 0 ||
14044 got_path_is_child(path, pe->path, pe->path_len))
14045 pe->data = NULL; /* no error */
14048 printf(GOT_COMMIT_SEP_STR);
14049 if (S_ISLNK(mode))
14050 printf("symlink: %s\n", path);
14051 else if (S_ISREG(mode)) {
14052 printf("file: %s\n", path);
14053 printf("mode: %o\n", mode & (S_IRWXU | S_IRWXG | S_IRWXO));
14054 } else if (S_ISDIR(mode))
14055 printf("directory: %s\n", path);
14056 else
14057 printf("something: %s\n", path);
14059 tm = localtime_r(&mtime, &mytm);
14060 if (tm == NULL)
14061 return NULL;
14062 if (strftime(datebuf, sizeof(datebuf), "%c %Z", tm) == 0)
14063 return got_error(GOT_ERR_NO_SPACE);
14064 printf("timestamp: %s\n", datebuf);
14066 if (blob_id) {
14067 err = got_object_id_str(&id_str, blob_id);
14068 if (err)
14069 return err;
14070 printf("based on blob: %s\n", id_str);
14071 free(id_str);
14074 if (staged_blob_id) {
14075 err = got_object_id_str(&id_str, staged_blob_id);
14076 if (err)
14077 return err;
14078 printf("based on staged blob: %s\n", id_str);
14079 free(id_str);
14082 if (commit_id) {
14083 err = got_object_id_str(&id_str, commit_id);
14084 if (err)
14085 return err;
14086 printf("based on commit: %s\n", id_str);
14087 free(id_str);
14090 return NULL;
14093 static const struct got_error *
14094 cmd_info(int argc, char *argv[])
14096 const struct got_error *error = NULL;
14097 struct got_worktree *worktree = NULL;
14098 char *cwd = NULL, *id_str = NULL;
14099 struct got_pathlist_head paths;
14100 char *uuidstr = NULL;
14101 int ch, show_files = 0;
14103 TAILQ_INIT(&paths);
14105 #ifndef PROFILE
14106 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
14107 NULL) == -1)
14108 err(1, "pledge");
14109 #endif
14111 while ((ch = getopt(argc, argv, "")) != -1) {
14112 switch (ch) {
14113 default:
14114 usage_info();
14115 /* NOTREACHED */
14119 argc -= optind;
14120 argv += optind;
14122 cwd = getcwd(NULL, 0);
14123 if (cwd == NULL) {
14124 error = got_error_from_errno("getcwd");
14125 goto done;
14128 error = got_worktree_open(&worktree, cwd);
14129 if (error) {
14130 if (error->code == GOT_ERR_NOT_WORKTREE)
14131 error = wrap_not_worktree_error(error, "info", cwd);
14132 goto done;
14135 #ifndef PROFILE
14136 /* Remove "wpath cpath proc exec sendfd" promises. */
14137 if (pledge("stdio rpath flock unveil", NULL) == -1)
14138 err(1, "pledge");
14139 #endif
14140 error = apply_unveil(NULL, 0, got_worktree_get_root_path(worktree));
14141 if (error)
14142 goto done;
14144 if (argc >= 1) {
14145 error = get_worktree_paths_from_argv(&paths, argc, argv,
14146 worktree);
14147 if (error)
14148 goto done;
14149 show_files = 1;
14152 error = got_object_id_str(&id_str,
14153 got_worktree_get_base_commit_id(worktree));
14154 if (error)
14155 goto done;
14157 error = got_worktree_get_uuid(&uuidstr, worktree);
14158 if (error)
14159 goto done;
14161 printf("work tree: %s\n", got_worktree_get_root_path(worktree));
14162 printf("work tree base commit: %s\n", id_str);
14163 printf("work tree path prefix: %s\n",
14164 got_worktree_get_path_prefix(worktree));
14165 printf("work tree branch reference: %s\n",
14166 got_worktree_get_head_ref_name(worktree));
14167 printf("work tree UUID: %s\n", uuidstr);
14168 printf("repository: %s\n", got_worktree_get_repo_path(worktree));
14170 if (show_files) {
14171 struct got_pathlist_entry *pe;
14172 TAILQ_FOREACH(pe, &paths, entry) {
14173 if (pe->path_len == 0)
14174 continue;
14176 * Assume this path will fail. This will be corrected
14177 * in print_path_info() in case the path does suceeed.
14179 pe->data = (void *)got_error(GOT_ERR_BAD_PATH);
14181 error = got_worktree_path_info(worktree, &paths,
14182 print_path_info, &paths, check_cancelled, NULL);
14183 if (error)
14184 goto done;
14185 TAILQ_FOREACH(pe, &paths, entry) {
14186 if (pe->data != NULL) {
14187 const struct got_error *perr;
14189 perr = pe->data;
14190 error = got_error_fmt(perr->code, "%s",
14191 pe->path);
14192 break;
14196 done:
14197 if (worktree)
14198 got_worktree_close(worktree);
14199 got_pathlist_free(&paths, GOT_PATHLIST_FREE_PATH);
14200 free(cwd);
14201 free(id_str);
14202 free(uuidstr);
14203 return error;