[gnome-builder/wip/chergert/git-oop: 3/12] wip



commit 405fe93ce3667e6fb6966e3f80327f213030de5a
Author: Christian Hergert <chergert redhat com>
Date:   Wed Mar 20 11:42:23 2019 -0700

    wip

 src/plugins/git/gbp-git-client.h           | 102 ++++++++------
 src/plugins/git/gbp-git-remote-callbacks.c | 219 +++++++++++++++--------------
 src/plugins/git/gbp-git-remote-callbacks.h |  24 ++--
 src/plugins/git/gbp-git.c                  |  83 +++++++++++
 src/plugins/git/gbp-git.h                  |  88 +++++++-----
 src/plugins/git/gnome-builder-git.c        | 187 ++++++++++++++++++++++++
 6 files changed, 502 insertions(+), 201 deletions(-)
---
diff --git a/src/plugins/git/gbp-git-client.h b/src/plugins/git/gbp-git-client.h
index b96f07840..cb9cd2c53 100644
--- a/src/plugins/git/gbp-git-client.h
+++ b/src/plugins/git/gbp-git-client.h
@@ -35,50 +35,62 @@ typedef enum
 
 G_DECLARE_FINAL_TYPE (GbpGitClient, gbp_git_client, GBP, GIT_CLIENT, IdeObject)
 
-GbpGitClient *gbp_git_client_from_context             (IdeContext           *context);
-void          gbp_git_client_call_async               (GbpGitClient         *self,
-                                                       const gchar          *method,
-                                                       GVariant             *params,
-                                                       GCancellable         *cancellable,
-                                                       GAsyncReadyCallback   callback,
-                                                       gpointer              user_data);
-gboolean      gbp_git_client_call_finish              (GbpGitClient         *self,
-                                                       GAsyncResult         *result,
-                                                       GVariant            **reply,
-                                                       GError              **error);
-void          gbp_git_client_is_ignored_async         (GbpGitClient         *self,
-                                                       const gchar          *path,
-                                                       GCancellable         *cancellable,
-                                                       GAsyncReadyCallback   callback,
-                                                       gpointer              user_data);
-gboolean      gbp_git_client_is_ignored_finish        (GbpGitClient         *self,
-                                                       GAsyncResult         *result,
-                                                       GError              **error);
-void          gbp_git_client_list_status_async        (GbpGitClient         *self,
-                                                       const gchar          *directory_or_file,
-                                                       gboolean              include_descendants,
-                                                       GCancellable         *cancellable,
-                                                       GAsyncReadyCallback   callback,
-                                                       gpointer              user_data);
-GPtrArray    *gbp_git_client_list_status_finish       (GbpGitClient         *self,
-                                                       GAsyncResult         *result,
-                                                       GError              **error);
-void          gbp_git_client_list_refs_by_kind_async  (GbpGitClient         *self,
-                                                       GbpGitRefKind         kind,
-                                                       GCancellable         *cancellable,
-                                                       GAsyncReadyCallback   callback,
-                                                       gpointer              user_data);
-GPtrArray    *gbp_git_client_list_refs_by_kind_finish (GbpGitClient         *self,
-                                                       GAsyncResult         *result,
-                                                       GError              **error);
-void          gbp_git_client_switch_branch_async      (GbpGitClient         *self,
-                                                       const gchar          *branch_name,
-                                                       GCancellable         *cancellable,
-                                                       GAsyncReadyCallback   callback,
-                                                       gpointer              user_data);
-gboolean      gbp_git_client_switch_branch_finish     (GbpGitClient         *self,
-                                                       GAsyncResult         *result,
-                                                       gchar               **switch_to_directory,
-                                                       GError              **error);
+GbpGitClient *gbp_git_client_from_context             (IdeContext             *context);
+void          gbp_git_client_call_async               (GbpGitClient           *self,
+                                                       const gchar            *method,
+                                                       GVariant               *params,
+                                                       GCancellable           *cancellable,
+                                                       GAsyncReadyCallback     callback,
+                                                       gpointer                user_data);
+gboolean      gbp_git_client_call_finish              (GbpGitClient           *self,
+                                                       GAsyncResult           *result,
+                                                       GVariant              **reply,
+                                                       GError                **error);
+void          gbp_git_client_is_ignored_async         (GbpGitClient           *self,
+                                                       const gchar            *path,
+                                                       GCancellable           *cancellable,
+                                                       GAsyncReadyCallback     callback,
+                                                       gpointer                user_data);
+gboolean      gbp_git_client_is_ignored_finish        (GbpGitClient           *self,
+                                                       GAsyncResult           *result,
+                                                       GError                **error);
+void          gbp_git_client_list_status_async        (GbpGitClient           *self,
+                                                       const gchar            *directory_or_file,
+                                                       gboolean                include_descendants,
+                                                       GCancellable           *cancellable,
+                                                       GAsyncReadyCallback     callback,
+                                                       gpointer                user_data);
+GPtrArray    *gbp_git_client_list_status_finish       (GbpGitClient           *self,
+                                                       GAsyncResult           *result,
+                                                       GError                **error);
+void          gbp_git_client_list_refs_by_kind_async  (GbpGitClient           *self,
+                                                       GbpGitRefKind           kind,
+                                                       GCancellable           *cancellable,
+                                                       GAsyncReadyCallback     callback,
+                                                       gpointer                user_data);
+GPtrArray    *gbp_git_client_list_refs_by_kind_finish (GbpGitClient           *self,
+                                                       GAsyncResult           *result,
+                                                       GError                **error);
+void          gbp_git_client_switch_branch_async      (GbpGitClient           *self,
+                                                       const gchar            *branch_name,
+                                                       GCancellable           *cancellable,
+                                                       GAsyncReadyCallback     callback,
+                                                       gpointer                user_data);
+gboolean      gbp_git_client_switch_branch_finish     (GbpGitClient           *self,
+                                                       GAsyncResult           *result,
+                                                       gchar                 **switch_to_directory,
+                                                       GError                **error);
+void          gbp_git_client_clone_url_async          (GbpGitClient           *self,
+                                                       const gchar            *url,
+                                                       GFile                   *destination,
+                                                       GFileProgressCallback   progress,
+                                                       gpointer                progress_data,
+                                                       GDestroyNotify          progress_notify,
+                                                       GCancellable           *cancellable,
+                                                       GAsyncReadyCallback     callback,
+                                                       gpointer                user_data);
+gboolean      gbp_git_client_clone_url_finish         (GbpGitClient           *self,
+                                                       GAsyncResult           *result,
+                                                       GError                **error);
 
 G_END_DECLS
