commit - e6bcace54ccc965bd93cf6769c7119f21cc1311e
commit + 05118f5ae5dbf7f5e714baec9417e4192659d06a
blob - 920c70117d0c5d2d028f95b8082678537cbf4f45
blob + fa31072bce1bf22d8516963a6d472bf2f45cfea1
--- gotadmin/Makefile
+++ gotadmin/Makefile
SRCS= gotadmin.c \
deflate.c delta.c delta_cache.c deltify.c error.c gotconfig.c \
inflate.c lockfile.c object.c object_cache.c object_create.c \
- object_idset.c object_parse.c opentemp.c pack.c \
- path.c privsep.c reference.c repository.c sha1.c
+ object_idset.c object_parse.c opentemp.c pack.c pack_create.c \
+ path.c privsep.c reference.c repository.c repository_admin.c \
+ sha1.c
MAN = ${PROG}.1
CPPFLAGS = -I${.CURDIR}/../include -I${.CURDIR}/../lib
blob - 61c214f5d923cc2ae9198a585bfa364076f830eb
blob + b87613ae1d71ee82a80a2d3c9bdbf4f980c22963
--- gotadmin/gotadmin.1
+++ gotadmin/gotadmin.1
If not specified, assume the repository is located at or above the current
working directory.
.El
+.It Cm pack Oo Fl a Oc Oo Fl r Ar repository-path Oc Oo Fl x Ar reference Oc Op Ar reference ...
+Generate a new pack file and a corresponding pack file index.
+By default, add any loose objects which are reachable via any references
+to the generated pack file.
+.Pp
+If one or more
+.Ar reference
+arguments is specified, only add objects which are reachable via the specified
+references.
+Each
+.Ar reference
+argument may either specify a specific reference or a reference namespace,
+in which case all references within this namespace will be used.
+.Pp
+.Cm gotadmin pack
+always ignores references in the
+.Pa refs/got/
+namespace, effectively treating such references as if they did not refer
+to any objects.
+.Pp
+The options for
+.Cm gotadmin pack
+are as follows:
+.Bl -tag -width Ds
+.It Fl a
+Add objects to the generated pack file even if they are already packed
+in a different pack file.
+Unless this option is specified, only loose objects will be added.
+.It Fl r Ar repository-path
+Use the repository at the specified path.
+If not specified, assume the repository is located at or above the current
+working directory.
+.It Fl x Ar reference
+Exclude objects reachable via the specified
+.Ar reference
+from the pack file.
+The
+.Ar reference
+argument may either specify a specific reference or a reference namespace,
+in which case all references within this namespace will be excluded.
+The
+.Fl x
+option may be specified multiple times to build a list of references to exclude.
+.Pp
+Exclusion takes precedence over inclusion.
+If a reference appears in both the included and excluded lists, it will
+be excluded.
.El
+.It Cm indexpack Ar packfile-path
+Create a pack index for the pack file at
+.Ar packfile-path .
+.Pp
+A pack index is required for using the corresponding pack file with
+.Xr got 1 .
+Usually, a pack index will be created by commands such as
+.Cm gotadmin pack
+or
+.Cm got fetch
+as part of regular operation.
+The
+.Cm gotadmin indexpack
+command may be used to recover from a corrupt or missing index.
+A given pack file will always yield the same bit-identical index.
+.Pp
+The provided
+.Ar packfile-path
+must be located within the
+.Pa objects/pack/
+directory of the repository and should end in
+.Pa .pack .
+The filename of the corresponding pack index is equivalent, except
+that it ends in
+.Pa .idx .
+.Pp
+.It Cm ix
+Short alias for
+.Cm indexpack .
+.It Cm listpack Oo Fl h Oc Oo Fl s Oc Ar packfile-path
+List the contents of the pack file at
+.Ar packfile-path .
+.Pp
+Each object contained in the pack file will be displayed on a single line.
+The information shown includes the object ID, object type, object offset,
+and object size.
+.Pp
+If a packed object is deltified against another object the delta base
+will be shown as well.
+For offset deltas, the delta base is identified via an offset into the
+pack file.
+For reference deltas, the delta base is identified via an object ID.
+.Pp
+The provided
+.Ar packfile-path
+must be located within the
+.Pa objects/pack/
+directory of the repository and should end in
+.Pa .pack .
+.Pp
+The options for
+.Cm gotadmin listpack
+are as follows:
+.Bl -tag -width Ds
+.It Fl h
+Show object sizes in human-readable form.
+.It Fl s
+Display statistics about the pack file after listing objects.
+This includes the total number of objects stored in the pack file
+and a break-down of the number of objects per object type.
+.El
+.It Cm ls
+Short alias for
+.Cm listpack .
+.El
.Sh EXIT STATUS
.Ex -std gotadmin
.Sh SEE ALSO
.Xr got.conf 5
.Sh AUTHORS
.An Stefan Sperling Aq Mt stsp@openbsd.org
+.An Ori Bernstein Aq Mt ori@openbsd.org
blob - 87e781ed58930ff07563b90ffc8f130eda1d0cfc
blob + 090d85e840f61e0caff5c3956bfd96897183cdc0
--- gotadmin/gotadmin.c
+++ gotadmin/gotadmin.c
*/
#include <sys/queue.h>
+#include <sys/types.h>
+#include <ctype.h>
#include <getopt.h>
#include <err.h>
#include <errno.h>
#include <locale.h>
+#include <inttypes.h>
+#include <sha1.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include "got_error.h"
#include "got_object.h"
#include "got_reference.h"
+#include "got_cancel.h"
#include "got_repository.h"
+#include "got_repository_admin.h"
#include "got_gotconfig.h"
#include "got_path.h"
-#include "got_cancel.h"
#include "got_privsep.h"
#include "got_opentemp.h"
sigpipe_received = 1;
}
+static const struct got_error *
+check_cancelled(void *arg)
+{
+ if (sigint_received || sigpipe_received)
+ return got_error(GOT_ERR_CANCELLED);
+ return NULL;
+}
struct gotadmin_cmd {
const char *cmd_name;
__dead static void usage(int, int);
__dead static void usage_info(void);
+__dead static void usage_pack(void);
+__dead static void usage_indexpack(void);
+__dead static void usage_listpack(void);
static const struct got_error* cmd_info(int, char *[]);
+static const struct got_error* cmd_pack(int, char *[]);
+static const struct got_error* cmd_indexpack(int, char *[]);
+static const struct got_error* cmd_listpack(int, char *[]);
static struct gotadmin_cmd gotadmin_commands[] = {
{ "info", cmd_info, usage_info, "" },
+ { "pack", cmd_pack, usage_pack, "" },
+ { "indexpack", cmd_indexpack, usage_indexpack,"ix" },
+ { "listpack", cmd_listpack, usage_listpack, "ls" },
};
static void
got_repo_close(repo);
free(cwd);
return error;
+}
+
+__dead static void
+usage_pack(void)
+{
+ fprintf(stderr, "usage: %s pack [-a] [-r repository-path] "
+ "[-x reference] [reference ...]\n",
+ getprogname());
+ exit(1);
+}
+
+struct got_pack_progress_arg {
+ char last_scaled_size[FMT_SCALED_STRSIZE];
+ int last_ncommits;
+ int last_nobj_total;
+ int last_p_deltify;
+ int last_p_written;
+ int last_p_indexed;
+ int last_p_resolved;
+ int verbosity;
+ int printed_something;
+};
+
+static const struct got_error *
+pack_progress(void *arg, off_t packfile_size, int ncommits,
+ int nobj_total, int nobj_deltify, int nobj_written)
+{
+ struct got_pack_progress_arg *a = arg;
+ char scaled_size[FMT_SCALED_STRSIZE];
+ int p_deltify, p_written;
+ int print_searching = 0, print_total = 0;
+ int print_deltify = 0, print_written = 0;
+
+ if (a->verbosity < 0)
+ return NULL;
+
+ if (fmt_scaled(packfile_size, scaled_size) == -1)
+ return got_error_from_errno("fmt_scaled");
+
+ if (a->last_ncommits != ncommits) {
+ print_searching = 1;
+ a->last_ncommits = ncommits;
+ }
+
+ if (a->last_nobj_total != nobj_total) {
+ print_searching = 1;
+ print_total = 1;
+ a->last_nobj_total = nobj_total;
+ }
+
+ if (packfile_size > 0 && (a->last_scaled_size[0] == '\0' ||
+ strcmp(scaled_size, a->last_scaled_size)) != 0) {
+ if (strlcpy(a->last_scaled_size, scaled_size,
+ FMT_SCALED_STRSIZE) >= FMT_SCALED_STRSIZE)
+ return got_error(GOT_ERR_NO_SPACE);
+ }
+
+ if (nobj_deltify > 0 || nobj_written > 0) {
+ if (nobj_deltify > 0) {
+ p_deltify = (nobj_deltify * 100) / nobj_total;
+ if (p_deltify != a->last_p_deltify) {
+ a->last_p_deltify = p_deltify;
+ print_searching = 1;
+ print_total = 1;
+ print_deltify = 1;
+ }
+ }
+ if (nobj_written > 0) {
+ p_written = (nobj_written * 100) / nobj_total;
+ if (p_written != a->last_p_written) {
+ a->last_p_written = p_written;
+ print_searching = 1;
+ print_total = 1;
+ print_deltify = 1;
+ print_written = 1;
+ }
+ }
+ }
+
+ if (print_searching || print_total || print_deltify || print_written)
+ printf("\r");
+ if (print_searching)
+ printf("packing %d reference%s", ncommits,
+ ncommits == 1 ? "" : "s");
+ if (print_total)
+ printf("; %d object%s", nobj_total,
+ nobj_total == 1 ? "" : "s");
+ if (print_deltify)
+ printf("; deltify: %d%%", p_deltify);
+ if (print_written)
+ printf("; writing pack: %*s %d%%", FMT_SCALED_STRSIZE,
+ scaled_size, p_written);
+ if (print_searching || print_total || print_deltify ||
+ print_written) {
+ a->printed_something = 1;
+ fflush(stdout);
+ }
+ return NULL;
+}
+
+static const struct got_error *
+pack_index_progress(void *arg, off_t packfile_size, int nobj_total,
+ int nobj_indexed, int nobj_loose, int nobj_resolved)
+{
+ struct got_pack_progress_arg *a = arg;
+ char scaled_size[FMT_SCALED_STRSIZE];
+ int p_indexed, p_resolved;
+ int print_size = 0, print_indexed = 0, print_resolved = 0;
+
+ if (a->verbosity < 0)
+ return NULL;
+
+ if (packfile_size > 0 || nobj_indexed > 0) {
+ if (fmt_scaled(packfile_size, scaled_size) == 0 &&
+ (a->last_scaled_size[0] == '\0' ||
+ strcmp(scaled_size, a->last_scaled_size)) != 0) {
+ print_size = 1;
+ if (strlcpy(a->last_scaled_size, scaled_size,
+ FMT_SCALED_STRSIZE) >= FMT_SCALED_STRSIZE)
+ return got_error(GOT_ERR_NO_SPACE);
+ }
+ if (nobj_indexed > 0) {
+ p_indexed = (nobj_indexed * 100) / nobj_total;
+ if (p_indexed != a->last_p_indexed) {
+ a->last_p_indexed = p_indexed;
+ print_indexed = 1;
+ print_size = 1;
+ }
+ }
+ if (nobj_resolved > 0) {
+ p_resolved = (nobj_resolved * 100) /
+ (nobj_total - nobj_loose);
+ if (p_resolved != a->last_p_resolved) {
+ a->last_p_resolved = p_resolved;
+ print_resolved = 1;
+ print_indexed = 1;
+ print_size = 1;
+ }
+ }
+
+ }
+ if (print_size || print_indexed || print_resolved)
+ printf("\r");
+ if (print_size)
+ printf("%*s packed", FMT_SCALED_STRSIZE, scaled_size);
+ if (print_indexed)
+ printf("; indexing %d%%", p_indexed);
+ if (print_resolved)
+ printf("; resolving deltas %d%%", p_resolved);
+ if (print_size || print_indexed || print_resolved)
+ fflush(stdout);
+
+ return NULL;
}
+
+static const struct got_error *
+add_ref(struct got_reflist_entry **new, struct got_reflist_head *refs,
+ const char *refname, struct got_repository *repo)
+{
+ const struct got_error *err;
+ struct got_reference *ref;
+
+ *new = NULL;
+
+ err = got_ref_open(&ref, repo, refname, 0);
+ if (err) {
+ if (err->code != GOT_ERR_NOT_REF)
+ return err;
+
+ /* Treat argument as a reference prefix. */
+ err = got_ref_list(refs, repo, refname,
+ got_ref_cmp_by_name, NULL);
+ } else {
+ err = got_reflist_insert(new, refs, ref, repo,
+ got_ref_cmp_by_name, NULL);
+ if (err || *new == NULL /* duplicate */)
+ got_ref_close(ref);
+ }
+
+ return err;
+}
+
+static const struct got_error *
+cmd_pack(int argc, char *argv[])
+{
+ const struct got_error *error = NULL;
+ char *cwd = NULL, *repo_path = NULL;
+ struct got_repository *repo = NULL;
+ int ch, i, loose_obj_only = 1;
+ struct got_object_id *pack_hash = NULL;
+ char *id_str = NULL;
+ struct got_pack_progress_arg ppa;
+ FILE *packfile = NULL;
+ struct got_pathlist_head exclude_args;
+ struct got_pathlist_entry *pe;
+ struct got_reflist_head exclude_refs;
+ struct got_reflist_head include_refs;
+ struct got_reflist_entry *re, *new;
+
+ TAILQ_INIT(&exclude_args);
+ TAILQ_INIT(&exclude_refs);
+ TAILQ_INIT(&include_refs);
+
+ while ((ch = getopt(argc, argv, "ar:x:")) != -1) {
+ switch (ch) {
+ case 'a':
+ loose_obj_only = 0;
+ break;
+ case 'r':
+ repo_path = realpath(optarg, NULL);
+ if (repo_path == NULL)
+ return got_error_from_errno2("realpath",
+ optarg);
+ got_path_strip_trailing_slashes(repo_path);
+ break;
+ case 'x':
+ got_path_strip_trailing_slashes(optarg);
+ error = got_pathlist_append(&exclude_args,
+ optarg, NULL);
+ if (error)
+ return error;
+ break;
+ default:
+ usage_pack();
+ /* NOTREACHED */
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+#ifndef PROFILE
+ if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd unveil",
+ NULL) == -1)
+ err(1, "pledge");
+#endif
+ cwd = getcwd(NULL, 0);
+ if (cwd == NULL) {
+ error = got_error_from_errno("getcwd");
+ goto done;
+ }
+
+ error = got_repo_open(&repo, repo_path ? repo_path : cwd, NULL);
+ if (error)
+ goto done;
+
+ error = apply_unveil(got_repo_get_path_git_dir(repo), 1);
+ if (error)
+ goto done;
+
+ TAILQ_FOREACH(pe, &exclude_args, entry) {
+ const char *refname = pe->path;
+ error = add_ref(&new, &exclude_refs, refname, repo);
+ if (error)
+ goto done;
+
+ }
+
+ if (argc == 0) {
+ error = got_ref_list(&include_refs, repo, "",
+ got_ref_cmp_by_name, NULL);
+ if (error)
+ goto done;
+ } else {
+ for (i = 0; i < argc; i++) {
+ const char *refname;
+ got_path_strip_trailing_slashes(argv[i]);
+ refname = argv[i];
+ error = add_ref(&new, &include_refs, refname, repo);
+ if (error)
+ goto done;
+ }
+ }
+
+ /* Ignore references in the refs/got/ namespace. */
+ TAILQ_FOREACH_SAFE(re, &include_refs, entry, new) {
+ const char *refname = got_ref_get_name(re->ref);
+ if (strncmp("refs/got/", refname, 9) != 0)
+ continue;
+ TAILQ_REMOVE(&include_refs, re, entry);
+ got_ref_close(re->ref);
+ free(re);
+ }
+
+ memset(&ppa, 0, sizeof(ppa));
+ ppa.last_scaled_size[0] = '\0';
+ ppa.last_p_indexed = -1;
+ ppa.last_p_resolved = -1;
+
+ error = got_repo_pack_objects(&packfile, &pack_hash,
+ &include_refs, &exclude_refs, repo, loose_obj_only,
+ pack_progress, &ppa, check_cancelled, NULL);
+ if (error) {
+ if (ppa.printed_something)
+ printf("\n");
+ goto done;
+ }
+
+ error = got_object_id_str(&id_str, pack_hash);
+ if (error)
+ goto done;
+ printf("\nWrote %s.pack\n", id_str);
+
+ error = got_repo_index_pack(packfile, pack_hash, repo,
+ pack_index_progress, &ppa, check_cancelled, NULL);
+ if (error)
+ goto done;
+ printf("\nIndexed %s.pack\n", id_str);
+done:
+ got_pathlist_free(&exclude_args);
+ got_ref_list_free(&exclude_refs);
+ got_ref_list_free(&include_refs);
+ free(id_str);
+ free(pack_hash);
+ free(cwd);
+ return error;
+}
+
+__dead static void
+usage_indexpack(void)
+{
+ fprintf(stderr, "usage: %s indexpack packfile-path\n",
+ getprogname());
+ exit(1);
+}
+
+static const struct got_error *
+cmd_indexpack(int argc, char *argv[])
+{
+ const struct got_error *error = NULL;
+ struct got_repository *repo = NULL;
+ int ch;
+ struct got_object_id *pack_hash = NULL;
+ char *packfile_path = NULL;
+ char *id_str = NULL;
+ struct got_pack_progress_arg ppa;
+ FILE *packfile = NULL;
+
+ while ((ch = getopt(argc, argv, "")) != -1) {
+ switch (ch) {
+ default:
+ usage_indexpack();
+ /* NOTREACHED */
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1)
+ usage_indexpack();
+
+ packfile_path = realpath(argv[0], NULL);
+ if (packfile_path == NULL)
+ return got_error_from_errno2("realpath", argv[0]);
+
+#ifndef PROFILE
+ if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd unveil",
+ NULL) == -1)
+ err(1, "pledge");
+#endif
+
+ error = got_repo_open(&repo, packfile_path, NULL);
+ if (error)
+ goto done;
+
+ error = apply_unveil(got_repo_get_path_git_dir(repo), 1);
+ if (error)
+ goto done;
+
+ memset(&ppa, 0, sizeof(ppa));
+ ppa.last_scaled_size[0] = '\0';
+ ppa.last_p_indexed = -1;
+ ppa.last_p_resolved = -1;
+
+ error = got_repo_find_pack(&packfile, &pack_hash, repo,
+ packfile_path);
+ if (error)
+ goto done;
+
+ error = got_object_id_str(&id_str, pack_hash);
+ if (error)
+ goto done;
+
+ error = got_repo_index_pack(packfile, pack_hash, repo,
+ pack_index_progress, &ppa, check_cancelled, NULL);
+ if (error)
+ goto done;
+ printf("\nIndexed %s.pack\n", id_str);
+done:
+ free(id_str);
+ free(pack_hash);
+ return error;
+}
+
+__dead static void
+usage_listpack(void)
+{
+ fprintf(stderr, "usage: %s listpack [-h] [-s] packfile-path\n",
+ getprogname());
+ exit(1);
+}
+
+struct gotadmin_list_pack_cb_args {
+ int nblobs;
+ int ntrees;
+ int ncommits;
+ int ntags;
+ int noffdeltas;
+ int nrefdeltas;
+ int human_readable;
+};
+
+static const struct got_error *
+list_pack_cb(void *arg, struct got_object_id *id, int type, off_t offset,
+ off_t size, off_t base_offset, struct got_object_id *base_id)
+{
+ const struct got_error *err;
+ struct gotadmin_list_pack_cb_args *a = arg;
+ char *id_str, *delta_str = NULL, *base_id_str = NULL;
+ const char *type_str;
+
+ err = got_object_id_str(&id_str, id);
+ if (err)
+ return err;
+
+ switch (type) {
+ case GOT_OBJ_TYPE_BLOB:
+ type_str = GOT_OBJ_LABEL_BLOB;
+ a->nblobs++;
+ break;
+ case GOT_OBJ_TYPE_TREE:
+ type_str = GOT_OBJ_LABEL_TREE;
+ a->ntrees++;
+ break;
+ case GOT_OBJ_TYPE_COMMIT:
+ type_str = GOT_OBJ_LABEL_COMMIT;
+ a->ncommits++;
+ break;
+ case GOT_OBJ_TYPE_TAG:
+ type_str = GOT_OBJ_LABEL_TAG;
+ a->ntags++;
+ break;
+ case GOT_OBJ_TYPE_OFFSET_DELTA:
+ type_str = "offset-delta";
+ if (asprintf(&delta_str, " base-offset %llu",
+ base_offset) == -1) {
+ err = got_error_from_errno("asprintf");
+ goto done;
+ }
+ a->noffdeltas++;
+ break;
+ case GOT_OBJ_TYPE_REF_DELTA:
+ type_str = "ref-delta";
+ err = got_object_id_str(&base_id_str, base_id);
+ if (err)
+ goto done;
+ if (asprintf(&delta_str, " base-id %s", base_id_str) == -1) {
+ err = got_error_from_errno("asprintf");
+ goto done;
+ }
+ a->nrefdeltas++;
+ break;
+ default:
+ err = got_error(GOT_ERR_OBJ_TYPE);
+ goto done;
+ }
+ if (a->human_readable) {
+ char scaled[FMT_SCALED_STRSIZE];
+ char *s;;
+ if (fmt_scaled(size, scaled) == -1) {
+ err = got_error_from_errno("fmt_scaled");
+ goto done;
+ }
+ s = scaled;
+ while (isspace((unsigned char)*s))
+ s++;
+ printf("%s %s at %llu size %s%s\n", id_str, type_str, offset,
+ s, delta_str ? delta_str : "");
+ } else {
+ printf("%s %s at %llu size %llu%s\n", id_str, type_str, offset,
+ size, delta_str ? delta_str : "");
+ }
+done:
+ free(id_str);
+ free(base_id_str);
+ free(delta_str);
+ return err;
+}
+
+static const struct got_error *
+cmd_listpack(int argc, char *argv[])
+{
+ const struct got_error *error = NULL;
+ struct got_repository *repo = NULL;
+ int ch;
+ struct got_object_id *pack_hash = NULL;
+ char *packfile_path = NULL;
+ char *id_str = NULL;
+ struct gotadmin_list_pack_cb_args lpa;
+ FILE *packfile = NULL;
+ int show_stats = 0, human_readable = 0;
+
+ while ((ch = getopt(argc, argv, "hs")) != -1) {
+ switch (ch) {
+ case 'h':
+ human_readable = 1;
+ break;
+ case 's':
+ show_stats = 1;
+ break;
+ default:
+ usage_listpack();
+ /* NOTREACHED */
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1)
+ usage_listpack();
+ packfile_path = realpath(argv[0], NULL);
+ if (packfile_path == NULL)
+ return got_error_from_errno2("realpath", argv[0]);
+
+#ifndef PROFILE
+ if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
+ NULL) == -1)
+ err(1, "pledge");
+#endif
+ error = got_repo_open(&repo, packfile_path, NULL);
+ if (error)
+ goto done;
+
+ error = apply_unveil(got_repo_get_path_git_dir(repo), 1);
+ if (error)
+ goto done;
+
+ error = got_repo_find_pack(&packfile, &pack_hash, repo,
+ packfile_path);
+ if (error)
+ goto done;
+ error = got_object_id_str(&id_str, pack_hash);
+ if (error)
+ goto done;
+
+ memset(&lpa, 0, sizeof(lpa));
+ lpa.human_readable = human_readable;
+ error = got_repo_list_pack(packfile, pack_hash, repo,
+ list_pack_cb, &lpa, check_cancelled, NULL);
+ if (error)
+ goto done;
+ if (show_stats) {
+ printf("objects: %d\n blobs: %d\n trees: %d\n commits: %d\n"
+ " tags: %d\n offset-deltas: %d\n ref-deltas: %d\n",
+ lpa.nblobs + lpa.ntrees + lpa.ncommits + lpa.ntags +
+ lpa.noffdeltas + lpa.nrefdeltas,
+ lpa.nblobs, lpa.ntrees, lpa.ncommits, lpa.ntags,
+ lpa.noffdeltas, lpa.nrefdeltas);
+ }
+done:
+ free(id_str);
+ free(pack_hash);
+ free(packfile_path);
+ return error;
+}
blob - 9244c2c00f8a56e7ef54417632a95c0f7aa36ddd
blob + 851d4ad4128c5bcac0cd5616088fb5063844a83f
--- include/got_error.h
+++ include/got_error.h
#define GOT_ERR_NO_CONFIG_FILE 128
#define GOT_ERR_BAD_SYMLINK 129
#define GOT_ERR_GIT_REPO_EXT 130
+#define GOT_ERR_CANNOT_PACK 131
static const struct got_error {
int code;
{ GOT_ERR_BAD_SYMLINK, "symbolic link points outside of paths under "
"version control" },
{ GOT_ERR_GIT_REPO_EXT, "unsupported repository format extension" },
+ { GOT_ERR_CANNOT_PACK, "not enough objects to pack" },
};
/*
blob - /dev/null
blob + 6c27cd2e90cece2a2487a786135e4cb6b269f1ad (mode 644)
--- /dev/null
+++ include/got_repository_admin.h
+/*
+ * Copyright (c) 2021 Stefan Sperling <stsp@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* A callback function which gets invoked with progress information to print. */
+typedef const struct got_error *(*got_pack_progress_cb)(void *arg,
+ off_t packfile_size, int ncommits, int nobj_total, int obj_deltify,
+ int nobj_written);
+
+/*
+ * Attempt to pack objects reachable via 'include_refs' into a new packfile.
+ * If 'excluded_refs' is not an empty list, do not pack any objects
+ * reachable from the listed references.
+ * If loose_obj_only is zero, pack reachable objects even if they are
+ * already packed in another packfile. Otherwise, add only loose
+ * objects to the new pack file.
+ * Return an open file handle for the generated pack file.
+ * Return the SHA1 digest of the resulting pack file in pack_hash which
+ * must freed by the caller when done.
+ */
+const struct got_error *
+got_repo_pack_objects(FILE **packfile, struct got_object_id **pack_hash,
+ struct got_reflist_head *include_refs,
+ struct got_reflist_head *exclude_refs, struct got_repository *repo,
+ int loose_obj_only,
+ got_pack_progress_cb progress_cb, void *progress_arg,
+ got_cancel_cb cancel_cb, void *cancel_arg);
+
+/*
+ * Attempt to open a pack file at the specified path. Return an open
+ * file handle and the expected hash of pack file contents.
+ */
+const struct got_error *
+got_repo_find_pack(FILE **packfile, struct got_object_id **pack_hash,
+ struct got_repository *repo, const char *packfile_path);
+
+/* A callback function which gets invoked with progress information to print. */
+typedef const struct got_error *(*got_pack_index_progress_cb)(void *arg,
+ off_t packfile_size, int nobj_total, int nobj_indexed,
+ int nobj_loose, int nobj_resolved);
+
+/* (Re-)Index the pack file identified by the given hash. */
+const struct got_error *
+got_repo_index_pack(FILE *packfile, struct got_object_id *pack_hash,
+ struct got_repository *repo,
+ got_pack_index_progress_cb progress_cb, void *progress_arg,
+ got_cancel_cb cancel_cb, void *cancel_arg);
+
+typedef const struct got_error *(*got_pack_list_cb)(void *arg,
+ struct got_object_id *id, int type, off_t offset, off_t size,
+ off_t base_offset, struct got_object_id *base_id);
+
+/* List the pack file identified by the given hash. */
+const struct got_error *
+got_repo_list_pack(FILE *packfile, struct got_object_id *pack_hash,
+ struct got_repository *repo, got_pack_list_cb list_cb, void *list_arg,
+ got_cancel_cb cancel_cb, void *cancel_arg);
blob - cddff333b1f0292034312d89f789fef6b2dcf720
blob + 180adaeec95923b909b3ce9e754eb670b166792d
--- lib/got_lib_pack_create.h
+++ lib/got_lib_pack_create.h
const struct got_error *got_pack_create(uint8_t *pack_sha1, FILE *packfile,
struct got_object_id **theirs, int ntheirs,
struct got_object_id **ours, int nours,
- struct got_repository *repo, got_cancel_cb cancel_cb, void *cancel_arg);
+ struct got_repository *repo, int loose_obj_only,
+ got_pack_progress_cb progress_cb, void *progress_arg,
+ got_cancel_cb cancel_cb, void *cancel_arg);
blob - 5d3b9a7d345565cc8c91eb8cf92b750344d2998c
blob + 687269a4a6ab8472e6491b4ac5485ba24c54934c
--- lib/pack_create.c
+++ lib/pack_create.c
#include <sys/stat.h>
#include <inttypes.h>
+#include <imsg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "got_error.h"
#include "got_cancel.h"
#include "got_object.h"
+#include "got_reference.h"
+#include "got_repository_admin.h"
#include "got_lib_deltify.h"
#include "got_lib_delta.h"
#include "got_lib_object.h"
#include "got_lib_object_idset.h"
+#include "got_lib_object_cache.h"
#include "got_lib_deflate.h"
#include "got_lib_pack.h"
+#include "got_lib_privsep.h"
+#include "got_lib_repository.h"
#ifndef MAX
#define MAX(_a,_b) ((_a) > (_b) ? (_a) : (_b))
}
static int
-showprogress(int x, int pct)
-{
- if(x > pct){
- pct = x;
- fprintf(stderr, "\b\b\b\b%3d%%", pct);
- }
- return pct;
-}
-
-static int
delta_size(struct got_delta_instruction *deltas, int ndeltas)
{
int i, size = 32;
static const struct got_error *
-pick_deltas(struct got_pack_meta **meta, int nmeta, struct got_repository *repo,
+pick_deltas(struct got_pack_meta **meta, int nmeta, int nours,
+ struct got_repository *repo,
+ got_pack_progress_cb progress_cb, void *progress_arg,
got_cancel_cb cancel_cb, void *cancel_arg)
{
const struct got_error *err = NULL;
struct got_pack_meta *m = NULL, *base = NULL;
struct got_raw_object *raw = NULL, *base_raw = NULL;
struct got_delta_instruction *deltas;
- int i, j, size, ndeltas, pct, best;
+ int i, j, size, ndeltas, best;
const int max_base_candidates = 10;
- pct = 0;
- fprintf(stderr, "picking deltas\n");
- fprintf(stderr, "deltifying %d objects: 0%%", nmeta);
qsort(meta, nmeta, sizeof(struct got_pack_meta *), delta_order_cmp);
for (i = 0; i < nmeta; i++) {
if (cancel_cb) {
if (err)
break;
}
-
+ if (progress_cb) {
+ err = progress_cb(progress_arg, 0L, nours, nmeta, i, 0);
+ if (err)
+ goto done;
+ }
m = meta[i];
- pct = showprogress((i*100) / nmeta, pct);
m->deltas = NULL;
m->ndeltas = 0;
got_object_raw_close(raw);
raw = NULL;
}
-
- fprintf(stderr, "\b\b\b\b100%%\n");
done:
for (i = MAX(0, nmeta - max_base_candidates); i < nmeta; i++) {
got_deltify_free(meta[i]->dtab);
}
static const struct got_error *
+search_packidx(int *found, struct got_object_id *id,
+ struct got_repository *repo)
+{
+ const struct got_error *err = NULL;
+ struct got_packidx *packidx = NULL;
+ int idx;
+
+ *found = 0;
+
+ err = got_repo_search_packidx(&packidx, &idx, repo, id);
+ if (err == NULL)
+ *found = 1; /* object is already packed */
+ else if (err->code == GOT_ERR_NO_OBJ)
+ err = NULL;
+ return err;
+}
+
+static const struct got_error *
add_meta(struct got_pack_metavec *v, struct got_object_idset *idset,
struct got_object_id *id, const char *path, int obj_type,
- time_t mtime)
+ time_t mtime, int loose_obj_only, struct got_repository *repo)
{
const struct got_error *err;
struct got_pack_meta *m;
+ if (loose_obj_only) {
+ int is_packed;
+ err = search_packidx(&is_packed, id, repo);
+ if (err)
+ return err;
+ if (is_packed)
+ return NULL;
+ }
+
err = got_object_idset_add(idset, id, NULL);
if (err)
return err;
load_tree_entries(struct got_object_id_queue *ids, struct got_pack_metavec *v,
struct got_object_idset *idset, struct got_object_id *tree_id,
const char *dpath, time_t mtime, struct got_repository *repo,
- got_cancel_cb cancel_cb, void *cancel_arg)
+ int loose_obj_only, got_cancel_cb cancel_cb, void *cancel_arg)
{
const struct got_error *err;
struct got_tree_object *tree;
SIMPLEQ_INSERT_TAIL(ids, qid, entry);
} else if (S_ISREG(mode)) {
err = add_meta(v, idset, id, p, GOT_OBJ_TYPE_BLOB,
- mtime);
+ mtime, loose_obj_only, repo);
if (err)
break;
}
static const struct got_error *
load_tree(struct got_pack_metavec *v, struct got_object_idset *idset,
struct got_object_id *tree_id, const char *dpath, time_t mtime,
- struct got_repository *repo, got_cancel_cb cancel_cb, void *cancel_arg)
+ int loose_obj_only, struct got_repository *repo,
+ got_cancel_cb cancel_cb, void *cancel_arg)
{
const struct got_error *err = NULL;
struct got_object_id_queue tree_ids;
}
err = add_meta(v, idset, qid->id, dpath, GOT_OBJ_TYPE_TREE,
- mtime);
+ mtime, loose_obj_only, repo);
if (err) {
got_object_qid_free(qid);
break;
}
err = load_tree_entries(&tree_ids, v, idset, qid->id, dpath,
- mtime, repo, cancel_cb, cancel_arg);
+ mtime, repo, loose_obj_only, cancel_cb, cancel_arg);
got_object_qid_free(qid);
if (err)
break;
static const struct got_error *
load_commit(struct got_pack_metavec *v, struct got_object_idset *idset,
- struct got_object_id *id, struct got_repository *repo,
+ struct got_object_id *id, struct got_repository *repo, int loose_obj_only,
got_cancel_cb cancel_cb, void *cancel_arg)
{
const struct got_error *err;
if (got_object_idset_contains(idset, id))
return NULL;
+
+ if (loose_obj_only) {
+ int is_packed;
+ err = search_packidx(&is_packed, id, repo);
+ if (err)
+ return err;
+ if (is_packed)
+ return NULL;
+ }
err = got_object_open_as_commit(&commit, repo, id);
if (err)
return err;
err = add_meta(v, idset, id, "", GOT_OBJ_TYPE_COMMIT,
- got_object_commit_get_committer_time(commit));
+ got_object_commit_get_committer_time(commit),
+ loose_obj_only, repo);
if (err)
goto done;
err = load_tree(v, idset, got_object_commit_get_tree_id(commit),
- "", got_object_commit_get_committer_time(commit), repo,
- cancel_cb, cancel_arg);
+ "", got_object_commit_get_committer_time(commit),
+ loose_obj_only, repo, cancel_cb, cancel_arg);
done:
got_object_commit_close(commit);
return err;
}
+static const struct got_error *
+load_tag(struct got_pack_metavec *v, struct got_object_idset *idset,
+ struct got_object_id *id, struct got_repository *repo, int loose_obj_only,
+ got_cancel_cb cancel_cb, void *cancel_arg)
+{
+ const struct got_error *err;
+ struct got_tag_object *tag = NULL;
+
+ if (got_object_idset_contains(idset, id))
+ return NULL;
+
+ if (loose_obj_only) {
+ int is_packed;
+ err = search_packidx(&is_packed, id, repo);
+ if (err)
+ return err;
+ if (is_packed)
+ return NULL;
+ }
+
+ err = got_object_open_as_tag(&tag, repo, id);
+ if (err)
+ return err;
+
+ err = add_meta(v, idset, id, "", GOT_OBJ_TYPE_TAG,
+ got_object_tag_get_tagger_time(tag),
+ loose_obj_only, repo);
+ if (err)
+ goto done;
+
+ switch (got_object_tag_get_object_type(tag)) {
+ case GOT_OBJ_TYPE_COMMIT:
+ err = load_commit(NULL, idset,
+ got_object_tag_get_object_id(tag), repo,
+ loose_obj_only, cancel_cb, cancel_arg);
+ break;
+ case GOT_OBJ_TYPE_TREE:
+ err = load_tree(v, idset, got_object_tag_get_object_id(tag),
+ "", got_object_tag_get_tagger_time(tag),
+ loose_obj_only, repo, cancel_cb, cancel_arg);
+ break;
+ default:
+ break;
+ }
+
+done:
+ got_object_tag_close(tag);
+ return err;
+}
+
enum findtwixt_color {
COLOR_KEEP = 0,
COLOR_DROP,
{
const struct got_error *err;
struct got_object_qid *qid;
- char *id_str;
- int obj_type;
- err = got_object_get_type(&obj_type, repo, id);
- if (err)
- return err;
-
- if (obj_type != GOT_OBJ_TYPE_COMMIT) {
- err = got_object_id_str(&id_str, id);
- if (err)
- return err;
- err = got_error_fmt(GOT_ERR_OBJ_TYPE,
- "%s is not a commit", id_str);
- free(id_str);
- return err;
- }
err = got_object_qid_alloc(&qid, id);
if (err)
return err;
struct got_object_id_queue ids;
struct got_object_idset *keep, *drop;
struct got_object_qid *qid;
- int i, ncolor, nkeep;
+ int i, ncolor, nkeep, obj_type;
SIMPLEQ_INIT(&ids);
*res = NULL;
goto done;
}
- for (i = 0; i < nhead; i++){
- if (head[i]) {
- err = queue_commit_id(&ids, head[i], COLOR_KEEP, repo);
- if (err)
- goto done;
- }
+ for (i = 0; i < nhead; i++) {
+ struct got_object_id *id = head[i];
+ if (id == NULL)
+ continue;
+ err = got_object_get_type(&obj_type, repo, id);
+ if (err)
+ return err;
+ if (obj_type != GOT_OBJ_TYPE_COMMIT)
+ continue;
+ err = queue_commit_id(&ids, id, COLOR_KEEP, repo);
+ if (err)
+ goto done;
}
- for (i = 0; i < ntail; i++){
- if (tail[i]) {
- err = queue_commit_id(&ids, tail[i], COLOR_DROP, repo);
- if (err)
- goto done;
- }
+ for (i = 0; i < ntail; i++) {
+ struct got_object_id *id = tail[i];
+ if (id == NULL)
+ continue;
+ err = got_object_get_type(&obj_type, repo, id);
+ if (err)
+ return err;
+ if (obj_type != GOT_OBJ_TYPE_COMMIT)
+ continue;
+ err = queue_commit_id(&ids, id, COLOR_DROP, repo);
+ if (err)
+ goto done;
}
while (!SIMPLEQ_EMPTY(&ids)) {
read_meta(struct got_pack_meta ***meta, int *nmeta,
struct got_object_id **theirs, int ntheirs,
struct got_object_id **ours, int nours, struct got_repository *repo,
+ int loose_obj_only, got_pack_progress_cb progress_cb, void *progress_arg,
got_cancel_cb cancel_cb, void *cancel_arg)
{
const struct got_error *err = NULL;
struct got_object_id **ids = NULL;
struct got_object_idset *idset;
- int i, nobj = 0;
+ int i, nobj = 0, obj_type;
struct got_pack_metavec v;
*meta = NULL;
goto done;
for (i = 0; i < ntheirs; i++) {
- if (theirs[i] != NULL) {
- err = load_commit(NULL, idset, theirs[i], repo,
- cancel_cb, cancel_arg);
+ struct got_object_id *id = theirs[i];
+ if (id == NULL)
+ continue;
+ err = got_object_get_type(&obj_type, repo, id);
+ if (err)
+ return err;
+ if (obj_type != GOT_OBJ_TYPE_COMMIT)
+ continue;
+ err = load_commit(NULL, idset, id, repo,
+ loose_obj_only, cancel_cb, cancel_arg);
+ if (err)
+ goto done;
+ if (progress_cb) {
+ err = progress_cb(progress_arg, 0L, nours,
+ v.nmeta, 0, 0);
if (err)
goto done;
}
for (i = 0; i < nobj; i++) {
err = load_commit(&v, idset, ids[i], repo,
- cancel_cb, cancel_arg);
+ loose_obj_only, cancel_cb, cancel_arg);
+ if (err)
+ goto done;
+ if (progress_cb) {
+ err = progress_cb(progress_arg, 0L, nours,
+ v.nmeta, 0, 0);
+ if (err)
+ goto done;
+ }
+ }
+
+ for (i = 0; i < ntheirs; i++) {
+ struct got_object_id *id = ours[i];
+ if (id == NULL)
+ continue;
+ err = got_object_get_type(&obj_type, repo, id);
+ if (err)
+ return err;
+ if (obj_type != GOT_OBJ_TYPE_TAG)
+ continue;
+ err = load_tag(NULL, idset, id, repo,
+ loose_obj_only, cancel_cb, cancel_arg);
+ if (err)
+ goto done;
+ if (progress_cb) {
+ err = progress_cb(progress_arg, 0L, nours,
+ v.nmeta, 0, 0);
+ if (err)
+ goto done;
+ }
+ }
+
+ for (i = 0; i < nours; i++) {
+ struct got_object_id *id = ours[i];
+ if (id == NULL)
+ continue;
+ err = got_object_get_type(&obj_type, repo, id);
if (err)
+ return err;
+ if (obj_type != GOT_OBJ_TYPE_TAG)
+ continue;
+ err = load_tag(&v, idset, id, repo,
+ loose_obj_only, cancel_cb, cancel_arg);
+ if (err)
goto done;
+ if (progress_cb) {
+ err = progress_cb(progress_arg, 0L, nours,
+ v.nmeta, 0, 0);
+ if (err)
+ goto done;
+ }
}
+
done:
for (i = 0; i < nobj; i++) {
free(ids[i]);
static const struct got_error *
genpack(uint8_t *pack_sha1, FILE *packfile,
- struct got_pack_meta **meta, int nmeta, int use_offset_deltas,
- struct got_repository *repo)
+ struct got_pack_meta **meta, int nmeta, int nours,
+ int use_offset_deltas, struct got_repository *repo,
+ got_pack_progress_cb progress_cb, void *progress_arg,
+ got_cancel_cb cancel_cb, void *cancel_arg)
{
const struct got_error *err = NULL;
- int i, nh, nd, pct;
+ int i, nh, nd;
SHA1_CTX ctx;
struct got_pack_meta *m;
struct got_raw_object *raw;
char *p = NULL, buf[32];
size_t outlen, n;
struct got_deflate_checksum csum;
+ off_t packfile_size = 0;
SHA1Init(&ctx);
csum.output_sha1 = &ctx;
csum.output_crc = NULL;
- pct = 0;
- fprintf(stderr, "generating pack\n");
-
err = hwrite(packfile, "PACK", 4, &ctx);
if (err)
return err;
if (err)
goto done;
qsort(meta, nmeta, sizeof(struct got_pack_meta *), write_order_cmp);
- fprintf(stderr, "writing %d objects: 0%%", nmeta);
- for (i = 0; i < nmeta; i++) {
- pct = showprogress((i*100)/nmeta, pct);
+ for (i = 0; i < nmeta; i++) {
+ if (progress_cb) {
+ err = progress_cb(progress_arg, packfile_size, nours,
+ nmeta, nmeta, i);
+ if (err)
+ goto done;
+ }
m = meta[i];
m->off = ftello(packfile);
err = got_object_raw_open(&raw, repo, &m->id, 8192);
err = hwrite(packfile, buf, nh, &ctx);
if (err)
goto done;
+ packfile_size += nh;
if (fseeko(raw->f, raw->hdrlen, SEEK_SET) == -1) {
err = got_error_from_errno("fseeko");
goto done;
&csum);
if (err)
goto done;
+ packfile_size += outlen;
} else {
FILE *delta_file;
struct got_raw_object *base_raw;
err = hwrite(packfile, buf, nh, &ctx);
if (err)
goto done;
+ packfile_size += nh;
} else {
err = packhdr(&nh, buf, sizeof(buf),
GOT_OBJ_TYPE_REF_DELTA, nd);
err = hwrite(packfile, buf, nh, &ctx);
if (err)
goto done;
+ packfile_size += nh;
err = hwrite(packfile, m->prev->id.sha1,
sizeof(m->prev->id.sha1), &ctx);
+ packfile_size += sizeof(m->prev->id.sha1);
if (err)
goto done;
}
fclose(delta_file);
if (err)
goto done;
+ packfile_size += outlen;
free(p);
p = NULL;
}
got_object_raw_close(raw);
raw = NULL;
}
- fprintf(stderr, "\b\b\b\b100%%\n");
SHA1Final(pack_sha1, &ctx);
n = fwrite(pack_sha1, 1, SHA1_DIGEST_LENGTH, packfile);
if (n != SHA1_DIGEST_LENGTH)
err = got_ferror(packfile, GOT_ERR_IO);
+ packfile_size += SHA1_DIGEST_LENGTH;
+ packfile_size += 16; /* pack file header */
+ err = progress_cb(progress_arg, packfile_size, nours,
+ nmeta, nmeta, nmeta);
+ if (err)
+ goto done;
done:
free(p);
return err;
got_pack_create(uint8_t *packsha1, FILE *packfile,
struct got_object_id **theirs, int ntheirs,
struct got_object_id **ours, int nours,
- struct got_repository *repo, got_cancel_cb cancel_cb, void *cancel_arg)
+ struct got_repository *repo, int loose_obj_only,
+ got_pack_progress_cb progress_cb, void *progress_arg,
+ got_cancel_cb cancel_cb, void *cancel_arg)
{
const struct got_error *err;
struct got_pack_meta **meta;
int nmeta;
err = read_meta(&meta, &nmeta, theirs, ntheirs, ours, nours, repo,
- cancel_cb, cancel_arg);
+ loose_obj_only, progress_cb, progress_arg, cancel_cb, cancel_arg);
if (err)
return err;
- err = pick_deltas(meta, nmeta, repo, cancel_cb, cancel_arg);
+ if (nmeta == 0) {
+ err = got_error(GOT_ERR_CANNOT_PACK);
+ goto done;
+ }
+
+ err = pick_deltas(meta, nmeta, nours, repo,
+ progress_cb, progress_arg, cancel_cb, cancel_arg);
if (err)
goto done;
- err = genpack(packsha1, packfile, meta, nmeta, 1, repo);
+ err = genpack(packsha1, packfile, meta, nmeta, nours, 1, repo,
+ progress_cb, progress_arg, cancel_cb, cancel_arg);
if (err)
goto done;
done:
blob - /dev/null
blob + 95151516f8146f021c5f8f3a919b810499c016e3 (mode 644)
--- /dev/null
+++ lib/repository_admin.c
+/*
+ * Copyright (c) 2020 Stefan Sperling <stsp@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <sha1.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <unistd.h>
+#include <imsg.h>
+
+#include "got_error.h"
+#include "got_cancel.h"
+#include "got_object.h"
+#include "got_reference.h"
+#include "got_repository.h"
+#include "got_repository_admin.h"
+#include "got_opentemp.h"
+#include "got_path.h"
+
+#include "got_lib_delta.h"
+#include "got_lib_object.h"
+#include "got_lib_object_cache.h"
+#include "got_lib_pack.h"
+#include "got_lib_privsep.h"
+#include "got_lib_repository.h"
+#include "got_lib_pack_create.h"
+#include "got_lib_sha1.h"
+
+#ifndef nitems
+#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
+#endif
+
+static const struct got_error *
+get_reflist_object_ids(struct got_object_id ***ids, int *nobjects,
+ unsigned int wanted_obj_type_mask, struct got_reflist_head *refs,
+ struct got_repository *repo,
+ got_cancel_cb cancel_cb, void *cancel_arg)
+{
+ const struct got_error *err = NULL;
+ const size_t alloc_chunksz = 256;
+ size_t nalloc;
+ struct got_reflist_entry *re;
+ int i;
+
+ *ids = NULL;
+ *nobjects = 0;
+
+ *ids = reallocarray(NULL, alloc_chunksz, sizeof(struct got_object_id *));
+ if (*ids == NULL)
+ return got_error_from_errno("reallocarray");
+ nalloc = alloc_chunksz;
+
+ TAILQ_FOREACH(re, refs, entry) {
+ struct got_object_id *id;
+
+ if (cancel_cb) {
+ err = cancel_cb(cancel_arg);
+ if (err)
+ goto done;
+ }
+
+ err = got_ref_resolve(&id, repo, re->ref);
+ if (err)
+ goto done;
+
+ if (wanted_obj_type_mask != GOT_OBJ_TYPE_ANY) {
+ int obj_type;
+ err = got_object_get_type(&obj_type, repo, id);
+ if (err)
+ goto done;
+ if ((wanted_obj_type_mask & (1 << obj_type)) == 0) {
+ free(id);
+ id = NULL;
+ continue;
+ }
+ }
+
+ if (nalloc >= *nobjects) {
+ struct got_object_id **new;
+ new = recallocarray(*ids, nalloc,
+ nalloc + alloc_chunksz,
+ sizeof(struct got_object_id *));
+ if (new == NULL) {
+ err = got_error_from_errno(
+ "recallocarray");
+ goto done;
+ }
+ *ids = new;
+ nalloc += alloc_chunksz;
+ }
+ (*ids)[*nobjects] = id;
+ if ((*ids)[*nobjects] == NULL) {
+ err = got_error_from_errno("got_object_id_dup");
+ goto done;
+ }
+ (*nobjects)++;
+ }
+done:
+ if (err) {
+ for (i = 0; i < *nobjects; i++)
+ free((*ids)[i]);
+ free(*ids);
+ *ids = NULL;
+ *nobjects = 0;
+ }
+ return err;
+}
+
+const struct got_error *
+got_repo_pack_objects(FILE **packfile, struct got_object_id **pack_hash,
+ struct got_reflist_head *include_refs,
+ struct got_reflist_head *exclude_refs, struct got_repository *repo,
+ int loose_obj_only, got_pack_progress_cb progress_cb, void *progress_arg,
+ got_cancel_cb cancel_cb, void *cancel_arg)
+{
+ const struct got_error *err = NULL;
+ struct got_object_id **ours = NULL, **theirs = NULL;
+ int nours = 0, ntheirs = 0, packfd = -1, i;
+ char *tmpfile_path = NULL, *path = NULL, *packfile_path = NULL;
+ char *sha1_str = NULL;
+
+ *packfile = NULL;
+ *pack_hash = NULL;
+
+ if (asprintf(&path, "%s/%s/packing.pack",
+ got_repo_get_path_git_dir(repo), GOT_OBJECTS_PACK_DIR) == -1) {
+ err = got_error_from_errno("asprintf");
+ goto done;
+ }
+ err = got_opentemp_named_fd(&tmpfile_path, &packfd, path);
+ if (err)
+ goto done;
+
+ if (fchmod(packfd, GOT_DEFAULT_FILE_MODE) != 0) {
+ err = got_error_from_errno2("fchmod", tmpfile_path);
+ goto done;
+ }
+
+ *packfile = fdopen(packfd, "w");
+ if (*packfile == NULL) {
+ err = got_error_from_errno2("fdopen", tmpfile_path);
+ goto done;
+ }
+ packfd = -1;
+
+ err = get_reflist_object_ids(&ours, &nours,
+ (1 << GOT_OBJ_TYPE_COMMIT) | (1 << GOT_OBJ_TYPE_TAG),
+ include_refs, repo, cancel_cb, cancel_arg);
+ if (err)
+ goto done;
+
+ if (nours == 0) {
+ err = got_error(GOT_ERR_CANNOT_PACK);
+ goto done;
+ }
+
+ if (!TAILQ_EMPTY(exclude_refs)) {
+ err = get_reflist_object_ids(&theirs, &ntheirs,
+ (1 << GOT_OBJ_TYPE_COMMIT) | (1 << GOT_OBJ_TYPE_TAG),
+ exclude_refs, repo,
+ cancel_cb, cancel_arg);
+ if (err)
+ goto done;
+ }
+
+ *pack_hash = calloc(1, sizeof(**pack_hash));
+ if (*pack_hash == NULL) {
+ err = got_error_from_errno("calloc");
+ goto done;
+ }
+
+ err = got_pack_create((*pack_hash)->sha1, *packfile, theirs, ntheirs,
+ ours, nours, repo, loose_obj_only, progress_cb, progress_arg,
+ cancel_cb, cancel_arg);
+ if (err)
+ goto done;
+
+ err = got_object_id_str(&sha1_str, *pack_hash);
+ if (err)
+ goto done;
+ if (asprintf(&packfile_path, "%s/%s/pack-%s.pack",
+ got_repo_get_path_git_dir(repo), GOT_OBJECTS_PACK_DIR,
+ sha1_str) == -1) {
+ err = got_error_from_errno("asprintf");
+ goto done;
+ }
+
+ if (fflush(*packfile) == -1) {
+ err = got_error_from_errno("fflush");
+ goto done;
+ }
+ if (fseek(*packfile, 0L, SEEK_SET) == -1) {
+ err = got_error_from_errno("fseek");
+ goto done;
+ }
+ if (rename(tmpfile_path, packfile_path) == -1) {
+ err = got_error_from_errno3("rename", tmpfile_path,
+ packfile_path);
+ goto done;
+ }
+ free(tmpfile_path);
+ tmpfile_path = NULL;
+done:
+ for (i = 0; i < nours; i++)
+ free(ours[i]);
+ free(ours);
+ for (i = 0; i < ntheirs; i++)
+ free(theirs[i]);
+ free(theirs);
+ if (packfd != -1 && close(packfd) == -1 && err == NULL)
+ err = got_error_from_errno2("close", packfile_path);
+ if (tmpfile_path && unlink(tmpfile_path) == -1 && err == NULL)
+ err = got_error_from_errno2("unlink", tmpfile_path);
+ free(tmpfile_path);
+ free(packfile_path);
+ free(sha1_str);
+ free(path);
+ if (err) {
+ free(*pack_hash);
+ *pack_hash = NULL;
+ if (*packfile)
+ fclose(*packfile);
+ *packfile = NULL;
+ }
+ return err;
+}
+
+const struct got_error *
+got_repo_index_pack(FILE *packfile, struct got_object_id *pack_hash,
+ struct got_repository *repo,
+ got_pack_index_progress_cb progress_cb, void *progress_arg,
+ got_cancel_cb cancel_cb, void *cancel_arg)
+{
+ size_t i;
+ char *path;
+ int imsg_idxfds[2];
+ int npackfd = -1, idxfd = -1, nidxfd = -1;
+ int tmpfds[3];
+ int idxstatus, done = 0;
+ const struct got_error *err;
+ struct imsgbuf idxibuf;
+ pid_t idxpid;
+ char *tmpidxpath = NULL;
+ char *packfile_path = NULL, *idxpath = NULL, *id_str = NULL;
+ const char *repo_path = got_repo_get_path_git_dir(repo);
+ struct stat sb;
+
+ for (i = 0; i < nitems(tmpfds); i++)
+ tmpfds[i] = -1;
+
+ if (asprintf(&path, "%s/%s/indexing.idx",
+ repo_path, GOT_OBJECTS_PACK_DIR) == -1) {
+ err = got_error_from_errno("asprintf");
+ goto done;
+ }
+ err = got_opentemp_named_fd(&tmpidxpath, &idxfd, path);
+ free(path);
+ if (err)
+ goto done;
+ if (fchmod(idxfd, GOT_DEFAULT_FILE_MODE) != 0) {
+ err = got_error_from_errno2("fchmod", tmpidxpath);
+ goto done;
+ }
+
+ nidxfd = dup(idxfd);
+ if (nidxfd == -1) {
+ err = got_error_from_errno("dup");
+ goto done;
+ }
+
+ for (i = 0; i < nitems(tmpfds); i++) {
+ tmpfds[i] = got_opentempfd();
+ if (tmpfds[i] == -1) {
+ err = got_error_from_errno("got_opentempfd");
+ goto done;
+ }
+ }
+
+ err = got_object_id_str(&id_str, pack_hash);
+ if (err)
+ goto done;
+
+ if (asprintf(&packfile_path, "%s/%s/pack-%s.pack",
+ repo_path, GOT_OBJECTS_PACK_DIR, id_str) == -1) {
+ err = got_error_from_errno("asprintf");
+ goto done;
+ }
+
+ if (fstat(fileno(packfile), &sb) == -1) {
+ err = got_error_from_errno2("fstat", packfile_path);
+ goto done;
+ }
+
+ if (asprintf(&idxpath, "%s/%s/pack-%s.idx",
+ repo_path, GOT_OBJECTS_PACK_DIR, id_str) == -1) {
+ err = got_error_from_errno("asprintf");
+ goto done;
+ }
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, imsg_idxfds) == -1) {
+ err = got_error_from_errno("socketpair");
+ goto done;
+ }
+ idxpid = fork();
+ if (idxpid == -1) {
+ err= got_error_from_errno("fork");
+ goto done;
+ } else if (idxpid == 0)
+ got_privsep_exec_child(imsg_idxfds,
+ GOT_PATH_PROG_INDEX_PACK, packfile_path);
+ if (close(imsg_idxfds[1]) == -1) {
+ err = got_error_from_errno("close");
+ goto done;
+ }
+ imsg_init(&idxibuf, imsg_idxfds[0]);
+
+ npackfd = dup(fileno(packfile));
+ if (npackfd == -1) {
+ err = got_error_from_errno("dup");
+ goto done;
+ }
+ err = got_privsep_send_index_pack_req(&idxibuf, pack_hash->sha1,
+ npackfd);
+ if (err != NULL)
+ goto done;
+ npackfd = -1;
+ err = got_privsep_send_index_pack_outfd(&idxibuf, nidxfd);
+ if (err != NULL)
+ goto done;
+ nidxfd = -1;
+ for (i = 0; i < nitems(tmpfds); i++) {
+ err = got_privsep_send_tmpfd(&idxibuf, tmpfds[i]);
+ if (err != NULL)
+ goto done;
+ tmpfds[i] = -1;
+ }
+ done = 0;
+ while (!done) {
+ int nobj_total, nobj_indexed, nobj_loose, nobj_resolved;
+
+ if (cancel_cb) {
+ err = cancel_cb(cancel_arg);
+ if (err)
+ goto done;
+ }
+
+ err = got_privsep_recv_index_progress(&done, &nobj_total,
+ &nobj_indexed, &nobj_loose, &nobj_resolved,
+ &idxibuf);
+ if (err != NULL)
+ goto done;
+ if (nobj_indexed != 0) {
+ err = progress_cb(progress_arg, sb.st_size,
+ nobj_total, nobj_indexed, nobj_loose,
+ nobj_resolved);
+ if (err)
+ break;
+ }
+ imsg_clear(&idxibuf);
+ }
+ if (close(imsg_idxfds[0]) == -1) {
+ err = got_error_from_errno("close");
+ goto done;
+ }
+ if (waitpid(idxpid, &idxstatus, 0) == -1) {
+ err = got_error_from_errno("waitpid");
+ goto done;
+ }
+
+ if (rename(tmpidxpath, idxpath) == -1) {
+ err = got_error_from_errno3("rename", tmpidxpath, idxpath);
+ goto done;
+ }
+ free(tmpidxpath);
+ tmpidxpath = NULL;
+
+done:
+ if (tmpidxpath && unlink(tmpidxpath) == -1 && err == NULL)
+ err = got_error_from_errno2("unlink", tmpidxpath);
+ if (npackfd != -1 && close(npackfd) == -1 && err == NULL)
+ err = got_error_from_errno("close");
+ if (idxfd != -1 && close(idxfd) == -1 && err == NULL)
+ err = got_error_from_errno("close");
+ for (i = 0; i < nitems(tmpfds); i++) {
+ if (tmpfds[i] != -1 && close(tmpfds[i]) == -1 && err == NULL)
+ err = got_error_from_errno("close");
+ }
+ free(tmpidxpath);
+ free(idxpath);
+ free(packfile_path);
+ return err;
+}
+
+const struct got_error *
+got_repo_find_pack(FILE **packfile, struct got_object_id **pack_hash,
+ struct got_repository *repo, const char *packfile_path)
+{
+ const struct got_error *err = NULL;
+ const char *packdir_path = NULL;
+ char *packfile_name = NULL, *p, *dot;
+ struct got_object_id id;
+ int packfd = -1;
+
+ *packfile = NULL;
+ *pack_hash = NULL;
+
+ packdir_path = got_repo_get_path_objects_pack(repo);
+ if (packdir_path == NULL)
+ return got_error_from_errno("got_repo_get_path_objects_pack");
+
+ if (!got_path_is_child(packfile_path, packdir_path,
+ strlen(packdir_path))) {
+ err = got_error_path(packfile_path, GOT_ERR_BAD_PATH);
+ goto done;
+
+ }
+
+ err = got_path_basename(&packfile_name, packfile_path);
+ if (err)
+ goto done;
+ p = packfile_name;
+
+ if (strncmp(p, "pack-", 5) != 0) {
+ err = got_error_fmt(GOT_ERR_BAD_PATH,
+ "'%s' is not a valid pack file name",
+ packfile_name);
+ goto done;
+ }
+ p += 5;
+ dot = strchr(p, '.');
+ if (dot == NULL) {
+ err = got_error_fmt(GOT_ERR_BAD_PATH,
+ "'%s' is not a valid pack file name",
+ packfile_name);
+ goto done;
+ }
+ if (strcmp(dot + 1, "pack") != 0) {
+ err = got_error_fmt(GOT_ERR_BAD_PATH,
+ "'%s' is not a valid pack file name",
+ packfile_name);
+ goto done;
+ }
+ *dot = '\0';
+ if (!got_parse_sha1_digest(id.sha1, p)) {
+ err = got_error_fmt(GOT_ERR_BAD_PATH,
+ "'%s' is not a valid pack file name",
+ packfile_name);
+ goto done;
+ }
+
+ *pack_hash = got_object_id_dup(&id);
+ if (*pack_hash == NULL) {
+ err = got_error_from_errno("got_object_id_dup");
+ goto done;
+ }
+
+ packfd = open(packfile_path, O_RDONLY | O_NOFOLLOW);
+ if (packfd == -1) {
+ err = got_error_from_errno2("open", packfile_path);
+ goto done;
+ }
+
+ *packfile = fdopen(packfd, "r");
+ if (*packfile == NULL) {
+ err = got_error_from_errno2("fdopen", packfile_path);
+ goto done;
+ }
+ packfd = -1;
+done:
+ if (packfd != -1 && close(packfd) == -1 && err == NULL)
+ err = got_error_from_errno2("close", packfile_path);
+ free(packfile_name);
+ if (err) {
+ free(*pack_hash);
+ *pack_hash = NULL;
+ }
+ return err;
+}
+
+const struct got_error *
+got_repo_list_pack(FILE *packfile, struct got_object_id *pack_hash,
+ struct got_repository *repo, got_pack_list_cb list_cb, void *list_arg,
+ got_cancel_cb cancel_cb, void *cancel_arg)
+{
+ const struct got_error *err = NULL;
+ char *id_str = NULL, *idxpath = NULL, *packpath = NULL;
+ struct got_packidx *packidx = NULL;
+ struct got_pack *pack = NULL;
+ uint32_t nobj, i;
+
+ err = got_object_id_str(&id_str, pack_hash);
+ if (err)
+ goto done;
+
+ if (asprintf(&packpath, "%s/pack-%s.pack",
+ GOT_OBJECTS_PACK_DIR, id_str) == -1) {
+ err = got_error_from_errno("asprintf");
+ goto done;
+ }
+ if (asprintf(&idxpath, "%s/pack-%s.idx",
+ GOT_OBJECTS_PACK_DIR, id_str) == -1) {
+ err = got_error_from_errno("asprintf");
+ goto done;
+ }
+
+ err = got_packidx_open(&packidx, got_repo_get_fd(repo), idxpath, 1);
+ if (err)
+ goto done;
+
+ err = got_repo_cache_pack(&pack, repo, packpath, packidx);
+ if (err)
+ goto done;
+
+ nobj = be32toh(packidx->hdr.fanout_table[0xff]);
+ for (i = 0; i < nobj; i++) {
+ struct got_packidx_object_id *oid;
+ struct got_object_id id, base_id;
+ off_t offset, base_offset = 0;
+ uint8_t type;
+ uint64_t size;
+ size_t tslen, len;
+
+ if (cancel_cb) {
+ err = cancel_cb(cancel_arg);
+ if (err)
+ break;
+ }
+ oid = &packidx->hdr.sorted_ids[i];
+ memcpy(id.sha1, oid->sha1, SHA1_DIGEST_LENGTH);
+
+ offset = got_packidx_get_object_offset(packidx, i);
+ if (offset == -1) {
+ err = got_error(GOT_ERR_BAD_PACKIDX);
+ goto done;
+ }
+
+ err = got_pack_parse_object_type_and_size(&type, &size, &tslen,
+ pack, offset);
+ if (err)
+ goto done;
+
+ switch (type) {
+ case GOT_OBJ_TYPE_OFFSET_DELTA:
+ err = got_pack_parse_offset_delta(&base_offset, &len,
+ pack, offset, tslen);
+ if (err)
+ goto done;
+ break;
+ case GOT_OBJ_TYPE_REF_DELTA:
+ err = got_pack_parse_ref_delta(&base_id,
+ pack, offset, tslen);
+ if (err)
+ goto done;
+ break;
+ }
+ err = (*list_cb)(list_arg, &id, type, offset, size,
+ base_offset, &base_id);
+ if (err)
+ goto done;
+ }
+
+done:
+ free(id_str);
+ free(idxpath);
+ free(packpath);
+ if (packidx)
+ got_packidx_close(packidx);
+ return err;
+}
blob - e32b5a142455da777776713187383ad1166a01d4
blob + 994b3c728968b83c996b8eb7fd40f01354a38383
--- regress/cmdline/Makefile
+++ regress/cmdline/Makefile
REGRESS_TARGETS=checkout update status log add rm diff blame branch tag \
ref commit revert cherrypick backout rebase import histedit \
- integrate stage unstage cat clone fetch tree
+ integrate stage unstage cat clone fetch tree pack
NOOBJ=Yes
GOT_TEST_ROOT=/tmp
tree:
./tree.sh -q -r "$(GOT_TEST_ROOT)"
+pack:
+ ./pack.sh -q -r "$(GOT_TEST_ROOT)"
+
.include <bsd.regress.mk>
blob - /dev/null
blob + b6d15ae7a39b4f755d66d6ea89e268ee45b22005 (mode 755)
--- /dev/null
+++ regress/cmdline/pack.sh
+#!/bin/sh
+#
+# Copyright (c) 2021 Stefan Sperling <stsp@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+. ./common.sh
+
+# disable automatic packing for these tests
+export GOT_TEST_PACK=""
+
+test_pack_all_loose_objects() {
+ local testroot=`test_init pack_all_loose_objects`
+
+ # tags should also be packed
+ got tag -r $testroot/repo -m 1.0 1.0 >/dev/null
+
+ # no pack files should exist yet
+ ls $testroot/repo/.git/objects/pack/ > $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+ echo -n > $testroot/stdout.expected
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ gotadmin pack -r $testroot/repo > $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ echo "gotadmin pack failed unexpectedly" >&2
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+ packname=`grep ^Wrote $testroot/stdout | cut -d ' ' -f2`
+ gotadmin listpack $testroot/repo/.git/objects/pack/pack-$packname \
+ > $testroot/stdout
+
+ for d in $testroot/repo/.git/objects/[0-9a-f][0-9a-f]; do
+ id0=`basename $d`
+ ret=0
+ for e in `ls $d`; do
+ obj_id=${id0}${e}
+ if grep -q ^$obj_id $testroot/stdout; then
+ continue
+ fi
+ echo "loose object $obj_id was not packed" >&2
+ ret=1
+ break
+ done
+ if [ "$ret" == "1" ]; then
+ break
+ fi
+ done
+
+ test_done "$testroot" "$ret"
+}
+
+test_pack_exclude() {
+ local testroot=`test_init pack_exclude`
+ local commit0=`git_show_head $testroot/repo`
+
+ # no pack files should exist yet
+ ls $testroot/repo/.git/objects/pack/ > $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+ echo -n > $testroot/stdout.expected
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ got branch -r $testroot/repo mybranch
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ got checkout -b mybranch $testroot/repo $testroot/wt > /dev/null
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ echo a new line >> $testroot/wt/alpha
+ (cd $testroot/wt && got commit -m "edit alpha" >/dev/null)
+
+ gotadmin pack -r $testroot/repo -x master > $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ echo "gotadmin pack failed unexpectedly" >&2
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+ packname=`grep ^Wrote $testroot/stdout | cut -d ' ' -f2`
+ gotadmin listpack $testroot/repo/.git/objects/pack/pack-$packname \
+ > $testroot/stdout
+
+ tree0=`got cat -r $testroot/repo $commit0 | grep ^tree | \
+ cut -d ' ' -f2`
+ excluded_ids=`got tree -r $testroot/repo -c $commit0 -R -i | \
+ cut -d ' ' -f 1`
+ excluded_ids="$excluded_ids $commit0 $tree0"
+ for id in $excluded_ids; do
+ ret=0
+ if grep -q ^$id $testroot/stdout; then
+ echo "found excluded object $id in pack file" >&2
+ ret=1
+ fi
+ if [ "$ret" == "1" ]; then
+ break
+ fi
+ done
+ if [ "$ret" == "1" ]; then
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ for d in $testroot/repo/.git/objects/[0-9a-f][0-9a-f]; do
+ id0=`basename $d`
+ ret=0
+ for e in `ls $d`; do
+ obj_id=${id0}${e}
+ excluded=0
+ for id in $excluded_ids; do
+ if [ "$obj_id" == "$id" ]; then
+ excluded=1
+ break
+ fi
+ done
+ if [ "$excluded" == "1" ]; then
+ continue
+ fi
+ if grep -q ^$obj_id $testroot/stdout; then
+ continue
+ fi
+ echo "loose object $obj_id was not packed" >&2
+ ret=1
+ break
+ done
+ if [ "$ret" == "1" ]; then
+ break
+ fi
+ done
+
+ test_done "$testroot" "$ret"
+}
+
+test_pack_include() {
+ local testroot=`test_init pack_include`
+ local commit0=`git_show_head $testroot/repo`
+
+ # no pack files should exist yet
+ ls $testroot/repo/.git/objects/pack/ > $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+ echo -n > $testroot/stdout.expected
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ got branch -r $testroot/repo mybranch
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ got checkout -b mybranch $testroot/repo $testroot/wt > /dev/null
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ echo a new line >> $testroot/wt/alpha
+ (cd $testroot/wt && got commit -m "edit alpha" >/dev/null)
+ local commit1=`git_show_branch_head $testroot/repo mybranch`
+
+ gotadmin pack -r $testroot/repo master > $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ echo "gotadmin pack failed unexpectedly" >&2
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+ packname=`grep ^Wrote $testroot/stdout | cut -d ' ' -f2`
+ gotadmin listpack $testroot/repo/.git/objects/pack/pack-$packname \
+ > $testroot/stdout
+
+ tree1=`got cat -r $testroot/repo $commit1 | grep ^tree | \
+ cut -d ' ' -f2`
+ alpha1=`got tree -r $testroot/repo -i -c $commit1 | \
+ grep "[0-9a-f] alpha$" | cut -d' ' -f 1`
+ excluded_ids="$alpha1 $commit1 $tree1"
+ for id in $excluded_ids; do
+ ret=0
+ if grep -q ^$id $testroot/stdout; then
+ echo "found excluded object $id in pack file" >&2
+ ret=1
+ fi
+ if [ "$ret" == "1" ]; then
+ break
+ fi
+ done
+ if [ "$ret" == "1" ]; then
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ tree0=`got cat -r $testroot/repo $commit0 | grep ^tree | \
+ cut -d ' ' -f2`
+ included_ids=`got tree -r $testroot/repo -c $commit0 -R -i | \
+ cut -d ' ' -f 1`
+ included_ids="$included_ids $commit0 $tree0"
+ for obj_id in $included_ids; do
+ for id in $excluded_ids; do
+ if [ "$obj_id" == "$id" ]; then
+ excluded=1
+ break
+ fi
+ done
+ if [ "$excluded" == "1" ]; then
+ continue
+ fi
+ if grep -q ^$obj_id $testroot/stdout; then
+ continue
+ fi
+ echo "included object $obj_id was not packed" >&2
+ ret=1
+ break
+ done
+
+ test_done "$testroot" "$ret"
+}
+
+test_pack_ambiguous_arg() {
+ local testroot=`test_init pack_ambiguous_arg`
+ local commit0=`git_show_head $testroot/repo`
+
+ # no pack files should exist yet
+ ls $testroot/repo/.git/objects/pack/ > $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+ echo -n > $testroot/stdout.expected
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ got branch -r $testroot/repo mybranch
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ got checkout -b mybranch $testroot/repo $testroot/wt > /dev/null
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ echo a new line >> $testroot/wt/alpha
+ (cd $testroot/wt && got commit -m "edit alpha" >/dev/null)
+ local commit1=`git_show_branch_head $testroot/repo mybranch`
+
+ gotadmin pack -r $testroot/repo -x master master \
+ > $testroot/stdout 2> $testroot/stderr
+ ret="$?"
+ if [ "$ret" == "0" ]; then
+ echo "gotadmin pack succeeded unexpectedly" >&2
+ test_done "$testroot" "1"
+ return 1
+ fi
+
+ printf "\rpacking 1 reference\n" > $testroot/stdout.expected
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ echo "gotadmin: not enough objects to pack" > $testroot/stderr.expected
+ cmp -s $testroot/stderr.expected $testroot/stderr
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stderr.expected $testroot/stderr
+ fi
+ test_done "$testroot" "$ret"
+}
+
+test_pack_loose_only() {
+ local testroot=`test_init pack_loose_only`
+ local commit0=`git_show_head $testroot/repo`
+
+ # no pack files should exist yet
+ ls $testroot/repo/.git/objects/pack/ > $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+ echo -n > $testroot/stdout.expected
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ got branch -r $testroot/repo mybranch
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ got checkout -b mybranch $testroot/repo $testroot/wt > /dev/null
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ echo a new line >> $testroot/wt/alpha
+ (cd $testroot/wt && got commit -m "edit alpha" >/dev/null)
+
+ # pack objects belonging to the 'master' branch; its objects
+ # should then be excluded while packing 'mybranch' since they
+ # are already packed
+ gotadmin pack -r $testroot/repo master > /dev/null
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ echo "gotadmin pack failed unexpectedly" >&2
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ gotadmin pack -r $testroot/repo mybranch > $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ echo "gotadmin pack failed unexpectedly" >&2
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+ packname=`grep ^Wrote $testroot/stdout | cut -d ' ' -f2`
+ gotadmin listpack $testroot/repo/.git/objects/pack/pack-$packname \
+ > $testroot/stdout
+
+ tree0=`got cat -r $testroot/repo $commit0 | grep ^tree | \
+ cut -d ' ' -f2`
+ excluded_ids=`got tree -r $testroot/repo -c $commit0 -R -i | \
+ cut -d ' ' -f 1`
+ excluded_ids="$excluded_ids $commit0 $tree0"
+ for id in $excluded_ids; do
+ ret=0
+ if grep -q ^$id $testroot/stdout; then
+ echo "found excluded object $id in pack file" >&2
+ ret=1
+ fi
+ if [ "$ret" == "1" ]; then
+ break
+ fi
+ done
+ if [ "$ret" == "1" ]; then
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ for d in $testroot/repo/.git/objects/[0-9a-f][0-9a-f]; do
+ id0=`basename $d`
+ ret=0
+ for e in `ls $d`; do
+ obj_id=${id0}${e}
+ excluded=0
+ for id in $excluded_ids; do
+ if [ "$obj_id" == "$id" ]; then
+ excluded=1
+ break
+ fi
+ done
+ if [ "$excluded" == "1" ]; then
+ continue
+ fi
+ if grep -q ^$obj_id $testroot/stdout; then
+ continue
+ fi
+ echo "loose object $obj_id was not packed" >&2
+ ret=1
+ break
+ done
+ if [ "$ret" == "1" ]; then
+ break
+ fi
+ done
+
+ test_done "$testroot" "$ret"
+}
+
+test_pack_all_objects() {
+ local testroot=`test_init pack_all_objects`
+ local commit0=`git_show_head $testroot/repo`
+
+ # no pack files should exist yet
+ ls $testroot/repo/.git/objects/pack/ > $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+ echo -n > $testroot/stdout.expected
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ got branch -r $testroot/repo mybranch
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ got checkout -b mybranch $testroot/repo $testroot/wt > /dev/null
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ echo a new line >> $testroot/wt/alpha
+ (cd $testroot/wt && got commit -m "edit alpha" >/dev/null)
+
+ # pack objects belonging to the 'master' branch
+ gotadmin pack -r $testroot/repo master > /dev/null
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ echo "gotadmin pack failed unexpectedly" >&2
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ # pack mybranch, including already packed objects on the
+ # 'master' branch which are reachable from mybranch
+ gotadmin pack -r $testroot/repo -a mybranch > $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ echo "gotadmin pack failed unexpectedly" >&2
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+ packname=`grep ^Wrote $testroot/stdout | cut -d ' ' -f2`
+ gotadmin listpack $testroot/repo/.git/objects/pack/pack-$packname \
+ > $testroot/stdout
+
+ for d in $testroot/repo/.git/objects/[0-9a-f][0-9a-f]; do
+ id0=`basename $d`
+ ret=0
+ for e in `ls $d`; do
+ obj_id=${id0}${e}
+ if grep -q ^$obj_id $testroot/stdout; then
+ continue
+ fi
+ echo "loose object $obj_id was not packed" >&2
+ ret=1
+ break
+ done
+ if [ "$ret" == "1" ]; then
+ break
+ fi
+ done
+
+ test_done "$testroot" "$ret"
+}
+
+test_pack_bad_ref() {
+ local testroot=`test_init pack_bad_ref`
+ local commit0=`git_show_head $testroot/repo`
+
+ # no pack files should exist yet
+ ls $testroot/repo/.git/objects/pack/ > $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+ echo -n > $testroot/stdout.expected
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ got branch -r $testroot/repo mybranch
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ got checkout -b mybranch $testroot/repo $testroot/wt > /dev/null
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ gotadmin pack -r $testroot/repo refs/got/worktree/ \
+ > $testroot/stdout 2> $testroot/stderr
+ ret="$?"
+ if [ "$ret" == "0" ]; then
+ echo "gotadmin pack succeeded unexpectedly" >&2
+ test_done "$testroot" "1"
+ return 1
+ fi
+
+ echo -n > $testroot/stdout.expected
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ echo "gotadmin: not enough objects to pack" > $testroot/stderr.expected
+ cmp -s $testroot/stderr.expected $testroot/stderr
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stderr.expected $testroot/stderr
+ fi
+ test_done "$testroot" "$ret"
+}
+
+test_parseargs "$@"
+run_test test_pack_all_loose_objects
+run_test test_pack_exclude
+run_test test_pack_include
+run_test test_pack_ambiguous_arg
+run_test test_pack_loose_only
+run_test test_pack_all_objects
+run_test test_pack_bad_ref