[gnome-builder] git: port git plugin API to use gnome-builder-git daemon



commit 77363f3829aa746521670b11d0314e5885aa28d5
Author: Christian Hergert <chergert redhat com>
Date:   Wed Apr 10 00:31:14 2019 -0700

    git: port git plugin API to use gnome-builder-git daemon
    
    This ports the various plugin API for Builder's git integration to use
    the new gnome-builder-git daemon. The daemon is spawned and uses the
    DBus marshalling format (without a DBus daemon) over STDIN/STDOUT pipes
    to the subprocess.
    
    The daemon itself manages most of the state now and allows for some
    more threading from DBus proxies in the Builder code-base.

 src/plugins/git/gbp-git-buffer-addin.c          |   47 +-
 src/plugins/git/gbp-git-buffer-change-monitor.c | 1036 ++++++-----------------
 src/plugins/git/gbp-git-buffer-change-monitor.h |   24 +-
 src/plugins/git/gbp-git-dependency-updater.c    |   86 +-
 src/plugins/git/gbp-git-vcs-cloner.c            |  160 ++--
 src/plugins/git/gbp-git-vcs-cloner.h            |    2 +-
 src/plugins/git/gbp-git-vcs-config.c            |  194 ++---
 src/plugins/git/gbp-git-vcs-config.h            |    6 +-
 src/plugins/git/gbp-git-vcs-initializer.c       |  117 ++-
 src/plugins/git/gbp-git-vcs-initializer.h       |    2 +-
 src/plugins/git/gbp-git-vcs.c                   |  954 ++++++++-------------
 src/plugins/git/gbp-git-vcs.h                   |   16 +-
 src/plugins/git/gbp-git-workbench-addin.c       |  369 +++-----
 src/plugins/git/gbp-git-workbench-addin.h       |    2 +-
 src/plugins/git/git-plugin.c                    |   76 +-
 src/plugins/git/git.plugin                      |    2 +-
 src/plugins/git/meson.build                     |    9 +-
 17 files changed, 1055 insertions(+), 2047 deletions(-)
---
diff --git a/src/plugins/git/gbp-git-buffer-addin.c b/src/plugins/git/gbp-git-buffer-addin.c
index 274366e88..441256226 100644
--- a/src/plugins/git/gbp-git-buffer-addin.c
+++ b/src/plugins/git/gbp-git-buffer-addin.c
@@ -22,7 +22,6 @@
 
 #include "config.h"
 
-#include <libgit2-glib/ggit.h>
 #include <libide-vcs.h>
 
 #include "gbp-git-buffer-addin.h"
@@ -31,8 +30,8 @@
 
 struct _GbpGitBufferAddin
 {
-  GObject                    parent_instance;
-  GbpGitBufferChangeMonitor *monitor;
+  GObject                 parent_instance;
+  IdeBufferChangeMonitor *monitor;
 };
 
 static void
@@ -41,9 +40,9 @@ gbp_git_buffer_addin_file_laoded (IdeBufferAddin *addin,
                                   GFile          *file)
 {
   GbpGitBufferAddin *self = (GbpGitBufferAddin *)addin;
-  g_autoptr(GbpGitBufferChangeMonitor) monitor = NULL;
+  g_autoptr(IdeBufferChangeMonitor) monitor = NULL;
   g_autoptr(IdeContext) context = NULL;
-  GgitRepository *repository;
+  IpcGitRepository *repository;
   IdeObjectBox *box;
   IdeVcs *vcs;
 
@@ -51,38 +50,19 @@ gbp_git_buffer_addin_file_laoded (IdeBufferAddin *addin,
   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))
+  if (!(context = ide_buffer_ref_context (buffer)) ||
+      !(vcs = ide_context_peek_child_typed (context, IDE_TYPE_VCS)) ||
+      !GBP_IS_GIT_VCS (vcs) ||
+      !(repository = gbp_git_vcs_get_repository (GBP_GIT_VCS (vcs))) ||
+      !(monitor = gbp_git_buffer_change_monitor_new (buffer, repository, file, NULL, NULL)))
     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);
+  ide_clear_and_destroy_object (&self->monitor);
+  self->monitor = g_steal_pointer (&monitor);
 
   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));
+  ide_buffer_set_change_monitor (buffer, self->monitor);
 }
 
 static void
@@ -137,7 +117,7 @@ gbp_git_buffer_addin_settle_async (IdeBufferAddin      *addin,
   if (self->monitor == NULL)
     ide_task_return_boolean (task, TRUE);
   else
-    gbp_git_buffer_change_monitor_wait_async (self->monitor,
+    gbp_git_buffer_change_monitor_wait_async (GBP_GIT_BUFFER_CHANGE_MONITOR (self->monitor),
                                               cancellable,
                                               gbp_git_buffer_addin_settle_cb,
                                               g_steal_pointer (&task));
@@ -155,7 +135,6 @@ 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;
   iface->settle_async = gbp_git_buffer_addin_settle_async;
   iface->settle_finish = gbp_git_buffer_addin_settle_finish;
diff --git a/src/plugins/git/gbp-git-buffer-change-monitor.c b/src/plugins/git/gbp-git-buffer-change-monitor.c
index 72f3ce13f..5a92f7f6a 100644
--- a/src/plugins/git/gbp-git-buffer-change-monitor.c
+++ b/src/plugins/git/gbp-git-buffer-change-monitor.c
@@ -1,6 +1,6 @@
 /* gbp-git-buffer-change-monitor.c
  *
- * Copyright 2015-2019 Christian Hergert <christian hergert me>
+ * Copyright 2015-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,508 +18,195 @@
  * SPDX-License-Identifier: GPL-3.0-or-later
  */
 
-/* Some code from this file is loosely based around the git-diff
- * plugin from Atom. Namely, API usage for iterating through hunks
- * containing changes. It's license is provided below.
- */
-
-/*
- * Copyright (c) 2014 GitHub Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy of
- * this software and associated documentation files (the "Software"), to deal in
- * the Software without restriction, including without limitation the rights to
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is furnished to do
- * so, subject to the following conditions:
-
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
-
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
 #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 "gbp-git-buffer-change-monitor.h"
-#include "gbp-git-vcs.h"
+#include "config.h"
 
-#include "line-cache.h"
+#include <dazzle.h>
+#include <string.h>
 
-#define DELAY_CHANGED_SEC 1
+#include "daemon/ipc-git-change-monitor.h"
+#include "daemon/line-cache.h"
 
-/**
- * SECTION:gbp-git-buffer-change-monitor
- *
- * 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 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.
- *
- * Since: 3.32
- */
+#include "gbp-git-buffer-change-monitor.h"
 
 struct _GbpGitBufferChangeMonitor
 {
-  IdeBufferChangeMonitor  parent_instance;
-
-  DzlSignalGroup         *signal_group;
-
-  GgitRepository         *repository;
-  GArray                 *lines;
-
-  GgitBlob               *cached_blob;
+  IdeBufferChangeMonitor  parent;
+  IpcGitChangeMonitor    *proxy;
+  DzlSignalGroup         *buffer_signals;
   LineCache              *cache;
-
-  GQueue                  wait_tasks;
-
-  guint                   changed_timeout;
-
-  guint                   state_dirty : 1;
-  guint                   in_calculation : 1;
+  guint                   last_change_count;
+  guint                   queued_source;
   guint                   delete_range_requires_recalculation : 1;
-  guint                   is_child_of_workdir : 1;
-  guint                   in_failed_state : 1;
 };
 
-typedef struct
-{
-  GgitRepository *repository;
-  LineCache      *cache;
-  GFile          *file;
-  GBytes         *content;
-  GgitBlob       *blob;
-  IdeObject      *lock_object;
-  guint           is_child_of_workdir : 1;
-} DiffTask;
-
-typedef struct
-{
-  gint old_start;
-  gint old_lines;
-  gint new_start;
-  gint new_lines;
-} Range;
+enum { SLOW, FAST };
+static const guint g_delay[] = { 750, 50 };
 
 G_DEFINE_TYPE (GbpGitBufferChangeMonitor, gbp_git_buffer_change_monitor, IDE_TYPE_BUFFER_CHANGE_MONITOR)
 
-DZL_DEFINE_COUNTER (instances, "GbpGitBufferChangeMonitor", "Instances", "The number of git buffer change 
monitor instances.");
-
-enum {
-  PROP_0,
-  PROP_REPOSITORY,
-  LAST_PROP
-};
-
-static GParamSpec  *properties [LAST_PROP];
-static GAsyncQueue *work_queue;
-static GThread     *work_thread;
-
-static void
-diff_task_free (gpointer data)
-{
-  DiffTask *diff = data;
-
-  if (diff)
-    {
-      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->cache, line_cache_free);
-      g_clear_pointer (&diff->content, g_bytes_unref);
-      g_slice_free (DiffTask, diff);
-    }
-}
-
-static void
-complete_wait_tasks (GbpGitBufferChangeMonitor *self,
-                     GParamSpec                *pspec,
-                     IdeTask                   *calculate_task)
-{
-  gpointer taskptr;
-
-  g_assert (IDE_IS_MAIN_THREAD ());
-  g_assert (GBP_IS_GIT_BUFFER_CHANGE_MONITOR (self));
-  g_assert (IDE_IS_TASK (calculate_task));
-
-  while ((taskptr = g_queue_pop_head (&self->wait_tasks)))
-    {
-      g_autoptr(IdeTask) task = taskptr;
-
-      ide_task_return_boolean (task, TRUE);
-    }
-}
-
-static LineCache *
-gbp_git_buffer_change_monitor_calculate_finish (GbpGitBufferChangeMonitor  *self,
-                                                GAsyncResult               *result,
-                                                GError                    **error)
+static gboolean
+queued_update_source_cb (GbpGitBufferChangeMonitor *self)
 {
-  IdeTask *task = (IdeTask *)result;
-  DiffTask *diff;
-
   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);
-
-  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));
-
-      /* Keep the blob around for future use */
-      if (diff->blob != self->cached_blob)
-        g_set_object (&self->cached_blob, diff->blob);
+  self->queued_source = 0;
 
-      /* If the file is a child of the working directory, we need to know */
-      self->is_child_of_workdir = diff->is_child_of_workdir;
-    }
+  gbp_git_buffer_change_monitor_wait_async (self, NULL, NULL, NULL);
 
-  return ide_task_propagate_pointer (task, error);
+  return G_SOURCE_REMOVE;
 }
 
 static void
-gbp_git_buffer_change_monitor_calculate_async (GbpGitBufferChangeMonitor *self,
-                                               GCancellable              *cancellable,
-                                               GAsyncReadyCallback        callback,
-                                               gpointer                   user_data)
+gbp_git_buffer_change_monitor_queue_update (GbpGitBufferChangeMonitor *self,
+                                            gboolean                  fast)
 {
-  g_autoptr(IdeTask) task = NULL;
-  g_autoptr(IdeContext) context = NULL;
-  GbpGitVcs *vcs;
-  IdeBuffer *buffer;
-  DiffTask *diff;
-  GFile *file;
+  guint delay;
 
   g_assert (GBP_IS_GIT_BUFFER_CHANGE_MONITOR (self));
-  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
-  g_assert (self->repository != NULL);
-
-  self->state_dirty = FALSE;
-
-  task = ide_task_new (self, cancellable, callback, user_data);
-  ide_task_set_source_tag (task, gbp_git_buffer_change_monitor_calculate_async);
-
-  g_signal_connect_object (task,
-                           "notify::completed",
-                           G_CALLBACK (complete_wait_tasks),
-                           self,
-                           G_CONNECT_SWAPPED);
-
-  buffer = ide_buffer_change_monitor_get_buffer (IDE_BUFFER_CHANGE_MONITOR (self));
-  g_assert (IDE_IS_BUFFER (buffer));
-
-  file = ide_buffer_get_file (buffer);
-  g_assert (G_IS_FILE (file));
 
-  context = ide_object_ref_context (IDE_OBJECT (self));
-  vcs = ide_context_peek_child_typed (context, GBP_TYPE_GIT_VCS);
+  fast = !!fast;
 
-  if (!GBP_IS_GIT_VCS (vcs))
-    {
-      ide_task_return_new_error (task,
-                                 G_IO_ERROR,
-                                 G_IO_ERROR_CANCELLED,
-                                 "Cannot provide changes, not connected to GbpGitVcs.");
-      return;
-    }
-
-  diff = g_slice_new0 (DiffTask);
-  diff->file = g_object_ref (file);
-  diff->repository = g_object_ref (self->repository);
-  diff->cache = line_cache_new ();
-  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));
+  /* Re-use existing source if this is slow */
+  if (fast == SLOW && self->queued_source)
+    return;
 
-  ide_task_set_task_data (task, diff, diff_task_free);
+  delay = g_delay[fast];
 
-  self->in_calculation = TRUE;
+  g_clear_handle_id (&self->queued_source, g_source_remove);
 
-  g_async_queue_push (work_queue, g_steal_pointer (&task));
+  self->queued_source =
+    gdk_threads_add_timeout_full (G_PRIORITY_HIGH,
+                                  delay,
+                                  (GSourceFunc) queued_update_source_cb,
+                                  g_object_ref (self),
+                                  g_object_unref);
 }
 
 static void
-foreach_cb (gpointer data,
-            gpointer user_data)
-{
-  const LineEntry *entry = data;
-  struct {
-    IdeBufferChangeMonitorForeachFunc func;
-    gpointer user_data;
-  } *state = user_data;
-  IdeBufferLineChange change = 0;
-
-  if (entry->mark & LINE_MARK_ADDED)
-    change |= IDE_BUFFER_LINE_CHANGE_ADDED;
-
-  if (entry->mark & LINE_MARK_REMOVED)
-    change |= IDE_BUFFER_LINE_CHANGE_DELETED;
-
-  if (entry->mark & LINE_MARK_PREVIOUS_REMOVED)
-    change |= IDE_BUFFER_LINE_CHANGE_PREVIOUS_DELETED;
-
-  if (entry->mark & LINE_MARK_CHANGED)
-    change |= IDE_BUFFER_LINE_CHANGE_CHANGED;
-
-  state->func (entry->line, change, state->user_data);
-}
-
-static void
-gbp_git_buffer_change_monitor_foreach_change (IdeBufferChangeMonitor            *monitor,
-                                              guint                              begin_line,
-                                              guint                              end_line,
-                                              IdeBufferChangeMonitorForeachFunc  callback,
-                                              gpointer                           user_data)
+gbp_git_buffer_change_monitor_destroy (IdeObject *object)
 {
-  GbpGitBufferChangeMonitor *self = (GbpGitBufferChangeMonitor *)monitor;
-  struct {
-    IdeBufferChangeMonitorForeachFunc func;
-    gpointer user_data;
-  } state = { callback, user_data };
-
-  g_assert (GBP_IS_GIT_BUFFER_CHANGE_MONITOR (self));
-  g_assert (callback != NULL);
-
-  if (end_line == G_MAXUINT)
-    end_line--;
+  GbpGitBufferChangeMonitor *self = (GbpGitBufferChangeMonitor *)object;
 
-  if (self->cache == NULL)
+  if (self->buffer_signals)
     {
-      /* 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;
+      dzl_signal_group_set_target (self->buffer_signals, NULL);
+      g_clear_object (&self->buffer_signals);
     }
 
-  line_cache_foreach_in_range (self->cache, begin_line, end_line, foreach_cb, &state);
-}
-
-static IdeBufferLineChange
-gbp_git_buffer_change_monitor_get_change (IdeBufferChangeMonitor *monitor,
-                                          guint                   line)
-{
-  GbpGitBufferChangeMonitor *self = (GbpGitBufferChangeMonitor *)monitor;
-  guint mark;
-
-  /* Don't imply changes we don't know are real, in the case that
-   * we failed to communicate with git properly about the blob diff.
-   */
-  if (self->in_failed_state)
-    return IDE_BUFFER_LINE_CHANGE_NONE;
-
-  if (self->cache == NULL)
+  if (self->proxy != NULL)
     {
-      /* If within working directory, synthesize line addition. */
-      if (self->is_child_of_workdir)
-        return IDE_BUFFER_LINE_CHANGE_ADDED;
-      return IDE_BUFFER_LINE_CHANGE_NONE;
+      ipc_git_change_monitor_call_close (self->proxy, NULL, NULL, NULL);
+      g_clear_object (&self->proxy);
     }
 
-  mark = line_cache_get_mark (self->cache, line + 1);
-
-  if (mark & LINE_MARK_ADDED)
-    return IDE_BUFFER_LINE_CHANGE_ADDED;
-  else if (mark & LINE_MARK_REMOVED)
-    return IDE_BUFFER_LINE_CHANGE_DELETED;
-  else if (mark & LINE_MARK_CHANGED)
-    return IDE_BUFFER_LINE_CHANGE_CHANGED;
-  else
-    return IDE_BUFFER_LINE_CHANGE_NONE;
-}
-
-void
-gbp_git_buffer_change_monitor_set_repository (GbpGitBufferChangeMonitor *self,
-                                              GgitRepository            *repository)
-{
-  gboolean do_reload;
-
-  g_return_if_fail (GBP_IS_GIT_BUFFER_CHANGE_MONITOR (self));
-  g_return_if_fail (GGIT_IS_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]);
+  g_clear_pointer (&self->cache, line_cache_free);
+  g_clear_handle_id (&self->queued_source, g_source_remove);
 
-      if (do_reload)
-        ide_buffer_change_monitor_reload (IDE_BUFFER_CHANGE_MONITOR (self));
-    }
+  IDE_OBJECT_CLASS (gbp_git_buffer_change_monitor_parent_class)->destroy (object);
 }
 
 static void
