[gitg] Implemented push, merge, rebase, stash by dnd
- From: Jesse van den Kieboom <jessevdk src gnome org>
- To: svn-commits-list gnome org
- Subject: [gitg] Implemented push, merge, rebase, stash by dnd
- Date: Sun, 5 Jul 2009 01:14:50 +0000 (UTC)
commit 9ca7a51589f0d3006a029750f0a451d76ac80257
Author: Jesse van den Kieboom <jessevdk gnome org>
Date: Sun Jul 5 02:12:08 2009 +0200
Implemented push, merge, rebase, stash by dnd
gitg/gitg-branch-actions.c | 608 +++++++++++++++++++++++++++++++++++++----
gitg/gitg-branch-actions.h | 1 +
gitg/gitg-menus.xml | 17 +-
gitg/gitg-ref.c | 7 +-
gitg/gitg-repository-dialog.c | 1 +
gitg/gitg-repository.c | 45 +++
gitg/gitg-repository.h | 2 +
gitg/gitg-runner.c | 59 ++++-
gitg/gitg-runner.h | 3 +
gitg/gitg-spinner.c | 2 +-
gitg/gitg-window.c | 161 ++++++++++-
11 files changed, 824 insertions(+), 82 deletions(-)
---
diff --git a/gitg/gitg-branch-actions.c b/gitg/gitg-branch-actions.c
index 4d7fdd5..0a13e94 100644
--- a/gitg/gitg-branch-actions.c
+++ b/gitg/gitg-branch-actions.c
@@ -1,6 +1,8 @@
#include <glib/gi18n.h>
+#include <unistd.h>
#include "gitg-branch-actions.h"
+#include "gitg-utils.h"
typedef enum
{
@@ -306,7 +308,6 @@ remove_remote_branch (GitgWindow *window,
GitgRef *ref)
{
gchar const *name = gitg_ref_get_shortname (ref);
- GitgRepository *repository = gitg_window_get_repository (window);
gint r = message_dialog (window,
GTK_MESSAGE_QUESTION,
@@ -367,62 +368,260 @@ gitg_branch_actions_remove (GitgWindow *window,
return ret;
}
+static void
+reset_buffer (GitgRunner *runner, GString *buffer)
+{
+ g_string_erase (buffer, 0, -1);
+}
+
+static void
+update_buffer (GitgRunner *runner, gchar **lines, GString *buffer)
+{
+ gchar **ptr = lines;
+
+ while (ptr && *ptr)
+ {
+ if (buffer->len != 0)
+ {
+ g_string_append_c (buffer, '\n');
+ }
+
+ g_string_append (buffer, *ptr);
+ ++ptr;
+ }
+}
+
static gboolean
-stash_changes (GitgWindow *window,
- GitgRef *ref)
+no_changes (GitgRepository *repository)
+{
+ return gitg_repository_commandv (repository, NULL,
+ "update-index", "--refresh", NULL) &&
+ gitg_repository_commandv (repository, NULL,
+ "diff-files", "--quiet", NULL) &&
+ gitg_repository_commandv (repository, NULL,
+ "diff-index", "--cached", "--quiet", "HEAD", "--", NULL);
+}
+
+static gboolean
+stash_changes_real (GitgWindow *window, gchar **ref, gboolean storeref)
{
GitgRepository *repository = gitg_window_get_repository (window);
+ gboolean ret;
+ gchar *tree = NULL;
+ gchar *commit = NULL;
+ gchar *head = NULL;
+ gboolean showerror = FALSE;
+
+ GitgRunner *runner = gitg_runner_new_synchronized (1000);
+ GString *buffer = g_string_new ("");
+
+ g_signal_connect (runner, "begin-loading", G_CALLBACK (reset_buffer), buffer);
+ g_signal_connect (runner, "update", G_CALLBACK (update_buffer), buffer);
+
+ gchar const *secondary;
+
+ if (storeref)
+ {
+ secondary = _("Do you want to temporarily stash these changes?");
+ }
+ else
+ {
+ secondary = _("Do you want to stash and reapply these changes?");
+ }
- gchar **output = gitg_repository_command_with_outputv (repository,
- NULL,
- "diff-files",
- NULL);
+ gint r = message_dialog (window,
+ GTK_MESSAGE_QUESTION,
+ _("You have uncommited changes in your current working tree"),
+ secondary,
+ _("Stash changes"));
- if (output && *output && **output)
+ if (r != GTK_RESPONSE_ACCEPT)
{
- gint ret = message_dialog (window,
- GTK_MESSAGE_QUESTION,
- _("You have uncommited changes in your current working copy"),
- _("Do you want to temporarily stash these changes?"),
- _("Stash changes"));
+ ret = FALSE;
+ goto cleanup;
+ }
+
+ // Create tree object of the current index
+ gitg_repository_run_commandv (repository, runner, NULL,
+ "write-tree", NULL);
+
+ if (buffer->len == 0)
+ {
+ ret = FALSE;
+ showerror = TRUE;
+ goto cleanup;
+ }
+
+ tree = g_strndup (buffer->str, buffer->len);
+ head = gitg_repository_parse_head (repository);
+
+ gitg_repository_run_commandv (repository, runner, NULL,
+ "commit-tree", tree, "-p", head, NULL);
- if (ret != GTK_RESPONSE_ACCEPT)
- {
- return FALSE;
- }
+ if (buffer->len == 0)
+ {
+ ret = FALSE;
+ showerror = TRUE;
+ goto cleanup;
+ }
+
+ commit = g_strndup (buffer->str, buffer->len);
+
+ // Working tree
+ gchar *tmpname = NULL;
+ gint fd = g_file_open_tmp ("gitg-temp-index-XXXXXX", &tmpname, NULL);
+
+ if (fd == -1)
+ {
+ ret = FALSE;
+ showerror = TRUE;
+ goto cleanup;
+ }
+
+ GFile *customindex = g_file_new_for_path (tmpname);
+ g_free (tmpname);
+
+ close (fd);
+
+ gchar const *gitdir = gitg_repository_get_path (repository);
+ gchar *indexpath = g_build_filename (gitdir, ".git", "index", NULL);
+
+ GFile *index = g_file_new_for_path (indexpath);
+ g_free (indexpath);
+
+ gboolean copied = g_file_copy (index, customindex, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, NULL);
+ g_object_unref (index);
+
+ if (!copied)
+ {
+ g_object_unref (customindex);
+
+ ret = FALSE;
+ showerror = TRUE;
+ goto cleanup;
+ }
+
+ gitg_runner_add_environment (runner, "GIT_INDEX_FILE", tmpname);
+
+ gboolean writestash;
+
+ writestash = gitg_repository_run_commandv (repository, runner, NULL,
+ "read-tree", "-m", tree, NULL) &&
+ gitg_repository_run_commandv (repository, runner, NULL,
+ "add", "-u", NULL) &&
+ gitg_repository_run_commandv (repository, runner, NULL,
+ "write-tree", NULL);
+
+ g_file_delete (customindex, NULL, NULL);
+ g_object_unref (customindex);
+
+ if (!writestash)
+ {
+ g_object_unref (customindex);
+ ret = FALSE;
+ showerror = TRUE;
- if (!gitg_repository_commandv (repository, NULL, "stash", NULL))
- {
- message_dialog (window,
- GTK_MESSAGE_ERROR,
- _("Could not stash changes from your current working copy."),
- NULL,
- NULL);
- return FALSE;
- }
+ goto cleanup;
+ }
+
+ gchar *stashtree = g_strndup (buffer->str, buffer->len);
+ gchar *reason = g_strdup_printf ("Automatic stash from gitg: ");
+
+ gitg_repository_run_command_with_inputv (repository, runner, reason, NULL,
+ "commit-tree", stashtree,
+ "-p", head,
+ "-p", commit, NULL);
+ g_free (stashtree);
+
+ if (buffer->len == 0)
+ {
+ g_free (reason);
+
+ ret = FALSE;
+ showerror = TRUE;
+
+ goto cleanup;
+ }
+
+ gchar *rref = g_strndup (buffer->str, buffer->len);
+
+ if (ref)
+ {
+ *ref = g_strdup (rref);
}
- if (output)
+ if (storeref)
{
- g_strfreev (output);
+ // Make ref
+ gitg_repository_run_commandv (repository, runner, NULL,
+ "update-ref", "-m", reason,
+ "refs/stash", rref, NULL);
}
- return TRUE;
+ g_free (rref);
+
+ gitg_repository_run_commandv (repository, runner, NULL,
+ "reset", "--hard", NULL);
+ ret = TRUE;
+
+cleanup:
+ g_string_free (buffer, TRUE);
+ g_object_unref (runner);
+ g_free (commit);
+ g_free (tree);
+ g_free (head);
+
+ if (showerror)
+ {
+ message_dialog (window,
+ GTK_MESSAGE_ERROR,
+ _("Failed to save current index state"),
+ NULL,
+ NULL);
+ }
+
+ return ret;
+}
+
+static gboolean
+stash_changes (GitgWindow *window, gchar **ref, gboolean storeref)
+{
+ if (no_changes (gitg_window_get_repository (window)))
+ {
+ *ref = NULL;
+ return TRUE;
+ }
+
+ return stash_changes_real (window, ref, storeref);
+}
+
+static gboolean
+checkout_local_branch_real (GitgWindow *window, GitgRef *ref)
+{
+ GitgRepository *repository = gitg_window_get_repository (window);
+
+ if (!gitg_repository_commandv (repository, NULL, "checkout", gitg_ref_get_shortname (ref), NULL))
+ {
+ return FALSE;
+ }
+ else
+ {
+ return TRUE;
+ }
}
static gboolean
checkout_local_branch (GitgWindow *window,
GitgRef *ref)
{
- if (!stash_changes (window, ref))
+ if (!stash_changes (window, NULL, TRUE))
{
return FALSE;
}
-
- GitgRepository *repository = gitg_window_get_repository (window);
+
gchar const *name = gitg_ref_get_shortname (ref);
- if (!gitg_repository_commandv (repository, NULL, "checkout", name, NULL))
+ if (!checkout_local_branch_real (window, ref))
{
message_dialog (window,
GTK_MESSAGE_ERROR,
@@ -434,7 +633,7 @@ checkout_local_branch (GitgWindow *window,
}
else
{
- gitg_repository_load (repository, 1, (gchar const **)&name, NULL);
+ gitg_repository_load (gitg_window_get_repository (window), 1, (gchar const **)&name, NULL);
return TRUE;
}
}
@@ -443,7 +642,7 @@ static gboolean
checkout_remote_branch (GitgWindow *window,
GitgRef *ref)
{
- if (!stash_changes (window, ref))
+ if (!stash_changes (window, NULL, TRUE))
{
return FALSE;
}
@@ -506,6 +705,122 @@ gitg_branch_actions_checkout (GitgWindow *window,
return ret;
}
+typedef struct
+{
+ gboolean rebase;
+
+ GitgRef *source;
+ GitgRef *dest;
+
+ gchar *stashcommit;
+ GitgRef *head;
+} RefInfo;
+
+static RefInfo *
+ref_info_new (GitgRef *source, GitgRef *dest)
+{
+ RefInfo *ret = g_slice_new0 (RefInfo);
+
+ ret->source = gitg_ref_copy (source);
+ ret->dest = gitg_ref_copy (dest);
+
+ return ret;
+}
+
+static void
+ref_info_free (RefInfo *info)
+{
+ gitg_ref_free (info->source);
+ gitg_ref_free (info->dest);
+
+ g_free (info->stashcommit);
+ gitg_ref_free (info->head);
+
+ g_slice_free (RefInfo, info);
+}
+
+static void
+on_merge_rebase_result (GitgWindow *window,
+ GitgProgress progress,
+ gpointer data)
+{
+ RefInfo *info = (RefInfo *)data;
+
+ if (progress == GITG_PROGRESS_ERROR)
+ {
+ gchar const *message;
+
+ if (info->rebase)
+ {
+ message = _("Failed to rebase %s branch <%s> onto %s branch <%s>");
+ }
+ else
+ {
+ message = _("Failed to merge %s branch <%s> with %s branch <%s>");
+ }
+
+ message_dialog (window,
+ GTK_MESSAGE_ERROR,
+ message,
+ NULL,
+ NULL,
+ gitg_ref_get_ref_type (info->source) == GITG_REF_TYPE_BRANCH ? _("local") : _("remote"),
+ gitg_ref_get_shortname (info->source),
+ gitg_ref_get_ref_type (info->dest) == GITG_REF_TYPE_BRANCH ? _("local") : _("remote"),
+ gitg_ref_get_shortname (info->dest));
+ }
+ else if (progress == GITG_PROGRESS_SUCCESS)
+ {
+ GitgRepository *repository = gitg_window_get_repository (window);
+
+ // Checkout head
+ if (!checkout_local_branch_real (window, info->head))
+ {
+ gchar const *message = NULL;
+
+ if (info->stashcommit)
+ {
+ gitg_repository_commandv (repository, NULL,
+ "update-ref", "-m", "gitg autosave stash",
+ "refs/stash", info->stashcommit, NULL);
+ message = _("The stashed changes have been stored to be reapplied manually");
+ }
+
+ message_dialog (window,
+ GTK_MESSAGE_ERROR,
+ _("Failed to checkout previously checked out branch"),
+ message,
+ NULL);
+ }
+ else if (info->stashcommit)
+ {
+ // Reapply stash
+ if (!gitg_repository_commandv (gitg_window_get_repository (window),
+ NULL,
+ "stash",
+ "apply",
+ "--index",
+ info->stashcommit,
+ NULL))
+ {
+ gitg_repository_commandv (repository, NULL,
+ "update-ref", "-m", "gitg autosave stash",
+ "refs/stash", info->stashcommit, NULL);
+
+ message_dialog (window,
+ GTK_MESSAGE_ERROR,
+ _("Failed to reapply stash correctly"),
+ _("There might be unresolved conflicts in the working tree or index which you need to resolve manually"),
+ NULL);
+ }
+ }
+
+ gitg_repository_reload (gitg_window_get_repository (window));
+ }
+
+ ref_info_free (info);
+}
+
GitgRunner *
gitg_branch_actions_merge (GitgWindow *window,
GitgRef *source,
@@ -516,7 +831,71 @@ gitg_branch_actions_merge (GitgWindow *window,
g_return_val_if_fail (source != NULL, NULL);
g_return_val_if_fail (gitg_ref_get_ref_type (dest) != GITG_REF_TYPE_REMOTE, NULL);
- return NULL;
+ gchar *message = g_strdup_printf (_("Are you sure you want to merge %s branch <%s> onto %s branch <%s>?"),
+ gitg_ref_get_ref_type (source) == GITG_REF_TYPE_BRANCH ? _("local") : _("remote"),
+ gitg_ref_get_shortname (source),
+ gitg_ref_get_ref_type (dest) == GITG_REF_TYPE_BRANCH ? _("local") : _("remote"),
+ gitg_ref_get_shortname (dest));
+
+ if (message_dialog (window,
+ GTK_MESSAGE_QUESTION,
+ _("Merge"),
+ message,
+ _("Merge")) != GTK_RESPONSE_ACCEPT)
+ {
+ g_free (message);
+ return NULL;
+ }
+
+ g_free (message);
+ GitgRepository *repository = gitg_window_get_repository (window);
+ gchar *stashcommit = NULL;
+
+ if (!stash_changes (window, &stashcommit, FALSE))
+ {
+ return NULL;
+ }
+
+ GitgRef *head = gitg_repository_get_current_working_ref (repository);
+
+ // First checkout the correct branch on which to merge, e.g. dest
+ if (!gitg_repository_commandv (repository, NULL, "checkout", gitg_ref_get_shortname (dest), NULL))
+ {
+ g_free (stashcommit);
+
+ message_dialog (window,
+ GTK_MESSAGE_ERROR,
+ _("Failed to checkout local branch <%s>"),
+ _("The branch on which to merge could not be checked out"),
+ NULL,
+ gitg_ref_get_shortname (dest));
+ return NULL;
+ }
+
+ message = g_strdup_printf (_("Merging %s branch <%s> onto %s branch <%s>"),
+ gitg_ref_get_ref_type (source) == GITG_REF_TYPE_BRANCH ? _("local") : _("remote"),
+ gitg_ref_get_shortname (source),
+ gitg_ref_get_ref_type (dest) == GITG_REF_TYPE_BRANCH ? _("local") : _("remote"),
+ gitg_ref_get_shortname (dest));
+
+ GitgRunner *ret;
+ RefInfo *info = ref_info_new (source, dest);
+ info->stashcommit = stashcommit;
+ info->head = gitg_ref_copy (head);
+ info->rebase = FALSE;
+
+ ret = run_progress (window,
+ _("Merge"),
+ message,
+ on_merge_rebase_result,
+ info,
+ "merge",
+ gitg_ref_get_shortname (source),
+ NULL);
+
+ g_free (message);
+
+ return ret;
}
GitgRunner *
@@ -527,23 +906,89 @@ gitg_branch_actions_rebase (GitgWindow *window,
g_return_val_if_fail (GITG_IS_WINDOW (window), NULL);
g_return_val_if_fail (dest != NULL, NULL);
g_return_val_if_fail (source != NULL, NULL);
- g_return_val_if_fail (gitg_ref_get_ref_type (dest) != GITG_REF_TYPE_REMOTE, NULL);
+ g_return_val_if_fail (gitg_ref_get_ref_type (source) != GITG_REF_TYPE_REMOTE, NULL);
- return NULL;
-}
+ gchar *message = g_strdup_printf (_("Are you sure you want to rebase %s branch <%s> onto %s branch <%s>?"),
+ gitg_ref_get_ref_type (source) == GITG_REF_TYPE_BRANCH ? _("local") : _("remote"),
+ gitg_ref_get_shortname (source),
+ gitg_ref_get_ref_type (dest) == GITG_REF_TYPE_BRANCH ? _("local") : _("remote"),
+ gitg_ref_get_shortname (dest));
-typedef struct
-{
- GitgRef *source;
- GitgRef *dest;
-} PushInfo;
+ if (message_dialog (window,
+ GTK_MESSAGE_QUESTION,
+ _("Rebase"),
+ message,
+ _("Rebase")) != GTK_RESPONSE_ACCEPT)
+ {
+ g_free (message);
+ return NULL;
+ }
+
+ g_free (message);
+ GitgRepository *repository = gitg_window_get_repository (window);
+ gchar *stashcommit = NULL;
+
+ if (!no_changes (repository))
+ {
+ // Check if destination is current HEAD
+ gchar *head = gitg_repository_parse_head (repository);
+ Hash hash;
+
+ gitg_utils_sha1_to_hash (head, hash);
+ g_free (head);
+
+ if (gitg_utils_hash_equal (hash, gitg_ref_get_hash (dest)))
+ {
+ message_dialog (window,
+ GTK_MESSAGE_ERROR,
+ _("Unable to rebase"),
+ _("There are still uncommitted changes in your working tree and you are trying to rebase a branch onto the currently checked out branch. Either remove, stash or commit your changes first and try again"),
+ NULL);
+ return NULL;
+ }
+
+ if (!stash_changes_real (window, &stashcommit, FALSE))
+ {
+ return NULL;
+ }
+ }
+
+ gchar *merge_head = gitg_utils_hash_to_sha1_new (gitg_ref_get_hash (dest));
+
+ message = g_strdup_printf (_("Rebasing %s branch <%s> onto %s branch <%s>"),
+ gitg_ref_get_ref_type (source) == GITG_REF_TYPE_BRANCH ? _("local") : _("remote"),
+ gitg_ref_get_shortname (source),
+ gitg_ref_get_ref_type (dest) == GITG_REF_TYPE_BRANCH ? _("local") : _("remote"),
+ gitg_ref_get_shortname (dest));
+
+ GitgRunner *ret;
+ RefInfo *info = ref_info_new (source, dest);
+ info->stashcommit = stashcommit;
+ info->head = gitg_ref_copy (gitg_repository_get_current_working_ref (repository));
+ info->rebase = TRUE;
+
+ ret = run_progress (window,
+ _("Rebase"),
+ message,
+ on_merge_rebase_result,
+ info,
+ "rebase",
+ merge_head,
+ gitg_ref_get_shortname (source),
+ NULL);
+
+ g_free (message);
+ g_free (merge_head);
+
+ return ret;
+}
static void
on_push_result (GitgWindow *window,
GitgProgress progress,
gpointer data)
{
- PushInfo *info = (PushInfo *)data;
+ RefInfo *info = (RefInfo *)data;
if (progress == GITG_PROGRESS_ERROR)
{
@@ -560,9 +1005,7 @@ on_push_result (GitgWindow *window,
gitg_repository_reload (gitg_window_get_repository (window));
}
- gitg_ref_free (info->source);
- gitg_ref_free (info->dest);
- g_slice_free (PushInfo, info);
+ ref_info_free (info);
}
GitgRunner *
@@ -584,7 +1027,6 @@ gitg_branch_actions_push (GitgWindow *window,
GTK_MESSAGE_QUESTION,
_("Push"),
message,
- NULL,
_("Push")) != GTK_RESPONSE_ACCEPT)
{
g_free (message);
@@ -598,14 +1040,12 @@ gitg_branch_actions_push (GitgWindow *window,
gchar const *name = gitg_ref_get_shortname (source);
gchar *spec = g_strconcat (name, ":", local, NULL);
- message = g_strdup_printf (_("Pushing local branch `%s' to remote branch `%s'"),
+ message = g_strdup_printf (_("Pushing local branch <%s> to remote branch <%s>"),
gitg_ref_get_shortname (source),
gitg_ref_get_shortname (dest));
GitgRunner *ret;
- PushInfo *info = g_slice_new (PushInfo);
- info->source = gitg_ref_copy (source);
- info->dest = gitg_ref_copy (dest);
+ RefInfo *info = ref_info_new (source, dest);
ret = run_progress (window,
_("Push"),
@@ -624,11 +1064,73 @@ gitg_branch_actions_push (GitgWindow *window,
return ret;
}
+GitgRunner *
+gitg_branch_actions_push_remote (GitgWindow *window,
+ GitgRef *source,
+ gchar const *remote)
+{
+ g_return_val_if_fail (GITG_IS_WINDOW (window), NULL);
+ g_return_val_if_fail (remote != NULL, NULL);
+ g_return_val_if_fail (source != NULL, NULL);
+ g_return_val_if_fail (gitg_ref_get_ref_type (source) == GITG_REF_TYPE_BRANCH, NULL);
+
+ gchar *message = g_strdup_printf (_("Are you sure you want to push <%s> to remote <%s>?"),
+ gitg_ref_get_shortname (source),
+ remote);
+
+ if (message_dialog (window,
+ GTK_MESSAGE_QUESTION,
+ _("Push"),
+ message,
+ _("Push")) != GTK_RESPONSE_ACCEPT)
+ {
+ g_free (message);
+ return NULL;
+ }
+
+ g_free (message);
+
+ gchar const *name = gitg_ref_get_shortname (source);
+ gchar *rm = g_strconcat (remote, "/", name, NULL);
+
+ gchar *spec = g_strconcat (name, ":", name, NULL);
+ message = g_strdup_printf (_("Pushing local branch <%s> to remote branch <%s>"),
+ gitg_ref_get_shortname (source),
+ rm);
+
+ g_free (rm);
+
+ GitgRunner *ret;
+ gchar *rr = g_strconcat ("refs/remotes/", remote, "/", name, NULL);
+ GitgRef *rmref = gitg_ref_new ("0000000000000000000000000000000000000000", remote);
+ g_free (rr);
+
+ RefInfo *info = ref_info_new (source, rmref);
+ gitg_ref_free (rmref);
+
+ ret = run_progress (window,
+ _("Push"),
+ message,
+ on_push_result,
+ info,
+ "push",
+ remote,
+ spec,
+ NULL);
+
+ g_free (message);
+ g_free (spec);
+
+ return ret;
+}
+
gboolean
gitg_branch_actions_apply_stash (GitgWindow *window,
GitgRef *stash)
{
g_return_val_if_fail (GITG_IS_WINDOW (window), FALSE);
+ g_return_val_if_fail (stash != NULL, FALSE);
+
return FALSE;
}
diff --git a/gitg/gitg-branch-actions.h b/gitg/gitg-branch-actions.h
index 3acb59a..4cc8cdb 100644
--- a/gitg/gitg-branch-actions.h
+++ b/gitg/gitg-branch-actions.h
@@ -35,6 +35,7 @@ GitgRunner *gitg_branch_actions_merge (GitgWindow *window, GitgRef *source, Gitg
GitgRunner *gitg_branch_actions_rebase (GitgWindow *window, GitgRef *source, GitgRef *dest);
GitgRunner *gitg_branch_actions_push (GitgWindow *window, GitgRef *source, GitgRef *dest);
+GitgRunner *gitg_branch_actions_push_remote (GitgWindow *window, GitgRef *source, gchar const *remote);
G_END_DECLS
diff --git a/gitg/gitg-menus.xml b/gitg/gitg-menus.xml
index d1fbb59..675d905 100644
--- a/gitg/gitg-menus.xml
+++ b/gitg/gitg-menus.xml
@@ -49,13 +49,18 @@
</object>
</child>
<child>
+ <object class="GtkAction" id="RebaseAction">
+ <property name="label">Rebase branch onto...</property>
+ </object>
+ </child>
+ <child>
<object class="GtkAction" id="MergeAction">
- <property name="label">Merge branch onto...</property>
+ <property name="label">Merge branch with...</property>
</object>
</child>
<child>
- <object class="GtkAction" id="RebaseAction">
- <property name="label">Rebase branch onto...</property>
+ <object class="GtkAction" id="PushAction">
+ <property name="label">Push branch to...</property>
</object>
</child>
</object>
@@ -65,11 +70,13 @@
<child>
<object class="GtkAction" id="MergeDndAction">
<property name="label">Merge</property>
+ <signal name="activate" handler="on_merge_branch_action_activate"/>
</object>
</child>
<child>
<object class="GtkAction" id="RebaseDndAction">
<property name="label">Rebase</property>
+ <signal name="activate" handler="on_rebase_branch_action_activate"/>
</object>
</child>
</object>
@@ -88,10 +95,12 @@
<menu name="Rebase" action="RebaseAction">
<placeholder name="Placeholder"/>
</menu>
- <separator/>
<menu name="Merge" action="MergeAction">
<placeholder name="Placeholder"/>
</menu>
+ <menu name="Push" action="PushAction">
+ <placeholder name="Placeholder"/>
+ </menu>
</popup>
<popup name="dnd_popup">
<menuitem action="RebaseDndAction"/>
diff --git a/gitg/gitg-ref.c b/gitg/gitg-ref.c
index f577338..b620170 100644
--- a/gitg/gitg-ref.c
+++ b/gitg/gitg-ref.c
@@ -105,7 +105,12 @@ gitg_ref_new(gchar const *hash, gchar const *name)
GitgRef *
gitg_ref_copy(GitgRef *ref)
{
- GitgRef *ret = g_slice_new0(GitgRef);
+ if (ref == NULL)
+ {
+ return NULL;
+ }
+
+ GitgRef *ret = g_slice_new0 (GitgRef);
ret->type = ref->type;
ret->name = g_strdup(ref->name);
diff --git a/gitg/gitg-repository-dialog.c b/gitg/gitg-repository-dialog.c
index 5eaa78d..c1bfe3c 100644
--- a/gitg/gitg-repository-dialog.c
+++ b/gitg/gitg-repository-dialog.c
@@ -316,6 +316,7 @@ init_remotes(GitgRepositoryDialog *dialog)
if (!ret)
{
+ update_sensitivity (dialog);
return;
}
diff --git a/gitg/gitg-repository.c b/gitg/gitg-repository.c
index e3aeac5..91a9e7a 100644
--- a/gitg/gitg-repository.c
+++ b/gitg/gitg-repository.c
@@ -27,6 +27,7 @@
#include "gitg-types.h"
#include "gitg-preferences.h"
#include "gitg-data-binding.h"
+#include "gitg-config.h"
#include <gio/gio.h>
#include <glib/gi18n.h>
@@ -1427,3 +1428,47 @@ gitg_repository_get_current_working_ref(GitgRepository *repository)
return repository->priv->working_ref;
}
+
+gchar **
+gitg_repository_get_remotes (GitgRepository *repository)
+{
+ g_return_val_if_fail (GITG_IS_REPOSITORY (repository), NULL);
+
+ GitgConfig *config = gitg_config_new (repository);
+ gchar *ret = gitg_config_get_value_regex (config, "remote\\..*\\.url");
+
+ gchar **remotes = g_malloc (sizeof (gchar *));
+ remotes[0] = NULL;
+
+ if (!ret)
+ {
+ g_object_unref (config);
+ return remotes;
+ }
+
+ gchar **lines = g_strsplit(ret, "\n", -1);
+ gchar **ptr = lines;
+
+ GRegex *regex = g_regex_new ("remote\\.(.+?)\\.url\\s+(.*)", 0, 0, NULL);
+ gint num = 0;
+
+ while (*ptr)
+ {
+ GMatchInfo *info = NULL;
+
+ if (g_regex_match (regex, *ptr, 0, &info))
+ {
+ gchar *name = g_match_info_fetch (info, 1);
+
+ remotes = g_realloc (ret, sizeof(gchar *) * (++num + 1));
+ remotes[num - 1] = name;
+ }
+
+ g_match_info_free (info);
+ ++ptr;
+ }
+
+ remotes[num] = NULL;
+ g_object_unref (config);
+ return remotes;
+}
diff --git a/gitg/gitg-repository.h b/gitg/gitg-repository.h
index d0c78f5..6060b0e 100644
--- a/gitg/gitg-repository.h
+++ b/gitg/gitg-repository.h
@@ -107,6 +107,8 @@ gchar *gitg_repository_parse_head(GitgRepository *repository);
void gitg_repository_reload(GitgRepository *repository);
+gchar **gitg_repository_get_remotes (GitgRepository *repository);
+
G_END_DECLS
#endif /* __GITG_REPOSITORY_H__ */
diff --git a/gitg/gitg-runner.c b/gitg/gitg-runner.c
index 1fdd3d1..73ae594 100644
--- a/gitg/gitg-runner.c
+++ b/gitg/gitg-runner.c
@@ -66,6 +66,7 @@ struct _GitgRunnerPrivate
gchar *buffer;
gchar *read_buffer;
gchar **lines;
+ gchar **environment;
gint exit_status;
};
@@ -119,11 +120,6 @@ runner_io_exit(GPid pid, gint status, GitgRunner *runner)
}
static void
-dummy_exit(GPid pid, gint status, gpointer data)
-{
-}
-
-static void
free_lines(GitgRunner *runner)
{
gint i = 0;
@@ -151,6 +147,7 @@ gitg_runner_finalize(GObject *object)
/* Remove line buffer */
g_free(runner->priv->buffer);
+ g_strfreev (runner->priv->environment);
g_object_unref(runner->priv->cancellable);
@@ -424,7 +421,7 @@ run_sync(GitgRunner *runner, gchar const *input, GError **error)
if (status != 0 && error)
g_set_error(error, GITG_RUNNER_ERROR, GITG_RUNNER_ERROR_EXIT, "Did not exit without error code");
- return status == 0;
+ return status == EXIT_SUCCESS;
}
static void
@@ -642,12 +639,15 @@ gitg_runner_cancel(GitgRunner *runner)
runner->priv->cancellable = g_cancellable_new();
- g_child_watch_add(runner->priv->pid, dummy_cb, NULL);
- kill(runner->priv->pid, SIGTERM);
+ if (runner->priv->pid)
+ {
+ g_child_watch_add(runner->priv->pid, dummy_cb, NULL);
+ kill(runner->priv->pid, SIGTERM);
+
+ runner_io_exit(runner->priv->pid, EXIT_FAILURE, runner);
+ }
- runner_io_exit(runner->priv->pid, EXIT_FAILURE, runner);
close_streams(runner);
-
g_signal_emit(runner, runner_signals[END_LOADING], 0, TRUE);
}
}
@@ -666,3 +666,42 @@ gitg_runner_get_exit_status(GitgRunner *runner)
return runner->priv->exit_status;
}
+
+void
+gitg_runner_set_environment (GitgRunner *runner, gchar const **environment)
+{
+ g_return_if_fail (GITG_IS_RUNNER (runner));
+
+ g_strfreev (runner->priv->environment);
+ gint len = g_strv_length ((gchar **)environment);
+
+ runner->priv->environment = g_new (gchar *, len + 1);
+ gint i;
+
+ for (i = 0; i < len; ++i)
+ {
+ runner->priv->environment[i] = g_strdup (environment[i]);
+ }
+
+ runner->priv->environment[len] = NULL;
+}
+
+void
+gitg_runner_add_environment (GitgRunner *runner, gchar const *key, gchar const *value)
+{
+ g_return_if_fail (GITG_IS_RUNNER (runner));
+ g_return_if_fail (key != NULL);
+ g_return_if_fail (value != NULL);
+
+ if (runner->priv->environment == NULL)
+ {
+ runner->priv->environment = g_listenv ();
+ }
+
+ gint len = g_strv_length (runner->priv->environment);
+ runner->priv->environment = g_realloc (runner->priv->environment,
+ sizeof(gchar *) * (len + 2));
+
+ runner->priv->environment[len] = g_strconcat (key, "=", value, NULL);
+ runner->priv->environment[len + 1] = NULL;
+}
diff --git a/gitg/gitg-runner.h b/gitg/gitg-runner.h
index d841089..2d848fa 100644
--- a/gitg/gitg-runner.h
+++ b/gitg/gitg-runner.h
@@ -79,6 +79,9 @@ gboolean gitg_runner_running(GitgRunner *runner);
gint gitg_runner_get_exit_status(GitgRunner *runner);
void gitg_runner_cancel(GitgRunner *runner);
+void gitg_runner_set_environment (GitgRunner *runner, gchar const **environment);
+void gitg_runner_add_environment(GitgRunner *runner, gchar const *key, gchar const *value);
+
GQuark gitg_runner_error_quark();
G_END_DECLS
diff --git a/gitg/gitg-spinner.c b/gitg/gitg-spinner.c
index a5f327b..d8d56ec 100644
--- a/gitg/gitg-spinner.c
+++ b/gitg/gitg-spinner.c
@@ -729,7 +729,7 @@ gitg_spinner_set_screen (GitgSpinner *spinner, GdkScreen *screen)
static void
gitg_spinner_dispose (GObject *object)
{
- GitgSpinner *spinner = GITG_SPINNER (object);
+ //GitgSpinner *spinner = GITG_SPINNER (object);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
diff --git a/gitg/gitg-window.c b/gitg/gitg-window.c
index d32223c..6d42003 100644
--- a/gitg/gitg-window.c
+++ b/gitg/gitg-window.c
@@ -43,6 +43,8 @@
#include "gitg-dnd.h"
#include "gitg-branch-actions.h"
+#define DYNAMIC_ACTION_DATA_KEY "GitgDynamicActionDataKey"
+
#define GITG_WINDOW_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GITG_TYPE_WINDOW, GitgWindowPrivate))
enum
@@ -85,7 +87,7 @@ struct _GitgWindowPrivate
gboolean destroy_has_run;
guint merge_rebase_uid;
GtkActionGroup *merge_rebase_action_group;
- GitgRef *popup_ref;
+ GitgRef *popup_refs[2];
GList *branch_actions;
};
@@ -482,7 +484,7 @@ update_dnd_status (GitgWindow *window, GitgRef *source, GitgRef *dest)
else if (gitg_ref_get_ref_type (source) == GITG_REF_TYPE_REMOTE &&
gitg_ref_get_ref_type (dest) == GITG_REF_TYPE_BRANCH)
{
- message = g_strdup_printf (_("Merge/rebase remote branch <%s> with/on local branch <%s>"), gitg_ref_get_shortname (source), gitg_ref_get_shortname (dest));
+ message = g_strdup_printf (_("Merge/rebase local branch <%s> with/on remote branch <%s>"), gitg_ref_get_shortname (dest), gitg_ref_get_shortname (source));
}
if (message)
@@ -510,6 +512,16 @@ on_refs_dnd (GitgRef *source, GitgRef *dest, gboolean dropped, GitgWindow *windo
{
ret = add_branch_action (window, gitg_branch_actions_push (window, source, dest));
}
+ else if (gitg_ref_get_ref_type (dest) == GITG_REF_TYPE_BRANCH)
+ {
+ GtkWidget *popup = gtk_ui_manager_get_widget (window->priv->menus_ui_manager,
+ "/ui/dnd_popup");
+
+ window->priv->popup_refs[0] = source;
+ window->priv->popup_refs[1] = dest;
+
+ gtk_menu_popup (GTK_MENU (popup), NULL, NULL, NULL, NULL, 1, gtk_get_current_event_time());
+ }
gtk_statusbar_push (window->priv->statusbar, 0, "");
return ret;
@@ -552,7 +564,8 @@ gitg_window_parser_finished(GtkBuildable *buildable, GtkBuilder *builder)
gtk_box_pack_start(GTK_BOX(vbox), menu, FALSE, FALSE, 0);
gtk_box_reorder_child(GTK_BOX(vbox), menu, 0);
-
+
+ gtk_window_add_accel_group (GTK_WINDOW (window), gtk_ui_manager_get_accel_group (uiman));
window->priv->edit_group = GTK_ACTION_GROUP(gtk_builder_get_object(b, "action_group_menu_edit"));
@@ -1364,6 +1377,37 @@ on_repository_properties(GtkAction *action, GitgWindow *window)
}
static void
+on_push_activated (GtkAction *action, GitgWindow *window)
+{
+ gchar const *ptr = g_object_get_data (G_OBJECT (action),
+ DYNAMIC_ACTION_DATA_KEY);
+ add_branch_action (window,
+ gitg_branch_actions_push_remote (window, window->priv->popup_refs[0], ptr));
+}
+
+static void
+on_rebase_activated (GtkAction *action, GitgWindow *window)
+{
+ GitgRef *dest = g_object_get_data (G_OBJECT (action),
+ DYNAMIC_ACTION_DATA_KEY);
+
+ add_branch_action (window, gitg_branch_actions_rebase (window,
+ window->priv->popup_refs[0],
+ dest));
+}
+
+static void
+on_merge_activated (GtkAction *action, GitgWindow *window)
+{
+ GitgRef *dest = g_object_get_data (G_OBJECT (action),
+ DYNAMIC_ACTION_DATA_KEY);
+
+ add_branch_action (window, gitg_branch_actions_merge (window,
+ dest,
+ window->priv->popup_refs[0]));
+}
+
+static void
update_merge_rebase (GitgWindow *window, GitgRef *ref)
{
if (window->priv->merge_rebase_uid != 0)
@@ -1415,16 +1459,36 @@ update_merge_rebase (GitgWindow *window, GitgRef *ref)
if (gitg_ref_get_ref_type (r) == GITG_REF_TYPE_BRANCH && !gitg_ref_equal (r, ref))
{
- gchar *rebase = g_strdup_printf ("Rebase%sAction", gitg_ref_get_shortname (r));
- gchar *merge = g_strdup_printf ("Merge%sAction", gitg_ref_get_shortname (r));
+ gchar const *rname = gitg_ref_get_shortname (r);
- GtkAction *rebaseac = gtk_action_new (rebase, gitg_ref_get_shortname (r), NULL, NULL);
- GtkAction *mergeac = gtk_action_new (merge, gitg_ref_get_shortname (r), NULL, NULL);
+ gchar *rebase = g_strconcat ("Rebase", rname, "Action", NULL);
+ gchar *merge = g_strconcat ("Merge", rname, "Action", NULL);
+
+ GtkAction *rebaseac = gtk_action_new (rebase, rname, NULL, NULL);
+ GtkAction *mergeac = gtk_action_new (merge, rname, NULL, NULL);
+
+ g_object_set_data_full (G_OBJECT (rebaseac),
+ DYNAMIC_ACTION_DATA_KEY,
+ gitg_ref_copy (r),
+ (GDestroyNotify)gitg_ref_free);
+ g_object_set_data_full (G_OBJECT (mergeac),
+ DYNAMIC_ACTION_DATA_KEY,
+ gitg_ref_copy (r),
+ (GDestroyNotify)gitg_ref_free);
+
+ g_signal_connect (rebaseac,
+ "activate",
+ G_CALLBACK (on_rebase_activated),
+ window);
+ g_signal_connect (mergeac,
+ "activate",
+ G_CALLBACK (on_merge_activated),
+ window);
gtk_action_group_add_action (ac, rebaseac);
gtk_action_group_add_action (ac, mergeac);
- gchar *name = g_strconcat ("Rebase", gitg_ref_get_shortname (r), NULL);
+ gchar *name = g_strconcat ("Rebase", rname, NULL);
gtk_ui_manager_add_ui (window->priv->menus_ui_manager,
window->priv->merge_rebase_uid,
@@ -1435,7 +1499,7 @@ update_merge_rebase (GitgWindow *window, GitgRef *ref)
FALSE);
g_free (name);
- name = g_strconcat ("Merge", gitg_ref_get_shortname (r), NULL);
+ name = g_strconcat ("Merge", rname, NULL);
gtk_ui_manager_add_ui (window->priv->menus_ui_manager,
window->priv->merge_rebase_uid,
@@ -1453,6 +1517,39 @@ update_merge_rebase (GitgWindow *window, GitgRef *ref)
g_slist_foreach (refs, (GFunc)gitg_ref_free, NULL);
g_slist_free (refs);
+
+ gchar **remotes = gitg_repository_get_remotes (window->priv->repository);
+ gchar **ptr = remotes;
+
+ while (*ptr)
+ {
+ gchar *push = g_strconcat ("Push", *ptr, "Action", NULL);
+ GtkAction *pushac = gtk_action_new (push, *ptr, NULL, NULL);
+
+ gtk_action_group_add_action (ac, pushac);
+
+ gchar *name = g_strconcat ("Push", *ptr, NULL);
+ gtk_ui_manager_add_ui (window->priv->menus_ui_manager,
+ window->priv->merge_rebase_uid,
+ "/ui/revision_popup/Push/Placeholder",
+ name,
+ push,
+ GTK_UI_MANAGER_MENUITEM,
+ FALSE);
+
+ g_object_set_data_full (G_OBJECT (pushac),
+ DYNAMIC_ACTION_DATA_KEY,
+ g_strdup (*ptr),
+ (GDestroyNotify)g_free);
+
+ g_signal_connect (pushac,
+ "activate",
+ G_CALLBACK (on_push_activated),
+ window);
+ ++ptr;
+ }
+
+ g_strfreev (remotes);
gtk_ui_manager_ensure_update (window->priv->menus_ui_manager);
}
@@ -1566,7 +1663,7 @@ popup_revision (GitgWindow *window, GdkEventButton *event)
}
update_merge_rebase (window, ref);
- window->priv->popup_ref = ref;
+ window->priv->popup_refs[0] = ref;
gtk_menu_popup (GTK_MENU (popup), NULL, NULL, NULL, window, event->button, event->time);
return TRUE;
@@ -1587,12 +1684,50 @@ on_tree_view_rv_button_press_event (GtkWidget *widget, GdkEventButton *event, Gi
void
on_checkout_branch_action_activate (GtkAction *action, GitgWindow *window)
{
- gitg_branch_actions_checkout (window, window->priv->popup_ref);
+ gitg_branch_actions_checkout (window, window->priv->popup_refs[0]);
}
-
void
on_remove_branch_action_activate (GtkAction *action, GitgWindow *window)
{
- gitg_branch_actions_remove (window, window->priv->popup_ref);
+ gitg_branch_actions_remove (window, window->priv->popup_refs[0]);
}
+
+void
+on_rebase_branch_action_activate (GtkAction *action, GitgWindow *window)
+{
+ gint source;
+
+ if (gitg_ref_get_ref_type (window->priv->popup_refs[0]) == GITG_REF_TYPE_REMOTE)
+ {
+ source = 1;
+ }
+ else
+ {
+ source = 0;
+ }
+
+ add_branch_action (window, gitg_branch_actions_rebase (window,
+ window->priv->popup_refs[source],
+ window->priv->popup_refs[!source]));
+}
+
+void
+on_merge_branch_action_activate (GtkAction *action, GitgWindow *window)
+{
+ gint source;
+
+ if (gitg_ref_get_ref_type (window->priv->popup_refs[0]) == GITG_REF_TYPE_REMOTE)
+ {
+ source = 1;
+ }
+ else
+ {
+ source = 0;
+ }
+
+ add_branch_action (window, gitg_branch_actions_merge (window,
+ window->priv->popup_refs[!source],
+ window->priv->popup_refs[source]));
+}
+
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]