2011-06-01 23:08:05 +00:00
|
|
|
|
// SparkleShare, a collaboration and sharing tool.
|
2010-06-20 21:05:11 +00:00
|
|
|
|
// 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
|
2010-11-21 12:33:24 +00:00
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
2010-06-20 21:05:11 +00:00
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
|
//
|
|
|
|
|
// You should have received a copy of the GNU General Public License
|
2010-07-22 21:10:38 +00:00
|
|
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2010-06-20 21:05:11 +00:00
|
|
|
|
|
2011-02-23 01:13:54 +00:00
|
|
|
|
|
2010-06-20 21:05:11 +00:00
|
|
|
|
using System;
|
2010-09-04 15:23:20 +00:00
|
|
|
|
using System.Collections.Generic;
|
2010-06-20 21:05:11 +00:00
|
|
|
|
using System.IO;
|
2012-07-01 08:46:42 +00:00
|
|
|
|
using System.Text;
|
2010-06-20 21:05:11 +00:00
|
|
|
|
using System.Text.RegularExpressions;
|
2012-07-03 07:58:35 +00:00
|
|
|
|
|
2012-02-28 13:34:15 +00:00
|
|
|
|
using SparkleLib;
|
2011-03-08 23:55:21 +00:00
|
|
|
|
|
2012-03-01 17:02:52 +00:00
|
|
|
|
namespace SparkleLib.Git {
|
2010-06-20 21:05:11 +00:00
|
|
|
|
|
2012-03-01 17:02:52 +00:00
|
|
|
|
public class SparkleRepo : SparkleRepoBase {
|
2011-05-18 18:12:45 +00:00
|
|
|
|
|
2012-06-29 16:08:12 +00:00
|
|
|
|
private bool user_is_set;
|
|
|
|
|
private bool remote_url_is_set;
|
2012-06-29 15:44:02 +00:00
|
|
|
|
private bool use_git_bin;
|
2012-06-24 18:14:52 +00:00
|
|
|
|
|
|
|
|
|
|
2012-03-01 17:02:52 +00:00
|
|
|
|
public SparkleRepo (string path) : base (path)
|
2012-01-29 20:33:12 +00:00
|
|
|
|
{
|
2012-06-29 15:44:02 +00:00
|
|
|
|
SparkleGit git = new SparkleGit (LocalPath, "config --get filter.bin.clean");
|
|
|
|
|
git.Start ();
|
|
|
|
|
git.WaitForExit ();
|
|
|
|
|
|
|
|
|
|
this.use_git_bin = (git.ExitCode == 0);
|
2012-01-29 20:33:12 +00:00
|
|
|
|
}
|
2011-05-18 18:12:45 +00:00
|
|
|
|
|
|
|
|
|
|
2011-12-29 11:44:18 +00:00
|
|
|
|
public override List<string> ExcludePaths {
|
|
|
|
|
get {
|
|
|
|
|
List<string> rules = new List<string> ();
|
2012-02-08 19:42:29 +00:00
|
|
|
|
rules.Add (".git");
|
2011-12-29 11:44:18 +00:00
|
|
|
|
|
|
|
|
|
return rules;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2011-12-15 15:15:29 +00:00
|
|
|
|
public override double Size {
|
|
|
|
|
get {
|
2012-01-24 13:46:57 +00:00
|
|
|
|
string file_path = new string [] {LocalPath, ".git", "repo_size"}.Combine ();
|
2012-01-18 23:21:09 +00:00
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
return double.Parse (File.ReadAllText (file_path));
|
|
|
|
|
|
|
|
|
|
} catch {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2011-12-15 15:15:29 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public override double HistorySize {
|
|
|
|
|
get {
|
2012-01-24 13:46:57 +00:00
|
|
|
|
string file_path = new string [] {LocalPath, ".git", "repo_history_size"}.Combine ();
|
2012-01-18 23:21:09 +00:00
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
return double.Parse (File.ReadAllText (file_path));
|
|
|
|
|
|
|
|
|
|
} catch {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2011-12-15 15:15:29 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2012-02-08 19:42:29 +00:00
|
|
|
|
private void UpdateSizes ()
|
2012-01-18 23:21:09 +00:00
|
|
|
|
{
|
2012-07-03 07:58:35 +00:00
|
|
|
|
double size = CalculateSizes (new DirectoryInfo (LocalPath));
|
|
|
|
|
double history_size = CalculateSizes (new DirectoryInfo (Path.Combine (LocalPath, ".git")));
|
2012-01-18 23:21:09 +00:00
|
|
|
|
|
2012-01-24 13:46:57 +00:00
|
|
|
|
string size_file_path = new string [] {LocalPath, ".git", "repo_size"}.Combine ();
|
|
|
|
|
string history_size_file_path = new string [] {LocalPath, ".git", "repo_history_size"}.Combine ();
|
2012-01-18 23:21:09 +00:00
|
|
|
|
|
|
|
|
|
File.WriteAllText (size_file_path, size.ToString ());
|
|
|
|
|
File.WriteAllText (history_size_file_path, history_size.ToString ());
|
|
|
|
|
}
|
2012-07-05 11:33:32 +00:00
|
|
|
|
|
2012-06-10 22:56:39 +00:00
|
|
|
|
|
2011-06-29 19:28:49 +00:00
|
|
|
|
public override string [] UnsyncedFilePaths {
|
|
|
|
|
get {
|
|
|
|
|
List<string> file_paths = new List<string> ();
|
|
|
|
|
|
|
|
|
|
SparkleGit git = new SparkleGit (LocalPath, "status --porcelain");
|
|
|
|
|
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 ().TrimEnd ();
|
|
|
|
|
git.WaitForExit ();
|
|
|
|
|
|
|
|
|
|
string [] lines = output.Split ("\n".ToCharArray ());
|
|
|
|
|
foreach (string line in lines) {
|
|
|
|
|
if (line [1].ToString ().Equals ("M") ||
|
|
|
|
|
line [1].ToString ().Equals ("?") ||
|
|
|
|
|
line [1].ToString ().Equals ("A")) {
|
|
|
|
|
|
|
|
|
|
string path = line.Substring (3);
|
2012-03-16 01:53:23 +00:00
|
|
|
|
path = path.Trim ("\"".ToCharArray ());
|
2011-06-29 19:28:49 +00:00
|
|
|
|
file_paths.Add (path);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return file_paths.ToArray ();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-30 21:22:48 +00:00
|
|
|
|
|
2011-05-19 16:05:58 +00:00
|
|
|
|
public override string CurrentRevision {
|
|
|
|
|
get {
|
|
|
|
|
// Remove stale rebase-apply files because it
|
|
|
|
|
// makes the method return the wrong hashes.
|
|
|
|
|
string rebase_apply_file = SparkleHelpers.CombineMore (LocalPath, ".git", "rebase-apply");
|
2012-06-10 22:56:39 +00:00
|
|
|
|
|
2011-05-19 16:05:58 +00:00
|
|
|
|
if (File.Exists (rebase_apply_file))
|
|
|
|
|
File.Delete (rebase_apply_file);
|
|
|
|
|
|
2012-06-07 13:29:27 +00:00
|
|
|
|
SparkleGit git = new SparkleGit (LocalPath, "rev-parse HEAD");
|
2011-05-19 16:05:58 +00:00
|
|
|
|
git.Start ();
|
2012-06-10 22:56:39 +00:00
|
|
|
|
|
|
|
|
|
string output = git.StandardOutput.ReadToEnd ();
|
2011-05-19 16:05:58 +00:00
|
|
|
|
git.WaitForExit ();
|
|
|
|
|
|
|
|
|
|
if (git.ExitCode == 0) {
|
|
|
|
|
return output.TrimEnd ();
|
2011-08-25 15:02:34 +00:00
|
|
|
|
|
2011-05-19 16:05:58 +00:00
|
|
|
|
} else {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2012-05-19 15:15:20 +00:00
|
|
|
|
public override bool HasRemoteChanges {
|
2012-02-06 14:02:41 +00:00
|
|
|
|
get {
|
2012-06-21 16:58:07 +00:00
|
|
|
|
SparkleHelpers.DebugInfo ("Git", Name + " | Checking for remote changes...");
|
2012-06-09 15:27:34 +00:00
|
|
|
|
|
|
|
|
|
string current_revision = CurrentRevision;
|
2012-06-29 15:44:02 +00:00
|
|
|
|
SparkleGit git = new SparkleGit (LocalPath, "ls-remote --heads --exit-code \"" + RemoteUrl + "\" master");
|
2012-02-06 14:02:41 +00:00
|
|
|
|
|
|
|
|
|
git.Start ();
|
|
|
|
|
git.WaitForExit ();
|
|
|
|
|
|
|
|
|
|
if (git.ExitCode != 0)
|
|
|
|
|
return false;
|
|
|
|
|
|
2012-07-03 07:58:35 +00:00
|
|
|
|
string output = git.StandardOutput.ReadToEnd ();
|
|
|
|
|
string remote_revision = output.Substring (0, 40);
|
|
|
|
|
|
2012-06-09 15:27:34 +00:00
|
|
|
|
if (!remote_revision.StartsWith (current_revision)) {
|
2012-02-06 14:02:41 +00:00
|
|
|
|
SparkleHelpers.DebugInfo ("Git",
|
2012-06-21 16:58:07 +00:00
|
|
|
|
Name + " | Remote changes detected (local: " +
|
2012-06-09 15:27:34 +00:00
|
|
|
|
current_revision + ", remote: " + remote_revision + ")");
|
2012-02-06 14:02:41 +00:00
|
|
|
|
|
|
|
|
|
return true;
|
2011-08-25 15:02:34 +00:00
|
|
|
|
|
2012-02-06 14:02:41 +00:00
|
|
|
|
} else {
|
2012-06-09 15:27:34 +00:00
|
|
|
|
SparkleHelpers.DebugInfo ("Git",
|
2012-06-21 16:58:07 +00:00
|
|
|
|
Name + " | No remote changes detected (local+remote: " + current_revision + ")");
|
2012-06-09 15:27:34 +00:00
|
|
|
|
|
2012-02-06 14:02:41 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2011-04-20 14:02:20 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2011-02-23 00:09:44 +00:00
|
|
|
|
|
2010-07-24 14:03:58 +00:00
|
|
|
|
|
2011-05-19 16:05:58 +00:00
|
|
|
|
public override bool SyncUp ()
|
2011-04-20 14:02:20 +00:00
|
|
|
|
{
|
2012-02-08 19:42:29 +00:00
|
|
|
|
if (HasLocalChanges) {
|
2011-12-03 11:40:03 +00:00
|
|
|
|
Add ();
|
2011-08-26 19:00:22 +00:00
|
|
|
|
|
2011-12-03 11:40:03 +00:00
|
|
|
|
string message = FormatCommitMessage ();
|
|
|
|
|
Commit (message);
|
|
|
|
|
}
|
2011-04-20 14:02:20 +00:00
|
|
|
|
|
2012-06-29 16:08:12 +00:00
|
|
|
|
SparkleGit git;
|
|
|
|
|
|
2012-06-29 15:44:02 +00:00
|
|
|
|
if (this.use_git_bin) {
|
2012-06-29 16:08:12 +00:00
|
|
|
|
if (this.remote_url_is_set) {
|
|
|
|
|
git = new SparkleGit (LocalPath, "config remote.origin.url \"" + RemoteUrl + "\"");
|
|
|
|
|
git.Start ();
|
|
|
|
|
git.WaitForExit ();
|
|
|
|
|
|
|
|
|
|
this.remote_url_is_set = true;
|
|
|
|
|
}
|
|
|
|
|
|
2012-06-29 15:44:02 +00:00
|
|
|
|
SparkleGitBin git_bin = new SparkleGitBin (LocalPath, "push");
|
2012-06-28 23:12:37 +00:00
|
|
|
|
git_bin.Start ();
|
|
|
|
|
git_bin.WaitForExit ();
|
|
|
|
|
|
2012-06-29 15:44:02 +00:00
|
|
|
|
// TODO: Progress
|
|
|
|
|
}
|
2012-06-28 23:12:37 +00:00
|
|
|
|
|
2012-06-29 16:08:12 +00:00
|
|
|
|
git = new SparkleGit (LocalPath,
|
2011-12-30 00:44:35 +00:00
|
|
|
|
"push --progress " + // Redirects progress stats to standarderror
|
2012-04-29 14:19:36 +00:00
|
|
|
|
"\"" + RemoteUrl + "\" master");
|
2011-12-30 00:44:35 +00:00
|
|
|
|
|
|
|
|
|
git.StartInfo.RedirectStandardError = true;
|
2011-05-19 16:05:58 +00:00
|
|
|
|
git.Start ();
|
2011-12-30 00:44:35 +00:00
|
|
|
|
|
|
|
|
|
double percentage = 1.0;
|
|
|
|
|
Regex progress_regex = new Regex (@"([0-9]+)%", RegexOptions.Compiled);
|
|
|
|
|
|
|
|
|
|
while (!git.StandardError.EndOfStream) {
|
|
|
|
|
string line = git.StandardError.ReadLine ();
|
|
|
|
|
Match match = progress_regex.Match (line);
|
|
|
|
|
string speed = "";
|
|
|
|
|
double number = 0.0;
|
|
|
|
|
|
|
|
|
|
if (match.Success) {
|
|
|
|
|
number = double.Parse (match.Groups [1].Value);
|
|
|
|
|
|
2011-12-30 14:00:15 +00:00
|
|
|
|
// The pushing progress consists of two stages: the "Compressing
|
2011-12-30 00:44:35 +00:00
|
|
|
|
// objects" stage which we count as 20% of the total progress, and
|
|
|
|
|
// the "Writing objects" stage which we count as the last 80%
|
|
|
|
|
if (line.StartsWith ("Compressing")) {
|
|
|
|
|
// "Compressing objects" stage
|
|
|
|
|
number = (number / 100 * 20);
|
|
|
|
|
|
|
|
|
|
} else {
|
2012-04-04 22:45:20 +00:00
|
|
|
|
if (line.StartsWith ("ERROR: QUOTA EXCEEDED")) {
|
|
|
|
|
int quota_limit = int.Parse (line.Substring (21).Trim ());
|
|
|
|
|
throw new QuotaExceededException ("Quota exceeded", quota_limit);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2011-12-30 00:44:35 +00:00
|
|
|
|
// "Writing objects" stage
|
|
|
|
|
number = (number / 100 * 80 + 20);
|
|
|
|
|
|
|
|
|
|
if (line.Contains ("|")) {
|
|
|
|
|
speed = line.Substring (line.IndexOf ("|") + 1).Trim ();
|
|
|
|
|
speed = speed.Replace (", done.", "").Trim ();
|
|
|
|
|
speed = speed.Replace ("i", "");
|
|
|
|
|
speed = speed.Replace ("KB/s", "ᴋʙ/s");
|
|
|
|
|
speed = speed.Replace ("MB/s", "ᴍʙ/s");
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-04-17 09:05:47 +00:00
|
|
|
|
|
|
|
|
|
} else {
|
2012-06-21 16:58:07 +00:00
|
|
|
|
SparkleHelpers.DebugInfo ("Git", Name + " | " + line);
|
2011-12-30 00:44:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (number >= percentage) {
|
|
|
|
|
percentage = number;
|
2012-02-09 01:46:25 +00:00
|
|
|
|
base.OnProgressChanged (percentage, speed);
|
2011-12-30 00:44:35 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-19 16:05:58 +00:00
|
|
|
|
git.WaitForExit ();
|
2011-04-20 14:02:20 +00:00
|
|
|
|
|
2012-02-08 19:42:29 +00:00
|
|
|
|
UpdateSizes ();
|
2012-04-29 14:19:36 +00:00
|
|
|
|
ChangeSets = GetChangeSets ();
|
2011-12-30 00:44:35 +00:00
|
|
|
|
|
2012-06-29 20:43:49 +00:00
|
|
|
|
if (git.ExitCode == 0) {
|
|
|
|
|
ClearCache ();
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2011-04-20 14:02:20 +00:00
|
|
|
|
}
|
2010-08-28 18:56:19 +00:00
|
|
|
|
|
|
|
|
|
|
2011-05-19 16:05:58 +00:00
|
|
|
|
public override bool SyncDown ()
|
2011-04-20 14:02:20 +00:00
|
|
|
|
{
|
2012-04-29 14:19:36 +00:00
|
|
|
|
SparkleGit git = new SparkleGit (LocalPath, "fetch --progress \"" + RemoteUrl + "\" master");
|
2011-12-30 14:00:15 +00:00
|
|
|
|
|
|
|
|
|
git.StartInfo.RedirectStandardError = true;
|
2011-05-19 16:05:58 +00:00
|
|
|
|
git.Start ();
|
2011-12-30 14:00:15 +00:00
|
|
|
|
|
|
|
|
|
double percentage = 1.0;
|
|
|
|
|
Regex progress_regex = new Regex (@"([0-9]+)%", RegexOptions.Compiled);
|
|
|
|
|
|
|
|
|
|
while (!git.StandardError.EndOfStream) {
|
|
|
|
|
string line = git.StandardError.ReadLine ();
|
|
|
|
|
Match match = progress_regex.Match (line);
|
|
|
|
|
string speed = "";
|
|
|
|
|
double number = 0.0;
|
|
|
|
|
|
|
|
|
|
if (match.Success) {
|
|
|
|
|
number = double.Parse (match.Groups [1].Value);
|
|
|
|
|
|
|
|
|
|
// The fetching progress consists of two stages: the "Compressing
|
|
|
|
|
// objects" stage which we count as 20% of the total progress, and
|
|
|
|
|
// the "Receiving objects" stage which we count as the last 80%
|
|
|
|
|
if (line.StartsWith ("Compressing")) {
|
|
|
|
|
// "Compressing objects" stage
|
|
|
|
|
number = (number / 100 * 20);
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
// "Writing objects" stage
|
|
|
|
|
number = (number / 100 * 80 + 20);
|
|
|
|
|
|
|
|
|
|
if (line.Contains ("|")) {
|
|
|
|
|
speed = line.Substring (line.IndexOf ("|") + 1).Trim ();
|
|
|
|
|
speed = speed.Replace (", done.", "").Trim ();
|
|
|
|
|
speed = speed.Replace ("i", "");
|
|
|
|
|
speed = speed.Replace ("KB/s", "ᴋʙ/s");
|
|
|
|
|
speed = speed.Replace ("MB/s", "ᴍʙ/s");
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-04-17 09:05:47 +00:00
|
|
|
|
|
|
|
|
|
} else {
|
2012-06-21 16:58:07 +00:00
|
|
|
|
SparkleHelpers.DebugInfo ("Git", Name + " | " + line);
|
2011-12-30 14:00:15 +00:00
|
|
|
|
}
|
2012-04-17 09:05:47 +00:00
|
|
|
|
|
2011-12-30 14:00:15 +00:00
|
|
|
|
|
|
|
|
|
if (number >= percentage) {
|
|
|
|
|
percentage = number;
|
2012-02-09 01:46:25 +00:00
|
|
|
|
base.OnProgressChanged (percentage, speed);
|
2011-12-30 14:00:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-19 16:05:58 +00:00
|
|
|
|
git.WaitForExit ();
|
2010-07-20 21:21:37 +00:00
|
|
|
|
|
2012-02-08 19:42:29 +00:00
|
|
|
|
UpdateSizes ();
|
2011-12-30 14:00:15 +00:00
|
|
|
|
|
2011-05-22 14:46:15 +00:00
|
|
|
|
if (git.ExitCode == 0) {
|
2011-05-19 16:05:58 +00:00
|
|
|
|
Rebase ();
|
2012-03-25 23:41:52 +00:00
|
|
|
|
|
|
|
|
|
File.SetAttributes (
|
|
|
|
|
Path.Combine (LocalPath, ".sparkleshare"),
|
|
|
|
|
FileAttributes.Hidden
|
|
|
|
|
);
|
2012-04-29 14:19:36 +00:00
|
|
|
|
|
|
|
|
|
ChangeSets = GetChangeSets ();
|
2012-06-29 20:43:49 +00:00
|
|
|
|
ClearCache ();
|
2012-04-29 14:19:36 +00:00
|
|
|
|
|
2012-03-25 23:41:52 +00:00
|
|
|
|
return true;
|
2011-08-25 15:02:34 +00:00
|
|
|
|
|
2011-05-19 16:05:58 +00:00
|
|
|
|
} else {
|
2012-04-29 14:19:36 +00:00
|
|
|
|
ChangeSets = GetChangeSets ();
|
2011-05-19 16:05:58 +00:00
|
|
|
|
return false;
|
2011-04-20 14:02:20 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2011-04-15 00:28:42 +00:00
|
|
|
|
|
|
|
|
|
|
2012-02-08 19:42:29 +00:00
|
|
|
|
public override bool HasLocalChanges {
|
2011-04-20 14:02:20 +00:00
|
|
|
|
get {
|
2012-01-16 19:58:25 +00:00
|
|
|
|
PrepareDirectories (LocalPath);
|
2011-09-14 17:30:17 +00:00
|
|
|
|
|
2011-04-20 14:02:20 +00:00
|
|
|
|
SparkleGit git = new SparkleGit (LocalPath, "status --porcelain");
|
|
|
|
|
git.Start ();
|
|
|
|
|
|
2012-07-02 21:58:37 +00:00
|
|
|
|
string output = git.StandardOutput.ReadToEnd ().Trim ();
|
2011-06-29 19:45:37 +00:00
|
|
|
|
git.WaitForExit ();
|
|
|
|
|
|
2012-07-02 21:58:37 +00:00
|
|
|
|
return !string.IsNullOrEmpty (output);
|
2011-04-20 14:02:20 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2010-07-20 21:21:37 +00:00
|
|
|
|
|
|
|
|
|
|
2011-05-19 16:05:58 +00:00
|
|
|
|
public override bool HasUnsyncedChanges {
|
2011-04-21 21:14:44 +00:00
|
|
|
|
get {
|
2011-05-19 16:05:58 +00:00
|
|
|
|
string unsynced_file_path = SparkleHelpers.CombineMore (LocalPath,
|
|
|
|
|
".git", "has_unsynced_changes");
|
|
|
|
|
|
|
|
|
|
return File.Exists (unsynced_file_path);
|
|
|
|
|
}
|
2011-04-21 21:14:44 +00:00
|
|
|
|
|
2011-05-19 16:05:58 +00:00
|
|
|
|
set {
|
|
|
|
|
string unsynced_file_path = SparkleHelpers.CombineMore (LocalPath,
|
|
|
|
|
".git", "has_unsynced_changes");
|
|
|
|
|
|
|
|
|
|
if (value) {
|
|
|
|
|
if (!File.Exists (unsynced_file_path))
|
2011-09-14 22:58:19 +00:00
|
|
|
|
File.Create (unsynced_file_path).Close ();
|
2011-12-30 00:44:35 +00:00
|
|
|
|
|
2011-05-19 16:05:58 +00:00
|
|
|
|
} else {
|
|
|
|
|
File.Delete (unsynced_file_path);
|
|
|
|
|
}
|
2011-04-21 21:14:44 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2011-04-20 14:02:20 +00:00
|
|
|
|
// Stages the made changes
|
|
|
|
|
private void Add ()
|
|
|
|
|
{
|
|
|
|
|
SparkleGit git = new SparkleGit (LocalPath, "add --all");
|
2011-04-16 23:30:57 +00:00
|
|
|
|
git.Start ();
|
|
|
|
|
git.WaitForExit ();
|
|
|
|
|
|
2012-06-21 16:58:07 +00:00
|
|
|
|
SparkleHelpers.DebugInfo ("Git", Name + " | Changes staged");
|
2011-04-20 14:02:20 +00:00
|
|
|
|
}
|
2010-07-22 21:10:38 +00:00
|
|
|
|
|
2010-07-20 21:21:37 +00:00
|
|
|
|
|
2011-04-20 14:02:20 +00:00
|
|
|
|
// Commits the made changes
|
2011-05-18 22:18:11 +00:00
|
|
|
|
private void Commit (string message)
|
2012-06-24 18:14:52 +00:00
|
|
|
|
{
|
2012-06-24 22:20:45 +00:00
|
|
|
|
SparkleGit git;
|
|
|
|
|
|
2012-06-29 16:08:12 +00:00
|
|
|
|
if (!this.user_is_set) {
|
2012-06-24 22:20:45 +00:00
|
|
|
|
git = new SparkleGit (LocalPath,
|
2012-06-24 18:14:52 +00:00
|
|
|
|
"config user.name \"" + SparkleConfig.DefaultConfig.User.Name + "\"");
|
|
|
|
|
|
2012-06-24 22:20:45 +00:00
|
|
|
|
git.Start ();
|
|
|
|
|
git.WaitForExit ();
|
2012-06-24 18:14:52 +00:00
|
|
|
|
|
2012-06-24 22:20:45 +00:00
|
|
|
|
git = new SparkleGit (LocalPath,
|
2012-06-24 18:14:52 +00:00
|
|
|
|
"config user.email \"" + SparkleConfig.DefaultConfig.User.Email + "\"");
|
|
|
|
|
|
2012-06-24 22:20:45 +00:00
|
|
|
|
git.Start ();
|
|
|
|
|
git.WaitForExit ();
|
2012-06-24 18:14:52 +00:00
|
|
|
|
|
2012-06-29 16:08:12 +00:00
|
|
|
|
this.user_is_set = true;
|
2012-06-24 18:14:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-06-24 22:20:45 +00:00
|
|
|
|
git = new SparkleGit (LocalPath,
|
2012-07-01 11:30:33 +00:00
|
|
|
|
"commit --all --message=\"" + message + "\" " +
|
2011-10-17 17:34:17 +00:00
|
|
|
|
"--author=\"" + SparkleConfig.DefaultConfig.User.Name +
|
|
|
|
|
" <" + SparkleConfig.DefaultConfig.User.Email + ">\"");
|
|
|
|
|
|
2011-04-20 14:02:20 +00:00
|
|
|
|
git.Start ();
|
2011-07-20 23:02:21 +00:00
|
|
|
|
git.StandardOutput.ReadToEnd ();
|
2011-04-20 14:02:20 +00:00
|
|
|
|
git.WaitForExit ();
|
|
|
|
|
}
|
2010-10-07 21:43:08 +00:00
|
|
|
|
|
2011-02-26 14:20:32 +00:00
|
|
|
|
|
2011-04-20 14:02:20 +00:00
|
|
|
|
// Merges the fetched changes
|
2011-05-18 22:18:11 +00:00
|
|
|
|
private void Rebase ()
|
2011-04-20 14:02:20 +00:00
|
|
|
|
{
|
2012-02-08 19:42:29 +00:00
|
|
|
|
if (HasLocalChanges) {
|
2011-04-20 14:02:20 +00:00
|
|
|
|
Add ();
|
2011-05-19 16:05:58 +00:00
|
|
|
|
|
2011-04-20 14:02:20 +00:00
|
|
|
|
string commit_message = FormatCommitMessage ();
|
|
|
|
|
Commit (commit_message);
|
|
|
|
|
}
|
|
|
|
|
|
2012-03-28 22:11:41 +00:00
|
|
|
|
SparkleGit git = new SparkleGit (LocalPath, "rebase FETCH_HEAD");
|
|
|
|
|
git.StartInfo.RedirectStandardOutput = false;
|
2011-05-18 18:57:52 +00:00
|
|
|
|
git.Start ();
|
|
|
|
|
git.WaitForExit ();
|
2010-07-20 21:21:37 +00:00
|
|
|
|
|
2011-05-18 18:57:52 +00:00
|
|
|
|
if (git.ExitCode != 0) {
|
2012-06-21 16:58:07 +00:00
|
|
|
|
SparkleHelpers.DebugInfo ("Git", Name + " | Conflict detected, trying to get out...");
|
2011-02-26 14:20:32 +00:00
|
|
|
|
|
2012-07-02 21:42:49 +00:00
|
|
|
|
while (HasLocalChanges) {
|
|
|
|
|
try {
|
|
|
|
|
ResolveConflict ();
|
|
|
|
|
|
|
|
|
|
} catch (IOException e) {
|
2012-07-02 21:58:37 +00:00
|
|
|
|
SparkleHelpers.DebugInfo ("Git",
|
|
|
|
|
Name + " | Failed to resolve conflict, trying again... (" + e.Message + ")");
|
2012-07-02 21:42:49 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2011-05-14 02:18:38 +00:00
|
|
|
|
|
2012-06-21 16:58:07 +00:00
|
|
|
|
SparkleHelpers.DebugInfo ("Git", Name + " | Conflict resolved");
|
2011-05-19 16:05:58 +00:00
|
|
|
|
OnConflictResolved ();
|
2011-05-18 18:57:52 +00:00
|
|
|
|
}
|
2011-04-20 14:02:20 +00:00
|
|
|
|
}
|
2010-06-20 21:05:11 +00:00
|
|
|
|
|
2010-07-21 23:17:20 +00:00
|
|
|
|
|
2011-04-28 11:49:14 +00:00
|
|
|
|
private void ResolveConflict ()
|
|
|
|
|
{
|
2012-04-29 14:19:36 +00:00
|
|
|
|
// This is a list of conflict status codes that Git uses, their
|
2011-04-30 00:42:48 +00:00
|
|
|
|
// meaning, and how SparkleShare should handle them.
|
|
|
|
|
//
|
|
|
|
|
// DD unmerged, both deleted -> Do nothing
|
2012-07-01 11:30:33 +00:00
|
|
|
|
// AU unmerged, added by us -> Use server's, save ours as a timestamped copy
|
2011-04-30 00:42:48 +00:00
|
|
|
|
// UD unmerged, deleted by them -> Use ours
|
2012-07-01 11:30:33 +00:00
|
|
|
|
// UA unmerged, added by them -> Use server's, save ours as a timestamped copy
|
|
|
|
|
// DU unmerged, deleted by us -> Use server's
|
|
|
|
|
// AA unmerged, both added -> Use server's, save ours as a timestamped copy
|
|
|
|
|
// UU unmerged, both modified -> Use server's, save ours as a timestamped copy
|
2012-01-12 00:59:05 +00:00
|
|
|
|
// ?? unmerged, new files -> Stage the new files
|
2011-04-30 00:42:48 +00:00
|
|
|
|
//
|
|
|
|
|
// Note that a rebase merge works by replaying each commit from the working branch on
|
|
|
|
|
// top of the upstream branch. Because of this, when a merge conflict happens the
|
|
|
|
|
// side reported as 'ours' is the so-far rebased series, starting with upstream,
|
|
|
|
|
// and 'theirs' is the working branch. In other words, the sides are swapped.
|
|
|
|
|
//
|
2012-07-01 11:30:33 +00:00
|
|
|
|
// So: 'ours' means the 'server's version' and 'theirs' means the 'local version' after this comment
|
2011-04-30 00:42:48 +00:00
|
|
|
|
|
2011-04-28 11:49:14 +00:00
|
|
|
|
SparkleGit git_status = new SparkleGit (LocalPath, "status --porcelain");
|
|
|
|
|
git_status.Start ();
|
|
|
|
|
|
2011-06-29 19:45:37 +00:00
|
|
|
|
// Reading the standard output HAS to go before
|
|
|
|
|
// WaitForExit, or it will hang forever on output > 4096 bytes
|
|
|
|
|
string output = git_status.StandardOutput.ReadToEnd ().TrimEnd ();
|
2011-06-29 21:40:34 +00:00
|
|
|
|
git_status.WaitForExit ();
|
2011-06-29 19:45:37 +00:00
|
|
|
|
|
2011-04-28 11:49:14 +00:00
|
|
|
|
string [] lines = output.Split ("\n".ToCharArray ());
|
2012-07-02 21:10:03 +00:00
|
|
|
|
bool changes_added = false;
|
2011-04-28 11:49:14 +00:00
|
|
|
|
|
|
|
|
|
foreach (string line in lines) {
|
2011-04-30 00:42:48 +00:00
|
|
|
|
string conflicting_path = line.Substring (3);
|
2012-07-01 08:46:42 +00:00
|
|
|
|
conflicting_path = EnsureSpecialCharacters (conflicting_path);
|
2011-04-30 00:42:48 +00:00
|
|
|
|
|
2012-06-21 16:58:07 +00:00
|
|
|
|
SparkleHelpers.DebugInfo ("Git", Name + " | Conflict type: " + line);
|
2011-05-06 10:31:30 +00:00
|
|
|
|
|
2012-06-26 23:13:17 +00:00
|
|
|
|
// Ignore conflicts in the .sparkleshare file and use the local version
|
2012-06-28 13:21:06 +00:00
|
|
|
|
if (conflicting_path.EndsWith (".sparkleshare") ||
|
|
|
|
|
conflicting_path.EndsWith (".empty")) {
|
|
|
|
|
|
2012-06-26 23:13:17 +00:00
|
|
|
|
// Recover local version
|
|
|
|
|
SparkleGit git_theirs = new SparkleGit (LocalPath,
|
|
|
|
|
"checkout --theirs \"" + conflicting_path + "\"");
|
|
|
|
|
|
|
|
|
|
git_theirs.Start ();
|
|
|
|
|
git_theirs.WaitForExit ();
|
|
|
|
|
|
2012-06-28 13:21:06 +00:00
|
|
|
|
File.SetAttributes (Path.Combine (LocalPath, conflicting_path), FileAttributes.Hidden);
|
2012-07-02 21:10:03 +00:00
|
|
|
|
changes_added = true;
|
2012-06-28 13:21:06 +00:00
|
|
|
|
|
2012-06-26 23:13:17 +00:00
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-30 00:42:48 +00:00
|
|
|
|
// Both the local and server version have been modified
|
|
|
|
|
if (line.StartsWith ("UU") || line.StartsWith ("AA") ||
|
|
|
|
|
line.StartsWith ("AU") || line.StartsWith ("UA")) {
|
|
|
|
|
|
|
|
|
|
// Recover local version
|
|
|
|
|
SparkleGit git_theirs = new SparkleGit (LocalPath,
|
|
|
|
|
"checkout --theirs \"" + conflicting_path + "\"");
|
2012-06-26 23:13:17 +00:00
|
|
|
|
|
2011-04-30 00:42:48 +00:00
|
|
|
|
git_theirs.Start ();
|
|
|
|
|
git_theirs.WaitForExit ();
|
2011-04-28 11:49:14 +00:00
|
|
|
|
|
2011-08-27 00:42:09 +00:00
|
|
|
|
// Append a timestamp to local version.
|
|
|
|
|
// Windows doesn't allow colons in the file name, so
|
|
|
|
|
// we use "h" between the hours and minutes instead.
|
2012-06-26 23:13:17 +00:00
|
|
|
|
string timestamp = DateTime.Now.ToString ("MMM d H\\hmm");
|
|
|
|
|
string their_path = Path.GetFileNameWithoutExtension (conflicting_path) +
|
|
|
|
|
" (" + SparkleConfig.DefaultConfig.User.Name + ", " + timestamp + ")" +
|
|
|
|
|
Path.GetExtension (conflicting_path);
|
2012-07-01 11:30:33 +00:00
|
|
|
|
|
2011-04-30 00:42:48 +00:00
|
|
|
|
string abs_conflicting_path = Path.Combine (LocalPath, conflicting_path);
|
|
|
|
|
string abs_their_path = Path.Combine (LocalPath, their_path);
|
2011-04-28 11:49:14 +00:00
|
|
|
|
|
2011-04-30 00:42:48 +00:00
|
|
|
|
File.Move (abs_conflicting_path, abs_their_path);
|
|
|
|
|
|
|
|
|
|
// Recover server version
|
2011-04-28 11:49:14 +00:00
|
|
|
|
SparkleGit git_ours = new SparkleGit (LocalPath,
|
2011-04-30 00:42:48 +00:00
|
|
|
|
"checkout --ours \"" + conflicting_path + "\"");
|
2012-06-26 23:13:17 +00:00
|
|
|
|
|
2011-04-28 11:49:14 +00:00
|
|
|
|
git_ours.Start ();
|
|
|
|
|
git_ours.WaitForExit ();
|
|
|
|
|
|
2012-07-02 21:10:03 +00:00
|
|
|
|
changes_added = true;
|
|
|
|
|
|
2011-04-30 00:42:48 +00:00
|
|
|
|
// The local version has been modified, but the server version was removed
|
2012-04-22 12:32:55 +00:00
|
|
|
|
} else if (line.StartsWith ("DU")) {
|
2011-04-30 00:42:48 +00:00
|
|
|
|
|
|
|
|
|
// The modified local version is already in the
|
|
|
|
|
// checkout, so it just needs to be added.
|
|
|
|
|
//
|
|
|
|
|
// We need to specifically mention the file, so
|
|
|
|
|
// we can't reuse the Add () method
|
|
|
|
|
SparkleGit git_add = new SparkleGit (LocalPath,
|
2011-10-01 21:15:46 +00:00
|
|
|
|
"add \"" + conflicting_path + "\"");
|
2012-06-26 23:13:17 +00:00
|
|
|
|
|
2011-04-30 00:42:48 +00:00
|
|
|
|
git_add.Start ();
|
|
|
|
|
git_add.WaitForExit ();
|
|
|
|
|
|
2012-07-02 21:10:03 +00:00
|
|
|
|
changes_added = true;
|
2012-01-12 00:59:05 +00:00
|
|
|
|
}
|
2011-04-28 11:49:14 +00:00
|
|
|
|
}
|
2012-07-02 21:10:03 +00:00
|
|
|
|
|
|
|
|
|
Add ();
|
|
|
|
|
SparkleGit git;
|
|
|
|
|
|
|
|
|
|
if (changes_added)
|
|
|
|
|
git = new SparkleGit (LocalPath, "rebase --continue");
|
|
|
|
|
else
|
|
|
|
|
git = new SparkleGit (LocalPath, "rebase --skip");
|
|
|
|
|
|
|
|
|
|
git.StartInfo.RedirectStandardOutput = false;
|
|
|
|
|
git.Start ();
|
|
|
|
|
git.WaitForExit ();
|
2011-04-28 11:49:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2011-05-18 18:12:45 +00:00
|
|
|
|
// Returns a list of the latest change sets
|
2012-04-29 14:19:36 +00:00
|
|
|
|
public override List<SparkleChangeSet> GetChangeSets (int count)
|
2011-04-20 14:02:20 +00:00
|
|
|
|
{
|
2011-07-23 20:34:04 +00:00
|
|
|
|
if (count < 1)
|
|
|
|
|
count = 30;
|
2011-05-19 16:05:58 +00:00
|
|
|
|
|
2012-05-27 23:14:00 +00:00
|
|
|
|
count = 150;
|
|
|
|
|
|
2011-05-16 23:49:01 +00:00
|
|
|
|
List <SparkleChangeSet> change_sets = new List <SparkleChangeSet> ();
|
2011-04-20 14:02:20 +00:00
|
|
|
|
|
2012-03-20 17:20:28 +00:00
|
|
|
|
SparkleGit git_log = new SparkleGit (LocalPath,
|
2012-07-02 22:08:34 +00:00
|
|
|
|
"log -" + count + " --raw --find-renames --date=iso --format=medium --no-color --no-merges");
|
2012-05-27 18:24:12 +00:00
|
|
|
|
|
2011-04-20 14:02:20 +00:00
|
|
|
|
git_log.Start ();
|
2011-05-19 16:05:58 +00:00
|
|
|
|
|
2011-04-20 14:02:20 +00:00
|
|
|
|
// Reading the standard output HAS to go before
|
|
|
|
|
// WaitForExit, or it will hang forever on output > 4096 bytes
|
|
|
|
|
string output = git_log.StandardOutput.ReadToEnd ();
|
|
|
|
|
git_log.WaitForExit ();
|
|
|
|
|
|
|
|
|
|
string [] lines = output.Split ("\n".ToCharArray ());
|
|
|
|
|
List <string> entries = new List <string> ();
|
|
|
|
|
|
2012-01-18 00:44:15 +00:00
|
|
|
|
int line_number = 0;
|
|
|
|
|
bool first_pass = true;
|
2011-04-20 14:02:20 +00:00
|
|
|
|
string entry = "", last_entry = "";
|
|
|
|
|
foreach (string line in lines) {
|
2012-01-18 00:44:15 +00:00
|
|
|
|
if (line.StartsWith ("commit") && !first_pass) {
|
2011-04-20 14:02:20 +00:00
|
|
|
|
entries.Add (entry);
|
|
|
|
|
entry = "";
|
2012-01-18 00:44:15 +00:00
|
|
|
|
line_number = 0;
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
first_pass = false;
|
2011-05-19 16:05:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-01-18 00:44:15 +00:00
|
|
|
|
// Only parse 250 files to prevent memory issues
|
|
|
|
|
if (line_number < 254) {
|
|
|
|
|
entry += line + "\n";
|
|
|
|
|
line_number++;
|
|
|
|
|
}
|
2011-05-19 16:05:58 +00:00
|
|
|
|
|
2011-04-20 14:02:20 +00:00
|
|
|
|
last_entry = entry;
|
|
|
|
|
}
|
2011-05-19 16:05:58 +00:00
|
|
|
|
|
2011-04-20 14:02:20 +00:00
|
|
|
|
entries.Add (last_entry);
|
|
|
|
|
|
2012-06-19 09:54:46 +00:00
|
|
|
|
Regex regex = new Regex (@"commit ([a-z0-9]{40})\n" +
|
|
|
|
|
"Author: (.+) <(.+)>\n" +
|
|
|
|
|
"*" +
|
|
|
|
|
"Date: ([0-9]{4})-([0-9]{2})-([0-9]{2}) " +
|
|
|
|
|
"([0-9]{2}):([0-9]{2}):([0-9]{2}) (.[0-9]{4})\n" +
|
|
|
|
|
"*", RegexOptions.Compiled);
|
2011-05-03 17:13:02 +00:00
|
|
|
|
|
2011-04-20 14:02:20 +00:00
|
|
|
|
foreach (string log_entry in entries) {
|
|
|
|
|
Match match = regex.Match (log_entry);
|
|
|
|
|
|
|
|
|
|
if (match.Success) {
|
2011-05-16 23:49:01 +00:00
|
|
|
|
SparkleChangeSet change_set = new SparkleChangeSet ();
|
2011-05-19 16:05:58 +00:00
|
|
|
|
|
2012-04-15 17:03:36 +00:00
|
|
|
|
change_set.Folder = new SparkleFolder (Name);
|
2012-02-17 20:26:13 +00:00
|
|
|
|
change_set.Revision = match.Groups [1].Value;
|
|
|
|
|
change_set.User = new SparkleUser (match.Groups [2].Value, match.Groups [3].Value);
|
2012-06-19 09:54:46 +00:00
|
|
|
|
change_set.RemoteUrl = RemoteUrl;
|
2011-04-20 14:02:20 +00:00
|
|
|
|
|
2011-05-16 23:59:03 +00:00
|
|
|
|
change_set.Timestamp = new DateTime (int.Parse (match.Groups [4].Value),
|
2011-04-20 14:02:20 +00:00
|
|
|
|
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));
|
2011-05-19 16:05:58 +00:00
|
|
|
|
|
2011-06-14 23:34:29 +00:00
|
|
|
|
string time_zone = match.Groups [10].Value;
|
|
|
|
|
int our_offset = TimeZone.CurrentTimeZone.GetUtcOffset(DateTime.Now).Hours;
|
|
|
|
|
int their_offset = int.Parse (time_zone.Substring (0, 3));
|
|
|
|
|
change_set.Timestamp = change_set.Timestamp.AddHours (their_offset * -1);
|
|
|
|
|
change_set.Timestamp = change_set.Timestamp.AddHours (our_offset);
|
2011-05-29 15:48:47 +00:00
|
|
|
|
|
|
|
|
|
|
2011-04-20 14:02:20 +00:00
|
|
|
|
string [] entry_lines = log_entry.Split ("\n".ToCharArray ());
|
2011-05-19 16:05:58 +00:00
|
|
|
|
|
2011-04-20 14:02:20 +00:00
|
|
|
|
foreach (string entry_line in entry_lines) {
|
|
|
|
|
if (entry_line.StartsWith (":")) {
|
2011-05-19 16:05:58 +00:00
|
|
|
|
|
2011-04-20 14:02:20 +00:00
|
|
|
|
string change_type = entry_line [37].ToString ();
|
|
|
|
|
string file_path = entry_line.Substring (39);
|
2012-06-30 21:26:16 +00:00
|
|
|
|
|
|
|
|
|
// Handle filepath with special characters
|
|
|
|
|
file_path = EnsureSpecialCharacters (file_path);
|
|
|
|
|
|
|
|
|
|
|
2011-04-28 11:46:22 +00:00
|
|
|
|
string to_file_path;
|
2011-05-19 16:05:58 +00:00
|
|
|
|
|
2011-09-14 15:57:40 +00:00
|
|
|
|
if (file_path.EndsWith (".empty"))
|
2012-02-11 13:29:23 +00:00
|
|
|
|
file_path = file_path.Substring (0,
|
|
|
|
|
file_path.Length - ".empty".Length);
|
2011-09-14 15:57:40 +00:00
|
|
|
|
|
2012-04-22 12:32:55 +00:00
|
|
|
|
if (file_path.Equals (".sparkleshare"))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (change_type.Equals ("A")) {
|
2012-05-27 18:24:12 +00:00
|
|
|
|
change_set.Changes.Add (
|
|
|
|
|
new SparkleChange () {
|
|
|
|
|
Path = file_path,
|
|
|
|
|
Timestamp = change_set.Timestamp,
|
|
|
|
|
Type = SparkleChangeType.Added
|
|
|
|
|
}
|
|
|
|
|
);
|
2011-06-09 23:01:45 +00:00
|
|
|
|
|
2011-04-20 14:02:20 +00:00
|
|
|
|
} else if (change_type.Equals ("M")) {
|
2012-05-27 18:24:12 +00:00
|
|
|
|
change_set.Changes.Add (
|
|
|
|
|
new SparkleChange () {
|
|
|
|
|
Path = file_path,
|
|
|
|
|
Timestamp = change_set.Timestamp,
|
|
|
|
|
Type = SparkleChangeType.Edited
|
|
|
|
|
}
|
|
|
|
|
);
|
2011-06-09 23:01:45 +00:00
|
|
|
|
|
2011-04-20 14:02:20 +00:00
|
|
|
|
} else if (change_type.Equals ("D")) {
|
2012-05-27 18:24:12 +00:00
|
|
|
|
change_set.Changes.Add (
|
|
|
|
|
new SparkleChange () {
|
|
|
|
|
Path = file_path,
|
|
|
|
|
Timestamp = change_set.Timestamp,
|
|
|
|
|
Type = SparkleChangeType.Deleted
|
|
|
|
|
}
|
|
|
|
|
);
|
2011-06-09 23:01:45 +00:00
|
|
|
|
|
2011-04-28 11:46:22 +00:00
|
|
|
|
} else if (change_type.Equals ("R")) {
|
|
|
|
|
int tab_pos = entry_line.LastIndexOf ("\t");
|
|
|
|
|
file_path = entry_line.Substring (42, tab_pos - 42);
|
|
|
|
|
to_file_path = entry_line.Substring (tab_pos + 1);
|
|
|
|
|
|
2012-07-01 08:46:42 +00:00
|
|
|
|
file_path = EnsureSpecialCharacters (file_path);
|
|
|
|
|
to_file_path = EnsureSpecialCharacters (to_file_path);
|
2012-06-30 21:26:16 +00:00
|
|
|
|
|
2012-02-11 13:29:23 +00:00
|
|
|
|
if (file_path.EndsWith (".empty"))
|
2012-05-27 23:14:00 +00:00
|
|
|
|
file_path = file_path.Substring (0, file_path.Length - 6);
|
2012-02-11 13:29:23 +00:00
|
|
|
|
|
|
|
|
|
if (to_file_path.EndsWith (".empty"))
|
2012-05-27 23:14:00 +00:00
|
|
|
|
to_file_path = to_file_path.Substring (0, to_file_path.Length - 6);
|
2012-02-11 13:29:23 +00:00
|
|
|
|
|
2012-05-27 18:24:12 +00:00
|
|
|
|
change_set.Changes.Add (
|
|
|
|
|
new SparkleChange () {
|
2012-06-19 09:54:46 +00:00
|
|
|
|
Path = file_path,
|
|
|
|
|
MovedToPath = to_file_path,
|
|
|
|
|
Timestamp = change_set.Timestamp,
|
|
|
|
|
Type = SparkleChangeType.Moved
|
2012-05-27 18:24:12 +00:00
|
|
|
|
}
|
|
|
|
|
);
|
2011-04-20 14:02:20 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-05-19 16:05:58 +00:00
|
|
|
|
|
2012-05-27 18:24:12 +00:00
|
|
|
|
if (change_set.Changes.Count > 0) {
|
|
|
|
|
if (change_sets.Count > 0) {
|
|
|
|
|
SparkleChangeSet last_change_set = change_sets [change_sets.Count - 1];
|
|
|
|
|
|
|
|
|
|
if (change_set.Timestamp.Year == last_change_set.Timestamp.Year &&
|
|
|
|
|
change_set.Timestamp.Month == last_change_set.Timestamp.Month &&
|
2012-05-27 23:14:00 +00:00
|
|
|
|
change_set.Timestamp.Day == last_change_set.Timestamp.Day &&
|
|
|
|
|
change_set.User.Name.Equals (last_change_set.User.Name)) {
|
2012-05-27 18:24:12 +00:00
|
|
|
|
|
|
|
|
|
last_change_set.Changes.AddRange (change_set.Changes);
|
|
|
|
|
|
|
|
|
|
if (DateTime.Compare (last_change_set.Timestamp, change_set.Timestamp) < 1) {
|
|
|
|
|
last_change_set.FirstTimestamp = last_change_set.Timestamp;
|
|
|
|
|
last_change_set.Timestamp = change_set.Timestamp;
|
|
|
|
|
last_change_set.Revision = change_set.Revision;
|
2011-07-17 00:22:39 +00:00
|
|
|
|
|
2012-05-27 18:24:12 +00:00
|
|
|
|
} else {
|
|
|
|
|
last_change_set.FirstTimestamp = change_set.Timestamp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
change_sets.Add (change_set);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
change_sets.Add (change_set);
|
|
|
|
|
}
|
2011-07-17 00:22:39 +00:00
|
|
|
|
}
|
2011-04-20 14:02:20 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2010-08-08 19:16:48 +00:00
|
|
|
|
|
2011-05-16 23:49:01 +00:00
|
|
|
|
return change_sets;
|
2011-04-20 14:02:20 +00:00
|
|
|
|
}
|
2010-08-29 10:38:34 +00:00
|
|
|
|
|
2012-06-30 21:26:16 +00:00
|
|
|
|
private string EnsureSpecialCharacters (string file_path)
|
|
|
|
|
{
|
|
|
|
|
if (file_path.StartsWith("\""))
|
|
|
|
|
{
|
|
|
|
|
System.Diagnostics.Debug.Assert(file_path.Length > 2 && file_path.EndsWith("\""), "unexpected path");
|
|
|
|
|
file_path = ResolveSpecialChars(file_path.Substring(1, file_path.Length - 2));
|
|
|
|
|
}
|
|
|
|
|
return file_path;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Resolves special characters like \303\244 (ä) to their real character
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="file_path"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
private string ResolveSpecialChars (string file_path)
|
|
|
|
|
{
|
|
|
|
|
var builder = new System.Text.StringBuilder(file_path.Length);
|
|
|
|
|
var codes = new List<byte>();
|
|
|
|
|
for (int i = 0; i < file_path.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
while (file_path[i] == '\\'
|
|
|
|
|
&& file_path.Length - i > 3
|
|
|
|
|
&& char.IsNumber(file_path[i + 1])
|
|
|
|
|
&& char.IsNumber(file_path[i + 2])
|
|
|
|
|
&& char.IsNumber(file_path[i + 3]))
|
|
|
|
|
{
|
|
|
|
|
codes.Add (Convert.ToByte(file_path.Substring(i + 1, 3), 8));
|
|
|
|
|
i += 4;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (codes.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
builder.Append(System.Text.Encoding.UTF8.GetString (codes.ToArray ()));
|
|
|
|
|
codes.Clear ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
builder.Append(file_path[i]);
|
|
|
|
|
}
|
|
|
|
|
return builder.ToString();
|
|
|
|
|
//System.Text.Encoding.UTF8.
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-01 08:46:42 +00:00
|
|
|
|
|
|
|
|
|
private string EnsureSpecialCharacters (string path)
|
2012-06-30 21:26:16 +00:00
|
|
|
|
{
|
2012-07-01 08:46:42 +00:00
|
|
|
|
// The path is quoted if it contains special characters
|
|
|
|
|
if (path.StartsWith ("\""))
|
|
|
|
|
path = ResolveSpecialChars (path.Substring (1, path.Length - 2));
|
|
|
|
|
|
|
|
|
|
return path;
|
2012-06-30 21:26:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-07-01 08:46:42 +00:00
|
|
|
|
|
2012-06-30 21:26:16 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Resolves special characters like \303\244 (ä) to their real character
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="file_path"></param>
|
|
|
|
|
/// <returns></returns>
|
2012-07-01 08:46:42 +00:00
|
|
|
|
private string ResolveSpecialChars (string s)
|
2012-06-30 21:26:16 +00:00
|
|
|
|
{
|
2012-07-01 08:46:42 +00:00
|
|
|
|
StringBuilder builder = new StringBuilder (s.Length);
|
|
|
|
|
List<byte> codes = new List<byte> ();
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < s.Length; i++) {
|
|
|
|
|
while (s [i] == '\\' &&
|
|
|
|
|
s.Length - i > 3 &&
|
|
|
|
|
char.IsNumber (s [i + 1]) &&
|
|
|
|
|
char.IsNumber (s [i + 2]) &&
|
|
|
|
|
char.IsNumber (s [i + 3])) {
|
|
|
|
|
|
|
|
|
|
codes.Add (Convert.ToByte (s.Substring (i + 1, 3), 8));
|
2012-06-30 21:26:16 +00:00
|
|
|
|
i += 4;
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-01 08:46:42 +00:00
|
|
|
|
if (codes.Count > 0) {
|
|
|
|
|
builder.Append (Encoding.UTF8.GetString (codes.ToArray ()));
|
2012-06-30 21:26:16 +00:00
|
|
|
|
codes.Clear ();
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-01 08:46:42 +00:00
|
|
|
|
builder.Append (s [i]);
|
2012-06-30 21:26:16 +00:00
|
|
|
|
}
|
2012-07-01 08:46:42 +00:00
|
|
|
|
|
|
|
|
|
return builder.ToString ();
|
2012-06-30 21:26:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
2010-06-20 21:05:11 +00:00
|
|
|
|
|
2012-06-29 20:43:49 +00:00
|
|
|
|
private void ClearCache ()
|
|
|
|
|
{
|
|
|
|
|
if (!this.use_git_bin)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
SparkleGitBin git_bin = new SparkleGitBin (LocalPath, "clear -f");
|
|
|
|
|
git_bin.Start ();
|
|
|
|
|
git_bin.WaitForExit ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2011-09-14 17:30:17 +00:00
|
|
|
|
// Git doesn't track empty directories, so this method
|
2012-01-16 19:58:25 +00:00
|
|
|
|
// fills them all with a hidden empty file.
|
|
|
|
|
//
|
|
|
|
|
// It also prevents git repositories from becoming
|
|
|
|
|
// git submodules by renaming the .git/HEAD file
|
|
|
|
|
private void PrepareDirectories (string path)
|
2011-09-14 17:30:17 +00:00
|
|
|
|
{
|
2012-03-12 19:56:06 +00:00
|
|
|
|
try {
|
|
|
|
|
foreach (string child_path in Directory.GetDirectories (path)) {
|
2012-04-17 17:00:05 +00:00
|
|
|
|
if (SparkleHelpers.IsSymlink (child_path))
|
|
|
|
|
continue;
|
|
|
|
|
|
2012-04-17 09:05:47 +00:00
|
|
|
|
if (child_path.EndsWith (".git")) {
|
|
|
|
|
if (child_path.Equals (Path.Combine (LocalPath, ".git")))
|
|
|
|
|
continue;
|
2012-03-12 19:56:06 +00:00
|
|
|
|
|
|
|
|
|
string HEAD_file_path = Path.Combine (child_path, "HEAD");
|
|
|
|
|
|
|
|
|
|
if (File.Exists (HEAD_file_path)) {
|
|
|
|
|
File.Move (HEAD_file_path, HEAD_file_path + ".backup");
|
2012-06-21 16:58:07 +00:00
|
|
|
|
SparkleHelpers.DebugInfo ("Git", Name + " | Renamed " + HEAD_file_path);
|
2012-03-12 19:56:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
continue;
|
2012-01-16 19:58:25 +00:00
|
|
|
|
}
|
2012-03-12 19:56:06 +00:00
|
|
|
|
|
|
|
|
|
PrepareDirectories (child_path);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Directory.GetFiles (path).Length == 0 &&
|
|
|
|
|
Directory.GetDirectories (path).Length == 0 &&
|
|
|
|
|
!path.Equals (LocalPath)) {
|
2012-04-22 12:32:55 +00:00
|
|
|
|
|
|
|
|
|
if (!File.Exists (Path.Combine (path, ".empty"))) {
|
2012-05-20 11:51:09 +00:00
|
|
|
|
try {
|
|
|
|
|
File.WriteAllText (Path.Combine (path, ".empty"), "I'm a folder!");
|
|
|
|
|
File.SetAttributes (Path.Combine (path, ".empty"), FileAttributes.Hidden);
|
|
|
|
|
} catch {
|
2012-06-21 16:58:07 +00:00
|
|
|
|
SparkleHelpers.DebugInfo ("Git", Name + " | Failed adding empty folder " + path);
|
2012-05-20 11:51:09 +00:00
|
|
|
|
}
|
2012-04-22 12:32:55 +00:00
|
|
|
|
}
|
2012-01-16 19:58:25 +00:00
|
|
|
|
}
|
2011-09-14 17:30:17 +00:00
|
|
|
|
|
2012-03-12 19:56:06 +00:00
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
SparkleHelpers.DebugInfo ("Git", "Failed preparing directory: " + e.Message);
|
2011-11-30 14:04:56 +00:00
|
|
|
|
}
|
2011-09-14 17:30:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2011-04-20 14:02:20 +00:00
|
|
|
|
// Creates a pretty commit message based on what has changed
|
|
|
|
|
private string FormatCommitMessage ()
|
|
|
|
|
{
|
2012-07-02 19:09:46 +00:00
|
|
|
|
int count = 0;
|
|
|
|
|
string message = "";
|
2011-04-21 12:25:28 +00:00
|
|
|
|
|
|
|
|
|
SparkleGit git_status = new SparkleGit (LocalPath, "status --porcelain");
|
|
|
|
|
git_status.Start ();
|
|
|
|
|
|
2012-07-02 19:09:46 +00:00
|
|
|
|
while (!git_status.StandardOutput.EndOfStream) {
|
|
|
|
|
string line = git_status.StandardOutput.ReadLine ();
|
2011-04-21 12:25:28 +00:00
|
|
|
|
|
2012-07-02 19:09:46 +00:00
|
|
|
|
if (line.EndsWith (".empty") || line.EndsWith (".empty\""))
|
2012-07-02 16:33:21 +00:00
|
|
|
|
continue;
|
|
|
|
|
|
2012-07-02 19:09:46 +00:00
|
|
|
|
if (line.StartsWith ("R")) {
|
|
|
|
|
string path = line.Substring (3, line.IndexOf (" -> ") - 3).Trim ("\"".ToCharArray ());
|
|
|
|
|
string moved_to_path = line.Substring (line.IndexOf (" -> ") + 4).Trim ("\"".ToCharArray ());
|
2011-04-21 12:25:28 +00:00
|
|
|
|
|
2012-07-02 19:09:46 +00:00
|
|
|
|
message += "- ‘" + EnsureSpecialCharacters (path) + "’\n";
|
|
|
|
|
message += "+ ‘" + EnsureSpecialCharacters (moved_to_path) + "’\n";
|
2011-05-17 21:37:22 +00:00
|
|
|
|
|
2012-07-02 19:09:46 +00:00
|
|
|
|
} else {
|
|
|
|
|
if (line.StartsWith ("M")) {
|
|
|
|
|
message += "/";
|
2011-05-17 21:37:22 +00:00
|
|
|
|
|
2012-07-02 19:09:46 +00:00
|
|
|
|
} else if (line.StartsWith ("D")) {
|
|
|
|
|
message += "-";
|
2011-04-20 14:02:20 +00:00
|
|
|
|
|
2012-07-02 19:09:46 +00:00
|
|
|
|
} else {
|
|
|
|
|
message += "+";
|
|
|
|
|
}
|
2011-04-20 14:02:20 +00:00
|
|
|
|
|
2012-07-02 19:09:46 +00:00
|
|
|
|
string path = line.Substring (3).Trim ("\"".ToCharArray ());
|
2012-07-02 21:10:03 +00:00
|
|
|
|
message += " ‘" + EnsureSpecialCharacters (path) + "’\n";
|
2012-07-02 19:09:46 +00:00
|
|
|
|
}
|
2011-04-20 14:02:20 +00:00
|
|
|
|
|
2011-05-18 13:03:50 +00:00
|
|
|
|
count++;
|
2012-07-02 19:09:46 +00:00
|
|
|
|
if (count == 10) {
|
|
|
|
|
message += "...\n";
|
|
|
|
|
break;
|
|
|
|
|
}
|
2011-04-20 14:02:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-07-02 19:09:46 +00:00
|
|
|
|
git_status.WaitForExit ();
|
2012-07-03 07:58:35 +00:00
|
|
|
|
return message.Replace ("\"", "\\\"");
|
2011-04-20 14:02:20 +00:00
|
|
|
|
}
|
2010-06-20 21:05:11 +00:00
|
|
|
|
|
2010-10-10 22:04:08 +00:00
|
|
|
|
|
2011-12-15 15:15:29 +00:00
|
|
|
|
// Recursively gets a folder's size in bytes
|
2012-02-08 19:42:29 +00:00
|
|
|
|
private double CalculateSizes (DirectoryInfo parent)
|
2011-12-15 15:15:29 +00:00
|
|
|
|
{
|
2012-04-10 21:19:33 +00:00
|
|
|
|
if (!Directory.Exists (parent.FullName))
|
2011-12-15 15:15:29 +00:00
|
|
|
|
return 0;
|
|
|
|
|
|
2012-02-08 19:42:29 +00:00
|
|
|
|
if (parent.Name.Equals ("rebase-apply"))
|
2011-12-15 15:15:29 +00:00
|
|
|
|
return 0;
|
|
|
|
|
|
2012-04-10 21:19:33 +00:00
|
|
|
|
double size = 0;
|
|
|
|
|
|
2011-12-15 15:15:29 +00:00
|
|
|
|
try {
|
2012-03-08 03:28:18 +00:00
|
|
|
|
foreach (FileInfo file in parent.GetFiles ()) {
|
2011-12-15 15:15:29 +00:00
|
|
|
|
if (!file.Exists)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2012-03-08 03:28:18 +00:00
|
|
|
|
if (file.Name.Equals (".empty"))
|
|
|
|
|
File.SetAttributes (file.FullName, FileAttributes.Hidden);
|
|
|
|
|
|
2011-12-15 15:15:29 +00:00
|
|
|
|
size += file.Length;
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-10 21:19:33 +00:00
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
SparkleHelpers.DebugInfo ("Local", "Error calculating size: " + e.Message);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
2011-12-15 15:15:29 +00:00
|
|
|
|
foreach (DirectoryInfo directory in parent.GetDirectories ())
|
2012-02-08 19:42:29 +00:00
|
|
|
|
size += CalculateSizes (directory);
|
2011-12-15 15:15:29 +00:00
|
|
|
|
|
2012-04-10 21:19:33 +00:00
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
SparkleHelpers.DebugInfo ("Local", "Error calculating size: " + e.Message);
|
2011-12-15 15:15:29 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return size;
|
|
|
|
|
}
|
2011-04-20 14:02:20 +00:00
|
|
|
|
}
|
2010-07-24 12:32:05 +00:00
|
|
|
|
}
|