diff --git a/src/plugins/git/gbp-git-remote-callbacks.c b/src/plugins/git/gbp-git-remote-callbacks.c
index 047b9010c..4bdb9b3e7 100644
--- a/src/plugins/git/gbp-git-remote-callbacks.c
+++ b/src/plugins/git/gbp-git-remote-callbacks.c
@@ -1,21 +1,21 @@
-/* gbp-git-remote-callbacks.c
+/* gnome-builder-git.c
  *
- * Copyright 2015-2019 Christian Hergert <christian hergert me>
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
  *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
  *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
  *
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
- * SPDX-License-Identifier: GPL-3.0-or-later
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #define G_LOG_DOMAIN "gbp-git-remote-callbacks"
@@ -29,63 +29,47 @@
 
 struct _GbpGitRemoteCallbacks
 {
-  GgitRemoteCallbacks  parent_instance;
+  GgitRemoteCallbacks    parent_instance;
 
-  IdeNotification     *progress;
-  GString             *body;
-  GgitCredtype         tried;
-  guint                cancelled : 1;
+  GMutex                 mutex;
+  GString               *body;
+  gdouble                progress;
+  guint                  status_source;
+
+  /* bitflags of what we've tried */
+  GgitCredtype           tried;
+
+  guint                  cancelled : 1;
 };
 
 G_DEFINE_TYPE (GbpGitRemoteCallbacks, gbp_git_remote_callbacks, GGIT_TYPE_REMOTE_CALLBACKS)
 
 enum {
-  PROP_0,
-  PROP_PROGRESS,
-  LAST_PROP
+  STATUS,
+  N_SIGNALS
 };
 
-static GParamSpec *properties [LAST_PROP];
+static guint signals [N_SIGNALS];
 
 GgitRemoteCallbacks *
-gbp_git_remote_callbacks_new (IdeNotification *progress)
+gbp_git_remote_callbacks_new (void)
 {
-  g_return_val_if_fail (IDE_IS_NOTIFICATION (progress), NULL);
-
-  return g_object_new (GBP_TYPE_GIT_REMOTE_CALLBACKS,
-                       "progress", progress,
-                       NULL);
+  return g_object_new (GBP_TYPE_GIT_REMOTE_CALLBACKS, NULL);
 }
 