-gbp_git_buffer_change_monitor__calculate_cb (GObject      *object,
-                                             GAsyncResult *result,
-                                             gpointer      user_data_unused)
+gbp_git_buffer_change_monitor_load (IdeBufferChangeMonitor *monitor,
+                                    IdeBuffer              *buffer)
 {
-  GbpGitBufferChangeMonitor *self = (GbpGitBufferChangeMonitor *)object;
-  LineCache *cache;
-  g_autoptr(GError) error = NULL;
+  GbpGitBufferChangeMonitor *self = (GbpGitBufferChangeMonitor *)monitor;
 
+  g_assert (IDE_IS_MAIN_THREAD ());
   g_assert (GBP_IS_GIT_BUFFER_CHANGE_MONITOR (self));
-  g_assert (user_data_unused == NULL);
-
-  self->in_calculation = FALSE;
-
-  cache = gbp_git_buffer_change_monitor_calculate_finish (self, result, &error);
-
-  if (cache == NULL)
-    {
-      if (!self->in_failed_state && !g_error_matches (error, GGIT_ERROR, GGIT_ERROR_NOTFOUND))
-        {
-          ide_object_warning (self,
-                              /* translators: %s is replaced with the error string from git */
-                              _("There was a failure while calculating line changes from git. The exact 
error was: %s"),
-                              error->message);
-          self->in_failed_state = TRUE;
-        }
-    }
-  else
-    {
-      g_clear_pointer (&self->cache, line_cache_free);
-      self->cache = g_steal_pointer (&cache);
-      self->in_failed_state = FALSE;
-    }
-
-  ide_buffer_change_monitor_emit_changed (IDE_BUFFER_CHANGE_MONITOR (self));
+  g_assert (IDE_IS_BUFFER (buffer));
 
-  /* Recalculate if the buffer has changed since last request. */
-  if (self->state_dirty)
-    gbp_git_buffer_change_monitor_calculate_async (self,
-                                                   NULL,
-                                                   gbp_git_buffer_change_monitor__calculate_cb,
-                                                   NULL);
+  dzl_signal_group_set_target (self->buffer_signals, buffer);
+  gbp_git_buffer_change_monitor_queue_update (self, FAST);
 }
 
 static void
-gbp_git_buffer_change_monitor_recalculate (GbpGitBufferChangeMonitor *self)
+gbp_git_buffer_change_monitor_reload (IdeBufferChangeMonitor *monitor)
 {
-  g_assert (GBP_IS_GIT_BUFFER_CHANGE_MONITOR (self));
+  GbpGitBufferChangeMonitor *self = (GbpGitBufferChangeMonitor *)monitor;
 
-  self->state_dirty = TRUE;
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_GIT_BUFFER_CHANGE_MONITOR (self));
 
-  if (!self->in_calculation)
-    gbp_git_buffer_change_monitor_calculate_async (self,
-                                                   NULL,
-                                                   gbp_git_buffer_change_monitor__calculate_cb,
-                                                   NULL);
+  gbp_git_buffer_change_monitor_queue_update (self, FAST);
 }
 
 static void
-gbp_git_buffer_change_monitor__buffer_delete_range_after_cb (GbpGitBufferChangeMonitor *self,
-                                                             GtkTextIter               *begin,
-                                                             GtkTextIter               *end,
-                                                             IdeBuffer                 *buffer)
+buffer_delete_range_after_cb (GbpGitBufferChangeMonitor *self,
+                              GtkTextIter               *begin,
+                              GtkTextIter               *end,
+                              IdeBuffer                 *buffer)
 {
-  IDE_ENTRY;
-
   g_assert (GBP_IS_GIT_BUFFER_CHANGE_MONITOR (self));
-  g_assert (begin);
-  g_assert (end);
+  g_assert (begin != NULL);
+  g_assert (end != NULL);
   g_assert (IDE_IS_BUFFER (buffer));
 
   if (self->delete_range_requires_recalculation)
     {
       self->delete_range_requires_recalculation = FALSE;
-      gbp_git_buffer_change_monitor_recalculate (self);
+      gbp_git_buffer_change_monitor_queue_update (self, FAST);
     }
-
-  IDE_EXIT;
 }
 
 static void
-gbp_git_buffer_change_monitor__buffer_delete_range_cb (GbpGitBufferChangeMonitor *self,
-                                                       GtkTextIter               *begin,
-                                                       GtkTextIter               *end,
-                                                       IdeBuffer                 *buffer)
+buffer_delete_range_cb (GbpGitBufferChangeMonitor *self,
+                        GtkTextIter               *begin,
+                        GtkTextIter               *end,
+                        IdeBuffer                 *buffer)
 {
-  IdeBufferLineChange change;
-
-  IDE_ENTRY;
+  guint begin_line;
 
   g_assert (GBP_IS_GIT_BUFFER_CHANGE_MONITOR (self));
   g_assert (begin != NULL);
   g_assert (end != NULL);
   g_assert (IDE_IS_BUFFER (buffer));
 
+  begin_line = gtk_text_iter_get_line (begin);
+
   /*
    * We need to recalculate the diff when text is deleted if:
    *
    * 1) The range includes a newline.
    * 2) The current line change is set to NONE.
    *
-   * 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 gbp_git_buffer_change_monitor__buffer_changed_cb().
+   * 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 gbp_git_buffer_change_monitor__buffer_changed_cb().
    */
 
-  if (gtk_text_iter_get_line (begin) != gtk_text_iter_get_line (end))
+  if (begin_line != gtk_text_iter_get_line (end))
     IDE_GOTO (recalculate);
 
-  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)
+  if (self->cache == NULL || !line_cache_get_mark (self->cache, begin_line))
     IDE_GOTO (recalculate);
 
-  IDE_EXIT;
+  return;
 
 recalculate:
   /*
    * We need to wait for the delete to occur, so mark it as necessary and let
-   * gbp_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;
-
-  IDE_EXIT;
 }
 
 static void
-gbp_git_buffer_change_monitor__buffer_insert_text_after_cb (GbpGitBufferChangeMonitor *self,
-                                                            GtkTextIter               *location,
-                                                            gchar                     *text,
-                                                            gint                       len,
-                                                            IdeBuffer                 *buffer)
+buffer_insert_text_after_cb (GbpGitBufferChangeMonitor *self,
+                             GtkTextIter               *location,
+                             gchar                     *text,
+                             gint                       len,
+                             IdeBuffer                 *buffer)
 {
-  IdeBufferLineChange change;
-
-  IDE_ENTRY;
+  guint line;
 
   g_assert (GBP_IS_GIT_BUFFER_CHANGE_MONITOR (self));
-  g_assert (location);
-  g_assert (text);
+  g_assert (location != NULL);
+  g_assert (text != NULL);
   g_assert (IDE_IS_BUFFER (buffer));
 
   /*
@@ -528,426 +215,241 @@ gbp_git_buffer_change_monitor__buffer_insert_text_after_cb (GbpGitBufferChangeMo
    * 1) A newline is included in the text.
    * 2) The line currently has flags of NONE.
    *
-   * 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 gbp_git_buffer_change_monitor__buffer_changed_cb().
+   * 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
    */
 
-  if (NULL != memmem (text, len, "\n", 1))
-    IDE_GOTO (recalculate);
-
-  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);
+  line = gtk_text_iter_get_line (location);
 
-  IDE_EXIT;
-
-recalculate:
-  gbp_git_buffer_change_monitor_recalculate (self);
-
-  IDE_EXIT;
+  if (self->cache == NULL ||
+      NULL != memmem (text, len, "\n", 1) ||
+      !line_cache_get_mark (self->cache, line))
+    gbp_git_buffer_change_monitor_queue_update (self, FAST);
 }
 
-static gboolean
-gbp_git_buffer_change_monitor__changed_timeout_cb (gpointer user_data)
+static void
+buffer_changed_after_cb (GbpGitBufferChangeMonitor *self,
+                         IdeBuffer                 *buffer)
 {
-  GbpGitBufferChangeMonitor *self = user_data;
-
-  g_assert (GBP_IS_GIT_BUFFER_CHANGE_MONITOR (self));
-
-  self->changed_timeout = 0;
-  gbp_git_buffer_change_monitor_recalculate (self);
+  g_assert (IDE_IS_BUFFER_CHANGE_MONITOR (self));
+  g_assert (IDE_IS_BUFFER (buffer));
 
-  return G_SOURCE_REMOVE;
+  gbp_git_buffer_change_monitor_queue_update (self, SLOW);
 }
 
 static void
-gbp_git_buffer_change_monitor__buffer_changed_after_cb (GbpGitBufferChangeMonitor *self,
-                                                        IdeBuffer                 *buffer)
+foreach_cb (gpointer data,
+            gpointer user_data)
 {
-  g_assert (IDE_IS_BUFFER_CHANGE_MONITOR (self));
-  g_assert (IDE_IS_BUFFER (buffer));
+  const LineEntry *entry = data;
+  struct {
+    IdeBufferChangeMonitorForeachFunc func;
+    gpointer user_data;
+  } *state = user_data;
+  IdeBufferLineChange change = 0;
 
-  self->state_dirty = TRUE;
+  if (entry->mark & LINE_MARK_ADDED)
+    change |= IDE_BUFFER_LINE_CHANGE_ADDED;
 
-  if (self->in_calculation)
-    return;
+  if (entry->mark & LINE_MARK_REMOVED)
+    change |= IDE_BUFFER_LINE_CHANGE_DELETED;
+
+  if (entry->mark & LINE_MARK_PREVIOUS_REMOVED)
+    change |= IDE_BUFFER_LINE_CHANGE_PREVIOUS_DELETED;
+
+  if (entry->mark & LINE_MARK_CHANGED)
+    change |= IDE_BUFFER_LINE_CHANGE_CHANGED;
 
-  dzl_clear_source (&self->changed_timeout);
-  self->changed_timeout = g_timeout_add_seconds (DELAY_CHANGED_SEC,
-                                                 gbp_git_buffer_change_monitor__changed_timeout_cb,
-                                                 self);
+  state->func (entry->line, change, state->user_data);
 }
 
 static void
-gbp_git_buffer_change_monitor_reload (IdeBufferChangeMonitor *monitor)
+gbp_git_buffer_change_monitor_foreach_change (IdeBufferChangeMonitor            *monitor,
+                                              guint                              begin_line,
+                                              guint                              end_line,
+                                              IdeBufferChangeMonitorForeachFunc  callback,
+                                              gpointer                           user_data)
 {
   GbpGitBufferChangeMonitor *self = (GbpGitBufferChangeMonitor *)monitor;
-
-  IDE_ENTRY;
+  struct {
+    IdeBufferChangeMonitorForeachFunc func;
+    gpointer user_data;
+  } state = { callback, user_data };
 
   g_assert (GBP_IS_GIT_BUFFER_CHANGE_MONITOR (self));
+  g_assert (callback != NULL);
 
-  g_clear_object (&self->cached_blob);
-  gbp_git_buffer_change_monitor_recalculate (self);
+  if (end_line == G_MAXUINT)
+    end_line--;
 
-  IDE_EXIT;
+  if (self->cache == NULL)
+    {
+      for (guint i = begin_line; i < end_line; i++)
+        callback (i, IDE_BUFFER_LINE_CHANGE_ADDED, user_data);
+      return;
+    }
+
+  line_cache_foreach_in_range (self->cache, begin_line, end_line, foreach_cb, &state);
 }
 
-static void
-gbp_git_buffer_change_monitor_load (IdeBufferChangeMonitor *monitor,
-                                    IdeBuffer              *buffer)
+static IdeBufferLineChange
+gbp_git_buffer_change_monitor_get_change (IdeBufferChangeMonitor *monitor,
+                                          guint                   line)
 {
   GbpGitBufferChangeMonitor *self = (GbpGitBufferChangeMonitor *)monitor;
+  guint mark;
 
-  IDE_ENTRY;
+  g_assert (GBP_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));
+  if (self->cache == NULL)
+    return IDE_BUFFER_LINE_CHANGE_ADDED;
 
-  dzl_signal_group_set_target (self->signal_group, buffer);
+  mark = line_cache_get_mark (self->cache, line + 1);
 
-  IDE_EXIT;
+  if (mark & LINE_MARK_ADDED)
+    return IDE_BUFFER_LINE_CHANGE_ADDED;
+  else if (mark & LINE_MARK_REMOVED)
+    return IDE_BUFFER_LINE_CHANGE_DELETED;
+  else if (mark & LINE_MARK_CHANGED)
+    return IDE_BUFFER_LINE_CHANGE_CHANGED;
+  else
+    return IDE_BUFFER_LINE_CHANGE_NONE;
 }
 
-static gint
-diff_hunk_cb (GgitDiffDelta *delta,
-              GgitDiffHunk  *hunk,
-              gpointer       user_data)
+static void
+gbp_git_buffer_change_monitor_class_init (GbpGitBufferChangeMonitorClass *klass)
 {
-  GArray *ranges = user_data;
-  Range range;
+  IdeObjectClass *i_object_class = IDE_OBJECT_CLASS (klass);
+  IdeBufferChangeMonitorClass *monitor_class = IDE_BUFFER_CHANGE_MONITOR_CLASS (klass);
 
-  g_assert (delta != NULL);
-  g_assert (hunk != NULL);
-  g_assert (ranges != NULL);
+  monitor_class->load = gbp_git_buffer_change_monitor_load;
+  monitor_class->reload = gbp_git_buffer_change_monitor_reload;
+  monitor_class->get_change = gbp_git_buffer_change_monitor_get_change;
+  monitor_class->foreach_change = gbp_git_buffer_change_monitor_foreach_change;
 
-  range.old_start = ggit_diff_hunk_get_old_start (hunk);
-  range.old_lines = ggit_diff_hunk_get_old_lines (hunk);
-  range.new_start = ggit_diff_hunk_get_new_start (hunk);
-  range.new_lines = ggit_diff_hunk_get_new_lines (hunk);
+  i_object_class->destroy = gbp_git_buffer_change_monitor_destroy;
+}
 
