[gitg] Implemented 'amend' commit



commit c9815fb2aaf17bbff1e2a61e96c8da2ecdd60191
Author: Jesse van den Kieboom <jessevdk gnome org>
Date:   Sun Jul 5 14:21:45 2009 +0200

    Implemented 'amend' commit

 gitg/gitg-commit-view.c |   41 +++++++++++-
 gitg/gitg-commit.c      |  169 ++++++++++++++++++++++++++++++++++++++++++++---
 gitg/gitg-commit.h      |    4 +-
 gitg/gitg-window.ui     |   51 +++++++++++---
 4 files changed, 240 insertions(+), 25 deletions(-)
---
diff --git a/gitg/gitg-commit-view.c b/gitg/gitg-commit-view.c
index 6f90062..0ee44c3 100644
--- a/gitg/gitg-commit-view.c
+++ b/gitg/gitg-commit-view.c
@@ -72,6 +72,7 @@ struct _GitgCommitViewPrivate
 	GtkSourceView *changes_view;
 	GtkTextView *comment_view;
 	GtkCheckButton *check_button_signed_off_by;
+	GtkCheckButton *check_button_amend;
 	
 	GtkHScale *hscale_context;
 	gint context_size;
@@ -123,6 +124,7 @@ static void on_revert_changes(GtkAction *action, GitgCommitView *view);
 static void on_ignore_file(GtkAction *action, GitgCommitView *view);
 static void on_unstage_changes(GtkAction *action, GitgCommitView *view);
 
+static void on_check_button_amend_toggled (GtkToggleButton *button, GitgCommitView *view);
 
 static void
 gitg_commit_view_finalize(GObject *object)
@@ -936,7 +938,8 @@ gitg_commit_view_parser_finished(GtkBuildable *buildable, GtkBuilder *builder)
 	self->priv->changes_view = GTK_SOURCE_VIEW(gtk_builder_get_object(builder, "source_view_changes"));
 	self->priv->comment_view = GTK_TEXT_VIEW(gtk_builder_get_object(builder, "text_view_comment"));
 	self->priv->check_button_signed_off_by = GTK_CHECK_BUTTON(gtk_builder_get_object(builder, "check_button_signed_off_by"));
-
+	self->priv->check_button_amend = GTK_CHECK_BUTTON(gtk_builder_get_object(builder, "check_button_amend"));
+	
 	GitgPreferences *preferences = gitg_preferences_get_default();
 	
 	gitg_data_binding_new(preferences, "message-show-right-margin",
@@ -1004,6 +1007,11 @@ gitg_commit_view_parser_finished(GtkBuildable *buildable, GtkBuilder *builder)
 	g_signal_connect(gtk_builder_get_object(builder, "button_commit"), "clicked", G_CALLBACK(on_commit_clicked), self);
 	
 	g_signal_connect(self->priv->hscale_context, "value-changed", G_CALLBACK(on_context_value_changed), self);
+	
+	g_signal_connect (self->priv->check_button_amend,
+	                  "toggled",
+	                  G_CALLBACK (on_check_button_amend_toggled),
+	                  self);
 }
 
 static void
@@ -1433,10 +1441,11 @@ on_commit_clicked(GtkButton *button, GitgCommitView *view)
 	}
 	
 	gboolean signoff = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(view->priv->check_button_signed_off_by));
-	
+	gboolean amend = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (view->priv->check_button_amend));
+
 	GError *error = NULL;
 	
-	if (!gitg_commit_commit(view->priv->commit, comment, signoff, &error))
+	if (!gitg_commit_commit(view->priv->commit, comment, signoff, amend, &error))
 	{
 		if (error && error->domain == GITG_COMMIT_ERROR && error->code == GITG_COMMIT_ERROR_SIGNOFF)
 			show_error(view, _("Your user name or email could not be retrieved for use in the sign off message"));
@@ -1449,6 +1458,8 @@ on_commit_clicked(GtkButton *button, GitgCommitView *view)
 	else
 	{
 		gtk_text_buffer_set_text(gtk_text_view_get_buffer(view->priv->comment_view), "", -1);
+		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (view->priv->check_button_amend), FALSE);
+		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (view->priv->check_button_signed_off_by), FALSE);
 	}
 	
 	g_free(comment);
