diff --git a/apidoc/ostree-sections.txt b/apidoc/ostree-sections.txt index 3cda9150..dbd12526 100644 --- a/apidoc/ostree-sections.txt +++ b/apidoc/ostree-sections.txt @@ -368,6 +368,7 @@ ostree_repo_commit_traverse_iter_next OstreeRepoPruneFlags ostree_repo_prune ostree_repo_prune_static_deltas +ostree_repo_prune_from_reachable OstreeRepoPullFlags ostree_repo_pull ostree_repo_pull_one_dir diff --git a/src/libostree/libostree.sym b/src/libostree/libostree.sym index 0b8410fb..888a99ba 100644 --- a/src/libostree/libostree.sym +++ b/src/libostree/libostree.sym @@ -371,12 +371,10 @@ global: * NOTE NOTE NOTE */ -/* Remove comment when first new symbol is added -LIBOSTREE_2016.XX { +LIBOSTREE_2017.1 { global: - someostree_symbol_deleteme; + ostree_repo_prune_from_reachable; } LIBOSTREE_2016.14; -*/ /* Stub section for the stable release *after* this development one; don't * edit this other than to update the last number. This is just a copy/paste diff --git a/src/libostree/ostree-repo-prune.c b/src/libostree/ostree-repo-prune.c index 22bff915..1b2a8257 100644 --- a/src/libostree/ostree-repo-prune.c +++ b/src/libostree/ostree-repo-prune.c @@ -254,6 +254,61 @@ ostree_repo_prune_static_deltas (OstreeRepo *self, const char *commit, return ret; } +static gboolean +repo_prune_internal (OstreeRepo *self, + GHashTable *objects, + OstreeRepoPruneOptions *options, + gint *out_objects_total, + gint *out_objects_pruned, + guint64 *out_pruned_object_size_total, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + GHashTableIter hash_iter; + gpointer key, value; + OtPruneData data = { 0, }; + + data.repo = self; + data.reachable = g_hash_table_ref (options->reachable); + + g_hash_table_iter_init (&hash_iter, objects); + while (g_hash_table_iter_next (&hash_iter, &key, &value)) + { + GVariant *serialized_key = key; + GVariant *objdata = value; + const char *checksum; + OstreeObjectType objtype; + gboolean is_loose; + + ostree_object_name_deserialize (serialized_key, &checksum, &objtype); + g_variant_get_child (objdata, 0, "b", &is_loose); + + if (!is_loose) + continue; + + if (!maybe_prune_loose_object (&data, options->flags, checksum, objtype, + cancellable, error)) + goto out; + } + + if (!ostree_repo_prune_static_deltas (self, NULL, cancellable, error)) + goto out; + + if (!_ostree_repo_prune_tmp (self, cancellable, error)) + goto out; + + ret = TRUE; + *out_objects_total = (data.n_reachable_meta + data.n_unreachable_meta + + data.n_reachable_content + data.n_unreachable_content); + *out_objects_pruned = (data.n_unreachable_meta + data.n_unreachable_content); + *out_pruned_object_size_total = data.freed_bytes; + out: + if (data.reachable) + g_hash_table_unref (data.reachable); + return ret; +} + /** * ostree_repo_prune: * @self: Repo @@ -289,39 +344,42 @@ ostree_repo_prune (OstreeRepo *self, GCancellable *cancellable, GError **error) { - gboolean ret = FALSE; GHashTableIter hash_iter; gpointer key, value; g_autoptr(GHashTable) objects = NULL; g_autoptr(GHashTable) all_refs = NULL; - OtPruneData data = { 0, }; + g_autoptr(GHashTable) reachable = NULL; gboolean refs_only = flags & OSTREE_REPO_PRUNE_FLAGS_REFS_ONLY; - data.repo = self; - data.reachable = ostree_repo_traverse_new_reachable (); + reachable = ostree_repo_traverse_new_reachable (); + + /* This original prune API has fixed logic for traversing refs or all commits + * combined with actually deleting content. The newer backend API just does + * the deletion. + */ if (refs_only) { if (!ostree_repo_list_refs (self, NULL, &all_refs, cancellable, error)) - goto out; - + return FALSE; + g_hash_table_iter_init (&hash_iter, all_refs); - + while (g_hash_table_iter_next (&hash_iter, &key, &value)) { const char *checksum = value; g_debug ("Finding objects to keep for commit %s", checksum); - if (!ostree_repo_traverse_commit_union (self, checksum, depth, data.reachable, + if (!ostree_repo_traverse_commit_union (self, checksum, depth, reachable, cancellable, error)) - goto out; + return FALSE; } } if (!ostree_repo_list_objects (self, OSTREE_REPO_LIST_OBJECTS_ALL | OSTREE_REPO_LIST_OBJECTS_NO_PARENTS, &objects, cancellable, error)) - goto out; + return FALSE; if (!refs_only) { @@ -338,45 +396,57 @@ ostree_repo_prune (OstreeRepo *self, continue; g_debug ("Finding objects to keep for commit %s", checksum); - if (!ostree_repo_traverse_commit_union (self, checksum, depth, data.reachable, + if (!ostree_repo_traverse_commit_union (self, checksum, depth, reachable, cancellable, error)) - goto out; + return FALSE; } } - g_hash_table_iter_init (&hash_iter, objects); - while (g_hash_table_iter_next (&hash_iter, &key, &value)) - { - GVariant *serialized_key = key; - GVariant *objdata = value; - const char *checksum; - OstreeObjectType objtype; - gboolean is_loose; - - ostree_object_name_deserialize (serialized_key, &checksum, &objtype); - g_variant_get_child (objdata, 0, "b", &is_loose); - - if (!is_loose) - continue; - - if (!maybe_prune_loose_object (&data, flags, checksum, objtype, - cancellable, error)) - goto out; - } - - if (!ostree_repo_prune_static_deltas (self, NULL, cancellable, error)) - goto out; - - if (!_ostree_repo_prune_tmp (self, cancellable, error)) - goto out; - - ret = TRUE; - *out_objects_total = (data.n_reachable_meta + data.n_unreachable_meta + - data.n_reachable_content + data.n_unreachable_content); - *out_objects_pruned = (data.n_unreachable_meta + data.n_unreachable_content); - *out_pruned_object_size_total = data.freed_bytes; - out: - if (data.reachable) - g_hash_table_unref (data.reachable); - return ret; + { OstreeRepoPruneOptions opts = { flags, reachable }; + return repo_prune_internal (self, objects, &opts, + out_objects_total, out_objects_pruned, + out_pruned_object_size_total, cancellable, error); + } +} + +/** + * ostree_repo_prune_from_reachable: + * @self: Repo + * @options: Options controlling prune process + * @out_objects_total: (out): Number of objects found + * @out_objects_pruned: (out): Number of objects deleted + * @out_pruned_object_size_total: (out): Storage size in bytes of objects deleted + * @cancellable: Cancellable + * @error: Error + * + * Delete content from the repository. This function is the "backend" + * half of the higher level ostree_repo_prune(). To use this function, + * you determine the root set yourself, and this function finds all other + * unreferenced objects and deletes them. + * + * Use this API when you want to perform more selective pruning - for example, + * retain all commits from a production branch, but just GC some history from + * your dev branch. + * + * The %OSTREE_REPO_PRUNE_FLAGS_NO_PRUNE flag may be specified to just determine + * statistics on objects that would be deleted, without actually deleting them. + */ +gboolean +ostree_repo_prune_from_reachable (OstreeRepo *self, + OstreeRepoPruneOptions *options, + gint *out_objects_total, + gint *out_objects_pruned, + guint64 *out_pruned_object_size_total, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(GHashTable) objects = NULL; + + if (!ostree_repo_list_objects (self, OSTREE_REPO_LIST_OBJECTS_ALL | OSTREE_REPO_LIST_OBJECTS_NO_PARENTS, + &objects, cancellable, error)) + return FALSE; + + return repo_prune_internal (self, objects, options, out_objects_total, + out_objects_pruned, out_pruned_object_size_total, + cancellable, error); } diff --git a/src/libostree/ostree-repo.h b/src/libostree/ostree-repo.h index d5303e41..341a4d9c 100644 --- a/src/libostree/ostree-repo.h +++ b/src/libostree/ostree-repo.h @@ -970,6 +970,27 @@ gboolean ostree_repo_prune (OstreeRepo *self, GCancellable *cancellable, GError **error); +struct _OstreeRepoPruneOptions { + OstreeRepoPruneFlags flags; + + GHashTable *reachable; /* Set (object names) */ + + gboolean unused_bools[6]; + int unused_ints[6]; + gpointer unused_ptrs[7]; +}; + +typedef struct _OstreeRepoPruneOptions OstreeRepoPruneOptions; + +_OSTREE_PUBLIC +gboolean ostree_repo_prune_from_reachable (OstreeRepo *self, + OstreeRepoPruneOptions *options, + gint *out_objects_total, + gint *out_objects_pruned, + guint64 *out_pruned_object_size_total, + GCancellable *cancellable, + GError **error); + /** * OstreeRepoPullFlags: * @OSTREE_REPO_PULL_FLAGS_NONE: No special options for pull diff --git a/src/ostree/ot-builtin-prune.c b/src/ostree/ot-builtin-prune.c index d226c84b..853c051f 100644 --- a/src/ostree/ot-builtin-prune.c +++ b/src/ostree/ot-builtin-prune.c @@ -34,6 +34,7 @@ static gint opt_depth = -1; static gboolean opt_refs_only; static char *opt_delete_commit; static char *opt_keep_younger_than; +static char **opt_retain_branch_depth; static GOptionEntry options[] = { { "no-prune", 0, 0, G_OPTION_ARG_NONE, &opt_no_prune, "Only display unreachable objects; don't delete", NULL }, @@ -42,6 +43,7 @@ static GOptionEntry options[] = { { "delete-commit", 0, 0, G_OPTION_ARG_STRING, &opt_delete_commit, "Specify a commit to delete", "COMMIT" }, { "keep-younger-than", 0, 0, G_OPTION_ARG_STRING, &opt_keep_younger_than, "Prune all commits older than the specified date", "DATE" }, { "static-deltas-only", 0, 0, G_OPTION_ARG_NONE, &opt_static_deltas_only, "Change the behavior of delete-commit and keep-younger-than to prune only static deltas" }, + { "retain-branch-depth", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_retain_branch_depth, "Additionally retain BRANCH=DEPTH commits", "BRANCH=DEPTH" }, { NULL } }; @@ -82,87 +84,53 @@ delete_commit (OstreeRepo *repo, const char *commit_to_delete, GCancellable *can } static gboolean -prune_commits_keep_younger_than_date (OstreeRepo *repo, const char *date, GCancellable *cancellable, GError **error) +traverse_keep_younger_than (OstreeRepo *repo, const char *checksum, + struct timespec *ts, + GHashTable *reachable, + GCancellable *cancellable, GError **error) { - g_autoptr(GHashTable) refs = NULL; - g_autoptr(GHashTable) ref_heads = g_hash_table_new (g_str_hash, g_str_equal); - g_autoptr(GHashTable) objects = NULL; - GHashTableIter hash_iter; - gpointer key, value; - struct timespec ts; - gboolean ret = FALSE; + g_autofree char *next_checksum = g_strdup (checksum); + g_autoptr(GVariant) commit = NULL; - if (!parse_datetime (&ts, date, NULL)) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Could not parse '%s'", date); - goto out; - } - - if (!ot_enable_tombstone_commits (repo, error)) - goto out; - - if (!ostree_repo_list_refs (repo, NULL, &refs, cancellable, error)) - goto out; - - /* We used to prune the HEAD of a given ref by default, but that's - * broken for a few reasons. One is that people may use branches as - * tags. Second is that if we do it, we should be deleting the ref - * too, otherwise e.g. `summary -u` breaks trying to load it, etc. + /* This is the first commit in our loop, which has a ref pointing to it. We + * don't want to auto-prune it. */ - g_hash_table_iter_init (&hash_iter, refs); - while (g_hash_table_iter_next (&hash_iter, &key, &value)) + if (!ostree_repo_traverse_commit_union (repo, checksum, 0, reachable, + cancellable, error)) + return FALSE; + + while (TRUE) { - /* Value is lifecycle bound to refs */ - g_hash_table_add (ref_heads, (char*)value); - } - - if (!ostree_repo_list_objects (repo, OSTREE_REPO_LIST_OBJECTS_ALL, &objects, - cancellable, error)) - goto out; - - g_hash_table_iter_init (&hash_iter, objects); - - while (g_hash_table_iter_next (&hash_iter, &key, &value)) - { - GVariant *serialized_key = key; - const char *checksum; - OstreeObjectType objtype; guint64 commit_timestamp; - g_autoptr(GVariant) commit = NULL; - ostree_object_name_deserialize (serialized_key, &checksum, &objtype); + if (!ostree_repo_load_variant_if_exists (repo, OSTREE_OBJECT_TYPE_COMMIT, next_checksum, + &commit, error)) + return FALSE; - if (objtype != OSTREE_OBJECT_TYPE_COMMIT) - continue; - - if (g_hash_table_contains (ref_heads, checksum)) - continue; - - if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT, checksum, - &commit, error)) - goto out; + if (!commit) + break; /* This commit was pruned, so we're done */ commit_timestamp = ostree_commit_get_timestamp (commit); - if (commit_timestamp < ts.tv_sec) + /* Is this commit newer than our --keep-younger-than spec? */ + if (commit_timestamp >= ts->tv_sec) { - if (opt_static_deltas_only) - { - if(!ostree_repo_prune_static_deltas (repo, checksum, cancellable, error)) - goto out; - } + /* It's newer, traverse it */ + if (!ostree_repo_traverse_commit_union (repo, next_checksum, 0, reachable, + cancellable, error)) + return FALSE; + + g_free (next_checksum); + next_checksum = ostree_commit_get_parent (commit); + if (next_checksum) + g_clear_pointer (&commit, (GDestroyNotify)g_variant_unref); else - { - if (!ostree_repo_delete_object (repo, OSTREE_OBJECT_TYPE_COMMIT, checksum, cancellable, error)) - goto out; - } + break; /* No parent, we're done */ } + else + break; /* It's older than our spec, we're done */ } - ret = TRUE; - - out: - return ret; + return TRUE; } gboolean @@ -185,6 +153,9 @@ ostree_builtin_prune (int argc, char **argv, GCancellable *cancellable, GError * if (!opt_no_prune && !ostree_ensure_repo_writable (repo, error)) goto out; + /* Special handling for explicit commit deletion here - we do this + * first. + */ if (opt_delete_commit) { if (opt_no_prune) @@ -200,26 +171,133 @@ ostree_builtin_prune (int argc, char **argv, GCancellable *cancellable, GError * else if (!delete_commit (repo, opt_delete_commit, cancellable, error)) goto out; } - if (opt_keep_younger_than) - { - if (opt_no_prune) - { - ot_util_usage_error (context, "Cannot specify both --keep-younger-than and --no-prune", error); - goto out; - } - if (!prune_commits_keep_younger_than_date (repo, opt_keep_younger_than, cancellable, error)) - goto out; - } if (opt_refs_only) pruneflags |= OSTREE_REPO_PRUNE_FLAGS_REFS_ONLY; if (opt_no_prune) pruneflags |= OSTREE_REPO_PRUNE_FLAGS_NO_PRUNE; - if (!ostree_repo_prune (repo, pruneflags, opt_depth, - &n_objects_total, &n_objects_pruned, &objsize_total, - cancellable, error)) - goto out; + /* If no newer more complex options are specified, drop down to the original + * prune API - both to avoid code duplication, and to keep it run from the + * test suite. + */ + if (!(opt_retain_branch_depth || opt_keep_younger_than)) + { + if (!ostree_repo_prune (repo, pruneflags, opt_depth, + &n_objects_total, &n_objects_pruned, &objsize_total, + cancellable, error)) + goto out; + } + else + { + g_autoptr(GHashTable) all_refs = NULL; + g_autoptr(GHashTable) reachable = ostree_repo_traverse_new_reachable (); + g_autoptr(GHashTable) retain_branch_depth = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + struct timespec keep_younger_than_ts; + GHashTableIter hash_iter; + gpointer key, value; + + /* Otherwise, the default is --refs-only; we set this just as a note */ + opt_refs_only = TRUE; + + if (opt_keep_younger_than) + { + if (!parse_datetime (&keep_younger_than_ts, opt_keep_younger_than, NULL)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Could not parse '%s'", opt_keep_younger_than); + goto out; + } + } + + for (char **iter = opt_retain_branch_depth; iter && *iter; iter++) + { + /* bd should look like BRANCH=DEPTH where DEPTH is an int */ + const char *bd = *iter; + const char *eq = strchr (bd, '='); + const char *depthstr; + gint64 depth; + char *endptr; + + if (!eq) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Invalid value %s, must specify BRANCH=DEPTH", + bd); + goto out; + } + depthstr = eq + 1; + errno = EPERM; + depth = g_ascii_strtoll (depthstr, &endptr, 10); + if (depth == 0) + { + if (errno == EINVAL) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Out of range depth %s", depthstr); + goto out; + } + else if (endptr == depthstr) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Invalid depth %s", depthstr); + goto out; + } + } + g_hash_table_insert (retain_branch_depth, g_strndup (bd, eq - bd), + GINT_TO_POINTER ((int)depth)); + } + + /* We start from the refs */ + if (!ostree_repo_list_refs (repo, NULL, &all_refs, + cancellable, error)) + return FALSE; + + g_hash_table_iter_init (&hash_iter, all_refs); + while (g_hash_table_iter_next (&hash_iter, &key, &value)) + { + const char *checksum = value; + gpointer depthp = g_hash_table_lookup (retain_branch_depth, key); + gint depth; + + /* Here, we handle a spec like + * --retain-branch-depth=myos/x86_64/stable=-1 + * --retain-branch-depth=myos/x86_64/dev=5 + */ + if (depthp) + depth = GPOINTER_TO_INT(depthp); + else if (opt_keep_younger_than) + { + if (!traverse_keep_younger_than (repo, checksum, + &keep_younger_than_ts, + reachable, + cancellable, error)) + goto out; + + /* Okay, we handled the younger-than case; the other + * two fall through to plain depth-based handling below. + */ + continue; /* Note again, we're skipping the below bit */ + } + else + depth = opt_depth; /* No --retain-branch-depth for this branch, use + the global default */ + + g_debug ("Finding objects to keep for commit %s", checksum); + if (!ostree_repo_traverse_commit_union (repo, checksum, depth, reachable, + cancellable, error)) + return FALSE; + } + + { OstreeRepoPruneOptions opts = { pruneflags, reachable }; + if (!ostree_repo_prune_from_reachable (repo, &opts, + &n_objects_total, + &n_objects_pruned, + &objsize_total, + cancellable, error)) + goto out; + } + } formatted_freed_size = g_format_size_full (objsize_total, 0); diff --git a/tests/test-prune.sh b/tests/test-prune.sh index ce14ce67..51ec7948 100755 --- a/tests/test-prune.sh +++ b/tests/test-prune.sh @@ -25,7 +25,7 @@ skip_without_user_xattrs setup_fake_remote_repo1 "archive-z2" -echo '1..3' +echo '1..5' cd ${test_tmpdir} mkdir repo @@ -49,6 +49,7 @@ find repo | grep \.commit$ | wc -l > commitcount assert_file_has_content commitcount "^3$" find repo/objects -name '*.tombstone-commit' | wc -l > tombstonecommitcount assert_file_has_content tombstonecommitcount "^0$" +$OSTREE fsck ${CMD_PREFIX} ostree prune --repo=repo --refs-only --depth=1 -v find repo | grep \.commit$ | wc -l > commitcount @@ -73,6 +74,7 @@ find repo/objects -name '*.commit' | wc -l > commitcount assert_file_has_content commitcount "^1$" find repo/objects -name '*.tombstone-commit' | wc -l > tombstonecommitcount assert_not_file_has_content tombstonecommitcount "^0$" +$OSTREE fsck # and that tombstone are deleted once the commits are pulled again ${CMD_PREFIX} ostree --repo=repo pull --depth=-1 origin test @@ -83,6 +85,7 @@ COMMIT_TO_DELETE=$(${CMD_PREFIX} ostree --repo=repo log test | grep ^commit | cu ${CMD_PREFIX} ostree --repo=repo prune --delete-commit=$COMMIT_TO_DELETE find repo/objects -name '*.tombstone-commit' | wc -l > tombstonecommitcount assert_file_has_content tombstonecommitcount "^1$" +$OSTREE fsck ${CMD_PREFIX} ostree prune --repo=repo --refs-only --depth=0 -v find repo/objects -name '*.commit' | wc -l > commitcount @@ -94,6 +97,7 @@ assert_file_has_content commitcount "^3$" ${CMD_PREFIX} ostree --repo=repo prune --keep-younger-than="2015-10-29 12:43:29 +0000" find repo/objects -name '*.commit' | wc -l > commitcount assert_file_has_content commitcount "^2$" +$OSTREE fsck ${CMD_PREFIX} ostree prune --repo=repo --refs-only --depth=0 -v @@ -112,6 +116,7 @@ oldcommit_rev=$($OSTREE --repo=repo rev-parse oldcommit) $OSTREE ls ${oldcommit_rev} ${CMD_PREFIX} ostree --repo=repo prune --keep-younger-than="1 week ago" $OSTREE ls ${oldcommit_rev} +$OSTREE fsck ${CMD_PREFIX} ostree --repo=repo pull --depth=-1 origin test ${CMD_PREFIX} ostree --repo=repo commit --branch=test -m test -s test tree --timestamp="November 05 1955" @@ -124,6 +129,7 @@ ${CMD_PREFIX} ostree --repo=repo static-delta list | wc -l > deltascount assert_file_has_content deltascount "^2$" COMMIT_TO_DELETE=$(${CMD_PREFIX} ostree --repo=repo rev-parse test) ${CMD_PREFIX} ostree --repo=repo prune --static-deltas-only --delete-commit=$COMMIT_TO_DELETE +${CMD_PREFIX} ostree --repo=repo fsck ${CMD_PREFIX} ostree --repo=repo static-delta list | wc -l > deltascount assert_file_has_content deltascount "^1$" ${CMD_PREFIX} ostree --repo=repo static-delta generate test @@ -178,3 +184,62 @@ ${CMD_PREFIX} ostree --repo=child-repo prune --refs-only --depth=0 assert_has_n_objects child-repo 3 echo "ok prune with parent repo" + +# Delete all the above since I can't be bothered to think about how new tests +# would interact. We make a new repo test suite, then clone it +# for "subtests" below with reinitialize_datesnap_repo() +rm repo datetest-snapshot-repo -rf +${CMD_PREFIX} ostree --repo=datetest-snapshot-repo init --mode=archive +# Some ancient commits on the both a stable/dev branch +for day in $(seq 5); do + ${CMD_PREFIX} ostree --repo=datetest-snapshot-repo commit --branch=stable -m test -s "old stable build $day" tree --timestamp="October $day 1985" + ${CMD_PREFIX} ostree --repo=datetest-snapshot-repo commit --branch=dev -m test -s "old dev build $day" tree --timestamp="October $day 1985" +done +# And some new ones +for x in $(seq 3); do + ${CMD_PREFIX} ostree --repo=datetest-snapshot-repo commit --branch=stable -m test -s "new stable build $x" tree + ${CMD_PREFIX} ostree --repo=datetest-snapshot-repo commit --branch=dev -m test -s "new dev build $x" tree +done +find datetest-snapshot-repo/objects -name '*.commit' | wc -l > commitcount +assert_file_has_content commitcount "^16$" + +# Snapshot the above +reinitialize_datesnap_repo() { + rm repo -rf + ${CMD_PREFIX} ostree --repo=repo init --mode=archive + ${CMD_PREFIX} ostree --repo=repo pull-local --depth=-1 datetest-snapshot-repo +} + +# This test prunes with both younger than as well as a full strong ref to the +# stable branch +reinitialize_datesnap_repo +# First, a quick test of invalid input +if ${CMD_PREFIX} ostree --repo=repo prune --keep-younger-than="1 week ago" --retain-branch-depth=stable=BACON 2>err.txt; then + assert_not_reached "BACON is a number?!" +fi +assert_file_has_content err.txt 'Invalid depth BACON' +${CMD_PREFIX} ostree --repo=repo prune --keep-younger-than="1 week ago" --retain-branch-depth=stable=-1 +find repo/objects -name '*.commit' | wc -l > commitcount +assert_file_has_content commitcount "^11$" +# Double check our backup is unchanged +find datetest-snapshot-repo/objects -name '*.commit' | wc -l > commitcount +assert_file_has_content commitcount "^16$" +$OSTREE fsck + +# Again but this time only retain 6 (5+1) commits on stable. This should drop +# out 8 - 6 = 2 commits (so the 11 above minus 2 = 9) +${CMD_PREFIX} ostree --repo=repo prune --keep-younger-than="1 week ago" --retain-branch-depth=stable=5 +find repo/objects -name '*.commit' | wc -l > commitcount +assert_file_has_content commitcount "^9$" +$OSTREE fsck +echo "ok retain branch depth and keep-younger-than" + +# Just stable branch ref, we should prune everything except the tip of dev, +# so 8 stable + 1 dev = 9 +reinitialize_datesnap_repo +${CMD_PREFIX} ostree --repo=repo prune --depth=0 --retain-branch-depth=stable=-1 +find repo/objects -name '*.commit' | wc -l > commitcount +assert_file_has_content commitcount "^9$" +$OSTREE fsck + +echo "ok retain branch depth (alone)"