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