save work
This commit is contained in:
parent
19aca9d238
commit
2168fa2ab9
|
@ -15,7 +15,9 @@ SOURCES = \
|
||||||
SparkleListenerIrc.cs \
|
SparkleListenerIrc.cs \
|
||||||
SparkleOptions.cs \
|
SparkleOptions.cs \
|
||||||
SparklePaths.cs \
|
SparklePaths.cs \
|
||||||
SparkleRepo.cs
|
SparkleRepoBase.cs \
|
||||||
|
SparkleRepoGit.cs
|
||||||
|
|
||||||
|
|
||||||
SMARTIRC4NET_FILES_EXPANDED = $(foreach file, $(SMARTIRC4NET_FILES), $(top_builddir)/$(file))
|
SMARTIRC4NET_FILES_EXPANDED = $(foreach file, $(SMARTIRC4NET_FILES), $(top_builddir)/$(file))
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,8 @@
|
||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="SparkleRepo.cs" />
|
<Compile Include="SparkleRepoBase.cs" />
|
||||||
|
<Compile Include="SparkleRepoGit.cs" />
|
||||||
<Compile Include="SparkleFetcherBase.cs" />
|
<Compile Include="SparkleFetcherBase.cs" />
|
||||||
<Compile Include="SparkleFetcherGit.cs" />
|
<Compile Include="SparkleFetcherGit.cs" />
|
||||||
<Compile Include="Defines.cs" />
|
<Compile Include="Defines.cs" />
|
||||||
|
|
479
SparkleLib/SparkleRepoBase.cs
Normal file
479
SparkleLib/SparkleRepoBase.cs
Normal file
|
@ -0,0 +1,479 @@
|
||||||
|
// SparkleShare, an instant update workflow to Git.
|
||||||
|
// Copyright (C) 2010 Hylke Bons <hylkebons@gmail.com>
|
||||||
|
//
|
||||||
|
// 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics; // remove
|
||||||
|
using System.IO;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Timers;
|
||||||
|
|
||||||
|
namespace SparkleLib {
|
||||||
|
|
||||||
|
public enum SyncStatus {
|
||||||
|
Idle,
|
||||||
|
SyncUp,
|
||||||
|
SyncDown,
|
||||||
|
Error
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public abstract class SparkleRepoBase {
|
||||||
|
|
||||||
|
public readonly SparkleBackend Backend;
|
||||||
|
public readonly string LocalPath;
|
||||||
|
public readonly string Name;
|
||||||
|
|
||||||
|
protected SyncStatus status;
|
||||||
|
protected bool is_buffering = false;
|
||||||
|
protected bool is_polling = true;
|
||||||
|
protected bool server_online = true;
|
||||||
|
|
||||||
|
private Timer local_timer = new Timer () { Interval = 250 };
|
||||||
|
private Timer remote_timer = new Timer () { Interval = 60000 };
|
||||||
|
private FileSystemWatcher watcher;
|
||||||
|
private SparkleListenerBase listener;
|
||||||
|
private List <double> sizebuffer = new List<double> ();
|
||||||
|
private bool has_changed = false;
|
||||||
|
private Object change_lock = new Object ();
|
||||||
|
|
||||||
|
public string Domain {
|
||||||
|
get {
|
||||||
|
Regex regex = new Regex (@"*://(.+)(/|:)*");
|
||||||
|
Match match = regex.Match (Url);
|
||||||
|
|
||||||
|
if (match.Success)
|
||||||
|
return match.Groups [1].Value;
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract string Url { get; }
|
||||||
|
public abstract bool AnyDifferences { get; }
|
||||||
|
public abstract string Identifier { get; }
|
||||||
|
public abstract string CurrentRevision { get; }
|
||||||
|
public abstract bool SyncUp ();
|
||||||
|
public abstract bool SyncDown ();
|
||||||
|
|
||||||
|
public virtual bool CheckForRemoteChanges () // HasRemoteChanges { get; } ?
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual List<SparkleChangeSet> GetChangeSets (int count) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool UsesNotificationCenter {
|
||||||
|
get {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string RemoteName {
|
||||||
|
get {
|
||||||
|
return Path.GetFileNameWithoutExtension (Url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public bool IsBuffering {
|
||||||
|
get {
|
||||||
|
return this.is_buffering;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsPolling {
|
||||||
|
get {
|
||||||
|
return this.is_polling;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract bool HasUnsyncedChanges { get; set; }
|
||||||
|
|
||||||
|
public bool ServerOnline {
|
||||||
|
get {
|
||||||
|
return this.server_online;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public SyncStatus Status {
|
||||||
|
get {
|
||||||
|
return this.status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public delegate void SyncStatusChangedEventHandler (SyncStatus new_status);
|
||||||
|
public event SyncStatusChangedEventHandler SyncStatusChanged;
|
||||||
|
|
||||||
|
|
||||||
|
public delegate void NewChangeSetEventHandler (SparkleChangeSet change_set, string source_path);
|
||||||
|
public delegate void ConflictResolvedEventHandler ();
|
||||||
|
public delegate void ChangesDetectedEventHandler ();
|
||||||
|
|
||||||
|
public event NewChangeSetEventHandler NewChangeSet;
|
||||||
|
public event ConflictResolvedEventHandler ConflictResolved;
|
||||||
|
public event ChangesDetectedEventHandler ChangesDetected;
|
||||||
|
|
||||||
|
protected void OnConflictResolved ()
|
||||||
|
{
|
||||||
|
if (ConflictResolved != null)
|
||||||
|
ConflictResolved ();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: constructor (path, url, backend)
|
||||||
|
public SparkleRepoBase (string path, SparkleBackend backend)
|
||||||
|
{
|
||||||
|
LocalPath = path;
|
||||||
|
Name = Path.GetFileName (LocalPath);
|
||||||
|
Backend = backend;
|
||||||
|
|
||||||
|
SyncStatusChanged += delegate (SyncStatus status) {
|
||||||
|
this.status = status;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (CurrentRevision == null) {
|
||||||
|
CreateInitialChangeSet ();
|
||||||
|
SyncUpBase ();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watch the repository's folder
|
||||||
|
this.watcher = new FileSystemWatcher (LocalPath) {
|
||||||
|
IncludeSubdirectories = true,
|
||||||
|
EnableRaisingEvents = true,
|
||||||
|
Filter = "*"
|
||||||
|
};
|
||||||
|
|
||||||
|
this.watcher.Changed += new FileSystemEventHandler (OnFileActivity);
|
||||||
|
this.watcher.Created += new FileSystemEventHandler (OnFileActivity);
|
||||||
|
this.watcher.Deleted += new FileSystemEventHandler (OnFileActivity);
|
||||||
|
this.watcher.Renamed += new RenamedEventHandler (OnFileActivity);
|
||||||
|
|
||||||
|
CreateListener ();
|
||||||
|
|
||||||
|
|
||||||
|
this.local_timer.Elapsed += delegate (object o, ElapsedEventArgs args) {
|
||||||
|
CheckForChanges ();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.remote_timer.Elapsed += delegate {
|
||||||
|
if (this.is_polling) {
|
||||||
|
if (CheckForRemoteChanges ())
|
||||||
|
SyncDownBase ();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.is_polling && !this.listener.IsConnected)
|
||||||
|
this.listener.Connect ();
|
||||||
|
|
||||||
|
if (HasUnsyncedChanges)
|
||||||
|
SyncUpBase ();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.remote_timer.Start ();
|
||||||
|
this.local_timer.Start ();
|
||||||
|
|
||||||
|
// Sync up everything that changed
|
||||||
|
// since we've been off
|
||||||
|
DisableWatching ();
|
||||||
|
if (AnyDifferences) {
|
||||||
|
SyncUpBase ();
|
||||||
|
while (HasUnsyncedChanges)
|
||||||
|
SyncUpBase ();
|
||||||
|
}
|
||||||
|
EnableWatching ();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void CreateListener ()
|
||||||
|
{
|
||||||
|
NotificationServerType server_type;
|
||||||
|
if (UsesNotificationCenter)
|
||||||
|
server_type = NotificationServerType.Central;
|
||||||
|
else
|
||||||
|
server_type = NotificationServerType.Own;
|
||||||
|
this.listener = new SparkleListenerIrc (Domain, Identifier, server_type);////////////
|
||||||
|
|
||||||
|
// Stop polling when the connection to the irc channel is succesful
|
||||||
|
this.listener.Connected += delegate {
|
||||||
|
this.is_polling = false;
|
||||||
|
|
||||||
|
// Check for changes manually one more time
|
||||||
|
CheckForRemoteChanges ();
|
||||||
|
|
||||||
|
// Push changes that were made since the last disconnect
|
||||||
|
if (HasUnsyncedChanges)
|
||||||
|
SyncUpBase ();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Start polling when the connection to the irc channel is lost
|
||||||
|
this.listener.Disconnected += delegate {
|
||||||
|
SparkleHelpers.DebugInfo ("Local", "[" + Name + "] Falling back to polling");
|
||||||
|
this.is_polling = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fetch changes when there is a message in the irc channel
|
||||||
|
this.listener.RemoteChange += delegate (string change_id) {
|
||||||
|
if (!change_id.Equals (CurrentRevision) && change_id.Length == 40) {
|
||||||
|
if (Status != SyncStatus.SyncUp &&
|
||||||
|
Status != SyncStatus.SyncDown &&
|
||||||
|
!this.is_buffering) {
|
||||||
|
|
||||||
|
while (this.listener.ChangesQueue > 0) {
|
||||||
|
SyncDownBase ();
|
||||||
|
this.listener.DecrementChangesQueue ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Start listening
|
||||||
|
this.listener.Connect ();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void CheckForChanges ()
|
||||||
|
{
|
||||||
|
lock (this.change_lock) {
|
||||||
|
if (this.has_changed) {
|
||||||
|
if ( this.sizebuffer.Count >= 4)
|
||||||
|
this.sizebuffer.RemoveAt (0);
|
||||||
|
|
||||||
|
DirectoryInfo dir_info = new DirectoryInfo (LocalPath);
|
||||||
|
this.sizebuffer.Add (CalculateFolderSize (dir_info));
|
||||||
|
|
||||||
|
if ( this.sizebuffer [0].Equals (this.sizebuffer [1]) &&
|
||||||
|
this.sizebuffer [1].Equals (this.sizebuffer [2]) &&
|
||||||
|
this.sizebuffer [2].Equals (this.sizebuffer [3])) {
|
||||||
|
|
||||||
|
SparkleHelpers.DebugInfo ("Local", "[" + Name + "] Changes have settled.");
|
||||||
|
this.is_buffering = false;
|
||||||
|
this.has_changed = false;
|
||||||
|
|
||||||
|
DisableWatching ();
|
||||||
|
while (AnyDifferences)
|
||||||
|
SyncUpBase ();//TODO look at algorithm
|
||||||
|
EnableWatching ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Starts a timer when something changes
|
||||||
|
private void OnFileActivity (object o, FileSystemEventArgs fse_args)
|
||||||
|
{
|
||||||
|
if (fse_args.Name.StartsWith (".git/"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
WatcherChangeTypes wct = fse_args.ChangeType;
|
||||||
|
|
||||||
|
if (AnyDifferences) {
|
||||||
|
this.is_buffering = true;
|
||||||
|
|
||||||
|
// Only fire the event if the timer has been stopped.
|
||||||
|
// This prevents multiple events from being raised whilst "buffering".
|
||||||
|
if (!this.has_changed) {
|
||||||
|
if (ChangesDetected != null)
|
||||||
|
ChangesDetected ();
|
||||||
|
}
|
||||||
|
|
||||||
|
SparkleHelpers.DebugInfo ("Event", "[" + Name + "] " + wct.ToString () + " '" + fse_args.Name + "'");
|
||||||
|
SparkleHelpers.DebugInfo ("Local", "[" + Name + "] Changes found, checking if settled.");
|
||||||
|
|
||||||
|
this.remote_timer.Stop ();
|
||||||
|
|
||||||
|
lock (this.change_lock) {
|
||||||
|
this.has_changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void SyncUpBase ()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
this.local_timer.Stop ();
|
||||||
|
this.remote_timer.Stop ();
|
||||||
|
|
||||||
|
SparkleHelpers.DebugInfo ("SyncUp", "[" + Name + "] Initiated");
|
||||||
|
|
||||||
|
//if (AnyDifferences) {
|
||||||
|
if (SyncStatusChanged != null)
|
||||||
|
SyncStatusChanged (SyncStatus.SyncUp);
|
||||||
|
|
||||||
|
if (SyncUp ()) {
|
||||||
|
SparkleHelpers.DebugInfo ("SyncUp", "[" + Name + "] Done");
|
||||||
|
|
||||||
|
HasUnsyncedChanges = false;
|
||||||
|
|
||||||
|
if (SyncStatusChanged != null)
|
||||||
|
SyncStatusChanged (SyncStatus.Idle);
|
||||||
|
|
||||||
|
this.listener.Announce (CurrentRevision);
|
||||||
|
} else {
|
||||||
|
SparkleHelpers.DebugInfo ("SyncUp", "[" + Name + "] Error");
|
||||||
|
|
||||||
|
HasUnsyncedChanges = true;
|
||||||
|
|
||||||
|
if (SyncStatusChanged != null)
|
||||||
|
SyncStatusChanged (SyncStatus.Error);
|
||||||
|
|
||||||
|
SyncDownBase ();
|
||||||
|
}
|
||||||
|
//}
|
||||||
|
} finally {
|
||||||
|
this.remote_timer.Start ();
|
||||||
|
this.local_timer.Start ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void SyncDownBase ()
|
||||||
|
{
|
||||||
|
SparkleHelpers.DebugInfo ("SyncDown", "[" + Name + "] Initiated");
|
||||||
|
this.remote_timer.Stop ();
|
||||||
|
|
||||||
|
if (SyncStatusChanged != null)
|
||||||
|
SyncStatusChanged (SyncStatus.SyncDown);
|
||||||
|
|
||||||
|
if (SyncDown ()) {
|
||||||
|
SparkleHelpers.DebugInfo ("SyncDown", "[" + Name + "] Done");
|
||||||
|
this.server_online = true;
|
||||||
|
|
||||||
|
if (SyncStatusChanged != null)
|
||||||
|
SyncStatusChanged (SyncStatus.Idle);
|
||||||
|
} else {
|
||||||
|
SparkleHelpers.DebugInfo ("SyncDown", "[" + Name + "] Error");
|
||||||
|
this.server_online = false;
|
||||||
|
|
||||||
|
if (SyncStatusChanged != null)
|
||||||
|
SyncStatusChanged (SyncStatus.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SyncStatusChanged != null)
|
||||||
|
SyncStatusChanged (SyncStatus.Idle);
|
||||||
|
|
||||||
|
this.remote_timer.Start ();
|
||||||
|
|
||||||
|
if (NewChangeSet != null)
|
||||||
|
NewChangeSet (GetChangeSets (1) [0], LocalPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void DisableWatching ()
|
||||||
|
{
|
||||||
|
this.watcher.EnableRaisingEvents = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void EnableWatching ()
|
||||||
|
{
|
||||||
|
this.watcher.EnableRaisingEvents = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private string GetUserName () // TODO
|
||||||
|
{
|
||||||
|
SparkleGit git = new SparkleGit (LocalPath, "config --get user.name");
|
||||||
|
git.Start ();
|
||||||
|
git.WaitForExit ();
|
||||||
|
|
||||||
|
string output = git.StandardOutput.ReadToEnd ();
|
||||||
|
string user_name = output.Trim ();
|
||||||
|
|
||||||
|
return user_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private string GetUserEmail ()
|
||||||
|
{
|
||||||
|
SparkleGit git = new SparkleGit (LocalPath, "config --get user.email");
|
||||||
|
git.Start ();
|
||||||
|
git.WaitForExit ();
|
||||||
|
|
||||||
|
string output = git.StandardOutput.ReadToEnd ();
|
||||||
|
string user_email = output.Trim ();
|
||||||
|
|
||||||
|
return user_email;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Recursively gets a folder's size in bytes
|
||||||
|
private double CalculateFolderSize (DirectoryInfo parent)
|
||||||
|
{
|
||||||
|
if (!System.IO.Directory.Exists (parent.ToString ()))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
double size = 0;
|
||||||
|
|
||||||
|
// Ignore the temporary 'rebase-apply' directory. This prevents potential
|
||||||
|
// crashes when files are being queried whilst the files have already been deleted.
|
||||||
|
if (parent.Name.Equals ("rebase-apply"))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
foreach (FileInfo file in parent.GetFiles()) {
|
||||||
|
if (!file.Exists)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
size += file.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (DirectoryInfo directory in parent.GetDirectories())
|
||||||
|
size += CalculateFolderSize (directory);
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Create an initial change set when the
|
||||||
|
// user has fetched an empty remote folder
|
||||||
|
public virtual void CreateInitialChangeSet ()
|
||||||
|
{
|
||||||
|
string file_path = Path.Combine (LocalPath, "SparkleShare.txt");
|
||||||
|
TextWriter writer = new StreamWriter (file_path);
|
||||||
|
writer.WriteLine (":)");
|
||||||
|
writer.Close ();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public string GetConfigItem (string name)
|
||||||
|
{
|
||||||
|
if (String.Compare (name, "sparkleshare.user.name", true) == 0)
|
||||||
|
return GetUserName ();
|
||||||
|
else if (String.Compare (name, "sparkleshare.user.email", true) == 0)
|
||||||
|
return GetUserEmail ();
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static bool IsRepo (string path)
|
||||||
|
{
|
||||||
|
return System.IO.Directory.Exists (Path.Combine (path, ".git"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Disposes all resourses of this object
|
||||||
|
public void Dispose ()
|
||||||
|
{
|
||||||
|
this.remote_timer.Dispose ();
|
||||||
|
this.local_timer.Dispose ();
|
||||||
|
this.listener.Dispose ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,44 +20,16 @@ using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Timers;
|
|
||||||
|
|
||||||
using Meebey.SmartIrc4net;
|
|
||||||
using Mono.Unix;
|
|
||||||
|
|
||||||
namespace SparkleLib {
|
namespace SparkleLib {
|
||||||
|
|
||||||
public enum SyncStatus {
|
public class SparkleRepoGit : SparkleRepoBase {
|
||||||
Idle,
|
|
||||||
SyncUp,
|
public SparkleRepoGit (string path, SparkleBackend backend) :
|
||||||
SyncDown,
|
base (path, backend) { }
|
||||||
Error
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public class SparkleRepo {
|
public override string Url {
|
||||||
|
|
||||||
public readonly SparkleBackend Backend;
|
|
||||||
public readonly string LocalPath;
|
|
||||||
public readonly string Name;
|
|
||||||
|
|
||||||
protected SyncStatus status;
|
|
||||||
protected string revision;
|
|
||||||
protected bool is_buffering = false;
|
|
||||||
protected bool is_polling = true;
|
|
||||||
protected bool server_online = true;
|
|
||||||
|
|
||||||
private Timer remote_timer;
|
|
||||||
private Timer local_timer;
|
|
||||||
private FileSystemWatcher watcher;
|
|
||||||
private SparkleListenerBase listener;
|
|
||||||
private List <double> sizebuffer;
|
|
||||||
private bool has_changed = false;
|
|
||||||
private Object change_lock = new Object ();
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: make this a regexp
|
|
||||||
public string Url {
|
|
||||||
get {
|
get {
|
||||||
SparkleGit git = new SparkleGit (LocalPath, "config --get remote.origin.url");
|
SparkleGit git = new SparkleGit (LocalPath, "config --get remote.origin.url");
|
||||||
git.Start ();
|
git.Start ();
|
||||||
|
@ -68,43 +40,132 @@ namespace SparkleLib {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: make this a regexp
|
|
||||||
public string Domain {
|
public override string Identifier {
|
||||||
get {
|
get {
|
||||||
string domain = Url.Substring (Url.IndexOf ("@") + 1);
|
|
||||||
|
|
||||||
if (domain.Contains (":"))
|
// Because git computes a hash based on content,
|
||||||
return domain = domain.Substring (0, domain.IndexOf (":"));
|
// author, and timestamp; it is unique enough to
|
||||||
else
|
// use the hash of the first commit as an identifier
|
||||||
return domain = domain.Substring (0, domain.IndexOf ("/"));
|
// for our folder
|
||||||
|
SparkleGit git = new SparkleGit (LocalPath, "rev-list --reverse HEAD");
|
||||||
|
git.Start ();
|
||||||
|
|
||||||
|
// Reading the standard output HAS to go before
|
||||||
|
// WaitForExit, or it will hang forever on output > 4096 bytes
|
||||||
|
string output = git.StandardOutput.ReadToEnd ();
|
||||||
|
git.WaitForExit ();
|
||||||
|
|
||||||
|
return output.Substring (0, 40);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string RemoteName {
|
|
||||||
|
public override string CurrentRevision {
|
||||||
get {
|
get {
|
||||||
return Path.GetFileNameWithoutExtension (Url);
|
|
||||||
|
// Remove stale rebase-apply files because it
|
||||||
|
// makes the method return the wrong hashes.
|
||||||
|
string rebase_apply_file = SparkleHelpers.CombineMore (LocalPath, ".git", "rebase-apply");
|
||||||
|
if (File.Exists (rebase_apply_file))
|
||||||
|
File.Delete (rebase_apply_file);
|
||||||
|
|
||||||
|
SparkleGit git = new SparkleGit (LocalPath, "log -1 --format=%H");
|
||||||
|
git.Start ();
|
||||||
|
git.WaitForExit ();
|
||||||
|
|
||||||
|
if (git.ExitCode == 0) {
|
||||||
|
string output = git.StandardOutput.ReadToEnd ();
|
||||||
|
return output.TrimEnd ();
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Revision {
|
|
||||||
|
public override bool CheckForRemoteChanges ()
|
||||||
|
{
|
||||||
|
SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Checking for remote changes...");
|
||||||
|
SparkleGit git = new SparkleGit (LocalPath, "ls-remote origin master");
|
||||||
|
|
||||||
|
git.Start ();
|
||||||
|
git.WaitForExit ();
|
||||||
|
|
||||||
|
if (git.ExitCode != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
string remote_revision = git.StandardOutput.ReadToEnd ().TrimEnd ();
|
||||||
|
|
||||||
|
if (!remote_revision.StartsWith (CurrentRevision)) {
|
||||||
|
SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Remote changes found. (" + remote_revision + ")");
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public override bool SyncUp ()
|
||||||
|
{
|
||||||
|
Add ();
|
||||||
|
|
||||||
|
string message = FormatCommitMessage ();
|
||||||
|
Commit (message);
|
||||||
|
|
||||||
|
SparkleGit git = new SparkleGit (LocalPath, "push origin master");
|
||||||
|
|
||||||
|
git.Start ();
|
||||||
|
git.WaitForExit ();
|
||||||
|
|
||||||
|
if (git.ExitCode == 0) {
|
||||||
|
return true;
|
||||||
|
//FetchRebaseAndPush ();TODO
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public override bool SyncDown ()
|
||||||
|
{
|
||||||
|
SparkleGit git = new SparkleGit (LocalPath, "fetch -v origin master");
|
||||||
|
|
||||||
|
git.Start ();
|
||||||
|
git.WaitForExit ();
|
||||||
|
|
||||||
|
if (git.ExitCode == 0) {
|
||||||
|
Rebase ();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public override bool AnyDifferences {
|
||||||
get {
|
get {
|
||||||
return this.revision;
|
SparkleGit git = new SparkleGit (LocalPath, "status --porcelain");
|
||||||
|
git.Start ();
|
||||||
|
git.WaitForExit ();
|
||||||
|
|
||||||
|
string output = git.StandardOutput.ReadToEnd ().TrimEnd ();
|
||||||
|
string [] lines = output.Split ("\n".ToCharArray ());
|
||||||
|
|
||||||
|
foreach (string line in lines) {
|
||||||
|
if (line.Length > 1 && !line [1].Equals (" "))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsBuffering {
|
|
||||||
get {
|
|
||||||
return this.is_buffering;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsPolling {
|
|
||||||
get {
|
|
||||||
return this.is_polling;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool HasUnsyncedChanges {
|
|
||||||
|
|
||||||
|
public override bool HasUnsyncedChanges {
|
||||||
get {
|
get {
|
||||||
string unsynced_file_path = SparkleHelpers.CombineMore (LocalPath,
|
string unsynced_file_path = SparkleHelpers.CombineMore (LocalPath,
|
||||||
".git", "has_unsynced_changes");
|
".git", "has_unsynced_changes");
|
||||||
|
@ -125,324 +186,10 @@ namespace SparkleLib {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ServerOnline {
|
|
||||||
get {
|
|
||||||
return this.server_online;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public SyncStatus Status {
|
|
||||||
get {
|
|
||||||
return this.status;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public delegate void SyncStatusChangedEventHandler (SyncStatus new_status);
|
|
||||||
public event SyncStatusChangedEventHandler SyncStatusChanged;
|
|
||||||
|
|
||||||
|
|
||||||
public delegate void NewChangeSetEventHandler (SparkleChangeSet change_set, string source_path);
|
|
||||||
public delegate void ConflictResolvedEventHandler ();
|
|
||||||
public delegate void ChangesDetectedEventHandler ();
|
|
||||||
|
|
||||||
public event NewChangeSetEventHandler NewChangeSet;
|
|
||||||
public event ConflictResolvedEventHandler ConflictResolved;
|
|
||||||
public event ChangesDetectedEventHandler ChangesDetected;
|
|
||||||
|
|
||||||
public SparkleRepo (string path, SparkleBackend backend)
|
|
||||||
{
|
|
||||||
LocalPath = path;
|
|
||||||
Name = Path.GetFileName (LocalPath);
|
|
||||||
Backend = backend;
|
|
||||||
|
|
||||||
SyncStatusChanged += delegate (SyncStatus status) {
|
|
||||||
this.status = status;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (IsEmpty)
|
|
||||||
this.revision = null;
|
|
||||||
else
|
|
||||||
this.revision = GetRevision ();
|
|
||||||
|
|
||||||
if (this.revision == null)
|
|
||||||
CreateInitialChangeSet ();
|
|
||||||
|
|
||||||
// Watch the repository's folder
|
|
||||||
this.watcher = new FileSystemWatcher (LocalPath) {
|
|
||||||
IncludeSubdirectories = true,
|
|
||||||
EnableRaisingEvents = true,
|
|
||||||
Filter = "*"
|
|
||||||
};
|
|
||||||
|
|
||||||
this.watcher.Changed += new FileSystemEventHandler (OnFileActivity);
|
|
||||||
this.watcher.Created += new FileSystemEventHandler (OnFileActivity);
|
|
||||||
this.watcher.Deleted += new FileSystemEventHandler (OnFileActivity);
|
|
||||||
this.watcher.Renamed += new RenamedEventHandler (OnFileActivity);
|
|
||||||
|
|
||||||
NotificationServerType server_type;
|
|
||||||
if (UsesNotificationCenter)
|
|
||||||
server_type = NotificationServerType.Central;
|
|
||||||
else
|
|
||||||
server_type = NotificationServerType.Own;
|
|
||||||
|
|
||||||
this.listener = new SparkleListenerIrc (Domain, Identifier, server_type);
|
|
||||||
|
|
||||||
// ...fetch remote changes every 60 seconds if that fails
|
|
||||||
this.remote_timer = new Timer () {
|
|
||||||
Interval = 60000
|
|
||||||
};
|
|
||||||
|
|
||||||
this.remote_timer.Elapsed += delegate {
|
|
||||||
if (this.is_polling) {
|
|
||||||
CheckForRemoteChanges ();
|
|
||||||
|
|
||||||
if (!this.listener.IsConnected)
|
|
||||||
this.listener.Connect ();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (HasUnsyncedChanges)
|
|
||||||
FetchRebaseAndPush ();
|
|
||||||
};
|
|
||||||
|
|
||||||
// Stop polling when the connection to the irc channel is succesful
|
|
||||||
this.listener.Connected += delegate {
|
|
||||||
this.is_polling = false;
|
|
||||||
|
|
||||||
// Check for changes manually one more time
|
|
||||||
CheckForRemoteChanges ();
|
|
||||||
|
|
||||||
// Push changes that were made since the last disconnect
|
|
||||||
if (HasUnsyncedChanges)
|
|
||||||
Push ();
|
|
||||||
};
|
|
||||||
|
|
||||||
// Start polling when the connection to the irc channel is lost
|
|
||||||
this.listener.Disconnected += delegate {
|
|
||||||
SparkleHelpers.DebugInfo ("Local", "[" + Name + "] Falling back to polling");
|
|
||||||
this.is_polling = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Fetch changes when there is a message in the irc channel
|
|
||||||
this.listener.RemoteChange += delegate (string change_id) {
|
|
||||||
if (!change_id.Equals (this.revision) && change_id.Length == 40) {
|
|
||||||
if (Status != SyncStatus.SyncUp &&
|
|
||||||
Status != SyncStatus.SyncDown &&
|
|
||||||
!this.is_buffering) {
|
|
||||||
|
|
||||||
while (this.listener.ChangesQueue > 0) {
|
|
||||||
SyncDown ();
|
|
||||||
this.listener.DecrementChangesQueue ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Start listening
|
|
||||||
this.listener.Connect ();
|
|
||||||
|
|
||||||
this.sizebuffer = new List <double> ();
|
|
||||||
|
|
||||||
// Keep a timer that checks if there are changes and
|
|
||||||
// whether they have settled
|
|
||||||
this.local_timer = new Timer () {
|
|
||||||
Interval = 250
|
|
||||||
};
|
|
||||||
|
|
||||||
this.local_timer.Elapsed += delegate (object o, ElapsedEventArgs args) {
|
|
||||||
CheckForChanges ();
|
|
||||||
};
|
|
||||||
|
|
||||||
this.remote_timer.Start ();
|
|
||||||
this.local_timer.Start ();
|
|
||||||
|
|
||||||
// Add everything that changed
|
|
||||||
// since SparkleShare was stopped
|
|
||||||
AddCommitAndPush ();
|
|
||||||
|
|
||||||
if (this.revision == null)
|
|
||||||
this.revision = GetRevision ();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public string Identifier {
|
|
||||||
get {
|
|
||||||
|
|
||||||
// Because git computes a hash based on content,
|
|
||||||
// author, and timestamp; it is unique enough to
|
|
||||||
// use the hash of the first commit as an identifier
|
|
||||||
// for our folder
|
|
||||||
SparkleGit git = new SparkleGit (LocalPath, "rev-list --reverse HEAD");
|
|
||||||
git.Start ();
|
|
||||||
|
|
||||||
// Reading the standard output HAS to go before
|
|
||||||
// WaitForExit, or it will hang forever on output > 4096 bytes
|
|
||||||
string output = git.StandardOutput.ReadToEnd ();
|
|
||||||
git.WaitForExit ();
|
|
||||||
|
|
||||||
return output.Substring (0, 40);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void CheckForRemoteChanges ()
|
|
||||||
{
|
|
||||||
SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Checking for remote changes...");
|
|
||||||
SparkleGit git = new SparkleGit (LocalPath, "ls-remote origin master");
|
|
||||||
|
|
||||||
git.Start ();
|
|
||||||
git.WaitForExit ();
|
|
||||||
|
|
||||||
if (git.ExitCode != 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
string remote_revision = git.StandardOutput.ReadToEnd ().TrimEnd ();
|
|
||||||
|
|
||||||
if (!remote_revision.StartsWith (this.revision)) {
|
|
||||||
SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Remote changes found. (" + remote_revision + ")");
|
|
||||||
SyncDown ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void CheckForChanges ()
|
|
||||||
{
|
|
||||||
lock (this.change_lock) {
|
|
||||||
if (this.has_changed) {
|
|
||||||
if ( this.sizebuffer.Count >= 4)
|
|
||||||
this.sizebuffer.RemoveAt (0);
|
|
||||||
|
|
||||||
DirectoryInfo dir_info = new DirectoryInfo (LocalPath);
|
|
||||||
this.sizebuffer.Add (CalculateFolderSize (dir_info));
|
|
||||||
|
|
||||||
if ( this.sizebuffer [0].Equals (this.sizebuffer [1]) &&
|
|
||||||
this.sizebuffer [1].Equals (this.sizebuffer [2]) &&
|
|
||||||
this.sizebuffer [2].Equals (this.sizebuffer [3])) {
|
|
||||||
|
|
||||||
SparkleHelpers.DebugInfo ("Local", "[" + Name + "] Changes have settled.");
|
|
||||||
this.is_buffering = false;
|
|
||||||
this.has_changed = false;
|
|
||||||
|
|
||||||
while (AnyDifferences) {
|
|
||||||
this.watcher.EnableRaisingEvents = false;
|
|
||||||
AddCommitAndPush ();
|
|
||||||
this.watcher.EnableRaisingEvents = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Starts a timer when something changes
|
|
||||||
private void OnFileActivity (object o, FileSystemEventArgs fse_args)
|
|
||||||
{
|
|
||||||
if (fse_args.Name.StartsWith (".git/"))
|
|
||||||
return;
|
|
||||||
|
|
||||||
WatcherChangeTypes wct = fse_args.ChangeType;
|
|
||||||
|
|
||||||
if (AnyDifferences) {
|
|
||||||
this.is_buffering = true;
|
|
||||||
|
|
||||||
// Only fire the event if the timer has been stopped.
|
|
||||||
// This prevents multiple events from being raised whilst "buffering".
|
|
||||||
if (!this.has_changed) {
|
|
||||||
if (ChangesDetected != null)
|
|
||||||
ChangesDetected ();
|
|
||||||
}
|
|
||||||
|
|
||||||
SparkleHelpers.DebugInfo ("Event", "[" + Name + "] " + wct.ToString () + " '" + fse_args.Name + "'");
|
|
||||||
SparkleHelpers.DebugInfo ("Local", "[" + Name + "] Changes found, checking if settled.");
|
|
||||||
|
|
||||||
this.remote_timer.Stop ();
|
|
||||||
|
|
||||||
lock (this.change_lock) {
|
|
||||||
this.has_changed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// When there are changes we generally want to Add, Commit and Push,
|
|
||||||
// so this method does them all with appropriate timers, etc. switched off
|
|
||||||
public void AddCommitAndPush ()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
this.local_timer.Stop ();
|
|
||||||
this.remote_timer.Stop ();
|
|
||||||
|
|
||||||
if (AnyDifferences) {
|
|
||||||
Add ();
|
|
||||||
|
|
||||||
string message = FormatCommitMessage ();
|
|
||||||
Commit (message);
|
|
||||||
|
|
||||||
Push ();
|
|
||||||
} else {
|
|
||||||
if (SyncStatusChanged != null)
|
|
||||||
SyncStatusChanged (SyncStatus.Idle); // TODO: in checklocalforchanges
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
this.remote_timer.Start ();
|
|
||||||
this.local_timer.Start ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void FetchRebaseAndPush ()
|
|
||||||
{
|
|
||||||
CheckForRemoteChanges ();
|
|
||||||
Push ();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private bool AnyDifferences {
|
|
||||||
get {
|
|
||||||
SparkleGit git = new SparkleGit (LocalPath, "status --porcelain");
|
|
||||||
git.Start ();
|
|
||||||
git.WaitForExit ();
|
|
||||||
|
|
||||||
string output = git.StandardOutput.ReadToEnd ().TrimEnd ();
|
|
||||||
string [] lines = output.Split ("\n".ToCharArray ());
|
|
||||||
|
|
||||||
foreach (string line in lines) {
|
|
||||||
if (line.Length > 1 && !line [1].Equals (" "))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private bool IsEmpty {
|
|
||||||
get {
|
|
||||||
SparkleGit git = new SparkleGit (LocalPath, "log -1");
|
|
||||||
git.Start ();
|
|
||||||
git.WaitForExit ();
|
|
||||||
|
|
||||||
return (git.ExitCode != 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private string GetRevision ()
|
|
||||||
{
|
|
||||||
// Remove stale rebase-apply files because it
|
|
||||||
// makes the method return the wrong hashes.
|
|
||||||
string rebase_apply_file = SparkleHelpers.CombineMore (LocalPath, ".git", "rebase-apply");
|
|
||||||
if (File.Exists (rebase_apply_file))
|
|
||||||
File.Delete (rebase_apply_file);
|
|
||||||
|
|
||||||
SparkleGit git = new SparkleGit (LocalPath, "log -1 --format=%H");
|
|
||||||
git.Start ();
|
|
||||||
git.WaitForExit ();
|
|
||||||
|
|
||||||
string output = git.StandardOutput.ReadToEnd ();
|
|
||||||
string revision = output.Trim ();
|
|
||||||
|
|
||||||
return revision;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Stages the made changes
|
// Stages the made changes
|
||||||
|
@ -477,59 +224,15 @@ namespace SparkleLib {
|
||||||
git.Start ();
|
git.Start ();
|
||||||
git.WaitForExit ();
|
git.WaitForExit ();
|
||||||
|
|
||||||
this.revision = GetRevision ();
|
SparkleHelpers.DebugInfo ("Commit", "[" + Name + "] " + message);
|
||||||
SparkleHelpers.DebugInfo ("Commit", "[" + Name + "] " + message + " (" + this.revision + ")");
|
|
||||||
|
|
||||||
// Collect garbage pseudo-randomly
|
// Collect garbage pseudo-randomly
|
||||||
if (DateTime.Now.Second % 10 == 0)
|
if (DateTime.Now.Second % 10 == 0)
|
||||||
CollectGarbage ();
|
CollectGarbage ();
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void SyncDownBase ()
|
|
||||||
{
|
|
||||||
SparkleHelpers.DebugInfo ("Sync", "[" + Name + "] Initiated");
|
|
||||||
this.remote_timer.Stop ();
|
|
||||||
|
|
||||||
if (SyncStatusChanged != null)
|
|
||||||
SyncStatusChanged (SyncStatus.SyncDown);
|
|
||||||
|
|
||||||
if (SyncDown ()) {
|
|
||||||
SparkleHelpers.DebugInfo ("Sync", "[" + Name + "] Done");
|
|
||||||
this.server_online = true;
|
|
||||||
this.revision = GetRevision ();
|
|
||||||
|
|
||||||
if (SyncStatusChanged != null)
|
|
||||||
SyncStatusChanged (SyncStatus.Idle);
|
|
||||||
} else {
|
|
||||||
SparkleHelpers.DebugInfo ("Sync", "[" + Name + "] Error");
|
|
||||||
this.server_online = false;
|
|
||||||
|
|
||||||
if (SyncStatusChanged != null)
|
|
||||||
SyncStatusChanged (SyncStatus.Error);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SyncStatusChanged != null)
|
|
||||||
SyncStatusChanged (SyncStatus.Idle);
|
|
||||||
|
|
||||||
this.remote_timer.Start ();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Fetches changes from the remote repository
|
|
||||||
public bool SyncDown ()
|
|
||||||
{
|
|
||||||
SparkleGit git = new SparkleGit (LocalPath, "fetch -v origin master");
|
|
||||||
|
|
||||||
git.Start ();
|
|
||||||
git.WaitForExit ();
|
|
||||||
|
|
||||||
if (git.ExitCode == 0) {
|
|
||||||
Rebase ();
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Merges the fetched changes
|
// Merges the fetched changes
|
||||||
|
@ -559,21 +262,17 @@ namespace SparkleLib {
|
||||||
SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Conflict resolved.");
|
SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Conflict resolved.");
|
||||||
EnableWatching ();
|
EnableWatching ();
|
||||||
|
|
||||||
if (ConflictResolved != null)
|
OnConflictResolved ();
|
||||||
ConflictResolved ();
|
|
||||||
|
|
||||||
Push ();
|
//TODO Push ();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.revision = GetRevision ();
|
|
||||||
|
|
||||||
if (NewChangeSet != null)
|
|
||||||
NewChangeSet (GetChangeSets (1) [0], LocalPath);
|
|
||||||
|
|
||||||
EnableWatching ();
|
EnableWatching ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void ResolveConflict ()
|
private void ResolveConflict ()
|
||||||
{
|
{
|
||||||
// This is al list of conflict status codes that Git uses, their
|
// This is al list of conflict status codes that Git uses, their
|
||||||
|
@ -669,149 +368,9 @@ namespace SparkleLib {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Pushes the changes to the remote repo
|
|
||||||
public void Push ()
|
|
||||||
{
|
|
||||||
SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Pushing changes");
|
|
||||||
SparkleGit git = new SparkleGit (LocalPath, "push origin master");
|
|
||||||
|
|
||||||
if (SyncStatusChanged != null)
|
|
||||||
SyncStatusChanged (SyncStatus.SyncUp);
|
|
||||||
|
|
||||||
git.Start ();
|
|
||||||
git.WaitForExit ();
|
|
||||||
|
|
||||||
if (git.ExitCode != 0) {
|
|
||||||
SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Changes not pushed");
|
|
||||||
|
|
||||||
HasUnsyncedChanges = true;
|
|
||||||
|
|
||||||
if (SyncStatusChanged != null)
|
|
||||||
SyncStatusChanged (SyncStatus.Error);
|
|
||||||
|
|
||||||
FetchRebaseAndPush ();
|
|
||||||
} else {
|
|
||||||
SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Changes pushed");
|
|
||||||
|
|
||||||
HasUnsyncedChanges = false;
|
|
||||||
|
|
||||||
if (SyncStatusChanged != null)
|
|
||||||
SyncStatusChanged (SyncStatus.Idle);
|
|
||||||
|
|
||||||
this.listener.Announce (this.revision);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void DisableWatching ()
|
|
||||||
{
|
|
||||||
this.watcher.EnableRaisingEvents = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void EnableWatching ()
|
|
||||||
{
|
|
||||||
this.watcher.EnableRaisingEvents = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Gets the repository's description
|
|
||||||
private string GetDescription ()
|
|
||||||
{
|
|
||||||
string description_file_path = SparkleHelpers.CombineMore (LocalPath, ".git", "description");
|
|
||||||
|
|
||||||
if (!File.Exists (description_file_path))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
StreamReader reader = new StreamReader (description_file_path);
|
|
||||||
string description = reader.ReadToEnd ();
|
|
||||||
reader.Close ();
|
|
||||||
|
|
||||||
if (description.StartsWith ("Unnamed"))
|
|
||||||
description = null;
|
|
||||||
|
|
||||||
return description;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private string GetUserName ()
|
|
||||||
{
|
|
||||||
SparkleGit git = new SparkleGit (LocalPath, "config --get user.name");
|
|
||||||
git.Start ();
|
|
||||||
git.WaitForExit ();
|
|
||||||
|
|
||||||
string output = git.StandardOutput.ReadToEnd ();
|
|
||||||
string user_name = output.Trim ();
|
|
||||||
|
|
||||||
return user_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private string GetUserEmail ()
|
|
||||||
{
|
|
||||||
SparkleGit git = new SparkleGit (LocalPath, "config --get user.email");
|
|
||||||
git.Start ();
|
|
||||||
git.WaitForExit ();
|
|
||||||
|
|
||||||
string output = git.StandardOutput.ReadToEnd ();
|
|
||||||
string user_email = output.Trim ();
|
|
||||||
|
|
||||||
return user_email;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Recursively gets a folder's size in bytes
|
|
||||||
private double CalculateFolderSize (DirectoryInfo parent)
|
|
||||||
{
|
|
||||||
if (!System.IO.Directory.Exists (parent.ToString ()))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
double size = 0;
|
|
||||||
|
|
||||||
// Ignore the temporary 'rebase-apply' directory. This prevents potential
|
|
||||||
// crashes when files are being queried whilst the files have already been deleted.
|
|
||||||
if (parent.Name.Equals ("rebase-apply"))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
foreach (FileInfo file in parent.GetFiles()) {
|
|
||||||
if (!file.Exists)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
size += file.Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (DirectoryInfo directory in parent.GetDirectories())
|
|
||||||
size += CalculateFolderSize (directory);
|
|
||||||
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Create an initial change set when the
|
|
||||||
// user has fetched an empty remote folder
|
|
||||||
protected virtual void CreateInitialChangeSet ()
|
|
||||||
{
|
|
||||||
string file_path = Path.Combine (LocalPath, "SparkleShare.txt");
|
|
||||||
TextWriter writer = new StreamWriter (file_path);
|
|
||||||
writer.WriteLine (":)");
|
|
||||||
writer.Close ();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public string GetConfigItem (string name)
|
|
||||||
{
|
|
||||||
if (String.Compare (name, "sparkleshare.user.name", true) == 0)
|
|
||||||
return GetUserName ();
|
|
||||||
else if (String.Compare (name, "sparkleshare.user.email", true) == 0)
|
|
||||||
return GetUserEmail ();
|
|
||||||
else
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Returns a list of the latest change sets
|
// Returns a list of the latest change sets
|
||||||
// TODO: Method needs to be made a lot faster
|
// TODO: Method needs to be made a lot faster
|
||||||
public virtual List <SparkleChangeSet> GetChangeSets (int count)
|
public override List <SparkleChangeSet> GetChangeSets (int count)
|
||||||
{
|
{
|
||||||
if (count < 1)
|
if (count < 1)
|
||||||
count = 30;
|
count = 30;
|
||||||
|
@ -987,13 +546,22 @@ namespace SparkleLib {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static bool IsRepo (string path)
|
public override void CreateInitialChangeSet ()
|
||||||
|
{
|
||||||
|
base.CreateInitialChangeSet ();
|
||||||
|
Add ();
|
||||||
|
|
||||||
|
string message = FormatCommitMessage ();
|
||||||
|
Commit (message);
|
||||||
|
}
|
||||||
|
|
||||||
|
new public static bool IsRepo (string path)
|
||||||
{
|
{
|
||||||
return System.IO.Directory.Exists (Path.Combine (path, ".git"));
|
return System.IO.Directory.Exists (Path.Combine (path, ".git"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public bool UsesNotificationCenter
|
public override bool UsesNotificationCenter
|
||||||
{
|
{
|
||||||
get {
|
get {
|
||||||
string file_path = SparkleHelpers.CombineMore (LocalPath, ".git", "disable_notification_center");
|
string file_path = SparkleHelpers.CombineMore (LocalPath, ".git", "disable_notification_center");
|
||||||
|
@ -1002,12 +570,5 @@ namespace SparkleLib {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Disposes all resourses of this object
|
|
||||||
public void Dispose ()
|
|
||||||
{
|
|
||||||
this.remote_timer.Dispose ();
|
|
||||||
this.local_timer.Dispose ();
|
|
||||||
this.listener.Dispose ();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -33,7 +33,7 @@ namespace SparkleShare {
|
||||||
|
|
||||||
public abstract class SparkleController {
|
public abstract class SparkleController {
|
||||||
|
|
||||||
public List <SparkleRepo> Repositories;
|
public List <SparkleRepoBase> Repositories;
|
||||||
public string FolderSize;
|
public string FolderSize;
|
||||||
public bool FirstRun;
|
public bool FirstRun;
|
||||||
public readonly string SparklePath;
|
public readonly string SparklePath;
|
||||||
|
@ -162,7 +162,7 @@ namespace SparkleShare {
|
||||||
// FIXME: this is broken :\
|
// FIXME: this is broken :\
|
||||||
if (OnInvitation != null)
|
if (OnInvitation != null)
|
||||||
OnInvitation (server, folder, token);
|
OnInvitation (server, folder, token);
|
||||||
} else if (SparkleRepo.IsRepo (args.FullPath)) {
|
} else if (SparkleRepoBase.IsRepo (args.FullPath)) {
|
||||||
AddRepository (args.FullPath);
|
AddRepository (args.FullPath);
|
||||||
|
|
||||||
if (FolderListChanged != null)
|
if (FolderListChanged != null)
|
||||||
|
@ -240,7 +240,7 @@ namespace SparkleShare {
|
||||||
get {
|
get {
|
||||||
List <string> folders = new List <string> ();
|
List <string> folders = new List <string> ();
|
||||||
|
|
||||||
foreach (SparkleRepo repo in Repositories)
|
foreach (SparkleRepoBase repo in Repositories)
|
||||||
folders.Add (repo.LocalPath);
|
folders.Add (repo.LocalPath);
|
||||||
|
|
||||||
return folders;
|
return folders;
|
||||||
|
@ -253,7 +253,7 @@ namespace SparkleShare {
|
||||||
string path = Path.Combine (SparklePaths.SparklePath, name);
|
string path = Path.Combine (SparklePaths.SparklePath, name);
|
||||||
int log_size = 30;
|
int log_size = 30;
|
||||||
|
|
||||||
foreach (SparkleRepo repo in Repositories) {
|
foreach (SparkleRepoBase repo in Repositories) {
|
||||||
if (repo.LocalPath.Equals (path))
|
if (repo.LocalPath.Equals (path))
|
||||||
return repo.GetChangeSets (log_size);
|
return repo.GetChangeSets (log_size);
|
||||||
}
|
}
|
||||||
|
@ -477,7 +477,7 @@ namespace SparkleShare {
|
||||||
// Fires events for the current syncing state
|
// Fires events for the current syncing state
|
||||||
private void UpdateState ()
|
private void UpdateState ()
|
||||||
{
|
{
|
||||||
foreach (SparkleRepo repo in Repositories) {
|
foreach (SparkleRepoBase repo in Repositories) {
|
||||||
if (repo.Status == SyncStatus.SyncDown ||
|
if (repo.Status == SyncStatus.SyncDown ||
|
||||||
repo.Status == SyncStatus.SyncUp ||
|
repo.Status == SyncStatus.SyncUp ||
|
||||||
repo.IsBuffering) {
|
repo.IsBuffering) {
|
||||||
|
@ -512,10 +512,11 @@ namespace SparkleShare {
|
||||||
// and use GitBackend.IsValidFolder (string path);
|
// and use GitBackend.IsValidFolder (string path);
|
||||||
|
|
||||||
// Check if the folder is a Git repository TODO: remove later
|
// Check if the folder is a Git repository TODO: remove later
|
||||||
if (!SparkleRepo.IsRepo (folder_path))
|
if (!SparkleRepoBase.IsRepo (folder_path))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SparkleRepo repo = new SparkleRepo (folder_path, SparkleBackend.DefaultBackend);
|
//TODO
|
||||||
|
SparkleRepoBase repo = new SparkleRepoGit (folder_path, SparkleBackend.DefaultBackend);
|
||||||
|
|
||||||
repo.NewChangeSet += delegate (SparkleChangeSet change_set, string repository_path) {
|
repo.NewChangeSet += delegate (SparkleChangeSet change_set, string repository_path) {
|
||||||
string message = FormatMessage (change_set);
|
string message = FormatMessage (change_set);
|
||||||
|
@ -554,7 +555,7 @@ namespace SparkleShare {
|
||||||
string folder_name = Path.GetFileName (folder_path);
|
string folder_name = Path.GetFileName (folder_path);
|
||||||
|
|
||||||
for (int i = 0; i < Repositories.Count; i++) {
|
for (int i = 0; i < Repositories.Count; i++) {
|
||||||
SparkleRepo repo = Repositories [i];
|
SparkleRepoBase repo = Repositories [i];
|
||||||
|
|
||||||
if (repo.Name.Equals (folder_name)) {
|
if (repo.Name.Equals (folder_name)) {
|
||||||
Repositories.Remove (repo);
|
Repositories.Remove (repo);
|
||||||
|
@ -570,7 +571,7 @@ namespace SparkleShare {
|
||||||
// folders in the SparkleShare folder
|
// folders in the SparkleShare folder
|
||||||
private void PopulateRepositories ()
|
private void PopulateRepositories ()
|
||||||
{
|
{
|
||||||
Repositories = new List <SparkleRepo> ();
|
Repositories = new List <SparkleRepoBase> ();
|
||||||
|
|
||||||
foreach (string folder_path in Directory.GetDirectories (SparklePaths.SparklePath))
|
foreach (string folder_path in Directory.GetDirectories (SparklePaths.SparklePath))
|
||||||
AddRepository (folder_path);
|
AddRepository (folder_path);
|
||||||
|
@ -1056,7 +1057,7 @@ namespace SparkleShare {
|
||||||
// quits if safe
|
// quits if safe
|
||||||
public void TryQuit ()
|
public void TryQuit ()
|
||||||
{
|
{
|
||||||
foreach (SparkleRepo repo in Repositories) {
|
foreach (SparkleRepoBase repo in Repositories) {
|
||||||
if (repo.Status == SyncStatus.SyncUp ||
|
if (repo.Status == SyncStatus.SyncUp ||
|
||||||
repo.Status == SyncStatus.SyncDown ||
|
repo.Status == SyncStatus.SyncDown ||
|
||||||
repo.IsBuffering) {
|
repo.IsBuffering) {
|
||||||
|
@ -1074,7 +1075,7 @@ namespace SparkleShare {
|
||||||
|
|
||||||
public void Quit ()
|
public void Quit ()
|
||||||
{
|
{
|
||||||
foreach (SparkleRepo repo in Repositories)
|
foreach (SparkleRepoBase repo in Repositories)
|
||||||
repo.Dispose ();
|
repo.Dispose ();
|
||||||
|
|
||||||
Environment.Exit (0);
|
Environment.Exit (0);
|
||||||
|
|
Loading…
Reference in a new issue