ostree_repo_get_collection_id
ostree_repo_set_collection_id
ostree_validate_collection_id
+ostree_repo_list_collection_refs
+ostree_repo_set_collection_ref_immediate
+ostree_repo_transaction_set_collection_ref
</SECTION>
ostree_collection_ref_hash;
ostree_collection_ref_new;
ostree_repo_get_collection_id;
+ ostree_repo_list_collection_refs;
ostree_repo_set_collection_id;
+ ostree_repo_set_collection_ref_immediate;
+ ostree_repo_transaction_set_collection_ref;
ostree_validate_collection_id;
} LIBOSTREE_2017.7_EXPERIMENTAL;
{
if (self->txn_refs == NULL)
self->txn_refs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ if (self->txn_collection_refs == NULL)
+ self->txn_collection_refs = g_hash_table_new_full (ostree_collection_ref_hash,
+ ostree_collection_ref_equal,
+ (GDestroyNotify) ostree_collection_ref_free,
+ g_free);
}
/**
g_hash_table_replace (self->txn_refs, refspec, g_strdup (checksum));
}
+/**
+ * ostree_repo_transaction_set_collection_ref:
+ * @self: An #OstreeRepo
+ * @ref: The collection–ref to write
+ * @checksum: (nullable): The checksum to point it to
+ *
+ * If @checksum is not %NULL, then record it as the target of local ref named
+ * @ref.
+ *
+ * Otherwise, if @checksum is %NULL, then record that the ref should
+ * be deleted.
+ *
+ * The change will not be written out immediately, but when the transaction
+ * is completed with ostree_repo_commit_transaction(). If the transaction
+ * is instead aborted with ostree_repo_abort_transaction(), no changes will
+ * be made to the repository.
+ *
+ * Since: 2017.8
+ */
+void
+ostree_repo_transaction_set_collection_ref (OstreeRepo *self,
+ const OstreeCollectionRef *ref,
+ const char *checksum)
+{
+ g_return_if_fail (OSTREE_IS_REPO (self));
+ g_return_if_fail (self->in_transaction == TRUE);
+ g_return_if_fail (ref != NULL);
+ g_return_if_fail (checksum == NULL || ostree_validate_checksum_string (checksum, NULL));
+
+ ensure_txn_refs (self);
+
+ g_hash_table_replace (self->txn_collection_refs,
+ ostree_collection_ref_dup (ref), g_strdup (checksum));
+}
+
/**
* ostree_repo_set_ref_immediate:
* @self: An #OstreeRepo
GCancellable *cancellable,
GError **error)
{
- return _ostree_repo_write_ref (self, remote, ref, checksum,
+ const OstreeCollectionRef _ref = { NULL, (gchar *) ref };
+ return _ostree_repo_write_ref (self, remote, &_ref, checksum,
+ cancellable, error);
+}
+
+/**
+ * ostree_repo_set_collection_ref_immediate:
+ * @self: An #OstreeRepo
+ * @ref: The collection–ref to write
+ * @checksum: (nullable): The checksum to point it to, or %NULL to unset
+ * @cancellable: GCancellable
+ * @error: GError
+ *
+ * This is like ostree_repo_transaction_set_collection_ref(), except it may be
+ * invoked outside of a transaction. This is presently safe for the
+ * case where we're creating or overwriting an existing ref.
+ *
+ * Returns: %TRUE on success, %FALSE otherwise
+ * Since: 2017.8
+ */
+gboolean
+ostree_repo_set_collection_ref_immediate (OstreeRepo *self,
+ const OstreeCollectionRef *ref,
+ const char *checksum,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (OSTREE_IS_REPO (self), FALSE);
+ g_return_val_if_fail (ref != NULL, FALSE);
+ g_return_val_if_fail (checksum == NULL || ostree_validate_checksum_string (checksum, NULL), FALSE);
+ g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ return _ostree_repo_write_ref (self, NULL, ref, checksum,
cancellable, error);
}
return FALSE;
g_clear_pointer (&self->txn_refs, g_hash_table_destroy);
+ if (self->txn_collection_refs)
+ if (!_ostree_repo_update_collection_refs (self, self->txn_collection_refs, cancellable, error))
+ return FALSE;
+ g_clear_pointer (&self->txn_collection_refs, g_hash_table_destroy);
+
if (self->commit_stagedir_fd != -1)
{
(void) close (self->commit_stagedir_fd);
g_hash_table_remove_all (self->loose_object_devino_hash);
g_clear_pointer (&self->txn_refs, g_hash_table_destroy);
+ g_clear_pointer (&self->txn_collection_refs, g_hash_table_destroy);
if (self->commit_stagedir_fd != -1)
{
/* Well-known keys for the additional metadata field in a summary file. */
#define OSTREE_SUMMARY_LAST_MODIFIED "ostree.summary.last-modified"
#define OSTREE_SUMMARY_EXPIRES "ostree.summary.expires"
+#define OSTREE_SUMMARY_COLLECTION_ID "ostree.summary.collection-id"
+#define OSTREE_SUMMARY_COLLECTION_MAP "ostree.summary.collection-map"
/* Well-known keys for the additional metadata field in a commit in a ref entry
* in a summary file. */
GFile *sysroot_dir;
char *remotes_config_dir;
- GHashTable *txn_refs;
+ GHashTable *txn_refs; /* (element-type utf8 utf8) */
+ GHashTable *txn_collection_refs; /* (element-type OstreeCollectionRef utf8) */
GMutex txn_stats_lock;
OstreeRepoTransactionStats txn_stats;
GCancellable *cancellable,
GError **error);
-gboolean
+gboolean
+_ostree_repo_update_collection_refs (OstreeRepo *self,
+ GHashTable *refs,
+ GCancellable *cancellable,
+ GError **error);
+
+gboolean
_ostree_repo_file_replace_contents (OstreeRepo *self,
int dfd,
const char *path,
GCancellable *cancellable,
GError **error);
-gboolean
-_ostree_repo_write_ref (OstreeRepo *self,
- const char *remote,
- const char *ref,
- const char *rev,
- GCancellable *cancellable,
- GError **error);
+gboolean
+_ostree_repo_write_ref (OstreeRepo *self,
+ const char *remote,
+ const OstreeCollectionRef *ref,
+ const char *rev,
+ GCancellable *cancellable,
+ GError **error);
OstreeRepoFile *
_ostree_repo_file_new_for_commit (OstreeRepo *repo,
const gchar *collection_id,
GError **error);
+gboolean ostree_repo_list_collection_refs (OstreeRepo *self,
+ const char *match_collection_id,
+ GHashTable **out_all_refs,
+ GCancellable *cancellable,
+ GError **error);
+
+void ostree_repo_transaction_set_collection_ref (OstreeRepo *self,
+ const OstreeCollectionRef *ref,
+ const char *checksum);
+
+gboolean ostree_repo_set_collection_ref_immediate (OstreeRepo *self,
+ const OstreeCollectionRef *ref,
+ const char *checksum,
+ GCancellable *cancellable,
+ GError **error);
+
#endif /* !OSTREE_ENABLE_EXPERIMENTAL_API */
G_END_DECLS
GHashTableIter hash_iter;
gpointer key, value;
g_autoptr(GHashTable) objects = NULL;
- g_autoptr(GHashTable) all_refs = NULL;
g_autoptr(GHashTable) reachable = NULL;
gboolean refs_only = flags & OSTREE_REPO_PRUNE_FLAGS_REFS_ONLY;
if (refs_only)
{
+ /* Ignoring collections. */
+ g_autoptr(GHashTable) all_refs = NULL; /* (element-type utf8 utf8) */
+
if (!ostree_repo_list_refs (self, 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;
+
+ g_debug ("Finding objects to keep for commit %s", checksum);
+ if (!ostree_repo_traverse_commit_union (self, checksum, depth, reachable,
+ cancellable, error))
+ return FALSE;
+ }
+
+ /* Using collections. */
+ g_autoptr(GHashTable) all_collection_refs = NULL; /* (element-type OstreeChecksumRef utf8) */
+
+ if (!ostree_repo_list_collection_refs (self, NULL, &all_collection_refs,
+ cancellable, error))
+ return FALSE;
+
+ g_hash_table_iter_init (&hash_iter, all_collection_refs);
+
while (g_hash_table_iter_next (&hash_iter, &key, &value))
{
const char *checksum = value;
#include "config.h"
+#include "ostree-core-private.h"
#include "ostree-repo-private.h"
#include "otutil.h"
#include "ot-fs-utils.h"
+/* This is polymorphic in @collection_id: if non-%NULL, @refs will be treated as of
+ * type OstreeCollectionRef ↦ checksum. Otherwise, it will be treated as of type
+ * refspec ↦ checksum. */
static gboolean
add_ref_to_set (const char *remote,
+ const char *collection_id,
int base_fd,
const char *path,
GHashTable *refs,
GCancellable *cancellable,
GError **error)
{
+ g_return_val_if_fail (remote == NULL || collection_id == NULL, FALSE);
+
gsize len;
char *contents = glnx_file_get_contents_utf8_at (base_fd, path, &len, cancellable, error);
if (!contents)
g_strchomp (contents);
- g_autoptr(GString) refname = g_string_new ("");
- if (remote)
+ if (collection_id == NULL)
+ {
+ g_autoptr(GString) refname = g_string_new ("");
+ if (remote)
+ {
+ g_string_append (refname, remote);
+ g_string_append_c (refname, ':');
+ }
+ g_string_append (refname, path);
+ g_hash_table_insert (refs, g_string_free (g_steal_pointer (&refname), FALSE), contents);
+ }
+ else
{
- g_string_append (refname, remote);
- g_string_append_c (refname, ':');
+ g_hash_table_insert (refs, ostree_collection_ref_new (collection_id, path), contents);
}
- g_string_append (refname, path);
- g_hash_table_insert (refs, g_string_free (g_steal_pointer (&refname), FALSE), contents);
return TRUE;
}
g_clear_error (&temp_error);
+ /* FIXME: Conflict detection needs to be extended to collection–refs
+ * using ostree_repo_list_collection_refs(). */
if (!ostree_repo_list_refs (self, name, &refs, cancellable, error))
return FALSE;
static gboolean
enumerate_refs_recurse (OstreeRepo *repo,
const char *remote,
+ const char *collection_id,
int base_dfd,
GString *base_path,
int child_dfd,
{
g_string_append_c (base_path, '/');
- if (!enumerate_refs_recurse (repo, remote, base_dfd, base_path,
+ if (!enumerate_refs_recurse (repo, remote, collection_id, base_dfd, base_path,
dfd_iter.fd, dent->d_name,
refs, cancellable, error))
return FALSE;
}
else if (dent->d_type == DT_REG)
{
- if (!add_ref_to_set (remote, base_dfd, base_path->str, refs,
+ if (!add_ref_to_set (remote, collection_id, base_dfd, base_path->str, refs,
cancellable, error))
return FALSE;
}
if (!glnx_opendirat (self->repo_dir_fd, cut_prefix ? path : prefix_path, TRUE, &base_fd, error))
return FALSE;
- if (!enumerate_refs_recurse (self, remote, base_fd, base_path,
+ if (!enumerate_refs_recurse (self, remote, NULL, base_fd, base_path,
base_fd, cut_prefix ? "." : ref_prefix,
ret_all_refs, cancellable, error))
return FALSE;
if (!glnx_opendirat (self->repo_dir_fd, prefix_path, TRUE, &prefix_dfd, error))
return FALSE;
- if (!add_ref_to_set (remote, prefix_dfd, ref_prefix, ret_all_refs,
+ if (!add_ref_to_set (remote, NULL, prefix_dfd, ref_prefix, ret_all_refs,
cancellable, error))
return FALSE;
}
if (!glnx_opendirat (self->repo_dir_fd, "refs/heads", TRUE, &refs_heads_dfd, error))
return FALSE;
- if (!enumerate_refs_recurse (self, NULL, refs_heads_dfd, base_path,
+ if (!enumerate_refs_recurse (self, NULL, NULL, refs_heads_dfd, base_path,
refs_heads_dfd, ".",
ret_all_refs, cancellable, error))
return FALSE;
if (!glnx_opendirat (dfd_iter.fd, dent->d_name, TRUE, &remote_dfd, error))
return FALSE;
- if (!enumerate_refs_recurse (self, dent->d_name, remote_dfd, base_path,
+ if (!enumerate_refs_recurse (self, dent->d_name, NULL, remote_dfd, base_path,
remote_dfd, ".",
ret_all_refs,
cancellable, error))
}
gboolean
-_ostree_repo_write_ref (OstreeRepo *self,
- const char *remote,
- const char *ref,
- const char *rev,
- GCancellable *cancellable,
- GError **error)
+_ostree_repo_write_ref (OstreeRepo *self,
+ const char *remote,
+ const OstreeCollectionRef *ref,
+ const char *rev,
+ GCancellable *cancellable,
+ GError **error)
{
glnx_fd_close int dfd = -1;
- if (remote == NULL)
+ g_return_val_if_fail (remote == NULL || ref->collection_id == NULL, FALSE);
+
+ if (remote == NULL &&
+ (ref->collection_id == NULL || g_strcmp0 (ref->collection_id, ostree_repo_get_collection_id (self)) == 0))
{
if (!glnx_opendirat (self->repo_dir_fd, "refs/heads", TRUE,
&dfd, error))
return FALSE;
}
}
+ else if (remote == NULL && ref->collection_id != NULL)
+ {
+ glnx_fd_close int refs_mirrors_dfd = -1;
+
+ /* refs/mirrors might not exist in older repositories, so create it. */
+ if (!glnx_shutil_mkdir_p_at_open (self->repo_dir_fd, "refs/mirrors", 0777,
+ &refs_mirrors_dfd, cancellable, error))
+ {
+ g_prefix_error (error, "Opening %s: ", "refs/mirrors");
+ return FALSE;
+ }
+
+ if (rev != NULL)
+ {
+ /* Ensure we have a dir for the collection */
+ if (!glnx_shutil_mkdir_p_at (refs_mirrors_dfd, ref->collection_id, 0777, cancellable, error))
+ return FALSE;
+ }
+
+ dfd = glnx_opendirat_with_errno (refs_mirrors_dfd, ref->collection_id, TRUE);
+ if (dfd < 0 && (errno != ENOENT || rev != NULL))
+ return glnx_throw_errno_prefix (error, "Opening mirrors/ dir %s", ref->collection_id);
+ }
else
{
glnx_fd_close int refs_remotes_dfd = -1;
{
if (dfd >= 0)
{
- if (unlinkat (dfd, ref, 0) != 0)
+ if (unlinkat (dfd, ref->ref_name, 0) != 0)
{
if (errno != ENOENT)
return glnx_throw_errno (error);
}
else
{
- if (!write_checksum_file_at (self, dfd, ref, rev, cancellable, error))
+ if (!write_checksum_file_at (self, dfd, ref->ref_name, rev, cancellable, error))
return FALSE;
}
gboolean
_ostree_repo_update_refs (OstreeRepo *self,
- GHashTable *refs,
+ GHashTable *refs, /* (element-type utf8 utf8) */
GCancellable *cancellable,
GError **error)
{
const char *refspec = key;
const char *rev = value;
g_autofree char *remote = NULL;
- g_autofree char *ref = NULL;
+ g_autofree char *ref_name = NULL;
+
+ if (!ostree_parse_refspec (refspec, &remote, &ref_name, error))
+ return FALSE;
+
+ const OstreeCollectionRef ref = { NULL, ref_name };
+ if (!_ostree_repo_write_ref (self, remote, &ref, rev,
+ cancellable, error))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+_ostree_repo_update_collection_refs (OstreeRepo *self,
+ GHashTable *refs, /* (element-type OstreeCollectionRef utf8) */
+ GCancellable *cancellable,
+ GError **error)
+{
+ GHashTableIter hash_iter;
+ gpointer key, value;
+
+ g_hash_table_iter_init (&hash_iter, refs);
+ while (g_hash_table_iter_next (&hash_iter, &key, &value))
+ {
+ const OstreeCollectionRef *ref = key;
+ const char *rev = value;
+
+ if (!_ostree_repo_write_ref (self, NULL, ref, rev,
+ cancellable, error))
+ return FALSE;
+ }
- if (!ostree_parse_refspec (refspec, &remote, &ref, error))
+ return TRUE;
+}
+
+/**
+ * ostree_repo_list_collection_refs:
+ * @self: Repo
+ * @match_collection_id: (nullable): If non-%NULL, only list refs from this collection
+ * @out_all_refs: (out) (element-type OstreeCollectionRef utf8): Mapping from collection–ref to checksum
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * List all local and mirrored refs, mapping them to the commit checksums they
+ * currently point to in @out_all_refs. If @match_collection_id is specified,
+ * the results will be limited to those with an equal collection ID.
+ *
+ * #OstreeCollectionRefs are guaranteed to be returned with their collection ID
+ * set to a non-%NULL value; so no refs from `refs/heads` will be listed if no
+ * collection ID is configured for the repository
+ * (ostree_repo_get_collection_id()).
+ *
+ * Returns: %TRUE on success, %FALSE otherwise
+ * Since: 2017.8
+ */
+gboolean
+ostree_repo_list_collection_refs (OstreeRepo *self,
+ const char *match_collection_id,
+ GHashTable **out_all_refs,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (OSTREE_IS_REPO (self), FALSE);
+ g_return_val_if_fail (match_collection_id == NULL ||
+ ostree_validate_collection_id (match_collection_id, NULL), FALSE);
+ g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ g_autoptr(GHashTable) ret_all_refs = NULL;
+
+ ret_all_refs = g_hash_table_new_full (ostree_collection_ref_hash,
+ ostree_collection_ref_equal,
+ (GDestroyNotify) ostree_collection_ref_free,
+ g_free);
+
+ g_auto(GLnxDirFdIterator) dfd_iter = { 0, };
+ g_autoptr(GString) base_path = g_string_new ("");
+
+ const gchar *main_collection_id = ostree_repo_get_collection_id (self);
+
+ if (main_collection_id != NULL &&
+ (match_collection_id == NULL || g_strcmp0 (match_collection_id, main_collection_id) == 0))
+ {
+ glnx_fd_close int refs_heads_dfd = -1;
+
+ if (!glnx_opendirat (self->repo_dir_fd, "refs/heads", TRUE, &refs_heads_dfd, error))
+ return FALSE;
+
+ if (!enumerate_refs_recurse (self, NULL, main_collection_id, refs_heads_dfd, base_path,
+ refs_heads_dfd, ".",
+ ret_all_refs, cancellable, error))
return FALSE;
+ }
- if (!_ostree_repo_write_ref (self, remote, ref, rev,
+ g_string_truncate (base_path, 0);
+
+ gboolean refs_mirrors_exists = FALSE;
+ if (!ot_dfd_iter_init_allow_noent (self->repo_dir_fd, "refs/mirrors",
+ &dfd_iter, &refs_mirrors_exists, error))
+ return FALSE;
+
+ while (refs_mirrors_exists)
+ {
+ struct dirent *dent;
+ glnx_fd_close int collection_dfd = -1;
+
+ if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&dfd_iter, &dent, cancellable, error))
+ return FALSE;
+ if (!dent)
+ break;
+
+ if (dent->d_type != DT_DIR)
+ continue;
+
+ if (match_collection_id != NULL && g_strcmp0 (match_collection_id, dent->d_name) != 0)
+ continue;
+
+ if (!glnx_opendirat (dfd_iter.fd, dent->d_name, TRUE, &collection_dfd, error))
+ return FALSE;
+
+ if (!enumerate_refs_recurse (self, NULL, dent->d_name, collection_dfd, base_path,
+ collection_dfd, ".",
+ ret_all_refs,
cancellable, error))
return FALSE;
}
+ ot_transfer_out_value (out_all_refs, &ret_all_refs);
return TRUE;
}
if (self->config)
g_key_file_free (self->config);
g_clear_pointer (&self->txn_refs, g_hash_table_destroy);
+ g_clear_pointer (&self->txn_collection_refs, g_hash_table_destroy);
g_clear_error (&self->writable_error);
g_clear_pointer (&self->object_sizes, (GDestroyNotify) g_hash_table_unref);
g_clear_pointer (&self->dirmeta_cache, (GDestroyNotify) g_hash_table_unref);
glnx_fd_close int dfd = -1;
struct stat stbuf;
const char *state_dirs[] = { "objects", "tmp", "extensions", "state",
- "refs", "refs/heads", "refs/remotes" };
+ "refs", "refs/heads", "refs/mirrors",
+ "refs/remotes" };
if (mkdir (repopath, 0755) != 0)
{
*
* It is regenerated automatically after a commit if
* `core/commit-update-summary` is set.
+ *
+ * If the `core/collection-id` key is set in the configuration, it will be
+ * included as %OSTREE_SUMMARY_COLLECTION_ID in the summary file. Refs from the
+ * `refs/mirrors` directory will be included in the generated summary file,
+ * listed under the %OSTREE_SUMMARY_COLLECTION_MAP key. Collection IDs and refs
+ * in %OSTREE_SUMMARY_COLLECTION_MAP are guaranteed to be in lexicographic
+ * order.
*/
gboolean
ostree_repo_regenerate_summary (OstreeRepo *self,
g_variant_dict_init (&additional_metadata_builder, additional_metadata);
g_autoptr(GVariantBuilder) refs_builder = g_variant_builder_new (G_VARIANT_TYPE ("a(s(taya{sv}))"));
+ const gchar *main_collection_id = ostree_repo_get_collection_id (self);
+
{
- g_autoptr(GHashTable) refs = NULL;
- if (!ostree_repo_list_refs (self, NULL, &refs, cancellable, error))
- return FALSE;
+ if (main_collection_id == NULL)
+ {
+ g_autoptr(GHashTable) refs = NULL;
+ if (!ostree_repo_list_refs (self, NULL, &refs, cancellable, error))
+ return FALSE;
- g_autoptr(GList) ordered_keys = g_hash_table_get_keys (refs);
- ordered_keys = g_list_sort (ordered_keys, (GCompareFunc)strcmp);
+ g_autoptr(GList) ordered_keys = g_hash_table_get_keys (refs);
+ ordered_keys = g_list_sort (ordered_keys, (GCompareFunc)strcmp);
- for (GList *iter = ordered_keys; iter; iter = iter->next)
- {
- const char *ref = iter->data;
- const char *commit = g_hash_table_lookup (refs, ref);
+ for (GList *iter = ordered_keys; iter; iter = iter->next)
+ {
+ const char *ref = iter->data;
+ const char *commit = g_hash_table_lookup (refs, ref);
- if (!summary_add_ref_entry (self, ref, commit, refs_builder, error))
- return FALSE;
+ if (!summary_add_ref_entry (self, ref, commit, refs_builder, error))
+ return FALSE;
+ }
}
}
g_variant_new_uint64 (GUINT64_TO_BE (g_get_real_time () / G_USEC_PER_SEC)));
}
+ /* Add refs which have a collection specified. ostree_repo_list_collection_refs()
+ * is guaranteed to only return refs which are in refs/mirrors, or those which
+ * are in refs/heads if the repository configuration specifies a collection ID
+ * (which we put in the main refs map, rather than the collection map, for
+ * backwards compatibility). */
+ {
+ g_autoptr(GHashTable) collection_refs = NULL;
+ if (!ostree_repo_list_collection_refs (self, NULL, &collection_refs, cancellable, error))
+ return FALSE;
+
+ gsize collection_map_size = 0;
+ GHashTableIter iter;
+ g_autoptr(GHashTable) collection_map = NULL; /* (element-type utf8 GHashTable) */
+ g_hash_table_iter_init (&iter, collection_refs);
+ collection_map = g_hash_table_new_full (g_str_hash, g_str_equal, NULL,
+ (GDestroyNotify) g_hash_table_unref);
+
+ const OstreeCollectionRef *ref;
+ const char *checksum;
+ while (g_hash_table_iter_next (&iter, (gpointer *) &ref, (gpointer *) &checksum))
+ {
+ GHashTable *ref_map = g_hash_table_lookup (collection_map, ref->collection_id);
+
+ if (ref_map == NULL)
+ {
+ ref_map = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
+ g_hash_table_insert (collection_map, ref->collection_id, ref_map);
+ }
+
+ g_hash_table_insert (ref_map, ref->ref_name, (gpointer) checksum);
+ }
+
+ g_autoptr(GVariantBuilder) collection_refs_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sa(s(taya{sv}))}"));
+
+ g_autoptr(GList) ordered_collection_ids = g_hash_table_get_keys (collection_map);
+ ordered_collection_ids = g_list_sort (ordered_collection_ids, (GCompareFunc) strcmp);
+
+ for (GList *collection_iter = ordered_collection_ids; collection_iter; collection_iter = collection_iter->next)
+ {
+ const char *collection_id = collection_iter->data;
+ GHashTable *ref_map = g_hash_table_lookup (collection_map, collection_id);
+
+ gboolean is_main_collection_id = (main_collection_id != NULL && g_str_equal (collection_id, main_collection_id));
+
+ if (!is_main_collection_id)
+ {
+ g_variant_builder_open (collection_refs_builder, G_VARIANT_TYPE ("{sa(s(taya{sv}))}"));
+ g_variant_builder_add (collection_refs_builder, "s", collection_id);
+ g_variant_builder_open (collection_refs_builder, G_VARIANT_TYPE ("a(s(taya{sv}))"));
+ }
+
+ g_autoptr(GList) ordered_refs = g_hash_table_get_keys (ref_map);
+ ordered_refs = g_list_sort (ordered_refs, (GCompareFunc) strcmp);
+
+ for (GList *ref_iter = ordered_refs; ref_iter != NULL; ref_iter = ref_iter->next)
+ {
+ const char *ref = ref_iter->data;
+ const char *commit = g_hash_table_lookup (ref_map, ref);
+ GVariantBuilder *builder = is_main_collection_id ? refs_builder : collection_refs_builder;
+
+ if (!summary_add_ref_entry (self, ref, commit, builder, error))
+ return FALSE;
+
+ if (!is_main_collection_id)
+ collection_map_size++;
+ }
+
+ if (!is_main_collection_id)
+ {
+ g_variant_builder_close (collection_refs_builder); /* array */
+ g_variant_builder_close (collection_refs_builder); /* dict entry */
+ }
+ }
+
+ if (main_collection_id != NULL)
+ g_variant_dict_insert_value (&additional_metadata_builder, OSTREE_SUMMARY_COLLECTION_ID,
+ g_variant_new_string (main_collection_id));
+ if (collection_map_size > 0)
+ g_variant_dict_insert_value (&additional_metadata_builder, OSTREE_SUMMARY_COLLECTION_MAP,
+ g_variant_builder_end (collection_refs_builder));
+ }
+
g_autoptr(GVariant) summary = NULL;
{
g_autoptr(GVariantBuilder) summary_builder =
const char *ref,
const char *checksum);
+#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
+
+_OSTREE_PUBLIC
+void ostree_repo_transaction_set_collection_ref (OstreeRepo *self,
+ const OstreeCollectionRef *ref,
+ const char *checksum);
+
+#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
+
_OSTREE_PUBLIC
gboolean ostree_repo_set_ref_immediate (OstreeRepo *self,
const char *remote,
GCancellable *cancellable,
GError **error);
+#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
+
+_OSTREE_PUBLIC
+gboolean ostree_repo_set_collection_ref_immediate (OstreeRepo *self,
+ const OstreeCollectionRef *ref,
+ const char *checksum,
+ GCancellable *cancellable,
+ GError **error);
+
+#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
+
_OSTREE_PUBLIC
gboolean ostree_repo_has_object (OstreeRepo *self,
OstreeObjectType objtype,
GCancellable *cancellable,
GError **error);
+#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
+
+_OSTREE_PUBLIC
+gboolean ostree_repo_list_collection_refs (OstreeRepo *self,
+ const char *match_collection_id,
+ GHashTable **out_all_refs,
+ GCancellable *cancellable,
+ GError **error);
+
+#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
+
_OSTREE_PUBLIC
void ostree_repo_pull_default_console_progress_changed (OstreeAsyncProgress *progress,
gpointer user_data);
cd ${test_tmpdir}
${CMD_PREFIX} ostree --repo=repo3 prune
find repo3/objects -name '*.commit' > objlist-before-prune
-rm repo3/refs/heads/* repo3/refs/remotes/* -rf
+rm repo3/refs/heads/* repo3/refs/mirrors/* repo3/refs/remotes/* -rf
${CMD_PREFIX} ostree --repo=repo3 prune --refs-only
find repo3/objects -name '*.commit' > objlist-after-prune
if cmp -s objlist-before-prune objlist-after-prune; then