-/**
- * gbp_git_remote_callbacks_get_progress:
- *
- * Gets the #IdeNotification for the operation.
- *
- * Returns: (transfer none): An #IdeNotification.
- *
- * Since: 3.32
- */
-IdeNotification *
-gbp_git_remote_callbacks_get_progress (GbpGitRemoteCallbacks *self)
+static gboolean
+gbp_git_remote_callbacks_do_emit_status (GbpGitRemoteCallbacks *self)
 {
-  g_return_val_if_fail (GBP_IS_GIT_REMOTE_CALLBACKS (self), NULL);
+  g_autofree gchar *message = NULL;
+  gdouble progress = 0.0;
 
-  return self->progress;
-}
+  g_assert (GBP_IS_GIT_REMOTE_CALLBACKS (self));
 
-static void
-gbp_git_remote_callbacks_real_progress (GgitRemoteCallbacks *callbacks,
-                                        const gchar         *message)
-{
-  GbpGitRemoteCallbacks *self = (GbpGitRemoteCallbacks *)callbacks;
+  g_mutex_lock (&self->mutex);
 
-  g_assert (GBP_IS_GIT_REMOTE_CALLBACKS (self));
+  self->status_source = 0;
 
-  if (self->body == NULL)
-    self->body = g_string_new (message);
-  else
-    g_string_append (self->body, message);
+  progress = self->progress;
 
   if (self->body->len > 1)
     {
@@ -100,7 +84,7 @@ gbp_git_remote_callbacks_real_progress (GgitRemoteCallbacks *callbacks,
         {
           if (*endptr == '\n' || *endptr == '\r')
             {
-              message = endptr + 1;
+              message = g_strdup (endptr + 1);
               break;
             }
 
@@ -108,7 +92,43 @@ gbp_git_remote_callbacks_real_progress (GgitRemoteCallbacks *callbacks,
         }
     }
 
-  ide_notification_set_body (self->progress, message);
+  g_mutex_unlock (&self->mutex);
+
+  g_signal_emit (self, signals [STATUS], 0, message, progress);
+
+  return G_SOURCE_REMOVE;
+}
+
+static void
+gbp_git_remote_callbacks_emit_status (GbpGitRemoteCallbacks *self)
+{
+  g_assert (GBP_IS_GIT_REMOTE_CALLBACKS (self));
+
+  g_mutex_lock (&self->mutex);
+  if (self->status_source == 0)
+    self->status_source = g_idle_add_full (G_PRIORITY_HIGH,
+                                           (GSourceFunc)gbp_git_remote_callbacks_do_emit_status,
+                                           g_object_ref (self),
+                                           g_object_unref);
+  g_mutex_unlock (&self->mutex);
+}
+
+static void
+gbp_git_remote_callbacks_real_progress (GgitRemoteCallbacks *callbacks,
+                                        const gchar         *message)
+{
+  GbpGitRemoteCallbacks *self = (GbpGitRemoteCallbacks *)callbacks;
+
+  g_assert (GBP_IS_GIT_REMOTE_CALLBACKS (self));
+
+  g_mutex_lock (&self->mutex);
+  if (self->body == NULL)
+    self->body = g_string_new (message);
+  else
+    g_string_append (self->body, message);
+  g_mutex_unlock (&self->mutex);
+
+  gbp_git_remote_callbacks_emit_status (self);
 }
 
 static void
@@ -130,7 +150,11 @@ gbp_git_remote_callbacks_real_transfer_progress (GgitRemoteCallbacks  *callbacks
   if (total == 0)
     return;
 
-  ide_notification_set_progress (self->progress, (gdouble)received / (gdouble)total);
+  g_mutex_lock (&self->mutex);
+  self->progress = (gdouble)received / (gdouble)total;
+  g_mutex_unlock (&self->mutex);
+
+  gbp_git_remote_callbacks_emit_status (self);
 }
 
 static GgitCred *
@@ -168,6 +192,9 @@ gbp_git_remote_callbacks_real_credentials (GgitRemoteCallbacks  *callbacks,
       cred = ggit_cred_ssh_key_from_agent_new (username_from_url, error);
       ret = GGIT_CRED (cred);
       self->tried |= GGIT_CREDTYPE_SSH_KEY;
+
+      if (ret != NULL)
+        IDE_RETURN (ret);
     }
 
   if ((allowed_types & GGIT_CREDTYPE_SSH_INTERACTIVE) != 0)
@@ -177,15 +204,17 @@ gbp_git_remote_callbacks_real_credentials (GgitRemoteCallbacks  *callbacks,
       cred = ggit_cred_ssh_interactive_new (username_from_url, error);
       ret = GGIT_CRED (cred);
       self->tried |= GGIT_CREDTYPE_SSH_INTERACTIVE;
+
+      if (ret != NULL)
+        IDE_RETURN (ret);
     }
 
-  if (ret == NULL)
-    g_set_error (error,
-                 G_IO_ERROR,
-                 G_IO_ERROR_NOT_SUPPORTED,
-                 _("Builder failed to provide appropriate credentials when cloning repository."));
+  g_set_error (error,
+               G_IO_ERROR,
+               G_IO_ERROR_NOT_SUPPORTED,
+               _("Builder failed to provide appropriate credentials when cloning repository."));
 
-  IDE_RETURN (ret);
+  IDE_RETURN (NULL);
 }
 
 static void
@@ -201,46 +230,11 @@ gbp_git_remote_callbacks_finalize (GObject *object)
       self->body = NULL;
     }
 
