1 /* $Id$ */
2 /* Copyright (c) 2012-2015 Pierre Pronchery <khorben@defora.org> */
3 /* This file is part of DeforaOS Desktop Browser */
4 /* Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * 3. Neither the name of the authors nor the names of the contributors may be
16 * used to endorse or promote products derived from this software without
17 * specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS "AS IS" AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
29 /* TODO:
30 * - parse git's meta-data */
31
32
33
34 #include <System.h>
35 #include <sys/stat.h>
36 #include <sys/wait.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <libgen.h>
40 #include <errno.h>
41 #define COMMON_PROMPT
42 #include "common.c"
43
44
45 /* Git */
46 /* private */
47 /* types */
48 typedef struct _CommonTask GitTask;
49
50 typedef struct _BrowserPlugin
51 {
52 BrowserPluginHelper * helper;
53
54 char * filename;
55
56 guint source;
57
58 /* widgets */
59 GtkWidget * widget;
60 GtkWidget * name;
61 GtkWidget * status;
62 /* init */
63 GtkWidget * init;
64 /* directory */
65 GtkWidget * directory;
66 /* file */
67 GtkWidget * file;
68
69 /* tasks */
70 GitTask ** tasks;
71 size_t tasks_cnt;
72 } Git;
73
74
75 /* constants */
76 #define GIT_GIT "git"
77
78
79 /* prototypes */
80 static Git * _git_init(BrowserPluginHelper * helper);
81 static void _git_destroy(Git * git);
82 static GtkWidget * _git_get_widget(Git * git);
83 static void _git_refresh(Git * git, GList * selection);
84
85 /* accessors */
86 static gboolean _git_is_managed(char const * filename);
87
88 /* useful */
89 static int _git_add_task(Git * git, char const * title,
90 char const * directory, char * argv[],
91 CommonTaskCallback callback);
92
93 /* callbacks */
94 static void _git_on_add(gpointer data);
95 static void _git_on_blame(gpointer data);
96 static void _git_on_clone(gpointer data);
97 static void _git_on_commit(gpointer data);
98 static void _git_on_diff(gpointer data);
99 static void _git_on_init(gpointer data);
100 static void _git_on_log(gpointer data);
101 static void _git_on_pull(gpointer data);
102 static void _git_on_push(gpointer data);
103 static void _git_on_reset(gpointer data);
104 static void _git_on_status(gpointer data);
105
106
107 /* public */
108 /* variables */
109 /* plug-in */
110 BrowserPluginDefinition plugin =
111 {
112 N_("Git"),
113 "applications-development",
114 NULL,
115 _git_init,
116 _git_destroy,
117 _git_get_widget,
118 _git_refresh
119 };
120
121
122 /* private */
123 /* functions */
124 /* git_init */
125 static GtkWidget * _init_button(GtkSizeGroup * group, char const * icon,
126 char const * label, GCallback callback, gpointer data);
127
_git_init(BrowserPluginHelper * helper)128 static Git * _git_init(BrowserPluginHelper * helper)
129 {
130 Git * git;
131 PangoFontDescription * font;
132 GtkSizeGroup * group;
133 GtkWidget * widget;
134
135 if((git = object_new(sizeof(*git))) == NULL)
136 return NULL;
137 git->helper = helper;
138 git->filename = NULL;
139 git->source = 0;
140 /* widgets */
141 git->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
142 font = pango_font_description_new();
143 pango_font_description_set_weight(font, PANGO_WEIGHT_BOLD);
144 group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
145 /* label */
146 git->name = gtk_label_new("");
147 gtk_label_set_ellipsize(GTK_LABEL(git->name), PANGO_ELLIPSIZE_MIDDLE);
148 #if GTK_CHECK_VERSION(3, 0, 0)
149 gtk_widget_override_font(git->name, font);
150 g_object_set(git->name, "halign", GTK_ALIGN_START, NULL);
151 #else
152 gtk_widget_modify_font(git->name, font);
153 gtk_misc_set_alignment(GTK_MISC(git->name), 0.0, 0.5);
154 #endif
155 gtk_box_pack_start(GTK_BOX(git->widget), git->name, FALSE, TRUE, 0);
156 git->status = gtk_label_new("");
157 gtk_label_set_ellipsize(GTK_LABEL(git->status), PANGO_ELLIPSIZE_END);
158 #if GTK_CHECK_VERSION(3, 0, 0)
159 g_object_set(git->status, "halign", GTK_ALIGN_START, NULL);
160 #else
161 gtk_misc_set_alignment(GTK_MISC(git->status), 0.0, 0.5);
162 #endif
163 gtk_box_pack_start(GTK_BOX(git->widget), git->status, FALSE, TRUE, 0);
164 /* init */
165 git->init = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
166 widget = _init_button(group, GTK_STOCK_OK, _("Initialize"), G_CALLBACK(
167 _git_on_init), git);
168 gtk_box_pack_start(GTK_BOX(git->init), widget, FALSE, TRUE, 0);
169 widget = _init_button(group, GTK_STOCK_SAVE_AS, _("Clone..."),
170 G_CALLBACK(_git_on_clone), git);
171 gtk_box_pack_start(GTK_BOX(git->init), widget, FALSE, TRUE, 0);
172 gtk_widget_show_all(git->init);
173 gtk_widget_set_no_show_all(git->init, TRUE);
174 gtk_box_pack_start(GTK_BOX(git->widget), git->init, FALSE, TRUE, 0);
175 /* directory */
176 git->directory = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
177 widget = _init_button(group, GTK_STOCK_FIND_AND_REPLACE,
178 _("Diff"), G_CALLBACK(_git_on_diff), git);
179 gtk_box_pack_start(GTK_BOX(git->directory), widget, FALSE, TRUE, 0);
180 widget = _init_button(group, GTK_STOCK_FIND, _("View log"),
181 G_CALLBACK(_git_on_log), git);
182 gtk_box_pack_start(GTK_BOX(git->directory), widget, FALSE, TRUE, 0);
183 widget = _init_button(group, GTK_STOCK_PROPERTIES, _("Status"),
184 G_CALLBACK(_git_on_status), git);
185 gtk_box_pack_start(GTK_BOX(git->directory), widget, FALSE, TRUE, 0);
186 widget = _init_button(group, GTK_STOCK_REFRESH, _("Pull"),
187 G_CALLBACK(_git_on_pull), git);
188 gtk_box_pack_start(GTK_BOX(git->directory), widget, FALSE, TRUE, 0);
189 widget = _init_button(group, GTK_STOCK_CONNECT, _("Push"),
190 G_CALLBACK(_git_on_push), git);
191 gtk_box_pack_start(GTK_BOX(git->directory), widget, FALSE, TRUE, 0);
192 widget = _init_button(group, GTK_STOCK_REVERT_TO_SAVED, _("Reset"),
193 G_CALLBACK(_git_on_reset), git);
194 gtk_box_pack_start(GTK_BOX(git->directory), widget, FALSE, TRUE, 0);
195 widget = _init_button(group, GTK_STOCK_JUMP_TO, _("Commit"),
196 G_CALLBACK(_git_on_commit), git);
197 gtk_box_pack_start(GTK_BOX(git->directory), widget, FALSE, TRUE, 0);
198 gtk_widget_show_all(git->directory);
199 gtk_widget_set_no_show_all(git->directory, TRUE);
200 gtk_box_pack_start(GTK_BOX(git->widget), git->directory, FALSE, TRUE,
201 0);
202 /* file */
203 git->file = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
204 widget = _init_button(group, GTK_STOCK_FIND_AND_REPLACE,
205 _("Diff"), G_CALLBACK(_git_on_diff), git);
206 gtk_box_pack_start(GTK_BOX(git->file), widget, FALSE, TRUE, 0);
207 widget = _init_button(group, GTK_STOCK_INDEX, _("Annotate"),
208 G_CALLBACK(_git_on_blame), git);
209 gtk_box_pack_start(GTK_BOX(git->file), widget, FALSE, TRUE, 0);
210 widget = _init_button(group, GTK_STOCK_FIND, _("View log"),
211 G_CALLBACK(_git_on_log), git);
212 gtk_box_pack_start(GTK_BOX(git->file), widget, FALSE, TRUE, 0);
213 widget = _init_button(group, GTK_STOCK_ADD, _("Stage"),
214 G_CALLBACK(_git_on_add), git);
215 gtk_box_pack_start(GTK_BOX(git->file), widget, FALSE, TRUE, 0);
216 widget = _init_button(group, GTK_STOCK_REVERT_TO_SAVED, _("Reset"),
217 G_CALLBACK(_git_on_reset), git);
218 gtk_box_pack_start(GTK_BOX(git->file), widget, FALSE, TRUE, 0);
219 widget = _init_button(group, GTK_STOCK_JUMP_TO, _("Commit"),
220 G_CALLBACK(_git_on_commit), git);
221 gtk_box_pack_start(GTK_BOX(git->file), widget, FALSE, TRUE, 0);
222 gtk_widget_show_all(git->file);
223 gtk_widget_set_no_show_all(git->file, TRUE);
224 gtk_box_pack_start(GTK_BOX(git->widget), git->file, FALSE, TRUE, 0);
225 gtk_widget_show_all(git->widget);
226 pango_font_description_free(font);
227 /* tasks */
228 git->tasks = NULL;
229 git->tasks_cnt = 0;
230 return git;
231 }
232
_init_button(GtkSizeGroup * group,char const * icon,char const * label,GCallback callback,gpointer data)233 static GtkWidget * _init_button(GtkSizeGroup * group, char const * icon,
234 char const * label, GCallback callback, gpointer data)
235 {
236 GtkWidget * hbox;
237 GtkWidget * image;
238 GtkWidget * widget;
239 char const stock[] = "gtk-";
240
241 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
242 widget = gtk_button_new_with_label(label);
243 gtk_size_group_add_widget(group, widget);
244 if(icon != NULL)
245 {
246 if(strncmp(icon, stock, sizeof(stock) - 1) == 0)
247 image = gtk_image_new_from_stock(icon,
248 GTK_ICON_SIZE_BUTTON);
249 else
250 image = gtk_image_new_from_icon_name(icon,
251 GTK_ICON_SIZE_BUTTON);
252 gtk_button_set_image(GTK_BUTTON(widget), image);
253 }
254 g_signal_connect_swapped(widget, "clicked", callback, data);
255 gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 0);
256 return hbox;
257 }
258
259
260 /* git_destroy */
_git_destroy(Git * git)261 static void _git_destroy(Git * git)
262 {
263 size_t i;
264
265 for(i = 0; i < git->tasks_cnt; i++)
266 _common_task_delete(git->tasks[i]);
267 free(git->tasks);
268 if(git->source != 0)
269 g_source_remove(git->source);
270 object_delete(git);
271 }
272
273
274 /* git_get_widget */
_git_get_widget(Git * git)275 static GtkWidget * _git_get_widget(Git * git)
276 {
277 return git->widget;
278 }
279
280
281 /* git_refresh */
282 static void _refresh_dir(Git * git);
283 static void _refresh_error(Git * git, char const * message);
284 static void _refresh_file(Git * git);
285 static void _refresh_hide(Git * git, gboolean name);
286 static void _refresh_status(Git * git, char const * status);
287
_git_refresh(Git * git,GList * selection)288 static void _git_refresh(Git * git, GList * selection)
289 {
290 char * path = (selection != NULL) ? selection->data : NULL;
291 struct stat st;
292 gchar * p;
293
294 if(git->source != 0)
295 g_source_remove(git->source);
296 free(git->filename);
297 git->filename = NULL;
298 if(path == NULL || selection->next != NULL)
299 {
300 _refresh_hide(git, TRUE);
301 return;
302 }
303 if(lstat(path, &st) != 0
304 || (git->filename = strdup(path)) == NULL)
305 {
306 _refresh_hide(git, TRUE);
307 if(errno != ENOENT)
308 _refresh_error(git, path);
309 return;
310 }
311 p = g_filename_display_basename(path);
312 gtk_label_set_text(GTK_LABEL(git->name), p);
313 g_free(p);
314 _refresh_hide(git, FALSE);
315 if(S_ISDIR(st.st_mode))
316 _refresh_dir(git);
317 else
318 _refresh_file(git);
319 }
320
_refresh_dir(Git * git)321 static void _refresh_dir(Git * git)
322 {
323 char const dir[] = "/.git";
324 size_t len = strlen(git->filename);
325
326 /* consider ".git" folders like their parent */
327 if((len = strlen(git->filename)) >= (sizeof(dir) - 1)
328 && strcmp(&git->filename[len - 4], dir) == 0)
329 git->filename[len - 4] = '\0';
330 if(_git_is_managed(git->filename) != TRUE)
331 {
332 _refresh_status(git, _("Not a Git repository"));
333 gtk_widget_show(git->init);
334 return;
335 }
336 gtk_widget_show(git->directory);
337 }
338
_refresh_error(Git * git,char const * message)339 static void _refresh_error(Git * git, char const * message)
340 {
341 BrowserPluginHelper * helper = git->helper;
342
343 error_set("%s: %s", message, strerror(errno));
344 helper->error(helper->browser, error_get(NULL), 1);
345 }
346
_refresh_file(Git * git)347 static void _refresh_file(Git * git)
348 {
349 /* FIXME detect if the file is actually managed */
350 gtk_widget_show(git->file);
351 }
352
_refresh_hide(Git * git,gboolean name)353 static void _refresh_hide(Git * git, gboolean name)
354 {
355 name ? gtk_widget_hide(git->name) : gtk_widget_show(git->name);
356 _refresh_status(git, NULL);
357 gtk_widget_hide(git->init);
358 gtk_widget_hide(git->directory);
359 gtk_widget_hide(git->file);
360 }
361
_refresh_status(Git * git,char const * status)362 static void _refresh_status(Git * git, char const * status)
363 {
364 if(status == NULL)
365 gtk_widget_hide(git->status);
366 else
367 {
368 gtk_label_set_text(GTK_LABEL(git->status), status);
369 gtk_widget_show(git->status);
370 }
371 }
372
373
374 /* accessors */
375 /* git_is_managed */
_git_is_managed(char const * filename)376 static gboolean _git_is_managed(char const * filename)
377 {
378 char * base = strdup(filename);
379 char * dir = base;
380 String * p;
381 struct stat st;
382 int res;
383
384 #ifdef DEBUG
385 fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, filename);
386 #endif
387 for(; strcmp(dir, ".") != 0; dir = dirname(dir))
388 {
389 if((p = string_new_append(dir, "/.git", NULL)) == NULL)
390 {
391 free(base);
392 return FALSE;
393 }
394 res = lstat(p, &st);
395 #ifdef DEBUG
396 fprintf(stderr, "DEBUG: %s() \"%s\" %d\n", __func__, p, res);
397 #endif
398 string_delete(p);
399 if(res == 0)
400 {
401 /* FIXME really implement */
402 free(base);
403 return TRUE;
404 }
405 if(strcmp(dir, "/") == 0)
406 break;
407 }
408 free(base);
409 return FALSE;
410 }
411
412
413 /* useful */
414 /* git_add_task */
_git_add_task(Git * git,char const * title,char const * directory,char * argv[],CommonTaskCallback callback)415 static int _git_add_task(Git * git, char const * title,
416 char const * directory, char * argv[],
417 CommonTaskCallback callback)
418 {
419 BrowserPluginHelper * helper = git->helper;
420 GitTask ** p;
421 GitTask * task;
422
423 if((p = realloc(git->tasks, sizeof(*p) * (git->tasks_cnt + 1))) == NULL)
424 return -helper->error(helper->browser, strerror(errno), 1);
425 git->tasks = p;
426 if((task = _common_task_new(helper, &plugin, title, directory, argv,
427 callback, git)) == NULL)
428 return -helper->error(helper->browser, error_get(NULL), 1);
429 git->tasks[git->tasks_cnt++] = task;
430 return 0;
431 }
432
433
434 /* callbacks */
435 /* git_on_add */
_git_on_add(gpointer data)436 static void _git_on_add(gpointer data)
437 {
438 Git * git = data;
439 gchar * dirname;
440 gchar * basename;
441 char * argv[] = { GIT_GIT, "add", "--", NULL, NULL };
442
443 if(git->filename == NULL)
444 return;
445 dirname = g_path_get_dirname(git->filename);
446 basename = g_path_get_basename(git->filename);
447 argv[3] = basename;
448 _git_add_task(git, "git add", dirname, argv, NULL);
449 g_free(basename);
450 g_free(dirname);
451 }
452
453
454 /* git_on_blame */
455 static void _blame_on_callback(Git * git, CommonTask * task, int ret);
456
_git_on_blame(gpointer data)457 static void _git_on_blame(gpointer data)
458 {
459 Git * git = data;
460 struct stat st;
461 gchar * dirname;
462 gchar * basename;
463 char * argv[] = { GIT_GIT, "blame", "--", NULL, NULL };
464
465 if(git->filename == NULL || lstat(git->filename, &st) != 0)
466 return;
467 dirname = S_ISDIR(st.st_mode) ? g_strdup(git->filename)
468 : g_path_get_dirname(git->filename);
469 basename = S_ISDIR(st.st_mode) ? NULL
470 : g_path_get_basename(git->filename);
471 argv[3] = basename;
472 _git_add_task(git, "git blame", dirname, argv, _blame_on_callback);
473 g_free(basename);
474 g_free(dirname);
475 }
476
_blame_on_callback(Git * git,CommonTask * task,int ret)477 static void _blame_on_callback(Git * git, CommonTask * task, int ret)
478 {
479 (void) git;
480
481 if(ret == 128)
482 _common_task_message(task, GTK_MESSAGE_ERROR,
483 _("This file is not managed by Git"), 1);
484 }
485
486
487 /* git_on_clone */
488 static void _clone_on_callback(Git * git, CommonTask * task, int ret);
489
_git_on_clone(gpointer data)490 static void _git_on_clone(gpointer data)
491 {
492 Git * git = data;
493 struct stat st;
494 char * argv[] = { GIT_GIT, "clone", "--", NULL, NULL };
495 char * dirname;
496 char * p;
497
498 if(git->filename == NULL || lstat(git->filename, &st) != 0)
499 return;
500 if(_common_prompt("Clone repository from:", &p) != GTK_RESPONSE_OK)
501 return;
502 dirname = S_ISDIR(st.st_mode) ? g_strdup(git->filename)
503 : g_path_get_dirname(git->filename);
504 argv[3] = p;
505 _git_add_task(git, "git clone", dirname, argv, _clone_on_callback);
506 g_free(dirname);
507 free(p);
508 }
509
_clone_on_callback(Git * git,CommonTask * task,int ret)510 static void _clone_on_callback(Git * git, CommonTask * task, int ret)
511 {
512 (void) git;
513
514 if(ret == 0)
515 _common_task_message(task, GTK_MESSAGE_INFO,
516 _("Repository cloned successfully"), 0);
517 else
518 _common_task_message(task, GTK_MESSAGE_ERROR,
519 _("Could not clone repository"), 1);
520 }
521
522
523 /* git_on_commit */
524 static void _commit_on_callback(Git * git, CommonTask * task, int ret);
525
_git_on_commit(gpointer data)526 static void _git_on_commit(gpointer data)
527 {
528 Git * git = data;
529 struct stat st;
530 gchar * dirname;
531 gchar * basename;
532 char * argv[] = { GIT_GIT, "commit", "--", NULL, NULL };
533
534 if(git->filename == NULL || lstat(git->filename, &st) != 0)
535 return;
536 dirname = S_ISDIR(st.st_mode) ? g_strdup(git->filename)
537 : g_path_get_dirname(git->filename);
538 basename = S_ISDIR(st.st_mode) ? g_strdup(".")
539 : g_path_get_basename(git->filename);
540 argv[3] = basename;
541 _git_add_task(git, "git commit", dirname, argv, _commit_on_callback);
542 g_free(basename);
543 g_free(dirname);
544 }
545
_commit_on_callback(Git * git,CommonTask * task,int ret)546 static void _commit_on_callback(Git * git, CommonTask * task, int ret)
547 {
548 (void) git;
549
550 if(ret != 0)
551 _common_task_message(task, GTK_MESSAGE_ERROR,
552 _("Could not commit the file or directory"), 1);
553 }
554
555
556 /* git_on_diff */
557 static void _diff_on_callback(Git * git, CommonTask * task, int ret);
558
_git_on_diff(gpointer data)559 static void _git_on_diff(gpointer data)
560 {
561 Git * git = data;
562 struct stat st;
563 gchar * dirname;
564 gchar * basename;
565 char * argv[] = { GIT_GIT, "diff", "--", NULL, NULL };
566
567 if(git->filename == NULL || lstat(git->filename, &st) != 0)
568 return;
569 dirname = S_ISDIR(st.st_mode) ? g_strdup(git->filename)
570 : g_path_get_dirname(git->filename);
571 basename = S_ISDIR(st.st_mode) ? NULL
572 : g_path_get_basename(git->filename);
573 argv[3] = basename;
574 _git_add_task(git, "git diff", dirname, argv, _diff_on_callback);
575 g_free(basename);
576 g_free(dirname);
577 }
578
_diff_on_callback(Git * git,CommonTask * task,int ret)579 static void _diff_on_callback(Git * git, CommonTask * task, int ret)
580 {
581 (void) git;
582 #ifdef notyet
583 GtkTextBuffer * tbuf;
584 #endif
585
586 if(ret != 0)
587 _common_task_message(task, GTK_MESSAGE_ERROR,
588 _("Could not diff the file or directory"), 1);
589 #ifdef notyet /* XXX race condition */
590 else
591 {
592 tbuf = _common_task_get_buffer(task);
593 if(gtk_text_buffer_get_char_count(tbuf) == 0)
594 _common_task_message(task, GTK_MESSAGE_INFO,
595 _("No difference was found"), 0);
596 }
597 #endif
598 }
599
600
601 /* git_on_init */
602 static void _init_on_callback(Git * git, CommonTask * task, int ret);
603
_git_on_init(gpointer data)604 static void _git_on_init(gpointer data)
605 {
606 Git * git = data;
607 struct stat st;
608 gchar * dirname;
609 char * argv[] = { GIT_GIT, "init", NULL };
610
611 if(git->filename == NULL || lstat(git->filename, &st) != 0)
612 return;
613 dirname = S_ISDIR(st.st_mode) ? g_strdup(git->filename)
614 : g_path_get_dirname(git->filename);
615 _git_add_task(git, "git pull", dirname, argv, _init_on_callback);
616 g_free(dirname);
617 }
618
_init_on_callback(Git * git,CommonTask * task,int ret)619 static void _init_on_callback(Git * git, CommonTask * task, int ret)
620 {
621 if(ret == 0)
622 /* refresh upon success */
623 git->helper->refresh(git->helper->browser);
624 else
625 _common_task_message(task, GTK_MESSAGE_ERROR,
626 _("Could not initialize repository"), 1);
627 }
628
629
630 /* git_on_log */
631 static void _log_on_callback(Git * git, CommonTask * task, int ret);
632
_git_on_log(gpointer data)633 static void _git_on_log(gpointer data)
634 {
635 Git * git = data;
636 struct stat st;
637 gchar * dirname;
638 gchar * basename;
639 char * argv[] = { GIT_GIT, "log", "--", NULL, NULL };
640
641 if(git->filename == NULL || lstat(git->filename, &st) != 0)
642 return;
643 dirname = S_ISDIR(st.st_mode) ? g_strdup(git->filename)
644 : g_path_get_dirname(git->filename);
645 basename = S_ISDIR(st.st_mode) ? NULL
646 : g_path_get_basename(git->filename);
647 argv[3] = basename;
648 _git_add_task(git, "git log", dirname, argv, _log_on_callback);
649 g_free(basename);
650 g_free(dirname);
651 }
652
_log_on_callback(Git * git,CommonTask * task,int ret)653 static void _log_on_callback(Git * git, CommonTask * task, int ret)
654 {
655 #ifdef notyet /* XXX race condition */
656 (void) git;
657 GtkTextBuffer * tbuf;
658
659 if(ret != 0)
660 return;
661 tbuf = _common_task_get_buffer(task);
662 if(gtk_text_buffer_get_char_count(tbuf) == 0)
663 _common_task_message(task, GTK_MESSAGE_ERROR,
664 _("This file is not managed by Git"), 1);
665 #else
666 (void) git;
667 (void) task;
668 (void) ret;
669 #endif
670 }
671
672
673 /* git_on_pull */
_git_on_pull(gpointer data)674 static void _git_on_pull(gpointer data)
675 {
676 Git * git = data;
677 struct stat st;
678 gchar * dirname;
679 gchar * basename;
680 char * argv[] = { GIT_GIT, "pull", "--", NULL, NULL };
681
682 if(git->filename == NULL || lstat(git->filename, &st) != 0)
683 return;
684 dirname = S_ISDIR(st.st_mode) ? g_strdup(git->filename)
685 : g_path_get_dirname(git->filename);
686 basename = S_ISDIR(st.st_mode) ? NULL
687 : g_path_get_basename(git->filename);
688 argv[3] = basename;
689 _git_add_task(git, "git pull", dirname, argv, NULL);
690 g_free(basename);
691 g_free(dirname);
692 }
693
694
695 /* git_on_push */
_git_on_push(gpointer data)696 static void _git_on_push(gpointer data)
697 {
698 Git * git = data;
699 struct stat st;
700 gchar * dirname;
701 gchar * basename;
702 char * argv[] = { GIT_GIT, "push", "--", NULL, NULL };
703
704 if(git->filename == NULL || lstat(git->filename, &st) != 0)
705 return;
706 dirname = S_ISDIR(st.st_mode) ? g_strdup(git->filename)
707 : g_path_get_dirname(git->filename);
708 basename = S_ISDIR(st.st_mode) ? NULL
709 : g_path_get_basename(git->filename);
710 argv[3] = basename;
711 _git_add_task(git, "git push", dirname, argv, NULL);
712 g_free(basename);
713 g_free(dirname);
714 }
715
716
717 /* git_on_reset */
_git_on_reset(gpointer data)718 static void _git_on_reset(gpointer data)
719 {
720 Git * git = data;
721 struct stat st;
722 gchar * dirname;
723 gchar * basename;
724 char * argv[] = { GIT_GIT, "reset", "--", NULL, NULL };
725
726 if(git->filename == NULL || lstat(git->filename, &st) != 0)
727 return;
728 dirname = S_ISDIR(st.st_mode) ? g_strdup(git->filename)
729 : g_path_get_dirname(git->filename);
730 basename = S_ISDIR(st.st_mode) ? NULL
731 : g_path_get_basename(git->filename);
732 argv[3] = basename;
733 _git_add_task(git, "git reset", dirname, argv, NULL);
734 g_free(basename);
735 g_free(dirname);
736 }
737
738
739 /* git_on_status */
_git_on_status(gpointer data)740 static void _git_on_status(gpointer data)
741 {
742 Git * git = data;
743 struct stat st;
744 gchar * dirname;
745 gchar * basename;
746 char * argv[] = { GIT_GIT, "status", "--", NULL, NULL };
747
748 if(git->filename == NULL || lstat(git->filename, &st) != 0)
749 return;
750 dirname = S_ISDIR(st.st_mode) ? g_strdup(git->filename)
751 : g_path_get_dirname(git->filename);
752 basename = S_ISDIR(st.st_mode) ? NULL
753 : g_path_get_basename(git->filename);
754 argv[3] = basename;
755 _git_add_task(git, "git status", dirname, argv, NULL);
756 g_free(basename);
757 g_free(dirname);
758 }
759