-  g_array_append_val (ranges, range);
+static void
+gbp_git_buffer_change_monitor_init (GbpGitBufferChangeMonitor *self)
+{
+  self->buffer_signals = dzl_signal_group_new (IDE_TYPE_BUFFER);
 
-  return 0;
+  dzl_signal_group_connect_object (self->buffer_signals,
+                                   "insert-text",
+                                   G_CALLBACK (buffer_insert_text_after_cb),
+                                   self,
+                                   G_CONNECT_SWAPPED | G_CONNECT_AFTER);
+  dzl_signal_group_connect_object (self->buffer_signals,
+                                   "delete-range",
+                                   G_CALLBACK (buffer_delete_range_cb),
+                                   self,
+                                   G_CONNECT_SWAPPED);
+  dzl_signal_group_connect_object (self->buffer_signals,
+                                   "delete-range",
+                                   G_CALLBACK (buffer_delete_range_after_cb),
+                                   self,
+                                   G_CONNECT_SWAPPED | G_CONNECT_AFTER);
+  dzl_signal_group_connect_object (self->buffer_signals,
+                                   "changed",
+                                   G_CALLBACK (buffer_changed_after_cb),
+                                   self,
+                                   G_CONNECT_SWAPPED | G_CONNECT_AFTER);
 }
 
-static gboolean
-gbp_git_buffer_change_monitor_calculate_threaded (GbpGitBufferChangeMonitor  *self,
-                                                  DiffTask                   *diff,
-                                                  GError                    **error)
+IdeBufferChangeMonitor *
+gbp_git_buffer_change_monitor_new (IdeBuffer         *buffer,
+                                   IpcGitRepository  *repository,
+                                   GFile             *file,
+                                   GCancellable      *cancellable,
+                                   GError           **error)
 {
-  g_autofree gchar *relative_path = NULL;
-  g_autoptr(GgitDiffOptions) options = NULL;
+  GbpGitBufferChangeMonitor *ret;
+  g_autoptr(IpcGitChangeMonitor) proxy = NULL;
+  g_autoptr(IdeContext) context = NULL;
   g_autoptr(GFile) workdir = NULL;
-  g_autoptr(GArray) ranges = NULL;
-  LineCache *cache;
-  const guint8 *data;
-  gsize data_len = 0;
+  g_autofree gchar *relative_path = NULL;
+  g_autofree gchar *obj_path = NULL;
+  GDBusConnection *connection;
 
-  g_assert (GBP_IS_GIT_BUFFER_CHANGE_MONITOR (self));
-  g_assert (diff != NULL);
-  g_assert (G_IS_FILE (diff->file));
-  g_assert (diff->cache != NULL);
-  g_assert (GGIT_IS_REPOSITORY (diff->repository));
-  g_assert (diff->content != NULL);
-  g_assert (!diff->blob || GGIT_IS_BLOB (diff->blob));
-  g_assert (error != NULL);
-  g_assert (*error == NULL);
-
-  workdir = ggit_repository_get_workdir (diff->repository);
-
-  if (!workdir)
-    {
-      g_set_error (error,
-                   G_IO_ERROR,
-                   G_IO_ERROR_INVALID_FILENAME,
-                   _("Repository does not have a working directory."));
-      return FALSE;
-    }
+  g_return_val_if_fail (IDE_IS_MAIN_THREAD (), NULL);
+  g_return_val_if_fail (IDE_IS_BUFFER (buffer), NULL);
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+  g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), NULL);
 
-  relative_path = g_file_get_relative_path (workdir, diff->file);
+  context = ide_buffer_ref_context (buffer);
+  workdir = ide_context_ref_workdir (context);
 
-  if (!relative_path)
+  if (!g_file_has_prefix (file, workdir))
     {
       g_set_error (error,
                    G_IO_ERROR,
-                   G_IO_ERROR_INVALID_FILENAME,
-                   _("File is not under control of git working directory."));
-      return FALSE;
-    }
-
-  diff->is_child_of_workdir = TRUE;
-
-  /*
-   * Find the blob if necessary. This will be cached by the main thread for
-   * us on the way out of the async operation.
-   */
-  if (diff->blob == NULL)
-    {
-      GgitOId *entry_oid = NULL;
-      GgitOId *oid = NULL;
-      GgitObject *blob = NULL;
-      GgitObject *commit = NULL;
-      GgitRef *head = NULL;
-      GgitTree *tree = NULL;
-      GgitTreeEntry *entry = NULL;
-
-      head = ggit_repository_get_head (diff->repository, error);
-      if (!head)
-        goto cleanup;
-
-      oid = ggit_ref_get_target (head);
-      if (!oid)
-        goto cleanup;
-
-      commit = ggit_repository_lookup (diff->repository, oid, GGIT_TYPE_COMMIT, error);
-      if (!commit)
-        goto cleanup;
-
-      tree = ggit_commit_get_tree (GGIT_COMMIT (commit));
-      if (!tree)
-        goto cleanup;
-
-      entry = ggit_tree_get_by_path (tree, relative_path, error);
-      if (!entry)
-        goto cleanup;
-
-      entry_oid = ggit_tree_entry_get_id (entry);
-      if (!entry_oid)
-        goto cleanup;
-
-      blob = ggit_repository_lookup (diff->repository, entry_oid, GGIT_TYPE_BLOB, error);
-      if (!blob)
-        goto cleanup;
-
-      diff->blob = g_object_ref (GGIT_BLOB (blob));
-
-    cleanup:
-      g_clear_object (&blob);
-      g_clear_pointer (&entry_oid, ggit_oid_free);
-      g_clear_pointer (&entry, ggit_tree_entry_unref);
-      g_clear_object (&tree);
-      g_clear_object (&commit);
-      g_clear_pointer (&oid, ggit_oid_free);
-      g_clear_object (&head);
-    }
-
-  if (diff->blob == NULL)
-    {
-      if (*error == NULL)
-        g_set_error (error,
-                     G_IO_ERROR,
-                     G_IO_ERROR_NOT_FOUND,
-                     _("The requested file does not exist within the git index."));
-      return FALSE;
+                   G_IO_ERROR_NOT_SUPPORTED,
+                   "Cannot monitor files outside the working directory");
+      return NULL;
     }
 
-  data = g_bytes_get_data (diff->content, &data_len);
+  relative_path = g_file_get_relative_path (workdir, file);
 
