2011-06-01 23:08:05 +00:00
|
|
|
|
// SparkleShare, a collaboration and sharing tool.
|
2017-07-23 12:47:54 +00:00
|
|
|
|
// Copyright (C) 2010 Hylke Bons <hi@planetpeanut.uk>
|
2010-06-20 21:05:11 +00:00
|
|
|
|
//
|
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
2018-04-09 03:41:59 +00:00
|
|
|
|
// it under the terms of the GNU Lesser General Public License as
|
|
|
|
|
// published by the Free Software Foundation, either version 3 of the
|
2013-10-11 15:13:46 +00:00
|
|
|
|
// License, or (at your option) any later version.
|
2010-06-20 21:05:11 +00:00
|
|
|
|
//
|
|
|
|
|
// 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
|
2013-10-11 15:13:46 +00:00
|
|
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Text.RegularExpressions;
|
|
|
|
|
using System.Threading;
|
2011-03-08 23:55:21 +00:00
|
|
|
|
|
2016-03-31 08:35:26 +00:00
|
|
|
|
namespace Sparkles.Git {
|
2010-06-20 21:05:11 +00:00
|
|
|
|
|
2016-03-30 23:36:31 +00:00
|
|
|
|
public class GitRepository : BaseRepository {
|
2011-05-18 18:12:45 +00:00
|
|
|
|
|
2016-04-04 09:27:20 +00:00
|
|
|
|
SSHAuthenticationInfo auth_info;
|
|
|
|
|
bool user_is_set;
|
2012-06-24 18:14:52 +00:00
|
|
|
|
|
2013-12-02 20:52:17 +00:00
|
|
|
|
|
2016-06-18 18:32:07 +00:00
|
|
|
|
string cached_branch;
|
2012-11-18 18:49:57 +00:00
|
|
|
|
|
2016-04-04 09:27:20 +00:00
|
|
|
|
string branch {
|
2012-11-12 22:47:40 +00:00
|
|
|
|
get {
|
2018-04-09 03:41:59 +00:00
|
|
|
|
if (!string.IsNullOrEmpty (this.cached_branch))
|
2012-11-28 20:17:39 +00:00
|
|
|
|
return this.cached_branch;
|
2012-11-12 22:47:40 +00:00
|
|
|
|
|
2016-04-04 09:27:20 +00:00
|
|
|
|
var git = new GitCommand (LocalPath, "config core.ignorecase true");
|
2013-06-29 10:48:36 +00:00
|
|
|
|
git.StartAndWaitForExit ();
|
|
|
|
|
|
2016-06-18 00:03:40 +00:00
|
|
|
|
// TODO: ugly
|
2013-12-02 20:52:17 +00:00
|
|
|
|
while (this.in_merge && HasLocalChanges) {
|
2012-11-28 20:17:39 +00:00
|
|
|
|
try {
|
|
|
|
|
ResolveConflict ();
|
2018-04-09 03:41:59 +00:00
|
|
|
|
|
2012-11-28 20:17:39 +00:00
|
|
|
|
} catch (IOException e) {
|
2016-03-30 23:36:31 +00:00
|
|
|
|
Logger.LogInfo ("Git", Name + " | Failed to resolve conflict, trying again...", e);
|
2012-11-28 20:17:39 +00:00
|
|
|
|
}
|
2012-11-12 22:47:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-03-30 23:36:31 +00:00
|
|
|
|
git = new GitCommand (LocalPath, "config core.ignorecase false");
|
2013-06-29 10:48:36 +00:00
|
|
|
|
git.StartAndWaitForExit ();
|
|
|
|
|
|
2016-03-30 23:36:31 +00:00
|
|
|
|
git = new GitCommand (LocalPath, "rev-parse --abbrev-ref HEAD");
|
2012-11-28 20:17:39 +00:00
|
|
|
|
this.cached_branch = git.StartAndReadStandardOutput ();
|
|
|
|
|
|
2012-11-12 22:47:40 +00:00
|
|
|
|
return this.cached_branch;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-06-24 18:14:52 +00:00
|
|
|
|
|
2016-04-04 09:27:20 +00:00
|
|
|
|
bool in_merge {
|
2013-12-02 20:52:17 +00:00
|
|
|
|
get {
|
2016-03-31 14:46:17 +00:00
|
|
|
|
string merge_file_path = Path.Combine (LocalPath, ".git", "MERGE_HEAD");
|
2013-12-02 20:52:17 +00:00
|
|
|
|
return File.Exists (merge_file_path);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2016-04-04 09:27:20 +00:00
|
|
|
|
public GitRepository (string path, Configuration config, SSHAuthenticationInfo auth_info) : base (path, config)
|
2012-01-29 20:33:12 +00:00
|
|
|
|
{
|
2016-04-04 09:27:20 +00:00
|
|
|
|
this.auth_info = auth_info;
|
|
|
|
|
|
2016-05-30 03:14:40 +00:00
|
|
|
|
var git_config = new GitCommand (LocalPath, "config core.ignorecase false");
|
|
|
|
|
git_config.StartAndWaitForExit ();
|
|
|
|
|
|
|
|
|
|
git_config = new GitCommand (LocalPath, "config remote.origin.url \"" + RemoteUrl + "\"");
|
|
|
|
|
git_config.StartAndWaitForExit ();
|
2016-10-25 09:25:34 +00:00
|
|
|
|
|
2017-04-15 11:19:30 +00:00
|
|
|
|
git_config = new GitCommand (LocalPath, "config core.sshCommand " + GitCommand.FormatGitSSHCommand (auth_info));
|
2016-10-25 09:25:34 +00:00
|
|
|
|
git_config.StartAndWaitForExit();
|
2018-06-15 21:25:43 +00:00
|
|
|
|
|
2018-06-16 11:41:13 +00:00
|
|
|
|
PrepareGitLFS ();
|
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 {
|
2016-03-31 14:46:17 +00:00
|
|
|
|
string file_path = Path.Combine (LocalPath, ".git", "info", "size");
|
2012-01-18 23:21:09 +00:00
|
|
|
|
|
2018-05-11 07:21:27 +00:00
|
|
|
|
if (!File.Exists (file_path))
|
|
|
|
|
File.WriteAllText (file_path, "0");
|
|
|
|
|
|
|
|
|
|
string size = File.ReadAllText (file_path);
|
|
|
|
|
|
2012-01-18 23:21:09 +00:00
|
|
|
|
try {
|
2012-07-18 12:49:11 +00:00
|
|
|
|
return double.Parse (size);
|
2012-01-18 23:21:09 +00:00
|
|
|
|
|
2018-03-10 19:09:48 +00:00
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
Logger.LogInfo ("Git", Name + " | Failed to parse " + file_path, e);
|
2012-01-18 23:21:09 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2011-12-15 15:15:29 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public override double HistorySize {
|
|
|
|
|
get {
|
2016-03-31 14:46:17 +00:00
|
|
|
|
string file_path = Path.Combine (LocalPath, ".git", "info", "history_size");
|
2012-01-18 23:21:09 +00:00
|
|
|
|
|
2018-05-11 07:21:27 +00:00
|
|
|
|
if (!File.Exists (file_path))
|
|
|
|
|
File.WriteAllText (file_path, "0");
|
|
|
|
|
|
|
|
|
|
string size = File.ReadAllText (file_path);
|
|
|
|
|
|
2012-01-18 23:21:09 +00:00
|
|
|
|
try {
|
2012-07-18 12:49:11 +00:00
|
|
|
|
return double.Parse (size);
|
2012-01-18 23:21:09 +00:00
|
|
|
|
|
2018-03-10 19:09:48 +00:00
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
Logger.LogInfo ("Git", Name + " | Failed to parse " + file_path, e);
|
2012-01-18 23:21:09 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2011-12-15 15:15:29 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2016-04-04 09:27:20 +00:00
|
|
|
|
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
|
|
|
|
|
2016-03-31 14:46:17 +00:00
|
|
|
|
string size_file_path = Path.Combine (LocalPath, ".git", "info", "size");
|
|
|
|
|
string history_size_file_path = Path.Combine (LocalPath, ".git", "info", "history_size");
|
2012-01-18 23:21:09 +00:00
|
|
|
|
|
|
|
|
|
File.WriteAllText (size_file_path, size.ToString ());
|
|
|
|
|
File.WriteAllText (history_size_file_path, history_size.ToString ());
|
|
|
|
|
}
|
2011-06-29 19:28:49 +00:00
|
|
|
|
|
2011-10-30 21:22:48 +00:00
|
|
|
|
|
2011-05-19 16:05:58 +00:00
|
|
|
|
public override string CurrentRevision {
|
|
|
|
|
get {
|
2016-04-04 09:27:20 +00:00
|
|
|
|
var git = new GitCommand (LocalPath, "rev-parse HEAD");
|
2012-07-26 10:12:14 +00:00
|
|
|
|
string output = git.StartAndReadStandardOutput ();
|
2011-05-19 16:05:58 +00:00
|
|
|
|
|
2012-07-22 09:40:49 +00:00
|
|
|
|
if (git.ExitCode == 0)
|
2012-07-26 10:12:14 +00:00
|
|
|
|
return output;
|
2016-04-04 09:27:20 +00:00
|
|
|
|
|
|
|
|
|
return null;
|
2011-05-19 16:05:58 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2012-05-19 15:15:20 +00:00
|
|
|
|
public override bool HasRemoteChanges {
|
2012-02-06 14:02:41 +00:00
|
|
|
|
get {
|
2016-03-30 23:36:31 +00:00
|
|
|
|
Logger.LogInfo ("Git", Name + " | Checking for remote changes...");
|
2012-06-09 15:27:34 +00:00
|
|
|
|
string current_revision = CurrentRevision;
|
2012-07-26 10:12:14 +00:00
|
|
|
|
|
2016-04-04 09:27:20 +00:00
|
|
|
|
var git = new GitCommand (LocalPath,
|
2016-06-19 06:39:33 +00:00
|
|
|
|
"ls-remote --heads --exit-code origin " + this.branch, auth_info);
|
2016-04-04 09:27:20 +00:00
|
|
|
|
|
2016-06-18 18:32:07 +00:00
|
|
|
|
string output = git.StartAndReadStandardOutput ();
|
2012-07-26 10:12:14 +00:00
|
|
|
|
|
2012-02-06 14:02:41 +00:00
|
|
|
|
if (git.ExitCode != 0)
|
|
|
|
|
return false;
|
2012-07-26 10:12:14 +00:00
|
|
|
|
|
2013-01-24 20:10:42 +00:00
|
|
|
|
string remote_revision = "" + output.Substring (0, 40);
|
|
|
|
|
|
|
|
|
|
if (!remote_revision.Equals (current_revision)) {
|
2020-06-23 15:18:03 +00:00
|
|
|
|
git = new GitCommand (LocalPath, "merge-base " + remote_revision + " " + this.branch);
|
2013-01-24 20:10:42 +00:00
|
|
|
|
git.StartAndWaitForExit ();
|
|
|
|
|
|
|
|
|
|
if (git.ExitCode != 0) {
|
2016-03-30 23:36:31 +00:00
|
|
|
|
Logger.LogInfo ("Git", Name + " | Remote changes found, local: " +
|
2013-01-24 20:10:42 +00:00
|
|
|
|
current_revision + ", remote: " + remote_revision);
|
|
|
|
|
|
|
|
|
|
Error = ErrorStatus.None;
|
|
|
|
|
return true;
|
2018-04-09 03:41:59 +00:00
|
|
|
|
|
2013-01-24 20:10:42 +00:00
|
|
|
|
} else {
|
2016-03-30 23:36:31 +00:00
|
|
|
|
Logger.LogInfo ("Git", Name + " | Remote " + remote_revision + " is already in our history");
|
2013-01-24 20:10:42 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2016-05-30 03:14:40 +00:00
|
|
|
|
}
|
2011-08-25 15:02:34 +00:00
|
|
|
|
|
2016-03-30 23:36:31 +00:00
|
|
|
|
Logger.LogInfo ("Git", Name + " | No remote changes, local+remote: " + current_revision);
|
2013-01-24 20:10:42 +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
|
|
|
|
{
|
2013-03-11 17:02:30 +00:00
|
|
|
|
if (!Add ()) {
|
|
|
|
|
Error = ErrorStatus.UnreadableFiles;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-15 16:45:34 +00:00
|
|
|
|
string message = base.status_message;
|
2014-10-28 15:12:37 +00:00
|
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty (message))
|
|
|
|
|
message = FormatCommitMessage ();
|
2011-08-26 19:00:22 +00:00
|
|
|
|
|
2013-03-11 17:02:30 +00:00
|
|
|
|
if (message != null)
|
2011-12-03 11:40:03 +00:00
|
|
|
|
Commit (message);
|
2012-06-28 23:12:37 +00:00
|
|
|
|
|
2018-06-16 11:41:13 +00:00
|
|
|
|
PrepareGitLFS ();
|
2016-05-30 03:14:40 +00:00
|
|
|
|
|
2016-06-19 06:39:33 +00:00
|
|
|
|
var git_push = new GitCommand (LocalPath, string.Format ("push --all --progress origin", RemoteUrl), auth_info);
|
2016-05-30 03:14:40 +00:00
|
|
|
|
git_push.StartInfo.RedirectStandardError = true;
|
|
|
|
|
git_push.Start ();
|
2011-12-30 00:44:35 +00:00
|
|
|
|
|
2016-06-29 13:44:15 +00:00
|
|
|
|
if (!ReadStream (git_push))
|
|
|
|
|
return false;
|
2011-12-30 00:44:35 +00:00
|
|
|
|
|
2016-05-30 03:14:40 +00:00
|
|
|
|
git_push.WaitForExit ();
|
2016-06-20 20:19:20 +00:00
|
|
|
|
|
2012-02-08 19:42:29 +00:00
|
|
|
|
UpdateSizes ();
|
2011-12-30 00:44:35 +00:00
|
|
|
|
|
2016-05-30 03:14:40 +00:00
|
|
|
|
if (git_push.ExitCode == 0)
|
2012-06-29 20:43:49 +00:00
|
|
|
|
return true;
|
|
|
|
|
|
2016-03-28 17:14:21 +00:00
|
|
|
|
Error = ErrorStatus.HostUnreachable;
|
|
|
|
|
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
|
|
|
|
{
|
2016-06-20 20:19:20 +00:00
|
|
|
|
string lfs_is_behind_file_path = Path.Combine (LocalPath, ".git", "lfs", "is_behind");
|
|
|
|
|
|
|
|
|
|
if (StorageType == StorageType.LargeFiles)
|
2018-03-10 12:25:44 +00:00
|
|
|
|
File.Create (lfs_is_behind_file_path).Close ();
|
2016-06-20 20:19:20 +00:00
|
|
|
|
|
2016-06-29 13:44:15 +00:00
|
|
|
|
var git_fetch = new GitCommand (LocalPath, "fetch --progress origin " + branch, auth_info);
|
2011-12-30 14:00:15 +00:00
|
|
|
|
|
2016-06-29 13:44:15 +00:00
|
|
|
|
git_fetch.StartInfo.RedirectStandardError = true;
|
|
|
|
|
git_fetch.Start ();
|
2011-12-30 14:00:15 +00:00
|
|
|
|
|
2016-06-29 13:44:15 +00:00
|
|
|
|
if (!ReadStream (git_fetch))
|
|
|
|
|
return false;
|
2011-12-30 14:00:15 +00:00
|
|
|
|
|
2016-06-29 13:44:15 +00:00
|
|
|
|
git_fetch.WaitForExit ();
|
2011-12-30 14:00:15 +00:00
|
|
|
|
|
2016-06-29 13:44:15 +00:00
|
|
|
|
if (git_fetch.ExitCode != 0) {
|
2012-09-18 18:40:06 +00:00
|
|
|
|
Error = ErrorStatus.HostUnreachable;
|
2011-05-19 16:05:58 +00:00
|
|
|
|
return false;
|
2011-04-20 14:02:20 +00:00
|
|
|
|
}
|
2016-06-20 20:19:20 +00:00
|
|
|
|
|
|
|
|
|
if (Merge ()) {
|
|
|
|
|
if (StorageType == StorageType.LargeFiles) {
|
|
|
|
|
// Pull LFS files manually to benefit from concurrency
|
|
|
|
|
var git_lfs_pull = new GitCommand (LocalPath, "lfs pull origin", auth_info);
|
|
|
|
|
git_lfs_pull.StartAndWaitForExit ();
|
|
|
|
|
|
|
|
|
|
if (git_lfs_pull.ExitCode != 0) {
|
|
|
|
|
Error = ErrorStatus.HostUnreachable;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (File.Exists (lfs_is_behind_file_path))
|
|
|
|
|
File.Delete (lfs_is_behind_file_path);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UpdateSizes ();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
2011-04-20 14:02:20 +00:00
|
|
|
|
}
|
2011-04-15 00:28:42 +00:00
|
|
|
|
|
|
|
|
|
|
2016-06-29 13:44:15 +00:00
|
|
|
|
bool ReadStream (GitCommand command)
|
|
|
|
|
{
|
|
|
|
|
StreamReader output_stream = command.StandardError;
|
|
|
|
|
|
|
|
|
|
if (StorageType == StorageType.LargeFiles)
|
|
|
|
|
output_stream = command.StandardOutput;
|
|
|
|
|
|
|
|
|
|
double percentage = 0;
|
|
|
|
|
double speed = 0;
|
|
|
|
|
string information = "";
|
|
|
|
|
|
|
|
|
|
while (!output_stream.EndOfStream) {
|
|
|
|
|
string line = output_stream.ReadLine ();
|
|
|
|
|
ErrorStatus error = GitCommand.ParseProgress (line, out percentage, out speed, out information);
|
|
|
|
|
|
|
|
|
|
if (error != ErrorStatus.None) {
|
|
|
|
|
Error = error;
|
|
|
|
|
information = line;
|
|
|
|
|
|
|
|
|
|
command.Kill ();
|
|
|
|
|
command.Dispose ();
|
|
|
|
|
Logger.LogInfo ("Git", Name + " | Error status changed to " + Error);
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OnProgressChanged (percentage, speed, information);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
2016-04-04 09:27:20 +00:00
|
|
|
|
var git = new GitCommand (LocalPath, "status --porcelain");
|
2012-07-26 10:12:14 +00:00
|
|
|
|
string output = git.StartAndReadStandardOutput ();
|
2011-06-29 19:45:37 +00:00
|
|
|
|
|
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 {
|
2016-06-20 20:19:20 +00:00
|
|
|
|
if (StorageType == StorageType.LargeFiles) {
|
|
|
|
|
string lfs_is_behind_file_path = Path.Combine (LocalPath, ".git", "lfs", "is_behind");
|
|
|
|
|
|
|
|
|
|
if (File.Exists (lfs_is_behind_file_path))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-31 14:46:17 +00:00
|
|
|
|
string unsynced_file_path = Path.Combine (LocalPath, ".git", "has_unsynced_changes");
|
2011-05-19 16:05:58 +00:00
|
|
|
|
return File.Exists (unsynced_file_path);
|
|
|
|
|
}
|
2011-04-21 21:14:44 +00:00
|
|
|
|
|
2011-05-19 16:05:58 +00:00
|
|
|
|
set {
|
2016-03-31 14:46:17 +00:00
|
|
|
|
string unsynced_file_path = Path.Combine (LocalPath, ".git", "has_unsynced_changes");
|
2011-05-19 16:05:58 +00:00
|
|
|
|
|
2012-07-18 12:49:11 +00:00
|
|
|
|
if (value)
|
|
|
|
|
File.WriteAllText (unsynced_file_path, "");
|
|
|
|
|
else
|
2011-05-19 16:05:58 +00:00
|
|
|
|
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
|
2016-04-04 09:27:20 +00:00
|
|
|
|
bool Add ()
|
2011-04-20 14:02:20 +00:00
|
|
|
|
{
|
2016-04-04 09:27:20 +00:00
|
|
|
|
var git = new GitCommand (LocalPath, "add --all");
|
2012-07-26 10:12:14 +00:00
|
|
|
|
git.StartAndWaitForExit ();
|
2013-03-11 17:02:30 +00:00
|
|
|
|
|
|
|
|
|
return (git.ExitCode == 0);
|
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
|
2016-04-04 09:27:20 +00:00
|
|
|
|
void Commit (string message)
|
2012-10-20 22:22:41 +00:00
|
|
|
|
{
|
2018-06-15 16:44:12 +00:00
|
|
|
|
GitCommand git_config;
|
2012-06-24 22:20:45 +00:00
|
|
|
|
|
2017-02-03 01:06:20 +00:00
|
|
|
|
string user_name = base.local_config.User.Name;
|
|
|
|
|
string user_email = base.local_config.User.Email;
|
|
|
|
|
|
2012-10-20 22:22:41 +00:00
|
|
|
|
if (!this.user_is_set) {
|
2018-06-15 16:44:12 +00:00
|
|
|
|
git_config = new GitCommand (LocalPath, "config user.name \"" + user_name + "\"");
|
|
|
|
|
git_config.StartAndWaitForExit ();
|
2012-06-24 18:14:52 +00:00
|
|
|
|
|
2018-06-15 16:44:12 +00:00
|
|
|
|
git_config = new GitCommand (LocalPath, "config user.email \"" + user_email + "\"");
|
|
|
|
|
git_config.StartAndWaitForExit ();
|
2012-06-24 18:14:52 +00:00
|
|
|
|
|
2012-10-20 22:22:41 +00:00
|
|
|
|
this.user_is_set = true;
|
|
|
|
|
}
|
2012-06-24 18:14:52 +00:00
|
|
|
|
|
2017-02-03 01:06:20 +00:00
|
|
|
|
if (StorageType == StorageType.Encrypted) {
|
|
|
|
|
string password_file_path = Path.Combine (LocalPath, ".git", "info", "encryption_password");
|
|
|
|
|
string password = File.ReadAllText (password_file_path);
|
|
|
|
|
|
|
|
|
|
user_name = user_name.AESEncrypt (password);
|
|
|
|
|
user_email = user_email.AESEncrypt (password);
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-15 16:42:16 +00:00
|
|
|
|
GitCommand git_commit;
|
|
|
|
|
string message_file_path = Path.Combine (LocalPath, ".git", "info", "commit_message");
|
2011-10-17 17:34:17 +00:00
|
|
|
|
|
2018-06-15 16:42:16 +00:00
|
|
|
|
try {
|
|
|
|
|
File.WriteAllText (message_file_path, message);
|
|
|
|
|
|
|
|
|
|
// Commit from message stored in temporary file to avoid special character conflicts on the command line
|
2018-06-21 08:10:16 +00:00
|
|
|
|
git_commit = new GitCommand (LocalPath, string.Format ("commit --all --file=\"{0}\" --author=\"{1} <{2}>\"",
|
|
|
|
|
message_file_path, user_name, user_email));
|
2018-06-15 16:42:16 +00:00
|
|
|
|
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
Logger.LogInfo ("Git", Name + " | Could not create commit message file: " + message_file_path, e);
|
|
|
|
|
|
|
|
|
|
// If committing with a temporary file fails, use a simple static commit message
|
2018-06-21 08:10:16 +00:00
|
|
|
|
git_commit = new GitCommand (LocalPath, string.Format ("commit --all --message=\"{0}\" --author=\"{1} <{2}>\"",
|
|
|
|
|
"Changes by SparkleShare", user_name, user_email));
|
2018-06-15 16:42:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
git_commit.StartAndReadStandardOutput ();
|
|
|
|
|
File.Delete (message_file_path);
|
2011-04-20 14:02:20 +00:00
|
|
|
|
}
|
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
|
2016-04-04 09:27:20 +00:00
|
|
|
|
bool Merge ()
|
2011-04-20 14:02:20 +00:00
|
|
|
|
{
|
2013-01-06 11:21:54 +00:00
|
|
|
|
string message = FormatCommitMessage ();
|
2018-04-09 03:41:59 +00:00
|
|
|
|
|
2013-01-06 11:21:54 +00:00
|
|
|
|
if (message != null) {
|
2011-04-20 14:02:20 +00:00
|
|
|
|
Add ();
|
2013-01-06 11:21:54 +00:00
|
|
|
|
Commit (message);
|
2011-04-20 14:02:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-03-30 23:36:31 +00:00
|
|
|
|
GitCommand git;
|
2012-12-19 20:57:55 +00:00
|
|
|
|
|
2013-12-02 20:52:17 +00:00
|
|
|
|
// Stop if we're already in a merge because something went wrong
|
|
|
|
|
if (this.in_merge) {
|
2016-03-30 23:36:31 +00:00
|
|
|
|
git = new GitCommand (LocalPath, "merge --abort");
|
2013-12-02 20:52:17 +00:00
|
|
|
|
git.StartAndWaitForExit ();
|
2018-04-09 03:41:59 +00:00
|
|
|
|
|
2013-12-02 20:52:17 +00:00
|
|
|
|
return false;
|
2012-12-19 20:57:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-11-11 19:15:42 +00:00
|
|
|
|
// Temporarily change the ignorecase setting to true to avoid
|
2012-12-19 20:57:55 +00:00
|
|
|
|
// conflicts in file names due to letter case changes
|
2016-03-30 23:36:31 +00:00
|
|
|
|
git = new GitCommand (LocalPath, "config core.ignorecase true");
|
2012-07-26 10:12:14 +00:00
|
|
|
|
git.StartAndWaitForExit ();
|
2010-07-20 21:21:37 +00:00
|
|
|
|
|
2018-10-05 16:34:15 +00:00
|
|
|
|
git = new GitCommand (LocalPath, "merge --no-edit FETCH_HEAD");
|
2012-11-11 19:15:42 +00:00
|
|
|
|
git.StartInfo.RedirectStandardOutput = false;
|
|
|
|
|
|
|
|
|
|
string error_output = git.StartAndReadStandardError ();
|
|
|
|
|
|
2011-05-18 18:57:52 +00:00
|
|
|
|
if (git.ExitCode != 0) {
|
2013-12-02 20:52:17 +00:00
|
|
|
|
// Stop when we can't merge due to locked local files
|
2012-11-11 19:15:42 +00:00
|
|
|
|
// error: cannot stat 'filename': Permission denied
|
2012-11-17 19:03:37 +00:00
|
|
|
|
if (error_output.Contains ("error: cannot stat")) {
|
2013-03-11 17:02:30 +00:00
|
|
|
|
Error = ErrorStatus.UnreadableFiles;
|
2016-03-30 23:36:31 +00:00
|
|
|
|
Logger.LogInfo ("Git", Name + " | Error status changed to " + Error);
|
2012-11-17 19:03:37 +00:00
|
|
|
|
|
2016-03-30 23:36:31 +00:00
|
|
|
|
git = new GitCommand (LocalPath, "merge --abort");
|
2012-11-17 19:03:37 +00:00
|
|
|
|
git.StartAndWaitForExit ();
|
|
|
|
|
|
2016-03-30 23:36:31 +00:00
|
|
|
|
git = new GitCommand (LocalPath, "config core.ignorecase false");
|
2012-11-17 19:03:37 +00:00
|
|
|
|
git.StartAndWaitForExit ();
|
2012-11-11 19:15:42 +00:00
|
|
|
|
|
2012-11-17 19:03:37 +00:00
|
|
|
|
return false;
|
2018-04-09 03:41:59 +00:00
|
|
|
|
|
2012-11-21 09:22:29 +00:00
|
|
|
|
} else {
|
2016-03-30 23:36:31 +00:00
|
|
|
|
Logger.LogInfo ("Git", error_output);
|
|
|
|
|
Logger.LogInfo ("Git", Name + " | Conflict detected, trying to get out...");
|
2018-04-09 03:41:59 +00:00
|
|
|
|
|
2013-12-02 20:52:17 +00:00
|
|
|
|
while (this.in_merge && HasLocalChanges) {
|
2012-11-21 09:22:29 +00:00
|
|
|
|
try {
|
|
|
|
|
ResolveConflict ();
|
2012-07-02 21:42:49 +00:00
|
|
|
|
|
2014-04-20 10:34:01 +00:00
|
|
|
|
} catch (Exception e) {
|
2016-03-30 23:36:31 +00:00
|
|
|
|
Logger.LogInfo ("Git", Name + " | Failed to resolve conflict, trying again...", e);
|
2012-11-21 09:22:29 +00:00
|
|
|
|
}
|
2012-07-02 21:42:49 +00:00
|
|
|
|
}
|
2011-05-14 02:18:38 +00:00
|
|
|
|
|
2016-03-30 23:36:31 +00:00
|
|
|
|
Logger.LogInfo ("Git", Name + " | Conflict resolved");
|
2012-11-21 09:22:29 +00:00
|
|
|
|
}
|
2011-05-18 18:57:52 +00:00
|
|
|
|
}
|
2012-11-11 19:15:42 +00:00
|
|
|
|
|
2016-03-30 23:36:31 +00:00
|
|
|
|
git = new GitCommand (LocalPath, "config core.ignorecase false");
|
2012-11-11 19:15:42 +00:00
|
|
|
|
git.StartAndWaitForExit ();
|
2012-11-17 19:03:37 +00:00
|
|
|
|
|
|
|
|
|
return true;
|
2011-04-20 14:02:20 +00:00
|
|
|
|
}
|
2010-06-20 21:05:11 +00:00
|
|
|
|
|
2010-07-21 23:17:20 +00:00
|
|
|
|
|
2016-04-04 09:27:20 +00:00
|
|
|
|
void ResolveConflict ()
|
2011-04-28 11:49:14 +00:00
|
|
|
|
{
|
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
|
|
|
|
|
2016-04-04 09:27:20 +00:00
|
|
|
|
var git_status = new GitCommand (LocalPath, "status --porcelain");
|
2018-06-15 16:44:40 +00:00
|
|
|
|
string output = git_status.StartAndReadStandardOutput ();
|
2011-06-29 19:45:37 +00:00
|
|
|
|
|
2011-04-28 11:49:14 +00:00
|
|
|
|
string [] lines = output.Split ("\n".ToCharArray ());
|
2013-12-02 20:52:17 +00:00
|
|
|
|
bool trigger_conflict_event = false;
|
2011-04-28 11:49:14 +00:00
|
|
|
|
|
|
|
|
|
foreach (string line in lines) {
|
2017-12-16 16:56:13 +00:00
|
|
|
|
string conflicting_file_path = line.Substring (3);
|
2018-06-16 11:45:12 +00:00
|
|
|
|
conflicting_file_path = conflicting_file_path.Trim ("\"".ToCharArray ());
|
2011-04-30 00:42:48 +00:00
|
|
|
|
|
2014-04-20 10:34:01 +00:00
|
|
|
|
// Remove possible rename indicators
|
|
|
|
|
string [] separators = {" -> \"", " -> "};
|
|
|
|
|
foreach (string separator in separators) {
|
2017-12-16 16:56:13 +00:00
|
|
|
|
if (conflicting_file_path.Contains (separator))
|
|
|
|
|
conflicting_file_path = conflicting_file_path.Substring (conflicting_file_path.IndexOf (separator) + separator.Length);
|
2014-04-20 10:34:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-03-30 23:36:31 +00:00
|
|
|
|
Logger.LogInfo ("Git", Name + " | Conflict type: " + line);
|
2011-05-06 10:31:30 +00:00
|
|
|
|
|
2014-04-20 10:34:01 +00:00
|
|
|
|
// Ignore conflicts in hidden files and use the local versions
|
2017-12-16 16:56:13 +00:00
|
|
|
|
if (conflicting_file_path.EndsWith (".sparkleshare") || conflicting_file_path.EndsWith (".empty")) {
|
|
|
|
|
Logger.LogInfo ("Git", Name + " | Ignoring conflict in special file: " + conflicting_file_path);
|
2013-06-29 10:48:36 +00:00
|
|
|
|
|
2012-06-26 23:13:17 +00:00
|
|
|
|
// Recover local version
|
2017-12-16 16:56:13 +00:00
|
|
|
|
var git_ours = new GitCommand (LocalPath, "checkout --ours \"" + conflicting_file_path + "\"");
|
2013-12-02 20:52:17 +00:00
|
|
|
|
git_ours.StartAndWaitForExit ();
|
2012-06-26 23:13:17 +00:00
|
|
|
|
|
2017-12-16 16:56:13 +00:00
|
|
|
|
string abs_conflicting_path = Path.Combine (LocalPath, conflicting_file_path);
|
2014-04-20 10:34:01 +00:00
|
|
|
|
|
|
|
|
|
if (File.Exists (abs_conflicting_path))
|
|
|
|
|
File.SetAttributes (abs_conflicting_path, FileAttributes.Hidden);
|
2017-12-16 13:52:38 +00:00
|
|
|
|
|
2012-06-26 23:13:17 +00:00
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-16 16:56:13 +00:00
|
|
|
|
Logger.LogInfo ("Git", Name + " | Resolving: " + conflicting_file_path);
|
2013-07-06 19:00:27 +00:00
|
|
|
|
|
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")) {
|
|
|
|
|
|
2017-12-16 16:56:13 +00:00
|
|
|
|
// Get the author name of the conflicting version
|
|
|
|
|
var git_log = new GitCommand (LocalPath, "log -n 1 FETCH_HEAD --pretty=format:%an " + conflicting_file_path);
|
|
|
|
|
string other_author_name = git_log.StartAndReadStandardOutput ();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Generate distinguishing names for both versions of the file
|
|
|
|
|
string clue_A = string.Format (" (by {0})", base.local_config.User.Name);
|
|
|
|
|
string clue_B = string.Format (" (by {0})", other_author_name);
|
|
|
|
|
|
|
|
|
|
if (base.local_config.User.Name == other_author_name) {
|
|
|
|
|
clue_A = " (A)";
|
|
|
|
|
clue_B = " (B)";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
string file_name_A = Path.GetFileNameWithoutExtension (conflicting_file_path) + clue_A + Path.GetExtension (conflicting_file_path);
|
|
|
|
|
string file_name_B = Path.GetFileNameWithoutExtension (conflicting_file_path) + clue_B + Path.GetExtension (conflicting_file_path);
|
|
|
|
|
|
|
|
|
|
string abs_conflicting_file_path = Path.Combine (LocalPath, conflicting_file_path);
|
2018-04-09 03:41:59 +00:00
|
|
|
|
|
2017-12-16 16:56:13 +00:00
|
|
|
|
string abs_file_path_A = Path.Combine (Path.GetDirectoryName (abs_conflicting_file_path), file_name_A);
|
|
|
|
|
string abs_file_path_B = Path.Combine (Path.GetDirectoryName (abs_conflicting_file_path), file_name_B);
|
2011-04-28 11:49:14 +00:00
|
|
|
|
|
2017-12-16 13:52:38 +00:00
|
|
|
|
|
2017-12-16 16:56:13 +00:00
|
|
|
|
// Recover local version
|
|
|
|
|
var git_checkout_A = new GitCommand (LocalPath, "checkout --ours \"" + conflicting_file_path + "\"");
|
|
|
|
|
git_checkout_A.StartAndWaitForExit ();
|
2012-07-01 11:30:33 +00:00
|
|
|
|
|
2017-12-16 16:56:13 +00:00
|
|
|
|
if (File.Exists (abs_conflicting_file_path) && !File.Exists (abs_file_path_A))
|
|
|
|
|
File.Move (abs_conflicting_file_path, abs_file_path_A);
|
2011-04-28 11:49:14 +00:00
|
|
|
|
|
2011-04-30 00:42:48 +00:00
|
|
|
|
|
|
|
|
|
// Recover server version
|
2017-12-16 16:56:13 +00:00
|
|
|
|
var git_checkout_B = new GitCommand (LocalPath, "checkout --theirs \"" + conflicting_file_path + "\"");
|
|
|
|
|
git_checkout_B.StartAndWaitForExit ();
|
|
|
|
|
|
|
|
|
|
if (File.Exists (abs_conflicting_file_path) && !File.Exists (abs_file_path_B))
|
|
|
|
|
File.Move (abs_conflicting_file_path, abs_file_path_B);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Recover original (before both versions diverged)
|
|
|
|
|
var git_checkout = new GitCommand (LocalPath, "checkout ORIG_HEAD^ \"" + conflicting_file_path + "\"");
|
|
|
|
|
git_checkout.StartAndWaitForExit ();
|
|
|
|
|
|
2011-04-28 11:49:14 +00:00
|
|
|
|
|
2013-12-02 20:52:17 +00:00
|
|
|
|
trigger_conflict_event = true;
|
2012-07-02 21:10:03 +00:00
|
|
|
|
|
2017-12-16 16:56:13 +00:00
|
|
|
|
|
2013-12-02 20:52:17 +00:00
|
|
|
|
// The server version has been modified, but the local version was removed
|
2012-04-22 12:32:55 +00:00
|
|
|
|
} else if (line.StartsWith ("DU")) {
|
2013-06-29 10:48:36 +00:00
|
|
|
|
|
2012-11-22 12:31: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
|
2017-12-16 16:56:13 +00:00
|
|
|
|
var git_add = new GitCommand (LocalPath, "add \"" + conflicting_file_path + "\"");
|
2012-07-26 10:12:14 +00:00
|
|
|
|
git_add.StartAndWaitForExit ();
|
2011-04-30 00:42:48 +00:00
|
|
|
|
|
2018-04-09 03:41:59 +00:00
|
|
|
|
|
2013-12-02 20:52:17 +00:00
|
|
|
|
// The local version has been modified, but the server version was removed
|
2013-07-06 19:00:27 +00:00
|
|
|
|
} else if (line.StartsWith ("UD")) {
|
2018-04-09 03:41:59 +00:00
|
|
|
|
|
2017-12-16 17:16:21 +00:00
|
|
|
|
// Recover our version
|
|
|
|
|
var git_theirs = new GitCommand (LocalPath, "checkout --ours \"" + conflicting_file_path + "\"");
|
2013-07-06 19:00:27 +00:00
|
|
|
|
git_theirs.StartAndWaitForExit ();
|
2013-07-10 16:11:16 +00:00
|
|
|
|
|
2018-04-09 03:41:59 +00:00
|
|
|
|
|
2013-07-06 19:00:27 +00:00
|
|
|
|
// Server and local versions were removed
|
|
|
|
|
} else if (line.StartsWith ("DD")) {
|
2016-03-30 23:36:31 +00:00
|
|
|
|
Logger.LogInfo ("Git", Name + " | No need to resolve: " + line);
|
2013-07-06 19:00:27 +00:00
|
|
|
|
|
|
|
|
|
// New local files
|
|
|
|
|
} else if (line.StartsWith ("??")) {
|
2016-03-30 23:36:31 +00:00
|
|
|
|
Logger.LogInfo ("Git", Name + " | Found new file, no need to resolve: " + line);
|
2018-04-09 03:41:59 +00:00
|
|
|
|
|
2013-07-10 16:11:16 +00:00
|
|
|
|
} else {
|
2016-03-30 23:36:31 +00:00
|
|
|
|
Logger.LogInfo ("Git", Name + " | Don't know what to do with: " + line);
|
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 ();
|
|
|
|
|
|
2017-02-03 01:03:01 +00:00
|
|
|
|
var git = new GitCommand (LocalPath,
|
|
|
|
|
"commit --message=\"Conflict resolution\" --author=\"SparkleShare <info@sparkleshare.org>\"");
|
|
|
|
|
|
2012-07-02 21:10:03 +00:00
|
|
|
|
git.StartInfo.RedirectStandardOutput = false;
|
2012-07-26 10:12:14 +00:00
|
|
|
|
git.StartAndWaitForExit ();
|
2013-12-02 20:52:17 +00:00
|
|
|
|
|
2017-12-16 17:17:16 +00:00
|
|
|
|
HasUnsyncedChanges = true;
|
|
|
|
|
|
2013-12-02 20:52:17 +00:00
|
|
|
|
if (trigger_conflict_event)
|
|
|
|
|
OnConflictResolved ();
|
2011-04-28 11:49:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2012-10-14 13:21:22 +00:00
|
|
|
|
public override void RestoreFile (string path, string revision, string target_file_path)
|
2012-08-03 22:21:00 +00:00
|
|
|
|
{
|
|
|
|
|
if (path == null)
|
|
|
|
|
throw new ArgumentNullException ("path");
|
|
|
|
|
|
|
|
|
|
if (revision == null)
|
|
|
|
|
throw new ArgumentNullException ("revision");
|
2012-10-14 22:22:55 +00:00
|
|
|
|
|
2016-03-30 23:36:31 +00:00
|
|
|
|
Logger.LogInfo ("Git", Name + " | Restoring \"" + path + "\" (revision " + revision + ")");
|
2012-08-04 15:22:34 +00:00
|
|
|
|
|
2016-06-20 20:24:59 +00:00
|
|
|
|
// Restore the older file...
|
|
|
|
|
var git = new GitCommand (LocalPath, "checkout " + revision + " \"" + path + "\"");
|
|
|
|
|
git.StartAndWaitForExit ();
|
2012-10-14 18:57:13 +00:00
|
|
|
|
|
2016-06-20 20:24:59 +00:00
|
|
|
|
string local_file_path = Path.Combine (LocalPath, path);
|
2012-10-14 18:57:13 +00:00
|
|
|
|
|
2016-06-20 20:24:59 +00:00
|
|
|
|
// ...move it...
|
|
|
|
|
try {
|
|
|
|
|
File.Move (local_file_path, target_file_path);
|
2018-04-09 03:41:59 +00:00
|
|
|
|
|
2018-03-10 19:09:48 +00:00
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
string message = string.Format ("Failed to move \"{0}\" to \"{1}\"", local_file_path, target_file_path);
|
|
|
|
|
Logger.LogInfo ("Git", Name + " | " + message, e);
|
2016-06-20 20:24:59 +00:00
|
|
|
|
}
|
2012-10-14 18:57:13 +00:00
|
|
|
|
|
2016-06-20 20:24:59 +00:00
|
|
|
|
// ...and restore the most recent revision
|
|
|
|
|
git = new GitCommand (LocalPath, "checkout " + CurrentRevision + " \"" + path + "\"");
|
|
|
|
|
git.StartAndWaitForExit ();
|
2012-10-14 18:57:13 +00:00
|
|
|
|
|
|
|
|
|
if (target_file_path.StartsWith (LocalPath))
|
|
|
|
|
new Thread (() => OnFileActivity (null)).Start ();
|
2012-08-03 22:21:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2016-03-30 23:56:48 +00:00
|
|
|
|
public override List<Change> UnsyncedChanges {
|
2014-10-28 21:59:57 +00:00
|
|
|
|
get {
|
|
|
|
|
return ParseStatus ();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2016-03-30 23:36:31 +00:00
|
|
|
|
public override List<ChangeSet> GetChangeSets ()
|
2013-07-06 19:00:27 +00:00
|
|
|
|
{
|
|
|
|
|
return GetChangeSetsInternal (null);
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-30 23:36:31 +00:00
|
|
|
|
public override List<ChangeSet> GetChangeSets (string path)
|
2013-07-06 19:00:27 +00:00
|
|
|
|
{
|
|
|
|
|
return GetChangeSetsInternal (path);
|
2018-04-09 03:41:59 +00:00
|
|
|
|
}
|
2013-07-06 19:00:27 +00:00
|
|
|
|
|
2016-04-04 09:27:20 +00:00
|
|
|
|
List<ChangeSet> GetChangeSetsInternal (string path)
|
2011-04-20 14:02:20 +00:00
|
|
|
|
{
|
2016-04-04 09:27:20 +00:00
|
|
|
|
var change_sets = new List <ChangeSet> ();
|
2016-03-30 23:36:31 +00:00
|
|
|
|
GitCommand git;
|
2012-05-27 18:24:12 +00:00
|
|
|
|
|
2018-02-24 20:38:46 +00:00
|
|
|
|
string log_args = "--since=1.month --name-status --date=iso --find-renames --no-merges --no-color";
|
|
|
|
|
|
2012-08-03 22:21:00 +00:00
|
|
|
|
if (path == null) {
|
2018-02-24 20:38:46 +00:00
|
|
|
|
git = new GitCommand (LocalPath, "--no-pager log " + log_args);
|
2011-04-20 14:02:20 +00:00
|
|
|
|
|
2012-08-03 22:21:00 +00:00
|
|
|
|
} else {
|
2012-08-04 15:22:34 +00:00
|
|
|
|
path = path.Replace ("\\", "/");
|
2018-02-24 20:38:46 +00:00
|
|
|
|
git = new GitCommand (LocalPath, "--no-pager log " + log_args + " -- \"" + path + "\"");
|
2012-08-03 22:21:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string output = git.StartAndReadStandardOutput ();
|
|
|
|
|
|
2013-01-11 18:38:49 +00:00
|
|
|
|
if (path == null && string.IsNullOrWhiteSpace (output)) {
|
2018-02-24 20:38:46 +00:00
|
|
|
|
git = new GitCommand (LocalPath, "--no-pager log -n 75 " + log_args);
|
2013-01-11 18:38:49 +00:00
|
|
|
|
output = git.StartAndReadStandardOutput ();
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-20 14:02:20 +00:00
|
|
|
|
|
2018-02-25 16:12:23 +00:00
|
|
|
|
// Offset the output so our log_regex can be simpler
|
|
|
|
|
string commit_sep = "commit ";
|
2011-04-20 14:02:20 +00:00
|
|
|
|
|
2018-02-25 16:12:23 +00:00
|
|
|
|
if (output.StartsWith (commit_sep))
|
|
|
|
|
output = output.Substring (commit_sep.Length) + "\n\n" + commit_sep;
|
2013-12-02 20:52:17 +00:00
|
|
|
|
|
2011-05-19 16:05:58 +00:00
|
|
|
|
|
2018-02-25 16:12:23 +00:00
|
|
|
|
MatchCollection matches = this.log_regex.Matches (output);
|
2011-04-20 14:02:20 +00:00
|
|
|
|
|
2018-02-25 16:12:23 +00:00
|
|
|
|
foreach (Match match in matches) {
|
|
|
|
|
ChangeSet change_set = ParseChangeSet (match);
|
2017-02-03 01:05:00 +00:00
|
|
|
|
|
2018-02-25 16:12:23 +00:00
|
|
|
|
if (change_set == null)
|
2017-02-03 01:05:00 +00:00
|
|
|
|
continue;
|
|
|
|
|
|
2018-02-25 16:12:23 +00:00
|
|
|
|
int count = 0;
|
|
|
|
|
foreach (string line in match.Groups ["files"].Value.Split ("\n".ToCharArray ())) {
|
|
|
|
|
if (count++ == 256)
|
|
|
|
|
break;
|
2013-02-14 20:21:23 +00:00
|
|
|
|
|
2018-02-25 16:12:23 +00:00
|
|
|
|
Change change = ParseChange (line);
|
2013-06-29 17:34:36 +00:00
|
|
|
|
|
2018-02-24 20:38:46 +00:00
|
|
|
|
if (change == null)
|
2013-07-06 15:04:59 +00:00
|
|
|
|
continue;
|
2011-05-19 16:05:58 +00:00
|
|
|
|
|
2018-02-24 20:38:46 +00:00
|
|
|
|
change.Timestamp = change_set.Timestamp;
|
2013-06-29 17:34:36 +00:00
|
|
|
|
change_set.Changes.Add (change);
|
|
|
|
|
}
|
2012-05-27 18:24:12 +00:00
|
|
|
|
|
2018-02-25 16:12:23 +00:00
|
|
|
|
|
|
|
|
|
if (path == null && change_sets.Count > 0) {
|
2016-03-30 23:36:31 +00:00
|
|
|
|
ChangeSet last_change_set = change_sets [change_sets.Count - 1];
|
2012-05-27 18:24:12 +00:00
|
|
|
|
|
2018-02-25 16:12:23 +00:00
|
|
|
|
// If a change set set already exists for this user and day, group into that one
|
|
|
|
|
if (change_set.Timestamp.Year == last_change_set.Timestamp.Year &&
|
2013-06-29 17:34:36 +00:00
|
|
|
|
change_set.Timestamp.Month == last_change_set.Timestamp.Month &&
|
2018-02-25 16:12:23 +00:00
|
|
|
|
change_set.Timestamp.Day == last_change_set.Timestamp.Day &&
|
2013-06-29 17:34:36 +00:00
|
|
|
|
change_set.User.Name.Equals (last_change_set.User.Name)) {
|
2012-05-27 18:24:12 +00:00
|
|
|
|
|
2013-06-29 17:34:36 +00:00
|
|
|
|
last_change_set.Changes.AddRange (change_set.Changes);
|
2012-05-27 18:24:12 +00:00
|
|
|
|
|
2013-06-29 17:34:36 +00:00
|
|
|
|
if (DateTime.Compare (last_change_set.Timestamp, change_set.Timestamp) < 1) {
|
|
|
|
|
last_change_set.FirstTimestamp = last_change_set.Timestamp;
|
2018-02-25 16:12:23 +00:00
|
|
|
|
last_change_set.Timestamp = change_set.Timestamp;
|
|
|
|
|
last_change_set.Revision = change_set.Revision;
|
2012-05-27 18:24:12 +00:00
|
|
|
|
|
|
|
|
|
} else {
|
2013-06-29 17:34:36 +00:00
|
|
|
|
last_change_set.FirstTimestamp = change_set.Timestamp;
|
2012-05-27 18:24:12 +00:00
|
|
|
|
}
|
2012-08-03 22:21:00 +00:00
|
|
|
|
|
|
|
|
|
} else {
|
2013-06-29 17:34:36 +00:00
|
|
|
|
change_sets.Add (change_set);
|
|
|
|
|
}
|
2012-08-03 22:21:00 +00:00
|
|
|
|
|
2018-02-25 16:12:23 +00:00
|
|
|
|
} else if (path != null) {
|
|
|
|
|
// Don't show removals or moves in the history list of a file
|
|
|
|
|
var changes = new Change [change_set.Changes.Count];
|
|
|
|
|
change_set.Changes.CopyTo (changes);
|
2012-08-03 22:21:00 +00:00
|
|
|
|
|
2018-02-25 16:12:23 +00:00
|
|
|
|
foreach (Change change in changes) {
|
|
|
|
|
if (!change.Path.Equals (path))
|
|
|
|
|
continue;
|
2013-06-29 17:34:36 +00:00
|
|
|
|
|
2018-02-25 16:12:23 +00:00
|
|
|
|
if (change.Type == ChangeType.Deleted || change.Type == ChangeType.Moved)
|
|
|
|
|
change_set.Changes.Remove (change);
|
2011-07-17 00:22:39 +00:00
|
|
|
|
}
|
2018-02-25 16:12:23 +00:00
|
|
|
|
|
|
|
|
|
change_sets.Add (change_set);
|
|
|
|
|
|
|
|
|
|
} else {
|
2013-06-29 17:34:36 +00:00
|
|
|
|
change_sets.Add (change_set);
|
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-07-01 08:46:42 +00:00
|
|
|
|
|
2018-02-25 16:12:23 +00:00
|
|
|
|
ChangeSet ParseChangeSet (Match match)
|
|
|
|
|
{
|
|
|
|
|
ChangeSet change_set = new ChangeSet ();
|
|
|
|
|
|
|
|
|
|
// Set the name and email
|
|
|
|
|
if (match.Groups ["name"].Value == "SparkleShare")
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
change_set.Folder = new SparkleFolder (Name);
|
|
|
|
|
change_set.Revision = match.Groups ["commit"].Value;
|
|
|
|
|
change_set.User = new User (match.Groups ["name"].Value, match.Groups ["email"].Value);
|
|
|
|
|
change_set.RemoteUrl = RemoteUrl;
|
|
|
|
|
|
|
|
|
|
if (StorageType == StorageType.Encrypted) {
|
|
|
|
|
string password_file_path = Path.Combine (LocalPath, ".git", "info", "encryption_password");
|
|
|
|
|
string password = File.ReadAllText (password_file_path);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
change_set.User = new User (
|
|
|
|
|
change_set.User.Name.AESDecrypt (password),
|
|
|
|
|
change_set.User.Email.AESDecrypt (password));
|
|
|
|
|
|
2018-06-16 11:45:12 +00:00
|
|
|
|
} catch (Exception) {
|
2018-02-25 16:12:23 +00:00
|
|
|
|
change_set.User = new User (match.Groups ["name"].Value, match.Groups ["email"].Value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get the right date and time by offsetting the timezones
|
|
|
|
|
change_set.Timestamp = new DateTime (
|
|
|
|
|
int.Parse (match.Groups ["year"].Value), int.Parse (match.Groups ["month"].Value), int.Parse (match.Groups ["day"].Value),
|
|
|
|
|
int.Parse (match.Groups ["hour"].Value), int.Parse (match.Groups ["minute"].Value), int.Parse (match.Groups ["second"].Value));
|
|
|
|
|
|
|
|
|
|
string time_zone = match.Groups ["timezone"].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);
|
|
|
|
|
|
|
|
|
|
return change_set;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Change ParseChange (string line)
|
2018-02-24 20:38:46 +00:00
|
|
|
|
{
|
|
|
|
|
// Skip lines containing backspace characters or the .sparkleshare file
|
|
|
|
|
if (line.Contains ("\\177") || line.Contains (".sparkleshare"))
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
// File lines start with a change type letter and then a tab character
|
|
|
|
|
if (!line.StartsWith ("A\t") &&
|
|
|
|
|
!line.StartsWith ("M\t") &&
|
|
|
|
|
!line.StartsWith ("D\t") &&
|
|
|
|
|
!line.StartsWith ("R100\t")) {
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Change change = new Change () { Type = ChangeType.Added };
|
|
|
|
|
string file_path;
|
2018-02-25 16:12:23 +00:00
|
|
|
|
|
2018-02-24 20:47:42 +00:00
|
|
|
|
int first_tab_pos = line.IndexOf ('\t');
|
2018-02-24 20:38:46 +00:00
|
|
|
|
int last_tab_pos = line.LastIndexOf ('\t');
|
|
|
|
|
|
|
|
|
|
if (first_tab_pos == last_tab_pos) {
|
|
|
|
|
char type_letter = line [0];
|
|
|
|
|
|
|
|
|
|
if (type_letter == 'M')
|
|
|
|
|
change.Type = ChangeType.Edited;
|
|
|
|
|
|
|
|
|
|
if (type_letter == 'D')
|
|
|
|
|
change.Type = ChangeType.Deleted;
|
|
|
|
|
|
|
|
|
|
file_path = line.Substring (first_tab_pos + 1);
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
change.Type = ChangeType.Moved;
|
|
|
|
|
|
|
|
|
|
// The "to" and "from" file paths are separated by a tab
|
|
|
|
|
string [] parts = line.Split ("\t".ToCharArray ());
|
|
|
|
|
|
|
|
|
|
file_path = parts [1];
|
|
|
|
|
string to_file_path = parts [2];
|
|
|
|
|
|
|
|
|
|
to_file_path = to_file_path.Replace ("\\\"", "\"");
|
|
|
|
|
change.MovedToPath = to_file_path;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
file_path = file_path.Replace ("\\\"", "\"");
|
|
|
|
|
change.Path = file_path;
|
|
|
|
|
|
2018-02-25 16:12:23 +00:00
|
|
|
|
string empty_name = ".empty";
|
|
|
|
|
|
2018-02-24 20:38:46 +00:00
|
|
|
|
// Handle .empty files as if they were folders
|
2018-02-25 16:12:23 +00:00
|
|
|
|
if (change.Path.EndsWith (empty_name)) {
|
|
|
|
|
change.Path = change.Path.Substring (0, change.Path.Length - empty_name.Length);
|
2018-02-24 20:38:46 +00:00
|
|
|
|
|
|
|
|
|
if (change.Type == ChangeType.Moved)
|
2018-02-25 16:12:23 +00:00
|
|
|
|
change.MovedToPath = change.MovedToPath.Substring (0, change.MovedToPath.Length - empty_name.Length);
|
2018-02-24 20:38:46 +00:00
|
|
|
|
|
|
|
|
|
change.IsFolder = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return change;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-06-16 11:41:13 +00:00
|
|
|
|
// The pre-push hook may have been changed by Git LFS, overwrite it to use our own configuration
|
|
|
|
|
void PrepareGitLFS ()
|
|
|
|
|
{
|
|
|
|
|
string pre_push_hook_path = Path.Combine (LocalPath, ".git", "hooks", "pre-push");
|
|
|
|
|
string pre_push_hook_content;
|
|
|
|
|
|
|
|
|
|
if (InstallationInfo.OperatingSystem == OS.macOS || InstallationInfo.OperatingSystem == OS.Windows) {
|
|
|
|
|
pre_push_hook_content =
|
|
|
|
|
"#!/bin/sh" + Environment.NewLine +
|
|
|
|
|
"env GIT_SSH_COMMAND='" + GitCommand.FormatGitSSHCommand (auth_info) + "' " +
|
|
|
|
|
Path.Combine (Configuration.DefaultConfiguration.BinPath, "git-lfs").Replace ("\\", "/") + " pre-push \"$@\"";
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
pre_push_hook_content =
|
|
|
|
|
"#!/bin/sh" + Environment.NewLine +
|
|
|
|
|
"env GIT_SSH_COMMAND='" + GitCommand.FormatGitSSHCommand (auth_info) + "' " +
|
|
|
|
|
"git-lfs pre-push \"$@\"";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (InstallationInfo.OperatingSystem != OS.Windows) {
|
|
|
|
|
// TODO: Use proper API
|
|
|
|
|
var chmod = new Command ("chmod", "700 " + pre_push_hook_path);
|
|
|
|
|
chmod.StartAndWaitForExit ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Directory.CreateDirectory (Path.GetDirectoryName (pre_push_hook_path));
|
|
|
|
|
File.WriteAllText (pre_push_hook_path, pre_push_hook_content);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
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
|
2016-04-04 09:27:20 +00:00
|
|
|
|
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-07-28 13:58:09 +00:00
|
|
|
|
if (IsSymlink (child_path))
|
2012-04-17 17:00:05 +00:00
|
|
|
|
continue;
|
|
|
|
|
|
2012-04-17 09:05:47 +00:00
|
|
|
|
if (child_path.EndsWith (".git")) {
|
|
|
|
|
if (child_path.Equals (Path.Combine (LocalPath, ".git")))
|
|
|
|
|
continue;
|
2016-06-20 20:19:20 +00:00
|
|
|
|
|
2012-03-12 19:56:06 +00:00
|
|
|
|
string HEAD_file_path = Path.Combine (child_path, "HEAD");
|
2018-04-09 03:41:59 +00:00
|
|
|
|
|
2012-03-12 19:56:06 +00:00
|
|
|
|
if (File.Exists (HEAD_file_path)) {
|
|
|
|
|
File.Move (HEAD_file_path, HEAD_file_path + ".backup");
|
2016-03-30 23:36:31 +00:00
|
|
|
|
Logger.LogInfo ("Git", Name + " | Renamed " + HEAD_file_path);
|
2012-03-12 19:56:06 +00:00
|
|
|
|
}
|
2018-04-09 03:41:59 +00:00
|
|
|
|
|
2012-03-12 19:56:06 +00:00
|
|
|
|
continue;
|
2012-01-16 19:58:25 +00:00
|
|
|
|
}
|
2018-04-09 03:41:59 +00:00
|
|
|
|
|
2012-03-12 19:56:06 +00:00
|
|
|
|
PrepareDirectories (child_path);
|
|
|
|
|
}
|
2018-04-09 03:41:59 +00:00
|
|
|
|
|
2012-03-12 19:56:06 +00:00
|
|
|
|
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);
|
2012-07-18 12:49:11 +00:00
|
|
|
|
|
2018-03-10 19:09:48 +00:00
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
Logger.LogInfo ("Git", Name + " | Failed adding empty folder " + path, e);
|
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) {
|
2016-03-30 23:36:31 +00:00
|
|
|
|
Logger.LogInfo ("Git", "Failed preparing directory", e);
|
2011-11-30 14:04:56 +00:00
|
|
|
|
}
|
2011-09-14 17:30:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2011-04-21 12:25:28 +00:00
|
|
|
|
|
2016-04-04 09:27:20 +00:00
|
|
|
|
List<Change> ParseStatus ()
|
2014-10-28 21:21:55 +00:00
|
|
|
|
{
|
2016-03-30 23:56:48 +00:00
|
|
|
|
List<Change> changes = new List<Change> ();
|
2014-11-08 17:26:09 +00:00
|
|
|
|
|
2016-04-04 09:27:20 +00:00
|
|
|
|
var git_status = new GitCommand (LocalPath, "status --porcelain");
|
2011-04-21 12:25:28 +00:00
|
|
|
|
git_status.Start ();
|
2018-04-09 03:41:59 +00:00
|
|
|
|
|
2012-07-02 19:09:46 +00:00
|
|
|
|
while (!git_status.StandardOutput.EndOfStream) {
|
|
|
|
|
string line = git_status.StandardOutput.ReadLine ();
|
2018-06-15 16:44:40 +00:00
|
|
|
|
line = line.Trim ();
|
2018-04-09 03:41:59 +00:00
|
|
|
|
|
2012-07-02 19:09:46 +00:00
|
|
|
|
if (line.EndsWith (".empty") || line.EndsWith (".empty\""))
|
2012-09-23 11:10:51 +00:00
|
|
|
|
line = line.Replace (".empty", "");
|
2012-07-02 16:33:21 +00:00
|
|
|
|
|
2016-03-30 23:56:48 +00:00
|
|
|
|
Change change;
|
2018-04-09 03:41:59 +00:00
|
|
|
|
|
2013-05-04 21:06:27 +00:00
|
|
|
|
if (line.StartsWith ("R")) {
|
2014-10-31 11:10:49 +00:00
|
|
|
|
string path = line.Substring (3, line.IndexOf (" -> ") - 3).Trim ("\" ".ToCharArray ());
|
|
|
|
|
string moved_to_path = line.Substring (line.IndexOf (" -> ") + 4).Trim ("\" ".ToCharArray ());
|
2018-04-09 03:41:59 +00:00
|
|
|
|
|
2016-03-30 23:56:48 +00:00
|
|
|
|
change = new Change () {
|
|
|
|
|
Type = ChangeType.Moved,
|
2018-06-16 11:45:12 +00:00
|
|
|
|
Path = path,
|
|
|
|
|
MovedToPath = moved_to_path
|
2014-10-28 21:21:55 +00:00
|
|
|
|
};
|
2018-04-09 03:41:59 +00:00
|
|
|
|
|
2014-10-28 21:21:55 +00:00
|
|
|
|
} else {
|
2014-10-31 11:10:49 +00:00
|
|
|
|
string path = line.Substring (2).Trim ("\" ".ToCharArray ());
|
2018-06-16 11:45:12 +00:00
|
|
|
|
change = new Change () { Path = path };
|
2016-03-30 23:56:48 +00:00
|
|
|
|
change.Type = ChangeType.Added;
|
2011-04-21 12:25:28 +00:00
|
|
|
|
|
2014-10-28 21:21:55 +00:00
|
|
|
|
if (line.StartsWith ("M")) {
|
2016-03-30 23:56:48 +00:00
|
|
|
|
change.Type = ChangeType.Edited;
|
2018-04-09 03:41:59 +00:00
|
|
|
|
|
2014-10-28 21:21:55 +00:00
|
|
|
|
} else if (line.StartsWith ("D")) {
|
2016-03-30 23:56:48 +00:00
|
|
|
|
change.Type = ChangeType.Deleted;
|
2014-10-28 21:21:55 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
changes.Add (change);
|
|
|
|
|
}
|
2018-04-09 03:41:59 +00:00
|
|
|
|
|
2014-10-28 21:21:55 +00:00
|
|
|
|
git_status.StandardOutput.ReadToEnd ();
|
|
|
|
|
git_status.WaitForExit ();
|
2014-10-28 21:59:57 +00:00
|
|
|
|
|
|
|
|
|
return changes;
|
2014-10-28 21:21:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Creates a pretty commit message based on what has changed
|
2016-04-04 09:27:20 +00:00
|
|
|
|
string FormatCommitMessage ()
|
2014-10-28 21:21:55 +00:00
|
|
|
|
{
|
|
|
|
|
string message = "";
|
|
|
|
|
|
2016-03-30 23:56:48 +00:00
|
|
|
|
foreach (Change change in ParseStatus ()) {
|
|
|
|
|
if (change.Type == ChangeType.Moved) {
|
2018-06-15 16:45:34 +00:00
|
|
|
|
message += "< ‘" + change.Path + "’\n";
|
|
|
|
|
message += "> ‘" + change.MovedToPath + "’\n";
|
2011-05-17 21:37:22 +00:00
|
|
|
|
|
2012-07-02 19:09:46 +00:00
|
|
|
|
} else {
|
2016-04-04 09:27:20 +00:00
|
|
|
|
switch (change.Type) {
|
|
|
|
|
case ChangeType.Edited:
|
2012-07-02 19:09:46 +00:00
|
|
|
|
message += "/";
|
2016-04-04 09:27:20 +00:00
|
|
|
|
break;
|
|
|
|
|
case ChangeType.Deleted:
|
2012-07-02 19:09:46 +00:00
|
|
|
|
message += "-";
|
2016-04-04 09:27:20 +00:00
|
|
|
|
break;
|
|
|
|
|
case ChangeType.Added:
|
2012-07-02 19:09:46 +00:00
|
|
|
|
message += "+";
|
2016-04-04 09:27:20 +00:00
|
|
|
|
break;
|
2012-07-02 19:09:46 +00:00
|
|
|
|
}
|
2011-04-20 14:02:20 +00:00
|
|
|
|
|
2014-10-28 21:21:55 +00:00
|
|
|
|
message += " ‘" + change.Path + "’\n";
|
2012-07-02 19:09:46 +00:00
|
|
|
|
}
|
2011-04-20 14:02:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-06 11:21:54 +00:00
|
|
|
|
if (string.IsNullOrWhiteSpace (message))
|
|
|
|
|
return null;
|
|
|
|
|
else
|
|
|
|
|
return message;
|
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
|
2016-03-31 14:46:17 +00:00
|
|
|
|
long CalculateSizes (DirectoryInfo parent)
|
2011-12-15 15:15:29 +00:00
|
|
|
|
{
|
2013-01-11 18:58:14 +00:00
|
|
|
|
long size = 0;
|
2012-04-10 21:19:33 +00:00
|
|
|
|
|
2011-12-15 15:15:29 +00:00
|
|
|
|
try {
|
2013-01-11 18:58:14 +00:00
|
|
|
|
foreach (DirectoryInfo directory in parent.GetDirectories ()) {
|
2016-04-01 08:24:01 +00:00
|
|
|
|
if (directory.FullName.IsSymlink () ||
|
2018-04-09 03:41:59 +00:00
|
|
|
|
directory.Name.Equals (".git") ||
|
2013-01-11 18:58:14 +00:00
|
|
|
|
directory.Name.Equals ("rebase-apply")) {
|
2011-12-15 15:15:29 +00:00
|
|
|
|
|
2013-01-11 18:58:14 +00:00
|
|
|
|
continue;
|
|
|
|
|
}
|
2012-03-08 03:28:18 +00:00
|
|
|
|
|
2013-01-11 18:58:14 +00:00
|
|
|
|
size += CalculateSizes (directory);
|
2011-12-15 15:15:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-04-10 21:19:33 +00:00
|
|
|
|
} catch (Exception e) {
|
2016-03-30 23:36:31 +00:00
|
|
|
|
Logger.LogInfo ("Local", "Error calculating directory size", e);
|
2012-04-10 21:19:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
2013-01-11 18:58:14 +00:00
|
|
|
|
foreach (FileInfo file in parent.GetFiles ()) {
|
2016-04-01 08:24:01 +00:00
|
|
|
|
if (file.FullName.IsSymlink ())
|
2013-01-11 18:58:14 +00:00
|
|
|
|
continue;
|
2018-04-09 03:41:59 +00:00
|
|
|
|
|
2013-01-11 18:58:14 +00:00
|
|
|
|
if (file.Name.Equals (".empty"))
|
|
|
|
|
File.SetAttributes (file.FullName, FileAttributes.Hidden);
|
|
|
|
|
else
|
|
|
|
|
size += file.Length;
|
2012-12-25 06:31:36 +00:00
|
|
|
|
}
|
2018-04-09 03:41:59 +00:00
|
|
|
|
|
2012-04-10 21:19:33 +00:00
|
|
|
|
} catch (Exception e) {
|
2016-03-30 23:36:31 +00:00
|
|
|
|
Logger.LogInfo ("Local", "Error calculating file size", e);
|
2011-12-15 15:15:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return size;
|
|
|
|
|
}
|
2012-07-28 13:58:09 +00:00
|
|
|
|
|
2018-04-09 03:41:59 +00:00
|
|
|
|
|
2016-03-31 14:46:17 +00:00
|
|
|
|
bool IsSymlink (string file)
|
2012-07-28 13:58:09 +00:00
|
|
|
|
{
|
|
|
|
|
FileAttributes attributes = File.GetAttributes (file);
|
|
|
|
|
return ((attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint);
|
|
|
|
|
}
|
2016-06-18 18:32:07 +00:00
|
|
|
|
|
|
|
|
|
|
2018-02-25 16:12:23 +00:00
|
|
|
|
Regex log_regex = new Regex (
|
|
|
|
|
"(?'commit'[a-f0-9]{40})\n" +
|
|
|
|
|
"Author: (?'name'.+?) <(?'email'.+?)>\n" +
|
|
|
|
|
"Date: (?'year'[0-9]{4})-(?'month'[0-9]{2})-(?'day'[0-9]{2}) (?'hour'[0-9]{2}):(?'minute'[0-9]{2}):(?'second'[0-9]{2}) (?'timezone'.[0-9]{4})\n" +
|
|
|
|
|
"\n" +
|
|
|
|
|
" (?'message'.+?)\n" +
|
|
|
|
|
"\n" +
|
|
|
|
|
"(?'files'.+?)\n\ncommit ", RegexOptions.Singleline | RegexOptions.Compiled);
|
2011-04-20 14:02:20 +00:00
|
|
|
|
}
|
2010-07-24 12:32:05 +00:00
|
|
|
|
}
|