[gitg] Implement cherry picking of single commits
- From: Jesse van den Kieboom <jessevdk src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gitg] Implement cherry picking of single commits
- Date: Sat, 22 Aug 2015 11:55:45 +0000 (UTC)
commit 881cb12a0130c25940c282d11f8e12d8d32d779a
Author: Jesse van den Kieboom <jessevdk gnome org>
Date: Thu Aug 20 00:47:29 2015 +0200
Implement cherry picking of single commits
gitg/Makefile.am | 1 +
gitg/gitg-action-support.vala | 105 +++++++-
gitg/gitg-commit-action-cherry-pick.vala | 282 ++++++++++++++++++
gitg/gitg-ref-action-merge.vala | 81 +-----
gitg/history/gitg-history.vala | 5 +
tests/gitg/Makefile.am | 2 +
tests/gitg/main.vala | 3 +-
tests/gitg/test-cherry-pick-commit.vala | 476 ++++++++++++++++++++++++++++++
tests/gitg/test-merge-ref.vala | 4 +-
9 files changed, 882 insertions(+), 77 deletions(-)
---
diff --git a/gitg/Makefile.am b/gitg/Makefile.am
index 5cdbaa1..52516ca 100644
--- a/gitg/Makefile.am
+++ b/gitg/Makefile.am
@@ -63,6 +63,7 @@ gitg_gitg_VALASOURCES = \
gitg/gitg-commit-action-create-branch.vala \
gitg/gitg-commit-action-create-patch.vala \
gitg/gitg-commit-action-create-tag.vala \
+ gitg/gitg-commit-action-cherry-pick.vala \
gitg/gitg-create-branch-dialog.vala \
gitg/gitg-create-tag-dialog.vala \
gitg/gitg-dash-view.vala \
diff --git a/gitg/gitg-action-support.vala b/gitg/gitg-action-support.vala
index 50ac09b..6eb7ad8 100644
--- a/gitg/gitg-action-support.vala
+++ b/gitg/gitg-action-support.vala
@@ -35,7 +35,9 @@ public class ActionSupport : Object
public async bool working_directory_dirty()
{
- var options = new Ggit.StatusOptions(0, Ggit.StatusShow.WORKDIR_ONLY, null);
+ var options = new Ggit.StatusOptions(Ggit.StatusOption.EXCLUDE_SUBMODULES,
+ Ggit.StatusShow.WORKDIR_ONLY,
+ null);
var is_dirty = false;
yield Async.thread_try(() => {
@@ -133,7 +135,7 @@ public class ActionSupport : Object
if ((yield application.user_query_async(q)) != Gtk.ResponseType.OK)
{
- notification.error(_("Merge failed with conflicts"));
+ notification.error(_("Failed with conflicts"));
return false;
}
@@ -146,7 +148,7 @@ public class ActionSupport : Object
return true;
}
- public async bool checkout_conflicts(SimpleNotification notification, Gitg.Ref reference, Ggit.Index
index, Gitg.Ref source, Gitg.Ref? head)
+ public async bool checkout_conflicts(SimpleNotification notification, Gitg.Ref reference, Ggit.Index
index, Gitg.Ref? head)
{
if (!(yield stash_if_needed(notification, head)))
{
@@ -182,6 +184,103 @@ public class ActionSupport : Object
return true;
}
+
+ public async Ggit.OId? commit_index(SimpleNotification notification,
+ Gitg.Ref reference,
+ Ggit.Index index,
+ owned Ggit.OId[]? parents,
+ Ggit.Signature? author,
+ string message)
+ {
+ var committer = application.get_verified_committer();
+
+ if (committer == null)
+ {
+ notification.error(_("Failed to obtain author details"));
+ return null;
+ }
+
+ if (author == null)
+ {
+ author = committer;
+ }
+
+ var stage = application.repository.stage;
+
+ Gitg.Ref? head = null;
+ var ishead = reference_is_head(reference, ref head);
+
+ Ggit.OId? oid = null;
+ Ggit.Tree? head_tree = null;
+ Gitg.Commit? commit = null;
+
+ try
+ {
+ commit = reference.lookup() as Gitg.Commit;
+ }
+ catch (Error e)
+ {
+ notification.error(_("Failed to lookup commit: %s").printf(e.message));
+ return null;
+ }
+
+ if (ishead)
+ {
+ if (!(yield stash_if_needed(notification, head)))
+ {
+ return null;
+ }
+
+ head_tree = commit.get_tree();
+ }
+
+ if (parents == null)
+ {
+ parents = new Ggit.OId[] { commit.get_id() };
+ }
+
+ try
+ {
+ // TODO: not all hooks are being executed yet
+ oid = yield stage.commit_index(index,
+ ishead ? head : reference,
+ message,
+ author,
+ committer,
+ parents,
+ StageCommitOptions.NONE);
+ }
+ catch (Error e)
+ {
+ notification.error(_("Failed to create commit: %s").printf(e.message));
+ return null;
+ }
+
+ if (ishead)
+ {
+ try
+ {
+ yield Async.thread(() => {
+ var opts = new Ggit.CheckoutOptions();
+
+ opts.set_strategy(Ggit.CheckoutStrategy.SAFE);
+ opts.set_baseline(head_tree);
+
+ var newcommit = application.repository.lookup<Ggit.Commit>(oid);
+ var newtree = newcommit.get_tree();
+
+ application.repository.checkout_tree(newtree, opts);
+ });
+ }
+ catch (Error e)
+ {
+ notification.error(_("Failed to checkout index: %s").printf(e.message));
+ return null;
+ }
+ }
+
+ return oid;
+ }
}
}
diff --git a/gitg/gitg-commit-action-cherry-pick.vala b/gitg/gitg-commit-action-cherry-pick.vala
new file mode 100644
index 0000000..0065d43
--- /dev/null
+++ b/gitg/gitg-commit-action-cherry-pick.vala
@@ -0,0 +1,282 @@
+/*
+ * This file is part of gitg
+ *
+ * Copyright (C) 2015 - Jesse van den Kieboom
+ *
+ * gitg 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * gitg 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 gitg. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace Gitg
+{
+
+class CommitActionCherryPick : GitgExt.UIElement, GitgExt.Action, GitgExt.CommitAction, Object
+{
+ // Do this to pull in config.h before glib.h (for gettext...)
+ private const string version = Gitg.Config.VERSION;
+
+ public GitgExt.Application? application { owned get; construct set; }
+ public GitgExt.RefActionInterface action_interface { get; construct set; }
+ public Gitg.Commit commit { get; construct set; }
+
+ private Gitg.Ref[]? d_destinations;
+ private ActionSupport d_support;
+
+ public CommitActionCherryPick(GitgExt.Application application,
+ GitgExt.RefActionInterface action_interface,
+ Gitg.Commit commit)
+ {
+ Object(application: application,
+ action_interface: action_interface,
+ commit: commit);
+
+ d_support = new ActionSupport(application, action_interface);
+ }
+
+ public string id
+ {
+ owned get { return "/org/gnome/gitg/commit-actions/cherry-pick"; }
+ }
+
+ public string display_name
+ {
+ owned get { return _("Cherry pick onto"); }
+ }
+
+ public string description
+ {
+ owned get { return _("Cherry pick this commit onto a branch"); }
+ }
+
+ public bool available
+ {
+ get { return true; }
+ }
+
+ public bool enabled
+ {
+ get
+ {
+ if (commit.get_parents().get_size() > 1)
+ {
+ return false;
+ }
+
+ ensure_destinations();
+ return d_destinations.length != 0;
+ }
+ }
+
+ private void ensure_destinations()
+ {
+ if (d_destinations != null)
+ {
+ return;
+ }
+
+ d_destinations = new Gitg.Ref[0];
+
+ foreach (var r in action_interface.references)
+ {
+ if (r.is_branch())
+ {
+ try
+ {
+ var c = r.lookup() as Ggit.Commit;
+
+ if (!c.get_id().equal(commit.get_id()))
+ {
+ d_destinations += r;
+ }
+ } catch {}
+ }
+ }
+ }
+
+ private async Ggit.Index? create_index(SimpleNotification notification, Gitg.Ref destination)
+ {
+ Gitg.Commit? theirs = null;
+ string theirs_name = destination.parsed_name.shortname;
+
+ try
+ {
+ theirs = destination.lookup() as Gitg.Commit;
+ }
+ catch (Error e)
+ {
+ notification.error(_("Failed to lookup the commit for branch %s:
%s").printf(@"'$theirs_name'", e.message));
+ return null;
+ }
+
+ var merge_options = new Ggit.MergeOptions();
+ Ggit.Index? index = null;
+
+ try
+ {
+ yield Async.thread(() => {
+ index = application.repository.cherry_pick_commit(commit, theirs, 0,
merge_options);
+ });
+ }
+ catch (Error e)
+ {
+ notification.error(_("Failed to cherry-pick the commit: %s").printf(e.message));
+ return null;
+ }
+
+ return index;
+ }
+
+ private async bool checkout_conflicts(SimpleNotification notification, Ggit.Index index, Gitg.Ref
destination)
+ {
+ var ours_name = commit.get_id().to_string()[0:6];
+ var theirs_name = destination.parsed_name.shortname;
+
+ notification.message = _("Cherry pick has conflicts");
+
+ Gitg.Ref? head = null;
+ var ishead = d_support.reference_is_head(destination, ref head);
+
+ string message;
+
+ if (ishead)
+ {
+ message = _("The cherry pick of %s onto %s has caused conflicts, would you like to
checkout branch %s with the cherry pick to your working directory to resolve the
conflicts?").printf(@"'$ours_name'", @"'$theirs_name'", @"'$theirs_name'");
+ }
+ else
+ {
+ message = _("The cherry-pick of %s onto %s has caused conflicts, would you like to
checkout the cherry pick to your working directory to resolve the conflicts?").printf(@"'$ours_name'",
@"'$theirs_name'");
+ }
+
+ var q = new GitgExt.UserQuery.full(_("Cherry pick has conflicts"),
+ message,
+ Gtk.MessageType.QUESTION,
+ _("Cancel"), Gtk.ResponseType.CANCEL,
+ _("Checkout"), Gtk.ResponseType.OK);
+
+ if ((yield application.user_query_async(q)) != Gtk.ResponseType.OK)
+ {
+ notification.error(_("Cherry pick failed with conflicts"));
+ return false;
+ }
+
+ if (!(yield d_support.checkout_conflicts(notification, destination, index, head)))
+ {
+ return false;
+ }
+
+ write_cherry_pick_state_files();
+
+ notification.success(_("Cherry pick finished with conflicts in working directory"));
+ return true;
+ }
+
+ private void write_cherry_pick_state_files()
+ {
+ var wd = application.repository.get_location().get_path();
+
+ try
+ {
+ FileUtils.set_contents(Path.build_filename(wd, "CHERRY_PICK_HEAD"),
"%s\n".printf(commit.get_id().to_string()));
+ } catch {}
+ }
+
+ public async void cherry_pick(Gitg.Ref destination)
+ {
+ var id = commit.get_id();
+ var shortid = id.to_string()[0:6];
+ var name = destination.parsed_name.shortname;
+
+ var notification = new SimpleNotification(_("Cherry pick %s onto %s").printf(@"'$shortid'",
@"'$name'"));
+
+ application.notifications.add(notification);
+
+ var index = yield create_index(notification, destination);
+
+ if (index == null)
+ {
+ return;
+ }
+
+ if (index.has_conflicts())
+ {
+ yield checkout_conflicts(notification, index, destination);
+ return;
+ }
+
+ var oid = yield d_support.commit_index(notification,
+ destination,
+ index,
+ null,
+ commit.get_author(),
+ commit.get_message());
+
+ if (oid != null) {
+ notification.success(_("Successfully cherry picked"));
+ }
+ }
+
+ private void activate_destination(Gitg.Ref destination)
+ {
+ cherry_pick.begin(destination, (obj, res) => {
+ cherry_pick.end(res);
+ });
+ }
+
+ public void populate_menu(Gtk.Menu menu)
+ {
+ if (!available)
+ {
+ return;
+ }
+
+ ensure_destinations();
+
+ if (!enabled)
+ {
+ return;
+ }
+
+ var item = new Gtk.MenuItem.with_label(display_name);
+ item.tooltip_text = description;
+ item.show();
+
+ var submenu = new Gtk.Menu();
+ submenu.show();
+
+ foreach (var dest in d_destinations)
+ {
+ var name = dest.parsed_name.shortname;
+ var subitem = new Gtk.MenuItem.with_label(name);
+
+ subitem.tooltip_text = _("Cherry pick onto %s").printf(@"'$name'");
+ subitem.show();
+
+ subitem.activate.connect(() => {
+ activate_destination(dest);
+ });
+
+ submenu.append(subitem);
+ }
+
+ item.submenu = submenu;
+ menu.append(item);
+ }
+
+ public void activate()
+ {
+ }
+}
+
+}
+
+// ex:set ts=4 noet
diff --git a/gitg/gitg-ref-action-merge.vala b/gitg/gitg-ref-action-merge.vala
index a368294..7815c2b 100644
--- a/gitg/gitg-ref-action-merge.vala
+++ b/gitg/gitg-ref-action-merge.vala
@@ -200,7 +200,7 @@ class RefActionMerge : GitgExt.UIElement, GitgExt.Action, GitgExt.RefAction, Obj
return false;
}
- if (!(yield d_support.checkout_conflicts(notification, reference, index, source, head)))
+ if (!(yield d_support.checkout_conflicts(notification, reference, index, head)))
{
return false;
}
@@ -255,14 +255,6 @@ class RefActionMerge : GitgExt.UIElement, GitgExt.Action, GitgExt.RefAction, Obj
return null;
}
- var committer = application.get_verified_committer();
-
- if (committer == null)
- {
- notification.error(_("Failed to obtain author details"));
- return null;
- }
-
string msg;
if (source.parsed_name.rtype == RefType.REMOTE)
@@ -274,73 +266,18 @@ class RefActionMerge : GitgExt.UIElement, GitgExt.Action, GitgExt.RefAction, Obj
msg = @"Merge branch '$theirs_name'";
}
- var stage = application.repository.stage;
-
- Gitg.Ref? head = null;
- var ishead = d_support.reference_is_head(reference, ref head);
-
- Ggit.OId? oid = null;
- Ggit.Tree? head_tree = null;
-
- if (ishead)
- {
- if (!(yield d_support.stash_if_needed(notification, head)))
- {
- return null;
- }
-
- try
- {
- head_tree = (reference.lookup() as Ggit.Commit).get_tree();
- }
- catch (Error e)
- {
- notification.error(_("Failed to obtain HEAD tree: %s").printf(e.message));
- return null;
- }
- }
-
- try
- {
- // TODO: not all hooks are being executed yet
- oid = yield stage.commit_index(index,
- ishead ? head : reference,
- msg,
- committer,
- committer,
- new Ggit.OId[] { ours.get_id(), theirs.get_id() },
- StageCommitOptions.NONE);
- }
- catch (Error e)
- {
- notification.error(_("Failed to create commit: %s").printf(e.message));
- return null;
- }
+ var oid = yield d_support.commit_index(notification,
+ reference,
+ index,
+ new Ggit.OId[] { ours.get_id(), theirs.get_id() },
+ null,
+ msg);
- if (ishead)
+ if (oid != null)
{
- try
- {
- yield Async.thread(() => {
- var opts = new Ggit.CheckoutOptions();
-
- opts.set_strategy(Ggit.CheckoutStrategy.SAFE);
- opts.set_baseline(head_tree);
-
- var commit = application.repository.lookup<Ggit.Commit>(oid);
- var tree = commit.get_tree();
-
- application.repository.checkout_tree(tree, opts);
- });
- }
- catch (Error e)
- {
- notification.error(_("Failed to checkout index: %s").printf(e.message));
- return null;
- }
+ notification.success(_("Successfully merged %s into %s").printf(@"'$theirs_name'",
@"'$ours_name'"));
}
- notification.success(_("Successfully merged %s into %s").printf(@"'$theirs_name'",
@"'$ours_name'"));
return oid;
}
diff --git a/gitg/history/gitg-history.vala b/gitg/history/gitg-history.vala
index a85b6cb..b2e3836 100644
--- a/gitg/history/gitg-history.vala
+++ b/gitg/history/gitg-history.vala
@@ -675,6 +675,11 @@ namespace GitgHistory
af,
commit));
+ add_commit_action(actions,
+ new Gitg.CommitActionCherryPick(application,
+ af,
+ commit));
+
var exts = new Peas.ExtensionSet(Gitg.PluginsEngine.get_default(),
typeof(GitgExt.CommitAction),
"application",
diff --git a/tests/gitg/Makefile.am b/tests/gitg/Makefile.am
index b3925ca..7dfd6c1 100644
--- a/tests/gitg/Makefile.am
+++ b/tests/gitg/Makefile.am
@@ -49,12 +49,14 @@ TESTS_GITG_TEST_GITG_COPIED_SOURCES = \
tests/gitg/support-repository.vala \
tests/gitg/gitg-ref-action-checkout.vala \
tests/gitg/gitg-ref-action-merge.vala \
+ tests/gitg/gitg-commit-action-cherry-pick.vala \
tests/gitg/gitg-action-support.vala
tests_gitg_test_gitg_SOURCES = \
tests/gitg/main.vala \
tests/gitg/test-checkout-ref.vala \
tests/gitg/test-merge-ref.vala \
+ tests/gitg/test-cherry-pick-commit.vala \
tests/gitg/simple-notification-mock.vala \
tests/gitg/application-mock.vala \
tests/gitg/notifications-mock.vala \
diff --git a/tests/gitg/main.vala b/tests/gitg/main.vala
index 77a3498..f1b5255 100644
--- a/tests/gitg/main.vala
+++ b/tests/gitg/main.vala
@@ -24,7 +24,8 @@ class Gitg.Test.Runner
var m = new Gitg.Test.Main(args);
m.add(new CheckoutRef(),
- new MergeRef());
+ new MergeRef(),
+ new CherryPickCommit());
m.run();
}
diff --git a/tests/gitg/test-cherry-pick-commit.vala b/tests/gitg/test-cherry-pick-commit.vala
new file mode 100644
index 0000000..ee1959c
--- /dev/null
+++ b/tests/gitg/test-cherry-pick-commit.vala
@@ -0,0 +1,476 @@
+/*
+ * This file is part of gitg
+ *
+ * Copyright (C) 2015 - Jesse van den Kieboom
+ *
+ * gitg 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * gitg 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 gitg. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+using Gitg.Test.Assert;
+
+class Gitg.Test.CherryPickCommit : Application
+{
+ private Gitg.Branch ours;
+
+ private Gitg.Branch theirs;
+ private Gitg.Commit theirs_commit;
+
+ private Gitg.Branch master;
+ private Gitg.Commit master_commit;
+
+ private Gitg.Branch not_master;
+
+ private RefActionInterface action_interface;
+
+ protected override void set_up()
+ {
+ base.set_up();
+
+ commit("a", "a file\n");
+ create_branch("theirs");
+
+ commit("b", "b file\n");
+
+ checkout_branch("theirs");
+ commit("c", "c file\n");
+
+ theirs = lookup_branch("theirs");
+ theirs_commit = theirs.lookup() as Gitg.Commit;
+
+ checkout_branch("master");
+ not_master = create_branch("not_master");
+
+ master = lookup_branch("master");
+ master_commit = master.lookup() as Gitg.Commit;
+
+ action_interface = new RefActionInterface(this);
+ }
+
+ protected virtual signal void test_cherry_pick_simple()
+ {
+ var loop = new MainLoop();
+ var action = new Gitg.CommitActionCherryPick(this, action_interface, theirs_commit);
+
+ action.cherry_pick.begin(master, (obj, res) => {
+ action.cherry_pick.end(res);
+ loop.quit();
+ });
+
+ loop.run();
+
+ assert_inteq(simple_notifications.size, 1);
+ assert_streq(simple_notifications[0].title, "Cherry pick '72af7c' onto 'master'");
+ assert_streq(simple_notifications[0].message, "Successfully cherry picked");
+ assert_inteq(simple_notifications[0].status, SimpleNotification.Status.SUCCESS);
+
+ assert_file_contents("a", "a file\n");
+ assert_file_contents("b", "b file\n");
+ assert_file_contents("c", "c file\n");
+
+ var commit = lookup_commit("master");
+
+ assert_streq(commit.get_message(), "commit c");
+ assert_streq(commit.get_id().to_string(), "87aef9f8f4320a9d997d194614d175254c24adc7");
+ assert_inteq((int)commit.get_author().get_time().to_unix(), 2);
+ assert_inteq((int)commit.get_committer().get_time().to_unix(), 3);
+ }
+
+ protected virtual signal void test_cherry_pick_not_head()
+ {
+ var loop = new MainLoop();
+
+ var action = new Gitg.CommitActionCherryPick(this, action_interface, theirs_commit);
+
+ action.cherry_pick.begin(not_master, (obj, res) => {
+ action.cherry_pick.end(res);
+ loop.quit();
+ });
+
+ loop.run();
+
+ assert_inteq(simple_notifications.size, 1);
+ assert_streq(simple_notifications[0].title, "Cherry pick '72af7c' onto 'not_master'");
+ assert_streq(simple_notifications[0].message, "Successfully cherry picked");
+ assert_inteq(simple_notifications[0].status, SimpleNotification.Status.SUCCESS);
+
+ assert_file_contents("a", "a file\n");
+ assert_file_contents("b", "b file\n");
+ assert_true(!file_exists("c"));
+
+ var commit = lookup_commit("not_master");
+
+ assert_streq(commit.get_message(), "commit c");
+ assert_streq(commit.get_id().to_string(), "87aef9f8f4320a9d997d194614d175254c24adc7");
+ assert_inteq((int)commit.get_author().get_time().to_unix(), 2);
+ assert_inteq((int)commit.get_committer().get_time().to_unix(), 3);
+ }
+
+ protected virtual signal void test_cherry_pick_not_head_would_have_conflicted()
+ {
+ var loop = new MainLoop();
+
+ commit("c", "c file other content\n");
+
+ var action = new Gitg.CommitActionCherryPick(this, action_interface, theirs_commit);
+
+ action.cherry_pick.begin(not_master, (obj, res) => {
+ action.cherry_pick.end(res);
+ loop.quit();
+ });
+
+ loop.run();
+
+ assert_inteq(simple_notifications.size, 1);
+ assert_streq(simple_notifications[0].title, "Cherry pick '72af7c' onto 'not_master'");
+ assert_streq(simple_notifications[0].message, "Successfully cherry picked");
+ assert_inteq(simple_notifications[0].status, SimpleNotification.Status.SUCCESS);
+
+ assert_file_contents("a", "a file\n");
+ assert_file_contents("b", "b file\n");
+ assert_file_contents("c", "c file other content\n");
+
+ var commit = lookup_commit("not_master");
+
+ assert_streq(commit.get_message(), "commit c");
+ assert_streq(commit.get_id().to_string(), "e9e99c25e6061b42b6d48d143e028d1806f85745");
+ assert_inteq((int)commit.get_author().get_time().to_unix(), 2);
+ assert_inteq((int)commit.get_committer().get_time().to_unix(), 4);
+ }
+
+ protected virtual signal void test_cherry_pick_theirs_conflicts_no_checkout()
+ {
+ var loop = new MainLoop();
+
+ commit("c", "c file other content\n");
+ master = lookup_branch("master");
+
+ var action = new Gitg.CommitActionCherryPick(this, action_interface, theirs_commit);
+
+ expect_user_query(new GitgExt.UserQuery.full("Cherry pick has conflicts",
+ "The cherry pick of '72af7c' onto 'master' has
caused conflicts, would you like to checkout branch 'master' with the cherry pick to your working directory
to resolve the conflicts?",
+ Gtk.MessageType.QUESTION,
+ "Cancel",
+ Gtk.ResponseType.CANCEL,
+ "Checkout",
+ Gtk.ResponseType.OK),
+ Gtk.ResponseType.CANCEL);
+
+ action.cherry_pick.begin(master, (obj, res) => {
+ action.cherry_pick.end(res);
+ loop.quit();
+ });
+
+ loop.run();
+
+ assert_inteq(simple_notifications.size, 1);
+ assert_streq(simple_notifications[0].title, "Cherry pick '72af7c' onto 'master'");
+ assert_streq(simple_notifications[0].message, "Cherry pick failed with conflicts");
+ assert_inteq(simple_notifications[0].status, SimpleNotification.Status.ERROR);
+
+ assert_file_contents("a", "a file\n");
+ assert_file_contents("b", "b file\n");
+ assert_file_contents("c", "c file other content\n");
+
+ var commit = lookup_commit("master");
+
+ assert_streq(commit.get_message(), "commit c");
+ assert_streq(commit.get_id().to_string(), "e1219dd5fbcf8fb5b17bbd3db7a9fa88e98d6651");
+ assert_inteq((int)commit.get_author().get_time().to_unix(), 3);
+ assert_inteq((int)commit.get_committer().get_time().to_unix(), 3);
+ }
+
+ protected virtual signal void test_merge_theirs_conflicts_checkout()
+ {
+ var loop = new MainLoop();
+
+ commit("c", "c file other content\n");
+ master = lookup_branch("master");
+
+ var action = new Gitg.CommitActionCherryPick(this, action_interface, theirs_commit);
+
+ expect_user_query(new GitgExt.UserQuery.full("Cherry pick has conflicts",
+ "The cherry pick of '72af7c' onto 'master' has
caused conflicts, would you like to checkout branch 'master' with the cherry pick to your working directory
to resolve the conflicts?",
+ Gtk.MessageType.QUESTION,
+ "Cancel",
+ Gtk.ResponseType.CANCEL,
+ "Checkout",
+ Gtk.ResponseType.OK),
+ Gtk.ResponseType.OK);
+
+ action.cherry_pick.begin(master, (obj, res) => {
+ action.cherry_pick.end(res);
+ loop.quit();
+ });
+
+ loop.run();
+
+ assert_inteq(simple_notifications.size, 1);
+ assert_streq(simple_notifications[0].title, "Cherry pick '72af7c' onto 'master'");
+ assert_streq(simple_notifications[0].message, "Cherry pick finished with conflicts in working
directory");
+ assert_inteq(simple_notifications[0].status, SimpleNotification.Status.SUCCESS);
+
+ assert_file_contents("a", "a file\n");
+ assert_file_contents("b", "b file\n");
+ assert_file_contents("c", "<<<<<<< ours\nc file other content\n=======\nc file\n>>>>>>>
theirs\n");
+
+ assert_file_contents(".git/CHERRY_PICK_HEAD", "72af7ccf47852d832b06c7244de8ae9ded639024\n");
+ }
+
+ protected virtual signal void test_cherry_pick_theirs_dirty_stash()
+ {
+ var loop = new MainLoop();
+
+ write_file("b", "b file other content\n");
+
+ var action = new Gitg.CommitActionCherryPick(this, action_interface, theirs_commit);
+
+ expect_user_query(new GitgExt.UserQuery.full("Unstaged changes",
+ "You appear to have unstaged changes in your
working directory. Would you like to stash the changes before the checkout?",
+ Gtk.MessageType.QUESTION,
+ "Cancel",
+ Gtk.ResponseType.CANCEL,
+ "Stash changes",
+ Gtk.ResponseType.OK),
+ Gtk.ResponseType.OK);
+
+ action.cherry_pick.begin(master, (obj, res) => {
+ action.cherry_pick.end(res);
+ loop.quit();
+ });
+
+ loop.run();
+
+ assert_inteq(simple_notifications.size, 1);
+ assert_streq(simple_notifications[0].title, "Cherry pick '72af7c' onto 'master'");
+ assert_streq(simple_notifications[0].message, "Successfully cherry picked");
+ assert_inteq(simple_notifications[0].status, SimpleNotification.Status.SUCCESS);
+
+ assert_file_contents("a", "a file\n");
+ assert_file_contents("b", "b file\n");
+ assert_file_contents("c", "c file\n");
+
+ var messages = new string[0];
+ var oids = new Ggit.OId[0];
+
+ d_repository.stash_foreach((index, message, oid) => {
+ messages += message;
+ oids += oid;
+
+ return 0;
+ });
+
+ assert_inteq(messages.length, 1);
+ assert_streq(messages[0], "On master: WIP on HEAD: 50ac9b commit b");
+ assert_streq(oids[0].to_string(), "aaf63a72d8c0d5799ccfcf1623daef228968382f");
+
+ var commit = lookup_commit("master");
+
+ assert_streq(commit.get_message(), "commit c");
+ assert_streq(commit.get_id().to_string(), "87aef9f8f4320a9d997d194614d175254c24adc7");
+ assert_inteq((int)commit.get_author().get_time().to_unix(), 2);
+ assert_inteq((int)commit.get_committer().get_time().to_unix(), 3);
+ }
+
+ protected virtual signal void test_cherry_pick_theirs_not_master_conflicts_checkout()
+ {
+ var loop = new MainLoop();
+
+ checkout_branch("not_master");
+ commit("c", "c file other content\n");
+
+ not_master = lookup_branch("not_master");
+ checkout_branch("master");
+
+ var action = new Gitg.CommitActionCherryPick(this, action_interface, theirs_commit);
+
+ expect_user_query(new GitgExt.UserQuery.full("Cherry pick has conflicts",
+ "The cherry-pick of '72af7c' onto 'not_master'
has caused conflicts, would you like to checkout the cherry pick to your working directory to resolve the
conflicts?",
+ Gtk.MessageType.QUESTION,
+ "Cancel",
+ Gtk.ResponseType.CANCEL,
+ "Checkout",
+ Gtk.ResponseType.OK),
+ Gtk.ResponseType.OK);
+
+ expect_user_query(new GitgExt.UserQuery.full("Unstaged changes",
+ "You appear to have unstaged changes in your
working directory. Would you like to stash the changes before the checkout?",
+ Gtk.MessageType.QUESTION,
+ "Cancel",
+ Gtk.ResponseType.CANCEL,
+ "Stash changes",
+ Gtk.ResponseType.OK),
+ Gtk.ResponseType.OK);
+
+ action.cherry_pick.begin(not_master, (obj, res) => {
+ action.cherry_pick.end(res);
+ loop.quit();
+ });
+
+ loop.run();
+
+ assert_inteq(simple_notifications.size, 2);
+
+ assert_streq(simple_notifications[0].title, "Cherry pick '72af7c' onto 'not_master'");
+ assert_streq(simple_notifications[0].message, "Cherry pick finished with conflicts in working
directory");
+ assert_inteq(simple_notifications[0].status, SimpleNotification.Status.SUCCESS);
+
+ assert_streq(simple_notifications[1].title, "Checkout 'not_master'");
+ assert_streq(simple_notifications[1].message, "Successfully checked out branch to working
directory");
+ assert_inteq(simple_notifications[1].status, SimpleNotification.Status.SUCCESS);
+
+ assert_streq(lookup_branch("HEAD").get_name(), "refs/heads/not_master");
+
+ assert_file_contents("a", "a file\n");
+ assert_file_contents("b", "b file\n");
+ assert_file_contents("c", "<<<<<<< ours\nc file other content\n=======\nc file\n>>>>>>>
theirs\n");
+
+ assert_file_contents(".git/CHERRY_PICK_HEAD", "72af7ccf47852d832b06c7244de8ae9ded639024\n");
+ }
+
+ protected virtual signal void test_cherry_pick_theirs_not_master_conflicts_checkout_dirty()
+ {
+ var loop = new MainLoop();
+
+ checkout_branch("not_master");
+ commit("c", "c file other content\n");
+
+ not_master = lookup_branch("not_master");
+ checkout_branch("master");
+
+ write_file("b", "b file other content\n");
+
+ var action = new Gitg.CommitActionCherryPick(this, action_interface, theirs_commit);
+
+ expect_user_query(new GitgExt.UserQuery.full("Cherry pick has conflicts",
+ "The cherry-pick of '72af7c' onto 'not_master'
has caused conflicts, would you like to checkout the cherry pick to your working directory to resolve the
conflicts?",
+ Gtk.MessageType.QUESTION,
+ "Cancel",
+ Gtk.ResponseType.CANCEL,
+ "Checkout",
+ Gtk.ResponseType.OK),
+ Gtk.ResponseType.OK);
+
+ expect_user_query(new GitgExt.UserQuery.full("Unstaged changes",
+ "You appear to have unstaged changes in your
working directory. Would you like to stash the changes before the checkout?",
+ Gtk.MessageType.QUESTION,
+ "Cancel",
+ Gtk.ResponseType.CANCEL,
+ "Stash changes",
+ Gtk.ResponseType.OK),
+ Gtk.ResponseType.OK);
+
+ action.cherry_pick.begin(not_master, (obj, res) => {
+ action.cherry_pick.end(res);
+ loop.quit();
+ });
+
+ loop.run();
+
+ assert_inteq(simple_notifications.size, 2);
+
+ assert_streq(simple_notifications[0].title, "Cherry pick '72af7c' onto 'not_master'");
+ assert_streq(simple_notifications[0].message, "Cherry pick finished with conflicts in working
directory");
+ assert_inteq(simple_notifications[0].status, SimpleNotification.Status.SUCCESS);
+
+ assert_streq(simple_notifications[1].title, "Checkout 'not_master'");
+ assert_streq(simple_notifications[1].message, "Successfully checked out branch to working
directory");
+ assert_inteq(simple_notifications[1].status, SimpleNotification.Status.SUCCESS);
+
+ assert_streq(lookup_branch("HEAD").get_name(), "refs/heads/not_master");
+
+ assert_file_contents("a", "a file\n");
+ assert_file_contents("b", "b file\n");
+ assert_file_contents("c", "<<<<<<< ours\nc file other content\n=======\nc file\n>>>>>>>
theirs\n");
+
+ assert_file_contents(".git/CHERRY_PICK_HEAD", "72af7ccf47852d832b06c7244de8ae9ded639024\n");
+
+ var messages = new string[0];
+ var oids = new Ggit.OId[0];
+
+ d_repository.stash_foreach((index, message, oid) => {
+ messages += message;
+ oids += oid;
+
+ return 0;
+ });
+
+ assert_inteq(messages.length, 1);
+ assert_streq(messages[0], "On master: WIP on HEAD");
+ assert_streq(oids[0].to_string(), "147b7b7b6ad2f9c90f4c93f3bfda78c78ec2dcde");
+ }
+
+ protected virtual signal void test_cherry_pick_theirs_not_master_conflicts_checkout_dirty_no_stash()
+ {
+ var loop = new MainLoop();
+
+ checkout_branch("not_master");
+ commit("c", "c file other content\n");
+
+ not_master = lookup_branch("not_master");
+ checkout_branch("master");
+
+ write_file("b", "b file other content\n");
+
+ var action = new Gitg.CommitActionCherryPick(this, action_interface, theirs_commit);
+
+ expect_user_query(new GitgExt.UserQuery.full("Cherry pick has conflicts",
+ "The cherry-pick of '72af7c' onto 'not_master'
has caused conflicts, would you like to checkout the cherry pick to your working directory to resolve the
conflicts?",
+ Gtk.MessageType.QUESTION,
+ "Cancel",
+ Gtk.ResponseType.CANCEL,
+ "Checkout",
+ Gtk.ResponseType.OK),
+ Gtk.ResponseType.OK);
+
+ expect_user_query(new GitgExt.UserQuery.full("Unstaged changes",
+ "You appear to have unstaged changes in your
working directory. Would you like to stash the changes before the checkout?",
+ Gtk.MessageType.QUESTION,
+ "Cancel",
+ Gtk.ResponseType.CANCEL,
+ "Stash changes",
+ Gtk.ResponseType.OK),
+ Gtk.ResponseType.CANCEL);
+
+ action.cherry_pick.begin(not_master, (obj, res) => {
+ action.cherry_pick.end(res);
+ loop.quit();
+ });
+
+ loop.run();
+
+ assert_inteq(simple_notifications.size, 1);
+
+ assert_streq(simple_notifications[0].title, "Cherry pick '72af7c' onto 'not_master'");
+ assert_streq(simple_notifications[0].message, "Failed with conflicts");
+ assert_inteq(simple_notifications[0].status, SimpleNotification.Status.ERROR);
+
+ assert_streq(lookup_branch("HEAD").get_name(), "refs/heads/master");
+
+ assert_file_contents("a", "a file\n");
+ assert_file_contents("b", "b file other content\n");
+ assert(!file_exists("c"));
+
+ assert(!file_exists(".git/ORIG_HEAD"));
+ assert(!file_exists(".git/MERGE_HEAD"));
+ assert(!file_exists(".git/MERGE_MODE"));
+ assert(!file_exists(".git/MERGE_MSG"));
+
+ d_repository.stash_foreach((index, message, oid) => {
+ assert(false);
+ return 0;
+ });
+ }
+}
+
+// ex:set ts=4 noet
diff --git a/tests/gitg/test-merge-ref.vala b/tests/gitg/test-merge-ref.vala
index 006c99d..13137c3 100644
--- a/tests/gitg/test-merge-ref.vala
+++ b/tests/gitg/test-merge-ref.vala
@@ -271,6 +271,8 @@ class Gitg.Test.MergeRef : Application
return 0;
});
+ assert_merged(ours_oid, theirs_oid, "master");
+
assert_inteq(messages.length, 1);
assert_streq(messages[0], "On master: WIP on HEAD: 50ac9b commit b");
assert_streq(oids[0].to_string(), "aaf63a72d8c0d5799ccfcf1623daef228968382f");
@@ -458,7 +460,7 @@ class Gitg.Test.MergeRef : Application
assert_inteq(simple_notifications.size, 1);
assert_streq(simple_notifications[0].title, "Merge 'theirs' into 'not_master'");
- assert_streq(simple_notifications[0].message, "Merge failed with conflicts");
+ assert_streq(simple_notifications[0].message, "Failed with conflicts");
assert_inteq(simple_notifications[0].status, SimpleNotification.Status.ERROR);
assert_streq(lookup_branch("HEAD").get_name(), "refs/heads/master");
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]