-  ranges = g_array_new (FALSE, FALSE, sizeof (Range));
-  options = ggit_diff_options_new ();
-
-  ggit_diff_options_set_n_context_lines (options, 0);
-
-  ggit_diff_blob_to_buffer (diff->blob,
-                            relative_path,
-                            data,
-                            data_len,
-                            relative_path,
-                            options,
-                            NULL,         /* File Callback */
-                            NULL,         /* Binary Callback */
-                            diff_hunk_cb, /* Hunk Callback */
-                            NULL,
-                            ranges,
-                            error);
-
-  cache = diff->cache;
-
-  for (guint i = 0; i < ranges->len; i++)
-    {
-      const Range *range = &g_array_index (ranges, Range, i);
-      gint start_line = range->new_start - 1;
-      gint end_line = range->new_start + range->new_lines - 1;
-
-      if (range->old_lines == 0 && range->new_lines > 0)
-        {
-          line_cache_mark_range (cache, start_line, end_line, LINE_MARK_ADDED);
-        }
-      else if (range->new_lines == 0 && range->old_lines > 0)
-        {
-          if (start_line < 0)
-            line_cache_mark_range (cache, 0, 0, LINE_MARK_PREVIOUS_REMOVED);
-          else
-            line_cache_mark_range (cache, start_line + 1, start_line + 1, LINE_MARK_REMOVED);
-        }
-      else
-        {
-          line_cache_mark_range (cache, start_line, end_line, LINE_MARK_CHANGED);
-        }
-    }
-
-  return *error == NULL;
-}
-
-static gpointer
-gbp_git_buffer_change_monitor_worker (gpointer data)
-{
-  GAsyncQueue *queue = data;
-  gpointer taskptr;
+  if (!ipc_git_repository_call_create_change_monitor_sync (repository,
+                                                           relative_path,
+                                                           &obj_path,
+                                                           cancellable,
+                                                           error))
+    return NULL;
 
-  g_assert (queue != NULL);
+  connection = g_dbus_proxy_get_connection (G_DBUS_PROXY (repository));
 
-  /*
-   * This is a single thread worker that dispatches the particular
-   * change to the given change monitor. We require a single thread
-   * so that we can mantain the invariant that only a single thread
-   * can access a GgitRepository at a time (and change monitors all
-   * share the same GgitRepository amongst themselves).
-   */
+  if (!(proxy = ipc_git_change_monitor_proxy_new_sync (connection,
+                                                       G_DBUS_PROXY_FLAGS_NONE,
+                                                       NULL,
+                                                       obj_path,
+                                                       cancellable,
+                                                       error)))
+    return NULL;
 
-  while ((taskptr = g_async_queue_pop (queue)))
-    {
-      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 (GBP_IS_GIT_BUFFER_CHANGE_MONITOR (self));
-
-      diff = ide_task_get_task_data (task);
-      g_assert (diff != NULL);
-
-      /* 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,
-                                 g_steal_pointer (&diff->cache),
-                                 line_cache_free);
-    }
+  ret = g_object_new (GBP_TYPE_GIT_BUFFER_CHANGE_MONITOR,
+                      "buffer", buffer,
+                      NULL);
+  ret->proxy = g_steal_pointer (&proxy);
 
-  return NULL;
+  return IDE_BUFFER_CHANGE_MONITOR (g_steal_pointer (&ret));
 }
 
 static void
-gbp_git_buffer_change_monitor_destroy (IdeObject *object)
+gbp_git_buffer_change_monitor_wait_cb (GObject      *object,
+                                       GAsyncResult *result,
+                                       gpointer      user_data)
 {
-  GbpGitBufferChangeMonitor *self = (GbpGitBufferChangeMonitor *)object;
+  IpcGitChangeMonitor *proxy = (IpcGitChangeMonitor *)object;
+  g_autoptr(GVariant) changes = NULL;
+  g_autoptr(IdeTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+  GbpGitBufferChangeMonitor *self;
 
-  dzl_clear_source (&self->changed_timeout);
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (IPC_IS_GIT_CHANGE_MONITOR (proxy));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (IDE_IS_TASK (task));
 
-  if (self->signal_group)
+  if (!ipc_git_change_monitor_call_list_changes_finish (proxy, &changes, result, &error))
     {
-      dzl_signal_group_set_target (self->signal_group, NULL);
-      g_clear_object (&self->signal_group);
+      ide_task_return_error (task, g_steal_pointer (&error));
+      return;
     }
 
-  g_clear_object (&self->cached_blob);
-  g_clear_object (&self->repository);
-  g_clear_pointer (&self->cache, line_cache_free);
+  self = ide_task_get_source_object (task);
 
-  IDE_OBJECT_CLASS (gbp_git_buffer_change_monitor_parent_class)->destroy (object);
-}
-
-static void
-gbp_git_buffer_change_monitor_finalize (GObject *object)
-{
-  G_OBJECT_CLASS (gbp_git_buffer_change_monitor_parent_class)->finalize (object);
-
-  DZL_COUNTER_DEC (instances);
-}
-
-static void
-gbp_git_buffer_change_monitor_set_property (GObject      *object,
-                                            guint         prop_id,
-                                            const GValue *value,
-                                            GParamSpec   *pspec)
-{
-  GbpGitBufferChangeMonitor *self = GBP_GIT_BUFFER_CHANGE_MONITOR (object);
-
-  switch (prop_id)
+  if (changes != NULL)
     {
-    case PROP_REPOSITORY:
-      gbp_git_buffer_change_monitor_set_repository (self, g_value_get_object (value));
-      break;
-
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      g_clear_pointer (&self->cache, line_cache_free);
+      self->cache = line_cache_new_from_variant (changes);
     }
-}
-
-static void
-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->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->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",
-                         "Repository",
-                         "The repository to use for calculating diffs.",
-                         GGIT_TYPE_REPOSITORY,
-                         (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
-
-  g_object_class_install_properties (object_class, LAST_PROP, properties);
-
-  /* Note: We use a single worker thread so that we can maintain the
-   *       invariant that only a single thread is touching the GgitRepository
-   *       at a time. (Also, you can only type in one editor at a time, so
-   *       on worker thread for interactive blob changes is fine.
-   */
-  work_queue = g_async_queue_new ();
-  work_thread = g_thread_new ("GbpGitBufferChangeMonitorWorker",
-                              gbp_git_buffer_change_monitor_worker,
-                              work_queue);
-}
-
-static void
-gbp_git_buffer_change_monitor_init (GbpGitBufferChangeMonitor *self)
-{
-  DZL_COUNTER_INC (instances);
+  ide_buffer_change_monitor_emit_changed (IDE_BUFFER_CHANGE_MONITOR (self));
 
-  self->signal_group = dzl_signal_group_new (IDE_TYPE_BUFFER);
-  dzl_signal_group_connect_object (self->signal_group,
-                                   "insert-text",
-                                   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 (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 (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 (gbp_git_buffer_change_monitor__buffer_changed_after_cb),
-                                   self,
-                                   G_CONNECT_SWAPPED | G_CONNECT_AFTER);
+  ide_task_return_boolean (task, TRUE);
 }
 
 void
@@ -957,6 +459,8 @@ gbp_git_buffer_change_monitor_wait_async (GbpGitBufferChangeMonitor *self,
                                           gpointer                   user_data)
 {
   g_autoptr(IdeTask) task = NULL;
+  IdeBuffer *buffer;
+  guint change_count;
 
   g_return_if_fail (IDE_IS_MAIN_THREAD ());
   g_return_if_fail (GBP_IS_GIT_BUFFER_CHANGE_MONITOR (self));
@@ -965,16 +469,29 @@ gbp_git_buffer_change_monitor_wait_async (GbpGitBufferChangeMonitor *self,
   task = ide_task_new (self, cancellable, callback, user_data);
   ide_task_set_source_tag (task, gbp_git_buffer_change_monitor_wait_async);
 
-  if (!self->state_dirty && !self->in_calculation)
+  if (ide_task_return_error_if_cancelled (task))
+    return;
+
+  buffer = ide_buffer_change_monitor_get_buffer (IDE_BUFFER_CHANGE_MONITOR (self));
+  change_count = ide_buffer_get_change_count (buffer);
+
+  /* Update the peer of buffer contents immediately in-case it does
+   * not yet have teh newest version.
+   */
+  if (change_count != self->last_change_count)
     {
-      ide_task_return_boolean (task, TRUE);
-      return;
-    }
+      g_autoptr(GBytes) bytes = ide_buffer_dup_content (buffer);
 
-  g_queue_push_tail (&self->wait_tasks, g_steal_pointer (&task));
+      self->last_change_count = change_count;
+      ipc_git_change_monitor_call_update_content (self->proxy,
+                                                  (const gchar *)g_bytes_get_data (bytes, NULL),
+                                                  NULL, NULL, NULL);
+    }
 
-  if (!self->in_calculation)
-    gbp_git_buffer_change_monitor_recalculate (self);
+  ipc_git_change_monitor_call_list_changes (self->proxy,
+                                            cancellable,
+                                            gbp_git_buffer_change_monitor_wait_cb,
+                                            g_steal_pointer (&task));
 }
 
 gboolean
@@ -984,7 +501,6 @@ gbp_git_buffer_change_monitor_wait_finish (GbpGitBufferChangeMonitor  *self,
 {
   g_return_val_if_fail (IDE_IS_MAIN_THREAD (), FALSE);
   g_return_val_if_fail (GBP_IS_GIT_BUFFER_CHANGE_MONITOR (self), FALSE);
-  g_return_val_if_fail (IDE_IS_TASK (result), FALSE);
 
   return ide_task_propagate_boolean (IDE_TASK (result), error);
 }
diff --git a/src/plugins/git/gbp-git-buffer-change-monitor.h b/src/plugins/git/gbp-git-buffer-change-monitor.h
index c5e620d7d..2d0e6b921 100644
--- a/src/plugins/git/gbp-git-buffer-change-monitor.h
+++ b/src/plugins/git/gbp-git-buffer-change-monitor.h
@@ -20,23 +20,27 @@
 
 #pragma once
 
-#include <libgit2-glib/ggit.h>
 #include <libide-code.h>
 
+#include "daemon/ipc-git-repository.h"
+
 G_BEGIN_DECLS
 
 #define GBP_TYPE_GIT_BUFFER_CHANGE_MONITOR (gbp_git_buffer_change_monitor_get_type())
 
 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);
-void     gbp_git_buffer_change_monitor_wait_async     (GbpGitBufferChangeMonitor  *self,
-                                                       GCancellable               *cancellable,
-                                                       GAsyncReadyCallback         callback,
-                                                       gpointer                    user_data);
-gboolean gbp_git_buffer_change_monitor_wait_finish    (GbpGitBufferChangeMonitor  *self,
-                                                       GAsyncResult               *result,
-                                                       GError                    **error);
+IdeBufferChangeMonitor *gbp_git_buffer_change_monitor_new         (IdeBuffer                  *buffer,
+                                                                   IpcGitRepository           *repository,
+                                                                   GFile                      *file,
+                                                                   GCancellable               *cancellable,
+                                                                   GError                    **error);
+void                    gbp_git_buffer_change_monitor_wait_async  (GbpGitBufferChangeMonitor  *self,
+                                                                   GCancellable               *cancellable,
+                                                                   GAsyncReadyCallback         callback,
+                                                                   gpointer                    user_data);
+gboolean                gbp_git_buffer_change_monitor_wait_finish (GbpGitBufferChangeMonitor  *self,
+                                                                   GAsyncResult               *result,
+                                                                   GError                    **error);
 
 G_END_DECLS
diff --git a/src/plugins/git/gbp-git-dependency-updater.c b/src/plugins/git/gbp-git-dependency-updater.c
index 6c75bd34d..3d5ac00dc 100644
--- a/src/plugins/git/gbp-git-dependency-updater.c
+++ b/src/plugins/git/gbp-git-dependency-updater.c
@@ -22,44 +22,35 @@
 
 #include "config.h"
 
+#include <glib/gi18n.h>
+
+#include "daemon/ipc-git-repository.h"
+
 #include "gbp-git-dependency-updater.h"
-#include "gbp-git-submodule-stage.h"
+#include "gbp-git-progress.h"
+#include "gbp-git-vcs.h"
 
 struct _GbpGitDependencyUpdater
 {
   IdeObject parent_instance;
 };
 
-static void
-find_submodule_stage_cb (gpointer data,
-                         gpointer user_data)
-{
-  GbpGitSubmoduleStage **stage = user_data;
-
-  g_assert (IDE_IS_PIPELINE_STAGE (data));
-  g_assert (stage != NULL);
-  g_assert (*stage == NULL || IDE_IS_PIPELINE_STAGE (*stage));
-
-  if (GBP_IS_GIT_SUBMODULE_STAGE (data))
-    *stage = GBP_GIT_SUBMODULE_STAGE (data);
-}
-
 static void
 gbp_git_dependency_updater_update_cb (GObject      *object,
                                       GAsyncResult *result,
                                       gpointer      user_data)
 {
-  IdeBuildManager *manager = (IdeBuildManager *)object;
+  IpcGitRepository *repository = (IpcGitRepository *)object;
   g_autoptr(IdeTask) task = user_data;
   g_autoptr(GError) error = NULL;
 
   IDE_ENTRY;
 
-  g_assert (IDE_IS_BUILD_MANAGER (manager));
+  g_assert (IPC_IS_GIT_REPOSITORY (repository));
   g_assert (G_IS_ASYNC_RESULT (result));
   g_assert (IDE_IS_TASK (task));
 
-  if (!ide_build_manager_rebuild_finish (manager, result, &error))
+  if (!ipc_git_repository_call_update_submodules_finish (repository, result, &error))
     ide_task_return_error (task, g_steal_pointer (&error));
   else
     ide_task_return_boolean (task, TRUE);
@@ -73,10 +64,13 @@ gbp_git_dependency_updater_update_async (IdeDependencyUpdater *self,
                                          GAsyncReadyCallback   callback,
                                          gpointer              user_data)
 {
+  g_autoptr(IpcGitProgress) progress = NULL;
   g_autoptr(IdeTask) task = NULL;
-  GbpGitSubmoduleStage *stage = NULL;
-  IdePipeline *pipeline;
-  IdeBuildManager *manager;
+  g_autoptr(IdeVcs) vcs = NULL;
+  g_autoptr(IdeNotification) notif = NULL;
+  g_autoptr(GError) error = NULL;
+  IpcGitRepository *repository;
+  GDBusConnection *connection;
   IdeContext *context;
 
   IDE_ENTRY;
@@ -86,50 +80,42 @@ gbp_git_dependency_updater_update_async (IdeDependencyUpdater *self,
 
   task = ide_task_new (self, cancellable, callback, user_data);
   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_build_manager_from_context (context);
-  pipeline = ide_build_manager_get_pipeline (manager);
+  vcs = ide_object_get_child_typed (IDE_OBJECT (context), IDE_TYPE_VCS);
 
-  g_assert (!pipeline || IDE_IS_PIPELINE (pipeline));
-
-  if (pipeline == NULL)
+  if (!GBP_IS_GIT_VCS (vcs))
     {
       ide_task_return_new_error (task,
                                  G_IO_ERROR,
                                  G_IO_ERROR_FAILED,
-                                 "Cannot update git submodules until build pipeline is initialized");
+                                 "Git version control is not in use");
       IDE_EXIT;
     }
 
-  /* Find the submodule stage and tell it to download updates one time */
-  ide_pipeline_foreach_stage (pipeline, find_submodule_stage_cb, &stage);
+  repository = gbp_git_vcs_get_repository (GBP_GIT_VCS (vcs));
+  connection = g_dbus_proxy_get_connection (G_DBUS_PROXY (repository));
+
+  notif = g_object_new (IDE_TYPE_NOTIFICATION,
+                        "title", _("Updating Git Submodules"),
+                        NULL);
 
-  if (stage == NULL)
+  if (!(progress = gbp_git_progress_new (connection, notif, cancellable, &error)))
     {
-      /* Synthesize success if there is no submodule stage */
-      ide_task_return_boolean (task, TRUE);
+      ide_task_return_error (task, g_steal_pointer (&error));
       IDE_EXIT;
     }
 
-  gbp_git_submodule_stage_force_update (stage);
-
-  /* Ensure downloads and everything past it is invalidated */
-  ide_pipeline_invalidate_phase (pipeline, IDE_PIPELINE_PHASE_DOWNLOADS);
-
-  /* Start building all the way up to the project configure so that
-   * the user knows if the updates broke their configuration or anything.
-   *
-   * TODO: This should probably be done by the calling API so that we don't
-   *       race with other updaters.
-   */
-  ide_build_manager_rebuild_async (manager,
-                                   IDE_PIPELINE_PHASE_CONFIGURE,
-                                   NULL,
-                                   NULL,
-                                   gbp_git_dependency_updater_update_cb,
-                                   g_steal_pointer (&task));
+  ide_task_set_task_data (task, g_object_ref (progress), g_object_unref);
+  gbp_git_progress_set_withdraw (GBP_GIT_PROGRESS (progress), TRUE);
+  ide_notification_attach (notif, IDE_OBJECT (context));
+
+  ipc_git_repository_call_update_submodules (repository,
+                                             TRUE,
+                                             g_dbus_interface_skeleton_get_object_path 
(G_DBUS_INTERFACE_SKELETON (progress)),
+                                             cancellable,
+                                             gbp_git_dependency_updater_update_cb,
+                                             g_steal_pointer (&task));
 
   IDE_EXIT;
 }
diff --git a/src/plugins/git/gbp-git-vcs-cloner.c b/src/plugins/git/gbp-git-vcs-cloner.c
index 0a03b7b4c..91454a4a9 100644
--- a/src/plugins/git/gbp-git-vcs-cloner.c
+++ b/src/plugins/git/gbp-git-vcs-cloner.c
@@ -22,34 +22,34 @@
 
 #include "config.h"
 
-#include <dazzle.h>
 #include <glib/gi18n.h>
 #include <libide-threading.h>
 
-#include "gbp-git-remote-callbacks.h"
+#include "daemon/ipc-git-service.h"
+
+#include "gbp-git-client.h"
+#include "gbp-git-progress.h"
 #include "gbp-git-vcs-cloner.h"
 
 struct _GbpGitVcsCloner
 {
-  GObject parent_instance;
+  IdeObject parent_instance;
 };
 
 typedef struct
 {
+  IpcGitProgress  *progress;
   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))
+G_DEFINE_TYPE_WITH_CODE (GbpGitVcsCloner, gbp_git_vcs_cloner, IDE_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (IDE_TYPE_VCS_CLONER, vcs_cloner_iface_init))
 
 static void
 clone_request_free (gpointer data)
@@ -63,6 +63,7 @@ clone_request_free (gpointer data)
       g_clear_object (&req->notif);
       g_clear_object (&req->location);
       g_clear_object (&req->project_file);
+      g_clear_object (&req->progress);
       g_slice_free (CloneRequest, req);
     }
 }
@@ -152,81 +153,30 @@ gbp_git_vcs_cloner_validate_uri (IdeVcsCloner  *cloner,
 }
 
 static void
-gbp_git_vcs_cloner_worker (IdeTask      *task,
-                           gpointer      source_object,
-                           gpointer      task_data,
-                           GCancellable *cancellable)
+gbp_git_vcs_cloner_clone_cb (GObject      *object,
+                             GAsyncResult *result,
+                             gpointer      user_data)
 {
-  g_autoptr(GgitConfig) config = NULL;
-  g_autoptr(GFile) config_file = NULL;
+  IpcGitService *service = (IpcGitService *)object;
+  g_autoptr(IdeTask) task = user_data;
   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_autofree gchar *git_location = NULL;
 
+  g_assert (IPC_IS_GIT_SERVICE (service));
+  g_assert (G_IS_ASYNC_RESULT (result));
   g_assert (IDE_IS_TASK (task));
-  g_assert (GBP_IS_GIT_VCS_CLONER (source_object));
-  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);
+  if (!ipc_git_service_call_clone_finish (service, &git_location, result, &error))
+    ide_task_return_error (task, g_steal_pointer (&error));
+  else
+    ide_task_return_boolean (task, TRUE);
 }
 
 static void
 gbp_git_vcs_cloner_clone_async (IdeVcsCloner        *cloner,
                                 const gchar         *uri,
                                 const gchar         *destination,
-                                GVariantDict        *options,
+                                GVariant            *options,
                                 IdeNotification     *notif,
                                 GCancellable        *cancellable,
                                 GAsyncReadyCallback  callback,
@@ -235,11 +185,17 @@ gbp_git_vcs_cloner_clone_async (IdeVcsCloner        *cloner,
   GbpGitVcsCloner *self = (GbpGitVcsCloner *)cloner;
   g_autoptr(IdeNotification) notif_local = NULL;
   g_autoptr(IdeVcsUri) vcs_uri = NULL;
+  g_autoptr(IpcGitService) service = NULL;
   g_autoptr(IdeTask) task = NULL;
   g_autoptr(GFile) location = NULL;
+  g_autoptr(GError) error = NULL;
   g_autofree gchar *uristr = NULL;
+  GDBusConnection *connection;
+  GbpGitClient *client = NULL;
   CloneRequest *req;
+  GVariantDict dict;
   const gchar *branch;
+  IdeContext *context;
 
   g_assert (GBP_IS_GIT_VCS_CLONER (cloner));
   g_assert (uri != NULL);
@@ -247,34 +203,32 @@ gbp_git_vcs_cloner_clone_async (IdeVcsCloner        *cloner,
   g_assert (!notif || IDE_IS_NOTIFICATION (notif));
   g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
 
+  /* Get our client to communicate with the daemon */
+  context = ide_object_get_context (IDE_OBJECT (self));
+  client = gbp_git_client_from_context (context);
+
   task = ide_task_new (self, cancellable, callback, user_data);
   ide_task_set_source_tag (task, gbp_git_vcs_cloner_clone_async);
 
+  /* Ensure we always have a notification to work with */
   if (notif == NULL)
     {
       notif_local = ide_notification_new ();
       notif = notif_local;
     }
 
-  if (!g_variant_dict_lookup (options, "branch", "&s", &branch))
-    branch = "master";
+  ide_notification_set_title (notif, _("Cloning repository"));
 
-  /*
-   * 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);
+  /* Extract branch, leave other options to pass-through */
+  g_variant_dict_init (&dict, options);
+  if (!g_variant_dict_lookup (&dict, "branch", "&s", &branch))
+    branch = "master";
+  g_variant_dict_remove (&dict, "branch");
 
+  /* Make sure we have a real URI to connect to */
   uristr = g_strstrip (g_strdup (uri));
   location = g_file_new_for_path (destination);
-
-  vcs_uri = ide_vcs_uri_new (uristr);
-
-  if (vcs_uri == NULL)
+  if (!(vcs_uri = ide_vcs_uri_new (uristr)))
     {
       ide_task_return_new_error (task,
                                  G_IO_ERROR,
@@ -283,21 +237,39 @@ gbp_git_vcs_cloner_clone_async (IdeVcsCloner        *cloner,
       return;
     }
 
+  /* Always set a username if the transport is SSH */
   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 ());
+        {
+          ide_vcs_uri_set_user (vcs_uri, g_get_user_name ());
+          g_free (uristr);
+          uristr = ide_vcs_uri_to_string (vcs_uri);
+        }
     }
 
-  g_assert (IDE_IS_NOTIFICATION (notif));
-
+  /* Create state for the task */
   req = clone_request_new (vcs_uri, branch, location, notif);
+  ide_task_set_task_data (task, req, clone_request_free);
 
-  g_variant_dict_lookup (options, "author-name", "s", &req->author_name);
-  g_variant_dict_lookup (options, "author-email", "s", &req->author_email);
+  if (!(service = gbp_git_client_get_service (client, cancellable, &error)) ||
+      !(connection = g_dbus_proxy_get_connection (G_DBUS_PROXY (service))) ||
+      !(req->progress = gbp_git_progress_new (connection, notif, cancellable, &error)))
+    {
+      ide_task_return_error (task, g_steal_pointer (&error));
+      g_variant_dict_clear (&dict);
+      return;
+    }
 
-  ide_task_set_task_data (task, req, clone_request_free);
-  ide_task_run_in_thread (task, gbp_git_vcs_cloner_worker);
+  ipc_git_service_call_clone (service,
+                              uristr,
+                              g_file_peek_path (req->location),
+                              req->branch ?: "master",
+                              g_variant_dict_end (&dict),
+                              g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON 
(req->progress)),
+                              cancellable,
+                              gbp_git_vcs_cloner_clone_cb,
+                              g_steal_pointer (&task));
 }
 
 static gboolean
diff --git a/src/plugins/git/gbp-git-vcs-cloner.h b/src/plugins/git/gbp-git-vcs-cloner.h
index d634242e9..2acc2dc49 100644
--- a/src/plugins/git/gbp-git-vcs-cloner.h
+++ b/src/plugins/git/gbp-git-vcs-cloner.h
@@ -26,6 +26,6 @@ G_BEGIN_DECLS
 
 #define GBP_TYPE_GIT_VCS_CLONER (gbp_git_vcs_cloner_get_type())
 
-G_DECLARE_FINAL_TYPE (GbpGitVcsCloner, gbp_git_vcs_cloner, GBP, GIT_VCS_CLONER, GObject)
+G_DECLARE_FINAL_TYPE (GbpGitVcsCloner, gbp_git_vcs_cloner, GBP, GIT_VCS_CLONER, IdeObject)
 
 G_END_DECLS
diff --git a/src/plugins/git/gbp-git-vcs-config.c b/src/plugins/git/gbp-git-vcs-config.c
index cf8a20153..9d67d2999 100644
--- a/src/plugins/git/gbp-git-vcs-config.c
+++ b/src/plugins/git/gbp-git-vcs-config.c
@@ -1,6 +1,7 @@
 /* gbp-git-vcs-config.c
  *
  * Copyright 2016 Akshaya Kakkilaya <akshaya kakkilaya gmail com>
+ * Copyright 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
@@ -22,147 +23,128 @@
 
 #include "config.h"
 
-#include <libgit2-glib/ggit.h>
+#include <libide-threading.h>
 #include <libide-vcs.h>
 
+#include "daemon/ipc-git-config.h"
+
+#include "gbp-git-client.h"
 #include "gbp-git-vcs-config.h"
 
 struct _GbpGitVcsConfig
 {
-  GObject     parent_instance;
-
-  GgitConfig *config;
+  IdeObject parent_instance;
+  guint is_global : 1;
 };
 
 static void vcs_config_init (IdeVcsConfigInterface *iface);
 
-G_DEFINE_TYPE_EXTENDED (GbpGitVcsConfig, gbp_git_vcs_config, G_TYPE_OBJECT, 0,
-                        G_IMPLEMENT_INTERFACE (IDE_TYPE_VCS_CONFIG, vcs_config_init))
-
-GbpGitVcsConfig *
-gbp_git_vcs_config_new (void)
-{
-  return g_object_new (GBP_TYPE_GIT_VCS_CONFIG, NULL);
-}
-
-static void
-gbp_git_vcs_config_get_string (GgitConfig  *config,
-                               const gchar *key,
-                               GValue      *value,
-                               GError     **error)
-{
-  const gchar *str;
-
-  g_assert (GGIT_IS_CONFIG (config));
-  g_assert (key != NULL);
+G_DEFINE_TYPE_WITH_CODE (GbpGitVcsConfig, gbp_git_vcs_config, IDE_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (IDE_TYPE_VCS_CONFIG, vcs_config_init))
 
-  str = ggit_config_get_string (config, key, error);
-
-  g_value_set_string (value, str);
-}
-
-static void
-gbp_git_vcs_config_set_string (GgitConfig   *config,
-                               const gchar  *key,
-                               const GValue *value,
-                               GError      **error)
+static const gchar *
+get_key (IdeVcsConfigType type)
 {
-  const gchar *str;
-
-  g_assert (GGIT_IS_CONFIG (config));
-  g_assert (key != NULL);
-
-  str = g_value_get_string (value);
-
-  if (str != NULL)
-    ggit_config_set_string (config, key, str, error);
-}
-
-static void
-gbp_git_vcs_config_get_config (IdeVcsConfig    *self,
-                               IdeVcsConfigType type,
-                               GValue          *value)
-{
-  g_autoptr(GgitConfig) config = NULL;
-  GgitConfig *orig_config;
-
-  g_return_if_fail (GBP_IS_GIT_VCS_CONFIG (self));
-
-  orig_config = GBP_GIT_VCS_CONFIG (self)->config;
-  config = ggit_config_snapshot (orig_config, NULL);
-
-  if(config == NULL)
-    return;
-
   switch (type)
     {
     case IDE_VCS_CONFIG_FULL_NAME:
-      gbp_git_vcs_config_get_string (config, "user.name", value, NULL);
-      break;
-
+      return "user.name";
     case IDE_VCS_CONFIG_EMAIL:
-      gbp_git_vcs_config_get_string (config, "user.email", value, NULL);
-      break;
-
+      return "user.email";
     default:
-      break;
+      return NULL;
     }
 }
 
-static void
-gbp_git_vcs_config_set_config (IdeVcsConfig    *self,
-                               IdeVcsConfigType type,
-                               const GValue    *value)
+static IpcGitConfig *
+get_config (GbpGitVcsConfig  *self,
+            GCancellable     *cancellable,
+            GError          **error)
 {
-  GgitConfig *config;
+  g_autofree gchar *obj_path = NULL;
+  g_autoptr(IpcGitService) service = NULL;
+  GDBusConnection *connection;
+  GbpGitClient *client;
+  IdeContext *context;
 
-  g_return_if_fail (GBP_IS_GIT_VCS_CONFIG (self));
+  g_assert (GBP_IS_GIT_VCS_CONFIG (self));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
 
-  config = GBP_GIT_VCS_CONFIG (self)->config;
+  context = ide_object_get_context (IDE_OBJECT (self));
+  client = gbp_git_client_from_context (context);
 
-  switch (type)
+  if (!self->is_global)
     {
-    case IDE_VCS_CONFIG_FULL_NAME:
-      gbp_git_vcs_config_set_string (config, "user.name", value, NULL);
-      break;
+      /* TODO: get config from repository */
+    }
 
