indentation fix
[got-portable.git] / got / got.c
blobb58621df4e1db4a6c7857bcab0578de5dab7924f
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>
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 #include "got_compat.h"
21 #include <sys/queue.h>
22 #include <sys/time.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <sys/wait.h>
27 #include <err.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <limits.h>
31 #include <locale.h>
32 #include <ctype.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>
44 #include "got_version.h"
45 #include "got_error.h"
46 #include "got_object.h"
47 #include "got_reference.h"
48 #include "got_repository.h"
49 #include "got_path.h"
50 #include "got_cancel.h"
51 #include "got_worktree.h"
52 #include "got_diff.h"
53 #include "got_commit_graph.h"
54 #include "got_fetch.h"
55 #include "got_send.h"
56 #include "got_blame.h"
57 #include "got_privsep.h"
58 #include "got_opentemp.h"
59 #include "got_gotconfig.h"
60 #include "got_dial.h"
61 #include "got_patch.h"
62 #include "got_sigs.h"
63 #include "got_date.h"
64 #include "got_keyword.h"
66 #ifndef nitems
67 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
68 #endif
70 #ifndef GOT_DEFAULT_EDITOR
71 #define GOT_DEFAULT_EDITOR "/usr/bin/vi"
72 #endif
74 static volatile sig_atomic_t sigint_received;
75 static volatile sig_atomic_t sigpipe_received;
77 static void
78 catch_sigint(int signo)
80 sigint_received = 1;
83 static void
84 catch_sigpipe(int signo)
86 sigpipe_received = 1;
90 struct got_cmd {
91 const char *cmd_name;
92 const struct got_error *(*cmd_main)(int, char *[]);
93 void (*cmd_usage)(void);
94 const char *cmd_alias;
97 __dead static void usage(int, int);
98 __dead static void usage_init(void);
99 __dead static void usage_import(void);
100 __dead static void usage_clone(void);
101 __dead static void usage_fetch(void);
102 __dead static void usage_checkout(void);
103 __dead static void usage_update(void);
104 __dead static void usage_log(void);
105 __dead static void usage_diff(void);
106 __dead static void usage_blame(void);
107 __dead static void usage_tree(void);
108 __dead static void usage_status(void);
109 __dead static void usage_ref(void);
110 __dead static void usage_branch(void);
111 __dead static void usage_tag(void);
112 __dead static void usage_add(void);
113 __dead static void usage_remove(void);
114 __dead static void usage_patch(void);
115 __dead static void usage_revert(void);
116 __dead static void usage_commit(void);
117 __dead static void usage_send(void);
118 __dead static void usage_cherrypick(void);
119 __dead static void usage_backout(void);
120 __dead static void usage_rebase(void);
121 __dead static void usage_histedit(void);
122 __dead static void usage_integrate(void);
123 __dead static void usage_merge(void);
124 __dead static void usage_stage(void);
125 __dead static void usage_unstage(void);
126 __dead static void usage_cat(void);
127 __dead static void usage_info(void);
129 static const struct got_error* cmd_init(int, char *[]);
130 static const struct got_error* cmd_import(int, char *[]);
131 static const struct got_error* cmd_clone(int, char *[]);
132 static const struct got_error* cmd_fetch(int, char *[]);
133 static const struct got_error* cmd_checkout(int, char *[]);
134 static const struct got_error* cmd_update(int, char *[]);
135 static const struct got_error* cmd_log(int, char *[]);
136 static const struct got_error* cmd_diff(int, char *[]);
137 static const struct got_error* cmd_blame(int, char *[]);
138 static const struct got_error* cmd_tree(int, char *[]);
139 static const struct got_error* cmd_status(int, char *[]);
140 static const struct got_error* cmd_ref(int, char *[]);
141 static const struct got_error* cmd_branch(int, char *[]);
142 static const struct got_error* cmd_tag(int, char *[]);
143 static const struct got_error* cmd_add(int, char *[]);
144 static const struct got_error* cmd_remove(int, char *[]);
145 static const struct got_error* cmd_patch(int, char *[]);
146 static const struct got_error* cmd_revert(int, char *[]);
147 static const struct got_error* cmd_commit(int, char *[]);
148 static const struct got_error* cmd_send(int, char *[]);
149 static const struct got_error* cmd_cherrypick(int, char *[]);
150 static const struct got_error* cmd_backout(int, char *[]);
151 static const struct got_error* cmd_rebase(int, char *[]);
152 static const struct got_error* cmd_histedit(int, char *[]);
153 static const struct got_error* cmd_integrate(int, char *[]);
154 static const struct got_error* cmd_merge(int, char *[]);
155 static const struct got_error* cmd_stage(int, char *[]);
156 static const struct got_error* cmd_unstage(int, char *[]);
157 static const struct got_error* cmd_cat(int, char *[]);
158 static const struct got_error* cmd_info(int, char *[]);
160 static const struct got_cmd got_commands[] = {
161 { "init", cmd_init, usage_init, "" },
162 { "import", cmd_import, usage_import, "im" },
163 { "clone", cmd_clone, usage_clone, "cl" },
164 { "fetch", cmd_fetch, usage_fetch, "fe" },
165 { "checkout", cmd_checkout, usage_checkout, "co" },
166 { "update", cmd_update, usage_update, "up" },
167 { "log", cmd_log, usage_log, "" },
168 { "diff", cmd_diff, usage_diff, "di" },
169 { "blame", cmd_blame, usage_blame, "bl" },
170 { "tree", cmd_tree, usage_tree, "tr" },
171 { "status", cmd_status, usage_status, "st" },
172 { "ref", cmd_ref, usage_ref, "" },
173 { "branch", cmd_branch, usage_branch, "br" },
174 { "tag", cmd_tag, usage_tag, "" },
175 { "add", cmd_add, usage_add, "" },
176 { "remove", cmd_remove, usage_remove, "rm" },
177 { "patch", cmd_patch, usage_patch, "pa" },
178 { "revert", cmd_revert, usage_revert, "rv" },
179 { "commit", cmd_commit, usage_commit, "ci" },
180 { "send", cmd_send, usage_send, "se" },
181 { "cherrypick", cmd_cherrypick, usage_cherrypick, "cy" },
182 { "backout", cmd_backout, usage_backout, "bo" },
183 { "rebase", cmd_rebase, usage_rebase, "rb" },
184 { "histedit", cmd_histedit, usage_histedit, "he" },
185 { "integrate", cmd_integrate, usage_integrate,"ig" },
186 { "merge", cmd_merge, usage_merge, "mg" },
187 { "stage", cmd_stage, usage_stage, "sg" },
188 { "unstage", cmd_unstage, usage_unstage, "ug" },
189 { "cat", cmd_cat, usage_cat, "" },
190 { "info", cmd_info, usage_info, "" },
193 static void
194 list_commands(FILE *fp)
196 size_t i;
198 fprintf(fp, "commands:");
199 for (i = 0; i < nitems(got_commands); i++) {
200 const struct got_cmd *cmd = &got_commands[i];
201 fprintf(fp, " %s", cmd->cmd_name);
203 fputc('\n', fp);
206 __dead static void
207 option_conflict(char a, char b)
209 errx(1, "-%c and -%c options are mutually exclusive", a, b);
213 main(int argc, char *argv[])
215 const struct got_cmd *cmd;
216 size_t i;
217 int ch;
218 int hflag = 0, Vflag = 0;
219 static const struct option longopts[] = {
220 { "version", no_argument, NULL, 'V' },
221 { NULL, 0, NULL, 0 }
224 setlocale(LC_CTYPE, "");
226 while ((ch = getopt_long(argc, argv, "+hV", longopts, NULL)) != -1) {
227 switch (ch) {
228 case 'h':
229 hflag = 1;
230 break;
231 case 'V':
232 Vflag = 1;
233 break;
234 default:
235 usage(hflag, 1);
236 /* NOTREACHED */
240 argc -= optind;
241 argv += optind;
242 optind = 1;
243 optreset = 1;
245 if (Vflag) {
246 got_version_print_str();
247 return 0;
250 if (argc <= 0)
251 usage(hflag, hflag ? 0 : 1);
253 signal(SIGINT, catch_sigint);
254 signal(SIGPIPE, catch_sigpipe);
256 for (i = 0; i < nitems(got_commands); i++) {
257 const struct got_error *error;
259 cmd = &got_commands[i];
261 if (strcmp(cmd->cmd_name, argv[0]) != 0 &&
262 strcmp(cmd->cmd_alias, argv[0]) != 0)
263 continue;
265 if (hflag)
266 cmd->cmd_usage();
268 error = cmd->cmd_main(argc, argv);
269 if (error && error->code != GOT_ERR_CANCELLED &&
270 error->code != GOT_ERR_PRIVSEP_EXIT &&
271 !(sigpipe_received &&
272 error->code == GOT_ERR_ERRNO && errno == EPIPE) &&
273 !(sigint_received &&
274 error->code == GOT_ERR_ERRNO && errno == EINTR)) {
275 fflush(stdout);
276 fprintf(stderr, "%s: %s\n", getprogname(), error->msg);
277 return 1;
280 return 0;
283 fprintf(stderr, "%s: unknown command '%s'\n", getprogname(), argv[0]);
284 list_commands(stderr);
285 return 1;
288 __dead static void
289 usage(int hflag, int status)
291 FILE *fp = (status == 0) ? stdout : stderr;
293 fprintf(fp, "usage: %s [-hV] command [arg ...]\n",
294 getprogname());
295 if (hflag)
296 list_commands(fp);
297 exit(status);
300 static const struct got_error *
301 get_editor(char **abspath)
303 const struct got_error *err = NULL;
304 const char *editor;
306 *abspath = NULL;
308 editor = getenv("VISUAL");
309 if (editor == NULL)
310 editor = getenv("EDITOR");
312 if (editor) {
313 err = got_path_find_prog(abspath, editor);
314 if (err)
315 return err;
318 if (*abspath == NULL) {
319 *abspath = strdup(GOT_DEFAULT_EDITOR);
320 if (*abspath == NULL)
321 return got_error_from_errno("strdup");
324 return NULL;
327 static const struct got_error *
328 apply_unveil(const char *repo_path, int repo_read_only,
329 const char *worktree_path)
331 const struct got_error *err;
333 #ifdef PROFILE
334 if (unveil("gmon.out", "rwc") != 0)
335 return got_error_from_errno2("unveil", "gmon.out");
336 #endif
337 if (repo_path && unveil(repo_path, repo_read_only ? "r" : "rwc") != 0)
338 return got_error_from_errno2("unveil", repo_path);
340 if (worktree_path && unveil(worktree_path, "rwc") != 0)
341 return got_error_from_errno2("unveil", worktree_path);
343 if (unveil(GOT_TMPDIR_STR, "rwc") != 0)
344 return got_error_from_errno2("unveil", GOT_TMPDIR_STR);
346 err = got_privsep_unveil_exec_helpers();
347 if (err != NULL)
348 return err;
350 if (unveil(NULL, NULL) != 0)
351 return got_error_from_errno("unveil");
353 return NULL;
356 __dead static void
357 usage_init(void)
359 fprintf(stderr, "usage: %s init [-A hashing-algorithm] [-b branch]"
360 " repository-path\n",
361 getprogname());
362 exit(1);
365 static const struct got_error *
366 cmd_init(int argc, char *argv[])
368 const struct got_error *error = NULL;
369 const char *head_name = NULL;
370 char *repo_path = NULL;
371 enum got_hash_algorithm algo = GOT_HASH_SHA1;
372 int ch;
374 while ((ch = getopt(argc, argv, "A:b:")) != -1) {
375 switch (ch) {
376 case 'A':
377 if (!strcmp(optarg, "sha1"))
378 algo = GOT_HASH_SHA1;
379 else if (!strcmp(optarg, "sha256"))
380 algo = GOT_HASH_SHA256;
381 else
382 return got_error_path(optarg,
383 GOT_ERR_OBJECT_FORMAT);
384 break;
385 case 'b':
386 head_name = optarg;
387 break;
388 default:
389 usage_init();
390 /* NOTREACHED */
394 argc -= optind;
395 argv += optind;
397 #ifndef PROFILE
398 if (pledge("stdio rpath wpath cpath unveil", NULL) == -1)
399 err(1, "pledge");
400 #endif
401 if (argc != 1)
402 usage_init();
404 repo_path = strdup(argv[0]);
405 if (repo_path == NULL)
406 return got_error_from_errno("strdup");
408 got_path_strip_trailing_slashes(repo_path);
410 error = got_path_mkdir(repo_path);
411 if (error &&
412 !(error->code == GOT_ERR_ERRNO && errno == EEXIST))
413 goto done;
415 error = apply_unveil(repo_path, 0, NULL);
416 if (error)
417 goto done;
419 error = got_repo_init(repo_path, head_name, algo);
420 done:
421 free(repo_path);
422 return error;
425 __dead static void
426 usage_import(void)
428 fprintf(stderr, "usage: %s import [-b branch] [-I pattern] [-m message] "
429 "[-r repository-path] directory\n", getprogname());
430 exit(1);
433 static int
434 spawn_editor(const char *editor, const char *file)
436 pid_t pid;
437 sig_t sighup, sigint, sigquit;
438 int st = -1;
440 sighup = signal(SIGHUP, SIG_IGN);
441 sigint = signal(SIGINT, SIG_IGN);
442 sigquit = signal(SIGQUIT, SIG_IGN);
444 switch (pid = fork()) {
445 case -1:
446 goto doneediting;
447 case 0:
448 execl(editor, editor, file, (char *)NULL);
449 _exit(127);
452 while (waitpid(pid, &st, 0) == -1)
453 if (errno != EINTR)
454 break;
456 doneediting:
457 (void)signal(SIGHUP, sighup);
458 (void)signal(SIGINT, sigint);
459 (void)signal(SIGQUIT, sigquit);
461 if (!WIFEXITED(st)) {
462 errno = EINTR;
463 return -1;
466 return WEXITSTATUS(st);
469 static const struct got_error *
470 read_logmsg(char **logmsg, size_t *len, FILE *fp, size_t filesize)
472 const struct got_error *err = NULL;
473 char *line = NULL;
474 size_t linesize = 0;
476 *logmsg = NULL;
477 *len = 0;
479 if (fseeko(fp, 0L, SEEK_SET) == -1)
480 return got_error_from_errno("fseeko");
482 *logmsg = malloc(filesize + 1);
483 if (*logmsg == NULL)
484 return got_error_from_errno("malloc");
485 (*logmsg)[0] = '\0';
487 while (getline(&line, &linesize, fp) != -1) {
488 if (line[0] == '#' || (*len == 0 && line[0] == '\n'))
489 continue; /* remove comments and leading empty lines */
490 *len = strlcat(*logmsg, line, filesize + 1);
491 if (*len >= filesize + 1) {
492 err = got_error(GOT_ERR_NO_SPACE);
493 goto done;
496 if (ferror(fp)) {
497 err = got_ferror(fp, GOT_ERR_IO);
498 goto done;
501 while (*len > 0 && (*logmsg)[*len - 1] == '\n') {
502 (*logmsg)[*len - 1] = '\0';
503 (*len)--;
505 done:
506 free(line);
507 if (err) {
508 free(*logmsg);
509 *logmsg = NULL;
510 *len = 0;
512 return err;
515 static const struct got_error *
516 edit_logmsg(char **logmsg, const char *editor, const char *logmsg_path,
517 const char *initial_content, size_t initial_content_len,
518 int require_modification)
520 const struct got_error *err = NULL;
521 struct stat st, st2;
522 FILE *fp = NULL;
523 size_t logmsg_len;
525 *logmsg = NULL;
527 if (stat(logmsg_path, &st) == -1)
528 return got_error_from_errno2("stat", logmsg_path);
530 if (spawn_editor(editor, logmsg_path) == -1)
531 return got_error_from_errno("failed spawning editor");
533 if (require_modification) {
534 struct timespec timeout;
536 timeout.tv_sec = 0;
537 timeout.tv_nsec = 1;
538 nanosleep(&timeout, NULL);
541 if (stat(logmsg_path, &st2) == -1)
542 return got_error_from_errno2("stat", logmsg_path);
544 if (require_modification && st.st_size == st2.st_size &&
545 timespeccmp(&st.st_mtim, &st2.st_mtim, ==))
546 return got_error_msg(GOT_ERR_COMMIT_MSG_EMPTY,
547 "no changes made to commit message, aborting");
549 fp = fopen(logmsg_path, "re");
550 if (fp == NULL) {
551 err = got_error_from_errno("fopen");
552 goto done;
555 /* strip comments and leading/trailing newlines */
556 err = read_logmsg(logmsg, &logmsg_len, fp, st2.st_size);
557 if (err)
558 goto done;
559 if (logmsg_len == 0) {
560 err = got_error_msg(GOT_ERR_COMMIT_MSG_EMPTY,
561 "commit message cannot be empty, aborting");
562 goto done;
564 done:
565 if (fp && fclose(fp) == EOF && err == NULL)
566 err = got_error_from_errno("fclose");
567 if (err) {
568 free(*logmsg);
569 *logmsg = NULL;
571 return err;
574 static const struct got_error *
575 collect_import_msg(char **logmsg, char **logmsg_path, const char *editor,
576 const char *path_dir, const char *branch_name)
578 char *initial_content = NULL;
579 const struct got_error *err = NULL;
580 int initial_content_len;
581 int fd = -1;
583 initial_content_len = asprintf(&initial_content,
584 "\n# %s to be imported to branch %s\n", path_dir,
585 branch_name);
586 if (initial_content_len == -1)
587 return got_error_from_errno("asprintf");
589 err = got_opentemp_named_fd(logmsg_path, &fd,
590 GOT_TMPDIR_STR "/got-importmsg", "");
591 if (err)
592 goto done;
594 if (write(fd, initial_content, initial_content_len) == -1) {
595 err = got_error_from_errno2("write", *logmsg_path);
596 goto done;
598 if (close(fd) == -1) {
599 err = got_error_from_errno2("close", *logmsg_path);
600 goto done;
602 fd = -1;
604 err = edit_logmsg(logmsg, editor, *logmsg_path, initial_content,
605 initial_content_len, 1);
606 done:
607 if (fd != -1 && close(fd) == -1 && err == NULL)
608 err = got_error_from_errno2("close", *logmsg_path);
609 free(initial_content);
610 if (err) {
611 free(*logmsg_path);
612 *logmsg_path = NULL;
614 return err;
617 static const struct got_error *
618 import_progress(void *arg, const char *path)
620 printf("A %s\n", path);
621 return NULL;
624 static const struct got_error *
625 valid_author(const char *author)
627 const char *email = author;
630 * Git' expects the author (or committer) to be in the form
631 * "name <email>", which are mostly free form (see the
632 * "committer" description in git-fast-import(1)). We're only
633 * doing this to avoid git's object parser breaking on commits
634 * we create.
637 while (*author && *author != '\n' && *author != '<' && *author != '>')
638 author++;
639 if (author != email && *author == '<' && *(author - 1) != ' ')
640 return got_error_fmt(GOT_ERR_COMMIT_BAD_AUTHOR, "%s: space "
641 "between author name and email required", email);
642 if (*author++ != '<')
643 return got_error_fmt(GOT_ERR_COMMIT_NO_EMAIL, "%s", email);
644 while (*author && *author != '\n' && *author != '<' && *author != '>')
645 author++;
646 if (strcmp(author, ">") != 0)
647 return got_error_fmt(GOT_ERR_COMMIT_NO_EMAIL, "%s", email);
648 return NULL;
651 static const struct got_error *
652 get_author(char **author, struct got_repository *repo,
653 struct got_worktree *worktree)
655 const struct got_error *err = NULL;
656 const char *got_author = NULL, *name, *email;
657 const struct got_gotconfig *worktree_conf = NULL, *repo_conf = NULL;
659 *author = NULL;
661 if (worktree)
662 worktree_conf = got_worktree_get_gotconfig(worktree);
663 repo_conf = got_repo_get_gotconfig(repo);
666 * Priority of potential author information sources, from most
667 * significant to least significant:
668 * 1) work tree's .got/got.conf file
669 * 2) repository's got.conf file
670 * 3) repository's git config file
671 * 4) environment variables
672 * 5) global git config files (in user's home directory or /etc)
675 if (worktree_conf)
676 got_author = got_gotconfig_get_author(worktree_conf);
677 if (got_author == NULL)
678 got_author = got_gotconfig_get_author(repo_conf);
679 if (got_author == NULL) {
680 name = got_repo_get_gitconfig_author_name(repo);
681 email = got_repo_get_gitconfig_author_email(repo);
682 if (name && email) {
683 if (asprintf(author, "%s <%s>", name, email) == -1)
684 return got_error_from_errno("asprintf");
685 return NULL;
688 got_author = getenv("GOT_AUTHOR");
689 if (got_author == NULL) {
690 name = got_repo_get_global_gitconfig_author_name(repo);
691 email = got_repo_get_global_gitconfig_author_email(
692 repo);
693 if (name && email) {
694 if (asprintf(author, "%s <%s>", name, email)
695 == -1)
696 return got_error_from_errno("asprintf");
697 return NULL;
699 /* TODO: Look up user in password database? */
700 return got_error(GOT_ERR_COMMIT_NO_AUTHOR);
704 *author = strdup(got_author);
705 if (*author == NULL)
706 return got_error_from_errno("strdup");
708 err = valid_author(*author);
709 if (err) {
710 free(*author);
711 *author = NULL;
713 return err;
716 static const struct got_error *
717 get_allowed_signers(char **allowed_signers, struct got_repository *repo,
718 struct got_worktree *worktree)
720 const char *got_allowed_signers = NULL;
721 const struct got_gotconfig *worktree_conf = NULL, *repo_conf = NULL;
723 *allowed_signers = NULL;
725 if (worktree)
726 worktree_conf = got_worktree_get_gotconfig(worktree);
727 repo_conf = got_repo_get_gotconfig(repo);
730 * Priority of potential author information sources, from most
731 * significant to least significant:
732 * 1) work tree's .got/got.conf file
733 * 2) repository's got.conf file
736 if (worktree_conf)
737 got_allowed_signers = got_gotconfig_get_allowed_signers_file(
738 worktree_conf);
739 if (got_allowed_signers == NULL)
740 got_allowed_signers = got_gotconfig_get_allowed_signers_file(
741 repo_conf);
743 if (got_allowed_signers) {
744 *allowed_signers = strdup(got_allowed_signers);
745 if (*allowed_signers == NULL)
746 return got_error_from_errno("strdup");
748 return NULL;
751 static const struct got_error *
752 get_revoked_signers(char **revoked_signers, struct got_repository *repo,
753 struct got_worktree *worktree)
755 const char *got_revoked_signers = NULL;
756 const struct got_gotconfig *worktree_conf = NULL, *repo_conf = NULL;
758 *revoked_signers = NULL;
760 if (worktree)
761 worktree_conf = got_worktree_get_gotconfig(worktree);
762 repo_conf = got_repo_get_gotconfig(repo);
765 * Priority of potential author information sources, from most
766 * significant to least significant:
767 * 1) work tree's .got/got.conf file
768 * 2) repository's got.conf file
771 if (worktree_conf)
772 got_revoked_signers = got_gotconfig_get_revoked_signers_file(
773 worktree_conf);
774 if (got_revoked_signers == NULL)
775 got_revoked_signers = got_gotconfig_get_revoked_signers_file(
776 repo_conf);
778 if (got_revoked_signers) {
779 *revoked_signers = strdup(got_revoked_signers);
780 if (*revoked_signers == NULL)
781 return got_error_from_errno("strdup");
783 return NULL;
786 static const char *
787 get_signer_id(struct got_repository *repo, struct got_worktree *worktree)
789 const char *got_signer_id = NULL;
790 const struct got_gotconfig *worktree_conf = NULL, *repo_conf = NULL;
792 if (worktree)
793 worktree_conf = got_worktree_get_gotconfig(worktree);
794 repo_conf = got_repo_get_gotconfig(repo);
797 * Priority of potential author information sources, from most
798 * significant to least significant:
799 * 1) work tree's .got/got.conf file
800 * 2) repository's got.conf file
803 if (worktree_conf)
804 got_signer_id = got_gotconfig_get_signer_id(worktree_conf);
805 if (got_signer_id == NULL)
806 got_signer_id = got_gotconfig_get_signer_id(repo_conf);
808 return got_signer_id;
811 static const struct got_error *
812 get_gitconfig_path(char **gitconfig_path)
814 const char *homedir = getenv("HOME");
816 *gitconfig_path = NULL;
817 if (homedir) {
818 if (asprintf(gitconfig_path, "%s/.gitconfig", homedir) == -1)
819 return got_error_from_errno("asprintf");
822 return NULL;
825 static const struct got_error *
826 cmd_import(int argc, char *argv[])
828 const struct got_error *error = NULL;
829 char *path_dir = NULL, *repo_path = NULL, *logmsg = NULL;
830 char *gitconfig_path = NULL, *editor = NULL, *author = NULL;
831 const char *branch_name = NULL;
832 char *id_str = NULL, *logmsg_path = NULL;
833 char refname[PATH_MAX] = "refs/heads/";
834 struct got_repository *repo = NULL;
835 struct got_reference *branch_ref = NULL, *head_ref = NULL;
836 struct got_object_id *new_commit_id = NULL;
837 int ch, n = 0;
838 struct got_pathlist_head ignores;
839 struct got_pathlist_entry *pe;
840 int preserve_logmsg = 0;
841 int *pack_fds = NULL;
843 TAILQ_INIT(&ignores);
845 #ifndef PROFILE
846 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
847 "unveil",
848 NULL) == -1)
849 err(1, "pledge");
850 #endif
852 while ((ch = getopt(argc, argv, "b:I:m:r:")) != -1) {
853 switch (ch) {
854 case 'b':
855 branch_name = optarg;
856 break;
857 case 'I':
858 if (optarg[0] == '\0')
859 break;
860 error = got_pathlist_insert(&pe, &ignores, optarg,
861 NULL);
862 if (error)
863 goto done;
864 break;
865 case 'm':
866 logmsg = strdup(optarg);
867 if (logmsg == NULL) {
868 error = got_error_from_errno("strdup");
869 goto done;
871 break;
872 case 'r':
873 repo_path = realpath(optarg, NULL);
874 if (repo_path == NULL) {
875 error = got_error_from_errno2("realpath",
876 optarg);
877 goto done;
879 break;
880 default:
881 usage_import();
882 /* NOTREACHED */
886 argc -= optind;
887 argv += optind;
889 if (argc != 1)
890 usage_import();
892 if (repo_path == NULL) {
893 repo_path = getcwd(NULL, 0);
894 if (repo_path == NULL)
895 return got_error_from_errno("getcwd");
897 got_path_strip_trailing_slashes(repo_path);
898 error = get_gitconfig_path(&gitconfig_path);
899 if (error)
900 goto done;
901 error = got_repo_pack_fds_open(&pack_fds);
902 if (error != NULL)
903 goto done;
904 error = got_repo_open(&repo, repo_path, gitconfig_path, pack_fds);
905 if (error)
906 goto done;
908 path_dir = realpath(argv[0], NULL);
909 if (path_dir == NULL) {
910 error = got_error_from_errno2("realpath", argv[0]);
911 goto done;
913 got_path_strip_trailing_slashes(path_dir);
915 error = get_editor(&editor);
916 if (error)
917 goto done;
919 if (unveil(path_dir, "r") != 0) {
920 error = got_error_from_errno2("unveil", path_dir);
921 goto done;
923 if (unveil(editor, "x") != 0) {
924 error = got_error_from_errno2("unveil", editor);
925 goto done;
927 error = apply_unveil(got_repo_get_path(repo), 0, NULL);
928 if (error)
929 goto done;
931 error = get_author(&author, repo, NULL);
932 if (error)
933 return error;
936 * Don't let the user create a branch name with a leading '-'.
937 * While technically a valid reference name, this case is usually
938 * an unintended typo.
940 if (branch_name && branch_name[0] == '-')
941 return got_error_path(branch_name, GOT_ERR_REF_NAME_MINUS);
943 error = got_ref_open(&head_ref, repo, GOT_REF_HEAD, 0);
944 if (error && error->code != GOT_ERR_NOT_REF)
945 goto done;
947 if (branch_name)
948 n = strlcat(refname, branch_name, sizeof(refname));
949 else if (head_ref && got_ref_is_symbolic(head_ref))
950 n = strlcpy(refname, got_ref_get_symref_target(head_ref),
951 sizeof(refname));
952 else
953 n = strlcat(refname, "main", sizeof(refname));
954 if (n >= sizeof(refname)) {
955 error = got_error(GOT_ERR_NO_SPACE);
956 goto done;
959 error = got_ref_open(&branch_ref, repo, refname, 0);
960 if (error) {
961 if (error->code != GOT_ERR_NOT_REF)
962 goto done;
963 } else {
964 error = got_error_msg(GOT_ERR_BRANCH_EXISTS,
965 "import target branch already exists");
966 goto done;
969 if (logmsg == NULL || *logmsg == '\0') {
970 free(logmsg);
971 error = collect_import_msg(&logmsg, &logmsg_path, editor,
972 path_dir, refname);
973 if (error) {
974 if (error->code != GOT_ERR_COMMIT_MSG_EMPTY &&
975 logmsg_path != NULL)
976 preserve_logmsg = 1;
977 goto done;
981 error = got_repo_import(&new_commit_id, path_dir, logmsg,
982 author, &ignores, repo, import_progress, NULL);
983 if (error) {
984 if (logmsg_path)
985 preserve_logmsg = 1;
986 goto done;
989 error = got_ref_alloc(&branch_ref, refname, new_commit_id);
990 if (error) {
991 if (logmsg_path)
992 preserve_logmsg = 1;
993 goto done;
996 error = got_ref_write(branch_ref, repo);
997 if (error) {
998 if (logmsg_path)
999 preserve_logmsg = 1;
1000 goto done;
1003 error = got_object_id_str(&id_str, new_commit_id);
1004 if (error) {
1005 if (logmsg_path)
1006 preserve_logmsg = 1;
1007 goto done;
1010 error = got_ref_open(&head_ref, repo, GOT_REF_HEAD, 0);
1011 if (error) {
1012 if (error->code != GOT_ERR_NOT_REF) {
1013 if (logmsg_path)
1014 preserve_logmsg = 1;
1015 goto done;
1018 error = got_ref_alloc_symref(&head_ref, GOT_REF_HEAD,
1019 branch_ref);
1020 if (error) {
1021 if (logmsg_path)
1022 preserve_logmsg = 1;
1023 goto done;
1026 error = got_ref_write(head_ref, repo);
1027 if (error) {
1028 if (logmsg_path)
1029 preserve_logmsg = 1;
1030 goto done;
1034 printf("Created branch %s with commit %s\n",
1035 got_ref_get_name(branch_ref), id_str);
1036 done:
1037 if (pack_fds) {
1038 const struct got_error *pack_err =
1039 got_repo_pack_fds_close(pack_fds);
1040 if (error == NULL)
1041 error = pack_err;
1043 if (repo) {
1044 const struct got_error *close_err = got_repo_close(repo);
1045 if (error == NULL)
1046 error = close_err;
1048 if (preserve_logmsg) {
1049 fprintf(stderr, "%s: log message preserved in %s\n",
1050 getprogname(), logmsg_path);
1051 } else if (logmsg_path && unlink(logmsg_path) == -1 && error == NULL)
1052 error = got_error_from_errno2("unlink", logmsg_path);
1053 free(logmsg);
1054 free(logmsg_path);
1055 free(repo_path);
1056 free(editor);
1057 free(new_commit_id);
1058 free(id_str);
1059 free(author);
1060 free(gitconfig_path);
1061 if (branch_ref)
1062 got_ref_close(branch_ref);
1063 if (head_ref)
1064 got_ref_close(head_ref);
1065 return error;
1068 __dead static void
1069 usage_clone(void)
1071 fprintf(stderr, "usage: %s clone [-almqv] [-b branch] [-R reference] "
1072 "repository-URL [directory]\n", getprogname());
1073 exit(1);
1076 struct got_fetch_progress_arg {
1077 char last_scaled_size[FMT_SCALED_STRSIZE];
1078 int last_p_indexed;
1079 int last_p_resolved;
1080 int verbosity;
1082 struct got_repository *repo;
1084 int create_configs;
1085 int configs_created;
1086 struct {
1087 struct got_pathlist_head *symrefs;
1088 struct got_pathlist_head *wanted_branches;
1089 struct got_pathlist_head *wanted_refs;
1090 const char *proto;
1091 const char *host;
1092 const char *port;
1093 const char *remote_repo_path;
1094 const char *git_url;
1095 int fetch_all_branches;
1096 int mirror_references;
1097 } config_info;
1100 /* XXX forward declaration */
1101 static const struct got_error *
1102 create_config_files(const char *proto, const char *host, const char *port,
1103 const char *remote_repo_path, const char *git_url, int fetch_all_branches,
1104 int mirror_references, struct got_pathlist_head *symrefs,
1105 struct got_pathlist_head *wanted_branches,
1106 struct got_pathlist_head *wanted_refs, struct got_repository *repo);
1108 static const struct got_error *
1109 fetch_progress(void *arg, const char *message, off_t packfile_size,
1110 int nobj_total, int nobj_indexed, int nobj_loose, int nobj_resolved)
1112 const struct got_error *err = NULL;
1113 struct got_fetch_progress_arg *a = arg;
1114 char scaled_size[FMT_SCALED_STRSIZE];
1115 int p_indexed, p_resolved;
1116 int print_size = 0, print_indexed = 0, print_resolved = 0;
1119 * In order to allow a failed clone to be resumed with 'got fetch'
1120 * we try to create configuration files as soon as possible.
1121 * Once the server has sent information about its default branch
1122 * we have all required information.
1124 if (a->create_configs && !a->configs_created &&
1125 !TAILQ_EMPTY(a->config_info.symrefs)) {
1126 err = create_config_files(a->config_info.proto,
1127 a->config_info.host, a->config_info.port,
1128 a->config_info.remote_repo_path,
1129 a->config_info.git_url,
1130 a->config_info.fetch_all_branches,
1131 a->config_info.mirror_references,
1132 a->config_info.symrefs,
1133 a->config_info.wanted_branches,
1134 a->config_info.wanted_refs, a->repo);
1135 if (err)
1136 return err;
1137 a->configs_created = 1;
1140 if (a->verbosity < 0)
1141 return NULL;
1143 if (message && message[0] != '\0') {
1144 printf("\rserver: %s", message);
1145 fflush(stdout);
1146 return NULL;
1149 if (packfile_size > 0 || nobj_indexed > 0) {
1150 if (fmt_scaled(packfile_size, scaled_size) == 0 &&
1151 (a->last_scaled_size[0] == '\0' ||
1152 strcmp(scaled_size, a->last_scaled_size)) != 0) {
1153 print_size = 1;
1154 if (strlcpy(a->last_scaled_size, scaled_size,
1155 FMT_SCALED_STRSIZE) >= FMT_SCALED_STRSIZE)
1156 return got_error(GOT_ERR_NO_SPACE);
1158 if (nobj_indexed > 0) {
1159 p_indexed = (nobj_indexed * 100) / nobj_total;
1160 if (p_indexed != a->last_p_indexed) {
1161 a->last_p_indexed = p_indexed;
1162 print_indexed = 1;
1163 print_size = 1;
1166 if (nobj_resolved > 0) {
1167 p_resolved = (nobj_resolved * 100) /
1168 (nobj_total - nobj_loose);
1169 if (p_resolved != a->last_p_resolved) {
1170 a->last_p_resolved = p_resolved;
1171 print_resolved = 1;
1172 print_indexed = 1;
1173 print_size = 1;
1178 if (print_size || print_indexed || print_resolved)
1179 printf("\r");
1180 if (print_size)
1181 printf("%*s fetched", FMT_SCALED_STRSIZE - 2, scaled_size);
1182 if (print_indexed)
1183 printf("; indexing %d%%", p_indexed);
1184 if (print_resolved)
1185 printf("; resolving deltas %d%%", p_resolved);
1186 if (print_size || print_indexed || print_resolved)
1187 fflush(stdout);
1189 return NULL;
1192 static const struct got_error *
1193 create_symref(const char *refname, struct got_reference *target_ref,
1194 int verbosity, struct got_repository *repo)
1196 const struct got_error *err;
1197 struct got_reference *head_symref;
1199 err = got_ref_alloc_symref(&head_symref, refname, target_ref);
1200 if (err)
1201 return err;
1203 err = got_ref_write(head_symref, repo);
1204 if (err == NULL && verbosity > 0) {
1205 printf("Created reference %s: %s\n", GOT_REF_HEAD,
1206 got_ref_get_name(target_ref));
1208 got_ref_close(head_symref);
1209 return err;
1212 static const struct got_error *
1213 list_remote_refs(struct got_pathlist_head *symrefs,
1214 struct got_pathlist_head *refs)
1216 const struct got_error *err;
1217 struct got_pathlist_entry *pe;
1219 TAILQ_FOREACH(pe, symrefs, entry) {
1220 const char *refname = pe->path;
1221 const char *targetref = pe->data;
1223 printf("%s: %s\n", refname, targetref);
1226 TAILQ_FOREACH(pe, refs, entry) {
1227 const char *refname = pe->path;
1228 struct got_object_id *id = pe->data;
1229 char *id_str;
1231 err = got_object_id_str(&id_str, id);
1232 if (err)
1233 return err;
1234 printf("%s: %s\n", refname, id_str);
1235 free(id_str);
1238 return NULL;
1241 static const struct got_error *
1242 create_ref(const char *refname, struct got_object_id *id,
1243 int verbosity, struct got_repository *repo)
1245 const struct got_error *err = NULL;
1246 struct got_reference *ref;
1247 char *id_str;
1249 err = got_object_id_str(&id_str, id);
1250 if (err)
1251 return err;
1253 err = got_ref_alloc(&ref, refname, id);
1254 if (err)
1255 goto done;
1257 err = got_ref_write(ref, repo);
1258 got_ref_close(ref);
1260 if (err == NULL && verbosity >= 0)
1261 printf("Created reference %s: %s\n", refname, id_str);
1262 done:
1263 free(id_str);
1264 return err;
1267 static int
1268 match_wanted_ref(const char *refname, const char *wanted_ref)
1270 if (strncmp(refname, "refs/", 5) != 0)
1271 return 0;
1272 refname += 5;
1275 * Prevent fetching of references that won't make any
1276 * sense outside of the remote repository's context.
1278 if (strncmp(refname, "got/", 4) == 0)
1279 return 0;
1280 if (strncmp(refname, "remotes/", 8) == 0)
1281 return 0;
1283 if (strncmp(wanted_ref, "refs/", 5) == 0)
1284 wanted_ref += 5;
1286 /* Allow prefix match. */
1287 if (got_path_is_child(refname, wanted_ref, strlen(wanted_ref)))
1288 return 1;
1290 /* Allow exact match. */
1291 return (strcmp(refname, wanted_ref) == 0);
1294 static int
1295 is_wanted_ref(struct got_pathlist_head *wanted_refs, const char *refname)
1297 struct got_pathlist_entry *pe;
1299 TAILQ_FOREACH(pe, wanted_refs, entry) {
1300 if (match_wanted_ref(refname, pe->path))
1301 return 1;
1304 return 0;
1307 static const struct got_error *
1308 create_wanted_ref(const char *refname, struct got_object_id *id,
1309 const char *remote_repo_name, int verbosity, struct got_repository *repo)
1311 const struct got_error *err;
1312 char *remote_refname;
1314 if (strncmp("refs/", refname, 5) == 0)
1315 refname += 5;
1317 if (asprintf(&remote_refname, "refs/remotes/%s/%s",
1318 remote_repo_name, refname) == -1)
1319 return got_error_from_errno("asprintf");
1321 err = create_ref(remote_refname, id, verbosity, repo);
1322 free(remote_refname);
1323 return err;
1326 static const struct got_error *
1327 create_gotconfig(const char *proto, const char *host, const char *port,
1328 const char *remote_repo_path, const char *default_branch,
1329 int fetch_all_branches, struct got_pathlist_head *wanted_branches,
1330 struct got_pathlist_head *wanted_refs, int mirror_references,
1331 struct got_repository *repo)
1333 const struct got_error *err = NULL;
1334 char *gotconfig_path = NULL;
1335 char *gotconfig = NULL;
1336 FILE *gotconfig_file = NULL;
1337 const char *branchname = NULL;
1338 char *branches = NULL, *refs = NULL;
1339 ssize_t n;
1341 if (!fetch_all_branches && !TAILQ_EMPTY(wanted_branches)) {
1342 struct got_pathlist_entry *pe;
1343 TAILQ_FOREACH(pe, wanted_branches, entry) {
1344 char *s;
1345 branchname = pe->path;
1346 if (strncmp(branchname, "refs/heads/", 11) == 0)
1347 branchname += 11;
1348 if (asprintf(&s, "%s\"%s\" ",
1349 branches ? branches : "", branchname) == -1) {
1350 err = got_error_from_errno("asprintf");
1351 goto done;
1353 free(branches);
1354 branches = s;
1356 } else if (!fetch_all_branches && default_branch) {
1357 branchname = default_branch;
1358 if (strncmp(branchname, "refs/heads/", 11) == 0)
1359 branchname += 11;
1360 if (asprintf(&branches, "\"%s\" ", branchname) == -1) {
1361 err = got_error_from_errno("asprintf");
1362 goto done;
1365 if (!TAILQ_EMPTY(wanted_refs)) {
1366 struct got_pathlist_entry *pe;
1367 TAILQ_FOREACH(pe, wanted_refs, entry) {
1368 char *s;
1369 const char *refname = pe->path;
1370 if (strncmp(refname, "refs/", 5) == 0)
1371 branchname += 5;
1372 if (asprintf(&s, "%s\"%s\" ",
1373 refs ? refs : "", refname) == -1) {
1374 err = got_error_from_errno("asprintf");
1375 goto done;
1377 free(refs);
1378 refs = s;
1382 /* Create got.conf(5). */
1383 gotconfig_path = got_repo_get_path_gotconfig(repo);
1384 if (gotconfig_path == NULL) {
1385 err = got_error_from_errno("got_repo_get_path_gotconfig");
1386 goto done;
1388 gotconfig_file = fopen(gotconfig_path, "ae");
1389 if (gotconfig_file == NULL) {
1390 err = got_error_from_errno2("fopen", gotconfig_path);
1391 goto done;
1393 if (asprintf(&gotconfig,
1394 "remote \"%s\" {\n"
1395 "\tserver %s\n"
1396 "\tprotocol %s\n"
1397 "%s%s%s"
1398 "\trepository \"%s\"\n"
1399 "%s%s%s"
1400 "%s%s%s"
1401 "%s"
1402 "%s"
1403 "}\n",
1404 GOT_FETCH_DEFAULT_REMOTE_NAME, host, proto,
1405 port ? "\tport " : "", port ? port : "", port ? "\n" : "",
1406 remote_repo_path, branches ? "\tbranch { " : "",
1407 branches ? branches : "", branches ? "}\n" : "",
1408 refs ? "\treference { " : "", refs ? refs : "", refs ? "}\n" : "",
1409 mirror_references ? "\tmirror_references yes\n" : "",
1410 fetch_all_branches ? "\tfetch_all_branches yes\n" : "") == -1) {
1411 err = got_error_from_errno("asprintf");
1412 goto done;
1414 n = fwrite(gotconfig, 1, strlen(gotconfig), gotconfig_file);
1415 if (n != strlen(gotconfig)) {
1416 err = got_ferror(gotconfig_file, GOT_ERR_IO);
1417 goto done;
1420 done:
1421 if (gotconfig_file && fclose(gotconfig_file) == EOF && err == NULL)
1422 err = got_error_from_errno2("fclose", gotconfig_path);
1423 free(gotconfig_path);
1424 free(branches);
1425 return err;
1428 static const struct got_error *
1429 create_gitconfig(const char *git_url, const char *default_branch,
1430 int fetch_all_branches, struct got_pathlist_head *wanted_branches,
1431 struct got_pathlist_head *wanted_refs, int mirror_references,
1432 struct got_repository *repo)
1434 const struct got_error *err = NULL;
1435 char *gitconfig_path = NULL;
1436 char *gitconfig = NULL;
1437 FILE *gitconfig_file = NULL;
1438 char *branches = NULL, *refs = NULL;
1439 const char *branchname;
1440 ssize_t n;
1442 /* Create a config file Git can understand. */
1443 gitconfig_path = got_repo_get_path_gitconfig(repo);
1444 if (gitconfig_path == NULL) {
1445 err = got_error_from_errno("got_repo_get_path_gitconfig");
1446 goto done;
1448 gitconfig_file = fopen(gitconfig_path, "ae");
1449 if (gitconfig_file == NULL) {
1450 err = got_error_from_errno2("fopen", gitconfig_path);
1451 goto done;
1453 if (fetch_all_branches) {
1454 if (mirror_references) {
1455 if (asprintf(&branches,
1456 "\tfetch = refs/heads/*:refs/heads/*\n") == -1) {
1457 err = got_error_from_errno("asprintf");
1458 goto done;
1460 } else if (asprintf(&branches,
1461 "\tfetch = refs/heads/*:refs/remotes/%s/*\n",
1462 GOT_FETCH_DEFAULT_REMOTE_NAME) == -1) {
1463 err = got_error_from_errno("asprintf");
1464 goto done;
1466 } else if (!TAILQ_EMPTY(wanted_branches)) {
1467 struct got_pathlist_entry *pe;
1468 TAILQ_FOREACH(pe, wanted_branches, entry) {
1469 char *s;
1470 branchname = pe->path;
1471 if (strncmp(branchname, "refs/heads/", 11) == 0)
1472 branchname += 11;
1473 if (mirror_references) {
1474 if (asprintf(&s,
1475 "%s\tfetch = refs/heads/%s:refs/heads/%s\n",
1476 branches ? branches : "",
1477 branchname, branchname) == -1) {
1478 err = got_error_from_errno("asprintf");
1479 goto done;
1481 } else if (asprintf(&s,
1482 "%s\tfetch = refs/heads/%s:refs/remotes/%s/%s\n",
1483 branches ? branches : "",
1484 branchname, GOT_FETCH_DEFAULT_REMOTE_NAME,
1485 branchname) == -1) {
1486 err = got_error_from_errno("asprintf");
1487 goto done;
1489 free(branches);
1490 branches = s;
1492 } else {
1494 * If the server specified a default branch, use just that one.
1495 * Otherwise fall back to fetching all branches on next fetch.
1497 if (default_branch) {
1498 branchname = default_branch;
1499 if (strncmp(branchname, "refs/heads/", 11) == 0)
1500 branchname += 11;
1501 } else
1502 branchname = "*"; /* fall back to all branches */
1503 if (mirror_references) {
1504 if (asprintf(&branches,
1505 "\tfetch = refs/heads/%s:refs/heads/%s\n",
1506 branchname, branchname) == -1) {
1507 err = got_error_from_errno("asprintf");
1508 goto done;
1510 } else if (asprintf(&branches,
1511 "\tfetch = refs/heads/%s:refs/remotes/%s/%s\n",
1512 branchname, GOT_FETCH_DEFAULT_REMOTE_NAME,
1513 branchname) == -1) {
1514 err = got_error_from_errno("asprintf");
1515 goto done;
1518 if (!TAILQ_EMPTY(wanted_refs)) {
1519 struct got_pathlist_entry *pe;
1520 TAILQ_FOREACH(pe, wanted_refs, entry) {
1521 char *s;
1522 const char *refname = pe->path;
1523 if (strncmp(refname, "refs/", 5) == 0)
1524 refname += 5;
1525 if (mirror_references) {
1526 if (asprintf(&s,
1527 "%s\tfetch = refs/%s:refs/%s\n",
1528 refs ? refs : "", refname, refname) == -1) {
1529 err = got_error_from_errno("asprintf");
1530 goto done;
1532 } else if (asprintf(&s,
1533 "%s\tfetch = refs/%s:refs/remotes/%s/%s\n",
1534 refs ? refs : "",
1535 refname, GOT_FETCH_DEFAULT_REMOTE_NAME,
1536 refname) == -1) {
1537 err = got_error_from_errno("asprintf");
1538 goto done;
1540 free(refs);
1541 refs = s;
1545 if (asprintf(&gitconfig,
1546 "[remote \"%s\"]\n"
1547 "\turl = %s\n"
1548 "%s"
1549 "%s"
1550 "\tfetch = refs/tags/*:refs/tags/*\n",
1551 GOT_FETCH_DEFAULT_REMOTE_NAME, git_url, branches ? branches : "",
1552 refs ? refs : "") == -1) {
1553 err = got_error_from_errno("asprintf");
1554 goto done;
1556 n = fwrite(gitconfig, 1, strlen(gitconfig), gitconfig_file);
1557 if (n != strlen(gitconfig)) {
1558 err = got_ferror(gitconfig_file, GOT_ERR_IO);
1559 goto done;
1561 done:
1562 if (gitconfig_file && fclose(gitconfig_file) == EOF && err == NULL)
1563 err = got_error_from_errno2("fclose", gitconfig_path);
1564 free(gitconfig_path);
1565 free(branches);
1566 return err;
1569 static const struct got_error *
1570 create_config_files(const char *proto, const char *host, const char *port,
1571 const char *remote_repo_path, const char *git_url, int fetch_all_branches,
1572 int mirror_references, struct got_pathlist_head *symrefs,
1573 struct got_pathlist_head *wanted_branches,
1574 struct got_pathlist_head *wanted_refs, struct got_repository *repo)
1576 const struct got_error *err = NULL;
1577 const char *default_branch = NULL;
1578 struct got_pathlist_entry *pe;
1581 * If we asked for a set of wanted branches then use the first
1582 * one of those.
1584 if (!TAILQ_EMPTY(wanted_branches)) {
1585 pe = TAILQ_FIRST(wanted_branches);
1586 default_branch = pe->path;
1587 } else {
1588 /* First HEAD ref listed by server is the default branch. */
1589 TAILQ_FOREACH(pe, symrefs, entry) {
1590 const char *refname = pe->path;
1591 const char *target = pe->data;
1593 if (strcmp(refname, GOT_REF_HEAD) != 0)
1594 continue;
1596 default_branch = target;
1597 break;
1601 /* Create got.conf(5). */
1602 err = create_gotconfig(proto, host, port, remote_repo_path,
1603 default_branch, fetch_all_branches, wanted_branches,
1604 wanted_refs, mirror_references, repo);
1605 if (err)
1606 return err;
1608 /* Create a config file Git can understand. */
1609 return create_gitconfig(git_url, default_branch, fetch_all_branches,
1610 wanted_branches, wanted_refs, mirror_references, repo);
1613 static const struct got_error *
1614 cmd_clone(int argc, char *argv[])
1616 const struct got_error *error = NULL;
1617 const char *uri, *dirname;
1618 char *proto, *host, *port, *repo_name, *server_path;
1619 char *default_destdir = NULL, *id_str = NULL;
1620 const char *repo_path;
1621 struct got_repository *repo = NULL;
1622 struct got_pathlist_head refs, symrefs, wanted_branches, wanted_refs;
1623 struct got_pathlist_entry *pe;
1624 struct got_object_id *pack_hash = NULL;
1625 int ch, fetchfd = -1, fetchstatus;
1626 pid_t fetchpid = -1;
1627 struct got_fetch_progress_arg fpa;
1628 char *git_url = NULL;
1629 int verbosity = 0, fetch_all_branches = 0, mirror_references = 0;
1630 int bflag = 0, list_refs_only = 0;
1631 int *pack_fds = NULL;
1633 TAILQ_INIT(&refs);
1634 TAILQ_INIT(&symrefs);
1635 TAILQ_INIT(&wanted_branches);
1636 TAILQ_INIT(&wanted_refs);
1638 while ((ch = getopt(argc, argv, "ab:lmqR:v")) != -1) {
1639 switch (ch) {
1640 case 'a':
1641 fetch_all_branches = 1;
1642 break;
1643 case 'b':
1644 error = got_pathlist_insert(NULL, &wanted_branches,
1645 optarg, NULL);
1646 if (error)
1647 return error;
1648 bflag = 1;
1649 break;
1650 case 'l':
1651 list_refs_only = 1;
1652 break;
1653 case 'm':
1654 mirror_references = 1;
1655 break;
1656 case 'q':
1657 verbosity = -1;
1658 break;
1659 case 'R':
1660 error = got_pathlist_insert(NULL, &wanted_refs,
1661 optarg, NULL);
1662 if (error)
1663 return error;
1664 break;
1665 case 'v':
1666 if (verbosity < 0)
1667 verbosity = 0;
1668 else if (verbosity < 3)
1669 verbosity++;
1670 break;
1671 default:
1672 usage_clone();
1673 break;
1676 argc -= optind;
1677 argv += optind;
1679 if (fetch_all_branches && !TAILQ_EMPTY(&wanted_branches))
1680 option_conflict('a', 'b');
1681 if (list_refs_only) {
1682 if (!TAILQ_EMPTY(&wanted_branches))
1683 option_conflict('l', 'b');
1684 if (fetch_all_branches)
1685 option_conflict('l', 'a');
1686 if (mirror_references)
1687 option_conflict('l', 'm');
1688 if (!TAILQ_EMPTY(&wanted_refs))
1689 option_conflict('l', 'R');
1692 uri = argv[0];
1694 if (argc == 1)
1695 dirname = NULL;
1696 else if (argc == 2)
1697 dirname = argv[1];
1698 else
1699 usage_clone();
1701 error = got_dial_parse_uri(&proto, &host, &port, &server_path,
1702 &repo_name, uri);
1703 if (error)
1704 goto done;
1706 if (asprintf(&git_url, "%s://%s%s%s%s%s", proto,
1707 host, port ? ":" : "", port ? port : "",
1708 server_path[0] != '/' ? "/" : "", server_path) == -1) {
1709 error = got_error_from_errno("asprintf");
1710 goto done;
1713 if (strcmp(proto, "git") == 0) {
1714 #ifndef PROFILE
1715 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
1716 "sendfd dns inet unveil", NULL) == -1)
1717 err(1, "pledge");
1718 #endif
1719 } else if (strcmp(proto, "git+ssh") == 0 ||
1720 strcmp(proto, "ssh") == 0 ||
1721 strcmp(proto, "git+http") == 0 ||
1722 strcmp(proto, "http") == 0 ||
1723 strcmp(proto, "git+https") == 0 ||
1724 strcmp(proto, "https") == 0) {
1725 #ifndef PROFILE
1726 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
1727 "sendfd unveil", NULL) == -1)
1728 err(1, "pledge");
1729 #endif
1730 } else {
1731 error = got_error_path(proto, GOT_ERR_BAD_PROTO);
1732 goto done;
1734 if (dirname == NULL) {
1735 if (asprintf(&default_destdir, "%s.git", repo_name) == -1) {
1736 error = got_error_from_errno("asprintf");
1737 goto done;
1739 repo_path = default_destdir;
1740 } else
1741 repo_path = dirname;
1743 if (!list_refs_only) {
1744 error = got_path_mkdir(repo_path);
1745 if (error &&
1746 (!(error->code == GOT_ERR_ERRNO && errno == EISDIR) &&
1747 !(error->code == GOT_ERR_ERRNO && errno == EEXIST)))
1748 goto done;
1749 if (!got_path_dir_is_empty(repo_path)) {
1750 error = got_error_path(repo_path,
1751 GOT_ERR_DIR_NOT_EMPTY);
1752 goto done;
1756 error = got_dial_apply_unveil(proto);
1757 if (error)
1758 goto done;
1760 error = apply_unveil(repo_path, 0, NULL);
1761 if (error)
1762 goto done;
1764 if (verbosity >= 0)
1765 printf("Connecting to %s\n", git_url);
1767 error = got_fetch_connect(&fetchpid, &fetchfd, proto, host, port,
1768 server_path, verbosity);
1769 if (error)
1770 goto done;
1772 #ifndef PROFILE
1773 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd",
1774 NULL) == -1)
1775 err(1, "pledge");
1776 #endif
1777 if (!list_refs_only) {
1778 error = got_repo_init(repo_path, NULL, GOT_HASH_SHA1);
1779 if (error)
1780 goto done;
1781 error = got_repo_pack_fds_open(&pack_fds);
1782 if (error != NULL)
1783 goto done;
1784 error = got_repo_open(&repo, repo_path, NULL, pack_fds);
1785 if (error)
1786 goto done;
1789 fpa.last_scaled_size[0] = '\0';
1790 fpa.last_p_indexed = -1;
1791 fpa.last_p_resolved = -1;
1792 fpa.verbosity = verbosity;
1793 fpa.create_configs = 1;
1794 fpa.configs_created = 0;
1795 fpa.repo = repo;
1796 fpa.config_info.symrefs = &symrefs;
1797 fpa.config_info.wanted_branches = &wanted_branches;
1798 fpa.config_info.wanted_refs = &wanted_refs;
1799 fpa.config_info.proto = proto;
1800 fpa.config_info.host = host;
1801 fpa.config_info.port = port;
1802 fpa.config_info.remote_repo_path = server_path;
1803 fpa.config_info.git_url = git_url;
1804 fpa.config_info.fetch_all_branches = fetch_all_branches;
1805 fpa.config_info.mirror_references = mirror_references;
1806 error = got_fetch_pack(&pack_hash, &refs, &symrefs,
1807 GOT_FETCH_DEFAULT_REMOTE_NAME, mirror_references,
1808 fetch_all_branches, &wanted_branches, &wanted_refs,
1809 list_refs_only, verbosity, fetchfd, repo, NULL, NULL, bflag,
1810 fetch_progress, &fpa);
1811 if (error)
1812 goto done;
1814 if (list_refs_only) {
1815 error = list_remote_refs(&symrefs, &refs);
1816 goto done;
1819 if (pack_hash == NULL) {
1820 error = got_error_fmt(GOT_ERR_FETCH_FAILED, "%s",
1821 "server sent an empty pack file");
1822 goto done;
1824 error = got_object_id_str(&id_str, pack_hash);
1825 if (error)
1826 goto done;
1827 if (verbosity >= 0)
1828 printf("\nFetched %s.pack\n", id_str);
1829 free(id_str);
1831 /* Set up references provided with the pack file. */
1832 TAILQ_FOREACH(pe, &refs, entry) {
1833 const char *refname = pe->path;
1834 struct got_object_id *id = pe->data;
1835 char *remote_refname;
1837 if (is_wanted_ref(&wanted_refs, refname) &&
1838 !mirror_references) {
1839 error = create_wanted_ref(refname, id,
1840 GOT_FETCH_DEFAULT_REMOTE_NAME,
1841 verbosity - 1, repo);
1842 if (error)
1843 goto done;
1844 continue;
1847 error = create_ref(refname, id, verbosity - 1, repo);
1848 if (error)
1849 goto done;
1851 if (mirror_references)
1852 continue;
1854 if (strncmp("refs/heads/", refname, 11) != 0)
1855 continue;
1857 if (asprintf(&remote_refname,
1858 "refs/remotes/%s/%s", GOT_FETCH_DEFAULT_REMOTE_NAME,
1859 refname + 11) == -1) {
1860 error = got_error_from_errno("asprintf");
1861 goto done;
1863 error = create_ref(remote_refname, id, verbosity - 1, repo);
1864 free(remote_refname);
1865 if (error)
1866 goto done;
1869 /* Set the HEAD reference if the server provided one. */
1870 TAILQ_FOREACH(pe, &symrefs, entry) {
1871 struct got_reference *target_ref;
1872 const char *refname = pe->path;
1873 const char *target = pe->data;
1874 char *remote_refname = NULL, *remote_target = NULL;
1876 if (strcmp(refname, GOT_REF_HEAD) != 0)
1877 continue;
1879 error = got_ref_open(&target_ref, repo, target, 0);
1880 if (error) {
1881 if (error->code == GOT_ERR_NOT_REF) {
1882 error = NULL;
1883 continue;
1885 goto done;
1888 error = create_symref(refname, target_ref, verbosity, repo);
1889 got_ref_close(target_ref);
1890 if (error)
1891 goto done;
1893 if (mirror_references)
1894 continue;
1896 if (strncmp("refs/heads/", target, 11) != 0)
1897 continue;
1899 if (asprintf(&remote_refname,
1900 "refs/remotes/%s/%s", GOT_FETCH_DEFAULT_REMOTE_NAME,
1901 refname) == -1) {
1902 error = got_error_from_errno("asprintf");
1903 goto done;
1905 if (asprintf(&remote_target,
1906 "refs/remotes/%s/%s", GOT_FETCH_DEFAULT_REMOTE_NAME,
1907 target + 11) == -1) {
1908 error = got_error_from_errno("asprintf");
1909 free(remote_refname);
1910 goto done;
1912 error = got_ref_open(&target_ref, repo, remote_target, 0);
1913 if (error) {
1914 free(remote_refname);
1915 free(remote_target);
1916 if (error->code == GOT_ERR_NOT_REF) {
1917 error = NULL;
1918 continue;
1920 goto done;
1922 error = create_symref(remote_refname, target_ref,
1923 verbosity - 1, repo);
1924 free(remote_refname);
1925 free(remote_target);
1926 got_ref_close(target_ref);
1927 if (error)
1928 goto done;
1930 if (pe == NULL) {
1932 * We failed to set the HEAD reference. If we asked for
1933 * a set of wanted branches use the first of one of those
1934 * which could be fetched instead.
1936 TAILQ_FOREACH(pe, &wanted_branches, entry) {
1937 const char *target = pe->path;
1938 struct got_reference *target_ref;
1940 error = got_ref_open(&target_ref, repo, target, 0);
1941 if (error) {
1942 if (error->code == GOT_ERR_NOT_REF) {
1943 error = NULL;
1944 continue;
1946 goto done;
1949 error = create_symref(GOT_REF_HEAD, target_ref,
1950 verbosity, repo);
1951 got_ref_close(target_ref);
1952 if (error)
1953 goto done;
1954 break;
1957 if (!fpa.configs_created && pe != NULL) {
1958 error = create_config_files(fpa.config_info.proto,
1959 fpa.config_info.host, fpa.config_info.port,
1960 fpa.config_info.remote_repo_path,
1961 fpa.config_info.git_url,
1962 fpa.config_info.fetch_all_branches,
1963 fpa.config_info.mirror_references,
1964 fpa.config_info.symrefs,
1965 fpa.config_info.wanted_branches,
1966 fpa.config_info.wanted_refs, fpa.repo);
1967 if (error)
1968 goto done;
1972 if (verbosity >= 0)
1973 printf("Created %s repository '%s'\n",
1974 mirror_references ? "mirrored" : "cloned", repo_path);
1975 done:
1976 if (pack_fds) {
1977 const struct got_error *pack_err =
1978 got_repo_pack_fds_close(pack_fds);
1979 if (error == NULL)
1980 error = pack_err;
1982 if (fetchpid > 0) {
1983 if (kill(fetchpid, SIGTERM) == -1)
1984 error = got_error_from_errno("kill");
1985 if (waitpid(fetchpid, &fetchstatus, 0) == -1 && error == NULL)
1986 error = got_error_from_errno("waitpid");
1988 if (fetchfd != -1 && close(fetchfd) == -1 && error == NULL)
1989 error = got_error_from_errno("close");
1990 if (repo) {
1991 const struct got_error *close_err = got_repo_close(repo);
1992 if (error == NULL)
1993 error = close_err;
1995 got_pathlist_free(&refs, GOT_PATHLIST_FREE_ALL);
1996 got_pathlist_free(&symrefs, GOT_PATHLIST_FREE_ALL);
1997 got_pathlist_free(&wanted_branches, GOT_PATHLIST_FREE_NONE);
1998 got_pathlist_free(&wanted_refs, GOT_PATHLIST_FREE_NONE);
1999 free(pack_hash);
2000 free(proto);
2001 free(host);
2002 free(port);
2003 free(server_path);
2004 free(repo_name);
2005 free(default_destdir);
2006 free(git_url);
2007 return error;
2010 static const struct got_error *
2011 update_ref(struct got_reference *ref, struct got_object_id *new_id,
2012 int replace_tags, int verbosity, struct got_repository *repo)
2014 const struct got_error *err = NULL;
2015 char *new_id_str = NULL;
2016 struct got_object_id *old_id = NULL;
2018 err = got_object_id_str(&new_id_str, new_id);
2019 if (err)
2020 goto done;
2022 if (!replace_tags &&
2023 strncmp(got_ref_get_name(ref), "refs/tags/", 10) == 0) {
2024 err = got_ref_resolve(&old_id, repo, ref);
2025 if (err)
2026 goto done;
2027 if (got_object_id_cmp(old_id, new_id) == 0)
2028 goto done;
2029 if (verbosity >= 0) {
2030 printf("Rejecting update of existing tag %s: %s\n",
2031 got_ref_get_name(ref), new_id_str);
2033 goto done;
2036 if (got_ref_is_symbolic(ref)) {
2037 if (verbosity >= 0) {
2038 printf("Replacing reference %s: %s\n",
2039 got_ref_get_name(ref),
2040 got_ref_get_symref_target(ref));
2042 err = got_ref_change_symref_to_ref(ref, new_id);
2043 if (err)
2044 goto done;
2045 err = got_ref_write(ref, repo);
2046 if (err)
2047 goto done;
2048 } else {
2049 err = got_ref_resolve(&old_id, repo, ref);
2050 if (err)
2051 goto done;
2052 if (got_object_id_cmp(old_id, new_id) == 0)
2053 goto done;
2055 err = got_ref_change_ref(ref, new_id);
2056 if (err)
2057 goto done;
2058 err = got_ref_write(ref, repo);
2059 if (err)
2060 goto done;
2063 if (verbosity >= 0)
2064 printf("Updated %s: %s\n", got_ref_get_name(ref),
2065 new_id_str);
2066 done:
2067 free(old_id);
2068 free(new_id_str);
2069 return err;
2072 static const struct got_error *
2073 update_symref(const char *refname, struct got_reference *target_ref,
2074 int verbosity, struct got_repository *repo)
2076 const struct got_error *err = NULL, *unlock_err;
2077 struct got_reference *symref;
2078 int symref_is_locked = 0;
2080 err = got_ref_open(&symref, repo, refname, 1);
2081 if (err) {
2082 if (err->code != GOT_ERR_NOT_REF)
2083 return err;
2084 err = got_ref_alloc_symref(&symref, refname, target_ref);
2085 if (err)
2086 goto done;
2088 err = got_ref_write(symref, repo);
2089 if (err)
2090 goto done;
2092 if (verbosity >= 0)
2093 printf("Created reference %s: %s\n",
2094 got_ref_get_name(symref),
2095 got_ref_get_symref_target(symref));
2096 } else {
2097 symref_is_locked = 1;
2099 if (strcmp(got_ref_get_symref_target(symref),
2100 got_ref_get_name(target_ref)) == 0)
2101 goto done;
2103 err = got_ref_change_symref(symref,
2104 got_ref_get_name(target_ref));
2105 if (err)
2106 goto done;
2108 err = got_ref_write(symref, repo);
2109 if (err)
2110 goto done;
2112 if (verbosity >= 0)
2113 printf("Updated %s: %s\n", got_ref_get_name(symref),
2114 got_ref_get_symref_target(symref));
2117 done:
2118 if (symref_is_locked) {
2119 unlock_err = got_ref_unlock(symref);
2120 if (unlock_err && err == NULL)
2121 err = unlock_err;
2123 got_ref_close(symref);
2124 return err;
2127 __dead static void
2128 usage_fetch(void)
2130 fprintf(stderr, "usage: %s fetch [-adlqtvX] [-b branch] "
2131 "[-R reference] [-r repository-path] [remote-repository]\n",
2132 getprogname());
2133 exit(1);
2136 static const struct got_error *
2137 delete_missing_ref(struct got_reference *ref,
2138 int verbosity, struct got_repository *repo)
2140 const struct got_error *err = NULL;
2141 struct got_object_id *id = NULL;
2142 char *id_str = NULL;
2144 if (got_ref_is_symbolic(ref)) {
2145 err = got_ref_delete(ref, repo);
2146 if (err)
2147 return err;
2148 if (verbosity >= 0) {
2149 printf("Deleted %s: %s\n",
2150 got_ref_get_name(ref),
2151 got_ref_get_symref_target(ref));
2153 } else {
2154 err = got_ref_resolve(&id, repo, ref);
2155 if (err)
2156 return err;
2157 err = got_object_id_str(&id_str, id);
2158 if (err)
2159 goto done;
2161 err = got_ref_delete(ref, repo);
2162 if (err)
2163 goto done;
2164 if (verbosity >= 0) {
2165 printf("Deleted %s: %s\n",
2166 got_ref_get_name(ref), id_str);
2169 done:
2170 free(id);
2171 free(id_str);
2172 return err;
2175 static const struct got_error *
2176 delete_missing_refs(struct got_pathlist_head *their_refs,
2177 struct got_pathlist_head *their_symrefs,
2178 const struct got_remote_repo *remote,
2179 int verbosity, struct got_repository *repo)
2181 const struct got_error *err = NULL, *unlock_err;
2182 struct got_reflist_head my_refs;
2183 struct got_reflist_entry *re;
2184 struct got_pathlist_entry *pe;
2185 char *remote_namespace = NULL;
2186 char *local_refname = NULL;
2188 TAILQ_INIT(&my_refs);
2190 if (asprintf(&remote_namespace, "refs/remotes/%s/", remote->name)
2191 == -1)
2192 return got_error_from_errno("asprintf");
2194 err = got_ref_list(&my_refs, repo, NULL, got_ref_cmp_by_name, NULL);
2195 if (err)
2196 goto done;
2198 TAILQ_FOREACH(re, &my_refs, entry) {
2199 const char *refname = got_ref_get_name(re->ref);
2200 const char *their_refname;
2202 if (remote->mirror_references) {
2203 their_refname = refname;
2204 } else {
2205 if (strncmp(refname, remote_namespace,
2206 strlen(remote_namespace)) == 0) {
2207 if (strcmp(refname + strlen(remote_namespace),
2208 GOT_REF_HEAD) == 0)
2209 continue;
2210 if (asprintf(&local_refname, "refs/heads/%s",
2211 refname + strlen(remote_namespace)) == -1) {
2212 err = got_error_from_errno("asprintf");
2213 goto done;
2215 } else if (strncmp(refname, "refs/tags/", 10) != 0)
2216 continue;
2218 their_refname = local_refname;
2221 TAILQ_FOREACH(pe, their_refs, entry) {
2222 if (strcmp(their_refname, pe->path) == 0)
2223 break;
2225 if (pe != NULL)
2226 continue;
2228 TAILQ_FOREACH(pe, their_symrefs, entry) {
2229 if (strcmp(their_refname, pe->path) == 0)
2230 break;
2232 if (pe != NULL)
2233 continue;
2235 err = delete_missing_ref(re->ref, verbosity, repo);
2236 if (err)
2237 break;
2239 if (local_refname) {
2240 struct got_reference *ref;
2241 err = got_ref_open(&ref, repo, local_refname, 1);
2242 if (err) {
2243 if (err->code != GOT_ERR_NOT_REF)
2244 break;
2245 free(local_refname);
2246 local_refname = NULL;
2247 continue;
2249 err = delete_missing_ref(ref, verbosity, repo);
2250 if (err)
2251 break;
2252 unlock_err = got_ref_unlock(ref);
2253 got_ref_close(ref);
2254 if (unlock_err && err == NULL) {
2255 err = unlock_err;
2256 break;
2259 free(local_refname);
2260 local_refname = NULL;
2263 done:
2264 got_ref_list_free(&my_refs);
2265 free(remote_namespace);
2266 free(local_refname);
2267 return err;
2270 static const struct got_error *
2271 update_wanted_ref(const char *refname, struct got_object_id *id,
2272 const char *remote_repo_name, int verbosity, struct got_repository *repo)
2274 const struct got_error *err, *unlock_err;
2275 char *remote_refname;
2276 struct got_reference *ref;
2278 if (strncmp("refs/", refname, 5) == 0)
2279 refname += 5;
2281 if (asprintf(&remote_refname, "refs/remotes/%s/%s",
2282 remote_repo_name, refname) == -1)
2283 return got_error_from_errno("asprintf");
2285 err = got_ref_open(&ref, repo, remote_refname, 1);
2286 if (err) {
2287 if (err->code != GOT_ERR_NOT_REF)
2288 goto done;
2289 err = create_ref(remote_refname, id, verbosity, repo);
2290 } else {
2291 err = update_ref(ref, id, 0, verbosity, repo);
2292 unlock_err = got_ref_unlock(ref);
2293 if (unlock_err && err == NULL)
2294 err = unlock_err;
2295 got_ref_close(ref);
2297 done:
2298 free(remote_refname);
2299 return err;
2302 static const struct got_error *
2303 delete_ref(struct got_repository *repo, struct got_reference *ref)
2305 const struct got_error *err = NULL;
2306 struct got_object_id *id = NULL;
2307 char *id_str = NULL;
2308 const char *target;
2310 if (got_ref_is_symbolic(ref)) {
2311 target = got_ref_get_symref_target(ref);
2312 } else {
2313 err = got_ref_resolve(&id, repo, ref);
2314 if (err)
2315 goto done;
2316 err = got_object_id_str(&id_str, id);
2317 if (err)
2318 goto done;
2319 target = id_str;
2322 err = got_ref_delete(ref, repo);
2323 if (err)
2324 goto done;
2326 printf("Deleted %s: %s\n", got_ref_get_name(ref), target);
2327 done:
2328 free(id);
2329 free(id_str);
2330 return err;
2333 static const struct got_error *
2334 delete_refs_for_remote(struct got_repository *repo, const char *remote_name)
2336 const struct got_error *err = NULL;
2337 struct got_reflist_head refs;
2338 struct got_reflist_entry *re;
2339 char *prefix;
2341 TAILQ_INIT(&refs);
2343 if (asprintf(&prefix, "refs/remotes/%s", remote_name) == -1) {
2344 err = got_error_from_errno("asprintf");
2345 goto done;
2347 err = got_ref_list(&refs, repo, prefix, got_ref_cmp_by_name, NULL);
2348 if (err)
2349 goto done;
2351 TAILQ_FOREACH(re, &refs, entry)
2352 delete_ref(repo, re->ref);
2353 done:
2354 got_ref_list_free(&refs);
2355 return err;
2358 static const struct got_error *
2359 cmd_fetch(int argc, char *argv[])
2361 const struct got_error *error = NULL, *unlock_err;
2362 char *cwd = NULL, *repo_path = NULL;
2363 const char *remote_name;
2364 char *proto = NULL, *host = NULL, *port = NULL;
2365 char *repo_name = NULL, *server_path = NULL;
2366 const struct got_remote_repo *remotes;
2367 struct got_remote_repo *remote = NULL;
2368 int nremotes;
2369 char *id_str = NULL;
2370 struct got_repository *repo = NULL;
2371 struct got_worktree *worktree = NULL;
2372 const struct got_gotconfig *repo_conf = NULL, *worktree_conf = NULL;
2373 struct got_pathlist_head refs, symrefs, wanted_branches, wanted_refs;
2374 char *head_refname = NULL;
2375 struct got_pathlist_entry *pe;
2376 struct got_reflist_head remote_refs;
2377 struct got_reflist_entry *re;
2378 struct got_object_id *pack_hash = NULL;
2379 int i, ch, fetchfd = -1, fetchstatus;
2380 pid_t fetchpid = -1;
2381 struct got_fetch_progress_arg fpa;
2382 int verbosity = 0, fetch_all_branches = 0, list_refs_only = 0;
2383 int delete_refs = 0, replace_tags = 0, delete_remote = 0;
2384 int *pack_fds = NULL, have_bflag = 0;
2385 const char *remote_head = NULL, *worktree_branch = NULL;
2387 TAILQ_INIT(&refs);
2388 TAILQ_INIT(&symrefs);
2389 TAILQ_INIT(&remote_refs);
2390 TAILQ_INIT(&wanted_branches);
2391 TAILQ_INIT(&wanted_refs);
2393 while ((ch = getopt(argc, argv, "ab:dlqR:r:tvX")) != -1) {
2394 switch (ch) {
2395 case 'a':
2396 fetch_all_branches = 1;
2397 break;
2398 case 'b':
2399 error = got_pathlist_insert(NULL, &wanted_branches,
2400 optarg, NULL);
2401 if (error)
2402 return error;
2403 have_bflag = 1;
2404 break;
2405 case 'd':
2406 delete_refs = 1;
2407 break;
2408 case 'l':
2409 list_refs_only = 1;
2410 break;
2411 case 'q':
2412 verbosity = -1;
2413 break;
2414 case 'R':
2415 error = got_pathlist_insert(NULL, &wanted_refs,
2416 optarg, NULL);
2417 if (error)
2418 return error;
2419 break;
2420 case 'r':
2421 repo_path = realpath(optarg, NULL);
2422 if (repo_path == NULL)
2423 return got_error_from_errno2("realpath",
2424 optarg);
2425 got_path_strip_trailing_slashes(repo_path);
2426 break;
2427 case 't':
2428 replace_tags = 1;
2429 break;
2430 case 'v':
2431 if (verbosity < 0)
2432 verbosity = 0;
2433 else if (verbosity < 3)
2434 verbosity++;
2435 break;
2436 case 'X':
2437 delete_remote = 1;
2438 break;
2439 default:
2440 usage_fetch();
2441 break;
2444 argc -= optind;
2445 argv += optind;
2447 if (fetch_all_branches && !TAILQ_EMPTY(&wanted_branches))
2448 option_conflict('a', 'b');
2449 if (list_refs_only) {
2450 if (!TAILQ_EMPTY(&wanted_branches))
2451 option_conflict('l', 'b');
2452 if (fetch_all_branches)
2453 option_conflict('l', 'a');
2454 if (delete_refs)
2455 option_conflict('l', 'd');
2456 if (delete_remote)
2457 option_conflict('l', 'X');
2459 if (delete_remote) {
2460 if (fetch_all_branches)
2461 option_conflict('X', 'a');
2462 if (!TAILQ_EMPTY(&wanted_branches))
2463 option_conflict('X', 'b');
2464 if (delete_refs)
2465 option_conflict('X', 'd');
2466 if (replace_tags)
2467 option_conflict('X', 't');
2468 if (!TAILQ_EMPTY(&wanted_refs))
2469 option_conflict('X', 'R');
2472 if (argc == 0) {
2473 if (delete_remote)
2474 errx(1, "-X option requires a remote name");
2475 remote_name = GOT_FETCH_DEFAULT_REMOTE_NAME;
2476 } else if (argc == 1)
2477 remote_name = argv[0];
2478 else
2479 usage_fetch();
2481 cwd = getcwd(NULL, 0);
2482 if (cwd == NULL) {
2483 error = got_error_from_errno("getcwd");
2484 goto done;
2487 error = got_repo_pack_fds_open(&pack_fds);
2488 if (error != NULL)
2489 goto done;
2491 if (repo_path == NULL) {
2492 error = got_worktree_open(&worktree, cwd, GOT_WORKTREE_GOT_DIR);
2493 if (error && error->code != GOT_ERR_NOT_WORKTREE)
2494 goto done;
2495 else
2496 error = NULL;
2497 if (worktree) {
2498 repo_path =
2499 strdup(got_worktree_get_repo_path(worktree));
2500 if (repo_path == NULL)
2501 error = got_error_from_errno("strdup");
2502 if (error)
2503 goto done;
2504 } else {
2505 repo_path = strdup(cwd);
2506 if (repo_path == NULL) {
2507 error = got_error_from_errno("strdup");
2508 goto done;
2513 error = got_repo_open(&repo, repo_path, NULL, pack_fds);
2514 if (error)
2515 goto done;
2517 if (delete_remote) {
2518 error = delete_refs_for_remote(repo, remote_name);
2519 goto done; /* nothing else to do */
2522 if (worktree) {
2523 worktree_conf = got_worktree_get_gotconfig(worktree);
2524 if (worktree_conf) {
2525 got_gotconfig_get_remotes(&nremotes, &remotes,
2526 worktree_conf);
2527 for (i = 0; i < nremotes; i++) {
2528 if (strcmp(remotes[i].name, remote_name) == 0) {
2529 error = got_repo_remote_repo_dup(&remote,
2530 &remotes[i]);
2531 if (error)
2532 goto done;
2533 break;
2538 if (remote == NULL) {
2539 repo_conf = got_repo_get_gotconfig(repo);
2540 if (repo_conf) {
2541 got_gotconfig_get_remotes(&nremotes, &remotes,
2542 repo_conf);
2543 for (i = 0; i < nremotes; i++) {
2544 if (strcmp(remotes[i].name, remote_name) == 0) {
2545 error = got_repo_remote_repo_dup(&remote,
2546 &remotes[i]);
2547 if (error)
2548 goto done;
2549 break;
2554 if (remote == NULL) {
2555 got_repo_get_gitconfig_remotes(&nremotes, &remotes, repo);
2556 for (i = 0; i < nremotes; i++) {
2557 if (strcmp(remotes[i].name, remote_name) == 0) {
2558 error = got_repo_remote_repo_dup(&remote,
2559 &remotes[i]);
2560 if (error)
2561 goto done;
2562 break;
2566 if (remote == NULL) {
2567 error = got_error_path(remote_name, GOT_ERR_NO_REMOTE);
2568 goto done;
2571 if (TAILQ_EMPTY(&wanted_branches)) {
2572 if (!fetch_all_branches)
2573 fetch_all_branches = remote->fetch_all_branches;
2574 for (i = 0; i < remote->nfetch_branches; i++) {
2575 error = got_pathlist_insert(NULL, &wanted_branches,
2576 remote->fetch_branches[i], NULL);
2577 if (error)
2578 goto done;
2581 if (TAILQ_EMPTY(&wanted_refs)) {
2582 for (i = 0; i < remote->nfetch_refs; i++) {
2583 error = got_pathlist_insert(NULL, &wanted_refs,
2584 remote->fetch_refs[i], NULL);
2585 if (error)
2586 goto done;
2590 error = got_dial_parse_uri(&proto, &host, &port, &server_path,
2591 &repo_name, remote->fetch_url);
2592 if (error)
2593 goto done;
2595 if (strcmp(proto, "git") == 0) {
2596 #ifndef PROFILE
2597 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
2598 "sendfd dns inet unveil", NULL) == -1)
2599 err(1, "pledge");
2600 #endif
2601 } else if (strcmp(proto, "git+ssh") == 0 ||
2602 strcmp(proto, "ssh") == 0 ||
2603 strcmp(proto, "git+http") == 0 ||
2604 strcmp(proto, "http") == 0 ||
2605 strcmp(proto, "git+https") == 0 ||
2606 strcmp(proto, "https") == 0) {
2607 #ifndef PROFILE
2608 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
2609 "sendfd unveil", NULL) == -1)
2610 err(1, "pledge");
2611 #endif
2612 } else {
2613 error = got_error_path(proto, GOT_ERR_BAD_PROTO);
2614 goto done;
2617 error = got_dial_apply_unveil(proto);
2618 if (error)
2619 goto done;
2621 error = apply_unveil(got_repo_get_path(repo), 0, NULL);
2622 if (error)
2623 goto done;
2625 if (worktree) {
2626 head_refname = strdup(got_worktree_get_head_ref_name(worktree));
2627 if (head_refname == NULL) {
2628 error = got_error_from_errno("strdup");
2629 goto done;
2632 /* Release work tree lock. */
2633 got_worktree_close(worktree);
2634 worktree = NULL;
2637 if (verbosity >= 0) {
2638 printf("Connecting to \"%s\" %s://%s%s%s%s%s\n",
2639 remote->name, proto, host,
2640 port ? ":" : "", port ? port : "",
2641 *server_path == '/' ? "" : "/", server_path);
2644 error = got_fetch_connect(&fetchpid, &fetchfd, proto, host, port,
2645 server_path, verbosity);
2646 if (error)
2647 goto done;
2648 #ifndef PROFILE
2649 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd",
2650 NULL) == -1)
2651 err(1, "pledge");
2652 #endif
2653 if (!have_bflag) {
2655 * If set, get this remote's HEAD ref target so
2656 * if it has changed on the server we can fetch it.
2658 error = got_ref_list(&remote_refs, repo, "refs/remotes",
2659 got_ref_cmp_by_name, repo);
2660 if (error)
2661 goto done;
2663 TAILQ_FOREACH(re, &remote_refs, entry) {
2664 const char *remote_refname, *remote_target;
2665 size_t remote_name_len;
2667 if (!got_ref_is_symbolic(re->ref))
2668 continue;
2670 remote_name_len = strlen(remote->name);
2671 remote_refname = got_ref_get_name(re->ref);
2673 /* we only want refs/remotes/$remote->name/HEAD */
2674 if (strncmp(remote_refname + 13, remote->name,
2675 remote_name_len) != 0)
2676 continue;
2678 if (strcmp(remote_refname + remote_name_len + 14,
2679 GOT_REF_HEAD) != 0)
2680 continue;
2683 * Take the name itself because we already
2684 * only match with refs/heads/ in fetch_pack().
2686 remote_target = got_ref_get_symref_target(re->ref);
2687 remote_head = remote_target + remote_name_len + 14;
2688 break;
2691 if (head_refname &&
2692 strncmp(head_refname, "refs/heads/", 11) == 0)
2693 worktree_branch = head_refname;
2696 fpa.last_scaled_size[0] = '\0';
2697 fpa.last_p_indexed = -1;
2698 fpa.last_p_resolved = -1;
2699 fpa.verbosity = verbosity;
2700 fpa.repo = repo;
2701 fpa.create_configs = 0;
2702 fpa.configs_created = 0;
2703 memset(&fpa.config_info, 0, sizeof(fpa.config_info));
2705 error = got_fetch_pack(&pack_hash, &refs, &symrefs, remote->name,
2706 remote->mirror_references, fetch_all_branches, &wanted_branches,
2707 &wanted_refs, list_refs_only, verbosity, fetchfd, repo,
2708 worktree_branch, remote_head, have_bflag, fetch_progress, &fpa);
2709 if (error)
2710 goto done;
2712 if (list_refs_only) {
2713 error = list_remote_refs(&symrefs, &refs);
2714 goto done;
2717 if (pack_hash == NULL) {
2718 if (verbosity >= 0)
2719 printf("Already up-to-date\n");
2720 } else if (verbosity >= 0) {
2721 error = got_object_id_str(&id_str, pack_hash);
2722 if (error)
2723 goto done;
2724 printf("\nFetched %s.pack\n", id_str);
2725 free(id_str);
2726 id_str = NULL;
2729 /* Update references provided with the pack file. */
2730 TAILQ_FOREACH(pe, &refs, entry) {
2731 const char *refname = pe->path;
2732 struct got_object_id *id = pe->data;
2733 struct got_reference *ref;
2734 char *remote_refname;
2736 if (is_wanted_ref(&wanted_refs, refname) &&
2737 !remote->mirror_references) {
2738 error = update_wanted_ref(refname, id,
2739 remote->name, verbosity, repo);
2740 if (error)
2741 goto done;
2742 continue;
2745 if (remote->mirror_references ||
2746 strncmp("refs/tags/", refname, 10) == 0) {
2747 error = got_ref_open(&ref, repo, refname, 1);
2748 if (error) {
2749 if (error->code != GOT_ERR_NOT_REF)
2750 goto done;
2751 error = create_ref(refname, id, verbosity,
2752 repo);
2753 if (error)
2754 goto done;
2755 } else {
2756 error = update_ref(ref, id, replace_tags,
2757 verbosity, repo);
2758 unlock_err = got_ref_unlock(ref);
2759 if (unlock_err && error == NULL)
2760 error = unlock_err;
2761 got_ref_close(ref);
2762 if (error)
2763 goto done;
2765 } else if (strncmp("refs/heads/", refname, 11) == 0) {
2766 if (asprintf(&remote_refname, "refs/remotes/%s/%s",
2767 remote_name, refname + 11) == -1) {
2768 error = got_error_from_errno("asprintf");
2769 goto done;
2772 error = got_ref_open(&ref, repo, remote_refname, 1);
2773 if (error) {
2774 if (error->code != GOT_ERR_NOT_REF)
2775 goto done;
2776 error = create_ref(remote_refname, id,
2777 verbosity, repo);
2778 if (error)
2779 goto done;
2780 } else {
2781 error = update_ref(ref, id, replace_tags,
2782 verbosity, repo);
2783 unlock_err = got_ref_unlock(ref);
2784 if (unlock_err && error == NULL)
2785 error = unlock_err;
2786 got_ref_close(ref);
2787 if (error)
2788 goto done;
2791 /* Also create a local branch if none exists yet. */
2792 error = got_ref_open(&ref, repo, refname, 1);
2793 if (error) {
2794 if (error->code != GOT_ERR_NOT_REF)
2795 goto done;
2796 error = create_ref(refname, id, verbosity,
2797 repo);
2798 if (error)
2799 goto done;
2800 } else {
2801 unlock_err = got_ref_unlock(ref);
2802 if (unlock_err && error == NULL)
2803 error = unlock_err;
2804 got_ref_close(ref);
2808 if (delete_refs) {
2809 error = delete_missing_refs(&refs, &symrefs, remote,
2810 verbosity, repo);
2811 if (error)
2812 goto done;
2815 if (!remote->mirror_references) {
2816 /* Update remote HEAD reference if the server provided one. */
2817 TAILQ_FOREACH(pe, &symrefs, entry) {
2818 struct got_reference *target_ref;
2819 const char *refname = pe->path;
2820 const char *target = pe->data;
2821 char *remote_refname = NULL, *remote_target = NULL;
2823 if (strcmp(refname, GOT_REF_HEAD) != 0)
2824 continue;
2826 if (strncmp("refs/heads/", target, 11) != 0)
2827 continue;
2829 if (asprintf(&remote_refname, "refs/remotes/%s/%s",
2830 remote->name, refname) == -1) {
2831 error = got_error_from_errno("asprintf");
2832 goto done;
2834 if (asprintf(&remote_target, "refs/remotes/%s/%s",
2835 remote->name, target + 11) == -1) {
2836 error = got_error_from_errno("asprintf");
2837 free(remote_refname);
2838 goto done;
2841 error = got_ref_open(&target_ref, repo, remote_target,
2843 if (error) {
2844 free(remote_refname);
2845 free(remote_target);
2846 if (error->code == GOT_ERR_NOT_REF) {
2847 error = NULL;
2848 continue;
2850 goto done;
2852 error = update_symref(remote_refname, target_ref,
2853 verbosity, repo);
2854 free(remote_refname);
2855 free(remote_target);
2856 got_ref_close(target_ref);
2857 if (error)
2858 goto done;
2861 done:
2862 if (fetchpid > 0) {
2863 if (kill(fetchpid, SIGTERM) == -1)
2864 error = got_error_from_errno("kill");
2865 if (waitpid(fetchpid, &fetchstatus, 0) == -1 && error == NULL)
2866 error = got_error_from_errno("waitpid");
2868 if (fetchfd != -1 && close(fetchfd) == -1 && error == NULL)
2869 error = got_error_from_errno("close");
2870 if (repo) {
2871 const struct got_error *close_err = got_repo_close(repo);
2872 if (error == NULL)
2873 error = close_err;
2875 if (worktree)
2876 got_worktree_close(worktree);
2877 if (pack_fds) {
2878 const struct got_error *pack_err =
2879 got_repo_pack_fds_close(pack_fds);
2880 if (error == NULL)
2881 error = pack_err;
2883 got_pathlist_free(&refs, GOT_PATHLIST_FREE_ALL);
2884 got_pathlist_free(&symrefs, GOT_PATHLIST_FREE_ALL);
2885 got_pathlist_free(&wanted_branches, GOT_PATHLIST_FREE_NONE);
2886 got_pathlist_free(&wanted_refs, GOT_PATHLIST_FREE_NONE);
2887 got_ref_list_free(&remote_refs);
2888 got_repo_free_remote_repo_data(remote);
2889 free(remote);
2890 free(head_refname);
2891 free(id_str);
2892 free(cwd);
2893 free(repo_path);
2894 free(pack_hash);
2895 free(proto);
2896 free(host);
2897 free(port);
2898 free(server_path);
2899 free(repo_name);
2900 return error;
2904 __dead static void
2905 usage_checkout(void)
2907 fprintf(stderr, "usage: %s checkout [-Eq] [-b branch] [-c commit] "
2908 "[-p path-prefix] repository-path [work-tree-path]\n",
2909 getprogname());
2910 exit(1);
2913 static void
2914 show_worktree_base_ref_warning(void)
2916 fprintf(stderr, "%s: warning: could not create a reference "
2917 "to the work tree's base commit; the commit could be "
2918 "garbage-collected by Git or 'gotadmin cleanup'; making the "
2919 "repository writable and running 'got update' will prevent this\n",
2920 getprogname());
2923 struct got_checkout_progress_arg {
2924 const char *worktree_path;
2925 int had_base_commit_ref_error;
2926 int verbosity;
2929 static const struct got_error *
2930 checkout_progress(void *arg, unsigned char status, const char *path)
2932 struct got_checkout_progress_arg *a = arg;
2934 /* Base commit bump happens silently. */
2935 if (status == GOT_STATUS_BUMP_BASE)
2936 return NULL;
2938 if (status == GOT_STATUS_BASE_REF_ERR) {
2939 a->had_base_commit_ref_error = 1;
2940 return NULL;
2943 while (path[0] == '/')
2944 path++;
2946 if (a->verbosity >= 0)
2947 printf("%c %s/%s\n", status, a->worktree_path, path);
2949 return NULL;
2952 static const struct got_error *
2953 check_cancelled(void *arg)
2955 if (sigint_received || sigpipe_received)
2956 return got_error(GOT_ERR_CANCELLED);
2957 return NULL;
2960 static const struct got_error *
2961 check_linear_ancestry(struct got_object_id *commit_id,
2962 struct got_object_id *base_commit_id, int allow_forwards_in_time_only,
2963 struct got_repository *repo)
2965 const struct got_error *err = NULL;
2966 struct got_object_id *yca_id;
2968 err = got_commit_graph_find_youngest_common_ancestor(&yca_id,
2969 commit_id, base_commit_id, 1, 0, repo, check_cancelled, NULL);
2970 if (err)
2971 return err;
2973 if (yca_id == NULL)
2974 return got_error(GOT_ERR_ANCESTRY);
2977 * Require a straight line of history between the target commit
2978 * and the work tree's base commit.
2980 * Non-linear situations such as this require a rebase:
2982 * (commit) D F (base_commit)
2983 * \ /
2984 * C E
2985 * \ /
2986 * B (yca)
2990 * 'got update' only handles linear cases:
2991 * Update forwards in time: A (base/yca) - B - C - D (commit)
2992 * Update backwards in time: D (base) - C - B - A (commit/yca)
2994 if (allow_forwards_in_time_only) {
2995 if (got_object_id_cmp(base_commit_id, yca_id) != 0)
2996 return got_error(GOT_ERR_ANCESTRY);
2997 } else if (got_object_id_cmp(commit_id, yca_id) != 0 &&
2998 got_object_id_cmp(base_commit_id, yca_id) != 0)
2999 return got_error(GOT_ERR_ANCESTRY);
3001 free(yca_id);
3002 return NULL;
3005 static const struct got_error *
3006 check_same_branch(struct got_object_id *commit_id,
3007 struct got_reference *head_ref, struct got_repository *repo)
3009 const struct got_error *err = NULL;
3010 struct got_commit_graph *graph = NULL;
3011 struct got_object_id *head_commit_id = NULL;
3013 err = got_ref_resolve(&head_commit_id, repo, head_ref);
3014 if (err)
3015 goto done;
3017 if (got_object_id_cmp(head_commit_id, commit_id) == 0)
3018 goto done;
3020 err = got_commit_graph_open(&graph, "/", 1);
3021 if (err)
3022 goto done;
3024 err = got_commit_graph_bfsort(graph, head_commit_id, repo,
3025 check_cancelled, NULL);
3026 if (err)
3027 goto done;
3029 for (;;) {
3030 struct got_object_id id;
3032 err = got_commit_graph_iter_next(&id, graph, repo,
3033 check_cancelled, NULL);
3034 if (err) {
3035 if (err->code == GOT_ERR_ITER_COMPLETED)
3036 err = got_error(GOT_ERR_ANCESTRY);
3037 break;
3040 if (got_object_id_cmp(&id, commit_id) == 0)
3041 break;
3043 done:
3044 if (graph)
3045 got_commit_graph_close(graph);
3046 free(head_commit_id);
3047 return err;
3050 static const struct got_error *
3051 checkout_ancestry_error(struct got_reference *ref, const char *commit_id_str)
3053 static char msg[512];
3054 const char *branch_name;
3056 if (got_ref_is_symbolic(ref))
3057 branch_name = got_ref_get_symref_target(ref);
3058 else
3059 branch_name = got_ref_get_name(ref);
3061 if (strncmp("refs/heads/", branch_name, 11) == 0)
3062 branch_name += 11;
3064 snprintf(msg, sizeof(msg),
3065 "target commit is not contained in branch '%s'; "
3066 "the branch to use must be specified with -b; "
3067 "if necessary a new branch can be created for "
3068 "this commit with 'got branch -c %s BRANCH_NAME'",
3069 branch_name, commit_id_str);
3071 return got_error_msg(GOT_ERR_ANCESTRY, msg);
3074 static const struct got_error *
3075 cmd_checkout(int argc, char *argv[])
3077 const struct got_error *close_err, *error = NULL;
3078 struct got_repository *repo = NULL;
3079 struct got_reference *head_ref = NULL, *ref = NULL;
3080 struct got_worktree *worktree = NULL;
3081 char *repo_path = NULL;
3082 char *worktree_path = NULL;
3083 const char *path_prefix = "";
3084 const char *branch_name = GOT_REF_HEAD, *refname = NULL;
3085 char *commit_id_str = NULL, *keyword_idstr = NULL;
3086 struct got_object_id *commit_id = NULL;
3087 char *cwd = NULL;
3088 int ch, same_path_prefix, allow_nonempty = 0, verbosity = 0;
3089 struct got_pathlist_head paths;
3090 struct got_checkout_progress_arg cpa;
3091 int *pack_fds = NULL;
3093 TAILQ_INIT(&paths);
3095 #ifndef PROFILE
3096 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
3097 "unveil", NULL) == -1)
3098 err(1, "pledge");
3099 #endif
3101 while ((ch = getopt(argc, argv, "b:c:Ep:q")) != -1) {
3102 switch (ch) {
3103 case 'b':
3104 branch_name = optarg;
3105 break;
3106 case 'c':
3107 commit_id_str = strdup(optarg);
3108 if (commit_id_str == NULL)
3109 return got_error_from_errno("strdup");
3110 break;
3111 case 'E':
3112 allow_nonempty = 1;
3113 break;
3114 case 'p':
3115 path_prefix = optarg;
3116 break;
3117 case 'q':
3118 verbosity = -1;
3119 break;
3120 default:
3121 usage_checkout();
3122 /* NOTREACHED */
3126 argc -= optind;
3127 argv += optind;
3129 if (argc == 1) {
3130 char *base, *dotgit;
3131 const char *path;
3132 repo_path = realpath(argv[0], NULL);
3133 if (repo_path == NULL)
3134 return got_error_from_errno2("realpath", argv[0]);
3135 cwd = getcwd(NULL, 0);
3136 if (cwd == NULL) {
3137 error = got_error_from_errno("getcwd");
3138 goto done;
3140 if (path_prefix[0])
3141 path = path_prefix;
3142 else
3143 path = repo_path;
3144 error = got_path_basename(&base, path);
3145 if (error)
3146 goto done;
3147 dotgit = strstr(base, ".git");
3148 if (dotgit)
3149 *dotgit = '\0';
3150 if (asprintf(&worktree_path, "%s/%s", cwd, base) == -1) {
3151 error = got_error_from_errno("asprintf");
3152 free(base);
3153 goto done;
3155 free(base);
3156 } else if (argc == 2) {
3157 repo_path = realpath(argv[0], NULL);
3158 if (repo_path == NULL) {
3159 error = got_error_from_errno2("realpath", argv[0]);
3160 goto done;
3162 worktree_path = realpath(argv[1], NULL);
3163 if (worktree_path == NULL) {
3164 if (errno != ENOENT) {
3165 error = got_error_from_errno2("realpath",
3166 argv[1]);
3167 goto done;
3169 worktree_path = strdup(argv[1]);
3170 if (worktree_path == NULL) {
3171 error = got_error_from_errno("strdup");
3172 goto done;
3175 } else
3176 usage_checkout();
3178 got_path_strip_trailing_slashes(repo_path);
3179 got_path_strip_trailing_slashes(worktree_path);
3181 if (got_path_is_child(worktree_path, repo_path, strlen(repo_path)) ||
3182 got_path_is_child(repo_path, worktree_path,
3183 strlen(worktree_path))) {
3184 error = got_error_fmt(GOT_ERR_BAD_PATH,
3185 "work tree and repository paths may not overlap: %s",
3186 worktree_path);
3187 goto done;
3190 error = got_repo_pack_fds_open(&pack_fds);
3191 if (error != NULL)
3192 goto done;
3194 error = got_repo_open(&repo, repo_path, NULL, pack_fds);
3195 if (error != NULL)
3196 goto done;
3198 /* Pre-create work tree path for unveil(2) */
3199 error = got_path_mkdir(worktree_path);
3200 if (error) {
3201 if (!(error->code == GOT_ERR_ERRNO && errno == EISDIR) &&
3202 !(error->code == GOT_ERR_ERRNO && errno == EEXIST))
3203 goto done;
3204 if (!allow_nonempty &&
3205 !got_path_dir_is_empty(worktree_path)) {
3206 error = got_error_path(worktree_path,
3207 GOT_ERR_DIR_NOT_EMPTY);
3208 goto done;
3212 error = apply_unveil(got_repo_get_path(repo), 0, worktree_path);
3213 if (error)
3214 goto done;
3216 error = got_ref_open(&head_ref, repo, branch_name, 0);
3217 if (error != NULL)
3218 goto done;
3220 error = got_worktree_init(worktree_path, head_ref, path_prefix,
3221 GOT_WORKTREE_GOT_DIR, repo);
3222 if (error != NULL && !(error->code == GOT_ERR_ERRNO && errno == EEXIST))
3223 goto done;
3225 error = got_worktree_open(&worktree, worktree_path,
3226 GOT_WORKTREE_GOT_DIR);
3227 if (error != NULL)
3228 goto done;
3230 error = got_worktree_match_path_prefix(&same_path_prefix, worktree,
3231 path_prefix);
3232 if (error != NULL)
3233 goto done;
3234 if (!same_path_prefix) {
3235 error = got_error(GOT_ERR_PATH_PREFIX);
3236 goto done;
3239 if (commit_id_str) {
3240 struct got_reflist_head refs;
3241 TAILQ_INIT(&refs);
3242 error = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name,
3243 NULL);
3244 if (error)
3245 goto done;
3247 error = got_keyword_to_idstr(&keyword_idstr, commit_id_str,
3248 repo, worktree);
3249 if (error != NULL)
3250 goto done;
3251 if (keyword_idstr != NULL) {
3252 free(commit_id_str);
3253 commit_id_str = keyword_idstr;
3256 error = got_repo_match_object_id(&commit_id, NULL,
3257 commit_id_str, GOT_OBJ_TYPE_COMMIT, &refs, repo);
3258 got_ref_list_free(&refs);
3259 if (error)
3260 goto done;
3261 error = check_linear_ancestry(commit_id,
3262 got_worktree_get_base_commit_id(worktree), 0, repo);
3263 if (error != NULL) {
3264 if (error->code == GOT_ERR_ANCESTRY) {
3265 error = checkout_ancestry_error(
3266 head_ref, commit_id_str);
3268 goto done;
3270 error = check_same_branch(commit_id, head_ref, repo);
3271 if (error) {
3272 if (error->code == GOT_ERR_ANCESTRY) {
3273 error = checkout_ancestry_error(
3274 head_ref, commit_id_str);
3276 goto done;
3278 error = got_worktree_set_base_commit_id(worktree, repo,
3279 commit_id);
3280 if (error)
3281 goto done;
3282 /* Expand potentially abbreviated commit ID string. */
3283 free(commit_id_str);
3284 error = got_object_id_str(&commit_id_str, commit_id);
3285 if (error)
3286 goto done;
3287 } else {
3288 commit_id = got_object_id_dup(
3289 got_worktree_get_base_commit_id(worktree));
3290 if (commit_id == NULL) {
3291 error = got_error_from_errno("got_object_id_dup");
3292 goto done;
3294 error = got_object_id_str(&commit_id_str, commit_id);
3295 if (error)
3296 goto done;
3299 error = got_pathlist_insert(NULL, &paths, "", NULL);
3300 if (error)
3301 goto done;
3302 cpa.worktree_path = worktree_path;
3303 cpa.had_base_commit_ref_error = 0;
3304 cpa.verbosity = verbosity;
3305 error = got_worktree_checkout_files(worktree, &paths, repo,
3306 checkout_progress, &cpa, check_cancelled, NULL);
3307 if (error != NULL)
3308 goto done;
3310 if (got_ref_is_symbolic(head_ref)) {
3311 error = got_ref_resolve_symbolic(&ref, repo, head_ref);
3312 if (error)
3313 goto done;
3314 refname = got_ref_get_name(ref);
3315 } else
3316 refname = got_ref_get_name(head_ref);
3317 printf("Checked out %s: %s\n", refname, commit_id_str);
3318 printf("Now shut up and hack\n");
3319 if (cpa.had_base_commit_ref_error)
3320 show_worktree_base_ref_warning();
3321 done:
3322 if (pack_fds) {
3323 const struct got_error *pack_err =
3324 got_repo_pack_fds_close(pack_fds);
3325 if (error == NULL)
3326 error = pack_err;
3328 if (head_ref)
3329 got_ref_close(head_ref);
3330 if (ref)
3331 got_ref_close(ref);
3332 if (repo) {
3333 close_err = got_repo_close(repo);
3334 if (error == NULL)
3335 error = close_err;
3337 if (worktree != NULL) {
3338 close_err = got_worktree_close(worktree);
3339 if (error == NULL)
3340 error = close_err;
3342 got_pathlist_free(&paths, GOT_PATHLIST_FREE_NONE);
3343 free(commit_id_str);
3344 free(commit_id);
3345 free(repo_path);
3346 free(worktree_path);
3347 free(cwd);
3348 return error;
3351 struct got_update_progress_arg {
3352 int did_something;
3353 int conflicts;
3354 int obstructed;
3355 int not_updated;
3356 int missing;
3357 int not_deleted;
3358 int unversioned;
3359 int verbosity;
3362 static void
3363 print_update_progress_stats(struct got_update_progress_arg *upa)
3365 if (!upa->did_something)
3366 return;
3368 if (upa->conflicts > 0)
3369 printf("Files with new merge conflicts: %d\n", upa->conflicts);
3370 if (upa->obstructed > 0)
3371 printf("File paths obstructed by a non-regular file: %d\n",
3372 upa->obstructed);
3373 if (upa->not_updated > 0)
3374 printf("Files not updated because of existing merge "
3375 "conflicts: %d\n", upa->not_updated);
3379 * The meaning of some status codes differs between merge-style operations and
3380 * update operations. For example, the ! status code means "file was missing"
3381 * if changes were merged into the work tree, and "missing file was restored"
3382 * if the work tree was updated. This function should be used by any operation
3383 * which merges changes into the work tree without updating the work tree.
3385 static void
3386 print_merge_progress_stats(struct got_update_progress_arg *upa)
3388 if (!upa->did_something)
3389 return;
3391 if (upa->conflicts > 0)
3392 printf("Files with new merge conflicts: %d\n", upa->conflicts);
3393 if (upa->obstructed > 0)
3394 printf("File paths obstructed by a non-regular file: %d\n",
3395 upa->obstructed);
3396 if (upa->missing > 0)
3397 printf("Files which had incoming changes but could not be "
3398 "found in the work tree: %d\n", upa->missing);
3399 if (upa->not_deleted > 0)
3400 printf("Files not deleted due to differences in deleted "
3401 "content: %d\n", upa->not_deleted);
3402 if (upa->unversioned > 0)
3403 printf("Files not merged because an unversioned file was "
3404 "found in the work tree: %d\n", upa->unversioned);
3407 __dead static void
3408 usage_update(void)
3410 fprintf(stderr, "usage: %s update [-q] [-b branch] [-c commit] "
3411 "[path ...]\n", getprogname());
3412 exit(1);
3415 static const struct got_error *
3416 update_progress(void *arg, unsigned char status, const char *path)
3418 struct got_update_progress_arg *upa = arg;
3420 if (status == GOT_STATUS_EXISTS ||
3421 status == GOT_STATUS_BASE_REF_ERR)
3422 return NULL;
3424 upa->did_something = 1;
3426 /* Base commit bump happens silently. */
3427 if (status == GOT_STATUS_BUMP_BASE)
3428 return NULL;
3430 if (status == GOT_STATUS_CONFLICT)
3431 upa->conflicts++;
3432 if (status == GOT_STATUS_OBSTRUCTED)
3433 upa->obstructed++;
3434 if (status == GOT_STATUS_CANNOT_UPDATE)
3435 upa->not_updated++;
3436 if (status == GOT_STATUS_MISSING)
3437 upa->missing++;
3438 if (status == GOT_STATUS_CANNOT_DELETE)
3439 upa->not_deleted++;
3440 if (status == GOT_STATUS_UNVERSIONED)
3441 upa->unversioned++;
3443 while (path[0] == '/')
3444 path++;
3445 if (upa->verbosity >= 0)
3446 printf("%c %s\n", status, path);
3448 return NULL;
3451 static const struct got_error *
3452 switch_head_ref(struct got_reference *head_ref,
3453 struct got_object_id *commit_id, struct got_worktree *worktree,
3454 struct got_repository *repo)
3456 const struct got_error *err = NULL;
3457 char *base_id_str;
3458 int ref_has_moved = 0;
3460 /* Trivial case: switching between two different references. */
3461 if (strcmp(got_ref_get_name(head_ref),
3462 got_worktree_get_head_ref_name(worktree)) != 0) {
3463 printf("Switching work tree from %s to %s\n",
3464 got_worktree_get_head_ref_name(worktree),
3465 got_ref_get_name(head_ref));
3466 return got_worktree_set_head_ref(worktree, head_ref);
3469 err = check_linear_ancestry(commit_id,
3470 got_worktree_get_base_commit_id(worktree), 0, repo);
3471 if (err) {
3472 if (err->code != GOT_ERR_ANCESTRY)
3473 return err;
3474 ref_has_moved = 1;
3476 if (!ref_has_moved)
3477 return NULL;
3479 /* Switching to a rebased branch with the same reference name. */
3480 err = got_object_id_str(&base_id_str,
3481 got_worktree_get_base_commit_id(worktree));
3482 if (err)
3483 return err;
3484 printf("Reference %s now points at a different branch\n",
3485 got_worktree_get_head_ref_name(worktree));
3486 printf("Switching work tree from %s to %s\n", base_id_str,
3487 got_worktree_get_head_ref_name(worktree));
3488 return NULL;
3491 static const struct got_error *
3492 check_rebase_or_histedit_in_progress(struct got_worktree *worktree)
3494 const struct got_error *err;
3495 int in_progress;
3497 err = got_worktree_rebase_in_progress(&in_progress, worktree);
3498 if (err)
3499 return err;
3500 if (in_progress)
3501 return got_error(GOT_ERR_REBASING);
3503 err = got_worktree_histedit_in_progress(&in_progress, worktree);
3504 if (err)
3505 return err;
3506 if (in_progress)
3507 return got_error(GOT_ERR_HISTEDIT_BUSY);
3509 return NULL;
3512 static const struct got_error *
3513 check_merge_in_progress(struct got_worktree *worktree,
3514 struct got_repository *repo)
3516 const struct got_error *err;
3517 int in_progress;
3519 err = got_worktree_merge_in_progress(&in_progress, worktree, repo);
3520 if (err)
3521 return err;
3522 if (in_progress)
3523 return got_error(GOT_ERR_MERGE_BUSY);
3525 return NULL;
3528 static const struct got_error *
3529 get_worktree_paths_from_argv(struct got_pathlist_head *paths, int argc,
3530 char *argv[], struct got_worktree *worktree)
3532 const struct got_error *err = NULL;
3533 char *path;
3534 struct got_pathlist_entry *new;
3535 int i;
3537 if (argc == 0) {
3538 path = strdup("");
3539 if (path == NULL)
3540 return got_error_from_errno("strdup");
3541 return got_pathlist_insert(NULL, paths, path, NULL);
3544 for (i = 0; i < argc; i++) {
3545 err = got_worktree_resolve_path(&path, worktree, argv[i]);
3546 if (err)
3547 break;
3548 err = got_pathlist_insert(&new, paths, path, NULL);
3549 if (err || new == NULL /* duplicate */) {
3550 free(path);
3551 if (err)
3552 break;
3556 return err;
3559 static const struct got_error *
3560 wrap_not_worktree_error(const struct got_error *orig_err,
3561 const char *cmdname, const char *path)
3563 const struct got_error *err;
3564 struct got_repository *repo;
3565 static char msg[512];
3566 int *pack_fds = NULL;
3568 err = got_repo_pack_fds_open(&pack_fds);
3569 if (err)
3570 return err;
3572 err = got_repo_open(&repo, path, NULL, pack_fds);
3573 if (err)
3574 return orig_err;
3576 snprintf(msg, sizeof(msg),
3577 "'got %s' needs a work tree in addition to a git repository\n"
3578 "Work trees can be checked out from this Git repository with "
3579 "'got checkout'.\n"
3580 "The got(1) manual page contains more information.", cmdname);
3581 err = got_error_msg(GOT_ERR_NOT_WORKTREE, msg);
3582 if (repo) {
3583 const struct got_error *close_err = got_repo_close(repo);
3584 if (err == NULL)
3585 err = close_err;
3587 if (pack_fds) {
3588 const struct got_error *pack_err =
3589 got_repo_pack_fds_close(pack_fds);
3590 if (err == NULL)
3591 err = pack_err;
3593 return err;
3596 static const struct got_error *
3597 cmd_update(int argc, char *argv[])
3599 const struct got_error *close_err, *error = NULL;
3600 struct got_repository *repo = NULL;
3601 struct got_worktree *worktree = NULL;
3602 char *worktree_path = NULL;
3603 struct got_object_id *commit_id = NULL;
3604 char *commit_id_str = NULL;
3605 const char *branch_name = NULL;
3606 struct got_reference *head_ref = NULL;
3607 struct got_pathlist_head paths;
3608 struct got_pathlist_entry *pe;
3609 int ch, verbosity = 0;
3610 struct got_update_progress_arg upa;
3611 int *pack_fds = NULL;
3613 TAILQ_INIT(&paths);
3615 #ifndef PROFILE
3616 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
3617 "unveil", NULL) == -1)
3618 err(1, "pledge");
3619 #endif
3621 while ((ch = getopt(argc, argv, "b:c:q")) != -1) {
3622 switch (ch) {
3623 case 'b':
3624 branch_name = optarg;
3625 break;
3626 case 'c':
3627 commit_id_str = strdup(optarg);
3628 if (commit_id_str == NULL)
3629 return got_error_from_errno("strdup");
3630 break;
3631 case 'q':
3632 verbosity = -1;
3633 break;
3634 default:
3635 usage_update();
3636 /* NOTREACHED */
3640 argc -= optind;
3641 argv += optind;
3643 worktree_path = getcwd(NULL, 0);
3644 if (worktree_path == NULL) {
3645 error = got_error_from_errno("getcwd");
3646 goto done;
3649 error = got_repo_pack_fds_open(&pack_fds);
3650 if (error != NULL)
3651 goto done;
3653 error = got_worktree_open(&worktree, worktree_path,
3654 GOT_WORKTREE_GOT_DIR);
3655 if (error) {
3656 if (error->code == GOT_ERR_NOT_WORKTREE)
3657 error = wrap_not_worktree_error(error, "update",
3658 worktree_path);
3659 goto done;
3662 error = check_rebase_or_histedit_in_progress(worktree);
3663 if (error)
3664 goto done;
3666 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
3667 NULL, pack_fds);
3668 if (error != NULL)
3669 goto done;
3671 error = apply_unveil(got_repo_get_path(repo), 0,
3672 got_worktree_get_root_path(worktree));
3673 if (error)
3674 goto done;
3676 error = check_merge_in_progress(worktree, repo);
3677 if (error)
3678 goto done;
3680 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
3681 if (error)
3682 goto done;
3684 error = got_ref_open(&head_ref, repo, branch_name ? branch_name :
3685 got_worktree_get_head_ref_name(worktree), 0);
3686 if (error != NULL)
3687 goto done;
3688 if (commit_id_str == NULL) {
3689 error = got_ref_resolve(&commit_id, repo, head_ref);
3690 if (error != NULL)
3691 goto done;
3692 error = got_object_id_str(&commit_id_str, commit_id);
3693 if (error != NULL)
3694 goto done;
3695 } else {
3696 struct got_reflist_head refs;
3697 char *keyword_idstr = NULL;
3699 TAILQ_INIT(&refs);
3701 error = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name,
3702 NULL);
3703 if (error)
3704 goto done;
3706 error = got_keyword_to_idstr(&keyword_idstr, commit_id_str,
3707 repo, worktree);
3708 if (error != NULL)
3709 goto done;
3710 if (keyword_idstr != NULL) {
3711 free(commit_id_str);
3712 commit_id_str = keyword_idstr;
3715 error = got_repo_match_object_id(&commit_id, NULL,
3716 commit_id_str, GOT_OBJ_TYPE_COMMIT, &refs, repo);
3717 got_ref_list_free(&refs);
3718 free(commit_id_str);
3719 commit_id_str = NULL;
3720 if (error)
3721 goto done;
3722 error = got_object_id_str(&commit_id_str, commit_id);
3723 if (error)
3724 goto done;
3727 if (branch_name) {
3728 struct got_object_id *head_commit_id;
3729 TAILQ_FOREACH(pe, &paths, entry) {
3730 if (pe->path_len == 0)
3731 continue;
3732 error = got_error_msg(GOT_ERR_BAD_PATH,
3733 "switching between branches requires that "
3734 "the entire work tree gets updated");
3735 goto done;
3737 error = got_ref_resolve(&head_commit_id, repo, head_ref);
3738 if (error)
3739 goto done;
3740 error = check_linear_ancestry(commit_id, head_commit_id, 0,
3741 repo);
3742 free(head_commit_id);
3743 if (error != NULL)
3744 goto done;
3745 error = check_same_branch(commit_id, head_ref, repo);
3746 if (error)
3747 goto done;
3748 error = switch_head_ref(head_ref, commit_id, worktree, repo);
3749 if (error)
3750 goto done;
3751 } else {
3752 error = check_linear_ancestry(commit_id,
3753 got_worktree_get_base_commit_id(worktree), 0, repo);
3754 if (error != NULL) {
3755 if (error->code == GOT_ERR_ANCESTRY)
3756 error = got_error(GOT_ERR_BRANCH_MOVED);
3757 goto done;
3759 error = check_same_branch(commit_id, head_ref, repo);
3760 if (error)
3761 goto done;
3764 if (got_object_id_cmp(got_worktree_get_base_commit_id(worktree),
3765 commit_id) != 0) {
3766 error = got_worktree_set_base_commit_id(worktree, repo,
3767 commit_id);
3768 if (error)
3769 goto done;
3772 memset(&upa, 0, sizeof(upa));
3773 upa.verbosity = verbosity;
3774 error = got_worktree_checkout_files(worktree, &paths, repo,
3775 update_progress, &upa, check_cancelled, NULL);
3776 if (error != NULL)
3777 goto done;
3779 if (upa.did_something) {
3780 printf("Updated to %s: %s\n",
3781 got_worktree_get_head_ref_name(worktree), commit_id_str);
3782 } else
3783 printf("Already up-to-date\n");
3785 print_update_progress_stats(&upa);
3786 done:
3787 if (pack_fds) {
3788 const struct got_error *pack_err =
3789 got_repo_pack_fds_close(pack_fds);
3790 if (error == NULL)
3791 error = pack_err;
3793 if (repo) {
3794 close_err = got_repo_close(repo);
3795 if (error == NULL)
3796 error = close_err;
3798 if (worktree != NULL) {
3799 close_err = got_worktree_close(worktree);
3800 if (error == NULL)
3801 error = close_err;
3803 if (head_ref != NULL)
3804 got_ref_close(head_ref);
3805 free(worktree_path);
3806 got_pathlist_free(&paths, GOT_PATHLIST_FREE_PATH);
3807 free(commit_id);
3808 free(commit_id_str);
3809 return error;
3812 static const struct got_error *
3813 diff_blobs(struct got_object_id *blob_id1, struct got_object_id *blob_id2,
3814 const char *path, int diff_context, int ignore_whitespace,
3815 int force_text_diff, struct got_diffstat_cb_arg *dsa,
3816 struct got_repository *repo, FILE *outfile)
3818 const struct got_error *err = NULL;
3819 struct got_blob_object *blob1 = NULL, *blob2 = NULL;
3820 FILE *f1 = NULL, *f2 = NULL;
3821 int fd1 = -1, fd2 = -1;
3823 fd1 = got_opentempfd();
3824 if (fd1 == -1)
3825 return got_error_from_errno("got_opentempfd");
3826 fd2 = got_opentempfd();
3827 if (fd2 == -1) {
3828 err = got_error_from_errno("got_opentempfd");
3829 goto done;
3832 if (blob_id1) {
3833 err = got_object_open_as_blob(&blob1, repo, blob_id1, 8192,
3834 fd1);
3835 if (err)
3836 goto done;
3839 err = got_object_open_as_blob(&blob2, repo, blob_id2, 8192, fd2);
3840 if (err)
3841 goto done;
3843 f1 = got_opentemp();
3844 if (f1 == NULL) {
3845 err = got_error_from_errno("got_opentemp");
3846 goto done;
3848 f2 = got_opentemp();
3849 if (f2 == NULL) {
3850 err = got_error_from_errno("got_opentemp");
3851 goto done;
3854 while (path[0] == '/')
3855 path++;
3856 err = got_diff_blob(NULL, NULL, blob1, blob2, f1, f2, path, path,
3857 GOT_DIFF_ALGORITHM_PATIENCE, diff_context, ignore_whitespace,
3858 force_text_diff, dsa, outfile);
3859 done:
3860 if (fd1 != -1 && close(fd1) == -1 && err == NULL)
3861 err = got_error_from_errno("close");
3862 if (blob1)
3863 got_object_blob_close(blob1);
3864 if (fd2 != -1 && close(fd2) == -1 && err == NULL)
3865 err = got_error_from_errno("close");
3866 if (blob2)
3867 got_object_blob_close(blob2);
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 return err;
3875 static const struct got_error *
3876 diff_trees(struct got_object_id *tree_id1, struct got_object_id *tree_id2,
3877 const char *path, int diff_context, int ignore_whitespace,
3878 int force_text_diff, struct got_diffstat_cb_arg *dsa,
3879 struct got_repository *repo, FILE *outfile)
3881 const struct got_error *err = NULL;
3882 struct got_tree_object *tree1 = NULL, *tree2 = NULL;
3883 struct got_diff_blob_output_unidiff_arg arg;
3884 FILE *f1 = NULL, *f2 = NULL;
3885 int fd1 = -1, fd2 = -1;
3887 if (tree_id1) {
3888 err = got_object_open_as_tree(&tree1, repo, tree_id1);
3889 if (err)
3890 goto done;
3891 fd1 = got_opentempfd();
3892 if (fd1 == -1) {
3893 err = got_error_from_errno("got_opentempfd");
3894 goto done;
3898 err = got_object_open_as_tree(&tree2, repo, tree_id2);
3899 if (err)
3900 goto done;
3902 f1 = got_opentemp();
3903 if (f1 == NULL) {
3904 err = got_error_from_errno("got_opentemp");
3905 goto done;
3908 f2 = got_opentemp();
3909 if (f2 == NULL) {
3910 err = got_error_from_errno("got_opentemp");
3911 goto done;
3913 fd2 = got_opentempfd();
3914 if (fd2 == -1) {
3915 err = got_error_from_errno("got_opentempfd");
3916 goto done;
3918 arg.diff_context = diff_context;
3919 arg.ignore_whitespace = ignore_whitespace;
3920 arg.force_text_diff = force_text_diff;
3921 arg.diffstat = dsa;
3922 arg.diff_algo = GOT_DIFF_ALGORITHM_PATIENCE;
3923 arg.outfile = outfile;
3924 arg.lines = NULL;
3925 arg.nlines = 0;
3926 while (path[0] == '/')
3927 path++;
3928 err = got_diff_tree(tree1, tree2, f1, f2, fd1, fd2, path, path, repo,
3929 got_diff_blob_output_unidiff, &arg, 1);
3930 done:
3931 if (tree1)
3932 got_object_tree_close(tree1);
3933 if (tree2)
3934 got_object_tree_close(tree2);
3935 if (f1 && fclose(f1) == EOF && err == NULL)
3936 err = got_error_from_errno("fclose");
3937 if (f2 && fclose(f2) == EOF && err == NULL)
3938 err = got_error_from_errno("fclose");
3939 if (fd1 != -1 && close(fd1) == -1 && err == NULL)
3940 err = got_error_from_errno("close");
3941 if (fd2 != -1 && close(fd2) == -1 && err == NULL)
3942 err = got_error_from_errno("close");
3943 return err;
3946 static const struct got_error *
3947 get_changed_paths(struct got_pathlist_head *paths,
3948 struct got_commit_object *commit, struct got_repository *repo,
3949 struct got_diffstat_cb_arg *dsa)
3951 const struct got_error *err = NULL;
3952 struct got_object_id *tree_id1 = NULL, *tree_id2 = NULL;
3953 struct got_tree_object *tree1 = NULL, *tree2 = NULL;
3954 struct got_object_qid *qid;
3955 got_diff_blob_cb cb = got_diff_tree_collect_changed_paths;
3956 FILE *f1 = NULL, *f2 = NULL;
3957 int fd1 = -1, fd2 = -1;
3959 if (dsa) {
3960 cb = got_diff_tree_compute_diffstat;
3962 f1 = got_opentemp();
3963 if (f1 == NULL) {
3964 err = got_error_from_errno("got_opentemp");
3965 goto done;
3967 f2 = got_opentemp();
3968 if (f2 == NULL) {
3969 err = got_error_from_errno("got_opentemp");
3970 goto done;
3972 fd1 = got_opentempfd();
3973 if (fd1 == -1) {
3974 err = got_error_from_errno("got_opentempfd");
3975 goto done;
3977 fd2 = got_opentempfd();
3978 if (fd2 == -1) {
3979 err = got_error_from_errno("got_opentempfd");
3980 goto done;
3984 qid = STAILQ_FIRST(got_object_commit_get_parent_ids(commit));
3985 if (qid != NULL) {
3986 struct got_commit_object *pcommit;
3987 err = got_object_open_as_commit(&pcommit, repo,
3988 &qid->id);
3989 if (err)
3990 return err;
3992 tree_id1 = got_object_id_dup(
3993 got_object_commit_get_tree_id(pcommit));
3994 if (tree_id1 == NULL) {
3995 got_object_commit_close(pcommit);
3996 return got_error_from_errno("got_object_id_dup");
3998 got_object_commit_close(pcommit);
4002 if (tree_id1) {
4003 err = got_object_open_as_tree(&tree1, repo, tree_id1);
4004 if (err)
4005 goto done;
4008 tree_id2 = got_object_commit_get_tree_id(commit);
4009 err = got_object_open_as_tree(&tree2, repo, tree_id2);
4010 if (err)
4011 goto done;
4013 err = got_diff_tree(tree1, tree2, f1, f2, fd1, fd2, "", "", repo,
4014 cb, dsa ? (void *)dsa : paths, dsa ? 1 : 0);
4015 done:
4016 if (tree1)
4017 got_object_tree_close(tree1);
4018 if (tree2)
4019 got_object_tree_close(tree2);
4020 if (fd1 != -1 && close(fd1) == -1 && err == NULL)
4021 err = got_error_from_errno("close");
4022 if (fd2 != -1 && close(fd2) == -1 && err == NULL)
4023 err = got_error_from_errno("close");
4024 if (f1 && fclose(f1) == EOF && err == NULL)
4025 err = got_error_from_errno("fclose");
4026 if (f2 && fclose(f2) == EOF && err == NULL)
4027 err = got_error_from_errno("fclose");
4028 free(tree_id1);
4029 return err;
4032 static const struct got_error *
4033 print_patch(struct got_commit_object *commit, struct got_object_id *id,
4034 const char *path, int diff_context, struct got_diffstat_cb_arg *dsa,
4035 struct got_repository *repo, FILE *outfile)
4037 const struct got_error *err = NULL;
4038 struct got_commit_object *pcommit = NULL;
4039 char *id_str1 = NULL, *id_str2 = NULL;
4040 struct got_object_id *obj_id1 = NULL, *obj_id2 = NULL;
4041 struct got_object_qid *qid;
4043 qid = STAILQ_FIRST(got_object_commit_get_parent_ids(commit));
4044 if (qid != NULL) {
4045 err = got_object_open_as_commit(&pcommit, repo,
4046 &qid->id);
4047 if (err)
4048 return err;
4049 err = got_object_id_str(&id_str1, &qid->id);
4050 if (err)
4051 goto done;
4054 err = got_object_id_str(&id_str2, id);
4055 if (err)
4056 goto done;
4058 if (path && path[0] != '\0') {
4059 int obj_type;
4060 err = got_object_id_by_path(&obj_id2, repo, commit, path);
4061 if (err)
4062 goto done;
4063 if (pcommit) {
4064 err = got_object_id_by_path(&obj_id1, repo,
4065 pcommit, path);
4066 if (err) {
4067 if (err->code != GOT_ERR_NO_TREE_ENTRY) {
4068 free(obj_id2);
4069 goto done;
4073 err = got_object_get_type(&obj_type, repo, obj_id2);
4074 if (err) {
4075 free(obj_id2);
4076 goto done;
4078 fprintf(outfile,
4079 "diff %s %s\n", id_str1 ? id_str1 : "/dev/null", id_str2);
4080 fprintf(outfile, "commit - %s\n",
4081 id_str1 ? id_str1 : "/dev/null");
4082 fprintf(outfile, "commit + %s\n", id_str2);
4083 switch (obj_type) {
4084 case GOT_OBJ_TYPE_BLOB:
4085 err = diff_blobs(obj_id1, obj_id2, path, diff_context,
4086 0, 0, dsa, repo, outfile);
4087 break;
4088 case GOT_OBJ_TYPE_TREE:
4089 err = diff_trees(obj_id1, obj_id2, path, diff_context,
4090 0, 0, dsa, repo, outfile);
4091 break;
4092 default:
4093 err = got_error(GOT_ERR_OBJ_TYPE);
4094 break;
4096 free(obj_id1);
4097 free(obj_id2);
4098 } else {
4099 obj_id2 = got_object_commit_get_tree_id(commit);
4100 if (pcommit)
4101 obj_id1 = got_object_commit_get_tree_id(pcommit);
4102 fprintf(outfile,
4103 "diff %s %s\n", id_str1 ? id_str1 : "/dev/null", id_str2);
4104 fprintf(outfile, "commit - %s\n",
4105 id_str1 ? id_str1 : "/dev/null");
4106 fprintf(outfile, "commit + %s\n", id_str2);
4107 err = diff_trees(obj_id1, obj_id2, "", diff_context, 0, 0,
4108 dsa, repo, outfile);
4110 done:
4111 free(id_str1);
4112 free(id_str2);
4113 if (pcommit)
4114 got_object_commit_close(pcommit);
4115 return err;
4118 static char *
4119 get_datestr(time_t *time, char *datebuf)
4121 struct tm mytm, *tm;
4122 char *p, *s;
4124 tm = gmtime_r(time, &mytm);
4125 if (tm == NULL)
4126 return NULL;
4127 s = asctime_r(tm, datebuf);
4128 if (s == NULL)
4129 return NULL;
4130 p = strchr(s, '\n');
4131 if (p)
4132 *p = '\0';
4133 return s;
4136 static const struct got_error *
4137 match_commit(int *have_match, struct got_object_id *id,
4138 struct got_commit_object *commit, regex_t *regex)
4140 const struct got_error *err = NULL;
4141 regmatch_t regmatch;
4142 char *id_str = NULL, *logmsg = NULL;
4144 *have_match = 0;
4146 err = got_object_id_str(&id_str, id);
4147 if (err)
4148 return err;
4150 err = got_object_commit_get_logmsg(&logmsg, commit);
4151 if (err)
4152 goto done;
4154 if (regexec(regex, got_object_commit_get_author(commit), 1,
4155 &regmatch, 0) == 0 ||
4156 regexec(regex, got_object_commit_get_committer(commit), 1,
4157 &regmatch, 0) == 0 ||
4158 regexec(regex, id_str, 1, &regmatch, 0) == 0 ||
4159 regexec(regex, logmsg, 1, &regmatch, 0) == 0)
4160 *have_match = 1;
4161 done:
4162 free(id_str);
4163 free(logmsg);
4164 return err;
4167 static void
4168 match_changed_paths(int *have_match, struct got_pathlist_head *changed_paths,
4169 regex_t *regex)
4171 regmatch_t regmatch;
4172 struct got_pathlist_entry *pe;
4174 *have_match = 0;
4176 TAILQ_FOREACH(pe, changed_paths, entry) {
4177 if (regexec(regex, pe->path, 1, &regmatch, 0) == 0) {
4178 *have_match = 1;
4179 break;
4184 static const struct got_error *
4185 match_patch(int *have_match, struct got_commit_object *commit,
4186 struct got_object_id *id, const char *path, int diff_context,
4187 struct got_repository *repo, regex_t *regex, FILE *f)
4189 const struct got_error *err = NULL;
4190 char *line = NULL;
4191 size_t linesize = 0;
4192 regmatch_t regmatch;
4194 *have_match = 0;
4196 err = got_opentemp_truncate(f);
4197 if (err)
4198 return err;
4200 err = print_patch(commit, id, path, diff_context, NULL, repo, f);
4201 if (err)
4202 goto done;
4204 if (fseeko(f, 0L, SEEK_SET) == -1) {
4205 err = got_error_from_errno("fseeko");
4206 goto done;
4209 while (getline(&line, &linesize, f) != -1) {
4210 if (regexec(regex, line, 1, &regmatch, 0) == 0) {
4211 *have_match = 1;
4212 break;
4215 done:
4216 free(line);
4217 return err;
4220 #define GOT_COMMIT_SEP_STR "-----------------------------------------------\n"
4222 static const struct got_error*
4223 build_refs_str(char **refs_str, struct got_reflist_head *refs,
4224 struct got_object_id *id, struct got_repository *repo,
4225 int local_only)
4227 static const struct got_error *err = NULL;
4228 struct got_reflist_entry *re;
4229 char *s;
4230 const char *name;
4232 *refs_str = NULL;
4234 TAILQ_FOREACH(re, refs, entry) {
4235 struct got_tag_object *tag = NULL;
4236 struct got_object_id *ref_id;
4237 int cmp;
4239 name = got_ref_get_name(re->ref);
4240 if (strcmp(name, GOT_REF_HEAD) == 0)
4241 continue;
4242 if (strncmp(name, "refs/", 5) == 0)
4243 name += 5;
4244 if (strncmp(name, "got/", 4) == 0)
4245 continue;
4246 if (strncmp(name, "heads/", 6) == 0)
4247 name += 6;
4248 if (strncmp(name, "remotes/", 8) == 0) {
4249 if (local_only)
4250 continue;
4251 name += 8;
4252 s = strstr(name, "/" GOT_REF_HEAD);
4253 if (s != NULL && strcmp(s, "/" GOT_REF_HEAD) == 0)
4254 continue;
4256 err = got_ref_resolve(&ref_id, repo, re->ref);
4257 if (err)
4258 break;
4259 if (strncmp(name, "tags/", 5) == 0) {
4260 err = got_object_open_as_tag(&tag, repo, ref_id);
4261 if (err) {
4262 if (err->code != GOT_ERR_OBJ_TYPE) {
4263 free(ref_id);
4264 break;
4266 /* Ref points at something other than a tag. */
4267 err = NULL;
4268 tag = NULL;
4271 cmp = got_object_id_cmp(tag ?
4272 got_object_tag_get_object_id(tag) : ref_id, id);
4273 free(ref_id);
4274 if (tag)
4275 got_object_tag_close(tag);
4276 if (cmp != 0)
4277 continue;
4278 s = *refs_str;
4279 if (asprintf(refs_str, "%s%s%s", s ? s : "",
4280 s ? ", " : "", name) == -1) {
4281 err = got_error_from_errno("asprintf");
4282 free(s);
4283 *refs_str = NULL;
4284 break;
4286 free(s);
4289 return err;
4292 static const struct got_error *
4293 print_commit_oneline(struct got_commit_object *commit, struct got_object_id *id,
4294 struct got_repository *repo, struct got_reflist_object_id_map *refs_idmap)
4296 const struct got_error *err = NULL;
4297 char *ref_str = NULL, *id_str = NULL, *logmsg0 = NULL;
4298 char *comma, *s, *nl;
4299 struct got_reflist_head *refs;
4300 char datebuf[12]; /* YYYY-MM-DD + SPACE + NUL */
4301 struct tm tm;
4302 time_t committer_time;
4304 refs = got_reflist_object_id_map_lookup(refs_idmap, id);
4305 if (refs) {
4306 err = build_refs_str(&ref_str, refs, id, repo, 1);
4307 if (err)
4308 return err;
4310 /* Display the first matching ref only. */
4311 if (ref_str && (comma = strchr(ref_str, ',')) != NULL)
4312 *comma = '\0';
4315 if (ref_str == NULL) {
4316 err = got_object_id_str(&id_str, id);
4317 if (err)
4318 return err;
4321 committer_time = got_object_commit_get_committer_time(commit);
4322 if (gmtime_r(&committer_time, &tm) == NULL) {
4323 err = got_error_from_errno("gmtime_r");
4324 goto done;
4326 if (strftime(datebuf, sizeof(datebuf), "%F ", &tm) == 0) {
4327 err = got_error(GOT_ERR_NO_SPACE);
4328 goto done;
4331 err = got_object_commit_get_logmsg(&logmsg0, commit);
4332 if (err)
4333 goto done;
4335 s = logmsg0;
4336 while (isspace((unsigned char)s[0]))
4337 s++;
4339 nl = strchr(s, '\n');
4340 if (nl) {
4341 *nl = '\0';
4344 if (ref_str)
4345 printf("%s%-7s %s\n", datebuf, ref_str, s);
4346 else
4347 printf("%s%.7s %s\n", datebuf, id_str, s);
4349 if (fflush(stdout) != 0 && err == NULL)
4350 err = got_error_from_errno("fflush");
4351 done:
4352 free(id_str);
4353 free(ref_str);
4354 free(logmsg0);
4355 return err;
4358 static const struct got_error *
4359 print_diffstat(struct got_diffstat_cb_arg *dsa, const char *header)
4361 struct got_pathlist_entry *pe;
4363 if (header != NULL)
4364 printf("%s\n", header);
4366 TAILQ_FOREACH(pe, dsa->paths, entry) {
4367 struct got_diff_changed_path *cp = pe->data;
4368 int pad = dsa->max_path_len - pe->path_len + 1;
4370 printf(" %c %s%*c | %*d+ %*d-\n", cp->status, pe->path, pad,
4371 ' ', dsa->add_cols + 1, cp->add, dsa->rm_cols + 1, cp->rm);
4373 printf("\n%d file%s changed, %d insertion%s(+), %d deletion%s(-)\n\n",
4374 dsa->nfiles, dsa->nfiles > 1 ? "s" : "", dsa->ins,
4375 dsa->ins != 1 ? "s" : "", dsa->del, dsa->del != 1 ? "s" : "");
4377 if (fflush(stdout) != 0)
4378 return got_error_from_errno("fflush");
4380 return NULL;
4383 static const struct got_error *
4384 printfile(FILE *f)
4386 char buf[8192];
4387 size_t r;
4389 if (fseeko(f, 0L, SEEK_SET) == -1)
4390 return got_error_from_errno("fseek");
4392 for (;;) {
4393 r = fread(buf, 1, sizeof(buf), f);
4394 if (r == 0) {
4395 if (ferror(f))
4396 return got_error_from_errno("fread");
4397 if (feof(f))
4398 break;
4400 if (fwrite(buf, 1, r, stdout) != r)
4401 return got_ferror(stdout, GOT_ERR_IO);
4404 return NULL;
4407 static const struct got_error *
4408 print_commit(struct got_commit_object *commit, struct got_object_id *id,
4409 struct got_repository *repo, const char *path,
4410 struct got_pathlist_head *changed_paths,
4411 struct got_diffstat_cb_arg *diffstat, int show_patch, int diff_context,
4412 struct got_reflist_object_id_map *refs_idmap, const char *custom_refs_str,
4413 const char *prefix)
4415 const struct got_error *err = NULL;
4416 FILE *f = NULL;
4417 char *id_str, *datestr, *logmsg0, *logmsg, *line;
4418 char datebuf[26];
4419 time_t committer_time;
4420 const char *author, *committer;
4421 char *refs_str = NULL;
4423 err = got_object_id_str(&id_str, id);
4424 if (err)
4425 return err;
4427 if (custom_refs_str == NULL) {
4428 struct got_reflist_head *refs;
4429 refs = got_reflist_object_id_map_lookup(refs_idmap, id);
4430 if (refs) {
4431 err = build_refs_str(&refs_str, refs, id, repo, 0);
4432 if (err)
4433 goto done;
4437 printf(GOT_COMMIT_SEP_STR);
4438 if (custom_refs_str)
4439 printf("%s %s (%s)\n", prefix ? prefix : "commit", id_str,
4440 custom_refs_str);
4441 else
4442 printf("%s %s%s%s%s\n", prefix ? prefix : "commit", id_str,
4443 refs_str ? " (" : "", refs_str ? refs_str : "",
4444 refs_str ? ")" : "");
4445 free(id_str);
4446 id_str = NULL;
4447 free(refs_str);
4448 refs_str = NULL;
4449 printf("from: %s\n", got_object_commit_get_author(commit));
4450 author = got_object_commit_get_author(commit);
4451 committer = got_object_commit_get_committer(commit);
4452 if (strcmp(author, committer) != 0)
4453 printf("via: %s\n", committer);
4454 committer_time = got_object_commit_get_committer_time(commit);
4455 datestr = get_datestr(&committer_time, datebuf);
4456 if (datestr)
4457 printf("date: %s UTC\n", datestr);
4458 if (got_object_commit_get_nparents(commit) > 1) {
4459 const struct got_object_id_queue *parent_ids;
4460 struct got_object_qid *qid;
4461 int n = 1;
4462 parent_ids = got_object_commit_get_parent_ids(commit);
4463 STAILQ_FOREACH(qid, parent_ids, entry) {
4464 err = got_object_id_str(&id_str, &qid->id);
4465 if (err)
4466 goto done;
4467 printf("parent %d: %s\n", n++, id_str);
4468 free(id_str);
4469 id_str = NULL;
4473 err = got_object_commit_get_logmsg(&logmsg0, commit);
4474 if (err)
4475 goto done;
4477 logmsg = logmsg0;
4478 do {
4479 line = strsep(&logmsg, "\n");
4480 if (line)
4481 printf(" %s\n", line);
4482 } while (line);
4483 free(logmsg0);
4485 if (changed_paths && diffstat == NULL) {
4486 struct got_pathlist_entry *pe;
4488 TAILQ_FOREACH(pe, changed_paths, entry) {
4489 struct got_diff_changed_path *cp = pe->data;
4491 printf(" %c %s\n", cp->status, pe->path);
4493 printf("\n");
4495 if (show_patch) {
4496 if (diffstat) {
4497 f = got_opentemp();
4498 if (f == NULL) {
4499 err = got_error_from_errno("got_opentemp");
4500 goto done;
4504 err = print_patch(commit, id, path, diff_context, diffstat,
4505 repo, diffstat == NULL ? stdout : f);
4506 if (err)
4507 goto done;
4509 if (diffstat) {
4510 err = print_diffstat(diffstat, NULL);
4511 if (err)
4512 goto done;
4513 if (show_patch) {
4514 err = printfile(f);
4515 if (err)
4516 goto done;
4519 if (show_patch)
4520 printf("\n");
4522 if (fflush(stdout) != 0 && err == NULL)
4523 err = got_error_from_errno("fflush");
4524 done:
4525 if (f && fclose(f) == EOF && err == NULL)
4526 err = got_error_from_errno("fclose");
4527 free(id_str);
4528 free(refs_str);
4529 return err;
4532 static const struct got_error *
4533 print_commits(struct got_object_id *root_id, struct got_object_id *end_id,
4534 struct got_repository *repo, const char *path, int show_changed_paths,
4535 int show_diffstat, int show_patch, const char *search_pattern,
4536 int diff_context, int limit, int log_branches, int reverse_display_order,
4537 struct got_reflist_object_id_map *refs_idmap, int one_line, int toposort,
4538 FILE *tmpfile)
4540 const struct got_error *err;
4541 struct got_commit_graph *graph;
4542 regex_t regex;
4543 int have_match;
4544 struct got_object_id_queue reversed_commits;
4545 struct got_object_qid *qid;
4546 struct got_commit_object *commit;
4547 struct got_pathlist_head changed_paths;
4549 STAILQ_INIT(&reversed_commits);
4550 TAILQ_INIT(&changed_paths);
4552 if (search_pattern && regcomp(&regex, search_pattern,
4553 REG_EXTENDED | REG_NOSUB | REG_NEWLINE))
4554 return got_error_msg(GOT_ERR_REGEX, search_pattern);
4556 err = got_commit_graph_open(&graph, path, !log_branches);
4557 if (err)
4558 return err;
4559 if (log_branches && toposort) {
4560 err = got_commit_graph_toposort(graph, root_id, repo,
4561 check_cancelled, NULL);
4562 } else {
4563 err = got_commit_graph_bfsort(graph, root_id, repo,
4564 check_cancelled, NULL);
4566 if (err)
4567 goto done;
4568 for (;;) {
4569 struct got_object_id id;
4570 struct got_diffstat_cb_arg dsa = { 0, 0, 0, 0, 0, 0,
4571 &changed_paths, 0, 0, GOT_DIFF_ALGORITHM_PATIENCE };
4573 if (sigint_received || sigpipe_received)
4574 break;
4576 err = got_commit_graph_iter_next(&id, graph, repo,
4577 check_cancelled, NULL);
4578 if (err) {
4579 if (err->code == GOT_ERR_ITER_COMPLETED)
4580 err = NULL;
4581 break;
4584 err = got_object_open_as_commit(&commit, repo, &id);
4585 if (err)
4586 break;
4588 if (((show_changed_paths && !show_diffstat) ||
4589 (show_diffstat && !show_patch))
4590 && !reverse_display_order) {
4591 err = get_changed_paths(&changed_paths, commit, repo,
4592 show_diffstat ? &dsa : NULL);
4593 if (err)
4594 break;
4597 if (search_pattern) {
4598 err = match_commit(&have_match, &id, commit, &regex);
4599 if (err) {
4600 got_object_commit_close(commit);
4601 break;
4603 if (have_match == 0 && show_changed_paths)
4604 match_changed_paths(&have_match,
4605 &changed_paths, &regex);
4606 if (have_match == 0 && show_patch) {
4607 err = match_patch(&have_match, commit, &id,
4608 path, diff_context, repo, &regex, tmpfile);
4609 if (err)
4610 break;
4612 if (have_match == 0) {
4613 got_object_commit_close(commit);
4614 got_pathlist_free(&changed_paths,
4615 GOT_PATHLIST_FREE_ALL);
4616 continue;
4620 if (reverse_display_order) {
4621 err = got_object_qid_alloc(&qid, &id);
4622 if (err)
4623 break;
4624 STAILQ_INSERT_HEAD(&reversed_commits, qid, entry);
4625 got_object_commit_close(commit);
4626 } else {
4627 if (one_line)
4628 err = print_commit_oneline(commit, &id,
4629 repo, refs_idmap);
4630 else
4631 err = print_commit(commit, &id, repo, path,
4632 (show_changed_paths || show_diffstat) ?
4633 &changed_paths : NULL,
4634 show_diffstat ? &dsa : NULL, show_patch,
4635 diff_context, refs_idmap, NULL, NULL);
4636 got_object_commit_close(commit);
4637 if (err)
4638 break;
4640 if ((limit && --limit == 0) ||
4641 (end_id && got_object_id_cmp(&id, end_id) == 0))
4642 break;
4644 got_pathlist_free(&changed_paths, GOT_PATHLIST_FREE_ALL);
4646 if (reverse_display_order) {
4647 STAILQ_FOREACH(qid, &reversed_commits, entry) {
4648 struct got_diffstat_cb_arg dsa = { 0, 0, 0, 0, 0, 0,
4649 &changed_paths, 0, 0, GOT_DIFF_ALGORITHM_PATIENCE };
4651 err = got_object_open_as_commit(&commit, repo,
4652 &qid->id);
4653 if (err)
4654 break;
4655 if ((show_changed_paths && !show_diffstat) ||
4656 (show_diffstat && !show_patch)) {
4657 err = get_changed_paths(&changed_paths, commit,
4658 repo, show_diffstat ? &dsa : NULL);
4659 if (err)
4660 break;
4662 if (one_line)
4663 err = print_commit_oneline(commit, &qid->id,
4664 repo, refs_idmap);
4665 else
4666 err = print_commit(commit, &qid->id, repo, path,
4667 (show_changed_paths || show_diffstat) ?
4668 &changed_paths : NULL,
4669 show_diffstat ? &dsa : NULL, show_patch,
4670 diff_context, refs_idmap, NULL, NULL);
4671 got_object_commit_close(commit);
4672 if (err)
4673 break;
4674 got_pathlist_free(&changed_paths, GOT_PATHLIST_FREE_ALL);
4677 done:
4678 while (!STAILQ_EMPTY(&reversed_commits)) {
4679 qid = STAILQ_FIRST(&reversed_commits);
4680 STAILQ_REMOVE_HEAD(&reversed_commits, entry);
4681 got_object_qid_free(qid);
4683 got_pathlist_free(&changed_paths, GOT_PATHLIST_FREE_ALL);
4684 if (search_pattern)
4685 regfree(&regex);
4686 got_commit_graph_close(graph);
4687 return err;
4690 __dead static void
4691 usage_log(void)
4693 fprintf(stderr, "usage: %s log [-bdPpRst] [-C number] [-c commit] "
4694 "[-l N] [-r repository-path] [-S search-pattern] [-x commit] "
4695 "[path]\n", getprogname());
4696 exit(1);
4699 static int
4700 get_default_log_limit(void)
4702 const char *got_default_log_limit;
4703 long long n;
4704 const char *errstr;
4706 got_default_log_limit = getenv("GOT_LOG_DEFAULT_LIMIT");
4707 if (got_default_log_limit == NULL)
4708 return 0;
4709 n = strtonum(got_default_log_limit, 0, INT_MAX, &errstr);
4710 if (errstr != NULL)
4711 return 0;
4712 return n;
4715 static const struct got_error *
4716 cmd_log(int argc, char *argv[])
4718 const struct got_error *error;
4719 struct got_repository *repo = NULL;
4720 struct got_worktree *worktree = NULL;
4721 struct got_object_id *start_id = NULL, *end_id = NULL;
4722 char *repo_path = NULL, *path = NULL, *cwd = NULL, *in_repo_path = NULL;
4723 char *keyword_idstr = NULL;
4724 const char *start_commit = NULL, *end_commit = NULL;
4725 const char *search_pattern = NULL;
4726 int diff_context = -1, ch;
4727 int show_changed_paths = 0, show_patch = 0, limit = 0, log_branches = 0;
4728 int show_diffstat = 0, reverse_display_order = 0, one_line = 0;
4729 int toposort = 0;
4730 const char *errstr;
4731 struct got_reflist_head refs;
4732 struct got_reflist_object_id_map *refs_idmap = NULL;
4733 FILE *tmpfile = NULL;
4734 int *pack_fds = NULL;
4736 TAILQ_INIT(&refs);
4738 #ifndef PROFILE
4739 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
4740 NULL)
4741 == -1)
4742 err(1, "pledge");
4743 #endif
4745 limit = get_default_log_limit();
4747 while ((ch = getopt(argc, argv, "bC:c:dl:PpRr:S:stx:")) != -1) {
4748 switch (ch) {
4749 case 'b':
4750 log_branches = 1;
4751 break;
4752 case 'C':
4753 diff_context = strtonum(optarg, 0, GOT_DIFF_MAX_CONTEXT,
4754 &errstr);
4755 if (errstr != NULL)
4756 errx(1, "number of context lines is %s: %s",
4757 errstr, optarg);
4758 break;
4759 case 'c':
4760 start_commit = optarg;
4761 break;
4762 case 'd':
4763 show_diffstat = 1;
4764 break;
4765 case 'l':
4766 limit = strtonum(optarg, 0, INT_MAX, &errstr);
4767 if (errstr != NULL)
4768 errx(1, "number of commits is %s: %s",
4769 errstr, optarg);
4770 break;
4771 case 'P':
4772 show_changed_paths = 1;
4773 break;
4774 case 'p':
4775 show_patch = 1;
4776 break;
4777 case 'R':
4778 reverse_display_order = 1;
4779 break;
4780 case 'r':
4781 repo_path = realpath(optarg, NULL);
4782 if (repo_path == NULL)
4783 return got_error_from_errno2("realpath",
4784 optarg);
4785 got_path_strip_trailing_slashes(repo_path);
4786 break;
4787 case 'S':
4788 search_pattern = optarg;
4789 break;
4790 case 's':
4791 one_line = 1;
4792 break;
4793 case 't':
4794 toposort = 1;
4795 break;
4796 case 'x':
4797 end_commit = optarg;
4798 break;
4799 default:
4800 usage_log();
4801 /* NOTREACHED */
4805 argc -= optind;
4806 argv += optind;
4808 if (diff_context == -1)
4809 diff_context = 3;
4810 else if (!show_patch)
4811 errx(1, "-C requires -p");
4813 if (one_line && (show_patch || show_changed_paths || show_diffstat))
4814 errx(1, "cannot use -s with -d, -p or -P");
4816 cwd = getcwd(NULL, 0);
4817 if (cwd == NULL) {
4818 error = got_error_from_errno("getcwd");
4819 goto done;
4822 error = got_repo_pack_fds_open(&pack_fds);
4823 if (error != NULL)
4824 goto done;
4826 if (repo_path == NULL) {
4827 error = got_worktree_open(&worktree, cwd,
4828 GOT_WORKTREE_GOT_DIR);
4829 if (error && error->code != GOT_ERR_NOT_WORKTREE)
4830 goto done;
4831 error = NULL;
4834 if (argc == 1) {
4835 if (worktree) {
4836 error = got_worktree_resolve_path(&path, worktree,
4837 argv[0]);
4838 if (error)
4839 goto done;
4840 } else {
4841 path = strdup(argv[0]);
4842 if (path == NULL) {
4843 error = got_error_from_errno("strdup");
4844 goto done;
4847 } else if (argc != 0)
4848 usage_log();
4850 if (repo_path == NULL) {
4851 repo_path = worktree ?
4852 strdup(got_worktree_get_repo_path(worktree)) : strdup(cwd);
4854 if (repo_path == NULL) {
4855 error = got_error_from_errno("strdup");
4856 goto done;
4859 error = got_repo_open(&repo, repo_path, NULL, pack_fds);
4860 if (error != NULL)
4861 goto done;
4863 error = apply_unveil(got_repo_get_path(repo), 1,
4864 worktree ? got_worktree_get_root_path(worktree) : NULL);
4865 if (error)
4866 goto done;
4868 error = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name, NULL);
4869 if (error)
4870 goto done;
4872 error = got_reflist_object_id_map_create(&refs_idmap, &refs, repo);
4873 if (error)
4874 goto done;
4876 if (start_commit == NULL) {
4877 struct got_reference *head_ref;
4878 struct got_commit_object *commit = NULL;
4879 error = got_ref_open(&head_ref, repo,
4880 worktree ? got_worktree_get_head_ref_name(worktree)
4881 : GOT_REF_HEAD, 0);
4882 if (error != NULL)
4883 goto done;
4884 error = got_ref_resolve(&start_id, repo, head_ref);
4885 got_ref_close(head_ref);
4886 if (error != NULL)
4887 goto done;
4888 error = got_object_open_as_commit(&commit, repo,
4889 start_id);
4890 if (error != NULL)
4891 goto done;
4892 got_object_commit_close(commit);
4893 } else {
4894 error = got_keyword_to_idstr(&keyword_idstr, start_commit,
4895 repo, worktree);
4896 if (error != NULL)
4897 goto done;
4898 if (keyword_idstr != NULL)
4899 start_commit = keyword_idstr;
4901 error = got_repo_match_object_id(&start_id, NULL,
4902 start_commit, GOT_OBJ_TYPE_COMMIT, &refs, repo);
4903 if (error != NULL)
4904 goto done;
4906 if (end_commit != NULL) {
4907 error = got_keyword_to_idstr(&keyword_idstr, end_commit,
4908 repo, worktree);
4909 if (error != NULL)
4910 goto done;
4911 if (keyword_idstr != NULL)
4912 end_commit = keyword_idstr;
4914 error = got_repo_match_object_id(&end_id, NULL,
4915 end_commit, GOT_OBJ_TYPE_COMMIT, &refs, repo);
4916 if (error != NULL)
4917 goto done;
4920 if (worktree) {
4922 * If a path was specified on the command line it was resolved
4923 * to a path in the work tree above. Prepend the work tree's
4924 * path prefix to obtain the corresponding in-repository path.
4926 if (path) {
4927 const char *prefix;
4928 prefix = got_worktree_get_path_prefix(worktree);
4929 if (asprintf(&in_repo_path, "%s%s%s", prefix,
4930 (path[0] != '\0') ? "/" : "", path) == -1) {
4931 error = got_error_from_errno("asprintf");
4932 goto done;
4935 } else
4936 error = got_repo_map_path(&in_repo_path, repo,
4937 path ? path : "");
4938 if (error != NULL)
4939 goto done;
4940 if (in_repo_path) {
4941 free(path);
4942 path = in_repo_path;
4945 if (worktree) {
4946 /* Release work tree lock. */
4947 got_worktree_close(worktree);
4948 worktree = NULL;
4951 if (search_pattern && show_patch) {
4952 tmpfile = got_opentemp();
4953 if (tmpfile == NULL) {
4954 error = got_error_from_errno("got_opentemp");
4955 goto done;
4959 error = print_commits(start_id, end_id, repo, path ? path : "",
4960 show_changed_paths, show_diffstat, show_patch, search_pattern,
4961 diff_context, limit, log_branches, reverse_display_order,
4962 refs_idmap, one_line, toposort, tmpfile);
4963 done:
4964 free(path);
4965 free(repo_path);
4966 free(cwd);
4967 free(start_id);
4968 free(end_id);
4969 free(keyword_idstr);
4970 if (worktree)
4971 got_worktree_close(worktree);
4972 if (repo) {
4973 const struct got_error *close_err = got_repo_close(repo);
4974 if (error == NULL)
4975 error = close_err;
4977 if (pack_fds) {
4978 const struct got_error *pack_err =
4979 got_repo_pack_fds_close(pack_fds);
4980 if (error == NULL)
4981 error = pack_err;
4983 if (refs_idmap)
4984 got_reflist_object_id_map_free(refs_idmap);
4985 if (tmpfile && fclose(tmpfile) == EOF && error == NULL)
4986 error = got_error_from_errno("fclose");
4987 got_ref_list_free(&refs);
4988 return error;
4991 __dead static void
4992 usage_diff(void)
4994 fprintf(stderr, "usage: %s diff [-adPsw] [-C number] [-c commit] "
4995 "[-r repository-path] [object1 object2 | path ...]\n",
4996 getprogname());
4997 exit(1);
5000 struct print_diff_arg {
5001 struct got_repository *repo;
5002 struct got_worktree *worktree;
5003 struct got_diffstat_cb_arg *diffstat;
5004 int diff_context;
5005 const char *id_str;
5006 int header_shown;
5007 int diff_staged;
5008 enum got_diff_algorithm diff_algo;
5009 int ignore_whitespace;
5010 int force_text_diff;
5011 FILE *f1;
5012 FILE *f2;
5013 FILE *outfile;
5017 * Create a file which contains the target path of a symlink so we can feed
5018 * it as content to the diff engine.
5020 static const struct got_error *
5021 get_symlink_target_file(int *fd, int dirfd, const char *de_name,
5022 const char *abspath)
5024 const struct got_error *err = NULL;
5025 char target_path[PATH_MAX];
5026 ssize_t target_len, outlen;
5028 *fd = -1;
5030 if (dirfd != -1) {
5031 target_len = readlinkat(dirfd, de_name, target_path, PATH_MAX);
5032 if (target_len == -1)
5033 return got_error_from_errno2("readlinkat", abspath);
5034 } else {
5035 target_len = readlink(abspath, target_path, PATH_MAX);
5036 if (target_len == -1)
5037 return got_error_from_errno2("readlink", abspath);
5040 *fd = got_opentempfd();
5041 if (*fd == -1)
5042 return got_error_from_errno("got_opentempfd");
5044 outlen = write(*fd, target_path, target_len);
5045 if (outlen == -1) {
5046 err = got_error_from_errno("got_opentempfd");
5047 goto done;
5050 if (lseek(*fd, 0, SEEK_SET) == -1) {
5051 err = got_error_from_errno2("lseek", abspath);
5052 goto done;
5054 done:
5055 if (err) {
5056 close(*fd);
5057 *fd = -1;
5059 return err;
5062 static const struct got_error *
5063 emit_base_commit_header(FILE *f, struct got_object_id *commit_id,
5064 struct got_worktree *worktree)
5066 const struct got_error *err;
5067 struct got_object_id *base_commit_id;
5068 char *base_commit_idstr;
5070 if (worktree == NULL) /* shouldn't happen */
5071 return got_error(GOT_ERR_NOT_WORKTREE);
5073 base_commit_id = got_worktree_get_base_commit_id(worktree);
5075 if (commit_id != NULL) {
5076 if (got_object_id_cmp(commit_id, base_commit_id) != 0)
5077 base_commit_id = commit_id;
5080 err = got_object_id_str(&base_commit_idstr, base_commit_id);
5081 if (err != NULL)
5082 return err;
5084 if (fprintf(f, "commit - %s\n", base_commit_idstr) < 0)
5085 err = got_error_from_errno("fprintf");
5086 free(base_commit_idstr);
5087 return err;
5090 static const struct got_error *
5091 print_diff(void *arg, unsigned char status, unsigned char staged_status,
5092 const char *path, struct got_object_id *blob_id,
5093 struct got_object_id *staged_blob_id, struct got_object_id *commit_id,
5094 int dirfd, const char *de_name)
5096 struct print_diff_arg *a = arg;
5097 const struct got_error *err = NULL;
5098 struct got_blob_object *blob1 = NULL;
5099 int fd = -1, fd1 = -1, fd2 = -1;
5100 FILE *f2 = NULL;
5101 char *abspath = NULL, *label1 = NULL;
5102 struct stat sb;
5103 off_t size1 = 0;
5104 int f2_exists = 0;
5106 memset(&sb, 0, sizeof(sb));
5108 if (a->diff_staged) {
5109 if (staged_status != GOT_STATUS_MODIFY &&
5110 staged_status != GOT_STATUS_ADD &&
5111 staged_status != GOT_STATUS_DELETE)
5112 return NULL;
5113 } else {
5114 if (staged_status == GOT_STATUS_DELETE)
5115 return NULL;
5116 if (status == GOT_STATUS_NONEXISTENT)
5117 return got_error_set_errno(ENOENT, path);
5118 if (status != GOT_STATUS_MODIFY &&
5119 status != GOT_STATUS_ADD &&
5120 status != GOT_STATUS_DELETE &&
5121 status != GOT_STATUS_CONFLICT)
5122 return NULL;
5125 err = got_opentemp_truncate(a->f1);
5126 if (err)
5127 return got_error_from_errno("got_opentemp_truncate");
5128 err = got_opentemp_truncate(a->f2);
5129 if (err)
5130 return got_error_from_errno("got_opentemp_truncate");
5132 if (!a->header_shown) {
5133 if (fprintf(a->outfile, "diff %s%s\n",
5134 a->diff_staged ? "-s " : "",
5135 got_worktree_get_root_path(a->worktree)) < 0) {
5136 err = got_error_from_errno("fprintf");
5137 goto done;
5139 if (fprintf(a->outfile, "path + %s%s\n",
5140 got_worktree_get_root_path(a->worktree),
5141 a->diff_staged ? " (staged changes)" : "") < 0) {
5142 err = got_error_from_errno("fprintf");
5143 goto done;
5145 a->header_shown = 1;
5148 err = emit_base_commit_header(a->outfile, commit_id, a->worktree);
5149 if (err != NULL)
5150 goto done;
5152 if (a->diff_staged) {
5153 const char *label1 = NULL, *label2 = NULL;
5154 switch (staged_status) {
5155 case GOT_STATUS_MODIFY:
5156 label1 = path;
5157 label2 = path;
5158 break;
5159 case GOT_STATUS_ADD:
5160 label2 = path;
5161 break;
5162 case GOT_STATUS_DELETE:
5163 label1 = path;
5164 break;
5165 default:
5166 return got_error(GOT_ERR_FILE_STATUS);
5168 fd1 = got_opentempfd();
5169 if (fd1 == -1) {
5170 err = got_error_from_errno("got_opentempfd");
5171 goto done;
5173 fd2 = got_opentempfd();
5174 if (fd2 == -1) {
5175 err = got_error_from_errno("got_opentempfd");
5176 goto done;
5178 err = got_diff_objects_as_blobs(NULL, NULL, a->f1, a->f2,
5179 fd1, fd2, blob_id, staged_blob_id, label1, label2,
5180 a->diff_algo, a->diff_context, a->ignore_whitespace,
5181 a->force_text_diff, a->diffstat, a->repo, a->outfile);
5182 goto done;
5185 fd1 = got_opentempfd();
5186 if (fd1 == -1) {
5187 err = got_error_from_errno("got_opentempfd");
5188 goto done;
5191 if (staged_status == GOT_STATUS_ADD ||
5192 staged_status == GOT_STATUS_MODIFY) {
5193 char *id_str;
5194 err = got_object_open_as_blob(&blob1, a->repo, staged_blob_id,
5195 8192, fd1);
5196 if (err)
5197 goto done;
5198 err = got_object_id_str(&id_str, staged_blob_id);
5199 if (err)
5200 goto done;
5201 if (asprintf(&label1, "%s (staged)", id_str) == -1) {
5202 err = got_error_from_errno("asprintf");
5203 free(id_str);
5204 goto done;
5206 free(id_str);
5207 } else if (status != GOT_STATUS_ADD) {
5208 err = got_object_open_as_blob(&blob1, a->repo, blob_id, 8192,
5209 fd1);
5210 if (err)
5211 goto done;
5214 if (status != GOT_STATUS_DELETE) {
5215 if (asprintf(&abspath, "%s/%s",
5216 got_worktree_get_root_path(a->worktree), path) == -1) {
5217 err = got_error_from_errno("asprintf");
5218 goto done;
5221 if (dirfd != -1) {
5222 fd = openat(dirfd, de_name,
5223 O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
5224 if (fd == -1) {
5225 if (!got_err_open_nofollow_on_symlink()) {
5226 err = got_error_from_errno2("openat",
5227 abspath);
5228 goto done;
5230 err = get_symlink_target_file(&fd, dirfd,
5231 de_name, abspath);
5232 if (err)
5233 goto done;
5235 } else {
5236 fd = open(abspath, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
5237 if (fd == -1) {
5238 if (!got_err_open_nofollow_on_symlink()) {
5239 err = got_error_from_errno2("open",
5240 abspath);
5241 goto done;
5243 err = get_symlink_target_file(&fd, dirfd,
5244 de_name, abspath);
5245 if (err)
5246 goto done;
5249 if (fstatat(fd, abspath, &sb, AT_SYMLINK_NOFOLLOW) == -1) {
5250 err = got_error_from_errno2("fstatat", abspath);
5251 goto done;
5253 f2 = fdopen(fd, "r");
5254 if (f2 == NULL) {
5255 err = got_error_from_errno2("fdopen", abspath);
5256 goto done;
5258 fd = -1;
5259 f2_exists = 1;
5262 if (blob1) {
5263 err = got_object_blob_dump_to_file(&size1, NULL, NULL,
5264 a->f1, blob1);
5265 if (err)
5266 goto done;
5269 err = got_diff_blob_file(blob1, a->f1, size1, label1, f2 ? f2 : a->f2,
5270 f2_exists, &sb, path, GOT_DIFF_ALGORITHM_PATIENCE, a->diff_context,
5271 a->ignore_whitespace, a->force_text_diff, a->diffstat, a->outfile);
5272 done:
5273 if (fd1 != -1 && close(fd1) == -1 && err == NULL)
5274 err = got_error_from_errno("close");
5275 if (fd2 != -1 && close(fd2) == -1 && err == NULL)
5276 err = got_error_from_errno("close");
5277 if (blob1)
5278 got_object_blob_close(blob1);
5279 if (fd != -1 && close(fd) == -1 && err == NULL)
5280 err = got_error_from_errno("close");
5281 if (f2 && fclose(f2) == EOF && err == NULL)
5282 err = got_error_from_errno("fclose");
5283 free(abspath);
5284 return err;
5287 static const struct got_error *
5288 cmd_diff(int argc, char *argv[])
5290 const struct got_error *error;
5291 struct got_repository *repo = NULL;
5292 struct got_worktree *worktree = NULL;
5293 char *cwd = NULL, *repo_path = NULL;
5294 const char *commit_args[2] = { NULL, NULL };
5295 int ncommit_args = 0;
5296 struct got_object_id *ids[2] = { NULL, NULL };
5297 char *labels[2] = { NULL, NULL };
5298 int type1 = GOT_OBJ_TYPE_ANY, type2 = GOT_OBJ_TYPE_ANY;
5299 int diff_context = 3, diff_staged = 0, ignore_whitespace = 0, ch, i;
5300 int force_text_diff = 0, force_path = 0, rflag = 0, show_diffstat = 0;
5301 const char *errstr;
5302 struct got_reflist_head refs;
5303 struct got_pathlist_head diffstat_paths, paths;
5304 FILE *f1 = NULL, *f2 = NULL, *outfile = NULL;
5305 int fd1 = -1, fd2 = -1;
5306 int *pack_fds = NULL;
5307 struct got_diffstat_cb_arg dsa;
5309 memset(&dsa, 0, sizeof(dsa));
5311 TAILQ_INIT(&refs);
5312 TAILQ_INIT(&paths);
5313 TAILQ_INIT(&diffstat_paths);
5315 #ifndef PROFILE
5316 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
5317 NULL) == -1)
5318 err(1, "pledge");
5319 #endif
5321 while ((ch = getopt(argc, argv, "aC:c:dPr:sw")) != -1) {
5322 switch (ch) {
5323 case 'a':
5324 force_text_diff = 1;
5325 break;
5326 case 'C':
5327 diff_context = strtonum(optarg, 0, GOT_DIFF_MAX_CONTEXT,
5328 &errstr);
5329 if (errstr != NULL)
5330 errx(1, "number of context lines is %s: %s",
5331 errstr, optarg);
5332 break;
5333 case 'c':
5334 if (ncommit_args >= 2)
5335 errx(1, "too many -c options used");
5336 commit_args[ncommit_args++] = optarg;
5337 break;
5338 case 'd':
5339 show_diffstat = 1;
5340 break;
5341 case 'P':
5342 force_path = 1;
5343 break;
5344 case 'r':
5345 repo_path = realpath(optarg, NULL);
5346 if (repo_path == NULL)
5347 return got_error_from_errno2("realpath",
5348 optarg);
5349 got_path_strip_trailing_slashes(repo_path);
5350 rflag = 1;
5351 break;
5352 case 's':
5353 diff_staged = 1;
5354 break;
5355 case 'w':
5356 ignore_whitespace = 1;
5357 break;
5358 default:
5359 usage_diff();
5360 /* NOTREACHED */
5364 argc -= optind;
5365 argv += optind;
5367 cwd = getcwd(NULL, 0);
5368 if (cwd == NULL) {
5369 error = got_error_from_errno("getcwd");
5370 goto done;
5373 error = got_repo_pack_fds_open(&pack_fds);
5374 if (error != NULL)
5375 goto done;
5377 if (repo_path == NULL) {
5378 error = got_worktree_open(&worktree, cwd,
5379 GOT_WORKTREE_GOT_DIR);
5380 if (error && error->code != GOT_ERR_NOT_WORKTREE)
5381 goto done;
5382 else
5383 error = NULL;
5384 if (worktree) {
5385 repo_path =
5386 strdup(got_worktree_get_repo_path(worktree));
5387 if (repo_path == NULL) {
5388 error = got_error_from_errno("strdup");
5389 goto done;
5391 } else {
5392 repo_path = strdup(cwd);
5393 if (repo_path == NULL) {
5394 error = got_error_from_errno("strdup");
5395 goto done;
5400 error = got_repo_open(&repo, repo_path, NULL, pack_fds);
5401 free(repo_path);
5402 if (error != NULL)
5403 goto done;
5405 if (show_diffstat) {
5406 dsa.paths = &diffstat_paths;
5407 dsa.force_text = force_text_diff;
5408 dsa.ignore_ws = ignore_whitespace;
5409 dsa.diff_algo = GOT_DIFF_ALGORITHM_PATIENCE;
5412 if (rflag || worktree == NULL || ncommit_args > 0) {
5413 if (force_path) {
5414 error = got_error_msg(GOT_ERR_NOT_IMPL,
5415 "-P option can only be used when diffing "
5416 "a work tree");
5417 goto done;
5419 if (diff_staged) {
5420 error = got_error_msg(GOT_ERR_NOT_IMPL,
5421 "-s option can only be used when diffing "
5422 "a work tree");
5423 goto done;
5427 error = apply_unveil(got_repo_get_path(repo), 1,
5428 worktree ? got_worktree_get_root_path(worktree) : NULL);
5429 if (error)
5430 goto done;
5432 if ((!force_path && argc == 2) || ncommit_args > 0) {
5433 int obj_type = (ncommit_args > 0 ?
5434 GOT_OBJ_TYPE_COMMIT : GOT_OBJ_TYPE_ANY);
5435 error = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name,
5436 NULL);
5437 if (error)
5438 goto done;
5439 for (i = 0; i < (ncommit_args > 0 ? ncommit_args : argc); i++) {
5440 const char *arg;
5441 char *keyword_idstr = NULL;
5443 if (ncommit_args > 0)
5444 arg = commit_args[i];
5445 else
5446 arg = argv[i];
5448 error = got_keyword_to_idstr(&keyword_idstr, arg,
5449 repo, worktree);
5450 if (error != NULL)
5451 goto done;
5452 if (keyword_idstr != NULL)
5453 arg = keyword_idstr;
5455 error = got_repo_match_object_id(&ids[i], &labels[i],
5456 arg, obj_type, &refs, repo);
5457 free(keyword_idstr);
5458 if (error) {
5459 if (error->code != GOT_ERR_NOT_REF &&
5460 error->code != GOT_ERR_NO_OBJ)
5461 goto done;
5462 if (ncommit_args > 0)
5463 goto done;
5464 error = NULL;
5465 break;
5470 f1 = got_opentemp();
5471 if (f1 == NULL) {
5472 error = got_error_from_errno("got_opentemp");
5473 goto done;
5476 f2 = got_opentemp();
5477 if (f2 == NULL) {
5478 error = got_error_from_errno("got_opentemp");
5479 goto done;
5482 outfile = got_opentemp();
5483 if (outfile == NULL) {
5484 error = got_error_from_errno("got_opentemp");
5485 goto done;
5488 if (ncommit_args == 0 && (ids[0] == NULL || ids[1] == NULL)) {
5489 struct print_diff_arg arg;
5490 char *id_str;
5492 if (worktree == NULL) {
5493 if (argc == 2 && ids[0] == NULL) {
5494 error = got_error_path(argv[0], GOT_ERR_NO_OBJ);
5495 goto done;
5496 } else if (argc == 2 && ids[1] == NULL) {
5497 error = got_error_path(argv[1], GOT_ERR_NO_OBJ);
5498 goto done;
5499 } else if (argc > 0) {
5500 error = got_error_fmt(GOT_ERR_NOT_WORKTREE,
5501 "%s", "specified paths cannot be resolved");
5502 goto done;
5503 } else {
5504 error = got_error(GOT_ERR_NOT_WORKTREE);
5505 goto done;
5509 error = get_worktree_paths_from_argv(&paths, argc, argv,
5510 worktree);
5511 if (error)
5512 goto done;
5514 error = got_object_id_str(&id_str,
5515 got_worktree_get_base_commit_id(worktree));
5516 if (error)
5517 goto done;
5518 arg.repo = repo;
5519 arg.worktree = worktree;
5520 arg.diff_algo = GOT_DIFF_ALGORITHM_PATIENCE;
5521 arg.diff_context = diff_context;
5522 arg.id_str = id_str;
5523 arg.header_shown = 0;
5524 arg.diff_staged = diff_staged;
5525 arg.ignore_whitespace = ignore_whitespace;
5526 arg.force_text_diff = force_text_diff;
5527 arg.diffstat = show_diffstat ? &dsa : NULL;
5528 arg.f1 = f1;
5529 arg.f2 = f2;
5530 arg.outfile = outfile;
5532 error = got_worktree_status(worktree, &paths, repo, 0,
5533 print_diff, &arg, check_cancelled, NULL);
5534 free(id_str);
5535 if (error)
5536 goto done;
5538 if (show_diffstat && dsa.nfiles > 0) {
5539 char *header;
5541 if (asprintf(&header, "diffstat %s%s",
5542 diff_staged ? "-s " : "",
5543 got_worktree_get_root_path(worktree)) == -1) {
5544 error = got_error_from_errno("asprintf");
5545 goto done;
5548 error = print_diffstat(&dsa, header);
5549 free(header);
5550 if (error)
5551 goto done;
5554 error = printfile(outfile);
5555 goto done;
5558 if (ncommit_args == 1) {
5559 struct got_commit_object *commit;
5560 error = got_object_open_as_commit(&commit, repo, ids[0]);
5561 if (error)
5562 goto done;
5564 labels[1] = labels[0];
5565 ids[1] = ids[0];
5566 if (got_object_commit_get_nparents(commit) > 0) {
5567 const struct got_object_id_queue *pids;
5568 struct got_object_qid *pid;
5569 pids = got_object_commit_get_parent_ids(commit);
5570 pid = STAILQ_FIRST(pids);
5571 ids[0] = got_object_id_dup(&pid->id);
5572 if (ids[0] == NULL) {
5573 error = got_error_from_errno(
5574 "got_object_id_dup");
5575 got_object_commit_close(commit);
5576 goto done;
5578 error = got_object_id_str(&labels[0], ids[0]);
5579 if (error) {
5580 got_object_commit_close(commit);
5581 goto done;
5583 } else {
5584 ids[0] = NULL;
5585 labels[0] = strdup("/dev/null");
5586 if (labels[0] == NULL) {
5587 error = got_error_from_errno("strdup");
5588 got_object_commit_close(commit);
5589 goto done;
5593 got_object_commit_close(commit);
5596 if (ncommit_args == 0 && argc > 2) {
5597 error = got_error_msg(GOT_ERR_BAD_PATH,
5598 "path arguments cannot be used when diffing two objects");
5599 goto done;
5602 if (ids[0]) {
5603 error = got_object_get_type(&type1, repo, ids[0]);
5604 if (error)
5605 goto done;
5608 error = got_object_get_type(&type2, repo, ids[1]);
5609 if (error)
5610 goto done;
5611 if (type1 != GOT_OBJ_TYPE_ANY && type1 != type2) {
5612 error = got_error(GOT_ERR_OBJ_TYPE);
5613 goto done;
5615 if (type1 == GOT_OBJ_TYPE_BLOB && argc > 2) {
5616 error = got_error_msg(GOT_ERR_OBJ_TYPE,
5617 "path arguments cannot be used when diffing blobs");
5618 goto done;
5621 for (i = 0; ncommit_args > 0 && i < argc; i++) {
5622 char *in_repo_path;
5623 struct got_pathlist_entry *new;
5624 if (worktree) {
5625 const char *prefix;
5626 char *p;
5627 error = got_worktree_resolve_path(&p, worktree,
5628 argv[i]);
5629 if (error)
5630 goto done;
5631 prefix = got_worktree_get_path_prefix(worktree);
5632 while (prefix[0] == '/')
5633 prefix++;
5634 if (asprintf(&in_repo_path, "%s%s%s", prefix,
5635 (p[0] != '\0' && prefix[0] != '\0') ? "/" : "",
5636 p) == -1) {
5637 error = got_error_from_errno("asprintf");
5638 free(p);
5639 goto done;
5641 free(p);
5642 } else {
5643 char *mapped_path, *s;
5644 error = got_repo_map_path(&mapped_path, repo, argv[i]);
5645 if (error)
5646 goto done;
5647 s = mapped_path;
5648 while (s[0] == '/')
5649 s++;
5650 in_repo_path = strdup(s);
5651 if (in_repo_path == NULL) {
5652 error = got_error_from_errno("asprintf");
5653 free(mapped_path);
5654 goto done;
5656 free(mapped_path);
5659 error = got_pathlist_insert(&new, &paths, in_repo_path, NULL);
5660 if (error || new == NULL /* duplicate */)
5661 free(in_repo_path);
5662 if (error)
5663 goto done;
5666 if (worktree) {
5667 /* Release work tree lock. */
5668 got_worktree_close(worktree);
5669 worktree = NULL;
5672 fd1 = got_opentempfd();
5673 if (fd1 == -1) {
5674 error = got_error_from_errno("got_opentempfd");
5675 goto done;
5678 fd2 = got_opentempfd();
5679 if (fd2 == -1) {
5680 error = got_error_from_errno("got_opentempfd");
5681 goto done;
5684 switch (type1 == GOT_OBJ_TYPE_ANY ? type2 : type1) {
5685 case GOT_OBJ_TYPE_BLOB:
5686 error = got_diff_objects_as_blobs(NULL, NULL, f1, f2,
5687 fd1, fd2, ids[0], ids[1], NULL, NULL,
5688 GOT_DIFF_ALGORITHM_PATIENCE, diff_context,
5689 ignore_whitespace, force_text_diff,
5690 show_diffstat ? &dsa : NULL, repo, outfile);
5691 break;
5692 case GOT_OBJ_TYPE_TREE:
5693 error = got_diff_objects_as_trees(NULL, NULL, f1, f2, fd1, fd2,
5694 ids[0], ids[1], &paths, "", "",
5695 GOT_DIFF_ALGORITHM_PATIENCE, diff_context,
5696 ignore_whitespace, force_text_diff,
5697 show_diffstat ? &dsa : NULL, repo, outfile);
5698 break;
5699 case GOT_OBJ_TYPE_COMMIT:
5700 fprintf(outfile, "diff %s %s\n", labels[0], labels[1]);
5701 error = got_diff_objects_as_commits(NULL, NULL, f1, f2,
5702 fd1, fd2, ids[0], ids[1], &paths,
5703 GOT_DIFF_ALGORITHM_PATIENCE, diff_context,
5704 ignore_whitespace, force_text_diff,
5705 show_diffstat ? &dsa : NULL, repo, outfile);
5706 break;
5707 default:
5708 error = got_error(GOT_ERR_OBJ_TYPE);
5710 if (error)
5711 goto done;
5713 if (show_diffstat && dsa.nfiles > 0) {
5714 char *header = NULL;
5716 if (asprintf(&header, "diffstat %s %s",
5717 labels[0], labels[1]) == -1) {
5718 error = got_error_from_errno("asprintf");
5719 goto done;
5722 error = print_diffstat(&dsa, header);
5723 free(header);
5724 if (error)
5725 goto done;
5728 error = printfile(outfile);
5730 done:
5731 free(cwd);
5732 free(labels[0]);
5733 free(labels[1]);
5734 free(ids[0]);
5735 free(ids[1]);
5736 if (worktree)
5737 got_worktree_close(worktree);
5738 if (repo) {
5739 const struct got_error *close_err = got_repo_close(repo);
5740 if (error == NULL)
5741 error = close_err;
5743 if (pack_fds) {
5744 const struct got_error *pack_err =
5745 got_repo_pack_fds_close(pack_fds);
5746 if (error == NULL)
5747 error = pack_err;
5749 got_pathlist_free(&paths, GOT_PATHLIST_FREE_PATH);
5750 got_pathlist_free(&diffstat_paths, GOT_PATHLIST_FREE_ALL);
5751 got_ref_list_free(&refs);
5752 if (outfile && fclose(outfile) == EOF && error == NULL)
5753 error = got_error_from_errno("fclose");
5754 if (f1 && fclose(f1) == EOF && error == NULL)
5755 error = got_error_from_errno("fclose");
5756 if (f2 && fclose(f2) == EOF && error == NULL)
5757 error = got_error_from_errno("fclose");
5758 if (fd1 != -1 && close(fd1) == -1 && error == NULL)
5759 error = got_error_from_errno("close");
5760 if (fd2 != -1 && close(fd2) == -1 && error == NULL)
5761 error = got_error_from_errno("close");
5762 return error;
5765 __dead static void
5766 usage_blame(void)
5768 fprintf(stderr,
5769 "usage: %s blame [-c commit] [-r repository-path] path\n",
5770 getprogname());
5771 exit(1);
5774 struct blame_line {
5775 int annotated;
5776 char *id_str;
5777 char *committer;
5778 char datebuf[11]; /* YYYY-MM-DD + NUL */
5781 struct blame_cb_args {
5782 struct blame_line *lines;
5783 int nlines;
5784 int nlines_prec;
5785 int lineno_cur;
5786 off_t *line_offsets;
5787 FILE *f;
5788 struct got_repository *repo;
5791 static const struct got_error *
5792 blame_cb(void *arg, int nlines, int lineno,
5793 struct got_commit_object *commit, struct got_object_id *id)
5795 const struct got_error *err = NULL;
5796 struct blame_cb_args *a = arg;
5797 struct blame_line *bline;
5798 char *line = NULL;
5799 size_t linesize = 0;
5800 off_t offset;
5801 struct tm tm;
5802 time_t committer_time;
5804 if (nlines != a->nlines ||
5805 (lineno != -1 && lineno < 1) || lineno > a->nlines)
5806 return got_error(GOT_ERR_RANGE);
5808 if (sigint_received)
5809 return got_error(GOT_ERR_ITER_COMPLETED);
5811 if (lineno == -1)
5812 return NULL; /* no change in this commit */
5814 /* Annotate this line. */
5815 bline = &a->lines[lineno - 1];
5816 if (bline->annotated)
5817 return NULL;
5818 err = got_object_id_str(&bline->id_str, id);
5819 if (err)
5820 return err;
5822 bline->committer = strdup(got_object_commit_get_committer(commit));
5823 if (bline->committer == NULL) {
5824 err = got_error_from_errno("strdup");
5825 goto done;
5828 committer_time = got_object_commit_get_committer_time(commit);
5829 if (gmtime_r(&committer_time, &tm) == NULL)
5830 return got_error_from_errno("gmtime_r");
5831 if (strftime(bline->datebuf, sizeof(bline->datebuf), "%F", &tm) == 0) {
5832 err = got_error(GOT_ERR_NO_SPACE);
5833 goto done;
5835 bline->annotated = 1;
5837 /* Print lines annotated so far. */
5838 bline = &a->lines[a->lineno_cur - 1];
5839 if (!bline->annotated)
5840 goto done;
5842 offset = a->line_offsets[a->lineno_cur - 1];
5843 if (fseeko(a->f, offset, SEEK_SET) == -1) {
5844 err = got_error_from_errno("fseeko");
5845 goto done;
5848 while (a->lineno_cur <= a->nlines && bline->annotated) {
5849 char *smallerthan, *at, *nl, *committer;
5850 size_t len;
5852 if (getline(&line, &linesize, a->f) == -1) {
5853 if (ferror(a->f))
5854 err = got_error_from_errno("getline");
5855 break;
5858 committer = bline->committer;
5859 smallerthan = strchr(committer, '<');
5860 if (smallerthan && smallerthan[1] != '\0')
5861 committer = smallerthan + 1;
5862 at = strchr(committer, '@');
5863 if (at)
5864 *at = '\0';
5865 len = strlen(committer);
5866 if (len >= 9)
5867 committer[8] = '\0';
5869 nl = strchr(line, '\n');
5870 if (nl)
5871 *nl = '\0';
5872 printf("%.*d) %.8s %s %-8s %s\n", a->nlines_prec, a->lineno_cur,
5873 bline->id_str, bline->datebuf, committer, line);
5875 a->lineno_cur++;
5876 bline = &a->lines[a->lineno_cur - 1];
5878 done:
5879 free(line);
5880 return err;
5883 static const struct got_error *
5884 cmd_blame(int argc, char *argv[])
5886 const struct got_error *error;
5887 struct got_repository *repo = NULL;
5888 struct got_worktree *worktree = NULL;
5889 char *path, *cwd = NULL, *repo_path = NULL, *in_repo_path = NULL;
5890 char *link_target = NULL;
5891 struct got_object_id *obj_id = NULL;
5892 struct got_object_id *commit_id = NULL;
5893 struct got_commit_object *commit = NULL;
5894 struct got_blob_object *blob = NULL;
5895 char *commit_id_str = NULL, *keyword_idstr = NULL;
5896 struct blame_cb_args bca;
5897 int ch, obj_type, i, fd1 = -1, fd2 = -1, fd3 = -1;
5898 off_t filesize;
5899 int *pack_fds = NULL;
5900 FILE *f1 = NULL, *f2 = NULL;
5902 fd1 = got_opentempfd();
5903 if (fd1 == -1)
5904 return got_error_from_errno("got_opentempfd");
5906 memset(&bca, 0, sizeof(bca));
5908 #ifndef PROFILE
5909 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
5910 NULL) == -1)
5911 err(1, "pledge");
5912 #endif
5914 while ((ch = getopt(argc, argv, "c:r:")) != -1) {
5915 switch (ch) {
5916 case 'c':
5917 commit_id_str = optarg;
5918 break;
5919 case 'r':
5920 repo_path = realpath(optarg, NULL);
5921 if (repo_path == NULL)
5922 return got_error_from_errno2("realpath",
5923 optarg);
5924 got_path_strip_trailing_slashes(repo_path);
5925 break;
5926 default:
5927 usage_blame();
5928 /* NOTREACHED */
5932 argc -= optind;
5933 argv += optind;
5935 if (argc == 1)
5936 path = argv[0];
5937 else
5938 usage_blame();
5940 cwd = getcwd(NULL, 0);
5941 if (cwd == NULL) {
5942 error = got_error_from_errno("getcwd");
5943 goto done;
5946 error = got_repo_pack_fds_open(&pack_fds);
5947 if (error != NULL)
5948 goto done;
5950 if (repo_path == NULL) {
5951 error = got_worktree_open(&worktree, cwd,
5952 GOT_WORKTREE_GOT_DIR);
5953 if (error && error->code != GOT_ERR_NOT_WORKTREE)
5954 goto done;
5955 else
5956 error = NULL;
5957 if (worktree) {
5958 repo_path =
5959 strdup(got_worktree_get_repo_path(worktree));
5960 if (repo_path == NULL) {
5961 error = got_error_from_errno("strdup");
5962 if (error)
5963 goto done;
5965 } else {
5966 repo_path = strdup(cwd);
5967 if (repo_path == NULL) {
5968 error = got_error_from_errno("strdup");
5969 goto done;
5974 error = got_repo_open(&repo, repo_path, NULL, pack_fds);
5975 if (error != NULL)
5976 goto done;
5978 if (worktree) {
5979 const char *prefix = got_worktree_get_path_prefix(worktree);
5980 char *p;
5982 error = got_worktree_resolve_path(&p, worktree, path);
5983 if (error)
5984 goto done;
5985 if (asprintf(&in_repo_path, "%s%s%s", prefix,
5986 (p[0] != '\0' && !got_path_is_root_dir(prefix)) ? "/" : "",
5987 p) == -1) {
5988 error = got_error_from_errno("asprintf");
5989 free(p);
5990 goto done;
5992 free(p);
5993 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
5994 } else {
5995 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
5996 if (error)
5997 goto done;
5998 error = got_repo_map_path(&in_repo_path, repo, path);
6000 if (error)
6001 goto done;
6003 if (commit_id_str == NULL) {
6004 struct got_reference *head_ref;
6005 error = got_ref_open(&head_ref, repo, worktree ?
6006 got_worktree_get_head_ref_name(worktree) : GOT_REF_HEAD, 0);
6007 if (error != NULL)
6008 goto done;
6009 error = got_ref_resolve(&commit_id, repo, head_ref);
6010 got_ref_close(head_ref);
6011 if (error != NULL)
6012 goto done;
6013 } else {
6014 struct got_reflist_head refs;
6016 TAILQ_INIT(&refs);
6017 error = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name,
6018 NULL);
6019 if (error)
6020 goto done;
6022 error = got_keyword_to_idstr(&keyword_idstr, commit_id_str,
6023 repo, worktree);
6024 if (error != NULL)
6025 goto done;
6026 if (keyword_idstr != NULL)
6027 commit_id_str = keyword_idstr;
6029 error = got_repo_match_object_id(&commit_id, NULL,
6030 commit_id_str, GOT_OBJ_TYPE_COMMIT, &refs, repo);
6031 got_ref_list_free(&refs);
6032 if (error)
6033 goto done;
6036 if (worktree) {
6037 /* Release work tree lock. */
6038 got_worktree_close(worktree);
6039 worktree = NULL;
6042 error = got_object_open_as_commit(&commit, repo, commit_id);
6043 if (error)
6044 goto done;
6046 error = got_object_resolve_symlinks(&link_target, in_repo_path,
6047 commit, repo);
6048 if (error)
6049 goto done;
6051 error = got_object_id_by_path(&obj_id, repo, commit,
6052 link_target ? link_target : in_repo_path);
6053 if (error)
6054 goto done;
6056 error = got_object_get_type(&obj_type, repo, obj_id);
6057 if (error)
6058 goto done;
6060 if (obj_type != GOT_OBJ_TYPE_BLOB) {
6061 error = got_error_path(link_target ? link_target : in_repo_path,
6062 GOT_ERR_OBJ_TYPE);
6063 goto done;
6066 error = got_object_open_as_blob(&blob, repo, obj_id, 8192, fd1);
6067 if (error)
6068 goto done;
6069 bca.f = got_opentemp();
6070 if (bca.f == NULL) {
6071 error = got_error_from_errno("got_opentemp");
6072 goto done;
6074 error = got_object_blob_dump_to_file(&filesize, &bca.nlines,
6075 &bca.line_offsets, bca.f, blob);
6076 if (error || bca.nlines == 0)
6077 goto done;
6079 /* Don't include \n at EOF in the blame line count. */
6080 if (bca.line_offsets[bca.nlines - 1] == filesize)
6081 bca.nlines--;
6083 bca.lines = calloc(bca.nlines, sizeof(*bca.lines));
6084 if (bca.lines == NULL) {
6085 error = got_error_from_errno("calloc");
6086 goto done;
6088 bca.lineno_cur = 1;
6089 bca.nlines_prec = 0;
6090 i = bca.nlines;
6091 while (i > 0) {
6092 i /= 10;
6093 bca.nlines_prec++;
6095 bca.repo = repo;
6097 fd2 = got_opentempfd();
6098 if (fd2 == -1) {
6099 error = got_error_from_errno("got_opentempfd");
6100 goto done;
6102 fd3 = got_opentempfd();
6103 if (fd3 == -1) {
6104 error = got_error_from_errno("got_opentempfd");
6105 goto done;
6107 f1 = got_opentemp();
6108 if (f1 == NULL) {
6109 error = got_error_from_errno("got_opentemp");
6110 goto done;
6112 f2 = got_opentemp();
6113 if (f2 == NULL) {
6114 error = got_error_from_errno("got_opentemp");
6115 goto done;
6117 error = got_blame(link_target ? link_target : in_repo_path, commit_id,
6118 repo, GOT_DIFF_ALGORITHM_PATIENCE, blame_cb, &bca,
6119 check_cancelled, NULL, fd2, fd3, f1, f2);
6120 done:
6121 free(keyword_idstr);
6122 free(in_repo_path);
6123 free(link_target);
6124 free(repo_path);
6125 free(cwd);
6126 free(commit_id);
6127 free(obj_id);
6128 if (commit)
6129 got_object_commit_close(commit);
6131 if (fd1 != -1 && close(fd1) == -1 && error == NULL)
6132 error = got_error_from_errno("close");
6133 if (fd2 != -1 && close(fd2) == -1 && error == NULL)
6134 error = got_error_from_errno("close");
6135 if (fd3 != -1 && close(fd3) == -1 && error == NULL)
6136 error = got_error_from_errno("close");
6137 if (f1 && fclose(f1) == EOF && error == NULL)
6138 error = got_error_from_errno("fclose");
6139 if (f2 && fclose(f2) == EOF && error == NULL)
6140 error = got_error_from_errno("fclose");
6142 if (blob)
6143 got_object_blob_close(blob);
6144 if (worktree)
6145 got_worktree_close(worktree);
6146 if (repo) {
6147 const struct got_error *close_err = got_repo_close(repo);
6148 if (error == NULL)
6149 error = close_err;
6151 if (pack_fds) {
6152 const struct got_error *pack_err =
6153 got_repo_pack_fds_close(pack_fds);
6154 if (error == NULL)
6155 error = pack_err;
6157 if (bca.lines) {
6158 for (i = 0; i < bca.nlines; i++) {
6159 struct blame_line *bline = &bca.lines[i];
6160 free(bline->id_str);
6161 free(bline->committer);
6163 free(bca.lines);
6165 free(bca.line_offsets);
6166 if (bca.f && fclose(bca.f) == EOF && error == NULL)
6167 error = got_error_from_errno("fclose");
6168 return error;
6171 __dead static void
6172 usage_tree(void)
6174 fprintf(stderr, "usage: %s tree [-iR] [-c commit] [-r repository-path] "
6175 "[path]\n", getprogname());
6176 exit(1);
6179 static const struct got_error *
6180 print_entry(struct got_tree_entry *te, const char *id, const char *path,
6181 const char *root_path, struct got_repository *repo)
6183 const struct got_error *err = NULL;
6184 int is_root_path = (strcmp(path, root_path) == 0);
6185 const char *modestr = "";
6186 mode_t mode = got_tree_entry_get_mode(te);
6187 char *link_target = NULL;
6189 path += strlen(root_path);
6190 while (path[0] == '/')
6191 path++;
6193 if (got_object_tree_entry_is_submodule(te))
6194 modestr = "$";
6195 else if (S_ISLNK(mode)) {
6196 int i;
6198 err = got_tree_entry_get_symlink_target(&link_target, te, repo);
6199 if (err)
6200 return err;
6201 for (i = 0; link_target[i] != '\0'; i++) {
6202 if (!isprint((unsigned char)link_target[i]))
6203 link_target[i] = '?';
6206 modestr = "@";
6208 else if (S_ISDIR(mode))
6209 modestr = "/";
6210 else if (mode & S_IXUSR)
6211 modestr = "*";
6213 printf("%s%s%s%s%s%s%s\n", id ? id : "", path,
6214 is_root_path ? "" : "/", got_tree_entry_get_name(te), modestr,
6215 link_target ? " -> ": "", link_target ? link_target : "");
6217 free(link_target);
6218 return NULL;
6221 static const struct got_error *
6222 print_tree(const char *path, struct got_commit_object *commit,
6223 int show_ids, int recurse, const char *root_path,
6224 struct got_repository *repo)
6226 const struct got_error *err = NULL;
6227 struct got_object_id *tree_id = NULL;
6228 struct got_tree_object *tree = NULL;
6229 int nentries, i;
6231 err = got_object_id_by_path(&tree_id, repo, commit, path);
6232 if (err)
6233 goto done;
6235 err = got_object_open_as_tree(&tree, repo, tree_id);
6236 if (err)
6237 goto done;
6238 nentries = got_object_tree_get_nentries(tree);
6239 for (i = 0; i < nentries; i++) {
6240 struct got_tree_entry *te;
6241 char *id = NULL;
6243 if (sigint_received || sigpipe_received)
6244 break;
6246 te = got_object_tree_get_entry(tree, i);
6247 if (show_ids) {
6248 char *id_str;
6249 err = got_object_id_str(&id_str,
6250 got_tree_entry_get_id(te));
6251 if (err)
6252 goto done;
6253 if (asprintf(&id, "%s ", id_str) == -1) {
6254 err = got_error_from_errno("asprintf");
6255 free(id_str);
6256 goto done;
6258 free(id_str);
6260 err = print_entry(te, id, path, root_path, repo);
6261 free(id);
6262 if (err)
6263 goto done;
6265 if (recurse && S_ISDIR(got_tree_entry_get_mode(te))) {
6266 char *child_path;
6267 if (asprintf(&child_path, "%s%s%s", path,
6268 path[0] == '/' && path[1] == '\0' ? "" : "/",
6269 got_tree_entry_get_name(te)) == -1) {
6270 err = got_error_from_errno("asprintf");
6271 goto done;
6273 err = print_tree(child_path, commit, show_ids, 1,
6274 root_path, repo);
6275 free(child_path);
6276 if (err)
6277 goto done;
6280 done:
6281 if (tree)
6282 got_object_tree_close(tree);
6283 free(tree_id);
6284 return err;
6287 static const struct got_error *
6288 cmd_tree(int argc, char *argv[])
6290 const struct got_error *error;
6291 struct got_repository *repo = NULL;
6292 struct got_worktree *worktree = NULL;
6293 const char *path, *refname = NULL;
6294 char *cwd = NULL, *repo_path = NULL, *in_repo_path = NULL;
6295 struct got_object_id *commit_id = NULL;
6296 struct got_commit_object *commit = NULL;
6297 char *commit_id_str = NULL, *keyword_idstr = NULL;
6298 int show_ids = 0, recurse = 0;
6299 int ch;
6300 int *pack_fds = NULL;
6302 #ifndef PROFILE
6303 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
6304 NULL) == -1)
6305 err(1, "pledge");
6306 #endif
6308 while ((ch = getopt(argc, argv, "c:iRr:")) != -1) {
6309 switch (ch) {
6310 case 'c':
6311 commit_id_str = optarg;
6312 break;
6313 case 'i':
6314 show_ids = 1;
6315 break;
6316 case 'R':
6317 recurse = 1;
6318 break;
6319 case 'r':
6320 repo_path = realpath(optarg, NULL);
6321 if (repo_path == NULL)
6322 return got_error_from_errno2("realpath",
6323 optarg);
6324 got_path_strip_trailing_slashes(repo_path);
6325 break;
6326 default:
6327 usage_tree();
6328 /* NOTREACHED */
6332 argc -= optind;
6333 argv += optind;
6335 if (argc == 1)
6336 path = argv[0];
6337 else if (argc > 1)
6338 usage_tree();
6339 else
6340 path = NULL;
6342 cwd = getcwd(NULL, 0);
6343 if (cwd == NULL) {
6344 error = got_error_from_errno("getcwd");
6345 goto done;
6348 error = got_repo_pack_fds_open(&pack_fds);
6349 if (error != NULL)
6350 goto done;
6352 if (repo_path == NULL) {
6353 error = got_worktree_open(&worktree, cwd,
6354 GOT_WORKTREE_GOT_DIR);
6355 if (error && error->code != GOT_ERR_NOT_WORKTREE)
6356 goto done;
6357 else
6358 error = NULL;
6359 if (worktree) {
6360 repo_path =
6361 strdup(got_worktree_get_repo_path(worktree));
6362 if (repo_path == NULL)
6363 error = got_error_from_errno("strdup");
6364 if (error)
6365 goto done;
6366 } else {
6367 repo_path = strdup(cwd);
6368 if (repo_path == NULL) {
6369 error = got_error_from_errno("strdup");
6370 goto done;
6375 error = got_repo_open(&repo, repo_path, NULL, pack_fds);
6376 if (error != NULL)
6377 goto done;
6379 if (worktree) {
6380 const char *prefix = got_worktree_get_path_prefix(worktree);
6381 char *p;
6383 if (path == NULL || got_path_is_root_dir(path))
6384 path = "";
6385 error = got_worktree_resolve_path(&p, worktree, path);
6386 if (error)
6387 goto done;
6388 if (asprintf(&in_repo_path, "%s%s%s", prefix,
6389 (p[0] != '\0' && !got_path_is_root_dir(prefix)) ? "/" : "",
6390 p) == -1) {
6391 error = got_error_from_errno("asprintf");
6392 free(p);
6393 goto done;
6395 free(p);
6396 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
6397 if (error)
6398 goto done;
6399 } else {
6400 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
6401 if (error)
6402 goto done;
6403 if (path == NULL)
6404 path = "/";
6405 error = got_repo_map_path(&in_repo_path, repo, path);
6406 if (error != NULL)
6407 goto done;
6410 if (commit_id_str == NULL) {
6411 struct got_reference *head_ref;
6412 if (worktree)
6413 refname = got_worktree_get_head_ref_name(worktree);
6414 else
6415 refname = GOT_REF_HEAD;
6416 error = got_ref_open(&head_ref, repo, refname, 0);
6417 if (error != NULL)
6418 goto done;
6419 error = got_ref_resolve(&commit_id, repo, head_ref);
6420 got_ref_close(head_ref);
6421 if (error != NULL)
6422 goto done;
6423 } else {
6424 struct got_reflist_head refs;
6426 TAILQ_INIT(&refs);
6427 error = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name,
6428 NULL);
6429 if (error)
6430 goto done;
6432 error = got_keyword_to_idstr(&keyword_idstr, commit_id_str,
6433 repo, worktree);
6434 if (error != NULL)
6435 goto done;
6436 if (keyword_idstr != NULL)
6437 commit_id_str = keyword_idstr;
6439 error = got_repo_match_object_id(&commit_id, NULL,
6440 commit_id_str, GOT_OBJ_TYPE_COMMIT, &refs, repo);
6441 got_ref_list_free(&refs);
6442 if (error)
6443 goto done;
6446 if (worktree) {
6447 /* Release work tree lock. */
6448 got_worktree_close(worktree);
6449 worktree = NULL;
6452 error = got_object_open_as_commit(&commit, repo, commit_id);
6453 if (error)
6454 goto done;
6456 error = print_tree(in_repo_path, commit, show_ids, recurse,
6457 in_repo_path, repo);
6458 done:
6459 free(keyword_idstr);
6460 free(in_repo_path);
6461 free(repo_path);
6462 free(cwd);
6463 free(commit_id);
6464 if (commit)
6465 got_object_commit_close(commit);
6466 if (worktree)
6467 got_worktree_close(worktree);
6468 if (repo) {
6469 const struct got_error *close_err = got_repo_close(repo);
6470 if (error == NULL)
6471 error = close_err;
6473 if (pack_fds) {
6474 const struct got_error *pack_err =
6475 got_repo_pack_fds_close(pack_fds);
6476 if (error == NULL)
6477 error = pack_err;
6479 return error;
6482 __dead static void
6483 usage_status(void)
6485 fprintf(stderr, "usage: %s status [-I] [-S status-codes] "
6486 "[-s status-codes] [path ...]\n", getprogname());
6487 exit(1);
6490 struct got_status_arg {
6491 char *status_codes;
6492 int suppress;
6495 static const struct got_error *
6496 print_status(void *arg, unsigned char status, unsigned char staged_status,
6497 const char *path, struct got_object_id *blob_id,
6498 struct got_object_id *staged_blob_id, struct got_object_id *commit_id,
6499 int dirfd, const char *de_name)
6501 struct got_status_arg *st = arg;
6503 if (status == staged_status && (status == GOT_STATUS_DELETE))
6504 status = GOT_STATUS_NO_CHANGE;
6505 if (st != NULL && st->status_codes) {
6506 size_t ncodes = strlen(st->status_codes);
6507 int i, j = 0;
6509 for (i = 0; i < ncodes ; i++) {
6510 if (st->suppress) {
6511 if (status == st->status_codes[i] ||
6512 staged_status == st->status_codes[i]) {
6513 j++;
6514 continue;
6516 } else {
6517 if (status == st->status_codes[i] ||
6518 staged_status == st->status_codes[i])
6519 break;
6523 if (st->suppress && j == 0)
6524 goto print;
6526 if (i == ncodes)
6527 return NULL;
6529 print:
6530 printf("%c%c %s\n", status, staged_status, path);
6531 return NULL;
6534 static const struct got_error *
6535 show_operation_in_progress(struct got_worktree *worktree,
6536 struct got_repository *repo)
6538 const struct got_error *err;
6539 char *new_base_branch_name = NULL;
6540 char *branch_name = NULL;
6541 int rebase_in_progress, histedit_in_progress, merge_in_progress;
6543 err = got_worktree_rebase_in_progress(&rebase_in_progress, worktree);
6544 if (err)
6545 return err;
6546 if (rebase_in_progress) {
6547 err = got_worktree_rebase_info(&new_base_branch_name,
6548 &branch_name, worktree, repo);
6549 if (err)
6550 return err;
6551 printf("Work tree is rebasing %s onto %s\n",
6552 branch_name, new_base_branch_name);
6555 err = got_worktree_histedit_in_progress(&histedit_in_progress,
6556 worktree);
6557 if (err)
6558 return err;
6559 if (histedit_in_progress) {
6560 err = got_worktree_histedit_info(&branch_name, worktree, repo);
6561 if (err)
6562 return err;
6563 printf("Work tree is editing the history of %s\n", branch_name);
6566 err = got_worktree_merge_in_progress(&merge_in_progress,
6567 worktree, repo);
6568 if (err)
6569 return err;
6570 if (merge_in_progress) {
6571 err = got_worktree_merge_info(&branch_name, worktree,
6572 repo);
6573 if (err)
6574 return err;
6575 printf("Work tree is merging %s into %s\n", branch_name,
6576 got_worktree_get_head_ref_name(worktree));
6579 free(new_base_branch_name);
6580 free(branch_name);
6581 return NULL;
6584 static const struct got_error *
6585 cmd_status(int argc, char *argv[])
6587 const struct got_error *close_err, *error = NULL;
6588 struct got_repository *repo = NULL;
6589 struct got_worktree *worktree = NULL;
6590 struct got_status_arg st;
6591 char *cwd = NULL;
6592 struct got_pathlist_head paths;
6593 int ch, i, no_ignores = 0;
6594 int *pack_fds = NULL;
6596 TAILQ_INIT(&paths);
6598 memset(&st, 0, sizeof(st));
6599 st.status_codes = NULL;
6600 st.suppress = 0;
6602 #ifndef PROFILE
6603 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
6604 NULL) == -1)
6605 err(1, "pledge");
6606 #endif
6608 while ((ch = getopt(argc, argv, "IS:s:")) != -1) {
6609 switch (ch) {
6610 case 'I':
6611 no_ignores = 1;
6612 break;
6613 case 'S':
6614 if (st.status_codes != NULL && st.suppress == 0)
6615 option_conflict('S', 's');
6616 st.suppress = 1;
6617 /* fallthrough */
6618 case 's':
6619 for (i = 0; optarg[i] != '\0'; i++) {
6620 switch (optarg[i]) {
6621 case GOT_STATUS_MODIFY:
6622 case GOT_STATUS_ADD:
6623 case GOT_STATUS_DELETE:
6624 case GOT_STATUS_CONFLICT:
6625 case GOT_STATUS_MISSING:
6626 case GOT_STATUS_OBSTRUCTED:
6627 case GOT_STATUS_UNVERSIONED:
6628 case GOT_STATUS_MODE_CHANGE:
6629 case GOT_STATUS_NONEXISTENT:
6630 break;
6631 default:
6632 errx(1, "invalid status code '%c'",
6633 optarg[i]);
6636 if (ch == 's' && st.suppress)
6637 option_conflict('s', 'S');
6638 st.status_codes = optarg;
6639 break;
6640 default:
6641 usage_status();
6642 /* NOTREACHED */
6646 argc -= optind;
6647 argv += optind;
6649 cwd = getcwd(NULL, 0);
6650 if (cwd == NULL) {
6651 error = got_error_from_errno("getcwd");
6652 goto done;
6655 error = got_repo_pack_fds_open(&pack_fds);
6656 if (error != NULL)
6657 goto done;
6659 error = got_worktree_open(&worktree, cwd,
6660 GOT_WORKTREE_GOT_DIR);
6661 if (error) {
6662 if (error->code == GOT_ERR_NOT_WORKTREE)
6663 error = wrap_not_worktree_error(error, "status", cwd);
6664 goto done;
6667 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
6668 NULL, pack_fds);
6669 if (error != NULL)
6670 goto done;
6672 error = apply_unveil(got_repo_get_path(repo), 1,
6673 got_worktree_get_root_path(worktree));
6674 if (error)
6675 goto done;
6677 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
6678 if (error)
6679 goto done;
6681 error = got_worktree_status(worktree, &paths, repo, no_ignores,
6682 print_status, &st, check_cancelled, NULL);
6683 if (error)
6684 goto done;
6686 error = show_operation_in_progress(worktree, repo);
6687 done:
6688 if (pack_fds) {
6689 const struct got_error *pack_err =
6690 got_repo_pack_fds_close(pack_fds);
6691 if (error == NULL)
6692 error = pack_err;
6694 if (repo) {
6695 close_err = got_repo_close(repo);
6696 if (error == NULL)
6697 error = close_err;
6699 if (worktree != NULL) {
6700 close_err = got_worktree_close(worktree);
6701 if (error == NULL)
6702 error = close_err;
6705 got_pathlist_free(&paths, GOT_PATHLIST_FREE_PATH);
6706 free(cwd);
6707 return error;
6710 __dead static void
6711 usage_ref(void)
6713 fprintf(stderr, "usage: %s ref [-dlt] [-c object] [-r repository-path] "
6714 "[-s reference] [name]\n", getprogname());
6715 exit(1);
6718 static const struct got_error *
6719 list_refs(struct got_repository *repo, const char *refname, int sort_by_time)
6721 static const struct got_error *err = NULL;
6722 struct got_reflist_head refs;
6723 struct got_reflist_entry *re;
6725 TAILQ_INIT(&refs);
6726 err = got_ref_list(&refs, repo, refname, sort_by_time ?
6727 got_ref_cmp_by_commit_timestamp_descending : got_ref_cmp_by_name,
6728 repo);
6729 if (err)
6730 return err;
6732 TAILQ_FOREACH(re, &refs, entry) {
6733 char *refstr;
6734 refstr = got_ref_to_str(re->ref);
6735 if (refstr == NULL) {
6736 err = got_error_from_errno("got_ref_to_str");
6737 break;
6739 printf("%s: %s\n", got_ref_get_name(re->ref), refstr);
6740 free(refstr);
6743 got_ref_list_free(&refs);
6744 return err;
6747 static const struct got_error *
6748 delete_ref_by_name(struct got_repository *repo, const char *refname)
6750 const struct got_error *err;
6751 struct got_reference *ref;
6753 err = got_ref_open(&ref, repo, refname, 0);
6754 if (err)
6755 return err;
6757 err = delete_ref(repo, ref);
6758 got_ref_close(ref);
6759 return err;
6762 static const struct got_error *
6763 add_ref(struct got_repository *repo, const char *refname, const char *target)
6765 const struct got_error *err = NULL;
6766 struct got_object_id *id = NULL;
6767 struct got_reference *ref = NULL;
6768 struct got_reflist_head refs;
6771 * Don't let the user create a reference name with a leading '-'.
6772 * While technically a valid reference name, this case is usually
6773 * an unintended typo.
6775 if (refname[0] == '-')
6776 return got_error_path(refname, GOT_ERR_REF_NAME_MINUS);
6778 TAILQ_INIT(&refs);
6779 err = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name, NULL);
6780 if (err)
6781 goto done;
6782 err = got_repo_match_object_id(&id, NULL, target, GOT_OBJ_TYPE_ANY,
6783 &refs, repo);
6784 got_ref_list_free(&refs);
6785 if (err)
6786 goto done;
6788 err = got_ref_alloc(&ref, refname, id);
6789 if (err)
6790 goto done;
6792 err = got_ref_write(ref, repo);
6793 done:
6794 if (ref)
6795 got_ref_close(ref);
6796 free(id);
6797 return err;
6800 static const struct got_error *
6801 add_symref(struct got_repository *repo, const char *refname, const char *target)
6803 const struct got_error *err = NULL;
6804 struct got_reference *ref = NULL;
6805 struct got_reference *target_ref = NULL;
6808 * Don't let the user create a reference name with a leading '-'.
6809 * While technically a valid reference name, this case is usually
6810 * an unintended typo.
6812 if (refname[0] == '-')
6813 return got_error_path(refname, GOT_ERR_REF_NAME_MINUS);
6815 err = got_ref_open(&target_ref, repo, target, 0);
6816 if (err)
6817 return err;
6819 err = got_ref_alloc_symref(&ref, refname, target_ref);
6820 if (err)
6821 goto done;
6823 err = got_ref_write(ref, repo);
6824 done:
6825 if (target_ref)
6826 got_ref_close(target_ref);
6827 if (ref)
6828 got_ref_close(ref);
6829 return err;
6832 static const struct got_error *
6833 cmd_ref(int argc, char *argv[])
6835 const struct got_error *error = NULL;
6836 struct got_repository *repo = NULL;
6837 struct got_worktree *worktree = NULL;
6838 char *cwd = NULL, *repo_path = NULL;
6839 int ch, do_list = 0, do_delete = 0, sort_by_time = 0;
6840 const char *obj_arg = NULL, *symref_target= NULL;
6841 char *refname = NULL, *keyword_idstr = NULL;
6842 int *pack_fds = NULL;
6844 #ifndef PROFILE
6845 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
6846 "sendfd unveil", NULL) == -1)
6847 err(1, "pledge");
6848 #endif
6850 while ((ch = getopt(argc, argv, "c:dlr:s:t")) != -1) {
6851 switch (ch) {
6852 case 'c':
6853 obj_arg = optarg;
6854 break;
6855 case 'd':
6856 do_delete = 1;
6857 break;
6858 case 'l':
6859 do_list = 1;
6860 break;
6861 case 'r':
6862 repo_path = realpath(optarg, NULL);
6863 if (repo_path == NULL)
6864 return got_error_from_errno2("realpath",
6865 optarg);
6866 got_path_strip_trailing_slashes(repo_path);
6867 break;
6868 case 's':
6869 symref_target = optarg;
6870 break;
6871 case 't':
6872 sort_by_time = 1;
6873 break;
6874 default:
6875 usage_ref();
6876 /* NOTREACHED */
6880 if (obj_arg && do_list)
6881 option_conflict('c', 'l');
6882 if (obj_arg && do_delete)
6883 option_conflict('c', 'd');
6884 if (obj_arg && symref_target)
6885 option_conflict('c', 's');
6886 if (symref_target && do_delete)
6887 option_conflict('s', 'd');
6888 if (symref_target && do_list)
6889 option_conflict('s', 'l');
6890 if (do_delete && do_list)
6891 option_conflict('d', 'l');
6892 if (sort_by_time && !do_list)
6893 errx(1, "-t option requires -l option");
6895 argc -= optind;
6896 argv += optind;
6898 if (do_list) {
6899 if (argc != 0 && argc != 1)
6900 usage_ref();
6901 if (argc == 1) {
6902 refname = strdup(argv[0]);
6903 if (refname == NULL) {
6904 error = got_error_from_errno("strdup");
6905 goto done;
6908 } else {
6909 if (argc != 1)
6910 usage_ref();
6911 refname = strdup(argv[0]);
6912 if (refname == NULL) {
6913 error = got_error_from_errno("strdup");
6914 goto done;
6918 if (refname)
6919 got_path_strip_trailing_slashes(refname);
6921 cwd = getcwd(NULL, 0);
6922 if (cwd == NULL) {
6923 error = got_error_from_errno("getcwd");
6924 goto done;
6927 error = got_repo_pack_fds_open(&pack_fds);
6928 if (error != NULL)
6929 goto done;
6931 if (repo_path == NULL) {
6932 error = got_worktree_open(&worktree, cwd,
6933 GOT_WORKTREE_GOT_DIR);
6934 if (error && error->code != GOT_ERR_NOT_WORKTREE)
6935 goto done;
6936 else
6937 error = NULL;
6938 if (worktree) {
6939 repo_path =
6940 strdup(got_worktree_get_repo_path(worktree));
6941 if (repo_path == NULL)
6942 error = got_error_from_errno("strdup");
6943 if (error)
6944 goto done;
6945 } else {
6946 repo_path = strdup(cwd);
6947 if (repo_path == NULL) {
6948 error = got_error_from_errno("strdup");
6949 goto done;
6954 error = got_repo_open(&repo, repo_path, NULL, pack_fds);
6955 if (error != NULL)
6956 goto done;
6958 #ifndef PROFILE
6959 if (do_list) {
6960 /* Remove "cpath" promise. */
6961 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
6962 NULL) == -1)
6963 err(1, "pledge");
6965 #endif
6967 error = apply_unveil(got_repo_get_path(repo), do_list,
6968 worktree ? got_worktree_get_root_path(worktree) : NULL);
6969 if (error)
6970 goto done;
6972 if (do_list)
6973 error = list_refs(repo, refname, sort_by_time);
6974 else if (do_delete)
6975 error = delete_ref_by_name(repo, refname);
6976 else if (symref_target)
6977 error = add_symref(repo, refname, symref_target);
6978 else {
6979 if (obj_arg == NULL)
6980 usage_ref();
6982 error = got_keyword_to_idstr(&keyword_idstr, obj_arg,
6983 repo, worktree);
6984 if (error != NULL)
6985 goto done;
6986 if (keyword_idstr != NULL)
6987 obj_arg = keyword_idstr;
6989 error = add_ref(repo, refname, obj_arg);
6991 done:
6992 free(refname);
6993 if (repo) {
6994 const struct got_error *close_err = got_repo_close(repo);
6995 if (error == NULL)
6996 error = close_err;
6998 if (worktree)
6999 got_worktree_close(worktree);
7000 if (pack_fds) {
7001 const struct got_error *pack_err =
7002 got_repo_pack_fds_close(pack_fds);
7003 if (error == NULL)
7004 error = pack_err;
7006 free(cwd);
7007 free(repo_path);
7008 free(keyword_idstr);
7009 return error;
7012 __dead static void
7013 usage_branch(void)
7015 fprintf(stderr, "usage: %s branch [-lnt] [-c commit] [-d name] "
7016 "[-r repository-path] [name]\n", getprogname());
7017 exit(1);
7020 static const struct got_error *
7021 list_branch(struct got_repository *repo, struct got_worktree *worktree,
7022 struct got_reference *ref)
7024 const struct got_error *err = NULL;
7025 const char *refname;
7026 char *refstr;
7027 char marker = ' ';
7029 refname = got_ref_get_name(ref);
7030 if (worktree && strcmp(refname,
7031 got_worktree_get_head_ref_name(worktree)) == 0) {
7032 err = got_worktree_get_state(&marker, repo, worktree,
7033 check_cancelled, NULL);
7034 if (err != NULL)
7035 return err;
7038 if (strncmp(refname, "refs/heads/", 11) == 0)
7039 refname += 11;
7040 if (strncmp(refname, "refs/got/worktree/", 18) == 0)
7041 refname += 18;
7042 if (strncmp(refname, "refs/remotes/", 13) == 0)
7043 refname += 13;
7045 refstr = got_ref_to_str(ref);
7046 if (refstr == NULL)
7047 return got_error_from_errno("got_ref_to_str");
7049 printf("%c %s: %s\n", marker, refname, refstr);
7050 free(refstr);
7051 return NULL;
7054 static const struct got_error *
7055 show_current_branch(struct got_repository *repo, struct got_worktree *worktree)
7057 const char *refname;
7059 if (worktree == NULL)
7060 return got_error(GOT_ERR_NOT_WORKTREE);
7062 refname = got_worktree_get_head_ref_name(worktree);
7064 if (strncmp(refname, "refs/heads/", 11) == 0)
7065 refname += 11;
7066 if (strncmp(refname, "refs/got/worktree/", 18) == 0)
7067 refname += 18;
7069 printf("%s\n", refname);
7071 return NULL;
7074 static const struct got_error *
7075 list_branches(struct got_repository *repo, struct got_worktree *worktree,
7076 int sort_by_time)
7078 static const struct got_error *err = NULL;
7079 struct got_reflist_head refs;
7080 struct got_reflist_entry *re;
7081 struct got_reference *temp_ref = NULL;
7082 int rebase_in_progress, histedit_in_progress;
7084 TAILQ_INIT(&refs);
7086 if (worktree) {
7087 err = got_worktree_rebase_in_progress(&rebase_in_progress,
7088 worktree);
7089 if (err)
7090 return err;
7092 err = got_worktree_histedit_in_progress(&histedit_in_progress,
7093 worktree);
7094 if (err)
7095 return err;
7097 if (rebase_in_progress || histedit_in_progress) {
7098 err = got_ref_open(&temp_ref, repo,
7099 got_worktree_get_head_ref_name(worktree), 0);
7100 if (err)
7101 return err;
7102 list_branch(repo, worktree, temp_ref);
7103 got_ref_close(temp_ref);
7107 err = got_ref_list(&refs, repo, "refs/heads", sort_by_time ?
7108 got_ref_cmp_by_commit_timestamp_descending : got_ref_cmp_by_name,
7109 repo);
7110 if (err)
7111 return err;
7113 TAILQ_FOREACH(re, &refs, entry)
7114 list_branch(repo, worktree, re->ref);
7116 got_ref_list_free(&refs);
7118 err = got_ref_list(&refs, repo, "refs/remotes", sort_by_time ?
7119 got_ref_cmp_by_commit_timestamp_descending : got_ref_cmp_by_name,
7120 repo);
7121 if (err)
7122 return err;
7124 TAILQ_FOREACH(re, &refs, entry)
7125 list_branch(repo, worktree, re->ref);
7127 got_ref_list_free(&refs);
7129 return NULL;
7132 static const struct got_error *
7133 delete_branch(struct got_repository *repo, struct got_worktree *worktree,
7134 const char *branch_name)
7136 const struct got_error *err = NULL;
7137 struct got_reference *ref = NULL;
7138 char *refname, *remote_refname = NULL;
7140 if (strncmp(branch_name, "refs/", 5) == 0)
7141 branch_name += 5;
7142 if (strncmp(branch_name, "heads/", 6) == 0)
7143 branch_name += 6;
7144 else if (strncmp(branch_name, "remotes/", 8) == 0)
7145 branch_name += 8;
7147 if (asprintf(&refname, "refs/heads/%s", branch_name) == -1)
7148 return got_error_from_errno("asprintf");
7150 if (asprintf(&remote_refname, "refs/remotes/%s",
7151 branch_name) == -1) {
7152 err = got_error_from_errno("asprintf");
7153 goto done;
7156 err = got_ref_open(&ref, repo, refname, 0);
7157 if (err) {
7158 const struct got_error *err2;
7159 if (err->code != GOT_ERR_NOT_REF)
7160 goto done;
7162 * Keep 'err' intact such that if neither branch exists
7163 * we report "refs/heads" rather than "refs/remotes" in
7164 * our error message.
7166 err2 = got_ref_open(&ref, repo, remote_refname, 0);
7167 if (err2)
7168 goto done;
7169 err = NULL;
7172 if (worktree &&
7173 strcmp(got_worktree_get_head_ref_name(worktree),
7174 got_ref_get_name(ref)) == 0) {
7175 err = got_error_msg(GOT_ERR_SAME_BRANCH,
7176 "will not delete this work tree's current branch");
7177 goto done;
7180 err = delete_ref(repo, ref);
7181 done:
7182 if (ref)
7183 got_ref_close(ref);
7184 free(refname);
7185 free(remote_refname);
7186 return err;
7189 static const struct got_error *
7190 add_branch(struct got_repository *repo, const char *branch_name,
7191 struct got_object_id *base_commit_id)
7193 const struct got_error *err = NULL;
7194 struct got_reference *ref = NULL;
7195 char *refname = NULL;
7198 * Don't let the user create a branch name with a leading '-'.
7199 * While technically a valid reference name, this case is usually
7200 * an unintended typo.
7202 if (branch_name[0] == '-')
7203 return got_error_path(branch_name, GOT_ERR_REF_NAME_MINUS);
7205 if (strncmp(branch_name, "refs/heads/", 11) == 0)
7206 branch_name += 11;
7208 if (asprintf(&refname, "refs/heads/%s", branch_name) == -1) {
7209 err = got_error_from_errno("asprintf");
7210 goto done;
7213 err = got_ref_open(&ref, repo, refname, 0);
7214 if (err == NULL) {
7215 err = got_error(GOT_ERR_BRANCH_EXISTS);
7216 goto done;
7217 } else if (err->code != GOT_ERR_NOT_REF)
7218 goto done;
7220 err = got_ref_alloc(&ref, refname, base_commit_id);
7221 if (err)
7222 goto done;
7224 err = got_ref_write(ref, repo);
7225 done:
7226 if (ref)
7227 got_ref_close(ref);
7228 free(refname);
7229 return err;
7232 static const struct got_error *
7233 cmd_branch(int argc, char *argv[])
7235 const struct got_error *error = NULL;
7236 struct got_repository *repo = NULL;
7237 struct got_worktree *worktree = NULL;
7238 char *cwd = NULL, *repo_path = NULL;
7239 int ch, do_list = 0, do_show = 0, do_update = 1, sort_by_time = 0;
7240 const char *delref = NULL, *commit_id_arg = NULL;
7241 struct got_reference *ref = NULL;
7242 struct got_pathlist_head paths;
7243 struct got_object_id *commit_id = NULL;
7244 char *commit_id_str = NULL, *keyword_idstr = NULL;;
7245 int *pack_fds = NULL;
7247 TAILQ_INIT(&paths);
7249 #ifndef PROFILE
7250 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
7251 "sendfd unveil", NULL) == -1)
7252 err(1, "pledge");
7253 #endif
7255 while ((ch = getopt(argc, argv, "c:d:lnr:t")) != -1) {
7256 switch (ch) {
7257 case 'c':
7258 commit_id_arg = optarg;
7259 break;
7260 case 'd':
7261 delref = optarg;
7262 break;
7263 case 'l':
7264 do_list = 1;
7265 break;
7266 case 'n':
7267 do_update = 0;
7268 break;
7269 case 'r':
7270 repo_path = realpath(optarg, NULL);
7271 if (repo_path == NULL)
7272 return got_error_from_errno2("realpath",
7273 optarg);
7274 got_path_strip_trailing_slashes(repo_path);
7275 break;
7276 case 't':
7277 sort_by_time = 1;
7278 break;
7279 default:
7280 usage_branch();
7281 /* NOTREACHED */
7285 if (do_list && delref)
7286 option_conflict('l', 'd');
7287 if (sort_by_time && !do_list)
7288 errx(1, "-t option requires -l option");
7290 argc -= optind;
7291 argv += optind;
7293 if (!do_list && !delref && argc == 0)
7294 do_show = 1;
7296 if ((do_list || delref || do_show) && commit_id_arg != NULL)
7297 errx(1, "-c option can only be used when creating a branch");
7299 if (do_list || delref) {
7300 if (argc > 0)
7301 usage_branch();
7302 } else if (!do_show && argc != 1)
7303 usage_branch();
7305 cwd = getcwd(NULL, 0);
7306 if (cwd == NULL) {
7307 error = got_error_from_errno("getcwd");
7308 goto done;
7311 error = got_repo_pack_fds_open(&pack_fds);
7312 if (error != NULL)
7313 goto done;
7315 if (repo_path == NULL) {
7316 error = got_worktree_open(&worktree, cwd,
7317 GOT_WORKTREE_GOT_DIR);
7318 if (error && error->code != GOT_ERR_NOT_WORKTREE)
7319 goto done;
7320 else
7321 error = NULL;
7322 if (worktree) {
7323 repo_path =
7324 strdup(got_worktree_get_repo_path(worktree));
7325 if (repo_path == NULL)
7326 error = got_error_from_errno("strdup");
7327 if (error)
7328 goto done;
7329 } else {
7330 repo_path = strdup(cwd);
7331 if (repo_path == NULL) {
7332 error = got_error_from_errno("strdup");
7333 goto done;
7338 error = got_repo_open(&repo, repo_path, NULL, pack_fds);
7339 if (error != NULL)
7340 goto done;
7342 #ifndef PROFILE
7343 if (do_list || do_show) {
7344 /* Remove "cpath" promise. */
7345 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
7346 NULL) == -1)
7347 err(1, "pledge");
7349 #endif
7351 error = apply_unveil(got_repo_get_path(repo), do_list,
7352 worktree ? got_worktree_get_root_path(worktree) : NULL);
7353 if (error)
7354 goto done;
7356 if (do_show)
7357 error = show_current_branch(repo, worktree);
7358 else if (do_list)
7359 error = list_branches(repo, worktree, sort_by_time);
7360 else if (delref)
7361 error = delete_branch(repo, worktree, delref);
7362 else {
7363 struct got_reflist_head refs;
7364 TAILQ_INIT(&refs);
7365 error = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name,
7366 NULL);
7367 if (error)
7368 goto done;
7369 if (commit_id_arg == NULL)
7370 commit_id_arg = worktree ?
7371 got_worktree_get_head_ref_name(worktree) :
7372 GOT_REF_HEAD;
7373 else {
7374 error = got_keyword_to_idstr(&keyword_idstr,
7375 commit_id_arg, repo, worktree);
7376 if (error != NULL)
7377 goto done;
7378 if (keyword_idstr != NULL)
7379 commit_id_arg = keyword_idstr;
7381 error = got_repo_match_object_id(&commit_id, NULL,
7382 commit_id_arg, GOT_OBJ_TYPE_COMMIT, &refs, repo);
7383 got_ref_list_free(&refs);
7384 if (error)
7385 goto done;
7386 error = add_branch(repo, argv[0], commit_id);
7387 if (error)
7388 goto done;
7389 if (worktree && do_update) {
7390 struct got_update_progress_arg upa;
7391 char *branch_refname = NULL;
7393 error = got_object_id_str(&commit_id_str, commit_id);
7394 if (error)
7395 goto done;
7396 error = get_worktree_paths_from_argv(&paths, 0, NULL,
7397 worktree);
7398 if (error)
7399 goto done;
7400 if (asprintf(&branch_refname, "refs/heads/%s", argv[0])
7401 == -1) {
7402 error = got_error_from_errno("asprintf");
7403 goto done;
7405 error = got_ref_open(&ref, repo, branch_refname, 0);
7406 free(branch_refname);
7407 if (error)
7408 goto done;
7409 error = switch_head_ref(ref, commit_id, worktree,
7410 repo);
7411 if (error)
7412 goto done;
7413 error = got_worktree_set_base_commit_id(worktree, repo,
7414 commit_id);
7415 if (error)
7416 goto done;
7417 memset(&upa, 0, sizeof(upa));
7418 error = got_worktree_checkout_files(worktree, &paths,
7419 repo, update_progress, &upa, check_cancelled,
7420 NULL);
7421 if (error)
7422 goto done;
7423 if (upa.did_something) {
7424 printf("Updated to %s: %s\n",
7425 got_worktree_get_head_ref_name(worktree),
7426 commit_id_str);
7428 print_update_progress_stats(&upa);
7431 done:
7432 free(keyword_idstr);
7433 if (ref)
7434 got_ref_close(ref);
7435 if (repo) {
7436 const struct got_error *close_err = got_repo_close(repo);
7437 if (error == NULL)
7438 error = close_err;
7440 if (worktree)
7441 got_worktree_close(worktree);
7442 if (pack_fds) {
7443 const struct got_error *pack_err =
7444 got_repo_pack_fds_close(pack_fds);
7445 if (error == NULL)
7446 error = pack_err;
7448 free(cwd);
7449 free(repo_path);
7450 free(commit_id);
7451 free(commit_id_str);
7452 got_pathlist_free(&paths, GOT_PATHLIST_FREE_PATH);
7453 return error;
7457 __dead static void
7458 usage_tag(void)
7460 fprintf(stderr, "usage: %s tag [-lVv] [-c commit] [-m message] "
7461 "[-r repository-path] [-s signer-id] name\n", getprogname());
7462 exit(1);
7465 #if 0
7466 static const struct got_error *
7467 sort_tags(struct got_reflist_head *sorted, struct got_reflist_head *tags)
7469 const struct got_error *err = NULL;
7470 struct got_reflist_entry *re, *se, *new;
7471 struct got_object_id *re_id, *se_id;
7472 struct got_tag_object *re_tag, *se_tag;
7473 time_t re_time, se_time;
7475 STAILQ_FOREACH(re, tags, entry) {
7476 se = STAILQ_FIRST(sorted);
7477 if (se == NULL) {
7478 err = got_reflist_entry_dup(&new, re);
7479 if (err)
7480 return err;
7481 STAILQ_INSERT_HEAD(sorted, new, entry);
7482 continue;
7483 } else {
7484 err = got_ref_resolve(&re_id, repo, re->ref);
7485 if (err)
7486 break;
7487 err = got_object_open_as_tag(&re_tag, repo, re_id);
7488 free(re_id);
7489 if (err)
7490 break;
7491 re_time = got_object_tag_get_tagger_time(re_tag);
7492 got_object_tag_close(re_tag);
7495 while (se) {
7496 err = got_ref_resolve(&se_id, repo, re->ref);
7497 if (err)
7498 break;
7499 err = got_object_open_as_tag(&se_tag, repo, se_id);
7500 free(se_id);
7501 if (err)
7502 break;
7503 se_time = got_object_tag_get_tagger_time(se_tag);
7504 got_object_tag_close(se_tag);
7506 if (se_time > re_time) {
7507 err = got_reflist_entry_dup(&new, re);
7508 if (err)
7509 return err;
7510 STAILQ_INSERT_AFTER(sorted, se, new, entry);
7511 break;
7513 se = STAILQ_NEXT(se, entry);
7514 continue;
7517 done:
7518 return err;
7520 #endif
7522 static const struct got_error *
7523 get_tag_refname(char **refname, const char *tag_name)
7525 const struct got_error *err;
7527 if (strncmp("refs/tags/", tag_name, 10) == 0) {
7528 *refname = strdup(tag_name);
7529 if (*refname == NULL)
7530 return got_error_from_errno("strdup");
7531 } else if (asprintf(refname, "refs/tags/%s", tag_name) == -1) {
7532 err = got_error_from_errno("asprintf");
7533 *refname = NULL;
7534 return err;
7537 return NULL;
7540 static const struct got_error *
7541 list_tags(struct got_repository *repo, const char *tag_name, int verify_tags,
7542 const char *allowed_signers, const char *revoked_signers, int verbosity)
7544 static const struct got_error *err = NULL;
7545 struct got_reflist_head refs;
7546 struct got_reflist_entry *re;
7547 char *wanted_refname = NULL;
7548 int bad_sigs = 0;
7550 TAILQ_INIT(&refs);
7552 err = got_ref_list(&refs, repo, "refs/tags", got_ref_cmp_tags, repo);
7553 if (err)
7554 return err;
7556 if (tag_name) {
7557 struct got_reference *ref;
7558 err = get_tag_refname(&wanted_refname, tag_name);
7559 if (err)
7560 goto done;
7561 /* Wanted tag reference should exist. */
7562 err = got_ref_open(&ref, repo, wanted_refname, 0);
7563 if (err)
7564 goto done;
7565 got_ref_close(ref);
7568 TAILQ_FOREACH(re, &refs, entry) {
7569 const char *refname;
7570 char *refstr, *tagmsg0, *tagmsg, *line, *id_str, *datestr;
7571 char datebuf[26];
7572 const char *tagger, *ssh_sig = NULL;
7573 char *sig_msg = NULL;
7574 time_t tagger_time;
7575 struct got_object_id *id;
7576 struct got_tag_object *tag;
7577 struct got_commit_object *commit = NULL;
7579 refname = got_ref_get_name(re->ref);
7580 if (strncmp(refname, "refs/tags/", 10) != 0 ||
7581 (wanted_refname && strcmp(refname, wanted_refname) != 0))
7582 continue;
7583 refname += 10;
7584 refstr = got_ref_to_str(re->ref);
7585 if (refstr == NULL) {
7586 err = got_error_from_errno("got_ref_to_str");
7587 break;
7590 err = got_ref_resolve(&id, repo, re->ref);
7591 if (err)
7592 break;
7593 err = got_object_open_as_tag(&tag, repo, id);
7594 if (err) {
7595 if (err->code != GOT_ERR_OBJ_TYPE) {
7596 free(id);
7597 break;
7599 /* "lightweight" tag */
7600 err = got_object_open_as_commit(&commit, repo, id);
7601 if (err) {
7602 free(id);
7603 break;
7605 tagger = got_object_commit_get_committer(commit);
7606 tagger_time =
7607 got_object_commit_get_committer_time(commit);
7608 err = got_object_id_str(&id_str, id);
7609 free(id);
7610 if (err)
7611 break;
7612 } else {
7613 free(id);
7614 tagger = got_object_tag_get_tagger(tag);
7615 tagger_time = got_object_tag_get_tagger_time(tag);
7616 err = got_object_id_str(&id_str,
7617 got_object_tag_get_object_id(tag));
7618 if (err)
7619 break;
7622 if (tag && verify_tags) {
7623 ssh_sig = got_sigs_get_tagmsg_ssh_signature(
7624 got_object_tag_get_message(tag));
7625 if (ssh_sig && allowed_signers == NULL) {
7626 err = got_error_msg(
7627 GOT_ERR_VERIFY_TAG_SIGNATURE,
7628 "SSH signature verification requires "
7629 "setting allowed_signers in "
7630 "got.conf(5)");
7631 break;
7635 printf("%stag %s %s\n", GOT_COMMIT_SEP_STR, refname, refstr);
7636 free(refstr);
7637 printf("from: %s\n", tagger);
7638 datestr = get_datestr(&tagger_time, datebuf);
7639 if (datestr)
7640 printf("date: %s UTC\n", datestr);
7641 if (commit)
7642 printf("object: %s %s\n", GOT_OBJ_LABEL_COMMIT, id_str);
7643 else {
7644 switch (got_object_tag_get_object_type(tag)) {
7645 case GOT_OBJ_TYPE_BLOB:
7646 printf("object: %s %s\n", GOT_OBJ_LABEL_BLOB,
7647 id_str);
7648 break;
7649 case GOT_OBJ_TYPE_TREE:
7650 printf("object: %s %s\n", GOT_OBJ_LABEL_TREE,
7651 id_str);
7652 break;
7653 case GOT_OBJ_TYPE_COMMIT:
7654 printf("object: %s %s\n", GOT_OBJ_LABEL_COMMIT,
7655 id_str);
7656 break;
7657 case GOT_OBJ_TYPE_TAG:
7658 printf("object: %s %s\n", GOT_OBJ_LABEL_TAG,
7659 id_str);
7660 break;
7661 default:
7662 break;
7665 free(id_str);
7667 if (ssh_sig) {
7668 err = got_sigs_verify_tag_ssh(&sig_msg, tag, ssh_sig,
7669 allowed_signers, revoked_signers, verbosity);
7670 if (err && err->code == GOT_ERR_BAD_TAG_SIGNATURE)
7671 bad_sigs = 1;
7672 else if (err)
7673 break;
7674 printf("signature: %s", sig_msg);
7675 free(sig_msg);
7676 sig_msg = NULL;
7679 if (commit) {
7680 err = got_object_commit_get_logmsg(&tagmsg0, commit);
7681 if (err)
7682 break;
7683 got_object_commit_close(commit);
7684 } else {
7685 tagmsg0 = strdup(got_object_tag_get_message(tag));
7686 got_object_tag_close(tag);
7687 if (tagmsg0 == NULL) {
7688 err = got_error_from_errno("strdup");
7689 break;
7693 tagmsg = tagmsg0;
7694 do {
7695 line = strsep(&tagmsg, "\n");
7696 if (line)
7697 printf(" %s\n", line);
7698 } while (line);
7699 free(tagmsg0);
7701 done:
7702 got_ref_list_free(&refs);
7703 free(wanted_refname);
7705 if (err == NULL && bad_sigs)
7706 err = got_error(GOT_ERR_BAD_TAG_SIGNATURE);
7707 return err;
7710 static const struct got_error *
7711 get_tag_message(char **tagmsg, char **tagmsg_path, const char *commit_id_str,
7712 const char *tag_name, const char *editor, const char *repo_path)
7714 const struct got_error *err = NULL;
7715 char *template = NULL, *initial_content = NULL;
7716 int initial_content_len;
7717 int fd = -1;
7719 if (asprintf(&template, GOT_TMPDIR_STR "/got-tagmsg") == -1) {
7720 err = got_error_from_errno("asprintf");
7721 goto done;
7724 initial_content_len = asprintf(&initial_content,
7725 "\n# tagging commit %s as %s\n",
7726 commit_id_str, tag_name);
7727 if (initial_content_len == -1) {
7728 err = got_error_from_errno("asprintf");
7729 goto done;
7732 err = got_opentemp_named_fd(tagmsg_path, &fd, template, "");
7733 if (err)
7734 goto done;
7736 if (write(fd, initial_content, initial_content_len) == -1) {
7737 err = got_error_from_errno2("write", *tagmsg_path);
7738 goto done;
7740 if (close(fd) == -1) {
7741 err = got_error_from_errno2("close", *tagmsg_path);
7742 goto done;
7744 fd = -1;
7746 err = edit_logmsg(tagmsg, editor, *tagmsg_path, initial_content,
7747 initial_content_len, 1);
7748 done:
7749 free(initial_content);
7750 free(template);
7752 if (fd != -1 && close(fd) == -1 && err == NULL)
7753 err = got_error_from_errno2("close", *tagmsg_path);
7755 if (err) {
7756 free(*tagmsg);
7757 *tagmsg = NULL;
7759 return err;
7762 static const struct got_error *
7763 add_tag(struct got_repository *repo, const char *tagger,
7764 const char *tag_name, const char *commit_arg, const char *tagmsg_arg,
7765 const char *signer_id, const char *editor, int verbosity)
7767 const struct got_error *err = NULL;
7768 struct got_object_id *commit_id = NULL, *tag_id = NULL;
7769 char *label = NULL, *commit_id_str = NULL;
7770 struct got_reference *ref = NULL;
7771 char *refname = NULL, *tagmsg = NULL;
7772 char *tagmsg_path = NULL, *tag_id_str = NULL;
7773 int preserve_tagmsg = 0;
7774 struct got_reflist_head refs;
7776 TAILQ_INIT(&refs);
7779 * Don't let the user create a tag name with a leading '-'.
7780 * While technically a valid reference name, this case is usually
7781 * an unintended typo.
7783 if (tag_name[0] == '-')
7784 return got_error_path(tag_name, GOT_ERR_REF_NAME_MINUS);
7786 err = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name, NULL);
7787 if (err)
7788 goto done;
7790 err = got_repo_match_object_id(&commit_id, &label, commit_arg,
7791 GOT_OBJ_TYPE_COMMIT, &refs, repo);
7792 if (err)
7793 goto done;
7795 err = got_object_id_str(&commit_id_str, commit_id);
7796 if (err)
7797 goto done;
7799 err = get_tag_refname(&refname, tag_name);
7800 if (err)
7801 goto done;
7802 if (strncmp("refs/tags/", tag_name, 10) == 0)
7803 tag_name += 10;
7805 err = got_ref_open(&ref, repo, refname, 0);
7806 if (err == NULL) {
7807 err = got_error(GOT_ERR_TAG_EXISTS);
7808 goto done;
7809 } else if (err->code != GOT_ERR_NOT_REF)
7810 goto done;
7812 if (tagmsg_arg == NULL) {
7813 err = get_tag_message(&tagmsg, &tagmsg_path, commit_id_str,
7814 tag_name, editor, got_repo_get_path(repo));
7815 if (err) {
7816 if (err->code != GOT_ERR_COMMIT_MSG_EMPTY &&
7817 tagmsg_path != NULL)
7818 preserve_tagmsg = 1;
7819 goto done;
7823 err = got_object_tag_create(&tag_id, tag_name, commit_id,
7824 tagger, time(NULL), tagmsg ? tagmsg : tagmsg_arg, signer_id, repo,
7825 verbosity);
7826 if (err) {
7827 if (tagmsg_path)
7828 preserve_tagmsg = 1;
7829 goto done;
7832 err = got_ref_alloc(&ref, refname, tag_id);
7833 if (err) {
7834 if (tagmsg_path)
7835 preserve_tagmsg = 1;
7836 goto done;
7839 err = got_ref_write(ref, repo);
7840 if (err) {
7841 if (tagmsg_path)
7842 preserve_tagmsg = 1;
7843 goto done;
7846 err = got_object_id_str(&tag_id_str, tag_id);
7847 if (err) {
7848 if (tagmsg_path)
7849 preserve_tagmsg = 1;
7850 goto done;
7852 printf("Created tag %s\n", tag_id_str);
7853 done:
7854 if (preserve_tagmsg) {
7855 fprintf(stderr, "%s: tag message preserved in %s\n",
7856 getprogname(), tagmsg_path);
7857 } else if (tagmsg_path && unlink(tagmsg_path) == -1 && err == NULL)
7858 err = got_error_from_errno2("unlink", tagmsg_path);
7859 free(tag_id_str);
7860 if (ref)
7861 got_ref_close(ref);
7862 free(commit_id);
7863 free(commit_id_str);
7864 free(refname);
7865 free(tagmsg);
7866 free(tagmsg_path);
7867 got_ref_list_free(&refs);
7868 return err;
7871 static const struct got_error *
7872 cmd_tag(int argc, char *argv[])
7874 const struct got_error *error = NULL;
7875 struct got_repository *repo = NULL;
7876 struct got_worktree *worktree = NULL;
7877 char *cwd = NULL, *repo_path = NULL, *commit_id_str = NULL;
7878 char *gitconfig_path = NULL, *tagger = NULL, *keyword_idstr = NULL;
7879 char *allowed_signers = NULL, *revoked_signers = NULL, *editor = NULL;
7880 const char *signer_id = NULL;
7881 const char *tag_name = NULL, *commit_id_arg = NULL, *tagmsg = NULL;
7882 int ch, do_list = 0, verify_tags = 0, verbosity = 0;
7883 int *pack_fds = NULL;
7885 #ifndef PROFILE
7886 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
7887 "sendfd unveil", NULL) == -1)
7888 err(1, "pledge");
7889 #endif
7891 while ((ch = getopt(argc, argv, "c:lm:r:s:Vv")) != -1) {
7892 switch (ch) {
7893 case 'c':
7894 commit_id_arg = optarg;
7895 break;
7896 case 'l':
7897 do_list = 1;
7898 break;
7899 case 'm':
7900 tagmsg = optarg;
7901 break;
7902 case 'r':
7903 repo_path = realpath(optarg, NULL);
7904 if (repo_path == NULL) {
7905 error = got_error_from_errno2("realpath",
7906 optarg);
7907 goto done;
7909 got_path_strip_trailing_slashes(repo_path);
7910 break;
7911 case 's':
7912 signer_id = optarg;
7913 break;
7914 case 'V':
7915 verify_tags = 1;
7916 break;
7917 case 'v':
7918 if (verbosity < 0)
7919 verbosity = 0;
7920 else if (verbosity < 3)
7921 verbosity++;
7922 break;
7923 default:
7924 usage_tag();
7925 /* NOTREACHED */
7929 argc -= optind;
7930 argv += optind;
7932 if (do_list || verify_tags) {
7933 if (commit_id_arg != NULL)
7934 errx(1,
7935 "-c option can only be used when creating a tag");
7936 if (tagmsg) {
7937 if (do_list)
7938 option_conflict('l', 'm');
7939 else
7940 option_conflict('V', 'm');
7942 if (signer_id) {
7943 if (do_list)
7944 option_conflict('l', 's');
7945 else
7946 option_conflict('V', 's');
7948 if (argc > 1)
7949 usage_tag();
7950 } else if (argc != 1)
7951 usage_tag();
7953 if (argc == 1)
7954 tag_name = argv[0];
7956 cwd = getcwd(NULL, 0);
7957 if (cwd == NULL) {
7958 error = got_error_from_errno("getcwd");
7959 goto done;
7962 error = got_repo_pack_fds_open(&pack_fds);
7963 if (error != NULL)
7964 goto done;
7966 if (repo_path == NULL) {
7967 error = got_worktree_open(&worktree, cwd,
7968 GOT_WORKTREE_GOT_DIR);
7969 if (error && error->code != GOT_ERR_NOT_WORKTREE)
7970 goto done;
7971 else
7972 error = NULL;
7973 if (worktree) {
7974 repo_path =
7975 strdup(got_worktree_get_repo_path(worktree));
7976 if (repo_path == NULL)
7977 error = got_error_from_errno("strdup");
7978 if (error)
7979 goto done;
7980 } else {
7981 repo_path = strdup(cwd);
7982 if (repo_path == NULL) {
7983 error = got_error_from_errno("strdup");
7984 goto done;
7989 if (do_list || verify_tags) {
7990 error = got_repo_open(&repo, repo_path, NULL, pack_fds);
7991 if (error != NULL)
7992 goto done;
7993 error = get_allowed_signers(&allowed_signers, repo, worktree);
7994 if (error)
7995 goto done;
7996 error = get_revoked_signers(&revoked_signers, repo, worktree);
7997 if (error)
7998 goto done;
7999 if (worktree) {
8000 /* Release work tree lock. */
8001 got_worktree_close(worktree);
8002 worktree = NULL;
8006 * Remove "cpath" promise unless needed for signature tmpfile
8007 * creation.
8009 if (verify_tags)
8010 got_sigs_apply_unveil();
8011 else {
8012 #ifndef PROFILE
8013 if (pledge("stdio rpath wpath flock proc exec sendfd "
8014 "unveil", NULL) == -1)
8015 err(1, "pledge");
8016 #endif
8018 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
8019 if (error)
8020 goto done;
8021 error = list_tags(repo, tag_name, verify_tags, allowed_signers,
8022 revoked_signers, verbosity);
8023 } else {
8024 error = get_gitconfig_path(&gitconfig_path);
8025 if (error)
8026 goto done;
8027 error = got_repo_open(&repo, repo_path, gitconfig_path,
8028 pack_fds);
8029 if (error != NULL)
8030 goto done;
8032 error = get_author(&tagger, repo, worktree);
8033 if (error)
8034 goto done;
8035 if (signer_id == NULL)
8036 signer_id = get_signer_id(repo, worktree);
8038 if (tagmsg == NULL) {
8039 error = get_editor(&editor);
8040 if (error)
8041 goto done;
8042 if (unveil(editor, "x") != 0) {
8043 error = got_error_from_errno2("unveil", editor);
8044 goto done;
8047 if (signer_id) {
8048 error = got_sigs_apply_unveil();
8049 if (error)
8050 goto done;
8052 error = apply_unveil(got_repo_get_path(repo), 0, NULL);
8053 if (error)
8054 goto done;
8056 if (commit_id_arg == NULL) {
8057 struct got_reference *head_ref;
8058 struct got_object_id *commit_id;
8059 error = got_ref_open(&head_ref, repo,
8060 worktree ? got_worktree_get_head_ref_name(worktree)
8061 : GOT_REF_HEAD, 0);
8062 if (error)
8063 goto done;
8064 error = got_ref_resolve(&commit_id, repo, head_ref);
8065 got_ref_close(head_ref);
8066 if (error)
8067 goto done;
8068 error = got_object_id_str(&commit_id_str, commit_id);
8069 free(commit_id);
8070 if (error)
8071 goto done;
8072 } else {
8073 error = got_keyword_to_idstr(&keyword_idstr,
8074 commit_id_arg, repo, worktree);
8075 if (error != NULL)
8076 goto done;
8077 commit_id_str = keyword_idstr;
8080 if (worktree) {
8081 /* Release work tree lock. */
8082 got_worktree_close(worktree);
8083 worktree = NULL;
8086 error = add_tag(repo, tagger, tag_name,
8087 commit_id_str ? commit_id_str : commit_id_arg, tagmsg,
8088 signer_id, editor, verbosity);
8090 done:
8091 if (repo) {
8092 const struct got_error *close_err = got_repo_close(repo);
8093 if (error == NULL)
8094 error = close_err;
8096 if (worktree)
8097 got_worktree_close(worktree);
8098 if (pack_fds) {
8099 const struct got_error *pack_err =
8100 got_repo_pack_fds_close(pack_fds);
8101 if (error == NULL)
8102 error = pack_err;
8104 free(cwd);
8105 free(editor);
8106 free(repo_path);
8107 free(gitconfig_path);
8108 free(commit_id_str);
8109 free(tagger);
8110 free(allowed_signers);
8111 free(revoked_signers);
8112 return error;
8115 __dead static void
8116 usage_add(void)
8118 fprintf(stderr, "usage: %s add [-IR] path ...\n", getprogname());
8119 exit(1);
8122 static const struct got_error *
8123 add_progress(void *arg, unsigned char status, const char *path)
8125 while (path[0] == '/')
8126 path++;
8127 printf("%c %s\n", status, path);
8128 return NULL;
8131 static const struct got_error *
8132 cmd_add(int argc, char *argv[])
8134 const struct got_error *error = NULL;
8135 struct got_repository *repo = NULL;
8136 struct got_worktree *worktree = NULL;
8137 char *cwd = NULL;
8138 struct got_pathlist_head paths;
8139 struct got_pathlist_entry *pe;
8140 int ch, can_recurse = 0, no_ignores = 0;
8141 int *pack_fds = NULL;
8143 TAILQ_INIT(&paths);
8145 #ifndef PROFILE
8146 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
8147 NULL) == -1)
8148 err(1, "pledge");
8149 #endif
8151 while ((ch = getopt(argc, argv, "IR")) != -1) {
8152 switch (ch) {
8153 case 'I':
8154 no_ignores = 1;
8155 break;
8156 case 'R':
8157 can_recurse = 1;
8158 break;
8159 default:
8160 usage_add();
8161 /* NOTREACHED */
8165 argc -= optind;
8166 argv += optind;
8168 if (argc < 1)
8169 usage_add();
8171 cwd = getcwd(NULL, 0);
8172 if (cwd == NULL) {
8173 error = got_error_from_errno("getcwd");
8174 goto done;
8177 error = got_repo_pack_fds_open(&pack_fds);
8178 if (error != NULL)
8179 goto done;
8181 error = got_worktree_open(&worktree, cwd, GOT_WORKTREE_GOT_DIR);
8182 if (error) {
8183 if (error->code == GOT_ERR_NOT_WORKTREE)
8184 error = wrap_not_worktree_error(error, "add", cwd);
8185 goto done;
8188 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
8189 NULL, pack_fds);
8190 if (error != NULL)
8191 goto done;
8193 error = apply_unveil(got_repo_get_path(repo), 1,
8194 got_worktree_get_root_path(worktree));
8195 if (error)
8196 goto done;
8198 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
8199 if (error)
8200 goto done;
8202 if (!can_recurse) {
8203 char *ondisk_path;
8204 struct stat sb;
8205 TAILQ_FOREACH(pe, &paths, entry) {
8206 if (asprintf(&ondisk_path, "%s/%s",
8207 got_worktree_get_root_path(worktree),
8208 pe->path) == -1) {
8209 error = got_error_from_errno("asprintf");
8210 goto done;
8212 if (lstat(ondisk_path, &sb) == -1) {
8213 if (errno == ENOENT) {
8214 free(ondisk_path);
8215 continue;
8217 error = got_error_from_errno2("lstat",
8218 ondisk_path);
8219 free(ondisk_path);
8220 goto done;
8222 free(ondisk_path);
8223 if (S_ISDIR(sb.st_mode)) {
8224 error = got_error_msg(GOT_ERR_BAD_PATH,
8225 "adding directories requires -R option");
8226 goto done;
8231 error = got_worktree_schedule_add(worktree, &paths, add_progress,
8232 NULL, repo, no_ignores);
8233 done:
8234 if (repo) {
8235 const struct got_error *close_err = got_repo_close(repo);
8236 if (error == NULL)
8237 error = close_err;
8239 if (worktree)
8240 got_worktree_close(worktree);
8241 if (pack_fds) {
8242 const struct got_error *pack_err =
8243 got_repo_pack_fds_close(pack_fds);
8244 if (error == NULL)
8245 error = pack_err;
8247 got_pathlist_free(&paths, GOT_PATHLIST_FREE_PATH);
8248 free(cwd);
8249 return error;
8252 __dead static void
8253 usage_remove(void)
8255 fprintf(stderr, "usage: %s remove [-fkR] [-s status-codes] path ...\n",
8256 getprogname());
8257 exit(1);
8260 static const struct got_error *
8261 print_remove_status(void *arg, unsigned char status,
8262 unsigned char staged_status, const char *path)
8264 while (path[0] == '/')
8265 path++;
8266 if (status == GOT_STATUS_NONEXISTENT)
8267 return NULL;
8268 if (status == staged_status && (status == GOT_STATUS_DELETE))
8269 status = GOT_STATUS_NO_CHANGE;
8270 printf("%c%c %s\n", status, staged_status, path);
8271 return NULL;
8274 static const struct got_error *
8275 cmd_remove(int argc, char *argv[])
8277 const struct got_error *error = NULL;
8278 struct got_worktree *worktree = NULL;
8279 struct got_repository *repo = NULL;
8280 const char *status_codes = NULL;
8281 char *cwd = NULL;
8282 struct got_pathlist_head paths;
8283 struct got_pathlist_entry *pe;
8284 int ch, delete_local_mods = 0, can_recurse = 0, keep_on_disk = 0, i;
8285 int ignore_missing_paths = 0;
8286 int *pack_fds = NULL;
8288 TAILQ_INIT(&paths);
8290 #ifndef PROFILE
8291 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
8292 NULL) == -1)
8293 err(1, "pledge");
8294 #endif
8296 while ((ch = getopt(argc, argv, "fkRs:")) != -1) {
8297 switch (ch) {
8298 case 'f':
8299 delete_local_mods = 1;
8300 ignore_missing_paths = 1;
8301 break;
8302 case 'k':
8303 keep_on_disk = 1;
8304 break;
8305 case 'R':
8306 can_recurse = 1;
8307 break;
8308 case 's':
8309 for (i = 0; optarg[i] != '\0'; i++) {
8310 switch (optarg[i]) {
8311 case GOT_STATUS_MODIFY:
8312 delete_local_mods = 1;
8313 break;
8314 case GOT_STATUS_MISSING:
8315 ignore_missing_paths = 1;
8316 break;
8317 default:
8318 errx(1, "invalid status code '%c'",
8319 optarg[i]);
8322 status_codes = optarg;
8323 break;
8324 default:
8325 usage_remove();
8326 /* NOTREACHED */
8330 argc -= optind;
8331 argv += optind;
8333 if (argc < 1)
8334 usage_remove();
8336 cwd = getcwd(NULL, 0);
8337 if (cwd == NULL) {
8338 error = got_error_from_errno("getcwd");
8339 goto done;
8342 error = got_repo_pack_fds_open(&pack_fds);
8343 if (error != NULL)
8344 goto done;
8346 error = got_worktree_open(&worktree, cwd, GOT_WORKTREE_GOT_DIR);
8347 if (error) {
8348 if (error->code == GOT_ERR_NOT_WORKTREE)
8349 error = wrap_not_worktree_error(error, "remove", cwd);
8350 goto done;
8353 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
8354 NULL, pack_fds);
8355 if (error)
8356 goto done;
8358 error = apply_unveil(got_repo_get_path(repo), 1,
8359 got_worktree_get_root_path(worktree));
8360 if (error)
8361 goto done;
8363 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
8364 if (error)
8365 goto done;
8367 if (!can_recurse) {
8368 char *ondisk_path;
8369 struct stat sb;
8370 TAILQ_FOREACH(pe, &paths, entry) {
8371 if (asprintf(&ondisk_path, "%s/%s",
8372 got_worktree_get_root_path(worktree),
8373 pe->path) == -1) {
8374 error = got_error_from_errno("asprintf");
8375 goto done;
8377 if (lstat(ondisk_path, &sb) == -1) {
8378 if (errno == ENOENT) {
8379 free(ondisk_path);
8380 continue;
8382 error = got_error_from_errno2("lstat",
8383 ondisk_path);
8384 free(ondisk_path);
8385 goto done;
8387 free(ondisk_path);
8388 if (S_ISDIR(sb.st_mode)) {
8389 error = got_error_msg(GOT_ERR_BAD_PATH,
8390 "removing directories requires -R option");
8391 goto done;
8396 error = got_worktree_schedule_delete(worktree, &paths,
8397 delete_local_mods, status_codes, print_remove_status, NULL,
8398 repo, keep_on_disk, ignore_missing_paths);
8399 done:
8400 if (repo) {
8401 const struct got_error *close_err = got_repo_close(repo);
8402 if (error == NULL)
8403 error = close_err;
8405 if (worktree)
8406 got_worktree_close(worktree);
8407 if (pack_fds) {
8408 const struct got_error *pack_err =
8409 got_repo_pack_fds_close(pack_fds);
8410 if (error == NULL)
8411 error = pack_err;
8413 got_pathlist_free(&paths, GOT_PATHLIST_FREE_PATH);
8414 free(cwd);
8415 return error;
8418 __dead static void
8419 usage_patch(void)
8421 fprintf(stderr, "usage: %s patch [-nR] [-c commit] [-p strip-count] "
8422 "[patchfile]\n", getprogname());
8423 exit(1);
8426 static const struct got_error *
8427 patch_from_stdin(int *patchfd)
8429 const struct got_error *err = NULL;
8430 ssize_t r;
8431 char buf[BUFSIZ];
8432 sig_t sighup, sigint, sigquit;
8434 *patchfd = got_opentempfd();
8435 if (*patchfd == -1)
8436 return got_error_from_errno("got_opentempfd");
8438 sighup = signal(SIGHUP, SIG_DFL);
8439 sigint = signal(SIGINT, SIG_DFL);
8440 sigquit = signal(SIGQUIT, SIG_DFL);
8442 for (;;) {
8443 r = read(0, buf, sizeof(buf));
8444 if (r == -1) {
8445 err = got_error_from_errno("read");
8446 break;
8448 if (r == 0)
8449 break;
8450 if (write(*patchfd, buf, r) == -1) {
8451 err = got_error_from_errno("write");
8452 break;
8456 signal(SIGHUP, sighup);
8457 signal(SIGINT, sigint);
8458 signal(SIGQUIT, sigquit);
8460 if (err == NULL && lseek(*patchfd, 0, SEEK_SET) == -1)
8461 err = got_error_from_errno("lseek");
8463 if (err != NULL) {
8464 close(*patchfd);
8465 *patchfd = -1;
8468 return err;
8471 struct got_patch_progress_arg {
8472 int did_something;
8473 int conflicts;
8474 int rejects;
8477 static const struct got_error *
8478 patch_progress(void *arg, const char *old, const char *new,
8479 unsigned char status, const struct got_error *error, int old_from,
8480 int old_lines, int new_from, int new_lines, int offset,
8481 int ws_mangled, const struct got_error *hunk_err)
8483 const char *path = new == NULL ? old : new;
8484 struct got_patch_progress_arg *a = arg;
8486 while (*path == '/')
8487 path++;
8489 if (status != GOT_STATUS_NO_CHANGE &&
8490 status != 0 /* per-hunk progress */) {
8491 printf("%c %s\n", status, path);
8492 a->did_something = 1;
8495 if (hunk_err == NULL) {
8496 if (status == GOT_STATUS_CANNOT_UPDATE)
8497 a->rejects++;
8498 else if (status == GOT_STATUS_CONFLICT)
8499 a->conflicts++;
8502 if (error != NULL)
8503 fprintf(stderr, "%s: %s\n", getprogname(), error->msg);
8505 if (offset != 0 || hunk_err != NULL || ws_mangled) {
8506 printf("@@ -%d,%d +%d,%d @@ ", old_from,
8507 old_lines, new_from, new_lines);
8508 if (hunk_err != NULL)
8509 printf("%s\n", hunk_err->msg);
8510 else if (offset != 0)
8511 printf("applied with offset %d\n", offset);
8512 else
8513 printf("hunk contains mangled whitespace\n");
8516 return NULL;
8519 static void
8520 print_patch_progress_stats(struct got_patch_progress_arg *ppa)
8522 if (!ppa->did_something)
8523 return;
8525 if (ppa->conflicts > 0)
8526 printf("Files with merge conflicts: %d\n", ppa->conflicts);
8528 if (ppa->rejects > 0) {
8529 printf("Files where patch failed to apply: %d\n",
8530 ppa->rejects);
8534 static const struct got_error *
8535 cmd_patch(int argc, char *argv[])
8537 const struct got_error *error = NULL, *close_error = NULL;
8538 struct got_worktree *worktree = NULL;
8539 struct got_repository *repo = NULL;
8540 struct got_reflist_head refs;
8541 struct got_object_id *commit_id = NULL;
8542 const char *commit_id_str = NULL;
8543 struct stat sb;
8544 const char *errstr;
8545 char *cwd = NULL, *keyword_idstr = NULL;
8546 int ch, nop = 0, strip = -1, reverse = 0;
8547 int patchfd;
8548 int *pack_fds = NULL;
8549 struct got_patch_progress_arg ppa;
8551 TAILQ_INIT(&refs);
8553 #ifndef PROFILE
8554 if (pledge("stdio rpath wpath cpath fattr proc exec sendfd flock "
8555 "unveil", NULL) == -1)
8556 err(1, "pledge");
8557 #endif
8559 while ((ch = getopt(argc, argv, "c:np:R")) != -1) {
8560 switch (ch) {
8561 case 'c':
8562 commit_id_str = optarg;
8563 break;
8564 case 'n':
8565 nop = 1;
8566 break;
8567 case 'p':
8568 strip = strtonum(optarg, 0, INT_MAX, &errstr);
8569 if (errstr != NULL)
8570 errx(1, "pathname strip count is %s: %s",
8571 errstr, optarg);
8572 break;
8573 case 'R':
8574 reverse = 1;
8575 break;
8576 default:
8577 usage_patch();
8578 /* NOTREACHED */
8582 argc -= optind;
8583 argv += optind;
8585 if (argc == 0) {
8586 error = patch_from_stdin(&patchfd);
8587 if (error)
8588 return error;
8589 } else if (argc == 1) {
8590 patchfd = open(argv[0], O_RDONLY);
8591 if (patchfd == -1)
8592 return got_error_from_errno2("open", argv[0]);
8593 if (fstat(patchfd, &sb) == -1) {
8594 error = got_error_from_errno2("fstat", argv[0]);
8595 goto done;
8597 if (!S_ISREG(sb.st_mode)) {
8598 error = got_error_path(argv[0], GOT_ERR_BAD_FILETYPE);
8599 goto done;
8601 } else
8602 usage_patch();
8604 if ((cwd = getcwd(NULL, 0)) == NULL) {
8605 error = got_error_from_errno("getcwd");
8606 goto done;
8609 error = got_repo_pack_fds_open(&pack_fds);
8610 if (error != NULL)
8611 goto done;
8613 error = got_worktree_open(&worktree, cwd, GOT_WORKTREE_GOT_DIR);
8614 if (error != NULL)
8615 goto done;
8617 const char *repo_path = got_worktree_get_repo_path(worktree);
8618 error = got_repo_open(&repo, repo_path, NULL, pack_fds);
8619 if (error != NULL)
8620 goto done;
8622 error = apply_unveil(got_repo_get_path(repo), 0,
8623 got_worktree_get_root_path(worktree));
8624 if (error != NULL)
8625 goto done;
8627 error = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name, NULL);
8628 if (error)
8629 goto done;
8631 if (commit_id_str != NULL) {
8632 error = got_keyword_to_idstr(&keyword_idstr, commit_id_str,
8633 repo, worktree);
8634 if (error != NULL)
8635 goto done;
8637 error = got_repo_match_object_id(&commit_id, NULL,
8638 keyword_idstr != NULL ? keyword_idstr : commit_id_str,
8639 GOT_OBJ_TYPE_COMMIT, &refs, repo);
8640 if (error)
8641 goto done;
8644 memset(&ppa, 0, sizeof(ppa));
8645 error = got_patch(patchfd, worktree, repo, nop, strip, reverse,
8646 commit_id, patch_progress, &ppa, check_cancelled, NULL);
8647 print_patch_progress_stats(&ppa);
8648 done:
8649 got_ref_list_free(&refs);
8650 free(keyword_idstr);
8651 free(commit_id);
8652 if (repo) {
8653 close_error = got_repo_close(repo);
8654 if (error == NULL)
8655 error = close_error;
8657 if (worktree != NULL) {
8658 close_error = got_worktree_close(worktree);
8659 if (error == NULL)
8660 error = close_error;
8662 if (pack_fds) {
8663 const struct got_error *pack_err =
8664 got_repo_pack_fds_close(pack_fds);
8665 if (error == NULL)
8666 error = pack_err;
8668 free(cwd);
8669 return error;
8672 __dead static void
8673 usage_revert(void)
8675 fprintf(stderr, "usage: %s revert [-pR] [-F response-script] path ...\n",
8676 getprogname());
8677 exit(1);
8680 static const struct got_error *
8681 revert_progress(void *arg, unsigned char status, const char *path)
8683 if (status == GOT_STATUS_UNVERSIONED)
8684 return NULL;
8686 while (path[0] == '/')
8687 path++;
8688 printf("%c %s\n", status, path);
8689 return NULL;
8692 struct choose_patch_arg {
8693 FILE *patch_script_file;
8694 const char *action;
8697 static const struct got_error *
8698 show_change(unsigned char status, const char *path, FILE *patch_file, int n,
8699 int nchanges, const char *action)
8701 const struct got_error *err;
8702 char *line = NULL;
8703 size_t linesize = 0;
8704 ssize_t linelen;
8706 switch (status) {
8707 case GOT_STATUS_ADD:
8708 printf("A %s\n%s this addition? [y/n] ", path, action);
8709 break;
8710 case GOT_STATUS_DELETE:
8711 printf("D %s\n%s this deletion? [y/n] ", path, action);
8712 break;
8713 case GOT_STATUS_MODIFY:
8714 if (fseek(patch_file, 0L, SEEK_SET) == -1)
8715 return got_error_from_errno("fseek");
8716 printf(GOT_COMMIT_SEP_STR);
8717 while ((linelen = getline(&line, &linesize, patch_file)) != -1)
8718 printf("%s", line);
8719 if (linelen == -1 && ferror(patch_file)) {
8720 err = got_error_from_errno("getline");
8721 free(line);
8722 return err;
8724 free(line);
8725 printf(GOT_COMMIT_SEP_STR);
8726 printf("M %s (change %d of %d)\n%s this change? [y/n/q] ",
8727 path, n, nchanges, action);
8728 break;
8729 default:
8730 return got_error_path(path, GOT_ERR_FILE_STATUS);
8733 fflush(stdout);
8734 return NULL;
8737 #define CHOOSE_PATCH_VALID_RESPONSES "ynq"
8739 static const struct got_error *
8740 choose_patch(int *choice, void *arg, unsigned char status, const char *path,
8741 FILE *patch_file, int n, int nchanges)
8743 const struct got_error *err = NULL;
8744 char *nl, *line = NULL;
8745 FILE *fp = stdin;
8746 size_t linesize = 0;
8747 ssize_t linelen;
8748 int interactive = 1;
8749 struct choose_patch_arg *a = arg;
8751 *choice = GOT_PATCH_CHOICE_NONE;
8753 if (a->patch_script_file) {
8754 interactive = 0;
8755 fp = a->patch_script_file;
8758 for (;;) {
8759 err = show_change(status, path, patch_file, n, nchanges,
8760 a->action);
8761 if (err)
8762 return err;
8764 linelen = getline(&line, &linesize, fp);
8765 if (linelen == -1) {
8766 free(line);
8767 if (ferror(fp))
8768 return got_error_from_errno("getline");
8769 if (feof(fp))
8770 return got_error(GOT_ERR_EOF);
8771 return NULL;
8774 nl = strchr(line, '\n');
8775 if (nl)
8776 *nl = '\0';
8777 if (strlen(line) != 1 ||
8778 !strchr(CHOOSE_PATCH_VALID_RESPONSES, line[0])) {
8779 printf("invalid response '%s'\n", line);
8780 if (interactive)
8781 continue;
8784 /* ADD and DELETE do not accept 'q' response currently. */
8785 if (status != GOT_STATUS_MODIFY && line[0] == 'q') {
8786 printf("invalid response '%s'\n", line);
8787 continue;
8790 break;
8793 switch (line[0]) {
8794 case 'y':
8795 *choice = GOT_PATCH_CHOICE_YES;
8796 break;
8797 case 'n':
8798 *choice = GOT_PATCH_CHOICE_NO;
8799 break;
8800 case 'q':
8801 if (status == GOT_STATUS_MODIFY)
8802 *choice = GOT_PATCH_CHOICE_QUIT;
8803 break;
8804 default:
8805 printf("invalid response '%s'\n", line);
8806 free(line);
8807 return NULL;
8810 if (!interactive)
8811 printf("%c\n", line[0]);
8813 free(line);
8814 return NULL;
8817 struct wt_commitable_path_arg {
8818 struct got_pathlist_head *commit_paths;
8819 int *has_changes;
8823 * Shortcut work tree status callback to determine if the set of paths scanned
8824 * has at least one versioned path that is being modified and, if not NULL, is
8825 * in the arg->commit_paths list. Set arg and return GOT_ERR_FILE_MODIFIED as
8826 * soon as a path is passed with a status that satisfies this criteria.
8828 static const struct got_error *
8829 worktree_has_commitable_path(void *arg, unsigned char status,
8830 unsigned char staged_status, const char *path,
8831 struct got_object_id *blob_id, struct got_object_id *staged_blob_id,
8832 struct got_object_id *commit_id, int dirfd, const char *de_name)
8834 struct wt_commitable_path_arg *a = arg;
8836 if (status == staged_status && (status == GOT_STATUS_DELETE))
8837 status = GOT_STATUS_NO_CHANGE;
8839 if (!(status == GOT_STATUS_NO_CHANGE ||
8840 status == GOT_STATUS_UNVERSIONED) ||
8841 staged_status != GOT_STATUS_NO_CHANGE) {
8842 if (a->commit_paths != NULL) {
8843 struct got_pathlist_entry *pe;
8845 TAILQ_FOREACH(pe, a->commit_paths, entry) {
8846 if (strncmp(path, pe->path,
8847 pe->path_len) == 0) {
8848 *a->has_changes = 1;
8849 break;
8852 } else
8853 *a->has_changes = 1;
8855 if (*a->has_changes)
8856 return got_error(GOT_ERR_FILE_MODIFIED);
8859 return NULL;
8863 * Check that the changeset of the commit identified by id is
8864 * comprised of at least one modified path that is being committed.
8866 static const struct got_error *
8867 commit_path_changed_in_worktree(struct wt_commitable_path_arg *wcpa,
8868 struct got_object_id *id, struct got_worktree *worktree,
8869 struct got_repository *repo)
8871 const struct got_error *err;
8872 struct got_pathlist_head paths;
8873 struct got_commit_object *commit = NULL, *pcommit = NULL;
8874 struct got_tree_object *tree = NULL, *ptree = NULL;
8875 struct got_object_qid *pid;
8877 TAILQ_INIT(&paths);
8879 err = got_object_open_as_commit(&commit, repo, id);
8880 if (err)
8881 goto done;
8883 err = got_object_open_as_tree(&tree, repo,
8884 got_object_commit_get_tree_id(commit));
8885 if (err)
8886 goto done;
8888 pid = STAILQ_FIRST(got_object_commit_get_parent_ids(commit));
8889 if (pid != NULL) {
8890 err = got_object_open_as_commit(&pcommit, repo, &pid->id);
8891 if (err)
8892 goto done;
8894 err = got_object_open_as_tree(&ptree, repo,
8895 got_object_commit_get_tree_id(pcommit));
8896 if (err)
8897 goto done;
8900 err = got_diff_tree(ptree, tree, NULL, NULL, -1, -1, "", "", repo,
8901 got_diff_tree_collect_changed_paths, &paths, 0);
8902 if (err)
8903 goto done;
8905 err = got_worktree_status(worktree, &paths, repo, 0,
8906 worktree_has_commitable_path, wcpa, check_cancelled, NULL);
8907 if (err && err->code == GOT_ERR_FILE_MODIFIED) {
8909 * At least one changed path in the referenced commit is
8910 * modified in the work tree, that's all we need to know!
8912 err = NULL;
8915 done:
8916 got_pathlist_free(&paths, GOT_PATHLIST_FREE_ALL);
8917 if (commit)
8918 got_object_commit_close(commit);
8919 if (pcommit)
8920 got_object_commit_close(pcommit);
8921 if (tree)
8922 got_object_tree_close(tree);
8923 if (ptree)
8924 got_object_tree_close(ptree);
8925 return err;
8929 * Remove any "logmsg" reference comprised entirely of paths that have
8930 * been reverted in this work tree. If any path in the logmsg ref changeset
8931 * remains in a changed state in the worktree, do not remove the reference.
8933 static const struct got_error *
8934 rm_logmsg_ref(struct got_worktree *worktree, struct got_repository *repo)
8936 const struct got_error *err;
8937 struct got_reflist_head refs;
8938 struct got_reflist_entry *re;
8939 struct got_commit_object *commit = NULL;
8940 struct got_object_id *commit_id = NULL;
8941 struct wt_commitable_path_arg wcpa;
8942 char *uuidstr = NULL;
8944 TAILQ_INIT(&refs);
8946 err = got_worktree_get_uuid(&uuidstr, worktree);
8947 if (err)
8948 goto done;
8950 err = got_ref_list(&refs, repo, "refs/got/worktree",
8951 got_ref_cmp_by_name, repo);
8952 if (err)
8953 goto done;
8955 TAILQ_FOREACH(re, &refs, entry) {
8956 const char *refname;
8957 int has_changes = 0;
8959 refname = got_ref_get_name(re->ref);
8961 if (!strncmp(refname, GOT_WORKTREE_CHERRYPICK_REF_PREFIX,
8962 GOT_WORKTREE_CHERRYPICK_REF_PREFIX_LEN))
8963 refname += GOT_WORKTREE_CHERRYPICK_REF_PREFIX_LEN + 1;
8964 else if (!strncmp(refname, GOT_WORKTREE_BACKOUT_REF_PREFIX,
8965 GOT_WORKTREE_BACKOUT_REF_PREFIX_LEN))
8966 refname += GOT_WORKTREE_BACKOUT_REF_PREFIX_LEN + 1;
8967 else
8968 continue;
8970 if (strncmp(refname, uuidstr, GOT_WORKTREE_UUID_STRLEN) == 0)
8971 refname += GOT_WORKTREE_UUID_STRLEN + 1; /* skip '-' */
8972 else
8973 continue;
8975 err = got_repo_match_object_id(&commit_id, NULL, refname,
8976 GOT_OBJ_TYPE_COMMIT, NULL, repo);
8977 if (err)
8978 goto done;
8980 err = got_object_open_as_commit(&commit, repo, commit_id);
8981 if (err)
8982 goto done;
8984 wcpa.commit_paths = NULL;
8985 wcpa.has_changes = &has_changes;
8987 err = commit_path_changed_in_worktree(&wcpa, commit_id,
8988 worktree, repo);
8989 if (err)
8990 goto done;
8992 if (!has_changes) {
8993 err = got_ref_delete(re->ref, repo);
8994 if (err)
8995 goto done;
8998 got_object_commit_close(commit);
8999 commit = NULL;
9000 free(commit_id);
9001 commit_id = NULL;
9004 done:
9005 free(uuidstr);
9006 free(commit_id);
9007 got_ref_list_free(&refs);
9008 if (commit)
9009 got_object_commit_close(commit);
9010 return err;
9013 static const struct got_error *
9014 cmd_revert(int argc, char *argv[])
9016 const struct got_error *error = NULL;
9017 struct got_worktree *worktree = NULL;
9018 struct got_repository *repo = NULL;
9019 char *cwd = NULL, *path = NULL;
9020 struct got_pathlist_head paths;
9021 struct got_pathlist_entry *pe;
9022 int ch, can_recurse = 0, pflag = 0;
9023 FILE *patch_script_file = NULL;
9024 const char *patch_script_path = NULL;
9025 struct choose_patch_arg cpa;
9026 int *pack_fds = NULL;
9028 TAILQ_INIT(&paths);
9030 #ifndef PROFILE
9031 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
9032 "unveil", NULL) == -1)
9033 err(1, "pledge");
9034 #endif
9036 while ((ch = getopt(argc, argv, "F:pR")) != -1) {
9037 switch (ch) {
9038 case 'F':
9039 patch_script_path = optarg;
9040 break;
9041 case 'p':
9042 pflag = 1;
9043 break;
9044 case 'R':
9045 can_recurse = 1;
9046 break;
9047 default:
9048 usage_revert();
9049 /* NOTREACHED */
9053 argc -= optind;
9054 argv += optind;
9056 if (argc < 1)
9057 usage_revert();
9058 if (patch_script_path && !pflag)
9059 errx(1, "-F option can only be used together with -p option");
9061 cwd = getcwd(NULL, 0);
9062 if (cwd == NULL) {
9063 error = got_error_from_errno("getcwd");
9064 goto done;
9067 error = got_repo_pack_fds_open(&pack_fds);
9068 if (error != NULL)
9069 goto done;
9071 error = got_worktree_open(&worktree, cwd, GOT_WORKTREE_GOT_DIR);
9072 if (error) {
9073 if (error->code == GOT_ERR_NOT_WORKTREE)
9074 error = wrap_not_worktree_error(error, "revert", cwd);
9075 goto done;
9078 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
9079 NULL, pack_fds);
9080 if (error != NULL)
9081 goto done;
9083 if (patch_script_path) {
9084 patch_script_file = fopen(patch_script_path, "re");
9085 if (patch_script_file == NULL) {
9086 error = got_error_from_errno2("fopen",
9087 patch_script_path);
9088 goto done;
9093 * XXX "c" perm needed on repo dir to delete merge references.
9095 error = apply_unveil(got_repo_get_path(repo), 0,
9096 got_worktree_get_root_path(worktree));
9097 if (error)
9098 goto done;
9100 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
9101 if (error)
9102 goto done;
9104 if (!can_recurse) {
9105 char *ondisk_path;
9106 struct stat sb;
9107 TAILQ_FOREACH(pe, &paths, entry) {
9108 if (asprintf(&ondisk_path, "%s/%s",
9109 got_worktree_get_root_path(worktree),
9110 pe->path) == -1) {
9111 error = got_error_from_errno("asprintf");
9112 goto done;
9114 if (lstat(ondisk_path, &sb) == -1) {
9115 if (errno == ENOENT) {
9116 free(ondisk_path);
9117 continue;
9119 error = got_error_from_errno2("lstat",
9120 ondisk_path);
9121 free(ondisk_path);
9122 goto done;
9124 free(ondisk_path);
9125 if (S_ISDIR(sb.st_mode)) {
9126 error = got_error_msg(GOT_ERR_BAD_PATH,
9127 "reverting directories requires -R option");
9128 goto done;
9133 cpa.patch_script_file = patch_script_file;
9134 cpa.action = "revert";
9135 error = got_worktree_revert(worktree, &paths, revert_progress, NULL,
9136 pflag ? choose_patch : NULL, &cpa, repo);
9138 error = rm_logmsg_ref(worktree, repo);
9139 done:
9140 if (patch_script_file && fclose(patch_script_file) == EOF &&
9141 error == NULL)
9142 error = got_error_from_errno2("fclose", patch_script_path);
9143 if (repo) {
9144 const struct got_error *close_err = got_repo_close(repo);
9145 if (error == NULL)
9146 error = close_err;
9148 if (worktree)
9149 got_worktree_close(worktree);
9150 if (pack_fds) {
9151 const struct got_error *pack_err =
9152 got_repo_pack_fds_close(pack_fds);
9153 if (error == NULL)
9154 error = pack_err;
9156 got_pathlist_free(&paths, GOT_PATHLIST_FREE_PATH);
9157 free(path);
9158 free(cwd);
9159 return error;
9162 __dead static void
9163 usage_commit(void)
9165 fprintf(stderr, "usage: %s commit [-CNnS] [-A author] [-F path] "
9166 "[-m message] [path ...]\n", getprogname());
9167 exit(1);
9170 struct collect_commit_logmsg_arg {
9171 const char *cmdline_log;
9172 const char *prepared_log;
9173 const char *merged_log;
9174 int non_interactive;
9175 const char *editor;
9176 const char *worktree_path;
9177 const char *branch_name;
9178 const char *repo_path;
9179 char *logmsg_path;
9183 static const struct got_error *
9184 read_prepared_logmsg(char **logmsg, const char *path)
9186 const struct got_error *err = NULL;
9187 FILE *f = NULL;
9188 struct stat sb;
9189 size_t r;
9191 *logmsg = NULL;
9192 memset(&sb, 0, sizeof(sb));
9194 f = fopen(path, "re");
9195 if (f == NULL)
9196 return got_error_from_errno2("fopen", path);
9198 if (fstat(fileno(f), &sb) == -1) {
9199 err = got_error_from_errno2("fstat", path);
9200 goto done;
9202 if (sb.st_size == 0) {
9203 err = got_error(GOT_ERR_COMMIT_MSG_EMPTY);
9204 goto done;
9207 *logmsg = malloc(sb.st_size + 1);
9208 if (*logmsg == NULL) {
9209 err = got_error_from_errno("malloc");
9210 goto done;
9213 r = fread(*logmsg, 1, sb.st_size, f);
9214 if (r != sb.st_size) {
9215 if (ferror(f))
9216 err = got_error_from_errno2("fread", path);
9217 else
9218 err = got_error(GOT_ERR_IO);
9219 goto done;
9221 (*logmsg)[sb.st_size] = '\0';
9222 done:
9223 if (fclose(f) == EOF && err == NULL)
9224 err = got_error_from_errno2("fclose", path);
9225 if (err) {
9226 free(*logmsg);
9227 *logmsg = NULL;
9229 return err;
9232 static const struct got_error *
9233 collect_commit_logmsg(struct got_pathlist_head *commitable_paths,
9234 const char *diff_path, char **logmsg, void *arg)
9236 char *initial_content = NULL;
9237 struct got_pathlist_entry *pe;
9238 const struct got_error *err = NULL;
9239 char *template = NULL;
9240 char *prepared_msg = NULL, *merged_msg = NULL;
9241 struct collect_commit_logmsg_arg *a = arg;
9242 int initial_content_len;
9243 int fd = -1;
9244 size_t len;
9246 /* if a message was specified on the command line, just use it */
9247 if (a->cmdline_log != NULL && *a->cmdline_log != '\0') {
9248 len = strlen(a->cmdline_log) + 1;
9249 *logmsg = malloc(len + 1);
9250 if (*logmsg == NULL)
9251 return got_error_from_errno("malloc");
9252 strlcpy(*logmsg, a->cmdline_log, len);
9253 return NULL;
9254 } else if (a->prepared_log != NULL && a->non_interactive)
9255 return read_prepared_logmsg(logmsg, a->prepared_log);
9257 if (asprintf(&template, "%s/logmsg", a->worktree_path) == -1)
9258 return got_error_from_errno("asprintf");
9260 err = got_opentemp_named_fd(&a->logmsg_path, &fd, template, "");
9261 if (err)
9262 goto done;
9264 if (a->prepared_log) {
9265 err = read_prepared_logmsg(&prepared_msg, a->prepared_log);
9266 if (err)
9267 goto done;
9268 } else if (a->merged_log) {
9269 err = read_prepared_logmsg(&merged_msg, a->merged_log);
9270 if (err)
9271 goto done;
9274 initial_content_len = asprintf(&initial_content,
9275 "%s%s\n# changes to be committed on branch %s:\n",
9276 prepared_msg ? prepared_msg : "",
9277 merged_msg ? merged_msg : "", a->branch_name);
9278 if (initial_content_len == -1) {
9279 err = got_error_from_errno("asprintf");
9280 goto done;
9283 if (write(fd, initial_content, initial_content_len) == -1) {
9284 err = got_error_from_errno2("write", a->logmsg_path);
9285 goto done;
9288 TAILQ_FOREACH(pe, commitable_paths, entry) {
9289 struct got_commitable *ct = pe->data;
9290 dprintf(fd, "# %c %s\n",
9291 got_commitable_get_status(ct),
9292 got_commitable_get_path(ct));
9295 if (diff_path) {
9296 dprintf(fd, "# detailed changes can be viewed in %s\n",
9297 diff_path);
9300 if (close(fd) == -1) {
9301 err = got_error_from_errno2("close", a->logmsg_path);
9302 goto done;
9304 fd = -1;
9306 err = edit_logmsg(logmsg, a->editor, a->logmsg_path, initial_content,
9307 initial_content_len, a->prepared_log ? 0 : 1);
9308 done:
9309 free(initial_content);
9310 free(template);
9311 free(prepared_msg);
9312 free(merged_msg);
9314 if (fd != -1 && close(fd) == -1 && err == NULL)
9315 err = got_error_from_errno2("close", a->logmsg_path);
9316 if (err) {
9317 free(*logmsg);
9318 *logmsg = NULL;
9320 return err;
9323 static const struct got_error *
9324 cat_logmsg(FILE *f, struct got_commit_object *commit, const char *idstr,
9325 const char *type, int has_content)
9327 const struct got_error *err = NULL;
9328 char *logmsg = NULL;
9330 err = got_object_commit_get_logmsg(&logmsg, commit);
9331 if (err)
9332 return err;
9334 if (fprintf(f, "%s# log message of %s commit %s:%s",
9335 has_content ? "\n" : "", type, idstr, logmsg) < 0)
9336 err = got_ferror(f, GOT_ERR_IO);
9338 free(logmsg);
9339 return err;
9343 * Lookup "logmsg" references of backed-out and cherrypicked commits
9344 * belonging to the current work tree. If found, and the worktree has
9345 * at least one modified file that was changed in the referenced commit,
9346 * add its log message to a new temporary file at *logmsg_path.
9347 * Add all refs found to matched_refs to be scheduled for removal on
9348 * successful commit.
9350 static const struct got_error *
9351 lookup_logmsg_ref(char **logmsg_path, struct got_pathlist_head *paths,
9352 struct got_reflist_head *matched_refs, struct got_worktree *worktree,
9353 struct got_repository *repo)
9355 const struct got_error *err;
9356 struct got_commit_object *commit = NULL;
9357 struct got_object_id *id = NULL;
9358 struct got_reflist_head refs;
9359 struct got_reflist_entry *re, *re_match;
9360 FILE *f = NULL;
9361 char *uuidstr = NULL;
9362 int added_logmsg = 0;
9364 TAILQ_INIT(&refs);
9366 *logmsg_path = NULL;
9368 err = got_worktree_get_uuid(&uuidstr, worktree);
9369 if (err)
9370 goto done;
9372 err = got_ref_list(&refs, repo, "refs/got/worktree",
9373 got_ref_cmp_by_name, repo);
9374 if (err)
9375 goto done;
9377 TAILQ_FOREACH(re, &refs, entry) {
9378 const char *refname, *type;
9379 struct wt_commitable_path_arg wcpa;
9380 int add_logmsg = 0;
9382 refname = got_ref_get_name(re->ref);
9384 if (strncmp(refname, GOT_WORKTREE_CHERRYPICK_REF_PREFIX,
9385 GOT_WORKTREE_CHERRYPICK_REF_PREFIX_LEN) == 0) {
9386 refname += GOT_WORKTREE_CHERRYPICK_REF_PREFIX_LEN + 1;
9387 type = "cherrypicked";
9388 } else if (strncmp(refname, GOT_WORKTREE_BACKOUT_REF_PREFIX,
9389 GOT_WORKTREE_BACKOUT_REF_PREFIX_LEN) == 0) {
9390 refname += GOT_WORKTREE_BACKOUT_REF_PREFIX_LEN + 1;
9391 type = "backed-out";
9392 } else
9393 continue;
9395 if (strncmp(refname, uuidstr, GOT_WORKTREE_UUID_STRLEN) == 0)
9396 refname += GOT_WORKTREE_UUID_STRLEN + 1; /* skip '-' */
9397 else
9398 continue;
9400 err = got_repo_match_object_id(&id, NULL, refname,
9401 GOT_OBJ_TYPE_COMMIT, NULL, repo);
9402 if (err)
9403 goto done;
9405 err = got_object_open_as_commit(&commit, repo, id);
9406 if (err)
9407 goto done;
9409 wcpa.commit_paths = paths;
9410 wcpa.has_changes = &add_logmsg;
9412 err = commit_path_changed_in_worktree(&wcpa, id,
9413 worktree, repo);
9414 if (err)
9415 goto done;
9417 if (add_logmsg) {
9418 if (f == NULL) {
9419 err = got_opentemp_named(logmsg_path, &f,
9420 "got-commit-logmsg", "");
9421 if (err)
9422 goto done;
9424 err = cat_logmsg(f, commit, refname, type,
9425 added_logmsg);
9426 if (err)
9427 goto done;
9428 if (!added_logmsg)
9429 ++added_logmsg;
9431 err = got_reflist_entry_dup(&re_match, re);
9432 if (err)
9433 goto done;
9434 TAILQ_INSERT_HEAD(matched_refs, re_match, entry);
9437 got_object_commit_close(commit);
9438 commit = NULL;
9439 free(id);
9440 id = NULL;
9443 done:
9444 free(id);
9445 free(uuidstr);
9446 got_ref_list_free(&refs);
9447 if (commit)
9448 got_object_commit_close(commit);
9449 if (f && fclose(f) == EOF && err == NULL)
9450 err = got_error_from_errno("fclose");
9451 if (!added_logmsg) {
9452 if (*logmsg_path && unlink(*logmsg_path) != 0 && err == NULL)
9453 err = got_error_from_errno2("unlink", *logmsg_path);
9454 *logmsg_path = NULL;
9456 return err;
9459 static const struct got_error *
9460 cmd_commit(int argc, char *argv[])
9462 const struct got_error *error = NULL;
9463 struct got_worktree *worktree = NULL;
9464 struct got_repository *repo = NULL;
9465 char *cwd = NULL, *id_str = NULL;
9466 struct got_object_id *id = NULL;
9467 const char *logmsg = NULL;
9468 char *prepared_logmsg = NULL, *merged_logmsg = NULL;
9469 struct collect_commit_logmsg_arg cl_arg;
9470 const char *author = NULL;
9471 char *gitconfig_path = NULL, *editor = NULL, *committer = NULL;
9472 int ch, rebase_in_progress, histedit_in_progress, preserve_logmsg = 0;
9473 int allow_bad_symlinks = 0, non_interactive = 0, merge_in_progress = 0;
9474 int show_diff = 1, commit_conflicts = 0;
9475 struct got_pathlist_head paths;
9476 struct got_reflist_head refs;
9477 struct got_reflist_entry *re;
9478 int *pack_fds = NULL;
9480 TAILQ_INIT(&refs);
9481 TAILQ_INIT(&paths);
9482 cl_arg.logmsg_path = NULL;
9484 #ifndef PROFILE
9485 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
9486 "unveil", NULL) == -1)
9487 err(1, "pledge");
9488 #endif
9490 while ((ch = getopt(argc, argv, "A:CF:m:NnS")) != -1) {
9491 switch (ch) {
9492 case 'A':
9493 author = optarg;
9494 error = valid_author(author);
9495 if (error)
9496 return error;
9497 break;
9498 case 'C':
9499 commit_conflicts = 1;
9500 break;
9501 case 'F':
9502 if (logmsg != NULL)
9503 option_conflict('F', 'm');
9504 prepared_logmsg = realpath(optarg, NULL);
9505 if (prepared_logmsg == NULL)
9506 return got_error_from_errno2("realpath",
9507 optarg);
9508 break;
9509 case 'm':
9510 if (prepared_logmsg)
9511 option_conflict('m', 'F');
9512 logmsg = optarg;
9513 break;
9514 case 'N':
9515 non_interactive = 1;
9516 break;
9517 case 'n':
9518 show_diff = 0;
9519 break;
9520 case 'S':
9521 allow_bad_symlinks = 1;
9522 break;
9523 default:
9524 usage_commit();
9525 /* NOTREACHED */
9529 argc -= optind;
9530 argv += optind;
9532 cwd = getcwd(NULL, 0);
9533 if (cwd == NULL) {
9534 error = got_error_from_errno("getcwd");
9535 goto done;
9538 error = got_repo_pack_fds_open(&pack_fds);
9539 if (error != NULL)
9540 goto done;
9542 error = got_worktree_open(&worktree, cwd, GOT_WORKTREE_GOT_DIR);
9543 if (error) {
9544 if (error->code == GOT_ERR_NOT_WORKTREE)
9545 error = wrap_not_worktree_error(error, "commit", cwd);
9546 goto done;
9549 error = got_worktree_rebase_in_progress(&rebase_in_progress, worktree);
9550 if (error)
9551 goto done;
9552 if (rebase_in_progress) {
9553 error = got_error(GOT_ERR_REBASING);
9554 goto done;
9557 error = got_worktree_histedit_in_progress(&histedit_in_progress,
9558 worktree);
9559 if (error)
9560 goto done;
9562 error = get_gitconfig_path(&gitconfig_path);
9563 if (error)
9564 goto done;
9565 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
9566 gitconfig_path, pack_fds);
9567 if (error != NULL)
9568 goto done;
9570 error = got_worktree_merge_in_progress(&merge_in_progress, worktree, repo);
9571 if (error)
9572 goto done;
9573 if (merge_in_progress) {
9574 error = got_error(GOT_ERR_MERGE_BUSY);
9575 goto done;
9578 error = get_author(&committer, repo, worktree);
9579 if (error)
9580 goto done;
9582 if (author == NULL)
9583 author = committer;
9585 if (logmsg == NULL || strlen(logmsg) == 0) {
9586 error = get_editor(&editor);
9587 if (error)
9588 goto done;
9589 if (unveil(editor, "x") != 0) {
9590 error = got_error_from_errno2("unveil", editor);
9591 goto done;
9594 if (prepared_logmsg) {
9595 if (unveil(prepared_logmsg, "r") != 0) {
9596 error = got_error_from_errno2("unveil",
9597 prepared_logmsg);
9598 goto done;
9602 error = apply_unveil(got_repo_get_path(repo), 0,
9603 got_worktree_get_root_path(worktree));
9604 if (error)
9605 goto done;
9607 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
9608 if (error)
9609 goto done;
9611 if (prepared_logmsg == NULL) {
9612 error = lookup_logmsg_ref(&merged_logmsg,
9613 argc > 0 ? &paths : NULL, &refs, worktree, repo);
9614 if (error)
9615 goto done;
9618 cl_arg.editor = editor;
9619 cl_arg.cmdline_log = logmsg;
9620 cl_arg.prepared_log = prepared_logmsg;
9621 cl_arg.merged_log = merged_logmsg;
9622 cl_arg.non_interactive = non_interactive;
9623 cl_arg.worktree_path = got_worktree_get_root_path(worktree);
9624 cl_arg.branch_name = got_worktree_get_head_ref_name(worktree);
9625 if (!histedit_in_progress) {
9626 if (strncmp(cl_arg.branch_name, "refs/heads/", 11) != 0) {
9627 error = got_error(GOT_ERR_COMMIT_BRANCH);
9628 goto done;
9630 cl_arg.branch_name += 11;
9632 cl_arg.repo_path = got_repo_get_path(repo);
9633 error = got_worktree_commit(&id, worktree, &paths, author, committer,
9634 allow_bad_symlinks, show_diff, commit_conflicts,
9635 collect_commit_logmsg, &cl_arg, print_status, NULL, repo);
9636 if (error) {
9637 if (error->code != GOT_ERR_COMMIT_MSG_EMPTY &&
9638 cl_arg.logmsg_path != NULL)
9639 preserve_logmsg = 1;
9640 goto done;
9643 error = got_object_id_str(&id_str, id);
9644 if (error)
9645 goto done;
9646 printf("Created commit %s\n", id_str);
9648 TAILQ_FOREACH(re, &refs, entry) {
9649 error = got_ref_delete(re->ref, repo);
9650 if (error)
9651 goto done;
9654 done:
9655 if (preserve_logmsg) {
9656 fprintf(stderr, "%s: log message preserved in %s\n",
9657 getprogname(), cl_arg.logmsg_path);
9658 } else if (cl_arg.logmsg_path && unlink(cl_arg.logmsg_path) == -1 &&
9659 error == NULL)
9660 error = got_error_from_errno2("unlink", cl_arg.logmsg_path);
9661 free(cl_arg.logmsg_path);
9662 if (merged_logmsg && unlink(merged_logmsg) == -1 && error == NULL)
9663 error = got_error_from_errno2("unlink", merged_logmsg);
9664 free(merged_logmsg);
9665 if (repo) {
9666 const struct got_error *close_err = got_repo_close(repo);
9667 if (error == NULL)
9668 error = close_err;
9670 if (worktree)
9671 got_worktree_close(worktree);
9672 if (pack_fds) {
9673 const struct got_error *pack_err =
9674 got_repo_pack_fds_close(pack_fds);
9675 if (error == NULL)
9676 error = pack_err;
9678 got_ref_list_free(&refs);
9679 got_pathlist_free(&paths, GOT_PATHLIST_FREE_PATH);
9680 free(cwd);
9681 free(id_str);
9682 free(gitconfig_path);
9683 free(editor);
9684 free(committer);
9685 free(prepared_logmsg);
9686 return error;
9689 __dead static void
9690 usage_send(void)
9692 fprintf(stderr, "usage: %s send [-afqTv] [-b branch] [-d branch] "
9693 "[-r repository-path] [-t tag] [remote-repository]\n",
9694 getprogname());
9695 exit(1);
9698 static void
9699 print_load_info(int print_colored, int print_found, int print_trees,
9700 int ncolored, int nfound, int ntrees)
9702 if (print_colored) {
9703 printf("%d commit%s colored", ncolored,
9704 ncolored == 1 ? "" : "s");
9706 if (print_found) {
9707 printf("%s%d object%s found",
9708 ncolored > 0 ? "; " : "",
9709 nfound, nfound == 1 ? "" : "s");
9711 if (print_trees) {
9712 printf("; %d tree%s scanned", ntrees,
9713 ntrees == 1 ? "" : "s");
9717 struct got_send_progress_arg {
9718 char last_scaled_packsize[FMT_SCALED_STRSIZE];
9719 int verbosity;
9720 int last_ncolored;
9721 int last_nfound;
9722 int last_ntrees;
9723 int loading_done;
9724 int last_ncommits;
9725 int last_nobj_total;
9726 int last_p_deltify;
9727 int last_p_written;
9728 int last_p_sent;
9729 int printed_something;
9730 int sent_something;
9731 struct got_pathlist_head *delete_branches;
9734 static const struct got_error *
9735 send_progress(void *arg, int ncolored, int nfound, int ntrees,
9736 off_t packfile_size, int ncommits, int nobj_total, int nobj_deltify,
9737 int nobj_written, off_t bytes_sent, const char *refname,
9738 const char *errmsg, int success)
9740 struct got_send_progress_arg *a = arg;
9741 char scaled_packsize[FMT_SCALED_STRSIZE];
9742 char scaled_sent[FMT_SCALED_STRSIZE];
9743 int p_deltify = 0, p_written = 0, p_sent = 0;
9744 int print_colored = 0, print_found = 0, print_trees = 0;
9745 int print_searching = 0, print_total = 0;
9746 int print_deltify = 0, print_written = 0, print_sent = 0;
9748 if (a->verbosity < 0)
9749 return NULL;
9751 if (refname) {
9752 const char *status = success ? "accepted" : "rejected";
9754 if (success) {
9755 struct got_pathlist_entry *pe;
9756 TAILQ_FOREACH(pe, a->delete_branches, entry) {
9757 const char *branchname = pe->path;
9758 if (got_path_cmp(branchname, refname,
9759 strlen(branchname), strlen(refname)) == 0) {
9760 status = "deleted";
9761 a->sent_something = 1;
9762 break;
9767 if (a->printed_something)
9768 putchar('\n');
9769 printf("Server has %s %s", status, refname);
9770 if (errmsg)
9771 printf(": %s", errmsg);
9772 a->printed_something = 1;
9773 return NULL;
9776 if (a->last_ncolored != ncolored) {
9777 print_colored = 1;
9778 a->last_ncolored = ncolored;
9781 if (a->last_nfound != nfound) {
9782 print_colored = 1;
9783 print_found = 1;
9784 a->last_nfound = nfound;
9787 if (a->last_ntrees != ntrees) {
9788 print_colored = 1;
9789 print_found = 1;
9790 print_trees = 1;
9791 a->last_ntrees = ntrees;
9794 if ((print_colored || print_found || print_trees) &&
9795 !a->loading_done) {
9796 printf("\r");
9797 print_load_info(print_colored, print_found, print_trees,
9798 ncolored, nfound, ntrees);
9799 a->printed_something = 1;
9800 fflush(stdout);
9801 return NULL;
9802 } else if (!a->loading_done) {
9803 printf("\r");
9804 print_load_info(1, 1, 1, ncolored, nfound, ntrees);
9805 printf("\n");
9806 a->loading_done = 1;
9809 if (fmt_scaled(packfile_size, scaled_packsize) == -1)
9810 return got_error_from_errno("fmt_scaled");
9811 if (fmt_scaled(bytes_sent, scaled_sent) == -1)
9812 return got_error_from_errno("fmt_scaled");
9814 if (a->last_ncommits != ncommits) {
9815 print_searching = 1;
9816 a->last_ncommits = ncommits;
9819 if (a->last_nobj_total != nobj_total) {
9820 print_searching = 1;
9821 print_total = 1;
9822 a->last_nobj_total = nobj_total;
9825 if (packfile_size > 0 && (a->last_scaled_packsize[0] == '\0' ||
9826 strcmp(scaled_packsize, a->last_scaled_packsize)) != 0) {
9827 if (strlcpy(a->last_scaled_packsize, scaled_packsize,
9828 FMT_SCALED_STRSIZE) >= FMT_SCALED_STRSIZE)
9829 return got_error(GOT_ERR_NO_SPACE);
9832 if (nobj_deltify > 0 || nobj_written > 0) {
9833 if (nobj_deltify > 0) {
9834 p_deltify = (nobj_deltify * 100) / nobj_total;
9835 if (p_deltify != a->last_p_deltify) {
9836 a->last_p_deltify = p_deltify;
9837 print_searching = 1;
9838 print_total = 1;
9839 print_deltify = 1;
9842 if (nobj_written > 0) {
9843 p_written = (nobj_written * 100) / nobj_total;
9844 if (p_written != a->last_p_written) {
9845 a->last_p_written = p_written;
9846 print_searching = 1;
9847 print_total = 1;
9848 print_deltify = 1;
9849 print_written = 1;
9854 if (bytes_sent > 0) {
9855 p_sent = (bytes_sent * 100) / packfile_size;
9856 if (p_sent != a->last_p_sent) {
9857 a->last_p_sent = p_sent;
9858 print_searching = 1;
9859 print_total = 1;
9860 print_deltify = 1;
9861 print_written = 1;
9862 print_sent = 1;
9864 a->sent_something = 1;
9867 if (print_searching || print_total || print_deltify || print_written ||
9868 print_sent)
9869 printf("\r");
9870 if (print_searching)
9871 printf("packing %d reference%s", ncommits,
9872 ncommits == 1 ? "" : "s");
9873 if (print_total)
9874 printf("; %d object%s", nobj_total,
9875 nobj_total == 1 ? "" : "s");
9876 if (print_deltify)
9877 printf("; deltify: %d%%", p_deltify);
9878 if (print_sent)
9879 printf("; uploading pack: %*s %d%%", FMT_SCALED_STRSIZE - 2,
9880 scaled_packsize, p_sent);
9881 else if (print_written)
9882 printf("; writing pack: %*s %d%%", FMT_SCALED_STRSIZE - 2,
9883 scaled_packsize, p_written);
9884 if (print_searching || print_total || print_deltify ||
9885 print_written || print_sent) {
9886 a->printed_something = 1;
9887 fflush(stdout);
9889 return NULL;
9892 static const struct got_error *
9893 cmd_send(int argc, char *argv[])
9895 const struct got_error *error = NULL;
9896 char *cwd = NULL, *repo_path = NULL;
9897 const char *remote_name;
9898 char *proto = NULL, *host = NULL, *port = NULL;
9899 char *repo_name = NULL, *server_path = NULL;
9900 const struct got_remote_repo *remotes;
9901 struct got_remote_repo *remote = NULL;
9902 int nremotes, nbranches = 0, ndelete_branches = 0;
9903 struct got_repository *repo = NULL;
9904 struct got_worktree *worktree = NULL;
9905 const struct got_gotconfig *repo_conf = NULL, *worktree_conf = NULL;
9906 struct got_pathlist_head branches;
9907 struct got_pathlist_head tags;
9908 struct got_reflist_head all_branches;
9909 struct got_reflist_head all_tags;
9910 struct got_pathlist_head delete_args;
9911 struct got_pathlist_head delete_branches;
9912 struct got_reflist_entry *re;
9913 struct got_pathlist_entry *pe;
9914 int i, ch, sendfd = -1, sendstatus;
9915 pid_t sendpid = -1;
9916 struct got_send_progress_arg spa;
9917 int verbosity = 0, overwrite_refs = 0;
9918 int send_all_branches = 0, send_all_tags = 0;
9919 struct got_reference *ref = NULL;
9920 int *pack_fds = NULL;
9922 TAILQ_INIT(&branches);
9923 TAILQ_INIT(&tags);
9924 TAILQ_INIT(&all_branches);
9925 TAILQ_INIT(&all_tags);
9926 TAILQ_INIT(&delete_args);
9927 TAILQ_INIT(&delete_branches);
9929 while ((ch = getopt(argc, argv, "ab:d:fqr:Tt:v")) != -1) {
9930 switch (ch) {
9931 case 'a':
9932 send_all_branches = 1;
9933 break;
9934 case 'b':
9935 error = got_pathlist_insert(NULL, &branches, optarg, NULL);
9936 if (error)
9937 return error;
9938 nbranches++;
9939 break;
9940 case 'd':
9941 error = got_pathlist_insert(NULL, &delete_args, optarg, NULL);
9942 if (error)
9943 return error;
9944 break;
9945 case 'f':
9946 overwrite_refs = 1;
9947 break;
9948 case 'q':
9949 verbosity = -1;
9950 break;
9951 case 'r':
9952 repo_path = realpath(optarg, NULL);
9953 if (repo_path == NULL)
9954 return got_error_from_errno2("realpath",
9955 optarg);
9956 got_path_strip_trailing_slashes(repo_path);
9957 break;
9958 case 'T':
9959 send_all_tags = 1;
9960 break;
9961 case 't':
9962 error = got_pathlist_insert(NULL, &tags, optarg, NULL);
9963 if (error)
9964 return error;
9965 break;
9966 case 'v':
9967 if (verbosity < 0)
9968 verbosity = 0;
9969 else if (verbosity < 3)
9970 verbosity++;
9971 break;
9972 default:
9973 usage_send();
9974 /* NOTREACHED */
9977 argc -= optind;
9978 argv += optind;
9980 if (send_all_branches && !TAILQ_EMPTY(&branches))
9981 option_conflict('a', 'b');
9982 if (send_all_tags && !TAILQ_EMPTY(&tags))
9983 option_conflict('T', 't');
9986 if (argc == 0)
9987 remote_name = GOT_SEND_DEFAULT_REMOTE_NAME;
9988 else if (argc == 1)
9989 remote_name = argv[0];
9990 else
9991 usage_send();
9993 cwd = getcwd(NULL, 0);
9994 if (cwd == NULL) {
9995 error = got_error_from_errno("getcwd");
9996 goto done;
9999 error = got_repo_pack_fds_open(&pack_fds);
10000 if (error != NULL)
10001 goto done;
10003 if (repo_path == NULL) {
10004 error = got_worktree_open(&worktree, cwd, GOT_WORKTREE_GOT_DIR);
10005 if (error && error->code != GOT_ERR_NOT_WORKTREE)
10006 goto done;
10007 else
10008 error = NULL;
10009 if (worktree) {
10010 repo_path =
10011 strdup(got_worktree_get_repo_path(worktree));
10012 if (repo_path == NULL)
10013 error = got_error_from_errno("strdup");
10014 if (error)
10015 goto done;
10016 } else {
10017 repo_path = strdup(cwd);
10018 if (repo_path == NULL) {
10019 error = got_error_from_errno("strdup");
10020 goto done;
10025 error = got_repo_open(&repo, repo_path, NULL, pack_fds);
10026 if (error)
10027 goto done;
10029 if (worktree) {
10030 worktree_conf = got_worktree_get_gotconfig(worktree);
10031 if (worktree_conf) {
10032 got_gotconfig_get_remotes(&nremotes, &remotes,
10033 worktree_conf);
10034 for (i = 0; i < nremotes; i++) {
10035 if (strcmp(remotes[i].name, remote_name) == 0) {
10036 error = got_repo_remote_repo_dup(&remote,
10037 &remotes[i]);
10038 if (error)
10039 goto done;
10040 break;
10045 if (remote == NULL) {
10046 repo_conf = got_repo_get_gotconfig(repo);
10047 if (repo_conf) {
10048 got_gotconfig_get_remotes(&nremotes, &remotes,
10049 repo_conf);
10050 for (i = 0; i < nremotes; i++) {
10051 if (strcmp(remotes[i].name, remote_name) == 0) {
10052 error = got_repo_remote_repo_dup(&remote,
10053 &remotes[i]);
10054 if (error)
10055 goto done;
10056 break;
10061 if (remote == NULL) {
10062 got_repo_get_gitconfig_remotes(&nremotes, &remotes, repo);
10063 for (i = 0; i < nremotes; i++) {
10064 if (strcmp(remotes[i].name, remote_name) == 0) {
10065 error = got_repo_remote_repo_dup(&remote,
10066 &remotes[i]);
10067 if (error)
10068 goto done;
10069 break;
10073 if (remote == NULL) {
10074 error = got_error_path(remote_name, GOT_ERR_NO_REMOTE);
10075 goto done;
10078 error = got_dial_parse_uri(&proto, &host, &port, &server_path,
10079 &repo_name, remote->send_url);
10080 if (error)
10081 goto done;
10083 if (strcmp(proto, "git") == 0) {
10084 #ifndef PROFILE
10085 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
10086 "sendfd dns inet unveil", NULL) == -1)
10087 err(1, "pledge");
10088 #endif
10089 } else if (strcmp(proto, "git+ssh") == 0 ||
10090 strcmp(proto, "ssh") == 0) {
10091 #ifndef PROFILE
10092 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
10093 "sendfd unveil", NULL) == -1)
10094 err(1, "pledge");
10095 #endif
10096 } else if (strcmp(proto, "http") == 0 ||
10097 strcmp(proto, "git+http") == 0) {
10098 error = got_error_path(proto, GOT_ERR_NOT_IMPL);
10099 goto done;
10100 } else {
10101 error = got_error_path(proto, GOT_ERR_BAD_PROTO);
10102 goto done;
10105 error = got_dial_apply_unveil(proto);
10106 if (error)
10107 goto done;
10109 error = apply_unveil(got_repo_get_path(repo), 0, NULL);
10110 if (error)
10111 goto done;
10113 if (send_all_branches) {
10114 error = got_ref_list(&all_branches, repo, "refs/heads",
10115 got_ref_cmp_by_name, NULL);
10116 if (error)
10117 goto done;
10118 TAILQ_FOREACH(re, &all_branches, entry) {
10119 const char *branchname = got_ref_get_name(re->ref);
10120 error = got_pathlist_insert(NULL, &branches,
10121 branchname, NULL);
10122 if (error)
10123 goto done;
10124 nbranches++;
10126 } else if (nbranches == 0) {
10127 for (i = 0; i < remote->nsend_branches; i++) {
10128 error = got_pathlist_insert(NULL, &branches,
10129 remote->send_branches[i], NULL);
10130 if (error)
10131 goto done;
10135 if (send_all_tags) {
10136 error = got_ref_list(&all_tags, repo, "refs/tags",
10137 got_ref_cmp_by_name, NULL);
10138 if (error)
10139 goto done;
10140 TAILQ_FOREACH(re, &all_tags, entry) {
10141 const char *tagname = got_ref_get_name(re->ref);
10142 error = got_pathlist_insert(NULL, &tags,
10143 tagname, NULL);
10144 if (error)
10145 goto done;
10150 * To prevent accidents only branches in refs/heads/ can be deleted
10151 * with 'got send -d'.
10152 * Deleting anything else requires local repository access or Git.
10154 TAILQ_FOREACH(pe, &delete_args, entry) {
10155 const char *branchname = pe->path;
10156 char *s;
10157 struct got_pathlist_entry *new;
10158 if (strncmp(branchname, "refs/heads/", 11) == 0) {
10159 s = strdup(branchname);
10160 if (s == NULL) {
10161 error = got_error_from_errno("strdup");
10162 goto done;
10164 } else {
10165 if (asprintf(&s, "refs/heads/%s", branchname) == -1) {
10166 error = got_error_from_errno("asprintf");
10167 goto done;
10170 error = got_pathlist_insert(&new, &delete_branches, s, NULL);
10171 if (error || new == NULL /* duplicate */)
10172 free(s);
10173 if (error)
10174 goto done;
10175 ndelete_branches++;
10178 if (nbranches == 0 && ndelete_branches == 0) {
10179 struct got_reference *head_ref;
10180 if (worktree)
10181 error = got_ref_open(&head_ref, repo,
10182 got_worktree_get_head_ref_name(worktree), 0);
10183 else
10184 error = got_ref_open(&head_ref, repo, GOT_REF_HEAD, 0);
10185 if (error)
10186 goto done;
10187 if (got_ref_is_symbolic(head_ref)) {
10188 error = got_ref_resolve_symbolic(&ref, repo, head_ref);
10189 got_ref_close(head_ref);
10190 if (error)
10191 goto done;
10192 } else
10193 ref = head_ref;
10194 error = got_pathlist_insert(NULL, &branches, got_ref_get_name(ref),
10195 NULL);
10196 if (error)
10197 goto done;
10198 nbranches++;
10201 if (worktree) {
10202 /* Release work tree lock. */
10203 got_worktree_close(worktree);
10204 worktree = NULL;
10207 if (verbosity >= 0) {
10208 printf("Connecting to \"%s\" %s://%s%s%s%s%s\n",
10209 remote->name, proto, host,
10210 port ? ":" : "", port ? port : "",
10211 *server_path == '/' ? "" : "/", server_path);
10214 error = got_send_connect(&sendpid, &sendfd, proto, host, port,
10215 server_path, verbosity);
10216 if (error)
10217 goto done;
10219 memset(&spa, 0, sizeof(spa));
10220 spa.last_scaled_packsize[0] = '\0';
10221 spa.last_p_deltify = -1;
10222 spa.last_p_written = -1;
10223 spa.verbosity = verbosity;
10224 spa.delete_branches = &delete_branches;
10225 error = got_send_pack(remote_name, &branches, &tags, &delete_branches,
10226 verbosity, overwrite_refs, sendfd, repo, send_progress, &spa,
10227 check_cancelled, NULL);
10228 if (spa.printed_something)
10229 putchar('\n');
10230 if (error)
10231 goto done;
10232 if (!spa.sent_something && verbosity >= 0)
10233 printf("Already up-to-date\n");
10234 done:
10235 if (sendpid > 0) {
10236 if (kill(sendpid, SIGTERM) == -1)
10237 error = got_error_from_errno("kill");
10238 if (waitpid(sendpid, &sendstatus, 0) == -1 && error == NULL)
10239 error = got_error_from_errno("waitpid");
10241 if (sendfd != -1 && close(sendfd) == -1 && error == NULL)
10242 error = got_error_from_errno("close");
10243 if (repo) {
10244 const struct got_error *close_err = got_repo_close(repo);
10245 if (error == NULL)
10246 error = close_err;
10248 if (worktree)
10249 got_worktree_close(worktree);
10250 if (pack_fds) {
10251 const struct got_error *pack_err =
10252 got_repo_pack_fds_close(pack_fds);
10253 if (error == NULL)
10254 error = pack_err;
10256 if (ref)
10257 got_ref_close(ref);
10258 got_repo_free_remote_repo_data(remote);
10259 free(remote);
10260 got_pathlist_free(&branches, GOT_PATHLIST_FREE_NONE);
10261 got_pathlist_free(&tags, GOT_PATHLIST_FREE_NONE);
10262 got_ref_list_free(&all_branches);
10263 got_ref_list_free(&all_tags);
10264 got_pathlist_free(&delete_args, GOT_PATHLIST_FREE_NONE);
10265 got_pathlist_free(&delete_branches, GOT_PATHLIST_FREE_PATH);
10266 free(cwd);
10267 free(repo_path);
10268 free(proto);
10269 free(host);
10270 free(port);
10271 free(server_path);
10272 free(repo_name);
10273 return error;
10277 * Print and if delete is set delete all ref_prefix references.
10278 * If wanted_ref is not NULL, only print or delete this reference.
10280 static const struct got_error *
10281 process_logmsg_refs(const char *ref_prefix, size_t prefix_len,
10282 const char *wanted_ref, int delete, struct got_worktree *worktree,
10283 struct got_repository *repo)
10285 const struct got_error *err;
10286 struct got_pathlist_head paths;
10287 struct got_reflist_head refs;
10288 struct got_reflist_entry *re;
10289 struct got_reflist_object_id_map *refs_idmap = NULL;
10290 struct got_commit_object *commit = NULL;
10291 struct got_object_id *id = NULL;
10292 const char *header_prefix;
10293 char *uuidstr = NULL;
10294 int found = 0;
10296 TAILQ_INIT(&refs);
10297 TAILQ_INIT(&paths);
10299 err = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name, repo);
10300 if (err)
10301 goto done;
10303 err = got_reflist_object_id_map_create(&refs_idmap, &refs, repo);
10304 if (err)
10305 goto done;
10307 if (worktree != NULL) {
10308 err = got_worktree_get_uuid(&uuidstr, worktree);
10309 if (err)
10310 goto done;
10313 if (wanted_ref) {
10314 if (strncmp(wanted_ref, "refs/heads/", 11) == 0)
10315 wanted_ref += 11;
10318 if (strcmp(ref_prefix, GOT_WORKTREE_BACKOUT_REF_PREFIX) == 0)
10319 header_prefix = "backout";
10320 else
10321 header_prefix = "cherrypick";
10323 TAILQ_FOREACH(re, &refs, entry) {
10324 const char *refname, *wt;
10326 refname = got_ref_get_name(re->ref);
10328 err = check_cancelled(NULL);
10329 if (err)
10330 goto done;
10332 if (strncmp(refname, ref_prefix, prefix_len) == 0)
10333 refname += prefix_len + 1; /* skip '-' delimiter */
10334 else
10335 continue;
10337 wt = refname;
10339 if (worktree == NULL || strncmp(refname, uuidstr,
10340 GOT_WORKTREE_UUID_STRLEN) == 0)
10341 refname += GOT_WORKTREE_UUID_STRLEN + 1; /* skip '-' */
10342 else
10343 continue;
10345 err = got_repo_match_object_id(&id, NULL, refname,
10346 GOT_OBJ_TYPE_COMMIT, NULL, repo);
10347 if (err)
10348 goto done;
10350 err = got_object_open_as_commit(&commit, repo, id);
10351 if (err)
10352 goto done;
10354 if (wanted_ref)
10355 found = strncmp(wanted_ref, refname,
10356 strlen(wanted_ref)) == 0;
10357 if (wanted_ref && !found) {
10358 struct got_reflist_head *ci_refs;
10360 ci_refs = got_reflist_object_id_map_lookup(refs_idmap,
10361 id);
10363 if (ci_refs) {
10364 char *refs_str = NULL;
10365 char const *r = NULL;
10367 err = build_refs_str(&refs_str, ci_refs, id,
10368 repo, 1);
10369 if (err)
10370 goto done;
10372 r = refs_str;
10373 while (r) {
10374 if (strncmp(r, wanted_ref,
10375 strlen(wanted_ref)) == 0) {
10376 found = 1;
10377 break;
10379 r = strchr(r, ' ');
10380 if (r)
10381 ++r;
10383 free(refs_str);
10387 if (wanted_ref == NULL || found) {
10388 if (delete) {
10389 err = got_ref_delete(re->ref, repo);
10390 if (err)
10391 goto done;
10392 printf("Deleted: ");
10393 err = print_commit_oneline(commit, id, repo,
10394 refs_idmap);
10395 } else {
10397 * Print paths modified by commit to help
10398 * associate commits with worktree changes.
10400 err = get_changed_paths(&paths, commit,
10401 repo, NULL);
10402 if (err)
10403 goto done;
10405 err = print_commit(commit, id, repo, NULL,
10406 &paths, NULL, 0, 0, refs_idmap, NULL,
10407 header_prefix);
10408 got_pathlist_free(&paths,
10409 GOT_PATHLIST_FREE_ALL);
10411 if (worktree == NULL)
10412 printf("work tree: %.*s\n\n",
10413 GOT_WORKTREE_UUID_STRLEN, wt);
10415 if (err || found)
10416 goto done;
10419 got_object_commit_close(commit);
10420 commit = NULL;
10421 free(id);
10422 id = NULL;
10425 if (wanted_ref != NULL && !found)
10426 err = got_error_fmt(GOT_ERR_NOT_REF, "%s", wanted_ref);
10428 done:
10429 free(id);
10430 free(uuidstr);
10431 got_ref_list_free(&refs);
10432 got_pathlist_free(&paths, GOT_PATHLIST_FREE_ALL);
10433 if (refs_idmap)
10434 got_reflist_object_id_map_free(refs_idmap);
10435 if (commit)
10436 got_object_commit_close(commit);
10437 return err;
10441 * Create new temp "logmsg" ref of the backed-out or cherrypicked commit
10442 * identified by id for log messages to prepopulate the editor on commit.
10444 static const struct got_error *
10445 logmsg_ref(struct got_object_id *id, const char *prefix,
10446 struct got_worktree *worktree, struct got_repository *repo)
10448 const struct got_error *err = NULL;
10449 char *idstr, *ref = NULL, *refname = NULL;
10450 int histedit_in_progress;
10451 int rebase_in_progress, merge_in_progress;
10454 * Silently refuse to create merge reference if any histedit, merge,
10455 * or rebase operation is in progress.
10457 err = got_worktree_histedit_in_progress(&histedit_in_progress,
10458 worktree);
10459 if (err)
10460 return err;
10461 if (histedit_in_progress)
10462 return NULL;
10464 err = got_worktree_rebase_in_progress(&rebase_in_progress, worktree);
10465 if (err)
10466 return err;
10467 if (rebase_in_progress)
10468 return NULL;
10470 err = got_worktree_merge_in_progress(&merge_in_progress, worktree,
10471 repo);
10472 if (err)
10473 return err;
10474 if (merge_in_progress)
10475 return NULL;
10477 err = got_object_id_str(&idstr, id);
10478 if (err)
10479 return err;
10481 err = got_worktree_get_logmsg_ref_name(&refname, worktree, prefix);
10482 if (err)
10483 goto done;
10485 if (asprintf(&ref, "%s-%s", refname, idstr) == -1) {
10486 err = got_error_from_errno("asprintf");
10487 goto done;
10490 err = create_ref(ref, got_worktree_get_base_commit_id(worktree),
10491 -1, repo);
10492 done:
10493 free(ref);
10494 free(idstr);
10495 free(refname);
10496 return err;
10499 __dead static void
10500 usage_cherrypick(void)
10502 fprintf(stderr, "usage: %s cherrypick [-lX] [commit-id]\n",
10503 getprogname());
10504 exit(1);
10507 static const struct got_error *
10508 cmd_cherrypick(int argc, char *argv[])
10510 const struct got_error *error = NULL;
10511 struct got_worktree *worktree = NULL;
10512 struct got_repository *repo = NULL;
10513 char *cwd = NULL, *commit_id_str = NULL, *keyword_idstr = NULL;
10514 struct got_object_id *commit_id = NULL;
10515 struct got_commit_object *commit = NULL;
10516 struct got_object_qid *pid;
10517 int ch, list_refs = 0, remove_refs = 0;
10518 struct got_update_progress_arg upa;
10519 int *pack_fds = NULL;
10521 #ifndef PROFILE
10522 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
10523 "unveil", NULL) == -1)
10524 err(1, "pledge");
10525 #endif
10527 while ((ch = getopt(argc, argv, "lX")) != -1) {
10528 switch (ch) {
10529 case 'l':
10530 list_refs = 1;
10531 break;
10532 case 'X':
10533 remove_refs = 1;
10534 break;
10535 default:
10536 usage_cherrypick();
10537 /* NOTREACHED */
10541 argc -= optind;
10542 argv += optind;
10544 if (list_refs || remove_refs) {
10545 if (argc != 0 && argc != 1)
10546 usage_cherrypick();
10547 } else if (argc != 1)
10548 usage_cherrypick();
10549 if (list_refs && remove_refs)
10550 option_conflict('l', 'X');
10552 cwd = getcwd(NULL, 0);
10553 if (cwd == NULL) {
10554 error = got_error_from_errno("getcwd");
10555 goto done;
10558 error = got_repo_pack_fds_open(&pack_fds);
10559 if (error != NULL)
10560 goto done;
10562 error = got_worktree_open(&worktree, cwd, GOT_WORKTREE_GOT_DIR);
10563 if (error) {
10564 if (list_refs || remove_refs) {
10565 if (error->code != GOT_ERR_NOT_WORKTREE)
10566 goto done;
10567 } else {
10568 if (error->code == GOT_ERR_NOT_WORKTREE)
10569 error = wrap_not_worktree_error(error,
10570 "cherrypick", cwd);
10571 goto done;
10575 error = got_repo_open(&repo,
10576 worktree ? got_worktree_get_repo_path(worktree) : cwd,
10577 NULL, pack_fds);
10578 if (error != NULL)
10579 goto done;
10581 error = apply_unveil(got_repo_get_path(repo), 0,
10582 worktree ? got_worktree_get_root_path(worktree) : NULL);
10583 if (error)
10584 goto done;
10586 if (list_refs || remove_refs) {
10587 error = process_logmsg_refs(GOT_WORKTREE_CHERRYPICK_REF_PREFIX,
10588 GOT_WORKTREE_CHERRYPICK_REF_PREFIX_LEN,
10589 argc == 1 ? argv[0] : NULL, remove_refs, worktree, repo);
10590 goto done;
10593 error = got_keyword_to_idstr(&keyword_idstr, argv[0], repo, worktree);
10594 if (error != NULL)
10595 goto done;
10597 error = got_repo_match_object_id(&commit_id, NULL,
10598 keyword_idstr != NULL ? keyword_idstr : argv[0],
10599 GOT_OBJ_TYPE_COMMIT, NULL, repo);
10600 if (error)
10601 goto done;
10602 error = got_object_id_str(&commit_id_str, commit_id);
10603 if (error)
10604 goto done;
10606 error = got_object_open_as_commit(&commit, repo, commit_id);
10607 if (error)
10608 goto done;
10609 pid = STAILQ_FIRST(got_object_commit_get_parent_ids(commit));
10610 memset(&upa, 0, sizeof(upa));
10611 error = got_worktree_merge_files(worktree, pid ? &pid->id : NULL,
10612 commit_id, repo, update_progress, &upa, check_cancelled,
10613 NULL);
10614 if (error != NULL)
10615 goto done;
10617 if (upa.did_something) {
10618 error = logmsg_ref(commit_id,
10619 GOT_WORKTREE_CHERRYPICK_REF_PREFIX, worktree, repo);
10620 if (error)
10621 goto done;
10622 printf("Merged commit %s\n", commit_id_str);
10624 print_merge_progress_stats(&upa);
10625 done:
10626 free(cwd);
10627 free(keyword_idstr);
10628 if (commit)
10629 got_object_commit_close(commit);
10630 free(commit_id_str);
10631 if (worktree)
10632 got_worktree_close(worktree);
10633 if (repo) {
10634 const struct got_error *close_err = got_repo_close(repo);
10635 if (error == NULL)
10636 error = close_err;
10638 if (pack_fds) {
10639 const struct got_error *pack_err =
10640 got_repo_pack_fds_close(pack_fds);
10641 if (error == NULL)
10642 error = pack_err;
10645 return error;
10648 __dead static void
10649 usage_backout(void)
10651 fprintf(stderr, "usage: %s backout [-lX] [commit-id]\n", getprogname());
10652 exit(1);
10655 static const struct got_error *
10656 cmd_backout(int argc, char *argv[])
10658 const struct got_error *error = NULL;
10659 struct got_worktree *worktree = NULL;
10660 struct got_repository *repo = NULL;
10661 char *cwd = NULL, *commit_id_str = NULL, *keyword_idstr = NULL;
10662 struct got_object_id *commit_id = NULL;
10663 struct got_commit_object *commit = NULL;
10664 struct got_object_qid *pid;
10665 int ch, list_refs = 0, remove_refs = 0;
10666 struct got_update_progress_arg upa;
10667 int *pack_fds = NULL;
10669 #ifndef PROFILE
10670 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
10671 "unveil", NULL) == -1)
10672 err(1, "pledge");
10673 #endif
10675 while ((ch = getopt(argc, argv, "lX")) != -1) {
10676 switch (ch) {
10677 case 'l':
10678 list_refs = 1;
10679 break;
10680 case 'X':
10681 remove_refs = 1;
10682 break;
10683 default:
10684 usage_backout();
10685 /* NOTREACHED */
10689 argc -= optind;
10690 argv += optind;
10692 if (list_refs || remove_refs) {
10693 if (argc != 0 && argc != 1)
10694 usage_backout();
10695 } else if (argc != 1)
10696 usage_backout();
10697 if (list_refs && remove_refs)
10698 option_conflict('l', 'X');
10700 cwd = getcwd(NULL, 0);
10701 if (cwd == NULL) {
10702 error = got_error_from_errno("getcwd");
10703 goto done;
10706 error = got_repo_pack_fds_open(&pack_fds);
10707 if (error != NULL)
10708 goto done;
10710 error = got_worktree_open(&worktree, cwd, GOT_WORKTREE_GOT_DIR);
10711 if (error) {
10712 if (list_refs || remove_refs) {
10713 if (error->code != GOT_ERR_NOT_WORKTREE)
10714 goto done;
10715 } else {
10716 if (error->code == GOT_ERR_NOT_WORKTREE)
10717 error = wrap_not_worktree_error(error,
10718 "backout", cwd);
10719 goto done;
10723 error = got_repo_open(&repo,
10724 worktree ? got_worktree_get_repo_path(worktree) : cwd,
10725 NULL, pack_fds);
10726 if (error != NULL)
10727 goto done;
10729 error = apply_unveil(got_repo_get_path(repo), 0,
10730 worktree ? got_worktree_get_root_path(worktree) : NULL);
10731 if (error)
10732 goto done;
10734 if (list_refs || remove_refs) {
10735 error = process_logmsg_refs(GOT_WORKTREE_BACKOUT_REF_PREFIX,
10736 GOT_WORKTREE_BACKOUT_REF_PREFIX_LEN,
10737 argc == 1 ? argv[0] : NULL, remove_refs, worktree, repo);
10738 goto done;
10741 error = got_keyword_to_idstr(&keyword_idstr, argv[0], repo, worktree);
10742 if (error != NULL)
10743 goto done;
10745 error = got_repo_match_object_id(&commit_id, NULL,
10746 keyword_idstr != NULL ? keyword_idstr : argv[0],
10747 GOT_OBJ_TYPE_COMMIT, NULL, repo);
10748 if (error)
10749 goto done;
10750 error = got_object_id_str(&commit_id_str, commit_id);
10751 if (error)
10752 goto done;
10754 error = got_object_open_as_commit(&commit, repo, commit_id);
10755 if (error)
10756 goto done;
10757 pid = STAILQ_FIRST(got_object_commit_get_parent_ids(commit));
10758 if (pid == NULL) {
10759 error = got_error(GOT_ERR_ROOT_COMMIT);
10760 goto done;
10763 memset(&upa, 0, sizeof(upa));
10764 error = got_worktree_merge_files(worktree, commit_id, &pid->id,
10765 repo, update_progress, &upa, check_cancelled, NULL);
10766 if (error != NULL)
10767 goto done;
10769 if (upa.did_something) {
10770 error = logmsg_ref(commit_id, GOT_WORKTREE_BACKOUT_REF_PREFIX,
10771 worktree, repo);
10772 if (error)
10773 goto done;
10774 printf("Backed out commit %s\n", commit_id_str);
10776 print_merge_progress_stats(&upa);
10777 done:
10778 free(cwd);
10779 free(keyword_idstr);
10780 if (commit)
10781 got_object_commit_close(commit);
10782 free(commit_id_str);
10783 if (worktree)
10784 got_worktree_close(worktree);
10785 if (repo) {
10786 const struct got_error *close_err = got_repo_close(repo);
10787 if (error == NULL)
10788 error = close_err;
10790 if (pack_fds) {
10791 const struct got_error *pack_err =
10792 got_repo_pack_fds_close(pack_fds);
10793 if (error == NULL)
10794 error = pack_err;
10796 return error;
10799 __dead static void
10800 usage_rebase(void)
10802 fprintf(stderr, "usage: %s rebase [-aCclX] [branch]\n", getprogname());
10803 exit(1);
10806 static void
10807 trim_logmsg(char *logmsg, int limit)
10809 char *nl;
10810 size_t len;
10812 len = strlen(logmsg);
10813 if (len > limit)
10814 len = limit;
10815 logmsg[len] = '\0';
10816 nl = strchr(logmsg, '\n');
10817 if (nl)
10818 *nl = '\0';
10821 static const struct got_error *
10822 get_short_logmsg(char **logmsg, int limit, struct got_commit_object *commit)
10824 const struct got_error *err;
10825 char *logmsg0 = NULL;
10826 const char *s;
10828 err = got_object_commit_get_logmsg(&logmsg0, commit);
10829 if (err)
10830 return err;
10832 s = logmsg0;
10833 while (isspace((unsigned char)s[0]))
10834 s++;
10836 *logmsg = strdup(s);
10837 if (*logmsg == NULL) {
10838 err = got_error_from_errno("strdup");
10839 goto done;
10842 trim_logmsg(*logmsg, limit);
10843 done:
10844 free(logmsg0);
10845 return err;
10848 static const struct got_error *
10849 show_rebase_merge_conflict(struct got_object_id *id,
10850 struct got_repository *repo)
10852 const struct got_error *err;
10853 struct got_commit_object *commit = NULL;
10854 char *id_str = NULL, *logmsg = NULL;
10856 err = got_object_open_as_commit(&commit, repo, id);
10857 if (err)
10858 return err;
10860 err = got_object_id_str(&id_str, id);
10861 if (err)
10862 goto done;
10864 id_str[12] = '\0';
10866 err = get_short_logmsg(&logmsg, 42, commit);
10867 if (err)
10868 goto done;
10870 printf("%s -> merge conflict: %s\n", id_str, logmsg);
10871 done:
10872 free(id_str);
10873 got_object_commit_close(commit);
10874 free(logmsg);
10875 return err;
10878 static const struct got_error *
10879 show_rebase_progress(struct got_commit_object *commit,
10880 struct got_object_id *old_id, struct got_object_id *new_id)
10882 const struct got_error *err;
10883 char *old_id_str = NULL, *new_id_str = NULL, *logmsg = NULL;
10885 err = got_object_id_str(&old_id_str, old_id);
10886 if (err)
10887 goto done;
10889 if (new_id) {
10890 err = got_object_id_str(&new_id_str, new_id);
10891 if (err)
10892 goto done;
10895 old_id_str[12] = '\0';
10896 if (new_id_str)
10897 new_id_str[12] = '\0';
10899 err = get_short_logmsg(&logmsg, 42, commit);
10900 if (err)
10901 goto done;
10903 printf("%s -> %s: %s\n", old_id_str,
10904 new_id_str ? new_id_str : "no-op change", logmsg);
10905 done:
10906 free(old_id_str);
10907 free(new_id_str);
10908 free(logmsg);
10909 return err;
10912 static const struct got_error *
10913 rebase_complete(struct got_worktree *worktree, struct got_fileindex *fileindex,
10914 struct got_reference *branch, struct got_reference *tmp_branch,
10915 struct got_repository *repo, int create_backup)
10917 printf("Switching work tree to %s\n", got_ref_get_name(branch));
10918 return got_worktree_rebase_complete(worktree, fileindex,
10919 tmp_branch, branch, repo, create_backup);
10922 static const struct got_error *
10923 rebase_commit(struct got_pathlist_head *merged_paths,
10924 struct got_worktree *worktree, struct got_fileindex *fileindex,
10925 struct got_reference *tmp_branch, const char *committer,
10926 struct got_object_id *commit_id, int allow_conflict,
10927 struct got_repository *repo)
10929 const struct got_error *error;
10930 struct got_commit_object *commit;
10931 struct got_object_id *new_commit_id;
10933 error = got_object_open_as_commit(&commit, repo, commit_id);
10934 if (error)
10935 return error;
10937 error = got_worktree_rebase_commit(&new_commit_id, merged_paths,
10938 worktree, fileindex, tmp_branch, committer, commit, commit_id,
10939 allow_conflict, repo);
10940 if (error) {
10941 if (error->code != GOT_ERR_COMMIT_NO_CHANGES)
10942 goto done;
10943 error = show_rebase_progress(commit, commit_id, NULL);
10944 } else {
10945 error = show_rebase_progress(commit, commit_id, new_commit_id);
10946 free(new_commit_id);
10948 done:
10949 got_object_commit_close(commit);
10950 return error;
10953 struct check_path_prefix_arg {
10954 const char *path_prefix;
10955 size_t len;
10956 int errcode;
10959 static const struct got_error *
10960 check_path_prefix_in_diff(void *arg, struct got_blob_object *blob1,
10961 struct got_blob_object *blob2, FILE *f1, FILE *f2,
10962 struct got_object_id *id1, struct got_object_id *id2,
10963 const char *path1, const char *path2,
10964 mode_t mode1, mode_t mode2, struct got_repository *repo)
10966 struct check_path_prefix_arg *a = arg;
10968 if ((path1 && !got_path_is_child(path1, a->path_prefix, a->len)) ||
10969 (path2 && !got_path_is_child(path2, a->path_prefix, a->len)))
10970 return got_error(a->errcode);
10972 return NULL;
10975 static const struct got_error *
10976 check_path_prefix(struct got_object_id *parent_id,
10977 struct got_object_id *commit_id, const char *path_prefix,
10978 int errcode, struct got_repository *repo)
10980 const struct got_error *err;
10981 struct got_tree_object *tree1 = NULL, *tree2 = NULL;
10982 struct got_commit_object *commit = NULL, *parent_commit = NULL;
10983 struct check_path_prefix_arg cpp_arg;
10985 if (got_path_is_root_dir(path_prefix))
10986 return NULL;
10988 err = got_object_open_as_commit(&commit, repo, commit_id);
10989 if (err)
10990 goto done;
10992 err = got_object_open_as_commit(&parent_commit, repo, parent_id);
10993 if (err)
10994 goto done;
10996 err = got_object_open_as_tree(&tree1, repo,
10997 got_object_commit_get_tree_id(parent_commit));
10998 if (err)
10999 goto done;
11001 err = got_object_open_as_tree(&tree2, repo,
11002 got_object_commit_get_tree_id(commit));
11003 if (err)
11004 goto done;
11006 cpp_arg.path_prefix = path_prefix;
11007 while (cpp_arg.path_prefix[0] == '/')
11008 cpp_arg.path_prefix++;
11009 cpp_arg.len = strlen(cpp_arg.path_prefix);
11010 cpp_arg.errcode = errcode;
11011 err = got_diff_tree(tree1, tree2, NULL, NULL, -1, -1, "", "", repo,
11012 check_path_prefix_in_diff, &cpp_arg, 0);
11013 done:
11014 if (tree1)
11015 got_object_tree_close(tree1);
11016 if (tree2)
11017 got_object_tree_close(tree2);
11018 if (commit)
11019 got_object_commit_close(commit);
11020 if (parent_commit)
11021 got_object_commit_close(parent_commit);
11022 return err;
11025 static const struct got_error *
11026 collect_commits(struct got_object_id_queue *commits,
11027 struct got_object_id *initial_commit_id,
11028 struct got_object_id *iter_start_id, struct got_object_id *iter_stop_id,
11029 const char *path_prefix, int path_prefix_errcode,
11030 struct got_repository *repo)
11032 const struct got_error *err = NULL;
11033 struct got_commit_graph *graph = NULL;
11034 struct got_object_id parent_id, commit_id;
11035 struct got_object_qid *qid;
11037 err = got_commit_graph_open(&graph, "/", 1);
11038 if (err)
11039 return err;
11041 err = got_commit_graph_bfsort(graph, iter_start_id, repo,
11042 check_cancelled, NULL);
11043 if (err)
11044 goto done;
11046 memcpy(&commit_id, initial_commit_id, sizeof(commit_id));
11047 while (got_object_id_cmp(&commit_id, iter_stop_id) != 0) {
11048 err = got_commit_graph_iter_next(&parent_id, graph, repo,
11049 check_cancelled, NULL);
11050 if (err) {
11051 if (err->code == GOT_ERR_ITER_COMPLETED) {
11052 err = got_error_msg(GOT_ERR_ANCESTRY,
11053 "ran out of commits to rebase before "
11054 "youngest common ancestor commit has "
11055 "been reached?!?");
11057 goto done;
11058 } else {
11059 err = check_path_prefix(&parent_id, &commit_id,
11060 path_prefix, path_prefix_errcode, repo);
11061 if (err)
11062 goto done;
11064 err = got_object_qid_alloc(&qid, &commit_id);
11065 if (err)
11066 goto done;
11067 STAILQ_INSERT_HEAD(commits, qid, entry);
11069 memcpy(&commit_id, &parent_id, sizeof(commit_id));
11072 done:
11073 got_commit_graph_close(graph);
11074 return err;
11077 static const struct got_error *
11078 get_commit_brief_str(char **brief_str, struct got_commit_object *commit)
11080 const struct got_error *err = NULL;
11081 time_t committer_time;
11082 struct tm tm;
11083 char datebuf[11]; /* YYYY-MM-DD + NUL */
11084 char *author0 = NULL, *author, *smallerthan;
11085 char *logmsg0 = NULL, *logmsg, *newline;
11087 committer_time = got_object_commit_get_committer_time(commit);
11088 if (gmtime_r(&committer_time, &tm) == NULL)
11089 return got_error_from_errno("gmtime_r");
11090 if (strftime(datebuf, sizeof(datebuf), "%F", &tm) == 0)
11091 return got_error(GOT_ERR_NO_SPACE);
11093 author0 = strdup(got_object_commit_get_author(commit));
11094 if (author0 == NULL)
11095 return got_error_from_errno("strdup");
11096 author = author0;
11097 smallerthan = strchr(author, '<');
11098 if (smallerthan && smallerthan[1] != '\0')
11099 author = smallerthan + 1;
11100 author[strcspn(author, "@>")] = '\0';
11102 err = got_object_commit_get_logmsg(&logmsg0, commit);
11103 if (err)
11104 goto done;
11105 logmsg = logmsg0;
11106 while (*logmsg == '\n')
11107 logmsg++;
11108 newline = strchr(logmsg, '\n');
11109 if (newline)
11110 *newline = '\0';
11112 if (asprintf(brief_str, "%s %s %s",
11113 datebuf, author, logmsg) == -1)
11114 err = got_error_from_errno("asprintf");
11115 done:
11116 free(author0);
11117 free(logmsg0);
11118 return err;
11121 static const struct got_error *
11122 delete_backup_ref(struct got_reference *ref, struct got_object_id *id,
11123 struct got_repository *repo)
11125 const struct got_error *err;
11126 char *id_str;
11128 err = got_object_id_str(&id_str, id);
11129 if (err)
11130 return err;
11132 err = got_ref_delete(ref, repo);
11133 if (err)
11134 goto done;
11136 printf("Deleted %s: %s\n", got_ref_get_name(ref), id_str);
11137 done:
11138 free(id_str);
11139 return err;
11142 static const struct got_error *
11143 print_backup_ref(const char *branch_name, const char *new_id_str,
11144 struct got_object_id *old_commit_id, struct got_commit_object *old_commit,
11145 struct got_reflist_object_id_map *refs_idmap,
11146 struct got_repository *repo)
11148 const struct got_error *err = NULL;
11149 struct got_reflist_head *refs;
11150 char *refs_str = NULL;
11151 struct got_object_id *new_commit_id = NULL;
11152 struct got_commit_object *new_commit = NULL;
11153 char *new_commit_brief_str = NULL;
11154 struct got_object_id *yca_id = NULL;
11155 struct got_commit_object *yca_commit = NULL;
11156 char *yca_id_str = NULL, *yca_brief_str = NULL;
11157 char *custom_refs_str;
11159 if (asprintf(&custom_refs_str, "formerly %s", branch_name) == -1)
11160 return got_error_from_errno("asprintf");
11162 err = print_commit(old_commit, old_commit_id, repo, NULL, NULL, NULL,
11163 0, 0, refs_idmap, custom_refs_str, NULL);
11164 if (err)
11165 goto done;
11167 err = got_object_resolve_id_str(&new_commit_id, repo, new_id_str);
11168 if (err)
11169 goto done;
11171 refs = got_reflist_object_id_map_lookup(refs_idmap, new_commit_id);
11172 if (refs) {
11173 err = build_refs_str(&refs_str, refs, new_commit_id, repo, 0);
11174 if (err)
11175 goto done;
11178 err = got_object_open_as_commit(&new_commit, repo, new_commit_id);
11179 if (err)
11180 goto done;
11182 err = get_commit_brief_str(&new_commit_brief_str, new_commit);
11183 if (err)
11184 goto done;
11186 err = got_commit_graph_find_youngest_common_ancestor(&yca_id,
11187 old_commit_id, new_commit_id, 1, 0, repo, check_cancelled, NULL);
11188 if (err)
11189 goto done;
11191 printf("has become commit %s%s%s%s\n %s\n", new_id_str,
11192 refs_str ? " (" : "", refs_str ? refs_str : "",
11193 refs_str ? ")" : "", new_commit_brief_str);
11194 if (yca_id && got_object_id_cmp(yca_id, new_commit_id) != 0 &&
11195 got_object_id_cmp(yca_id, old_commit_id) != 0) {
11196 free(refs_str);
11197 refs_str = NULL;
11199 err = got_object_open_as_commit(&yca_commit, repo, yca_id);
11200 if (err)
11201 goto done;
11203 err = get_commit_brief_str(&yca_brief_str, yca_commit);
11204 if (err)
11205 goto done;
11207 err = got_object_id_str(&yca_id_str, yca_id);
11208 if (err)
11209 goto done;
11211 refs = got_reflist_object_id_map_lookup(refs_idmap, yca_id);
11212 if (refs) {
11213 err = build_refs_str(&refs_str, refs, yca_id, repo, 0);
11214 if (err)
11215 goto done;
11217 printf("history forked at %s%s%s%s\n %s\n",
11218 yca_id_str,
11219 refs_str ? " (" : "", refs_str ? refs_str : "",
11220 refs_str ? ")" : "", yca_brief_str);
11222 done:
11223 free(custom_refs_str);
11224 free(new_commit_id);
11225 free(refs_str);
11226 free(yca_id);
11227 free(yca_id_str);
11228 free(yca_brief_str);
11229 if (new_commit)
11230 got_object_commit_close(new_commit);
11231 if (yca_commit)
11232 got_object_commit_close(yca_commit);
11234 return err;
11237 static const struct got_error *
11238 worktree_has_logmsg_ref(const char *caller, struct got_worktree *worktree,
11239 struct got_repository *repo)
11241 const struct got_error *err;
11242 struct got_reflist_head refs;
11243 struct got_reflist_entry *re;
11244 char *uuidstr = NULL;
11245 static char msg[160];
11247 TAILQ_INIT(&refs);
11249 err = got_worktree_get_uuid(&uuidstr, worktree);
11250 if (err)
11251 goto done;
11253 err = got_ref_list(&refs, repo, "refs/got/worktree",
11254 got_ref_cmp_by_name, repo);
11255 if (err)
11256 goto done;
11258 TAILQ_FOREACH(re, &refs, entry) {
11259 const char *cmd, *refname, *type;
11261 refname = got_ref_get_name(re->ref);
11263 if (strncmp(refname, GOT_WORKTREE_CHERRYPICK_REF_PREFIX,
11264 GOT_WORKTREE_CHERRYPICK_REF_PREFIX_LEN) == 0) {
11265 refname += GOT_WORKTREE_CHERRYPICK_REF_PREFIX_LEN + 1;
11266 cmd = "cherrypick";
11267 type = "cherrypicked";
11268 } else if (strncmp(refname, GOT_WORKTREE_BACKOUT_REF_PREFIX,
11269 GOT_WORKTREE_BACKOUT_REF_PREFIX_LEN) == 0) {
11270 refname += GOT_WORKTREE_BACKOUT_REF_PREFIX_LEN + 1;
11271 cmd = "backout";
11272 type = "backed-out";
11273 } else
11274 continue;
11276 if (strncmp(refname, uuidstr, GOT_WORKTREE_UUID_STRLEN) != 0)
11277 continue;
11279 snprintf(msg, sizeof(msg),
11280 "work tree has references created by %s commits which "
11281 "must be removed with 'got %s -X' before running the %s "
11282 "command", type, cmd, caller);
11283 err = got_error_msg(GOT_ERR_WORKTREE_META, msg);
11284 goto done;
11287 done:
11288 free(uuidstr);
11289 got_ref_list_free(&refs);
11290 return err;
11293 static const struct got_error *
11294 process_backup_refs(const char *backup_ref_prefix,
11295 const char *wanted_branch_name,
11296 int delete, struct got_repository *repo)
11298 const struct got_error *err;
11299 struct got_reflist_head refs, backup_refs;
11300 struct got_reflist_entry *re;
11301 const size_t backup_ref_prefix_len = strlen(backup_ref_prefix);
11302 struct got_object_id *old_commit_id = NULL;
11303 char *branch_name = NULL;
11304 struct got_commit_object *old_commit = NULL;
11305 struct got_reflist_object_id_map *refs_idmap = NULL;
11306 int wanted_branch_found = 0;
11308 TAILQ_INIT(&refs);
11309 TAILQ_INIT(&backup_refs);
11311 err = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name, NULL);
11312 if (err)
11313 return err;
11315 err = got_reflist_object_id_map_create(&refs_idmap, &refs, repo);
11316 if (err)
11317 goto done;
11319 if (wanted_branch_name) {
11320 if (strncmp(wanted_branch_name, "refs/heads/", 11) == 0)
11321 wanted_branch_name += 11;
11324 err = got_ref_list(&backup_refs, repo, backup_ref_prefix,
11325 got_ref_cmp_by_commit_timestamp_descending, repo);
11326 if (err)
11327 goto done;
11329 TAILQ_FOREACH(re, &backup_refs, entry) {
11330 const char *refname = got_ref_get_name(re->ref);
11331 char *slash;
11333 err = check_cancelled(NULL);
11334 if (err)
11335 break;
11337 err = got_ref_resolve(&old_commit_id, repo, re->ref);
11338 if (err)
11339 break;
11341 err = got_object_open_as_commit(&old_commit, repo,
11342 old_commit_id);
11343 if (err)
11344 break;
11346 if (strncmp(backup_ref_prefix, refname,
11347 backup_ref_prefix_len) == 0)
11348 refname += backup_ref_prefix_len;
11350 while (refname[0] == '/')
11351 refname++;
11353 branch_name = strdup(refname);
11354 if (branch_name == NULL) {
11355 err = got_error_from_errno("strdup");
11356 break;
11358 slash = strrchr(branch_name, '/');
11359 if (slash) {
11360 *slash = '\0';
11361 refname += strlen(branch_name) + 1;
11364 if (wanted_branch_name == NULL ||
11365 strcmp(wanted_branch_name, branch_name) == 0) {
11366 wanted_branch_found = 1;
11367 if (delete) {
11368 err = delete_backup_ref(re->ref,
11369 old_commit_id, repo);
11370 } else {
11371 err = print_backup_ref(branch_name, refname,
11372 old_commit_id, old_commit, refs_idmap,
11373 repo);
11375 if (err)
11376 break;
11379 free(old_commit_id);
11380 old_commit_id = NULL;
11381 free(branch_name);
11382 branch_name = NULL;
11383 got_object_commit_close(old_commit);
11384 old_commit = NULL;
11387 if (wanted_branch_name && !wanted_branch_found) {
11388 err = got_error_fmt(GOT_ERR_NOT_REF,
11389 "%s/%s/", backup_ref_prefix, wanted_branch_name);
11391 done:
11392 if (refs_idmap)
11393 got_reflist_object_id_map_free(refs_idmap);
11394 got_ref_list_free(&refs);
11395 got_ref_list_free(&backup_refs);
11396 free(old_commit_id);
11397 free(branch_name);
11398 if (old_commit)
11399 got_object_commit_close(old_commit);
11400 return err;
11403 static const struct got_error *
11404 abort_progress(void *arg, unsigned char status, const char *path)
11407 * Unversioned files should not clutter progress output when
11408 * an operation is aborted.
11410 if (status == GOT_STATUS_UNVERSIONED)
11411 return NULL;
11413 return update_progress(arg, status, path);
11416 static const struct got_error *
11417 find_merge_commit_yca(struct got_object_id **new_yca_id,
11418 struct got_object_id *branch_head_commit_id,
11419 struct got_object_id *yca_id,
11420 struct got_object_id *base_commit_id,
11421 struct got_repository *repo)
11423 const struct got_error *err = NULL;
11424 struct got_commit_graph *graph = NULL;
11425 struct got_commit_object *commit = NULL;
11427 *new_yca_id = NULL;
11429 err = got_commit_graph_open(&graph, "/", 1);
11430 if (err)
11431 return err;
11433 err = got_commit_graph_bfsort(graph, base_commit_id,
11434 repo, check_cancelled, NULL);
11435 if (err)
11436 goto done;
11438 for (;;) {
11439 struct got_object_id id;
11441 err = got_commit_graph_iter_next(&id, graph, repo,
11442 check_cancelled, NULL);
11443 if (err) {
11444 if (err->code == GOT_ERR_ITER_COMPLETED)
11445 err = NULL;
11446 break;
11449 err = got_object_open_as_commit(&commit, repo, &id);
11450 if (err)
11451 break;
11453 if (got_object_commit_get_nparents(commit) > 1) {
11454 /* Search for a better YCA using toposort. */
11455 err = got_commit_graph_find_youngest_common_ancestor(
11456 new_yca_id, base_commit_id, branch_head_commit_id,
11457 0, 1, repo, check_cancelled, NULL);
11458 break;
11461 if (got_object_id_cmp(&id, yca_id) == 0)
11462 break;
11463 got_object_commit_close(commit);
11464 commit = NULL;
11466 done:
11467 got_commit_graph_close(graph);
11468 if (commit)
11469 got_object_commit_close(commit);
11470 return err;
11473 static const struct got_error *
11474 cmd_rebase(int argc, char *argv[])
11476 const struct got_error *error = NULL;
11477 struct got_worktree *worktree = NULL;
11478 struct got_repository *repo = NULL;
11479 struct got_fileindex *fileindex = NULL;
11480 char *cwd = NULL, *committer = NULL, *gitconfig_path = NULL;
11481 struct got_reference *branch = NULL;
11482 struct got_reference *new_base_branch = NULL, *tmp_branch = NULL;
11483 struct got_object_id *commit_id = NULL, *parent_id = NULL;
11484 struct got_object_id *resume_commit_id = NULL;
11485 struct got_object_id *branch_head_commit_id = NULL, *yca_id = NULL;
11486 struct got_object_id *head_commit_id = NULL;
11487 struct got_reference *head_ref = NULL;
11488 struct got_commit_object *commit = NULL;
11489 int ch, rebase_in_progress = 0, abort_rebase = 0, continue_rebase = 0;
11490 int histedit_in_progress = 0, merge_in_progress = 0;
11491 int create_backup = 1, list_backups = 0, delete_backups = 0;
11492 int allow_conflict = 0;
11493 struct got_object_id_queue commits;
11494 struct got_pathlist_head merged_paths;
11495 const struct got_object_id_queue *parent_ids;
11496 struct got_object_qid *qid, *pid;
11497 struct got_update_progress_arg upa;
11498 int *pack_fds = NULL;
11500 STAILQ_INIT(&commits);
11501 TAILQ_INIT(&merged_paths);
11502 memset(&upa, 0, sizeof(upa));
11504 #ifndef PROFILE
11505 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
11506 "unveil", NULL) == -1)
11507 err(1, "pledge");
11508 #endif
11510 while ((ch = getopt(argc, argv, "aCclX")) != -1) {
11511 switch (ch) {
11512 case 'a':
11513 abort_rebase = 1;
11514 break;
11515 case 'C':
11516 allow_conflict = 1;
11517 break;
11518 case 'c':
11519 continue_rebase = 1;
11520 break;
11521 case 'l':
11522 list_backups = 1;
11523 break;
11524 case 'X':
11525 delete_backups = 1;
11526 break;
11527 default:
11528 usage_rebase();
11529 /* NOTREACHED */
11533 argc -= optind;
11534 argv += optind;
11536 if (list_backups) {
11537 if (abort_rebase)
11538 option_conflict('l', 'a');
11539 if (allow_conflict)
11540 option_conflict('l', 'C');
11541 if (continue_rebase)
11542 option_conflict('l', 'c');
11543 if (delete_backups)
11544 option_conflict('l', 'X');
11545 if (argc != 0 && argc != 1)
11546 usage_rebase();
11547 } else if (delete_backups) {
11548 if (abort_rebase)
11549 option_conflict('X', 'a');
11550 if (allow_conflict)
11551 option_conflict('X', 'C');
11552 if (continue_rebase)
11553 option_conflict('X', 'c');
11554 if (list_backups)
11555 option_conflict('l', 'X');
11556 if (argc != 0 && argc != 1)
11557 usage_rebase();
11558 } else if (allow_conflict) {
11559 if (abort_rebase)
11560 option_conflict('C', 'a');
11561 if (!continue_rebase)
11562 errx(1, "-C option requires -c");
11563 } else {
11564 if (abort_rebase && continue_rebase)
11565 usage_rebase();
11566 else if (abort_rebase || continue_rebase) {
11567 if (argc != 0)
11568 usage_rebase();
11569 } else if (argc != 1)
11570 usage_rebase();
11573 cwd = getcwd(NULL, 0);
11574 if (cwd == NULL) {
11575 error = got_error_from_errno("getcwd");
11576 goto done;
11579 error = got_repo_pack_fds_open(&pack_fds);
11580 if (error != NULL)
11581 goto done;
11583 error = got_worktree_open(&worktree, cwd, GOT_WORKTREE_GOT_DIR);
11584 if (error) {
11585 if (list_backups || delete_backups) {
11586 if (error->code != GOT_ERR_NOT_WORKTREE)
11587 goto done;
11588 } else {
11589 if (error->code == GOT_ERR_NOT_WORKTREE)
11590 error = wrap_not_worktree_error(error,
11591 "rebase", cwd);
11592 goto done;
11596 error = get_gitconfig_path(&gitconfig_path);
11597 if (error)
11598 goto done;
11599 error = got_repo_open(&repo,
11600 worktree ? got_worktree_get_repo_path(worktree) : cwd,
11601 gitconfig_path, pack_fds);
11602 if (error != NULL)
11603 goto done;
11605 if (worktree != NULL && !list_backups && !delete_backups) {
11606 error = worktree_has_logmsg_ref("rebase", worktree, repo);
11607 if (error)
11608 goto done;
11611 error = get_author(&committer, repo, worktree);
11612 if (error && error->code != GOT_ERR_COMMIT_NO_AUTHOR)
11613 goto done;
11615 error = apply_unveil(got_repo_get_path(repo), 0,
11616 worktree ? got_worktree_get_root_path(worktree) : NULL);
11617 if (error)
11618 goto done;
11620 if (list_backups || delete_backups) {
11621 error = process_backup_refs(
11622 GOT_WORKTREE_REBASE_BACKUP_REF_PREFIX,
11623 argc == 1 ? argv[0] : NULL, delete_backups, repo);
11624 goto done; /* nothing else to do */
11627 error = got_worktree_histedit_in_progress(&histedit_in_progress,
11628 worktree);
11629 if (error)
11630 goto done;
11631 if (histedit_in_progress) {
11632 error = got_error(GOT_ERR_HISTEDIT_BUSY);
11633 goto done;
11636 error = got_worktree_merge_in_progress(&merge_in_progress,
11637 worktree, repo);
11638 if (error)
11639 goto done;
11640 if (merge_in_progress) {
11641 error = got_error(GOT_ERR_MERGE_BUSY);
11642 goto done;
11645 error = got_worktree_rebase_in_progress(&rebase_in_progress, worktree);
11646 if (error)
11647 goto done;
11649 if (abort_rebase) {
11650 if (!rebase_in_progress) {
11651 error = got_error(GOT_ERR_NOT_REBASING);
11652 goto done;
11654 error = got_worktree_rebase_continue(&resume_commit_id,
11655 &new_base_branch, &tmp_branch, &branch, &fileindex,
11656 worktree, repo);
11657 if (error)
11658 goto done;
11659 printf("Switching work tree to %s\n",
11660 got_ref_get_symref_target(new_base_branch));
11661 error = got_worktree_rebase_abort(worktree, fileindex, repo,
11662 new_base_branch, abort_progress, &upa);
11663 if (error)
11664 goto done;
11665 printf("Rebase of %s aborted\n", got_ref_get_name(branch));
11666 print_merge_progress_stats(&upa);
11667 goto done; /* nothing else to do */
11670 if (continue_rebase) {
11671 if (!rebase_in_progress) {
11672 error = got_error(GOT_ERR_NOT_REBASING);
11673 goto done;
11675 error = got_worktree_rebase_continue(&resume_commit_id,
11676 &new_base_branch, &tmp_branch, &branch, &fileindex,
11677 worktree, repo);
11678 if (error)
11679 goto done;
11681 error = rebase_commit(NULL, worktree, fileindex, tmp_branch,
11682 committer, resume_commit_id, allow_conflict, repo);
11683 if (error)
11684 goto done;
11686 yca_id = got_object_id_dup(resume_commit_id);
11687 if (yca_id == NULL) {
11688 error = got_error_from_errno("got_object_id_dup");
11689 goto done;
11691 } else {
11692 error = got_ref_open(&branch, repo, argv[0], 0);
11693 if (error != NULL)
11694 goto done;
11695 if (strncmp(got_ref_get_name(branch), "refs/heads/", 11) != 0) {
11696 error = got_error_msg(GOT_ERR_COMMIT_BRANCH,
11697 "will not rebase a branch which lives outside "
11698 "the \"refs/heads/\" reference namespace");
11699 goto done;
11703 error = got_ref_resolve(&branch_head_commit_id, repo, branch);
11704 if (error)
11705 goto done;
11707 if (!continue_rebase) {
11708 struct got_object_id *base_commit_id;
11710 error = got_ref_open(&head_ref, repo,
11711 got_worktree_get_head_ref_name(worktree), 0);
11712 if (error)
11713 goto done;
11714 error = got_ref_resolve(&head_commit_id, repo, head_ref);
11715 if (error)
11716 goto done;
11717 base_commit_id = got_worktree_get_base_commit_id(worktree);
11718 if (got_object_id_cmp(base_commit_id, head_commit_id) != 0) {
11719 error = got_error(GOT_ERR_REBASE_OUT_OF_DATE);
11720 goto done;
11723 error = got_commit_graph_find_youngest_common_ancestor(&yca_id,
11724 base_commit_id, branch_head_commit_id, 1, 0,
11725 repo, check_cancelled, NULL);
11726 if (error) {
11727 if (error->code == GOT_ERR_ANCESTRY) {
11728 error = got_error_msg(GOT_ERR_ANCESTRY,
11729 "specified branch shares no common "
11730 "ancestry with work tree's branch");
11732 goto done;
11736 * If a merge commit appears between the new base branch tip
11737 * and a YCA found via first-parent traversal then we might
11738 * find a better YCA using topologically sorted commits.
11740 if (got_object_id_cmp(base_commit_id, yca_id) != 0) {
11741 struct got_object_id *better_yca_id;
11742 error = find_merge_commit_yca(&better_yca_id,
11743 branch_head_commit_id, yca_id,
11744 base_commit_id, repo);
11745 if (error)
11746 goto done;
11747 if (better_yca_id) {
11748 free(yca_id);
11749 yca_id = better_yca_id;
11753 if (got_object_id_cmp(base_commit_id, yca_id) == 0) {
11754 struct got_pathlist_head paths;
11755 const char *branch_name = got_ref_get_name(branch);
11756 const char *base =
11757 got_worktree_get_head_ref_name(worktree);
11759 if (strcmp(branch_name, base) == 0) {
11760 error = got_error_fmt(GOT_ERR_WRONG_BRANCH,
11761 "cannot rebase %s onto itself",
11762 branch_name);
11763 goto done;
11764 } else {
11765 printf("%s is already based on %s\n",
11766 branch_name, base);
11768 error = switch_head_ref(branch, branch_head_commit_id,
11769 worktree, repo);
11770 if (error)
11771 goto done;
11772 error = got_worktree_set_base_commit_id(worktree, repo,
11773 branch_head_commit_id);
11774 if (error)
11775 goto done;
11776 TAILQ_INIT(&paths);
11777 error = got_pathlist_insert(NULL, &paths, "", NULL);
11778 if (error)
11779 goto done;
11780 error = got_worktree_checkout_files(worktree,
11781 &paths, repo, update_progress, &upa,
11782 check_cancelled, NULL);
11783 got_pathlist_free(&paths, GOT_PATHLIST_FREE_NONE);
11784 if (error)
11785 goto done;
11786 if (upa.did_something) {
11787 char *id_str;
11788 error = got_object_id_str(&id_str,
11789 branch_head_commit_id);
11790 if (error)
11791 goto done;
11792 printf("Updated to %s: %s\n",
11793 got_worktree_get_head_ref_name(worktree),
11794 id_str);
11795 free(id_str);
11796 } else
11797 printf("Already up-to-date\n");
11798 print_update_progress_stats(&upa);
11799 goto done;
11803 commit_id = branch_head_commit_id;
11804 error = got_object_open_as_commit(&commit, repo, commit_id);
11805 if (error)
11806 goto done;
11808 parent_ids = got_object_commit_get_parent_ids(commit);
11809 pid = STAILQ_FIRST(parent_ids);
11810 if (pid) {
11811 error = collect_commits(&commits, commit_id, &pid->id,
11812 yca_id, got_worktree_get_path_prefix(worktree),
11813 GOT_ERR_REBASE_PATH, repo);
11814 if (error)
11815 goto done;
11818 got_object_commit_close(commit);
11819 commit = NULL;
11821 if (!continue_rebase) {
11822 error = got_worktree_rebase_prepare(&new_base_branch,
11823 &tmp_branch, &fileindex, worktree, branch, repo);
11824 if (error)
11825 goto done;
11828 if (STAILQ_EMPTY(&commits)) {
11829 if (continue_rebase) {
11830 error = rebase_complete(worktree, fileindex,
11831 branch, tmp_branch, repo, create_backup);
11832 goto done;
11833 } else {
11834 /* Fast-forward the reference of the branch. */
11835 struct got_object_id *new_head_commit_id;
11836 char *id_str;
11837 error = got_ref_resolve(&new_head_commit_id, repo,
11838 new_base_branch);
11839 if (error)
11840 goto done;
11841 error = got_object_id_str(&id_str, new_head_commit_id);
11842 if (error)
11843 goto done;
11844 printf("Forwarding %s to commit %s\n",
11845 got_ref_get_name(branch), id_str);
11846 free(id_str);
11847 error = got_ref_change_ref(branch,
11848 new_head_commit_id);
11849 if (error)
11850 goto done;
11851 /* No backup needed since objects did not change. */
11852 create_backup = 0;
11856 pid = NULL;
11857 STAILQ_FOREACH(qid, &commits, entry) {
11859 commit_id = &qid->id;
11860 parent_id = pid ? &pid->id : yca_id;
11861 pid = qid;
11863 memset(&upa, 0, sizeof(upa));
11864 error = got_worktree_rebase_merge_files(&merged_paths,
11865 worktree, fileindex, parent_id, commit_id, repo,
11866 update_progress, &upa, check_cancelled, NULL);
11867 if (error)
11868 goto done;
11870 print_merge_progress_stats(&upa);
11871 if (upa.conflicts > 0 || upa.missing > 0 ||
11872 upa.not_deleted > 0 || upa.unversioned > 0) {
11873 if (upa.conflicts > 0) {
11874 error = show_rebase_merge_conflict(&qid->id,
11875 repo);
11876 if (error)
11877 goto done;
11879 got_pathlist_free(&merged_paths, GOT_PATHLIST_FREE_PATH);
11880 break;
11883 error = rebase_commit(&merged_paths, worktree, fileindex,
11884 tmp_branch, committer, commit_id, 0, repo);
11885 got_pathlist_free(&merged_paths, GOT_PATHLIST_FREE_PATH);
11886 if (error)
11887 goto done;
11890 if (upa.conflicts > 0 || upa.missing > 0 ||
11891 upa.not_deleted > 0 || upa.unversioned > 0) {
11892 error = got_worktree_rebase_postpone(worktree, fileindex);
11893 if (error)
11894 goto done;
11895 if (upa.conflicts > 0 && upa.missing == 0 &&
11896 upa.not_deleted == 0 && upa.unversioned == 0) {
11897 error = got_error_msg(GOT_ERR_CONFLICTS,
11898 "conflicts must be resolved before rebasing "
11899 "can continue");
11900 } else if (upa.conflicts > 0) {
11901 error = got_error_msg(GOT_ERR_CONFLICTS,
11902 "conflicts must be resolved before rebasing "
11903 "can continue; changes destined for some "
11904 "files were not yet merged and should be "
11905 "merged manually if required before the "
11906 "rebase operation is continued");
11907 } else {
11908 error = got_error_msg(GOT_ERR_CONFLICTS,
11909 "changes destined for some files were not "
11910 "yet merged and should be merged manually "
11911 "if required before the rebase operation "
11912 "is continued");
11914 } else
11915 error = rebase_complete(worktree, fileindex, branch,
11916 tmp_branch, repo, create_backup);
11917 done:
11918 free(cwd);
11919 free(committer);
11920 free(gitconfig_path);
11921 got_object_id_queue_free(&commits);
11922 free(branch_head_commit_id);
11923 free(resume_commit_id);
11924 free(head_commit_id);
11925 free(yca_id);
11926 if (commit)
11927 got_object_commit_close(commit);
11928 if (branch)
11929 got_ref_close(branch);
11930 if (new_base_branch)
11931 got_ref_close(new_base_branch);
11932 if (tmp_branch)
11933 got_ref_close(tmp_branch);
11934 if (head_ref)
11935 got_ref_close(head_ref);
11936 if (worktree)
11937 got_worktree_close(worktree);
11938 if (repo) {
11939 const struct got_error *close_err = got_repo_close(repo);
11940 if (error == NULL)
11941 error = close_err;
11943 if (pack_fds) {
11944 const struct got_error *pack_err =
11945 got_repo_pack_fds_close(pack_fds);
11946 if (error == NULL)
11947 error = pack_err;
11949 return error;
11952 __dead static void
11953 usage_histedit(void)
11955 fprintf(stderr, "usage: %s histedit [-aCcdeflmX] [-F histedit-script] "
11956 "[branch]\n", getprogname());
11957 exit(1);
11960 #define GOT_HISTEDIT_PICK 'p'
11961 #define GOT_HISTEDIT_EDIT 'e'
11962 #define GOT_HISTEDIT_FOLD 'f'
11963 #define GOT_HISTEDIT_DROP 'd'
11964 #define GOT_HISTEDIT_MESG 'm'
11966 static const struct got_histedit_cmd {
11967 unsigned char code;
11968 const char *name;
11969 const char *desc;
11970 } got_histedit_cmds[] = {
11971 { GOT_HISTEDIT_PICK, "pick", "use commit" },
11972 { GOT_HISTEDIT_EDIT, "edit", "use commit but stop for amending" },
11973 { GOT_HISTEDIT_FOLD, "fold", "combine with next commit that will "
11974 "be used" },
11975 { GOT_HISTEDIT_DROP, "drop", "remove commit from history" },
11976 { GOT_HISTEDIT_MESG, "mesg", "open editor to edit the log message" },
11979 struct got_histedit_list_entry {
11980 TAILQ_ENTRY(got_histedit_list_entry) entry;
11981 struct got_object_id *commit_id;
11982 const struct got_histedit_cmd *cmd;
11983 char *logmsg;
11985 TAILQ_HEAD(got_histedit_list, got_histedit_list_entry);
11987 static const struct got_error *
11988 histedit_write_commit(struct got_object_id *commit_id, const char *cmdname,
11989 FILE *f, struct got_repository *repo)
11991 const struct got_error *err = NULL;
11992 char *logmsg = NULL, *id_str = NULL;
11993 struct got_commit_object *commit = NULL;
11994 int n;
11996 err = got_object_open_as_commit(&commit, repo, commit_id);
11997 if (err)
11998 goto done;
12000 err = get_short_logmsg(&logmsg, 34, commit);
12001 if (err)
12002 goto done;
12004 err = got_object_id_str(&id_str, commit_id);
12005 if (err)
12006 goto done;
12008 n = fprintf(f, "%s %s %s\n", cmdname, id_str, logmsg);
12009 if (n < 0)
12010 err = got_ferror(f, GOT_ERR_IO);
12011 done:
12012 if (commit)
12013 got_object_commit_close(commit);
12014 free(id_str);
12015 free(logmsg);
12016 return err;
12019 static const struct got_error *
12020 histedit_write_commit_list(struct got_object_id_queue *commits,
12021 FILE *f, int edit_logmsg_only, int fold_only, int drop_only,
12022 int edit_only, struct got_repository *repo)
12024 const struct got_error *err = NULL;
12025 struct got_object_qid *qid;
12026 const char *histedit_cmd = NULL;
12028 if (STAILQ_EMPTY(commits))
12029 return got_error(GOT_ERR_EMPTY_HISTEDIT);
12031 STAILQ_FOREACH(qid, commits, entry) {
12032 histedit_cmd = got_histedit_cmds[0].name;
12033 if (drop_only)
12034 histedit_cmd = "drop";
12035 else if (edit_only)
12036 histedit_cmd = "edit";
12037 else if (fold_only && STAILQ_NEXT(qid, entry) != NULL)
12038 histedit_cmd = "fold";
12039 else if (edit_logmsg_only)
12040 histedit_cmd = "mesg";
12041 err = histedit_write_commit(&qid->id, histedit_cmd, f, repo);
12042 if (err)
12043 break;
12046 return err;
12049 static const struct got_error *
12050 write_cmd_list(FILE *f, const char *branch_name,
12051 struct got_object_id_queue *commits)
12053 const struct got_error *err = NULL;
12054 size_t i;
12055 int n;
12056 char *id_str;
12057 struct got_object_qid *qid;
12059 qid = STAILQ_FIRST(commits);
12060 err = got_object_id_str(&id_str, &qid->id);
12061 if (err)
12062 return err;
12064 n = fprintf(f,
12065 "# Editing the history of branch '%s' starting at\n"
12066 "# commit %s\n"
12067 "# Commits will be processed in order from top to "
12068 "bottom of this file.\n", branch_name, id_str);
12069 if (n < 0) {
12070 err = got_ferror(f, GOT_ERR_IO);
12071 goto done;
12074 n = fprintf(f, "# Available histedit commands:\n");
12075 if (n < 0) {
12076 err = got_ferror(f, GOT_ERR_IO);
12077 goto done;
12080 for (i = 0; i < nitems(got_histedit_cmds); i++) {
12081 const struct got_histedit_cmd *cmd = &got_histedit_cmds[i];
12082 n = fprintf(f, "# %s (%c): %s\n", cmd->name, cmd->code,
12083 cmd->desc);
12084 if (n < 0) {
12085 err = got_ferror(f, GOT_ERR_IO);
12086 break;
12089 done:
12090 free(id_str);
12091 return err;
12094 static const struct got_error *
12095 histedit_syntax_error(int lineno)
12097 static char msg[42];
12098 int ret;
12100 ret = snprintf(msg, sizeof(msg), "histedit syntax error on line %d",
12101 lineno);
12102 if (ret < 0 || (size_t)ret >= sizeof(msg))
12103 return got_error(GOT_ERR_HISTEDIT_SYNTAX);
12105 return got_error_msg(GOT_ERR_HISTEDIT_SYNTAX, msg);
12108 static const struct got_error *
12109 append_folded_commit_msg(char **new_msg, struct got_histedit_list_entry *hle,
12110 char *logmsg, struct got_repository *repo)
12112 const struct got_error *err;
12113 struct got_commit_object *folded_commit = NULL;
12114 char *id_str, *folded_logmsg = NULL;
12116 err = got_object_id_str(&id_str, hle->commit_id);
12117 if (err)
12118 return err;
12120 err = got_object_open_as_commit(&folded_commit, repo, hle->commit_id);
12121 if (err)
12122 goto done;
12124 err = got_object_commit_get_logmsg(&folded_logmsg, folded_commit);
12125 if (err)
12126 goto done;
12127 if (asprintf(new_msg, "%s%s# log message of folded commit %s: %s",
12128 logmsg ? logmsg : "", logmsg ? "\n" : "", id_str,
12129 folded_logmsg) == -1) {
12130 err = got_error_from_errno("asprintf");
12132 done:
12133 if (folded_commit)
12134 got_object_commit_close(folded_commit);
12135 free(id_str);
12136 free(folded_logmsg);
12137 return err;
12140 static struct got_histedit_list_entry *
12141 get_folded_commits(struct got_histedit_list_entry *hle)
12143 struct got_histedit_list_entry *prev, *folded = NULL;
12145 prev = TAILQ_PREV(hle, got_histedit_list, entry);
12146 while (prev && (prev->cmd->code == GOT_HISTEDIT_FOLD ||
12147 prev->cmd->code == GOT_HISTEDIT_DROP)) {
12148 if (prev->cmd->code == GOT_HISTEDIT_FOLD)
12149 folded = prev;
12150 prev = TAILQ_PREV(prev, got_histedit_list, entry);
12153 return folded;
12156 static const struct got_error *
12157 histedit_edit_logmsg(struct got_histedit_list_entry *hle,
12158 const char *editor, struct got_repository *repo)
12160 char *logmsg_path = NULL, *id_str = NULL, *orig_logmsg = NULL;
12161 char *logmsg = NULL, *new_msg = NULL;
12162 const struct got_error *err = NULL;
12163 struct got_commit_object *commit = NULL;
12164 int logmsg_len;
12165 int fd = -1;
12166 struct got_histedit_list_entry *folded = NULL;
12168 err = got_object_open_as_commit(&commit, repo, hle->commit_id);
12169 if (err)
12170 return err;
12172 folded = get_folded_commits(hle);
12173 if (folded) {
12174 while (folded != hle) {
12175 if (folded->cmd->code == GOT_HISTEDIT_DROP) {
12176 folded = TAILQ_NEXT(folded, entry);
12177 continue;
12179 err = append_folded_commit_msg(&new_msg, folded,
12180 logmsg, repo);
12181 if (err)
12182 goto done;
12183 free(logmsg);
12184 logmsg = new_msg;
12185 folded = TAILQ_NEXT(folded, entry);
12189 err = got_object_id_str(&id_str, hle->commit_id);
12190 if (err)
12191 goto done;
12192 err = got_object_commit_get_logmsg(&orig_logmsg, commit);
12193 if (err)
12194 goto done;
12195 logmsg_len = asprintf(&new_msg,
12196 "%s\n# original log message of commit %s: %s",
12197 logmsg ? logmsg : "", id_str, orig_logmsg);
12198 if (logmsg_len == -1) {
12199 err = got_error_from_errno("asprintf");
12200 goto done;
12202 free(logmsg);
12203 logmsg = new_msg;
12205 err = got_object_id_str(&id_str, hle->commit_id);
12206 if (err)
12207 goto done;
12209 err = got_opentemp_named_fd(&logmsg_path, &fd,
12210 GOT_TMPDIR_STR "/got-logmsg", "");
12211 if (err)
12212 goto done;
12214 if (write(fd, logmsg, logmsg_len) == -1) {
12215 err = got_error_from_errno2("write", logmsg_path);
12216 goto done;
12218 if (close(fd) == -1) {
12219 err = got_error_from_errno2("close", logmsg_path);
12220 goto done;
12222 fd = -1;
12224 err = edit_logmsg(&hle->logmsg, editor, logmsg_path, logmsg,
12225 logmsg_len, 0);
12226 if (err) {
12227 if (err->code != GOT_ERR_COMMIT_MSG_EMPTY)
12228 goto done;
12229 err = NULL;
12230 hle->logmsg = strdup(new_msg);
12231 if (hle->logmsg == NULL)
12232 err = got_error_from_errno("strdup");
12234 done:
12235 if (fd != -1 && close(fd) == -1 && err == NULL)
12236 err = got_error_from_errno2("close", logmsg_path);
12237 if (logmsg_path && unlink(logmsg_path) != 0 && err == NULL)
12238 err = got_error_from_errno2("unlink", logmsg_path);
12239 free(logmsg_path);
12240 free(logmsg);
12241 free(orig_logmsg);
12242 if (commit)
12243 got_object_commit_close(commit);
12244 return err;
12247 static const struct got_error *
12248 histedit_parse_list(struct got_histedit_list *histedit_cmds,
12249 FILE *f, struct got_repository *repo)
12251 const struct got_error *err = NULL;
12252 char *line = NULL, *p, *end;
12253 size_t i, linesize = 0;
12254 ssize_t linelen;
12255 int lineno = 0;
12256 const struct got_histedit_cmd *cmd;
12257 struct got_object_id *commit_id = NULL;
12258 struct got_histedit_list_entry *hle = NULL;
12260 for (;;) {
12261 linelen = getline(&line, &linesize, f);
12262 if (linelen == -1) {
12263 const struct got_error *getline_err;
12264 if (feof(f))
12265 break;
12266 getline_err = got_error_from_errno("getline");
12267 err = got_ferror(f, getline_err->code);
12268 break;
12270 lineno++;
12271 p = line;
12272 while (isspace((unsigned char)p[0]))
12273 p++;
12274 if (p[0] == '#' || p[0] == '\0')
12275 continue;
12276 cmd = NULL;
12277 for (i = 0; i < nitems(got_histedit_cmds); i++) {
12278 cmd = &got_histedit_cmds[i];
12279 if (strncmp(cmd->name, p, strlen(cmd->name)) == 0 &&
12280 isspace((unsigned char)p[strlen(cmd->name)])) {
12281 p += strlen(cmd->name);
12282 break;
12284 if (p[0] == cmd->code && isspace((unsigned char)p[1])) {
12285 p++;
12286 break;
12289 if (i == nitems(got_histedit_cmds)) {
12290 err = histedit_syntax_error(lineno);
12291 break;
12293 while (isspace((unsigned char)p[0]))
12294 p++;
12295 end = p;
12296 while (end[0] && !isspace((unsigned char)end[0]))
12297 end++;
12298 *end = '\0';
12299 err = got_object_resolve_id_str(&commit_id, repo, p);
12300 if (err) {
12301 /* override error code */
12302 err = histedit_syntax_error(lineno);
12303 break;
12305 hle = malloc(sizeof(*hle));
12306 if (hle == NULL) {
12307 err = got_error_from_errno("malloc");
12308 break;
12310 hle->cmd = cmd;
12311 hle->commit_id = commit_id;
12312 hle->logmsg = NULL;
12313 commit_id = NULL;
12314 TAILQ_INSERT_TAIL(histedit_cmds, hle, entry);
12317 free(line);
12318 free(commit_id);
12319 return err;
12322 static const struct got_error *
12323 histedit_check_script(struct got_histedit_list *histedit_cmds,
12324 struct got_object_id_queue *commits, struct got_repository *repo)
12326 const struct got_error *err = NULL;
12327 struct got_object_qid *qid;
12328 struct got_histedit_list_entry *hle;
12329 static char msg[128];
12330 char *id_str;
12332 if (TAILQ_EMPTY(histedit_cmds))
12333 return got_error_msg(GOT_ERR_EMPTY_HISTEDIT,
12334 "histedit script contains no commands");
12335 if (STAILQ_EMPTY(commits))
12336 return got_error(GOT_ERR_EMPTY_HISTEDIT);
12338 TAILQ_FOREACH(hle, histedit_cmds, entry) {
12339 struct got_histedit_list_entry *hle2;
12340 TAILQ_FOREACH(hle2, histedit_cmds, entry) {
12341 if (hle == hle2)
12342 continue;
12343 if (got_object_id_cmp(hle->commit_id,
12344 hle2->commit_id) != 0)
12345 continue;
12346 err = got_object_id_str(&id_str, hle->commit_id);
12347 if (err)
12348 return err;
12349 snprintf(msg, sizeof(msg), "commit %s is listed "
12350 "more than once in histedit script", id_str);
12351 free(id_str);
12352 return got_error_msg(GOT_ERR_HISTEDIT_CMD, msg);
12356 STAILQ_FOREACH(qid, commits, entry) {
12357 TAILQ_FOREACH(hle, histedit_cmds, entry) {
12358 if (got_object_id_cmp(&qid->id, hle->commit_id) == 0)
12359 break;
12361 if (hle == NULL) {
12362 err = got_object_id_str(&id_str, &qid->id);
12363 if (err)
12364 return err;
12365 snprintf(msg, sizeof(msg),
12366 "commit %s missing from histedit script", id_str);
12367 free(id_str);
12368 return got_error_msg(GOT_ERR_HISTEDIT_CMD, msg);
12372 hle = TAILQ_LAST(histedit_cmds, got_histedit_list);
12373 if (hle && hle->cmd->code == GOT_HISTEDIT_FOLD)
12374 return got_error_msg(GOT_ERR_HISTEDIT_CMD,
12375 "last commit in histedit script cannot be folded");
12377 return NULL;
12380 static const struct got_error *
12381 histedit_run_editor(struct got_histedit_list *histedit_cmds,
12382 const char *editor, const char *path,
12383 struct got_object_id_queue *commits, struct got_repository *repo)
12385 const struct got_error *err = NULL;
12386 struct stat st, st2;
12387 struct timespec timeout;
12388 FILE *f = NULL;
12390 if (stat(path, &st) == -1) {
12391 err = got_error_from_errno2("stat", path);
12392 goto done;
12395 if (spawn_editor(editor, path) == -1) {
12396 err = got_error_from_errno("failed spawning editor");
12397 goto done;
12400 timeout.tv_sec = 0;
12401 timeout.tv_nsec = 1;
12402 nanosleep(&timeout, NULL);
12404 if (stat(path, &st2) == -1) {
12405 err = got_error_from_errno2("stat", path);
12406 goto done;
12409 if (st.st_size == st2.st_size &&
12410 timespeccmp(&st.st_mtim, &st2.st_mtim, ==)) {
12411 err = got_error_msg(GOT_ERR_EMPTY_HISTEDIT,
12412 "no changes made to histedit script, aborting");
12413 goto done;
12416 f = fopen(path, "re");
12417 if (f == NULL) {
12418 err = got_error_from_errno("fopen");
12419 goto done;
12421 err = histedit_parse_list(histedit_cmds, f, repo);
12422 if (err)
12423 goto done;
12425 err = histedit_check_script(histedit_cmds, commits, repo);
12426 done:
12427 if (f && fclose(f) == EOF && err == NULL)
12428 err = got_error_from_errno("fclose");
12429 return err;
12432 static const struct got_error *
12433 histedit_edit_list_retry(struct got_histedit_list *, const struct got_error *,
12434 struct got_object_id_queue *, const char *, const char *, const char *,
12435 struct got_repository *);
12437 static const struct got_error *
12438 histedit_edit_script(struct got_histedit_list *histedit_cmds,
12439 struct got_object_id_queue *commits, const char *branch_name,
12440 int edit_logmsg_only, int fold_only, int drop_only, int edit_only,
12441 const char *editor, struct got_repository *repo)
12443 const struct got_error *err;
12444 FILE *f = NULL;
12445 char *path = NULL;
12447 err = got_opentemp_named(&path, &f, "got-histedit", "");
12448 if (err)
12449 return err;
12451 err = write_cmd_list(f, branch_name, commits);
12452 if (err)
12453 goto done;
12455 err = histedit_write_commit_list(commits, f, edit_logmsg_only,
12456 fold_only, drop_only, edit_only, repo);
12457 if (err)
12458 goto done;
12460 if (drop_only || edit_logmsg_only || fold_only || edit_only) {
12461 rewind(f);
12462 err = histedit_parse_list(histedit_cmds, f, repo);
12463 } else {
12464 if (fclose(f) == EOF) {
12465 err = got_error_from_errno("fclose");
12466 goto done;
12468 f = NULL;
12469 err = histedit_run_editor(histedit_cmds, editor, path,
12470 commits, repo);
12471 if (err) {
12472 if (err->code != GOT_ERR_HISTEDIT_SYNTAX &&
12473 err->code != GOT_ERR_HISTEDIT_CMD)
12474 goto done;
12475 err = histedit_edit_list_retry(histedit_cmds, err,
12476 commits, editor, path, branch_name, repo);
12479 done:
12480 if (f && fclose(f) == EOF && err == NULL)
12481 err = got_error_from_errno("fclose");
12482 if (path && unlink(path) != 0 && err == NULL)
12483 err = got_error_from_errno2("unlink", path);
12484 free(path);
12485 return err;
12488 static const struct got_error *
12489 histedit_save_list(struct got_histedit_list *histedit_cmds,
12490 struct got_worktree *worktree, struct got_repository *repo)
12492 const struct got_error *err = NULL;
12493 char *path = NULL;
12494 FILE *f = NULL;
12495 struct got_histedit_list_entry *hle;
12497 err = got_worktree_get_histedit_script_path(&path, worktree);
12498 if (err)
12499 return err;
12501 f = fopen(path, "we");
12502 if (f == NULL) {
12503 err = got_error_from_errno2("fopen", path);
12504 goto done;
12506 TAILQ_FOREACH(hle, histedit_cmds, entry) {
12507 err = histedit_write_commit(hle->commit_id, hle->cmd->name, f,
12508 repo);
12509 if (err)
12510 break;
12512 done:
12513 if (f && fclose(f) == EOF && err == NULL)
12514 err = got_error_from_errno("fclose");
12515 free(path);
12516 return err;
12519 static void
12520 histedit_free_list(struct got_histedit_list *histedit_cmds)
12522 struct got_histedit_list_entry *hle;
12524 while ((hle = TAILQ_FIRST(histedit_cmds))) {
12525 TAILQ_REMOVE(histedit_cmds, hle, entry);
12526 free(hle);
12530 static const struct got_error *
12531 histedit_load_list(struct got_histedit_list *histedit_cmds,
12532 const char *path, struct got_repository *repo)
12534 const struct got_error *err = NULL;
12535 FILE *f = NULL;
12537 f = fopen(path, "re");
12538 if (f == NULL) {
12539 err = got_error_from_errno2("fopen", path);
12540 goto done;
12543 err = histedit_parse_list(histedit_cmds, f, repo);
12544 done:
12545 if (f && fclose(f) == EOF && err == NULL)
12546 err = got_error_from_errno("fclose");
12547 return err;
12550 static const struct got_error *
12551 histedit_edit_list_retry(struct got_histedit_list *histedit_cmds,
12552 const struct got_error *edit_err, struct got_object_id_queue *commits,
12553 const char *editor, const char *path, const char *branch_name,
12554 struct got_repository *repo)
12556 const struct got_error *err = NULL, *prev_err = edit_err;
12557 int resp = ' ';
12559 while (resp != 'c' && resp != 'r' && resp != 'a') {
12560 printf("%s: %s\n(c)ontinue editing, (r)estart editing, "
12561 "or (a)bort: ", getprogname(), prev_err->msg);
12562 resp = getchar();
12563 if (resp == '\n')
12564 resp = getchar();
12565 if (resp == 'c') {
12566 histedit_free_list(histedit_cmds);
12567 err = histedit_run_editor(histedit_cmds, editor, path,
12568 commits, repo);
12569 if (err) {
12570 if (err->code != GOT_ERR_HISTEDIT_SYNTAX &&
12571 err->code != GOT_ERR_HISTEDIT_CMD)
12572 break;
12573 prev_err = err;
12574 resp = ' ';
12575 continue;
12577 break;
12578 } else if (resp == 'r') {
12579 histedit_free_list(histedit_cmds);
12580 err = histedit_edit_script(histedit_cmds,
12581 commits, branch_name, 0, 0, 0, 0, editor, repo);
12582 if (err) {
12583 if (err->code != GOT_ERR_HISTEDIT_SYNTAX &&
12584 err->code != GOT_ERR_HISTEDIT_CMD)
12585 break;
12586 prev_err = err;
12587 resp = ' ';
12588 continue;
12590 break;
12591 } else if (resp == 'a') {
12592 err = got_error(GOT_ERR_HISTEDIT_CANCEL);
12593 break;
12594 } else
12595 printf("invalid response '%c'\n", resp);
12598 return err;
12601 static const struct got_error *
12602 histedit_complete(struct got_worktree *worktree,
12603 struct got_fileindex *fileindex, struct got_reference *tmp_branch,
12604 struct got_reference *branch, struct got_repository *repo)
12606 printf("Switching work tree to %s\n",
12607 got_ref_get_symref_target(branch));
12608 return got_worktree_histedit_complete(worktree, fileindex, tmp_branch,
12609 branch, repo);
12612 static const struct got_error *
12613 show_histedit_progress(struct got_commit_object *commit,
12614 struct got_histedit_list_entry *hle, struct got_object_id *new_id)
12616 const struct got_error *err;
12617 char *old_id_str = NULL, *new_id_str = NULL, *logmsg = NULL;
12619 err = got_object_id_str(&old_id_str, hle->commit_id);
12620 if (err)
12621 goto done;
12623 if (new_id) {
12624 err = got_object_id_str(&new_id_str, new_id);
12625 if (err)
12626 goto done;
12629 old_id_str[12] = '\0';
12630 if (new_id_str)
12631 new_id_str[12] = '\0';
12633 if (hle->logmsg) {
12634 logmsg = strdup(hle->logmsg);
12635 if (logmsg == NULL) {
12636 err = got_error_from_errno("strdup");
12637 goto done;
12639 trim_logmsg(logmsg, 42);
12640 } else {
12641 err = get_short_logmsg(&logmsg, 42, commit);
12642 if (err)
12643 goto done;
12646 switch (hle->cmd->code) {
12647 case GOT_HISTEDIT_PICK:
12648 case GOT_HISTEDIT_EDIT:
12649 case GOT_HISTEDIT_MESG:
12650 printf("%s -> %s: %s\n", old_id_str,
12651 new_id_str ? new_id_str : "no-op change", logmsg);
12652 break;
12653 case GOT_HISTEDIT_DROP:
12654 case GOT_HISTEDIT_FOLD:
12655 printf("%s -> %s commit: %s\n", old_id_str, hle->cmd->name,
12656 logmsg);
12657 break;
12658 default:
12659 break;
12661 done:
12662 free(old_id_str);
12663 free(new_id_str);
12664 return err;
12667 static const struct got_error *
12668 histedit_commit(struct got_pathlist_head *merged_paths,
12669 struct got_worktree *worktree, struct got_fileindex *fileindex,
12670 struct got_reference *tmp_branch, struct got_histedit_list_entry *hle,
12671 const char *committer, int allow_conflict, const char *editor,
12672 struct got_repository *repo)
12674 const struct got_error *err;
12675 struct got_commit_object *commit;
12676 struct got_object_id *new_commit_id;
12678 if ((hle->cmd->code == GOT_HISTEDIT_EDIT || get_folded_commits(hle))
12679 && hle->logmsg == NULL) {
12680 err = histedit_edit_logmsg(hle, editor, repo);
12681 if (err)
12682 return err;
12685 err = got_object_open_as_commit(&commit, repo, hle->commit_id);
12686 if (err)
12687 return err;
12689 err = got_worktree_histedit_commit(&new_commit_id, merged_paths,
12690 worktree, fileindex, tmp_branch, committer, commit, hle->commit_id,
12691 hle->logmsg, allow_conflict, repo);
12692 if (err) {
12693 if (err->code != GOT_ERR_COMMIT_NO_CHANGES)
12694 goto done;
12695 err = show_histedit_progress(commit, hle, NULL);
12696 } else {
12697 err = show_histedit_progress(commit, hle, new_commit_id);
12698 free(new_commit_id);
12700 done:
12701 got_object_commit_close(commit);
12702 return err;
12705 static const struct got_error *
12706 histedit_skip_commit(struct got_histedit_list_entry *hle,
12707 struct got_worktree *worktree, struct got_repository *repo)
12709 const struct got_error *error;
12710 struct got_commit_object *commit;
12712 error = got_worktree_histedit_skip_commit(worktree, hle->commit_id,
12713 repo);
12714 if (error)
12715 return error;
12717 error = got_object_open_as_commit(&commit, repo, hle->commit_id);
12718 if (error)
12719 return error;
12721 error = show_histedit_progress(commit, hle, NULL);
12722 got_object_commit_close(commit);
12723 return error;
12726 static const struct got_error *
12727 check_local_changes(void *arg, unsigned char status,
12728 unsigned char staged_status, const char *path,
12729 struct got_object_id *blob_id, struct got_object_id *staged_blob_id,
12730 struct got_object_id *commit_id, int dirfd, const char *de_name)
12732 int *have_local_changes = arg;
12734 switch (status) {
12735 case GOT_STATUS_ADD:
12736 case GOT_STATUS_DELETE:
12737 case GOT_STATUS_MODIFY:
12738 case GOT_STATUS_CONFLICT:
12739 *have_local_changes = 1;
12740 return got_error(GOT_ERR_CANCELLED);
12741 default:
12742 break;
12745 switch (staged_status) {
12746 case GOT_STATUS_ADD:
12747 case GOT_STATUS_DELETE:
12748 case GOT_STATUS_MODIFY:
12749 *have_local_changes = 1;
12750 return got_error(GOT_ERR_CANCELLED);
12751 default:
12752 break;
12755 return NULL;
12758 static const struct got_error *
12759 cmd_histedit(int argc, char *argv[])
12761 const struct got_error *error = NULL;
12762 struct got_worktree *worktree = NULL;
12763 struct got_fileindex *fileindex = NULL;
12764 struct got_repository *repo = NULL;
12765 char *cwd = NULL, *committer = NULL, *gitconfig_path = NULL;
12766 struct got_reference *branch = NULL;
12767 struct got_reference *tmp_branch = NULL;
12768 struct got_object_id *resume_commit_id = NULL;
12769 struct got_object_id *base_commit_id = NULL;
12770 struct got_object_id *head_commit_id = NULL;
12771 struct got_commit_object *commit = NULL;
12772 int ch, rebase_in_progress = 0, merge_in_progress = 0;
12773 struct got_update_progress_arg upa;
12774 int edit_in_progress = 0, abort_edit = 0, continue_edit = 0;
12775 int drop_only = 0, edit_logmsg_only = 0, fold_only = 0, edit_only = 0;
12776 int allow_conflict = 0, list_backups = 0, delete_backups = 0;
12777 const char *edit_script_path = NULL;
12778 char *editor = NULL;
12779 struct got_object_id_queue commits;
12780 struct got_pathlist_head merged_paths;
12781 const struct got_object_id_queue *parent_ids;
12782 struct got_object_qid *pid;
12783 struct got_histedit_list histedit_cmds;
12784 struct got_histedit_list_entry *hle;
12785 int *pack_fds = NULL;
12787 STAILQ_INIT(&commits);
12788 TAILQ_INIT(&histedit_cmds);
12789 TAILQ_INIT(&merged_paths);
12790 memset(&upa, 0, sizeof(upa));
12792 #ifndef PROFILE
12793 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
12794 "unveil", NULL) == -1)
12795 err(1, "pledge");
12796 #endif
12798 while ((ch = getopt(argc, argv, "aCcdeF:flmX")) != -1) {
12799 switch (ch) {
12800 case 'a':
12801 abort_edit = 1;
12802 break;
12803 case 'C':
12804 allow_conflict = 1;
12805 break;
12806 case 'c':
12807 continue_edit = 1;
12808 break;
12809 case 'd':
12810 drop_only = 1;
12811 break;
12812 case 'e':
12813 edit_only = 1;
12814 break;
12815 case 'F':
12816 edit_script_path = optarg;
12817 break;
12818 case 'f':
12819 fold_only = 1;
12820 break;
12821 case 'l':
12822 list_backups = 1;
12823 break;
12824 case 'm':
12825 edit_logmsg_only = 1;
12826 break;
12827 case 'X':
12828 delete_backups = 1;
12829 break;
12830 default:
12831 usage_histedit();
12832 /* NOTREACHED */
12836 argc -= optind;
12837 argv += optind;
12839 if (abort_edit && allow_conflict)
12840 option_conflict('a', 'C');
12841 if (abort_edit && continue_edit)
12842 option_conflict('a', 'c');
12843 if (edit_script_path && allow_conflict)
12844 option_conflict('F', 'C');
12845 if (edit_script_path && edit_logmsg_only)
12846 option_conflict('F', 'm');
12847 if (abort_edit && edit_logmsg_only)
12848 option_conflict('a', 'm');
12849 if (edit_logmsg_only && allow_conflict)
12850 option_conflict('m', 'C');
12851 if (continue_edit && edit_logmsg_only)
12852 option_conflict('c', 'm');
12853 if (abort_edit && fold_only)
12854 option_conflict('a', 'f');
12855 if (fold_only && allow_conflict)
12856 option_conflict('f', 'C');
12857 if (continue_edit && fold_only)
12858 option_conflict('c', 'f');
12859 if (fold_only && edit_logmsg_only)
12860 option_conflict('f', 'm');
12861 if (edit_script_path && fold_only)
12862 option_conflict('F', 'f');
12863 if (abort_edit && edit_only)
12864 option_conflict('a', 'e');
12865 if (continue_edit && edit_only)
12866 option_conflict('c', 'e');
12867 if (edit_only && edit_logmsg_only)
12868 option_conflict('e', 'm');
12869 if (edit_script_path && edit_only)
12870 option_conflict('F', 'e');
12871 if (fold_only && edit_only)
12872 option_conflict('f', 'e');
12873 if (drop_only && abort_edit)
12874 option_conflict('d', 'a');
12875 if (drop_only && allow_conflict)
12876 option_conflict('d', 'C');
12877 if (drop_only && continue_edit)
12878 option_conflict('d', 'c');
12879 if (drop_only && edit_logmsg_only)
12880 option_conflict('d', 'm');
12881 if (drop_only && edit_only)
12882 option_conflict('d', 'e');
12883 if (drop_only && edit_script_path)
12884 option_conflict('d', 'F');
12885 if (drop_only && fold_only)
12886 option_conflict('d', 'f');
12887 if (list_backups) {
12888 if (abort_edit)
12889 option_conflict('l', 'a');
12890 if (allow_conflict)
12891 option_conflict('l', 'C');
12892 if (continue_edit)
12893 option_conflict('l', 'c');
12894 if (edit_script_path)
12895 option_conflict('l', 'F');
12896 if (edit_logmsg_only)
12897 option_conflict('l', 'm');
12898 if (drop_only)
12899 option_conflict('l', 'd');
12900 if (fold_only)
12901 option_conflict('l', 'f');
12902 if (edit_only)
12903 option_conflict('l', 'e');
12904 if (delete_backups)
12905 option_conflict('l', 'X');
12906 if (argc != 0 && argc != 1)
12907 usage_histedit();
12908 } else if (delete_backups) {
12909 if (abort_edit)
12910 option_conflict('X', 'a');
12911 if (allow_conflict)
12912 option_conflict('X', 'C');
12913 if (continue_edit)
12914 option_conflict('X', 'c');
12915 if (drop_only)
12916 option_conflict('X', 'd');
12917 if (edit_script_path)
12918 option_conflict('X', 'F');
12919 if (edit_logmsg_only)
12920 option_conflict('X', 'm');
12921 if (fold_only)
12922 option_conflict('X', 'f');
12923 if (edit_only)
12924 option_conflict('X', 'e');
12925 if (list_backups)
12926 option_conflict('X', 'l');
12927 if (argc != 0 && argc != 1)
12928 usage_histedit();
12929 } else if (allow_conflict && !continue_edit)
12930 errx(1, "-C option requires -c");
12931 else if (argc != 0)
12932 usage_histedit();
12934 cwd = getcwd(NULL, 0);
12935 if (cwd == NULL) {
12936 error = got_error_from_errno("getcwd");
12937 goto done;
12940 error = got_repo_pack_fds_open(&pack_fds);
12941 if (error != NULL)
12942 goto done;
12944 error = got_worktree_open(&worktree, cwd, GOT_WORKTREE_GOT_DIR);
12945 if (error) {
12946 if (list_backups || delete_backups) {
12947 if (error->code != GOT_ERR_NOT_WORKTREE)
12948 goto done;
12949 } else {
12950 if (error->code == GOT_ERR_NOT_WORKTREE)
12951 error = wrap_not_worktree_error(error,
12952 "histedit", cwd);
12953 goto done;
12957 if (list_backups || delete_backups) {
12958 error = got_repo_open(&repo,
12959 worktree ? got_worktree_get_repo_path(worktree) : cwd,
12960 NULL, pack_fds);
12961 if (error != NULL)
12962 goto done;
12963 error = apply_unveil(got_repo_get_path(repo), 0,
12964 worktree ? got_worktree_get_root_path(worktree) : NULL);
12965 if (error)
12966 goto done;
12967 error = process_backup_refs(
12968 GOT_WORKTREE_HISTEDIT_BACKUP_REF_PREFIX,
12969 argc == 1 ? argv[0] : NULL, delete_backups, repo);
12970 goto done; /* nothing else to do */
12971 } else {
12972 error = get_gitconfig_path(&gitconfig_path);
12973 if (error)
12974 goto done;
12975 error = got_repo_open(&repo,
12976 got_worktree_get_repo_path(worktree), gitconfig_path,
12977 pack_fds);
12978 if (error != NULL)
12979 goto done;
12980 error = get_editor(&editor);
12981 if (error)
12982 goto done;
12983 if (unveil(editor, "x") != 0) {
12984 error = got_error_from_errno2("unveil", editor);
12985 goto done;
12987 if (edit_script_path) {
12988 if (unveil(edit_script_path, "r") != 0) {
12989 error = got_error_from_errno2("unveil",
12990 edit_script_path);
12991 goto done;
12994 error = apply_unveil(got_repo_get_path(repo), 0,
12995 got_worktree_get_root_path(worktree));
12996 if (error)
12997 goto done;
13000 if (worktree != NULL && !list_backups && !delete_backups) {
13001 error = worktree_has_logmsg_ref("histedit", worktree, repo);
13002 if (error)
13003 goto done;
13006 error = got_worktree_rebase_in_progress(&rebase_in_progress, worktree);
13007 if (error)
13008 goto done;
13009 if (rebase_in_progress) {
13010 error = got_error(GOT_ERR_REBASING);
13011 goto done;
13014 error = got_worktree_merge_in_progress(&merge_in_progress, worktree,
13015 repo);
13016 if (error)
13017 goto done;
13018 if (merge_in_progress) {
13019 error = got_error(GOT_ERR_MERGE_BUSY);
13020 goto done;
13023 error = got_worktree_histedit_in_progress(&edit_in_progress, worktree);
13024 if (error)
13025 goto done;
13027 if (edit_in_progress && edit_logmsg_only) {
13028 error = got_error_msg(GOT_ERR_HISTEDIT_BUSY,
13029 "histedit operation is in progress in this "
13030 "work tree and must be continued or aborted "
13031 "before the -m option can be used");
13032 goto done;
13034 if (edit_in_progress && drop_only) {
13035 error = got_error_msg(GOT_ERR_HISTEDIT_BUSY,
13036 "histedit operation is in progress in this "
13037 "work tree and must be continued or aborted "
13038 "before the -d option can be used");
13039 goto done;
13041 if (edit_in_progress && fold_only) {
13042 error = got_error_msg(GOT_ERR_HISTEDIT_BUSY,
13043 "histedit operation is in progress in this "
13044 "work tree and must be continued or aborted "
13045 "before the -f option can be used");
13046 goto done;
13048 if (edit_in_progress && edit_only) {
13049 error = got_error_msg(GOT_ERR_HISTEDIT_BUSY,
13050 "histedit operation is in progress in this "
13051 "work tree and must be continued or aborted "
13052 "before the -e option can be used");
13053 goto done;
13056 if (edit_in_progress && abort_edit) {
13057 error = got_worktree_histedit_continue(&resume_commit_id,
13058 &tmp_branch, &branch, &base_commit_id, &fileindex,
13059 worktree, repo);
13060 if (error)
13061 goto done;
13062 printf("Switching work tree to %s\n",
13063 got_ref_get_symref_target(branch));
13064 error = got_worktree_histedit_abort(worktree, fileindex, repo,
13065 branch, base_commit_id, abort_progress, &upa);
13066 if (error)
13067 goto done;
13068 printf("Histedit of %s aborted\n",
13069 got_ref_get_symref_target(branch));
13070 print_merge_progress_stats(&upa);
13071 goto done; /* nothing else to do */
13072 } else if (abort_edit) {
13073 error = got_error(GOT_ERR_NOT_HISTEDIT);
13074 goto done;
13077 error = get_author(&committer, repo, worktree);
13078 if (error)
13079 goto done;
13081 if (continue_edit) {
13082 char *path;
13084 if (!edit_in_progress) {
13085 error = got_error(GOT_ERR_NOT_HISTEDIT);
13086 goto done;
13089 error = got_worktree_get_histedit_script_path(&path, worktree);
13090 if (error)
13091 goto done;
13093 error = histedit_load_list(&histedit_cmds, path, repo);
13094 free(path);
13095 if (error)
13096 goto done;
13098 error = got_worktree_histedit_continue(&resume_commit_id,
13099 &tmp_branch, &branch, &base_commit_id, &fileindex,
13100 worktree, repo);
13101 if (error)
13102 goto done;
13104 error = got_ref_resolve(&head_commit_id, repo, branch);
13105 if (error)
13106 goto done;
13108 error = got_object_open_as_commit(&commit, repo,
13109 head_commit_id);
13110 if (error)
13111 goto done;
13112 parent_ids = got_object_commit_get_parent_ids(commit);
13113 pid = STAILQ_FIRST(parent_ids);
13114 if (pid == NULL) {
13115 error = got_error(GOT_ERR_EMPTY_HISTEDIT);
13116 goto done;
13118 error = collect_commits(&commits, head_commit_id, &pid->id,
13119 base_commit_id, got_worktree_get_path_prefix(worktree),
13120 GOT_ERR_HISTEDIT_PATH, repo);
13121 got_object_commit_close(commit);
13122 commit = NULL;
13123 if (error)
13124 goto done;
13125 } else {
13126 if (edit_in_progress) {
13127 error = got_error(GOT_ERR_HISTEDIT_BUSY);
13128 goto done;
13131 error = got_ref_open(&branch, repo,
13132 got_worktree_get_head_ref_name(worktree), 0);
13133 if (error != NULL)
13134 goto done;
13136 if (strncmp(got_ref_get_name(branch), "refs/heads/", 11) != 0) {
13137 error = got_error_msg(GOT_ERR_COMMIT_BRANCH,
13138 "will not edit commit history of a branch outside "
13139 "the \"refs/heads/\" reference namespace");
13140 goto done;
13143 error = got_ref_resolve(&head_commit_id, repo, branch);
13144 got_ref_close(branch);
13145 branch = NULL;
13146 if (error)
13147 goto done;
13149 error = got_object_open_as_commit(&commit, repo,
13150 head_commit_id);
13151 if (error)
13152 goto done;
13153 parent_ids = got_object_commit_get_parent_ids(commit);
13154 pid = STAILQ_FIRST(parent_ids);
13155 if (pid == NULL) {
13156 error = got_error(GOT_ERR_EMPTY_HISTEDIT);
13157 goto done;
13159 error = collect_commits(&commits, head_commit_id, &pid->id,
13160 got_worktree_get_base_commit_id(worktree),
13161 got_worktree_get_path_prefix(worktree),
13162 GOT_ERR_HISTEDIT_PATH, repo);
13163 got_object_commit_close(commit);
13164 commit = NULL;
13165 if (error)
13166 goto done;
13168 if (STAILQ_EMPTY(&commits)) {
13169 error = got_error(GOT_ERR_EMPTY_HISTEDIT);
13170 goto done;
13173 error = got_worktree_histedit_prepare(&tmp_branch, &branch,
13174 &base_commit_id, &fileindex, worktree, repo);
13175 if (error)
13176 goto done;
13178 if (edit_script_path) {
13179 error = histedit_load_list(&histedit_cmds,
13180 edit_script_path, repo);
13181 if (error) {
13182 got_worktree_histedit_abort(worktree, fileindex,
13183 repo, branch, base_commit_id,
13184 abort_progress, &upa);
13185 print_merge_progress_stats(&upa);
13186 goto done;
13188 } else {
13189 const char *branch_name;
13190 branch_name = got_ref_get_symref_target(branch);
13191 if (strncmp(branch_name, "refs/heads/", 11) == 0)
13192 branch_name += 11;
13193 error = histedit_edit_script(&histedit_cmds, &commits,
13194 branch_name, edit_logmsg_only, fold_only,
13195 drop_only, edit_only, editor, repo);
13196 if (error) {
13197 got_worktree_histedit_abort(worktree, fileindex,
13198 repo, branch, base_commit_id,
13199 abort_progress, &upa);
13200 print_merge_progress_stats(&upa);
13201 goto done;
13206 error = histedit_save_list(&histedit_cmds, worktree,
13207 repo);
13208 if (error) {
13209 got_worktree_histedit_abort(worktree, fileindex,
13210 repo, branch, base_commit_id,
13211 abort_progress, &upa);
13212 print_merge_progress_stats(&upa);
13213 goto done;
13218 error = histedit_check_script(&histedit_cmds, &commits, repo);
13219 if (error)
13220 goto done;
13222 TAILQ_FOREACH(hle, &histedit_cmds, entry) {
13223 if (resume_commit_id) {
13224 if (got_object_id_cmp(hle->commit_id,
13225 resume_commit_id) != 0)
13226 continue;
13228 resume_commit_id = NULL;
13229 if (hle->cmd->code == GOT_HISTEDIT_DROP ||
13230 hle->cmd->code == GOT_HISTEDIT_FOLD) {
13231 error = histedit_skip_commit(hle, worktree,
13232 repo);
13233 if (error)
13234 goto done;
13235 } else {
13236 struct got_pathlist_head paths;
13237 int have_changes = 0;
13239 TAILQ_INIT(&paths);
13240 error = got_pathlist_insert(NULL, &paths, "", NULL);
13241 if (error)
13242 goto done;
13243 error = got_worktree_status(worktree, &paths,
13244 repo, 0, check_local_changes, &have_changes,
13245 check_cancelled, NULL);
13246 got_pathlist_free(&paths,
13247 GOT_PATHLIST_FREE_NONE);
13248 if (error) {
13249 if (error->code != GOT_ERR_CANCELLED)
13250 goto done;
13251 if (sigint_received || sigpipe_received)
13252 goto done;
13254 if (have_changes) {
13255 error = histedit_commit(NULL, worktree,
13256 fileindex, tmp_branch, hle,
13257 committer, allow_conflict, editor,
13258 repo);
13259 if (error)
13260 goto done;
13261 } else {
13262 error = histedit_skip_commit(hle,
13263 worktree, repo);
13264 if (error)
13265 goto done;
13268 continue;
13271 if (hle->cmd->code == GOT_HISTEDIT_DROP) {
13272 error = histedit_skip_commit(hle, worktree, repo);
13273 if (error)
13274 goto done;
13275 continue;
13277 error = got_object_open_as_commit(&commit, repo,
13278 hle->commit_id);
13279 if (error)
13280 goto done;
13281 parent_ids = got_object_commit_get_parent_ids(commit);
13282 pid = STAILQ_FIRST(parent_ids);
13284 error = got_worktree_histedit_merge_files(&merged_paths,
13285 worktree, fileindex, &pid->id, hle->commit_id, repo,
13286 update_progress, &upa, check_cancelled, NULL);
13287 if (error)
13288 goto done;
13289 got_object_commit_close(commit);
13290 commit = NULL;
13292 print_merge_progress_stats(&upa);
13293 if (upa.conflicts > 0 || upa.missing > 0 ||
13294 upa.not_deleted > 0 || upa.unversioned > 0) {
13295 if (upa.conflicts > 0) {
13296 error = show_rebase_merge_conflict(
13297 hle->commit_id, repo);
13298 if (error)
13299 goto done;
13301 got_pathlist_free(&merged_paths, GOT_PATHLIST_FREE_PATH);
13302 break;
13305 if (hle->cmd->code == GOT_HISTEDIT_EDIT) {
13306 char *id_str;
13307 error = got_object_id_str(&id_str, hle->commit_id);
13308 if (error)
13309 goto done;
13310 printf("Stopping histedit for amending commit %s\n",
13311 id_str);
13312 free(id_str);
13313 got_pathlist_free(&merged_paths, GOT_PATHLIST_FREE_PATH);
13314 error = got_worktree_histedit_postpone(worktree,
13315 fileindex);
13316 goto done;
13317 } else if (hle->cmd->code == GOT_HISTEDIT_FOLD) {
13318 error = histedit_skip_commit(hle, worktree, repo);
13319 if (error)
13320 goto done;
13321 continue;
13322 } else if (hle->cmd->code == GOT_HISTEDIT_MESG) {
13323 error = histedit_edit_logmsg(hle, editor, repo);
13324 if (error)
13325 goto done;
13328 error = histedit_commit(&merged_paths, worktree, fileindex,
13329 tmp_branch, hle, committer, allow_conflict, editor, repo);
13330 got_pathlist_free(&merged_paths, GOT_PATHLIST_FREE_PATH);
13331 if (error)
13332 goto done;
13335 if (upa.conflicts > 0 || upa.missing > 0 ||
13336 upa.not_deleted > 0 || upa.unversioned > 0) {
13337 error = got_worktree_histedit_postpone(worktree, fileindex);
13338 if (error)
13339 goto done;
13340 if (upa.conflicts > 0 && upa.missing == 0 &&
13341 upa.not_deleted == 0 && upa.unversioned == 0) {
13342 error = got_error_msg(GOT_ERR_CONFLICTS,
13343 "conflicts must be resolved before histedit "
13344 "can continue");
13345 } else if (upa.conflicts > 0) {
13346 error = got_error_msg(GOT_ERR_CONFLICTS,
13347 "conflicts must be resolved before histedit "
13348 "can continue; changes destined for some "
13349 "files were not yet merged and should be "
13350 "merged manually if required before the "
13351 "histedit operation is continued");
13352 } else {
13353 error = got_error_msg(GOT_ERR_CONFLICTS,
13354 "changes destined for some files were not "
13355 "yet merged and should be merged manually "
13356 "if required before the histedit operation "
13357 "is continued");
13359 } else
13360 error = histedit_complete(worktree, fileindex, tmp_branch,
13361 branch, repo);
13362 done:
13363 free(cwd);
13364 free(editor);
13365 free(committer);
13366 free(gitconfig_path);
13367 got_object_id_queue_free(&commits);
13368 histedit_free_list(&histedit_cmds);
13369 free(head_commit_id);
13370 free(base_commit_id);
13371 free(resume_commit_id);
13372 if (commit)
13373 got_object_commit_close(commit);
13374 if (branch)
13375 got_ref_close(branch);
13376 if (tmp_branch)
13377 got_ref_close(tmp_branch);
13378 if (worktree)
13379 got_worktree_close(worktree);
13380 if (repo) {
13381 const struct got_error *close_err = got_repo_close(repo);
13382 if (error == NULL)
13383 error = close_err;
13385 if (pack_fds) {
13386 const struct got_error *pack_err =
13387 got_repo_pack_fds_close(pack_fds);
13388 if (error == NULL)
13389 error = pack_err;
13391 return error;
13394 __dead static void
13395 usage_integrate(void)
13397 fprintf(stderr, "usage: %s integrate branch\n", getprogname());
13398 exit(1);
13401 static const struct got_error *
13402 cmd_integrate(int argc, char *argv[])
13404 const struct got_error *error = NULL;
13405 struct got_repository *repo = NULL;
13406 struct got_worktree *worktree = NULL;
13407 char *cwd = NULL, *refname = NULL, *base_refname = NULL;
13408 const char *branch_arg = NULL;
13409 struct got_reference *branch_ref = NULL, *base_branch_ref = NULL;
13410 struct got_fileindex *fileindex = NULL;
13411 struct got_object_id *commit_id = NULL, *base_commit_id = NULL;
13412 int ch;
13413 struct got_update_progress_arg upa;
13414 int *pack_fds = NULL;
13416 #ifndef PROFILE
13417 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
13418 "unveil", NULL) == -1)
13419 err(1, "pledge");
13420 #endif
13422 while ((ch = getopt(argc, argv, "")) != -1) {
13423 switch (ch) {
13424 default:
13425 usage_integrate();
13426 /* NOTREACHED */
13430 argc -= optind;
13431 argv += optind;
13433 if (argc != 1)
13434 usage_integrate();
13435 branch_arg = argv[0];
13437 cwd = getcwd(NULL, 0);
13438 if (cwd == NULL) {
13439 error = got_error_from_errno("getcwd");
13440 goto done;
13443 error = got_repo_pack_fds_open(&pack_fds);
13444 if (error != NULL)
13445 goto done;
13447 error = got_worktree_open(&worktree, cwd, GOT_WORKTREE_GOT_DIR);
13448 if (error) {
13449 if (error->code == GOT_ERR_NOT_WORKTREE)
13450 error = wrap_not_worktree_error(error, "integrate",
13451 cwd);
13452 goto done;
13455 error = check_rebase_or_histedit_in_progress(worktree);
13456 if (error)
13457 goto done;
13459 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
13460 NULL, pack_fds);
13461 if (error != NULL)
13462 goto done;
13464 error = apply_unveil(got_repo_get_path(repo), 0,
13465 got_worktree_get_root_path(worktree));
13466 if (error)
13467 goto done;
13469 error = check_merge_in_progress(worktree, repo);
13470 if (error)
13471 goto done;
13473 if (asprintf(&refname, "refs/heads/%s", branch_arg) == -1) {
13474 error = got_error_from_errno("asprintf");
13475 goto done;
13478 error = got_worktree_integrate_prepare(&fileindex, &branch_ref,
13479 &base_branch_ref, worktree, refname, repo);
13480 if (error)
13481 goto done;
13483 refname = strdup(got_ref_get_name(branch_ref));
13484 if (refname == NULL) {
13485 error = got_error_from_errno("strdup");
13486 got_worktree_integrate_abort(worktree, fileindex, repo,
13487 branch_ref, base_branch_ref);
13488 goto done;
13490 base_refname = strdup(got_ref_get_name(base_branch_ref));
13491 if (base_refname == NULL) {
13492 error = got_error_from_errno("strdup");
13493 got_worktree_integrate_abort(worktree, fileindex, repo,
13494 branch_ref, base_branch_ref);
13495 goto done;
13497 if (strncmp(base_refname, "refs/heads/", 11) != 0) {
13498 error = got_error(GOT_ERR_INTEGRATE_BRANCH);
13499 got_worktree_integrate_abort(worktree, fileindex, repo,
13500 branch_ref, base_branch_ref);
13501 goto done;
13504 error = got_ref_resolve(&commit_id, repo, branch_ref);
13505 if (error)
13506 goto done;
13508 error = got_ref_resolve(&base_commit_id, repo, base_branch_ref);
13509 if (error)
13510 goto done;
13512 if (got_object_id_cmp(commit_id, base_commit_id) == 0) {
13513 error = got_error_msg(GOT_ERR_SAME_BRANCH,
13514 "specified branch has already been integrated");
13515 got_worktree_integrate_abort(worktree, fileindex, repo,
13516 branch_ref, base_branch_ref);
13517 goto done;
13520 error = check_linear_ancestry(commit_id, base_commit_id, 1, repo);
13521 if (error) {
13522 if (error->code == GOT_ERR_ANCESTRY)
13523 error = got_error(GOT_ERR_REBASE_REQUIRED);
13524 got_worktree_integrate_abort(worktree, fileindex, repo,
13525 branch_ref, base_branch_ref);
13526 goto done;
13529 memset(&upa, 0, sizeof(upa));
13530 error = got_worktree_integrate_continue(worktree, fileindex, repo,
13531 branch_ref, base_branch_ref, update_progress, &upa,
13532 check_cancelled, NULL);
13533 if (error)
13534 goto done;
13536 printf("Integrated %s into %s\n", refname, base_refname);
13537 print_update_progress_stats(&upa);
13538 done:
13539 if (repo) {
13540 const struct got_error *close_err = got_repo_close(repo);
13541 if (error == NULL)
13542 error = close_err;
13544 if (worktree)
13545 got_worktree_close(worktree);
13546 if (pack_fds) {
13547 const struct got_error *pack_err =
13548 got_repo_pack_fds_close(pack_fds);
13549 if (error == NULL)
13550 error = pack_err;
13552 free(cwd);
13553 free(base_commit_id);
13554 free(commit_id);
13555 free(refname);
13556 free(base_refname);
13557 return error;
13560 __dead static void
13561 usage_merge(void)
13563 fprintf(stderr, "usage: %s merge [-aCcn] [branch]\n", getprogname());
13564 exit(1);
13567 static const struct got_error *
13568 cmd_merge(int argc, char *argv[])
13570 const struct got_error *error = NULL;
13571 struct got_worktree *worktree = NULL;
13572 struct got_repository *repo = NULL;
13573 struct got_fileindex *fileindex = NULL;
13574 char *cwd = NULL, *id_str = NULL, *author = NULL;
13575 char *gitconfig_path = NULL;
13576 struct got_reference *branch = NULL, *wt_branch = NULL;
13577 struct got_object_id *branch_tip = NULL, *yca_id = NULL;
13578 struct got_object_id *wt_branch_tip = NULL;
13579 int ch, merge_in_progress = 0, abort_merge = 0, continue_merge = 0;
13580 int allow_conflict = 0, prefer_fast_forward = 1, interrupt_merge = 0;
13581 struct got_update_progress_arg upa;
13582 struct got_object_id *merge_commit_id = NULL;
13583 char *branch_name = NULL;
13584 int *pack_fds = NULL;
13586 memset(&upa, 0, sizeof(upa));
13588 #ifndef PROFILE
13589 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
13590 "unveil", NULL) == -1)
13591 err(1, "pledge");
13592 #endif
13594 while ((ch = getopt(argc, argv, "aCcMn")) != -1) {
13595 switch (ch) {
13596 case 'a':
13597 abort_merge = 1;
13598 break;
13599 case 'C':
13600 allow_conflict = 1;
13601 break;
13602 case 'c':
13603 continue_merge = 1;
13604 break;
13605 case 'M':
13606 prefer_fast_forward = 0;
13607 break;
13608 case 'n':
13609 interrupt_merge = 1;
13610 break;
13611 default:
13612 usage_merge();
13613 /* NOTREACHED */
13617 argc -= optind;
13618 argv += optind;
13620 if (abort_merge) {
13621 if (continue_merge)
13622 option_conflict('a', 'c');
13623 if (!prefer_fast_forward)
13624 option_conflict('a', 'M');
13625 if (interrupt_merge)
13626 option_conflict('a', 'n');
13627 } else if (continue_merge) {
13628 if (!prefer_fast_forward)
13629 option_conflict('c', 'M');
13630 if (interrupt_merge)
13631 option_conflict('c', 'n');
13633 if (allow_conflict) {
13634 if (!continue_merge)
13635 errx(1, "-C option requires -c");
13637 if (abort_merge || continue_merge) {
13638 if (argc != 0)
13639 usage_merge();
13640 } else if (argc != 1)
13641 usage_merge();
13643 cwd = getcwd(NULL, 0);
13644 if (cwd == NULL) {
13645 error = got_error_from_errno("getcwd");
13646 goto done;
13649 error = got_repo_pack_fds_open(&pack_fds);
13650 if (error != NULL)
13651 goto done;
13653 error = got_worktree_open(&worktree, cwd, GOT_WORKTREE_GOT_DIR);
13654 if (error) {
13655 if (error->code == GOT_ERR_NOT_WORKTREE)
13656 error = wrap_not_worktree_error(error,
13657 "merge", cwd);
13658 goto done;
13661 error = get_gitconfig_path(&gitconfig_path);
13662 if (error)
13663 goto done;
13664 error = got_repo_open(&repo,
13665 worktree ? got_worktree_get_repo_path(worktree) : cwd,
13666 gitconfig_path, pack_fds);
13667 if (error != NULL)
13668 goto done;
13670 if (worktree != NULL) {
13671 error = worktree_has_logmsg_ref("merge", worktree, repo);
13672 if (error)
13673 goto done;
13676 error = apply_unveil(got_repo_get_path(repo), 0,
13677 worktree ? got_worktree_get_root_path(worktree) : NULL);
13678 if (error)
13679 goto done;
13681 error = check_rebase_or_histedit_in_progress(worktree);
13682 if (error)
13683 goto done;
13685 error = got_worktree_merge_in_progress(&merge_in_progress, worktree,
13686 repo);
13687 if (error)
13688 goto done;
13690 if (merge_in_progress && !(abort_merge || continue_merge)) {
13691 error = got_error(GOT_ERR_MERGE_BUSY);
13692 goto done;
13695 if (!merge_in_progress && (abort_merge || continue_merge)) {
13696 error = got_error(GOT_ERR_NOT_MERGING);
13697 goto done;
13700 if (abort_merge) {
13701 error = got_worktree_merge_continue(&branch_name,
13702 &branch_tip, &fileindex, worktree, repo);
13703 if (error)
13704 goto done;
13705 error = got_worktree_merge_abort(worktree, fileindex, repo,
13706 abort_progress, &upa);
13707 if (error)
13708 goto done;
13709 printf("Merge of %s aborted\n", branch_name);
13710 goto done; /* nothing else to do */
13713 if (strncmp(got_worktree_get_head_ref_name(worktree),
13714 "refs/heads/", 11) != 0) {
13715 error = got_error_fmt(GOT_ERR_COMMIT_BRANCH,
13716 "work tree's current branch %s is outside the "
13717 "\"refs/heads/\" reference namespace; "
13718 "update -b required",
13719 got_worktree_get_head_ref_name(worktree));
13720 goto done;
13723 error = get_author(&author, repo, worktree);
13724 if (error)
13725 goto done;
13727 error = got_ref_open(&wt_branch, repo,
13728 got_worktree_get_head_ref_name(worktree), 0);
13729 if (error)
13730 goto done;
13731 error = got_ref_resolve(&wt_branch_tip, repo, wt_branch);
13732 if (error)
13733 goto done;
13735 if (continue_merge) {
13736 struct got_object_id *base_commit_id;
13737 base_commit_id = got_worktree_get_base_commit_id(worktree);
13738 if (got_object_id_cmp(wt_branch_tip, base_commit_id) != 0) {
13739 error = got_error(GOT_ERR_MERGE_COMMIT_OUT_OF_DATE);
13740 goto done;
13742 error = got_worktree_merge_continue(&branch_name,
13743 &branch_tip, &fileindex, worktree, repo);
13744 if (error)
13745 goto done;
13746 } else {
13747 error = got_ref_open(&branch, repo, argv[0], 0);
13748 if (error != NULL)
13749 goto done;
13750 branch_name = strdup(got_ref_get_name(branch));
13751 if (branch_name == NULL) {
13752 error = got_error_from_errno("strdup");
13753 goto done;
13755 error = got_ref_resolve(&branch_tip, repo, branch);
13756 if (error)
13757 goto done;
13760 error = got_commit_graph_find_youngest_common_ancestor(&yca_id,
13761 wt_branch_tip, branch_tip, 0, 0, repo,
13762 check_cancelled, NULL);
13763 if (error && error->code != GOT_ERR_ANCESTRY)
13764 goto done;
13766 if (!continue_merge) {
13767 error = check_path_prefix(wt_branch_tip, branch_tip,
13768 got_worktree_get_path_prefix(worktree),
13769 GOT_ERR_MERGE_PATH, repo);
13770 if (error)
13771 goto done;
13772 error = got_worktree_merge_prepare(&fileindex, worktree, repo);
13773 if (error)
13774 goto done;
13775 if (prefer_fast_forward && yca_id &&
13776 got_object_id_cmp(wt_branch_tip, yca_id) == 0) {
13777 struct got_pathlist_head paths;
13778 if (interrupt_merge) {
13779 error = got_error_fmt(GOT_ERR_BAD_OPTION,
13780 "there are no changes to merge since %s "
13781 "is already based on %s; merge cannot be "
13782 "interrupted for amending; -n",
13783 branch_name, got_ref_get_name(wt_branch));
13784 goto done;
13786 printf("Forwarding %s to %s\n",
13787 got_ref_get_name(wt_branch), branch_name);
13788 error = got_ref_change_ref(wt_branch, branch_tip);
13789 if (error)
13790 goto done;
13791 error = got_ref_write(wt_branch, repo);
13792 if (error)
13793 goto done;
13794 error = got_worktree_set_base_commit_id(worktree, repo,
13795 branch_tip);
13796 if (error)
13797 goto done;
13798 TAILQ_INIT(&paths);
13799 error = got_pathlist_insert(NULL, &paths, "", NULL);
13800 if (error)
13801 goto done;
13802 error = got_worktree_checkout_files(worktree,
13803 &paths, repo, update_progress, &upa,
13804 check_cancelled, NULL);
13805 got_pathlist_free(&paths, GOT_PATHLIST_FREE_NONE);
13806 if (error)
13807 goto done;
13808 if (upa.did_something) {
13809 char *id_str;
13810 error = got_object_id_str(&id_str, branch_tip);
13811 if (error)
13812 goto done;
13813 printf("Updated to commit %s\n", id_str);
13814 free(id_str);
13815 } else
13816 printf("Already up-to-date\n");
13817 print_update_progress_stats(&upa);
13818 goto done;
13820 error = got_worktree_merge_write_refs(worktree, branch, repo);
13821 if (error)
13822 goto done;
13824 error = got_worktree_merge_branch(worktree, fileindex,
13825 yca_id, branch_tip, repo, update_progress, &upa,
13826 check_cancelled, NULL);
13827 if (error)
13828 goto done;
13829 print_merge_progress_stats(&upa);
13830 if (!upa.did_something) {
13831 error = got_worktree_merge_abort(worktree, fileindex,
13832 repo, abort_progress, &upa);
13833 if (error)
13834 goto done;
13835 printf("Already up-to-date\n");
13836 goto done;
13840 if (interrupt_merge) {
13841 error = got_worktree_merge_postpone(worktree, fileindex);
13842 if (error)
13843 goto done;
13844 printf("Merge of %s interrupted on request\n", branch_name);
13845 } else if (upa.conflicts > 0 || upa.missing > 0 ||
13846 upa.not_deleted > 0 || upa.unversioned > 0) {
13847 error = got_worktree_merge_postpone(worktree, fileindex);
13848 if (error)
13849 goto done;
13850 if (upa.conflicts > 0 && upa.missing == 0 &&
13851 upa.not_deleted == 0 && upa.unversioned == 0) {
13852 error = got_error_msg(GOT_ERR_CONFLICTS,
13853 "conflicts must be resolved before merging "
13854 "can continue");
13855 } else if (upa.conflicts > 0) {
13856 error = got_error_msg(GOT_ERR_CONFLICTS,
13857 "conflicts must be resolved before merging "
13858 "can continue; changes destined for some "
13859 "files were not yet merged and "
13860 "should be merged manually if required before the "
13861 "merge operation is continued");
13862 } else {
13863 error = got_error_msg(GOT_ERR_CONFLICTS,
13864 "changes destined for some "
13865 "files were not yet merged and should be "
13866 "merged manually if required before the "
13867 "merge operation is continued");
13869 goto done;
13870 } else {
13871 error = got_worktree_merge_commit(&merge_commit_id, worktree,
13872 fileindex, author, NULL, 1, branch_tip, branch_name,
13873 allow_conflict, repo, continue_merge ? print_status : NULL,
13874 NULL);
13875 if (error)
13876 goto done;
13877 error = got_worktree_merge_complete(worktree, fileindex, repo);
13878 if (error)
13879 goto done;
13880 error = got_object_id_str(&id_str, merge_commit_id);
13881 if (error)
13882 goto done;
13883 printf("Merged %s into %s: %s\n", branch_name,
13884 got_worktree_get_head_ref_name(worktree),
13885 id_str);
13888 done:
13889 free(cwd);
13890 free(gitconfig_path);
13891 free(id_str);
13892 free(merge_commit_id);
13893 free(author);
13894 free(branch_tip);
13895 free(branch_name);
13896 free(yca_id);
13897 if (branch)
13898 got_ref_close(branch);
13899 if (wt_branch)
13900 got_ref_close(wt_branch);
13901 if (worktree)
13902 got_worktree_close(worktree);
13903 if (repo) {
13904 const struct got_error *close_err = got_repo_close(repo);
13905 if (error == NULL)
13906 error = close_err;
13908 if (pack_fds) {
13909 const struct got_error *pack_err =
13910 got_repo_pack_fds_close(pack_fds);
13911 if (error == NULL)
13912 error = pack_err;
13914 return error;
13917 __dead static void
13918 usage_stage(void)
13920 fprintf(stderr, "usage: %s stage [-lpS] [-F response-script] "
13921 "[path ...]\n", getprogname());
13922 exit(1);
13925 static const struct got_error *
13926 print_stage(void *arg, unsigned char status, unsigned char staged_status,
13927 const char *path, struct got_object_id *blob_id,
13928 struct got_object_id *staged_blob_id, struct got_object_id *commit_id,
13929 int dirfd, const char *de_name)
13931 const struct got_error *err = NULL;
13932 char *id_str = NULL;
13934 if (staged_status != GOT_STATUS_ADD &&
13935 staged_status != GOT_STATUS_MODIFY &&
13936 staged_status != GOT_STATUS_DELETE)
13937 return NULL;
13939 if (staged_status == GOT_STATUS_ADD ||
13940 staged_status == GOT_STATUS_MODIFY)
13941 err = got_object_id_str(&id_str, staged_blob_id);
13942 else
13943 err = got_object_id_str(&id_str, blob_id);
13944 if (err)
13945 return err;
13947 printf("%s %c %s\n", id_str, staged_status, path);
13948 free(id_str);
13949 return NULL;
13952 static const struct got_error *
13953 cmd_stage(int argc, char *argv[])
13955 const struct got_error *error = NULL;
13956 struct got_repository *repo = NULL;
13957 struct got_worktree *worktree = NULL;
13958 char *cwd = NULL;
13959 struct got_pathlist_head paths;
13960 int ch, list_stage = 0, pflag = 0, allow_bad_symlinks = 0;
13961 FILE *patch_script_file = NULL;
13962 const char *patch_script_path = NULL;
13963 struct choose_patch_arg cpa;
13964 int *pack_fds = NULL;
13966 TAILQ_INIT(&paths);
13968 #ifndef PROFILE
13969 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
13970 "unveil", NULL) == -1)
13971 err(1, "pledge");
13972 #endif
13974 while ((ch = getopt(argc, argv, "F:lpS")) != -1) {
13975 switch (ch) {
13976 case 'F':
13977 patch_script_path = optarg;
13978 break;
13979 case 'l':
13980 list_stage = 1;
13981 break;
13982 case 'p':
13983 pflag = 1;
13984 break;
13985 case 'S':
13986 allow_bad_symlinks = 1;
13987 break;
13988 default:
13989 usage_stage();
13990 /* NOTREACHED */
13994 argc -= optind;
13995 argv += optind;
13997 if (list_stage && (pflag || patch_script_path))
13998 errx(1, "-l option cannot be used with other options");
13999 if (patch_script_path && !pflag)
14000 errx(1, "-F option can only be used together with -p option");
14002 cwd = getcwd(NULL, 0);
14003 if (cwd == NULL) {
14004 error = got_error_from_errno("getcwd");
14005 goto done;
14008 error = got_repo_pack_fds_open(&pack_fds);
14009 if (error != NULL)
14010 goto done;
14012 error = got_worktree_open(&worktree, cwd, GOT_WORKTREE_GOT_DIR);
14013 if (error) {
14014 if (error->code == GOT_ERR_NOT_WORKTREE)
14015 error = wrap_not_worktree_error(error, "stage", cwd);
14016 goto done;
14019 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
14020 NULL, pack_fds);
14021 if (error != NULL)
14022 goto done;
14024 if (patch_script_path) {
14025 patch_script_file = fopen(patch_script_path, "re");
14026 if (patch_script_file == NULL) {
14027 error = got_error_from_errno2("fopen",
14028 patch_script_path);
14029 goto done;
14032 error = apply_unveil(got_repo_get_path(repo), 0,
14033 got_worktree_get_root_path(worktree));
14034 if (error)
14035 goto done;
14037 error = check_merge_in_progress(worktree, repo);
14038 if (error)
14039 goto done;
14041 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
14042 if (error)
14043 goto done;
14045 if (list_stage)
14046 error = got_worktree_status(worktree, &paths, repo, 0,
14047 print_stage, NULL, check_cancelled, NULL);
14048 else {
14049 cpa.patch_script_file = patch_script_file;
14050 cpa.action = "stage";
14051 error = got_worktree_stage(worktree, &paths,
14052 pflag ? NULL : print_status, NULL,
14053 pflag ? choose_patch : NULL, &cpa,
14054 allow_bad_symlinks, repo);
14056 done:
14057 if (patch_script_file && fclose(patch_script_file) == EOF &&
14058 error == NULL)
14059 error = got_error_from_errno2("fclose", patch_script_path);
14060 if (repo) {
14061 const struct got_error *close_err = got_repo_close(repo);
14062 if (error == NULL)
14063 error = close_err;
14065 if (worktree)
14066 got_worktree_close(worktree);
14067 if (pack_fds) {
14068 const struct got_error *pack_err =
14069 got_repo_pack_fds_close(pack_fds);
14070 if (error == NULL)
14071 error = pack_err;
14073 got_pathlist_free(&paths, GOT_PATHLIST_FREE_PATH);
14074 free(cwd);
14075 return error;
14078 __dead static void
14079 usage_unstage(void)
14081 fprintf(stderr, "usage: %s unstage [-p] [-F response-script] "
14082 "[path ...]\n", getprogname());
14083 exit(1);
14087 static const struct got_error *
14088 cmd_unstage(int argc, char *argv[])
14090 const struct got_error *error = NULL;
14091 struct got_repository *repo = NULL;
14092 struct got_worktree *worktree = NULL;
14093 char *cwd = NULL;
14094 struct got_pathlist_head paths;
14095 int ch, pflag = 0;
14096 struct got_update_progress_arg upa;
14097 FILE *patch_script_file = NULL;
14098 const char *patch_script_path = NULL;
14099 struct choose_patch_arg cpa;
14100 int *pack_fds = NULL;
14102 TAILQ_INIT(&paths);
14104 #ifndef PROFILE
14105 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
14106 "unveil", NULL) == -1)
14107 err(1, "pledge");
14108 #endif
14110 while ((ch = getopt(argc, argv, "F:p")) != -1) {
14111 switch (ch) {
14112 case 'F':
14113 patch_script_path = optarg;
14114 break;
14115 case 'p':
14116 pflag = 1;
14117 break;
14118 default:
14119 usage_unstage();
14120 /* NOTREACHED */
14124 argc -= optind;
14125 argv += optind;
14127 if (patch_script_path && !pflag)
14128 errx(1, "-F option can only be used together with -p option");
14130 cwd = getcwd(NULL, 0);
14131 if (cwd == NULL) {
14132 error = got_error_from_errno("getcwd");
14133 goto done;
14136 error = got_repo_pack_fds_open(&pack_fds);
14137 if (error != NULL)
14138 goto done;
14140 error = got_worktree_open(&worktree, cwd, GOT_WORKTREE_GOT_DIR);
14141 if (error) {
14142 if (error->code == GOT_ERR_NOT_WORKTREE)
14143 error = wrap_not_worktree_error(error, "unstage", cwd);
14144 goto done;
14147 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
14148 NULL, pack_fds);
14149 if (error != NULL)
14150 goto done;
14152 if (patch_script_path) {
14153 patch_script_file = fopen(patch_script_path, "re");
14154 if (patch_script_file == NULL) {
14155 error = got_error_from_errno2("fopen",
14156 patch_script_path);
14157 goto done;
14161 error = apply_unveil(got_repo_get_path(repo), 0,
14162 got_worktree_get_root_path(worktree));
14163 if (error)
14164 goto done;
14166 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
14167 if (error)
14168 goto done;
14170 cpa.patch_script_file = patch_script_file;
14171 cpa.action = "unstage";
14172 memset(&upa, 0, sizeof(upa));
14173 error = got_worktree_unstage(worktree, &paths, update_progress,
14174 &upa, pflag ? choose_patch : NULL, &cpa, repo);
14175 if (!error)
14176 print_merge_progress_stats(&upa);
14177 done:
14178 if (patch_script_file && fclose(patch_script_file) == EOF &&
14179 error == NULL)
14180 error = got_error_from_errno2("fclose", patch_script_path);
14181 if (repo) {
14182 const struct got_error *close_err = got_repo_close(repo);
14183 if (error == NULL)
14184 error = close_err;
14186 if (worktree)
14187 got_worktree_close(worktree);
14188 if (pack_fds) {
14189 const struct got_error *pack_err =
14190 got_repo_pack_fds_close(pack_fds);
14191 if (error == NULL)
14192 error = pack_err;
14194 got_pathlist_free(&paths, GOT_PATHLIST_FREE_PATH);
14195 free(cwd);
14196 return error;
14199 __dead static void
14200 usage_cat(void)
14202 fprintf(stderr, "usage: %s cat [-P] [-c commit] [-r repository-path] "
14203 "arg ...\n", getprogname());
14204 exit(1);
14207 static const struct got_error *
14208 cat_blob(struct got_object_id *id, struct got_repository *repo, FILE *outfile)
14210 const struct got_error *err;
14211 struct got_blob_object *blob;
14212 int fd = -1;
14214 fd = got_opentempfd();
14215 if (fd == -1)
14216 return got_error_from_errno("got_opentempfd");
14218 err = got_object_open_as_blob(&blob, repo, id, 8192, fd);
14219 if (err)
14220 goto done;
14222 err = got_object_blob_dump_to_file(NULL, NULL, NULL, outfile, blob);
14223 done:
14224 if (fd != -1 && close(fd) == -1 && err == NULL)
14225 err = got_error_from_errno("close");
14226 if (blob)
14227 got_object_blob_close(blob);
14228 return err;
14231 static const struct got_error *
14232 cat_tree(struct got_object_id *id, struct got_repository *repo, FILE *outfile)
14234 const struct got_error *err;
14235 struct got_tree_object *tree;
14236 int nentries, i;
14238 err = got_object_open_as_tree(&tree, repo, id);
14239 if (err)
14240 return err;
14242 nentries = got_object_tree_get_nentries(tree);
14243 for (i = 0; i < nentries; i++) {
14244 struct got_tree_entry *te;
14245 char *id_str;
14246 if (sigint_received || sigpipe_received)
14247 break;
14248 te = got_object_tree_get_entry(tree, i);
14249 err = got_object_id_str(&id_str, got_tree_entry_get_id(te));
14250 if (err)
14251 break;
14252 fprintf(outfile, "%s %.7o %s\n", id_str,
14253 got_tree_entry_get_mode(te),
14254 got_tree_entry_get_name(te));
14255 free(id_str);
14258 got_object_tree_close(tree);
14259 return err;
14262 static const struct got_error *
14263 cat_commit(struct got_object_id *id, struct got_repository *repo, FILE *outfile)
14265 const struct got_error *err;
14266 struct got_commit_object *commit;
14267 const struct got_object_id_queue *parent_ids;
14268 struct got_object_qid *pid;
14269 char *id_str = NULL;
14270 const char *logmsg = NULL;
14271 char gmtoff[6];
14273 err = got_object_open_as_commit(&commit, repo, id);
14274 if (err)
14275 return err;
14277 err = got_object_id_str(&id_str, got_object_commit_get_tree_id(commit));
14278 if (err)
14279 goto done;
14281 fprintf(outfile, "%s%s\n", GOT_COMMIT_LABEL_TREE, id_str);
14282 parent_ids = got_object_commit_get_parent_ids(commit);
14283 fprintf(outfile, "numparents %d\n",
14284 got_object_commit_get_nparents(commit));
14285 STAILQ_FOREACH(pid, parent_ids, entry) {
14286 char *pid_str;
14287 err = got_object_id_str(&pid_str, &pid->id);
14288 if (err)
14289 goto done;
14290 fprintf(outfile, "%s%s\n", GOT_COMMIT_LABEL_PARENT, pid_str);
14291 free(pid_str);
14293 got_date_format_gmtoff(gmtoff, sizeof(gmtoff),
14294 got_object_commit_get_author_gmtoff(commit));
14295 fprintf(outfile, "%s%s %lld %s\n", GOT_COMMIT_LABEL_AUTHOR,
14296 got_object_commit_get_author(commit),
14297 (long long)got_object_commit_get_author_time(commit),
14298 gmtoff);
14300 got_date_format_gmtoff(gmtoff, sizeof(gmtoff),
14301 got_object_commit_get_committer_gmtoff(commit));
14302 fprintf(outfile, "%s%s %lld %s\n", GOT_COMMIT_LABEL_COMMITTER,
14303 got_object_commit_get_committer(commit),
14304 (long long)got_object_commit_get_committer_time(commit),
14305 gmtoff);
14307 logmsg = got_object_commit_get_logmsg_raw(commit);
14308 fprintf(outfile, "messagelen %zd\n", strlen(logmsg));
14309 fprintf(outfile, "%s", logmsg);
14310 done:
14311 free(id_str);
14312 got_object_commit_close(commit);
14313 return err;
14316 static const struct got_error *
14317 cat_tag(struct got_object_id *id, struct got_repository *repo, FILE *outfile)
14319 const struct got_error *err;
14320 struct got_tag_object *tag;
14321 char *id_str = NULL;
14322 const char *tagmsg = NULL;
14323 char gmtoff[6];
14325 err = got_object_open_as_tag(&tag, repo, id);
14326 if (err)
14327 return err;
14329 err = got_object_id_str(&id_str, got_object_tag_get_object_id(tag));
14330 if (err)
14331 goto done;
14333 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_OBJECT, id_str);
14335 switch (got_object_tag_get_object_type(tag)) {
14336 case GOT_OBJ_TYPE_BLOB:
14337 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TYPE,
14338 GOT_OBJ_LABEL_BLOB);
14339 break;
14340 case GOT_OBJ_TYPE_TREE:
14341 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TYPE,
14342 GOT_OBJ_LABEL_TREE);
14343 break;
14344 case GOT_OBJ_TYPE_COMMIT:
14345 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TYPE,
14346 GOT_OBJ_LABEL_COMMIT);
14347 break;
14348 case GOT_OBJ_TYPE_TAG:
14349 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TYPE,
14350 GOT_OBJ_LABEL_TAG);
14351 break;
14352 default:
14353 break;
14356 fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TAG,
14357 got_object_tag_get_name(tag));
14359 got_date_format_gmtoff(gmtoff, sizeof(gmtoff),
14360 got_object_tag_get_tagger_gmtoff(tag));
14361 fprintf(outfile, "%s%s %lld %s\n", GOT_TAG_LABEL_TAGGER,
14362 got_object_tag_get_tagger(tag),
14363 (long long)got_object_tag_get_tagger_time(tag),
14364 gmtoff);
14366 tagmsg = got_object_tag_get_message(tag);
14367 fprintf(outfile, "messagelen %zd\n", strlen(tagmsg));
14368 fprintf(outfile, "%s", tagmsg);
14369 done:
14370 free(id_str);
14371 got_object_tag_close(tag);
14372 return err;
14375 static const struct got_error *
14376 cmd_cat(int argc, char *argv[])
14378 const struct got_error *error;
14379 struct got_repository *repo = NULL;
14380 struct got_worktree *worktree = NULL;
14381 char *cwd = NULL, *repo_path = NULL, *label = NULL;
14382 char *keyword_idstr = NULL;
14383 const char *commit_id_str = NULL;
14384 struct got_object_id *id = NULL, *commit_id = NULL;
14385 struct got_commit_object *commit = NULL;
14386 int ch, obj_type, i, force_path = 0;
14387 struct got_reflist_head refs;
14388 int *pack_fds = NULL;
14390 TAILQ_INIT(&refs);
14392 #ifndef PROFILE
14393 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
14394 NULL) == -1)
14395 err(1, "pledge");
14396 #endif
14398 while ((ch = getopt(argc, argv, "c:Pr:")) != -1) {
14399 switch (ch) {
14400 case 'c':
14401 commit_id_str = optarg;
14402 break;
14403 case 'P':
14404 force_path = 1;
14405 break;
14406 case 'r':
14407 repo_path = realpath(optarg, NULL);
14408 if (repo_path == NULL)
14409 return got_error_from_errno2("realpath",
14410 optarg);
14411 got_path_strip_trailing_slashes(repo_path);
14412 break;
14413 default:
14414 usage_cat();
14415 /* NOTREACHED */
14419 argc -= optind;
14420 argv += optind;
14422 cwd = getcwd(NULL, 0);
14423 if (cwd == NULL) {
14424 error = got_error_from_errno("getcwd");
14425 goto done;
14428 error = got_repo_pack_fds_open(&pack_fds);
14429 if (error != NULL)
14430 goto done;
14432 if (repo_path == NULL) {
14433 error = got_worktree_open(&worktree, cwd, GOT_WORKTREE_GOT_DIR);
14434 if (error && error->code != GOT_ERR_NOT_WORKTREE)
14435 goto done;
14436 if (worktree) {
14437 repo_path = strdup(
14438 got_worktree_get_repo_path(worktree));
14439 if (repo_path == NULL) {
14440 error = got_error_from_errno("strdup");
14441 goto done;
14444 if (commit_id_str == NULL) {
14445 /* Release work tree lock. */
14446 got_worktree_close(worktree);
14447 worktree = NULL;
14452 if (repo_path == NULL) {
14453 repo_path = strdup(cwd);
14454 if (repo_path == NULL)
14455 return got_error_from_errno("strdup");
14458 error = got_repo_open(&repo, repo_path, NULL, pack_fds);
14459 free(repo_path);
14460 if (error != NULL)
14461 goto done;
14463 error = apply_unveil(got_repo_get_path(repo), 1, NULL);
14464 if (error)
14465 goto done;
14467 error = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name, NULL);
14468 if (error)
14469 goto done;
14471 if (commit_id_str != NULL) {
14472 error = got_keyword_to_idstr(&keyword_idstr, commit_id_str,
14473 repo, worktree);
14474 if (error != NULL)
14475 goto done;
14476 if (keyword_idstr != NULL)
14477 commit_id_str = keyword_idstr;
14478 if (worktree != NULL) {
14479 got_worktree_close(worktree);
14480 worktree = NULL;
14482 } else
14483 commit_id_str = GOT_REF_HEAD;
14484 error = got_repo_match_object_id(&commit_id, NULL,
14485 commit_id_str, GOT_OBJ_TYPE_COMMIT, &refs, repo);
14486 if (error)
14487 goto done;
14489 error = got_object_open_as_commit(&commit, repo, commit_id);
14490 if (error)
14491 goto done;
14493 for (i = 0; i < argc; i++) {
14494 if (force_path) {
14495 error = got_object_id_by_path(&id, repo, commit,
14496 argv[i]);
14497 if (error)
14498 break;
14499 } else {
14500 error = got_repo_match_object_id(&id, &label, argv[i],
14501 GOT_OBJ_TYPE_ANY, NULL /* do not resolve tags */,
14502 repo);
14503 if (error) {
14504 if (error->code != GOT_ERR_BAD_OBJ_ID_STR &&
14505 error->code != GOT_ERR_NOT_REF)
14506 break;
14507 error = got_object_id_by_path(&id, repo,
14508 commit, argv[i]);
14509 if (error)
14510 break;
14514 error = got_object_get_type(&obj_type, repo, id);
14515 if (error)
14516 break;
14518 switch (obj_type) {
14519 case GOT_OBJ_TYPE_BLOB:
14520 error = cat_blob(id, repo, stdout);
14521 break;
14522 case GOT_OBJ_TYPE_TREE:
14523 error = cat_tree(id, repo, stdout);
14524 break;
14525 case GOT_OBJ_TYPE_COMMIT:
14526 error = cat_commit(id, repo, stdout);
14527 break;
14528 case GOT_OBJ_TYPE_TAG:
14529 error = cat_tag(id, repo, stdout);
14530 break;
14531 default:
14532 error = got_error(GOT_ERR_OBJ_TYPE);
14533 break;
14535 if (error)
14536 break;
14537 free(label);
14538 label = NULL;
14539 free(id);
14540 id = NULL;
14542 done:
14543 free(cwd);
14544 free(label);
14545 free(id);
14546 free(commit_id);
14547 free(keyword_idstr);
14548 if (commit)
14549 got_object_commit_close(commit);
14550 if (worktree)
14551 got_worktree_close(worktree);
14552 if (repo) {
14553 const struct got_error *close_err = got_repo_close(repo);
14554 if (error == NULL)
14555 error = close_err;
14557 if (pack_fds) {
14558 const struct got_error *pack_err =
14559 got_repo_pack_fds_close(pack_fds);
14560 if (error == NULL)
14561 error = pack_err;
14564 got_ref_list_free(&refs);
14565 return error;
14568 __dead static void
14569 usage_info(void)
14571 fprintf(stderr, "usage: %s info [path ...]\n",
14572 getprogname());
14573 exit(1);
14576 static const struct got_error *
14577 print_path_info(void *arg, const char *path, mode_t mode, time_t mtime,
14578 struct got_object_id *blob_id, struct got_object_id *staged_blob_id,
14579 struct got_object_id *commit_id)
14581 const struct got_error *err = NULL;
14582 char *id_str = NULL;
14583 char datebuf[128];
14584 struct tm mytm, *tm;
14585 struct got_pathlist_head *paths = arg;
14586 struct got_pathlist_entry *pe;
14589 * Clear error indication from any of the path arguments which
14590 * would cause this file index entry to be displayed.
14592 TAILQ_FOREACH(pe, paths, entry) {
14593 if (got_path_cmp(path, pe->path, strlen(path),
14594 pe->path_len) == 0 ||
14595 got_path_is_child(path, pe->path, pe->path_len))
14596 pe->data = NULL; /* no error */
14599 printf(GOT_COMMIT_SEP_STR);
14600 if (S_ISLNK(mode))
14601 printf("symlink: %s\n", path);
14602 else if (S_ISREG(mode)) {
14603 printf("file: %s\n", path);
14604 printf("mode: %o\n", mode & (S_IRWXU | S_IRWXG | S_IRWXO));
14605 } else if (S_ISDIR(mode))
14606 printf("directory: %s\n", path);
14607 else
14608 printf("something: %s\n", path);
14610 tm = localtime_r(&mtime, &mytm);
14611 if (tm == NULL)
14612 return NULL;
14613 if (strftime(datebuf, sizeof(datebuf), "%c %Z", tm) == 0)
14614 return got_error(GOT_ERR_NO_SPACE);
14615 printf("timestamp: %s\n", datebuf);
14617 if (blob_id) {
14618 err = got_object_id_str(&id_str, blob_id);
14619 if (err)
14620 return err;
14621 printf("based on blob: %s\n", id_str);
14622 free(id_str);
14625 if (staged_blob_id) {
14626 err = got_object_id_str(&id_str, staged_blob_id);
14627 if (err)
14628 return err;
14629 printf("based on staged blob: %s\n", id_str);
14630 free(id_str);
14633 if (commit_id) {
14634 err = got_object_id_str(&id_str, commit_id);
14635 if (err)
14636 return err;
14637 printf("based on commit: %s\n", id_str);
14638 free(id_str);
14641 return NULL;
14644 static const struct got_error *
14645 cmd_info(int argc, char *argv[])
14647 const struct got_error *error = NULL;
14648 struct got_repository *repo = NULL;
14649 struct got_worktree *worktree = NULL;
14650 struct got_fileindex *fileindex = NULL;
14651 char *cwd = NULL, *id_str = NULL;
14652 struct got_pathlist_head paths;
14653 char *uuidstr = NULL;
14654 int *pack_fds = NULL;
14655 int ch, show_files = 0;
14657 TAILQ_INIT(&paths);
14659 #ifndef PROFILE
14660 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
14661 NULL) == -1)
14662 err(1, "pledge");
14663 #endif
14665 while ((ch = getopt(argc, argv, "")) != -1) {
14666 switch (ch) {
14667 default:
14668 usage_info();
14669 /* NOTREACHED */
14673 argc -= optind;
14674 argv += optind;
14676 cwd = getcwd(NULL, 0);
14677 if (cwd == NULL) {
14678 error = got_error_from_errno("getcwd");
14679 goto done;
14682 error = got_worktree_open(&worktree, cwd, GOT_WORKTREE_GOT_DIR);
14683 if (error) {
14684 if (error->code == GOT_ERR_NOT_WORKTREE)
14685 error = wrap_not_worktree_error(error, "info", cwd);
14686 goto done;
14689 error = got_repo_pack_fds_open(&pack_fds);
14690 if (error != NULL)
14691 goto done;
14693 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree), NULL,
14694 pack_fds);
14695 if (error)
14696 goto done;
14698 #ifndef PROFILE
14699 /* Remove "wpath cpath proc exec sendfd" promises. */
14700 if (pledge("stdio rpath flock unveil", NULL) == -1)
14701 err(1, "pledge");
14702 #endif
14703 error = apply_unveil(NULL, 0, got_worktree_get_root_path(worktree));
14704 if (error)
14705 goto done;
14707 if (argc >= 1) {
14708 error = get_worktree_paths_from_argv(&paths, argc, argv,
14709 worktree);
14710 if (error)
14711 goto done;
14712 show_files = 1;
14715 error = got_worktree_path_info_prepare(&fileindex, worktree, repo);
14716 if (error)
14717 goto done;
14719 error = got_object_id_str(&id_str,
14720 got_worktree_get_base_commit_id(worktree));
14721 if (error)
14722 goto done;
14724 error = got_worktree_get_uuid(&uuidstr, worktree);
14725 if (error)
14726 goto done;
14728 printf("work tree: %s\n", got_worktree_get_root_path(worktree));
14729 printf("work tree base commit: %s\n", id_str);
14730 printf("work tree path prefix: %s\n",
14731 got_worktree_get_path_prefix(worktree));
14732 printf("work tree branch reference: %s\n",
14733 got_worktree_get_head_ref_name(worktree));
14734 printf("work tree UUID: %s\n", uuidstr);
14735 printf("work tree format version: %d\n",
14736 got_worktree_get_format_version(worktree));
14737 printf("file index version: %u\n",
14738 got_worktree_get_fileindex_version(fileindex));
14739 printf("repository: %s\n", got_worktree_get_repo_path(worktree));
14741 if (show_files) {
14742 struct got_pathlist_entry *pe;
14743 TAILQ_FOREACH(pe, &paths, entry) {
14744 if (pe->path_len == 0)
14745 continue;
14747 * Assume this path will fail. This will be corrected
14748 * in print_path_info() in case the path does suceeed.
14750 pe->data = (void *)got_error(GOT_ERR_BAD_PATH);
14752 error = got_worktree_path_info(worktree, fileindex, &paths,
14753 print_path_info, &paths, check_cancelled, NULL);
14754 if (error)
14755 goto done;
14756 TAILQ_FOREACH(pe, &paths, entry) {
14757 if (pe->data != NULL) {
14758 const struct got_error *perr;
14760 perr = pe->data;
14761 error = got_error_path(pe->path, perr->code);
14762 break;
14766 done:
14767 if (worktree) {
14768 const struct got_error *cerr;
14770 cerr = got_worktree_path_info_complete(fileindex, worktree);
14771 if (error == NULL)
14772 error = cerr;
14773 got_worktree_close(worktree);
14775 if (repo) {
14776 const struct got_error *close_err = got_repo_close(repo);
14777 if (error == NULL)
14778 error = close_err;
14780 if (pack_fds) {
14781 const struct got_error *pack_err =
14782 got_repo_pack_fds_close(pack_fds);
14783 if (error == NULL)
14784 error = pack_err;
14786 got_pathlist_free(&paths, GOT_PATHLIST_FREE_PATH);
14787 free(cwd);
14788 free(id_str);
14789 free(uuidstr);
14790 return error;