-  G_OBJECT_CLASS (gbp_git_remote_callbacks_parent_class)->finalize (object);
-}
-
-static void
-gbp_git_remote_callbacks_get_property (GObject    *object,
-                                       guint       prop_id,
-                                       GValue     *value,
-                                       GParamSpec *pspec)
-{
-  GbpGitRemoteCallbacks *self = GBP_GIT_REMOTE_CALLBACKS (object);
-
-  switch (prop_id)
-    {
-    case PROP_PROGRESS:
-      g_value_set_object (value, gbp_git_remote_callbacks_get_progress (self));
-      break;
-
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-    }
-}
-
-static void
-gbp_git_remote_callbacks_set_property (GObject      *object,
-                                       guint         prop_id,
-                                       const GValue *value,
-                                       GParamSpec   *pspec)
-{
-  GbpGitRemoteCallbacks *self = GBP_GIT_REMOTE_CALLBACKS (object);
+  g_clear_handle_id (&self->status_source, g_source_remove);
 
-  switch (prop_id)
-    {
-    case PROP_PROGRESS:
-      g_clear_object (&self->progress);
-      self->progress = g_value_dup_object (value);
-      break;
+  g_mutex_clear (&self->mutex);
 
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-    }
+  G_OBJECT_CLASS (gbp_git_remote_callbacks_parent_class)->finalize (object);
 }
 
 static void
@@ -250,26 +244,37 @@ gbp_git_remote_callbacks_class_init (GbpGitRemoteCallbacksClass *klass)
   GgitRemoteCallbacksClass *callbacks_class = GGIT_REMOTE_CALLBACKS_CLASS (klass);
 
   object_class->finalize = gbp_git_remote_callbacks_finalize;
-  object_class->get_property = gbp_git_remote_callbacks_get_property;
-  object_class->set_property = gbp_git_remote_callbacks_set_property;
 
   callbacks_class->transfer_progress = gbp_git_remote_callbacks_real_transfer_progress;
   callbacks_class->progress = gbp_git_remote_callbacks_real_progress;
   callbacks_class->credentials = gbp_git_remote_callbacks_real_credentials;
 