@@ -1681,3 +1692,27 @@ on_changes_view_popup_menu(GtkTextView *textview, GtkMenu *menu, GitgCommitView
 		gtk_menu_shell_append(GTK_MENU_SHELL(menu), revert);
 	}
 }
+
+static void
+on_check_button_amend_toggled (GtkToggleButton *button, GitgCommitView *view)
+{
+	gboolean active = gtk_toggle_button_get_active (button);
+	GtkTextBuffer *buffer = gtk_text_view_get_buffer (view->priv->comment_view);
+	GtkTextIter start;
+	GtkTextIter end;
+	
+	gtk_text_buffer_get_bounds (buffer, &start, &end);
+	
+	if (active && gtk_text_iter_compare (&start, &end) == 0)
+	{
+		// Get last commit message
+		gchar *message = gitg_commit_amend_message (view->priv->commit);
+		
+		if (message)
+		{
+			gtk_text_buffer_set_text (buffer, message, -1);
+		}
+		
+		g_free (message);
+	}
+}
diff --git a/gitg/gitg-commit.c b/gitg/gitg-commit.c
index ea4cf5d..4382ac0 100644
--- a/gitg/gitg-commit.c
+++ b/gitg/gitg-commit.c
@@ -710,8 +710,69 @@ get_signed_off_line(GitgCommit *commit)
 	return ret;
 }
 
+static void
+on_commit_tree_update (GitgRunner *runner, gchar **lines, GString *buffer)
+{
+	while (lines && *lines)
+	{
+		if (buffer->len != 0)
+		{
+			g_string_append_c (buffer, '\n');
+		}
+		
+		g_string_append (buffer, *lines);
+		++lines;
+	}
+}
+
+static void
+set_amend_environment (GitgCommit *commit, GitgRunner *runner)
+{
+	gchar **out;
+	
+	out = gitg_repository_command_with_outputv (commit->priv->repository,
+	                                            NULL,
+	                                            "cat-file",
+	                                            "commit",
+	                                            "HEAD",
+	                                            NULL);
+
+	// Parse author
+	GRegex *r = g_regex_new ("^author (.*) <([^>]*)> ([0-9]+.*)$",
+	                         G_REGEX_CASELESS,
+	                         0,
+	                         NULL);
+
+	GMatchInfo *info = NULL;
+	gchar **ptr = out;
+	
+	while (ptr && *ptr)
+	{
+		if (g_regex_match (r, *ptr, 0, &info))
+		{
+			gchar *name = g_match_info_fetch (info, 1);
+			gchar *email = g_match_info_fetch (info, 2);
+			gchar *date = g_match_info_fetch (info, 3);
+
+			gitg_runner_add_environment (runner, "GIT_AUTHOR_NAME", name);
+			gitg_runner_add_environment (runner, "GIT_AUTHOR_EMAIL", email);
+			gitg_runner_add_environment (runner, "GIT_AUTHOR_DATE", date);
+			
+			g_free (name);
+			g_free (email);
+			g_free (date);
+		
+			break;
+		}
+		
+		++ptr;
+	}
+	
+	g_strfreev (out);
+}
+
 static gboolean 
