[gitg/wip/fetch: 6/9] Add remote management



commit 1a8960b3fca5b0cebf0d69fa9044ef52726eb87b
Author: Jesse van den Kieboom <jessevdk gmail com>
Date:   Tue Dec 23 17:39:18 2014 +0100

    Add remote management

 gitg/Makefile.am                        |    1 +
 gitg/gitg-remote-manager.vala           |  214 +++++++++++++++++++++++++++++++
 gitg/gitg-window.vala                   |   11 ++
 libgitg-ext/Makefile.am                 |    1 +
 libgitg-ext/gitg-ext-application.vala   |    2 +
 libgitg-ext/gitg-ext-remote-lookup.vala |   30 +++++
 libgitg/Makefile.am                     |    3 +-
 libgitg/gitg-init.vala                  |    3 +
 libgitg/gitg-remote.vala                |  183 ++++++++++++++++++++++++++
 9 files changed, 447 insertions(+), 1 deletions(-)
---
diff --git a/gitg/Makefile.am b/gitg/Makefile.am
index 7654b40..5ec9391 100644
--- a/gitg/Makefile.am
+++ b/gitg/Makefile.am
@@ -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        \
diff --git a/gitg/gitg-remote-manager.vala b/gitg/gitg-remote-manager.vala
new file mode 100644
index 0000000..ed67160
--- /dev/null
+++ b/gitg/gitg-remote-manager.vala
@@ -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;
+       }
+}
+
+}
diff --git a/gitg/gitg-window.vala b/gitg/gitg-window.vala
index b6f92b1..a2e6633 100644
--- a/gitg/gitg-window.vala
+++ b/gitg/gitg-window.vala
@@ -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; }
+       }
 }
 
 }
diff --git a/libgitg-ext/Makefile.am b/libgitg-ext/Makefile.am
index babe367..5833173 100644
--- a/libgitg-ext/Makefile.am
+++ b/libgitg-ext/Makefile.am
@@ -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                    \
diff --git a/libgitg-ext/gitg-ext-application.vala b/libgitg-ext/gitg-ext-application.vala
index 379647f..c5350cd 100644
--- a/libgitg-ext/gitg-ext-application.vala
+++ b/libgitg-ext/gitg-ext-application.vala
@@ -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; }
 }
 
 }
diff --git a/libgitg-ext/gitg-ext-remote-lookup.vala b/libgitg-ext/gitg-ext-remote-lookup.vala
new file mode 100644
index 0000000..58b392f
--- /dev/null
+++ b/libgitg-ext/gitg-ext-remote-lookup.vala
@@ -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:
diff --git a/libgitg/Makefile.am b/libgitg/Makefile.am
index 5eb7b91..26c58eb 100644
--- a/libgitg/Makefile.am
+++ b/libgitg/Makefile.am
@@ -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)   \
diff --git a/libgitg/gitg-init.vala b/libgitg/gitg-init.vala
index ad3d2d3..63d8df5 100644
--- a/libgitg/gitg-init.vala
+++ b/libgitg/gitg-init.vala
@@ -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));
 }
 
 }
diff --git a/libgitg/gitg-remote.vala b/libgitg/gitg-remote.vala
new file mode 100644
index 0000000..df7763c
--- /dev/null
+++ b/libgitg/gitg-remote.vala
@@ -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);
+       }
+}
+
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]