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 [-A hashing-algorithm] [-b branch]"
360 " repository-path\n",
365 static const struct got_error
*
366 cmd_init(int argc
, char *argv
[])
368 const struct got_error
*error
= NULL
;
369 const char *head_name
= NULL
;
370 char *repo_path
= NULL
;
371 enum got_hash_algorithm algo
= GOT_HASH_SHA1
;
374 while ((ch
= getopt(argc
, argv
, "A:b:")) != -1) {
377 if (!strcmp(optarg
, "sha1"))
378 algo
= GOT_HASH_SHA1
;
379 else if (!strcmp(optarg
, "sha256"))
380 algo
= GOT_HASH_SHA256
;
382 return got_error_path(optarg
,
383 GOT_ERR_OBJECT_FORMAT
);
398 if (pledge("stdio rpath wpath cpath unveil", NULL
) == -1)
404 repo_path
= strdup(argv
[0]);
405 if (repo_path
== NULL
)
406 return got_error_from_errno("strdup");
408 got_path_strip_trailing_slashes(repo_path
);
410 error
= got_path_mkdir(repo_path
);
412 !(error
->code
== GOT_ERR_ERRNO
&& errno
== EEXIST
))
415 error
= apply_unveil(repo_path
, 0, NULL
);
419 error
= got_repo_init(repo_path
, head_name
, algo
);
428 fprintf(stderr
, "usage: %s import [-b branch] [-I pattern] [-m message] "
429 "[-r repository-path] directory\n", getprogname());
434 spawn_editor(const char *editor
, const char *file
)
437 sig_t sighup
, sigint
, sigquit
;
440 sighup
= signal(SIGHUP
, SIG_IGN
);
441 sigint
= signal(SIGINT
, SIG_IGN
);
442 sigquit
= signal(SIGQUIT
, SIG_IGN
);
444 switch (pid
= fork()) {
448 execl(editor
, editor
, file
, (char *)NULL
);
452 while (waitpid(pid
, &st
, 0) == -1)
457 (void)signal(SIGHUP
, sighup
);
458 (void)signal(SIGINT
, sigint
);
459 (void)signal(SIGQUIT
, sigquit
);
461 if (!WIFEXITED(st
)) {
466 return WEXITSTATUS(st
);
469 static const struct got_error
*
470 read_logmsg(char **logmsg
, size_t *len
, FILE *fp
, size_t filesize
)
472 const struct got_error
*err
= NULL
;
479 if (fseeko(fp
, 0L, SEEK_SET
) == -1)
480 return got_error_from_errno("fseeko");
482 *logmsg
= malloc(filesize
+ 1);
484 return got_error_from_errno("malloc");
487 while (getline(&line
, &linesize
, fp
) != -1) {
488 if (line
[0] == '#' || (*len
== 0 && line
[0] == '\n'))
489 continue; /* remove comments and leading empty lines */
490 *len
= strlcat(*logmsg
, line
, filesize
+ 1);
491 if (*len
>= filesize
+ 1) {
492 err
= got_error(GOT_ERR_NO_SPACE
);
497 err
= got_ferror(fp
, GOT_ERR_IO
);
501 while (*len
> 0 && (*logmsg
)[*len
- 1] == '\n') {
502 (*logmsg
)[*len
- 1] = '\0';
515 static const struct got_error
*
516 edit_logmsg(char **logmsg
, const char *editor
, const char *logmsg_path
,
517 const char *initial_content
, size_t initial_content_len
,
518 int require_modification
)
520 const struct got_error
*err
= NULL
;
527 if (stat(logmsg_path
, &st
) == -1)
528 return got_error_from_errno2("stat", logmsg_path
);
530 if (spawn_editor(editor
, logmsg_path
) == -1)
531 return got_error_from_errno("failed spawning editor");
533 if (require_modification
) {
534 struct timespec timeout
;
538 nanosleep(&timeout
, NULL
);
541 if (stat(logmsg_path
, &st2
) == -1)
542 return got_error_from_errno2("stat", logmsg_path
);
544 if (require_modification
&& st
.st_size
== st2
.st_size
&&
545 timespeccmp(&st
.st_mtim
, &st2
.st_mtim
, ==))
546 return got_error_msg(GOT_ERR_COMMIT_MSG_EMPTY
,
547 "no changes made to commit message, aborting");
549 fp
= fopen(logmsg_path
, "re");
551 err
= got_error_from_errno("fopen");
555 /* strip comments and leading/trailing newlines */
556 err
= read_logmsg(logmsg
, &logmsg_len
, fp
, st2
.st_size
);
559 if (logmsg_len
== 0) {
560 err
= got_error_msg(GOT_ERR_COMMIT_MSG_EMPTY
,
561 "commit message cannot be empty, aborting");
565 if (fp
&& fclose(fp
) == EOF
&& err
== NULL
)
566 err
= got_error_from_errno("fclose");
574 static const struct got_error
*
575 collect_import_msg(char **logmsg
, char **logmsg_path
, const char *editor
,
576 const char *path_dir
, const char *branch_name
)
578 char *initial_content
= NULL
;
579 const struct got_error
*err
= NULL
;
580 int initial_content_len
;
583 initial_content_len
= asprintf(&initial_content
,
584 "\n# %s to be imported to branch %s\n", path_dir
,
586 if (initial_content_len
== -1)
587 return got_error_from_errno("asprintf");
589 err
= got_opentemp_named_fd(logmsg_path
, &fd
,
590 GOT_TMPDIR_STR
"/got-importmsg", "");
594 if (write(fd
, initial_content
, initial_content_len
) == -1) {
595 err
= got_error_from_errno2("write", *logmsg_path
);
598 if (close(fd
) == -1) {
599 err
= got_error_from_errno2("close", *logmsg_path
);
604 err
= edit_logmsg(logmsg
, editor
, *logmsg_path
, initial_content
,
605 initial_content_len
, 1);
607 if (fd
!= -1 && close(fd
) == -1 && err
== NULL
)
608 err
= got_error_from_errno2("close", *logmsg_path
);
609 free(initial_content
);
617 static const struct got_error
*
618 import_progress(void *arg
, const char *path
)
620 printf("A %s\n", path
);
624 static const struct got_error
*
625 valid_author(const char *author
)
627 const char *email
= author
;
630 * Git' expects the author (or committer) to be in the form
631 * "name <email>", which are mostly free form (see the
632 * "committer" description in git-fast-import(1)). We're only
633 * doing this to avoid git's object parser breaking on commits
637 while (*author
&& *author
!= '\n' && *author
!= '<' && *author
!= '>')
639 if (author
!= email
&& *author
== '<' && *(author
- 1) != ' ')
640 return got_error_fmt(GOT_ERR_COMMIT_BAD_AUTHOR
, "%s: space "
641 "between author name and email required", email
);
642 if (*author
++ != '<')
643 return got_error_fmt(GOT_ERR_COMMIT_NO_EMAIL
, "%s", email
);
644 while (*author
&& *author
!= '\n' && *author
!= '<' && *author
!= '>')
646 if (strcmp(author
, ">") != 0)
647 return got_error_fmt(GOT_ERR_COMMIT_NO_EMAIL
, "%s", email
);
651 static const struct got_error
*
652 get_author(char **author
, struct got_repository
*repo
,
653 struct got_worktree
*worktree
)
655 const struct got_error
*err
= NULL
;
656 const char *got_author
= NULL
, *name
, *email
;
657 const struct got_gotconfig
*worktree_conf
= NULL
, *repo_conf
= NULL
;
662 worktree_conf
= got_worktree_get_gotconfig(worktree
);
663 repo_conf
= got_repo_get_gotconfig(repo
);
666 * Priority of potential author information sources, from most
667 * significant to least significant:
668 * 1) work tree's .got/got.conf file
669 * 2) repository's got.conf file
670 * 3) repository's git config file
671 * 4) environment variables
672 * 5) global git config files (in user's home directory or /etc)
676 got_author
= got_gotconfig_get_author(worktree_conf
);
677 if (got_author
== NULL
)
678 got_author
= got_gotconfig_get_author(repo_conf
);
679 if (got_author
== NULL
) {
680 name
= got_repo_get_gitconfig_author_name(repo
);
681 email
= got_repo_get_gitconfig_author_email(repo
);
683 if (asprintf(author
, "%s <%s>", name
, email
) == -1)
684 return got_error_from_errno("asprintf");
688 got_author
= getenv("GOT_AUTHOR");
689 if (got_author
== NULL
) {
690 name
= got_repo_get_global_gitconfig_author_name(repo
);
691 email
= got_repo_get_global_gitconfig_author_email(
694 if (asprintf(author
, "%s <%s>", name
, email
)
696 return got_error_from_errno("asprintf");
699 /* TODO: Look up user in password database? */
700 return got_error(GOT_ERR_COMMIT_NO_AUTHOR
);
704 *author
= strdup(got_author
);
706 return got_error_from_errno("strdup");
708 err
= valid_author(*author
);
716 static const struct got_error
*
717 get_allowed_signers(char **allowed_signers
, struct got_repository
*repo
,
718 struct got_worktree
*worktree
)
720 const char *got_allowed_signers
= NULL
;
721 const struct got_gotconfig
*worktree_conf
= NULL
, *repo_conf
= NULL
;
723 *allowed_signers
= NULL
;
726 worktree_conf
= got_worktree_get_gotconfig(worktree
);
727 repo_conf
= got_repo_get_gotconfig(repo
);
730 * Priority of potential author information sources, from most
731 * significant to least significant:
732 * 1) work tree's .got/got.conf file
733 * 2) repository's got.conf file
737 got_allowed_signers
= got_gotconfig_get_allowed_signers_file(
739 if (got_allowed_signers
== NULL
)
740 got_allowed_signers
= got_gotconfig_get_allowed_signers_file(
743 if (got_allowed_signers
) {
744 *allowed_signers
= strdup(got_allowed_signers
);
745 if (*allowed_signers
== NULL
)
746 return got_error_from_errno("strdup");
751 static const struct got_error
*
752 get_revoked_signers(char **revoked_signers
, struct got_repository
*repo
,
753 struct got_worktree
*worktree
)
755 const char *got_revoked_signers
= NULL
;
756 const struct got_gotconfig
*worktree_conf
= NULL
, *repo_conf
= NULL
;
758 *revoked_signers
= NULL
;
761 worktree_conf
= got_worktree_get_gotconfig(worktree
);
762 repo_conf
= got_repo_get_gotconfig(repo
);
765 * Priority of potential author information sources, from most
766 * significant to least significant:
767 * 1) work tree's .got/got.conf file
768 * 2) repository's got.conf file
772 got_revoked_signers
= got_gotconfig_get_revoked_signers_file(
774 if (got_revoked_signers
== NULL
)
775 got_revoked_signers
= got_gotconfig_get_revoked_signers_file(
778 if (got_revoked_signers
) {
779 *revoked_signers
= strdup(got_revoked_signers
);
780 if (*revoked_signers
== NULL
)
781 return got_error_from_errno("strdup");
787 get_signer_id(struct got_repository
*repo
, struct got_worktree
*worktree
)
789 const char *got_signer_id
= NULL
;
790 const struct got_gotconfig
*worktree_conf
= NULL
, *repo_conf
= NULL
;
793 worktree_conf
= got_worktree_get_gotconfig(worktree
);
794 repo_conf
= got_repo_get_gotconfig(repo
);
797 * Priority of potential author information sources, from most
798 * significant to least significant:
799 * 1) work tree's .got/got.conf file
800 * 2) repository's got.conf file
804 got_signer_id
= got_gotconfig_get_signer_id(worktree_conf
);
805 if (got_signer_id
== NULL
)
806 got_signer_id
= got_gotconfig_get_signer_id(repo_conf
);
808 return got_signer_id
;
811 static const struct got_error
*
812 get_gitconfig_path(char **gitconfig_path
)
814 const char *homedir
= getenv("HOME");
816 *gitconfig_path
= NULL
;
818 if (asprintf(gitconfig_path
, "%s/.gitconfig", homedir
) == -1)
819 return got_error_from_errno("asprintf");
825 static const struct got_error
*
826 cmd_import(int argc
, char *argv
[])
828 const struct got_error
*error
= NULL
;
829 char *path_dir
= NULL
, *repo_path
= NULL
, *logmsg
= NULL
;
830 char *gitconfig_path
= NULL
, *editor
= NULL
, *author
= NULL
;
831 const char *branch_name
= NULL
;
832 char *id_str
= NULL
, *logmsg_path
= NULL
;
833 char refname
[PATH_MAX
] = "refs/heads/";
834 struct got_repository
*repo
= NULL
;
835 struct got_reference
*branch_ref
= NULL
, *head_ref
= NULL
;
836 struct got_object_id
*new_commit_id
= NULL
;
838 struct got_pathlist_head ignores
;
839 struct got_pathlist_entry
*pe
;
840 int preserve_logmsg
= 0;
841 int *pack_fds
= NULL
;
843 TAILQ_INIT(&ignores
);
846 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
852 while ((ch
= getopt(argc
, argv
, "b:I:m:r:")) != -1) {
855 branch_name
= optarg
;
858 if (optarg
[0] == '\0')
860 error
= got_pathlist_insert(&pe
, &ignores
, optarg
,
866 logmsg
= strdup(optarg
);
867 if (logmsg
== NULL
) {
868 error
= got_error_from_errno("strdup");
873 repo_path
= realpath(optarg
, NULL
);
874 if (repo_path
== NULL
) {
875 error
= got_error_from_errno2("realpath",
892 if (repo_path
== NULL
) {
893 repo_path
= getcwd(NULL
, 0);
894 if (repo_path
== NULL
)
895 return got_error_from_errno("getcwd");
897 got_path_strip_trailing_slashes(repo_path
);
898 error
= get_gitconfig_path(&gitconfig_path
);
901 error
= got_repo_pack_fds_open(&pack_fds
);
904 error
= got_repo_open(&repo
, repo_path
, gitconfig_path
, pack_fds
);
908 path_dir
= realpath(argv
[0], NULL
);
909 if (path_dir
== NULL
) {
910 error
= got_error_from_errno2("realpath", argv
[0]);
913 got_path_strip_trailing_slashes(path_dir
);
915 error
= get_editor(&editor
);
919 if (unveil(path_dir
, "r") != 0) {
920 error
= got_error_from_errno2("unveil", path_dir
);
923 if (unveil(editor
, "x") != 0) {
924 error
= got_error_from_errno2("unveil", editor
);
927 error
= apply_unveil(got_repo_get_path(repo
), 0, NULL
);
931 error
= get_author(&author
, repo
, NULL
);
936 * Don't let the user create a branch name with a leading '-'.
937 * While technically a valid reference name, this case is usually
938 * an unintended typo.
940 if (branch_name
&& branch_name
[0] == '-')
941 return got_error_path(branch_name
, GOT_ERR_REF_NAME_MINUS
);
943 error
= got_ref_open(&head_ref
, repo
, GOT_REF_HEAD
, 0);
944 if (error
&& error
->code
!= GOT_ERR_NOT_REF
)
948 n
= strlcat(refname
, branch_name
, sizeof(refname
));
949 else if (head_ref
&& got_ref_is_symbolic(head_ref
))
950 n
= strlcpy(refname
, got_ref_get_symref_target(head_ref
),
953 n
= strlcat(refname
, "main", sizeof(refname
));
954 if (n
>= sizeof(refname
)) {
955 error
= got_error(GOT_ERR_NO_SPACE
);
959 error
= got_ref_open(&branch_ref
, repo
, refname
, 0);
961 if (error
->code
!= GOT_ERR_NOT_REF
)
964 error
= got_error_msg(GOT_ERR_BRANCH_EXISTS
,
965 "import target branch already exists");
969 if (logmsg
== NULL
|| *logmsg
== '\0') {
971 error
= collect_import_msg(&logmsg
, &logmsg_path
, editor
,
974 if (error
->code
!= GOT_ERR_COMMIT_MSG_EMPTY
&&
981 error
= got_repo_import(&new_commit_id
, path_dir
, logmsg
,
982 author
, &ignores
, repo
, import_progress
, NULL
);
989 error
= got_ref_alloc(&branch_ref
, refname
, new_commit_id
);
996 error
= got_ref_write(branch_ref
, repo
);
1003 error
= got_object_id_str(&id_str
, new_commit_id
);
1006 preserve_logmsg
= 1;
1010 error
= got_ref_open(&head_ref
, repo
, GOT_REF_HEAD
, 0);
1012 if (error
->code
!= GOT_ERR_NOT_REF
) {
1014 preserve_logmsg
= 1;
1018 error
= got_ref_alloc_symref(&head_ref
, GOT_REF_HEAD
,
1022 preserve_logmsg
= 1;
1026 error
= got_ref_write(head_ref
, repo
);
1029 preserve_logmsg
= 1;
1034 printf("Created branch %s with commit %s\n",
1035 got_ref_get_name(branch_ref
), id_str
);
1038 const struct got_error
*pack_err
=
1039 got_repo_pack_fds_close(pack_fds
);
1044 const struct got_error
*close_err
= got_repo_close(repo
);
1048 if (preserve_logmsg
) {
1049 fprintf(stderr
, "%s: log message preserved in %s\n",
1050 getprogname(), logmsg_path
);
1051 } else if (logmsg_path
&& unlink(logmsg_path
) == -1 && error
== NULL
)
1052 error
= got_error_from_errno2("unlink", logmsg_path
);
1057 free(new_commit_id
);
1060 free(gitconfig_path
);
1062 got_ref_close(branch_ref
);
1064 got_ref_close(head_ref
);
1071 fprintf(stderr
, "usage: %s clone [-almqv] [-b branch] [-R reference] "
1072 "repository-URL [directory]\n", getprogname());
1076 struct got_fetch_progress_arg
{
1077 char last_scaled_size
[FMT_SCALED_STRSIZE
];
1079 int last_p_resolved
;
1082 struct got_repository
*repo
;
1085 int configs_created
;
1087 struct got_pathlist_head
*symrefs
;
1088 struct got_pathlist_head
*wanted_branches
;
1089 struct got_pathlist_head
*wanted_refs
;
1093 const char *remote_repo_path
;
1094 const char *git_url
;
1095 int fetch_all_branches
;
1096 int mirror_references
;
1100 /* XXX forward declaration */
1101 static const struct got_error
*
1102 create_config_files(const char *proto
, const char *host
, const char *port
,
1103 const char *remote_repo_path
, const char *git_url
, int fetch_all_branches
,
1104 int mirror_references
, struct got_pathlist_head
*symrefs
,
1105 struct got_pathlist_head
*wanted_branches
,
1106 struct got_pathlist_head
*wanted_refs
, struct got_repository
*repo
);
1108 static const struct got_error
*
1109 fetch_progress(void *arg
, const char *message
, off_t packfile_size
,
1110 int nobj_total
, int nobj_indexed
, int nobj_loose
, int nobj_resolved
)
1112 const struct got_error
*err
= NULL
;
1113 struct got_fetch_progress_arg
*a
= arg
;
1114 char scaled_size
[FMT_SCALED_STRSIZE
];
1115 int p_indexed
, p_resolved
;
1116 int print_size
= 0, print_indexed
= 0, print_resolved
= 0;
1119 * In order to allow a failed clone to be resumed with 'got fetch'
1120 * we try to create configuration files as soon as possible.
1121 * Once the server has sent information about its default branch
1122 * we have all required information.
1124 if (a
->create_configs
&& !a
->configs_created
&&
1125 !TAILQ_EMPTY(a
->config_info
.symrefs
)) {
1126 err
= create_config_files(a
->config_info
.proto
,
1127 a
->config_info
.host
, a
->config_info
.port
,
1128 a
->config_info
.remote_repo_path
,
1129 a
->config_info
.git_url
,
1130 a
->config_info
.fetch_all_branches
,
1131 a
->config_info
.mirror_references
,
1132 a
->config_info
.symrefs
,
1133 a
->config_info
.wanted_branches
,
1134 a
->config_info
.wanted_refs
, a
->repo
);
1137 a
->configs_created
= 1;
1140 if (a
->verbosity
< 0)
1143 if (message
&& message
[0] != '\0') {
1144 printf("\rserver: %s", message
);
1149 if (packfile_size
> 0 || nobj_indexed
> 0) {
1150 if (fmt_scaled(packfile_size
, scaled_size
) == 0 &&
1151 (a
->last_scaled_size
[0] == '\0' ||
1152 strcmp(scaled_size
, a
->last_scaled_size
)) != 0) {
1154 if (strlcpy(a
->last_scaled_size
, scaled_size
,
1155 FMT_SCALED_STRSIZE
) >= FMT_SCALED_STRSIZE
)
1156 return got_error(GOT_ERR_NO_SPACE
);
1158 if (nobj_indexed
> 0) {
1159 p_indexed
= (nobj_indexed
* 100) / nobj_total
;
1160 if (p_indexed
!= a
->last_p_indexed
) {
1161 a
->last_p_indexed
= p_indexed
;
1166 if (nobj_resolved
> 0) {
1167 p_resolved
= (nobj_resolved
* 100) /
1168 (nobj_total
- nobj_loose
);
1169 if (p_resolved
!= a
->last_p_resolved
) {
1170 a
->last_p_resolved
= p_resolved
;
1178 if (print_size
|| print_indexed
|| print_resolved
)
1181 printf("%*s fetched", FMT_SCALED_STRSIZE
- 2, scaled_size
);
1183 printf("; indexing %d%%", p_indexed
);
1185 printf("; resolving deltas %d%%", p_resolved
);
1186 if (print_size
|| print_indexed
|| print_resolved
)
1192 static const struct got_error
*
1193 create_symref(const char *refname
, struct got_reference
*target_ref
,
1194 int verbosity
, struct got_repository
*repo
)
1196 const struct got_error
*err
;
1197 struct got_reference
*head_symref
;
1199 err
= got_ref_alloc_symref(&head_symref
, refname
, target_ref
);
1203 err
= got_ref_write(head_symref
, repo
);
1204 if (err
== NULL
&& verbosity
> 0) {
1205 printf("Created reference %s: %s\n", GOT_REF_HEAD
,
1206 got_ref_get_name(target_ref
));
1208 got_ref_close(head_symref
);
1212 static const struct got_error
*
1213 list_remote_refs(struct got_pathlist_head
*symrefs
,
1214 struct got_pathlist_head
*refs
)
1216 const struct got_error
*err
;
1217 struct got_pathlist_entry
*pe
;
1219 TAILQ_FOREACH(pe
, symrefs
, entry
) {
1220 const char *refname
= pe
->path
;
1221 const char *targetref
= pe
->data
;
1223 printf("%s: %s\n", refname
, targetref
);
1226 TAILQ_FOREACH(pe
, refs
, entry
) {
1227 const char *refname
= pe
->path
;
1228 struct got_object_id
*id
= pe
->data
;
1231 err
= got_object_id_str(&id_str
, id
);
1234 printf("%s: %s\n", refname
, id_str
);
1241 static const struct got_error
*
1242 create_ref(const char *refname
, struct got_object_id
*id
,
1243 int verbosity
, struct got_repository
*repo
)
1245 const struct got_error
*err
= NULL
;
1246 struct got_reference
*ref
;
1249 err
= got_object_id_str(&id_str
, id
);
1253 err
= got_ref_alloc(&ref
, refname
, id
);
1257 err
= got_ref_write(ref
, repo
);
1260 if (err
== NULL
&& verbosity
>= 0)
1261 printf("Created reference %s: %s\n", refname
, id_str
);
1268 match_wanted_ref(const char *refname
, const char *wanted_ref
)
1270 if (strncmp(refname
, "refs/", 5) != 0)
1275 * Prevent fetching of references that won't make any
1276 * sense outside of the remote repository's context.
1278 if (strncmp(refname
, "got/", 4) == 0)
1280 if (strncmp(refname
, "remotes/", 8) == 0)
1283 if (strncmp(wanted_ref
, "refs/", 5) == 0)
1286 /* Allow prefix match. */
1287 if (got_path_is_child(refname
, wanted_ref
, strlen(wanted_ref
)))
1290 /* Allow exact match. */
1291 return (strcmp(refname
, wanted_ref
) == 0);
1295 is_wanted_ref(struct got_pathlist_head
*wanted_refs
, const char *refname
)
1297 struct got_pathlist_entry
*pe
;
1299 TAILQ_FOREACH(pe
, wanted_refs
, entry
) {
1300 if (match_wanted_ref(refname
, pe
->path
))
1307 static const struct got_error
*
1308 create_wanted_ref(const char *refname
, struct got_object_id
*id
,
1309 const char *remote_repo_name
, int verbosity
, struct got_repository
*repo
)
1311 const struct got_error
*err
;
1312 char *remote_refname
;
1314 if (strncmp("refs/", refname
, 5) == 0)
1317 if (asprintf(&remote_refname
, "refs/remotes/%s/%s",
1318 remote_repo_name
, refname
) == -1)
1319 return got_error_from_errno("asprintf");
1321 err
= create_ref(remote_refname
, id
, verbosity
, repo
);
1322 free(remote_refname
);
1326 static const struct got_error
*
1327 create_gotconfig(const char *proto
, const char *host
, const char *port
,
1328 const char *remote_repo_path
, const char *default_branch
,
1329 int fetch_all_branches
, struct got_pathlist_head
*wanted_branches
,
1330 struct got_pathlist_head
*wanted_refs
, int mirror_references
,
1331 struct got_repository
*repo
)
1333 const struct got_error
*err
= NULL
;
1334 char *gotconfig_path
= NULL
;
1335 char *gotconfig
= NULL
;
1336 FILE *gotconfig_file
= NULL
;
1337 const char *branchname
= NULL
;
1338 char *branches
= NULL
, *refs
= NULL
;
1341 if (!fetch_all_branches
&& !TAILQ_EMPTY(wanted_branches
)) {
1342 struct got_pathlist_entry
*pe
;
1343 TAILQ_FOREACH(pe
, wanted_branches
, entry
) {
1345 branchname
= pe
->path
;
1346 if (strncmp(branchname
, "refs/heads/", 11) == 0)
1348 if (asprintf(&s
, "%s\"%s\" ",
1349 branches
? branches
: "", branchname
) == -1) {
1350 err
= got_error_from_errno("asprintf");
1356 } else if (!fetch_all_branches
&& default_branch
) {
1357 branchname
= default_branch
;
1358 if (strncmp(branchname
, "refs/heads/", 11) == 0)
1360 if (asprintf(&branches
, "\"%s\" ", branchname
) == -1) {
1361 err
= got_error_from_errno("asprintf");
1365 if (!TAILQ_EMPTY(wanted_refs
)) {
1366 struct got_pathlist_entry
*pe
;
1367 TAILQ_FOREACH(pe
, wanted_refs
, entry
) {
1369 const char *refname
= pe
->path
;
1370 if (strncmp(refname
, "refs/", 5) == 0)
1372 if (asprintf(&s
, "%s\"%s\" ",
1373 refs
? refs
: "", refname
) == -1) {
1374 err
= got_error_from_errno("asprintf");
1382 /* Create got.conf(5). */
1383 gotconfig_path
= got_repo_get_path_gotconfig(repo
);
1384 if (gotconfig_path
== NULL
) {
1385 err
= got_error_from_errno("got_repo_get_path_gotconfig");
1388 gotconfig_file
= fopen(gotconfig_path
, "ae");
1389 if (gotconfig_file
== NULL
) {
1390 err
= got_error_from_errno2("fopen", gotconfig_path
);
1393 if (asprintf(&gotconfig
,
1398 "\trepository \"%s\"\n"
1404 GOT_FETCH_DEFAULT_REMOTE_NAME
, host
, proto
,
1405 port
? "\tport " : "", port
? port
: "", port
? "\n" : "",
1406 remote_repo_path
, branches
? "\tbranch { " : "",
1407 branches
? branches
: "", branches
? "}\n" : "",
1408 refs
? "\treference { " : "", refs
? refs
: "", refs
? "}\n" : "",
1409 mirror_references
? "\tmirror_references yes\n" : "",
1410 fetch_all_branches
? "\tfetch_all_branches yes\n" : "") == -1) {
1411 err
= got_error_from_errno("asprintf");
1414 n
= fwrite(gotconfig
, 1, strlen(gotconfig
), gotconfig_file
);
1415 if (n
!= strlen(gotconfig
)) {
1416 err
= got_ferror(gotconfig_file
, GOT_ERR_IO
);
1421 if (gotconfig_file
&& fclose(gotconfig_file
) == EOF
&& err
== NULL
)
1422 err
= got_error_from_errno2("fclose", gotconfig_path
);
1423 free(gotconfig_path
);
1428 static const struct got_error
*
1429 create_gitconfig(const char *git_url
, const char *default_branch
,
1430 int fetch_all_branches
, struct got_pathlist_head
*wanted_branches
,
1431 struct got_pathlist_head
*wanted_refs
, int mirror_references
,
1432 struct got_repository
*repo
)
1434 const struct got_error
*err
= NULL
;
1435 char *gitconfig_path
= NULL
;
1436 char *gitconfig
= NULL
;
1437 FILE *gitconfig_file
= NULL
;
1438 char *branches
= NULL
, *refs
= NULL
;
1439 const char *branchname
;
1442 /* Create a config file Git can understand. */
1443 gitconfig_path
= got_repo_get_path_gitconfig(repo
);
1444 if (gitconfig_path
== NULL
) {
1445 err
= got_error_from_errno("got_repo_get_path_gitconfig");
1448 gitconfig_file
= fopen(gitconfig_path
, "ae");
1449 if (gitconfig_file
== NULL
) {
1450 err
= got_error_from_errno2("fopen", gitconfig_path
);
1453 if (fetch_all_branches
) {
1454 if (mirror_references
) {
1455 if (asprintf(&branches
,
1456 "\tfetch = refs/heads/*:refs/heads/*\n") == -1) {
1457 err
= got_error_from_errno("asprintf");
1460 } else if (asprintf(&branches
,
1461 "\tfetch = refs/heads/*:refs/remotes/%s/*\n",
1462 GOT_FETCH_DEFAULT_REMOTE_NAME
) == -1) {
1463 err
= got_error_from_errno("asprintf");
1466 } else if (!TAILQ_EMPTY(wanted_branches
)) {
1467 struct got_pathlist_entry
*pe
;
1468 TAILQ_FOREACH(pe
, wanted_branches
, entry
) {
1470 branchname
= pe
->path
;
1471 if (strncmp(branchname
, "refs/heads/", 11) == 0)
1473 if (mirror_references
) {
1475 "%s\tfetch = refs/heads/%s:refs/heads/%s\n",
1476 branches
? branches
: "",
1477 branchname
, branchname
) == -1) {
1478 err
= got_error_from_errno("asprintf");
1481 } else if (asprintf(&s
,
1482 "%s\tfetch = refs/heads/%s:refs/remotes/%s/%s\n",
1483 branches
? branches
: "",
1484 branchname
, GOT_FETCH_DEFAULT_REMOTE_NAME
,
1485 branchname
) == -1) {
1486 err
= got_error_from_errno("asprintf");
1494 * If the server specified a default branch, use just that one.
1495 * Otherwise fall back to fetching all branches on next fetch.
1497 if (default_branch
) {
1498 branchname
= default_branch
;
1499 if (strncmp(branchname
, "refs/heads/", 11) == 0)
1502 branchname
= "*"; /* fall back to all branches */
1503 if (mirror_references
) {
1504 if (asprintf(&branches
,
1505 "\tfetch = refs/heads/%s:refs/heads/%s\n",
1506 branchname
, branchname
) == -1) {
1507 err
= got_error_from_errno("asprintf");
1510 } else if (asprintf(&branches
,
1511 "\tfetch = refs/heads/%s:refs/remotes/%s/%s\n",
1512 branchname
, GOT_FETCH_DEFAULT_REMOTE_NAME
,
1513 branchname
) == -1) {
1514 err
= got_error_from_errno("asprintf");
1518 if (!TAILQ_EMPTY(wanted_refs
)) {
1519 struct got_pathlist_entry
*pe
;
1520 TAILQ_FOREACH(pe
, wanted_refs
, entry
) {
1522 const char *refname
= pe
->path
;
1523 if (strncmp(refname
, "refs/", 5) == 0)
1525 if (mirror_references
) {
1527 "%s\tfetch = refs/%s:refs/%s\n",
1528 refs
? refs
: "", refname
, refname
) == -1) {
1529 err
= got_error_from_errno("asprintf");
1532 } else if (asprintf(&s
,
1533 "%s\tfetch = refs/%s:refs/remotes/%s/%s\n",
1535 refname
, GOT_FETCH_DEFAULT_REMOTE_NAME
,
1537 err
= got_error_from_errno("asprintf");
1545 if (asprintf(&gitconfig
,
1550 "\tfetch = refs/tags/*:refs/tags/*\n",
1551 GOT_FETCH_DEFAULT_REMOTE_NAME
, git_url
, branches
? branches
: "",
1552 refs
? refs
: "") == -1) {
1553 err
= got_error_from_errno("asprintf");
1556 n
= fwrite(gitconfig
, 1, strlen(gitconfig
), gitconfig_file
);
1557 if (n
!= strlen(gitconfig
)) {
1558 err
= got_ferror(gitconfig_file
, GOT_ERR_IO
);
1562 if (gitconfig_file
&& fclose(gitconfig_file
) == EOF
&& err
== NULL
)
1563 err
= got_error_from_errno2("fclose", gitconfig_path
);
1564 free(gitconfig_path
);
1569 static const struct got_error
*
1570 create_config_files(const char *proto
, const char *host
, const char *port
,
1571 const char *remote_repo_path
, const char *git_url
, int fetch_all_branches
,
1572 int mirror_references
, struct got_pathlist_head
*symrefs
,
1573 struct got_pathlist_head
*wanted_branches
,
1574 struct got_pathlist_head
*wanted_refs
, struct got_repository
*repo
)
1576 const struct got_error
*err
= NULL
;
1577 const char *default_branch
= NULL
;
1578 struct got_pathlist_entry
*pe
;
1581 * If we asked for a set of wanted branches then use the first
1584 if (!TAILQ_EMPTY(wanted_branches
)) {
1585 pe
= TAILQ_FIRST(wanted_branches
);
1586 default_branch
= pe
->path
;
1588 /* First HEAD ref listed by server is the default branch. */
1589 TAILQ_FOREACH(pe
, symrefs
, entry
) {
1590 const char *refname
= pe
->path
;
1591 const char *target
= pe
->data
;
1593 if (strcmp(refname
, GOT_REF_HEAD
) != 0)
1596 default_branch
= target
;
1601 /* Create got.conf(5). */
1602 err
= create_gotconfig(proto
, host
, port
, remote_repo_path
,
1603 default_branch
, fetch_all_branches
, wanted_branches
,
1604 wanted_refs
, mirror_references
, repo
);
1608 /* Create a config file Git can understand. */
1609 return create_gitconfig(git_url
, default_branch
, fetch_all_branches
,
1610 wanted_branches
, wanted_refs
, mirror_references
, repo
);
1613 static const struct got_error
*
1614 cmd_clone(int argc
, char *argv
[])
1616 const struct got_error
*error
= NULL
;
1617 const char *uri
, *dirname
;
1618 char *proto
, *host
, *port
, *repo_name
, *server_path
;
1619 char *default_destdir
= NULL
, *id_str
= NULL
;
1620 const char *repo_path
;
1621 struct got_repository
*repo
= NULL
;
1622 struct got_pathlist_head refs
, symrefs
, wanted_branches
, wanted_refs
;
1623 struct got_pathlist_entry
*pe
;
1624 struct got_object_id
*pack_hash
= NULL
;
1625 int ch
, fetchfd
= -1, fetchstatus
;
1626 pid_t fetchpid
= -1;
1627 struct got_fetch_progress_arg fpa
;
1628 char *git_url
= NULL
;
1629 int verbosity
= 0, fetch_all_branches
= 0, mirror_references
= 0;
1630 int bflag
= 0, list_refs_only
= 0;
1631 int *pack_fds
= NULL
;
1634 TAILQ_INIT(&symrefs
);
1635 TAILQ_INIT(&wanted_branches
);
1636 TAILQ_INIT(&wanted_refs
);
1638 while ((ch
= getopt(argc
, argv
, "ab:lmqR:v")) != -1) {
1641 fetch_all_branches
= 1;
1644 error
= got_pathlist_insert(NULL
, &wanted_branches
,
1654 mirror_references
= 1;
1660 error
= got_pathlist_insert(NULL
, &wanted_refs
,
1668 else if (verbosity
< 3)
1679 if (fetch_all_branches
&& !TAILQ_EMPTY(&wanted_branches
))
1680 option_conflict('a', 'b');
1681 if (list_refs_only
) {
1682 if (!TAILQ_EMPTY(&wanted_branches
))
1683 option_conflict('l', 'b');
1684 if (fetch_all_branches
)
1685 option_conflict('l', 'a');
1686 if (mirror_references
)
1687 option_conflict('l', 'm');
1688 if (!TAILQ_EMPTY(&wanted_refs
))
1689 option_conflict('l', 'R');
1701 error
= got_dial_parse_uri(&proto
, &host
, &port
, &server_path
,
1706 if (asprintf(&git_url
, "%s://%s%s%s%s%s", proto
,
1707 host
, port
? ":" : "", port
? port
: "",
1708 server_path
[0] != '/' ? "/" : "", server_path
) == -1) {
1709 error
= got_error_from_errno("asprintf");
1713 if (strcmp(proto
, "git") == 0) {
1715 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
1716 "sendfd dns inet unveil", NULL
) == -1)
1719 } else if (strcmp(proto
, "git+ssh") == 0 ||
1720 strcmp(proto
, "ssh") == 0 ||
1721 strcmp(proto
, "git+http") == 0 ||
1722 strcmp(proto
, "http") == 0 ||
1723 strcmp(proto
, "git+https") == 0 ||
1724 strcmp(proto
, "https") == 0) {
1726 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
1727 "sendfd unveil", NULL
) == -1)
1731 error
= got_error_path(proto
, GOT_ERR_BAD_PROTO
);
1734 if (dirname
== NULL
) {
1735 if (asprintf(&default_destdir
, "%s.git", repo_name
) == -1) {
1736 error
= got_error_from_errno("asprintf");
1739 repo_path
= default_destdir
;
1741 repo_path
= dirname
;
1743 if (!list_refs_only
) {
1744 error
= got_path_mkdir(repo_path
);
1746 (!(error
->code
== GOT_ERR_ERRNO
&& errno
== EISDIR
) &&
1747 !(error
->code
== GOT_ERR_ERRNO
&& errno
== EEXIST
)))
1749 if (!got_path_dir_is_empty(repo_path
)) {
1750 error
= got_error_path(repo_path
,
1751 GOT_ERR_DIR_NOT_EMPTY
);
1756 error
= got_dial_apply_unveil(proto
);
1760 error
= apply_unveil(repo_path
, 0, NULL
);
1765 printf("Connecting to %s\n", git_url
);
1767 error
= got_fetch_connect(&fetchpid
, &fetchfd
, proto
, host
, port
,
1768 server_path
, verbosity
);
1773 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd",
1777 if (!list_refs_only
) {
1778 error
= got_repo_init(repo_path
, NULL
, GOT_HASH_SHA1
);
1781 error
= got_repo_pack_fds_open(&pack_fds
);
1784 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
1789 fpa
.last_scaled_size
[0] = '\0';
1790 fpa
.last_p_indexed
= -1;
1791 fpa
.last_p_resolved
= -1;
1792 fpa
.verbosity
= verbosity
;
1793 fpa
.create_configs
= 1;
1794 fpa
.configs_created
= 0;
1796 fpa
.config_info
.symrefs
= &symrefs
;
1797 fpa
.config_info
.wanted_branches
= &wanted_branches
;
1798 fpa
.config_info
.wanted_refs
= &wanted_refs
;
1799 fpa
.config_info
.proto
= proto
;
1800 fpa
.config_info
.host
= host
;
1801 fpa
.config_info
.port
= port
;
1802 fpa
.config_info
.remote_repo_path
= server_path
;
1803 fpa
.config_info
.git_url
= git_url
;
1804 fpa
.config_info
.fetch_all_branches
= fetch_all_branches
;
1805 fpa
.config_info
.mirror_references
= mirror_references
;
1806 error
= got_fetch_pack(&pack_hash
, &refs
, &symrefs
,
1807 GOT_FETCH_DEFAULT_REMOTE_NAME
, mirror_references
,
1808 fetch_all_branches
, &wanted_branches
, &wanted_refs
,
1809 list_refs_only
, verbosity
, fetchfd
, repo
, NULL
, NULL
, bflag
,
1810 fetch_progress
, &fpa
);
1814 if (list_refs_only
) {
1815 error
= list_remote_refs(&symrefs
, &refs
);
1819 if (pack_hash
== NULL
) {
1820 error
= got_error_fmt(GOT_ERR_FETCH_FAILED
, "%s",
1821 "server sent an empty pack file");
1824 error
= got_object_id_str(&id_str
, pack_hash
);
1828 printf("\nFetched %s.pack\n", id_str
);
1831 /* Set up references provided with the pack file. */
1832 TAILQ_FOREACH(pe
, &refs
, entry
) {
1833 const char *refname
= pe
->path
;
1834 struct got_object_id
*id
= pe
->data
;
1835 char *remote_refname
;
1837 if (is_wanted_ref(&wanted_refs
, refname
) &&
1838 !mirror_references
) {
1839 error
= create_wanted_ref(refname
, id
,
1840 GOT_FETCH_DEFAULT_REMOTE_NAME
,
1841 verbosity
- 1, repo
);
1847 error
= create_ref(refname
, id
, verbosity
- 1, repo
);
1851 if (mirror_references
)
1854 if (strncmp("refs/heads/", refname
, 11) != 0)
1857 if (asprintf(&remote_refname
,
1858 "refs/remotes/%s/%s", GOT_FETCH_DEFAULT_REMOTE_NAME
,
1859 refname
+ 11) == -1) {
1860 error
= got_error_from_errno("asprintf");
1863 error
= create_ref(remote_refname
, id
, verbosity
- 1, repo
);
1864 free(remote_refname
);
1869 /* Set the HEAD reference if the server provided one. */
1870 TAILQ_FOREACH(pe
, &symrefs
, entry
) {
1871 struct got_reference
*target_ref
;
1872 const char *refname
= pe
->path
;
1873 const char *target
= pe
->data
;
1874 char *remote_refname
= NULL
, *remote_target
= NULL
;
1876 if (strcmp(refname
, GOT_REF_HEAD
) != 0)
1879 error
= got_ref_open(&target_ref
, repo
, target
, 0);
1881 if (error
->code
== GOT_ERR_NOT_REF
) {
1888 error
= create_symref(refname
, target_ref
, verbosity
, repo
);
1889 got_ref_close(target_ref
);
1893 if (mirror_references
)
1896 if (strncmp("refs/heads/", target
, 11) != 0)
1899 if (asprintf(&remote_refname
,
1900 "refs/remotes/%s/%s", GOT_FETCH_DEFAULT_REMOTE_NAME
,
1902 error
= got_error_from_errno("asprintf");
1905 if (asprintf(&remote_target
,
1906 "refs/remotes/%s/%s", GOT_FETCH_DEFAULT_REMOTE_NAME
,
1907 target
+ 11) == -1) {
1908 error
= got_error_from_errno("asprintf");
1909 free(remote_refname
);
1912 error
= got_ref_open(&target_ref
, repo
, remote_target
, 0);
1914 free(remote_refname
);
1915 free(remote_target
);
1916 if (error
->code
== GOT_ERR_NOT_REF
) {
1922 error
= create_symref(remote_refname
, target_ref
,
1923 verbosity
- 1, repo
);
1924 free(remote_refname
);
1925 free(remote_target
);
1926 got_ref_close(target_ref
);
1932 * We failed to set the HEAD reference. If we asked for
1933 * a set of wanted branches use the first of one of those
1934 * which could be fetched instead.
1936 TAILQ_FOREACH(pe
, &wanted_branches
, entry
) {
1937 const char *target
= pe
->path
;
1938 struct got_reference
*target_ref
;
1940 error
= got_ref_open(&target_ref
, repo
, target
, 0);
1942 if (error
->code
== GOT_ERR_NOT_REF
) {
1949 error
= create_symref(GOT_REF_HEAD
, target_ref
,
1951 got_ref_close(target_ref
);
1957 if (!fpa
.configs_created
&& pe
!= NULL
) {
1958 error
= create_config_files(fpa
.config_info
.proto
,
1959 fpa
.config_info
.host
, fpa
.config_info
.port
,
1960 fpa
.config_info
.remote_repo_path
,
1961 fpa
.config_info
.git_url
,
1962 fpa
.config_info
.fetch_all_branches
,
1963 fpa
.config_info
.mirror_references
,
1964 fpa
.config_info
.symrefs
,
1965 fpa
.config_info
.wanted_branches
,
1966 fpa
.config_info
.wanted_refs
, fpa
.repo
);
1973 printf("Created %s repository '%s'\n",
1974 mirror_references
? "mirrored" : "cloned", repo_path
);
1977 const struct got_error
*pack_err
=
1978 got_repo_pack_fds_close(pack_fds
);
1983 if (kill(fetchpid
, SIGTERM
) == -1)
1984 error
= got_error_from_errno("kill");
1985 if (waitpid(fetchpid
, &fetchstatus
, 0) == -1 && error
== NULL
)
1986 error
= got_error_from_errno("waitpid");
1988 if (fetchfd
!= -1 && close(fetchfd
) == -1 && error
== NULL
)
1989 error
= got_error_from_errno("close");
1991 const struct got_error
*close_err
= got_repo_close(repo
);
1995 got_pathlist_free(&refs
, GOT_PATHLIST_FREE_ALL
);
1996 got_pathlist_free(&symrefs
, GOT_PATHLIST_FREE_ALL
);
1997 got_pathlist_free(&wanted_branches
, GOT_PATHLIST_FREE_NONE
);
1998 got_pathlist_free(&wanted_refs
, GOT_PATHLIST_FREE_NONE
);
2005 free(default_destdir
);
2010 static const struct got_error
*
2011 update_ref(struct got_reference
*ref
, struct got_object_id
*new_id
,
2012 int replace_tags
, int verbosity
, struct got_repository
*repo
)
2014 const struct got_error
*err
= NULL
;
2015 char *new_id_str
= NULL
;
2016 struct got_object_id
*old_id
= NULL
;
2018 err
= got_object_id_str(&new_id_str
, new_id
);
2022 if (!replace_tags
&&
2023 strncmp(got_ref_get_name(ref
), "refs/tags/", 10) == 0) {
2024 err
= got_ref_resolve(&old_id
, repo
, ref
);
2027 if (got_object_id_cmp(old_id
, new_id
) == 0)
2029 if (verbosity
>= 0) {
2030 printf("Rejecting update of existing tag %s: %s\n",
2031 got_ref_get_name(ref
), new_id_str
);
2036 if (got_ref_is_symbolic(ref
)) {
2037 if (verbosity
>= 0) {
2038 printf("Replacing reference %s: %s\n",
2039 got_ref_get_name(ref
),
2040 got_ref_get_symref_target(ref
));
2042 err
= got_ref_change_symref_to_ref(ref
, new_id
);
2045 err
= got_ref_write(ref
, repo
);
2049 err
= got_ref_resolve(&old_id
, repo
, ref
);
2052 if (got_object_id_cmp(old_id
, new_id
) == 0)
2055 err
= got_ref_change_ref(ref
, new_id
);
2058 err
= got_ref_write(ref
, repo
);
2064 printf("Updated %s: %s\n", got_ref_get_name(ref
),
2072 static const struct got_error
*
2073 update_symref(const char *refname
, struct got_reference
*target_ref
,
2074 int verbosity
, struct got_repository
*repo
)
2076 const struct got_error
*err
= NULL
, *unlock_err
;
2077 struct got_reference
*symref
;
2078 int symref_is_locked
= 0;
2080 err
= got_ref_open(&symref
, repo
, refname
, 1);
2082 if (err
->code
!= GOT_ERR_NOT_REF
)
2084 err
= got_ref_alloc_symref(&symref
, refname
, target_ref
);
2088 err
= got_ref_write(symref
, repo
);
2093 printf("Created reference %s: %s\n",
2094 got_ref_get_name(symref
),
2095 got_ref_get_symref_target(symref
));
2097 symref_is_locked
= 1;
2099 if (strcmp(got_ref_get_symref_target(symref
),
2100 got_ref_get_name(target_ref
)) == 0)
2103 err
= got_ref_change_symref(symref
,
2104 got_ref_get_name(target_ref
));
2108 err
= got_ref_write(symref
, repo
);
2113 printf("Updated %s: %s\n", got_ref_get_name(symref
),
2114 got_ref_get_symref_target(symref
));
2118 if (symref_is_locked
) {
2119 unlock_err
= got_ref_unlock(symref
);
2120 if (unlock_err
&& err
== NULL
)
2123 got_ref_close(symref
);
2130 fprintf(stderr
, "usage: %s fetch [-adlqtvX] [-b branch] "
2131 "[-R reference] [-r repository-path] [remote-repository]\n",
2136 static const struct got_error
*
2137 delete_missing_ref(struct got_reference
*ref
,
2138 int verbosity
, struct got_repository
*repo
)
2140 const struct got_error
*err
= NULL
;
2141 struct got_object_id
*id
= NULL
;
2142 char *id_str
= NULL
;
2144 if (got_ref_is_symbolic(ref
)) {
2145 err
= got_ref_delete(ref
, repo
);
2148 if (verbosity
>= 0) {
2149 printf("Deleted %s: %s\n",
2150 got_ref_get_name(ref
),
2151 got_ref_get_symref_target(ref
));
2154 err
= got_ref_resolve(&id
, repo
, ref
);
2157 err
= got_object_id_str(&id_str
, id
);
2161 err
= got_ref_delete(ref
, repo
);
2164 if (verbosity
>= 0) {
2165 printf("Deleted %s: %s\n",
2166 got_ref_get_name(ref
), id_str
);
2175 static const struct got_error
*
2176 delete_missing_refs(struct got_pathlist_head
*their_refs
,
2177 struct got_pathlist_head
*their_symrefs
,
2178 const struct got_remote_repo
*remote
,
2179 int verbosity
, struct got_repository
*repo
)
2181 const struct got_error
*err
= NULL
, *unlock_err
;
2182 struct got_reflist_head my_refs
;
2183 struct got_reflist_entry
*re
;
2184 struct got_pathlist_entry
*pe
;
2185 char *remote_namespace
= NULL
;
2186 char *local_refname
= NULL
;
2188 TAILQ_INIT(&my_refs
);
2190 if (asprintf(&remote_namespace
, "refs/remotes/%s/", remote
->name
)
2192 return got_error_from_errno("asprintf");
2194 err
= got_ref_list(&my_refs
, repo
, NULL
, got_ref_cmp_by_name
, NULL
);
2198 TAILQ_FOREACH(re
, &my_refs
, entry
) {
2199 const char *refname
= got_ref_get_name(re
->ref
);
2200 const char *their_refname
;
2202 if (remote
->mirror_references
) {
2203 their_refname
= refname
;
2205 if (strncmp(refname
, remote_namespace
,
2206 strlen(remote_namespace
)) == 0) {
2207 if (strcmp(refname
+ strlen(remote_namespace
),
2210 if (asprintf(&local_refname
, "refs/heads/%s",
2211 refname
+ strlen(remote_namespace
)) == -1) {
2212 err
= got_error_from_errno("asprintf");
2215 } else if (strncmp(refname
, "refs/tags/", 10) != 0)
2218 their_refname
= local_refname
;
2221 TAILQ_FOREACH(pe
, their_refs
, entry
) {
2222 if (strcmp(their_refname
, pe
->path
) == 0)
2228 TAILQ_FOREACH(pe
, their_symrefs
, entry
) {
2229 if (strcmp(their_refname
, pe
->path
) == 0)
2235 err
= delete_missing_ref(re
->ref
, verbosity
, repo
);
2239 if (local_refname
) {
2240 struct got_reference
*ref
;
2241 err
= got_ref_open(&ref
, repo
, local_refname
, 1);
2243 if (err
->code
!= GOT_ERR_NOT_REF
)
2245 free(local_refname
);
2246 local_refname
= NULL
;
2249 err
= delete_missing_ref(ref
, verbosity
, repo
);
2252 unlock_err
= got_ref_unlock(ref
);
2254 if (unlock_err
&& err
== NULL
) {
2259 free(local_refname
);
2260 local_refname
= NULL
;
2264 got_ref_list_free(&my_refs
);
2265 free(remote_namespace
);
2266 free(local_refname
);
2270 static const struct got_error
*
2271 update_wanted_ref(const char *refname
, struct got_object_id
*id
,
2272 const char *remote_repo_name
, int verbosity
, struct got_repository
*repo
)
2274 const struct got_error
*err
, *unlock_err
;
2275 char *remote_refname
;
2276 struct got_reference
*ref
;
2278 if (strncmp("refs/", refname
, 5) == 0)
2281 if (asprintf(&remote_refname
, "refs/remotes/%s/%s",
2282 remote_repo_name
, refname
) == -1)
2283 return got_error_from_errno("asprintf");
2285 err
= got_ref_open(&ref
, repo
, remote_refname
, 1);
2287 if (err
->code
!= GOT_ERR_NOT_REF
)
2289 err
= create_ref(remote_refname
, id
, verbosity
, repo
);
2291 err
= update_ref(ref
, id
, 0, verbosity
, repo
);
2292 unlock_err
= got_ref_unlock(ref
);
2293 if (unlock_err
&& err
== NULL
)
2298 free(remote_refname
);
2302 static const struct got_error
*
2303 delete_ref(struct got_repository
*repo
, struct got_reference
*ref
)
2305 const struct got_error
*err
= NULL
;
2306 struct got_object_id
*id
= NULL
;
2307 char *id_str
= NULL
;
2310 if (got_ref_is_symbolic(ref
)) {
2311 target
= got_ref_get_symref_target(ref
);
2313 err
= got_ref_resolve(&id
, repo
, ref
);
2316 err
= got_object_id_str(&id_str
, id
);
2322 err
= got_ref_delete(ref
, repo
);
2326 printf("Deleted %s: %s\n", got_ref_get_name(ref
), target
);
2333 static const struct got_error
*
2334 delete_refs_for_remote(struct got_repository
*repo
, const char *remote_name
)
2336 const struct got_error
*err
= NULL
;
2337 struct got_reflist_head refs
;
2338 struct got_reflist_entry
*re
;
2343 if (asprintf(&prefix
, "refs/remotes/%s", remote_name
) == -1) {
2344 err
= got_error_from_errno("asprintf");
2347 err
= got_ref_list(&refs
, repo
, prefix
, got_ref_cmp_by_name
, NULL
);
2351 TAILQ_FOREACH(re
, &refs
, entry
)
2352 delete_ref(repo
, re
->ref
);
2354 got_ref_list_free(&refs
);
2358 static const struct got_error
*
2359 cmd_fetch(int argc
, char *argv
[])
2361 const struct got_error
*error
= NULL
, *unlock_err
;
2362 char *cwd
= NULL
, *repo_path
= NULL
;
2363 const char *remote_name
;
2364 char *proto
= NULL
, *host
= NULL
, *port
= NULL
;
2365 char *repo_name
= NULL
, *server_path
= NULL
;
2366 const struct got_remote_repo
*remotes
;
2367 struct got_remote_repo
*remote
= NULL
;
2369 char *id_str
= NULL
;
2370 struct got_repository
*repo
= NULL
;
2371 struct got_worktree
*worktree
= NULL
;
2372 const struct got_gotconfig
*repo_conf
= NULL
, *worktree_conf
= NULL
;
2373 struct got_pathlist_head refs
, symrefs
, wanted_branches
, wanted_refs
;
2374 char *head_refname
= NULL
;
2375 struct got_pathlist_entry
*pe
;
2376 struct got_reflist_head remote_refs
;
2377 struct got_reflist_entry
*re
;
2378 struct got_object_id
*pack_hash
= NULL
;
2379 int i
, ch
, fetchfd
= -1, fetchstatus
;
2380 pid_t fetchpid
= -1;
2381 struct got_fetch_progress_arg fpa
;
2382 int verbosity
= 0, fetch_all_branches
= 0, list_refs_only
= 0;
2383 int delete_refs
= 0, replace_tags
= 0, delete_remote
= 0;
2384 int *pack_fds
= NULL
, have_bflag
= 0;
2385 const char *remote_head
= NULL
, *worktree_branch
= NULL
;
2388 TAILQ_INIT(&symrefs
);
2389 TAILQ_INIT(&remote_refs
);
2390 TAILQ_INIT(&wanted_branches
);
2391 TAILQ_INIT(&wanted_refs
);
2393 while ((ch
= getopt(argc
, argv
, "ab:dlqR:r:tvX")) != -1) {
2396 fetch_all_branches
= 1;
2399 error
= got_pathlist_insert(NULL
, &wanted_branches
,
2415 error
= got_pathlist_insert(NULL
, &wanted_refs
,
2421 repo_path
= realpath(optarg
, NULL
);
2422 if (repo_path
== NULL
)
2423 return got_error_from_errno2("realpath",
2425 got_path_strip_trailing_slashes(repo_path
);
2433 else if (verbosity
< 3)
2447 if (fetch_all_branches
&& !TAILQ_EMPTY(&wanted_branches
))
2448 option_conflict('a', 'b');
2449 if (list_refs_only
) {
2450 if (!TAILQ_EMPTY(&wanted_branches
))
2451 option_conflict('l', 'b');
2452 if (fetch_all_branches
)
2453 option_conflict('l', 'a');
2455 option_conflict('l', 'd');
2457 option_conflict('l', 'X');
2459 if (delete_remote
) {
2460 if (fetch_all_branches
)
2461 option_conflict('X', 'a');
2462 if (!TAILQ_EMPTY(&wanted_branches
))
2463 option_conflict('X', 'b');
2465 option_conflict('X', 'd');
2467 option_conflict('X', 't');
2468 if (!TAILQ_EMPTY(&wanted_refs
))
2469 option_conflict('X', 'R');
2474 errx(1, "-X option requires a remote name");
2475 remote_name
= GOT_FETCH_DEFAULT_REMOTE_NAME
;
2476 } else if (argc
== 1)
2477 remote_name
= argv
[0];
2481 cwd
= getcwd(NULL
, 0);
2483 error
= got_error_from_errno("getcwd");
2487 error
= got_repo_pack_fds_open(&pack_fds
);
2491 if (repo_path
== NULL
) {
2492 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_GOT_DIR
);
2493 if (error
&& error
->code
!= GOT_ERR_NOT_WORKTREE
)
2499 strdup(got_worktree_get_repo_path(worktree
));
2500 if (repo_path
== NULL
)
2501 error
= got_error_from_errno("strdup");
2505 repo_path
= strdup(cwd
);
2506 if (repo_path
== NULL
) {
2507 error
= got_error_from_errno("strdup");
2513 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
2517 if (delete_remote
) {
2518 error
= delete_refs_for_remote(repo
, remote_name
);
2519 goto done
; /* nothing else to do */
2523 worktree_conf
= got_worktree_get_gotconfig(worktree
);
2524 if (worktree_conf
) {
2525 got_gotconfig_get_remotes(&nremotes
, &remotes
,
2527 for (i
= 0; i
< nremotes
; i
++) {
2528 if (strcmp(remotes
[i
].name
, remote_name
) == 0) {
2529 error
= got_repo_remote_repo_dup(&remote
,
2538 if (remote
== NULL
) {
2539 repo_conf
= got_repo_get_gotconfig(repo
);
2541 got_gotconfig_get_remotes(&nremotes
, &remotes
,
2543 for (i
= 0; i
< nremotes
; i
++) {
2544 if (strcmp(remotes
[i
].name
, remote_name
) == 0) {
2545 error
= got_repo_remote_repo_dup(&remote
,
2554 if (remote
== NULL
) {
2555 got_repo_get_gitconfig_remotes(&nremotes
, &remotes
, repo
);
2556 for (i
= 0; i
< nremotes
; i
++) {
2557 if (strcmp(remotes
[i
].name
, remote_name
) == 0) {
2558 error
= got_repo_remote_repo_dup(&remote
,
2566 if (remote
== NULL
) {
2567 error
= got_error_path(remote_name
, GOT_ERR_NO_REMOTE
);
2571 if (TAILQ_EMPTY(&wanted_branches
)) {
2572 if (!fetch_all_branches
)
2573 fetch_all_branches
= remote
->fetch_all_branches
;
2574 for (i
= 0; i
< remote
->nfetch_branches
; i
++) {
2575 error
= got_pathlist_insert(NULL
, &wanted_branches
,
2576 remote
->fetch_branches
[i
], NULL
);
2581 if (TAILQ_EMPTY(&wanted_refs
)) {
2582 for (i
= 0; i
< remote
->nfetch_refs
; i
++) {
2583 error
= got_pathlist_insert(NULL
, &wanted_refs
,
2584 remote
->fetch_refs
[i
], NULL
);
2590 error
= got_dial_parse_uri(&proto
, &host
, &port
, &server_path
,
2591 &repo_name
, remote
->fetch_url
);
2595 if (strcmp(proto
, "git") == 0) {
2597 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
2598 "sendfd dns inet unveil", NULL
) == -1)
2601 } else if (strcmp(proto
, "git+ssh") == 0 ||
2602 strcmp(proto
, "ssh") == 0 ||
2603 strcmp(proto
, "git+http") == 0 ||
2604 strcmp(proto
, "http") == 0 ||
2605 strcmp(proto
, "git+https") == 0 ||
2606 strcmp(proto
, "https") == 0) {
2608 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
2609 "sendfd unveil", NULL
) == -1)
2613 error
= got_error_path(proto
, GOT_ERR_BAD_PROTO
);
2617 error
= got_dial_apply_unveil(proto
);
2621 error
= apply_unveil(got_repo_get_path(repo
), 0, NULL
);
2626 head_refname
= strdup(got_worktree_get_head_ref_name(worktree
));
2627 if (head_refname
== NULL
) {
2628 error
= got_error_from_errno("strdup");
2632 /* Release work tree lock. */
2633 got_worktree_close(worktree
);
2637 if (verbosity
>= 0) {
2638 printf("Connecting to \"%s\" %s://%s%s%s%s%s\n",
2639 remote
->name
, proto
, host
,
2640 port
? ":" : "", port
? port
: "",
2641 *server_path
== '/' ? "" : "/", server_path
);
2644 error
= got_fetch_connect(&fetchpid
, &fetchfd
, proto
, host
, port
,
2645 server_path
, verbosity
);
2649 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd",
2655 * If set, get this remote's HEAD ref target so
2656 * if it has changed on the server we can fetch it.
2658 error
= got_ref_list(&remote_refs
, repo
, "refs/remotes",
2659 got_ref_cmp_by_name
, repo
);
2663 TAILQ_FOREACH(re
, &remote_refs
, entry
) {
2664 const char *remote_refname
, *remote_target
;
2665 size_t remote_name_len
;
2667 if (!got_ref_is_symbolic(re
->ref
))
2670 remote_name_len
= strlen(remote
->name
);
2671 remote_refname
= got_ref_get_name(re
->ref
);
2673 /* we only want refs/remotes/$remote->name/HEAD */
2674 if (strncmp(remote_refname
+ 13, remote
->name
,
2675 remote_name_len
) != 0)
2678 if (strcmp(remote_refname
+ remote_name_len
+ 14,
2683 * Take the name itself because we already
2684 * only match with refs/heads/ in fetch_pack().
2686 remote_target
= got_ref_get_symref_target(re
->ref
);
2687 remote_head
= remote_target
+ remote_name_len
+ 14;
2692 strncmp(head_refname
, "refs/heads/", 11) == 0)
2693 worktree_branch
= head_refname
;
2696 fpa
.last_scaled_size
[0] = '\0';
2697 fpa
.last_p_indexed
= -1;
2698 fpa
.last_p_resolved
= -1;
2699 fpa
.verbosity
= verbosity
;
2701 fpa
.create_configs
= 0;
2702 fpa
.configs_created
= 0;
2703 memset(&fpa
.config_info
, 0, sizeof(fpa
.config_info
));
2705 error
= got_fetch_pack(&pack_hash
, &refs
, &symrefs
, remote
->name
,
2706 remote
->mirror_references
, fetch_all_branches
, &wanted_branches
,
2707 &wanted_refs
, list_refs_only
, verbosity
, fetchfd
, repo
,
2708 worktree_branch
, remote_head
, have_bflag
, fetch_progress
, &fpa
);
2712 if (list_refs_only
) {
2713 error
= list_remote_refs(&symrefs
, &refs
);
2717 if (pack_hash
== NULL
) {
2719 printf("Already up-to-date\n");
2720 } else if (verbosity
>= 0) {
2721 error
= got_object_id_str(&id_str
, pack_hash
);
2724 printf("\nFetched %s.pack\n", id_str
);
2729 /* Update references provided with the pack file. */
2730 TAILQ_FOREACH(pe
, &refs
, entry
) {
2731 const char *refname
= pe
->path
;
2732 struct got_object_id
*id
= pe
->data
;
2733 struct got_reference
*ref
;
2734 char *remote_refname
;
2736 if (is_wanted_ref(&wanted_refs
, refname
) &&
2737 !remote
->mirror_references
) {
2738 error
= update_wanted_ref(refname
, id
,
2739 remote
->name
, verbosity
, repo
);
2745 if (remote
->mirror_references
||
2746 strncmp("refs/tags/", refname
, 10) == 0) {
2747 error
= got_ref_open(&ref
, repo
, refname
, 1);
2749 if (error
->code
!= GOT_ERR_NOT_REF
)
2751 error
= create_ref(refname
, id
, verbosity
,
2756 error
= update_ref(ref
, id
, replace_tags
,
2758 unlock_err
= got_ref_unlock(ref
);
2759 if (unlock_err
&& error
== NULL
)
2765 } else if (strncmp("refs/heads/", refname
, 11) == 0) {
2766 if (asprintf(&remote_refname
, "refs/remotes/%s/%s",
2767 remote_name
, refname
+ 11) == -1) {
2768 error
= got_error_from_errno("asprintf");
2772 error
= got_ref_open(&ref
, repo
, remote_refname
, 1);
2774 if (error
->code
!= GOT_ERR_NOT_REF
)
2776 error
= create_ref(remote_refname
, id
,
2781 error
= update_ref(ref
, id
, replace_tags
,
2783 unlock_err
= got_ref_unlock(ref
);
2784 if (unlock_err
&& error
== NULL
)
2791 /* Also create a local branch if none exists yet. */
2792 error
= got_ref_open(&ref
, repo
, refname
, 1);
2794 if (error
->code
!= GOT_ERR_NOT_REF
)
2796 error
= create_ref(refname
, id
, verbosity
,
2801 unlock_err
= got_ref_unlock(ref
);
2802 if (unlock_err
&& error
== NULL
)
2809 error
= delete_missing_refs(&refs
, &symrefs
, remote
,
2815 if (!remote
->mirror_references
) {
2816 /* Update remote HEAD reference if the server provided one. */
2817 TAILQ_FOREACH(pe
, &symrefs
, entry
) {
2818 struct got_reference
*target_ref
;
2819 const char *refname
= pe
->path
;
2820 const char *target
= pe
->data
;
2821 char *remote_refname
= NULL
, *remote_target
= NULL
;
2823 if (strcmp(refname
, GOT_REF_HEAD
) != 0)
2826 if (strncmp("refs/heads/", target
, 11) != 0)
2829 if (asprintf(&remote_refname
, "refs/remotes/%s/%s",
2830 remote
->name
, refname
) == -1) {
2831 error
= got_error_from_errno("asprintf");
2834 if (asprintf(&remote_target
, "refs/remotes/%s/%s",
2835 remote
->name
, target
+ 11) == -1) {
2836 error
= got_error_from_errno("asprintf");
2837 free(remote_refname
);
2841 error
= got_ref_open(&target_ref
, repo
, remote_target
,
2844 free(remote_refname
);
2845 free(remote_target
);
2846 if (error
->code
== GOT_ERR_NOT_REF
) {
2852 error
= update_symref(remote_refname
, target_ref
,
2854 free(remote_refname
);
2855 free(remote_target
);
2856 got_ref_close(target_ref
);
2863 if (kill(fetchpid
, SIGTERM
) == -1)
2864 error
= got_error_from_errno("kill");
2865 if (waitpid(fetchpid
, &fetchstatus
, 0) == -1 && error
== NULL
)
2866 error
= got_error_from_errno("waitpid");
2868 if (fetchfd
!= -1 && close(fetchfd
) == -1 && error
== NULL
)
2869 error
= got_error_from_errno("close");
2871 const struct got_error
*close_err
= got_repo_close(repo
);
2876 got_worktree_close(worktree
);
2878 const struct got_error
*pack_err
=
2879 got_repo_pack_fds_close(pack_fds
);
2883 got_pathlist_free(&refs
, GOT_PATHLIST_FREE_ALL
);
2884 got_pathlist_free(&symrefs
, GOT_PATHLIST_FREE_ALL
);
2885 got_pathlist_free(&wanted_branches
, GOT_PATHLIST_FREE_NONE
);
2886 got_pathlist_free(&wanted_refs
, GOT_PATHLIST_FREE_NONE
);
2887 got_ref_list_free(&remote_refs
);
2888 got_repo_free_remote_repo_data(remote
);
2905 usage_checkout(void)
2907 fprintf(stderr
, "usage: %s checkout [-Eq] [-b branch] [-c commit] "
2908 "[-p path-prefix] repository-path [work-tree-path]\n",
2914 show_worktree_base_ref_warning(void)
2916 fprintf(stderr
, "%s: warning: could not create a reference "
2917 "to the work tree's base commit; the commit could be "
2918 "garbage-collected by Git or 'gotadmin cleanup'; making the "
2919 "repository writable and running 'got update' will prevent this\n",
2923 struct got_checkout_progress_arg
{
2924 const char *worktree_path
;
2925 int had_base_commit_ref_error
;
2929 static const struct got_error
*
2930 checkout_progress(void *arg
, unsigned char status
, const char *path
)
2932 struct got_checkout_progress_arg
*a
= arg
;
2934 /* Base commit bump happens silently. */
2935 if (status
== GOT_STATUS_BUMP_BASE
)
2938 if (status
== GOT_STATUS_BASE_REF_ERR
) {
2939 a
->had_base_commit_ref_error
= 1;
2943 while (path
[0] == '/')
2946 if (a
->verbosity
>= 0)
2947 printf("%c %s/%s\n", status
, a
->worktree_path
, path
);
2952 static const struct got_error
*
2953 check_cancelled(void *arg
)
2955 if (sigint_received
|| sigpipe_received
)
2956 return got_error(GOT_ERR_CANCELLED
);
2960 static const struct got_error
*
2961 check_linear_ancestry(struct got_object_id
*commit_id
,
2962 struct got_object_id
*base_commit_id
, int allow_forwards_in_time_only
,
2963 struct got_repository
*repo
)
2965 const struct got_error
*err
= NULL
;
2966 struct got_object_id
*yca_id
;
2968 err
= got_commit_graph_find_youngest_common_ancestor(&yca_id
,
2969 commit_id
, base_commit_id
, 1, 0, repo
, check_cancelled
, NULL
);
2974 return got_error(GOT_ERR_ANCESTRY
);
2977 * Require a straight line of history between the target commit
2978 * and the work tree's base commit.
2980 * Non-linear situations such as this require a rebase:
2982 * (commit) D F (base_commit)
2990 * 'got update' only handles linear cases:
2991 * Update forwards in time: A (base/yca) - B - C - D (commit)
2992 * Update backwards in time: D (base) - C - B - A (commit/yca)
2994 if (allow_forwards_in_time_only
) {
2995 if (got_object_id_cmp(base_commit_id
, yca_id
) != 0)
2996 return got_error(GOT_ERR_ANCESTRY
);
2997 } else if (got_object_id_cmp(commit_id
, yca_id
) != 0 &&
2998 got_object_id_cmp(base_commit_id
, yca_id
) != 0)
2999 return got_error(GOT_ERR_ANCESTRY
);
3005 static const struct got_error
*
3006 check_same_branch(struct got_object_id
*commit_id
,
3007 struct got_reference
*head_ref
, struct got_repository
*repo
)
3009 const struct got_error
*err
= NULL
;
3010 struct got_commit_graph
*graph
= NULL
;
3011 struct got_object_id
*head_commit_id
= NULL
;
3013 err
= got_ref_resolve(&head_commit_id
, repo
, head_ref
);
3017 if (got_object_id_cmp(head_commit_id
, commit_id
) == 0)
3020 err
= got_commit_graph_open(&graph
, "/", 1);
3024 err
= got_commit_graph_bfsort(graph
, head_commit_id
, repo
,
3025 check_cancelled
, NULL
);
3030 struct got_object_id id
;
3032 err
= got_commit_graph_iter_next(&id
, graph
, repo
,
3033 check_cancelled
, NULL
);
3035 if (err
->code
== GOT_ERR_ITER_COMPLETED
)
3036 err
= got_error(GOT_ERR_ANCESTRY
);
3040 if (got_object_id_cmp(&id
, commit_id
) == 0)
3045 got_commit_graph_close(graph
);
3046 free(head_commit_id
);
3050 static const struct got_error
*
3051 checkout_ancestry_error(struct got_reference
*ref
, const char *commit_id_str
)
3053 static char msg
[512];
3054 const char *branch_name
;
3056 if (got_ref_is_symbolic(ref
))
3057 branch_name
= got_ref_get_symref_target(ref
);
3059 branch_name
= got_ref_get_name(ref
);
3061 if (strncmp("refs/heads/", branch_name
, 11) == 0)
3064 snprintf(msg
, sizeof(msg
),
3065 "target commit is not contained in branch '%s'; "
3066 "the branch to use must be specified with -b; "
3067 "if necessary a new branch can be created for "
3068 "this commit with 'got branch -c %s BRANCH_NAME'",
3069 branch_name
, commit_id_str
);
3071 return got_error_msg(GOT_ERR_ANCESTRY
, msg
);
3074 static const struct got_error
*
3075 cmd_checkout(int argc
, char *argv
[])
3077 const struct got_error
*close_err
, *error
= NULL
;
3078 struct got_repository
*repo
= NULL
;
3079 struct got_reference
*head_ref
= NULL
, *ref
= NULL
;
3080 struct got_worktree
*worktree
= NULL
;
3081 char *repo_path
= NULL
;
3082 char *worktree_path
= NULL
;
3083 const char *path_prefix
= "";
3084 const char *branch_name
= GOT_REF_HEAD
, *refname
= NULL
;
3085 char *commit_id_str
= NULL
, *keyword_idstr
= NULL
;
3086 struct got_object_id
*commit_id
= NULL
;
3088 int ch
, same_path_prefix
, allow_nonempty
= 0, verbosity
= 0;
3089 struct got_pathlist_head paths
;
3090 struct got_checkout_progress_arg cpa
;
3091 int *pack_fds
= NULL
;
3096 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
3097 "unveil", NULL
) == -1)
3101 while ((ch
= getopt(argc
, argv
, "b:c:Ep:q")) != -1) {
3104 branch_name
= optarg
;
3107 commit_id_str
= strdup(optarg
);
3108 if (commit_id_str
== NULL
)
3109 return got_error_from_errno("strdup");
3115 path_prefix
= optarg
;
3130 char *base
, *dotgit
;
3132 repo_path
= realpath(argv
[0], NULL
);
3133 if (repo_path
== NULL
)
3134 return got_error_from_errno2("realpath", argv
[0]);
3135 cwd
= getcwd(NULL
, 0);
3137 error
= got_error_from_errno("getcwd");
3144 error
= got_path_basename(&base
, path
);
3147 dotgit
= strstr(base
, ".git");
3150 if (asprintf(&worktree_path
, "%s/%s", cwd
, base
) == -1) {
3151 error
= got_error_from_errno("asprintf");
3156 } else if (argc
== 2) {
3157 repo_path
= realpath(argv
[0], NULL
);
3158 if (repo_path
== NULL
) {
3159 error
= got_error_from_errno2("realpath", argv
[0]);
3162 worktree_path
= realpath(argv
[1], NULL
);
3163 if (worktree_path
== NULL
) {
3164 if (errno
!= ENOENT
) {
3165 error
= got_error_from_errno2("realpath",
3169 worktree_path
= strdup(argv
[1]);
3170 if (worktree_path
== NULL
) {
3171 error
= got_error_from_errno("strdup");
3178 got_path_strip_trailing_slashes(repo_path
);
3179 got_path_strip_trailing_slashes(worktree_path
);
3181 if (got_path_is_child(worktree_path
, repo_path
, strlen(repo_path
)) ||
3182 got_path_is_child(repo_path
, worktree_path
,
3183 strlen(worktree_path
))) {
3184 error
= got_error_fmt(GOT_ERR_BAD_PATH
,
3185 "work tree and repository paths may not overlap: %s",
3190 error
= got_repo_pack_fds_open(&pack_fds
);
3194 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
3198 /* Pre-create work tree path for unveil(2) */
3199 error
= got_path_mkdir(worktree_path
);
3201 if (!(error
->code
== GOT_ERR_ERRNO
&& errno
== EISDIR
) &&
3202 !(error
->code
== GOT_ERR_ERRNO
&& errno
== EEXIST
))
3204 if (!allow_nonempty
&&
3205 !got_path_dir_is_empty(worktree_path
)) {
3206 error
= got_error_path(worktree_path
,
3207 GOT_ERR_DIR_NOT_EMPTY
);
3212 error
= apply_unveil(got_repo_get_path(repo
), 0, worktree_path
);
3216 error
= got_ref_open(&head_ref
, repo
, branch_name
, 0);
3220 error
= got_worktree_init(worktree_path
, head_ref
, path_prefix
,
3221 GOT_WORKTREE_GOT_DIR
, repo
);
3222 if (error
!= NULL
&& !(error
->code
== GOT_ERR_ERRNO
&& errno
== EEXIST
))
3225 error
= got_worktree_open(&worktree
, worktree_path
,
3226 GOT_WORKTREE_GOT_DIR
);
3230 error
= got_worktree_match_path_prefix(&same_path_prefix
, worktree
,
3234 if (!same_path_prefix
) {
3235 error
= got_error(GOT_ERR_PATH_PREFIX
);
3239 if (commit_id_str
) {
3240 struct got_reflist_head refs
;
3242 error
= got_ref_list(&refs
, repo
, NULL
, got_ref_cmp_by_name
,
3247 error
= got_keyword_to_idstr(&keyword_idstr
, commit_id_str
,
3251 if (keyword_idstr
!= NULL
) {
3252 free(commit_id_str
);
3253 commit_id_str
= keyword_idstr
;
3256 error
= got_repo_match_object_id(&commit_id
, NULL
,
3257 commit_id_str
, GOT_OBJ_TYPE_COMMIT
, &refs
, repo
);
3258 got_ref_list_free(&refs
);
3261 error
= check_linear_ancestry(commit_id
,
3262 got_worktree_get_base_commit_id(worktree
), 0, repo
);
3263 if (error
!= NULL
) {
3264 if (error
->code
== GOT_ERR_ANCESTRY
) {
3265 error
= checkout_ancestry_error(
3266 head_ref
, commit_id_str
);
3270 error
= check_same_branch(commit_id
, head_ref
, repo
);
3272 if (error
->code
== GOT_ERR_ANCESTRY
) {
3273 error
= checkout_ancestry_error(
3274 head_ref
, commit_id_str
);
3278 error
= got_worktree_set_base_commit_id(worktree
, repo
,
3282 /* Expand potentially abbreviated commit ID string. */
3283 free(commit_id_str
);
3284 error
= got_object_id_str(&commit_id_str
, commit_id
);
3288 commit_id
= got_object_id_dup(
3289 got_worktree_get_base_commit_id(worktree
));
3290 if (commit_id
== NULL
) {
3291 error
= got_error_from_errno("got_object_id_dup");
3294 error
= got_object_id_str(&commit_id_str
, commit_id
);
3299 error
= got_pathlist_insert(NULL
, &paths
, "", NULL
);
3302 cpa
.worktree_path
= worktree_path
;
3303 cpa
.had_base_commit_ref_error
= 0;
3304 cpa
.verbosity
= verbosity
;
3305 error
= got_worktree_checkout_files(worktree
, &paths
, repo
,
3306 checkout_progress
, &cpa
, check_cancelled
, NULL
);
3310 if (got_ref_is_symbolic(head_ref
)) {
3311 error
= got_ref_resolve_symbolic(&ref
, repo
, head_ref
);
3314 refname
= got_ref_get_name(ref
);
3316 refname
= got_ref_get_name(head_ref
);
3317 printf("Checked out %s: %s\n", refname
, commit_id_str
);
3318 printf("Now shut up and hack\n");
3319 if (cpa
.had_base_commit_ref_error
)
3320 show_worktree_base_ref_warning();
3323 const struct got_error
*pack_err
=
3324 got_repo_pack_fds_close(pack_fds
);
3329 got_ref_close(head_ref
);
3333 close_err
= got_repo_close(repo
);
3337 if (worktree
!= NULL
) {
3338 close_err
= got_worktree_close(worktree
);
3342 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_NONE
);
3343 free(commit_id_str
);
3346 free(worktree_path
);
3351 struct got_update_progress_arg
{
3363 print_update_progress_stats(struct got_update_progress_arg
*upa
)
3365 if (!upa
->did_something
)
3368 if (upa
->conflicts
> 0)
3369 printf("Files with new merge conflicts: %d\n", upa
->conflicts
);
3370 if (upa
->obstructed
> 0)
3371 printf("File paths obstructed by a non-regular file: %d\n",
3373 if (upa
->not_updated
> 0)
3374 printf("Files not updated because of existing merge "
3375 "conflicts: %d\n", upa
->not_updated
);
3379 * The meaning of some status codes differs between merge-style operations and
3380 * update operations. For example, the ! status code means "file was missing"
3381 * if changes were merged into the work tree, and "missing file was restored"
3382 * if the work tree was updated. This function should be used by any operation
3383 * which merges changes into the work tree without updating the work tree.
3386 print_merge_progress_stats(struct got_update_progress_arg
*upa
)
3388 if (!upa
->did_something
)
3391 if (upa
->conflicts
> 0)
3392 printf("Files with new merge conflicts: %d\n", upa
->conflicts
);
3393 if (upa
->obstructed
> 0)
3394 printf("File paths obstructed by a non-regular file: %d\n",
3396 if (upa
->missing
> 0)
3397 printf("Files which had incoming changes but could not be "
3398 "found in the work tree: %d\n", upa
->missing
);
3399 if (upa
->not_deleted
> 0)
3400 printf("Files not deleted due to differences in deleted "
3401 "content: %d\n", upa
->not_deleted
);
3402 if (upa
->unversioned
> 0)
3403 printf("Files not merged because an unversioned file was "
3404 "found in the work tree: %d\n", upa
->unversioned
);
3410 fprintf(stderr
, "usage: %s update [-q] [-b branch] [-c commit] "
3411 "[path ...]\n", getprogname());
3415 static const struct got_error
*
3416 update_progress(void *arg
, unsigned char status
, const char *path
)
3418 struct got_update_progress_arg
*upa
= arg
;
3420 if (status
== GOT_STATUS_EXISTS
||
3421 status
== GOT_STATUS_BASE_REF_ERR
)
3424 upa
->did_something
= 1;
3426 /* Base commit bump happens silently. */
3427 if (status
== GOT_STATUS_BUMP_BASE
)
3430 if (status
== GOT_STATUS_CONFLICT
)
3432 if (status
== GOT_STATUS_OBSTRUCTED
)
3434 if (status
== GOT_STATUS_CANNOT_UPDATE
)
3436 if (status
== GOT_STATUS_MISSING
)
3438 if (status
== GOT_STATUS_CANNOT_DELETE
)
3440 if (status
== GOT_STATUS_UNVERSIONED
)
3443 while (path
[0] == '/')
3445 if (upa
->verbosity
>= 0)
3446 printf("%c %s\n", status
, path
);
3451 static const struct got_error
*
3452 switch_head_ref(struct got_reference
*head_ref
,
3453 struct got_object_id
*commit_id
, struct got_worktree
*worktree
,
3454 struct got_repository
*repo
)
3456 const struct got_error
*err
= NULL
;
3458 int ref_has_moved
= 0;
3460 /* Trivial case: switching between two different references. */
3461 if (strcmp(got_ref_get_name(head_ref
),
3462 got_worktree_get_head_ref_name(worktree
)) != 0) {
3463 printf("Switching work tree from %s to %s\n",
3464 got_worktree_get_head_ref_name(worktree
),
3465 got_ref_get_name(head_ref
));
3466 return got_worktree_set_head_ref(worktree
, head_ref
);
3469 err
= check_linear_ancestry(commit_id
,
3470 got_worktree_get_base_commit_id(worktree
), 0, repo
);
3472 if (err
->code
!= GOT_ERR_ANCESTRY
)
3479 /* Switching to a rebased branch with the same reference name. */
3480 err
= got_object_id_str(&base_id_str
,
3481 got_worktree_get_base_commit_id(worktree
));
3484 printf("Reference %s now points at a different branch\n",
3485 got_worktree_get_head_ref_name(worktree
));
3486 printf("Switching work tree from %s to %s\n", base_id_str
,
3487 got_worktree_get_head_ref_name(worktree
));
3491 static const struct got_error
*
3492 check_rebase_or_histedit_in_progress(struct got_worktree
*worktree
)
3494 const struct got_error
*err
;
3497 err
= got_worktree_rebase_in_progress(&in_progress
, worktree
);
3501 return got_error(GOT_ERR_REBASING
);
3503 err
= got_worktree_histedit_in_progress(&in_progress
, worktree
);
3507 return got_error(GOT_ERR_HISTEDIT_BUSY
);
3512 static const struct got_error
*
3513 check_merge_in_progress(struct got_worktree
*worktree
,
3514 struct got_repository
*repo
)
3516 const struct got_error
*err
;
3519 err
= got_worktree_merge_in_progress(&in_progress
, worktree
, repo
);
3523 return got_error(GOT_ERR_MERGE_BUSY
);
3528 static const struct got_error
*
3529 get_worktree_paths_from_argv(struct got_pathlist_head
*paths
, int argc
,
3530 char *argv
[], struct got_worktree
*worktree
)
3532 const struct got_error
*err
= NULL
;
3534 struct got_pathlist_entry
*new;
3540 return got_error_from_errno("strdup");
3541 return got_pathlist_insert(NULL
, paths
, path
, NULL
);
3544 for (i
= 0; i
< argc
; i
++) {
3545 err
= got_worktree_resolve_path(&path
, worktree
, argv
[i
]);
3548 err
= got_pathlist_insert(&new, paths
, path
, NULL
);
3549 if (err
|| new == NULL
/* duplicate */) {
3559 static const struct got_error
*
3560 wrap_not_worktree_error(const struct got_error
*orig_err
,
3561 const char *cmdname
, const char *path
)
3563 const struct got_error
*err
;
3564 struct got_repository
*repo
;
3565 static char msg
[512];
3566 int *pack_fds
= NULL
;
3568 err
= got_repo_pack_fds_open(&pack_fds
);
3572 err
= got_repo_open(&repo
, path
, NULL
, pack_fds
);
3576 snprintf(msg
, sizeof(msg
),
3577 "'got %s' needs a work tree in addition to a git repository\n"
3578 "Work trees can be checked out from this Git repository with "
3580 "The got(1) manual page contains more information.", cmdname
);
3581 err
= got_error_msg(GOT_ERR_NOT_WORKTREE
, msg
);
3583 const struct got_error
*close_err
= got_repo_close(repo
);
3588 const struct got_error
*pack_err
=
3589 got_repo_pack_fds_close(pack_fds
);
3596 static const struct got_error
*
3597 cmd_update(int argc
, char *argv
[])
3599 const struct got_error
*close_err
, *error
= NULL
;
3600 struct got_repository
*repo
= NULL
;
3601 struct got_worktree
*worktree
= NULL
;
3602 char *worktree_path
= NULL
;
3603 struct got_object_id
*commit_id
= NULL
;
3604 char *commit_id_str
= NULL
;
3605 const char *branch_name
= NULL
;
3606 struct got_reference
*head_ref
= NULL
;
3607 struct got_pathlist_head paths
;
3608 struct got_pathlist_entry
*pe
;
3609 int ch
, verbosity
= 0;
3610 struct got_update_progress_arg upa
;
3611 int *pack_fds
= NULL
;
3616 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
3617 "unveil", NULL
) == -1)
3621 while ((ch
= getopt(argc
, argv
, "b:c:q")) != -1) {
3624 branch_name
= optarg
;
3627 commit_id_str
= strdup(optarg
);
3628 if (commit_id_str
== NULL
)
3629 return got_error_from_errno("strdup");
3643 worktree_path
= getcwd(NULL
, 0);
3644 if (worktree_path
== NULL
) {
3645 error
= got_error_from_errno("getcwd");
3649 error
= got_repo_pack_fds_open(&pack_fds
);
3653 error
= got_worktree_open(&worktree
, worktree_path
,
3654 GOT_WORKTREE_GOT_DIR
);
3656 if (error
->code
== GOT_ERR_NOT_WORKTREE
)
3657 error
= wrap_not_worktree_error(error
, "update",
3662 error
= check_rebase_or_histedit_in_progress(worktree
);
3666 error
= got_repo_open(&repo
, got_worktree_get_repo_path(worktree
),
3671 error
= apply_unveil(got_repo_get_path(repo
), 0,
3672 got_worktree_get_root_path(worktree
));
3676 error
= check_merge_in_progress(worktree
, repo
);
3680 error
= get_worktree_paths_from_argv(&paths
, argc
, argv
, worktree
);
3684 error
= got_ref_open(&head_ref
, repo
, branch_name
? branch_name
:
3685 got_worktree_get_head_ref_name(worktree
), 0);
3688 if (commit_id_str
== NULL
) {
3689 error
= got_ref_resolve(&commit_id
, repo
, head_ref
);
3692 error
= got_object_id_str(&commit_id_str
, commit_id
);
3696 struct got_reflist_head refs
;
3697 char *keyword_idstr
= NULL
;
3701 error
= got_ref_list(&refs
, repo
, NULL
, got_ref_cmp_by_name
,
3706 error
= got_keyword_to_idstr(&keyword_idstr
, commit_id_str
,
3710 if (keyword_idstr
!= NULL
) {
3711 free(commit_id_str
);
3712 commit_id_str
= keyword_idstr
;
3715 error
= got_repo_match_object_id(&commit_id
, NULL
,
3716 commit_id_str
, GOT_OBJ_TYPE_COMMIT
, &refs
, repo
);
3717 got_ref_list_free(&refs
);
3718 free(commit_id_str
);
3719 commit_id_str
= NULL
;
3722 error
= got_object_id_str(&commit_id_str
, commit_id
);
3728 struct got_object_id
*head_commit_id
;
3729 TAILQ_FOREACH(pe
, &paths
, entry
) {
3730 if (pe
->path_len
== 0)
3732 error
= got_error_msg(GOT_ERR_BAD_PATH
,
3733 "switching between branches requires that "
3734 "the entire work tree gets updated");
3737 error
= got_ref_resolve(&head_commit_id
, repo
, head_ref
);
3740 error
= check_linear_ancestry(commit_id
, head_commit_id
, 0,
3742 free(head_commit_id
);
3745 error
= check_same_branch(commit_id
, head_ref
, repo
);
3748 error
= switch_head_ref(head_ref
, commit_id
, worktree
, repo
);
3752 error
= check_linear_ancestry(commit_id
,
3753 got_worktree_get_base_commit_id(worktree
), 0, repo
);
3754 if (error
!= NULL
) {
3755 if (error
->code
== GOT_ERR_ANCESTRY
)
3756 error
= got_error(GOT_ERR_BRANCH_MOVED
);
3759 error
= check_same_branch(commit_id
, head_ref
, repo
);
3764 if (got_object_id_cmp(got_worktree_get_base_commit_id(worktree
),
3766 error
= got_worktree_set_base_commit_id(worktree
, repo
,
3772 memset(&upa
, 0, sizeof(upa
));
3773 upa
.verbosity
= verbosity
;
3774 error
= got_worktree_checkout_files(worktree
, &paths
, repo
,
3775 update_progress
, &upa
, check_cancelled
, NULL
);
3779 if (upa
.did_something
) {
3780 printf("Updated to %s: %s\n",
3781 got_worktree_get_head_ref_name(worktree
), commit_id_str
);
3783 printf("Already up-to-date\n");
3785 print_update_progress_stats(&upa
);
3788 const struct got_error
*pack_err
=
3789 got_repo_pack_fds_close(pack_fds
);
3794 close_err
= got_repo_close(repo
);
3798 if (worktree
!= NULL
) {
3799 close_err
= got_worktree_close(worktree
);
3803 if (head_ref
!= NULL
)
3804 got_ref_close(head_ref
);
3805 free(worktree_path
);
3806 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_PATH
);
3808 free(commit_id_str
);
3812 static const struct got_error
*
3813 diff_blobs(struct got_object_id
*blob_id1
, struct got_object_id
*blob_id2
,
3814 const char *path
, int diff_context
, int ignore_whitespace
,
3815 int force_text_diff
, struct got_diffstat_cb_arg
*dsa
,
3816 struct got_repository
*repo
, FILE *outfile
)
3818 const struct got_error
*err
= NULL
;
3819 struct got_blob_object
*blob1
= NULL
, *blob2
= NULL
;
3820 FILE *f1
= NULL
, *f2
= NULL
;
3821 int fd1
= -1, fd2
= -1;
3823 fd1
= got_opentempfd();
3825 return got_error_from_errno("got_opentempfd");
3826 fd2
= got_opentempfd();
3828 err
= got_error_from_errno("got_opentempfd");
3833 err
= got_object_open_as_blob(&blob1
, repo
, blob_id1
, 8192,
3839 err
= got_object_open_as_blob(&blob2
, repo
, blob_id2
, 8192, fd2
);
3843 f1
= got_opentemp();
3845 err
= got_error_from_errno("got_opentemp");
3848 f2
= got_opentemp();
3850 err
= got_error_from_errno("got_opentemp");
3854 while (path
[0] == '/')
3856 err
= got_diff_blob(NULL
, NULL
, blob1
, blob2
, f1
, f2
, path
, path
,
3857 GOT_DIFF_ALGORITHM_PATIENCE
, diff_context
, ignore_whitespace
,
3858 force_text_diff
, dsa
, outfile
);
3860 if (fd1
!= -1 && close(fd1
) == -1 && err
== NULL
)
3861 err
= got_error_from_errno("close");
3863 got_object_blob_close(blob1
);
3864 if (fd2
!= -1 && close(fd2
) == -1 && err
== NULL
)
3865 err
= got_error_from_errno("close");
3867 got_object_blob_close(blob2
);
3868 if (f1
&& fclose(f1
) == EOF
&& err
== NULL
)
3869 err
= got_error_from_errno("fclose");
3870 if (f2
&& fclose(f2
) == EOF
&& err
== NULL
)
3871 err
= got_error_from_errno("fclose");
3875 static const struct got_error
*
3876 diff_trees(struct got_object_id
*tree_id1
, struct got_object_id
*tree_id2
,
3877 const char *path
, int diff_context
, int ignore_whitespace
,
3878 int force_text_diff
, struct got_diffstat_cb_arg
*dsa
,
3879 struct got_repository
*repo
, FILE *outfile
)
3881 const struct got_error
*err
= NULL
;
3882 struct got_tree_object
*tree1
= NULL
, *tree2
= NULL
;
3883 struct got_diff_blob_output_unidiff_arg arg
;
3884 FILE *f1
= NULL
, *f2
= NULL
;
3885 int fd1
= -1, fd2
= -1;
3888 err
= got_object_open_as_tree(&tree1
, repo
, tree_id1
);
3891 fd1
= got_opentempfd();
3893 err
= got_error_from_errno("got_opentempfd");
3898 err
= got_object_open_as_tree(&tree2
, repo
, tree_id2
);
3902 f1
= got_opentemp();
3904 err
= got_error_from_errno("got_opentemp");
3908 f2
= got_opentemp();
3910 err
= got_error_from_errno("got_opentemp");
3913 fd2
= got_opentempfd();
3915 err
= got_error_from_errno("got_opentempfd");
3918 arg
.diff_context
= diff_context
;
3919 arg
.ignore_whitespace
= ignore_whitespace
;
3920 arg
.force_text_diff
= force_text_diff
;
3922 arg
.diff_algo
= GOT_DIFF_ALGORITHM_PATIENCE
;
3923 arg
.outfile
= outfile
;
3926 while (path
[0] == '/')
3928 err
= got_diff_tree(tree1
, tree2
, f1
, f2
, fd1
, fd2
, path
, path
, repo
,
3929 got_diff_blob_output_unidiff
, &arg
, 1);
3932 got_object_tree_close(tree1
);
3934 got_object_tree_close(tree2
);
3935 if (f1
&& fclose(f1
) == EOF
&& err
== NULL
)
3936 err
= got_error_from_errno("fclose");
3937 if (f2
&& fclose(f2
) == EOF
&& err
== NULL
)
3938 err
= got_error_from_errno("fclose");
3939 if (fd1
!= -1 && close(fd1
) == -1 && err
== NULL
)
3940 err
= got_error_from_errno("close");
3941 if (fd2
!= -1 && close(fd2
) == -1 && err
== NULL
)
3942 err
= got_error_from_errno("close");
3946 static const struct got_error
*
3947 get_changed_paths(struct got_pathlist_head
*paths
,
3948 struct got_commit_object
*commit
, struct got_repository
*repo
,
3949 struct got_diffstat_cb_arg
*dsa
)
3951 const struct got_error
*err
= NULL
;
3952 struct got_object_id
*tree_id1
= NULL
, *tree_id2
= NULL
;
3953 struct got_tree_object
*tree1
= NULL
, *tree2
= NULL
;
3954 struct got_object_qid
*qid
;
3955 got_diff_blob_cb cb
= got_diff_tree_collect_changed_paths
;
3956 FILE *f1
= NULL
, *f2
= NULL
;
3957 int fd1
= -1, fd2
= -1;
3960 cb
= got_diff_tree_compute_diffstat
;
3962 f1
= got_opentemp();
3964 err
= got_error_from_errno("got_opentemp");
3967 f2
= got_opentemp();
3969 err
= got_error_from_errno("got_opentemp");
3972 fd1
= got_opentempfd();
3974 err
= got_error_from_errno("got_opentempfd");
3977 fd2
= got_opentempfd();
3979 err
= got_error_from_errno("got_opentempfd");
3984 qid
= STAILQ_FIRST(got_object_commit_get_parent_ids(commit
));
3986 struct got_commit_object
*pcommit
;
3987 err
= got_object_open_as_commit(&pcommit
, repo
,
3992 tree_id1
= got_object_id_dup(
3993 got_object_commit_get_tree_id(pcommit
));
3994 if (tree_id1
== NULL
) {
3995 got_object_commit_close(pcommit
);
3996 return got_error_from_errno("got_object_id_dup");
3998 got_object_commit_close(pcommit
);
4003 err
= got_object_open_as_tree(&tree1
, repo
, tree_id1
);
4008 tree_id2
= got_object_commit_get_tree_id(commit
);
4009 err
= got_object_open_as_tree(&tree2
, repo
, tree_id2
);
4013 err
= got_diff_tree(tree1
, tree2
, f1
, f2
, fd1
, fd2
, "", "", repo
,
4014 cb
, dsa
? (void *)dsa
: paths
, dsa
? 1 : 0);
4017 got_object_tree_close(tree1
);
4019 got_object_tree_close(tree2
);
4020 if (fd1
!= -1 && close(fd1
) == -1 && err
== NULL
)
4021 err
= got_error_from_errno("close");
4022 if (fd2
!= -1 && close(fd2
) == -1 && err
== NULL
)
4023 err
= got_error_from_errno("close");
4024 if (f1
&& fclose(f1
) == EOF
&& err
== NULL
)
4025 err
= got_error_from_errno("fclose");
4026 if (f2
&& fclose(f2
) == EOF
&& err
== NULL
)
4027 err
= got_error_from_errno("fclose");
4032 static const struct got_error
*
4033 print_patch(struct got_commit_object
*commit
, struct got_object_id
*id
,
4034 const char *path
, int diff_context
, struct got_diffstat_cb_arg
*dsa
,
4035 struct got_repository
*repo
, FILE *outfile
)
4037 const struct got_error
*err
= NULL
;
4038 struct got_commit_object
*pcommit
= NULL
;
4039 char *id_str1
= NULL
, *id_str2
= NULL
;
4040 struct got_object_id
*obj_id1
= NULL
, *obj_id2
= NULL
;
4041 struct got_object_qid
*qid
;
4043 qid
= STAILQ_FIRST(got_object_commit_get_parent_ids(commit
));
4045 err
= got_object_open_as_commit(&pcommit
, repo
,
4049 err
= got_object_id_str(&id_str1
, &qid
->id
);
4054 err
= got_object_id_str(&id_str2
, id
);
4058 if (path
&& path
[0] != '\0') {
4060 err
= got_object_id_by_path(&obj_id2
, repo
, commit
, path
);
4064 err
= got_object_id_by_path(&obj_id1
, repo
,
4067 if (err
->code
!= GOT_ERR_NO_TREE_ENTRY
) {
4073 err
= got_object_get_type(&obj_type
, repo
, obj_id2
);
4079 "diff %s %s\n", id_str1
? id_str1
: "/dev/null", id_str2
);
4080 fprintf(outfile
, "commit - %s\n",
4081 id_str1
? id_str1
: "/dev/null");
4082 fprintf(outfile
, "commit + %s\n", id_str2
);
4084 case GOT_OBJ_TYPE_BLOB
:
4085 err
= diff_blobs(obj_id1
, obj_id2
, path
, diff_context
,
4086 0, 0, dsa
, repo
, outfile
);
4088 case GOT_OBJ_TYPE_TREE
:
4089 err
= diff_trees(obj_id1
, obj_id2
, path
, diff_context
,
4090 0, 0, dsa
, repo
, outfile
);
4093 err
= got_error(GOT_ERR_OBJ_TYPE
);
4099 obj_id2
= got_object_commit_get_tree_id(commit
);
4101 obj_id1
= got_object_commit_get_tree_id(pcommit
);
4103 "diff %s %s\n", id_str1
? id_str1
: "/dev/null", id_str2
);
4104 fprintf(outfile
, "commit - %s\n",
4105 id_str1
? id_str1
: "/dev/null");
4106 fprintf(outfile
, "commit + %s\n", id_str2
);
4107 err
= diff_trees(obj_id1
, obj_id2
, "", diff_context
, 0, 0,
4108 dsa
, repo
, outfile
);
4114 got_object_commit_close(pcommit
);
4119 get_datestr(time_t *time
, char *datebuf
)
4121 struct tm mytm
, *tm
;
4124 tm
= gmtime_r(time
, &mytm
);
4127 s
= asctime_r(tm
, datebuf
);
4130 p
= strchr(s
, '\n');
4136 static const struct got_error
*
4137 match_commit(int *have_match
, struct got_object_id
*id
,
4138 struct got_commit_object
*commit
, regex_t
*regex
)
4140 const struct got_error
*err
= NULL
;
4141 regmatch_t regmatch
;
4142 char *id_str
= NULL
, *logmsg
= NULL
;
4146 err
= got_object_id_str(&id_str
, id
);
4150 err
= got_object_commit_get_logmsg(&logmsg
, commit
);
4154 if (regexec(regex
, got_object_commit_get_author(commit
), 1,
4155 ®match
, 0) == 0 ||
4156 regexec(regex
, got_object_commit_get_committer(commit
), 1,
4157 ®match
, 0) == 0 ||
4158 regexec(regex
, id_str
, 1, ®match
, 0) == 0 ||
4159 regexec(regex
, logmsg
, 1, ®match
, 0) == 0)
4168 match_changed_paths(int *have_match
, struct got_pathlist_head
*changed_paths
,
4171 regmatch_t regmatch
;
4172 struct got_pathlist_entry
*pe
;
4176 TAILQ_FOREACH(pe
, changed_paths
, entry
) {
4177 if (regexec(regex
, pe
->path
, 1, ®match
, 0) == 0) {
4184 static const struct got_error
*
4185 match_patch(int *have_match
, struct got_commit_object
*commit
,
4186 struct got_object_id
*id
, const char *path
, int diff_context
,
4187 struct got_repository
*repo
, regex_t
*regex
, FILE *f
)
4189 const struct got_error
*err
= NULL
;
4191 size_t linesize
= 0;
4192 regmatch_t regmatch
;
4196 err
= got_opentemp_truncate(f
);
4200 err
= print_patch(commit
, id
, path
, diff_context
, NULL
, repo
, f
);
4204 if (fseeko(f
, 0L, SEEK_SET
) == -1) {
4205 err
= got_error_from_errno("fseeko");
4209 while (getline(&line
, &linesize
, f
) != -1) {
4210 if (regexec(regex
, line
, 1, ®match
, 0) == 0) {
4220 #define GOT_COMMIT_SEP_STR "-----------------------------------------------\n"
4222 static const struct got_error
*
4223 build_refs_str(char **refs_str
, struct got_reflist_head
*refs
,
4224 struct got_object_id
*id
, struct got_repository
*repo
,
4227 static const struct got_error
*err
= NULL
;
4228 struct got_reflist_entry
*re
;
4234 TAILQ_FOREACH(re
, refs
, entry
) {
4235 struct got_tag_object
*tag
= NULL
;
4236 struct got_object_id
*ref_id
;
4239 name
= got_ref_get_name(re
->ref
);
4240 if (strcmp(name
, GOT_REF_HEAD
) == 0)
4242 if (strncmp(name
, "refs/", 5) == 0)
4244 if (strncmp(name
, "got/", 4) == 0)
4246 if (strncmp(name
, "heads/", 6) == 0)
4248 if (strncmp(name
, "remotes/", 8) == 0) {
4252 s
= strstr(name
, "/" GOT_REF_HEAD
);
4253 if (s
!= NULL
&& strcmp(s
, "/" GOT_REF_HEAD
) == 0)
4256 err
= got_ref_resolve(&ref_id
, repo
, re
->ref
);
4259 if (strncmp(name
, "tags/", 5) == 0) {
4260 err
= got_object_open_as_tag(&tag
, repo
, ref_id
);
4262 if (err
->code
!= GOT_ERR_OBJ_TYPE
) {
4266 /* Ref points at something other than a tag. */
4271 cmp
= got_object_id_cmp(tag
?
4272 got_object_tag_get_object_id(tag
) : ref_id
, id
);
4275 got_object_tag_close(tag
);
4279 if (asprintf(refs_str
, "%s%s%s", s
? s
: "",
4280 s
? ", " : "", name
) == -1) {
4281 err
= got_error_from_errno("asprintf");
4292 static const struct got_error
*
4293 print_commit_oneline(struct got_commit_object
*commit
, struct got_object_id
*id
,
4294 struct got_repository
*repo
, struct got_reflist_object_id_map
*refs_idmap
)
4296 const struct got_error
*err
= NULL
;
4297 char *ref_str
= NULL
, *id_str
= NULL
, *logmsg0
= NULL
;
4298 char *comma
, *s
, *nl
;
4299 struct got_reflist_head
*refs
;
4300 char datebuf
[12]; /* YYYY-MM-DD + SPACE + NUL */
4302 time_t committer_time
;
4304 refs
= got_reflist_object_id_map_lookup(refs_idmap
, id
);
4306 err
= build_refs_str(&ref_str
, refs
, id
, repo
, 1);
4310 /* Display the first matching ref only. */
4311 if (ref_str
&& (comma
= strchr(ref_str
, ',')) != NULL
)
4315 if (ref_str
== NULL
) {
4316 err
= got_object_id_str(&id_str
, id
);
4321 committer_time
= got_object_commit_get_committer_time(commit
);
4322 if (gmtime_r(&committer_time
, &tm
) == NULL
) {
4323 err
= got_error_from_errno("gmtime_r");
4326 if (strftime(datebuf
, sizeof(datebuf
), "%F ", &tm
) == 0) {
4327 err
= got_error(GOT_ERR_NO_SPACE
);
4331 err
= got_object_commit_get_logmsg(&logmsg0
, commit
);
4336 while (isspace((unsigned char)s
[0]))
4339 nl
= strchr(s
, '\n');
4345 printf("%s%-7s %s\n", datebuf
, ref_str
, s
);
4347 printf("%s%.7s %s\n", datebuf
, id_str
, s
);
4349 if (fflush(stdout
) != 0 && err
== NULL
)
4350 err
= got_error_from_errno("fflush");
4358 static const struct got_error
*
4359 print_diffstat(struct got_diffstat_cb_arg
*dsa
, const char *header
)
4361 struct got_pathlist_entry
*pe
;
4364 printf("%s\n", header
);
4366 TAILQ_FOREACH(pe
, dsa
->paths
, entry
) {
4367 struct got_diff_changed_path
*cp
= pe
->data
;
4368 int pad
= dsa
->max_path_len
- pe
->path_len
+ 1;
4370 printf(" %c %s%*c | %*d+ %*d-\n", cp
->status
, pe
->path
, pad
,
4371 ' ', dsa
->add_cols
+ 1, cp
->add
, dsa
->rm_cols
+ 1, cp
->rm
);
4373 printf("\n%d file%s changed, %d insertion%s(+), %d deletion%s(-)\n\n",
4374 dsa
->nfiles
, dsa
->nfiles
> 1 ? "s" : "", dsa
->ins
,
4375 dsa
->ins
!= 1 ? "s" : "", dsa
->del
, dsa
->del
!= 1 ? "s" : "");
4377 if (fflush(stdout
) != 0)
4378 return got_error_from_errno("fflush");
4383 static const struct got_error
*
4389 if (fseeko(f
, 0L, SEEK_SET
) == -1)
4390 return got_error_from_errno("fseek");
4393 r
= fread(buf
, 1, sizeof(buf
), f
);
4396 return got_error_from_errno("fread");
4400 if (fwrite(buf
, 1, r
, stdout
) != r
)
4401 return got_ferror(stdout
, GOT_ERR_IO
);
4407 static const struct got_error
*
4408 print_commit(struct got_commit_object
*commit
, struct got_object_id
*id
,
4409 struct got_repository
*repo
, const char *path
,
4410 struct got_pathlist_head
*changed_paths
,
4411 struct got_diffstat_cb_arg
*diffstat
, int show_patch
, int diff_context
,
4412 struct got_reflist_object_id_map
*refs_idmap
, const char *custom_refs_str
,
4415 const struct got_error
*err
= NULL
;
4417 char *id_str
, *datestr
, *logmsg0
, *logmsg
, *line
;
4419 time_t committer_time
;
4420 const char *author
, *committer
;
4421 char *refs_str
= NULL
;
4423 err
= got_object_id_str(&id_str
, id
);
4427 if (custom_refs_str
== NULL
) {
4428 struct got_reflist_head
*refs
;
4429 refs
= got_reflist_object_id_map_lookup(refs_idmap
, id
);
4431 err
= build_refs_str(&refs_str
, refs
, id
, repo
, 0);
4437 printf(GOT_COMMIT_SEP_STR
);
4438 if (custom_refs_str
)
4439 printf("%s %s (%s)\n", prefix
? prefix
: "commit", id_str
,
4442 printf("%s %s%s%s%s\n", prefix
? prefix
: "commit", id_str
,
4443 refs_str
? " (" : "", refs_str
? refs_str
: "",
4444 refs_str
? ")" : "");
4449 printf("from: %s\n", got_object_commit_get_author(commit
));
4450 author
= got_object_commit_get_author(commit
);
4451 committer
= got_object_commit_get_committer(commit
);
4452 if (strcmp(author
, committer
) != 0)
4453 printf("via: %s\n", committer
);
4454 committer_time
= got_object_commit_get_committer_time(commit
);
4455 datestr
= get_datestr(&committer_time
, datebuf
);
4457 printf("date: %s UTC\n", datestr
);
4458 if (got_object_commit_get_nparents(commit
) > 1) {
4459 const struct got_object_id_queue
*parent_ids
;
4460 struct got_object_qid
*qid
;
4462 parent_ids
= got_object_commit_get_parent_ids(commit
);
4463 STAILQ_FOREACH(qid
, parent_ids
, entry
) {
4464 err
= got_object_id_str(&id_str
, &qid
->id
);
4467 printf("parent %d: %s\n", n
++, id_str
);
4473 err
= got_object_commit_get_logmsg(&logmsg0
, commit
);
4479 line
= strsep(&logmsg
, "\n");
4481 printf(" %s\n", line
);
4485 if (changed_paths
&& diffstat
== NULL
) {
4486 struct got_pathlist_entry
*pe
;
4488 TAILQ_FOREACH(pe
, changed_paths
, entry
) {
4489 struct got_diff_changed_path
*cp
= pe
->data
;
4491 printf(" %c %s\n", cp
->status
, pe
->path
);
4499 err
= got_error_from_errno("got_opentemp");
4504 err
= print_patch(commit
, id
, path
, diff_context
, diffstat
,
4505 repo
, diffstat
== NULL
? stdout
: f
);
4510 err
= print_diffstat(diffstat
, NULL
);
4522 if (fflush(stdout
) != 0 && err
== NULL
)
4523 err
= got_error_from_errno("fflush");
4525 if (f
&& fclose(f
) == EOF
&& err
== NULL
)
4526 err
= got_error_from_errno("fclose");
4532 static const struct got_error
*
4533 print_commits(struct got_object_id
*root_id
, struct got_object_id
*end_id
,
4534 struct got_repository
*repo
, const char *path
, int show_changed_paths
,
4535 int show_diffstat
, int show_patch
, const char *search_pattern
,
4536 int diff_context
, int limit
, int log_branches
, int reverse_display_order
,
4537 struct got_reflist_object_id_map
*refs_idmap
, int one_line
, int toposort
,
4540 const struct got_error
*err
;
4541 struct got_commit_graph
*graph
;
4544 struct got_object_id_queue reversed_commits
;
4545 struct got_object_qid
*qid
;
4546 struct got_commit_object
*commit
;
4547 struct got_pathlist_head changed_paths
;
4549 STAILQ_INIT(&reversed_commits
);
4550 TAILQ_INIT(&changed_paths
);
4552 if (search_pattern
&& regcomp(®ex
, search_pattern
,
4553 REG_EXTENDED
| REG_NOSUB
| REG_NEWLINE
))
4554 return got_error_msg(GOT_ERR_REGEX
, search_pattern
);
4556 err
= got_commit_graph_open(&graph
, path
, !log_branches
);
4559 if (log_branches
&& toposort
) {
4560 err
= got_commit_graph_toposort(graph
, root_id
, repo
,
4561 check_cancelled
, NULL
);
4563 err
= got_commit_graph_bfsort(graph
, root_id
, repo
,
4564 check_cancelled
, NULL
);
4569 struct got_object_id id
;
4570 struct got_diffstat_cb_arg dsa
= { 0, 0, 0, 0, 0, 0,
4571 &changed_paths
, 0, 0, GOT_DIFF_ALGORITHM_PATIENCE
};
4573 if (sigint_received
|| sigpipe_received
)
4576 err
= got_commit_graph_iter_next(&id
, graph
, repo
,
4577 check_cancelled
, NULL
);
4579 if (err
->code
== GOT_ERR_ITER_COMPLETED
)
4584 err
= got_object_open_as_commit(&commit
, repo
, &id
);
4588 if (((show_changed_paths
&& !show_diffstat
) ||
4589 (show_diffstat
&& !show_patch
))
4590 && !reverse_display_order
) {
4591 err
= get_changed_paths(&changed_paths
, commit
, repo
,
4592 show_diffstat
? &dsa
: NULL
);
4597 if (search_pattern
) {
4598 err
= match_commit(&have_match
, &id
, commit
, ®ex
);
4600 got_object_commit_close(commit
);
4603 if (have_match
== 0 && show_changed_paths
)
4604 match_changed_paths(&have_match
,
4605 &changed_paths
, ®ex
);
4606 if (have_match
== 0 && show_patch
) {
4607 err
= match_patch(&have_match
, commit
, &id
,
4608 path
, diff_context
, repo
, ®ex
, tmpfile
);
4612 if (have_match
== 0) {
4613 got_object_commit_close(commit
);
4614 got_pathlist_free(&changed_paths
,
4615 GOT_PATHLIST_FREE_ALL
);
4620 if (reverse_display_order
) {
4621 err
= got_object_qid_alloc(&qid
, &id
);
4624 STAILQ_INSERT_HEAD(&reversed_commits
, qid
, entry
);
4625 got_object_commit_close(commit
);
4628 err
= print_commit_oneline(commit
, &id
,
4631 err
= print_commit(commit
, &id
, repo
, path
,
4632 (show_changed_paths
|| show_diffstat
) ?
4633 &changed_paths
: NULL
,
4634 show_diffstat
? &dsa
: NULL
, show_patch
,
4635 diff_context
, refs_idmap
, NULL
, NULL
);
4636 got_object_commit_close(commit
);
4640 if ((limit
&& --limit
== 0) ||
4641 (end_id
&& got_object_id_cmp(&id
, end_id
) == 0))
4644 got_pathlist_free(&changed_paths
, GOT_PATHLIST_FREE_ALL
);
4646 if (reverse_display_order
) {
4647 STAILQ_FOREACH(qid
, &reversed_commits
, entry
) {
4648 struct got_diffstat_cb_arg dsa
= { 0, 0, 0, 0, 0, 0,
4649 &changed_paths
, 0, 0, GOT_DIFF_ALGORITHM_PATIENCE
};
4651 err
= got_object_open_as_commit(&commit
, repo
,
4655 if ((show_changed_paths
&& !show_diffstat
) ||
4656 (show_diffstat
&& !show_patch
)) {
4657 err
= get_changed_paths(&changed_paths
, commit
,
4658 repo
, show_diffstat
? &dsa
: NULL
);
4663 err
= print_commit_oneline(commit
, &qid
->id
,
4666 err
= print_commit(commit
, &qid
->id
, repo
, path
,
4667 (show_changed_paths
|| show_diffstat
) ?
4668 &changed_paths
: NULL
,
4669 show_diffstat
? &dsa
: NULL
, show_patch
,
4670 diff_context
, refs_idmap
, NULL
, NULL
);
4671 got_object_commit_close(commit
);
4674 got_pathlist_free(&changed_paths
, GOT_PATHLIST_FREE_ALL
);
4678 while (!STAILQ_EMPTY(&reversed_commits
)) {
4679 qid
= STAILQ_FIRST(&reversed_commits
);
4680 STAILQ_REMOVE_HEAD(&reversed_commits
, entry
);
4681 got_object_qid_free(qid
);
4683 got_pathlist_free(&changed_paths
, GOT_PATHLIST_FREE_ALL
);
4686 got_commit_graph_close(graph
);
4693 fprintf(stderr
, "usage: %s log [-bdPpRst] [-C number] [-c commit] "
4694 "[-l N] [-r repository-path] [-S search-pattern] [-x commit] "
4695 "[path]\n", getprogname());
4700 get_default_log_limit(void)
4702 const char *got_default_log_limit
;
4706 got_default_log_limit
= getenv("GOT_LOG_DEFAULT_LIMIT");
4707 if (got_default_log_limit
== NULL
)
4709 n
= strtonum(got_default_log_limit
, 0, INT_MAX
, &errstr
);
4715 static const struct got_error
*
4716 cmd_log(int argc
, char *argv
[])
4718 const struct got_error
*error
;
4719 struct got_repository
*repo
= NULL
;
4720 struct got_worktree
*worktree
= NULL
;
4721 struct got_object_id
*start_id
= NULL
, *end_id
= NULL
;
4722 char *repo_path
= NULL
, *path
= NULL
, *cwd
= NULL
, *in_repo_path
= NULL
;
4723 char *keyword_idstr
= NULL
;
4724 const char *start_commit
= NULL
, *end_commit
= NULL
;
4725 const char *search_pattern
= NULL
;
4726 int diff_context
= -1, ch
;
4727 int show_changed_paths
= 0, show_patch
= 0, limit
= 0, log_branches
= 0;
4728 int show_diffstat
= 0, reverse_display_order
= 0, one_line
= 0;
4731 struct got_reflist_head refs
;
4732 struct got_reflist_object_id_map
*refs_idmap
= NULL
;
4733 FILE *tmpfile
= NULL
;
4734 int *pack_fds
= NULL
;
4739 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
4745 limit
= get_default_log_limit();
4747 while ((ch
= getopt(argc
, argv
, "bC:c:dl:PpRr:S:stx:")) != -1) {
4753 diff_context
= strtonum(optarg
, 0, GOT_DIFF_MAX_CONTEXT
,
4756 errx(1, "number of context lines is %s: %s",
4760 start_commit
= optarg
;
4766 limit
= strtonum(optarg
, 0, INT_MAX
, &errstr
);
4768 errx(1, "number of commits is %s: %s",
4772 show_changed_paths
= 1;
4778 reverse_display_order
= 1;
4781 repo_path
= realpath(optarg
, NULL
);
4782 if (repo_path
== NULL
)
4783 return got_error_from_errno2("realpath",
4785 got_path_strip_trailing_slashes(repo_path
);
4788 search_pattern
= optarg
;
4797 end_commit
= optarg
;
4808 if (diff_context
== -1)
4810 else if (!show_patch
)
4811 errx(1, "-C requires -p");
4813 if (one_line
&& (show_patch
|| show_changed_paths
|| show_diffstat
))
4814 errx(1, "cannot use -s with -d, -p or -P");
4816 cwd
= getcwd(NULL
, 0);
4818 error
= got_error_from_errno("getcwd");
4822 error
= got_repo_pack_fds_open(&pack_fds
);
4826 if (repo_path
== NULL
) {
4827 error
= got_worktree_open(&worktree
, cwd
,
4828 GOT_WORKTREE_GOT_DIR
);
4829 if (error
&& error
->code
!= GOT_ERR_NOT_WORKTREE
)
4836 error
= got_worktree_resolve_path(&path
, worktree
,
4841 path
= strdup(argv
[0]);
4843 error
= got_error_from_errno("strdup");
4847 } else if (argc
!= 0)
4850 if (repo_path
== NULL
) {
4851 repo_path
= worktree
?
4852 strdup(got_worktree_get_repo_path(worktree
)) : strdup(cwd
);
4854 if (repo_path
== NULL
) {
4855 error
= got_error_from_errno("strdup");
4859 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
4863 error
= apply_unveil(got_repo_get_path(repo
), 1,
4864 worktree
? got_worktree_get_root_path(worktree
) : NULL
);
4868 error
= got_ref_list(&refs
, repo
, NULL
, got_ref_cmp_by_name
, NULL
);
4872 error
= got_reflist_object_id_map_create(&refs_idmap
, &refs
, repo
);
4876 if (start_commit
== NULL
) {
4877 struct got_reference
*head_ref
;
4878 struct got_commit_object
*commit
= NULL
;
4879 error
= got_ref_open(&head_ref
, repo
,
4880 worktree
? got_worktree_get_head_ref_name(worktree
)
4884 error
= got_ref_resolve(&start_id
, repo
, head_ref
);
4885 got_ref_close(head_ref
);
4888 error
= got_object_open_as_commit(&commit
, repo
,
4892 got_object_commit_close(commit
);
4894 error
= got_keyword_to_idstr(&keyword_idstr
, start_commit
,
4898 if (keyword_idstr
!= NULL
)
4899 start_commit
= keyword_idstr
;
4901 error
= got_repo_match_object_id(&start_id
, NULL
,
4902 start_commit
, GOT_OBJ_TYPE_COMMIT
, &refs
, repo
);
4906 if (end_commit
!= NULL
) {
4907 error
= got_keyword_to_idstr(&keyword_idstr
, end_commit
,
4911 if (keyword_idstr
!= NULL
)
4912 end_commit
= keyword_idstr
;
4914 error
= got_repo_match_object_id(&end_id
, NULL
,
4915 end_commit
, GOT_OBJ_TYPE_COMMIT
, &refs
, repo
);
4922 * If a path was specified on the command line it was resolved
4923 * to a path in the work tree above. Prepend the work tree's
4924 * path prefix to obtain the corresponding in-repository path.
4928 prefix
= got_worktree_get_path_prefix(worktree
);
4929 if (asprintf(&in_repo_path
, "%s%s%s", prefix
,
4930 (path
[0] != '\0') ? "/" : "", path
) == -1) {
4931 error
= got_error_from_errno("asprintf");
4936 error
= got_repo_map_path(&in_repo_path
, repo
,
4942 path
= in_repo_path
;
4946 /* Release work tree lock. */
4947 got_worktree_close(worktree
);
4951 if (search_pattern
&& show_patch
) {
4952 tmpfile
= got_opentemp();
4953 if (tmpfile
== NULL
) {
4954 error
= got_error_from_errno("got_opentemp");
4959 error
= print_commits(start_id
, end_id
, repo
, path
? path
: "",
4960 show_changed_paths
, show_diffstat
, show_patch
, search_pattern
,
4961 diff_context
, limit
, log_branches
, reverse_display_order
,
4962 refs_idmap
, one_line
, toposort
, tmpfile
);
4969 free(keyword_idstr
);
4971 got_worktree_close(worktree
);
4973 const struct got_error
*close_err
= got_repo_close(repo
);
4978 const struct got_error
*pack_err
=
4979 got_repo_pack_fds_close(pack_fds
);
4984 got_reflist_object_id_map_free(refs_idmap
);
4985 if (tmpfile
&& fclose(tmpfile
) == EOF
&& error
== NULL
)
4986 error
= got_error_from_errno("fclose");
4987 got_ref_list_free(&refs
);
4994 fprintf(stderr
, "usage: %s diff [-adPsw] [-C number] [-c commit] "
4995 "[-r repository-path] [object1 object2 | path ...]\n",
5000 struct print_diff_arg
{
5001 struct got_repository
*repo
;
5002 struct got_worktree
*worktree
;
5003 struct got_diffstat_cb_arg
*diffstat
;
5008 enum got_diff_algorithm diff_algo
;
5009 int ignore_whitespace
;
5010 int force_text_diff
;
5017 * Create a file which contains the target path of a symlink so we can feed
5018 * it as content to the diff engine.
5020 static const struct got_error
*
5021 get_symlink_target_file(int *fd
, int dirfd
, const char *de_name
,
5022 const char *abspath
)
5024 const struct got_error
*err
= NULL
;
5025 char target_path
[PATH_MAX
];
5026 ssize_t target_len
, outlen
;
5031 target_len
= readlinkat(dirfd
, de_name
, target_path
, PATH_MAX
);
5032 if (target_len
== -1)
5033 return got_error_from_errno2("readlinkat", abspath
);
5035 target_len
= readlink(abspath
, target_path
, PATH_MAX
);
5036 if (target_len
== -1)
5037 return got_error_from_errno2("readlink", abspath
);
5040 *fd
= got_opentempfd();
5042 return got_error_from_errno("got_opentempfd");
5044 outlen
= write(*fd
, target_path
, target_len
);
5046 err
= got_error_from_errno("got_opentempfd");
5050 if (lseek(*fd
, 0, SEEK_SET
) == -1) {
5051 err
= got_error_from_errno2("lseek", abspath
);
5062 static const struct got_error
*
5063 emit_base_commit_header(FILE *f
, struct got_object_id
*commit_id
,
5064 struct got_worktree
*worktree
)
5066 const struct got_error
*err
;
5067 struct got_object_id
*base_commit_id
;
5068 char *base_commit_idstr
;
5070 if (worktree
== NULL
) /* shouldn't happen */
5071 return got_error(GOT_ERR_NOT_WORKTREE
);
5073 base_commit_id
= got_worktree_get_base_commit_id(worktree
);
5075 if (commit_id
!= NULL
) {
5076 if (got_object_id_cmp(commit_id
, base_commit_id
) != 0)
5077 base_commit_id
= commit_id
;
5080 err
= got_object_id_str(&base_commit_idstr
, base_commit_id
);
5084 if (fprintf(f
, "commit - %s\n", base_commit_idstr
) < 0)
5085 err
= got_error_from_errno("fprintf");
5086 free(base_commit_idstr
);
5090 static const struct got_error
*
5091 print_diff(void *arg
, unsigned char status
, unsigned char staged_status
,
5092 const char *path
, struct got_object_id
*blob_id
,
5093 struct got_object_id
*staged_blob_id
, struct got_object_id
*commit_id
,
5094 int dirfd
, const char *de_name
)
5096 struct print_diff_arg
*a
= arg
;
5097 const struct got_error
*err
= NULL
;
5098 struct got_blob_object
*blob1
= NULL
;
5099 int fd
= -1, fd1
= -1, fd2
= -1;
5101 char *abspath
= NULL
, *label1
= NULL
;
5106 memset(&sb
, 0, sizeof(sb
));
5108 if (a
->diff_staged
) {
5109 if (staged_status
!= GOT_STATUS_MODIFY
&&
5110 staged_status
!= GOT_STATUS_ADD
&&
5111 staged_status
!= GOT_STATUS_DELETE
)
5114 if (staged_status
== GOT_STATUS_DELETE
)
5116 if (status
== GOT_STATUS_NONEXISTENT
)
5117 return got_error_set_errno(ENOENT
, path
);
5118 if (status
!= GOT_STATUS_MODIFY
&&
5119 status
!= GOT_STATUS_ADD
&&
5120 status
!= GOT_STATUS_DELETE
&&
5121 status
!= GOT_STATUS_CONFLICT
)
5125 err
= got_opentemp_truncate(a
->f1
);
5127 return got_error_from_errno("got_opentemp_truncate");
5128 err
= got_opentemp_truncate(a
->f2
);
5130 return got_error_from_errno("got_opentemp_truncate");
5132 if (!a
->header_shown
) {
5133 if (fprintf(a
->outfile
, "diff %s%s\n",
5134 a
->diff_staged
? "-s " : "",
5135 got_worktree_get_root_path(a
->worktree
)) < 0) {
5136 err
= got_error_from_errno("fprintf");
5139 if (fprintf(a
->outfile
, "path + %s%s\n",
5140 got_worktree_get_root_path(a
->worktree
),
5141 a
->diff_staged
? " (staged changes)" : "") < 0) {
5142 err
= got_error_from_errno("fprintf");
5145 a
->header_shown
= 1;
5148 err
= emit_base_commit_header(a
->outfile
, commit_id
, a
->worktree
);
5152 if (a
->diff_staged
) {
5153 const char *label1
= NULL
, *label2
= NULL
;
5154 switch (staged_status
) {
5155 case GOT_STATUS_MODIFY
:
5159 case GOT_STATUS_ADD
:
5162 case GOT_STATUS_DELETE
:
5166 return got_error(GOT_ERR_FILE_STATUS
);
5168 fd1
= got_opentempfd();
5170 err
= got_error_from_errno("got_opentempfd");
5173 fd2
= got_opentempfd();
5175 err
= got_error_from_errno("got_opentempfd");
5178 err
= got_diff_objects_as_blobs(NULL
, NULL
, a
->f1
, a
->f2
,
5179 fd1
, fd2
, blob_id
, staged_blob_id
, label1
, label2
,
5180 a
->diff_algo
, a
->diff_context
, a
->ignore_whitespace
,
5181 a
->force_text_diff
, a
->diffstat
, a
->repo
, a
->outfile
);
5185 fd1
= got_opentempfd();
5187 err
= got_error_from_errno("got_opentempfd");
5191 if (staged_status
== GOT_STATUS_ADD
||
5192 staged_status
== GOT_STATUS_MODIFY
) {
5194 err
= got_object_open_as_blob(&blob1
, a
->repo
, staged_blob_id
,
5198 err
= got_object_id_str(&id_str
, staged_blob_id
);
5201 if (asprintf(&label1
, "%s (staged)", id_str
) == -1) {
5202 err
= got_error_from_errno("asprintf");
5207 } else if (status
!= GOT_STATUS_ADD
) {
5208 err
= got_object_open_as_blob(&blob1
, a
->repo
, blob_id
, 8192,
5214 if (status
!= GOT_STATUS_DELETE
) {
5215 if (asprintf(&abspath
, "%s/%s",
5216 got_worktree_get_root_path(a
->worktree
), path
) == -1) {
5217 err
= got_error_from_errno("asprintf");
5222 fd
= openat(dirfd
, de_name
,
5223 O_RDONLY
| O_NOFOLLOW
| O_CLOEXEC
);
5225 if (!got_err_open_nofollow_on_symlink()) {
5226 err
= got_error_from_errno2("openat",
5230 err
= get_symlink_target_file(&fd
, dirfd
,
5236 fd
= open(abspath
, O_RDONLY
| O_NOFOLLOW
| O_CLOEXEC
);
5238 if (!got_err_open_nofollow_on_symlink()) {
5239 err
= got_error_from_errno2("open",
5243 err
= get_symlink_target_file(&fd
, dirfd
,
5249 if (fstatat(fd
, abspath
, &sb
, AT_SYMLINK_NOFOLLOW
) == -1) {
5250 err
= got_error_from_errno2("fstatat", abspath
);
5253 f2
= fdopen(fd
, "r");
5255 err
= got_error_from_errno2("fdopen", abspath
);
5263 err
= got_object_blob_dump_to_file(&size1
, NULL
, NULL
,
5269 err
= got_diff_blob_file(blob1
, a
->f1
, size1
, label1
, f2
? f2
: a
->f2
,
5270 f2_exists
, &sb
, path
, GOT_DIFF_ALGORITHM_PATIENCE
, a
->diff_context
,
5271 a
->ignore_whitespace
, a
->force_text_diff
, a
->diffstat
, a
->outfile
);
5273 if (fd1
!= -1 && close(fd1
) == -1 && err
== NULL
)
5274 err
= got_error_from_errno("close");
5275 if (fd2
!= -1 && close(fd2
) == -1 && err
== NULL
)
5276 err
= got_error_from_errno("close");
5278 got_object_blob_close(blob1
);
5279 if (fd
!= -1 && close(fd
) == -1 && err
== NULL
)
5280 err
= got_error_from_errno("close");
5281 if (f2
&& fclose(f2
) == EOF
&& err
== NULL
)
5282 err
= got_error_from_errno("fclose");
5287 static const struct got_error
*
5288 cmd_diff(int argc
, char *argv
[])
5290 const struct got_error
*error
;
5291 struct got_repository
*repo
= NULL
;
5292 struct got_worktree
*worktree
= NULL
;
5293 char *cwd
= NULL
, *repo_path
= NULL
;
5294 const char *commit_args
[2] = { NULL
, NULL
};
5295 int ncommit_args
= 0;
5296 struct got_object_id
*ids
[2] = { NULL
, NULL
};
5297 char *labels
[2] = { NULL
, NULL
};
5298 int type1
= GOT_OBJ_TYPE_ANY
, type2
= GOT_OBJ_TYPE_ANY
;
5299 int diff_context
= 3, diff_staged
= 0, ignore_whitespace
= 0, ch
, i
;
5300 int force_text_diff
= 0, force_path
= 0, rflag
= 0, show_diffstat
= 0;
5302 struct got_reflist_head refs
;
5303 struct got_pathlist_head diffstat_paths
, paths
;
5304 FILE *f1
= NULL
, *f2
= NULL
, *outfile
= NULL
;
5305 int fd1
= -1, fd2
= -1;
5306 int *pack_fds
= NULL
;
5307 struct got_diffstat_cb_arg dsa
;
5309 memset(&dsa
, 0, sizeof(dsa
));
5313 TAILQ_INIT(&diffstat_paths
);
5316 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
5321 while ((ch
= getopt(argc
, argv
, "aC:c:dPr:sw")) != -1) {
5324 force_text_diff
= 1;
5327 diff_context
= strtonum(optarg
, 0, GOT_DIFF_MAX_CONTEXT
,
5330 errx(1, "number of context lines is %s: %s",
5334 if (ncommit_args
>= 2)
5335 errx(1, "too many -c options used");
5336 commit_args
[ncommit_args
++] = optarg
;
5345 repo_path
= realpath(optarg
, NULL
);
5346 if (repo_path
== NULL
)
5347 return got_error_from_errno2("realpath",
5349 got_path_strip_trailing_slashes(repo_path
);
5356 ignore_whitespace
= 1;
5367 cwd
= getcwd(NULL
, 0);
5369 error
= got_error_from_errno("getcwd");
5373 error
= got_repo_pack_fds_open(&pack_fds
);
5377 if (repo_path
== NULL
) {
5378 error
= got_worktree_open(&worktree
, cwd
,
5379 GOT_WORKTREE_GOT_DIR
);
5380 if (error
&& error
->code
!= GOT_ERR_NOT_WORKTREE
)
5386 strdup(got_worktree_get_repo_path(worktree
));
5387 if (repo_path
== NULL
) {
5388 error
= got_error_from_errno("strdup");
5392 repo_path
= strdup(cwd
);
5393 if (repo_path
== NULL
) {
5394 error
= got_error_from_errno("strdup");
5400 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
5405 if (show_diffstat
) {
5406 dsa
.paths
= &diffstat_paths
;
5407 dsa
.force_text
= force_text_diff
;
5408 dsa
.ignore_ws
= ignore_whitespace
;
5409 dsa
.diff_algo
= GOT_DIFF_ALGORITHM_PATIENCE
;
5412 if (rflag
|| worktree
== NULL
|| ncommit_args
> 0) {
5414 error
= got_error_msg(GOT_ERR_NOT_IMPL
,
5415 "-P option can only be used when diffing "
5420 error
= got_error_msg(GOT_ERR_NOT_IMPL
,
5421 "-s option can only be used when diffing "
5427 error
= apply_unveil(got_repo_get_path(repo
), 1,
5428 worktree
? got_worktree_get_root_path(worktree
) : NULL
);
5432 if ((!force_path
&& argc
== 2) || ncommit_args
> 0) {
5433 int obj_type
= (ncommit_args
> 0 ?
5434 GOT_OBJ_TYPE_COMMIT
: GOT_OBJ_TYPE_ANY
);
5435 error
= got_ref_list(&refs
, repo
, NULL
, got_ref_cmp_by_name
,
5439 for (i
= 0; i
< (ncommit_args
> 0 ? ncommit_args
: argc
); i
++) {
5441 char *keyword_idstr
= NULL
;
5443 if (ncommit_args
> 0)
5444 arg
= commit_args
[i
];
5448 error
= got_keyword_to_idstr(&keyword_idstr
, arg
,
5452 if (keyword_idstr
!= NULL
)
5453 arg
= keyword_idstr
;
5455 error
= got_repo_match_object_id(&ids
[i
], &labels
[i
],
5456 arg
, obj_type
, &refs
, repo
);
5457 free(keyword_idstr
);
5459 if (error
->code
!= GOT_ERR_NOT_REF
&&
5460 error
->code
!= GOT_ERR_NO_OBJ
)
5462 if (ncommit_args
> 0)
5470 f1
= got_opentemp();
5472 error
= got_error_from_errno("got_opentemp");
5476 f2
= got_opentemp();
5478 error
= got_error_from_errno("got_opentemp");
5482 outfile
= got_opentemp();
5483 if (outfile
== NULL
) {
5484 error
= got_error_from_errno("got_opentemp");
5488 if (ncommit_args
== 0 && (ids
[0] == NULL
|| ids
[1] == NULL
)) {
5489 struct print_diff_arg arg
;
5492 if (worktree
== NULL
) {
5493 if (argc
== 2 && ids
[0] == NULL
) {
5494 error
= got_error_path(argv
[0], GOT_ERR_NO_OBJ
);
5496 } else if (argc
== 2 && ids
[1] == NULL
) {
5497 error
= got_error_path(argv
[1], GOT_ERR_NO_OBJ
);
5499 } else if (argc
> 0) {
5500 error
= got_error_fmt(GOT_ERR_NOT_WORKTREE
,
5501 "%s", "specified paths cannot be resolved");
5504 error
= got_error(GOT_ERR_NOT_WORKTREE
);
5509 error
= get_worktree_paths_from_argv(&paths
, argc
, argv
,
5514 error
= got_object_id_str(&id_str
,
5515 got_worktree_get_base_commit_id(worktree
));
5519 arg
.worktree
= worktree
;
5520 arg
.diff_algo
= GOT_DIFF_ALGORITHM_PATIENCE
;
5521 arg
.diff_context
= diff_context
;
5522 arg
.id_str
= id_str
;
5523 arg
.header_shown
= 0;
5524 arg
.diff_staged
= diff_staged
;
5525 arg
.ignore_whitespace
= ignore_whitespace
;
5526 arg
.force_text_diff
= force_text_diff
;
5527 arg
.diffstat
= show_diffstat
? &dsa
: NULL
;
5530 arg
.outfile
= outfile
;
5532 error
= got_worktree_status(worktree
, &paths
, repo
, 0,
5533 print_diff
, &arg
, check_cancelled
, NULL
);
5538 if (show_diffstat
&& dsa
.nfiles
> 0) {
5541 if (asprintf(&header
, "diffstat %s%s",
5542 diff_staged
? "-s " : "",
5543 got_worktree_get_root_path(worktree
)) == -1) {
5544 error
= got_error_from_errno("asprintf");
5548 error
= print_diffstat(&dsa
, header
);
5554 error
= printfile(outfile
);
5558 if (ncommit_args
== 1) {
5559 struct got_commit_object
*commit
;
5560 error
= got_object_open_as_commit(&commit
, repo
, ids
[0]);
5564 labels
[1] = labels
[0];
5566 if (got_object_commit_get_nparents(commit
) > 0) {
5567 const struct got_object_id_queue
*pids
;
5568 struct got_object_qid
*pid
;
5569 pids
= got_object_commit_get_parent_ids(commit
);
5570 pid
= STAILQ_FIRST(pids
);
5571 ids
[0] = got_object_id_dup(&pid
->id
);
5572 if (ids
[0] == NULL
) {
5573 error
= got_error_from_errno(
5574 "got_object_id_dup");
5575 got_object_commit_close(commit
);
5578 error
= got_object_id_str(&labels
[0], ids
[0]);
5580 got_object_commit_close(commit
);
5585 labels
[0] = strdup("/dev/null");
5586 if (labels
[0] == NULL
) {
5587 error
= got_error_from_errno("strdup");
5588 got_object_commit_close(commit
);
5593 got_object_commit_close(commit
);
5596 if (ncommit_args
== 0 && argc
> 2) {
5597 error
= got_error_msg(GOT_ERR_BAD_PATH
,
5598 "path arguments cannot be used when diffing two objects");
5603 error
= got_object_get_type(&type1
, repo
, ids
[0]);
5608 error
= got_object_get_type(&type2
, repo
, ids
[1]);
5611 if (type1
!= GOT_OBJ_TYPE_ANY
&& type1
!= type2
) {
5612 error
= got_error(GOT_ERR_OBJ_TYPE
);
5615 if (type1
== GOT_OBJ_TYPE_BLOB
&& argc
> 2) {
5616 error
= got_error_msg(GOT_ERR_OBJ_TYPE
,
5617 "path arguments cannot be used when diffing blobs");
5621 for (i
= 0; ncommit_args
> 0 && i
< argc
; i
++) {
5623 struct got_pathlist_entry
*new;
5627 error
= got_worktree_resolve_path(&p
, worktree
,
5631 prefix
= got_worktree_get_path_prefix(worktree
);
5632 while (prefix
[0] == '/')
5634 if (asprintf(&in_repo_path
, "%s%s%s", prefix
,
5635 (p
[0] != '\0' && prefix
[0] != '\0') ? "/" : "",
5637 error
= got_error_from_errno("asprintf");
5643 char *mapped_path
, *s
;
5644 error
= got_repo_map_path(&mapped_path
, repo
, argv
[i
]);
5650 in_repo_path
= strdup(s
);
5651 if (in_repo_path
== NULL
) {
5652 error
= got_error_from_errno("asprintf");
5659 error
= got_pathlist_insert(&new, &paths
, in_repo_path
, NULL
);
5660 if (error
|| new == NULL
/* duplicate */)
5667 /* Release work tree lock. */
5668 got_worktree_close(worktree
);
5672 fd1
= got_opentempfd();
5674 error
= got_error_from_errno("got_opentempfd");
5678 fd2
= got_opentempfd();
5680 error
= got_error_from_errno("got_opentempfd");
5684 switch (type1
== GOT_OBJ_TYPE_ANY
? type2
: type1
) {
5685 case GOT_OBJ_TYPE_BLOB
:
5686 error
= got_diff_objects_as_blobs(NULL
, NULL
, f1
, f2
,
5687 fd1
, fd2
, ids
[0], ids
[1], NULL
, NULL
,
5688 GOT_DIFF_ALGORITHM_PATIENCE
, diff_context
,
5689 ignore_whitespace
, force_text_diff
,
5690 show_diffstat
? &dsa
: NULL
, repo
, outfile
);
5692 case GOT_OBJ_TYPE_TREE
:
5693 error
= got_diff_objects_as_trees(NULL
, NULL
, f1
, f2
, fd1
, fd2
,
5694 ids
[0], ids
[1], &paths
, "", "",
5695 GOT_DIFF_ALGORITHM_PATIENCE
, diff_context
,
5696 ignore_whitespace
, force_text_diff
,
5697 show_diffstat
? &dsa
: NULL
, repo
, outfile
);
5699 case GOT_OBJ_TYPE_COMMIT
:
5700 fprintf(outfile
, "diff %s %s\n", labels
[0], labels
[1]);
5701 error
= got_diff_objects_as_commits(NULL
, NULL
, f1
, f2
,
5702 fd1
, fd2
, ids
[0], ids
[1], &paths
,
5703 GOT_DIFF_ALGORITHM_PATIENCE
, diff_context
,
5704 ignore_whitespace
, force_text_diff
,
5705 show_diffstat
? &dsa
: NULL
, repo
, outfile
);
5708 error
= got_error(GOT_ERR_OBJ_TYPE
);
5713 if (show_diffstat
&& dsa
.nfiles
> 0) {
5714 char *header
= NULL
;
5716 if (asprintf(&header
, "diffstat %s %s",
5717 labels
[0], labels
[1]) == -1) {
5718 error
= got_error_from_errno("asprintf");
5722 error
= print_diffstat(&dsa
, header
);
5728 error
= printfile(outfile
);
5737 got_worktree_close(worktree
);
5739 const struct got_error
*close_err
= got_repo_close(repo
);
5744 const struct got_error
*pack_err
=
5745 got_repo_pack_fds_close(pack_fds
);
5749 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_PATH
);
5750 got_pathlist_free(&diffstat_paths
, GOT_PATHLIST_FREE_ALL
);
5751 got_ref_list_free(&refs
);
5752 if (outfile
&& fclose(outfile
) == EOF
&& error
== NULL
)
5753 error
= got_error_from_errno("fclose");
5754 if (f1
&& fclose(f1
) == EOF
&& error
== NULL
)
5755 error
= got_error_from_errno("fclose");
5756 if (f2
&& fclose(f2
) == EOF
&& error
== NULL
)
5757 error
= got_error_from_errno("fclose");
5758 if (fd1
!= -1 && close(fd1
) == -1 && error
== NULL
)
5759 error
= got_error_from_errno("close");
5760 if (fd2
!= -1 && close(fd2
) == -1 && error
== NULL
)
5761 error
= got_error_from_errno("close");
5769 "usage: %s blame [-c commit] [-r repository-path] path\n",
5778 char datebuf
[11]; /* YYYY-MM-DD + NUL */
5781 struct blame_cb_args
{
5782 struct blame_line
*lines
;
5786 off_t
*line_offsets
;
5788 struct got_repository
*repo
;
5791 static const struct got_error
*
5792 blame_cb(void *arg
, int nlines
, int lineno
,
5793 struct got_commit_object
*commit
, struct got_object_id
*id
)
5795 const struct got_error
*err
= NULL
;
5796 struct blame_cb_args
*a
= arg
;
5797 struct blame_line
*bline
;
5799 size_t linesize
= 0;
5802 time_t committer_time
;
5804 if (nlines
!= a
->nlines
||
5805 (lineno
!= -1 && lineno
< 1) || lineno
> a
->nlines
)
5806 return got_error(GOT_ERR_RANGE
);
5808 if (sigint_received
)
5809 return got_error(GOT_ERR_ITER_COMPLETED
);
5812 return NULL
; /* no change in this commit */
5814 /* Annotate this line. */
5815 bline
= &a
->lines
[lineno
- 1];
5816 if (bline
->annotated
)
5818 err
= got_object_id_str(&bline
->id_str
, id
);
5822 bline
->committer
= strdup(got_object_commit_get_committer(commit
));
5823 if (bline
->committer
== NULL
) {
5824 err
= got_error_from_errno("strdup");
5828 committer_time
= got_object_commit_get_committer_time(commit
);
5829 if (gmtime_r(&committer_time
, &tm
) == NULL
)
5830 return got_error_from_errno("gmtime_r");
5831 if (strftime(bline
->datebuf
, sizeof(bline
->datebuf
), "%F", &tm
) == 0) {
5832 err
= got_error(GOT_ERR_NO_SPACE
);
5835 bline
->annotated
= 1;
5837 /* Print lines annotated so far. */
5838 bline
= &a
->lines
[a
->lineno_cur
- 1];
5839 if (!bline
->annotated
)
5842 offset
= a
->line_offsets
[a
->lineno_cur
- 1];
5843 if (fseeko(a
->f
, offset
, SEEK_SET
) == -1) {
5844 err
= got_error_from_errno("fseeko");
5848 while (a
->lineno_cur
<= a
->nlines
&& bline
->annotated
) {
5849 char *smallerthan
, *at
, *nl
, *committer
;
5852 if (getline(&line
, &linesize
, a
->f
) == -1) {
5854 err
= got_error_from_errno("getline");
5858 committer
= bline
->committer
;
5859 smallerthan
= strchr(committer
, '<');
5860 if (smallerthan
&& smallerthan
[1] != '\0')
5861 committer
= smallerthan
+ 1;
5862 at
= strchr(committer
, '@');
5865 len
= strlen(committer
);
5867 committer
[8] = '\0';
5869 nl
= strchr(line
, '\n');
5872 printf("%.*d) %.8s %s %-8s %s\n", a
->nlines_prec
, a
->lineno_cur
,
5873 bline
->id_str
, bline
->datebuf
, committer
, line
);
5876 bline
= &a
->lines
[a
->lineno_cur
- 1];
5883 static const struct got_error
*
5884 cmd_blame(int argc
, char *argv
[])
5886 const struct got_error
*error
;
5887 struct got_repository
*repo
= NULL
;
5888 struct got_worktree
*worktree
= NULL
;
5889 char *path
, *cwd
= NULL
, *repo_path
= NULL
, *in_repo_path
= NULL
;
5890 char *link_target
= NULL
;
5891 struct got_object_id
*obj_id
= NULL
;
5892 struct got_object_id
*commit_id
= NULL
;
5893 struct got_commit_object
*commit
= NULL
;
5894 struct got_blob_object
*blob
= NULL
;
5895 char *commit_id_str
= NULL
, *keyword_idstr
= NULL
;
5896 struct blame_cb_args bca
;
5897 int ch
, obj_type
, i
, fd1
= -1, fd2
= -1, fd3
= -1;
5899 int *pack_fds
= NULL
;
5900 FILE *f1
= NULL
, *f2
= NULL
;
5902 fd1
= got_opentempfd();
5904 return got_error_from_errno("got_opentempfd");
5906 memset(&bca
, 0, sizeof(bca
));
5909 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
5914 while ((ch
= getopt(argc
, argv
, "c:r:")) != -1) {
5917 commit_id_str
= optarg
;
5920 repo_path
= realpath(optarg
, NULL
);
5921 if (repo_path
== NULL
)
5922 return got_error_from_errno2("realpath",
5924 got_path_strip_trailing_slashes(repo_path
);
5940 cwd
= getcwd(NULL
, 0);
5942 error
= got_error_from_errno("getcwd");
5946 error
= got_repo_pack_fds_open(&pack_fds
);
5950 if (repo_path
== NULL
) {
5951 error
= got_worktree_open(&worktree
, cwd
,
5952 GOT_WORKTREE_GOT_DIR
);
5953 if (error
&& error
->code
!= GOT_ERR_NOT_WORKTREE
)
5959 strdup(got_worktree_get_repo_path(worktree
));
5960 if (repo_path
== NULL
) {
5961 error
= got_error_from_errno("strdup");
5966 repo_path
= strdup(cwd
);
5967 if (repo_path
== NULL
) {
5968 error
= got_error_from_errno("strdup");
5974 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
5979 const char *prefix
= got_worktree_get_path_prefix(worktree
);
5982 error
= got_worktree_resolve_path(&p
, worktree
, path
);
5985 if (asprintf(&in_repo_path
, "%s%s%s", prefix
,
5986 (p
[0] != '\0' && !got_path_is_root_dir(prefix
)) ? "/" : "",
5988 error
= got_error_from_errno("asprintf");
5993 error
= apply_unveil(got_repo_get_path(repo
), 1, NULL
);
5995 error
= apply_unveil(got_repo_get_path(repo
), 1, NULL
);
5998 error
= got_repo_map_path(&in_repo_path
, repo
, path
);
6003 if (commit_id_str
== NULL
) {
6004 struct got_reference
*head_ref
;
6005 error
= got_ref_open(&head_ref
, repo
, worktree
?
6006 got_worktree_get_head_ref_name(worktree
) : GOT_REF_HEAD
, 0);
6009 error
= got_ref_resolve(&commit_id
, repo
, head_ref
);
6010 got_ref_close(head_ref
);
6014 struct got_reflist_head refs
;
6017 error
= got_ref_list(&refs
, repo
, NULL
, got_ref_cmp_by_name
,
6022 error
= got_keyword_to_idstr(&keyword_idstr
, commit_id_str
,
6026 if (keyword_idstr
!= NULL
)
6027 commit_id_str
= keyword_idstr
;
6029 error
= got_repo_match_object_id(&commit_id
, NULL
,
6030 commit_id_str
, GOT_OBJ_TYPE_COMMIT
, &refs
, repo
);
6031 got_ref_list_free(&refs
);
6037 /* Release work tree lock. */
6038 got_worktree_close(worktree
);
6042 error
= got_object_open_as_commit(&commit
, repo
, commit_id
);
6046 error
= got_object_resolve_symlinks(&link_target
, in_repo_path
,
6051 error
= got_object_id_by_path(&obj_id
, repo
, commit
,
6052 link_target
? link_target
: in_repo_path
);
6056 error
= got_object_get_type(&obj_type
, repo
, obj_id
);
6060 if (obj_type
!= GOT_OBJ_TYPE_BLOB
) {
6061 error
= got_error_path(link_target
? link_target
: in_repo_path
,
6066 error
= got_object_open_as_blob(&blob
, repo
, obj_id
, 8192, fd1
);
6069 bca
.f
= got_opentemp();
6070 if (bca
.f
== NULL
) {
6071 error
= got_error_from_errno("got_opentemp");
6074 error
= got_object_blob_dump_to_file(&filesize
, &bca
.nlines
,
6075 &bca
.line_offsets
, bca
.f
, blob
);
6076 if (error
|| bca
.nlines
== 0)
6079 /* Don't include \n at EOF in the blame line count. */
6080 if (bca
.line_offsets
[bca
.nlines
- 1] == filesize
)
6083 bca
.lines
= calloc(bca
.nlines
, sizeof(*bca
.lines
));
6084 if (bca
.lines
== NULL
) {
6085 error
= got_error_from_errno("calloc");
6089 bca
.nlines_prec
= 0;
6097 fd2
= got_opentempfd();
6099 error
= got_error_from_errno("got_opentempfd");
6102 fd3
= got_opentempfd();
6104 error
= got_error_from_errno("got_opentempfd");
6107 f1
= got_opentemp();
6109 error
= got_error_from_errno("got_opentemp");
6112 f2
= got_opentemp();
6114 error
= got_error_from_errno("got_opentemp");
6117 error
= got_blame(link_target
? link_target
: in_repo_path
, commit_id
,
6118 repo
, GOT_DIFF_ALGORITHM_PATIENCE
, blame_cb
, &bca
,
6119 check_cancelled
, NULL
, fd2
, fd3
, f1
, f2
);
6121 free(keyword_idstr
);
6129 got_object_commit_close(commit
);
6131 if (fd1
!= -1 && close(fd1
) == -1 && error
== NULL
)
6132 error
= got_error_from_errno("close");
6133 if (fd2
!= -1 && close(fd2
) == -1 && error
== NULL
)
6134 error
= got_error_from_errno("close");
6135 if (fd3
!= -1 && close(fd3
) == -1 && error
== NULL
)
6136 error
= got_error_from_errno("close");
6137 if (f1
&& fclose(f1
) == EOF
&& error
== NULL
)
6138 error
= got_error_from_errno("fclose");
6139 if (f2
&& fclose(f2
) == EOF
&& error
== NULL
)
6140 error
= got_error_from_errno("fclose");
6143 got_object_blob_close(blob
);
6145 got_worktree_close(worktree
);
6147 const struct got_error
*close_err
= got_repo_close(repo
);
6152 const struct got_error
*pack_err
=
6153 got_repo_pack_fds_close(pack_fds
);
6158 for (i
= 0; i
< bca
.nlines
; i
++) {
6159 struct blame_line
*bline
= &bca
.lines
[i
];
6160 free(bline
->id_str
);
6161 free(bline
->committer
);
6165 free(bca
.line_offsets
);
6166 if (bca
.f
&& fclose(bca
.f
) == EOF
&& error
== NULL
)
6167 error
= got_error_from_errno("fclose");
6174 fprintf(stderr
, "usage: %s tree [-iR] [-c commit] [-r repository-path] "
6175 "[path]\n", getprogname());
6179 static const struct got_error
*
6180 print_entry(struct got_tree_entry
*te
, const char *id
, const char *path
,
6181 const char *root_path
, struct got_repository
*repo
)
6183 const struct got_error
*err
= NULL
;
6184 int is_root_path
= (strcmp(path
, root_path
) == 0);
6185 const char *modestr
= "";
6186 mode_t mode
= got_tree_entry_get_mode(te
);
6187 char *link_target
= NULL
;
6189 path
+= strlen(root_path
);
6190 while (path
[0] == '/')
6193 if (got_object_tree_entry_is_submodule(te
))
6195 else if (S_ISLNK(mode
)) {
6198 err
= got_tree_entry_get_symlink_target(&link_target
, te
, repo
);
6201 for (i
= 0; link_target
[i
] != '\0'; i
++) {
6202 if (!isprint((unsigned char)link_target
[i
]))
6203 link_target
[i
] = '?';
6208 else if (S_ISDIR(mode
))
6210 else if (mode
& S_IXUSR
)
6213 printf("%s%s%s%s%s%s%s\n", id
? id
: "", path
,
6214 is_root_path
? "" : "/", got_tree_entry_get_name(te
), modestr
,
6215 link_target
? " -> ": "", link_target
? link_target
: "");
6221 static const struct got_error
*
6222 print_tree(const char *path
, struct got_commit_object
*commit
,
6223 int show_ids
, int recurse
, const char *root_path
,
6224 struct got_repository
*repo
)
6226 const struct got_error
*err
= NULL
;
6227 struct got_object_id
*tree_id
= NULL
;
6228 struct got_tree_object
*tree
= NULL
;
6231 err
= got_object_id_by_path(&tree_id
, repo
, commit
, path
);
6235 err
= got_object_open_as_tree(&tree
, repo
, tree_id
);
6238 nentries
= got_object_tree_get_nentries(tree
);
6239 for (i
= 0; i
< nentries
; i
++) {
6240 struct got_tree_entry
*te
;
6243 if (sigint_received
|| sigpipe_received
)
6246 te
= got_object_tree_get_entry(tree
, i
);
6249 err
= got_object_id_str(&id_str
,
6250 got_tree_entry_get_id(te
));
6253 if (asprintf(&id
, "%s ", id_str
) == -1) {
6254 err
= got_error_from_errno("asprintf");
6260 err
= print_entry(te
, id
, path
, root_path
, repo
);
6265 if (recurse
&& S_ISDIR(got_tree_entry_get_mode(te
))) {
6267 if (asprintf(&child_path
, "%s%s%s", path
,
6268 path
[0] == '/' && path
[1] == '\0' ? "" : "/",
6269 got_tree_entry_get_name(te
)) == -1) {
6270 err
= got_error_from_errno("asprintf");
6273 err
= print_tree(child_path
, commit
, show_ids
, 1,
6282 got_object_tree_close(tree
);
6287 static const struct got_error
*
6288 cmd_tree(int argc
, char *argv
[])
6290 const struct got_error
*error
;
6291 struct got_repository
*repo
= NULL
;
6292 struct got_worktree
*worktree
= NULL
;
6293 const char *path
, *refname
= NULL
;
6294 char *cwd
= NULL
, *repo_path
= NULL
, *in_repo_path
= NULL
;
6295 struct got_object_id
*commit_id
= NULL
;
6296 struct got_commit_object
*commit
= NULL
;
6297 char *commit_id_str
= NULL
, *keyword_idstr
= NULL
;
6298 int show_ids
= 0, recurse
= 0;
6300 int *pack_fds
= NULL
;
6303 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
6308 while ((ch
= getopt(argc
, argv
, "c:iRr:")) != -1) {
6311 commit_id_str
= optarg
;
6320 repo_path
= realpath(optarg
, NULL
);
6321 if (repo_path
== NULL
)
6322 return got_error_from_errno2("realpath",
6324 got_path_strip_trailing_slashes(repo_path
);
6342 cwd
= getcwd(NULL
, 0);
6344 error
= got_error_from_errno("getcwd");
6348 error
= got_repo_pack_fds_open(&pack_fds
);
6352 if (repo_path
== NULL
) {
6353 error
= got_worktree_open(&worktree
, cwd
,
6354 GOT_WORKTREE_GOT_DIR
);
6355 if (error
&& error
->code
!= GOT_ERR_NOT_WORKTREE
)
6361 strdup(got_worktree_get_repo_path(worktree
));
6362 if (repo_path
== NULL
)
6363 error
= got_error_from_errno("strdup");
6367 repo_path
= strdup(cwd
);
6368 if (repo_path
== NULL
) {
6369 error
= got_error_from_errno("strdup");
6375 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
6380 const char *prefix
= got_worktree_get_path_prefix(worktree
);
6383 if (path
== NULL
|| got_path_is_root_dir(path
))
6385 error
= got_worktree_resolve_path(&p
, worktree
, path
);
6388 if (asprintf(&in_repo_path
, "%s%s%s", prefix
,
6389 (p
[0] != '\0' && !got_path_is_root_dir(prefix
)) ? "/" : "",
6391 error
= got_error_from_errno("asprintf");
6396 error
= apply_unveil(got_repo_get_path(repo
), 1, NULL
);
6400 error
= apply_unveil(got_repo_get_path(repo
), 1, NULL
);
6405 error
= got_repo_map_path(&in_repo_path
, repo
, path
);
6410 if (commit_id_str
== NULL
) {
6411 struct got_reference
*head_ref
;
6413 refname
= got_worktree_get_head_ref_name(worktree
);
6415 refname
= GOT_REF_HEAD
;
6416 error
= got_ref_open(&head_ref
, repo
, refname
, 0);
6419 error
= got_ref_resolve(&commit_id
, repo
, head_ref
);
6420 got_ref_close(head_ref
);
6424 struct got_reflist_head refs
;
6427 error
= got_ref_list(&refs
, repo
, NULL
, got_ref_cmp_by_name
,
6432 error
= got_keyword_to_idstr(&keyword_idstr
, commit_id_str
,
6436 if (keyword_idstr
!= NULL
)
6437 commit_id_str
= keyword_idstr
;
6439 error
= got_repo_match_object_id(&commit_id
, NULL
,
6440 commit_id_str
, GOT_OBJ_TYPE_COMMIT
, &refs
, repo
);
6441 got_ref_list_free(&refs
);
6447 /* Release work tree lock. */
6448 got_worktree_close(worktree
);
6452 error
= got_object_open_as_commit(&commit
, repo
, commit_id
);
6456 error
= print_tree(in_repo_path
, commit
, show_ids
, recurse
,
6457 in_repo_path
, repo
);
6459 free(keyword_idstr
);
6465 got_object_commit_close(commit
);
6467 got_worktree_close(worktree
);
6469 const struct got_error
*close_err
= got_repo_close(repo
);
6474 const struct got_error
*pack_err
=
6475 got_repo_pack_fds_close(pack_fds
);
6485 fprintf(stderr
, "usage: %s status [-I] [-S status-codes] "
6486 "[-s status-codes] [path ...]\n", getprogname());
6490 struct got_status_arg
{
6495 static const struct got_error
*
6496 print_status(void *arg
, unsigned char status
, unsigned char staged_status
,
6497 const char *path
, struct got_object_id
*blob_id
,
6498 struct got_object_id
*staged_blob_id
, struct got_object_id
*commit_id
,
6499 int dirfd
, const char *de_name
)
6501 struct got_status_arg
*st
= arg
;
6503 if (status
== staged_status
&& (status
== GOT_STATUS_DELETE
))
6504 status
= GOT_STATUS_NO_CHANGE
;
6505 if (st
!= NULL
&& st
->status_codes
) {
6506 size_t ncodes
= strlen(st
->status_codes
);
6509 for (i
= 0; i
< ncodes
; i
++) {
6511 if (status
== st
->status_codes
[i
] ||
6512 staged_status
== st
->status_codes
[i
]) {
6517 if (status
== st
->status_codes
[i
] ||
6518 staged_status
== st
->status_codes
[i
])
6523 if (st
->suppress
&& j
== 0)
6530 printf("%c%c %s\n", status
, staged_status
, path
);
6534 static const struct got_error
*
6535 show_operation_in_progress(struct got_worktree
*worktree
,
6536 struct got_repository
*repo
)
6538 const struct got_error
*err
;
6539 char *new_base_branch_name
= NULL
;
6540 char *branch_name
= NULL
;
6541 int rebase_in_progress
, histedit_in_progress
, merge_in_progress
;
6543 err
= got_worktree_rebase_in_progress(&rebase_in_progress
, worktree
);
6546 if (rebase_in_progress
) {
6547 err
= got_worktree_rebase_info(&new_base_branch_name
,
6548 &branch_name
, worktree
, repo
);
6551 printf("Work tree is rebasing %s onto %s\n",
6552 branch_name
, new_base_branch_name
);
6555 err
= got_worktree_histedit_in_progress(&histedit_in_progress
,
6559 if (histedit_in_progress
) {
6560 err
= got_worktree_histedit_info(&branch_name
, worktree
, repo
);
6563 printf("Work tree is editing the history of %s\n", branch_name
);
6566 err
= got_worktree_merge_in_progress(&merge_in_progress
,
6570 if (merge_in_progress
) {
6571 err
= got_worktree_merge_info(&branch_name
, worktree
,
6575 printf("Work tree is merging %s into %s\n", branch_name
,
6576 got_worktree_get_head_ref_name(worktree
));
6579 free(new_base_branch_name
);
6584 static const struct got_error
*
6585 cmd_status(int argc
, char *argv
[])
6587 const struct got_error
*close_err
, *error
= NULL
;
6588 struct got_repository
*repo
= NULL
;
6589 struct got_worktree
*worktree
= NULL
;
6590 struct got_status_arg st
;
6592 struct got_pathlist_head paths
;
6593 int ch
, i
, no_ignores
= 0;
6594 int *pack_fds
= NULL
;
6598 memset(&st
, 0, sizeof(st
));
6599 st
.status_codes
= NULL
;
6603 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
6608 while ((ch
= getopt(argc
, argv
, "IS:s:")) != -1) {
6614 if (st
.status_codes
!= NULL
&& st
.suppress
== 0)
6615 option_conflict('S', 's');
6619 for (i
= 0; optarg
[i
] != '\0'; i
++) {
6620 switch (optarg
[i
]) {
6621 case GOT_STATUS_MODIFY
:
6622 case GOT_STATUS_ADD
:
6623 case GOT_STATUS_DELETE
:
6624 case GOT_STATUS_CONFLICT
:
6625 case GOT_STATUS_MISSING
:
6626 case GOT_STATUS_OBSTRUCTED
:
6627 case GOT_STATUS_UNVERSIONED
:
6628 case GOT_STATUS_MODE_CHANGE
:
6629 case GOT_STATUS_NONEXISTENT
:
6632 errx(1, "invalid status code '%c'",
6636 if (ch
== 's' && st
.suppress
)
6637 option_conflict('s', 'S');
6638 st
.status_codes
= optarg
;
6649 cwd
= getcwd(NULL
, 0);
6651 error
= got_error_from_errno("getcwd");
6655 error
= got_repo_pack_fds_open(&pack_fds
);
6659 error
= got_worktree_open(&worktree
, cwd
,
6660 GOT_WORKTREE_GOT_DIR
);
6662 if (error
->code
== GOT_ERR_NOT_WORKTREE
)
6663 error
= wrap_not_worktree_error(error
, "status", cwd
);
6667 error
= got_repo_open(&repo
, got_worktree_get_repo_path(worktree
),
6672 error
= apply_unveil(got_repo_get_path(repo
), 1,
6673 got_worktree_get_root_path(worktree
));
6677 error
= get_worktree_paths_from_argv(&paths
, argc
, argv
, worktree
);
6681 error
= got_worktree_status(worktree
, &paths
, repo
, no_ignores
,
6682 print_status
, &st
, check_cancelled
, NULL
);
6686 error
= show_operation_in_progress(worktree
, repo
);
6689 const struct got_error
*pack_err
=
6690 got_repo_pack_fds_close(pack_fds
);
6695 close_err
= got_repo_close(repo
);
6699 if (worktree
!= NULL
) {
6700 close_err
= got_worktree_close(worktree
);
6705 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_PATH
);
6713 fprintf(stderr
, "usage: %s ref [-dlt] [-c object] [-r repository-path] "
6714 "[-s reference] [name]\n", getprogname());
6718 static const struct got_error
*
6719 list_refs(struct got_repository
*repo
, const char *refname
, int sort_by_time
)
6721 static const struct got_error
*err
= NULL
;
6722 struct got_reflist_head refs
;
6723 struct got_reflist_entry
*re
;
6726 err
= got_ref_list(&refs
, repo
, refname
, sort_by_time
?
6727 got_ref_cmp_by_commit_timestamp_descending
: got_ref_cmp_by_name
,
6732 TAILQ_FOREACH(re
, &refs
, entry
) {
6734 refstr
= got_ref_to_str(re
->ref
);
6735 if (refstr
== NULL
) {
6736 err
= got_error_from_errno("got_ref_to_str");
6739 printf("%s: %s\n", got_ref_get_name(re
->ref
), refstr
);
6743 got_ref_list_free(&refs
);
6747 static const struct got_error
*
6748 delete_ref_by_name(struct got_repository
*repo
, const char *refname
)
6750 const struct got_error
*err
;
6751 struct got_reference
*ref
;
6753 err
= got_ref_open(&ref
, repo
, refname
, 0);
6757 err
= delete_ref(repo
, ref
);
6762 static const struct got_error
*
6763 add_ref(struct got_repository
*repo
, const char *refname
, const char *target
)
6765 const struct got_error
*err
= NULL
;
6766 struct got_object_id
*id
= NULL
;
6767 struct got_reference
*ref
= NULL
;
6768 struct got_reflist_head refs
;
6771 * Don't let the user create a reference name with a leading '-'.
6772 * While technically a valid reference name, this case is usually
6773 * an unintended typo.
6775 if (refname
[0] == '-')
6776 return got_error_path(refname
, GOT_ERR_REF_NAME_MINUS
);
6779 err
= got_ref_list(&refs
, repo
, NULL
, got_ref_cmp_by_name
, NULL
);
6782 err
= got_repo_match_object_id(&id
, NULL
, target
, GOT_OBJ_TYPE_ANY
,
6784 got_ref_list_free(&refs
);
6788 err
= got_ref_alloc(&ref
, refname
, id
);
6792 err
= got_ref_write(ref
, repo
);
6800 static const struct got_error
*
6801 add_symref(struct got_repository
*repo
, const char *refname
, const char *target
)
6803 const struct got_error
*err
= NULL
;
6804 struct got_reference
*ref
= NULL
;
6805 struct got_reference
*target_ref
= NULL
;
6808 * Don't let the user create a reference name with a leading '-'.
6809 * While technically a valid reference name, this case is usually
6810 * an unintended typo.
6812 if (refname
[0] == '-')
6813 return got_error_path(refname
, GOT_ERR_REF_NAME_MINUS
);
6815 err
= got_ref_open(&target_ref
, repo
, target
, 0);
6819 err
= got_ref_alloc_symref(&ref
, refname
, target_ref
);
6823 err
= got_ref_write(ref
, repo
);
6826 got_ref_close(target_ref
);
6832 static const struct got_error
*
6833 cmd_ref(int argc
, char *argv
[])
6835 const struct got_error
*error
= NULL
;
6836 struct got_repository
*repo
= NULL
;
6837 struct got_worktree
*worktree
= NULL
;
6838 char *cwd
= NULL
, *repo_path
= NULL
;
6839 int ch
, do_list
= 0, do_delete
= 0, sort_by_time
= 0;
6840 const char *obj_arg
= NULL
, *symref_target
= NULL
;
6841 char *refname
= NULL
, *keyword_idstr
= NULL
;
6842 int *pack_fds
= NULL
;
6845 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
6846 "sendfd unveil", NULL
) == -1)
6850 while ((ch
= getopt(argc
, argv
, "c:dlr:s:t")) != -1) {
6862 repo_path
= realpath(optarg
, NULL
);
6863 if (repo_path
== NULL
)
6864 return got_error_from_errno2("realpath",
6866 got_path_strip_trailing_slashes(repo_path
);
6869 symref_target
= optarg
;
6880 if (obj_arg
&& do_list
)
6881 option_conflict('c', 'l');
6882 if (obj_arg
&& do_delete
)
6883 option_conflict('c', 'd');
6884 if (obj_arg
&& symref_target
)
6885 option_conflict('c', 's');
6886 if (symref_target
&& do_delete
)
6887 option_conflict('s', 'd');
6888 if (symref_target
&& do_list
)
6889 option_conflict('s', 'l');
6890 if (do_delete
&& do_list
)
6891 option_conflict('d', 'l');
6892 if (sort_by_time
&& !do_list
)
6893 errx(1, "-t option requires -l option");
6899 if (argc
!= 0 && argc
!= 1)
6902 refname
= strdup(argv
[0]);
6903 if (refname
== NULL
) {
6904 error
= got_error_from_errno("strdup");
6911 refname
= strdup(argv
[0]);
6912 if (refname
== NULL
) {
6913 error
= got_error_from_errno("strdup");
6919 got_path_strip_trailing_slashes(refname
);
6921 cwd
= getcwd(NULL
, 0);
6923 error
= got_error_from_errno("getcwd");
6927 error
= got_repo_pack_fds_open(&pack_fds
);
6931 if (repo_path
== NULL
) {
6932 error
= got_worktree_open(&worktree
, cwd
,
6933 GOT_WORKTREE_GOT_DIR
);
6934 if (error
&& error
->code
!= GOT_ERR_NOT_WORKTREE
)
6940 strdup(got_worktree_get_repo_path(worktree
));
6941 if (repo_path
== NULL
)
6942 error
= got_error_from_errno("strdup");
6946 repo_path
= strdup(cwd
);
6947 if (repo_path
== NULL
) {
6948 error
= got_error_from_errno("strdup");
6954 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
6960 /* Remove "cpath" promise. */
6961 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
6967 error
= apply_unveil(got_repo_get_path(repo
), do_list
,
6968 worktree
? got_worktree_get_root_path(worktree
) : NULL
);
6973 error
= list_refs(repo
, refname
, sort_by_time
);
6975 error
= delete_ref_by_name(repo
, refname
);
6976 else if (symref_target
)
6977 error
= add_symref(repo
, refname
, symref_target
);
6979 if (obj_arg
== NULL
)
6982 error
= got_keyword_to_idstr(&keyword_idstr
, obj_arg
,
6986 if (keyword_idstr
!= NULL
)
6987 obj_arg
= keyword_idstr
;
6989 error
= add_ref(repo
, refname
, obj_arg
);
6994 const struct got_error
*close_err
= got_repo_close(repo
);
6999 got_worktree_close(worktree
);
7001 const struct got_error
*pack_err
=
7002 got_repo_pack_fds_close(pack_fds
);
7008 free(keyword_idstr
);
7015 fprintf(stderr
, "usage: %s branch [-lnt] [-c commit] [-d name] "
7016 "[-r repository-path] [name]\n", getprogname());
7020 static const struct got_error
*
7021 list_branch(struct got_repository
*repo
, struct got_worktree
*worktree
,
7022 struct got_reference
*ref
)
7024 const struct got_error
*err
= NULL
;
7025 const char *refname
;
7029 refname
= got_ref_get_name(ref
);
7030 if (worktree
&& strcmp(refname
,
7031 got_worktree_get_head_ref_name(worktree
)) == 0) {
7032 err
= got_worktree_get_state(&marker
, repo
, worktree
,
7033 check_cancelled
, NULL
);
7038 if (strncmp(refname
, "refs/heads/", 11) == 0)
7040 if (strncmp(refname
, "refs/got/worktree/", 18) == 0)
7042 if (strncmp(refname
, "refs/remotes/", 13) == 0)
7045 refstr
= got_ref_to_str(ref
);
7047 return got_error_from_errno("got_ref_to_str");
7049 printf("%c %s: %s\n", marker
, refname
, refstr
);
7054 static const struct got_error
*
7055 show_current_branch(struct got_repository
*repo
, struct got_worktree
*worktree
)
7057 const char *refname
;
7059 if (worktree
== NULL
)
7060 return got_error(GOT_ERR_NOT_WORKTREE
);
7062 refname
= got_worktree_get_head_ref_name(worktree
);
7064 if (strncmp(refname
, "refs/heads/", 11) == 0)
7066 if (strncmp(refname
, "refs/got/worktree/", 18) == 0)
7069 printf("%s\n", refname
);
7074 static const struct got_error
*
7075 list_branches(struct got_repository
*repo
, struct got_worktree
*worktree
,
7078 static const struct got_error
*err
= NULL
;
7079 struct got_reflist_head refs
;
7080 struct got_reflist_entry
*re
;
7081 struct got_reference
*temp_ref
= NULL
;
7082 int rebase_in_progress
, histedit_in_progress
;
7087 err
= got_worktree_rebase_in_progress(&rebase_in_progress
,
7092 err
= got_worktree_histedit_in_progress(&histedit_in_progress
,
7097 if (rebase_in_progress
|| histedit_in_progress
) {
7098 err
= got_ref_open(&temp_ref
, repo
,
7099 got_worktree_get_head_ref_name(worktree
), 0);
7102 list_branch(repo
, worktree
, temp_ref
);
7103 got_ref_close(temp_ref
);
7107 err
= got_ref_list(&refs
, repo
, "refs/heads", sort_by_time
?
7108 got_ref_cmp_by_commit_timestamp_descending
: got_ref_cmp_by_name
,
7113 TAILQ_FOREACH(re
, &refs
, entry
)
7114 list_branch(repo
, worktree
, re
->ref
);
7116 got_ref_list_free(&refs
);
7118 err
= got_ref_list(&refs
, repo
, "refs/remotes", sort_by_time
?
7119 got_ref_cmp_by_commit_timestamp_descending
: got_ref_cmp_by_name
,
7124 TAILQ_FOREACH(re
, &refs
, entry
)
7125 list_branch(repo
, worktree
, re
->ref
);
7127 got_ref_list_free(&refs
);
7132 static const struct got_error
*
7133 delete_branch(struct got_repository
*repo
, struct got_worktree
*worktree
,
7134 const char *branch_name
)
7136 const struct got_error
*err
= NULL
;
7137 struct got_reference
*ref
= NULL
;
7138 char *refname
, *remote_refname
= NULL
;
7140 if (strncmp(branch_name
, "refs/", 5) == 0)
7142 if (strncmp(branch_name
, "heads/", 6) == 0)
7144 else if (strncmp(branch_name
, "remotes/", 8) == 0)
7147 if (asprintf(&refname
, "refs/heads/%s", branch_name
) == -1)
7148 return got_error_from_errno("asprintf");
7150 if (asprintf(&remote_refname
, "refs/remotes/%s",
7151 branch_name
) == -1) {
7152 err
= got_error_from_errno("asprintf");
7156 err
= got_ref_open(&ref
, repo
, refname
, 0);
7158 const struct got_error
*err2
;
7159 if (err
->code
!= GOT_ERR_NOT_REF
)
7162 * Keep 'err' intact such that if neither branch exists
7163 * we report "refs/heads" rather than "refs/remotes" in
7164 * our error message.
7166 err2
= got_ref_open(&ref
, repo
, remote_refname
, 0);
7173 strcmp(got_worktree_get_head_ref_name(worktree
),
7174 got_ref_get_name(ref
)) == 0) {
7175 err
= got_error_msg(GOT_ERR_SAME_BRANCH
,
7176 "will not delete this work tree's current branch");
7180 err
= delete_ref(repo
, ref
);
7185 free(remote_refname
);
7189 static const struct got_error
*
7190 add_branch(struct got_repository
*repo
, const char *branch_name
,
7191 struct got_object_id
*base_commit_id
)
7193 const struct got_error
*err
= NULL
;
7194 struct got_reference
*ref
= NULL
;
7195 char *refname
= NULL
;
7198 * Don't let the user create a branch name with a leading '-'.
7199 * While technically a valid reference name, this case is usually
7200 * an unintended typo.
7202 if (branch_name
[0] == '-')
7203 return got_error_path(branch_name
, GOT_ERR_REF_NAME_MINUS
);
7205 if (strncmp(branch_name
, "refs/heads/", 11) == 0)
7208 if (asprintf(&refname
, "refs/heads/%s", branch_name
) == -1) {
7209 err
= got_error_from_errno("asprintf");
7213 err
= got_ref_open(&ref
, repo
, refname
, 0);
7215 err
= got_error(GOT_ERR_BRANCH_EXISTS
);
7217 } else if (err
->code
!= GOT_ERR_NOT_REF
)
7220 err
= got_ref_alloc(&ref
, refname
, base_commit_id
);
7224 err
= got_ref_write(ref
, repo
);
7232 static const struct got_error
*
7233 cmd_branch(int argc
, char *argv
[])
7235 const struct got_error
*error
= NULL
;
7236 struct got_repository
*repo
= NULL
;
7237 struct got_worktree
*worktree
= NULL
;
7238 char *cwd
= NULL
, *repo_path
= NULL
;
7239 int ch
, do_list
= 0, do_show
= 0, do_update
= 1, sort_by_time
= 0;
7240 const char *delref
= NULL
, *commit_id_arg
= NULL
;
7241 struct got_reference
*ref
= NULL
;
7242 struct got_pathlist_head paths
;
7243 struct got_object_id
*commit_id
= NULL
;
7244 char *commit_id_str
= NULL
, *keyword_idstr
= NULL
;;
7245 int *pack_fds
= NULL
;
7250 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
7251 "sendfd unveil", NULL
) == -1)
7255 while ((ch
= getopt(argc
, argv
, "c:d:lnr:t")) != -1) {
7258 commit_id_arg
= optarg
;
7270 repo_path
= realpath(optarg
, NULL
);
7271 if (repo_path
== NULL
)
7272 return got_error_from_errno2("realpath",
7274 got_path_strip_trailing_slashes(repo_path
);
7285 if (do_list
&& delref
)
7286 option_conflict('l', 'd');
7287 if (sort_by_time
&& !do_list
)
7288 errx(1, "-t option requires -l option");
7293 if (!do_list
&& !delref
&& argc
== 0)
7296 if ((do_list
|| delref
|| do_show
) && commit_id_arg
!= NULL
)
7297 errx(1, "-c option can only be used when creating a branch");
7299 if (do_list
|| delref
) {
7302 } else if (!do_show
&& argc
!= 1)
7305 cwd
= getcwd(NULL
, 0);
7307 error
= got_error_from_errno("getcwd");
7311 error
= got_repo_pack_fds_open(&pack_fds
);
7315 if (repo_path
== NULL
) {
7316 error
= got_worktree_open(&worktree
, cwd
,
7317 GOT_WORKTREE_GOT_DIR
);
7318 if (error
&& error
->code
!= GOT_ERR_NOT_WORKTREE
)
7324 strdup(got_worktree_get_repo_path(worktree
));
7325 if (repo_path
== NULL
)
7326 error
= got_error_from_errno("strdup");
7330 repo_path
= strdup(cwd
);
7331 if (repo_path
== NULL
) {
7332 error
= got_error_from_errno("strdup");
7338 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
7343 if (do_list
|| do_show
) {
7344 /* Remove "cpath" promise. */
7345 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
7351 error
= apply_unveil(got_repo_get_path(repo
), do_list
,
7352 worktree
? got_worktree_get_root_path(worktree
) : NULL
);
7357 error
= show_current_branch(repo
, worktree
);
7359 error
= list_branches(repo
, worktree
, sort_by_time
);
7361 error
= delete_branch(repo
, worktree
, delref
);
7363 struct got_reflist_head refs
;
7365 error
= got_ref_list(&refs
, repo
, NULL
, got_ref_cmp_by_name
,
7369 if (commit_id_arg
== NULL
)
7370 commit_id_arg
= worktree
?
7371 got_worktree_get_head_ref_name(worktree
) :
7374 error
= got_keyword_to_idstr(&keyword_idstr
,
7375 commit_id_arg
, repo
, worktree
);
7378 if (keyword_idstr
!= NULL
)
7379 commit_id_arg
= keyword_idstr
;
7381 error
= got_repo_match_object_id(&commit_id
, NULL
,
7382 commit_id_arg
, GOT_OBJ_TYPE_COMMIT
, &refs
, repo
);
7383 got_ref_list_free(&refs
);
7386 error
= add_branch(repo
, argv
[0], commit_id
);
7389 if (worktree
&& do_update
) {
7390 struct got_update_progress_arg upa
;
7391 char *branch_refname
= NULL
;
7393 error
= got_object_id_str(&commit_id_str
, commit_id
);
7396 error
= get_worktree_paths_from_argv(&paths
, 0, NULL
,
7400 if (asprintf(&branch_refname
, "refs/heads/%s", argv
[0])
7402 error
= got_error_from_errno("asprintf");
7405 error
= got_ref_open(&ref
, repo
, branch_refname
, 0);
7406 free(branch_refname
);
7409 error
= switch_head_ref(ref
, commit_id
, worktree
,
7413 error
= got_worktree_set_base_commit_id(worktree
, repo
,
7417 memset(&upa
, 0, sizeof(upa
));
7418 error
= got_worktree_checkout_files(worktree
, &paths
,
7419 repo
, update_progress
, &upa
, check_cancelled
,
7423 if (upa
.did_something
) {
7424 printf("Updated to %s: %s\n",
7425 got_worktree_get_head_ref_name(worktree
),
7428 print_update_progress_stats(&upa
);
7432 free(keyword_idstr
);
7436 const struct got_error
*close_err
= got_repo_close(repo
);
7441 got_worktree_close(worktree
);
7443 const struct got_error
*pack_err
=
7444 got_repo_pack_fds_close(pack_fds
);
7451 free(commit_id_str
);
7452 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_PATH
);
7460 fprintf(stderr
, "usage: %s tag [-lVv] [-c commit] [-m message] "
7461 "[-r repository-path] [-s signer-id] name\n", getprogname());
7466 static const struct got_error
*
7467 sort_tags(struct got_reflist_head
*sorted
, struct got_reflist_head
*tags
)
7469 const struct got_error
*err
= NULL
;
7470 struct got_reflist_entry
*re
, *se
, *new;
7471 struct got_object_id
*re_id
, *se_id
;
7472 struct got_tag_object
*re_tag
, *se_tag
;
7473 time_t re_time
, se_time
;
7475 STAILQ_FOREACH(re
, tags
, entry
) {
7476 se
= STAILQ_FIRST(sorted
);
7478 err
= got_reflist_entry_dup(&new, re
);
7481 STAILQ_INSERT_HEAD(sorted
, new, entry
);
7484 err
= got_ref_resolve(&re_id
, repo
, re
->ref
);
7487 err
= got_object_open_as_tag(&re_tag
, repo
, re_id
);
7491 re_time
= got_object_tag_get_tagger_time(re_tag
);
7492 got_object_tag_close(re_tag
);
7496 err
= got_ref_resolve(&se_id
, repo
, re
->ref
);
7499 err
= got_object_open_as_tag(&se_tag
, repo
, se_id
);
7503 se_time
= got_object_tag_get_tagger_time(se_tag
);
7504 got_object_tag_close(se_tag
);
7506 if (se_time
> re_time
) {
7507 err
= got_reflist_entry_dup(&new, re
);
7510 STAILQ_INSERT_AFTER(sorted
, se
, new, entry
);
7513 se
= STAILQ_NEXT(se
, entry
);
7522 static const struct got_error
*
7523 get_tag_refname(char **refname
, const char *tag_name
)
7525 const struct got_error
*err
;
7527 if (strncmp("refs/tags/", tag_name
, 10) == 0) {
7528 *refname
= strdup(tag_name
);
7529 if (*refname
== NULL
)
7530 return got_error_from_errno("strdup");
7531 } else if (asprintf(refname
, "refs/tags/%s", tag_name
) == -1) {
7532 err
= got_error_from_errno("asprintf");
7540 static const struct got_error
*
7541 list_tags(struct got_repository
*repo
, const char *tag_name
, int verify_tags
,
7542 const char *allowed_signers
, const char *revoked_signers
, int verbosity
)
7544 static const struct got_error
*err
= NULL
;
7545 struct got_reflist_head refs
;
7546 struct got_reflist_entry
*re
;
7547 char *wanted_refname
= NULL
;
7552 err
= got_ref_list(&refs
, repo
, "refs/tags", got_ref_cmp_tags
, repo
);
7557 struct got_reference
*ref
;
7558 err
= get_tag_refname(&wanted_refname
, tag_name
);
7561 /* Wanted tag reference should exist. */
7562 err
= got_ref_open(&ref
, repo
, wanted_refname
, 0);
7568 TAILQ_FOREACH(re
, &refs
, entry
) {
7569 const char *refname
;
7570 char *refstr
, *tagmsg0
, *tagmsg
, *line
, *id_str
, *datestr
;
7572 const char *tagger
, *ssh_sig
= NULL
;
7573 char *sig_msg
= NULL
;
7575 struct got_object_id
*id
;
7576 struct got_tag_object
*tag
;
7577 struct got_commit_object
*commit
= NULL
;
7579 refname
= got_ref_get_name(re
->ref
);
7580 if (strncmp(refname
, "refs/tags/", 10) != 0 ||
7581 (wanted_refname
&& strcmp(refname
, wanted_refname
) != 0))
7584 refstr
= got_ref_to_str(re
->ref
);
7585 if (refstr
== NULL
) {
7586 err
= got_error_from_errno("got_ref_to_str");
7590 err
= got_ref_resolve(&id
, repo
, re
->ref
);
7593 err
= got_object_open_as_tag(&tag
, repo
, id
);
7595 if (err
->code
!= GOT_ERR_OBJ_TYPE
) {
7599 /* "lightweight" tag */
7600 err
= got_object_open_as_commit(&commit
, repo
, id
);
7605 tagger
= got_object_commit_get_committer(commit
);
7607 got_object_commit_get_committer_time(commit
);
7608 err
= got_object_id_str(&id_str
, id
);
7614 tagger
= got_object_tag_get_tagger(tag
);
7615 tagger_time
= got_object_tag_get_tagger_time(tag
);
7616 err
= got_object_id_str(&id_str
,
7617 got_object_tag_get_object_id(tag
));
7622 if (tag
&& verify_tags
) {
7623 ssh_sig
= got_sigs_get_tagmsg_ssh_signature(
7624 got_object_tag_get_message(tag
));
7625 if (ssh_sig
&& allowed_signers
== NULL
) {
7626 err
= got_error_msg(
7627 GOT_ERR_VERIFY_TAG_SIGNATURE
,
7628 "SSH signature verification requires "
7629 "setting allowed_signers in "
7635 printf("%stag %s %s\n", GOT_COMMIT_SEP_STR
, refname
, refstr
);
7637 printf("from: %s\n", tagger
);
7638 datestr
= get_datestr(&tagger_time
, datebuf
);
7640 printf("date: %s UTC\n", datestr
);
7642 printf("object: %s %s\n", GOT_OBJ_LABEL_COMMIT
, id_str
);
7644 switch (got_object_tag_get_object_type(tag
)) {
7645 case GOT_OBJ_TYPE_BLOB
:
7646 printf("object: %s %s\n", GOT_OBJ_LABEL_BLOB
,
7649 case GOT_OBJ_TYPE_TREE
:
7650 printf("object: %s %s\n", GOT_OBJ_LABEL_TREE
,
7653 case GOT_OBJ_TYPE_COMMIT
:
7654 printf("object: %s %s\n", GOT_OBJ_LABEL_COMMIT
,
7657 case GOT_OBJ_TYPE_TAG
:
7658 printf("object: %s %s\n", GOT_OBJ_LABEL_TAG
,
7668 err
= got_sigs_verify_tag_ssh(&sig_msg
, tag
, ssh_sig
,
7669 allowed_signers
, revoked_signers
, verbosity
);
7670 if (err
&& err
->code
== GOT_ERR_BAD_TAG_SIGNATURE
)
7674 printf("signature: %s", sig_msg
);
7680 err
= got_object_commit_get_logmsg(&tagmsg0
, commit
);
7683 got_object_commit_close(commit
);
7685 tagmsg0
= strdup(got_object_tag_get_message(tag
));
7686 got_object_tag_close(tag
);
7687 if (tagmsg0
== NULL
) {
7688 err
= got_error_from_errno("strdup");
7695 line
= strsep(&tagmsg
, "\n");
7697 printf(" %s\n", line
);
7702 got_ref_list_free(&refs
);
7703 free(wanted_refname
);
7705 if (err
== NULL
&& bad_sigs
)
7706 err
= got_error(GOT_ERR_BAD_TAG_SIGNATURE
);
7710 static const struct got_error
*
7711 get_tag_message(char **tagmsg
, char **tagmsg_path
, const char *commit_id_str
,
7712 const char *tag_name
, const char *editor
, const char *repo_path
)
7714 const struct got_error
*err
= NULL
;
7715 char *template = NULL
, *initial_content
= NULL
;
7716 int initial_content_len
;
7719 if (asprintf(&template, GOT_TMPDIR_STR
"/got-tagmsg") == -1) {
7720 err
= got_error_from_errno("asprintf");
7724 initial_content_len
= asprintf(&initial_content
,
7725 "\n# tagging commit %s as %s\n",
7726 commit_id_str
, tag_name
);
7727 if (initial_content_len
== -1) {
7728 err
= got_error_from_errno("asprintf");
7732 err
= got_opentemp_named_fd(tagmsg_path
, &fd
, template, "");
7736 if (write(fd
, initial_content
, initial_content_len
) == -1) {
7737 err
= got_error_from_errno2("write", *tagmsg_path
);
7740 if (close(fd
) == -1) {
7741 err
= got_error_from_errno2("close", *tagmsg_path
);
7746 err
= edit_logmsg(tagmsg
, editor
, *tagmsg_path
, initial_content
,
7747 initial_content_len
, 1);
7749 free(initial_content
);
7752 if (fd
!= -1 && close(fd
) == -1 && err
== NULL
)
7753 err
= got_error_from_errno2("close", *tagmsg_path
);
7762 static const struct got_error
*
7763 add_tag(struct got_repository
*repo
, const char *tagger
,
7764 const char *tag_name
, const char *commit_arg
, const char *tagmsg_arg
,
7765 const char *signer_id
, const char *editor
, int verbosity
)
7767 const struct got_error
*err
= NULL
;
7768 struct got_object_id
*commit_id
= NULL
, *tag_id
= NULL
;
7769 char *label
= NULL
, *commit_id_str
= NULL
;
7770 struct got_reference
*ref
= NULL
;
7771 char *refname
= NULL
, *tagmsg
= NULL
;
7772 char *tagmsg_path
= NULL
, *tag_id_str
= NULL
;
7773 int preserve_tagmsg
= 0;
7774 struct got_reflist_head refs
;
7779 * Don't let the user create a tag name with a leading '-'.
7780 * While technically a valid reference name, this case is usually
7781 * an unintended typo.
7783 if (tag_name
[0] == '-')
7784 return got_error_path(tag_name
, GOT_ERR_REF_NAME_MINUS
);
7786 err
= got_ref_list(&refs
, repo
, NULL
, got_ref_cmp_by_name
, NULL
);
7790 err
= got_repo_match_object_id(&commit_id
, &label
, commit_arg
,
7791 GOT_OBJ_TYPE_COMMIT
, &refs
, repo
);
7795 err
= got_object_id_str(&commit_id_str
, commit_id
);
7799 err
= get_tag_refname(&refname
, tag_name
);
7802 if (strncmp("refs/tags/", tag_name
, 10) == 0)
7805 err
= got_ref_open(&ref
, repo
, refname
, 0);
7807 err
= got_error(GOT_ERR_TAG_EXISTS
);
7809 } else if (err
->code
!= GOT_ERR_NOT_REF
)
7812 if (tagmsg_arg
== NULL
) {
7813 err
= get_tag_message(&tagmsg
, &tagmsg_path
, commit_id_str
,
7814 tag_name
, editor
, got_repo_get_path(repo
));
7816 if (err
->code
!= GOT_ERR_COMMIT_MSG_EMPTY
&&
7817 tagmsg_path
!= NULL
)
7818 preserve_tagmsg
= 1;
7823 err
= got_object_tag_create(&tag_id
, tag_name
, commit_id
,
7824 tagger
, time(NULL
), tagmsg
? tagmsg
: tagmsg_arg
, signer_id
, repo
,
7828 preserve_tagmsg
= 1;
7832 err
= got_ref_alloc(&ref
, refname
, tag_id
);
7835 preserve_tagmsg
= 1;
7839 err
= got_ref_write(ref
, repo
);
7842 preserve_tagmsg
= 1;
7846 err
= got_object_id_str(&tag_id_str
, tag_id
);
7849 preserve_tagmsg
= 1;
7852 printf("Created tag %s\n", tag_id_str
);
7854 if (preserve_tagmsg
) {
7855 fprintf(stderr
, "%s: tag message preserved in %s\n",
7856 getprogname(), tagmsg_path
);
7857 } else if (tagmsg_path
&& unlink(tagmsg_path
) == -1 && err
== NULL
)
7858 err
= got_error_from_errno2("unlink", tagmsg_path
);
7863 free(commit_id_str
);
7867 got_ref_list_free(&refs
);
7871 static const struct got_error
*
7872 cmd_tag(int argc
, char *argv
[])
7874 const struct got_error
*error
= NULL
;
7875 struct got_repository
*repo
= NULL
;
7876 struct got_worktree
*worktree
= NULL
;
7877 char *cwd
= NULL
, *repo_path
= NULL
, *commit_id_str
= NULL
;
7878 char *gitconfig_path
= NULL
, *tagger
= NULL
, *keyword_idstr
= NULL
;
7879 char *allowed_signers
= NULL
, *revoked_signers
= NULL
, *editor
= NULL
;
7880 const char *signer_id
= NULL
;
7881 const char *tag_name
= NULL
, *commit_id_arg
= NULL
, *tagmsg
= NULL
;
7882 int ch
, do_list
= 0, verify_tags
= 0, verbosity
= 0;
7883 int *pack_fds
= NULL
;
7886 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
7887 "sendfd unveil", NULL
) == -1)
7891 while ((ch
= getopt(argc
, argv
, "c:lm:r:s:Vv")) != -1) {
7894 commit_id_arg
= optarg
;
7903 repo_path
= realpath(optarg
, NULL
);
7904 if (repo_path
== NULL
) {
7905 error
= got_error_from_errno2("realpath",
7909 got_path_strip_trailing_slashes(repo_path
);
7920 else if (verbosity
< 3)
7932 if (do_list
|| verify_tags
) {
7933 if (commit_id_arg
!= NULL
)
7935 "-c option can only be used when creating a tag");
7938 option_conflict('l', 'm');
7940 option_conflict('V', 'm');
7944 option_conflict('l', 's');
7946 option_conflict('V', 's');
7950 } else if (argc
!= 1)
7956 cwd
= getcwd(NULL
, 0);
7958 error
= got_error_from_errno("getcwd");
7962 error
= got_repo_pack_fds_open(&pack_fds
);
7966 if (repo_path
== NULL
) {
7967 error
= got_worktree_open(&worktree
, cwd
,
7968 GOT_WORKTREE_GOT_DIR
);
7969 if (error
&& error
->code
!= GOT_ERR_NOT_WORKTREE
)
7975 strdup(got_worktree_get_repo_path(worktree
));
7976 if (repo_path
== NULL
)
7977 error
= got_error_from_errno("strdup");
7981 repo_path
= strdup(cwd
);
7982 if (repo_path
== NULL
) {
7983 error
= got_error_from_errno("strdup");
7989 if (do_list
|| verify_tags
) {
7990 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
7993 error
= get_allowed_signers(&allowed_signers
, repo
, worktree
);
7996 error
= get_revoked_signers(&revoked_signers
, repo
, worktree
);
8000 /* Release work tree lock. */
8001 got_worktree_close(worktree
);
8006 * Remove "cpath" promise unless needed for signature tmpfile
8010 got_sigs_apply_unveil();
8013 if (pledge("stdio rpath wpath flock proc exec sendfd "
8014 "unveil", NULL
) == -1)
8018 error
= apply_unveil(got_repo_get_path(repo
), 1, NULL
);
8021 error
= list_tags(repo
, tag_name
, verify_tags
, allowed_signers
,
8022 revoked_signers
, verbosity
);
8024 error
= get_gitconfig_path(&gitconfig_path
);
8027 error
= got_repo_open(&repo
, repo_path
, gitconfig_path
,
8032 error
= get_author(&tagger
, repo
, worktree
);
8035 if (signer_id
== NULL
)
8036 signer_id
= get_signer_id(repo
, worktree
);
8038 if (tagmsg
== NULL
) {
8039 error
= get_editor(&editor
);
8042 if (unveil(editor
, "x") != 0) {
8043 error
= got_error_from_errno2("unveil", editor
);
8048 error
= got_sigs_apply_unveil();
8052 error
= apply_unveil(got_repo_get_path(repo
), 0, NULL
);
8056 if (commit_id_arg
== NULL
) {
8057 struct got_reference
*head_ref
;
8058 struct got_object_id
*commit_id
;
8059 error
= got_ref_open(&head_ref
, repo
,
8060 worktree
? got_worktree_get_head_ref_name(worktree
)
8064 error
= got_ref_resolve(&commit_id
, repo
, head_ref
);
8065 got_ref_close(head_ref
);
8068 error
= got_object_id_str(&commit_id_str
, commit_id
);
8073 error
= got_keyword_to_idstr(&keyword_idstr
,
8074 commit_id_arg
, repo
, worktree
);
8077 commit_id_str
= keyword_idstr
;
8081 /* Release work tree lock. */
8082 got_worktree_close(worktree
);
8086 error
= add_tag(repo
, tagger
, tag_name
,
8087 commit_id_str
? commit_id_str
: commit_id_arg
, tagmsg
,
8088 signer_id
, editor
, verbosity
);
8092 const struct got_error
*close_err
= got_repo_close(repo
);
8097 got_worktree_close(worktree
);
8099 const struct got_error
*pack_err
=
8100 got_repo_pack_fds_close(pack_fds
);
8107 free(gitconfig_path
);
8108 free(commit_id_str
);
8110 free(allowed_signers
);
8111 free(revoked_signers
);
8118 fprintf(stderr
, "usage: %s add [-IR] path ...\n", getprogname());
8122 static const struct got_error
*
8123 add_progress(void *arg
, unsigned char status
, const char *path
)
8125 while (path
[0] == '/')
8127 printf("%c %s\n", status
, path
);
8131 static const struct got_error
*
8132 cmd_add(int argc
, char *argv
[])
8134 const struct got_error
*error
= NULL
;
8135 struct got_repository
*repo
= NULL
;
8136 struct got_worktree
*worktree
= NULL
;
8138 struct got_pathlist_head paths
;
8139 struct got_pathlist_entry
*pe
;
8140 int ch
, can_recurse
= 0, no_ignores
= 0;
8141 int *pack_fds
= NULL
;
8146 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
8151 while ((ch
= getopt(argc
, argv
, "IR")) != -1) {
8171 cwd
= getcwd(NULL
, 0);
8173 error
= got_error_from_errno("getcwd");
8177 error
= got_repo_pack_fds_open(&pack_fds
);
8181 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_GOT_DIR
);
8183 if (error
->code
== GOT_ERR_NOT_WORKTREE
)
8184 error
= wrap_not_worktree_error(error
, "add", cwd
);
8188 error
= got_repo_open(&repo
, got_worktree_get_repo_path(worktree
),
8193 error
= apply_unveil(got_repo_get_path(repo
), 1,
8194 got_worktree_get_root_path(worktree
));
8198 error
= get_worktree_paths_from_argv(&paths
, argc
, argv
, worktree
);
8205 TAILQ_FOREACH(pe
, &paths
, entry
) {
8206 if (asprintf(&ondisk_path
, "%s/%s",
8207 got_worktree_get_root_path(worktree
),
8209 error
= got_error_from_errno("asprintf");
8212 if (lstat(ondisk_path
, &sb
) == -1) {
8213 if (errno
== ENOENT
) {
8217 error
= got_error_from_errno2("lstat",
8223 if (S_ISDIR(sb
.st_mode
)) {
8224 error
= got_error_msg(GOT_ERR_BAD_PATH
,
8225 "adding directories requires -R option");
8231 error
= got_worktree_schedule_add(worktree
, &paths
, add_progress
,
8232 NULL
, repo
, no_ignores
);
8235 const struct got_error
*close_err
= got_repo_close(repo
);
8240 got_worktree_close(worktree
);
8242 const struct got_error
*pack_err
=
8243 got_repo_pack_fds_close(pack_fds
);
8247 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_PATH
);
8255 fprintf(stderr
, "usage: %s remove [-fkR] [-s status-codes] path ...\n",
8260 static const struct got_error
*
8261 print_remove_status(void *arg
, unsigned char status
,
8262 unsigned char staged_status
, const char *path
)
8264 while (path
[0] == '/')
8266 if (status
== GOT_STATUS_NONEXISTENT
)
8268 if (status
== staged_status
&& (status
== GOT_STATUS_DELETE
))
8269 status
= GOT_STATUS_NO_CHANGE
;
8270 printf("%c%c %s\n", status
, staged_status
, path
);
8274 static const struct got_error
*
8275 cmd_remove(int argc
, char *argv
[])
8277 const struct got_error
*error
= NULL
;
8278 struct got_worktree
*worktree
= NULL
;
8279 struct got_repository
*repo
= NULL
;
8280 const char *status_codes
= NULL
;
8282 struct got_pathlist_head paths
;
8283 struct got_pathlist_entry
*pe
;
8284 int ch
, delete_local_mods
= 0, can_recurse
= 0, keep_on_disk
= 0, i
;
8285 int ignore_missing_paths
= 0;
8286 int *pack_fds
= NULL
;
8291 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
8296 while ((ch
= getopt(argc
, argv
, "fkRs:")) != -1) {
8299 delete_local_mods
= 1;
8300 ignore_missing_paths
= 1;
8309 for (i
= 0; optarg
[i
] != '\0'; i
++) {
8310 switch (optarg
[i
]) {
8311 case GOT_STATUS_MODIFY
:
8312 delete_local_mods
= 1;
8314 case GOT_STATUS_MISSING
:
8315 ignore_missing_paths
= 1;
8318 errx(1, "invalid status code '%c'",
8322 status_codes
= optarg
;
8336 cwd
= getcwd(NULL
, 0);
8338 error
= got_error_from_errno("getcwd");
8342 error
= got_repo_pack_fds_open(&pack_fds
);
8346 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_GOT_DIR
);
8348 if (error
->code
== GOT_ERR_NOT_WORKTREE
)
8349 error
= wrap_not_worktree_error(error
, "remove", cwd
);
8353 error
= got_repo_open(&repo
, got_worktree_get_repo_path(worktree
),
8358 error
= apply_unveil(got_repo_get_path(repo
), 1,
8359 got_worktree_get_root_path(worktree
));
8363 error
= get_worktree_paths_from_argv(&paths
, argc
, argv
, worktree
);
8370 TAILQ_FOREACH(pe
, &paths
, entry
) {
8371 if (asprintf(&ondisk_path
, "%s/%s",
8372 got_worktree_get_root_path(worktree
),
8374 error
= got_error_from_errno("asprintf");
8377 if (lstat(ondisk_path
, &sb
) == -1) {
8378 if (errno
== ENOENT
) {
8382 error
= got_error_from_errno2("lstat",
8388 if (S_ISDIR(sb
.st_mode
)) {
8389 error
= got_error_msg(GOT_ERR_BAD_PATH
,
8390 "removing directories requires -R option");
8396 error
= got_worktree_schedule_delete(worktree
, &paths
,
8397 delete_local_mods
, status_codes
, print_remove_status
, NULL
,
8398 repo
, keep_on_disk
, ignore_missing_paths
);
8401 const struct got_error
*close_err
= got_repo_close(repo
);
8406 got_worktree_close(worktree
);
8408 const struct got_error
*pack_err
=
8409 got_repo_pack_fds_close(pack_fds
);
8413 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_PATH
);
8421 fprintf(stderr
, "usage: %s patch [-nR] [-c commit] [-p strip-count] "
8422 "[patchfile]\n", getprogname());
8426 static const struct got_error
*
8427 patch_from_stdin(int *patchfd
)
8429 const struct got_error
*err
= NULL
;
8432 sig_t sighup
, sigint
, sigquit
;
8434 *patchfd
= got_opentempfd();
8436 return got_error_from_errno("got_opentempfd");
8438 sighup
= signal(SIGHUP
, SIG_DFL
);
8439 sigint
= signal(SIGINT
, SIG_DFL
);
8440 sigquit
= signal(SIGQUIT
, SIG_DFL
);
8443 r
= read(0, buf
, sizeof(buf
));
8445 err
= got_error_from_errno("read");
8450 if (write(*patchfd
, buf
, r
) == -1) {
8451 err
= got_error_from_errno("write");
8456 signal(SIGHUP
, sighup
);
8457 signal(SIGINT
, sigint
);
8458 signal(SIGQUIT
, sigquit
);
8460 if (err
== NULL
&& lseek(*patchfd
, 0, SEEK_SET
) == -1)
8461 err
= got_error_from_errno("lseek");
8471 struct got_patch_progress_arg
{
8477 static const struct got_error
*
8478 patch_progress(void *arg
, const char *old
, const char *new,
8479 unsigned char status
, const struct got_error
*error
, int old_from
,
8480 int old_lines
, int new_from
, int new_lines
, int offset
,
8481 int ws_mangled
, const struct got_error
*hunk_err
)
8483 const char *path
= new == NULL
? old
: new;
8484 struct got_patch_progress_arg
*a
= arg
;
8486 while (*path
== '/')
8489 if (status
!= GOT_STATUS_NO_CHANGE
&&
8490 status
!= 0 /* per-hunk progress */) {
8491 printf("%c %s\n", status
, path
);
8492 a
->did_something
= 1;
8495 if (hunk_err
== NULL
) {
8496 if (status
== GOT_STATUS_CANNOT_UPDATE
)
8498 else if (status
== GOT_STATUS_CONFLICT
)
8503 fprintf(stderr
, "%s: %s\n", getprogname(), error
->msg
);
8505 if (offset
!= 0 || hunk_err
!= NULL
|| ws_mangled
) {
8506 printf("@@ -%d,%d +%d,%d @@ ", old_from
,
8507 old_lines
, new_from
, new_lines
);
8508 if (hunk_err
!= NULL
)
8509 printf("%s\n", hunk_err
->msg
);
8510 else if (offset
!= 0)
8511 printf("applied with offset %d\n", offset
);
8513 printf("hunk contains mangled whitespace\n");
8520 print_patch_progress_stats(struct got_patch_progress_arg
*ppa
)
8522 if (!ppa
->did_something
)
8525 if (ppa
->conflicts
> 0)
8526 printf("Files with merge conflicts: %d\n", ppa
->conflicts
);
8528 if (ppa
->rejects
> 0) {
8529 printf("Files where patch failed to apply: %d\n",
8534 static const struct got_error
*
8535 cmd_patch(int argc
, char *argv
[])
8537 const struct got_error
*error
= NULL
, *close_error
= NULL
;
8538 struct got_worktree
*worktree
= NULL
;
8539 struct got_repository
*repo
= NULL
;
8540 struct got_reflist_head refs
;
8541 struct got_object_id
*commit_id
= NULL
;
8542 const char *commit_id_str
= NULL
;
8545 char *cwd
= NULL
, *keyword_idstr
= NULL
;
8546 int ch
, nop
= 0, strip
= -1, reverse
= 0;
8548 int *pack_fds
= NULL
;
8549 struct got_patch_progress_arg ppa
;
8554 if (pledge("stdio rpath wpath cpath fattr proc exec sendfd flock "
8555 "unveil", NULL
) == -1)
8559 while ((ch
= getopt(argc
, argv
, "c:np:R")) != -1) {
8562 commit_id_str
= optarg
;
8568 strip
= strtonum(optarg
, 0, INT_MAX
, &errstr
);
8570 errx(1, "pathname strip count is %s: %s",
8586 error
= patch_from_stdin(&patchfd
);
8589 } else if (argc
== 1) {
8590 patchfd
= open(argv
[0], O_RDONLY
);
8592 return got_error_from_errno2("open", argv
[0]);
8593 if (fstat(patchfd
, &sb
) == -1) {
8594 error
= got_error_from_errno2("fstat", argv
[0]);
8597 if (!S_ISREG(sb
.st_mode
)) {
8598 error
= got_error_path(argv
[0], GOT_ERR_BAD_FILETYPE
);
8604 if ((cwd
= getcwd(NULL
, 0)) == NULL
) {
8605 error
= got_error_from_errno("getcwd");
8609 error
= got_repo_pack_fds_open(&pack_fds
);
8613 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_GOT_DIR
);
8617 const char *repo_path
= got_worktree_get_repo_path(worktree
);
8618 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
8622 error
= apply_unveil(got_repo_get_path(repo
), 0,
8623 got_worktree_get_root_path(worktree
));
8627 error
= got_ref_list(&refs
, repo
, NULL
, got_ref_cmp_by_name
, NULL
);
8631 if (commit_id_str
!= NULL
) {
8632 error
= got_keyword_to_idstr(&keyword_idstr
, commit_id_str
,
8637 error
= got_repo_match_object_id(&commit_id
, NULL
,
8638 keyword_idstr
!= NULL
? keyword_idstr
: commit_id_str
,
8639 GOT_OBJ_TYPE_COMMIT
, &refs
, repo
);
8644 memset(&ppa
, 0, sizeof(ppa
));
8645 error
= got_patch(patchfd
, worktree
, repo
, nop
, strip
, reverse
,
8646 commit_id
, patch_progress
, &ppa
, check_cancelled
, NULL
);
8647 print_patch_progress_stats(&ppa
);
8649 got_ref_list_free(&refs
);
8650 free(keyword_idstr
);
8653 close_error
= got_repo_close(repo
);
8655 error
= close_error
;
8657 if (worktree
!= NULL
) {
8658 close_error
= got_worktree_close(worktree
);
8660 error
= close_error
;
8663 const struct got_error
*pack_err
=
8664 got_repo_pack_fds_close(pack_fds
);
8675 fprintf(stderr
, "usage: %s revert [-pR] [-F response-script] path ...\n",
8680 static const struct got_error
*
8681 revert_progress(void *arg
, unsigned char status
, const char *path
)
8683 if (status
== GOT_STATUS_UNVERSIONED
)
8686 while (path
[0] == '/')
8688 printf("%c %s\n", status
, path
);
8692 struct choose_patch_arg
{
8693 FILE *patch_script_file
;
8697 static const struct got_error
*
8698 show_change(unsigned char status
, const char *path
, FILE *patch_file
, int n
,
8699 int nchanges
, const char *action
)
8701 const struct got_error
*err
;
8703 size_t linesize
= 0;
8707 case GOT_STATUS_ADD
:
8708 printf("A %s\n%s this addition? [y/n] ", path
, action
);
8710 case GOT_STATUS_DELETE
:
8711 printf("D %s\n%s this deletion? [y/n] ", path
, action
);
8713 case GOT_STATUS_MODIFY
:
8714 if (fseek(patch_file
, 0L, SEEK_SET
) == -1)
8715 return got_error_from_errno("fseek");
8716 printf(GOT_COMMIT_SEP_STR
);
8717 while ((linelen
= getline(&line
, &linesize
, patch_file
)) != -1)
8719 if (linelen
== -1 && ferror(patch_file
)) {
8720 err
= got_error_from_errno("getline");
8725 printf(GOT_COMMIT_SEP_STR
);
8726 printf("M %s (change %d of %d)\n%s this change? [y/n/q] ",
8727 path
, n
, nchanges
, action
);
8730 return got_error_path(path
, GOT_ERR_FILE_STATUS
);
8737 #define CHOOSE_PATCH_VALID_RESPONSES "ynq"
8739 static const struct got_error
*
8740 choose_patch(int *choice
, void *arg
, unsigned char status
, const char *path
,
8741 FILE *patch_file
, int n
, int nchanges
)
8743 const struct got_error
*err
= NULL
;
8744 char *nl
, *line
= NULL
;
8746 size_t linesize
= 0;
8748 int interactive
= 1;
8749 struct choose_patch_arg
*a
= arg
;
8751 *choice
= GOT_PATCH_CHOICE_NONE
;
8753 if (a
->patch_script_file
) {
8755 fp
= a
->patch_script_file
;
8759 err
= show_change(status
, path
, patch_file
, n
, nchanges
,
8764 linelen
= getline(&line
, &linesize
, fp
);
8765 if (linelen
== -1) {
8768 return got_error_from_errno("getline");
8770 return got_error(GOT_ERR_EOF
);
8774 nl
= strchr(line
, '\n');
8777 if (strlen(line
) != 1 ||
8778 !strchr(CHOOSE_PATCH_VALID_RESPONSES
, line
[0])) {
8779 printf("invalid response '%s'\n", line
);
8784 /* ADD and DELETE do not accept 'q' response currently. */
8785 if (status
!= GOT_STATUS_MODIFY
&& line
[0] == 'q') {
8786 printf("invalid response '%s'\n", line
);
8795 *choice
= GOT_PATCH_CHOICE_YES
;
8798 *choice
= GOT_PATCH_CHOICE_NO
;
8801 if (status
== GOT_STATUS_MODIFY
)
8802 *choice
= GOT_PATCH_CHOICE_QUIT
;
8805 printf("invalid response '%s'\n", line
);
8811 printf("%c\n", line
[0]);
8817 struct wt_commitable_path_arg
{
8818 struct got_pathlist_head
*commit_paths
;
8823 * Shortcut work tree status callback to determine if the set of paths scanned
8824 * has at least one versioned path that is being modified and, if not NULL, is
8825 * in the arg->commit_paths list. Set arg and return GOT_ERR_FILE_MODIFIED as
8826 * soon as a path is passed with a status that satisfies this criteria.
8828 static const struct got_error
*
8829 worktree_has_commitable_path(void *arg
, unsigned char status
,
8830 unsigned char staged_status
, const char *path
,
8831 struct got_object_id
*blob_id
, struct got_object_id
*staged_blob_id
,
8832 struct got_object_id
*commit_id
, int dirfd
, const char *de_name
)
8834 struct wt_commitable_path_arg
*a
= arg
;
8836 if (status
== staged_status
&& (status
== GOT_STATUS_DELETE
))
8837 status
= GOT_STATUS_NO_CHANGE
;
8839 if (!(status
== GOT_STATUS_NO_CHANGE
||
8840 status
== GOT_STATUS_UNVERSIONED
) ||
8841 staged_status
!= GOT_STATUS_NO_CHANGE
) {
8842 if (a
->commit_paths
!= NULL
) {
8843 struct got_pathlist_entry
*pe
;
8845 TAILQ_FOREACH(pe
, a
->commit_paths
, entry
) {
8846 if (strncmp(path
, pe
->path
,
8847 pe
->path_len
) == 0) {
8848 *a
->has_changes
= 1;
8853 *a
->has_changes
= 1;
8855 if (*a
->has_changes
)
8856 return got_error(GOT_ERR_FILE_MODIFIED
);
8863 * Check that the changeset of the commit identified by id is
8864 * comprised of at least one modified path that is being committed.
8866 static const struct got_error
*
8867 commit_path_changed_in_worktree(struct wt_commitable_path_arg
*wcpa
,
8868 struct got_object_id
*id
, struct got_worktree
*worktree
,
8869 struct got_repository
*repo
)
8871 const struct got_error
*err
;
8872 struct got_pathlist_head paths
;
8873 struct got_commit_object
*commit
= NULL
, *pcommit
= NULL
;
8874 struct got_tree_object
*tree
= NULL
, *ptree
= NULL
;
8875 struct got_object_qid
*pid
;
8879 err
= got_object_open_as_commit(&commit
, repo
, id
);
8883 err
= got_object_open_as_tree(&tree
, repo
,
8884 got_object_commit_get_tree_id(commit
));
8888 pid
= STAILQ_FIRST(got_object_commit_get_parent_ids(commit
));
8890 err
= got_object_open_as_commit(&pcommit
, repo
, &pid
->id
);
8894 err
= got_object_open_as_tree(&ptree
, repo
,
8895 got_object_commit_get_tree_id(pcommit
));
8900 err
= got_diff_tree(ptree
, tree
, NULL
, NULL
, -1, -1, "", "", repo
,
8901 got_diff_tree_collect_changed_paths
, &paths
, 0);
8905 err
= got_worktree_status(worktree
, &paths
, repo
, 0,
8906 worktree_has_commitable_path
, wcpa
, check_cancelled
, NULL
);
8907 if (err
&& err
->code
== GOT_ERR_FILE_MODIFIED
) {
8909 * At least one changed path in the referenced commit is
8910 * modified in the work tree, that's all we need to know!
8916 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_ALL
);
8918 got_object_commit_close(commit
);
8920 got_object_commit_close(pcommit
);
8922 got_object_tree_close(tree
);
8924 got_object_tree_close(ptree
);
8929 * Remove any "logmsg" reference comprised entirely of paths that have
8930 * been reverted in this work tree. If any path in the logmsg ref changeset
8931 * remains in a changed state in the worktree, do not remove the reference.
8933 static const struct got_error
*
8934 rm_logmsg_ref(struct got_worktree
*worktree
, struct got_repository
*repo
)
8936 const struct got_error
*err
;
8937 struct got_reflist_head refs
;
8938 struct got_reflist_entry
*re
;
8939 struct got_commit_object
*commit
= NULL
;
8940 struct got_object_id
*commit_id
= NULL
;
8941 struct wt_commitable_path_arg wcpa
;
8942 char *uuidstr
= NULL
;
8946 err
= got_worktree_get_uuid(&uuidstr
, worktree
);
8950 err
= got_ref_list(&refs
, repo
, "refs/got/worktree",
8951 got_ref_cmp_by_name
, repo
);
8955 TAILQ_FOREACH(re
, &refs
, entry
) {
8956 const char *refname
;
8957 int has_changes
= 0;
8959 refname
= got_ref_get_name(re
->ref
);
8961 if (!strncmp(refname
, GOT_WORKTREE_CHERRYPICK_REF_PREFIX
,
8962 GOT_WORKTREE_CHERRYPICK_REF_PREFIX_LEN
))
8963 refname
+= GOT_WORKTREE_CHERRYPICK_REF_PREFIX_LEN
+ 1;
8964 else if (!strncmp(refname
, GOT_WORKTREE_BACKOUT_REF_PREFIX
,
8965 GOT_WORKTREE_BACKOUT_REF_PREFIX_LEN
))
8966 refname
+= GOT_WORKTREE_BACKOUT_REF_PREFIX_LEN
+ 1;
8970 if (strncmp(refname
, uuidstr
, GOT_WORKTREE_UUID_STRLEN
) == 0)
8971 refname
+= GOT_WORKTREE_UUID_STRLEN
+ 1; /* skip '-' */
8975 err
= got_repo_match_object_id(&commit_id
, NULL
, refname
,
8976 GOT_OBJ_TYPE_COMMIT
, NULL
, repo
);
8980 err
= got_object_open_as_commit(&commit
, repo
, commit_id
);
8984 wcpa
.commit_paths
= NULL
;
8985 wcpa
.has_changes
= &has_changes
;
8987 err
= commit_path_changed_in_worktree(&wcpa
, commit_id
,
8993 err
= got_ref_delete(re
->ref
, repo
);
8998 got_object_commit_close(commit
);
9007 got_ref_list_free(&refs
);
9009 got_object_commit_close(commit
);
9013 static const struct got_error
*
9014 cmd_revert(int argc
, char *argv
[])
9016 const struct got_error
*error
= NULL
;
9017 struct got_worktree
*worktree
= NULL
;
9018 struct got_repository
*repo
= NULL
;
9019 char *cwd
= NULL
, *path
= NULL
;
9020 struct got_pathlist_head paths
;
9021 struct got_pathlist_entry
*pe
;
9022 int ch
, can_recurse
= 0, pflag
= 0;
9023 FILE *patch_script_file
= NULL
;
9024 const char *patch_script_path
= NULL
;
9025 struct choose_patch_arg cpa
;
9026 int *pack_fds
= NULL
;
9031 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
9032 "unveil", NULL
) == -1)
9036 while ((ch
= getopt(argc
, argv
, "F:pR")) != -1) {
9039 patch_script_path
= optarg
;
9058 if (patch_script_path
&& !pflag
)
9059 errx(1, "-F option can only be used together with -p option");
9061 cwd
= getcwd(NULL
, 0);
9063 error
= got_error_from_errno("getcwd");
9067 error
= got_repo_pack_fds_open(&pack_fds
);
9071 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_GOT_DIR
);
9073 if (error
->code
== GOT_ERR_NOT_WORKTREE
)
9074 error
= wrap_not_worktree_error(error
, "revert", cwd
);
9078 error
= got_repo_open(&repo
, got_worktree_get_repo_path(worktree
),
9083 if (patch_script_path
) {
9084 patch_script_file
= fopen(patch_script_path
, "re");
9085 if (patch_script_file
== NULL
) {
9086 error
= got_error_from_errno2("fopen",
9093 * XXX "c" perm needed on repo dir to delete merge references.
9095 error
= apply_unveil(got_repo_get_path(repo
), 0,
9096 got_worktree_get_root_path(worktree
));
9100 error
= get_worktree_paths_from_argv(&paths
, argc
, argv
, worktree
);
9107 TAILQ_FOREACH(pe
, &paths
, entry
) {
9108 if (asprintf(&ondisk_path
, "%s/%s",
9109 got_worktree_get_root_path(worktree
),
9111 error
= got_error_from_errno("asprintf");
9114 if (lstat(ondisk_path
, &sb
) == -1) {
9115 if (errno
== ENOENT
) {
9119 error
= got_error_from_errno2("lstat",
9125 if (S_ISDIR(sb
.st_mode
)) {
9126 error
= got_error_msg(GOT_ERR_BAD_PATH
,
9127 "reverting directories requires -R option");
9133 cpa
.patch_script_file
= patch_script_file
;
9134 cpa
.action
= "revert";
9135 error
= got_worktree_revert(worktree
, &paths
, revert_progress
, NULL
,
9136 pflag
? choose_patch
: NULL
, &cpa
, repo
);
9138 error
= rm_logmsg_ref(worktree
, repo
);
9140 if (patch_script_file
&& fclose(patch_script_file
) == EOF
&&
9142 error
= got_error_from_errno2("fclose", patch_script_path
);
9144 const struct got_error
*close_err
= got_repo_close(repo
);
9149 got_worktree_close(worktree
);
9151 const struct got_error
*pack_err
=
9152 got_repo_pack_fds_close(pack_fds
);
9156 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_PATH
);
9165 fprintf(stderr
, "usage: %s commit [-CNnS] [-A author] [-F path] "
9166 "[-m message] [path ...]\n", getprogname());
9170 struct collect_commit_logmsg_arg
{
9171 const char *cmdline_log
;
9172 const char *prepared_log
;
9173 const char *merged_log
;
9174 int non_interactive
;
9176 const char *worktree_path
;
9177 const char *branch_name
;
9178 const char *repo_path
;
9183 static const struct got_error
*
9184 read_prepared_logmsg(char **logmsg
, const char *path
)
9186 const struct got_error
*err
= NULL
;
9192 memset(&sb
, 0, sizeof(sb
));
9194 f
= fopen(path
, "re");
9196 return got_error_from_errno2("fopen", path
);
9198 if (fstat(fileno(f
), &sb
) == -1) {
9199 err
= got_error_from_errno2("fstat", path
);
9202 if (sb
.st_size
== 0) {
9203 err
= got_error(GOT_ERR_COMMIT_MSG_EMPTY
);
9207 *logmsg
= malloc(sb
.st_size
+ 1);
9208 if (*logmsg
== NULL
) {
9209 err
= got_error_from_errno("malloc");
9213 r
= fread(*logmsg
, 1, sb
.st_size
, f
);
9214 if (r
!= sb
.st_size
) {
9216 err
= got_error_from_errno2("fread", path
);
9218 err
= got_error(GOT_ERR_IO
);
9221 (*logmsg
)[sb
.st_size
] = '\0';
9223 if (fclose(f
) == EOF
&& err
== NULL
)
9224 err
= got_error_from_errno2("fclose", path
);
9232 static const struct got_error
*
9233 collect_commit_logmsg(struct got_pathlist_head
*commitable_paths
,
9234 const char *diff_path
, char **logmsg
, void *arg
)
9236 char *initial_content
= NULL
;
9237 struct got_pathlist_entry
*pe
;
9238 const struct got_error
*err
= NULL
;
9239 char *template = NULL
;
9240 char *prepared_msg
= NULL
, *merged_msg
= NULL
;
9241 struct collect_commit_logmsg_arg
*a
= arg
;
9242 int initial_content_len
;
9246 /* if a message was specified on the command line, just use it */
9247 if (a
->cmdline_log
!= NULL
&& *a
->cmdline_log
!= '\0') {
9248 len
= strlen(a
->cmdline_log
) + 1;
9249 *logmsg
= malloc(len
+ 1);
9250 if (*logmsg
== NULL
)
9251 return got_error_from_errno("malloc");
9252 strlcpy(*logmsg
, a
->cmdline_log
, len
);
9254 } else if (a
->prepared_log
!= NULL
&& a
->non_interactive
)
9255 return read_prepared_logmsg(logmsg
, a
->prepared_log
);
9257 if (asprintf(&template, "%s/logmsg", a
->worktree_path
) == -1)
9258 return got_error_from_errno("asprintf");
9260 err
= got_opentemp_named_fd(&a
->logmsg_path
, &fd
, template, "");
9264 if (a
->prepared_log
) {
9265 err
= read_prepared_logmsg(&prepared_msg
, a
->prepared_log
);
9268 } else if (a
->merged_log
) {
9269 err
= read_prepared_logmsg(&merged_msg
, a
->merged_log
);
9274 initial_content_len
= asprintf(&initial_content
,
9275 "%s%s\n# changes to be committed on branch %s:\n",
9276 prepared_msg
? prepared_msg
: "",
9277 merged_msg
? merged_msg
: "", a
->branch_name
);
9278 if (initial_content_len
== -1) {
9279 err
= got_error_from_errno("asprintf");
9283 if (write(fd
, initial_content
, initial_content_len
) == -1) {
9284 err
= got_error_from_errno2("write", a
->logmsg_path
);
9288 TAILQ_FOREACH(pe
, commitable_paths
, entry
) {
9289 struct got_commitable
*ct
= pe
->data
;
9290 dprintf(fd
, "# %c %s\n",
9291 got_commitable_get_status(ct
),
9292 got_commitable_get_path(ct
));
9296 dprintf(fd
, "# detailed changes can be viewed in %s\n",
9300 if (close(fd
) == -1) {
9301 err
= got_error_from_errno2("close", a
->logmsg_path
);
9306 err
= edit_logmsg(logmsg
, a
->editor
, a
->logmsg_path
, initial_content
,
9307 initial_content_len
, a
->prepared_log
? 0 : 1);
9309 free(initial_content
);
9314 if (fd
!= -1 && close(fd
) == -1 && err
== NULL
)
9315 err
= got_error_from_errno2("close", a
->logmsg_path
);
9323 static const struct got_error
*
9324 cat_logmsg(FILE *f
, struct got_commit_object
*commit
, const char *idstr
,
9325 const char *type
, int has_content
)
9327 const struct got_error
*err
= NULL
;
9328 char *logmsg
= NULL
;
9330 err
= got_object_commit_get_logmsg(&logmsg
, commit
);
9334 if (fprintf(f
, "%s# log message of %s commit %s:%s",
9335 has_content
? "\n" : "", type
, idstr
, logmsg
) < 0)
9336 err
= got_ferror(f
, GOT_ERR_IO
);
9343 * Lookup "logmsg" references of backed-out and cherrypicked commits
9344 * belonging to the current work tree. If found, and the worktree has
9345 * at least one modified file that was changed in the referenced commit,
9346 * add its log message to a new temporary file at *logmsg_path.
9347 * Add all refs found to matched_refs to be scheduled for removal on
9348 * successful commit.
9350 static const struct got_error
*
9351 lookup_logmsg_ref(char **logmsg_path
, struct got_pathlist_head
*paths
,
9352 struct got_reflist_head
*matched_refs
, struct got_worktree
*worktree
,
9353 struct got_repository
*repo
)
9355 const struct got_error
*err
;
9356 struct got_commit_object
*commit
= NULL
;
9357 struct got_object_id
*id
= NULL
;
9358 struct got_reflist_head refs
;
9359 struct got_reflist_entry
*re
, *re_match
;
9361 char *uuidstr
= NULL
;
9362 int added_logmsg
= 0;
9366 *logmsg_path
= NULL
;
9368 err
= got_worktree_get_uuid(&uuidstr
, worktree
);
9372 err
= got_ref_list(&refs
, repo
, "refs/got/worktree",
9373 got_ref_cmp_by_name
, repo
);
9377 TAILQ_FOREACH(re
, &refs
, entry
) {
9378 const char *refname
, *type
;
9379 struct wt_commitable_path_arg wcpa
;
9382 refname
= got_ref_get_name(re
->ref
);
9384 if (strncmp(refname
, GOT_WORKTREE_CHERRYPICK_REF_PREFIX
,
9385 GOT_WORKTREE_CHERRYPICK_REF_PREFIX_LEN
) == 0) {
9386 refname
+= GOT_WORKTREE_CHERRYPICK_REF_PREFIX_LEN
+ 1;
9387 type
= "cherrypicked";
9388 } else if (strncmp(refname
, GOT_WORKTREE_BACKOUT_REF_PREFIX
,
9389 GOT_WORKTREE_BACKOUT_REF_PREFIX_LEN
) == 0) {
9390 refname
+= GOT_WORKTREE_BACKOUT_REF_PREFIX_LEN
+ 1;
9391 type
= "backed-out";
9395 if (strncmp(refname
, uuidstr
, GOT_WORKTREE_UUID_STRLEN
) == 0)
9396 refname
+= GOT_WORKTREE_UUID_STRLEN
+ 1; /* skip '-' */
9400 err
= got_repo_match_object_id(&id
, NULL
, refname
,
9401 GOT_OBJ_TYPE_COMMIT
, NULL
, repo
);
9405 err
= got_object_open_as_commit(&commit
, repo
, id
);
9409 wcpa
.commit_paths
= paths
;
9410 wcpa
.has_changes
= &add_logmsg
;
9412 err
= commit_path_changed_in_worktree(&wcpa
, id
,
9419 err
= got_opentemp_named(logmsg_path
, &f
,
9420 "got-commit-logmsg", "");
9424 err
= cat_logmsg(f
, commit
, refname
, type
,
9431 err
= got_reflist_entry_dup(&re_match
, re
);
9434 TAILQ_INSERT_HEAD(matched_refs
, re_match
, entry
);
9437 got_object_commit_close(commit
);
9446 got_ref_list_free(&refs
);
9448 got_object_commit_close(commit
);
9449 if (f
&& fclose(f
) == EOF
&& err
== NULL
)
9450 err
= got_error_from_errno("fclose");
9451 if (!added_logmsg
) {
9452 if (*logmsg_path
&& unlink(*logmsg_path
) != 0 && err
== NULL
)
9453 err
= got_error_from_errno2("unlink", *logmsg_path
);
9454 *logmsg_path
= NULL
;
9459 static const struct got_error
*
9460 cmd_commit(int argc
, char *argv
[])
9462 const struct got_error
*error
= NULL
;
9463 struct got_worktree
*worktree
= NULL
;
9464 struct got_repository
*repo
= NULL
;
9465 char *cwd
= NULL
, *id_str
= NULL
;
9466 struct got_object_id
*id
= NULL
;
9467 const char *logmsg
= NULL
;
9468 char *prepared_logmsg
= NULL
, *merged_logmsg
= NULL
;
9469 struct collect_commit_logmsg_arg cl_arg
;
9470 const char *author
= NULL
;
9471 char *gitconfig_path
= NULL
, *editor
= NULL
, *committer
= NULL
;
9472 int ch
, rebase_in_progress
, histedit_in_progress
, preserve_logmsg
= 0;
9473 int allow_bad_symlinks
= 0, non_interactive
= 0, merge_in_progress
= 0;
9474 int show_diff
= 1, commit_conflicts
= 0;
9475 struct got_pathlist_head paths
;
9476 struct got_reflist_head refs
;
9477 struct got_reflist_entry
*re
;
9478 int *pack_fds
= NULL
;
9482 cl_arg
.logmsg_path
= NULL
;
9485 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
9486 "unveil", NULL
) == -1)
9490 while ((ch
= getopt(argc
, argv
, "A:CF:m:NnS")) != -1) {
9494 error
= valid_author(author
);
9499 commit_conflicts
= 1;
9503 option_conflict('F', 'm');
9504 prepared_logmsg
= realpath(optarg
, NULL
);
9505 if (prepared_logmsg
== NULL
)
9506 return got_error_from_errno2("realpath",
9510 if (prepared_logmsg
)
9511 option_conflict('m', 'F');
9515 non_interactive
= 1;
9521 allow_bad_symlinks
= 1;
9532 cwd
= getcwd(NULL
, 0);
9534 error
= got_error_from_errno("getcwd");
9538 error
= got_repo_pack_fds_open(&pack_fds
);
9542 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_GOT_DIR
);
9544 if (error
->code
== GOT_ERR_NOT_WORKTREE
)
9545 error
= wrap_not_worktree_error(error
, "commit", cwd
);
9549 error
= got_worktree_rebase_in_progress(&rebase_in_progress
, worktree
);
9552 if (rebase_in_progress
) {
9553 error
= got_error(GOT_ERR_REBASING
);
9557 error
= got_worktree_histedit_in_progress(&histedit_in_progress
,
9562 error
= get_gitconfig_path(&gitconfig_path
);
9565 error
= got_repo_open(&repo
, got_worktree_get_repo_path(worktree
),
9566 gitconfig_path
, pack_fds
);
9570 error
= got_worktree_merge_in_progress(&merge_in_progress
, worktree
, repo
);
9573 if (merge_in_progress
) {
9574 error
= got_error(GOT_ERR_MERGE_BUSY
);
9578 error
= get_author(&committer
, repo
, worktree
);
9585 if (logmsg
== NULL
|| strlen(logmsg
) == 0) {
9586 error
= get_editor(&editor
);
9589 if (unveil(editor
, "x") != 0) {
9590 error
= got_error_from_errno2("unveil", editor
);
9594 if (prepared_logmsg
) {
9595 if (unveil(prepared_logmsg
, "r") != 0) {
9596 error
= got_error_from_errno2("unveil",
9602 error
= apply_unveil(got_repo_get_path(repo
), 0,
9603 got_worktree_get_root_path(worktree
));
9607 error
= get_worktree_paths_from_argv(&paths
, argc
, argv
, worktree
);
9611 if (prepared_logmsg
== NULL
) {
9612 error
= lookup_logmsg_ref(&merged_logmsg
,
9613 argc
> 0 ? &paths
: NULL
, &refs
, worktree
, repo
);
9618 cl_arg
.editor
= editor
;
9619 cl_arg
.cmdline_log
= logmsg
;
9620 cl_arg
.prepared_log
= prepared_logmsg
;
9621 cl_arg
.merged_log
= merged_logmsg
;
9622 cl_arg
.non_interactive
= non_interactive
;
9623 cl_arg
.worktree_path
= got_worktree_get_root_path(worktree
);
9624 cl_arg
.branch_name
= got_worktree_get_head_ref_name(worktree
);
9625 if (!histedit_in_progress
) {
9626 if (strncmp(cl_arg
.branch_name
, "refs/heads/", 11) != 0) {
9627 error
= got_error(GOT_ERR_COMMIT_BRANCH
);
9630 cl_arg
.branch_name
+= 11;
9632 cl_arg
.repo_path
= got_repo_get_path(repo
);
9633 error
= got_worktree_commit(&id
, worktree
, &paths
, author
, committer
,
9634 allow_bad_symlinks
, show_diff
, commit_conflicts
,
9635 collect_commit_logmsg
, &cl_arg
, print_status
, NULL
, repo
);
9637 if (error
->code
!= GOT_ERR_COMMIT_MSG_EMPTY
&&
9638 cl_arg
.logmsg_path
!= NULL
)
9639 preserve_logmsg
= 1;
9643 error
= got_object_id_str(&id_str
, id
);
9646 printf("Created commit %s\n", id_str
);
9648 TAILQ_FOREACH(re
, &refs
, entry
) {
9649 error
= got_ref_delete(re
->ref
, repo
);
9655 if (preserve_logmsg
) {
9656 fprintf(stderr
, "%s: log message preserved in %s\n",
9657 getprogname(), cl_arg
.logmsg_path
);
9658 } else if (cl_arg
.logmsg_path
&& unlink(cl_arg
.logmsg_path
) == -1 &&
9660 error
= got_error_from_errno2("unlink", cl_arg
.logmsg_path
);
9661 free(cl_arg
.logmsg_path
);
9662 if (merged_logmsg
&& unlink(merged_logmsg
) == -1 && error
== NULL
)
9663 error
= got_error_from_errno2("unlink", merged_logmsg
);
9664 free(merged_logmsg
);
9666 const struct got_error
*close_err
= got_repo_close(repo
);
9671 got_worktree_close(worktree
);
9673 const struct got_error
*pack_err
=
9674 got_repo_pack_fds_close(pack_fds
);
9678 got_ref_list_free(&refs
);
9679 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_PATH
);
9682 free(gitconfig_path
);
9685 free(prepared_logmsg
);
9692 fprintf(stderr
, "usage: %s send [-afqTv] [-b branch] [-d branch] "
9693 "[-r repository-path] [-t tag] [remote-repository]\n",
9699 print_load_info(int print_colored
, int print_found
, int print_trees
,
9700 int ncolored
, int nfound
, int ntrees
)
9702 if (print_colored
) {
9703 printf("%d commit%s colored", ncolored
,
9704 ncolored
== 1 ? "" : "s");
9707 printf("%s%d object%s found",
9708 ncolored
> 0 ? "; " : "",
9709 nfound
, nfound
== 1 ? "" : "s");
9712 printf("; %d tree%s scanned", ntrees
,
9713 ntrees
== 1 ? "" : "s");
9717 struct got_send_progress_arg
{
9718 char last_scaled_packsize
[FMT_SCALED_STRSIZE
];
9725 int last_nobj_total
;
9729 int printed_something
;
9731 struct got_pathlist_head
*delete_branches
;
9734 static const struct got_error
*
9735 send_progress(void *arg
, int ncolored
, int nfound
, int ntrees
,
9736 off_t packfile_size
, int ncommits
, int nobj_total
, int nobj_deltify
,
9737 int nobj_written
, off_t bytes_sent
, const char *refname
,
9738 const char *errmsg
, int success
)
9740 struct got_send_progress_arg
*a
= arg
;
9741 char scaled_packsize
[FMT_SCALED_STRSIZE
];
9742 char scaled_sent
[FMT_SCALED_STRSIZE
];
9743 int p_deltify
= 0, p_written
= 0, p_sent
= 0;
9744 int print_colored
= 0, print_found
= 0, print_trees
= 0;
9745 int print_searching
= 0, print_total
= 0;
9746 int print_deltify
= 0, print_written
= 0, print_sent
= 0;
9748 if (a
->verbosity
< 0)
9752 const char *status
= success
? "accepted" : "rejected";
9755 struct got_pathlist_entry
*pe
;
9756 TAILQ_FOREACH(pe
, a
->delete_branches
, entry
) {
9757 const char *branchname
= pe
->path
;
9758 if (got_path_cmp(branchname
, refname
,
9759 strlen(branchname
), strlen(refname
)) == 0) {
9761 a
->sent_something
= 1;
9767 if (a
->printed_something
)
9769 printf("Server has %s %s", status
, refname
);
9771 printf(": %s", errmsg
);
9772 a
->printed_something
= 1;
9776 if (a
->last_ncolored
!= ncolored
) {
9778 a
->last_ncolored
= ncolored
;
9781 if (a
->last_nfound
!= nfound
) {
9784 a
->last_nfound
= nfound
;
9787 if (a
->last_ntrees
!= ntrees
) {
9791 a
->last_ntrees
= ntrees
;
9794 if ((print_colored
|| print_found
|| print_trees
) &&
9797 print_load_info(print_colored
, print_found
, print_trees
,
9798 ncolored
, nfound
, ntrees
);
9799 a
->printed_something
= 1;
9802 } else if (!a
->loading_done
) {
9804 print_load_info(1, 1, 1, ncolored
, nfound
, ntrees
);
9806 a
->loading_done
= 1;
9809 if (fmt_scaled(packfile_size
, scaled_packsize
) == -1)
9810 return got_error_from_errno("fmt_scaled");
9811 if (fmt_scaled(bytes_sent
, scaled_sent
) == -1)
9812 return got_error_from_errno("fmt_scaled");
9814 if (a
->last_ncommits
!= ncommits
) {
9815 print_searching
= 1;
9816 a
->last_ncommits
= ncommits
;
9819 if (a
->last_nobj_total
!= nobj_total
) {
9820 print_searching
= 1;
9822 a
->last_nobj_total
= nobj_total
;
9825 if (packfile_size
> 0 && (a
->last_scaled_packsize
[0] == '\0' ||
9826 strcmp(scaled_packsize
, a
->last_scaled_packsize
)) != 0) {
9827 if (strlcpy(a
->last_scaled_packsize
, scaled_packsize
,
9828 FMT_SCALED_STRSIZE
) >= FMT_SCALED_STRSIZE
)
9829 return got_error(GOT_ERR_NO_SPACE
);
9832 if (nobj_deltify
> 0 || nobj_written
> 0) {
9833 if (nobj_deltify
> 0) {
9834 p_deltify
= (nobj_deltify
* 100) / nobj_total
;
9835 if (p_deltify
!= a
->last_p_deltify
) {
9836 a
->last_p_deltify
= p_deltify
;
9837 print_searching
= 1;
9842 if (nobj_written
> 0) {
9843 p_written
= (nobj_written
* 100) / nobj_total
;
9844 if (p_written
!= a
->last_p_written
) {
9845 a
->last_p_written
= p_written
;
9846 print_searching
= 1;
9854 if (bytes_sent
> 0) {
9855 p_sent
= (bytes_sent
* 100) / packfile_size
;
9856 if (p_sent
!= a
->last_p_sent
) {
9857 a
->last_p_sent
= p_sent
;
9858 print_searching
= 1;
9864 a
->sent_something
= 1;
9867 if (print_searching
|| print_total
|| print_deltify
|| print_written
||
9870 if (print_searching
)
9871 printf("packing %d reference%s", ncommits
,
9872 ncommits
== 1 ? "" : "s");
9874 printf("; %d object%s", nobj_total
,
9875 nobj_total
== 1 ? "" : "s");
9877 printf("; deltify: %d%%", p_deltify
);
9879 printf("; uploading pack: %*s %d%%", FMT_SCALED_STRSIZE
- 2,
9880 scaled_packsize
, p_sent
);
9881 else if (print_written
)
9882 printf("; writing pack: %*s %d%%", FMT_SCALED_STRSIZE
- 2,
9883 scaled_packsize
, p_written
);
9884 if (print_searching
|| print_total
|| print_deltify
||
9885 print_written
|| print_sent
) {
9886 a
->printed_something
= 1;
9892 static const struct got_error
*
9893 cmd_send(int argc
, char *argv
[])
9895 const struct got_error
*error
= NULL
;
9896 char *cwd
= NULL
, *repo_path
= NULL
;
9897 const char *remote_name
;
9898 char *proto
= NULL
, *host
= NULL
, *port
= NULL
;
9899 char *repo_name
= NULL
, *server_path
= NULL
;
9900 const struct got_remote_repo
*remotes
;
9901 struct got_remote_repo
*remote
= NULL
;
9902 int nremotes
, nbranches
= 0, ndelete_branches
= 0;
9903 struct got_repository
*repo
= NULL
;
9904 struct got_worktree
*worktree
= NULL
;
9905 const struct got_gotconfig
*repo_conf
= NULL
, *worktree_conf
= NULL
;
9906 struct got_pathlist_head branches
;
9907 struct got_pathlist_head tags
;
9908 struct got_reflist_head all_branches
;
9909 struct got_reflist_head all_tags
;
9910 struct got_pathlist_head delete_args
;
9911 struct got_pathlist_head delete_branches
;
9912 struct got_reflist_entry
*re
;
9913 struct got_pathlist_entry
*pe
;
9914 int i
, ch
, sendfd
= -1, sendstatus
;
9916 struct got_send_progress_arg spa
;
9917 int verbosity
= 0, overwrite_refs
= 0;
9918 int send_all_branches
= 0, send_all_tags
= 0;
9919 struct got_reference
*ref
= NULL
;
9920 int *pack_fds
= NULL
;
9922 TAILQ_INIT(&branches
);
9924 TAILQ_INIT(&all_branches
);
9925 TAILQ_INIT(&all_tags
);
9926 TAILQ_INIT(&delete_args
);
9927 TAILQ_INIT(&delete_branches
);
9929 while ((ch
= getopt(argc
, argv
, "ab:d:fqr:Tt:v")) != -1) {
9932 send_all_branches
= 1;
9935 error
= got_pathlist_insert(NULL
, &branches
, optarg
, NULL
);
9941 error
= got_pathlist_insert(NULL
, &delete_args
, optarg
, NULL
);
9952 repo_path
= realpath(optarg
, NULL
);
9953 if (repo_path
== NULL
)
9954 return got_error_from_errno2("realpath",
9956 got_path_strip_trailing_slashes(repo_path
);
9962 error
= got_pathlist_insert(NULL
, &tags
, optarg
, NULL
);
9969 else if (verbosity
< 3)
9980 if (send_all_branches
&& !TAILQ_EMPTY(&branches
))
9981 option_conflict('a', 'b');
9982 if (send_all_tags
&& !TAILQ_EMPTY(&tags
))
9983 option_conflict('T', 't');
9987 remote_name
= GOT_SEND_DEFAULT_REMOTE_NAME
;
9989 remote_name
= argv
[0];
9993 cwd
= getcwd(NULL
, 0);
9995 error
= got_error_from_errno("getcwd");
9999 error
= got_repo_pack_fds_open(&pack_fds
);
10003 if (repo_path
== NULL
) {
10004 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_GOT_DIR
);
10005 if (error
&& error
->code
!= GOT_ERR_NOT_WORKTREE
)
10011 strdup(got_worktree_get_repo_path(worktree
));
10012 if (repo_path
== NULL
)
10013 error
= got_error_from_errno("strdup");
10017 repo_path
= strdup(cwd
);
10018 if (repo_path
== NULL
) {
10019 error
= got_error_from_errno("strdup");
10025 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
10030 worktree_conf
= got_worktree_get_gotconfig(worktree
);
10031 if (worktree_conf
) {
10032 got_gotconfig_get_remotes(&nremotes
, &remotes
,
10034 for (i
= 0; i
< nremotes
; i
++) {
10035 if (strcmp(remotes
[i
].name
, remote_name
) == 0) {
10036 error
= got_repo_remote_repo_dup(&remote
,
10045 if (remote
== NULL
) {
10046 repo_conf
= got_repo_get_gotconfig(repo
);
10048 got_gotconfig_get_remotes(&nremotes
, &remotes
,
10050 for (i
= 0; i
< nremotes
; i
++) {
10051 if (strcmp(remotes
[i
].name
, remote_name
) == 0) {
10052 error
= got_repo_remote_repo_dup(&remote
,
10061 if (remote
== NULL
) {
10062 got_repo_get_gitconfig_remotes(&nremotes
, &remotes
, repo
);
10063 for (i
= 0; i
< nremotes
; i
++) {
10064 if (strcmp(remotes
[i
].name
, remote_name
) == 0) {
10065 error
= got_repo_remote_repo_dup(&remote
,
10073 if (remote
== NULL
) {
10074 error
= got_error_path(remote_name
, GOT_ERR_NO_REMOTE
);
10078 error
= got_dial_parse_uri(&proto
, &host
, &port
, &server_path
,
10079 &repo_name
, remote
->send_url
);
10083 if (strcmp(proto
, "git") == 0) {
10085 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
10086 "sendfd dns inet unveil", NULL
) == -1)
10089 } else if (strcmp(proto
, "git+ssh") == 0 ||
10090 strcmp(proto
, "ssh") == 0) {
10092 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
10093 "sendfd unveil", NULL
) == -1)
10096 } else if (strcmp(proto
, "http") == 0 ||
10097 strcmp(proto
, "git+http") == 0) {
10098 error
= got_error_path(proto
, GOT_ERR_NOT_IMPL
);
10101 error
= got_error_path(proto
, GOT_ERR_BAD_PROTO
);
10105 error
= got_dial_apply_unveil(proto
);
10109 error
= apply_unveil(got_repo_get_path(repo
), 0, NULL
);
10113 if (send_all_branches
) {
10114 error
= got_ref_list(&all_branches
, repo
, "refs/heads",
10115 got_ref_cmp_by_name
, NULL
);
10118 TAILQ_FOREACH(re
, &all_branches
, entry
) {
10119 const char *branchname
= got_ref_get_name(re
->ref
);
10120 error
= got_pathlist_insert(NULL
, &branches
,
10126 } else if (nbranches
== 0) {
10127 for (i
= 0; i
< remote
->nsend_branches
; i
++) {
10128 error
= got_pathlist_insert(NULL
, &branches
,
10129 remote
->send_branches
[i
], NULL
);
10135 if (send_all_tags
) {
10136 error
= got_ref_list(&all_tags
, repo
, "refs/tags",
10137 got_ref_cmp_by_name
, NULL
);
10140 TAILQ_FOREACH(re
, &all_tags
, entry
) {
10141 const char *tagname
= got_ref_get_name(re
->ref
);
10142 error
= got_pathlist_insert(NULL
, &tags
,
10150 * To prevent accidents only branches in refs/heads/ can be deleted
10151 * with 'got send -d'.
10152 * Deleting anything else requires local repository access or Git.
10154 TAILQ_FOREACH(pe
, &delete_args
, entry
) {
10155 const char *branchname
= pe
->path
;
10157 struct got_pathlist_entry
*new;
10158 if (strncmp(branchname
, "refs/heads/", 11) == 0) {
10159 s
= strdup(branchname
);
10161 error
= got_error_from_errno("strdup");
10165 if (asprintf(&s
, "refs/heads/%s", branchname
) == -1) {
10166 error
= got_error_from_errno("asprintf");
10170 error
= got_pathlist_insert(&new, &delete_branches
, s
, NULL
);
10171 if (error
|| new == NULL
/* duplicate */)
10175 ndelete_branches
++;
10178 if (nbranches
== 0 && ndelete_branches
== 0) {
10179 struct got_reference
*head_ref
;
10181 error
= got_ref_open(&head_ref
, repo
,
10182 got_worktree_get_head_ref_name(worktree
), 0);
10184 error
= got_ref_open(&head_ref
, repo
, GOT_REF_HEAD
, 0);
10187 if (got_ref_is_symbolic(head_ref
)) {
10188 error
= got_ref_resolve_symbolic(&ref
, repo
, head_ref
);
10189 got_ref_close(head_ref
);
10194 error
= got_pathlist_insert(NULL
, &branches
, got_ref_get_name(ref
),
10202 /* Release work tree lock. */
10203 got_worktree_close(worktree
);
10207 if (verbosity
>= 0) {
10208 printf("Connecting to \"%s\" %s://%s%s%s%s%s\n",
10209 remote
->name
, proto
, host
,
10210 port
? ":" : "", port
? port
: "",
10211 *server_path
== '/' ? "" : "/", server_path
);
10214 error
= got_send_connect(&sendpid
, &sendfd
, proto
, host
, port
,
10215 server_path
, verbosity
);
10219 memset(&spa
, 0, sizeof(spa
));
10220 spa
.last_scaled_packsize
[0] = '\0';
10221 spa
.last_p_deltify
= -1;
10222 spa
.last_p_written
= -1;
10223 spa
.verbosity
= verbosity
;
10224 spa
.delete_branches
= &delete_branches
;
10225 error
= got_send_pack(remote_name
, &branches
, &tags
, &delete_branches
,
10226 verbosity
, overwrite_refs
, sendfd
, repo
, send_progress
, &spa
,
10227 check_cancelled
, NULL
);
10228 if (spa
.printed_something
)
10232 if (!spa
.sent_something
&& verbosity
>= 0)
10233 printf("Already up-to-date\n");
10236 if (kill(sendpid
, SIGTERM
) == -1)
10237 error
= got_error_from_errno("kill");
10238 if (waitpid(sendpid
, &sendstatus
, 0) == -1 && error
== NULL
)
10239 error
= got_error_from_errno("waitpid");
10241 if (sendfd
!= -1 && close(sendfd
) == -1 && error
== NULL
)
10242 error
= got_error_from_errno("close");
10244 const struct got_error
*close_err
= got_repo_close(repo
);
10249 got_worktree_close(worktree
);
10251 const struct got_error
*pack_err
=
10252 got_repo_pack_fds_close(pack_fds
);
10257 got_ref_close(ref
);
10258 got_repo_free_remote_repo_data(remote
);
10260 got_pathlist_free(&branches
, GOT_PATHLIST_FREE_NONE
);
10261 got_pathlist_free(&tags
, GOT_PATHLIST_FREE_NONE
);
10262 got_ref_list_free(&all_branches
);
10263 got_ref_list_free(&all_tags
);
10264 got_pathlist_free(&delete_args
, GOT_PATHLIST_FREE_NONE
);
10265 got_pathlist_free(&delete_branches
, GOT_PATHLIST_FREE_PATH
);
10277 * Print and if delete is set delete all ref_prefix references.
10278 * If wanted_ref is not NULL, only print or delete this reference.
10280 static const struct got_error
*
10281 process_logmsg_refs(const char *ref_prefix
, size_t prefix_len
,
10282 const char *wanted_ref
, int delete, struct got_worktree
*worktree
,
10283 struct got_repository
*repo
)
10285 const struct got_error
*err
;
10286 struct got_pathlist_head paths
;
10287 struct got_reflist_head refs
;
10288 struct got_reflist_entry
*re
;
10289 struct got_reflist_object_id_map
*refs_idmap
= NULL
;
10290 struct got_commit_object
*commit
= NULL
;
10291 struct got_object_id
*id
= NULL
;
10292 const char *header_prefix
;
10293 char *uuidstr
= NULL
;
10297 TAILQ_INIT(&paths
);
10299 err
= got_ref_list(&refs
, repo
, NULL
, got_ref_cmp_by_name
, repo
);
10303 err
= got_reflist_object_id_map_create(&refs_idmap
, &refs
, repo
);
10307 if (worktree
!= NULL
) {
10308 err
= got_worktree_get_uuid(&uuidstr
, worktree
);
10314 if (strncmp(wanted_ref
, "refs/heads/", 11) == 0)
10318 if (strcmp(ref_prefix
, GOT_WORKTREE_BACKOUT_REF_PREFIX
) == 0)
10319 header_prefix
= "backout";
10321 header_prefix
= "cherrypick";
10323 TAILQ_FOREACH(re
, &refs
, entry
) {
10324 const char *refname
, *wt
;
10326 refname
= got_ref_get_name(re
->ref
);
10328 err
= check_cancelled(NULL
);
10332 if (strncmp(refname
, ref_prefix
, prefix_len
) == 0)
10333 refname
+= prefix_len
+ 1; /* skip '-' delimiter */
10339 if (worktree
== NULL
|| strncmp(refname
, uuidstr
,
10340 GOT_WORKTREE_UUID_STRLEN
) == 0)
10341 refname
+= GOT_WORKTREE_UUID_STRLEN
+ 1; /* skip '-' */
10345 err
= got_repo_match_object_id(&id
, NULL
, refname
,
10346 GOT_OBJ_TYPE_COMMIT
, NULL
, repo
);
10350 err
= got_object_open_as_commit(&commit
, repo
, id
);
10355 found
= strncmp(wanted_ref
, refname
,
10356 strlen(wanted_ref
)) == 0;
10357 if (wanted_ref
&& !found
) {
10358 struct got_reflist_head
*ci_refs
;
10360 ci_refs
= got_reflist_object_id_map_lookup(refs_idmap
,
10364 char *refs_str
= NULL
;
10365 char const *r
= NULL
;
10367 err
= build_refs_str(&refs_str
, ci_refs
, id
,
10374 if (strncmp(r
, wanted_ref
,
10375 strlen(wanted_ref
)) == 0) {
10379 r
= strchr(r
, ' ');
10387 if (wanted_ref
== NULL
|| found
) {
10389 err
= got_ref_delete(re
->ref
, repo
);
10392 printf("Deleted: ");
10393 err
= print_commit_oneline(commit
, id
, repo
,
10397 * Print paths modified by commit to help
10398 * associate commits with worktree changes.
10400 err
= get_changed_paths(&paths
, commit
,
10405 err
= print_commit(commit
, id
, repo
, NULL
,
10406 &paths
, NULL
, 0, 0, refs_idmap
, NULL
,
10408 got_pathlist_free(&paths
,
10409 GOT_PATHLIST_FREE_ALL
);
10411 if (worktree
== NULL
)
10412 printf("work tree: %.*s\n\n",
10413 GOT_WORKTREE_UUID_STRLEN
, wt
);
10419 got_object_commit_close(commit
);
10425 if (wanted_ref
!= NULL
&& !found
)
10426 err
= got_error_fmt(GOT_ERR_NOT_REF
, "%s", wanted_ref
);
10431 got_ref_list_free(&refs
);
10432 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_ALL
);
10434 got_reflist_object_id_map_free(refs_idmap
);
10436 got_object_commit_close(commit
);
10441 * Create new temp "logmsg" ref of the backed-out or cherrypicked commit
10442 * identified by id for log messages to prepopulate the editor on commit.
10444 static const struct got_error
*
10445 logmsg_ref(struct got_object_id
*id
, const char *prefix
,
10446 struct got_worktree
*worktree
, struct got_repository
*repo
)
10448 const struct got_error
*err
= NULL
;
10449 char *idstr
, *ref
= NULL
, *refname
= NULL
;
10450 int histedit_in_progress
;
10451 int rebase_in_progress
, merge_in_progress
;
10454 * Silently refuse to create merge reference if any histedit, merge,
10455 * or rebase operation is in progress.
10457 err
= got_worktree_histedit_in_progress(&histedit_in_progress
,
10461 if (histedit_in_progress
)
10464 err
= got_worktree_rebase_in_progress(&rebase_in_progress
, worktree
);
10467 if (rebase_in_progress
)
10470 err
= got_worktree_merge_in_progress(&merge_in_progress
, worktree
,
10474 if (merge_in_progress
)
10477 err
= got_object_id_str(&idstr
, id
);
10481 err
= got_worktree_get_logmsg_ref_name(&refname
, worktree
, prefix
);
10485 if (asprintf(&ref
, "%s-%s", refname
, idstr
) == -1) {
10486 err
= got_error_from_errno("asprintf");
10490 err
= create_ref(ref
, got_worktree_get_base_commit_id(worktree
),
10500 usage_cherrypick(void)
10502 fprintf(stderr
, "usage: %s cherrypick [-lX] [commit-id]\n",
10507 static const struct got_error
*
10508 cmd_cherrypick(int argc
, char *argv
[])
10510 const struct got_error
*error
= NULL
;
10511 struct got_worktree
*worktree
= NULL
;
10512 struct got_repository
*repo
= NULL
;
10513 char *cwd
= NULL
, *commit_id_str
= NULL
, *keyword_idstr
= NULL
;
10514 struct got_object_id
*commit_id
= NULL
;
10515 struct got_commit_object
*commit
= NULL
;
10516 struct got_object_qid
*pid
;
10517 int ch
, list_refs
= 0, remove_refs
= 0;
10518 struct got_update_progress_arg upa
;
10519 int *pack_fds
= NULL
;
10522 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
10523 "unveil", NULL
) == -1)
10527 while ((ch
= getopt(argc
, argv
, "lX")) != -1) {
10536 usage_cherrypick();
10544 if (list_refs
|| remove_refs
) {
10545 if (argc
!= 0 && argc
!= 1)
10546 usage_cherrypick();
10547 } else if (argc
!= 1)
10548 usage_cherrypick();
10549 if (list_refs
&& remove_refs
)
10550 option_conflict('l', 'X');
10552 cwd
= getcwd(NULL
, 0);
10554 error
= got_error_from_errno("getcwd");
10558 error
= got_repo_pack_fds_open(&pack_fds
);
10562 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_GOT_DIR
);
10564 if (list_refs
|| remove_refs
) {
10565 if (error
->code
!= GOT_ERR_NOT_WORKTREE
)
10568 if (error
->code
== GOT_ERR_NOT_WORKTREE
)
10569 error
= wrap_not_worktree_error(error
,
10570 "cherrypick", cwd
);
10575 error
= got_repo_open(&repo
,
10576 worktree
? got_worktree_get_repo_path(worktree
) : cwd
,
10581 error
= apply_unveil(got_repo_get_path(repo
), 0,
10582 worktree
? got_worktree_get_root_path(worktree
) : NULL
);
10586 if (list_refs
|| remove_refs
) {
10587 error
= process_logmsg_refs(GOT_WORKTREE_CHERRYPICK_REF_PREFIX
,
10588 GOT_WORKTREE_CHERRYPICK_REF_PREFIX_LEN
,
10589 argc
== 1 ? argv
[0] : NULL
, remove_refs
, worktree
, repo
);
10593 error
= got_keyword_to_idstr(&keyword_idstr
, argv
[0], repo
, worktree
);
10597 error
= got_repo_match_object_id(&commit_id
, NULL
,
10598 keyword_idstr
!= NULL
? keyword_idstr
: argv
[0],
10599 GOT_OBJ_TYPE_COMMIT
, NULL
, repo
);
10602 error
= got_object_id_str(&commit_id_str
, commit_id
);
10606 error
= got_object_open_as_commit(&commit
, repo
, commit_id
);
10609 pid
= STAILQ_FIRST(got_object_commit_get_parent_ids(commit
));
10610 memset(&upa
, 0, sizeof(upa
));
10611 error
= got_worktree_merge_files(worktree
, pid
? &pid
->id
: NULL
,
10612 commit_id
, repo
, update_progress
, &upa
, check_cancelled
,
10617 if (upa
.did_something
) {
10618 error
= logmsg_ref(commit_id
,
10619 GOT_WORKTREE_CHERRYPICK_REF_PREFIX
, worktree
, repo
);
10622 printf("Merged commit %s\n", commit_id_str
);
10624 print_merge_progress_stats(&upa
);
10627 free(keyword_idstr
);
10629 got_object_commit_close(commit
);
10630 free(commit_id_str
);
10632 got_worktree_close(worktree
);
10634 const struct got_error
*close_err
= got_repo_close(repo
);
10639 const struct got_error
*pack_err
=
10640 got_repo_pack_fds_close(pack_fds
);
10649 usage_backout(void)
10651 fprintf(stderr
, "usage: %s backout [-lX] [commit-id]\n", getprogname());
10655 static const struct got_error
*
10656 cmd_backout(int argc
, char *argv
[])
10658 const struct got_error
*error
= NULL
;
10659 struct got_worktree
*worktree
= NULL
;
10660 struct got_repository
*repo
= NULL
;
10661 char *cwd
= NULL
, *commit_id_str
= NULL
, *keyword_idstr
= NULL
;
10662 struct got_object_id
*commit_id
= NULL
;
10663 struct got_commit_object
*commit
= NULL
;
10664 struct got_object_qid
*pid
;
10665 int ch
, list_refs
= 0, remove_refs
= 0;
10666 struct got_update_progress_arg upa
;
10667 int *pack_fds
= NULL
;
10670 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
10671 "unveil", NULL
) == -1)
10675 while ((ch
= getopt(argc
, argv
, "lX")) != -1) {
10692 if (list_refs
|| remove_refs
) {
10693 if (argc
!= 0 && argc
!= 1)
10695 } else if (argc
!= 1)
10697 if (list_refs
&& remove_refs
)
10698 option_conflict('l', 'X');
10700 cwd
= getcwd(NULL
, 0);
10702 error
= got_error_from_errno("getcwd");
10706 error
= got_repo_pack_fds_open(&pack_fds
);
10710 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_GOT_DIR
);
10712 if (list_refs
|| remove_refs
) {
10713 if (error
->code
!= GOT_ERR_NOT_WORKTREE
)
10716 if (error
->code
== GOT_ERR_NOT_WORKTREE
)
10717 error
= wrap_not_worktree_error(error
,
10723 error
= got_repo_open(&repo
,
10724 worktree
? got_worktree_get_repo_path(worktree
) : cwd
,
10729 error
= apply_unveil(got_repo_get_path(repo
), 0,
10730 worktree
? got_worktree_get_root_path(worktree
) : NULL
);
10734 if (list_refs
|| remove_refs
) {
10735 error
= process_logmsg_refs(GOT_WORKTREE_BACKOUT_REF_PREFIX
,
10736 GOT_WORKTREE_BACKOUT_REF_PREFIX_LEN
,
10737 argc
== 1 ? argv
[0] : NULL
, remove_refs
, worktree
, repo
);
10741 error
= got_keyword_to_idstr(&keyword_idstr
, argv
[0], repo
, worktree
);
10745 error
= got_repo_match_object_id(&commit_id
, NULL
,
10746 keyword_idstr
!= NULL
? keyword_idstr
: argv
[0],
10747 GOT_OBJ_TYPE_COMMIT
, NULL
, repo
);
10750 error
= got_object_id_str(&commit_id_str
, commit_id
);
10754 error
= got_object_open_as_commit(&commit
, repo
, commit_id
);
10757 pid
= STAILQ_FIRST(got_object_commit_get_parent_ids(commit
));
10759 error
= got_error(GOT_ERR_ROOT_COMMIT
);
10763 memset(&upa
, 0, sizeof(upa
));
10764 error
= got_worktree_merge_files(worktree
, commit_id
, &pid
->id
,
10765 repo
, update_progress
, &upa
, check_cancelled
, NULL
);
10769 if (upa
.did_something
) {
10770 error
= logmsg_ref(commit_id
, GOT_WORKTREE_BACKOUT_REF_PREFIX
,
10774 printf("Backed out commit %s\n", commit_id_str
);
10776 print_merge_progress_stats(&upa
);
10779 free(keyword_idstr
);
10781 got_object_commit_close(commit
);
10782 free(commit_id_str
);
10784 got_worktree_close(worktree
);
10786 const struct got_error
*close_err
= got_repo_close(repo
);
10791 const struct got_error
*pack_err
=
10792 got_repo_pack_fds_close(pack_fds
);
10802 fprintf(stderr
, "usage: %s rebase [-aCclX] [branch]\n", getprogname());
10807 trim_logmsg(char *logmsg
, int limit
)
10812 len
= strlen(logmsg
);
10815 logmsg
[len
] = '\0';
10816 nl
= strchr(logmsg
, '\n');
10821 static const struct got_error
*
10822 get_short_logmsg(char **logmsg
, int limit
, struct got_commit_object
*commit
)
10824 const struct got_error
*err
;
10825 char *logmsg0
= NULL
;
10828 err
= got_object_commit_get_logmsg(&logmsg0
, commit
);
10833 while (isspace((unsigned char)s
[0]))
10836 *logmsg
= strdup(s
);
10837 if (*logmsg
== NULL
) {
10838 err
= got_error_from_errno("strdup");
10842 trim_logmsg(*logmsg
, limit
);
10848 static const struct got_error
*
10849 show_rebase_merge_conflict(struct got_object_id
*id
,
10850 struct got_repository
*repo
)
10852 const struct got_error
*err
;
10853 struct got_commit_object
*commit
= NULL
;
10854 char *id_str
= NULL
, *logmsg
= NULL
;
10856 err
= got_object_open_as_commit(&commit
, repo
, id
);
10860 err
= got_object_id_str(&id_str
, id
);
10866 err
= get_short_logmsg(&logmsg
, 42, commit
);
10870 printf("%s -> merge conflict: %s\n", id_str
, logmsg
);
10873 got_object_commit_close(commit
);
10878 static const struct got_error
*
10879 show_rebase_progress(struct got_commit_object
*commit
,
10880 struct got_object_id
*old_id
, struct got_object_id
*new_id
)
10882 const struct got_error
*err
;
10883 char *old_id_str
= NULL
, *new_id_str
= NULL
, *logmsg
= NULL
;
10885 err
= got_object_id_str(&old_id_str
, old_id
);
10890 err
= got_object_id_str(&new_id_str
, new_id
);
10895 old_id_str
[12] = '\0';
10897 new_id_str
[12] = '\0';
10899 err
= get_short_logmsg(&logmsg
, 42, commit
);
10903 printf("%s -> %s: %s\n", old_id_str
,
10904 new_id_str
? new_id_str
: "no-op change", logmsg
);
10912 static const struct got_error
*
10913 rebase_complete(struct got_worktree
*worktree
, struct got_fileindex
*fileindex
,
10914 struct got_reference
*branch
, struct got_reference
*tmp_branch
,
10915 struct got_repository
*repo
, int create_backup
)
10917 printf("Switching work tree to %s\n", got_ref_get_name(branch
));
10918 return got_worktree_rebase_complete(worktree
, fileindex
,
10919 tmp_branch
, branch
, repo
, create_backup
);
10922 static const struct got_error
*
10923 rebase_commit(struct got_pathlist_head
*merged_paths
,
10924 struct got_worktree
*worktree
, struct got_fileindex
*fileindex
,
10925 struct got_reference
*tmp_branch
, const char *committer
,
10926 struct got_object_id
*commit_id
, int allow_conflict
,
10927 struct got_repository
*repo
)
10929 const struct got_error
*error
;
10930 struct got_commit_object
*commit
;
10931 struct got_object_id
*new_commit_id
;
10933 error
= got_object_open_as_commit(&commit
, repo
, commit_id
);
10937 error
= got_worktree_rebase_commit(&new_commit_id
, merged_paths
,
10938 worktree
, fileindex
, tmp_branch
, committer
, commit
, commit_id
,
10939 allow_conflict
, repo
);
10941 if (error
->code
!= GOT_ERR_COMMIT_NO_CHANGES
)
10943 error
= show_rebase_progress(commit
, commit_id
, NULL
);
10945 error
= show_rebase_progress(commit
, commit_id
, new_commit_id
);
10946 free(new_commit_id
);
10949 got_object_commit_close(commit
);
10953 struct check_path_prefix_arg
{
10954 const char *path_prefix
;
10959 static const struct got_error
*
10960 check_path_prefix_in_diff(void *arg
, struct got_blob_object
*blob1
,
10961 struct got_blob_object
*blob2
, FILE *f1
, FILE *f2
,
10962 struct got_object_id
*id1
, struct got_object_id
*id2
,
10963 const char *path1
, const char *path2
,
10964 mode_t mode1
, mode_t mode2
, struct got_repository
*repo
)
10966 struct check_path_prefix_arg
*a
= arg
;
10968 if ((path1
&& !got_path_is_child(path1
, a
->path_prefix
, a
->len
)) ||
10969 (path2
&& !got_path_is_child(path2
, a
->path_prefix
, a
->len
)))
10970 return got_error(a
->errcode
);
10975 static const struct got_error
*
10976 check_path_prefix(struct got_object_id
*parent_id
,
10977 struct got_object_id
*commit_id
, const char *path_prefix
,
10978 int errcode
, struct got_repository
*repo
)
10980 const struct got_error
*err
;
10981 struct got_tree_object
*tree1
= NULL
, *tree2
= NULL
;
10982 struct got_commit_object
*commit
= NULL
, *parent_commit
= NULL
;
10983 struct check_path_prefix_arg cpp_arg
;
10985 if (got_path_is_root_dir(path_prefix
))
10988 err
= got_object_open_as_commit(&commit
, repo
, commit_id
);
10992 err
= got_object_open_as_commit(&parent_commit
, repo
, parent_id
);
10996 err
= got_object_open_as_tree(&tree1
, repo
,
10997 got_object_commit_get_tree_id(parent_commit
));
11001 err
= got_object_open_as_tree(&tree2
, repo
,
11002 got_object_commit_get_tree_id(commit
));
11006 cpp_arg
.path_prefix
= path_prefix
;
11007 while (cpp_arg
.path_prefix
[0] == '/')
11008 cpp_arg
.path_prefix
++;
11009 cpp_arg
.len
= strlen(cpp_arg
.path_prefix
);
11010 cpp_arg
.errcode
= errcode
;
11011 err
= got_diff_tree(tree1
, tree2
, NULL
, NULL
, -1, -1, "", "", repo
,
11012 check_path_prefix_in_diff
, &cpp_arg
, 0);
11015 got_object_tree_close(tree1
);
11017 got_object_tree_close(tree2
);
11019 got_object_commit_close(commit
);
11021 got_object_commit_close(parent_commit
);
11025 static const struct got_error
*
11026 collect_commits(struct got_object_id_queue
*commits
,
11027 struct got_object_id
*initial_commit_id
,
11028 struct got_object_id
*iter_start_id
, struct got_object_id
*iter_stop_id
,
11029 const char *path_prefix
, int path_prefix_errcode
,
11030 struct got_repository
*repo
)
11032 const struct got_error
*err
= NULL
;
11033 struct got_commit_graph
*graph
= NULL
;
11034 struct got_object_id parent_id
, commit_id
;
11035 struct got_object_qid
*qid
;
11037 err
= got_commit_graph_open(&graph
, "/", 1);
11041 err
= got_commit_graph_bfsort(graph
, iter_start_id
, repo
,
11042 check_cancelled
, NULL
);
11046 memcpy(&commit_id
, initial_commit_id
, sizeof(commit_id
));
11047 while (got_object_id_cmp(&commit_id
, iter_stop_id
) != 0) {
11048 err
= got_commit_graph_iter_next(&parent_id
, graph
, repo
,
11049 check_cancelled
, NULL
);
11051 if (err
->code
== GOT_ERR_ITER_COMPLETED
) {
11052 err
= got_error_msg(GOT_ERR_ANCESTRY
,
11053 "ran out of commits to rebase before "
11054 "youngest common ancestor commit has "
11055 "been reached?!?");
11059 err
= check_path_prefix(&parent_id
, &commit_id
,
11060 path_prefix
, path_prefix_errcode
, repo
);
11064 err
= got_object_qid_alloc(&qid
, &commit_id
);
11067 STAILQ_INSERT_HEAD(commits
, qid
, entry
);
11069 memcpy(&commit_id
, &parent_id
, sizeof(commit_id
));
11073 got_commit_graph_close(graph
);
11077 static const struct got_error
*
11078 get_commit_brief_str(char **brief_str
, struct got_commit_object
*commit
)
11080 const struct got_error
*err
= NULL
;
11081 time_t committer_time
;
11083 char datebuf
[11]; /* YYYY-MM-DD + NUL */
11084 char *author0
= NULL
, *author
, *smallerthan
;
11085 char *logmsg0
= NULL
, *logmsg
, *newline
;
11087 committer_time
= got_object_commit_get_committer_time(commit
);
11088 if (gmtime_r(&committer_time
, &tm
) == NULL
)
11089 return got_error_from_errno("gmtime_r");
11090 if (strftime(datebuf
, sizeof(datebuf
), "%F", &tm
) == 0)
11091 return got_error(GOT_ERR_NO_SPACE
);
11093 author0
= strdup(got_object_commit_get_author(commit
));
11094 if (author0
== NULL
)
11095 return got_error_from_errno("strdup");
11097 smallerthan
= strchr(author
, '<');
11098 if (smallerthan
&& smallerthan
[1] != '\0')
11099 author
= smallerthan
+ 1;
11100 author
[strcspn(author
, "@>")] = '\0';
11102 err
= got_object_commit_get_logmsg(&logmsg0
, commit
);
11106 while (*logmsg
== '\n')
11108 newline
= strchr(logmsg
, '\n');
11112 if (asprintf(brief_str
, "%s %s %s",
11113 datebuf
, author
, logmsg
) == -1)
11114 err
= got_error_from_errno("asprintf");
11121 static const struct got_error
*
11122 delete_backup_ref(struct got_reference
*ref
, struct got_object_id
*id
,
11123 struct got_repository
*repo
)
11125 const struct got_error
*err
;
11128 err
= got_object_id_str(&id_str
, id
);
11132 err
= got_ref_delete(ref
, repo
);
11136 printf("Deleted %s: %s\n", got_ref_get_name(ref
), id_str
);
11142 static const struct got_error
*
11143 print_backup_ref(const char *branch_name
, const char *new_id_str
,
11144 struct got_object_id
*old_commit_id
, struct got_commit_object
*old_commit
,
11145 struct got_reflist_object_id_map
*refs_idmap
,
11146 struct got_repository
*repo
)
11148 const struct got_error
*err
= NULL
;
11149 struct got_reflist_head
*refs
;
11150 char *refs_str
= NULL
;
11151 struct got_object_id
*new_commit_id
= NULL
;
11152 struct got_commit_object
*new_commit
= NULL
;
11153 char *new_commit_brief_str
= NULL
;
11154 struct got_object_id
*yca_id
= NULL
;
11155 struct got_commit_object
*yca_commit
= NULL
;
11156 char *yca_id_str
= NULL
, *yca_brief_str
= NULL
;
11157 char *custom_refs_str
;
11159 if (asprintf(&custom_refs_str
, "formerly %s", branch_name
) == -1)
11160 return got_error_from_errno("asprintf");
11162 err
= print_commit(old_commit
, old_commit_id
, repo
, NULL
, NULL
, NULL
,
11163 0, 0, refs_idmap
, custom_refs_str
, NULL
);
11167 err
= got_object_resolve_id_str(&new_commit_id
, repo
, new_id_str
);
11171 refs
= got_reflist_object_id_map_lookup(refs_idmap
, new_commit_id
);
11173 err
= build_refs_str(&refs_str
, refs
, new_commit_id
, repo
, 0);
11178 err
= got_object_open_as_commit(&new_commit
, repo
, new_commit_id
);
11182 err
= get_commit_brief_str(&new_commit_brief_str
, new_commit
);
11186 err
= got_commit_graph_find_youngest_common_ancestor(&yca_id
,
11187 old_commit_id
, new_commit_id
, 1, 0, repo
, check_cancelled
, NULL
);
11191 printf("has become commit %s%s%s%s\n %s\n", new_id_str
,
11192 refs_str
? " (" : "", refs_str
? refs_str
: "",
11193 refs_str
? ")" : "", new_commit_brief_str
);
11194 if (yca_id
&& got_object_id_cmp(yca_id
, new_commit_id
) != 0 &&
11195 got_object_id_cmp(yca_id
, old_commit_id
) != 0) {
11199 err
= got_object_open_as_commit(&yca_commit
, repo
, yca_id
);
11203 err
= get_commit_brief_str(&yca_brief_str
, yca_commit
);
11207 err
= got_object_id_str(&yca_id_str
, yca_id
);
11211 refs
= got_reflist_object_id_map_lookup(refs_idmap
, yca_id
);
11213 err
= build_refs_str(&refs_str
, refs
, yca_id
, repo
, 0);
11217 printf("history forked at %s%s%s%s\n %s\n",
11219 refs_str
? " (" : "", refs_str
? refs_str
: "",
11220 refs_str
? ")" : "", yca_brief_str
);
11223 free(custom_refs_str
);
11224 free(new_commit_id
);
11228 free(yca_brief_str
);
11230 got_object_commit_close(new_commit
);
11232 got_object_commit_close(yca_commit
);
11237 static const struct got_error
*
11238 worktree_has_logmsg_ref(const char *caller
, struct got_worktree
*worktree
,
11239 struct got_repository
*repo
)
11241 const struct got_error
*err
;
11242 struct got_reflist_head refs
;
11243 struct got_reflist_entry
*re
;
11244 char *uuidstr
= NULL
;
11245 static char msg
[160];
11249 err
= got_worktree_get_uuid(&uuidstr
, worktree
);
11253 err
= got_ref_list(&refs
, repo
, "refs/got/worktree",
11254 got_ref_cmp_by_name
, repo
);
11258 TAILQ_FOREACH(re
, &refs
, entry
) {
11259 const char *cmd
, *refname
, *type
;
11261 refname
= got_ref_get_name(re
->ref
);
11263 if (strncmp(refname
, GOT_WORKTREE_CHERRYPICK_REF_PREFIX
,
11264 GOT_WORKTREE_CHERRYPICK_REF_PREFIX_LEN
) == 0) {
11265 refname
+= GOT_WORKTREE_CHERRYPICK_REF_PREFIX_LEN
+ 1;
11266 cmd
= "cherrypick";
11267 type
= "cherrypicked";
11268 } else if (strncmp(refname
, GOT_WORKTREE_BACKOUT_REF_PREFIX
,
11269 GOT_WORKTREE_BACKOUT_REF_PREFIX_LEN
) == 0) {
11270 refname
+= GOT_WORKTREE_BACKOUT_REF_PREFIX_LEN
+ 1;
11272 type
= "backed-out";
11276 if (strncmp(refname
, uuidstr
, GOT_WORKTREE_UUID_STRLEN
) != 0)
11279 snprintf(msg
, sizeof(msg
),
11280 "work tree has references created by %s commits which "
11281 "must be removed with 'got %s -X' before running the %s "
11282 "command", type
, cmd
, caller
);
11283 err
= got_error_msg(GOT_ERR_WORKTREE_META
, msg
);
11289 got_ref_list_free(&refs
);
11293 static const struct got_error
*
11294 process_backup_refs(const char *backup_ref_prefix
,
11295 const char *wanted_branch_name
,
11296 int delete, struct got_repository
*repo
)
11298 const struct got_error
*err
;
11299 struct got_reflist_head refs
, backup_refs
;
11300 struct got_reflist_entry
*re
;
11301 const size_t backup_ref_prefix_len
= strlen(backup_ref_prefix
);
11302 struct got_object_id
*old_commit_id
= NULL
;
11303 char *branch_name
= NULL
;
11304 struct got_commit_object
*old_commit
= NULL
;
11305 struct got_reflist_object_id_map
*refs_idmap
= NULL
;
11306 int wanted_branch_found
= 0;
11309 TAILQ_INIT(&backup_refs
);
11311 err
= got_ref_list(&refs
, repo
, NULL
, got_ref_cmp_by_name
, NULL
);
11315 err
= got_reflist_object_id_map_create(&refs_idmap
, &refs
, repo
);
11319 if (wanted_branch_name
) {
11320 if (strncmp(wanted_branch_name
, "refs/heads/", 11) == 0)
11321 wanted_branch_name
+= 11;
11324 err
= got_ref_list(&backup_refs
, repo
, backup_ref_prefix
,
11325 got_ref_cmp_by_commit_timestamp_descending
, repo
);
11329 TAILQ_FOREACH(re
, &backup_refs
, entry
) {
11330 const char *refname
= got_ref_get_name(re
->ref
);
11333 err
= check_cancelled(NULL
);
11337 err
= got_ref_resolve(&old_commit_id
, repo
, re
->ref
);
11341 err
= got_object_open_as_commit(&old_commit
, repo
,
11346 if (strncmp(backup_ref_prefix
, refname
,
11347 backup_ref_prefix_len
) == 0)
11348 refname
+= backup_ref_prefix_len
;
11350 while (refname
[0] == '/')
11353 branch_name
= strdup(refname
);
11354 if (branch_name
== NULL
) {
11355 err
= got_error_from_errno("strdup");
11358 slash
= strrchr(branch_name
, '/');
11361 refname
+= strlen(branch_name
) + 1;
11364 if (wanted_branch_name
== NULL
||
11365 strcmp(wanted_branch_name
, branch_name
) == 0) {
11366 wanted_branch_found
= 1;
11368 err
= delete_backup_ref(re
->ref
,
11369 old_commit_id
, repo
);
11371 err
= print_backup_ref(branch_name
, refname
,
11372 old_commit_id
, old_commit
, refs_idmap
,
11379 free(old_commit_id
);
11380 old_commit_id
= NULL
;
11382 branch_name
= NULL
;
11383 got_object_commit_close(old_commit
);
11387 if (wanted_branch_name
&& !wanted_branch_found
) {
11388 err
= got_error_fmt(GOT_ERR_NOT_REF
,
11389 "%s/%s/", backup_ref_prefix
, wanted_branch_name
);
11393 got_reflist_object_id_map_free(refs_idmap
);
11394 got_ref_list_free(&refs
);
11395 got_ref_list_free(&backup_refs
);
11396 free(old_commit_id
);
11399 got_object_commit_close(old_commit
);
11403 static const struct got_error
*
11404 abort_progress(void *arg
, unsigned char status
, const char *path
)
11407 * Unversioned files should not clutter progress output when
11408 * an operation is aborted.
11410 if (status
== GOT_STATUS_UNVERSIONED
)
11413 return update_progress(arg
, status
, path
);
11416 static const struct got_error
*
11417 find_merge_commit_yca(struct got_object_id
**new_yca_id
,
11418 struct got_object_id
*branch_head_commit_id
,
11419 struct got_object_id
*yca_id
,
11420 struct got_object_id
*base_commit_id
,
11421 struct got_repository
*repo
)
11423 const struct got_error
*err
= NULL
;
11424 struct got_commit_graph
*graph
= NULL
;
11425 struct got_commit_object
*commit
= NULL
;
11427 *new_yca_id
= NULL
;
11429 err
= got_commit_graph_open(&graph
, "/", 1);
11433 err
= got_commit_graph_bfsort(graph
, base_commit_id
,
11434 repo
, check_cancelled
, NULL
);
11439 struct got_object_id id
;
11441 err
= got_commit_graph_iter_next(&id
, graph
, repo
,
11442 check_cancelled
, NULL
);
11444 if (err
->code
== GOT_ERR_ITER_COMPLETED
)
11449 err
= got_object_open_as_commit(&commit
, repo
, &id
);
11453 if (got_object_commit_get_nparents(commit
) > 1) {
11454 /* Search for a better YCA using toposort. */
11455 err
= got_commit_graph_find_youngest_common_ancestor(
11456 new_yca_id
, base_commit_id
, branch_head_commit_id
,
11457 0, 1, repo
, check_cancelled
, NULL
);
11461 if (got_object_id_cmp(&id
, yca_id
) == 0)
11463 got_object_commit_close(commit
);
11467 got_commit_graph_close(graph
);
11469 got_object_commit_close(commit
);
11473 static const struct got_error
*
11474 cmd_rebase(int argc
, char *argv
[])
11476 const struct got_error
*error
= NULL
;
11477 struct got_worktree
*worktree
= NULL
;
11478 struct got_repository
*repo
= NULL
;
11479 struct got_fileindex
*fileindex
= NULL
;
11480 char *cwd
= NULL
, *committer
= NULL
, *gitconfig_path
= NULL
;
11481 struct got_reference
*branch
= NULL
;
11482 struct got_reference
*new_base_branch
= NULL
, *tmp_branch
= NULL
;
11483 struct got_object_id
*commit_id
= NULL
, *parent_id
= NULL
;
11484 struct got_object_id
*resume_commit_id
= NULL
;
11485 struct got_object_id
*branch_head_commit_id
= NULL
, *yca_id
= NULL
;
11486 struct got_object_id
*head_commit_id
= NULL
;
11487 struct got_reference
*head_ref
= NULL
;
11488 struct got_commit_object
*commit
= NULL
;
11489 int ch
, rebase_in_progress
= 0, abort_rebase
= 0, continue_rebase
= 0;
11490 int histedit_in_progress
= 0, merge_in_progress
= 0;
11491 int create_backup
= 1, list_backups
= 0, delete_backups
= 0;
11492 int allow_conflict
= 0;
11493 struct got_object_id_queue commits
;
11494 struct got_pathlist_head merged_paths
;
11495 const struct got_object_id_queue
*parent_ids
;
11496 struct got_object_qid
*qid
, *pid
;
11497 struct got_update_progress_arg upa
;
11498 int *pack_fds
= NULL
;
11500 STAILQ_INIT(&commits
);
11501 TAILQ_INIT(&merged_paths
);
11502 memset(&upa
, 0, sizeof(upa
));
11505 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
11506 "unveil", NULL
) == -1)
11510 while ((ch
= getopt(argc
, argv
, "aCclX")) != -1) {
11516 allow_conflict
= 1;
11519 continue_rebase
= 1;
11525 delete_backups
= 1;
11536 if (list_backups
) {
11538 option_conflict('l', 'a');
11539 if (allow_conflict
)
11540 option_conflict('l', 'C');
11541 if (continue_rebase
)
11542 option_conflict('l', 'c');
11543 if (delete_backups
)
11544 option_conflict('l', 'X');
11545 if (argc
!= 0 && argc
!= 1)
11547 } else if (delete_backups
) {
11549 option_conflict('X', 'a');
11550 if (allow_conflict
)
11551 option_conflict('X', 'C');
11552 if (continue_rebase
)
11553 option_conflict('X', 'c');
11555 option_conflict('l', 'X');
11556 if (argc
!= 0 && argc
!= 1)
11558 } else if (allow_conflict
) {
11560 option_conflict('C', 'a');
11561 if (!continue_rebase
)
11562 errx(1, "-C option requires -c");
11564 if (abort_rebase
&& continue_rebase
)
11566 else if (abort_rebase
|| continue_rebase
) {
11569 } else if (argc
!= 1)
11573 cwd
= getcwd(NULL
, 0);
11575 error
= got_error_from_errno("getcwd");
11579 error
= got_repo_pack_fds_open(&pack_fds
);
11583 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_GOT_DIR
);
11585 if (list_backups
|| delete_backups
) {
11586 if (error
->code
!= GOT_ERR_NOT_WORKTREE
)
11589 if (error
->code
== GOT_ERR_NOT_WORKTREE
)
11590 error
= wrap_not_worktree_error(error
,
11596 error
= get_gitconfig_path(&gitconfig_path
);
11599 error
= got_repo_open(&repo
,
11600 worktree
? got_worktree_get_repo_path(worktree
) : cwd
,
11601 gitconfig_path
, pack_fds
);
11605 if (worktree
!= NULL
&& !list_backups
&& !delete_backups
) {
11606 error
= worktree_has_logmsg_ref("rebase", worktree
, repo
);
11611 error
= get_author(&committer
, repo
, worktree
);
11612 if (error
&& error
->code
!= GOT_ERR_COMMIT_NO_AUTHOR
)
11615 error
= apply_unveil(got_repo_get_path(repo
), 0,
11616 worktree
? got_worktree_get_root_path(worktree
) : NULL
);
11620 if (list_backups
|| delete_backups
) {
11621 error
= process_backup_refs(
11622 GOT_WORKTREE_REBASE_BACKUP_REF_PREFIX
,
11623 argc
== 1 ? argv
[0] : NULL
, delete_backups
, repo
);
11624 goto done
; /* nothing else to do */
11627 error
= got_worktree_histedit_in_progress(&histedit_in_progress
,
11631 if (histedit_in_progress
) {
11632 error
= got_error(GOT_ERR_HISTEDIT_BUSY
);
11636 error
= got_worktree_merge_in_progress(&merge_in_progress
,
11640 if (merge_in_progress
) {
11641 error
= got_error(GOT_ERR_MERGE_BUSY
);
11645 error
= got_worktree_rebase_in_progress(&rebase_in_progress
, worktree
);
11649 if (abort_rebase
) {
11650 if (!rebase_in_progress
) {
11651 error
= got_error(GOT_ERR_NOT_REBASING
);
11654 error
= got_worktree_rebase_continue(&resume_commit_id
,
11655 &new_base_branch
, &tmp_branch
, &branch
, &fileindex
,
11659 printf("Switching work tree to %s\n",
11660 got_ref_get_symref_target(new_base_branch
));
11661 error
= got_worktree_rebase_abort(worktree
, fileindex
, repo
,
11662 new_base_branch
, abort_progress
, &upa
);
11665 printf("Rebase of %s aborted\n", got_ref_get_name(branch
));
11666 print_merge_progress_stats(&upa
);
11667 goto done
; /* nothing else to do */
11670 if (continue_rebase
) {
11671 if (!rebase_in_progress
) {
11672 error
= got_error(GOT_ERR_NOT_REBASING
);
11675 error
= got_worktree_rebase_continue(&resume_commit_id
,
11676 &new_base_branch
, &tmp_branch
, &branch
, &fileindex
,
11681 error
= rebase_commit(NULL
, worktree
, fileindex
, tmp_branch
,
11682 committer
, resume_commit_id
, allow_conflict
, repo
);
11686 yca_id
= got_object_id_dup(resume_commit_id
);
11687 if (yca_id
== NULL
) {
11688 error
= got_error_from_errno("got_object_id_dup");
11692 error
= got_ref_open(&branch
, repo
, argv
[0], 0);
11695 if (strncmp(got_ref_get_name(branch
), "refs/heads/", 11) != 0) {
11696 error
= got_error_msg(GOT_ERR_COMMIT_BRANCH
,
11697 "will not rebase a branch which lives outside "
11698 "the \"refs/heads/\" reference namespace");
11703 error
= got_ref_resolve(&branch_head_commit_id
, repo
, branch
);
11707 if (!continue_rebase
) {
11708 struct got_object_id
*base_commit_id
;
11710 error
= got_ref_open(&head_ref
, repo
,
11711 got_worktree_get_head_ref_name(worktree
), 0);
11714 error
= got_ref_resolve(&head_commit_id
, repo
, head_ref
);
11717 base_commit_id
= got_worktree_get_base_commit_id(worktree
);
11718 if (got_object_id_cmp(base_commit_id
, head_commit_id
) != 0) {
11719 error
= got_error(GOT_ERR_REBASE_OUT_OF_DATE
);
11723 error
= got_commit_graph_find_youngest_common_ancestor(&yca_id
,
11724 base_commit_id
, branch_head_commit_id
, 1, 0,
11725 repo
, check_cancelled
, NULL
);
11727 if (error
->code
== GOT_ERR_ANCESTRY
) {
11728 error
= got_error_msg(GOT_ERR_ANCESTRY
,
11729 "specified branch shares no common "
11730 "ancestry with work tree's branch");
11736 * If a merge commit appears between the new base branch tip
11737 * and a YCA found via first-parent traversal then we might
11738 * find a better YCA using topologically sorted commits.
11740 if (got_object_id_cmp(base_commit_id
, yca_id
) != 0) {
11741 struct got_object_id
*better_yca_id
;
11742 error
= find_merge_commit_yca(&better_yca_id
,
11743 branch_head_commit_id
, yca_id
,
11744 base_commit_id
, repo
);
11747 if (better_yca_id
) {
11749 yca_id
= better_yca_id
;
11753 if (got_object_id_cmp(base_commit_id
, yca_id
) == 0) {
11754 struct got_pathlist_head paths
;
11755 const char *branch_name
= got_ref_get_name(branch
);
11757 got_worktree_get_head_ref_name(worktree
);
11759 if (strcmp(branch_name
, base
) == 0) {
11760 error
= got_error_fmt(GOT_ERR_WRONG_BRANCH
,
11761 "cannot rebase %s onto itself",
11765 printf("%s is already based on %s\n",
11766 branch_name
, base
);
11768 error
= switch_head_ref(branch
, branch_head_commit_id
,
11772 error
= got_worktree_set_base_commit_id(worktree
, repo
,
11773 branch_head_commit_id
);
11776 TAILQ_INIT(&paths
);
11777 error
= got_pathlist_insert(NULL
, &paths
, "", NULL
);
11780 error
= got_worktree_checkout_files(worktree
,
11781 &paths
, repo
, update_progress
, &upa
,
11782 check_cancelled
, NULL
);
11783 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_NONE
);
11786 if (upa
.did_something
) {
11788 error
= got_object_id_str(&id_str
,
11789 branch_head_commit_id
);
11792 printf("Updated to %s: %s\n",
11793 got_worktree_get_head_ref_name(worktree
),
11797 printf("Already up-to-date\n");
11798 print_update_progress_stats(&upa
);
11803 commit_id
= branch_head_commit_id
;
11804 error
= got_object_open_as_commit(&commit
, repo
, commit_id
);
11808 parent_ids
= got_object_commit_get_parent_ids(commit
);
11809 pid
= STAILQ_FIRST(parent_ids
);
11811 error
= collect_commits(&commits
, commit_id
, &pid
->id
,
11812 yca_id
, got_worktree_get_path_prefix(worktree
),
11813 GOT_ERR_REBASE_PATH
, repo
);
11818 got_object_commit_close(commit
);
11821 if (!continue_rebase
) {
11822 error
= got_worktree_rebase_prepare(&new_base_branch
,
11823 &tmp_branch
, &fileindex
, worktree
, branch
, repo
);
11828 if (STAILQ_EMPTY(&commits
)) {
11829 if (continue_rebase
) {
11830 error
= rebase_complete(worktree
, fileindex
,
11831 branch
, tmp_branch
, repo
, create_backup
);
11834 /* Fast-forward the reference of the branch. */
11835 struct got_object_id
*new_head_commit_id
;
11837 error
= got_ref_resolve(&new_head_commit_id
, repo
,
11841 error
= got_object_id_str(&id_str
, new_head_commit_id
);
11844 printf("Forwarding %s to commit %s\n",
11845 got_ref_get_name(branch
), id_str
);
11847 error
= got_ref_change_ref(branch
,
11848 new_head_commit_id
);
11851 /* No backup needed since objects did not change. */
11857 STAILQ_FOREACH(qid
, &commits
, entry
) {
11859 commit_id
= &qid
->id
;
11860 parent_id
= pid
? &pid
->id
: yca_id
;
11863 memset(&upa
, 0, sizeof(upa
));
11864 error
= got_worktree_rebase_merge_files(&merged_paths
,
11865 worktree
, fileindex
, parent_id
, commit_id
, repo
,
11866 update_progress
, &upa
, check_cancelled
, NULL
);
11870 print_merge_progress_stats(&upa
);
11871 if (upa
.conflicts
> 0 || upa
.missing
> 0 ||
11872 upa
.not_deleted
> 0 || upa
.unversioned
> 0) {
11873 if (upa
.conflicts
> 0) {
11874 error
= show_rebase_merge_conflict(&qid
->id
,
11879 got_pathlist_free(&merged_paths
, GOT_PATHLIST_FREE_PATH
);
11883 error
= rebase_commit(&merged_paths
, worktree
, fileindex
,
11884 tmp_branch
, committer
, commit_id
, 0, repo
);
11885 got_pathlist_free(&merged_paths
, GOT_PATHLIST_FREE_PATH
);
11890 if (upa
.conflicts
> 0 || upa
.missing
> 0 ||
11891 upa
.not_deleted
> 0 || upa
.unversioned
> 0) {
11892 error
= got_worktree_rebase_postpone(worktree
, fileindex
);
11895 if (upa
.conflicts
> 0 && upa
.missing
== 0 &&
11896 upa
.not_deleted
== 0 && upa
.unversioned
== 0) {
11897 error
= got_error_msg(GOT_ERR_CONFLICTS
,
11898 "conflicts must be resolved before rebasing "
11900 } else if (upa
.conflicts
> 0) {
11901 error
= got_error_msg(GOT_ERR_CONFLICTS
,
11902 "conflicts must be resolved before rebasing "
11903 "can continue; changes destined for some "
11904 "files were not yet merged and should be "
11905 "merged manually if required before the "
11906 "rebase operation is continued");
11908 error
= got_error_msg(GOT_ERR_CONFLICTS
,
11909 "changes destined for some files were not "
11910 "yet merged and should be merged manually "
11911 "if required before the rebase operation "
11915 error
= rebase_complete(worktree
, fileindex
, branch
,
11916 tmp_branch
, repo
, create_backup
);
11920 free(gitconfig_path
);
11921 got_object_id_queue_free(&commits
);
11922 free(branch_head_commit_id
);
11923 free(resume_commit_id
);
11924 free(head_commit_id
);
11927 got_object_commit_close(commit
);
11929 got_ref_close(branch
);
11930 if (new_base_branch
)
11931 got_ref_close(new_base_branch
);
11933 got_ref_close(tmp_branch
);
11935 got_ref_close(head_ref
);
11937 got_worktree_close(worktree
);
11939 const struct got_error
*close_err
= got_repo_close(repo
);
11944 const struct got_error
*pack_err
=
11945 got_repo_pack_fds_close(pack_fds
);
11953 usage_histedit(void)
11955 fprintf(stderr
, "usage: %s histedit [-aCcdeflmX] [-F histedit-script] "
11956 "[branch]\n", getprogname());
11960 #define GOT_HISTEDIT_PICK 'p'
11961 #define GOT_HISTEDIT_EDIT 'e'
11962 #define GOT_HISTEDIT_FOLD 'f'
11963 #define GOT_HISTEDIT_DROP 'd'
11964 #define GOT_HISTEDIT_MESG 'm'
11966 static const struct got_histedit_cmd
{
11967 unsigned char code
;
11970 } got_histedit_cmds
[] = {
11971 { GOT_HISTEDIT_PICK
, "pick", "use commit" },
11972 { GOT_HISTEDIT_EDIT
, "edit", "use commit but stop for amending" },
11973 { GOT_HISTEDIT_FOLD
, "fold", "combine with next commit that will "
11975 { GOT_HISTEDIT_DROP
, "drop", "remove commit from history" },
11976 { GOT_HISTEDIT_MESG
, "mesg", "open editor to edit the log message" },
11979 struct got_histedit_list_entry
{
11980 TAILQ_ENTRY(got_histedit_list_entry
) entry
;
11981 struct got_object_id
*commit_id
;
11982 const struct got_histedit_cmd
*cmd
;
11985 TAILQ_HEAD(got_histedit_list
, got_histedit_list_entry
);
11987 static const struct got_error
*
11988 histedit_write_commit(struct got_object_id
*commit_id
, const char *cmdname
,
11989 FILE *f
, struct got_repository
*repo
)
11991 const struct got_error
*err
= NULL
;
11992 char *logmsg
= NULL
, *id_str
= NULL
;
11993 struct got_commit_object
*commit
= NULL
;
11996 err
= got_object_open_as_commit(&commit
, repo
, commit_id
);
12000 err
= get_short_logmsg(&logmsg
, 34, commit
);
12004 err
= got_object_id_str(&id_str
, commit_id
);
12008 n
= fprintf(f
, "%s %s %s\n", cmdname
, id_str
, logmsg
);
12010 err
= got_ferror(f
, GOT_ERR_IO
);
12013 got_object_commit_close(commit
);
12019 static const struct got_error
*
12020 histedit_write_commit_list(struct got_object_id_queue
*commits
,
12021 FILE *f
, int edit_logmsg_only
, int fold_only
, int drop_only
,
12022 int edit_only
, struct got_repository
*repo
)
12024 const struct got_error
*err
= NULL
;
12025 struct got_object_qid
*qid
;
12026 const char *histedit_cmd
= NULL
;
12028 if (STAILQ_EMPTY(commits
))
12029 return got_error(GOT_ERR_EMPTY_HISTEDIT
);
12031 STAILQ_FOREACH(qid
, commits
, entry
) {
12032 histedit_cmd
= got_histedit_cmds
[0].name
;
12034 histedit_cmd
= "drop";
12035 else if (edit_only
)
12036 histedit_cmd
= "edit";
12037 else if (fold_only
&& STAILQ_NEXT(qid
, entry
) != NULL
)
12038 histedit_cmd
= "fold";
12039 else if (edit_logmsg_only
)
12040 histedit_cmd
= "mesg";
12041 err
= histedit_write_commit(&qid
->id
, histedit_cmd
, f
, repo
);
12049 static const struct got_error
*
12050 write_cmd_list(FILE *f
, const char *branch_name
,
12051 struct got_object_id_queue
*commits
)
12053 const struct got_error
*err
= NULL
;
12057 struct got_object_qid
*qid
;
12059 qid
= STAILQ_FIRST(commits
);
12060 err
= got_object_id_str(&id_str
, &qid
->id
);
12065 "# Editing the history of branch '%s' starting at\n"
12067 "# Commits will be processed in order from top to "
12068 "bottom of this file.\n", branch_name
, id_str
);
12070 err
= got_ferror(f
, GOT_ERR_IO
);
12074 n
= fprintf(f
, "# Available histedit commands:\n");
12076 err
= got_ferror(f
, GOT_ERR_IO
);
12080 for (i
= 0; i
< nitems(got_histedit_cmds
); i
++) {
12081 const struct got_histedit_cmd
*cmd
= &got_histedit_cmds
[i
];
12082 n
= fprintf(f
, "# %s (%c): %s\n", cmd
->name
, cmd
->code
,
12085 err
= got_ferror(f
, GOT_ERR_IO
);
12094 static const struct got_error
*
12095 histedit_syntax_error(int lineno
)
12097 static char msg
[42];
12100 ret
= snprintf(msg
, sizeof(msg
), "histedit syntax error on line %d",
12102 if (ret
< 0 || (size_t)ret
>= sizeof(msg
))
12103 return got_error(GOT_ERR_HISTEDIT_SYNTAX
);
12105 return got_error_msg(GOT_ERR_HISTEDIT_SYNTAX
, msg
);
12108 static const struct got_error
*
12109 append_folded_commit_msg(char **new_msg
, struct got_histedit_list_entry
*hle
,
12110 char *logmsg
, struct got_repository
*repo
)
12112 const struct got_error
*err
;
12113 struct got_commit_object
*folded_commit
= NULL
;
12114 char *id_str
, *folded_logmsg
= NULL
;
12116 err
= got_object_id_str(&id_str
, hle
->commit_id
);
12120 err
= got_object_open_as_commit(&folded_commit
, repo
, hle
->commit_id
);
12124 err
= got_object_commit_get_logmsg(&folded_logmsg
, folded_commit
);
12127 if (asprintf(new_msg
, "%s%s# log message of folded commit %s: %s",
12128 logmsg
? logmsg
: "", logmsg
? "\n" : "", id_str
,
12129 folded_logmsg
) == -1) {
12130 err
= got_error_from_errno("asprintf");
12134 got_object_commit_close(folded_commit
);
12136 free(folded_logmsg
);
12140 static struct got_histedit_list_entry
*
12141 get_folded_commits(struct got_histedit_list_entry
*hle
)
12143 struct got_histedit_list_entry
*prev
, *folded
= NULL
;
12145 prev
= TAILQ_PREV(hle
, got_histedit_list
, entry
);
12146 while (prev
&& (prev
->cmd
->code
== GOT_HISTEDIT_FOLD
||
12147 prev
->cmd
->code
== GOT_HISTEDIT_DROP
)) {
12148 if (prev
->cmd
->code
== GOT_HISTEDIT_FOLD
)
12150 prev
= TAILQ_PREV(prev
, got_histedit_list
, entry
);
12156 static const struct got_error
*
12157 histedit_edit_logmsg(struct got_histedit_list_entry
*hle
,
12158 const char *editor
, struct got_repository
*repo
)
12160 char *logmsg_path
= NULL
, *id_str
= NULL
, *orig_logmsg
= NULL
;
12161 char *logmsg
= NULL
, *new_msg
= NULL
;
12162 const struct got_error
*err
= NULL
;
12163 struct got_commit_object
*commit
= NULL
;
12166 struct got_histedit_list_entry
*folded
= NULL
;
12168 err
= got_object_open_as_commit(&commit
, repo
, hle
->commit_id
);
12172 folded
= get_folded_commits(hle
);
12174 while (folded
!= hle
) {
12175 if (folded
->cmd
->code
== GOT_HISTEDIT_DROP
) {
12176 folded
= TAILQ_NEXT(folded
, entry
);
12179 err
= append_folded_commit_msg(&new_msg
, folded
,
12185 folded
= TAILQ_NEXT(folded
, entry
);
12189 err
= got_object_id_str(&id_str
, hle
->commit_id
);
12192 err
= got_object_commit_get_logmsg(&orig_logmsg
, commit
);
12195 logmsg_len
= asprintf(&new_msg
,
12196 "%s\n# original log message of commit %s: %s",
12197 logmsg
? logmsg
: "", id_str
, orig_logmsg
);
12198 if (logmsg_len
== -1) {
12199 err
= got_error_from_errno("asprintf");
12205 err
= got_object_id_str(&id_str
, hle
->commit_id
);
12209 err
= got_opentemp_named_fd(&logmsg_path
, &fd
,
12210 GOT_TMPDIR_STR
"/got-logmsg", "");
12214 if (write(fd
, logmsg
, logmsg_len
) == -1) {
12215 err
= got_error_from_errno2("write", logmsg_path
);
12218 if (close(fd
) == -1) {
12219 err
= got_error_from_errno2("close", logmsg_path
);
12224 err
= edit_logmsg(&hle
->logmsg
, editor
, logmsg_path
, logmsg
,
12227 if (err
->code
!= GOT_ERR_COMMIT_MSG_EMPTY
)
12230 hle
->logmsg
= strdup(new_msg
);
12231 if (hle
->logmsg
== NULL
)
12232 err
= got_error_from_errno("strdup");
12235 if (fd
!= -1 && close(fd
) == -1 && err
== NULL
)
12236 err
= got_error_from_errno2("close", logmsg_path
);
12237 if (logmsg_path
&& unlink(logmsg_path
) != 0 && err
== NULL
)
12238 err
= got_error_from_errno2("unlink", logmsg_path
);
12243 got_object_commit_close(commit
);
12247 static const struct got_error
*
12248 histedit_parse_list(struct got_histedit_list
*histedit_cmds
,
12249 FILE *f
, struct got_repository
*repo
)
12251 const struct got_error
*err
= NULL
;
12252 char *line
= NULL
, *p
, *end
;
12253 size_t i
, linesize
= 0;
12256 const struct got_histedit_cmd
*cmd
;
12257 struct got_object_id
*commit_id
= NULL
;
12258 struct got_histedit_list_entry
*hle
= NULL
;
12261 linelen
= getline(&line
, &linesize
, f
);
12262 if (linelen
== -1) {
12263 const struct got_error
*getline_err
;
12266 getline_err
= got_error_from_errno("getline");
12267 err
= got_ferror(f
, getline_err
->code
);
12272 while (isspace((unsigned char)p
[0]))
12274 if (p
[0] == '#' || p
[0] == '\0')
12277 for (i
= 0; i
< nitems(got_histedit_cmds
); i
++) {
12278 cmd
= &got_histedit_cmds
[i
];
12279 if (strncmp(cmd
->name
, p
, strlen(cmd
->name
)) == 0 &&
12280 isspace((unsigned char)p
[strlen(cmd
->name
)])) {
12281 p
+= strlen(cmd
->name
);
12284 if (p
[0] == cmd
->code
&& isspace((unsigned char)p
[1])) {
12289 if (i
== nitems(got_histedit_cmds
)) {
12290 err
= histedit_syntax_error(lineno
);
12293 while (isspace((unsigned char)p
[0]))
12296 while (end
[0] && !isspace((unsigned char)end
[0]))
12299 err
= got_object_resolve_id_str(&commit_id
, repo
, p
);
12301 /* override error code */
12302 err
= histedit_syntax_error(lineno
);
12305 hle
= malloc(sizeof(*hle
));
12307 err
= got_error_from_errno("malloc");
12311 hle
->commit_id
= commit_id
;
12312 hle
->logmsg
= NULL
;
12314 TAILQ_INSERT_TAIL(histedit_cmds
, hle
, entry
);
12322 static const struct got_error
*
12323 histedit_check_script(struct got_histedit_list
*histedit_cmds
,
12324 struct got_object_id_queue
*commits
, struct got_repository
*repo
)
12326 const struct got_error
*err
= NULL
;
12327 struct got_object_qid
*qid
;
12328 struct got_histedit_list_entry
*hle
;
12329 static char msg
[128];
12332 if (TAILQ_EMPTY(histedit_cmds
))
12333 return got_error_msg(GOT_ERR_EMPTY_HISTEDIT
,
12334 "histedit script contains no commands");
12335 if (STAILQ_EMPTY(commits
))
12336 return got_error(GOT_ERR_EMPTY_HISTEDIT
);
12338 TAILQ_FOREACH(hle
, histedit_cmds
, entry
) {
12339 struct got_histedit_list_entry
*hle2
;
12340 TAILQ_FOREACH(hle2
, histedit_cmds
, entry
) {
12343 if (got_object_id_cmp(hle
->commit_id
,
12344 hle2
->commit_id
) != 0)
12346 err
= got_object_id_str(&id_str
, hle
->commit_id
);
12349 snprintf(msg
, sizeof(msg
), "commit %s is listed "
12350 "more than once in histedit script", id_str
);
12352 return got_error_msg(GOT_ERR_HISTEDIT_CMD
, msg
);
12356 STAILQ_FOREACH(qid
, commits
, entry
) {
12357 TAILQ_FOREACH(hle
, histedit_cmds
, entry
) {
12358 if (got_object_id_cmp(&qid
->id
, hle
->commit_id
) == 0)
12362 err
= got_object_id_str(&id_str
, &qid
->id
);
12365 snprintf(msg
, sizeof(msg
),
12366 "commit %s missing from histedit script", id_str
);
12368 return got_error_msg(GOT_ERR_HISTEDIT_CMD
, msg
);
12372 hle
= TAILQ_LAST(histedit_cmds
, got_histedit_list
);
12373 if (hle
&& hle
->cmd
->code
== GOT_HISTEDIT_FOLD
)
12374 return got_error_msg(GOT_ERR_HISTEDIT_CMD
,
12375 "last commit in histedit script cannot be folded");
12380 static const struct got_error
*
12381 histedit_run_editor(struct got_histedit_list
*histedit_cmds
,
12382 const char *editor
, const char *path
,
12383 struct got_object_id_queue
*commits
, struct got_repository
*repo
)
12385 const struct got_error
*err
= NULL
;
12386 struct stat st
, st2
;
12387 struct timespec timeout
;
12390 if (stat(path
, &st
) == -1) {
12391 err
= got_error_from_errno2("stat", path
);
12395 if (spawn_editor(editor
, path
) == -1) {
12396 err
= got_error_from_errno("failed spawning editor");
12400 timeout
.tv_sec
= 0;
12401 timeout
.tv_nsec
= 1;
12402 nanosleep(&timeout
, NULL
);
12404 if (stat(path
, &st2
) == -1) {
12405 err
= got_error_from_errno2("stat", path
);
12409 if (st
.st_size
== st2
.st_size
&&
12410 timespeccmp(&st
.st_mtim
, &st2
.st_mtim
, ==)) {
12411 err
= got_error_msg(GOT_ERR_EMPTY_HISTEDIT
,
12412 "no changes made to histedit script, aborting");
12416 f
= fopen(path
, "re");
12418 err
= got_error_from_errno("fopen");
12421 err
= histedit_parse_list(histedit_cmds
, f
, repo
);
12425 err
= histedit_check_script(histedit_cmds
, commits
, repo
);
12427 if (f
&& fclose(f
) == EOF
&& err
== NULL
)
12428 err
= got_error_from_errno("fclose");
12432 static const struct got_error
*
12433 histedit_edit_list_retry(struct got_histedit_list
*, const struct got_error
*,
12434 struct got_object_id_queue
*, const char *, const char *, const char *,
12435 struct got_repository
*);
12437 static const struct got_error
*
12438 histedit_edit_script(struct got_histedit_list
*histedit_cmds
,
12439 struct got_object_id_queue
*commits
, const char *branch_name
,
12440 int edit_logmsg_only
, int fold_only
, int drop_only
, int edit_only
,
12441 const char *editor
, struct got_repository
*repo
)
12443 const struct got_error
*err
;
12447 err
= got_opentemp_named(&path
, &f
, "got-histedit", "");
12451 err
= write_cmd_list(f
, branch_name
, commits
);
12455 err
= histedit_write_commit_list(commits
, f
, edit_logmsg_only
,
12456 fold_only
, drop_only
, edit_only
, repo
);
12460 if (drop_only
|| edit_logmsg_only
|| fold_only
|| edit_only
) {
12462 err
= histedit_parse_list(histedit_cmds
, f
, repo
);
12464 if (fclose(f
) == EOF
) {
12465 err
= got_error_from_errno("fclose");
12469 err
= histedit_run_editor(histedit_cmds
, editor
, path
,
12472 if (err
->code
!= GOT_ERR_HISTEDIT_SYNTAX
&&
12473 err
->code
!= GOT_ERR_HISTEDIT_CMD
)
12475 err
= histedit_edit_list_retry(histedit_cmds
, err
,
12476 commits
, editor
, path
, branch_name
, repo
);
12480 if (f
&& fclose(f
) == EOF
&& err
== NULL
)
12481 err
= got_error_from_errno("fclose");
12482 if (path
&& unlink(path
) != 0 && err
== NULL
)
12483 err
= got_error_from_errno2("unlink", path
);
12488 static const struct got_error
*
12489 histedit_save_list(struct got_histedit_list
*histedit_cmds
,
12490 struct got_worktree
*worktree
, struct got_repository
*repo
)
12492 const struct got_error
*err
= NULL
;
12495 struct got_histedit_list_entry
*hle
;
12497 err
= got_worktree_get_histedit_script_path(&path
, worktree
);
12501 f
= fopen(path
, "we");
12503 err
= got_error_from_errno2("fopen", path
);
12506 TAILQ_FOREACH(hle
, histedit_cmds
, entry
) {
12507 err
= histedit_write_commit(hle
->commit_id
, hle
->cmd
->name
, f
,
12513 if (f
&& fclose(f
) == EOF
&& err
== NULL
)
12514 err
= got_error_from_errno("fclose");
12520 histedit_free_list(struct got_histedit_list
*histedit_cmds
)
12522 struct got_histedit_list_entry
*hle
;
12524 while ((hle
= TAILQ_FIRST(histedit_cmds
))) {
12525 TAILQ_REMOVE(histedit_cmds
, hle
, entry
);
12530 static const struct got_error
*
12531 histedit_load_list(struct got_histedit_list
*histedit_cmds
,
12532 const char *path
, struct got_repository
*repo
)
12534 const struct got_error
*err
= NULL
;
12537 f
= fopen(path
, "re");
12539 err
= got_error_from_errno2("fopen", path
);
12543 err
= histedit_parse_list(histedit_cmds
, f
, repo
);
12545 if (f
&& fclose(f
) == EOF
&& err
== NULL
)
12546 err
= got_error_from_errno("fclose");
12550 static const struct got_error
*
12551 histedit_edit_list_retry(struct got_histedit_list
*histedit_cmds
,
12552 const struct got_error
*edit_err
, struct got_object_id_queue
*commits
,
12553 const char *editor
, const char *path
, const char *branch_name
,
12554 struct got_repository
*repo
)
12556 const struct got_error
*err
= NULL
, *prev_err
= edit_err
;
12559 while (resp
!= 'c' && resp
!= 'r' && resp
!= 'a') {
12560 printf("%s: %s\n(c)ontinue editing, (r)estart editing, "
12561 "or (a)bort: ", getprogname(), prev_err
->msg
);
12566 histedit_free_list(histedit_cmds
);
12567 err
= histedit_run_editor(histedit_cmds
, editor
, path
,
12570 if (err
->code
!= GOT_ERR_HISTEDIT_SYNTAX
&&
12571 err
->code
!= GOT_ERR_HISTEDIT_CMD
)
12578 } else if (resp
== 'r') {
12579 histedit_free_list(histedit_cmds
);
12580 err
= histedit_edit_script(histedit_cmds
,
12581 commits
, branch_name
, 0, 0, 0, 0, editor
, repo
);
12583 if (err
->code
!= GOT_ERR_HISTEDIT_SYNTAX
&&
12584 err
->code
!= GOT_ERR_HISTEDIT_CMD
)
12591 } else if (resp
== 'a') {
12592 err
= got_error(GOT_ERR_HISTEDIT_CANCEL
);
12595 printf("invalid response '%c'\n", resp
);
12601 static const struct got_error
*
12602 histedit_complete(struct got_worktree
*worktree
,
12603 struct got_fileindex
*fileindex
, struct got_reference
*tmp_branch
,
12604 struct got_reference
*branch
, struct got_repository
*repo
)
12606 printf("Switching work tree to %s\n",
12607 got_ref_get_symref_target(branch
));
12608 return got_worktree_histedit_complete(worktree
, fileindex
, tmp_branch
,
12612 static const struct got_error
*
12613 show_histedit_progress(struct got_commit_object
*commit
,
12614 struct got_histedit_list_entry
*hle
, struct got_object_id
*new_id
)
12616 const struct got_error
*err
;
12617 char *old_id_str
= NULL
, *new_id_str
= NULL
, *logmsg
= NULL
;
12619 err
= got_object_id_str(&old_id_str
, hle
->commit_id
);
12624 err
= got_object_id_str(&new_id_str
, new_id
);
12629 old_id_str
[12] = '\0';
12631 new_id_str
[12] = '\0';
12634 logmsg
= strdup(hle
->logmsg
);
12635 if (logmsg
== NULL
) {
12636 err
= got_error_from_errno("strdup");
12639 trim_logmsg(logmsg
, 42);
12641 err
= get_short_logmsg(&logmsg
, 42, commit
);
12646 switch (hle
->cmd
->code
) {
12647 case GOT_HISTEDIT_PICK
:
12648 case GOT_HISTEDIT_EDIT
:
12649 case GOT_HISTEDIT_MESG
:
12650 printf("%s -> %s: %s\n", old_id_str
,
12651 new_id_str
? new_id_str
: "no-op change", logmsg
);
12653 case GOT_HISTEDIT_DROP
:
12654 case GOT_HISTEDIT_FOLD
:
12655 printf("%s -> %s commit: %s\n", old_id_str
, hle
->cmd
->name
,
12667 static const struct got_error
*
12668 histedit_commit(struct got_pathlist_head
*merged_paths
,
12669 struct got_worktree
*worktree
, struct got_fileindex
*fileindex
,
12670 struct got_reference
*tmp_branch
, struct got_histedit_list_entry
*hle
,
12671 const char *committer
, int allow_conflict
, const char *editor
,
12672 struct got_repository
*repo
)
12674 const struct got_error
*err
;
12675 struct got_commit_object
*commit
;
12676 struct got_object_id
*new_commit_id
;
12678 if ((hle
->cmd
->code
== GOT_HISTEDIT_EDIT
|| get_folded_commits(hle
))
12679 && hle
->logmsg
== NULL
) {
12680 err
= histedit_edit_logmsg(hle
, editor
, repo
);
12685 err
= got_object_open_as_commit(&commit
, repo
, hle
->commit_id
);
12689 err
= got_worktree_histedit_commit(&new_commit_id
, merged_paths
,
12690 worktree
, fileindex
, tmp_branch
, committer
, commit
, hle
->commit_id
,
12691 hle
->logmsg
, allow_conflict
, repo
);
12693 if (err
->code
!= GOT_ERR_COMMIT_NO_CHANGES
)
12695 err
= show_histedit_progress(commit
, hle
, NULL
);
12697 err
= show_histedit_progress(commit
, hle
, new_commit_id
);
12698 free(new_commit_id
);
12701 got_object_commit_close(commit
);
12705 static const struct got_error
*
12706 histedit_skip_commit(struct got_histedit_list_entry
*hle
,
12707 struct got_worktree
*worktree
, struct got_repository
*repo
)
12709 const struct got_error
*error
;
12710 struct got_commit_object
*commit
;
12712 error
= got_worktree_histedit_skip_commit(worktree
, hle
->commit_id
,
12717 error
= got_object_open_as_commit(&commit
, repo
, hle
->commit_id
);
12721 error
= show_histedit_progress(commit
, hle
, NULL
);
12722 got_object_commit_close(commit
);
12726 static const struct got_error
*
12727 check_local_changes(void *arg
, unsigned char status
,
12728 unsigned char staged_status
, const char *path
,
12729 struct got_object_id
*blob_id
, struct got_object_id
*staged_blob_id
,
12730 struct got_object_id
*commit_id
, int dirfd
, const char *de_name
)
12732 int *have_local_changes
= arg
;
12735 case GOT_STATUS_ADD
:
12736 case GOT_STATUS_DELETE
:
12737 case GOT_STATUS_MODIFY
:
12738 case GOT_STATUS_CONFLICT
:
12739 *have_local_changes
= 1;
12740 return got_error(GOT_ERR_CANCELLED
);
12745 switch (staged_status
) {
12746 case GOT_STATUS_ADD
:
12747 case GOT_STATUS_DELETE
:
12748 case GOT_STATUS_MODIFY
:
12749 *have_local_changes
= 1;
12750 return got_error(GOT_ERR_CANCELLED
);
12758 static const struct got_error
*
12759 cmd_histedit(int argc
, char *argv
[])
12761 const struct got_error
*error
= NULL
;
12762 struct got_worktree
*worktree
= NULL
;
12763 struct got_fileindex
*fileindex
= NULL
;
12764 struct got_repository
*repo
= NULL
;
12765 char *cwd
= NULL
, *committer
= NULL
, *gitconfig_path
= NULL
;
12766 struct got_reference
*branch
= NULL
;
12767 struct got_reference
*tmp_branch
= NULL
;
12768 struct got_object_id
*resume_commit_id
= NULL
;
12769 struct got_object_id
*base_commit_id
= NULL
;
12770 struct got_object_id
*head_commit_id
= NULL
;
12771 struct got_commit_object
*commit
= NULL
;
12772 int ch
, rebase_in_progress
= 0, merge_in_progress
= 0;
12773 struct got_update_progress_arg upa
;
12774 int edit_in_progress
= 0, abort_edit
= 0, continue_edit
= 0;
12775 int drop_only
= 0, edit_logmsg_only
= 0, fold_only
= 0, edit_only
= 0;
12776 int allow_conflict
= 0, list_backups
= 0, delete_backups
= 0;
12777 const char *edit_script_path
= NULL
;
12778 char *editor
= NULL
;
12779 struct got_object_id_queue commits
;
12780 struct got_pathlist_head merged_paths
;
12781 const struct got_object_id_queue
*parent_ids
;
12782 struct got_object_qid
*pid
;
12783 struct got_histedit_list histedit_cmds
;
12784 struct got_histedit_list_entry
*hle
;
12785 int *pack_fds
= NULL
;
12787 STAILQ_INIT(&commits
);
12788 TAILQ_INIT(&histedit_cmds
);
12789 TAILQ_INIT(&merged_paths
);
12790 memset(&upa
, 0, sizeof(upa
));
12793 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
12794 "unveil", NULL
) == -1)
12798 while ((ch
= getopt(argc
, argv
, "aCcdeF:flmX")) != -1) {
12804 allow_conflict
= 1;
12816 edit_script_path
= optarg
;
12825 edit_logmsg_only
= 1;
12828 delete_backups
= 1;
12839 if (abort_edit
&& allow_conflict
)
12840 option_conflict('a', 'C');
12841 if (abort_edit
&& continue_edit
)
12842 option_conflict('a', 'c');
12843 if (edit_script_path
&& allow_conflict
)
12844 option_conflict('F', 'C');
12845 if (edit_script_path
&& edit_logmsg_only
)
12846 option_conflict('F', 'm');
12847 if (abort_edit
&& edit_logmsg_only
)
12848 option_conflict('a', 'm');
12849 if (edit_logmsg_only
&& allow_conflict
)
12850 option_conflict('m', 'C');
12851 if (continue_edit
&& edit_logmsg_only
)
12852 option_conflict('c', 'm');
12853 if (abort_edit
&& fold_only
)
12854 option_conflict('a', 'f');
12855 if (fold_only
&& allow_conflict
)
12856 option_conflict('f', 'C');
12857 if (continue_edit
&& fold_only
)
12858 option_conflict('c', 'f');
12859 if (fold_only
&& edit_logmsg_only
)
12860 option_conflict('f', 'm');
12861 if (edit_script_path
&& fold_only
)
12862 option_conflict('F', 'f');
12863 if (abort_edit
&& edit_only
)
12864 option_conflict('a', 'e');
12865 if (continue_edit
&& edit_only
)
12866 option_conflict('c', 'e');
12867 if (edit_only
&& edit_logmsg_only
)
12868 option_conflict('e', 'm');
12869 if (edit_script_path
&& edit_only
)
12870 option_conflict('F', 'e');
12871 if (fold_only
&& edit_only
)
12872 option_conflict('f', 'e');
12873 if (drop_only
&& abort_edit
)
12874 option_conflict('d', 'a');
12875 if (drop_only
&& allow_conflict
)
12876 option_conflict('d', 'C');
12877 if (drop_only
&& continue_edit
)
12878 option_conflict('d', 'c');
12879 if (drop_only
&& edit_logmsg_only
)
12880 option_conflict('d', 'm');
12881 if (drop_only
&& edit_only
)
12882 option_conflict('d', 'e');
12883 if (drop_only
&& edit_script_path
)
12884 option_conflict('d', 'F');
12885 if (drop_only
&& fold_only
)
12886 option_conflict('d', 'f');
12887 if (list_backups
) {
12889 option_conflict('l', 'a');
12890 if (allow_conflict
)
12891 option_conflict('l', 'C');
12893 option_conflict('l', 'c');
12894 if (edit_script_path
)
12895 option_conflict('l', 'F');
12896 if (edit_logmsg_only
)
12897 option_conflict('l', 'm');
12899 option_conflict('l', 'd');
12901 option_conflict('l', 'f');
12903 option_conflict('l', 'e');
12904 if (delete_backups
)
12905 option_conflict('l', 'X');
12906 if (argc
!= 0 && argc
!= 1)
12908 } else if (delete_backups
) {
12910 option_conflict('X', 'a');
12911 if (allow_conflict
)
12912 option_conflict('X', 'C');
12914 option_conflict('X', 'c');
12916 option_conflict('X', 'd');
12917 if (edit_script_path
)
12918 option_conflict('X', 'F');
12919 if (edit_logmsg_only
)
12920 option_conflict('X', 'm');
12922 option_conflict('X', 'f');
12924 option_conflict('X', 'e');
12926 option_conflict('X', 'l');
12927 if (argc
!= 0 && argc
!= 1)
12929 } else if (allow_conflict
&& !continue_edit
)
12930 errx(1, "-C option requires -c");
12931 else if (argc
!= 0)
12934 cwd
= getcwd(NULL
, 0);
12936 error
= got_error_from_errno("getcwd");
12940 error
= got_repo_pack_fds_open(&pack_fds
);
12944 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_GOT_DIR
);
12946 if (list_backups
|| delete_backups
) {
12947 if (error
->code
!= GOT_ERR_NOT_WORKTREE
)
12950 if (error
->code
== GOT_ERR_NOT_WORKTREE
)
12951 error
= wrap_not_worktree_error(error
,
12957 if (list_backups
|| delete_backups
) {
12958 error
= got_repo_open(&repo
,
12959 worktree
? got_worktree_get_repo_path(worktree
) : cwd
,
12963 error
= apply_unveil(got_repo_get_path(repo
), 0,
12964 worktree
? got_worktree_get_root_path(worktree
) : NULL
);
12967 error
= process_backup_refs(
12968 GOT_WORKTREE_HISTEDIT_BACKUP_REF_PREFIX
,
12969 argc
== 1 ? argv
[0] : NULL
, delete_backups
, repo
);
12970 goto done
; /* nothing else to do */
12972 error
= get_gitconfig_path(&gitconfig_path
);
12975 error
= got_repo_open(&repo
,
12976 got_worktree_get_repo_path(worktree
), gitconfig_path
,
12980 error
= get_editor(&editor
);
12983 if (unveil(editor
, "x") != 0) {
12984 error
= got_error_from_errno2("unveil", editor
);
12987 if (edit_script_path
) {
12988 if (unveil(edit_script_path
, "r") != 0) {
12989 error
= got_error_from_errno2("unveil",
12994 error
= apply_unveil(got_repo_get_path(repo
), 0,
12995 got_worktree_get_root_path(worktree
));
13000 if (worktree
!= NULL
&& !list_backups
&& !delete_backups
) {
13001 error
= worktree_has_logmsg_ref("histedit", worktree
, repo
);
13006 error
= got_worktree_rebase_in_progress(&rebase_in_progress
, worktree
);
13009 if (rebase_in_progress
) {
13010 error
= got_error(GOT_ERR_REBASING
);
13014 error
= got_worktree_merge_in_progress(&merge_in_progress
, worktree
,
13018 if (merge_in_progress
) {
13019 error
= got_error(GOT_ERR_MERGE_BUSY
);
13023 error
= got_worktree_histedit_in_progress(&edit_in_progress
, worktree
);
13027 if (edit_in_progress
&& edit_logmsg_only
) {
13028 error
= got_error_msg(GOT_ERR_HISTEDIT_BUSY
,
13029 "histedit operation is in progress in this "
13030 "work tree and must be continued or aborted "
13031 "before the -m option can be used");
13034 if (edit_in_progress
&& drop_only
) {
13035 error
= got_error_msg(GOT_ERR_HISTEDIT_BUSY
,
13036 "histedit operation is in progress in this "
13037 "work tree and must be continued or aborted "
13038 "before the -d option can be used");
13041 if (edit_in_progress
&& fold_only
) {
13042 error
= got_error_msg(GOT_ERR_HISTEDIT_BUSY
,
13043 "histedit operation is in progress in this "
13044 "work tree and must be continued or aborted "
13045 "before the -f option can be used");
13048 if (edit_in_progress
&& edit_only
) {
13049 error
= got_error_msg(GOT_ERR_HISTEDIT_BUSY
,
13050 "histedit operation is in progress in this "
13051 "work tree and must be continued or aborted "
13052 "before the -e option can be used");
13056 if (edit_in_progress
&& abort_edit
) {
13057 error
= got_worktree_histedit_continue(&resume_commit_id
,
13058 &tmp_branch
, &branch
, &base_commit_id
, &fileindex
,
13062 printf("Switching work tree to %s\n",
13063 got_ref_get_symref_target(branch
));
13064 error
= got_worktree_histedit_abort(worktree
, fileindex
, repo
,
13065 branch
, base_commit_id
, abort_progress
, &upa
);
13068 printf("Histedit of %s aborted\n",
13069 got_ref_get_symref_target(branch
));
13070 print_merge_progress_stats(&upa
);
13071 goto done
; /* nothing else to do */
13072 } else if (abort_edit
) {
13073 error
= got_error(GOT_ERR_NOT_HISTEDIT
);
13077 error
= get_author(&committer
, repo
, worktree
);
13081 if (continue_edit
) {
13084 if (!edit_in_progress
) {
13085 error
= got_error(GOT_ERR_NOT_HISTEDIT
);
13089 error
= got_worktree_get_histedit_script_path(&path
, worktree
);
13093 error
= histedit_load_list(&histedit_cmds
, path
, repo
);
13098 error
= got_worktree_histedit_continue(&resume_commit_id
,
13099 &tmp_branch
, &branch
, &base_commit_id
, &fileindex
,
13104 error
= got_ref_resolve(&head_commit_id
, repo
, branch
);
13108 error
= got_object_open_as_commit(&commit
, repo
,
13112 parent_ids
= got_object_commit_get_parent_ids(commit
);
13113 pid
= STAILQ_FIRST(parent_ids
);
13115 error
= got_error(GOT_ERR_EMPTY_HISTEDIT
);
13118 error
= collect_commits(&commits
, head_commit_id
, &pid
->id
,
13119 base_commit_id
, got_worktree_get_path_prefix(worktree
),
13120 GOT_ERR_HISTEDIT_PATH
, repo
);
13121 got_object_commit_close(commit
);
13126 if (edit_in_progress
) {
13127 error
= got_error(GOT_ERR_HISTEDIT_BUSY
);
13131 error
= got_ref_open(&branch
, repo
,
13132 got_worktree_get_head_ref_name(worktree
), 0);
13136 if (strncmp(got_ref_get_name(branch
), "refs/heads/", 11) != 0) {
13137 error
= got_error_msg(GOT_ERR_COMMIT_BRANCH
,
13138 "will not edit commit history of a branch outside "
13139 "the \"refs/heads/\" reference namespace");
13143 error
= got_ref_resolve(&head_commit_id
, repo
, branch
);
13144 got_ref_close(branch
);
13149 error
= got_object_open_as_commit(&commit
, repo
,
13153 parent_ids
= got_object_commit_get_parent_ids(commit
);
13154 pid
= STAILQ_FIRST(parent_ids
);
13156 error
= got_error(GOT_ERR_EMPTY_HISTEDIT
);
13159 error
= collect_commits(&commits
, head_commit_id
, &pid
->id
,
13160 got_worktree_get_base_commit_id(worktree
),
13161 got_worktree_get_path_prefix(worktree
),
13162 GOT_ERR_HISTEDIT_PATH
, repo
);
13163 got_object_commit_close(commit
);
13168 if (STAILQ_EMPTY(&commits
)) {
13169 error
= got_error(GOT_ERR_EMPTY_HISTEDIT
);
13173 error
= got_worktree_histedit_prepare(&tmp_branch
, &branch
,
13174 &base_commit_id
, &fileindex
, worktree
, repo
);
13178 if (edit_script_path
) {
13179 error
= histedit_load_list(&histedit_cmds
,
13180 edit_script_path
, repo
);
13182 got_worktree_histedit_abort(worktree
, fileindex
,
13183 repo
, branch
, base_commit_id
,
13184 abort_progress
, &upa
);
13185 print_merge_progress_stats(&upa
);
13189 const char *branch_name
;
13190 branch_name
= got_ref_get_symref_target(branch
);
13191 if (strncmp(branch_name
, "refs/heads/", 11) == 0)
13193 error
= histedit_edit_script(&histedit_cmds
, &commits
,
13194 branch_name
, edit_logmsg_only
, fold_only
,
13195 drop_only
, edit_only
, editor
, repo
);
13197 got_worktree_histedit_abort(worktree
, fileindex
,
13198 repo
, branch
, base_commit_id
,
13199 abort_progress
, &upa
);
13200 print_merge_progress_stats(&upa
);
13206 error
= histedit_save_list(&histedit_cmds
, worktree
,
13209 got_worktree_histedit_abort(worktree
, fileindex
,
13210 repo
, branch
, base_commit_id
,
13211 abort_progress
, &upa
);
13212 print_merge_progress_stats(&upa
);
13218 error
= histedit_check_script(&histedit_cmds
, &commits
, repo
);
13222 TAILQ_FOREACH(hle
, &histedit_cmds
, entry
) {
13223 if (resume_commit_id
) {
13224 if (got_object_id_cmp(hle
->commit_id
,
13225 resume_commit_id
) != 0)
13228 resume_commit_id
= NULL
;
13229 if (hle
->cmd
->code
== GOT_HISTEDIT_DROP
||
13230 hle
->cmd
->code
== GOT_HISTEDIT_FOLD
) {
13231 error
= histedit_skip_commit(hle
, worktree
,
13236 struct got_pathlist_head paths
;
13237 int have_changes
= 0;
13239 TAILQ_INIT(&paths
);
13240 error
= got_pathlist_insert(NULL
, &paths
, "", NULL
);
13243 error
= got_worktree_status(worktree
, &paths
,
13244 repo
, 0, check_local_changes
, &have_changes
,
13245 check_cancelled
, NULL
);
13246 got_pathlist_free(&paths
,
13247 GOT_PATHLIST_FREE_NONE
);
13249 if (error
->code
!= GOT_ERR_CANCELLED
)
13251 if (sigint_received
|| sigpipe_received
)
13254 if (have_changes
) {
13255 error
= histedit_commit(NULL
, worktree
,
13256 fileindex
, tmp_branch
, hle
,
13257 committer
, allow_conflict
, editor
,
13262 error
= histedit_skip_commit(hle
,
13271 if (hle
->cmd
->code
== GOT_HISTEDIT_DROP
) {
13272 error
= histedit_skip_commit(hle
, worktree
, repo
);
13277 error
= got_object_open_as_commit(&commit
, repo
,
13281 parent_ids
= got_object_commit_get_parent_ids(commit
);
13282 pid
= STAILQ_FIRST(parent_ids
);
13284 error
= got_worktree_histedit_merge_files(&merged_paths
,
13285 worktree
, fileindex
, &pid
->id
, hle
->commit_id
, repo
,
13286 update_progress
, &upa
, check_cancelled
, NULL
);
13289 got_object_commit_close(commit
);
13292 print_merge_progress_stats(&upa
);
13293 if (upa
.conflicts
> 0 || upa
.missing
> 0 ||
13294 upa
.not_deleted
> 0 || upa
.unversioned
> 0) {
13295 if (upa
.conflicts
> 0) {
13296 error
= show_rebase_merge_conflict(
13297 hle
->commit_id
, repo
);
13301 got_pathlist_free(&merged_paths
, GOT_PATHLIST_FREE_PATH
);
13305 if (hle
->cmd
->code
== GOT_HISTEDIT_EDIT
) {
13307 error
= got_object_id_str(&id_str
, hle
->commit_id
);
13310 printf("Stopping histedit for amending commit %s\n",
13313 got_pathlist_free(&merged_paths
, GOT_PATHLIST_FREE_PATH
);
13314 error
= got_worktree_histedit_postpone(worktree
,
13317 } else if (hle
->cmd
->code
== GOT_HISTEDIT_FOLD
) {
13318 error
= histedit_skip_commit(hle
, worktree
, repo
);
13322 } else if (hle
->cmd
->code
== GOT_HISTEDIT_MESG
) {
13323 error
= histedit_edit_logmsg(hle
, editor
, repo
);
13328 error
= histedit_commit(&merged_paths
, worktree
, fileindex
,
13329 tmp_branch
, hle
, committer
, allow_conflict
, editor
, repo
);
13330 got_pathlist_free(&merged_paths
, GOT_PATHLIST_FREE_PATH
);
13335 if (upa
.conflicts
> 0 || upa
.missing
> 0 ||
13336 upa
.not_deleted
> 0 || upa
.unversioned
> 0) {
13337 error
= got_worktree_histedit_postpone(worktree
, fileindex
);
13340 if (upa
.conflicts
> 0 && upa
.missing
== 0 &&
13341 upa
.not_deleted
== 0 && upa
.unversioned
== 0) {
13342 error
= got_error_msg(GOT_ERR_CONFLICTS
,
13343 "conflicts must be resolved before histedit "
13345 } else if (upa
.conflicts
> 0) {
13346 error
= got_error_msg(GOT_ERR_CONFLICTS
,
13347 "conflicts must be resolved before histedit "
13348 "can continue; changes destined for some "
13349 "files were not yet merged and should be "
13350 "merged manually if required before the "
13351 "histedit operation is continued");
13353 error
= got_error_msg(GOT_ERR_CONFLICTS
,
13354 "changes destined for some files were not "
13355 "yet merged and should be merged manually "
13356 "if required before the histedit operation "
13360 error
= histedit_complete(worktree
, fileindex
, tmp_branch
,
13366 free(gitconfig_path
);
13367 got_object_id_queue_free(&commits
);
13368 histedit_free_list(&histedit_cmds
);
13369 free(head_commit_id
);
13370 free(base_commit_id
);
13371 free(resume_commit_id
);
13373 got_object_commit_close(commit
);
13375 got_ref_close(branch
);
13377 got_ref_close(tmp_branch
);
13379 got_worktree_close(worktree
);
13381 const struct got_error
*close_err
= got_repo_close(repo
);
13386 const struct got_error
*pack_err
=
13387 got_repo_pack_fds_close(pack_fds
);
13395 usage_integrate(void)
13397 fprintf(stderr
, "usage: %s integrate branch\n", getprogname());
13401 static const struct got_error
*
13402 cmd_integrate(int argc
, char *argv
[])
13404 const struct got_error
*error
= NULL
;
13405 struct got_repository
*repo
= NULL
;
13406 struct got_worktree
*worktree
= NULL
;
13407 char *cwd
= NULL
, *refname
= NULL
, *base_refname
= NULL
;
13408 const char *branch_arg
= NULL
;
13409 struct got_reference
*branch_ref
= NULL
, *base_branch_ref
= NULL
;
13410 struct got_fileindex
*fileindex
= NULL
;
13411 struct got_object_id
*commit_id
= NULL
, *base_commit_id
= NULL
;
13413 struct got_update_progress_arg upa
;
13414 int *pack_fds
= NULL
;
13417 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
13418 "unveil", NULL
) == -1)
13422 while ((ch
= getopt(argc
, argv
, "")) != -1) {
13435 branch_arg
= argv
[0];
13437 cwd
= getcwd(NULL
, 0);
13439 error
= got_error_from_errno("getcwd");
13443 error
= got_repo_pack_fds_open(&pack_fds
);
13447 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_GOT_DIR
);
13449 if (error
->code
== GOT_ERR_NOT_WORKTREE
)
13450 error
= wrap_not_worktree_error(error
, "integrate",
13455 error
= check_rebase_or_histedit_in_progress(worktree
);
13459 error
= got_repo_open(&repo
, got_worktree_get_repo_path(worktree
),
13464 error
= apply_unveil(got_repo_get_path(repo
), 0,
13465 got_worktree_get_root_path(worktree
));
13469 error
= check_merge_in_progress(worktree
, repo
);
13473 if (asprintf(&refname
, "refs/heads/%s", branch_arg
) == -1) {
13474 error
= got_error_from_errno("asprintf");
13478 error
= got_worktree_integrate_prepare(&fileindex
, &branch_ref
,
13479 &base_branch_ref
, worktree
, refname
, repo
);
13483 refname
= strdup(got_ref_get_name(branch_ref
));
13484 if (refname
== NULL
) {
13485 error
= got_error_from_errno("strdup");
13486 got_worktree_integrate_abort(worktree
, fileindex
, repo
,
13487 branch_ref
, base_branch_ref
);
13490 base_refname
= strdup(got_ref_get_name(base_branch_ref
));
13491 if (base_refname
== NULL
) {
13492 error
= got_error_from_errno("strdup");
13493 got_worktree_integrate_abort(worktree
, fileindex
, repo
,
13494 branch_ref
, base_branch_ref
);
13497 if (strncmp(base_refname
, "refs/heads/", 11) != 0) {
13498 error
= got_error(GOT_ERR_INTEGRATE_BRANCH
);
13499 got_worktree_integrate_abort(worktree
, fileindex
, repo
,
13500 branch_ref
, base_branch_ref
);
13504 error
= got_ref_resolve(&commit_id
, repo
, branch_ref
);
13508 error
= got_ref_resolve(&base_commit_id
, repo
, base_branch_ref
);
13512 if (got_object_id_cmp(commit_id
, base_commit_id
) == 0) {
13513 error
= got_error_msg(GOT_ERR_SAME_BRANCH
,
13514 "specified branch has already been integrated");
13515 got_worktree_integrate_abort(worktree
, fileindex
, repo
,
13516 branch_ref
, base_branch_ref
);
13520 error
= check_linear_ancestry(commit_id
, base_commit_id
, 1, repo
);
13522 if (error
->code
== GOT_ERR_ANCESTRY
)
13523 error
= got_error(GOT_ERR_REBASE_REQUIRED
);
13524 got_worktree_integrate_abort(worktree
, fileindex
, repo
,
13525 branch_ref
, base_branch_ref
);
13529 memset(&upa
, 0, sizeof(upa
));
13530 error
= got_worktree_integrate_continue(worktree
, fileindex
, repo
,
13531 branch_ref
, base_branch_ref
, update_progress
, &upa
,
13532 check_cancelled
, NULL
);
13536 printf("Integrated %s into %s\n", refname
, base_refname
);
13537 print_update_progress_stats(&upa
);
13540 const struct got_error
*close_err
= got_repo_close(repo
);
13545 got_worktree_close(worktree
);
13547 const struct got_error
*pack_err
=
13548 got_repo_pack_fds_close(pack_fds
);
13553 free(base_commit_id
);
13556 free(base_refname
);
13563 fprintf(stderr
, "usage: %s merge [-aCcn] [branch]\n", getprogname());
13567 static const struct got_error
*
13568 cmd_merge(int argc
, char *argv
[])
13570 const struct got_error
*error
= NULL
;
13571 struct got_worktree
*worktree
= NULL
;
13572 struct got_repository
*repo
= NULL
;
13573 struct got_fileindex
*fileindex
= NULL
;
13574 char *cwd
= NULL
, *id_str
= NULL
, *author
= NULL
;
13575 char *gitconfig_path
= NULL
;
13576 struct got_reference
*branch
= NULL
, *wt_branch
= NULL
;
13577 struct got_object_id
*branch_tip
= NULL
, *yca_id
= NULL
;
13578 struct got_object_id
*wt_branch_tip
= NULL
;
13579 int ch
, merge_in_progress
= 0, abort_merge
= 0, continue_merge
= 0;
13580 int allow_conflict
= 0, prefer_fast_forward
= 1, interrupt_merge
= 0;
13581 struct got_update_progress_arg upa
;
13582 struct got_object_id
*merge_commit_id
= NULL
;
13583 char *branch_name
= NULL
;
13584 int *pack_fds
= NULL
;
13586 memset(&upa
, 0, sizeof(upa
));
13589 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
13590 "unveil", NULL
) == -1)
13594 while ((ch
= getopt(argc
, argv
, "aCcMn")) != -1) {
13600 allow_conflict
= 1;
13603 continue_merge
= 1;
13606 prefer_fast_forward
= 0;
13609 interrupt_merge
= 1;
13621 if (continue_merge
)
13622 option_conflict('a', 'c');
13623 if (!prefer_fast_forward
)
13624 option_conflict('a', 'M');
13625 if (interrupt_merge
)
13626 option_conflict('a', 'n');
13627 } else if (continue_merge
) {
13628 if (!prefer_fast_forward
)
13629 option_conflict('c', 'M');
13630 if (interrupt_merge
)
13631 option_conflict('c', 'n');
13633 if (allow_conflict
) {
13634 if (!continue_merge
)
13635 errx(1, "-C option requires -c");
13637 if (abort_merge
|| continue_merge
) {
13640 } else if (argc
!= 1)
13643 cwd
= getcwd(NULL
, 0);
13645 error
= got_error_from_errno("getcwd");
13649 error
= got_repo_pack_fds_open(&pack_fds
);
13653 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_GOT_DIR
);
13655 if (error
->code
== GOT_ERR_NOT_WORKTREE
)
13656 error
= wrap_not_worktree_error(error
,
13661 error
= get_gitconfig_path(&gitconfig_path
);
13664 error
= got_repo_open(&repo
,
13665 worktree
? got_worktree_get_repo_path(worktree
) : cwd
,
13666 gitconfig_path
, pack_fds
);
13670 if (worktree
!= NULL
) {
13671 error
= worktree_has_logmsg_ref("merge", worktree
, repo
);
13676 error
= apply_unveil(got_repo_get_path(repo
), 0,
13677 worktree
? got_worktree_get_root_path(worktree
) : NULL
);
13681 error
= check_rebase_or_histedit_in_progress(worktree
);
13685 error
= got_worktree_merge_in_progress(&merge_in_progress
, worktree
,
13690 if (merge_in_progress
&& !(abort_merge
|| continue_merge
)) {
13691 error
= got_error(GOT_ERR_MERGE_BUSY
);
13695 if (!merge_in_progress
&& (abort_merge
|| continue_merge
)) {
13696 error
= got_error(GOT_ERR_NOT_MERGING
);
13701 error
= got_worktree_merge_continue(&branch_name
,
13702 &branch_tip
, &fileindex
, worktree
, repo
);
13705 error
= got_worktree_merge_abort(worktree
, fileindex
, repo
,
13706 abort_progress
, &upa
);
13709 printf("Merge of %s aborted\n", branch_name
);
13710 goto done
; /* nothing else to do */
13713 if (strncmp(got_worktree_get_head_ref_name(worktree
),
13714 "refs/heads/", 11) != 0) {
13715 error
= got_error_fmt(GOT_ERR_COMMIT_BRANCH
,
13716 "work tree's current branch %s is outside the "
13717 "\"refs/heads/\" reference namespace; "
13718 "update -b required",
13719 got_worktree_get_head_ref_name(worktree
));
13723 error
= get_author(&author
, repo
, worktree
);
13727 error
= got_ref_open(&wt_branch
, repo
,
13728 got_worktree_get_head_ref_name(worktree
), 0);
13731 error
= got_ref_resolve(&wt_branch_tip
, repo
, wt_branch
);
13735 if (continue_merge
) {
13736 struct got_object_id
*base_commit_id
;
13737 base_commit_id
= got_worktree_get_base_commit_id(worktree
);
13738 if (got_object_id_cmp(wt_branch_tip
, base_commit_id
) != 0) {
13739 error
= got_error(GOT_ERR_MERGE_COMMIT_OUT_OF_DATE
);
13742 error
= got_worktree_merge_continue(&branch_name
,
13743 &branch_tip
, &fileindex
, worktree
, repo
);
13747 error
= got_ref_open(&branch
, repo
, argv
[0], 0);
13750 branch_name
= strdup(got_ref_get_name(branch
));
13751 if (branch_name
== NULL
) {
13752 error
= got_error_from_errno("strdup");
13755 error
= got_ref_resolve(&branch_tip
, repo
, branch
);
13760 error
= got_commit_graph_find_youngest_common_ancestor(&yca_id
,
13761 wt_branch_tip
, branch_tip
, 0, 0, repo
,
13762 check_cancelled
, NULL
);
13763 if (error
&& error
->code
!= GOT_ERR_ANCESTRY
)
13766 if (!continue_merge
) {
13767 error
= check_path_prefix(wt_branch_tip
, branch_tip
,
13768 got_worktree_get_path_prefix(worktree
),
13769 GOT_ERR_MERGE_PATH
, repo
);
13772 error
= got_worktree_merge_prepare(&fileindex
, worktree
, repo
);
13775 if (prefer_fast_forward
&& yca_id
&&
13776 got_object_id_cmp(wt_branch_tip
, yca_id
) == 0) {
13777 struct got_pathlist_head paths
;
13778 if (interrupt_merge
) {
13779 error
= got_error_fmt(GOT_ERR_BAD_OPTION
,
13780 "there are no changes to merge since %s "
13781 "is already based on %s; merge cannot be "
13782 "interrupted for amending; -n",
13783 branch_name
, got_ref_get_name(wt_branch
));
13786 printf("Forwarding %s to %s\n",
13787 got_ref_get_name(wt_branch
), branch_name
);
13788 error
= got_ref_change_ref(wt_branch
, branch_tip
);
13791 error
= got_ref_write(wt_branch
, repo
);
13794 error
= got_worktree_set_base_commit_id(worktree
, repo
,
13798 TAILQ_INIT(&paths
);
13799 error
= got_pathlist_insert(NULL
, &paths
, "", NULL
);
13802 error
= got_worktree_checkout_files(worktree
,
13803 &paths
, repo
, update_progress
, &upa
,
13804 check_cancelled
, NULL
);
13805 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_NONE
);
13808 if (upa
.did_something
) {
13810 error
= got_object_id_str(&id_str
, branch_tip
);
13813 printf("Updated to commit %s\n", id_str
);
13816 printf("Already up-to-date\n");
13817 print_update_progress_stats(&upa
);
13820 error
= got_worktree_merge_write_refs(worktree
, branch
, repo
);
13824 error
= got_worktree_merge_branch(worktree
, fileindex
,
13825 yca_id
, branch_tip
, repo
, update_progress
, &upa
,
13826 check_cancelled
, NULL
);
13829 print_merge_progress_stats(&upa
);
13830 if (!upa
.did_something
) {
13831 error
= got_worktree_merge_abort(worktree
, fileindex
,
13832 repo
, abort_progress
, &upa
);
13835 printf("Already up-to-date\n");
13840 if (interrupt_merge
) {
13841 error
= got_worktree_merge_postpone(worktree
, fileindex
);
13844 printf("Merge of %s interrupted on request\n", branch_name
);
13845 } else if (upa
.conflicts
> 0 || upa
.missing
> 0 ||
13846 upa
.not_deleted
> 0 || upa
.unversioned
> 0) {
13847 error
= got_worktree_merge_postpone(worktree
, fileindex
);
13850 if (upa
.conflicts
> 0 && upa
.missing
== 0 &&
13851 upa
.not_deleted
== 0 && upa
.unversioned
== 0) {
13852 error
= got_error_msg(GOT_ERR_CONFLICTS
,
13853 "conflicts must be resolved before merging "
13855 } else if (upa
.conflicts
> 0) {
13856 error
= got_error_msg(GOT_ERR_CONFLICTS
,
13857 "conflicts must be resolved before merging "
13858 "can continue; changes destined for some "
13859 "files were not yet merged and "
13860 "should be merged manually if required before the "
13861 "merge operation is continued");
13863 error
= got_error_msg(GOT_ERR_CONFLICTS
,
13864 "changes destined for some "
13865 "files were not yet merged and should be "
13866 "merged manually if required before the "
13867 "merge operation is continued");
13871 error
= got_worktree_merge_commit(&merge_commit_id
, worktree
,
13872 fileindex
, author
, NULL
, 1, branch_tip
, branch_name
,
13873 allow_conflict
, repo
, continue_merge
? print_status
: NULL
,
13877 error
= got_worktree_merge_complete(worktree
, fileindex
, repo
);
13880 error
= got_object_id_str(&id_str
, merge_commit_id
);
13883 printf("Merged %s into %s: %s\n", branch_name
,
13884 got_worktree_get_head_ref_name(worktree
),
13890 free(gitconfig_path
);
13892 free(merge_commit_id
);
13898 got_ref_close(branch
);
13900 got_ref_close(wt_branch
);
13902 got_worktree_close(worktree
);
13904 const struct got_error
*close_err
= got_repo_close(repo
);
13909 const struct got_error
*pack_err
=
13910 got_repo_pack_fds_close(pack_fds
);
13920 fprintf(stderr
, "usage: %s stage [-lpS] [-F response-script] "
13921 "[path ...]\n", getprogname());
13925 static const struct got_error
*
13926 print_stage(void *arg
, unsigned char status
, unsigned char staged_status
,
13927 const char *path
, struct got_object_id
*blob_id
,
13928 struct got_object_id
*staged_blob_id
, struct got_object_id
*commit_id
,
13929 int dirfd
, const char *de_name
)
13931 const struct got_error
*err
= NULL
;
13932 char *id_str
= NULL
;
13934 if (staged_status
!= GOT_STATUS_ADD
&&
13935 staged_status
!= GOT_STATUS_MODIFY
&&
13936 staged_status
!= GOT_STATUS_DELETE
)
13939 if (staged_status
== GOT_STATUS_ADD
||
13940 staged_status
== GOT_STATUS_MODIFY
)
13941 err
= got_object_id_str(&id_str
, staged_blob_id
);
13943 err
= got_object_id_str(&id_str
, blob_id
);
13947 printf("%s %c %s\n", id_str
, staged_status
, path
);
13952 static const struct got_error
*
13953 cmd_stage(int argc
, char *argv
[])
13955 const struct got_error
*error
= NULL
;
13956 struct got_repository
*repo
= NULL
;
13957 struct got_worktree
*worktree
= NULL
;
13959 struct got_pathlist_head paths
;
13960 int ch
, list_stage
= 0, pflag
= 0, allow_bad_symlinks
= 0;
13961 FILE *patch_script_file
= NULL
;
13962 const char *patch_script_path
= NULL
;
13963 struct choose_patch_arg cpa
;
13964 int *pack_fds
= NULL
;
13966 TAILQ_INIT(&paths
);
13969 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
13970 "unveil", NULL
) == -1)
13974 while ((ch
= getopt(argc
, argv
, "F:lpS")) != -1) {
13977 patch_script_path
= optarg
;
13986 allow_bad_symlinks
= 1;
13997 if (list_stage
&& (pflag
|| patch_script_path
))
13998 errx(1, "-l option cannot be used with other options");
13999 if (patch_script_path
&& !pflag
)
14000 errx(1, "-F option can only be used together with -p option");
14002 cwd
= getcwd(NULL
, 0);
14004 error
= got_error_from_errno("getcwd");
14008 error
= got_repo_pack_fds_open(&pack_fds
);
14012 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_GOT_DIR
);
14014 if (error
->code
== GOT_ERR_NOT_WORKTREE
)
14015 error
= wrap_not_worktree_error(error
, "stage", cwd
);
14019 error
= got_repo_open(&repo
, got_worktree_get_repo_path(worktree
),
14024 if (patch_script_path
) {
14025 patch_script_file
= fopen(patch_script_path
, "re");
14026 if (patch_script_file
== NULL
) {
14027 error
= got_error_from_errno2("fopen",
14028 patch_script_path
);
14032 error
= apply_unveil(got_repo_get_path(repo
), 0,
14033 got_worktree_get_root_path(worktree
));
14037 error
= check_merge_in_progress(worktree
, repo
);
14041 error
= get_worktree_paths_from_argv(&paths
, argc
, argv
, worktree
);
14046 error
= got_worktree_status(worktree
, &paths
, repo
, 0,
14047 print_stage
, NULL
, check_cancelled
, NULL
);
14049 cpa
.patch_script_file
= patch_script_file
;
14050 cpa
.action
= "stage";
14051 error
= got_worktree_stage(worktree
, &paths
,
14052 pflag
? NULL
: print_status
, NULL
,
14053 pflag
? choose_patch
: NULL
, &cpa
,
14054 allow_bad_symlinks
, repo
);
14057 if (patch_script_file
&& fclose(patch_script_file
) == EOF
&&
14059 error
= got_error_from_errno2("fclose", patch_script_path
);
14061 const struct got_error
*close_err
= got_repo_close(repo
);
14066 got_worktree_close(worktree
);
14068 const struct got_error
*pack_err
=
14069 got_repo_pack_fds_close(pack_fds
);
14073 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_PATH
);
14079 usage_unstage(void)
14081 fprintf(stderr
, "usage: %s unstage [-p] [-F response-script] "
14082 "[path ...]\n", getprogname());
14087 static const struct got_error
*
14088 cmd_unstage(int argc
, char *argv
[])
14090 const struct got_error
*error
= NULL
;
14091 struct got_repository
*repo
= NULL
;
14092 struct got_worktree
*worktree
= NULL
;
14094 struct got_pathlist_head paths
;
14096 struct got_update_progress_arg upa
;
14097 FILE *patch_script_file
= NULL
;
14098 const char *patch_script_path
= NULL
;
14099 struct choose_patch_arg cpa
;
14100 int *pack_fds
= NULL
;
14102 TAILQ_INIT(&paths
);
14105 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
14106 "unveil", NULL
) == -1)
14110 while ((ch
= getopt(argc
, argv
, "F:p")) != -1) {
14113 patch_script_path
= optarg
;
14127 if (patch_script_path
&& !pflag
)
14128 errx(1, "-F option can only be used together with -p option");
14130 cwd
= getcwd(NULL
, 0);
14132 error
= got_error_from_errno("getcwd");
14136 error
= got_repo_pack_fds_open(&pack_fds
);
14140 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_GOT_DIR
);
14142 if (error
->code
== GOT_ERR_NOT_WORKTREE
)
14143 error
= wrap_not_worktree_error(error
, "unstage", cwd
);
14147 error
= got_repo_open(&repo
, got_worktree_get_repo_path(worktree
),
14152 if (patch_script_path
) {
14153 patch_script_file
= fopen(patch_script_path
, "re");
14154 if (patch_script_file
== NULL
) {
14155 error
= got_error_from_errno2("fopen",
14156 patch_script_path
);
14161 error
= apply_unveil(got_repo_get_path(repo
), 0,
14162 got_worktree_get_root_path(worktree
));
14166 error
= get_worktree_paths_from_argv(&paths
, argc
, argv
, worktree
);
14170 cpa
.patch_script_file
= patch_script_file
;
14171 cpa
.action
= "unstage";
14172 memset(&upa
, 0, sizeof(upa
));
14173 error
= got_worktree_unstage(worktree
, &paths
, update_progress
,
14174 &upa
, pflag
? choose_patch
: NULL
, &cpa
, repo
);
14176 print_merge_progress_stats(&upa
);
14178 if (patch_script_file
&& fclose(patch_script_file
) == EOF
&&
14180 error
= got_error_from_errno2("fclose", patch_script_path
);
14182 const struct got_error
*close_err
= got_repo_close(repo
);
14187 got_worktree_close(worktree
);
14189 const struct got_error
*pack_err
=
14190 got_repo_pack_fds_close(pack_fds
);
14194 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_PATH
);
14202 fprintf(stderr
, "usage: %s cat [-P] [-c commit] [-r repository-path] "
14203 "arg ...\n", getprogname());
14207 static const struct got_error
*
14208 cat_blob(struct got_object_id
*id
, struct got_repository
*repo
, FILE *outfile
)
14210 const struct got_error
*err
;
14211 struct got_blob_object
*blob
;
14214 fd
= got_opentempfd();
14216 return got_error_from_errno("got_opentempfd");
14218 err
= got_object_open_as_blob(&blob
, repo
, id
, 8192, fd
);
14222 err
= got_object_blob_dump_to_file(NULL
, NULL
, NULL
, outfile
, blob
);
14224 if (fd
!= -1 && close(fd
) == -1 && err
== NULL
)
14225 err
= got_error_from_errno("close");
14227 got_object_blob_close(blob
);
14231 static const struct got_error
*
14232 cat_tree(struct got_object_id
*id
, struct got_repository
*repo
, FILE *outfile
)
14234 const struct got_error
*err
;
14235 struct got_tree_object
*tree
;
14238 err
= got_object_open_as_tree(&tree
, repo
, id
);
14242 nentries
= got_object_tree_get_nentries(tree
);
14243 for (i
= 0; i
< nentries
; i
++) {
14244 struct got_tree_entry
*te
;
14246 if (sigint_received
|| sigpipe_received
)
14248 te
= got_object_tree_get_entry(tree
, i
);
14249 err
= got_object_id_str(&id_str
, got_tree_entry_get_id(te
));
14252 fprintf(outfile
, "%s %.7o %s\n", id_str
,
14253 got_tree_entry_get_mode(te
),
14254 got_tree_entry_get_name(te
));
14258 got_object_tree_close(tree
);
14262 static const struct got_error
*
14263 cat_commit(struct got_object_id
*id
, struct got_repository
*repo
, FILE *outfile
)
14265 const struct got_error
*err
;
14266 struct got_commit_object
*commit
;
14267 const struct got_object_id_queue
*parent_ids
;
14268 struct got_object_qid
*pid
;
14269 char *id_str
= NULL
;
14270 const char *logmsg
= NULL
;
14273 err
= got_object_open_as_commit(&commit
, repo
, id
);
14277 err
= got_object_id_str(&id_str
, got_object_commit_get_tree_id(commit
));
14281 fprintf(outfile
, "%s%s\n", GOT_COMMIT_LABEL_TREE
, id_str
);
14282 parent_ids
= got_object_commit_get_parent_ids(commit
);
14283 fprintf(outfile
, "numparents %d\n",
14284 got_object_commit_get_nparents(commit
));
14285 STAILQ_FOREACH(pid
, parent_ids
, entry
) {
14287 err
= got_object_id_str(&pid_str
, &pid
->id
);
14290 fprintf(outfile
, "%s%s\n", GOT_COMMIT_LABEL_PARENT
, pid_str
);
14293 got_date_format_gmtoff(gmtoff
, sizeof(gmtoff
),
14294 got_object_commit_get_author_gmtoff(commit
));
14295 fprintf(outfile
, "%s%s %lld %s\n", GOT_COMMIT_LABEL_AUTHOR
,
14296 got_object_commit_get_author(commit
),
14297 (long long)got_object_commit_get_author_time(commit
),
14300 got_date_format_gmtoff(gmtoff
, sizeof(gmtoff
),
14301 got_object_commit_get_committer_gmtoff(commit
));
14302 fprintf(outfile
, "%s%s %lld %s\n", GOT_COMMIT_LABEL_COMMITTER
,
14303 got_object_commit_get_committer(commit
),
14304 (long long)got_object_commit_get_committer_time(commit
),
14307 logmsg
= got_object_commit_get_logmsg_raw(commit
);
14308 fprintf(outfile
, "messagelen %zd\n", strlen(logmsg
));
14309 fprintf(outfile
, "%s", logmsg
);
14312 got_object_commit_close(commit
);
14316 static const struct got_error
*
14317 cat_tag(struct got_object_id
*id
, struct got_repository
*repo
, FILE *outfile
)
14319 const struct got_error
*err
;
14320 struct got_tag_object
*tag
;
14321 char *id_str
= NULL
;
14322 const char *tagmsg
= NULL
;
14325 err
= got_object_open_as_tag(&tag
, repo
, id
);
14329 err
= got_object_id_str(&id_str
, got_object_tag_get_object_id(tag
));
14333 fprintf(outfile
, "%s%s\n", GOT_TAG_LABEL_OBJECT
, id_str
);
14335 switch (got_object_tag_get_object_type(tag
)) {
14336 case GOT_OBJ_TYPE_BLOB
:
14337 fprintf(outfile
, "%s%s\n", GOT_TAG_LABEL_TYPE
,
14338 GOT_OBJ_LABEL_BLOB
);
14340 case GOT_OBJ_TYPE_TREE
:
14341 fprintf(outfile
, "%s%s\n", GOT_TAG_LABEL_TYPE
,
14342 GOT_OBJ_LABEL_TREE
);
14344 case GOT_OBJ_TYPE_COMMIT
:
14345 fprintf(outfile
, "%s%s\n", GOT_TAG_LABEL_TYPE
,
14346 GOT_OBJ_LABEL_COMMIT
);
14348 case GOT_OBJ_TYPE_TAG
:
14349 fprintf(outfile
, "%s%s\n", GOT_TAG_LABEL_TYPE
,
14350 GOT_OBJ_LABEL_TAG
);
14356 fprintf(outfile
, "%s%s\n", GOT_TAG_LABEL_TAG
,
14357 got_object_tag_get_name(tag
));
14359 got_date_format_gmtoff(gmtoff
, sizeof(gmtoff
),
14360 got_object_tag_get_tagger_gmtoff(tag
));
14361 fprintf(outfile
, "%s%s %lld %s\n", GOT_TAG_LABEL_TAGGER
,
14362 got_object_tag_get_tagger(tag
),
14363 (long long)got_object_tag_get_tagger_time(tag
),
14366 tagmsg
= got_object_tag_get_message(tag
);
14367 fprintf(outfile
, "messagelen %zd\n", strlen(tagmsg
));
14368 fprintf(outfile
, "%s", tagmsg
);
14371 got_object_tag_close(tag
);
14375 static const struct got_error
*
14376 cmd_cat(int argc
, char *argv
[])
14378 const struct got_error
*error
;
14379 struct got_repository
*repo
= NULL
;
14380 struct got_worktree
*worktree
= NULL
;
14381 char *cwd
= NULL
, *repo_path
= NULL
, *label
= NULL
;
14382 char *keyword_idstr
= NULL
;
14383 const char *commit_id_str
= NULL
;
14384 struct got_object_id
*id
= NULL
, *commit_id
= NULL
;
14385 struct got_commit_object
*commit
= NULL
;
14386 int ch
, obj_type
, i
, force_path
= 0;
14387 struct got_reflist_head refs
;
14388 int *pack_fds
= NULL
;
14393 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
14398 while ((ch
= getopt(argc
, argv
, "c:Pr:")) != -1) {
14401 commit_id_str
= optarg
;
14407 repo_path
= realpath(optarg
, NULL
);
14408 if (repo_path
== NULL
)
14409 return got_error_from_errno2("realpath",
14411 got_path_strip_trailing_slashes(repo_path
);
14422 cwd
= getcwd(NULL
, 0);
14424 error
= got_error_from_errno("getcwd");
14428 error
= got_repo_pack_fds_open(&pack_fds
);
14432 if (repo_path
== NULL
) {
14433 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_GOT_DIR
);
14434 if (error
&& error
->code
!= GOT_ERR_NOT_WORKTREE
)
14437 repo_path
= strdup(
14438 got_worktree_get_repo_path(worktree
));
14439 if (repo_path
== NULL
) {
14440 error
= got_error_from_errno("strdup");
14444 if (commit_id_str
== NULL
) {
14445 /* Release work tree lock. */
14446 got_worktree_close(worktree
);
14452 if (repo_path
== NULL
) {
14453 repo_path
= strdup(cwd
);
14454 if (repo_path
== NULL
)
14455 return got_error_from_errno("strdup");
14458 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
14463 error
= apply_unveil(got_repo_get_path(repo
), 1, NULL
);
14467 error
= got_ref_list(&refs
, repo
, NULL
, got_ref_cmp_by_name
, NULL
);
14471 if (commit_id_str
!= NULL
) {
14472 error
= got_keyword_to_idstr(&keyword_idstr
, commit_id_str
,
14476 if (keyword_idstr
!= NULL
)
14477 commit_id_str
= keyword_idstr
;
14478 if (worktree
!= NULL
) {
14479 got_worktree_close(worktree
);
14483 commit_id_str
= GOT_REF_HEAD
;
14484 error
= got_repo_match_object_id(&commit_id
, NULL
,
14485 commit_id_str
, GOT_OBJ_TYPE_COMMIT
, &refs
, repo
);
14489 error
= got_object_open_as_commit(&commit
, repo
, commit_id
);
14493 for (i
= 0; i
< argc
; i
++) {
14495 error
= got_object_id_by_path(&id
, repo
, commit
,
14500 error
= got_repo_match_object_id(&id
, &label
, argv
[i
],
14501 GOT_OBJ_TYPE_ANY
, NULL
/* do not resolve tags */,
14504 if (error
->code
!= GOT_ERR_BAD_OBJ_ID_STR
&&
14505 error
->code
!= GOT_ERR_NOT_REF
)
14507 error
= got_object_id_by_path(&id
, repo
,
14514 error
= got_object_get_type(&obj_type
, repo
, id
);
14518 switch (obj_type
) {
14519 case GOT_OBJ_TYPE_BLOB
:
14520 error
= cat_blob(id
, repo
, stdout
);
14522 case GOT_OBJ_TYPE_TREE
:
14523 error
= cat_tree(id
, repo
, stdout
);
14525 case GOT_OBJ_TYPE_COMMIT
:
14526 error
= cat_commit(id
, repo
, stdout
);
14528 case GOT_OBJ_TYPE_TAG
:
14529 error
= cat_tag(id
, repo
, stdout
);
14532 error
= got_error(GOT_ERR_OBJ_TYPE
);
14547 free(keyword_idstr
);
14549 got_object_commit_close(commit
);
14551 got_worktree_close(worktree
);
14553 const struct got_error
*close_err
= got_repo_close(repo
);
14558 const struct got_error
*pack_err
=
14559 got_repo_pack_fds_close(pack_fds
);
14564 got_ref_list_free(&refs
);
14571 fprintf(stderr
, "usage: %s info [path ...]\n",
14576 static const struct got_error
*
14577 print_path_info(void *arg
, const char *path
, mode_t mode
, time_t mtime
,
14578 struct got_object_id
*blob_id
, struct got_object_id
*staged_blob_id
,
14579 struct got_object_id
*commit_id
)
14581 const struct got_error
*err
= NULL
;
14582 char *id_str
= NULL
;
14584 struct tm mytm
, *tm
;
14585 struct got_pathlist_head
*paths
= arg
;
14586 struct got_pathlist_entry
*pe
;
14589 * Clear error indication from any of the path arguments which
14590 * would cause this file index entry to be displayed.
14592 TAILQ_FOREACH(pe
, paths
, entry
) {
14593 if (got_path_cmp(path
, pe
->path
, strlen(path
),
14594 pe
->path_len
) == 0 ||
14595 got_path_is_child(path
, pe
->path
, pe
->path_len
))
14596 pe
->data
= NULL
; /* no error */
14599 printf(GOT_COMMIT_SEP_STR
);
14601 printf("symlink: %s\n", path
);
14602 else if (S_ISREG(mode
)) {
14603 printf("file: %s\n", path
);
14604 printf("mode: %o\n", mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
));
14605 } else if (S_ISDIR(mode
))
14606 printf("directory: %s\n", path
);
14608 printf("something: %s\n", path
);
14610 tm
= localtime_r(&mtime
, &mytm
);
14613 if (strftime(datebuf
, sizeof(datebuf
), "%c %Z", tm
) == 0)
14614 return got_error(GOT_ERR_NO_SPACE
);
14615 printf("timestamp: %s\n", datebuf
);
14618 err
= got_object_id_str(&id_str
, blob_id
);
14621 printf("based on blob: %s\n", id_str
);
14625 if (staged_blob_id
) {
14626 err
= got_object_id_str(&id_str
, staged_blob_id
);
14629 printf("based on staged blob: %s\n", id_str
);
14634 err
= got_object_id_str(&id_str
, commit_id
);
14637 printf("based on commit: %s\n", id_str
);
14644 static const struct got_error
*
14645 cmd_info(int argc
, char *argv
[])
14647 const struct got_error
*error
= NULL
;
14648 struct got_repository
*repo
= NULL
;
14649 struct got_worktree
*worktree
= NULL
;
14650 struct got_fileindex
*fileindex
= NULL
;
14651 char *cwd
= NULL
, *id_str
= NULL
;
14652 struct got_pathlist_head paths
;
14653 char *uuidstr
= NULL
;
14654 int *pack_fds
= NULL
;
14655 int ch
, show_files
= 0;
14657 TAILQ_INIT(&paths
);
14660 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
14665 while ((ch
= getopt(argc
, argv
, "")) != -1) {
14676 cwd
= getcwd(NULL
, 0);
14678 error
= got_error_from_errno("getcwd");
14682 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_GOT_DIR
);
14684 if (error
->code
== GOT_ERR_NOT_WORKTREE
)
14685 error
= wrap_not_worktree_error(error
, "info", cwd
);
14689 error
= got_repo_pack_fds_open(&pack_fds
);
14693 error
= got_repo_open(&repo
, got_worktree_get_repo_path(worktree
), NULL
,
14699 /* Remove "wpath cpath proc exec sendfd" promises. */
14700 if (pledge("stdio rpath flock unveil", NULL
) == -1)
14703 error
= apply_unveil(NULL
, 0, got_worktree_get_root_path(worktree
));
14708 error
= get_worktree_paths_from_argv(&paths
, argc
, argv
,
14715 error
= got_worktree_path_info_prepare(&fileindex
, worktree
, repo
);
14719 error
= got_object_id_str(&id_str
,
14720 got_worktree_get_base_commit_id(worktree
));
14724 error
= got_worktree_get_uuid(&uuidstr
, worktree
);
14728 printf("work tree: %s\n", got_worktree_get_root_path(worktree
));
14729 printf("work tree base commit: %s\n", id_str
);
14730 printf("work tree path prefix: %s\n",
14731 got_worktree_get_path_prefix(worktree
));
14732 printf("work tree branch reference: %s\n",
14733 got_worktree_get_head_ref_name(worktree
));
14734 printf("work tree UUID: %s\n", uuidstr
);
14735 printf("work tree format version: %d\n",
14736 got_worktree_get_format_version(worktree
));
14737 printf("file index version: %u\n",
14738 got_worktree_get_fileindex_version(fileindex
));
14739 printf("repository: %s\n", got_worktree_get_repo_path(worktree
));
14742 struct got_pathlist_entry
*pe
;
14743 TAILQ_FOREACH(pe
, &paths
, entry
) {
14744 if (pe
->path_len
== 0)
14747 * Assume this path will fail. This will be corrected
14748 * in print_path_info() in case the path does suceeed.
14750 pe
->data
= (void *)got_error(GOT_ERR_BAD_PATH
);
14752 error
= got_worktree_path_info(worktree
, fileindex
, &paths
,
14753 print_path_info
, &paths
, check_cancelled
, NULL
);
14756 TAILQ_FOREACH(pe
, &paths
, entry
) {
14757 if (pe
->data
!= NULL
) {
14758 const struct got_error
*perr
;
14761 error
= got_error_path(pe
->path
, perr
->code
);
14768 const struct got_error
*cerr
;
14770 cerr
= got_worktree_path_info_complete(fileindex
, worktree
);
14773 got_worktree_close(worktree
);
14776 const struct got_error
*close_err
= got_repo_close(repo
);
14781 const struct got_error
*pack_err
=
14782 got_repo_pack_fds_close(pack_fds
);
14786 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_PATH
);