-    case IDE_VCS_CONFIG_EMAIL:
-      gbp_git_vcs_config_set_string (config, "user.email", value, NULL);
-      break;
+  if (!(service = gbp_git_client_get_service (client, cancellable, error)))
+    return NULL;
+
+  if (!ipc_git_service_call_load_config_sync (service, &obj_path, cancellable, error))
+    return NULL;
+
+  connection = g_dbus_proxy_get_connection (G_DBUS_PROXY (service));
+
+  return ipc_git_config_proxy_new_sync (connection,
+                                        G_DBUS_PROXY_FLAGS_NONE,
+                                        NULL,
+                                        obj_path,
+                                        cancellable,
+                                        error);
 
-    default:
-      break;
-    }
 }
 
 static void
-gbp_git_vcs_config_constructed (GObject *object)
+gbp_git_vcs_config_get_config (IdeVcsConfig    *config,
+                               IdeVcsConfigType type,
+                               GValue          *value)
 {
-  GbpGitVcsConfig *self = GBP_GIT_VCS_CONFIG (object);
+  GbpGitVcsConfig *self = (GbpGitVcsConfig *)config;
+  g_autoptr(IpcGitConfig) proxy = NULL;
 
-  g_autoptr(GFile) global_file = NULL;
+  g_assert (GBP_IS_GIT_VCS_CONFIG (self));
+  g_assert (value != NULL);
 
-  if (!(global_file = ggit_config_find_global ()))
+  if ((proxy = get_config (self, NULL, NULL)))
     {
-      g_autofree gchar *path = NULL;
-
-      path = g_build_filename (g_get_home_dir (), ".gitconfig", NULL);
-      global_file = g_file_new_for_path (path);
-    }
+      g_autofree gchar *str = NULL;
 
-  self->config = ggit_config_new_from_file (global_file, NULL);
+      ipc_git_config_call_read_key_sync (proxy, get_key (type), &str, NULL, NULL);
+      ipc_git_config_call_close (proxy, NULL, NULL, NULL);
 
-  G_OBJECT_CLASS (gbp_git_vcs_config_parent_class)->constructed (object);
+      g_value_set_string (value, str);
+    }
 }
 
 static void
-gbp_git_vcs_config_finalize (GObject *object)
+gbp_git_vcs_config_set_config (IdeVcsConfig     *config,
+                               IdeVcsConfigType  type,
+                               const GValue     *value)
 {
-  GbpGitVcsConfig *self = GBP_GIT_VCS_CONFIG (object);
+  GbpGitVcsConfig *self = (GbpGitVcsConfig *)config;
+  g_autoptr(IpcGitConfig) proxy = NULL;
 
-  g_object_unref (self->config);
+  g_assert (GBP_IS_GIT_VCS_CONFIG (self));
+  g_assert (value != NULL);
 
-  G_OBJECT_CLASS (gbp_git_vcs_config_parent_class)->finalize (object);
+  if ((proxy = get_config (self, NULL, NULL)))
+    {
+      g_auto(GValue) str = G_VALUE_INIT;
+
+      if (!G_VALUE_HOLDS_STRING (value))
+        {
+          g_value_init (&str, G_TYPE_STRING);
+          g_value_transform (value, &str);
+          value = &str;
+        }
+
+      ipc_git_config_call_write_key_sync (proxy,
+                                          get_key (type),
+                                          g_value_get_string (value),
+                                          NULL, NULL);
+      ipc_git_config_call_close (proxy, NULL, NULL, NULL);
+    }
 }
 
 static void
@@ -175,13 +157,19 @@ vcs_config_init (IdeVcsConfigInterface *iface)
 static void
 gbp_git_vcs_config_class_init (GbpGitVcsConfigClass *klass)
 {
-  GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
-  object_class->constructed = gbp_git_vcs_config_constructed;
-  object_class->finalize = gbp_git_vcs_config_finalize;
 }
 
 static void
 gbp_git_vcs_config_init (GbpGitVcsConfig *self)
 {
+  self->is_global = TRUE;
+}
+
+void
+gbp_git_vcs_config_set_global (GbpGitVcsConfig *self,
+                               gboolean         is_global)
+{
+  g_return_if_fail (GBP_IS_GIT_VCS_CONFIG (self));
+
+  self->is_global = !!is_global;
 }
diff --git a/src/plugins/git/gbp-git-vcs-config.h b/src/plugins/git/gbp-git-vcs-config.h
index acc416d03..65a1d3721 100644
--- a/src/plugins/git/gbp-git-vcs-config.h
+++ b/src/plugins/git/gbp-git-vcs-config.h
@@ -1,6 +1,7 @@
 /* gbp-git-vcs-config.h
  *
  * Copyright 2016 Akshaya Kakkilaya <akshaya kakkilaya gmail com>
+ * Copyright 2016-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
@@ -26,8 +27,9 @@ G_BEGIN_DECLS
 
 #define GBP_TYPE_GIT_VCS_CONFIG (gbp_git_vcs_config_get_type())
 
-G_DECLARE_FINAL_TYPE (GbpGitVcsConfig, gbp_git_vcs_config, GBP, GIT_VCS_CONFIG, GObject)
+G_DECLARE_FINAL_TYPE (GbpGitVcsConfig, gbp_git_vcs_config, GBP, GIT_VCS_CONFIG, IdeObject)
 
-GbpGitVcsConfig *gbp_git_vcs_config_new (void);
+void gbp_git_vcs_config_set_global (GbpGitVcsConfig *self,
+                                    gboolean         is_global);
 
 G_END_DECLS
diff --git a/src/plugins/git/gbp-git-vcs-initializer.c b/src/plugins/git/gbp-git-vcs-initializer.c
index d05b3690d..faa4f6595 100644
--- a/src/plugins/git/gbp-git-vcs-initializer.c
+++ b/src/plugins/git/gbp-git-vcs-initializer.c
@@ -20,53 +20,70 @@
 
 #define G_LOG_DOMAIN "gbp-git-vcs-initializer"
 
-#include "config.h"
-
-#include <libgit2-glib/ggit.h>
-#include <libide-threading.h>
-
+#include "gbp-git-client.h"
 #include "gbp-git-vcs-initializer.h"
 
 struct _GbpGitVcsInitializer
 {
-  GObject parent_instance;
+  IdeObject parent_instance;
 };
 
-static void vcs_initializer_init (IdeVcsInitializerInterface *iface);
-
-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
-gbp_git_vcs_initializer_class_init (GbpGitVcsInitializerClass *klass)
+static gchar *
+gbp_git_vcs_initializer_get_title (IdeVcsInitializer *self)
 {
+  return g_strdup ("Git");
 }
 
 static void
-gbp_git_vcs_initializer_init (GbpGitVcsInitializer *self)
+create_cb (GObject      *object,
+           GAsyncResult *result,
+           gpointer      user_data)
 {
+  IpcGitService *service = (IpcGitService *)object;
+  g_autoptr(IdeTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+  g_autofree gchar *git_location = NULL;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (IPC_IS_GIT_SERVICE (service));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (IDE_IS_TASK (task));
+
+  if (!ipc_git_service_call_create_finish (service, &git_location, result, &error))
+    ide_task_return_error (task, g_steal_pointer (&error));
+  else
+    ide_task_return_boolean (task, TRUE);
 }
 
 static void
-gbp_git_vcs_initializer_initialize_worker (IdeTask      *task,
-                                           gpointer      source_object,
-                                           gpointer      task_data,
-                                           GCancellable *cancellable)
+get_service_cb (GObject      *object,
+                GAsyncResult *result,
+                gpointer      user_data)
 {
-  g_autoptr(GgitRepository) repository = NULL;
+  GbpGitClient *client = (GbpGitClient *)object;
+  g_autoptr(IpcGitService) service = NULL;
+  g_autoptr(IdeTask) task = user_data;
   g_autoptr(GError) error = NULL;
-  GFile *file = task_data;
+  GCancellable *cancellable;
+  GFile *file;
 
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_GIT_CLIENT (client));
+  g_assert (G_IS_ASYNC_RESULT (result));
   g_assert (IDE_IS_TASK (task));
-  g_assert (GBP_IS_GIT_VCS_INITIALIZER (source_object));
-  g_assert (G_IS_FILE (file));
 
-  repository = ggit_repository_init_repository (file, FALSE, &error);
+  cancellable = ide_task_get_cancellable (task);
+  file = ide_task_get_task_data (task);
 
-  if (repository == NULL)
+  if (!(service = gbp_git_client_get_service_finish (client, result, &error)))
     ide_task_return_error (task, g_steal_pointer (&error));
   else
-    ide_task_return_boolean (task, TRUE);
+    ipc_git_service_call_create (service,
+                                 g_file_peek_path (file),
+                                 FALSE,
+                                 cancellable,
+                                 create_cb,
+                                 g_steal_pointer (&task));
 }
 
 static void
@@ -76,16 +93,26 @@ gbp_git_vcs_initializer_initialize_async (IdeVcsInitializer   *initializer,
                                           GAsyncReadyCallback  callback,
                                           gpointer             user_data)
 {
-  GbpGitVcsInitializer *self = (GbpGitVcsInitializer *)initializer;
   g_autoptr(IdeTask) task = NULL;
+  GbpGitClient *client;
+  IdeContext *context;
 
-  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));
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_GIT_VCS_INITIALIZER (initializer));
+  g_assert (G_IS_FILE (file));
+  g_assert (!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, gbp_git_vcs_initializer_initialize_worker);
+  task = ide_task_new (initializer, cancellable, callback, user_data);
+  ide_task_set_source_tag (task, gbp_git_vcs_initializer_initialize_async);
+  ide_task_set_task_data (task, g_file_dup (file), g_object_unref);
+
+  context = ide_object_get_context (IDE_OBJECT (initializer));
+  client = gbp_git_client_from_context (context);
+
+  gbp_git_client_get_service_async (client,
+                                    cancellable,
+                                    get_service_cb,
+                                    g_steal_pointer (&task));
 }
 
 static gboolean
@@ -93,22 +120,30 @@ gbp_git_vcs_initializer_initialize_finish (IdeVcsInitializer  *initializer,
                                            GAsyncResult       *result,
                                            GError            **error)
 {
-  g_return_val_if_fail (GBP_IS_GIT_VCS_INITIALIZER (initializer), FALSE);
-  g_return_val_if_fail (IDE_IS_TASK (result), FALSE);
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_GIT_VCS_INITIALIZER (initializer));
+  g_assert (IDE_IS_TASK (result));
 
   return ide_task_propagate_boolean (IDE_TASK (result), error);
 }
 
-static gchar *
-gbp_git_vcs_initializer_get_title (IdeVcsInitializer *initilizer)
-{
-  return g_strdup ("Git");
-}
-
 static void
-vcs_initializer_init (IdeVcsInitializerInterface *iface)
+vcs_initializer_iface_init (IdeVcsInitializerInterface *iface)
 {
   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;
 }
+
+G_DEFINE_TYPE_WITH_CODE (GbpGitVcsInitializer, gbp_git_vcs_initializer, IDE_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (IDE_TYPE_VCS_INITIALIZER, vcs_initializer_iface_init))
+
+static void
+gbp_git_vcs_initializer_class_init (GbpGitVcsInitializerClass *klass)
+{
+}
+
+static void
+gbp_git_vcs_initializer_init (GbpGitVcsInitializer *self)
+{
+}
diff --git a/src/plugins/git/gbp-git-vcs-initializer.h b/src/plugins/git/gbp-git-vcs-initializer.h
index 16493bf88..cdaaab4db 100644
--- a/src/plugins/git/gbp-git-vcs-initializer.h
+++ b/src/plugins/git/gbp-git-vcs-initializer.h
@@ -26,6 +26,6 @@ G_BEGIN_DECLS
 
 #define GBP_TYPE_GIT_VCS_INITIALIZER (gbp_git_vcs_initializer_get_type())
 
-G_DECLARE_FINAL_TYPE (GbpGitVcsInitializer, gbp_git_vcs_initializer, GBP, GIT_VCS_INITIALIZER, GObject)
+G_DECLARE_FINAL_TYPE (GbpGitVcsInitializer, gbp_git_vcs_initializer, GBP, GIT_VCS_INITIALIZER, IdeObject)
 
 G_END_DECLS
diff --git a/src/plugins/git/gbp-git-vcs.c b/src/plugins/git/gbp-git-vcs.c
index b70e0f683..b74641e59 100644
--- a/src/plugins/git/gbp-git-vcs.c
+++ b/src/plugins/git/gbp-git-vcs.c
@@ -1,6 +1,6 @@
 /* gbp-git-vcs.c
  *
- * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ * Copyright 2014-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
@@ -22,7 +22,7 @@
 
 #include "config.h"
 
-#include <stdlib.h>
+#include "daemon/ipc-git-types.h"
 
 #include "gbp-git-branch.h"
 #include "gbp-git-tag.h"
@@ -31,476 +31,181 @@
 
 struct _GbpGitVcs
 {
-  IdeObject       parent_instance;
-  GgitRepository *repository;
-  GFile          *location;
-  GFile          *workdir;
-  gchar          *branch;
+  IdeObject         parent;
+
+  /* read-only, thread-safe access */
+  IpcGitRepository *repository;
+  GFile            *workdir;
 };
 
 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_autofree gchar *relative_path = NULL;
