~ubuntu-branches/debian/stretch/gnome-builder/stretch

« back to all changes in this revision

Viewing changes to libide/git/ide-git-vcs.c

  • Committer: Package Import Robot
  • Author(s): Andreas Henriksson
  • Date: 2015-10-11 12:38:45 UTC
  • Revision ID: package-import@ubuntu.com-20151011123845-a0hvkz01se0p1p5a
Tags: upstream-3.16.3
ImportĀ upstreamĀ versionĀ 3.16.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* ide-git-vcs.c
 
2
 *
 
3
 * Copyright (C) 2015 Christian Hergert <christian@hergert.me>
 
4
 *
 
5
 * This program is free software: you can redistribute it and/or modify
 
6
 * it under the terms of the GNU General Public License as published by
 
7
 * the Free Software Foundation, either version 3 of the License, or
 
8
 * (at your option) any later version.
 
9
 *
 
10
 * This program is distributed in the hope that it will be useful,
 
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
 * GNU General Public License for more details.
 
14
 *
 
15
 * You should have received a copy of the GNU General Public License
 
16
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
17
 */
 
18
 
 
19
#define G_LOG_DOMAIN "ide-git-vcs"
 
20
 
 
21
#include <git2.h>
 
22
#include <glib/gi18n.h>
 
23
#include <libgit2-glib/ggit.h>
 
24
 
 
25
#include "ide-async-helper.h"
 
26
#include "ide-context.h"
 
27
#include "ide-debug.h"
 
28
#include "ide-git-buffer-change-monitor.h"
 
29
#include "ide-git-vcs.h"
 
30
#include "ide-project.h"
 
31
#include "ide-project-file.h"
 
32
#include "ide-project-files.h"
 
33
 
 
34
#define DEFAULT_CHANGED_TIMEOUT_SECS 1
 
35
 
 
36
struct _IdeGitVcs
 
37
{
 
38
  IdeVcs parent_instance;
 
39
 
 
40
  GgitRepository *repository;
 
41
  GgitRepository *change_monitor_repository;
 
42
  GFile          *working_directory;
 
43
  GFileMonitor   *monitor;
 
44
 
 
45
  guint           changed_timeout;
 
46
 
 
47
  guint           reloading : 1;
 
48
  guint           loaded_files : 1;
 
49
};
 
50
 
 
51
static void     g_async_initable_init_interface (GAsyncInitableIface  *iface);
 
52
static void     ide_git_vcs_reload_async        (IdeGitVcs            *self,
 
53
                                                 GCancellable         *cancellable,
 
54
                                                 GAsyncReadyCallback   callback,
 
55
                                                 gpointer              user_data);
 
56
static gboolean ide_git_vcs_reload_finish       (IdeGitVcs            *self,
 
57
                                                 GAsyncResult         *result,
 
58
                                                 GError              **error);
 
59
 
 
60
G_DEFINE_TYPE_EXTENDED (IdeGitVcs, ide_git_vcs, IDE_TYPE_VCS, 0,
 
61
                        G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE,
 
62
                                               g_async_initable_init_interface))
 
63
 
 
64
enum {
 
65
  PROP_0,
 
66
  PROP_REPOSITORY,
 
67
  LAST_PROP
 
68
};
 
69
 
 
70
enum {
 
71
  RELOADED,
 
72
  LAST_SIGNAL
 
73
};
 
74
 
 
75
static GParamSpec *gParamSpecs [LAST_PROP];
 
76
static guint gSignals [LAST_SIGNAL];
 
77
 
 
78
/**
 
79
 * ide_git_vcs_get_repository:
 
80
 *
 
81
 * Retrieves the underlying #GgitRepository used by @vcs.
 
82
 *
 
83
 * Returns: (transfer none): A #GgitRepository.
 
84
 */
 
85
GgitRepository *
 
86
ide_git_vcs_get_repository (IdeGitVcs *self)
 
87
{
 
88
  g_return_val_if_fail (IDE_IS_GIT_VCS (self), NULL);
 
89
 
 
90
  return self->repository;
 
91
}
 
92
 
 
93
static GFile *
 
94
ide_git_vcs_get_working_directory (IdeVcs *vcs)
 
95
{
 
96
  IdeGitVcs *self = (IdeGitVcs *)vcs;
 
97
 
 
98
  g_return_val_if_fail (IDE_IS_GIT_VCS (self), NULL);
 
99
 
 
100
  return self->working_directory;
 
101
}
 
