mirror of
https://gitlab.gnome.org/GNOME/gitg
synced 2024-10-02 14:13:48 +00:00
Add monitoring .git for changes and automatic refresh
https://bugzilla.gnome.org/show_bug.cgi?id=647879
This commit is contained in:
parent
d3289895a1
commit
f4cd34ed2d
|
@ -53,6 +53,13 @@
|
|||
Enable the use of gravatar to display user avatars.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="enable-monitoring" type="b">
|
||||
<default>true</default>
|
||||
<_summary>Enable Monitoring</_summary>
|
||||
<_description>
|
||||
Automatically update when external changes to .git are detected
|
||||
</_description>
|
||||
</key>
|
||||
</schema>
|
||||
<schema gettext-domain="@GETTEXT_PACKAGE@" id="org.gnome.gitg.preferences.history" path="/org/gnome/gitg/preferences/history/">
|
||||
<key name="collapse-inactive-lanes" type="i">
|
||||
|
|
|
@ -69,6 +69,7 @@ gitg_gitg_VALASOURCES = \
|
|||
gitg/gitg-notifications.vala \
|
||||
gitg/gitg-plugins-engine.vala \
|
||||
gitg/gitg-popup-menu.vala \
|
||||
gitg/gitg-recursive-monitor.vala \
|
||||
gitg/gitg-ref-action-copy-name.vala \
|
||||
gitg/gitg-ref-action-delete.vala \
|
||||
gitg/gitg-ref-action-fetch.vala \
|
||||
|
|
|
@ -26,6 +26,9 @@ namespace GitgCommit
|
|||
private Paned? d_main;
|
||||
private bool d_reloading;
|
||||
private bool d_has_staged;
|
||||
private ulong d_externally_changed_id;
|
||||
private bool d_ignore_external_changes;
|
||||
private Gitg.WhenMapped? d_reload_when_mapped;
|
||||
|
||||
private enum UiType
|
||||
{
|
||||
|
@ -64,6 +67,36 @@ namespace GitgCommit
|
|||
{
|
||||
application.bind_property("repository", this,
|
||||
"repository", BindingFlags.DEFAULT);
|
||||
|
||||
d_externally_changed_id = application.repository_changed_externally.connect(repository_changed_externally);
|
||||
}
|
||||
|
||||
public override void dispose()
|
||||
{
|
||||
if (d_externally_changed_id != 0)
|
||||
{
|
||||
application.disconnect(d_externally_changed_id);
|
||||
d_externally_changed_id = 0;
|
||||
}
|
||||
|
||||
base.dispose();
|
||||
}
|
||||
|
||||
private void repository_changed_externally(GitgExt.ExternalChangeHint hint)
|
||||
{
|
||||
if (!d_ignore_external_changes)
|
||||
{
|
||||
if (d_main != null && (hint & GitgExt.ExternalChangeHint.INDEX) != 0)
|
||||
{
|
||||
d_reload_when_mapped = new Gitg.WhenMapped(d_main);
|
||||
|
||||
d_reload_when_mapped.update(() => {
|
||||
reload();
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
|
||||
d_ignore_external_changes = false;
|
||||
}
|
||||
|
||||
public string display_name
|
||||
|
@ -168,6 +201,8 @@ namespace GitgCommit
|
|||
{
|
||||
stage_submodule.begin(d_current_submodule, commit, (obj, res) => {
|
||||
stage_submodule.end(res);
|
||||
|
||||
d_ignore_external_changes = true;
|
||||
reload();
|
||||
});
|
||||
}
|
||||
|
@ -281,10 +316,12 @@ namespace GitgCommit
|
|||
|
||||
if (item is Gitg.StageStatusFile)
|
||||
{
|
||||
d_ignore_external_changes = true;
|
||||
ok = yield stage_file((Gitg.StageStatusFile)item);
|
||||
}
|
||||
else if (item is Gitg.StageStatusSubmodule)
|
||||
{
|
||||
d_ignore_external_changes = true;
|
||||
ok = yield stage_submodule((Gitg.StageStatusSubmodule)item, null);
|
||||
}
|
||||
else
|
||||
|
@ -615,10 +652,12 @@ namespace GitgCommit
|
|||
|
||||
if (parents.size != 0)
|
||||
{
|
||||
d_ignore_external_changes = true;
|
||||
stage_submodule_at(parents[0] as Gitg.Commit);
|
||||
}
|
||||
else
|
||||
{
|
||||
d_ignore_external_changes = true;
|
||||
unstage_submodule.begin(d_current_submodule, (obj, res) => {
|
||||
unstage_submodule.end(res);
|
||||
reload();
|
||||
|
@ -634,10 +673,12 @@ namespace GitgCommit
|
|||
|
||||
if (item is Gitg.StageStatusFile)
|
||||
{
|
||||
d_ignore_external_changes = true;
|
||||
ok = yield unstage_file((Gitg.StageStatusFile)item);
|
||||
}
|
||||
else if (item is Gitg.StageStatusSubmodule)
|
||||
{
|
||||
d_ignore_external_changes = true;
|
||||
ok = yield unstage_submodule((Gitg.StageStatusSubmodule)item);
|
||||
}
|
||||
else
|
||||
|
@ -696,6 +737,8 @@ namespace GitgCommit
|
|||
|
||||
private void reload()
|
||||
{
|
||||
d_reload_when_mapped = null;
|
||||
|
||||
var repository = application.repository;
|
||||
|
||||
if (repository == null || d_reloading)
|
||||
|
@ -1034,6 +1077,7 @@ namespace GitgCommit
|
|||
opts |= Gitg.StageCommitOptions.SKIP_HOOKS;
|
||||
}
|
||||
|
||||
d_ignore_external_changes = true;
|
||||
stage.commit.begin(dlg.pretty_message,
|
||||
author,
|
||||
committer,
|
||||
|
@ -1327,6 +1371,7 @@ namespace GitgCommit
|
|||
{
|
||||
application.busy = true;
|
||||
|
||||
d_ignore_external_changes = true;
|
||||
discard_selection.begin((obj, res) => {
|
||||
try
|
||||
{
|
||||
|
@ -1352,6 +1397,7 @@ namespace GitgCommit
|
|||
{
|
||||
var staging = d_main.diff_view.unstaged;
|
||||
|
||||
d_ignore_external_changes = true;
|
||||
stage_unstage_selection.begin(staging, (obj, res) => {
|
||||
try
|
||||
{
|
||||
|
@ -1419,6 +1465,7 @@ namespace GitgCommit
|
|||
paths[i] = items[i].path;
|
||||
}
|
||||
|
||||
d_ignore_external_changes = true;
|
||||
revert_paths.begin(paths, (o, ret) => {
|
||||
try
|
||||
{
|
||||
|
@ -1533,6 +1580,7 @@ namespace GitgCommit
|
|||
files[i] = application.repository.get_workdir().get_child(items[i].path);
|
||||
}
|
||||
|
||||
d_ignore_external_changes = true;
|
||||
delete_files.begin(files, (o, ret) => {
|
||||
try
|
||||
{
|
||||
|
|
191
gitg/gitg-recursive-monitor.vala
Normal file
191
gitg/gitg-recursive-monitor.vala
Normal file
|
@ -0,0 +1,191 @@
|
|||
namespace Gitg
|
||||
{
|
||||
|
||||
class RecursiveMonitor : Object
|
||||
{
|
||||
class Monitor : Object
|
||||
{
|
||||
public File location;
|
||||
public RecursiveMonitor monitor;
|
||||
|
||||
public Monitor(File location, RecursiveMonitor monitor)
|
||||
{
|
||||
this.location = location;
|
||||
this.monitor = monitor;
|
||||
}
|
||||
}
|
||||
|
||||
public delegate bool FilterFunc(File file);
|
||||
|
||||
private FileMonitor? d_monitor;
|
||||
private Gee.List<Monitor> d_sub_monitors;
|
||||
private uint d_monitor_changed_timeout_id;
|
||||
private FilterFunc? d_filter_func;
|
||||
private Cancellable d_cancellable;
|
||||
private File[] d_changed_files;
|
||||
|
||||
public signal void changed(File[] files);
|
||||
|
||||
public RecursiveMonitor(File location, owned FilterFunc? filter_func = null)
|
||||
{
|
||||
d_filter_func = (owned)filter_func;
|
||||
d_sub_monitors = new Gee.LinkedList<Monitor>();
|
||||
|
||||
try
|
||||
{
|
||||
d_monitor = location.monitor_directory(FileMonitorFlags.NONE);
|
||||
}
|
||||
catch {}
|
||||
|
||||
if (d_monitor != null)
|
||||
{
|
||||
d_monitor.changed.connect(monitor_changed_timeout);
|
||||
}
|
||||
|
||||
d_cancellable = new Cancellable();
|
||||
|
||||
location.enumerate_children_async.begin(FileAttribute.STANDARD_NAME + "," + FileAttribute.STANDARD_TYPE, FileQueryInfoFlags.NONE, Priority.DEFAULT, d_cancellable, (obj, res) => {
|
||||
FileEnumerator enumerator;
|
||||
|
||||
try
|
||||
{
|
||||
enumerator = location.enumerate_children_async.end(res);
|
||||
|
||||
FileInfo? info;
|
||||
|
||||
while ((info = enumerator.next_file()) != null)
|
||||
{
|
||||
if (info.get_file_type() == FileType.DIRECTORY)
|
||||
{
|
||||
add_submonitor(location.get_child(info.get_name()));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch {}
|
||||
});
|
||||
}
|
||||
|
||||
private void add_submonitor(File location)
|
||||
{
|
||||
if (d_filter_func != null && !d_filter_func(location))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var mon = new RecursiveMonitor(location, (l) => {
|
||||
return d_filter_func(l);
|
||||
});
|
||||
|
||||
d_sub_monitors.add(new Monitor(location, mon));
|
||||
mon.changed.connect((files) => { changed_timeout(files); });
|
||||
}
|
||||
|
||||
private void add_submonitor_if_directory(File location)
|
||||
{
|
||||
try
|
||||
{
|
||||
var info = location.query_info(FileAttribute.STANDARD_TYPE, FileQueryInfoFlags.NONE);
|
||||
|
||||
if (info.get_file_type() == FileType.DIRECTORY)
|
||||
{
|
||||
add_submonitor(location);
|
||||
}
|
||||
}
|
||||
catch {}
|
||||
}
|
||||
|
||||
public override void dispose()
|
||||
{
|
||||
cancel();
|
||||
base.dispose();
|
||||
}
|
||||
|
||||
private void remove_submonitor(File location)
|
||||
{
|
||||
foreach (var monitor in d_sub_monitors)
|
||||
{
|
||||
if (location.equal(monitor.location))
|
||||
{
|
||||
d_sub_monitors.remove(monitor);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void monitor_changed_timeout(File file, File? other_file, FileMonitorEvent event)
|
||||
{
|
||||
if (event == FileMonitorEvent.CREATED)
|
||||
{
|
||||
add_submonitor_if_directory(file);
|
||||
}
|
||||
else if (event == FileMonitorEvent.DELETED)
|
||||
{
|
||||
remove_submonitor(file);
|
||||
}
|
||||
else if (event == FileMonitorEvent.MOVED)
|
||||
{
|
||||
remove_submonitor(file);
|
||||
|
||||
if (other_file != null)
|
||||
{
|
||||
add_submonitor_if_directory(other_file);
|
||||
}
|
||||
}
|
||||
|
||||
changed_timeout(new File[] { file, other_file });
|
||||
}
|
||||
|
||||
private void changed_timeout(File?[] files)
|
||||
{
|
||||
foreach (var f in files)
|
||||
{
|
||||
if (f != null && (d_filter_func == null || d_filter_func(f)))
|
||||
{
|
||||
d_changed_files += f;
|
||||
}
|
||||
}
|
||||
|
||||
if (d_monitor_changed_timeout_id != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (d_changed_files.length > 0)
|
||||
{
|
||||
d_monitor_changed_timeout_id = Timeout.add_seconds(1, () => {
|
||||
d_monitor_changed_timeout_id = 0;
|
||||
|
||||
changed(d_changed_files);
|
||||
d_changed_files = new File[0];
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void cancel()
|
||||
{
|
||||
d_cancellable.cancel();
|
||||
|
||||
if (d_monitor_changed_timeout_id != 0)
|
||||
{
|
||||
Source.remove(d_monitor_changed_timeout_id);
|
||||
d_monitor_changed_timeout_id = 0;
|
||||
}
|
||||
|
||||
foreach (var monitor in d_sub_monitors)
|
||||
{
|
||||
monitor.monitor.cancel();
|
||||
}
|
||||
|
||||
d_sub_monitors.clear();
|
||||
|
||||
if (d_monitor != null)
|
||||
{
|
||||
d_monitor.cancel();
|
||||
d_monitor = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -26,6 +26,7 @@ public class Window : Gtk.ApplicationWindow, GitgExt.Application, Initable
|
|||
private Settings d_state_settings;
|
||||
private Settings d_interface_settings;
|
||||
private Repository? d_repository;
|
||||
private RecursiveMonitor? d_repository_monitor;
|
||||
private GitgExt.MessageBus d_message_bus;
|
||||
private string? d_action;
|
||||
private Gee.HashMap<string, string> d_environment;
|
||||
|
@ -278,6 +279,11 @@ public class Window : Gtk.ApplicationWindow, GitgExt.Application, Initable
|
|||
0,
|
||||
"cancel",
|
||||
0);
|
||||
|
||||
d_interface_settings.bind("enable-monitoring",
|
||||
this,
|
||||
"enable-monitoring",
|
||||
SettingsBindFlags.GET | SettingsBindFlags.SET);
|
||||
}
|
||||
|
||||
protected override bool delete_event(Gdk.EventAny event)
|
||||
|
@ -344,10 +350,7 @@ public class Window : Gtk.ApplicationWindow, GitgExt.Application, Initable
|
|||
owned get { return d_repository; }
|
||||
set
|
||||
{
|
||||
d_repository = value;
|
||||
d_remote_manager = new RemoteManager(this);
|
||||
|
||||
notify_property("repository");
|
||||
set_repository_internal(value);
|
||||
repository_changed();
|
||||
}
|
||||
}
|
||||
|
@ -467,16 +470,98 @@ public class Window : Gtk.ApplicationWindow, GitgExt.Application, Initable
|
|||
return base.configure_event(event);
|
||||
}
|
||||
|
||||
private GitgExt.ExternalChangeHint external_change_hint_from_file(File location)
|
||||
{
|
||||
var l = d_repository.get_location();
|
||||
|
||||
var refs = l.get_child("refs");
|
||||
var index = l.get_child("index");
|
||||
var head = l.get_child("HEAD");
|
||||
|
||||
if (location.equal(refs) || location.has_prefix(refs) || location.equal(head))
|
||||
{
|
||||
return GitgExt.ExternalChangeHint.REFS;
|
||||
}
|
||||
else if (location.equal(index))
|
||||
{
|
||||
return GitgExt.ExternalChangeHint.INDEX;
|
||||
}
|
||||
else
|
||||
{
|
||||
return GitgExt.ExternalChangeHint.NONE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private bool filter_repository_changes(File location)
|
||||
{
|
||||
return external_change_hint_from_file(location) != GitgExt.ExternalChangeHint.NONE;
|
||||
}
|
||||
|
||||
private void set_repository_internal(Repository? repository)
|
||||
{
|
||||
if (d_repository_monitor != null)
|
||||
{
|
||||
d_repository_monitor.cancel();
|
||||
d_repository_monitor = null;
|
||||
}
|
||||
|
||||
d_repository = repository;
|
||||
|
||||
if (d_repository != null)
|
||||
{
|
||||
update_enable_monitoring();
|
||||
}
|
||||
|
||||
d_remote_manager = new RemoteManager(this);
|
||||
notify_property("repository");
|
||||
}
|
||||
|
||||
private bool d_enable_monitoring;
|
||||
|
||||
public bool enable_monitoring
|
||||
{
|
||||
get
|
||||
{
|
||||
return d_enable_monitoring;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
d_enable_monitoring = value;
|
||||
update_enable_monitoring();
|
||||
}
|
||||
}
|
||||
|
||||
private void update_enable_monitoring()
|
||||
{
|
||||
if (d_repository_monitor != null)
|
||||
{
|
||||
d_repository_monitor.cancel();
|
||||
d_repository_monitor = null;
|
||||
}
|
||||
|
||||
if (enable_monitoring && d_repository != null)
|
||||
{
|
||||
d_repository_monitor = new RecursiveMonitor(d_repository.get_location(), filter_repository_changes);
|
||||
d_repository_monitor.changed.connect((files) => {
|
||||
var hint = GitgExt.ExternalChangeHint.NONE;
|
||||
|
||||
foreach (var f in files)
|
||||
{
|
||||
hint |= external_change_hint_from_file(f);
|
||||
}
|
||||
|
||||
repository_changed_externally(hint);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void on_reload_activated()
|
||||
{
|
||||
try
|
||||
{
|
||||
d_repository = new Gitg.Repository(this.repository.get_location(),
|
||||
null);
|
||||
|
||||
d_remote_manager = new RemoteManager(this);
|
||||
|
||||
notify_property("repository");
|
||||
set_repository_internal(new Gitg.Repository(this.repository.get_location(), null));
|
||||
update_title();
|
||||
}
|
||||
catch {}
|
||||
|
@ -681,8 +766,7 @@ public class Window : Gtk.ApplicationWindow, GitgExt.Application, Initable
|
|||
if (ret != null)
|
||||
{
|
||||
ret.application = app;
|
||||
ret.d_repository = repository;
|
||||
ret.d_remote_manager = new RemoteManager(ret);
|
||||
ret.set_repository_internal(repository);
|
||||
ret.d_action = action;
|
||||
}
|
||||
|
||||
|
|
|
@ -46,6 +46,8 @@ namespace GitgHistory
|
|||
private uint d_walker_update_idle_id;
|
||||
private ulong d_refs_list_selection_id;
|
||||
private ulong d_refs_list_changed_id;
|
||||
private ulong d_externally_changed_id;
|
||||
private Gitg.WhenMapped? d_reload_when_mapped;
|
||||
|
||||
private Paned d_main;
|
||||
private Gitg.PopupMenu d_refs_list_popup;
|
||||
|
@ -146,6 +148,20 @@ namespace GitgHistory
|
|||
"repository", BindingFlags.DEFAULT);
|
||||
|
||||
reload_mainline();
|
||||
|
||||
d_externally_changed_id = application.repository_changed_externally.connect(repository_changed_externally);
|
||||
}
|
||||
|
||||
private void repository_changed_externally(GitgExt.ExternalChangeHint hint)
|
||||
{
|
||||
if (d_main != null && (hint & GitgExt.ExternalChangeHint.REFS) != 0)
|
||||
{
|
||||
d_reload_when_mapped = new Gitg.WhenMapped(d_main);
|
||||
|
||||
d_reload_when_mapped.update(() => {
|
||||
reload();
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
|
||||
public override void dispose()
|
||||
|
@ -168,6 +184,12 @@ namespace GitgHistory
|
|||
d_walker_update_idle_id = 0;
|
||||
}
|
||||
|
||||
if (d_externally_changed_id != 0)
|
||||
{
|
||||
application.disconnect(d_externally_changed_id);
|
||||
d_externally_changed_id = 0;
|
||||
}
|
||||
|
||||
d_commit_list_model.repository = null;
|
||||
base.dispose();
|
||||
}
|
||||
|
@ -351,6 +373,8 @@ namespace GitgHistory
|
|||
|
||||
private void reload_mainline()
|
||||
{
|
||||
d_reload_when_mapped = null;
|
||||
|
||||
var uniq = new Gee.HashSet<string>();
|
||||
|
||||
d_mainline = new string[0];
|
||||
|
|
|
@ -37,6 +37,9 @@ public class PreferencesInterface : Gtk.Grid, GitgExt.Preferences
|
|||
[GtkChild (name = "gravatar_enabled")]
|
||||
private Gtk.CheckButton d_gravatar_enabled;
|
||||
|
||||
[GtkChild (name = "monitoring_enabled" )]
|
||||
private Gtk.CheckButton d_monitoring_enabled;
|
||||
|
||||
construct
|
||||
{
|
||||
d_settings = new Settings("org.gnome.gitg.preferences.interface");
|
||||
|
@ -66,6 +69,11 @@ public class PreferencesInterface : Gtk.Grid, GitgExt.Preferences
|
|||
d_gravatar_enabled,
|
||||
"active",
|
||||
SettingsBindFlags.GET | SettingsBindFlags.SET);
|
||||
|
||||
d_settings.bind("enable-monitoring",
|
||||
d_monitoring_enabled,
|
||||
"active",
|
||||
SettingsBindFlags.GET | SettingsBindFlags.SET);
|
||||
}
|
||||
|
||||
public override void dispose()
|
||||
|
|
|
@ -160,6 +160,50 @@
|
|||
<property name="top_attach">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label5">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Monitoring</property>
|
||||
<property name="margin_top">12</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">6</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkGrid" id="grid5">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_start">12</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="row_spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="monitoring_enabled">
|
||||
<property name="label" translatable="yes">Automatically update when external changes to .git are detected</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">7</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
|
|
|
@ -35,6 +35,8 @@ public interface Application : Object
|
|||
*/
|
||||
public abstract Gitg.Repository? repository { owned get; set; }
|
||||
|
||||
public signal void repository_changed_externally(ExternalChangeHint hint);
|
||||
|
||||
/**
|
||||
* An application wide message bus over which plugins can communicate.
|
||||
*/
|
||||
|
@ -81,6 +83,15 @@ public interface Application : Object
|
|||
public abstract void open_repository(File path);
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum ExternalChangeHint
|
||||
{
|
||||
NONE = 0,
|
||||
|
||||
REFS = 1 << 0,
|
||||
INDEX = 1 << 1
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ex:set ts=4 noet:
|
||||
|
|
Loading…
Reference in a new issue