From 0681b47521b7b1ff84f560201f521a52fd1c533a Mon Sep 17 00:00:00 2001 From: James Liggett Date: Tue, 14 Dec 2010 01:28:41 -0800 Subject: [PATCH] git: Process log data asynchronously --- plugins/git/Makefile.am | 5 +- plugins/git/git-log-command.c | 199 ++++++++--------------------- plugins/git/git-log-command.h | 1 + plugins/git/git-log-data-command.c | 250 +++++++++++++++++++++++++++++++++++++ plugins/git/git-log-data-command.h | 61 +++++++++ 5 files changed, 365 insertions(+), 151 deletions(-) create mode 100644 plugins/git/git-log-data-command.c create mode 100644 plugins/git/git-log-data-command.h diff --git a/plugins/git/Makefile.am b/plugins/git/Makefile.am index d986b52fa..46aa34f2c 100644 --- a/plugins/git/Makefile.am +++ b/plugins/git/Makefile.am @@ -217,7 +217,10 @@ libanjuta_git_la_SOURCES = \ git-rebase-pane.c \ git-rebase-pane.h \ git-log-pane.c \ - git-log-pane.h + git-log-pane.h \ + git-log-data-command.c \ + git-log-data-command.h + libanjuta_git_la_LDFLAGS = $(ANJUTA_PLUGIN_LDFLAGS) diff --git a/plugins/git/git-log-command.c b/plugins/git/git-log-command.c index a63526e6c..c93794020 100644 --- a/plugins/git/git-log-command.c +++ b/plugins/git/git-log-command.c @@ -22,24 +22,13 @@ * Boston, MA 02110-1301, USA. */ -#define COMMIT_REGEX "^commit ([[:xdigit:]]{40})" -#define PARENT_REGEX "^parents (.*)" -#define AUTHOR_REGEX "^author (.*)" -#define TIME_REGEX "^time (\\d*)" -#define SHORT_LOG_REGEX "^(?:short log) (.*)" - #include "git-log-command.h" struct _GitLogCommandPriv { - GQueue *output_queue; - GHashTable *revisions; - GitRevision *current_revision; - GRegex *commit_regex; - GRegex *parent_regex; - GRegex *author_regex; - GRegex *time_regex; - GRegex *short_log_regex; + GitLogDataCommand *data_command; + guint return_code; + gchar *branch; gchar *path; @@ -55,44 +44,43 @@ struct _GitLogCommandPriv G_DEFINE_TYPE (GitLogCommand, git_log_command, GIT_TYPE_COMMAND); static void +on_data_command_data_arrived (AnjutaCommand *command, GitLogCommand *self) +{ + anjuta_command_notify_data_arrived (command); +} + +static void +on_data_command_finished (AnjutaCommand *command, guint return_code, + GitLogCommand *self) +{ + ANJUTA_COMMAND_CLASS (git_log_command_parent_class)->notify_complete (ANJUTA_COMMAND (self), + self->priv->return_code); +} + +static void git_log_command_init (GitLogCommand *self) { self->priv = g_new0 (GitLogCommandPriv, 1); - self->priv->output_queue = g_queue_new (); - self->priv->revisions = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, g_object_unref); - self->priv->commit_regex = g_regex_new (COMMIT_REGEX, 0, 0, NULL); - self->priv->parent_regex = g_regex_new (PARENT_REGEX, 0, 0, NULL); - self->priv->author_regex = g_regex_new (AUTHOR_REGEX, 0, 0, NULL); - self->priv->time_regex = g_regex_new (TIME_REGEX, 0, 0, NULL); - self->priv->short_log_regex = g_regex_new (SHORT_LOG_REGEX, 0, 0, NULL); + + self->priv->data_command = git_log_data_command_new (); + + g_signal_connect (G_OBJECT (self->priv->data_command), "data-arrived", + G_CALLBACK (on_data_command_data_arrived), + self); + + g_signal_connect (G_OBJECT (self->priv->data_command), "command-finished", + G_CALLBACK (on_data_command_finished), + self); } static void git_log_command_finalize (GObject *object) { GitLogCommand *self; - GList *current_output; self = GIT_LOG_COMMAND (object); - current_output = self->priv->output_queue->head; - - while (current_output) - { - g_object_unref (current_output->data); - current_output = g_list_next (current_output); - } - - g_queue_free (self->priv->output_queue); - g_hash_table_destroy (self->priv->revisions); - g_regex_unref (self->priv->commit_regex); - g_regex_unref (self->priv->parent_regex); - g_regex_unref (self->priv->author_regex); - g_regex_unref (self->priv->time_regex); - g_regex_unref (self->priv->short_log_regex); - g_free (self->priv->branch); - g_free (self->priv->path); + g_object_unref (self->priv->data_command); g_free (self->priv->author); g_free (self->priv->grep); g_free (self->priv->since_date); @@ -177,127 +165,37 @@ git_log_command_run (AnjutaCommand *command) git_command_add_arg (GIT_COMMAND (command), "--"); git_command_add_arg (GIT_COMMAND (command), self->priv->path); } + + /* Start the data processing task */ + anjuta_command_start (ANJUTA_COMMAND (self->priv->data_command)); return 0; } static void -git_log_command_handle_output (GitCommand *git_command, const gchar *output) +git_log_command_notify_complete (AnjutaCommand *command, guint return_code) { GitLogCommand *self; - GMatchInfo *commit_match_info; - GMatchInfo *parent_match_info; - GMatchInfo *author_match_info; - GMatchInfo *time_match_info; - GMatchInfo *short_log_match_info; - gchar *commit_sha; - gchar *parents; - gchar **parent_shas; - gint i; - GitRevision *parent_revision; - gchar *author; - gchar *time; - gchar *short_log; - - self = GIT_LOG_COMMAND (git_command); - commit_match_info = NULL; - parent_match_info = NULL; - author_match_info = NULL; - time_match_info = NULL; - short_log_match_info = NULL; - - /* Entries are delimited by the hex value 0x0c */ - if (*output == 0x0c && self->priv->current_revision) - { - g_queue_push_tail (self->priv->output_queue, - self->priv->current_revision); - anjuta_command_notify_data_arrived (ANJUTA_COMMAND (git_command)); - } - - if (g_regex_match (self->priv->commit_regex, output, 0, &commit_match_info)) - { - commit_sha = g_match_info_fetch (commit_match_info, 1); - - self->priv->current_revision = g_hash_table_lookup (self->priv->revisions, - commit_sha); - - if (!self->priv->current_revision) - { - self->priv->current_revision = git_revision_new (); - git_revision_set_sha (self->priv->current_revision, commit_sha); - g_hash_table_insert (self->priv->revisions, g_strdup (commit_sha), - g_object_ref (self->priv->current_revision)); - } - - g_free (commit_sha); - } - else if (g_regex_match (self->priv->parent_regex, output, 0, - &parent_match_info)) - { - parents = g_match_info_fetch (parent_match_info, 1); - parent_shas = g_strsplit (parents, " ", -1); - - for (i = 0; parent_shas[i]; i++) - { - parent_revision = g_hash_table_lookup (self->priv->revisions, - parent_shas[i]); - - if (!parent_revision) - { - parent_revision = git_revision_new (); - git_revision_set_sha (parent_revision, parent_shas[i]); - g_hash_table_insert (self->priv->revisions, - g_strdup (parent_shas[i]), - g_object_ref (parent_revision)); - } - - git_revision_add_child (parent_revision, - self->priv->current_revision); - } - - g_free (parents); - g_strfreev (parent_shas); - } - else if (g_regex_match (self->priv->author_regex, output, 0, - &author_match_info)) - { - author = g_match_info_fetch (author_match_info, 1); - git_revision_set_author (self->priv->current_revision, author); - - g_free (author); - } - else if (g_regex_match (self->priv->time_regex, output, 0, - &time_match_info)) - { - time = g_match_info_fetch (time_match_info, 1); - git_revision_set_date (self->priv->current_revision, atol (time)); - - g_free (time); - } - else if (g_regex_match (self->priv->short_log_regex, output, 0, - &short_log_match_info)) - { - short_log = g_match_info_fetch (short_log_match_info, 1); - git_revision_set_short_log (self->priv->current_revision, short_log); - - g_free (short_log); - } - - if (commit_match_info) - g_match_info_free (commit_match_info); + self = GIT_LOG_COMMAND (command); - if (parent_match_info) - g_match_info_free (parent_match_info); + /* Send an empty string to the data processing command so that it knows + * to stop when it's done processing data. The command will finish when + * the processing thread finishes, and not when git stops executing */ + git_log_data_command_push_line (self->priv->data_command, ""); - if (author_match_info) - g_match_info_free (author_match_info); + /* Use the git return code */ + self->priv->return_code = return_code; +} - if (time_match_info) - g_match_info_free (time_match_info); +static void +git_log_command_handle_output (GitCommand *git_command, const gchar *output) +{ + GitLogCommand *self; + + self = GIT_LOG_COMMAND (git_command); - if (short_log_match_info) - g_match_info_free (short_log_match_info); + git_log_data_command_push_line (self->priv->data_command, output); } static void @@ -310,6 +208,7 @@ git_log_command_class_init (GitLogCommandClass *klass) object_class->finalize = git_log_command_finalize; parent_class->output_handler = git_log_command_handle_output; command_class->run = git_log_command_run; + command_class->notify_complete = git_log_command_notify_complete; } @@ -343,5 +242,5 @@ git_log_command_new (const gchar *working_directory, GQueue * git_log_command_get_output_queue (GitLogCommand *self) { - return self->priv->output_queue; + return git_log_data_command_get_output (self->priv->data_command); } diff --git a/plugins/git/git-log-command.h b/plugins/git/git-log-command.h index 5d80600fd..a708d71a6 100644 --- a/plugins/git/git-log-command.h +++ b/plugins/git/git-log-command.h @@ -28,6 +28,7 @@ #include #include #include "git-command.h" +#include "git-log-data-command.h" #include "git-revision.h" G_BEGIN_DECLS diff --git a/plugins/git/git-log-data-command.c b/plugins/git/git-log-data-command.c new file mode 100644 index 000000000..e9821c61f --- /dev/null +++ b/plugins/git/git-log-data-command.c @@ -0,0 +1,250 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ +/* + * anjuta + * Copyright (C) James Liggett 2010 + * + * anjuta is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * anjuta is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "git-log-data-command.h" + +#define COMMIT_REGEX "^commit ([[:xdigit:]]{40})" +#define PARENT_REGEX "^parents (.*)" +#define AUTHOR_REGEX "^author (.*)" +#define TIME_REGEX "^time (\\d*)" +#define SHORT_LOG_REGEX "^(?:short log) (.*)" + +struct _GitLogDataCommandPriv +{ + GAsyncQueue *input_queue; + GQueue *output_queue; + GHashTable *revisions; + GitRevision *current_revision; + GRegex *commit_regex; + GRegex *parent_regex; + GRegex *author_regex; + GRegex *time_regex; + GRegex *short_log_regex; +}; + +G_DEFINE_TYPE (GitLogDataCommand, git_log_data_command, + ANJUTA_TYPE_ASYNC_COMMAND); + +static void +git_log_data_command_init (GitLogDataCommand *self) +{ + self->priv = g_new0 (GitLogDataCommandPriv, 1); + self->priv->input_queue = g_async_queue_new_full (g_free); + self->priv->output_queue = g_queue_new (); + self->priv->revisions = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, g_object_unref); + self->priv->commit_regex = g_regex_new (COMMIT_REGEX, 0, 0, NULL); + self->priv->parent_regex = g_regex_new (PARENT_REGEX, 0, 0, NULL); + self->priv->author_regex = g_regex_new (AUTHOR_REGEX, 0, 0, NULL); + self->priv->time_regex = g_regex_new (TIME_REGEX, 0, 0, NULL); + self->priv->short_log_regex = g_regex_new (SHORT_LOG_REGEX, 0, 0, NULL); +} + +static void +git_log_data_command_finalize (GObject *object) +{ + GitLogDataCommand *self; + GList *current_output; + + self = GIT_LOG_DATA_COMMAND (object); + + g_async_queue_unref (self->priv->input_queue); + current_output = self->priv->output_queue->head; + + while (current_output) + { + g_object_unref (current_output->data); + current_output = g_list_next (current_output); + } + + g_queue_free (self->priv->output_queue); + g_hash_table_destroy (self->priv->revisions); + g_regex_unref (self->priv->commit_regex); + g_regex_unref (self->priv->parent_regex); + g_regex_unref (self->priv->author_regex); + g_regex_unref (self->priv->time_regex); + g_regex_unref (self->priv->short_log_regex); + + G_OBJECT_CLASS (git_log_data_command_parent_class)->finalize (object); +} + +static guint +git_log_data_command_run (AnjutaCommand *command) +{ + GitLogDataCommand *self; + gchar *line; + GMatchInfo *commit_match_info; + GMatchInfo *parent_match_info; + GMatchInfo *author_match_info; + GMatchInfo *time_match_info; + GMatchInfo *short_log_match_info; + gchar *commit_sha; + gchar *parents; + gchar **parent_shas; + gint i; + GitRevision *parent_revision; + gchar *author; + gchar *time; + gchar *short_log; + + self = GIT_LOG_DATA_COMMAND (command); + + while ((line = g_async_queue_pop (self->priv->input_queue))) + { + /* An empty string means there's nothing left to process */ + if (g_utf8_strlen (line, -1) == 0) + { + g_free (line); + break; + } + + commit_match_info = NULL; + parent_match_info = NULL; + author_match_info = NULL; + time_match_info = NULL; + short_log_match_info = NULL; + + /* Entries are delimited by the hex value 0x0c */ + if (*line == 0x0c && self->priv->current_revision) + { + anjuta_async_command_lock (ANJUTA_ASYNC_COMMAND (command)); + g_queue_push_tail (self->priv->output_queue, + self->priv->current_revision); + anjuta_async_command_unlock (ANJUTA_ASYNC_COMMAND (command)); + + anjuta_command_notify_data_arrived (command); + } + + if (g_regex_match (self->priv->commit_regex, line, 0, &commit_match_info)) + { + commit_sha = g_match_info_fetch (commit_match_info, 1); + + self->priv->current_revision = g_hash_table_lookup (self->priv->revisions, + commit_sha); + + if (!self->priv->current_revision) + { + self->priv->current_revision = git_revision_new (); + git_revision_set_sha (self->priv->current_revision, commit_sha); + g_hash_table_insert (self->priv->revisions, g_strdup (commit_sha), + g_object_ref (self->priv->current_revision)); + } + + g_free (commit_sha); + } + else if (g_regex_match (self->priv->parent_regex, line, 0, + &parent_match_info)) + { + parents = g_match_info_fetch (parent_match_info, 1); + parent_shas = g_strsplit (parents, " ", -1); + + for (i = 0; parent_shas[i]; i++) + { + parent_revision = g_hash_table_lookup (self->priv->revisions, + parent_shas[i]); + + if (!parent_revision) + { + parent_revision = git_revision_new (); + git_revision_set_sha (parent_revision, parent_shas[i]); + g_hash_table_insert (self->priv->revisions, + g_strdup (parent_shas[i]), + g_object_ref (parent_revision)); + } + + git_revision_add_child (parent_revision, + self->priv->current_revision); + } + + g_free (parents); + g_strfreev (parent_shas); + } + else if (g_regex_match (self->priv->author_regex, line, 0, + &author_match_info)) + { + author = g_match_info_fetch (author_match_info, 1); + git_revision_set_author (self->priv->current_revision, author); + + g_free (author); + } + else if (g_regex_match (self->priv->time_regex, line, 0, + &time_match_info)) + { + time = g_match_info_fetch (time_match_info, 1); + git_revision_set_date (self->priv->current_revision, atol (time)); + + g_free (time); + } + else if (g_regex_match (self->priv->short_log_regex, line, 0, + &short_log_match_info)) + { + short_log = g_match_info_fetch (short_log_match_info, 1); + git_revision_set_short_log (self->priv->current_revision, short_log); + + g_free (short_log); + } + + if (commit_match_info) + g_match_info_free (commit_match_info); + + if (parent_match_info) + g_match_info_free (parent_match_info); + + if (author_match_info) + g_match_info_free (author_match_info); + + if (time_match_info) + g_match_info_free (time_match_info); + + if (short_log_match_info) + g_match_info_free (short_log_match_info); + + g_free (line); + } + + return 0; +} + +static void +git_log_data_command_class_init (GitLogDataCommandClass *klass) +{ + GObjectClass* object_class = G_OBJECT_CLASS (klass); + AnjutaCommandClass *command_class = ANJUTA_COMMAND_CLASS (klass); + + object_class->finalize = git_log_data_command_finalize; + command_class->run = git_log_data_command_run; +} + +GitLogDataCommand * +git_log_data_command_new (void) +{ + return g_object_new (GIT_TYPE_LOG_DATA_COMMAND, NULL); +} + +GQueue * +git_log_data_command_get_output (GitLogDataCommand *self) +{ + return self->priv->output_queue; +} + +void +git_log_data_command_push_line (GitLogDataCommand *self, const gchar *line) +{ + g_async_queue_push (self->priv->input_queue, g_strdup (line)); +} diff --git a/plugins/git/git-log-data-command.h b/plugins/git/git-log-data-command.h new file mode 100644 index 000000000..f7f73e577 --- /dev/null +++ b/plugins/git/git-log-data-command.h @@ -0,0 +1,61 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ +/* + * anjuta + * Copyright (C) James Liggett 2010 + * + * anjuta is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * anjuta is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef _GIT_LOG_DATA_COMMAND_H_ +#define _GIT_LOG_DATA_COMMAND_H_ + +#include +#include +#include +#include "git-revision.h" + +G_BEGIN_DECLS + +#define GIT_TYPE_LOG_DATA_COMMAND (git_log_data_command_get_type ()) +#define GIT_LOG_DATA_COMMAND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIT_TYPE_LOG_DATA_COMMAND, GitLogDataCommand)) +#define GIT_LOG_DATA_COMMAND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIT_TYPE_LOG_DATA_COMMAND, GitLogDataCommandClass)) +#define GIT_IS_LOG_DATA_COMMAND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIT_TYPE_LOG_DATA_COMMAND)) +#define GIT_IS_LOG_DATA_COMMAND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIT_TYPE_LOG_DATA_COMMAND)) +#define GIT_LOG_DATA_COMMAND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIT_TYPE_LOG_DATA_COMMAND, GitLogDataCommandClass)) + +typedef struct _GitLogDataCommandClass GitLogDataCommandClass; +typedef struct _GitLogDataCommand GitLogDataCommand; +typedef struct _GitLogDataCommandPriv GitLogDataCommandPriv; + +struct _GitLogDataCommandClass +{ + AnjutaAsyncCommandClass parent_class; +}; + +struct _GitLogDataCommand +{ + AnjutaAsyncCommand parent_instance; + + GitLogDataCommandPriv *priv; +}; + +GType git_log_data_command_get_type (void) G_GNUC_CONST; +GitLogDataCommand *git_log_data_command_new (void); +GQueue *git_log_data_command_get_output (GitLogDataCommand *self); +void git_log_data_command_push_line (GitLogDataCommand *self, + const gchar *line); + +G_END_DECLS + +#endif /* _GIT_LOG_DATA_COMMAND_H_ */ -- 2.11.4.GIT