Changed included in this update:

Handle IOExceptions when setting Console.InputEncoding
Only add to the projection if the index entry has the skipworktree bit and is
Update Git for Windows for status cache fixes
Stand up script to enable Mac functional tests to run as a build definition
Native hooks on Mac
Update the readme to indicate the rename is in progress
Remove GC.Collect, update allocation strategy, and add logging for index parsing
Update to ProjFS version 2018.719.1
Remove the string split from upgrading to modified paths
Update git for windows with version that uses new msys2 runtime
add utility method to convert from Windows to Unix paths
Run prefetch in the background
Update to ProjFS version 2018.706.1
Tweak logic for determining the current test directory
Update GVFS.Hooks to work on Mac, install GVFS.Hooks on Mac, and fix clone+mount with GVFS aware Git
Fixes incremental builds when changing props files in GVFS.Build
This commit is contained in:
Kevin Willford 2018-08-03 09:55:34 -06:00
Родитель cb68bfee31
Коммит c7e37417bd
146 изменённых файлов: 3197 добавлений и 1132 удалений

Просмотреть файл

@ -45,7 +45,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GVFS.NativeTests", "GVFS\GV
{A4984251-840E-4622-AD0C-66DFCE2B2574} = {A4984251-840E-4622-AD0C-66DFCE2B2574}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GVFS.Hooks", "GVFS\GVFS.Hooks\GVFS.Hooks.csproj", "{BDA91EE5-C684-4FC5-A90A-B7D677421917}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GVFS.Hooks.Windows", "GVFS\GVFS.Hooks\GVFS.Hooks.Windows.csproj", "{BDA91EE5-C684-4FC5-A90A-B7D677421917}"
ProjectSection(ProjectDependencies) = postProject
{A4984251-840E-4622-AD0C-66DFCE2B2574} = {A4984251-840E-4622-AD0C-66DFCE2B2574}
EndProjectSection
@ -57,7 +57,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GVFS.Service", "GVFS\GVFS.S
{BDA91EE5-C684-4FC5-A90A-B7D677421917} = {BDA91EE5-C684-4FC5-A90A-B7D677421917}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GVFS.ReadObjectHook", "GVFS\GVFS.ReadObjectHook\GVFS.ReadObjectHook.vcxproj", "{5A6656D5-81C7-472C-9DC8-32D071CB2258}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GVFS.ReadObjectHook", "GVFS\GVFS.ReadObjectHook\GVFS.ReadObjectHook.Windows.vcxproj", "{5A6656D5-81C7-472C-9DC8-32D071CB2258}"
ProjectSection(ProjectDependencies) = postProject
{A4984251-840E-4622-AD0C-66DFCE2B2574} = {A4984251-840E-4622-AD0C-66DFCE2B2574}
EndProjectSection
@ -106,7 +106,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GVFS.SignFiles", "GVFS\GVFS
{93B403FD-DAFB-46C5-9636-B122792A548A} = {93B403FD-DAFB-46C5-9636-B122792A548A}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GVFS.VirtualFileSystemHook", "GVFS\GVFS.VirtualFileSystemHook\GVFS.VirtualFileSystemHook.vcxproj", "{2D23AB54-541F-4ABC-8DCA-08C199E97ABB}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GVFS.VirtualFileSystemHook", "GVFS\GVFS.VirtualFileSystemHook\GVFS.VirtualFileSystemHook.Windows.vcxproj", "{2D23AB54-541F-4ABC-8DCA-08C199E97ABB}"
ProjectSection(ProjectDependencies) = postProject
{A4984251-840E-4622-AD0C-66DFCE2B2574} = {A4984251-840E-4622-AD0C-66DFCE2B2574}
EndProjectSection
@ -165,6 +165,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GVFS.FunctionalTests", "GVF
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GVFS.FunctionalTests.LockHolder", "GVFS\GVFS.FunctionalTests.LockHolder\GVFS.FunctionalTests.LockHolder.csproj", "{FA273F69-5762-43D8-AEA1-B4F08090D624}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GVFS.Hooks.Mac", "GVFS\GVFS.Hooks\GVFS.Hooks.Mac.csproj", "{4CC2A90D-D240-4382-B4BF-5E175515E492}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug.Mac|x64 = Debug.Mac|x64
@ -359,6 +361,14 @@ Global
{FA273F69-5762-43D8-AEA1-B4F08090D624}.Release.Mac|x64.ActiveCfg = Release|x64
{FA273F69-5762-43D8-AEA1-B4F08090D624}.Release.Windows|x64.ActiveCfg = Release|x64
{FA273F69-5762-43D8-AEA1-B4F08090D624}.Release.Windows|x64.Build.0 = Release|x64
{4CC2A90D-D240-4382-B4BF-5E175515E492}.Debug.Mac|x64.ActiveCfg = Debug|x64
{4CC2A90D-D240-4382-B4BF-5E175515E492}.Debug.Mac|x64.Build.0 = Debug|x64
{4CC2A90D-D240-4382-B4BF-5E175515E492}.Debug.Windows|x64.ActiveCfg = Debug|x64
{4CC2A90D-D240-4382-B4BF-5E175515E492}.Debug.Windows|x64.Build.0 = Debug|x64
{4CC2A90D-D240-4382-B4BF-5E175515E492}.Release.Mac|x64.ActiveCfg = Release|x64
{4CC2A90D-D240-4382-B4BF-5E175515E492}.Release.Mac|x64.Build.0 = Release|x64
{4CC2A90D-D240-4382-B4BF-5E175515E492}.Release.Windows|x64.ActiveCfg = Release|x64
{4CC2A90D-D240-4382-B4BF-5E175515E492}.Release.Windows|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -392,6 +402,7 @@ Global
{0F0A008E-AB12-40EC-A671-37A541B08C7F} = {C41F10F9-1163-4CFA-A465-EA728F75E9FA}
{BD7C5776-82F2-40C6-AF01-B3CC1E2D83AF} = {C41F10F9-1163-4CFA-A465-EA728F75E9FA}
{FA273F69-5762-43D8-AEA1-B4F08090D624} = {C41F10F9-1163-4CFA-A465-EA728F75E9FA}
{4CC2A90D-D240-4382-B4BF-5E175515E492} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A025908B-DAB1-46CB-83A3-56F3B863D8FA}

Просмотреть файл