102
 
 
103
static IdeBufferChangeMonitor *
 
104
ide_git_vcs_get_buffer_change_monitor (IdeVcs    *vcs,
 
105
                                       IdeBuffer *buffer)
 
106
{
 
107
  IdeGitVcs *self = (IdeGitVcs *)vcs;
 
108
  IdeContext *context;
 
109
 
 
110
  g_return_val_if_fail (IDE_IS_GIT_VCS (vcs), NULL);
 
111
 
 
112
  context = ide_object_get_context (IDE_OBJECT (vcs));
 
113
 
 
114
  return g_object_new (IDE_TYPE_GIT_BUFFER_CHANGE_MONITOR,
 
115
                       "buffer", buffer,
 
116
                       "context", context,
 
117
                       "repository", self->change_monitor_repository,
 
118
                       NULL);
 
119
}
 
120
 
 
121
static void
 
122
ide_git_vcs_load_repository_worker (GTask        *task,
 
123
                                    gpointer      source_object,
 
124
                                    gpointer      task_data,
 
125
                                    GCancellable *cancellable)
 
126
{
 
127
  GFile *project_file = task_data;
 
128
  g_autoptr(GFile) location = NULL;
 
129
  GgitRepository *repository = NULL;
 
130
  GError *error = NULL;
 
131
 
 
132
  g_assert (G_IS_TASK (task));
 
133
  g_assert (G_IS_FILE (project_file));
 
134
 
 
135
  location = ggit_repository_discover (project_file, &error);
 
136
 
 
137
  if (!location)
 
138
    {
 
139
      g_task_return_error (task, error);
 
140
      return;
 
141
    }
 
142
 
 
143
  repository = ggit_repository_open (location, &error);
 
144
 
 
145
  if (!repository)
 
146
    {
 
147
      g_task_return_error (task, error);
 
148
      return;
 
149
    }
 
150
 
 
151
  g_task_return_pointer (task, repository, g_object_unref);
 
152
}
 
153
 
 
154
static void
 
155
ide_git_vcs_load_repository_async (IdeGitVcs           *self,
 
156
                                   GCancellable        *cancellable,
 
157
                                   GAsyncReadyCallback  callback,
 
158
                                   gpointer             user_data)
 
159
{
 
160
  g_autoptr(GTask) task = NULL;
 
161
  IdeContext *context;
 
162
  GFile *project_file;
 
163
 
 
164
  g_assert (IDE_IS_GIT_VCS (self));
 
165
  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
 
166
 
 
167
  context = ide_object_get_context (IDE_OBJECT (self));
 
168
  project_file = ide_context_get_project_file (context);
 
169
 
 
170
  task = g_task_new (self, cancellable, callback, user_data);
 
171
  g_task_set_task_data (task, g_object_ref (project_file), g_object_unref);
 
172
  g_task_run_in_thread (task, ide_git_vcs_load_repository_worker);
 
173
}
 
174
 
 
175
static GgitRepository *
 
176
ide_git_vcs_load_repository_finish (IdeGitVcs     *self,
 
177
                                    GAsyncResult  *result,
 
178
                                    GError       **error)
 
179
{
 
180
  GTask *task = (GTask *)result;
 
181
  GgitRepository *ret;
 
182
 
 
183
  g_assert (IDE_IS_GIT_VCS (self));
 
184
 
 
185
  ret = g_task_propagate_pointer (task, error);
 
186
 
 
187
  if (ret)
 
188
    {
 
189
      GFile *working_directory;
 
190
 
 
191
      working_directory = ggit_repository_get_workdir (ret);
 
192
      g_set_object (&self->working_directory, working_directory);
 
193
    }
 
194
 
 
195
  return ret;
 
196
}
 
197
 
 
198
static void
 
199
ide_git_vcs_reload_index_add_path (IdeGitVcs   *self,
 
200
                                   GHashTable  *cache,
 
201
                                   const gchar *path,
 
202
                                   const gchar *workdir,
 
203
                                   gboolean     is_directory)
 