+  gboolean is_ignored = 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 (g_file_equal (self->workdir, file))
+    return FALSE;
 
   /*
-   * 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.
+   * This may be called from threads.
+   *
+   * However, we do not change our GbpGitVcs.repository field after the
+   * creation of the GbpGitVcs. Also, the GDBusProxy (IpcGitRepository)
+   * is thread-safe in terms of calling operations on the remote object
+   * from multiple threads.
+   *
+   * Also, GbpGitVcs.workdir is not changed after creation, so we can
+   * use that to for determining the relative path.
    */
-  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;
-}
+  if (!g_file_has_prefix (file, self->workdir))
+    return TRUE;
 
-typedef struct
-{
-  GFile      *repository_location;
-  GFile      *directory_or_file;
-  GFile      *workdir;
-  GListStore *store;
-  guint       recursive : 1;
-} ListStatus;
+  relative_path = g_file_get_relative_path (self->workdir, file);
 
-static void
-list_status_free (gpointer data)
-{
-  ListStatus *ls = data;
+  if (!ipc_git_repository_call_path_is_ignored_sync (self->repository,
+                                                     relative_path,
+                                                     &is_ignored,
+                                                     NULL,
+                                                     error))
+    return FALSE;
 
-  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);
+  return is_ignored;
 }
 
-static gint
-gbp_git_vcs_list_status_cb (const gchar     *path,
-                            GgitStatusFlags  flags,
-                            gpointer         user_data)
+static IdeVcsConfig *
+gbp_git_vcs_get_config (IdeVcs *vcs)
 {
-  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));
+  IdeVcsConfig *config;
 
-  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;
-    }
+  g_assert (GBP_IS_GIT_VCS (vcs));
 
-  info = g_object_new (IDE_TYPE_VCS_FILE_INFO,
-                       "file", file,
-                       "status", status,
-                       NULL);
+  config = g_object_new (GBP_TYPE_GIT_VCS_CONFIG,
+                         "parent", vcs,
+                         NULL);
+  gbp_git_vcs_config_set_global (GBP_GIT_VCS_CONFIG (config), FALSE);
 
-  g_list_store_append (state->store, info);
+  return g_steal_pointer (&config);
+}
 
-  return 0;
+static gchar *
+gbp_git_vcs_get_branch_name (IdeVcs *vcs)
+{
+  return ipc_git_repository_dup_branch (GBP_GIT_VCS (vcs)->repository);
 }
 
 static void
-gbp_git_vcs_list_status_worker (IdeTask      *task,
-                                gpointer      source_object,
-                                gpointer      task_data,
-                                GCancellable *cancellable)
+gbp_git_vcs_switch_branch_cb (GObject      *object,
+                              GAsyncResult *result,
+                              gpointer      user_data)
 {
-  ListStatus *state = task_data;
-  g_autoptr(GListStore) store = NULL;
-  g_autoptr(GFile) workdir = NULL;
-  g_autoptr(GgitRepository) repository = NULL;
-  g_autoptr(GgitStatusOptions) options = NULL;
+  IpcGitRepository *repository = (IpcGitRepository *)object;
+  g_autoptr(IdeTask) task = user_data;
   g_autoptr(GError) error = NULL;
-  g_autofree gchar *relative = NULL;
-  gchar *strv[] = { NULL, NULL };
 
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (IPC_IS_GIT_REPOSITORY (repository));
+  g_assert (G_IS_ASYNC_RESULT (result));
   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))
+  if (!ipc_git_repository_call_switch_branch_finish (repository, result, &error))
     ide_task_return_error (task, g_steal_pointer (&error));
   else
-    ide_task_return_pointer (task, g_steal_pointer (&store), g_object_unref);
+    ide_task_return_boolean (task, TRUE);
 }
 
 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)
+gbp_git_vcs_switch_branch_async (IdeVcs              *vcs,
+                                 IdeVcsBranch        *branch,
+                                 GCancellable        *cancellable,
+                                 GAsyncReadyCallback  callback,
+                                 gpointer             user_data)
 {
   GbpGitVcs *self = (GbpGitVcs *)vcs;
   g_autoptr(IdeTask) task = NULL;
-  ListStatus *state;
+  g_autofree gchar *branch_name = NULL;
 
-  IDE_ENTRY;
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_GIT_VCS (self));
+  g_assert (GBP_IS_GIT_BRANCH (branch));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
 
-  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));
+  task = ide_task_new (self, cancellable, callback, user_data);
+  ide_task_set_source_tag (task, gbp_git_vcs_switch_branch_async);
 
-  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));
+  branch_name = ide_vcs_branch_get_name (branch);
 
-  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);
+  ipc_git_repository_call_switch_branch (self->repository,
+                                         branch_name,
+                                         cancellable,
+                                         gbp_git_vcs_switch_branch_cb,
+                                         g_steal_pointer (&task));
+}
 
-  IDE_EXIT;
+static gboolean
+gbp_git_vcs_switch_branch_finish (IdeVcs        *vcs,
+                                  GAsyncResult  *result,
+                                  GError       **error)
+{
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_GIT_VCS (vcs));
+  g_assert (IDE_IS_TASK (result));
+
+  return ide_task_propagate_boolean (IDE_TASK (result), error);
 }
 
-static GListModel *
-gbp_git_vcs_list_status_finish (IdeVcs        *vcs,
-                                GAsyncResult  *result,
-                                GError       **error)
+static GPtrArray *
+create_branches (gchar **refs)
 {
-  g_return_val_if_fail (GBP_IS_GIT_VCS (vcs), NULL);
-  g_return_val_if_fail (IDE_IS_TASK (result), NULL);
+  GPtrArray *ret = g_ptr_array_new_with_free_func (g_object_unref);
+
+  if (refs != NULL)
+    {
+      for (guint i = 0; refs[i]; i++)
+        g_ptr_array_add (ret, gbp_git_branch_new (refs[i]));
+    }
 
-  return ide_task_propagate_pointer (IDE_TASK (result), error);
+  return g_steal_pointer (&ret);
 }
 
 static void
-gbp_git_vcs_list_branches_worker (IdeTask      *task,
-                                  gpointer      source_object,
-                                  gpointer      task_data,
-                                  GCancellable *cancellable)
+gbp_git_vcs_list_branches_cb (GObject      *object,
+                              GAsyncResult *result,
+                              gpointer      user_data)
 {
-  GbpGitVcs *self = source_object;
-  g_autoptr(GPtrArray) branches = NULL;
+  IpcGitRepository *repository = (IpcGitRepository *)object;
+  g_autoptr(IdeTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+  g_auto(GStrv) refs = NULL;
 
+  g_assert (IPC_IS_GIT_REPOSITORY (repository));
+  g_assert (G_IS_ASYNC_RESULT (result));
   g_assert (IDE_IS_TASK (task));
-  g_assert (GBP_IS_GIT_VCS (self));
-  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
-
-  branches = g_ptr_array_new_with_free_func (g_object_unref);
-
-  ide_object_lock (IDE_OBJECT (self));
 
-  if (self->repository != NULL)
-    {
-      g_autoptr(GgitBranchEnumerator) enumerator = NULL;
-      g_autoptr(GError) error = NULL;
-
-      if (!(enumerator = ggit_repository_enumerate_branches (self->repository,
-                                                             GGIT_BRANCH_LOCAL,
-                                                             &error)))
-        {
-          ide_task_return_error (task, g_steal_pointer (&error));
-          goto unlock;
-        }
-
-      while (ggit_branch_enumerator_next (enumerator))
-        {
-          g_autoptr(GgitRef) ref = ggit_branch_enumerator_get (enumerator);
-          const gchar *name = ggit_ref_get_name (ref);
-
-          g_ptr_array_add (branches, gbp_git_branch_new (name));
-        }
-
-      ide_task_return_pointer (task,
-                               g_steal_pointer (&branches),
-                               g_ptr_array_unref);
-    }
+  if (!ipc_git_repository_call_list_refs_by_kind_finish (repository, &refs, result, &error))
+    ide_task_return_error (task, g_steal_pointer (&error));
   else
-    {
-      ide_task_return_new_error (task,
-                                 G_IO_ERROR,
-                                 G_IO_ERROR_FAILED,
-                                 "No repository to access");
-    }
-
-unlock:
-  ide_object_unlock (IDE_OBJECT (self));
+    ide_task_return_pointer (task, create_branches (refs), g_ptr_array_unref);
 }
 
 static void
@@ -512,27 +217,18 @@ gbp_git_vcs_list_branches_async (IdeVcs              *vcs,
   GbpGitVcs *self = (GbpGitVcs *)vcs;
   g_autoptr(IdeTask) task = NULL;
 
-  IDE_ENTRY;
-
   g_assert (IDE_IS_MAIN_THREAD ());
   g_assert (GBP_IS_GIT_VCS (self));
   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_list_status_async);
-  ide_task_set_return_on_cancel (task, TRUE);
-
-  ide_object_lock (IDE_OBJECT (self));
-  if (self->repository == 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_branches_worker);
-  ide_object_unlock (IDE_OBJECT (self));
+  ide_task_set_source_tag (task, gbp_git_vcs_list_branches_async);
 
-  IDE_EXIT;
+  ipc_git_repository_call_list_refs_by_kind (self->repository,
+                                             IPC_GIT_REF_BRANCH,
+                                             cancellable,
+                                             gbp_git_vcs_list_branches_cb,
+                                             g_steal_pointer (&task));
 }
 
 static GPtrArray *
@@ -540,67 +236,49 @@ gbp_git_vcs_list_branches_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);
+  GPtrArray *ret;
 
-  return ide_task_propagate_pointer (IDE_TASK (result), error);
-}
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_GIT_VCS (vcs));
+  g_assert (IDE_IS_TASK (result));
 
-static gint
-compare_tags (gconstpointer a,
-              gconstpointer b)
-{
-  return g_utf8_collate (*(const gchar **)a, *(const gchar **)b);
+  ret = ide_task_propagate_pointer (IDE_TASK (result), error);
+
+  return IDE_PTR_ARRAY_STEAL_FULL (&ret);
 }
 
-static void
-gbp_git_vcs_list_tags_worker (IdeTask      *task,
-                              gpointer      source_object,
-                              gpointer      task_data,
-                              GCancellable *cancellable)
+static GPtrArray *
+create_tags (gchar **refs)
 {
-  GbpGitVcs *self = source_object;
-  g_autoptr(GPtrArray) tags = NULL;
-
-  g_assert (IDE_IS_TASK (task));
-  g_assert (GBP_IS_GIT_VCS (self));
-  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+  GPtrArray *ret = g_ptr_array_new_with_free_func (g_object_unref);
 
-  tags = g_ptr_array_new_with_free_func (g_object_unref);
-
-  ide_object_lock (IDE_OBJECT (self));
-
-  if (self->repository != NULL)
+  if (refs != NULL)
     {
-      g_autoptr(GgitBranchEnumerator) enumerator = NULL;
-      g_auto(GStrv) names = NULL;
-      g_autoptr(GError) error = NULL;
+      for (guint i = 0; refs[i]; i++)
+        g_ptr_array_add (ret, gbp_git_tag_new (refs[i]));
+    }
 
-      if (!(names = ggit_repository_list_tags (self->repository, &error)))
-        {
-          ide_task_return_error (task, g_steal_pointer (&error));
-          goto unlock;
-        }
+  return g_steal_pointer (&ret);
+}
 
-      qsort (names, g_strv_length (names), sizeof (gchar *), compare_tags);
+static void
+gbp_git_vcs_list_tags_cb (GObject      *object,
+                              GAsyncResult *result,
+                              gpointer      user_data)
+{
+  IpcGitRepository *repository = (IpcGitRepository *)object;
+  g_autoptr(IdeTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+  g_auto(GStrv) refs = NULL;
 
-      for (guint i = 0; names[i] != NULL; i++)
-        g_ptr_array_add (tags, gbp_git_tag_new (names[i]));
+  g_assert (IPC_IS_GIT_REPOSITORY (repository));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (IDE_IS_TASK (task));
 
-      ide_task_return_pointer (task,
-                               g_steal_pointer (&tags),
-                               g_ptr_array_unref);
-    }
+  if (!ipc_git_repository_call_list_refs_by_kind_finish (repository, &refs, result, &error))
+    ide_task_return_error (task, g_steal_pointer (&error));
   else
-    {
-      ide_task_return_new_error (task,
-                                 G_IO_ERROR,
-                                 G_IO_ERROR_FAILED,
-                                 "No repository to access");
-    }
-
-unlock:
-  ide_object_unlock (IDE_OBJECT (self));
+    ide_task_return_pointer (task, create_tags (refs), g_ptr_array_unref);
 }
 
 static void
