From 2168fa2ab9951c178008e8868ecdca625c2edf77 Mon Sep 17 00:00:00 2001 From: Hylke Bons Date: Thu, 19 May 2011 17:05:58 +0100 Subject: [PATCH] save work --- SparkleLib/Makefile.am | 4 +- SparkleLib/SparkleLib.csproj | 3 +- SparkleLib/SparkleRepoBase.cs | 479 ++++++++++++ .../{SparkleRepo.cs => SparkleRepoGit.cs} | 733 ++++-------------- SparkleShare/SparkleController.cs | 23 +- 5 files changed, 643 insertions(+), 599 deletions(-) create mode 100644 SparkleLib/SparkleRepoBase.cs rename SparkleLib/{SparkleRepo.cs => SparkleRepoGit.cs} (52%) diff --git a/SparkleLib/Makefile.am b/SparkleLib/Makefile.am index de9d8f1a..e0b2d050 100644 --- a/SparkleLib/Makefile.am +++ b/SparkleLib/Makefile.am @@ -15,7 +15,9 @@ SOURCES = \ SparkleListenerIrc.cs \ SparkleOptions.cs \ SparklePaths.cs \ - SparkleRepo.cs + SparkleRepoBase.cs \ + SparkleRepoGit.cs + SMARTIRC4NET_FILES_EXPANDED = $(foreach file, $(SMARTIRC4NET_FILES), $(top_builddir)/$(file)) diff --git a/SparkleLib/SparkleLib.csproj b/SparkleLib/SparkleLib.csproj index 9e69e0e1..e297a0b7 100644 --- a/SparkleLib/SparkleLib.csproj +++ b/SparkleLib/SparkleLib.csproj @@ -37,7 +37,8 @@ - + + diff --git a/SparkleLib/SparkleRepoBase.cs b/SparkleLib/SparkleRepoBase.cs new file mode 100644 index 00000000..bbe73d4a --- /dev/null +++ b/SparkleLib/SparkleRepoBase.cs @@ -0,0 +1,479 @@ +// SparkleShare, an instant update workflow to Git. +// Copyright (C) 2010 Hylke Bons +// +// 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 . + + +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 sizebuffer = new List (); + 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 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 (); + } + } +} diff --git a/SparkleLib/SparkleRepo.cs b/SparkleLib/SparkleRepoGit.cs similarity index 52% rename from SparkleLib/SparkleRepo.cs rename to SparkleLib/SparkleRepoGit.cs index e79a3ced..61cf9f03 100644 --- a/SparkleLib/SparkleRepo.cs +++ b/SparkleLib/SparkleRepoGit.cs @@ -20,91 +20,152 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Text.RegularExpressions; -using System.Timers; - -using Meebey.SmartIrc4net; -using Mono.Unix; namespace SparkleLib { - public enum SyncStatus { - Idle, - SyncUp, - SyncDown, - Error - } + public class SparkleRepoGit : SparkleRepoBase { + + public SparkleRepoGit (string path, SparkleBackend backend) : + base (path, backend) { } - public class SparkleRepo { - - 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 sizebuffer; - private bool has_changed = false; - private Object change_lock = new Object (); - - - // TODO: make this a regexp - public string Url { + public override string Url { get { SparkleGit git = new SparkleGit (LocalPath, "config --get remote.origin.url"); git.Start (); git.WaitForExit (); - + string output = git.StandardOutput.ReadToEnd (); return output.TrimEnd (); } } - // TODO: make this a regexp - public string Domain { - get { - string domain = Url.Substring (Url.IndexOf ("@") + 1); - if (domain.Contains (":")) - return domain = domain.Substring (0, domain.IndexOf (":")); - else - return domain = domain.Substring (0, domain.IndexOf ("/")); + public override 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); } } - public string RemoteName { + + public override string CurrentRevision { 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 { - get { - return this.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 bool IsBuffering { - get { - return this.is_buffering; + + 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 bool IsPolling { - get { - return this.is_polling; + + 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 bool HasUnsyncedChanges { + + public override 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; + } + } + + + + + + public override bool HasUnsyncedChanges { get { string unsynced_file_path = SparkleHelpers.CombineMore (LocalPath, ".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 (); - - // 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 @@ -477,59 +224,15 @@ namespace SparkleLib { git.Start (); git.WaitForExit (); - this.revision = GetRevision (); - SparkleHelpers.DebugInfo ("Commit", "[" + Name + "] " + message + " (" + this.revision + ")"); + SparkleHelpers.DebugInfo ("Commit", "[" + Name + "] " + message); // Collect garbage pseudo-randomly if (DateTime.Now.Second % 10 == 0) 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 @@ -539,7 +242,7 @@ namespace SparkleLib { if (AnyDifferences) { Add (); - + string commit_message = FormatCommitMessage (); Commit (commit_message); } @@ -559,21 +262,17 @@ namespace SparkleLib { SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Conflict resolved."); EnableWatching (); - if (ConflictResolved != null) - ConflictResolved (); + OnConflictResolved (); - Push (); + //TODO Push (); } - this.revision = GetRevision (); - - if (NewChangeSet != null) - NewChangeSet (GetChangeSets (1) [0], LocalPath); - EnableWatching (); } + + private void ResolveConflict () { // This is al list of conflict status codes that Git uses, their @@ -669,159 +368,19 @@ 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 // TODO: Method needs to be made a lot faster - public virtual List GetChangeSets (int count) + public override List GetChangeSets (int count) { if (count < 1) count = 30; - + List change_sets = new List (); SparkleGit git_log = new SparkleGit (LocalPath, "log -" + count + " --raw -M --date=iso"); Console.OutputEncoding = System.Text.Encoding.Unicode; git_log.Start (); - + // Reading the standard output HAS to go before // WaitForExit, or it will hang forever on output > 4096 bytes string output = git_log.StandardOutput.ReadToEnd (); @@ -836,14 +395,14 @@ namespace SparkleLib { if (line.StartsWith ("commit") && j > 0) { entries.Add (entry); entry = ""; - } - + } + entry += line + "\n"; j++; - + last_entry = entry; } - + entries.Add (last_entry); Regex merge_regex = new Regex (@"commit ([a-z0-9]{40})\n" + @@ -863,19 +422,19 @@ namespace SparkleLib { foreach (string log_entry in entries) { Regex regex; bool is_merge_commit = false; - + if (log_entry.Contains ("\nMerge: ")) { regex = merge_regex; is_merge_commit = true; } else { regex = non_merge_regex; } - + Match match = regex.Match (log_entry); if (match.Success) { SparkleChangeSet change_set = new SparkleChangeSet (); - + change_set.Revision = match.Groups [1].Value; change_set.UserName = match.Groups [2].Value; change_set.UserEmail = match.Groups [3].Value; @@ -885,16 +444,16 @@ namespace SparkleLib { int.Parse (match.Groups [5].Value), int.Parse (match.Groups [6].Value), int.Parse (match.Groups [7].Value), int.Parse (match.Groups [8].Value), int.Parse (match.Groups [9].Value)); - + string [] entry_lines = log_entry.Split ("\n".ToCharArray ()); - + foreach (string entry_line in entry_lines) { if (entry_line.StartsWith (":")) { - + string change_type = entry_line [37].ToString (); string file_path = entry_line.Substring (39); string to_file_path; - + if (change_type.Equals ("A")) { change_set.Added.Add (file_path); } else if (change_type.Equals ("M")) { @@ -911,7 +470,7 @@ namespace SparkleLib { } } } - + change_sets.Add (change_set); } } @@ -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")); } - public bool UsesNotificationCenter + public override bool UsesNotificationCenter { get { 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 (); - } } } diff --git a/SparkleShare/SparkleController.cs b/SparkleShare/SparkleController.cs index f402adc9..435bb169 100644 --- a/SparkleShare/SparkleController.cs +++ b/SparkleShare/SparkleController.cs @@ -33,7 +33,7 @@ namespace SparkleShare { public abstract class SparkleController { - public List Repositories; + public List Repositories; public string FolderSize; public bool FirstRun; public readonly string SparklePath; @@ -162,7 +162,7 @@ namespace SparkleShare { // FIXME: this is broken :\ if (OnInvitation != null) OnInvitation (server, folder, token); - } else if (SparkleRepo.IsRepo (args.FullPath)) { + } else if (SparkleRepoBase.IsRepo (args.FullPath)) { AddRepository (args.FullPath); if (FolderListChanged != null) @@ -240,7 +240,7 @@ namespace SparkleShare { get { List folders = new List (); - foreach (SparkleRepo repo in Repositories) + foreach (SparkleRepoBase repo in Repositories) folders.Add (repo.LocalPath); return folders; @@ -253,7 +253,7 @@ namespace SparkleShare { string path = Path.Combine (SparklePaths.SparklePath, name); int log_size = 30; - foreach (SparkleRepo repo in Repositories) { + foreach (SparkleRepoBase repo in Repositories) { if (repo.LocalPath.Equals (path)) return repo.GetChangeSets (log_size); } @@ -477,7 +477,7 @@ namespace SparkleShare { // Fires events for the current syncing state private void UpdateState () { - foreach (SparkleRepo repo in Repositories) { + foreach (SparkleRepoBase repo in Repositories) { if (repo.Status == SyncStatus.SyncDown || repo.Status == SyncStatus.SyncUp || repo.IsBuffering) { @@ -512,10 +512,11 @@ namespace SparkleShare { // and use GitBackend.IsValidFolder (string path); // Check if the folder is a Git repository TODO: remove later - if (!SparkleRepo.IsRepo (folder_path)) + if (!SparkleRepoBase.IsRepo (folder_path)) 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) { string message = FormatMessage (change_set); @@ -554,7 +555,7 @@ namespace SparkleShare { string folder_name = Path.GetFileName (folder_path); for (int i = 0; i < Repositories.Count; i++) { - SparkleRepo repo = Repositories [i]; + SparkleRepoBase repo = Repositories [i]; if (repo.Name.Equals (folder_name)) { Repositories.Remove (repo); @@ -570,7 +571,7 @@ namespace SparkleShare { // folders in the SparkleShare folder private void PopulateRepositories () { - Repositories = new List (); + Repositories = new List (); foreach (string folder_path in Directory.GetDirectories (SparklePaths.SparklePath)) AddRepository (folder_path); @@ -1056,7 +1057,7 @@ namespace SparkleShare { // quits if safe public void TryQuit () { - foreach (SparkleRepo repo in Repositories) { + foreach (SparkleRepoBase repo in Repositories) { if (repo.Status == SyncStatus.SyncUp || repo.Status == SyncStatus.SyncDown || repo.IsBuffering) { @@ -1074,7 +1075,7 @@ namespace SparkleShare { public void Quit () { - foreach (SparkleRepo repo in Repositories) + foreach (SparkleRepoBase repo in Repositories) repo.Dispose (); Environment.Exit (0);