204
{
 
205
  IdeProjectItem *parent;
 
206
  IdeProjectItem *item;
 
207
  IdeContext *context;
 
208
  GFileInfo *file_info = NULL;
 
209
  GFile *file = NULL;
 
210
  g_autofree gchar *fullpath = NULL;
 
211
  gchar *dir;
 
212
  gchar *name;
 
213
 
 
214
  g_return_if_fail (IDE_IS_GIT_VCS (self));
 
215
  g_return_if_fail (cache);
 
216
  g_return_if_fail (path);
 
217
 
 
218
  context = ide_object_get_context (IDE_OBJECT (self));
 
219
 
 
220
  dir = g_path_get_dirname (path);
 
221
  name = g_path_get_basename (path);
 
222
 
 
223
  parent = g_hash_table_lookup (cache, dir);
 
224
 
 
225
  if (!parent)
 
226
    {
 
227
      ide_git_vcs_reload_index_add_path (self, cache, dir, workdir, TRUE);
 
228
      parent = g_hash_table_lookup (cache, dir);
 
229
    }
 
230
 
 
231
  g_assert (IDE_IS_PROJECT_ITEM (parent));
 
232
 
 
233
  file_info = g_file_info_new ();
 
234
  g_file_info_set_name (file_info, name);
 
235
  g_file_info_set_display_name (file_info, name);
 
236
 
 
237
  /*
 
238
   * TODO: We can probably extract some additional information from the
 
239
   *       index such as symbolic link, etc.
 
240
   */
 
241
  if (is_directory)
 
242
    g_file_info_set_file_type (file_info, G_FILE_TYPE_DIRECTORY);
 
243
 
 
244
  fullpath = g_build_filename (workdir, path, NULL);
 
245
  file = g_file_new_for_path (fullpath);
 
246
 
 
247
  item = g_object_new (IDE_TYPE_PROJECT_FILE,
 
248
                       "context", context,
 
249
                       "file", file,
 
250
                       "file-info", file_info,
 
251
                       "parent", parent,
 
252
                       "path", path,
 
253
                       NULL);
 
254
  ide_project_item_append (parent, item);
 
255
 
 
256
  g_hash_table_insert (cache, g_strdup (path), g_object_ref (item));
 
257
 
 
258
  g_clear_object (&file);
 
259
  g_clear_object (&file_info);
 
260
  g_clear_object (&item);
 
261
  g_clear_pointer (&dir, g_free);
 
262
  g_clear_pointer (&name, g_free);
 
263
}
 
264
 
 
265
static void
 
266
ide_git_vcs_build_tree_worker (GTask        *task,
 
267
                               gpointer      source_object,
 
268
                               gpointer      task_data,
 
269
                               GCancellable *cancellable)
 
270
{
 
271
  IdeGitVcs *self = source_object;
 
272
  GgitIndexEntries *entries = NULL;
 
273
  GgitRepository *repository = task_data;
 
274
  IdeProjectItem *root;
 
275
  IdeProjectItem *files = NULL;
 
276
  IdeContext *context;
 
277
  IdeProject *project;
 
278
  GgitIndex *index = NULL;
 
279
  GHashTable *cache = NULL;
 
280
  g_autofree gchar *workdir = NULL;
 
281
  g_autoptr(GFile) workdir_file = NULL;
 
282
  GError *error = NULL;
 
283
  guint count;
 
284
  guint i;
 
285
 
 
286
  g_assert (G_IS_TASK (task));
 
287
  g_assert (IDE_IS_GIT_VCS (self));
 
288
  g_assert (GGIT_IS_REPOSITORY (repository));
 
289
  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
 
290
 
 
291
  index = ggit_repository_get_index (repository, &error);
 
292
  if (!index)
 
293
    goto cleanup;
 
294
 
 
295
  entries = ggit_index_get_entries (index);
 
296
  if (!entries)
 
297
    goto cleanup;
 
298
 
 
299
  count = ggit_index_entries_size (entries);
 
300
  cache = g_hash_table_new_full (g_str_hash, g_str_equal,
 
301
                                 g_free, g_object_unref);
 
302
 
 
303
  context = ide_object_get_context (IDE_OBJECT (self));
 
304
  project = ide_context_get_project (context);
 
305
 
 
306
  ide_project_reader_lock (project);
 
307
  root = ide_project_get_root (project);
 
308
  files = g_object_new (IDE_TYPE_PROJECT_FILES,
 
309
                        "context", context,
 
310
                        "parent", root,
 
311
                        NULL);
 
312
  ide_project_reader_unlock (project);
 
313
 
 
314
  g_hash_table_insert (cache, g_strdup ("."), g_object_ref (files));
 
315
 
 
316
  workdir_file = ggit_repository_get_workdir (repository);
 
317
  workdir = g_file_get_path (workdir_file);
 
318
 
 
319
  for (i = 0; i < count; i++)
 
320
    {
 
321
      GgitIndexEntry *entry;
 
322
      const gchar *path;
 
323
 
 
324
      entry = ggit_index_entries_get_by_index (entries, i);
 
325
      path = ggit_index_entry_get_path (entry);
 
326
      ide_git_vcs_reload_index_add_path (self, cache, path, workdir, FALSE);
 
327
      ggit_index_entry_unref (entry);
 
328
    }
 
329
 
 
330
cleanup:
 
331
  if (error)
 
332
    g_task_return_error (task, error);
 
333
  else
 
334
    g_task_return_pointer (task, g_object_ref (files), g_object_unref);
 
335
 
 
336
  g_clear_pointer (&cache, g_hash_table_unref);
 
337
  g_clear_pointer (&entries, ggit_index_entries_unref);
 
338
  g_clear_object (&files);
 
339
  g_clear_object (&index);
 
340
}
 