-commit_tree(GitgCommit *commit, gchar const *tree, gchar const *comment, gboolean signoff, gchar **ref, GError **error)
+commit_tree(GitgCommit *commit, gchar const *tree, gchar const *comment, gboolean signoff, gboolean amend, gchar **ref, GError **error)
 {
 	gchar *fullcomment;
 	
@@ -734,21 +795,47 @@ commit_tree(GitgCommit *commit, gchar const *tree, gchar const *comment, gboolea
 		fullcomment = g_strdup(comment);
 	}
 	
-	gchar *head = gitg_repository_parse_ref(commit->priv->repository, "HEAD");
-
-	gchar **lines = gitg_repository_command_with_input_and_outputv(commit->priv->repository, fullcomment, error, "commit-tree", tree, head ? "-p" : NULL, head, NULL);
+	gchar *head;
+	
+	if (amend)
+	{
+		head = gitg_repository_parse_ref(commit->priv->repository, "HEAD^");
+	}
+	else
+	{
+		head = gitg_repository_parse_ref(commit->priv->repository, "HEAD");
+	}
+	
+	GitgRunner *runner = gitg_runner_new_synchronized (1000);
+	GString *buffer = g_string_new ("");
+	
+	if (amend)
+	{
+		set_amend_environment (commit, runner);
+	}
+	
+	g_signal_connect (runner, "update", G_CALLBACK (on_commit_tree_update), buffer);
+	gitg_repository_run_command_with_inputv (commit->priv->repository,
+	                                         runner,
+	                                         fullcomment,
+	                                         error,
+	                                         "commit-tree",
+	                                         tree,
+	                                         head ? "-p" : NULL,
+	                                         head,
+	                                         NULL);
 
 	g_free(head);
 	g_free(fullcomment);
+	g_object_unref (runner);
 
-	if (!lines || strlen(*lines) != HASH_SHA_SIZE)
+	if (buffer->len != HASH_SHA_SIZE)
 	{
-		g_strfreev(lines);
+		g_string_free(buffer, TRUE);
 		return FALSE;
 	}
 	
-	*ref = g_strdup(*lines);
-	g_strfreev(lines);
+	*ref = g_string_free (buffer, FALSE);
 	return TRUE;
 }
 
@@ -760,16 +847,24 @@ update_ref(GitgCommit *commit, gchar const *ref, gchar const *subject, GError **
 }
 
 gboolean
-gitg_commit_commit(GitgCommit *commit, gchar const *comment, gboolean signoff, GError **error)
+gitg_commit_commit(GitgCommit *commit, gchar const *comment, gboolean signoff, gboolean amend, GError **error)
 {
 	g_return_val_if_fail(GITG_IS_COMMIT(commit), FALSE);
 	
 	gchar *tree;
+
 	if (!write_tree(commit, &tree, error))
 		return FALSE;
 	
+	gchar *path = g_build_filename (gitg_repository_get_path (commit->priv->repository),
+	                                ".git",
+	                                "COMMIT_EDITMSG",
+	                                NULL);
+	g_file_set_contents (path, comment, -1, NULL);\
+	g_free (path);
+	
 	gchar *ref;
-	gboolean ret = commit_tree(commit, tree, comment, signoff, &ref, error);
+	gboolean ret = commit_tree(commit, tree, comment, signoff, amend, &ref, error);
 	g_free(tree);
 	
 	if (!ret)
@@ -889,3 +984,57 @@ gitg_commit_find_changed_file(GitgCommit *commit, GFile *file)
 		return NULL;
 	}
 }
