git: Use AnjutaColumnTextView in the Merge pane
[anjuta.git] / plugins / git / git-status-command.c
blobc5d145454396b75cfd9425b5bc111c1cb00a069a
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3 * anjuta
4 * Copyright (C) James Liggett 2008 <jrliggett@cox.net>
5 *
6 * anjuta is free software.
7 *
8 * You may redistribute it and/or modify it under the terms of the
9 * GNU General Public License, as published by the Free Software
10 * Foundation; either version 2 of the License, or (at your option)
11 * any later version.
13 * anjuta is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 * See the GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with anjuta. If not, write to:
20 * The Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor
22 * Boston, MA 02110-1301, USA.
25 #include "git-status-command.h"
27 #define STATUS_REGEX "(modified|new file|deleted|unmerged|both modified|both added|both deleted): (.*)"
28 #define UNTRACKED_FILES_REGEX "(?:#\\t)(.*)"
29 #define SECTION_COMMIT_REGEX "Changes to be committed:"
30 #define SECTION_NOT_UPDATED_REGEX "Changed but not updated:"
31 #define SECTION_UNTRACKED_REGEX "Untracked files:"
33 struct _GitStatusCommandPriv
35 GQueue *status_queue;
36 GHashTable *path_lookup_table;
37 GitStatusSections sections;
38 GitStatusSections current_section;
39 GRegex *current_section_regex;
40 GRegex *status_regex;
41 GRegex *untracked_files_regex;
42 GRegex *section_commit_regex;
43 GRegex *section_not_updated_regex;
44 GRegex *section_untracked_regex;
45 GFileMonitor *head_monitor;
46 GFileMonitor *index_monitor;
49 G_DEFINE_TYPE (GitStatusCommand, git_status_command, GIT_TYPE_COMMAND);
51 static guint
52 git_status_command_run (AnjutaCommand *command)
54 git_command_add_arg (GIT_COMMAND (command), "status");
56 return 0;
59 static void
60 git_status_command_handle_output (GitCommand *git_command, const gchar *output)
62 GitStatusCommand *self;
63 GMatchInfo *match_info;
64 GitStatus *status_object;
65 gchar *status;
66 gchar *path;
68 self = GIT_STATUS_COMMAND (git_command);
70 /* See if the section has changed */
71 if (g_regex_match (self->priv->section_commit_regex, output, 0, NULL))
73 self->priv->current_section = GIT_STATUS_SECTION_COMMIT;
74 self->priv->current_section_regex = self->priv->status_regex;
75 return;
77 else if (g_regex_match (self->priv->section_not_updated_regex, output, 0,
78 NULL))
80 self->priv->current_section = GIT_STATUS_SECTION_NOT_UPDATED;
81 self->priv->current_section_regex = self->priv->status_regex;
82 return;
84 else if (g_regex_match (self->priv->section_untracked_regex, output, 0,
85 NULL))
87 self->priv->current_section = GIT_STATUS_SECTION_UNTRACKED;
88 self->priv->current_section_regex = self->priv->untracked_files_regex;
89 return;
92 if (self->priv->sections & self->priv->current_section)
94 if (g_regex_match (self->priv->current_section_regex, output, 0,
95 &match_info))
97 if (self->priv->current_section_regex == self->priv->status_regex)
99 status = g_match_info_fetch (match_info, 1);
100 path = g_match_info_fetch (match_info, 2);
102 else
104 status = g_strdup ("untracked");
105 path = g_match_info_fetch (match_info, 1);
108 /* Git sometimes mentions paths twice in status output. This can
109 * happen, for example, where there is a conflict, in which case a
110 * path would show up as both "unmerged" and "modified." */
111 g_strchug (path);
113 if (!g_hash_table_lookup_extended (self->priv->path_lookup_table,
114 path, NULL, NULL))
116 status_object = git_status_new (path, status);
117 g_queue_push_tail (self->priv->status_queue, status_object);
118 g_hash_table_insert (self->priv->path_lookup_table,
119 g_strdup (path), NULL);
120 anjuta_command_notify_data_arrived (ANJUTA_COMMAND (git_command));
123 g_free (status);
124 g_free (path);
127 g_match_info_free (match_info);
131 static void
132 git_status_command_init (GitStatusCommand *self)
134 self->priv = g_new0 (GitStatusCommandPriv, 1);
135 self->priv->status_queue = g_queue_new ();
136 self->priv->path_lookup_table = g_hash_table_new_full (g_str_hash,
137 g_str_equal,
138 g_free, NULL);
139 self->priv->status_regex = g_regex_new (STATUS_REGEX, 0, 0, NULL);
140 self->priv->untracked_files_regex = g_regex_new (UNTRACKED_FILES_REGEX,
141 0, 0, NULL);
142 self->priv->section_commit_regex = g_regex_new (SECTION_COMMIT_REGEX, 0, 0,
143 NULL);
144 self->priv->section_not_updated_regex = g_regex_new (SECTION_NOT_UPDATED_REGEX,
145 0, 0, NULL);
146 self->priv->section_untracked_regex = g_regex_new (SECTION_UNTRACKED_REGEX,
147 0, 0, NULL);
150 static void
151 on_file_monitor_changed (GFileMonitor *monitor, GFile *file, GFile *other_file,
152 GFileMonitorEvent event, AnjutaCommand *command)
154 /* Handle created and modified events just to cover all possible cases.
155 * Sometimes git does some odd things... */
156 if (event == G_FILE_MONITOR_EVENT_CHANGED ||
157 event == G_FILE_MONITOR_EVENT_CREATED)
159 anjuta_command_start (command);
163 static gboolean
164 git_status_command_start_automatic_monitor (AnjutaCommand *command)
166 GitStatusCommand *self;
167 gchar *working_directory;
168 gchar *git_head_path;
169 gchar *git_index_path;
170 GFile *git_head_file;
171 GFile *git_index_file;
173 self = GIT_STATUS_COMMAND (command);
175 g_object_get (self, "working-directory", &working_directory, NULL);
177 /* Watch for changes to the HEAD file and the index file, so that we can
178 * at least detect commits and index changes. */
179 git_head_path = g_strjoin (G_DIR_SEPARATOR_S,
180 working_directory,
181 ".git",
182 "HEAD",
183 NULL);
184 git_index_path = g_strjoin (G_DIR_SEPARATOR_S,
185 working_directory,
186 ".git",
187 "index",
188 NULL);
189 git_head_file = g_file_new_for_path (git_head_path);
190 git_index_file = g_file_new_for_path (git_index_path);
191 self->priv->head_monitor = g_file_monitor_file (git_head_file, 0, NULL,
192 NULL);
193 self->priv->index_monitor = g_file_monitor_file (git_index_file, 0, NULL,
194 NULL);
196 g_signal_connect (G_OBJECT (self->priv->head_monitor), "changed",
197 G_CALLBACK (on_file_monitor_changed),
198 command);
200 g_signal_connect (G_OBJECT (self->priv->index_monitor), "changed",
201 G_CALLBACK (on_file_monitor_changed),
202 command);
204 g_free (git_head_path);
205 g_free (git_index_path);
206 g_object_unref (git_head_file);
207 g_object_unref (git_index_file);
209 return TRUE;
212 static void
213 git_status_command_stop_automatic_monitor (AnjutaCommand *command)
215 GitStatusCommand *self;
217 self = GIT_STATUS_COMMAND (command);
219 if (self->priv->head_monitor)
221 g_file_monitor_cancel (self->priv->head_monitor);
222 g_object_unref (self->priv->head_monitor);
223 self->priv->head_monitor = NULL;
226 if (self->priv->index_monitor)
228 g_file_monitor_cancel (self->priv->index_monitor);
229 g_object_unref (self->priv->index_monitor);
230 self->priv->index_monitor = NULL;
234 static void
235 git_status_command_clear_output (GitStatusCommand *self)
237 GList *current_output;
239 current_output = self->priv->status_queue->head;
241 while (current_output)
243 g_object_unref (current_output->data);
244 current_output = g_list_next (current_output);
247 g_queue_clear (self->priv->status_queue);
250 static void
251 git_status_command_data_arrived (AnjutaCommand *command)
253 git_status_command_clear_output (GIT_STATUS_COMMAND (command));
256 static void
257 git_status_command_finished (AnjutaCommand *command, guint return_code)
259 GitStatusCommand *self;
261 self = GIT_STATUS_COMMAND (command);
263 g_hash_table_remove_all (self->priv->path_lookup_table);
265 ANJUTA_COMMAND_CLASS (git_status_command_parent_class)->command_finished (command,
266 return_code);
269 static void
270 git_status_command_finalize (GObject *object)
272 GitStatusCommand *self;
273 GList *current_status;
275 self = GIT_STATUS_COMMAND (object);
276 current_status = self->priv->status_queue->head;
278 git_status_command_clear_output (self);
279 git_status_command_stop_automatic_monitor (ANJUTA_COMMAND (self));
281 g_queue_free (self->priv->status_queue);
282 g_hash_table_destroy (self->priv->path_lookup_table);
283 g_regex_unref (self->priv->status_regex);
284 g_regex_unref (self->priv->untracked_files_regex);
285 g_regex_unref (self->priv->section_commit_regex);
286 g_regex_unref (self->priv->section_not_updated_regex);
287 g_regex_unref (self->priv->section_untracked_regex);
289 g_free (self->priv);
291 G_OBJECT_CLASS (git_status_command_parent_class)->finalize (object);
294 static void
295 git_status_command_class_init (GitStatusCommandClass *klass)
297 GObjectClass* object_class = G_OBJECT_CLASS (klass);
298 GitCommandClass* parent_class = GIT_COMMAND_CLASS (klass);
299 AnjutaCommandClass* command_class = ANJUTA_COMMAND_CLASS (klass);
301 object_class->finalize = git_status_command_finalize;
302 parent_class->output_handler = git_status_command_handle_output;
303 command_class->run = git_status_command_run;
304 command_class->data_arrived = git_status_command_data_arrived;
305 command_class->command_finished = git_status_command_finished;
306 command_class->start_automatic_monitor = git_status_command_start_automatic_monitor;
307 command_class->stop_automatic_monitor = git_status_command_stop_automatic_monitor;
311 GitStatusCommand *
312 git_status_command_new (const gchar *working_directory,
313 GitStatusSections sections)
315 GitStatusCommand *self;
317 self = g_object_new (GIT_TYPE_STATUS_COMMAND,
318 "working-directory", working_directory,
319 "single-line-output", TRUE,
320 NULL);
322 self->priv->sections = sections;
324 return self;
327 GQueue *
328 git_status_command_get_status_queue (GitStatusCommand *self)
330 return self->priv->status_queue;