341
 
 
342
static void
 
343
ide_git_vcs_build_tree_async (IdeGitVcs           *self,
 
344
                              GgitRepository      *repository,
 
345
                              GCancellable        *cancellable,
 
346
                              GAsyncReadyCallback  callback,
 
347
                              gpointer             user_data)
 
348
{
 
349
  g_autoptr(GTask) task = NULL;
 
350
 
 
351
  g_assert (IDE_IS_GIT_VCS (self));
 
352
  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
 
353
 
 
354
  task = g_task_new (self, cancellable, callback, user_data);
 
355
  g_task_set_task_data (task, g_object_ref (repository), g_object_unref);
 
356
  g_task_run_in_thread (task, ide_git_vcs_build_tree_worker);
 
357
}
 
358
 
 
359
static IdeProjectFiles *
 
360
ide_git_vcs_build_tree_finish (IdeGitVcs     *self,
 
361
                               GAsyncResult  *result,
 
362
                               GError       **error)
 
363
{
 
364
  GTask *task = (GTask *)result;
 
365
 
 
366
  g_return_val_if_fail (IDE_IS_GIT_VCS (self), NULL);
 
367
 
 
368
  return g_task_propagate_pointer (task, error);
 
369
}
 
370
 
 
371
static void
 
372
ide_git_vcs__reload_cb (GObject      *object,
 
373
                        GAsyncResult *result,
 
374
                        gpointer      user_data)
 
375
{
 
376
  IdeGitVcs *self = (IdeGitVcs *)object;
 
377
  g_autoptr(GError) error = NULL;
 
378
 
 
379
  g_assert (IDE_IS_GIT_VCS (self));
 
380
 
 
381
  if (!ide_git_vcs_reload_finish (self, result, &error))
 
382
    g_message ("%s", error->message);
 
383
}
 
384
 
 
385
 
 
386
static gboolean
 
387
ide_git_vcs__changed_timeout_cb (gpointer user_data)
 
388
{
 
389
  IdeGitVcs *self = user_data;
 
390
 
 
391
  IDE_ENTRY;
 
392
 
 
393
  g_assert (IDE_IS_GIT_VCS (self));
 
394
 
 
395
  self->changed_timeout = 0;
 
396
  ide_git_vcs_reload_async (self, NULL, ide_git_vcs__reload_cb, NULL);
 
397
 
 
398
  IDE_RETURN (G_SOURCE_REMOVE);
 
399
}
 
400
 
 
401
static void
 
402
ide_git_vcs__monitor_changed_cb (IdeGitVcs         *self,
 
403
                                 GFile             *file,
 
404
                                 GFile             *other_file,
 
405
                                 GFileMonitorEvent  event_type,
 
406
                                 gpointer           user_data)
 
407
{
 
408
  IDE_ENTRY;
 
409
 
 
410
  g_assert (IDE_IS_GIT_VCS (self));
 
411
 
 
412
  if (self->changed_timeout != 0)
 
413
    g_source_remove (self->changed_timeout);
 
414
 
 
415
  self->changed_timeout = g_timeout_add_seconds (DEFAULT_CHANGED_TIMEOUT_SECS,
 
416
                                                 ide_git_vcs__changed_timeout_cb,
 
417
                                                 self);
 
418
 
 
419
  IDE_EXIT;
 
420
}
 
