[gitg] Add basic merging support
- From: Jesse van den Kieboom <jessevdk src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gitg] Add basic merging support
- Date: Thu, 13 Aug 2015 22:59:05 +0000 (UTC)
commit 8eb1e3ed662c4bc9c05b74965f8919b1aeabf438
Author: Jesse van den Kieboom <jessevdk gnome org>
Date: Fri Aug 14 00:55:43 2015 +0200
Add basic merging support
gitg/Makefile.am | 1 +
gitg/gitg-ref-action-merge.vala | 708 ++++++++++++++++++++++++++++++
gitg/history/gitg-history.vala | 39 ++-
libgitg/gitg-stage.vala | 19 +-
tests/gitg/Makefile.am | 4 +-
tests/gitg/application-mock.vala | 44 ++-
tests/gitg/main.vala | 3 +-
tests/gitg/simple-notification-mock.vala | 2 +-
tests/gitg/test-merge-ref.vala | 482 ++++++++++++++++++++
tests/support/gitg-assert.h | 13 +-
tests/support/gitg-assert.vapi | 1 +
tests/support/repository.vala | 40 ++-
12 files changed, 1337 insertions(+), 19 deletions(-)
---
diff --git a/gitg/Makefile.am b/gitg/Makefile.am
index 4be7d14..d3d70e8 100644
--- a/gitg/Makefile.am
+++ b/gitg/Makefile.am
@@ -75,6 +75,7 @@ gitg_gitg_VALASOURCES = \
gitg/gitg-ref-action-copy-name.vala \
gitg/gitg-ref-action-delete.vala \
gitg/gitg-ref-action-fetch.vala \
+ gitg/gitg-ref-action-merge.vala \
gitg/gitg-ref-action-rename.vala \
gitg/gitg-remote-manager.vala \
gitg/gitg-remote-notification.vala \
diff --git a/gitg/gitg-ref-action-merge.vala b/gitg/gitg-ref-action-merge.vala
new file mode 100644
index 0000000..346fa82
--- /dev/null
+++ b/gitg/gitg-ref-action-merge.vala
@@ -0,0 +1,708 @@
+/*
+ * 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 RefActionMerge : GitgExt.UIElement, GitgExt.Action, GitgExt.RefAction, 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.Ref reference { get; construct set; }
+
+ private struct RemoteSource
+ {
+ public string name;
+ public Gitg.Ref[] sources;
+ }
+
+ private bool d_has_sourced;
+ private Gitg.Ref? d_upstream;
+ private Gitg.Ref[]? d_local_sources;
+ private RemoteSource[]? d_remote_sources;
+ private Gitg.Ref[]? d_tag_sources;
+
+ public RefActionMerge(GitgExt.Application application,
+ GitgExt.RefActionInterface action_interface,
+ Gitg.Ref reference)
+ {
+ Object(application: application,
+ action_interface: action_interface,
+ reference: reference);
+ }
+
+ public string id
+ {
+ owned get { return "/org/gnome/gitg/ref-actions/merge"; }
+ }
+
+ public string display_name
+ {
+ owned get { return _("Merge into %s").printf(reference.parsed_name.shortname); }
+ }
+
+ public string description
+ {
+ // TODO
+ owned get { return _("Merge another branch into branch
%s").printf(reference.parsed_name.shortname); }
+ }
+
+ public bool available
+ {
+ get
+ {
+ return reference.is_branch();
+ }
+ }
+
+ public bool enabled
+ {
+ get
+ {
+ ensure_sources();
+
+ return d_upstream != null ||
+ d_local_sources.length != 0 ||
+ d_remote_sources.length != 0 ||
+ d_tag_sources.length != 0;
+ }
+ }
+
+ private async Ggit.Index create_merge_index(SimpleNotification notification, Ggit.Commit ours,
Ggit.Commit theirs)
+ {
+ Ggit.Index? index = null;
+
+ yield Async.thread_try(() => {
+ var options = new Ggit.MergeOptions();
+
+ try
+ {
+ index = application.repository.merge_commits(ours, theirs, options);
+ }
+ catch (Error e)
+ {
+ notification.error(_("Failed to merge commits: %s").printf(e.message));
+ return;
+ }
+ });
+
+ return index;
+ }
+
+ private async bool working_directory_dirty()
+ {
+ var options = new Ggit.StatusOptions(0, Ggit.StatusShow.WORKDIR_ONLY, null);
+ var is_dirty = false;
+
+ yield Async.thread_try(() => {
+ application.repository.file_status_foreach(options, (path, flags) => {
+ is_dirty = true;
+ return -1;
+ });
+ });
+
+ return is_dirty;
+ }
+
+ private async bool save_stash(SimpleNotification notification, Gitg.Ref? head)
+ {
+ var committer = application.get_verified_committer();
+
+ if (committer == null)
+ {
+ return false;
+ }
+
+ try
+ {
+ yield Async.thread(() => {
+ // Try to stash changes
+ string message;
+
+ if (head != null)
+ {
+ var headname = head.parsed_name.shortname;
+
+ try
+ {
+ var head_commit = head.resolve().lookup() as Ggit.Commit;
+ var shortid = head_commit.get_id().to_string()[0:6];
+ var subject = head_commit.get_subject();
+
+ message = @"WIP on $(headname): $(shortid) $(subject)";
+ }
+ catch
+ {
+ message = @"WIP on $(headname)";
+ }
+ }
+ else
+ {
+ message = "WIP on HEAD";
+ }
+
+ application.repository.save_stash(committer, message,
Ggit.StashFlags.DEFAULT);
+ });
+ }
+ catch (Error err)
+ {
+ notification.error(_("Failed to stash changes: %s").printf(err.message));
+ return false;
+ }
+
+ return true;
+ }
+
+ private bool reference_is_head(ref Gitg.Ref? head)
+ {
+ var branch = reference as Ggit.Branch;
+ head = null;
+
+ if (branch == null)
+ {
+ return false;
+ }
+
+ try
+ {
+ if (!branch.is_head())
+ {
+ return false;
+ }
+
+ head = application.repository.lookup_reference("HEAD");
+ } catch {}
+
+ return head != null;
+ }
+
+ private async bool stash_if_needed(SimpleNotification notification, Gitg.Ref head)
+ {
+ // Offer to stash if there are any local changes
+ if ((yield working_directory_dirty()))
+ {
+ var q = 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);
+
+ if ((yield application.user_query_async(q)) != Gtk.ResponseType.OK)
+ {
+ notification.error(_("Merge failed with conflicts"));
+ return false;
+ }
+
+ if (!(yield save_stash(notification, head)))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private async bool checkout_conflicts(SimpleNotification notification, Ggit.Index index, Gitg.Ref
source)
+ {
+ var ours_name = reference.parsed_name.shortname;
+ var theirs_name = source.parsed_name.shortname;
+
+ notification.message = _("Merge has conflicts");
+
+ Gitg.Ref? head = null;
+ var ishead = reference_is_head(ref head);
+
+ string message;
+
+ if (ishead)
+ {
+ message = _("The merge of %s into %s has caused conflicts, would you like to checkout
branch %s with the merge to your working directory to resolve the conflicts?").printf(@"'$theirs_name'",
@"'$ours_name'", @"'$ours_name'");
+ }
+ else
+ {
+ message = _("The merge of %s into %s has caused conflicts, would you like to checkout
the merge to your working directory to resolve the conflicts?").printf(@"'$theirs_name'", @"'$ours_name'");
+ }
+
+ var q = new GitgExt.UserQuery.full(_("Merge 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(_("Merge failed with conflicts"));
+ return false;
+ }
+
+ if (!(yield stash_if_needed(notification, head)))
+ {
+ return false;
+ }
+
+ if (!ishead)
+ {
+ // Perform checkout of the local branch first
+ var checkout = new RefActionCheckout(application, action_interface, reference);
+
+ if (!(yield checkout.checkout()))
+ {
+ notification.error(_("Merge failed with conflicts"));
+ return false;
+ }
+ }
+
+ // Finally, checkout the conflicted index
+ try
+ {
+ yield Async.thread(() => {
+ var opts = new Ggit.CheckoutOptions();
+ opts.set_strategy(Ggit.CheckoutStrategy.SAFE);
+ application.repository.checkout_index(index, opts);
+ });
+ }
+ catch (Error err)
+ {
+ notification.error(_("Failed to checkout conflicts: %s").printf(err.message));
+ return false;
+ }
+
+ // Write the merge state files
+ var wd = application.repository.get_location().get_path();
+
+ try
+ {
+ var dest_oid = reference.resolve().get_target();
+
+ FileUtils.set_contents(Path.build_filename(wd, "ORIG_HEAD"),
"%s\n".printf(dest_oid.to_string()));
+ } catch {}
+
+ try
+ {
+ var source_oid = source.resolve().get_target();
+
+ FileUtils.set_contents(Path.build_filename(wd, "MERGE_HEAD"),
"%s\n".printf(source_oid.to_string()));
+ } catch {}
+
+ try
+ {
+ FileUtils.set_contents(Path.build_filename(wd, "MERGE_MODE"), "no-ff\n");
+ } catch {}
+
+ try
+ {
+ string msg;
+
+ if (source.parsed_name.rtype == RefType.REMOTE)
+ {
+ msg = @"Merge remote branch '$theirs_name'";
+ }
+ else
+ {
+ msg = @"Merge branch '$theirs_name'";
+ }
+
+ msg += "\n\nConflicts:\n";
+
+ var entries = index.get_entries();
+ var seen = new Gee.HashSet<string>();
+
+ for (var i = 0; i < entries.size(); i++)
+ {
+ var entry = entries.get_by_index(i);
+ var p = entry.get_path();
+
+ if (entry.is_conflict() && !seen.contains(p))
+ {
+ msg += "\t%s\n".printf(p);
+ seen.add(p);
+ }
+ }
+
+ FileUtils.set_contents(Path.build_filename(wd, "MERGE_MSG"), msg);
+ } catch {}
+
+ notification.success(_("Finished merge with conflicts in working directory"));
+ return true;
+ }
+
+ public async Ggit.OId? merge(Gitg.Ref source)
+ {
+ Ggit.Commit ours;
+ Ggit.Commit theirs;
+
+ var ours_name = reference.parsed_name.shortname;
+ var theirs_name = source.parsed_name.shortname;
+
+ var notification = new SimpleNotification(_("Merge %s into %s").printf(@"'$theirs_name'",
@"'$ours_name'"));
+ application.notifications.add(notification);
+
+ try
+ {
+ ours = reference.resolve().lookup() as Ggit.Commit;
+ }
+ catch (Error e)
+ {
+ notification.error(_("Failed to lookup our commit: %s").printf(e.message));
+ return null;
+ }
+
+ try
+ {
+ theirs = source.resolve().lookup() as Ggit.Commit;
+ }
+ catch (Error e)
+ {
+ notification.error(_("Failed to lookup their commit: %s").printf(e.message));
+ return null;
+ }
+
+ var index = yield create_merge_index(notification, ours, theirs);
+
+ if (index == null)
+ {
+ return null;
+ }
+
+ if (index.has_conflicts())
+ {
+ yield checkout_conflicts(notification, index, source);
+ 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)
+ {
+ msg = @"Merge remote branch '$theirs_name'";
+ }
+ else
+ {
+ msg = @"Merge branch '$theirs_name'";
+ }
+
+ var stage = application.repository.stage;
+
+ Gitg.Ref? head = null;
+ var ishead = reference_is_head(ref head);
+
+ Ggit.OId? oid = null;
+ Ggit.Tree? head_tree = null;
+
+ if (ishead)
+ {
+ if (!(yield 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;
+ }
+
+ if (ishead)
+ {
+ 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'"));
+ return oid;
+ }
+
+ public void activate_source(Gitg.Ref source)
+ {
+ merge.begin(source, (obj, res) => {
+ merge.end(res);
+ });
+ }
+
+ private Gitg.Ref? upstream_reference()
+ {
+ var branch = reference as Ggit.Branch;
+
+ if (branch != null)
+ {
+ try
+ {
+ return branch.get_upstream() as Gitg.Ref;
+ } catch {}
+ }
+
+ return null;
+ }
+
+ private void add_merge_source(Gtk.Menu submenu, Gitg.Ref? source)
+ {
+ if (source == null)
+ {
+ var sep = new Gtk.SeparatorMenuItem();
+ sep.show();
+ submenu.append(sep);
+ return;
+ }
+
+ var name = source.parsed_name.shortname;
+ var item = new Gtk.MenuItem.with_label(name);
+
+ item.show();
+ item.tooltip_text = _("Merge %s into branch %s").printf(@"'$name'",
@"'$(reference.parsed_name.shortname)'");
+
+ item.activate.connect(() => {
+ activate_source(source);
+ });
+
+ submenu.append(item);
+ }
+
+ private void ensure_sources()
+ {
+ if (d_has_sourced)
+ {
+ return;
+ }
+
+ d_has_sourced = true;
+
+ if (!available)
+ {
+ return;
+ }
+
+ // Allow merging from remotes and other local branches, offer
+ // to merge upstream first.
+ d_upstream = upstream_reference();
+
+ d_local_sources = new Gitg.Ref[0];
+ d_remote_sources = new RemoteSource[0];
+ d_tag_sources = new Gitg.Ref[0];
+
+ Ggit.OId? target_oid = null;
+
+ try
+ {
+ target_oid = reference.resolve().get_target();
+ } catch {}
+
+ string? last_remote = null;
+
+ foreach (var r in action_interface.references)
+ {
+ if (d_upstream != null && r.get_name() == d_upstream.get_name())
+ {
+ continue;
+ }
+
+ // Filter out things where merging is a noop
+ if (target_oid != null)
+ {
+ Ggit.OId? oid = null;
+
+ try
+ {
+ oid = r.resolve().get_target();
+ } catch {}
+
+ if (oid != null && oid.equal(target_oid))
+ {
+ continue;
+ }
+ }
+
+ if (r.is_branch())
+ {
+ d_local_sources += r;
+ }
+ else if (r.is_tag())
+ {
+ d_tag_sources += r;
+ }
+ else if (r.parsed_name.rtype == RefType.REMOTE)
+ {
+ var remote_name = r.parsed_name.remote_name;
+
+ if (remote_name != last_remote)
+ {
+ var source = RemoteSource() {
+ name = remote_name,
+ sources = new Gitg.Ref[] { r }
+ };
+
+ d_remote_sources += source;
+ }
+ else
+ {
+ d_remote_sources[d_remote_sources.length - 1].sources += r;
+ }
+
+ last_remote = remote_name;
+ }
+ }
+ }
+
+ public void populate_menu(Gtk.Menu menu)
+ {
+ if (!available)
+ {
+ return;
+ }
+
+ var item = new Gtk.MenuItem.with_label(display_name);
+ item.tooltip_text = description;
+
+ if (enabled)
+ {
+ var submenu = new Gtk.Menu();
+ submenu.show();
+
+ if (d_upstream != null)
+ {
+ add_merge_source(submenu, d_upstream);
+ }
+
+ if (d_local_sources.length != 0)
+ {
+ if (d_upstream != null)
+ {
+ // Add a separator
+ add_merge_source(submenu, null);
+ }
+
+ foreach (var source in d_local_sources)
+ {
+ add_merge_source(submenu, source);
+ }
+ }
+
+ if (d_remote_sources.length != 0)
+ {
+ if (d_local_sources.length != 0 || d_upstream != null)
+ {
+ // Add a separator
+ add_merge_source(submenu, null);
+ }
+
+ foreach (var remote in d_remote_sources)
+ {
+ var subitem = new Gtk.MenuItem.with_label(remote.name);
+ subitem.show();
+
+ var subsubmenu = new Gtk.Menu();
+ subsubmenu.show();
+
+ foreach (var source in remote.sources)
+ {
+ add_merge_source(subsubmenu, source);
+ }
+
+ subitem.submenu = subsubmenu;
+ submenu.append(subitem);
+ }
+ }
+
+ if (d_tag_sources.length != 0)
+ {
+ if (d_remote_sources.length != 0 || d_local_sources.length != 0 || d_upstream
!= null)
+ {
+ // Add a separator
+ add_merge_source(submenu, null);
+ }
+
+ var subitem = new Gtk.MenuItem.with_label(_("Tags"));
+ subitem.show();
+
+ var subsubmenu = new Gtk.Menu();
+ subsubmenu.show();
+
+ foreach (var source in d_tag_sources)
+ {
+ add_merge_source(subsubmenu, source);
+ }
+
+ subitem.submenu = subsubmenu;
+ submenu.append(subitem);
+ }
+
+ item.submenu = submenu;
+ }
+ else
+ {
+ item.sensitive = false;
+ }
+
+ item.show();
+ menu.append(item);
+ }
+}
+
+}
+
+// ex:set ts=4 noet
diff --git a/gitg/history/gitg-history.vala b/gitg/history/gitg-history.vala
index 205f543..a85b6cb 100644
--- a/gitg/history/gitg-history.vala
+++ b/gitg/history/gitg-history.vala
@@ -708,7 +708,7 @@ namespace GitgHistory
private Gtk.Menu? popup_menu_for_ref(Gitg.Ref reference)
{
- var actions = new Gee.LinkedList<GitgExt.RefAction>();
+ var actions = new Gee.LinkedList<GitgExt.RefAction?>();
var af = new ActionInterface(application, d_main.refs_list);
@@ -720,7 +720,23 @@ namespace GitgHistory
add_ref_action(actions, new Gitg.RefActionRename(application, af, reference));
add_ref_action(actions, new Gitg.RefActionDelete(application, af, reference));
add_ref_action(actions, new Gitg.RefActionCopyName(application, af, reference));
- add_ref_action(actions, new Gitg.RefActionFetch(application, af, reference));
+
+ var fetch = new Gitg.RefActionFetch(application, af, reference);
+
+ if (fetch.available)
+ {
+ actions.add(null);
+ }
+
+ add_ref_action(actions, fetch);
+
+ var merge = new Gitg.RefActionMerge(application, af, reference);
+
+ if (merge.available)
+ {
+ actions.add(null);
+ add_ref_action(actions, merge);
+ }
var exts = new Peas.ExtensionSet(Gitg.PluginsEngine.get_default(),
typeof(GitgExt.RefAction),
@@ -731,7 +747,15 @@ namespace GitgHistory
"reference",
reference);
+ var addedsep = false;
+
exts.foreach((extset, info, extension) => {
+ if (!addedsep)
+ {
+ actions.add(null);
+ addedsep = true;
+ }
+
add_ref_action(actions, extension as GitgExt.RefAction);
});
@@ -744,7 +768,16 @@ namespace GitgHistory
foreach (var ac in actions)
{
- ac.populate_menu(menu);
+ if (ac != null)
+ {
+ ac.populate_menu(menu);
+ }
+ else
+ {
+ var sep = new Gtk.SeparatorMenuItem();
+ sep.show();
+ menu.append(sep);
+ }
}
var sep = new Gtk.SeparatorMenuItem();
diff --git a/libgitg/gitg-stage.vala b/libgitg/gitg-stage.vala
index 08b38d2..49955a0 100644
--- a/libgitg/gitg-stage.vala
+++ b/libgitg/gitg-stage.vala
@@ -375,6 +375,23 @@ public class Stage : Object
Ggit.OId[]? parents,
StageCommitOptions options) throws Error
{
+ Ggit.OId? treeoid = null;
+
+ yield Async.thread(() => {
+ treeoid = index.write_tree_to(d_repository);
+ });
+
+ return yield commit_tree(treeoid, reference, message, author, committer, parents, options);
+ }
+
+ public async Ggit.OId? commit_tree(Ggit.OId treeoid,
+ Ggit.Ref reference,
+ string message,
+ Ggit.Signature author,
+ Ggit.Signature committer,
+ Ggit.OId[]? parents,
+ StageCommitOptions options) throws Error
+ {
Ggit.OId? ret = null;
yield Async.thread(() => {
@@ -400,8 +417,6 @@ public class Stage : Object
emsg = commit_msg_hook(emsg, author, committer);
}
- var treeoid = index.write_tree_to(d_repository);
-
Ggit.OId? refoid = null;
try
diff --git a/tests/gitg/Makefile.am b/tests/gitg/Makefile.am
index 46ef18f..4b8b6f4 100644
--- a/tests/gitg/Makefile.am
+++ b/tests/gitg/Makefile.am
@@ -47,11 +47,13 @@ TESTS_GITG_TEST_GITG_COPIED_SOURCES = \
tests/gitg/support-test.vala \
tests/gitg/support-main.vala \
tests/gitg/support-repository.vala \
- tests/gitg/gitg-ref-action-checkout.vala
+ tests/gitg/gitg-ref-action-checkout.vala \
+ tests/gitg/gitg-ref-action-merge.vala
tests_gitg_test_gitg_SOURCES = \
tests/gitg/main.vala \
tests/gitg/test-checkout-ref.vala \
+ tests/gitg/test-merge-ref.vala \
tests/gitg/simple-notification-mock.vala \
tests/gitg/application-mock.vala \
tests/gitg/notifications-mock.vala \
diff --git a/tests/gitg/application-mock.vala b/tests/gitg/application-mock.vala
index a9c53c4..b8265e2 100644
--- a/tests/gitg/application-mock.vala
+++ b/tests/gitg/application-mock.vala
@@ -21,6 +21,13 @@ using Gitg.Test.Assert;
class Gitg.Test.Application : Gitg.Test.Repository, GitgExt.Application
{
+ class ExpectedUserQuery : Object
+ {
+ public GitgExt.UserQuery query;
+ public Gtk.ResponseType response;
+ }
+
+ private Gee.ArrayQueue<ExpectedUserQuery> d_expected_queries;
private Notifications d_notifications;
public Application()
@@ -33,6 +40,7 @@ class Gitg.Test.Application : Gitg.Test.Repository, GitgExt.Application
base.set_up();
d_notifications = new Notifications();
+ d_expected_queries = new Gee.ArrayQueue<ExpectedUserQuery>();
}
public Gitg.Repository? repository
@@ -64,13 +72,47 @@ class Gitg.Test.Application : Gitg.Test.Repository, GitgExt.Application
public GitgExt.Activity? get_activity_by_id(string id) { return null; }
public GitgExt.Activity? set_activity_by_id(string id) { return null; }
+ protected Application expect_user_query(GitgExt.UserQuery query, Gtk.ResponseType response)
+ {
+ d_expected_queries.add(new ExpectedUserQuery() {
+ query = query,
+ response = response
+ });
+
+ return this;
+ }
+
+ private Gtk.ResponseType user_query_respond(GitgExt.UserQuery query)
+ {
+ assert_true(d_expected_queries.size > 0);
+
+ var expected = d_expected_queries.poll();
+
+ assert_streq(expected.query.title, query.title);
+ assert_streq(expected.query.message, query.message);
+ assert_inteq(expected.query.message_type, query.message_type);
+ assert_inteq(expected.query.default_response, query.default_response);
+ assert_booleq(expected.query.default_is_destructive, query.default_is_destructive);
+ assert_booleq(expected.query.message_use_markup, query.message_use_markup);
+ assert_inteq(expected.query.responses.length, query.responses.length);
+
+ for (var i = 0; i < expected.query.responses.length; i++)
+ {
+ assert_inteq(expected.query.responses[i].response_type,
query.responses[i].response_type);
+ assert_streq(expected.query.responses[i].text, query.responses[i].text);
+ }
+
+ return expected.response;
+ }
+
public void user_query(GitgExt.UserQuery query)
{
+ query.response(user_query_respond(query));
}
public async Gtk.ResponseType user_query_async(GitgExt.UserQuery query)
{
- return Gtk.ResponseType.CLOSE;
+ return user_query_respond(query);
}
public void show_infobar(string primary_msg,
diff --git a/tests/gitg/main.vala b/tests/gitg/main.vala
index 9673d50..77a3498 100644
--- a/tests/gitg/main.vala
+++ b/tests/gitg/main.vala
@@ -23,7 +23,8 @@ class Gitg.Test.Runner
{
var m = new Gitg.Test.Main(args);
- m.add(new CheckoutRef());
+ m.add(new CheckoutRef(),
+ new MergeRef());
m.run();
}
diff --git a/tests/gitg/simple-notification-mock.vala b/tests/gitg/simple-notification-mock.vala
index 8416451..f516fcc 100644
--- a/tests/gitg/simple-notification-mock.vala
+++ b/tests/gitg/simple-notification-mock.vala
@@ -30,7 +30,7 @@ public class SimpleNotification : Object, GitgExt.Notification
}
public signal void cancel();
- public Status status;
+ public Status status { get; set; }
public string title { get; set; }
public string message { get; set; }
diff --git a/tests/gitg/test-merge-ref.vala b/tests/gitg/test-merge-ref.vala
new file mode 100644
index 0000000..006c99d
--- /dev/null
+++ b/tests/gitg/test-merge-ref.vala
@@ -0,0 +1,482 @@
+/*
+ * 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.MergeRef : Application
+{
+ private Gitg.Branch ours;
+ private Gitg.Branch theirs;
+ private Gitg.Branch master;
+ private Gitg.Branch not_master;
+
+ private RefActionInterface action_interface;
+ private RefActionCheckout action;
+
+ 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");
+
+ checkout_branch("master");
+ not_master = create_branch("not_master");
+
+ master = lookup_branch("master");
+
+ action_interface = new RefActionInterface(this);
+ }
+
+ private void assert_merged(Ggit.OId ours, Ggit.OId theirs, string name)
+ {
+ var now_commit = lookup_commit(name);
+
+ var parents = now_commit.get_parents();
+ assert_uinteq(parents.size, 2);
+
+ assert_streq(parents[0].get_id().to_string(), ours.to_string());
+ assert_streq(parents[1].get_id().to_string(), theirs.to_string());
+ }
+
+ protected virtual signal void test_merge_simple()
+ {
+ var loop = new MainLoop();
+ var action = new Gitg.RefActionMerge(this, action_interface, master);
+
+ var ours_oid = lookup_commit("master").get_id();
+ var theirs_oid = theirs.get_target();
+
+ action.merge.begin(theirs, (obj, res) => {
+ action.merge.end(res);
+ loop.quit();
+ });
+
+ loop.run();
+
+ assert_inteq(simple_notifications.size, 1);
+ assert_streq(simple_notifications[0].title, "Merge 'theirs' into 'master'");
+ assert_streq(simple_notifications[0].message, "Successfully merged 'theirs' into 'master'");
+ 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");
+
+ assert_merged(ours_oid, theirs_oid, "master");
+ }
+
+ protected virtual signal void test_merge_not_head()
+ {
+ var loop = new MainLoop();
+ var action = new Gitg.RefActionMerge(this, action_interface, not_master);
+
+ var ours_oid = not_master.get_target();
+ var theirs_oid = theirs.get_target();
+
+ action.merge.begin(theirs, (obj, res) => {
+ action.merge.end(res);
+ loop.quit();
+ });
+
+ loop.run();
+
+ assert_inteq(simple_notifications.size, 1);
+ assert_streq(simple_notifications[0].title, "Merge 'theirs' into 'not_master'");
+ assert_streq(simple_notifications[0].message, "Successfully merged 'theirs' into
'not_master'");
+ 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"));
+
+ assert_merged(ours_oid, theirs_oid, "not_master");
+ }
+
+ protected virtual signal void test_merge_not_head_would_have_conflicted()
+ {
+ var loop = new MainLoop();
+
+ commit("c", "c file other content\n");
+
+ var ours_oid = not_master.get_target();
+ var theirs_oid = theirs.get_target();
+
+ var action = new Gitg.RefActionMerge(this, action_interface, not_master);
+
+ action.merge.begin(theirs, (obj, res) => {
+ action.merge.end(res);
+ loop.quit();
+ });
+
+ loop.run();
+
+ assert_inteq(simple_notifications.size, 1);
+ assert_streq(simple_notifications[0].title, "Merge 'theirs' into 'not_master'");
+ assert_streq(simple_notifications[0].message, "Successfully merged 'theirs' into
'not_master'");
+ 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");
+
+ assert_merged(ours_oid, theirs_oid, "not_master");
+ }
+
+ protected virtual signal void test_merge_theirs_conflicts_no_checkout()
+ {
+ var loop = new MainLoop();
+
+ commit("c", "c file other content\n");
+
+ var ours_oid = lookup_commit("master").get_id();
+ var theirs_oid = theirs.get_target();
+
+ var action = new Gitg.RefActionMerge(this, action_interface, master);
+
+ expect_user_query(new GitgExt.UserQuery.full("Merge has conflicts",
+ "The merge of 'theirs' into 'master' has caused
conflicts, would you like to checkout branch 'master' with the merge to your working directory to resolve the
conflicts?",
+ Gtk.MessageType.QUESTION,
+ "Cancel",
+ Gtk.ResponseType.CANCEL,
+ "Checkout",
+ Gtk.ResponseType.OK),
+ Gtk.ResponseType.CANCEL);
+
+ action.merge.begin(theirs, (obj, res) => {
+ action.merge.end(res);
+ loop.quit();
+ });
+
+ loop.run();
+
+ assert_inteq(simple_notifications.size, 1);
+ assert_streq(simple_notifications[0].title, "Merge 'theirs' into 'master'");
+ assert_streq(simple_notifications[0].message, "Merge 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");
+ }
+
+ protected virtual signal void test_merge_theirs_conflicts_checkout()
+ {
+ var loop = new MainLoop();
+
+ commit("c", "c file other content\n");
+
+ var ours_oid = lookup_commit("master").get_id();
+ var theirs_oid = theirs.get_target();
+
+ var action = new Gitg.RefActionMerge(this, action_interface, master);
+
+ expect_user_query(new GitgExt.UserQuery.full("Merge has conflicts",
+ "The merge of 'theirs' into 'master' has caused
conflicts, would you like to checkout branch 'master' with the merge to your working directory to resolve the
conflicts?",
+ Gtk.MessageType.QUESTION,
+ "Cancel",
+ Gtk.ResponseType.CANCEL,
+ "Checkout",
+ Gtk.ResponseType.OK),
+ Gtk.ResponseType.OK);
+
+ action.merge.begin(theirs, (obj, res) => {
+ action.merge.end(res);
+ loop.quit();
+ });
+
+ loop.run();
+
+ assert_inteq(simple_notifications.size, 1);
+ assert_streq(simple_notifications[0].title, "Merge 'theirs' into 'master'");
+ assert_streq(simple_notifications[0].message, "Finished merge 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/ORIG_HEAD", "e1219dd5fbcf8fb5b17bbd3db7a9fa88e98d6651\n");
+ assert_file_contents(".git/MERGE_HEAD", "72af7ccf47852d832b06c7244de8ae9ded639024\n");
+ assert_file_contents(".git/MERGE_MODE", "no-ff\n");
+ assert_file_contents(".git/MERGE_MSG", "Merge branch 'theirs'\n\nConflicts:\n\tc\n");
+ }
+
+ protected virtual signal void test_merge_theirs_dirty_stash()
+ {
+ var loop = new MainLoop();
+
+ write_file("b", "b file other content\n");
+
+ var ours_oid = lookup_commit("master").get_id();
+ var theirs_oid = theirs.get_target();
+
+ var action = new Gitg.RefActionMerge(this, action_interface, master);
+
+ 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.merge.begin(theirs, (obj, res) => {
+ action.merge.end(res);
+ loop.quit();
+ });
+
+ loop.run();
+
+ assert_inteq(simple_notifications.size, 1);
+ assert_streq(simple_notifications[0].title, "Merge 'theirs' into 'master'");
+ assert_streq(simple_notifications[0].message, "Successfully merged 'theirs' into 'master'");
+ 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");
+ }
+
+ protected virtual signal void test_merge_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 ours_oid = not_master.get_target();
+ var theirs_oid = theirs.get_target();
+
+ var action = new Gitg.RefActionMerge(this, action_interface, not_master);
+
+ expect_user_query(new GitgExt.UserQuery.full("Merge has conflicts",
+ "The merge of 'theirs' into 'not_master' has
caused conflicts, would you like to checkout the merge 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.merge.begin(theirs, (obj, res) => {
+ action.merge.end(res);
+ loop.quit();
+ });
+
+ loop.run();
+
+ assert_inteq(simple_notifications.size, 2);
+
+ assert_streq(simple_notifications[0].title, "Merge 'theirs' into 'not_master'");
+ assert_streq(simple_notifications[0].message, "Finished merge 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/ORIG_HEAD", "e1219dd5fbcf8fb5b17bbd3db7a9fa88e98d6651\n");
+ assert_file_contents(".git/MERGE_HEAD", "72af7ccf47852d832b06c7244de8ae9ded639024\n");
+ assert_file_contents(".git/MERGE_MODE", "no-ff\n");
+ assert_file_contents(".git/MERGE_MSG", "Merge branch 'theirs'\n\nConflicts:\n\tc\n");
+ }
+
+ protected virtual signal void test_merge_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 ours_oid = not_master.get_target();
+ var theirs_oid = theirs.get_target();
+
+ var action = new Gitg.RefActionMerge(this, action_interface, not_master);
+
+ expect_user_query(new GitgExt.UserQuery.full("Merge has conflicts",
+ "The merge of 'theirs' into 'not_master' has
caused conflicts, would you like to checkout the merge 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.merge.begin(theirs, (obj, res) => {
+ action.merge.end(res);
+ loop.quit();
+ });
+
+ loop.run();
+
+ assert_inteq(simple_notifications.size, 2);
+
+ assert_streq(simple_notifications[0].title, "Merge 'theirs' into 'not_master'");
+ assert_streq(simple_notifications[0].message, "Finished merge 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/ORIG_HEAD", "e1219dd5fbcf8fb5b17bbd3db7a9fa88e98d6651\n");
+ assert_file_contents(".git/MERGE_HEAD", "72af7ccf47852d832b06c7244de8ae9ded639024\n");
+ assert_file_contents(".git/MERGE_MODE", "no-ff\n");
+ assert_file_contents(".git/MERGE_MSG", "Merge branch 'theirs'\n\nConflicts:\n\tc\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_merge_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 ours_oid = not_master.get_target();
+ var theirs_oid = theirs.get_target();
+
+ var action = new Gitg.RefActionMerge(this, action_interface, not_master);
+
+ expect_user_query(new GitgExt.UserQuery.full("Merge has conflicts",
+ "The merge of 'theirs' into 'not_master' has
caused conflicts, would you like to checkout the merge 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.merge.begin(theirs, (obj, res) => {
+ action.merge.end(res);
+ loop.quit();
+ });
+
+ loop.run();
+
+ 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_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/support/gitg-assert.h b/tests/support/gitg-assert.h
index 561e70c..37ff302 100644
--- a/tests/support/gitg-assert.h
+++ b/tests/support/gitg-assert.h
@@ -23,14 +23,15 @@
#include <glib.h>
#define gitg_test_assert_assert_no_error(error) g_assert_no_error(error)
-#define gitg_test_assert_assert_streq(a, b) g_assert_cmpstr(a, ==, b)
-#define gitg_test_assert_assert_inteq(a, b) g_assert_cmpint(a, ==, b)
-#define gitg_test_assert_assert_uinteq(a, b) g_assert_cmpuint(a, ==, b)
-#define gitg_test_assert_assert_floateq(a, b) g_assert_cmpfloat(a, ==, b)
+#define gitg_test_assert_assert_streq(a, b) g_assert_cmpstr((a), ==, (b))
+#define gitg_test_assert_assert_inteq(a, b) g_assert_cmpint((a), ==, (b))
+#define gitg_test_assert_assert_booleq(a, b) g_assert_cmpuint((guint)(a), ==, (guint)(b))
+#define gitg_test_assert_assert_uinteq(a, b) g_assert_cmpuint((a), ==, (b))
+#define gitg_test_assert_assert_floateq(a, b) g_assert_cmpfloat((a), ==, (b))
#define gitg_test_assert_assert_datetime(a, b) \
- g_assert_cmpstr (g_date_time_format (a, "%F %T %z"), \
+ g_assert_cmpstr (g_date_time_format ((a), "%F %T %z"), \
==, \
- g_date_time_format (b, "%F %T %z") \
+ g_date_time_format ((b), "%F %T %z") \
)
#endif /* __GITG_ASSERT_H__ */
diff --git a/tests/support/gitg-assert.vapi b/tests/support/gitg-assert.vapi
index bca04c4..e6bfebe 100644
--- a/tests/support/gitg-assert.vapi
+++ b/tests/support/gitg-assert.vapi
@@ -4,6 +4,7 @@ namespace Gitg.Test.Assert
public static void assert_no_error(GLib.Error e);
public static void assert_streq(string a, string b);
public static void assert_inteq(int a, int b);
+ public static void assert_booleq(bool a, bool b);
public static void assert_uinteq(uint a, uint b);
public static void assert_floateq(float a, float b);
public static void assert_datetime(GLib.DateTime a, GLib.DateTime b);
diff --git a/tests/support/repository.vala b/tests/support/repository.vala
index 3271cec..f4bcee3 100644
--- a/tests/support/repository.vala
+++ b/tests/support/repository.vala
@@ -397,10 +397,8 @@ class Gitg.Test.Repository : Gitg.Test.Test
remove_recursively(c);
}
}
- else
- {
- f.delete();
- }
+
+ f.delete();
}
catch (Error e)
{
@@ -408,6 +406,40 @@ class Gitg.Test.Repository : Gitg.Test.Test
}
}
+ protected Gitg.Branch? lookup_branch(string name)
+ {
+ try
+ {
+ var ret = d_repository.lookup_reference_dwim(name) as Gitg.Branch;
+ assert_nonnull(ret);
+
+ return ret;
+ }
+ catch (Error e)
+ {
+ assert_no_error(e);
+ }
+
+ return null;
+ }
+
+ protected Gitg.Commit? lookup_commit(string name)
+ {
+ try
+ {
+ var ret = lookup_branch(name).lookup() as Gitg.Commit;
+ assert_nonnull(ret);
+
+ return ret;
+ }
+ catch (Error e)
+ {
+ assert_no_error(e);
+ }
+
+ return null;
+ }
+
protected override void tear_down()
{
if (d_repository == null)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]