Add remote management

This commit is contained in:
Jesse van den Kieboom 2014-12-23 17:39:18 +01:00
parent 0bdd143e50
commit 1a8960b3fc
9 changed files with 447 additions and 1 deletions

View File

@ -69,6 +69,7 @@ gitg_gitg_VALASOURCES = \
gitg/gitg-commit-action-create-tag.vala \
gitg/gitg-create-tag-dialog.vala \
gitg/gitg-commit-action-create-patch.vala \
gitg/gitg-remote-manager.vala \
gitg/preferences/gitg-preferences-commit.vala \
gitg/preferences/gitg-preferences-dialog.vala \
gitg/preferences/gitg-preferences-interface.vala \

View File

@ -0,0 +1,214 @@
/*
* This file is part of gitg
*
* Copyright (C) 2012 - 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 RemoteManager : Object, GitgExt.RemoteLookup
{
class CredSshInteractive : Ggit.CredSshInteractive
{
public CredSshInteractive(string username) throws Error
{
Object(username: username);
((Initable)this).init(null);
}
protected override void prompt(Ggit.CredSshInteractivePrompt[] prompts)
{
// TODO
}
}
class Callbacks : Ggit.RemoteCallbacks
{
private weak Remote d_remote;
private Window d_window;
public Callbacks(Remote remote, Window window)
{
d_remote = remote;
d_window = window;
}
protected override bool credentials(string url,
string username,
Ggit.Credtype allowed_types,
out Ggit.Cred cred) throws Error
{
cred = null;
if ((allowed_types & Ggit.Credtype.SSH_KEY) != 0)
{
cred = new Ggit.CredSshKeyFromAgent(username);
}
else if ((allowed_types & Ggit.Credtype.SSH_INTERACTIVE) != 0)
{
cred = new CredSshInteractive(username);
}
else if ((allowed_types & Ggit.Credtype.USERPASS_PLAINTEXT) != 0)
{
// TODO: query for user + pass
}
return cred != null;
}
}
struct InsteadOf
{
string prefix;
string replacement;
}
private Gee.HashMap<string, Gitg.Remote> d_remotes;
private InsteadOf[] d_insteadof;
private Window d_window;
public RemoteManager(Window window)
{
d_window = window;
d_remotes = new Gee.HashMap<string, Gitg.Remote>();
extract_insteadof();
}
private void extract_insteadof()
{
d_insteadof = new InsteadOf[10];
d_insteadof.length = 0;
Ggit.Config config;
try
{
config = d_window.repository.get_config();
} catch { return; }
Regex r;
try
{
r = new Regex("url\\.(.*)\\.insteadof");
}
catch (Error e)
{
stderr.printf("Failed to compile regex: %s\n", e.message);
return;
}
try
{
config.match_foreach(r, (info, value) => {
d_insteadof += InsteadOf() {
prefix = value,
replacement = info.fetch(1)
};
return 0;
});
} catch {}
}
public Gitg.Remote? lookup(string name)
{
if (d_window.repository == null)
{
return null;
}
if (d_remotes == null)
{
d_remotes = new Gee.HashMap<string, Gitg.Remote>();
}
if (d_remotes.has_key(name))
{
return d_remotes[name];
}
Gitg.Remote remote;
try
{
remote = d_window.repository.get_remote(name) as Gitg.Remote;
} catch { return null; }
var url = remote.get_url();
foreach (var io in d_insteadof)
{
if (url.has_prefix(io.prefix))
{
url = io.replacement + url.substring(io.prefix.length);
string[] fetch_specs;
string[] push_specs;
try
{
fetch_specs = remote.get_fetch_specs();
} catch { break; }
try
{
push_specs = remote.get_push_specs();
} catch { break; }
var defspec = "+refs/heads/*:refs/remotes/" + name + "/*";
Gitg.Remote? tmp = null;
try
{
tmp = (new Ggit.Remote.anonymous(d_window.repository, url, defspec)) as Gitg.Remote;
}
catch (Error e)
{
stderr.printf("Failed to create remote: %s\n", e.message);
}
if (tmp == null)
{
break;
}
try
{
tmp.set_fetch_specs(fetch_specs);
} catch { break; }
try
{
tmp.set_push_specs(push_specs);
} catch { break; }
remote = tmp;
break;
}
}
remote.set_callbacks(new Callbacks(remote, d_window));
d_remotes[name] = remote;
return remote;
}
}
}

View File

@ -38,6 +38,8 @@ public class Window : Gtk.ApplicationWindow, GitgExt.Application, Initable
private UIElements<GitgExt.Activity> d_activities;
private RemoteManager d_remote_manager;
// Widgets
[GtkChild]
private Gtk.HeaderBar d_header_bar;
@ -333,6 +335,7 @@ public class Window : Gtk.ApplicationWindow, GitgExt.Application, Initable
set
{
d_repository = value;
d_remote_manager = new RemoteManager(this);
notify_property("repository");
repository_changed();
@ -487,6 +490,8 @@ public class Window : Gtk.ApplicationWindow, GitgExt.Application, Initable
d_repository = new Gitg.Repository(this.repository.get_location(),
null);
d_remote_manager = new RemoteManager(this);
notify_property("repository");
update_title();
}
@ -698,6 +703,7 @@ public class Window : Gtk.ApplicationWindow, GitgExt.Application, Initable
{
ret.application = app;
ret.d_repository = repository;
ret.d_remote_manager = new RemoteManager(ret);
ret.d_action = action;
}
@ -935,6 +941,11 @@ public class Window : Gtk.ApplicationWindow, GitgExt.Application, Initable
selectable_mode = GitgExt.SelectionMode.NORMAL;
}
}
public GitgExt.RemoteLookup remote_lookup
{
owned get { return d_remote_manager; }
}
}
}

View File

@ -57,6 +57,7 @@ libgitg_ext_libgitg_ext_1_0_la_VALASOURCES = \
libgitg-ext/gitg-ext-history-panel.vala \
libgitg-ext/gitg-ext-command-line.vala \
libgitg-ext/gitg-ext-preferences.vala \
libgitg-ext/gitg-ext-remote-lookup.vala \
libgitg-ext/gitg-ext-searchable.vala \
libgitg-ext/gitg-ext-selectable.vala \
libgitg-ext/gitg-ext-ui.vala \

View File

@ -66,6 +66,8 @@ public interface Application : Object
public abstract Gee.Map<string, string> environment { owned get; }
public abstract Application open_new(Ggit.Repository repository, string? hint = null);
public abstract RemoteLookup remote_lookup { owned get; }
}
}

View File

@ -0,0 +1,30 @@
/*
* This file is part of gitg
*
* Copyright (C) 2014 - 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 GitgExt
{
public interface RemoteLookup : Object
{
public abstract Gitg.Remote? lookup(string name);
}
}
// ex:set ts=4 noet:

View File

@ -72,7 +72,8 @@ libgitg_libgitg_1_0_la_VALASOURCES = \
libgitg/gitg-hook.vala \
libgitg/gitg-date.vala \
libgitg/gitg-avatar-cache.vala \
libgitg/gitg-diff-stat.vala
libgitg/gitg-diff-stat.vala \
libgitg/gitg-remote.vala
libgitg_libgitg_1_0_la_SOURCES = \
$(libgitg_libgitg_1_0_la_VALASOURCES) \

View File

@ -65,6 +65,9 @@ public void init() throws Error
factory.register(typeof(Ggit.Commit),
typeof(Gitg.Commit));
factory.register(typeof(Ggit.Remote),
typeof(Gitg.Remote));
}
}

183
libgitg/gitg-remote.vala Normal file
View File

@ -0,0 +1,183 @@
/*
* This file is part of gitg
*
* Copyright (C) 2014 - 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
{
public enum RemoteState
{
DISCONNECTED,
CONNECTING,
CONNECTED,
TRANSFERRING
}
public errordomain RemoteError
{
ALREADY_CONNECTED,
ALREADY_CONNECTING,
ALREADY_DISCONNECTED,
STILL_CONNECTING
}
public class Remote : Ggit.Remote
{
private RemoteState d_state;
public RemoteState state
{
get { return d_state; }
private set
{
if (d_state != value)
{
d_state = value;
notify_property("state");
}
}
}
private void update_state(bool force_disconnect = false)
{
if (get_connected())
{
if (force_disconnect)
{
disconnect.begin((obj, res) => {
try
{
disconnect.end(res);
} catch {}
});
}
else
{
state = RemoteState.CONNECTED;
}
}
else
{
state = RemoteState.DISCONNECTED;
}
}
public new async void connect(Ggit.Direction direction) throws Error
{
if (get_connected())
{
if (state != RemoteState.CONNECTED)
{
state = RemoteState.CONNECTED;
}
throw new RemoteError.ALREADY_CONNECTED("already connected");
}
else if (state == RemoteState.CONNECTING)
{
throw new RemoteError.ALREADY_CONNECTING("already connecting");
}
state = RemoteState.CONNECTING;
try
{
yield Async.thread(() => {
base.connect(direction);
});
}
catch (Error e)
{
update_state();
throw e;
}
update_state();
}
public new async void disconnect() throws Error
{
if (!get_connected())
{
if (state != RemoteState.DISCONNECTED)
{
state = RemoteState.DISCONNECTED;
}
throw new RemoteError.ALREADY_DISCONNECTED("already disconnected");
}
try
{
yield Async.thread(() => {
base.disconnect();
});
}
catch (Error e)
{
update_state();
throw e;
}
update_state();
}
private async void download_intern(Ggit.Signature? signature, string? message) throws Error
{
bool dis = false;
if (!get_connected())
{
dis = true;
yield connect(Ggit.Direction.FETCH);
}
state = RemoteState.TRANSFERRING;
try
{
yield Async.thread(() => {
base.download();
if (signature != null)
{
base.update_tips(signature, message);
}
});
}
catch (Error e)
{
update_state(dis);
throw e;
}
update_state(dis);
}
public new async void download() throws Error
{
yield download_intern(null, null);
}
public new async void fetch(Ggit.Signature signature, string? message) throws Error
{
yield download_intern(signature, message);
}
}
}