1 /* ide-vcs.c
2 *
3 * Copyright 2015-2019 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 * SPDX-License-Identifier: GPL-3.0-or-later
19 */
20
21 #define G_LOG_DOMAIN "ide-vcs"
22
23 #include "config.h"
24
25 #include <libide-io.h>
26 #include <string.h>
27
28 #include "ide-directory-vcs.h"
29 #include "ide-vcs.h"
30 #include "ide-vcs-enums.h"
31
32 G_DEFINE_INTERFACE (IdeVcs, ide_vcs, IDE_TYPE_OBJECT)
33
34 enum {
35 CHANGED,
36 N_SIGNALS
37 };
38
39 static guint signals [N_SIGNALS];
40
41 static void
ide_vcs_real_list_status_async(IdeVcs * self,GFile * directory_or_file,gboolean include_descendants,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)42 ide_vcs_real_list_status_async (IdeVcs *self,
43 GFile *directory_or_file,
44 gboolean include_descendants,
45 gint io_priority,
46 GCancellable *cancellable,
47 GAsyncReadyCallback callback,
48 gpointer user_data)
49 {
50 g_task_report_new_error (self,
51 callback,
52 user_data,
53 ide_vcs_real_list_status_async,
54 G_IO_ERROR,
55 G_IO_ERROR_NOT_SUPPORTED,
56 "Not supported by %s",
57 G_OBJECT_TYPE_NAME (self));
58 }
59
60 static GListModel *
ide_vcs_real_list_status_finish(IdeVcs * self,GAsyncResult * result,GError ** error)61 ide_vcs_real_list_status_finish (IdeVcs *self,
62 GAsyncResult *result,
63 GError **error)
64 {
65 return g_task_propagate_pointer (G_TASK (result), error);
66 }
67
68 static void
ide_vcs_real_list_branches_async(IdeVcs * self,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)69 ide_vcs_real_list_branches_async (IdeVcs *self,
70 GCancellable *cancellable,
71 GAsyncReadyCallback callback,
72 gpointer user_data)
73 {
74 g_task_report_new_error (self,
75 callback,
76 user_data,
77 ide_vcs_real_list_branches_async,
78 G_IO_ERROR,
79 G_IO_ERROR_NOT_SUPPORTED,
80 "Not supported by %s",
81 G_OBJECT_TYPE_NAME (self));
82 }
83
84 static GPtrArray *
ide_vcs_real_list_branches_finish(IdeVcs * self,GAsyncResult * result,GError ** error)85 ide_vcs_real_list_branches_finish (IdeVcs *self,
86 GAsyncResult *result,
87 GError **error)
88 {
89 return g_task_propagate_pointer (G_TASK (result), error);
90 }
91
92 static void
ide_vcs_real_list_tags_async(IdeVcs * self,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)93 ide_vcs_real_list_tags_async (IdeVcs *self,
94 GCancellable *cancellable,
95 GAsyncReadyCallback callback,
96 gpointer user_data)
97 {
98 g_task_report_new_error (self,
99 callback,
100 user_data,
101 ide_vcs_real_list_tags_async,
102 G_IO_ERROR,
103 G_IO_ERROR_NOT_SUPPORTED,
104 "Not supported by %s",
105 G_OBJECT_TYPE_NAME (self));
106 }
107
108 static GPtrArray *
ide_vcs_real_list_tags_finish(IdeVcs * self,GAsyncResult * result,GError ** error)109 ide_vcs_real_list_tags_finish (IdeVcs *self,
110 GAsyncResult *result,
111 GError **error)
112 {
113 return g_task_propagate_pointer (G_TASK (result), error);
114 }
115
116 static void
ide_vcs_real_switch_branch_async(IdeVcs * self,IdeVcsBranch * branch,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)117 ide_vcs_real_switch_branch_async (IdeVcs *self,
118 IdeVcsBranch *branch,
119 GCancellable *cancellable,
120 GAsyncReadyCallback callback,
121 gpointer user_data)
122 {
123 g_task_report_new_error (self,
124 callback,
125 user_data,
126 ide_vcs_real_switch_branch_async,
127 G_IO_ERROR,
128 G_IO_ERROR_NOT_SUPPORTED,
129 "Not supported by %s",
130 G_OBJECT_TYPE_NAME (self));
131 }
132
133 static gboolean
ide_vcs_real_switch_branch_finish(IdeVcs * self,GAsyncResult * result,GError ** error)134 ide_vcs_real_switch_branch_finish (IdeVcs *self,
135 GAsyncResult *result,
136 GError **error)
137 {
138 return g_task_propagate_boolean (G_TASK (result), error);
139 }
140
141 static void
ide_vcs_real_push_branch_async(IdeVcs * self,IdeVcsBranch * branch,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)142 ide_vcs_real_push_branch_async (IdeVcs *self,
143 IdeVcsBranch *branch,
144 GCancellable *cancellable,
145 GAsyncReadyCallback callback,
146 gpointer user_data)
147 {
148 g_task_report_new_error (self,
149 callback,
150 user_data,
151 ide_vcs_real_push_branch_async,
152 G_IO_ERROR,
153 G_IO_ERROR_NOT_SUPPORTED,
154 "Not supported by %s",
155 G_OBJECT_TYPE_NAME (self));
156 }
157
158 static gboolean
ide_vcs_real_push_branch_finish(IdeVcs * self,GAsyncResult * result,GError ** error)159 ide_vcs_real_push_branch_finish (IdeVcs *self,
160 GAsyncResult *result,
161 GError **error)
162 {
163 return g_task_propagate_boolean (G_TASK (result), error);
164 }
165
166 static void
ide_vcs_default_init(IdeVcsInterface * iface)167 ide_vcs_default_init (IdeVcsInterface *iface)
168 {
169 iface->list_status_async = ide_vcs_real_list_status_async;
170 iface->list_status_finish = ide_vcs_real_list_status_finish;
171 iface->list_branches_async = ide_vcs_real_list_branches_async;
172 iface->list_branches_finish = ide_vcs_real_list_branches_finish;
173 iface->list_tags_async = ide_vcs_real_list_tags_async;
174 iface->list_tags_finish = ide_vcs_real_list_tags_finish;
175 iface->switch_branch_async = ide_vcs_real_switch_branch_async;
176 iface->switch_branch_finish = ide_vcs_real_switch_branch_finish;
177 iface->push_branch_async = ide_vcs_real_push_branch_async;
178 iface->push_branch_finish = ide_vcs_real_push_branch_finish;
179
180 g_object_interface_install_property (iface,
181 g_param_spec_string ("branch-name",
182 "Branch Name",
183 "The current name of the branch",
184 NULL,
185 (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
186
187 g_object_interface_install_property (iface,
188 g_param_spec_object ("workdir",
189 "Working Directory",
190 "The working directory for the VCS",
191 G_TYPE_FILE,
192 (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
193
194 /**
195 * IdeVcs::changed:
196 *
197 * The "changed" signal should be emitted when the VCS has detected a change
198 * to the underlying VCS storage. This can be used by consumers to reload
199 * their respective data structures.
200 *
201 * Since: 3.32
202 */
203 signals [CHANGED] =
204 g_signal_new ("changed",
205 G_TYPE_FROM_INTERFACE (iface),
206 G_SIGNAL_RUN_LAST,
207 G_STRUCT_OFFSET (IdeVcsInterface, changed),
208 NULL, NULL,
209 g_cclosure_marshal_VOID__VOID,
210 G_TYPE_NONE, 0);
211 g_signal_set_va_marshaller (signals [CHANGED],
212 G_TYPE_FROM_INTERFACE (iface),
213 g_cclosure_marshal_VOID__VOIDv);
214 }
215
216 /**
217 * ide_vcs_is_ignored:
218 * @self: An #IdeVcs
219 * @file: (nullable): a #GFile
220 * @error: A location for a #GError, or %NULL
221 *
222 * This function will check if @file is considered an "ignored file" by
223 * the underlying Version Control System.
224 *
225 * For convenience, this function will return %TRUE if @file is %NULL.
226 *
227 * If @self is %NULL, only static checks against known ignored files
228 * will be performed (such as .git, .flatpak-builder, etc).
229 *
230 * Returns: %TRUE if the path should be ignored.
231 *
232 * Thread safety: This function is safe to call from a thread as
233 * #IdeVcs implementations are required to ensure this function
234 * is thread-safe.
235 *
236 * Since: 3.32
237 */
238 gboolean
ide_vcs_is_ignored(IdeVcs * self,GFile * file,GError ** error)239 ide_vcs_is_ignored (IdeVcs *self,
240 GFile *file,
241 GError **error)
242 {
243 g_return_val_if_fail (!self || IDE_IS_VCS (self), FALSE);
244 g_return_val_if_fail (!file || G_IS_FILE (file), FALSE);
245
246 if (file == NULL)
247 return TRUE;
248
249 if (ide_g_file_is_ignored (file))
250 return TRUE;
251
252 if (self != NULL)
253 {
254 if (IDE_VCS_GET_IFACE (self)->is_ignored)
255 return IDE_VCS_GET_IFACE (self)->is_ignored (self, file, error);
256 }
257
258 return FALSE;
259 }
260
261 /**
262 * ide_vcs_path_is_ignored:
263 * @self: An #IdeVcs
264 * @path: (nullable): The path to check
265 * @error: A location for a #GError, or %NULL
266 *
267 * This function acts like ide_vcs_is_ignored() except that it
268 * allows for using a regular file-system path.
269 *
270 * It will check if the path is absolute or relative to the project
271 * directory and adjust as necessary.
272 *
273 * For convenience, this function will return %TRUE if @path is %NULL.
274 *
275 * If @self is %NULL, only registered ignore patterns will be checked.
276 *
277 * Returns: %TRUE if the path should be ignored.
278 *
279 * Thread safety: This function is safe to call from a thread as
280 * #IdeVcs implementations are required to ensure this function
281 * is thread-safe.
282 *
283 * Since: 3.32
284 */
285 gboolean
ide_vcs_path_is_ignored(IdeVcs * self,const gchar * path,GError ** error)286 ide_vcs_path_is_ignored (IdeVcs *self,
287 const gchar *path,
288 GError **error)
289 {
290 gboolean ret = FALSE;
291
292 g_return_val_if_fail (!self || IDE_IS_VCS (self), FALSE);
293
294 if (path == NULL)
295 return TRUE;
296
297 if (ide_path_is_ignored (path))
298 return TRUE;
299
300 if (self != NULL)
301 {
302 if (!ret && IDE_VCS_GET_IFACE (self)->is_ignored)
303 {
304 g_autoptr(GFile) file = NULL;
305
306 if (g_path_is_absolute (path))
307 file = g_file_new_for_path (path);
308 else
309 file = g_file_get_child (ide_vcs_get_workdir (self), path);
310
311 ret = IDE_VCS_GET_IFACE (self)->is_ignored (self, file, error);
312 }
313 }
314
315 return ret;
316 }
317
318 gint
ide_vcs_get_priority(IdeVcs * self)319 ide_vcs_get_priority (IdeVcs *self)
320 {
321 gint ret = 0;
322
323 g_return_val_if_fail (IDE_IS_VCS (self), 0);
324
325 if (IDE_VCS_GET_IFACE (self)->get_priority)
326 ret = IDE_VCS_GET_IFACE (self)->get_priority (self);
327
328 return ret;
329 }
330
331 /**
332 * ide_vcs_get_workdir:
333 * @self: An #IdeVcs.
334 *
335 * Retrieves the working directory for the context. This is the root of where
336 * the project files exist.
337 *
338 * This function is safe to call from threads holding a reference to @self.
339 *
340 * Returns: (transfer none): a #GFile.
341 *
342 * Since: 3.32
343 *
344 * Thread safety: this function is safe to call from threads. The working
345 * directory should only be set at creating and therefore safe to call
346 * at any time from any thread that holds a reference to @self. Those
347 * implementing #IdeVcs are required to ensure this invariant holds true.
348 */
349 GFile *
ide_vcs_get_workdir(IdeVcs * self)350 ide_vcs_get_workdir (IdeVcs *self)
351 {
352 g_return_val_if_fail (IDE_IS_VCS (self), NULL);
353
354 if (IDE_VCS_GET_IFACE (self)->get_workdir)
355 return IDE_VCS_GET_IFACE (self)->get_workdir (self);
356
357 return NULL;
358 }
359
360 void
ide_vcs_emit_changed(IdeVcs * self)361 ide_vcs_emit_changed (IdeVcs *self)
362 {
363 g_return_if_fail (IDE_IS_VCS (self));
364
365 g_signal_emit (self, signals [CHANGED], 0);
366 }
367
368 /**
369 * ide_vcs_get_config:
370 *
371 * Retrieves an #IdeVcsConfig for the #IdeVcs provided. If the #IdeVcs implementation does not
372 * support access to configuration, then %NULL is returned.
373 *
374 * Returns: (transfer full) (nullable): An #IdeVcsConfig or %NULL.
375 *
376 * Since: 3.32
377 */
378 IdeVcsConfig *
ide_vcs_get_config(IdeVcs * self)379 ide_vcs_get_config (IdeVcs *self)
380 {
381 IdeVcsConfig *ret = NULL;
382
383 g_return_val_if_fail (IDE_IS_VCS (self), NULL);
384
385 if (IDE_VCS_GET_IFACE (self)->get_config)
386 ret = IDE_VCS_GET_IFACE (self)->get_config (self);
387
388 g_return_val_if_fail (!ret || IDE_IS_VCS_CONFIG (ret), NULL);
389
390 return ret;
391 }
392
393 /**
394 * ide_vcs_get_branch_name:
395 *
396 * Retrieves the name of the branch in the current working directory.
397 *
398 * Returns: (transfer full): A string containing the branch name.
399 *
400 * Since: 3.32
401 */
402 gchar *
ide_vcs_get_branch_name(IdeVcs * self)403 ide_vcs_get_branch_name (IdeVcs *self)
404 {
405 gchar *ret = NULL;
406
407 g_return_val_if_fail (IDE_IS_VCS (self), NULL);
408
409 ide_object_lock (IDE_OBJECT (self));
410 if (IDE_VCS_GET_IFACE (self)->get_branch_name)
411 ret = IDE_VCS_GET_IFACE (self)->get_branch_name (self);
412 ide_object_unlock (IDE_OBJECT (self));
413
414 if (ret == NULL)
415 ret = g_strdup ("primary");
416
417 return ret;
418 }
419
420 /**
421 * ide_vcs_list_status_async:
422 * @self: a #IdeVcs
423 * @directory_or_file: a #GFile containing a file or directory within the
424 * working tree to retrieve the status of.
425 * @include_descendants: if descendants of @directory_or_file should be
426 * included when retrieving status information.
427 * @io_priority: a priority for the IO, such as %G_PRIORITY_DEFAULT.
428 * @cancellable: (nullable): A #GCancellable or %NULL
429 * @callback: a callback for the operation
430 * @user_data: closure data for @callback
431 *
432 * Retrieves the status of the files matching the request. If
433 * @directory_or_file is a directory, then all files within that directory
434 * will be scanned for changes. If @include_descendants is %TRUE, the
435 * #IdeVcs will scan sub-directories for changes as well.
436 *
437 * The function specified by @callback should call ide_vcs_list_status_finish()
438 * to retrieve the result of this asynchronous operation.
439 *
440 * Since: 3.32
441 */
442 void
ide_vcs_list_status_async(IdeVcs * self,GFile * directory_or_file,gboolean include_descendants,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)443 ide_vcs_list_status_async (IdeVcs *self,
444 GFile *directory_or_file,
445 gboolean include_descendants,
446 gint io_priority,
447 GCancellable *cancellable,
448 GAsyncReadyCallback callback,
449 gpointer user_data)
450 {
451 g_return_if_fail (IDE_IS_VCS (self));
452 g_return_if_fail (!directory_or_file || G_IS_FILE (directory_or_file));
453 g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
454
455 if (directory_or_file == NULL)
456 directory_or_file = ide_vcs_get_workdir (self);
457
458 IDE_VCS_GET_IFACE (self)->list_status_async (self,
459 directory_or_file,
460 include_descendants,
461 io_priority,
462 cancellable,
463 callback,
464 user_data);
465 }
466
467 /**
468 * ide_vcs_list_status_finish:
469 * @self: a #IdeVcs
470 * @result: a #GAsyncResult provided to the callback
471 * @error: a location for a #GError
472 *
473 * Completes an asynchronous request to ide_vcs_list_status_async().
474 *
475 * The result of this function is a #GListModel containing objects that are
476 * #IdeVcsFileInfo.
477 *
478 * Returns: (transfer full) (nullable):
479 * A #GListModel containing an #IdeVcsFileInfo for each of the files scanned
480 * by the #IdeVcs. Upon failure, %NULL is returned and @error is set.
481 *
482 * Since: 3.32
483 */
484 GListModel *
ide_vcs_list_status_finish(IdeVcs * self,GAsyncResult * result,GError ** error)485 ide_vcs_list_status_finish (IdeVcs *self,
486 GAsyncResult *result,
487 GError **error)
488 {
489 g_return_val_if_fail (IDE_IS_VCS (self), NULL);
490 g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
491
492 return IDE_VCS_GET_IFACE (self)->list_status_finish (self, result, error);
493 }
494
495 /**
496 * ide_vcs_from_context:
497 * @context: an #IdeContext
498 *
499 * Gets the #IdeVcs for the context.
500 *
501 * Returns: (transfer none): an #IdeVcs
502 *
503 * Since: 3.32
504 */
505 IdeVcs *
ide_vcs_from_context(IdeContext * context)506 ide_vcs_from_context (IdeContext *context)
507 {
508 IdeVcs *ret;
509
510 g_return_val_if_fail (IDE_IS_MAIN_THREAD (), NULL);
511 g_return_val_if_fail (IDE_IS_CONTEXT (context), NULL);
512
513 /* Release full reference, into borrowed ref */
514 ret = ide_vcs_ref_from_context (context);
515 g_object_unref (ret);
516
517 return ret;
518 }
519
520 /**
521 * ide_vcs_ref_from_context:
522 * @context: an #IdeContext
523 *
524 * A thread-safe version of ide_vcs_from_context().
525 *
526 * Returns: (transfer full): an #IdeVcs
527 *
528 * Since: 3.32
529 */
530 IdeVcs *
ide_vcs_ref_from_context(IdeContext * context)531 ide_vcs_ref_from_context (IdeContext *context)
532 {
533 IdeVcs *ret;
534
535 g_return_val_if_fail (IDE_IS_CONTEXT (context), NULL);
536
537 ide_object_lock (IDE_OBJECT (context));
538 ret = ide_object_get_child_typed (IDE_OBJECT (context), IDE_TYPE_VCS);
539 if (ret == NULL)
540 {
541 g_autoptr(GFile) workdir = ide_context_ref_workdir (context);
542 ret = (IdeVcs *)ide_directory_vcs_new (workdir);
543 ide_object_prepend (IDE_OBJECT (context), IDE_OBJECT (ret));
544 }
545 ide_object_unlock (IDE_OBJECT (context));
546
547 return g_steal_pointer (&ret);
548 }
549
550 void
ide_vcs_list_branches_async(IdeVcs * self,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)551 ide_vcs_list_branches_async (IdeVcs *self,
552 GCancellable *cancellable,
553 GAsyncReadyCallback callback,
554 gpointer user_data)
555 {
556 g_return_if_fail (IDE_IS_VCS (self));
557 g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
558
559 IDE_VCS_GET_IFACE (self)->list_branches_async (self, cancellable, callback, user_data);
560 }
561
562 /**
563 * ide_vcs_list_branches_finish:
564 * @self: an #IdeVcs
565 * @result: a #GAsyncResult
566 * @error: location for a #GError
567 *
568 * Returns: (transfer full) (element-type IdeVcsBranch): an array of
569 * #IdeVcsBranch.
570 *
571 * Since: 3.32
572 */
573 GPtrArray *
ide_vcs_list_branches_finish(IdeVcs * self,GAsyncResult * result,GError ** error)574 ide_vcs_list_branches_finish (IdeVcs *self,
575 GAsyncResult *result,
576 GError **error)
577 {
578 g_return_val_if_fail (IDE_IS_MAIN_THREAD (), NULL);
579 g_return_val_if_fail (IDE_IS_VCS (self), NULL);
580 g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
581
582 return IDE_VCS_GET_IFACE (self)->list_branches_finish (self, result, error);
583 }
584
585 void
ide_vcs_list_tags_async(IdeVcs * self,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)586 ide_vcs_list_tags_async (IdeVcs *self,
587 GCancellable *cancellable,
588 GAsyncReadyCallback callback,
589 gpointer user_data)
590 {
591 g_return_if_fail (IDE_IS_VCS (self));
592 g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
593
594 IDE_VCS_GET_IFACE (self)->list_tags_async (self, cancellable, callback, user_data);
595 }
596
597 /**
598 * ide_vcs_list_tags_finish:
599 * @self: an #IdeVcs
600 * @result: a #GAsyncResult
601 * @error: location for a #GError
602 *
603 * Returns: (transfer full) (element-type IdeVcsBranch): an array of
604 * #IdeVcsBranch.
605 *
606 * Since: 3.32
607 */
608 GPtrArray *
ide_vcs_list_tags_finish(IdeVcs * self,GAsyncResult * result,GError ** error)609 ide_vcs_list_tags_finish (IdeVcs *self,
610 GAsyncResult *result,
611 GError **error)
612 {
613 g_return_val_if_fail (IDE_IS_MAIN_THREAD (), NULL);
614 g_return_val_if_fail (IDE_IS_VCS (self), NULL);
615 g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
616
617 return IDE_VCS_GET_IFACE (self)->list_tags_finish (self, result, error);
618 }
619
620 void
ide_vcs_switch_branch_async(IdeVcs * self,IdeVcsBranch * branch,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)621 ide_vcs_switch_branch_async (IdeVcs *self,
622 IdeVcsBranch *branch,
623 GCancellable *cancellable,
624 GAsyncReadyCallback callback,
625 gpointer user_data)
626 {
627 g_return_if_fail (IDE_IS_MAIN_THREAD ());
628 g_return_if_fail (IDE_IS_VCS (self));
629 g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
630
631 IDE_VCS_GET_IFACE (self)->switch_branch_async (self, branch, cancellable, callback, user_data);
632 }
633
634 gboolean
ide_vcs_switch_branch_finish(IdeVcs * self,GAsyncResult * result,GError ** error)635 ide_vcs_switch_branch_finish (IdeVcs *self,
636 GAsyncResult *result,
637 GError **error)
638 {
639 g_return_val_if_fail (IDE_IS_MAIN_THREAD (), FALSE);
640 g_return_val_if_fail (IDE_IS_VCS (self), FALSE);
641 g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
642
643 return IDE_VCS_GET_IFACE (self)->switch_branch_finish (self, result, error);
644 }
645
646 void
ide_vcs_push_branch_async(IdeVcs * self,IdeVcsBranch * branch,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)647 ide_vcs_push_branch_async (IdeVcs *self,
648 IdeVcsBranch *branch,
649 GCancellable *cancellable,
650 GAsyncReadyCallback callback,
651 gpointer user_data)
652 {
653 g_return_if_fail (IDE_IS_MAIN_THREAD ());
654 g_return_if_fail (IDE_IS_VCS (self));
655 g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
656
657 IDE_VCS_GET_IFACE (self)->push_branch_async (self, branch, cancellable, callback, user_data);
658 }
659
660 gboolean
ide_vcs_push_branch_finish(IdeVcs * self,GAsyncResult * result,GError ** error)661 ide_vcs_push_branch_finish (IdeVcs *self,
662 GAsyncResult *result,
663 GError **error)
664 {
665 g_return_val_if_fail (IDE_IS_MAIN_THREAD (), FALSE);
666 g_return_val_if_fail (IDE_IS_VCS (self), FALSE);
667 g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
668
669 return IDE_VCS_GET_IFACE (self)->push_branch_finish (self, result, error);
670 }
671