refs: Add a --collections argument to the built-in refs command
authorPhilip Withnall <withnall@endlessm.com>
Wed, 7 Jun 2017 13:51:58 +0000 (14:51 +0100)
committerAtomic Bot <atomic-devel@projectatomic.io>
Mon, 26 Jun 2017 15:56:07 +0000 (15:56 +0000)
Rather than change the output format used by the existing refs command
to output collection IDs in addition to ref names, this functionality has
been hidden behind an --collections argument. If it’s not specified `ostree
refs` will output the same content as before for a given repository. If
it is specified, the collection ID for each ref will be included in the
output as (collection ID, ref name).

Signed-off-by: Philip Withnall <withnall@endlessm.com>
Closes: #924
Approved by: cgwalters

man/ostree-refs.xml
src/ostree/ot-builtin-refs.c

index 43e934ff9c3332731d0de839034c85272651b9e4..726a94ac463fe5676d6d1ba377edd7559b180530 100644 (file)
@@ -83,6 +83,26 @@ Boston, MA 02111-1307, USA.
                  you will then need to <command>ostree prune</command> or <command>ostree admin cleanup</command>.
                 </para></listitem>
             </varlistentry>
+
+            <varlistentry>
+                <term><option>--collections</option></term>
+
+                <listitem><para>
+                  Enable interactions with refs using the combination of their
+                  collection IDs and ref names. When listing refs, this changes
+                  the output format to include collection IDs, and enables
+                  listing remote mirrored refs.</para>
+
+                  <para>When creating refs, the refspec value passed to the
+                  <option>--create</option> option is treated as
+                  <literal>COLLECTION-ID:REF-NAME</literal> and a mirrored ref
+                  is created. (This is an abuse of the refspec syntax.)</para>
+
+                  <para>When deleting refs, all refs whose collection ID equals
+                  the value of the <option>--delete</option> argument are
+                  deleted.
+                </para></listitem>
+            </varlistentry>
         </variablelist>
     </refsect1>
 
index c7ea9a951764a776a2d4f3bfadf6984a8a01ecb9..1942084245299d0ed226bbaaaebc0bdcd3c52e98 100644 (file)
 static gboolean opt_delete;
 static gboolean opt_list;
 static char *opt_create;
+#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
+static gboolean opt_collections;
+#endif  /* OSTREE_ENABLE_EXPERIMENTAL_API */
 
 static GOptionEntry options[] = {
   { "delete", 0, 0, G_OPTION_ARG_NONE, &opt_delete, "Delete refs which match PREFIX, rather than listing them", NULL },
   { "list", 0, 0, G_OPTION_ARG_NONE, &opt_list, "Do not remove the prefix from the refs", NULL },
   { "create", 0, 0, G_OPTION_ARG_STRING, &opt_create, "Create a new ref for an existing commit", "NEWREF" },
+#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
+  { "collections", 'c', 0, G_OPTION_ARG_NONE, &opt_collections, "Enable listing collection IDs for refs", NULL },
+#endif  /* OSTREE_ENABLE_EXPERIMENTAL_API */
   { NULL }
 };
 
+#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
+static gboolean
+do_ref_with_collections (OstreeRepo    *repo,
+                         const char    *refspec_prefix,
+                         GCancellable  *cancellable,
+                         GError       **error)
+{
+  g_autoptr(GHashTable) refs = NULL;  /* (element-type OstreeCollectionRef utf8) */
+  GHashTableIter hashiter;
+  gpointer hashkey, hashvalue;
+  gboolean ret = FALSE;
+
+  if (!ostree_repo_list_collection_refs (repo,
+                                         (!opt_create) ? refspec_prefix : NULL,
+                                         &refs, cancellable, error))
+    goto out;
+
+  if (!opt_delete && !opt_create)
+    {
+      g_hash_table_iter_init (&hashiter, refs);
+      while (g_hash_table_iter_next (&hashiter, &hashkey, &hashvalue))
+        {
+          const OstreeCollectionRef *ref = hashkey;
+          g_print ("(%s, %s)\n", ref->collection_id, ref->ref_name);
+        }
+    }
+  else if (opt_create)
+    {
+      g_autofree char *checksum = NULL;
+      g_autofree char *checksum_existing = NULL;
+
+      if (!ostree_repo_resolve_rev_ext (repo, opt_create, TRUE, OSTREE_REPO_RESOLVE_REV_EXT_NONE, &checksum_existing, error))
+        {
+          if (g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY))
+            {
+              /* A folder exists with the specified ref name,
+               * which is handled by _ostree_repo_write_ref */
+              g_clear_error (error);
+            }
+          else goto out;
+        }
+
+      if (checksum_existing != NULL)
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       "--create specified but ref %s already exists", opt_create);
+          goto out;
+        }
+
+      if (!ostree_repo_resolve_rev (repo, refspec_prefix, FALSE, &checksum, error))
+        goto out;
+
+      /* This is technically an abuse of the refspec syntax: collection IDs
+       * should not be treated like remote names. */
+      g_auto(GStrv) parts = g_strsplit (opt_create, ":", 2);
+      const char *collection_id = parts[0];
+      const char *ref_name = parts[1];
+      if (!ostree_validate_collection_id (collection_id, error))
+        goto out;
+      if (!ostree_validate_rev (ref_name, error))
+        goto out;
+
+      const OstreeCollectionRef ref = { (gchar *) collection_id, (gchar *) ref_name };
+      if (!ostree_repo_set_collection_ref_immediate (repo, &ref, checksum,
+                                                     cancellable, error))
+        goto out;
+    }
+  else
+    /* delete */
+    {
+      g_hash_table_iter_init (&hashiter, refs);
+      while (g_hash_table_iter_next (&hashiter, &hashkey, &hashvalue))
+        {
+          const OstreeCollectionRef *ref = hashkey;
+
+          if (!ostree_repo_set_collection_ref_immediate (repo, ref, NULL,
+                                                         cancellable, error))
+            goto out;
+        }
+    }
+  ret = TRUE;
+ out:
+  return ret;
+}
+#endif  /* OSTREE_ENABLE_EXPERIMENTAL_API */
+
 static gboolean do_ref (OstreeRepo *repo, const char *refspec_prefix, GCancellable *cancellable, GError **error)
 {
   g_autoptr(GHashTable) refs = NULL;
@@ -44,6 +136,11 @@ static gboolean do_ref (OstreeRepo *repo, const char *refspec_prefix, GCancellab
   gpointer hashkey, hashvalue;
   gboolean ret = FALSE;
 
+#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
+  if (opt_collections)
+    return do_ref_with_collections (repo, refspec_prefix, cancellable, error);
+#endif  /* OSTREE_ENABLE_EXPERIMENTAL_API */
+
   if (opt_delete || opt_list)
     {
       if (!ostree_repo_list_refs_ext (repo, refspec_prefix, &refs, OSTREE_REPO_LIST_REFS_EXT_NONE,