-
-
Notifications
You must be signed in to change notification settings - Fork 114
Clone remote repository. #1548
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Clone remote repository. #1548
Changes from 12 commits
e1d2e5c
ebb8ed1
5200d9d
01d124f
e39eac8
7e3da04
3b4ec49
328c75b
a267a98
7881aee
85b2bb3
2251850
15c6fc5
ea1a356
576ca43
9c38d00
5287f59
9f8bf46
497bac7
b4c79ed
55490d2
7a4bf5d
a59c64b
6138cfa
0582c77
50293b5
ec4b2d0
4dd6f6d
ffbb883
636d871
7cadd28
2c315a2
1bfa1aa
d428416
3273fe5
67be8d9
273113c
731a05a
e3a22b9
25bbd0e
4d66c36
379ef30
c2fe76f
22e57b7
7d60208
2595107
1454a5b
1b3232d
0ae3fb9
e949241
5536cc4
784460d
6915fce
01346c9
4119847
db0e6f5
af36e42
e463c9f
9d95150
d049990
1a09d91
fc536e0
49e95a9
e4cf12b
4469d6c
1afe983
6779e66
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,176 @@ | ||
| /* | ||
| * Copyright 2025 elementary, Inc. (https://elementary.io) | ||
| * | ||
| * This program 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. | ||
| * | ||
| * This program 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 this program; if not, write to the | ||
| * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | ||
| * Boston, MA 02110-1301 USA. | ||
| * | ||
| * Authored by: Jeremy Wootten <jeremywootten@gmail.com> | ||
| */ | ||
|
|
||
| public class Scratch.Dialogs.CloneRepositoryDialog : Granite.MessageDialog { | ||
| public FolderManager.ProjectFolderItem? active_project { get; construct; } | ||
| public bool can_clone { get; private set; default = false; } | ||
|
|
||
|
|
||
| //Taken from "switchboard-plug-parental-controls/src/plug/Views/InternetView.vala" | ||
| private const string NAME_REGEX = "^[0-9a-zA-Z_-]+$"; | ||
| private const string LOCAL_FOLDER_REGEX ="""^(/[^/ ]*)+/?$"""; | ||
| private const string URL_REGEX = "([^/w.])[-a-zA-Z0-9@:%._\\+~#=]{2,256}\\.[a-z]{1,3}([^/])\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*\\b)"; | ||
| private Regex name_regex; | ||
| private Regex local_folder_regex; | ||
danirabbit marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| private Regex url_regex; | ||
| private Granite.ValidatedEntry repository_host_uri_entry; | ||
| private Granite.ValidatedEntry repository_user_entry; | ||
| private Granite.ValidatedEntry repository_name_entry; | ||
| private Granite.ValidatedEntry repository_local_folder_entry; | ||
| private Granite.ValidatedEntry repository_local_name_entry; | ||
| private Gtk.CheckButton set_as_active_check; | ||
|
|
||
| public CloneRepositoryDialog (string local_folder) { | ||
| Object ( | ||
| transient_for: ((Gtk.Application)(GLib.Application.get_default ())).get_active_window (), | ||
| image_icon: new ThemedIcon ("git") | ||
| ); | ||
|
|
||
| repository_local_folder_entry.text = local_folder; | ||
| repository_host_uri_entry.text = "https://github.com"; | ||
| repository_user_entry.text = "elementary"; | ||
danirabbit marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| repository_name_entry.text = ""; | ||
| repository_local_name_entry.text = ""; | ||
danirabbit marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| on_is_valid_changed (); | ||
danirabbit marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| construct { | ||
| try { | ||
| name_regex = new Regex (NAME_REGEX, RegexCompileFlags.OPTIMIZE); | ||
| local_folder_regex = new Regex (LOCAL_FOLDER_REGEX, RegexCompileFlags.OPTIMIZE); | ||
| url_regex = new Regex (URL_REGEX, RegexCompileFlags.OPTIMIZE); | ||
danirabbit marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } catch (RegexError e) { | ||
| warning ("%s\n", e.message); | ||
| } | ||
|
|
||
| add_button (_("Cancel"), Gtk.ResponseType.CANCEL); | ||
| primary_text = _("Create a local clone of a Git repository"); | ||
| ///TRANSLATORS "Git" is a proper name and must not be translated | ||
danirabbit marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| secondary_text = _("The source repository and local folder must exist and be accessible"); | ||
danirabbit marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| badge_icon = new ThemedIcon ("download"); | ||
danirabbit marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| repository_host_uri_entry = new Granite.ValidatedEntry.from_regex (url_regex) { | ||
jeremypw marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| activates_default = false | ||
| }; | ||
| repository_user_entry = new Granite.ValidatedEntry.from_regex (name_regex) { | ||
danirabbit marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| activates_default = false | ||
|
||
| }; | ||
| repository_name_entry = new Granite.ValidatedEntry.from_regex (name_regex) { | ||
| activates_default = false | ||
| }; | ||
| repository_local_folder_entry = new Granite.ValidatedEntry.from_regex (local_folder_regex) { | ||
danirabbit marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| activates_default = false, | ||
| width_chars = 50 | ||
danirabbit marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| }; | ||
| repository_local_name_entry = new Granite.ValidatedEntry.from_regex (name_regex) { | ||
|
||
| activates_default = false, | ||
| }; | ||
|
|
||
| set_as_active_check = new Gtk.CheckButton.with_label (_("Set as Active Project")) { | ||
|
||
| margin_top = 12, | ||
| active = true | ||
| }; | ||
|
|
||
| var content_box = new Gtk.Box (VERTICAL, 12); | ||
| content_box.add (new Granite.HeaderLabel (_("Source Repository"))); | ||
| content_box.add (new CloneEntry (_("Host URI"), repository_host_uri_entry)); | ||
danirabbit marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| content_box.add (new CloneEntry (_("User"), repository_user_entry)); | ||
| content_box.add (new CloneEntry (_("Name"), repository_name_entry)); | ||
danirabbit marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| content_box.add (new Granite.HeaderLabel (_("Clone"))); | ||
| content_box.add (new CloneEntry (_("Target Folder"), repository_local_folder_entry)); | ||
| content_box.add (new CloneEntry (_("Target Name"), repository_local_name_entry)); | ||
| content_box.add (set_as_active_check); | ||
|
|
||
| custom_bin.add (content_box); | ||
|
|
||
| var clone_button = (Gtk.Button) add_button (_("Clone Repository"), Gtk.ResponseType.APPLY); | ||
| clone_button.can_default = true; | ||
| clone_button.has_default = true; | ||
| clone_button.get_style_context ().add_class (Gtk.STYLE_CLASS_SUGGESTED_ACTION); | ||
|
|
||
| repository_host_uri_entry.notify["is-valid"].connect (on_is_valid_changed); | ||
| repository_user_entry.notify["is-valid"].connect (on_is_valid_changed); | ||
| repository_name_entry.notify["is-valid"].connect (on_is_valid_changed); | ||
| repository_local_folder_entry.notify["is-valid"].connect (on_is_valid_changed); | ||
|
|
||
| clone_button.sensitive = can_clone; | ||
| bind_property ("can-clone", clone_button, "sensitive"); | ||
|
||
|
|
||
| show_all (); | ||
danirabbit marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| public string get_source_repository_uri () requires (can_clone) { | ||
| //TODO Further validation here? | ||
| var repo_uri = Path.build_path ( | ||
| Path.DIR_SEPARATOR_S, | ||
| repository_host_uri_entry.text, | ||
| repository_user_entry.text, | ||
| repository_name_entry.text | ||
| ); | ||
|
|
||
| if (!repo_uri.has_suffix (".git")) { | ||
| repo_uri += ".git"; | ||
| } | ||
|
|
||
| return repo_uri; | ||
| } | ||
|
|
||
| public string get_local_folder () requires (can_clone) { | ||
| //TODO Further validation here? | ||
| return repository_local_folder_entry.text; | ||
| } | ||
|
|
||
| public string get_local_name () requires (can_clone) { | ||
| var local_name = repository_local_name_entry.text; | ||
| if (local_name == "") { | ||
| local_name = repository_name_entry.text; | ||
| } | ||
| //TODO Further validation here? | ||
| return local_name; | ||
| } | ||
|
|
||
| private void on_is_valid_changed () { | ||
| can_clone = repository_host_uri_entry.is_valid && | ||
| repository_user_entry.is_valid && | ||
| repository_name_entry.is_valid && | ||
| repository_local_folder_entry.is_valid; | ||
| } | ||
|
|
||
| private class CloneEntry : Gtk.Box { | ||
| public CloneEntry (string label_text, Gtk.Widget entry) { | ||
| var label = new Gtk.Label (label_text) { | ||
danirabbit marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| halign = START | ||
| }; | ||
| add (label); | ||
| add (entry); | ||
| } | ||
|
|
||
| construct { | ||
| orientation = VERTICAL; | ||
| spacing = 6; | ||
| hexpand = false; | ||
| margin_start = 12; | ||
| margin_end = 12; | ||
| margin_bottom = 12; | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -70,6 +70,7 @@ namespace Scratch { | |
| public const string ACTION_GROUP = "win"; | ||
| public const string ACTION_PREFIX = ACTION_GROUP + "."; | ||
| public const string ACTION_FIND = "action-find"; | ||
| public const string ACTION_CLONE_REPO = "action-clone-repo"; | ||
| public const string ACTION_FIND_NEXT = "action-find-next"; | ||
| public const string ACTION_FIND_PREVIOUS = "action-find-previous"; | ||
| public const string ACTION_FIND_GLOBAL = "action-find-global"; | ||
|
|
@@ -130,6 +131,7 @@ namespace Scratch { | |
| private Services.GitManager git_manager; | ||
|
|
||
| private const ActionEntry[] ACTION_ENTRIES = { | ||
| { ACTION_CLONE_REPO, action_clone_repo }, | ||
| { ACTION_FIND, action_find, "s"}, | ||
| { ACTION_FIND_NEXT, action_find_next }, | ||
| { ACTION_FIND_PREVIOUS, action_find_previous }, | ||
|
|
@@ -1040,6 +1042,47 @@ namespace Scratch { | |
| } | ||
| } | ||
|
|
||
| private void action_clone_repo (SimpleAction action, Variant? param) { | ||
| var uri = ""; | ||
| //By default, create clone in parent of the current project | ||
| var local_folder = Path.get_dirname (git_manager.active_project_path); | ||
| var local_name = ""; | ||
| var clone_dialog = new Dialogs.CloneRepositoryDialog (local_folder); | ||
| clone_dialog.response.connect ((res) => { | ||
| if (res == Gtk.ResponseType.APPLY) { | ||
| uri = clone_dialog.get_source_repository_uri (); | ||
| local_folder = clone_dialog.get_local_folder (); | ||
| local_name = clone_dialog.get_local_name (); | ||
| } | ||
|
|
||
| clone_dialog.destroy (); | ||
| }); | ||
|
|
||
| clone_dialog.run (); | ||
danirabbit marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| if (clone_dialog.can_clone) { | ||
| //TODO Show progress while cloning | ||
| git_manager.clone_repository.begin ( | ||
| uri, | ||
| Path.build_filename (Path.DIR_SEPARATOR_S, local_folder, local_name), | ||
| (obj, res) => { | ||
| try { | ||
| File? workdir = null; | ||
| if (git_manager.clone_repository.end (res, out workdir)) { | ||
| debug ("Repository cloned into %s", workdir.get_uri ()); | ||
| open_folder (workdir); | ||
| //TODO Make active according to dialog checkbox | ||
| } | ||
| } catch (Error e) { | ||
| warning ("Unable to clone '%s'. %s", uri, e.message); | ||
| } | ||
| } | ||
| ); | ||
| } else { | ||
| //TODO Give feedback | ||
| } | ||
|
||
| } | ||
|
|
||
| private void action_collapse_all_folders () { | ||
| folder_manager_view.collapse_all (); | ||
| } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.