[gitg] Add scanning of folders for git repositories
- From: Jesse van den Kieboom <jessevdk src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gitg] Add scanning of folders for git repositories
- Date: Sun, 9 Aug 2015 16:11:47 +0000 (UTC)
commit 480de9ced04313fb113bd5e2cc015113d8134864
Author: Jesse van den Kieboom <jessevdk gnome org>
Date: Sun Aug 9 18:08:42 2015 +0200
Add scanning of folders for git repositories
gitg/Makefile.am | 1 +
gitg/gitg-dash-view.vala | 127 +++++++++++++++++++++++++++++++++--
gitg/gitg-recursive-scanner.vala | 102 ++++++++++++++++++++++++++++
gitg/resources/ui/gitg-dash-view.ui | 36 ++++++++--
4 files changed, 255 insertions(+), 11 deletions(-)
---
diff --git a/gitg/Makefile.am b/gitg/Makefile.am
index 874adef..a821c3d 100644
--- a/gitg/Makefile.am
+++ b/gitg/Makefile.am
@@ -70,6 +70,7 @@ gitg_gitg_VALASOURCES = \
gitg/gitg-plugins-engine.vala \
gitg/gitg-popup-menu.vala \
gitg/gitg-recursive-monitor.vala \
+ gitg/gitg-recursive-scanner.vala \
gitg/gitg-ref-action-copy-name.vala \
gitg/gitg-ref-action-delete.vala \
gitg/gitg-ref-action-fetch.vala \
diff --git a/gitg/gitg-dash-view.vala b/gitg/gitg-dash-view.vala
index 3ab30fb..87bedc1 100644
--- a/gitg/gitg-dash-view.vala
+++ b/gitg/gitg-dash-view.vala
@@ -21,7 +21,7 @@ namespace Gitg
{
[GtkTemplate (ui = "/org/gnome/gitg/ui/gitg-dash-view.ui")]
-class DashView : Gtk.Grid, GitgExt.UIElement, GitgExt.Activity, GitgExt.Selectable, GitgExt.Searchable
+class DashView : Gtk.Grid, GitgExt.UIElement, GitgExt.Activity, GitgExt.Selectable, GitgExt.Searchable,
RecursiveScanner
{
private const string version = Config.VERSION;
@@ -33,6 +33,9 @@ class DashView : Gtk.Grid, GitgExt.UIElement, GitgExt.Activity, GitgExt.Selectab
[GtkChild( name = "introduction" )]
private Gtk.Grid d_introduction;
+ [GtkChild( name = "label_scan" )]
+ private Gtk.Label d_label_scan;
+
[GtkChild( name = "label_profile") ]
private Gtk.Label d_label_profile;
@@ -240,6 +243,9 @@ class DashView : Gtk.Grid, GitgExt.UIElement, GitgExt.Activity, GitgExt.Selectab
d_repository_list_box.add.connect(update_availability);
d_repository_list_box.remove.connect(update_availability);
+ // Translators: the two %s will be replaced to create a link to perform the scanning action.
+ d_label_scan.label = _("We can also %sscan your home directory%s for git
repositories.").printf("<a href=\"scan-home\">", "</a>");
+
// Translators: the two %s will be used to create a link to the author dialog.
d_label_profile.label = _("In the mean time, you may want to %sset up your git
profile%s.").printf("<a href=\"setup-profile\">", "</a>");
update_setup_profile_visibility();
@@ -264,6 +270,19 @@ class DashView : Gtk.Grid, GitgExt.UIElement, GitgExt.Activity, GitgExt.Selectab
}
[GtkCallback]
+ private bool scan_home_activated()
+ {
+ var homedir = Environment.get_home_dir();
+
+ if (homedir != null)
+ {
+ add_repositories_scan(File.new_for_path(homedir));
+ }
+
+ return true;
+ }
+
+ [GtkCallback]
private bool setup_profile_activated()
{
AuthorDetailsDialog.show_global(application as Window);
@@ -409,7 +428,7 @@ class DashView : Gtk.Grid, GitgExt.UIElement, GitgExt.Activity, GitgExt.Selectab
}
}
- private void do_add_repository(File location)
+ private void do_add_repository(File location, bool report_errors)
{
Repository repo;
@@ -419,7 +438,11 @@ class DashView : Gtk.Grid, GitgExt.UIElement, GitgExt.Activity, GitgExt.Selectab
}
catch (Error err)
{
- application.show_infobar(_("Failed to add repository"), err.message,
Gtk.MessageType.ERROR);
+ if (report_errors)
+ {
+ application.show_infobar(_("Failed to add repository"), err.message,
Gtk.MessageType.ERROR);
+ }
+
return;
}
@@ -484,6 +507,88 @@ class DashView : Gtk.Grid, GitgExt.UIElement, GitgExt.Activity, GitgExt.Selectab
location.get_child("refs").query_exists();
}
+ private async bool file_exists_async(File file, Cancellable? cancellable)
+ {
+ try
+ {
+ return (yield file.query_info_async(FileAttribute.STANDARD_TYPE,
FileQueryInfoFlags.NONE, Priority.DEFAULT, cancellable)) != null;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ protected async bool scan_visit_directory(File file, Cancellable? cancellable)
+ {
+ if (cancellable != null && cancellable.is_cancelled())
+ {
+ return false;
+ }
+
+ // Check for .git
+ if ((yield file_exists_async(file.get_child(".git"), cancellable)))
+ {
+ do_add_repository(file, false);
+ return false;
+ }
+
+ // Check for bare
+ if ((yield file_exists_async(file.get_child("objects"), cancellable)) &&
+ (yield file_exists_async(file.get_child("HEAD"), cancellable)) &&
+ (yield file_exists_async(file.get_child("refs"), cancellable)))
+ {
+ do_add_repository(file, false);
+ return false;
+ }
+
+ return scan_visit_directory_default(file);
+ }
+
+ private void add_repositories_scan(File location)
+ {
+ var dlg = new Gtk.MessageDialog(application as Gtk.Window,
+ Gtk.DialogFlags.MODAL,
+ Gtk.MessageType.INFO,
+ Gtk.ButtonsType.CANCEL,
+ _("Scanning for repositories in %s"),
+ Utils.replace_home_dir_with_tilde(location));
+
+ dlg.set_default_response(Gtk.ResponseType.CANCEL);
+
+ var cancellable = new Cancellable();
+
+ dlg.response.connect(() => {
+ cancellable.cancel();
+ });
+
+ uint timeout_id = 0;
+
+ timeout_id = Timeout.add_seconds(1, () => {
+ if (timeout_id == 0)
+ {
+ dlg.destroy();
+ }
+
+ timeout_id = 0;
+ return false;
+ });
+
+ scan.begin(location, cancellable, () => {
+ if (timeout_id != 0)
+ {
+ timeout_id = 0;
+ }
+ else
+ {
+ dlg.destroy();
+ }
+ });
+
+ dlg.show();
+ dlg.get_window().set_cursor(new Gdk.Cursor.for_display(get_display(), Gdk.CursorType.WATCH));
+ }
+
[GtkCallback]
private void add_repository_clicked()
{
@@ -493,6 +598,14 @@ class DashView : Gtk.Grid, GitgExt.UIElement, GitgExt.Activity, GitgExt.Selectab
_("_Cancel"), Gtk.ResponseType.CANCEL,
_("_Add"), Gtk.ResponseType.OK);
+ var scan_all = new Gtk.CheckButton.with_mnemonic(_("_Scan for all git repositories from this
directory"));
+
+ scan_all.halign = Gtk.Align.END;
+ scan_all.hexpand = true;
+ scan_all.show();
+
+ chooser.extra_widget = scan_all;
+
chooser.modal = true;
chooser.set_default_response(Gtk.ResponseType.OK);
@@ -506,13 +619,17 @@ class DashView : Gtk.Grid, GitgExt.UIElement, GitgExt.Activity, GitgExt.Selectab
file = chooser.get_current_folder_file();
}
- if (!looks_like_git(file))
+ if (scan_all.active)
+ {
+ add_repositories_scan(file);
+ }
+ else if (!looks_like_git(file))
{
query_create_repository(file);
}
else
{
- do_add_repository(file);
+ do_add_repository(file, true);
}
}
diff --git a/gitg/gitg-recursive-scanner.vala b/gitg/gitg-recursive-scanner.vala
new file mode 100644
index 0000000..490d048
--- /dev/null
+++ b/gitg/gitg-recursive-scanner.vala
@@ -0,0 +1,102 @@
+/*
+ *
+ * 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
+{
+
+interface RecursiveScanner : Object
+{
+ protected async virtual void scan_visit_file(File file, Cancellable? cancellable)
+ {
+ }
+
+ protected bool scan_visit_directory_default(File file)
+ {
+ return !file.get_basename().has_prefix(".");
+ }
+
+ protected async virtual bool scan_visit_directory(File file, Cancellable? cancellable)
+ {
+ return scan_visit_directory_default(file);
+ }
+
+ public async void scan(File location, Cancellable? cancellable = null)
+ {
+ yield scan_real(location, cancellable, new Gee.HashSet<File>((file) => { return file.hash();
}, (file, other) => { return file.equal(other); }));
+ }
+
+ private async void scan_real(File location, Cancellable? cancellable, Gee.HashSet<File> seen)
+ {
+ if (cancellable != null && cancellable.is_cancelled())
+ {
+ return;
+ }
+
+ FileEnumerator? e;
+
+ try
+ {
+ e = yield location.enumerate_children_async(FileAttribute.STANDARD_NAME + "," +
FileAttribute.STANDARD_TYPE,
+ FileQueryInfoFlags.NONE,
+ Priority.DEFAULT,
+ cancellable);
+ } catch { return; }
+
+ while (cancellable == null || !cancellable.is_cancelled())
+ {
+ List<FileInfo>? files = null;
+
+ try
+ {
+ files = yield e.next_files_async(10, Priority.DEFAULT);
+ } catch {}
+
+ if (files == null) {
+ break;
+ }
+
+ foreach (var f in files)
+ {
+ var file = location.get_child(f.get_name());
+
+ if (seen.contains(file))
+ {
+ continue;
+ }
+
+ seen.add(file);
+
+ yield scan_visit_file(file, cancellable);
+
+ if (f.get_file_type() == FileType.DIRECTORY)
+ {
+ if (!(yield scan_visit_directory(file, cancellable)))
+ {
+ continue;
+ }
+
+ yield scan_real(file, cancellable, seen);
+ }
+ }
+ }
+ }
+}
+
+}
+
+// ex:ts=4 noet
\ No newline at end of file
diff --git a/gitg/resources/ui/gitg-dash-view.ui b/gitg/resources/ui/gitg-dash-view.ui
index b47e76e..f74b43e 100644
--- a/gitg/resources/ui/gitg-dash-view.ui
+++ b/gitg/resources/ui/gitg-dash-view.ui
@@ -25,7 +25,7 @@
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="width">1</property>
- <property name="height">2</property>
+ <property name="height">3</property>
</packing>
</child>
<child>
@@ -38,8 +38,8 @@
<property name="hexpand">False</property>
<property name="vexpand">True</property>
<property name="wrap">True</property>
- <property name="xalign">0</property>
<property name="max-width-chars">40</property>
+ <property name="xalign">0</property>
</object>
<packing>
<property name="left_attach">1</property>
@@ -49,22 +49,44 @@
</packing>
</child>
<child>
- <object class="GtkLabel" id="label_profile">
+ <object class="GtkLabel" id="label_scan">
<property name="visible">True</property>
<property name="can_focus">False</property>
+ <property name="label"></property>
+ <property name="use_markup">True</property>
<property name="halign">start</property>
<property name="valign">start</property>
<property name="hexpand">False</property>
<property name="vexpand">True</property>
<property name="wrap">True</property>
+ <property name="max-width-chars">40</property>
<property name="xalign">0</property>
+ <signal name="activate-link" handler="scan_home_activated" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label_profile">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="valign">start</property>
+ <property name="hexpand">False</property>
+ <property name="vexpand">True</property>
+ <property name="wrap">True</property>
<property name="max_width_chars">40</property>
<property name="use_markup">True</property>
+ <property name="xalign">0</property>
<signal name="activate-link" handler="setup_profile_activated" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
- <property name="top_attach">1</property>
+ <property name="top_attach">2</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
@@ -109,7 +131,8 @@
<child>
<object class="GtkButton" id="add_repository">
<property name="visible">True</property>
- <property name="label" translatable="yes">Add repository</property>
+ <property name="label" translatable="yes">_Add repository</property>
+ <property name="use_underline">True</property>
<signal name="clicked" handler="add_repository_clicked" swapped="no"/>
</object>
<packing>
@@ -119,7 +142,8 @@
<child>
<object class="GtkButton" id="clone_repository">
<property name="visible">True</property>
- <property name="label" translatable="yes">Clone repository</property>
+ <property name="label" translatable="yes">_Clone repository</property>
+ <property name="use_underline">True</property>
<signal name="clicked" handler="clone_repository_clicked" swapped="no"/>
</object>
<packing>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]