Skip to content

Commit

Permalink
Merge branch 'feature/11' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
kekyo committed Nov 7, 2023
2 parents 8769fa2 + d199c32 commit 40aad98
Show file tree
Hide file tree
Showing 20 changed files with 571 additions and 113 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
{
Hash: {
HashCode: 1205dc34ce48bda28fc543daaf9525a9bb6e6d10
},
Children: [
{
Name: .github,
Hash: {
HashCode: 8efbe3e1a885781cd4af635fc36c8f7c3e4a61b4
},
Children: [
{
Name: workflows,
Hash: {
HashCode: 9f13238c667645041aa83159e66e125afd0ff3ce
},
Children: [
{
Name: build.yml,
Expand All @@ -11,17 +22,9 @@
HashCode: 9e1cb4c6ea6b897bbb34488312365c8b3135b172
}
}
],
Name: workflows,
Hash: {
HashCode: 9f13238c667645041aa83159e66e125afd0ff3ce
}
]
}
],
Name: .github,
Hash: {
HashCode: 8efbe3e1a885781cd4af635fc36c8f7c3e4a61b4
}
]
},
{
Name: .gitignore,
Expand All @@ -38,6 +41,10 @@
}
},
{
Name: CenterCLR.NamingFormatter,
Hash: {
HashCode: deeac68f4a9e88e0e9c01c83deee1aceb3a05cdb
},
Children: [
{
Name: CenterCLR.NamingFormatter.csproj,
Expand Down Expand Up @@ -82,6 +89,10 @@
}
},
{
Name: Internal,
Hash: {
HashCode: 01d6075020c7c7b5741b7c5f3649420d11d01e88
},
Children: [
{
Name: ValueTuple.cs,
Expand All @@ -90,11 +101,7 @@
HashCode: 0a6b12f8df4cd69e1b12ec969c8cd7e70589f0c8
}
}
],
Name: Internal,
Hash: {
HashCode: 01d6075020c7c7b5741b7c5f3649420d11d01e88
}
]
},
{
Name: Named.cs,
Expand All @@ -117,13 +124,13 @@
HashCode: 62afd008687eca2557ba3aeb4e79c3c14c9ac2af
}
}
],
Name: CenterCLR.NamingFormatter,
Hash: {
HashCode: deeac68f4a9e88e0e9c01c83deee1aceb3a05cdb
}
]
},
{
Name: CenterCLR.NamingFormatterTests,
Hash: {
HashCode: c53d90ac78df69109f6bac85e7496ad52fcde4a7
},
Children: [
{
Name: CenterCLR.NamingFormatterTests.csproj,
Expand Down Expand Up @@ -160,13 +167,13 @@
HashCode: ab60b99121ba537ebeb3327a5d9ed896696c917c
}
}
],
Name: CenterCLR.NamingFormatterTests,
Hash: {
HashCode: c53d90ac78df69109f6bac85e7496ad52fcde4a7
}
]
},
{
Name: Images,
Hash: {
HashCode: d4cb9825ee137b53148b9776a20ba093debb8ef5
},
Children: [
{
Name: CenterCLR.NamingFormatter.100.png,
Expand Down Expand Up @@ -196,11 +203,7 @@
HashCode: d9a13dc7f3de1f0a7aa8e4e2b0d3f00e77bb8e3a
}
}
],
Name: Images,
Hash: {
HashCode: d4cb9825ee137b53148b9776a20ba093debb8ef5
}
]
},
{
Name: README.md,
Expand All @@ -216,8 +219,5 @@
HashCode: 0dc4f6eba2a37054088754456f63dd7b34b24753
}
}
],
Hash: {
HashCode: 1205dc34ce48bda28fc543daaf9525a9bb6e6d10
}
]
}
5 changes: 5 additions & 0 deletions FSharp.GitReader/Structures/RepositoryExtension.fs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ module public RepositoryExtension =
StructuredRepositoryFacade.OpenBlobAsync(
entry, unwrapCT ct) |> Async.AwaitTask

type TreeSubModuleEntry with
member entry.openSubModule(?ct: CancellationToken) =
StructuredRepositoryFacade.OpenSubModuleAsync(
entry, unwrapCT ct) |> Async.AwaitTask

let (|StructuredRepository|) (repository: StructuredRepository) =
(repository.GitPath,
repository.RemoteUrls,
Expand Down
69 changes: 68 additions & 1 deletion GitReader.Core/Internal/RepositoryAccessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ private static async ValueTask<Stream> OpenFileAsync(
string path, CancellationToken ct)
#else
private static async Task<Stream> OpenFileAsync(
string path, CancellationToken ct)
string path, CancellationToken ct)
#endif
{
// Many Git clients are supposed to be OK to use at the same time.
Expand Down Expand Up @@ -132,6 +132,73 @@ private static async Task<Stream> OpenFileAsync(
return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 65536, true);
}

#if NET45_OR_GREATER || NETSTANDARD || NETCOREAPP
public static async ValueTask<string> DetectLocalRepositoryPathAsync(
string startPath, CancellationToken ct)
#else
public static async Task<string> DetectLocalRepositoryPathAsync(
string startPath, CancellationToken ct)
#endif
{
var currentPath = Path.GetFullPath(startPath);

while (true)
{
ct.ThrowIfCancellationRequested();

var candidatePath = Path.GetFileName(currentPath) != ".git" ?
Utilities.Combine(currentPath, ".git") : currentPath;

if (Directory.Exists(candidatePath) &&
File.Exists(Path.Combine(candidatePath, "config")))
{
return candidatePath;
}

// Issue #11
if (File.Exists(candidatePath))
{
using var fs = await OpenFileAsync(candidatePath, ct);
var tr = new AsyncTextReader(fs);

while (true)
{
var line = await tr.ReadLineAsync(ct);
if (line == null)
{
break;
}

line = line.Trim();
if (line.StartsWith("gitdir:"))
{
// Resolve to full path (And normalize path directory separators)
var gitDirPath = line.Substring(7).TrimStart();
candidatePath = Utilities.ResolveRelativePath(currentPath, gitDirPath);

if (Directory.Exists(candidatePath) &&
File.Exists(Path.Combine(candidatePath, "config")))
{
return candidatePath;
}

break;
}
}

throw new ArgumentException(
"Repository does not exist. `.git` file exists. But failed to `gitdir` or specified directory is not exists.");
}

if (Path.GetPathRoot(currentPath) == currentPath)
{
throw new ArgumentException("Repository does not exist.");
}

currentPath = Utilities.GetDirectoryPath(currentPath);
}
}

public static string GetReferenceTypeName(ReferenceTypes type) =>
type switch
{
Expand Down
36 changes: 36 additions & 0 deletions GitReader.Core/Internal/Utilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,18 @@ internal static class Utilities
private const long UnixEpochTicks = DaysTo1970 * TicksPerDay;
private const long UnixEpochSeconds = UnixEpochTicks / TimeSpan.TicksPerSecond;

private static readonly bool isWindows =
#if NETSTANDARD1_6
!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("HOMEDRIVE"));
#else
Environment.OSVersion.Platform.ToString().Contains("Win");
#endif

private static readonly string homePath =
Path.GetFullPath(isWindows ?
$"{Environment.GetEnvironmentVariable("HOMEDRIVE") ?? "C:"}{Environment.GetEnvironmentVariable("HOMEPATH") ?? "\\"}" :
(Environment.GetEnvironmentVariable("HOME") ?? "/"));

public static DateTimeOffset FromUnixTimeSeconds(long seconds, TimeSpan offset)
{
var ticks = seconds * TimeSpan.TicksPerSecond + UnixEpochTicks;
Expand Down Expand Up @@ -297,6 +309,23 @@ public static IEnumerable<U> CollectValue<T, U>(
}
}

[DebuggerStepThrough]
public static IEnumerable<T> Traverse<T>(
this T value,
Func<T, T?> selector)
where T : class
{
while (true)
{
yield return value;
if (selector(value) is not { } selected)
{
break;
}
value = selected;
}
}

#if !NET6_0_OR_GREATER
private sealed class DistinctKeyComparer<T, TKey> : IEqualityComparer<T>
{
Expand Down Expand Up @@ -675,4 +704,11 @@ public static string ToGitAuthorString(
Signature signature) =>
signature.MailAddress is { } mailAddress ?
$"{signature.Name} <{mailAddress}>" : signature.Name;

public static string ResolveRelativePath(string basePath, string path) =>
Path.GetFullPath(Path.IsPathRooted(path) ?
path :
path.StartsWith("~/") ?
Combine(homePath, path.Substring(2)) :
Combine(basePath, path));
}
44 changes: 34 additions & 10 deletions GitReader.Core/Primitive/PrimitiveRepositoryFacade.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,9 @@ namespace GitReader.Primitive;

internal static class PrimitiveRepositoryFacade
{
public static async Task<PrimitiveRepository> OpenPrimitiveAsync(
string path, CancellationToken ct)
private static async Task<PrimitiveRepository> InternalOpenPrimitiveAsync(
string repositoryPath, CancellationToken ct)
{
var repositoryPath = Path.GetFileName(path) != ".git" ?
Utilities.Combine(path, ".git") : path;

if (!Directory.Exists(repositoryPath))
{
throw new ArgumentException("Repository does not exist.");
}

var repository = new PrimitiveRepository(repositoryPath);

try
Expand All @@ -52,6 +44,13 @@ public static async Task<PrimitiveRepository> OpenPrimitiveAsync(
}
}

public static async Task<PrimitiveRepository> OpenPrimitiveAsync(
string path, CancellationToken ct)
{
var repositoryPath = await RepositoryAccessor.DetectLocalRepositoryPathAsync(path, ct);
return await InternalOpenPrimitiveAsync(repositoryPath, ct);
}

//////////////////////////////////////////////////////////////////////////

public static async Task<PrimitiveReference?> GetCurrentHeadReferenceAsync(
Expand Down Expand Up @@ -94,4 +93,29 @@ public static async Task<PrimitiveTagReference> GetTagReferenceAsync(
new PrimitiveTagReference(tagName, relativePathOrLocation, r.Hash, null) :
throw new ArgumentException($"Could not find a tag: {tagName}");
}

public static Task<PrimitiveRepository> OpenSubModuleAsync(
Repository repository,
PrimitiveTreeEntry[] treePath, CancellationToken ct)
{
if (treePath.Length == 0)
{
throw new ArgumentException("Could not empty tree path.");
}
if (treePath[treePath.Length - 1].SpecialModes != PrimitiveSpecialModes.SubModule)
{
throw new ArgumentException($"Could not use non-submodule entry: {treePath[treePath.Length - 1]}");
}

var repositoryPath = Utilities.Combine(
repository.GitPath, "modules",
Utilities.Combine(treePath.Select(tree => tree.Name).ToArray()));

if (!Directory.Exists(repositoryPath))
{
throw new ArgumentException("Submodule repository does not exist.");
}

return InternalOpenPrimitiveAsync(repositoryPath, ct);
}
}
1 change: 1 addition & 0 deletions GitReader.Core/Primitive/PrimitiveTree.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ public PrimitiveTreeEntry(
PrimitiveModeFlags.Blob => PrimitiveSpecialModes.Blob,
PrimitiveModeFlags.SubModule => PrimitiveSpecialModes.SubModule,
PrimitiveModeFlags.Tree => PrimitiveSpecialModes.Tree,
(PrimitiveModeFlags.Directory | PrimitiveModeFlags.Tree) => PrimitiveSpecialModes.SubModule,
_ => PrimitiveSpecialModes.Unknown,
};

Expand Down
Loading

0 comments on commit 40aad98

Please sign in to comment.