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 [-A hashing-algorithm] [-b branch]"
283 " repository-path\n",
288 static const struct got_error
*
289 cmd_init(int argc
, char *argv
[])
291 const struct got_error
*error
= NULL
;
292 const char *head_name
= NULL
;
293 char *repo_path
= NULL
;
294 enum got_hash_algorithm algo
= GOT_HASH_SHA1
;
298 if (pledge("stdio rpath wpath cpath unveil", NULL
) == -1)
302 while ((ch
= getopt(argc
, argv
, "A:b:")) != -1) {
305 if (!strcmp(optarg
, "sha1"))
306 algo
= GOT_HASH_SHA1
;
307 else if (!strcmp(optarg
, "sha256"))
308 algo
= GOT_HASH_SHA256
;
310 return got_error_path(optarg
,
311 GOT_ERR_OBJECT_FORMAT
);
328 repo_path
= strdup(argv
[0]);
329 if (repo_path
== NULL
)
330 return got_error_from_errno("strdup");
332 got_path_strip_trailing_slashes(repo_path
);
334 error
= got_path_mkdir(repo_path
);
336 !(error
->code
== GOT_ERR_ERRNO
&& errno
== EEXIST
))
339 error
= apply_unveil(repo_path
, 0);
343 error
= got_repo_init(repo_path
, head_name
, algo
);
349 static const struct got_error
*
350 cmd_info(int argc
, char *argv
[])
352 const struct got_error
*error
= NULL
;
353 char *repo_path
= NULL
;
354 struct got_repository
*repo
= NULL
;
355 const struct got_gotconfig
*gotconfig
= NULL
;
356 int ch
, npackfiles
, npackedobj
, nobj
;
357 off_t packsize
, loose_size
;
358 char scaled
[FMT_SCALED_STRSIZE
];
359 int *pack_fds
= NULL
;
362 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
367 while ((ch
= getopt(argc
, argv
, "r:")) != -1) {
370 repo_path
= realpath(optarg
, NULL
);
371 if (repo_path
== NULL
)
372 return got_error_from_errno2("realpath",
374 got_path_strip_trailing_slashes(repo_path
);
385 if (repo_path
== NULL
) {
386 error
= get_repo_path(&repo_path
);
390 error
= got_repo_pack_fds_open(&pack_fds
);
393 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
397 /* Remove "cpath" promise. */
398 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
402 error
= apply_unveil(got_repo_get_path_git_dir(repo
), 1);
406 printf("repository: %s\n", got_repo_get_path_git_dir(repo
));
407 printf("hashing algorithm: %s\n",
408 got_repo_get_object_format(repo
) == GOT_HASH_SHA1
? "sha1"
411 gotconfig
= got_repo_get_gotconfig(repo
);
413 const struct got_remote_repo
*remotes
;
415 if (got_gotconfig_get_author(gotconfig
)) {
416 printf("default author: %s\n",
417 got_gotconfig_get_author(gotconfig
));
419 got_gotconfig_get_remotes(&nremotes
, &remotes
, gotconfig
);
420 for (i
= 0; i
< nremotes
; i
++) {
421 const char *fetch_url
= remotes
[i
].fetch_url
;
422 const char *send_url
= remotes
[i
].send_url
;
423 if (strcmp(fetch_url
, send_url
) == 0) {
424 printf("remote \"%s\": %s\n", remotes
[i
].name
,
425 remotes
[i
].fetch_url
);
427 printf("remote \"%s\" (fetch): %s\n",
428 remotes
[i
].name
, remotes
[i
].fetch_url
);
429 printf("remote \"%s\" (send): %s\n",
430 remotes
[i
].name
, remotes
[i
].send_url
);
435 error
= got_repo_get_packfile_info(&npackfiles
, &npackedobj
,
439 printf("pack files: %d\n", npackfiles
);
440 if (npackfiles
> 0) {
441 if (fmt_scaled(packsize
, scaled
) == -1) {
442 error
= got_error_from_errno("fmt_scaled");
445 printf("packed objects: %d\n", npackedobj
);
446 printf("packed total size: %s\n", scaled
);
449 error
= got_repo_get_loose_object_info(&nobj
, &loose_size
, repo
);
452 printf("loose objects: %d\n", nobj
);
454 if (fmt_scaled(loose_size
, scaled
) == -1) {
455 error
= got_error_from_errno("fmt_scaled");
458 printf("loose total size: %s\n", scaled
);
462 got_repo_close(repo
);
464 const struct got_error
*pack_err
=
465 got_repo_pack_fds_close(pack_fds
);
477 fprintf(stderr
, "usage: %s pack [-aDq] [-r repository-path] "
478 "[-x reference] [reference ...]\n", getprogname());
482 struct got_pack_progress_arg
{
484 char last_scaled_size
[FMT_SCALED_STRSIZE
];
496 int printed_something
;
500 print_load_info(FILE *out
, int print_colored
, int print_found
, int print_trees
,
501 int ncolored
, int nfound
, int ntrees
)
504 fprintf(out
, "%d commit%s colored", ncolored
,
505 ncolored
== 1 ? "" : "s");
508 fprintf(out
, "%s%d object%s found",
509 ncolored
> 0 ? "; " : "",
510 nfound
, nfound
== 1 ? "" : "s");
513 fprintf(out
, "; %d tree%s scanned", ntrees
,
514 ntrees
== 1 ? "" : "s");
518 static const struct got_error
*
519 pack_progress(void *arg
, int ncolored
, int nfound
, int ntrees
,
520 off_t packfile_size
, int ncommits
, int nobj_total
, int nobj_deltify
,
523 struct got_pack_progress_arg
*a
= arg
;
524 char scaled_size
[FMT_SCALED_STRSIZE
];
525 int p_deltify
, p_written
;
526 int print_colored
= 0, print_found
= 0, print_trees
= 0;
527 int print_searching
= 0, print_total
= 0;
528 int print_deltify
= 0, print_written
= 0;
530 if (a
->verbosity
< 0)
533 if (a
->last_ncolored
!= ncolored
) {
535 a
->last_ncolored
= ncolored
;
538 if (a
->last_nfound
!= nfound
) {
541 a
->last_nfound
= nfound
;
544 if (a
->last_ntrees
!= ntrees
) {
548 a
->last_ntrees
= ntrees
;
551 if ((print_colored
|| print_found
|| print_trees
) &&
553 fprintf(a
->out
, "\r");
554 print_load_info(a
->out
, print_colored
, print_found
,
555 print_trees
, ncolored
, nfound
, ntrees
);
556 a
->printed_something
= 1;
559 } else if (!a
->loading_done
) {
560 fprintf(a
->out
, "\r");
561 print_load_info(a
->out
, 1, 1, 1, ncolored
, nfound
, ntrees
);
562 fprintf(a
->out
, "\n");
566 if (fmt_scaled(packfile_size
, scaled_size
) == -1)
567 return got_error_from_errno("fmt_scaled");
569 if (a
->last_ncommits
!= ncommits
) {
571 a
->last_ncommits
= ncommits
;
574 if (a
->last_nobj_total
!= nobj_total
) {
577 a
->last_nobj_total
= nobj_total
;
580 if (packfile_size
> 0 && (a
->last_scaled_size
[0] == '\0' ||
581 strcmp(scaled_size
, a
->last_scaled_size
)) != 0) {
582 if (strlcpy(a
->last_scaled_size
, scaled_size
,
583 FMT_SCALED_STRSIZE
) >= FMT_SCALED_STRSIZE
)
584 return got_error(GOT_ERR_NO_SPACE
);
587 if (nobj_deltify
> 0 || nobj_written
> 0) {
588 if (nobj_deltify
> 0) {
589 p_deltify
= (nobj_deltify
* 100) / nobj_total
;
590 if (p_deltify
!= a
->last_p_deltify
) {
591 a
->last_p_deltify
= p_deltify
;
597 if (nobj_written
> 0) {
598 p_written
= (nobj_written
* 100) / nobj_total
;
599 if (p_written
!= a
->last_p_written
) {
600 a
->last_p_written
= p_written
;
609 if (print_searching
|| print_total
|| print_deltify
|| print_written
)
610 fprintf(a
->out
, "\r");
612 fprintf(a
->out
, "packing %d reference%s", ncommits
,
613 ncommits
== 1 ? "" : "s");
615 fprintf(a
->out
, "; %d object%s", nobj_total
,
616 nobj_total
== 1 ? "" : "s");
618 fprintf(a
->out
, "; deltify: %d%%", p_deltify
);
620 fprintf(a
->out
, "; writing pack: %*s %d%%",
621 FMT_SCALED_STRSIZE
- 2, scaled_size
, p_written
);
622 if (print_searching
|| print_total
|| print_deltify
||
624 a
->printed_something
= 1;
630 static const struct got_error
*
631 pack_index_progress(void *arg
, off_t packfile_size
, int nobj_total
,
632 int nobj_indexed
, int nobj_loose
, int nobj_resolved
)
634 struct got_pack_progress_arg
*a
= arg
;
635 char scaled_size
[FMT_SCALED_STRSIZE
];
636 int p_indexed
, p_resolved
;
637 int print_size
= 0, print_indexed
= 0, print_resolved
= 0;
639 if (a
->verbosity
< 0)
642 if (packfile_size
> 0 || nobj_indexed
> 0) {
643 if (fmt_scaled(packfile_size
, scaled_size
) == 0 &&
644 (a
->last_scaled_size
[0] == '\0' ||
645 strcmp(scaled_size
, a
->last_scaled_size
)) != 0) {
647 if (strlcpy(a
->last_scaled_size
, scaled_size
,
648 FMT_SCALED_STRSIZE
) >= FMT_SCALED_STRSIZE
)
649 return got_error(GOT_ERR_NO_SPACE
);
651 if (nobj_indexed
> 0) {
652 p_indexed
= (nobj_indexed
* 100) / nobj_total
;
653 if (p_indexed
!= a
->last_p_indexed
) {
654 a
->last_p_indexed
= p_indexed
;
659 if (nobj_resolved
> 0) {
660 p_resolved
= (nobj_resolved
* 100) /
661 (nobj_total
- nobj_loose
);
662 if (p_resolved
!= a
->last_p_resolved
) {
663 a
->last_p_resolved
= p_resolved
;
671 if (print_size
|| print_indexed
|| print_resolved
)
674 printf("%*s packed", FMT_SCALED_STRSIZE
- 2, scaled_size
);
676 printf("; indexing %d%%", p_indexed
);
678 printf("; resolving deltas %d%%", p_resolved
);
679 if (print_size
|| print_indexed
|| print_resolved
)
685 static const struct got_error
*
686 add_ref(struct got_reflist_entry
**new, struct got_reflist_head
*refs
,
687 const char *refname
, struct got_repository
*repo
)
689 const struct got_error
*err
;
690 struct got_reference
*ref
;
694 err
= got_ref_open(&ref
, repo
, refname
, 0);
696 if (err
->code
!= GOT_ERR_NOT_REF
)
699 /* Treat argument as a reference prefix. */
700 err
= got_ref_list(refs
, repo
, refname
,
701 got_ref_cmp_by_name
, NULL
);
703 err
= got_reflist_insert(new, refs
, ref
,
704 got_ref_cmp_by_name
, NULL
);
705 if (err
|| *new == NULL
/* duplicate */)
712 static const struct got_error
*
713 cmd_pack(int argc
, char *argv
[])
715 const struct got_error
*error
= NULL
;
716 char *repo_path
= NULL
;
717 struct got_repository
*repo
= NULL
;
718 int ch
, i
, loose_obj_only
= 1, force_refdelta
= 0, verbosity
= 0;
719 struct got_object_id
*pack_hash
= NULL
;
721 struct got_pack_progress_arg ppa
;
722 FILE *packfile
= NULL
;
723 struct got_pathlist_head exclude_args
;
724 struct got_pathlist_entry
*pe
;
725 struct got_reflist_head exclude_refs
;
726 struct got_reflist_head include_refs
;
727 struct got_reflist_entry
*re
, *new;
728 int *pack_fds
= NULL
;
730 TAILQ_INIT(&exclude_args
);
731 TAILQ_INIT(&exclude_refs
);
732 TAILQ_INIT(&include_refs
);
735 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd unveil",
740 while ((ch
= getopt(argc
, argv
, "aDqr:x:")) != -1) {
752 repo_path
= realpath(optarg
, NULL
);
753 if (repo_path
== NULL
)
754 return got_error_from_errno2("realpath",
756 got_path_strip_trailing_slashes(repo_path
);
759 got_path_strip_trailing_slashes(optarg
);
760 error
= got_pathlist_append(&exclude_args
,
774 if (repo_path
== NULL
) {
775 error
= get_repo_path(&repo_path
);
779 error
= got_repo_pack_fds_open(&pack_fds
);
782 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
786 error
= apply_unveil(got_repo_get_path_git_dir(repo
), 0);
790 TAILQ_FOREACH(pe
, &exclude_args
, entry
) {
791 const char *refname
= pe
->path
;
792 error
= add_ref(&new, &exclude_refs
, refname
, repo
);
798 error
= got_ref_list(&include_refs
, repo
, "",
799 got_ref_cmp_by_name
, NULL
);
803 for (i
= 0; i
< argc
; i
++) {
805 got_path_strip_trailing_slashes(argv
[i
]);
807 error
= add_ref(&new, &include_refs
, refname
, repo
);
813 /* Ignore references in the refs/got/ namespace. */
814 TAILQ_FOREACH_SAFE(re
, &include_refs
, entry
, new) {
815 const char *refname
= got_ref_get_name(re
->ref
);
816 if (strncmp("refs/got/", refname
, 9) != 0)
818 TAILQ_REMOVE(&include_refs
, re
, entry
);
819 got_ref_close(re
->ref
);
823 memset(&ppa
, 0, sizeof(ppa
));
825 ppa
.last_scaled_size
[0] = '\0';
826 ppa
.last_p_indexed
= -1;
827 ppa
.last_p_resolved
= -1;
828 ppa
.verbosity
= verbosity
;
830 error
= got_repo_pack_objects(&packfile
, &pack_hash
,
831 &include_refs
, &exclude_refs
, repo
, loose_obj_only
,
832 force_refdelta
, pack_progress
, &ppa
, check_cancelled
, NULL
);
834 if (ppa
.printed_something
)
839 error
= got_object_id_str(&id_str
, pack_hash
);
843 printf("\nWrote %s.pack\n", id_str
);
845 error
= got_repo_index_pack(packfile
, pack_hash
, repo
,
846 pack_index_progress
, &ppa
, check_cancelled
, NULL
);
850 printf("\nIndexed %s.pack\n", id_str
);
853 got_repo_close(repo
);
855 const struct got_error
*pack_err
=
856 got_repo_pack_fds_close(pack_fds
);
860 got_pathlist_free(&exclude_args
, GOT_PATHLIST_FREE_NONE
);
861 got_ref_list_free(&exclude_refs
);
862 got_ref_list_free(&include_refs
);
870 usage_indexpack(void)
872 fprintf(stderr
, "usage: %s indexpack packfile-path\n",
877 static const struct got_error
*
878 cmd_indexpack(int argc
, char *argv
[])
880 const struct got_error
*error
= NULL
;
881 struct got_repository
*repo
= NULL
;
883 struct got_object_id
*pack_hash
= NULL
;
884 char *packfile_path
= NULL
;
886 struct got_pack_progress_arg ppa
;
887 FILE *packfile
= NULL
;
888 int *pack_fds
= NULL
;
890 while ((ch
= getopt(argc
, argv
, "")) != -1) {
904 packfile_path
= realpath(argv
[0], NULL
);
905 if (packfile_path
== NULL
)
906 return got_error_from_errno2("realpath", argv
[0]);
909 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd unveil",
914 error
= got_repo_pack_fds_open(&pack_fds
);
917 error
= got_repo_open(&repo
, packfile_path
, NULL
, pack_fds
);
921 error
= apply_unveil(got_repo_get_path_git_dir(repo
), 0);
925 memset(&ppa
, 0, sizeof(ppa
));
927 ppa
.last_scaled_size
[0] = '\0';
928 ppa
.last_p_indexed
= -1;
929 ppa
.last_p_resolved
= -1;
931 error
= got_repo_find_pack(&packfile
, &pack_hash
, repo
,
936 error
= got_object_id_str(&id_str
, pack_hash
);
940 error
= got_repo_index_pack(packfile
, pack_hash
, repo
,
941 pack_index_progress
, &ppa
, check_cancelled
, NULL
);
944 printf("\nIndexed %s.pack\n", id_str
);
947 got_repo_close(repo
);
949 const struct got_error
*pack_err
=
950 got_repo_pack_fds_close(pack_fds
);
962 fprintf(stderr
, "usage: %s listpack [-hs] packfile-path\n",
967 struct gotadmin_list_pack_cb_args
{
977 static const struct got_error
*
978 list_pack_cb(void *arg
, struct got_object_id
*id
, int type
, off_t offset
,
979 off_t size
, off_t base_offset
, struct got_object_id
*base_id
)
981 const struct got_error
*err
;
982 struct gotadmin_list_pack_cb_args
*a
= arg
;
983 char *id_str
, *delta_str
= NULL
, *base_id_str
= NULL
;
984 const char *type_str
;
986 err
= got_object_id_str(&id_str
, id
);
991 case GOT_OBJ_TYPE_BLOB
:
992 type_str
= GOT_OBJ_LABEL_BLOB
;
995 case GOT_OBJ_TYPE_TREE
:
996 type_str
= GOT_OBJ_LABEL_TREE
;
999 case GOT_OBJ_TYPE_COMMIT
:
1000 type_str
= GOT_OBJ_LABEL_COMMIT
;
1003 case GOT_OBJ_TYPE_TAG
:
1004 type_str
= GOT_OBJ_LABEL_TAG
;
1007 case GOT_OBJ_TYPE_OFFSET_DELTA
:
1008 type_str
= "offset-delta";
1009 if (asprintf(&delta_str
, " base-offset %lld",
1010 (long long)base_offset
) == -1) {
1011 err
= got_error_from_errno("asprintf");
1016 case GOT_OBJ_TYPE_REF_DELTA
:
1017 type_str
= "ref-delta";
1018 err
= got_object_id_str(&base_id_str
, base_id
);
1021 if (asprintf(&delta_str
, " base-id %s", base_id_str
) == -1) {
1022 err
= got_error_from_errno("asprintf");
1028 err
= got_error(GOT_ERR_OBJ_TYPE
);
1031 if (a
->human_readable
) {
1032 char scaled
[FMT_SCALED_STRSIZE
];
1034 if (fmt_scaled(size
, scaled
) == -1) {
1035 err
= got_error_from_errno("fmt_scaled");
1039 while (isspace((unsigned char)*s
))
1041 printf("%s %s at %lld size %s%s\n", id_str
, type_str
,
1042 (long long)offset
, s
, delta_str
? delta_str
: "");
1044 printf("%s %s at %lld size %lld%s\n", id_str
, type_str
,
1045 (long long)offset
, (long long)size
,
1046 delta_str
? delta_str
: "");
1055 static const struct got_error
*
1056 cmd_listpack(int argc
, char *argv
[])
1058 const struct got_error
*error
= NULL
;
1059 struct got_repository
*repo
= NULL
;
1061 struct got_object_id
*pack_hash
= NULL
;
1062 char *packfile_path
= NULL
;
1063 char *id_str
= NULL
;
1064 struct gotadmin_list_pack_cb_args lpa
;
1065 FILE *packfile
= NULL
;
1066 int show_stats
= 0, human_readable
= 0;
1067 int *pack_fds
= NULL
;
1070 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
1075 while ((ch
= getopt(argc
, argv
, "hs")) != -1) {
1094 packfile_path
= realpath(argv
[0], NULL
);
1095 if (packfile_path
== NULL
)
1096 return got_error_from_errno2("realpath", argv
[0]);
1098 error
= got_repo_pack_fds_open(&pack_fds
);
1101 error
= got_repo_open(&repo
, packfile_path
, NULL
, pack_fds
);
1105 /* Remove "cpath" promise. */
1106 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
1110 error
= apply_unveil(got_repo_get_path_git_dir(repo
), 1);
1114 error
= got_repo_find_pack(&packfile
, &pack_hash
, repo
,
1118 error
= got_object_id_str(&id_str
, pack_hash
);
1122 memset(&lpa
, 0, sizeof(lpa
));
1123 lpa
.human_readable
= human_readable
;
1124 error
= got_repo_list_pack(packfile
, pack_hash
, repo
,
1125 list_pack_cb
, &lpa
, check_cancelled
, NULL
);
1129 printf("objects: %d\n blobs: %d\n trees: %d\n commits: %d\n"
1130 " tags: %d\n offset-deltas: %d\n ref-deltas: %d\n",
1131 lpa
.nblobs
+ lpa
.ntrees
+ lpa
.ncommits
+ lpa
.ntags
+
1132 lpa
.noffdeltas
+ lpa
.nrefdeltas
,
1133 lpa
.nblobs
, lpa
.ntrees
, lpa
.ncommits
, lpa
.ntags
,
1134 lpa
.noffdeltas
, lpa
.nrefdeltas
);
1138 got_repo_close(repo
);
1140 const struct got_error
*pack_err
=
1141 got_repo_pack_fds_close(pack_fds
);
1147 free(packfile_path
);
1154 fprintf(stderr
, "usage: %s cleanup [-anpq] [-r repository-path]\n",
1159 struct got_cleanup_progress_arg
{
1163 int last_nredundant
;
1165 int printed_something
;
1169 static const struct got_error
*
1170 cleanup_progress(void *arg
, int ncommits
, int nloose
, int npurged
,
1173 struct got_cleanup_progress_arg
*a
= arg
;
1174 int print_loose
= 0, print_commits
= 0, print_purged
= 0;
1175 int print_redundant
= 0;
1177 if (a
->last_ncommits
!= ncommits
) {
1179 a
->last_ncommits
= ncommits
;
1181 if (a
->last_nloose
!= nloose
) {
1184 a
->last_nloose
= nloose
;
1186 if (a
->last_npurged
!= npurged
) {
1190 a
->last_npurged
= npurged
;
1192 if (a
->last_nredundant
!= nredundant
) {
1196 print_redundant
= 1;
1197 a
->last_nredundant
= nredundant
;
1200 if (a
->verbosity
< 0)
1203 if (print_loose
|| print_commits
|| print_purged
|| print_redundant
)
1206 printf("%d commit%s scanned", ncommits
,
1207 ncommits
== 1 ? "" : "s");
1209 printf("; %d loose object%s", nloose
, nloose
== 1 ? "" : "s");
1210 if (print_purged
|| print_redundant
) {
1212 printf("; could purge %d object%s", npurged
,
1213 npurged
== 1 ? "" : "s");
1215 printf("; purged %d object%s", npurged
,
1216 npurged
== 1 ? "" : "s");
1219 if (print_redundant
) {
1221 printf(", %d pack file%s", nredundant
,
1222 nredundant
== 1 ? "" : "s");
1224 printf(", %d pack file%s", nredundant
,
1225 nredundant
== 1 ? "" : "s");
1228 if (print_loose
|| print_commits
|| print_purged
|| print_redundant
) {
1229 a
->printed_something
= 1;
1235 struct got_lonely_packidx_progress_arg
{
1237 int printed_something
;
1241 static const struct got_error
*
1242 lonely_packidx_progress(void *arg
, const char *path
)
1244 struct got_lonely_packidx_progress_arg
*a
= arg
;
1246 if (a
->verbosity
< 0)
1250 printf("%s could be removed\n", path
);
1252 printf("%s removed\n", path
);
1254 a
->printed_something
= 1;
1258 static const struct got_error
*
1259 cmd_cleanup(int argc
, char *argv
[])
1261 const struct got_error
*error
= NULL
;
1262 char *repo_path
= NULL
;
1263 struct got_repository
*repo
= NULL
;
1264 int ch
, dry_run
= 0, verbosity
= 0;
1265 int ncommits
= 0, nloose
= 0, npacked
= 0;
1266 int remove_lonely_packidx
= 0, ignore_mtime
= 0;
1267 struct got_cleanup_progress_arg cpa
;
1268 struct got_lonely_packidx_progress_arg lpa
;
1269 off_t loose_before
, loose_after
;
1270 off_t pack_before
, pack_after
;
1272 char loose_before_scaled
[FMT_SCALED_STRSIZE
];
1273 char loose_after_scaled
[FMT_SCALED_STRSIZE
];
1274 char pack_before_scaled
[FMT_SCALED_STRSIZE
];
1275 char pack_after_scaled
[FMT_SCALED_STRSIZE
];
1276 char total_size_scaled
[FMT_SCALED_STRSIZE
];
1277 int *pack_fds
= NULL
;
1280 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
1285 while ((ch
= getopt(argc
, argv
, "anpqr:")) != -1) {
1294 remove_lonely_packidx
= 1;
1300 repo_path
= realpath(optarg
, NULL
);
1301 if (repo_path
== NULL
)
1302 return got_error_from_errno2("realpath",
1304 got_path_strip_trailing_slashes(repo_path
);
1315 if (repo_path
== NULL
) {
1316 error
= get_repo_path(&repo_path
);
1320 error
= got_repo_pack_fds_open(&pack_fds
);
1323 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
1327 error
= apply_unveil(got_repo_get_path_git_dir(repo
), 0);
1331 if (got_repo_has_extension(repo
, "preciousObjects")) {
1332 error
= got_error_msg(GOT_ERR_GIT_REPO_EXT
,
1333 "the preciousObjects Git extension is enabled; "
1334 "this implies that objects must not be deleted");
1338 if (remove_lonely_packidx
) {
1339 memset(&lpa
, 0, sizeof(lpa
));
1340 lpa
.dry_run
= dry_run
;
1341 lpa
.verbosity
= verbosity
;
1342 error
= got_repo_remove_lonely_packidx(repo
, dry_run
,
1343 lonely_packidx_progress
, &lpa
, check_cancelled
, NULL
);
1347 memset(&cpa
, 0, sizeof(cpa
));
1348 cpa
.last_nloose
= -1;
1349 cpa
.last_npurged
= -1;
1350 cpa
.last_nredundant
= -1;
1351 cpa
.dry_run
= dry_run
;
1352 cpa
.verbosity
= verbosity
;
1354 error
= got_repo_cleanup(repo
, &loose_before
, &loose_after
,
1355 &pack_before
, &pack_after
, &ncommits
, &nloose
, &npacked
,
1356 dry_run
, ignore_mtime
, cleanup_progress
, &cpa
,
1357 check_cancelled
, NULL
);
1358 if (cpa
.printed_something
)
1363 total_size
= (loose_before
- loose_after
) + (pack_before
- pack_after
);
1365 if (cpa
.printed_something
) {
1366 if (fmt_scaled(loose_before
, loose_before_scaled
) == -1) {
1367 error
= got_error_from_errno("fmt_scaled");
1370 if (fmt_scaled(loose_after
, loose_after_scaled
) == -1) {
1371 error
= got_error_from_errno("fmt_scaled");
1374 if (fmt_scaled(pack_before
, pack_before_scaled
) == -1) {
1375 error
= got_error_from_errno("fmt_scaled");
1378 if (fmt_scaled(pack_after
, pack_after_scaled
) == -1) {
1379 error
= got_error_from_errno("fmt_scaled");
1382 if (fmt_scaled(total_size
, total_size_scaled
) == -1) {
1383 error
= got_error_from_errno("fmt_scaled");
1386 printf("loose total size before: %s\n", loose_before_scaled
);
1387 printf("loose total size after: %s\n", loose_after_scaled
);
1388 printf("pack files total size before: %s\n",
1389 pack_before_scaled
);
1390 printf("pack files total size after: %s\n", pack_after_scaled
);
1392 printf("disk space which would be freed: %s\n",
1395 printf("disk space freed: %s\n", total_size_scaled
);
1396 printf("loose objects also found in pack files: %d\n", npacked
);
1401 got_repo_close(repo
);
1403 const struct got_error
*pack_err
=
1404 got_repo_pack_fds_close(pack_fds
);
1415 fprintf(stderr
, "usage: %s dump [-q] [-r repository-path] "
1416 "[-x reference] [reference]...\n", getprogname());
1420 static const struct got_error
*
1421 cmd_dump(int argc
, char *argv
[])
1423 const struct got_error
*error
= NULL
;
1424 struct got_pack_progress_arg ppa
;
1425 struct got_repository
*repo
= NULL
;
1426 struct got_pathlist_head exclude_args
;
1427 struct got_pathlist_entry
*pe
;
1428 struct got_reflist_head exclude_refs
;
1429 struct got_reflist_head include_refs
;
1430 struct got_reflist_entry
*re
, *new;
1431 const char *refname
;
1432 char *repo_path
= NULL
;
1433 int *pack_fds
= NULL
;
1437 TAILQ_INIT(&exclude_args
);
1438 TAILQ_INIT(&exclude_refs
);
1439 TAILQ_INIT(&include_refs
);
1442 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
1447 while ((ch
= getopt(argc
, argv
, "qr:x:")) != -1) {
1453 repo_path
= realpath(optarg
, NULL
);
1454 if (repo_path
== NULL
)
1455 return got_error_from_errno2("realpath",
1457 got_path_strip_trailing_slashes(repo_path
);
1460 error
= got_pathlist_append(&exclude_args
,
1473 if (repo_path
== NULL
) {
1474 error
= get_repo_path(&repo_path
);
1478 error
= got_repo_pack_fds_open(&pack_fds
);
1481 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
1485 error
= apply_unveil(got_repo_get_path_git_dir(repo
), 1);
1489 TAILQ_FOREACH(pe
, &exclude_args
, entry
) {
1491 error
= add_ref(&new, &exclude_refs
, refname
, repo
);
1497 error
= got_ref_list(&include_refs
, repo
, "",
1498 got_ref_cmp_by_name
, NULL
);
1502 for (i
= 0; i
< argc
; i
++) {
1503 got_path_strip_trailing_slashes(argv
[i
]);
1505 error
= add_ref(&new, &include_refs
, refname
, repo
);
1511 /* Ignore references in the refs/got/ namespace. */
1512 TAILQ_FOREACH_SAFE(re
, &include_refs
, entry
, new) {
1513 refname
= got_ref_get_name(re
->ref
);
1514 if (strncmp("refs/got/", refname
, 9) != 0)
1516 TAILQ_REMOVE(&include_refs
, re
, entry
);
1517 got_ref_close(re
->ref
);
1521 memset(&ppa
, 0, sizeof(ppa
));
1523 ppa
.verbosity
= verbosity
;
1525 error
= got_repo_dump(stdout
, &include_refs
, &exclude_refs
,
1526 repo
, pack_progress
, &ppa
, check_cancelled
, NULL
);
1527 if (ppa
.printed_something
)
1528 fprintf(stderr
, "\n");
1531 got_repo_close(repo
);
1534 const struct got_error
*pack_err
;
1536 pack_err
= got_repo_pack_fds_close(pack_fds
);
1541 got_pathlist_free(&exclude_args
, GOT_PATHLIST_FREE_NONE
);
1542 got_ref_list_free(&exclude_refs
);
1543 got_ref_list_free(&include_refs
);
1552 fprintf(stderr
, "usage: %s load [-nq] [-l bundle-file] "
1553 "[-r repository-path] [reference ...]\n",
1558 static const struct got_error
*
1559 load_progress(void *arg
, off_t packfile_size
, int nobj_total
,
1560 int nobj_indexed
, int nobj_loose
, int nobj_resolved
)
1562 return pack_index_progress(arg
, packfile_size
, nobj_total
,
1563 nobj_indexed
, nobj_loose
, nobj_resolved
);
1567 is_wanted_ref(struct got_pathlist_head
*wanted
, const char *ref
)
1569 struct got_pathlist_entry
*pe
;
1571 if (TAILQ_EMPTY(wanted
))
1574 TAILQ_FOREACH(pe
, wanted
, entry
) {
1575 if (strcmp(pe
->path
, ref
) == 0)
1582 static const struct got_error
*
1583 create_ref(const char *refname
, struct got_object_id
*id
,
1584 int verbosity
, struct got_repository
*repo
)
1586 const struct got_error
*err
= NULL
;
1587 struct got_reference
*ref
;
1590 err
= got_object_id_str(&id_str
, id
);
1594 err
= got_ref_alloc(&ref
, refname
, id
);
1598 err
= got_ref_write(ref
, repo
);
1601 if (err
== NULL
&& verbosity
>= 0)
1602 printf("Created reference %s: %s\n", refname
, id_str
);
1608 static const struct got_error
*
1609 update_ref(struct got_reference
*ref
, struct got_object_id
*new_id
,
1610 int replace_tags
, int verbosity
, struct got_repository
*repo
)
1612 const struct got_error
*err
= NULL
;
1613 char *new_id_str
= NULL
;
1614 struct got_object_id
*old_id
= NULL
;
1616 err
= got_object_id_str(&new_id_str
, new_id
);
1620 if (!replace_tags
&&
1621 strncmp(got_ref_get_name(ref
), "refs/tags/", 10) == 0) {
1622 err
= got_ref_resolve(&old_id
, repo
, ref
);
1625 if (got_object_id_cmp(old_id
, new_id
) == 0)
1627 if (verbosity
>= 0) {
1628 printf("Rejecting update of existing tag %s: %s\n",
1629 got_ref_get_name(ref
), new_id_str
);
1634 if (got_ref_is_symbolic(ref
)) {
1635 if (verbosity
>= 0) {
1636 printf("Replacing reference %s: %s\n",
1637 got_ref_get_name(ref
),
1638 got_ref_get_symref_target(ref
));
1640 err
= got_ref_change_symref_to_ref(ref
, new_id
);
1643 err
= got_ref_write(ref
, repo
);
1647 err
= got_ref_resolve(&old_id
, repo
, ref
);
1650 if (got_object_id_cmp(old_id
, new_id
) == 0)
1653 err
= got_ref_change_ref(ref
, new_id
);
1656 err
= got_ref_write(ref
, repo
);
1662 printf("Updated %s: %s\n", got_ref_get_name(ref
),
1670 static const struct got_error
*
1671 cmd_load(int argc
, char *argv
[])
1673 const struct got_error
*error
= NULL
;
1674 struct got_repository
*repo
= NULL
;
1675 struct got_pathlist_head include_args
;
1676 struct got_pathlist_head available_refs
;
1677 struct got_pathlist_entry
*pe
;
1678 struct got_pack_progress_arg ppa
;
1680 int *pack_fds
= NULL
;
1681 char *repo_path
= NULL
;
1682 int list_refs_only
= 0;
1687 TAILQ_INIT(&include_args
);
1688 TAILQ_INIT(&available_refs
);
1691 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
1692 "sendfd unveil", NULL
) == -1)
1696 while ((ch
= getopt(argc
, argv
, "l:nqr:")) != -1) {
1700 in
= fopen(optarg
, "re");
1702 return got_error_from_errno2("open", optarg
);
1711 repo_path
= realpath(optarg
, NULL
);
1712 if (repo_path
== NULL
)
1713 return got_error_from_errno2("realpath",
1715 got_path_strip_trailing_slashes(repo_path
);
1725 if (list_refs_only
&& argc
> 1)
1726 errx(1, "-l and references on the command line are exclusive");
1727 if (list_refs_only
&& noop
)
1728 errx(1, "-n and -l are mutually exclusive");
1730 for (i
= 0; i
< argc
; i
++) {
1731 char *refname
= argv
[i
];
1732 got_path_strip_trailing_slashes(refname
);
1733 if (!got_ref_name_is_valid(refname
))
1734 errx(1, "invalid reference name %s", refname
);
1735 error
= got_pathlist_append(&include_args
, refname
, NULL
);
1740 if (repo_path
== NULL
) {
1741 error
= get_repo_path(&repo_path
);
1745 error
= got_repo_pack_fds_open(&pack_fds
);
1748 error
= got_repo_open(&repo
, repo_path
, NULL
, pack_fds
);
1752 error
= apply_unveil(got_repo_get_path_git_dir(repo
), 0);
1756 memset(&ppa
, 0, sizeof(ppa
));
1758 ppa
.verbosity
= verbosity
;
1760 error
= got_repo_load(in
, &available_refs
, repo
, list_refs_only
, noop
,
1761 load_progress
, &ppa
, check_cancelled
, NULL
);
1762 if (verbosity
>= 0 && !list_refs_only
)
1767 if (list_refs_only
) {
1768 TAILQ_FOREACH(pe
, &available_refs
, entry
) {
1769 const char *refname
= pe
->path
;
1770 struct got_object_id
*id
= pe
->data
;
1773 error
= got_object_id_str(&idstr
, id
);
1777 printf("%s: %s\n", refname
, idstr
);
1786 /* Update references */
1787 TAILQ_FOREACH(pe
, &available_refs
, entry
) {
1788 const struct got_error
*unlock_err
;
1789 struct got_reference
*ref
;
1790 const char *refname
= pe
->path
;
1791 struct got_object_id
*id
= pe
->data
;
1793 if (!is_wanted_ref(&include_args
, pe
->path
))
1796 error
= got_ref_open(&ref
, repo
, refname
, 1);
1798 if (error
->code
!= GOT_ERR_NOT_REF
)
1800 error
= create_ref(refname
, id
, verbosity
, repo
);
1804 /* XXX: check advances only and add -f to force? */
1805 error
= update_ref(ref
, id
, 1, verbosity
, repo
);
1806 unlock_err
= got_ref_unlock(ref
);
1807 if (unlock_err
&& error
== NULL
)
1816 if (in
!= stdin
&& fclose(in
) == EOF
&& error
== NULL
)
1817 error
= got_error_from_errno("fclose");
1820 got_repo_close(repo
);
1823 const struct got_error
*pack_err
;
1825 pack_err
= got_repo_pack_fds_close(pack_fds
);
1830 got_pathlist_free(&include_args
, GOT_PATHLIST_FREE_NONE
);
1831 got_pathlist_free(&available_refs
, GOT_PATHLIST_FREE_ALL
);