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_gotconfig.h"
43 #include "got_privsep.h"
44 #include "got_opentemp.h"
45 #include "got_worktree.h"
48 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
51 static volatile sig_atomic_t sigint_received
;
52 static volatile sig_atomic_t sigpipe_received
;
55 catch_sigint(int signo
)
61 catch_sigpipe(int signo
)
66 static const struct got_error
*
67 check_cancelled(void *arg
)
69 if (sigint_received
|| sigpipe_received
)
70 return got_error(GOT_ERR_CANCELLED
);
76 const struct got_error
*(*cmd_main
)(int, char *[]);
77 void (*cmd_usage
)(void);
78 const char *cmd_alias
;
81 __dead
static void usage(int, int);
82 __dead
static void usage_init(void);
83 __dead
static void usage_info(void);
84 __dead
static void usage_pack(void);
85 __dead
static void usage_indexpack(void);
86 __dead
static void usage_listpack(void);
87 __dead
static void usage_cleanup(void);
89 static const struct got_error
* cmd_init(int, char *[]);
90 static const struct got_error
* cmd_info(int, char *[]);
91 static const struct got_error
* cmd_pack(int, char *[]);
92 static const struct got_error
* cmd_indexpack(int, char *[]);
93 static const struct got_error
* cmd_listpack(int, char *[]);
94 static const struct got_error
* cmd_cleanup(int, char *[]);
96 static const struct gotadmin_cmd gotadmin_commands
[] = {
97 { "init", cmd_init
, usage_init
, "" },
98 { "info", cmd_info
, usage_info
, "" },
99 { "pack", cmd_pack
, usage_pack
, "" },
100 { "indexpack", cmd_indexpack
, usage_indexpack
,"ix" },
101 { "listpack", cmd_listpack
, usage_listpack
, "ls" },
102 { "cleanup", cmd_cleanup
, usage_cleanup
, "cl" },
106 list_commands(FILE *fp
)
110 fprintf(fp
, "commands:");
111 for (i
= 0; i
< nitems(gotadmin_commands
); i
++) {
112 const struct gotadmin_cmd
*cmd
= &gotadmin_commands
[i
];
113 fprintf(fp
, " %s", cmd
->cmd_name
);
119 main(int argc
, char *argv
[])
121 const struct gotadmin_cmd
*cmd
;
124 int hflag
= 0, Vflag
= 0;
125 static const struct option longopts
[] = {
126 { "version", no_argument
, NULL
, 'V' },
130 setlocale(LC_CTYPE
, "");
132 while ((ch
= getopt_long(argc
, argv
, "+hV", longopts
, NULL
)) != -1) {
152 got_version_print_str();
157 usage(hflag
, hflag
? 0 : 1);
159 signal(SIGINT
, catch_sigint
);
160 signal(SIGPIPE
, catch_sigpipe
);
162 for (i
= 0; i
< nitems(gotadmin_commands
); i
++) {
163 const struct got_error
*error
;
165 cmd
= &gotadmin_commands
[i
];
167 if (strcmp(cmd
->cmd_name
, argv
[0]) != 0 &&
168 strcmp(cmd
->cmd_alias
, argv
[0]) != 0)
174 error
= cmd
->cmd_main(argc
, argv
);
175 if (error
&& error
->code
!= GOT_ERR_CANCELLED
&&
176 error
->code
!= GOT_ERR_PRIVSEP_EXIT
&&
177 !(sigpipe_received
&&
178 error
->code
== GOT_ERR_ERRNO
&& errno
== EPIPE
) &&
180 error
->code
== GOT_ERR_ERRNO
&& errno
== EINTR
)) {
181 fprintf(stderr
, "%s: %s\n", getprogname(), error
->msg
);
188 fprintf(stderr
, "%s: unknown command '%s'\n", getprogname(), argv
[0]);
189 list_commands(stderr
);
194 usage(int hflag
, int status
)
196 FILE *fp
= (status
== 0) ? stdout
: stderr
;
198 fprintf(fp
, "usage: %s [-hV] command [arg ...]\n",
205 static const struct got_error
*
206 apply_unveil(const char *repo_path
, int repo_read_only
)
208 const struct got_error
*err
;
211 if (unveil("gmon.out", "rwc") != 0)
212 return got_error_from_errno2("unveil", "gmon.out");
214 if (repo_path
&& unveil(repo_path
, repo_read_only
? "r" : "rwc") != 0)
215 return got_error_from_errno2("unveil", repo_path
);
217 if (unveil(GOT_TMPDIR_STR
, "rwc") != 0)
218 return got_error_from_errno2("unveil", GOT_TMPDIR_STR
);
220 err
= got_privsep_unveil_exec_helpers();
224 if (unveil(NULL
, NULL
) != 0)
225 return got_error_from_errno("unveil");
233 fprintf(stderr
, "usage: %s info [-r repository-path]\n",
238 static const struct got_error
*
239 get_repo_path(char **repo_path
)
241 const struct got_error
*err
= NULL
;
242 struct got_worktree
*worktree
= NULL
;
247 cwd
= getcwd(NULL
, 0);
249 return got_error_from_errno("getcwd");
251 err
= got_worktree_open(&worktree
, cwd
);
253 if (err
->code
!= GOT_ERR_NOT_WORKTREE
)
259 *repo_path
= strdup(got_worktree_get_repo_path(worktree
));
261 *repo_path
= strdup(cwd
);
262 if (*repo_path
== NULL
)
263 err
= got_error_from_errno("strdup");
266 got_worktree_close(worktree
);
274 fprintf(stderr
, "usage: %s init [-b branch] repository-path\n",
279 static const struct got_error
*
280 cmd_init(int argc
, char *argv
[])
282 const struct got_error
*error
= NULL
;
283 const char *head_name
= NULL
;
284 char *repo_path
= NULL
;
288 if (pledge("stdio rpath wpath cpath unveil", NULL
) == -1)
292 while ((ch
= getopt(argc
, argv
, "b:")) != -1) {
309 repo_path
= strdup(argv
[0]);
310 if (repo_path
== NULL
)
311 return got_error_from_errno("strdup");
313 got_path_strip_trailing_slashes(repo_path
);
315 error
= got_path_mkdir(repo_path
);
317 !(error
->code
== GOT_ERR_ERRNO
&& errno
== EEXIST
))
320 error
= apply_unveil(repo_path
, 0);
324 error
= got_repo_init(repo_path
, head_name
);
330 static const struct got_error
*
331 cmd_info(int argc
, char *argv
[])
333 const struct got_error
*error
= NULL
;
334 char *repo_path
= NULL
;
335 struct got_repository
*repo
= NULL
;
336 const struct got_gotconfig
*gotconfig
= NULL
;
337 int ch
, npackfiles
, npackedobj
, nobj
;
338 off_t packsize
, loose_size
;
339 char scaled
[FMT_SCALED_STRSIZE
];
340 int *pack_fds
= NULL
;
343 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
348 while ((ch
= getopt(argc
, argv
, "r:")) != -1) {
351 repo_path
= realpath(optarg
, NULL
);
352 if (repo_path
== NULL
)
353 return got_error_from_errno2("realpath",
355 got_path_strip_trailing_slashes(repo_path
);
366 if (repo_path
== NULL
) {
367 error
= get_repo_path(&repo_path
);
371 error
= got_repo_pack_fds_open(&pack_fds
);
374 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
378 /* Remove "cpath" promise. */
379 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
383 error
= apply_unveil(got_repo_get_path_git_dir(repo
), 1);
387 printf("repository: %s\n", got_repo_get_path_git_dir(repo
));
389 gotconfig
= got_repo_get_gotconfig(repo
);
391 const struct got_remote_repo
*remotes
;
393 if (got_gotconfig_get_author(gotconfig
)) {
394 printf("default author: %s\n",
395 got_gotconfig_get_author(gotconfig
));
397 got_gotconfig_get_remotes(&nremotes
, &remotes
, gotconfig
);
398 for (i
= 0; i
< nremotes
; i
++) {
399 const char *fetch_url
= remotes
[i
].fetch_url
;
400 const char *send_url
= remotes
[i
].send_url
;
401 if (strcmp(fetch_url
, send_url
) == 0) {
402 printf("remote \"%s\": %s\n", remotes
[i
].name
,
403 remotes
[i
].fetch_url
);
405 printf("remote \"%s\" (fetch): %s\n",
406 remotes
[i
].name
, remotes
[i
].fetch_url
);
407 printf("remote \"%s\" (send): %s\n",
408 remotes
[i
].name
, remotes
[i
].send_url
);
413 error
= got_repo_get_packfile_info(&npackfiles
, &npackedobj
,
417 printf("pack files: %d\n", npackfiles
);
418 if (npackfiles
> 0) {
419 if (fmt_scaled(packsize
, scaled
) == -1) {
420 error
= got_error_from_errno("fmt_scaled");
423 printf("packed objects: %d\n", npackedobj
);
424 printf("packed total size: %s\n", scaled
);
427 error
= got_repo_get_loose_object_info(&nobj
, &loose_size
, repo
);
430 printf("loose objects: %d\n", nobj
);
432 if (fmt_scaled(loose_size
, scaled
) == -1) {
433 error
= got_error_from_errno("fmt_scaled");
436 printf("loose total size: %s\n", scaled
);
440 got_repo_close(repo
);
442 const struct got_error
*pack_err
=
443 got_repo_pack_fds_close(pack_fds
);
455 fprintf(stderr
, "usage: %s pack [-aDq] [-r repository-path] "
456 "[-x reference] [reference ...]\n", getprogname());
460 struct got_pack_progress_arg
{
461 char last_scaled_size
[FMT_SCALED_STRSIZE
];
473 int printed_something
;
477 print_load_info(int print_colored
, int print_found
, int print_trees
,
478 int ncolored
, int nfound
, int ntrees
)
481 printf("%d commit%s colored", ncolored
,
482 ncolored
== 1 ? "" : "s");
485 printf("%s%d object%s found",
486 ncolored
> 0 ? "; " : "",
487 nfound
, nfound
== 1 ? "" : "s");
490 printf("; %d tree%s scanned", ntrees
,
491 ntrees
== 1 ? "" : "s");
495 static const struct got_error
*
496 pack_progress(void *arg
, int ncolored
, int nfound
, int ntrees
,
497 off_t packfile_size
, int ncommits
, int nobj_total
, int nobj_deltify
,
500 struct got_pack_progress_arg
*a
= arg
;
501 char scaled_size
[FMT_SCALED_STRSIZE
];
502 int p_deltify
, p_written
;
503 int print_colored
= 0, print_found
= 0, print_trees
= 0;
504 int print_searching
= 0, print_total
= 0;
505 int print_deltify
= 0, print_written
= 0;
507 if (a
->verbosity
< 0)
510 if (a
->last_ncolored
!= ncolored
) {
512 a
->last_ncolored
= ncolored
;
515 if (a
->last_nfound
!= nfound
) {
518 a
->last_nfound
= nfound
;
521 if (a
->last_ntrees
!= ntrees
) {
525 a
->last_ntrees
= ntrees
;
528 if ((print_colored
|| print_found
|| print_trees
) &&
531 print_load_info(print_colored
, print_found
, print_trees
,
532 ncolored
, nfound
, ntrees
);
533 a
->printed_something
= 1;
536 } else if (!a
->loading_done
) {
538 print_load_info(1, 1, 1, ncolored
, nfound
, ntrees
);
543 if (fmt_scaled(packfile_size
, scaled_size
) == -1)
544 return got_error_from_errno("fmt_scaled");
546 if (a
->last_ncommits
!= ncommits
) {
548 a
->last_ncommits
= ncommits
;
551 if (a
->last_nobj_total
!= nobj_total
) {
554 a
->last_nobj_total
= nobj_total
;
557 if (packfile_size
> 0 && (a
->last_scaled_size
[0] == '\0' ||
558 strcmp(scaled_size
, a
->last_scaled_size
)) != 0) {
559 if (strlcpy(a
->last_scaled_size
, scaled_size
,
560 FMT_SCALED_STRSIZE
) >= FMT_SCALED_STRSIZE
)
561 return got_error(GOT_ERR_NO_SPACE
);
564 if (nobj_deltify
> 0 || nobj_written
> 0) {
565 if (nobj_deltify
> 0) {
566 p_deltify
= (nobj_deltify
* 100) / nobj_total
;
567 if (p_deltify
!= a
->last_p_deltify
) {
568 a
->last_p_deltify
= p_deltify
;
574 if (nobj_written
> 0) {
575 p_written
= (nobj_written
* 100) / nobj_total
;
576 if (p_written
!= a
->last_p_written
) {
577 a
->last_p_written
= p_written
;
586 if (print_searching
|| print_total
|| print_deltify
|| print_written
)
589 printf("packing %d reference%s", ncommits
,
590 ncommits
== 1 ? "" : "s");
592 printf("; %d object%s", nobj_total
,
593 nobj_total
== 1 ? "" : "s");
595 printf("; deltify: %d%%", p_deltify
);
597 printf("; writing pack: %*s %d%%", FMT_SCALED_STRSIZE
- 2,
598 scaled_size
, p_written
);
599 if (print_searching
|| print_total
|| print_deltify
||
601 a
->printed_something
= 1;
607 static const struct got_error
*
608 pack_index_progress(void *arg
, off_t packfile_size
, int nobj_total
,
609 int nobj_indexed
, int nobj_loose
, int nobj_resolved
)
611 struct got_pack_progress_arg
*a
= arg
;
612 char scaled_size
[FMT_SCALED_STRSIZE
];
613 int p_indexed
, p_resolved
;
614 int print_size
= 0, print_indexed
= 0, print_resolved
= 0;
616 if (a
->verbosity
< 0)
619 if (packfile_size
> 0 || nobj_indexed
> 0) {
620 if (fmt_scaled(packfile_size
, scaled_size
) == 0 &&
621 (a
->last_scaled_size
[0] == '\0' ||
622 strcmp(scaled_size
, a
->last_scaled_size
)) != 0) {
624 if (strlcpy(a
->last_scaled_size
, scaled_size
,
625 FMT_SCALED_STRSIZE
) >= FMT_SCALED_STRSIZE
)
626 return got_error(GOT_ERR_NO_SPACE
);
628 if (nobj_indexed
> 0) {
629 p_indexed
= (nobj_indexed
* 100) / nobj_total
;
630 if (p_indexed
!= a
->last_p_indexed
) {
631 a
->last_p_indexed
= p_indexed
;
636 if (nobj_resolved
> 0) {
637 p_resolved
= (nobj_resolved
* 100) /
638 (nobj_total
- nobj_loose
);
639 if (p_resolved
!= a
->last_p_resolved
) {
640 a
->last_p_resolved
= p_resolved
;
648 if (print_size
|| print_indexed
|| print_resolved
)
651 printf("%*s packed", FMT_SCALED_STRSIZE
- 2, scaled_size
);
653 printf("; indexing %d%%", p_indexed
);
655 printf("; resolving deltas %d%%", p_resolved
);
656 if (print_size
|| print_indexed
|| print_resolved
)
662 static const struct got_error
*
663 add_ref(struct got_reflist_entry
**new, struct got_reflist_head
*refs
,
664 const char *refname
, struct got_repository
*repo
)
666 const struct got_error
*err
;
667 struct got_reference
*ref
;
671 err
= got_ref_open(&ref
, repo
, refname
, 0);
673 if (err
->code
!= GOT_ERR_NOT_REF
)
676 /* Treat argument as a reference prefix. */
677 err
= got_ref_list(refs
, repo
, refname
,
678 got_ref_cmp_by_name
, NULL
);
680 err
= got_reflist_insert(new, refs
, ref
,
681 got_ref_cmp_by_name
, NULL
);
682 if (err
|| *new == NULL
/* duplicate */)
689 static const struct got_error
*
690 cmd_pack(int argc
, char *argv
[])
692 const struct got_error
*error
= NULL
;
693 char *repo_path
= NULL
;
694 struct got_repository
*repo
= NULL
;
695 int ch
, i
, loose_obj_only
= 1, force_refdelta
= 0, verbosity
= 0;
696 struct got_object_id
*pack_hash
= NULL
;
698 struct got_pack_progress_arg ppa
;
699 FILE *packfile
= NULL
;
700 struct got_pathlist_head exclude_args
;
701 struct got_pathlist_entry
*pe
;
702 struct got_reflist_head exclude_refs
;
703 struct got_reflist_head include_refs
;
704 struct got_reflist_entry
*re
, *new;
705 int *pack_fds
= NULL
;
707 TAILQ_INIT(&exclude_args
);
708 TAILQ_INIT(&exclude_refs
);
709 TAILQ_INIT(&include_refs
);
712 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd unveil",
717 while ((ch
= getopt(argc
, argv
, "aDqr:x:")) != -1) {
729 repo_path
= realpath(optarg
, NULL
);
730 if (repo_path
== NULL
)
731 return got_error_from_errno2("realpath",
733 got_path_strip_trailing_slashes(repo_path
);
736 got_path_strip_trailing_slashes(optarg
);
737 error
= got_pathlist_append(&exclude_args
,
751 if (repo_path
== NULL
) {
752 error
= get_repo_path(&repo_path
);
756 error
= got_repo_pack_fds_open(&pack_fds
);
759 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
763 error
= apply_unveil(got_repo_get_path_git_dir(repo
), 0);
767 TAILQ_FOREACH(pe
, &exclude_args
, entry
) {
768 const char *refname
= pe
->path
;
769 error
= add_ref(&new, &exclude_refs
, refname
, repo
);
775 error
= got_ref_list(&include_refs
, repo
, "",
776 got_ref_cmp_by_name
, NULL
);
780 for (i
= 0; i
< argc
; i
++) {
782 got_path_strip_trailing_slashes(argv
[i
]);
784 error
= add_ref(&new, &include_refs
, refname
, repo
);
790 /* Ignore references in the refs/got/ namespace. */
791 TAILQ_FOREACH_SAFE(re
, &include_refs
, entry
, new) {
792 const char *refname
= got_ref_get_name(re
->ref
);
793 if (strncmp("refs/got/", refname
, 9) != 0)
795 TAILQ_REMOVE(&include_refs
, re
, entry
);
796 got_ref_close(re
->ref
);
800 memset(&ppa
, 0, sizeof(ppa
));
801 ppa
.last_scaled_size
[0] = '\0';
802 ppa
.last_p_indexed
= -1;
803 ppa
.last_p_resolved
= -1;
804 ppa
.verbosity
= verbosity
;
806 error
= got_repo_pack_objects(&packfile
, &pack_hash
,
807 &include_refs
, &exclude_refs
, repo
, loose_obj_only
,
808 force_refdelta
, pack_progress
, &ppa
, check_cancelled
, NULL
);
810 if (ppa
.printed_something
)
815 error
= got_object_id_str(&id_str
, pack_hash
);
819 printf("\nWrote %s.pack\n", id_str
);
821 error
= got_repo_index_pack(packfile
, pack_hash
, repo
,
822 pack_index_progress
, &ppa
, check_cancelled
, NULL
);
826 printf("\nIndexed %s.pack\n", id_str
);
829 got_repo_close(repo
);
831 const struct got_error
*pack_err
=
832 got_repo_pack_fds_close(pack_fds
);
836 got_pathlist_free(&exclude_args
, GOT_PATHLIST_FREE_NONE
);
837 got_ref_list_free(&exclude_refs
);
838 got_ref_list_free(&include_refs
);
846 usage_indexpack(void)
848 fprintf(stderr
, "usage: %s indexpack packfile-path\n",
853 static const struct got_error
*
854 cmd_indexpack(int argc
, char *argv
[])
856 const struct got_error
*error
= NULL
;
857 struct got_repository
*repo
= NULL
;
859 struct got_object_id
*pack_hash
= NULL
;
860 char *packfile_path
= NULL
;
862 struct got_pack_progress_arg ppa
;
863 FILE *packfile
= NULL
;
864 int *pack_fds
= NULL
;
866 while ((ch
= getopt(argc
, argv
, "")) != -1) {
880 packfile_path
= realpath(argv
[0], NULL
);
881 if (packfile_path
== NULL
)
882 return got_error_from_errno2("realpath", argv
[0]);
885 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd unveil",
890 error
= got_repo_pack_fds_open(&pack_fds
);
893 error
= got_repo_open(&repo
, packfile_path
, NULL
, pack_fds
);
897 error
= apply_unveil(got_repo_get_path_git_dir(repo
), 0);
901 memset(&ppa
, 0, sizeof(ppa
));
902 ppa
.last_scaled_size
[0] = '\0';
903 ppa
.last_p_indexed
= -1;
904 ppa
.last_p_resolved
= -1;
906 error
= got_repo_find_pack(&packfile
, &pack_hash
, repo
,
911 error
= got_object_id_str(&id_str
, pack_hash
);
915 error
= got_repo_index_pack(packfile
, pack_hash
, repo
,
916 pack_index_progress
, &ppa
, check_cancelled
, NULL
);
919 printf("\nIndexed %s.pack\n", id_str
);
922 got_repo_close(repo
);
924 const struct got_error
*pack_err
=
925 got_repo_pack_fds_close(pack_fds
);
937 fprintf(stderr
, "usage: %s listpack [-hs] packfile-path\n",
942 struct gotadmin_list_pack_cb_args
{
952 static const struct got_error
*
953 list_pack_cb(void *arg
, struct got_object_id
*id
, int type
, off_t offset
,
954 off_t size
, off_t base_offset
, struct got_object_id
*base_id
)
956 const struct got_error
*err
;
957 struct gotadmin_list_pack_cb_args
*a
= arg
;
958 char *id_str
, *delta_str
= NULL
, *base_id_str
= NULL
;
959 const char *type_str
;
961 err
= got_object_id_str(&id_str
, id
);
966 case GOT_OBJ_TYPE_BLOB
:
967 type_str
= GOT_OBJ_LABEL_BLOB
;
970 case GOT_OBJ_TYPE_TREE
:
971 type_str
= GOT_OBJ_LABEL_TREE
;
974 case GOT_OBJ_TYPE_COMMIT
:
975 type_str
= GOT_OBJ_LABEL_COMMIT
;
978 case GOT_OBJ_TYPE_TAG
:
979 type_str
= GOT_OBJ_LABEL_TAG
;
982 case GOT_OBJ_TYPE_OFFSET_DELTA
:
983 type_str
= "offset-delta";
984 if (asprintf(&delta_str
, " base-offset %lld",
985 (long long)base_offset
) == -1) {
986 err
= got_error_from_errno("asprintf");
991 case GOT_OBJ_TYPE_REF_DELTA
:
992 type_str
= "ref-delta";
993 err
= got_object_id_str(&base_id_str
, base_id
);
996 if (asprintf(&delta_str
, " base-id %s", base_id_str
) == -1) {
997 err
= got_error_from_errno("asprintf");
1003 err
= got_error(GOT_ERR_OBJ_TYPE
);
1006 if (a
->human_readable
) {
1007 char scaled
[FMT_SCALED_STRSIZE
];
1009 if (fmt_scaled(size
, scaled
) == -1) {
1010 err
= got_error_from_errno("fmt_scaled");
1014 while (isspace((unsigned char)*s
))
1016 printf("%s %s at %lld size %s%s\n", id_str
, type_str
,
1017 (long long)offset
, s
, delta_str
? delta_str
: "");
1019 printf("%s %s at %lld size %lld%s\n", id_str
, type_str
,
1020 (long long)offset
, (long long)size
,
1021 delta_str
? delta_str
: "");
1030 static const struct got_error
*
1031 cmd_listpack(int argc
, char *argv
[])
1033 const struct got_error
*error
= NULL
;
1034 struct got_repository
*repo
= NULL
;
1036 struct got_object_id
*pack_hash
= NULL
;
1037 char *packfile_path
= NULL
;
1038 char *id_str
= NULL
;
1039 struct gotadmin_list_pack_cb_args lpa
;
1040 FILE *packfile
= NULL
;
1041 int show_stats
= 0, human_readable
= 0;
1042 int *pack_fds
= NULL
;
1045 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
1050 while ((ch
= getopt(argc
, argv
, "hs")) != -1) {
1069 packfile_path
= realpath(argv
[0], NULL
);
1070 if (packfile_path
== NULL
)
1071 return got_error_from_errno2("realpath", argv
[0]);
1073 error
= got_repo_pack_fds_open(&pack_fds
);
1076 error
= got_repo_open(&repo
, packfile_path
, NULL
, pack_fds
);
1080 /* Remove "cpath" promise. */
1081 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
1085 error
= apply_unveil(got_repo_get_path_git_dir(repo
), 1);
1089 error
= got_repo_find_pack(&packfile
, &pack_hash
, repo
,
1093 error
= got_object_id_str(&id_str
, pack_hash
);
1097 memset(&lpa
, 0, sizeof(lpa
));
1098 lpa
.human_readable
= human_readable
;
1099 error
= got_repo_list_pack(packfile
, pack_hash
, repo
,
1100 list_pack_cb
, &lpa
, check_cancelled
, NULL
);
1104 printf("objects: %d\n blobs: %d\n trees: %d\n commits: %d\n"
1105 " tags: %d\n offset-deltas: %d\n ref-deltas: %d\n",
1106 lpa
.nblobs
+ lpa
.ntrees
+ lpa
.ncommits
+ lpa
.ntags
+
1107 lpa
.noffdeltas
+ lpa
.nrefdeltas
,
1108 lpa
.nblobs
, lpa
.ntrees
, lpa
.ncommits
, lpa
.ntags
,
1109 lpa
.noffdeltas
, lpa
.nrefdeltas
);
1113 got_repo_close(repo
);
1115 const struct got_error
*pack_err
=
1116 got_repo_pack_fds_close(pack_fds
);
1122 free(packfile_path
);
1129 fprintf(stderr
, "usage: %s cleanup [-anpq] [-r repository-path]\n",
1134 struct got_cleanup_progress_arg
{
1139 int printed_something
;
1143 static const struct got_error
*
1144 cleanup_progress(void *arg
, int nloose
, int ncommits
, int npurged
)
1146 struct got_cleanup_progress_arg
*a
= arg
;
1147 int print_loose
= 0, print_commits
= 0, print_purged
= 0;
1149 if (a
->last_nloose
!= nloose
) {
1151 a
->last_nloose
= nloose
;
1153 if (a
->last_ncommits
!= ncommits
) {
1156 a
->last_ncommits
= ncommits
;
1158 if (a
->last_npurged
!= npurged
) {
1162 a
->last_npurged
= npurged
;
1165 if (a
->verbosity
< 0)
1168 if (print_loose
|| print_commits
|| print_purged
)
1171 printf("%d loose object%s", nloose
, nloose
== 1 ? "" : "s");
1173 printf("; %d commit%s scanned", ncommits
,
1174 ncommits
== 1 ? "" : "s");
1177 printf("; %d object%s could be purged", npurged
,
1178 npurged
== 1 ? "" : "s");
1180 printf("; %d object%s purged", npurged
,
1181 npurged
== 1 ? "" : "s");
1184 if (print_loose
|| print_commits
|| print_purged
) {
1185 a
->printed_something
= 1;
1191 struct got_lonely_packidx_progress_arg
{
1193 int printed_something
;
1197 static const struct got_error
*
1198 lonely_packidx_progress(void *arg
, const char *path
)
1200 struct got_lonely_packidx_progress_arg
*a
= arg
;
1202 if (a
->verbosity
< 0)
1206 printf("%s could be removed\n", path
);
1208 printf("%s removed\n", path
);
1210 a
->printed_something
= 1;
1214 static const struct got_error
*
1215 cmd_cleanup(int argc
, char *argv
[])
1217 const struct got_error
*error
= NULL
;
1218 char *repo_path
= NULL
;
1219 struct got_repository
*repo
= NULL
;
1220 int ch
, dry_run
= 0, npacked
= 0, verbosity
= 0;
1221 int remove_lonely_packidx
= 0, ignore_mtime
= 0;
1222 struct got_cleanup_progress_arg cpa
;
1223 struct got_lonely_packidx_progress_arg lpa
;
1224 off_t size_before
, size_after
;
1225 char scaled_before
[FMT_SCALED_STRSIZE
];
1226 char scaled_after
[FMT_SCALED_STRSIZE
];
1227 char scaled_diff
[FMT_SCALED_STRSIZE
];
1228 int *pack_fds
= NULL
;
1231 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
1236 while ((ch
= getopt(argc
, argv
, "anpqr:")) != -1) {
1245 remove_lonely_packidx
= 1;
1251 repo_path
= realpath(optarg
, NULL
);
1252 if (repo_path
== NULL
)
1253 return got_error_from_errno2("realpath",
1255 got_path_strip_trailing_slashes(repo_path
);
1266 if (repo_path
== NULL
) {
1267 error
= get_repo_path(&repo_path
);
1271 error
= got_repo_pack_fds_open(&pack_fds
);
1274 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
1278 error
= apply_unveil(got_repo_get_path_git_dir(repo
), 0);
1282 if (got_repo_has_extension(repo
, "preciousObjects")) {
1283 error
= got_error_msg(GOT_ERR_GIT_REPO_EXT
,
1284 "the preciousObjects Git extension is enabled; "
1285 "this implies that objects must not be deleted");
1289 if (remove_lonely_packidx
) {
1290 memset(&lpa
, 0, sizeof(lpa
));
1291 lpa
.dry_run
= dry_run
;
1292 lpa
.verbosity
= verbosity
;
1293 error
= got_repo_remove_lonely_packidx(repo
, dry_run
,
1294 lonely_packidx_progress
, &lpa
, check_cancelled
, NULL
);
1298 memset(&cpa
, 0, sizeof(cpa
));
1299 cpa
.last_ncommits
= -1;
1300 cpa
.last_npurged
= -1;
1301 cpa
.dry_run
= dry_run
;
1302 cpa
.verbosity
= verbosity
;
1303 error
= got_repo_purge_unreferenced_loose_objects(repo
,
1304 &size_before
, &size_after
, &npacked
, dry_run
, ignore_mtime
,
1305 cleanup_progress
, &cpa
, check_cancelled
, NULL
);
1306 if (cpa
.printed_something
)
1310 if (cpa
.printed_something
) {
1311 if (fmt_scaled(size_before
, scaled_before
) == -1) {
1312 error
= got_error_from_errno("fmt_scaled");
1315 if (fmt_scaled(size_after
, scaled_after
) == -1) {
1316 error
= got_error_from_errno("fmt_scaled");
1319 if (fmt_scaled(size_before
- size_after
, scaled_diff
) == -1) {
1320 error
= got_error_from_errno("fmt_scaled");
1323 printf("loose total size before: %s\n", scaled_before
);
1324 printf("loose total size after: %s\n", scaled_after
);
1326 printf("disk space which would be freed: %s\n",
1329 printf("disk space freed: %s\n", scaled_diff
);
1330 printf("loose objects also found in pack files: %d\n", npacked
);
1334 got_repo_close(repo
);
1336 const struct got_error
*pack_err
=
1337 got_repo_pack_fds_close(pack_fds
);