diff --git a/src/libostree/ostree-repo-pull.c b/src/libostree/ostree-repo-pull.c index 41d7ae2c..464d7e66 100644 --- a/src/libostree/ostree-repo-pull.c +++ b/src/libostree/ostree-repo-pull.c @@ -4611,7 +4611,11 @@ static void find_remotes_cb (GObject *obj, * Pass the results to ostree_repo_pull_from_remotes_async() to pull the given @refs * from those remotes. * - * No @options are currently supported. + * The following @options are currently defined: + * + * * `override-commit-ids` (`as`): Array of specific commit IDs to fetch. The nth + * commit ID applies to the nth ref, so this must be the same length as @refs, if + * provided. * * @finders must be a non-empty %NULL-terminated array of the #OstreeRepoFinder * instances to use, or %NULL to use the system default set of finders, which @@ -4640,6 +4644,7 @@ ostree_repo_find_remotes_async (OstreeRepo *self, g_autoptr(OstreeRepoFinder) finder_config = NULL; g_autoptr(OstreeRepoFinder) finder_mount = NULL; g_autoptr(OstreeRepoFinder) finder_avahi = NULL; + g_autofree char **override_commit_ids = NULL; g_return_if_fail (OSTREE_IS_REPO (self)); g_return_if_fail (is_valid_collection_ref_array (refs)); @@ -4649,6 +4654,12 @@ ostree_repo_find_remotes_async (OstreeRepo *self, g_return_if_fail (progress == NULL || OSTREE_IS_ASYNC_PROGRESS (progress)); g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + if (options) + { + (void) g_variant_lookup (options, "override-commit-ids", "^a&s", &override_commit_ids); + g_return_if_fail (override_commit_ids == NULL || g_strv_length ((gchar **) refs) == g_strv_length (override_commit_ids)); + } + /* Set up a task for the whole operation. */ task = g_task_new (self, cancellable, callback, user_data); g_task_set_source_tag (task, ostree_repo_find_remotes_async); @@ -4856,6 +4867,7 @@ find_remotes_cb (GObject *obj, g_autoptr(OstreeFetcher) fetcher = NULL; g_autofree const gchar **ref_to_latest_commit = NULL; /* indexed as @refs; (element-type commit-checksum) */ gsize n_refs; + g_autofree char **override_commit_ids = NULL; g_autoptr(GPtrArray) remotes_to_remove = NULL; /* (element-type OstreeRemote) */ g_autoptr(GPtrArray) final_results = NULL; /* (element-type OstreeRepoFinderResult) */ @@ -4888,13 +4900,10 @@ find_remotes_cb (GObject *obj, * to be %NULL-safe. */ g_ptr_array_set_free_func (results, (GDestroyNotify) repo_finder_result_free0); - /* FIXME: Add support for options: - * - override-commit-ids (allow downgrades) - * - * Use case: multiple pulls of separate subdirs; want them to use the same - * configuration. - * Use case: downgrading a flatpak app. - */ + if (data->options) + { + (void) g_variant_lookup (data->options, "override-commit-ids", "^a&s", &override_commit_ids); + } /* FIXME: In future, we also want to pull static delta superblocks in this * phase, so that we have all the metadata we need for accurate size @@ -5144,10 +5153,11 @@ find_remotes_cb (GObject *obj, * differences between remotes: two remotes could both contain ref R, but one * remote could be outdated compared to the other, and point to an older * commit. For each ref, we want to find the most recent commit any remote - * points to for it. + * points to for it (unless override-commit-ids was used). * * @ref_to_latest_commit is indexed by @ref_index, and its values are the - * latest checksum for each ref. */ + * latest checksum for each ref. If override-commit-ids was used, + * @ref_to_latest_commit won't be initialized or used.*/ ref_to_latest_commit = g_new0 (const gchar *, n_refs); for (i = 0; i < n_refs; i++) @@ -5157,6 +5167,13 @@ find_remotes_cb (GObject *obj, const CommitMetadata *latest_commit_metadata = NULL; g_autofree gchar *latest_commit_timestamp_str = NULL; + if (override_commit_ids) + { + g_debug ("%s: Using specified commit ‘%s’ for ref (%s, %s).", + G_STRFUNC, override_commit_ids[i], refs[i]->collection_id, refs[i]->ref_name); + continue; + } + for (j = 0; j < results->len; j++) { const CommitMetadata *candidate_commit_metadata; @@ -5217,26 +5234,37 @@ find_remotes_cb (GObject *obj, ostree_collection_ref_equal, (GDestroyNotify) ostree_collection_ref_free, g_free); - n_latest_refs = 0; - for (j = 0; refs[j] != NULL; j++) + if (override_commit_ids) { - const gchar *latest_commit_for_ref = ref_to_latest_commit[j]; - - if (pointer_table_get (refs_and_remotes_table, j, i) != latest_commit_for_ref) - latest_commit_for_ref = NULL; - if (latest_commit_for_ref != NULL) - n_latest_refs++; - - g_hash_table_insert (validated_ref_to_checksum, ostree_collection_ref_dup (refs[j]), g_strdup (latest_commit_for_ref)); + for (j = 0; refs[j] != NULL; j++) + g_hash_table_insert (validated_ref_to_checksum, ostree_collection_ref_dup (refs[j]), + g_strdup (override_commit_ids[j])); } - - if (n_latest_refs == 0) + else { - g_debug ("%s: Omitting remote ‘%s’ from results as none of its refs are new enough.", - G_STRFUNC, result->remote->name); - ostree_repo_finder_result_free (g_steal_pointer (&g_ptr_array_index (results, i))); - continue; + n_latest_refs = 0; + + for (j = 0; refs[j] != NULL; j++) + { + const gchar *latest_commit_for_ref = ref_to_latest_commit[j]; + + if (pointer_table_get (refs_and_remotes_table, j, i) != latest_commit_for_ref) + latest_commit_for_ref = NULL; + if (latest_commit_for_ref != NULL) + n_latest_refs++; + + g_hash_table_insert (validated_ref_to_checksum, ostree_collection_ref_dup (refs[j]), + g_strdup (latest_commit_for_ref)); + } + + if (n_latest_refs == 0) + { + g_debug ("%s: Omitting remote ‘%s’ from results as none of its refs are new enough.", + G_STRFUNC, result->remote->name); + ostree_repo_finder_result_free (g_steal_pointer (&g_ptr_array_index (results, i))); + continue; + } } result->ref_to_checksum = g_steal_pointer (&validated_ref_to_checksum);