2 * Copyright (c) 2021 Stefan Sperling <stsp@openbsd.org>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 #include "got_compat.h"
19 #include <sys/queue.h>
20 #include <sys/types.h>
34 #include "got_version.h"
35 #include "got_error.h"
36 #include "got_object.h"
37 #include "got_reference.h"
38 #include "got_cancel.h"
39 #include "got_repository.h"
40 #include "got_repository_admin.h"
41 #include "got_repository_dump.h"
42 #include "got_repository_load.h"
43 #include "got_gotconfig.h"
45 #include "got_privsep.h"
46 #include "got_opentemp.h"
47 #include "got_worktree.h"
50 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
53 static volatile sig_atomic_t sigint_received
;
54 static volatile sig_atomic_t sigpipe_received
;
57 catch_sigint(int signo
)
63 catch_sigpipe(int signo
)
68 static const struct got_error
*
69 check_cancelled(void *arg
)
71 if (sigint_received
|| sigpipe_received
)
72 return got_error(GOT_ERR_CANCELLED
);
78 const struct got_error
*(*cmd_main
)(int, char *[]);
79 void (*cmd_usage
)(void);
80 const char *cmd_alias
;
83 __dead
static void usage(int, int);
84 __dead
static void usage_init(void);
85 __dead
static void usage_info(void);
86 __dead
static void usage_pack(void);
87 __dead
static void usage_indexpack(void);
88 __dead
static void usage_listpack(void);
89 __dead
static void usage_cleanup(void);
90 __dead
static void usage_dump(void);
91 __dead
static void usage_load(void);
93 static const struct got_error
* cmd_init(int, char *[]);
94 static const struct got_error
* cmd_info(int, char *[]);
95 static const struct got_error
* cmd_pack(int, char *[]);
96 static const struct got_error
* cmd_indexpack(int, char *[]);
97 static const struct got_error
* cmd_listpack(int, char *[]);
98 static const struct got_error
* cmd_cleanup(int, char *[]);
99 static const struct got_error
* cmd_dump(int, char *[]);
100 static const struct got_error
* cmd_load(int, char *[]);
102 static const struct gotadmin_cmd gotadmin_commands
[] = {
103 { "init", cmd_init
, usage_init
, "" },
104 { "info", cmd_info
, usage_info
, "" },
105 { "pack", cmd_pack
, usage_pack
, "" },
106 { "indexpack", cmd_indexpack
, usage_indexpack
,"ix" },
107 { "listpack", cmd_listpack
, usage_listpack
, "ls" },
108 { "cleanup", cmd_cleanup
, usage_cleanup
, "cl" },
109 { "dump", cmd_dump
, usage_dump
, "" },
110 { "load", cmd_load
, usage_load
, "" },
114 list_commands(FILE *fp
)
118 fprintf(fp
, "commands:");
119 for (i
= 0; i
< nitems(gotadmin_commands
); i
++) {
120 const struct gotadmin_cmd
*cmd
= &gotadmin_commands
[i
];
121 fprintf(fp
, " %s", cmd
->cmd_name
);
127 main(int argc
, char *argv
[])
129 const struct gotadmin_cmd
*cmd
;
132 int hflag
= 0, Vflag
= 0;
133 static const struct option longopts
[] = {
134 { "version", no_argument
, NULL
, 'V' },
138 setlocale(LC_CTYPE
, "");
140 while ((ch
= getopt_long(argc
, argv
, "+hV", longopts
, NULL
)) != -1) {
160 got_version_print_str();
165 usage(hflag
, hflag
? 0 : 1);
167 signal(SIGINT
, catch_sigint
);
168 signal(SIGPIPE
, catch_sigpipe
);
170 for (i
= 0; i
< nitems(gotadmin_commands
); i
++) {
171 const struct got_error
*error
;
173 cmd
= &gotadmin_commands
[i
];
175 if (strcmp(cmd
->cmd_name
, argv
[0]) != 0 &&
176 strcmp(cmd
->cmd_alias
, argv
[0]) != 0)
182 error
= cmd
->cmd_main(argc
, argv
);
183 if (error
&& error
->code
!= GOT_ERR_CANCELLED
&&
184 error
->code
!= GOT_ERR_PRIVSEP_EXIT
&&
185 !(sigpipe_received
&&
186 error
->code
== GOT_ERR_ERRNO
&& errno
== EPIPE
) &&
188 error
->code
== GOT_ERR_ERRNO
&& errno
== EINTR
)) {
189 fprintf(stderr
, "%s: %s\n", getprogname(), error
->msg
);
196 fprintf(stderr
, "%s: unknown command '%s'\n", getprogname(), argv
[0]);
197 list_commands(stderr
);
202 usage(int hflag
, int status
)
204 FILE *fp
= (status
== 0) ? stdout
: stderr
;
206 fprintf(fp
, "usage: %s [-hV] command [arg ...]\n",
213 static const struct got_error
*
214 apply_unveil(const char *repo_path
, int repo_read_only
)
216 const struct got_error
*err
;
219 if (unveil("gmon.out", "rwc") != 0)
220 return got_error_from_errno2("unveil", "gmon.out");
222 if (repo_path
&& unveil(repo_path
, repo_read_only
? "r" : "rwc") != 0)
223 return got_error_from_errno2("unveil", repo_path
);
225 if (unveil(GOT_TMPDIR_STR
, "rwc") != 0)
226 return got_error_from_errno2("unveil", GOT_TMPDIR_STR
);
228 err
= got_privsep_unveil_exec_helpers();
232 if (unveil(NULL
, NULL
) != 0)
233 return got_error_from_errno("unveil");
241 fprintf(stderr
, "usage: %s info [-r repository-path]\n",
246 static const struct got_error
*
247 get_repo_path(char **repo_path
)
249 const struct got_error
*err
= NULL
;
250 struct got_worktree
*worktree
= NULL
;
255 cwd
= getcwd(NULL
, 0);
257 return got_error_from_errno("getcwd");
259 err
= got_worktree_open(&worktree
, cwd
, NULL
);
261 if (err
->code
!= GOT_ERR_NOT_WORKTREE
)
267 *repo_path
= strdup(got_worktree_get_repo_path(worktree
));
269 *repo_path
= strdup(cwd
);
270 if (*repo_path
== NULL
)
271 err
= got_error_from_errno("strdup");
274 got_worktree_close(worktree
);
282 fprintf(stderr
, "usage: %s init [-b branch] repository-path\n",
287 static const struct got_error
*
288 cmd_init(int argc
, char *argv
[])
290 const struct got_error
*error
= NULL
;
291 const char *head_name
= NULL
;
292 char *repo_path
= NULL
;
296 if (pledge("stdio rpath wpath cpath unveil", NULL
) == -1)
300 while ((ch
= getopt(argc
, argv
, "b:")) != -1) {
317 repo_path
= strdup(argv
[0]);
318 if (repo_path
== NULL
)
319 return got_error_from_errno("strdup");
321 got_path_strip_trailing_slashes(repo_path
);
323 error
= got_path_mkdir(repo_path
);
325 !(error
->code
== GOT_ERR_ERRNO
&& errno
== EEXIST
))
328 error
= apply_unveil(repo_path
, 0);
332 error
= got_repo_init(repo_path
, head_name
);
338 static const struct got_error
*
339 cmd_info(int argc
, char *argv
[])
341 const struct got_error
*error
= NULL
;
342 char *repo_path
= NULL
;
343 struct got_repository
*repo
= NULL
;
344 const struct got_gotconfig
*gotconfig
= NULL
;
345 int ch
, npackfiles
, npackedobj
, nobj
;
346 off_t packsize
, loose_size
;
347 char scaled
[FMT_SCALED_STRSIZE
];
348 int *pack_fds
= NULL
;
351 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
356 while ((ch
= getopt(argc
, argv
, "r:")) != -1) {
359 repo_path
= realpath(optarg
, NULL
);
360 if (repo_path
== NULL
)
361 return got_error_from_errno2("realpath",
363 got_path_strip_trailing_slashes(repo_path
);
374 if (repo_path
== NULL
) {
375 error
= get_repo_path(&repo_path
);
379 error
= got_repo_pack_fds_open(&pack_fds
);
382 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
386 /* Remove "cpath" promise. */
387 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
391 error
= apply_unveil(got_repo_get_path_git_dir(repo
), 1);
395 printf("repository: %s\n", got_repo_get_path_git_dir(repo
));
397 gotconfig
= got_repo_get_gotconfig(repo
);
399 const struct got_remote_repo
*remotes
;
401 if (got_gotconfig_get_author(gotconfig
)) {
402 printf("default author: %s\n",
403 got_gotconfig_get_author(gotconfig
));
405 got_gotconfig_get_remotes(&nremotes
, &remotes
, gotconfig
);
406 for (i
= 0; i
< nremotes
; i
++) {
407 const char *fetch_url
= remotes
[i
].fetch_url
;
408 const char *send_url
= remotes
[i
].send_url
;
409 if (strcmp(fetch_url
, send_url
) == 0) {
410 printf("remote \"%s\": %s\n", remotes
[i
].name
,
411 remotes
[i
].fetch_url
);
413 printf("remote \"%s\" (fetch): %s\n",
414 remotes
[i
].name
, remotes
[i
].fetch_url
);
415 printf("remote \"%s\" (send): %s\n",
416 remotes
[i
].name
, remotes
[i
].send_url
);
421 error
= got_repo_get_packfile_info(&npackfiles
, &npackedobj
,
425 printf("pack files: %d\n", npackfiles
);
426 if (npackfiles
> 0) {
427 if (fmt_scaled(packsize
, scaled
) == -1) {
428 error
= got_error_from_errno("fmt_scaled");
431 printf("packed objects: %d\n", npackedobj
);
432 printf("packed total size: %s\n", scaled
);
435 error
= got_repo_get_loose_object_info(&nobj
, &loose_size
, repo
);
438 printf("loose objects: %d\n", nobj
);
440 if (fmt_scaled(loose_size
, scaled
) == -1) {
441 error
= got_error_from_errno("fmt_scaled");
444 printf("loose total size: %s\n", scaled
);
448 got_repo_close(repo
);
450 const struct got_error
*pack_err
=
451 got_repo_pack_fds_close(pack_fds
);
463 fprintf(stderr
, "usage: %s pack [-aDq] [-r repository-path] "
464 "[-x reference] [reference ...]\n", getprogname());
468 struct got_pack_progress_arg
{
470 char last_scaled_size
[FMT_SCALED_STRSIZE
];
482 int printed_something
;
486 print_load_info(FILE *out
, int print_colored
, int print_found
, int print_trees
,
487 int ncolored
, int nfound
, int ntrees
)
490 fprintf(out
, "%d commit%s colored", ncolored
,
491 ncolored
== 1 ? "" : "s");
494 fprintf(out
, "%s%d object%s found",
495 ncolored
> 0 ? "; " : "",
496 nfound
, nfound
== 1 ? "" : "s");
499 fprintf(out
, "; %d tree%s scanned", ntrees
,
500 ntrees
== 1 ? "" : "s");
504 static const struct got_error
*
505 pack_progress(void *arg
, int ncolored
, int nfound
, int ntrees
,
506 off_t packfile_size
, int ncommits
, int nobj_total
, int nobj_deltify
,
509 struct got_pack_progress_arg
*a
= arg
;
510 char scaled_size
[FMT_SCALED_STRSIZE
];
511 int p_deltify
, p_written
;
512 int print_colored
= 0, print_found
= 0, print_trees
= 0;
513 int print_searching
= 0, print_total
= 0;
514 int print_deltify
= 0, print_written
= 0;
516 if (a
->verbosity
< 0)
519 if (a
->last_ncolored
!= ncolored
) {
521 a
->last_ncolored
= ncolored
;
524 if (a
->last_nfound
!= nfound
) {
527 a
->last_nfound
= nfound
;
530 if (a
->last_ntrees
!= ntrees
) {
534 a
->last_ntrees
= ntrees
;
537 if ((print_colored
|| print_found
|| print_trees
) &&
539 fprintf(a
->out
, "\r");
540 print_load_info(a
->out
, print_colored
, print_found
,
541 print_trees
, ncolored
, nfound
, ntrees
);
542 a
->printed_something
= 1;
545 } else if (!a
->loading_done
) {
546 fprintf(a
->out
, "\r");
547 print_load_info(a
->out
, 1, 1, 1, ncolored
, nfound
, ntrees
);
548 fprintf(a
->out
, "\n");
552 if (fmt_scaled(packfile_size
, scaled_size
) == -1)
553 return got_error_from_errno("fmt_scaled");
555 if (a
->last_ncommits
!= ncommits
) {
557 a
->last_ncommits
= ncommits
;
560 if (a
->last_nobj_total
!= nobj_total
) {
563 a
->last_nobj_total
= nobj_total
;
566 if (packfile_size
> 0 && (a
->last_scaled_size
[0] == '\0' ||
567 strcmp(scaled_size
, a
->last_scaled_size
)) != 0) {
568 if (strlcpy(a
->last_scaled_size
, scaled_size
,
569 FMT_SCALED_STRSIZE
) >= FMT_SCALED_STRSIZE
)
570 return got_error(GOT_ERR_NO_SPACE
);
573 if (nobj_deltify
> 0 || nobj_written
> 0) {
574 if (nobj_deltify
> 0) {
575 p_deltify
= (nobj_deltify
* 100) / nobj_total
;
576 if (p_deltify
!= a
->last_p_deltify
) {
577 a
->last_p_deltify
= p_deltify
;
583 if (nobj_written
> 0) {
584 p_written
= (nobj_written
* 100) / nobj_total
;
585 if (p_written
!= a
->last_p_written
) {
586 a
->last_p_written
= p_written
;
595 if (print_searching
|| print_total
|| print_deltify
|| print_written
)
596 fprintf(a
->out
, "\r");
598 fprintf(a
->out
, "packing %d reference%s", ncommits
,
599 ncommits
== 1 ? "" : "s");
601 fprintf(a
->out
, "; %d object%s", nobj_total
,
602 nobj_total
== 1 ? "" : "s");
604 fprintf(a
->out
, "; deltify: %d%%", p_deltify
);
606 fprintf(a
->out
, "; writing pack: %*s %d%%",
607 FMT_SCALED_STRSIZE
- 2, scaled_size
, p_written
);
608 if (print_searching
|| print_total
|| print_deltify
||
610 a
->printed_something
= 1;
616 static const struct got_error
*
617 pack_index_progress(void *arg
, off_t packfile_size
, int nobj_total
,
618 int nobj_indexed
, int nobj_loose
, int nobj_resolved
)
620 struct got_pack_progress_arg
*a
= arg
;
621 char scaled_size
[FMT_SCALED_STRSIZE
];
622 int p_indexed
, p_resolved
;
623 int print_size
= 0, print_indexed
= 0, print_resolved
= 0;
625 if (a
->verbosity
< 0)
628 if (packfile_size
> 0 || nobj_indexed
> 0) {
629 if (fmt_scaled(packfile_size
, scaled_size
) == 0 &&
630 (a
->last_scaled_size
[0] == '\0' ||
631 strcmp(scaled_size
, a
->last_scaled_size
)) != 0) {
633 if (strlcpy(a
->last_scaled_size
, scaled_size
,
634 FMT_SCALED_STRSIZE
) >= FMT_SCALED_STRSIZE
)
635 return got_error(GOT_ERR_NO_SPACE
);
637 if (nobj_indexed
> 0) {
638 p_indexed
= (nobj_indexed
* 100) / nobj_total
;
639 if (p_indexed
!= a
->last_p_indexed
) {
640 a
->last_p_indexed
= p_indexed
;
645 if (nobj_resolved
> 0) {
646 p_resolved
= (nobj_resolved
* 100) /
647 (nobj_total
- nobj_loose
);
648 if (p_resolved
!= a
->last_p_resolved
) {
649 a
->last_p_resolved
= p_resolved
;
657 if (print_size
|| print_indexed
|| print_resolved
)
660 printf("%*s packed", FMT_SCALED_STRSIZE
- 2, scaled_size
);
662 printf("; indexing %d%%", p_indexed
);
664 printf("; resolving deltas %d%%", p_resolved
);
665 if (print_size
|| print_indexed
|| print_resolved
)
671 static const struct got_error
*
672 add_ref(struct got_reflist_entry
**new, struct got_reflist_head
*refs
,
673 const char *refname
, struct got_repository
*repo
)
675 const struct got_error
*err
;
676 struct got_reference
*ref
;
680 err
= got_ref_open(&ref
, repo
, refname
, 0);
682 if (err
->code
!= GOT_ERR_NOT_REF
)
685 /* Treat argument as a reference prefix. */
686 err
= got_ref_list(refs
, repo
, refname
,
687 got_ref_cmp_by_name
, NULL
);
689 err
= got_reflist_insert(new, refs
, ref
,
690 got_ref_cmp_by_name
, NULL
);
691 if (err
|| *new == NULL
/* duplicate */)
698 static const struct got_error
*
699 cmd_pack(int argc
, char *argv
[])
701 const struct got_error
*error
= NULL
;
702 char *repo_path
= NULL
;
703 struct got_repository
*repo
= NULL
;
704 int ch
, i
, loose_obj_only
= 1, force_refdelta
= 0, verbosity
= 0;
705 struct got_object_id
*pack_hash
= NULL
;
707 struct got_pack_progress_arg ppa
;
708 FILE *packfile
= NULL
;
709 struct got_pathlist_head exclude_args
;
710 struct got_pathlist_entry
*pe
;
711 struct got_reflist_head exclude_refs
;
712 struct got_reflist_head include_refs
;
713 struct got_reflist_entry
*re
, *new;
714 int *pack_fds
= NULL
;
716 TAILQ_INIT(&exclude_args
);
717 TAILQ_INIT(&exclude_refs
);
718 TAILQ_INIT(&include_refs
);
721 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd unveil",
726 while ((ch
= getopt(argc
, argv
, "aDqr:x:")) != -1) {
738 repo_path
= realpath(optarg
, NULL
);
739 if (repo_path
== NULL
)
740 return got_error_from_errno2("realpath",
742 got_path_strip_trailing_slashes(repo_path
);
745 got_path_strip_trailing_slashes(optarg
);
746 error
= got_pathlist_append(&exclude_args
,
760 if (repo_path
== NULL
) {
761 error
= get_repo_path(&repo_path
);
765 error
= got_repo_pack_fds_open(&pack_fds
);
768 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
772 error
= apply_unveil(got_repo_get_path_git_dir(repo
), 0);
776 TAILQ_FOREACH(pe
, &exclude_args
, entry
) {
777 const char *refname
= pe
->path
;
778 error
= add_ref(&new, &exclude_refs
, refname
, repo
);
784 error
= got_ref_list(&include_refs
, repo
, "",
785 got_ref_cmp_by_name
, NULL
);
789 for (i
= 0; i
< argc
; i
++) {
791 got_path_strip_trailing_slashes(argv
[i
]);
793 error
= add_ref(&new, &include_refs
, refname
, repo
);
799 /* Ignore references in the refs/got/ namespace. */
800 TAILQ_FOREACH_SAFE(re
, &include_refs
, entry
, new) {
801 const char *refname
= got_ref_get_name(re
->ref
);
802 if (strncmp("refs/got/", refname
, 9) != 0)
804 TAILQ_REMOVE(&include_refs
, re
, entry
);
805 got_ref_close(re
->ref
);
809 memset(&ppa
, 0, sizeof(ppa
));
811 ppa
.last_scaled_size
[0] = '\0';
812 ppa
.last_p_indexed
= -1;
813 ppa
.last_p_resolved
= -1;
814 ppa
.verbosity
= verbosity
;
816 error
= got_repo_pack_objects(&packfile
, &pack_hash
,
817 &include_refs
, &exclude_refs
, repo
, loose_obj_only
,
818 force_refdelta
, pack_progress
, &ppa
, check_cancelled
, NULL
);
820 if (ppa
.printed_something
)
825 error
= got_object_id_str(&id_str
, pack_hash
);
829 printf("\nWrote %s.pack\n", id_str
);
831 error
= got_repo_index_pack(packfile
, pack_hash
, repo
,
832 pack_index_progress
, &ppa
, check_cancelled
, NULL
);
836 printf("\nIndexed %s.pack\n", id_str
);
839 got_repo_close(repo
);
841 const struct got_error
*pack_err
=
842 got_repo_pack_fds_close(pack_fds
);
846 got_pathlist_free(&exclude_args
, GOT_PATHLIST_FREE_NONE
);
847 got_ref_list_free(&exclude_refs
);
848 got_ref_list_free(&include_refs
);
856 usage_indexpack(void)
858 fprintf(stderr
, "usage: %s indexpack packfile-path\n",
863 static const struct got_error
*
864 cmd_indexpack(int argc
, char *argv
[])
866 const struct got_error
*error
= NULL
;
867 struct got_repository
*repo
= NULL
;
869 struct got_object_id
*pack_hash
= NULL
;
870 char *packfile_path
= NULL
;
872 struct got_pack_progress_arg ppa
;
873 FILE *packfile
= NULL
;
874 int *pack_fds
= NULL
;
876 while ((ch
= getopt(argc
, argv
, "")) != -1) {
890 packfile_path
= realpath(argv
[0], NULL
);
891 if (packfile_path
== NULL
)
892 return got_error_from_errno2("realpath", argv
[0]);
895 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd unveil",
900 error
= got_repo_pack_fds_open(&pack_fds
);
903 error
= got_repo_open(&repo
, packfile_path
, NULL
, pack_fds
);
907 error
= apply_unveil(got_repo_get_path_git_dir(repo
), 0);
911 memset(&ppa
, 0, sizeof(ppa
));
913 ppa
.last_scaled_size
[0] = '\0';
914 ppa
.last_p_indexed
= -1;
915 ppa
.last_p_resolved
= -1;
917 error
= got_repo_find_pack(&packfile
, &pack_hash
, repo
,
922 error
= got_object_id_str(&id_str
, pack_hash
);
926 error
= got_repo_index_pack(packfile
, pack_hash
, repo
,
927 pack_index_progress
, &ppa
, check_cancelled
, NULL
);
930 printf("\nIndexed %s.pack\n", id_str
);
933 got_repo_close(repo
);
935 const struct got_error
*pack_err
=
936 got_repo_pack_fds_close(pack_fds
);
948 fprintf(stderr
, "usage: %s listpack [-hs] packfile-path\n",
953 struct gotadmin_list_pack_cb_args
{
963 static const struct got_error
*
964 list_pack_cb(void *arg
, struct got_object_id
*id
, int type
, off_t offset
,
965 off_t size
, off_t base_offset
, struct got_object_id
*base_id
)
967 const struct got_error
*err
;
968 struct gotadmin_list_pack_cb_args
*a
= arg
;
969 char *id_str
, *delta_str
= NULL
, *base_id_str
= NULL
;
970 const char *type_str
;
972 err
= got_object_id_str(&id_str
, id
);
977 case GOT_OBJ_TYPE_BLOB
:
978 type_str
= GOT_OBJ_LABEL_BLOB
;
981 case GOT_OBJ_TYPE_TREE
:
982 type_str
= GOT_OBJ_LABEL_TREE
;
985 case GOT_OBJ_TYPE_COMMIT
:
986 type_str
= GOT_OBJ_LABEL_COMMIT
;
989 case GOT_OBJ_TYPE_TAG
:
990 type_str
= GOT_OBJ_LABEL_TAG
;
993 case GOT_OBJ_TYPE_OFFSET_DELTA
:
994 type_str
= "offset-delta";
995 if (asprintf(&delta_str
, " base-offset %lld",
996 (long long)base_offset
) == -1) {
997 err
= got_error_from_errno("asprintf");
1002 case GOT_OBJ_TYPE_REF_DELTA
:
1003 type_str
= "ref-delta";
1004 err
= got_object_id_str(&base_id_str
, base_id
);
1007 if (asprintf(&delta_str
, " base-id %s", base_id_str
) == -1) {
1008 err
= got_error_from_errno("asprintf");
1014 err
= got_error(GOT_ERR_OBJ_TYPE
);
1017 if (a
->human_readable
) {
1018 char scaled
[FMT_SCALED_STRSIZE
];
1020 if (fmt_scaled(size
, scaled
) == -1) {
1021 err
= got_error_from_errno("fmt_scaled");
1025 while (isspace((unsigned char)*s
))
1027 printf("%s %s at %lld size %s%s\n", id_str
, type_str
,
1028 (long long)offset
, s
, delta_str
? delta_str
: "");
1030 printf("%s %s at %lld size %lld%s\n", id_str
, type_str
,
1031 (long long)offset
, (long long)size
,
1032 delta_str
? delta_str
: "");
1041 static const struct got_error
*
1042 cmd_listpack(int argc
, char *argv
[])
1044 const struct got_error
*error
= NULL
;
1045 struct got_repository
*repo
= NULL
;
1047 struct got_object_id
*pack_hash
= NULL
;
1048 char *packfile_path
= NULL
;
1049 char *id_str
= NULL
;
1050 struct gotadmin_list_pack_cb_args lpa
;
1051 FILE *packfile
= NULL
;
1052 int show_stats
= 0, human_readable
= 0;
1053 int *pack_fds
= NULL
;
1056 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
1061 while ((ch
= getopt(argc
, argv
, "hs")) != -1) {
1080 packfile_path
= realpath(argv
[0], NULL
);
1081 if (packfile_path
== NULL
)
1082 return got_error_from_errno2("realpath", argv
[0]);
1084 error
= got_repo_pack_fds_open(&pack_fds
);
1087 error
= got_repo_open(&repo
, packfile_path
, NULL
, pack_fds
);
1091 /* Remove "cpath" promise. */
1092 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
1096 error
= apply_unveil(got_repo_get_path_git_dir(repo
), 1);
1100 error
= got_repo_find_pack(&packfile
, &pack_hash
, repo
,
1104 error
= got_object_id_str(&id_str
, pack_hash
);
1108 memset(&lpa
, 0, sizeof(lpa
));
1109 lpa
.human_readable
= human_readable
;
1110 error
= got_repo_list_pack(packfile
, pack_hash
, repo
,
1111 list_pack_cb
, &lpa
, check_cancelled
, NULL
);
1115 printf("objects: %d\n blobs: %d\n trees: %d\n commits: %d\n"
1116 " tags: %d\n offset-deltas: %d\n ref-deltas: %d\n",
1117 lpa
.nblobs
+ lpa
.ntrees
+ lpa
.ncommits
+ lpa
.ntags
+
1118 lpa
.noffdeltas
+ lpa
.nrefdeltas
,
1119 lpa
.nblobs
, lpa
.ntrees
, lpa
.ncommits
, lpa
.ntags
,
1120 lpa
.noffdeltas
, lpa
.nrefdeltas
);
1124 got_repo_close(repo
);
1126 const struct got_error
*pack_err
=
1127 got_repo_pack_fds_close(pack_fds
);
1133 free(packfile_path
);
1140 fprintf(stderr
, "usage: %s cleanup [-anpq] [-r repository-path]\n",
1145 struct got_cleanup_progress_arg
{
1149 int last_nredundant
;
1151 int printed_something
;
1155 static const struct got_error
*
1156 cleanup_progress(void *arg
, int ncommits
, int nloose
, int npurged
,
1159 struct got_cleanup_progress_arg
*a
= arg
;
1160 int print_loose
= 0, print_commits
= 0, print_purged
= 0;
1161 int print_redundant
= 0;
1163 if (a
->last_ncommits
!= ncommits
) {
1165 a
->last_ncommits
= ncommits
;
1167 if (a
->last_nloose
!= nloose
) {
1170 a
->last_nloose
= nloose
;
1172 if (a
->last_npurged
!= npurged
) {
1176 a
->last_npurged
= npurged
;
1178 if (a
->last_nredundant
!= nredundant
) {
1182 print_redundant
= 1;
1183 a
->last_nredundant
= nredundant
;
1186 if (a
->verbosity
< 0)
1189 if (print_loose
|| print_commits
|| print_purged
|| print_redundant
)
1192 printf("%d commit%s scanned", ncommits
,
1193 ncommits
== 1 ? "" : "s");
1195 printf("; %d loose object%s", nloose
, nloose
== 1 ? "" : "s");
1196 if (print_purged
|| print_redundant
) {
1198 printf("; could purge %d object%s", npurged
,
1199 npurged
== 1 ? "" : "s");
1201 printf("; purged %d object%s", npurged
,
1202 npurged
== 1 ? "" : "s");
1205 if (print_redundant
) {
1207 printf(", %d pack file%s", nredundant
,
1208 nredundant
== 1 ? "" : "s");
1210 printf(", %d pack file%s", nredundant
,
1211 nredundant
== 1 ? "" : "s");
1214 if (print_loose
|| print_commits
|| print_purged
|| print_redundant
) {
1215 a
->printed_something
= 1;
1221 struct got_lonely_packidx_progress_arg
{
1223 int printed_something
;
1227 static const struct got_error
*
1228 lonely_packidx_progress(void *arg
, const char *path
)
1230 struct got_lonely_packidx_progress_arg
*a
= arg
;
1232 if (a
->verbosity
< 0)
1236 printf("%s could be removed\n", path
);
1238 printf("%s removed\n", path
);
1240 a
->printed_something
= 1;
1244 static const struct got_error
*
1245 cmd_cleanup(int argc
, char *argv
[])
1247 const struct got_error
*error
= NULL
;
1248 char *repo_path
= NULL
;
1249 struct got_repository
*repo
= NULL
;
1250 int ch
, dry_run
= 0, verbosity
= 0;
1251 int ncommits
= 0, nloose
= 0, npacked
= 0;
1252 int remove_lonely_packidx
= 0, ignore_mtime
= 0;
1253 struct got_cleanup_progress_arg cpa
;
1254 struct got_lonely_packidx_progress_arg lpa
;
1255 off_t loose_before
, loose_after
;
1256 off_t pack_before
, pack_after
;
1258 char loose_before_scaled
[FMT_SCALED_STRSIZE
];
1259 char loose_after_scaled
[FMT_SCALED_STRSIZE
];
1260 char pack_before_scaled
[FMT_SCALED_STRSIZE
];
1261 char pack_after_scaled
[FMT_SCALED_STRSIZE
];
1262 char total_size_scaled
[FMT_SCALED_STRSIZE
];
1263 int *pack_fds
= NULL
;
1266 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
1271 while ((ch
= getopt(argc
, argv
, "anpqr:")) != -1) {
1280 remove_lonely_packidx
= 1;
1286 repo_path
= realpath(optarg
, NULL
);
1287 if (repo_path
== NULL
)
1288 return got_error_from_errno2("realpath",
1290 got_path_strip_trailing_slashes(repo_path
);
1301 if (repo_path
== NULL
) {
1302 error
= get_repo_path(&repo_path
);
1306 error
= got_repo_pack_fds_open(&pack_fds
);
1309 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
1313 error
= apply_unveil(got_repo_get_path_git_dir(repo
), 0);
1317 if (got_repo_has_extension(repo
, "preciousObjects")) {
1318 error
= got_error_msg(GOT_ERR_GIT_REPO_EXT
,
1319 "the preciousObjects Git extension is enabled; "
1320 "this implies that objects must not be deleted");
1324 if (remove_lonely_packidx
) {
1325 memset(&lpa
, 0, sizeof(lpa
));
1326 lpa
.dry_run
= dry_run
;
1327 lpa
.verbosity
= verbosity
;
1328 error
= got_repo_remove_lonely_packidx(repo
, dry_run
,
1329 lonely_packidx_progress
, &lpa
, check_cancelled
, NULL
);
1333 memset(&cpa
, 0, sizeof(cpa
));
1334 cpa
.last_nloose
= -1;
1335 cpa
.last_npurged
= -1;
1336 cpa
.last_nredundant
= -1;
1337 cpa
.dry_run
= dry_run
;
1338 cpa
.verbosity
= verbosity
;
1340 error
= got_repo_cleanup(repo
, &loose_before
, &loose_after
,
1341 &pack_before
, &pack_after
, &ncommits
, &nloose
, &npacked
,
1342 dry_run
, ignore_mtime
, cleanup_progress
, &cpa
,
1343 check_cancelled
, NULL
);
1344 if (cpa
.printed_something
)
1349 total_size
= (loose_before
- loose_after
) + (pack_before
- pack_after
);
1351 if (cpa
.printed_something
) {
1352 if (fmt_scaled(loose_before
, loose_before_scaled
) == -1) {
1353 error
= got_error_from_errno("fmt_scaled");
1356 if (fmt_scaled(loose_after
, loose_after_scaled
) == -1) {
1357 error
= got_error_from_errno("fmt_scaled");
1360 if (fmt_scaled(pack_before
, pack_before_scaled
) == -1) {
1361 error
= got_error_from_errno("fmt_scaled");
1364 if (fmt_scaled(pack_after
, pack_after_scaled
) == -1) {
1365 error
= got_error_from_errno("fmt_scaled");
1368 if (fmt_scaled(total_size
, total_size_scaled
) == -1) {
1369 error
= got_error_from_errno("fmt_scaled");
1372 printf("loose total size before: %s\n", loose_before_scaled
);
1373 printf("loose total size after: %s\n", loose_after_scaled
);
1374 printf("pack files total size before: %s\n",
1375 pack_before_scaled
);
1376 printf("pack files total size after: %s\n", pack_after_scaled
);
1378 printf("disk space which would be freed: %s\n",
1381 printf("disk space freed: %s\n", total_size_scaled
);
1382 printf("loose objects also found in pack files: %d\n", npacked
);
1387 got_repo_close(repo
);
1389 const struct got_error
*pack_err
=
1390 got_repo_pack_fds_close(pack_fds
);
1401 fprintf(stderr
, "usage: %s dump [-q] [-r repository-path] "
1402 "[-x reference] [reference]...\n", getprogname());
1406 static const struct got_error
*
1407 cmd_dump(int argc
, char *argv
[])
1409 const struct got_error
*error
= NULL
;
1410 struct got_pack_progress_arg ppa
;
1411 struct got_repository
*repo
= NULL
;
1412 struct got_pathlist_head exclude_args
;
1413 struct got_pathlist_entry
*pe
;
1414 struct got_reflist_head exclude_refs
;
1415 struct got_reflist_head include_refs
;
1416 struct got_reflist_entry
*re
, *new;
1417 const char *refname
;
1418 char *repo_path
= NULL
;
1419 int *pack_fds
= NULL
;
1423 TAILQ_INIT(&exclude_args
);
1424 TAILQ_INIT(&exclude_refs
);
1425 TAILQ_INIT(&include_refs
);
1428 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
1433 while ((ch
= getopt(argc
, argv
, "qr:x:")) != -1) {
1439 repo_path
= realpath(optarg
, NULL
);
1440 if (repo_path
== NULL
)
1441 return got_error_from_errno2("realpath",
1443 got_path_strip_trailing_slashes(repo_path
);
1446 error
= got_pathlist_append(&exclude_args
,
1459 if (repo_path
== NULL
) {
1460 error
= get_repo_path(&repo_path
);
1464 error
= got_repo_pack_fds_open(&pack_fds
);
1467 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
1471 error
= apply_unveil(got_repo_get_path_git_dir(repo
), 1);
1475 TAILQ_FOREACH(pe
, &exclude_args
, entry
) {
1477 error
= add_ref(&new, &exclude_refs
, refname
, repo
);
1483 error
= got_ref_list(&include_refs
, repo
, "",
1484 got_ref_cmp_by_name
, NULL
);
1488 for (i
= 0; i
< argc
; i
++) {
1489 got_path_strip_trailing_slashes(argv
[i
]);
1491 error
= add_ref(&new, &include_refs
, refname
, repo
);
1497 /* Ignore references in the refs/got/ namespace. */
1498 TAILQ_FOREACH_SAFE(re
, &include_refs
, entry
, new) {
1499 refname
= got_ref_get_name(re
->ref
);
1500 if (strncmp("refs/got/", refname
, 9) != 0)
1502 TAILQ_REMOVE(&include_refs
, re
, entry
);
1503 got_ref_close(re
->ref
);
1507 memset(&ppa
, 0, sizeof(ppa
));
1509 ppa
.verbosity
= verbosity
;
1511 error
= got_repo_dump(stdout
, &include_refs
, &exclude_refs
,
1512 repo
, pack_progress
, &ppa
, check_cancelled
, NULL
);
1513 if (ppa
.printed_something
)
1514 fprintf(stderr
, "\n");
1517 got_repo_close(repo
);
1520 const struct got_error
*pack_err
;
1522 pack_err
= got_repo_pack_fds_close(pack_fds
);
1527 got_pathlist_free(&exclude_args
, GOT_PATHLIST_FREE_NONE
);
1528 got_ref_list_free(&exclude_refs
);
1529 got_ref_list_free(&include_refs
);
1538 fprintf(stderr
, "usage: %s load [-nq] [-l bundle-file] "
1539 "[-r repository-path] [reference ...]\n",
1544 static const struct got_error
*
1545 load_progress(void *arg
, off_t packfile_size
, int nobj_total
,
1546 int nobj_indexed
, int nobj_loose
, int nobj_resolved
)
1548 return pack_index_progress(arg
, packfile_size
, nobj_total
,
1549 nobj_indexed
, nobj_loose
, nobj_resolved
);
1553 is_wanted_ref(struct got_pathlist_head
*wanted
, const char *ref
)
1555 struct got_pathlist_entry
*pe
;
1557 if (TAILQ_EMPTY(wanted
))
1560 TAILQ_FOREACH(pe
, wanted
, entry
) {
1561 if (strcmp(pe
->path
, ref
) == 0)
1568 static const struct got_error
*
1569 create_ref(const char *refname
, struct got_object_id
*id
,
1570 int verbosity
, struct got_repository
*repo
)
1572 const struct got_error
*err
= NULL
;
1573 struct got_reference
*ref
;
1576 err
= got_object_id_str(&id_str
, id
);
1580 err
= got_ref_alloc(&ref
, refname
, id
);
1584 err
= got_ref_write(ref
, repo
);
1587 if (err
== NULL
&& verbosity
>= 0)
1588 printf("Created reference %s: %s\n", refname
, id_str
);
1594 static const struct got_error
*
1595 update_ref(struct got_reference
*ref
, struct got_object_id
*new_id
,
1596 int replace_tags
, int verbosity
, struct got_repository
*repo
)
1598 const struct got_error
*err
= NULL
;
1599 char *new_id_str
= NULL
;
1600 struct got_object_id
*old_id
= NULL
;
1602 err
= got_object_id_str(&new_id_str
, new_id
);
1606 if (!replace_tags
&&
1607 strncmp(got_ref_get_name(ref
), "refs/tags/", 10) == 0) {
1608 err
= got_ref_resolve(&old_id
, repo
, ref
);
1611 if (got_object_id_cmp(old_id
, new_id
) == 0)
1613 if (verbosity
>= 0) {
1614 printf("Rejecting update of existing tag %s: %s\n",
1615 got_ref_get_name(ref
), new_id_str
);
1620 if (got_ref_is_symbolic(ref
)) {
1621 if (verbosity
>= 0) {
1622 printf("Replacing reference %s: %s\n",
1623 got_ref_get_name(ref
),
1624 got_ref_get_symref_target(ref
));
1626 err
= got_ref_change_symref_to_ref(ref
, new_id
);
1629 err
= got_ref_write(ref
, repo
);
1633 err
= got_ref_resolve(&old_id
, repo
, ref
);
1636 if (got_object_id_cmp(old_id
, new_id
) == 0)
1639 err
= got_ref_change_ref(ref
, new_id
);
1642 err
= got_ref_write(ref
, repo
);
1648 printf("Updated %s: %s\n", got_ref_get_name(ref
),
1656 static const struct got_error
*
1657 cmd_load(int argc
, char *argv
[])
1659 const struct got_error
*error
= NULL
;
1660 struct got_repository
*repo
= NULL
;
1661 struct got_pathlist_head include_args
;
1662 struct got_pathlist_head available_refs
;
1663 struct got_pathlist_entry
*pe
;
1664 struct got_pack_progress_arg ppa
;
1666 int *pack_fds
= NULL
;
1667 char *repo_path
= NULL
;
1668 int list_refs_only
= 0;
1673 TAILQ_INIT(&include_args
);
1674 TAILQ_INIT(&available_refs
);
1677 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
1678 "sendfd unveil", NULL
) == -1)
1682 while ((ch
= getopt(argc
, argv
, "l:nqr:")) != -1) {
1686 in
= fopen(optarg
, "re");
1688 return got_error_from_errno2("open", optarg
);
1697 repo_path
= realpath(optarg
, NULL
);
1698 if (repo_path
== NULL
)
1699 return got_error_from_errno2("realpath",
1701 got_path_strip_trailing_slashes(repo_path
);
1711 if (list_refs_only
&& argc
> 1)
1712 errx(1, "-l and references on the command line are exclusive");
1713 if (list_refs_only
&& noop
)
1714 errx(1, "-n and -l are mutually exclusive");
1716 for (i
= 0; i
< argc
; i
++) {
1717 char *refname
= argv
[i
];
1718 got_path_strip_trailing_slashes(refname
);
1719 if (!got_ref_name_is_valid(refname
))
1720 errx(1, "invalid reference name %s", refname
);
1721 error
= got_pathlist_append(&include_args
, refname
, NULL
);
1726 if (repo_path
== NULL
) {
1727 error
= get_repo_path(&repo_path
);
1731 error
= got_repo_pack_fds_open(&pack_fds
);
1734 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
1738 error
= apply_unveil(got_repo_get_path_git_dir(repo
), 0);
1742 memset(&ppa
, 0, sizeof(ppa
));
1744 ppa
.verbosity
= verbosity
;
1746 error
= got_repo_load(in
, &available_refs
, repo
, list_refs_only
, noop
,
1747 load_progress
, &ppa
, check_cancelled
, NULL
);
1748 if (verbosity
>= 0 && !list_refs_only
)
1753 if (list_refs_only
) {
1754 TAILQ_FOREACH(pe
, &available_refs
, entry
) {
1755 const char *refname
= pe
->path
;
1756 struct got_object_id
*id
= pe
->data
;
1759 error
= got_object_id_str(&idstr
, id
);
1763 printf("%s: %s\n", refname
, idstr
);
1772 /* Update references */
1773 TAILQ_FOREACH(pe
, &available_refs
, entry
) {
1774 const struct got_error
*unlock_err
;
1775 struct got_reference
*ref
;
1776 const char *refname
= pe
->path
;
1777 struct got_object_id
*id
= pe
->data
;
1779 if (!is_wanted_ref(&include_args
, pe
->path
))
1782 error
= got_ref_open(&ref
, repo
, refname
, 1);
1784 if (error
->code
!= GOT_ERR_NOT_REF
)
1786 error
= create_ref(refname
, id
, verbosity
, repo
);
1790 /* XXX: check advances only and add -f to force? */
1791 error
= update_ref(ref
, id
, 1, verbosity
, repo
);
1792 unlock_err
= got_ref_unlock(ref
);
1793 if (unlock_err
&& error
== NULL
)
1802 if (in
!= stdin
&& fclose(in
) == EOF
&& error
== NULL
)
1803 error
= got_error_from_errno("fclose");
1806 got_repo_close(repo
);
1809 const struct got_error
*pack_err
;
1811 pack_err
= got_repo_pack_fds_close(pack_fds
);
1816 got_pathlist_free(&include_args
, GOT_PATHLIST_FREE_NONE
);
1817 got_pathlist_free(&available_refs
, GOT_PATHLIST_FREE_ALL
);