@@ -612,27 +290,18 @@ gbp_git_vcs_list_tags_async (IdeVcs              *vcs,
   GbpGitVcs *self = (GbpGitVcs *)vcs;
   g_autoptr(IdeTask) task = NULL;
 
-  IDE_ENTRY;
-
   g_assert (IDE_IS_MAIN_THREAD ());
   g_assert (GBP_IS_GIT_VCS (self));
   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_list_status_async);
-  ide_task_set_return_on_cancel (task, TRUE);
-
-  ide_object_lock (IDE_OBJECT (self));
-  if (self->repository == 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_tags_worker);
-  ide_object_unlock (IDE_OBJECT (self));
+  ide_task_set_source_tag (task, gbp_git_vcs_list_tags_async);
 
-  IDE_EXIT;
+  ipc_git_repository_call_list_refs_by_kind (self->repository,
+                                             IPC_GIT_REF_TAG,
+                                             cancellable,
+                                             gbp_git_vcs_list_tags_cb,
+                                             g_steal_pointer (&task));
 }
 
 static GPtrArray *
@@ -640,215 +309,256 @@ gbp_git_vcs_list_tags_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);
+  GPtrArray *ret;
 
-  return ide_task_propagate_pointer (IDE_TASK (result), error);
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_GIT_VCS (vcs));
+  g_assert (IDE_IS_TASK (result));
+
+  ret = ide_task_propagate_pointer (IDE_TASK (result), error);
+
+  return IDE_PTR_ARRAY_STEAL_FULL (&ret);
 }
 
-static void
-gbp_git_vcs_switch_branch_worker (IdeTask      *task,
-                                  gpointer      source_object,
-                                  gpointer      task_data,
-                                  GCancellable *cancellable)
+static GListModel *
+create_status_model (GbpGitVcs *self,
+                     GVariant  *files)
 {
-  g_autoptr(GgitCheckoutOptions) checkout_options = NULL;
-  g_autoptr(GgitObject) obj = NULL;
-  g_autoptr(GgitRef) ref = NULL;
-  g_autoptr(GError) error = NULL;
-  GbpGitVcs *self = source_object;
-  const gchar *id = task_data;
+  g_autoptr(GListStore) store = NULL;
+  GVariantIter iter;
+  const gchar *path = NULL;
+  guint flags = 0;
 
-  g_assert (IDE_IS_TASK (task));
   g_assert (GBP_IS_GIT_VCS (self));
-  g_assert (id != NULL);
-  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+  g_assert (files != NULL);
 
-  ide_object_lock (IDE_OBJECT (self));
+  store = g_list_store_new (IDE_TYPE_VCS_FILE_INFO);
 
-  if (self->repository == NULL)
-    {
-      ide_task_return_new_error (task,
-                                 G_IO_ERROR,
-                                 G_IO_ERROR_FAILED,
-                                 "No repository to switch");
-      goto unlock;
-    }
+  g_variant_iter_init (&iter, files);
 
-  if (!(ref = ggit_repository_lookup_reference (self->repository, id, &error)) ||
-      !(obj = ggit_ref_lookup (ref, &error)))
+  while (g_variant_iter_next (&iter, "(&su)", &path, &flags))
     {
-      ide_task_return_error (task, g_steal_pointer (&error));
-      goto unlock;
+      g_autoptr(GFile) file = g_file_get_child (self->workdir, path);
+
+      g_list_store_append (store,
+                           g_object_new (IDE_TYPE_VCS_FILE_INFO,
+                                         "file", file,
+                                         "status", flags,
+                                         NULL));
     }
 
-  checkout_options = ggit_checkout_options_new ();
-  ggit_checkout_options_set_strategy (checkout_options, GGIT_CHECKOUT_SAFE);
+  return G_LIST_MODEL (g_steal_pointer (&store));
+}
 
-  /* Update the tree contents */
-  if (!ggit_repository_checkout_tree (self->repository,
-                                      obj,
-                                      checkout_options,
-                                      &error))
-    {
-      ide_task_return_error (task, g_steal_pointer (&error));
-      goto unlock;
-    }
+static void
+gbp_git_vcs_list_status_cb (GObject      *object,
+                            GAsyncResult *result,
+                            gpointer      user_data)
+{
+  IpcGitRepository *repository = (IpcGitRepository *)object;
+  g_autoptr(GVariant) files = NULL;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(IdeTask) task = user_data;
+  GbpGitVcs *self;
 
-  /* Now update head to point at the branch */
-  if (!ggit_repository_set_head (self->repository, id, &error))
-    {
-      ide_task_return_error (task, g_steal_pointer (&error));
-      goto unlock;
-    }
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (IPC_IS_GIT_REPOSITORY (repository));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (IDE_IS_TASK (task));
 
-  ide_task_return_boolean (task, TRUE);
+  self = ide_task_get_source_object (task);
 
-unlock:
-  ide_object_unlock (IDE_OBJECT (self));
+  if (!ipc_git_repository_call_list_status_finish (repository, &files, result, &error))
+    ide_task_return_error (task, g_steal_pointer (&error));
+  else
+    ide_task_return_object (task, create_status_model (self, files));
 }
 
 static void
-gbp_git_vcs_switch_branch_async (IdeVcs              *vcs,
-                                 IdeVcsBranch        *branch,
-                                 GCancellable        *cancellable,
-                                 GAsyncReadyCallback  callback,
-                                 gpointer             user_data)
+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;
+  g_autofree gchar *relative_path = NULL;
 
   g_assert (IDE_IS_MAIN_THREAD ());
-  g_assert (GBP_IS_GIT_VCS (vcs));
-  g_assert (GBP_IS_GIT_BRANCH (branch));
+  g_assert (GBP_IS_GIT_VCS (self));
+  g_assert (G_IS_FILE (directory_or_file));
   g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
 
-  task = ide_task_new (vcs, cancellable, callback, user_data);
-  ide_task_set_source_tag (task, gbp_git_vcs_switch_branch_async);
-  ide_task_set_task_data (task,
-                          g_strdup (ide_vcs_branch_get_name (branch)),
-                          g_free);
-  ide_task_run_in_thread (task, gbp_git_vcs_switch_branch_worker);
+  task = ide_task_new (self, cancellable, callback, user_data);
+  ide_task_set_source_tag (task, gbp_git_vcs_list_status_async);
+
+  if (!g_file_has_prefix (directory_or_file, self->workdir) &&
+      !g_file_equal (directory_or_file, self->workdir))
+    {
+      ide_task_return_new_error (task,
+                                 G_IO_ERROR,
+                                 G_IO_ERROR_NOT_SUPPORTED,
+                                 "Directory is not within repository");
+      return;
+    }
+
+  relative_path = g_file_get_relative_path (self->workdir, directory_or_file);
+
+  ipc_git_repository_call_list_status (self->repository,
+                                       relative_path ?: "",
+                                       cancellable,
+                                       gbp_git_vcs_list_status_cb,
+                                       g_steal_pointer (&task));
 }
 
-static gboolean
-gbp_git_vcs_switch_branch_finish (IdeVcs        *vcs,
-                                  GAsyncResult  *result,
-                                  GError       **error)
+static GListModel *
+gbp_git_vcs_list_status_finish (IdeVcs        *vcs,
+                                GAsyncResult  *result,
+                                GError       **error)
 {
   g_assert (IDE_IS_MAIN_THREAD ());
   g_assert (GBP_IS_GIT_VCS (vcs));
   g_assert (IDE_IS_TASK (result));
 
-  ide_vcs_emit_changed (vcs);
-
-  return ide_task_propagate_boolean (IDE_TASK (result), error);
+  return ide_task_propagate_object (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;
+  iface->get_config = gbp_git_vcs_get_config;
+  iface->get_branch_name = gbp_git_vcs_get_branch_name;
+  iface->switch_branch_async = gbp_git_vcs_switch_branch_async;
+  iface->switch_branch_finish = gbp_git_vcs_switch_branch_finish;
   iface->list_branches_async = gbp_git_vcs_list_branches_async;
   iface->list_branches_finish = gbp_git_vcs_list_branches_finish;
   iface->list_tags_async = gbp_git_vcs_list_tags_async;
   iface->list_tags_finish = gbp_git_vcs_list_tags_finish;
-  iface->switch_branch_async = gbp_git_vcs_switch_branch_async;
-  iface->switch_branch_finish = gbp_git_vcs_switch_branch_finish;
+  iface->list_status_async = gbp_git_vcs_list_status_async;
+  iface->list_status_finish = gbp_git_vcs_list_status_finish;
 }
 
+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_reload_worker (IdeTask      *task,
-                           gpointer      source_object,
-                           gpointer      task_data,
-                           GCancellable *cancellable)
+gbp_git_vcs_finalize (GObject *object)
 {
-  g_autoptr(GgitRepository) repository = NULL;
-  g_autoptr(GError) error = NULL;
-  GFile *location = task_data;
+  GbpGitVcs *self = (GbpGitVcs *)object;
 
-  IDE_ENTRY;
+  g_clear_object (&self->repository);
 
-  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));
+  G_OBJECT_CLASS (gbp_git_vcs_parent_class)->finalize (object);
+}
 
-  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);
+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_take_string (value, gbp_git_vcs_get_branch_name (IDE_VCS (self)));
+      break;
 
-  IDE_EXIT;
+    case PROP_WORKDIR:
+      g_value_take_object (value, g_file_dup (self->workdir));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
 }
 
-void
-gbp_git_vcs_reload_async (GbpGitVcs           *self,
-                          GCancellable        *cancellable,
-                          GAsyncReadyCallback  callback,
-                          gpointer             user_data)
+static void
+gbp_git_vcs_class_init (GbpGitVcsClass *klass)
 {
-  g_autoptr(IdeTask) task = NULL;
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
-  IDE_ENTRY;
+  object_class->finalize = gbp_git_vcs_finalize;
+  object_class->get_property = gbp_git_vcs_get_property;
 
-  g_assert (IDE_IS_MAIN_THREAD ());
-  g_assert (GBP_IS_GIT_VCS (self));
+  properties [PROP_BRANCH_NAME] =
+    g_param_spec_string ("branch-name",
+                         "Branch Name",
+                         "The name of the current branch",
+                         NULL,
+                         (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
 
-  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);
+  properties [PROP_WORKDIR] =
+    g_param_spec_object ("workdir",
+                         "Workdir",
+                         "The workdir of the vcs",
+                         G_TYPE_FILE,
+                         (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
 
-  IDE_EXIT;
+  g_object_class_install_properties (object_class, N_PROPS, properties);
 }
 
-gboolean
-gbp_git_vcs_reload_finish (GbpGitVcs     *self,
-                           GAsyncResult  *result,
-                           GError       **error)
+static void
+gbp_git_vcs_init (GbpGitVcs *self)
 {
-  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);
+static void
+gbp_git_vcs_notify_branch_cb (GbpGitVcs        *self,
+                              GParamSpec       *pspec,
+                              IpcGitRepository *repository)
+{
+  g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BRANCH_NAME]);
+}
 
-  ide_object_lock (IDE_OBJECT (self));
+static void
+gbp_git_vcs_changed_cb (GbpGitVcs        *self,
+                        IpcGitRepository *repository)
+{
+  ide_vcs_emit_changed (IDE_VCS (self));
+}
 
-  if (!(repository = ide_task_propagate_pointer (IDE_TASK (result), error)))
-    goto failure;
+GbpGitVcs *
+gbp_git_vcs_new (IpcGitRepository *repository)
+{
+  const gchar *workdir;
+  GbpGitVcs *ret;
 
-  if ((ref = ggit_repository_get_head (repository, NULL)))
-    {
-      const gchar *name = ggit_ref_get_shorthand (ref);
-
-      if (name != NULL)
-        {
-          if (!ide_str_equal0 (name, self->branch))
-            {
-              g_free (self->branch);
-              self->branch = g_strdup (name);
-              g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BRANCH_NAME]);
-            }
-        }
-    }
+  g_return_val_if_fail (IPC_IS_GIT_REPOSITORY (repository), NULL);
 
-  if (g_set_object (&self->repository, repository))
-    {
-      ide_vcs_emit_changed (IDE_VCS (self));
-      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_REPOSITORY]);
-    }
+  workdir = ipc_git_repository_get_workdir (repository);
+
+  ret = g_object_new (GBP_TYPE_GIT_VCS, NULL);
+  ret->repository = g_object_ref (repository);
+  ret->workdir = g_file_new_for_path (workdir);
+
+  g_signal_connect_object (repository,
+                           "notify::branch",
+                           G_CALLBACK (gbp_git_vcs_notify_branch_cb),
+                           ret,
+                           G_CONNECT_SWAPPED);
+
+  g_signal_connect_object (repository,
+                           "changed",
+                           G_CALLBACK (gbp_git_vcs_changed_cb),
+                           ret,
+                           G_CONNECT_SWAPPED);
+
+  return g_steal_pointer (&ret);
+}
 
-failure:
-  ide_object_unlock (IDE_OBJECT (self));
+IpcGitRepository *
+gbp_git_vcs_get_repository (GbpGitVcs *self)
+{
+  g_return_val_if_fail (GBP_IS_GIT_VCS (self), NULL);
 
-  return repository != NULL;
+  return self->repository;
 }