-  properties [PROP_PROGRESS] =
-    g_param_spec_object ("progress",
-                         "Progress",
-                         "An IdeNotification instance containing the operation progress.",
-                         IDE_TYPE_NOTIFICATION,
-                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
-
-  g_object_class_install_properties (object_class, LAST_PROP, properties);
+  /**
+   * GbpGitRemoteCallbacks::status:
+   * @self: a GbpGitRemoteCallbacks
+   * @message: the status message string
+   * @progress: the progress for the operation
+   *
+   * This signal is emitted when the progress or the status message changes
+   * for the operation.
+   *
+   * Since: 3.34
+   */
+  signals [STATUS] =
+    g_signal_new ("status",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  0, NULL, NULL, NULL,
+                  G_TYPE_NONE,
+                  2,
+                  G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE,
+                  G_TYPE_DOUBLE);
 }
 
 static void
 gbp_git_remote_callbacks_init (GbpGitRemoteCallbacks *self)
 {
+  g_mutex_init (&self->mutex);
 }
 
 /**
diff --git a/src/plugins/git/gbp-git-remote-callbacks.h b/src/plugins/git/gbp-git-remote-callbacks.h
index 185fb597c..7ddfbb51c 100644
--- a/src/plugins/git/gbp-git-remote-callbacks.h
+++ b/src/plugins/git/gbp-git-remote-callbacks.h
@@ -1,21 +1,21 @@
-/* gbp-git-remote-callbacks.h
+/* gnome-builder-git.c
  *
- * Copyright 2015-2019 Christian Hergert <christian hergert me>
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
  *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
  *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
  *
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
- * SPDX-License-Identifier: GPL-3.0-or-later
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #pragma once
@@ -29,7 +29,7 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (GbpGitRemoteCallbacks, gbp_git_remote_callbacks, GBP, GIT_REMOTE_CALLBACKS, 
GgitRemoteCallbacks)
 
-GgitRemoteCallbacks *gbp_git_remote_callbacks_new          (IdeNotification       *progress);
+GgitRemoteCallbacks *gbp_git_remote_callbacks_new          (void);
 gdouble              gbp_git_remote_callbacks_get_fraction (GbpGitRemoteCallbacks *self);
 IdeNotification     *gbp_git_remote_callbacks_get_progress (GbpGitRemoteCallbacks *self);
 void                 gbp_git_remote_callbacks_cancel       (GbpGitRemoteCallbacks *self);
diff --git a/src/plugins/git/gbp-git.c b/src/plugins/git/gbp-git.c
index 1ab230ec2..88c4fb1cd 100644
--- a/src/plugins/git/gbp-git.c
+++ b/src/plugins/git/gbp-git.c
@@ -258,3 +258,86 @@ gbp_git_list_status_finish (GbpGit        *self,
 
   return g_task_propagate_pointer (G_TASK (result), error);
 }
+
+typedef struct
+{
+  gchar            *url;
+  GFile            *destination;
+  GgitCloneOptions *options;
+} Clone;
+
+static void
+clone_free (Clone *c)
+{
+  g_clear_pointer (&c->url, g_free);
+  g_clear_object (&c->destination);
+  g_clear_object (&c->options);
+  g_slice_free (Clone, c);
+}
+
+static void
+gbp_git_clone_url_worker (GTask        *task,
+                          gpointer      source_object,
+                          gpointer      task_data,
+                          GCancellable *cancellable)
+{
+  Clone *state = task_data;
+  g_autoptr(GError) error = NULL;
+
+  g_assert (G_IS_TASK (task));
+  g_assert (GBP_IS_GIT (source_object));
+  g_assert (state != NULL);
+  g_assert (state->url != NULL);
+  g_assert (state->destination != NULL);
+
+  ggit_repository_clone (state->url,
+                         state->destination,
+                         state->options,
+                         &error);
+
+  if (error != NULL)
+    g_task_return_error (task, g_steal_pointer (&error));
+  else
+    g_task_return_boolean (task, TRUE);
+}
+
+void
+gbp_git_clone_url_async (GbpGit                *self,
+                         const gchar           *url,
+                         GFile                 *destination,
+                         GgitCloneOptions      *options,
+                         GCancellable          *cancellable,
+                         GAsyncReadyCallback    callback,
+                         gpointer               user_data)
+{
+  g_autoptr(GTask) task = NULL;
+  Clone *c;
+
+  g_assert (GBP_IS_GIT (self));
+  g_assert (url != NULL);
+  g_assert (G_IS_FILE (destination));
+  g_assert (!options || GGIT_IS_CLONE_OPTIONS (options));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  c = g_slice_new0 (Clone);
+  c->url = g_strdup (url);
+  c->destination = g_object_ref (destination);
+  c->options = options ? g_object_ref (options) : NULL;
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, gbp_git_clone_url_async);
+  g_task_set_priority (task, G_PRIORITY_LOW);
+  g_task_set_task_data (task, c, (GDestroyNotify)clone_free);
+  g_task_run_in_thread (task, gbp_git_clone_url_worker);
+}
+
+gboolean
+gbp_git_clone_url_finish (GbpGit        *self,
+                          GAsyncResult  *result,
+                          GError       **error)
+{
+  g_assert (GBP_IS_GIT (self));
+  g_assert (G_IS_TASK (result));
+
+  return g_task_propagate_boolean (G_TASK (result), error);
+}
diff --git a/src/plugins/git/gbp-git.h b/src/plugins/git/gbp-git.h
index fb9a59276..63133b51e 100644
--- a/src/plugins/git/gbp-git.h
+++ b/src/plugins/git/gbp-git.h
@@ -20,7 +20,7 @@
 
 #pragma once
 
-#include <gio/gio.h>
+#include <libgit2-glib/ggit.h>
 
 G_BEGIN_DECLS
 
@@ -46,44 +46,58 @@ typedef struct
   guint  is_ignored : 1;
 } GbpGitFile;
 
+typedef void (*GbpGitProgress) (const gchar *message,
+                                gdouble      progress,
+                                gpointer     user_data);
+
 G_DECLARE_FINAL_TYPE (GbpGit, gbp_git, GBP, GIT, GObject)
 
 GbpGit    *gbp_git_new                      (void);
-void       gbp_git_set_workdir              (GbpGit               *self,
-                                             GFile                *workdir);
-void       gbp_git_is_ignored_async         (GbpGit               *self,
-                                             const gchar          *path,
-                                             GCancellable         *cancellable,
-                                             GAsyncReadyCallback   callback,
-                                             gpointer              user_data);
-gboolean   gbp_git_is_ignored_finish        (GbpGit               *self,
-                                             GAsyncResult         *result,
-                                             GError              **error);
-void       gbp_git_list_status_async        (GbpGit               *self,
-                                             const gchar          *directory_or_file,
-                                             gboolean              include_descendants,
-                                             GCancellable         *cancellable,
-                                             GAsyncReadyCallback   callback,
-                                             gpointer              user_data);
-GPtrArray *gbp_git_list_status_finish       (GbpGit               *self,
-                                             GAsyncResult         *result,
-                                             GError              **error);
-void       gbp_git_list_refs_by_kind_async  (GbpGit               *self,
-                                             GbpGitRefKind         kind,
-                                             GCancellable         *cancellable,
-                                             GAsyncReadyCallback   callback,
-                                             gpointer              user_data);
-GPtrArray *gbp_git_list_refs_by_kind_finish (GbpGit               *self,
-                                             GAsyncResult         *result,
-                                             GError              **error);
-void       gbp_git_switch_branch_async      (GbpGit               *self,
-                                             const gchar          *branch_name,
-                                             GCancellable         *cancellable,
-                                             GAsyncReadyCallback   callback,
-                                             gpointer              user_data);
-gboolean   gbp_git_switch_branch_finish     (GbpGit               *self,
-                                             GAsyncResult         *result,
-                                             gchar               **switch_to_directory,
-                                             GError              **error);
+void       gbp_git_set_workdir              (GbpGit                 *self,
+                                             GFile                  *workdir);
+void       gbp_git_is_ignored_async         (GbpGit                 *self,
+                                             const gchar            *path,
+                                             GCancellable           *cancellable,
+                                             GAsyncReadyCallback     callback,
+                                             gpointer                user_data);
+gboolean   gbp_git_is_ignored_finish        (GbpGit                 *self,
+                                             GAsyncResult           *result,
+                                             GError                **error);
+void       gbp_git_list_status_async        (GbpGit                 *self,
+                                             const gchar            *directory_or_file,
+                                             gboolean                include_descendants,
+                                             GCancellable           *cancellable,
+                                             GAsyncReadyCallback     callback,
+                                             gpointer                user_data);
+GPtrArray *gbp_git_list_status_finish       (GbpGit                 *self,
+                                             GAsyncResult           *result,
+                                             GError                **error);
+void       gbp_git_list_refs_by_kind_async  (GbpGit                 *self,
+                                             GbpGitRefKind           kind,
+                                             GCancellable           *cancellable,
+                                             GAsyncReadyCallback     callback,
+                                             gpointer                user_data);
+GPtrArray *gbp_git_list_refs_by_kind_finish (GbpGit                 *self,
+                                             GAsyncResult           *result,
+                                             GError                **error);
+void       gbp_git_switch_branch_async      (GbpGit                 *self,
+                                             const gchar            *branch_name,
+                                             GCancellable           *cancellable,
+                                             GAsyncReadyCallback     callback,
+                                             gpointer                user_data);
+gboolean   gbp_git_switch_branch_finish     (GbpGit                 *self,
+                                             GAsyncResult           *result,
+                                             gchar                 **switch_to_directory,
+                                             GError                **error);
+void       gbp_git_clone_url_async          (GbpGit                 *self,
+                                             const gchar            *url,
+                                             GFile                  *destination,
+                                             GgitCloneOptions       *options,
+                                             GCancellable           *cancellable,
+                                             GAsyncReadyCallback     callback,
+                                             gpointer                user_data);
+gboolean   gbp_git_clone_url_finish         (GbpGit                 *self,
+                                             GAsyncResult           *result,
+                                             GError                **error);
 
 G_END_DECLS
diff --git a/src/plugins/git/gnome-builder-git.c b/src/plugins/git/gnome-builder-git.c
index c7d024b14..88a4fe735 100644
--- a/src/plugins/git/gnome-builder-git.c
+++ b/src/plugins/git/gnome-builder-git.c
@@ -34,6 +34,7 @@
 #include <unistd.h>
 
 #include "gbp-git.h"
+#include "gbp-git-remote-callbacks.h"
 
 static guint      in_flight;
 static gboolean   closing;
@@ -48,6 +49,7 @@ typedef struct
   JsonrpcClient *client;
   GVariant      *id;
   GCancellable  *cancellable;
+  gchar         *token;
   GList          link;
 } ClientOp;
 
@@ -99,6 +101,7 @@ client_op_unref (ClientOp *op)
       g_clear_object (&op->cancellable);
       g_clear_object (&op->client);
       g_clear_pointer (&op->id, g_variant_unref);
+      g_clear_pointer (&op->token, g_free);
       g_queue_unlink (&ops, &op->link);
       g_slice_free (ClientOp, op);
 
@@ -151,6 +154,21 @@ handle_reply_cb (JsonrpcClient *client,
     }
 }
 
+static void
+client_op_notify (ClientOp    *op,
+                  const gchar *method,
+                  GVariant    *reply)
+{
+  g_assert (op != NULL);
+  g_assert (op->client != NULL);
+
+  jsonrpc_client_send_notification (op->client,
+                                    method,
+                                    reply,
+                                    op->cancellable,
+                                    NULL);
+}
+
 static void
 client_op_reply (ClientOp *op,
                  GVariant *reply)
@@ -166,6 +184,29 @@ client_op_reply (ClientOp *op,
                               client_op_ref (op));
 }
 
+static void
+progress_cb (goffset  current_num_bytes,
+             goffset  total_num_bytes,
+             gpointer user_data)
+{
+  ClientOp *op = user_data;
+  g_autoptr(GVariant) reply = NULL;
+  gdouble progress = 0.0;
+
+  g_assert (op != NULL);
+  g_assert (op->client != NULL);
+
+  if (total_num_bytes > 0)
+    progress = (gdouble)current_num_bytes / (gdouble)total_num_bytes;
+
+  reply = JSONRPC_MESSAGE_NEW (
+    "token", JSONRPC_MESSAGE_PUT_STRING (op->token),
+    "progress", JSONRPC_MESSAGE_PUT_DOUBLE (progress)
+  );
+
+  client_op_notify (op, "$/progress", reply);
+}
+
 /* Initialize {{{1 */
 
 static void
