Beispiel #1
0
static IdeVcsConfig *
ide_git_vcs_get_config (IdeVcs *vcs)
{
  g_return_val_if_fail (IDE_IS_GIT_VCS (vcs), NULL);

  return (IdeVcsConfig *)ide_git_vcs_config_new ();
}
Beispiel #2
0
static void
ide_git_vcs_reload_worker (GTask        *task,
                           gpointer      source_object,
                           gpointer      task_data,
                           GCancellable *cancellable)
{
  IdeGitVcs *self = source_object;
  g_autoptr(GgitRepository) repository1 = NULL;
  g_autoptr(GgitRepository) repository2 = NULL;
  GError *error = NULL;

  g_assert (G_IS_TASK (task));
  g_assert (IDE_IS_GIT_VCS (self));
  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));

  if (!(repository1 = ide_git_vcs_load (self, &error)) ||
      !(repository2 = ide_git_vcs_load (self, &error)))
    {
      g_task_return_error (task, error);
      return;
    }

  g_set_object (&self->repository, repository1);
  g_set_object (&self->change_monitor_repository, repository2);

  if (!ide_git_vcs_load_monitor (self, &error))
    {
      g_task_return_error (task, error);
      return;
    }

  g_task_return_boolean (task, TRUE);
}
Beispiel #3
0
static gboolean
ide_git_vcs_load_monitor (IdeGitVcs  *self,
                          GError    **error)
{
  gboolean ret = TRUE;

  g_assert (IDE_IS_GIT_VCS (self));

  if (self->monitor == NULL)
    {
      g_autoptr(GFile) location = NULL;
      g_autoptr(GFileMonitor) monitor = NULL;
      g_autoptr(GFile) heads_dir = NULL;
      GFileMonitorFlags flags = G_FILE_MONITOR_WATCH_MOUNTS;

      location = ggit_repository_get_location (self->repository);
      heads_dir = g_file_get_child (location, "refs/heads");
      monitor = g_file_monitor (heads_dir, flags, NULL, error);

      ret = !!monitor;

      if (monitor)
        {
          IDE_TRACE_MSG ("Git index monitor registered.");
          g_signal_connect_object (monitor,
                                   "changed",
                                   G_CALLBACK (ide_git_vcs__monitor_changed_cb),
                                   self,
                                   G_CONNECT_SWAPPED);
          self->monitor = g_object_ref (monitor);
        }
    }

  return ret;
}
Beispiel #4
0
static GgitRepository *
ide_git_vcs_load (IdeGitVcs  *self,
                  GError    **error)
{
  g_autoptr(GFile) location = NULL;
  GgitRepository *repository = NULL;
  IdeContext *context;
  GFile *project_file;

  g_assert (IDE_IS_GIT_VCS (self));

  context = ide_object_get_context (IDE_OBJECT (self));
  project_file = ide_context_get_project_file (context);

  if (!(location = ggit_repository_discover (project_file, error)))
    return NULL;

  if (!(repository = ggit_repository_open (location, error)))
    return NULL;

  /* Only set once, on load. Not thread-safe otherwise. */
  if (self->working_directory == NULL)
    self->working_directory = ggit_repository_get_workdir (repository);

  return repository;
}
Beispiel #5
0
static void
ide_git_vcs__monitor_changed_cb (IdeGitVcs         *self,
                                 GFile             *file,
                                 GFile             *other_file,
                                 GFileMonitorEvent  event_type,
                                 gpointer           user_data)
{
  g_autofree gchar *name = NULL;
  g_autofree gchar *other_name = NULL;

  IDE_ENTRY;

  g_assert (IDE_IS_GIT_VCS (self));
  g_assert (G_IS_FILE (file));
  g_assert (!other_file || G_IS_FILE (other_file));

  name = g_file_get_basename (file);

  if (other_file != NULL)
    other_name = g_file_get_basename (other_file);

  if (dzl_str_equal0 (name, "index") ||
      dzl_str_equal0 (other_name, "index"))
    {
      dzl_clear_source (&self->changed_timeout);
      self->changed_timeout = g_timeout_add_seconds (DEFAULT_CHANGED_TIMEOUT_SECS,
                                                     ide_git_vcs__changed_timeout_cb,
                                                     self);
    }

  IDE_EXIT;
}
Beispiel #6
0
/**
 * ide_git_vcs_get_repository:
 *
 * Retrieves the underlying #GgitRepository used by @vcs.
 *
 * Returns: (transfer none): A #GgitRepository.
 */
GgitRepository *
ide_git_vcs_get_repository (IdeGitVcs *self)
{
  g_return_val_if_fail (IDE_IS_GIT_VCS (self), NULL);

  return self->repository;
}
Beispiel #7
0
static gchar *
ide_git_vcs_get_branch_name (IdeVcs *vcs)
{
  IdeGitVcs *self = (IdeGitVcs *)vcs;
  GgitRef *ref;
  gchar *ret = NULL;

  g_assert (IDE_IS_GIT_VCS (self));

  g_mutex_lock (&self->repository_mutex);
  ref = ggit_repository_get_head (self->repository, NULL);
  g_mutex_unlock (&self->repository_mutex);

  if (ref != NULL)
    {
      ret = g_strdup (ggit_ref_get_shorthand (ref));
      g_object_unref (ref);
    }
  else
    {
      /* initial commit, no branch name yet */
      ret = g_strdup ("master");
    }

  return ret;
}
Beispiel #8
0
static void
ide_git_vcs_list_status_worker (GTask        *task,
                                gpointer      source_object,
                                gpointer      task_data,
                                GCancellable *cancellable)
{
  ListStatus *state = task_data;
  g_autoptr(GListStore) store = NULL;
  g_autoptr(GFile) workdir = NULL;
  g_autoptr(GgitRepository) repository = NULL;
  g_autoptr(GgitStatusOptions) options = NULL;
  g_autoptr(GString) pathspec = NULL;
  g_autoptr(GError) error = NULL;
  g_autofree gchar *relative = NULL;
  gchar *strv[] = { NULL, NULL };

  g_assert (G_IS_TASK (task));
  g_assert (IDE_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)))
    {
      g_task_return_error (task, g_steal_pointer (&error));
      return;
    }

  if (!(workdir = ggit_repository_get_workdir (repository)))
    {
      g_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,
                                            ide_git_vcs_list_status_cb,
                                            state,
                                            &error))
    g_task_return_error (task, g_steal_pointer (&error));
  else
    g_task_return_pointer (task, g_steal_pointer (&store), g_object_unref);
}
Beispiel #9
0
static GFile *
ide_git_vcs_get_working_directory (IdeVcs *vcs)
{
  IdeGitVcs *self = (IdeGitVcs *)vcs;

  g_return_val_if_fail (IDE_IS_GIT_VCS (self), NULL);

  return self->working_directory;
}
Beispiel #10
0
static void
ide_git_vcs_real_reloaded (IdeGitVcs      *self,
                           GgitRepository *repository)
{
  g_assert (IDE_IS_GIT_VCS (self));
  g_assert (GGIT_IS_REPOSITORY (repository));

  g_object_notify (G_OBJECT (self), "branch-name");
}
Beispiel #11
0
static GListModel *
ide_git_vcs_list_status_finish (IdeVcs        *vcs,
                                GAsyncResult  *result,
                                GError       **error)
{
  g_return_val_if_fail (IDE_IS_GIT_VCS (vcs), NULL);
  g_return_val_if_fail (G_IS_TASK (result), NULL);

  return g_task_propagate_pointer (G_TASK (result), error);
}
Beispiel #12
0
static gboolean
ide_git_vcs__changed_timeout_cb (gpointer user_data)
{
  IdeGitVcs *self = user_data;

  IDE_ENTRY;

  g_assert (IDE_IS_GIT_VCS (self));

  self->changed_timeout = 0;
  ide_git_vcs_reload_async (self, NULL, NULL, NULL);

  IDE_RETURN (G_SOURCE_REMOVE);
}
Beispiel #13
0
static void
handle_reload_from_changed_timeout (GObject      *object,
                                    GAsyncResult *result,
                                    gpointer      user_data)
{
  IdeGitVcs *self = (IdeGitVcs *)object;
  g_autoptr(GError) error = NULL;

  g_assert (IDE_IS_GIT_VCS (self));
  g_assert (G_IS_ASYNC_RESULT (result));

  /* Call finish() so that "changed" is emitted */
  if (!ide_git_vcs_reload_finish (self, result, &error))
    g_warning ("%s", error->message);
}
Beispiel #14
0
static GFile *
ide_git_vcs_get_working_directory (IdeVcs *vcs)
{
  IdeGitVcs *self = (IdeGitVcs *)vcs;

  g_return_val_if_fail (IDE_IS_GIT_VCS (self), NULL);

  /* Note: This function is expected to be thread-safe for
   *       those holding a reference to @vcs. So
   *       @working_directory cannot be changed after creation
   *       and must be valid for the lifetime of @vcs.
   */

  return self->working_directory;
}
Beispiel #15
0
static void
ide_git_vcs_init_async__reload_cb (GObject      *object,
                                   GAsyncResult *result,
                                   gpointer      user_data)
{
  IdeGitVcs *self = (IdeGitVcs *)object;
  g_autoptr(GTask) task = user_data;
  g_autoptr(GError) error = NULL;

  g_assert (G_IS_TASK (task));
  g_assert (IDE_IS_GIT_VCS (self));

  if (!ide_git_vcs_reload_finish (self, result, &error))
    g_task_return_error (task, g_steal_pointer (&error));
  else
    g_task_return_boolean (task, TRUE);
}
Beispiel #16
0
static IdeBufferChangeMonitor *
ide_git_vcs_get_buffer_change_monitor (IdeVcs    *vcs,
                                       IdeBuffer *buffer)
{
  IdeGitVcs *self = (IdeGitVcs *)vcs;
  IdeContext *context;

  g_return_val_if_fail (IDE_IS_GIT_VCS (vcs), NULL);

  context = ide_object_get_context (IDE_OBJECT (vcs));

  return g_object_new (IDE_TYPE_GIT_BUFFER_CHANGE_MONITOR,
                       "buffer", buffer,
                       "context", context,
                       "repository", self->change_monitor_repository,
                       NULL);
}
Beispiel #17
0
static void
ide_git_vcs_init_async (GAsyncInitable      *initable,
                        int                  io_priority,
                        GCancellable        *cancellable,
                        GAsyncReadyCallback  callback,
                        gpointer             user_data)
{
  IdeGitVcs *self = (IdeGitVcs *)initable;
  g_autoptr(GTask) task = NULL;

  g_return_if_fail (IDE_IS_GIT_VCS (self));

  task = g_task_new (self, cancellable, callback, user_data);
  ide_git_vcs_reload_async (self,
                            cancellable,
                            ide_git_vcs_init_async__reload_cb,
                            g_object_ref (task));
}
Beispiel #18
0
static gboolean
ide_git_vcs_reload_finish (IdeGitVcs     *self,
                           GAsyncResult  *result,
                           GError       **error)
{
  GTask *task = (GTask *)result;
  gboolean ret;

  IDE_ENTRY;

  g_return_val_if_fail (IDE_IS_GIT_VCS (self), FALSE);

  self->reloading = FALSE;
  g_signal_emit (self, gSignals [RELOADED], 0, self->change_monitor_repository);
  ret = g_task_propagate_boolean (task, error);

  IDE_RETURN (ret);
}
Beispiel #19
0
static void
ide_git_vcs_reload_async (IdeGitVcs           *self,
                          GCancellable        *cancellable,
                          GAsyncReadyCallback  callback,
                          gpointer             user_data)
{
  g_autoptr(GTask) task = NULL;

  IDE_ENTRY;

  g_assert (IDE_IS_GIT_VCS (self));
  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));

  task = g_task_new (self, cancellable, callback, user_data);
  g_task_run_in_thread (task, ide_git_vcs_reload_worker);

  IDE_EXIT;
}
static void
ide_git_buffer_change_monitor__vcs_reloaded_cb (IdeGitBufferChangeMonitor *self,
                                                GgitRepository            *new_repository,
                                                IdeGitVcs                 *vcs)
{
  IDE_ENTRY;

  g_assert (IDE_IS_GIT_BUFFER_CHANGE_MONITOR (self));
  g_assert (IDE_IS_GIT_VCS (vcs));

  g_set_object (&self->repository, new_repository);

  /* force reload of the git object on next calculation */
  g_clear_object (&self->cached_blob);

  ide_git_buffer_change_monitor_recalculate (self);

  IDE_EXIT;
}
Beispiel #21
0
static void
ide_git_vcs_list_status_async (IdeVcs              *vcs,
                               GFile               *directory_or_file,
                               gboolean             include_descendants,
                               gint                 io_priority,
                               GCancellable        *cancellable,
                               GAsyncReadyCallback  callback,
                               gpointer             user_data)
{
  IdeGitVcs *self = (IdeGitVcs *)vcs;
  g_autoptr(GTask) task = NULL;
  ListStatus *state;

  IDE_ENTRY;

  g_return_if_fail (IDE_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));

  g_mutex_lock (&self->repository_mutex);
  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;
  g_mutex_unlock (&self->repository_mutex);

  task = g_task_new (self, cancellable, callback, user_data);
  g_task_set_source_tag (task, ide_git_vcs_list_status_async);
  g_task_set_priority (task, io_priority);
  g_task_set_return_on_cancel (task, TRUE);
  g_task_set_task_data (task, state, list_status_free);

  if (state->repository_location == NULL)
    g_task_return_new_error (task,
                             G_IO_ERROR,
                             G_IO_ERROR_FAILED,
                             "No repository loaded");
  else
    g_task_run_in_thread (task, ide_git_vcs_list_status_worker);

  IDE_EXIT;
}
Beispiel #22
0
static void
ide_git_vcs__monitor_changed_cb (IdeGitVcs         *self,
                                 GFile             *file,
                                 GFile             *other_file,
                                 GFileMonitorEvent  event_type,
                                 gpointer           user_data)
{
  IDE_ENTRY;

  g_assert (IDE_IS_GIT_VCS (self));

  if (self->changed_timeout != 0)
    g_source_remove (self->changed_timeout);

  self->changed_timeout = g_timeout_add_seconds (DEFAULT_CHANGED_TIMEOUT_SECS,
                                                 ide_git_vcs__changed_timeout_cb,
                                                 self);

  IDE_EXIT;
}
Beispiel #23
0
static gboolean
ide_git_vcs_load_monitor_locked (IdeGitVcs  *self,
                                 GError    **error)
{
  gboolean ret = TRUE;

  g_assert (IDE_IS_GIT_VCS (self));

  if (self->monitor == NULL)
    {
      g_autoptr(GFile) location = NULL;
      g_autoptr(GFileMonitor) monitor = NULL;
      GError *local_error = NULL;

      location = ggit_repository_get_location (self->repository);

      monitor = g_file_monitor_directory (location,
                                          G_FILE_MONITOR_NONE,
                                          NULL,
                                          &local_error);

      if (monitor == NULL)
        {
          g_warning ("Failed to establish git monitor: %s", local_error->message);
          g_propagate_error (error, local_error);
          ret = FALSE;
        }
      else
        {
          IDE_TRACE_MSG ("Git index monitor registered.");
          g_signal_connect_object (monitor,
                                   "changed",
                                   G_CALLBACK (ide_git_vcs__monitor_changed_cb),
                                   self,
                                   G_CONNECT_SWAPPED);
          self->monitor = g_steal_pointer (&monitor);
        }
    }

  return ret;
}
Beispiel #24
0
static gboolean
ide_git_vcs_is_ignored (IdeVcs  *vcs,
                        GFile   *file,
                        GError **error)
{
  g_autofree gchar *name = NULL;
  IdeGitVcs *self = (IdeGitVcs *)vcs;
  gboolean ret = FALSE;

  g_assert (IDE_IS_GIT_VCS (self));
  g_assert (G_IS_FILE (file));

  name = g_file_get_relative_path (self->working_directory, file);
  if (g_strcmp0 (name, ".git") == 0)
    return TRUE;

  if (name != NULL)
    return ggit_repository_path_is_ignored (self->repository, name, error);

  return ret;
}
Beispiel #25
0
static GgitRepository *
ide_git_vcs_load (IdeGitVcs  *self,
                  GError    **error)
{
  g_autofree gchar *uri = NULL;
  g_autoptr(GFile) location = NULL;
  GgitRepository *repository = NULL;
  IdeContext *context;
  GFile *project_file;

  g_assert (IDE_IS_GIT_VCS (self));
  g_assert (error != NULL);

  context = ide_object_get_context (IDE_OBJECT (self));
  project_file = ide_context_get_project_file (context);

  if (!(location = ide_git_vcs_discover (self, project_file, error)))
    {
      if (g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED))
        return NULL;

      g_clear_error (error);

      /* Fallback to libgit2(-glib) discovery */
      if (!(location = ggit_repository_discover (project_file, error)))
        return NULL;
    }

  uri = g_file_get_uri (location);
  g_debug ("Discovered .git location at “%s”", uri);

  if (!(repository = ggit_repository_open (location, error)))
    return NULL;

  /* Note: Only set this once on load, otherwise not thread-safe. */
  if (self->working_directory == NULL)
    self->working_directory = ggit_repository_get_workdir (repository);

  return repository;
}
Beispiel #26
0
static void
ide_git_vcs_reload_worker (GTask        *task,
                           gpointer      source_object,
                           gpointer      task_data,
                           GCancellable *cancellable)
{
  IdeGitVcs *self = source_object;
  g_autoptr(GgitRepository) repository1 = NULL;
  g_autoptr(GgitRepository) repository2 = NULL;
  g_autoptr(GError) error = NULL;

  IDE_ENTRY;

  g_assert (G_IS_TASK (task));
  g_assert (IDE_IS_GIT_VCS (self));
  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));

  if (!(repository1 = ide_git_vcs_load (self, &error)) ||
      !(repository2 = ide_git_vcs_load (self, &error)))
    {
      g_debug ("%s", error->message);
      g_task_return_error (task, g_steal_pointer (&error));
      IDE_EXIT;
    }

  g_mutex_lock (&self->repository_mutex);

  g_set_object (&self->repository, repository1);
  g_set_object (&self->change_monitor_repository, repository2);

  if (!ide_git_vcs_load_monitor_locked (self, &error))
    g_task_return_error (task, g_steal_pointer (&error));
  else
    g_task_return_boolean (task, TRUE);

  g_mutex_unlock (&self->repository_mutex);

  IDE_EXIT;
}
Beispiel #27
0
static gboolean
ide_git_vcs_is_ignored (IdeVcs  *vcs,
                        GFile   *file,
                        GError **error)
{
  g_autofree gchar *name = NULL;
  IdeGitVcs *self = (IdeGitVcs *)vcs;
  gboolean ret = FALSE;

  g_assert (IDE_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->working_directory is not changed after creation, so safe
   * to access it from a thread.
   */
  name = g_file_get_relative_path (self->working_directory, file);
  if (g_strcmp0 (name, ".git") == 0)
    return TRUE;

  /*
   * If we have a valid name to work with, we want to query the
   * repository. But this could be called from a thread, so ensure
   * we are the only thread accessing self->repository right now.
   */
  if (name != NULL)
    {
      g_mutex_lock (&self->repository_mutex);
      ret = ggit_repository_path_is_ignored (self->repository, name, error);
      g_mutex_unlock (&self->repository_mutex);
    }

  return ret;
}
Beispiel #28
0
/*
 * This is like ggit_repository_discover() but seems to work inside
 * of the flatpak runtime. Something is preventing git_repository_discover()
 * in libgit2 from working correctly, possibly the mount setup. Either
 * way, until it's fixed we can use this.
 */
static GFile *
ide_git_vcs_discover (IdeGitVcs  *self,
                      GFile      *file,
                      GError    **error)
{
  g_autofree gchar *name = NULL;
  g_autoptr(GFile) parent = NULL;
  g_autoptr(GFile) git = NULL;
  g_autoptr(GFile) child = NULL;
  GFile *ret;

  g_assert (IDE_IS_GIT_VCS (self));
  g_assert (G_IS_FILE (file));

  /*
   * TODO: Switch to using the new discover_full().
   */

  if (!g_file_is_native (file))
    {
      g_set_error (error,
                   G_IO_ERROR,
                   G_IO_ERROR_NOT_SUPPORTED,
                   "Only native file systems are supported for git.");
      return NULL;
    }

  name = g_file_get_basename (file);

  if (g_strcmp0 (name, ".git") == 0)
    return g_object_ref (file);

  /*
   * Work around for in-tree tests which we do not
   * want to use the git backend.
   *
   * TODO: Allow options during context creation.
   */
  child = g_file_get_child (file, ".you-dont-git-me");

  if (g_file_query_exists (child, NULL))
    {
      g_set_error (error,
                   G_IO_ERROR,
                   G_IO_ERROR_NOT_SUPPORTED,
                   "The project has blocked use of the git plugin");
      return NULL;
    }

  g_clear_object (&child);
  child = g_file_get_child (file, ".git");

  if (g_file_query_exists (child, NULL))
    return g_object_ref (child);

  parent = g_file_get_parent (file);

  if (parent == NULL || g_file_equal (parent, file))
    {
      g_set_error (error,
                   G_IO_ERROR,
                   G_IO_ERROR_NOT_FOUND,
                   "Failed to discover git directory");
      return NULL;
    }

  ret = ide_git_vcs_discover (self, parent, error);

  return ret;
}