421
 
 
422
static gboolean
 
423
ide_git_vcs_load_monitor (IdeGitVcs  *self,
 
424
                          GError    **error)
 
425
{
 
426
  gboolean ret = TRUE;
 
427
 
 
428
  g_assert (IDE_IS_GIT_VCS (self));
 
429
 
 
430
  if (self->monitor == NULL)
 
431
    {
 
432
      g_autoptr(GFile) location = NULL;
 
433
      g_autoptr(GFileMonitor) monitor = NULL;
 
434
      g_autoptr(GFile) heads_dir = NULL;
 
435
      GFileMonitorFlags flags = G_FILE_MONITOR_WATCH_MOUNTS;
 
436
 
 
437
      location = ggit_repository_get_location (self->repository);
 
438
      heads_dir = g_file_get_child (location, "refs/heads");
 
439
      monitor = g_file_monitor (heads_dir, flags, NULL, error);
 
440
 
 
441
      ret = !!monitor;
 
442
 
 
443
      if (monitor)
 
444
        {
 
445
          IDE_TRACE_MSG ("Git index monitor registered.");
 
446
          g_signal_connect_object (monitor,
 
447
                                   "changed",
 
448
                                   G_CALLBACK (ide_git_vcs__monitor_changed_cb),
 
449
                                   self,
 
450
                                   G_CONNECT_SWAPPED);
 
451
          self->monitor = g_object_ref (monitor);
 
452
        }
 
453
    }
 
454
 
 
455
  return ret;
 
456
}
 
457
 
 
458
static void
 
459
ide_git_vcs_reload__load_repository_cb3 (GObject      *object,
 
460
                                         GAsyncResult *result,
 
461
                                         gpointer      user_data)
 
462
{
 
463
  IdeGitVcs *self = (IdeGitVcs *)object;
 
464
  g_autoptr(GTask) task = user_data;
 
465
  GgitRepository *repository;
 
466
  GError *error = NULL;
 
467
 
 
468
  g_assert (IDE_IS_GIT_VCS (self));
 
469
  g_assert (G_IS_ASYNC_RESULT (result));
 
470
 
 
471
  repository = ide_git_vcs_load_repository_finish (self, result, &error);
 
472
 
 
473
  if (!repository)
 
474
    {
 
475
      g_task_return_error (task, error);
 
476
      return;
 
477
    }
 
478
 
 
479
  g_set_object (&self->change_monitor_repository, repository);
 
480
 
 
481
  /*
 
482
   * Now finally, load the change monitor so that we can detect future changes.
 
483
   */
 
484
  if (!ide_git_vcs_load_monitor (self, &error))
 
485
    {
 
486
      g_task_return_error (task, error);
 
487
      return;
 
488
    }
 
489
 
 
490
  g_task_return_boolean (task, TRUE);
 
491
}
 
492
 
 
493
static void
 
494
ide_git_vcs_reload__build_tree_cb (GObject      *object,
 
495
                                   GAsyncResult *result,
 
496
                                   gpointer      user_data)
 
497
{
 
498
  IdeGitVcs *self = (IdeGitVcs *)object;
 
499
  g_autoptr(GTask) task = user_data;
 
500
  g_autoptr(IdeProjectFiles) files = NULL;
 
501
  GError *error = NULL;
 
502
 
 
503
  g_assert (IDE_IS_GIT_VCS (self));
 
504
  g_assert (G_IS_TASK (task));
 
505
 
 
506
  files = ide_git_vcs_build_tree_finish (self, result, &error);
 
507
 
 
508
  if (!files)
 
509
    {
 
510
      g_task_return_error (task, error);
 
511
      return;
 
512
    }
 
513
 
 
514
  /*
 
515
   * XXX:
 
516
   *
 
517
   * This is a hack to only load the project files the first time. We need to do this for real
 
518
   * in the project tree to make appropriate events for tree changes.
 
519
   */
 
520
  if (!self->loaded_files)
 
521
    {
 
522
#if 0
 
523
      IdeContext *context;
 
524
      IdeProject *project;
 
525
      IdeProjectItem *root;
 
526
 
 
527
      context = ide_object_get_context (IDE_OBJECT (self));
 
528
      project = ide_context_get_project (context);
 
529
 
 
530
      if (FALSE)
 
531
        {
 
532
          ide_project_writer_lock (project);
 
533
          root = ide_project_get_root (project);
 
534
          /* TODO: Replace existing item!!! */
 
535
          ide_project_item_append (root, IDE_PROJECT_ITEM (files));
 
536
          ide_project_writer_unlock (project);
 
537
        }
 
538
#endif
 
539
      self->loaded_files = TRUE;
 
540
    }
 
541
 
 
542
  /*
 
543
   * Load the repository a third time for use by the threaded change monitors generating diffs.
 
544
   * I know it seems like a lot of loading, but it is a lot better than the N_FILES repositories
 
545
   * we had open previously.
 
546
   */
 
547
  ide_git_vcs_load_repository_async (self,
 
548
                                     g_task_get_cancellable (task),
 
549
                                     ide_git_vcs_reload__load_repository_cb3,
 
550
                                     g_object_ref (task));
 
551
}
 
