From 1ba852ebaa3f73163930e410783695c99a8d7bcb Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sat, 29 Jun 2013 11:42:33 -0400 Subject: [PATCH] core: Add "refspec" which is remote:refname This allows an unambiguous reference; otherwise, it was too easy to have confusion between local heads and remotes. --- src/libostree/ostree-core.c | 70 ++++++++++- src/libostree/ostree-core.h | 5 + src/libostree/ostree-repo.c | 239 +++++++++++++++++++++++++----------- src/libostree/ostree-repo.h | 7 +- 4 files changed, 245 insertions(+), 76 deletions(-) diff --git a/src/libostree/ostree-core.c b/src/libostree/ostree-core.c index 9a90da57..642154ba 100644 --- a/src/libostree/ostree-core.c +++ b/src/libostree/ostree-core.c @@ -61,25 +61,85 @@ ostree_validate_checksum_string (const char *sha256, return ostree_validate_structureof_checksum_string (sha256, error); } +#define OSTREE_REF_FRAGMENT_REGEXP "[-_\\w\\d]+" +#define OSTREE_REF_REGEXP "(?:" OSTREE_REF_FRAGMENT_REGEXP "/)*" OSTREE_REF_FRAGMENT_REGEXP + +gboolean +ostree_parse_refspec (const char *refspec, + char **out_remote, + char **out_ref, + GError **error) +{ + gboolean ret = FALSE; + GMatchInfo *match = NULL; + char *remote; + + static gsize regex_initialized; + static GRegex *regex; + + if (g_once_init_enter (®ex_initialized)) + { + regex = g_regex_new ("^(" OSTREE_REF_FRAGMENT_REGEXP ":)?(" OSTREE_REF_REGEXP ")$", 0, 0, NULL); + g_assert (regex); + g_once_init_leave (®ex_initialized, 1); + } + + if (!g_regex_match (regex, refspec, 0, &match)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Invalid refspec %s", refspec); + goto out; + } + + remote = g_match_info_fetch (match, 1); + if (*remote == '\0') + { + g_clear_pointer (&remote, g_free); + } + else + { + /* Trim the : */ + remote[strlen(remote)-1] = '\0'; + } + + ret = TRUE; + *out_remote = remote; + *out_ref = g_match_info_fetch (match, 2); + out: + if (match) + g_match_info_unref (match); + return ret; +} + gboolean ostree_validate_rev (const char *rev, GError **error) { gboolean ret = FALSE; - ot_lptrarray GPtrArray *components = NULL; + gs_unref_ptrarray GPtrArray *components = NULL; + GMatchInfo *match = NULL; - if (!ot_util_path_split_validate (rev, &components, error)) - goto out; + static gsize regex_initialized; + static GRegex *regex; - if (components->len == 0) + if (g_once_init_enter (®ex_initialized)) + { + regex = g_regex_new ("^" OSTREE_REF_REGEXP "$", 0, 0, NULL); + g_assert (regex); + g_once_init_leave (®ex_initialized, 1); + } + + if (!g_regex_match (regex, rev, 0, &match)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Invalid empty rev"); + "Invalid ref name %s", rev); goto out; } ret = TRUE; out: + if (match) + g_match_info_unref (match); return ret; } diff --git a/src/libostree/ostree-core.h b/src/libostree/ostree-core.h index b3f761e8..93ec5ec7 100644 --- a/src/libostree/ostree-core.h +++ b/src/libostree/ostree-core.h @@ -122,6 +122,11 @@ int ostree_cmp_checksum_bytes (const guchar *a, const guchar *b); gboolean ostree_validate_rev (const char *rev, GError **error); +gboolean ostree_parse_refspec (const char *refspec, + char **out_remote, + char **out_ref, + GError **error); + void ostree_checksum_update_meta (GChecksum *checksum, GFileInfo *file_info, GVariant *xattrs); const char * ostree_object_type_to_string (OstreeObjectType objtype); diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c index 13b95e1e..31d25fb7 100644 --- a/src/libostree/ostree-repo.c +++ b/src/libostree/ostree-repo.c @@ -284,7 +284,7 @@ parse_rev_file (OstreeRepo *self, } static gboolean -find_rev_in_remotes (OstreeRepo *self, +find_ref_in_remotes (OstreeRepo *self, const char *rev, GFile **out_file, GError **error) @@ -325,112 +325,190 @@ find_rev_in_remotes (OstreeRepo *self, return ret; } -gboolean -ostree_repo_resolve_rev (OstreeRepo *self, - const char *rev, - gboolean allow_noent, - char **sha256, - GError **error) +static gboolean +resolve_refspec (OstreeRepo *self, + const char *remote, + const char *ref, + gboolean allow_noent, + char **out_rev, + GError **error); + +static gboolean +resolve_refspec_fallback (OstreeRepo *self, + const char *remote, + const char *ref, + gboolean allow_noent, + char **out_rev, + GCancellable *cancellable, + GError **error) { gboolean ret = FALSE; + gs_free char *ret_rev = NULL; + + if (self->parent_repo) + { + if (!resolve_refspec (self->parent_repo, remote, ref, + allow_noent, &ret_rev, error)) + goto out; + } + else if (!allow_noent) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Refspec '%s%s%s' not found", + remote ? remote : "", + remote ? ":" : "", + ref); + goto out; + } + + ret = TRUE; + ot_transfer_out_value (out_rev, &ret_rev); + out: + return ret; +} + +static gboolean +resolve_refspec (OstreeRepo *self, + const char *remote, + const char *ref, + gboolean allow_noent, + char **out_rev, + GError **error) +{ + gboolean ret = FALSE; + __attribute__((unused)) GCancellable *cancellable = NULL; GError *temp_error = NULL; ot_lfree char *tmp = NULL; ot_lfree char *tmp2 = NULL; ot_lfree char *ret_rev = NULL; ot_lobj GFile *child = NULL; ot_lobj GFile *origindir = NULL; - ot_lvariant GVariant *commit = NULL; - ot_lvariant GVariant *parent_csum_v = NULL; - g_return_val_if_fail (rev != NULL, FALSE); - - if (!ostree_validate_rev (rev, error)) - goto out; + g_return_val_if_fail (ref != NULL, FALSE); /* We intentionally don't allow a ref that looks like a checksum */ - if (ostree_validate_checksum_string (rev, NULL)) + if (ostree_validate_checksum_string (ref, NULL)) { - ret_rev = g_strdup (rev); + ret_rev = g_strdup (ref); } - else if (g_str_has_suffix (rev, "^")) + else if (remote != NULL) { - tmp = g_strdup (rev); - tmp[strlen(tmp) - 1] = '\0'; - - if (!ostree_repo_resolve_rev (self, tmp, allow_noent, &tmp2, error)) - goto out; - - if (!ostree_repo_load_variant (self, OSTREE_OBJECT_TYPE_COMMIT, tmp2, &commit, error)) - goto out; - - g_variant_get_child (commit, 1, "@ay", &parent_csum_v); - if (g_variant_n_children (parent_csum_v) == 0) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Commit %s has no parent", tmp2); - goto out; - } - ret_rev = ostree_checksum_from_bytes_v (parent_csum_v); + child = ot_gfile_resolve_path_printf (self->remote_heads_dir, "%s/%s", + remote, ref); + if (!g_file_query_exists (child, NULL)) + g_clear_object (&child); } else { - child = g_file_resolve_relative_path (self->local_heads_dir, rev); + child = g_file_resolve_relative_path (self->local_heads_dir, ref); if (!g_file_query_exists (child, NULL)) { g_clear_object (&child); - child = g_file_resolve_relative_path (self->remote_heads_dir, rev); + child = g_file_resolve_relative_path (self->remote_heads_dir, ref); if (!g_file_query_exists (child, NULL)) { g_clear_object (&child); - if (!find_rev_in_remotes (self, rev, &child, error)) + if (!find_ref_in_remotes (self, ref, &child, error)) goto out; - - if (child == NULL) - { - if (self->parent_repo) - { - if (!ostree_repo_resolve_rev (self->parent_repo, rev, - allow_noent, &ret_rev, - error)) - goto out; - } - else if (!allow_noent) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Rev '%s' not found", rev); - goto out; - } - else - g_clear_object (&child); - } } } - - if (child) - { - if ((ret_rev = gs_file_load_contents_utf8 (child, NULL, &temp_error)) == NULL) - { - g_propagate_error (error, temp_error); - g_prefix_error (error, "Couldn't open ref '%s': ", gs_file_get_path_cached (child)); - goto out; - } - - g_strchomp (ret_rev); - if (!ostree_validate_checksum_string (ret_rev, error)) - goto out; - } } - ot_transfer_out_value(sha256, &ret_rev); + if (child) + { + if ((ret_rev = gs_file_load_contents_utf8 (child, NULL, &temp_error)) == NULL) + { + g_propagate_error (error, temp_error); + g_prefix_error (error, "Couldn't open ref '%s': ", gs_file_get_path_cached (child)); + goto out; + } + + g_strchomp (ret_rev); + if (!ostree_validate_checksum_string (ret_rev, error)) + goto out; + } + else + { + if (!resolve_refspec_fallback (self, remote, ref, allow_noent, + &ret_rev, cancellable, error)) + goto out; + } + + ot_transfer_out_value (out_rev, &ret_rev); ret = TRUE; out: return ret; } +gboolean +ostree_repo_resolve_rev (OstreeRepo *self, + const char *refspec, + gboolean allow_noent, + char **out_rev, + GError **error) +{ + gboolean ret = FALSE; + gs_free char *ret_rev = NULL; + + g_return_val_if_fail (refspec != NULL, FALSE); + + if (ostree_validate_checksum_string (refspec, NULL)) + { + ret_rev = g_strdup (refspec); + } + else + { + if (g_str_has_suffix (refspec, "^")) + { + gs_free char *parent_refspec = NULL; + gs_free char *parent_rev = NULL; + gs_unref_variant GVariant *commit = NULL; + gs_unref_variant GVariant *parent_csum_v = NULL; + + parent_refspec = g_strdup (refspec); + parent_refspec[strlen(parent_refspec) - 1] = '\0'; + + if (!ostree_repo_resolve_rev (self, parent_refspec, allow_noent, &parent_rev, error)) + goto out; + + if (!ostree_repo_load_variant (self, OSTREE_OBJECT_TYPE_COMMIT, parent_rev, + &commit, error)) + goto out; + + g_variant_get_child (commit, 1, "@ay", &parent_csum_v); + if (g_variant_n_children (parent_csum_v) == 0) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Commit %s has no parent", parent_rev); + goto out; + } + ret_rev = ostree_checksum_from_bytes_v (parent_csum_v); + } + else + { + gs_free char *remote = NULL; + gs_free char *ref = NULL; + + if (!ostree_parse_refspec (refspec, &remote, &ref, error)) + goto out; + + if (!resolve_refspec (self, remote, ref, allow_noent, + &ret_rev, error)) + goto out; + } + } + + ret = TRUE; + ot_transfer_out_value (out_rev, &ret_rev); + out: + return ret; +} + + static gboolean write_checksum_file (GFile *parentdir, const char *name, @@ -1837,6 +1915,27 @@ ostree_repo_write_ref (OstreeRepo *self, return ret; } +gboolean +ostree_repo_write_refspec (OstreeRepo *self, + const char *refspec, + const char *rev, + GError **error) +{ + gboolean ret = FALSE; + gs_free char *remote = NULL; + gs_free char *ref = NULL; + + if (!ostree_parse_refspec (refspec, &remote, &ref, error)) + goto out; + + if (!ostree_repo_write_ref (self, remote, ref, rev, error)) + goto out; + + ret = TRUE; + out: + return ret; +} + gboolean ostree_repo_stage_commit (OstreeRepo *self, const char *branch, diff --git a/src/libostree/ostree-repo.h b/src/libostree/ostree-repo.h index 4b11c81d..e10c78f3 100644 --- a/src/libostree/ostree-repo.h +++ b/src/libostree/ostree-repo.h @@ -153,7 +153,7 @@ gboolean ostree_repo_stage_content_finish (OstreeRepo *self, GError **error); gboolean ostree_repo_resolve_rev (OstreeRepo *self, - const char *rev, + const char *refspec, gboolean allow_noent, char **out_resolved, GError **error); @@ -164,6 +164,11 @@ gboolean ostree_repo_write_ref (OstreeRepo *self, const char *rev, GError **error); +gboolean ostree_repo_write_refspec (OstreeRepo *self, + const char *refspec, + const char *rev, + GError **error); + gboolean ostree_repo_list_all_refs (OstreeRepo *repo, GHashTable **out_all_refs, GCancellable *cancellable,