diff --git a/GitHub.Unity.sln.DotSettings b/GitHub.Unity.sln.DotSettings
index ded507d07..d4374a413 100644
--- a/GitHub.Unity.sln.DotSettings
+++ b/GitHub.Unity.sln.DotSettings
@@ -342,6 +342,7 @@
True
True
True
+ True
False
x64
\ No newline at end of file
diff --git a/README.md b/README.md
index 6e869e011..71571e1c0 100644
--- a/README.md
+++ b/README.md
@@ -47,8 +47,8 @@ The GitHub for Unity extension brings [Git](https://git-scm.com/) and GitHub int
### Requirements
-- Unity 5.4-5.6
- - We've only tested the extension so far on Unity 5.4 to 5.6. There's currently an blocker issue opened for 5.3 support, so we know it doesn't run there. There are some issues for 2017.x, so it may or may not run well on that version. Personal edition is fine.
+- Unity 5.4-2017.1
+ - We've only tested the extension so far on Unity 5.4 to 2017.1. There's currently an blocker issue opened for 5.3 support, so we know it doesn't run there. There are some issues for 2017.2, so it may or may not run well on that version. Personal edition is fine.
- Git and Git LFS 2.x
#### Git on macOS
diff --git a/common/properties.props b/common/properties.props
index 21be6dba0..9d7fd40c6 100644
--- a/common/properties.props
+++ b/common/properties.props
@@ -7,6 +7,7 @@
$(SolutionDir)\script\lib\
$(SolutionDir)\lib\
+ C:\Program Files\Unity\Editor\Data\Managed\
C:\Program Files (x86)\Unity\Editor\Data\Managed\
\Applications\Unity\Unity.app\Contents\Managed\
Debug
diff --git a/docs/contributing/how-to-build.md b/docs/contributing/how-to-build.md
index 597a624aa..3e9169196 100644
--- a/docs/contributing/how-to-build.md
+++ b/docs/contributing/how-to-build.md
@@ -9,14 +9,14 @@ This repository is LFS-enabled. To clone it, you should use a git client that su
- Visual Studio 2015+ or Mono 4.x + bash shell (git bash).
- Mono 5.x will not work
- `UnityEngine.dll` and `UnityEditor.dll`.
- - If you've installed Unity in the default location of `C:\Program Files\Unity` or `C:\Program Files (x86)\Unity`, the build will be able to reference these DLLs automatically. Otherwise, you'll need to copy these DLLs from your Unity installation into the `lib` directory in order for the build to work
+ - If you've installed Unity in the default location of `C:\Program Files\Unity` or `C:\Program Files (x86)\Unity`, the build will be able to reference these DLLs automatically. Otherwise, you'll need to copy these DLLs from `[Unity installation path]\Unity\Editor\Data\Managed` into the `lib` directory in order for the build to work
### MacOS
- Mono 4.x required.
- Mono 5.x will not work
- `UnityEngine.dll` and `UnityEditor.dll`.
- - If you've installed Unity in the default location of `/Applications/Unity`, the build will be able to reference these DLLs automatically. Otherwise, you'll need to copy these DLLs from your Unity installation into the `lib` directory in order for the build to work
+ - If you've installed Unity in the default location of `/Applications/Unity`, the build will be able to reference these DLLs automatically. Otherwise, you'll need to copy these DLLs from `[Unity installation path]/Unity.app/Contents/Managed` into the `lib` directory in order for the build to work
## How to Build
@@ -40,11 +40,11 @@ To be able to authenticate in GitHub for Unity, you'll need to:
- [Register a new developer application](https://github.com/settings/developers) in your profile.
- Copy [common/ApplicationInfo_Local.cs-example](../../common/ApplicationInfo_Local.cs-example) to `common/ApplicationInfo_Local.cs` and fill out the clientId/clientSecret fields for your application.
-The build needs to reference `UnityEngine.dll` and `UnityEditor.dll`. These DLLs are included with Unity. If you've installed Unity in the default location, the build will be able to find them automatically. If not, copy these DLLs from your Unity installation into the `lib` directory in order for the build to work.
+The build needs to reference `UnityEngine.dll` and `UnityEditor.dll`. These DLLs are included with Unity. If you've installed Unity in the default location, the build will be able to find them automatically. If not, copy these DLLs from `[your Unity installation path]\Unity\Editor\Data\Managed` into the `lib` directory in order for the build to work.
### Visual Studio
-To build with Visual Studio 2015 open the solution file `GitHub.Unity.sln`. Select `Build Solution` in the `Build` menu.
+To build with Visual Studio 2015+, open the solution file `GitHub.Unity.sln`. Select `Build Solution` in the `Build` menu.
### Mono and Bash (windows and mac)
diff --git a/src/GitHub.Api/Application/ApiClient.cs b/src/GitHub.Api/Application/ApiClient.cs
index c4ae1782d..d456e35af 100644
--- a/src/GitHub.Api/Application/ApiClient.cs
+++ b/src/GitHub.Api/Application/ApiClient.cs
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Threading;
using System.Threading.Tasks;
using Octokit;
@@ -20,20 +19,13 @@ public static IApiClient Create(UriString repositoryUrl, IKeychain keychain)
new GitHubClient(AppConfiguration.ProductHeader, credentialStore, hostAddress.ApiUri));
}
- private static readonly Unity.ILogging logger = Unity.Logging.GetLogger();
+ private static readonly ILogging logger = Logging.GetLogger();
public HostAddress HostAddress { get; }
public UriString OriginalUrl { get; }
private readonly IKeychain keychain;
private readonly IGitHubClient githubClient;
private readonly ILoginManager loginManager;
- private static readonly SemaphoreSlim sem = new SemaphoreSlim(1);
-
- IList organizationsCache;
- Octokit.User userCache;
-
- string owner;
- bool? isEnterprise;
public ApiClient(UriString hostUrl, IKeychain keychain, IGitHubClient githubClient)
{
@@ -58,7 +50,7 @@ private async Task LogoutInternal(UriString host)
await loginManager.Logout(host);
}
- public async Task CreateRepository(NewRepository newRepository, Action callback, string organization = null)
+ public async Task CreateRepository(NewRepository newRepository, Action callback, string organization = null)
{
Guard.ArgumentNotNull(callback, "callback");
try
@@ -72,21 +64,27 @@ public async Task CreateRepository(NewRepository newRepository, Action> callback)
+ public async Task GetOrganizations(Action onSuccess, Action onError = null)
{
- Guard.ArgumentNotNull(callback, "callback");
- var organizations = await GetOrganizationInternal();
- callback(organizations);
+ Guard.ArgumentNotNull(onSuccess, nameof(onSuccess));
+ await GetOrganizationInternal(onSuccess, onError);
}
- public async Task LoadKeychain(Action callback)
+ public async Task ValidateCurrentUser(Action onSuccess, Action onError = null)
{
- Guard.ArgumentNotNull(callback, "callback");
- var hasLoadedKeys = await LoadKeychainInternal();
- callback(hasLoadedKeys);
+ Guard.ArgumentNotNull(onSuccess, nameof(onSuccess));
+ try
+ {
+ await ValidateCurrentUserInternal();
+ onSuccess();
+ }
+ catch (Exception e)
+ {
+ onError?.Invoke(e);
+ }
}
- public async Task GetCurrentUser(Action callback)
+ public async Task GetCurrentUser(Action callback)
{
Guard.ArgumentNotNull(callback, "callback");
var user = await GetCurrentUserInternal();
@@ -189,29 +187,27 @@ public async Task ContinueLoginAsync(LoginResult loginResult, Func CreateRepositoryInternal(NewRepository newRepository, string organization)
+ private async Task CreateRepositoryInternal(NewRepository newRepository, string organization)
{
try
{
logger.Trace("Creating repository");
- if (!await LoadKeychainInternal())
- {
- throw new InvalidOperationException("The keychain did not load");
- }
+ await ValidateKeychain();
+ await ValidateCurrentUserInternal();
- Octokit.Repository repository;
+ GitHubRepository repository;
if (!string.IsNullOrEmpty(organization))
{
logger.Trace("Creating repository for organization");
- repository = await githubClient.Repository.Create(organization, newRepository);
+ repository = (await githubClient.Repository.Create(organization, newRepository)).ToGitHubRepository();
}
else
{
logger.Trace("Creating repository for user");
- repository = await githubClient.Repository.Create(newRepository);
+ repository = (await githubClient.Repository.Create(newRepository)).ToGitHubRepository();
}
logger.Trace("Created Repository");
@@ -224,16 +220,14 @@ public async Task ContinueLoginAsync(LoginResult loginResult, Func> GetOrganizationInternal()
+ private async Task GetOrganizationInternal(Action onSuccess, Action onError = null)
{
try
{
logger.Trace("Getting Organizations");
- if (!await LoadKeychainInternal())
- {
- return new List();
- }
+ await ValidateKeychain();
+ await ValidateCurrentUserInternal();
var organizations = await githubClient.Organization.GetAllForCurrent();
@@ -241,49 +235,63 @@ private async Task> GetOrganizationInternal()
if (organizations != null)
{
- organizationsCache = organizations.ToArray();
+ var array = organizations.Select(organization => new Organization() {
+ Name = organization.Name,
+ Login = organization.Login
+ }).ToArray();
+ onSuccess(array);
}
}
catch(Exception ex)
{
logger.Error(ex, "Error Getting Organizations");
- throw;
+ onError?.Invoke(ex);
}
-
- return organizationsCache;
}
- private async Task GetCurrentUserInternal()
+ private async Task GetCurrentUserInternal()
{
try
{
- logger.Trace("Getting Organizations");
-
- if (!await LoadKeychainInternal())
- {
- return null;
- }
+ logger.Trace("Getting Current User");
+ await ValidateKeychain();
- userCache = await githubClient.User.Current();
+ return (await githubClient.User.Current()).ToGitHubUser();
}
- catch(Exception ex)
+ catch (KeychainEmptyException)
+ {
+ logger.Warning("Keychain is empty");
+ throw;
+ }
+ catch (Exception ex)
{
logger.Error(ex, "Error Getting Current User");
throw;
}
+ }
+
+ private async Task ValidateCurrentUserInternal()
+ {
+ logger.Trace("Validating User");
- return userCache;
+ var apiUser = await GetCurrentUserInternal();
+ var apiUsername = apiUser.Login;
+
+ var cachedUsername = keychain.Connections.First().Username;
+
+ if (apiUsername != cachedUsername)
+ {
+ throw new TokenUsernameMismatchException(cachedUsername, apiUsername);
+ }
}
private async Task LoadKeychainInternal()
{
- logger.Trace("LoadKeychainInternal");
-
if (keychain.HasKeys)
{
if (!keychain.NeedsLoad)
{
- logger.Trace("LoadKeychainInternal: Has keys does not need load");
+ logger.Trace("LoadKeychainInternal: Previously Loaded");
return true;
}
@@ -293,6 +301,8 @@ private async Task LoadKeychainInternal()
var uriString = keychain.Connections.First().Host;
var keychainAdapter = await keychain.Load(uriString);
+ logger.Trace("LoadKeychainInternal: Loaded");
+
return keychainAdapter.OctokitCredentials != Credentials.Anonymous;
}
@@ -301,23 +311,54 @@ private async Task LoadKeychainInternal()
return false;
}
- public async Task ValidateCredentials()
+ private async Task ValidateKeychain()
{
- try
+ if (!await LoadKeychainInternal())
{
- var store = keychain.Connect(OriginalUrl);
-
- if (store.OctokitCredentials != Credentials.Anonymous)
- {
- var credential = store.Credential;
- await githubClient.Authorization.CheckApplicationAuthentication(ApplicationInfo.ClientId, credential.Token);
- }
+ throw new KeychainEmptyException();
}
- catch
- {
- return false;
- }
- return true;
}
}
+
+ class GitHubUser
+ {
+ public string Name { get; set; }
+ public string Login { get; set; }
+ }
+
+ class GitHubRepository
+ {
+ public string Name { get; set; }
+ public string CloneUrl { get; set; }
+ }
+
+ class ApiClientException : Exception
+ {
+ public ApiClientException()
+ { }
+
+ public ApiClientException(string message) : base(message)
+ { }
+
+ public ApiClientException(string message, Exception innerException) : base(message, innerException)
+ { }
+ }
+
+ class TokenUsernameMismatchException : ApiClientException
+ {
+ public string CachedUsername { get; }
+ public string CurrentUsername { get; }
+
+ public TokenUsernameMismatchException(string cachedUsername, string currentUsername)
+ {
+ CachedUsername = cachedUsername;
+ CurrentUsername = currentUsername;
+ }
+ }
+
+ class KeychainEmptyException : ApiClientException
+ {
+ public KeychainEmptyException()
+ { }
+ }
}
diff --git a/src/GitHub.Api/Application/ApplicationManagerBase.cs b/src/GitHub.Api/Application/ApplicationManagerBase.cs
index d4cd6b412..ffa1bf77d 100644
--- a/src/GitHub.Api/Application/ApplicationManagerBase.cs
+++ b/src/GitHub.Api/Application/ApplicationManagerBase.cs
@@ -12,7 +12,6 @@ abstract class ApplicationManagerBase : IApplicationManager
protected static ILogging Logger { get; } = Logging.GetLogger();
private RepositoryManager repositoryManager;
- private IBranchCache branchCache;
public ApplicationManagerBase(SynchronizationContext synchronizationContext)
{
@@ -22,7 +21,6 @@ public ApplicationManagerBase(SynchronizationContext synchronizationContext)
UIScheduler = TaskScheduler.FromCurrentSynchronizationContext();
ThreadingHelper.MainThreadScheduler = UIScheduler;
TaskManager = new TaskManager(UIScheduler);
- CacheManager = new CacheManager();
}
protected void Initialize()
@@ -41,7 +39,7 @@ protected void Initialize()
Logging.TracingEnabled = UserSettings.Get(Constants.TraceLoggingKey, false);
ProcessManager = new ProcessManager(Environment, Platform.GitEnvironment, CancellationToken);
Platform.Initialize(ProcessManager, TaskManager);
- GitClient = new GitClient(Environment, ProcessManager, Platform.CredentialManager, TaskManager);
+ GitClient = new GitClient(Environment, ProcessManager, TaskManager);
SetupMetrics();
}
@@ -82,11 +80,6 @@ private async Task SetupGit()
}
- public void SetupCache(IBranchCache bcache)
- {
- branchCache = bcache;
- }
-
public ITask InitializeRepository()
{
Logger.Trace("Running Repository Initialize");
@@ -134,7 +127,7 @@ public void RestartRepository()
{
if (Environment.RepositoryPath != null)
{
- repositoryManager = Unity.RepositoryManager.CreateInstance(Platform, TaskManager, UsageTracker, GitClient, Environment.RepositoryPath);
+ repositoryManager = Unity.RepositoryManager.CreateInstance(Platform, TaskManager, GitClient, Environment.RepositoryPath);
repositoryManager.Initialize();
Environment.Repository.Initialize(repositoryManager);
repositoryManager.Start();
@@ -221,9 +214,7 @@ public void Dispose()
public ISettings LocalSettings { get; protected set; }
public ISettings SystemSettings { get; protected set; }
public ISettings UserSettings { get; protected set; }
- public CacheManager CacheManager { get; private set; }
public IUsageTracker UsageTracker { get; protected set; }
-
protected TaskScheduler UIScheduler { get; private set; }
protected SynchronizationContext SynchronizationContext { get; private set; }
protected IRepositoryManager RepositoryManager { get { return repositoryManager; } }
diff --git a/src/GitHub.Api/Application/IApiClient.cs b/src/GitHub.Api/Application/IApiClient.cs
index 1ebbcd414..d4a87e3e2 100644
--- a/src/GitHub.Api/Application/IApiClient.cs
+++ b/src/GitHub.Api/Application/IApiClient.cs
@@ -9,14 +9,13 @@ interface IApiClient
{
HostAddress HostAddress { get; }
UriString OriginalUrl { get; }
- Task CreateRepository(NewRepository newRepository, Action callback, string organization = null);
- Task GetOrganizations(Action> callback);
+ Task CreateRepository(NewRepository newRepository, Action callback, string organization = null);
+ Task GetOrganizations(Action onSuccess, Action onError = null);
Task Login(string username, string password, Action need2faCode, Action result);
Task ContinueLogin(LoginResult loginResult, string code);
Task LoginAsync(string username, string password, Func need2faCode);
- Task ValidateCredentials();
Task Logout(UriString host);
- Task GetCurrentUser(Action callback);
- Task LoadKeychain(Action callback);
+ Task GetCurrentUser(Action callback);
+ Task ValidateCurrentUser(Action onSuccess, Action onError = null);
}
}
diff --git a/src/GitHub.Api/Application/IApplicationManager.cs b/src/GitHub.Api/Application/IApplicationManager.cs
index 7ae10272f..fd59878a8 100644
--- a/src/GitHub.Api/Application/IApplicationManager.cs
+++ b/src/GitHub.Api/Application/IApplicationManager.cs
@@ -15,7 +15,6 @@ public interface IApplicationManager : IDisposable
ISettings LocalSettings { get; }
ISettings UserSettings { get; }
ITaskManager TaskManager { get; }
- CacheManager CacheManager { get; }
IGitClient GitClient { get; }
IUsageTracker UsageTracker { get; }
diff --git a/src/GitHub.Api/Application/OctokitExtensions.cs b/src/GitHub.Api/Application/OctokitExtensions.cs
new file mode 100644
index 000000000..c53101c74
--- /dev/null
+++ b/src/GitHub.Api/Application/OctokitExtensions.cs
@@ -0,0 +1,21 @@
+namespace GitHub.Unity
+{
+ static class OctokitExtensions
+ {
+ public static GitHubUser ToGitHubUser(this Octokit.User user)
+ {
+ return new GitHubUser() {
+ Name = user.Name,
+ Login = user.Login,
+ };
+ }
+
+ public static GitHubRepository ToGitHubRepository(this Octokit.Repository repository)
+ {
+ return new GitHubRepository {
+ Name = repository.Name,
+ CloneUrl = repository.CloneUrl
+ };
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/GitHub.Api/Application/Organization.cs b/src/GitHub.Api/Application/Organization.cs
new file mode 100644
index 000000000..e78849dd6
--- /dev/null
+++ b/src/GitHub.Api/Application/Organization.cs
@@ -0,0 +1,8 @@
+namespace GitHub.Unity
+{
+ class Organization
+ {
+ public string Name { get; set; }
+ public string Login { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/GitHub.Api/Authentication/Keychain.cs b/src/GitHub.Api/Authentication/Keychain.cs
index b531735f9..bab311239 100644
--- a/src/GitHub.Api/Authentication/Keychain.cs
+++ b/src/GitHub.Api/Authentication/Keychain.cs
@@ -68,7 +68,7 @@ public async Task Load(UriString host)
{
if (keychainItem.Username != cachedConnection.Username)
{
- logger.Warning("Keychain Username: {0} does not match; Hopefully it works", keychainItem.Username);
+ logger.Warning("Keychain Username:\"{0}\" does not match cached Username:\"{1}\"; Hopefully it works", keychainItem.Username, cachedConnection.Username);
}
logger.Trace("Loaded from Credential Manager Host:\"{0}\" Username:\"{1}\"", keychainItem.Host, keychainItem.Username);
diff --git a/src/GitHub.Api/Cache/CacheInterfaces.cs b/src/GitHub.Api/Cache/CacheInterfaces.cs
new file mode 100644
index 000000000..a303520fa
--- /dev/null
+++ b/src/GitHub.Api/Cache/CacheInterfaces.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Collections.Generic;
+
+namespace GitHub.Unity
+{
+ public enum CacheType
+ {
+ RepositoryInfoCache,
+ BranchCache,
+ GitLogCache,
+ GitStatusCache,
+ GitLocksCache,
+ GitUserCache
+ }
+
+ public interface ICacheContainer
+ {
+ event Action CacheInvalidated;
+ event Action CacheUpdated;
+
+ IBranchCache BranchCache { get; }
+ IGitLogCache GitLogCache { get; }
+ IGitStatusCache GitStatusCache { get; }
+ IGitLocksCache GitLocksCache { get; }
+ IGitUserCache GitUserCache { get; }
+ IRepositoryInfoCache RepositoryInfoCache { get; }
+ void Validate(CacheType cacheType);
+ void ValidateAll();
+ void Invalidate(CacheType cacheType);
+ void InvalidateAll();
+ }
+
+ public interface IManagedCache
+ {
+ event Action CacheInvalidated;
+ event Action CacheUpdated;
+
+ void ValidateData();
+ void InvalidateData();
+
+ DateTimeOffset LastUpdatedAt { get; }
+ DateTimeOffset LastVerifiedAt { get; }
+ }
+
+ public interface IGitLocksCache : IManagedCache
+ {
+ List GitLocks { get; set; }
+ }
+
+ public interface IGitUserCache : IManagedCache
+ {
+ User User { get; }
+ }
+
+ public interface IGitStatusCache : IManagedCache
+ {
+ GitStatus GitStatus { get; set; }
+ }
+
+ public interface ILocalConfigBranchDictionary : IDictionary
+ {
+
+ }
+
+ public interface IRemoteConfigBranchDictionary : IDictionary>
+ {
+
+ }
+
+ public interface IConfigRemoteDictionary : IDictionary
+ {
+
+ }
+
+ public interface IBranchCache : IManagedCache
+ {
+ ConfigRemote? CurrentConfigRemote { get; set; }
+ ConfigBranch? CurentConfigBranch { get; set; }
+
+ GitBranch[] LocalBranches { get; set; }
+ GitBranch[] RemoteBranches { get; set; }
+ GitRemote[] Remotes { get; set; }
+
+ ILocalConfigBranchDictionary LocalConfigBranches { get; }
+ IRemoteConfigBranchDictionary RemoteConfigBranches { get; }
+ IConfigRemoteDictionary ConfigRemotes { get; }
+
+ void RemoveLocalBranch(string branch);
+ void AddLocalBranch(string branch);
+ void AddRemoteBranch(string remote, string branch);
+ void RemoveRemoteBranch(string remote, string branch);
+ void SetRemotes(Dictionary remoteDictionary, Dictionary> branchDictionary);
+ void SetLocals(Dictionary branchDictionary);
+ }
+
+ public interface IRepositoryInfoCache : IManagedCache
+ {
+ GitRemote? CurrentGitRemote { get; set; }
+ GitBranch? CurentGitBranch { get; set; }
+ }
+
+ public interface IGitLogCache : IManagedCache
+ {
+ List Log { get; set; }
+ }
+}
diff --git a/src/GitHub.Api/Cache/CacheManager.cs b/src/GitHub.Api/Cache/CacheManager.cs
deleted file mode 100644
index 5e3cdaadc..000000000
--- a/src/GitHub.Api/Cache/CacheManager.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-using System;
-using System.Linq;
-
-namespace GitHub.Unity
-{
- public class CacheManager
- {
- private IBranchCache branchCache;
- public IBranchCache BranchCache
- {
- get { return branchCache; }
- set
- {
- if (branchCache == null)
- branchCache = value;
- }
- }
-
- private Action onLocalBranchListChanged;
-
- public void SetupCache(IBranchCache branchCache, IRepository repository)
- {
- if (repository == null)
- return;
-
- BranchCache = branchCache;
- UpdateCache(repository);
- if (onLocalBranchListChanged != null)
- repository.OnLocalBranchListChanged -= onLocalBranchListChanged;
- onLocalBranchListChanged = () =>
- {
- if (!ThreadingHelper.InUIThread)
- new ActionTask(TaskManager.Instance.Token, () => UpdateCache(repository)) { Affinity = TaskAffinity.UI }.Start();
- else
- UpdateCache(repository);
- };
- repository.OnLocalBranchListChanged += onLocalBranchListChanged;
- }
-
- private void UpdateCache(IRepository repository)
- {
- BranchCache.LocalBranches = repository.LocalBranches.ToList();
- BranchCache.RemoteBranches = repository.RemoteBranches.ToList();
- }
- }
-}
\ No newline at end of file
diff --git a/src/GitHub.Api/Cache/IBranchCache.cs b/src/GitHub.Api/Cache/IBranchCache.cs
deleted file mode 100644
index 026d4f6bb..000000000
--- a/src/GitHub.Api/Cache/IBranchCache.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using System.Collections.Generic;
-
-namespace GitHub.Unity
-{
- public interface IBranchCache
- {
- List LocalBranches { get; set; }
- List RemoteBranches { get; set; }
- }
-}
diff --git a/src/GitHub.Api/Events/RepositoryWatcher.cs b/src/GitHub.Api/Events/RepositoryWatcher.cs
index b72d6bd3c..6528b0d3a 100644
--- a/src/GitHub.Api/Events/RepositoryWatcher.cs
+++ b/src/GitHub.Api/Events/RepositoryWatcher.cs
@@ -30,10 +30,8 @@ class RepositoryWatcher : IRepositoryWatcher
private readonly CancellationToken cancellationToken;
private readonly NPath[] ignoredPaths;
private readonly ManualResetEventSlim pauseEvent;
- private readonly bool disableNative;
private NativeInterface nativeInterface;
private bool running;
- private Task task;
private int lastCountOfProcessedEvents = 0;
private bool processingEvents;
private readonly ManualResetEventSlim signalProcessingEventsDone = new ManualResetEventSlim(false);
@@ -68,8 +66,7 @@ public void Initialize()
try
{
- if (!disableNative)
- nativeInterface = new NativeInterface(pathsRepositoryPath);
+ nativeInterface = new NativeInterface(pathsRepositoryPath);
}
catch (Exception ex)
{
@@ -79,12 +76,6 @@ public void Initialize()
public void Start()
{
- if (disableNative)
- {
- Logger.Trace("Native interface is disabled");
- return;
- }
-
if (nativeInterface == null)
{
Logger.Warning("NativeInterface is null");
@@ -95,7 +86,7 @@ public void Start()
running = true;
pauseEvent.Reset();
- task = Task.Factory.StartNew(WatcherLoop, cancellationToken, TaskCreationOptions.None, TaskScheduler.Default);
+ Task.Factory.StartNew(WatcherLoop, cancellationToken, TaskCreationOptions.None, TaskScheduler.Default);
}
public void Stop()
diff --git a/src/GitHub.Api/Git/GitBranch.cs b/src/GitHub.Api/Git/GitBranch.cs
index 9080accce..733b845df 100644
--- a/src/GitHub.Api/Git/GitBranch.cs
+++ b/src/GitHub.Api/Git/GitBranch.cs
@@ -11,12 +11,15 @@ interface ITreeData
[Serializable]
public struct GitBranch : ITreeData
{
- private string name;
- private string tracking;
- private bool active;
+ public static GitBranch Default = new GitBranch();
+
+ public string name;
+ public string tracking;
+ public bool isActive;
+
public string Name { get { return name; } }
public string Tracking { get { return tracking; } }
- public bool IsActive { get { return active; } }
+ public bool IsActive { get { return isActive; } }
public GitBranch(string name, string tracking, bool active)
{
@@ -24,7 +27,7 @@ public GitBranch(string name, string tracking, bool active)
this.name = name;
this.tracking = tracking;
- this.active = active;
+ this.isActive = active;
}
public override string ToString()
diff --git a/src/GitHub.Api/Git/GitClient.cs b/src/GitHub.Api/Git/GitClient.cs
index 0b7978338..12d9bf78b 100644
--- a/src/GitHub.Api/Git/GitClient.cs
+++ b/src/GitHub.Api/Git/GitClient.cs
@@ -15,7 +15,7 @@ public interface IGitClient
ITask LfsInstall(IOutputProcessor processor = null);
- ITask Status(IOutputProcessor processor = null);
+ ITask Status(IOutputProcessor processor = null);
ITask GetConfig(string key, GitConfigSource configSource,
IOutputProcessor processor = null);
@@ -23,6 +23,8 @@ ITask GetConfig(string key, GitConfigSource configSource,
ITask SetConfig(string key, string value, GitConfigSource configSource,
IOutputProcessor processor = null);
+ ITask GetConfigUserAndEmail();
+
ITask> ListLocks(bool local,
BaseOutputListProcessor processor = null);
@@ -87,16 +89,13 @@ class GitClient : IGitClient
{
private readonly IEnvironment environment;
private readonly IProcessManager processManager;
- private readonly ICredentialManager credentialManager;
private readonly ITaskManager taskManager;
private readonly CancellationToken cancellationToken;
- public GitClient(IEnvironment environment, IProcessManager processManager,
- ICredentialManager credentialManager, ITaskManager taskManager)
+ public GitClient(IEnvironment environment, IProcessManager processManager, ITaskManager taskManager)
{
this.environment = environment;
this.processManager = processManager;
- this.credentialManager = credentialManager;
this.taskManager = taskManager;
this.cancellationToken = taskManager.Token;
}
@@ -208,7 +207,7 @@ public ITask LfsInstall(IOutputProcessor processor = null)
.Configure(processManager);
}
- public ITask Status(IOutputProcessor processor = null)
+ public ITask Status(IOutputProcessor processor = null)
{
Logger.Trace("Status");
@@ -242,7 +241,7 @@ public ITask LfsVersion(IOutputProcessor processor = null)
public ITask GetConfig(string key, GitConfigSource configSource, IOutputProcessor processor = null)
{
- Logger.Trace("GetConfig");
+ Logger.Trace("GetConfig: {0}", key);
return new GitConfigGetTask(key, configSource, cancellationToken, processor)
.Configure(processManager);
@@ -256,6 +255,28 @@ public ITask SetConfig(string key, string value, GitConfigSource configS
.Configure(processManager);
}
+ public ITask GetConfigUserAndEmail()
+ {
+ string username = null;
+ string email = null;
+
+ return GetConfig("user.name", GitConfigSource.User).Then((success, value) => {
+ if (success)
+ {
+ username = value;
+ }
+
+ }).Then(GetConfig("user.email", GitConfigSource.User).Then((success, value) => {
+ if (success)
+ {
+ email = value;
+ }
+ })).Then(success => {
+ Logger.Trace("user.name:{1} user.email:{2}", success, username, email);
+ return new[] { username, email };
+ });
+ }
+
public ITask> ListLocks(bool local, BaseOutputListProcessor processor = null)
{
Logger.Trace("ListLocks");
diff --git a/src/GitHub.Api/Git/GitConfig.cs b/src/GitHub.Api/Git/GitConfig.cs
index c80d59662..05ce1bb10 100644
--- a/src/GitHub.Api/Git/GitConfig.cs
+++ b/src/GitHub.Api/Git/GitConfig.cs
@@ -216,16 +216,16 @@ public string GetString(string key)
public int GetInt(string key)
{
var value = this[key];
- var result = 0;
- var success = int.TryParse(value, out result);
+ int result = 0;
+ int.TryParse(value, out result);
return result;
}
public float GetFloat(string key)
{
var value = this[key];
- var result = 0F;
- var success = float.TryParse(value, out result);
+ float result = 0F;
+ float.TryParse(value, out result);
return result;
}
diff --git a/src/GitHub.Api/Git/GitCredentialManager.cs b/src/GitHub.Api/Git/GitCredentialManager.cs
index 4922d7a26..fceeec221 100644
--- a/src/GitHub.Api/Git/GitCredentialManager.cs
+++ b/src/GitHub.Api/Git/GitCredentialManager.cs
@@ -11,14 +11,12 @@ class GitCredentialManager : ICredentialManager
private ICredential credential;
private string credHelper = null;
- private readonly IEnvironment environment;
private readonly IProcessManager processManager;
private readonly ITaskManager taskManager;
- public GitCredentialManager(IEnvironment environment, IProcessManager processManager,
+ public GitCredentialManager(IProcessManager processManager,
ITaskManager taskManager)
{
- this.environment = environment;
this.processManager = processManager;
this.taskManager = taskManager;
}
diff --git a/src/GitHub.Api/Git/GitLock.cs b/src/GitHub.Api/Git/GitLock.cs
index b7ee35cdf..1267e7af4 100644
--- a/src/GitHub.Api/Git/GitLock.cs
+++ b/src/GitHub.Api/Git/GitLock.cs
@@ -5,12 +5,12 @@ namespace GitHub.Unity
[Serializable]
public struct GitLock
{
- public static GitLock Default = new GitLock(null, null, null, -1);
+ public static GitLock Default = new GitLock { ID = -1 };
- public readonly int ID;
- public readonly string Path;
- public readonly string FullPath;
- public readonly string User;
+ public int ID;
+ public string Path;
+ public string FullPath;
+ public string User;
public GitLock(string path, string fullPath, string user, int id)
{
diff --git a/src/GitHub.Api/Git/GitLogEntry.cs b/src/GitHub.Api/Git/GitLogEntry.cs
index 617245379..5861f4334 100644
--- a/src/GitHub.Api/Git/GitLogEntry.cs
+++ b/src/GitHub.Api/Git/GitLogEntry.cs
@@ -42,7 +42,7 @@ public string PrettyTimeString
}
}
- [NonSerialized] public DateTimeOffset? timeValue;
+ [NonSerialized] private DateTimeOffset? timeValue;
public DateTimeOffset Time
{
get
@@ -56,7 +56,7 @@ public DateTimeOffset Time
}
}
- [NonSerialized] public DateTimeOffset? commitTimeValue;
+ [NonSerialized] private DateTimeOffset? commitTimeValue;
public DateTimeOffset? CommitTime
{
get
diff --git a/src/GitHub.Api/Git/GitRemote.cs b/src/GitHub.Api/Git/GitRemote.cs
index d5478d897..f8e2c0529 100644
--- a/src/GitHub.Api/Git/GitRemote.cs
+++ b/src/GitHub.Api/Git/GitRemote.cs
@@ -14,13 +14,59 @@ public enum GitRemoteFunction
[Serializable]
public struct GitRemote
{
- public string Name;
- public string Url;
- public string Login;
- public string User;
- public string Token;
- public string Host;
- public GitRemoteFunction Function;
+ public static GitRemote Default = new GitRemote();
+
+ public string name;
+ public string url;
+ public string login;
+ public string user;
+ public string host;
+ public GitRemoteFunction function;
+ public string token;
+
+ public GitRemote(string name, string host, string url, GitRemoteFunction function, string user, string login, string token)
+ {
+ this.name = name;
+ this.url = url;
+ this.host = host;
+ this.function = function;
+ this.user = user;
+ this.login = login;
+ this.token = token;
+ }
+
+ public GitRemote(string name, string host, string url, GitRemoteFunction function, string user)
+ {
+ this.name = name;
+ this.url = url;
+ this.host = host;
+ this.function = function;
+ this.user = user;
+ this.login = null;
+ this.token = null;
+ }
+
+ public GitRemote(string name, string host, string url, GitRemoteFunction function)
+ {
+ this.name = name;
+ this.url = url;
+ this.host = host;
+ this.function = function;
+ this.user = null;
+ this.login = null;
+ this.token = null;
+ }
+
+ public GitRemote(string name, string url)
+ {
+ this.name = name;
+ this.url = url;
+ this.login = null;
+ this.user = null;
+ this.token = null;
+ this.host = null;
+ this.function = GitRemoteFunction.Unknown;
+ }
public override string ToString()
{
@@ -33,5 +79,13 @@ public override string ToString()
sb.AppendLine(String.Format("Function: {0}", Function));
return sb.ToString();
}
+
+ public string Name => name;
+ public string Url => url;
+ public string Login => login;
+ public string User => user;
+ public string Token => token;
+ public string Host => host;
+ public GitRemoteFunction Function => function;
}
}
\ No newline at end of file
diff --git a/src/GitHub.Api/Git/IRepository.cs b/src/GitHub.Api/Git/IRepository.cs
index 5818a9f52..7e2d279fa 100644
--- a/src/GitHub.Api/Git/IRepository.cs
+++ b/src/GitHub.Api/Git/IRepository.cs
@@ -9,19 +9,26 @@ namespace GitHub.Unity
public interface IRepository : IEquatable
{
void Initialize(IRepositoryManager repositoryManager);
- void Refresh();
ITask CommitAllFiles(string message, string body);
ITask CommitFiles(List files, string message, string body);
ITask SetupRemote(string remoteName, string remoteUrl);
- ITask> Log();
ITask Pull();
ITask Push();
ITask Fetch();
ITask Revert(string changeset);
- ITask ListLocks();
ITask RequestLock(string file);
ITask ReleaseLock(string file, bool force);
+ void CheckLogChangedEvent(CacheUpdateEvent gitLogCacheUpdateEvent);
+ void CheckStatusChangedEvent(CacheUpdateEvent cacheUpdateEvent);
+ void CheckCurrentBranchChangedEvent(CacheUpdateEvent cacheUpdateEvent);
+ void CheckCurrentRemoteChangedEvent(CacheUpdateEvent cacheUpdateEvent);
+ void CheckCurrentBranchAndRemoteChangedEvent(CacheUpdateEvent cacheUpdateEvent);
+ void CheckLocalBranchListChangedEvent(CacheUpdateEvent cacheUpdateEvent);
+ void CheckLocksChangedEvent(CacheUpdateEvent cacheUpdateEvent);
+ void CheckRemoteBranchListChangedEvent(CacheUpdateEvent cacheUpdateEvent);
+ void CheckLocalAndRemoteBranchListChangedEvent(CacheUpdateEvent cacheUpdateEvent);
+
///
/// Gets the name of the repository.
///
@@ -51,20 +58,22 @@ public interface IRepository : IEquatable
///
GitBranch? CurrentBranch { get; }
GitStatus CurrentStatus { get; }
- IList Remotes { get; }
- IEnumerable LocalBranches { get; }
- IEnumerable RemoteBranches { get; }
+ GitRemote[] Remotes { get; }
+ GitBranch[] LocalBranches { get; }
+ GitBranch[] RemoteBranches { get; }
IUser User { get; set; }
- IList CurrentLocks { get; }
+ List CurrentLocks { get; }
string CurrentBranchName { get; }
+ List CurrentLog { get; }
- event Action OnStatusChanged;
- event Action OnCurrentBranchChanged;
- event Action OnCurrentRemoteChanged;
- event Action OnLocalBranchListChanged;
- event Action OnCurrentBranchUpdated;
- event Action> OnLocksChanged;
- event Action OnRepositoryInfoChanged;
- event Action OnRemoteBranchListChanged;
+ event Action LogChanged;
+ event Action StatusChanged;
+ event Action CurrentBranchChanged;
+ event Action CurrentRemoteChanged;
+ event Action CurrentBranchAndRemoteChanged;
+ event Action LocalBranchListChanged;
+ event Action LocksChanged;
+ event Action RemoteBranchListChanged;
+ event Action LocalAndRemoteBranchListChanged;
}
}
\ No newline at end of file
diff --git a/src/GitHub.Api/Git/Repository.cs b/src/GitHub.Api/Git/Repository.cs
index 68d53af17..f4c3359c9 100644
--- a/src/GitHub.Api/Git/Repository.cs
+++ b/src/GitHub.Api/Git/Repository.cs
@@ -10,49 +10,46 @@ namespace GitHub.Unity
[DebuggerDisplay("{DebuggerDisplay,nq}")]
class Repository : IEquatable, IRepository
{
- private ConfigBranch? currentBranch;
- private IList currentLocks;
- private ConfigRemote? currentRemote;
- private GitStatus currentStatus;
- private Dictionary localBranches = new Dictionary();
- private Dictionary> remoteBranches = new Dictionary>();
- private Dictionary remotes;
private IRepositoryManager repositoryManager;
- public event Action OnCurrentBranchChanged;
- public event Action OnCurrentRemoteChanged;
- public event Action OnCurrentBranchUpdated;
- public event Action OnLocalBranchListChanged;
- public event Action> OnLocksChanged;
- public event Action OnRemoteBranchListChanged;
- public event Action OnRepositoryInfoChanged;
-
- public event Action OnStatusChanged;
+ private ICacheContainer cacheContainer;
+ private UriString cloneUrl;
+ private string name;
+
+ public event Action LogChanged;
+ public event Action StatusChanged;
+ public event Action CurrentBranchChanged;
+ public event Action CurrentRemoteChanged;
+ public event Action CurrentBranchAndRemoteChanged;
+ public event Action LocalBranchListChanged;
+ public event Action LocksChanged;
+ public event Action RemoteBranchListChanged;
+ public event Action LocalAndRemoteBranchListChanged;
///
/// Initializes a new instance of the class.
///
- /// The repository name.
///
- public Repository(string name, NPath localPath)
+ ///
+ public Repository(NPath localPath, ICacheContainer container)
{
- Guard.ArgumentNotNullOrWhiteSpace(name, nameof(name));
Guard.ArgumentNotNull(localPath, nameof(localPath));
- Name = name;
LocalPath = localPath;
- this.User = new User();
+ User = new User();
+
+ cacheContainer = container;
+ cacheContainer.CacheInvalidated += CacheContainer_OnCacheInvalidated;
+ cacheContainer.CacheUpdated += CacheContainer_OnCacheUpdated;
}
- public void Initialize(IRepositoryManager repositoryManager)
+ public void Initialize(IRepositoryManager initRepositoryManager)
{
- Guard.ArgumentNotNull(repositoryManager, nameof(repositoryManager));
+ Logger.Trace("Initialize");
+ Guard.ArgumentNotNull(initRepositoryManager, nameof(initRepositoryManager));
- this.repositoryManager = repositoryManager;
-
- repositoryManager.OnCurrentBranchUpdated += RepositoryManager_OnCurrentBranchUpdated;
- repositoryManager.OnCurrentRemoteUpdated += RepositoryManager_OnCurrentRemoteUpdated;
- repositoryManager.OnStatusUpdated += status => CurrentStatus = status;
- repositoryManager.OnLocksUpdated += locks => CurrentLocks = locks;
+ repositoryManager = initRepositoryManager;
+ repositoryManager.OnCurrentBranchAndRemoteUpdated += RepositoryManager_OnCurrentBranchAndRemoteUpdated;
+ repositoryManager.OnRepositoryUpdated += RepositoryManager_OnRepositoryUpdated;
repositoryManager.OnLocalBranchListUpdated += RepositoryManager_OnLocalBranchListUpdated;
repositoryManager.OnRemoteBranchListUpdated += RepositoryManager_OnRemoteBranchListUpdated;
repositoryManager.OnLocalBranchUpdated += RepositoryManager_OnLocalBranchUpdated;
@@ -61,11 +58,11 @@ public void Initialize(IRepositoryManager repositoryManager)
repositoryManager.OnRemoteBranchAdded += RepositoryManager_OnRemoteBranchAdded;
repositoryManager.OnRemoteBranchRemoved += RepositoryManager_OnRemoteBranchRemoved;
repositoryManager.OnGitUserLoaded += user => User = user;
- }
- public void Refresh()
- {
- repositoryManager?.Refresh();
+ UpdateGitStatus();
+ UpdateGitLog();
+
+ new ActionTask(CancellationToken.None, UpdateLocks) { Affinity = TaskAffinity.UI }.Start();
}
public ITask SetupRemote(string remote, string remoteUrl)
@@ -82,14 +79,6 @@ public ITask SetupRemote(string remote, string remoteUrl)
}
}
- public ITask> Log()
- {
- if (repositoryManager == null)
- return new FuncListTask(TaskHelpers.GetCompletedTask(new List()));
-
- return repositoryManager.Log();
- }
-
public ITask CommitAllFiles(string message, string body)
{
return repositoryManager.CommitAllFiles(message, body);
@@ -107,7 +96,8 @@ public ITask Pull()
public ITask Push()
{
- return repositoryManager.Push(CurrentRemote.Value.Name, CurrentBranch?.Name);
+ return repositoryManager.Push(CurrentRemote.Value.Name, CurrentBranch?.Name)
+ .Then(UpdateGitStatus);
}
public ITask Fetch()
@@ -120,21 +110,111 @@ public ITask Revert(string changeset)
return repositoryManager.Revert(changeset);
}
- public ITask ListLocks()
+ public ITask RequestLock(string file)
{
- if (repositoryManager == null)
- return new ActionTask(TaskExtensions.CompletedTask);
- return repositoryManager.ListLocks(false);
+ return repositoryManager.LockFile(file)
+ .Then(UpdateLocks);
}
- public ITask RequestLock(string file)
+ public ITask ReleaseLock(string file, bool force)
{
- return repositoryManager.LockFile(file);
+ return repositoryManager.UnlockFile(file, force)
+ .Then(UpdateLocks);
}
- public ITask ReleaseLock(string file, bool force)
+ public void CheckLogChangedEvent(CacheUpdateEvent cacheUpdateEvent)
+ {
+ var managedCache = cacheContainer.GitLogCache;
+ var raiseEvent = ShouldRaiseCacheEvent(cacheUpdateEvent, managedCache);
+
+ Logger.Trace("Check GitLogCache CacheUpdateEvent Current:{0} Check:{1} Result:{2}", managedCache.LastUpdatedAt,
+ cacheUpdateEvent.UpdatedTimeString ?? "[NULL]", raiseEvent);
+
+ if (raiseEvent)
+ {
+ var dateTimeOffset = managedCache.LastUpdatedAt;
+ var updateEvent = new CacheUpdateEvent { UpdatedTimeString = dateTimeOffset.ToString() };
+ HandleGitLogCacheUpdatedEvent(updateEvent);
+ }
+ }
+
+ public void CheckStatusChangedEvent(CacheUpdateEvent cacheUpdateEvent)
+ {
+ var managedCache = cacheContainer.GitStatusCache;
+ var raiseEvent = ShouldRaiseCacheEvent(cacheUpdateEvent, managedCache);
+
+ Logger.Trace("Check GitStatusCache CacheUpdateEvent Current:{0} Check:{1} Result:{2}", managedCache.LastUpdatedAt,
+ cacheUpdateEvent.UpdatedTimeString ?? "[NULL]", raiseEvent);
+
+ if (raiseEvent)
+ {
+ var dateTimeOffset = managedCache.LastUpdatedAt;
+ var updateEvent = new CacheUpdateEvent { UpdatedTimeString = dateTimeOffset.ToString() };
+ HandleGitStatusCacheUpdatedEvent(updateEvent);
+ }
+ }
+
+ public void CheckCurrentBranchChangedEvent(CacheUpdateEvent cacheUpdateEvent)
+ {
+ CheckRepositoryInfoCacheEvent(cacheUpdateEvent);
+ }
+
+ public void CheckCurrentRemoteChangedEvent(CacheUpdateEvent cacheUpdateEvent)
+ {
+ CheckRepositoryInfoCacheEvent(cacheUpdateEvent);
+ }
+
+ public void CheckCurrentBranchAndRemoteChangedEvent(CacheUpdateEvent cacheUpdateEvent)
+ {
+ CheckRepositoryInfoCacheEvent(cacheUpdateEvent);
+ }
+
+ private void CheckRepositoryInfoCacheEvent(CacheUpdateEvent cacheUpdateEvent)
+ {
+ var managedCache = cacheContainer.RepositoryInfoCache;
+ var raiseEvent = ShouldRaiseCacheEvent(cacheUpdateEvent, managedCache);
+
+ Logger.Trace("Check RepositoryInfoCache CacheUpdateEvent Current:{0} Check:{1} Result:{2}", managedCache.LastUpdatedAt,
+ cacheUpdateEvent.UpdatedTimeString ?? "[NULL]", raiseEvent);
+
+ if (raiseEvent)
+ {
+ var dateTimeOffset = managedCache.LastUpdatedAt;
+ var updateEvent = new CacheUpdateEvent { UpdatedTimeString = dateTimeOffset.ToString() };
+ HandleRepositoryInfoCacheUpdatedEvent(updateEvent);
+ }
+ }
+
+ public void CheckLocksChangedEvent(CacheUpdateEvent cacheUpdateEvent)
+ {
+ CacheUpdateEvent cacheUpdateEvent1 = cacheUpdateEvent;
+ var managedCache = cacheContainer.GitLocksCache;
+ var raiseEvent = ShouldRaiseCacheEvent(cacheUpdateEvent1, managedCache);
+
+ Logger.Trace("Check GitLocksCache CacheUpdateEvent Current:{0} Check:{1} Result:{2}", managedCache.LastUpdatedAt,
+ cacheUpdateEvent1.UpdatedTimeString ?? "[NULL]", raiseEvent);
+
+ if (raiseEvent)
+ {
+ var dateTimeOffset = managedCache.LastUpdatedAt;
+ var updateEvent = new CacheUpdateEvent { UpdatedTimeString = dateTimeOffset.ToString() };
+ HandleGitLocksCacheUpdatedEvent(updateEvent);
+ }
+ }
+
+ public void CheckLocalBranchListChangedEvent(CacheUpdateEvent cacheUpdateEvent)
+ {
+ CheckBranchCacheEvent(cacheUpdateEvent);
+ }
+
+ public void CheckRemoteBranchListChangedEvent(CacheUpdateEvent cacheUpdateEvent)
+ {
+ CheckBranchCacheEvent(cacheUpdateEvent);
+ }
+
+ public void CheckLocalAndRemoteBranchListChangedEvent(CacheUpdateEvent cacheUpdateEvent)
{
- return repositoryManager.UnlockFile(file, force);
+ CheckBranchCacheEvent(cacheUpdateEvent);
}
///
@@ -151,6 +231,7 @@ public override bool Equals(object obj)
{
if (ReferenceEquals(this, obj))
return true;
+
var other = obj as Repository;
return Equals(other);
}
@@ -164,62 +245,219 @@ public bool Equals(IRepository other)
{
if (ReferenceEquals(this, other))
return true;
- return other != null &&
- object.Equals(LocalPath, other.LocalPath);
+
+ return other != null && object.Equals(LocalPath, other.LocalPath);
+ }
+
+ private void CheckBranchCacheEvent(CacheUpdateEvent cacheUpdateEvent)
+ {
+ var managedCache = cacheContainer.BranchCache;
+ var raiseEvent = ShouldRaiseCacheEvent(cacheUpdateEvent, managedCache);
+
+ Logger.Trace("Check BranchCache CacheUpdateEvent Current:{0} Check:{1} Result:{2}", managedCache.LastUpdatedAt,
+ cacheUpdateEvent.UpdatedTimeString ?? "[NULL]", raiseEvent);
+
+ if (raiseEvent)
+ {
+ var dateTimeOffset = managedCache.LastUpdatedAt;
+ var updateEvent = new CacheUpdateEvent { UpdatedTimeString = dateTimeOffset.ToString() };
+ HandleBranchCacheUpdatedEvent(updateEvent);
+ }
+ }
+
+ private static bool ShouldRaiseCacheEvent(CacheUpdateEvent cacheUpdateEvent, IManagedCache managedCache)
+ {
+ bool raiseEvent;
+ if (cacheUpdateEvent.UpdatedTimeString == null)
+ {
+ raiseEvent = managedCache.LastUpdatedAt != DateTimeOffset.MinValue;
+ }
+ else
+ {
+ raiseEvent = managedCache.LastUpdatedAt.ToString() != cacheUpdateEvent.UpdatedTimeString;
+ }
+ return raiseEvent;
}
- private void RepositoryManager_OnCurrentRemoteUpdated(ConfigRemote? remote)
+ private void CacheContainer_OnCacheInvalidated(CacheType cacheType)
{
- if (!Nullable.Equals(currentRemote, remote))
+ switch (cacheType)
{
- currentRemote = remote;
+ case CacheType.BranchCache:
+ break;
+
+ case CacheType.GitLogCache:
+ break;
+
+ case CacheType.GitStatusCache:
+ break;
- Logger.Trace("OnCurrentRemoteChanged: {0}", currentRemote.HasValue ? currentRemote.Value.ToString() : "[NULL]");
- OnCurrentRemoteChanged?.Invoke(currentRemote.HasValue ? currentRemote.Value.Name : null);
+ case CacheType.GitLocksCache:
+ break;
- UpdateRepositoryInfo();
+ case CacheType.GitUserCache:
+ break;
+
+ default:
+ throw new ArgumentOutOfRangeException(nameof(cacheType), cacheType, null);
}
}
- private void RepositoryManager_OnCurrentBranchUpdated(ConfigBranch? branch)
+ private void CacheContainer_OnCacheUpdated(CacheType cacheType, DateTimeOffset offset)
{
- if (!Nullable.Equals(currentBranch, branch))
+ var cacheUpdateEvent = new CacheUpdateEvent { UpdatedTimeString = offset.ToString() };
+ switch (cacheType)
{
- currentBranch = branch;
+ case CacheType.BranchCache:
+ HandleBranchCacheUpdatedEvent(cacheUpdateEvent);
+ break;
+
+ case CacheType.GitLogCache:
+ HandleGitLogCacheUpdatedEvent(cacheUpdateEvent);
+ break;
+
+ case CacheType.GitStatusCache:
+ HandleGitStatusCacheUpdatedEvent(cacheUpdateEvent);
+ break;
+
+ case CacheType.GitLocksCache:
+ HandleGitLocksCacheUpdatedEvent(cacheUpdateEvent);
+ break;
- Logger.Trace("OnCurrentBranchChanged: {0}", currentBranch.HasValue ? currentBranch.ToString() : "[NULL]");
- OnCurrentBranchChanged?.Invoke(currentBranch.HasValue ? currentBranch.Value.Name : null);
+ case CacheType.GitUserCache:
+ break;
+
+ case CacheType.RepositoryInfoCache:
+ HandleRepositoryInfoCacheUpdatedEvent(cacheUpdateEvent);
+ break;
+
+ default:
+ throw new ArgumentOutOfRangeException(nameof(cacheType), cacheType, null);
}
}
- private void RepositoryManager_OnLocalBranchUpdated(string name)
+ private void HandleRepositoryInfoCacheUpdatedEvent(CacheUpdateEvent cacheUpdateEvent)
{
- if (name == currentBranch?.Name)
+ Logger.Trace("RepositoryInfoCache Updated {0}", cacheUpdateEvent.UpdatedTimeString);
+ CurrentBranchChanged?.Invoke(cacheUpdateEvent);
+ CurrentRemoteChanged?.Invoke(cacheUpdateEvent);
+ CurrentBranchAndRemoteChanged?.Invoke(cacheUpdateEvent);
+ }
+
+ private void HandleGitLocksCacheUpdatedEvent(CacheUpdateEvent cacheUpdateEvent)
+ {
+ Logger.Trace("GitLocksCache Updated {0}", cacheUpdateEvent.UpdatedTimeString);
+ LocksChanged?.Invoke(cacheUpdateEvent);
+ }
+
+ private void HandleGitStatusCacheUpdatedEvent(CacheUpdateEvent cacheUpdateEvent)
+ {
+ Logger.Trace("GitStatusCache Updated {0}", cacheUpdateEvent.UpdatedTimeString);
+ StatusChanged?.Invoke(cacheUpdateEvent);
+ }
+
+ private void HandleGitLogCacheUpdatedEvent(CacheUpdateEvent cacheUpdateEvent)
+ {
+ Logger.Trace("GitLogCache Updated {0}", cacheUpdateEvent.UpdatedTimeString);
+ LogChanged?.Invoke(cacheUpdateEvent);
+ }
+
+ private void HandleBranchCacheUpdatedEvent(CacheUpdateEvent cacheUpdateEvent)
+ {
+ Logger.Trace("BranchCache Updated {0}", cacheUpdateEvent.UpdatedTimeString);
+ LocalBranchListChanged?.Invoke(cacheUpdateEvent);
+ RemoteBranchListChanged?.Invoke(cacheUpdateEvent);
+ LocalAndRemoteBranchListChanged?.Invoke(cacheUpdateEvent);
+ }
+
+ private void RepositoryManager_OnRepositoryUpdated()
+ {
+ Logger.Trace("OnRepositoryUpdated");
+ UpdateGitStatus();
+ UpdateGitLog();
+ }
+
+ private void UpdateGitStatus()
+ {
+ repositoryManager?.Status()
+ .ThenInUI((b, status) => { CurrentStatus = status; })
+ .Start();
+ }
+
+ private void UpdateGitLog()
+ {
+ repositoryManager?.Log()
+ .ThenInUI((b, log) => { CurrentLog = log; })
+ .Start();
+ }
+
+ private void UpdateLocks()
+ {
+ if (CurrentRemote.HasValue)
{
- Logger.Trace("OnCurrentBranchUpdated: {0}", name);
- OnCurrentBranchUpdated?.Invoke();
- Refresh();
+ repositoryManager?.ListLocks(false)
+ .ThenInUI((b, locks) => { CurrentLocks = locks; })
+ .Start();
}
}
+
+ private void RepositoryManager_OnCurrentBranchAndRemoteUpdated(ConfigBranch? branch, ConfigRemote? remote)
+ {
+ new ActionTask(CancellationToken.None, () => {
+ if (!Nullable.Equals(CurrentConfigBranch, branch))
+ {
+ var currentBranch = branch != null ? (GitBranch?)GetLocalGitBranch(branch.Value) : null;
- private void RepositoryManager_OnRemoteBranchListUpdated(Dictionary updatedRemotes, Dictionary> branches)
+ CurrentConfigBranch = branch;
+ CurrentBranch = currentBranch;
+ UpdateLocalBranches();
+ }
+
+ if (!Nullable.Equals(CurrentConfigRemote, remote))
+ {
+ CurrentConfigRemote = remote;
+ CurrentRemote = GetGitRemote(remote.Value);
+ UpdateRepositoryInfo();
+ }
+ }) { Affinity = TaskAffinity.UI }.Start();
+ }
+
+ private void RepositoryManager_OnLocalBranchUpdated(string name)
{
- remotes = updatedRemotes;
+ if (name == CurrentConfigBranch?.Name)
+ {
+ UpdateGitStatus();
+ UpdateGitLog();
+ }
+ }
- Remotes = remotes.Select(pair => GetGitRemote(pair.Value)).ToArray();
+ private void RepositoryManager_OnRemoteBranchListUpdated(Dictionary remotes,
+ Dictionary> branches)
+ {
+ new ActionTask(CancellationToken.None, () => {
+ cacheContainer.BranchCache.SetRemotes(remotes, branches);
+ UpdateRemoteAndRemoteBranches();
+ }) { Affinity = TaskAffinity.UI }.Start();
+ }
- remoteBranches = branches;
+ private void UpdateRemoteAndRemoteBranches()
+ {
+ Remotes = ConfigRemotes.Values.Select(GetGitRemote).ToArray();
- Logger.Trace("OnRemoteBranchListChanged");
- OnRemoteBranchListChanged?.Invoke();
+ RemoteBranches = RemoteConfigBranches.Values.SelectMany(x => x.Values).Select(GetRemoteGitBranch).ToArray();
}
private void RepositoryManager_OnLocalBranchListUpdated(Dictionary branches)
{
- localBranches = branches;
+ new ActionTask(CancellationToken.None, () => {
+ cacheContainer.BranchCache.SetLocals(branches);
+ UpdateLocalBranches();
+ }) { Affinity = TaskAffinity.UI }.Start();
+ }
- Logger.Trace("OnLocalBranchListChanged");
- OnLocalBranchListChanged?.Invoke();
+ private void UpdateLocalBranches()
+ {
+ LocalBranches = LocalConfigBranches.Values.Select(GetLocalGitBranch).ToArray();
}
private void UpdateRepositoryInfo()
@@ -236,192 +474,185 @@ private void UpdateRepositoryInfo()
Name = LocalPath.FileName;
Logger.Trace("CloneUrl: [NULL]");
}
-
- OnRepositoryInfoChanged?.Invoke();
}
private void RepositoryManager_OnLocalBranchRemoved(string name)
{
- if (localBranches.ContainsKey(name))
- {
- localBranches.Remove(name);
-
- Logger.Trace("OnLocalBranchListChanged");
- OnLocalBranchListChanged?.Invoke();
- }
- else
- {
- Logger.Warning("Branch {0} is not found", name);
- }
+ new ActionTask(CancellationToken.None, () => {
+ cacheContainer.BranchCache.RemoveLocalBranch(name);
+ UpdateLocalBranches();
+ }) { Affinity = TaskAffinity.UI }.Start();
}
private void RepositoryManager_OnLocalBranchAdded(string name)
{
- if (!localBranches.ContainsKey(name))
- {
- var branch = repositoryManager.Config.GetBranch(name);
- if (!branch.HasValue)
- {
- branch = new ConfigBranch { Name = name };
- }
- localBranches.Add(name, branch.Value);
-
- Logger.Trace("OnLocalBranchListChanged");
- OnLocalBranchListChanged?.Invoke();
- }
- else
- {
- Logger.Warning("Branch {0} is already present", name);
- }
+ new ActionTask(CancellationToken.None, () => {
+ cacheContainer.BranchCache.AddLocalBranch(name);
+ UpdateLocalBranches();
+ }) { Affinity = TaskAffinity.UI }.Start();
}
private void RepositoryManager_OnRemoteBranchAdded(string remote, string name)
{
- Dictionary branchList;
- if (remoteBranches.TryGetValue(remote, out branchList))
- {
- if (!branchList.ContainsKey(name))
- {
- branchList.Add(name, new ConfigBranch { Name = name, Remote = remotes[remote] });
-
- Logger.Trace("OnRemoteBranchListChanged");
- OnRemoteBranchListChanged?.Invoke();
- }
- else
- {
- Logger.Warning("Branch {0} is already present in Remote {1}", name, remote);
- }
- }
- else
- {
- Logger.Warning("Remote {0} is not found", remote);
- }
+ new ActionTask(CancellationToken.None, () => {
+ cacheContainer.BranchCache.AddRemoteBranch(remote, name);
+ UpdateRemoteAndRemoteBranches();
+ }) { Affinity = TaskAffinity.UI }.Start();
}
private void RepositoryManager_OnRemoteBranchRemoved(string remote, string name)
{
- Dictionary branchList;
- if (remoteBranches.TryGetValue(remote, out branchList))
- {
- if (localBranches.ContainsKey(name))
- {
- localBranches.Remove(name);
-
- Logger.Trace("OnRemoteBranchListChanged");
- OnRemoteBranchListChanged?.Invoke();
- }
- else
- {
- Logger.Warning("Branch {0} is not found in Remote {1}", name, remote);
- }
- }
- else
- {
- Logger.Warning("Remote {0} is not found", remote);
- }
+ new ActionTask(CancellationToken.None, () => {
+ cacheContainer.BranchCache.RemoveRemoteBranch(remote, name);
+ UpdateRemoteAndRemoteBranches();
+ }) { Affinity = TaskAffinity.UI }.Start();
}
private GitBranch GetLocalGitBranch(ConfigBranch x)
{
var name = x.Name;
var trackingName = x.IsTracking ? x.Remote.Value.Name + "/" + name : "[None]";
- var isActive = name == currentBranch?.Name;
+ var isActive = name == CurrentBranchName;
return new GitBranch(name, trackingName, isActive);
}
- private GitBranch GetRemoteGitBranch(ConfigBranch x)
+ private static GitBranch GetRemoteGitBranch(ConfigBranch x)
{
var name = x.Remote.Value.Name + "/" + x.Name;
- var trackingName = "[None]";
- return new GitBranch(name, trackingName, false);
+ return new GitBranch(name, "[None]", false);
}
- private GitRemote GetGitRemote(ConfigRemote configRemote)
+ private static GitRemote GetGitRemote(ConfigRemote configRemote)
{
- return new GitRemote { Name = configRemote.Name, Url = configRemote.Url };
+ return new GitRemote(configRemote.Name, configRemote.Url);
}
- public IList Remotes { get; private set; }
+ private IRemoteConfigBranchDictionary RemoteConfigBranches => cacheContainer.BranchCache.RemoteConfigBranches;
- public IEnumerable LocalBranches => localBranches.Values.Select(GetLocalGitBranch);
+ private IConfigRemoteDictionary ConfigRemotes => cacheContainer.BranchCache.ConfigRemotes;
- public IEnumerable RemoteBranches => remoteBranches.Values.SelectMany(x => x.Values).Select(GetRemoteGitBranch);
+ private ILocalConfigBranchDictionary LocalConfigBranches => cacheContainer.BranchCache.LocalConfigBranches;
- public GitBranch? CurrentBranch
+ public GitRemote[] Remotes
{
- get
- {
- if (currentBranch != null)
- {
- return GetLocalGitBranch(currentBranch.Value);
- }
+ get { return cacheContainer.BranchCache.Remotes; }
+ private set { cacheContainer.BranchCache.Remotes = value; }
+ }
- return null;
- }
+ public GitBranch[] LocalBranches
+ {
+ get { return cacheContainer.BranchCache.LocalBranches; }
+ private set { cacheContainer.BranchCache.LocalBranches = value; }
}
- public string CurrentBranchName => currentBranch?.Name;
+ public GitBranch[] RemoteBranches
+ {
+ get { return cacheContainer.BranchCache.RemoteBranches; }
+ private set { cacheContainer.BranchCache.RemoteBranches = value; }
+ }
- public GitRemote? CurrentRemote
+ private ConfigBranch? CurrentConfigBranch
{
- get
- {
- if (currentRemote != null)
- {
- return GetGitRemote(currentRemote.Value);
- }
+ get { return this.cacheContainer.BranchCache.CurentConfigBranch; }
+ set { cacheContainer.BranchCache.CurentConfigBranch = value; }
+ }
- return null;
- }
+ private ConfigRemote? CurrentConfigRemote
+ {
+ get { return this.cacheContainer.BranchCache.CurrentConfigRemote; }
+ set { cacheContainer.BranchCache.CurrentConfigRemote = value; }
}
- public UriString CloneUrl { get; private set; }
+ public GitStatus CurrentStatus
+ {
+ get { return cacheContainer.GitStatusCache.GitStatus; }
+ private set { cacheContainer.GitStatusCache.GitStatus = value; }
+ }
- public string Name { get; private set; }
+ public GitBranch? CurrentBranch
+ {
+ get { return cacheContainer.RepositoryInfoCache.CurentGitBranch; }
+ private set { cacheContainer.RepositoryInfoCache.CurentGitBranch = value; }
+ }
- public NPath LocalPath { get; private set; }
+ public string CurrentBranchName => CurrentConfigBranch?.Name;
- public string Owner => CloneUrl?.Owner ?? null;
+ public GitRemote? CurrentRemote
+ {
+ get { return cacheContainer.RepositoryInfoCache.CurrentGitRemote; }
+ private set { cacheContainer.RepositoryInfoCache.CurrentGitRemote = value; }
+ }
- public bool IsGitHub { get { return HostAddress.IsGitHubDotCom(CloneUrl); } }
+ public List CurrentLog
+ {
+ get { return cacheContainer.GitLogCache.Log; }
+ private set { cacheContainer.GitLogCache.Log = value; }
+ }
- internal string DebuggerDisplay => String.Format(
- CultureInfo.InvariantCulture,
- "{0} Owner: {1} Name: {2} CloneUrl: {3} LocalPath: {4} Branch: {5} Remote: {6}",
- GetHashCode(),
- Owner,
- Name,
- CloneUrl,
- LocalPath,
- CurrentBranch,
- CurrentRemote);
+ public List CurrentLocks
+ {
+ get { return cacheContainer.GitLocksCache.GitLocks; }
+ private set { cacheContainer.GitLocksCache.GitLocks = value; }
+ }
- public GitStatus CurrentStatus
+ public UriString CloneUrl
{
- get { return currentStatus; }
+ get
+ {
+ if (cloneUrl == null)
+ {
+ var currentRemote = CurrentRemote;
+ if (currentRemote.HasValue && currentRemote.Value.Url != null)
+ {
+ cloneUrl = new UriString(currentRemote.Value.Url);
+ }
+ }
+ return cloneUrl;
+ }
private set
{
- currentStatus = value;
- Logger.Trace("OnStatusChanged: {0}", value.ToString());
- OnStatusChanged?.Invoke(value);
+ cloneUrl = value;
}
}
- public IUser User { get; set; }
-
- public IList CurrentLocks
+ public string Name
{
- get { return currentLocks; }
- private set
+ get
{
- Logger.Trace("OnLocksChanged: {0}", value.ToString());
- currentLocks = value;
- OnLocksChanged?.Invoke(value);
+ if (name == null)
+ {
+ var url = CloneUrl;
+ if (url != null)
+ {
+ name = url.RepositoryName;
+ }
+ else
+ {
+ name = LocalPath.FileName;
+ }
+ }
+ return name;
}
+ private set { name = value; }
}
+ public NPath LocalPath { get; private set; }
+
+ public string Owner => CloneUrl?.Owner ?? null;
+
+ public bool IsGitHub
+ {
+ get { return HostAddress.IsGitHubDotCom(CloneUrl); }
+ }
+
+ internal string DebuggerDisplay => String.Format(CultureInfo.InvariantCulture,
+ "{0} Owner: {1} Name: {2} CloneUrl: {3} LocalPath: {4} Branch: {5} Remote: {6}", GetHashCode(), Owner, Name,
+ CloneUrl, LocalPath, CurrentBranch, CurrentRemote);
+
+ public IUser User { get; set; }
+
protected static ILogging Logger { get; } = Logging.GetLogger();
}
@@ -432,7 +663,7 @@ public interface IUser
}
[Serializable]
- class User : IUser
+ public class User : IUser
{
public override string ToString()
{
@@ -442,4 +673,10 @@ public override string ToString()
public string Name { get; set; }
public string Email { get; set; }
}
-}
\ No newline at end of file
+
+ [Serializable]
+ public struct CacheUpdateEvent
+ {
+ public string UpdatedTimeString;
+ }
+}
diff --git a/src/GitHub.Api/Git/RepositoryManager.cs b/src/GitHub.Api/Git/RepositoryManager.cs
index e3892806a..0e2300a1a 100644
--- a/src/GitHub.Api/Git/RepositoryManager.cs
+++ b/src/GitHub.Api/Git/RepositoryManager.cs
@@ -1,34 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Threading;
using System.Threading.Tasks;
namespace GitHub.Unity
{
public interface IRepositoryManager : IDisposable
{
- event Action OnCurrentBranchUpdated;
- event Action OnCurrentRemoteUpdated;
+ event Action OnCurrentBranchAndRemoteUpdated;
event Action OnGitUserLoaded;
event Action OnIsBusyChanged;
event Action OnLocalBranchAdded;
event Action> OnLocalBranchListUpdated;
event Action OnLocalBranchRemoved;
event Action OnLocalBranchUpdated;
- event Action> OnLocksUpdated;
event Action OnRemoteBranchAdded;
event Action, Dictionary>> OnRemoteBranchListUpdated;
event Action OnRemoteBranchRemoved;
- event Action OnStatusUpdated;
+ event Action OnRepositoryUpdated;
void Initialize();
void Start();
void Stop();
- void Refresh();
ITask CommitAllFiles(string message, string body);
ITask CommitFiles(List files, string message, string body);
ITask> Log();
+ ITask Status();
ITask Fetch(string remote);
ITask Pull(string remote, string branch);
ITask Push(string remote, string branch);
@@ -39,7 +36,7 @@ public interface IRepositoryManager : IDisposable
ITask SwitchBranch(string branch);
ITask DeleteBranch(string branch, bool deleteUnmerged = false);
ITask CreateBranch(string branch, string baseBranch);
- ITask ListLocks(bool local);
+ ITask> ListLocks(bool local);
ITask LockFile(string file);
ITask UnlockFile(string file, bool force);
int WaitForEvents();
@@ -94,40 +91,34 @@ public RepositoryPathConfiguration(NPath repositoryPath)
class RepositoryManager : IRepositoryManager
{
- private readonly CancellationToken cancellationToken;
private readonly IGitConfig config;
private readonly IGitClient gitClient;
private readonly IPlatform platform;
private readonly IRepositoryPathConfiguration repositoryPaths;
private readonly ITaskManager taskManager;
- private readonly IUsageTracker usageTracker;
private readonly IRepositoryWatcher watcher;
private bool isBusy;
- public event Action OnCurrentBranchUpdated;
- public event Action OnCurrentRemoteUpdated;
+ public event Action OnCurrentBranchAndRemoteUpdated;
public event Action OnGitUserLoaded;
public event Action OnIsBusyChanged;
public event Action OnLocalBranchAdded;
public event Action> OnLocalBranchListUpdated;
public event Action OnLocalBranchRemoved;
public event Action OnLocalBranchUpdated;
- public event Action> OnLocksUpdated;
public event Action OnRemoteBranchAdded;
public event Action, Dictionary>> OnRemoteBranchListUpdated;
public event Action OnRemoteBranchRemoved;
- public event Action OnStatusUpdated;
+ public event Action OnRepositoryUpdated;
- public RepositoryManager(IPlatform platform, ITaskManager taskManager, IUsageTracker usageTracker, IGitConfig gitConfig,
+ public RepositoryManager(IPlatform platform, ITaskManager taskManager, IGitConfig gitConfig,
IRepositoryWatcher repositoryWatcher, IGitClient gitClient,
- IRepositoryPathConfiguration repositoryPaths, CancellationToken cancellationToken)
+ IRepositoryPathConfiguration repositoryPaths)
{
this.repositoryPaths = repositoryPaths;
this.platform = platform;
this.taskManager = taskManager;
- this.usageTracker = usageTracker;
- this.cancellationToken = cancellationToken;
this.gitClient = gitClient;
this.watcher = repositoryWatcher;
this.config = gitConfig;
@@ -135,7 +126,7 @@ public RepositoryManager(IPlatform platform, ITaskManager taskManager, IUsageTra
SetupWatcher();
}
- public static RepositoryManager CreateInstance(IPlatform platform, ITaskManager taskManager, IUsageTracker usageTracker,
+ public static RepositoryManager CreateInstance(IPlatform platform, ITaskManager taskManager,
IGitClient gitClient, NPath repositoryRoot)
{
var repositoryPathConfiguration = new RepositoryPathConfiguration(repositoryRoot);
@@ -144,8 +135,8 @@ public static RepositoryManager CreateInstance(IPlatform platform, ITaskManager
var repositoryWatcher = new RepositoryWatcher(platform, repositoryPathConfiguration, taskManager.Token);
- return new RepositoryManager(platform, taskManager, usageTracker, gitConfig, repositoryWatcher,
- gitClient, repositoryPathConfiguration, taskManager.Token);
+ return new RepositoryManager(platform, taskManager, gitConfig, repositoryWatcher,
+ gitClient, repositoryPathConfiguration);
}
public void Initialize()
@@ -180,12 +171,6 @@ public int WaitForEvents()
return watcher.CheckAndProcessEvents();
}
- public void Refresh()
- {
- Logger.Trace("Refresh");
- UpdateGitStatus();
- }
-
public ITask CommitAllFiles(string message, string body)
{
var add = GitClient.AddAll();
@@ -211,6 +196,13 @@ public ITask> Log()
return task;
}
+ public ITask Status()
+ {
+ var task = GitClient.Status();
+ HookupHandlers(task);
+ return task;
+ }
+
public ITask Fetch(string remote)
{
var task = GitClient.Fetch(remote);
@@ -285,49 +277,41 @@ public ITask CreateBranch(string branch, string baseBranch)
return HookupHandlers(task);
}
- public ITask ListLocks(bool local)
+ public ITask> ListLocks(bool local)
{
- var task = GitClient
- .ListLocks(local)
- .Then((success, locks) =>
- {
- if (success)
- {
- Logger.Trace("OnLocksUpdated");
- OnLocksUpdated?.Invoke(locks);
- }
- });
- return HookupHandlers(task);
+ var task = GitClient.ListLocks(local);
+ HookupHandlers(task);
+ return task;
}
public ITask LockFile(string file)
{
var task = GitClient.Lock(file);
- HookupHandlers(task);
-
- return task.Then(ListLocks(false));
+ return HookupHandlers(task);
}
public ITask UnlockFile(string file, bool force)
{
var task = GitClient.Unlock(file, force);
- HookupHandlers(task).Schedule(taskManager);
-
- return task.Then(ListLocks(false));
+ return HookupHandlers(task);
}
private void LoadGitUser()
{
- var user = new User();
- GitClient.GetConfig("user.name", GitConfigSource.User)
- .Then((success, value) => user.Name = value).Then(
- GitClient.GetConfig("user.email", GitConfigSource.User)
- .Then((success, value) => user.Email = value))
- .Then(() => {
- Logger.Trace("OnGitUserLoaded: {0}", user);
- OnGitUserLoaded?.Invoke(user);
- })
- .Start();
+ GitClient.GetConfigUserAndEmail()
+ .Then((success, strings) => {
+ var username = strings[0];
+ var email = strings[1];
+
+ var user = new User {
+ Name = username,
+ Email = email
+ };
+
+ Logger.Trace("OnGitUserLoaded: {0}", user);
+ OnGitUserLoaded?.Invoke(user);
+
+ }).Start();
}
private void SetupWatcher()
@@ -388,25 +372,7 @@ private void Watcher_OnRemoteBranchCreated(string remote, string name)
private void Watcher_OnRepositoryChanged()
{
Logger.Trace("OnRepositoryChanged");
- UpdateGitStatus();
- }
-
- private void UpdateGitStatus()
- {
- Logger.Trace("Updating Git Status");
-
- var task = GitClient.Status()
- .Finally((success, ex, data) =>
- {
- Logger.Trace($"GitStatus update: {success} {(data.HasValue ? data.Value.ToString() : "[null]")}");
- if (success && data.HasValue)
- {
- OnStatusUpdated?.Invoke(data.Value);
- Logger.Trace("Updated Git Status");
- }
- });
-
- HookupHandlers(task).Start();
+ OnRepositoryUpdated?.Invoke();
}
private void Watcher_OnConfigChanged()
@@ -418,7 +384,6 @@ private void Watcher_OnHeadChanged()
{
Logger.Trace("Watcher_OnHeadChanged");
UpdateHead();
- UpdateGitStatus();
}
private void UpdateCurrentBranchAndRemote(string head)
@@ -459,10 +424,8 @@ private void UpdateCurrentBranchAndRemote(string head)
}
Logger.Trace("OnCurrentBranchUpdated: {0}", branch.HasValue ? branch.Value.ToString() : "[NULL]");
- OnCurrentBranchUpdated?.Invoke(branch);
-
Logger.Trace("OnCurrentRemoteUpdated: {0}", remote.HasValue ? remote.Value.ToString() : "[NULL]");
- OnCurrentRemoteUpdated?.Invoke(remote);
+ OnCurrentBranchAndRemoteUpdated?.Invoke(branch, remote);
}
private void Watcher_OnIndexChanged()
diff --git a/src/GitHub.Api/Git/Tasks/GitStatusTask.cs b/src/GitHub.Api/Git/Tasks/GitStatusTask.cs
index e8ee0bf8f..da66e2c98 100644
--- a/src/GitHub.Api/Git/Tasks/GitStatusTask.cs
+++ b/src/GitHub.Api/Git/Tasks/GitStatusTask.cs
@@ -2,12 +2,12 @@
namespace GitHub.Unity
{
- class GitStatusTask : ProcessTask
+ class GitStatusTask : ProcessTask
{
private const string TaskName = "git status";
public GitStatusTask(IGitObjectFactory gitObjectFactory,
- CancellationToken token, IOutputProcessor processor = null)
+ CancellationToken token, IOutputProcessor processor = null)
: base(token, processor ?? new StatusOutputProcessor(gitObjectFactory))
{
Name = TaskName;
diff --git a/src/GitHub.Api/GitHub.Api.csproj b/src/GitHub.Api/GitHub.Api.csproj
index 8539e9725..2816e0140 100644
--- a/src/GitHub.Api/GitHub.Api.csproj
+++ b/src/GitHub.Api/GitHub.Api.csproj
@@ -99,6 +99,9 @@
+
+
+
@@ -110,8 +113,6 @@
-
-
diff --git a/src/GitHub.Api/GitHub.Api.csproj.DotSettings b/src/GitHub.Api/GitHub.Api.csproj.DotSettings
index 56bc11b91..83ace06a4 100644
--- a/src/GitHub.Api/GitHub.Api.csproj.DotSettings
+++ b/src/GitHub.Api/GitHub.Api.csproj.DotSettings
@@ -2,6 +2,7 @@
True
True
True
+ True
True
True
True
diff --git a/src/GitHub.Api/Helpers/Constants.cs b/src/GitHub.Api/Helpers/Constants.cs
index d89de8360..1f3fbe702 100644
--- a/src/GitHub.Api/Helpers/Constants.cs
+++ b/src/GitHub.Api/Helpers/Constants.cs
@@ -11,6 +11,6 @@ static class Constants
public const string TraceLoggingKey = "EnableTraceLogging";
public static readonly Version MinimumGitVersion = new Version(2, 11, 0);
- public static readonly Version MinimumGitLfsVersion = new Version(2, 2, 0);
+ public static readonly Version MinimumGitLfsVersion = new Version(2, 3, 4);
}
}
\ No newline at end of file
diff --git a/src/GitHub.Api/Helpers/SimpleJson.cs b/src/GitHub.Api/Helpers/SimpleJson.cs
index da3325c28..dd2cf8483 100644
--- a/src/GitHub.Api/Helpers/SimpleJson.cs
+++ b/src/GitHub.Api/Helpers/SimpleJson.cs
@@ -517,7 +517,6 @@ static class SimpleJson
private static readonly char[] EscapeTable;
private static readonly char[] EscapeCharacters = new char[] { '"', '\\', '\b', '\f', '\n', '\r', '\t' };
- private static readonly string EscapeCharactersString = new string(EscapeCharacters);
static SimpleJson()
{
diff --git a/src/GitHub.Api/Helpers/TaskHelpers.cs b/src/GitHub.Api/Helpers/TaskHelpers.cs
index e1c6e301e..1698b92ab 100644
--- a/src/GitHub.Api/Helpers/TaskHelpers.cs
+++ b/src/GitHub.Api/Helpers/TaskHelpers.cs
@@ -1,3 +1,4 @@
+using System;
using System.Threading.Tasks;
namespace GitHub.Unity
@@ -8,5 +9,16 @@ public static Task GetCompletedTask(T result)
{
return TaskEx.FromResult(result);
}
+
+ public static Task ToTask(this Exception exception)
+ {
+ TaskCompletionSource completionSource = new TaskCompletionSource();
+ completionSource.TrySetException(exception);
+ return completionSource.Task;
+ }
+ }
+
+ public class NotReadyException : Exception
+ {
}
}
\ No newline at end of file
diff --git a/src/GitHub.Api/Installer/GitInstaller.cs b/src/GitHub.Api/Installer/GitInstaller.cs
index ee18a262e..f54765298 100644
--- a/src/GitHub.Api/Installer/GitInstaller.cs
+++ b/src/GitHub.Api/Installer/GitInstaller.cs
@@ -6,8 +6,8 @@ namespace GitHub.Unity
{
class GitInstaller : IGitInstaller
{
- public const string WindowsGitLfsExecutableMD5 = "ef51379a06577bcdeef372d297d6cd7f";
- public const string MacGitLfsExecutableMD5 = "2b324cbfbb9196cf6a3c0a0918c434c7";
+ public const string WindowsGitLfsExecutableMD5 = "177bb14d0c08f665a24f0d5516c3b080";
+ public const string MacGitLfsExecutableMD5 = "f81a1a065a26a4123193e8fd96c561ad";
private const string PortableGitExpectedVersion = "f02737a78695063deace08e96d5042710d3e32db";
private const string PackageName = "PortableGit";
diff --git a/src/GitHub.Api/Localization.resx b/src/GitHub.Api/Localization.resx
index 6768713f9..ba8fc9667 100644
--- a/src/GitHub.Api/Localization.resx
+++ b/src/GitHub.Api/Localization.resx
@@ -1,17 +1,17 @@
-
@@ -286,7 +286,7 @@
Branch pushed
- Initialize repository
+ Initialize a git repository for this project
Switch branch
@@ -294,4 +294,4 @@
Could not switch to branch {0}
-
\ No newline at end of file
+
diff --git a/src/GitHub.Api/NewTaskSystem/BaseOutputProcessor.cs b/src/GitHub.Api/NewTaskSystem/BaseOutputProcessor.cs
index 9456c7c43..1c2f2ea0d 100644
--- a/src/GitHub.Api/NewTaskSystem/BaseOutputProcessor.cs
+++ b/src/GitHub.Api/NewTaskSystem/BaseOutputProcessor.cs
@@ -84,7 +84,6 @@ public override void LineReceived(string line)
abstract class FirstResultOutputProcessor : BaseOutputProcessor
{
- private readonly StringBuilder sb = new StringBuilder();
private bool isSet = false;
public override void LineReceived(string line)
{
diff --git a/src/GitHub.Api/OutputProcessors/RemoteListOutputProcessor.cs b/src/GitHub.Api/OutputProcessors/RemoteListOutputProcessor.cs
index dbb854cf7..293ebf142 100644
--- a/src/GitHub.Api/OutputProcessors/RemoteListOutputProcessor.cs
+++ b/src/GitHub.Api/OutputProcessors/RemoteListOutputProcessor.cs
@@ -99,15 +99,7 @@ private void ReturnRemote()
currentUrl = currentUrl.Substring(user.Length + 1);
}
- RaiseOnEntry(new GitRemote
- {
- Name = currentName,
- Host = host,
- Url = currentUrl,
- User = user,
- Function = remoteFunction
- });
-
+ RaiseOnEntry(new GitRemote(currentName, host, currentUrl, remoteFunction, user, null, null));
Reset();
}
diff --git a/src/GitHub.Api/OutputProcessors/StatusOutputProcessor.cs b/src/GitHub.Api/OutputProcessors/StatusOutputProcessor.cs
index a2629893f..1b95daca4 100644
--- a/src/GitHub.Api/OutputProcessors/StatusOutputProcessor.cs
+++ b/src/GitHub.Api/OutputProcessors/StatusOutputProcessor.cs
@@ -5,7 +5,7 @@
namespace GitHub.Unity
{
- class StatusOutputProcessor : BaseOutputProcessor
+ class StatusOutputProcessor : BaseOutputProcessor
{
private static readonly Regex branchTrackedAndDelta = new Regex(@"(.*)\.\.\.(.*)\s\[(.*)\]",
RegexOptions.Compiled);
diff --git a/src/GitHub.Api/Platform/DefaultEnvironment.cs b/src/GitHub.Api/Platform/DefaultEnvironment.cs
index f4b60389a..7301e3875 100644
--- a/src/GitHub.Api/Platform/DefaultEnvironment.cs
+++ b/src/GitHub.Api/Platform/DefaultEnvironment.cs
@@ -7,6 +7,7 @@ namespace GitHub.Unity
public class DefaultEnvironment : IEnvironment
{
private const string logFile = "github-unity.log";
+ private ICacheContainer cacheContainer;
public NPath LogPath { get; }
public DefaultEnvironment()
@@ -35,6 +36,12 @@ public DefaultEnvironment()
LogPath = UserCachePath.Combine(logFile);
}
+ public DefaultEnvironment(ICacheContainer cacheContainer)
+ : this()
+ {
+ this.cacheContainer = cacheContainer;
+ }
+
public void Initialize(string unityVersion, NPath extensionInstallPath, NPath unityPath, NPath assetsPath)
{
ExtensionInstallPath = extensionInstallPath;
@@ -42,7 +49,6 @@ public void Initialize(string unityVersion, NPath extensionInstallPath, NPath un
UnityAssetsPath = assetsPath;
UnityProjectPath = assetsPath.Parent;
UnityVersion = unityVersion;
- InitializeRepository();
}
public void InitializeRepository(NPath expectedRepositoryPath = null)
@@ -80,7 +86,7 @@ public void InitializeRepository(NPath expectedRepositoryPath = null)
{
Logger.Trace("Determined expectedRepositoryPath:{0}", expectedRepositoryPath);
RepositoryPath = expectedRepositoryPath;
- Repository = new Repository(RepositoryPath.FileName, RepositoryPath);
+ Repository = new Repository(RepositoryPath, cacheContainer);
}
}
diff --git a/src/GitHub.Api/Platform/Platform.cs b/src/GitHub.Api/Platform/Platform.cs
index 6e4a20e4e..71456a5a5 100644
--- a/src/GitHub.Api/Platform/Platform.cs
+++ b/src/GitHub.Api/Platform/Platform.cs
@@ -26,7 +26,7 @@ public IPlatform Initialize(IProcessManager processManager, ITaskManager taskMan
if (CredentialManager == null)
{
- CredentialManager = new GitCredentialManager(Environment, processManager, taskManager);
+ CredentialManager = new GitCredentialManager(processManager, taskManager);
Keychain = new Keychain(Environment, CredentialManager);
Keychain.Initialize();
}
diff --git a/src/GitHub.Api/PlatformResources/mac/git-lfs.zip b/src/GitHub.Api/PlatformResources/mac/git-lfs.zip
index 1ef246ea3..3932710f3 100644
--- a/src/GitHub.Api/PlatformResources/mac/git-lfs.zip
+++ b/src/GitHub.Api/PlatformResources/mac/git-lfs.zip
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:8b30e08751549f70f052eda49e0e2875ce006c98d013a17c46943b830f22e867
-size 2780647
+oid sha256:5bde4722bdbb24ec6651aa2ab559bfa6e85d31ee9e4195c81f6d6fa7548cacc6
+size 2905910
diff --git a/src/GitHub.Api/PlatformResources/windows/git-lfs.zip b/src/GitHub.Api/PlatformResources/windows/git-lfs.zip
index 82a637cce..5a56712a7 100644
--- a/src/GitHub.Api/PlatformResources/windows/git-lfs.zip
+++ b/src/GitHub.Api/PlatformResources/windows/git-lfs.zip
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:64b2e9186ff553f2e4afe4db38b21d27e3dde08f3347ff8ea88c667dfcefb92f
-size 2807324
+oid sha256:6a4699fe6028a3727d76b218a10a7e9c6276f097b8ebd782f2e7b3418dacda07
+size 2652291
diff --git a/src/UnityExtension/Assets/Editor/GitHub.Unity/ApplicationCache.cs b/src/UnityExtension/Assets/Editor/GitHub.Unity/ApplicationCache.cs
index c18c19d51..55c9ffd38 100644
--- a/src/UnityExtension/Assets/Editor/GitHub.Unity/ApplicationCache.cs
+++ b/src/UnityExtension/Assets/Editor/GitHub.Unity/ApplicationCache.cs
@@ -1,16 +1,17 @@
using System;
using System.Collections.Generic;
+using System.Linq;
+using Octokit;
using UnityEditor;
using UnityEngine;
-using Debug = System.Diagnostics.Debug;
+using Application = UnityEngine.Application;
namespace GitHub.Unity
{
sealed class ApplicationCache : ScriptObjectSingleton
{
- [SerializeField] private bool firstRun = true;
-
[NonSerialized] private bool? val;
+ [SerializeField] private bool firstRun = true;
public bool FirstRun
{
@@ -34,21 +35,39 @@ public bool FirstRun
sealed class EnvironmentCache : ScriptObjectSingleton
{
+ [NonSerialized] private IEnvironment environment;
+ [SerializeField] private string extensionInstallPath;
[SerializeField] private string repositoryPath;
[SerializeField] private string unityApplication;
[SerializeField] private string unityAssetsPath;
- [SerializeField] private string extensionInstallPath;
- [SerializeField] private string gitExecutablePath;
[SerializeField] private string unityVersion;
- [NonSerialized] private IEnvironment environment;
+ public void Flush()
+ {
+ repositoryPath = Environment.RepositoryPath;
+ unityApplication = Environment.UnityApplication;
+ unityAssetsPath = Environment.UnityAssetsPath;
+ extensionInstallPath = Environment.ExtensionInstallPath;
+ Save(true);
+ }
+
+ private NPath DetermineInstallationPath()
+ {
+ // Juggling to find out where we got installed
+ var shim = CreateInstance();
+ var script = MonoScript.FromScriptableObject(shim);
+ var scriptPath = AssetDatabase.GetAssetPath(script).ToNPath();
+ DestroyImmediate(shim);
+ return scriptPath.Parent;
+ }
+
public IEnvironment Environment
{
get
{
if (environment == null)
{
- environment = new DefaultEnvironment();
+ environment = new DefaultEnvironment(new CacheContainer());
if (unityApplication == null)
{
unityAssetsPath = Application.dataPath;
@@ -56,145 +75,881 @@ public IEnvironment Environment
extensionInstallPath = DetermineInstallationPath();
unityVersion = Application.unityVersion;
}
- environment.Initialize(unityVersion, extensionInstallPath.ToNPath(), unityApplication.ToNPath(), unityAssetsPath.ToNPath());
- environment.InitializeRepository(!String.IsNullOrEmpty(repositoryPath) ? repositoryPath.ToNPath() : null);
+ environment.Initialize(unityVersion, extensionInstallPath.ToNPath(), unityApplication.ToNPath(),
+ unityAssetsPath.ToNPath());
+ environment.InitializeRepository(!String.IsNullOrEmpty(repositoryPath)
+ ? repositoryPath.ToNPath()
+ : null);
Flush();
}
return environment;
}
}
+ }
- private NPath DetermineInstallationPath()
+ abstract class ManagedCacheBase : ScriptObjectSingleton where T : ScriptableObject, IManagedCache
+ {
+ private static readonly TimeSpan DataTimeout = TimeSpan.MaxValue;
+
+ [NonSerialized] private DateTimeOffset? lastUpdatedAtValue;
+
+ [NonSerialized] private DateTimeOffset? lastVerifiedAtValue;
+
+ public event Action CacheInvalidated;
+ public event Action CacheUpdated;
+
+ protected ManagedCacheBase()
{
- // Juggling to find out where we got installed
- var shim = ScriptableObject.CreateInstance();
- var script = MonoScript.FromScriptableObject(shim);
- var scriptPath = AssetDatabase.GetAssetPath(script).ToNPath();
- ScriptableObject.DestroyImmediate(shim);
- return scriptPath.Parent;
+ Logger = Logging.GetLogger(GetType());
}
- public void Flush()
+ public void ValidateData()
{
- repositoryPath = Environment.RepositoryPath;
- unityApplication = Environment.UnityApplication;
- unityAssetsPath = Environment.UnityAssetsPath;
- extensionInstallPath = Environment.ExtensionInstallPath;
- gitExecutablePath = Environment.GitExecutablePath;
+ if (DateTimeOffset.Now - LastUpdatedAt > DataTimeout)
+ {
+ InvalidateData();
+ }
+ }
+
+ public void InvalidateData()
+ {
+ Logger.Trace("Invalidated");
+ CacheInvalidated.SafeInvoke();
+ SaveData(DateTimeOffset.Now, true);
+ }
+
+ protected void SaveData(DateTimeOffset now, bool isUpdated)
+ {
+ if (isUpdated)
+ {
+ LastUpdatedAt = now;
+ }
+
+ LastVerifiedAt = now;
Save(true);
+
+ if (isUpdated)
+ {
+ Logger.Trace("Updated: {0}", now);
+ CacheUpdated.SafeInvoke(now);
+ }
+ else
+ {
+ Logger.Trace("Verified: {0}", now);
+ }
+ }
+
+ public abstract string LastUpdatedAtString { get; protected set; }
+ public abstract string LastVerifiedAtString { get; protected set; }
+
+ public DateTimeOffset LastUpdatedAt
+ {
+ get
+ {
+ if (!lastUpdatedAtValue.HasValue)
+ {
+ lastUpdatedAtValue = DateTimeOffset.Parse(LastUpdatedAtString);
+ }
+
+ return lastUpdatedAtValue.Value;
+ }
+ set
+ {
+ LastUpdatedAtString = value.ToString();
+ lastUpdatedAtValue = null;
+ }
+ }
+
+ public DateTimeOffset LastVerifiedAt
+ {
+ get
+ {
+ if (!lastVerifiedAtValue.HasValue)
+ {
+ lastVerifiedAtValue = DateTimeOffset.Parse(LastVerifiedAtString);
+ }
+
+ return lastVerifiedAtValue.Value;
+ }
+ set
+ {
+ LastVerifiedAtString = value.ToString();
+ lastVerifiedAtValue = null;
+ }
}
+
+ protected ILogging Logger { get; private set; }
}
- [Location("cache/branches.yaml", LocationAttribute.Location.LibraryFolder)]
- sealed class BranchCache : ScriptObjectSingleton, IBranchCache
+ [Serializable]
+ class LocalConfigBranchDictionary : SerializableDictionary, ILocalConfigBranchDictionary
+ {
+ public LocalConfigBranchDictionary()
+ { }
+
+ public LocalConfigBranchDictionary(IDictionary dictionary) : base()
+ {
+ foreach (var pair in dictionary)
+ {
+ this.Add(pair.Key, pair.Value);
+ }
+ }
+ }
+
+ [Serializable]
+ public class ArrayContainer
+ {
+ [SerializeField] public T[] Values = new T[0];
+ }
+
+ [Serializable]
+ public class StringArrayContainer: ArrayContainer
+ {
+ }
+
+ [Serializable]
+ public class ConfigBranchArrayContainer : ArrayContainer
+ {
+ }
+
+ [Serializable]
+ class RemoteConfigBranchDictionary : Dictionary>, ISerializationCallbackReceiver, IRemoteConfigBranchDictionary
+ {
+ [SerializeField] private string[] keys = new string[0];
+ [SerializeField] private StringArrayContainer[] subKeys = new StringArrayContainer[0];
+ [SerializeField] private ConfigBranchArrayContainer[] subKeyValues = new ConfigBranchArrayContainer[0];
+
+ public RemoteConfigBranchDictionary()
+ { }
+
+ public RemoteConfigBranchDictionary(Dictionary> dictionary)
+ {
+ foreach (var pair in dictionary)
+ {
+ Add(pair.Key, pair.Value.ToDictionary(valuePair => valuePair.Key, valuePair => valuePair.Value));
+ }
+ }
+
+ // save the dictionary to lists
+ public void OnBeforeSerialize()
+ {
+ var keyList = new List();
+ var subKeysList = new List();
+ var subKeysValuesList = new List();
+
+ foreach (var pair in this)
+ {
+ var pairKey = pair.Key;
+ keyList.Add(pairKey);
+
+ var serializeSubKeys = new List();
+ var serializeSubKeyValues = new List();
+
+ var subDictionary = pair.Value;
+ foreach (var subPair in subDictionary)
+ {
+ serializeSubKeys.Add(subPair.Key);
+ serializeSubKeyValues.Add(subPair.Value);
+ }
+
+ subKeysList.Add(new StringArrayContainer { Values = serializeSubKeys.ToArray() });
+ subKeysValuesList.Add(new ConfigBranchArrayContainer { Values = serializeSubKeyValues.ToArray() });
+ }
+
+ keys = keyList.ToArray();
+ subKeys = subKeysList.ToArray();
+ subKeyValues = subKeysValuesList.ToArray();
+ }
+
+ // load dictionary from lists
+ public void OnAfterDeserialize()
+ {
+ Clear();
+
+ if (keys.Length != subKeys.Length || subKeys.Length != subKeyValues.Length)
+ {
+ throw new Exception("Deserialization length mismatch");
+ }
+
+ for (var remoteIndex = 0; remoteIndex < keys.Length; remoteIndex++)
+ {
+ var remote = keys[remoteIndex];
+
+ var subKeyContainer = subKeys[remoteIndex];
+ var subKeyValueContainer = subKeyValues[remoteIndex];
+
+ if (subKeyContainer.Values.Length != subKeyValueContainer.Values.Length)
+ {
+ throw new Exception("Deserialization length mismatch");
+ }
+
+ var branchesDictionary = new Dictionary();
+ for (var branchIndex = 0; branchIndex < subKeyContainer.Values.Length; branchIndex++)
+ {
+ var remoteBranchKey = subKeyContainer.Values[branchIndex];
+ var remoteBranch = subKeyValueContainer.Values[branchIndex];
+
+ branchesDictionary.Add(remoteBranchKey, remoteBranch);
+ }
+
+ Add(remote, branchesDictionary);
+ }
+ }
+
+ IEnumerator>> IEnumerable>>.GetEnumerator()
+ {
+ throw new NotImplementedException();
+ //return AsDictionary
+ // .Select(pair => new KeyValuePair>(pair.Key, pair.Value.AsDictionary))
+ // .GetEnumerator();
+ }
+
+ void ICollection>>.Add(KeyValuePair> item)
+ {
+ throw new NotImplementedException();
+ //Guard.ArgumentNotNull(item, "item");
+ //Guard.ArgumentNotNull(item.Value, "item.Value");
+ //
+ //var serializableDictionary = item.Value as SerializableDictionary;
+ //if (serializableDictionary == null)
+ //{
+ // serializableDictionary = new SerializableDictionary(item.Value);
+ //}
+ //
+ //Add(item.Key, serializableDictionary);
+ }
+
+ bool ICollection>>.Contains(KeyValuePair> item)
+ {
+ throw new NotImplementedException();
+ }
+
+ void ICollection>>.CopyTo(KeyValuePair>[] array, int arrayIndex)
+ {
+ throw new NotImplementedException();
+ }
+
+ bool ICollection>>.Remove(KeyValuePair> item)
+ {
+ throw new NotImplementedException();
+ }
+
+ bool ICollection>>.IsReadOnly
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ void IDictionary>.Add(string key, IDictionary value)
+ {
+ throw new NotImplementedException();
+ }
+
+ bool IDictionary>.TryGetValue(string key, out IDictionary value)
+ {
+ value = null;
+
+ Dictionary branches;
+ if (TryGetValue(key, out branches))
+ {
+ value = branches;
+ return true;
+ }
+
+ return false;
+ }
+
+ IDictionary IDictionary>.this[string key]
+ {
+ get
+ {
+ throw new NotImplementedException();
+ //var dictionary = (IDictionary>)this;
+ //IDictionary value;
+ //if (!dictionary.TryGetValue(key, out value))
+ //{
+ // throw new KeyNotFoundException();
+ //}
+ //
+ //return value;
+ }
+ set
+ {
+ throw new NotImplementedException();
+ //var dictionary = (IDictionary>)this;
+ //dictionary.Add(key, value);
+ }
+ }
+
+ ICollection IDictionary>.Keys
+ {
+ get
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ ICollection> IDictionary>.Values
+ {
+ get
+ {
+ return Values.Cast>().ToArray();
+ }
+ }
+ }
+
+ [Serializable]
+ class ConfigRemoteDictionary : SerializableDictionary, IConfigRemoteDictionary
{
- [SerializeField] private List localBranches;
- [SerializeField] private List remoteBranches;
+ public ConfigRemoteDictionary()
+ { }
- public BranchCache()
+ public ConfigRemoteDictionary(IDictionary dictionary)
{
+ foreach (var pair in dictionary)
+ {
+ this.Add(pair.Key, pair.Value);
+ }
}
+ }
- public List LocalBranches
+ [Location("cache/repoinfo.yaml", LocationAttribute.Location.LibraryFolder)]
+ sealed class RepositoryInfoCache : ManagedCacheBase, IRepositoryInfoCache
+ {
+ [SerializeField] private string lastUpdatedAtString = DateTimeOffset.MinValue.ToString();
+ [SerializeField] private string lastVerifiedAtString = DateTimeOffset.MinValue.ToString();
+ [SerializeField] private GitRemote gitRemote;
+ [SerializeField] private GitBranch gitBranch;
+
+ public GitRemote? CurrentGitRemote
{
get
{
- if (localBranches == null)
- localBranches = new List();
- return localBranches;
+ ValidateData();
+ return gitRemote.Equals(GitRemote.Default) ? (GitRemote?)null : gitRemote;
}
set
{
- localBranches = value;
- Save(true);
+ var now = DateTimeOffset.Now;
+ var isUpdated = false;
+
+ Logger.Trace("Updating: {0} gitRemote:{1}", now, value);
+
+ if (!Nullable.Equals(gitRemote, value))
+ {
+ gitRemote = value ?? GitRemote.Default;
+ isUpdated = true;
+ }
+
+ SaveData(now, isUpdated);
}
}
- public List RemoteBranches
+
+ public GitBranch? CurentGitBranch
{
get
{
- if (remoteBranches == null)
- remoteBranches = new List();
- return remoteBranches;
+ ValidateData();
+ return gitBranch.Equals(GitBranch.Default) ? (GitBranch?)null : gitBranch;
}
set
{
- remoteBranches = value;
- Save(true);
+ var now = DateTimeOffset.Now;
+ var isUpdated = false;
+
+ Logger.Trace("Updating: {0} gitBranch:{1}", now, value);
+
+ if (!Nullable.Equals(gitBranch, value))
+ {
+ gitBranch = value ?? GitBranch.Default;
+ isUpdated = true;
+ }
+
+ SaveData(now, isUpdated);
}
}
+
+ public override string LastUpdatedAtString
+ {
+ get { return lastUpdatedAtString; }
+ protected set { lastUpdatedAtString = value; }
+ }
+
+ public override string LastVerifiedAtString
+ {
+ get { return lastVerifiedAtString; }
+ protected set { lastVerifiedAtString = value; }
+ }
}
- [Location("views/branches.yaml", LocationAttribute.Location.LibraryFolder)]
- sealed class Favorites : ScriptObjectSingleton
+ [Location("cache/branches.yaml", LocationAttribute.Location.LibraryFolder)]
+ sealed class BranchCache : ManagedCacheBase