552
 
 
553
static void
 
554
ide_git_vcs_reload__load_repository_cb2 (GObject      *object,
 
555
                                         GAsyncResult *result,
 
556
                                         gpointer      user_data)
 
557
{
 
558
  IdeGitVcs *self = (IdeGitVcs *)object;
 
559
  g_autoptr(GTask) task = user_data;
 
560
  GgitRepository *repository;
 
561
  GError *error = NULL;
 
562
 
 
563
  g_assert (IDE_IS_GIT_VCS (self));
 
564
  g_assert (G_IS_ASYNC_RESULT (result));
 
565
 
 
566
  repository = ide_git_vcs_load_repository_finish (self, result, &error);
 
567
 
 
568
  if (!repository)
 
569
    {
 
570
      g_task_return_error (task, error);
 
571
      return;
 
572
    }
 
573
 
 
574
  /*
 
575
   * Now go load the files for the project tree.
 
576
   */
 
577
  ide_git_vcs_build_tree_async (self,
 
578
                                repository,
 
579
                                g_task_get_cancellable (task),
 
580
                                ide_git_vcs_reload__build_tree_cb,
 
581
                                g_object_ref (task));
 
582
 
 
583
  g_clear_object (&repository);
 
584
}
 
585
 
 
586
static void
 
587
ide_git_vcs_reload__load_repository_cb (GObject      *object,
 
588
                                        GAsyncResult *result,
 
589
                                        gpointer      user_data)
 
590
{
 
591
  IdeGitVcs *self = (IdeGitVcs *)object;
 
592
  g_autoptr(GTask) task = user_data;
 
593
  GgitRepository *repository;
 
594
  GError *error = NULL;
 
595
 
 
596
  g_assert (IDE_IS_GIT_VCS (self));
 
597
  g_assert (G_IS_ASYNC_RESULT (result));
 
598
 
 
599
  repository = ide_git_vcs_load_repository_finish (self, result, &error);
 
600
 
 
601
  if (!repository)
 
602
    {
 
603
      g_task_return_error (task, error);
 
604
      return;
 
605
    }
 
606
 
 
607
  g_set_object (&self->repository, repository);
 
608
 
 
609
  /*
 
610
   * Now load the repository again for use by the threaded index builder.
 
611
   */
 
612
  ide_git_vcs_load_repository_async (self,
 
613
                                     g_task_get_cancellable (task),
 
614
                                     ide_git_vcs_reload__load_repository_cb2,
 
615
                                     g_object_ref (task));
 
616
}
 
617
 
 
618
static void
 
619
ide_git_vcs_reload_async (IdeGitVcs           *self,
 
620
                          GCancellable        *cancellable,
 
621
                          GAsyncReadyCallback  callback,
 
622
                          gpointer             user_data)
 
623
{
 
624
  g_autoptr(GTask) task = NULL;
 
625
 
 
626
  IDE_ENTRY;
 
627
 
 
628
  g_assert (IDE_IS_GIT_VCS (self));
 
629
  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
 
630
 
 
631
  task = g_task_new (self, cancellable, callback, user_data);
 
632
 
 
633
  if (self->reloading)
 
634
    {
 
635
      /*
 
636
       * Ignore if we are already reloading. We should probably set a bit here and attept to
 
637
       * reload again after the current process completes.
 
638
       */
 
639
      g_task_return_boolean (task, TRUE);
 
640
      IDE_EXIT;
 
641
    }
 
642
 
 
643
  self->reloading = TRUE;
 
644
 
 
645
  ide_git_vcs_load_repository_async (self,
 
646
                                     NULL,
 
647
                                     ide_git_vcs_reload__load_repository_cb,
 
648
                                     g_object_ref (task));
 
649
 
 
650
  IDE_EXIT;
 
651
}
 