+
+gchar *
+gitg_commit_amend_message (GitgCommit *commit)
+{
+	g_return_val_if_fail (GITG_IS_COMMIT (commit), NULL);
+	
+	gchar **out;
+	
+	out = gitg_repository_command_with_outputv (commit->priv->repository,
+	                                            NULL,
+	                                            "cat-file",
+	                                            "commit",
+	                                            "HEAD",
+	                                            NULL);
+
+	gchar *ret = NULL;
+	
+	if (out)
+	{
+		gchar **ptr = out;
+		
+		while (*ptr)
+		{
+			if (!**ptr)
+			{
+				++ptr;
+				break;
+			}
+			
+			++ptr;
+		}
+		
+		if (*ptr && **ptr)
+		{
+			GString *buffer = g_string_new ("");
+			
+			while (*ptr)
+			{
+				if (buffer->len != 0)
+				{
+					g_string_append_c (buffer, '\n');
+				}
+				
+				g_string_append (buffer, *ptr);
+				++ptr;
+			}
+			
+			ret = g_string_free (buffer, FALSE);
+		}
+	}
+	
+	g_strfreev (out);
+	return ret;
+}
diff --git a/gitg/gitg-commit.h b/gitg/gitg-commit.h
index d37fc2f..600ee5c 100644
--- a/gitg/gitg-commit.h
+++ b/gitg/gitg-commit.h
@@ -72,13 +72,15 @@ gboolean gitg_commit_stage(GitgCommit *commit, GitgChangedFile *file, gchar cons
 gboolean gitg_commit_unstage(GitgCommit *commit, GitgChangedFile *file, gchar const *hunk, GError **error);
 
 gboolean gitg_commit_has_changes(GitgCommit *commit);
-gboolean gitg_commit_commit(GitgCommit *commit, gchar const *comment, gboolean signoff, GError **error);
+gboolean gitg_commit_commit(GitgCommit *commit, gchar const *comment, gboolean signoff, gboolean amend, GError **error);
 
 gboolean gitg_commit_revert(GitgCommit *commit, GitgChangedFile *file, gchar const *hunk, GError **error);
 gboolean gitg_commit_add_ignore(GitgCommit *commit, GitgChangedFile *file, GError **error);
 
 GitgChangedFile *gitg_commit_find_changed_file(GitgCommit *commit, GFile *file);
 
+gchar *gitg_commit_amend_message (GitgCommit *commit);
+
 G_END_DECLS
 
 #endif /* __GITG_COMMIT_H__ */
diff --git a/gitg/gitg-window.ui b/gitg/gitg-window.ui
index 99b4136..125a3b6 100644
--- a/gitg/gitg-window.ui
+++ b/gitg/gitg-window.ui
@@ -708,27 +708,56 @@
                                 <property name="visible">True</property>
                                 <property name="spacing">3</property>
                                 <child>
-                                  <object class="GtkCheckButton" id="check_button_signed_off_by">
-                                    <property name="label" translatable="yes">Add signed-off-by</property>
-                                    <property name="can_focus">True</property>
-                                    <property name="receives_default">False</property>
-                                    <property name="draw_indicator">True</property>
+                                  <object class="GtkVBox" id="vbox1">
+                                    <property name="visible">True</property>
+                                    <property name="orientation">vertical</property>
+                                    <child>
+                                      <object class="GtkCheckButton" id="check_button_amend">
+                                        <property name="label" translatable="yes">Amend</property>
+                                        <property name="can_focus">True</property>
+                                        <property name="receives_default">False</property>
+                                        <property name="draw_indicator">True</property>
+                                      </object>
+                                      <packing>
+                                        <property name="expand">False</property>
+                                        <property name="position">0</property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <object class="GtkCheckButton" id="check_button_signed_off_by">
+                                        <property name="label" translatable="yes">Add signed-off-by</property>
+                                        <property name="can_focus">True</property>
+                                        <property name="receives_default">False</property>
+                                        <property name="draw_indicator">True</property>
+                                      </object>
+                                      <packing>
+                                        <property name="expand">False</property>
+                                        <property name="position">1</property>
+                                      </packing>
+                                    </child>
                                   </object>
                                   <packing>
-                                    <property name="expand">False</property>
                                     <property name="position">1</property>
                                   </packing>
                                 </child>
                                 <child>
-                                  <object class="GtkButton" id="button_commit">
-                                    <property name="label" translatable="yes">Commit</property>
+                                  <object class="GtkAlignment" id="alignment1">
                                     <property name="visible">True</property>
-                                    <property name="can_focus">True</property>
-                                    <property name="receives_default">True</property>
-                                    <property name="image">image_commit</property>
+                                    <property name="yalign">1</property>
+                                    <property name="yscale">0</property>
+                                    <child>
+                                      <object class="GtkButton" id="button_commit">
+                                        <property name="label" translatable="yes">Commit</property>
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">True</property>
+                                        <property name="receives_default">True</property>
+                                        <property name="image">image_commit</property>
+                                      </object>
+                                    </child>
                                   </object>
                                   <packing>
                                     <property name="expand">False</property>
+                                    <property name="fill">False</property>
                                     <property name="pack_type">end</property>
                                     <property name="position">0</property>
                                   </packing>



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]