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 <sys/queue.h>
18 #include <sys/types.h>
32 #include "got_compat.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 [-h] [-V | --version] 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 repository-path\n", getprogname());
278 static const struct got_error
*
279 cmd_init(int argc
, char *argv
[])
281 const struct got_error
*error
= NULL
;
282 char *repo_path
= NULL
;
285 while ((ch
= getopt(argc
, argv
, "")) != -1) {
297 if (pledge("stdio rpath wpath cpath unveil", NULL
) == -1)
303 repo_path
= strdup(argv
[0]);
304 if (repo_path
== NULL
)
305 return got_error_from_errno("strdup");
307 got_path_strip_trailing_slashes(repo_path
);
309 error
= got_path_mkdir(repo_path
);
311 !(error
->code
== GOT_ERR_ERRNO
&& errno
== EEXIST
))
314 error
= apply_unveil(repo_path
, 0);
318 error
= got_repo_init(repo_path
);
324 static const struct got_error
*
325 cmd_info(int argc
, char *argv
[])
327 const struct got_error
*error
= NULL
;
328 char *repo_path
= NULL
;
329 struct got_repository
*repo
= NULL
;
330 const struct got_gotconfig
*gotconfig
= NULL
;
331 int ch
, npackfiles
, npackedobj
, nobj
;
332 off_t packsize
, loose_size
;
333 char scaled
[FMT_SCALED_STRSIZE
];
334 int *pack_fds
= NULL
;
336 while ((ch
= getopt(argc
, argv
, "r:")) != -1) {
339 repo_path
= realpath(optarg
, NULL
);
340 if (repo_path
== NULL
)
341 return got_error_from_errno2("realpath",
343 got_path_strip_trailing_slashes(repo_path
);
355 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
359 if (repo_path
== NULL
) {
360 error
= get_repo_path(&repo_path
);
364 error
= got_repo_pack_fds_open(&pack_fds
);
367 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
371 /* Remove "cpath" promise. */
372 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
376 error
= apply_unveil(got_repo_get_path_git_dir(repo
), 1);
380 printf("repository: %s\n", got_repo_get_path_git_dir(repo
));
382 gotconfig
= got_repo_get_gotconfig(repo
);
384 const struct got_remote_repo
*remotes
;
386 if (got_gotconfig_get_author(gotconfig
)) {
387 printf("default author: %s\n",
388 got_gotconfig_get_author(gotconfig
));
390 got_gotconfig_get_remotes(&nremotes
, &remotes
, gotconfig
);
391 for (i
= 0; i
< nremotes
; i
++) {
392 const char *fetch_url
= remotes
[i
].fetch_url
;
393 const char *send_url
= remotes
[i
].send_url
;
394 if (strcmp(fetch_url
, send_url
) == 0) {
395 printf("remote \"%s\": %s\n", remotes
[i
].name
,
396 remotes
[i
].fetch_url
);
398 printf("remote \"%s\" (fetch): %s\n",
399 remotes
[i
].name
, remotes
[i
].fetch_url
);
400 printf("remote \"%s\" (send): %s\n",
401 remotes
[i
].name
, remotes
[i
].send_url
);
406 error
= got_repo_get_packfile_info(&npackfiles
, &npackedobj
,
410 printf("pack files: %d\n", npackfiles
);
411 if (npackfiles
> 0) {
412 if (fmt_scaled(packsize
, scaled
) == -1) {
413 error
= got_error_from_errno("fmt_scaled");
416 printf("packed objects: %d\n", npackedobj
);
417 printf("packed total size: %s\n", scaled
);
420 error
= got_repo_get_loose_object_info(&nobj
, &loose_size
, repo
);
423 printf("loose objects: %d\n", nobj
);
425 if (fmt_scaled(loose_size
, scaled
) == -1) {
426 error
= got_error_from_errno("fmt_scaled");
429 printf("loose total size: %s\n", scaled
);
433 got_repo_close(repo
);
435 const struct got_error
*pack_err
=
436 got_repo_pack_fds_close(pack_fds
);
448 fprintf(stderr
, "usage: %s pack [-a] [-r repository-path] "
449 "[-x reference] [-q] [reference ...]\n",
454 struct got_pack_progress_arg
{
455 char last_scaled_size
[FMT_SCALED_STRSIZE
];
467 int printed_something
;
471 print_load_info(int print_colored
, int print_found
, int print_trees
,
472 int ncolored
, int nfound
, int ntrees
)
475 printf("%d commit%s colored", ncolored
,
476 ncolored
== 1 ? "" : "s");
479 printf("%s%d object%s found",
480 ncolored
> 0 ? "; " : "",
481 nfound
, nfound
== 1 ? "" : "s");
484 printf("; %d tree%s scanned", ntrees
,
485 ntrees
== 1 ? "" : "s");
489 static const struct got_error
*
490 pack_progress(void *arg
, int ncolored
, int nfound
, int ntrees
,
491 off_t packfile_size
, int ncommits
, int nobj_total
, int nobj_deltify
,
494 struct got_pack_progress_arg
*a
= arg
;
495 char scaled_size
[FMT_SCALED_STRSIZE
];
496 int p_deltify
, p_written
;
497 int print_colored
= 0, print_found
= 0, print_trees
= 0;
498 int print_searching
= 0, print_total
= 0;
499 int print_deltify
= 0, print_written
= 0;
501 if (a
->verbosity
< 0)
504 if (a
->last_ncolored
!= ncolored
) {
506 a
->last_ncolored
= ncolored
;
509 if (a
->last_nfound
!= nfound
) {
512 a
->last_nfound
= nfound
;
515 if (a
->last_ntrees
!= ntrees
) {
519 a
->last_ntrees
= ntrees
;
522 if ((print_colored
|| print_found
|| print_trees
) &&
525 print_load_info(print_colored
, print_found
, print_trees
,
526 ncolored
, nfound
, ntrees
);
527 a
->printed_something
= 1;
530 } else if (!a
->loading_done
) {
532 print_load_info(1, 1, 1, ncolored
, nfound
, ntrees
);
537 if (fmt_scaled(packfile_size
, scaled_size
) == -1)
538 return got_error_from_errno("fmt_scaled");
540 if (a
->last_ncommits
!= ncommits
) {
542 a
->last_ncommits
= ncommits
;
545 if (a
->last_nobj_total
!= nobj_total
) {
548 a
->last_nobj_total
= nobj_total
;
551 if (packfile_size
> 0 && (a
->last_scaled_size
[0] == '\0' ||
552 strcmp(scaled_size
, a
->last_scaled_size
)) != 0) {
553 if (strlcpy(a
->last_scaled_size
, scaled_size
,
554 FMT_SCALED_STRSIZE
) >= FMT_SCALED_STRSIZE
)
555 return got_error(GOT_ERR_NO_SPACE
);
558 if (nobj_deltify
> 0 || nobj_written
> 0) {
559 if (nobj_deltify
> 0) {
560 p_deltify
= (nobj_deltify
* 100) / nobj_total
;
561 if (p_deltify
!= a
->last_p_deltify
) {
562 a
->last_p_deltify
= p_deltify
;
568 if (nobj_written
> 0) {
569 p_written
= (nobj_written
* 100) / nobj_total
;
570 if (p_written
!= a
->last_p_written
) {
571 a
->last_p_written
= p_written
;
580 if (print_searching
|| print_total
|| print_deltify
|| print_written
)
583 printf("packing %d reference%s", ncommits
,
584 ncommits
== 1 ? "" : "s");
586 printf("; %d object%s", nobj_total
,
587 nobj_total
== 1 ? "" : "s");
589 printf("; deltify: %d%%", p_deltify
);
591 printf("; writing pack: %*s %d%%", FMT_SCALED_STRSIZE
- 2,
592 scaled_size
, p_written
);
593 if (print_searching
|| print_total
|| print_deltify
||
595 a
->printed_something
= 1;
601 static const struct got_error
*
602 pack_index_progress(void *arg
, off_t packfile_size
, int nobj_total
,
603 int nobj_indexed
, int nobj_loose
, int nobj_resolved
)
605 struct got_pack_progress_arg
*a
= arg
;
606 char scaled_size
[FMT_SCALED_STRSIZE
];
607 int p_indexed
, p_resolved
;
608 int print_size
= 0, print_indexed
= 0, print_resolved
= 0;
610 if (a
->verbosity
< 0)
613 if (packfile_size
> 0 || nobj_indexed
> 0) {
614 if (fmt_scaled(packfile_size
, scaled_size
) == 0 &&
615 (a
->last_scaled_size
[0] == '\0' ||
616 strcmp(scaled_size
, a
->last_scaled_size
)) != 0) {
618 if (strlcpy(a
->last_scaled_size
, scaled_size
,
619 FMT_SCALED_STRSIZE
) >= FMT_SCALED_STRSIZE
)
620 return got_error(GOT_ERR_NO_SPACE
);
622 if (nobj_indexed
> 0) {
623 p_indexed
= (nobj_indexed
* 100) / nobj_total
;
624 if (p_indexed
!= a
->last_p_indexed
) {
625 a
->last_p_indexed
= p_indexed
;
630 if (nobj_resolved
> 0) {
631 p_resolved
= (nobj_resolved
* 100) /
632 (nobj_total
- nobj_loose
);
633 if (p_resolved
!= a
->last_p_resolved
) {
634 a
->last_p_resolved
= p_resolved
;
642 if (print_size
|| print_indexed
|| print_resolved
)
645 printf("%*s packed", FMT_SCALED_STRSIZE
- 2, scaled_size
);
647 printf("; indexing %d%%", p_indexed
);
649 printf("; resolving deltas %d%%", p_resolved
);
650 if (print_size
|| print_indexed
|| print_resolved
)
656 static const struct got_error
*
657 add_ref(struct got_reflist_entry
**new, struct got_reflist_head
*refs
,
658 const char *refname
, struct got_repository
*repo
)
660 const struct got_error
*err
;
661 struct got_reference
*ref
;
665 err
= got_ref_open(&ref
, repo
, refname
, 0);
667 if (err
->code
!= GOT_ERR_NOT_REF
)
670 /* Treat argument as a reference prefix. */
671 err
= got_ref_list(refs
, repo
, refname
,
672 got_ref_cmp_by_name
, NULL
);
674 err
= got_reflist_insert(new, refs
, ref
,
675 got_ref_cmp_by_name
, NULL
);
676 if (err
|| *new == NULL
/* duplicate */)
683 static const struct got_error
*
684 cmd_pack(int argc
, char *argv
[])
686 const struct got_error
*error
= NULL
;
687 char *repo_path
= NULL
;
688 struct got_repository
*repo
= NULL
;
689 int ch
, i
, loose_obj_only
= 1, verbosity
= 0;
690 struct got_object_id
*pack_hash
= NULL
;
692 struct got_pack_progress_arg ppa
;
693 FILE *packfile
= NULL
;
694 struct got_pathlist_head exclude_args
;
695 struct got_pathlist_entry
*pe
;
696 struct got_reflist_head exclude_refs
;
697 struct got_reflist_head include_refs
;
698 struct got_reflist_entry
*re
, *new;
699 int *pack_fds
= NULL
;
701 TAILQ_INIT(&exclude_args
);
702 TAILQ_INIT(&exclude_refs
);
703 TAILQ_INIT(&include_refs
);
705 while ((ch
= getopt(argc
, argv
, "ar:x:q")) != -1) {
711 repo_path
= realpath(optarg
, NULL
);
712 if (repo_path
== NULL
)
713 return got_error_from_errno2("realpath",
715 got_path_strip_trailing_slashes(repo_path
);
718 got_path_strip_trailing_slashes(optarg
);
719 error
= got_pathlist_append(&exclude_args
,
737 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd unveil",
741 if (repo_path
== NULL
) {
742 error
= get_repo_path(&repo_path
);
746 error
= got_repo_pack_fds_open(&pack_fds
);
749 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
753 error
= apply_unveil(got_repo_get_path_git_dir(repo
), 0);
757 TAILQ_FOREACH(pe
, &exclude_args
, entry
) {
758 const char *refname
= pe
->path
;
759 error
= add_ref(&new, &exclude_refs
, refname
, repo
);
766 error
= got_ref_list(&include_refs
, repo
, "",
767 got_ref_cmp_by_name
, NULL
);
771 for (i
= 0; i
< argc
; i
++) {
773 got_path_strip_trailing_slashes(argv
[i
]);
775 error
= add_ref(&new, &include_refs
, refname
, repo
);
781 /* Ignore references in the refs/got/ namespace. */
782 TAILQ_FOREACH_SAFE(re
, &include_refs
, entry
, new) {
783 const char *refname
= got_ref_get_name(re
->ref
);
784 if (strncmp("refs/got/", refname
, 9) != 0)
786 TAILQ_REMOVE(&include_refs
, re
, entry
);
787 got_ref_close(re
->ref
);
791 memset(&ppa
, 0, sizeof(ppa
));
792 ppa
.last_scaled_size
[0] = '\0';
793 ppa
.last_p_indexed
= -1;
794 ppa
.last_p_resolved
= -1;
795 ppa
.verbosity
= verbosity
;
797 error
= got_repo_pack_objects(&packfile
, &pack_hash
,
798 &include_refs
, &exclude_refs
, repo
, loose_obj_only
,
799 pack_progress
, &ppa
, check_cancelled
, NULL
);
801 if (ppa
.printed_something
)
806 error
= got_object_id_str(&id_str
, pack_hash
);
810 printf("\nWrote %s.pack\n", id_str
);
812 error
= got_repo_index_pack(packfile
, pack_hash
, repo
,
813 pack_index_progress
, &ppa
, check_cancelled
, NULL
);
817 printf("\nIndexed %s.pack\n", id_str
);
820 got_repo_close(repo
);
822 const struct got_error
*pack_err
=
823 got_repo_pack_fds_close(pack_fds
);
827 got_pathlist_free(&exclude_args
);
828 got_ref_list_free(&exclude_refs
);
829 got_ref_list_free(&include_refs
);
837 usage_indexpack(void)
839 fprintf(stderr
, "usage: %s indexpack packfile-path\n",
844 static const struct got_error
*
845 cmd_indexpack(int argc
, char *argv
[])
847 const struct got_error
*error
= NULL
;
848 struct got_repository
*repo
= NULL
;
850 struct got_object_id
*pack_hash
= NULL
;
851 char *packfile_path
= NULL
;
853 struct got_pack_progress_arg ppa
;
854 FILE *packfile
= NULL
;
855 int *pack_fds
= NULL
;
857 while ((ch
= getopt(argc
, argv
, "")) != -1) {
871 packfile_path
= realpath(argv
[0], NULL
);
872 if (packfile_path
== NULL
)
873 return got_error_from_errno2("realpath", argv
[0]);
876 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd unveil",
881 error
= got_repo_pack_fds_open(&pack_fds
);
884 error
= got_repo_open(&repo
, packfile_path
, NULL
, pack_fds
);
888 error
= apply_unveil(got_repo_get_path_git_dir(repo
), 0);
892 memset(&ppa
, 0, sizeof(ppa
));
893 ppa
.last_scaled_size
[0] = '\0';
894 ppa
.last_p_indexed
= -1;
895 ppa
.last_p_resolved
= -1;
897 error
= got_repo_find_pack(&packfile
, &pack_hash
, repo
,
902 error
= got_object_id_str(&id_str
, pack_hash
);
906 error
= got_repo_index_pack(packfile
, pack_hash
, repo
,
907 pack_index_progress
, &ppa
, check_cancelled
, NULL
);
910 printf("\nIndexed %s.pack\n", id_str
);
913 got_repo_close(repo
);
915 const struct got_error
*pack_err
=
916 got_repo_pack_fds_close(pack_fds
);
928 fprintf(stderr
, "usage: %s listpack [-h] [-s] packfile-path\n",
933 struct gotadmin_list_pack_cb_args
{
943 static const struct got_error
*
944 list_pack_cb(void *arg
, struct got_object_id
*id
, int type
, off_t offset
,
945 off_t size
, off_t base_offset
, struct got_object_id
*base_id
)
947 const struct got_error
*err
;
948 struct gotadmin_list_pack_cb_args
*a
= arg
;
949 char *id_str
, *delta_str
= NULL
, *base_id_str
= NULL
;
950 const char *type_str
;
952 err
= got_object_id_str(&id_str
, id
);
957 case GOT_OBJ_TYPE_BLOB
:
958 type_str
= GOT_OBJ_LABEL_BLOB
;
961 case GOT_OBJ_TYPE_TREE
:
962 type_str
= GOT_OBJ_LABEL_TREE
;
965 case GOT_OBJ_TYPE_COMMIT
:
966 type_str
= GOT_OBJ_LABEL_COMMIT
;
969 case GOT_OBJ_TYPE_TAG
:
970 type_str
= GOT_OBJ_LABEL_TAG
;
973 case GOT_OBJ_TYPE_OFFSET_DELTA
:
974 type_str
= "offset-delta";
975 if (asprintf(&delta_str
, " base-offset %lld",
976 (long long)base_offset
) == -1) {
977 err
= got_error_from_errno("asprintf");
982 case GOT_OBJ_TYPE_REF_DELTA
:
983 type_str
= "ref-delta";
984 err
= got_object_id_str(&base_id_str
, base_id
);
987 if (asprintf(&delta_str
, " base-id %s", base_id_str
) == -1) {
988 err
= got_error_from_errno("asprintf");
994 err
= got_error(GOT_ERR_OBJ_TYPE
);
997 if (a
->human_readable
) {
998 char scaled
[FMT_SCALED_STRSIZE
];
1000 if (fmt_scaled(size
, scaled
) == -1) {
1001 err
= got_error_from_errno("fmt_scaled");
1005 while (isspace((unsigned char)*s
))
1007 printf("%s %s at %lld size %s%s\n", id_str
, type_str
,
1008 (long long)offset
, s
, delta_str
? delta_str
: "");
1010 printf("%s %s at %lld size %lld%s\n", id_str
, type_str
,
1011 (long long)offset
, (long long)size
,
1012 delta_str
? delta_str
: "");
1021 static const struct got_error
*
1022 cmd_listpack(int argc
, char *argv
[])
1024 const struct got_error
*error
= NULL
;
1025 struct got_repository
*repo
= NULL
;
1027 struct got_object_id
*pack_hash
= NULL
;
1028 char *packfile_path
= NULL
;
1029 char *id_str
= NULL
;
1030 struct gotadmin_list_pack_cb_args lpa
;
1031 FILE *packfile
= NULL
;
1032 int show_stats
= 0, human_readable
= 0;
1033 int *pack_fds
= NULL
;
1035 while ((ch
= getopt(argc
, argv
, "hs")) != -1) {
1054 packfile_path
= realpath(argv
[0], NULL
);
1055 if (packfile_path
== NULL
)
1056 return got_error_from_errno2("realpath", argv
[0]);
1059 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
1063 error
= got_repo_pack_fds_open(&pack_fds
);
1066 error
= got_repo_open(&repo
, packfile_path
, NULL
, pack_fds
);
1070 /* Remove "cpath" promise. */
1071 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
1075 error
= apply_unveil(got_repo_get_path_git_dir(repo
), 1);
1079 error
= got_repo_find_pack(&packfile
, &pack_hash
, repo
,
1083 error
= got_object_id_str(&id_str
, pack_hash
);
1087 memset(&lpa
, 0, sizeof(lpa
));
1088 lpa
.human_readable
= human_readable
;
1089 error
= got_repo_list_pack(packfile
, pack_hash
, repo
,
1090 list_pack_cb
, &lpa
, check_cancelled
, NULL
);
1094 printf("objects: %d\n blobs: %d\n trees: %d\n commits: %d\n"
1095 " tags: %d\n offset-deltas: %d\n ref-deltas: %d\n",
1096 lpa
.nblobs
+ lpa
.ntrees
+ lpa
.ncommits
+ lpa
.ntags
+
1097 lpa
.noffdeltas
+ lpa
.nrefdeltas
,
1098 lpa
.nblobs
, lpa
.ntrees
, lpa
.ncommits
, lpa
.ntags
,
1099 lpa
.noffdeltas
, lpa
.nrefdeltas
);
1103 got_repo_close(repo
);
1105 const struct got_error
*pack_err
=
1106 got_repo_pack_fds_close(pack_fds
);
1112 free(packfile_path
);
1119 fprintf(stderr
, "usage: %s cleanup [-a] [-p] [-n] [-r repository-path] "
1120 "[-q]\n", getprogname());
1124 struct got_cleanup_progress_arg
{
1129 int printed_something
;
1133 static const struct got_error
*
1134 cleanup_progress(void *arg
, int nloose
, int ncommits
, int npurged
)
1136 struct got_cleanup_progress_arg
*a
= arg
;
1137 int print_loose
= 0, print_commits
= 0, print_purged
= 0;
1139 if (a
->last_nloose
!= nloose
) {
1141 a
->last_nloose
= nloose
;
1143 if (a
->last_ncommits
!= ncommits
) {
1146 a
->last_ncommits
= ncommits
;
1148 if (a
->last_npurged
!= npurged
) {
1152 a
->last_npurged
= npurged
;
1155 if (a
->verbosity
< 0)
1158 if (print_loose
|| print_commits
|| print_purged
)
1161 printf("%d loose object%s", nloose
, nloose
== 1 ? "" : "s");
1163 printf("; %d commit%s scanned", ncommits
,
1164 ncommits
== 1 ? "" : "s");
1167 printf("; %d object%s could be purged", npurged
,
1168 npurged
== 1 ? "" : "s");
1170 printf("; %d object%s purged", npurged
,
1171 npurged
== 1 ? "" : "s");
1174 if (print_loose
|| print_commits
|| print_purged
) {
1175 a
->printed_something
= 1;
1181 struct got_lonely_packidx_progress_arg
{
1183 int printed_something
;
1187 static const struct got_error
*
1188 lonely_packidx_progress(void *arg
, const char *path
)
1190 struct got_lonely_packidx_progress_arg
*a
= arg
;
1192 if (a
->verbosity
< 0)
1196 printf("%s could be removed\n", path
);
1198 printf("%s removed\n", path
);
1200 a
->printed_something
= 1;
1204 static const struct got_error
*
1205 cmd_cleanup(int argc
, char *argv
[])
1207 const struct got_error
*error
= NULL
;
1208 char *repo_path
= NULL
;
1209 struct got_repository
*repo
= NULL
;
1210 int ch
, dry_run
= 0, npacked
= 0, verbosity
= 0;
1211 int remove_lonely_packidx
= 0, ignore_mtime
= 0;
1212 struct got_cleanup_progress_arg cpa
;
1213 struct got_lonely_packidx_progress_arg lpa
;
1214 off_t size_before
, size_after
;
1215 char scaled_before
[FMT_SCALED_STRSIZE
];
1216 char scaled_after
[FMT_SCALED_STRSIZE
];
1217 char scaled_diff
[FMT_SCALED_STRSIZE
];
1220 int *pack_fds
= NULL
;
1222 while ((ch
= getopt(argc
, argv
, "apr:nq")) != -1) {
1228 remove_lonely_packidx
= 1;
1231 repo_path
= realpath(optarg
, NULL
);
1232 if (repo_path
== NULL
)
1233 return got_error_from_errno2("realpath",
1235 got_path_strip_trailing_slashes(repo_path
);
1253 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
1257 if (repo_path
== NULL
) {
1258 error
= get_repo_path(&repo_path
);
1262 error
= got_repo_pack_fds_open(&pack_fds
);
1265 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
1269 error
= apply_unveil(got_repo_get_path_git_dir(repo
), 0);
1273 got_repo_get_gitconfig_extensions(&extensions
, &nextensions
,
1275 for (i
= 0; i
< nextensions
; i
++) {
1276 if (strcasecmp(extensions
[i
], "preciousObjects") == 0) {
1277 error
= got_error_msg(GOT_ERR_GIT_REPO_EXT
,
1278 "the preciousObjects Git extension is enabled; "
1279 "this implies that objects must not be deleted");
1284 if (remove_lonely_packidx
) {
1285 memset(&lpa
, 0, sizeof(lpa
));
1286 lpa
.dry_run
= dry_run
;
1287 lpa
.verbosity
= verbosity
;
1288 error
= got_repo_remove_lonely_packidx(repo
, dry_run
,
1289 lonely_packidx_progress
, &lpa
, check_cancelled
, NULL
);
1293 memset(&cpa
, 0, sizeof(cpa
));
1294 cpa
.last_ncommits
= -1;
1295 cpa
.last_npurged
= -1;
1296 cpa
.dry_run
= dry_run
;
1297 cpa
.verbosity
= verbosity
;
1298 error
= got_repo_purge_unreferenced_loose_objects(repo
,
1299 &size_before
, &size_after
, &npacked
, dry_run
, ignore_mtime
,
1300 cleanup_progress
, &cpa
, check_cancelled
, NULL
);
1301 if (cpa
.printed_something
)
1305 if (cpa
.printed_something
) {
1306 if (fmt_scaled(size_before
, scaled_before
) == -1) {
1307 error
= got_error_from_errno("fmt_scaled");
1310 if (fmt_scaled(size_after
, scaled_after
) == -1) {
1311 error
= got_error_from_errno("fmt_scaled");
1314 if (fmt_scaled(size_before
- size_after
, scaled_diff
) == -1) {
1315 error
= got_error_from_errno("fmt_scaled");
1318 printf("loose total size before: %s\n", scaled_before
);
1319 printf("loose total size after: %s\n", scaled_after
);
1321 printf("disk space which would be freed: %s\n",
1324 printf("disk space freed: %s\n", scaled_diff
);
1325 printf("loose objects also found in pack files: %d\n", npacked
);
1329 got_repo_close(repo
);
1331 const struct got_error
*pack_err
=
1332 got_repo_pack_fds_close(pack_fds
);