[gnome-builder: 85/139] git: port to new libide designs



commit 2ebce21fb4737d700111e7900d3177b2bda5792b
Author: Christian Hergert <chergert redhat com>
Date:   Wed Jan 9 17:23:08 2019 -0800

    git: port to new libide designs
    
    This ports the git plugin to use new libide designs. It also adjusts the
    prefix for the plugin to Gbp from Ide (which was holdoff from the original
    libide days).
    
    Some components have been simplified, such as the buffer change monitor
    integration now provided via the IdeBufferAddin extension.
    
    The new index monitor will also allow us to be a bit more clever in the
    future about tracking changes to activate branches and commits. However,
    it does not do that yet.

 src/plugins/git/gbp-git-buffer-addin.c             | 123 +++
 ...-git-genesis-addin.h => gbp-git-buffer-addin.h} |  10 +-
 ...e-monitor.c => gbp-git-buffer-change-monitor.c} | 354 ++++----
 ...e-monitor.h => gbp-git-buffer-change-monitor.h} |  13 +-
 ...ency-updater.c => gbp-git-dependency-updater.c} |  47 +-
 ...ency-updater.h => gbp-git-dependency-updater.h} |   8 +-
 src/plugins/git/gbp-git-index-monitor.c            | 140 +++
 src/plugins/git/gbp-git-index-monitor.h            |  33 +
 ...t-pipeline-addin.c => gbp-git-pipeline-addin.c} |  41 +-
 ...t-pipeline-addin.h => gbp-git-pipeline-addin.h} |   8 +-
 src/plugins/git/gbp-git-remote-callbacks.c         | 265 ++++++
 ...mote-callbacks.h => gbp-git-remote-callbacks.h} |  17 +-
 ...submodule-stage.c => gbp-git-submodule-stage.c} |  60 +-
 ...submodule-stage.h => gbp-git-submodule-stage.h} |  12 +-
 src/plugins/git/gbp-git-vcs-cloner.c               | 317 +++++++
 .../git/{ide-git-vcs.h => gbp-git-vcs-cloner.h}    |  10 +-
 .../{ide-git-vcs-config.c => gbp-git-vcs-config.c} |  67 +-
 .../{ide-git-vcs-config.h => gbp-git-vcs-config.h} |  10 +-
 ...vcs-initializer.c => gbp-git-vcs-initializer.c} |  41 +-
 ...vcs-initializer.h => gbp-git-vcs-initializer.h} |   8 +-
 src/plugins/git/gbp-git-vcs.c                      | 543 ++++++++++++
 .../git/{ide-git-clone-widget.h => gbp-git-vcs.h}  |  21 +-
 src/plugins/git/gbp-git-workbench-addin.c          | 386 +++++++++
 src/plugins/git/gbp-git-workbench-addin.h          |  31 +
 src/plugins/git/{ide-git-plugin.c => git-plugin.c} |  66 +-
 src/plugins/git/git.gresource.xml                  |   6 +-
 src/plugins/git/git.plugin                         |  10 +-
 src/plugins/git/ide-git-clone-widget.c             | 589 -------------
 src/plugins/git/ide-git-clone-widget.ui            | 174 ----
 src/plugins/git/ide-git-genesis-addin.c            | 221 -----
 src/plugins/git/ide-git-remote-callbacks.c         | 288 -------
 src/plugins/git/ide-git-vcs.c                      | 945 ---------------------
 src/plugins/git/meson.build                        |  53 +-
 src/plugins/git/themes/shared.css                  |   5 -
 34 files changed, 2292 insertions(+), 2630 deletions(-)
---
diff --git a/src/plugins/git/gbp-git-buffer-addin.c b/src/plugins/git/gbp-git-buffer-addin.c
new file mode 100644
index 000000000..7d7038477
--- /dev/null
+++ b/src/plugins/git/gbp-git-buffer-addin.c
@@ -0,0 +1,123 @@
+/* gbp-git-buffer-addin.c
+ *
+ * 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 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
+ */
+
+#define G_LOG_DOMAIN "gbp-git-buffer-addin"
+
+#include "config.h"
+
+#include <libgit2-glib/ggit.h>
+#include <libide-vcs.h>
+
+#include "gbp-git-buffer-addin.h"
+#include "gbp-git-buffer-change-monitor.h"
+#include "gbp-git-vcs.h"
+
+struct _GbpGitBufferAddin
+{
+  GObject                    parent_instance;
+  GbpGitBufferChangeMonitor *monitor;
+};
+
+static void
+gbp_git_buffer_addin_file_laoded (IdeBufferAddin *addin,
+                                  IdeBuffer      *buffer,
+                                  GFile          *file)
+{
+  GbpGitBufferAddin *self = (GbpGitBufferAddin *)addin;
+  g_autoptr(GbpGitBufferChangeMonitor) monitor = NULL;
+  g_autoptr(IdeContext) context = NULL;
+  GgitRepository *repository;
+  IdeObjectBox *box;
+  IdeVcs *vcs;
+
+  g_assert (GBP_IS_GIT_BUFFER_ADDIN (self));
+  g_assert (IDE_IS_BUFFER (buffer));
+  g_assert (G_IS_FILE (file));
+
+  context = ide_buffer_ref_context (buffer);
+  vcs = ide_context_peek_child_typed (context, IDE_TYPE_VCS);
+  if (!GBP_IS_GIT_VCS (vcs))
+    return;
+
+  if (!(repository = gbp_git_vcs_get_repository (GBP_GIT_VCS (vcs))))
+    return;
+
+  self->monitor = g_object_new (GBP_TYPE_GIT_BUFFER_CHANGE_MONITOR,
+                                "buffer", buffer,
+                                "repository", repository,
+                                NULL);
+
+  box = ide_object_box_from_object (G_OBJECT (buffer));
+  ide_object_append (IDE_OBJECT (box), IDE_OBJECT (self->monitor));
+
+  ide_buffer_set_change_monitor (buffer, IDE_BUFFER_CHANGE_MONITOR (self->monitor));
+}
+
+static void
+gbp_git_buffer_addin_file_saved (IdeBufferAddin *addin,
+                                 IdeBuffer      *buffer,
+                                 GFile          *file)
+{
+  GbpGitBufferAddin *self = (GbpGitBufferAddin *)addin;
+
+  g_assert (GBP_IS_GIT_BUFFER_ADDIN (self));
+  g_assert (IDE_IS_BUFFER (buffer));
+  g_assert (G_IS_FILE (file));
+
+  if (self->monitor != NULL)
+    ide_buffer_change_monitor_reload (IDE_BUFFER_CHANGE_MONITOR (self->monitor));
+}
+
+static void
+gbp_git_buffer_addin_unload (IdeBufferAddin *addin,
+                             IdeBuffer      *buffer)
+{
+  GbpGitBufferAddin *self = (GbpGitBufferAddin *)addin;
+
+  g_assert (GBP_IS_GIT_BUFFER_ADDIN (self));
+  g_assert (IDE_IS_BUFFER (buffer));
+
+  if (self->monitor != NULL)
+    {
+      ide_buffer_set_change_monitor (buffer, NULL);
+      ide_clear_and_destroy_object (&self->monitor);
+    }
+}
+
+static void
+buffer_addin_iface_init (IdeBufferAddinInterface *iface)
+{
+  iface->file_loaded = gbp_git_buffer_addin_file_laoded;
+  iface->file_saved = gbp_git_buffer_addin_file_saved;
+  iface->unload = gbp_git_buffer_addin_unload;
+}
+
+G_DEFINE_TYPE_WITH_CODE (GbpGitBufferAddin, gbp_git_buffer_addin, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (IDE_TYPE_BUFFER_ADDIN, buffer_addin_iface_init))
+
+static void
+gbp_git_buffer_addin_class_init (GbpGitBufferAddinClass *klass)
+{
+}
+
+static void
+gbp_git_buffer_addin_init (GbpGitBufferAddin *self)
+{
+}
diff --git a/src/plugins/git/ide-git-genesis-addin.h b/src/plugins/git/gbp-git-buffer-addin.h
similarity index 72%
rename from src/plugins/git/ide-git-genesis-addin.h
rename to src/plugins/git/gbp-git-buffer-addin.h
index c30b81cb9..3ede066e2 100644
--- a/src/plugins/git/ide-git-genesis-addin.h
+++ b/src/plugins/git/gbp-git-buffer-addin.h
@@ -1,6 +1,6 @@
-/* ide-git-genesis-addin.h
+/* gbp-git-buffer-addin.h
  *
- * Copyright 2015-2019 Christian Hergert <chergert redhat com>
+ * 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
@@ -20,12 +20,12 @@
 
 #pragma once
 
-#include <gtk/gtk.h>
+#include <glib-object.h>
 
 G_BEGIN_DECLS
 
-#define IDE_TYPE_GIT_GENESIS_ADDIN (ide_git_genesis_addin_get_type())
+#define GBP_TYPE_GIT_BUFFER_ADDIN (gbp_git_buffer_addin_get_type())
 
-G_DECLARE_FINAL_TYPE (IdeGitGenesisAddin, ide_git_genesis_addin, IDE, GIT_GENESIS_ADDIN, GObject)
+G_DECLARE_FINAL_TYPE (GbpGitBufferAddin, gbp_git_buffer_addin, GBP, GIT_BUFFER_ADDIN, GObject)
 
 G_END_DECLS
diff --git a/src/plugins/git/ide-git-buffer-change-monitor.c b/src/plugins/git/gbp-git-buffer-change-monitor.c
similarity index 70%
rename from src/plugins/git/ide-git-buffer-change-monitor.c
rename to src/plugins/git/gbp-git-buffer-change-monitor.c
index 67868eee5..6f73901b6 100644
--- a/src/plugins/git/ide-git-buffer-change-monitor.c
+++ b/src/plugins/git/gbp-git-buffer-change-monitor.c
@@ -1,4 +1,4 @@
-/* ide-git-buffer-change-monitor.c
+/* gbp-git-buffer-change-monitor.c
  *
  * Copyright 2015-2019 Christian Hergert <christian hergert me>
  *
@@ -18,46 +18,43 @@
  * SPDX-License-Identifier: GPL-3.0-or-later
  */
 
-#define G_LOG_DOMAIN "ide-git-buffer-change-monitor"
+#define G_LOG_DOMAIN "gbp-git-buffer-change-monitor"
 
 #include <dazzle.h>
 #include <glib/gi18n.h>
 #include <libgit2-glib/ggit.h>
 #include <stdlib.h>
 
-#include "ide-git-buffer-change-monitor.h"
-#include "ide-git-vcs.h"
+#include "gbp-git-buffer-change-monitor.h"
+#include "gbp-git-vcs.h"
 
 #define DELAY_CHANGED_SEC 1
 
 /**
- * SECTION:idegitbufferchangemonitor
+ * SECTION:gbp-git-buffer-change-monitor
  *
- * This module provides line change monitoring when used in conjunction with an IdeGitVcs.
- * The changes are generated by comparing the buffer contents to the version found inside of
- * the git repository.
+ * This module provides line change monitoring when used in conjunction with an
+ * GbpGitVcs.  The changes are generated by comparing the buffer contents to
+ * the version found inside of the git repository.
  *
- * To enable us to avoid blocking the main loop, the actual diff is performed in a background
- * thread. To avoid threading issues with the rest of LibIDE, this module creates a copy of the
- * loaded repository. A single thread will be dispatched for the context and all reload tasks
- * will be performed from that thread.
+ * To enable us to avoid blocking the main loop, the actual diff is performed
+ * in a background thread. To avoid threading issues with the rest of LibGBP,
+ * this module creates a copy of the loaded repository. A single thread will be
+ * dispatched for the context and all reload tasks will be performed from that
+ * thread.
  *
- * Upon completion of the diff, the results will be passed back to the primary thread and the
- * state updated for use by line change renderer in the source view.
- *
- * TODO: Move the thread work into ide_thread_pool?
+ * Upon completion of the diff, the results will be passed back to the primary
+ * thread and the state updated for use by line change renderer in the source
+ * view.
  *
  * Since: 3.32
  */
 
-struct _IdeGitBufferChangeMonitor
+struct _GbpGitBufferChangeMonitor
 {
   IdeBufferChangeMonitor  parent_instance;
 
   DzlSignalGroup         *signal_group;
-  DzlSignalGroup         *vcs_signal_group;
-
-  IdeBuffer              *buffer;
 
   GgitRepository         *repository;
   GArray                 *lines;
@@ -80,6 +77,7 @@ typedef struct
   GFile          *file;
   GBytes         *content;
   GgitBlob       *blob;
+  IdeObject      *lock_object;
   guint           is_child_of_workdir : 1;
 } DiffTask;
 
@@ -107,12 +105,9 @@ typedef struct
   gint hunk_del_count;
 } DiffCallbackData;
 
-G_DEFINE_TYPE (IdeGitBufferChangeMonitor,
-               ide_git_buffer_change_monitor,
-               IDE_TYPE_BUFFER_CHANGE_MONITOR)
+G_DEFINE_TYPE (GbpGitBufferChangeMonitor, gbp_git_buffer_change_monitor, IDE_TYPE_BUFFER_CHANGE_MONITOR)
 
-DZL_DEFINE_COUNTER (instances, "IdeGitBufferChangeMonitor", "Instances",
-                    "The number of git buffer change monitor instances.");
+DZL_DEFINE_COUNTER (instances, "GbpGitBufferChangeMonitor", "Instances", "The number of git buffer change 
monitor instances.");
 
 enum {
   PROP_0,
@@ -134,6 +129,7 @@ diff_task_free (gpointer data)
       g_clear_object (&diff->file);
       g_clear_object (&diff->blob);
       g_clear_object (&diff->repository);
+      g_clear_object (&diff->lock_object);
       g_clear_pointer (&diff->lines, g_array_unref);
       g_clear_pointer (&diff->content, g_bytes_unref);
       g_slice_free (DiffTask, diff);
@@ -148,71 +144,87 @@ diff_line_compare (const DiffLine *left,
 }
 
 static GArray *
-ide_git_buffer_change_monitor_calculate_finish (IdeGitBufferChangeMonitor  *self,
+gbp_git_buffer_change_monitor_calculate_finish (GbpGitBufferChangeMonitor  *self,
                                                 GAsyncResult               *result,
                                                 GError                    **error)
 {
   IdeTask *task = (IdeTask *)result;
   DiffTask *diff;
 
-  g_assert (IDE_IS_GIT_BUFFER_CHANGE_MONITOR (self));
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_GIT_BUFFER_CHANGE_MONITOR (self));
   g_assert (IDE_IS_TASK (result));
 
+  if (ide_object_set_error_if_destroyed (IDE_OBJECT (self), error))
+    return NULL;
+
   diff = ide_task_get_task_data (task);
 
-  /* Keep the blob around for future use */
-  if (diff->blob != self->cached_blob)
-    g_set_object (&self->cached_blob, diff->blob);
+  if (diff != NULL)
+    {
+      g_assert (GGIT_IS_REPOSITORY (diff->repository));
+      g_assert (G_IS_FILE (diff->file));
+      g_assert (diff->content != NULL);
+      g_assert (GBP_IS_GIT_VCS (diff->lock_object));
 
-  /* If the file is a child of the working directory, we need to know */
-  self->is_child_of_workdir = diff->is_child_of_workdir;
+      /* Keep the blob around for future use */
+      if (diff->blob != self->cached_blob)
+        g_set_object (&self->cached_blob, diff->blob);
+
+      /* If the file is a child of the working directory, we need to know */
+      self->is_child_of_workdir = diff->is_child_of_workdir;
+    }
 
   return ide_task_propagate_pointer (task, error);
 }
 
 static void
-ide_git_buffer_change_monitor_calculate_async (IdeGitBufferChangeMonitor *self,
+gbp_git_buffer_change_monitor_calculate_async (GbpGitBufferChangeMonitor *self,
                                                GCancellable              *cancellable,
                                                GAsyncReadyCallback        callback,
                                                gpointer                   user_data)
 {
   g_autoptr(IdeTask) task = NULL;
+  g_autoptr(IdeContext) context = NULL;
+  GbpGitVcs *vcs;
+  IdeBuffer *buffer;
   DiffTask *diff;
-  IdeFile *file;
-  GFile *gfile;
+  GFile *file;
 
-  g_assert (IDE_IS_GIT_BUFFER_CHANGE_MONITOR (self));
+  g_assert (GBP_IS_GIT_BUFFER_CHANGE_MONITOR (self));
   g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
-  g_assert (self->buffer != NULL);
   g_assert (self->repository != NULL);
 
   self->state_dirty = FALSE;
 
   task = ide_task_new (self, cancellable, callback, user_data);
-  ide_task_set_source_tag (task, ide_git_buffer_change_monitor_calculate_async);
-  ide_task_set_priority (task, G_PRIORITY_LOW);
+  ide_task_set_source_tag (task, gbp_git_buffer_change_monitor_calculate_async);
+
+  buffer = ide_buffer_change_monitor_get_buffer (IDE_BUFFER_CHANGE_MONITOR (self));
+  g_assert (IDE_IS_BUFFER (buffer));
 
-  file = ide_buffer_get_file (self->buffer);
-  g_assert (IDE_IS_FILE (file));
+  file = ide_buffer_get_file (buffer);
+  g_assert (G_IS_FILE (file));
 
-  gfile = ide_file_get_file (file);
-  g_assert (!gfile || G_IS_FILE (gfile));
+  context = ide_object_ref_context (IDE_OBJECT (self));
+  vcs = ide_context_peek_child_typed (context, GBP_TYPE_GIT_VCS);
 
-  if (gfile == NULL)
+  if (!GBP_IS_GIT_VCS (vcs))
     {
       ide_task_return_new_error (task,
                                  G_IO_ERROR,
-                                 G_IO_ERROR_NOT_FOUND,
-                                 _("Cannot provide diff, no backing file provided."));
+                                 G_IO_ERROR_CANCELLED,
+                                 "Cannot provide changes, not connected to GbpGitVcs.");
       return;
     }
 
   diff = g_slice_new0 (DiffTask);
-  diff->file = g_object_ref (gfile);
+  diff->file = g_object_ref (file);
   diff->repository = g_object_ref (self->repository);
   diff->lines = g_array_sized_new (FALSE, FALSE, sizeof (DiffLine), 32);
-  diff->content = ide_buffer_get_content (self->buffer);
+  diff->content = ide_buffer_dup_content (buffer);
   diff->blob = self->cached_blob ? g_object_ref (self->cached_blob) : NULL;
+  diff->lock_object = g_object_ref (IDE_OBJECT (vcs));
 
   ide_task_set_task_data (task, diff, diff_task_free);
 
@@ -221,11 +233,55 @@ ide_git_buffer_change_monitor_calculate_async (IdeGitBufferChangeMonitor *self,
   g_async_queue_push (work_queue, g_steal_pointer (&task));
 }
 
+static void
+gbp_git_buffer_change_monitor_foreach_change (IdeBufferChangeMonitor            *monitor,
+                                              guint                              begin_line,
+                                              guint                              end_line,
+                                              IdeBufferChangeMonitorForeachFunc  callback,
+                                              gpointer                           user_data)
+{
+  GbpGitBufferChangeMonitor *self = (GbpGitBufferChangeMonitor *)monitor;
+
+  g_assert (GBP_IS_GIT_BUFFER_CHANGE_MONITOR (self));
+  g_assert (callback != NULL);
+
+  if (end_line == G_MAXUINT)
+    end_line--;
+
+  if (self->lines == NULL || self->lines->data == NULL)
+    {
+      /* If within working directory, synthesize line addition. */
+      if (self->is_child_of_workdir)
+        {
+          for (guint i = begin_line; i < end_line; i++)
+            callback (i, IDE_BUFFER_LINE_CHANGE_ADDED, user_data);
+        }
+      return;
+    }
+
+  /* TODO: We could bsearch for the nearest start line */
+
+  for (guint i = 0; i < self->lines->len; i++)
+    {
+      DiffLine *line = &g_array_index (self->lines, DiffLine, i);
+      guint lineno = line->line - 1;
+
+      if (lineno < begin_line)
+        continue;
+
+      if (lineno > end_line)
+        break;
+
+      /* git is 1-based lines */
+      callback (lineno, line->change, user_data);
+    }
+}
+
 static IdeBufferLineChange
-ide_git_buffer_change_monitor_get_change (IdeBufferChangeMonitor *monitor,
+gbp_git_buffer_change_monitor_get_change (IdeBufferChangeMonitor *monitor,
                                           guint                   line)
 {
-  IdeGitBufferChangeMonitor *self = (IdeGitBufferChangeMonitor *)monitor;
+  GbpGitBufferChangeMonitor *self = (GbpGitBufferChangeMonitor *)monitor;
   DiffLine key = { line + 1, 0 }; /* Git is 1-based */
   DiffLine *ret;
 
@@ -250,31 +306,41 @@ ide_git_buffer_change_monitor_get_change (IdeBufferChangeMonitor *monitor,
   return ret != NULL ? ret->change : 0;
 }
 
-static void
-ide_git_buffer_change_monitor_set_repository (IdeGitBufferChangeMonitor *self,
+void
+gbp_git_buffer_change_monitor_set_repository (GbpGitBufferChangeMonitor *self,
                                               GgitRepository            *repository)
 {
-  g_return_if_fail (IDE_IS_GIT_BUFFER_CHANGE_MONITOR (self));
+  gboolean do_reload;
+
+  g_return_if_fail (GBP_IS_GIT_BUFFER_CHANGE_MONITOR (self));
   g_return_if_fail (GGIT_IS_REPOSITORY (repository));
 
-  g_set_object (&self->repository, repository);
+  do_reload = self->repository != NULL && repository != NULL;
+
+  if (g_set_object (&self->repository, repository))
+    {
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_REPOSITORY]);
+
+      if (do_reload)
+        ide_buffer_change_monitor_reload (IDE_BUFFER_CHANGE_MONITOR (self));
+    }
 }
 
 static void
-ide_git_buffer_change_monitor__calculate_cb (GObject      *object,
+gbp_git_buffer_change_monitor__calculate_cb (GObject      *object,
                                              GAsyncResult *result,
                                              gpointer      user_data_unused)
 {
-  IdeGitBufferChangeMonitor *self = (IdeGitBufferChangeMonitor *)object;
+  GbpGitBufferChangeMonitor *self = (GbpGitBufferChangeMonitor *)object;
   g_autoptr(GArray) lines = NULL;
   g_autoptr(GError) error = NULL;
 
-  g_assert (IDE_IS_GIT_BUFFER_CHANGE_MONITOR (self));
+  g_assert (GBP_IS_GIT_BUFFER_CHANGE_MONITOR (self));
   g_assert (user_data_unused == NULL);
 
   self->in_calculation = FALSE;
 
-  lines = ide_git_buffer_change_monitor_calculate_finish (self, result, &error);
+  lines = gbp_git_buffer_change_monitor_calculate_finish (self, result, &error);
 
   if (lines == NULL)
     {
@@ -298,35 +364,35 @@ ide_git_buffer_change_monitor__calculate_cb (GObject      *object,
 
   /* Recalculate if the buffer has changed since last request. */
   if (self->state_dirty)
-    ide_git_buffer_change_monitor_calculate_async (self,
+    gbp_git_buffer_change_monitor_calculate_async (self,
                                                    NULL,
-                                                   ide_git_buffer_change_monitor__calculate_cb,
+                                                   gbp_git_buffer_change_monitor__calculate_cb,
                                                    NULL);
 }
 
 static void
-ide_git_buffer_change_monitor_recalculate (IdeGitBufferChangeMonitor *self)
+gbp_git_buffer_change_monitor_recalculate (GbpGitBufferChangeMonitor *self)
 {
-  g_assert (IDE_IS_GIT_BUFFER_CHANGE_MONITOR (self));
+  g_assert (GBP_IS_GIT_BUFFER_CHANGE_MONITOR (self));
 
   self->state_dirty = TRUE;
 
   if (!self->in_calculation)
-    ide_git_buffer_change_monitor_calculate_async (self,
+    gbp_git_buffer_change_monitor_calculate_async (self,
                                                    NULL,
-                                                   ide_git_buffer_change_monitor__calculate_cb,
+                                                   gbp_git_buffer_change_monitor__calculate_cb,
                                                    NULL);
 }
 
 static void
-ide_git_buffer_change_monitor__buffer_delete_range_after_cb (IdeGitBufferChangeMonitor *self,
+gbp_git_buffer_change_monitor__buffer_delete_range_after_cb (GbpGitBufferChangeMonitor *self,
                                                              GtkTextIter               *begin,
                                                              GtkTextIter               *end,
                                                              IdeBuffer                 *buffer)
 {
   IDE_ENTRY;
 
-  g_assert (IDE_IS_GIT_BUFFER_CHANGE_MONITOR (self));
+  g_assert (GBP_IS_GIT_BUFFER_CHANGE_MONITOR (self));
   g_assert (begin);
   g_assert (end);
   g_assert (IDE_IS_BUFFER (buffer));
@@ -334,14 +400,14 @@ ide_git_buffer_change_monitor__buffer_delete_range_after_cb (IdeGitBufferChangeM
   if (self->delete_range_requires_recalculation)
     {
       self->delete_range_requires_recalculation = FALSE;
-      ide_git_buffer_change_monitor_recalculate (self);
+      gbp_git_buffer_change_monitor_recalculate (self);
     }
 
   IDE_EXIT;
 }
 
 static void
-ide_git_buffer_change_monitor__buffer_delete_range_cb (IdeGitBufferChangeMonitor *self,
+gbp_git_buffer_change_monitor__buffer_delete_range_cb (GbpGitBufferChangeMonitor *self,
                                                        GtkTextIter               *begin,
                                                        GtkTextIter               *end,
                                                        IdeBuffer                 *buffer)
@@ -350,7 +416,7 @@ ide_git_buffer_change_monitor__buffer_delete_range_cb (IdeGitBufferChangeMonitor
 
   IDE_ENTRY;
 
-  g_assert (IDE_IS_GIT_BUFFER_CHANGE_MONITOR (self));
+  g_assert (GBP_IS_GIT_BUFFER_CHANGE_MONITOR (self));
   g_assert (begin != NULL);
   g_assert (end != NULL);
   g_assert (IDE_IS_BUFFER (buffer));
@@ -363,13 +429,13 @@ ide_git_buffer_change_monitor__buffer_delete_range_cb (IdeGitBufferChangeMonitor
    *
    * Technically we need to do it on every change to be more correct, but that wastes a lot of
    * power. So instead, we'll be a bit lazy about it here and pick up the other changes on a much
-   * more conservative timeout, generated by ide_git_buffer_change_monitor__buffer_changed_cb().
+   * more conservative timeout, generated by gbp_git_buffer_change_monitor__buffer_changed_cb().
    */
 
   if (gtk_text_iter_get_line (begin) != gtk_text_iter_get_line (end))
     IDE_GOTO (recalculate);
 
-  change = ide_git_buffer_change_monitor_get_change (IDE_BUFFER_CHANGE_MONITOR (self),
+  change = gbp_git_buffer_change_monitor_get_change (IDE_BUFFER_CHANGE_MONITOR (self),
                                                      gtk_text_iter_get_line (begin));
   if (change == IDE_BUFFER_LINE_CHANGE_NONE)
     IDE_GOTO (recalculate);
@@ -379,7 +445,7 @@ ide_git_buffer_change_monitor__buffer_delete_range_cb (IdeGitBufferChangeMonitor
 recalculate:
   /*
    * We need to wait for the delete to occur, so mark it as necessary and let
-   * ide_git_buffer_change_monitor__buffer_delete_range_after_cb perform the operation.
+   * gbp_git_buffer_change_monitor__buffer_delete_range_after_cb perform the operation.
    */
   self->delete_range_requires_recalculation = TRUE;
 
@@ -387,7 +453,7 @@ recalculate:
 }
 
 static void
-ide_git_buffer_change_monitor__buffer_insert_text_after_cb (IdeGitBufferChangeMonitor *self,
+gbp_git_buffer_change_monitor__buffer_insert_text_after_cb (GbpGitBufferChangeMonitor *self,
                                                             GtkTextIter               *location,
                                                             gchar                     *text,
                                                             gint                       len,
@@ -397,7 +463,7 @@ ide_git_buffer_change_monitor__buffer_insert_text_after_cb (IdeGitBufferChangeMo
 
   IDE_ENTRY;
 
-  g_assert (IDE_IS_GIT_BUFFER_CHANGE_MONITOR (self));
+  g_assert (GBP_IS_GIT_BUFFER_CHANGE_MONITOR (self));
   g_assert (location);
   g_assert (text);
   g_assert (IDE_IS_BUFFER (buffer));
@@ -410,13 +476,13 @@ ide_git_buffer_change_monitor__buffer_insert_text_after_cb (IdeGitBufferChangeMo
    *
    * Technically we need to do it on every change to be more correct, but that wastes a lot of
    * power. So instead, we'll be a bit lazy about it here and pick up the other changes on a much
-   * more conservative timeout, generated by ide_git_buffer_change_monitor__buffer_changed_cb().
+   * more conservative timeout, generated by gbp_git_buffer_change_monitor__buffer_changed_cb().
    */
 
   if (NULL != memmem (text, len, "\n", 1))
     IDE_GOTO (recalculate);
 
-  change = ide_git_buffer_change_monitor_get_change (IDE_BUFFER_CHANGE_MONITOR (self),
+  change = gbp_git_buffer_change_monitor_get_change (IDE_BUFFER_CHANGE_MONITOR (self),
                                                      gtk_text_iter_get_line (location));
   if (change == IDE_BUFFER_LINE_CHANGE_NONE)
     IDE_GOTO (recalculate);
@@ -424,26 +490,26 @@ ide_git_buffer_change_monitor__buffer_insert_text_after_cb (IdeGitBufferChangeMo
   IDE_EXIT;
 
 recalculate:
-  ide_git_buffer_change_monitor_recalculate (self);
+  gbp_git_buffer_change_monitor_recalculate (self);
 
   IDE_EXIT;
 }
 
 static gboolean
-ide_git_buffer_change_monitor__changed_timeout_cb (gpointer user_data)
+gbp_git_buffer_change_monitor__changed_timeout_cb (gpointer user_data)
 {
-  IdeGitBufferChangeMonitor *self = user_data;
+  GbpGitBufferChangeMonitor *self = user_data;
 
-  g_assert (IDE_IS_GIT_BUFFER_CHANGE_MONITOR (self));
+  g_assert (GBP_IS_GIT_BUFFER_CHANGE_MONITOR (self));
 
   self->changed_timeout = 0;
-  ide_git_buffer_change_monitor_recalculate (self);
+  gbp_git_buffer_change_monitor_recalculate (self);
 
   return G_SOURCE_REMOVE;
 }
 
 static void
-ide_git_buffer_change_monitor__buffer_changed_after_cb (IdeGitBufferChangeMonitor *self,
+gbp_git_buffer_change_monitor__buffer_changed_after_cb (GbpGitBufferChangeMonitor *self,
                                                         IdeBuffer                 *buffer)
 {
   g_assert (IDE_IS_BUFFER_CHANGE_MONITOR (self));
@@ -456,63 +522,37 @@ ide_git_buffer_change_monitor__buffer_changed_after_cb (IdeGitBufferChangeMonito
 
   dzl_clear_source (&self->changed_timeout);
   self->changed_timeout = g_timeout_add_seconds (DELAY_CHANGED_SEC,
-                                                 ide_git_buffer_change_monitor__changed_timeout_cb,
+                                                 gbp_git_buffer_change_monitor__changed_timeout_cb,
                                                  self);
 }
 
 static void
-ide_git_buffer_change_monitor_reload (IdeBufferChangeMonitor *monitor)
+gbp_git_buffer_change_monitor_reload (IdeBufferChangeMonitor *monitor)
 {
-  IdeGitBufferChangeMonitor *self = (IdeGitBufferChangeMonitor *)monitor;
+  GbpGitBufferChangeMonitor *self = (GbpGitBufferChangeMonitor *)monitor;
 
   IDE_ENTRY;
 
-  g_assert (IDE_IS_GIT_BUFFER_CHANGE_MONITOR (self));
+  g_assert (GBP_IS_GIT_BUFFER_CHANGE_MONITOR (self));
 
   g_clear_object (&self->cached_blob);
-  ide_git_buffer_change_monitor_recalculate (self);
+  gbp_git_buffer_change_monitor_recalculate (self);
 
   IDE_EXIT;
 }
 
 static void
-ide_git_buffer_change_monitor__vcs_reloaded_cb (IdeGitBufferChangeMonitor *self,
-                                                GgitRepository            *new_repository,
-                                                IdeGitVcs                 *vcs)
+gbp_git_buffer_change_monitor_load (IdeBufferChangeMonitor *monitor,
+                                    IdeBuffer              *buffer)
 {
-  IDE_ENTRY;
-
-  g_assert (IDE_IS_GIT_BUFFER_CHANGE_MONITOR (self));
-  g_assert (IDE_IS_GIT_VCS (vcs));
-
-  g_set_object (&self->repository, new_repository);
-
-  ide_buffer_change_monitor_reload (IDE_BUFFER_CHANGE_MONITOR (self));
-
-  IDE_EXIT;
-}
-
-static void
-ide_git_buffer_change_monitor_set_buffer (IdeBufferChangeMonitor *monitor,
-                                          IdeBuffer              *buffer)
-{
-  IdeGitBufferChangeMonitor *self = (IdeGitBufferChangeMonitor *)monitor;
-  IdeContext *context;
-  IdeVcs *vcs;
+  GbpGitBufferChangeMonitor *self = (GbpGitBufferChangeMonitor *)monitor;
 
   IDE_ENTRY;
 
-  g_return_if_fail (IDE_IS_GIT_BUFFER_CHANGE_MONITOR (self));
+  g_return_if_fail (GBP_IS_GIT_BUFFER_CHANGE_MONITOR (self));
   g_return_if_fail (IDE_IS_BUFFER (buffer));
-  g_return_if_fail (!self->buffer);
-
-  g_set_weak_pointer (&self->buffer, buffer);
-
-  context = ide_object_get_context (IDE_OBJECT (self));
-  vcs = ide_context_get_vcs (context);
 
   dzl_signal_group_set_target (self->signal_group, buffer);
-  dzl_signal_group_set_target (self->vcs_signal_group, vcs);
 
   IDE_EXIT;
 }
@@ -665,7 +705,7 @@ diff_line_cb (GgitDiffDelta *delta,
 }
 
 static gboolean
-ide_git_buffer_change_monitor_calculate_threaded (IdeGitBufferChangeMonitor  *self,
+gbp_git_buffer_change_monitor_calculate_threaded (GbpGitBufferChangeMonitor  *self,
                                                   DiffTask                   *diff,
                                                   GError                    **error)
 {
@@ -675,7 +715,7 @@ ide_git_buffer_change_monitor_calculate_threaded (IdeGitBufferChangeMonitor  *se
   const guint8 *data;
   gsize data_len = 0;
 
-  g_assert (IDE_IS_GIT_BUFFER_CHANGE_MONITOR (self));
+  g_assert (GBP_IS_GIT_BUFFER_CHANGE_MONITOR (self));
   g_assert (diff != NULL);
   g_assert (G_IS_FILE (diff->file));
   g_assert (diff->lines != NULL);
@@ -787,7 +827,7 @@ ide_git_buffer_change_monitor_calculate_threaded (IdeGitBufferChangeMonitor  *se
 }
 
 static gpointer
-ide_git_buffer_change_monitor_worker (gpointer data)
+gbp_git_buffer_change_monitor_worker (gpointer data)
 {
   GAsyncQueue *queue = data;
   gpointer taskptr;
@@ -804,18 +844,24 @@ ide_git_buffer_change_monitor_worker (gpointer data)
 
   while (NULL != (taskptr = g_async_queue_pop (queue)))
     {
-      IdeGitBufferChangeMonitor *self;
+      GbpGitBufferChangeMonitor *self;
       g_autoptr(GError) error = NULL;
       g_autoptr(IdeTask) task = taskptr;
       DiffTask *diff;
+      gboolean ret;
 
       self = ide_task_get_source_object (task);
-      g_assert (IDE_IS_GIT_BUFFER_CHANGE_MONITOR (self));
+      g_assert (GBP_IS_GIT_BUFFER_CHANGE_MONITOR (self));
 
       diff = ide_task_get_task_data (task);
       g_assert (diff != NULL);
 
-      if (!ide_git_buffer_change_monitor_calculate_threaded (self, diff, &error))
+      /* Acquire the lock for the parent to ensure we have access to repository */
+      ide_object_lock (diff->lock_object);
+      ret = gbp_git_buffer_change_monitor_calculate_threaded (self, diff, &error);
+      ide_object_unlock (diff->lock_object);
+
+      if (!ret)
         ide_task_return_error (task, g_steal_pointer (&error));
       else
         ide_task_return_pointer (task,
@@ -827,42 +873,44 @@ ide_git_buffer_change_monitor_worker (gpointer data)
 }
 
 static void
-ide_git_buffer_change_monitor_dispose (GObject *object)
+gbp_git_buffer_change_monitor_destroy (IdeObject *object)
 {
-  IdeGitBufferChangeMonitor *self = (IdeGitBufferChangeMonitor *)object;
+  GbpGitBufferChangeMonitor *self = (GbpGitBufferChangeMonitor *)object;
 
   dzl_clear_source (&self->changed_timeout);
 
-  g_clear_weak_pointer (&self->buffer);
+  if (self->signal_group)
+    {
+      dzl_signal_group_set_target (self->signal_group, NULL);
+      g_clear_object (&self->signal_group);
+    }
 
-  g_clear_object (&self->signal_group);
-  g_clear_object (&self->vcs_signal_group);
   g_clear_object (&self->cached_blob);
   g_clear_object (&self->repository);
 
-  G_OBJECT_CLASS (ide_git_buffer_change_monitor_parent_class)->dispose (object);
+  IDE_OBJECT_CLASS (gbp_git_buffer_change_monitor_parent_class)->destroy (object);
 }
 
 static void
-ide_git_buffer_change_monitor_finalize (GObject *object)
+gbp_git_buffer_change_monitor_finalize (GObject *object)
 {
-  G_OBJECT_CLASS (ide_git_buffer_change_monitor_parent_class)->finalize (object);
+  G_OBJECT_CLASS (gbp_git_buffer_change_monitor_parent_class)->finalize (object);
 
   DZL_COUNTER_DEC (instances);
 }
 
 static void
-ide_git_buffer_change_monitor_set_property (GObject      *object,
+gbp_git_buffer_change_monitor_set_property (GObject      *object,
                                             guint         prop_id,
                                             const GValue *value,
                                             GParamSpec   *pspec)
 {
-  IdeGitBufferChangeMonitor *self = IDE_GIT_BUFFER_CHANGE_MONITOR (object);
+  GbpGitBufferChangeMonitor *self = GBP_GIT_BUFFER_CHANGE_MONITOR (object);
 
   switch (prop_id)
     {
     case PROP_REPOSITORY:
-      ide_git_buffer_change_monitor_set_repository (self, g_value_get_object (value));
+      gbp_git_buffer_change_monitor_set_repository (self, g_value_get_object (value));
       break;
 
     default:
@@ -871,18 +919,21 @@ ide_git_buffer_change_monitor_set_property (GObject      *object,
 }
 
 static void
-ide_git_buffer_change_monitor_class_init (IdeGitBufferChangeMonitorClass *klass)
+gbp_git_buffer_change_monitor_class_init (GbpGitBufferChangeMonitorClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  IdeObjectClass *i_object_class = IDE_OBJECT_CLASS (klass);
   IdeBufferChangeMonitorClass *parent_class = IDE_BUFFER_CHANGE_MONITOR_CLASS (klass);
 
-  object_class->dispose = ide_git_buffer_change_monitor_dispose;
-  object_class->finalize = ide_git_buffer_change_monitor_finalize;
-  object_class->set_property = ide_git_buffer_change_monitor_set_property;
+  object_class->finalize = gbp_git_buffer_change_monitor_finalize;
+  object_class->set_property = gbp_git_buffer_change_monitor_set_property;
+
+  i_object_class->destroy = gbp_git_buffer_change_monitor_destroy;
 
-  parent_class->set_buffer = ide_git_buffer_change_monitor_set_buffer;
-  parent_class->get_change = ide_git_buffer_change_monitor_get_change;
-  parent_class->reload = ide_git_buffer_change_monitor_reload;
+  parent_class->load = gbp_git_buffer_change_monitor_load;
+  parent_class->get_change = gbp_git_buffer_change_monitor_get_change;
+  parent_class->reload = gbp_git_buffer_change_monitor_reload;
+  parent_class->foreach_change = gbp_git_buffer_change_monitor_foreach_change;
 
   properties [PROP_REPOSITORY] =
     g_param_spec_object ("repository",
@@ -899,42 +950,35 @@ ide_git_buffer_change_monitor_class_init (IdeGitBufferChangeMonitorClass *klass)
    *       on worker thread for interactive blob changes is fine.
    */
   work_queue = g_async_queue_new ();
-  work_thread = g_thread_new ("IdeGitBufferChangeMonitorWorker",
-                              ide_git_buffer_change_monitor_worker,
+  work_thread = g_thread_new ("GbpGitBufferChangeMonitorWorker",
+                              gbp_git_buffer_change_monitor_worker,
                               work_queue);
 }
 
 static void
-ide_git_buffer_change_monitor_init (IdeGitBufferChangeMonitor *self)
+gbp_git_buffer_change_monitor_init (GbpGitBufferChangeMonitor *self)
 {
   DZL_COUNTER_INC (instances);
 
   self->signal_group = dzl_signal_group_new (IDE_TYPE_BUFFER);
   dzl_signal_group_connect_object (self->signal_group,
                                    "insert-text",
-                                   G_CALLBACK (ide_git_buffer_change_monitor__buffer_insert_text_after_cb),
+                                   G_CALLBACK (gbp_git_buffer_change_monitor__buffer_insert_text_after_cb),
                                    self,
                                    G_CONNECT_SWAPPED | G_CONNECT_AFTER);
   dzl_signal_group_connect_object (self->signal_group,
                                    "delete-range",
-                                   G_CALLBACK (ide_git_buffer_change_monitor__buffer_delete_range_cb),
+                                   G_CALLBACK (gbp_git_buffer_change_monitor__buffer_delete_range_cb),
                                    self,
                                    G_CONNECT_SWAPPED);
   dzl_signal_group_connect_object (self->signal_group,
                                    "delete-range",
-                                   G_CALLBACK (ide_git_buffer_change_monitor__buffer_delete_range_after_cb),
+                                   G_CALLBACK (gbp_git_buffer_change_monitor__buffer_delete_range_after_cb),
                                    self,
                                    G_CONNECT_SWAPPED | G_CONNECT_AFTER);
   dzl_signal_group_connect_object (self->signal_group,
                                    "changed",
-                                   G_CALLBACK (ide_git_buffer_change_monitor__buffer_changed_after_cb),
+                                   G_CALLBACK (gbp_git_buffer_change_monitor__buffer_changed_after_cb),
                                    self,
                                    G_CONNECT_SWAPPED | G_CONNECT_AFTER);
-
-  self->vcs_signal_group = dzl_signal_group_new (IDE_TYPE_GIT_VCS);
-  dzl_signal_group_connect_object (self->vcs_signal_group,
-                                   "reloaded",
-                                   G_CALLBACK (ide_git_buffer_change_monitor__vcs_reloaded_cb),
-                                   self,
-                                   G_CONNECT_SWAPPED);
 }
diff --git a/src/plugins/git/ide-git-buffer-change-monitor.h b/src/plugins/git/gbp-git-buffer-change-monitor.h
similarity index 64%
rename from src/plugins/git/ide-git-buffer-change-monitor.h
rename to src/plugins/git/gbp-git-buffer-change-monitor.h
index cd493e09c..9c29d940d 100644
--- a/src/plugins/git/ide-git-buffer-change-monitor.h
+++ b/src/plugins/git/gbp-git-buffer-change-monitor.h
@@ -1,4 +1,4 @@
-/* ide-git-buffer-change-monitor.h
+/* gbp-git-buffer-change-monitor.h
  *
  * Copyright 2015-2019 Christian Hergert <christian hergert me>
  *
@@ -20,13 +20,16 @@
 
 #pragma once
 
-#include <ide.h>
+#include <libgit2-glib/ggit.h>
+#include <libide-code.h>
 
 G_BEGIN_DECLS
 
-#define IDE_TYPE_GIT_BUFFER_CHANGE_MONITOR (ide_git_buffer_change_monitor_get_type())
+#define GBP_TYPE_GIT_BUFFER_CHANGE_MONITOR (gbp_git_buffer_change_monitor_get_type())
 
-G_DECLARE_FINAL_TYPE (IdeGitBufferChangeMonitor, ide_git_buffer_change_monitor,
-                      IDE, GIT_BUFFER_CHANGE_MONITOR, IdeBufferChangeMonitor)
+G_DECLARE_FINAL_TYPE (GbpGitBufferChangeMonitor, gbp_git_buffer_change_monitor, GBP, 
GIT_BUFFER_CHANGE_MONITOR, IdeBufferChangeMonitor)
+
+void gbp_git_buffer_change_monitor_set_repository (GbpGitBufferChangeMonitor *self,
+                                                   GgitRepository            *repository);
 
 G_END_DECLS
diff --git a/src/plugins/git/ide-git-dependency-updater.c b/src/plugins/git/gbp-git-dependency-updater.c
similarity index 77%
rename from src/plugins/git/ide-git-dependency-updater.c
rename to src/plugins/git/gbp-git-dependency-updater.c
index 89fa9f056..33ccecd02 100644
--- a/src/plugins/git/ide-git-dependency-updater.c
+++ b/src/plugins/git/gbp-git-dependency-updater.c
@@ -1,4 +1,4 @@
-/* ide-git-dependency-updater.c
+/* gbp-git-dependency-updater.c
  *
  * Copyright 2018-2019 Christian Hergert <chergert redhat com>
  *
@@ -18,14 +18,14 @@
  * SPDX-License-Identifier: GPL-3.0-or-later
  */
 
-#define G_LOG_DOMAIN "ide-git-dependency-updater"
+#define G_LOG_DOMAIN "gbp-git-dependency-updater"
 
 #include "config.h"
 
-#include "ide-git-dependency-updater.h"
-#include "ide-git-submodule-stage.h"
+#include "gbp-git-dependency-updater.h"
+#include "gbp-git-submodule-stage.h"
 
-struct _IdeGitDependencyUpdater
+struct _GbpGitDependencyUpdater
 {
   IdeObject parent_instance;
 };
@@ -34,18 +34,18 @@ static void
 find_submodule_stage_cb (gpointer data,
                          gpointer user_data)
 {
-  IdeGitSubmoduleStage **stage = user_data;
+  GbpGitSubmoduleStage **stage = user_data;
 
   g_assert (IDE_IS_BUILD_STAGE (data));
   g_assert (stage != NULL);
   g_assert (*stage == NULL || IDE_IS_BUILD_STAGE (*stage));
 
-  if (IDE_IS_GIT_SUBMODULE_STAGE (data))
-    *stage = IDE_GIT_SUBMODULE_STAGE (data);
+  if (GBP_IS_GIT_SUBMODULE_STAGE (data))
+    *stage = GBP_GIT_SUBMODULE_STAGE (data);
 }
 
 static void
-ide_git_dependency_updater_update_cb (GObject      *object,
+gbp_git_dependency_updater_update_cb (GObject      *object,
                                       GAsyncResult *result,
                                       gpointer      user_data)
 {
@@ -68,28 +68,28 @@ ide_git_dependency_updater_update_cb (GObject      *object,
 }
 
 static void
-ide_git_dependency_updater_update_async (IdeDependencyUpdater *self,
+gbp_git_dependency_updater_update_async (IdeDependencyUpdater *self,
                                          GCancellable         *cancellable,
                                          GAsyncReadyCallback   callback,
                                          gpointer              user_data)
 {
   g_autoptr(IdeTask) task = NULL;
-  IdeGitSubmoduleStage *stage = NULL;
+  GbpGitSubmoduleStage *stage = NULL;
   IdeBuildPipeline *pipeline;
   IdeBuildManager *manager;
   IdeContext *context;
 
   IDE_ENTRY;
 
-  g_assert (IDE_IS_GIT_DEPENDENCY_UPDATER (self));
+  g_assert (GBP_IS_GIT_DEPENDENCY_UPDATER (self));
   g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
 
   task = ide_task_new (self, cancellable, callback, user_data);
-  ide_task_set_source_tag (task, ide_git_dependency_updater_update_async);
+  ide_task_set_source_tag (task, gbp_git_dependency_updater_update_async);
   ide_task_set_priority (task, G_PRIORITY_LOW);
 
   context = ide_object_get_context (IDE_OBJECT (self));
-  manager = ide_context_get_build_manager (context);
+  manager = ide_build_manager_from_context (context);
   pipeline = ide_build_manager_get_pipeline (manager);
 
   g_assert (!pipeline || IDE_IS_BUILD_PIPELINE (pipeline));
@@ -113,7 +113,7 @@ ide_git_dependency_updater_update_async (IdeDependencyUpdater *self,
       IDE_EXIT;
     }
 
-  ide_git_submodule_stage_force_update (stage);
+  gbp_git_submodule_stage_force_update (stage);
 
   /* Ensure downloads and everything past it is invalidated */
   ide_build_pipeline_invalidate_phase (pipeline, IDE_BUILD_PHASE_DOWNLOADS);
@@ -127,18 +127,19 @@ ide_git_dependency_updater_update_async (IdeDependencyUpdater *self,
   ide_build_manager_rebuild_async (manager,
                                    IDE_BUILD_PHASE_CONFIGURE,
                                    NULL,
-                                  ide_git_dependency_updater_update_cb,
+                                   NULL,
+                                   gbp_git_dependency_updater_update_cb,
                                    g_steal_pointer (&task));
 
   IDE_EXIT;
 }
 
 static gboolean
-ide_git_dependency_updater_update_finish (IdeDependencyUpdater  *self,
+gbp_git_dependency_updater_update_finish (IdeDependencyUpdater  *self,
                                           GAsyncResult          *result,
                                           GError               **error)
 {
-  g_assert (IDE_IS_GIT_DEPENDENCY_UPDATER (self));
+  g_assert (GBP_IS_GIT_DEPENDENCY_UPDATER (self));
   g_assert (IDE_IS_TASK (result));
 
   return ide_task_propagate_boolean (IDE_TASK (result), error);
@@ -147,20 +148,20 @@ ide_git_dependency_updater_update_finish (IdeDependencyUpdater  *self,
 static void
 dependency_updater_iface_init (IdeDependencyUpdaterInterface *iface)
 {
-  iface->update_async = ide_git_dependency_updater_update_async;
-  iface->update_finish = ide_git_dependency_updater_update_finish;
+  iface->update_async = gbp_git_dependency_updater_update_async;
+  iface->update_finish = gbp_git_dependency_updater_update_finish;
 }
 
-G_DEFINE_TYPE_WITH_CODE (IdeGitDependencyUpdater, ide_git_dependency_updater, IDE_TYPE_OBJECT,
+G_DEFINE_TYPE_WITH_CODE (GbpGitDependencyUpdater, gbp_git_dependency_updater, IDE_TYPE_OBJECT,
                          G_IMPLEMENT_INTERFACE (IDE_TYPE_DEPENDENCY_UPDATER,
                                                 dependency_updater_iface_init))
 
 static void
-ide_git_dependency_updater_class_init (IdeGitDependencyUpdaterClass *klass)
+gbp_git_dependency_updater_class_init (GbpGitDependencyUpdaterClass *klass)
 {
 }
 
 static void
-ide_git_dependency_updater_init (IdeGitDependencyUpdater *self)
+gbp_git_dependency_updater_init (GbpGitDependencyUpdater *self)
 {
 }
diff --git a/src/plugins/git/ide-git-dependency-updater.h b/src/plugins/git/gbp-git-dependency-updater.h
similarity index 77%
rename from src/plugins/git/ide-git-dependency-updater.h
rename to src/plugins/git/gbp-git-dependency-updater.h
index b854f11a6..39b07db18 100644
--- a/src/plugins/git/ide-git-dependency-updater.h
+++ b/src/plugins/git/gbp-git-dependency-updater.h
@@ -1,4 +1,4 @@
-/* ide-git-dependency-updater.h
+/* gbp-git-dependency-updater.h
  *
  * Copyright 2018-2019 Christian Hergert <chergert redhat com>
  *
@@ -20,12 +20,12 @@
 
 #pragma once
 
-#include <ide.h>
+#include <libide-foundry.h>
 
 G_BEGIN_DECLS
 
-#define IDE_TYPE_GIT_DEPENDENCY_UPDATER (ide_git_dependency_updater_get_type())
+#define GBP_TYPE_GIT_DEPENDENCY_UPDATER (gbp_git_dependency_updater_get_type())
 
-G_DECLARE_FINAL_TYPE (IdeGitDependencyUpdater, ide_git_dependency_updater, IDE, GIT_DEPENDENCY_UPDATER, 
IdeObject)
+G_DECLARE_FINAL_TYPE (GbpGitDependencyUpdater, gbp_git_dependency_updater, GBP, GIT_DEPENDENCY_UPDATER, 
IdeObject)
 
 G_END_DECLS
diff --git a/src/plugins/git/gbp-git-index-monitor.c b/src/plugins/git/gbp-git-index-monitor.c
new file mode 100644
index 000000000..6765f13aa
--- /dev/null
+++ b/src/plugins/git/gbp-git-index-monitor.c
@@ -0,0 +1,140 @@
+/* gbp-git-index-monitor.c
+ *
+ * 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 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
+ */
+
+#define G_LOG_DOMAIN "gbp-git-index-monitor"
+
+#include "config.h"
+
+#include <libide-core.h>
+
+#include "gbp-git-index-monitor.h"
+
+struct _GbpGitIndexMonitor
+{
+  GObject       parent_instance;
+  GFile        *repository_dir;
+  GFileMonitor *monitor;
+};
+
+G_DEFINE_TYPE (GbpGitIndexMonitor, gbp_git_index_monitor, G_TYPE_OBJECT)
+
+enum {
+  CHANGED,
+  N_SIGNALS
+};
+
+static guint signals [N_SIGNALS];
+
+static void
+gbp_git_index_monitor_dispose (GObject *object)
+{
+  GbpGitIndexMonitor *self = (GbpGitIndexMonitor *)object;
+
+  g_clear_object (&self->repository_dir);
+
+  if (self->monitor != NULL)
+    {
+      g_file_monitor_cancel (self->monitor);
+      g_clear_object (&self->monitor);
+    }
+
+  G_OBJECT_CLASS (gbp_git_index_monitor_parent_class)->dispose (object);
+}
+
+static void
+gbp_git_index_monitor_class_init (GbpGitIndexMonitorClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->dispose = gbp_git_index_monitor_dispose;
+
+  signals [CHANGED] =
+    g_signal_new ("changed",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  0,
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__VOID,
+                  G_TYPE_NONE, 0);
+  g_signal_set_va_marshaller (signals [CHANGED],
+                              G_TYPE_FROM_CLASS (klass),
+                              g_cclosure_marshal_VOID__VOIDv);
+}
+
+static void
+gbp_git_index_monitor_init (GbpGitIndexMonitor *self)
+{
+}
+
+static void
+gbp_git_index_monitor_changed_cb (GbpGitIndexMonitor *self,
+                                  GFile              *file,
+                                  GFile              *other_file,
+                                  GFileMonitorEvent   event,
+                                  GFileMonitor       *monitor)
+{
+  g_autofree gchar *name = NULL;
+
+  IDE_ENTRY;
+
+  g_assert (GBP_IS_GIT_INDEX_MONITOR (self));
+  g_assert (G_IS_FILE (file));
+  g_assert (!other_file || G_IS_FILE (other_file));
+  g_assert (G_IS_FILE_MONITOR (monitor));
+
+  if (event != G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
+    IDE_EXIT;
+
+  name = g_file_get_basename (file);
+
+  if (ide_str_equal0 (name, "index"))
+    g_signal_emit (self, signals [CHANGED], 0);
+
+  IDE_EXIT;
+}
+
+GbpGitIndexMonitor *
+gbp_git_index_monitor_new (GFile *repository_dir)
+{
+  GbpGitIndexMonitor *self;
+  g_autoptr(GError) error = NULL;
+
+  g_return_val_if_fail (IDE_IS_MAIN_THREAD (), NULL);
+  g_return_val_if_fail (G_IS_FILE (repository_dir), NULL);
+
+  self = g_object_new (GBP_TYPE_GIT_INDEX_MONITOR, NULL);
+  self->repository_dir = g_object_ref (repository_dir);
+  self->monitor = g_file_monitor_directory (repository_dir,
+                                            G_FILE_MONITOR_NONE,
+                                            NULL,
+                                            &error);
+
+  if (error != NULL)
+    g_critical ("Failed to monitor git repository, no changes will be detected: %s",
+                error->message);
+  else
+    g_signal_connect_object (self->monitor,
+                             "changed",
+                             G_CALLBACK (gbp_git_index_monitor_changed_cb),
+                             self,
+                             G_CONNECT_SWAPPED);
+
+  return g_steal_pointer (&self);
+}
diff --git a/src/plugins/git/gbp-git-index-monitor.h b/src/plugins/git/gbp-git-index-monitor.h
new file mode 100644
index 000000000..155e0d89e
--- /dev/null
+++ b/src/plugins/git/gbp-git-index-monitor.h
@@ -0,0 +1,33 @@
+/* gbp-git-index-monitor.h
+ *
+ * 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 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
+ */
+
+#pragma once
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_GIT_INDEX_MONITOR (gbp_git_index_monitor_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpGitIndexMonitor, gbp_git_index_monitor, GBP, GIT_INDEX_MONITOR, GObject)
+
+GbpGitIndexMonitor *gbp_git_index_monitor_new (GFile *repository_dir);
+
+G_END_DECLS
diff --git a/src/plugins/git/ide-git-pipeline-addin.c b/src/plugins/git/gbp-git-pipeline-addin.c
similarity index 61%
rename from src/plugins/git/ide-git-pipeline-addin.c
rename to src/plugins/git/gbp-git-pipeline-addin.c
index 2f913879d..0e2e683b0 100644
--- a/src/plugins/git/ide-git-pipeline-addin.c
+++ b/src/plugins/git/gbp-git-pipeline-addin.c
@@ -1,4 +1,4 @@
-/* ide-git-pipeline-addin.c
+/* gbp-git-pipeline-addin.c
  *
  * Copyright 2018-2019 Christian Hergert <chergert redhat com>
  *
@@ -18,64 +18,65 @@
  * SPDX-License-Identifier: GPL-3.0-or-later
  */
 
-#define G_LOG_DOMAIN "ide-git-pipeline-addin"
+#define G_LOG_DOMAIN "gbp-git-pipeline-addin"
 
 #include "config.h"
 
+#include <libide-foundry.h>
 #include <glib/gi18n.h>
 
-#include "ide-git-pipeline-addin.h"
-#include "ide-git-submodule-stage.h"
-#include "ide-git-vcs.h"
+#include "gbp-git-pipeline-addin.h"
+#include "gbp-git-submodule-stage.h"
+#include "gbp-git-vcs.h"
 
-struct _IdeGitPipelineAddin
+struct _GbpGitPipelineAddin
 {
   IdeObject parent_instance;
 };
 
 static void
-ide_git_pipeline_addin_load (IdeBuildPipelineAddin *addin,
+gbp_git_pipeline_addin_load (IdeBuildPipelineAddin *addin,
                              IdeBuildPipeline      *pipeline)
 {
-  g_autoptr(IdeGitSubmoduleStage) submodule = NULL;
+  g_autoptr(GbpGitSubmoduleStage) submodule = NULL;
   IdeContext *context;
   IdeVcs *vcs;
   guint stage_id;
 
-  g_assert (IDE_IS_GIT_PIPELINE_ADDIN (addin));
+  g_assert (GBP_IS_GIT_PIPELINE_ADDIN (addin));
   g_assert (IDE_IS_BUILD_PIPELINE (pipeline));
 
   context = ide_object_get_context (IDE_OBJECT (addin));
-  vcs = ide_context_get_vcs (context);
+  vcs = ide_vcs_from_context (context);
 
   /* Ignore everything if this isn't a git-based repository */
-  if (!IDE_IS_GIT_VCS (vcs))
+  if (!GBP_IS_GIT_VCS (vcs))
     return;
 
-  submodule = ide_git_submodule_stage_new (context);
-  stage_id = ide_build_pipeline_connect (pipeline,
-                                         IDE_BUILD_PHASE_DOWNLOADS,
-                                         100,
-                                         IDE_BUILD_STAGE (submodule));
+  submodule = gbp_git_submodule_stage_new (context);
+  stage_id = ide_build_pipeline_attach (pipeline,
+                                        IDE_BUILD_PHASE_DOWNLOADS,
+                                        100,
+                                        IDE_BUILD_STAGE (submodule));
   ide_build_pipeline_addin_track (addin, stage_id);
 }
 
 static void
 build_pipeline_addin_iface_init (IdeBuildPipelineAddinInterface *iface)
 {
-  iface->load = ide_git_pipeline_addin_load;
+  iface->load = gbp_git_pipeline_addin_load;
 }
 
-G_DEFINE_TYPE_WITH_CODE (IdeGitPipelineAddin, ide_git_pipeline_addin, IDE_TYPE_OBJECT,
+G_DEFINE_TYPE_WITH_CODE (GbpGitPipelineAddin, gbp_git_pipeline_addin, IDE_TYPE_OBJECT,
                          G_IMPLEMENT_INTERFACE (IDE_TYPE_BUILD_PIPELINE_ADDIN,
                                                 build_pipeline_addin_iface_init))
 
 static void
-ide_git_pipeline_addin_class_init (IdeGitPipelineAddinClass *klass)
+gbp_git_pipeline_addin_class_init (GbpGitPipelineAddinClass *klass)
 {
 }
 
 static void
-ide_git_pipeline_addin_init (IdeGitPipelineAddin *self)
+gbp_git_pipeline_addin_init (GbpGitPipelineAddin *self)
 {
 }
diff --git a/src/plugins/git/ide-git-pipeline-addin.h b/src/plugins/git/gbp-git-pipeline-addin.h
similarity index 78%
rename from src/plugins/git/ide-git-pipeline-addin.h
rename to src/plugins/git/gbp-git-pipeline-addin.h
index 92bb7856b..19a8d5be5 100644
--- a/src/plugins/git/ide-git-pipeline-addin.h
+++ b/src/plugins/git/gbp-git-pipeline-addin.h
@@ -1,4 +1,4 @@
-/* ide-git-pipeline-addin.h
+/* gbp-git-pipeline-addin.h
  *
  * Copyright 2018-2019 Christian Hergert <chergert redhat com>
  *
@@ -20,12 +20,12 @@
 
 #pragma once
 
-#include <ide.h>
+#include <libide-core.h>
 
 G_BEGIN_DECLS
 
-#define IDE_TYPE_GIT_PIPELINE_ADDIN (ide_git_pipeline_addin_get_type())
+#define GBP_TYPE_GIT_PIPELINE_ADDIN (gbp_git_pipeline_addin_get_type())
 
-G_DECLARE_FINAL_TYPE (IdeGitPipelineAddin, ide_git_pipeline_addin, IDE, GIT_PIPELINE_ADDIN, IdeObject)
+G_DECLARE_FINAL_TYPE (GbpGitPipelineAddin, gbp_git_pipeline_addin, GBP, GIT_PIPELINE_ADDIN, IdeObject)
 
 G_END_DECLS
diff --git a/src/plugins/git/gbp-git-remote-callbacks.c b/src/plugins/git/gbp-git-remote-callbacks.c
new file mode 100644
index 000000000..ce5dbec79
--- /dev/null
+++ b/src/plugins/git/gbp-git-remote-callbacks.c
@@ -0,0 +1,265 @@
+/* gbp-git-remote-callbacks.c
+ *
+ * Copyright 2015-2019 Christian Hergert <christian hergert me>
+ *
+ * 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 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
+ */
+
+#define G_LOG_DOMAIN "gbp-git-remote-callbacks"
+
+#include <dazzle.h>
+#include <glib/gi18n.h>
+
+#include "gbp-git-remote-callbacks.h"
+
+#define ANIMATION_DURATION_MSEC 250
+
+struct _GbpGitRemoteCallbacks
+{
+  GgitRemoteCallbacks  parent_instance;
+
+  IdeNotification     *progress;
+  GString             *body;
+  GgitCredtype         tried;
+  guint                cancelled : 1;
+};
+
+G_DEFINE_TYPE (GbpGitRemoteCallbacks, gbp_git_remote_callbacks, GGIT_TYPE_REMOTE_CALLBACKS)
+
+enum {
+  PROP_0,
+  PROP_PROGRESS,
+  LAST_PROP
+};
+
+static GParamSpec *properties [LAST_PROP];
+
+GgitRemoteCallbacks *
+gbp_git_remote_callbacks_new (IdeNotification *progress)
+{
+  g_return_val_if_fail (IDE_IS_NOTIFICATION (progress), NULL);
+
+  return g_object_new (GBP_TYPE_GIT_REMOTE_CALLBACKS,
+                       "progress", progress,
+                       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)
+{
+  g_return_val_if_fail (GBP_IS_GIT_REMOTE_CALLBACKS (self), NULL);
+
+  return self->progress;
+}
+
+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));
+
+  if (self->body == NULL)
+    self->body = g_string_new (message);
+  else
+    g_string_append (self->body, message);
+
+  ide_notification_set_body (self->progress, self->body->str);
+}
+
+static void
+gbp_git_remote_callbacks_real_transfer_progress (GgitRemoteCallbacks  *callbacks,
+                                                 GgitTransferProgress *stats)
+{
+  GbpGitRemoteCallbacks *self = (GbpGitRemoteCallbacks *)callbacks;
+  guint total;
+  guint received;
+
+  g_assert (GBP_IS_GIT_REMOTE_CALLBACKS (self));
+  g_assert (stats != NULL);
+
+  if (self->cancelled)
+    return;
+
+  total = ggit_transfer_progress_get_total_objects (stats);
+  received = ggit_transfer_progress_get_received_objects (stats);
+  if (total == 0)
+    return;
+
+  ide_notification_set_progress (self->progress, (gdouble)received / (gdouble)total);
+}
+
+static GgitCred *
+gbp_git_remote_callbacks_real_credentials (GgitRemoteCallbacks  *callbacks,
+                                           const gchar          *url,
+                                           const gchar          *username_from_url,
+                                           GgitCredtype          allowed_types,
+                                           GError              **error)
+{
+  GbpGitRemoteCallbacks *self = (GbpGitRemoteCallbacks *)callbacks;
+  GgitCred *ret = NULL;
+
+  IDE_ENTRY;
+
+  g_assert (GBP_IS_GIT_REMOTE_CALLBACKS (self));
+  g_assert (url != NULL);
+
+  IDE_TRACE_MSG ("username=%s url=%s", username_from_url ?: "", url);
+
+  if (self->cancelled)
+    {
+      g_set_error (error,
+                   G_IO_ERROR,
+                   G_IO_ERROR_CANCELLED,
+                   "The operation has been canceled");
+      IDE_RETURN (NULL);
+    }
+
+  allowed_types &= ~self->tried;
+
+  if ((allowed_types & GGIT_CREDTYPE_SSH_KEY) != 0)
+    {
+      GgitCredSshKeyFromAgent *cred;
+
+      cred = ggit_cred_ssh_key_from_agent_new (username_from_url, error);
+      ret = GGIT_CRED (cred);
+      self->tried |= GGIT_CREDTYPE_SSH_KEY;
+    }
+
+  if ((allowed_types & GGIT_CREDTYPE_SSH_INTERACTIVE) != 0)
+    {
+      GgitCredSshInteractive *cred;
+
+      cred = ggit_cred_ssh_interactive_new (username_from_url, error);
+      ret = GGIT_CRED (cred);
+      self->tried |= GGIT_CREDTYPE_SSH_INTERACTIVE;
+    }
+
+  if (ret == NULL)
+    g_set_error (error,
+                 G_IO_ERROR,
+                 G_IO_ERROR_NOT_SUPPORTED,
+                 _("Builder failed to provide appropriate credentials when cloning repository."));
+
+  IDE_RETURN (ret);
+}
+
+static void
+gbp_git_remote_callbacks_finalize (GObject *object)
+{
+  GbpGitRemoteCallbacks *self = (GbpGitRemoteCallbacks *)object;
+
+  g_clear_object (&self->progress);
+
+  g_string_free (self->body, TRUE);
+  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);
+
+  switch (prop_id)
+    {
+    case PROP_PROGRESS:
+      g_clear_object (&self->progress);
+      self->progress = g_value_dup_object (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gbp_git_remote_callbacks_class_init (GbpGitRemoteCallbacksClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (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);
+}
+
+static void
+gbp_git_remote_callbacks_init (GbpGitRemoteCallbacks *self)
+{
+}
+
+/**
+ * gbp_git_remote_callbacks_cancel:
+ *
+ * This function should be called when a clone was canceled so that we can
+ * avoid dispatching more events.
+ *
+ * Since: 3.32
+ */
+void
+gbp_git_remote_callbacks_cancel (GbpGitRemoteCallbacks *self)
+{
+  g_return_if_fail (GBP_IS_GIT_REMOTE_CALLBACKS (self));
+
+  self->cancelled  = TRUE;
+}
diff --git a/src/plugins/git/ide-git-remote-callbacks.h b/src/plugins/git/gbp-git-remote-callbacks.h
similarity index 64%
rename from src/plugins/git/ide-git-remote-callbacks.h
rename to src/plugins/git/gbp-git-remote-callbacks.h
index 7865c2388..185fb597c 100644
--- a/src/plugins/git/ide-git-remote-callbacks.h
+++ b/src/plugins/git/gbp-git-remote-callbacks.h
@@ -1,4 +1,4 @@
-/* ide-git-remote-callbacks.h
+/* gbp-git-remote-callbacks.h
  *
  * Copyright 2015-2019 Christian Hergert <christian hergert me>
  *
@@ -21,18 +21,17 @@
 #pragma once
 
 #include <libgit2-glib/ggit.h>
-#include <ide.h>
+#include <libide-core.h>
 
 G_BEGIN_DECLS
 
-#define IDE_TYPE_GIT_REMOTE_CALLBACKS (ide_git_remote_callbacks_get_type())
+#define GBP_TYPE_GIT_REMOTE_CALLBACKS (gbp_git_remote_callbacks_get_type())
 
-G_DECLARE_FINAL_TYPE (IdeGitRemoteCallbacks, ide_git_remote_callbacks,
-                      IDE, GIT_REMOTE_CALLBACKS, GgitRemoteCallbacks)
+G_DECLARE_FINAL_TYPE (GbpGitRemoteCallbacks, gbp_git_remote_callbacks, GBP, GIT_REMOTE_CALLBACKS, 
GgitRemoteCallbacks)
 
-GgitRemoteCallbacks *ide_git_remote_callbacks_new          (void);
-gdouble              ide_git_remote_callbacks_get_fraction (IdeGitRemoteCallbacks *self);
-IdeProgress         *ide_git_remote_callbacks_get_progress (IdeGitRemoteCallbacks *self);
-void                 ide_git_remote_callbacks_cancel       (IdeGitRemoteCallbacks *self);
+GgitRemoteCallbacks *gbp_git_remote_callbacks_new          (IdeNotification       *progress);
+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);
 
 G_END_DECLS
diff --git a/src/plugins/git/ide-git-submodule-stage.c b/src/plugins/git/gbp-git-submodule-stage.c
similarity index 80%
rename from src/plugins/git/ide-git-submodule-stage.c
rename to src/plugins/git/gbp-git-submodule-stage.c
index af0cb538a..e8dc4ae90 100644
--- a/src/plugins/git/ide-git-submodule-stage.c
+++ b/src/plugins/git/gbp-git-submodule-stage.c
@@ -1,4 +1,4 @@
-/* ide-git-submodule-stage.c
+/* gbp-git-submodule-stage.c
  *
  * Copyright 2018-2019 Christian Hergert <chergert redhat com>
  *
@@ -18,15 +18,18 @@
  * SPDX-License-Identifier: GPL-3.0-or-later
  */
 
-#define G_LOG_DOMAIN "ide-git-submodule-stage"
+#define G_LOG_DOMAIN "gbp-git-submodule-stage"
 
 #include "config.h"
 
 #include <glib/gi18n.h>
+#include <libide-code.h>
+#include <libide-gui.h>
+#include <libide-vcs.h>
 
-#include "ide-git-submodule-stage.h"
+#include "gbp-git-submodule-stage.h"
 
-struct _IdeGitSubmoduleStage
+struct _GbpGitSubmoduleStage
 {
   IdeBuildStageLauncher parent_instance;
 
@@ -34,24 +37,20 @@ struct _IdeGitSubmoduleStage
   guint force_update : 1;
 };
 
-G_DEFINE_TYPE (IdeGitSubmoduleStage, ide_git_submodule_stage, IDE_TYPE_BUILD_STAGE_LAUNCHER)
+G_DEFINE_TYPE (GbpGitSubmoduleStage, gbp_git_submodule_stage, IDE_TYPE_BUILD_STAGE_LAUNCHER)
 
-IdeGitSubmoduleStage *
-ide_git_submodule_stage_new (IdeContext *context)
+GbpGitSubmoduleStage *
+gbp_git_submodule_stage_new (IdeContext *context)
 {
   g_autoptr(IdeSubprocessLauncher) launcher = NULL;
-  g_autoptr(IdeGitSubmoduleStage) self = NULL;
-  IdeVcs *vcs;
-  GFile *workdir;
+  g_autoptr(GbpGitSubmoduleStage) self = NULL;
+  g_autoptr(GFile) workdir = NULL;
 
   g_return_val_if_fail (IDE_IS_CONTEXT (context), NULL);
 
-  vcs = ide_context_get_vcs (context);
-  workdir = ide_vcs_get_working_directory (vcs);
+  workdir = ide_context_ref_workdir (context);
 
-  self = g_object_new (IDE_TYPE_GIT_SUBMODULE_STAGE,
-                       "context", context,
-                       NULL);
+  self = g_object_new (GBP_TYPE_GIT_SUBMODULE_STAGE, NULL);
 
   launcher = ide_subprocess_launcher_new (0);
   ide_subprocess_launcher_set_cwd (launcher, g_file_peek_path (workdir));
@@ -66,12 +65,12 @@ ide_git_submodule_stage_new (IdeContext *context)
 }
 
 static void
-ide_git_submodule_stage_query_cb (GObject      *object,
+gbp_git_submodule_stage_query_cb (GObject      *object,
                                   GAsyncResult *result,
                                   gpointer      user_data)
 {
   IdeSubprocess *subprocess = (IdeSubprocess *)object;
-  g_autoptr(IdeGitSubmoduleStage) self = user_data;
+  g_autoptr(GbpGitSubmoduleStage) self = user_data;
   g_autoptr(GError) error = NULL;
   g_autofree gchar *stdout_buf = NULL;
   IdeLineReader reader;
@@ -80,7 +79,7 @@ ide_git_submodule_stage_query_cb (GObject      *object,
 
   g_assert (IDE_IS_SUBPROCESS (subprocess));
   g_assert (G_IS_ASYNC_RESULT (result));
-  g_assert (IDE_IS_GIT_SUBMODULE_STAGE (self));
+  g_assert (GBP_IS_GIT_SUBMODULE_STAGE (self));
 
   if (!ide_subprocess_communicate_utf8_finish (subprocess, result, &stdout_buf, NULL, &error))
     {
@@ -112,21 +111,21 @@ unpause:
 }
 
 static void
-ide_git_submodule_stage_query (IdeBuildStage    *stage,
+gbp_git_submodule_stage_query (IdeBuildStage    *stage,
                                IdeBuildPipeline *pipeline,
+                               GPtrArray        *targets,
                                GCancellable     *cancellable)
 {
-  IdeGitSubmoduleStage *self = (IdeGitSubmoduleStage *)stage;
+  GbpGitSubmoduleStage *self = (GbpGitSubmoduleStage *)stage;
   g_autoptr(IdeSubprocessLauncher) launcher = NULL;
   g_autoptr(IdeSubprocess) subprocess = NULL;
   g_autoptr(GError) error = NULL;
+  g_autoptr(GFile) workdir = NULL;
   IdeContext *context;
-  IdeVcs *vcs;
-  GFile *workdir;
 
   IDE_ENTRY;
 
-  g_assert (IDE_IS_GIT_SUBMODULE_STAGE (self));
+  g_assert (GBP_IS_GIT_SUBMODULE_STAGE (self));
   g_assert (IDE_IS_BUILD_PIPELINE (pipeline));
   g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
 
@@ -165,8 +164,7 @@ ide_git_submodule_stage_query (IdeBuildStage    *stage,
    */
 
   context = ide_object_get_context (IDE_OBJECT (self));
-  vcs = ide_context_get_vcs (context);
-  workdir = ide_vcs_get_working_directory (vcs);
+  workdir = ide_context_ref_workdir (context);
 
   launcher = ide_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE);
   ide_subprocess_launcher_push_argv (launcher, "git");
@@ -190,31 +188,31 @@ ide_git_submodule_stage_query (IdeBuildStage    *stage,
   ide_subprocess_communicate_utf8_async (subprocess,
                                          NULL,
                                          cancellable,
-                                         ide_git_submodule_stage_query_cb,
+                                         gbp_git_submodule_stage_query_cb,
                                          g_object_ref (self));
 
   IDE_EXIT;
 }
 
 static void
-ide_git_submodule_stage_class_init (IdeGitSubmoduleStageClass *klass)
+gbp_git_submodule_stage_class_init (GbpGitSubmoduleStageClass *klass)
 {
   IdeBuildStageClass *stage_class = IDE_BUILD_STAGE_CLASS (klass);
 
-  stage_class->query = ide_git_submodule_stage_query;
+  stage_class->query = gbp_git_submodule_stage_query;
 }
 
 static void
-ide_git_submodule_stage_init (IdeGitSubmoduleStage *self)
+gbp_git_submodule_stage_init (GbpGitSubmoduleStage *self)
 {
   ide_build_stage_set_name (IDE_BUILD_STAGE (self), _("Initialize git submodules"));
   ide_build_stage_launcher_set_ignore_exit_status (IDE_BUILD_STAGE_LAUNCHER (self), TRUE);
 }
 
 void
-ide_git_submodule_stage_force_update (IdeGitSubmoduleStage *self)
+gbp_git_submodule_stage_force_update (GbpGitSubmoduleStage *self)
 {
-  g_return_if_fail (IDE_IS_GIT_SUBMODULE_STAGE (self));
+  g_return_if_fail (GBP_IS_GIT_SUBMODULE_STAGE (self));
 
   self->force_update = TRUE;
 }
diff --git a/src/plugins/git/ide-git-submodule-stage.h b/src/plugins/git/gbp-git-submodule-stage.h
similarity index 70%
rename from src/plugins/git/ide-git-submodule-stage.h
rename to src/plugins/git/gbp-git-submodule-stage.h
index 4ce80502f..3b23c033c 100644
--- a/src/plugins/git/ide-git-submodule-stage.h
+++ b/src/plugins/git/gbp-git-submodule-stage.h
@@ -1,4 +1,4 @@
-/* ide-git-submodule-stage.h
+/* gbp-git-submodule-stage.h
  *
  * Copyright 2018-2019 Christian Hergert <chergert redhat com>
  *
@@ -20,15 +20,15 @@
 
 #pragma once
 
-#include <ide.h>
+#include <libide-foundry.h>
 
 G_BEGIN_DECLS
 
-#define IDE_TYPE_GIT_SUBMODULE_STAGE (ide_git_submodule_stage_get_type())
+#define GBP_TYPE_GIT_SUBMODULE_STAGE (gbp_git_submodule_stage_get_type())
 
-G_DECLARE_FINAL_TYPE (IdeGitSubmoduleStage, ide_git_submodule_stage, IDE, GIT_SUBMODULE_STAGE, 
IdeBuildStageLauncher)
+G_DECLARE_FINAL_TYPE (GbpGitSubmoduleStage, gbp_git_submodule_stage, GBP, GIT_SUBMODULE_STAGE, 
IdeBuildStageLauncher)
 
-IdeGitSubmoduleStage *ide_git_submodule_stage_new          (IdeContext           *context);
-void                  ide_git_submodule_stage_force_update (IdeGitSubmoduleStage *self);
+GbpGitSubmoduleStage *gbp_git_submodule_stage_new          (IdeContext           *context);
+void                  gbp_git_submodule_stage_force_update (GbpGitSubmoduleStage *self);
 
 G_END_DECLS
diff --git a/src/plugins/git/gbp-git-vcs-cloner.c b/src/plugins/git/gbp-git-vcs-cloner.c
new file mode 100644
index 000000000..2a0b9958a
--- /dev/null
+++ b/src/plugins/git/gbp-git-vcs-cloner.c
@@ -0,0 +1,317 @@
+/* gbp-git-vcs-cloner.c
+ *
+ * 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 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
+ */
+
+#define G_LOG_DOMAIN "gbp-git-vcs-cloner"
+
+#include "config.h"
+
+#include <dazzle.h>
+#include <glib/gi18n.h>
+#include <libide-threading.h>
+
+#include "gbp-git-remote-callbacks.h"
+#include "gbp-git-vcs-cloner.h"
+
+struct _GbpGitVcsCloner
+{
+  GObject parent_instance;
+};
+
+typedef struct
+{
+  IdeNotification *notif;
+  IdeVcsUri       *uri;
+  gchar           *branch;
+  GFile           *location;
+  GFile           *project_file;
+  gchar           *author_name;
+  gchar           *author_email;
+} CloneRequest;
+
+static void vcs_cloner_iface_init (IdeVcsClonerInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GbpGitVcsCloner, gbp_git_vcs_cloner, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (IDE_TYPE_VCS_CLONER,
+                                                vcs_cloner_iface_init))
+
+static void
+clone_request_free (gpointer data)
+{
+  CloneRequest *req = data;
+
+  if (req != NULL)
+    {
+      g_clear_pointer (&req->uri, ide_vcs_uri_unref);
+      g_clear_pointer (&req->branch, g_free);
+      g_clear_object (&req->notif);
+      g_clear_object (&req->location);
+      g_clear_object (&req->project_file);
+      g_slice_free (CloneRequest, req);
+    }
+}
+
+static CloneRequest *
+clone_request_new (IdeVcsUri       *uri,
+                   const gchar     *branch,
+                   GFile           *location,
+                   IdeNotification *notif)
+{
+  CloneRequest *req;
+
+  g_assert (uri);
+  g_assert (location);
+  g_assert (notif);
+
+  req = g_slice_new0 (CloneRequest);
+  req->uri = ide_vcs_uri_ref (uri);
+  req->branch = g_strdup (branch);
+  req->location = g_object_ref (location);
+  req->project_file = NULL;
+  req->notif = g_object_ref (notif);
+
+  return req;
+}
+
+static void
+gbp_git_vcs_cloner_class_init (GbpGitVcsClonerClass *klass)
+{
+}
+
+static void
+gbp_git_vcs_cloner_init (GbpGitVcsCloner *self)
+{
+}
+
+static gchar *
+gbp_git_vcs_cloner_get_title (IdeVcsCloner *cloner)
+{
+  return g_strdup ("Git");
+}
+
+static gboolean
+gbp_git_vcs_cloner_validate_uri (IdeVcsCloner  *cloner,
+                                 const gchar   *uri,
+                                 gchar        **errmsg)
+{
+  g_autoptr(IdeVcsUri) vcs_uri = NULL;
+
+  g_assert (IDE_IS_VCS_CLONER (cloner));
+  g_assert (uri != NULL);
+
+  vcs_uri = ide_vcs_uri_new (uri);
+
+  if (vcs_uri != NULL)
+    {
+      const gchar *scheme = ide_vcs_uri_get_scheme (vcs_uri);
+      const gchar *path = ide_vcs_uri_get_path (vcs_uri);
+
+      if (ide_str_equal0 (scheme, "file"))
+        {
+          g_autoptr(GFile) file = g_file_new_for_path (path);
+
+          if (!g_file_query_exists (file, NULL))
+            {
+              if (errmsg != NULL)
+                *errmsg = g_strdup_printf ("A resository could not be found at “%s”.", path);
+              return FALSE;
+            }
+
+          return TRUE;
+        }
+
+      /* We can only support certain schemes */
+      if (ide_str_equal0 (scheme, "http") ||
+          ide_str_equal0 (scheme, "https") ||
+          ide_str_equal0 (scheme, "git") ||
+          ide_str_equal0 (scheme, "rsync") ||
+          ide_str_equal0 (scheme, "ssh"))
+        return TRUE;
+
+      if (errmsg != NULL)
+        *errmsg = g_strdup_printf (_("The protocol “%s” is not supported."), scheme);
+    }
+
+  return FALSE;
+}
+
+static void
+gbp_git_vcs_cloner_worker (IdeTask      *task,
+                           gpointer      source_object,
+                           gpointer      task_data,
+                           GCancellable *cancellable)
+{
+  GbpGitVcsCloner *self = source_object;
+  g_autoptr(GgitConfig) config = NULL;
+  g_autoptr(GFile) config_file = NULL;
+  g_autoptr(GError) error = NULL;
+  g_autofree gchar *uristr = NULL;
+  GgitRepository *repository;
+  GgitCloneOptions *clone_options;
+  GgitFetchOptions *fetch_options;
+  GgitRemoteCallbacks *callbacks;
+  CloneRequest *req = task_data;
+
+  g_assert (IDE_IS_TASK (task));
+  g_assert (GBP_IS_GIT_VCS_CLONER (self));
+  g_assert (req != NULL);
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  callbacks = gbp_git_remote_callbacks_new (req->notif);
+
+  g_signal_connect_object (cancellable,
+                           "cancelled",
+                           G_CALLBACK (gbp_git_remote_callbacks_cancel),
+                           callbacks,
+                           G_CONNECT_SWAPPED);
+
+  fetch_options = ggit_fetch_options_new ();
+  ggit_fetch_options_set_remote_callbacks (fetch_options, callbacks);
+
+  clone_options = ggit_clone_options_new ();
+  ggit_clone_options_set_is_bare (clone_options, FALSE);
+  ggit_clone_options_set_checkout_branch (clone_options, req->branch);
+  ggit_clone_options_set_fetch_options (clone_options, fetch_options);
+  g_clear_pointer (&fetch_options, ggit_fetch_options_free);
+
+  uristr = ide_vcs_uri_to_string (req->uri);
+
+  repository = ggit_repository_clone (uristr, req->location, clone_options, &error);
+
+  g_clear_object (&callbacks);
+  g_clear_object (&clone_options);
+
+  if (repository == NULL)
+    {
+      ide_task_return_error (task, g_steal_pointer (&error));
+      return;
+    }
+
+  if (ide_task_return_error_if_cancelled (task))
+    return;
+
+  config_file = g_file_get_child (req->location, ".git/config");
+
+  if ((config = ggit_config_new_from_file (config_file, &error)))
+    {
+      if (req->author_name)
+        ggit_config_set_string (config, "user.name", req->author_name, &error);
+      if (req->author_email)
+        ggit_config_set_string (config, "user.email", req->author_email, &error);
+    }
+
+  req->project_file = ggit_repository_get_workdir (repository);
+
+  ide_task_return_boolean (task, TRUE);
+
+  g_clear_object (&repository);
+}
+
+static void
+gbp_git_vcs_cloner_clone_async (IdeVcsCloner        *cloner,
+                                const gchar         *uri,
+                                const gchar         *destination,
+                                GVariantDict        *options,
+                                GCancellable        *cancellable,
+                                IdeNotification    **notif,
+                                GAsyncReadyCallback  callback,
+                                gpointer             user_data)
+{
+  GbpGitVcsCloner *self = (GbpGitVcsCloner *)cloner;
+  g_autoptr(IdeNotification) notif_local = NULL;
+  g_autoptr(IdeVcsUri) vcs_uri = NULL;
+  g_autoptr(IdeTask) task = NULL;
+  g_autoptr(GFile) location = NULL;
+  g_autofree gchar *uristr = NULL;
+  CloneRequest *req;
+  const gchar *branch;
+
+  g_assert (GBP_IS_GIT_VCS_CLONER (cloner));
+  g_assert (uri != NULL);
+  g_assert (destination != NULL);
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = ide_task_new (self, cancellable, callback, user_data);
+  ide_task_set_source_tag (task, gbp_git_vcs_cloner_clone_async);
+
+  notif_local = ide_notification_new ();
+  if (notif != NULL)
+    *notif = g_object_ref (notif_local);
+
+  if (!g_variant_dict_lookup (options, "branch", "&s", &branch))
+    branch = "master";
+
+  /*
+   * ggit_repository_clone() will block and we don't have a good way to
+   * cancel it. So we need to return immediately (even though the clone
+   * will continue in the background for now).
+   *
+   * FIXME: Find Ggit API to cancel clone. We might need access to the
+   *    GgitRemote so we can ggit_remote_disconnect().
+   */
+  ide_task_set_return_on_cancel (task, TRUE);
+
+  uristr = g_strstrip (g_strdup (uri));
+  location = g_file_new_for_path (destination);
+
+  vcs_uri = ide_vcs_uri_new (uristr);
+
+  if (vcs_uri == NULL)
+    {
+      ide_task_return_new_error (task,
+                                 G_IO_ERROR,
+                                 G_IO_ERROR_INVAL,
+                                 _("A valid Git URL is required"));
+      return;
+    }
+
+  if (g_strcmp0 ("ssh", ide_vcs_uri_get_scheme (vcs_uri)) == 0)
+    {
+      if (ide_vcs_uri_get_user (vcs_uri) == NULL)
+        ide_vcs_uri_set_user (vcs_uri, g_get_user_name ());
+    }
+
+  req = clone_request_new (vcs_uri, branch, location, notif_local);
+
+  g_variant_dict_lookup (options, "author-name", "s", &req->author_name);
+  g_variant_dict_lookup (options, "author-email", "s", &req->author_email);
+
+  ide_task_set_task_data (task, req, clone_request_free);
+  ide_task_run_in_thread (task, gbp_git_vcs_cloner_worker);
+}
+
+static gboolean
+gbp_git_vcs_cloner_clone_finish (IdeVcsCloner  *cloner,
+                                 GAsyncResult  *result,
+                                 GError       **error)
+{
+  g_assert (GBP_IS_GIT_VCS_CLONER (cloner));
+  g_assert (IDE_IS_TASK (result));
+
+  return ide_task_propagate_boolean (IDE_TASK (result), error);
+}
+
+static void
+vcs_cloner_iface_init (IdeVcsClonerInterface *iface)
+{
+  iface->get_title = gbp_git_vcs_cloner_get_title;
+  iface->validate_uri = gbp_git_vcs_cloner_validate_uri;
+  iface->clone_async = gbp_git_vcs_cloner_clone_async;
+  iface->clone_finish = gbp_git_vcs_cloner_clone_finish;
+}
diff --git a/src/plugins/git/ide-git-vcs.h b/src/plugins/git/gbp-git-vcs-cloner.h
similarity index 73%
rename from src/plugins/git/ide-git-vcs.h
rename to src/plugins/git/gbp-git-vcs-cloner.h
index 544326ce4..d634242e9 100644
--- a/src/plugins/git/ide-git-vcs.h
+++ b/src/plugins/git/gbp-git-vcs-cloner.h
@@ -1,6 +1,6 @@
-/* ide-git-vcs.h
+/* gbp-git-vcs-cloner.h
  *
- * 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
@@ -20,12 +20,12 @@
 
 #pragma once
 
-#include <ide.h>
+#include <libide-vcs.h>
 
 G_BEGIN_DECLS
 
-#define IDE_TYPE_GIT_VCS (ide_git_vcs_get_type())
+#define GBP_TYPE_GIT_VCS_CLONER (gbp_git_vcs_cloner_get_type())
 
-G_DECLARE_FINAL_TYPE (IdeGitVcs, ide_git_vcs, IDE, GIT_VCS, IdeObject)
+G_DECLARE_FINAL_TYPE (GbpGitVcsCloner, gbp_git_vcs_cloner, GBP, GIT_VCS_CLONER, GObject)
 
 G_END_DECLS
diff --git a/src/plugins/git/ide-git-vcs-config.c b/src/plugins/git/gbp-git-vcs-config.c
similarity index 64%
rename from src/plugins/git/ide-git-vcs-config.c
rename to src/plugins/git/gbp-git-vcs-config.c
index 31f663077..cf8a20153 100644
--- a/src/plugins/git/ide-git-vcs-config.c
+++ b/src/plugins/git/gbp-git-vcs-config.c
@@ -1,4 +1,4 @@
-/* ide-git-vcs-config.c
+/* gbp-git-vcs-config.c
  *
  * Copyright 2016 Akshaya Kakkilaya <akshaya kakkilaya gmail com>
  *
@@ -18,11 +18,16 @@
  * SPDX-License-Identifier: GPL-3.0-or-later
  */
 
+#define G_LOG_DOMAIN "gbp-git-vcs-config"
+
+#include "config.h"
+
 #include <libgit2-glib/ggit.h>
+#include <libide-vcs.h>
 
-#include "ide-git-vcs-config.h"
+#include "gbp-git-vcs-config.h"
 
-struct _IdeGitVcsConfig
+struct _GbpGitVcsConfig
 {
   GObject     parent_instance;
 
@@ -31,17 +36,17 @@ struct _IdeGitVcsConfig
 
 static void vcs_config_init (IdeVcsConfigInterface *iface);
 
-G_DEFINE_TYPE_EXTENDED (IdeGitVcsConfig, ide_git_vcs_config, G_TYPE_OBJECT, 0,
+G_DEFINE_TYPE_EXTENDED (GbpGitVcsConfig, gbp_git_vcs_config, G_TYPE_OBJECT, 0,
                         G_IMPLEMENT_INTERFACE (IDE_TYPE_VCS_CONFIG, vcs_config_init))
 
-IdeGitVcsConfig *
-ide_git_vcs_config_new (void)
+GbpGitVcsConfig *
+gbp_git_vcs_config_new (void)
 {
-  return g_object_new (IDE_TYPE_GIT_VCS_CONFIG, NULL);
+  return g_object_new (GBP_TYPE_GIT_VCS_CONFIG, NULL);
 }
 
 static void
-ide_git_vcs_config_get_string (GgitConfig  *config,
+gbp_git_vcs_config_get_string (GgitConfig  *config,
                                const gchar *key,
                                GValue      *value,
                                GError     **error)
@@ -57,7 +62,7 @@ ide_git_vcs_config_get_string (GgitConfig  *config,
 }
 
 static void
-ide_git_vcs_config_set_string (GgitConfig   *config,
+gbp_git_vcs_config_set_string (GgitConfig   *config,
                                const gchar  *key,
                                const GValue *value,
                                GError      **error)
@@ -74,16 +79,16 @@ ide_git_vcs_config_set_string (GgitConfig   *config,
 }
 
 static void
-ide_git_vcs_config_get_config (IdeVcsConfig    *self,
+gbp_git_vcs_config_get_config (IdeVcsConfig    *self,
                                IdeVcsConfigType type,
                                GValue          *value)
 {
   g_autoptr(GgitConfig) config = NULL;
   GgitConfig *orig_config;
 
-  g_return_if_fail (IDE_IS_GIT_VCS_CONFIG (self));
+  g_return_if_fail (GBP_IS_GIT_VCS_CONFIG (self));
 
-  orig_config = IDE_GIT_VCS_CONFIG (self)->config;
+  orig_config = GBP_GIT_VCS_CONFIG (self)->config;
   config = ggit_config_snapshot (orig_config, NULL);
 
   if(config == NULL)
@@ -92,11 +97,11 @@ ide_git_vcs_config_get_config (IdeVcsConfig    *self,
   switch (type)
     {
     case IDE_VCS_CONFIG_FULL_NAME:
-      ide_git_vcs_config_get_string (config, "user.name", value, NULL);
+      gbp_git_vcs_config_get_string (config, "user.name", value, NULL);
       break;
 
     case IDE_VCS_CONFIG_EMAIL:
-      ide_git_vcs_config_get_string (config, "user.email", value, NULL);
+      gbp_git_vcs_config_get_string (config, "user.email", value, NULL);
       break;
 
     default:
@@ -105,24 +110,24 @@ ide_git_vcs_config_get_config (IdeVcsConfig    *self,
 }
 
 static void
-ide_git_vcs_config_set_config (IdeVcsConfig    *self,
+gbp_git_vcs_config_set_config (IdeVcsConfig    *self,
                                IdeVcsConfigType type,
                                const GValue    *value)
 {
   GgitConfig *config;
 
-  g_return_if_fail (IDE_IS_GIT_VCS_CONFIG (self));
+  g_return_if_fail (GBP_IS_GIT_VCS_CONFIG (self));
 
-  config = IDE_GIT_VCS_CONFIG (self)->config;
+  config = GBP_GIT_VCS_CONFIG (self)->config;
 
   switch (type)
     {
     case IDE_VCS_CONFIG_FULL_NAME:
-      ide_git_vcs_config_set_string (config, "user.name", value, NULL);
+      gbp_git_vcs_config_set_string (config, "user.name", value, NULL);
       break;
 
     case IDE_VCS_CONFIG_EMAIL:
-      ide_git_vcs_config_set_string (config, "user.email", value, NULL);
+      gbp_git_vcs_config_set_string (config, "user.email", value, NULL);
       break;
 
     default:
@@ -131,9 +136,9 @@ ide_git_vcs_config_set_config (IdeVcsConfig    *self,
 }
 
 static void
-ide_git_vcs_config_constructed (GObject *object)
+gbp_git_vcs_config_constructed (GObject *object)
 {
-  IdeGitVcsConfig *self = IDE_GIT_VCS_CONFIG (object);
+  GbpGitVcsConfig *self = GBP_GIT_VCS_CONFIG (object);
 
   g_autoptr(GFile) global_file = NULL;
 
@@ -147,36 +152,36 @@ ide_git_vcs_config_constructed (GObject *object)
 
   self->config = ggit_config_new_from_file (global_file, NULL);
 
-  G_OBJECT_CLASS (ide_git_vcs_config_parent_class)->constructed (object);
+  G_OBJECT_CLASS (gbp_git_vcs_config_parent_class)->constructed (object);
 }
 
 static void
-ide_git_vcs_config_finalize (GObject *object)
+gbp_git_vcs_config_finalize (GObject *object)
 {
-  IdeGitVcsConfig *self = IDE_GIT_VCS_CONFIG (object);
+  GbpGitVcsConfig *self = GBP_GIT_VCS_CONFIG (object);
 
   g_object_unref (self->config);
 
-  G_OBJECT_CLASS (ide_git_vcs_config_parent_class)->finalize (object);
+  G_OBJECT_CLASS (gbp_git_vcs_config_parent_class)->finalize (object);
 }
 
 static void
 vcs_config_init (IdeVcsConfigInterface *iface)
 {
-  iface->get_config = ide_git_vcs_config_get_config;
-  iface->set_config = ide_git_vcs_config_set_config;
+  iface->get_config = gbp_git_vcs_config_get_config;
+  iface->set_config = gbp_git_vcs_config_set_config;
 }
 
 static void
-ide_git_vcs_config_class_init (IdeGitVcsConfigClass *klass)
+gbp_git_vcs_config_class_init (GbpGitVcsConfigClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
-  object_class->constructed = ide_git_vcs_config_constructed;
-  object_class->finalize = ide_git_vcs_config_finalize;
+  object_class->constructed = gbp_git_vcs_config_constructed;
+  object_class->finalize = gbp_git_vcs_config_finalize;
 }
 
 static void
-ide_git_vcs_config_init (IdeGitVcsConfig *self)
+gbp_git_vcs_config_init (GbpGitVcsConfig *self)
 {
 }
diff --git a/src/plugins/git/ide-git-vcs-config.h b/src/plugins/git/gbp-git-vcs-config.h
similarity index 78%
rename from src/plugins/git/ide-git-vcs-config.h
rename to src/plugins/git/gbp-git-vcs-config.h
index 45e75d9d9..acc416d03 100644
--- a/src/plugins/git/ide-git-vcs-config.h
+++ b/src/plugins/git/gbp-git-vcs-config.h
@@ -1,4 +1,4 @@
-/* ide-git-vcs-config.h
+/* gbp-git-vcs-config.h
  *
  * Copyright 2016 Akshaya Kakkilaya <akshaya kakkilaya gmail com>
  *
@@ -20,14 +20,14 @@
 
 #pragma once
 
-#include <ide.h>
+#include <libide-core.h>
 
 G_BEGIN_DECLS
 
-#define IDE_TYPE_GIT_VCS_CONFIG (ide_git_vcs_config_get_type())
+#define GBP_TYPE_GIT_VCS_CONFIG (gbp_git_vcs_config_get_type())
 
-G_DECLARE_FINAL_TYPE (IdeGitVcsConfig, ide_git_vcs_config, IDE, GIT_VCS_CONFIG, GObject)
+G_DECLARE_FINAL_TYPE (GbpGitVcsConfig, gbp_git_vcs_config, GBP, GIT_VCS_CONFIG, GObject)
 
-IdeGitVcsConfig *ide_git_vcs_config_new (void);
+GbpGitVcsConfig *gbp_git_vcs_config_new (void);
 
 G_END_DECLS
diff --git a/src/plugins/git/ide-git-vcs-initializer.c b/src/plugins/git/gbp-git-vcs-initializer.c
similarity index 70%
rename from src/plugins/git/ide-git-vcs-initializer.c
rename to src/plugins/git/gbp-git-vcs-initializer.c
index 634d73e9a..d05b3690d 100644
--- a/src/plugins/git/ide-git-vcs-initializer.c
+++ b/src/plugins/git/gbp-git-vcs-initializer.c
@@ -1,4 +1,4 @@
-/* ide-git-vcs-initializer.c
+/* gbp-git-vcs-initializer.c
  *
  * Copyright 2016-2019 Christian Hergert <chergert redhat com>
  *
@@ -18,32 +18,37 @@
  * SPDX-License-Identifier: GPL-3.0-or-later
  */
 
+#define G_LOG_DOMAIN "gbp-git-vcs-initializer"
+
+#include "config.h"
+
 #include <libgit2-glib/ggit.h>
+#include <libide-threading.h>
 
-#include "ide-git-vcs-initializer.h"
+#include "gbp-git-vcs-initializer.h"
 
-struct _IdeGitVcsInitializer
+struct _GbpGitVcsInitializer
 {
   GObject parent_instance;
 };
 
 static void vcs_initializer_init (IdeVcsInitializerInterface *iface);
 
-G_DEFINE_TYPE_EXTENDED (IdeGitVcsInitializer, ide_git_vcs_initializer, G_TYPE_OBJECT, 0,
+G_DEFINE_TYPE_EXTENDED (GbpGitVcsInitializer, gbp_git_vcs_initializer, G_TYPE_OBJECT, 0,
                         G_IMPLEMENT_INTERFACE (IDE_TYPE_VCS_INITIALIZER, vcs_initializer_init))
 
 static void
-ide_git_vcs_initializer_class_init (IdeGitVcsInitializerClass *klass)
+gbp_git_vcs_initializer_class_init (GbpGitVcsInitializerClass *klass)
 {
 }
 
 static void
-ide_git_vcs_initializer_init (IdeGitVcsInitializer *self)
+gbp_git_vcs_initializer_init (GbpGitVcsInitializer *self)
 {
 }
 
 static void
-ide_git_vcs_initializer_initialize_worker (IdeTask      *task,
+gbp_git_vcs_initializer_initialize_worker (IdeTask      *task,
                                            gpointer      source_object,
                                            gpointer      task_data,
                                            GCancellable *cancellable)
@@ -53,7 +58,7 @@ ide_git_vcs_initializer_initialize_worker (IdeTask      *task,
   GFile *file = task_data;
 
   g_assert (IDE_IS_TASK (task));
-  g_assert (IDE_IS_GIT_VCS_INITIALIZER (source_object));
+  g_assert (GBP_IS_GIT_VCS_INITIALIZER (source_object));
   g_assert (G_IS_FILE (file));
 
   repository = ggit_repository_init_repository (file, FALSE, &error);
@@ -65,37 +70,37 @@ ide_git_vcs_initializer_initialize_worker (IdeTask      *task,
 }
 
 static void
-ide_git_vcs_initializer_initialize_async (IdeVcsInitializer   *initializer,
+gbp_git_vcs_initializer_initialize_async (IdeVcsInitializer   *initializer,
                                           GFile               *file,
                                           GCancellable        *cancellable,
                                           GAsyncReadyCallback  callback,
                                           gpointer             user_data)
 {
-  IdeGitVcsInitializer *self = (IdeGitVcsInitializer *)initializer;
+  GbpGitVcsInitializer *self = (GbpGitVcsInitializer *)initializer;
   g_autoptr(IdeTask) task = NULL;
 
-  g_return_if_fail (IDE_IS_GIT_VCS_INITIALIZER (self));
+  g_return_if_fail (GBP_IS_GIT_VCS_INITIALIZER (self));
   g_return_if_fail (G_IS_FILE (file));
   g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
 
   task = ide_task_new (self, cancellable, callback, user_data);
   ide_task_set_task_data (task, g_object_ref (file), g_object_unref);
-  ide_task_run_in_thread (task, ide_git_vcs_initializer_initialize_worker);
+  ide_task_run_in_thread (task, gbp_git_vcs_initializer_initialize_worker);
 }
 
 static gboolean
-ide_git_vcs_initializer_initialize_finish (IdeVcsInitializer  *initializer,
+gbp_git_vcs_initializer_initialize_finish (IdeVcsInitializer  *initializer,
                                            GAsyncResult       *result,
                                            GError            **error)
 {
-  g_return_val_if_fail (IDE_IS_GIT_VCS_INITIALIZER (initializer), FALSE);
+  g_return_val_if_fail (GBP_IS_GIT_VCS_INITIALIZER (initializer), FALSE);
   g_return_val_if_fail (IDE_IS_TASK (result), FALSE);
 
   return ide_task_propagate_boolean (IDE_TASK (result), error);
 }
 
 static gchar *
-ide_git_vcs_initializer_get_title (IdeVcsInitializer *initilizer)
+gbp_git_vcs_initializer_get_title (IdeVcsInitializer *initilizer)
 {
   return g_strdup ("Git");
 }
@@ -103,7 +108,7 @@ ide_git_vcs_initializer_get_title (IdeVcsInitializer *initilizer)
 static void
 vcs_initializer_init (IdeVcsInitializerInterface *iface)
 {
-  iface->get_title = ide_git_vcs_initializer_get_title;
-  iface->initialize_async = ide_git_vcs_initializer_initialize_async;
-  iface->initialize_finish = ide_git_vcs_initializer_initialize_finish;
+  iface->get_title = gbp_git_vcs_initializer_get_title;
+  iface->initialize_async = gbp_git_vcs_initializer_initialize_async;
+  iface->initialize_finish = gbp_git_vcs_initializer_initialize_finish;
 }
diff --git a/src/plugins/git/ide-git-vcs-initializer.h b/src/plugins/git/gbp-git-vcs-initializer.h
similarity index 78%
rename from src/plugins/git/ide-git-vcs-initializer.h
rename to src/plugins/git/gbp-git-vcs-initializer.h
index e1ffc281c..16493bf88 100644
--- a/src/plugins/git/ide-git-vcs-initializer.h
+++ b/src/plugins/git/gbp-git-vcs-initializer.h
@@ -1,4 +1,4 @@
-/* ide-git-vcs-initializer.h
+/* gbp-git-vcs-initializer.h
  *
  * Copyright 2016-2019 Christian Hergert <chergert redhat com>
  *
@@ -20,12 +20,12 @@
 
 #pragma once
 
-#include <ide.h>
+#include <libide-vcs.h>
 
 G_BEGIN_DECLS
 
-#define IDE_TYPE_GIT_VCS_INITIALIZER (ide_git_vcs_initializer_get_type())
+#define GBP_TYPE_GIT_VCS_INITIALIZER (gbp_git_vcs_initializer_get_type())
 
-G_DECLARE_FINAL_TYPE (IdeGitVcsInitializer, ide_git_vcs_initializer, IDE, GIT_VCS_INITIALIZER, GObject)
+G_DECLARE_FINAL_TYPE (GbpGitVcsInitializer, gbp_git_vcs_initializer, GBP, GIT_VCS_INITIALIZER, GObject)
 
 G_END_DECLS
diff --git a/src/plugins/git/gbp-git-vcs.c b/src/plugins/git/gbp-git-vcs.c
new file mode 100644
index 000000000..c00a73fc4
--- /dev/null
+++ b/src/plugins/git/gbp-git-vcs.c
@@ -0,0 +1,543 @@
+/* gbp-git-vcs.c
+ *
+ * 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 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
+ */
+
+#define G_LOG_DOMAIN "gbp-git-vcs"
+
+#include "config.h"
+
+#include "gbp-git-vcs.h"
+#include "gbp-git-vcs-config.h"
+
+struct _GbpGitVcs
+{
+  IdeObject       parent_instance;
+  GgitRepository *repository;
+  GFile          *location;
+  GFile          *workdir;
+  gchar          *branch;
+};
+
+enum {
+  PROP_0,
+  PROP_BRANCH_NAME,
+  PROP_LOCATION,
+  PROP_REPOSITORY,
+  PROP_WORKDIR,
+  N_PROPS
+};
+
+static void vcs_iface_init (IdeVcsInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GbpGitVcs, gbp_git_vcs, IDE_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (IDE_TYPE_VCS, vcs_iface_init))
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+gbp_git_vcs_finalize (GObject *object)
+{
+  GbpGitVcs *self = (GbpGitVcs *)object;
+
+  g_clear_object (&self->repository);
+  g_clear_object (&self->location);
+  g_clear_object (&self->workdir);
+  g_clear_pointer (&self->branch, g_free);
+
+  G_OBJECT_CLASS (gbp_git_vcs_parent_class)->finalize (object);
+}
+
+static void
+gbp_git_vcs_get_property (GObject    *object,
+                          guint       prop_id,
+                          GValue     *value,
+                          GParamSpec *pspec)
+{
+  GbpGitVcs *self = GBP_GIT_VCS (object);
+
+  switch (prop_id)
+    {
+    case PROP_BRANCH_NAME:
+      g_value_set_string (value, self->branch);
+      break;
+
+    case PROP_LOCATION:
+      g_value_set_object (value, self->location);
+      break;
+
+    case PROP_REPOSITORY:
+      g_value_set_object (value, self->repository);
+      break;
+
+    case PROP_WORKDIR:
+      g_value_set_object (value, self->workdir);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gbp_git_vcs_set_property (GObject      *object,
+                          guint         prop_id,
+                          const GValue *value,
+                          GParamSpec   *pspec)
+{
+  GbpGitVcs *self = GBP_GIT_VCS (object);
+
+  switch (prop_id)
+    {
+    case PROP_BRANCH_NAME:
+      self->branch = g_value_dup_string (value);
+      break;
+
+    case PROP_LOCATION:
+      self->location = g_value_dup_object (value);
+      break;
+
+    case PROP_REPOSITORY:
+      self->repository = g_value_dup_object (value);
+      break;
+
+    case PROP_WORKDIR:
+      self->workdir = g_value_dup_object (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gbp_git_vcs_class_init (GbpGitVcsClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = gbp_git_vcs_finalize;
+  object_class->get_property = gbp_git_vcs_get_property;
+  object_class->set_property = gbp_git_vcs_set_property;
+
+  properties [PROP_BRANCH_NAME] =
+    g_param_spec_string ("branch-name",
+                         "Branch Name",
+                         "The name of the branch",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_LOCATION] =
+    g_param_spec_object ("location",
+                         "Location",
+                         "The location for the repository",
+                         G_TYPE_FILE,
+                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_REPOSITORY] =
+    g_param_spec_object ("repository",
+                         "Repository",
+                         "The underlying repository object",
+                         GGIT_TYPE_REPOSITORY,
+                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_WORKDIR] =
+    g_param_spec_object ("workdir",
+                         "Workdir",
+                         "Working directory of the repository",
+                         G_TYPE_FILE,
+                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+gbp_git_vcs_init (GbpGitVcs *self)
+{
+}
+
+GFile *
+gbp_git_vcs_get_location (GbpGitVcs *self)
+{
+  g_return_val_if_fail (GBP_IS_GIT_VCS (self), NULL);
+  return self->location;
+}
+
+GgitRepository *
+gbp_git_vcs_get_repository (GbpGitVcs *self)
+{
+  g_return_val_if_fail (GBP_IS_GIT_VCS (self), NULL);
+  return self->repository;
+}
+
+static GFile *
+gbp_git_vcs_get_workdir (IdeVcs *vcs)
+{
+  return GBP_GIT_VCS (vcs)->workdir;
+}
+
+static gchar *
+gbp_git_vcs_get_branch_name (IdeVcs *vcs)
+{
+  gchar *ret;
+
+  g_return_val_if_fail (GBP_IS_GIT_VCS (vcs), NULL);
+
+  ide_object_lock (IDE_OBJECT (vcs));
+  ret = g_strdup (GBP_GIT_VCS (vcs)->branch);
+  ide_object_unlock (IDE_OBJECT (vcs));
+
+  return g_steal_pointer (&ret);
+}
+
+static IdeVcsConfig *
+gbp_git_vcs_get_config (IdeVcs *vcs)
+{
+  return g_object_new (GBP_TYPE_GIT_VCS_CONFIG, NULL);
+}
+static gboolean
+gbp_git_vcs_is_ignored (IdeVcs  *vcs,
+                        GFile   *file,
+                        GError **error)
+{
+  g_autofree gchar *name = NULL;
+  GbpGitVcs *self = (GbpGitVcs *)vcs;
+  gboolean ret = FALSE;
+
+  g_assert (GBP_IS_GIT_VCS (self));
+  g_assert (G_IS_FILE (file));
+
+  /* Note: this function is required to be thread-safe so that workers
+   *       can check if files are ignored from a thread without
+   *       round-tripping to the main thread.
+   */
+
+  /* self->workdir is not changed after creation, so safe
+   * to access it from a thread.
+   */
+  name = g_file_get_relative_path (self->workdir, file);
+  if (g_strcmp0 (name, ".git") == 0)
+    return TRUE;
+
+  /*
+   * If we have a valid name to work with, we want to query the
+   * repository. But this could be called from a thread, so ensure
+   * we are the only thread accessing self->repository right now.
+   */
+  if (name != NULL)
+    {
+      ide_object_lock (IDE_OBJECT (self));
+      ret = ggit_repository_path_is_ignored (self->repository, name, error);
+      ide_object_unlock (IDE_OBJECT (self));
+    }
+
+  return ret;
+}
+
+typedef struct
+{
+  GFile      *repository_location;
+  GFile      *directory_or_file;
+  GFile      *workdir;
+  GListStore *store;
+  guint       recursive : 1;
+} ListStatus;
+
+static void
+list_status_free (gpointer data)
+{
+  ListStatus *ls = data;
+
+  g_clear_object (&ls->repository_location);
+  g_clear_object (&ls->directory_or_file);
+  g_clear_object (&ls->workdir);
+  g_clear_object (&ls->store);
+  g_slice_free (ListStatus, ls);
+}
+
+static gint
+gbp_git_vcs_list_status_cb (const gchar     *path,
+                            GgitStatusFlags  flags,
+                            gpointer         user_data)
+{
+  ListStatus *state = user_data;
+  g_autoptr(GFile) file = NULL;
+  g_autoptr(IdeVcsFileInfo) info = NULL;
+  IdeVcsFileStatus status = 0;
+
+  g_assert (path != NULL);
+  g_assert (state != NULL);
+  g_assert (G_IS_LIST_STORE (state->store));
+  g_assert (G_IS_FILE (state->workdir));
+
+  file = g_file_get_child (state->workdir, path);
+
+  switch (flags)
+    {
+    case GGIT_STATUS_INDEX_DELETED:
+    case GGIT_STATUS_WORKING_TREE_DELETED:
+      status = IDE_VCS_FILE_STATUS_DELETED;
+      break;
+
+    case GGIT_STATUS_INDEX_RENAMED:
+      status = IDE_VCS_FILE_STATUS_RENAMED;
+      break;
+
+    case GGIT_STATUS_INDEX_NEW:
+    case GGIT_STATUS_WORKING_TREE_NEW:
+      status = IDE_VCS_FILE_STATUS_ADDED;
+      break;
+
+    case GGIT_STATUS_INDEX_MODIFIED:
+    case GGIT_STATUS_INDEX_TYPECHANGE:
+    case GGIT_STATUS_WORKING_TREE_MODIFIED:
+    case GGIT_STATUS_WORKING_TREE_TYPECHANGE:
+      status = IDE_VCS_FILE_STATUS_CHANGED;
+      break;
+
+    case GGIT_STATUS_IGNORED:
+      status = IDE_VCS_FILE_STATUS_IGNORED;
+      break;
+
+    case GGIT_STATUS_CURRENT:
+      status = IDE_VCS_FILE_STATUS_UNCHANGED;
+      break;
+
+    default:
+      status = IDE_VCS_FILE_STATUS_UNTRACKED;
+      break;
+    }
+
+  info = g_object_new (IDE_TYPE_VCS_FILE_INFO,
+                       "file", file,
+                       "status", status,
+                       NULL);
+
+  g_list_store_append (state->store, info);
+
+  return 0;
+}
+
+static void
+gbp_git_vcs_list_status_worker (IdeTask      *task,
+                                gpointer      source_object,
+                                gpointer      task_data,
+                                GCancellable *cancellable)
+{
+  ListStatus *state = task_data;
+  g_autoptr(GListStore) store = NULL;
+  g_autoptr(GFile) workdir = NULL;
+  g_autoptr(GgitRepository) repository = NULL;
+  g_autoptr(GgitStatusOptions) options = NULL;
+  g_autoptr(GError) error = NULL;
+  g_autofree gchar *relative = NULL;
+  gchar *strv[] = { NULL, NULL };
+
+  g_assert (IDE_IS_TASK (task));
+  g_assert (GBP_IS_GIT_VCS (source_object));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+  g_assert (state != NULL);
+  g_assert (G_IS_FILE (state->repository_location));
+
+  if (!(repository = ggit_repository_open (state->repository_location, &error)))
+    {
+      ide_task_return_error (task, g_steal_pointer (&error));
+      return;
+    }
+
+  if (!(workdir = ggit_repository_get_workdir (repository)))
+    {
+      ide_task_return_new_error (task,
+                                 G_IO_ERROR,
+                                 G_IO_ERROR_FAILED,
+                                 "Failed to locate working directory");
+      return;
+    }
+
+  g_set_object (&state->workdir, workdir);
+
+  if (state->directory_or_file != NULL)
+    relative = g_file_get_relative_path (workdir, state->directory_or_file);
+
+  strv[0] = relative;
+  options = ggit_status_options_new (GGIT_STATUS_OPTION_DEFAULT,
+                                     GGIT_STATUS_SHOW_INDEX_AND_WORKDIR,
+                                     (const gchar **)strv);
+
+  store = g_list_store_new (IDE_TYPE_VCS_FILE_INFO);
+  g_set_object (&state->store, store);
+
+  if (!ggit_repository_file_status_foreach (repository,
+                                            options,
+                                            gbp_git_vcs_list_status_cb,
+                                            state,
+                                            &error))
+    ide_task_return_error (task, g_steal_pointer (&error));
+  else
+    ide_task_return_pointer (task, g_steal_pointer (&store), g_object_unref);
+}
+
+static void
+gbp_git_vcs_list_status_async (IdeVcs              *vcs,
+                               GFile               *directory_or_file,
+                               gboolean             include_descendants,
+                               gint                 io_priority,
+                               GCancellable        *cancellable,
+                               GAsyncReadyCallback  callback,
+                               gpointer             user_data)
+{
+  GbpGitVcs *self = (GbpGitVcs *)vcs;
+  g_autoptr(IdeTask) task = NULL;
+  ListStatus *state;
+
+  IDE_ENTRY;
+
+  g_return_if_fail (GBP_IS_GIT_VCS (self));
+  g_return_if_fail (!directory_or_file || G_IS_FILE (directory_or_file));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  ide_object_lock (IDE_OBJECT (self));
+  state = g_slice_new0 (ListStatus);
+  state->directory_or_file = g_object_ref (directory_or_file);
+  state->repository_location = ggit_repository_get_location (self->repository);
+  state->recursive = !!include_descendants;
+  ide_object_unlock (IDE_OBJECT (self));
+
+  task = ide_task_new (self, cancellable, callback, user_data);
+  ide_task_set_source_tag (task, gbp_git_vcs_list_status_async);
+  ide_task_set_priority (task, io_priority);
+  ide_task_set_return_on_cancel (task, TRUE);
+  ide_task_set_task_data (task, state, list_status_free);
+
+  if (state->repository_location == NULL)
+    ide_task_return_new_error (task,
+                               G_IO_ERROR,
+                               G_IO_ERROR_FAILED,
+                               "No repository loaded");
+  else
+    ide_task_run_in_thread (task, gbp_git_vcs_list_status_worker);
+
+  IDE_EXIT;
+}
+
+static GListModel *
+gbp_git_vcs_list_status_finish (IdeVcs        *vcs,
+                                GAsyncResult  *result,
+                                GError       **error)
+{
+  g_return_val_if_fail (GBP_IS_GIT_VCS (vcs), NULL);
+  g_return_val_if_fail (IDE_IS_TASK (result), NULL);
+
+  return ide_task_propagate_pointer (IDE_TASK (result), error);
+}
+
+static void
+vcs_iface_init (IdeVcsInterface *iface)
+{
+  iface->get_workdir = gbp_git_vcs_get_workdir;
+  iface->get_branch_name = gbp_git_vcs_get_branch_name;
+  iface->get_config = gbp_git_vcs_get_config;
+  iface->is_ignored = gbp_git_vcs_is_ignored;
+  iface->list_status_async = gbp_git_vcs_list_status_async;
+  iface->list_status_finish = gbp_git_vcs_list_status_finish;
+}
+
+static void
+gbp_git_vcs_reload_worker (IdeTask      *task,
+                           gpointer      source_object,
+                           gpointer      task_data,
+                           GCancellable *cancellable)
+{
+  g_autoptr(GgitRepository) repository = NULL;
+  g_autoptr(GError) error = NULL;
+  GFile *location = task_data;
+
+  IDE_ENTRY;
+
+  g_assert (!IDE_IS_MAIN_THREAD ());
+  g_assert (IDE_IS_TASK (task));
+  g_assert (GBP_IS_GIT_VCS (source_object));
+  g_assert (G_IS_FILE (location));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  if (!(repository = ggit_repository_open (location, &error)))
+    ide_task_return_error (task, g_steal_pointer (&error));
+  else
+    ide_task_return_pointer (task, g_steal_pointer (&repository), g_object_unref);
+
+  IDE_EXIT;
+}
+
+void
+gbp_git_vcs_reload_async (GbpGitVcs           *self,
+                          GCancellable        *cancellable,
+                          GAsyncReadyCallback  callback,
+                          gpointer             user_data)
+{
+  g_autoptr(IdeTask) task = NULL;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_GIT_VCS (self));
+
+  task = ide_task_new (self, cancellable, callback, user_data);
+  ide_task_set_priority (task, G_PRIORITY_LOW);
+  ide_task_set_source_tag (task, gbp_git_vcs_reload_async);
+  ide_task_set_task_data (task, g_object_ref (self->location), g_object_unref);
+  ide_task_run_in_thread (task, gbp_git_vcs_reload_worker);
+
+  IDE_EXIT;
+}
+
+gboolean
+gbp_git_vcs_reload_finish (GbpGitVcs     *self,
+                           GAsyncResult  *result,
+                           GError       **error)
+{
+  g_autoptr(GgitRepository) repository = NULL;
+  g_autoptr(GgitRef) ref = NULL;
+
+  g_return_val_if_fail (IDE_IS_MAIN_THREAD (), FALSE);
+  g_return_val_if_fail (GBP_IS_GIT_VCS (self), FALSE);
+  g_return_val_if_fail (IDE_IS_TASK (result), FALSE);
+
+  ide_object_lock (IDE_OBJECT (self));
+
+  if (!(repository = ide_task_propagate_pointer (IDE_TASK (result), error)))
+    goto failure;
+
+  if ((ref = ggit_repository_get_head (repository, NULL)))
+    {
+      const gchar *name = ggit_ref_get_shorthand (ref);
+
+      if (name != NULL)
+        {
+          g_free (self->branch);
+          self->branch = g_strdup (name);
+          g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BRANCH_NAME]);
+        }
+    }
+
+  if (g_set_object (&self->repository, repository))
+    g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_REPOSITORY]);
+
+failure:
+  ide_object_unlock (IDE_OBJECT (self));
+
+  return repository != NULL;
+}
diff --git a/src/plugins/git/ide-git-clone-widget.h b/src/plugins/git/gbp-git-vcs.h
similarity index 66%
rename from src/plugins/git/ide-git-clone-widget.h
rename to src/plugins/git/gbp-git-vcs.h
index e85b6908a..cb337921a 100644
--- a/src/plugins/git/ide-git-clone-widget.h
+++ b/src/plugins/git/gbp-git-vcs.h
@@ -1,6 +1,6 @@
-/* ide-git-clone-widget.h
+/* gbp-git-vcs.h
  *
- * Copyright 2015-2019 Christian Hergert <chergert redhat com>
+ * 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
@@ -20,23 +20,22 @@
 
 #pragma once
 
-#include <gtk/gtk.h>
-
-#include "vcs/ide-vcs-uri.h"
+#include <libgit2-glib/ggit.h>
+#include <libide-vcs.h>
 
 G_BEGIN_DECLS
 
-#define IDE_TYPE_GIT_CLONE_WIDGET (ide_git_clone_widget_get_type())
+#define GBP_TYPE_GIT_VCS (gbp_git_vcs_get_type())
 
-G_DECLARE_FINAL_TYPE (IdeGitCloneWidget, ide_git_clone_widget, IDE, GIT_CLONE_WIDGET, GtkBin)
+G_DECLARE_FINAL_TYPE (GbpGitVcs, gbp_git_vcs, GBP, GIT_VCS, IdeObject)
 
-void     ide_git_clone_widget_set_uri      (IdeGitCloneWidget    *self,
-                                            IdeVcsUri            *uri);
-void     ide_git_clone_widget_clone_async  (IdeGitCloneWidget    *self,
+GFile          *gbp_git_vcs_get_location   (GbpGitVcs            *self);
+GgitRepository *gbp_git_vcs_get_repository (GbpGitVcs            *self);
+void            gbp_git_vcs_reload_async   (GbpGitVcs            *self,
                                             GCancellable         *cancellable,
                                             GAsyncReadyCallback   callback,
                                             gpointer              user_data);
-gboolean ide_git_clone_widget_clone_finish (IdeGitCloneWidget    *self,
+gboolean        gbp_git_vcs_reload_finish  (GbpGitVcs            *self,
                                             GAsyncResult         *result,
                                             GError              **error);
 
diff --git a/src/plugins/git/gbp-git-workbench-addin.c b/src/plugins/git/gbp-git-workbench-addin.c
new file mode 100644
index 000000000..678895804
--- /dev/null
+++ b/src/plugins/git/gbp-git-workbench-addin.c
@@ -0,0 +1,386 @@
+/* gbp-git-workbench-addin.c
+ *
+ * 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 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
+ */
+
+#define G_LOG_DOMAIN "gbp-git-workbench-addin"
+
+#include "config.h"
+
+#include <libgit2-glib/ggit.h>
+#include <libide-editor.h>
+#include <libide-io.h>
+#include <libide-threading.h>
+
+#include "gbp-git-buffer-change-monitor.h"
+#include "gbp-git-index-monitor.h"
+#include "gbp-git-vcs.h"
+#include "gbp-git-workbench-addin.h"
+
+struct _GbpGitWorkbenchAddin
+{
+  GObject             parent_instance;
+  IdeWorkbench       *workbench;
+  GbpGitIndexMonitor *monitor;
+  guint               has_loaded : 1;
+};
+
+static void
+gbp_git_workbench_addin_load_project_worker (IdeTask      *task,
+                                             gpointer      source_object,
+                                             gpointer      task_data,
+                                             GCancellable *cancellable)
+{
+  GbpGitWorkbenchAddin *self = source_object;
+  g_autoptr(GgitRepository) repository = NULL;
+  g_autoptr(GbpGitVcs) vcs = NULL;
+  g_autoptr(GFile) location = NULL;
+  g_autoptr(GFile) workdir = NULL;
+  g_autoptr(GError) error = NULL;
+  g_autofree gchar *worktree_branch = NULL;
+  GFile *directory = task_data;
+
+  g_assert (IDE_IS_TASK (task));
+  g_assert (GBP_IS_GIT_WORKBENCH_ADDIN (self));
+  g_assert (G_IS_FILE (directory));
+
+  /* Short-circuit if we don't .git */
+  if (!(location = ggit_repository_discover_full (directory, TRUE, NULL, &error)))
+    {
+      ide_task_return_new_error (task,
+                                 G_IO_ERROR,
+                                 G_IO_ERROR_NOT_SUPPORTED,
+                                 "Failed to locate git repository location");
+      return;
+    }
+
+  g_debug ("Located .git at %s", g_file_peek_path (location));
+
+  /* If @location is a regular file, we might have a git-worktree link */
+  if (g_file_query_file_type (location, 0, NULL) == G_FILE_TYPE_REGULAR)
+    {
+      g_autofree gchar *contents = NULL;
+      gsize len;
+
+      if (g_file_load_contents (location, NULL, &contents, &len, NULL, NULL))
+        {
+          IdeLineReader reader;
+          gchar *line;
+          gsize line_len;
+
+          ide_line_reader_init (&reader, contents, len);
+
+          while ((line = ide_line_reader_next (&reader, &line_len)))
+            {
+              line[line_len] = 0;
+
+              if (g_str_has_prefix (line, "gitdir: "))
+                {
+                  g_autoptr(GFile) location_parent = g_file_get_parent (location);
+                  const gchar *path = line + strlen ("gitdir: ");
+                  const gchar *branch;
+
+                  g_clear_object (&location);
+
+                  if (g_path_is_absolute (path))
+                    location = g_file_new_for_path (path);
+                  else
+                    location = g_file_resolve_relative_path (location_parent, path);
+
+                  /*
+                   * Worktrees only have a single branch, and it is the name
+                   * of the suffix of .git/worktrees/<name>
+                   */
+                  if ((branch = strrchr (line, G_DIR_SEPARATOR)))
+                    worktree_branch = g_strdup (branch + 1);
+
+                  break;
+                }
+            }
+        }
+    }
+
+  if (!(repository = ggit_repository_open (location, &error)))
+    {
+      ide_task_return_error (task, g_steal_pointer (&error));
+      return;
+    }
+
+  workdir = ggit_repository_get_workdir (repository);
+
+  g_assert (G_IS_FILE (location));
+  g_assert (G_IS_FILE (workdir));
+  g_assert (GGIT_IS_REPOSITORY (repository));
+
+  if (worktree_branch == NULL)
+    {
+      g_autoptr(GgitRef) ref = NULL;
+
+      if ((ref = ggit_repository_get_head (repository, NULL)))
+        worktree_branch = g_strdup (ggit_ref_get_shorthand (ref));
+
+      if (worktree_branch == NULL)
+        worktree_branch = g_strdup ("master");
+    }
+
+  vcs = g_object_new (GBP_TYPE_GIT_VCS,
+                      "branch-name", worktree_branch,
+                      "location", location,
+                      "repository", repository,
+                      "workdir", workdir,
+                      NULL);
+
+  ide_task_return_pointer (task, g_steal_pointer (&vcs), g_object_unref);
+}
+
+static void
+gbp_git_workbench_addin_load_project_async (IdeWorkbenchAddin   *addin,
+                                            IdeProjectInfo      *project_info,
+                                            GCancellable        *cancellable,
+                                            GAsyncReadyCallback  callback,
+                                            gpointer             user_data)
+{
+  GbpGitWorkbenchAddin *self = (GbpGitWorkbenchAddin *)addin;
+  g_autoptr(IdeTask) task = NULL;
+  GFile *directory;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_GIT_WORKBENCH_ADDIN (self));
+  g_assert (IDE_IS_PROJECT_INFO (project_info));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  self->has_loaded = TRUE;
+
+  task = ide_task_new (self, cancellable, callback, user_data);
+  ide_task_set_source_tag (task, gbp_git_workbench_addin_load_project_async);
+
+  if (!(directory = ide_project_info_get_directory (project_info)))
+    {
+      ide_task_return_new_error (task,
+                                 G_IO_ERROR,
+                                 G_IO_ERROR_NOT_SUPPORTED,
+                                 "Missing directory from project info");
+      return;
+    }
+
+  /* Try to discover the git repository from a worker thread. If we find
+   * it, we'll set the VCS on the workbench for various components to use.
+   */
+  ide_task_set_task_data (task, g_object_ref (directory), g_object_unref);
+  ide_task_run_in_thread (task, gbp_git_workbench_addin_load_project_worker);
+}
+
+static void
+gbp_git_workbench_addin_foreach_buffer_cb (IdeBuffer *buffer,
+                                           gpointer   user_data)
+{
+  GgitRepository *repository = user_data;
+  IdeBufferChangeMonitor *monitor;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (IDE_IS_BUFFER (buffer));
+  g_assert (GGIT_IS_REPOSITORY (repository));
+
+  monitor = ide_buffer_get_change_monitor (buffer);
+
+  if (GBP_IS_GIT_BUFFER_CHANGE_MONITOR (monitor))
+    gbp_git_buffer_change_monitor_set_repository (GBP_GIT_BUFFER_CHANGE_MONITOR (monitor),
+                                                  repository);
+}
+
+static void
+gbp_git_workbench_addin_reload_cb (GObject      *object,
+                                   GAsyncResult *result,
+                                   gpointer      user_data)
+{
+  GbpGitVcs *vcs = (GbpGitVcs *)object;
+  g_autoptr(GbpGitWorkbenchAddin) self = user_data;
+  g_autoptr(GError) error = NULL;
+  IdeBufferManager *buffer_manager;
+  GgitRepository *repository;
+  IdeContext *context;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_GIT_VCS (vcs));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (GBP_IS_GIT_WORKBENCH_ADDIN (self));
+
+  if (!gbp_git_vcs_reload_finish (vcs, result, &error))
+    return;
+
+  if (self->workbench == NULL)
+    return;
+
+  repository = gbp_git_vcs_get_repository (vcs);
+  context = ide_workbench_get_context (self->workbench);
+  buffer_manager = ide_buffer_manager_from_context (context);
+
+  ide_buffer_manager_foreach (buffer_manager,
+                              gbp_git_workbench_addin_foreach_buffer_cb,
+                              repository);
+}
+
+static void
+gbp_git_workbench_addin_monitor_changed_cb (GbpGitWorkbenchAddin *self,
+                                            GbpGitIndexMonitor   *monitor)
+{
+  IdeContext *context;
+  IdeVcs *vcs;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_GIT_WORKBENCH_ADDIN (self));
+  g_assert (GBP_IS_GIT_INDEX_MONITOR (monitor));
+
+  context = ide_workbench_get_context (self->workbench);
+  vcs = ide_vcs_from_context (context);
+
+  if (!GBP_IS_GIT_VCS (vcs))
+    IDE_EXIT;
+
+  gbp_git_vcs_reload_async (GBP_GIT_VCS (vcs),
+                            NULL,
+                            gbp_git_workbench_addin_reload_cb,
+                            g_object_ref (self));
+
+  IDE_EXIT;
+}
+
+static gboolean
+gbp_git_workbench_addin_load_project_finish (IdeWorkbenchAddin  *addin,
+                                             GAsyncResult       *result,
+                                             GError            **error)
+{
+  GbpGitWorkbenchAddin *self = (GbpGitWorkbenchAddin *)addin;
+  g_autoptr(GbpGitVcs) vcs = NULL;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_GIT_WORKBENCH_ADDIN (self));
+  g_assert (IDE_IS_TASK (result));
+
+  if ((vcs = ide_task_propagate_pointer (IDE_TASK (result), error)))
+    {
+      if (IDE_IS_WORKBENCH (self->workbench))
+        {
+          /* Set the vcs for the workbench */
+          ide_workbench_set_vcs (self->workbench, IDE_VCS (vcs));
+
+          if (self->monitor == NULL)
+            {
+              GFile *location = gbp_git_vcs_get_location (vcs);
+
+              self->monitor = gbp_git_index_monitor_new (location);
+
+              g_signal_connect_object (self->monitor,
+                                       "changed",
+                                       G_CALLBACK (gbp_git_workbench_addin_monitor_changed_cb),
+                                       self,
+                                       G_CONNECT_SWAPPED);
+            }
+        }
+    }
+
+  return vcs != NULL;
+}
+
+static void
+gbp_git_workbench_addin_load (IdeWorkbenchAddin *addin,
+                              IdeWorkbench      *workbench)
+{
+  GBP_GIT_WORKBENCH_ADDIN (addin)->workbench = workbench;
+}
+
+static void
+gbp_git_workbench_addin_unload (IdeWorkbenchAddin *addin,
+                                IdeWorkbench      *workbench)
+{
+  GbpGitWorkbenchAddin *self = (GbpGitWorkbenchAddin *)addin;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_GIT_WORKBENCH_ADDIN (self));
+  g_assert (IDE_IS_WORKBENCH (workbench));
+
+  g_clear_object (&self->monitor);
+
+  self->workbench = NULL;
+}
+
+static void
+load_git_for_editor_cb (GObject      *object,
+                        GAsyncResult *result,
+                        gpointer      user_data)
+{
+  gbp_git_workbench_addin_load_project_finish (IDE_WORKBENCH_ADDIN (object), result, NULL);
+}
+
+static void
+gbp_git_workbench_addin_workspace_added (IdeWorkbenchAddin *addin,
+                                         IdeWorkspace      *workspace)
+{
+  GbpGitWorkbenchAddin *self = (GbpGitWorkbenchAddin *)addin;
+
+  g_assert (GBP_IS_GIT_WORKBENCH_ADDIN (self));
+  g_assert (IDE_IS_WORKSPACE (workspace));
+
+  if (!self->has_loaded)
+    {
+      /* If we see a new IdeEditorWorkspace without having loaded a project,
+       * that means that we are in a non-project scenario (dedicated editor
+       * window). We can try our best to load a git repository based on
+       * the files that are loaded.
+       */
+      if (IDE_IS_EDITOR_WORKSPACE (workspace))
+        {
+          IdeContext *context = ide_workbench_get_context (self->workbench);
+          g_autoptr(GFile) workdir = ide_context_ref_workdir (context);
+          g_autoptr(IdeTask) task = NULL;
+
+          self->has_loaded = TRUE;
+
+          task = ide_task_new (self, NULL, load_git_for_editor_cb, NULL);
+          ide_task_set_source_tag (task, gbp_git_workbench_addin_workspace_added);
+          ide_task_set_task_data (task, g_object_ref (workdir), g_object_unref);
+          ide_task_run_in_thread (task, gbp_git_workbench_addin_load_project_worker);
+        }
+    }
+}
+
+static void
+workbench_addin_iface_init (IdeWorkbenchAddinInterface *iface)
+{
+  iface->load = gbp_git_workbench_addin_load;
+  iface->unload = gbp_git_workbench_addin_unload;
+  iface->load_project_async = gbp_git_workbench_addin_load_project_async;
+  iface->load_project_finish = gbp_git_workbench_addin_load_project_finish;
+  iface->workspace_added = gbp_git_workbench_addin_workspace_added;
+}
+
+G_DEFINE_TYPE_WITH_CODE (GbpGitWorkbenchAddin, gbp_git_workbench_addin, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (IDE_TYPE_WORKBENCH_ADDIN,
+                                                workbench_addin_iface_init))
+
+static void
+gbp_git_workbench_addin_class_init (GbpGitWorkbenchAddinClass *klass)
+{
+}
+
+static void
+gbp_git_workbench_addin_init (GbpGitWorkbenchAddin *self)
+{
+}
diff --git a/src/plugins/git/gbp-git-workbench-addin.h b/src/plugins/git/gbp-git-workbench-addin.h
new file mode 100644
index 000000000..218b02004
--- /dev/null
+++ b/src/plugins/git/gbp-git-workbench-addin.h
@@ -0,0 +1,31 @@
+/* gbp-git-workbench-addin.h
+ *
+ * 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 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
+ */
+
+#pragma once
+
+#include <libide-gui.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_GIT_WORKBENCH_ADDIN (gbp_git_workbench_addin_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpGitWorkbenchAddin, gbp_git_workbench_addin, GBP, GIT_WORKBENCH_ADDIN, GObject)
+
+G_END_DECLS
diff --git a/src/plugins/git/ide-git-plugin.c b/src/plugins/git/git-plugin.c
similarity index 56%
rename from src/plugins/git/ide-git-plugin.c
rename to src/plugins/git/git-plugin.c
index 2a5d0aa65..4d4d016cc 100644
--- a/src/plugins/git/ide-git-plugin.c
+++ b/src/plugins/git/git-plugin.c
@@ -1,6 +1,6 @@
-/* ide-git-plugin.c
+/* git-plugin.c
  *
- * Copyright 2015-2019 Christian Hergert <chergert redhat com>
+ * 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
@@ -18,16 +18,24 @@
  * SPDX-License-Identifier: GPL-3.0-or-later
  */
 
+#define G_LOG_DOMAIN "git-plugin"
+
+#include "config.h"
+
 #include <libpeas/peas.h>
-#include <ide.h>
+#include <libgit2-glib/ggit.h>
+#include <libide-editor.h>
+#include <libide-foundry.h>
+#include <libide-vcs.h>
 
-#include "ide-git-dependency-updater.h"
-#include "ide-git-genesis-addin.h"
-#include "ide-git-pipeline-addin.h"
-#include "ide-git-remote-callbacks.h"
-#include "ide-git-vcs.h"
-#include "ide-git-vcs-config.h"
-#include "ide-git-vcs-initializer.h"
+#include "gbp-git-buffer-addin.h"
+#include "gbp-git-dependency-updater.h"
+#include "gbp-git-pipeline-addin.h"
+#include "gbp-git-remote-callbacks.h"
+#include "gbp-git-vcs-cloner.h"
+#include "gbp-git-vcs-config.h"
+#include "gbp-git-vcs-initializer.h"
+#include "gbp-git-workbench-addin.h"
 
 static gboolean
 register_ggit (void)
@@ -54,35 +62,35 @@ register_ggit (void)
 }
 
 
-void
-ide_git_register_types (PeasObjectModule *module)
+_IDE_EXTERN void
+_gbp_git_register_types (PeasObjectModule *module)
 {
   if (register_ggit ())
     {
-      /* HACK: we load this type by name from the flatpak plugin, so make
-       * sure it exists.
-       */
-      g_type_ensure (IDE_TYPE_GIT_REMOTE_CALLBACKS);
+      ide_g_file_add_ignored_pattern (".git");
 
       peas_object_module_register_extension_type (module,
-                                                  IDE_TYPE_VCS,
-                                                  IDE_TYPE_GIT_VCS);
+                                                  IDE_TYPE_BUFFER_ADDIN,
+                                                  GBP_TYPE_GIT_BUFFER_ADDIN);
       peas_object_module_register_extension_type (module,
-                                                  IDE_TYPE_VCS_CONFIG,
-                                                  IDE_TYPE_GIT_VCS_CONFIG);
+                                                  IDE_TYPE_BUILD_PIPELINE_ADDIN,
+                                                  GBP_TYPE_GIT_PIPELINE_ADDIN);
       peas_object_module_register_extension_type (module,
-                                                  IDE_TYPE_VCS_INITIALIZER,
-                                                  IDE_TYPE_GIT_VCS_INITIALIZER);
+                                                  IDE_TYPE_DEPENDENCY_UPDATER,
+                                                  GBP_TYPE_GIT_DEPENDENCY_UPDATER);
       peas_object_module_register_extension_type (module,
-                                                  IDE_TYPE_GENESIS_ADDIN,
-                                                  IDE_TYPE_GIT_GENESIS_ADDIN);
+                                                  IDE_TYPE_VCS_CONFIG,
+                                                  GBP_TYPE_GIT_VCS_CONFIG);
       peas_object_module_register_extension_type (module,
-                                                  IDE_TYPE_BUILD_PIPELINE_ADDIN,
-                                                  IDE_TYPE_GIT_PIPELINE_ADDIN);
+                                                  IDE_TYPE_VCS_CLONER,
+                                                  GBP_TYPE_GIT_VCS_CLONER);
       peas_object_module_register_extension_type (module,
-                                                  IDE_TYPE_DEPENDENCY_UPDATER,
-                                                  IDE_TYPE_GIT_DEPENDENCY_UPDATER);
+                                                  IDE_TYPE_VCS_INITIALIZER,
+                                                  GBP_TYPE_GIT_VCS_INITIALIZER);
+      peas_object_module_register_extension_type (module,
+                                                  IDE_TYPE_WORKBENCH_ADDIN,
+                                                  GBP_TYPE_GIT_WORKBENCH_ADDIN);
 
-      ide_vcs_register_ignored (".git");
+      g_type_ensure (GBP_TYPE_GIT_REMOTE_CALLBACKS);
     }
 }
diff --git a/src/plugins/git/git.gresource.xml b/src/plugins/git/git.gresource.xml
index c5c3a8dc1..73415c06f 100644
--- a/src/plugins/git/git.gresource.xml
+++ b/src/plugins/git/git.gresource.xml
@@ -1,10 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <gresources>
-  <gresource prefix="/org/gnome/builder/plugins">
+  <gresource prefix="/plugins/git">
     <file>git.plugin</file>
   </gresource>
-  <gresource prefix="/org/gnome/builder/plugins/git-plugin">
-    <file>ide-git-clone-widget.ui</file>
-    <file>themes/shared.css</file>
-  </gresource>
 </gresources>
diff --git a/src/plugins/git/git.plugin b/src/plugins/git/git.plugin
index 040eca3a4..e190366cc 100644
--- a/src/plugins/git/git.plugin
+++ b/src/plugins/git/git.plugin
@@ -1,8 +1,8 @@
 [Plugin]
-Module=git-plugin
-Name=Git
-Description=Provides support for the Git version control system
 Authors=Christian Hergert <christian hergert me>
-Copyright=Copyright © 2015 Christian Hergert
 Builtin=true
-Embedded=ide_git_register_types
+Copyright=Copyright © 2015-2018 Christian Hergert
+Description=Support for the Git version control system
+Embedded=_gbp_git_register_types
+Module=git
+Name=Git
diff --git a/src/plugins/git/meson.build b/src/plugins/git/meson.build
index 69ae084cc..29d471a14 100644
--- a/src/plugins/git/meson.build
+++ b/src/plugins/git/meson.build
@@ -1,40 +1,27 @@
-if get_option('with_git')
+if get_option('plugin_git')
 
-git_resources = gnome.compile_resources(
+plugins_sources += files([
+  'git-plugin.c',
+  'gbp-git-buffer-addin.c',
+  'gbp-git-buffer-change-monitor.c',
+  'gbp-git-dependency-updater.c',
+  'gbp-git-index-monitor.c',
+  'gbp-git-pipeline-addin.c',
+  'gbp-git-remote-callbacks.c',
+  'gbp-git-submodule-stage.c',
+  'gbp-git-vcs.c',
+  'gbp-git-vcs-cloner.c',
+  'gbp-git-vcs-config.c',
+  'gbp-git-vcs-initializer.c',
+  'gbp-git-workbench-addin.c',
+])
+
+plugin_git_resources = gnome.compile_resources(
   'git-resources',
   'git.gresource.xml',
-  c_name: 'ide_git',
+  c_name: 'gbp_git',
 )
 
-git_sources = [
-  'ide-git-buffer-change-monitor.c',
-  'ide-git-buffer-change-monitor.h',
-  'ide-git-clone-widget.c',
-  'ide-git-clone-widget.h',
-  'ide-git-dependency-updater.c',
-  'ide-git-dependency-updater.h',
-  'ide-git-genesis-addin.c',
-  'ide-git-genesis-addin.h',
-  'ide-git-pipeline-addin.c',
-  'ide-git-pipeline-addin.h',
-  'ide-git-plugin.c',
-  'ide-git-remote-callbacks.c',
-  'ide-git-remote-callbacks.h',
-  'ide-git-submodule-stage.c',
-  'ide-git-submodule-stage.h',
-  'ide-git-vcs.c',
-  'ide-git-vcs.h',
-  'ide-git-vcs-config.c',
-  'ide-git-vcs-config.h',
-  'ide-git-vcs-initializer.c',
-  'ide-git-vcs-initializer.h',
-]
-
-gnome_builder_plugins_deps += [
-  libgit_dep,
-]
-
-gnome_builder_plugins_sources += files(git_sources)
-gnome_builder_plugins_sources += git_resources[0]
+plugins_sources += plugin_git_resources[0]
 
 endif



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