[ostree] core: Add "refspec" which is remote:refname



commit 1ba852ebaa3f73163930e410783695c99a8d7bcb
Author: Colin Walters <walters verbum org>
Date:   Sat Jun 29 11:42:33 2013 -0400

    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 |  223 +++++++++++++++++++++++++++++++------------
 src/libostree/ostree-repo.h |    7 +-
 4 files changed, 237 insertions(+), 68 deletions(-)
---
diff --git a/src/libostree/ostree-core.c b/src/libostree/ostree-core.c
index 9a90da5..642154b 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 (&regex_initialized))
+    {
+      regex = g_regex_new ("^(" OSTREE_REF_FRAGMENT_REGEXP ":)?(" OSTREE_REF_REGEXP ")$", 0, 0, NULL);
+      g_assert (regex);
+      g_once_init_leave (&regex_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 (g_once_init_enter (&regex_initialized))
+    {
+      regex = g_regex_new ("^" OSTREE_REF_REGEXP "$", 0, 0, NULL);
+      g_assert (regex);
+      g_once_init_leave (&regex_initialized, 1);
+    }
 
-  if (components->len == 0)
+  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 b3f761e..93ec5ec 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 13b95e1..31d25fb 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 (child)
+    {
+      if ((ret_rev = gs_file_load_contents_utf8 (child, NULL, &temp_error)) == NULL)
         {
-          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_propagate_error (error, temp_error);
-              g_prefix_error (error, "Couldn't open ref '%s': ", gs_file_get_path_cached (child));
+              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;
 
-          g_strchomp (ret_rev);
-          if (!ostree_validate_checksum_string (ret_rev, error))
+          if (!ostree_parse_refspec (refspec, &remote, &ref, error))
+            goto out;
+          
+          if (!resolve_refspec (self, remote, ref, allow_noent,
+                                &ret_rev, error))
             goto out;
         }
     }
 
-  ot_transfer_out_value(sha256, &ret_rev);
   ret = TRUE;
+  ot_transfer_out_value (out_rev, &ret_rev);
  out:
   return ret;
 }
 
+
 static gboolean
 write_checksum_file (GFile *parentdir,
                      const char *name,
@@ -1838,6 +1916,27 @@ ostree_repo_write_ref (OstreeRepo  *self,
 }
 
 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,
                           const char   *parent,
diff --git a/src/libostree/ostree-repo.h b/src/libostree/ostree-repo.h
index 4b11c81..e10c78f 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,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]