2 * Copyright (c) 2017 Martin Pieuchot <mpi@openbsd.org>
3 * Copyright (c) 2018, 2019, 2020 Stefan Sperling <stsp@openbsd.org>
4 * Copyright (c) 2020 Ori Bernstein <ori@openbsd.org>
5 * Copyright (C) 2023 Josh Rickmar <jrick@zettaport.com>
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 #include "got_compat.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"
52 #include "got_worktree_cvg.h"
54 #include "got_commit_graph.h"
55 #include "got_fetch.h"
57 #include "got_blame.h"
58 #include "got_privsep.h"
59 #include "got_opentemp.h"
60 #include "got_gotconfig.h"
62 #include "got_patch.h"
67 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
70 static volatile sig_atomic_t sigint_received
;
71 static volatile sig_atomic_t sigpipe_received
;
74 catch_sigint(int signo
)
80 catch_sigpipe(int signo
)
88 const struct got_error
*(*cmd_main
)(int, char *[]);
89 void (*cmd_usage
)(void);
90 const char *cmd_alias
;
93 __dead
static void usage(int, int);
94 __dead
static void usage_import(void);
95 __dead
static void usage_clone(void);
96 __dead
static void usage_checkout(void);
97 __dead
static void usage_update(void);
98 __dead
static void usage_log(void);
99 __dead
static void usage_diff(void);
100 __dead
static void usage_blame(void);
101 __dead
static void usage_tree(void);
102 __dead
static void usage_status(void);
103 __dead
static void usage_tag(void);
104 __dead
static void usage_add(void);
105 __dead
static void usage_remove(void);
106 __dead
static void usage_patch(void);
107 __dead
static void usage_revert(void);
108 __dead
static void usage_commit(void);
109 __dead
static void usage_cherrypick(void);
110 __dead
static void usage_backout(void);
111 __dead
static void usage_cat(void);
112 __dead
static void usage_info(void);
114 static const struct got_error
* cmd_import(int, char *[]);
115 static const struct got_error
* cmd_clone(int, char *[]);
116 static const struct got_error
* cmd_checkout(int, char *[]);
117 static const struct got_error
* cmd_update(int, char *[]);
118 static const struct got_error
* cmd_log(int, char *[]);
119 static const struct got_error
* cmd_diff(int, char *[]);
120 static const struct got_error
* cmd_blame(int, char *[]);
121 static const struct got_error
* cmd_tree(int, char *[]);
122 static const struct got_error
* cmd_status(int, char *[]);
123 static const struct got_error
* cmd_tag(int, char *[]);
124 static const struct got_error
* cmd_add(int, char *[]);
125 static const struct got_error
* cmd_remove(int, char *[]);
126 static const struct got_error
* cmd_patch(int, char *[]);
127 static const struct got_error
* cmd_revert(int, char *[]);
128 static const struct got_error
* cmd_commit(int, char *[]);
129 static const struct got_error
* cmd_cherrypick(int, char *[]);
130 static const struct got_error
* cmd_backout(int, char *[]);
131 static const struct got_error
* cmd_cat(int, char *[]);
132 static const struct got_error
* cmd_info(int, char *[]);
134 static const struct got_cmd got_commands
[] = {
135 { "import", cmd_import
, usage_import
, "im" },
136 { "clone", cmd_clone
, usage_clone
, "cl" },
137 { "checkout", cmd_checkout
, usage_checkout
, "co" },
138 { "update", cmd_update
, usage_update
, "up" },
139 { "log", cmd_log
, usage_log
, "" },
140 { "diff", cmd_diff
, usage_diff
, "di" },
141 { "blame", cmd_blame
, usage_blame
, "bl" },
142 { "tree", cmd_tree
, usage_tree
, "tr" },
143 { "status", cmd_status
, usage_status
, "st" },
144 { "tag", cmd_tag
, usage_tag
, "" },
145 { "add", cmd_add
, usage_add
, "" },
146 { "remove", cmd_remove
, usage_remove
, "rm" },
147 { "patch", cmd_patch
, usage_patch
, "pa" },
148 { "revert", cmd_revert
, usage_revert
, "rv" },
149 { "commit", cmd_commit
, usage_commit
, "ci" },
150 { "cherrypick", cmd_cherrypick
, usage_cherrypick
, "cy" },
151 { "backout", cmd_backout
, usage_backout
, "bo" },
152 { "cat", cmd_cat
, usage_cat
, "" },
153 { "info", cmd_info
, usage_info
, "" },
157 list_commands(FILE *fp
)
161 fprintf(fp
, "commands:");
162 for (i
= 0; i
< nitems(got_commands
); i
++) {
163 const struct got_cmd
*cmd
= &got_commands
[i
];
164 fprintf(fp
, " %s", cmd
->cmd_name
);
170 option_conflict(char a
, char b
)
172 errx(1, "-%c and -%c options are mutually exclusive", a
, b
);
176 main(int argc
, char *argv
[])
178 const struct got_cmd
*cmd
;
181 int hflag
= 0, Vflag
= 0;
182 static const struct option longopts
[] = {
183 { "version", no_argument
, NULL
, 'V' },
187 setlocale(LC_CTYPE
, "");
189 while ((ch
= getopt_long(argc
, argv
, "+hV", longopts
, NULL
)) != -1) {
209 got_version_print_str();
214 usage(hflag
, hflag
? 0 : 1);
216 signal(SIGINT
, catch_sigint
);
217 signal(SIGPIPE
, catch_sigpipe
);
219 for (i
= 0; i
< nitems(got_commands
); i
++) {
220 const struct got_error
*error
;
222 cmd
= &got_commands
[i
];
224 if (strcmp(cmd
->cmd_name
, argv
[0]) != 0 &&
225 strcmp(cmd
->cmd_alias
, argv
[0]) != 0)
231 error
= cmd
->cmd_main(argc
, argv
);
232 if (error
&& error
->code
!= GOT_ERR_CANCELLED
&&
233 error
->code
!= GOT_ERR_PRIVSEP_EXIT
&&
234 !(sigpipe_received
&&
235 error
->code
== GOT_ERR_ERRNO
&& errno
== EPIPE
) &&
237 error
->code
== GOT_ERR_ERRNO
&& errno
== EINTR
)) {
239 fprintf(stderr
, "%s: %s\n", getprogname(), error
->msg
);
246 fprintf(stderr
, "%s: unknown command '%s'\n", getprogname(), argv
[0]);
247 list_commands(stderr
);
252 usage(int hflag
, int status
)
254 FILE *fp
= (status
== 0) ? stdout
: stderr
;
256 fprintf(fp
, "usage: %s [-hV] command [arg ...]\n",
263 static const struct got_error
*
264 get_editor(char **abspath
)
266 const struct got_error
*err
= NULL
;
271 editor
= getenv("VISUAL");
273 editor
= getenv("EDITOR");
276 err
= got_path_find_prog(abspath
, editor
);
281 if (*abspath
== NULL
) {
282 *abspath
= strdup("/usr/bin/vi");
283 if (*abspath
== NULL
)
284 return got_error_from_errno("strdup");
290 static const struct got_error
*
291 apply_unveil(const char *repo_path
, int repo_read_only
,
292 const char *worktree_path
)
294 const struct got_error
*err
;
297 if (unveil("gmon.out", "rwc") != 0)
298 return got_error_from_errno2("unveil", "gmon.out");
300 if (repo_path
&& unveil(repo_path
, repo_read_only
? "r" : "rwc") != 0)
301 return got_error_from_errno2("unveil", repo_path
);
303 if (worktree_path
&& unveil(worktree_path
, "rwc") != 0)
304 return got_error_from_errno2("unveil", worktree_path
);
306 if (unveil(GOT_TMPDIR_STR
, "rwc") != 0)
307 return got_error_from_errno2("unveil", GOT_TMPDIR_STR
);
309 err
= got_privsep_unveil_exec_helpers();
313 if (unveil(NULL
, NULL
) != 0)
314 return got_error_from_errno("unveil");
322 fprintf(stderr
, "usage: %s import [-b branch] [-I pattern] [-m message] "
323 "[-r repository-path] directory\n", getprogname());
328 spawn_editor(const char *editor
, const char *file
)
331 sig_t sighup
, sigint
, sigquit
;
334 sighup
= signal(SIGHUP
, SIG_IGN
);
335 sigint
= signal(SIGINT
, SIG_IGN
);
336 sigquit
= signal(SIGQUIT
, SIG_IGN
);
338 switch (pid
= fork()) {
342 execl(editor
, editor
, file
, (char *)NULL
);
346 while (waitpid(pid
, &st
, 0) == -1)
351 (void)signal(SIGHUP
, sighup
);
352 (void)signal(SIGINT
, sigint
);
353 (void)signal(SIGQUIT
, sigquit
);
355 if (!WIFEXITED(st
)) {
360 return WEXITSTATUS(st
);
363 static const struct got_error
*
364 read_logmsg(char **logmsg
, size_t *len
, FILE *fp
, size_t filesize
)
366 const struct got_error
*err
= NULL
;
373 if (fseeko(fp
, 0L, SEEK_SET
) == -1)
374 return got_error_from_errno("fseeko");
376 *logmsg
= malloc(filesize
+ 1);
378 return got_error_from_errno("malloc");
381 while (getline(&line
, &linesize
, fp
) != -1) {
382 if (line
[0] == '#' || (*len
== 0 && line
[0] == '\n'))
383 continue; /* remove comments and leading empty lines */
384 *len
= strlcat(*logmsg
, line
, filesize
+ 1);
385 if (*len
>= filesize
+ 1) {
386 err
= got_error(GOT_ERR_NO_SPACE
);
391 err
= got_ferror(fp
, GOT_ERR_IO
);
395 while (*len
> 0 && (*logmsg
)[*len
- 1] == '\n') {
396 (*logmsg
)[*len
- 1] = '\0';
409 static const struct got_error
*
410 edit_logmsg(char **logmsg
, const char *editor
, const char *logmsg_path
,
411 const char *initial_content
, size_t initial_content_len
,
412 int require_modification
)
414 const struct got_error
*err
= NULL
;
421 if (stat(logmsg_path
, &st
) == -1)
422 return got_error_from_errno2("stat", logmsg_path
);
424 if (spawn_editor(editor
, logmsg_path
) == -1)
425 return got_error_from_errno("failed spawning editor");
427 if (require_modification
) {
428 struct timespec timeout
;
432 nanosleep(&timeout
, NULL
);
435 if (stat(logmsg_path
, &st2
) == -1)
436 return got_error_from_errno2("stat", logmsg_path
);
438 if (require_modification
&& st
.st_size
== st2
.st_size
&&
439 timespeccmp(&st
.st_mtim
, &st2
.st_mtim
, ==))
440 return got_error_msg(GOT_ERR_COMMIT_MSG_EMPTY
,
441 "no changes made to commit message, aborting");
443 fp
= fopen(logmsg_path
, "re");
445 err
= got_error_from_errno("fopen");
449 /* strip comments and leading/trailing newlines */
450 err
= read_logmsg(logmsg
, &logmsg_len
, fp
, st2
.st_size
);
453 if (logmsg_len
== 0) {
454 err
= got_error_msg(GOT_ERR_COMMIT_MSG_EMPTY
,
455 "commit message cannot be empty, aborting");
459 if (fp
&& fclose(fp
) == EOF
&& err
== NULL
)
460 err
= got_error_from_errno("fclose");
468 static const struct got_error
*
469 collect_import_msg(char **logmsg
, char **logmsg_path
, const char *editor
,
470 const char *path_dir
, const char *branch_name
)
472 char *initial_content
= NULL
;
473 const struct got_error
*err
= NULL
;
474 int initial_content_len
;
477 initial_content_len
= asprintf(&initial_content
,
478 "\n# %s to be imported to branch %s\n", path_dir
,
480 if (initial_content_len
== -1)
481 return got_error_from_errno("asprintf");
483 err
= got_opentemp_named_fd(logmsg_path
, &fd
,
484 GOT_TMPDIR_STR
"/got-importmsg", "");
488 if (write(fd
, initial_content
, initial_content_len
) == -1) {
489 err
= got_error_from_errno2("write", *logmsg_path
);
492 if (close(fd
) == -1) {
493 err
= got_error_from_errno2("close", *logmsg_path
);
498 err
= edit_logmsg(logmsg
, editor
, *logmsg_path
, initial_content
,
499 initial_content_len
, 1);
501 if (fd
!= -1 && close(fd
) == -1 && err
== NULL
)
502 err
= got_error_from_errno2("close", *logmsg_path
);
503 free(initial_content
);
511 static const struct got_error
*
512 import_progress(void *arg
, const char *path
)
514 printf("A %s\n", path
);
518 static const struct got_error
*
519 valid_author(const char *author
)
521 const char *email
= author
;
524 * Git' expects the author (or committer) to be in the form
525 * "name <email>", which are mostly free form (see the
526 * "committer" description in git-fast-import(1)). We're only
527 * doing this to avoid git's object parser breaking on commits
531 while (*author
&& *author
!= '\n' && *author
!= '<' && *author
!= '>')
533 if (author
!= email
&& *author
== '<' && *(author
- 1) != ' ')
534 return got_error_fmt(GOT_ERR_COMMIT_BAD_AUTHOR
, "%s: space "
535 "between author name and email required", email
);
536 if (*author
++ != '<')
537 return got_error_fmt(GOT_ERR_COMMIT_NO_EMAIL
, "%s", email
);
538 while (*author
&& *author
!= '\n' && *author
!= '<' && *author
!= '>')
540 if (strcmp(author
, ">") != 0)
541 return got_error_fmt(GOT_ERR_COMMIT_NO_EMAIL
, "%s", email
);
545 static const struct got_error
*
546 get_author(char **author
, struct got_repository
*repo
,
547 struct got_worktree
*worktree
)
549 const struct got_error
*err
= NULL
;
550 const char *got_author
= NULL
, *name
, *email
;
551 const struct got_gotconfig
*worktree_conf
= NULL
, *repo_conf
= NULL
;
556 worktree_conf
= got_worktree_get_gotconfig(worktree
);
557 repo_conf
= got_repo_get_gotconfig(repo
);
560 * Priority of potential author information sources, from most
561 * significant to least significant:
562 * 1) work tree's .got/got.conf file
563 * 2) repository's got.conf file
564 * 3) repository's git config file
565 * 4) environment variables
566 * 5) global git config files (in user's home directory or /etc)
570 got_author
= got_gotconfig_get_author(worktree_conf
);
571 if (got_author
== NULL
)
572 got_author
= got_gotconfig_get_author(repo_conf
);
573 if (got_author
== NULL
) {
574 name
= got_repo_get_gitconfig_author_name(repo
);
575 email
= got_repo_get_gitconfig_author_email(repo
);
577 if (asprintf(author
, "%s <%s>", name
, email
) == -1)
578 return got_error_from_errno("asprintf");
582 got_author
= getenv("GOT_AUTHOR");
583 if (got_author
== NULL
) {
584 name
= got_repo_get_global_gitconfig_author_name(repo
);
585 email
= got_repo_get_global_gitconfig_author_email(
588 if (asprintf(author
, "%s <%s>", name
, email
)
590 return got_error_from_errno("asprintf");
593 /* TODO: Look up user in password database? */
594 return got_error(GOT_ERR_COMMIT_NO_AUTHOR
);
598 *author
= strdup(got_author
);
600 return got_error_from_errno("strdup");
602 err
= valid_author(*author
);
610 static const struct got_error
*
611 get_allowed_signers(char **allowed_signers
, struct got_repository
*repo
,
612 struct got_worktree
*worktree
)
614 const char *got_allowed_signers
= NULL
;
615 const struct got_gotconfig
*worktree_conf
= NULL
, *repo_conf
= NULL
;
617 *allowed_signers
= NULL
;
620 worktree_conf
= got_worktree_get_gotconfig(worktree
);
621 repo_conf
= got_repo_get_gotconfig(repo
);
624 * Priority of potential author information sources, from most
625 * significant to least significant:
626 * 1) work tree's .got/got.conf file
627 * 2) repository's got.conf file
631 got_allowed_signers
= got_gotconfig_get_allowed_signers_file(
633 if (got_allowed_signers
== NULL
)
634 got_allowed_signers
= got_gotconfig_get_allowed_signers_file(
637 if (got_allowed_signers
) {
638 *allowed_signers
= strdup(got_allowed_signers
);
639 if (*allowed_signers
== NULL
)
640 return got_error_from_errno("strdup");
645 static const struct got_error
*
646 get_revoked_signers(char **revoked_signers
, struct got_repository
*repo
,
647 struct got_worktree
*worktree
)
649 const char *got_revoked_signers
= NULL
;
650 const struct got_gotconfig
*worktree_conf
= NULL
, *repo_conf
= NULL
;
652 *revoked_signers
= NULL
;
655 worktree_conf
= got_worktree_get_gotconfig(worktree
);
656 repo_conf
= got_repo_get_gotconfig(repo
);
659 * Priority of potential author information sources, from most
660 * significant to least significant:
661 * 1) work tree's .got/got.conf file
662 * 2) repository's got.conf file
666 got_revoked_signers
= got_gotconfig_get_revoked_signers_file(
668 if (got_revoked_signers
== NULL
)
669 got_revoked_signers
= got_gotconfig_get_revoked_signers_file(
672 if (got_revoked_signers
) {
673 *revoked_signers
= strdup(got_revoked_signers
);
674 if (*revoked_signers
== NULL
)
675 return got_error_from_errno("strdup");
681 get_signer_id(struct got_repository
*repo
, struct got_worktree
*worktree
)
683 const char *got_signer_id
= NULL
;
684 const struct got_gotconfig
*worktree_conf
= NULL
, *repo_conf
= NULL
;
687 worktree_conf
= got_worktree_get_gotconfig(worktree
);
688 repo_conf
= got_repo_get_gotconfig(repo
);
691 * Priority of potential author information sources, from most
692 * significant to least significant:
693 * 1) work tree's .got/got.conf file
694 * 2) repository's got.conf file
698 got_signer_id
= got_gotconfig_get_signer_id(worktree_conf
);
699 if (got_signer_id
== NULL
)
700 got_signer_id
= got_gotconfig_get_signer_id(repo_conf
);
702 return got_signer_id
;
705 static const struct got_error
*
706 get_gitconfig_path(char **gitconfig_path
)
708 const char *homedir
= getenv("HOME");
710 *gitconfig_path
= NULL
;
712 if (asprintf(gitconfig_path
, "%s/.gitconfig", homedir
) == -1)
713 return got_error_from_errno("asprintf");
719 static const struct got_error
*
720 cmd_import(int argc
, char *argv
[])
722 const struct got_error
*error
= NULL
;
723 char *path_dir
= NULL
, *repo_path
= NULL
, *logmsg
= NULL
;
724 char *gitconfig_path
= NULL
, *editor
= NULL
, *author
= NULL
;
725 const char *branch_name
= NULL
;
726 char *id_str
= NULL
, *logmsg_path
= NULL
;
727 char refname
[PATH_MAX
] = "refs/heads/";
728 struct got_repository
*repo
= NULL
;
729 struct got_reference
*branch_ref
= NULL
, *head_ref
= NULL
;
730 struct got_object_id
*new_commit_id
= NULL
;
732 struct got_pathlist_head ignores
;
733 struct got_pathlist_entry
*pe
;
734 int preserve_logmsg
= 0;
735 int *pack_fds
= NULL
;
737 TAILQ_INIT(&ignores
);
740 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
746 while ((ch
= getopt(argc
, argv
, "b:I:m:r:")) != -1) {
749 branch_name
= optarg
;
752 if (optarg
[0] == '\0')
754 error
= got_pathlist_insert(&pe
, &ignores
, optarg
,
760 logmsg
= strdup(optarg
);
761 if (logmsg
== NULL
) {
762 error
= got_error_from_errno("strdup");
767 repo_path
= realpath(optarg
, NULL
);
768 if (repo_path
== NULL
) {
769 error
= got_error_from_errno2("realpath",
786 if (repo_path
== NULL
) {
787 repo_path
= getcwd(NULL
, 0);
788 if (repo_path
== NULL
)
789 return got_error_from_errno("getcwd");
791 got_path_strip_trailing_slashes(repo_path
);
792 error
= get_gitconfig_path(&gitconfig_path
);
795 error
= got_repo_pack_fds_open(&pack_fds
);
798 error
= got_repo_open(&repo
, repo_path
, gitconfig_path
, pack_fds
);
802 error
= get_author(&author
, repo
, NULL
);
807 * Don't let the user create a branch name with a leading '-'.
808 * While technically a valid reference name, this case is usually
809 * an unintended typo.
811 if (branch_name
&& branch_name
[0] == '-')
812 return got_error_path(branch_name
, GOT_ERR_REF_NAME_MINUS
);
814 error
= got_ref_open(&head_ref
, repo
, GOT_REF_HEAD
, 0);
815 if (error
&& error
->code
!= GOT_ERR_NOT_REF
)
819 n
= strlcat(refname
, branch_name
, sizeof(refname
));
820 else if (head_ref
&& got_ref_is_symbolic(head_ref
))
821 n
= strlcpy(refname
, got_ref_get_symref_target(head_ref
),
824 n
= strlcat(refname
, "main", sizeof(refname
));
825 if (n
>= sizeof(refname
)) {
826 error
= got_error(GOT_ERR_NO_SPACE
);
830 error
= got_ref_open(&branch_ref
, repo
, refname
, 0);
832 if (error
->code
!= GOT_ERR_NOT_REF
)
835 error
= got_error_msg(GOT_ERR_BRANCH_EXISTS
,
836 "import target branch already exists");
840 path_dir
= realpath(argv
[0], NULL
);
841 if (path_dir
== NULL
) {
842 error
= got_error_from_errno2("realpath", argv
[0]);
845 got_path_strip_trailing_slashes(path_dir
);
848 * unveil(2) traverses exec(2); if an editor is used we have
849 * to apply unveil after the log message has been written.
851 if (logmsg
== NULL
|| *logmsg
== '\0') {
852 error
= get_editor(&editor
);
856 error
= collect_import_msg(&logmsg
, &logmsg_path
, editor
,
859 if (error
->code
!= GOT_ERR_COMMIT_MSG_EMPTY
&&
866 if (unveil(path_dir
, "r") != 0) {
867 error
= got_error_from_errno2("unveil", path_dir
);
873 error
= apply_unveil(got_repo_get_path(repo
), 0, NULL
);
880 error
= got_repo_import(&new_commit_id
, path_dir
, logmsg
,
881 author
, &ignores
, repo
, import_progress
, NULL
);
888 error
= got_ref_alloc(&branch_ref
, refname
, new_commit_id
);
895 error
= got_ref_write(branch_ref
, repo
);
902 error
= got_object_id_str(&id_str
, new_commit_id
);
909 error
= got_ref_open(&head_ref
, repo
, GOT_REF_HEAD
, 0);
911 if (error
->code
!= GOT_ERR_NOT_REF
) {
917 error
= got_ref_alloc_symref(&head_ref
, GOT_REF_HEAD
,
925 error
= got_ref_write(head_ref
, repo
);
933 printf("Created branch %s with commit %s\n",
934 got_ref_get_name(branch_ref
), id_str
);
937 const struct got_error
*pack_err
=
938 got_repo_pack_fds_close(pack_fds
);
943 const struct got_error
*close_err
= got_repo_close(repo
);
947 if (preserve_logmsg
) {
948 fprintf(stderr
, "%s: log message preserved in %s\n",
949 getprogname(), logmsg_path
);
950 } else if (logmsg_path
&& unlink(logmsg_path
) == -1 && error
== NULL
)
951 error
= got_error_from_errno2("unlink", logmsg_path
);
959 free(gitconfig_path
);
961 got_ref_close(branch_ref
);
963 got_ref_close(head_ref
);
970 fprintf(stderr
, "usage: %s clone [-almqv] [-b branch] [-R reference] "
971 "repository-URL [directory]\n", getprogname());
975 struct got_fetch_progress_arg
{
976 char last_scaled_size
[FMT_SCALED_STRSIZE
];
981 struct got_repository
*repo
;
986 struct got_pathlist_head
*symrefs
;
987 struct got_pathlist_head
*wanted_branches
;
988 struct got_pathlist_head
*wanted_refs
;
992 const char *remote_repo_path
;
994 int fetch_all_branches
;
995 int mirror_references
;
999 /* XXX forward declaration */
1000 static const struct got_error
*
1001 create_config_files(const char *proto
, const char *host
, const char *port
,
1002 const char *remote_repo_path
, const char *git_url
, int fetch_all_branches
,
1003 int mirror_references
, struct got_pathlist_head
*symrefs
,
1004 struct got_pathlist_head
*wanted_branches
,
1005 struct got_pathlist_head
*wanted_refs
, struct got_repository
*repo
);
1007 static const struct got_error
*
1008 fetch_progress(void *arg
, const char *message
, off_t packfile_size
,
1009 int nobj_total
, int nobj_indexed
, int nobj_loose
, int nobj_resolved
)
1011 const struct got_error
*err
= NULL
;
1012 struct got_fetch_progress_arg
*a
= arg
;
1013 char scaled_size
[FMT_SCALED_STRSIZE
];
1014 int p_indexed
, p_resolved
;
1015 int print_size
= 0, print_indexed
= 0, print_resolved
= 0;
1018 * In order to allow a failed clone to be resumed with 'got fetch'
1019 * we try to create configuration files as soon as possible.
1020 * Once the server has sent information about its default branch
1021 * we have all required information.
1023 if (a
->create_configs
&& !a
->configs_created
&&
1024 !TAILQ_EMPTY(a
->config_info
.symrefs
)) {
1025 err
= create_config_files(a
->config_info
.proto
,
1026 a
->config_info
.host
, a
->config_info
.port
,
1027 a
->config_info
.remote_repo_path
,
1028 a
->config_info
.git_url
,
1029 a
->config_info
.fetch_all_branches
,
1030 a
->config_info
.mirror_references
,
1031 a
->config_info
.symrefs
,
1032 a
->config_info
.wanted_branches
,
1033 a
->config_info
.wanted_refs
, a
->repo
);
1036 a
->configs_created
= 1;
1039 if (a
->verbosity
< 0)
1042 if (message
&& message
[0] != '\0') {
1043 printf("\rserver: %s", message
);
1048 if (packfile_size
> 0 || nobj_indexed
> 0) {
1049 if (fmt_scaled(packfile_size
, scaled_size
) == 0 &&
1050 (a
->last_scaled_size
[0] == '\0' ||
1051 strcmp(scaled_size
, a
->last_scaled_size
)) != 0) {
1053 if (strlcpy(a
->last_scaled_size
, scaled_size
,
1054 FMT_SCALED_STRSIZE
) >= FMT_SCALED_STRSIZE
)
1055 return got_error(GOT_ERR_NO_SPACE
);
1057 if (nobj_indexed
> 0) {
1058 p_indexed
= (nobj_indexed
* 100) / nobj_total
;
1059 if (p_indexed
!= a
->last_p_indexed
) {
1060 a
->last_p_indexed
= p_indexed
;
1065 if (nobj_resolved
> 0) {
1066 p_resolved
= (nobj_resolved
* 100) /
1067 (nobj_total
- nobj_loose
);
1068 if (p_resolved
!= a
->last_p_resolved
) {
1069 a
->last_p_resolved
= p_resolved
;
1077 if (print_size
|| print_indexed
|| print_resolved
)
1080 printf("%*s fetched", FMT_SCALED_STRSIZE
- 2, scaled_size
);
1082 printf("; indexing %d%%", p_indexed
);
1084 printf("; resolving deltas %d%%", p_resolved
);
1085 if (print_size
|| print_indexed
|| print_resolved
)
1091 static const struct got_error
*
1092 create_symref(const char *refname
, struct got_reference
*target_ref
,
1093 int verbosity
, struct got_repository
*repo
)
1095 const struct got_error
*err
;
1096 struct got_reference
*head_symref
;
1098 err
= got_ref_alloc_symref(&head_symref
, refname
, target_ref
);
1102 err
= got_ref_write(head_symref
, repo
);
1103 if (err
== NULL
&& verbosity
> 0) {
1104 printf("Created reference %s: %s\n", GOT_REF_HEAD
,
1105 got_ref_get_name(target_ref
));
1107 got_ref_close(head_symref
);
1111 static const struct got_error
*
1112 list_remote_refs(struct got_pathlist_head
*symrefs
,
1113 struct got_pathlist_head
*refs
)
1115 const struct got_error
*err
;
1116 struct got_pathlist_entry
*pe
;
1118 TAILQ_FOREACH(pe
, symrefs
, entry
) {
1119 const char *refname
= pe
->path
;
1120 const char *targetref
= pe
->data
;
1122 printf("%s: %s\n", refname
, targetref
);
1125 TAILQ_FOREACH(pe
, refs
, entry
) {
1126 const char *refname
= pe
->path
;
1127 struct got_object_id
*id
= pe
->data
;
1130 err
= got_object_id_str(&id_str
, id
);
1133 printf("%s: %s\n", refname
, id_str
);
1140 static const struct got_error
*
1141 create_ref(const char *refname
, struct got_object_id
*id
,
1142 int verbosity
, struct got_repository
*repo
)
1144 const struct got_error
*err
= NULL
;
1145 struct got_reference
*ref
;
1148 err
= got_object_id_str(&id_str
, id
);
1152 err
= got_ref_alloc(&ref
, refname
, id
);
1156 err
= got_ref_write(ref
, repo
);
1159 if (err
== NULL
&& verbosity
>= 0)
1160 printf("Created reference %s: %s\n", refname
, id_str
);
1167 match_wanted_ref(const char *refname
, const char *wanted_ref
)
1169 if (strncmp(refname
, "refs/", 5) != 0)
1174 * Prevent fetching of references that won't make any
1175 * sense outside of the remote repository's context.
1177 if (strncmp(refname
, "got/", 4) == 0)
1179 if (strncmp(refname
, "remotes/", 8) == 0)
1182 if (strncmp(wanted_ref
, "refs/", 5) == 0)
1185 /* Allow prefix match. */
1186 if (got_path_is_child(refname
, wanted_ref
, strlen(wanted_ref
)))
1189 /* Allow exact match. */
1190 return (strcmp(refname
, wanted_ref
) == 0);
1194 is_wanted_ref(struct got_pathlist_head
*wanted_refs
, const char *refname
)
1196 struct got_pathlist_entry
*pe
;
1198 TAILQ_FOREACH(pe
, wanted_refs
, entry
) {
1199 if (match_wanted_ref(refname
, pe
->path
))
1206 static const struct got_error
*
1207 create_wanted_ref(const char *refname
, struct got_object_id
*id
,
1208 const char *remote_repo_name
, int verbosity
, struct got_repository
*repo
)
1210 const struct got_error
*err
;
1211 char *remote_refname
;
1213 if (strncmp("refs/", refname
, 5) == 0)
1216 if (asprintf(&remote_refname
, "refs/remotes/%s/%s",
1217 remote_repo_name
, refname
) == -1)
1218 return got_error_from_errno("asprintf");
1220 err
= create_ref(remote_refname
, id
, verbosity
, repo
);
1221 free(remote_refname
);
1225 static const struct got_error
*
1226 create_gotconfig(const char *proto
, const char *host
, const char *port
,
1227 const char *remote_repo_path
, const char *default_branch
,
1228 int fetch_all_branches
, struct got_pathlist_head
*wanted_branches
,
1229 struct got_pathlist_head
*wanted_refs
, int mirror_references
,
1230 struct got_repository
*repo
)
1232 const struct got_error
*err
= NULL
;
1233 char *gotconfig_path
= NULL
;
1234 char *gotconfig
= NULL
;
1235 FILE *gotconfig_file
= NULL
;
1236 const char *branchname
= NULL
;
1237 char *branches
= NULL
, *refs
= NULL
;
1240 if (!fetch_all_branches
&& !TAILQ_EMPTY(wanted_branches
)) {
1241 struct got_pathlist_entry
*pe
;
1242 TAILQ_FOREACH(pe
, wanted_branches
, entry
) {
1244 branchname
= pe
->path
;
1245 if (strncmp(branchname
, "refs/heads/", 11) == 0)
1247 if (asprintf(&s
, "%s\"%s\" ",
1248 branches
? branches
: "", branchname
) == -1) {
1249 err
= got_error_from_errno("asprintf");
1255 } else if (!fetch_all_branches
&& default_branch
) {
1256 branchname
= default_branch
;
1257 if (strncmp(branchname
, "refs/heads/", 11) == 0)
1259 if (asprintf(&branches
, "\"%s\" ", branchname
) == -1) {
1260 err
= got_error_from_errno("asprintf");
1264 if (!TAILQ_EMPTY(wanted_refs
)) {
1265 struct got_pathlist_entry
*pe
;
1266 TAILQ_FOREACH(pe
, wanted_refs
, entry
) {
1268 const char *refname
= pe
->path
;
1269 if (strncmp(refname
, "refs/", 5) == 0)
1271 if (asprintf(&s
, "%s\"%s\" ",
1272 refs
? refs
: "", refname
) == -1) {
1273 err
= got_error_from_errno("asprintf");
1281 /* Create got.conf(5). */
1282 gotconfig_path
= got_repo_get_path_gotconfig(repo
);
1283 if (gotconfig_path
== NULL
) {
1284 err
= got_error_from_errno("got_repo_get_path_gotconfig");
1287 gotconfig_file
= fopen(gotconfig_path
, "ae");
1288 if (gotconfig_file
== NULL
) {
1289 err
= got_error_from_errno2("fopen", gotconfig_path
);
1292 if (asprintf(&gotconfig
,
1297 "\trepository \"%s\"\n"
1303 GOT_FETCH_DEFAULT_REMOTE_NAME
, host
, proto
,
1304 port
? "\tport " : "", port
? port
: "", port
? "\n" : "",
1305 remote_repo_path
, branches
? "\tbranch { " : "",
1306 branches
? branches
: "", branches
? "}\n" : "",
1307 refs
? "\treference { " : "", refs
? refs
: "", refs
? "}\n" : "",
1308 mirror_references
? "\tmirror_references yes\n" : "",
1309 fetch_all_branches
? "\tfetch_all_branches yes\n" : "") == -1) {
1310 err
= got_error_from_errno("asprintf");
1313 n
= fwrite(gotconfig
, 1, strlen(gotconfig
), gotconfig_file
);
1314 if (n
!= strlen(gotconfig
)) {
1315 err
= got_ferror(gotconfig_file
, GOT_ERR_IO
);
1320 if (gotconfig_file
&& fclose(gotconfig_file
) == EOF
&& err
== NULL
)
1321 err
= got_error_from_errno2("fclose", gotconfig_path
);
1322 free(gotconfig_path
);
1327 static const struct got_error
*
1328 create_gitconfig(const char *git_url
, 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 *gitconfig_path
= NULL
;
1335 char *gitconfig
= NULL
;
1336 FILE *gitconfig_file
= NULL
;
1337 char *branches
= NULL
, *refs
= NULL
;
1338 const char *branchname
;
1341 /* Create a config file Git can understand. */
1342 gitconfig_path
= got_repo_get_path_gitconfig(repo
);
1343 if (gitconfig_path
== NULL
) {
1344 err
= got_error_from_errno("got_repo_get_path_gitconfig");
1347 gitconfig_file
= fopen(gitconfig_path
, "ae");
1348 if (gitconfig_file
== NULL
) {
1349 err
= got_error_from_errno2("fopen", gitconfig_path
);
1352 if (fetch_all_branches
) {
1353 if (mirror_references
) {
1354 if (asprintf(&branches
,
1355 "\tfetch = refs/heads/*:refs/heads/*\n") == -1) {
1356 err
= got_error_from_errno("asprintf");
1359 } else if (asprintf(&branches
,
1360 "\tfetch = refs/heads/*:refs/remotes/%s/*\n",
1361 GOT_FETCH_DEFAULT_REMOTE_NAME
) == -1) {
1362 err
= got_error_from_errno("asprintf");
1365 } else if (!TAILQ_EMPTY(wanted_branches
)) {
1366 struct got_pathlist_entry
*pe
;
1367 TAILQ_FOREACH(pe
, wanted_branches
, entry
) {
1369 branchname
= pe
->path
;
1370 if (strncmp(branchname
, "refs/heads/", 11) == 0)
1372 if (mirror_references
) {
1374 "%s\tfetch = refs/heads/%s:refs/heads/%s\n",
1375 branches
? branches
: "",
1376 branchname
, branchname
) == -1) {
1377 err
= got_error_from_errno("asprintf");
1380 } else if (asprintf(&s
,
1381 "%s\tfetch = refs/heads/%s:refs/remotes/%s/%s\n",
1382 branches
? branches
: "",
1383 branchname
, GOT_FETCH_DEFAULT_REMOTE_NAME
,
1384 branchname
) == -1) {
1385 err
= got_error_from_errno("asprintf");
1393 * If the server specified a default branch, use just that one.
1394 * Otherwise fall back to fetching all branches on next fetch.
1396 if (default_branch
) {
1397 branchname
= default_branch
;
1398 if (strncmp(branchname
, "refs/heads/", 11) == 0)
1401 branchname
= "*"; /* fall back to all branches */
1402 if (mirror_references
) {
1403 if (asprintf(&branches
,
1404 "\tfetch = refs/heads/%s:refs/heads/%s\n",
1405 branchname
, branchname
) == -1) {
1406 err
= got_error_from_errno("asprintf");
1409 } else if (asprintf(&branches
,
1410 "\tfetch = refs/heads/%s:refs/remotes/%s/%s\n",
1411 branchname
, GOT_FETCH_DEFAULT_REMOTE_NAME
,
1412 branchname
) == -1) {
1413 err
= got_error_from_errno("asprintf");
1417 if (!TAILQ_EMPTY(wanted_refs
)) {
1418 struct got_pathlist_entry
*pe
;
1419 TAILQ_FOREACH(pe
, wanted_refs
, entry
) {
1421 const char *refname
= pe
->path
;
1422 if (strncmp(refname
, "refs/", 5) == 0)
1424 if (mirror_references
) {
1426 "%s\tfetch = refs/%s:refs/%s\n",
1427 refs
? refs
: "", refname
, refname
) == -1) {
1428 err
= got_error_from_errno("asprintf");
1431 } else if (asprintf(&s
,
1432 "%s\tfetch = refs/%s:refs/remotes/%s/%s\n",
1434 refname
, GOT_FETCH_DEFAULT_REMOTE_NAME
,
1436 err
= got_error_from_errno("asprintf");
1444 if (asprintf(&gitconfig
,
1449 "\tfetch = refs/tags/*:refs/tags/*\n",
1450 GOT_FETCH_DEFAULT_REMOTE_NAME
, git_url
, branches
? branches
: "",
1451 refs
? refs
: "") == -1) {
1452 err
= got_error_from_errno("asprintf");
1455 n
= fwrite(gitconfig
, 1, strlen(gitconfig
), gitconfig_file
);
1456 if (n
!= strlen(gitconfig
)) {
1457 err
= got_ferror(gitconfig_file
, GOT_ERR_IO
);
1461 if (gitconfig_file
&& fclose(gitconfig_file
) == EOF
&& err
== NULL
)
1462 err
= got_error_from_errno2("fclose", gitconfig_path
);
1463 free(gitconfig_path
);
1468 static const struct got_error
*
1469 create_config_files(const char *proto
, const char *host
, const char *port
,
1470 const char *remote_repo_path
, const char *git_url
, int fetch_all_branches
,
1471 int mirror_references
, struct got_pathlist_head
*symrefs
,
1472 struct got_pathlist_head
*wanted_branches
,
1473 struct got_pathlist_head
*wanted_refs
, struct got_repository
*repo
)
1475 const struct got_error
*err
= NULL
;
1476 const char *default_branch
= NULL
;
1477 struct got_pathlist_entry
*pe
;
1480 * If we asked for a set of wanted branches then use the first
1483 if (!TAILQ_EMPTY(wanted_branches
)) {
1484 pe
= TAILQ_FIRST(wanted_branches
);
1485 default_branch
= pe
->path
;
1487 /* First HEAD ref listed by server is the default branch. */
1488 TAILQ_FOREACH(pe
, symrefs
, entry
) {
1489 const char *refname
= pe
->path
;
1490 const char *target
= pe
->data
;
1492 if (strcmp(refname
, GOT_REF_HEAD
) != 0)
1495 default_branch
= target
;
1500 /* Create got.conf(5). */
1501 err
= create_gotconfig(proto
, host
, port
, remote_repo_path
,
1502 default_branch
, fetch_all_branches
, wanted_branches
,
1503 wanted_refs
, mirror_references
, repo
);
1507 /* Create a config file Git can understand. */
1508 return create_gitconfig(git_url
, default_branch
, fetch_all_branches
,
1509 wanted_branches
, wanted_refs
, mirror_references
, repo
);
1512 static const struct got_error
*
1513 cmd_clone(int argc
, char *argv
[])
1515 const struct got_error
*error
= NULL
;
1516 const char *uri
, *dirname
;
1517 char *proto
, *host
, *port
, *repo_name
, *server_path
;
1518 char *default_destdir
= NULL
, *id_str
= NULL
;
1519 const char *repo_path
;
1520 struct got_repository
*repo
= NULL
;
1521 struct got_pathlist_head refs
, symrefs
, wanted_branches
, wanted_refs
;
1522 struct got_pathlist_entry
*pe
;
1523 struct got_object_id
*pack_hash
= NULL
;
1524 int ch
, fetchfd
= -1, fetchstatus
;
1525 pid_t fetchpid
= -1;
1526 struct got_fetch_progress_arg fpa
;
1527 char *git_url
= NULL
;
1528 int verbosity
= 0, fetch_all_branches
= 0, mirror_references
= 0;
1529 int bflag
= 0, list_refs_only
= 0;
1530 int *pack_fds
= NULL
;
1533 TAILQ_INIT(&symrefs
);
1534 TAILQ_INIT(&wanted_branches
);
1535 TAILQ_INIT(&wanted_refs
);
1537 while ((ch
= getopt(argc
, argv
, "ab:lmqR:v")) != -1) {
1540 fetch_all_branches
= 1;
1543 error
= got_pathlist_append(&wanted_branches
,
1553 mirror_references
= 1;
1559 error
= got_pathlist_append(&wanted_refs
,
1567 else if (verbosity
< 3)
1578 if (fetch_all_branches
&& !TAILQ_EMPTY(&wanted_branches
))
1579 option_conflict('a', 'b');
1580 if (list_refs_only
) {
1581 if (!TAILQ_EMPTY(&wanted_branches
))
1582 option_conflict('l', 'b');
1583 if (fetch_all_branches
)
1584 option_conflict('l', 'a');
1585 if (mirror_references
)
1586 option_conflict('l', 'm');
1587 if (!TAILQ_EMPTY(&wanted_refs
))
1588 option_conflict('l', 'R');
1600 error
= got_dial_parse_uri(&proto
, &host
, &port
, &server_path
,
1605 if (asprintf(&git_url
, "%s://%s%s%s%s%s", proto
,
1606 host
, port
? ":" : "", port
? port
: "",
1607 server_path
[0] != '/' ? "/" : "", server_path
) == -1) {
1608 error
= got_error_from_errno("asprintf");
1612 if (strcmp(proto
, "git") == 0) {
1614 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
1615 "sendfd dns inet unveil", NULL
) == -1)
1618 } else if (strcmp(proto
, "git+ssh") == 0 ||
1619 strcmp(proto
, "ssh") == 0) {
1621 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
1622 "sendfd unveil", NULL
) == -1)
1625 } else if (strcmp(proto
, "http") == 0 ||
1626 strcmp(proto
, "git+http") == 0) {
1627 error
= got_error_path(proto
, GOT_ERR_NOT_IMPL
);
1630 error
= got_error_path(proto
, GOT_ERR_BAD_PROTO
);
1633 if (dirname
== NULL
) {
1634 if (asprintf(&default_destdir
, "%s.git", repo_name
) == -1) {
1635 error
= got_error_from_errno("asprintf");
1638 repo_path
= default_destdir
;
1640 repo_path
= dirname
;
1642 if (!list_refs_only
) {
1643 error
= got_path_mkdir(repo_path
);
1645 (!(error
->code
== GOT_ERR_ERRNO
&& errno
== EISDIR
) &&
1646 !(error
->code
== GOT_ERR_ERRNO
&& errno
== EEXIST
)))
1648 if (!got_path_dir_is_empty(repo_path
)) {
1649 error
= got_error_path(repo_path
,
1650 GOT_ERR_DIR_NOT_EMPTY
);
1655 error
= got_dial_apply_unveil(proto
);
1659 error
= apply_unveil(repo_path
, 0, NULL
);
1664 printf("Connecting to %s\n", git_url
);
1666 error
= got_fetch_connect(&fetchpid
, &fetchfd
, proto
, host
, port
,
1667 server_path
, verbosity
);
1671 if (!list_refs_only
) {
1672 error
= got_repo_init(repo_path
, NULL
);
1675 error
= got_repo_pack_fds_open(&pack_fds
);
1678 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
1683 fpa
.last_scaled_size
[0] = '\0';
1684 fpa
.last_p_indexed
= -1;
1685 fpa
.last_p_resolved
= -1;
1686 fpa
.verbosity
= verbosity
;
1687 fpa
.create_configs
= 1;
1688 fpa
.configs_created
= 0;
1690 fpa
.config_info
.symrefs
= &symrefs
;
1691 fpa
.config_info
.wanted_branches
= &wanted_branches
;
1692 fpa
.config_info
.wanted_refs
= &wanted_refs
;
1693 fpa
.config_info
.proto
= proto
;
1694 fpa
.config_info
.host
= host
;
1695 fpa
.config_info
.port
= port
;
1696 fpa
.config_info
.remote_repo_path
= server_path
;
1697 fpa
.config_info
.git_url
= git_url
;
1698 fpa
.config_info
.fetch_all_branches
= fetch_all_branches
;
1699 fpa
.config_info
.mirror_references
= mirror_references
;
1700 error
= got_fetch_pack(&pack_hash
, &refs
, &symrefs
,
1701 GOT_FETCH_DEFAULT_REMOTE_NAME
, mirror_references
,
1702 fetch_all_branches
, &wanted_branches
, &wanted_refs
,
1703 list_refs_only
, verbosity
, fetchfd
, repo
, NULL
, NULL
, bflag
,
1704 fetch_progress
, &fpa
);
1708 if (list_refs_only
) {
1709 error
= list_remote_refs(&symrefs
, &refs
);
1713 if (pack_hash
== NULL
) {
1714 error
= got_error_fmt(GOT_ERR_FETCH_FAILED
, "%s",
1715 "server sent an empty pack file");
1718 error
= got_object_id_str(&id_str
, pack_hash
);
1722 printf("\nFetched %s.pack\n", id_str
);
1725 /* Set up references provided with the pack file. */
1726 TAILQ_FOREACH(pe
, &refs
, entry
) {
1727 const char *refname
= pe
->path
;
1728 struct got_object_id
*id
= pe
->data
;
1729 char *remote_refname
;
1731 if (is_wanted_ref(&wanted_refs
, refname
) &&
1732 !mirror_references
) {
1733 error
= create_wanted_ref(refname
, id
,
1734 GOT_FETCH_DEFAULT_REMOTE_NAME
,
1735 verbosity
- 1, repo
);
1741 error
= create_ref(refname
, id
, verbosity
- 1, repo
);
1745 if (mirror_references
)
1748 if (strncmp("refs/heads/", refname
, 11) != 0)
1751 if (asprintf(&remote_refname
,
1752 "refs/remotes/%s/%s", GOT_FETCH_DEFAULT_REMOTE_NAME
,
1753 refname
+ 11) == -1) {
1754 error
= got_error_from_errno("asprintf");
1757 error
= create_ref(remote_refname
, id
, verbosity
- 1, repo
);
1758 free(remote_refname
);
1763 /* Set the HEAD reference if the server provided one. */
1764 TAILQ_FOREACH(pe
, &symrefs
, entry
) {
1765 struct got_reference
*target_ref
;
1766 const char *refname
= pe
->path
;
1767 const char *target
= pe
->data
;
1768 char *remote_refname
= NULL
, *remote_target
= NULL
;
1770 if (strcmp(refname
, GOT_REF_HEAD
) != 0)
1773 error
= got_ref_open(&target_ref
, repo
, target
, 0);
1775 if (error
->code
== GOT_ERR_NOT_REF
) {
1782 error
= create_symref(refname
, target_ref
, verbosity
, repo
);
1783 got_ref_close(target_ref
);
1787 if (mirror_references
)
1790 if (strncmp("refs/heads/", target
, 11) != 0)
1793 if (asprintf(&remote_refname
,
1794 "refs/remotes/%s/%s", GOT_FETCH_DEFAULT_REMOTE_NAME
,
1796 error
= got_error_from_errno("asprintf");
1799 if (asprintf(&remote_target
,
1800 "refs/remotes/%s/%s", GOT_FETCH_DEFAULT_REMOTE_NAME
,
1801 target
+ 11) == -1) {
1802 error
= got_error_from_errno("asprintf");
1803 free(remote_refname
);
1806 error
= got_ref_open(&target_ref
, repo
, remote_target
, 0);
1808 free(remote_refname
);
1809 free(remote_target
);
1810 if (error
->code
== GOT_ERR_NOT_REF
) {
1816 error
= create_symref(remote_refname
, target_ref
,
1817 verbosity
- 1, repo
);
1818 free(remote_refname
);
1819 free(remote_target
);
1820 got_ref_close(target_ref
);
1826 * We failed to set the HEAD reference. If we asked for
1827 * a set of wanted branches use the first of one of those
1828 * which could be fetched instead.
1830 TAILQ_FOREACH(pe
, &wanted_branches
, entry
) {
1831 const char *target
= pe
->path
;
1832 struct got_reference
*target_ref
;
1834 error
= got_ref_open(&target_ref
, repo
, target
, 0);
1836 if (error
->code
== GOT_ERR_NOT_REF
) {
1843 error
= create_symref(GOT_REF_HEAD
, target_ref
,
1845 got_ref_close(target_ref
);
1851 if (!fpa
.configs_created
&& pe
!= NULL
) {
1852 error
= create_config_files(fpa
.config_info
.proto
,
1853 fpa
.config_info
.host
, fpa
.config_info
.port
,
1854 fpa
.config_info
.remote_repo_path
,
1855 fpa
.config_info
.git_url
,
1856 fpa
.config_info
.fetch_all_branches
,
1857 fpa
.config_info
.mirror_references
,
1858 fpa
.config_info
.symrefs
,
1859 fpa
.config_info
.wanted_branches
,
1860 fpa
.config_info
.wanted_refs
, fpa
.repo
);
1867 printf("Created %s repository '%s'\n",
1868 mirror_references
? "mirrored" : "cloned", repo_path
);
1871 const struct got_error
*pack_err
=
1872 got_repo_pack_fds_close(pack_fds
);
1877 if (kill(fetchpid
, SIGTERM
) == -1)
1878 error
= got_error_from_errno("kill");
1879 if (waitpid(fetchpid
, &fetchstatus
, 0) == -1 && error
== NULL
)
1880 error
= got_error_from_errno("waitpid");
1882 if (fetchfd
!= -1 && close(fetchfd
) == -1 && error
== NULL
)
1883 error
= got_error_from_errno("close");
1885 const struct got_error
*close_err
= got_repo_close(repo
);
1889 got_pathlist_free(&refs
, GOT_PATHLIST_FREE_ALL
);
1890 got_pathlist_free(&symrefs
, GOT_PATHLIST_FREE_ALL
);
1891 got_pathlist_free(&wanted_branches
, GOT_PATHLIST_FREE_NONE
);
1892 got_pathlist_free(&wanted_refs
, GOT_PATHLIST_FREE_NONE
);
1899 free(default_destdir
);
1904 static const struct got_error
*
1905 update_ref(struct got_reference
*ref
, struct got_object_id
*new_id
,
1906 int replace_tags
, int verbosity
, struct got_repository
*repo
)
1908 const struct got_error
*err
= NULL
;
1909 char *new_id_str
= NULL
;
1910 struct got_object_id
*old_id
= NULL
;
1912 err
= got_object_id_str(&new_id_str
, new_id
);
1916 if (!replace_tags
&&
1917 strncmp(got_ref_get_name(ref
), "refs/tags/", 10) == 0) {
1918 err
= got_ref_resolve(&old_id
, repo
, ref
);
1921 if (got_object_id_cmp(old_id
, new_id
) == 0)
1923 if (verbosity
>= 0) {
1924 printf("Rejecting update of existing tag %s: %s\n",
1925 got_ref_get_name(ref
), new_id_str
);
1930 if (got_ref_is_symbolic(ref
)) {
1931 if (verbosity
>= 0) {
1932 printf("Replacing reference %s: %s\n",
1933 got_ref_get_name(ref
),
1934 got_ref_get_symref_target(ref
));
1936 err
= got_ref_change_symref_to_ref(ref
, new_id
);
1939 err
= got_ref_write(ref
, repo
);
1943 err
= got_ref_resolve(&old_id
, repo
, ref
);
1946 if (got_object_id_cmp(old_id
, new_id
) == 0)
1949 err
= got_ref_change_ref(ref
, new_id
);
1952 err
= got_ref_write(ref
, repo
);
1958 printf("Updated %s: %s\n", got_ref_get_name(ref
),
1966 static const struct got_error
*
1967 update_wanted_ref(const char *refname
, struct got_object_id
*id
,
1968 const char *remote_repo_name
, int verbosity
, struct got_repository
*repo
)
1970 const struct got_error
*err
, *unlock_err
;
1971 char *remote_refname
;
1972 struct got_reference
*ref
;
1974 if (strncmp("refs/", refname
, 5) == 0)
1977 if (asprintf(&remote_refname
, "refs/remotes/%s/%s",
1978 remote_repo_name
, refname
) == -1)
1979 return got_error_from_errno("asprintf");
1981 err
= got_ref_open(&ref
, repo
, remote_refname
, 1);
1983 if (err
->code
!= GOT_ERR_NOT_REF
)
1985 err
= create_ref(remote_refname
, id
, verbosity
, repo
);
1987 err
= update_ref(ref
, id
, 0, verbosity
, repo
);
1988 unlock_err
= got_ref_unlock(ref
);
1989 if (unlock_err
&& err
== NULL
)
1994 free(remote_refname
);
1999 usage_checkout(void)
2001 fprintf(stderr
, "usage: %s checkout [-Eq] [-b branch] [-c commit] "
2002 "[-p path-prefix] repository-path [work-tree-path]\n",
2008 show_worktree_base_ref_warning(void)
2010 fprintf(stderr
, "%s: warning: could not create a reference "
2011 "to the work tree's base commit; the commit could be "
2012 "garbage-collected by Git or 'gotadmin cleanup'; making the "
2013 "repository writable and running 'got update' will prevent this\n",
2017 struct got_checkout_progress_arg
{
2018 const char *worktree_path
;
2019 int had_base_commit_ref_error
;
2023 static const struct got_error
*
2024 checkout_progress(void *arg
, unsigned char status
, const char *path
)
2026 struct got_checkout_progress_arg
*a
= arg
;
2028 /* Base commit bump happens silently. */
2029 if (status
== GOT_STATUS_BUMP_BASE
)
2032 if (status
== GOT_STATUS_BASE_REF_ERR
) {
2033 a
->had_base_commit_ref_error
= 1;
2037 while (path
[0] == '/')
2040 if (a
->verbosity
>= 0)
2041 printf("%c %s/%s\n", status
, a
->worktree_path
, path
);
2046 static const struct got_error
*
2047 check_cancelled(void *arg
)
2049 if (sigint_received
|| sigpipe_received
)
2050 return got_error(GOT_ERR_CANCELLED
);
2054 static const struct got_error
*
2055 check_linear_ancestry(struct got_object_id
*commit_id
,
2056 struct got_object_id
*base_commit_id
, int allow_forwards_in_time_only
,
2057 struct got_repository
*repo
)
2059 const struct got_error
*err
= NULL
;
2060 struct got_object_id
*yca_id
;
2062 err
= got_commit_graph_find_youngest_common_ancestor(&yca_id
,
2063 commit_id
, base_commit_id
, 1, repo
, check_cancelled
, NULL
);
2068 return got_error(GOT_ERR_ANCESTRY
);
2071 * Require a straight line of history between the target commit
2072 * and the work tree's base commit.
2074 * Non-linear situations such as this require a rebase:
2076 * (commit) D F (base_commit)
2084 * 'got update' only handles linear cases:
2085 * Update forwards in time: A (base/yca) - B - C - D (commit)
2086 * Update backwards in time: D (base) - C - B - A (commit/yca)
2088 if (allow_forwards_in_time_only
) {
2089 if (got_object_id_cmp(base_commit_id
, yca_id
) != 0)
2090 return got_error(GOT_ERR_ANCESTRY
);
2091 } else if (got_object_id_cmp(commit_id
, yca_id
) != 0 &&
2092 got_object_id_cmp(base_commit_id
, yca_id
) != 0)
2093 return got_error(GOT_ERR_ANCESTRY
);
2099 static const struct got_error
*
2100 check_same_branch(struct got_object_id
*commit_id
,
2101 struct got_reference
*head_ref
, struct got_repository
*repo
)
2103 const struct got_error
*err
= NULL
;
2104 struct got_commit_graph
*graph
= NULL
;
2105 struct got_object_id
*head_commit_id
= NULL
;
2107 err
= got_ref_resolve(&head_commit_id
, repo
, head_ref
);
2111 if (got_object_id_cmp(head_commit_id
, commit_id
) == 0)
2114 err
= got_commit_graph_open(&graph
, "/", 1);
2118 err
= got_commit_graph_iter_start(graph
, head_commit_id
, repo
,
2119 check_cancelled
, NULL
);
2124 struct got_object_id id
;
2126 err
= got_commit_graph_iter_next(&id
, graph
, repo
,
2127 check_cancelled
, NULL
);
2129 if (err
->code
== GOT_ERR_ITER_COMPLETED
)
2130 err
= got_error(GOT_ERR_ANCESTRY
);
2134 if (got_object_id_cmp(&id
, commit_id
) == 0)
2139 got_commit_graph_close(graph
);
2140 free(head_commit_id
);
2144 static const struct got_error
*
2145 checkout_ancestry_error(struct got_reference
*ref
, const char *commit_id_str
)
2147 static char msg
[512];
2148 const char *branch_name
;
2150 if (got_ref_is_symbolic(ref
))
2151 branch_name
= got_ref_get_symref_target(ref
);
2153 branch_name
= got_ref_get_name(ref
);
2155 if (strncmp("refs/heads/", branch_name
, 11) == 0)
2158 snprintf(msg
, sizeof(msg
),
2159 "target commit is not contained in branch '%s'; "
2160 "the branch to use must be specified with -b; "
2161 "if necessary a new branch can be created for "
2162 "this commit with 'got branch -c %s BRANCH_NAME'",
2163 branch_name
, commit_id_str
);
2165 return got_error_msg(GOT_ERR_ANCESTRY
, msg
);
2168 static const struct got_error
*
2169 cmd_checkout(int argc
, char *argv
[])
2171 const struct got_error
*error
= NULL
;
2172 struct got_repository
*repo
= NULL
;
2173 struct got_reference
*head_ref
= NULL
, *ref
= NULL
;
2174 struct got_worktree
*worktree
= NULL
;
2175 char *repo_path
= NULL
;
2176 char *worktree_path
= NULL
;
2177 const char *path_prefix
= "";
2178 const char *branch_name
= GOT_REF_HEAD
, *refname
= NULL
;
2179 char *commit_id_str
= NULL
;
2180 struct got_object_id
*commit_id
= NULL
;
2182 int ch
, same_path_prefix
, allow_nonempty
= 0, verbosity
= 0;
2183 struct got_pathlist_head paths
;
2184 struct got_checkout_progress_arg cpa
;
2185 int *pack_fds
= NULL
;
2190 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
2191 "unveil", NULL
) == -1)
2195 while ((ch
= getopt(argc
, argv
, "b:c:Ep:q")) != -1) {
2198 branch_name
= optarg
;
2201 commit_id_str
= strdup(optarg
);
2202 if (commit_id_str
== NULL
)
2203 return got_error_from_errno("strdup");
2209 path_prefix
= optarg
;
2224 char *base
, *dotgit
;
2226 repo_path
= realpath(argv
[0], NULL
);
2227 if (repo_path
== NULL
)
2228 return got_error_from_errno2("realpath", argv
[0]);
2229 cwd
= getcwd(NULL
, 0);
2231 error
= got_error_from_errno("getcwd");
2238 error
= got_path_basename(&base
, path
);
2241 dotgit
= strstr(base
, ".git");
2244 if (asprintf(&worktree_path
, "%s/%s", cwd
, base
) == -1) {
2245 error
= got_error_from_errno("asprintf");
2250 } else if (argc
== 2) {
2251 repo_path
= realpath(argv
[0], NULL
);
2252 if (repo_path
== NULL
) {
2253 error
= got_error_from_errno2("realpath", argv
[0]);
2256 worktree_path
= realpath(argv
[1], NULL
);
2257 if (worktree_path
== NULL
) {
2258 if (errno
!= ENOENT
) {
2259 error
= got_error_from_errno2("realpath",
2263 worktree_path
= strdup(argv
[1]);
2264 if (worktree_path
== NULL
) {
2265 error
= got_error_from_errno("strdup");
2272 got_path_strip_trailing_slashes(repo_path
);
2273 got_path_strip_trailing_slashes(worktree_path
);
2275 error
= got_repo_pack_fds_open(&pack_fds
);
2279 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
2283 /* Pre-create work tree path for unveil(2) */
2284 error
= got_path_mkdir(worktree_path
);
2286 if (!(error
->code
== GOT_ERR_ERRNO
&& errno
== EISDIR
) &&
2287 !(error
->code
== GOT_ERR_ERRNO
&& errno
== EEXIST
))
2289 if (!allow_nonempty
&&
2290 !got_path_dir_is_empty(worktree_path
)) {
2291 error
= got_error_path(worktree_path
,
2292 GOT_ERR_DIR_NOT_EMPTY
);
2297 error
= apply_unveil(got_repo_get_path(repo
), 0, worktree_path
);
2301 error
= got_ref_open(&head_ref
, repo
, branch_name
, 0);
2305 error
= got_worktree_init(worktree_path
, head_ref
, path_prefix
,
2306 GOT_WORKTREE_CVG_DIR
, repo
);
2307 if (error
!= NULL
&& !(error
->code
== GOT_ERR_ERRNO
&& errno
== EEXIST
))
2310 error
= got_worktree_open(&worktree
, worktree_path
, GOT_WORKTREE_CVG_DIR
);
2314 error
= got_worktree_match_path_prefix(&same_path_prefix
, worktree
,
2318 if (!same_path_prefix
) {
2319 error
= got_error(GOT_ERR_PATH_PREFIX
);
2323 if (commit_id_str
) {
2324 struct got_reflist_head refs
;
2326 error
= got_ref_list(&refs
, repo
, NULL
, got_ref_cmp_by_name
,
2330 error
= got_repo_match_object_id(&commit_id
, NULL
,
2331 commit_id_str
, GOT_OBJ_TYPE_COMMIT
, &refs
, repo
);
2332 got_ref_list_free(&refs
);
2335 error
= check_linear_ancestry(commit_id
,
2336 got_worktree_get_base_commit_id(worktree
), 0, repo
);
2337 if (error
!= NULL
) {
2338 if (error
->code
== GOT_ERR_ANCESTRY
) {
2339 error
= checkout_ancestry_error(
2340 head_ref
, commit_id_str
);
2344 error
= check_same_branch(commit_id
, head_ref
, repo
);
2346 if (error
->code
== GOT_ERR_ANCESTRY
) {
2347 error
= checkout_ancestry_error(
2348 head_ref
, commit_id_str
);
2352 error
= got_worktree_set_base_commit_id(worktree
, repo
,
2356 /* Expand potentially abbreviated commit ID string. */
2357 free(commit_id_str
);
2358 error
= got_object_id_str(&commit_id_str
, commit_id
);
2362 commit_id
= got_object_id_dup(
2363 got_worktree_get_base_commit_id(worktree
));
2364 if (commit_id
== NULL
) {
2365 error
= got_error_from_errno("got_object_id_dup");
2368 error
= got_object_id_str(&commit_id_str
, commit_id
);
2373 error
= got_pathlist_append(&paths
, "", NULL
);
2376 cpa
.worktree_path
= worktree_path
;
2377 cpa
.had_base_commit_ref_error
= 0;
2378 cpa
.verbosity
= verbosity
;
2379 error
= got_worktree_checkout_files(worktree
, &paths
, repo
,
2380 checkout_progress
, &cpa
, check_cancelled
, NULL
);
2384 if (got_ref_is_symbolic(head_ref
)) {
2385 error
= got_ref_resolve_symbolic(&ref
, repo
, head_ref
);
2388 refname
= got_ref_get_name(ref
);
2390 refname
= got_ref_get_name(head_ref
);
2391 printf("Checked out %s: %s\n", refname
, commit_id_str
);
2392 printf("Now shut up and hack\n");
2393 if (cpa
.had_base_commit_ref_error
)
2394 show_worktree_base_ref_warning();
2397 const struct got_error
*pack_err
=
2398 got_repo_pack_fds_close(pack_fds
);
2403 got_ref_close(head_ref
);
2407 const struct got_error
*close_err
= got_repo_close(repo
);
2411 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_NONE
);
2412 free(commit_id_str
);
2415 free(worktree_path
);
2420 struct got_update_progress_arg
{
2432 print_update_progress_stats(struct got_update_progress_arg
*upa
)
2434 if (!upa
->did_something
)
2437 if (upa
->conflicts
> 0)
2438 printf("Files with new merge conflicts: %d\n", upa
->conflicts
);
2439 if (upa
->obstructed
> 0)
2440 printf("File paths obstructed by a non-regular file: %d\n",
2442 if (upa
->not_updated
> 0)
2443 printf("Files not updated because of existing merge "
2444 "conflicts: %d\n", upa
->not_updated
);
2448 * The meaning of some status codes differs between merge-style operations and
2449 * update operations. For example, the ! status code means "file was missing"
2450 * if changes were merged into the work tree, and "missing file was restored"
2451 * if the work tree was updated. This function should be used by any operation
2452 * which merges changes into the work tree without updating the work tree.
2455 print_merge_progress_stats(struct got_update_progress_arg
*upa
)
2457 if (!upa
->did_something
)
2460 if (upa
->conflicts
> 0)
2461 printf("Files with new merge conflicts: %d\n", upa
->conflicts
);
2462 if (upa
->obstructed
> 0)
2463 printf("File paths obstructed by a non-regular file: %d\n",
2465 if (upa
->missing
> 0)
2466 printf("Files which had incoming changes but could not be "
2467 "found in the work tree: %d\n", upa
->missing
);
2468 if (upa
->not_deleted
> 0)
2469 printf("Files not deleted due to differences in deleted "
2470 "content: %d\n", upa
->not_deleted
);
2471 if (upa
->unversioned
> 0)
2472 printf("Files not merged because an unversioned file was "
2473 "found in the work tree: %d\n", upa
->unversioned
);
2479 fprintf(stderr
, "usage: %s update [-qtvX] [-c commit] [-r remote] "
2480 "[path ...]\n", getprogname());
2484 static const struct got_error
*
2485 update_progress(void *arg
, unsigned char status
, const char *path
)
2487 struct got_update_progress_arg
*upa
= arg
;
2489 if (status
== GOT_STATUS_EXISTS
||
2490 status
== GOT_STATUS_BASE_REF_ERR
)
2493 upa
->did_something
= 1;
2495 /* Base commit bump happens silently. */
2496 if (status
== GOT_STATUS_BUMP_BASE
)
2499 if (status
== GOT_STATUS_CONFLICT
)
2501 if (status
== GOT_STATUS_OBSTRUCTED
)
2503 if (status
== GOT_STATUS_CANNOT_UPDATE
)
2505 if (status
== GOT_STATUS_MISSING
)
2507 if (status
== GOT_STATUS_CANNOT_DELETE
)
2509 if (status
== GOT_STATUS_UNVERSIONED
)
2512 while (path
[0] == '/')
2514 if (upa
->verbosity
>= 0)
2515 printf("%c %s\n", status
, path
);
2520 static const struct got_error
*
2521 check_rebase_or_histedit_in_progress(struct got_worktree
*worktree
)
2523 const struct got_error
*err
;
2526 err
= got_worktree_rebase_in_progress(&in_progress
, worktree
);
2530 return got_error(GOT_ERR_REBASING
);
2532 err
= got_worktree_histedit_in_progress(&in_progress
, worktree
);
2536 return got_error(GOT_ERR_HISTEDIT_BUSY
);
2541 static const struct got_error
*
2542 check_merge_in_progress(struct got_worktree
*worktree
,
2543 struct got_repository
*repo
)
2545 const struct got_error
*err
;
2548 err
= got_worktree_merge_in_progress(&in_progress
, worktree
, repo
);
2552 return got_error(GOT_ERR_MERGE_BUSY
);
2557 static const struct got_error
*
2558 get_worktree_paths_from_argv(struct got_pathlist_head
*paths
, int argc
,
2559 char *argv
[], struct got_worktree
*worktree
)
2561 const struct got_error
*err
= NULL
;
2563 struct got_pathlist_entry
*new;
2569 return got_error_from_errno("strdup");
2570 return got_pathlist_append(paths
, path
, NULL
);
2573 for (i
= 0; i
< argc
; i
++) {
2574 err
= got_worktree_resolve_path(&path
, worktree
, argv
[i
]);
2577 err
= got_pathlist_insert(&new, paths
, path
, NULL
);
2578 if (err
|| new == NULL
/* duplicate */) {
2588 static const struct got_error
*
2589 wrap_not_worktree_error(const struct got_error
*orig_err
,
2590 const char *cmdname
, const char *path
)
2592 const struct got_error
*err
;
2593 struct got_repository
*repo
;
2594 static char msg
[512];
2595 int *pack_fds
= NULL
;
2597 err
= got_repo_pack_fds_open(&pack_fds
);
2601 err
= got_repo_open(&repo
, path
, NULL
, pack_fds
);
2605 snprintf(msg
, sizeof(msg
),
2606 "'got %s' needs a work tree in addition to a git repository\n"
2607 "Work trees can be checked out from this Git repository with "
2609 "The got(1) manual page contains more information.", cmdname
);
2610 err
= got_error_msg(GOT_ERR_NOT_WORKTREE
, msg
);
2612 const struct got_error
*close_err
= got_repo_close(repo
);
2617 const struct got_error
*pack_err
=
2618 got_repo_pack_fds_close(pack_fds
);
2625 static const struct got_error
*
2626 cmd_update(int argc
, char *argv
[])
2628 const struct got_error
*error
= NULL
, *unlock_err
;
2629 char *worktree_path
= NULL
;
2630 const char *repo_path
= NULL
;
2631 const char *remote_name
= NULL
;
2632 char *proto
= NULL
, *host
= NULL
, *port
= NULL
;
2633 char *repo_name
= NULL
, *server_path
= NULL
;
2634 const struct got_remote_repo
*remotes
, *remote
= NULL
;
2636 char *id_str
= NULL
;
2637 struct got_repository
*repo
= NULL
;
2638 struct got_worktree
*worktree
= NULL
;
2639 const struct got_gotconfig
*repo_conf
= NULL
, *worktree_conf
= NULL
;
2640 struct got_pathlist_head paths
, refs
, symrefs
;
2641 struct got_pathlist_head wanted_branches
, wanted_refs
;
2642 struct got_pathlist_entry
*pe
;
2643 struct got_reflist_head remote_refs
;
2644 struct got_reflist_entry
*re
;
2645 struct got_object_id
*pack_hash
= NULL
;
2646 int i
, ch
, fetchfd
= -1, fetchstatus
;
2647 pid_t fetchpid
= -1;
2648 struct got_fetch_progress_arg fpa
;
2649 struct got_update_progress_arg upa
;
2651 int delete_remote
= 0;
2652 int replace_tags
= 0;
2653 int *pack_fds
= NULL
;
2654 const char *remote_head
= NULL
, *worktree_branch
= NULL
;
2655 struct got_object_id
*commit_id
= NULL
;
2656 char *commit_id_str
= NULL
;
2657 const char *refname
;
2658 struct got_reference
*head_ref
= NULL
;
2662 TAILQ_INIT(&symrefs
);
2663 TAILQ_INIT(&remote_refs
);
2664 TAILQ_INIT(&wanted_branches
);
2665 TAILQ_INIT(&wanted_refs
);
2667 while ((ch
= getopt(argc
, argv
, "c:qr:vX")) != -1) {
2670 commit_id_str
= strdup(optarg
);
2671 if (commit_id_str
== NULL
)
2672 return got_error_from_errno("strdup");
2681 remote_name
= optarg
;
2686 else if (verbosity
< 3)
2700 if (delete_remote
) {
2702 option_conflict('X', 't');
2703 if (remote_name
== NULL
)
2704 errx(1, "-X option requires a remote name");
2706 if (remote_name
== NULL
)
2707 remote_name
= GOT_FETCH_DEFAULT_REMOTE_NAME
;
2709 worktree_path
= getcwd(NULL
, 0);
2710 if (worktree_path
== NULL
) {
2711 error
= got_error_from_errno("getcwd");
2714 error
= got_worktree_open(&worktree
, worktree_path
, GOT_WORKTREE_CVG_DIR
);
2716 if (error
->code
== GOT_ERR_NOT_WORKTREE
)
2717 error
= wrap_not_worktree_error(error
, "update",
2721 repo_path
= got_worktree_get_repo_path(worktree
);
2723 error
= got_repo_pack_fds_open(&pack_fds
);
2727 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
2731 error
= check_rebase_or_histedit_in_progress(worktree
);
2734 error
= check_merge_in_progress(worktree
, repo
);
2738 error
= get_worktree_paths_from_argv(&paths
, argc
, argv
, worktree
);
2742 worktree_conf
= got_worktree_get_gotconfig(worktree
);
2743 if (worktree_conf
) {
2744 got_gotconfig_get_remotes(&nremotes
, &remotes
,
2746 for (i
= 0; i
< nremotes
; i
++) {
2747 if (strcmp(remotes
[i
].name
, remote_name
) == 0) {
2748 remote
= &remotes
[i
];
2754 if (remote
== NULL
) {
2755 repo_conf
= got_repo_get_gotconfig(repo
);
2757 got_gotconfig_get_remotes(&nremotes
, &remotes
,
2759 for (i
= 0; i
< nremotes
; i
++) {
2760 if (strcmp(remotes
[i
].name
, remote_name
) == 0) {
2761 remote
= &remotes
[i
];
2767 if (remote
== NULL
) {
2768 got_repo_get_gitconfig_remotes(&nremotes
, &remotes
, repo
);
2769 for (i
= 0; i
< nremotes
; i
++) {
2770 if (strcmp(remotes
[i
].name
, remote_name
) == 0) {
2771 remote
= &remotes
[i
];
2776 if (remote
== NULL
) {
2777 error
= got_error_path(remote_name
, GOT_ERR_NO_REMOTE
);
2781 error
= got_dial_parse_uri(&proto
, &host
, &port
, &server_path
,
2782 &repo_name
, remote
->fetch_url
);
2786 if (strcmp(proto
, "git") == 0) {
2788 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
2789 "sendfd dns inet unveil", NULL
) == -1)
2792 } else if (strcmp(proto
, "git+ssh") == 0 ||
2793 strcmp(proto
, "ssh") == 0) {
2795 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
2796 "sendfd unveil", NULL
) == -1)
2799 } else if (strcmp(proto
, "http") == 0 ||
2800 strcmp(proto
, "git+http") == 0) {
2801 error
= got_error_path(proto
, GOT_ERR_NOT_IMPL
);
2804 error
= got_error_path(proto
, GOT_ERR_BAD_PROTO
);
2808 error
= got_dial_apply_unveil(proto
);
2812 error
= apply_unveil(got_repo_get_path(repo
), 0,
2813 got_worktree_get_root_path(worktree
));
2817 if (verbosity
>= 0) {
2818 printf("Connecting to \"%s\" %s://%s%s%s%s%s\n",
2819 remote
->name
, proto
, host
,
2820 port
? ":" : "", port
? port
: "",
2821 *server_path
== '/' ? "" : "/", server_path
);
2824 error
= got_fetch_connect(&fetchpid
, &fetchfd
, proto
, host
, port
,
2825 server_path
, verbosity
);
2830 * If set, get this remote's HEAD ref target so
2831 * if it has changed on the server we can fetch it.
2833 error
= got_ref_list(&remote_refs
, repo
, "refs/remotes",
2834 got_ref_cmp_by_name
, repo
);
2838 TAILQ_FOREACH(re
, &remote_refs
, entry
) {
2839 const char *remote_refname
, *remote_target
;
2840 size_t remote_name_len
;
2842 if (!got_ref_is_symbolic(re
->ref
))
2845 remote_name_len
= strlen(remote
->name
);
2846 remote_refname
= got_ref_get_name(re
->ref
);
2848 /* we only want refs/remotes/$remote->name/HEAD */
2849 if (strncmp(remote_refname
+ 13, remote
->name
,
2850 remote_name_len
) != 0)
2853 if (strcmp(remote_refname
+ remote_name_len
+ 14,
2858 * Take the name itself because we already
2859 * only match with refs/heads/ in fetch_pack().
2861 remote_target
= got_ref_get_symref_target(re
->ref
);
2862 remote_head
= remote_target
+ remote_name_len
+ 14;
2866 refname
= got_worktree_get_head_ref_name(worktree
);
2867 if (strncmp(refname
, "refs/heads/", 11) == 0)
2868 worktree_branch
= refname
;
2870 fpa
.last_scaled_size
[0] = '\0';
2871 fpa
.last_p_indexed
= -1;
2872 fpa
.last_p_resolved
= -1;
2873 fpa
.verbosity
= verbosity
;
2875 fpa
.create_configs
= 0;
2876 fpa
.configs_created
= 0;
2877 memset(&fpa
.config_info
, 0, sizeof(fpa
.config_info
));
2879 error
= got_fetch_pack(&pack_hash
, &refs
, &symrefs
, remote
->name
,
2880 remote
->mirror_references
, 0, &wanted_branches
, &wanted_refs
,
2881 0, verbosity
, fetchfd
, repo
, worktree_branch
, remote_head
,
2882 0, fetch_progress
, &fpa
);
2886 if (pack_hash
!= NULL
&& verbosity
>= 0) {
2887 error
= got_object_id_str(&id_str
, pack_hash
);
2890 printf("\nFetched %s.pack\n", id_str
);
2895 /* Update references provided with the pack file. */
2896 TAILQ_FOREACH(pe
, &refs
, entry
) {
2897 const char *refname
= pe
->path
;
2898 struct got_object_id
*id
= pe
->data
;
2899 struct got_reference
*ref
;
2901 if (is_wanted_ref(&wanted_refs
, refname
)) {
2902 error
= update_wanted_ref(refname
, id
,
2903 remote
->name
, verbosity
, repo
);
2909 error
= got_ref_open(&ref
, repo
, refname
, 1);
2911 if (error
->code
!= GOT_ERR_NOT_REF
)
2913 error
= create_ref(refname
, id
, verbosity
,
2918 error
= update_ref(ref
, id
, replace_tags
,
2920 unlock_err
= got_ref_unlock(ref
);
2921 if (unlock_err
&& error
== NULL
)
2929 /* Update worktree */
2930 error
= got_ref_open(&head_ref
, repo
,
2931 got_worktree_get_head_ref_name(worktree
), 0);
2934 if (commit_id_str
== NULL
) {
2935 error
= got_ref_resolve(&commit_id
, repo
, head_ref
);
2938 error
= got_object_id_str(&commit_id_str
, commit_id
);
2942 struct got_reflist_head refs
;
2944 error
= got_ref_list(&refs
, repo
, NULL
, got_ref_cmp_by_name
,
2948 error
= got_repo_match_object_id(&commit_id
, NULL
,
2949 commit_id_str
, GOT_OBJ_TYPE_COMMIT
, &refs
, repo
);
2950 got_ref_list_free(&refs
);
2951 free(commit_id_str
);
2952 commit_id_str
= NULL
;
2955 error
= got_object_id_str(&commit_id_str
, commit_id
);
2961 error
= check_linear_ancestry(commit_id
,
2962 got_worktree_get_base_commit_id(worktree
), 0, repo
);
2963 if (error
!= NULL
) {
2964 if (error
->code
== GOT_ERR_ANCESTRY
)
2965 error
= got_error(GOT_ERR_BRANCH_MOVED
);
2968 error
= check_same_branch(commit_id
, head_ref
, repo
);
2972 if (got_object_id_cmp(got_worktree_get_base_commit_id(worktree
),
2974 error
= got_worktree_set_base_commit_id(worktree
, repo
,
2980 memset(&upa
, 0, sizeof(upa
));
2981 upa
.verbosity
= verbosity
;
2982 error
= got_worktree_checkout_files(worktree
, &paths
, repo
,
2983 update_progress
, &upa
, check_cancelled
, NULL
);
2987 if (upa
.did_something
) {
2988 printf("Updated to %s: %s\n",
2989 got_worktree_get_head_ref_name(worktree
), commit_id_str
);
2991 printf("Already up-to-date\n");
2993 print_update_progress_stats(&upa
);
2996 if (kill(fetchpid
, SIGTERM
) == -1)
2997 error
= got_error_from_errno("kill");
2998 if (waitpid(fetchpid
, &fetchstatus
, 0) == -1 && error
== NULL
)
2999 error
= got_error_from_errno("waitpid");
3001 if (fetchfd
!= -1 && close(fetchfd
) == -1 && error
== NULL
)
3002 error
= got_error_from_errno("close");
3004 const struct got_error
*close_err
= got_repo_close(repo
);
3009 got_worktree_close(worktree
);
3011 const struct got_error
*pack_err
=
3012 got_repo_pack_fds_close(pack_fds
);
3016 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_PATH
);
3017 got_pathlist_free(&refs
, GOT_PATHLIST_FREE_ALL
);
3018 got_pathlist_free(&symrefs
, GOT_PATHLIST_FREE_ALL
);
3019 got_pathlist_free(&wanted_branches
, GOT_PATHLIST_FREE_NONE
);
3020 got_pathlist_free(&wanted_refs
, GOT_PATHLIST_FREE_NONE
);
3021 got_ref_list_free(&remote_refs
);
3023 free(worktree_path
);
3031 free(commit_id_str
);
3035 static const struct got_error
*
3036 diff_blobs(struct got_object_id
*blob_id1
, struct got_object_id
*blob_id2
,
3037 const char *path
, int diff_context
, int ignore_whitespace
,
3038 int force_text_diff
, struct got_diffstat_cb_arg
*dsa
,
3039 struct got_repository
*repo
, FILE *outfile
)
3041 const struct got_error
*err
= NULL
;
3042 struct got_blob_object
*blob1
= NULL
, *blob2
= NULL
;
3043 FILE *f1
= NULL
, *f2
= NULL
;
3044 int fd1
= -1, fd2
= -1;
3046 fd1
= got_opentempfd();
3048 return got_error_from_errno("got_opentempfd");
3049 fd2
= got_opentempfd();
3051 err
= got_error_from_errno("got_opentempfd");
3056 err
= got_object_open_as_blob(&blob1
, repo
, blob_id1
, 8192,
3062 err
= got_object_open_as_blob(&blob2
, repo
, blob_id2
, 8192, fd2
);
3066 f1
= got_opentemp();
3068 err
= got_error_from_errno("got_opentemp");
3071 f2
= got_opentemp();
3073 err
= got_error_from_errno("got_opentemp");
3077 while (path
[0] == '/')
3079 err
= got_diff_blob(NULL
, NULL
, blob1
, blob2
, f1
, f2
, path
, path
,
3080 GOT_DIFF_ALGORITHM_PATIENCE
, diff_context
, ignore_whitespace
,
3081 force_text_diff
, dsa
, outfile
);
3083 if (fd1
!= -1 && close(fd1
) == -1 && err
== NULL
)
3084 err
= got_error_from_errno("close");
3086 got_object_blob_close(blob1
);
3087 if (fd2
!= -1 && close(fd2
) == -1 && err
== NULL
)
3088 err
= got_error_from_errno("close");
3090 got_object_blob_close(blob2
);
3091 if (f1
&& fclose(f1
) == EOF
&& err
== NULL
)
3092 err
= got_error_from_errno("fclose");
3093 if (f2
&& fclose(f2
) == EOF
&& err
== NULL
)
3094 err
= got_error_from_errno("fclose");
3098 static const struct got_error
*
3099 diff_trees(struct got_object_id
*tree_id1
, struct got_object_id
*tree_id2
,
3100 const char *path
, int diff_context
, int ignore_whitespace
,
3101 int force_text_diff
, struct got_diffstat_cb_arg
*dsa
,
3102 struct got_repository
*repo
, FILE *outfile
)
3104 const struct got_error
*err
= NULL
;
3105 struct got_tree_object
*tree1
= NULL
, *tree2
= NULL
;
3106 struct got_diff_blob_output_unidiff_arg arg
;
3107 FILE *f1
= NULL
, *f2
= NULL
;
3108 int fd1
= -1, fd2
= -1;
3111 err
= got_object_open_as_tree(&tree1
, repo
, tree_id1
);
3114 fd1
= got_opentempfd();
3116 err
= got_error_from_errno("got_opentempfd");
3121 err
= got_object_open_as_tree(&tree2
, repo
, tree_id2
);
3125 f1
= got_opentemp();
3127 err
= got_error_from_errno("got_opentemp");
3131 f2
= got_opentemp();
3133 err
= got_error_from_errno("got_opentemp");
3136 fd2
= got_opentempfd();
3138 err
= got_error_from_errno("got_opentempfd");
3141 arg
.diff_context
= diff_context
;
3142 arg
.ignore_whitespace
= ignore_whitespace
;
3143 arg
.force_text_diff
= force_text_diff
;
3145 arg
.diff_algo
= GOT_DIFF_ALGORITHM_PATIENCE
;
3146 arg
.outfile
= outfile
;
3149 while (path
[0] == '/')
3151 err
= got_diff_tree(tree1
, tree2
, f1
, f2
, fd1
, fd2
, path
, path
, repo
,
3152 got_diff_blob_output_unidiff
, &arg
, 1);
3155 got_object_tree_close(tree1
);
3157 got_object_tree_close(tree2
);
3158 if (f1
&& fclose(f1
) == EOF
&& err
== NULL
)
3159 err
= got_error_from_errno("fclose");
3160 if (f2
&& fclose(f2
) == EOF
&& err
== NULL
)
3161 err
= got_error_from_errno("fclose");
3162 if (fd1
!= -1 && close(fd1
) == -1 && err
== NULL
)
3163 err
= got_error_from_errno("close");
3164 if (fd2
!= -1 && close(fd2
) == -1 && err
== NULL
)
3165 err
= got_error_from_errno("close");
3169 static const struct got_error
*
3170 get_changed_paths(struct got_pathlist_head
*paths
,
3171 struct got_commit_object
*commit
, struct got_repository
*repo
,
3172 struct got_diffstat_cb_arg
*dsa
)
3174 const struct got_error
*err
= NULL
;
3175 struct got_object_id
*tree_id1
= NULL
, *tree_id2
= NULL
;
3176 struct got_tree_object
*tree1
= NULL
, *tree2
= NULL
;
3177 struct got_object_qid
*qid
;
3178 got_diff_blob_cb cb
= got_diff_tree_collect_changed_paths
;
3179 FILE *f1
= NULL
, *f2
= NULL
;
3180 int fd1
= -1, fd2
= -1;
3183 cb
= got_diff_tree_compute_diffstat
;
3185 f1
= got_opentemp();
3187 err
= got_error_from_errno("got_opentemp");
3190 f2
= got_opentemp();
3192 err
= got_error_from_errno("got_opentemp");
3195 fd1
= got_opentempfd();
3197 err
= got_error_from_errno("got_opentempfd");
3200 fd2
= got_opentempfd();
3202 err
= got_error_from_errno("got_opentempfd");
3207 qid
= STAILQ_FIRST(got_object_commit_get_parent_ids(commit
));
3209 struct got_commit_object
*pcommit
;
3210 err
= got_object_open_as_commit(&pcommit
, repo
,
3215 tree_id1
= got_object_id_dup(
3216 got_object_commit_get_tree_id(pcommit
));
3217 if (tree_id1
== NULL
) {
3218 got_object_commit_close(pcommit
);
3219 return got_error_from_errno("got_object_id_dup");
3221 got_object_commit_close(pcommit
);
3226 err
= got_object_open_as_tree(&tree1
, repo
, tree_id1
);
3231 tree_id2
= got_object_commit_get_tree_id(commit
);
3232 err
= got_object_open_as_tree(&tree2
, repo
, tree_id2
);
3236 err
= got_diff_tree(tree1
, tree2
, f1
, f2
, fd1
, fd2
, "", "", repo
,
3237 cb
, dsa
? (void *)dsa
: paths
, dsa
? 1 : 0);
3240 got_object_tree_close(tree1
);
3242 got_object_tree_close(tree2
);
3243 if (fd1
!= -1 && close(fd1
) == -1 && err
== NULL
)
3244 err
= got_error_from_errno("close");
3245 if (fd2
!= -1 && close(fd2
) == -1 && err
== NULL
)
3246 err
= got_error_from_errno("close");
3247 if (f1
&& fclose(f1
) == EOF
&& err
== NULL
)
3248 err
= got_error_from_errno("fclose");
3249 if (f2
&& fclose(f2
) == EOF
&& err
== NULL
)
3250 err
= got_error_from_errno("fclose");
3255 static const struct got_error
*
3256 print_patch(struct got_commit_object
*commit
, struct got_object_id
*id
,
3257 const char *path
, int diff_context
, struct got_diffstat_cb_arg
*dsa
,
3258 struct got_repository
*repo
, FILE *outfile
)
3260 const struct got_error
*err
= NULL
;
3261 struct got_commit_object
*pcommit
= NULL
;
3262 char *id_str1
= NULL
, *id_str2
= NULL
;
3263 struct got_object_id
*obj_id1
= NULL
, *obj_id2
= NULL
;
3264 struct got_object_qid
*qid
;
3266 qid
= STAILQ_FIRST(got_object_commit_get_parent_ids(commit
));
3268 err
= got_object_open_as_commit(&pcommit
, repo
,
3272 err
= got_object_id_str(&id_str1
, &qid
->id
);
3277 err
= got_object_id_str(&id_str2
, id
);
3281 if (path
&& path
[0] != '\0') {
3283 err
= got_object_id_by_path(&obj_id2
, repo
, commit
, path
);
3287 err
= got_object_id_by_path(&obj_id1
, repo
,
3290 if (err
->code
!= GOT_ERR_NO_TREE_ENTRY
) {
3296 err
= got_object_get_type(&obj_type
, repo
, obj_id2
);
3302 "diff %s %s\n", id_str1
? id_str1
: "/dev/null", id_str2
);
3303 fprintf(outfile
, "commit - %s\n",
3304 id_str1
? id_str1
: "/dev/null");
3305 fprintf(outfile
, "commit + %s\n", id_str2
);
3307 case GOT_OBJ_TYPE_BLOB
:
3308 err
= diff_blobs(obj_id1
, obj_id2
, path
, diff_context
,
3309 0, 0, dsa
, repo
, outfile
);
3311 case GOT_OBJ_TYPE_TREE
:
3312 err
= diff_trees(obj_id1
, obj_id2
, path
, diff_context
,
3313 0, 0, dsa
, repo
, outfile
);
3316 err
= got_error(GOT_ERR_OBJ_TYPE
);
3322 obj_id2
= got_object_commit_get_tree_id(commit
);
3324 obj_id1
= got_object_commit_get_tree_id(pcommit
);
3326 "diff %s %s\n", id_str1
? id_str1
: "/dev/null", id_str2
);
3327 fprintf(outfile
, "commit - %s\n",
3328 id_str1
? id_str1
: "/dev/null");
3329 fprintf(outfile
, "commit + %s\n", id_str2
);
3330 err
= diff_trees(obj_id1
, obj_id2
, "", diff_context
, 0, 0,
3331 dsa
, repo
, outfile
);
3337 got_object_commit_close(pcommit
);
3342 get_datestr(time_t *time
, char *datebuf
)
3344 struct tm mytm
, *tm
;
3347 tm
= gmtime_r(time
, &mytm
);
3350 s
= asctime_r(tm
, datebuf
);
3353 p
= strchr(s
, '\n');
3359 static const struct got_error
*
3360 match_commit(int *have_match
, struct got_object_id
*id
,
3361 struct got_commit_object
*commit
, regex_t
*regex
)
3363 const struct got_error
*err
= NULL
;
3364 regmatch_t regmatch
;
3365 char *id_str
= NULL
, *logmsg
= NULL
;
3369 err
= got_object_id_str(&id_str
, id
);
3373 err
= got_object_commit_get_logmsg(&logmsg
, commit
);
3377 if (regexec(regex
, got_object_commit_get_author(commit
), 1,
3378 ®match
, 0) == 0 ||
3379 regexec(regex
, got_object_commit_get_committer(commit
), 1,
3380 ®match
, 0) == 0 ||
3381 regexec(regex
, id_str
, 1, ®match
, 0) == 0 ||
3382 regexec(regex
, logmsg
, 1, ®match
, 0) == 0)
3391 match_changed_paths(int *have_match
, struct got_pathlist_head
*changed_paths
,
3394 regmatch_t regmatch
;
3395 struct got_pathlist_entry
*pe
;
3399 TAILQ_FOREACH(pe
, changed_paths
, entry
) {
3400 if (regexec(regex
, pe
->path
, 1, ®match
, 0) == 0) {
3407 static const struct got_error
*
3408 match_patch(int *have_match
, struct got_commit_object
*commit
,
3409 struct got_object_id
*id
, const char *path
, int diff_context
,
3410 struct got_repository
*repo
, regex_t
*regex
, FILE *f
)
3412 const struct got_error
*err
= NULL
;
3414 size_t linesize
= 0;
3415 regmatch_t regmatch
;
3419 err
= got_opentemp_truncate(f
);
3423 err
= print_patch(commit
, id
, path
, diff_context
, NULL
, repo
, f
);
3427 if (fseeko(f
, 0L, SEEK_SET
) == -1) {
3428 err
= got_error_from_errno("fseeko");
3432 while (getline(&line
, &linesize
, f
) != -1) {
3433 if (regexec(regex
, line
, 1, ®match
, 0) == 0) {
3443 #define GOT_COMMIT_SEP_STR "-----------------------------------------------\n"
3445 static const struct got_error
*
3446 build_refs_str(char **refs_str
, struct got_reflist_head
*refs
,
3447 struct got_object_id
*id
, struct got_repository
*repo
,
3450 static const struct got_error
*err
= NULL
;
3451 struct got_reflist_entry
*re
;
3457 TAILQ_FOREACH(re
, refs
, entry
) {
3458 struct got_tag_object
*tag
= NULL
;
3459 struct got_object_id
*ref_id
;
3462 name
= got_ref_get_name(re
->ref
);
3463 if (strcmp(name
, GOT_REF_HEAD
) == 0)
3465 if (strncmp(name
, "refs/", 5) == 0)
3467 if (strncmp(name
, "got/", 4) == 0)
3469 if (strncmp(name
, "heads/", 6) == 0)
3471 if (strncmp(name
, "remotes/", 8) == 0) {
3475 s
= strstr(name
, "/" GOT_REF_HEAD
);
3476 if (s
!= NULL
&& strcmp(s
, "/" GOT_REF_HEAD
) == 0)
3479 err
= got_ref_resolve(&ref_id
, repo
, re
->ref
);
3482 if (strncmp(name
, "tags/", 5) == 0) {
3483 err
= got_object_open_as_tag(&tag
, repo
, ref_id
);
3485 if (err
->code
!= GOT_ERR_OBJ_TYPE
) {
3489 /* Ref points at something other than a tag. */
3494 cmp
= got_object_id_cmp(tag
?
3495 got_object_tag_get_object_id(tag
) : ref_id
, id
);
3498 got_object_tag_close(tag
);
3502 if (asprintf(refs_str
, "%s%s%s", s
? s
: "",
3503 s
? ", " : "", name
) == -1) {
3504 err
= got_error_from_errno("asprintf");
3515 static const struct got_error
*
3516 print_commit_oneline(struct got_commit_object
*commit
, struct got_object_id
*id
,
3517 struct got_repository
*repo
, struct got_reflist_object_id_map
*refs_idmap
)
3519 const struct got_error
*err
= NULL
;
3520 char *ref_str
= NULL
, *id_str
= NULL
, *logmsg0
= NULL
;
3521 char *comma
, *s
, *nl
;
3522 struct got_reflist_head
*refs
;
3523 char datebuf
[12]; /* YYYY-MM-DD + SPACE + NUL */
3525 time_t committer_time
;
3527 refs
= got_reflist_object_id_map_lookup(refs_idmap
, id
);
3529 err
= build_refs_str(&ref_str
, refs
, id
, repo
, 1);
3533 /* Display the first matching ref only. */
3534 if (ref_str
&& (comma
= strchr(ref_str
, ',')) != NULL
)
3538 if (ref_str
== NULL
) {
3539 err
= got_object_id_str(&id_str
, id
);
3544 committer_time
= got_object_commit_get_committer_time(commit
);
3545 if (gmtime_r(&committer_time
, &tm
) == NULL
) {
3546 err
= got_error_from_errno("gmtime_r");
3549 if (strftime(datebuf
, sizeof(datebuf
), "%G-%m-%d ", &tm
) == 0) {
3550 err
= got_error(GOT_ERR_NO_SPACE
);
3554 err
= got_object_commit_get_logmsg(&logmsg0
, commit
);
3559 while (isspace((unsigned char)s
[0]))
3562 nl
= strchr(s
, '\n');
3568 printf("%s%-7s %s\n", datebuf
, ref_str
, s
);
3570 printf("%s%.7s %s\n", datebuf
, id_str
, s
);
3572 if (fflush(stdout
) != 0 && err
== NULL
)
3573 err
= got_error_from_errno("fflush");
3581 static const struct got_error
*
3582 print_diffstat(struct got_diffstat_cb_arg
*dsa
, const char *header
)
3584 struct got_pathlist_entry
*pe
;
3587 printf("%s\n", header
);
3589 TAILQ_FOREACH(pe
, dsa
->paths
, entry
) {
3590 struct got_diff_changed_path
*cp
= pe
->data
;
3591 int pad
= dsa
->max_path_len
- pe
->path_len
+ 1;
3593 printf(" %c %s%*c | %*d+ %*d-\n", cp
->status
, pe
->path
, pad
,
3594 ' ', dsa
->add_cols
+ 1, cp
->add
, dsa
->rm_cols
+ 1, cp
->rm
);
3596 printf("\n%d file%s changed, %d insertion%s(+), %d deletion%s(-)\n\n",
3597 dsa
->nfiles
, dsa
->nfiles
> 1 ? "s" : "", dsa
->ins
,
3598 dsa
->ins
!= 1 ? "s" : "", dsa
->del
, dsa
->del
!= 1 ? "s" : "");
3600 if (fflush(stdout
) != 0)
3601 return got_error_from_errno("fflush");
3606 static const struct got_error
*
3612 if (fseeko(f
, 0L, SEEK_SET
) == -1)
3613 return got_error_from_errno("fseek");
3616 r
= fread(buf
, 1, sizeof(buf
), f
);
3619 return got_error_from_errno("fread");
3623 if (fwrite(buf
, 1, r
, stdout
) != r
)
3624 return got_ferror(stdout
, GOT_ERR_IO
);
3630 static const struct got_error
*
3631 print_commit(struct got_commit_object
*commit
, struct got_object_id
*id
,
3632 struct got_repository
*repo
, const char *path
,
3633 struct got_pathlist_head
*changed_paths
,
3634 struct got_diffstat_cb_arg
*diffstat
, int show_patch
, int diff_context
,
3635 struct got_reflist_object_id_map
*refs_idmap
, const char *custom_refs_str
,
3638 const struct got_error
*err
= NULL
;
3640 char *id_str
, *datestr
, *logmsg0
, *logmsg
, *line
;
3642 time_t committer_time
;
3643 const char *author
, *committer
;
3644 char *refs_str
= NULL
;
3646 err
= got_object_id_str(&id_str
, id
);
3650 if (custom_refs_str
== NULL
) {
3651 struct got_reflist_head
*refs
;
3652 refs
= got_reflist_object_id_map_lookup(refs_idmap
, id
);
3654 err
= build_refs_str(&refs_str
, refs
, id
, repo
, 0);
3660 printf(GOT_COMMIT_SEP_STR
);
3661 if (custom_refs_str
)
3662 printf("%s %s (%s)\n", prefix
? prefix
: "commit", id_str
,
3665 printf("%s %s%s%s%s\n", prefix
? prefix
: "commit", id_str
,
3666 refs_str
? " (" : "", refs_str
? refs_str
: "",
3667 refs_str
? ")" : "");
3672 printf("from: %s\n", got_object_commit_get_author(commit
));
3673 author
= got_object_commit_get_author(commit
);
3674 committer
= got_object_commit_get_committer(commit
);
3675 if (strcmp(author
, committer
) != 0)
3676 printf("via: %s\n", committer
);
3677 committer_time
= got_object_commit_get_committer_time(commit
);
3678 datestr
= get_datestr(&committer_time
, datebuf
);
3680 printf("date: %s UTC\n", datestr
);
3681 if (got_object_commit_get_nparents(commit
) > 1) {
3682 const struct got_object_id_queue
*parent_ids
;
3683 struct got_object_qid
*qid
;
3685 parent_ids
= got_object_commit_get_parent_ids(commit
);
3686 STAILQ_FOREACH(qid
, parent_ids
, entry
) {
3687 err
= got_object_id_str(&id_str
, &qid
->id
);
3690 printf("parent %d: %s\n", n
++, id_str
);
3696 err
= got_object_commit_get_logmsg(&logmsg0
, commit
);
3702 line
= strsep(&logmsg
, "\n");
3704 printf(" %s\n", line
);
3708 if (changed_paths
&& diffstat
== NULL
) {
3709 struct got_pathlist_entry
*pe
;
3711 TAILQ_FOREACH(pe
, changed_paths
, entry
) {
3712 struct got_diff_changed_path
*cp
= pe
->data
;
3714 printf(" %c %s\n", cp
->status
, pe
->path
);
3722 err
= got_error_from_errno("got_opentemp");
3727 err
= print_patch(commit
, id
, path
, diff_context
, diffstat
,
3728 repo
, diffstat
== NULL
? stdout
: f
);
3733 err
= print_diffstat(diffstat
, NULL
);
3745 if (fflush(stdout
) != 0 && err
== NULL
)
3746 err
= got_error_from_errno("fflush");
3748 if (f
&& fclose(f
) == EOF
&& err
== NULL
)
3749 err
= got_error_from_errno("fclose");
3755 static const struct got_error
*
3756 print_commits(struct got_object_id
*root_id
, struct got_object_id
*end_id
,
3757 struct got_repository
*repo
, const char *path
, int show_changed_paths
,
3758 int show_diffstat
, int show_patch
, const char *search_pattern
,
3759 int diff_context
, int limit
, int log_branches
, int reverse_display_order
,
3760 struct got_reflist_object_id_map
*refs_idmap
, int one_line
,
3763 const struct got_error
*err
;
3764 struct got_commit_graph
*graph
;
3767 struct got_object_id_queue reversed_commits
;
3768 struct got_object_qid
*qid
;
3769 struct got_commit_object
*commit
;
3770 struct got_pathlist_head changed_paths
;
3772 STAILQ_INIT(&reversed_commits
);
3773 TAILQ_INIT(&changed_paths
);
3775 if (search_pattern
&& regcomp(®ex
, search_pattern
,
3776 REG_EXTENDED
| REG_NOSUB
| REG_NEWLINE
))
3777 return got_error_msg(GOT_ERR_REGEX
, search_pattern
);
3779 err
= got_commit_graph_open(&graph
, path
, !log_branches
);
3782 err
= got_commit_graph_iter_start(graph
, root_id
, repo
,
3783 check_cancelled
, NULL
);
3787 struct got_object_id id
;
3788 struct got_diffstat_cb_arg dsa
= { 0, 0, 0, 0, 0, 0,
3789 &changed_paths
, 0, 0, GOT_DIFF_ALGORITHM_PATIENCE
};
3791 if (sigint_received
|| sigpipe_received
)
3794 err
= got_commit_graph_iter_next(&id
, graph
, repo
,
3795 check_cancelled
, NULL
);
3797 if (err
->code
== GOT_ERR_ITER_COMPLETED
)
3802 err
= got_object_open_as_commit(&commit
, repo
, &id
);
3806 if ((show_changed_paths
|| (show_diffstat
&& !show_patch
))
3807 && !reverse_display_order
) {
3808 err
= get_changed_paths(&changed_paths
, commit
, repo
,
3809 show_diffstat
? &dsa
: NULL
);
3814 if (search_pattern
) {
3815 err
= match_commit(&have_match
, &id
, commit
, ®ex
);
3817 got_object_commit_close(commit
);
3820 if (have_match
== 0 && show_changed_paths
)
3821 match_changed_paths(&have_match
,
3822 &changed_paths
, ®ex
);
3823 if (have_match
== 0 && show_patch
) {
3824 err
= match_patch(&have_match
, commit
, &id
,
3825 path
, diff_context
, repo
, ®ex
, tmpfile
);
3829 if (have_match
== 0) {
3830 got_object_commit_close(commit
);
3831 got_pathlist_free(&changed_paths
,
3832 GOT_PATHLIST_FREE_ALL
);
3837 if (reverse_display_order
) {
3838 err
= got_object_qid_alloc(&qid
, &id
);
3841 STAILQ_INSERT_HEAD(&reversed_commits
, qid
, entry
);
3842 got_object_commit_close(commit
);
3845 err
= print_commit_oneline(commit
, &id
,
3848 err
= print_commit(commit
, &id
, repo
, path
,
3849 (show_changed_paths
|| show_diffstat
) ?
3850 &changed_paths
: NULL
,
3851 show_diffstat
? &dsa
: NULL
, show_patch
,
3852 diff_context
, refs_idmap
, NULL
, NULL
);
3853 got_object_commit_close(commit
);
3857 if ((limit
&& --limit
== 0) ||
3858 (end_id
&& got_object_id_cmp(&id
, end_id
) == 0))
3861 got_pathlist_free(&changed_paths
, GOT_PATHLIST_FREE_ALL
);
3863 if (reverse_display_order
) {
3864 STAILQ_FOREACH(qid
, &reversed_commits
, entry
) {
3865 struct got_diffstat_cb_arg dsa
= { 0, 0, 0, 0, 0, 0,
3866 &changed_paths
, 0, 0, GOT_DIFF_ALGORITHM_PATIENCE
};
3868 err
= got_object_open_as_commit(&commit
, repo
,
3872 if (show_changed_paths
||
3873 (show_diffstat
&& !show_patch
)) {
3874 err
= get_changed_paths(&changed_paths
, commit
,
3875 repo
, show_diffstat
? &dsa
: NULL
);
3880 err
= print_commit_oneline(commit
, &qid
->id
,
3883 err
= print_commit(commit
, &qid
->id
, repo
, path
,
3884 (show_changed_paths
|| show_diffstat
) ?
3885 &changed_paths
: NULL
,
3886 show_diffstat
? &dsa
: NULL
, show_patch
,
3887 diff_context
, refs_idmap
, NULL
, NULL
);
3888 got_object_commit_close(commit
);
3891 got_pathlist_free(&changed_paths
, GOT_PATHLIST_FREE_ALL
);
3895 while (!STAILQ_EMPTY(&reversed_commits
)) {
3896 qid
= STAILQ_FIRST(&reversed_commits
);
3897 STAILQ_REMOVE_HEAD(&reversed_commits
, entry
);
3898 got_object_qid_free(qid
);
3900 got_pathlist_free(&changed_paths
, GOT_PATHLIST_FREE_ALL
);
3903 got_commit_graph_close(graph
);
3910 fprintf(stderr
, "usage: %s log [-bdPpRs] [-C number] [-c commit] "
3911 "[-l N] [-r repository-path] [-S search-pattern] [-x commit] "
3912 "[path]\n", getprogname());
3917 get_default_log_limit(void)
3919 const char *got_default_log_limit
;
3923 got_default_log_limit
= getenv("GOT_LOG_DEFAULT_LIMIT");
3924 if (got_default_log_limit
== NULL
)
3926 n
= strtonum(got_default_log_limit
, 0, INT_MAX
, &errstr
);
3932 static const struct got_error
*
3933 cmd_log(int argc
, char *argv
[])
3935 const struct got_error
*error
;
3936 struct got_repository
*repo
= NULL
;
3937 struct got_worktree
*worktree
= NULL
;
3938 struct got_object_id
*start_id
= NULL
, *end_id
= NULL
;
3939 char *repo_path
= NULL
, *path
= NULL
, *cwd
= NULL
, *in_repo_path
= NULL
;
3940 const char *start_commit
= NULL
, *end_commit
= NULL
;
3941 const char *search_pattern
= NULL
;
3942 int diff_context
= -1, ch
;
3943 int show_changed_paths
= 0, show_patch
= 0, limit
= 0, log_branches
= 0;
3944 int show_diffstat
= 0, reverse_display_order
= 0, one_line
= 0;
3946 struct got_reflist_head refs
;
3947 struct got_reflist_object_id_map
*refs_idmap
= NULL
;
3948 FILE *tmpfile
= NULL
;
3949 int *pack_fds
= NULL
;
3954 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
3960 limit
= get_default_log_limit();
3962 while ((ch
= getopt(argc
, argv
, "bC:c:dl:PpRr:S:sx:")) != -1) {
3968 diff_context
= strtonum(optarg
, 0, GOT_DIFF_MAX_CONTEXT
,
3971 errx(1, "number of context lines is %s: %s",
3975 start_commit
= optarg
;
3981 limit
= strtonum(optarg
, 0, INT_MAX
, &errstr
);
3983 errx(1, "number of commits is %s: %s",
3987 show_changed_paths
= 1;
3993 reverse_display_order
= 1;
3996 repo_path
= realpath(optarg
, NULL
);
3997 if (repo_path
== NULL
)
3998 return got_error_from_errno2("realpath",
4000 got_path_strip_trailing_slashes(repo_path
);
4003 search_pattern
= optarg
;
4009 end_commit
= optarg
;
4020 if (diff_context
== -1)
4022 else if (!show_patch
)
4023 errx(1, "-C requires -p");
4025 if (one_line
&& (show_patch
|| show_changed_paths
|| show_diffstat
))
4026 errx(1, "cannot use -s with -d, -p or -P");
4028 cwd
= getcwd(NULL
, 0);
4030 error
= got_error_from_errno("getcwd");
4034 error
= got_repo_pack_fds_open(&pack_fds
);
4038 if (repo_path
== NULL
) {
4039 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_CVG_DIR
);
4040 if (error
&& error
->code
!= GOT_ERR_NOT_WORKTREE
)
4047 error
= got_worktree_resolve_path(&path
, worktree
,
4052 path
= strdup(argv
[0]);
4054 error
= got_error_from_errno("strdup");
4058 } else if (argc
!= 0)
4061 if (repo_path
== NULL
) {
4062 repo_path
= worktree
?
4063 strdup(got_worktree_get_repo_path(worktree
)) : strdup(cwd
);
4065 if (repo_path
== NULL
) {
4066 error
= got_error_from_errno("strdup");
4070 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
4074 error
= apply_unveil(got_repo_get_path(repo
), 1,
4075 worktree
? got_worktree_get_root_path(worktree
) : NULL
);
4079 error
= got_ref_list(&refs
, repo
, NULL
, got_ref_cmp_by_name
, NULL
);
4083 error
= got_reflist_object_id_map_create(&refs_idmap
, &refs
, repo
);
4087 if (start_commit
== NULL
) {
4088 struct got_reference
*head_ref
;
4089 struct got_commit_object
*commit
= NULL
;
4090 error
= got_ref_open(&head_ref
, repo
,
4091 worktree
? got_worktree_get_head_ref_name(worktree
)
4095 error
= got_ref_resolve(&start_id
, repo
, head_ref
);
4096 got_ref_close(head_ref
);
4099 error
= got_object_open_as_commit(&commit
, repo
,
4103 got_object_commit_close(commit
);
4105 error
= got_repo_match_object_id(&start_id
, NULL
,
4106 start_commit
, GOT_OBJ_TYPE_COMMIT
, &refs
, repo
);
4110 if (end_commit
!= NULL
) {
4111 error
= got_repo_match_object_id(&end_id
, NULL
,
4112 end_commit
, GOT_OBJ_TYPE_COMMIT
, &refs
, repo
);
4119 * If a path was specified on the command line it was resolved
4120 * to a path in the work tree above. Prepend the work tree's
4121 * path prefix to obtain the corresponding in-repository path.
4125 prefix
= got_worktree_get_path_prefix(worktree
);
4126 if (asprintf(&in_repo_path
, "%s%s%s", prefix
,
4127 (path
[0] != '\0') ? "/" : "", path
) == -1) {
4128 error
= got_error_from_errno("asprintf");
4133 error
= got_repo_map_path(&in_repo_path
, repo
,
4139 path
= in_repo_path
;
4143 /* Release work tree lock. */
4144 got_worktree_close(worktree
);
4148 if (search_pattern
&& show_patch
) {
4149 tmpfile
= got_opentemp();
4150 if (tmpfile
== NULL
) {
4151 error
= got_error_from_errno("got_opentemp");
4156 error
= print_commits(start_id
, end_id
, repo
, path
? path
: "",
4157 show_changed_paths
, show_diffstat
, show_patch
, search_pattern
,
4158 diff_context
, limit
, log_branches
, reverse_display_order
,
4159 refs_idmap
, one_line
, tmpfile
);
4167 got_worktree_close(worktree
);
4169 const struct got_error
*close_err
= got_repo_close(repo
);
4174 const struct got_error
*pack_err
=
4175 got_repo_pack_fds_close(pack_fds
);
4180 got_reflist_object_id_map_free(refs_idmap
);
4181 if (tmpfile
&& fclose(tmpfile
) == EOF
&& error
== NULL
)
4182 error
= got_error_from_errno("fclose");
4183 got_ref_list_free(&refs
);
4190 fprintf(stderr
, "usage: %s diff [-adPsw] [-C number] [-c commit] "
4191 "[-r repository-path] [object1 object2 | path ...]\n",
4196 struct print_diff_arg
{
4197 struct got_repository
*repo
;
4198 struct got_worktree
*worktree
;
4199 struct got_diffstat_cb_arg
*diffstat
;
4204 enum got_diff_algorithm diff_algo
;
4205 int ignore_whitespace
;
4206 int force_text_diff
;
4213 * Create a file which contains the target path of a symlink so we can feed
4214 * it as content to the diff engine.
4216 static const struct got_error
*
4217 get_symlink_target_file(int *fd
, int dirfd
, const char *de_name
,
4218 const char *abspath
)
4220 const struct got_error
*err
= NULL
;
4221 char target_path
[PATH_MAX
];
4222 ssize_t target_len
, outlen
;
4227 target_len
= readlinkat(dirfd
, de_name
, target_path
, PATH_MAX
);
4228 if (target_len
== -1)
4229 return got_error_from_errno2("readlinkat", abspath
);
4231 target_len
= readlink(abspath
, target_path
, PATH_MAX
);
4232 if (target_len
== -1)
4233 return got_error_from_errno2("readlink", abspath
);
4236 *fd
= got_opentempfd();
4238 return got_error_from_errno("got_opentempfd");
4240 outlen
= write(*fd
, target_path
, target_len
);
4242 err
= got_error_from_errno("got_opentempfd");
4246 if (lseek(*fd
, 0, SEEK_SET
) == -1) {
4247 err
= got_error_from_errno2("lseek", abspath
);
4258 static const struct got_error
*
4259 print_diff(void *arg
, unsigned char status
, unsigned char staged_status
,
4260 const char *path
, struct got_object_id
*blob_id
,
4261 struct got_object_id
*staged_blob_id
, struct got_object_id
*commit_id
,
4262 int dirfd
, const char *de_name
)
4264 struct print_diff_arg
*a
= arg
;
4265 const struct got_error
*err
= NULL
;
4266 struct got_blob_object
*blob1
= NULL
;
4267 int fd
= -1, fd1
= -1, fd2
= -1;
4269 char *abspath
= NULL
, *label1
= NULL
;
4274 memset(&sb
, 0, sizeof(sb
));
4276 if (a
->diff_staged
) {
4277 if (staged_status
!= GOT_STATUS_MODIFY
&&
4278 staged_status
!= GOT_STATUS_ADD
&&
4279 staged_status
!= GOT_STATUS_DELETE
)
4282 if (staged_status
== GOT_STATUS_DELETE
)
4284 if (status
== GOT_STATUS_NONEXISTENT
)
4285 return got_error_set_errno(ENOENT
, path
);
4286 if (status
!= GOT_STATUS_MODIFY
&&
4287 status
!= GOT_STATUS_ADD
&&
4288 status
!= GOT_STATUS_DELETE
&&
4289 status
!= GOT_STATUS_CONFLICT
)
4293 err
= got_opentemp_truncate(a
->f1
);
4295 return got_error_from_errno("got_opentemp_truncate");
4296 err
= got_opentemp_truncate(a
->f2
);
4298 return got_error_from_errno("got_opentemp_truncate");
4300 if (!a
->header_shown
) {
4301 if (fprintf(a
->outfile
, "diff %s%s\n",
4302 a
->diff_staged
? "-s " : "",
4303 got_worktree_get_root_path(a
->worktree
)) < 0) {
4304 err
= got_error_from_errno("fprintf");
4307 if (fprintf(a
->outfile
, "commit - %s\n", a
->id_str
) < 0) {
4308 err
= got_error_from_errno("fprintf");
4311 if (fprintf(a
->outfile
, "path + %s%s\n",
4312 got_worktree_get_root_path(a
->worktree
),
4313 a
->diff_staged
? " (staged changes)" : "") < 0) {
4314 err
= got_error_from_errno("fprintf");
4317 a
->header_shown
= 1;
4320 if (a
->diff_staged
) {
4321 const char *label1
= NULL
, *label2
= NULL
;
4322 switch (staged_status
) {
4323 case GOT_STATUS_MODIFY
:
4327 case GOT_STATUS_ADD
:
4330 case GOT_STATUS_DELETE
:
4334 return got_error(GOT_ERR_FILE_STATUS
);
4336 fd1
= got_opentempfd();
4338 err
= got_error_from_errno("got_opentempfd");
4341 fd2
= got_opentempfd();
4343 err
= got_error_from_errno("got_opentempfd");
4346 err
= got_diff_objects_as_blobs(NULL
, NULL
, a
->f1
, a
->f2
,
4347 fd1
, fd2
, blob_id
, staged_blob_id
, label1
, label2
,
4348 a
->diff_algo
, a
->diff_context
, a
->ignore_whitespace
,
4349 a
->force_text_diff
, a
->diffstat
, a
->repo
, a
->outfile
);
4353 fd1
= got_opentempfd();
4355 err
= got_error_from_errno("got_opentempfd");
4359 if (staged_status
== GOT_STATUS_ADD
||
4360 staged_status
== GOT_STATUS_MODIFY
) {
4362 err
= got_object_open_as_blob(&blob1
, a
->repo
, staged_blob_id
,
4366 err
= got_object_id_str(&id_str
, staged_blob_id
);
4369 if (asprintf(&label1
, "%s (staged)", id_str
) == -1) {
4370 err
= got_error_from_errno("asprintf");
4375 } else if (status
!= GOT_STATUS_ADD
) {
4376 err
= got_object_open_as_blob(&blob1
, a
->repo
, blob_id
, 8192,
4382 if (status
!= GOT_STATUS_DELETE
) {
4383 if (asprintf(&abspath
, "%s/%s",
4384 got_worktree_get_root_path(a
->worktree
), path
) == -1) {
4385 err
= got_error_from_errno("asprintf");
4390 fd
= openat(dirfd
, de_name
,
4391 O_RDONLY
| O_NOFOLLOW
| O_CLOEXEC
);
4393 if (!got_err_open_nofollow_on_symlink()) {
4394 err
= got_error_from_errno2("openat",
4398 err
= get_symlink_target_file(&fd
, dirfd
,
4404 fd
= open(abspath
, O_RDONLY
| O_NOFOLLOW
| O_CLOEXEC
);
4406 if (!got_err_open_nofollow_on_symlink()) {
4407 err
= got_error_from_errno2("open",
4411 err
= get_symlink_target_file(&fd
, dirfd
,
4417 if (fstatat(fd
, abspath
, &sb
, AT_SYMLINK_NOFOLLOW
) == -1) {
4418 err
= got_error_from_errno2("fstatat", abspath
);
4421 f2
= fdopen(fd
, "r");
4423 err
= got_error_from_errno2("fdopen", abspath
);
4431 err
= got_object_blob_dump_to_file(&size1
, NULL
, NULL
,
4437 err
= got_diff_blob_file(blob1
, a
->f1
, size1
, label1
, f2
? f2
: a
->f2
,
4438 f2_exists
, &sb
, path
, GOT_DIFF_ALGORITHM_PATIENCE
, a
->diff_context
,
4439 a
->ignore_whitespace
, a
->force_text_diff
, a
->diffstat
, a
->outfile
);
4441 if (fd1
!= -1 && close(fd1
) == -1 && err
== NULL
)
4442 err
= got_error_from_errno("close");
4443 if (fd2
!= -1 && close(fd2
) == -1 && err
== NULL
)
4444 err
= got_error_from_errno("close");
4446 got_object_blob_close(blob1
);
4447 if (fd
!= -1 && close(fd
) == -1 && err
== NULL
)
4448 err
= got_error_from_errno("close");
4449 if (f2
&& fclose(f2
) == EOF
&& err
== NULL
)
4450 err
= got_error_from_errno("fclose");
4455 static const struct got_error
*
4456 cmd_diff(int argc
, char *argv
[])
4458 const struct got_error
*error
;
4459 struct got_repository
*repo
= NULL
;
4460 struct got_worktree
*worktree
= NULL
;
4461 char *cwd
= NULL
, *repo_path
= NULL
;
4462 const char *commit_args
[2] = { NULL
, NULL
};
4463 int ncommit_args
= 0;
4464 struct got_object_id
*ids
[2] = { NULL
, NULL
};
4465 char *labels
[2] = { NULL
, NULL
};
4466 int type1
= GOT_OBJ_TYPE_ANY
, type2
= GOT_OBJ_TYPE_ANY
;
4467 int diff_context
= 3, diff_staged
= 0, ignore_whitespace
= 0, ch
, i
;
4468 int force_text_diff
= 0, force_path
= 0, rflag
= 0, show_diffstat
= 0;
4470 struct got_reflist_head refs
;
4471 struct got_pathlist_head diffstat_paths
, paths
;
4472 FILE *f1
= NULL
, *f2
= NULL
, *outfile
= NULL
;
4473 int fd1
= -1, fd2
= -1;
4474 int *pack_fds
= NULL
;
4475 struct got_diffstat_cb_arg dsa
;
4477 memset(&dsa
, 0, sizeof(dsa
));
4481 TAILQ_INIT(&diffstat_paths
);
4484 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
4489 while ((ch
= getopt(argc
, argv
, "aC:c:dPr:sw")) != -1) {
4492 force_text_diff
= 1;
4495 diff_context
= strtonum(optarg
, 0, GOT_DIFF_MAX_CONTEXT
,
4498 errx(1, "number of context lines is %s: %s",
4502 if (ncommit_args
>= 2)
4503 errx(1, "too many -c options used");
4504 commit_args
[ncommit_args
++] = optarg
;
4513 repo_path
= realpath(optarg
, NULL
);
4514 if (repo_path
== NULL
)
4515 return got_error_from_errno2("realpath",
4517 got_path_strip_trailing_slashes(repo_path
);
4524 ignore_whitespace
= 1;
4535 cwd
= getcwd(NULL
, 0);
4537 error
= got_error_from_errno("getcwd");
4541 error
= got_repo_pack_fds_open(&pack_fds
);
4545 if (repo_path
== NULL
) {
4546 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_CVG_DIR
);
4547 if (error
&& error
->code
!= GOT_ERR_NOT_WORKTREE
)
4553 strdup(got_worktree_get_repo_path(worktree
));
4554 if (repo_path
== NULL
) {
4555 error
= got_error_from_errno("strdup");
4559 repo_path
= strdup(cwd
);
4560 if (repo_path
== NULL
) {
4561 error
= got_error_from_errno("strdup");
4567 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
4572 if (show_diffstat
) {
4573 dsa
.paths
= &diffstat_paths
;
4574 dsa
.force_text
= force_text_diff
;
4575 dsa
.ignore_ws
= ignore_whitespace
;
4576 dsa
.diff_algo
= GOT_DIFF_ALGORITHM_PATIENCE
;
4579 if (rflag
|| worktree
== NULL
|| ncommit_args
> 0) {
4581 error
= got_error_msg(GOT_ERR_NOT_IMPL
,
4582 "-P option can only be used when diffing "
4587 error
= got_error_msg(GOT_ERR_NOT_IMPL
,
4588 "-s option can only be used when diffing "
4594 error
= apply_unveil(got_repo_get_path(repo
), 1,
4595 worktree
? got_worktree_get_root_path(worktree
) : NULL
);
4599 if ((!force_path
&& argc
== 2) || ncommit_args
> 0) {
4600 int obj_type
= (ncommit_args
> 0 ?
4601 GOT_OBJ_TYPE_COMMIT
: GOT_OBJ_TYPE_ANY
);
4602 error
= got_ref_list(&refs
, repo
, NULL
, got_ref_cmp_by_name
,
4606 for (i
= 0; i
< (ncommit_args
> 0 ? ncommit_args
: argc
); i
++) {
4608 if (ncommit_args
> 0)
4609 arg
= commit_args
[i
];
4612 error
= got_repo_match_object_id(&ids
[i
], &labels
[i
],
4613 arg
, obj_type
, &refs
, repo
);
4615 if (error
->code
!= GOT_ERR_NOT_REF
&&
4616 error
->code
!= GOT_ERR_NO_OBJ
)
4618 if (ncommit_args
> 0)
4626 f1
= got_opentemp();
4628 error
= got_error_from_errno("got_opentemp");
4632 f2
= got_opentemp();
4634 error
= got_error_from_errno("got_opentemp");
4638 outfile
= got_opentemp();
4639 if (outfile
== NULL
) {
4640 error
= got_error_from_errno("got_opentemp");
4644 if (ncommit_args
== 0 && (ids
[0] == NULL
|| ids
[1] == NULL
)) {
4645 struct print_diff_arg arg
;
4648 if (worktree
== NULL
) {
4649 if (argc
== 2 && ids
[0] == NULL
) {
4650 error
= got_error_path(argv
[0], GOT_ERR_NO_OBJ
);
4652 } else if (argc
== 2 && ids
[1] == NULL
) {
4653 error
= got_error_path(argv
[1], GOT_ERR_NO_OBJ
);
4655 } else if (argc
> 0) {
4656 error
= got_error_fmt(GOT_ERR_NOT_WORKTREE
,
4657 "%s", "specified paths cannot be resolved");
4660 error
= got_error(GOT_ERR_NOT_WORKTREE
);
4665 error
= get_worktree_paths_from_argv(&paths
, argc
, argv
,
4670 error
= got_object_id_str(&id_str
,
4671 got_worktree_get_base_commit_id(worktree
));
4675 arg
.worktree
= worktree
;
4676 arg
.diff_algo
= GOT_DIFF_ALGORITHM_PATIENCE
;
4677 arg
.diff_context
= diff_context
;
4678 arg
.id_str
= id_str
;
4679 arg
.header_shown
= 0;
4680 arg
.diff_staged
= diff_staged
;
4681 arg
.ignore_whitespace
= ignore_whitespace
;
4682 arg
.force_text_diff
= force_text_diff
;
4683 arg
.diffstat
= show_diffstat
? &dsa
: NULL
;
4686 arg
.outfile
= outfile
;
4688 error
= got_worktree_status(worktree
, &paths
, repo
, 0,
4689 print_diff
, &arg
, check_cancelled
, NULL
);
4694 if (show_diffstat
&& dsa
.nfiles
> 0) {
4697 if (asprintf(&header
, "diffstat %s%s",
4698 diff_staged
? "-s " : "",
4699 got_worktree_get_root_path(worktree
)) == -1) {
4700 error
= got_error_from_errno("asprintf");
4704 error
= print_diffstat(&dsa
, header
);
4710 error
= printfile(outfile
);
4714 if (ncommit_args
== 1) {
4715 struct got_commit_object
*commit
;
4716 error
= got_object_open_as_commit(&commit
, repo
, ids
[0]);
4720 labels
[1] = labels
[0];
4722 if (got_object_commit_get_nparents(commit
) > 0) {
4723 const struct got_object_id_queue
*pids
;
4724 struct got_object_qid
*pid
;
4725 pids
= got_object_commit_get_parent_ids(commit
);
4726 pid
= STAILQ_FIRST(pids
);
4727 ids
[0] = got_object_id_dup(&pid
->id
);
4728 if (ids
[0] == NULL
) {
4729 error
= got_error_from_errno(
4730 "got_object_id_dup");
4731 got_object_commit_close(commit
);
4734 error
= got_object_id_str(&labels
[0], ids
[0]);
4736 got_object_commit_close(commit
);
4741 labels
[0] = strdup("/dev/null");
4742 if (labels
[0] == NULL
) {
4743 error
= got_error_from_errno("strdup");
4744 got_object_commit_close(commit
);
4749 got_object_commit_close(commit
);
4752 if (ncommit_args
== 0 && argc
> 2) {
4753 error
= got_error_msg(GOT_ERR_BAD_PATH
,
4754 "path arguments cannot be used when diffing two objects");
4759 error
= got_object_get_type(&type1
, repo
, ids
[0]);
4764 error
= got_object_get_type(&type2
, repo
, ids
[1]);
4767 if (type1
!= GOT_OBJ_TYPE_ANY
&& type1
!= type2
) {
4768 error
= got_error(GOT_ERR_OBJ_TYPE
);
4771 if (type1
== GOT_OBJ_TYPE_BLOB
&& argc
> 2) {
4772 error
= got_error_msg(GOT_ERR_OBJ_TYPE
,
4773 "path arguments cannot be used when diffing blobs");
4777 for (i
= 0; ncommit_args
> 0 && i
< argc
; i
++) {
4779 struct got_pathlist_entry
*new;
4783 error
= got_worktree_resolve_path(&p
, worktree
,
4787 prefix
= got_worktree_get_path_prefix(worktree
);
4788 while (prefix
[0] == '/')
4790 if (asprintf(&in_repo_path
, "%s%s%s", prefix
,
4791 (p
[0] != '\0' && prefix
[0] != '\0') ? "/" : "",
4793 error
= got_error_from_errno("asprintf");
4799 char *mapped_path
, *s
;
4800 error
= got_repo_map_path(&mapped_path
, repo
, argv
[i
]);
4806 in_repo_path
= strdup(s
);
4807 if (in_repo_path
== NULL
) {
4808 error
= got_error_from_errno("asprintf");
4815 error
= got_pathlist_insert(&new, &paths
, in_repo_path
, NULL
);
4816 if (error
|| new == NULL
/* duplicate */)
4823 /* Release work tree lock. */
4824 got_worktree_close(worktree
);
4828 fd1
= got_opentempfd();
4830 error
= got_error_from_errno("got_opentempfd");
4834 fd2
= got_opentempfd();
4836 error
= got_error_from_errno("got_opentempfd");
4840 switch (type1
== GOT_OBJ_TYPE_ANY
? type2
: type1
) {
4841 case GOT_OBJ_TYPE_BLOB
:
4842 error
= got_diff_objects_as_blobs(NULL
, NULL
, f1
, f2
,
4843 fd1
, fd2
, ids
[0], ids
[1], NULL
, NULL
,
4844 GOT_DIFF_ALGORITHM_PATIENCE
, diff_context
,
4845 ignore_whitespace
, force_text_diff
,
4846 show_diffstat
? &dsa
: NULL
, repo
, outfile
);
4848 case GOT_OBJ_TYPE_TREE
:
4849 error
= got_diff_objects_as_trees(NULL
, NULL
, f1
, f2
, fd1
, fd2
,
4850 ids
[0], ids
[1], &paths
, "", "",
4851 GOT_DIFF_ALGORITHM_PATIENCE
, diff_context
,
4852 ignore_whitespace
, force_text_diff
,
4853 show_diffstat
? &dsa
: NULL
, repo
, outfile
);
4855 case GOT_OBJ_TYPE_COMMIT
:
4856 fprintf(outfile
, "diff %s %s\n", labels
[0], labels
[1]);
4857 error
= got_diff_objects_as_commits(NULL
, NULL
, f1
, f2
,
4858 fd1
, fd2
, ids
[0], ids
[1], &paths
,
4859 GOT_DIFF_ALGORITHM_PATIENCE
, diff_context
,
4860 ignore_whitespace
, force_text_diff
,
4861 show_diffstat
? &dsa
: NULL
, repo
, outfile
);
4864 error
= got_error(GOT_ERR_OBJ_TYPE
);
4869 if (show_diffstat
&& dsa
.nfiles
> 0) {
4870 char *header
= NULL
;
4872 if (asprintf(&header
, "diffstat %s %s",
4873 labels
[0], labels
[1]) == -1) {
4874 error
= got_error_from_errno("asprintf");
4878 error
= print_diffstat(&dsa
, header
);
4884 error
= printfile(outfile
);
4892 got_worktree_close(worktree
);
4894 const struct got_error
*close_err
= got_repo_close(repo
);
4899 const struct got_error
*pack_err
=
4900 got_repo_pack_fds_close(pack_fds
);
4904 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_PATH
);
4905 got_pathlist_free(&diffstat_paths
, GOT_PATHLIST_FREE_ALL
);
4906 got_ref_list_free(&refs
);
4907 if (outfile
&& fclose(outfile
) == EOF
&& error
== NULL
)
4908 error
= got_error_from_errno("fclose");
4909 if (f1
&& fclose(f1
) == EOF
&& error
== NULL
)
4910 error
= got_error_from_errno("fclose");
4911 if (f2
&& fclose(f2
) == EOF
&& error
== NULL
)
4912 error
= got_error_from_errno("fclose");
4913 if (fd1
!= -1 && close(fd1
) == -1 && error
== NULL
)
4914 error
= got_error_from_errno("close");
4915 if (fd2
!= -1 && close(fd2
) == -1 && error
== NULL
)
4916 error
= got_error_from_errno("close");
4924 "usage: %s blame [-c commit] [-r repository-path] path\n",
4933 char datebuf
[11]; /* YYYY-MM-DD + NUL */
4936 struct blame_cb_args
{
4937 struct blame_line
*lines
;
4941 off_t
*line_offsets
;
4943 struct got_repository
*repo
;
4946 static const struct got_error
*
4947 blame_cb(void *arg
, int nlines
, int lineno
,
4948 struct got_commit_object
*commit
, struct got_object_id
*id
)
4950 const struct got_error
*err
= NULL
;
4951 struct blame_cb_args
*a
= arg
;
4952 struct blame_line
*bline
;
4954 size_t linesize
= 0;
4957 time_t committer_time
;
4959 if (nlines
!= a
->nlines
||
4960 (lineno
!= -1 && lineno
< 1) || lineno
> a
->nlines
)
4961 return got_error(GOT_ERR_RANGE
);
4963 if (sigint_received
)
4964 return got_error(GOT_ERR_ITER_COMPLETED
);
4967 return NULL
; /* no change in this commit */
4969 /* Annotate this line. */
4970 bline
= &a
->lines
[lineno
- 1];
4971 if (bline
->annotated
)
4973 err
= got_object_id_str(&bline
->id_str
, id
);
4977 bline
->committer
= strdup(got_object_commit_get_committer(commit
));
4978 if (bline
->committer
== NULL
) {
4979 err
= got_error_from_errno("strdup");
4983 committer_time
= got_object_commit_get_committer_time(commit
);
4984 if (gmtime_r(&committer_time
, &tm
) == NULL
)
4985 return got_error_from_errno("gmtime_r");
4986 if (strftime(bline
->datebuf
, sizeof(bline
->datebuf
), "%G-%m-%d",
4988 err
= got_error(GOT_ERR_NO_SPACE
);
4991 bline
->annotated
= 1;
4993 /* Print lines annotated so far. */
4994 bline
= &a
->lines
[a
->lineno_cur
- 1];
4995 if (!bline
->annotated
)
4998 offset
= a
->line_offsets
[a
->lineno_cur
- 1];
4999 if (fseeko(a
->f
, offset
, SEEK_SET
) == -1) {
5000 err
= got_error_from_errno("fseeko");
5004 while (a
->lineno_cur
<= a
->nlines
&& bline
->annotated
) {
5005 char *smallerthan
, *at
, *nl
, *committer
;
5008 if (getline(&line
, &linesize
, a
->f
) == -1) {
5010 err
= got_error_from_errno("getline");
5014 committer
= bline
->committer
;
5015 smallerthan
= strchr(committer
, '<');
5016 if (smallerthan
&& smallerthan
[1] != '\0')
5017 committer
= smallerthan
+ 1;
5018 at
= strchr(committer
, '@');
5021 len
= strlen(committer
);
5023 committer
[8] = '\0';
5025 nl
= strchr(line
, '\n');
5028 printf("%.*d) %.8s %s %-8s %s\n", a
->nlines_prec
, a
->lineno_cur
,
5029 bline
->id_str
, bline
->datebuf
, committer
, line
);
5032 bline
= &a
->lines
[a
->lineno_cur
- 1];
5039 static const struct got_error
*
5040 cmd_blame(int argc
, char *argv
[])
5042 const struct got_error
*error
;
5043 struct got_repository
*repo
= NULL
;
5044 struct got_worktree
*worktree
= NULL
;
5045 char *path
, *cwd
= NULL
, *repo_path
= NULL
, *in_repo_path
= NULL
;
5046 char *link_target
= NULL
;
5047 struct got_object_id
*obj_id
= NULL
;
5048 struct got_object_id
*commit_id
= NULL
;
5049 struct got_commit_object
*commit
= NULL
;
5050 struct got_blob_object
*blob
= NULL
;
5051 char *commit_id_str
= NULL
;
5052 struct blame_cb_args bca
;
5053 int ch
, obj_type
, i
, fd1
= -1, fd2
= -1, fd3
= -1;
5055 int *pack_fds
= NULL
;
5056 FILE *f1
= NULL
, *f2
= NULL
;
5058 fd1
= got_opentempfd();
5060 return got_error_from_errno("got_opentempfd");
5062 memset(&bca
, 0, sizeof(bca
));
5065 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
5070 while ((ch
= getopt(argc
, argv
, "c:r:")) != -1) {
5073 commit_id_str
= optarg
;
5076 repo_path
= realpath(optarg
, NULL
);
5077 if (repo_path
== NULL
)
5078 return got_error_from_errno2("realpath",
5080 got_path_strip_trailing_slashes(repo_path
);
5096 cwd
= getcwd(NULL
, 0);
5098 error
= got_error_from_errno("getcwd");
5102 error
= got_repo_pack_fds_open(&pack_fds
);
5106 if (repo_path
== NULL
) {
5107 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_CVG_DIR
);
5108 if (error
&& error
->code
!= GOT_ERR_NOT_WORKTREE
)
5114 strdup(got_worktree_get_repo_path(worktree
));
5115 if (repo_path
== NULL
) {
5116 error
= got_error_from_errno("strdup");
5121 repo_path
= strdup(cwd
);
5122 if (repo_path
== NULL
) {
5123 error
= got_error_from_errno("strdup");
5129 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
5134 const char *prefix
= got_worktree_get_path_prefix(worktree
);
5137 error
= got_worktree_resolve_path(&p
, worktree
, path
);
5140 if (asprintf(&in_repo_path
, "%s%s%s", prefix
,
5141 (p
[0] != '\0' && !got_path_is_root_dir(prefix
)) ? "/" : "",
5143 error
= got_error_from_errno("asprintf");
5148 error
= apply_unveil(got_repo_get_path(repo
), 1, NULL
);
5150 error
= apply_unveil(got_repo_get_path(repo
), 1, NULL
);
5153 error
= got_repo_map_path(&in_repo_path
, repo
, path
);
5158 if (commit_id_str
== NULL
) {
5159 struct got_reference
*head_ref
;
5160 error
= got_ref_open(&head_ref
, repo
, worktree
?
5161 got_worktree_get_head_ref_name(worktree
) : GOT_REF_HEAD
, 0);
5164 error
= got_ref_resolve(&commit_id
, repo
, head_ref
);
5165 got_ref_close(head_ref
);
5169 struct got_reflist_head refs
;
5171 error
= got_ref_list(&refs
, repo
, NULL
, got_ref_cmp_by_name
,
5175 error
= got_repo_match_object_id(&commit_id
, NULL
,
5176 commit_id_str
, GOT_OBJ_TYPE_COMMIT
, &refs
, repo
);
5177 got_ref_list_free(&refs
);
5183 /* Release work tree lock. */
5184 got_worktree_close(worktree
);
5188 error
= got_object_open_as_commit(&commit
, repo
, commit_id
);
5192 error
= got_object_resolve_symlinks(&link_target
, in_repo_path
,
5197 error
= got_object_id_by_path(&obj_id
, repo
, commit
,
5198 link_target
? link_target
: in_repo_path
);
5202 error
= got_object_get_type(&obj_type
, repo
, obj_id
);
5206 if (obj_type
!= GOT_OBJ_TYPE_BLOB
) {
5207 error
= got_error_path(link_target
? link_target
: in_repo_path
,
5212 error
= got_object_open_as_blob(&blob
, repo
, obj_id
, 8192, fd1
);
5215 bca
.f
= got_opentemp();
5216 if (bca
.f
== NULL
) {
5217 error
= got_error_from_errno("got_opentemp");
5220 error
= got_object_blob_dump_to_file(&filesize
, &bca
.nlines
,
5221 &bca
.line_offsets
, bca
.f
, blob
);
5222 if (error
|| bca
.nlines
== 0)
5225 /* Don't include \n at EOF in the blame line count. */
5226 if (bca
.line_offsets
[bca
.nlines
- 1] == filesize
)
5229 bca
.lines
= calloc(bca
.nlines
, sizeof(*bca
.lines
));
5230 if (bca
.lines
== NULL
) {
5231 error
= got_error_from_errno("calloc");
5235 bca
.nlines_prec
= 0;
5243 fd2
= got_opentempfd();
5245 error
= got_error_from_errno("got_opentempfd");
5248 fd3
= got_opentempfd();
5250 error
= got_error_from_errno("got_opentempfd");
5253 f1
= got_opentemp();
5255 error
= got_error_from_errno("got_opentemp");
5258 f2
= got_opentemp();
5260 error
= got_error_from_errno("got_opentemp");
5263 error
= got_blame(link_target
? link_target
: in_repo_path
, commit_id
,
5264 repo
, GOT_DIFF_ALGORITHM_PATIENCE
, blame_cb
, &bca
,
5265 check_cancelled
, NULL
, fd2
, fd3
, f1
, f2
);
5274 got_object_commit_close(commit
);
5276 if (fd1
!= -1 && close(fd1
) == -1 && error
== NULL
)
5277 error
= got_error_from_errno("close");
5278 if (fd2
!= -1 && close(fd2
) == -1 && error
== NULL
)
5279 error
= got_error_from_errno("close");
5280 if (fd3
!= -1 && close(fd3
) == -1 && error
== NULL
)
5281 error
= got_error_from_errno("close");
5282 if (f1
&& fclose(f1
) == EOF
&& error
== NULL
)
5283 error
= got_error_from_errno("fclose");
5284 if (f2
&& fclose(f2
) == EOF
&& error
== NULL
)
5285 error
= got_error_from_errno("fclose");
5288 got_object_blob_close(blob
);
5290 got_worktree_close(worktree
);
5292 const struct got_error
*close_err
= got_repo_close(repo
);
5297 const struct got_error
*pack_err
=
5298 got_repo_pack_fds_close(pack_fds
);
5303 for (i
= 0; i
< bca
.nlines
; i
++) {
5304 struct blame_line
*bline
= &bca
.lines
[i
];
5305 free(bline
->id_str
);
5306 free(bline
->committer
);
5310 free(bca
.line_offsets
);
5311 if (bca
.f
&& fclose(bca
.f
) == EOF
&& error
== NULL
)
5312 error
= got_error_from_errno("fclose");
5319 fprintf(stderr
, "usage: %s tree [-iR] [-c commit] [-r repository-path] "
5320 "[path]\n", getprogname());
5324 static const struct got_error
*
5325 print_entry(struct got_tree_entry
*te
, const char *id
, const char *path
,
5326 const char *root_path
, struct got_repository
*repo
)
5328 const struct got_error
*err
= NULL
;
5329 int is_root_path
= (strcmp(path
, root_path
) == 0);
5330 const char *modestr
= "";
5331 mode_t mode
= got_tree_entry_get_mode(te
);
5332 char *link_target
= NULL
;
5334 path
+= strlen(root_path
);
5335 while (path
[0] == '/')
5338 if (got_object_tree_entry_is_submodule(te
))
5340 else if (S_ISLNK(mode
)) {
5343 err
= got_tree_entry_get_symlink_target(&link_target
, te
, repo
);
5346 for (i
= 0; link_target
[i
] != '\0'; i
++) {
5347 if (!isprint((unsigned char)link_target
[i
]))
5348 link_target
[i
] = '?';
5353 else if (S_ISDIR(mode
))
5355 else if (mode
& S_IXUSR
)
5358 printf("%s%s%s%s%s%s%s\n", id
? id
: "", path
,
5359 is_root_path
? "" : "/", got_tree_entry_get_name(te
), modestr
,
5360 link_target
? " -> ": "", link_target
? link_target
: "");
5366 static const struct got_error
*
5367 print_tree(const char *path
, struct got_commit_object
*commit
,
5368 int show_ids
, int recurse
, const char *root_path
,
5369 struct got_repository
*repo
)
5371 const struct got_error
*err
= NULL
;
5372 struct got_object_id
*tree_id
= NULL
;
5373 struct got_tree_object
*tree
= NULL
;
5376 err
= got_object_id_by_path(&tree_id
, repo
, commit
, path
);
5380 err
= got_object_open_as_tree(&tree
, repo
, tree_id
);
5383 nentries
= got_object_tree_get_nentries(tree
);
5384 for (i
= 0; i
< nentries
; i
++) {
5385 struct got_tree_entry
*te
;
5388 if (sigint_received
|| sigpipe_received
)
5391 te
= got_object_tree_get_entry(tree
, i
);
5394 err
= got_object_id_str(&id_str
,
5395 got_tree_entry_get_id(te
));
5398 if (asprintf(&id
, "%s ", id_str
) == -1) {
5399 err
= got_error_from_errno("asprintf");
5405 err
= print_entry(te
, id
, path
, root_path
, repo
);
5410 if (recurse
&& S_ISDIR(got_tree_entry_get_mode(te
))) {
5412 if (asprintf(&child_path
, "%s%s%s", path
,
5413 path
[0] == '/' && path
[1] == '\0' ? "" : "/",
5414 got_tree_entry_get_name(te
)) == -1) {
5415 err
= got_error_from_errno("asprintf");
5418 err
= print_tree(child_path
, commit
, show_ids
, 1,
5427 got_object_tree_close(tree
);
5432 static const struct got_error
*
5433 cmd_tree(int argc
, char *argv
[])
5435 const struct got_error
*error
;
5436 struct got_repository
*repo
= NULL
;
5437 struct got_worktree
*worktree
= NULL
;
5438 const char *path
, *refname
= NULL
;
5439 char *cwd
= NULL
, *repo_path
= NULL
, *in_repo_path
= NULL
;
5440 struct got_object_id
*commit_id
= NULL
;
5441 struct got_commit_object
*commit
= NULL
;
5442 char *commit_id_str
= NULL
;
5443 int show_ids
= 0, recurse
= 0;
5445 int *pack_fds
= NULL
;
5448 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
5453 while ((ch
= getopt(argc
, argv
, "c:iRr:")) != -1) {
5456 commit_id_str
= optarg
;
5465 repo_path
= realpath(optarg
, NULL
);
5466 if (repo_path
== NULL
)
5467 return got_error_from_errno2("realpath",
5469 got_path_strip_trailing_slashes(repo_path
);
5487 cwd
= getcwd(NULL
, 0);
5489 error
= got_error_from_errno("getcwd");
5493 error
= got_repo_pack_fds_open(&pack_fds
);
5497 if (repo_path
== NULL
) {
5498 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_CVG_DIR
);
5499 if (error
&& error
->code
!= GOT_ERR_NOT_WORKTREE
)
5505 strdup(got_worktree_get_repo_path(worktree
));
5506 if (repo_path
== NULL
)
5507 error
= got_error_from_errno("strdup");
5511 repo_path
= strdup(cwd
);
5512 if (repo_path
== NULL
) {
5513 error
= got_error_from_errno("strdup");
5519 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
5524 const char *prefix
= got_worktree_get_path_prefix(worktree
);
5527 if (path
== NULL
|| got_path_is_root_dir(path
))
5529 error
= got_worktree_resolve_path(&p
, worktree
, path
);
5532 if (asprintf(&in_repo_path
, "%s%s%s", prefix
,
5533 (p
[0] != '\0' && !got_path_is_root_dir(prefix
)) ? "/" : "",
5535 error
= got_error_from_errno("asprintf");
5540 error
= apply_unveil(got_repo_get_path(repo
), 1, NULL
);
5544 error
= apply_unveil(got_repo_get_path(repo
), 1, NULL
);
5549 error
= got_repo_map_path(&in_repo_path
, repo
, path
);
5554 if (commit_id_str
== NULL
) {
5555 struct got_reference
*head_ref
;
5557 refname
= got_worktree_get_head_ref_name(worktree
);
5559 refname
= GOT_REF_HEAD
;
5560 error
= got_ref_open(&head_ref
, repo
, refname
, 0);
5563 error
= got_ref_resolve(&commit_id
, repo
, head_ref
);
5564 got_ref_close(head_ref
);
5568 struct got_reflist_head refs
;
5570 error
= got_ref_list(&refs
, repo
, NULL
, got_ref_cmp_by_name
,
5574 error
= got_repo_match_object_id(&commit_id
, NULL
,
5575 commit_id_str
, GOT_OBJ_TYPE_COMMIT
, &refs
, repo
);
5576 got_ref_list_free(&refs
);
5582 /* Release work tree lock. */
5583 got_worktree_close(worktree
);
5587 error
= got_object_open_as_commit(&commit
, repo
, commit_id
);
5591 error
= print_tree(in_repo_path
, commit
, show_ids
, recurse
,
5592 in_repo_path
, repo
);
5599 got_object_commit_close(commit
);
5601 got_worktree_close(worktree
);
5603 const struct got_error
*close_err
= got_repo_close(repo
);
5608 const struct got_error
*pack_err
=
5609 got_repo_pack_fds_close(pack_fds
);
5619 fprintf(stderr
, "usage: %s status [-I] [-S status-codes] "
5620 "[-s status-codes] [path ...]\n", getprogname());
5624 struct got_status_arg
{
5629 static const struct got_error
*
5630 print_status(void *arg
, unsigned char status
, unsigned char staged_status
,
5631 const char *path
, struct got_object_id
*blob_id
,
5632 struct got_object_id
*staged_blob_id
, struct got_object_id
*commit_id
,
5633 int dirfd
, const char *de_name
)
5635 struct got_status_arg
*st
= arg
;
5637 if (status
== staged_status
&& (status
== GOT_STATUS_DELETE
))
5638 status
= GOT_STATUS_NO_CHANGE
;
5639 if (st
!= NULL
&& st
->status_codes
) {
5640 size_t ncodes
= strlen(st
->status_codes
);
5643 for (i
= 0; i
< ncodes
; i
++) {
5645 if (status
== st
->status_codes
[i
] ||
5646 staged_status
== st
->status_codes
[i
]) {
5651 if (status
== st
->status_codes
[i
] ||
5652 staged_status
== st
->status_codes
[i
])
5657 if (st
->suppress
&& j
== 0)
5664 printf("%c%c %s\n", status
, staged_status
, path
);
5668 static const struct got_error
*
5669 cmd_status(int argc
, char *argv
[])
5671 const struct got_error
*error
= NULL
;
5672 struct got_repository
*repo
= NULL
;
5673 struct got_worktree
*worktree
= NULL
;
5674 struct got_status_arg st
;
5676 struct got_pathlist_head paths
;
5677 int ch
, i
, no_ignores
= 0;
5678 int *pack_fds
= NULL
;
5682 memset(&st
, 0, sizeof(st
));
5683 st
.status_codes
= NULL
;
5687 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
5692 while ((ch
= getopt(argc
, argv
, "IS:s:")) != -1) {
5698 if (st
.status_codes
!= NULL
&& st
.suppress
== 0)
5699 option_conflict('S', 's');
5703 for (i
= 0; optarg
[i
] != '\0'; i
++) {
5704 switch (optarg
[i
]) {
5705 case GOT_STATUS_MODIFY
:
5706 case GOT_STATUS_ADD
:
5707 case GOT_STATUS_DELETE
:
5708 case GOT_STATUS_CONFLICT
:
5709 case GOT_STATUS_MISSING
:
5710 case GOT_STATUS_OBSTRUCTED
:
5711 case GOT_STATUS_UNVERSIONED
:
5712 case GOT_STATUS_MODE_CHANGE
:
5713 case GOT_STATUS_NONEXISTENT
:
5716 errx(1, "invalid status code '%c'",
5720 if (ch
== 's' && st
.suppress
)
5721 option_conflict('s', 'S');
5722 st
.status_codes
= optarg
;
5733 cwd
= getcwd(NULL
, 0);
5735 error
= got_error_from_errno("getcwd");
5739 error
= got_repo_pack_fds_open(&pack_fds
);
5743 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_CVG_DIR
);
5745 if (error
->code
== GOT_ERR_NOT_WORKTREE
)
5746 error
= wrap_not_worktree_error(error
, "status", cwd
);
5750 error
= got_repo_open(&repo
, got_worktree_get_repo_path(worktree
),
5755 error
= apply_unveil(got_repo_get_path(repo
), 1,
5756 got_worktree_get_root_path(worktree
));
5760 error
= get_worktree_paths_from_argv(&paths
, argc
, argv
, worktree
);
5764 error
= got_worktree_status(worktree
, &paths
, repo
, no_ignores
,
5765 print_status
, &st
, check_cancelled
, NULL
);
5768 const struct got_error
*pack_err
=
5769 got_repo_pack_fds_close(pack_fds
);
5774 const struct got_error
*close_err
= got_repo_close(repo
);
5779 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_PATH
);
5787 fprintf(stderr
, "usage: %s tag [-lVv] [-c commit] [-m message] "
5788 "[-r repository-path] [-s signer-id] name\n", getprogname());
5793 static const struct got_error
*
5794 sort_tags(struct got_reflist_head
*sorted
, struct got_reflist_head
*tags
)
5796 const struct got_error
*err
= NULL
;
5797 struct got_reflist_entry
*re
, *se
, *new;
5798 struct got_object_id
*re_id
, *se_id
;
5799 struct got_tag_object
*re_tag
, *se_tag
;
5800 time_t re_time
, se_time
;
5802 STAILQ_FOREACH(re
, tags
, entry
) {
5803 se
= STAILQ_FIRST(sorted
);
5805 err
= got_reflist_entry_dup(&new, re
);
5808 STAILQ_INSERT_HEAD(sorted
, new, entry
);
5811 err
= got_ref_resolve(&re_id
, repo
, re
->ref
);
5814 err
= got_object_open_as_tag(&re_tag
, repo
, re_id
);
5818 re_time
= got_object_tag_get_tagger_time(re_tag
);
5819 got_object_tag_close(re_tag
);
5823 err
= got_ref_resolve(&se_id
, repo
, re
->ref
);
5826 err
= got_object_open_as_tag(&se_tag
, repo
, se_id
);
5830 se_time
= got_object_tag_get_tagger_time(se_tag
);
5831 got_object_tag_close(se_tag
);
5833 if (se_time
> re_time
) {
5834 err
= got_reflist_entry_dup(&new, re
);
5837 STAILQ_INSERT_AFTER(sorted
, se
, new, entry
);
5840 se
= STAILQ_NEXT(se
, entry
);
5849 static const struct got_error
*
5850 get_tag_refname(char **refname
, const char *tag_name
)
5852 const struct got_error
*err
;
5854 if (strncmp("refs/tags/", tag_name
, 10) == 0) {
5855 *refname
= strdup(tag_name
);
5856 if (*refname
== NULL
)
5857 return got_error_from_errno("strdup");
5858 } else if (asprintf(refname
, "refs/tags/%s", tag_name
) == -1) {
5859 err
= got_error_from_errno("asprintf");
5867 static const struct got_error
*
5868 list_tags(struct got_repository
*repo
, const char *tag_name
, int verify_tags
,
5869 const char *allowed_signers
, const char *revoked_signers
, int verbosity
)
5871 static const struct got_error
*err
= NULL
;
5872 struct got_reflist_head refs
;
5873 struct got_reflist_entry
*re
;
5874 char *wanted_refname
= NULL
;
5879 err
= got_ref_list(&refs
, repo
, "refs/tags", got_ref_cmp_tags
, repo
);
5884 struct got_reference
*ref
;
5885 err
= get_tag_refname(&wanted_refname
, tag_name
);
5888 /* Wanted tag reference should exist. */
5889 err
= got_ref_open(&ref
, repo
, wanted_refname
, 0);
5895 TAILQ_FOREACH(re
, &refs
, entry
) {
5896 const char *refname
;
5897 char *refstr
, *tagmsg0
, *tagmsg
, *line
, *id_str
, *datestr
;
5899 const char *tagger
, *ssh_sig
= NULL
;
5900 char *sig_msg
= NULL
;
5902 struct got_object_id
*id
;
5903 struct got_tag_object
*tag
;
5904 struct got_commit_object
*commit
= NULL
;
5906 refname
= got_ref_get_name(re
->ref
);
5907 if (strncmp(refname
, "refs/tags/", 10) != 0 ||
5908 (wanted_refname
&& strcmp(refname
, wanted_refname
) != 0))
5911 refstr
= got_ref_to_str(re
->ref
);
5912 if (refstr
== NULL
) {
5913 err
= got_error_from_errno("got_ref_to_str");
5917 err
= got_ref_resolve(&id
, repo
, re
->ref
);
5920 err
= got_object_open_as_tag(&tag
, repo
, id
);
5922 if (err
->code
!= GOT_ERR_OBJ_TYPE
) {
5926 /* "lightweight" tag */
5927 err
= got_object_open_as_commit(&commit
, repo
, id
);
5932 tagger
= got_object_commit_get_committer(commit
);
5934 got_object_commit_get_committer_time(commit
);
5935 err
= got_object_id_str(&id_str
, id
);
5941 tagger
= got_object_tag_get_tagger(tag
);
5942 tagger_time
= got_object_tag_get_tagger_time(tag
);
5943 err
= got_object_id_str(&id_str
,
5944 got_object_tag_get_object_id(tag
));
5949 if (tag
&& verify_tags
) {
5950 ssh_sig
= got_sigs_get_tagmsg_ssh_signature(
5951 got_object_tag_get_message(tag
));
5952 if (ssh_sig
&& allowed_signers
== NULL
) {
5953 err
= got_error_msg(
5954 GOT_ERR_VERIFY_TAG_SIGNATURE
,
5955 "SSH signature verification requires "
5956 "setting allowed_signers in "
5962 printf("%stag %s %s\n", GOT_COMMIT_SEP_STR
, refname
, refstr
);
5964 printf("from: %s\n", tagger
);
5965 datestr
= get_datestr(&tagger_time
, datebuf
);
5967 printf("date: %s UTC\n", datestr
);
5969 printf("object: %s %s\n", GOT_OBJ_LABEL_COMMIT
, id_str
);
5971 switch (got_object_tag_get_object_type(tag
)) {
5972 case GOT_OBJ_TYPE_BLOB
:
5973 printf("object: %s %s\n", GOT_OBJ_LABEL_BLOB
,
5976 case GOT_OBJ_TYPE_TREE
:
5977 printf("object: %s %s\n", GOT_OBJ_LABEL_TREE
,
5980 case GOT_OBJ_TYPE_COMMIT
:
5981 printf("object: %s %s\n", GOT_OBJ_LABEL_COMMIT
,
5984 case GOT_OBJ_TYPE_TAG
:
5985 printf("object: %s %s\n", GOT_OBJ_LABEL_TAG
,
5995 err
= got_sigs_verify_tag_ssh(&sig_msg
, tag
, ssh_sig
,
5996 allowed_signers
, revoked_signers
, verbosity
);
5997 if (err
&& err
->code
== GOT_ERR_BAD_TAG_SIGNATURE
)
6001 printf("signature: %s", sig_msg
);
6007 err
= got_object_commit_get_logmsg(&tagmsg0
, commit
);
6010 got_object_commit_close(commit
);
6012 tagmsg0
= strdup(got_object_tag_get_message(tag
));
6013 got_object_tag_close(tag
);
6014 if (tagmsg0
== NULL
) {
6015 err
= got_error_from_errno("strdup");
6022 line
= strsep(&tagmsg
, "\n");
6024 printf(" %s\n", line
);
6029 got_ref_list_free(&refs
);
6030 free(wanted_refname
);
6032 if (err
== NULL
&& bad_sigs
)
6033 err
= got_error(GOT_ERR_BAD_TAG_SIGNATURE
);
6037 static const struct got_error
*
6038 get_tag_message(char **tagmsg
, char **tagmsg_path
, const char *commit_id_str
,
6039 const char *tag_name
, const char *repo_path
)
6041 const struct got_error
*err
= NULL
;
6042 char *template = NULL
, *initial_content
= NULL
;
6043 char *editor
= NULL
;
6044 int initial_content_len
;
6047 if (asprintf(&template, GOT_TMPDIR_STR
"/got-tagmsg") == -1) {
6048 err
= got_error_from_errno("asprintf");
6052 initial_content_len
= asprintf(&initial_content
,
6053 "\n# tagging commit %s as %s\n",
6054 commit_id_str
, tag_name
);
6055 if (initial_content_len
== -1) {
6056 err
= got_error_from_errno("asprintf");
6060 err
= got_opentemp_named_fd(tagmsg_path
, &fd
, template, "");
6064 if (write(fd
, initial_content
, initial_content_len
) == -1) {
6065 err
= got_error_from_errno2("write", *tagmsg_path
);
6068 if (close(fd
) == -1) {
6069 err
= got_error_from_errno2("close", *tagmsg_path
);
6074 err
= get_editor(&editor
);
6077 err
= edit_logmsg(tagmsg
, editor
, *tagmsg_path
, initial_content
,
6078 initial_content_len
, 1);
6080 free(initial_content
);
6084 if (fd
!= -1 && close(fd
) == -1 && err
== NULL
)
6085 err
= got_error_from_errno2("close", *tagmsg_path
);
6094 static const struct got_error
*
6095 add_tag(struct got_repository
*repo
, const char *tagger
,
6096 const char *tag_name
, const char *commit_arg
, const char *tagmsg_arg
,
6097 const char *signer_id
, int verbosity
)
6099 const struct got_error
*err
= NULL
;
6100 struct got_object_id
*commit_id
= NULL
, *tag_id
= NULL
;
6101 char *label
= NULL
, *commit_id_str
= NULL
;
6102 struct got_reference
*ref
= NULL
;
6103 char *refname
= NULL
, *tagmsg
= NULL
;
6104 char *tagmsg_path
= NULL
, *tag_id_str
= NULL
;
6105 int preserve_tagmsg
= 0;
6106 struct got_reflist_head refs
;
6111 * Don't let the user create a tag name with a leading '-'.
6112 * While technically a valid reference name, this case is usually
6113 * an unintended typo.
6115 if (tag_name
[0] == '-')
6116 return got_error_path(tag_name
, GOT_ERR_REF_NAME_MINUS
);
6118 err
= got_ref_list(&refs
, repo
, NULL
, got_ref_cmp_by_name
, NULL
);
6122 err
= got_repo_match_object_id(&commit_id
, &label
, commit_arg
,
6123 GOT_OBJ_TYPE_COMMIT
, &refs
, repo
);
6127 err
= got_object_id_str(&commit_id_str
, commit_id
);
6131 err
= get_tag_refname(&refname
, tag_name
);
6134 if (strncmp("refs/tags/", tag_name
, 10) == 0)
6137 err
= got_ref_open(&ref
, repo
, refname
, 0);
6139 err
= got_error(GOT_ERR_TAG_EXISTS
);
6141 } else if (err
->code
!= GOT_ERR_NOT_REF
)
6144 if (tagmsg_arg
== NULL
) {
6145 err
= get_tag_message(&tagmsg
, &tagmsg_path
, commit_id_str
,
6146 tag_name
, got_repo_get_path(repo
));
6148 if (err
->code
!= GOT_ERR_COMMIT_MSG_EMPTY
&&
6149 tagmsg_path
!= NULL
)
6150 preserve_tagmsg
= 1;
6153 /* Editor is done; we can now apply unveil(2) */
6154 err
= got_sigs_apply_unveil();
6157 err
= apply_unveil(got_repo_get_path(repo
), 0, NULL
);
6162 err
= got_object_tag_create(&tag_id
, tag_name
, commit_id
,
6163 tagger
, time(NULL
), tagmsg
? tagmsg
: tagmsg_arg
, signer_id
, repo
,
6167 preserve_tagmsg
= 1;
6171 err
= got_ref_alloc(&ref
, refname
, tag_id
);
6174 preserve_tagmsg
= 1;
6178 err
= got_ref_write(ref
, repo
);
6181 preserve_tagmsg
= 1;
6185 err
= got_object_id_str(&tag_id_str
, tag_id
);
6188 preserve_tagmsg
= 1;
6191 printf("Created tag %s\n", tag_id_str
);
6193 if (preserve_tagmsg
) {
6194 fprintf(stderr
, "%s: tag message preserved in %s\n",
6195 getprogname(), tagmsg_path
);
6196 } else if (tagmsg_path
&& unlink(tagmsg_path
) == -1 && err
== NULL
)
6197 err
= got_error_from_errno2("unlink", tagmsg_path
);
6202 free(commit_id_str
);
6206 got_ref_list_free(&refs
);
6210 static const struct got_error
*
6211 cmd_tag(int argc
, char *argv
[])
6213 const struct got_error
*error
= NULL
;
6214 struct got_repository
*repo
= NULL
;
6215 struct got_worktree
*worktree
= NULL
;
6216 char *cwd
= NULL
, *repo_path
= NULL
, *commit_id_str
= NULL
;
6217 char *gitconfig_path
= NULL
, *tagger
= NULL
;
6218 char *allowed_signers
= NULL
, *revoked_signers
= NULL
;
6219 const char *signer_id
= NULL
;
6220 const char *tag_name
= NULL
, *commit_id_arg
= NULL
, *tagmsg
= NULL
;
6221 int ch
, do_list
= 0, verify_tags
= 0, verbosity
= 0;
6222 int *pack_fds
= NULL
;
6225 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
6226 "sendfd unveil", NULL
) == -1)
6230 while ((ch
= getopt(argc
, argv
, "c:lm:r:s:Vv")) != -1) {
6233 commit_id_arg
= optarg
;
6242 repo_path
= realpath(optarg
, NULL
);
6243 if (repo_path
== NULL
) {
6244 error
= got_error_from_errno2("realpath",
6248 got_path_strip_trailing_slashes(repo_path
);
6259 else if (verbosity
< 3)
6271 if (do_list
|| verify_tags
) {
6272 if (commit_id_arg
!= NULL
)
6274 "-c option can only be used when creating a tag");
6277 option_conflict('l', 'm');
6279 option_conflict('V', 'm');
6283 option_conflict('l', 's');
6285 option_conflict('V', 's');
6289 } else if (argc
!= 1)
6295 cwd
= getcwd(NULL
, 0);
6297 error
= got_error_from_errno("getcwd");
6301 error
= got_repo_pack_fds_open(&pack_fds
);
6305 if (repo_path
== NULL
) {
6306 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_CVG_DIR
);
6307 if (error
&& error
->code
!= GOT_ERR_NOT_WORKTREE
)
6313 strdup(got_worktree_get_repo_path(worktree
));
6314 if (repo_path
== NULL
)
6315 error
= got_error_from_errno("strdup");
6319 repo_path
= strdup(cwd
);
6320 if (repo_path
== NULL
) {
6321 error
= got_error_from_errno("strdup");
6327 if (do_list
|| verify_tags
) {
6328 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
6331 error
= get_allowed_signers(&allowed_signers
, repo
, worktree
);
6334 error
= get_revoked_signers(&revoked_signers
, repo
, worktree
);
6338 /* Release work tree lock. */
6339 got_worktree_close(worktree
);
6344 * Remove "cpath" promise unless needed for signature tmpfile
6348 got_sigs_apply_unveil();
6351 if (pledge("stdio rpath wpath flock proc exec sendfd "
6352 "unveil", NULL
) == -1)
6356 error
= apply_unveil(got_repo_get_path(repo
), 1, NULL
);
6359 error
= list_tags(repo
, tag_name
, verify_tags
, allowed_signers
,
6360 revoked_signers
, verbosity
);
6362 error
= get_gitconfig_path(&gitconfig_path
);
6365 error
= got_repo_open(&repo
, repo_path
, gitconfig_path
,
6370 error
= get_author(&tagger
, repo
, worktree
);
6373 if (signer_id
== NULL
)
6374 signer_id
= get_signer_id(repo
, worktree
);
6378 error
= got_sigs_apply_unveil();
6382 error
= apply_unveil(got_repo_get_path(repo
), 0, NULL
);
6387 if (commit_id_arg
== NULL
) {
6388 struct got_reference
*head_ref
;
6389 struct got_object_id
*commit_id
;
6390 error
= got_ref_open(&head_ref
, repo
,
6391 worktree
? got_worktree_get_head_ref_name(worktree
)
6395 error
= got_ref_resolve(&commit_id
, repo
, head_ref
);
6396 got_ref_close(head_ref
);
6399 error
= got_object_id_str(&commit_id_str
, commit_id
);
6406 /* Release work tree lock. */
6407 got_worktree_close(worktree
);
6411 error
= add_tag(repo
, tagger
, tag_name
,
6412 commit_id_str
? commit_id_str
: commit_id_arg
, tagmsg
,
6413 signer_id
, verbosity
);
6417 const struct got_error
*close_err
= got_repo_close(repo
);
6422 got_worktree_close(worktree
);
6424 const struct got_error
*pack_err
=
6425 got_repo_pack_fds_close(pack_fds
);
6431 free(gitconfig_path
);
6432 free(commit_id_str
);
6434 free(allowed_signers
);
6435 free(revoked_signers
);
6442 fprintf(stderr
, "usage: %s add [-IR] path ...\n", getprogname());
6446 static const struct got_error
*
6447 add_progress(void *arg
, unsigned char status
, const char *path
)
6449 while (path
[0] == '/')
6451 printf("%c %s\n", status
, path
);
6455 static const struct got_error
*
6456 cmd_add(int argc
, char *argv
[])
6458 const struct got_error
*error
= NULL
;
6459 struct got_repository
*repo
= NULL
;
6460 struct got_worktree
*worktree
= NULL
;
6462 struct got_pathlist_head paths
;
6463 struct got_pathlist_entry
*pe
;
6464 int ch
, can_recurse
= 0, no_ignores
= 0;
6465 int *pack_fds
= NULL
;
6470 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
6475 while ((ch
= getopt(argc
, argv
, "IR")) != -1) {
6495 cwd
= getcwd(NULL
, 0);
6497 error
= got_error_from_errno("getcwd");
6501 error
= got_repo_pack_fds_open(&pack_fds
);
6505 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_CVG_DIR
);
6507 if (error
->code
== GOT_ERR_NOT_WORKTREE
)
6508 error
= wrap_not_worktree_error(error
, "add", cwd
);
6512 error
= got_repo_open(&repo
, got_worktree_get_repo_path(worktree
),
6517 error
= apply_unveil(got_repo_get_path(repo
), 1,
6518 got_worktree_get_root_path(worktree
));
6522 error
= get_worktree_paths_from_argv(&paths
, argc
, argv
, worktree
);
6529 TAILQ_FOREACH(pe
, &paths
, entry
) {
6530 if (asprintf(&ondisk_path
, "%s/%s",
6531 got_worktree_get_root_path(worktree
),
6533 error
= got_error_from_errno("asprintf");
6536 if (lstat(ondisk_path
, &sb
) == -1) {
6537 if (errno
== ENOENT
) {
6541 error
= got_error_from_errno2("lstat",
6547 if (S_ISDIR(sb
.st_mode
)) {
6548 error
= got_error_msg(GOT_ERR_BAD_PATH
,
6549 "adding directories requires -R option");
6555 error
= got_worktree_schedule_add(worktree
, &paths
, add_progress
,
6556 NULL
, repo
, no_ignores
);
6559 const struct got_error
*close_err
= got_repo_close(repo
);
6564 got_worktree_close(worktree
);
6566 const struct got_error
*pack_err
=
6567 got_repo_pack_fds_close(pack_fds
);
6571 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_PATH
);
6579 fprintf(stderr
, "usage: %s remove [-fkR] [-s status-codes] path ...\n",
6584 static const struct got_error
*
6585 print_remove_status(void *arg
, unsigned char status
,
6586 unsigned char staged_status
, const char *path
)
6588 while (path
[0] == '/')
6590 if (status
== GOT_STATUS_NONEXISTENT
)
6592 if (status
== staged_status
&& (status
== GOT_STATUS_DELETE
))
6593 status
= GOT_STATUS_NO_CHANGE
;
6594 printf("%c%c %s\n", status
, staged_status
, path
);
6598 static const struct got_error
*
6599 cmd_remove(int argc
, char *argv
[])
6601 const struct got_error
*error
= NULL
;
6602 struct got_worktree
*worktree
= NULL
;
6603 struct got_repository
*repo
= NULL
;
6604 const char *status_codes
= NULL
;
6606 struct got_pathlist_head paths
;
6607 struct got_pathlist_entry
*pe
;
6608 int ch
, delete_local_mods
= 0, can_recurse
= 0, keep_on_disk
= 0, i
;
6609 int ignore_missing_paths
= 0;
6610 int *pack_fds
= NULL
;
6615 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
6620 while ((ch
= getopt(argc
, argv
, "fkRs:")) != -1) {
6623 delete_local_mods
= 1;
6624 ignore_missing_paths
= 1;
6633 for (i
= 0; optarg
[i
] != '\0'; i
++) {
6634 switch (optarg
[i
]) {
6635 case GOT_STATUS_MODIFY
:
6636 delete_local_mods
= 1;
6638 case GOT_STATUS_MISSING
:
6639 ignore_missing_paths
= 1;
6642 errx(1, "invalid status code '%c'",
6646 status_codes
= optarg
;
6660 cwd
= getcwd(NULL
, 0);
6662 error
= got_error_from_errno("getcwd");
6666 error
= got_repo_pack_fds_open(&pack_fds
);
6670 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_CVG_DIR
);
6672 if (error
->code
== GOT_ERR_NOT_WORKTREE
)
6673 error
= wrap_not_worktree_error(error
, "remove", cwd
);
6677 error
= got_repo_open(&repo
, got_worktree_get_repo_path(worktree
),
6682 error
= apply_unveil(got_repo_get_path(repo
), 1,
6683 got_worktree_get_root_path(worktree
));
6687 error
= get_worktree_paths_from_argv(&paths
, argc
, argv
, worktree
);
6694 TAILQ_FOREACH(pe
, &paths
, entry
) {
6695 if (asprintf(&ondisk_path
, "%s/%s",
6696 got_worktree_get_root_path(worktree
),
6698 error
= got_error_from_errno("asprintf");
6701 if (lstat(ondisk_path
, &sb
) == -1) {
6702 if (errno
== ENOENT
) {
6706 error
= got_error_from_errno2("lstat",
6712 if (S_ISDIR(sb
.st_mode
)) {
6713 error
= got_error_msg(GOT_ERR_BAD_PATH
,
6714 "removing directories requires -R option");
6720 error
= got_worktree_schedule_delete(worktree
, &paths
,
6721 delete_local_mods
, status_codes
, print_remove_status
, NULL
,
6722 repo
, keep_on_disk
, ignore_missing_paths
);
6725 const struct got_error
*close_err
= got_repo_close(repo
);
6730 got_worktree_close(worktree
);
6732 const struct got_error
*pack_err
=
6733 got_repo_pack_fds_close(pack_fds
);
6737 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_PATH
);
6745 fprintf(stderr
, "usage: %s patch [-nR] [-c commit] [-p strip-count] "
6746 "[patchfile]\n", getprogname());
6750 static const struct got_error
*
6751 patch_from_stdin(int *patchfd
)
6753 const struct got_error
*err
= NULL
;
6756 sig_t sighup
, sigint
, sigquit
;
6758 *patchfd
= got_opentempfd();
6760 return got_error_from_errno("got_opentempfd");
6762 sighup
= signal(SIGHUP
, SIG_DFL
);
6763 sigint
= signal(SIGINT
, SIG_DFL
);
6764 sigquit
= signal(SIGQUIT
, SIG_DFL
);
6767 r
= read(0, buf
, sizeof(buf
));
6769 err
= got_error_from_errno("read");
6774 if (write(*patchfd
, buf
, r
) == -1) {
6775 err
= got_error_from_errno("write");
6780 signal(SIGHUP
, sighup
);
6781 signal(SIGINT
, sigint
);
6782 signal(SIGQUIT
, sigquit
);
6784 if (err
== NULL
&& lseek(*patchfd
, 0, SEEK_SET
) == -1)
6785 err
= got_error_from_errno("lseek");
6795 struct got_patch_progress_arg
{
6801 static const struct got_error
*
6802 patch_progress(void *arg
, const char *old
, const char *new,
6803 unsigned char status
, const struct got_error
*error
, int old_from
,
6804 int old_lines
, int new_from
, int new_lines
, int offset
,
6805 int ws_mangled
, const struct got_error
*hunk_err
)
6807 const char *path
= new == NULL
? old
: new;
6808 struct got_patch_progress_arg
*a
= arg
;
6810 while (*path
== '/')
6813 if (status
!= GOT_STATUS_NO_CHANGE
&&
6814 status
!= 0 /* per-hunk progress */) {
6815 printf("%c %s\n", status
, path
);
6816 a
->did_something
= 1;
6819 if (hunk_err
== NULL
) {
6820 if (status
== GOT_STATUS_CANNOT_UPDATE
)
6822 else if (status
== GOT_STATUS_CONFLICT
)
6827 fprintf(stderr
, "%s: %s\n", getprogname(), error
->msg
);
6829 if (offset
!= 0 || hunk_err
!= NULL
|| ws_mangled
) {
6830 printf("@@ -%d,%d +%d,%d @@ ", old_from
,
6831 old_lines
, new_from
, new_lines
);
6832 if (hunk_err
!= NULL
)
6833 printf("%s\n", hunk_err
->msg
);
6834 else if (offset
!= 0)
6835 printf("applied with offset %d\n", offset
);
6837 printf("hunk contains mangled whitespace\n");
6844 print_patch_progress_stats(struct got_patch_progress_arg
*ppa
)
6846 if (!ppa
->did_something
)
6849 if (ppa
->conflicts
> 0)
6850 printf("Files with merge conflicts: %d\n", ppa
->conflicts
);
6852 if (ppa
->rejects
> 0) {
6853 printf("Files where patch failed to apply: %d\n",
6858 static const struct got_error
*
6859 cmd_patch(int argc
, char *argv
[])
6861 const struct got_error
*error
= NULL
, *close_error
= NULL
;
6862 struct got_worktree
*worktree
= NULL
;
6863 struct got_repository
*repo
= NULL
;
6864 struct got_reflist_head refs
;
6865 struct got_object_id
*commit_id
= NULL
;
6866 const char *commit_id_str
= NULL
;
6870 int ch
, nop
= 0, strip
= -1, reverse
= 0;
6872 int *pack_fds
= NULL
;
6873 struct got_patch_progress_arg ppa
;
6878 if (pledge("stdio rpath wpath cpath fattr proc exec sendfd flock "
6879 "unveil", NULL
) == -1)
6883 while ((ch
= getopt(argc
, argv
, "c:np:R")) != -1) {
6886 commit_id_str
= optarg
;
6892 strip
= strtonum(optarg
, 0, INT_MAX
, &errstr
);
6894 errx(1, "pathname strip count is %s: %s",
6910 error
= patch_from_stdin(&patchfd
);
6913 } else if (argc
== 1) {
6914 patchfd
= open(argv
[0], O_RDONLY
);
6915 if (patchfd
== -1) {
6916 error
= got_error_from_errno2("open", argv
[0]);
6919 if (fstat(patchfd
, &sb
) == -1) {
6920 error
= got_error_from_errno2("fstat", argv
[0]);
6923 if (!S_ISREG(sb
.st_mode
)) {
6924 error
= got_error_path(argv
[0], GOT_ERR_BAD_FILETYPE
);
6930 if ((cwd
= getcwd(NULL
, 0)) == NULL
) {
6931 error
= got_error_from_errno("getcwd");
6935 error
= got_repo_pack_fds_open(&pack_fds
);
6939 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_CVG_DIR
);
6943 const char *repo_path
= got_worktree_get_repo_path(worktree
);
6944 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
6948 error
= apply_unveil(got_repo_get_path(repo
), 0,
6949 got_worktree_get_root_path(worktree
));
6953 error
= got_ref_list(&refs
, repo
, NULL
, got_ref_cmp_by_name
, NULL
);
6957 if (commit_id_str
!= NULL
) {
6958 error
= got_repo_match_object_id(&commit_id
, NULL
,
6959 commit_id_str
, GOT_OBJ_TYPE_COMMIT
, &refs
, repo
);
6964 memset(&ppa
, 0, sizeof(ppa
));
6965 error
= got_patch(patchfd
, worktree
, repo
, nop
, strip
, reverse
,
6966 commit_id
, patch_progress
, &ppa
, check_cancelled
, NULL
);
6967 print_patch_progress_stats(&ppa
);
6969 got_ref_list_free(&refs
);
6972 close_error
= got_repo_close(repo
);
6974 error
= close_error
;
6976 if (worktree
!= NULL
) {
6977 close_error
= got_worktree_close(worktree
);
6979 error
= close_error
;
6982 const struct got_error
*pack_err
=
6983 got_repo_pack_fds_close(pack_fds
);
6994 fprintf(stderr
, "usage: %s revert [-pR] [-F response-script] path ...\n",
6999 static const struct got_error
*
7000 revert_progress(void *arg
, unsigned char status
, const char *path
)
7002 if (status
== GOT_STATUS_UNVERSIONED
)
7005 while (path
[0] == '/')
7007 printf("%c %s\n", status
, path
);
7011 struct choose_patch_arg
{
7012 FILE *patch_script_file
;
7016 static const struct got_error
*
7017 show_change(unsigned char status
, const char *path
, FILE *patch_file
, int n
,
7018 int nchanges
, const char *action
)
7020 const struct got_error
*err
;
7022 size_t linesize
= 0;
7026 case GOT_STATUS_ADD
:
7027 printf("A %s\n%s this addition? [y/n] ", path
, action
);
7029 case GOT_STATUS_DELETE
:
7030 printf("D %s\n%s this deletion? [y/n] ", path
, action
);
7032 case GOT_STATUS_MODIFY
:
7033 if (fseek(patch_file
, 0L, SEEK_SET
) == -1)
7034 return got_error_from_errno("fseek");
7035 printf(GOT_COMMIT_SEP_STR
);
7036 while ((linelen
= getline(&line
, &linesize
, patch_file
)) != -1)
7038 if (linelen
== -1 && ferror(patch_file
)) {
7039 err
= got_error_from_errno("getline");
7044 printf(GOT_COMMIT_SEP_STR
);
7045 printf("M %s (change %d of %d)\n%s this change? [y/n/q] ",
7046 path
, n
, nchanges
, action
);
7049 return got_error_path(path
, GOT_ERR_FILE_STATUS
);
7056 static const struct got_error
*
7057 choose_patch(int *choice
, void *arg
, unsigned char status
, const char *path
,
7058 FILE *patch_file
, int n
, int nchanges
)
7060 const struct got_error
*err
= NULL
;
7062 size_t linesize
= 0;
7065 struct choose_patch_arg
*a
= arg
;
7067 *choice
= GOT_PATCH_CHOICE_NONE
;
7069 if (a
->patch_script_file
) {
7071 err
= show_change(status
, path
, patch_file
, n
, nchanges
,
7075 linelen
= getline(&line
, &linesize
, a
->patch_script_file
);
7076 if (linelen
== -1) {
7077 if (ferror(a
->patch_script_file
))
7078 return got_error_from_errno("getline");
7081 nl
= strchr(line
, '\n');
7084 if (strcmp(line
, "y") == 0) {
7085 *choice
= GOT_PATCH_CHOICE_YES
;
7087 } else if (strcmp(line
, "n") == 0) {
7088 *choice
= GOT_PATCH_CHOICE_NO
;
7090 } else if (strcmp(line
, "q") == 0 &&
7091 status
== GOT_STATUS_MODIFY
) {
7092 *choice
= GOT_PATCH_CHOICE_QUIT
;
7095 printf("invalid response '%s'\n", line
);
7100 while (resp
!= 'y' && resp
!= 'n' && resp
!= 'q') {
7101 err
= show_change(status
, path
, patch_file
, n
, nchanges
,
7108 if (status
== GOT_STATUS_MODIFY
) {
7109 if (resp
!= 'y' && resp
!= 'n' && resp
!= 'q') {
7110 printf("invalid response '%c'\n", resp
);
7113 } else if (resp
!= 'y' && resp
!= 'n') {
7114 printf("invalid response '%c'\n", resp
);
7120 *choice
= GOT_PATCH_CHOICE_YES
;
7121 else if (resp
== 'n')
7122 *choice
= GOT_PATCH_CHOICE_NO
;
7123 else if (resp
== 'q' && status
== GOT_STATUS_MODIFY
)
7124 *choice
= GOT_PATCH_CHOICE_QUIT
;
7129 struct wt_commitable_path_arg
{
7130 struct got_pathlist_head
*commit_paths
;
7135 * Shortcut work tree status callback to determine if the set of paths scanned
7136 * has at least one versioned path that is being modified and, if not NULL, is
7137 * in the arg->commit_paths list. Set arg and return GOT_ERR_FILE_MODIFIED as
7138 * soon as a path is passed with a status that satisfies this criteria.
7140 static const struct got_error
*
7141 worktree_has_commitable_path(void *arg
, unsigned char status
,
7142 unsigned char staged_status
, const char *path
,
7143 struct got_object_id
*blob_id
, struct got_object_id
*staged_blob_id
,
7144 struct got_object_id
*commit_id
, int dirfd
, const char *de_name
)
7146 struct wt_commitable_path_arg
*a
= arg
;
7148 if (status
== staged_status
&& (status
== GOT_STATUS_DELETE
))
7149 status
= GOT_STATUS_NO_CHANGE
;
7151 if (!(status
== GOT_STATUS_NO_CHANGE
||
7152 status
== GOT_STATUS_UNVERSIONED
) ||
7153 staged_status
!= GOT_STATUS_NO_CHANGE
) {
7154 if (a
->commit_paths
!= NULL
) {
7155 struct got_pathlist_entry
*pe
;
7157 TAILQ_FOREACH(pe
, a
->commit_paths
, entry
) {
7158 if (strncmp(path
, pe
->path
,
7159 pe
->path_len
) == 0) {
7160 *a
->has_changes
= 1;
7165 *a
->has_changes
= 1;
7167 if (*a
->has_changes
)
7168 return got_error(GOT_ERR_FILE_MODIFIED
);
7175 * Check that the changeset of the commit identified by id is
7176 * comprised of at least one modified path that is being committed.
7178 static const struct got_error
*
7179 commit_path_changed_in_worktree(struct wt_commitable_path_arg
*wcpa
,
7180 struct got_object_id
*id
, struct got_worktree
*worktree
,
7181 struct got_repository
*repo
)
7183 const struct got_error
*err
;
7184 struct got_pathlist_head paths
;
7185 struct got_commit_object
*commit
= NULL
, *pcommit
= NULL
;
7186 struct got_tree_object
*tree
= NULL
, *ptree
= NULL
;
7187 struct got_object_qid
*pid
;
7191 err
= got_object_open_as_commit(&commit
, repo
, id
);
7195 err
= got_object_open_as_tree(&tree
, repo
,
7196 got_object_commit_get_tree_id(commit
));
7200 pid
= STAILQ_FIRST(got_object_commit_get_parent_ids(commit
));
7202 err
= got_object_open_as_commit(&pcommit
, repo
, &pid
->id
);
7206 err
= got_object_open_as_tree(&ptree
, repo
,
7207 got_object_commit_get_tree_id(pcommit
));
7212 err
= got_diff_tree(ptree
, tree
, NULL
, NULL
, -1, -1, "", "", repo
,
7213 got_diff_tree_collect_changed_paths
, &paths
, 0);
7217 err
= got_worktree_status(worktree
, &paths
, repo
, 0,
7218 worktree_has_commitable_path
, wcpa
, check_cancelled
, NULL
);
7219 if (err
&& err
->code
== GOT_ERR_FILE_MODIFIED
) {
7221 * At least one changed path in the referenced commit is
7222 * modified in the work tree, that's all we need to know!
7228 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_ALL
);
7230 got_object_commit_close(commit
);
7232 got_object_commit_close(pcommit
);
7234 got_object_tree_close(tree
);
7236 got_object_tree_close(ptree
);
7241 * Remove any "logmsg" reference comprised entirely of paths that have
7242 * been reverted in this work tree. If any path in the logmsg ref changeset
7243 * remains in a changed state in the worktree, do not remove the reference.
7245 static const struct got_error
*
7246 rm_logmsg_ref(struct got_worktree
*worktree
, struct got_repository
*repo
)
7248 const struct got_error
*err
;
7249 struct got_reflist_head refs
;
7250 struct got_reflist_entry
*re
;
7251 struct got_commit_object
*commit
= NULL
;
7252 struct got_object_id
*commit_id
= NULL
;
7253 struct wt_commitable_path_arg wcpa
;
7254 char *uuidstr
= NULL
;
7258 err
= got_worktree_get_uuid(&uuidstr
, worktree
);
7262 err
= got_ref_list(&refs
, repo
, "refs/got/worktree",
7263 got_ref_cmp_by_name
, repo
);
7267 TAILQ_FOREACH(re
, &refs
, entry
) {
7268 const char *refname
;
7269 int has_changes
= 0;
7271 refname
= got_ref_get_name(re
->ref
);
7273 if (!strncmp(refname
, GOT_WORKTREE_CHERRYPICK_REF_PREFIX
,
7274 GOT_WORKTREE_CHERRYPICK_REF_PREFIX_LEN
))
7275 refname
+= GOT_WORKTREE_CHERRYPICK_REF_PREFIX_LEN
+ 1;
7276 else if (!strncmp(refname
, GOT_WORKTREE_BACKOUT_REF_PREFIX
,
7277 GOT_WORKTREE_BACKOUT_REF_PREFIX_LEN
))
7278 refname
+= GOT_WORKTREE_BACKOUT_REF_PREFIX_LEN
+ 1;
7282 if (strncmp(refname
, uuidstr
, GOT_WORKTREE_UUID_STRLEN
) == 0)
7283 refname
+= GOT_WORKTREE_UUID_STRLEN
+ 1; /* skip '-' */
7287 err
= got_repo_match_object_id(&commit_id
, NULL
, refname
,
7288 GOT_OBJ_TYPE_COMMIT
, NULL
, repo
);
7292 err
= got_object_open_as_commit(&commit
, repo
, commit_id
);
7296 wcpa
.commit_paths
= NULL
;
7297 wcpa
.has_changes
= &has_changes
;
7299 err
= commit_path_changed_in_worktree(&wcpa
, commit_id
,
7305 err
= got_ref_delete(re
->ref
, repo
);
7310 got_object_commit_close(commit
);
7319 got_ref_list_free(&refs
);
7321 got_object_commit_close(commit
);
7325 static const struct got_error
*
7326 cmd_revert(int argc
, char *argv
[])
7328 const struct got_error
*error
= NULL
;
7329 struct got_worktree
*worktree
= NULL
;
7330 struct got_repository
*repo
= NULL
;
7331 char *cwd
= NULL
, *path
= NULL
;
7332 struct got_pathlist_head paths
;
7333 struct got_pathlist_entry
*pe
;
7334 int ch
, can_recurse
= 0, pflag
= 0;
7335 FILE *patch_script_file
= NULL
;
7336 const char *patch_script_path
= NULL
;
7337 struct choose_patch_arg cpa
;
7338 int *pack_fds
= NULL
;
7343 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
7344 "unveil", NULL
) == -1)
7348 while ((ch
= getopt(argc
, argv
, "F:pR")) != -1) {
7351 patch_script_path
= optarg
;
7370 if (patch_script_path
&& !pflag
)
7371 errx(1, "-F option can only be used together with -p option");
7373 cwd
= getcwd(NULL
, 0);
7375 error
= got_error_from_errno("getcwd");
7379 error
= got_repo_pack_fds_open(&pack_fds
);
7383 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_CVG_DIR
);
7385 if (error
->code
== GOT_ERR_NOT_WORKTREE
)
7386 error
= wrap_not_worktree_error(error
, "revert", cwd
);
7390 error
= got_repo_open(&repo
, got_worktree_get_repo_path(worktree
),
7395 if (patch_script_path
) {
7396 patch_script_file
= fopen(patch_script_path
, "re");
7397 if (patch_script_file
== NULL
) {
7398 error
= got_error_from_errno2("fopen",
7405 * XXX "c" perm needed on repo dir to delete merge references.
7407 error
= apply_unveil(got_repo_get_path(repo
), 0,
7408 got_worktree_get_root_path(worktree
));
7412 error
= get_worktree_paths_from_argv(&paths
, argc
, argv
, worktree
);
7419 TAILQ_FOREACH(pe
, &paths
, entry
) {
7420 if (asprintf(&ondisk_path
, "%s/%s",
7421 got_worktree_get_root_path(worktree
),
7423 error
= got_error_from_errno("asprintf");
7426 if (lstat(ondisk_path
, &sb
) == -1) {
7427 if (errno
== ENOENT
) {
7431 error
= got_error_from_errno2("lstat",
7437 if (S_ISDIR(sb
.st_mode
)) {
7438 error
= got_error_msg(GOT_ERR_BAD_PATH
,
7439 "reverting directories requires -R option");
7445 cpa
.patch_script_file
= patch_script_file
;
7446 cpa
.action
= "revert";
7447 error
= got_worktree_revert(worktree
, &paths
, revert_progress
, NULL
,
7448 pflag
? choose_patch
: NULL
, &cpa
, repo
);
7450 error
= rm_logmsg_ref(worktree
, repo
);
7452 if (patch_script_file
&& fclose(patch_script_file
) == EOF
&&
7454 error
= got_error_from_errno2("fclose", patch_script_path
);
7456 const struct got_error
*close_err
= got_repo_close(repo
);
7461 got_worktree_close(worktree
);
7463 const struct got_error
*pack_err
=
7464 got_repo_pack_fds_close(pack_fds
);
7468 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_PATH
);
7477 fprintf(stderr
, "usage: %s commit [-CNnS] [-A author] [-F path] "
7478 "[-m message] [path ...]\n", getprogname());
7482 struct collect_commit_logmsg_arg
{
7483 const char *cmdline_log
;
7484 const char *prepared_log
;
7485 const char *merged_log
;
7486 int non_interactive
;
7488 const char *worktree_path
;
7489 const char *repo_path
;
7490 const char *dial_proto
;
7494 static const struct got_error
*
7495 read_prepared_logmsg(char **logmsg
, const char *path
)
7497 const struct got_error
*err
= NULL
;
7503 memset(&sb
, 0, sizeof(sb
));
7505 f
= fopen(path
, "re");
7507 return got_error_from_errno2("fopen", path
);
7509 if (fstat(fileno(f
), &sb
) == -1) {
7510 err
= got_error_from_errno2("fstat", path
);
7513 if (sb
.st_size
== 0) {
7514 err
= got_error(GOT_ERR_COMMIT_MSG_EMPTY
);
7518 *logmsg
= malloc(sb
.st_size
+ 1);
7519 if (*logmsg
== NULL
) {
7520 err
= got_error_from_errno("malloc");
7524 r
= fread(*logmsg
, 1, sb
.st_size
, f
);
7525 if (r
!= sb
.st_size
) {
7527 err
= got_error_from_errno2("fread", path
);
7529 err
= got_error(GOT_ERR_IO
);
7532 (*logmsg
)[sb
.st_size
] = '\0';
7534 if (fclose(f
) == EOF
&& err
== NULL
)
7535 err
= got_error_from_errno2("fclose", path
);
7543 static const struct got_error
*
7544 collect_commit_logmsg(struct got_pathlist_head
*commitable_paths
,
7545 const char *diff_path
, char **logmsg
, void *arg
)
7547 char *initial_content
= NULL
;
7548 struct got_pathlist_entry
*pe
;
7549 const struct got_error
*err
= NULL
;
7550 char *template = NULL
;
7551 char *prepared_msg
= NULL
, *merged_msg
= NULL
;
7552 struct collect_commit_logmsg_arg
*a
= arg
;
7553 int initial_content_len
;
7557 /* if a message was specified on the command line, just use it */
7558 if (a
->cmdline_log
!= NULL
&& *a
->cmdline_log
!= '\0') {
7559 len
= strlen(a
->cmdline_log
) + 1;
7560 *logmsg
= malloc(len
+ 1);
7561 if (*logmsg
== NULL
)
7562 return got_error_from_errno("malloc");
7563 strlcpy(*logmsg
, a
->cmdline_log
, len
);
7565 } else if (a
->prepared_log
!= NULL
&& a
->non_interactive
)
7566 return read_prepared_logmsg(logmsg
, a
->prepared_log
);
7568 if (asprintf(&template, "%s/logmsg", a
->worktree_path
) == -1)
7569 return got_error_from_errno("asprintf");
7571 err
= got_opentemp_named_fd(&a
->logmsg_path
, &fd
, template, "");
7575 if (a
->prepared_log
) {
7576 err
= read_prepared_logmsg(&prepared_msg
, a
->prepared_log
);
7579 } else if (a
->merged_log
) {
7580 err
= read_prepared_logmsg(&merged_msg
, a
->merged_log
);
7585 initial_content_len
= asprintf(&initial_content
,
7586 "%s%s\n# changes to be committed:\n",
7587 prepared_msg
? prepared_msg
: "",
7588 merged_msg
? merged_msg
: "");
7589 if (initial_content_len
== -1) {
7590 err
= got_error_from_errno("asprintf");
7594 if (write(fd
, initial_content
, initial_content_len
) == -1) {
7595 err
= got_error_from_errno2("write", a
->logmsg_path
);
7599 TAILQ_FOREACH(pe
, commitable_paths
, entry
) {
7600 struct got_commitable
*ct
= pe
->data
;
7601 dprintf(fd
, "# %c %s\n",
7602 got_commitable_get_status(ct
),
7603 got_commitable_get_path(ct
));
7607 dprintf(fd
, "# detailed changes can be viewed in %s\n",
7611 if (close(fd
) == -1) {
7612 err
= got_error_from_errno2("close", a
->logmsg_path
);
7617 err
= edit_logmsg(logmsg
, a
->editor
, a
->logmsg_path
, initial_content
,
7618 initial_content_len
, a
->prepared_log
? 0 : 1);
7620 free(initial_content
);
7625 if (fd
!= -1 && close(fd
) == -1 && err
== NULL
)
7626 err
= got_error_from_errno2("close", a
->logmsg_path
);
7628 /* Editor is done; we can now apply unveil(2) */
7630 err
= got_dial_apply_unveil(a
->dial_proto
);
7632 err
= apply_unveil(a
->repo_path
, 0, a
->worktree_path
);
7640 static const struct got_error
*
7641 cat_logmsg(FILE *f
, struct got_commit_object
*commit
, const char *idstr
,
7642 const char *type
, int has_content
)
7644 const struct got_error
*err
= NULL
;
7645 char *logmsg
= NULL
;
7647 err
= got_object_commit_get_logmsg(&logmsg
, commit
);
7651 if (fprintf(f
, "%s# log message of %s commit %s:%s",
7652 has_content
? "\n" : "", type
, idstr
, logmsg
) < 0)
7653 err
= got_ferror(f
, GOT_ERR_IO
);
7660 * Lookup "logmsg" references of backed-out and cherrypicked commits
7661 * belonging to the current work tree. If found, and the worktree has
7662 * at least one modified file that was changed in the referenced commit,
7663 * add its log message to a new temporary file at *logmsg_path.
7664 * Add all refs found to matched_refs to be scheduled for removal on
7665 * successful commit.
7667 static const struct got_error
*
7668 lookup_logmsg_ref(char **logmsg_path
, struct got_pathlist_head
*paths
,
7669 struct got_reflist_head
*matched_refs
, struct got_worktree
*worktree
,
7670 struct got_repository
*repo
)
7672 const struct got_error
*err
;
7673 struct got_commit_object
*commit
= NULL
;
7674 struct got_object_id
*id
= NULL
;
7675 struct got_reflist_head refs
;
7676 struct got_reflist_entry
*re
, *re_match
;
7678 char *uuidstr
= NULL
;
7679 int added_logmsg
= 0;
7683 *logmsg_path
= NULL
;
7685 err
= got_worktree_get_uuid(&uuidstr
, worktree
);
7689 err
= got_ref_list(&refs
, repo
, "refs/got/worktree",
7690 got_ref_cmp_by_name
, repo
);
7694 TAILQ_FOREACH(re
, &refs
, entry
) {
7695 const char *refname
, *type
;
7696 struct wt_commitable_path_arg wcpa
;
7699 refname
= got_ref_get_name(re
->ref
);
7701 if (strncmp(refname
, GOT_WORKTREE_CHERRYPICK_REF_PREFIX
,
7702 GOT_WORKTREE_CHERRYPICK_REF_PREFIX_LEN
) == 0) {
7703 refname
+= GOT_WORKTREE_CHERRYPICK_REF_PREFIX_LEN
+ 1;
7704 type
= "cherrypicked";
7705 } else if (strncmp(refname
, GOT_WORKTREE_BACKOUT_REF_PREFIX
,
7706 GOT_WORKTREE_BACKOUT_REF_PREFIX_LEN
) == 0) {
7707 refname
+= GOT_WORKTREE_BACKOUT_REF_PREFIX_LEN
+ 1;
7708 type
= "backed-out";
7712 if (strncmp(refname
, uuidstr
, GOT_WORKTREE_UUID_STRLEN
) == 0)
7713 refname
+= GOT_WORKTREE_UUID_STRLEN
+ 1; /* skip '-' */
7717 err
= got_repo_match_object_id(&id
, NULL
, refname
,
7718 GOT_OBJ_TYPE_COMMIT
, NULL
, repo
);
7722 err
= got_object_open_as_commit(&commit
, repo
, id
);
7726 wcpa
.commit_paths
= paths
;
7727 wcpa
.has_changes
= &add_logmsg
;
7729 err
= commit_path_changed_in_worktree(&wcpa
, id
,
7736 err
= got_opentemp_named(logmsg_path
, &f
,
7737 "got-commit-logmsg", "");
7741 err
= cat_logmsg(f
, commit
, refname
, type
,
7748 err
= got_reflist_entry_dup(&re_match
, re
);
7751 TAILQ_INSERT_HEAD(matched_refs
, re_match
, entry
);
7754 got_object_commit_close(commit
);
7763 got_ref_list_free(&refs
);
7765 got_object_commit_close(commit
);
7766 if (f
&& fclose(f
) == EOF
&& err
== NULL
)
7767 err
= got_error_from_errno("fclose");
7768 if (!added_logmsg
) {
7769 if (*logmsg_path
&& unlink(*logmsg_path
) != 0 && err
== NULL
)
7770 err
= got_error_from_errno2("unlink", *logmsg_path
);
7771 *logmsg_path
= NULL
;
7776 static const struct got_error
*
7777 cmd_commit(int argc
, char *argv
[])
7779 const struct got_error
*error
= NULL
;
7780 struct got_worktree
*worktree
= NULL
;
7781 struct got_repository
*repo
= NULL
;
7782 char *cwd
= NULL
, *id_str
= NULL
;
7783 struct got_object_id
*id
= NULL
;
7784 const char *logmsg
= NULL
;
7785 char *prepared_logmsg
= NULL
, *merged_logmsg
= NULL
;
7786 struct collect_commit_logmsg_arg cl_arg
;
7787 const char *author
= NULL
;
7788 char *gitconfig_path
= NULL
, *editor
= NULL
, *committer
= NULL
;
7789 int ch
, rebase_in_progress
, histedit_in_progress
, preserve_logmsg
= 0;
7790 int allow_bad_symlinks
= 0, non_interactive
= 0, merge_in_progress
= 0;
7791 int show_diff
= 1, commit_conflicts
= 0;
7792 struct got_pathlist_head paths
;
7793 struct got_reflist_head refs
;
7794 struct got_reflist_entry
*re
;
7795 int *pack_fds
= NULL
;
7796 const struct got_gotconfig
*repo_conf
= NULL
, *worktree_conf
= NULL
;
7797 const struct got_remote_repo
*remotes
, *remote
= NULL
;
7799 char *proto
= NULL
, *host
= NULL
, *port
= NULL
;
7800 char *repo_name
= NULL
, *server_path
= NULL
;
7801 const char *remote_name
;
7807 cl_arg
.logmsg_path
= NULL
;
7810 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
7811 "unveil", NULL
) == -1)
7815 while ((ch
= getopt(argc
, argv
, "A:CF:m:NnS")) != -1) {
7819 error
= valid_author(author
);
7824 commit_conflicts
= 1;
7828 option_conflict('F', 'm');
7829 prepared_logmsg
= realpath(optarg
, NULL
);
7830 if (prepared_logmsg
== NULL
)
7831 return got_error_from_errno2("realpath",
7835 if (prepared_logmsg
)
7836 option_conflict('m', 'F');
7840 non_interactive
= 1;
7846 allow_bad_symlinks
= 1;
7857 cwd
= getcwd(NULL
, 0);
7859 error
= got_error_from_errno("getcwd");
7863 error
= got_repo_pack_fds_open(&pack_fds
);
7867 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_CVG_DIR
);
7869 if (error
->code
== GOT_ERR_NOT_WORKTREE
)
7870 error
= wrap_not_worktree_error(error
, "commit", cwd
);
7874 error
= got_worktree_rebase_in_progress(&rebase_in_progress
, worktree
);
7877 if (rebase_in_progress
) {
7878 error
= got_error(GOT_ERR_REBASING
);
7882 error
= got_worktree_histedit_in_progress(&histedit_in_progress
,
7887 error
= get_gitconfig_path(&gitconfig_path
);
7890 error
= got_repo_open(&repo
, got_worktree_get_repo_path(worktree
),
7891 gitconfig_path
, pack_fds
);
7895 error
= got_worktree_merge_in_progress(&merge_in_progress
, worktree
, repo
);
7898 if (merge_in_progress
) {
7899 error
= got_error(GOT_ERR_MERGE_BUSY
);
7903 error
= get_author(&committer
, repo
, worktree
);
7910 remote_name
= GOT_SEND_DEFAULT_REMOTE_NAME
;
7911 worktree_conf
= got_worktree_get_gotconfig(worktree
);
7912 if (worktree_conf
) {
7913 got_gotconfig_get_remotes(&nremotes
, &remotes
,
7915 for (i
= 0; i
< nremotes
; i
++) {
7916 if (strcmp(remotes
[i
].name
, remote_name
) == 0) {
7917 remote
= &remotes
[i
];
7922 if (remote
== NULL
) {
7923 repo_conf
= got_repo_get_gotconfig(repo
);
7925 got_gotconfig_get_remotes(&nremotes
, &remotes
,
7927 for (i
= 0; i
< nremotes
; i
++) {
7928 if (strcmp(remotes
[i
].name
, remote_name
) == 0) {
7929 remote
= &remotes
[i
];
7935 if (remote
== NULL
) {
7936 got_repo_get_gitconfig_remotes(&nremotes
, &remotes
, repo
);
7937 for (i
= 0; i
< nremotes
; i
++) {
7938 if (strcmp(remotes
[i
].name
, remote_name
) == 0) {
7939 remote
= &remotes
[i
];
7944 if (remote
== NULL
) {
7945 error
= got_error_path(remote_name
, GOT_ERR_NO_REMOTE
);
7949 error
= got_dial_parse_uri(&proto
, &host
, &port
, &server_path
,
7950 &repo_name
, remote
->fetch_url
);
7954 if (strcmp(proto
, "git") == 0) {
7956 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
7957 "sendfd dns inet unveil", NULL
) == -1)
7960 } else if (strcmp(proto
, "git+ssh") == 0 ||
7961 strcmp(proto
, "ssh") == 0) {
7963 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
7964 "sendfd unveil", NULL
) == -1)
7967 } else if (strcmp(proto
, "http") == 0 ||
7968 strcmp(proto
, "git+http") == 0) {
7969 error
= got_error_path(proto
, GOT_ERR_NOT_IMPL
);
7972 error
= got_error_path(proto
, GOT_ERR_BAD_PROTO
);
7977 /*if (verbosity >= 0) {
7978 printf("Connecting to \"%s\" %s://%s%s%s%s%s\n",
7979 remote->name, proto, host,
7980 port ? ":" : "", port ? port : "",
7981 *server_path == '/' ? "" : "/", server_path);
7985 * unveil(2) traverses exec(2); if an editor is used we have
7986 * to apply unveil after the log message has been written during
7989 if (logmsg
== NULL
|| strlen(logmsg
) == 0)
7990 error
= get_editor(&editor
);
7992 error
= got_dial_apply_unveil(proto
);
7995 error
= apply_unveil(got_repo_get_path(repo
), 0,
7996 got_worktree_get_root_path(worktree
));
8001 if (prepared_logmsg
== NULL
) {
8002 error
= lookup_logmsg_ref(&merged_logmsg
,
8003 argc
> 0 ? &paths
: NULL
, &refs
, worktree
, repo
);
8008 error
= get_worktree_paths_from_argv(&paths
, argc
, argv
, worktree
);
8012 cl_arg
.editor
= editor
;
8013 cl_arg
.cmdline_log
= logmsg
;
8014 cl_arg
.prepared_log
= prepared_logmsg
;
8015 cl_arg
.merged_log
= merged_logmsg
;
8016 cl_arg
.non_interactive
= non_interactive
;
8017 cl_arg
.worktree_path
= got_worktree_get_root_path(worktree
);
8018 cl_arg
.repo_path
= got_repo_get_path(repo
);
8019 cl_arg
.dial_proto
= proto
;
8020 error
= got_worktree_cvg_commit(&id
, worktree
, &paths
, author
,
8021 committer
, allow_bad_symlinks
, show_diff
, commit_conflicts
,
8022 collect_commit_logmsg
, &cl_arg
, print_status
, NULL
, proto
, host
,
8023 port
, server_path
, verbosity
, remote
, check_cancelled
, repo
);
8025 if (error
->code
!= GOT_ERR_COMMIT_MSG_EMPTY
&&
8026 cl_arg
.logmsg_path
!= NULL
)
8027 preserve_logmsg
= 1;
8031 error
= got_object_id_str(&id_str
, id
);
8034 printf("Created commit %s\n", id_str
);
8036 TAILQ_FOREACH(re
, &refs
, entry
) {
8037 error
= got_ref_delete(re
->ref
, repo
);
8043 if (preserve_logmsg
) {
8044 fprintf(stderr
, "%s: log message preserved in %s\n",
8045 getprogname(), cl_arg
.logmsg_path
);
8046 } else if (cl_arg
.logmsg_path
&& unlink(cl_arg
.logmsg_path
) == -1 &&
8048 error
= got_error_from_errno2("unlink", cl_arg
.logmsg_path
);
8049 free(cl_arg
.logmsg_path
);
8050 if (merged_logmsg
&& unlink(merged_logmsg
) == -1 && error
== NULL
)
8051 error
= got_error_from_errno2("unlink", merged_logmsg
);
8052 free(merged_logmsg
);
8054 const struct got_error
*close_err
= got_repo_close(repo
);
8059 got_worktree_close(worktree
);
8061 const struct got_error
*pack_err
=
8062 got_repo_pack_fds_close(pack_fds
);
8066 got_ref_list_free(&refs
);
8067 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_PATH
);
8070 free(gitconfig_path
);
8073 free(prepared_logmsg
);
8078 * Print and if delete is set delete all ref_prefix references.
8079 * If wanted_ref is not NULL, only print or delete this reference.
8081 static const struct got_error
*
8082 process_logmsg_refs(const char *ref_prefix
, size_t prefix_len
,
8083 const char *wanted_ref
, int delete, struct got_worktree
*worktree
,
8084 struct got_repository
*repo
)
8086 const struct got_error
*err
;
8087 struct got_pathlist_head paths
;
8088 struct got_reflist_head refs
;
8089 struct got_reflist_entry
*re
;
8090 struct got_reflist_object_id_map
*refs_idmap
= NULL
;
8091 struct got_commit_object
*commit
= NULL
;
8092 struct got_object_id
*id
= NULL
;
8093 const char *header_prefix
;
8094 char *uuidstr
= NULL
;
8100 err
= got_ref_list(&refs
, repo
, NULL
, got_ref_cmp_by_name
, repo
);
8104 err
= got_reflist_object_id_map_create(&refs_idmap
, &refs
, repo
);
8108 if (worktree
!= NULL
) {
8109 err
= got_worktree_get_uuid(&uuidstr
, worktree
);
8115 if (strncmp(wanted_ref
, "refs/heads/", 11) == 0)
8119 if (strcmp(ref_prefix
, GOT_WORKTREE_BACKOUT_REF_PREFIX
) == 0)
8120 header_prefix
= "backout";
8122 header_prefix
= "cherrypick";
8124 TAILQ_FOREACH(re
, &refs
, entry
) {
8125 const char *refname
, *wt
;
8127 refname
= got_ref_get_name(re
->ref
);
8129 err
= check_cancelled(NULL
);
8133 if (strncmp(refname
, ref_prefix
, prefix_len
) == 0)
8134 refname
+= prefix_len
+ 1; /* skip '-' delimiter */
8140 if (worktree
== NULL
|| strncmp(refname
, uuidstr
,
8141 GOT_WORKTREE_UUID_STRLEN
) == 0)
8142 refname
+= GOT_WORKTREE_UUID_STRLEN
+ 1; /* skip '-' */
8146 err
= got_repo_match_object_id(&id
, NULL
, refname
,
8147 GOT_OBJ_TYPE_COMMIT
, NULL
, repo
);
8151 err
= got_object_open_as_commit(&commit
, repo
, id
);
8156 found
= strncmp(wanted_ref
, refname
,
8157 strlen(wanted_ref
)) == 0;
8158 if (wanted_ref
&& !found
) {
8159 struct got_reflist_head
*ci_refs
;
8161 ci_refs
= got_reflist_object_id_map_lookup(refs_idmap
,
8165 char *refs_str
= NULL
;
8166 char const *r
= NULL
;
8168 err
= build_refs_str(&refs_str
, ci_refs
, id
,
8175 if (strncmp(r
, wanted_ref
,
8176 strlen(wanted_ref
)) == 0) {
8188 if (wanted_ref
== NULL
|| found
) {
8190 err
= got_ref_delete(re
->ref
, repo
);
8193 printf("Deleted: ");
8194 err
= print_commit_oneline(commit
, id
, repo
,
8198 * Print paths modified by commit to help
8199 * associate commits with worktree changes.
8201 err
= get_changed_paths(&paths
, commit
,
8206 err
= print_commit(commit
, id
, repo
, NULL
,
8207 &paths
, NULL
, 0, 0, refs_idmap
, NULL
,
8209 got_pathlist_free(&paths
,
8210 GOT_PATHLIST_FREE_ALL
);
8212 if (worktree
== NULL
)
8213 printf("work tree: %.*s\n\n",
8214 GOT_WORKTREE_UUID_STRLEN
, wt
);
8220 got_object_commit_close(commit
);
8226 if (wanted_ref
!= NULL
&& !found
)
8227 err
= got_error_fmt(GOT_ERR_NOT_REF
, "%s", wanted_ref
);
8232 got_ref_list_free(&refs
);
8233 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_ALL
);
8235 got_reflist_object_id_map_free(refs_idmap
);
8237 got_object_commit_close(commit
);
8242 * Create new temp "logmsg" ref of the backed-out or cherrypicked commit
8243 * identified by id for log messages to prepopulate the editor on commit.
8245 static const struct got_error
*
8246 logmsg_ref(struct got_object_id
*id
, const char *prefix
,
8247 struct got_worktree
*worktree
, struct got_repository
*repo
)
8249 const struct got_error
*err
= NULL
;
8250 char *idstr
, *ref
= NULL
, *refname
= NULL
;
8251 int histedit_in_progress
;
8252 int rebase_in_progress
, merge_in_progress
;
8255 * Silenty refuse to create merge reference if any histedit, merge,
8256 * or rebase operation is in progress.
8258 err
= got_worktree_histedit_in_progress(&histedit_in_progress
,
8262 if (histedit_in_progress
)
8265 err
= got_worktree_rebase_in_progress(&rebase_in_progress
, worktree
);
8268 if (rebase_in_progress
)
8271 err
= got_worktree_merge_in_progress(&merge_in_progress
, worktree
,
8275 if (merge_in_progress
)
8278 err
= got_object_id_str(&idstr
, id
);
8282 err
= got_worktree_get_logmsg_ref_name(&refname
, worktree
, prefix
);
8286 if (asprintf(&ref
, "%s-%s", refname
, idstr
) == -1) {
8287 err
= got_error_from_errno("asprintf");
8291 err
= create_ref(ref
, got_worktree_get_base_commit_id(worktree
),
8301 usage_cherrypick(void)
8303 fprintf(stderr
, "usage: %s cherrypick [-lX] [commit-id]\n",
8308 static const struct got_error
*
8309 cmd_cherrypick(int argc
, char *argv
[])
8311 const struct got_error
*error
= NULL
;
8312 struct got_worktree
*worktree
= NULL
;
8313 struct got_repository
*repo
= NULL
;
8314 char *cwd
= NULL
, *commit_id_str
= NULL
;
8315 struct got_object_id
*commit_id
= NULL
;
8316 struct got_commit_object
*commit
= NULL
;
8317 struct got_object_qid
*pid
;
8318 int ch
, list_refs
= 0, remove_refs
= 0;
8319 struct got_update_progress_arg upa
;
8320 int *pack_fds
= NULL
;
8323 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
8324 "unveil", NULL
) == -1)
8328 while ((ch
= getopt(argc
, argv
, "lX")) != -1) {
8345 if (list_refs
|| remove_refs
) {
8346 if (argc
!= 0 && argc
!= 1)
8348 } else if (argc
!= 1)
8350 if (list_refs
&& remove_refs
)
8351 option_conflict('l', 'X');
8353 cwd
= getcwd(NULL
, 0);
8355 error
= got_error_from_errno("getcwd");
8359 error
= got_repo_pack_fds_open(&pack_fds
);
8363 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_CVG_DIR
);
8365 if (list_refs
|| remove_refs
) {
8366 if (error
->code
!= GOT_ERR_NOT_WORKTREE
)
8369 if (error
->code
== GOT_ERR_NOT_WORKTREE
)
8370 error
= wrap_not_worktree_error(error
,
8376 error
= got_repo_open(&repo
,
8377 worktree
? got_worktree_get_repo_path(worktree
) : cwd
,
8382 error
= apply_unveil(got_repo_get_path(repo
), 0,
8383 worktree
? got_worktree_get_root_path(worktree
) : NULL
);
8387 if (list_refs
|| remove_refs
) {
8388 error
= process_logmsg_refs(GOT_WORKTREE_CHERRYPICK_REF_PREFIX
,
8389 GOT_WORKTREE_CHERRYPICK_REF_PREFIX_LEN
,
8390 argc
== 1 ? argv
[0] : NULL
, remove_refs
, worktree
, repo
);
8394 error
= got_repo_match_object_id(&commit_id
, NULL
, argv
[0],
8395 GOT_OBJ_TYPE_COMMIT
, NULL
, repo
);
8398 error
= got_object_id_str(&commit_id_str
, commit_id
);
8402 error
= got_object_open_as_commit(&commit
, repo
, commit_id
);
8405 pid
= STAILQ_FIRST(got_object_commit_get_parent_ids(commit
));
8406 memset(&upa
, 0, sizeof(upa
));
8407 error
= got_worktree_merge_files(worktree
, pid
? &pid
->id
: NULL
,
8408 commit_id
, repo
, update_progress
, &upa
, check_cancelled
,
8413 if (upa
.did_something
) {
8414 error
= logmsg_ref(commit_id
,
8415 GOT_WORKTREE_CHERRYPICK_REF_PREFIX
, worktree
, repo
);
8418 printf("Merged commit %s\n", commit_id_str
);
8420 print_merge_progress_stats(&upa
);
8424 got_object_commit_close(commit
);
8425 free(commit_id_str
);
8427 got_worktree_close(worktree
);
8429 const struct got_error
*close_err
= got_repo_close(repo
);
8434 const struct got_error
*pack_err
=
8435 got_repo_pack_fds_close(pack_fds
);
8446 fprintf(stderr
, "usage: %s backout [-lX] [commit-id]\n", getprogname());
8450 static const struct got_error
*
8451 cmd_backout(int argc
, char *argv
[])
8453 const struct got_error
*error
= NULL
;
8454 struct got_worktree
*worktree
= NULL
;
8455 struct got_repository
*repo
= NULL
;
8456 char *cwd
= NULL
, *commit_id_str
= NULL
;
8457 struct got_object_id
*commit_id
= NULL
;
8458 struct got_commit_object
*commit
= NULL
;
8459 struct got_object_qid
*pid
;
8460 int ch
, list_refs
= 0, remove_refs
= 0;
8461 struct got_update_progress_arg upa
;
8462 int *pack_fds
= NULL
;
8465 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
8466 "unveil", NULL
) == -1)
8470 while ((ch
= getopt(argc
, argv
, "lX")) != -1) {
8487 if (list_refs
|| remove_refs
) {
8488 if (argc
!= 0 && argc
!= 1)
8490 } else if (argc
!= 1)
8492 if (list_refs
&& remove_refs
)
8493 option_conflict('l', 'X');
8495 cwd
= getcwd(NULL
, 0);
8497 error
= got_error_from_errno("getcwd");
8501 error
= got_repo_pack_fds_open(&pack_fds
);
8505 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_CVG_DIR
);
8507 if (list_refs
|| remove_refs
) {
8508 if (error
->code
!= GOT_ERR_NOT_WORKTREE
)
8511 if (error
->code
== GOT_ERR_NOT_WORKTREE
)
8512 error
= wrap_not_worktree_error(error
,
8518 error
= got_repo_open(&repo
,
8519 worktree
? got_worktree_get_repo_path(worktree
) : cwd
,
8524 error
= apply_unveil(got_repo_get_path(repo
), 0,
8525 worktree
? got_worktree_get_root_path(worktree
) : NULL
);
8529 if (list_refs
|| remove_refs
) {
8530 error
= process_logmsg_refs(GOT_WORKTREE_BACKOUT_REF_PREFIX
,
8531 GOT_WORKTREE_BACKOUT_REF_PREFIX_LEN
,
8532 argc
== 1 ? argv
[0] : NULL
, remove_refs
, worktree
, repo
);
8536 error
= got_repo_match_object_id(&commit_id
, NULL
, argv
[0],
8537 GOT_OBJ_TYPE_COMMIT
, NULL
, repo
);
8540 error
= got_object_id_str(&commit_id_str
, commit_id
);
8544 error
= got_object_open_as_commit(&commit
, repo
, commit_id
);
8547 pid
= STAILQ_FIRST(got_object_commit_get_parent_ids(commit
));
8549 error
= got_error(GOT_ERR_ROOT_COMMIT
);
8553 memset(&upa
, 0, sizeof(upa
));
8554 error
= got_worktree_merge_files(worktree
, commit_id
, &pid
->id
,
8555 repo
, update_progress
, &upa
, check_cancelled
, NULL
);
8559 if (upa
.did_something
) {
8560 error
= logmsg_ref(commit_id
, GOT_WORKTREE_BACKOUT_REF_PREFIX
,
8564 printf("Backed out commit %s\n", commit_id_str
);
8566 print_merge_progress_stats(&upa
);
8570 got_object_commit_close(commit
);
8571 free(commit_id_str
);
8573 got_worktree_close(worktree
);
8575 const struct got_error
*close_err
= got_repo_close(repo
);
8580 const struct got_error
*pack_err
=
8581 got_repo_pack_fds_close(pack_fds
);
8591 fprintf(stderr
, "usage: %s cat [-P] [-c commit] [-r repository-path] "
8592 "arg ...\n", getprogname());
8596 static const struct got_error
*
8597 cat_blob(struct got_object_id
*id
, struct got_repository
*repo
, FILE *outfile
)
8599 const struct got_error
*err
;
8600 struct got_blob_object
*blob
;
8603 fd
= got_opentempfd();
8605 return got_error_from_errno("got_opentempfd");
8607 err
= got_object_open_as_blob(&blob
, repo
, id
, 8192, fd
);
8611 err
= got_object_blob_dump_to_file(NULL
, NULL
, NULL
, outfile
, blob
);
8613 if (fd
!= -1 && close(fd
) == -1 && err
== NULL
)
8614 err
= got_error_from_errno("close");
8616 got_object_blob_close(blob
);
8620 static const struct got_error
*
8621 cat_tree(struct got_object_id
*id
, struct got_repository
*repo
, FILE *outfile
)
8623 const struct got_error
*err
;
8624 struct got_tree_object
*tree
;
8627 err
= got_object_open_as_tree(&tree
, repo
, id
);
8631 nentries
= got_object_tree_get_nentries(tree
);
8632 for (i
= 0; i
< nentries
; i
++) {
8633 struct got_tree_entry
*te
;
8635 if (sigint_received
|| sigpipe_received
)
8637 te
= got_object_tree_get_entry(tree
, i
);
8638 err
= got_object_id_str(&id_str
, got_tree_entry_get_id(te
));
8641 fprintf(outfile
, "%s %.7o %s\n", id_str
,
8642 got_tree_entry_get_mode(te
),
8643 got_tree_entry_get_name(te
));
8647 got_object_tree_close(tree
);
8651 static const struct got_error
*
8652 cat_commit(struct got_object_id
*id
, struct got_repository
*repo
, FILE *outfile
)
8654 const struct got_error
*err
;
8655 struct got_commit_object
*commit
;
8656 const struct got_object_id_queue
*parent_ids
;
8657 struct got_object_qid
*pid
;
8658 char *id_str
= NULL
;
8659 const char *logmsg
= NULL
;
8662 err
= got_object_open_as_commit(&commit
, repo
, id
);
8666 err
= got_object_id_str(&id_str
, got_object_commit_get_tree_id(commit
));
8670 fprintf(outfile
, "%s%s\n", GOT_COMMIT_LABEL_TREE
, id_str
);
8671 parent_ids
= got_object_commit_get_parent_ids(commit
);
8672 fprintf(outfile
, "numparents %d\n",
8673 got_object_commit_get_nparents(commit
));
8674 STAILQ_FOREACH(pid
, parent_ids
, entry
) {
8676 err
= got_object_id_str(&pid_str
, &pid
->id
);
8679 fprintf(outfile
, "%s%s\n", GOT_COMMIT_LABEL_PARENT
, pid_str
);
8682 got_date_format_gmtoff(gmtoff
, sizeof(gmtoff
),
8683 got_object_commit_get_author_gmtoff(commit
));
8684 fprintf(outfile
, "%s%s %lld %s\n", GOT_COMMIT_LABEL_AUTHOR
,
8685 got_object_commit_get_author(commit
),
8686 (long long)got_object_commit_get_author_time(commit
),
8689 got_date_format_gmtoff(gmtoff
, sizeof(gmtoff
),
8690 got_object_commit_get_committer_gmtoff(commit
));
8691 fprintf(outfile
, "%s%s %lld %s\n", GOT_COMMIT_LABEL_COMMITTER
,
8692 got_object_commit_get_committer(commit
),
8693 (long long)got_object_commit_get_committer_time(commit
),
8696 logmsg
= got_object_commit_get_logmsg_raw(commit
);
8697 fprintf(outfile
, "messagelen %zd\n", strlen(logmsg
));
8698 fprintf(outfile
, "%s", logmsg
);
8701 got_object_commit_close(commit
);
8705 static const struct got_error
*
8706 cat_tag(struct got_object_id
*id
, struct got_repository
*repo
, FILE *outfile
)
8708 const struct got_error
*err
;
8709 struct got_tag_object
*tag
;
8710 char *id_str
= NULL
;
8711 const char *tagmsg
= NULL
;
8714 err
= got_object_open_as_tag(&tag
, repo
, id
);
8718 err
= got_object_id_str(&id_str
, got_object_tag_get_object_id(tag
));
8722 fprintf(outfile
, "%s%s\n", GOT_TAG_LABEL_OBJECT
, id_str
);
8724 switch (got_object_tag_get_object_type(tag
)) {
8725 case GOT_OBJ_TYPE_BLOB
:
8726 fprintf(outfile
, "%s%s\n", GOT_TAG_LABEL_TYPE
,
8727 GOT_OBJ_LABEL_BLOB
);
8729 case GOT_OBJ_TYPE_TREE
:
8730 fprintf(outfile
, "%s%s\n", GOT_TAG_LABEL_TYPE
,
8731 GOT_OBJ_LABEL_TREE
);
8733 case GOT_OBJ_TYPE_COMMIT
:
8734 fprintf(outfile
, "%s%s\n", GOT_TAG_LABEL_TYPE
,
8735 GOT_OBJ_LABEL_COMMIT
);
8737 case GOT_OBJ_TYPE_TAG
:
8738 fprintf(outfile
, "%s%s\n", GOT_TAG_LABEL_TYPE
,
8745 fprintf(outfile
, "%s%s\n", GOT_TAG_LABEL_TAG
,
8746 got_object_tag_get_name(tag
));
8748 got_date_format_gmtoff(gmtoff
, sizeof(gmtoff
),
8749 got_object_tag_get_tagger_gmtoff(tag
));
8750 fprintf(outfile
, "%s%s %lld %s\n", GOT_TAG_LABEL_TAGGER
,
8751 got_object_tag_get_tagger(tag
),
8752 (long long)got_object_tag_get_tagger_time(tag
),
8755 tagmsg
= got_object_tag_get_message(tag
);
8756 fprintf(outfile
, "messagelen %zd\n", strlen(tagmsg
));
8757 fprintf(outfile
, "%s", tagmsg
);
8760 got_object_tag_close(tag
);
8764 static const struct got_error
*
8765 cmd_cat(int argc
, char *argv
[])
8767 const struct got_error
*error
;
8768 struct got_repository
*repo
= NULL
;
8769 struct got_worktree
*worktree
= NULL
;
8770 char *cwd
= NULL
, *repo_path
= NULL
, *label
= NULL
;
8771 const char *commit_id_str
= NULL
;
8772 struct got_object_id
*id
= NULL
, *commit_id
= NULL
;
8773 struct got_commit_object
*commit
= NULL
;
8774 int ch
, obj_type
, i
, force_path
= 0;
8775 struct got_reflist_head refs
;
8776 int *pack_fds
= NULL
;
8781 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
8786 while ((ch
= getopt(argc
, argv
, "c:Pr:")) != -1) {
8789 commit_id_str
= optarg
;
8795 repo_path
= realpath(optarg
, NULL
);
8796 if (repo_path
== NULL
)
8797 return got_error_from_errno2("realpath",
8799 got_path_strip_trailing_slashes(repo_path
);
8810 cwd
= getcwd(NULL
, 0);
8812 error
= got_error_from_errno("getcwd");
8816 error
= got_repo_pack_fds_open(&pack_fds
);
8820 if (repo_path
== NULL
) {
8821 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_CVG_DIR
);
8822 if (error
&& error
->code
!= GOT_ERR_NOT_WORKTREE
)
8826 got_worktree_get_repo_path(worktree
));
8827 if (repo_path
== NULL
) {
8828 error
= got_error_from_errno("strdup");
8832 /* Release work tree lock. */
8833 got_worktree_close(worktree
);
8838 if (repo_path
== NULL
) {
8839 repo_path
= strdup(cwd
);
8840 if (repo_path
== NULL
)
8841 return got_error_from_errno("strdup");
8844 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
8849 error
= apply_unveil(got_repo_get_path(repo
), 1, NULL
);
8853 error
= got_ref_list(&refs
, repo
, NULL
, got_ref_cmp_by_name
, NULL
);
8857 if (commit_id_str
== NULL
)
8858 commit_id_str
= GOT_REF_HEAD
;
8859 error
= got_repo_match_object_id(&commit_id
, NULL
,
8860 commit_id_str
, GOT_OBJ_TYPE_COMMIT
, &refs
, repo
);
8864 error
= got_object_open_as_commit(&commit
, repo
, commit_id
);
8868 for (i
= 0; i
< argc
; i
++) {
8870 error
= got_object_id_by_path(&id
, repo
, commit
,
8875 error
= got_repo_match_object_id(&id
, &label
, argv
[i
],
8876 GOT_OBJ_TYPE_ANY
, NULL
/* do not resolve tags */,
8879 if (error
->code
!= GOT_ERR_BAD_OBJ_ID_STR
&&
8880 error
->code
!= GOT_ERR_NOT_REF
)
8882 error
= got_object_id_by_path(&id
, repo
,
8889 error
= got_object_get_type(&obj_type
, repo
, id
);
8894 case GOT_OBJ_TYPE_BLOB
:
8895 error
= cat_blob(id
, repo
, stdout
);
8897 case GOT_OBJ_TYPE_TREE
:
8898 error
= cat_tree(id
, repo
, stdout
);
8900 case GOT_OBJ_TYPE_COMMIT
:
8901 error
= cat_commit(id
, repo
, stdout
);
8903 case GOT_OBJ_TYPE_TAG
:
8904 error
= cat_tag(id
, repo
, stdout
);
8907 error
= got_error(GOT_ERR_OBJ_TYPE
);
8922 got_object_commit_close(commit
);
8924 got_worktree_close(worktree
);
8926 const struct got_error
*close_err
= got_repo_close(repo
);
8931 const struct got_error
*pack_err
=
8932 got_repo_pack_fds_close(pack_fds
);
8937 got_ref_list_free(&refs
);
8944 fprintf(stderr
, "usage: %s info [path ...]\n",
8949 static const struct got_error
*
8950 print_path_info(void *arg
, const char *path
, mode_t mode
, time_t mtime
,
8951 struct got_object_id
*blob_id
, struct got_object_id
*staged_blob_id
,
8952 struct got_object_id
*commit_id
)
8954 const struct got_error
*err
= NULL
;
8955 char *id_str
= NULL
;
8957 struct tm mytm
, *tm
;
8958 struct got_pathlist_head
*paths
= arg
;
8959 struct got_pathlist_entry
*pe
;
8962 * Clear error indication from any of the path arguments which
8963 * would cause this file index entry to be displayed.
8965 TAILQ_FOREACH(pe
, paths
, entry
) {
8966 if (got_path_cmp(path
, pe
->path
, strlen(path
),
8967 pe
->path_len
) == 0 ||
8968 got_path_is_child(path
, pe
->path
, pe
->path_len
))
8969 pe
->data
= NULL
; /* no error */
8972 printf(GOT_COMMIT_SEP_STR
);
8974 printf("symlink: %s\n", path
);
8975 else if (S_ISREG(mode
)) {
8976 printf("file: %s\n", path
);
8977 printf("mode: %o\n", mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
));
8978 } else if (S_ISDIR(mode
))
8979 printf("directory: %s\n", path
);
8981 printf("something: %s\n", path
);
8983 tm
= localtime_r(&mtime
, &mytm
);
8986 if (strftime(datebuf
, sizeof(datebuf
), "%c %Z", tm
) == 0)
8987 return got_error(GOT_ERR_NO_SPACE
);
8988 printf("timestamp: %s\n", datebuf
);
8991 err
= got_object_id_str(&id_str
, blob_id
);
8994 printf("based on blob: %s\n", id_str
);
8998 if (staged_blob_id
) {
8999 err
= got_object_id_str(&id_str
, staged_blob_id
);
9002 printf("based on staged blob: %s\n", id_str
);
9007 err
= got_object_id_str(&id_str
, commit_id
);
9010 printf("based on commit: %s\n", id_str
);
9017 static const struct got_error
*
9018 cmd_info(int argc
, char *argv
[])
9020 const struct got_error
*error
= NULL
;
9021 struct got_worktree
*worktree
= NULL
;
9022 char *cwd
= NULL
, *id_str
= NULL
;
9023 struct got_pathlist_head paths
;
9024 char *uuidstr
= NULL
;
9025 int ch
, show_files
= 0;
9030 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
9035 while ((ch
= getopt(argc
, argv
, "")) != -1) {
9046 cwd
= getcwd(NULL
, 0);
9048 error
= got_error_from_errno("getcwd");
9052 error
= got_worktree_open(&worktree
, cwd
, GOT_WORKTREE_CVG_DIR
);
9054 if (error
->code
== GOT_ERR_NOT_WORKTREE
)
9055 error
= wrap_not_worktree_error(error
, "info", cwd
);
9060 /* Remove "wpath cpath proc exec sendfd" promises. */
9061 if (pledge("stdio rpath flock unveil", NULL
) == -1)
9064 error
= apply_unveil(NULL
, 0, got_worktree_get_root_path(worktree
));
9069 error
= get_worktree_paths_from_argv(&paths
, argc
, argv
,
9076 error
= got_object_id_str(&id_str
,
9077 got_worktree_get_base_commit_id(worktree
));
9081 error
= got_worktree_get_uuid(&uuidstr
, worktree
);
9085 printf("work tree: %s\n", got_worktree_get_root_path(worktree
));
9086 printf("work tree base commit: %s\n", id_str
);
9087 printf("work tree path prefix: %s\n",
9088 got_worktree_get_path_prefix(worktree
));
9089 printf("work tree branch reference: %s\n",
9090 got_worktree_get_head_ref_name(worktree
));
9091 printf("work tree UUID: %s\n", uuidstr
);
9092 printf("repository: %s\n", got_worktree_get_repo_path(worktree
));
9095 struct got_pathlist_entry
*pe
;
9096 TAILQ_FOREACH(pe
, &paths
, entry
) {
9097 if (pe
->path_len
== 0)
9100 * Assume this path will fail. This will be corrected
9101 * in print_path_info() in case the path does suceeed.
9103 pe
->data
= (void *)got_error(GOT_ERR_BAD_PATH
);
9105 error
= got_worktree_path_info(worktree
, &paths
,
9106 print_path_info
, &paths
, check_cancelled
, NULL
);
9109 TAILQ_FOREACH(pe
, &paths
, entry
) {
9110 if (pe
->data
!= NULL
) {
9111 const struct got_error
*perr
;
9114 error
= got_error_fmt(perr
->code
, "%s",
9122 got_worktree_close(worktree
);
9123 got_pathlist_free(&paths
, GOT_PATHLIST_FREE_PATH
);