diff --git a/src/plugins/git/gbp-git-vcs.h b/src/plugins/git/gbp-git-vcs.h
index cb337921a..0d395dbf6 100644
--- a/src/plugins/git/gbp-git-vcs.h
+++ b/src/plugins/git/gbp-git-vcs.h
@@ -1,6 +1,6 @@
 /* gbp-git-vcs.h
  *
- * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ * Copyright 2014-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,17 @@
 
 #pragma once
 
-#include <libgit2-glib/ggit.h>
 #include <libide-vcs.h>
 
+#include "daemon/ipc-git-repository.h"
+
 G_BEGIN_DECLS
 
 #define GBP_TYPE_GIT_VCS (gbp_git_vcs_get_type())
 
 G_DECLARE_FINAL_TYPE (GbpGitVcs, gbp_git_vcs, GBP, GIT_VCS, IdeObject)
 
-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        gbp_git_vcs_reload_finish  (GbpGitVcs            *self,
-                                            GAsyncResult         *result,
-                                            GError              **error);
+GbpGitVcs        *gbp_git_vcs_new            (IpcGitRepository *repository);
+IpcGitRepository *gbp_git_vcs_get_repository (GbpGitVcs        *self);
 
 G_END_DECLS
diff --git a/src/plugins/git/gbp-git-workbench-addin.c b/src/plugins/git/gbp-git-workbench-addin.c
index e3831d98e..669250456 100644
--- a/src/plugins/git/gbp-git-workbench-addin.c
+++ b/src/plugins/git/gbp-git-workbench-addin.c
@@ -1,6 +1,6 @@
 /* gbp-git-workbench-addin.c
  *
- * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ * Copyright 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
@@ -22,243 +22,164 @@
 
 #include "config.h"
 
-#include <libgit2-glib/ggit.h>
-#include <libide-editor.h>
-#include <libide-io.h>
-#include <libide-threading.h>
+#include <libide-vcs.h>
 
-#include "gbp-git-buffer-change-monitor.h"
-#include "gbp-git-index-monitor.h"
-#include "gbp-git-vcs.h"
+#include "daemon/ipc-git-service.h"
+
+#include "gbp-git-client.h"
 #include "gbp-git-workbench-addin.h"
+#include "gbp-git-vcs.h"
 
 struct _GbpGitWorkbenchAddin
 {
-  GObject             parent_instance;
-  IdeWorkbench       *workbench;
-  GbpGitIndexMonitor *monitor;
-  guint               has_loaded : 1;
+  GObject       parent_instance;
+  IdeWorkbench *workbench;
 };
 
 static void
-gbp_git_workbench_addin_load_project_worker (IdeTask      *task,
-                                             gpointer      source_object,
-                                             gpointer      task_data,
-                                             GCancellable *cancellable)
+gbp_git_workbench_addin_load_project_new_cb (GObject      *object,
+                                             GAsyncResult *result,
+                                             gpointer      user_data)
 {
-  g_autoptr(GgitRepository) repository = NULL;
-  g_autoptr(GbpGitVcs) vcs = NULL;
-  g_autoptr(GFile) location = NULL;
-  g_autoptr(GFile) workdir = NULL;
+  GbpGitWorkbenchAddin *self;
+  g_autoptr(IpcGitRepository) repository = NULL;
+  g_autoptr(IdeTask) task = user_data;
   g_autoptr(GError) error = NULL;
-  g_autofree gchar *worktree_branch = NULL;
-  GFile *directory = task_data;
 
+  g_assert (G_IS_ASYNC_RESULT (result));
   g_assert (IDE_IS_TASK (task));
-  g_assert (GBP_IS_GIT_WORKBENCH_ADDIN (source_object));
-  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)))
+  if (!(repository = ipc_git_repository_proxy_new_finish (result, &error)))
     {
       ide_task_return_error (task, g_steal_pointer (&error));
       return;
     }
 
-  workdir = ggit_repository_get_workdir (repository);
+  self = ide_task_get_source_object (task);
 
-  g_assert (G_IS_FILE (location));
-  g_assert (G_IS_FILE (workdir));
-  g_assert (GGIT_IS_REPOSITORY (repository));
-
-  if (worktree_branch == NULL)
+  if (self->workbench != NULL)
     {
-      g_autoptr(GgitRef) ref = NULL;
-
-      if ((ref = ggit_repository_get_head (repository, NULL)))
-        worktree_branch = g_strdup (ggit_ref_get_shorthand (ref));
+      g_autoptr(GbpGitVcs) vcs = gbp_git_vcs_new (repository);
 
-      if (worktree_branch == NULL)
-        worktree_branch = g_strdup ("master");
+      ide_workbench_set_vcs (self->workbench, IDE_VCS (vcs));
     }
 
-  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);
+  ide_task_return_boolean (task, TRUE);
 }
 
 static void
-gbp_git_workbench_addin_load_project_async (IdeWorkbenchAddin   *addin,
-                                            IdeProjectInfo      *project_info,
-                                            GCancellable        *cancellable,
-                                            GAsyncReadyCallback  callback,
-                                            gpointer             user_data)
+gbp_git_workbench_addin_load_project_open_cb (GObject      *object,
+                                              GAsyncResult *result,
+                                              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);
+  IpcGitService *service = (IpcGitService *)object;
+  g_autofree gchar *obj_path = NULL;
+  g_autoptr(IdeTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+  GDBusConnection *connection;
 
-  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;
-    }
+  g_assert (IPC_IS_GIT_SERVICE (service));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (IDE_IS_TASK (task));
 
-  /* 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);
+  connection = g_dbus_proxy_get_connection (G_DBUS_PROXY (service));
+
+  if (!ipc_git_service_call_open_finish (service, &obj_path, result, &error))
+    ide_task_return_error (task, g_steal_pointer (&error));
+  else
+    ipc_git_repository_proxy_new (connection,
+                                  G_DBUS_PROXY_FLAGS_NONE,
+                                  NULL,
+                                  obj_path,
+                                  ide_task_get_cancellable (task),
+                                  gbp_git_workbench_addin_load_project_new_cb,
+                                  g_object_ref (task));
 }
 
 static void
-gbp_git_workbench_addin_foreach_buffer_cb (IdeBuffer *buffer,
-                                           gpointer   user_data)
+gbp_git_workbench_addin_load_project_discover_cb (GObject      *object,
+                                                  GAsyncResult *result,
+                                                  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));
+  IpcGitService *service = (IpcGitService *)object;
+  g_autofree gchar *git_location = NULL;
+  g_autoptr(IdeTask) task = user_data;
+  g_autoptr(GError) error = NULL;
 
-  monitor = ide_buffer_get_change_monitor (buffer);
+  g_assert (IPC_IS_GIT_SERVICE (service));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (IDE_IS_TASK (task));
 
-  if (GBP_IS_GIT_BUFFER_CHANGE_MONITOR (monitor))
-    gbp_git_buffer_change_monitor_set_repository (GBP_GIT_BUFFER_CHANGE_MONITOR (monitor),
-                                                  repository);
+  if (!ipc_git_service_call_discover_finish (service, &git_location, result, &error))
+    ide_task_return_error (task, g_steal_pointer (&error));
+  else
+    ipc_git_service_call_open (service,
+                               git_location,
+                               ide_task_get_cancellable (task),
+                               gbp_git_workbench_addin_load_project_open_cb,
+                               g_object_ref (task));
 }
 
 static void
-gbp_git_workbench_addin_reload_cb (GObject      *object,
-                                   GAsyncResult *result,
-                                   gpointer      user_data)
+gbp_git_workbench_addin_load_project_service_cb (GObject      *object,
+                                                 GAsyncResult *result,
+                                                 gpointer      user_data)
 {
-  GbpGitVcs *vcs = (GbpGitVcs *)object;
-  g_autoptr(GbpGitWorkbenchAddin) self = user_data;
+  GbpGitClient *client = (GbpGitClient *)object;
+  g_autoptr(IpcGitService) service = NULL;
+  g_autoptr(IdeTask) task = user_data;
   g_autoptr(GError) error = NULL;
-  IdeBufferManager *buffer_manager;
-  GgitRepository *repository;
-  IdeContext *context;
+  IdeProjectInfo *project_info;
+  GFile *directory;
 
-  g_assert (IDE_IS_MAIN_THREAD ());
-  g_assert (GBP_IS_GIT_VCS (vcs));
+  g_assert (GBP_IS_GIT_CLIENT (client));
   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;
+  g_assert (IDE_IS_TASK (task));
 
-  if (self->workbench == NULL)
-    return;
+  if (!(service = gbp_git_client_get_service_finish (client, result, &error)))
+    {
+      ide_task_return_error (task, g_steal_pointer (&error));
+      return;
+    }
 
-  repository = gbp_git_vcs_get_repository (vcs);
-  context = ide_workbench_get_context (self->workbench);
-  buffer_manager = ide_buffer_manager_from_context (context);
+  project_info = ide_task_get_task_data (task);
+  directory = ide_project_info_get_directory (project_info);
 
-  ide_buffer_manager_foreach (buffer_manager,
-                              gbp_git_workbench_addin_foreach_buffer_cb,
-                              repository);
+  ipc_git_service_call_discover (service,
+                                 g_file_peek_path (directory),
+                                 ide_task_get_cancellable (task),
+                                 gbp_git_workbench_addin_load_project_discover_cb,
+                                 g_object_ref (task));
 }
 
 static void
-gbp_git_workbench_addin_monitor_changed_cb (GbpGitWorkbenchAddin *self,
-                                            GbpGitIndexMonitor   *monitor)
+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;
+  g_autoptr(GError) error = NULL;
+  GbpGitClient *client;
   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);
+  g_assert (IDE_IS_PROJECT_INFO (project_info));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
 
-  if (!GBP_IS_GIT_VCS (vcs))
-    IDE_EXIT;
+  task = ide_task_new (self, cancellable, callback, user_data);
+  ide_task_set_source_tag (task, gbp_git_workbench_addin_load_project_async);
+  ide_task_set_task_data (task, g_object_ref (project_info), g_object_unref);
 
-  gbp_git_vcs_reload_async (GBP_GIT_VCS (vcs),
-                            NULL,
-                            gbp_git_workbench_addin_reload_cb,
-                            g_object_ref (self));
+  context = ide_workbench_get_context (self->workbench);
+  client = gbp_git_client_from_context (context);
 
-  IDE_EXIT;
+  gbp_git_client_get_service_async (client,
+                                    cancellable,
+                                    gbp_git_workbench_addin_load_project_service_cb,
+                                    g_steal_pointer (&task));
 }
 
 static gboolean
@@ -266,98 +187,34 @@ 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 (GBP_IS_GIT_WORKBENCH_ADDIN (addin));
   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;
+  return ide_task_propagate_boolean (IDE_TASK (result), error);
 }
 
 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);
+  self->workbench = workbench;
 }
 
 static void
-gbp_git_workbench_addin_workspace_added (IdeWorkbenchAddin *addin,
-                                         IdeWorkspace      *workspace)
+gbp_git_workbench_addin_unload (IdeWorkbenchAddin *addin,
+                                IdeWorkbench      *workbench)
 {
   GbpGitWorkbenchAddin *self = (GbpGitWorkbenchAddin *)addin;
 
   g_assert (GBP_IS_GIT_WORKBENCH_ADDIN (self));
-  g_assert (IDE_IS_WORKSPACE (workspace));
+  g_assert (IDE_IS_WORKBENCH (workbench));
 
-  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);
-        }
-    }
+  self->workbench = NULL;
 }
 
 static void
@@ -367,12 +224,10 @@ workbench_addin_iface_init (IdeWorkbenchAddinInterface *iface)
   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))
+                         G_IMPLEMENT_INTERFACE (IDE_TYPE_WORKBENCH_ADDIN, workbench_addin_iface_init))
 
 static void
 gbp_git_workbench_addin_class_init (GbpGitWorkbenchAddinClass *klass)
diff --git a/src/plugins/git/gbp-git-workbench-addin.h b/src/plugins/git/gbp-git-workbench-addin.h
index 218b02004..9b7dba9bd 100644
--- a/src/plugins/git/gbp-git-workbench-addin.h
+++ b/src/plugins/git/gbp-git-workbench-addin.h
@@ -1,6 +1,6 @@
 /* gbp-git-workbench-addin.h
  *
- * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ * Copyright 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
diff --git a/src/plugins/git/git-plugin.c b/src/plugins/git/git-plugin.c
index 85ae8d074..c77425f12 100644
--- a/src/plugins/git/git-plugin.c
+++ b/src/plugins/git/git-plugin.c
@@ -23,7 +23,6 @@
 #include "config.h"
 
 #include <libpeas/peas.h>
-#include <libgit2-glib/ggit.h>
 #include <libide-editor.h>
 #include <libide-foundry.h>
 #include <libide-vcs.h>
@@ -31,66 +30,35 @@
 #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)
-{
-  GgitFeatureFlags ggit_flags;
-
-  ggit_init ();
-
-  ggit_flags = ggit_get_features ();
-
-  if ((ggit_flags & GGIT_FEATURE_THREADS) == 0)
-    {
-      g_printerr ("Builder requires libgit2-glib with threading support.");
-      return FALSE;
-    }
-
-  if ((ggit_flags & GGIT_FEATURE_SSH) == 0)
-    {
-      g_printerr ("Builder requires libgit2-glib with SSH support.");
-      return FALSE;
-    }
-
-  return TRUE;
-}
-
-
 _IDE_EXTERN void
 _gbp_git_register_types (PeasObjectModule *module)
 {
-  if (register_ggit ())
-    {
-      ide_g_file_add_ignored_pattern (".git");
-
-      peas_object_module_register_extension_type (module,
-                                                  IDE_TYPE_BUFFER_ADDIN,
-                                                  GBP_TYPE_GIT_BUFFER_ADDIN);
-      peas_object_module_register_extension_type (module,
-                                                  IDE_TYPE_PIPELINE_ADDIN,
-                                                  GBP_TYPE_GIT_PIPELINE_ADDIN);
-      peas_object_module_register_extension_type (module,
-                                                  IDE_TYPE_DEPENDENCY_UPDATER,
-                                                  GBP_TYPE_GIT_DEPENDENCY_UPDATER);
-      peas_object_module_register_extension_type (module,
-                                                  IDE_TYPE_VCS_CONFIG,
-                                                  GBP_TYPE_GIT_VCS_CONFIG);
-      peas_object_module_register_extension_type (module,
-                                                  IDE_TYPE_VCS_CLONER,
-                                                  GBP_TYPE_GIT_VCS_CLONER);
-      peas_object_module_register_extension_type (module,
-                                                  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_g_file_add_ignored_pattern (".git");
 
-      g_type_ensure (GBP_TYPE_GIT_REMOTE_CALLBACKS);
-    }
+  peas_object_module_register_extension_type (module,
+                                              IDE_TYPE_BUFFER_ADDIN,
+                                              GBP_TYPE_GIT_BUFFER_ADDIN);
+  peas_object_module_register_extension_type (module,
+                                              IDE_TYPE_DEPENDENCY_UPDATER,
+                                              GBP_TYPE_GIT_DEPENDENCY_UPDATER);
+  peas_object_module_register_extension_type (module,
+                                              IDE_TYPE_PIPELINE_ADDIN,
+                                              GBP_TYPE_GIT_PIPELINE_ADDIN);
+  peas_object_module_register_extension_type (module,
+                                              IDE_TYPE_VCS_CLONER,
+                                              GBP_TYPE_GIT_VCS_CLONER);
+  peas_object_module_register_extension_type (module,
+                                              IDE_TYPE_VCS_CONFIG,
+                                              GBP_TYPE_GIT_VCS_CONFIG);
+  peas_object_module_register_extension_type (module,
+                                              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);
 }
diff --git a/src/plugins/git/git.plugin b/src/plugins/git/git.plugin
index e190366cc..8592d5293 100644
--- a/src/plugins/git/git.plugin
+++ b/src/plugins/git/git.plugin
@@ -1,7 +1,7 @@
 [Plugin]
 Authors=Christian Hergert <christian hergert me>
 Builtin=true
-Copyright=Copyright © 2015-2018 Christian Hergert
+Copyright=Copyright © 2015-2019 Christian Hergert
 Description=Support for the Git version control system
 Embedded=_gbp_git_register_types
 Module=git
diff --git a/src/plugins/git/meson.build b/src/plugins/git/meson.build
index 6bf39781f..d08ba06c5 100644
--- a/src/plugins/git/meson.build
+++ b/src/plugins/git/meson.build
@@ -3,23 +3,22 @@ if get_option('plugin_git')
 subdir('daemon')
 
 plugins_sources += files([
-  'git-plugin.c',
+  'daemon/line-cache.c',
   'gbp-git-branch.c',
   'gbp-git-buffer-addin.c',
   'gbp-git-buffer-change-monitor.c',
   'gbp-git-client.c',
   'gbp-git-dependency-updater.c',
-  'gbp-git-index-monitor.c',
   'gbp-git-pipeline-addin.c',
-  'gbp-git-remote-callbacks.c',
+  'gbp-git-progress.c',
   'gbp-git-submodule-stage.c',
   'gbp-git-tag.c',
-  'gbp-git-vcs.c',
   'gbp-git-vcs-cloner.c',
   'gbp-git-vcs-config.c',
   'gbp-git-vcs-initializer.c',
+  'gbp-git-vcs.c',
   'gbp-git-workbench-addin.c',
-  'line-cache.c',
+  'git-plugin.c',
 ])
 
 plugins_sources += [


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