Skip to content

Commit 0a145bc

Browse files
committed
Fix config discovery under Native AOT (GitBuf marshalling)
Under Native AOT, sequential-layout class marshalling for GitBuf is effectively [In]-only, so git_config_find_* succeeds but managed ptr stays zero and global/system config never loads. BuildSignature then returns null when user.name/email live outside local repo config. Use a blittable GitBufNative struct with ref P/Invoke for path-find APIs and ConvertPath so config discovery works in AOT publishes. Fixes #2187
1 parent cb58177 commit 0a145bc

3 files changed

Lines changed: 49 additions & 9 deletions

File tree

LibGit2Sharp/Core/GitBuf.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,16 @@
33

44
namespace LibGit2Sharp.Core.Handles
55
{
6+
/// <summary>
7+
/// Managed mirror of libgit2's <c>git_buf</c> for APIs that still pass a
8+
/// sequential-layout class through the runtime marshaller.
9+
/// </summary>
10+
/// <remarks>
11+
/// Prefer <see cref="GitBufNative"/> for new or Native AOT-sensitive call
12+
/// sites. Under Native AOT, class-based sequential marshalling is effectively
13+
/// [In]-only, so native writes to <c>ptr</c>/<c>size</c> never appear in
14+
/// managed code.
15+
/// </remarks>
616
[StructLayout(LayoutKind.Sequential)]
717
internal class GitBuf : IDisposable
818
{
@@ -15,4 +25,17 @@ public void Dispose()
1525
Proxy.git_buf_dispose(this);
1626
}
1727
}
28+
29+
/// <summary>
30+
/// Blittable <c>git_buf</c> for P/Invoke via <c>ref</c>. Required for Native AOT
31+
/// so that libgit2 can write <c>ptr</c>/<c>asize</c>/<c>size</c> back to managed
32+
/// memory (class marshalling does not).
33+
/// </summary>
34+
[StructLayout(LayoutKind.Sequential)]
35+
internal struct GitBufNative
36+
{
37+
public IntPtr ptr;
38+
public UIntPtr asize;
39+
public UIntPtr size;
40+
}
1841
}

LibGit2Sharp/Core/NativeMethods.cs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,9 @@ internal static extern unsafe int git_branch_upstream_name(
360360
[DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)]
361361
internal static extern void git_buf_dispose(GitBuf buf);
362362

363+
[DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)]
364+
internal static extern void git_buf_dispose(ref GitBufNative buf);
365+
363366
[DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)]
364367
internal static extern unsafe int git_checkout_tree(
365368
git_repository* repo,
@@ -468,17 +471,19 @@ internal static extern unsafe int git_config_set_multivar(
468471
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string regexp,
469472
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string value);
470473

474+
// Use ref GitBufNative (not class GitBuf): under Native AOT, sequential
475+
// class marshalling does not write ptr/size back to the managed object.
471476
[DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)]
472-
internal static extern int git_config_find_global(GitBuf global_config_path);
477+
internal static extern int git_config_find_global(ref GitBufNative global_config_path);
473478

474479
[DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)]
475-
internal static extern int git_config_find_system(GitBuf system_config_path);
480+
internal static extern int git_config_find_system(ref GitBufNative system_config_path);
476481

477482
[DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)]
478-
internal static extern int git_config_find_xdg(GitBuf xdg_config_path);
483+
internal static extern int git_config_find_xdg(ref GitBufNative xdg_config_path);
479484

480485
[DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)]
481-
internal static extern int git_config_find_programdata(GitBuf programdata_config_path);
486+
internal static extern int git_config_find_programdata(ref GitBufNative programdata_config_path);
482487

483488
[DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)]
484489
internal static extern unsafe void git_config_free(git_config* cfg);
@@ -1522,7 +1527,7 @@ IntPtr data
15221527

15231528
[DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)]
15241529
internal static extern int git_repository_discover(
1525-
GitBuf buf,
1530+
ref GitBufNative buf,
15261531
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictFilePathMarshaler))] FilePath start_path,
15271532
[MarshalAs(UnmanagedType.Bool)] bool across_fs,
15281533
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictFilePathMarshaler))] FilePath ceiling_dirs);

LibGit2Sharp/Core/Proxy.cs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2447,7 +2447,7 @@ public static unsafe string git_remote_pushurl(RemoteHandle remote)
24472447

24482448
public static FilePath git_repository_discover(FilePath start_path)
24492449
{
2450-
return ConvertPath(buf => NativeMethods.git_repository_discover(buf, start_path, false, null));
2450+
return ConvertPath((ref GitBufNative buf) => NativeMethods.git_repository_discover(ref buf, start_path, false, null));
24512451
}
24522452

24532453
public static unsafe bool git_repository_head_detached(RepositoryHandle repo)
@@ -3830,11 +3830,19 @@ private static unsafe bool RepositoryStateChecker(RepositoryHandle repo, Func<In
38303830
return (res == 1);
38313831
}
38323832

3833-
private static FilePath ConvertPath(Func<GitBuf, int> pathRetriever)
3833+
private delegate int PathRetriever(ref GitBufNative buf);
3834+
3835+
/// <summary>
3836+
/// Invokes a native path-returning API using a blittable <see cref="GitBufNative"/>.
3837+
/// Must not use class <see cref="GitBuf"/>: Native AOT marshalling does not
3838+
/// copy native fills of ptr/size back onto sequential-layout classes.
3839+
/// </summary>
3840+
private static FilePath ConvertPath(PathRetriever pathRetriever)
38343841
{
3835-
using (var buf = new GitBuf())
3842+
var buf = new GitBufNative();
3843+
try
38363844
{
3837-
int result = pathRetriever(buf);
3845+
int result = pathRetriever(ref buf);
38383846

38393847
if (result == (int)GitErrorCode.NotFound)
38403848
{
@@ -3844,6 +3852,10 @@ private static FilePath ConvertPath(Func<GitBuf, int> pathRetriever)
38443852
Ensure.ZeroResult(result);
38453853
return LaxFilePathMarshaler.FromNative(buf.ptr);
38463854
}
3855+
finally
3856+
{
3857+
NativeMethods.git_buf_dispose(ref buf);
3858+
}
38473859
}
38483860

38493861
private static readonly IDictionary<Type, Func<string, object>> configurationParser = new Dictionary<Type, Func<string, object>>

0 commit comments

Comments
 (0)