652
 
 
653
static gboolean
 
654
ide_git_vcs_reload_finish (IdeGitVcs     *self,
 
655
                           GAsyncResult  *result,
 
656
                           GError       **error)
 
657
{
 
658
  GTask *task = (GTask *)result;
 
659
  gboolean ret;
 
660
 
 
661
  IDE_ENTRY;
 
662
 
 
663
  g_return_val_if_fail (IDE_IS_GIT_VCS (self), FALSE);
 
664
 
 
665
  self->reloading = FALSE;
 
666
  g_signal_emit (self, gSignals [RELOADED], 0, self->change_monitor_repository);
 
667
  ret = g_task_propagate_boolean (task, error);
 
668
 
 
669
  IDE_RETURN (ret);
 
670
}
 
671
 
 
672
static gboolean
 
673
ide_git_vcs_is_ignored (IdeVcs  *vcs,
 
674
                        GFile   *file,
 
675
                        GError **error)
 
676
{
 
677
  g_autofree gchar *name = NULL;
 
678
  IdeGitVcs *self = (IdeGitVcs *)vcs;
 
679
  gboolean ret = FALSE;
 
680
 
 
681
  g_assert (IDE_IS_GIT_VCS (self));
 
682
  g_assert (G_IS_FILE (file));
 
683
 
 
684
  name = g_file_get_relative_path (self->working_directory, file);
 
685
  if (g_strcmp0 (name, ".git") == 0)
 
686
    return TRUE;
 
687
 
 
688
  if (name != NULL)
 
689
    return ggit_repository_path_is_ignored (self->repository, name, error);
 
690
 
 
691
  return ret;
 
692
}
 
693
 
 
694
static void
 
695
ide_git_vcs_dispose (GObject *object)
 
696
{
 
697
  IdeGitVcs *self = (IdeGitVcs *)object;
 
698
 
 
699
  IDE_ENTRY;
 
700
 
 
701
  if (self->changed_timeout)
 
702
    {
 
703
      g_source_remove (self->changed_timeout);
 
704
      self->changed_timeout = 0;
 
705
    }
 
706
 
 
707
  if (self->monitor)
 
708
    {
 
709
      if (!g_file_monitor_is_cancelled (self->monitor))
 
710
        g_file_monitor_cancel (self->monitor);
 
711
      g_clear_object (&self->monitor);
 
712
    }
 
713
 
 
714
  g_clear_object (&self->change_monitor_repository);
 
715
  g_clear_object (&self->repository);
 
716
  g_clear_object (&self->working_directory);
 
717
 
 
718
  G_OBJECT_CLASS (ide_git_vcs_parent_class)->dispose (object);
 
719
 
 
720
  IDE_EXIT;
 
721
}
 
722
 
 
723
static void
 
724
ide_git_vcs_get_property (GObject    *object,
 
725
                          guint       prop_id,
 
726
                          GValue     *value,
 
727
                          GParamSpec *pspec)
 
728
{
 
729
  IdeGitVcs *self = IDE_GIT_VCS (object);
 
730
 
 
731
  switch (prop_id)
 
732
    {
 
733
    case PROP_REPOSITORY:
 
734
      g_value_set_object (value, ide_git_vcs_get_repository (self));
 
735
      break;
 
736
 
 
737
    default:
 
738
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
739
    }
 
740
}
 
741
 
 
742
static void
 
743
ide_git_vcs_class_init (IdeGitVcsClass *klass)
 