@@ -487,6 +528,151 @@ handle_list_refs_by_kind (JsonrpcServer *server,
                                    client_op_ref (op));
 }
 
+/* Clone URL {{{1 */
+
+static void
+handle_clone_url_cb (GbpGit       *git,
+                     GAsyncResult *result,
+                     gpointer      user_data)
+{
+  g_autoptr(ClientOp) op = user_data;
+  g_autoptr(GError) error = NULL;
+
+  g_assert (GBP_IS_GIT (git));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (op != NULL);
+
+  if (!gbp_git_clone_url_finish (git, result, &error))
+    client_op_error (op, error);
+  else
+    client_op_reply (op, g_variant_new_boolean (TRUE));
+}
+
+static void
+handle_clone_url_transfer_cb (GbpGitRemoteCallbacks *callbacks,
+                              const gchar           *message,
+                              ClientOp              *op)
+{
+  g_autoptr(GVariant) reply = NULL;
+
+  g_assert (GBP_GIT_REMOTE_CALLBACKS (callbacks));
+  g_assert (op != NULL);
+
+  reply = JSONRPC_MESSAGE_NEW (
+    "token", JSONRPC_MESSAGE_PUT_STRING (op->token),
+    "message", JSONRPC_MESSAGE_PUT_STRING (message)
+  );
+
+  client_op_notify (op, "$/progress", reply);
+}
+
+static void
+handle_clone_url_transfer_progress_cb (GbpGitRemoteCallbacks *callbacks,
+                                       GgitTransferProgress  *stats,
+                                       ClientOp              *op)
+{
+  g_autoptr(GVariant) reply = NULL;
+  gdouble total;
+  gdouble received;
+  gdouble progress = 0.0;
+
+  g_assert (GBP_GIT_REMOTE_CALLBACKS (callbacks));
+  g_assert (op != NULL);
+
+  total = ggit_transfer_progress_get_total_objects (stats);
+  received = ggit_transfer_progress_get_received_objects (stats);
+
+  if (total != 0.0)
+    progress = received / total;
+
+  reply = JSONRPC_MESSAGE_NEW (
+    "token", JSONRPC_MESSAGE_PUT_STRING (op->token),
+    "progress", JSONRPC_MESSAGE_PUT_DOUBLE (progress)
+  );
+
+  client_op_notify (op, "$/progress", reply);
+}
+
+static void
+handle_clone_url (JsonrpcServer *server,
+                  JsonrpcClient *client,
+                  const gchar   *method,
+                  GVariant      *id,
+                  GVariant      *params,
+                  GbpGit        *git)
+{
+  g_autoptr(ClientOp) op = NULL;
+  g_autoptr(GFile) destination = NULL;
+  g_autoptr(GgitCloneOptions) options = NULL;
+  g_autoptr(GgitRemoteCallbacks) callbacks = NULL;
+  GgitFetchOptions *fetch_options = NULL;
+  const gchar *url = NULL;
+  const gchar *dest_uri = NULL;
+  const gchar *token = NULL;
+  const gchar *branch = NULL;
+  gboolean r;
+
+  g_assert (JSONRPC_IS_SERVER (server));
+  g_assert (JSONRPC_IS_CLIENT (client));
+  g_assert (g_str_equal (method, "git/cloneUrl"));
+  g_assert (id != NULL);
+  g_assert (GBP_IS_GIT (git));
+
+  op = client_op_new (client, id);
+
+  r = JSONRPC_MESSAGE_PARSE (params,
+    "url", JSONRPC_MESSAGE_GET_STRING (&url),
+    "destination", JSONRPC_MESSAGE_GET_STRING (&dest_uri),
+    "token", JSONRPC_MESSAGE_GET_STRING (&token)
+  );
+
+  JSONRPC_MESSAGE_PARSE (params,
+    "branch", JSONRPC_MESSAGE_GET_STRING (&branch)
+  );
+
+  if (!r || !(destination = g_file_new_for_uri (dest_uri)))
+    {
+      client_op_bad_params (op);
+      return;
+    }
+
+  op->token = g_strdup (token);
+
+  callbacks = gbp_git_remote_callbacks_new ();
+
+  g_signal_connect_data (callbacks,
+                         "progress",
+                         G_CALLBACK (handle_clone_url_transfer_cb),
+                         client_op_ref (op),
+                         (GClosureNotify)client_op_unref,
+                         0);
+
+  g_signal_connect_data (callbacks,
+                         "transfer-progress",
+                         G_CALLBACK (handle_clone_url_transfer_progress_cb),
+                         client_op_ref (op),
+                         (GClosureNotify)client_op_unref,
+                         0);
+
+  fetch_options = ggit_fetch_options_new ();
+  ggit_fetch_options_set_remote_callbacks (fetch_options, GGIT_REMOTE_CALLBACKS (callbacks));
+
+  options = ggit_clone_options_new ();
+  ggit_clone_options_set_is_bare (options, FALSE);
+  ggit_clone_options_set_checkout_branch (options, branch ? branch : "master");
+  ggit_clone_options_set_fetch_options (options, fetch_options);
+
+  gbp_git_clone_url_async (git,
+                           url,
+                           destination,
+                           options,
+                           op->cancellable,
+                           (GAsyncReadyCallback)handle_clone_url_cb,
+                           client_op_ref (op));
+
+  g_clear_pointer (&fetch_options, ggit_fetch_options_free);
+}
+
 /* Main Loop and Setup {{{1 */
 
 gint
@@ -528,6 +714,7 @@ main (gint argc,
   jsonrpc_server_add_handler (server, method, (JsonrpcServerHandler)func, g_object_ref (git), g_object_unref)
 
   ADD_HANDLER ("initialize", handle_initialize);
+  ADD_HANDLER ("git/cloneUrl", handle_clone_url);
   ADD_HANDLER ("git/isIgnored", handle_is_ignored);
   ADD_HANDLER ("git/listRefsByKind", handle_list_refs_by_kind);
   ADD_HANDLER ("git/switchBranch", handle_switch_branch);



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