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/types.h>
31 #include "got_compat.h"
33 #include "got_version.h"
34 #include "got_error.h"
35 #include "got_object.h"
36 #include "got_reference.h"
37 #include "got_cancel.h"
38 #include "got_repository.h"
39 #include "got_repository_admin.h"
40 #include "got_gotconfig.h"
42 #include "got_privsep.h"
43 #include "got_opentemp.h"
46 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
49 static volatile sig_atomic_t sigint_received
;
50 static volatile sig_atomic_t sigpipe_received
;
53 catch_sigint(int signo
)
59 catch_sigpipe(int signo
)
64 static const struct got_error
*
65 check_cancelled(void *arg
)
67 if (sigint_received
|| sigpipe_received
)
68 return got_error(GOT_ERR_CANCELLED
);
74 const struct got_error
*(*cmd_main
)(int, char *[]);
75 void (*cmd_usage
)(void);
76 const char *cmd_alias
;
79 __dead
static void usage(int, int);
80 __dead
static void usage_info(void);
81 __dead
static void usage_pack(void);
82 __dead
static void usage_indexpack(void);
83 __dead
static void usage_listpack(void);
84 __dead
static void usage_cleanup(void);
86 static const struct got_error
* cmd_info(int, char *[]);
87 static const struct got_error
* cmd_pack(int, char *[]);
88 static const struct got_error
* cmd_indexpack(int, char *[]);
89 static const struct got_error
* cmd_listpack(int, char *[]);
90 static const struct got_error
* cmd_cleanup(int, char *[]);
92 static struct gotadmin_cmd gotadmin_commands
[] = {
93 { "info", cmd_info
, usage_info
, "" },
94 { "pack", cmd_pack
, usage_pack
, "" },
95 { "indexpack", cmd_indexpack
, usage_indexpack
,"ix" },
96 { "listpack", cmd_listpack
, usage_listpack
, "ls" },
97 { "cleanup", cmd_cleanup
, usage_cleanup
, "cl" },
101 list_commands(FILE *fp
)
105 fprintf(fp
, "commands:");
106 for (i
= 0; i
< nitems(gotadmin_commands
); i
++) {
107 struct gotadmin_cmd
*cmd
= &gotadmin_commands
[i
];
108 fprintf(fp
, " %s", cmd
->cmd_name
);
114 main(int argc
, char *argv
[])
116 struct gotadmin_cmd
*cmd
;
119 int hflag
= 0, Vflag
= 0;
120 static struct option longopts
[] = {
121 { "version", no_argument
, NULL
, 'V' },
125 setlocale(LC_CTYPE
, "");
127 while ((ch
= getopt_long(argc
, argv
, "+hV", longopts
, NULL
)) != -1) {
147 got_version_print_str();
152 usage(hflag
, hflag
? 0 : 1);
154 signal(SIGINT
, catch_sigint
);
155 signal(SIGPIPE
, catch_sigpipe
);
157 for (i
= 0; i
< nitems(gotadmin_commands
); i
++) {
158 const struct got_error
*error
;
160 cmd
= &gotadmin_commands
[i
];
162 if (strcmp(cmd
->cmd_name
, argv
[0]) != 0 &&
163 strcmp(cmd
->cmd_alias
, argv
[0]) != 0)
167 gotadmin_commands
[i
].cmd_usage();
169 error
= gotadmin_commands
[i
].cmd_main(argc
, argv
);
170 if (error
&& error
->code
!= GOT_ERR_CANCELLED
&&
171 error
->code
!= GOT_ERR_PRIVSEP_EXIT
&&
172 !(sigpipe_received
&&
173 error
->code
== GOT_ERR_ERRNO
&& errno
== EPIPE
) &&
175 error
->code
== GOT_ERR_ERRNO
&& errno
== EINTR
)) {
176 fprintf(stderr
, "%s: %s\n", getprogname(), error
->msg
);
183 fprintf(stderr
, "%s: unknown command '%s'\n", getprogname(), argv
[0]);
184 list_commands(stderr
);
189 usage(int hflag
, int status
)
191 FILE *fp
= (status
== 0) ? stdout
: stderr
;
193 fprintf(fp
, "usage: %s [-h] [-V | --version] command [arg ...]\n",
200 static const struct got_error
*
201 apply_unveil(const char *repo_path
, int repo_read_only
)
203 const struct got_error
*err
;
206 if (unveil("gmon.out", "rwc") != 0)
207 return got_error_from_errno2("unveil", "gmon.out");
209 if (repo_path
&& unveil(repo_path
, repo_read_only
? "r" : "rwc") != 0)
210 return got_error_from_errno2("unveil", repo_path
);
212 if (unveil(GOT_TMPDIR_STR
, "rwc") != 0)
213 return got_error_from_errno2("unveil", GOT_TMPDIR_STR
);
215 err
= got_privsep_unveil_exec_helpers();
219 if (unveil(NULL
, NULL
) != 0)
220 return got_error_from_errno("unveil");
228 fprintf(stderr
, "usage: %s info [-r repository-path]\n",
233 static const struct got_error
*
234 cmd_info(int argc
, char *argv
[])
236 const struct got_error
*error
= NULL
;
237 char *cwd
= NULL
, *repo_path
= NULL
;
238 struct got_repository
*repo
= NULL
;
239 const struct got_gotconfig
*gotconfig
= NULL
;
240 int ch
, npackfiles
, npackedobj
, nobj
;
241 off_t packsize
, loose_size
;
242 char scaled
[FMT_SCALED_STRSIZE
];
244 while ((ch
= getopt(argc
, argv
, "r:")) != -1) {
247 repo_path
= realpath(optarg
, NULL
);
248 if (repo_path
== NULL
)
249 return got_error_from_errno2("realpath",
251 got_path_strip_trailing_slashes(repo_path
);
263 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
267 cwd
= getcwd(NULL
, 0);
269 error
= got_error_from_errno("getcwd");
273 error
= got_repo_open(&repo
, repo_path
? repo_path
: cwd
, NULL
);
277 error
= apply_unveil(got_repo_get_path_git_dir(repo
), 1);
281 printf("repository: %s\n", got_repo_get_path_git_dir(repo
));
283 gotconfig
= got_repo_get_gotconfig(repo
);
285 const struct got_remote_repo
*remotes
;
287 if (got_gotconfig_get_author(gotconfig
)) {
288 printf("default author: %s\n",
289 got_gotconfig_get_author(gotconfig
));
291 got_gotconfig_get_remotes(&nremotes
, &remotes
, gotconfig
);
292 for (i
= 0; i
< nremotes
; i
++) {
293 const char *fetch_url
= remotes
[i
].fetch_url
;
294 const char *send_url
= remotes
[i
].send_url
;
295 if (strcmp(fetch_url
, send_url
) == 0) {
296 printf("remote \"%s\": %s\n", remotes
[i
].name
,
297 remotes
[i
].fetch_url
);
299 printf("remote \"%s\" (fetch): %s\n",
300 remotes
[i
].name
, remotes
[i
].fetch_url
);
301 printf("remote \"%s\" (send): %s\n",
302 remotes
[i
].name
, remotes
[i
].send_url
);
307 error
= got_repo_get_packfile_info(&npackfiles
, &npackedobj
,
311 printf("pack files: %d\n", npackfiles
);
312 if (npackfiles
> 0) {
313 if (fmt_scaled(packsize
, scaled
) == -1) {
314 error
= got_error_from_errno("fmt_scaled");
317 printf("packed objects: %d\n", npackedobj
);
318 printf("packed total size: %s\n", scaled
);
321 error
= got_repo_get_loose_object_info(&nobj
, &loose_size
, repo
);
324 printf("loose objects: %d\n", nobj
);
326 if (fmt_scaled(loose_size
, scaled
) == -1) {
327 error
= got_error_from_errno("fmt_scaled");
330 printf("loose total size: %s\n", scaled
);
334 got_repo_close(repo
);
342 fprintf(stderr
, "usage: %s pack [-a] [-r repository-path] "
343 "[-x reference] [reference ...]\n",
348 struct got_pack_progress_arg
{
349 char last_scaled_size
[FMT_SCALED_STRSIZE
];
357 int printed_something
;
360 static const struct got_error
*
361 pack_progress(void *arg
, off_t packfile_size
, int ncommits
,
362 int nobj_total
, int nobj_deltify
, int nobj_written
)
364 struct got_pack_progress_arg
*a
= arg
;
365 char scaled_size
[FMT_SCALED_STRSIZE
];
366 int p_deltify
, p_written
;
367 int print_searching
= 0, print_total
= 0;
368 int print_deltify
= 0, print_written
= 0;
370 if (a
->verbosity
< 0)
373 if (fmt_scaled(packfile_size
, scaled_size
) == -1)
374 return got_error_from_errno("fmt_scaled");
376 if (a
->last_ncommits
!= ncommits
) {
378 a
->last_ncommits
= ncommits
;
381 if (a
->last_nobj_total
!= nobj_total
) {
384 a
->last_nobj_total
= nobj_total
;
387 if (packfile_size
> 0 && (a
->last_scaled_size
[0] == '\0' ||
388 strcmp(scaled_size
, a
->last_scaled_size
)) != 0) {
389 if (strlcpy(a
->last_scaled_size
, scaled_size
,
390 FMT_SCALED_STRSIZE
) >= FMT_SCALED_STRSIZE
)
391 return got_error(GOT_ERR_NO_SPACE
);
394 if (nobj_deltify
> 0 || nobj_written
> 0) {
395 if (nobj_deltify
> 0) {
396 p_deltify
= (nobj_deltify
* 100) / nobj_total
;
397 if (p_deltify
!= a
->last_p_deltify
) {
398 a
->last_p_deltify
= p_deltify
;
404 if (nobj_written
> 0) {
405 p_written
= (nobj_written
* 100) / nobj_total
;
406 if (p_written
!= a
->last_p_written
) {
407 a
->last_p_written
= p_written
;
416 if (print_searching
|| print_total
|| print_deltify
|| print_written
)
419 printf("packing %d reference%s", ncommits
,
420 ncommits
== 1 ? "" : "s");
422 printf("; %d object%s", nobj_total
,
423 nobj_total
== 1 ? "" : "s");
425 printf("; deltify: %d%%", p_deltify
);
427 printf("; writing pack: %*s %d%%", FMT_SCALED_STRSIZE
,
428 scaled_size
, p_written
);
429 if (print_searching
|| print_total
|| print_deltify
||
431 a
->printed_something
= 1;
437 static const struct got_error
*
438 pack_index_progress(void *arg
, off_t packfile_size
, int nobj_total
,
439 int nobj_indexed
, int nobj_loose
, int nobj_resolved
)
441 struct got_pack_progress_arg
*a
= arg
;
442 char scaled_size
[FMT_SCALED_STRSIZE
];
443 int p_indexed
, p_resolved
;
444 int print_size
= 0, print_indexed
= 0, print_resolved
= 0;
446 if (a
->verbosity
< 0)
449 if (packfile_size
> 0 || nobj_indexed
> 0) {
450 if (fmt_scaled(packfile_size
, scaled_size
) == 0 &&
451 (a
->last_scaled_size
[0] == '\0' ||
452 strcmp(scaled_size
, a
->last_scaled_size
)) != 0) {
454 if (strlcpy(a
->last_scaled_size
, scaled_size
,
455 FMT_SCALED_STRSIZE
) >= FMT_SCALED_STRSIZE
)
456 return got_error(GOT_ERR_NO_SPACE
);
458 if (nobj_indexed
> 0) {
459 p_indexed
= (nobj_indexed
* 100) / nobj_total
;
460 if (p_indexed
!= a
->last_p_indexed
) {
461 a
->last_p_indexed
= p_indexed
;
466 if (nobj_resolved
> 0) {
467 p_resolved
= (nobj_resolved
* 100) /
468 (nobj_total
- nobj_loose
);
469 if (p_resolved
!= a
->last_p_resolved
) {
470 a
->last_p_resolved
= p_resolved
;
478 if (print_size
|| print_indexed
|| print_resolved
)
481 printf("%*s packed", FMT_SCALED_STRSIZE
, scaled_size
);
483 printf("; indexing %d%%", p_indexed
);
485 printf("; resolving deltas %d%%", p_resolved
);
486 if (print_size
|| print_indexed
|| print_resolved
)
492 static const struct got_error
*
493 add_ref(struct got_reflist_entry
**new, struct got_reflist_head
*refs
,
494 const char *refname
, struct got_repository
*repo
)
496 const struct got_error
*err
;
497 struct got_reference
*ref
;
501 err
= got_ref_open(&ref
, repo
, refname
, 0);
503 if (err
->code
!= GOT_ERR_NOT_REF
)
506 /* Treat argument as a reference prefix. */
507 err
= got_ref_list(refs
, repo
, refname
,
508 got_ref_cmp_by_name
, NULL
);
510 err
= got_reflist_insert(new, refs
, ref
,
511 got_ref_cmp_by_name
, NULL
);
512 if (err
|| *new == NULL
/* duplicate */)
519 static const struct got_error
*
520 cmd_pack(int argc
, char *argv
[])
522 const struct got_error
*error
= NULL
;
523 char *cwd
= NULL
, *repo_path
= NULL
;
524 struct got_repository
*repo
= NULL
;
525 int ch
, i
, loose_obj_only
= 1;
526 struct got_object_id
*pack_hash
= NULL
;
528 struct got_pack_progress_arg ppa
;
529 FILE *packfile
= NULL
;
530 struct got_pathlist_head exclude_args
;
531 struct got_pathlist_entry
*pe
;
532 struct got_reflist_head exclude_refs
;
533 struct got_reflist_head include_refs
;
534 struct got_reflist_entry
*re
, *new;
536 TAILQ_INIT(&exclude_args
);
537 TAILQ_INIT(&exclude_refs
);
538 TAILQ_INIT(&include_refs
);
540 while ((ch
= getopt(argc
, argv
, "ar:x:")) != -1) {
546 repo_path
= realpath(optarg
, NULL
);
547 if (repo_path
== NULL
)
548 return got_error_from_errno2("realpath",
550 got_path_strip_trailing_slashes(repo_path
);
553 got_path_strip_trailing_slashes(optarg
);
554 error
= got_pathlist_append(&exclude_args
,
569 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd unveil",
573 cwd
= getcwd(NULL
, 0);
575 error
= got_error_from_errno("getcwd");
579 error
= got_repo_open(&repo
, repo_path
? repo_path
: cwd
, NULL
);
583 error
= apply_unveil(got_repo_get_path_git_dir(repo
), 0);
587 TAILQ_FOREACH(pe
, &exclude_args
, entry
) {
588 const char *refname
= pe
->path
;
589 error
= add_ref(&new, &exclude_refs
, refname
, repo
);
596 error
= got_ref_list(&include_refs
, repo
, "",
597 got_ref_cmp_by_name
, NULL
);
601 for (i
= 0; i
< argc
; i
++) {
603 got_path_strip_trailing_slashes(argv
[i
]);
605 error
= add_ref(&new, &include_refs
, refname
, repo
);
611 /* Ignore references in the refs/got/ namespace. */
612 TAILQ_FOREACH_SAFE(re
, &include_refs
, entry
, new) {
613 const char *refname
= got_ref_get_name(re
->ref
);
614 if (strncmp("refs/got/", refname
, 9) != 0)
616 TAILQ_REMOVE(&include_refs
, re
, entry
);
617 got_ref_close(re
->ref
);
621 memset(&ppa
, 0, sizeof(ppa
));
622 ppa
.last_scaled_size
[0] = '\0';
623 ppa
.last_p_indexed
= -1;
624 ppa
.last_p_resolved
= -1;
626 error
= got_repo_pack_objects(&packfile
, &pack_hash
,
627 &include_refs
, &exclude_refs
, repo
, loose_obj_only
,
628 pack_progress
, &ppa
, check_cancelled
, NULL
);
630 if (ppa
.printed_something
)
635 error
= got_object_id_str(&id_str
, pack_hash
);
638 printf("\nWrote %s.pack\n", id_str
);
640 error
= got_repo_index_pack(packfile
, pack_hash
, repo
,
641 pack_index_progress
, &ppa
, check_cancelled
, NULL
);
644 printf("\nIndexed %s.pack\n", id_str
);
646 got_pathlist_free(&exclude_args
);
647 got_ref_list_free(&exclude_refs
);
648 got_ref_list_free(&include_refs
);
656 usage_indexpack(void)
658 fprintf(stderr
, "usage: %s indexpack packfile-path\n",
663 static const struct got_error
*
664 cmd_indexpack(int argc
, char *argv
[])
666 const struct got_error
*error
= NULL
;
667 struct got_repository
*repo
= NULL
;
669 struct got_object_id
*pack_hash
= NULL
;
670 char *packfile_path
= NULL
;
672 struct got_pack_progress_arg ppa
;
673 FILE *packfile
= NULL
;
675 while ((ch
= getopt(argc
, argv
, "")) != -1) {
689 packfile_path
= realpath(argv
[0], NULL
);
690 if (packfile_path
== NULL
)
691 return got_error_from_errno2("realpath", argv
[0]);
694 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd unveil",
699 error
= got_repo_open(&repo
, packfile_path
, NULL
);
703 error
= apply_unveil(got_repo_get_path_git_dir(repo
), 1);
707 memset(&ppa
, 0, sizeof(ppa
));
708 ppa
.last_scaled_size
[0] = '\0';
709 ppa
.last_p_indexed
= -1;
710 ppa
.last_p_resolved
= -1;
712 error
= got_repo_find_pack(&packfile
, &pack_hash
, repo
,
717 error
= got_object_id_str(&id_str
, pack_hash
);
721 error
= got_repo_index_pack(packfile
, pack_hash
, repo
,
722 pack_index_progress
, &ppa
, check_cancelled
, NULL
);
725 printf("\nIndexed %s.pack\n", id_str
);
735 fprintf(stderr
, "usage: %s listpack [-h] [-s] packfile-path\n",
740 struct gotadmin_list_pack_cb_args
{
750 static const struct got_error
*
751 list_pack_cb(void *arg
, struct got_object_id
*id
, int type
, off_t offset
,
752 off_t size
, off_t base_offset
, struct got_object_id
*base_id
)
754 const struct got_error
*err
;
755 struct gotadmin_list_pack_cb_args
*a
= arg
;
756 char *id_str
, *delta_str
= NULL
, *base_id_str
= NULL
;
757 const char *type_str
;
759 err
= got_object_id_str(&id_str
, id
);
764 case GOT_OBJ_TYPE_BLOB
:
765 type_str
= GOT_OBJ_LABEL_BLOB
;
768 case GOT_OBJ_TYPE_TREE
:
769 type_str
= GOT_OBJ_LABEL_TREE
;
772 case GOT_OBJ_TYPE_COMMIT
:
773 type_str
= GOT_OBJ_LABEL_COMMIT
;
776 case GOT_OBJ_TYPE_TAG
:
777 type_str
= GOT_OBJ_LABEL_TAG
;
780 case GOT_OBJ_TYPE_OFFSET_DELTA
:
781 type_str
= "offset-delta";
782 if (asprintf(&delta_str
, " base-offset %lld",
783 (long long)base_offset
) == -1) {
784 err
= got_error_from_errno("asprintf");
789 case GOT_OBJ_TYPE_REF_DELTA
:
790 type_str
= "ref-delta";
791 err
= got_object_id_str(&base_id_str
, base_id
);
794 if (asprintf(&delta_str
, " base-id %s", base_id_str
) == -1) {
795 err
= got_error_from_errno("asprintf");
801 err
= got_error(GOT_ERR_OBJ_TYPE
);
804 if (a
->human_readable
) {
805 char scaled
[FMT_SCALED_STRSIZE
];
807 if (fmt_scaled(size
, scaled
) == -1) {
808 err
= got_error_from_errno("fmt_scaled");
812 while (isspace((unsigned char)*s
))
814 printf("%s %s at %lld size %s%s\n", id_str
, type_str
,
815 (long long)offset
, s
, delta_str
? delta_str
: "");
817 printf("%s %s at %lld size %lld%s\n", id_str
, type_str
,
818 (long long)offset
, (long long)size
,
819 delta_str
? delta_str
: "");
828 static const struct got_error
*
829 cmd_listpack(int argc
, char *argv
[])
831 const struct got_error
*error
= NULL
;
832 struct got_repository
*repo
= NULL
;
834 struct got_object_id
*pack_hash
= NULL
;
835 char *packfile_path
= NULL
;
837 struct gotadmin_list_pack_cb_args lpa
;
838 FILE *packfile
= NULL
;
839 int show_stats
= 0, human_readable
= 0;
841 while ((ch
= getopt(argc
, argv
, "hs")) != -1) {
860 packfile_path
= realpath(argv
[0], NULL
);
861 if (packfile_path
== NULL
)
862 return got_error_from_errno2("realpath", argv
[0]);
865 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
869 error
= got_repo_open(&repo
, packfile_path
, NULL
);
873 error
= apply_unveil(got_repo_get_path_git_dir(repo
), 1);
877 error
= got_repo_find_pack(&packfile
, &pack_hash
, repo
,
881 error
= got_object_id_str(&id_str
, pack_hash
);
885 memset(&lpa
, 0, sizeof(lpa
));
886 lpa
.human_readable
= human_readable
;
887 error
= got_repo_list_pack(packfile
, pack_hash
, repo
,
888 list_pack_cb
, &lpa
, check_cancelled
, NULL
);
892 printf("objects: %d\n blobs: %d\n trees: %d\n commits: %d\n"
893 " tags: %d\n offset-deltas: %d\n ref-deltas: %d\n",
894 lpa
.nblobs
+ lpa
.ntrees
+ lpa
.ncommits
+ lpa
.ntags
+
895 lpa
.noffdeltas
+ lpa
.nrefdeltas
,
896 lpa
.nblobs
, lpa
.ntrees
, lpa
.ncommits
, lpa
.ntags
,
897 lpa
.noffdeltas
, lpa
.nrefdeltas
);
909 fprintf(stderr
, "usage: %s cleanup [-a] [-p] [-n] [-r repository-path] "
910 "[-q]\n", getprogname());
914 struct got_cleanup_progress_arg
{
919 int printed_something
;
923 static const struct got_error
*
924 cleanup_progress(void *arg
, int nloose
, int ncommits
, int npurged
)
926 struct got_cleanup_progress_arg
*a
= arg
;
927 int print_loose
= 0, print_commits
= 0, print_purged
= 0;
929 if (a
->last_nloose
!= nloose
) {
931 a
->last_nloose
= nloose
;
933 if (a
->last_ncommits
!= ncommits
) {
936 a
->last_ncommits
= ncommits
;
938 if (a
->last_npurged
!= npurged
) {
942 a
->last_npurged
= npurged
;
945 if (a
->verbosity
< 0)
948 if (print_loose
|| print_commits
|| print_purged
)
951 printf("%d loose object%s", nloose
, nloose
== 1 ? "" : "s");
953 printf("; %d commit%s scanned", ncommits
,
954 ncommits
== 1 ? "" : "s");
957 printf("; %d object%s could be purged", npurged
,
958 npurged
== 1 ? "" : "s");
960 printf("; %d object%s purged", npurged
,
961 npurged
== 1 ? "" : "s");
964 if (print_loose
|| print_commits
|| print_purged
) {
965 a
->printed_something
= 1;
971 struct got_lonely_packidx_progress_arg
{
973 int printed_something
;
977 static const struct got_error
*
978 lonely_packidx_progress(void *arg
, const char *path
)
980 struct got_lonely_packidx_progress_arg
*a
= arg
;
982 if (a
->verbosity
< 0)
986 printf("%s could be removed\n", path
);
988 printf("%s removed\n", path
);
990 a
->printed_something
= 1;
994 static const struct got_error
*
995 cmd_cleanup(int argc
, char *argv
[])
997 const struct got_error
*error
= NULL
;
998 char *cwd
= NULL
, *repo_path
= NULL
;
999 struct got_repository
*repo
= NULL
;
1000 int ch
, dry_run
= 0, npacked
= 0, verbosity
= 0;
1001 int remove_lonely_packidx
= 0, ignore_mtime
= 0;
1002 struct got_cleanup_progress_arg cpa
;
1003 struct got_lonely_packidx_progress_arg lpa
;
1004 off_t size_before
, size_after
;
1005 char scaled_before
[FMT_SCALED_STRSIZE
];
1006 char scaled_after
[FMT_SCALED_STRSIZE
];
1007 char scaled_diff
[FMT_SCALED_STRSIZE
];
1011 while ((ch
= getopt(argc
, argv
, "apr:nq")) != -1) {
1017 remove_lonely_packidx
= 1;
1020 repo_path
= realpath(optarg
, NULL
);
1021 if (repo_path
== NULL
)
1022 return got_error_from_errno2("realpath",
1024 got_path_strip_trailing_slashes(repo_path
);
1042 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
1046 cwd
= getcwd(NULL
, 0);
1048 error
= got_error_from_errno("getcwd");
1052 error
= got_repo_open(&repo
, repo_path
? repo_path
: cwd
, NULL
);
1056 error
= apply_unveil(got_repo_get_path_git_dir(repo
), 0);
1060 got_repo_get_gitconfig_extensions(&extensions
, &nextensions
,
1062 for (i
= 0; i
< nextensions
; i
++) {
1063 if (strcasecmp(extensions
[i
], "preciousObjects") == 0) {
1064 error
= got_error_msg(GOT_ERR_GIT_REPO_EXT
,
1065 "the preciousObjects Git extension is enabled; "
1066 "this implies that objects must not be deleted");
1071 if (remove_lonely_packidx
) {
1072 memset(&lpa
, 0, sizeof(lpa
));
1073 lpa
.dry_run
= dry_run
;
1074 lpa
.verbosity
= verbosity
;
1075 error
= got_repo_remove_lonely_packidx(repo
, dry_run
,
1076 lonely_packidx_progress
, &lpa
, check_cancelled
, NULL
);
1080 memset(&cpa
, 0, sizeof(cpa
));
1081 cpa
.last_ncommits
= -1;
1082 cpa
.last_npurged
= -1;
1083 cpa
.dry_run
= dry_run
;
1084 cpa
.verbosity
= verbosity
;
1085 error
= got_repo_purge_unreferenced_loose_objects(repo
,
1086 &size_before
, &size_after
, &npacked
, dry_run
, ignore_mtime
,
1087 cleanup_progress
, &cpa
, check_cancelled
, NULL
);
1088 if (cpa
.printed_something
)
1092 if (cpa
.printed_something
) {
1093 if (fmt_scaled(size_before
, scaled_before
) == -1) {
1094 error
= got_error_from_errno("fmt_scaled");
1097 if (fmt_scaled(size_after
, scaled_after
) == -1) {
1098 error
= got_error_from_errno("fmt_scaled");
1101 if (fmt_scaled(size_before
- size_after
, scaled_diff
) == -1) {
1102 error
= got_error_from_errno("fmt_scaled");
1105 printf("loose total size before: %s\n", scaled_before
);
1106 printf("loose total size after: %s\n", scaled_after
);
1108 printf("disk space which would be freed: %s\n",
1111 printf("disk space freed: %s\n", scaled_diff
);
1112 printf("loose objects also found in pack files: %d\n", npacked
);
1116 got_repo_close(repo
);