From 684f9172e90670683b12dc0c64ffb7c1e5086132 Mon Sep 17 00:00:00 2001 From: Jesse van den Kieboom Date: Sat, 9 Jan 2010 22:18:28 +0100 Subject: [PATCH] Implemented cherry-picking --- gitg/gitg-branch-actions.c | 182 +++++++++++++++++++++++++++++++++++++ gitg/gitg-branch-actions.h | 2 + gitg/gitg-dnd.c | 26 ++++-- gitg/gitg-dnd.h | 8 +- gitg/gitg-window.c | 43 ++++++++- 5 files changed, 253 insertions(+), 8 deletions(-) diff --git a/gitg/gitg-branch-actions.c b/gitg/gitg-branch-actions.c index 7f83beea..68f9a175 100644 --- a/gitg/gitg-branch-actions.c +++ b/gitg/gitg-branch-actions.c @@ -1594,3 +1594,185 @@ gitg_branch_actions_tag (GitgWindow *window, gchar const *sha1, gchar const *nam return TRUE; } } + +typedef struct +{ + GitgRevision *revision; + GitgRef *dest; + + gchar *stashcommit; + GitgRef *head; +} CherryPickInfo; + +static CherryPickInfo * +cherry_pick_info_new (GitgRevision *revision, GitgRef *dest) +{ + CherryPickInfo *ret = g_slice_new0 (CherryPickInfo); + + ret->revision = gitg_revision_ref (revision); + ret->dest = gitg_ref_copy (dest); + + return ret; +} + +static void +cherry_pick_info_free (CherryPickInfo *info) +{ + gitg_revision_unref (info->revision); + gitg_ref_free (info->dest); + + g_free (info->stashcommit); + gitg_ref_free (info->head); + + g_slice_free (CherryPickInfo, info); +} + +static void +on_cherry_pick_result (GitgWindow *window, + GitgProgress progress, + gpointer data) +{ + CherryPickInfo *info = (CherryPickInfo *)data; + + if (progress == GITG_PROGRESS_ERROR) + { + gchar const *message; + + message_dialog (window, + GTK_MESSAGE_ERROR, + _("Failed to cherry-pick on <%s>"), + NULL, + NULL, + 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)); + } + + cherry_pick_info_free (info); +} + +GitgRunner * +gitg_branch_actions_cherry_pick (GitgWindow *window, + GitgRevision *revision, + GitgRef *dest) +{ + g_return_val_if_fail (GITG_IS_WINDOW (window), NULL); + g_return_val_if_fail (revision != NULL, NULL); + g_return_val_if_fail (dest != NULL, NULL); + + gchar *message = g_strdup_printf (_("Are you sure you want to cherry-pick that revision on <%s>?"), + gitg_ref_get_shortname (dest)); + + if (message_dialog (window, + GTK_MESSAGE_QUESTION, + _("Cherry-pick"), + message, + _("Cherry-pick")) != GTK_RESPONSE_ACCEPT) + { + g_free (message); + return NULL; + } + + gchar *stashcommit; + + if (!stash_changes (window, &stashcommit, FALSE)) + { + return NULL; + } + + GitgRepository *repository = gitg_window_get_repository (window); + GitgRef *head = gitg_repository_get_current_working_ref (repository); + + // First checkout the correct branch on which to cherry-pick + 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 cherry-pick could not be checked out"), + NULL, + gitg_ref_get_shortname (dest)); + + return NULL; + } + + message = g_strdup_printf (_("Cherry-picking on <%s>"), + gitg_ref_get_shortname (dest)); + + GitgRunner *ret; + + CherryPickInfo *info = cherry_pick_info_new (revision, dest); + + info->stashcommit = stashcommit; + info->head = gitg_ref_copy (head); + + gchar *sha1 = gitg_revision_get_sha1 (revision); + + ret = run_progress (window, + _("Cherry-pick"), + message, + on_cherry_pick_result, + info, + "cherry-pick", + sha1, + NULL); + + g_free (message); + gitg_ref_free (head); + g_free (sha1); + + return ret; +} diff --git a/gitg/gitg-branch-actions.h b/gitg/gitg-branch-actions.h index 80ab57d1..b7b58592 100644 --- a/gitg/gitg-branch-actions.h +++ b/gitg/gitg-branch-actions.h @@ -42,6 +42,8 @@ gboolean gitg_branch_actions_apply_stash (GitgWindow *window, GitgRef *stash, Gi gboolean gitg_branch_actions_tag (GitgWindow *window, gchar const *sha1, gchar const *name, gchar const *message, gboolean sign); +GitgRunner *gitg_branch_actions_cherry_pick (GitgWindow *window, GitgRevision *revision, GitgRef *dest); + G_END_DECLS #endif /* __GITG_BRANCH_ACTIONS_H__ */ diff --git a/gitg/gitg-dnd.c b/gitg/gitg-dnd.c index 72152a19..949f096d 100644 --- a/gitg/gitg-dnd.c +++ b/gitg/gitg-dnd.c @@ -29,6 +29,7 @@ typedef struct GitgRef *cursor_ref; GitgDndCallback callback; + GitgDndRevisionCallback revision_callback; gpointer callback_data; gdouble x; @@ -554,7 +555,7 @@ gitg_drag_source_motion_cb (GtkWidget *widget, guint time, GitgDndData *data) { - if (!data->ref) + if (!data->ref && !data->revision) { return FALSE; } @@ -587,13 +588,18 @@ gitg_drag_source_motion_cb (GtkWidget *widget, gtk_widget_queue_draw (widget); } - if (data->callback) + if (data->ref && data->callback) { data->callback (data->ref, ref, FALSE, data->callback_data); } + else if (data->revision && data->revision_callback) + { + data->revision_callback (data->revision, ref, FALSE, data->callback_data); + } } - if (ref && can_drop (data->ref, ref)) + if ((data->ref && ref && can_drop (data->ref, ref)) || + (data->revision && ref && gitg_ref_get_ref_type (ref) == GITG_REF_TYPE_BRANCH)) { if (ref != data->target) { @@ -627,17 +633,21 @@ gitg_drag_source_drop_cb (GtkWidget *widget, guint time, GitgDndData *data) { - if (!data->ref || !data->target) + if (!(data->ref || data->revision) || !data->target) { return FALSE; } gboolean ret = FALSE; - if (data->callback) + if (data->ref && data->callback) { ret = data->callback (data->ref, data->target, TRUE, data->callback_data); } + else if (data->revision && data->revision_callback) + { + ret = data->revision_callback (data->revision, data->target, TRUE, data->callback_data); + } gtk_drag_finish (context, ret, FALSE, time); return ret; @@ -784,7 +794,10 @@ gitg_drag_source_data_get_cb (GtkWidget *widget, } void -gitg_dnd_enable (GtkTreeView *tree_view, GitgDndCallback callback, gpointer callback_data) +gitg_dnd_enable (GtkTreeView *tree_view, + GitgDndCallback callback, + GitgDndRevisionCallback revision_callback, + gpointer callback_data) { if (GITG_DND_GET_DATA (tree_view)) { @@ -795,6 +808,7 @@ gitg_dnd_enable (GtkTreeView *tree_view, GitgDndCallback callback, gpointer call data->tree_view = tree_view; data->callback = callback; + data->revision_callback = revision_callback; data->callback_data = callback_data; g_object_set_data_full (G_OBJECT (tree_view), diff --git a/gitg/gitg-dnd.h b/gitg/gitg-dnd.h index 37a211b1..cfce9f77 100644 --- a/gitg/gitg-dnd.h +++ b/gitg/gitg-dnd.h @@ -3,12 +3,18 @@ #include #include "gitg-ref.h" +#include "gitg-revision.h" G_BEGIN_DECLS typedef gboolean (*GitgDndCallback)(GitgRef *source, GitgRef *dest, gboolean dropped, gpointer callback_data); +typedef gboolean (*GitgDndRevisionCallback)(GitgRevision *source, GitgRef *dest, gboolean dropped, gpointer callback_data); + +void gitg_dnd_enable (GtkTreeView *tree_view, + GitgDndCallback callback, + GitgDndRevisionCallback revision_callback, + gpointer callback_data); -void gitg_dnd_enable (GtkTreeView *tree_view, GitgDndCallback callback, gpointer callback_data); void gitg_dnd_disable (GtkTreeView *tree_view); G_END_DECLS diff --git a/gitg/gitg-window.c b/gitg/gitg-window.c index 822ff942..6583758e 100644 --- a/gitg/gitg-window.c +++ b/gitg/gitg-window.c @@ -580,6 +580,44 @@ on_refs_dnd (GitgRef *source, GitgRef *dest, gboolean dropped, GitgWindow *windo return ret; } +static void +update_revision_dnd_status (GitgWindow *window, GitgRevision *source, GitgRef *dest) +{ + if (!dest) + { + gtk_statusbar_push (window->priv->statusbar, 0, ""); + } + else + { + gchar *message = g_strdup_printf (_("Cherry-pick revision on <%s>"), + gitg_ref_get_shortname (dest)); + + gtk_statusbar_push (window->priv->statusbar, 0, message); + g_free (message); + } +} + +static gboolean +on_revision_dnd (GitgRevision *source, + GitgRef *dest, + gboolean dropped, + GitgWindow *window) +{ + if (!dropped) + { + update_revision_dnd_status (window, source, dest); + return FALSE; + } + + if (gitg_ref_get_ref_type (dest) != GITG_REF_TYPE_BRANCH) + { + return FALSE; + } + + return add_branch_action (window, + gitg_branch_actions_cherry_pick (window, source, dest)); +} + static void init_tree_view (GitgWindow *window, GtkBuilder *builder) { @@ -589,7 +627,10 @@ init_tree_view (GitgWindow *window, GtkBuilder *builder) gtk_tree_view_column_set_cell_data_func(col, GTK_CELL_RENDERER(window->priv->renderer_path), (GtkTreeCellDataFunc)on_renderer_path, window, NULL); - gitg_dnd_enable (window->priv->tree_view, (GitgDndCallback)on_refs_dnd, window); + gitg_dnd_enable (window->priv->tree_view, + (GitgDndCallback)on_refs_dnd, + (GitgDndRevisionCallback)on_revision_dnd, + window); } static void