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>
23 #include <sys/types.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"
50 #include "got_cancel.h"
51 #include "got_worktree.h"
53 #include "got_commit_graph.h"
54 #include "got_fetch.h"
56 #include "got_blame.h"
57 #include "got_privsep.h"
58 #include "got_opentemp.h"
59 #include "got_gotconfig.h"
61 #include "got_patch.h"
64 #include "got_keyword.h"
67 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
70 #ifndef GOT_DEFAULT_EDITOR
71 #define GOT_DEFAULT_EDITOR "/usr/bin/vi"
74 static volatile sig_atomic_t sigint_received
;
75 static volatile sig_atomic_t sigpipe_received
;
78 catch_sigint(int signo
)
84 catch_sigpipe(int signo
)
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
, "" },
194 list_commands(FILE *fp
)
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
);
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
;
218 int hflag
= 0, Vflag
= 0;
219 static const struct option longopts
[] = {
220 { "version", no_argument
, NULL
, 'V' },
224 setlocale(LC_CTYPE
, "");
226 while ((ch
= getopt_long(argc
, argv
, "+hV", longopts
, NULL
)) != -1) {
246 got_version_print_str();
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)
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
) &&
274 error
->code
== GOT_ERR_ERRNO
&& errno
== EINTR
)) {
276 fprintf(stderr
, "%s: %s\n", getprogname(), error
->msg
);
283 fprintf(stderr
, "%s: unknown command '%s'\n", getprogname(), argv
[0]);
284 list_commands(stderr
);
289 usage(int hflag
, int status
)
291 FILE *fp
= (status
== 0) ? stdout
: stderr
;
293 fprintf(fp
, "usage: %s [-hV] command [arg ...]\n",
300 static const struct got_error
*
301 get_editor(char **abspath
)
303 const struct got_error
*err
= NULL
;
308 editor
= getenv("VISUAL");
310 editor
= getenv("EDITOR");
313 err
= got_path_find_prog(abspath
, editor
);
318 if (*abspath
== NULL
) {
319 *abspath
= strdup(GOT_DEFAULT_EDITOR
);
320 if (*abspath
== NULL
)
321 return got_error_from_errno("strdup");
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
;
334 if (unveil("gmon.out", "rwc") != 0)
335 return got_error_from_errno2("unveil", "gmon.out");
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();
350 if (unveil(NULL
, NULL
) != 0)
351 return got_error_from_errno("unveil");
359 fprintf(stderr
, "usage: %s init [-b branch] repository-path\n",
364 static const struct got_error
*
365 cmd_init(int argc
, char *argv
[])
367 const struct got_error
*error
= NULL
;
368 const char *head_name
= NULL
;
369 char *repo_path
= NULL
;
372 while ((ch
= getopt(argc
, argv
, "b:")) != -1) {
387 if (pledge("stdio rpath wpath cpath unveil", NULL
) == -1)
393 repo_path
= strdup(argv
[0]);
394 if (repo_path
== NULL
)
395 return got_error_from_errno("strdup");
397 got_path_strip_trailing_slashes(repo_path
);
399 error
= got_path_mkdir(repo_path
);
401 !(error
->code
== GOT_ERR_ERRNO
&& errno
== EEXIST
))
404 error
= apply_unveil(repo_path
, 0, NULL
);
408 error
= got_repo_init(repo_path
, head_name
);
417 fprintf(stderr
, "usage: %s import [-b branch] [-I pattern] [-m message] "
418 "[-r repository-path] directory\n", getprogname());
423 spawn_editor(const char *editor
, const char *file
)
426 sig_t sighup
, sigint
, sigquit
;
429 sighup
= signal(SIGHUP
, SIG_IGN
);
430 sigint
= signal(SIGINT
, SIG_IGN
);
431 sigquit
= signal(SIGQUIT
, SIG_IGN
);
433 switch (pid
= fork()) {
437 execl(editor
, editor
, file
, (char *)NULL
);
441 while (waitpid(pid
, &st
, 0) == -1)
446 (void)signal(SIGHUP
, sighup
);
447 (void)signal(SIGINT
, sigint
);
448 (void)signal(SIGQUIT
, sigquit
);
450 if (!WIFEXITED(st
)) {
455 return WEXITSTATUS(st
);
458 static const struct got_error
*
459 read_logmsg(char **logmsg
, size_t *len
, FILE *fp
, size_t filesize
)
461 const struct got_error
*err
= NULL
;
468 if (fseeko(fp
, 0L, SEEK_SET
) == -1)
469 return got_error_from_errno("fseeko");
471 *logmsg
= malloc(filesize
+ 1);
473 return got_error_from_errno("malloc");
476 while (getline(&line
, &linesize
, fp
) != -1) {
477 if (line
[0] == '#' || (*len
== 0 && line
[0] == '\n'))
478 continue; /* remove comments and leading empty lines */
479 *len
= strlcat(*logmsg
, line
, filesize
+ 1);
480 if (*len
>= filesize
+ 1) {
481 err
= got_error(GOT_ERR_NO_SPACE
);
486 err
= got_ferror(fp
, GOT_ERR_IO
);
490 while (*len
> 0 && (*logmsg
)[*len
- 1] == '\n') {
491 (*logmsg
)[*len
- 1] = '\0';
504 static const struct got_error
*
505 edit_logmsg(char **logmsg
, const char *editor
, const char *logmsg_path
,
506 const char *initial_content
, size_t initial_content_len
,
507 int require_modification
)
509 const struct got_error
*err
= NULL
;
516 if (stat(logmsg_path
, &st
) == -1)
517 return got_error_from_errno2("stat", logmsg_path
);
519 if (spawn_editor(editor
, logmsg_path
) == -1)
520 return got_error_from_errno("failed spawning editor");
522 if (require_modification
) {
523 struct timespec timeout
;
527 nanosleep(&timeout
, NULL
);
530 if (stat(logmsg_path
, &st2
) == -1)
531 return got_error_from_errno2("stat", logmsg_path
);
533 if (require_modification
&& st
.st_size
== st2
.st_size
&&
534 timespeccmp(&st
.st_mtim
, &st2
.st_mtim
, ==))
535 return got_error_msg(GOT_ERR_COMMIT_MSG_EMPTY
,
536 "no changes made to commit message, aborting");
538 fp
= fopen(logmsg_path
, "re");
540 err
= got_error_from_errno("fopen");
544 /* strip comments and leading/trailing newlines */
545 err
= read_logmsg(logmsg
, &logmsg_len
, fp
, st2
.st_size
);
548 if (logmsg_len
== 0) {
549 err
= got_error_msg(GOT_ERR_COMMIT_MSG_EMPTY
,
550 "commit message cannot be empty, aborting");
554 if (fp
&& fclose(fp
) == EOF
&& err
== NULL
)
555 err
= got_error_from_errno("fclose");
563 static const struct got_error
*
564 collect_import_msg(char **logmsg
, char **logmsg_path
, const char *editor
,
565 const char *path_dir
, const char *branch_name
)
567 char *initial_content
= NULL
;
568 const struct got_error
*err
= NULL
;
569 int initial_content_len
;
572 initial_content_len
= asprintf(&initial_content
,
573 "\n# %s to be imported to branch %s\n", path_dir
,
575 if (initial_content_len
== -1)
576 return got_error_from_errno("asprintf");
578 err
= got_opentemp_named_fd(logmsg_path
, &fd
,
579 GOT_TMPDIR_STR
"/got-importmsg", "");
583 if (write(fd
, initial_content
, initial_content_len
) == -1) {
584 err
= got_error_from_errno2("write", *logmsg_path
);
587 if (close(fd
) == -1) {
588 err
= got_error_from_errno2("close", *logmsg_path
);
593 err
= edit_logmsg(logmsg
, editor
, *logmsg_path
, initial_content
,
594 initial_content_len
, 1);
596 if (fd
!= -1 && close(fd
) == -1 && err
== NULL
)
597 err
= got_error_from_errno2("close", *logmsg_path
);
598 free(initial_content
);
606 static const struct got_error
*
607 import_progress(void *arg
, const char *path
)
609 printf("A %s\n", path
);
613 static const struct got_error
*
614 valid_author(const char *author
)
616 const char *email
= author
;
619 * Git' expects the author (or committer) to be in the form
620 * "name <email>", which are mostly free form (see the
621 * "committer" description in git-fast-import(1)). We're only
622 * doing this to avoid git's object parser breaking on commits
626 while (*author
&& *author
!= '\n' && *author
!= '<' && *author
!= '>')
628 if (author
!= email
&& *author
== '<' && *(author
- 1) != ' ')
629 return got_error_fmt(GOT_ERR_COMMIT_BAD_AUTHOR
, "%s: space "
630 "between author name and email required", email
);
631 if (*author
++ != '<')
632 return got_error_fmt(GOT_ERR_COMMIT_NO_EMAIL
, "%s", email
);
633 while (*author
&& *author
!= '\n' && *author
!= '<' && *author
!= '>')
635 if (strcmp(author
, ">") != 0)
636 return got_error_fmt(GOT_ERR_COMMIT_NO_EMAIL
, "%s", email
);
640 static const struct got_error
*
641 get_author(char **author
, struct got_repository
*repo
,
642 struct got_worktree
*worktree
)
644 const struct got_error
*err
= NULL
;
645 const char *got_author
= NULL
, *name
, *email
;
646 const struct got_gotconfig
*worktree_conf
= NULL
, *repo_conf
= NULL
;
651 worktree_conf
= got_worktree_get_gotconfig(worktree
);
652 repo_conf
= got_repo_get_gotconfig(repo
);
655 * Priority of potential author information sources, from most
656 * significant to least significant:
657 * 1) work tree's .got/got.conf file
658 * 2) repository's got.conf file
659 * 3) repository's git config file
660 * 4) environment variables
661 * 5) global git config files (in user's home directory or /etc)
665 got_author
= got_gotconfig_get_author(worktree_conf
);
666 if (got_author
== NULL
)
667 got_author
= got_gotconfig_get_author(repo_conf
);
668 if (got_author
== NULL
) {
669 name
= got_repo_get_gitconfig_author_name(repo
);
670 email
= got_repo_get_gitconfig_author_email(repo
);
672 if (asprintf(author
, "%s <%s>", name
, email
) == -1)
673 return got_error_from_errno("asprintf");
677 got_author
= getenv("GOT_AUTHOR");
678 if (got_author
== NULL
) {
679 name
= got_repo_get_global_gitconfig_author_name(repo
);
680 email
= got_repo_get_global_gitconfig_author_email(
683 if (asprintf(author
, "%s <%s>", name
, email
)
685 return got_error_from_errno("asprintf");
688 /* TODO: Look up user in password database? */
689 return got_error(GOT_ERR_COMMIT_NO_AUTHOR
);
693 *author
= strdup(got_author
);
695 return got_error_from_errno("strdup");
697 err
= valid_author(*author
);
705 static const struct got_error
*
706 get_allowed_signers(char **allowed_signers
, struct got_repository
*repo
,
707 struct got_worktree
*worktree
)
709 const char *got_allowed_signers
= NULL
;
710 const struct got_gotconfig
*worktree_conf
= NULL
, *repo_conf
= NULL
;
712 *allowed_signers
= NULL
;
715 worktree_conf
= got_worktree_get_gotconfig(worktree
);
716 repo_conf
= got_repo_get_gotconfig(repo
);
719 * Priority of potential author information sources, from most
720 * significant to least significant:
721 * 1) work tree's .got/got.conf file
722 * 2) repository's got.conf file
726 got_allowed_signers
= got_gotconfig_get_allowed_signers_file(
728 if (got_allowed_signers
== NULL
)
729 got_allowed_signers
= got_gotconfig_get_allowed_signers_file(
732 if (got_allowed_signers
) {
733 *allowed_signers
= strdup(got_allowed_signers
);
734 if (*allowed_signers
== NULL
)
735 return got_error_from_errno("strdup");
740 static const struct got_error
*
741 get_revoked_signers(char **revoked_signers
, struct got_repository
*repo
,
742 struct got_worktree
*worktree
)
744 const char *got_revoked_signers
= NULL
;
745 const struct got_gotconfig
*worktree_conf
= NULL
, *repo_conf
= NULL
;
747 *revoked_signers
= NULL
;
750 worktree_conf
= got_worktree_get_gotconfig(worktree
);
751 repo_conf
= got_repo_get_gotconfig(repo
);
754 * Priority of potential author information sources, from most
755 * significant to least significant:
756 * 1) work tree's .got/got.conf file
757 * 2) repository's got.conf file
761 got_revoked_signers
= got_gotconfig_get_revoked_signers_file(
763 if (got_revoked_signers
== NULL
)
764 got_revoked_signers
= got_gotconfig_get_revoked_signers_file(
767 if (got_revoked_signers
) {
768 *revoked_signers
= strdup(got_revoked_signers
);
769 if (*revoked_signers
== NULL
)
770 return got_error_from_errno("strdup");
776 get_signer_id(struct got_repository
*repo
, struct got_worktree
*worktree
)
778 const char *got_signer_id
= NULL
;
779 const struct got_gotconfig
*worktree_conf
= NULL
, *repo_conf
= NULL
;
782 worktree_conf
= got_worktree_get_gotconfig(worktree
);
783 repo_conf
= got_repo_get_gotconfig(repo
);
786 * Priority of potential author information sources, from most
787 * significant to least significant:
788 * 1) work tree's .got/got.conf file
789 * 2) repository's got.conf file
793 got_signer_id
= got_gotconfig_get_signer_id(worktree_conf
);
794 if (got_signer_id
== NULL
)
795 got_signer_id
= got_gotconfig_get_signer_id(repo_conf
);
797 return got_signer_id
;
800 static const struct got_error
*
801 get_gitconfig_path(char **gitconfig_path
)
803 const char *homedir
= getenv("HOME");
805 *gitconfig_path
= NULL
;
807 if (asprintf(gitconfig_path
, "%s/.gitconfig", homedir
) == -1)
808 return got_error_from_errno("asprintf");
814 static const struct got_error
*
815 cmd_import(int argc
, char *argv
[])
817 const struct got_error
*error
= NULL
;
818 char *path_dir
= NULL
, *repo_path
= NULL
, *logmsg
= NULL
;
819 char *gitconfig_path
= NULL
, *editor
= NULL
, *author
= NULL
;
820 const char *branch_name
= NULL
;
821 char *id_str
= NULL
, *logmsg_path
= NULL
;
822 char refname
[PATH_MAX
] = "refs/heads/";
823 struct got_repository
*repo
= NULL
;
824 struct got_reference
*branch_ref
= NULL
, *head_ref
= NULL
;
825 struct got_object_id
*new_commit_id
= NULL
;
827 struct got_pathlist_head ignores
;
828 struct got_pathlist_entry
*pe
;
829 int preserve_logmsg
= 0;
830 int *pack_fds
= NULL
;
832 TAILQ_INIT(&ignores
);
835 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
841 while ((ch
= getopt(argc
, argv
, "b:I:m:r:")) != -1) {
844 branch_name
= optarg
;
847 if (optarg
[0] == '\0')
849 error
= got_pathlist_insert(&pe
, &ignores
, optarg
,
855 logmsg
= strdup(optarg
);
856 if (logmsg
== NULL
) {
857 error
= got_error_from_errno("strdup");
862 repo_path
= realpath(optarg
, NULL
);
863 if (repo_path
== NULL
) {
864 error
= got_error_from_errno2("realpath",
881 if (repo_path
== NULL
) {
882 repo_path
= getcwd(NULL
, 0);
883 if (repo_path
== NULL
)
884 return got_error_from_errno("getcwd");
886 got_path_strip_trailing_slashes(repo_path
);
887 error
= get_gitconfig_path(&gitconfig_path
);
890 error
= got_repo_pack_fds_open(&pack_fds
);
893 error
= got_repo_open(&repo
, repo_path
, gitconfig_path
, pack_fds
);
897 path_dir
= realpath(argv
[0], NULL
);
898 if (path_dir
== NULL
) {
899 error
= got_error_from_errno2("realpath", argv
[0]);
902 got_path_strip_trailing_slashes(path_dir
);
904 error
= get_editor(&editor
);
908 if (unveil(path_dir
, "r") != 0) {
909 error
= got_error_from_errno2("unveil", path_dir
);
912 if (unveil(editor
, "x") != 0) {
913 error
= got_error_from_errno2("unveil", editor
);
916 error
= apply_unveil(got_repo_get_path(repo
), 0, NULL
);
920 error
= get_author(&author
, repo
, NULL
);
925 * Don't let the user create a branch name with a leading '-'.
926 * While technically a valid reference name, this case is usually
927 * an unintended typo.
929 if (branch_name
&& branch_name
[0] == '-')
930 return got_error_path(branch_name
, GOT_ERR_REF_NAME_MINUS
);
932 error
= got_ref_open(&head_ref
, repo
, GOT_REF_HEAD
, 0);
933 if (error
&& error
->code
!= GOT_ERR_NOT_REF
)
937 n
= strlcat(refname
, branch_name
, sizeof(refname
));
938 else if (head_ref
&& got_ref_is_symbolic(head_ref
))
939 n
= strlcpy(refname
, got_ref_get_symref_target(head_ref
),
942 n
= strlcat(refname
, "main", sizeof(refname
));
943 if (n
>= sizeof(refname
)) {
944 error
= got_error(GOT_ERR_NO_SPACE
);
948 error
= got_ref_open(&branch_ref
, repo
, refname
, 0);
950 if (error
->code
!= GOT_ERR_NOT_REF
)
953 error
= got_error_msg(GOT_ERR_BRANCH_EXISTS
,
954 "import target branch already exists");
958 if (logmsg
== NULL
|| *logmsg
== '\0') {
960 error
= collect_import_msg(&logmsg
, &logmsg_path
, editor
,
963 if (error
->code
!= GOT_ERR_COMMIT_MSG_EMPTY
&&
970 error
= got_repo_import(&new_commit_id
, path_dir
, logmsg
,
971 author
, &ignores
, repo
, import_progress
, NULL
);
978 error
= got_ref_alloc(&branch_ref
, refname
, new_commit_id
);
985 error
= got_ref_write(branch_ref
, repo
);
992 error
= got_object_id_str(&id_str
, new_commit_id
);
999 error
= got_ref_open(&head_ref
, repo
, GOT_REF_HEAD
, 0);
1001 if (error
->code
!= GOT_ERR_NOT_REF
) {
1003 preserve_logmsg
= 1;
1007 error
= got_ref_alloc_symref(&head_ref
, GOT_REF_HEAD
,
1011 preserve_logmsg
= 1;
1015 error
= got_ref_write(head_ref
, repo
);
1018 preserve_logmsg
= 1;
1023 printf("Created branch %s with commit %s\n",
1024 got_ref_get_name(branch_ref
), id_str
);
1027 const struct got_error
*pack_err
=
1028 got_repo_pack_fds_close(pack_fds
);
1033 const struct got_error
*close_err
= got_repo_close(repo
);
1037 if (preserve_logmsg
) {
1038 fprintf(stderr
, "%s: log message preserved in %s\n",
1039 getprogname(), logmsg_path
);
1040 } else if (logmsg_path
&& unlink(logmsg_path
) == -1 && error
== NULL
)
1041 error
= got_error_from_errno2("unlink", logmsg_path
);
1046 free(new_commit_id
);
1049 free(gitconfig_path
);
1051 got_ref_close(branch_ref
);
1053 got_ref_close(head_ref
);
1060 fprintf(stderr
, "usage: %s clone [-almqv] [-b branch] [-R reference] "
1061 "repository-URL [directory]\n", getprogname());
1065 struct got_fetch_progress_arg
{
1066 char last_scaled_size
[FMT_SCALED_STRSIZE
];
1068 int last_p_resolved
;
1071 struct got_repository
*repo
;
1074 int configs_created
;
1076 struct got_pathlist_head
*symrefs
;
1077 struct got_pathlist_head
*wanted_branches
;
1078 struct got_pathlist_head
*wanted_refs
;
1082 const char *remote_repo_path
;
1083 const char *git_url
;
1084 int fetch_all_branches
;
1085 int mirror_references
;
1089 /* XXX forward declaration */
1090 static const struct got_error
*
1091 create_config_files(const char *proto
, const char *host
, const char *port
,
1092 const char *remote_repo_path
, const char *git_url
, int fetch_all_branches
,
1093 int mirror_references
, struct got_pathlist_head
*symrefs
,
1094 struct got_pathlist_head
*wanted_branches
,
1095 struct got_pathlist_head
*wanted_refs
, struct got_repository
*repo
);
1097 static const struct got_error
*
1098 fetch_progress(void *arg
, const char *message
, off_t packfile_size
,
1099 int nobj_total
, int nobj_indexed
, int nobj_loose
, int nobj_resolved
)
1101 const struct got_error
*err
= NULL
;
1102 struct got_fetch_progress_arg
*a
= arg
;
1103 char scaled_size
[FMT_SCALED_STRSIZE
];
1104 int p_indexed
, p_resolved
;
1105 int print_size
= 0, print_indexed
= 0, print_resolved
= 0;
1108 * In order to allow a failed clone to be resumed with 'got fetch'
1109 * we try to create configuration files as soon as possible.
1110 * Once the server has sent information about its default branch
1111 * we have all required information.
1113 if (a
->create_configs
&& !a
->configs_created
&&
1114 !TAILQ_EMPTY(a
->config_info
.symrefs
)) {
1115 err
= create_config_files(a
->config_info
.proto
,
1116 a
->config_info
.host
, a
->config_info
.port
,
1117 a
->config_info
.remote_repo_path
,
1118 a
->config_info
.git_url
,
1119 a
->config_info
.fetch_all_branches
,
1120 a
->config_info
.mirror_references
,
1121 a
->config_info
.symrefs
,
1122 a
->config_info
.wanted_branches
,
1123 a
->config_info
.wanted_refs
, a
->repo
);
1126 a
->configs_created
= 1;
1129 if (a
->verbosity
< 0)
1132 if (message
&& message
[0] != '\0') {
1133 printf("\rserver: %s", message
);
1138 if (packfile_size
> 0 || nobj_indexed
> 0) {
1139 if (fmt_scaled(packfile_size
, scaled_size
) == 0 &&
1140 (a
->last_scaled_size
[0] == '\0' ||
1141 strcmp(scaled_size
, a
->last_scaled_size
)) != 0) {
1143 if (strlcpy(a
->last_scaled_size
, scaled_size
,
1144 FMT_SCALED_STRSIZE
) >= FMT_SCALED_STRSIZE
)
1145 return got_error(GOT_ERR_NO_SPACE
);
1147 if (nobj_indexed
> 0) {
1148 p_indexed
= (nobj_indexed
* 100) / nobj_total
;
1149 if (p_indexed
!= a
->last_p_indexed
) {
1150 a
->last_p_indexed
= p_indexed
;
1155 if (nobj_resolved
> 0) {
1156 p_resolved
= (nobj_resolved
* 100) /
1157 (nobj_total
- nobj_loose
);
1158 if (p_resolved
!= a
->last_p_resolved
) {
1159 a
->last_p_resolved
= p_resolved
;
1167 if (print_size
|| print_indexed
|| print_resolved
)
1170 printf("%*s fetched", FMT_SCALED_STRSIZE
- 2, scaled_size
);
1172 printf("; indexing %d%%", p_indexed
);
1174 printf("; resolving deltas %d%%", p_resolved
);
1175 if (print_size
|| print_indexed
|| print_resolved
)
1181 static const struct got_error
*
1182 create_symref(const char *refname
, struct got_reference
*target_ref
,
1183 int verbosity
, struct got_repository
*repo
)
1185 const struct got_error
*err
;
1186 struct got_reference
*head_symref
;
1188 err
= got_ref_alloc_symref(&head_symref
, refname
, target_ref
);
1192 err
= got_ref_write(head_symref
, repo
);
1193 if (err
== NULL
&& verbosity
> 0) {
1194 printf("Created reference %s: %s\n", GOT_REF_HEAD
,
1195 got_ref_get_name(target_ref
));
1197 got_ref_close(head_symref
);
1201 static const struct got_error
*
1202 list_remote_refs(struct got_pathlist_head
*symrefs
,
1203 struct got_pathlist_head
*refs
)
1205 const struct got_error
*err
;
1206 struct got_pathlist_entry
*pe
;
1208 TAILQ_FOREACH(pe
, symrefs
, entry
) {
1209 const char *refname
= pe
->path
;
1210 const char *targetref
= pe
->data
;
1212 printf("%s: %s\n", refname
, targetref
);
1215 TAILQ_FOREACH(pe
, refs
, entry
) {
1216 const char *refname
= pe
->path
;
1217 struct got_object_id
*id
= pe
->data
;
1220 err
= got_object_id_str(&id_str
, id
);
1223 printf("%s: %s\n", refname
, id_str
);
1230 static const struct got_error
*
1231 create_ref(const char *refname
, struct got_object_id
*id
,
1232 int verbosity
, struct got_repository
*repo
)
1234 const struct got_error
*err
= NULL
;
1235 struct got_reference
*ref
;
1238 err
= got_object_id_str(&id_str
, id
);
1242 err
= got_ref_alloc(&ref
, refname
, id
);
1246 err
= got_ref_write(ref
, repo
);
1249 if (err
== NULL
&& verbosity
>= 0)
1250 printf("Created reference %s: %s\n", refname
, id_str
);
1257 match_wanted_ref(const char *refname
, const char *wanted_ref
)
1259 if (strncmp(refname
, "refs/", 5) != 0)
1264 * Prevent fetching of references that won't make any
1265 * sense outside of the remote repository's context.
1267 if (strncmp(refname
, "got/", 4) == 0)
1269 if (strncmp(refname
, "remotes/", 8) == 0)
1272 if (strncmp(wanted_ref
, "refs/", 5) == 0)
1275 /* Allow prefix match. */
1276 if (got_path_is_child(refname
, wanted_ref
, strlen(wanted_ref
)))
1279 /* Allow exact match. */
1280 return (strcmp(refname
, wanted_ref
) == 0);
1284 is_wanted_ref(struct got_pathlist_head
*wanted_refs
, const char *refname
)
1286 struct got_pathlist_entry
*pe
;
1288 TAILQ_FOREACH(pe
, wanted_refs
, entry
) {
1289 if (match_wanted_ref(refname
, pe
->path
))
1296 static const struct got_error
*
1297 create_wanted_ref(const char *refname
, struct got_object_id
*id
,
1298 const char *remote_repo_name
, int verbosity
, struct got_repository
*repo
)
1300 const struct got_error
*err
;
1301 char *remote_refname
;
1303 if (strncmp("refs/", refname
, 5) == 0)
1306 if (asprintf(&remote_refname
, "refs/remotes/%s/%s",
1307 remote_repo_name
, refname
) == -1)
1308 return got_error_from_errno("asprintf");
1310 err
= create_ref(remote_refname
, id
, verbosity
, repo
);
1311 free(remote_refname
);
1315 static const struct got_error
*
1316 create_gotconfig(const char *proto
, const char *host
, const char *port
,
1317 const char *remote_repo_path
, const char *default_branch
,
1318 int fetch_all_branches
, struct got_pathlist_head
*wanted_branches
,
1319 struct got_pathlist_head
*wanted_refs
, int mirror_references
,
1320 struct got_repository
*repo
)
1322 const struct got_error
*err
= NULL
;
1323 char *gotconfig_path
= NULL
;
1324 char *gotconfig
= NULL
;
1325 FILE *gotconfig_file
= NULL
;
1326 const char *branchname
= NULL
;
1327 char *branches
= NULL
, *refs
= NULL
;
1330 if (!fetch_all_branches
&& !TAILQ_EMPTY(wanted_branches
)) {
1331 struct got_pathlist_entry
*pe
;
1332 TAILQ_FOREACH(pe
, wanted_branches
, entry
) {
1334 branchname
= pe
->path
;
1335 if (strncmp(branchname
, "refs/heads/", 11) == 0)
1337 if (asprintf(&s
, "%s\"%s\" ",
1338 branches
? branches
: "", branchname
) == -1) {
1339 err
= got_error_from_errno("asprintf");
1345 } else if (!fetch_all_branches
&& default_branch
) {
1346 branchname
= default_branch
;
1347 if (strncmp(branchname
, "refs/heads/", 11) == 0)
1349 if (asprintf(&branches
, "\"%s\" ", branchname
) == -1) {
1350 err
= got_error_from_errno("asprintf");
1354 if (!TAILQ_EMPTY(wanted_refs
)) {
1355 struct got_pathlist_entry
*pe
;
1356 TAILQ_FOREACH(pe
, wanted_refs
, entry
) {
1358 const char *refname
= pe
->path
;
1359 if (strncmp(refname
, "refs/", 5) == 0)
1361 if (asprintf(&s
, "%s\"%s\" ",
1362 refs
? refs
: "", refname
) == -1) {
1363 err
= got_error_from_errno("asprintf");
1371 /* Create got.conf(5). */
1372 gotconfig_path
= got_repo_get_path_gotconfig(repo
);
1373 if (gotconfig_path
== NULL
) {
1374 err
= got_error_from_errno("got_repo_get_path_gotconfig");
1377 gotconfig_file
= fopen(gotconfig_path
, "ae");
1378 if (gotconfig_file
== NULL
) {
1379 err
= got_error_from_errno2("fopen", gotconfig_path
);
1382 if (asprintf(&gotconfig
,
1387 "\trepository \"%s\"\n"
1393 GOT_FETCH_DEFAULT_REMOTE_NAME
, host
, proto
,
1394 port
? "\tport " : "", port
? port
: "", port
? "\n" : "",
1395 remote_repo_path
, branches
? "\tbranch { " : "",
1396 branches
? branches
: "", branches
? "}\n" : "",
1397 refs
? "\treference { " : "", refs
? refs
: "", refs
? "}\n" : "",
1398 mirror_references
? "\tmirror_references yes\n" : "",
1399 fetch_all_branches
? "\tfetch_all_branches yes\n" : "") == -1) {
1400 err
= got_error_from_errno("asprintf");
1403 n
= fwrite(gotconfig
, 1, strlen(gotconfig
), gotconfig_file
);
1404 if (n
!= strlen(gotconfig
)) {
1405 err
= got_ferror(gotconfig_file
, GOT_ERR_IO
);
1410 if (gotconfig_file
&& fclose(gotconfig_file
) == EOF
&& err
== NULL
)
1411 err
= got_error_from_errno2("fclose", gotconfig_path
);
1412 free(gotconfig_path
);
1417 static const struct got_error
*
1418 create_gitconfig(const char *git_url
, const char *default_branch
,
1419 int fetch_all_branches
, struct got_pathlist_head
*wanted_branches
,
1420 struct got_pathlist_head
*wanted_refs
, int mirror_references
,
1421 struct got_repository
*repo
)
1423 const struct got_error
*err
= NULL
;
1424 char *gitconfig_path
= NULL
;
1425 char *gitconfig
= NULL
;
1426 FILE *gitconfig_file
= NULL
;
1427 char *branches
= NULL
, *refs
= NULL
;
1428 const char *branchname
;
1431 /* Create a config file Git can understand. */
1432 gitconfig_path
= got_repo_get_path_gitconfig(repo
);
1433 if (gitconfig_path
== NULL
) {
1434 err
= got_error_from_errno("got_repo_get_path_gitconfig");
1437 gitconfig_file
= fopen(gitconfig_path
, "ae");
1438 if (gitconfig_file
== NULL
) {
1439 err
= got_error_from_errno2("fopen", gitconfig_path
);
1442 if (fetch_all_branches
) {
1443 if (mirror_references
) {
1444 if (asprintf(&branches
,
1445 "\tfetch = refs/heads/*:refs/heads/*\n") == -1) {
1446 err
= got_error_from_errno("asprintf");
1449 } else if (asprintf(&branches
,
1450 "\tfetch = refs/heads/*:refs/remotes/%s/*\n",
1451 GOT_FETCH_DEFAULT_REMOTE_NAME
) == -1) {
1452 err
= got_error_from_errno("asprintf");
1455 } else if (!TAILQ_EMPTY(wanted_branches
)) {
1456 struct got_pathlist_entry
*pe
;
1457 TAILQ_FOREACH(pe
, wanted_branches
, entry
) {
1459 branchname
= pe
->path
;
1460 if (strncmp(branchname
, "refs/heads/", 11) == 0)
1462 if (mirror_references
) {
1464 "%s\tfetch = refs/heads/%s:refs/heads/%s\n",
1465 branches
? branches
: "",
1466 branchname
, branchname
) == -1) {
1467 err
= got_error_from_errno("asprintf");
1470 } else if (asprintf(&s
,
1471 "%s\tfetch = refs/heads/%s:refs/remotes/%s/%s\n",
1472 branches
? branches
: "",
1473 branchname
, GOT_FETCH_DEFAULT_REMOTE_NAME
,
1474 branchname
) == -1) {
1475 err
= got_error_from_errno("asprintf");
1483 * If the server specified a default branch, use just that one.
1484 * Otherwise fall back to fetching all branches on next fetch.
1486 if (default_branch
) {
1487 branchname
= default_branch
;
1488 if (strncmp(branchname
, "refs/heads/", 11) == 0)
1491 branchname
= "*"; /* fall back to all branches */
1492 if (mirror_references
) {
1493 if (asprintf(&branches
,
1494 "\tfetch = refs/heads/%s:refs/heads/%s\n",
1495 branchname
, branchname
) == -1) {
1496 err
= got_error_from_errno("asprintf");
1499 } else if (asprintf(&branches
,
1500 "\tfetch = refs/heads/%s:refs/remotes/%s/%s\n",
1501 branchname
, GOT_FETCH_DEFAULT_REMOTE_NAME
,
1502 branchname
) == -1) {
1503 err
= got_error_from_errno("asprintf");
1507 if (!TAILQ_EMPTY(wanted_refs
)) {
1508 struct got_pathlist_entry
*pe
;
1509 TAILQ_FOREACH(pe
, wanted_refs
, entry
) {
1511 const char *refname
= pe
->path
;
1512 if (strncmp(refname
, "refs/", 5) == 0)
1514 if (mirror_references
) {
1516 "%s\tfetch = refs/%s:refs/%s\n",
1517 refs
? refs
: "", refname
, refname
) == -1) {
1518 err
= got_error_from_errno("asprintf");
1521 } else if (asprintf(&s
,
1522 "%s\tfetch = refs/%s:refs/remotes/%s/%s\n",
1524 refname
, GOT_FETCH_DEFAULT_REMOTE_NAME
,
1526 err
= got_error_from_errno("asprintf");
1534 if (asprintf(&gitconfig
,
1539 "\tfetch = refs/tags/*:refs/tags/*\n",
1540 GOT_FETCH_DEFAULT_REMOTE_NAME
, git_url
, branches
? branches
: "",
1541 refs
? refs
: "") == -1) {
1542 err
= got_error_from_errno("asprintf");
1545 n
= fwrite(gitconfig
, 1, strlen(gitconfig
), gitconfig_file
);
1546 if (n
!= strlen(gitconfig
)) {
1547 err
= got_ferror(gitconfig_file
, GOT_ERR_IO
);
1551 if (gitconfig_file
&& fclose(gitconfig_file
) == EOF
&& err
== NULL
)
1552 err
= got_error_from_errno2("fclose", gitconfig_path
);
1553 free(gitconfig_path
);
1558 static const struct got_error
*
1559 create_config_files(const char *proto
, const char *host
, const char *port
,
1560 const char *remote_repo_path
, const char *git_url
, int fetch_all_branches
,
1561 int mirror_references
, struct got_pathlist_head
*symrefs
,
1562 struct got_pathlist_head
*wanted_branches
,
1563 struct got_pathlist_head
*wanted_refs
, struct got_repository
*repo
)
1565 const struct got_error
*err
= NULL
;
1566 const char *default_branch
= NULL
;
1567 struct got_pathlist_entry
*pe
;
1570 * If we asked for a set of wanted branches then use the first
1573 if (!TAILQ_EMPTY(wanted_branches
)) {
1574 pe
= TAILQ_FIRST(wanted_branches
);
1575 default_branch
= pe
->path
;
1577 /* First HEAD ref listed by server is the default branch. */
1578 TAILQ_FOREACH(pe
, symrefs
, entry
) {
1579 const char *refname
= pe
->path
;
1580 const char *target
= pe
->data
;
1582 if (strcmp(refname
, GOT_REF_HEAD
) != 0)
1585 default_branch
= target
;
1590 /* Create got.conf(5). */
1591 err
= create_gotconfig(proto
, host
, port
, remote_repo_path
,
1592 default_branch
, fetch_all_branches
, wanted_branches
,
1593 wanted_refs
, mirror_references
, repo
);
1597 /* Create a config file Git can understand. */
1598 return create_gitconfig(git_url
, default_branch
, fetch_all_branches
,
1599 wanted_branches
, wanted_refs
, mirror_references
, repo
);
1602 static const struct got_error
*
1603 cmd_clone(int argc
, char *argv
[])
1605 const struct got_error
*error
= NULL
;
1606 const char *uri
, *dirname
;
1607 char *proto
, *host
, *port
, *repo_name
, *server_path
;
1608 char *default_destdir
= NULL
, *id_str
= NULL
;
1609 const char *repo_path
;
1610 struct got_repository
*repo
= NULL
;
1611 struct got_pathlist_head refs
, symrefs
, wanted_branches
, wanted_refs
;
1612 struct got_pathlist_entry
*pe
;
1613 struct got_object_id
*pack_hash
= NULL
;
1614 int ch
, fetchfd
= -1, fetchstatus
;
1615 pid_t fetchpid
= -1;
1616 struct got_fetch_progress_arg fpa
;
1617 char *git_url
= NULL
;
1618 int verbosity
= 0, fetch_all_branches
= 0, mirror_references
= 0;
1619 int bflag
= 0, list_refs_only
= 0;
1620 int *pack_fds
= NULL
;
1623 TAILQ_INIT(&symrefs
);
1624 TAILQ_INIT(&wanted_branches
);
1625 TAILQ_INIT(&wanted_refs
);
1627 while ((ch
= getopt(argc
, argv
, "ab:lmqR:v")) != -1) {
1630 fetch_all_branches
= 1;
1633 error
= got_pathlist_append(&wanted_branches
,
1643 mirror_references
= 1;
1649 error
= got_pathlist_append(&wanted_refs
,
1657 else if (verbosity
< 3)
1668 if (fetch_all_branches
&& !TAILQ_EMPTY(&wanted_branches
))
1669 option_conflict('a', 'b');
1670 if (list_refs_only
) {
1671 if (!TAILQ_EMPTY(&wanted_branches
))
1672 option_conflict('l', 'b');
1673 if (fetch_all_branches
)
1674 option_conflict('l', 'a');
1675 if (mirror_references
)
1676 option_conflict('l', 'm');
1677 if (!TAILQ_EMPTY(&wanted_refs
))
1678 option_conflict('l', 'R');
1690 error
= got_dial_parse_uri(&proto
, &host
, &port
, &server_path
,
1695 if (asprintf(&git_url
, "%s://%s%s%s%s%s", proto
,
1696 host
, port
? ":" : "", port
? port
: "",
1697 server_path
[0] != '/' ? "/" : "", server_path
) == -1) {
1698 error
= got_error_from_errno("asprintf");
1702 if (strcmp(proto
, "git") == 0) {
1704 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
1705 "sendfd dns inet unveil", NULL
) == -1)
1708 } else if (strcmp(proto
, "git+ssh") == 0 ||
1709 strcmp(proto
, "ssh") == 0 ||
1710 strcmp(proto
, "git+http") == 0 ||
1711 strcmp(proto
, "http") == 0 ||
1712 strcmp(proto
, "git+https") == 0 ||
1713 strcmp(proto
, "https") == 0) {
1715 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
1716 "sendfd unveil", NULL
) == -1)
1720 error
= got_error_path(proto
, GOT_ERR_BAD_PROTO
);
1723 if (dirname
== NULL
) {
1724 if (asprintf(&default_destdir
, "%s.git", repo_name
) == -1) {
1725 error
= got_error_from_errno("asprintf");
1728 repo_path
= default_destdir
;
1730 repo_path
= dirname
;
1732 if (!list_refs_only
) {
1733 error
= got_path_mkdir(repo_path
);
1735 (!(error
->code
== GOT_ERR_ERRNO
&& errno
== EISDIR
) &&
1736 !(error
->code
== GOT_ERR_ERRNO
&& errno
== EEXIST
)))
1738 if (!got_path_dir_is_empty(repo_path
)) {
1739 error
= got_error_path(repo_path
,
1740 GOT_ERR_DIR_NOT_EMPTY
);
1745 error
= got_dial_apply_unveil(proto
);
1749 error
= apply_unveil(repo_path
, 0, NULL
);
1754 printf("Connecting to %s\n", git_url
);
1756 error
= got_fetch_connect(&fetchpid
, &fetchfd
, proto
, host
, port
,
1757 server_path
, verbosity
);
1762 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd",
1766 if (!list_refs_only
) {
1767 error
= got_repo_init(repo_path
, NULL
);
1770 error
= got_repo_pack_fds_open(&pack_fds
);
1773 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
1778 fpa
.last_scaled_size
[0] = '\0';
1779 fpa
.last_p_indexed
= -1;
1780 fpa
.last_p_resolved
= -1;
1781 fpa
.verbosity
= verbosity
;
1782 fpa
.create_configs
= 1;
1783 fpa
.configs_created
= 0;
1785 fpa
.config_info
.symrefs
= &symrefs
;
1786 fpa
.config_info
.wanted_branches
= &wanted_branches
;
1787 fpa
.config_info
.wanted_refs
= &wanted_refs
;
1788 fpa
.config_info
.proto
= proto
;
1789 fpa
.config_info
.host
= host
;
1790 fpa
.config_info
.port
= port
;
1791 fpa
.config_info
.remote_repo_path
= server_path
;
1792 fpa
.config_info
.git_url
= git_url
;
1793 fpa
.config_info
.fetch_all_branches
= fetch_all_branches
;
1794 fpa
.config_info
.mirror_references
= mirror_references
;
1795 error
= got_fetch_pack(&pack_hash
, &refs
, &symrefs
,
1796 GOT_FETCH_DEFAULT_REMOTE_NAME
, mirror_references
,
1797 fetch_all_branches
, &wanted_branches
, &wanted_refs
,
1798 list_refs_only
, verbosity
, fetchfd
, repo
, NULL
, NULL
, bflag
,
1799 fetch_progress
, &fpa
);
1803 if (list_refs_only
) {
1804 error
= list_remote_refs(&symrefs
, &refs
);
1808 if (pack_hash
== NULL
) {
1809 error
= got_error_fmt(GOT_ERR_FETCH_FAILED
, "%s",
1810 "server sent an empty pack file");
1813 error
= got_object_id_str(&id_str
, pack_hash
);
1817 printf("\nFetched %s.pack\n", id_str
);
1820 /* Set up references provided with the pack file. */
1821 TAILQ_FOREACH(pe
, &refs
, entry
) {
1822 const char *refname
= pe
->path
;
1823 struct got_object_id
*id
= pe
->data
;
1824 char *remote_refname
;
1826 if (is_wanted_ref(&wanted_refs
, refname
) &&
1827 !mirror_references
) {
1828 error
= create_wanted_ref(refname
, id
,
1829 GOT_FETCH_DEFAULT_REMOTE_NAME
,
1830 verbosity
- 1, repo
);
1836 error
= create_ref(refname
, id
, verbosity
- 1, repo
);
1840 if (mirror_references
)
1843 if (strncmp("refs/heads/", refname
, 11) != 0)
1846 if (asprintf(&remote_refname
,
1847 "refs/remotes/%s/%s", GOT_FETCH_DEFAULT_REMOTE_NAME
,
1848 refname
+ 11) == -1) {
1849 error
= got_error_from_errno("asprintf");
1852 error
= create_ref(remote_refname
, id
, verbosity
- 1, repo
);
1853 free(remote_refname
);
1858 /* Set the HEAD reference if the server provided one. */
1859 TAILQ_FOREACH(pe
, &symrefs
, entry
) {
1860 struct got_reference
*target_ref
;
1861 const char *refname
= pe
->path
;
1862 const char *target
= pe
->data
;
1863 char *remote_refname
= NULL
, *remote_target
= NULL
;
1865 if (strcmp(refname
, GOT_REF_HEAD
) != 0)
1868 error
= got_ref_open(&target_ref
, repo
, target
, 0);
1870 if (error
->code
== GOT_ERR_NOT_REF
) {
1877 error
= create_symref(refname
, target_ref
, verbosity
, repo
);
1878 got_ref_close(target_ref
);
1882 if (mirror_references
)
1885 if (strncmp("refs/heads/", target
, 11) != 0)
1888 if (asprintf(&remote_refname
,
1889 "refs/remotes/%s/%s", GOT_FETCH_DEFAULT_REMOTE_NAME
,
1891 error
= got_error_from_errno("asprintf");
1894 if (asprintf(&remote_target
,
1895 "refs/remotes/%s/%s", GOT_FETCH_DEFAULT_REMOTE_NAME
,
1896 target
+ 11) == -1) {
1897 error
= got_error_from_errno("asprintf");
1898 free(remote_refname
);
1901 error
= got_ref_open(&target_ref
, repo
, remote_target
, 0);
1903 free(remote_refname
);
1904 free(remote_target
);
1905 if (error
->code
== GOT_ERR_NOT_REF
) {
1911 error
= create_symref(remote_refname
, target_ref
,
1912 verbosity
- 1, repo
);
1913 free(remote_refname
);
1914 free(remote_target
);
1915 got_ref_close(target_ref
);
1921 * We failed to set the HEAD reference. If we asked for
1922 * a set of wanted branches use the first of one of those
1923 * which could be fetched instead.
1925 TAILQ_FOREACH(pe
, &wanted_branches
, entry
) {
1926 const char *target
= pe
->path
;
1927 struct got_reference
*target_ref
;
1929 error
= got_ref_open(&target_ref
, repo
, target
, 0);
1931 if (error
->code
== GOT_ERR_NOT_REF
) {
1938 error
= create_symref(GOT_REF_HEAD
, target_ref
,
1940 got_ref_close(target_ref
);
1946 if (!fpa
.configs_created
&& pe
!= NULL
) {
1947 error
= create_config_files(fpa
.config_info
.proto
,
1948 fpa
.config_info
.host
, fpa
.config_info
.port
,
1949 fpa
.config_info
.remote_repo_path
,
1950 fpa
.config_info
.git_url
,
1951 fpa
.config_info
.fetch_all_branches
,
1952 fpa
.config_info
.mirror_references
,
1953 fpa
.config_info
.symrefs
,
1954 fpa
.config_info
.wanted_branches
,
1955 fpa
.config_info
.wanted_refs
, fpa
.repo
);
1962 printf("Created %s repository '%s'\n",
1963 mirror_references
? "mirrored" : "cloned", repo_path
);
1966 const struct got_error
*pack_err
=
1967 got_repo_pack_fds_close(pack_fds
);
1972 if (kill(fetchpid
, SIGTERM
) == -1)
1973 error
= got_error_from_errno("kill");
1974 if (waitpid(fetchpid
, &fetchstatus
, 0) == -1 && error
== NULL
)
1975 error
= got_error_from_errno("waitpid");
1977 if (fetchfd
!= -1 && close(fetchfd
) == -1 && error
== NULL
)
1978 error
= got_error_from_errno("close");
1980 const struct got_error
*close_err
= got_repo_close(repo
);
1984 got_pathlist_free(&refs
, GOT_PATHLIST_FREE_ALL
);
1985 got_pathlist_free(&symrefs
, GOT_PATHLIST_FREE_ALL
);
1986 got_pathlist_free(&wanted_branches
, GOT_PATHLIST_FREE_NONE
);
1987 got_pathlist_free(&wanted_refs
, GOT_PATHLIST_FREE_NONE
);
1994 free(default_destdir
);
1999 static const struct got_error
*
2000 update_ref(struct got_reference
*ref
, struct got_object_id
*new_id
,
2001 int replace_tags
, int verbosity
, struct got_repository
*repo
)
2003 const struct got_error
*err
= NULL
;
2004 char *new_id_str
= NULL
;
2005 struct got_object_id
*old_id
= NULL
;
2007 err
= got_object_id_str(&new_id_str
, new_id
);
2011 if (!replace_tags
&&
2012 strncmp(got_ref_get_name(ref
), "refs/tags/", 10) == 0) {
2013 err
= got_ref_resolve(&old_id
, repo
, ref
);
2016 if (got_object_id_cmp(old_id
, new_id
) == 0)
2018 if (verbosity
>= 0) {
2019 printf("Rejecting update of existing tag %s: %s\n",
2020 got_ref_get_name(ref
), new_id_str
);
2025 if (got_ref_is_symbolic(ref
)) {
2026 if (verbosity
>= 0) {
2027 printf("Replacing reference %s: %s\n",
2028 got_ref_get_name(ref
),
2029 got_ref_get_symref_target(ref
));
2031 err
= got_ref_change_symref_to_ref(ref
, new_id
);
2034 err
= got_ref_write(ref
, repo
);
2038 err
= got_ref_resolve(&old_id
, repo
, ref
);
2041 if (got_object_id_cmp(old_id
, new_id
) == 0)
2044 err
= got_ref_change_ref(ref
, new_id
);
2047 err
= got_ref_write(ref
, repo
);
2053 printf("Updated %s: %s\n", got_ref_get_name(ref
),
2061 static const struct got_error
*
2062 update_symref(const char *refname
, struct got_reference
*target_ref
,
2063 int verbosity
, struct got_repository
*repo
)
2065 const struct got_error
*err
= NULL
, *unlock_err
;
2066 struct got_reference
*symref
;
2067 int symref_is_locked
= 0;
2069 err
= got_ref_open(&symref
, repo
, refname
, 1);
2071 if (err
->code
!= GOT_ERR_NOT_REF
)
2073 err
= got_ref_alloc_symref(&symref
, refname
, target_ref
);
2077 err
= got_ref_write(symref
, repo
);
2082 printf("Created reference %s: %s\n",
2083 got_ref_get_name(symref
),
2084 got_ref_get_symref_target(symref
));
2086 symref_is_locked
= 1;
2088 if (strcmp(got_ref_get_symref_target(symref
),
2089 got_ref_get_name(target_ref
)) == 0)
2092 err
= got_ref_change_symref(symref
,
2093 got_ref_get_name(target_ref
));
2097 err
= got_ref_write(symref
, repo
);
2102 printf("Updated %s: %s\n", got_ref_get_name(symref
),
2103 got_ref_get_symref_target(symref
));
2107 if (symref_is_locked
) {
2108 unlock_err
= got_ref_unlock(symref
);
2109 if (unlock_err
&& err
== NULL
)
2112 got_ref_close(symref
);
2119 fprintf(stderr
, "usage: %s fetch [-adlqtvX] [-b branch] "
2120 "[-R reference] [-r repository-path] [remote-repository]\n",
2125 static const struct got_error
*
2126 delete_missing_ref(struct got_reference
*ref
,
2127 int verbosity
, struct got_repository
*repo
)
2129 const struct got_error
*err
= NULL
;
2130 struct got_object_id
*id
= NULL
;
2131 char *id_str
= NULL
;
2133 if (got_ref_is_symbolic(ref
)) {
2134 err
= got_ref_delete(ref
, repo
);
2137 if (verbosity
>= 0) {
2138 printf("Deleted %s: %s\n",
2139 got_ref_get_name(ref
),
2140 got_ref_get_symref_target(ref
));
2143 err
= got_ref_resolve(&id
, repo
, ref
);
2146 err
= got_object_id_str(&id_str
, id
);
2150 err
= got_ref_delete(ref
, repo
);
2153 if (verbosity
>= 0) {
2154 printf("Deleted %s: %s\n",
2155 got_ref_get_name(ref
), id_str
);
2164 static const struct got_error
*
2165 delete_missing_refs(struct got_pathlist_head
*their_refs
,
2166 struct got_pathlist_head
*their_symrefs
,
2167 const struct got_remote_repo
*remote
,
2168 int verbosity
, struct got_repository
*repo
)
2170 const struct got_error
*err
= NULL
, *unlock_err
;
2171 struct got_reflist_head my_refs
;
2172 struct got_reflist_entry
*re
;
2173 struct got_pathlist_entry
*pe
;
2174 char *remote_namespace
= NULL
;
2175 char *local_refname
= NULL
;
2177 TAILQ_INIT(&my_refs
);
2179 if (asprintf(&remote_namespace
, "refs/remotes/%s/", remote
->name
)
2181 return got_error_from_errno("asprintf");
2183 err
= got_ref_list(&my_refs
, repo
, NULL
, got_ref_cmp_by_name
, NULL
);
2187 TAILQ_FOREACH(re
, &my_refs
, entry
) {
2188 const char *refname
= got_ref_get_name(re
->ref
);
2189 const char *their_refname
;
2191 if (remote
->mirror_references
) {
2192 their_refname
= refname
;
2194 if (strncmp(refname
, remote_namespace
,
2195 strlen(remote_namespace
)) == 0) {
2196 if (strcmp(refname
+ strlen(remote_namespace
),
2199 if (asprintf(&local_refname
, "refs/heads/%s",
2200 refname
+ strlen(remote_namespace
)) == -1) {
2201 err
= got_error_from_errno("asprintf");
2204 } else if (strncmp(refname
, "refs/tags/", 10) != 0)
2207 their_refname
= local_refname
;
2210 TAILQ_FOREACH(pe
, their_refs
, entry
) {
2211 if (strcmp(their_refname
, pe
->path
) == 0)
2217 TAILQ_FOREACH(pe
, their_symrefs
, entry
) {
2218 if (strcmp(their_refname
, pe
->path
) == 0)
2224 err
= delete_missing_ref(re
->ref
, verbosity
, repo
);
2228 if (local_refname
) {
2229 struct got_reference
*ref
;
2230 err
= got_ref_open(&ref
, repo
, local_refname
, 1);
2232 if (err
->code
!= GOT_ERR_NOT_REF
)
2234 free(local_refname
);
2235 local_refname
= NULL
;
2238 err
= delete_missing_ref(ref
, verbosity
, repo
);
2241 unlock_err
= got_ref_unlock(ref
);
2243 if (unlock_err
&& err
== NULL
) {
2248 free(local_refname
);
2249 local_refname
= NULL
;
2253 got_ref_list_free(&my_refs
);
2254 free(remote_namespace
);
2255 free(local_refname
);
2259 static const struct got_error
*
2260 update_wanted_ref(const char *refname
, struct got_object_id
*id
,
2261 const char *remote_repo_name
, int verbosity
, struct got_repository
*repo
)
2263 const struct got_error
*err
, *unlock_err
;
2264 char *remote_refname
;
2265 struct got_reference
*ref
;
2267 if (strncmp("refs/", refname
, 5) == 0)
2270 if (asprintf(&remote_refname
, "refs/remotes/%s/%s",
2271 remote_repo_name
, refname
) == -1)
2272 return got_error_from_errno("asprintf");
2274 err
= got_ref_open(&ref
, repo
, remote_refname
, 1);
2276 if (err
->code
!= GOT_ERR_NOT_REF
)
2278 err
= create_ref(remote_refname
, id
, verbosity
, repo
);
2280 err
= update_ref(ref
, id
, 0, verbosity
, repo
);
2281 unlock_err
= got_ref_unlock(ref
);
2282 if (unlock_err
&& err
== NULL
)
2287 free(remote_refname
);
2291 static const struct got_error
*
2292 delete_ref(struct got_repository
*repo
, struct got_reference
*ref
)
2294 const struct got_error
*err
= NULL
;
2295 struct got_object_id
*id
= NULL
;
2296 char *id_str
= NULL
;
2299 if (got_ref_is_symbolic(ref
)) {
2300 target
= got_ref_get_symref_target(ref
);
2302 err
= got_ref_resolve(&id
, repo
, ref
);
2305 err
= got_object_id_str(&id_str
, id
);
2311 err
= got_ref_delete(ref
, repo
);
2315 printf("Deleted %s: %s\n", got_ref_get_name(ref
), target
);
2322 static const struct got_error
*
2323 delete_refs_for_remote(struct got_repository
*repo
, const char *remote_name
)
2325 const struct got_error
*err
= NULL
;
2326 struct got_reflist_head refs
;
2327 struct got_reflist_entry
*re
;
2332 if (asprintf(&prefix
, "refs/remotes/%s", remote_name
) == -1) {
2333 err
= got_error_from_errno("asprintf");
2336 err
= got_ref_list(&refs
, repo
, prefix
, got_ref_cmp_by_name
, NULL
);
2340 TAILQ_FOREACH(re
, &refs
, entry
)
2341 delete_ref(repo
, re
->ref
);
2343 got_ref_list_free(&refs
);
2347 static const struct got_error
*
2348 cmd_fetch(int argc
, char *argv
[])
2350 const struct got_error
*error
= NULL
, *unlock_err
;
2351 char *cwd
= NULL
, *repo_path
= NULL
;
2352 const char *remote_name
;
2353 char *proto
= NULL
, *host
= NULL
, *port
= NULL
;
2354 char *repo_name
= NULL
, *server_path
= NULL
;
2355 const struct got_remote_repo
*remotes
;
2356 struct got_remote_repo
*remote
= NULL
;
2358 char *id_str
= NULL
;
2359 struct got_repository
*repo
= NULL
;
2360 struct got_worktree
*worktree
= NULL
;
2361 const struct got_gotconfig
*repo_conf
= NULL
, *worktree_conf
= NULL
;
2362 struct got_pathlist_head refs
, symrefs
, wanted_branches
, wanted_refs
;
2363 char *head_refname
= NULL
;
2364 struct got_pathlist_entry
*pe
;
2365 struct got_reflist_head remote_refs
;
2366 struct got_reflist_entry
*re
;
2367 struct got_object_id
*pack_hash
= NULL
;
2368 int i
, ch
, fetchfd
= -1, fetchstatus
;
2369 pid_t fetchpid
= -1;
2370 struct got_fetch_progress_arg fpa
;
2371 int verbosity
= 0, fetch_all_branches
= 0, list_refs_only
= 0;
2372 int delete_refs
= 0, replace_tags
= 0, delete_remote
= 0;
2373 int *pack_fds
= NULL
, have_bflag
= 0;
2374 const char *remote_head
= NULL
, *worktree_branch
= NULL
;
2377 TAILQ_INIT(&symrefs
);
2378 TAILQ_INIT(&remote_refs
);
2379 TAILQ_INIT(&wanted_branches
);
2380 TAILQ_INIT(&wanted_refs
);
2382 while ((ch
= getopt(argc
, argv
, "ab:dlqR:r:tvX")) != -1) {
2385 fetch_all_branches
= 1;
2388 error
= got_pathlist_append(&wanted_branches
,
2404 error
= got_pathlist_append(&wanted_refs
,
2410 repo_path
= realpath(optarg
, NULL
);
2411 if (repo_path
== NULL
)
2412 return got_error_from_errno2("realpath",
2414 got_path_strip_trailing_slashes(repo_path
);
2422 else if (verbosity
< 3)
2436 if (fetch_all_branches
&& !TAILQ_EMPTY(&wanted_branches
))
2437 option_conflict('a', 'b');
2438 if (list_refs_only
) {
2439 if (!TAILQ_EMPTY(&wanted_branches
))
2440 option_conflict('l', 'b');
2441 if (fetch_all_branches
)
2442 option_conflict('l', 'a');
2444 option_conflict('l', 'd');
2446 option_conflict('l', 'X');
2448 if (delete_remote
) {
2449 if (fetch_all_branches
)
2450 option_conflict('X', 'a');
2451 if (!TAILQ_EMPTY(&wanted_branches
))
2452 option_conflict('X', 'b');
2454 option_conflict('X', 'd');
2456 option_conflict('X', 't');
2457 if (!TAILQ_EMPTY(&wanted_refs
))
2458 option_conflict('X', 'R');
2463 errx(1, "-X option requires a remote name");
2464 remote_name
= GOT_FETCH_DEFAULT_REMOTE_NAME
;
2465 } else if (argc
== 1)
2466 remote_name
= argv
[0];
2470 cwd
= getcwd(NULL
, 0);
2472 error
= got_error_from_errno("getcwd");
2476 error
= got_repo_pack_fds_open(&pack_fds
);
2480 if (repo_path
== NULL
) {
2481 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_GOT_DIR
);
2482 if (error
&& error
->code
!= GOT_ERR_NOT_WORKTREE
)
2488 strdup(got_worktree_get_repo_path(worktree
));
2489 if (repo_path
== NULL
)
2490 error
= got_error_from_errno("strdup");
2494 repo_path
= strdup(cwd
);
2495 if (repo_path
== NULL
) {
2496 error
= got_error_from_errno("strdup");
2502 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
2506 if (delete_remote
) {
2507 error
= delete_refs_for_remote(repo
, remote_name
);
2508 goto done
; /* nothing else to do */
2512 worktree_conf
= got_worktree_get_gotconfig(worktree
);
2513 if (worktree_conf
) {
2514 got_gotconfig_get_remotes(&nremotes
, &remotes
,
2516 for (i
= 0; i
< nremotes
; i
++) {
2517 if (strcmp(remotes
[i
].name
, remote_name
) == 0) {
2518 error
= got_repo_remote_repo_dup(&remote
,
2527 if (remote
== NULL
) {
2528 repo_conf
= got_repo_get_gotconfig(repo
);
2530 got_gotconfig_get_remotes(&nremotes
, &remotes
,
2532 for (i
= 0; i
< nremotes
; i
++) {
2533 if (strcmp(remotes
[i
].name
, remote_name
) == 0) {
2534 error
= got_repo_remote_repo_dup(&remote
,
2543 if (remote
== NULL
) {
2544 got_repo_get_gitconfig_remotes(&nremotes
, &remotes
, repo
);
2545 for (i
= 0; i
< nremotes
; i
++) {
2546 if (strcmp(remotes
[i
].name
, remote_name
) == 0) {
2547 error
= got_repo_remote_repo_dup(&remote
,
2555 if (remote
== NULL
) {
2556 error
= got_error_path(remote_name
, GOT_ERR_NO_REMOTE
);
2560 if (TAILQ_EMPTY(&wanted_branches
)) {
2561 if (!fetch_all_branches
)
2562 fetch_all_branches
= remote
->fetch_all_branches
;
2563 for (i
= 0; i
< remote
->nfetch_branches
; i
++) {
2564 error
= got_pathlist_append(&wanted_branches
,
2565 remote
->fetch_branches
[i
], NULL
);
2570 if (TAILQ_EMPTY(&wanted_refs
)) {
2571 for (i
= 0; i
< remote
->nfetch_refs
; i
++) {
2572 error
= got_pathlist_append(&wanted_refs
,
2573 remote
->fetch_refs
[i
], NULL
);
2579 error
= got_dial_parse_uri(&proto
, &host
, &port
, &server_path
,
2580 &repo_name
, remote
->fetch_url
);
2584 if (strcmp(proto
, "git") == 0) {
2586 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
2587 "sendfd dns inet unveil", NULL
) == -1)
2590 } else if (strcmp(proto
, "git+ssh") == 0 ||
2591 strcmp(proto
, "ssh") == 0 ||
2592 strcmp(proto
, "git+http") == 0 ||
2593 strcmp(proto
, "http") == 0 ||
2594 strcmp(proto
, "git+https") == 0 ||
2595 strcmp(proto
, "https") == 0) {
2597 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
2598 "sendfd unveil", NULL
) == -1)
2602 error
= got_error_path(proto
, GOT_ERR_BAD_PROTO
);
2606 error
= got_dial_apply_unveil(proto
);
2610 error
= apply_unveil(got_repo_get_path(repo
), 0, NULL
);
2615 head_refname
= strdup(got_worktree_get_head_ref_name(worktree
));
2616 if (head_refname
== NULL
) {
2617 error
= got_error_from_errno("strdup");
2621 /* Release work tree lock. */
2622 got_worktree_close(worktree
);
2626 if (verbosity
>= 0) {
2627 printf("Connecting to \"%s\" %s://%s%s%s%s%s\n",
2628 remote
->name
, proto
, host
,
2629 port
? ":" : "", port
? port
: "",
2630 *server_path
== '/' ? "" : "/", server_path
);
2633 error
= got_fetch_connect(&fetchpid
, &fetchfd
, proto
, host
, port
,
2634 server_path
, verbosity
);
2638 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd",
2644 * If set, get this remote's HEAD ref target so
2645 * if it has changed on the server we can fetch it.
2647 error
= got_ref_list(&remote_refs
, repo
, "refs/remotes",
2648 got_ref_cmp_by_name
, repo
);
2652 TAILQ_FOREACH(re
, &remote_refs
, entry
) {
2653 const char *remote_refname
, *remote_target
;
2654 size_t remote_name_len
;
2656 if (!got_ref_is_symbolic(re
->ref
))
2659 remote_name_len
= strlen(remote
->name
);
2660 remote_refname
= got_ref_get_name(re
->ref
);
2662 /* we only want refs/remotes/$remote->name/HEAD */
2663 if (strncmp(remote_refname
+ 13, remote
->name
,
2664 remote_name_len
) != 0)
2667 if (strcmp(remote_refname
+ remote_name_len
+ 14,
2672 * Take the name itself because we already
2673 * only match with refs/heads/ in fetch_pack().
2675 remote_target
= got_ref_get_symref_target(re
->ref
);
2676 remote_head
= remote_target
+ remote_name_len
+ 14;
2681 strncmp(head_refname
, "refs/heads/", 11) == 0)
2682 worktree_branch
= head_refname
;
2685 fpa
.last_scaled_size
[0] = '\0';
2686 fpa
.last_p_indexed
= -1;
2687 fpa
.last_p_resolved
= -1;
2688 fpa
.verbosity
= verbosity
;
2690 fpa
.create_configs
= 0;
2691 fpa
.configs_created
= 0;
2692 memset(&fpa
.config_info
, 0, sizeof(fpa
.config_info
));
2694 error
= got_fetch_pack(&pack_hash
, &refs
, &symrefs
, remote
->name
,
2695 remote
->mirror_references
, fetch_all_branches
, &wanted_branches
,
2696 &wanted_refs
, list_refs_only
, verbosity
, fetchfd
, repo
,
2697 worktree_branch
, remote_head
, have_bflag
, fetch_progress
, &fpa
);
2701 if (list_refs_only
) {
2702 error
= list_remote_refs(&symrefs
, &refs
);
2706 if (pack_hash
== NULL
) {
2708 printf("Already up-to-date\n");
2709 } else if (verbosity
>= 0) {
2710 error
= got_object_id_str(&id_str
, pack_hash
);
2713 printf("\nFetched %s.pack\n", id_str
);
2718 /* Update references provided with the pack file. */
2719 TAILQ_FOREACH(pe
, &refs
, entry
) {
2720 const char *refname
= pe
->path
;
2721 struct got_object_id
*id
= pe
->data
;
2722 struct got_reference
*ref
;
2723 char *remote_refname
;
2725 if (is_wanted_ref(&wanted_refs
, refname
) &&
2726 !remote
->mirror_references
) {
2727 error
= update_wanted_ref(refname
, id
,
2728 remote
->name
, verbosity
, repo
);
2734 if (remote
->mirror_references
||
2735 strncmp("refs/tags/", refname
, 10) == 0) {
2736 error
= got_ref_open(&ref
, repo
, refname
, 1);
2738 if (error
->code
!= GOT_ERR_NOT_REF
)
2740 error
= create_ref(refname
, id
, verbosity
,
2745 error
= update_ref(ref
, id
, replace_tags
,
2747 unlock_err
= got_ref_unlock(ref
);
2748 if (unlock_err
&& error
== NULL
)
2754 } else if (strncmp("refs/heads/", refname
, 11) == 0) {
2755 if (asprintf(&remote_refname
, "refs/remotes/%s/%s",
2756 remote_name
, refname
+ 11) == -1) {
2757 error
= got_error_from_errno("asprintf");
2761 error
= got_ref_open(&ref
, repo
, remote_refname
, 1);
2763 if (error
->code
!= GOT_ERR_NOT_REF
)
2765 error
= create_ref(remote_refname
, id
,
2770 error
= update_ref(ref
, id
, replace_tags
,
2772 unlock_err
= got_ref_unlock(ref
);
2773 if (unlock_err
&& error
== NULL
)
2780 /* Also create a local branch if none exists yet. */
2781 error
= got_ref_open(&ref
, repo
, refname
, 1);
2783 if (error
->code
!= GOT_ERR_NOT_REF
)
2785 error
= create_ref(refname
, id
, verbosity
,
2790 unlock_err
= got_ref_unlock(ref
);
2791 if (unlock_err
&& error
== NULL
)
2798 error
= delete_missing_refs(&refs
, &symrefs
, remote
,
2804 if (!remote
->mirror_references
) {
2805 /* Update remote HEAD reference if the server provided one. */
2806 TAILQ_FOREACH(pe
, &symrefs
, entry
) {
2807 struct got_reference
*target_ref
;
2808 const char *refname
= pe
->path
;
2809 const char *target
= pe
->data
;
2810 char *remote_refname
= NULL
, *remote_target
= NULL
;
2812 if (strcmp(refname
, GOT_REF_HEAD
) != 0)
2815 if (strncmp("refs/heads/", target
, 11) != 0)
2818 if (asprintf(&remote_refname
, "refs/remotes/%s/%s",
2819 remote
->name
, refname
) == -1) {
2820 error
= got_error_from_errno("asprintf");
2823 if (asprintf(&remote_target
, "refs/remotes/%s/%s",
2824 remote
->name
, target
+ 11) == -1) {
2825 error
= got_error_from_errno("asprintf");
2826 free(remote_refname
);
2830 error
= got_ref_open(&target_ref
, repo
, remote_target
,
2833 free(remote_refname
);
2834 free(remote_target
);
2835 if (error
->code
== GOT_ERR_NOT_REF
) {
2841 error
= update_symref(remote_refname
, target_ref
,
2843 free(remote_refname
);
2844 free(remote_target
);
2845 got_ref_close(target_ref
);
2852 if (kill(fetchpid
, SIGTERM
) == -1)
2853 error
= got_error_from_errno("kill");
2854 if (waitpid(fetchpid
, &fetchstatus
, 0) == -1 && error
== NULL
)
2855 error
= got_error_from_errno("waitpid");
2857 if (fetchfd
!= -1 && close(fetchfd
) == -1 && error
== NULL
)
2858 error
= got_error_from_errno("close");
2860 const struct got_error
*close_err
= got_repo_close(repo
);
2865 got_worktree_close(worktree
);
2867 const struct got_error
*pack_err
=
2868 got_repo_pack_fds_close(pack_fds
);
2872 got_pathlist_free(&refs
, GOT_PATHLIST_FREE_ALL
);
2873 got_pathlist_free(&symrefs
, GOT_PATHLIST_FREE_ALL
);
2874 got_pathlist_free(&wanted_branches
, GOT_PATHLIST_FREE_NONE
);
2875 got_pathlist_free(&wanted_refs
, GOT_PATHLIST_FREE_NONE
);
2876 got_ref_list_free(&remote_refs
);
2877 got_repo_free_remote_repo_data(remote
);
2894 usage_checkout(void)
2896 fprintf(stderr
, "usage: %s checkout [-Eq] [-b branch] [-c commit] "
2897 "[-p path-prefix] repository-path [work-tree-path]\n",
2903 show_worktree_base_ref_warning(void)
2905 fprintf(stderr
, "%s: warning: could not create a reference "
2906 "to the work tree's base commit; the commit could be "
2907 "garbage-collected by Git or 'gotadmin cleanup'; making the "
2908 "repository writable and running 'got update' will prevent this\n",
2912 struct got_checkout_progress_arg
{
2913 const char *worktree_path
;
2914 int had_base_commit_ref_error
;
2918 static const struct got_error
*
2919 checkout_progress(void *arg
, unsigned char status
, const char *path
)
2921 struct got_checkout_progress_arg
*a
= arg
;
2923 /* Base commit bump happens silently. */
2924 if (status
== GOT_STATUS_BUMP_BASE
)
2927 if (status
== GOT_STATUS_BASE_REF_ERR
) {
2928 a
->had_base_commit_ref_error
= 1;
2932 while (path
[0] == '/')
2935 if (a
->verbosity
>= 0)
2936 printf("%c %s/%s\n", status
, a
->worktree_path
, path
);
2941 static const struct got_error
*
2942 check_cancelled(void *arg
)
2944 if (sigint_received
|| sigpipe_received
)
2945 return got_error(GOT_ERR_CANCELLED
);
2949 static const struct got_error
*
2950 check_linear_ancestry(struct got_object_id
*commit_id
,
2951 struct got_object_id
*base_commit_id
, int allow_forwards_in_time_only
,
2952 struct got_repository
*repo
)
2954 const struct got_error
*err
= NULL
;
2955 struct got_object_id
*yca_id
;
2957 err
= got_commit_graph_find_youngest_common_ancestor(&yca_id
,
2958 commit_id
, base_commit_id
, 1, 0, repo
, check_cancelled
, NULL
);
2963 return got_error(GOT_ERR_ANCESTRY
);
2966 * Require a straight line of history between the target commit
2967 * and the work tree's base commit.
2969 * Non-linear situations such as this require a rebase:
2971 * (commit) D F (base_commit)
2979 * 'got update' only handles linear cases:
2980 * Update forwards in time: A (base/yca) - B - C - D (commit)
2981 * Update backwards in time: D (base) - C - B - A (commit/yca)
2983 if (allow_forwards_in_time_only
) {
2984 if (got_object_id_cmp(base_commit_id
, yca_id
) != 0)
2985 return got_error(GOT_ERR_ANCESTRY
);
2986 } else if (got_object_id_cmp(commit_id
, yca_id
) != 0 &&
2987 got_object_id_cmp(base_commit_id
, yca_id
) != 0)
2988 return got_error(GOT_ERR_ANCESTRY
);
2994 static const struct got_error
*
2995 check_same_branch(struct got_object_id
*commit_id
,
2996 struct got_reference
*head_ref
, struct got_repository
*repo
)
2998 const struct got_error
*err
= NULL
;
2999 struct got_commit_graph
*graph
= NULL
;
3000 struct got_object_id
*head_commit_id
= NULL
;
3002 err
= got_ref_resolve(&head_commit_id
, repo
, head_ref
);
3006 if (got_object_id_cmp(head_commit_id
, commit_id
) == 0)
3009 err
= got_commit_graph_open(&graph
, "/", 1);
3013 err
= got_commit_graph_bfsort(graph
, head_commit_id
, repo
,
3014 check_cancelled
, NULL
);
3019 struct got_object_id id
;
3021 err
= got_commit_graph_iter_next(&id
, graph
, repo
,
3022 check_cancelled
, NULL
);
3024 if (err
->code
== GOT_ERR_ITER_COMPLETED
)
3025 err
= got_error(GOT_ERR_ANCESTRY
);
3029 if (got_object_id_cmp(&id
, commit_id
) == 0)
3034 got_commit_graph_close(graph
);
3035 free(head_commit_id
);
3039 static const struct got_error
*
3040 checkout_ancestry_error(struct got_reference
*ref
, const char *commit_id_str
)
3042 static char msg
[512];
3043 const char *branch_name
;
3045 if (got_ref_is_symbolic(ref
))
3046 branch_name
= got_ref_get_symref_target(ref
);
3048 branch_name
= got_ref_get_name(ref
);
3050 if (strncmp("refs/heads/", branch_name
, 11) == 0)
3053 snprintf(msg
, sizeof(msg
),
3054 "target commit is not contained in branch '%s'; "
3055 "the branch to use must be specified with -b; "
3056 "if necessary a new branch can be created for "
3057 "this commit with 'got branch -c %s BRANCH_NAME'",
3058 branch_name
, commit_id_str
);
3060 return got_error_msg(GOT_ERR_ANCESTRY
, msg
);
3063 static const struct got_error
*
3064 cmd_checkout(int argc
, char *argv
[])
3066 const struct got_error
*close_err
, *error
= NULL
;
3067 struct got_repository
*repo
= NULL
;
3068 struct got_reference
*head_ref
= NULL
, *ref
= NULL
;
3069 struct got_worktree
*worktree
= NULL
;
3070 char *repo_path
= NULL
;
3071 char *worktree_path
= NULL
;
3072 const char *path_prefix
= "";
3073 const char *branch_name
= GOT_REF_HEAD
, *refname
= NULL
;
3074 char *commit_id_str
= NULL
, *keyword_idstr
= NULL
;
3075 struct got_object_id
*commit_id
= NULL
;
3077 int ch
, same_path_prefix
, allow_nonempty
= 0, verbosity
= 0;
3078 struct got_pathlist_head paths
;
3079 struct got_checkout_progress_arg cpa
;
3080 int *pack_fds
= NULL
;
3085 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
3086 "unveil", NULL
) == -1)
3090 while ((ch
= getopt(argc
, argv
, "b:c:Ep:q")) != -1) {
3093 branch_name
= optarg
;
3096 commit_id_str
= strdup(optarg
);
3097 if (commit_id_str
== NULL
)
3098 return got_error_from_errno("strdup");
3104 path_prefix
= optarg
;
3119 char *base
, *dotgit
;
3121 repo_path
= realpath(argv
[0], NULL
);
3122 if (repo_path
== NULL
)
3123 return got_error_from_errno2("realpath", argv
[0]);
3124 cwd
= getcwd(NULL
, 0);
3126 error
= got_error_from_errno("getcwd");
3133 error
= got_path_basename(&base
, path
);
3136 dotgit
= strstr(base
, ".git");
3139 if (asprintf(&worktree_path
, "%s/%s", cwd
, base
) == -1) {
3140 error
= got_error_from_errno("asprintf");
3145 } else if (argc
== 2) {
3146 repo_path
= realpath(argv
[0], NULL
);
3147 if (repo_path
== NULL
) {
3148 error
= got_error_from_errno2("realpath", argv
[0]);
3151 worktree_path
= realpath(argv
[1], NULL
);
3152 if (worktree_path
== NULL
) {
3153 if (errno
!= ENOENT
) {
3154 error
= got_error_from_errno2("realpath",
3158 worktree_path
= strdup(argv
[1]);
3159 if (worktree_path
== NULL
) {
3160 error
= got_error_from_errno("strdup");
3167 got_path_strip_trailing_slashes(repo_path
);
3168 got_path_strip_trailing_slashes(worktree_path
);
3170 if (got_path_is_child(worktree_path
, repo_path
, strlen(repo_path
)) ||
3171 got_path_is_child(repo_path
, worktree_path
,
3172 strlen(worktree_path
))) {
3173 error
= got_error_fmt(GOT_ERR_BAD_PATH
,
3174 "work tree and repository paths may not overlap: %s",
3179 error
= got_repo_pack_fds_open(&pack_fds
);
3183 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
3187 /* Pre-create work tree path for unveil(2) */
3188 error
= got_path_mkdir(worktree_path
);
3190 if (!(error
->code
== GOT_ERR_ERRNO
&& errno
== EISDIR
) &&
3191 !(error
->code
== GOT_ERR_ERRNO
&& errno
== EEXIST
))
3193 if (!allow_nonempty
&&
3194 !got_path_dir_is_empty(worktree_path
)) {
3195 error
= got_error_path(worktree_path
,
3196 GOT_ERR_DIR_NOT_EMPTY
);
3201 error
= apply_unveil(got_repo_get_path(repo
), 0, worktree_path
);
3205 error
= got_ref_open(&head_ref
, repo
, branch_name
, 0);
3209 error
= got_worktree_init(worktree_path
, head_ref
, path_prefix
,
3210 GOT_WORKTREE_GOT_DIR
, repo
);
3211 if (error
!= NULL
&& !(error
->code
== GOT_ERR_ERRNO
&& errno
== EEXIST
))
3214 error
= got_worktree_open(&worktree
, worktree_path
,
3215 GOT_WORKTREE_GOT_DIR
);
3219 error
= got_worktree_match_path_prefix(&same_path_prefix
, worktree
,
3223 if (!same_path_prefix
) {
3224 error
= got_error(GOT_ERR_PATH_PREFIX
);
3228 if (commit_id_str
) {
3229 struct got_reflist_head refs
;
3231 error
= got_ref_list(&refs
, repo
, NULL
, got_ref_cmp_by_name
,
3236 error
= got_keyword_to_idstr(&keyword_idstr
, commit_id_str
,
3240 if (keyword_idstr
!= NULL
) {
3241 free(commit_id_str
);
3242 commit_id_str
= keyword_idstr
;
3245 error
= got_repo_match_object_id(&commit_id
, NULL
,
3246 commit_id_str
, GOT_OBJ_TYPE_COMMIT
, &refs
, repo
);
3247 got_ref_list_free(&refs
);
3250 error
= check_linear_ancestry(commit_id
,
3251 got_worktree_get_base_commit_id(worktree
), 0, repo
);
3252 if (error
!= NULL
) {
3253 if (error
->code
== GOT_ERR_ANCESTRY
) {
3254 error
= checkout_ancestry_error(
3255 head_ref
, commit_id_str
);
3259 error
= check_same_branch(commit_id
, head_ref
, repo
);
3261 if (error
->code
== GOT_ERR_ANCESTRY
) {
3262 error
= checkout_ancestry_error(
3263 head_ref
, commit_id_str
);
3267 error
= got_worktree_set_base_commit_id(worktree
, repo
,
3271 /* Expand potentially abbreviated commit ID string. */
3272 free(commit_id_str
);
3273 error
= got_object_id_str(&commit_id_str
, commit_id
);
3277 commit_id
= got_object_id_dup(
3278 got_worktree_get_base_commit_id(worktree
));
3279 if (commit_id
== NULL
) {
3280 error
= got_error_from_errno("got_object_id_dup");
3283 error
= got_object_id_str(&commit_id_str
, commit_id
);
3288 error
= got_pathlist_append(&paths
, "", NULL
);
3291 cpa
.worktree_path
= worktree_path
;
3292 cpa
.had_base_commit_ref_error
= 0;
3293 cpa
.verbosity
= verbosity
;
3294 error
= got_worktree_checkout_files(worktree
, &paths
, repo
,
3295 checkout_progress
, &cpa
, check_cancelled
, NULL
);
3299 if (got_ref_is_symbolic(head_ref
)) {
3300 error
= got_ref_resolve_symbolic(&ref
, repo
, head_ref
);
3303 refname
= got_ref_get_name(ref
);
3305 refname
= got_ref_get_name(head_ref
);
3306 printf("Checked out %s: %s\n", refname
, commit_id_str
);
3307 printf("Now shut up and hack\n");
3308 if (cpa
.had_base_commit_ref_error
)
3309 show_worktree_base_ref_warning();
3312 const struct got_error
*pack_err
=
3313 got_repo_pack_fds_close(pack_fds
);
3318 got_ref_close(head_ref
);
3322 close_err
= got_repo_close(repo
);
3326 if (worktree
!= NULL
) {
3327 close_err
= got_worktree_close(worktree
);
3331 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_NONE
);
3332 free(commit_id_str
);
3335 free(worktree_path
);
3340 struct got_update_progress_arg
{
3352 print_update_progress_stats(struct got_update_progress_arg
*upa
)
3354 if (!upa
->did_something
)
3357 if (upa
->conflicts
> 0)
3358 printf("Files with new merge conflicts: %d\n", upa
->conflicts
);
3359 if (upa
->obstructed
> 0)
3360 printf("File paths obstructed by a non-regular file: %d\n",
3362 if (upa
->not_updated
> 0)
3363 printf("Files not updated because of existing merge "
3364 "conflicts: %d\n", upa
->not_updated
);
3368 * The meaning of some status codes differs between merge-style operations and
3369 * update operations. For example, the ! status code means "file was missing"
3370 * if changes were merged into the work tree, and "missing file was restored"
3371 * if the work tree was updated. This function should be used by any operation
3372 * which merges changes into the work tree without updating the work tree.
3375 print_merge_progress_stats(struct got_update_progress_arg
*upa
)
3377 if (!upa
->did_something
)
3380 if (upa
->conflicts
> 0)
3381 printf("Files with new merge conflicts: %d\n", upa
->conflicts
);
3382 if (upa
->obstructed
> 0)
3383 printf("File paths obstructed by a non-regular file: %d\n",
3385 if (upa
->missing
> 0)
3386 printf("Files which had incoming changes but could not be "
3387 "found in the work tree: %d\n", upa
->missing
);
3388 if (upa
->not_deleted
> 0)
3389 printf("Files not deleted due to differences in deleted "
3390 "content: %d\n", upa
->not_deleted
);
3391 if (upa
->unversioned
> 0)
3392 printf("Files not merged because an unversioned file was "
3393 "found in the work tree: %d\n", upa
->unversioned
);
3399 fprintf(stderr
, "usage: %s update [-q] [-b branch] [-c commit] "
3400 "[path ...]\n", getprogname());
3404 static const struct got_error
*
3405 update_progress(void *arg
, unsigned char status
, const char *path
)
3407 struct got_update_progress_arg
*upa
= arg
;
3409 if (status
== GOT_STATUS_EXISTS
||
3410 status
== GOT_STATUS_BASE_REF_ERR
)
3413 upa
->did_something
= 1;
3415 /* Base commit bump happens silently. */
3416 if (status
== GOT_STATUS_BUMP_BASE
)
3419 if (status
== GOT_STATUS_CONFLICT
)
3421 if (status
== GOT_STATUS_OBSTRUCTED
)
3423 if (status
== GOT_STATUS_CANNOT_UPDATE
)
3425 if (status
== GOT_STATUS_MISSING
)
3427 if (status
== GOT_STATUS_CANNOT_DELETE
)
3429 if (status
== GOT_STATUS_UNVERSIONED
)
3432 while (path
[0] == '/')
3434 if (upa
->verbosity
>= 0)
3435 printf("%c %s\n", status
, path
);
3440 static const struct got_error
*
3441 switch_head_ref(struct got_reference
*head_ref
,
3442 struct got_object_id
*commit_id
, struct got_worktree
*worktree
,
3443 struct got_repository
*repo
)
3445 const struct got_error
*err
= NULL
;
3447 int ref_has_moved
= 0;
3449 /* Trivial case: switching between two different references. */
3450 if (strcmp(got_ref_get_name(head_ref
),
3451 got_worktree_get_head_ref_name(worktree
)) != 0) {
3452 printf("Switching work tree from %s to %s\n",
3453 got_worktree_get_head_ref_name(worktree
),
3454 got_ref_get_name(head_ref
));
3455 return got_worktree_set_head_ref(worktree
, head_ref
);
3458 err
= check_linear_ancestry(commit_id
,
3459 got_worktree_get_base_commit_id(worktree
), 0, repo
);
3461 if (err
->code
!= GOT_ERR_ANCESTRY
)
3468 /* Switching to a rebased branch with the same reference name. */
3469 err
= got_object_id_str(&base_id_str
,
3470 got_worktree_get_base_commit_id(worktree
));
3473 printf("Reference %s now points at a different branch\n",
3474 got_worktree_get_head_ref_name(worktree
));
3475 printf("Switching work tree from %s to %s\n", base_id_str
,
3476 got_worktree_get_head_ref_name(worktree
));
3480 static const struct got_error
*
3481 check_rebase_or_histedit_in_progress(struct got_worktree
*worktree
)
3483 const struct got_error
*err
;
3486 err
= got_worktree_rebase_in_progress(&in_progress
, worktree
);
3490 return got_error(GOT_ERR_REBASING
);
3492 err
= got_worktree_histedit_in_progress(&in_progress
, worktree
);
3496 return got_error(GOT_ERR_HISTEDIT_BUSY
);
3501 static const struct got_error
*
3502 check_merge_in_progress(struct got_worktree
*worktree
,
3503 struct got_repository
*repo
)
3505 const struct got_error
*err
;
3508 err
= got_worktree_merge_in_progress(&in_progress
, worktree
, repo
);
3512 return got_error(GOT_ERR_MERGE_BUSY
);
3517 static const struct got_error
*
3518 get_worktree_paths_from_argv(struct got_pathlist_head
*paths
, int argc
,
3519 char *argv
[], struct got_worktree
*worktree
)
3521 const struct got_error
*err
= NULL
;
3523 struct got_pathlist_entry
*new;
3529 return got_error_from_errno("strdup");
3530 return got_pathlist_append(paths
, path
, NULL
);
3533 for (i
= 0; i
< argc
; i
++) {
3534 err
= got_worktree_resolve_path(&path
, worktree
, argv
[i
]);
3537 err
= got_pathlist_insert(&new, paths
, path
, NULL
);
3538 if (err
|| new == NULL
/* duplicate */) {
3548 static const struct got_error
*
3549 wrap_not_worktree_error(const struct got_error
*orig_err
,
3550 const char *cmdname
, const char *path
)
3552 const struct got_error
*err
;
3553 struct got_repository
*repo
;
3554 static char msg
[512];
3555 int *pack_fds
= NULL
;
3557 err
= got_repo_pack_fds_open(&pack_fds
);
3561 err
= got_repo_open(&repo
, path
, NULL
, pack_fds
);
3565 snprintf(msg
, sizeof(msg
),
3566 "'got %s' needs a work tree in addition to a git repository\n"
3567 "Work trees can be checked out from this Git repository with "
3569 "The got(1) manual page contains more information.", cmdname
);
3570 err
= got_error_msg(GOT_ERR_NOT_WORKTREE
, msg
);
3572 const struct got_error
*close_err
= got_repo_close(repo
);
3577 const struct got_error
*pack_err
=
3578 got_repo_pack_fds_close(pack_fds
);
3585 static const struct got_error
*
3586 cmd_update(int argc
, char *argv
[])
3588 const struct got_error
*close_err
, *error
= NULL
;
3589 struct got_repository
*repo
= NULL
;
3590 struct got_worktree
*worktree
= NULL
;
3591 char *worktree_path
= NULL
;
3592 struct got_object_id
*commit_id
= NULL
;
3593 char *commit_id_str
= NULL
;
3594 const char *branch_name
= NULL
;
3595 struct got_reference
*head_ref
= NULL
;
3596 struct got_pathlist_head paths
;
3597 struct got_pathlist_entry
*pe
;
3598 int ch
, verbosity
= 0;
3599 struct got_update_progress_arg upa
;
3600 int *pack_fds
= NULL
;
3605 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
3606 "unveil", NULL
) == -1)
3610 while ((ch
= getopt(argc
, argv
, "b:c:q")) != -1) {
3613 branch_name
= optarg
;
3616 commit_id_str
= strdup(optarg
);
3617 if (commit_id_str
== NULL
)
3618 return got_error_from_errno("strdup");
3632 worktree_path
= getcwd(NULL
, 0);
3633 if (worktree_path
== NULL
) {
3634 error
= got_error_from_errno("getcwd");
3638 error
= got_repo_pack_fds_open(&pack_fds
);
3642 error
= got_worktree_open(&worktree
, worktree_path
,
3643 GOT_WORKTREE_GOT_DIR
);
3645 if (error
->code
== GOT_ERR_NOT_WORKTREE
)
3646 error
= wrap_not_worktree_error(error
, "update",
3651 error
= check_rebase_or_histedit_in_progress(worktree
);
3655 error
= got_repo_open(&repo
, got_worktree_get_repo_path(worktree
),
3660 error
= apply_unveil(got_repo_get_path(repo
), 0,
3661 got_worktree_get_root_path(worktree
));
3665 error
= check_merge_in_progress(worktree
, repo
);
3669 error
= get_worktree_paths_from_argv(&paths
, argc
, argv
, worktree
);
3673 error
= got_ref_open(&head_ref
, repo
, branch_name
? branch_name
:
3674 got_worktree_get_head_ref_name(worktree
), 0);
3677 if (commit_id_str
== NULL
) {
3678 error
= got_ref_resolve(&commit_id
, repo
, head_ref
);
3681 error
= got_object_id_str(&commit_id_str
, commit_id
);
3685 struct got_reflist_head refs
;
3686 char *keyword_idstr
= NULL
;
3690 error
= got_ref_list(&refs
, repo
, NULL
, got_ref_cmp_by_name
,
3695 error
= got_keyword_to_idstr(&keyword_idstr
, commit_id_str
,
3699 if (keyword_idstr
!= NULL
) {
3700 free(commit_id_str
);
3701 commit_id_str
= keyword_idstr
;
3704 error
= got_repo_match_object_id(&commit_id
, NULL
,
3705 commit_id_str
, GOT_OBJ_TYPE_COMMIT
, &refs
, repo
);
3706 got_ref_list_free(&refs
);
3707 free(commit_id_str
);
3708 commit_id_str
= NULL
;
3711 error
= got_object_id_str(&commit_id_str
, commit_id
);
3717 struct got_object_id
*head_commit_id
;
3718 TAILQ_FOREACH(pe
, &paths
, entry
) {
3719 if (pe
->path_len
== 0)
3721 error
= got_error_msg(GOT_ERR_BAD_PATH
,
3722 "switching between branches requires that "
3723 "the entire work tree gets updated");
3726 error
= got_ref_resolve(&head_commit_id
, repo
, head_ref
);
3729 error
= check_linear_ancestry(commit_id
, head_commit_id
, 0,
3731 free(head_commit_id
);
3734 error
= check_same_branch(commit_id
, head_ref
, repo
);
3737 error
= switch_head_ref(head_ref
, commit_id
, worktree
, repo
);
3741 error
= check_linear_ancestry(commit_id
,
3742 got_worktree_get_base_commit_id(worktree
), 0, repo
);
3743 if (error
!= NULL
) {
3744 if (error
->code
== GOT_ERR_ANCESTRY
)
3745 error
= got_error(GOT_ERR_BRANCH_MOVED
);
3748 error
= check_same_branch(commit_id
, head_ref
, repo
);
3753 if (got_object_id_cmp(got_worktree_get_base_commit_id(worktree
),
3755 error
= got_worktree_set_base_commit_id(worktree
, repo
,
3761 memset(&upa
, 0, sizeof(upa
));
3762 upa
.verbosity
= verbosity
;
3763 error
= got_worktree_checkout_files(worktree
, &paths
, repo
,
3764 update_progress
, &upa
, check_cancelled
, NULL
);
3768 if (upa
.did_something
) {
3769 printf("Updated to %s: %s\n",
3770 got_worktree_get_head_ref_name(worktree
), commit_id_str
);
3772 printf("Already up-to-date\n");
3774 print_update_progress_stats(&upa
);
3777 const struct got_error
*pack_err
=
3778 got_repo_pack_fds_close(pack_fds
);
3783 close_err
= got_repo_close(repo
);
3787 if (worktree
!= NULL
) {
3788 close_err
= got_worktree_close(worktree
);
3792 if (head_ref
!= NULL
)
3793 got_ref_close(head_ref
);
3794 free(worktree_path
);
3795 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_PATH
);
3797 free(commit_id_str
);
3801 static const struct got_error
*
3802 diff_blobs(struct got_object_id
*blob_id1
, struct got_object_id
*blob_id2
,
3803 const char *path
, int diff_context
, int ignore_whitespace
,
3804 int force_text_diff
, struct got_diffstat_cb_arg
*dsa
,
3805 struct got_repository
*repo
, FILE *outfile
)
3807 const struct got_error
*err
= NULL
;
3808 struct got_blob_object
*blob1
= NULL
, *blob2
= NULL
;
3809 FILE *f1
= NULL
, *f2
= NULL
;
3810 int fd1
= -1, fd2
= -1;
3812 fd1
= got_opentempfd();
3814 return got_error_from_errno("got_opentempfd");
3815 fd2
= got_opentempfd();
3817 err
= got_error_from_errno("got_opentempfd");
3822 err
= got_object_open_as_blob(&blob1
, repo
, blob_id1
, 8192,
3828 err
= got_object_open_as_blob(&blob2
, repo
, blob_id2
, 8192, fd2
);
3832 f1
= got_opentemp();
3834 err
= got_error_from_errno("got_opentemp");
3837 f2
= got_opentemp();
3839 err
= got_error_from_errno("got_opentemp");
3843 while (path
[0] == '/')
3845 err
= got_diff_blob(NULL
, NULL
, blob1
, blob2
, f1
, f2
, path
, path
,
3846 GOT_DIFF_ALGORITHM_PATIENCE
, diff_context
, ignore_whitespace
,
3847 force_text_diff
, dsa
, outfile
);
3849 if (fd1
!= -1 && close(fd1
) == -1 && err
== NULL
)
3850 err
= got_error_from_errno("close");
3852 got_object_blob_close(blob1
);
3853 if (fd2
!= -1 && close(fd2
) == -1 && err
== NULL
)
3854 err
= got_error_from_errno("close");
3856 got_object_blob_close(blob2
);
3857 if (f1
&& fclose(f1
) == EOF
&& err
== NULL
)
3858 err
= got_error_from_errno("fclose");
3859 if (f2
&& fclose(f2
) == EOF
&& err
== NULL
)
3860 err
= got_error_from_errno("fclose");
3864 static const struct got_error
*
3865 diff_trees(struct got_object_id
*tree_id1
, struct got_object_id
*tree_id2
,
3866 const char *path
, int diff_context
, int ignore_whitespace
,
3867 int force_text_diff
, struct got_diffstat_cb_arg
*dsa
,
3868 struct got_repository
*repo
, FILE *outfile
)
3870 const struct got_error
*err
= NULL
;
3871 struct got_tree_object
*tree1
= NULL
, *tree2
= NULL
;
3872 struct got_diff_blob_output_unidiff_arg arg
;
3873 FILE *f1
= NULL
, *f2
= NULL
;
3874 int fd1
= -1, fd2
= -1;
3877 err
= got_object_open_as_tree(&tree1
, repo
, tree_id1
);
3880 fd1
= got_opentempfd();
3882 err
= got_error_from_errno("got_opentempfd");
3887 err
= got_object_open_as_tree(&tree2
, repo
, tree_id2
);
3891 f1
= got_opentemp();
3893 err
= got_error_from_errno("got_opentemp");
3897 f2
= got_opentemp();
3899 err
= got_error_from_errno("got_opentemp");
3902 fd2
= got_opentempfd();
3904 err
= got_error_from_errno("got_opentempfd");
3907 arg
.diff_context
= diff_context
;
3908 arg
.ignore_whitespace
= ignore_whitespace
;
3909 arg
.force_text_diff
= force_text_diff
;
3911 arg
.diff_algo
= GOT_DIFF_ALGORITHM_PATIENCE
;
3912 arg
.outfile
= outfile
;
3915 while (path
[0] == '/')
3917 err
= got_diff_tree(tree1
, tree2
, f1
, f2
, fd1
, fd2
, path
, path
, repo
,
3918 got_diff_blob_output_unidiff
, &arg
, 1);
3921 got_object_tree_close(tree1
);
3923 got_object_tree_close(tree2
);
3924 if (f1
&& fclose(f1
) == EOF
&& err
== NULL
)
3925 err
= got_error_from_errno("fclose");
3926 if (f2
&& fclose(f2
) == EOF
&& err
== NULL
)
3927 err
= got_error_from_errno("fclose");
3928 if (fd1
!= -1 && close(fd1
) == -1 && err
== NULL
)
3929 err
= got_error_from_errno("close");
3930 if (fd2
!= -1 && close(fd2
) == -1 && err
== NULL
)
3931 err
= got_error_from_errno("close");
3935 static const struct got_error
*
3936 get_changed_paths(struct got_pathlist_head
*paths
,
3937 struct got_commit_object
*commit
, struct got_repository
*repo
,
3938 struct got_diffstat_cb_arg
*dsa
)
3940 const struct got_error
*err
= NULL
;
3941 struct got_object_id
*tree_id1
= NULL
, *tree_id2
= NULL
;
3942 struct got_tree_object
*tree1
= NULL
, *tree2
= NULL
;
3943 struct got_object_qid
*qid
;
3944 got_diff_blob_cb cb
= got_diff_tree_collect_changed_paths
;
3945 FILE *f1
= NULL
, *f2
= NULL
;
3946 int fd1
= -1, fd2
= -1;
3949 cb
= got_diff_tree_compute_diffstat
;
3951 f1
= got_opentemp();
3953 err
= got_error_from_errno("got_opentemp");
3956 f2
= got_opentemp();
3958 err
= got_error_from_errno("got_opentemp");
3961 fd1
= got_opentempfd();
3963 err
= got_error_from_errno("got_opentempfd");
3966 fd2
= got_opentempfd();
3968 err
= got_error_from_errno("got_opentempfd");
3973 qid
= STAILQ_FIRST(got_object_commit_get_parent_ids(commit
));
3975 struct got_commit_object
*pcommit
;
3976 err
= got_object_open_as_commit(&pcommit
, repo
,
3981 tree_id1
= got_object_id_dup(
3982 got_object_commit_get_tree_id(pcommit
));
3983 if (tree_id1
== NULL
) {
3984 got_object_commit_close(pcommit
);
3985 return got_error_from_errno("got_object_id_dup");
3987 got_object_commit_close(pcommit
);
3992 err
= got_object_open_as_tree(&tree1
, repo
, tree_id1
);
3997 tree_id2
= got_object_commit_get_tree_id(commit
);
3998 err
= got_object_open_as_tree(&tree2
, repo
, tree_id2
);
4002 err
= got_diff_tree(tree1
, tree2
, f1
, f2
, fd1
, fd2
, "", "", repo
,
4003 cb
, dsa
? (void *)dsa
: paths
, dsa
? 1 : 0);
4006 got_object_tree_close(tree1
);
4008 got_object_tree_close(tree2
);
4009 if (fd1
!= -1 && close(fd1
) == -1 && err
== NULL
)
4010 err
= got_error_from_errno("close");
4011 if (fd2
!= -1 && close(fd2
) == -1 && err
== NULL
)
4012 err
= got_error_from_errno("close");
4013 if (f1
&& fclose(f1
) == EOF
&& err
== NULL
)
4014 err
= got_error_from_errno("fclose");
4015 if (f2
&& fclose(f2
) == EOF
&& err
== NULL
)
4016 err
= got_error_from_errno("fclose");
4021 static const struct got_error
*
4022 print_patch(struct got_commit_object
*commit
, struct got_object_id
*id
,
4023 const char *path
, int diff_context
, struct got_diffstat_cb_arg
*dsa
,
4024 struct got_repository
*repo
, FILE *outfile
)
4026 const struct got_error
*err
= NULL
;
4027 struct got_commit_object
*pcommit
= NULL
;
4028 char *id_str1
= NULL
, *id_str2
= NULL
;
4029 struct got_object_id
*obj_id1
= NULL
, *obj_id2
= NULL
;
4030 struct got_object_qid
*qid
;
4032 qid
= STAILQ_FIRST(got_object_commit_get_parent_ids(commit
));
4034 err
= got_object_open_as_commit(&pcommit
, repo
,
4038 err
= got_object_id_str(&id_str1
, &qid
->id
);
4043 err
= got_object_id_str(&id_str2
, id
);
4047 if (path
&& path
[0] != '\0') {
4049 err
= got_object_id_by_path(&obj_id2
, repo
, commit
, path
);
4053 err
= got_object_id_by_path(&obj_id1
, repo
,
4056 if (err
->code
!= GOT_ERR_NO_TREE_ENTRY
) {
4062 err
= got_object_get_type(&obj_type
, repo
, obj_id2
);
4068 "diff %s %s\n", id_str1
? id_str1
: "/dev/null", id_str2
);
4069 fprintf(outfile
, "commit - %s\n",
4070 id_str1
? id_str1
: "/dev/null");
4071 fprintf(outfile
, "commit + %s\n", id_str2
);
4073 case GOT_OBJ_TYPE_BLOB
:
4074 err
= diff_blobs(obj_id1
, obj_id2
, path
, diff_context
,
4075 0, 0, dsa
, repo
, outfile
);
4077 case GOT_OBJ_TYPE_TREE
:
4078 err
= diff_trees(obj_id1
, obj_id2
, path
, diff_context
,
4079 0, 0, dsa
, repo
, outfile
);
4082 err
= got_error(GOT_ERR_OBJ_TYPE
);
4088 obj_id2
= got_object_commit_get_tree_id(commit
);
4090 obj_id1
= got_object_commit_get_tree_id(pcommit
);
4092 "diff %s %s\n", id_str1
? id_str1
: "/dev/null", id_str2
);
4093 fprintf(outfile
, "commit - %s\n",
4094 id_str1
? id_str1
: "/dev/null");
4095 fprintf(outfile
, "commit + %s\n", id_str2
);
4096 err
= diff_trees(obj_id1
, obj_id2
, "", diff_context
, 0, 0,
4097 dsa
, repo
, outfile
);
4103 got_object_commit_close(pcommit
);
4108 get_datestr(time_t *time
, char *datebuf
)
4110 struct tm mytm
, *tm
;
4113 tm
= gmtime_r(time
, &mytm
);
4116 s
= asctime_r(tm
, datebuf
);
4119 p
= strchr(s
, '\n');
4125 static const struct got_error
*
4126 match_commit(int *have_match
, struct got_object_id
*id
,
4127 struct got_commit_object
*commit
, regex_t
*regex
)
4129 const struct got_error
*err
= NULL
;
4130 regmatch_t regmatch
;
4131 char *id_str
= NULL
, *logmsg
= NULL
;
4135 err
= got_object_id_str(&id_str
, id
);
4139 err
= got_object_commit_get_logmsg(&logmsg
, commit
);
4143 if (regexec(regex
, got_object_commit_get_author(commit
), 1,
4144 ®match
, 0) == 0 ||
4145 regexec(regex
, got_object_commit_get_committer(commit
), 1,
4146 ®match
, 0) == 0 ||
4147 regexec(regex
, id_str
, 1, ®match
, 0) == 0 ||
4148 regexec(regex
, logmsg
, 1, ®match
, 0) == 0)
4157 match_changed_paths(int *have_match
, struct got_pathlist_head
*changed_paths
,
4160 regmatch_t regmatch
;
4161 struct got_pathlist_entry
*pe
;
4165 TAILQ_FOREACH(pe
, changed_paths
, entry
) {
4166 if (regexec(regex
, pe
->path
, 1, ®match
, 0) == 0) {
4173 static const struct got_error
*
4174 match_patch(int *have_match
, struct got_commit_object
*commit
,
4175 struct got_object_id
*id
, const char *path
, int diff_context
,
4176 struct got_repository
*repo
, regex_t
*regex
, FILE *f
)
4178 const struct got_error
*err
= NULL
;
4180 size_t linesize
= 0;
4181 regmatch_t regmatch
;
4185 err
= got_opentemp_truncate(f
);
4189 err
= print_patch(commit
, id
, path
, diff_context
, NULL
, repo
, f
);
4193 if (fseeko(f
, 0L, SEEK_SET
) == -1) {
4194 err
= got_error_from_errno("fseeko");
4198 while (getline(&line
, &linesize
, f
) != -1) {
4199 if (regexec(regex
, line
, 1, ®match
, 0) == 0) {
4209 #define GOT_COMMIT_SEP_STR "-----------------------------------------------\n"
4211 static const struct got_error
*
4212 build_refs_str(char **refs_str
, struct got_reflist_head
*refs
,
4213 struct got_object_id
*id
, struct got_repository
*repo
,
4216 static const struct got_error
*err
= NULL
;
4217 struct got_reflist_entry
*re
;
4223 TAILQ_FOREACH(re
, refs
, entry
) {
4224 struct got_tag_object
*tag
= NULL
;
4225 struct got_object_id
*ref_id
;
4228 name
= got_ref_get_name(re
->ref
);
4229 if (strcmp(name
, GOT_REF_HEAD
) == 0)
4231 if (strncmp(name
, "refs/", 5) == 0)
4233 if (strncmp(name
, "got/", 4) == 0)
4235 if (strncmp(name
, "heads/", 6) == 0)
4237 if (strncmp(name
, "remotes/", 8) == 0) {
4241 s
= strstr(name
, "/" GOT_REF_HEAD
);
4242 if (s
!= NULL
&& strcmp(s
, "/" GOT_REF_HEAD
) == 0)
4245 err
= got_ref_resolve(&ref_id
, repo
, re
->ref
);
4248 if (strncmp(name
, "tags/", 5) == 0) {
4249 err
= got_object_open_as_tag(&tag
, repo
, ref_id
);
4251 if (err
->code
!= GOT_ERR_OBJ_TYPE
) {
4255 /* Ref points at something other than a tag. */
4260 cmp
= got_object_id_cmp(tag
?
4261 got_object_tag_get_object_id(tag
) : ref_id
, id
);
4264 got_object_tag_close(tag
);
4268 if (asprintf(refs_str
, "%s%s%s", s
? s
: "",
4269 s
? ", " : "", name
) == -1) {
4270 err
= got_error_from_errno("asprintf");
4281 static const struct got_error
*
4282 print_commit_oneline(struct got_commit_object
*commit
, struct got_object_id
*id
,
4283 struct got_repository
*repo
, struct got_reflist_object_id_map
*refs_idmap
)
4285 const struct got_error
*err
= NULL
;
4286 char *ref_str
= NULL
, *id_str
= NULL
, *logmsg0
= NULL
;
4287 char *comma
, *s
, *nl
;
4288 struct got_reflist_head
*refs
;
4289 char datebuf
[12]; /* YYYY-MM-DD + SPACE + NUL */
4291 time_t committer_time
;
4293 refs
= got_reflist_object_id_map_lookup(refs_idmap
, id
);
4295 err
= build_refs_str(&ref_str
, refs
, id
, repo
, 1);
4299 /* Display the first matching ref only. */
4300 if (ref_str
&& (comma
= strchr(ref_str
, ',')) != NULL
)
4304 if (ref_str
== NULL
) {
4305 err
= got_object_id_str(&id_str
, id
);
4310 committer_time
= got_object_commit_get_committer_time(commit
);
4311 if (gmtime_r(&committer_time
, &tm
) == NULL
) {
4312 err
= got_error_from_errno("gmtime_r");
4315 if (strftime(datebuf
, sizeof(datebuf
), "%F ", &tm
) == 0) {
4316 err
= got_error(GOT_ERR_NO_SPACE
);
4320 err
= got_object_commit_get_logmsg(&logmsg0
, commit
);
4325 while (isspace((unsigned char)s
[0]))
4328 nl
= strchr(s
, '\n');
4334 printf("%s%-7s %s\n", datebuf
, ref_str
, s
);
4336 printf("%s%.7s %s\n", datebuf
, id_str
, s
);
4338 if (fflush(stdout
) != 0 && err
== NULL
)
4339 err
= got_error_from_errno("fflush");
4347 static const struct got_error
*
4348 print_diffstat(struct got_diffstat_cb_arg
*dsa
, const char *header
)
4350 struct got_pathlist_entry
*pe
;
4353 printf("%s\n", header
);
4355 TAILQ_FOREACH(pe
, dsa
->paths
, entry
) {
4356 struct got_diff_changed_path
*cp
= pe
->data
;
4357 int pad
= dsa
->max_path_len
- pe
->path_len
+ 1;
4359 printf(" %c %s%*c | %*d+ %*d-\n", cp
->status
, pe
->path
, pad
,
4360 ' ', dsa
->add_cols
+ 1, cp
->add
, dsa
->rm_cols
+ 1, cp
->rm
);
4362 printf("\n%d file%s changed, %d insertion%s(+), %d deletion%s(-)\n\n",
4363 dsa
->nfiles
, dsa
->nfiles
> 1 ? "s" : "", dsa
->ins
,
4364 dsa
->ins
!= 1 ? "s" : "", dsa
->del
, dsa
->del
!= 1 ? "s" : "");
4366 if (fflush(stdout
) != 0)
4367 return got_error_from_errno("fflush");
4372 static const struct got_error
*
4378 if (fseeko(f
, 0L, SEEK_SET
) == -1)
4379 return got_error_from_errno("fseek");
4382 r
= fread(buf
, 1, sizeof(buf
), f
);
4385 return got_error_from_errno("fread");
4389 if (fwrite(buf
, 1, r
, stdout
) != r
)
4390 return got_ferror(stdout
, GOT_ERR_IO
);
4396 static const struct got_error
*
4397 print_commit(struct got_commit_object
*commit
, struct got_object_id
*id
,
4398 struct got_repository
*repo
, const char *path
,
4399 struct got_pathlist_head
*changed_paths
,
4400 struct got_diffstat_cb_arg
*diffstat
, int show_patch
, int diff_context
,
4401 struct got_reflist_object_id_map
*refs_idmap
, const char *custom_refs_str
,
4404 const struct got_error
*err
= NULL
;
4406 char *id_str
, *datestr
, *logmsg0
, *logmsg
, *line
;
4408 time_t committer_time
;
4409 const char *author
, *committer
;
4410 char *refs_str
= NULL
;
4412 err
= got_object_id_str(&id_str
, id
);
4416 if (custom_refs_str
== NULL
) {
4417 struct got_reflist_head
*refs
;
4418 refs
= got_reflist_object_id_map_lookup(refs_idmap
, id
);
4420 err
= build_refs_str(&refs_str
, refs
, id
, repo
, 0);
4426 printf(GOT_COMMIT_SEP_STR
);
4427 if (custom_refs_str
)
4428 printf("%s %s (%s)\n", prefix
? prefix
: "commit", id_str
,
4431 printf("%s %s%s%s%s\n", prefix
? prefix
: "commit", id_str
,
4432 refs_str
? " (" : "", refs_str
? refs_str
: "",
4433 refs_str
? ")" : "");
4438 printf("from: %s\n", got_object_commit_get_author(commit
));
4439 author
= got_object_commit_get_author(commit
);
4440 committer
= got_object_commit_get_committer(commit
);
4441 if (strcmp(author
, committer
) != 0)
4442 printf("via: %s\n", committer
);
4443 committer_time
= got_object_commit_get_committer_time(commit
);
4444 datestr
= get_datestr(&committer_time
, datebuf
);
4446 printf("date: %s UTC\n", datestr
);
4447 if (got_object_commit_get_nparents(commit
) > 1) {
4448 const struct got_object_id_queue
*parent_ids
;
4449 struct got_object_qid
*qid
;
4451 parent_ids
= got_object_commit_get_parent_ids(commit
);
4452 STAILQ_FOREACH(qid
, parent_ids
, entry
) {
4453 err
= got_object_id_str(&id_str
, &qid
->id
);
4456 printf("parent %d: %s\n", n
++, id_str
);
4462 err
= got_object_commit_get_logmsg(&logmsg0
, commit
);
4468 line
= strsep(&logmsg
, "\n");
4470 printf(" %s\n", line
);
4474 if (changed_paths
&& diffstat
== NULL
) {
4475 struct got_pathlist_entry
*pe
;
4477 TAILQ_FOREACH(pe
, changed_paths
, entry
) {
4478 struct got_diff_changed_path
*cp
= pe
->data
;
4480 printf(" %c %s\n", cp
->status
, pe
->path
);
4488 err
= got_error_from_errno("got_opentemp");
4493 err
= print_patch(commit
, id
, path
, diff_context
, diffstat
,
4494 repo
, diffstat
== NULL
? stdout
: f
);
4499 err
= print_diffstat(diffstat
, NULL
);
4511 if (fflush(stdout
) != 0 && err
== NULL
)
4512 err
= got_error_from_errno("fflush");
4514 if (f
&& fclose(f
) == EOF
&& err
== NULL
)
4515 err
= got_error_from_errno("fclose");
4521 static const struct got_error
*
4522 print_commits(struct got_object_id
*root_id
, struct got_object_id
*end_id
,
4523 struct got_repository
*repo
, const char *path
, int show_changed_paths
,
4524 int show_diffstat
, int show_patch
, const char *search_pattern
,
4525 int diff_context
, int limit
, int log_branches
, int reverse_display_order
,
4526 struct got_reflist_object_id_map
*refs_idmap
, int one_line
, int toposort
,
4529 const struct got_error
*err
;
4530 struct got_commit_graph
*graph
;
4533 struct got_object_id_queue reversed_commits
;
4534 struct got_object_qid
*qid
;
4535 struct got_commit_object
*commit
;
4536 struct got_pathlist_head changed_paths
;
4538 STAILQ_INIT(&reversed_commits
);
4539 TAILQ_INIT(&changed_paths
);
4541 if (search_pattern
&& regcomp(®ex
, search_pattern
,
4542 REG_EXTENDED
| REG_NOSUB
| REG_NEWLINE
))
4543 return got_error_msg(GOT_ERR_REGEX
, search_pattern
);
4545 err
= got_commit_graph_open(&graph
, path
, !log_branches
);
4548 if (log_branches
&& toposort
) {
4549 err
= got_commit_graph_toposort(graph
, root_id
, repo
,
4550 check_cancelled
, NULL
);
4552 err
= got_commit_graph_bfsort(graph
, root_id
, repo
,
4553 check_cancelled
, NULL
);
4558 struct got_object_id id
;
4559 struct got_diffstat_cb_arg dsa
= { 0, 0, 0, 0, 0, 0,
4560 &changed_paths
, 0, 0, GOT_DIFF_ALGORITHM_PATIENCE
};
4562 if (sigint_received
|| sigpipe_received
)
4565 err
= got_commit_graph_iter_next(&id
, graph
, repo
,
4566 check_cancelled
, NULL
);
4568 if (err
->code
== GOT_ERR_ITER_COMPLETED
)
4573 err
= got_object_open_as_commit(&commit
, repo
, &id
);
4577 if (((show_changed_paths
&& !show_diffstat
) ||
4578 (show_diffstat
&& !show_patch
))
4579 && !reverse_display_order
) {
4580 err
= get_changed_paths(&changed_paths
, commit
, repo
,
4581 show_diffstat
? &dsa
: NULL
);
4586 if (search_pattern
) {
4587 err
= match_commit(&have_match
, &id
, commit
, ®ex
);
4589 got_object_commit_close(commit
);
4592 if (have_match
== 0 && show_changed_paths
)
4593 match_changed_paths(&have_match
,
4594 &changed_paths
, ®ex
);
4595 if (have_match
== 0 && show_patch
) {
4596 err
= match_patch(&have_match
, commit
, &id
,
4597 path
, diff_context
, repo
, ®ex
, tmpfile
);
4601 if (have_match
== 0) {
4602 got_object_commit_close(commit
);
4603 got_pathlist_free(&changed_paths
,
4604 GOT_PATHLIST_FREE_ALL
);
4609 if (reverse_display_order
) {
4610 err
= got_object_qid_alloc(&qid
, &id
);
4613 STAILQ_INSERT_HEAD(&reversed_commits
, qid
, entry
);
4614 got_object_commit_close(commit
);
4617 err
= print_commit_oneline(commit
, &id
,
4620 err
= print_commit(commit
, &id
, repo
, path
,
4621 (show_changed_paths
|| show_diffstat
) ?
4622 &changed_paths
: NULL
,
4623 show_diffstat
? &dsa
: NULL
, show_patch
,
4624 diff_context
, refs_idmap
, NULL
, NULL
);
4625 got_object_commit_close(commit
);
4629 if ((limit
&& --limit
== 0) ||
4630 (end_id
&& got_object_id_cmp(&id
, end_id
) == 0))
4633 got_pathlist_free(&changed_paths
, GOT_PATHLIST_FREE_ALL
);
4635 if (reverse_display_order
) {
4636 STAILQ_FOREACH(qid
, &reversed_commits
, entry
) {
4637 struct got_diffstat_cb_arg dsa
= { 0, 0, 0, 0, 0, 0,
4638 &changed_paths
, 0, 0, GOT_DIFF_ALGORITHM_PATIENCE
};
4640 err
= got_object_open_as_commit(&commit
, repo
,
4644 if ((show_changed_paths
&& !show_diffstat
) ||
4645 (show_diffstat
&& !show_patch
)) {
4646 err
= get_changed_paths(&changed_paths
, commit
,
4647 repo
, show_diffstat
? &dsa
: NULL
);
4652 err
= print_commit_oneline(commit
, &qid
->id
,
4655 err
= print_commit(commit
, &qid
->id
, repo
, path
,
4656 (show_changed_paths
|| show_diffstat
) ?
4657 &changed_paths
: NULL
,
4658 show_diffstat
? &dsa
: NULL
, show_patch
,
4659 diff_context
, refs_idmap
, NULL
, NULL
);
4660 got_object_commit_close(commit
);
4663 got_pathlist_free(&changed_paths
, GOT_PATHLIST_FREE_ALL
);
4667 while (!STAILQ_EMPTY(&reversed_commits
)) {
4668 qid
= STAILQ_FIRST(&reversed_commits
);
4669 STAILQ_REMOVE_HEAD(&reversed_commits
, entry
);
4670 got_object_qid_free(qid
);
4672 got_pathlist_free(&changed_paths
, GOT_PATHLIST_FREE_ALL
);
4675 got_commit_graph_close(graph
);
4682 fprintf(stderr
, "usage: %s log [-bdPpRst] [-C number] [-c commit] "
4683 "[-l N] [-r repository-path] [-S search-pattern] [-x commit] "
4684 "[path]\n", getprogname());
4689 get_default_log_limit(void)
4691 const char *got_default_log_limit
;
4695 got_default_log_limit
= getenv("GOT_LOG_DEFAULT_LIMIT");
4696 if (got_default_log_limit
== NULL
)
4698 n
= strtonum(got_default_log_limit
, 0, INT_MAX
, &errstr
);
4704 static const struct got_error
*
4705 cmd_log(int argc
, char *argv
[])
4707 const struct got_error
*error
;
4708 struct got_repository
*repo
= NULL
;
4709 struct got_worktree
*worktree
= NULL
;
4710 struct got_object_id
*start_id
= NULL
, *end_id
= NULL
;
4711 char *repo_path
= NULL
, *path
= NULL
, *cwd
= NULL
, *in_repo_path
= NULL
;
4712 char *keyword_idstr
= NULL
;
4713 const char *start_commit
= NULL
, *end_commit
= NULL
;
4714 const char *search_pattern
= NULL
;
4715 int diff_context
= -1, ch
;
4716 int show_changed_paths
= 0, show_patch
= 0, limit
= 0, log_branches
= 0;
4717 int show_diffstat
= 0, reverse_display_order
= 0, one_line
= 0;
4720 struct got_reflist_head refs
;
4721 struct got_reflist_object_id_map
*refs_idmap
= NULL
;
4722 FILE *tmpfile
= NULL
;
4723 int *pack_fds
= NULL
;
4728 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
4734 limit
= get_default_log_limit();
4736 while ((ch
= getopt(argc
, argv
, "bC:c:dl:PpRr:S:stx:")) != -1) {
4742 diff_context
= strtonum(optarg
, 0, GOT_DIFF_MAX_CONTEXT
,
4745 errx(1, "number of context lines is %s: %s",
4749 start_commit
= optarg
;
4755 limit
= strtonum(optarg
, 0, INT_MAX
, &errstr
);
4757 errx(1, "number of commits is %s: %s",
4761 show_changed_paths
= 1;
4767 reverse_display_order
= 1;
4770 repo_path
= realpath(optarg
, NULL
);
4771 if (repo_path
== NULL
)
4772 return got_error_from_errno2("realpath",
4774 got_path_strip_trailing_slashes(repo_path
);
4777 search_pattern
= optarg
;
4786 end_commit
= optarg
;
4797 if (diff_context
== -1)
4799 else if (!show_patch
)
4800 errx(1, "-C requires -p");
4802 if (one_line
&& (show_patch
|| show_changed_paths
|| show_diffstat
))
4803 errx(1, "cannot use -s with -d, -p or -P");
4805 cwd
= getcwd(NULL
, 0);
4807 error
= got_error_from_errno("getcwd");
4811 error
= got_repo_pack_fds_open(&pack_fds
);
4815 if (repo_path
== NULL
) {
4816 error
= got_worktree_open(&worktree
, cwd
,
4817 GOT_WORKTREE_GOT_DIR
);
4818 if (error
&& error
->code
!= GOT_ERR_NOT_WORKTREE
)
4825 error
= got_worktree_resolve_path(&path
, worktree
,
4830 path
= strdup(argv
[0]);
4832 error
= got_error_from_errno("strdup");
4836 } else if (argc
!= 0)
4839 if (repo_path
== NULL
) {
4840 repo_path
= worktree
?
4841 strdup(got_worktree_get_repo_path(worktree
)) : strdup(cwd
);
4843 if (repo_path
== NULL
) {
4844 error
= got_error_from_errno("strdup");
4848 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
4852 error
= apply_unveil(got_repo_get_path(repo
), 1,
4853 worktree
? got_worktree_get_root_path(worktree
) : NULL
);
4857 error
= got_ref_list(&refs
, repo
, NULL
, got_ref_cmp_by_name
, NULL
);
4861 error
= got_reflist_object_id_map_create(&refs_idmap
, &refs
, repo
);
4865 if (start_commit
== NULL
) {
4866 struct got_reference
*head_ref
;
4867 struct got_commit_object
*commit
= NULL
;
4868 error
= got_ref_open(&head_ref
, repo
,
4869 worktree
? got_worktree_get_head_ref_name(worktree
)
4873 error
= got_ref_resolve(&start_id
, repo
, head_ref
);
4874 got_ref_close(head_ref
);
4877 error
= got_object_open_as_commit(&commit
, repo
,
4881 got_object_commit_close(commit
);
4883 error
= got_keyword_to_idstr(&keyword_idstr
, start_commit
,
4887 if (keyword_idstr
!= NULL
)
4888 start_commit
= keyword_idstr
;
4890 error
= got_repo_match_object_id(&start_id
, NULL
,
4891 start_commit
, GOT_OBJ_TYPE_COMMIT
, &refs
, repo
);
4895 if (end_commit
!= NULL
) {
4896 error
= got_keyword_to_idstr(&keyword_idstr
, end_commit
,
4900 if (keyword_idstr
!= NULL
)
4901 end_commit
= keyword_idstr
;
4903 error
= got_repo_match_object_id(&end_id
, NULL
,
4904 end_commit
, GOT_OBJ_TYPE_COMMIT
, &refs
, repo
);
4911 * If a path was specified on the command line it was resolved
4912 * to a path in the work tree above. Prepend the work tree's
4913 * path prefix to obtain the corresponding in-repository path.
4917 prefix
= got_worktree_get_path_prefix(worktree
);
4918 if (asprintf(&in_repo_path
, "%s%s%s", prefix
,
4919 (path
[0] != '\0') ? "/" : "", path
) == -1) {
4920 error
= got_error_from_errno("asprintf");
4925 error
= got_repo_map_path(&in_repo_path
, repo
,
4931 path
= in_repo_path
;
4935 /* Release work tree lock. */
4936 got_worktree_close(worktree
);
4940 if (search_pattern
&& show_patch
) {
4941 tmpfile
= got_opentemp();
4942 if (tmpfile
== NULL
) {
4943 error
= got_error_from_errno("got_opentemp");
4948 error
= print_commits(start_id
, end_id
, repo
, path
? path
: "",
4949 show_changed_paths
, show_diffstat
, show_patch
, search_pattern
,
4950 diff_context
, limit
, log_branches
, reverse_display_order
,
4951 refs_idmap
, one_line
, toposort
, tmpfile
);
4958 free(keyword_idstr
);
4960 got_worktree_close(worktree
);
4962 const struct got_error
*close_err
= got_repo_close(repo
);
4967 const struct got_error
*pack_err
=
4968 got_repo_pack_fds_close(pack_fds
);
4973 got_reflist_object_id_map_free(refs_idmap
);
4974 if (tmpfile
&& fclose(tmpfile
) == EOF
&& error
== NULL
)
4975 error
= got_error_from_errno("fclose");
4976 got_ref_list_free(&refs
);
4983 fprintf(stderr
, "usage: %s diff [-adPsw] [-C number] [-c commit] "
4984 "[-r repository-path] [object1 object2 | path ...]\n",
4989 struct print_diff_arg
{
4990 struct got_repository
*repo
;
4991 struct got_worktree
*worktree
;
4992 struct got_diffstat_cb_arg
*diffstat
;
4997 enum got_diff_algorithm diff_algo
;
4998 int ignore_whitespace
;
4999 int force_text_diff
;
5006 * Create a file which contains the target path of a symlink so we can feed
5007 * it as content to the diff engine.
5009 static const struct got_error
*
5010 get_symlink_target_file(int *fd
, int dirfd
, const char *de_name
,
5011 const char *abspath
)
5013 const struct got_error
*err
= NULL
;
5014 char target_path
[PATH_MAX
];
5015 ssize_t target_len
, outlen
;
5020 target_len
= readlinkat(dirfd
, de_name
, target_path
, PATH_MAX
);
5021 if (target_len
== -1)
5022 return got_error_from_errno2("readlinkat", abspath
);
5024 target_len
= readlink(abspath
, target_path
, PATH_MAX
);
5025 if (target_len
== -1)
5026 return got_error_from_errno2("readlink", abspath
);
5029 *fd
= got_opentempfd();
5031 return got_error_from_errno("got_opentempfd");
5033 outlen
= write(*fd
, target_path
, target_len
);
5035 err
= got_error_from_errno("got_opentempfd");
5039 if (lseek(*fd
, 0, SEEK_SET
) == -1) {
5040 err
= got_error_from_errno2("lseek", abspath
);
5051 static const struct got_error
*
5052 print_diff(void *arg
, unsigned char status
, unsigned char staged_status
,
5053 const char *path
, struct got_object_id
*blob_id
,
5054 struct got_object_id
*staged_blob_id
, struct got_object_id
*commit_id
,
5055 int dirfd
, const char *de_name
)
5057 struct print_diff_arg
*a
= arg
;
5058 const struct got_error
*err
= NULL
;
5059 struct got_blob_object
*blob1
= NULL
;
5060 int fd
= -1, fd1
= -1, fd2
= -1;
5062 char *abspath
= NULL
, *label1
= NULL
;
5067 memset(&sb
, 0, sizeof(sb
));
5069 if (a
->diff_staged
) {
5070 if (staged_status
!= GOT_STATUS_MODIFY
&&
5071 staged_status
!= GOT_STATUS_ADD
&&
5072 staged_status
!= GOT_STATUS_DELETE
)
5075 if (staged_status
== GOT_STATUS_DELETE
)
5077 if (status
== GOT_STATUS_NONEXISTENT
)
5078 return got_error_set_errno(ENOENT
, path
);
5079 if (status
!= GOT_STATUS_MODIFY
&&
5080 status
!= GOT_STATUS_ADD
&&
5081 status
!= GOT_STATUS_DELETE
&&
5082 status
!= GOT_STATUS_CONFLICT
)
5086 err
= got_opentemp_truncate(a
->f1
);
5088 return got_error_from_errno("got_opentemp_truncate");
5089 err
= got_opentemp_truncate(a
->f2
);
5091 return got_error_from_errno("got_opentemp_truncate");
5093 if (!a
->header_shown
) {
5094 if (fprintf(a
->outfile
, "diff %s%s\n",
5095 a
->diff_staged
? "-s " : "",
5096 got_worktree_get_root_path(a
->worktree
)) < 0) {
5097 err
= got_error_from_errno("fprintf");
5100 if (fprintf(a
->outfile
, "commit - %s\n", a
->id_str
) < 0) {
5101 err
= got_error_from_errno("fprintf");
5104 if (fprintf(a
->outfile
, "path + %s%s\n",
5105 got_worktree_get_root_path(a
->worktree
),
5106 a
->diff_staged
? " (staged changes)" : "") < 0) {
5107 err
= got_error_from_errno("fprintf");
5110 a
->header_shown
= 1;
5113 if (a
->diff_staged
) {
5114 const char *label1
= NULL
, *label2
= NULL
;
5115 switch (staged_status
) {
5116 case GOT_STATUS_MODIFY
:
5120 case GOT_STATUS_ADD
:
5123 case GOT_STATUS_DELETE
:
5127 return got_error(GOT_ERR_FILE_STATUS
);
5129 fd1
= got_opentempfd();
5131 err
= got_error_from_errno("got_opentempfd");
5134 fd2
= got_opentempfd();
5136 err
= got_error_from_errno("got_opentempfd");
5139 err
= got_diff_objects_as_blobs(NULL
, NULL
, a
->f1
, a
->f2
,
5140 fd1
, fd2
, blob_id
, staged_blob_id
, label1
, label2
,
5141 a
->diff_algo
, a
->diff_context
, a
->ignore_whitespace
,
5142 a
->force_text_diff
, a
->diffstat
, a
->repo
, a
->outfile
);
5146 fd1
= got_opentempfd();
5148 err
= got_error_from_errno("got_opentempfd");
5152 if (staged_status
== GOT_STATUS_ADD
||
5153 staged_status
== GOT_STATUS_MODIFY
) {
5155 err
= got_object_open_as_blob(&blob1
, a
->repo
, staged_blob_id
,
5159 err
= got_object_id_str(&id_str
, staged_blob_id
);
5162 if (asprintf(&label1
, "%s (staged)", id_str
) == -1) {
5163 err
= got_error_from_errno("asprintf");
5168 } else if (status
!= GOT_STATUS_ADD
) {
5169 err
= got_object_open_as_blob(&blob1
, a
->repo
, blob_id
, 8192,
5175 if (status
!= GOT_STATUS_DELETE
) {
5176 if (asprintf(&abspath
, "%s/%s",
5177 got_worktree_get_root_path(a
->worktree
), path
) == -1) {
5178 err
= got_error_from_errno("asprintf");
5183 fd
= openat(dirfd
, de_name
,
5184 O_RDONLY
| O_NOFOLLOW
| O_CLOEXEC
);
5186 if (!got_err_open_nofollow_on_symlink()) {
5187 err
= got_error_from_errno2("openat",
5191 err
= get_symlink_target_file(&fd
, dirfd
,
5197 fd
= open(abspath
, O_RDONLY
| O_NOFOLLOW
| O_CLOEXEC
);
5199 if (!got_err_open_nofollow_on_symlink()) {
5200 err
= got_error_from_errno2("open",
5204 err
= get_symlink_target_file(&fd
, dirfd
,
5210 if (fstatat(fd
, abspath
, &sb
, AT_SYMLINK_NOFOLLOW
) == -1) {
5211 err
= got_error_from_errno2("fstatat", abspath
);
5214 f2
= fdopen(fd
, "r");
5216 err
= got_error_from_errno2("fdopen", abspath
);
5224 err
= got_object_blob_dump_to_file(&size1
, NULL
, NULL
,
5230 err
= got_diff_blob_file(blob1
, a
->f1
, size1
, label1
, f2
? f2
: a
->f2
,
5231 f2_exists
, &sb
, path
, GOT_DIFF_ALGORITHM_PATIENCE
, a
->diff_context
,
5232 a
->ignore_whitespace
, a
->force_text_diff
, a
->diffstat
, a
->outfile
);
5234 if (fd1
!= -1 && close(fd1
) == -1 && err
== NULL
)
5235 err
= got_error_from_errno("close");
5236 if (fd2
!= -1 && close(fd2
) == -1 && err
== NULL
)
5237 err
= got_error_from_errno("close");
5239 got_object_blob_close(blob1
);
5240 if (fd
!= -1 && close(fd
) == -1 && err
== NULL
)
5241 err
= got_error_from_errno("close");
5242 if (f2
&& fclose(f2
) == EOF
&& err
== NULL
)
5243 err
= got_error_from_errno("fclose");
5248 static const struct got_error
*
5249 cmd_diff(int argc
, char *argv
[])
5251 const struct got_error
*error
;
5252 struct got_repository
*repo
= NULL
;
5253 struct got_worktree
*worktree
= NULL
;
5254 char *cwd
= NULL
, *repo_path
= NULL
;
5255 const char *commit_args
[2] = { NULL
, NULL
};
5256 int ncommit_args
= 0;
5257 struct got_object_id
*ids
[2] = { NULL
, NULL
};
5258 char *labels
[2] = { NULL
, NULL
};
5259 int type1
= GOT_OBJ_TYPE_ANY
, type2
= GOT_OBJ_TYPE_ANY
;
5260 int diff_context
= 3, diff_staged
= 0, ignore_whitespace
= 0, ch
, i
;
5261 int force_text_diff
= 0, force_path
= 0, rflag
= 0, show_diffstat
= 0;
5263 struct got_reflist_head refs
;
5264 struct got_pathlist_head diffstat_paths
, paths
;
5265 FILE *f1
= NULL
, *f2
= NULL
, *outfile
= NULL
;
5266 int fd1
= -1, fd2
= -1;
5267 int *pack_fds
= NULL
;
5268 struct got_diffstat_cb_arg dsa
;
5270 memset(&dsa
, 0, sizeof(dsa
));
5274 TAILQ_INIT(&diffstat_paths
);
5277 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
5282 while ((ch
= getopt(argc
, argv
, "aC:c:dPr:sw")) != -1) {
5285 force_text_diff
= 1;
5288 diff_context
= strtonum(optarg
, 0, GOT_DIFF_MAX_CONTEXT
,
5291 errx(1, "number of context lines is %s: %s",
5295 if (ncommit_args
>= 2)
5296 errx(1, "too many -c options used");
5297 commit_args
[ncommit_args
++] = optarg
;
5306 repo_path
= realpath(optarg
, NULL
);
5307 if (repo_path
== NULL
)
5308 return got_error_from_errno2("realpath",
5310 got_path_strip_trailing_slashes(repo_path
);
5317 ignore_whitespace
= 1;
5328 cwd
= getcwd(NULL
, 0);
5330 error
= got_error_from_errno("getcwd");
5334 error
= got_repo_pack_fds_open(&pack_fds
);
5338 if (repo_path
== NULL
) {
5339 error
= got_worktree_open(&worktree
, cwd
,
5340 GOT_WORKTREE_GOT_DIR
);
5341 if (error
&& error
->code
!= GOT_ERR_NOT_WORKTREE
)
5347 strdup(got_worktree_get_repo_path(worktree
));
5348 if (repo_path
== NULL
) {
5349 error
= got_error_from_errno("strdup");
5353 repo_path
= strdup(cwd
);
5354 if (repo_path
== NULL
) {
5355 error
= got_error_from_errno("strdup");
5361 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
5366 if (show_diffstat
) {
5367 dsa
.paths
= &diffstat_paths
;
5368 dsa
.force_text
= force_text_diff
;
5369 dsa
.ignore_ws
= ignore_whitespace
;
5370 dsa
.diff_algo
= GOT_DIFF_ALGORITHM_PATIENCE
;
5373 if (rflag
|| worktree
== NULL
|| ncommit_args
> 0) {
5375 error
= got_error_msg(GOT_ERR_NOT_IMPL
,
5376 "-P option can only be used when diffing "
5381 error
= got_error_msg(GOT_ERR_NOT_IMPL
,
5382 "-s option can only be used when diffing "
5388 error
= apply_unveil(got_repo_get_path(repo
), 1,
5389 worktree
? got_worktree_get_root_path(worktree
) : NULL
);
5393 if ((!force_path
&& argc
== 2) || ncommit_args
> 0) {
5394 int obj_type
= (ncommit_args
> 0 ?
5395 GOT_OBJ_TYPE_COMMIT
: GOT_OBJ_TYPE_ANY
);
5396 error
= got_ref_list(&refs
, repo
, NULL
, got_ref_cmp_by_name
,
5400 for (i
= 0; i
< (ncommit_args
> 0 ? ncommit_args
: argc
); i
++) {
5402 char *keyword_idstr
= NULL
;
5404 if (ncommit_args
> 0)
5405 arg
= commit_args
[i
];
5409 error
= got_keyword_to_idstr(&keyword_idstr
, arg
,
5413 if (keyword_idstr
!= NULL
)
5414 arg
= keyword_idstr
;
5416 error
= got_repo_match_object_id(&ids
[i
], &labels
[i
],
5417 arg
, obj_type
, &refs
, repo
);
5418 free(keyword_idstr
);
5420 if (error
->code
!= GOT_ERR_NOT_REF
&&
5421 error
->code
!= GOT_ERR_NO_OBJ
)
5423 if (ncommit_args
> 0)
5431 f1
= got_opentemp();
5433 error
= got_error_from_errno("got_opentemp");
5437 f2
= got_opentemp();
5439 error
= got_error_from_errno("got_opentemp");
5443 outfile
= got_opentemp();
5444 if (outfile
== NULL
) {
5445 error
= got_error_from_errno("got_opentemp");
5449 if (ncommit_args
== 0 && (ids
[0] == NULL
|| ids
[1] == NULL
)) {
5450 struct print_diff_arg arg
;
5453 if (worktree
== NULL
) {
5454 if (argc
== 2 && ids
[0] == NULL
) {
5455 error
= got_error_path(argv
[0], GOT_ERR_NO_OBJ
);
5457 } else if (argc
== 2 && ids
[1] == NULL
) {
5458 error
= got_error_path(argv
[1], GOT_ERR_NO_OBJ
);
5460 } else if (argc
> 0) {
5461 error
= got_error_fmt(GOT_ERR_NOT_WORKTREE
,
5462 "%s", "specified paths cannot be resolved");
5465 error
= got_error(GOT_ERR_NOT_WORKTREE
);
5470 error
= get_worktree_paths_from_argv(&paths
, argc
, argv
,
5475 error
= got_object_id_str(&id_str
,
5476 got_worktree_get_base_commit_id(worktree
));
5480 arg
.worktree
= worktree
;
5481 arg
.diff_algo
= GOT_DIFF_ALGORITHM_PATIENCE
;
5482 arg
.diff_context
= diff_context
;
5483 arg
.id_str
= id_str
;
5484 arg
.header_shown
= 0;
5485 arg
.diff_staged
= diff_staged
;
5486 arg
.ignore_whitespace
= ignore_whitespace
;
5487 arg
.force_text_diff
= force_text_diff
;
5488 arg
.diffstat
= show_diffstat
? &dsa
: NULL
;
5491 arg
.outfile
= outfile
;
5493 error
= got_worktree_status(worktree
, &paths
, repo
, 0,
5494 print_diff
, &arg
, check_cancelled
, NULL
);
5499 if (show_diffstat
&& dsa
.nfiles
> 0) {
5502 if (asprintf(&header
, "diffstat %s%s",
5503 diff_staged
? "-s " : "",
5504 got_worktree_get_root_path(worktree
)) == -1) {
5505 error
= got_error_from_errno("asprintf");
5509 error
= print_diffstat(&dsa
, header
);
5515 error
= printfile(outfile
);
5519 if (ncommit_args
== 1) {
5520 struct got_commit_object
*commit
;
5521 error
= got_object_open_as_commit(&commit
, repo
, ids
[0]);
5525 labels
[1] = labels
[0];
5527 if (got_object_commit_get_nparents(commit
) > 0) {
5528 const struct got_object_id_queue
*pids
;
5529 struct got_object_qid
*pid
;
5530 pids
= got_object_commit_get_parent_ids(commit
);
5531 pid
= STAILQ_FIRST(pids
);
5532 ids
[0] = got_object_id_dup(&pid
->id
);
5533 if (ids
[0] == NULL
) {
5534 error
= got_error_from_errno(
5535 "got_object_id_dup");
5536 got_object_commit_close(commit
);
5539 error
= got_object_id_str(&labels
[0], ids
[0]);
5541 got_object_commit_close(commit
);
5546 labels
[0] = strdup("/dev/null");
5547 if (labels
[0] == NULL
) {
5548 error
= got_error_from_errno("strdup");
5549 got_object_commit_close(commit
);
5554 got_object_commit_close(commit
);
5557 if (ncommit_args
== 0 && argc
> 2) {
5558 error
= got_error_msg(GOT_ERR_BAD_PATH
,
5559 "path arguments cannot be used when diffing two objects");
5564 error
= got_object_get_type(&type1
, repo
, ids
[0]);
5569 error
= got_object_get_type(&type2
, repo
, ids
[1]);
5572 if (type1
!= GOT_OBJ_TYPE_ANY
&& type1
!= type2
) {
5573 error
= got_error(GOT_ERR_OBJ_TYPE
);
5576 if (type1
== GOT_OBJ_TYPE_BLOB
&& argc
> 2) {
5577 error
= got_error_msg(GOT_ERR_OBJ_TYPE
,
5578 "path arguments cannot be used when diffing blobs");
5582 for (i
= 0; ncommit_args
> 0 && i
< argc
; i
++) {
5584 struct got_pathlist_entry
*new;
5588 error
= got_worktree_resolve_path(&p
, worktree
,
5592 prefix
= got_worktree_get_path_prefix(worktree
);
5593 while (prefix
[0] == '/')
5595 if (asprintf(&in_repo_path
, "%s%s%s", prefix
,
5596 (p
[0] != '\0' && prefix
[0] != '\0') ? "/" : "",
5598 error
= got_error_from_errno("asprintf");
5604 char *mapped_path
, *s
;
5605 error
= got_repo_map_path(&mapped_path
, repo
, argv
[i
]);
5611 in_repo_path
= strdup(s
);
5612 if (in_repo_path
== NULL
) {
5613 error
= got_error_from_errno("asprintf");
5620 error
= got_pathlist_insert(&new, &paths
, in_repo_path
, NULL
);
5621 if (error
|| new == NULL
/* duplicate */)
5628 /* Release work tree lock. */
5629 got_worktree_close(worktree
);
5633 fd1
= got_opentempfd();
5635 error
= got_error_from_errno("got_opentempfd");
5639 fd2
= got_opentempfd();
5641 error
= got_error_from_errno("got_opentempfd");
5645 switch (type1
== GOT_OBJ_TYPE_ANY
? type2
: type1
) {
5646 case GOT_OBJ_TYPE_BLOB
:
5647 error
= got_diff_objects_as_blobs(NULL
, NULL
, f1
, f2
,
5648 fd1
, fd2
, ids
[0], ids
[1], NULL
, NULL
,
5649 GOT_DIFF_ALGORITHM_PATIENCE
, diff_context
,
5650 ignore_whitespace
, force_text_diff
,
5651 show_diffstat
? &dsa
: NULL
, repo
, outfile
);
5653 case GOT_OBJ_TYPE_TREE
:
5654 error
= got_diff_objects_as_trees(NULL
, NULL
, f1
, f2
, fd1
, fd2
,
5655 ids
[0], ids
[1], &paths
, "", "",
5656 GOT_DIFF_ALGORITHM_PATIENCE
, diff_context
,
5657 ignore_whitespace
, force_text_diff
,
5658 show_diffstat
? &dsa
: NULL
, repo
, outfile
);
5660 case GOT_OBJ_TYPE_COMMIT
:
5661 fprintf(outfile
, "diff %s %s\n", labels
[0], labels
[1]);
5662 error
= got_diff_objects_as_commits(NULL
, NULL
, f1
, f2
,
5663 fd1
, fd2
, ids
[0], ids
[1], &paths
,
5664 GOT_DIFF_ALGORITHM_PATIENCE
, diff_context
,
5665 ignore_whitespace
, force_text_diff
,
5666 show_diffstat
? &dsa
: NULL
, repo
, outfile
);
5669 error
= got_error(GOT_ERR_OBJ_TYPE
);
5674 if (show_diffstat
&& dsa
.nfiles
> 0) {
5675 char *header
= NULL
;
5677 if (asprintf(&header
, "diffstat %s %s",
5678 labels
[0], labels
[1]) == -1) {
5679 error
= got_error_from_errno("asprintf");
5683 error
= print_diffstat(&dsa
, header
);
5689 error
= printfile(outfile
);
5698 got_worktree_close(worktree
);
5700 const struct got_error
*close_err
= got_repo_close(repo
);
5705 const struct got_error
*pack_err
=
5706 got_repo_pack_fds_close(pack_fds
);
5710 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_PATH
);
5711 got_pathlist_free(&diffstat_paths
, GOT_PATHLIST_FREE_ALL
);
5712 got_ref_list_free(&refs
);
5713 if (outfile
&& fclose(outfile
) == EOF
&& error
== NULL
)
5714 error
= got_error_from_errno("fclose");
5715 if (f1
&& fclose(f1
) == EOF
&& error
== NULL
)
5716 error
= got_error_from_errno("fclose");
5717 if (f2
&& fclose(f2
) == EOF
&& error
== NULL
)
5718 error
= got_error_from_errno("fclose");
5719 if (fd1
!= -1 && close(fd1
) == -1 && error
== NULL
)
5720 error
= got_error_from_errno("close");
5721 if (fd2
!= -1 && close(fd2
) == -1 && error
== NULL
)
5722 error
= got_error_from_errno("close");
5730 "usage: %s blame [-c commit] [-r repository-path] path\n",
5739 char datebuf
[11]; /* YYYY-MM-DD + NUL */
5742 struct blame_cb_args
{
5743 struct blame_line
*lines
;
5747 off_t
*line_offsets
;
5749 struct got_repository
*repo
;
5752 static const struct got_error
*
5753 blame_cb(void *arg
, int nlines
, int lineno
,
5754 struct got_commit_object
*commit
, struct got_object_id
*id
)
5756 const struct got_error
*err
= NULL
;
5757 struct blame_cb_args
*a
= arg
;
5758 struct blame_line
*bline
;
5760 size_t linesize
= 0;
5763 time_t committer_time
;
5765 if (nlines
!= a
->nlines
||
5766 (lineno
!= -1 && lineno
< 1) || lineno
> a
->nlines
)
5767 return got_error(GOT_ERR_RANGE
);
5769 if (sigint_received
)
5770 return got_error(GOT_ERR_ITER_COMPLETED
);
5773 return NULL
; /* no change in this commit */
5775 /* Annotate this line. */
5776 bline
= &a
->lines
[lineno
- 1];
5777 if (bline
->annotated
)
5779 err
= got_object_id_str(&bline
->id_str
, id
);
5783 bline
->committer
= strdup(got_object_commit_get_committer(commit
));
5784 if (bline
->committer
== NULL
) {
5785 err
= got_error_from_errno("strdup");
5789 committer_time
= got_object_commit_get_committer_time(commit
);
5790 if (gmtime_r(&committer_time
, &tm
) == NULL
)
5791 return got_error_from_errno("gmtime_r");
5792 if (strftime(bline
->datebuf
, sizeof(bline
->datebuf
), "%F", &tm
) == 0) {
5793 err
= got_error(GOT_ERR_NO_SPACE
);
5796 bline
->annotated
= 1;
5798 /* Print lines annotated so far. */
5799 bline
= &a
->lines
[a
->lineno_cur
- 1];
5800 if (!bline
->annotated
)
5803 offset
= a
->line_offsets
[a
->lineno_cur
- 1];
5804 if (fseeko(a
->f
, offset
, SEEK_SET
) == -1) {
5805 err
= got_error_from_errno("fseeko");
5809 while (a
->lineno_cur
<= a
->nlines
&& bline
->annotated
) {
5810 char *smallerthan
, *at
, *nl
, *committer
;
5813 if (getline(&line
, &linesize
, a
->f
) == -1) {
5815 err
= got_error_from_errno("getline");
5819 committer
= bline
->committer
;
5820 smallerthan
= strchr(committer
, '<');
5821 if (smallerthan
&& smallerthan
[1] != '\0')
5822 committer
= smallerthan
+ 1;
5823 at
= strchr(committer
, '@');
5826 len
= strlen(committer
);
5828 committer
[8] = '\0';
5830 nl
= strchr(line
, '\n');
5833 printf("%.*d) %.8s %s %-8s %s\n", a
->nlines_prec
, a
->lineno_cur
,
5834 bline
->id_str
, bline
->datebuf
, committer
, line
);
5837 bline
= &a
->lines
[a
->lineno_cur
- 1];
5844 static const struct got_error
*
5845 cmd_blame(int argc
, char *argv
[])
5847 const struct got_error
*error
;
5848 struct got_repository
*repo
= NULL
;
5849 struct got_worktree
*worktree
= NULL
;
5850 char *path
, *cwd
= NULL
, *repo_path
= NULL
, *in_repo_path
= NULL
;
5851 char *link_target
= NULL
;
5852 struct got_object_id
*obj_id
= NULL
;
5853 struct got_object_id
*commit_id
= NULL
;
5854 struct got_commit_object
*commit
= NULL
;
5855 struct got_blob_object
*blob
= NULL
;
5856 char *commit_id_str
= NULL
, *keyword_idstr
= NULL
;
5857 struct blame_cb_args bca
;
5858 int ch
, obj_type
, i
, fd1
= -1, fd2
= -1, fd3
= -1;
5860 int *pack_fds
= NULL
;
5861 FILE *f1
= NULL
, *f2
= NULL
;
5863 fd1
= got_opentempfd();
5865 return got_error_from_errno("got_opentempfd");
5867 memset(&bca
, 0, sizeof(bca
));
5870 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
5875 while ((ch
= getopt(argc
, argv
, "c:r:")) != -1) {
5878 commit_id_str
= optarg
;
5881 repo_path
= realpath(optarg
, NULL
);
5882 if (repo_path
== NULL
)
5883 return got_error_from_errno2("realpath",
5885 got_path_strip_trailing_slashes(repo_path
);
5901 cwd
= getcwd(NULL
, 0);
5903 error
= got_error_from_errno("getcwd");
5907 error
= got_repo_pack_fds_open(&pack_fds
);
5911 if (repo_path
== NULL
) {
5912 error
= got_worktree_open(&worktree
, cwd
,
5913 GOT_WORKTREE_GOT_DIR
);
5914 if (error
&& error
->code
!= GOT_ERR_NOT_WORKTREE
)
5920 strdup(got_worktree_get_repo_path(worktree
));
5921 if (repo_path
== NULL
) {
5922 error
= got_error_from_errno("strdup");
5927 repo_path
= strdup(cwd
);
5928 if (repo_path
== NULL
) {
5929 error
= got_error_from_errno("strdup");
5935 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
5940 const char *prefix
= got_worktree_get_path_prefix(worktree
);
5943 error
= got_worktree_resolve_path(&p
, worktree
, path
);
5946 if (asprintf(&in_repo_path
, "%s%s%s", prefix
,
5947 (p
[0] != '\0' && !got_path_is_root_dir(prefix
)) ? "/" : "",
5949 error
= got_error_from_errno("asprintf");
5954 error
= apply_unveil(got_repo_get_path(repo
), 1, NULL
);
5956 error
= apply_unveil(got_repo_get_path(repo
), 1, NULL
);
5959 error
= got_repo_map_path(&in_repo_path
, repo
, path
);
5964 if (commit_id_str
== NULL
) {
5965 struct got_reference
*head_ref
;
5966 error
= got_ref_open(&head_ref
, repo
, worktree
?
5967 got_worktree_get_head_ref_name(worktree
) : GOT_REF_HEAD
, 0);
5970 error
= got_ref_resolve(&commit_id
, repo
, head_ref
);
5971 got_ref_close(head_ref
);
5975 struct got_reflist_head refs
;
5978 error
= got_ref_list(&refs
, repo
, NULL
, got_ref_cmp_by_name
,
5983 error
= got_keyword_to_idstr(&keyword_idstr
, commit_id_str
,
5987 if (keyword_idstr
!= NULL
)
5988 commit_id_str
= keyword_idstr
;
5990 error
= got_repo_match_object_id(&commit_id
, NULL
,
5991 commit_id_str
, GOT_OBJ_TYPE_COMMIT
, &refs
, repo
);
5992 got_ref_list_free(&refs
);
5998 /* Release work tree lock. */
5999 got_worktree_close(worktree
);
6003 error
= got_object_open_as_commit(&commit
, repo
, commit_id
);
6007 error
= got_object_resolve_symlinks(&link_target
, in_repo_path
,
6012 error
= got_object_id_by_path(&obj_id
, repo
, commit
,
6013 link_target
? link_target
: in_repo_path
);
6017 error
= got_object_get_type(&obj_type
, repo
, obj_id
);
6021 if (obj_type
!= GOT_OBJ_TYPE_BLOB
) {
6022 error
= got_error_path(link_target
? link_target
: in_repo_path
,
6027 error
= got_object_open_as_blob(&blob
, repo
, obj_id
, 8192, fd1
);
6030 bca
.f
= got_opentemp();
6031 if (bca
.f
== NULL
) {
6032 error
= got_error_from_errno("got_opentemp");
6035 error
= got_object_blob_dump_to_file(&filesize
, &bca
.nlines
,
6036 &bca
.line_offsets
, bca
.f
, blob
);
6037 if (error
|| bca
.nlines
== 0)
6040 /* Don't include \n at EOF in the blame line count. */
6041 if (bca
.line_offsets
[bca
.nlines
- 1] == filesize
)
6044 bca
.lines
= calloc(bca
.nlines
, sizeof(*bca
.lines
));
6045 if (bca
.lines
== NULL
) {
6046 error
= got_error_from_errno("calloc");
6050 bca
.nlines_prec
= 0;
6058 fd2
= got_opentempfd();
6060 error
= got_error_from_errno("got_opentempfd");
6063 fd3
= got_opentempfd();
6065 error
= got_error_from_errno("got_opentempfd");
6068 f1
= got_opentemp();
6070 error
= got_error_from_errno("got_opentemp");
6073 f2
= got_opentemp();
6075 error
= got_error_from_errno("got_opentemp");
6078 error
= got_blame(link_target
? link_target
: in_repo_path
, commit_id
,
6079 repo
, GOT_DIFF_ALGORITHM_PATIENCE
, blame_cb
, &bca
,
6080 check_cancelled
, NULL
, fd2
, fd3
, f1
, f2
);
6082 free(keyword_idstr
);
6090 got_object_commit_close(commit
);
6092 if (fd1
!= -1 && close(fd1
) == -1 && error
== NULL
)
6093 error
= got_error_from_errno("close");
6094 if (fd2
!= -1 && close(fd2
) == -1 && error
== NULL
)
6095 error
= got_error_from_errno("close");
6096 if (fd3
!= -1 && close(fd3
) == -1 && error
== NULL
)
6097 error
= got_error_from_errno("close");
6098 if (f1
&& fclose(f1
) == EOF
&& error
== NULL
)
6099 error
= got_error_from_errno("fclose");
6100 if (f2
&& fclose(f2
) == EOF
&& error
== NULL
)
6101 error
= got_error_from_errno("fclose");
6104 got_object_blob_close(blob
);
6106 got_worktree_close(worktree
);
6108 const struct got_error
*close_err
= got_repo_close(repo
);
6113 const struct got_error
*pack_err
=
6114 got_repo_pack_fds_close(pack_fds
);
6119 for (i
= 0; i
< bca
.nlines
; i
++) {
6120 struct blame_line
*bline
= &bca
.lines
[i
];
6121 free(bline
->id_str
);
6122 free(bline
->committer
);
6126 free(bca
.line_offsets
);
6127 if (bca
.f
&& fclose(bca
.f
) == EOF
&& error
== NULL
)
6128 error
= got_error_from_errno("fclose");
6135 fprintf(stderr
, "usage: %s tree [-iR] [-c commit] [-r repository-path] "
6136 "[path]\n", getprogname());
6140 static const struct got_error
*
6141 print_entry(struct got_tree_entry
*te
, const char *id
, const char *path
,
6142 const char *root_path
, struct got_repository
*repo
)
6144 const struct got_error
*err
= NULL
;
6145 int is_root_path
= (strcmp(path
, root_path
) == 0);
6146 const char *modestr
= "";
6147 mode_t mode
= got_tree_entry_get_mode(te
);
6148 char *link_target
= NULL
;
6150 path
+= strlen(root_path
);
6151 while (path
[0] == '/')
6154 if (got_object_tree_entry_is_submodule(te
))
6156 else if (S_ISLNK(mode
)) {
6159 err
= got_tree_entry_get_symlink_target(&link_target
, te
, repo
);
6162 for (i
= 0; link_target
[i
] != '\0'; i
++) {
6163 if (!isprint((unsigned char)link_target
[i
]))
6164 link_target
[i
] = '?';
6169 else if (S_ISDIR(mode
))
6171 else if (mode
& S_IXUSR
)
6174 printf("%s%s%s%s%s%s%s\n", id
? id
: "", path
,
6175 is_root_path
? "" : "/", got_tree_entry_get_name(te
), modestr
,
6176 link_target
? " -> ": "", link_target
? link_target
: "");
6182 static const struct got_error
*
6183 print_tree(const char *path
, struct got_commit_object
*commit
,
6184 int show_ids
, int recurse
, const char *root_path
,
6185 struct got_repository
*repo
)
6187 const struct got_error
*err
= NULL
;
6188 struct got_object_id
*tree_id
= NULL
;
6189 struct got_tree_object
*tree
= NULL
;
6192 err
= got_object_id_by_path(&tree_id
, repo
, commit
, path
);
6196 err
= got_object_open_as_tree(&tree
, repo
, tree_id
);
6199 nentries
= got_object_tree_get_nentries(tree
);
6200 for (i
= 0; i
< nentries
; i
++) {
6201 struct got_tree_entry
*te
;
6204 if (sigint_received
|| sigpipe_received
)
6207 te
= got_object_tree_get_entry(tree
, i
);
6210 err
= got_object_id_str(&id_str
,
6211 got_tree_entry_get_id(te
));
6214 if (asprintf(&id
, "%s ", id_str
) == -1) {
6215 err
= got_error_from_errno("asprintf");
6221 err
= print_entry(te
, id
, path
, root_path
, repo
);
6226 if (recurse
&& S_ISDIR(got_tree_entry_get_mode(te
))) {
6228 if (asprintf(&child_path
, "%s%s%s", path
,
6229 path
[0] == '/' && path
[1] == '\0' ? "" : "/",
6230 got_tree_entry_get_name(te
)) == -1) {
6231 err
= got_error_from_errno("asprintf");
6234 err
= print_tree(child_path
, commit
, show_ids
, 1,
6243 got_object_tree_close(tree
);
6248 static const struct got_error
*
6249 cmd_tree(int argc
, char *argv
[])
6251 const struct got_error
*error
;
6252 struct got_repository
*repo
= NULL
;
6253 struct got_worktree
*worktree
= NULL
;
6254 const char *path
, *refname
= NULL
;
6255 char *cwd
= NULL
, *repo_path
= NULL
, *in_repo_path
= NULL
;
6256 struct got_object_id
*commit_id
= NULL
;
6257 struct got_commit_object
*commit
= NULL
;
6258 char *commit_id_str
= NULL
, *keyword_idstr
= NULL
;
6259 int show_ids
= 0, recurse
= 0;
6261 int *pack_fds
= NULL
;
6264 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
6269 while ((ch
= getopt(argc
, argv
, "c:iRr:")) != -1) {
6272 commit_id_str
= optarg
;
6281 repo_path
= realpath(optarg
, NULL
);
6282 if (repo_path
== NULL
)
6283 return got_error_from_errno2("realpath",
6285 got_path_strip_trailing_slashes(repo_path
);
6303 cwd
= getcwd(NULL
, 0);
6305 error
= got_error_from_errno("getcwd");
6309 error
= got_repo_pack_fds_open(&pack_fds
);
6313 if (repo_path
== NULL
) {
6314 error
= got_worktree_open(&worktree
, cwd
,
6315 GOT_WORKTREE_GOT_DIR
);
6316 if (error
&& error
->code
!= GOT_ERR_NOT_WORKTREE
)
6322 strdup(got_worktree_get_repo_path(worktree
));
6323 if (repo_path
== NULL
)
6324 error
= got_error_from_errno("strdup");
6328 repo_path
= strdup(cwd
);
6329 if (repo_path
== NULL
) {
6330 error
= got_error_from_errno("strdup");
6336 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
6341 const char *prefix
= got_worktree_get_path_prefix(worktree
);
6344 if (path
== NULL
|| got_path_is_root_dir(path
))
6346 error
= got_worktree_resolve_path(&p
, worktree
, path
);
6349 if (asprintf(&in_repo_path
, "%s%s%s", prefix
,
6350 (p
[0] != '\0' && !got_path_is_root_dir(prefix
)) ? "/" : "",
6352 error
= got_error_from_errno("asprintf");
6357 error
= apply_unveil(got_repo_get_path(repo
), 1, NULL
);
6361 error
= apply_unveil(got_repo_get_path(repo
), 1, NULL
);
6366 error
= got_repo_map_path(&in_repo_path
, repo
, path
);
6371 if (commit_id_str
== NULL
) {
6372 struct got_reference
*head_ref
;
6374 refname
= got_worktree_get_head_ref_name(worktree
);
6376 refname
= GOT_REF_HEAD
;
6377 error
= got_ref_open(&head_ref
, repo
, refname
, 0);
6380 error
= got_ref_resolve(&commit_id
, repo
, head_ref
);
6381 got_ref_close(head_ref
);
6385 struct got_reflist_head refs
;
6388 error
= got_ref_list(&refs
, repo
, NULL
, got_ref_cmp_by_name
,
6393 error
= got_keyword_to_idstr(&keyword_idstr
, commit_id_str
,
6397 if (keyword_idstr
!= NULL
)
6398 commit_id_str
= keyword_idstr
;
6400 error
= got_repo_match_object_id(&commit_id
, NULL
,
6401 commit_id_str
, GOT_OBJ_TYPE_COMMIT
, &refs
, repo
);
6402 got_ref_list_free(&refs
);
6408 /* Release work tree lock. */
6409 got_worktree_close(worktree
);
6413 error
= got_object_open_as_commit(&commit
, repo
, commit_id
);
6417 error
= print_tree(in_repo_path
, commit
, show_ids
, recurse
,
6418 in_repo_path
, repo
);
6420 free(keyword_idstr
);
6426 got_object_commit_close(commit
);
6428 got_worktree_close(worktree
);
6430 const struct got_error
*close_err
= got_repo_close(repo
);
6435 const struct got_error
*pack_err
=
6436 got_repo_pack_fds_close(pack_fds
);
6446 fprintf(stderr
, "usage: %s status [-I] [-S status-codes] "
6447 "[-s status-codes] [path ...]\n", getprogname());
6451 struct got_status_arg
{
6456 static const struct got_error
*
6457 print_status(void *arg
, unsigned char status
, unsigned char staged_status
,
6458 const char *path
, struct got_object_id
*blob_id
,
6459 struct got_object_id
*staged_blob_id
, struct got_object_id
*commit_id
,
6460 int dirfd
, const char *de_name
)
6462 struct got_status_arg
*st
= arg
;
6464 if (status
== staged_status
&& (status
== GOT_STATUS_DELETE
))
6465 status
= GOT_STATUS_NO_CHANGE
;
6466 if (st
!= NULL
&& st
->status_codes
) {
6467 size_t ncodes
= strlen(st
->status_codes
);
6470 for (i
= 0; i
< ncodes
; i
++) {
6472 if (status
== st
->status_codes
[i
] ||
6473 staged_status
== st
->status_codes
[i
]) {
6478 if (status
== st
->status_codes
[i
] ||
6479 staged_status
== st
->status_codes
[i
])
6484 if (st
->suppress
&& j
== 0)
6491 printf("%c%c %s\n", status
, staged_status
, path
);
6495 static const struct got_error
*
6496 show_operation_in_progress(struct got_worktree
*worktree
,
6497 struct got_repository
*repo
)
6499 const struct got_error
*err
;
6500 char *new_base_branch_name
= NULL
;
6501 char *branch_name
= NULL
;
6502 int rebase_in_progress
, histedit_in_progress
, merge_in_progress
;
6504 err
= got_worktree_rebase_in_progress(&rebase_in_progress
, worktree
);
6507 if (rebase_in_progress
) {
6508 err
= got_worktree_rebase_info(&new_base_branch_name
,
6509 &branch_name
, worktree
, repo
);
6512 printf("Work tree is rebasing %s onto %s\n",
6513 branch_name
, new_base_branch_name
);
6516 err
= got_worktree_histedit_in_progress(&histedit_in_progress
,
6520 if (histedit_in_progress
) {
6521 err
= got_worktree_histedit_info(&branch_name
, worktree
, repo
);
6524 printf("Work tree is editing the history of %s\n", branch_name
);
6527 err
= got_worktree_merge_in_progress(&merge_in_progress
,
6531 if (merge_in_progress
) {
6532 err
= got_worktree_merge_info(&branch_name
, worktree
,
6536 printf("Work tree is merging %s into %s\n", branch_name
,
6537 got_worktree_get_head_ref_name(worktree
));
6540 free(new_base_branch_name
);
6545 static const struct got_error
*
6546 cmd_status(int argc
, char *argv
[])
6548 const struct got_error
*close_err
, *error
= NULL
;
6549 struct got_repository
*repo
= NULL
;
6550 struct got_worktree
*worktree
= NULL
;
6551 struct got_status_arg st
;
6553 struct got_pathlist_head paths
;
6554 int ch
, i
, no_ignores
= 0;
6555 int *pack_fds
= NULL
;
6559 memset(&st
, 0, sizeof(st
));
6560 st
.status_codes
= NULL
;
6564 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
6569 while ((ch
= getopt(argc
, argv
, "IS:s:")) != -1) {
6575 if (st
.status_codes
!= NULL
&& st
.suppress
== 0)
6576 option_conflict('S', 's');
6580 for (i
= 0; optarg
[i
] != '\0'; i
++) {
6581 switch (optarg
[i
]) {
6582 case GOT_STATUS_MODIFY
:
6583 case GOT_STATUS_ADD
:
6584 case GOT_STATUS_DELETE
:
6585 case GOT_STATUS_CONFLICT
:
6586 case GOT_STATUS_MISSING
:
6587 case GOT_STATUS_OBSTRUCTED
:
6588 case GOT_STATUS_UNVERSIONED
:
6589 case GOT_STATUS_MODE_CHANGE
:
6590 case GOT_STATUS_NONEXISTENT
:
6593 errx(1, "invalid status code '%c'",
6597 if (ch
== 's' && st
.suppress
)
6598 option_conflict('s', 'S');
6599 st
.status_codes
= optarg
;
6610 cwd
= getcwd(NULL
, 0);
6612 error
= got_error_from_errno("getcwd");
6616 error
= got_repo_pack_fds_open(&pack_fds
);
6620 error
= got_worktree_open(&worktree
, cwd
,
6621 GOT_WORKTREE_GOT_DIR
);
6623 if (error
->code
== GOT_ERR_NOT_WORKTREE
)
6624 error
= wrap_not_worktree_error(error
, "status", cwd
);
6628 error
= got_repo_open(&repo
, got_worktree_get_repo_path(worktree
),
6633 error
= apply_unveil(got_repo_get_path(repo
), 1,
6634 got_worktree_get_root_path(worktree
));
6638 error
= get_worktree_paths_from_argv(&paths
, argc
, argv
, worktree
);
6642 error
= got_worktree_status(worktree
, &paths
, repo
, no_ignores
,
6643 print_status
, &st
, check_cancelled
, NULL
);
6647 error
= show_operation_in_progress(worktree
, repo
);
6650 const struct got_error
*pack_err
=
6651 got_repo_pack_fds_close(pack_fds
);
6656 close_err
= got_repo_close(repo
);
6660 if (worktree
!= NULL
) {
6661 close_err
= got_worktree_close(worktree
);
6666 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_PATH
);
6674 fprintf(stderr
, "usage: %s ref [-dlt] [-c object] [-r repository-path] "
6675 "[-s reference] [name]\n", getprogname());
6679 static const struct got_error
*
6680 list_refs(struct got_repository
*repo
, const char *refname
, int sort_by_time
)
6682 static const struct got_error
*err
= NULL
;
6683 struct got_reflist_head refs
;
6684 struct got_reflist_entry
*re
;
6687 err
= got_ref_list(&refs
, repo
, refname
, sort_by_time
?
6688 got_ref_cmp_by_commit_timestamp_descending
: got_ref_cmp_by_name
,
6693 TAILQ_FOREACH(re
, &refs
, entry
) {
6695 refstr
= got_ref_to_str(re
->ref
);
6696 if (refstr
== NULL
) {
6697 err
= got_error_from_errno("got_ref_to_str");
6700 printf("%s: %s\n", got_ref_get_name(re
->ref
), refstr
);
6704 got_ref_list_free(&refs
);
6708 static const struct got_error
*
6709 delete_ref_by_name(struct got_repository
*repo
, const char *refname
)
6711 const struct got_error
*err
;
6712 struct got_reference
*ref
;
6714 err
= got_ref_open(&ref
, repo
, refname
, 0);
6718 err
= delete_ref(repo
, ref
);
6723 static const struct got_error
*
6724 add_ref(struct got_repository
*repo
, const char *refname
, const char *target
)
6726 const struct got_error
*err
= NULL
;
6727 struct got_object_id
*id
= NULL
;
6728 struct got_reference
*ref
= NULL
;
6729 struct got_reflist_head refs
;
6732 * Don't let the user create a reference name with a leading '-'.
6733 * While technically a valid reference name, this case is usually
6734 * an unintended typo.
6736 if (refname
[0] == '-')
6737 return got_error_path(refname
, GOT_ERR_REF_NAME_MINUS
);
6740 err
= got_ref_list(&refs
, repo
, NULL
, got_ref_cmp_by_name
, NULL
);
6743 err
= got_repo_match_object_id(&id
, NULL
, target
, GOT_OBJ_TYPE_ANY
,
6745 got_ref_list_free(&refs
);
6749 err
= got_ref_alloc(&ref
, refname
, id
);
6753 err
= got_ref_write(ref
, repo
);
6761 static const struct got_error
*
6762 add_symref(struct got_repository
*repo
, const char *refname
, const char *target
)
6764 const struct got_error
*err
= NULL
;
6765 struct got_reference
*ref
= NULL
;
6766 struct got_reference
*target_ref
= NULL
;
6769 * Don't let the user create a reference name with a leading '-'.
6770 * While technically a valid reference name, this case is usually
6771 * an unintended typo.
6773 if (refname
[0] == '-')
6774 return got_error_path(refname
, GOT_ERR_REF_NAME_MINUS
);
6776 err
= got_ref_open(&target_ref
, repo
, target
, 0);
6780 err
= got_ref_alloc_symref(&ref
, refname
, target_ref
);
6784 err
= got_ref_write(ref
, repo
);
6787 got_ref_close(target_ref
);
6793 static const struct got_error
*
6794 cmd_ref(int argc
, char *argv
[])
6796 const struct got_error
*error
= NULL
;
6797 struct got_repository
*repo
= NULL
;
6798 struct got_worktree
*worktree
= NULL
;
6799 char *cwd
= NULL
, *repo_path
= NULL
;
6800 int ch
, do_list
= 0, do_delete
= 0, sort_by_time
= 0;
6801 const char *obj_arg
= NULL
, *symref_target
= NULL
;
6802 char *refname
= NULL
, *keyword_idstr
= NULL
;
6803 int *pack_fds
= NULL
;
6806 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
6807 "sendfd unveil", NULL
) == -1)
6811 while ((ch
= getopt(argc
, argv
, "c:dlr:s:t")) != -1) {
6823 repo_path
= realpath(optarg
, NULL
);
6824 if (repo_path
== NULL
)
6825 return got_error_from_errno2("realpath",
6827 got_path_strip_trailing_slashes(repo_path
);
6830 symref_target
= optarg
;
6841 if (obj_arg
&& do_list
)
6842 option_conflict('c', 'l');
6843 if (obj_arg
&& do_delete
)
6844 option_conflict('c', 'd');
6845 if (obj_arg
&& symref_target
)
6846 option_conflict('c', 's');
6847 if (symref_target
&& do_delete
)
6848 option_conflict('s', 'd');
6849 if (symref_target
&& do_list
)
6850 option_conflict('s', 'l');
6851 if (do_delete
&& do_list
)
6852 option_conflict('d', 'l');
6853 if (sort_by_time
&& !do_list
)
6854 errx(1, "-t option requires -l option");
6860 if (argc
!= 0 && argc
!= 1)
6863 refname
= strdup(argv
[0]);
6864 if (refname
== NULL
) {
6865 error
= got_error_from_errno("strdup");
6872 refname
= strdup(argv
[0]);
6873 if (refname
== NULL
) {
6874 error
= got_error_from_errno("strdup");
6880 got_path_strip_trailing_slashes(refname
);
6882 cwd
= getcwd(NULL
, 0);
6884 error
= got_error_from_errno("getcwd");
6888 error
= got_repo_pack_fds_open(&pack_fds
);
6892 if (repo_path
== NULL
) {
6893 error
= got_worktree_open(&worktree
, cwd
,
6894 GOT_WORKTREE_GOT_DIR
);
6895 if (error
&& error
->code
!= GOT_ERR_NOT_WORKTREE
)
6901 strdup(got_worktree_get_repo_path(worktree
));
6902 if (repo_path
== NULL
)
6903 error
= got_error_from_errno("strdup");
6907 repo_path
= strdup(cwd
);
6908 if (repo_path
== NULL
) {
6909 error
= got_error_from_errno("strdup");
6915 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
6921 /* Remove "cpath" promise. */
6922 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
6928 error
= apply_unveil(got_repo_get_path(repo
), do_list
,
6929 worktree
? got_worktree_get_root_path(worktree
) : NULL
);
6934 error
= list_refs(repo
, refname
, sort_by_time
);
6936 error
= delete_ref_by_name(repo
, refname
);
6937 else if (symref_target
)
6938 error
= add_symref(repo
, refname
, symref_target
);
6940 if (obj_arg
== NULL
)
6943 error
= got_keyword_to_idstr(&keyword_idstr
, obj_arg
,
6947 if (keyword_idstr
!= NULL
)
6948 obj_arg
= keyword_idstr
;
6950 error
= add_ref(repo
, refname
, obj_arg
);
6955 const struct got_error
*close_err
= got_repo_close(repo
);
6960 got_worktree_close(worktree
);
6962 const struct got_error
*pack_err
=
6963 got_repo_pack_fds_close(pack_fds
);
6969 free(keyword_idstr
);
6976 fprintf(stderr
, "usage: %s branch [-lnt] [-c commit] [-d name] "
6977 "[-r repository-path] [name]\n", getprogname());
6981 static const struct got_error
*
6982 list_branch(struct got_repository
*repo
, struct got_worktree
*worktree
,
6983 struct got_reference
*ref
)
6985 const struct got_error
*err
= NULL
;
6986 const char *refname
;
6990 refname
= got_ref_get_name(ref
);
6991 if (worktree
&& strcmp(refname
,
6992 got_worktree_get_head_ref_name(worktree
)) == 0) {
6993 err
= got_worktree_get_state(&marker
, repo
, worktree
,
6994 check_cancelled
, NULL
);
6999 if (strncmp(refname
, "refs/heads/", 11) == 0)
7001 if (strncmp(refname
, "refs/got/worktree/", 18) == 0)
7003 if (strncmp(refname
, "refs/remotes/", 13) == 0)
7006 refstr
= got_ref_to_str(ref
);
7008 return got_error_from_errno("got_ref_to_str");
7010 printf("%c %s: %s\n", marker
, refname
, refstr
);
7015 static const struct got_error
*
7016 show_current_branch(struct got_repository
*repo
, struct got_worktree
*worktree
)
7018 const char *refname
;
7020 if (worktree
== NULL
)
7021 return got_error(GOT_ERR_NOT_WORKTREE
);
7023 refname
= got_worktree_get_head_ref_name(worktree
);
7025 if (strncmp(refname
, "refs/heads/", 11) == 0)
7027 if (strncmp(refname
, "refs/got/worktree/", 18) == 0)
7030 printf("%s\n", refname
);
7035 static const struct got_error
*
7036 list_branches(struct got_repository
*repo
, struct got_worktree
*worktree
,
7039 static const struct got_error
*err
= NULL
;
7040 struct got_reflist_head refs
;
7041 struct got_reflist_entry
*re
;
7042 struct got_reference
*temp_ref
= NULL
;
7043 int rebase_in_progress
, histedit_in_progress
;
7048 err
= got_worktree_rebase_in_progress(&rebase_in_progress
,
7053 err
= got_worktree_histedit_in_progress(&histedit_in_progress
,
7058 if (rebase_in_progress
|| histedit_in_progress
) {
7059 err
= got_ref_open(&temp_ref
, repo
,
7060 got_worktree_get_head_ref_name(worktree
), 0);
7063 list_branch(repo
, worktree
, temp_ref
);
7064 got_ref_close(temp_ref
);
7068 err
= got_ref_list(&refs
, repo
, "refs/heads", sort_by_time
?
7069 got_ref_cmp_by_commit_timestamp_descending
: got_ref_cmp_by_name
,
7074 TAILQ_FOREACH(re
, &refs
, entry
)
7075 list_branch(repo
, worktree
, re
->ref
);
7077 got_ref_list_free(&refs
);
7079 err
= got_ref_list(&refs
, repo
, "refs/remotes", sort_by_time
?
7080 got_ref_cmp_by_commit_timestamp_descending
: got_ref_cmp_by_name
,
7085 TAILQ_FOREACH(re
, &refs
, entry
)
7086 list_branch(repo
, worktree
, re
->ref
);
7088 got_ref_list_free(&refs
);
7093 static const struct got_error
*
7094 delete_branch(struct got_repository
*repo
, struct got_worktree
*worktree
,
7095 const char *branch_name
)
7097 const struct got_error
*err
= NULL
;
7098 struct got_reference
*ref
= NULL
;
7099 char *refname
, *remote_refname
= NULL
;
7101 if (strncmp(branch_name
, "refs/", 5) == 0)
7103 if (strncmp(branch_name
, "heads/", 6) == 0)
7105 else if (strncmp(branch_name
, "remotes/", 8) == 0)
7108 if (asprintf(&refname
, "refs/heads/%s", branch_name
) == -1)
7109 return got_error_from_errno("asprintf");
7111 if (asprintf(&remote_refname
, "refs/remotes/%s",
7112 branch_name
) == -1) {
7113 err
= got_error_from_errno("asprintf");
7117 err
= got_ref_open(&ref
, repo
, refname
, 0);
7119 const struct got_error
*err2
;
7120 if (err
->code
!= GOT_ERR_NOT_REF
)
7123 * Keep 'err' intact such that if neither branch exists
7124 * we report "refs/heads" rather than "refs/remotes" in
7125 * our error message.
7127 err2
= got_ref_open(&ref
, repo
, remote_refname
, 0);
7134 strcmp(got_worktree_get_head_ref_name(worktree
),
7135 got_ref_get_name(ref
)) == 0) {
7136 err
= got_error_msg(GOT_ERR_SAME_BRANCH
,
7137 "will not delete this work tree's current branch");
7141 err
= delete_ref(repo
, ref
);
7146 free(remote_refname
);
7150 static const struct got_error
*
7151 add_branch(struct got_repository
*repo
, const char *branch_name
,
7152 struct got_object_id
*base_commit_id
)
7154 const struct got_error
*err
= NULL
;
7155 struct got_reference
*ref
= NULL
;
7156 char *refname
= NULL
;
7159 * Don't let the user create a branch name with a leading '-'.
7160 * While technically a valid reference name, this case is usually
7161 * an unintended typo.
7163 if (branch_name
[0] == '-')
7164 return got_error_path(branch_name
, GOT_ERR_REF_NAME_MINUS
);
7166 if (strncmp(branch_name
, "refs/heads/", 11) == 0)
7169 if (asprintf(&refname
, "refs/heads/%s", branch_name
) == -1) {
7170 err
= got_error_from_errno("asprintf");
7174 err
= got_ref_open(&ref
, repo
, refname
, 0);
7176 err
= got_error(GOT_ERR_BRANCH_EXISTS
);
7178 } else if (err
->code
!= GOT_ERR_NOT_REF
)
7181 err
= got_ref_alloc(&ref
, refname
, base_commit_id
);
7185 err
= got_ref_write(ref
, repo
);
7193 static const struct got_error
*
7194 cmd_branch(int argc
, char *argv
[])
7196 const struct got_error
*error
= NULL
;
7197 struct got_repository
*repo
= NULL
;
7198 struct got_worktree
*worktree
= NULL
;
7199 char *cwd
= NULL
, *repo_path
= NULL
;
7200 int ch
, do_list
= 0, do_show
= 0, do_update
= 1, sort_by_time
= 0;
7201 const char *delref
= NULL
, *commit_id_arg
= NULL
;
7202 struct got_reference
*ref
= NULL
;
7203 struct got_pathlist_head paths
;
7204 struct got_object_id
*commit_id
= NULL
;
7205 char *commit_id_str
= NULL
, *keyword_idstr
= NULL
;;
7206 int *pack_fds
= NULL
;
7211 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
7212 "sendfd unveil", NULL
) == -1)
7216 while ((ch
= getopt(argc
, argv
, "c:d:lnr:t")) != -1) {
7219 commit_id_arg
= optarg
;
7231 repo_path
= realpath(optarg
, NULL
);
7232 if (repo_path
== NULL
)
7233 return got_error_from_errno2("realpath",
7235 got_path_strip_trailing_slashes(repo_path
);
7246 if (do_list
&& delref
)
7247 option_conflict('l', 'd');
7248 if (sort_by_time
&& !do_list
)
7249 errx(1, "-t option requires -l option");
7254 if (!do_list
&& !delref
&& argc
== 0)
7257 if ((do_list
|| delref
|| do_show
) && commit_id_arg
!= NULL
)
7258 errx(1, "-c option can only be used when creating a branch");
7260 if (do_list
|| delref
) {
7263 } else if (!do_show
&& argc
!= 1)
7266 cwd
= getcwd(NULL
, 0);
7268 error
= got_error_from_errno("getcwd");
7272 error
= got_repo_pack_fds_open(&pack_fds
);
7276 if (repo_path
== NULL
) {
7277 error
= got_worktree_open(&worktree
, cwd
,
7278 GOT_WORKTREE_GOT_DIR
);
7279 if (error
&& error
->code
!= GOT_ERR_NOT_WORKTREE
)
7285 strdup(got_worktree_get_repo_path(worktree
));
7286 if (repo_path
== NULL
)
7287 error
= got_error_from_errno("strdup");
7291 repo_path
= strdup(cwd
);
7292 if (repo_path
== NULL
) {
7293 error
= got_error_from_errno("strdup");
7299 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
7304 if (do_list
|| do_show
) {
7305 /* Remove "cpath" promise. */
7306 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
7312 error
= apply_unveil(got_repo_get_path(repo
), do_list
,
7313 worktree
? got_worktree_get_root_path(worktree
) : NULL
);
7318 error
= show_current_branch(repo
, worktree
);
7320 error
= list_branches(repo
, worktree
, sort_by_time
);
7322 error
= delete_branch(repo
, worktree
, delref
);
7324 struct got_reflist_head refs
;
7326 error
= got_ref_list(&refs
, repo
, NULL
, got_ref_cmp_by_name
,
7330 if (commit_id_arg
== NULL
)
7331 commit_id_arg
= worktree
?
7332 got_worktree_get_head_ref_name(worktree
) :
7335 error
= got_keyword_to_idstr(&keyword_idstr
,
7336 commit_id_arg
, repo
, worktree
);
7339 if (keyword_idstr
!= NULL
)
7340 commit_id_arg
= keyword_idstr
;
7342 error
= got_repo_match_object_id(&commit_id
, NULL
,
7343 commit_id_arg
, GOT_OBJ_TYPE_COMMIT
, &refs
, repo
);
7344 got_ref_list_free(&refs
);
7347 error
= add_branch(repo
, argv
[0], commit_id
);
7350 if (worktree
&& do_update
) {
7351 struct got_update_progress_arg upa
;
7352 char *branch_refname
= NULL
;
7354 error
= got_object_id_str(&commit_id_str
, commit_id
);
7357 error
= get_worktree_paths_from_argv(&paths
, 0, NULL
,
7361 if (asprintf(&branch_refname
, "refs/heads/%s", argv
[0])
7363 error
= got_error_from_errno("asprintf");
7366 error
= got_ref_open(&ref
, repo
, branch_refname
, 0);
7367 free(branch_refname
);
7370 error
= switch_head_ref(ref
, commit_id
, worktree
,
7374 error
= got_worktree_set_base_commit_id(worktree
, repo
,
7378 memset(&upa
, 0, sizeof(upa
));
7379 error
= got_worktree_checkout_files(worktree
, &paths
,
7380 repo
, update_progress
, &upa
, check_cancelled
,
7384 if (upa
.did_something
) {
7385 printf("Updated to %s: %s\n",
7386 got_worktree_get_head_ref_name(worktree
),
7389 print_update_progress_stats(&upa
);
7393 free(keyword_idstr
);
7397 const struct got_error
*close_err
= got_repo_close(repo
);
7402 got_worktree_close(worktree
);
7404 const struct got_error
*pack_err
=
7405 got_repo_pack_fds_close(pack_fds
);
7412 free(commit_id_str
);
7413 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_PATH
);
7421 fprintf(stderr
, "usage: %s tag [-lVv] [-c commit] [-m message] "
7422 "[-r repository-path] [-s signer-id] name\n", getprogname());
7427 static const struct got_error
*
7428 sort_tags(struct got_reflist_head
*sorted
, struct got_reflist_head
*tags
)
7430 const struct got_error
*err
= NULL
;
7431 struct got_reflist_entry
*re
, *se
, *new;
7432 struct got_object_id
*re_id
, *se_id
;
7433 struct got_tag_object
*re_tag
, *se_tag
;
7434 time_t re_time
, se_time
;
7436 STAILQ_FOREACH(re
, tags
, entry
) {
7437 se
= STAILQ_FIRST(sorted
);
7439 err
= got_reflist_entry_dup(&new, re
);
7442 STAILQ_INSERT_HEAD(sorted
, new, entry
);
7445 err
= got_ref_resolve(&re_id
, repo
, re
->ref
);
7448 err
= got_object_open_as_tag(&re_tag
, repo
, re_id
);
7452 re_time
= got_object_tag_get_tagger_time(re_tag
);
7453 got_object_tag_close(re_tag
);
7457 err
= got_ref_resolve(&se_id
, repo
, re
->ref
);
7460 err
= got_object_open_as_tag(&se_tag
, repo
, se_id
);
7464 se_time
= got_object_tag_get_tagger_time(se_tag
);
7465 got_object_tag_close(se_tag
);
7467 if (se_time
> re_time
) {
7468 err
= got_reflist_entry_dup(&new, re
);
7471 STAILQ_INSERT_AFTER(sorted
, se
, new, entry
);
7474 se
= STAILQ_NEXT(se
, entry
);
7483 static const struct got_error
*
7484 get_tag_refname(char **refname
, const char *tag_name
)
7486 const struct got_error
*err
;
7488 if (strncmp("refs/tags/", tag_name
, 10) == 0) {
7489 *refname
= strdup(tag_name
);
7490 if (*refname
== NULL
)
7491 return got_error_from_errno("strdup");
7492 } else if (asprintf(refname
, "refs/tags/%s", tag_name
) == -1) {
7493 err
= got_error_from_errno("asprintf");
7501 static const struct got_error
*
7502 list_tags(struct got_repository
*repo
, const char *tag_name
, int verify_tags
,
7503 const char *allowed_signers
, const char *revoked_signers
, int verbosity
)
7505 static const struct got_error
*err
= NULL
;
7506 struct got_reflist_head refs
;
7507 struct got_reflist_entry
*re
;
7508 char *wanted_refname
= NULL
;
7513 err
= got_ref_list(&refs
, repo
, "refs/tags", got_ref_cmp_tags
, repo
);
7518 struct got_reference
*ref
;
7519 err
= get_tag_refname(&wanted_refname
, tag_name
);
7522 /* Wanted tag reference should exist. */
7523 err
= got_ref_open(&ref
, repo
, wanted_refname
, 0);
7529 TAILQ_FOREACH(re
, &refs
, entry
) {
7530 const char *refname
;
7531 char *refstr
, *tagmsg0
, *tagmsg
, *line
, *id_str
, *datestr
;
7533 const char *tagger
, *ssh_sig
= NULL
;
7534 char *sig_msg
= NULL
;
7536 struct got_object_id
*id
;
7537 struct got_tag_object
*tag
;
7538 struct got_commit_object
*commit
= NULL
;
7540 refname
= got_ref_get_name(re
->ref
);
7541 if (strncmp(refname
, "refs/tags/", 10) != 0 ||
7542 (wanted_refname
&& strcmp(refname
, wanted_refname
) != 0))
7545 refstr
= got_ref_to_str(re
->ref
);
7546 if (refstr
== NULL
) {
7547 err
= got_error_from_errno("got_ref_to_str");
7551 err
= got_ref_resolve(&id
, repo
, re
->ref
);
7554 err
= got_object_open_as_tag(&tag
, repo
, id
);
7556 if (err
->code
!= GOT_ERR_OBJ_TYPE
) {
7560 /* "lightweight" tag */
7561 err
= got_object_open_as_commit(&commit
, repo
, id
);
7566 tagger
= got_object_commit_get_committer(commit
);
7568 got_object_commit_get_committer_time(commit
);
7569 err
= got_object_id_str(&id_str
, id
);
7575 tagger
= got_object_tag_get_tagger(tag
);
7576 tagger_time
= got_object_tag_get_tagger_time(tag
);
7577 err
= got_object_id_str(&id_str
,
7578 got_object_tag_get_object_id(tag
));
7583 if (tag
&& verify_tags
) {
7584 ssh_sig
= got_sigs_get_tagmsg_ssh_signature(
7585 got_object_tag_get_message(tag
));
7586 if (ssh_sig
&& allowed_signers
== NULL
) {
7587 err
= got_error_msg(
7588 GOT_ERR_VERIFY_TAG_SIGNATURE
,
7589 "SSH signature verification requires "
7590 "setting allowed_signers in "
7596 printf("%stag %s %s\n", GOT_COMMIT_SEP_STR
, refname
, refstr
);
7598 printf("from: %s\n", tagger
);
7599 datestr
= get_datestr(&tagger_time
, datebuf
);
7601 printf("date: %s UTC\n", datestr
);
7603 printf("object: %s %s\n", GOT_OBJ_LABEL_COMMIT
, id_str
);
7605 switch (got_object_tag_get_object_type(tag
)) {
7606 case GOT_OBJ_TYPE_BLOB
:
7607 printf("object: %s %s\n", GOT_OBJ_LABEL_BLOB
,
7610 case GOT_OBJ_TYPE_TREE
:
7611 printf("object: %s %s\n", GOT_OBJ_LABEL_TREE
,
7614 case GOT_OBJ_TYPE_COMMIT
:
7615 printf("object: %s %s\n", GOT_OBJ_LABEL_COMMIT
,
7618 case GOT_OBJ_TYPE_TAG
:
7619 printf("object: %s %s\n", GOT_OBJ_LABEL_TAG
,
7629 err
= got_sigs_verify_tag_ssh(&sig_msg
, tag
, ssh_sig
,
7630 allowed_signers
, revoked_signers
, verbosity
);
7631 if (err
&& err
->code
== GOT_ERR_BAD_TAG_SIGNATURE
)
7635 printf("signature: %s", sig_msg
);
7641 err
= got_object_commit_get_logmsg(&tagmsg0
, commit
);
7644 got_object_commit_close(commit
);
7646 tagmsg0
= strdup(got_object_tag_get_message(tag
));
7647 got_object_tag_close(tag
);
7648 if (tagmsg0
== NULL
) {
7649 err
= got_error_from_errno("strdup");
7656 line
= strsep(&tagmsg
, "\n");
7658 printf(" %s\n", line
);
7663 got_ref_list_free(&refs
);
7664 free(wanted_refname
);
7666 if (err
== NULL
&& bad_sigs
)
7667 err
= got_error(GOT_ERR_BAD_TAG_SIGNATURE
);
7671 static const struct got_error
*
7672 get_tag_message(char **tagmsg
, char **tagmsg_path
, const char *commit_id_str
,
7673 const char *tag_name
, const char *editor
, const char *repo_path
)
7675 const struct got_error
*err
= NULL
;
7676 char *template = NULL
, *initial_content
= NULL
;
7677 int initial_content_len
;
7680 if (asprintf(&template, GOT_TMPDIR_STR
"/got-tagmsg") == -1) {
7681 err
= got_error_from_errno("asprintf");
7685 initial_content_len
= asprintf(&initial_content
,
7686 "\n# tagging commit %s as %s\n",
7687 commit_id_str
, tag_name
);
7688 if (initial_content_len
== -1) {
7689 err
= got_error_from_errno("asprintf");
7693 err
= got_opentemp_named_fd(tagmsg_path
, &fd
, template, "");
7697 if (write(fd
, initial_content
, initial_content_len
) == -1) {
7698 err
= got_error_from_errno2("write", *tagmsg_path
);
7701 if (close(fd
) == -1) {
7702 err
= got_error_from_errno2("close", *tagmsg_path
);
7707 err
= edit_logmsg(tagmsg
, editor
, *tagmsg_path
, initial_content
,
7708 initial_content_len
, 1);
7710 free(initial_content
);
7713 if (fd
!= -1 && close(fd
) == -1 && err
== NULL
)
7714 err
= got_error_from_errno2("close", *tagmsg_path
);
7723 static const struct got_error
*
7724 add_tag(struct got_repository
*repo
, const char *tagger
,
7725 const char *tag_name
, const char *commit_arg
, const char *tagmsg_arg
,
7726 const char *signer_id
, const char *editor
, int verbosity
)
7728 const struct got_error
*err
= NULL
;
7729 struct got_object_id
*commit_id
= NULL
, *tag_id
= NULL
;
7730 char *label
= NULL
, *commit_id_str
= NULL
;
7731 struct got_reference
*ref
= NULL
;
7732 char *refname
= NULL
, *tagmsg
= NULL
;
7733 char *tagmsg_path
= NULL
, *tag_id_str
= NULL
;
7734 int preserve_tagmsg
= 0;
7735 struct got_reflist_head refs
;
7740 * Don't let the user create a tag name with a leading '-'.
7741 * While technically a valid reference name, this case is usually
7742 * an unintended typo.
7744 if (tag_name
[0] == '-')
7745 return got_error_path(tag_name
, GOT_ERR_REF_NAME_MINUS
);
7747 err
= got_ref_list(&refs
, repo
, NULL
, got_ref_cmp_by_name
, NULL
);
7751 err
= got_repo_match_object_id(&commit_id
, &label
, commit_arg
,
7752 GOT_OBJ_TYPE_COMMIT
, &refs
, repo
);
7756 err
= got_object_id_str(&commit_id_str
, commit_id
);
7760 err
= get_tag_refname(&refname
, tag_name
);
7763 if (strncmp("refs/tags/", tag_name
, 10) == 0)
7766 err
= got_ref_open(&ref
, repo
, refname
, 0);
7768 err
= got_error(GOT_ERR_TAG_EXISTS
);
7770 } else if (err
->code
!= GOT_ERR_NOT_REF
)
7773 if (tagmsg_arg
== NULL
) {
7774 err
= get_tag_message(&tagmsg
, &tagmsg_path
, commit_id_str
,
7775 tag_name
, editor
, got_repo_get_path(repo
));
7777 if (err
->code
!= GOT_ERR_COMMIT_MSG_EMPTY
&&
7778 tagmsg_path
!= NULL
)
7779 preserve_tagmsg
= 1;
7784 err
= got_object_tag_create(&tag_id
, tag_name
, commit_id
,
7785 tagger
, time(NULL
), tagmsg
? tagmsg
: tagmsg_arg
, signer_id
, repo
,
7789 preserve_tagmsg
= 1;
7793 err
= got_ref_alloc(&ref
, refname
, tag_id
);
7796 preserve_tagmsg
= 1;
7800 err
= got_ref_write(ref
, repo
);
7803 preserve_tagmsg
= 1;
7807 err
= got_object_id_str(&tag_id_str
, tag_id
);
7810 preserve_tagmsg
= 1;
7813 printf("Created tag %s\n", tag_id_str
);
7815 if (preserve_tagmsg
) {
7816 fprintf(stderr
, "%s: tag message preserved in %s\n",
7817 getprogname(), tagmsg_path
);
7818 } else if (tagmsg_path
&& unlink(tagmsg_path
) == -1 && err
== NULL
)
7819 err
= got_error_from_errno2("unlink", tagmsg_path
);
7824 free(commit_id_str
);
7828 got_ref_list_free(&refs
);
7832 static const struct got_error
*
7833 cmd_tag(int argc
, char *argv
[])
7835 const struct got_error
*error
= NULL
;
7836 struct got_repository
*repo
= NULL
;
7837 struct got_worktree
*worktree
= NULL
;
7838 char *cwd
= NULL
, *repo_path
= NULL
, *commit_id_str
= NULL
;
7839 char *gitconfig_path
= NULL
, *tagger
= NULL
, *keyword_idstr
= NULL
;
7840 char *allowed_signers
= NULL
, *revoked_signers
= NULL
, *editor
= NULL
;
7841 const char *signer_id
= NULL
;
7842 const char *tag_name
= NULL
, *commit_id_arg
= NULL
, *tagmsg
= NULL
;
7843 int ch
, do_list
= 0, verify_tags
= 0, verbosity
= 0;
7844 int *pack_fds
= NULL
;
7847 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
7848 "sendfd unveil", NULL
) == -1)
7852 while ((ch
= getopt(argc
, argv
, "c:lm:r:s:Vv")) != -1) {
7855 commit_id_arg
= optarg
;
7864 repo_path
= realpath(optarg
, NULL
);
7865 if (repo_path
== NULL
) {
7866 error
= got_error_from_errno2("realpath",
7870 got_path_strip_trailing_slashes(repo_path
);
7881 else if (verbosity
< 3)
7893 if (do_list
|| verify_tags
) {
7894 if (commit_id_arg
!= NULL
)
7896 "-c option can only be used when creating a tag");
7899 option_conflict('l', 'm');
7901 option_conflict('V', 'm');
7905 option_conflict('l', 's');
7907 option_conflict('V', 's');
7911 } else if (argc
!= 1)
7917 cwd
= getcwd(NULL
, 0);
7919 error
= got_error_from_errno("getcwd");
7923 error
= got_repo_pack_fds_open(&pack_fds
);
7927 if (repo_path
== NULL
) {
7928 error
= got_worktree_open(&worktree
, cwd
,
7929 GOT_WORKTREE_GOT_DIR
);
7930 if (error
&& error
->code
!= GOT_ERR_NOT_WORKTREE
)
7936 strdup(got_worktree_get_repo_path(worktree
));
7937 if (repo_path
== NULL
)
7938 error
= got_error_from_errno("strdup");
7942 repo_path
= strdup(cwd
);
7943 if (repo_path
== NULL
) {
7944 error
= got_error_from_errno("strdup");
7950 if (do_list
|| verify_tags
) {
7951 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
7954 error
= get_allowed_signers(&allowed_signers
, repo
, worktree
);
7957 error
= get_revoked_signers(&revoked_signers
, repo
, worktree
);
7961 /* Release work tree lock. */
7962 got_worktree_close(worktree
);
7967 * Remove "cpath" promise unless needed for signature tmpfile
7971 got_sigs_apply_unveil();
7974 if (pledge("stdio rpath wpath flock proc exec sendfd "
7975 "unveil", NULL
) == -1)
7979 error
= apply_unveil(got_repo_get_path(repo
), 1, NULL
);
7982 error
= list_tags(repo
, tag_name
, verify_tags
, allowed_signers
,
7983 revoked_signers
, verbosity
);
7985 error
= get_gitconfig_path(&gitconfig_path
);
7988 error
= got_repo_open(&repo
, repo_path
, gitconfig_path
,
7993 error
= get_author(&tagger
, repo
, worktree
);
7996 if (signer_id
== NULL
)
7997 signer_id
= get_signer_id(repo
, worktree
);
7999 if (tagmsg
== NULL
) {
8000 error
= get_editor(&editor
);
8003 if (unveil(editor
, "x") != 0) {
8004 error
= got_error_from_errno2("unveil", editor
);
8009 error
= got_sigs_apply_unveil();
8013 error
= apply_unveil(got_repo_get_path(repo
), 0, NULL
);
8017 if (commit_id_arg
== NULL
) {
8018 struct got_reference
*head_ref
;
8019 struct got_object_id
*commit_id
;
8020 error
= got_ref_open(&head_ref
, repo
,
8021 worktree
? got_worktree_get_head_ref_name(worktree
)
8025 error
= got_ref_resolve(&commit_id
, repo
, head_ref
);
8026 got_ref_close(head_ref
);
8029 error
= got_object_id_str(&commit_id_str
, commit_id
);
8034 error
= got_keyword_to_idstr(&keyword_idstr
,
8035 commit_id_arg
, repo
, worktree
);
8038 commit_id_str
= keyword_idstr
;
8042 /* Release work tree lock. */
8043 got_worktree_close(worktree
);
8047 error
= add_tag(repo
, tagger
, tag_name
,
8048 commit_id_str
? commit_id_str
: commit_id_arg
, tagmsg
,
8049 signer_id
, editor
, verbosity
);
8053 const struct got_error
*close_err
= got_repo_close(repo
);
8058 got_worktree_close(worktree
);
8060 const struct got_error
*pack_err
=
8061 got_repo_pack_fds_close(pack_fds
);
8068 free(gitconfig_path
);
8069 free(commit_id_str
);
8071 free(allowed_signers
);
8072 free(revoked_signers
);
8079 fprintf(stderr
, "usage: %s add [-IR] path ...\n", getprogname());
8083 static const struct got_error
*
8084 add_progress(void *arg
, unsigned char status
, const char *path
)
8086 while (path
[0] == '/')
8088 printf("%c %s\n", status
, path
);
8092 static const struct got_error
*
8093 cmd_add(int argc
, char *argv
[])
8095 const struct got_error
*error
= NULL
;
8096 struct got_repository
*repo
= NULL
;
8097 struct got_worktree
*worktree
= NULL
;
8099 struct got_pathlist_head paths
;
8100 struct got_pathlist_entry
*pe
;
8101 int ch
, can_recurse
= 0, no_ignores
= 0;
8102 int *pack_fds
= NULL
;
8107 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
8112 while ((ch
= getopt(argc
, argv
, "IR")) != -1) {
8132 cwd
= getcwd(NULL
, 0);
8134 error
= got_error_from_errno("getcwd");
8138 error
= got_repo_pack_fds_open(&pack_fds
);
8142 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_GOT_DIR
);
8144 if (error
->code
== GOT_ERR_NOT_WORKTREE
)
8145 error
= wrap_not_worktree_error(error
, "add", cwd
);
8149 error
= got_repo_open(&repo
, got_worktree_get_repo_path(worktree
),
8154 error
= apply_unveil(got_repo_get_path(repo
), 1,
8155 got_worktree_get_root_path(worktree
));
8159 error
= get_worktree_paths_from_argv(&paths
, argc
, argv
, worktree
);
8166 TAILQ_FOREACH(pe
, &paths
, entry
) {
8167 if (asprintf(&ondisk_path
, "%s/%s",
8168 got_worktree_get_root_path(worktree
),
8170 error
= got_error_from_errno("asprintf");
8173 if (lstat(ondisk_path
, &sb
) == -1) {
8174 if (errno
== ENOENT
) {
8178 error
= got_error_from_errno2("lstat",
8184 if (S_ISDIR(sb
.st_mode
)) {
8185 error
= got_error_msg(GOT_ERR_BAD_PATH
,
8186 "adding directories requires -R option");
8192 error
= got_worktree_schedule_add(worktree
, &paths
, add_progress
,
8193 NULL
, repo
, no_ignores
);
8196 const struct got_error
*close_err
= got_repo_close(repo
);
8201 got_worktree_close(worktree
);
8203 const struct got_error
*pack_err
=
8204 got_repo_pack_fds_close(pack_fds
);
8208 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_PATH
);
8216 fprintf(stderr
, "usage: %s remove [-fkR] [-s status-codes] path ...\n",
8221 static const struct got_error
*
8222 print_remove_status(void *arg
, unsigned char status
,
8223 unsigned char staged_status
, const char *path
)
8225 while (path
[0] == '/')
8227 if (status
== GOT_STATUS_NONEXISTENT
)
8229 if (status
== staged_status
&& (status
== GOT_STATUS_DELETE
))
8230 status
= GOT_STATUS_NO_CHANGE
;
8231 printf("%c%c %s\n", status
, staged_status
, path
);
8235 static const struct got_error
*
8236 cmd_remove(int argc
, char *argv
[])
8238 const struct got_error
*error
= NULL
;
8239 struct got_worktree
*worktree
= NULL
;
8240 struct got_repository
*repo
= NULL
;
8241 const char *status_codes
= NULL
;
8243 struct got_pathlist_head paths
;
8244 struct got_pathlist_entry
*pe
;
8245 int ch
, delete_local_mods
= 0, can_recurse
= 0, keep_on_disk
= 0, i
;
8246 int ignore_missing_paths
= 0;
8247 int *pack_fds
= NULL
;
8252 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
8257 while ((ch
= getopt(argc
, argv
, "fkRs:")) != -1) {
8260 delete_local_mods
= 1;
8261 ignore_missing_paths
= 1;
8270 for (i
= 0; optarg
[i
] != '\0'; i
++) {
8271 switch (optarg
[i
]) {
8272 case GOT_STATUS_MODIFY
:
8273 delete_local_mods
= 1;
8275 case GOT_STATUS_MISSING
:
8276 ignore_missing_paths
= 1;
8279 errx(1, "invalid status code '%c'",
8283 status_codes
= optarg
;
8297 cwd
= getcwd(NULL
, 0);
8299 error
= got_error_from_errno("getcwd");
8303 error
= got_repo_pack_fds_open(&pack_fds
);
8307 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_GOT_DIR
);
8309 if (error
->code
== GOT_ERR_NOT_WORKTREE
)
8310 error
= wrap_not_worktree_error(error
, "remove", cwd
);
8314 error
= got_repo_open(&repo
, got_worktree_get_repo_path(worktree
),
8319 error
= apply_unveil(got_repo_get_path(repo
), 1,
8320 got_worktree_get_root_path(worktree
));
8324 error
= get_worktree_paths_from_argv(&paths
, argc
, argv
, worktree
);
8331 TAILQ_FOREACH(pe
, &paths
, entry
) {
8332 if (asprintf(&ondisk_path
, "%s/%s",
8333 got_worktree_get_root_path(worktree
),
8335 error
= got_error_from_errno("asprintf");
8338 if (lstat(ondisk_path
, &sb
) == -1) {
8339 if (errno
== ENOENT
) {
8343 error
= got_error_from_errno2("lstat",
8349 if (S_ISDIR(sb
.st_mode
)) {
8350 error
= got_error_msg(GOT_ERR_BAD_PATH
,
8351 "removing directories requires -R option");
8357 error
= got_worktree_schedule_delete(worktree
, &paths
,
8358 delete_local_mods
, status_codes
, print_remove_status
, NULL
,
8359 repo
, keep_on_disk
, ignore_missing_paths
);
8362 const struct got_error
*close_err
= got_repo_close(repo
);
8367 got_worktree_close(worktree
);
8369 const struct got_error
*pack_err
=
8370 got_repo_pack_fds_close(pack_fds
);
8374 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_PATH
);
8382 fprintf(stderr
, "usage: %s patch [-nR] [-c commit] [-p strip-count] "
8383 "[patchfile]\n", getprogname());
8387 static const struct got_error
*
8388 patch_from_stdin(int *patchfd
)
8390 const struct got_error
*err
= NULL
;
8393 sig_t sighup
, sigint
, sigquit
;
8395 *patchfd
= got_opentempfd();
8397 return got_error_from_errno("got_opentempfd");
8399 sighup
= signal(SIGHUP
, SIG_DFL
);
8400 sigint
= signal(SIGINT
, SIG_DFL
);
8401 sigquit
= signal(SIGQUIT
, SIG_DFL
);
8404 r
= read(0, buf
, sizeof(buf
));
8406 err
= got_error_from_errno("read");
8411 if (write(*patchfd
, buf
, r
) == -1) {
8412 err
= got_error_from_errno("write");
8417 signal(SIGHUP
, sighup
);
8418 signal(SIGINT
, sigint
);
8419 signal(SIGQUIT
, sigquit
);
8421 if (err
== NULL
&& lseek(*patchfd
, 0, SEEK_SET
) == -1)
8422 err
= got_error_from_errno("lseek");
8432 struct got_patch_progress_arg
{
8438 static const struct got_error
*
8439 patch_progress(void *arg
, const char *old
, const char *new,
8440 unsigned char status
, const struct got_error
*error
, int old_from
,
8441 int old_lines
, int new_from
, int new_lines
, int offset
,
8442 int ws_mangled
, const struct got_error
*hunk_err
)
8444 const char *path
= new == NULL
? old
: new;
8445 struct got_patch_progress_arg
*a
= arg
;
8447 while (*path
== '/')
8450 if (status
!= GOT_STATUS_NO_CHANGE
&&
8451 status
!= 0 /* per-hunk progress */) {
8452 printf("%c %s\n", status
, path
);
8453 a
->did_something
= 1;
8456 if (hunk_err
== NULL
) {
8457 if (status
== GOT_STATUS_CANNOT_UPDATE
)
8459 else if (status
== GOT_STATUS_CONFLICT
)
8464 fprintf(stderr
, "%s: %s\n", getprogname(), error
->msg
);
8466 if (offset
!= 0 || hunk_err
!= NULL
|| ws_mangled
) {
8467 printf("@@ -%d,%d +%d,%d @@ ", old_from
,
8468 old_lines
, new_from
, new_lines
);
8469 if (hunk_err
!= NULL
)
8470 printf("%s\n", hunk_err
->msg
);
8471 else if (offset
!= 0)
8472 printf("applied with offset %d\n", offset
);
8474 printf("hunk contains mangled whitespace\n");
8481 print_patch_progress_stats(struct got_patch_progress_arg
*ppa
)
8483 if (!ppa
->did_something
)
8486 if (ppa
->conflicts
> 0)
8487 printf("Files with merge conflicts: %d\n", ppa
->conflicts
);
8489 if (ppa
->rejects
> 0) {
8490 printf("Files where patch failed to apply: %d\n",
8495 static const struct got_error
*
8496 cmd_patch(int argc
, char *argv
[])
8498 const struct got_error
*error
= NULL
, *close_error
= NULL
;
8499 struct got_worktree
*worktree
= NULL
;
8500 struct got_repository
*repo
= NULL
;
8501 struct got_reflist_head refs
;
8502 struct got_object_id
*commit_id
= NULL
;
8503 const char *commit_id_str
= NULL
;
8506 char *cwd
= NULL
, *keyword_idstr
= NULL
;
8507 int ch
, nop
= 0, strip
= -1, reverse
= 0;
8509 int *pack_fds
= NULL
;
8510 struct got_patch_progress_arg ppa
;
8515 if (pledge("stdio rpath wpath cpath fattr proc exec sendfd flock "
8516 "unveil", NULL
) == -1)
8520 while ((ch
= getopt(argc
, argv
, "c:np:R")) != -1) {
8523 commit_id_str
= optarg
;
8529 strip
= strtonum(optarg
, 0, INT_MAX
, &errstr
);
8531 errx(1, "pathname strip count is %s: %s",
8547 error
= patch_from_stdin(&patchfd
);
8550 } else if (argc
== 1) {
8551 patchfd
= open(argv
[0], O_RDONLY
);
8552 if (patchfd
== -1) {
8553 error
= got_error_from_errno2("open", argv
[0]);
8556 if (fstat(patchfd
, &sb
) == -1) {
8557 error
= got_error_from_errno2("fstat", argv
[0]);
8560 if (!S_ISREG(sb
.st_mode
)) {
8561 error
= got_error_path(argv
[0], GOT_ERR_BAD_FILETYPE
);
8567 if ((cwd
= getcwd(NULL
, 0)) == NULL
) {
8568 error
= got_error_from_errno("getcwd");
8572 error
= got_repo_pack_fds_open(&pack_fds
);
8576 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_GOT_DIR
);
8580 const char *repo_path
= got_worktree_get_repo_path(worktree
);
8581 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
8585 error
= apply_unveil(got_repo_get_path(repo
), 0,
8586 got_worktree_get_root_path(worktree
));
8590 error
= got_ref_list(&refs
, repo
, NULL
, got_ref_cmp_by_name
, NULL
);
8594 if (commit_id_str
!= NULL
) {
8595 error
= got_keyword_to_idstr(&keyword_idstr
, commit_id_str
,
8600 error
= got_repo_match_object_id(&commit_id
, NULL
,
8601 keyword_idstr
!= NULL
? keyword_idstr
: commit_id_str
,
8602 GOT_OBJ_TYPE_COMMIT
, &refs
, repo
);
8607 memset(&ppa
, 0, sizeof(ppa
));
8608 error
= got_patch(patchfd
, worktree
, repo
, nop
, strip
, reverse
,
8609 commit_id
, patch_progress
, &ppa
, check_cancelled
, NULL
);
8610 print_patch_progress_stats(&ppa
);
8612 got_ref_list_free(&refs
);
8613 free(keyword_idstr
);
8616 close_error
= got_repo_close(repo
);
8618 error
= close_error
;
8620 if (worktree
!= NULL
) {
8621 close_error
= got_worktree_close(worktree
);
8623 error
= close_error
;
8626 const struct got_error
*pack_err
=
8627 got_repo_pack_fds_close(pack_fds
);
8638 fprintf(stderr
, "usage: %s revert [-pR] [-F response-script] path ...\n",
8643 static const struct got_error
*
8644 revert_progress(void *arg
, unsigned char status
, const char *path
)
8646 if (status
== GOT_STATUS_UNVERSIONED
)
8649 while (path
[0] == '/')
8651 printf("%c %s\n", status
, path
);
8655 struct choose_patch_arg
{
8656 FILE *patch_script_file
;
8660 static const struct got_error
*
8661 show_change(unsigned char status
, const char *path
, FILE *patch_file
, int n
,
8662 int nchanges
, const char *action
)
8664 const struct got_error
*err
;
8666 size_t linesize
= 0;
8670 case GOT_STATUS_ADD
:
8671 printf("A %s\n%s this addition? [y/n] ", path
, action
);
8673 case GOT_STATUS_DELETE
:
8674 printf("D %s\n%s this deletion? [y/n] ", path
, action
);
8676 case GOT_STATUS_MODIFY
:
8677 if (fseek(patch_file
, 0L, SEEK_SET
) == -1)
8678 return got_error_from_errno("fseek");
8679 printf(GOT_COMMIT_SEP_STR
);
8680 while ((linelen
= getline(&line
, &linesize
, patch_file
)) != -1)
8682 if (linelen
== -1 && ferror(patch_file
)) {
8683 err
= got_error_from_errno("getline");
8688 printf(GOT_COMMIT_SEP_STR
);
8689 printf("M %s (change %d of %d)\n%s this change? [y/n/q] ",
8690 path
, n
, nchanges
, action
);
8693 return got_error_path(path
, GOT_ERR_FILE_STATUS
);
8700 #define CHOOSE_PATCH_VALID_RESPONSES "ynq"
8702 static const struct got_error
*
8703 choose_patch(int *choice
, void *arg
, unsigned char status
, const char *path
,
8704 FILE *patch_file
, int n
, int nchanges
)
8706 const struct got_error
*err
= NULL
;
8707 char *nl
, *line
= NULL
;
8709 size_t linesize
= 0;
8711 int interactive
= 1;
8712 struct choose_patch_arg
*a
= arg
;
8714 *choice
= GOT_PATCH_CHOICE_NONE
;
8716 if (a
->patch_script_file
) {
8718 fp
= a
->patch_script_file
;
8722 err
= show_change(status
, path
, patch_file
, n
, nchanges
,
8727 linelen
= getline(&line
, &linesize
, fp
);
8728 if (linelen
== -1) {
8731 return got_error_from_errno("getline");
8733 return got_error(GOT_ERR_EOF
);
8737 nl
= strchr(line
, '\n');
8740 if (strlen(line
) != 1 ||
8741 !strchr(CHOOSE_PATCH_VALID_RESPONSES
, line
[0])) {
8742 printf("invalid response '%s'\n", line
);
8747 /* ADD and DELETE do not accept 'q' response currently. */
8748 if (status
!= GOT_STATUS_MODIFY
&& line
[0] == 'q') {
8749 printf("invalid response '%s'\n", line
);
8758 *choice
= GOT_PATCH_CHOICE_YES
;
8761 *choice
= GOT_PATCH_CHOICE_NO
;
8764 if (status
== GOT_STATUS_MODIFY
)
8765 *choice
= GOT_PATCH_CHOICE_QUIT
;
8768 printf("invalid response '%s'\n", line
);
8774 printf("%c\n", line
[0]);
8780 struct wt_commitable_path_arg
{
8781 struct got_pathlist_head
*commit_paths
;
8786 * Shortcut work tree status callback to determine if the set of paths scanned
8787 * has at least one versioned path that is being modified and, if not NULL, is
8788 * in the arg->commit_paths list. Set arg and return GOT_ERR_FILE_MODIFIED as
8789 * soon as a path is passed with a status that satisfies this criteria.
8791 static const struct got_error
*
8792 worktree_has_commitable_path(void *arg
, unsigned char status
,
8793 unsigned char staged_status
, const char *path
,
8794 struct got_object_id
*blob_id
, struct got_object_id
*staged_blob_id
,
8795 struct got_object_id
*commit_id
, int dirfd
, const char *de_name
)
8797 struct wt_commitable_path_arg
*a
= arg
;
8799 if (status
== staged_status
&& (status
== GOT_STATUS_DELETE
))
8800 status
= GOT_STATUS_NO_CHANGE
;
8802 if (!(status
== GOT_STATUS_NO_CHANGE
||
8803 status
== GOT_STATUS_UNVERSIONED
) ||
8804 staged_status
!= GOT_STATUS_NO_CHANGE
) {
8805 if (a
->commit_paths
!= NULL
) {
8806 struct got_pathlist_entry
*pe
;
8808 TAILQ_FOREACH(pe
, a
->commit_paths
, entry
) {
8809 if (strncmp(path
, pe
->path
,
8810 pe
->path_len
) == 0) {
8811 *a
->has_changes
= 1;
8816 *a
->has_changes
= 1;
8818 if (*a
->has_changes
)
8819 return got_error(GOT_ERR_FILE_MODIFIED
);
8826 * Check that the changeset of the commit identified by id is
8827 * comprised of at least one modified path that is being committed.
8829 static const struct got_error
*
8830 commit_path_changed_in_worktree(struct wt_commitable_path_arg
*wcpa
,
8831 struct got_object_id
*id
, struct got_worktree
*worktree
,
8832 struct got_repository
*repo
)
8834 const struct got_error
*err
;
8835 struct got_pathlist_head paths
;
8836 struct got_commit_object
*commit
= NULL
, *pcommit
= NULL
;
8837 struct got_tree_object
*tree
= NULL
, *ptree
= NULL
;
8838 struct got_object_qid
*pid
;
8842 err
= got_object_open_as_commit(&commit
, repo
, id
);
8846 err
= got_object_open_as_tree(&tree
, repo
,
8847 got_object_commit_get_tree_id(commit
));
8851 pid
= STAILQ_FIRST(got_object_commit_get_parent_ids(commit
));
8853 err
= got_object_open_as_commit(&pcommit
, repo
, &pid
->id
);
8857 err
= got_object_open_as_tree(&ptree
, repo
,
8858 got_object_commit_get_tree_id(pcommit
));
8863 err
= got_diff_tree(ptree
, tree
, NULL
, NULL
, -1, -1, "", "", repo
,
8864 got_diff_tree_collect_changed_paths
, &paths
, 0);
8868 err
= got_worktree_status(worktree
, &paths
, repo
, 0,
8869 worktree_has_commitable_path
, wcpa
, check_cancelled
, NULL
);
8870 if (err
&& err
->code
== GOT_ERR_FILE_MODIFIED
) {
8872 * At least one changed path in the referenced commit is
8873 * modified in the work tree, that's all we need to know!
8879 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_ALL
);
8881 got_object_commit_close(commit
);
8883 got_object_commit_close(pcommit
);
8885 got_object_tree_close(tree
);
8887 got_object_tree_close(ptree
);
8892 * Remove any "logmsg" reference comprised entirely of paths that have
8893 * been reverted in this work tree. If any path in the logmsg ref changeset
8894 * remains in a changed state in the worktree, do not remove the reference.
8896 static const struct got_error
*
8897 rm_logmsg_ref(struct got_worktree
*worktree
, struct got_repository
*repo
)
8899 const struct got_error
*err
;
8900 struct got_reflist_head refs
;
8901 struct got_reflist_entry
*re
;
8902 struct got_commit_object
*commit
= NULL
;
8903 struct got_object_id
*commit_id
= NULL
;
8904 struct wt_commitable_path_arg wcpa
;
8905 char *uuidstr
= NULL
;
8909 err
= got_worktree_get_uuid(&uuidstr
, worktree
);
8913 err
= got_ref_list(&refs
, repo
, "refs/got/worktree",
8914 got_ref_cmp_by_name
, repo
);
8918 TAILQ_FOREACH(re
, &refs
, entry
) {
8919 const char *refname
;
8920 int has_changes
= 0;
8922 refname
= got_ref_get_name(re
->ref
);
8924 if (!strncmp(refname
, GOT_WORKTREE_CHERRYPICK_REF_PREFIX
,
8925 GOT_WORKTREE_CHERRYPICK_REF_PREFIX_LEN
))
8926 refname
+= GOT_WORKTREE_CHERRYPICK_REF_PREFIX_LEN
+ 1;
8927 else if (!strncmp(refname
, GOT_WORKTREE_BACKOUT_REF_PREFIX
,
8928 GOT_WORKTREE_BACKOUT_REF_PREFIX_LEN
))
8929 refname
+= GOT_WORKTREE_BACKOUT_REF_PREFIX_LEN
+ 1;
8933 if (strncmp(refname
, uuidstr
, GOT_WORKTREE_UUID_STRLEN
) == 0)
8934 refname
+= GOT_WORKTREE_UUID_STRLEN
+ 1; /* skip '-' */
8938 err
= got_repo_match_object_id(&commit_id
, NULL
, refname
,
8939 GOT_OBJ_TYPE_COMMIT
, NULL
, repo
);
8943 err
= got_object_open_as_commit(&commit
, repo
, commit_id
);
8947 wcpa
.commit_paths
= NULL
;
8948 wcpa
.has_changes
= &has_changes
;
8950 err
= commit_path_changed_in_worktree(&wcpa
, commit_id
,
8956 err
= got_ref_delete(re
->ref
, repo
);
8961 got_object_commit_close(commit
);
8970 got_ref_list_free(&refs
);
8972 got_object_commit_close(commit
);
8976 static const struct got_error
*
8977 cmd_revert(int argc
, char *argv
[])
8979 const struct got_error
*error
= NULL
;
8980 struct got_worktree
*worktree
= NULL
;
8981 struct got_repository
*repo
= NULL
;
8982 char *cwd
= NULL
, *path
= NULL
;
8983 struct got_pathlist_head paths
;
8984 struct got_pathlist_entry
*pe
;
8985 int ch
, can_recurse
= 0, pflag
= 0;
8986 FILE *patch_script_file
= NULL
;
8987 const char *patch_script_path
= NULL
;
8988 struct choose_patch_arg cpa
;
8989 int *pack_fds
= NULL
;
8994 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
8995 "unveil", NULL
) == -1)
8999 while ((ch
= getopt(argc
, argv
, "F:pR")) != -1) {
9002 patch_script_path
= optarg
;
9021 if (patch_script_path
&& !pflag
)
9022 errx(1, "-F option can only be used together with -p option");
9024 cwd
= getcwd(NULL
, 0);
9026 error
= got_error_from_errno("getcwd");
9030 error
= got_repo_pack_fds_open(&pack_fds
);
9034 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_GOT_DIR
);
9036 if (error
->code
== GOT_ERR_NOT_WORKTREE
)
9037 error
= wrap_not_worktree_error(error
, "revert", cwd
);
9041 error
= got_repo_open(&repo
, got_worktree_get_repo_path(worktree
),
9046 if (patch_script_path
) {
9047 patch_script_file
= fopen(patch_script_path
, "re");
9048 if (patch_script_file
== NULL
) {
9049 error
= got_error_from_errno2("fopen",
9056 * XXX "c" perm needed on repo dir to delete merge references.
9058 error
= apply_unveil(got_repo_get_path(repo
), 0,
9059 got_worktree_get_root_path(worktree
));
9063 error
= get_worktree_paths_from_argv(&paths
, argc
, argv
, worktree
);
9070 TAILQ_FOREACH(pe
, &paths
, entry
) {
9071 if (asprintf(&ondisk_path
, "%s/%s",
9072 got_worktree_get_root_path(worktree
),
9074 error
= got_error_from_errno("asprintf");
9077 if (lstat(ondisk_path
, &sb
) == -1) {
9078 if (errno
== ENOENT
) {
9082 error
= got_error_from_errno2("lstat",
9088 if (S_ISDIR(sb
.st_mode
)) {
9089 error
= got_error_msg(GOT_ERR_BAD_PATH
,
9090 "reverting directories requires -R option");
9096 cpa
.patch_script_file
= patch_script_file
;
9097 cpa
.action
= "revert";
9098 error
= got_worktree_revert(worktree
, &paths
, revert_progress
, NULL
,
9099 pflag
? choose_patch
: NULL
, &cpa
, repo
);
9101 error
= rm_logmsg_ref(worktree
, repo
);
9103 if (patch_script_file
&& fclose(patch_script_file
) == EOF
&&
9105 error
= got_error_from_errno2("fclose", patch_script_path
);
9107 const struct got_error
*close_err
= got_repo_close(repo
);
9112 got_worktree_close(worktree
);
9114 const struct got_error
*pack_err
=
9115 got_repo_pack_fds_close(pack_fds
);
9119 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_PATH
);
9128 fprintf(stderr
, "usage: %s commit [-CNnS] [-A author] [-F path] "
9129 "[-m message] [path ...]\n", getprogname());
9133 struct collect_commit_logmsg_arg
{
9134 const char *cmdline_log
;
9135 const char *prepared_log
;
9136 const char *merged_log
;
9137 int non_interactive
;
9139 const char *worktree_path
;
9140 const char *branch_name
;
9141 const char *repo_path
;
9146 static const struct got_error
*
9147 read_prepared_logmsg(char **logmsg
, const char *path
)
9149 const struct got_error
*err
= NULL
;
9155 memset(&sb
, 0, sizeof(sb
));
9157 f
= fopen(path
, "re");
9159 return got_error_from_errno2("fopen", path
);
9161 if (fstat(fileno(f
), &sb
) == -1) {
9162 err
= got_error_from_errno2("fstat", path
);
9165 if (sb
.st_size
== 0) {
9166 err
= got_error(GOT_ERR_COMMIT_MSG_EMPTY
);
9170 *logmsg
= malloc(sb
.st_size
+ 1);
9171 if (*logmsg
== NULL
) {
9172 err
= got_error_from_errno("malloc");
9176 r
= fread(*logmsg
, 1, sb
.st_size
, f
);
9177 if (r
!= sb
.st_size
) {
9179 err
= got_error_from_errno2("fread", path
);
9181 err
= got_error(GOT_ERR_IO
);
9184 (*logmsg
)[sb
.st_size
] = '\0';
9186 if (fclose(f
) == EOF
&& err
== NULL
)
9187 err
= got_error_from_errno2("fclose", path
);
9195 static const struct got_error
*
9196 collect_commit_logmsg(struct got_pathlist_head
*commitable_paths
,
9197 const char *diff_path
, char **logmsg
, void *arg
)
9199 char *initial_content
= NULL
;
9200 struct got_pathlist_entry
*pe
;
9201 const struct got_error
*err
= NULL
;
9202 char *template = NULL
;
9203 char *prepared_msg
= NULL
, *merged_msg
= NULL
;
9204 struct collect_commit_logmsg_arg
*a
= arg
;
9205 int initial_content_len
;
9209 /* if a message was specified on the command line, just use it */
9210 if (a
->cmdline_log
!= NULL
&& *a
->cmdline_log
!= '\0') {
9211 len
= strlen(a
->cmdline_log
) + 1;
9212 *logmsg
= malloc(len
+ 1);
9213 if (*logmsg
== NULL
)
9214 return got_error_from_errno("malloc");
9215 strlcpy(*logmsg
, a
->cmdline_log
, len
);
9217 } else if (a
->prepared_log
!= NULL
&& a
->non_interactive
)
9218 return read_prepared_logmsg(logmsg
, a
->prepared_log
);
9220 if (asprintf(&template, "%s/logmsg", a
->worktree_path
) == -1)
9221 return got_error_from_errno("asprintf");
9223 err
= got_opentemp_named_fd(&a
->logmsg_path
, &fd
, template, "");
9227 if (a
->prepared_log
) {
9228 err
= read_prepared_logmsg(&prepared_msg
, a
->prepared_log
);
9231 } else if (a
->merged_log
) {
9232 err
= read_prepared_logmsg(&merged_msg
, a
->merged_log
);
9237 initial_content_len
= asprintf(&initial_content
,
9238 "%s%s\n# changes to be committed on branch %s:\n",
9239 prepared_msg
? prepared_msg
: "",
9240 merged_msg
? merged_msg
: "", a
->branch_name
);
9241 if (initial_content_len
== -1) {
9242 err
= got_error_from_errno("asprintf");
9246 if (write(fd
, initial_content
, initial_content_len
) == -1) {
9247 err
= got_error_from_errno2("write", a
->logmsg_path
);
9251 TAILQ_FOREACH(pe
, commitable_paths
, entry
) {
9252 struct got_commitable
*ct
= pe
->data
;
9253 dprintf(fd
, "# %c %s\n",
9254 got_commitable_get_status(ct
),
9255 got_commitable_get_path(ct
));
9259 dprintf(fd
, "# detailed changes can be viewed in %s\n",
9263 if (close(fd
) == -1) {
9264 err
= got_error_from_errno2("close", a
->logmsg_path
);
9269 err
= edit_logmsg(logmsg
, a
->editor
, a
->logmsg_path
, initial_content
,
9270 initial_content_len
, a
->prepared_log
? 0 : 1);
9272 free(initial_content
);
9277 if (fd
!= -1 && close(fd
) == -1 && err
== NULL
)
9278 err
= got_error_from_errno2("close", a
->logmsg_path
);
9286 static const struct got_error
*
9287 cat_logmsg(FILE *f
, struct got_commit_object
*commit
, const char *idstr
,
9288 const char *type
, int has_content
)
9290 const struct got_error
*err
= NULL
;
9291 char *logmsg
= NULL
;
9293 err
= got_object_commit_get_logmsg(&logmsg
, commit
);
9297 if (fprintf(f
, "%s# log message of %s commit %s:%s",
9298 has_content
? "\n" : "", type
, idstr
, logmsg
) < 0)
9299 err
= got_ferror(f
, GOT_ERR_IO
);
9306 * Lookup "logmsg" references of backed-out and cherrypicked commits
9307 * belonging to the current work tree. If found, and the worktree has
9308 * at least one modified file that was changed in the referenced commit,
9309 * add its log message to a new temporary file at *logmsg_path.
9310 * Add all refs found to matched_refs to be scheduled for removal on
9311 * successful commit.
9313 static const struct got_error
*
9314 lookup_logmsg_ref(char **logmsg_path
, struct got_pathlist_head
*paths
,
9315 struct got_reflist_head
*matched_refs
, struct got_worktree
*worktree
,
9316 struct got_repository
*repo
)
9318 const struct got_error
*err
;
9319 struct got_commit_object
*commit
= NULL
;
9320 struct got_object_id
*id
= NULL
;
9321 struct got_reflist_head refs
;
9322 struct got_reflist_entry
*re
, *re_match
;
9324 char *uuidstr
= NULL
;
9325 int added_logmsg
= 0;
9329 *logmsg_path
= NULL
;
9331 err
= got_worktree_get_uuid(&uuidstr
, worktree
);
9335 err
= got_ref_list(&refs
, repo
, "refs/got/worktree",
9336 got_ref_cmp_by_name
, repo
);
9340 TAILQ_FOREACH(re
, &refs
, entry
) {
9341 const char *refname
, *type
;
9342 struct wt_commitable_path_arg wcpa
;
9345 refname
= got_ref_get_name(re
->ref
);
9347 if (strncmp(refname
, GOT_WORKTREE_CHERRYPICK_REF_PREFIX
,
9348 GOT_WORKTREE_CHERRYPICK_REF_PREFIX_LEN
) == 0) {
9349 refname
+= GOT_WORKTREE_CHERRYPICK_REF_PREFIX_LEN
+ 1;
9350 type
= "cherrypicked";
9351 } else if (strncmp(refname
, GOT_WORKTREE_BACKOUT_REF_PREFIX
,
9352 GOT_WORKTREE_BACKOUT_REF_PREFIX_LEN
) == 0) {
9353 refname
+= GOT_WORKTREE_BACKOUT_REF_PREFIX_LEN
+ 1;
9354 type
= "backed-out";
9358 if (strncmp(refname
, uuidstr
, GOT_WORKTREE_UUID_STRLEN
) == 0)
9359 refname
+= GOT_WORKTREE_UUID_STRLEN
+ 1; /* skip '-' */
9363 err
= got_repo_match_object_id(&id
, NULL
, refname
,
9364 GOT_OBJ_TYPE_COMMIT
, NULL
, repo
);
9368 err
= got_object_open_as_commit(&commit
, repo
, id
);
9372 wcpa
.commit_paths
= paths
;
9373 wcpa
.has_changes
= &add_logmsg
;
9375 err
= commit_path_changed_in_worktree(&wcpa
, id
,
9382 err
= got_opentemp_named(logmsg_path
, &f
,
9383 "got-commit-logmsg", "");
9387 err
= cat_logmsg(f
, commit
, refname
, type
,
9394 err
= got_reflist_entry_dup(&re_match
, re
);
9397 TAILQ_INSERT_HEAD(matched_refs
, re_match
, entry
);
9400 got_object_commit_close(commit
);
9409 got_ref_list_free(&refs
);
9411 got_object_commit_close(commit
);
9412 if (f
&& fclose(f
) == EOF
&& err
== NULL
)
9413 err
= got_error_from_errno("fclose");
9414 if (!added_logmsg
) {
9415 if (*logmsg_path
&& unlink(*logmsg_path
) != 0 && err
== NULL
)
9416 err
= got_error_from_errno2("unlink", *logmsg_path
);
9417 *logmsg_path
= NULL
;
9422 static const struct got_error
*
9423 cmd_commit(int argc
, char *argv
[])
9425 const struct got_error
*error
= NULL
;
9426 struct got_worktree
*worktree
= NULL
;
9427 struct got_repository
*repo
= NULL
;
9428 char *cwd
= NULL
, *id_str
= NULL
;
9429 struct got_object_id
*id
= NULL
;
9430 const char *logmsg
= NULL
;
9431 char *prepared_logmsg
= NULL
, *merged_logmsg
= NULL
;
9432 struct collect_commit_logmsg_arg cl_arg
;
9433 const char *author
= NULL
;
9434 char *gitconfig_path
= NULL
, *editor
= NULL
, *committer
= NULL
;
9435 int ch
, rebase_in_progress
, histedit_in_progress
, preserve_logmsg
= 0;
9436 int allow_bad_symlinks
= 0, non_interactive
= 0, merge_in_progress
= 0;
9437 int show_diff
= 1, commit_conflicts
= 0;
9438 struct got_pathlist_head paths
;
9439 struct got_reflist_head refs
;
9440 struct got_reflist_entry
*re
;
9441 int *pack_fds
= NULL
;
9445 cl_arg
.logmsg_path
= NULL
;
9448 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
9449 "unveil", NULL
) == -1)
9453 while ((ch
= getopt(argc
, argv
, "A:CF:m:NnS")) != -1) {
9457 error
= valid_author(author
);
9462 commit_conflicts
= 1;
9466 option_conflict('F', 'm');
9467 prepared_logmsg
= realpath(optarg
, NULL
);
9468 if (prepared_logmsg
== NULL
)
9469 return got_error_from_errno2("realpath",
9473 if (prepared_logmsg
)
9474 option_conflict('m', 'F');
9478 non_interactive
= 1;
9484 allow_bad_symlinks
= 1;
9495 cwd
= getcwd(NULL
, 0);
9497 error
= got_error_from_errno("getcwd");
9501 error
= got_repo_pack_fds_open(&pack_fds
);
9505 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_GOT_DIR
);
9507 if (error
->code
== GOT_ERR_NOT_WORKTREE
)
9508 error
= wrap_not_worktree_error(error
, "commit", cwd
);
9512 error
= got_worktree_rebase_in_progress(&rebase_in_progress
, worktree
);
9515 if (rebase_in_progress
) {
9516 error
= got_error(GOT_ERR_REBASING
);
9520 error
= got_worktree_histedit_in_progress(&histedit_in_progress
,
9525 error
= get_gitconfig_path(&gitconfig_path
);
9528 error
= got_repo_open(&repo
, got_worktree_get_repo_path(worktree
),
9529 gitconfig_path
, pack_fds
);
9533 error
= got_worktree_merge_in_progress(&merge_in_progress
, worktree
, repo
);
9536 if (merge_in_progress
) {
9537 error
= got_error(GOT_ERR_MERGE_BUSY
);
9541 error
= get_author(&committer
, repo
, worktree
);
9548 if (logmsg
== NULL
|| strlen(logmsg
) == 0) {
9549 error
= get_editor(&editor
);
9552 if (unveil(editor
, "x") != 0) {
9553 error
= got_error_from_errno2("unveil", editor
);
9557 if (prepared_logmsg
) {
9558 if (unveil(prepared_logmsg
, "r") != 0) {
9559 error
= got_error_from_errno2("unveil",
9565 error
= apply_unveil(got_repo_get_path(repo
), 0,
9566 got_worktree_get_root_path(worktree
));
9570 error
= get_worktree_paths_from_argv(&paths
, argc
, argv
, worktree
);
9574 if (prepared_logmsg
== NULL
) {
9575 error
= lookup_logmsg_ref(&merged_logmsg
,
9576 argc
> 0 ? &paths
: NULL
, &refs
, worktree
, repo
);
9581 cl_arg
.editor
= editor
;
9582 cl_arg
.cmdline_log
= logmsg
;
9583 cl_arg
.prepared_log
= prepared_logmsg
;
9584 cl_arg
.merged_log
= merged_logmsg
;
9585 cl_arg
.non_interactive
= non_interactive
;
9586 cl_arg
.worktree_path
= got_worktree_get_root_path(worktree
);
9587 cl_arg
.branch_name
= got_worktree_get_head_ref_name(worktree
);
9588 if (!histedit_in_progress
) {
9589 if (strncmp(cl_arg
.branch_name
, "refs/heads/", 11) != 0) {
9590 error
= got_error(GOT_ERR_COMMIT_BRANCH
);
9593 cl_arg
.branch_name
+= 11;
9595 cl_arg
.repo_path
= got_repo_get_path(repo
);
9596 error
= got_worktree_commit(&id
, worktree
, &paths
, author
, committer
,
9597 allow_bad_symlinks
, show_diff
, commit_conflicts
,
9598 collect_commit_logmsg
, &cl_arg
, print_status
, NULL
, repo
);
9600 if (error
->code
!= GOT_ERR_COMMIT_MSG_EMPTY
&&
9601 cl_arg
.logmsg_path
!= NULL
)
9602 preserve_logmsg
= 1;
9606 error
= got_object_id_str(&id_str
, id
);
9609 printf("Created commit %s\n", id_str
);
9611 TAILQ_FOREACH(re
, &refs
, entry
) {
9612 error
= got_ref_delete(re
->ref
, repo
);
9618 if (preserve_logmsg
) {
9619 fprintf(stderr
, "%s: log message preserved in %s\n",
9620 getprogname(), cl_arg
.logmsg_path
);
9621 } else if (cl_arg
.logmsg_path
&& unlink(cl_arg
.logmsg_path
) == -1 &&
9623 error
= got_error_from_errno2("unlink", cl_arg
.logmsg_path
);
9624 free(cl_arg
.logmsg_path
);
9625 if (merged_logmsg
&& unlink(merged_logmsg
) == -1 && error
== NULL
)
9626 error
= got_error_from_errno2("unlink", merged_logmsg
);
9627 free(merged_logmsg
);
9629 const struct got_error
*close_err
= got_repo_close(repo
);
9634 got_worktree_close(worktree
);
9636 const struct got_error
*pack_err
=
9637 got_repo_pack_fds_close(pack_fds
);
9641 got_ref_list_free(&refs
);
9642 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_PATH
);
9645 free(gitconfig_path
);
9648 free(prepared_logmsg
);
9655 fprintf(stderr
, "usage: %s send [-afqTv] [-b branch] [-d branch] "
9656 "[-r repository-path] [-t tag] [remote-repository]\n",
9662 print_load_info(int print_colored
, int print_found
, int print_trees
,
9663 int ncolored
, int nfound
, int ntrees
)
9665 if (print_colored
) {
9666 printf("%d commit%s colored", ncolored
,
9667 ncolored
== 1 ? "" : "s");
9670 printf("%s%d object%s found",
9671 ncolored
> 0 ? "; " : "",
9672 nfound
, nfound
== 1 ? "" : "s");
9675 printf("; %d tree%s scanned", ntrees
,
9676 ntrees
== 1 ? "" : "s");
9680 struct got_send_progress_arg
{
9681 char last_scaled_packsize
[FMT_SCALED_STRSIZE
];
9688 int last_nobj_total
;
9692 int printed_something
;
9694 struct got_pathlist_head
*delete_branches
;
9697 static const struct got_error
*
9698 send_progress(void *arg
, int ncolored
, int nfound
, int ntrees
,
9699 off_t packfile_size
, int ncommits
, int nobj_total
, int nobj_deltify
,
9700 int nobj_written
, off_t bytes_sent
, const char *refname
,
9701 const char *errmsg
, int success
)
9703 struct got_send_progress_arg
*a
= arg
;
9704 char scaled_packsize
[FMT_SCALED_STRSIZE
];
9705 char scaled_sent
[FMT_SCALED_STRSIZE
];
9706 int p_deltify
= 0, p_written
= 0, p_sent
= 0;
9707 int print_colored
= 0, print_found
= 0, print_trees
= 0;
9708 int print_searching
= 0, print_total
= 0;
9709 int print_deltify
= 0, print_written
= 0, print_sent
= 0;
9711 if (a
->verbosity
< 0)
9715 const char *status
= success
? "accepted" : "rejected";
9718 struct got_pathlist_entry
*pe
;
9719 TAILQ_FOREACH(pe
, a
->delete_branches
, entry
) {
9720 const char *branchname
= pe
->path
;
9721 if (got_path_cmp(branchname
, refname
,
9722 strlen(branchname
), strlen(refname
)) == 0) {
9724 a
->sent_something
= 1;
9730 if (a
->printed_something
)
9732 printf("Server has %s %s", status
, refname
);
9734 printf(": %s", errmsg
);
9735 a
->printed_something
= 1;
9739 if (a
->last_ncolored
!= ncolored
) {
9741 a
->last_ncolored
= ncolored
;
9744 if (a
->last_nfound
!= nfound
) {
9747 a
->last_nfound
= nfound
;
9750 if (a
->last_ntrees
!= ntrees
) {
9754 a
->last_ntrees
= ntrees
;
9757 if ((print_colored
|| print_found
|| print_trees
) &&
9760 print_load_info(print_colored
, print_found
, print_trees
,
9761 ncolored
, nfound
, ntrees
);
9762 a
->printed_something
= 1;
9765 } else if (!a
->loading_done
) {
9767 print_load_info(1, 1, 1, ncolored
, nfound
, ntrees
);
9769 a
->loading_done
= 1;
9772 if (fmt_scaled(packfile_size
, scaled_packsize
) == -1)
9773 return got_error_from_errno("fmt_scaled");
9774 if (fmt_scaled(bytes_sent
, scaled_sent
) == -1)
9775 return got_error_from_errno("fmt_scaled");
9777 if (a
->last_ncommits
!= ncommits
) {
9778 print_searching
= 1;
9779 a
->last_ncommits
= ncommits
;
9782 if (a
->last_nobj_total
!= nobj_total
) {
9783 print_searching
= 1;
9785 a
->last_nobj_total
= nobj_total
;
9788 if (packfile_size
> 0 && (a
->last_scaled_packsize
[0] == '\0' ||
9789 strcmp(scaled_packsize
, a
->last_scaled_packsize
)) != 0) {
9790 if (strlcpy(a
->last_scaled_packsize
, scaled_packsize
,
9791 FMT_SCALED_STRSIZE
) >= FMT_SCALED_STRSIZE
)
9792 return got_error(GOT_ERR_NO_SPACE
);
9795 if (nobj_deltify
> 0 || nobj_written
> 0) {
9796 if (nobj_deltify
> 0) {
9797 p_deltify
= (nobj_deltify
* 100) / nobj_total
;
9798 if (p_deltify
!= a
->last_p_deltify
) {
9799 a
->last_p_deltify
= p_deltify
;
9800 print_searching
= 1;
9805 if (nobj_written
> 0) {
9806 p_written
= (nobj_written
* 100) / nobj_total
;
9807 if (p_written
!= a
->last_p_written
) {
9808 a
->last_p_written
= p_written
;
9809 print_searching
= 1;
9817 if (bytes_sent
> 0) {
9818 p_sent
= (bytes_sent
* 100) / packfile_size
;
9819 if (p_sent
!= a
->last_p_sent
) {
9820 a
->last_p_sent
= p_sent
;
9821 print_searching
= 1;
9827 a
->sent_something
= 1;
9830 if (print_searching
|| print_total
|| print_deltify
|| print_written
||
9833 if (print_searching
)
9834 printf("packing %d reference%s", ncommits
,
9835 ncommits
== 1 ? "" : "s");
9837 printf("; %d object%s", nobj_total
,
9838 nobj_total
== 1 ? "" : "s");
9840 printf("; deltify: %d%%", p_deltify
);
9842 printf("; uploading pack: %*s %d%%", FMT_SCALED_STRSIZE
- 2,
9843 scaled_packsize
, p_sent
);
9844 else if (print_written
)
9845 printf("; writing pack: %*s %d%%", FMT_SCALED_STRSIZE
- 2,
9846 scaled_packsize
, p_written
);
9847 if (print_searching
|| print_total
|| print_deltify
||
9848 print_written
|| print_sent
) {
9849 a
->printed_something
= 1;
9855 static const struct got_error
*
9856 cmd_send(int argc
, char *argv
[])
9858 const struct got_error
*error
= NULL
;
9859 char *cwd
= NULL
, *repo_path
= NULL
;
9860 const char *remote_name
;
9861 char *proto
= NULL
, *host
= NULL
, *port
= NULL
;
9862 char *repo_name
= NULL
, *server_path
= NULL
;
9863 const struct got_remote_repo
*remotes
;
9864 struct got_remote_repo
*remote
= NULL
;
9865 int nremotes
, nbranches
= 0, ndelete_branches
= 0;
9866 struct got_repository
*repo
= NULL
;
9867 struct got_worktree
*worktree
= NULL
;
9868 const struct got_gotconfig
*repo_conf
= NULL
, *worktree_conf
= NULL
;
9869 struct got_pathlist_head branches
;
9870 struct got_pathlist_head tags
;
9871 struct got_reflist_head all_branches
;
9872 struct got_reflist_head all_tags
;
9873 struct got_pathlist_head delete_args
;
9874 struct got_pathlist_head delete_branches
;
9875 struct got_reflist_entry
*re
;
9876 struct got_pathlist_entry
*pe
;
9877 int i
, ch
, sendfd
= -1, sendstatus
;
9879 struct got_send_progress_arg spa
;
9880 int verbosity
= 0, overwrite_refs
= 0;
9881 int send_all_branches
= 0, send_all_tags
= 0;
9882 struct got_reference
*ref
= NULL
;
9883 int *pack_fds
= NULL
;
9885 TAILQ_INIT(&branches
);
9887 TAILQ_INIT(&all_branches
);
9888 TAILQ_INIT(&all_tags
);
9889 TAILQ_INIT(&delete_args
);
9890 TAILQ_INIT(&delete_branches
);
9892 while ((ch
= getopt(argc
, argv
, "ab:d:fqr:Tt:v")) != -1) {
9895 send_all_branches
= 1;
9898 error
= got_pathlist_append(&branches
, optarg
, NULL
);
9904 error
= got_pathlist_append(&delete_args
, optarg
, NULL
);
9915 repo_path
= realpath(optarg
, NULL
);
9916 if (repo_path
== NULL
)
9917 return got_error_from_errno2("realpath",
9919 got_path_strip_trailing_slashes(repo_path
);
9925 error
= got_pathlist_append(&tags
, optarg
, NULL
);
9932 else if (verbosity
< 3)
9943 if (send_all_branches
&& !TAILQ_EMPTY(&branches
))
9944 option_conflict('a', 'b');
9945 if (send_all_tags
&& !TAILQ_EMPTY(&tags
))
9946 option_conflict('T', 't');
9950 remote_name
= GOT_SEND_DEFAULT_REMOTE_NAME
;
9952 remote_name
= argv
[0];
9956 cwd
= getcwd(NULL
, 0);
9958 error
= got_error_from_errno("getcwd");
9962 error
= got_repo_pack_fds_open(&pack_fds
);
9966 if (repo_path
== NULL
) {
9967 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_GOT_DIR
);
9968 if (error
&& error
->code
!= GOT_ERR_NOT_WORKTREE
)
9974 strdup(got_worktree_get_repo_path(worktree
));
9975 if (repo_path
== NULL
)
9976 error
= got_error_from_errno("strdup");
9980 repo_path
= strdup(cwd
);
9981 if (repo_path
== NULL
) {
9982 error
= got_error_from_errno("strdup");
9988 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
9993 worktree_conf
= got_worktree_get_gotconfig(worktree
);
9994 if (worktree_conf
) {
9995 got_gotconfig_get_remotes(&nremotes
, &remotes
,
9997 for (i
= 0; i
< nremotes
; i
++) {
9998 if (strcmp(remotes
[i
].name
, remote_name
) == 0) {
9999 error
= got_repo_remote_repo_dup(&remote
,
10008 if (remote
== NULL
) {
10009 repo_conf
= got_repo_get_gotconfig(repo
);
10011 got_gotconfig_get_remotes(&nremotes
, &remotes
,
10013 for (i
= 0; i
< nremotes
; i
++) {
10014 if (strcmp(remotes
[i
].name
, remote_name
) == 0) {
10015 error
= got_repo_remote_repo_dup(&remote
,
10024 if (remote
== NULL
) {
10025 got_repo_get_gitconfig_remotes(&nremotes
, &remotes
, repo
);
10026 for (i
= 0; i
< nremotes
; i
++) {
10027 if (strcmp(remotes
[i
].name
, remote_name
) == 0) {
10028 error
= got_repo_remote_repo_dup(&remote
,
10036 if (remote
== NULL
) {
10037 error
= got_error_path(remote_name
, GOT_ERR_NO_REMOTE
);
10041 error
= got_dial_parse_uri(&proto
, &host
, &port
, &server_path
,
10042 &repo_name
, remote
->send_url
);
10046 if (strcmp(proto
, "git") == 0) {
10048 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
10049 "sendfd dns inet unveil", NULL
) == -1)
10052 } else if (strcmp(proto
, "git+ssh") == 0 ||
10053 strcmp(proto
, "ssh") == 0) {
10055 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
10056 "sendfd unveil", NULL
) == -1)
10059 } else if (strcmp(proto
, "http") == 0 ||
10060 strcmp(proto
, "git+http") == 0) {
10061 error
= got_error_path(proto
, GOT_ERR_NOT_IMPL
);
10064 error
= got_error_path(proto
, GOT_ERR_BAD_PROTO
);
10068 error
= got_dial_apply_unveil(proto
);
10072 error
= apply_unveil(got_repo_get_path(repo
), 0, NULL
);
10076 if (send_all_branches
) {
10077 error
= got_ref_list(&all_branches
, repo
, "refs/heads",
10078 got_ref_cmp_by_name
, NULL
);
10081 TAILQ_FOREACH(re
, &all_branches
, entry
) {
10082 const char *branchname
= got_ref_get_name(re
->ref
);
10083 error
= got_pathlist_append(&branches
,
10089 } else if (nbranches
== 0) {
10090 for (i
= 0; i
< remote
->nsend_branches
; i
++) {
10091 error
= got_pathlist_append(&branches
,
10092 remote
->send_branches
[i
], NULL
);
10098 if (send_all_tags
) {
10099 error
= got_ref_list(&all_tags
, repo
, "refs/tags",
10100 got_ref_cmp_by_name
, NULL
);
10103 TAILQ_FOREACH(re
, &all_tags
, entry
) {
10104 const char *tagname
= got_ref_get_name(re
->ref
);
10105 error
= got_pathlist_append(&tags
,
10113 * To prevent accidents only branches in refs/heads/ can be deleted
10114 * with 'got send -d'.
10115 * Deleting anything else requires local repository access or Git.
10117 TAILQ_FOREACH(pe
, &delete_args
, entry
) {
10118 const char *branchname
= pe
->path
;
10120 struct got_pathlist_entry
*new;
10121 if (strncmp(branchname
, "refs/heads/", 11) == 0) {
10122 s
= strdup(branchname
);
10124 error
= got_error_from_errno("strdup");
10128 if (asprintf(&s
, "refs/heads/%s", branchname
) == -1) {
10129 error
= got_error_from_errno("asprintf");
10133 error
= got_pathlist_insert(&new, &delete_branches
, s
, NULL
);
10134 if (error
|| new == NULL
/* duplicate */)
10138 ndelete_branches
++;
10141 if (nbranches
== 0 && ndelete_branches
== 0) {
10142 struct got_reference
*head_ref
;
10144 error
= got_ref_open(&head_ref
, repo
,
10145 got_worktree_get_head_ref_name(worktree
), 0);
10147 error
= got_ref_open(&head_ref
, repo
, GOT_REF_HEAD
, 0);
10150 if (got_ref_is_symbolic(head_ref
)) {
10151 error
= got_ref_resolve_symbolic(&ref
, repo
, head_ref
);
10152 got_ref_close(head_ref
);
10157 error
= got_pathlist_append(&branches
, got_ref_get_name(ref
),
10165 /* Release work tree lock. */
10166 got_worktree_close(worktree
);
10170 if (verbosity
>= 0) {
10171 printf("Connecting to \"%s\" %s://%s%s%s%s%s\n",
10172 remote
->name
, proto
, host
,
10173 port
? ":" : "", port
? port
: "",
10174 *server_path
== '/' ? "" : "/", server_path
);
10177 error
= got_send_connect(&sendpid
, &sendfd
, proto
, host
, port
,
10178 server_path
, verbosity
);
10182 memset(&spa
, 0, sizeof(spa
));
10183 spa
.last_scaled_packsize
[0] = '\0';
10184 spa
.last_p_deltify
= -1;
10185 spa
.last_p_written
= -1;
10186 spa
.verbosity
= verbosity
;
10187 spa
.delete_branches
= &delete_branches
;
10188 error
= got_send_pack(remote_name
, &branches
, &tags
, &delete_branches
,
10189 verbosity
, overwrite_refs
, sendfd
, repo
, send_progress
, &spa
,
10190 check_cancelled
, NULL
);
10191 if (spa
.printed_something
)
10195 if (!spa
.sent_something
&& verbosity
>= 0)
10196 printf("Already up-to-date\n");
10199 if (kill(sendpid
, SIGTERM
) == -1)
10200 error
= got_error_from_errno("kill");
10201 if (waitpid(sendpid
, &sendstatus
, 0) == -1 && error
== NULL
)
10202 error
= got_error_from_errno("waitpid");
10204 if (sendfd
!= -1 && close(sendfd
) == -1 && error
== NULL
)
10205 error
= got_error_from_errno("close");
10207 const struct got_error
*close_err
= got_repo_close(repo
);
10212 got_worktree_close(worktree
);
10214 const struct got_error
*pack_err
=
10215 got_repo_pack_fds_close(pack_fds
);
10220 got_ref_close(ref
);
10221 got_repo_free_remote_repo_data(remote
);
10223 got_pathlist_free(&branches
, GOT_PATHLIST_FREE_NONE
);
10224 got_pathlist_free(&tags
, GOT_PATHLIST_FREE_NONE
);
10225 got_ref_list_free(&all_branches
);
10226 got_ref_list_free(&all_tags
);
10227 got_pathlist_free(&delete_args
, GOT_PATHLIST_FREE_NONE
);
10228 got_pathlist_free(&delete_branches
, GOT_PATHLIST_FREE_PATH
);
10240 * Print and if delete is set delete all ref_prefix references.
10241 * If wanted_ref is not NULL, only print or delete this reference.
10243 static const struct got_error
*
10244 process_logmsg_refs(const char *ref_prefix
, size_t prefix_len
,
10245 const char *wanted_ref
, int delete, struct got_worktree
*worktree
,
10246 struct got_repository
*repo
)
10248 const struct got_error
*err
;
10249 struct got_pathlist_head paths
;
10250 struct got_reflist_head refs
;
10251 struct got_reflist_entry
*re
;
10252 struct got_reflist_object_id_map
*refs_idmap
= NULL
;
10253 struct got_commit_object
*commit
= NULL
;
10254 struct got_object_id
*id
= NULL
;
10255 const char *header_prefix
;
10256 char *uuidstr
= NULL
;
10260 TAILQ_INIT(&paths
);
10262 err
= got_ref_list(&refs
, repo
, NULL
, got_ref_cmp_by_name
, repo
);
10266 err
= got_reflist_object_id_map_create(&refs_idmap
, &refs
, repo
);
10270 if (worktree
!= NULL
) {
10271 err
= got_worktree_get_uuid(&uuidstr
, worktree
);
10277 if (strncmp(wanted_ref
, "refs/heads/", 11) == 0)
10281 if (strcmp(ref_prefix
, GOT_WORKTREE_BACKOUT_REF_PREFIX
) == 0)
10282 header_prefix
= "backout";
10284 header_prefix
= "cherrypick";
10286 TAILQ_FOREACH(re
, &refs
, entry
) {
10287 const char *refname
, *wt
;
10289 refname
= got_ref_get_name(re
->ref
);
10291 err
= check_cancelled(NULL
);
10295 if (strncmp(refname
, ref_prefix
, prefix_len
) == 0)
10296 refname
+= prefix_len
+ 1; /* skip '-' delimiter */
10302 if (worktree
== NULL
|| strncmp(refname
, uuidstr
,
10303 GOT_WORKTREE_UUID_STRLEN
) == 0)
10304 refname
+= GOT_WORKTREE_UUID_STRLEN
+ 1; /* skip '-' */
10308 err
= got_repo_match_object_id(&id
, NULL
, refname
,
10309 GOT_OBJ_TYPE_COMMIT
, NULL
, repo
);
10313 err
= got_object_open_as_commit(&commit
, repo
, id
);
10318 found
= strncmp(wanted_ref
, refname
,
10319 strlen(wanted_ref
)) == 0;
10320 if (wanted_ref
&& !found
) {
10321 struct got_reflist_head
*ci_refs
;
10323 ci_refs
= got_reflist_object_id_map_lookup(refs_idmap
,
10327 char *refs_str
= NULL
;
10328 char const *r
= NULL
;
10330 err
= build_refs_str(&refs_str
, ci_refs
, id
,
10337 if (strncmp(r
, wanted_ref
,
10338 strlen(wanted_ref
)) == 0) {
10342 r
= strchr(r
, ' ');
10350 if (wanted_ref
== NULL
|| found
) {
10352 err
= got_ref_delete(re
->ref
, repo
);
10355 printf("Deleted: ");
10356 err
= print_commit_oneline(commit
, id
, repo
,
10360 * Print paths modified by commit to help
10361 * associate commits with worktree changes.
10363 err
= get_changed_paths(&paths
, commit
,
10368 err
= print_commit(commit
, id
, repo
, NULL
,
10369 &paths
, NULL
, 0, 0, refs_idmap
, NULL
,
10371 got_pathlist_free(&paths
,
10372 GOT_PATHLIST_FREE_ALL
);
10374 if (worktree
== NULL
)
10375 printf("work tree: %.*s\n\n",
10376 GOT_WORKTREE_UUID_STRLEN
, wt
);
10382 got_object_commit_close(commit
);
10388 if (wanted_ref
!= NULL
&& !found
)
10389 err
= got_error_fmt(GOT_ERR_NOT_REF
, "%s", wanted_ref
);
10394 got_ref_list_free(&refs
);
10395 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_ALL
);
10397 got_reflist_object_id_map_free(refs_idmap
);
10399 got_object_commit_close(commit
);
10404 * Create new temp "logmsg" ref of the backed-out or cherrypicked commit
10405 * identified by id for log messages to prepopulate the editor on commit.
10407 static const struct got_error
*
10408 logmsg_ref(struct got_object_id
*id
, const char *prefix
,
10409 struct got_worktree
*worktree
, struct got_repository
*repo
)
10411 const struct got_error
*err
= NULL
;
10412 char *idstr
, *ref
= NULL
, *refname
= NULL
;
10413 int histedit_in_progress
;
10414 int rebase_in_progress
, merge_in_progress
;
10417 * Silenty refuse to create merge reference if any histedit, merge,
10418 * or rebase operation is in progress.
10420 err
= got_worktree_histedit_in_progress(&histedit_in_progress
,
10424 if (histedit_in_progress
)
10427 err
= got_worktree_rebase_in_progress(&rebase_in_progress
, worktree
);
10430 if (rebase_in_progress
)
10433 err
= got_worktree_merge_in_progress(&merge_in_progress
, worktree
,
10437 if (merge_in_progress
)
10440 err
= got_object_id_str(&idstr
, id
);
10444 err
= got_worktree_get_logmsg_ref_name(&refname
, worktree
, prefix
);
10448 if (asprintf(&ref
, "%s-%s", refname
, idstr
) == -1) {
10449 err
= got_error_from_errno("asprintf");
10453 err
= create_ref(ref
, got_worktree_get_base_commit_id(worktree
),
10463 usage_cherrypick(void)
10465 fprintf(stderr
, "usage: %s cherrypick [-lX] [commit-id]\n",
10470 static const struct got_error
*
10471 cmd_cherrypick(int argc
, char *argv
[])
10473 const struct got_error
*error
= NULL
;
10474 struct got_worktree
*worktree
= NULL
;
10475 struct got_repository
*repo
= NULL
;
10476 char *cwd
= NULL
, *commit_id_str
= NULL
, *keyword_idstr
= NULL
;
10477 struct got_object_id
*commit_id
= NULL
;
10478 struct got_commit_object
*commit
= NULL
;
10479 struct got_object_qid
*pid
;
10480 int ch
, list_refs
= 0, remove_refs
= 0;
10481 struct got_update_progress_arg upa
;
10482 int *pack_fds
= NULL
;
10485 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
10486 "unveil", NULL
) == -1)
10490 while ((ch
= getopt(argc
, argv
, "lX")) != -1) {
10499 usage_cherrypick();
10507 if (list_refs
|| remove_refs
) {
10508 if (argc
!= 0 && argc
!= 1)
10509 usage_cherrypick();
10510 } else if (argc
!= 1)
10511 usage_cherrypick();
10512 if (list_refs
&& remove_refs
)
10513 option_conflict('l', 'X');
10515 cwd
= getcwd(NULL
, 0);
10517 error
= got_error_from_errno("getcwd");
10521 error
= got_repo_pack_fds_open(&pack_fds
);
10525 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_GOT_DIR
);
10527 if (list_refs
|| remove_refs
) {
10528 if (error
->code
!= GOT_ERR_NOT_WORKTREE
)
10531 if (error
->code
== GOT_ERR_NOT_WORKTREE
)
10532 error
= wrap_not_worktree_error(error
,
10533 "cherrypick", cwd
);
10538 error
= got_repo_open(&repo
,
10539 worktree
? got_worktree_get_repo_path(worktree
) : cwd
,
10544 error
= apply_unveil(got_repo_get_path(repo
), 0,
10545 worktree
? got_worktree_get_root_path(worktree
) : NULL
);
10549 if (list_refs
|| remove_refs
) {
10550 error
= process_logmsg_refs(GOT_WORKTREE_CHERRYPICK_REF_PREFIX
,
10551 GOT_WORKTREE_CHERRYPICK_REF_PREFIX_LEN
,
10552 argc
== 1 ? argv
[0] : NULL
, remove_refs
, worktree
, repo
);
10556 error
= got_keyword_to_idstr(&keyword_idstr
, argv
[0], repo
, worktree
);
10560 error
= got_repo_match_object_id(&commit_id
, NULL
,
10561 keyword_idstr
!= NULL
? keyword_idstr
: argv
[0],
10562 GOT_OBJ_TYPE_COMMIT
, NULL
, repo
);
10565 error
= got_object_id_str(&commit_id_str
, commit_id
);
10569 error
= got_object_open_as_commit(&commit
, repo
, commit_id
);
10572 pid
= STAILQ_FIRST(got_object_commit_get_parent_ids(commit
));
10573 memset(&upa
, 0, sizeof(upa
));
10574 error
= got_worktree_merge_files(worktree
, pid
? &pid
->id
: NULL
,
10575 commit_id
, repo
, update_progress
, &upa
, check_cancelled
,
10580 if (upa
.did_something
) {
10581 error
= logmsg_ref(commit_id
,
10582 GOT_WORKTREE_CHERRYPICK_REF_PREFIX
, worktree
, repo
);
10585 printf("Merged commit %s\n", commit_id_str
);
10587 print_merge_progress_stats(&upa
);
10590 free(keyword_idstr
);
10592 got_object_commit_close(commit
);
10593 free(commit_id_str
);
10595 got_worktree_close(worktree
);
10597 const struct got_error
*close_err
= got_repo_close(repo
);
10602 const struct got_error
*pack_err
=
10603 got_repo_pack_fds_close(pack_fds
);
10612 usage_backout(void)
10614 fprintf(stderr
, "usage: %s backout [-lX] [commit-id]\n", getprogname());
10618 static const struct got_error
*
10619 cmd_backout(int argc
, char *argv
[])
10621 const struct got_error
*error
= NULL
;
10622 struct got_worktree
*worktree
= NULL
;
10623 struct got_repository
*repo
= NULL
;
10624 char *cwd
= NULL
, *commit_id_str
= NULL
, *keyword_idstr
= NULL
;
10625 struct got_object_id
*commit_id
= NULL
;
10626 struct got_commit_object
*commit
= NULL
;
10627 struct got_object_qid
*pid
;
10628 int ch
, list_refs
= 0, remove_refs
= 0;
10629 struct got_update_progress_arg upa
;
10630 int *pack_fds
= NULL
;
10633 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
10634 "unveil", NULL
) == -1)
10638 while ((ch
= getopt(argc
, argv
, "lX")) != -1) {
10655 if (list_refs
|| remove_refs
) {
10656 if (argc
!= 0 && argc
!= 1)
10658 } else if (argc
!= 1)
10660 if (list_refs
&& remove_refs
)
10661 option_conflict('l', 'X');
10663 cwd
= getcwd(NULL
, 0);
10665 error
= got_error_from_errno("getcwd");
10669 error
= got_repo_pack_fds_open(&pack_fds
);
10673 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_GOT_DIR
);
10675 if (list_refs
|| remove_refs
) {
10676 if (error
->code
!= GOT_ERR_NOT_WORKTREE
)
10679 if (error
->code
== GOT_ERR_NOT_WORKTREE
)
10680 error
= wrap_not_worktree_error(error
,
10686 error
= got_repo_open(&repo
,
10687 worktree
? got_worktree_get_repo_path(worktree
) : cwd
,
10692 error
= apply_unveil(got_repo_get_path(repo
), 0,
10693 worktree
? got_worktree_get_root_path(worktree
) : NULL
);
10697 if (list_refs
|| remove_refs
) {
10698 error
= process_logmsg_refs(GOT_WORKTREE_BACKOUT_REF_PREFIX
,
10699 GOT_WORKTREE_BACKOUT_REF_PREFIX_LEN
,
10700 argc
== 1 ? argv
[0] : NULL
, remove_refs
, worktree
, repo
);
10704 error
= got_keyword_to_idstr(&keyword_idstr
, argv
[0], repo
, worktree
);
10708 error
= got_repo_match_object_id(&commit_id
, NULL
,
10709 keyword_idstr
!= NULL
? keyword_idstr
: argv
[0],
10710 GOT_OBJ_TYPE_COMMIT
, NULL
, repo
);
10713 error
= got_object_id_str(&commit_id_str
, commit_id
);
10717 error
= got_object_open_as_commit(&commit
, repo
, commit_id
);
10720 pid
= STAILQ_FIRST(got_object_commit_get_parent_ids(commit
));
10722 error
= got_error(GOT_ERR_ROOT_COMMIT
);
10726 memset(&upa
, 0, sizeof(upa
));
10727 error
= got_worktree_merge_files(worktree
, commit_id
, &pid
->id
,
10728 repo
, update_progress
, &upa
, check_cancelled
, NULL
);
10732 if (upa
.did_something
) {
10733 error
= logmsg_ref(commit_id
, GOT_WORKTREE_BACKOUT_REF_PREFIX
,
10737 printf("Backed out commit %s\n", commit_id_str
);
10739 print_merge_progress_stats(&upa
);
10742 free(keyword_idstr
);
10744 got_object_commit_close(commit
);
10745 free(commit_id_str
);
10747 got_worktree_close(worktree
);
10749 const struct got_error
*close_err
= got_repo_close(repo
);
10754 const struct got_error
*pack_err
=
10755 got_repo_pack_fds_close(pack_fds
);
10765 fprintf(stderr
, "usage: %s rebase [-aCclX] [branch]\n", getprogname());
10770 trim_logmsg(char *logmsg
, int limit
)
10775 len
= strlen(logmsg
);
10778 logmsg
[len
] = '\0';
10779 nl
= strchr(logmsg
, '\n');
10784 static const struct got_error
*
10785 get_short_logmsg(char **logmsg
, int limit
, struct got_commit_object
*commit
)
10787 const struct got_error
*err
;
10788 char *logmsg0
= NULL
;
10791 err
= got_object_commit_get_logmsg(&logmsg0
, commit
);
10796 while (isspace((unsigned char)s
[0]))
10799 *logmsg
= strdup(s
);
10800 if (*logmsg
== NULL
) {
10801 err
= got_error_from_errno("strdup");
10805 trim_logmsg(*logmsg
, limit
);
10811 static const struct got_error
*
10812 show_rebase_merge_conflict(struct got_object_id
*id
,
10813 struct got_repository
*repo
)
10815 const struct got_error
*err
;
10816 struct got_commit_object
*commit
= NULL
;
10817 char *id_str
= NULL
, *logmsg
= NULL
;
10819 err
= got_object_open_as_commit(&commit
, repo
, id
);
10823 err
= got_object_id_str(&id_str
, id
);
10829 err
= get_short_logmsg(&logmsg
, 42, commit
);
10833 printf("%s -> merge conflict: %s\n", id_str
, logmsg
);
10836 got_object_commit_close(commit
);
10841 static const struct got_error
*
10842 show_rebase_progress(struct got_commit_object
*commit
,
10843 struct got_object_id
*old_id
, struct got_object_id
*new_id
)
10845 const struct got_error
*err
;
10846 char *old_id_str
= NULL
, *new_id_str
= NULL
, *logmsg
= NULL
;
10848 err
= got_object_id_str(&old_id_str
, old_id
);
10853 err
= got_object_id_str(&new_id_str
, new_id
);
10858 old_id_str
[12] = '\0';
10860 new_id_str
[12] = '\0';
10862 err
= get_short_logmsg(&logmsg
, 42, commit
);
10866 printf("%s -> %s: %s\n", old_id_str
,
10867 new_id_str
? new_id_str
: "no-op change", logmsg
);
10875 static const struct got_error
*
10876 rebase_complete(struct got_worktree
*worktree
, struct got_fileindex
*fileindex
,
10877 struct got_reference
*branch
, struct got_reference
*tmp_branch
,
10878 struct got_repository
*repo
, int create_backup
)
10880 printf("Switching work tree to %s\n", got_ref_get_name(branch
));
10881 return got_worktree_rebase_complete(worktree
, fileindex
,
10882 tmp_branch
, branch
, repo
, create_backup
);
10885 static const struct got_error
*
10886 rebase_commit(struct got_pathlist_head
*merged_paths
,
10887 struct got_worktree
*worktree
, struct got_fileindex
*fileindex
,
10888 struct got_reference
*tmp_branch
, const char *committer
,
10889 struct got_object_id
*commit_id
, int allow_conflict
,
10890 struct got_repository
*repo
)
10892 const struct got_error
*error
;
10893 struct got_commit_object
*commit
;
10894 struct got_object_id
*new_commit_id
;
10896 error
= got_object_open_as_commit(&commit
, repo
, commit_id
);
10900 error
= got_worktree_rebase_commit(&new_commit_id
, merged_paths
,
10901 worktree
, fileindex
, tmp_branch
, committer
, commit
, commit_id
,
10902 allow_conflict
, repo
);
10904 if (error
->code
!= GOT_ERR_COMMIT_NO_CHANGES
)
10906 error
= show_rebase_progress(commit
, commit_id
, NULL
);
10908 error
= show_rebase_progress(commit
, commit_id
, new_commit_id
);
10909 free(new_commit_id
);
10912 got_object_commit_close(commit
);
10916 struct check_path_prefix_arg
{
10917 const char *path_prefix
;
10922 static const struct got_error
*
10923 check_path_prefix_in_diff(void *arg
, struct got_blob_object
*blob1
,
10924 struct got_blob_object
*blob2
, FILE *f1
, FILE *f2
,
10925 struct got_object_id
*id1
, struct got_object_id
*id2
,
10926 const char *path1
, const char *path2
,
10927 mode_t mode1
, mode_t mode2
, struct got_repository
*repo
)
10929 struct check_path_prefix_arg
*a
= arg
;
10931 if ((path1
&& !got_path_is_child(path1
, a
->path_prefix
, a
->len
)) ||
10932 (path2
&& !got_path_is_child(path2
, a
->path_prefix
, a
->len
)))
10933 return got_error(a
->errcode
);
10938 static const struct got_error
*
10939 check_path_prefix(struct got_object_id
*parent_id
,
10940 struct got_object_id
*commit_id
, const char *path_prefix
,
10941 int errcode
, struct got_repository
*repo
)
10943 const struct got_error
*err
;
10944 struct got_tree_object
*tree1
= NULL
, *tree2
= NULL
;
10945 struct got_commit_object
*commit
= NULL
, *parent_commit
= NULL
;
10946 struct check_path_prefix_arg cpp_arg
;
10948 if (got_path_is_root_dir(path_prefix
))
10951 err
= got_object_open_as_commit(&commit
, repo
, commit_id
);
10955 err
= got_object_open_as_commit(&parent_commit
, repo
, parent_id
);
10959 err
= got_object_open_as_tree(&tree1
, repo
,
10960 got_object_commit_get_tree_id(parent_commit
));
10964 err
= got_object_open_as_tree(&tree2
, repo
,
10965 got_object_commit_get_tree_id(commit
));
10969 cpp_arg
.path_prefix
= path_prefix
;
10970 while (cpp_arg
.path_prefix
[0] == '/')
10971 cpp_arg
.path_prefix
++;
10972 cpp_arg
.len
= strlen(cpp_arg
.path_prefix
);
10973 cpp_arg
.errcode
= errcode
;
10974 err
= got_diff_tree(tree1
, tree2
, NULL
, NULL
, -1, -1, "", "", repo
,
10975 check_path_prefix_in_diff
, &cpp_arg
, 0);
10978 got_object_tree_close(tree1
);
10980 got_object_tree_close(tree2
);
10982 got_object_commit_close(commit
);
10984 got_object_commit_close(parent_commit
);
10988 static const struct got_error
*
10989 collect_commits(struct got_object_id_queue
*commits
,
10990 struct got_object_id
*initial_commit_id
,
10991 struct got_object_id
*iter_start_id
, struct got_object_id
*iter_stop_id
,
10992 const char *path_prefix
, int path_prefix_errcode
,
10993 struct got_repository
*repo
)
10995 const struct got_error
*err
= NULL
;
10996 struct got_commit_graph
*graph
= NULL
;
10997 struct got_object_id parent_id
, commit_id
;
10998 struct got_object_qid
*qid
;
11000 err
= got_commit_graph_open(&graph
, "/", 1);
11004 err
= got_commit_graph_bfsort(graph
, iter_start_id
, repo
,
11005 check_cancelled
, NULL
);
11009 memcpy(&commit_id
, initial_commit_id
, sizeof(commit_id
));
11010 while (got_object_id_cmp(&commit_id
, iter_stop_id
) != 0) {
11011 err
= got_commit_graph_iter_next(&parent_id
, graph
, repo
,
11012 check_cancelled
, NULL
);
11014 if (err
->code
== GOT_ERR_ITER_COMPLETED
) {
11015 err
= got_error_msg(GOT_ERR_ANCESTRY
,
11016 "ran out of commits to rebase before "
11017 "youngest common ancestor commit has "
11018 "been reached?!?");
11022 err
= check_path_prefix(&parent_id
, &commit_id
,
11023 path_prefix
, path_prefix_errcode
, repo
);
11027 err
= got_object_qid_alloc(&qid
, &commit_id
);
11030 STAILQ_INSERT_HEAD(commits
, qid
, entry
);
11032 memcpy(&commit_id
, &parent_id
, sizeof(commit_id
));
11036 got_commit_graph_close(graph
);
11040 static const struct got_error
*
11041 get_commit_brief_str(char **brief_str
, struct got_commit_object
*commit
)
11043 const struct got_error
*err
= NULL
;
11044 time_t committer_time
;
11046 char datebuf
[11]; /* YYYY-MM-DD + NUL */
11047 char *author0
= NULL
, *author
, *smallerthan
;
11048 char *logmsg0
= NULL
, *logmsg
, *newline
;
11050 committer_time
= got_object_commit_get_committer_time(commit
);
11051 if (gmtime_r(&committer_time
, &tm
) == NULL
)
11052 return got_error_from_errno("gmtime_r");
11053 if (strftime(datebuf
, sizeof(datebuf
), "%F", &tm
) == 0)
11054 return got_error(GOT_ERR_NO_SPACE
);
11056 author0
= strdup(got_object_commit_get_author(commit
));
11057 if (author0
== NULL
)
11058 return got_error_from_errno("strdup");
11060 smallerthan
= strchr(author
, '<');
11061 if (smallerthan
&& smallerthan
[1] != '\0')
11062 author
= smallerthan
+ 1;
11063 author
[strcspn(author
, "@>")] = '\0';
11065 err
= got_object_commit_get_logmsg(&logmsg0
, commit
);
11069 while (*logmsg
== '\n')
11071 newline
= strchr(logmsg
, '\n');
11075 if (asprintf(brief_str
, "%s %s %s",
11076 datebuf
, author
, logmsg
) == -1)
11077 err
= got_error_from_errno("asprintf");
11084 static const struct got_error
*
11085 delete_backup_ref(struct got_reference
*ref
, struct got_object_id
*id
,
11086 struct got_repository
*repo
)
11088 const struct got_error
*err
;
11091 err
= got_object_id_str(&id_str
, id
);
11095 err
= got_ref_delete(ref
, repo
);
11099 printf("Deleted %s: %s\n", got_ref_get_name(ref
), id_str
);
11105 static const struct got_error
*
11106 print_backup_ref(const char *branch_name
, const char *new_id_str
,
11107 struct got_object_id
*old_commit_id
, struct got_commit_object
*old_commit
,
11108 struct got_reflist_object_id_map
*refs_idmap
,
11109 struct got_repository
*repo
)
11111 const struct got_error
*err
= NULL
;
11112 struct got_reflist_head
*refs
;
11113 char *refs_str
= NULL
;
11114 struct got_object_id
*new_commit_id
= NULL
;
11115 struct got_commit_object
*new_commit
= NULL
;
11116 char *new_commit_brief_str
= NULL
;
11117 struct got_object_id
*yca_id
= NULL
;
11118 struct got_commit_object
*yca_commit
= NULL
;
11119 char *yca_id_str
= NULL
, *yca_brief_str
= NULL
;
11120 char *custom_refs_str
;
11122 if (asprintf(&custom_refs_str
, "formerly %s", branch_name
) == -1)
11123 return got_error_from_errno("asprintf");
11125 err
= print_commit(old_commit
, old_commit_id
, repo
, NULL
, NULL
, NULL
,
11126 0, 0, refs_idmap
, custom_refs_str
, NULL
);
11130 err
= got_object_resolve_id_str(&new_commit_id
, repo
, new_id_str
);
11134 refs
= got_reflist_object_id_map_lookup(refs_idmap
, new_commit_id
);
11136 err
= build_refs_str(&refs_str
, refs
, new_commit_id
, repo
, 0);
11141 err
= got_object_open_as_commit(&new_commit
, repo
, new_commit_id
);
11145 err
= get_commit_brief_str(&new_commit_brief_str
, new_commit
);
11149 err
= got_commit_graph_find_youngest_common_ancestor(&yca_id
,
11150 old_commit_id
, new_commit_id
, 1, 0, repo
, check_cancelled
, NULL
);
11154 printf("has become commit %s%s%s%s\n %s\n", new_id_str
,
11155 refs_str
? " (" : "", refs_str
? refs_str
: "",
11156 refs_str
? ")" : "", new_commit_brief_str
);
11157 if (yca_id
&& got_object_id_cmp(yca_id
, new_commit_id
) != 0 &&
11158 got_object_id_cmp(yca_id
, old_commit_id
) != 0) {
11162 err
= got_object_open_as_commit(&yca_commit
, repo
, yca_id
);
11166 err
= get_commit_brief_str(&yca_brief_str
, yca_commit
);
11170 err
= got_object_id_str(&yca_id_str
, yca_id
);
11174 refs
= got_reflist_object_id_map_lookup(refs_idmap
, yca_id
);
11176 err
= build_refs_str(&refs_str
, refs
, yca_id
, repo
, 0);
11180 printf("history forked at %s%s%s%s\n %s\n",
11182 refs_str
? " (" : "", refs_str
? refs_str
: "",
11183 refs_str
? ")" : "", yca_brief_str
);
11186 free(custom_refs_str
);
11187 free(new_commit_id
);
11191 free(yca_brief_str
);
11193 got_object_commit_close(new_commit
);
11195 got_object_commit_close(yca_commit
);
11200 static const struct got_error
*
11201 worktree_has_logmsg_ref(const char *caller
, struct got_worktree
*worktree
,
11202 struct got_repository
*repo
)
11204 const struct got_error
*err
;
11205 struct got_reflist_head refs
;
11206 struct got_reflist_entry
*re
;
11207 char *uuidstr
= NULL
;
11208 static char msg
[160];
11212 err
= got_worktree_get_uuid(&uuidstr
, worktree
);
11216 err
= got_ref_list(&refs
, repo
, "refs/got/worktree",
11217 got_ref_cmp_by_name
, repo
);
11221 TAILQ_FOREACH(re
, &refs
, entry
) {
11222 const char *cmd
, *refname
, *type
;
11224 refname
= got_ref_get_name(re
->ref
);
11226 if (strncmp(refname
, GOT_WORKTREE_CHERRYPICK_REF_PREFIX
,
11227 GOT_WORKTREE_CHERRYPICK_REF_PREFIX_LEN
) == 0) {
11228 refname
+= GOT_WORKTREE_CHERRYPICK_REF_PREFIX_LEN
+ 1;
11229 cmd
= "cherrypick";
11230 type
= "cherrypicked";
11231 } else if (strncmp(refname
, GOT_WORKTREE_BACKOUT_REF_PREFIX
,
11232 GOT_WORKTREE_BACKOUT_REF_PREFIX_LEN
) == 0) {
11233 refname
+= GOT_WORKTREE_BACKOUT_REF_PREFIX_LEN
+ 1;
11235 type
= "backed-out";
11239 if (strncmp(refname
, uuidstr
, GOT_WORKTREE_UUID_STRLEN
) != 0)
11242 snprintf(msg
, sizeof(msg
),
11243 "work tree has references created by %s commits which "
11244 "must be removed with 'got %s -X' before running the %s "
11245 "command", type
, cmd
, caller
);
11246 err
= got_error_msg(GOT_ERR_WORKTREE_META
, msg
);
11252 got_ref_list_free(&refs
);
11256 static const struct got_error
*
11257 process_backup_refs(const char *backup_ref_prefix
,
11258 const char *wanted_branch_name
,
11259 int delete, struct got_repository
*repo
)
11261 const struct got_error
*err
;
11262 struct got_reflist_head refs
, backup_refs
;
11263 struct got_reflist_entry
*re
;
11264 const size_t backup_ref_prefix_len
= strlen(backup_ref_prefix
);
11265 struct got_object_id
*old_commit_id
= NULL
;
11266 char *branch_name
= NULL
;
11267 struct got_commit_object
*old_commit
= NULL
;
11268 struct got_reflist_object_id_map
*refs_idmap
= NULL
;
11269 int wanted_branch_found
= 0;
11272 TAILQ_INIT(&backup_refs
);
11274 err
= got_ref_list(&refs
, repo
, NULL
, got_ref_cmp_by_name
, NULL
);
11278 err
= got_reflist_object_id_map_create(&refs_idmap
, &refs
, repo
);
11282 if (wanted_branch_name
) {
11283 if (strncmp(wanted_branch_name
, "refs/heads/", 11) == 0)
11284 wanted_branch_name
+= 11;
11287 err
= got_ref_list(&backup_refs
, repo
, backup_ref_prefix
,
11288 got_ref_cmp_by_commit_timestamp_descending
, repo
);
11292 TAILQ_FOREACH(re
, &backup_refs
, entry
) {
11293 const char *refname
= got_ref_get_name(re
->ref
);
11296 err
= check_cancelled(NULL
);
11300 err
= got_ref_resolve(&old_commit_id
, repo
, re
->ref
);
11304 err
= got_object_open_as_commit(&old_commit
, repo
,
11309 if (strncmp(backup_ref_prefix
, refname
,
11310 backup_ref_prefix_len
) == 0)
11311 refname
+= backup_ref_prefix_len
;
11313 while (refname
[0] == '/')
11316 branch_name
= strdup(refname
);
11317 if (branch_name
== NULL
) {
11318 err
= got_error_from_errno("strdup");
11321 slash
= strrchr(branch_name
, '/');
11324 refname
+= strlen(branch_name
) + 1;
11327 if (wanted_branch_name
== NULL
||
11328 strcmp(wanted_branch_name
, branch_name
) == 0) {
11329 wanted_branch_found
= 1;
11331 err
= delete_backup_ref(re
->ref
,
11332 old_commit_id
, repo
);
11334 err
= print_backup_ref(branch_name
, refname
,
11335 old_commit_id
, old_commit
, refs_idmap
,
11342 free(old_commit_id
);
11343 old_commit_id
= NULL
;
11345 branch_name
= NULL
;
11346 got_object_commit_close(old_commit
);
11350 if (wanted_branch_name
&& !wanted_branch_found
) {
11351 err
= got_error_fmt(GOT_ERR_NOT_REF
,
11352 "%s/%s/", backup_ref_prefix
, wanted_branch_name
);
11356 got_reflist_object_id_map_free(refs_idmap
);
11357 got_ref_list_free(&refs
);
11358 got_ref_list_free(&backup_refs
);
11359 free(old_commit_id
);
11362 got_object_commit_close(old_commit
);
11366 static const struct got_error
*
11367 abort_progress(void *arg
, unsigned char status
, const char *path
)
11370 * Unversioned files should not clutter progress output when
11371 * an operation is aborted.
11373 if (status
== GOT_STATUS_UNVERSIONED
)
11376 return update_progress(arg
, status
, path
);
11379 static const struct got_error
*
11380 find_merge_commit_yca(struct got_object_id
**new_yca_id
,
11381 struct got_object_id
*branch_head_commit_id
,
11382 struct got_object_id
*yca_id
,
11383 struct got_object_id
*base_commit_id
,
11384 struct got_repository
*repo
)
11386 const struct got_error
*err
= NULL
;
11387 struct got_commit_graph
*graph
= NULL
;
11388 struct got_commit_object
*commit
= NULL
;
11390 *new_yca_id
= NULL
;
11392 err
= got_commit_graph_open(&graph
, "/", 1);
11396 err
= got_commit_graph_bfsort(graph
, base_commit_id
,
11397 repo
, check_cancelled
, NULL
);
11402 struct got_object_id id
;
11404 err
= got_commit_graph_iter_next(&id
, graph
, repo
,
11405 check_cancelled
, NULL
);
11407 if (err
->code
== GOT_ERR_ITER_COMPLETED
)
11412 err
= got_object_open_as_commit(&commit
, repo
, &id
);
11416 if (got_object_commit_get_nparents(commit
) > 1) {
11417 /* Search for a better YCA using toposort. */
11418 err
= got_commit_graph_find_youngest_common_ancestor(
11419 new_yca_id
, base_commit_id
, branch_head_commit_id
,
11420 0, 1, repo
, check_cancelled
, NULL
);
11424 if (got_object_id_cmp(&id
, yca_id
) == 0)
11426 got_object_commit_close(commit
);
11430 got_commit_graph_close(graph
);
11432 got_object_commit_close(commit
);
11436 static const struct got_error
*
11437 cmd_rebase(int argc
, char *argv
[])
11439 const struct got_error
*error
= NULL
;
11440 struct got_worktree
*worktree
= NULL
;
11441 struct got_repository
*repo
= NULL
;
11442 struct got_fileindex
*fileindex
= NULL
;
11443 char *cwd
= NULL
, *committer
= NULL
, *gitconfig_path
= NULL
;
11444 struct got_reference
*branch
= NULL
;
11445 struct got_reference
*new_base_branch
= NULL
, *tmp_branch
= NULL
;
11446 struct got_object_id
*commit_id
= NULL
, *parent_id
= NULL
;
11447 struct got_object_id
*resume_commit_id
= NULL
;
11448 struct got_object_id
*branch_head_commit_id
= NULL
, *yca_id
= NULL
;
11449 struct got_object_id
*head_commit_id
= NULL
;
11450 struct got_reference
*head_ref
= NULL
;
11451 struct got_commit_object
*commit
= NULL
;
11452 int ch
, rebase_in_progress
= 0, abort_rebase
= 0, continue_rebase
= 0;
11453 int histedit_in_progress
= 0, merge_in_progress
= 0;
11454 int create_backup
= 1, list_backups
= 0, delete_backups
= 0;
11455 int allow_conflict
= 0;
11456 struct got_object_id_queue commits
;
11457 struct got_pathlist_head merged_paths
;
11458 const struct got_object_id_queue
*parent_ids
;
11459 struct got_object_qid
*qid
, *pid
;
11460 struct got_update_progress_arg upa
;
11461 int *pack_fds
= NULL
;
11463 STAILQ_INIT(&commits
);
11464 TAILQ_INIT(&merged_paths
);
11465 memset(&upa
, 0, sizeof(upa
));
11468 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
11469 "unveil", NULL
) == -1)
11473 while ((ch
= getopt(argc
, argv
, "aCclX")) != -1) {
11479 allow_conflict
= 1;
11482 continue_rebase
= 1;
11488 delete_backups
= 1;
11499 if (list_backups
) {
11501 option_conflict('l', 'a');
11502 if (allow_conflict
)
11503 option_conflict('l', 'C');
11504 if (continue_rebase
)
11505 option_conflict('l', 'c');
11506 if (delete_backups
)
11507 option_conflict('l', 'X');
11508 if (argc
!= 0 && argc
!= 1)
11510 } else if (delete_backups
) {
11512 option_conflict('X', 'a');
11513 if (allow_conflict
)
11514 option_conflict('X', 'C');
11515 if (continue_rebase
)
11516 option_conflict('X', 'c');
11518 option_conflict('l', 'X');
11519 if (argc
!= 0 && argc
!= 1)
11521 } else if (allow_conflict
) {
11523 option_conflict('C', 'a');
11524 if (!continue_rebase
)
11525 errx(1, "-C option requires -c");
11527 if (abort_rebase
&& continue_rebase
)
11529 else if (abort_rebase
|| continue_rebase
) {
11532 } else if (argc
!= 1)
11536 cwd
= getcwd(NULL
, 0);
11538 error
= got_error_from_errno("getcwd");
11542 error
= got_repo_pack_fds_open(&pack_fds
);
11546 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_GOT_DIR
);
11548 if (list_backups
|| delete_backups
) {
11549 if (error
->code
!= GOT_ERR_NOT_WORKTREE
)
11552 if (error
->code
== GOT_ERR_NOT_WORKTREE
)
11553 error
= wrap_not_worktree_error(error
,
11559 error
= get_gitconfig_path(&gitconfig_path
);
11562 error
= got_repo_open(&repo
,
11563 worktree
? got_worktree_get_repo_path(worktree
) : cwd
,
11564 gitconfig_path
, pack_fds
);
11568 if (worktree
!= NULL
&& !list_backups
&& !delete_backups
) {
11569 error
= worktree_has_logmsg_ref("rebase", worktree
, repo
);
11574 error
= get_author(&committer
, repo
, worktree
);
11575 if (error
&& error
->code
!= GOT_ERR_COMMIT_NO_AUTHOR
)
11578 error
= apply_unveil(got_repo_get_path(repo
), 0,
11579 worktree
? got_worktree_get_root_path(worktree
) : NULL
);
11583 if (list_backups
|| delete_backups
) {
11584 error
= process_backup_refs(
11585 GOT_WORKTREE_REBASE_BACKUP_REF_PREFIX
,
11586 argc
== 1 ? argv
[0] : NULL
, delete_backups
, repo
);
11587 goto done
; /* nothing else to do */
11590 error
= got_worktree_histedit_in_progress(&histedit_in_progress
,
11594 if (histedit_in_progress
) {
11595 error
= got_error(GOT_ERR_HISTEDIT_BUSY
);
11599 error
= got_worktree_merge_in_progress(&merge_in_progress
,
11603 if (merge_in_progress
) {
11604 error
= got_error(GOT_ERR_MERGE_BUSY
);
11608 error
= got_worktree_rebase_in_progress(&rebase_in_progress
, worktree
);
11612 if (abort_rebase
) {
11613 if (!rebase_in_progress
) {
11614 error
= got_error(GOT_ERR_NOT_REBASING
);
11617 error
= got_worktree_rebase_continue(&resume_commit_id
,
11618 &new_base_branch
, &tmp_branch
, &branch
, &fileindex
,
11622 printf("Switching work tree to %s\n",
11623 got_ref_get_symref_target(new_base_branch
));
11624 error
= got_worktree_rebase_abort(worktree
, fileindex
, repo
,
11625 new_base_branch
, abort_progress
, &upa
);
11628 printf("Rebase of %s aborted\n", got_ref_get_name(branch
));
11629 print_merge_progress_stats(&upa
);
11630 goto done
; /* nothing else to do */
11633 if (continue_rebase
) {
11634 if (!rebase_in_progress
) {
11635 error
= got_error(GOT_ERR_NOT_REBASING
);
11638 error
= got_worktree_rebase_continue(&resume_commit_id
,
11639 &new_base_branch
, &tmp_branch
, &branch
, &fileindex
,
11644 error
= rebase_commit(NULL
, worktree
, fileindex
, tmp_branch
,
11645 committer
, resume_commit_id
, allow_conflict
, repo
);
11649 yca_id
= got_object_id_dup(resume_commit_id
);
11650 if (yca_id
== NULL
) {
11651 error
= got_error_from_errno("got_object_id_dup");
11655 error
= got_ref_open(&branch
, repo
, argv
[0], 0);
11658 if (strncmp(got_ref_get_name(branch
), "refs/heads/", 11) != 0) {
11659 error
= got_error_msg(GOT_ERR_COMMIT_BRANCH
,
11660 "will not rebase a branch which lives outside "
11661 "the \"refs/heads/\" reference namespace");
11666 error
= got_ref_resolve(&branch_head_commit_id
, repo
, branch
);
11670 if (!continue_rebase
) {
11671 struct got_object_id
*base_commit_id
;
11673 error
= got_ref_open(&head_ref
, repo
,
11674 got_worktree_get_head_ref_name(worktree
), 0);
11677 error
= got_ref_resolve(&head_commit_id
, repo
, head_ref
);
11680 base_commit_id
= got_worktree_get_base_commit_id(worktree
);
11681 if (got_object_id_cmp(base_commit_id
, head_commit_id
) != 0) {
11682 error
= got_error(GOT_ERR_REBASE_OUT_OF_DATE
);
11686 error
= got_commit_graph_find_youngest_common_ancestor(&yca_id
,
11687 base_commit_id
, branch_head_commit_id
, 1, 0,
11688 repo
, check_cancelled
, NULL
);
11690 if (error
->code
== GOT_ERR_ANCESTRY
) {
11691 error
= got_error_msg(GOT_ERR_ANCESTRY
,
11692 "specified branch shares no common "
11693 "ancestry with work tree's branch");
11699 * If a merge commit appears between the new base branch tip
11700 * and a YCA found via first-parent traversal then we might
11701 * find a better YCA using topologically sorted commits.
11703 if (got_object_id_cmp(base_commit_id
, yca_id
) != 0) {
11704 struct got_object_id
*better_yca_id
;
11705 error
= find_merge_commit_yca(&better_yca_id
,
11706 branch_head_commit_id
, yca_id
,
11707 base_commit_id
, repo
);
11710 if (better_yca_id
) {
11712 yca_id
= better_yca_id
;
11716 if (got_object_id_cmp(base_commit_id
, yca_id
) == 0) {
11717 struct got_pathlist_head paths
;
11718 const char *branch_name
= got_ref_get_name(branch
);
11720 got_worktree_get_head_ref_name(worktree
);
11722 if (strcmp(branch_name
, base
) == 0) {
11723 error
= got_error_fmt(GOT_ERR_WRONG_BRANCH
,
11724 "cannot rebase %s onto itself",
11728 printf("%s is already based on %s\n",
11729 branch_name
, base
);
11731 error
= switch_head_ref(branch
, branch_head_commit_id
,
11735 error
= got_worktree_set_base_commit_id(worktree
, repo
,
11736 branch_head_commit_id
);
11739 TAILQ_INIT(&paths
);
11740 error
= got_pathlist_append(&paths
, "", NULL
);
11743 error
= got_worktree_checkout_files(worktree
,
11744 &paths
, repo
, update_progress
, &upa
,
11745 check_cancelled
, NULL
);
11746 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_NONE
);
11749 if (upa
.did_something
) {
11751 error
= got_object_id_str(&id_str
,
11752 branch_head_commit_id
);
11755 printf("Updated to %s: %s\n",
11756 got_worktree_get_head_ref_name(worktree
),
11760 printf("Already up-to-date\n");
11761 print_update_progress_stats(&upa
);
11766 commit_id
= branch_head_commit_id
;
11767 error
= got_object_open_as_commit(&commit
, repo
, commit_id
);
11771 parent_ids
= got_object_commit_get_parent_ids(commit
);
11772 pid
= STAILQ_FIRST(parent_ids
);
11774 error
= collect_commits(&commits
, commit_id
, &pid
->id
,
11775 yca_id
, got_worktree_get_path_prefix(worktree
),
11776 GOT_ERR_REBASE_PATH
, repo
);
11781 got_object_commit_close(commit
);
11784 if (!continue_rebase
) {
11785 error
= got_worktree_rebase_prepare(&new_base_branch
,
11786 &tmp_branch
, &fileindex
, worktree
, branch
, repo
);
11791 if (STAILQ_EMPTY(&commits
)) {
11792 if (continue_rebase
) {
11793 error
= rebase_complete(worktree
, fileindex
,
11794 branch
, tmp_branch
, repo
, create_backup
);
11797 /* Fast-forward the reference of the branch. */
11798 struct got_object_id
*new_head_commit_id
;
11800 error
= got_ref_resolve(&new_head_commit_id
, repo
,
11804 error
= got_object_id_str(&id_str
, new_head_commit_id
);
11807 printf("Forwarding %s to commit %s\n",
11808 got_ref_get_name(branch
), id_str
);
11810 error
= got_ref_change_ref(branch
,
11811 new_head_commit_id
);
11814 /* No backup needed since objects did not change. */
11820 STAILQ_FOREACH(qid
, &commits
, entry
) {
11822 commit_id
= &qid
->id
;
11823 parent_id
= pid
? &pid
->id
: yca_id
;
11826 memset(&upa
, 0, sizeof(upa
));
11827 error
= got_worktree_rebase_merge_files(&merged_paths
,
11828 worktree
, fileindex
, parent_id
, commit_id
, repo
,
11829 update_progress
, &upa
, check_cancelled
, NULL
);
11833 print_merge_progress_stats(&upa
);
11834 if (upa
.conflicts
> 0 || upa
.missing
> 0 ||
11835 upa
.not_deleted
> 0 || upa
.unversioned
> 0) {
11836 if (upa
.conflicts
> 0) {
11837 error
= show_rebase_merge_conflict(&qid
->id
,
11842 got_pathlist_free(&merged_paths
, GOT_PATHLIST_FREE_PATH
);
11846 error
= rebase_commit(&merged_paths
, worktree
, fileindex
,
11847 tmp_branch
, committer
, commit_id
, 0, repo
);
11848 got_pathlist_free(&merged_paths
, GOT_PATHLIST_FREE_PATH
);
11853 if (upa
.conflicts
> 0 || upa
.missing
> 0 ||
11854 upa
.not_deleted
> 0 || upa
.unversioned
> 0) {
11855 error
= got_worktree_rebase_postpone(worktree
, fileindex
);
11858 if (upa
.conflicts
> 0 && upa
.missing
== 0 &&
11859 upa
.not_deleted
== 0 && upa
.unversioned
== 0) {
11860 error
= got_error_msg(GOT_ERR_CONFLICTS
,
11861 "conflicts must be resolved before rebasing "
11863 } else if (upa
.conflicts
> 0) {
11864 error
= got_error_msg(GOT_ERR_CONFLICTS
,
11865 "conflicts must be resolved before rebasing "
11866 "can continue; changes destined for some "
11867 "files were not yet merged and should be "
11868 "merged manually if required before the "
11869 "rebase operation is continued");
11871 error
= got_error_msg(GOT_ERR_CONFLICTS
,
11872 "changes destined for some files were not "
11873 "yet merged and should be merged manually "
11874 "if required before the rebase operation "
11878 error
= rebase_complete(worktree
, fileindex
, branch
,
11879 tmp_branch
, repo
, create_backup
);
11883 free(gitconfig_path
);
11884 got_object_id_queue_free(&commits
);
11885 free(branch_head_commit_id
);
11886 free(resume_commit_id
);
11887 free(head_commit_id
);
11890 got_object_commit_close(commit
);
11892 got_ref_close(branch
);
11893 if (new_base_branch
)
11894 got_ref_close(new_base_branch
);
11896 got_ref_close(tmp_branch
);
11898 got_ref_close(head_ref
);
11900 got_worktree_close(worktree
);
11902 const struct got_error
*close_err
= got_repo_close(repo
);
11907 const struct got_error
*pack_err
=
11908 got_repo_pack_fds_close(pack_fds
);
11916 usage_histedit(void)
11918 fprintf(stderr
, "usage: %s histedit [-aCcdeflmX] [-F histedit-script] "
11919 "[branch]\n", getprogname());
11923 #define GOT_HISTEDIT_PICK 'p'
11924 #define GOT_HISTEDIT_EDIT 'e'
11925 #define GOT_HISTEDIT_FOLD 'f'
11926 #define GOT_HISTEDIT_DROP 'd'
11927 #define GOT_HISTEDIT_MESG 'm'
11929 static const struct got_histedit_cmd
{
11930 unsigned char code
;
11933 } got_histedit_cmds
[] = {
11934 { GOT_HISTEDIT_PICK
, "pick", "use commit" },
11935 { GOT_HISTEDIT_EDIT
, "edit", "use commit but stop for amending" },
11936 { GOT_HISTEDIT_FOLD
, "fold", "combine with next commit that will "
11938 { GOT_HISTEDIT_DROP
, "drop", "remove commit from history" },
11939 { GOT_HISTEDIT_MESG
, "mesg", "open editor to edit the log message" },
11942 struct got_histedit_list_entry
{
11943 TAILQ_ENTRY(got_histedit_list_entry
) entry
;
11944 struct got_object_id
*commit_id
;
11945 const struct got_histedit_cmd
*cmd
;
11948 TAILQ_HEAD(got_histedit_list
, got_histedit_list_entry
);
11950 static const struct got_error
*
11951 histedit_write_commit(struct got_object_id
*commit_id
, const char *cmdname
,
11952 FILE *f
, struct got_repository
*repo
)
11954 const struct got_error
*err
= NULL
;
11955 char *logmsg
= NULL
, *id_str
= NULL
;
11956 struct got_commit_object
*commit
= NULL
;
11959 err
= got_object_open_as_commit(&commit
, repo
, commit_id
);
11963 err
= get_short_logmsg(&logmsg
, 34, commit
);
11967 err
= got_object_id_str(&id_str
, commit_id
);
11971 n
= fprintf(f
, "%s %s %s\n", cmdname
, id_str
, logmsg
);
11973 err
= got_ferror(f
, GOT_ERR_IO
);
11976 got_object_commit_close(commit
);
11982 static const struct got_error
*
11983 histedit_write_commit_list(struct got_object_id_queue
*commits
,
11984 FILE *f
, int edit_logmsg_only
, int fold_only
, int drop_only
,
11985 int edit_only
, struct got_repository
*repo
)
11987 const struct got_error
*err
= NULL
;
11988 struct got_object_qid
*qid
;
11989 const char *histedit_cmd
= NULL
;
11991 if (STAILQ_EMPTY(commits
))
11992 return got_error(GOT_ERR_EMPTY_HISTEDIT
);
11994 STAILQ_FOREACH(qid
, commits
, entry
) {
11995 histedit_cmd
= got_histedit_cmds
[0].name
;
11997 histedit_cmd
= "drop";
11998 else if (edit_only
)
11999 histedit_cmd
= "edit";
12000 else if (fold_only
&& STAILQ_NEXT(qid
, entry
) != NULL
)
12001 histedit_cmd
= "fold";
12002 else if (edit_logmsg_only
)
12003 histedit_cmd
= "mesg";
12004 err
= histedit_write_commit(&qid
->id
, histedit_cmd
, f
, repo
);
12012 static const struct got_error
*
12013 write_cmd_list(FILE *f
, const char *branch_name
,
12014 struct got_object_id_queue
*commits
)
12016 const struct got_error
*err
= NULL
;
12020 struct got_object_qid
*qid
;
12022 qid
= STAILQ_FIRST(commits
);
12023 err
= got_object_id_str(&id_str
, &qid
->id
);
12028 "# Editing the history of branch '%s' starting at\n"
12030 "# Commits will be processed in order from top to "
12031 "bottom of this file.\n", branch_name
, id_str
);
12033 err
= got_ferror(f
, GOT_ERR_IO
);
12037 n
= fprintf(f
, "# Available histedit commands:\n");
12039 err
= got_ferror(f
, GOT_ERR_IO
);
12043 for (i
= 0; i
< nitems(got_histedit_cmds
); i
++) {
12044 const struct got_histedit_cmd
*cmd
= &got_histedit_cmds
[i
];
12045 n
= fprintf(f
, "# %s (%c): %s\n", cmd
->name
, cmd
->code
,
12048 err
= got_ferror(f
, GOT_ERR_IO
);
12057 static const struct got_error
*
12058 histedit_syntax_error(int lineno
)
12060 static char msg
[42];
12063 ret
= snprintf(msg
, sizeof(msg
), "histedit syntax error on line %d",
12065 if (ret
< 0 || (size_t)ret
>= sizeof(msg
))
12066 return got_error(GOT_ERR_HISTEDIT_SYNTAX
);
12068 return got_error_msg(GOT_ERR_HISTEDIT_SYNTAX
, msg
);
12071 static const struct got_error
*
12072 append_folded_commit_msg(char **new_msg
, struct got_histedit_list_entry
*hle
,
12073 char *logmsg
, struct got_repository
*repo
)
12075 const struct got_error
*err
;
12076 struct got_commit_object
*folded_commit
= NULL
;
12077 char *id_str
, *folded_logmsg
= NULL
;
12079 err
= got_object_id_str(&id_str
, hle
->commit_id
);
12083 err
= got_object_open_as_commit(&folded_commit
, repo
, hle
->commit_id
);
12087 err
= got_object_commit_get_logmsg(&folded_logmsg
, folded_commit
);
12090 if (asprintf(new_msg
, "%s%s# log message of folded commit %s: %s",
12091 logmsg
? logmsg
: "", logmsg
? "\n" : "", id_str
,
12092 folded_logmsg
) == -1) {
12093 err
= got_error_from_errno("asprintf");
12097 got_object_commit_close(folded_commit
);
12099 free(folded_logmsg
);
12103 static struct got_histedit_list_entry
*
12104 get_folded_commits(struct got_histedit_list_entry
*hle
)
12106 struct got_histedit_list_entry
*prev
, *folded
= NULL
;
12108 prev
= TAILQ_PREV(hle
, got_histedit_list
, entry
);
12109 while (prev
&& (prev
->cmd
->code
== GOT_HISTEDIT_FOLD
||
12110 prev
->cmd
->code
== GOT_HISTEDIT_DROP
)) {
12111 if (prev
->cmd
->code
== GOT_HISTEDIT_FOLD
)
12113 prev
= TAILQ_PREV(prev
, got_histedit_list
, entry
);
12119 static const struct got_error
*
12120 histedit_edit_logmsg(struct got_histedit_list_entry
*hle
,
12121 const char *editor
, struct got_repository
*repo
)
12123 char *logmsg_path
= NULL
, *id_str
= NULL
, *orig_logmsg
= NULL
;
12124 char *logmsg
= NULL
, *new_msg
= NULL
;
12125 const struct got_error
*err
= NULL
;
12126 struct got_commit_object
*commit
= NULL
;
12129 struct got_histedit_list_entry
*folded
= NULL
;
12131 err
= got_object_open_as_commit(&commit
, repo
, hle
->commit_id
);
12135 folded
= get_folded_commits(hle
);
12137 while (folded
!= hle
) {
12138 if (folded
->cmd
->code
== GOT_HISTEDIT_DROP
) {
12139 folded
= TAILQ_NEXT(folded
, entry
);
12142 err
= append_folded_commit_msg(&new_msg
, folded
,
12148 folded
= TAILQ_NEXT(folded
, entry
);
12152 err
= got_object_id_str(&id_str
, hle
->commit_id
);
12155 err
= got_object_commit_get_logmsg(&orig_logmsg
, commit
);
12158 logmsg_len
= asprintf(&new_msg
,
12159 "%s\n# original log message of commit %s: %s",
12160 logmsg
? logmsg
: "", id_str
, orig_logmsg
);
12161 if (logmsg_len
== -1) {
12162 err
= got_error_from_errno("asprintf");
12168 err
= got_object_id_str(&id_str
, hle
->commit_id
);
12172 err
= got_opentemp_named_fd(&logmsg_path
, &fd
,
12173 GOT_TMPDIR_STR
"/got-logmsg", "");
12177 if (write(fd
, logmsg
, logmsg_len
) == -1) {
12178 err
= got_error_from_errno2("write", logmsg_path
);
12181 if (close(fd
) == -1) {
12182 err
= got_error_from_errno2("close", logmsg_path
);
12187 err
= edit_logmsg(&hle
->logmsg
, editor
, logmsg_path
, logmsg
,
12190 if (err
->code
!= GOT_ERR_COMMIT_MSG_EMPTY
)
12193 hle
->logmsg
= strdup(new_msg
);
12194 if (hle
->logmsg
== NULL
)
12195 err
= got_error_from_errno("strdup");
12198 if (fd
!= -1 && close(fd
) == -1 && err
== NULL
)
12199 err
= got_error_from_errno2("close", logmsg_path
);
12200 if (logmsg_path
&& unlink(logmsg_path
) != 0 && err
== NULL
)
12201 err
= got_error_from_errno2("unlink", logmsg_path
);
12206 got_object_commit_close(commit
);
12210 static const struct got_error
*
12211 histedit_parse_list(struct got_histedit_list
*histedit_cmds
,
12212 FILE *f
, struct got_repository
*repo
)
12214 const struct got_error
*err
= NULL
;
12215 char *line
= NULL
, *p
, *end
;
12216 size_t i
, linesize
= 0;
12219 const struct got_histedit_cmd
*cmd
;
12220 struct got_object_id
*commit_id
= NULL
;
12221 struct got_histedit_list_entry
*hle
= NULL
;
12224 linelen
= getline(&line
, &linesize
, f
);
12225 if (linelen
== -1) {
12226 const struct got_error
*getline_err
;
12229 getline_err
= got_error_from_errno("getline");
12230 err
= got_ferror(f
, getline_err
->code
);
12235 while (isspace((unsigned char)p
[0]))
12237 if (p
[0] == '#' || p
[0] == '\0')
12240 for (i
= 0; i
< nitems(got_histedit_cmds
); i
++) {
12241 cmd
= &got_histedit_cmds
[i
];
12242 if (strncmp(cmd
->name
, p
, strlen(cmd
->name
)) == 0 &&
12243 isspace((unsigned char)p
[strlen(cmd
->name
)])) {
12244 p
+= strlen(cmd
->name
);
12247 if (p
[0] == cmd
->code
&& isspace((unsigned char)p
[1])) {
12252 if (i
== nitems(got_histedit_cmds
)) {
12253 err
= histedit_syntax_error(lineno
);
12256 while (isspace((unsigned char)p
[0]))
12259 while (end
[0] && !isspace((unsigned char)end
[0]))
12262 err
= got_object_resolve_id_str(&commit_id
, repo
, p
);
12264 /* override error code */
12265 err
= histedit_syntax_error(lineno
);
12268 hle
= malloc(sizeof(*hle
));
12270 err
= got_error_from_errno("malloc");
12274 hle
->commit_id
= commit_id
;
12275 hle
->logmsg
= NULL
;
12277 TAILQ_INSERT_TAIL(histedit_cmds
, hle
, entry
);
12285 static const struct got_error
*
12286 histedit_check_script(struct got_histedit_list
*histedit_cmds
,
12287 struct got_object_id_queue
*commits
, struct got_repository
*repo
)
12289 const struct got_error
*err
= NULL
;
12290 struct got_object_qid
*qid
;
12291 struct got_histedit_list_entry
*hle
;
12292 static char msg
[92];
12295 if (TAILQ_EMPTY(histedit_cmds
))
12296 return got_error_msg(GOT_ERR_EMPTY_HISTEDIT
,
12297 "histedit script contains no commands");
12298 if (STAILQ_EMPTY(commits
))
12299 return got_error(GOT_ERR_EMPTY_HISTEDIT
);
12301 TAILQ_FOREACH(hle
, histedit_cmds
, entry
) {
12302 struct got_histedit_list_entry
*hle2
;
12303 TAILQ_FOREACH(hle2
, histedit_cmds
, entry
) {
12306 if (got_object_id_cmp(hle
->commit_id
,
12307 hle2
->commit_id
) != 0)
12309 err
= got_object_id_str(&id_str
, hle
->commit_id
);
12312 snprintf(msg
, sizeof(msg
), "commit %s is listed "
12313 "more than once in histedit script", id_str
);
12315 return got_error_msg(GOT_ERR_HISTEDIT_CMD
, msg
);
12319 STAILQ_FOREACH(qid
, commits
, entry
) {
12320 TAILQ_FOREACH(hle
, histedit_cmds
, entry
) {
12321 if (got_object_id_cmp(&qid
->id
, hle
->commit_id
) == 0)
12325 err
= got_object_id_str(&id_str
, &qid
->id
);
12328 snprintf(msg
, sizeof(msg
),
12329 "commit %s missing from histedit script", id_str
);
12331 return got_error_msg(GOT_ERR_HISTEDIT_CMD
, msg
);
12335 hle
= TAILQ_LAST(histedit_cmds
, got_histedit_list
);
12336 if (hle
&& hle
->cmd
->code
== GOT_HISTEDIT_FOLD
)
12337 return got_error_msg(GOT_ERR_HISTEDIT_CMD
,
12338 "last commit in histedit script cannot be folded");
12343 static const struct got_error
*
12344 histedit_run_editor(struct got_histedit_list
*histedit_cmds
,
12345 const char *editor
, const char *path
,
12346 struct got_object_id_queue
*commits
, struct got_repository
*repo
)
12348 const struct got_error
*err
= NULL
;
12349 struct stat st
, st2
;
12350 struct timespec timeout
;
12353 if (stat(path
, &st
) == -1) {
12354 err
= got_error_from_errno2("stat", path
);
12358 if (spawn_editor(editor
, path
) == -1) {
12359 err
= got_error_from_errno("failed spawning editor");
12363 timeout
.tv_sec
= 0;
12364 timeout
.tv_nsec
= 1;
12365 nanosleep(&timeout
, NULL
);
12367 if (stat(path
, &st2
) == -1) {
12368 err
= got_error_from_errno2("stat", path
);
12372 if (st
.st_size
== st2
.st_size
&&
12373 timespeccmp(&st
.st_mtim
, &st2
.st_mtim
, ==)) {
12374 err
= got_error_msg(GOT_ERR_EMPTY_HISTEDIT
,
12375 "no changes made to histedit script, aborting");
12379 f
= fopen(path
, "re");
12381 err
= got_error_from_errno("fopen");
12384 err
= histedit_parse_list(histedit_cmds
, f
, repo
);
12388 err
= histedit_check_script(histedit_cmds
, commits
, repo
);
12390 if (f
&& fclose(f
) == EOF
&& err
== NULL
)
12391 err
= got_error_from_errno("fclose");
12395 static const struct got_error
*
12396 histedit_edit_list_retry(struct got_histedit_list
*, const struct got_error
*,
12397 struct got_object_id_queue
*, const char *, const char *, const char *,
12398 struct got_repository
*);
12400 static const struct got_error
*
12401 histedit_edit_script(struct got_histedit_list
*histedit_cmds
,
12402 struct got_object_id_queue
*commits
, const char *branch_name
,
12403 int edit_logmsg_only
, int fold_only
, int drop_only
, int edit_only
,
12404 const char *editor
, struct got_repository
*repo
)
12406 const struct got_error
*err
;
12410 err
= got_opentemp_named(&path
, &f
, "got-histedit", "");
12414 err
= write_cmd_list(f
, branch_name
, commits
);
12418 err
= histedit_write_commit_list(commits
, f
, edit_logmsg_only
,
12419 fold_only
, drop_only
, edit_only
, repo
);
12423 if (drop_only
|| edit_logmsg_only
|| fold_only
|| edit_only
) {
12425 err
= histedit_parse_list(histedit_cmds
, f
, repo
);
12427 if (fclose(f
) == EOF
) {
12428 err
= got_error_from_errno("fclose");
12432 err
= histedit_run_editor(histedit_cmds
, editor
, path
,
12435 if (err
->code
!= GOT_ERR_HISTEDIT_SYNTAX
&&
12436 err
->code
!= GOT_ERR_HISTEDIT_CMD
)
12438 err
= histedit_edit_list_retry(histedit_cmds
, err
,
12439 commits
, editor
, path
, branch_name
, repo
);
12443 if (f
&& fclose(f
) == EOF
&& err
== NULL
)
12444 err
= got_error_from_errno("fclose");
12445 if (path
&& unlink(path
) != 0 && err
== NULL
)
12446 err
= got_error_from_errno2("unlink", path
);
12451 static const struct got_error
*
12452 histedit_save_list(struct got_histedit_list
*histedit_cmds
,
12453 struct got_worktree
*worktree
, struct got_repository
*repo
)
12455 const struct got_error
*err
= NULL
;
12458 struct got_histedit_list_entry
*hle
;
12460 err
= got_worktree_get_histedit_script_path(&path
, worktree
);
12464 f
= fopen(path
, "we");
12466 err
= got_error_from_errno2("fopen", path
);
12469 TAILQ_FOREACH(hle
, histedit_cmds
, entry
) {
12470 err
= histedit_write_commit(hle
->commit_id
, hle
->cmd
->name
, f
,
12476 if (f
&& fclose(f
) == EOF
&& err
== NULL
)
12477 err
= got_error_from_errno("fclose");
12483 histedit_free_list(struct got_histedit_list
*histedit_cmds
)
12485 struct got_histedit_list_entry
*hle
;
12487 while ((hle
= TAILQ_FIRST(histedit_cmds
))) {
12488 TAILQ_REMOVE(histedit_cmds
, hle
, entry
);
12493 static const struct got_error
*
12494 histedit_load_list(struct got_histedit_list
*histedit_cmds
,
12495 const char *path
, struct got_repository
*repo
)
12497 const struct got_error
*err
= NULL
;
12500 f
= fopen(path
, "re");
12502 err
= got_error_from_errno2("fopen", path
);
12506 err
= histedit_parse_list(histedit_cmds
, f
, repo
);
12508 if (f
&& fclose(f
) == EOF
&& err
== NULL
)
12509 err
= got_error_from_errno("fclose");
12513 static const struct got_error
*
12514 histedit_edit_list_retry(struct got_histedit_list
*histedit_cmds
,
12515 const struct got_error
*edit_err
, struct got_object_id_queue
*commits
,
12516 const char *editor
, const char *path
, const char *branch_name
,
12517 struct got_repository
*repo
)
12519 const struct got_error
*err
= NULL
, *prev_err
= edit_err
;
12522 while (resp
!= 'c' && resp
!= 'r' && resp
!= 'a') {
12523 printf("%s: %s\n(c)ontinue editing, (r)estart editing, "
12524 "or (a)bort: ", getprogname(), prev_err
->msg
);
12529 histedit_free_list(histedit_cmds
);
12530 err
= histedit_run_editor(histedit_cmds
, editor
, path
,
12533 if (err
->code
!= GOT_ERR_HISTEDIT_SYNTAX
&&
12534 err
->code
!= GOT_ERR_HISTEDIT_CMD
)
12541 } else if (resp
== 'r') {
12542 histedit_free_list(histedit_cmds
);
12543 err
= histedit_edit_script(histedit_cmds
,
12544 commits
, branch_name
, 0, 0, 0, 0, editor
, repo
);
12546 if (err
->code
!= GOT_ERR_HISTEDIT_SYNTAX
&&
12547 err
->code
!= GOT_ERR_HISTEDIT_CMD
)
12554 } else if (resp
== 'a') {
12555 err
= got_error(GOT_ERR_HISTEDIT_CANCEL
);
12558 printf("invalid response '%c'\n", resp
);
12564 static const struct got_error
*
12565 histedit_complete(struct got_worktree
*worktree
,
12566 struct got_fileindex
*fileindex
, struct got_reference
*tmp_branch
,
12567 struct got_reference
*branch
, struct got_repository
*repo
)
12569 printf("Switching work tree to %s\n",
12570 got_ref_get_symref_target(branch
));
12571 return got_worktree_histedit_complete(worktree
, fileindex
, tmp_branch
,
12575 static const struct got_error
*
12576 show_histedit_progress(struct got_commit_object
*commit
,
12577 struct got_histedit_list_entry
*hle
, struct got_object_id
*new_id
)
12579 const struct got_error
*err
;
12580 char *old_id_str
= NULL
, *new_id_str
= NULL
, *logmsg
= NULL
;
12582 err
= got_object_id_str(&old_id_str
, hle
->commit_id
);
12587 err
= got_object_id_str(&new_id_str
, new_id
);
12592 old_id_str
[12] = '\0';
12594 new_id_str
[12] = '\0';
12597 logmsg
= strdup(hle
->logmsg
);
12598 if (logmsg
== NULL
) {
12599 err
= got_error_from_errno("strdup");
12602 trim_logmsg(logmsg
, 42);
12604 err
= get_short_logmsg(&logmsg
, 42, commit
);
12609 switch (hle
->cmd
->code
) {
12610 case GOT_HISTEDIT_PICK
:
12611 case GOT_HISTEDIT_EDIT
:
12612 case GOT_HISTEDIT_MESG
:
12613 printf("%s -> %s: %s\n", old_id_str
,
12614 new_id_str
? new_id_str
: "no-op change", logmsg
);
12616 case GOT_HISTEDIT_DROP
:
12617 case GOT_HISTEDIT_FOLD
:
12618 printf("%s -> %s commit: %s\n", old_id_str
, hle
->cmd
->name
,
12630 static const struct got_error
*
12631 histedit_commit(struct got_pathlist_head
*merged_paths
,
12632 struct got_worktree
*worktree
, struct got_fileindex
*fileindex
,
12633 struct got_reference
*tmp_branch
, struct got_histedit_list_entry
*hle
,
12634 const char *committer
, int allow_conflict
, const char *editor
,
12635 struct got_repository
*repo
)
12637 const struct got_error
*err
;
12638 struct got_commit_object
*commit
;
12639 struct got_object_id
*new_commit_id
;
12641 if ((hle
->cmd
->code
== GOT_HISTEDIT_EDIT
|| get_folded_commits(hle
))
12642 && hle
->logmsg
== NULL
) {
12643 err
= histedit_edit_logmsg(hle
, editor
, repo
);
12648 err
= got_object_open_as_commit(&commit
, repo
, hle
->commit_id
);
12652 err
= got_worktree_histedit_commit(&new_commit_id
, merged_paths
,
12653 worktree
, fileindex
, tmp_branch
, committer
, commit
, hle
->commit_id
,
12654 hle
->logmsg
, allow_conflict
, repo
);
12656 if (err
->code
!= GOT_ERR_COMMIT_NO_CHANGES
)
12658 err
= show_histedit_progress(commit
, hle
, NULL
);
12660 err
= show_histedit_progress(commit
, hle
, new_commit_id
);
12661 free(new_commit_id
);
12664 got_object_commit_close(commit
);
12668 static const struct got_error
*
12669 histedit_skip_commit(struct got_histedit_list_entry
*hle
,
12670 struct got_worktree
*worktree
, struct got_repository
*repo
)
12672 const struct got_error
*error
;
12673 struct got_commit_object
*commit
;
12675 error
= got_worktree_histedit_skip_commit(worktree
, hle
->commit_id
,
12680 error
= got_object_open_as_commit(&commit
, repo
, hle
->commit_id
);
12684 error
= show_histedit_progress(commit
, hle
, NULL
);
12685 got_object_commit_close(commit
);
12689 static const struct got_error
*
12690 check_local_changes(void *arg
, unsigned char status
,
12691 unsigned char staged_status
, const char *path
,
12692 struct got_object_id
*blob_id
, struct got_object_id
*staged_blob_id
,
12693 struct got_object_id
*commit_id
, int dirfd
, const char *de_name
)
12695 int *have_local_changes
= arg
;
12698 case GOT_STATUS_ADD
:
12699 case GOT_STATUS_DELETE
:
12700 case GOT_STATUS_MODIFY
:
12701 case GOT_STATUS_CONFLICT
:
12702 *have_local_changes
= 1;
12703 return got_error(GOT_ERR_CANCELLED
);
12708 switch (staged_status
) {
12709 case GOT_STATUS_ADD
:
12710 case GOT_STATUS_DELETE
:
12711 case GOT_STATUS_MODIFY
:
12712 *have_local_changes
= 1;
12713 return got_error(GOT_ERR_CANCELLED
);
12721 static const struct got_error
*
12722 cmd_histedit(int argc
, char *argv
[])
12724 const struct got_error
*error
= NULL
;
12725 struct got_worktree
*worktree
= NULL
;
12726 struct got_fileindex
*fileindex
= NULL
;
12727 struct got_repository
*repo
= NULL
;
12728 char *cwd
= NULL
, *committer
= NULL
, *gitconfig_path
= NULL
;
12729 struct got_reference
*branch
= NULL
;
12730 struct got_reference
*tmp_branch
= NULL
;
12731 struct got_object_id
*resume_commit_id
= NULL
;
12732 struct got_object_id
*base_commit_id
= NULL
;
12733 struct got_object_id
*head_commit_id
= NULL
;
12734 struct got_commit_object
*commit
= NULL
;
12735 int ch
, rebase_in_progress
= 0, merge_in_progress
= 0;
12736 struct got_update_progress_arg upa
;
12737 int edit_in_progress
= 0, abort_edit
= 0, continue_edit
= 0;
12738 int drop_only
= 0, edit_logmsg_only
= 0, fold_only
= 0, edit_only
= 0;
12739 int allow_conflict
= 0, list_backups
= 0, delete_backups
= 0;
12740 const char *edit_script_path
= NULL
;
12741 char *editor
= NULL
;
12742 struct got_object_id_queue commits
;
12743 struct got_pathlist_head merged_paths
;
12744 const struct got_object_id_queue
*parent_ids
;
12745 struct got_object_qid
*pid
;
12746 struct got_histedit_list histedit_cmds
;
12747 struct got_histedit_list_entry
*hle
;
12748 int *pack_fds
= NULL
;
12750 STAILQ_INIT(&commits
);
12751 TAILQ_INIT(&histedit_cmds
);
12752 TAILQ_INIT(&merged_paths
);
12753 memset(&upa
, 0, sizeof(upa
));
12756 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
12757 "unveil", NULL
) == -1)
12761 while ((ch
= getopt(argc
, argv
, "aCcdeF:flmX")) != -1) {
12767 allow_conflict
= 1;
12779 edit_script_path
= optarg
;
12788 edit_logmsg_only
= 1;
12791 delete_backups
= 1;
12802 if (abort_edit
&& allow_conflict
)
12803 option_conflict('a', 'C');
12804 if (abort_edit
&& continue_edit
)
12805 option_conflict('a', 'c');
12806 if (edit_script_path
&& allow_conflict
)
12807 option_conflict('F', 'C');
12808 if (edit_script_path
&& edit_logmsg_only
)
12809 option_conflict('F', 'm');
12810 if (abort_edit
&& edit_logmsg_only
)
12811 option_conflict('a', 'm');
12812 if (edit_logmsg_only
&& allow_conflict
)
12813 option_conflict('m', 'C');
12814 if (continue_edit
&& edit_logmsg_only
)
12815 option_conflict('c', 'm');
12816 if (abort_edit
&& fold_only
)
12817 option_conflict('a', 'f');
12818 if (fold_only
&& allow_conflict
)
12819 option_conflict('f', 'C');
12820 if (continue_edit
&& fold_only
)
12821 option_conflict('c', 'f');
12822 if (fold_only
&& edit_logmsg_only
)
12823 option_conflict('f', 'm');
12824 if (edit_script_path
&& fold_only
)
12825 option_conflict('F', 'f');
12826 if (abort_edit
&& edit_only
)
12827 option_conflict('a', 'e');
12828 if (continue_edit
&& edit_only
)
12829 option_conflict('c', 'e');
12830 if (edit_only
&& edit_logmsg_only
)
12831 option_conflict('e', 'm');
12832 if (edit_script_path
&& edit_only
)
12833 option_conflict('F', 'e');
12834 if (fold_only
&& edit_only
)
12835 option_conflict('f', 'e');
12836 if (drop_only
&& abort_edit
)
12837 option_conflict('d', 'a');
12838 if (drop_only
&& allow_conflict
)
12839 option_conflict('d', 'C');
12840 if (drop_only
&& continue_edit
)
12841 option_conflict('d', 'c');
12842 if (drop_only
&& edit_logmsg_only
)
12843 option_conflict('d', 'm');
12844 if (drop_only
&& edit_only
)
12845 option_conflict('d', 'e');
12846 if (drop_only
&& edit_script_path
)
12847 option_conflict('d', 'F');
12848 if (drop_only
&& fold_only
)
12849 option_conflict('d', 'f');
12850 if (list_backups
) {
12852 option_conflict('l', 'a');
12853 if (allow_conflict
)
12854 option_conflict('l', 'C');
12856 option_conflict('l', 'c');
12857 if (edit_script_path
)
12858 option_conflict('l', 'F');
12859 if (edit_logmsg_only
)
12860 option_conflict('l', 'm');
12862 option_conflict('l', 'd');
12864 option_conflict('l', 'f');
12866 option_conflict('l', 'e');
12867 if (delete_backups
)
12868 option_conflict('l', 'X');
12869 if (argc
!= 0 && argc
!= 1)
12871 } else if (delete_backups
) {
12873 option_conflict('X', 'a');
12874 if (allow_conflict
)
12875 option_conflict('X', 'C');
12877 option_conflict('X', 'c');
12879 option_conflict('X', 'd');
12880 if (edit_script_path
)
12881 option_conflict('X', 'F');
12882 if (edit_logmsg_only
)
12883 option_conflict('X', 'm');
12885 option_conflict('X', 'f');
12887 option_conflict('X', 'e');
12889 option_conflict('X', 'l');
12890 if (argc
!= 0 && argc
!= 1)
12892 } else if (allow_conflict
&& !continue_edit
)
12893 errx(1, "-C option requires -c");
12894 else if (argc
!= 0)
12897 cwd
= getcwd(NULL
, 0);
12899 error
= got_error_from_errno("getcwd");
12903 error
= got_repo_pack_fds_open(&pack_fds
);
12907 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_GOT_DIR
);
12909 if (list_backups
|| delete_backups
) {
12910 if (error
->code
!= GOT_ERR_NOT_WORKTREE
)
12913 if (error
->code
== GOT_ERR_NOT_WORKTREE
)
12914 error
= wrap_not_worktree_error(error
,
12920 if (list_backups
|| delete_backups
) {
12921 error
= got_repo_open(&repo
,
12922 worktree
? got_worktree_get_repo_path(worktree
) : cwd
,
12926 error
= apply_unveil(got_repo_get_path(repo
), 0,
12927 worktree
? got_worktree_get_root_path(worktree
) : NULL
);
12930 error
= process_backup_refs(
12931 GOT_WORKTREE_HISTEDIT_BACKUP_REF_PREFIX
,
12932 argc
== 1 ? argv
[0] : NULL
, delete_backups
, repo
);
12933 goto done
; /* nothing else to do */
12935 error
= get_gitconfig_path(&gitconfig_path
);
12938 error
= got_repo_open(&repo
,
12939 got_worktree_get_repo_path(worktree
), gitconfig_path
,
12943 error
= get_editor(&editor
);
12946 if (unveil(editor
, "x") != 0) {
12947 error
= got_error_from_errno2("unveil", editor
);
12950 if (edit_script_path
) {
12951 if (unveil(edit_script_path
, "r") != 0) {
12952 error
= got_error_from_errno2("unveil",
12957 error
= apply_unveil(got_repo_get_path(repo
), 0,
12958 got_worktree_get_root_path(worktree
));
12963 if (worktree
!= NULL
&& !list_backups
&& !delete_backups
) {
12964 error
= worktree_has_logmsg_ref("histedit", worktree
, repo
);
12969 error
= got_worktree_rebase_in_progress(&rebase_in_progress
, worktree
);
12972 if (rebase_in_progress
) {
12973 error
= got_error(GOT_ERR_REBASING
);
12977 error
= got_worktree_merge_in_progress(&merge_in_progress
, worktree
,
12981 if (merge_in_progress
) {
12982 error
= got_error(GOT_ERR_MERGE_BUSY
);
12986 error
= got_worktree_histedit_in_progress(&edit_in_progress
, worktree
);
12990 if (edit_in_progress
&& edit_logmsg_only
) {
12991 error
= got_error_msg(GOT_ERR_HISTEDIT_BUSY
,
12992 "histedit operation is in progress in this "
12993 "work tree and must be continued or aborted "
12994 "before the -m option can be used");
12997 if (edit_in_progress
&& drop_only
) {
12998 error
= got_error_msg(GOT_ERR_HISTEDIT_BUSY
,
12999 "histedit operation is in progress in this "
13000 "work tree and must be continued or aborted "
13001 "before the -d option can be used");
13004 if (edit_in_progress
&& fold_only
) {
13005 error
= got_error_msg(GOT_ERR_HISTEDIT_BUSY
,
13006 "histedit operation is in progress in this "
13007 "work tree and must be continued or aborted "
13008 "before the -f option can be used");
13011 if (edit_in_progress
&& edit_only
) {
13012 error
= got_error_msg(GOT_ERR_HISTEDIT_BUSY
,
13013 "histedit operation is in progress in this "
13014 "work tree and must be continued or aborted "
13015 "before the -e option can be used");
13019 if (edit_in_progress
&& abort_edit
) {
13020 error
= got_worktree_histedit_continue(&resume_commit_id
,
13021 &tmp_branch
, &branch
, &base_commit_id
, &fileindex
,
13025 printf("Switching work tree to %s\n",
13026 got_ref_get_symref_target(branch
));
13027 error
= got_worktree_histedit_abort(worktree
, fileindex
, repo
,
13028 branch
, base_commit_id
, abort_progress
, &upa
);
13031 printf("Histedit of %s aborted\n",
13032 got_ref_get_symref_target(branch
));
13033 print_merge_progress_stats(&upa
);
13034 goto done
; /* nothing else to do */
13035 } else if (abort_edit
) {
13036 error
= got_error(GOT_ERR_NOT_HISTEDIT
);
13040 error
= get_author(&committer
, repo
, worktree
);
13044 if (continue_edit
) {
13047 if (!edit_in_progress
) {
13048 error
= got_error(GOT_ERR_NOT_HISTEDIT
);
13052 error
= got_worktree_get_histedit_script_path(&path
, worktree
);
13056 error
= histedit_load_list(&histedit_cmds
, path
, repo
);
13061 error
= got_worktree_histedit_continue(&resume_commit_id
,
13062 &tmp_branch
, &branch
, &base_commit_id
, &fileindex
,
13067 error
= got_ref_resolve(&head_commit_id
, repo
, branch
);
13071 error
= got_object_open_as_commit(&commit
, repo
,
13075 parent_ids
= got_object_commit_get_parent_ids(commit
);
13076 pid
= STAILQ_FIRST(parent_ids
);
13078 error
= got_error(GOT_ERR_EMPTY_HISTEDIT
);
13081 error
= collect_commits(&commits
, head_commit_id
, &pid
->id
,
13082 base_commit_id
, got_worktree_get_path_prefix(worktree
),
13083 GOT_ERR_HISTEDIT_PATH
, repo
);
13084 got_object_commit_close(commit
);
13089 if (edit_in_progress
) {
13090 error
= got_error(GOT_ERR_HISTEDIT_BUSY
);
13094 error
= got_ref_open(&branch
, repo
,
13095 got_worktree_get_head_ref_name(worktree
), 0);
13099 if (strncmp(got_ref_get_name(branch
), "refs/heads/", 11) != 0) {
13100 error
= got_error_msg(GOT_ERR_COMMIT_BRANCH
,
13101 "will not edit commit history of a branch outside "
13102 "the \"refs/heads/\" reference namespace");
13106 error
= got_ref_resolve(&head_commit_id
, repo
, branch
);
13107 got_ref_close(branch
);
13112 error
= got_object_open_as_commit(&commit
, repo
,
13116 parent_ids
= got_object_commit_get_parent_ids(commit
);
13117 pid
= STAILQ_FIRST(parent_ids
);
13119 error
= got_error(GOT_ERR_EMPTY_HISTEDIT
);
13122 error
= collect_commits(&commits
, head_commit_id
, &pid
->id
,
13123 got_worktree_get_base_commit_id(worktree
),
13124 got_worktree_get_path_prefix(worktree
),
13125 GOT_ERR_HISTEDIT_PATH
, repo
);
13126 got_object_commit_close(commit
);
13131 if (STAILQ_EMPTY(&commits
)) {
13132 error
= got_error(GOT_ERR_EMPTY_HISTEDIT
);
13136 error
= got_worktree_histedit_prepare(&tmp_branch
, &branch
,
13137 &base_commit_id
, &fileindex
, worktree
, repo
);
13141 if (edit_script_path
) {
13142 error
= histedit_load_list(&histedit_cmds
,
13143 edit_script_path
, repo
);
13145 got_worktree_histedit_abort(worktree
, fileindex
,
13146 repo
, branch
, base_commit_id
,
13147 abort_progress
, &upa
);
13148 print_merge_progress_stats(&upa
);
13152 const char *branch_name
;
13153 branch_name
= got_ref_get_symref_target(branch
);
13154 if (strncmp(branch_name
, "refs/heads/", 11) == 0)
13156 error
= histedit_edit_script(&histedit_cmds
, &commits
,
13157 branch_name
, edit_logmsg_only
, fold_only
,
13158 drop_only
, edit_only
, editor
, repo
);
13160 got_worktree_histedit_abort(worktree
, fileindex
,
13161 repo
, branch
, base_commit_id
,
13162 abort_progress
, &upa
);
13163 print_merge_progress_stats(&upa
);
13169 error
= histedit_save_list(&histedit_cmds
, worktree
,
13172 got_worktree_histedit_abort(worktree
, fileindex
,
13173 repo
, branch
, base_commit_id
,
13174 abort_progress
, &upa
);
13175 print_merge_progress_stats(&upa
);
13181 error
= histedit_check_script(&histedit_cmds
, &commits
, repo
);
13185 TAILQ_FOREACH(hle
, &histedit_cmds
, entry
) {
13186 if (resume_commit_id
) {
13187 if (got_object_id_cmp(hle
->commit_id
,
13188 resume_commit_id
) != 0)
13191 resume_commit_id
= NULL
;
13192 if (hle
->cmd
->code
== GOT_HISTEDIT_DROP
||
13193 hle
->cmd
->code
== GOT_HISTEDIT_FOLD
) {
13194 error
= histedit_skip_commit(hle
, worktree
,
13199 struct got_pathlist_head paths
;
13200 int have_changes
= 0;
13202 TAILQ_INIT(&paths
);
13203 error
= got_pathlist_append(&paths
, "", NULL
);
13206 error
= got_worktree_status(worktree
, &paths
,
13207 repo
, 0, check_local_changes
, &have_changes
,
13208 check_cancelled
, NULL
);
13209 got_pathlist_free(&paths
,
13210 GOT_PATHLIST_FREE_NONE
);
13212 if (error
->code
!= GOT_ERR_CANCELLED
)
13214 if (sigint_received
|| sigpipe_received
)
13217 if (have_changes
) {
13218 error
= histedit_commit(NULL
, worktree
,
13219 fileindex
, tmp_branch
, hle
,
13220 committer
, allow_conflict
, editor
,
13225 error
= histedit_skip_commit(hle
,
13234 if (hle
->cmd
->code
== GOT_HISTEDIT_DROP
) {
13235 error
= histedit_skip_commit(hle
, worktree
, repo
);
13240 error
= got_object_open_as_commit(&commit
, repo
,
13244 parent_ids
= got_object_commit_get_parent_ids(commit
);
13245 pid
= STAILQ_FIRST(parent_ids
);
13247 error
= got_worktree_histedit_merge_files(&merged_paths
,
13248 worktree
, fileindex
, &pid
->id
, hle
->commit_id
, repo
,
13249 update_progress
, &upa
, check_cancelled
, NULL
);
13252 got_object_commit_close(commit
);
13255 print_merge_progress_stats(&upa
);
13256 if (upa
.conflicts
> 0 || upa
.missing
> 0 ||
13257 upa
.not_deleted
> 0 || upa
.unversioned
> 0) {
13258 if (upa
.conflicts
> 0) {
13259 error
= show_rebase_merge_conflict(
13260 hle
->commit_id
, repo
);
13264 got_pathlist_free(&merged_paths
, GOT_PATHLIST_FREE_PATH
);
13268 if (hle
->cmd
->code
== GOT_HISTEDIT_EDIT
) {
13270 error
= got_object_id_str(&id_str
, hle
->commit_id
);
13273 printf("Stopping histedit for amending commit %s\n",
13276 got_pathlist_free(&merged_paths
, GOT_PATHLIST_FREE_PATH
);
13277 error
= got_worktree_histedit_postpone(worktree
,
13280 } else if (hle
->cmd
->code
== GOT_HISTEDIT_FOLD
) {
13281 error
= histedit_skip_commit(hle
, worktree
, repo
);
13285 } else if (hle
->cmd
->code
== GOT_HISTEDIT_MESG
) {
13286 error
= histedit_edit_logmsg(hle
, editor
, repo
);
13291 error
= histedit_commit(&merged_paths
, worktree
, fileindex
,
13292 tmp_branch
, hle
, committer
, allow_conflict
, editor
, repo
);
13293 got_pathlist_free(&merged_paths
, GOT_PATHLIST_FREE_PATH
);
13298 if (upa
.conflicts
> 0 || upa
.missing
> 0 ||
13299 upa
.not_deleted
> 0 || upa
.unversioned
> 0) {
13300 error
= got_worktree_histedit_postpone(worktree
, fileindex
);
13303 if (upa
.conflicts
> 0 && upa
.missing
== 0 &&
13304 upa
.not_deleted
== 0 && upa
.unversioned
== 0) {
13305 error
= got_error_msg(GOT_ERR_CONFLICTS
,
13306 "conflicts must be resolved before histedit "
13308 } else if (upa
.conflicts
> 0) {
13309 error
= got_error_msg(GOT_ERR_CONFLICTS
,
13310 "conflicts must be resolved before histedit "
13311 "can continue; changes destined for some "
13312 "files were not yet merged and should be "
13313 "merged manually if required before the "
13314 "histedit operation is continued");
13316 error
= got_error_msg(GOT_ERR_CONFLICTS
,
13317 "changes destined for some files were not "
13318 "yet merged and should be merged manually "
13319 "if required before the histedit operation "
13323 error
= histedit_complete(worktree
, fileindex
, tmp_branch
,
13329 free(gitconfig_path
);
13330 got_object_id_queue_free(&commits
);
13331 histedit_free_list(&histedit_cmds
);
13332 free(head_commit_id
);
13333 free(base_commit_id
);
13334 free(resume_commit_id
);
13336 got_object_commit_close(commit
);
13338 got_ref_close(branch
);
13340 got_ref_close(tmp_branch
);
13342 got_worktree_close(worktree
);
13344 const struct got_error
*close_err
= got_repo_close(repo
);
13349 const struct got_error
*pack_err
=
13350 got_repo_pack_fds_close(pack_fds
);
13358 usage_integrate(void)
13360 fprintf(stderr
, "usage: %s integrate branch\n", getprogname());
13364 static const struct got_error
*
13365 cmd_integrate(int argc
, char *argv
[])
13367 const struct got_error
*error
= NULL
;
13368 struct got_repository
*repo
= NULL
;
13369 struct got_worktree
*worktree
= NULL
;
13370 char *cwd
= NULL
, *refname
= NULL
, *base_refname
= NULL
;
13371 const char *branch_arg
= NULL
;
13372 struct got_reference
*branch_ref
= NULL
, *base_branch_ref
= NULL
;
13373 struct got_fileindex
*fileindex
= NULL
;
13374 struct got_object_id
*commit_id
= NULL
, *base_commit_id
= NULL
;
13376 struct got_update_progress_arg upa
;
13377 int *pack_fds
= NULL
;
13380 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
13381 "unveil", NULL
) == -1)
13385 while ((ch
= getopt(argc
, argv
, "")) != -1) {
13398 branch_arg
= argv
[0];
13400 cwd
= getcwd(NULL
, 0);
13402 error
= got_error_from_errno("getcwd");
13406 error
= got_repo_pack_fds_open(&pack_fds
);
13410 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_GOT_DIR
);
13412 if (error
->code
== GOT_ERR_NOT_WORKTREE
)
13413 error
= wrap_not_worktree_error(error
, "integrate",
13418 error
= check_rebase_or_histedit_in_progress(worktree
);
13422 error
= got_repo_open(&repo
, got_worktree_get_repo_path(worktree
),
13427 error
= apply_unveil(got_repo_get_path(repo
), 0,
13428 got_worktree_get_root_path(worktree
));
13432 error
= check_merge_in_progress(worktree
, repo
);
13436 if (asprintf(&refname
, "refs/heads/%s", branch_arg
) == -1) {
13437 error
= got_error_from_errno("asprintf");
13441 error
= got_worktree_integrate_prepare(&fileindex
, &branch_ref
,
13442 &base_branch_ref
, worktree
, refname
, repo
);
13446 refname
= strdup(got_ref_get_name(branch_ref
));
13447 if (refname
== NULL
) {
13448 error
= got_error_from_errno("strdup");
13449 got_worktree_integrate_abort(worktree
, fileindex
, repo
,
13450 branch_ref
, base_branch_ref
);
13453 base_refname
= strdup(got_ref_get_name(base_branch_ref
));
13454 if (base_refname
== NULL
) {
13455 error
= got_error_from_errno("strdup");
13456 got_worktree_integrate_abort(worktree
, fileindex
, repo
,
13457 branch_ref
, base_branch_ref
);
13460 if (strncmp(base_refname
, "refs/heads/", 11) != 0) {
13461 error
= got_error(GOT_ERR_INTEGRATE_BRANCH
);
13462 got_worktree_integrate_abort(worktree
, fileindex
, repo
,
13463 branch_ref
, base_branch_ref
);
13467 error
= got_ref_resolve(&commit_id
, repo
, branch_ref
);
13471 error
= got_ref_resolve(&base_commit_id
, repo
, base_branch_ref
);
13475 if (got_object_id_cmp(commit_id
, base_commit_id
) == 0) {
13476 error
= got_error_msg(GOT_ERR_SAME_BRANCH
,
13477 "specified branch has already been integrated");
13478 got_worktree_integrate_abort(worktree
, fileindex
, repo
,
13479 branch_ref
, base_branch_ref
);
13483 error
= check_linear_ancestry(commit_id
, base_commit_id
, 1, repo
);
13485 if (error
->code
== GOT_ERR_ANCESTRY
)
13486 error
= got_error(GOT_ERR_REBASE_REQUIRED
);
13487 got_worktree_integrate_abort(worktree
, fileindex
, repo
,
13488 branch_ref
, base_branch_ref
);
13492 memset(&upa
, 0, sizeof(upa
));
13493 error
= got_worktree_integrate_continue(worktree
, fileindex
, repo
,
13494 branch_ref
, base_branch_ref
, update_progress
, &upa
,
13495 check_cancelled
, NULL
);
13499 printf("Integrated %s into %s\n", refname
, base_refname
);
13500 print_update_progress_stats(&upa
);
13503 const struct got_error
*close_err
= got_repo_close(repo
);
13508 got_worktree_close(worktree
);
13510 const struct got_error
*pack_err
=
13511 got_repo_pack_fds_close(pack_fds
);
13516 free(base_commit_id
);
13519 free(base_refname
);
13526 fprintf(stderr
, "usage: %s merge [-aCcn] [branch]\n", getprogname());
13530 static const struct got_error
*
13531 cmd_merge(int argc
, char *argv
[])
13533 const struct got_error
*error
= NULL
;
13534 struct got_worktree
*worktree
= NULL
;
13535 struct got_repository
*repo
= NULL
;
13536 struct got_fileindex
*fileindex
= NULL
;
13537 char *cwd
= NULL
, *id_str
= NULL
, *author
= NULL
;
13538 char *gitconfig_path
= NULL
;
13539 struct got_reference
*branch
= NULL
, *wt_branch
= NULL
;
13540 struct got_object_id
*branch_tip
= NULL
, *yca_id
= NULL
;
13541 struct got_object_id
*wt_branch_tip
= NULL
;
13542 int ch
, merge_in_progress
= 0, abort_merge
= 0, continue_merge
= 0;
13543 int allow_conflict
= 0, prefer_fast_forward
= 1, interrupt_merge
= 0;
13544 struct got_update_progress_arg upa
;
13545 struct got_object_id
*merge_commit_id
= NULL
;
13546 char *branch_name
= NULL
;
13547 int *pack_fds
= NULL
;
13549 memset(&upa
, 0, sizeof(upa
));
13552 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
13553 "unveil", NULL
) == -1)
13557 while ((ch
= getopt(argc
, argv
, "aCcMn")) != -1) {
13563 allow_conflict
= 1;
13566 continue_merge
= 1;
13569 prefer_fast_forward
= 0;
13572 interrupt_merge
= 1;
13584 if (continue_merge
)
13585 option_conflict('a', 'c');
13586 if (!prefer_fast_forward
)
13587 option_conflict('a', 'M');
13588 if (interrupt_merge
)
13589 option_conflict('a', 'n');
13590 } else if (continue_merge
) {
13591 if (!prefer_fast_forward
)
13592 option_conflict('c', 'M');
13593 if (interrupt_merge
)
13594 option_conflict('c', 'n');
13596 if (allow_conflict
) {
13597 if (!continue_merge
)
13598 errx(1, "-C option requires -c");
13600 if (abort_merge
|| continue_merge
) {
13603 } else if (argc
!= 1)
13606 cwd
= getcwd(NULL
, 0);
13608 error
= got_error_from_errno("getcwd");
13612 error
= got_repo_pack_fds_open(&pack_fds
);
13616 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_GOT_DIR
);
13618 if (error
->code
== GOT_ERR_NOT_WORKTREE
)
13619 error
= wrap_not_worktree_error(error
,
13624 error
= get_gitconfig_path(&gitconfig_path
);
13627 error
= got_repo_open(&repo
,
13628 worktree
? got_worktree_get_repo_path(worktree
) : cwd
,
13629 gitconfig_path
, pack_fds
);
13633 if (worktree
!= NULL
) {
13634 error
= worktree_has_logmsg_ref("merge", worktree
, repo
);
13639 error
= apply_unveil(got_repo_get_path(repo
), 0,
13640 worktree
? got_worktree_get_root_path(worktree
) : NULL
);
13644 error
= check_rebase_or_histedit_in_progress(worktree
);
13648 error
= got_worktree_merge_in_progress(&merge_in_progress
, worktree
,
13653 if (merge_in_progress
&& !(abort_merge
|| continue_merge
)) {
13654 error
= got_error(GOT_ERR_MERGE_BUSY
);
13658 if (!merge_in_progress
&& (abort_merge
|| continue_merge
)) {
13659 error
= got_error(GOT_ERR_NOT_MERGING
);
13664 error
= got_worktree_merge_continue(&branch_name
,
13665 &branch_tip
, &fileindex
, worktree
, repo
);
13668 error
= got_worktree_merge_abort(worktree
, fileindex
, repo
,
13669 abort_progress
, &upa
);
13672 printf("Merge of %s aborted\n", branch_name
);
13673 goto done
; /* nothing else to do */
13676 if (strncmp(got_worktree_get_head_ref_name(worktree
),
13677 "refs/heads/", 11) != 0) {
13678 error
= got_error_fmt(GOT_ERR_COMMIT_BRANCH
,
13679 "work tree's current branch %s is outside the "
13680 "\"refs/heads/\" reference namespace; "
13681 "update -b required",
13682 got_worktree_get_head_ref_name(worktree
));
13686 error
= get_author(&author
, repo
, worktree
);
13690 error
= got_ref_open(&wt_branch
, repo
,
13691 got_worktree_get_head_ref_name(worktree
), 0);
13694 error
= got_ref_resolve(&wt_branch_tip
, repo
, wt_branch
);
13698 if (continue_merge
) {
13699 struct got_object_id
*base_commit_id
;
13700 base_commit_id
= got_worktree_get_base_commit_id(worktree
);
13701 if (got_object_id_cmp(wt_branch_tip
, base_commit_id
) != 0) {
13702 error
= got_error(GOT_ERR_MERGE_COMMIT_OUT_OF_DATE
);
13705 error
= got_worktree_merge_continue(&branch_name
,
13706 &branch_tip
, &fileindex
, worktree
, repo
);
13710 error
= got_ref_open(&branch
, repo
, argv
[0], 0);
13713 branch_name
= strdup(got_ref_get_name(branch
));
13714 if (branch_name
== NULL
) {
13715 error
= got_error_from_errno("strdup");
13718 error
= got_ref_resolve(&branch_tip
, repo
, branch
);
13723 error
= got_commit_graph_find_youngest_common_ancestor(&yca_id
,
13724 wt_branch_tip
, branch_tip
, 0, 0, repo
,
13725 check_cancelled
, NULL
);
13726 if (error
&& error
->code
!= GOT_ERR_ANCESTRY
)
13729 if (!continue_merge
) {
13730 error
= check_path_prefix(wt_branch_tip
, branch_tip
,
13731 got_worktree_get_path_prefix(worktree
),
13732 GOT_ERR_MERGE_PATH
, repo
);
13735 error
= got_worktree_merge_prepare(&fileindex
, worktree
, repo
);
13738 if (prefer_fast_forward
&& yca_id
&&
13739 got_object_id_cmp(wt_branch_tip
, yca_id
) == 0) {
13740 struct got_pathlist_head paths
;
13741 if (interrupt_merge
) {
13742 error
= got_error_fmt(GOT_ERR_BAD_OPTION
,
13743 "there are no changes to merge since %s "
13744 "is already based on %s; merge cannot be "
13745 "interrupted for amending; -n",
13746 branch_name
, got_ref_get_name(wt_branch
));
13749 printf("Forwarding %s to %s\n",
13750 got_ref_get_name(wt_branch
), branch_name
);
13751 error
= got_ref_change_ref(wt_branch
, branch_tip
);
13754 error
= got_ref_write(wt_branch
, repo
);
13757 error
= got_worktree_set_base_commit_id(worktree
, repo
,
13761 TAILQ_INIT(&paths
);
13762 error
= got_pathlist_append(&paths
, "", NULL
);
13765 error
= got_worktree_checkout_files(worktree
,
13766 &paths
, repo
, update_progress
, &upa
,
13767 check_cancelled
, NULL
);
13768 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_NONE
);
13771 if (upa
.did_something
) {
13773 error
= got_object_id_str(&id_str
, branch_tip
);
13776 printf("Updated to commit %s\n", id_str
);
13779 printf("Already up-to-date\n");
13780 print_update_progress_stats(&upa
);
13783 error
= got_worktree_merge_write_refs(worktree
, branch
, repo
);
13787 error
= got_worktree_merge_branch(worktree
, fileindex
,
13788 yca_id
, branch_tip
, repo
, update_progress
, &upa
,
13789 check_cancelled
, NULL
);
13792 print_merge_progress_stats(&upa
);
13793 if (!upa
.did_something
) {
13794 error
= got_worktree_merge_abort(worktree
, fileindex
,
13795 repo
, abort_progress
, &upa
);
13798 printf("Already up-to-date\n");
13803 if (interrupt_merge
) {
13804 error
= got_worktree_merge_postpone(worktree
, fileindex
);
13807 printf("Merge of %s interrupted on request\n", branch_name
);
13808 } else if (upa
.conflicts
> 0 || upa
.missing
> 0 ||
13809 upa
.not_deleted
> 0 || upa
.unversioned
> 0) {
13810 error
= got_worktree_merge_postpone(worktree
, fileindex
);
13813 if (upa
.conflicts
> 0 && upa
.missing
== 0 &&
13814 upa
.not_deleted
== 0 && upa
.unversioned
== 0) {
13815 error
= got_error_msg(GOT_ERR_CONFLICTS
,
13816 "conflicts must be resolved before merging "
13818 } else if (upa
.conflicts
> 0) {
13819 error
= got_error_msg(GOT_ERR_CONFLICTS
,
13820 "conflicts must be resolved before merging "
13821 "can continue; changes destined for some "
13822 "files were not yet merged and "
13823 "should be merged manually if required before the "
13824 "merge operation is continued");
13826 error
= got_error_msg(GOT_ERR_CONFLICTS
,
13827 "changes destined for some "
13828 "files were not yet merged and should be "
13829 "merged manually if required before the "
13830 "merge operation is continued");
13834 error
= got_worktree_merge_commit(&merge_commit_id
, worktree
,
13835 fileindex
, author
, NULL
, 1, branch_tip
, branch_name
,
13836 allow_conflict
, repo
, continue_merge
? print_status
: NULL
,
13840 error
= got_worktree_merge_complete(worktree
, fileindex
, repo
);
13843 error
= got_object_id_str(&id_str
, merge_commit_id
);
13846 printf("Merged %s into %s: %s\n", branch_name
,
13847 got_worktree_get_head_ref_name(worktree
),
13853 free(gitconfig_path
);
13855 free(merge_commit_id
);
13861 got_ref_close(branch
);
13863 got_ref_close(wt_branch
);
13865 got_worktree_close(worktree
);
13867 const struct got_error
*close_err
= got_repo_close(repo
);
13872 const struct got_error
*pack_err
=
13873 got_repo_pack_fds_close(pack_fds
);
13883 fprintf(stderr
, "usage: %s stage [-lpS] [-F response-script] "
13884 "[path ...]\n", getprogname());
13888 static const struct got_error
*
13889 print_stage(void *arg
, unsigned char status
, unsigned char staged_status
,
13890 const char *path
, struct got_object_id
*blob_id
,
13891 struct got_object_id
*staged_blob_id
, struct got_object_id
*commit_id
,
13892 int dirfd
, const char *de_name
)
13894 const struct got_error
*err
= NULL
;
13895 char *id_str
= NULL
;
13897 if (staged_status
!= GOT_STATUS_ADD
&&
13898 staged_status
!= GOT_STATUS_MODIFY
&&
13899 staged_status
!= GOT_STATUS_DELETE
)
13902 if (staged_status
== GOT_STATUS_ADD
||
13903 staged_status
== GOT_STATUS_MODIFY
)
13904 err
= got_object_id_str(&id_str
, staged_blob_id
);
13906 err
= got_object_id_str(&id_str
, blob_id
);
13910 printf("%s %c %s\n", id_str
, staged_status
, path
);
13915 static const struct got_error
*
13916 cmd_stage(int argc
, char *argv
[])
13918 const struct got_error
*error
= NULL
;
13919 struct got_repository
*repo
= NULL
;
13920 struct got_worktree
*worktree
= NULL
;
13922 struct got_pathlist_head paths
;
13923 int ch
, list_stage
= 0, pflag
= 0, allow_bad_symlinks
= 0;
13924 FILE *patch_script_file
= NULL
;
13925 const char *patch_script_path
= NULL
;
13926 struct choose_patch_arg cpa
;
13927 int *pack_fds
= NULL
;
13929 TAILQ_INIT(&paths
);
13932 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
13933 "unveil", NULL
) == -1)
13937 while ((ch
= getopt(argc
, argv
, "F:lpS")) != -1) {
13940 patch_script_path
= optarg
;
13949 allow_bad_symlinks
= 1;
13960 if (list_stage
&& (pflag
|| patch_script_path
))
13961 errx(1, "-l option cannot be used with other options");
13962 if (patch_script_path
&& !pflag
)
13963 errx(1, "-F option can only be used together with -p option");
13965 cwd
= getcwd(NULL
, 0);
13967 error
= got_error_from_errno("getcwd");
13971 error
= got_repo_pack_fds_open(&pack_fds
);
13975 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_GOT_DIR
);
13977 if (error
->code
== GOT_ERR_NOT_WORKTREE
)
13978 error
= wrap_not_worktree_error(error
, "stage", cwd
);
13982 error
= got_repo_open(&repo
, got_worktree_get_repo_path(worktree
),
13987 if (patch_script_path
) {
13988 patch_script_file
= fopen(patch_script_path
, "re");
13989 if (patch_script_file
== NULL
) {
13990 error
= got_error_from_errno2("fopen",
13991 patch_script_path
);
13995 error
= apply_unveil(got_repo_get_path(repo
), 0,
13996 got_worktree_get_root_path(worktree
));
14000 error
= check_merge_in_progress(worktree
, repo
);
14004 error
= get_worktree_paths_from_argv(&paths
, argc
, argv
, worktree
);
14009 error
= got_worktree_status(worktree
, &paths
, repo
, 0,
14010 print_stage
, NULL
, check_cancelled
, NULL
);
14012 cpa
.patch_script_file
= patch_script_file
;
14013 cpa
.action
= "stage";
14014 error
= got_worktree_stage(worktree
, &paths
,
14015 pflag
? NULL
: print_status
, NULL
,
14016 pflag
? choose_patch
: NULL
, &cpa
,
14017 allow_bad_symlinks
, repo
);
14020 if (patch_script_file
&& fclose(patch_script_file
) == EOF
&&
14022 error
= got_error_from_errno2("fclose", patch_script_path
);
14024 const struct got_error
*close_err
= got_repo_close(repo
);
14029 got_worktree_close(worktree
);
14031 const struct got_error
*pack_err
=
14032 got_repo_pack_fds_close(pack_fds
);
14036 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_PATH
);
14042 usage_unstage(void)
14044 fprintf(stderr
, "usage: %s unstage [-p] [-F response-script] "
14045 "[path ...]\n", getprogname());
14050 static const struct got_error
*
14051 cmd_unstage(int argc
, char *argv
[])
14053 const struct got_error
*error
= NULL
;
14054 struct got_repository
*repo
= NULL
;
14055 struct got_worktree
*worktree
= NULL
;
14057 struct got_pathlist_head paths
;
14059 struct got_update_progress_arg upa
;
14060 FILE *patch_script_file
= NULL
;
14061 const char *patch_script_path
= NULL
;
14062 struct choose_patch_arg cpa
;
14063 int *pack_fds
= NULL
;
14065 TAILQ_INIT(&paths
);
14068 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
14069 "unveil", NULL
) == -1)
14073 while ((ch
= getopt(argc
, argv
, "F:p")) != -1) {
14076 patch_script_path
= optarg
;
14090 if (patch_script_path
&& !pflag
)
14091 errx(1, "-F option can only be used together with -p option");
14093 cwd
= getcwd(NULL
, 0);
14095 error
= got_error_from_errno("getcwd");
14099 error
= got_repo_pack_fds_open(&pack_fds
);
14103 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_GOT_DIR
);
14105 if (error
->code
== GOT_ERR_NOT_WORKTREE
)
14106 error
= wrap_not_worktree_error(error
, "unstage", cwd
);
14110 error
= got_repo_open(&repo
, got_worktree_get_repo_path(worktree
),
14115 if (patch_script_path
) {
14116 patch_script_file
= fopen(patch_script_path
, "re");
14117 if (patch_script_file
== NULL
) {
14118 error
= got_error_from_errno2("fopen",
14119 patch_script_path
);
14124 error
= apply_unveil(got_repo_get_path(repo
), 0,
14125 got_worktree_get_root_path(worktree
));
14129 error
= get_worktree_paths_from_argv(&paths
, argc
, argv
, worktree
);
14133 cpa
.patch_script_file
= patch_script_file
;
14134 cpa
.action
= "unstage";
14135 memset(&upa
, 0, sizeof(upa
));
14136 error
= got_worktree_unstage(worktree
, &paths
, update_progress
,
14137 &upa
, pflag
? choose_patch
: NULL
, &cpa
, repo
);
14139 print_merge_progress_stats(&upa
);
14141 if (patch_script_file
&& fclose(patch_script_file
) == EOF
&&
14143 error
= got_error_from_errno2("fclose", patch_script_path
);
14145 const struct got_error
*close_err
= got_repo_close(repo
);
14150 got_worktree_close(worktree
);
14152 const struct got_error
*pack_err
=
14153 got_repo_pack_fds_close(pack_fds
);
14157 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_PATH
);
14165 fprintf(stderr
, "usage: %s cat [-P] [-c commit] [-r repository-path] "
14166 "arg ...\n", getprogname());
14170 static const struct got_error
*
14171 cat_blob(struct got_object_id
*id
, struct got_repository
*repo
, FILE *outfile
)
14173 const struct got_error
*err
;
14174 struct got_blob_object
*blob
;
14177 fd
= got_opentempfd();
14179 return got_error_from_errno("got_opentempfd");
14181 err
= got_object_open_as_blob(&blob
, repo
, id
, 8192, fd
);
14185 err
= got_object_blob_dump_to_file(NULL
, NULL
, NULL
, outfile
, blob
);
14187 if (fd
!= -1 && close(fd
) == -1 && err
== NULL
)
14188 err
= got_error_from_errno("close");
14190 got_object_blob_close(blob
);
14194 static const struct got_error
*
14195 cat_tree(struct got_object_id
*id
, struct got_repository
*repo
, FILE *outfile
)
14197 const struct got_error
*err
;
14198 struct got_tree_object
*tree
;
14201 err
= got_object_open_as_tree(&tree
, repo
, id
);
14205 nentries
= got_object_tree_get_nentries(tree
);
14206 for (i
= 0; i
< nentries
; i
++) {
14207 struct got_tree_entry
*te
;
14209 if (sigint_received
|| sigpipe_received
)
14211 te
= got_object_tree_get_entry(tree
, i
);
14212 err
= got_object_id_str(&id_str
, got_tree_entry_get_id(te
));
14215 fprintf(outfile
, "%s %.7o %s\n", id_str
,
14216 got_tree_entry_get_mode(te
),
14217 got_tree_entry_get_name(te
));
14221 got_object_tree_close(tree
);
14225 static const struct got_error
*
14226 cat_commit(struct got_object_id
*id
, struct got_repository
*repo
, FILE *outfile
)
14228 const struct got_error
*err
;
14229 struct got_commit_object
*commit
;
14230 const struct got_object_id_queue
*parent_ids
;
14231 struct got_object_qid
*pid
;
14232 char *id_str
= NULL
;
14233 const char *logmsg
= NULL
;
14236 err
= got_object_open_as_commit(&commit
, repo
, id
);
14240 err
= got_object_id_str(&id_str
, got_object_commit_get_tree_id(commit
));
14244 fprintf(outfile
, "%s%s\n", GOT_COMMIT_LABEL_TREE
, id_str
);
14245 parent_ids
= got_object_commit_get_parent_ids(commit
);
14246 fprintf(outfile
, "numparents %d\n",
14247 got_object_commit_get_nparents(commit
));
14248 STAILQ_FOREACH(pid
, parent_ids
, entry
) {
14250 err
= got_object_id_str(&pid_str
, &pid
->id
);
14253 fprintf(outfile
, "%s%s\n", GOT_COMMIT_LABEL_PARENT
, pid_str
);
14256 got_date_format_gmtoff(gmtoff
, sizeof(gmtoff
),
14257 got_object_commit_get_author_gmtoff(commit
));
14258 fprintf(outfile
, "%s%s %lld %s\n", GOT_COMMIT_LABEL_AUTHOR
,
14259 got_object_commit_get_author(commit
),
14260 (long long)got_object_commit_get_author_time(commit
),
14263 got_date_format_gmtoff(gmtoff
, sizeof(gmtoff
),
14264 got_object_commit_get_committer_gmtoff(commit
));
14265 fprintf(outfile
, "%s%s %lld %s\n", GOT_COMMIT_LABEL_COMMITTER
,
14266 got_object_commit_get_committer(commit
),
14267 (long long)got_object_commit_get_committer_time(commit
),
14270 logmsg
= got_object_commit_get_logmsg_raw(commit
);
14271 fprintf(outfile
, "messagelen %zd\n", strlen(logmsg
));
14272 fprintf(outfile
, "%s", logmsg
);
14275 got_object_commit_close(commit
);
14279 static const struct got_error
*
14280 cat_tag(struct got_object_id
*id
, struct got_repository
*repo
, FILE *outfile
)
14282 const struct got_error
*err
;
14283 struct got_tag_object
*tag
;
14284 char *id_str
= NULL
;
14285 const char *tagmsg
= NULL
;
14288 err
= got_object_open_as_tag(&tag
, repo
, id
);
14292 err
= got_object_id_str(&id_str
, got_object_tag_get_object_id(tag
));
14296 fprintf(outfile
, "%s%s\n", GOT_TAG_LABEL_OBJECT
, id_str
);
14298 switch (got_object_tag_get_object_type(tag
)) {
14299 case GOT_OBJ_TYPE_BLOB
:
14300 fprintf(outfile
, "%s%s\n", GOT_TAG_LABEL_TYPE
,
14301 GOT_OBJ_LABEL_BLOB
);
14303 case GOT_OBJ_TYPE_TREE
:
14304 fprintf(outfile
, "%s%s\n", GOT_TAG_LABEL_TYPE
,
14305 GOT_OBJ_LABEL_TREE
);
14307 case GOT_OBJ_TYPE_COMMIT
:
14308 fprintf(outfile
, "%s%s\n", GOT_TAG_LABEL_TYPE
,
14309 GOT_OBJ_LABEL_COMMIT
);
14311 case GOT_OBJ_TYPE_TAG
:
14312 fprintf(outfile
, "%s%s\n", GOT_TAG_LABEL_TYPE
,
14313 GOT_OBJ_LABEL_TAG
);
14319 fprintf(outfile
, "%s%s\n", GOT_TAG_LABEL_TAG
,
14320 got_object_tag_get_name(tag
));
14322 got_date_format_gmtoff(gmtoff
, sizeof(gmtoff
),
14323 got_object_tag_get_tagger_gmtoff(tag
));
14324 fprintf(outfile
, "%s%s %lld %s\n", GOT_TAG_LABEL_TAGGER
,
14325 got_object_tag_get_tagger(tag
),
14326 (long long)got_object_tag_get_tagger_time(tag
),
14329 tagmsg
= got_object_tag_get_message(tag
);
14330 fprintf(outfile
, "messagelen %zd\n", strlen(tagmsg
));
14331 fprintf(outfile
, "%s", tagmsg
);
14334 got_object_tag_close(tag
);
14338 static const struct got_error
*
14339 cmd_cat(int argc
, char *argv
[])
14341 const struct got_error
*error
;
14342 struct got_repository
*repo
= NULL
;
14343 struct got_worktree
*worktree
= NULL
;
14344 char *cwd
= NULL
, *repo_path
= NULL
, *label
= NULL
;
14345 char *keyword_idstr
= NULL
;
14346 const char *commit_id_str
= NULL
;
14347 struct got_object_id
*id
= NULL
, *commit_id
= NULL
;
14348 struct got_commit_object
*commit
= NULL
;
14349 int ch
, obj_type
, i
, force_path
= 0;
14350 struct got_reflist_head refs
;
14351 int *pack_fds
= NULL
;
14356 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
14361 while ((ch
= getopt(argc
, argv
, "c:Pr:")) != -1) {
14364 commit_id_str
= optarg
;
14370 repo_path
= realpath(optarg
, NULL
);
14371 if (repo_path
== NULL
)
14372 return got_error_from_errno2("realpath",
14374 got_path_strip_trailing_slashes(repo_path
);
14385 cwd
= getcwd(NULL
, 0);
14387 error
= got_error_from_errno("getcwd");
14391 error
= got_repo_pack_fds_open(&pack_fds
);
14395 if (repo_path
== NULL
) {
14396 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_GOT_DIR
);
14397 if (error
&& error
->code
!= GOT_ERR_NOT_WORKTREE
)
14400 repo_path
= strdup(
14401 got_worktree_get_repo_path(worktree
));
14402 if (repo_path
== NULL
) {
14403 error
= got_error_from_errno("strdup");
14407 if (commit_id_str
== NULL
) {
14408 /* Release work tree lock. */
14409 got_worktree_close(worktree
);
14415 if (repo_path
== NULL
) {
14416 repo_path
= strdup(cwd
);
14417 if (repo_path
== NULL
)
14418 return got_error_from_errno("strdup");
14421 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
14426 error
= apply_unveil(got_repo_get_path(repo
), 1, NULL
);
14430 error
= got_ref_list(&refs
, repo
, NULL
, got_ref_cmp_by_name
, NULL
);
14434 if (commit_id_str
!= NULL
) {
14435 error
= got_keyword_to_idstr(&keyword_idstr
, commit_id_str
,
14439 if (keyword_idstr
!= NULL
)
14440 commit_id_str
= keyword_idstr
;
14441 if (worktree
!= NULL
) {
14442 got_worktree_close(worktree
);
14446 commit_id_str
= GOT_REF_HEAD
;
14447 error
= got_repo_match_object_id(&commit_id
, NULL
,
14448 commit_id_str
, GOT_OBJ_TYPE_COMMIT
, &refs
, repo
);
14452 error
= got_object_open_as_commit(&commit
, repo
, commit_id
);
14456 for (i
= 0; i
< argc
; i
++) {
14458 error
= got_object_id_by_path(&id
, repo
, commit
,
14463 error
= got_repo_match_object_id(&id
, &label
, argv
[i
],
14464 GOT_OBJ_TYPE_ANY
, NULL
/* do not resolve tags */,
14467 if (error
->code
!= GOT_ERR_BAD_OBJ_ID_STR
&&
14468 error
->code
!= GOT_ERR_NOT_REF
)
14470 error
= got_object_id_by_path(&id
, repo
,
14477 error
= got_object_get_type(&obj_type
, repo
, id
);
14481 switch (obj_type
) {
14482 case GOT_OBJ_TYPE_BLOB
:
14483 error
= cat_blob(id
, repo
, stdout
);
14485 case GOT_OBJ_TYPE_TREE
:
14486 error
= cat_tree(id
, repo
, stdout
);
14488 case GOT_OBJ_TYPE_COMMIT
:
14489 error
= cat_commit(id
, repo
, stdout
);
14491 case GOT_OBJ_TYPE_TAG
:
14492 error
= cat_tag(id
, repo
, stdout
);
14495 error
= got_error(GOT_ERR_OBJ_TYPE
);
14510 free(keyword_idstr
);
14512 got_object_commit_close(commit
);
14514 got_worktree_close(worktree
);
14516 const struct got_error
*close_err
= got_repo_close(repo
);
14521 const struct got_error
*pack_err
=
14522 got_repo_pack_fds_close(pack_fds
);
14527 got_ref_list_free(&refs
);
14534 fprintf(stderr
, "usage: %s info [path ...]\n",
14539 static const struct got_error
*
14540 print_path_info(void *arg
, const char *path
, mode_t mode
, time_t mtime
,
14541 struct got_object_id
*blob_id
, struct got_object_id
*staged_blob_id
,
14542 struct got_object_id
*commit_id
)
14544 const struct got_error
*err
= NULL
;
14545 char *id_str
= NULL
;
14547 struct tm mytm
, *tm
;
14548 struct got_pathlist_head
*paths
= arg
;
14549 struct got_pathlist_entry
*pe
;
14552 * Clear error indication from any of the path arguments which
14553 * would cause this file index entry to be displayed.
14555 TAILQ_FOREACH(pe
, paths
, entry
) {
14556 if (got_path_cmp(path
, pe
->path
, strlen(path
),
14557 pe
->path_len
) == 0 ||
14558 got_path_is_child(path
, pe
->path
, pe
->path_len
))
14559 pe
->data
= NULL
; /* no error */
14562 printf(GOT_COMMIT_SEP_STR
);
14564 printf("symlink: %s\n", path
);
14565 else if (S_ISREG(mode
)) {
14566 printf("file: %s\n", path
);
14567 printf("mode: %o\n", mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
));
14568 } else if (S_ISDIR(mode
))
14569 printf("directory: %s\n", path
);
14571 printf("something: %s\n", path
);
14573 tm
= localtime_r(&mtime
, &mytm
);
14576 if (strftime(datebuf
, sizeof(datebuf
), "%c %Z", tm
) == 0)
14577 return got_error(GOT_ERR_NO_SPACE
);
14578 printf("timestamp: %s\n", datebuf
);
14581 err
= got_object_id_str(&id_str
, blob_id
);
14584 printf("based on blob: %s\n", id_str
);
14588 if (staged_blob_id
) {
14589 err
= got_object_id_str(&id_str
, staged_blob_id
);
14592 printf("based on staged blob: %s\n", id_str
);
14597 err
= got_object_id_str(&id_str
, commit_id
);
14600 printf("based on commit: %s\n", id_str
);
14607 static const struct got_error
*
14608 cmd_info(int argc
, char *argv
[])
14610 const struct got_error
*error
= NULL
;
14611 struct got_worktree
*worktree
= NULL
;
14612 char *cwd
= NULL
, *id_str
= NULL
;
14613 struct got_pathlist_head paths
;
14614 char *uuidstr
= NULL
;
14615 int ch
, show_files
= 0;
14617 TAILQ_INIT(&paths
);
14620 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
14625 while ((ch
= getopt(argc
, argv
, "")) != -1) {
14636 cwd
= getcwd(NULL
, 0);
14638 error
= got_error_from_errno("getcwd");
14642 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_GOT_DIR
);
14644 if (error
->code
== GOT_ERR_NOT_WORKTREE
)
14645 error
= wrap_not_worktree_error(error
, "info", cwd
);
14650 /* Remove "wpath cpath proc exec sendfd" promises. */
14651 if (pledge("stdio rpath flock unveil", NULL
) == -1)
14654 error
= apply_unveil(NULL
, 0, got_worktree_get_root_path(worktree
));
14659 error
= get_worktree_paths_from_argv(&paths
, argc
, argv
,
14666 error
= got_object_id_str(&id_str
,
14667 got_worktree_get_base_commit_id(worktree
));
14671 error
= got_worktree_get_uuid(&uuidstr
, worktree
);
14675 printf("work tree: %s\n", got_worktree_get_root_path(worktree
));
14676 printf("work tree base commit: %s\n", id_str
);
14677 printf("work tree path prefix: %s\n",
14678 got_worktree_get_path_prefix(worktree
));
14679 printf("work tree branch reference: %s\n",
14680 got_worktree_get_head_ref_name(worktree
));
14681 printf("work tree UUID: %s\n", uuidstr
);
14682 printf("repository: %s\n", got_worktree_get_repo_path(worktree
));
14685 struct got_pathlist_entry
*pe
;
14686 TAILQ_FOREACH(pe
, &paths
, entry
) {
14687 if (pe
->path_len
== 0)
14690 * Assume this path will fail. This will be corrected
14691 * in print_path_info() in case the path does suceeed.
14693 pe
->data
= (void *)got_error(GOT_ERR_BAD_PATH
);
14695 error
= got_worktree_path_info(worktree
, &paths
,
14696 print_path_info
, &paths
, check_cancelled
, NULL
);
14699 TAILQ_FOREACH(pe
, &paths
, entry
) {
14700 if (pe
->data
!= NULL
) {
14701 const struct got_error
*perr
;
14704 error
= got_error_fmt(perr
->code
, "%s",
14712 got_worktree_close(worktree
);
14713 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_PATH
);