@ -12,12 +12,12 @@ using System.Linq;
namespace FastFetch
{
public class CheckoutFetchHelper : PrefetchHelper
public class CheckoutPrefetcher : BlobPrefetcher
{
private readonly bool allowIndexMetadataUpdateFromWorkingTree;
private readonly int checkoutThreadCount;
public CheckoutFetchHelper(
public CheckoutPrefetcher(
ITracer tracer,
Enlistment enlistment,
GitObjectsHttpRequestor objectRequestor,
@ -64,7 +64,7 @@ namespace FastFetch
this.DownloadMissingCommit(commitToFetch, this.GitObjects);
// Configure pipeline
// Checkout uses DiffHelper when running checkout.Start(), which we use instead of LsTreeHelper like in FetchHelper.cs
// Checkout uses DiffHelper when running checkout.Start(), which we use instead of LsTreeHelper
// Checkout diff output => FindMissingBlobs => BatchDownload => IndexPack => Checkout available blobs
CheckoutJob checkout = new CheckoutJob(this.checkoutThreadCount, this.FolderList, commitToFetch, this.Tracer, this.Enlistment);
FindMissingBlobsJob blobFinder = new FindMissingBlobsJob(this.SearchThreadCount, checkout.RequiredBlobs, checkout.AvailableBlobShas, this.Tracer, this.Enlistment);

Просмотреть файл

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\LibGit2Sharp.NativeBinaries.props" Condition="Exists('..\LibGit2Sharp.NativeBinaries.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
@ -56,7 +56,7 @@
<Compile Include="$(BuildOutputDir)\CommonAssemblyVersion.cs">
<Link>CommonAssemblyVersion.cs</Link>
</Compile>
<Compile Include="CheckoutFetchHelper.cs" />
<Compile Include="CheckoutPrefetcher.cs" />
<Compile Include="FastFetchVerb.cs" />
<Compile Include="..\GVFS.PlatformLoader\PlatformLoader.Windows.cs" />
<Compile Include="Program.cs" />

Просмотреть файл

@ -218,9 +218,9 @@ namespace FastFetch
});
RetryConfig retryConfig = new RetryConfig(this.MaxAttempts, TimeSpan.FromMinutes(RetryConfig.FetchAndCloneTimeoutMinutes));
PrefetchHelper fetchHelper = this.GetFetchHelper(tracer, enlistment, cacheServer, retryConfig);
BlobPrefetcher prefetcher = this.GetFolderPrefetcher(tracer, enlistment, cacheServer, retryConfig);
string error;
if (!PrefetchHelper.TryLoadFolderList(enlistment, this.FolderList, this.FolderListFile, fetchHelper.FolderList, out error))
if (!BlobPrefetcher.TryLoadFolderList(enlistment, this.FolderList, this.FolderListFile, prefetcher.FolderList, out error))
{
tracer.RelatedError(error);
Console.WriteLine(error);
@ -237,10 +237,10 @@ namespace FastFetch
try
{
bool isBranch = this.Commit == null;
fetchHelper.Prefetch(commitish, isBranch);
return !fetchHelper.HasFailures;
prefetcher.Prefetch(commitish, isBranch);
return !prefetcher.HasFailures;
}
catch (PrefetchHelper.FetchException e)
catch (BlobPrefetcher.FetchException e)
{
tracer.RelatedError(e.Message);
return false;
@ -263,7 +263,7 @@ namespace FastFetch
Console.WriteLine("See the full log at " + fastfetchLogFile);
}
isSuccess &= !fetchHelper.HasFailures;
isSuccess &= !prefetcher.HasFailures;
}
catch (AggregateException e)
{
@ -303,13 +303,13 @@ namespace FastFetch
return enlistment.RepoUrl;
}
private PrefetchHelper GetFetchHelper(ITracer tracer, Enlistment enlistment, CacheServerInfo cacheServer, RetryConfig retryConfig)
private BlobPrefetcher GetFolderPrefetcher(ITracer tracer, Enlistment enlistment, CacheServerInfo cacheServer, RetryConfig retryConfig)
{
GitObjectsHttpRequestor objectRequestor = new GitObjectsHttpRequestor(tracer, enlistment, cacheServer, retryConfig);
if (this.Checkout)
{
return new CheckoutFetchHelper(
return new CheckoutPrefetcher(
tracer,
enlistment,
objectRequestor,
@ -322,7 +322,7 @@ namespace FastFetch
}
else
{
return new PrefetchHelper(
return new BlobPrefetcher(
tracer,
enlistment,
objectRequestor,

Просмотреть файл

@ -40,19 +40,19 @@
<Compile Include="GenerateVersionInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="GVFS.props">
<BuildProps Include="GVFS.props">
<SubType>Designer</SubType>
</None>
<None Include="GVFS.cs.props">
</BuildProps>
<BuildProps Include="GVFS.cs.props">
<SubType>Designer</SubType>
</None>
<None Include="GVFS.cpp.props">
</BuildProps>
<BuildProps Include="GVFS.cpp.props">
<SubType>Designer</SubType>
</None>
<None Include="packages.config" />
<None Include="ProjFS.props">
</BuildProps>
<GeneratedPackageConfig Include="packages.config" />
<BuildProps Include="ProjFS.props">
<SubType>Designer</SubType>
</None>
</BuildProps>
</ItemGroup>
<Target Name="GetTargetFrameworkProperties" />
@ -129,7 +129,6 @@
<PropertyGroup>
<GVFSSetupPath>$(BuildOutputDir)\GVFS.Installer\bin\x64\$(Configuration)\SetupGVFS.$(GVFSVersion).exe</GVFSSetupPath>
<GitPackagesConfigPath>$(MSBuildThisFileDirectory)packages.config</GitPackagesConfigPath>
<OutDir>$(BuildOutputDir)\GVFS.Build\</OutDir>
<GitVersionConstantsPath>$(OutDir)GVFSConstants.GitVersion.cs</GitVersionConstantsPath>
<G4WInstallBatPath>$(OutDir)InstallG4W.bat</G4WInstallBatPath>
@ -160,13 +159,13 @@
</ItemGroup>
<Target Name="GVFSPreBuild"
Inputs="$(MSBuildThisFileFullPath);$(MSBuildProjectFullPath);$(MSBuildThisFileDirectory)GenerateG4WNugetReference.cs"
Outputs="$(GitPackagesConfigPath)">
Inputs="$(MSBuildThisFileFullPath);$(MSBuildProjectFullPath);$(MSBuildThisFileDirectory)GenerateG4WNugetReference.cs;@(BuildProps)"
Outputs="@(GeneratedPackageConfig)">
<GenerateG4WNugetReference GitPackageVersion="$(GitPackageVersion)" Condition="'$(OS)' == 'Windows_NT'" />
</Target>
<Target Name="GVFSPackageRestore"
Inputs="$(MSBuildThisFileFullPath);$(MSBuildProjectFullPath);$(GitPackagesConfigPath)"
Inputs="$(MSBuildThisFileFullPath);$(MSBuildProjectFullPath);@(GeneratedPackageConfig)"
Outputs="$(RestoreTimestampPath)"
DependsOnTargets="GVFSPreBuild"
Condition="'$(OS)' == 'Windows_NT'">
@ -177,7 +176,7 @@
<Target Name="GenerateInstallScripts"
DependsOnTargets="GVFSPackageRestore;$(GenerateInstallScriptsDependsOn)"
Inputs="$(RestoreTimestampPath);$(GitPackagesConfigPath);$(MSBuildThisFileFullPath);$(MSBuildProjectFullPath);$(MSBuildThisFileDirectory)GenerateGitVersionConstants.cs;$(MSBuildThisFileDirectory)GenerateInstallScripts.cs;$(MSBuildThisFileDirectory)GenerateGVFSInstallersNuspec.cs"
Inputs="$(RestoreTimestampPath);@(GeneratedPackageConfig);$(MSBuildThisFileFullPath);$(MSBuildProjectFullPath);$(MSBuildThisFileDirectory)GenerateGitVersionConstants.cs;$(MSBuildThisFileDirectory)GenerateInstallScripts.cs;$(MSBuildThisFileDirectory)GenerateGVFSInstallersNuspec.cs"
Outputs="$(GitVersionConstantsPath);$(G4WInstallBatPath);$(GVFSInstallBatPath);$(GVFSInstallerNuspecPath)"
Condition="'$(OS)' == 'Windows_NT'">
<GenerateGitVersionConstants GitPackageVersion="$(GitPackageVersion)" PackagesPath="$(PackagesDir)" OutputFile="$(GitVersionConstantsPath)">
@ -189,7 +188,7 @@
<Target Name="GenerateShared"
DependsOnTargets="GenerateInstallScripts;$(GenerateSharedDependsOn)"
Inputs="$(RestoreTimestampPath);$(GitPackagesConfigPath);$(MSBuildThisFileFullPath);$(MSBuildProjectFullPath);$(MSBuildThisFileDirectory)GenerateVersionInfo.cs;$(MSBuildThisFileDirectory)GenerateApplicationManifests.cs"
Inputs="$(RestoreTimestampPath);@(GeneratedPackageConfig);$(MSBuildThisFileFullPath);$(MSBuildProjectFullPath);$(MSBuildThisFileDirectory)GenerateVersionInfo.cs;$(MSBuildThisFileDirectory)GenerateApplicationManifests.cs"
Outputs="$(AssemblyVersionPath);$(VersionHeaderPath);@(ApplicationNeedsManifest->'%(OutputFile)')">
<GenerateVersionInfo Version="$(GVFSVersion)" AssemblyVersion="$(AssemblyVersionPath)" VersionHeader="$(VersionHeaderPath)" />
<GenerateApplicationManifests Version="$(GVFSVersion)" ApplicationName="@(ApplicationNeedsManifest)" ManifestPath="@(ApplicationNeedsManifest->'%(OutputFile)')" />

Просмотреть файл

@ -3,7 +3,7 @@
<PropertyGroup Label="Parameters">
<GVFSVersion>0.2.173.2</GVFSVersion>
<GitPackageVersion>2.6479675</GitPackageVersion>
<GitPackageVersion>2.20180730.3</GitPackageVersion>
</PropertyGroup>
<PropertyGroup Label="DefaultSettings">

Просмотреть файл

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjFSPackage>GVFS.ProjFS.2018.628.1</ProjFSPackage>
<ProjFSPackage>GVFS.ProjFS.2018.719.1</ProjFSPackage>
</PropertyGroup>
</Project>

Просмотреть файл

@ -14,22 +14,6 @@ namespace GVFS.Common
Failure,
}
private enum StdHandle
{
Stdin = -10,
Stdout = -11,
Stderr = -12
}
private enum FileType : uint
{
Unknown = 0x0000,
Disk = 0x0001,
Char = 0x0002,
Pipe = 0x0003,
Remote = 0x8000,
}
public static bool ShowStatusWhileRunning(
Func<bool> action,
string message,
@ -156,20 +140,9 @@ namespace GVFS.Common
return result;
}
public static bool IsConsoleOutputRedirectedToFile()
{
return FileType.Disk == GetFileType(GetStdHandle(StdHandle.Stdout));
}
public static string GetGVFSLogMessage(string enlistmentRoot)
{
return "Run 'gvfs log " + enlistmentRoot + "' for more info.";
}
[DllImport("kernel32.dll")]
private static extern IntPtr GetStdHandle(StdHandle std);
[DllImport("kernel32.dll")]
private static extern FileType GetFileType(IntPtr hdl);
}
}

Просмотреть файл

@ -0,0 +1,19 @@
using System;
namespace GVFS.Common
{
public static class EpochConverter
{
private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
public static long ToUnixEpochSeconds(DateTime datetime)
{
return Convert.ToInt64(Math.Truncate((datetime - UnixEpoch).TotalSeconds));
}
public static DateTime FromUnixEpochSeconds(long secondsSinceEpoch)
{
return UnixEpoch.AddSeconds(secondsSinceEpoch);
}
}
}

Просмотреть файл

@ -12,12 +12,6 @@ namespace GVFS.Common.FileSystem
{
public static class HooksInstaller
{
private const string HooksConfigContentTemplate =
@"########################################################################
# Automatically generated file, do not modify.
# See {0} config setting
########################################################################
{1}";
private static readonly string ExecutingDirectory;
static HooksInstaller()
@ -29,23 +23,23 @@ namespace GVFS.Common.FileSystem
{
IEnumerable<string> valuableHooksLines = defaultHooksLines.Where(line => !string.IsNullOrEmpty(line.Trim()));
if (valuableHooksLines.Contains(GVFSConstants.GVFSHooksExecutableName, StringComparer.OrdinalIgnoreCase))
if (valuableHooksLines.Contains(GVFSPlatform.Instance.Constants.GVFSHooksExecutableName, StringComparer.OrdinalIgnoreCase))
{
throw new HooksConfigurationException(
"GVFS.Hooks.exe should not be specified in the configuration for "
$"{GVFSPlatform.Instance.Constants.GVFSHooksExecutableName} should not be specified in the configuration for "
+ GVFSConstants.DotGit.Hooks.PostCommandHookName + " hooks (" + filename + ").");
}
else if (!valuableHooksLines.Any())
{
return GVFSConstants.GVFSHooksExecutableName;
return GVFSPlatform.Instance.Constants.GVFSHooksExecutableName;
}
else if (hookName.Equals(GVFSConstants.DotGit.Hooks.PostCommandHookName))
{
return string.Join("\n", new string[] { GVFSConstants.GVFSHooksExecutableName }.Concat(valuableHooksLines));
return string.Join("\n", new string[] { GVFSPlatform.Instance.Constants.GVFSHooksExecutableName }.Concat(valuableHooksLines));
}
else
{
return string.Join("\n", valuableHooksLines.Concat(new string[] { GVFSConstants.GVFSHooksExecutableName }));
return string.Join("\n", valuableHooksLines.Concat(new string[] { GVFSPlatform.Instance.Constants.GVFSHooksExecutableName }));
}
}
@ -54,30 +48,30 @@ namespace GVFS.Common.FileSystem
error = string.Empty;
try
{
string installedReadObjectHookPath = Path.Combine(ExecutingDirectory, GVFSConstants.GVFSReadObjectHookExecutableName);
string targetReadObjectHookPath = Path.Combine(context.Enlistment.WorkingDirectoryRoot, GVFSConstants.DotGit.Hooks.ReadObjectPath + GVFSConstants.ExecutableExtension);
if (!TryAction(() => CopyHook(context, installedReadObjectHookPath, targetReadObjectHookPath), out error))
string installedReadObjectHookPath = Path.Combine(ExecutingDirectory, GVFSPlatform.Instance.Constants.GVFSReadObjectHookExecutableName);
string targetReadObjectHookPath = Path.Combine(context.Enlistment.WorkingDirectoryRoot, GVFSConstants.DotGit.Hooks.ReadObjectPath + GVFSPlatform.Instance.Constants.ExecutableExtension);
if (!TryHooksInstallationAction(() => CopyHook(context, installedReadObjectHookPath, targetReadObjectHookPath), out error))
{
error = "Failed to copy " + installedReadObjectHookPath + "\n" + error;
return false;
}
string installedVirtualFileSystemHookPath = Path.Combine(ExecutingDirectory, GVFSConstants.GVFSVirtualFileSystemHookExecutableName);
string targetVirtualFileSystemHookPath = Path.Combine(context.Enlistment.WorkingDirectoryRoot, GVFSConstants.DotGit.Hooks.VirtualFileSystemPath + GVFSConstants.ExecutableExtension);
if (!TryAction(() => CopyHook(context, installedVirtualFileSystemHookPath, targetVirtualFileSystemHookPath), out error))
string installedVirtualFileSystemHookPath = Path.Combine(ExecutingDirectory, GVFSPlatform.Instance.Constants.GVFSVirtualFileSystemHookExecutableName);
string targetVirtualFileSystemHookPath = Path.Combine(context.Enlistment.WorkingDirectoryRoot, GVFSConstants.DotGit.Hooks.VirtualFileSystemPath + GVFSPlatform.Instance.Constants.ExecutableExtension);
if (!TryHooksInstallationAction(() => CopyHook(context, installedVirtualFileSystemHookPath, targetVirtualFileSystemHookPath), out error))
{
error = "Failed to copy " + installedVirtualFileSystemHookPath + "\n" + error;
return false;
}
string precommandHookPath = Path.Combine(context.Enlistment.WorkingDirectoryRoot, GVFSConstants.DotGit.Hooks.PreCommandPath);
if (!TryInstallGitCommandHooks(context, GVFSConstants.DotGit.Hooks.PreCommandHookName, precommandHookPath, out error))
if (!GVFSPlatform.Instance.TryInstallGitCommandHooks(context, ExecutingDirectory, GVFSConstants.DotGit.Hooks.PreCommandHookName, precommandHookPath, out error))
{
return false;
}
string postcommandHookPath = Path.Combine(context.Enlistment.WorkingDirectoryRoot, GVFSConstants.DotGit.Hooks.PostCommandPath);
if (!TryInstallGitCommandHooks(context, GVFSConstants.DotGit.Hooks.PostCommandHookName, postcommandHookPath, out error))
if (!GVFSPlatform.Instance.TryInstallGitCommandHooks(context, ExecutingDirectory, GVFSConstants.DotGit.Hooks.PostCommandHookName, postcommandHookPath, out error))
{
return false;
}
@ -97,7 +91,7 @@ namespace GVFS.Common.FileSystem
context,
GVFSConstants.DotGit.Hooks.ReadObjectName,
GVFSConstants.DotGit.Hooks.ReadObjectPath,
GVFSConstants.GVFSReadObjectHookExecutableName,
GVFSPlatform.Instance.Constants.GVFSReadObjectHookExecutableName,
out errorMessage))
{
return false;
@ -107,7 +101,7 @@ namespace GVFS.Common.FileSystem
context,
GVFSConstants.DotGit.Hooks.VirtualFileSystemName,
GVFSConstants.DotGit.Hooks.VirtualFileSystemPath,
GVFSConstants.GVFSVirtualFileSystemHookExecutableName,
GVFSPlatform.Instance.Constants.GVFSVirtualFileSystemHookExecutableName,
out errorMessage))
{
return false;
@ -116,7 +110,24 @@ namespace GVFS.Common.FileSystem
return true;
}
private static bool TryAction(Action action, out string errorMessage)
public static void CopyHook(GVFSContext context, string sourcePath, string destinationPath)
{
Exception ex;
if (!context.FileSystem.TryCopyToTempFileAndRename(sourcePath, destinationPath, out ex))
{
throw new RetryableException($"Error installing {sourcePath} to {destinationPath}", ex);
}
}
/// <summary>
/// Try to perform the specified action. The action will be retried (with backoff) up to 3 times.
/// </summary>
/// <param name="action">Action to perform</param>
/// <param name="errorMessage">Error message</param>
/// <returns>True if the action succeeded and false otherwise</returns>
/// <remarks>This method is optimized for the hooks installation process and should not be used
/// as a generic retry mechanism. See RetryWrapper for a general purpose retry mechanism</remarks>
public static bool TryHooksInstallationAction(Action action, out string errorMessage)
{
int retriesLeft = 3;
int retryWaitMillis = 500; // Will grow exponentially on each retry attempt
@ -157,7 +168,7 @@ namespace GVFS.Common.FileSystem
out string errorMessage)
{
bool copyHook = false;
string enlistmentHookPath = Path.Combine(context.Enlistment.WorkingDirectoryRoot, hookPath + GVFSConstants.ExecutableExtension);
string enlistmentHookPath = Path.Combine(context.Enlistment.WorkingDirectoryRoot, hookPath + GVFSPlatform.Instance.Constants.ExecutableExtension);
string installedHookPath = Path.Combine(ExecutingDirectory, hookExecutableName);
if (!context.FileSystem.FileExists(installedHookPath))
@ -221,74 +232,6 @@ namespace GVFS.Common.FileSystem
return true;
}
private static bool TryInstallGitCommandHooks(GVFSContext context, string hookName, string commandHookPath, out string errorMessage)
{
// The GitHooksLoader requires the following setup to invoke a hook:
// Copy GithooksLoader.exe to hook-name.exe
// Create a text file named hook-name.hooks that lists the applications to execute for the hook, one application per line
string gitHooksloaderPath = Path.Combine(ExecutingDirectory, GVFSConstants.DotGit.Hooks.LoaderExecutable);
if (!TryAction(() => CopyHook(context, gitHooksloaderPath, commandHookPath + GVFSConstants.ExecutableExtension), out errorMessage))
{
errorMessage = "Failed to copy " + GVFSConstants.DotGit.Hooks.LoaderExecutable + " to " + commandHookPath + GVFSConstants.ExecutableExtension + "\n" + errorMessage;
return false;
}
if (!TryAction(() => CreateHookCommandConfig(context, hookName, commandHookPath), out errorMessage))
{
errorMessage = "Failed to create " + commandHookPath + GVFSConstants.GitConfig.HooksExtension + "\n" + errorMessage;
return false;
}
return true;
}
private static void CopyHook(GVFSContext context, string sourcePath, string destinationPath)
{
Exception ex;
if (!context.FileSystem.TryCopyToTempFileAndRename(sourcePath, destinationPath, out ex))
{
throw new RetryableException($"Error installing {sourcePath} to {destinationPath}", ex);
}
}
private static void CreateHookCommandConfig(GVFSContext context, string hookName, string commandHookPath)
{
string targetPath = commandHookPath + GVFSConstants.GitConfig.HooksExtension;
try
{
string configSetting = GVFSConstants.GitConfig.HooksPrefix + hookName;
string mergedHooks = MergeHooks(context, configSetting, hookName);
string contents = string.Format(HooksConfigContentTemplate, configSetting, mergedHooks);
Exception ex;
if (!context.FileSystem.TryWriteTempFileAndRename(targetPath, contents, out ex))
{
throw new RetryableException("Error installing " + targetPath, ex);
}
}
catch (IOException io)
{
throw new RetryableException("Error installing " + targetPath, io);
}
}
private static string MergeHooks(GVFSContext context, string configSettingName, string hookName)
{
GitProcess configProcess = new GitProcess(context.Enlistment);
string filename;
string[] defaultHooksLines = { };
if (configProcess.TryGetFromConfig(configSettingName, forceOutsideEnlistment: true, value: out filename))
{
filename = filename.Trim(' ', '\n');
defaultHooksLines = File.ReadAllLines(filename);
}
return MergeHooksData(defaultHooksLines, filename, hookName);
}
public class HooksConfigurationException : Exception
{
public HooksConfigurationException(string message)

Просмотреть файл

@ -7,5 +7,6 @@
void MoveAndOverwriteFile(string sourceFileName, string destinationFilename);
void CreateHardLink(string newFileName, string existingFileName);
bool TryGetNormalizedPath(string path, out string normalizedPath, out string errorMessage);
void ChangeMode(string path, int mode);
}
}

Просмотреть файл

@ -4,7 +4,7 @@
<Import Project="..\GVFS.Build\GVFS.cs.props" />
<PropertyGroup>
<TargetFrameworks>netcoreapp2.0;netstandard2.0</TargetFrameworks>
<TargetFrameworks>netcoreapp2.1;netstandard2.0</TargetFrameworks>
<Platforms>x64</Platforms>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

Просмотреть файл

@ -18,12 +18,6 @@ namespace GVFS.Common
public const string WorkingDirectoryRootName = "src";
public const string UnattendedEnvironmentVariable = "GVFS_UNATTENDED";
public const string GVFSExecutableName = "GVFS.exe";
public const string GVFSHooksExecutableName = "GVFS.Hooks.exe";
public const string GVFSReadObjectHookExecutableName = "GVFS.ReadObjectHook.exe";
public const string GVFSVirtualFileSystemHookExecutableName = "GVFS.VirtualFileSystemHook.exe";
public const string MountExecutableName = "GVFS.Mount.exe";
public const string ExecutableExtension = ".exe";
public const string GitIsNotInstalledError = "Could not find git.exe. Ensure that Git is installed.";
public static class GitConfig

Просмотреть файл

@ -29,7 +29,7 @@ namespace GVFS.Common
gvfsHooksRoot,
flushFileBuffersForPacks: true)
{
this.NamedPipeName = Paths.GetNamedPipeName(this.EnlistmentRoot);
this.NamedPipeName = GVFSPlatform.Instance.GetNamedPipeName(this.EnlistmentRoot);
this.DotGVFSRoot = Path.Combine(this.EnlistmentRoot, GVFSConstants.DotGVFS.Root);
this.GVFSLogsRoot = Path.Combine(this.EnlistmentRoot, GVFSConstants.DotGVFS.LogPath);
this.LocalObjectsRoot = Path.Combine(this.WorkingDirectoryRoot, GVFSConstants.DotGit.Objects.Root);
@ -119,7 +119,7 @@ namespace GVFS.Common
public static bool WaitUntilMounted(string enlistmentRoot, bool unattended, out string errorMessage)
{
errorMessage = null;
using (NamedPipeClient pipeClient = new NamedPipeClient(Paths.GetNamedPipeName(enlistmentRoot)))
using (NamedPipeClient pipeClient = new NamedPipeClient(GVFSPlatform.Instance.GetNamedPipeName(enlistmentRoot)))
{
int timeout = unattended ? 300000 : 60000;
if (!pipeClient.Connect(timeout))

Просмотреть файл

@ -14,6 +14,7 @@ namespace GVFS.Common
string fullCommand,
int pid,
bool isElevated,
bool isConsoleOutputRedirectedToFile,
bool checkAvailabilityOnly,
string gvfsEnlistmentRoot,
out string result)
@ -90,7 +91,7 @@ namespace GVFS.Common
waitForLock,
message,
output: Console.Out,
showSpinner: !ConsoleHelper.IsConsoleOutputRedirectedToFile(),
showSpinner: !isConsoleOutputRedirectedToFile,
gvfsLogEnlistmentRoot: gvfsEnlistmentRoot);
}
@ -104,6 +105,7 @@ namespace GVFS.Common
string fullCommand,
int pid,
bool isElevated,
bool isConsoleOutputRedirectedToFile,
Action<NamedPipeMessages.ReleaseLock.Response> responseHandler,
string gvfsEnlistmentRoot,
string waitingMessage = "",
@ -124,7 +126,7 @@ namespace GVFS.Common
return ConsoleHelper.ActionResult.Success;
};
if (unattended || ConsoleHelper.IsConsoleOutputRedirectedToFile())
if (unattended || isConsoleOutputRedirectedToFile)
{
releaseLock();
}

Просмотреть файл

@ -396,7 +396,7 @@ namespace GVFS.Common
if (this.externalLockHolder != null)
{
int pid = this.externalLockHolder.PID;
externalHolderTerminatedWithoutReleasingLock = !ProcessHelper.IsProcessActive(pid);
externalHolderTerminatedWithoutReleasingLock = !GVFSPlatform.Instance.IsProcessActive(pid);
}
return this.externalLockHolder;

Просмотреть файл

@ -10,6 +10,11 @@ namespace GVFS.Common
{
public abstract class GVFSPlatform
{
public GVFSPlatform(string executableExtension)
{
this.Constants = new GVFSPlatformConstants(executableExtension);
}
public static GVFSPlatform Instance { get; private set; }
public abstract IKernelDriver KernelDriver { get; }
@ -18,6 +23,8 @@ namespace GVFS.Common
public abstract IPlatformFileSystem FileSystem { get; }
public virtual bool IsUnderConstruction { get; } = false;
public virtual bool SupportsGVFSService { get; } = true;
public GVFSPlatformConstants Constants { get; }
public static void Register(GVFSPlatform platform)
{
if (GVFSPlatform.Instance != null)
@ -29,13 +36,19 @@ namespace GVFS.Common
}
public abstract void StartBackgroundProcess(string programName, string[] args);
public abstract bool IsProcessActive(int processId);
public abstract string GetNamedPipeName(string enlistmentRoot);
public abstract NamedPipeServerStream CreatePipeByName(string pipeName);
public abstract string GetOSVersionInformation();
public abstract void InitializeEnlistmentACLs(string enlistmentPath);
public abstract bool IsElevated();
public abstract string GetCurrentUser();
public abstract void ConfigureVisualStudio(string gitBinPath, ITracer tracer);
public abstract bool TryGetGVFSHooksPathAndVersion(out string hooksPaths, out string hooksVersion, out string error);
public abstract bool TryGetGVFSHooksPathAndVersion(out string hooksPaths, out string hooksVersion, out string error);
public abstract bool TryInstallGitCommandHooks(GVFSContext context, string executingDirectory, string hookName, string commandHookPath, out string errorMessage);
public abstract InProcEventListener CreateTelemetryListenerIfEnabled(string providerName);
@ -58,5 +71,39 @@ namespace GVFS.Common
pathRoot = Path.GetPathRoot(normalizedPath);
return true;
}
public class GVFSPlatformConstants
{
public GVFSPlatformConstants(string executableExtension)
{
this.ExecutableExtension = executableExtension;
}
public string ExecutableExtension { get; }
public string GVFSExecutableName
{
get { return "GVFS" + this.ExecutableExtension; }
}
public string GVFSHooksExecutableName
{
get { return "GVFS.Hooks" + this.ExecutableExtension; }
}
public string GVFSReadObjectHookExecutableName
{
get { return "GVFS.ReadObjectHook" + this.ExecutableExtension; }
}
public string GVFSVirtualFileSystemHookExecutableName
{
get { return "GVFS.VirtualFileSystemHook" + this.ExecutableExtension; }
}
public string MountExecutableName
{
get { return "GVFS.Mount" + this.ExecutableExtension; }
}
}
}
}

Просмотреть файл

@ -70,7 +70,7 @@ namespace GVFS.Common.Git
return output.Succeeded && output.Result.Success;
}
public void DeleteStaleTempPrefetchPackAndIdxs()
public virtual void DeleteStaleTempPrefetchPackAndIdxs()
{
string[] staleTempPacks = this.ReadPackFileNames(Path.Combine(this.Enlistment.GitPackRoot, GitObjects.TempPackFolder), GVFSConstants.PrefetchPackPrefix);
foreach (string stalePackPath in staleTempPacks)
@ -92,7 +92,7 @@ namespace GVFS.Common.Git
}
}
public bool TryDownloadPrefetchPacks(long latestTimestamp, out List<string> packIndexes)
public virtual bool TryDownloadPrefetchPacks(long latestTimestamp, out List<string> packIndexes)
{
EventMetadata metadata = CreateEventMetadata();
metadata.Add("latestTimestamp", latestTimestamp);

Просмотреть файл

@ -13,7 +13,10 @@ namespace GVFS.Common.Git
{
public class GitProcess
{
private const int HResultEHANDLE = -2147024890; // 0x80070006 E_HANDLE
private static readonly Encoding UTF8NoBOM = new UTF8Encoding(false);
private static bool failedToSetEncoding = false;
private object executionLock = new object();
@ -28,7 +31,22 @@ namespace GVFS.Common.Git
// We need to use the BOM-less encoding because Git doesn't understand it
if (Console.InputEncoding.CodePage == UTF8NoBOM.CodePage)
{
Console.InputEncoding = UTF8NoBOM;
try
{
Console.InputEncoding = UTF8NoBOM;
}
catch (IOException ex) when (ex.HResult == HResultEHANDLE)
{
// If the standard input for a console is redirected / not available,
// then we might not be able to set the InputEncoding here.
// In practice, this can happen if we attempt to run a GitProcess from within a Service,
// such as GVFS.Service.
// Record that we failed to set the encoding, but do not quite the process.
// This means that git commands that use stdin will not work, but
// for our scenarios, we do not expect these calls at this this time.
// We will check and fail if we attempt to write to stdin in in a git call below.
GitProcess.failedToSetEncoding = true;
}
}
}
@ -417,6 +435,11 @@ namespace GVFS.Common.Git
Action<string> parseStdOutLine,
int timeoutMs)
{
if (failedToSetEncoding && writeStdIn != null)
{
return new Result(string.Empty, "Attempting to use to stdin, but the process does not have the right input encodings set.", Result.GenericFailureCode);
}
try
{
// From https://msdn.microsoft.com/en-us/library/system.diagnostics.process.standardoutput.aspx

Просмотреть файл

@ -68,7 +68,7 @@ namespace GVFS.Common.NamedPipes
try
{
this.writer.WriteLine(message);
this.writer.WritePlatformIndependentLine(message);
this.writer.Flush();
}
catch (IOException e)

Просмотреть файл

@ -216,7 +216,7 @@ namespace GVFS.Common.NamedPipes
{
try
{
this.writer.WriteLine(message);
this.writer.WritePlatformIndependentLine(message);
this.writer.Flush();
return true;

Просмотреть файл

@ -0,0 +1,16 @@
using System.IO;
namespace GVFS.Common.NamedPipes
{
public static class NamedPipeStreamWriterExtensions
{
public const int Foo = 0;
public static void WritePlatformIndependentLine(this StreamWriter writer, string value)
{
// WriteLine is not platform independent as on some platforms it terminates lines with \r\n
// and on others it uses \n
writer.Write(value + "\n");
}
}
}

Просмотреть файл

@ -11,11 +11,6 @@ namespace GVFS.Common
return GetRoot(directory, GVFSConstants.DotGit.Root);
}
public static string GetNamedPipeName(string enlistmentRoot)
{
return "GVFS_" + enlistmentRoot.ToUpper().Replace(':', '_').Replace('/', '_');
}
public static string GetServiceDataRoot(string serviceName)
{
return Path.Combine(
@ -68,5 +63,10 @@ namespace GVFS.Common
return null;
}
public static string ConvertPathToGitFormat(string path)
{
return path.Replace(Path.DirectorySeparatorChar, GVFSConstants.GitPathSeparator);
}
}
}

Просмотреть файл

@ -0,0 +1,126 @@
using GVFS.Common.FileSystem;
using GVFS.Common.Git;
using GVFS.Common.Tracing;
using System;
using System.IO;
using System.Threading;
namespace GVFS.Common.Prefetch
{
public class BackgroundPrefetcher : IDisposable
{
private const string TelemetryKey = nameof(BackgroundPrefetcher);
private readonly TimeSpan timerPeriod = TimeSpan.FromMinutes(15);
private readonly TimeSpan timeBetweenPrefetches = TimeSpan.FromMinutes(70);
private ITracer tracer;
private GVFSEnlistment enlistment;
private PhysicalFileSystem fileSystem;
private GitObjects gitObjects;
private Timer prefetchJobTimer;
private Thread prefetchJobThread;
public BackgroundPrefetcher(ITracer tracer, GVFSEnlistment enlistment, PhysicalFileSystem fileSystem, GitObjects gitObjects)
{
this.tracer = tracer;
this.enlistment = enlistment;
this.fileSystem = fileSystem;
this.gitObjects = gitObjects;
this.prefetchJobThread = null;
this.prefetchJobTimer = new Timer((state) => this.LaunchPrefetchJobIfIdle(), null, this.timerPeriod, this.timerPeriod);
}
public void Dispose()
{
this.prefetchJobTimer?.Dispose();
this.prefetchJobTimer = null;
}
public bool LaunchPrefetchJobIfIdle()
{
if (this.prefetchJobThread?.IsAlive == true)
{
this.tracer.RelatedInfo(nameof(BackgroundPrefetcher) + ": background thread not idle, skipping timed start");
}
else
{
this.prefetchJobThread = new Thread(() => this.BackgroundPrefetch());
this.prefetchJobThread.IsBackground = true;
this.prefetchJobThread.Start();
return true;
}
return false;
}
/// <summary>
/// This method is used for test purposes only.
/// </summary>
public void WaitForPrefetchToFinish()
{
this.prefetchJobThread?.Join();
}
private void BackgroundPrefetch()
{
try
{
using (ITracer activity = this.tracer.StartActivity(nameof(this.BackgroundPrefetch), EventLevel.Informational))
{
long last;
string error;
if (!CommitPrefetcher.TryGetMaxGoodPrefetchTimestamp(activity, this.enlistment, this.fileSystem, this.gitObjects, out last, out error))
{
activity.RelatedError(error);
return;
}
DateTime lastDateTime = EpochConverter.FromUnixEpochSeconds(last);
DateTime now = DateTime.UtcNow;
if (now <= lastDateTime + this.timeBetweenPrefetches)
{
activity.RelatedInfo(TelemetryKey + ": Skipping prefetch since most-recent prefetch ({0}) is too close to now ({1})", lastDateTime, now);
return;
}
if (!CommitPrefetcher.TryPrefetchCommitsAndTrees(activity, this.enlistment, this.fileSystem, this.gitObjects, out error))
{
activity.RelatedError($"{TelemetryKey}: {nameof(CommitPrefetcher.TryPrefetchCommitsAndTrees)} failed with error '{error}'");
}
}
}
catch (ThreadAbortException)
{
this.tracer.RelatedInfo(TelemetryKey + ": Aborting prefetch background thread due to ThreadAbortException");
return;
}
catch (IOException e)
{
EventMetadata metadata = new EventMetadata();
metadata.Add("Method", nameof(this.BackgroundPrefetch));
metadata.Add("ExceptionMessage", e.Message);
metadata.Add("StackTrace", e.StackTrace);
this.tracer.RelatedWarning(
metadata: metadata,
message: TelemetryKey + ": IOException while running prefetch background thread (non-fatal): " + e.Message,
keywords: Keywords.Telemetry);
}
catch (Exception e)
{
EventMetadata metadata = new EventMetadata();
metadata.Add("Method", nameof(this.BackgroundPrefetch));
metadata.Add("ExceptionMessage", e.Message);
metadata.Add("StackTrace", e.StackTrace);
this.tracer.RelatedError(
metadata: metadata,
message: TelemetryKey + ": Unexpected Exception while running prefetch background thread (fatal): " + e.Message,
keywords: Keywords.Telemetry);
Environment.Exit((int)ReturnCode.GenericError);
}
}
}
}

Просмотреть файл

@ -13,7 +13,7 @@ using System.Threading;
namespace GVFS.Common.Prefetch
{
public class PrefetchHelper
public class BlobPrefetcher
{
protected const string RefsHeadsGitPath = "refs/heads/";
@ -29,10 +29,10 @@ namespace GVFS.Common.Prefetch
protected readonly bool SkipConfigUpdate;
private const string AreaPath = nameof(PrefetchHelper);
private const string AreaPath = nameof(BlobPrefetcher);
private static string pathSeparatorString = Path.DirectorySeparatorChar.ToString();
public PrefetchHelper(
public BlobPrefetcher(
ITracer tracer,
Enlistment enlistment,
GitObjectsHttpRequestor objectRequestor,
@ -67,7 +67,7 @@ namespace GVFS.Common.Prefetch
{
folderListOutput.AddRange(
foldersInput.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)
.Select(path => PrefetchHelper.ToAbsolutePath(enlistment, path, isFolder: true)));
.Select(path => BlobPrefetcher.ToAbsolutePath(enlistment, path, isFolder: true)));
if (!string.IsNullOrWhiteSpace(folderListFile))
{
@ -77,7 +77,7 @@ namespace GVFS.Common.Prefetch
.Select(line => line.Trim())
.Where(line => !string.IsNullOrEmpty(line))
.Where(line => !line.StartsWith(GVFSConstants.GitCommentSign.ToString()))
.Select(path => PrefetchHelper.ToAbsolutePath(enlistment, path, isFolder: true));
.Select(path => BlobPrefetcher.ToAbsolutePath(enlistment, path, isFolder: true));
folderListOutput.AddRange(allLines);
}
@ -107,7 +107,7 @@ namespace GVFS.Common.Prefetch
{
fileListOutput.AddRange(
filesInput.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)
.Select(path => PrefetchHelper.ToAbsolutePath(enlistment, path, isFolder: false)));
.Select(path => BlobPrefetcher.ToAbsolutePath(enlistment, path, isFolder: false)));
foreach (string file in fileListOutput)
{

Просмотреть файл

@ -0,0 +1,235 @@
using GVFS.Common.FileSystem;
using GVFS.Common.Git;
using GVFS.Common.NamedPipes;
using GVFS.Common.Tracing;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
namespace GVFS.Common.Prefetch
{
public static class CommitPrefetcher
{
private const int IoFailureRetryDelayMS = 50;
private const int LockWaitTimeMs = 100;
private const int WaitingOnLockLogThreshold = 50;
private const string PrefetchCommitsAndTreesLock = "prefetch-commits-trees.lock";
public static bool TryPrefetchCommitsAndTrees(
ITracer tracer,
GVFSEnlistment enlistment,
PhysicalFileSystem fileSystem,
GitObjects gitObjects,
out string error)
{
List<string> packIndexes;
using (FileBasedLock prefetchLock = new FileBasedLock(
fileSystem,
tracer,
Path.Combine(enlistment.GitPackRoot, PrefetchCommitsAndTreesLock),
enlistment.EnlistmentRoot,
overwriteExistingLock: true))
{
WaitUntilLockIsAcquired(tracer, prefetchLock);
long maxGoodTimeStamp;
gitObjects.DeleteStaleTempPrefetchPackAndIdxs();
if (!TryGetMaxGoodPrefetchTimestamp(tracer, enlistment, fileSystem, gitObjects, out maxGoodTimeStamp, out error))
{
return false;
}
if (!gitObjects.TryDownloadPrefetchPacks(maxGoodTimeStamp, out packIndexes))
{
error = "Failed to download prefetch packs";
return false;
}
}
if (packIndexes == null || packIndexes.Count == 0)
{
return true;
}
// We make a best-effort request to run MIDX and commit-graph writes
using (NamedPipeClient pipeClient = new NamedPipeClient(enlistment.NamedPipeName))
{
if (!pipeClient.Connect())
{
tracer.RelatedWarning(
metadata: null,
message: "Failed to connect to GVFS. Skipping post-fetch job request.",
keywords: Keywords.Telemetry);
return true;
}
NamedPipeMessages.RunPostFetchJob.Request request = new NamedPipeMessages.RunPostFetchJob.Request(packIndexes);
if (pipeClient.TrySendRequest(request.CreateMessage()))
{
NamedPipeMessages.Message response;
if (pipeClient.TryReadResponse(out response))
{
tracer.RelatedInfo("Requested post-fetch job with resonse '{0}'", response.Header);
return true;
}
else
{
tracer.RelatedWarning(
metadata: null,
message: "Requested post-fetch job failed to respond",
keywords: Keywords.Telemetry);
}
}
else
{
tracer.RelatedWarning(
metadata: null,
message: "Message to named pipe failed to send, skipping post-fetch job request.",
keywords: Keywords.Telemetry);
}
}
return false;
}
public static bool TryGetMaxGoodPrefetchTimestamp(
ITracer tracer,
GVFSEnlistment enlistment,
PhysicalFileSystem fileSystem,
GitObjects gitObjects,
out long maxGoodTimestamp,
out string error)
{
fileSystem.CreateDirectory(enlistment.GitPackRoot);
string[] packs = gitObjects.ReadPackFileNames(enlistment.GitPackRoot, GVFSConstants.PrefetchPackPrefix);
List<PrefetchPackInfo> orderedPacks = packs
.Where(pack => GetTimestamp(pack).HasValue)
.Select(pack => new PrefetchPackInfo(GetTimestamp(pack).Value, pack))
.OrderBy(packInfo => packInfo.Timestamp)
.ToList();
maxGoodTimestamp = -1;
int firstBadPack = -1;
for (int i = 0; i < orderedPacks.Count; ++i)
{
long timestamp = orderedPacks[i].Timestamp;
string packPath = orderedPacks[i].Path;
string idxPath = Path.ChangeExtension(packPath, ".idx");
if (!fileSystem.FileExists(idxPath))
{
EventMetadata metadata = new EventMetadata();
metadata.Add("pack", packPath);
metadata.Add("idxPath", idxPath);
metadata.Add("timestamp", timestamp);
GitProcess.Result indexResult = gitObjects.IndexPackFile(packPath);
if (indexResult.HasErrors)
{
firstBadPack = i;
metadata.Add("Errors", indexResult.Errors);
tracer.RelatedWarning(metadata, $"{nameof(TryGetMaxGoodPrefetchTimestamp)}: Found pack file that's missing idx file, and failed to regenerate idx");
break;
}
else
{
maxGoodTimestamp = timestamp;
metadata.Add(TracingConstants.MessageKey.InfoMessage, $"{nameof(TryGetMaxGoodPrefetchTimestamp)}: Found pack file that's missing idx file, and regenerated idx");
tracer.RelatedEvent(EventLevel.Informational, $"{nameof(TryGetMaxGoodPrefetchTimestamp)}_RebuildIdx", metadata);
}
}
else
{
maxGoodTimestamp = timestamp;
}
}
if (firstBadPack != -1)
{
const int MaxDeleteRetries = 200; // 200 * IoFailureRetryDelayMS (50ms) = 10 seconds
const int RetryLoggingThreshold = 40; // 40 * IoFailureRetryDelayMS (50ms) = 2 seconds
// Delete packs and indexes in reverse order so that if prefetch is killed, subseqeuent prefetch commands will
// find the right starting spot.
for (int i = orderedPacks.Count - 1; i >= firstBadPack; --i)
{
string packPath = orderedPacks[i].Path;
string idxPath = Path.ChangeExtension(packPath, ".idx");
EventMetadata metadata = new EventMetadata();
metadata.Add("path", idxPath);
metadata.Add(TracingConstants.MessageKey.InfoMessage, $"{nameof(TryGetMaxGoodPrefetchTimestamp)} deleting bad idx file");
tracer.RelatedEvent(EventLevel.Informational, $"{nameof(TryGetMaxGoodPrefetchTimestamp)}_DeleteBadIdx", metadata);
if (!fileSystem.TryWaitForDelete(tracer, idxPath, IoFailureRetryDelayMS, MaxDeleteRetries, RetryLoggingThreshold))
{
error = $"Unable to delete {idxPath}";
return false;
}
metadata = new EventMetadata();
metadata.Add("path", packPath);
metadata.Add(TracingConstants.MessageKey.InfoMessage, $"{nameof(TryGetMaxGoodPrefetchTimestamp)} deleting bad pack file");
tracer.RelatedEvent(EventLevel.Informational, $"{nameof(TryGetMaxGoodPrefetchTimestamp)}_DeleteBadPack", metadata);
if (!fileSystem.TryWaitForDelete(tracer, packPath, IoFailureRetryDelayMS, MaxDeleteRetries, RetryLoggingThreshold))
{
error = $"Unable to delete {packPath}";
return false;
}
}
}
error = null;
return true;
}
private static long? GetTimestamp(string packName)
{
string filename = Path.GetFileName(packName);
if (!filename.StartsWith(GVFSConstants.PrefetchPackPrefix))
{
return null;
}
string[] parts = filename.Split('-');
long parsed;
if (parts.Length > 1 && long.TryParse(parts[1], out parsed))
{
return parsed;
}
return null;
}
private static void WaitUntilLockIsAcquired(ITracer tracer, FileBasedLock fileBasedLock)
{
int attempt = 0;
while (!fileBasedLock.TryAcquireLockAndDeleteOnClose())
{
Thread.Sleep(LockWaitTimeMs);
++attempt;
if (attempt == WaitingOnLockLogThreshold)
{
attempt = 0;
tracer.RelatedInfo("WaitUntilLockIsAcquired: Waiting to acquire prefetch lock");
}
}
}
private class PrefetchPackInfo
{
public PrefetchPackInfo(long timestamp, string path)
{
this.Timestamp = timestamp;
this.Path = path;
}
public long Timestamp { get; }
public string Path { get; }
}
}
}

Просмотреть файл

@ -9,25 +9,6 @@ namespace GVFS.Common
{
public static class ProcessHelper
{
private const int StillActive = 259; /* from Win32 STILL_ACTIVE */
public static bool IsProcessActive(int processId)
{
using (SafeFileHandle process = NativeMethods.OpenProcess(NativeMethods.ProcessAccessFlags.QueryLimitedInformation, false, processId))
{
if (!process.IsInvalid)
{
uint exitCode;
if (NativeMethods.GetExitCodeProcess(process, out exitCode) && exitCode == StillActive)
{
return true;
}
}
return false;
}
}
public static ProcessResult Run(string programName, string args, bool redirectOutput = true)
{
ProcessStartInfo processInfo = new ProcessStartInfo(programName);

Просмотреть файл

@ -33,7 +33,7 @@ namespace GVFS.FunctionalTests.LockHolder
throw new Exception("Unable to get GVFS Enlistment root: " + errorMessage);
}
string enlistmentPipename = Paths.GetNamedPipeName(enlistmentRoot);
string enlistmentPipename = GVFSPlatform.Instance.GetNamedPipeName(enlistmentRoot);
AcquireLock(enlistmentPipename);
@ -63,6 +63,7 @@ namespace GVFS.FunctionalTests.LockHolder
fullCommand: AcquireGVFSLockVerb.fullCommand,
pid: pid,
isElevated: false,
isConsoleOutputRedirectedToFile: false,
checkAvailabilityOnly: false,
gvfsEnlistmentRoot: null,
result: out result))

Просмотреть файл

@ -57,6 +57,9 @@
<Compile Include="AcquireGVFSLock.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="..\GVFS.PlatformLoader\PlatformLoader.Windows.cs">
<Link>PlatformLoader.Windows.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
@ -71,6 +74,10 @@
<Project>{4CE404E7-D3FC-471C-993C-64615861EA63}</Project>
<Name>GVFS.Platform.Windows</Name>
</ProjectReference>
<ProjectReference Include="..\GVFS.Virtualization\GVFS.Virtualization.csproj">
<Project>{f468b05a-95e5-46bc-8c67-b80a78527b7d}</Project>
<Name>GVFS.Virtualization</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\..\..\packages\StyleCop.MSBuild.5.0.0\build\StyleCop.MSBuild.targets" Condition="Exists('..\..\..\packages\StyleCop.MSBuild.5.0.0\build\StyleCop.MSBuild.targets')" />

Просмотреть файл

@ -1,4 +1,5 @@
using CommandLine;
using GVFS.PlatformLoader;
namespace GVFS.FunctionalTests.LockHolder
{
@ -6,6 +7,8 @@ namespace GVFS.FunctionalTests.LockHolder
{
public static void Main(string[] args)
{
GVFSPlatformLoader.Initialize();
Parser.Default.ParseArguments<AcquireGVFSLockVerb>(args)
.WithParsed(acquireGVFSLock => acquireGVFSLock.Execute());
}

Просмотреть файл

@ -188,7 +188,7 @@
xcopy /Y $(BuildOutputDir)\GVFS.Mount.Windows\bin\$(Platform)\$(Configuration)\* $(TargetDir)
xcopy /Y $(BuildOutputDir)\FastFetch\bin\$(Platform)\$(Configuration)\* $(TargetDir)
xcopy /Y $(BuildOutputDir)\GVFS.NativeTests\bin\$(Platform)\$(Configuration)\* $(TargetDir)
xcopy /Y $(BuildOutputDir)\GVFS.Hooks\bin\$(Platform)\$(Configuration)\* $(TargetDir)
xcopy /Y $(BuildOutputDir)\GVFS.Hooks.Windows\bin\$(Platform)\$(Configuration)\* $(TargetDir)
xcopy /Y $(BuildOutputDir)\GVFS.FunctionalTests.LockHolder\bin\$(Platform)\$(Configuration)\* $(TargetDir)
REM If there is an inbox version of the ProjFS dll, we must use that one to guarantee compat with the sys, so delete whatever is in our TargetDir

Просмотреть файл

@ -264,6 +264,7 @@ namespace GVFS.FunctionalTests.Windows.Tests
}
}
[TestCase]
public void MountCreatesModifiedPathsDatabase()
{
this.Enlistment.UnmountGVFS();

Просмотреть файл

@ -2,7 +2,9 @@
using GVFS.Tests.Should;
using NUnit.Framework;
using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
namespace GVFS.FunctionalTests.FileSystemRunners
{
@ -53,6 +55,31 @@ namespace GVFS.FunctionalTests.FileSystemRunners
}
}
public static void DeleteDirectoryWithUnlimitedRetries(string path)
{
BashRunner runner = new BashRunner();
bool pathExists = Directory.Exists(path);
int retryCount = 0;
while (pathExists)
{
string output = runner.DeleteDirectory(path);
pathExists = Directory.Exists(path);
if (pathExists)
{
++retryCount;
Thread.Sleep(500);
if (retryCount > 10)
{
retryCount = 0;
if (Debugger.IsAttached)
{
Debugger.Break();
}
}
}
}
}
public override bool FileExists(string path)
{
string bashPath = this.ConvertWinPathToBashPath(path);

Просмотреть файл

@ -35,7 +35,7 @@ namespace GVFS.FunctionalTests.FileSystemRunners
}
}
public static void DeleteDirectoryWithRetry(string path)
public static void DeleteDirectoryWithUnlimitedRetries(string path)
{
CmdRunner runner = new CmdRunner();
bool pathExists = Directory.Exists(path);

Просмотреть файл

@ -2,7 +2,7 @@
<Import Project="..\GVFS.Build\GVFS.cs.props" />
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<TargetFramework>netcoreapp2.1</TargetFramework>
<Platforms>x64</Platforms>
</PropertyGroup>
@ -37,7 +37,7 @@
<WindowsBuildOutputs Include="$(BuildOutputDir)\GVFS.Mount.Windows\bin\$(Platform)\$(Configuration)\**\*.*" />
<WindowsBuildOutputs Include="$(BuildOutputDir)\FastFetch\bin\$(Platform)\$(Configuration)\**\*.*" />
<WindowsBuildOutputs Include="$(BuildOutputDir)\GVFS.NativeTests\bin\$(Platform)\$(Configuration)\**\*.*" />
<WindowsBuildOutputs Include="$(BuildOutputDir)\GVFS.Hooks\bin\$(Platform)\$(Configuration)\**\*.*" />
<WindowsBuildOutputs Include="$(BuildOutputDir)\GVFS.Hooks.Windows\bin\$(Platform)\$(Configuration)\**\*.*" />
<WindowsBuildOutputs Include="$(BuildOutputDir)\GVFS.FunctionalTests.LockHolder\bin\$(Platform)\$(Configuration)\**\*.*" />
</ItemGroup>

Просмотреть файл

@ -2,6 +2,7 @@
using GVFS.FunctionalTests.Tools;
using GVFS.Tests;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
@ -35,6 +36,9 @@ namespace GVFS.FunctionalTests
GVFSTestConfig.LocalCacheRoot = runner.GetCustomArgWithParam("--shared-gvfs-cache-root");
List<string> includeCategories = new List<string>();
List<string> excludeCategories = new List<string>();
if (runner.HasCustomArg("--full-suite"))
{
Console.WriteLine("Running the full suite of tests");
@ -50,22 +54,22 @@ namespace GVFS.FunctionalTests
}
else
{
runner.ExcludeCategory(Categories.FullSuiteOnly);
excludeCategories.Add(Categories.FullSuiteOnly);
GVFSTestConfig.FileSystemRunners = FileSystemRunners.FileSystemRunner.DefaultRunners;
}
if (runner.HasCustomArg("--windows-only"))
{
runner.IncludeCategory(Categories.Windows);
includeCategories.Add(Categories.Windows);
}
if (runner.HasCustomArg("--mac-only"))
{
runner.IncludeCategory(Categories.Mac.M1);
runner.ExcludeCategory(Categories.Mac.M2);
runner.ExcludeCategory(Categories.Mac.M3);
runner.ExcludeCategory(Categories.Mac.M4);
runner.ExcludeCategory(Categories.Windows);
includeCategories.Add(Categories.Mac.M1);
includeCategories.Add(Categories.Mac.M2);
excludeCategories.Add(Categories.Mac.M3);
excludeCategories.Add(Categories.Mac.M4);
excludeCategories.Add(Categories.Windows);
}
GVFSTestConfig.RepoToClone =
@ -73,7 +77,7 @@ namespace GVFS.FunctionalTests
?? Properties.Settings.Default.RepoToClone;
RunBeforeAnyTests();
Environment.ExitCode = runner.RunTests();
Environment.ExitCode = runner.RunTests(includeCategories, excludeCategories);
RunAfterAllTests();
if (Debugger.IsAttached)

Просмотреть файл

@ -20,10 +20,11 @@ namespace GVFS.FunctionalTests.Properties
public static string FastFetchControl { get; set; }
public static string PathToGit { get; set; }
public static string PathToGVFSService { get; set; }
public static string BinaryFileNameExtension { get; set; }
public static void Initialize()
{
CurrentDirectory = Path.GetFullPath(Path.GetDirectoryName(Environment.CommandLine.Trim('"', ' ')));
CurrentDirectory = Path.GetFullPath(Path.GetDirectoryName(Environment.GetCommandLineArgs()[0]));
RepoToClone = @"https://gvfs.visualstudio.com/ci/_git/ForTests";
Commitish = @"FunctionalTests/20180214";
@ -39,14 +40,17 @@ namespace GVFS.FunctionalTests.Properties
FastFetchRoot = @"C:\Repos\GVFSFunctionalTests\FastFetch\Test";
FastFetchControl = @"C:\Repos\GVFSFunctionalTests\FastFetch\Control";
PathToGVFSService = @"GVFS.Service.exe";
BinaryFileNameExtension = ".exe";
}
else
{
string root = "/GVFS.FT";
EnlistmentRoot = Path.Combine(root, "test");
ControlGitRepoRoot = Path.Combine(root, "control");
PathToGVFS = "gvfs";
PathToGit = "/usr/local/bin/git";
PathToBash = "/bin/bash";
BinaryFileNameExtension = string.Empty;
}
}
}

Просмотреть файл

@ -6,6 +6,7 @@ namespace GVFS.FunctionalTests.Tests.EnlistmentPerFixture
{
[TestFixture]
[Category(Categories.FullSuiteOnly)]
[Category(Categories.Mac.M2)]
public class CacheServerTests : TestsWithEnlistmentPerFixture
{
private const string CustomUrl = "https://myCache";

Просмотреть файл

@ -48,7 +48,7 @@ namespace GVFS.FunctionalTests.Tests.EnlistmentPerFixture
public void DehydrateShouldSucceedEvenIfObjectCacheIsDeleted()
{
this.Enlistment.UnmountGVFS();
CmdRunner.DeleteDirectoryWithRetry(this.Enlistment.GetObjectRoot(this.fileSystem));
CmdRunner.DeleteDirectoryWithUnlimitedRetries(this.Enlistment.GetObjectRoot(this.fileSystem));
this.DehydrateShouldSucceed("The repo was successfully dehydrated and remounted", confirm: true, noStatus: true);
}

Просмотреть файл

@ -22,6 +22,7 @@ namespace GVFS.FunctionalTests.Tests.EnlistmentPerFixture
}
[TestCase, Order(1)]
[Category(Categories.Mac.M2)]
public void GitStatus()
{
GitHelpers.CheckGitCommandAgainstGVFSRepo(
@ -32,12 +33,14 @@ namespace GVFS.FunctionalTests.Tests.EnlistmentPerFixture
}
[TestCase, Order(2)]
[Category(Categories.Mac.M2)]
public void GitLog()
{
GitHelpers.CheckGitCommandAgainstGVFSRepo(this.Enlistment.RepoRoot, "log -n1", "commit", "Author:", "Date:");
}
[TestCase, Order(3)]
[Category(Categories.Mac.M2)]
public void GitBranch()
{
GitHelpers.CheckGitCommandAgainstGVFSRepo(

Просмотреть файл

@ -1,4 +1,5 @@
using GVFS.FunctionalTests.FileSystemRunners;
using GVFS.FunctionalTests.Properties;
using GVFS.FunctionalTests.Should;
using GVFS.FunctionalTests.Tools;
using GVFS.Tests.Should;
@ -45,7 +46,7 @@ namespace GVFS.FunctionalTests.Tests.EnlistmentPerFixture
{
this.Enlistment.UnmountGVFS();
string readObjectPath = this.Enlistment.GetVirtualPathTo(".git", "hooks", "read-object.exe");
string readObjectPath = this.Enlistment.GetVirtualPathTo(".git", "hooks", "read-object" + Settings.Default.BinaryFileNameExtension);
readObjectPath.ShouldBeAFile(this.fileSystem);
this.fileSystem.DeleteFile(readObjectPath);
readObjectPath.ShouldNotExistOnDisk(this.fileSystem);

Просмотреть файл

@ -14,6 +14,7 @@ using System.Text;
namespace GVFS.FunctionalTests.Tests.EnlistmentPerFixture
{
[TestFixtureSource(typeof(FileSystemRunner), FileSystemRunner.TestRunners)]
[Category(Categories.Mac.M2)]
public class WorkingDirectoryTests : TestsWithEnlistmentPerFixture
{
private const int CurrentPlaceholderVersion = 1;
@ -53,10 +54,13 @@ BOOL APIENTRY DllMain( HMODULE hModule,
[TestCase, Order(1)]
public void ProjectedFileHasExpectedContents()
{
this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests\\ProjectedFileHasExpectedContents.cpp").ShouldBeAFile(this.fileSystem).WithContents(TestFileContents);
this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests", "ProjectedFileHasExpectedContents.cpp")
.ShouldBeAFile(this.fileSystem)
.WithContents(TestFileContents);
}
[TestCase, Order(2)]
[Category(Categories.Mac.M3)]
public void StreamAccessReadWriteMemoryMappedProjectedFile()
{
string filename = @"Test_EPF_WorkingDirectoryTests\StreamAccessReadWriteMemoryMappedProjectedFile.cs";
@ -120,6 +124,7 @@ BOOL APIENTRY DllMain( HMODULE hModule,
}
[TestCase, Order(3)]
[Category(Categories.Mac.M3)]
public void RandomAccessReadWriteMemoryMappedProjectedFile()
{
string filename = @"Test_EPF_WorkingDirectoryTests\RandomAccessReadWriteMemoryMappedProjectedFile.cs";
@ -180,6 +185,7 @@ BOOL APIENTRY DllMain( HMODULE hModule,
}
[TestCase, Order(4)]
[Category(Categories.Mac.M3)]
public void StreamAndRandomAccessReadWriteMemoryMappedProjectedFile()
{
string filename = @"Test_EPF_WorkingDirectoryTests\StreamAndRandomAccessReadWriteMemoryMappedProjectedFile.cs";
@ -274,6 +280,7 @@ BOOL APIENTRY DllMain( HMODULE hModule,
}
[TestCase, Order(5)]
[Category(Categories.Mac.M3)]
public void MoveProjectedFileToInvalidFolder()
{
string targetFolderName = "test_folder";
@ -299,7 +306,7 @@ BOOL APIENTRY DllMain( HMODULE hModule,
public void EnumerateAndReadDoesNotChangeEnumerationOrder()
{
string folderVirtualPath = this.Enlistment.GetVirtualPathTo("EnumerateAndReadTestFiles");
NativeTests.EnumerateAndReadDoesNotChangeEnumerationOrder(folderVirtualPath).ShouldEqual(true);
this.EnumerateAndReadShouldNotChangeEnumerationOrder(folderVirtualPath);
folderVirtualPath.ShouldBeADirectory(this.fileSystem);
folderVirtualPath.ShouldBeADirectory(this.fileSystem).WithItems();
}
@ -322,7 +329,7 @@ BOOL APIENTRY DllMain( HMODULE hModule,
[TestCase, Order(8)]
public void HydratingNestedFileUsesNameCaseFromRepo()
{
string filePath = "GVFS\\FastFetch\\Properties\\AssemblyInfo.cs";
string filePath = Path.Combine("GVFS", "FastFetch", "Properties", "AssemblyInfo.cs");
string filePathAllCaps = filePath.ToUpper();
string parentFolderVirtualPathAllCaps = this.Enlistment.GetVirtualPathTo(Path.GetDirectoryName(filePathAllCaps));
parentFolderVirtualPathAllCaps.ShouldBeADirectory(this.fileSystem).WithItems().ShouldContainSingle(info => info.Name.Equals(Path.GetFileName(filePath), StringComparison.Ordinal));
@ -346,6 +353,7 @@ BOOL APIENTRY DllMain( HMODULE hModule,
}
[TestCase, Order(9)]
[Category(Categories.Mac.M3)]
public void WriteToHydratedFileAfterRemount()
{
string fileName = "Test_EPF_WorkingDirectoryTests\\WriteToHydratedFileAfterRemount.cpp";
@ -363,8 +371,8 @@ BOOL APIENTRY DllMain( HMODULE hModule,
[TestCase, Order(10)]
public void ReadDeepProjectedFile()
{
string testFilePath = "Test_EPF_WorkingDirectoryTests\\1\\2\\3\\4\\ReadDeepProjectedFile.cpp";
{
string testFilePath = Path.Combine("Test_EPF_WorkingDirectoryTests", "1", "2", "3", "4", "ReadDeepProjectedFile.cpp");
this.Enlistment.GetVirtualPathTo(testFilePath).ShouldBeAFile(this.fileSystem).WithContents(TestFileContents);
}
@ -372,28 +380,29 @@ BOOL APIENTRY DllMain( HMODULE hModule,
public void FilePlaceHolderHasVersionInfo()
{
string sha = "BB1C8B9ADA90D6B8F6C88F12C6DDB07C186155BD";
string virtualFilePath = this.Enlistment.GetVirtualPathTo("GVFlt_BugRegressionTest\\GVFlt_ModifyFileInScratchAndDir\\ModifyFileInScratchAndDir.txt");
string virtualFilePath = this.Enlistment.GetVirtualPathTo("GVFlt_BugRegressionTest", "GVFlt_ModifyFileInScratchAndDir", "ModifyFileInScratchAndDir.txt");
virtualFilePath.ShouldBeAFile(this.fileSystem).WithContents();
ProcessResult revParseHeadResult = GitProcess.InvokeProcess(this.Enlistment.RepoRoot, "rev-parse HEAD");
string commitID = revParseHeadResult.Output.Trim();
NativeTests.PlaceHolderHasVersionInfo(virtualFilePath, CurrentPlaceholderVersion, sha).ShouldEqual(true);
this.PlaceholderHasVersionInfo(virtualFilePath, CurrentPlaceholderVersion, sha).ShouldEqual(true);
}
[TestCase, Order(12), Ignore("Results in an access violation in the functional test on the build server")]
public void FolderPlaceHolderHasVersionInfo()
{
string virtualFilePath = this.Enlistment.GetVirtualPathTo("GVFlt_BugRegressionTest\\GVFlt_ModifyFileInScratchAndDir");
string virtualFilePath = this.Enlistment.GetVirtualPathTo("GVFlt_BugRegressionTest", "GVFlt_ModifyFileInScratchAndDir");
ProcessResult revParseHeadResult = GitProcess.InvokeProcess(this.Enlistment.RepoRoot, "rev-parse HEAD");
string commitID = revParseHeadResult.Output.Trim();
NativeTests.PlaceHolderHasVersionInfo(virtualFilePath, CurrentPlaceholderVersion, string.Empty).ShouldEqual(true);
this.PlaceholderHasVersionInfo(virtualFilePath, CurrentPlaceholderVersion, string.Empty).ShouldEqual(true);
}
[TestCase, Order(13)]
[Category(Categories.GitCommands)]
[Category(Categories.Mac.M3)]
public void FolderContentsProjectedAfterFolderCreateAndCheckout()
{
string folderName = "GVFlt_MultiThreadTest";
@ -419,6 +428,7 @@ BOOL APIENTRY DllMain( HMODULE hModule,
[TestCase, Order(14)]
[Category(Categories.GitCommands)]
[Category(Categories.Mac.M3)]
public void FolderContentsCorrectAfterCreateNewFolderRenameAndCheckoutCommitWithSameFolder()
{
// 1ca414ced40f64bf94fc6c7f885974708bc600be is the commit prior to adding Test_EPF_MoveRenameFileTests
@ -478,7 +488,7 @@ BOOL APIENTRY DllMain( HMODULE hModule,
// Read a copy of AllNullObjectRedownloaded.txt to force the object to be downloaded
GitProcess.InvokeProcess(this.Enlistment.RepoRoot, "rev-parse :Test_EPF_WorkingDirectoryTests/AllNullObjectRedownloaded_copy.txt").Output.Trim().ShouldEqual(sha);
string testFileContents = this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests\\AllNullObjectRedownloaded_copy.txt").ShouldBeAFile(this.fileSystem).WithContents();
string testFileContents = this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests", "AllNullObjectRedownloaded_copy.txt").ShouldBeAFile(this.fileSystem).WithContents();
objectPath.ShouldBeAFile(this.fileSystem);
// Set the contents of objectPath to all NULL
@ -486,7 +496,7 @@ BOOL APIENTRY DllMain( HMODULE hModule,
File.WriteAllBytes(objectPath, Enumerable.Repeat<byte>(0, (int)objectFileInfo.Length).ToArray());
// Read the original path and verify its contents are correct
this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests\\AllNullObjectRedownloaded.txt").ShouldBeAFile(this.fileSystem).WithContents(testFileContents);
this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests", "AllNullObjectRedownloaded.txt").ShouldBeAFile(this.fileSystem).WithContents(testFileContents);
// Confirm there's a new item in the corrupt objects folder
corruptObjectFolderPath.ShouldBeADirectory(this.fileSystem);
@ -495,6 +505,8 @@ BOOL APIENTRY DllMain( HMODULE hModule,
}
[TestCase, Order(17)]
//// TODO(Mac): Figure out why git for Mac is not requesting a redownload of the truncated object
[Category(Categories.Mac.M3)]
public void TruncatedObjectRedownloaded()
{
GitProcess.InvokeProcess(this.Enlistment.RepoRoot, "checkout " + this.Enlistment.Commitish);
@ -513,18 +525,30 @@ BOOL APIENTRY DllMain( HMODULE hModule,
// Read a copy of TruncatedObjectRedownloaded.txt to force the object to be downloaded
GitProcess.InvokeProcess(this.Enlistment.RepoRoot, "rev-parse :Test_EPF_WorkingDirectoryTests/TruncatedObjectRedownloaded_copy.txt").Output.Trim().ShouldEqual(sha);
string testFileContents = this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests\\TruncatedObjectRedownloaded_copy.txt").ShouldBeAFile(this.fileSystem).WithContents();
string testFileContents = this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests", "TruncatedObjectRedownloaded_copy.txt").ShouldBeAFile(this.fileSystem).WithContents();
objectPath.ShouldBeAFile(this.fileSystem);
// Truncate the contents of objectPath
string tempTruncatedObjectPath = objectPath + "truncated";
FileInfo objectFileInfo = new FileInfo(objectPath);
long objectLength = objectFileInfo.Length;
using (FileStream objectStream = new FileStream(objectPath, FileMode.Open))
using (FileStream truncatedObjectStream = new FileStream(tempTruncatedObjectPath, FileMode.CreateNew))
{
objectStream.SetLength(objectFileInfo.Length - 8);
for (int i = 0; i < (objectStream.Length - 16); ++i)
{
truncatedObjectStream.WriteByte((byte)objectStream.ReadByte());
}
}
this.fileSystem.DeleteFile(objectPath);
this.fileSystem.MoveFile(tempTruncatedObjectPath, objectPath);
tempTruncatedObjectPath.ShouldNotExistOnDisk(this.fileSystem);
objectPath.ShouldBeAFile(this.fileSystem);
new FileInfo(objectPath).Length.ShouldEqual(objectLength - 16);
// Read the original path and verify its contents are correct
this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests\\TruncatedObjectRedownloaded.txt").ShouldBeAFile(this.fileSystem).WithContents(testFileContents);
this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests", "TruncatedObjectRedownloaded.txt").ShouldBeAFile(this.fileSystem).WithContents(testFileContents);
// Confirm there's a new item in the corrupt objects folder
corruptObjectFolderPath.ShouldBeADirectory(this.fileSystem).WithItems().Count().ShouldEqual(initialCorruptObjectCount + 1);
@ -533,7 +557,7 @@ BOOL APIENTRY DllMain( HMODULE hModule,
[TestCase, Order(18)]
public void CreateFileAfterTryOpenNonExistentFile()
{
string filePath = this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests\\CreateFileAfterTryOpenNonExistentFile_NotProjected.txt");
string filePath = this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests", "CreateFileAfterTryOpenNonExistentFile_NotProjected.txt");
string fileContents = "CreateFileAfterTryOpenNonExistentFile file contents";
filePath.ShouldNotExistOnDisk(this.fileSystem);
this.fileSystem.WriteAllText(filePath, fileContents);
@ -543,11 +567,11 @@ BOOL APIENTRY DllMain( HMODULE hModule,
[TestCase, Order(19)]
public void RenameFileAfterTryOpenNonExistentFile()
{
string filePath = this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests\\RenameFileAfterTryOpenNonExistentFile_NotProjected.txt");
string filePath = this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests", "RenameFileAfterTryOpenNonExistentFile_NotProjected.txt");
string fileContents = "CreateFileAfterTryOpenNonExistentFile file contents";
filePath.ShouldNotExistOnDisk(this.fileSystem);
string newFilePath = this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests\\RenameFileAfterTryOpenNonExistentFile_NewFile.txt");
string newFilePath = this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests", "RenameFileAfterTryOpenNonExistentFile_NewFile.txt");
this.fileSystem.WriteAllText(newFilePath, fileContents);
newFilePath.ShouldBeAFile(this.fileSystem).WithContents(fileContents);
@ -571,6 +595,48 @@ BOOL APIENTRY DllMain( HMODULE hModule,
folderEntries.ShouldContain(file => file.Name.Equals(expectedEntryName));
}
private void EnumerateAndReadShouldNotChangeEnumerationOrder(string folderRelativePath)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
NativeTests.EnumerateAndReadDoesNotChangeEnumerationOrder(folderRelativePath).ShouldEqual(true);
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
string[] entries = Directory.GetFileSystemEntries(folderRelativePath);
foreach (string entry in entries)
{
File.ReadAllText(entry);
}
string[] postReadEntries = Directory.GetFileSystemEntries(folderRelativePath);
Enumerable.SequenceEqual(entries, postReadEntries)
.ShouldBeTrue($"Entries are not the same after reading. Orignial list:\n{string.Join(",", entries)}\n\nAfter read:\n{string.Join(",", postReadEntries)}");
}
else
{
Assert.Fail("Unsupported platform");
}
}
private bool PlaceholderHasVersionInfo(string relativePath, int version, string sha)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return NativeTests.PlaceHolderHasVersionInfo(relativePath, version, sha);
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
//// TODO(Mac): Add a version of PlaceHolderHasVersionInfo that works on Mac
return true;
}
else
{
Assert.Fail("Unsupported platform");
return false;
}
}
private class NativeTests
{
[DllImport("GVFS.NativeTests.dll")]

Просмотреть файл

@ -10,7 +10,8 @@ using System.Linq;
namespace GVFS.FunctionalTests.Tests.EnlistmentPerTestCase
{
[TestFixture]
[Category(Categories.Mac.M2)]
//// TODO(Mac): Enable for M2 once notifications are working
//// [Category(Categories.Mac.M2)]
public class PersistedModifiedPathsTests : TestsWithEnlistmentPerTestCase
{
private static readonly string FileToAdd = Path.Combine("GVFS", "TestAddFile.txt");

Просмотреть файл

@ -45,13 +45,13 @@ namespace GVFS.FunctionalTests.Tests
[TearDown]
public void TearDownTests()
{
CmdRunner.DeleteDirectoryWithRetry(this.fastFetchRepoRoot);
CmdRunner.DeleteDirectoryWithUnlimitedRetries(this.fastFetchRepoRoot);
}
[OneTimeTearDown]
public void DeleteControlRepo()
{
CmdRunner.DeleteDirectoryWithRetry(this.fastFetchControlRoot);
CmdRunner.DeleteDirectoryWithUnlimitedRetries(this.fastFetchControlRoot);
}
[TestCase]

Просмотреть файл

@ -15,19 +15,21 @@ namespace GVFS.FunctionalTests.Tests.GitCommands
private const string EncodingFileFolder = "FilenameEncoding";
private const string EncodingFilename = "ريلٌأكتوبرûمارسأغسطسºٰٰۂْٗ۵ريلٌأك.txt";
private const string ContentWhenEditingFile = "// Adding a comment to the file";
private const string EditFilePath = @"GVFS\GVFS.Common\GVFSContext.cs";
private const string DeleteFilePath = @"GVFS\GVFS\Program.cs";
private const string RenameFilePathFrom = @"GVFS\GVFS.Common\Physical\FileSystem\FileProperties.cs";
private const string RenameFilePathTo = @"GVFS\GVFS.Common\Physical\FileSystem\FileProperties2.cs";
private const string RenameFolderPathFrom = @"GVFS\GVFS.Common\PrefetchPacks";
private const string RenameFolderPathTo = @"GVFS\GVFS.Common\PrefetchPacksRenamed";
private const string UnknownTestName = "Unknown";
private static readonly string EditFilePath = Path.Combine("GVFS", "GVFS.Common", "GVFSContext.cs");
private static readonly string DeleteFilePath = Path.Combine("GVFS", "GVFS", "Program.cs");
private static readonly string RenameFilePathFrom = Path.Combine("GVFS", "GVFS.Common", "Physical", "FileSystem", "FileProperties.cs");
private static readonly string RenameFilePathTo = Path.Combine("GVFS", "GVFS.Common", "Physical", "FileSystem", "FileProperties2.cs");
private static readonly string RenameFolderPathFrom = Path.Combine("GVFS", "GVFS.Common", "PrefetchPacks");
private static readonly string RenameFolderPathTo = Path.Combine("GVFS", "GVFS.Common", "PrefetchPacksRenamed");
public GitCommandsTests() : base(enlistmentPerTest: false)
{
}
[TestCase]
[Category(Categories.Mac.M2)]
public void VerifyTestFilesExist()
{
// Sanity checks to ensure that the test files we expect to be in our test repo are present
@ -39,24 +41,28 @@ namespace GVFS.FunctionalTests.Tests.GitCommands
}
[TestCase]
[Category(Categories.Mac.M2)]
public void StatusTest()
{
this.ValidateGitCommand("status");
}
[TestCase]
[Category(Categories.Mac.M2)]
public void StatusShortTest()
{
this.ValidateGitCommand("status -s");
}
[TestCase]
[Category(Categories.Mac.M2)]
public void BranchTest()
{
this.ValidateGitCommand("branch");
}
[TestCase]
[Category(Categories.Mac.M2)]
public void NewBranchTest()
{
this.ValidateGitCommand("branch tests/functional/NewBranchTest");
@ -64,6 +70,7 @@ namespace GVFS.FunctionalTests.Tests.GitCommands
}
[TestCase]
[Category(Categories.Mac.M2)]
public void DeleteBranchTest()
{
this.ValidateGitCommand("branch tests/functional/DeleteBranchTest");

Просмотреть файл

@ -46,6 +46,14 @@ namespace GVFS.FunctionalTests.Tests.GitCommands
this.FilesShouldMatchCheckoutOfSourceBranch();
}
[TestCase]
public void RebaseConflict_RemoveDeletedTheirsFile()
{
this.ValidateGitCommand("checkout " + GitRepoTests.ConflictTargetBranch);
this.RunGitCommand("rebase " + GitRepoTests.ConflictSourceBranch);
this.ValidateGitCommand("rm Test_ConflictTests/ModifiedFiles/ChangeInSourceDeleteInTarget.txt");
}
[TestCase]
public void RebaseConflict_AddThenContinue()
{

Просмотреть файл

@ -132,7 +132,7 @@ namespace GVFS.FunctionalTests.Tests.MultiEnlistmentTests
string objectsRoot = GVFSHelpers.GetPersistedGitObjectsRoot(enlistment1.DotGVFSRoot).ShouldNotBeNull();
objectsRoot.ShouldBeADirectory(this.fileSystem);
CmdRunner.DeleteDirectoryWithRetry(objectsRoot);
CmdRunner.DeleteDirectoryWithUnlimitedRetries(objectsRoot);
string metadataPath = Path.Combine(this.localCachePath, "mapping.dat");
metadataPath.ShouldBeAFile(this.fileSystem);
@ -172,7 +172,7 @@ namespace GVFS.FunctionalTests.Tests.MultiEnlistmentTests
try
{
// Delete objectsRoot rather than this.localCachePath as the blob sizes database cannot be deleted while GVFS is mounted
CmdRunner.DeleteDirectoryWithRetry(objectsRoot);
CmdRunner.DeleteDirectoryWithUnlimitedRetries(objectsRoot);
Thread.Sleep(100);
}
catch (IOException)
@ -212,7 +212,7 @@ namespace GVFS.FunctionalTests.Tests.MultiEnlistmentTests
mappingFileContents.Length.ShouldNotEqual(0, "mapping.dat should not be empty");
// Delete the git objects root folder, mount should re-create it and the mapping.dat file should not change
CmdRunner.DeleteDirectoryWithRetry(objectsRoot);
CmdRunner.DeleteDirectoryWithUnlimitedRetries(objectsRoot);
enlistment.MountGVFS();
@ -239,7 +239,7 @@ namespace GVFS.FunctionalTests.Tests.MultiEnlistmentTests
mappingFileContents.Length.ShouldNotEqual(0, "mapping.dat should not be empty");
// Delete the local cache folder, mount should re-create it and generate a new mapping file and local cache key
CmdRunner.DeleteDirectoryWithRetry(enlistment.LocalCacheRoot);
CmdRunner.DeleteDirectoryWithUnlimitedRetries(enlistment.LocalCacheRoot);
enlistment.MountGVFS();
@ -267,7 +267,7 @@ namespace GVFS.FunctionalTests.Tests.MultiEnlistmentTests
// localCacheParentPath can be deleted (as the SQLite blob sizes database cannot be deleted while GVFS is mounted)
protected override void OnTearDownEnlistmentsDeleted()
{
CmdRunner.DeleteDirectoryWithRetry(this.localCacheParentPath);
CmdRunner.DeleteDirectoryWithUnlimitedRetries(this.localCacheParentPath);
}
private GVFSFunctionalTestEnlistment CloneAndMountEnlistment(string branch = null)

Просмотреть файл

@ -1,6 +1,7 @@
using GVFS.FunctionalTests.FileSystemRunners;
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace GVFS.FunctionalTests.Tools
{
@ -68,7 +69,15 @@ namespace GVFS.FunctionalTests.Tools
public void Delete()
{
CmdRunner.DeleteDirectoryWithRetry(this.RootPath);
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
CmdRunner.DeleteDirectoryWithUnlimitedRetries(this.RootPath);
}
else
{
// TODO(Mac): See if we can use BashRunner.DeleteDirectoryWithRetry on Windows as well
BashRunner.DeleteDirectoryWithUnlimitedRetries(this.RootPath);
}
}
}
}

Просмотреть файл

@ -133,11 +133,12 @@ namespace GVFS.FunctionalTests.Tools
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
// Use cmd.exe to delete the enlistment as it properly handles tombstones and reparse points
CmdRunner.DeleteDirectoryWithRetry(this.EnlistmentRoot);
CmdRunner.DeleteDirectoryWithUnlimitedRetries(this.EnlistmentRoot);
}
else
{
// TODO(Mac): BashRunner.DeleteDirectory(this.EnlistmentRoot);
// TODO(Mac): Figure out why the call to DeleteDirectoryWithRetry is not returning
// BashRunner.DeleteDirectoryWithRetry(this.EnlistmentRoot);
}
}

Просмотреть файл

@ -0,0 +1,103 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\GVFS.Build\GVFS.cs.props" />
<PropertyGroup>
<OutputType>Exe</OutputType>
<AssemblyName>GVFS.Hooks</AssemblyName>
<TargetFramework>netcoreapp2.1</TargetFramework>
<Platforms>x64</Platforms>
<RunTimeIdentifiers>osx-x64</RunTimeIdentifiers>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<PropertyGroup>
<RootNamespace>GVFS.Hooks</RootNamespace>
<AssemblyName>GVFS.Hooks</AssemblyName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Version>$(GVFSVersion)</Version>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Version>$(GVFSVersion)</Version>
</PropertyGroup>
<ItemGroup>
<Compile Remove="HooksPlatform\GVFSHooksPlatform.Windows.cs" />
</ItemGroup>
<ItemGroup>
<!--
Files from GVFS.Common included as links here to prevent adding
project reference. The project reference leads to performance degradation
due to the other dependencies that come along with GVFS.Common.
-->
<Compile Include="..\GVFS.Common\ConsoleHelper.cs">
<Link>Common\ConsoleHelper.cs</Link>
</Compile>
<Compile Include="..\GVFS.Common\Git\GitConfigHelper.cs">
<Link>Common\Git\GitConfigHelper.cs</Link>
</Compile>
<Compile Include="..\GVFS.Common\Git\GitConfigSetting.cs">
<Link>Common\Git\GitConfigSetting.cs</Link>
</Compile>
<Compile Include="..\GVFS.Common\Git\GitVersion.cs">
<Link>Common\Git\GitVersion.cs</Link>
</Compile>
<Compile Include="..\GVFS.Common\GVFSConstants.cs">
<Link>Common\GVFSConstants.cs</Link>
</Compile>
<Compile Include="..\GVFS.Common\GVFSEnlistment.Shared.cs">
<Link>Common\GVFSEnlistment.Shared.cs</Link>
</Compile>
<Compile Include="..\GVFS.Common\GVFSLock.Shared.cs">
<Link>Common\GVFSLock.Shared.cs</Link>
</Compile>
<Compile Include="..\GVFS.Common\NamedPipes\BrokenPipeException.cs">
<Link>Common\NamedPipes\BrokenPipeException.cs</Link>
</Compile>
<Compile Include="..\GVFS.Common\NamedPipes\LockNamedPipeMessages.cs">
<Link>Common\NamedPipes\LockNamedPipeMessages.cs</Link>
</Compile>
<Compile Include="..\GVFS.Common\NamedPipes\NamedPipeClient.cs">
<Link>Common\NamedPipes\NamedPipeClient.cs</Link>
</Compile>
<Compile Include="..\GVFS.Common\NamedPipes\NamedPipeStreamWriterExtensions.cs">
<Link>Common\NamedPipes\NamedPipeStreamWriterExtensions.cs</Link>
</Compile>
<Compile Include="..\GVFS.Common\NativeMethods.Shared.cs">
<Link>Common\NativeMethods.Shared.cs</Link>
</Compile>
<Compile Include="..\GVFS.Common\Paths.Shared.cs">
<Link>Common\Paths.Shared.cs</Link>
</Compile>
<Compile Include="..\GVFS.Common\ProcessHelper.cs">
<Link>Common\ProcessHelper.cs</Link>
</Compile>
<Compile Include="..\GVFS.Common\ProcessResult.cs">
<Link>Common\ProcessResult.cs</Link>
</Compile>
<Compile Include="..\GVFS.Common\Tracing\EventLevel.cs">
<Link>Common\Tracing\EventLevel.cs</Link>
</Compile>
<Compile Include="..\GVFS.Common\Tracing\EventMetadata.cs">
<Link>Common\Tracing\EventMetadata.cs</Link>
</Compile>
<Compile Include="..\GVFS.Common\Tracing\EventOpcode.cs">
<Link>Common\Tracing\EventOpcode.cs</Link>
</Compile>
<Compile Include="..\GVFS.Common\Tracing\ITracer.cs">
<Link>Common\Tracing\ITracer.cs</Link>
</Compile>
<Compile Include="..\GVFS.Common\Tracing\Keywords.cs">
<Link>Common\Tracing\Keywords.cs</Link>
</Compile>
<Compile Include="..\GVFS.Platform.Mac\MacFileSystem.Shared.cs">
<Link>Mac\MacFileSystem.Shared.cs</Link>
</Compile>
<Compile Include="..\GVFS.Platform.Mac\MacPlatform.Shared.cs">
<Link>Mac\MacPlatform.Shared.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup>
<PackageReference Include="StyleCop.Error.MSBuild" Version="1.0.0" Condition="'$(OS)' == 'Windows_NT'" />
<PackageReference Include="StyleCop.MSBuild" Version="5.0.0" Condition="'$(OS)' == 'Windows_NT'" />
</ItemGroup>
</Project>

Просмотреть файл

@ -83,6 +83,9 @@
<Compile Include="..\GVFS.Common\NamedPipes\NamedPipeClient.cs">
<Link>Common\NamedPipes\NamedPipeClient.cs</Link>
</Compile>
<Compile Include="..\GVFS.Common\NamedPipes\NamedPipeStreamWriterExtensions.cs">
<Link>Common\NamedPipes\NamedPipeStreamWriterExtensions.cs</Link>
</Compile>
<Compile Include="..\GVFS.Common\NativeMethods.Shared.cs">
<Link>Common\NativeMethods.Shared.cs</Link>
</Compile>
@ -116,6 +119,7 @@
<Compile Include="..\GVFS.Platform.Windows\WindowsPlatform.Shared.cs">
<Link>Windows\WindowsPlatform.Shared.cs</Link>
</Compile>
<Compile Include="HooksPlatform\GVFSHooksPlatform.Windows.cs" />
<Compile Include="KnownGitCommands.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />

Просмотреть файл

@ -0,0 +1,37 @@
using GVFS.Platform.Mac;
namespace GVFS.Hooks.HooksPlatform
{
public static class GVFSHooksPlatform
{
public static bool IsElevated()
{
return MacPlatform.IsElevatedImplementation();
}
public static bool IsProcessActive(int processId)
{
return MacPlatform.IsProcessActiveImplementation(processId);
}
public static string GetNamedPipeName(string enlistmentRoot)
{
return MacPlatform.GetNamedPipeNameImplementation(enlistmentRoot);
}
public static bool IsConsoleOutputRedirectedToFile()
{
return MacPlatform.IsConsoleOutputRedirectedToFileImplementation();
}
public static bool TryGetGVFSEnlistmentRoot(string directory, out string enlistmentRoot, out string errorMessage)
{
return MacPlatform.TryGetGVFSEnlistmentRootImplementation(directory, out enlistmentRoot, out errorMessage);
}
public static bool TryGetNormalizedPath(string path, out string normalizedPath, out string errorMessage)
{
return MacFileSystem.TryGetNormalizedPathImplementation(path, out normalizedPath, out errorMessage);
}
}
}

Просмотреть файл

@ -0,0 +1,37 @@
using GVFS.Platform.Windows;
namespace GVFS.Hooks.HooksPlatform
{
public static class GVFSHooksPlatform
{
public static bool IsElevated()
{
return WindowsPlatform.IsElevatedImplementation();
}
public static bool IsProcessActive(int processId)
{
return WindowsPlatform.IsProcessActiveImplementation(processId);
}
public static string GetNamedPipeName(string enlistmentRoot)
{
return WindowsPlatform.GetNamedPipeNameImplementation(enlistmentRoot);
}
public static bool IsConsoleOutputRedirectedToFile()
{
return WindowsPlatform.IsConsoleOutputRedirectedToFileImplementation();
}
public static bool TryGetGVFSEnlistmentRoot(string directory, out string enlistmentRoot, out string errorMessage)
{
return WindowsPlatform.TryGetGVFSEnlistmentRootImplementation(directory, out enlistmentRoot, out errorMessage);
}
public static bool TryGetNormalizedPath(string path, out string normalizedPath, out string errorMessage)
{
return WindowsFileSystem.TryGetNormalizedPathImplementation(path, out normalizedPath, out errorMessage);
}
}
}

Просмотреть файл

@ -1,13 +1,11 @@
using GVFS.Common;
using GVFS.Common.Git;
using GVFS.Common.NamedPipes;
using GVFS.Platform.Windows;
using GVFS.Hooks.HooksPlatform;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Security.Principal;
namespace GVFS.Hooks
{
@ -40,19 +38,19 @@ namespace GVFS.Hooks
string errorMessage;
string normalizedCurrentDirectory;
if (!WindowsFileSystem.TryGetNormalizedPathImplementation(Environment.CurrentDirectory, out normalizedCurrentDirectory, out errorMessage))
if (!GVFSHooksPlatform.TryGetNormalizedPath(Environment.CurrentDirectory, out normalizedCurrentDirectory, out errorMessage))
{
ExitWithError($"Failed to determine final path for current directory {Environment.CurrentDirectory}. Error: {errorMessage}");
}
if (!WindowsPlatform.TryGetGVFSEnlistmentRootImplementation(Environment.CurrentDirectory, out enlistmentRoot, out errorMessage))
if (!GVFSHooksPlatform.TryGetGVFSEnlistmentRoot(Environment.CurrentDirectory, out enlistmentRoot, out errorMessage))
{
// Nothing to hook when being run outside of a GVFS repo.
// This is also the path when run with --git-dir outside of a GVFS directory, see Story #949665
Environment.Exit(0);
}
enlistmentPipename = Paths.GetNamedPipeName(enlistmentRoot);
enlistmentPipename = GVFSHooksPlatform.GetNamedPipeName(enlistmentRoot);
switch (GetHookType(args))
{
@ -205,7 +203,7 @@ namespace GVFS.Hooks
int pid = GetParentPid(args);
if (pid == Program.InvalidProcessId ||
!ProcessHelper.IsProcessActive(pid))
!GVFSHooksPlatform.IsProcessActive(pid))
{
ExitWithError("GVFS.Hooks: Unable to find parent git.exe process " + "(PID: " + pid + ").");
}
@ -251,7 +249,7 @@ namespace GVFS.Hooks
private static void AcquireGVFSLockForProcess(bool unattended, string[] args, int pid, NamedPipeClient pipeClient)
{
string result;
bool checkGvfsLockAvailabilitOnly = CheckGVFSLockAvailabilityOnly(args);
bool checkGvfsLockAvailabilityOnly = CheckGVFSLockAvailabilityOnly(args);
string fullCommand = GenerateFullCommand(args);
if (!GVFSLock.TryAcquireGVFSLockForProcess(
@ -259,10 +257,11 @@ namespace GVFS.Hooks
pipeClient,
fullCommand,
pid,
WindowsPlatform.IsElevatedImplementation(),
checkGvfsLockAvailabilitOnly,
null, // gvfsEnlistmentRoot
out result))
GVFSHooksPlatform.IsElevated(),
isConsoleOutputRedirectedToFile: GVFSHooksPlatform.IsConsoleOutputRedirectedToFile(),
checkAvailabilityOnly: checkGvfsLockAvailabilityOnly,
gvfsEnlistmentRoot: null,
result: out result))
{
ExitWithError(result);
}
@ -277,7 +276,8 @@ namespace GVFS.Hooks
pipeClient,
fullCommand,
pid,
WindowsPlatform.IsElevatedImplementation(),
GVFSHooksPlatform.IsElevated(),
GVFSHooksPlatform.IsConsoleOutputRedirectedToFile(),
response =>
{
if (response == null || response.ResponseData == null)

Просмотреть файл

@ -7,13 +7,13 @@
#define VCRuntimeDir PackagesDir + "\GVFS.VCRuntime.0.1.0-build\lib\x64"
#define GVFSDir BuildOutputDir + "\GVFS.Windows\bin\" + PlatformAndConfiguration
#define GVFSCommonDir BuildOutputDir + "\GVFS.Common\bin\" + PlatformAndConfiguration + "\netstandard2.0"
#define HooksDir BuildOutputDir + "\GVFS.Hooks\bin\" + PlatformAndConfiguration
#define HooksDir BuildOutputDir + "\GVFS.Hooks.Windows\bin\" + PlatformAndConfiguration
#define HooksLoaderDir BuildOutputDir + "\GitHooksLoader\bin\" + PlatformAndConfiguration
#define ServiceDir BuildOutputDir + "\GVFS.Service\bin\" + PlatformAndConfiguration
#define ServiceUIDir BuildOutputDir + "\GVFS.Service.UI\bin\" + PlatformAndConfiguration
#define GVFSMountDir BuildOutputDir + "\GVFS.Mount.Windows\bin\" + PlatformAndConfiguration
#define ReadObjectDir BuildOutputDir + "\GVFS.ReadObjectHook\bin\" + PlatformAndConfiguration
#define VirtualFileSystemDir BuildOutputDir + "\GVFS.VirtualFileSystemHook\bin\" + PlatformAndConfiguration
#define ReadObjectDir BuildOutputDir + "\GVFS.ReadObjectHook.Windows\bin\" + PlatformAndConfiguration
#define VirtualFileSystemDir BuildOutputDir + "\GVFS.VirtualFileSystemHook.Windows\bin\" + PlatformAndConfiguration
#define MyAppName "GVFS"
#define MyAppInstallerVersion GetFileVersion(GVFSDir + "\GVFS.exe")

Просмотреть файл

@ -3,7 +3,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<AssemblyName>gvfs.mount</AssemblyName>
<TargetFramework>netcoreapp2.0</TargetFramework>
<TargetFramework>netcoreapp2.1</TargetFramework>
<Platforms>x64</Platforms>
<RunTimeIdentifiers>osx-x64</RunTimeIdentifiers>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>

Просмотреть файл

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Import Project="$(SolutionDir)\GVFS\GVFS.Build\GVFS.cs.props" />
@ -78,7 +78,7 @@
<Link>CommonAssemblyVersion.cs</Link>
</Compile>
<Compile Include="..\GVFS.PlatformLoader\PlatformLoader.Windows.cs">
<Link>PlatformLoader.Windows.cs</Link>
<Link>PlatformLoader.Windows.cs</Link>
</Compile>
<Compile Include="InProcessMount.cs" />
<Compile Include="MountAbortedException.cs" />
@ -122,9 +122,9 @@
</Target>
<Import Project="..\..\..\packages\StyleCop.Error.MSBuild.1.0.0\build\StyleCop.Error.MSBuild.Targets" Condition="Exists('..\..\..\packages\StyleCop.Error.MSBuild.1.0.0\build\StyleCop.Error.MSBuild.Targets')" />
<PropertyGroup>
<PostBuildEvent>xcopy /Y $(BuildOutputDir)\GVFS.ReadObjectHook\bin\$(Platform)\$(Configuration)\GVFS.ReadObjectHook.* $(TargetDir)
xcopy /Y $(BuildOutputDir)\GVFS.VirtualFileSystemHook\bin\$(Platform)\$(Configuration)\GVFS.VirtualFileSystemHook.* $(TargetDir)
xcopy /Y $(BuildOutputDir)\GVFS.Hooks\bin\$(Platform)\$(Configuration)\GVFS.Hooks.* $(TargetDir)</PostBuildEvent>
<PostBuildEvent>xcopy /Y $(BuildOutputDir)\GVFS.ReadObjectHook.Windows\bin\$(Platform)\$(Configuration)\GVFS.ReadObjectHook.* $(TargetDir)
xcopy /Y $(BuildOutputDir)\GVFS.VirtualFileSystemHook.Windows\bin\$(Platform)\$(Configuration)\GVFS.VirtualFileSystemHook.* $(TargetDir)
xcopy /Y $(BuildOutputDir)\GVFS.Hooks.Windows\bin\$(Platform)\$(Configuration)\GVFS.Hooks.* $(TargetDir)</PostBuildEvent>
</PropertyGroup>
<Import Project="..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.7\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets" Condition="Exists('..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.7\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets')" />
<Import Project="..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.7\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets" Condition="Exists('..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.7\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets')" />

Просмотреть файл

@ -3,6 +3,7 @@ using GVFS.Common.FileSystem;
using GVFS.Common.Git;
using GVFS.Common.Http;
using GVFS.Common.NamedPipes;
using GVFS.Common.Prefetch;
using GVFS.Common.Tracing;
using GVFS.PlatformLoader;
using GVFS.Virtualization;
@ -28,6 +29,7 @@ namespace GVFS.Mount
private FileSystemCallbacks fileSystemCallbacks;
private GVFSEnlistment enlistment;
private ITracer tracer;
private BackgroundPrefetcher prefetcher;
private CacheServerInfo cacheServer;
private RetryConfig retryConfig;
@ -117,13 +119,10 @@ namespace GVFS.Mount
this.ValidateMountPoints();
if (!GVFSPlatform.Instance.IsUnderConstruction)
string errorMessage;
if (!HooksInstaller.TryUpdateHooks(this.context, out errorMessage))
{
string errorMessage;
if (!HooksInstaller.TryUpdateHooks(this.context, out errorMessage))
{
this.FailMountAndExit(errorMessage);
}
this.FailMountAndExit(errorMessage);
}
GVFSPlatform.Instance.ConfigureVisualStudio(this.enlistment.GitBinPath, this.tracer);
@ -509,6 +508,11 @@ namespace GVFS.Mount
FileSystemVirtualizer virtualizer = this.CreateOrReportAndExit(() => GVFSPlatformLoader.CreateFileSystemVirtualizer(this.context, this.gitObjects), "Failed to create src folder virtualizer");
this.fileSystemCallbacks = this.CreateOrReportAndExit(() => new FileSystemCallbacks(this.context, this.gitObjects, RepoMetadata.Instance, virtualizer), "Failed to create src folder callback listener");
if (!this.context.Unattended)
{
this.prefetcher = this.CreateOrReportAndExit(() => new BackgroundPrefetcher(this.tracer, this.enlistment, this.context.FileSystem, this.gitObjects), "Failed to start background prefetcher");
}
int majorVersion;
int minorVersion;
if (!RepoMetadata.Instance.TryGetOnDiskLayoutVersion(out majorVersion, out minorVersion, out error))
@ -542,6 +546,12 @@ namespace GVFS.Mount
private void UnmountAndStopWorkingDirectoryCallbacks()
{
if (this.prefetcher != null)
{
this.prefetcher.Dispose();
this.prefetcher = null;
}
if (this.heartbeat != null)
{
this.heartbeat.Stop();

Просмотреть файл

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "container:../GVFS.VirtualFileSystemHook/GVFS.VirtualFileSystemHook.Mac.xcodeproj">
</FileRef>
<FileRef
location = "container:../GVFS.ReadObjectHook/GVFS.ReadObjectHook.Mac.xcodeproj">
</FileRef>
</Workspace>

Просмотреть файл

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

Просмотреть файл

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded</key>
<false/>
</dict>
</plist>

Просмотреть файл

@ -0,0 +1,108 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0940"
version = "2.0">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "267A836720EE9F27005E6B60"
BuildableName = "GVFS.VirtualFileSystemHook"
BlueprintName = "GVFS.VirtualFileSystemHook"
ReferencedContainer = "container:../GVFS.VirtualFileSystemHook/GVFS.VirtualFileSystemHook.Mac.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2673FD9120EBDAA900B64B7F"
BuildableName = "GVFS.ReadObjectHook"
BlueprintName = "GVFS.ReadObjectHook"
ReferencedContainer = "container:../GVFS.ReadObjectHook/GVFS.ReadObjectHook.Mac.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "267A836720EE9F27005E6B60"
BuildableName = "GVFS.VirtualFileSystemHook"
BlueprintName = "GVFS.VirtualFileSystemHook"
ReferencedContainer = "container:../GVFS.VirtualFileSystemHook/GVFS.VirtualFileSystemHook.Mac.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES"
launchAutomaticallySubstyle = "2">
<PathRunnable
runnableDebuggingMode = "0"
FilePath = "/&lt;placeholder&gt;">
</PathRunnable>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "267A836720EE9F27005E6B60"
BuildableName = "GVFS.VirtualFileSystemHook"
BlueprintName = "GVFS.VirtualFileSystemHook"
ReferencedContainer = "container:../GVFS.VirtualFileSystemHook/GVFS.VirtualFileSystemHook.Mac.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "267A836720EE9F27005E6B60"
BuildableName = "GVFS.VirtualFileSystemHook"
BlueprintName = "GVFS.VirtualFileSystemHook"
ReferencedContainer = "container:../GVFS.VirtualFileSystemHook/GVFS.VirtualFileSystemHook.Mac.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

Просмотреть файл

@ -1,4 +1,14 @@
#pragma once
#pragma once
#ifdef __APPLE__
typedef std::string PATH_STRING;
typedef int PIPE_HANDLE;
#elif _WIN32
typedef std::wstring PATH_STRING;
typedef HANDLE PIPE_HANDLE;
#else
#error Unsupported platform
#endif
enum ReturnCode
{
@ -24,153 +34,23 @@ inline void die(int err, const char *fmt, ...)
vfprintf(stderr, fmt, params);
va_end(params);
exit(err);
}
inline std::wstring GetFinalPathName(const std::wstring& path)
{
HANDLE fileHandle;
// Using FILE_FLAG_BACKUP_SEMANTICS as it works with file as well as folder path
// According to MSDN, https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx,
// we must set this flag to obtain a handle to a directory
fileHandle = CreateFileW(
path.c_str(),
FILE_READ_ATTRIBUTES,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
NULL);
if (fileHandle == INVALID_HANDLE_VALUE)
{
die(ReturnCode::PathNameError, "Could not open oppen handle to %ls to determine final path name, Error: %d\n", path.c_str(), GetLastError());
}
wchar_t finalPathByHandle[MAX_PATH] = { 0 };
DWORD finalPathSize = GetFinalPathNameByHandleW(fileHandle, finalPathByHandle, MAX_PATH, FILE_NAME_NORMALIZED);
if (finalPathSize == 0)
{
die(ReturnCode::PathNameError, "Could not get final path name by handle for %ls, Error: %d\n", path.c_str(), GetLastError());
}
std::wstring finalPath(finalPathByHandle);
// The remarks section of GetFinalPathNameByHandle mentions the return being prefixed with "\\?\" or "\\?\UNC\"
// More information the prefixes is here http://msdn.microsoft.com/en-us/library/aa365247(v=VS.85).aspx
std::wstring PathPrefix(L"\\\\?\\");
std::wstring UncPrefix(L"\\\\?\\UNC\\");
if (finalPath.compare(0, UncPrefix.length(), UncPrefix) == 0)
{
finalPath = L"\\\\" + finalPath.substr(UncPrefix.length());
}
else if (finalPath.compare(0, PathPrefix.length(), PathPrefix) == 0)
{
finalPath = finalPath.substr(PathPrefix.length());
}
return finalPath;
}
inline std::wstring GetGVFSPipeName(const char *appName)
{
// The pipe name is build using the path of the GVFS enlistment root.
// Start in the current directory and walk up the directory tree
// until we find a folder that contains the ".gvfs" folder
const size_t dotGVFSRelativePathLength = sizeof(L"\\.gvfs") / sizeof(wchar_t);
// TODO 640838: Support paths longer than MAX_PATH
wchar_t enlistmentRoot[MAX_PATH];
DWORD currentDirResult = GetCurrentDirectoryW(MAX_PATH - dotGVFSRelativePathLength, enlistmentRoot);
if (currentDirResult == 0 || currentDirResult > MAX_PATH - dotGVFSRelativePathLength)
{
die(ReturnCode::GetCurrentDirectoryFailure, "GetCurrentDirectory failed (%d)\n", GetLastError());
}
std::wstring finalRootPath(GetFinalPathName(enlistmentRoot));
errno_t copyResult = wcscpy_s(enlistmentRoot, finalRootPath.c_str());
if (copyResult != 0)
{
die(ReturnCode::PipeConnectError, "Could not copy finalRootPath: %ls. Error: %d\n", finalRootPath.c_str(), copyResult);
}
size_t enlistmentRootLength = wcslen(enlistmentRoot);
if ('\\' != enlistmentRoot[enlistmentRootLength - 1])
{
wcscat_s(enlistmentRoot, L"\\");
enlistmentRootLength++;
}
// Walk up enlistmentRoot looking for a folder named .gvfs
wchar_t* lastslash = enlistmentRoot + enlistmentRootLength - 1;
WIN32_FIND_DATAW findFileData;
HANDLE dotGVFSHandle;
while (1)
{
wcscat_s(lastslash, MAX_PATH - (lastslash - enlistmentRoot), L".gvfs");
dotGVFSHandle = FindFirstFileW(enlistmentRoot, &findFileData);
if (dotGVFSHandle != INVALID_HANDLE_VALUE)
{
FindClose(dotGVFSHandle);
if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
break;
}
}
lastslash--;
while ((enlistmentRoot != lastslash) && (*lastslash != '\\'))
{
lastslash--;
}
if (enlistmentRoot == lastslash)
{
die(ReturnCode::NotInGVFSEnlistment, "%s must be run from inside a GVFS enlistment\n", appName);
}
*(lastslash + 1) = 0;
};
*(lastslash) = 0;
std::wstring namedPipe(CharUpperW(enlistmentRoot));
std::replace(namedPipe.begin(), namedPipe.end(), L':', L'_');
return L"\\\\.\\pipe\\GVFS_" + namedPipe;
}
inline HANDLE CreatePipeToGVFS(const std::wstring& pipeName)
{
HANDLE pipeHandle;
while (1)
{
pipeHandle = CreateFileW(
pipeName.c_str(), // pipe name
GENERIC_READ | // read and write access
GENERIC_WRITE,
0, // no sharing
NULL, // default security attributes
OPEN_EXISTING, // opens existing pipe
0, // default attributes
NULL); // no template file
if (pipeHandle != INVALID_HANDLE_VALUE)
{
break;
}
if (GetLastError() != ERROR_PIPE_BUSY)
{
die(ReturnCode::PipeConnectError, "Could not open pipe: %ls, Error: %d\n", pipeName.c_str(), GetLastError());
}
if (!WaitNamedPipeW(pipeName.c_str(), 3000))
{
die(ReturnCode::PipeConnectTimeout, "Could not open pipe: %ls, Timed out.", pipeName.c_str());
}
}
return pipeHandle;
}
}
PATH_STRING GetFinalPathName(const PATH_STRING& path);
PATH_STRING GetGVFSPipeName(const char *appName);
PIPE_HANDLE CreatePipeToGVFS(const PATH_STRING& pipeName);
void DisableCRLFTranslationOnStdPipes();
bool WriteToPipe(
PIPE_HANDLE pipe,
const char* message,
unsigned long messageLength,
/* out */ unsigned long* bytesWritten,
/* out */ int* error);
bool ReadFromPipe(
PIPE_HANDLE pipe,
char* buffer,
unsigned long bufferLength,
/* out */ unsigned long* bytesRead,
/* out */ int* error);

Просмотреть файл

@ -0,0 +1,170 @@
#include "stdafx.h"
#include <dirent.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>
#include "common.h"
#define MAX_PATH 260
PATH_STRING GetFinalPathName(const PATH_STRING& path)
{
// TODO(Mac): Implement
return path;
}
PATH_STRING GetGVFSPipeName(const char *appName)
{
// The pipe name is built using the path of the GVFS enlistment root.
// Start in the current directory and walk up the directory tree
// until we find a folder that contains the ".gvfs" folder
// TODO 640838: Support paths longer than MAX_PATH
char enlistmentRoot[MAX_PATH];
if (getcwd(enlistmentRoot, MAX_PATH) == nullptr)
{
die(ReturnCode::GetCurrentDirectoryFailure, "getcwd failed (%d)\n", errno);
}
PATH_STRING finalRootPath(GetFinalPathName(enlistmentRoot));
size_t resultLength = strlcpy(enlistmentRoot, finalRootPath.c_str(), sizeof(enlistmentRoot));
if (resultLength >= sizeof(enlistmentRoot))
{
die(ReturnCode::PipeConnectError,
"Could not copy finalRootPath: %ls, insufficient buffer. resultLength: %d, sizeof(enlistmentRoot): %d\n",
finalRootPath.c_str(),
resultLength,
sizeof(enlistmentRoot));
}
size_t enlistmentRootLength = strlen(enlistmentRoot);
if ('/' != enlistmentRoot[enlistmentRootLength - 1])
{
strlcat(enlistmentRoot, "/", sizeof(enlistmentRoot));
enlistmentRootLength++;
}
// Walk up enlistmentRoot looking for a folder named .gvfs
char* lastslash = enlistmentRoot + enlistmentRootLength - 1;
bool gvfsFound = false;
while (1)
{
*lastslash = '\0';
DIR* directory = opendir(enlistmentRoot);
if (directory == nullptr)
{
die(ReturnCode::NotInGVFSEnlistment, "Failed to open directory: %s, error: %d\n", enlistmentRoot, errno);
}
dirent* dirEntry = readdir(directory);
while (!gvfsFound && dirEntry != nullptr)
{
if (dirEntry->d_type == DT_DIR && strcmp(dirEntry->d_name, ".gvfs") == 0)
{
gvfsFound = true;
}
else
{
dirEntry = readdir(directory);
}
}
closedir(directory);
if (gvfsFound)
{
break;
}
if (errno != 0)
{
die(ReturnCode::NotInGVFSEnlistment, "readdir failed in directory: %s, error: %i\n", enlistmentRoot, errno);
}
lastslash--;
while ((enlistmentRoot != lastslash) && (*lastslash != '/'))
{
lastslash--;
}
if (enlistmentRoot == lastslash)
{
die(ReturnCode::NotInGVFSEnlistment, "%s must be run from inside a GVFS enlistment\n", appName);
}
*(lastslash + 1) = 0;
};
*(lastslash) = 0;
return PATH_STRING(enlistmentRoot) + "/.gvfs/GVFS_NetCorePipe";
}
PIPE_HANDLE CreatePipeToGVFS(const PATH_STRING& pipeName)
{
PIPE_HANDLE socket_fd = socket(PF_UNIX, SOCK_STREAM, 0);
if (socket_fd < 0)
{
die(ReturnCode::PipeConnectError, "Failed to create a new socket, pipeName: %s, error: %d\n", pipeName.c_str(), errno);
}
struct sockaddr_un socket_address;
memset(&socket_address, 0, sizeof(struct sockaddr_un));
socket_address.sun_family = AF_UNIX;
size_t resultLength = strlcpy(socket_address.sun_path, pipeName.c_str(), sizeof(socket_address.sun_path));
if (resultLength >= sizeof(socket_address.sun_path))
{
die(ReturnCode::PipeConnectError,
"Could not copy pipeName: %ls, insufficient buffer. resultLength: %d, sizeof(socket_address.sun_path): %d\n",
pipeName.c_str(),
resultLength,
sizeof(socket_address.sun_path));
}
if(connect(socket_fd, (struct sockaddr *) &socket_address, sizeof(struct sockaddr_un)) != 0)
{
die(ReturnCode::PipeConnectError, "Failed to connect socket, pipeName: %s, error: %d\n", pipeName.c_str(), errno);
}
return socket_fd;
}
void DisableCRLFTranslationOnStdPipes()
{
// not required on Mac
}
bool WriteToPipe(PIPE_HANDLE pipe, const char* message, size_t messageLength, /* out */ size_t* bytesWritten, /* out */ int* error)
{
*bytesWritten = write(pipe, message, messageLength);
bool success = *bytesWritten == messageLength;
*error = success ? 0 : errno;
return success;
}
bool ReadFromPipe(PIPE_HANDLE pipe, char* buffer, size_t bufferLength, /* out */ size_t* bytesRead, /* out */ int* error)
{
*error = 0;
*bytesRead = 0;
ssize_t readByteCount;
do
{
readByteCount = recv(pipe, buffer, bufferLength, 0);
} while (readByteCount == -1 && errno == EINTR);
if (readByteCount < 0)
{
*error = errno;
return false;
}
*bytesRead = readByteCount;
return true;
}

Просмотреть файл

@ -0,0 +1,195 @@
#pragma once
#include "stdafx.h"
#include <fcntl.h>
#include <io.h>
#include <algorithm>
#include "common.h"
PATH_STRING GetFinalPathName(const PATH_STRING& path)
{
HANDLE fileHandle;
// Using FILE_FLAG_BACKUP_SEMANTICS as it works with file as well as folder path
// According to MSDN, https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx,
// we must set this flag to obtain a handle to a directory
fileHandle = CreateFileW(
path.c_str(),
FILE_READ_ATTRIBUTES,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
NULL);
if (fileHandle == INVALID_HANDLE_VALUE)
{
die(ReturnCode::PathNameError, "Could not open oppen handle to %ls to determine final path name, Error: %d\n", path.c_str(), GetLastError());
}
wchar_t finalPathByHandle[MAX_PATH] = { 0 };
DWORD finalPathSize = GetFinalPathNameByHandleW(fileHandle, finalPathByHandle, MAX_PATH, FILE_NAME_NORMALIZED);
if (finalPathSize == 0)
{
die(ReturnCode::PathNameError, "Could not get final path name by handle for %ls, Error: %d\n", path.c_str(), GetLastError());
}
std::wstring finalPath(finalPathByHandle);
// The remarks section of GetFinalPathNameByHandle mentions the return being prefixed with "\\?\" or "\\?\UNC\"
// More information the prefixes is here http://msdn.microsoft.com/en-us/library/aa365247(v=VS.85).aspx
std::wstring PathPrefix(L"\\\\?\\");
std::wstring UncPrefix(L"\\\\?\\UNC\\");
if (finalPath.compare(0, UncPrefix.length(), UncPrefix) == 0)
{
finalPath = L"\\\\" + finalPath.substr(UncPrefix.length());
}
else if (finalPath.compare(0, PathPrefix.length(), PathPrefix) == 0)
{
finalPath = finalPath.substr(PathPrefix.length());
}
return finalPath;
}
PATH_STRING GetGVFSPipeName(const char *appName)
{
// The pipe name is built using the path of the GVFS enlistment root.
// Start in the current directory and walk up the directory tree
// until we find a folder that contains the ".gvfs" folder
const size_t dotGVFSRelativePathLength = sizeof(L"\\.gvfs") / sizeof(wchar_t);
// TODO 640838: Support paths longer than MAX_PATH
wchar_t enlistmentRoot[MAX_PATH];
DWORD currentDirResult = GetCurrentDirectoryW(MAX_PATH - dotGVFSRelativePathLength, enlistmentRoot);
if (currentDirResult == 0 || currentDirResult > MAX_PATH - dotGVFSRelativePathLength)
{
die(ReturnCode::GetCurrentDirectoryFailure, "GetCurrentDirectory failed (%d)\n", GetLastError());
}
PATH_STRING finalRootPath(GetFinalPathName(enlistmentRoot));
errno_t copyResult = wcscpy_s(enlistmentRoot, finalRootPath.c_str());
if (copyResult != 0)
{
die(ReturnCode::PipeConnectError, "Could not copy finalRootPath: %ls. Error: %d\n", finalRootPath.c_str(), copyResult);
}
size_t enlistmentRootLength = wcslen(enlistmentRoot);
if ('\\' != enlistmentRoot[enlistmentRootLength - 1])
{
wcscat_s(enlistmentRoot, L"\\");
enlistmentRootLength++;
}
// Walk up enlistmentRoot looking for a folder named .gvfs
wchar_t* lastslash = enlistmentRoot + enlistmentRootLength - 1;
WIN32_FIND_DATAW findFileData;
HANDLE dotGVFSHandle;
while (1)
{
wcscat_s(lastslash, MAX_PATH - (lastslash - enlistmentRoot), L".gvfs");
dotGVFSHandle = FindFirstFileW(enlistmentRoot, &findFileData);
if (dotGVFSHandle != INVALID_HANDLE_VALUE)
{
FindClose(dotGVFSHandle);
if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
break;
}
}
lastslash--;
while ((enlistmentRoot != lastslash) && (*lastslash != '\\'))
{
lastslash--;
}
if (enlistmentRoot == lastslash)
{
die(ReturnCode::NotInGVFSEnlistment, "%s must be run from inside a GVFS enlistment\n", appName);
}
*(lastslash + 1) = 0;
};
*(lastslash) = 0;
PATH_STRING namedPipe(CharUpperW(enlistmentRoot));
std::replace(namedPipe.begin(), namedPipe.end(), L':', L'_');
return L"\\\\.\\pipe\\GVFS_" + namedPipe;
}
PIPE_HANDLE CreatePipeToGVFS(const PATH_STRING& pipeName)
{
PIPE_HANDLE pipeHandle;
while (1)
{
pipeHandle = CreateFileW(
pipeName.c_str(), // pipe name
GENERIC_READ | // read and write access
GENERIC_WRITE,
0, // no sharing
NULL, // default security attributes
OPEN_EXISTING, // opens existing pipe
0, // default attributes
NULL); // no template file
if (pipeHandle != INVALID_HANDLE_VALUE)
{
break;
}
if (GetLastError() != ERROR_PIPE_BUSY)
{
die(ReturnCode::PipeConnectError, "Could not open pipe: %ls, Error: %d\n", pipeName.c_str(), GetLastError());
}
if (!WaitNamedPipeW(pipeName.c_str(), 3000))
{
die(ReturnCode::PipeConnectTimeout, "Could not open pipe: %ls, Timed out.", pipeName.c_str());
}
}
return pipeHandle;
}
void DisableCRLFTranslationOnStdPipes()
{
// set the mode to binary so we don't get CRLF translation
_setmode(_fileno(stdin), _O_BINARY);
_setmode(_fileno(stdout), _O_BINARY);
}
bool WriteToPipe(PIPE_HANDLE pipe, const char* message, unsigned long messageLength, /* out */ unsigned long* bytesWritten, /* out */ int* error)
{
BOOL success = WriteFile(
pipe, // pipe handle
message, // message
messageLength, // message length
bytesWritten, // bytes written
NULL); // not overlapped
*error = success ? 0 : GetLastError();
return success != FALSE;
}
bool ReadFromPipe(PIPE_HANDLE pipe, char* buffer, unsigned long bufferLength, /* out */ unsigned long* bytesRead, /* out */ int* error)
{
*error = 0;
*bytesRead = 0;
BOOL success = ReadFile(
pipe, // pipe handle
buffer, // buffer to receive reply
bufferLength, // size of buffer
bytesRead, // number of bytes read
NULL); // not overlapped
if (!success)
{
*error = GetLastError();
}
return success || (*error == ERROR_MORE_DATA);
}

Просмотреть файл

@ -75,7 +75,7 @@ bool QueryDirectoryFileRestartScanResetsFilter(const char* folderPath)
&nonExistentFileFilter, // FileName
TRUE); // RestartScan
SHOULD_EQUAL(status, STATUS_NO_SUCH_FILE);
SHOULD_EQUAL(status, STATUS_NO_MORE_FILES);
}
catch (TestException&)
{

Просмотреть файл

@ -260,7 +260,7 @@ bool ProjFS_EnumTestNoMoreNoSuchReturnCodes(const char* virtualRootPath)
restartScan);
// Check expected status code for a repeat query on a given handle for a non-existent name.
VERIFY_ARE_EQUAL(STATUS_NO_SUCH_FILE, repeatStatus);
VERIFY_ARE_EQUAL(STATUS_NO_MORE_FILES, repeatStatus);
// Once more, this time without SL_RESTART_SCAN.
restartScan = false;

Просмотреть файл

@ -62,6 +62,9 @@
<Compile Include="ProfilingEnvironment.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="..\GVFS.PlatformLoader\PlatformLoader.Windows.cs">
<Link>PlatformLoader.Windows.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup>
<None Include="App.config" />

Просмотреть файл

@ -25,7 +25,7 @@ namespace GVFS.PerfProfiling
{
GVFSPlatform.Register(new WindowsPlatform());
string gitBinPath = GVFSPlatform.Instance.GitInstallation.GetInstalledGitBinPath();
string hooksPath = ProcessHelper.WhereDirectory(GVFSConstants.GVFSHooksExecutableName);
string hooksPath = ProcessHelper.WhereDirectory(GVFSPlatform.Instance.Constants.GVFSHooksExecutableName);
return GVFSEnlistment.CreateFromDirectory(enlistmentRootPath, gitBinPath, hooksPath);
}

Просмотреть файл

@ -1,4 +1,5 @@
using GVFS.Common;
using GVFS.PlatformLoader;
using GVFS.Virtualization.Projection;
using System;
using System.Collections.Generic;
@ -21,6 +22,7 @@ namespace GVFS.PerfProfiling
static void Main(string[] args)
{
GVFSPlatformLoader.Initialize();
string enlistmentRootPath = @"M:\OS";
if (args.Length > 0 && !string.IsNullOrWhiteSpace(args[0]))
{
@ -41,9 +43,9 @@ namespace GVFS.PerfProfiling
Dictionary<TestsToRun, Action> allTests = new Dictionary<TestsToRun, Action>
{
{ TestsToRun.ValidateIndex, () => GitIndexProjection.ReadIndex(Path.Combine(environment.Enlistment.WorkingDirectoryRoot, GVFSConstants.DotGit.Index)) },
{ TestsToRun.ValidateIndex, () => GitIndexProjection.ReadIndex(environment.Context.Tracer, Path.Combine(environment.Enlistment.WorkingDirectoryRoot, GVFSConstants.DotGit.Index)) },
{ TestsToRun.RebuildProjection, () => environment.FileSystemCallbacks.GitIndexProjectionProfiler.ForceRebuildProjection() },
{ TestsToRun.ValidateModifiedPaths, () => environment.FileSystemCallbacks.GitIndexProjectionProfiler.ForceAddMissingModifiedPaths() },
{ TestsToRun.ValidateModifiedPaths, () => environment.FileSystemCallbacks.GitIndexProjectionProfiler.ForceAddMissingModifiedPaths(environment.Context.Tracer) },
};
long before = GetMemoryUsage();

Просмотреть файл

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\GVFS.Build\GVFS.cs.props" />
<PropertyGroup>
<TargetFrameworks>netcoreapp2.0;netstandard2.0</TargetFrameworks>
<TargetFrameworks>netcoreapp2.1;netstandard2.0</TargetFrameworks>
<Platforms>x64</Platforms>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">

Просмотреть файл

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace GVFS.Platform.Mac
{
public partial class MacFileSystem
{
public static bool TryGetNormalizedPathImplementation(string path, out string normalizedPath, out string errorMessage)
{
// TODO(Mac): Properly determine normalized paths (e.g. across links)
errorMessage = null;
normalizedPath = path;
return true;
}
}
}

Просмотреть файл

@ -1,10 +1,11 @@
using GVFS.Common;
using GVFS.Common.FileSystem;
using System.IO;
using System.Runtime.InteropServices;
namespace GVFS.Platform.Mac
{
public class MacFileSystem : IPlatformFileSystem
public partial class MacFileSystem : IPlatformFileSystem
{
public bool SupportsFileMode { get; } = true;
@ -30,12 +31,17 @@ namespace GVFS.Platform.Mac
File.Copy(existingFileName, newFileName);
}
public void ChangeMode(string path, int mode)
{
Chmod(path, mode);
}
public bool TryGetNormalizedPath(string path, out string normalizedPath, out string errorMessage)
{
// TODO(Mac): Properly determine normalized paths (e.g. across links)
errorMessage = null;
normalizedPath = path;
return true;
return MacFileSystem.TryGetNormalizedPathImplementation(path, out normalizedPath, out errorMessage);
}
[DllImport("libc", EntryPoint = "chmod", SetLastError = true)]
private static extern int Chmod(string pathname, int mode);
}
}

Просмотреть файл

@ -0,0 +1,54 @@
using GVFS.Common;
using System.IO;
namespace GVFS.Platform.Mac
{
public partial class MacPlatform
{
public static bool IsElevatedImplementation()
{
// TODO(Mac): Implement proper check
return false;
}
public static bool IsProcessActiveImplementation(int processId)
{
// TODO(Mac): Implement proper check
return true;
}
public static string GetNamedPipeNameImplementation(string enlistmentRoot)
{
// Pipes are stored as files on OSX, use a rooted pipe name to keep full control of the location of the file
return Path.Combine(enlistmentRoot, GVFSConstants.DotGVFS.Root, "GVFS_NetCorePipe");
}
public static bool IsConsoleOutputRedirectedToFileImplementation()
{
// TODO(Mac): Implement proper check
return false;
}
public static bool TryGetGVFSEnlistmentRootImplementation(string directory, out string enlistmentRoot, out string errorMessage)
{
// TODO(Mac): Merge this code with the implementation in WindowsPlatform
enlistmentRoot = null;
string finalDirectory;
if (!MacFileSystem.TryGetNormalizedPathImplementation(directory, out finalDirectory, out errorMessage))
{
return false;
}
enlistmentRoot = Paths.GetRoot(finalDirectory, GVFSConstants.DotGVFS.Root);
if (enlistmentRoot == null)
{
errorMessage = $"Failed to find the root directory for {GVFSConstants.DotGVFS.Root} in {finalDirectory}";
return false;
}
return true;
}
}
}

Просмотреть файл

@ -4,12 +4,18 @@ using GVFS.Common.Git;
using GVFS.Common.Tracing;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Pipes;
namespace GVFS.Platform.Mac
{
public class MacPlatform : GVFSPlatform
public partial class MacPlatform : GVFSPlatform
{
public MacPlatform()
: base(executableExtension: string.Empty)
{
}
public override IKernelDriver KernelDriver { get; } = new ProjFSKext();
public override IGitInstallation GitInstallation { get; } = new MacGitInstallation();
public override IDiskLayoutUpgradeData DiskLayoutUpgrade { get; } = new MacDiskLayoutUpgradeData();
@ -31,6 +37,25 @@ namespace GVFS.Platform.Mac
return true;
}
public override bool TryInstallGitCommandHooks(GVFSContext context, string executingDirectory, string hookName, string commandHookPath, out string errorMessage)
{
errorMessage = null;
string gvfsHooksPath = Path.Combine(executingDirectory, GVFSPlatform.Instance.Constants.GVFSHooksExecutableName);
File.WriteAllText(
commandHookPath,
$"#!/bin/sh\n{gvfsHooksPath} {hookName} \"$@\"");
GVFSPlatform.Instance.FileSystem.ChangeMode(commandHookPath, Convert.ToInt32("755", 8));
return true;
}
public override bool IsProcessActive(int processId)
{
return MacPlatform.IsProcessActiveImplementation(processId);
}
public override void StartBackgroundProcess(string programName, string[] args)
{
ProcessLauncher.StartBackgroundProcess(programName, args);
@ -77,38 +102,24 @@ namespace GVFS.Platform.Mac
{
}
public override string GetNamedPipeName(string enlistmentRoot)
{
return MacPlatform.GetNamedPipeNameImplementation(enlistmentRoot);
}
public override bool IsConsoleOutputRedirectedToFile()
{
// TODO(Mac): Implement proper check
return false;
return MacPlatform.IsConsoleOutputRedirectedToFileImplementation();
}
public override bool IsElevated()
{
// TODO(Mac): Implement proper check
return false;
return MacPlatform.IsElevatedImplementation();
}
public override bool TryGetGVFSEnlistmentRoot(string directory, out string enlistmentRoot, out string errorMessage)
{
// TODO(Mac): Merge this code with the implementation in WindowsPlatform
enlistmentRoot = null;
string finalDirectory;
if (!this.FileSystem.TryGetNormalizedPath(directory, out finalDirectory, out errorMessage))
{
return false;
}
enlistmentRoot = Paths.GetRoot(finalDirectory, GVFSConstants.DotGVFS.Root);
if (enlistmentRoot == null)
{
errorMessage = $"Failed to find the root directory for {GVFSConstants.DotGVFS.Root} in {finalDirectory}";
return false;
}
return true;
return MacPlatform.TryGetGVFSEnlistmentRootImplementation(directory, out enlistmentRoot, out errorMessage);
}
}
}

Просмотреть файл

@ -28,25 +28,45 @@ namespace GVFS.Platform.Windows.DiskLayoutUpgrades
}
string sparseCheckoutPath = Path.Combine(enlistmentRoot, GVFSConstants.WorkingDirectoryRootName, GVFSConstants.DotGit.Info.SparseCheckoutPath);
IEnumerable<string> sparseCheckoutLines = fileSystem.ReadAllText(sparseCheckoutPath).Split(new string[] { "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries);
bool isRetryable;
foreach (string entry in sparseCheckoutLines)
using (FileStream fs = File.OpenRead(sparseCheckoutPath))
using (StreamReader reader = new StreamReader(fs))
{
bool isFolder = entry.EndsWith(GVFSConstants.GitPathSeparatorString);
if (!modifiedPaths.TryAdd(entry.Trim(GVFSConstants.GitPathSeparator), isFolder, out isRetryable))
string entry = reader.ReadLine();
while (entry != null)
{
tracer.RelatedError("Unable to add to the modified paths database.");
return false;
entry = entry.Trim();
if (!string.IsNullOrWhiteSpace(entry))
{
bool isFolder = entry.EndsWith(GVFSConstants.GitPathSeparatorString);
if (!modifiedPaths.TryAdd(entry.Trim(GVFSConstants.GitPathSeparator), isFolder, out isRetryable))
{
tracer.RelatedError("Unable to add to the modified paths database.");
return false;
}
}
entry = reader.ReadLine();
}
}
string alwaysExcludePath = Path.Combine(enlistmentRoot, GVFSConstants.WorkingDirectoryRootName, GVFSConstants.DotGit.Info.AlwaysExcludePath);
if (fileSystem.FileExists(alwaysExcludePath))
{
string[] alwaysExcludeLines = fileSystem.ReadAllText(alwaysExcludePath).Split(new string[] { "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries);
for (int i = alwaysExcludeLines.Length - 1; i >= 0; i--)
string alwaysExcludeData = fileSystem.ReadAllText(alwaysExcludePath);
char[] carriageReturnOrLineFeed = new[] { '\r', '\n' };
int endPosition = alwaysExcludeData.Length;
while (endPosition > 0)
{
string entry = alwaysExcludeLines[i];
int startPosition = alwaysExcludeData.LastIndexOfAny(carriageReturnOrLineFeed, endPosition - 1);
if (startPosition < 0)
{
startPosition = 0;
}
string entry = alwaysExcludeData.Substring(startPosition, endPosition - startPosition).Trim();
if (entry.EndsWith("*"))
{
// This is the first entry using the old format and we don't want to process old entries
@ -55,16 +75,23 @@ namespace GVFS.Platform.Windows.DiskLayoutUpgrades
break;
}
entry = entry.TrimStart('!');
bool isFolder = entry.EndsWith(GVFSConstants.GitPathSeparatorString);
if (!isFolder)
// Substring will not return a null and the Trim will get rid of all the whitespace
// if there is a length it will be a valid path that we need to process
if (entry.Length > 0)
{
if (!modifiedPaths.TryAdd(entry.Trim(GVFSConstants.GitPathSeparator), isFolder, out isRetryable))
entry = entry.TrimStart('!');
bool isFolder = entry.EndsWith(GVFSConstants.GitPathSeparatorString);
if (!isFolder)
{
tracer.RelatedError("Unable to add to the modified paths database.");
return false;
if (!modifiedPaths.TryAdd(entry.Trim(GVFSConstants.GitPathSeparator), isFolder, out isRetryable))
{
tracer.RelatedError("Unable to add to the modified paths database.");
return false;
}
}
}
endPosition = startPosition;
}
}

Просмотреть файл

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Import Project="$(SolutionDir)\GVFS\GVFS.Build\ProjFS.props" />
@ -92,6 +92,7 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="WindowsFileSystem.Shared.cs" />
<Compile Include="WindowsFileSystemVirtualizer.cs" />
<Compile Include="WindowsGitHooksInstaller.cs" />
<Compile Include="WindowsGitInstallation.cs" />
<Compile Include="WindowsPhysicalDiskInfo.cs" />
<Compile Include="WindowsPlatform.cs" />

Просмотреть файл

@ -25,6 +25,10 @@ namespace GVFS.Platform.Windows
NativeMethods.CreateHardLink(newFileName, existingFileName);
}
public void ChangeMode(string path, int mode)
{
}
public bool TryGetNormalizedPath(string path, out string normalizedPath, out string errorMessage)
{
return WindowsFileSystem.TryGetNormalizedPathImplementation(path, out normalizedPath, out errorMessage);

Просмотреть файл

@ -0,0 +1,55 @@
using GVFS.Common;
using GVFS.Common.FileSystem;
using GVFS.Common.Git;
using System;
using System.IO;
namespace GVFS.Platform.Windows
{
internal static class WindowsGitHooksInstaller
{
private const string HooksConfigContentTemplate =
@"########################################################################
# Automatically generated file, do not modify.
# See {0} config setting
########################################################################
{1}";
public static void CreateHookCommandConfig(GVFSContext context, string hookName, string commandHookPath)
{
string targetPath = commandHookPath + GVFSConstants.GitConfig.HooksExtension;
try
{
string configSetting = GVFSConstants.GitConfig.HooksPrefix + hookName;
string mergedHooks = MergeHooks(context, configSetting, hookName);
string contents = string.Format(HooksConfigContentTemplate, configSetting, mergedHooks);
Exception ex;
if (!context.FileSystem.TryWriteTempFileAndRename(targetPath, contents, out ex))
{
throw new RetryableException("Error installing " + targetPath, ex);
}
}
catch (IOException io)
{
throw new RetryableException("Error installing " + targetPath, io);
}
}
private static string MergeHooks(GVFSContext context, string configSettingName, string hookName)
{
GitProcess configProcess = new GitProcess(context.Enlistment);
string filename;
string[] defaultHooksLines = { };
if (configProcess.TryGetFromConfig(configSettingName, forceOutsideEnlistment: true, value: out filename))
{
filename = filename.Trim(' ', '\n');
defaultHooksLines = File.ReadAllLines(filename);
}
return HooksInstaller.MergeHooksData(defaultHooksLines, filename, hookName);
}
}
}

Просмотреть файл

@ -1,10 +1,31 @@
using GVFS.Common;
using Microsoft.Win32.SafeHandles;
using System;
using System.Runtime.InteropServices;
using System.Security.Principal;
namespace GVFS.Platform.Windows
{
public partial class WindowsPlatform
{
private const int StillActive = 259; /* from Win32 STILL_ACTIVE */
private enum StdHandle
{
Stdin = -10,
Stdout = -11,
Stderr = -12
}
private enum FileType : uint
{
Unknown = 0x0000,
Disk = 0x0001,
Char = 0x0002,
Pipe = 0x0003,
Remote = 0x8000,
}
public static bool IsElevatedImplementation()
{
using (WindowsIdentity id = WindowsIdentity.GetCurrent())
@ -13,6 +34,33 @@ namespace GVFS.Platform.Windows
}
}
public static bool IsProcessActiveImplementation(int processId)
{
using (SafeFileHandle process = NativeMethods.OpenProcess(NativeMethods.ProcessAccessFlags.QueryLimitedInformation, false, processId))
{
if (!process.IsInvalid)
{
uint exitCode;
if (NativeMethods.GetExitCodeProcess(process, out exitCode) && exitCode == StillActive)
{
return true;
}
}
return false;
}
}
public static string GetNamedPipeNameImplementation(string enlistmentRoot)
{
return "GVFS_" + enlistmentRoot.ToUpper().Replace(':', '_');
}
public static bool IsConsoleOutputRedirectedToFileImplementation()
{
return FileType.Disk == GetFileType(GetStdHandle(StdHandle.Stdout));
}
public static bool TryGetGVFSEnlistmentRootImplementation(string directory, out string enlistmentRoot, out string errorMessage)
{
enlistmentRoot = null;
@ -32,5 +80,11 @@ namespace GVFS.Platform.Windows
return true;
}
[DllImport("kernel32.dll")]
private static extern IntPtr GetStdHandle(StdHandle std);
[DllImport("kernel32.dll")]
private static extern FileType GetFileType(IntPtr hdl);
}
}

Просмотреть файл

@ -22,6 +22,11 @@ namespace GVFS.Platform.Windows
private const string BuildLabRegistryValue = "BuildLab";
private const string BuildLabExRegistryValue = "BuildLabEx";
public WindowsPlatform()
: base(executableExtension: ".exe")
{
}
public override IKernelDriver KernelDriver { get; } = new ProjFSFilter();
public override IGitInstallation GitInstallation { get; } = new WindowsGitInstallation();
public override IDiskLayoutUpgradeData DiskLayoutUpgrade { get; } = new WindowsDiskLayoutUpgradeData();
@ -158,6 +163,16 @@ namespace GVFS.Platform.Windows
return WindowsPlatform.IsElevatedImplementation();
}
public override bool IsProcessActive(int processId)
{
return WindowsPlatform.IsProcessActiveImplementation(processId);
}
public override string GetNamedPipeName(string enlistmentRoot)
{
return WindowsPlatform.GetNamedPipeNameImplementation(enlistmentRoot);
}
public override void ConfigureVisualStudio(string gitBinPath, ITracer tracer)
{
const string GitBinPathEnd = "\\cmd\\git.exe";
@ -181,18 +196,44 @@ namespace GVFS.Platform.Windows
{
error = null;
hooksVersion = null;
hooksPath = ProcessHelper.WhereDirectory(GVFSConstants.GVFSHooksExecutableName);
hooksPath = ProcessHelper.WhereDirectory(GVFSPlatform.Instance.Constants.GVFSHooksExecutableName);
if (hooksPath == null)
{
error = "Could not find " + GVFSConstants.GVFSHooksExecutableName;
error = "Could not find " + GVFSPlatform.Instance.Constants.GVFSHooksExecutableName;
return false;
}
FileVersionInfo hooksFileVersionInfo = FileVersionInfo.GetVersionInfo(hooksPath + "\\" + GVFSConstants.GVFSHooksExecutableName);
FileVersionInfo hooksFileVersionInfo = FileVersionInfo.GetVersionInfo(Path.Combine(hooksPath, GVFSPlatform.Instance.Constants.GVFSHooksExecutableName));
hooksVersion = hooksFileVersionInfo.ProductVersion;
return true;
}
public override bool TryInstallGitCommandHooks(GVFSContext context, string executingDirectory, string hookName, string commandHookPath, out string errorMessage)
{
// The GitHooksLoader requires the following setup to invoke a hook:
// Copy GithooksLoader.exe to hook-name.exe
// Create a text file named hook-name.hooks that lists the applications to execute for the hook, one application per line
string gitHooksloaderPath = Path.Combine(executingDirectory, GVFSConstants.DotGit.Hooks.LoaderExecutable);
if (!HooksInstaller.TryHooksInstallationAction(
() => HooksInstaller.CopyHook(context, gitHooksloaderPath, commandHookPath + GVFSPlatform.Instance.Constants.ExecutableExtension),
out errorMessage))
{
errorMessage = "Failed to copy " + GVFSConstants.DotGit.Hooks.LoaderExecutable + " to " + commandHookPath + GVFSPlatform.Instance.Constants.ExecutableExtension + "\n" + errorMessage;
return false;
}
if (!HooksInstaller.TryHooksInstallationAction(
() => WindowsGitHooksInstaller.CreateHookCommandConfig(context, hookName, commandHookPath),
out errorMessage))
{
errorMessage = "Failed to create " + commandHookPath + GVFSConstants.GitConfig.HooksExtension + "\n" + errorMessage;
return false;
}
return true;
}
public override string GetCurrentUser()
{
WindowsIdentity identity = WindowsIdentity.GetCurrent();
@ -204,7 +245,7 @@ namespace GVFS.Platform.Windows
public override bool IsConsoleOutputRedirectedToFile()
{
return ConsoleHelper.IsConsoleOutputRedirectedToFile();
return WindowsPlatform.IsConsoleOutputRedirectedToFileImplementation();
}
public override bool TryGetGVFSEnlistmentRoot(string directory, out string enlistmentRoot, out string errorMessage)

Просмотреть файл

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="GVFS.ProjFS" version="2018.628.1" targetFramework="net461" />
<package id="GVFS.ProjFS" version="2018.719.1" targetFramework="net461" />
<package id="ManagedEsent" version="1.9.4" targetFramework="net461" />
<package id="Microsoft.Database.Collections.Generic" version="1.9.4" targetFramework="net461" />
<package id="Microsoft.Database.Isam" version="1.9.4" targetFramework="net461" />

Просмотреть файл

@ -0,0 +1,284 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 50;
objects = {
/* Begin PBXBuildFile section */
2673FD9620EBDAA900B64B7F /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2673FD9520EBDAA900B64B7F /* main.cpp */; };
2673FD9C20EBDEA500B64B7F /* packet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2673FD9B20EBDEA500B64B7F /* packet.cpp */; };
26E839D820FD387D004E53CE /* common.mac.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26E839D720FD29EC004E53CE /* common.mac.cpp */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
2673FD9020EBDAA900B64B7F /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = /usr/share/man/man1/;
dstSubfolderSpec = 0;
files = (
);
runOnlyForDeploymentPostprocessing = 1;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
2673FD9220EBDAA900B64B7F /* GVFS.ReadObjectHook */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = GVFS.ReadObjectHook; sourceTree = BUILT_PRODUCTS_DIR; };
2673FD9520EBDAA900B64B7F /* main.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = SOURCE_ROOT; };
2673FD9A20EBDEA500B64B7F /* packet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = packet.h; sourceTree = SOURCE_ROOT; };
2673FD9B20EBDEA500B64B7F /* packet.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = packet.cpp; sourceTree = SOURCE_ROOT; };
2673FD9D20EBDEAA00B64B7F /* common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = common.h; path = ../GVFS.NativeHooks.Common/common.h; sourceTree = SOURCE_ROOT; };
26E839D720FD29EC004E53CE /* common.mac.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = common.mac.cpp; path = ../GVFS.NativeHooks.Common/common.mac.cpp; sourceTree = SOURCE_ROOT; };
26E839DA20FD58B8004E53CE /* stdafx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stdafx.h; sourceTree = SOURCE_ROOT; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
2673FD8F20EBDAA900B64B7F /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
2673FD7F20EBD81D00B64B7F = {
isa = PBXGroup;
children = (
26E839DA20FD58B8004E53CE /* stdafx.h */,
26E839D720FD29EC004E53CE /* common.mac.cpp */,
2673FD9D20EBDEAA00B64B7F /* common.h */,
2673FD9B20EBDEA500B64B7F /* packet.cpp */,
2673FD9A20EBDEA500B64B7F /* packet.h */,
2673FD9520EBDAA900B64B7F /* main.cpp */,
2673FD9320EBDAA900B64B7F /* Products */,
);
sourceTree = "<group>";
};
2673FD9320EBDAA900B64B7F /* Products */ = {
isa = PBXGroup;
children = (
2673FD9220EBDAA900B64B7F /* GVFS.ReadObjectHook */,
);
name = Products;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
2673FD9120EBDAA900B64B7F /* GVFS.ReadObjectHook */ = {
isa = PBXNativeTarget;
buildConfigurationList = 2673FD9720EBDAA900B64B7F /* Build configuration list for PBXNativeTarget "GVFS.ReadObjectHook" */;
buildPhases = (
2673FD8E20EBDAA900B64B7F /* Sources */,
2673FD8F20EBDAA900B64B7F /* Frameworks */,
2673FD9020EBDAA900B64B7F /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = GVFS.ReadObjectHook;
productName = GVFS.ReadObjectHook;
productReference = 2673FD9220EBDAA900B64B7F /* GVFS.ReadObjectHook */;
productType = "com.apple.product-type.tool";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
2673FD8020EBD81D00B64B7F /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0940;
TargetAttributes = {
2673FD9120EBDAA900B64B7F = {
CreatedOnToolsVersion = 9.4.1;
};
};
};
buildConfigurationList = 2673FD8320EBD81D00B64B7F /* Build configuration list for PBXProject "GVFS.ReadObjectHook.Mac" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = 2673FD7F20EBD81D00B64B7F;
productRefGroup = 2673FD9320EBDAA900B64B7F /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
2673FD9120EBDAA900B64B7F /* GVFS.ReadObjectHook */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
2673FD8E20EBDAA900B64B7F /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
26E839D820FD387D004E53CE /* common.mac.cpp in Sources */,
2673FD9620EBDAA900B64B7F /* main.cpp in Sources */,
2673FD9C20EBDEA500B64B7F /* packet.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
2673FD8420EBD81D00B64B7F /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
};
name = Debug;
};
2673FD8520EBD81D00B64B7F /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
};
name = Release;
};
2673FD9820EBDAA900B64B7F /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "Mac Developer";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = UBF8T346G9;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.13;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
};
name = Debug;
};
2673FD9920EBDAA900B64B7F /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "Mac Developer";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = UBF8T346G9;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.13;
MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
2673FD8320EBD81D00B64B7F /* Build configuration list for PBXProject "GVFS.ReadObjectHook.Mac" */ = {
isa = XCConfigurationList;
buildConfigurations = (
2673FD8420EBD81D00B64B7F /* Debug */,
2673FD8520EBD81D00B64B7F /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
2673FD9720EBDAA900B64B7F /* Build configuration list for PBXNativeTarget "GVFS.ReadObjectHook" */ = {
isa = XCConfigurationList;
buildConfigurations = (
2673FD9820EBDAA900B64B7F /* Debug */,
2673FD9920EBDAA900B64B7F /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 2673FD8020EBD81D00B64B7F /* Project object */;
}

Просмотреть файл

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:GVFS.ReadObjectHook.Mac.xcodeproj">
</FileRef>
</Workspace>

Просмотреть файл

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

Просмотреть файл

@ -15,6 +15,8 @@
<Keyword>Win32Proj</Keyword>
<RootNamespace>readobject</RootNamespace>
<WindowsTargetPlatformVersion>10.0.10240.0</WindowsTargetPlatformVersion>
<ProjectName>GVFS.ReadObjectHook.Windows</ProjectName>
<TargetName>GVFS.ReadObjectHook</TargetName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<Import Project="$(SolutionDir)\GVFS\GVFS.Build\GVFS.cpp.props" />
@ -109,6 +111,7 @@
<ClInclude Include="targetver.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\GVFS.NativeHooks.Common\common.windows.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="packet.cpp" />
<ClCompile Include="stdafx.cpp">

Просмотреть файл

@ -16,6 +16,9 @@
<Filter Include="Shared Header Files">
<UniqueIdentifier>{c3243239-d853-4df9-bdbb-9a4efa72a827}</UniqueIdentifier>
</Filter>
<Filter Include="Shared Source Files">
<UniqueIdentifier>{e6c30bd2-e246-47d9-ad10-137165f4628c}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="stdafx.h">
@ -44,6 +47,9 @@
<ClCompile Include="packet.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\GVFS.NativeHooks.Common\common.windows.cpp">
<Filter>Shared Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Version.rc">

Просмотреть файл

@ -1,147 +1,150 @@
// GVFS.ReadObjectHook
//
// When GVFS installs GVFS.ReadObjectHook.exe, it copies the file to
// the .git\hooks folder, and renames the executable to read-object.exe
// read-object.exe is called by git.exe when it fails to find the object it's looking for on disk.
// When GVFS installs GVFS.ReadObjectHook, it copies the file to
// the .git\hooks folder, and renames the executable to read-object
// read-object is called by git when it fails to find the object it's looking for on disk.
//
// Git and read-object.exe negoiate an interface and capabilities then git issues a "get" command for the missing SHA.
// Git and read-object negotiate an interface and capabilities then git issues a "get" command for the missing SHA.
// See Git Documentation/Technical/read-object-protocol.txt for details.
// GVFS.ReadObjectHook decides which GVFS instance to connect to based on it's path.
// GVFS.ReadObjectHook decides which GVFS instance to connect to based on its path.
// It then connects to GVFS and asks GVFS to download the requested object (to the .git\objects folder).
#include "stdafx.h"
#include "packet.h"
#include "packet.h"
#include "common.h"
#define MAX_PACKET_LENGTH 512
#define SHA1_LENGTH 40
#define MESSAGE_LENGTH (4 + SHA1_LENGTH + 1)
#define DLO_REQUEST_LENGTH (4 + SHA1_LENGTH + 1)
// Expected response:
// "S\n" -> Success
// "F\n" -> Failure
#define DLO_RESPONSE_LENGTH 2
enum ReadObjectHookErrorReturnCode
{
ErrorReadObjectProtocol = ReturnCode::LastError + 1,
ErrorReadObjectProtocol = ReturnCode::LastError + 1,
};
int DownloadSHA(HANDLE pipeHandle, const char *sha1)
int DownloadSHA(PIPE_HANDLE pipeHandle, const char *sha1)
{
// Construct download request message
// Format: "DLO|<40 character SHA>"
// Example: "DLO|920C34DCDDFC8F07AC4704C8C0D087D6F2095729"
char message[MESSAGE_LENGTH+1];
if (_snprintf_s(message, _TRUNCATE, "DLO|%s\n", sha1) < 0)
{
die(ReturnCode::InvalidSHA, "First argument must be a 40 character SHA, actual value: %s\n", sha1);
}
// Construct download request message
// Format: "DLO|<40 character SHA>"
// Example: "DLO|920C34DCDDFC8F07AC4704C8C0D087D6F2095729"
char request[DLO_REQUEST_LENGTH+1];
if (snprintf(request, DLO_REQUEST_LENGTH+1, "DLO|%s\n", sha1) != DLO_REQUEST_LENGTH)
{
die(ReturnCode::InvalidSHA, "First argument must be a 40 character SHA, actual value: %s\n", sha1);
}
DWORD bytesWritten;
BOOL success = WriteFile(
pipeHandle, // pipe handle
message, // message
MESSAGE_LENGTH, // message length
&bytesWritten, // bytes written
NULL); // not overlapped
unsigned long bytesWritten;
int error = 0;
bool success = WriteToPipe(
pipeHandle,
request,
DLO_REQUEST_LENGTH,
&bytesWritten,
&error);
if (!success || bytesWritten != MESSAGE_LENGTH)
{
die(ReturnCode::PipeWriteFailed, "Failed to write to pipe (%d)\n", GetLastError());
}
if (!success || bytesWritten != DLO_REQUEST_LENGTH)
{
die(ReturnCode::PipeWriteFailed, "Failed to write to pipe (%d)\n", error);
}
char response[DLO_RESPONSE_LENGTH];
unsigned long totalBytesRead = 0;
error = 0;
do
{
unsigned long bytesRead = 0;
success = ReadFromPipe(
pipeHandle,
response + totalBytesRead,
sizeof(response) - (sizeof(char) * totalBytesRead),
&bytesRead,
&error);
totalBytesRead += bytesRead;
} while (success && totalBytesRead < DLO_RESPONSE_LENGTH);
if (!success)
{
die(ReturnCode::PipeReadFailed, "Read response from pipe failed (%d)\n", error);
}
DWORD bytesRead;
do
{
// Read from the pipe.
success = ReadFile(
pipeHandle, // pipe handle
message, // buffer to receive reply
sizeof(message), // size of buffer
&bytesRead, // number of bytes read
NULL); // not overlapped
if (!success && GetLastError() != ERROR_MORE_DATA)
{
break;
}
} while (!success); // repeat loop if ERROR_MORE_DATA
if (!success)
{
die(ReturnCode::PipeReadFailed, "Read response from pipe failed (%d)\n", GetLastError());
}
return *message == 'S' ? ReturnCode::Success : ReturnCode::FailureToDownload;
return *response == 'S' ? ReturnCode::Success : ReturnCode::FailureToDownload;
}
int main(int, char *argv[])
{
char packet_buffer[MAX_PACKET_LENGTH];
size_t len;
int err;
char packet_buffer[MAX_PACKET_LENGTH];
size_t len;
int err;
// set the mode to binary so we don't get CRLF translation
_setmode(_fileno(stdin), _O_BINARY);
_setmode(_fileno(stdout), _O_BINARY);
DisableCRLFTranslationOnStdPipes();
packet_txt_read(packet_buffer, sizeof(packet_buffer));
if (strcmp(packet_buffer, "git-read-object-client"))
{
die(ReadObjectHookErrorReturnCode::ErrorReadObjectProtocol, "Bad welcome message\n");
}
packet_txt_read(packet_buffer, sizeof(packet_buffer));
if (strcmp(packet_buffer, "git-read-object-client"))
{
die(ReadObjectHookErrorReturnCode::ErrorReadObjectProtocol, "Bad welcome message\n");
}
packet_txt_read(packet_buffer, sizeof(packet_buffer));
if (strcmp(packet_buffer, "version=1"))
{
die(ReadObjectHookErrorReturnCode::ErrorReadObjectProtocol, "Bad version\n");
}
packet_txt_read(packet_buffer, sizeof(packet_buffer));
if (strcmp(packet_buffer, "version=1"))
{
die(ReadObjectHookErrorReturnCode::ErrorReadObjectProtocol, "Bad version\n");
}
if (packet_txt_read(packet_buffer, sizeof(packet_buffer)))
{
die(ReadObjectHookErrorReturnCode::ErrorReadObjectProtocol, "Bad version end\n");
}
if (packet_txt_read(packet_buffer, sizeof(packet_buffer)))
{
die(ReadObjectHookErrorReturnCode::ErrorReadObjectProtocol, "Bad version end\n");
}
packet_txt_write("git-read-object-server");
packet_txt_write("version=1");
packet_flush();
packet_txt_write("git-read-object-server");
packet_txt_write("version=1");
packet_flush();
packet_txt_read(packet_buffer, sizeof(packet_buffer));
if (strcmp(packet_buffer, "capability=get"))
{
die(ReadObjectHookErrorReturnCode::ErrorReadObjectProtocol, "Bad capability\n");
}
packet_txt_read(packet_buffer, sizeof(packet_buffer));
if (strcmp(packet_buffer, "capability=get"))
{
die(ReadObjectHookErrorReturnCode::ErrorReadObjectProtocol, "Bad capability\n");
}
if (packet_txt_read(packet_buffer, sizeof(packet_buffer)))
{
die(ReadObjectHookErrorReturnCode::ErrorReadObjectProtocol, "Bad capability end\n");
}
if (packet_txt_read(packet_buffer, sizeof(packet_buffer)))
{
die(ReadObjectHookErrorReturnCode::ErrorReadObjectProtocol, "Bad capability end\n");
}
packet_txt_write("capability=get");
packet_flush();
packet_txt_write("capability=get");
packet_flush();
std::wstring pipeName(GetGVFSPipeName(argv[0]));
PATH_STRING pipeName(GetGVFSPipeName(argv[0]));
HANDLE pipeHandle = CreatePipeToGVFS(pipeName);
PIPE_HANDLE pipeHandle = CreatePipeToGVFS(pipeName);
while (1)
{
packet_txt_read(packet_buffer, sizeof(packet_buffer));
if (strcmp(packet_buffer, "command=get"))
{
die(ReadObjectHookErrorReturnCode::ErrorReadObjectProtocol, "Bad command\n");
}
while (1)
{
packet_txt_read(packet_buffer, sizeof(packet_buffer));
if (strcmp(packet_buffer, "command=get"))
{
die(ReadObjectHookErrorReturnCode::ErrorReadObjectProtocol, "Bad command\n");
}
len = packet_txt_read(packet_buffer, sizeof(packet_buffer));
if ((len != SHA1_LENGTH + 5) || strncmp(packet_buffer, "sha1=", 5))
{
die(ReadObjectHookErrorReturnCode::ErrorReadObjectProtocol, "Bad sha1 in get command\n");
}
len = packet_txt_read(packet_buffer, sizeof(packet_buffer));
if ((len != SHA1_LENGTH + 5) || strncmp(packet_buffer, "sha1=", 5))
{
die(ReadObjectHookErrorReturnCode::ErrorReadObjectProtocol, "Bad sha1 in get command\n");
}
if (packet_txt_read(packet_buffer, sizeof(packet_buffer)))
{
die(ReadObjectHookErrorReturnCode::ErrorReadObjectProtocol, "Bad command end\n");
}
if (packet_txt_read(packet_buffer, sizeof(packet_buffer)))
{
die(ReadObjectHookErrorReturnCode::ErrorReadObjectProtocol, "Bad command end\n");
}
err = DownloadSHA(pipeHandle, packet_buffer + 5);
packet_txt_write(err ? "status=error" : "status=success");
packet_flush();
}
err = DownloadSHA(pipeHandle, packet_buffer + 5);
packet_txt_write(err ? "status=error" : "status=success");
packet_flush();
}
// we'll never reach here as the signal to exit is having stdin closed which is handled in packet_bin_read
// we'll never reach here as the signal to exit is having stdin closed which is handled in packet_bin_read
}

Просмотреть файл

@ -4,14 +4,11 @@
//
#pragma once
#include "targetver.h"
#include <Windows.h>
#include <stdio.h>
#include <string>
#include <algorithm>
#include <locale>
#include <codecvt>
#include <fcntl.h>
#include <io.h>
#include <string.h>
#ifdef _WIN32
#include "targetver.h"
#include <Windows.h>
#endif
#include <string>
#include <stdarg.h>

Просмотреть файл

@ -10,8 +10,8 @@ namespace GVFS.Service
private Configuration()
{
this.GVFSLocation = Path.Combine(AssemblyPath, GVFSConstants.GVFSExecutableName);
this.GVFSServiceUILocation = Path.Combine(AssemblyPath, GVFSConstants.Service.UIName + GVFSConstants.ExecutableExtension);
this.GVFSLocation = Path.Combine(AssemblyPath, GVFSPlatform.Instance.Constants.GVFSExecutableName);
this.GVFSServiceUILocation = Path.Combine(AssemblyPath, GVFSConstants.Service.UIName + GVFSPlatform.Instance.Constants.ExecutableExtension);
}
public static Configuration Instance

Просмотреть файл

@ -71,7 +71,7 @@ namespace GVFS.Service.Handlers
{
WindowsGitInstallation windowsGitInstallation = new WindowsGitInstallation();
string gitBinPath = windowsGitInstallation.GetInstalledGitBinPath();
string hooksPath = ProcessHelper.WhereDirectory(GVFSConstants.GVFSHooksExecutableName);
string hooksPath = ProcessHelper.WhereDirectory(GVFSPlatform.Instance.Constants.GVFSHooksExecutableName);
GVFSEnlistment enlistment = null;
try

Просмотреть файл

@ -37,18 +37,16 @@
<FilesToSign Include="
$(BuildOutputDir)\GitHooksLoader\bin\$(Platform)\$(Configuration)\GitHooksLoader.exe;
$(BuildOutputDir)\FastFetch\bin\$(Platform)\$(Configuration)\FastFetch.exe;
$(BuildOutputDir)\GVFS.Common\bin\$(Platform)\$(Configuration)\netcoreapp2.0\GVFS.Common.dll;
$(BuildOutputDir)\GVFS.Common\bin\$(Platform)\$(Configuration)\netstandard2.0\GVFS.Common.dll;
$(BuildOutputDir)\GVFS.GVFlt\bin\$(Platform)\$(Configuration)\GVFS.GVFlt.dll;
$(BuildOutputDir)\GVFS.Hooks\bin\$(Platform)\$(Configuration)\GVFS.Hooks.exe;
$(BuildOutputDir)\GVFS.Hooks.Windows\bin\$(Platform)\$(Configuration)\GVFS.Hooks.exe;
$(BuildOutputDir)\GVFS.Mount.Windows\bin\$(Platform)\$(Configuration)\GVFS.Mount.exe;
$(BuildOutputDir)\GVFS.Platform.Windows\bin\$(Platform)\$(Configuration)\GVFS.Platform.Windows.dll;
$(BuildOutputDir)\GVFS.ReadObjectHook\bin\$(Platform)\$(Configuration)\GVFS.ReadObjectHook.exe;
$(BuildOutputDir)\GVFS.ReadObjectHook.Windows\bin\$(Platform)\$(Configuration)\GVFS.ReadObjectHook.exe;
$(BuildOutputDir)\GVFS.Service\bin\$(Platform)\$(Configuration)\GVFS.Service.exe;
$(BuildOutputDir)\GVFS.Service.UI\bin\$(Platform)\$(Configuration)\GVFS.Service.UI.exe;
$(BuildOutputDir)\GVFS.Virtualization\bin\$(Platform)\$(Configuration)\netcoreapp2.0\GVFS.Virtualization.dll;
$(BuildOutputDir)\GVFS.Virtualization\bin\$(Platform)\$(Configuration)\netstandard2.0\GVFS.Virtualization.dll;
$(BuildOutputDir)\GVFS.VirtualFileSystemHook\bin\$(Platform)\$(Configuration)\GVFS.VirtualFileSystemHook.exe;
$(BuildOutputDir)\GVFS.VirtualFileSystemHook.Windows\bin\$(Platform)\$(Configuration)\GVFS.VirtualFileSystemHook.exe;
$(BuildOutputDir)\GVFS.Windows\bin\$(Platform)\$(Configuration)\GVFS.exe;">
<Authenticode>Microsoft</Authenticode>
<InProject>false</InProject>

Просмотреть файл

@ -2,7 +2,7 @@
<Import Project="..\GVFS.Build\GVFS.cs.props" />
<PropertyGroup>
<TargetFrameworks>netcoreapp2.0;netstandard2.0</TargetFrameworks>
<TargetFrameworks>netcoreapp2.1;netstandard2.0</TargetFrameworks>
<Platforms>x64</Platforms>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">

Просмотреть файл

@ -9,12 +9,10 @@ namespace GVFS.Tests
public class NUnitRunner
{
private List<string> args;
private List<string> filters;
public NUnitRunner(string[] args)
{
this.args = new List<string>(args);
this.filters = new List<string>();
}
public string GetCustomArgWithParam(string arg)
@ -35,22 +33,13 @@ namespace GVFS.Tests
return this.args.Remove(arg);
}
public void ExcludeCategory(string category)
public int RunTests(ICollection<string> includeCategories, ICollection<string> excludeCategories)
{
this.filters.Add("cat!=" + category);
}
public void IncludeCategory(string category)
{
this.filters.Add("cat==" + category);
}
public int RunTests()
{
if (this.filters.Count > 0)
string filters = GetFiltersArgument(includeCategories, excludeCategories);
if (filters.Length > 0)
{
this.args.Add("--where");
this.args.Add(string.Join("&&", this.filters));
this.args.Add(filters);
}
DateTime now = DateTime.Now;
@ -61,5 +50,21 @@ namespace GVFS.Tests
return result;
}
private static string GetFiltersArgument(ICollection<string> includeCategories, ICollection<string> excludeCategories)
{
string filters = string.Empty;
if (includeCategories != null && includeCategories.Any())
{
filters = "(" + string.Join("||", includeCategories.Select(x => $"cat=={x}")) + ")";
}
if (excludeCategories != null && excludeCategories.Any())
{
filters += (filters.Length > 0 ? "&&" : string.Empty) + string.Join("&&", excludeCategories.Select(x => $"cat!={x}"));
}
return filters;
}
}
}

Просмотреть файл

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="GVFS.ProjFS" version="2018.628.1" targetFramework="net461" />
<package id="GVFS.ProjFS" version="2018.719.1" targetFramework="net461" />
<package id="Microsoft.Data.Sqlite" version="2.0.0" targetFramework="net461" />
<package id="Microsoft.Data.Sqlite.Core" version="2.0.0" targetFramework="net461" />
<package id="Newtonsoft.Json" version="11.0.2" targetFramework="net461" />

Просмотреть файл

@ -19,7 +19,10 @@ namespace GVFS.UnitTests.CommandLine
public void MergeHooksDataThrowsOnFoundGVFSHooks()
{
Assert.Throws<HooksInstaller.HooksConfigurationException>(
() => HooksInstaller.MergeHooksData(new string[] { "first", "gvfs.hooks.exe" }, Filename, GVFSConstants.DotGit.Hooks.PreCommandHookName));
() => HooksInstaller.MergeHooksData(
new string[] { "first", GVFSPlatform.Instance.Constants.GVFSHooksExecutableName },
Filename,
GVFSConstants.DotGit.Hooks.PreCommandHookName));
}
[TestCase]
@ -30,7 +33,7 @@ namespace GVFS.UnitTests.CommandLine
.Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries)
.Where(line => !line.StartsWith("#"));
resultLines.Single().ShouldEqual(GVFSConstants.GVFSHooksExecutableName);
resultLines.Single().ShouldEqual(GVFSPlatform.Instance.Constants.GVFSHooksExecutableName);
}
[TestCase]
@ -44,7 +47,7 @@ namespace GVFS.UnitTests.CommandLine
resultLines.Count().ShouldEqual(3);
resultLines.ElementAt(0).ShouldEqual("first");
resultLines.ElementAt(1).ShouldEqual("second");
resultLines.ElementAt(2).ShouldEqual(GVFSConstants.GVFSHooksExecutableName);
resultLines.ElementAt(2).ShouldEqual(GVFSPlatform.Instance.Constants.GVFSHooksExecutableName);
}
[TestCase]
@ -56,7 +59,7 @@ namespace GVFS.UnitTests.CommandLine
.Where(line => !line.StartsWith("#"));
resultLines.Count().ShouldEqual(3);
resultLines.ElementAt(0).ShouldEqual(GVFSConstants.GVFSHooksExecutableName);
resultLines.ElementAt(0).ShouldEqual(GVFSPlatform.Instance.Constants.GVFSHooksExecutableName);
resultLines.ElementAt(1).ShouldEqual("first");
resultLines.ElementAt(2).ShouldEqual("second");
}
@ -72,7 +75,7 @@ namespace GVFS.UnitTests.CommandLine
resultLines.Count().ShouldEqual(3);
resultLines.ElementAt(0).ShouldEqual("first");
resultLines.ElementAt(1).ShouldEqual("second");
resultLines.ElementAt(2).ShouldEqual(GVFSConstants.GVFSHooksExecutableName);
resultLines.ElementAt(2).ShouldEqual(GVFSPlatform.Instance.Constants.GVFSHooksExecutableName);
}
}
}

Просмотреть файл

@ -0,0 +1,35 @@
using GVFS.Common;
using GVFS.Tests.Should;
using NUnit.Framework;
using System.Runtime.InteropServices;
namespace GVFS.UnitTests.Common
{
[TestFixture]
public class PathsTests
{
[TestCase]
public void CanConvertOSPathToGitFormat()
{
string systemPath;
string expectedGitPath;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
systemPath = @"C:\This\is\a\path";
expectedGitPath = @"C:/This/is/a/path";
}
else
{
systemPath = @"/This/is/a/path";
expectedGitPath = systemPath;
}
string actualTransformedPath = Paths.ConvertPathToGitFormat(systemPath);
actualTransformedPath.ShouldEqual(expectedGitPath);
string doubleTransformedPath = Paths.ConvertPathToGitFormat(actualTransformedPath);
doubleTransformedPath.ShouldEqual(expectedGitPath);
}
}
}

Просмотреть файл

@ -2,7 +2,7 @@
<Import Project="..\GVFS.Build\GVFS.cs.props" />
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<TargetFramework>netcoreapp2.1</TargetFramework>
<Platforms>x64</Platforms>
</PropertyGroup>

Просмотреть файл

@ -12,6 +12,11 @@ namespace GVFS.UnitTests.Mock.Common
{
public class MockPlatform : GVFSPlatform
{
public MockPlatform()
: base(executableExtension: ".mockexe")
{
}
public override IKernelDriver KernelDriver => throw new NotSupportedException();
public override IGitInstallation GitInstallation => throw new NotSupportedException();
@ -30,6 +35,16 @@ namespace GVFS.UnitTests.Mock.Common
throw new NotSupportedException();
}
public override bool TryInstallGitCommandHooks(GVFSContext context, string executingDirectory, string hookName, string commandHookPath, out string errorMessage)
{
throw new NotSupportedException();
}
public override string GetNamedPipeName(string enlistmentRoot)
{
return "GVFS_Mock_PipeName";
}
public override NamedPipeServerStream CreatePipeByName(string pipeName)
{
throw new NotSupportedException();
@ -70,6 +85,11 @@ namespace GVFS.UnitTests.Mock.Common
throw new NotSupportedException();
}
public override bool IsProcessActive(int processId)
{
throw new NotSupportedException();
}
public override bool TryGetGVFSEnlistmentRoot(string directory, out string enlistmentRoot, out string errorMessage)
{
throw new NotSupportedException();

Просмотреть файл

@ -173,6 +173,12 @@ namespace GVFS.UnitTests.Mock.FileSystem
public MockDirectory CreateDirectory(string path)
{
int lastSlashIdx = path.LastIndexOf(Path.DirectorySeparatorChar);
if (lastSlashIdx <= 0)
{
return this;
}
string parentPath = path.Substring(0, lastSlashIdx);
MockDirectory parentDirectory = this.FindDirectory(parentPath);
if (parentDirectory == null)

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше