Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions LibGit2Sharp.Tests/GlobalSettingsFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ public void LoadFromSpecifiedPath(string architecture)
{
Skip.IfNot(Platform.IsRunningOnNetFramework(), ".NET Framework only test.");

var nativeDllFileName = NativeDllName.Name + ".dll";
var testDir = Path.GetDirectoryName(typeof(GlobalSettingsFixture).Assembly.Location);
var testAppExe = Path.Combine(testDir, $"NativeLibraryLoadTestApp.{architecture}.exe");
var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
Expand All @@ -71,12 +70,14 @@ public void LoadFromSpecifiedPath(string architecture)
try
{
Directory.CreateDirectory(platformDir);
File.Copy(Path.Combine(libraryPath, nativeDllFileName), Path.Combine(platformDir, nativeDllFileName));
foreach (var file in Directory.GetFiles(libraryPath, "*.dll"))
{
File.Copy(file, Path.Combine(platformDir, Path.GetFileName(file)));
}

var (output, exitCode) = ProcessHelper.RunProcess(testAppExe, arguments: $@"{NativeDllName.Name} ""{platformDir}""", workingDirectory: tempDir);

Assert.Empty(output);
Assert.Equal(0, exitCode);
Assert.True(exitCode == 0, $"Test app exited with code {exitCode}. Output: {output}");
}
finally
{
Expand Down
128 changes: 127 additions & 1 deletion LibGit2Sharp.Tests/NetworkFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ public void CanListRemoteReferences(string url)
Remote remote = repo.Network.Remotes.Add(remoteName, url);
IList<Reference> references = repo.Network.ListReferences(remote).ToList();


foreach (var reference in references)
{
// None of those references point to an existing
Expand Down Expand Up @@ -136,6 +135,133 @@ public void CanListRemoteReferencesWithCredentials()
}
}

[Theory]
[InlineData("http://github.com/libgit2/TestGitRepository")]
[InlineData("https://github.com/libgit2/TestGitRepository")]
public void CanListRemoteReferencesWithListRemoteOptions(string url)
{
string remoteName = "testRemote";

string repoPath = InitNewRepository();

using (var repo = new Repository(repoPath))
{
Remote remote = repo.Network.Remotes.Add(remoteName, url);
var options = new ListRemoteOptions
{
ProxyOptions = new ProxyOptions()
};

IList<Reference> references = repo.Network.ListReferences(remote, options).ToList();

foreach (var reference in references)
{
Assert.Null(reference.ResolveToDirectReference().Target);
}

List<Tuple<string, string>> actualRefs = references.
Select(directRef => new Tuple<string, string>(directRef.CanonicalName, directRef.ResolveToDirectReference()
.TargetIdentifier)).ToList();

Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs.Count, actualRefs.Count);
Assert.True(references.Single(reference => reference.CanonicalName == "HEAD") is SymbolicReference);
for (int i = 0; i < TestRemoteRefs.ExpectedRemoteRefs.Count; i++)
{
Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs[i].Item2, actualRefs[i].Item2);
Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs[i].Item1, actualRefs[i].Item1);
}
}
}

[Theory]
[InlineData("http://github.com/libgit2/TestGitRepository")]
[InlineData("https://github.com/libgit2/TestGitRepository")]
public void CanListRemoteReferencesFromUrlWithListRemoteOptions(string url)
{
string repoPath = InitNewRepository();

using (var repo = new Repository(repoPath))
{
var options = new ListRemoteOptions
{
ProxyOptions = new ProxyOptions()
};

IList<Reference> references = repo.Network.ListReferences(url, options).ToList();

foreach (var reference in references)
{
Assert.Null(reference.ResolveToDirectReference().Target);
}

List<Tuple<string, string>> actualRefs = references.
Select(directRef => new Tuple<string, string>(directRef.CanonicalName, directRef.ResolveToDirectReference()
.TargetIdentifier)).ToList();

Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs.Count, actualRefs.Count);
Assert.True(references.Single(reference => reference.CanonicalName == "HEAD") is SymbolicReference);
for (int i = 0; i < TestRemoteRefs.ExpectedRemoteRefs.Count; i++)
{
Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs[i].Item2, actualRefs[i].Item2);
Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs[i].Item1, actualRefs[i].Item1);
}
}
}

[Theory]
[InlineData("https://github.com/libgit2/TestGitRepository")]
public void CanListRemoteReferencesWithCertificateCheckCallback(string url)
{
string repoPath = InitNewRepository();

bool certificateCheckCalled = false;

using (var repo = new Repository(repoPath))
{
var options = new ListRemoteOptions
{
CertificateCheck = (cert, valid, host) =>
{
certificateCheckCalled = true;
return true;
}
};

IList<Reference> references = repo.Network.ListReferences(url, options).ToList();

Assert.True(certificateCheckCalled);
Assert.NotEmpty(references);
}
}

[SkippableFact]
public void CanListRemoteReferencesWithCredentialsInListRemoteOptions()
{
InconclusiveIf(() => string.IsNullOrEmpty(Constants.PrivateRepoUrl),
"Populate Constants.PrivateRepo* to run this test");

string remoteName = "origin";

string repoPath = InitNewRepository();

using (var repo = new Repository(repoPath))
{
Remote remote = repo.Network.Remotes.Add(remoteName, Constants.PrivateRepoUrl);

var options = new ListRemoteOptions
{
CredentialsProvider = Constants.PrivateRepoCredentials
};

var references = repo.Network.ListReferences(remote, options);

foreach (var reference in references)
{
Assert.NotNull(reference);
}
}
}

[Theory]
[InlineData(FastForwardStrategy.Default)]
[InlineData(FastForwardStrategy.NoFastForward)]
Expand Down
12 changes: 12 additions & 0 deletions LibGit2Sharp.Tests/PushFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,18 @@ public void CanInvokePrePushCallbackAndFail()
Assert.True(prePushHandlerCalled);
}

[Fact]
public void CanPushWithRemoteProgressCallback()
{
PushOptions options = new PushOptions()
{
OnPushStatusError = OnPushStatusError,
OnPushRemoteProgress = (progress) => { return true; },
};

AssertPush(repo => repo.Network.Push(repo.Network.Remotes["origin"], "HEAD", @"refs/heads/master", options));
}

[Fact]
public void PushingABranchThatDoesNotTrackAnUpstreamBranchThrows()
{
Expand Down
24 changes: 24 additions & 0 deletions LibGit2Sharp.Tests/RepositoryFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -724,6 +724,30 @@ public void CanListRemoteReferences(string url)
}
}

[Theory]
[InlineData("http://github.com/libgit2/TestGitRepository")]
[InlineData("https://github.com/libgit2/TestGitRepository")]
public void CanListRemoteReferencesWithListRemoteOptions(string url)
{
var options = new ListRemoteOptions
{
ProxyOptions = new ProxyOptions()
};

IEnumerable<Reference> references = Repository.ListRemoteReferences(url, options).ToList();

List<Tuple<string, string>> actualRefs = references.
Select(reference => new Tuple<string, string>(reference.CanonicalName, reference.ResolveToDirectReference().TargetIdentifier)).ToList();

Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs.Count, actualRefs.Count);
Assert.True(references.Single(reference => reference.CanonicalName == "HEAD") is SymbolicReference);
for (int i = 0; i < TestRemoteRefs.ExpectedRemoteRefs.Count; i++)
{
Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs[i].Item2, actualRefs[i].Item2);
Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs[i].Item1, actualRefs[i].Item1);
}
}

[Fact]
public void CanListRemoteReferencesWithDetachedRemoteHead()
{
Expand Down
29 changes: 29 additions & 0 deletions LibGit2Sharp/CertificateSsh.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ protected CertificateSsh()
/// </summary>
public readonly byte[] HashSHA1;

/// <summary>
/// The SHA256 hash of the host. Meaningful if <see cref="HasSHA256"/> is true
/// </summary>
public readonly byte[] HashSHA256;

/// <summary>
/// True if we have the MD5 hostkey hash from the server
/// </summary>
Expand All @@ -35,11 +40,17 @@ protected CertificateSsh()
/// </summary>
public readonly bool HasSHA1;

/// <summary>
/// True if we have the SHA256 hostkey hash from the server
/// </summary>
public readonly bool HasSHA256;

internal unsafe CertificateSsh(git_certificate_ssh* cert)
{

HasMD5 = cert->type.HasFlag(GitCertificateSshType.MD5);
HasSHA1 = cert->type.HasFlag(GitCertificateSshType.SHA1);
HasSHA256 = cert->type.HasFlag(GitCertificateSshType.SHA256);

HashMD5 = new byte[16];
for (var i = 0; i < HashMD5.Length; i++)
Expand All @@ -52,6 +63,12 @@ internal unsafe CertificateSsh(git_certificate_ssh* cert)
{
HashSHA1[i] = cert->HashSHA1[i];
}

HashSHA256 = new byte[32];
for (var i = 0; i < HashSHA256.Length; i++)
{
HashSHA256[i] = cert->HashSHA256[i];
}
}

internal unsafe IntPtr ToPointer()
Expand All @@ -65,6 +82,10 @@ internal unsafe IntPtr ToPointer()
{
sshCertType |= GitCertificateSshType.SHA1;
}
if (HasSHA256)
{
sshCertType |= GitCertificateSshType.SHA256;
}

var gitCert = new git_certificate_ssh()
{
Expand All @@ -88,6 +109,14 @@ internal unsafe IntPtr ToPointer()
}
}

fixed (byte* p = &HashSHA256[0])
{
for (var i = 0; i < HashSHA256.Length; i++)
{
gitCert.HashSHA256[i] = p[i];
}
}

var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(gitCert));
Marshal.StructureToPtr(gitCert, ptr, false);

Expand Down
20 changes: 18 additions & 2 deletions LibGit2Sharp/Core/NativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,24 @@ private static IntPtr ResolveDll(string libraryName, Assembly assembly, DllImpor
[DllImport("libdl", EntryPoint = "dlopen")]
private static extern IntPtr LoadUnixLibrary(string path, int flags);

[DllImport("kernel32", EntryPoint = "LoadLibrary")]
private static extern IntPtr LoadWindowsLibrary(string path);
[DllImport("kernel32", EntryPoint = "AddDllDirectory", CharSet = CharSet.Unicode)]
private static extern IntPtr AddDllDirectory(string path);

[DllImport("kernel32", EntryPoint = "LoadLibraryExW", CharSet = CharSet.Unicode)]
private static extern IntPtr LoadWindowsLibraryEx(string path, IntPtr hFile, uint flags);

private const uint LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x00001000;

private static IntPtr LoadWindowsLibrary(string path)
{
var directory = Path.GetDirectoryName(path);
if (directory != null)
{
AddDllDirectory(directory);
}

return LoadWindowsLibraryEx(path, IntPtr.Zero, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
}

// Avoid inlining this method because otherwise mono's JITter may try
// to load the library _before_ we've configured the path.
Expand Down
27 changes: 27 additions & 0 deletions LibGit2Sharp/Core/SshExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using LibGit2Sharp.Core;
using System;
using System.Runtime.InteropServices;

namespace LibGit2Sharp.Ssh
{
internal static class NativeMethods
{
private const string libgit2 = NativeDllName.Name;

[DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)]
internal static extern int git_cred_ssh_key_new(
out IntPtr cred,
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string username,
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string publickey,
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string privatekey,
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string passphrase);

[DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)]
internal static extern int git_cred_ssh_key_memory_new(
out IntPtr cred,
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string username,
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string publickey,
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string privatekey,
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string passphrase);
}
}
2 changes: 1 addition & 1 deletion LibGit2Sharp/LibGit2Sharp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Octopus.LibGit2Sharp.NativeBinaries" Version="2.0.323-octopus.1" PrivateAssets="none" />
<PackageReference Include="Octopus.LibGit2Sharp.NativeBinaries" Version="2.0.323-octopus.2" PrivateAssets="none" />
<PackageReference Include="MinVer" Version="6.0.0" PrivateAssets="all" />
</ItemGroup>

Expand Down
26 changes: 26 additions & 0 deletions LibGit2Sharp/ListRemoteOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using LibGit2Sharp.Handlers;

namespace LibGit2Sharp;

/// <summary>
/// Options controlling ListRemote behavior.
/// </summary>
public sealed class ListRemoteOptions
{
/// <summary>
/// Handler to generate <see cref="LibGit2Sharp.Credentials"/> for authentication.
/// </summary>
public CredentialsHandler CredentialsProvider { get; set; }

/// <summary>
/// This handler will be called to let the user make a decision on whether to allow
/// the connection to proceed based on the certificate presented by the server.
/// </summary>
public CertificateCheckHandler CertificateCheck { get; set; }


/// <summary>
/// Options for connecting through a proxy.
/// </summary>
public ProxyOptions ProxyOptions { get; set; } = new();
}
Loading