744
{
 
745
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
746
  IdeVcsClass *vcs_class = IDE_VCS_CLASS (klass);
 
747
 
 
748
  object_class->dispose = ide_git_vcs_dispose;
 
749
  object_class->get_property = ide_git_vcs_get_property;
 
750
 
 
751
  vcs_class->get_working_directory = ide_git_vcs_get_working_directory;
 
752
  vcs_class->get_buffer_change_monitor = ide_git_vcs_get_buffer_change_monitor;
 
753
  vcs_class->is_ignored = ide_git_vcs_is_ignored;
 
754
 
 
755
  /**
 
756
   * IdeGitVcs:repository:
 
757
   *
 
758
   * This property contains the underlying #GgitRepository that can be used to lookup git
 
759
   * information. Consumers should be careful about using this directly. It is not thread-safe
 
760
   * to use this object, nor is it safe to perform many blocking calls from the main thread.
 
761
   *
 
762
   * You might want to get the #GgitRepository:location property and create your own instance
 
763
   * of the repository for threaded operations.
 
764
   */
 
765
  gParamSpecs [PROP_REPOSITORY] =
 
766
    g_param_spec_object ("repository",
 
767
                         _("Repository"),
 
768
                         _("The git repository for the project."),
 
769
                         GGIT_TYPE_REPOSITORY,
 
770
                         (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
 
771
 
 
772
  g_object_class_install_properties (object_class, LAST_PROP, gParamSpecs);
 
773
 
 
774
  /**
 
775
   * IdeGitVcs::reloaded:
 
776
   * @self: An #IdeGitVfs
 
777
   * @repository: A #GgitRepository
 
778
   *
 
779
   * This signal is emitted when the git index has been reloaded. Various consumers may want to
 
780
   * reload their git objects upon this notification. Such an example would be the line diffs
 
781
   * that are rendered in the source view gutter.
 
782
   *
 
783
   * The @repository instance is to aide consumers in locating the repository and should not
 
784
   * be used directly except in very specific situations. The gutter change renderer uses this
 
785
   * instance in a threaded manner.
 
786
   */
 
787
  gSignals [RELOADED] = g_signal_new ("reloaded",
 
788
                                      G_TYPE_FROM_CLASS (klass),
 
789
                                      G_SIGNAL_RUN_LAST,
 
790
                                      0,
 
791
                                      NULL, NULL, NULL,
 
792
                                      G_TYPE_NONE,
 
793
                                      1,
 
794
                                      GGIT_TYPE_REPOSITORY);
 
795
}
 
796
 
 
797
static void
 
798
ide_git_vcs_init (IdeGitVcs *self)
 
799
{
 
800
}
 
801
 
 
802
static void
 
803
ide_git_vcs_init_async__reload_cb (GObject      *object,
 
804
                                   GAsyncResult *result,
 
805
                                   gpointer      user_data)
 
806
{
 
807
  IdeGitVcs *self = (IdeGitVcs *)object;
 
808
  g_autoptr(GTask) task = user_data;
 
809
  GError *error = NULL;
 
810
 
 
811
  g_assert (G_IS_TASK (task));
 
812
  g_assert (IDE_IS_GIT_VCS (self));
 
813
 
 
814
  if (!ide_git_vcs_reload_finish (self, result, &error))
 
815
    g_task_return_error (task, error);
 
816
  else
 
817
    g_task_return_boolean (task, TRUE);
 
818
}
 
819
 
 
820
static void
 
821
ide_git_vcs_init_async (GAsyncInitable      *initable,
 
822
                        int                  io_priority,
 
823
                        GCancellable        *cancellable,
 
824
                        GAsyncReadyCallback  callback,
 
825
                        gpointer             user_data)
 
826
{
 
827
  IdeGitVcs *self = (IdeGitVcs *)initable;
 
828
  g_autoptr(GTask) task = NULL;
 
829
 
 
830
  g_return_if_fail (IDE_IS_GIT_VCS (self));
 
831
 
 
832
  task = g_task_new (self, cancellable, callback, user_data);
 
833
  ide_git_vcs_reload_async (self,
 
834
                            cancellable,
 
835
                            ide_git_vcs_init_async__reload_cb,
 
836
                            g_object_ref (task));
 
837
}
 
838
 
 
839
static gboolean
 
840
ide_git_vcs_init_finish (GAsyncInitable  *initable,
 
841
                         GAsyncResult    *result,
 
842
                         GError         **error)
 
843
{
 
844
  GTask *task = (GTask *)result;
 
845
 
 
846
  g_return_val_if_fail (G_IS_TASK (task), FALSE);
 
847
 
 
848
  return g_task_propagate_boolean (task, error);
 
849
}
 
850
 
 
851
static void
 
852
g_async_initable_init_interface (GAsyncInitableIface *iface)
 
853
{
 
854
  iface->init_async = ide_git_vcs_init_async;
 
855
  iface->init_finish = ide_git_vcs_init_finish;
 
856
}