11using AwesomeAssertions ;
22using System ;
3+ using System . Collections . Generic ;
34using System . Diagnostics ;
45using System . IO ;
6+ using System . Linq ;
57using System . Security . Cryptography ;
68using Xunit ;
79
@@ -327,6 +329,128 @@ public void Load_tree_contents_from_head()
327329 }
328330 }
329331
332+ [ Fact ]
333+ public void Load_subdirectory_contents_from_known_commit ( )
334+ {
335+ var gitDir = _fixture . GitDirectory ;
336+
337+ // Use commit 0166a832097feb94bd565354b31559ccb355e0be ("parse commit properties and add API for reading commits") on main
338+ var commitSha = "0166a832097feb94bd565354b31559ccb355e0be" ;
339+
340+ var subdirContents =
341+ LoadFromLocalFiles . LoadSubdirectoryContentsFromCommit (
342+ gitDir ,
343+ commitSha ,
344+ [ "implement" , "GitCore" ] ) ;
345+
346+ subdirContents . Should ( ) . NotBeNull ( "Subdirectory contents should be loaded" ) ;
347+ subdirContents . Count . Should ( ) . Be ( 9 , "implement/GitCore should contain 9 files at this commit" ) ;
348+
349+ // Verify well-known files exist with paths relative to the subdirectory
350+ subdirContents . Should ( ) . ContainKey ( [ "GitCore.csproj" ] ) ;
351+ subdirContents . Should ( ) . ContainKey ( [ "README.md" ] ) ;
352+ subdirContents . Should ( ) . ContainKey ( [ "Repository.cs" ] ) ;
353+ subdirContents . Should ( ) . ContainKey ( [ "GitObjects.cs" ] ) ;
354+ subdirContents . Should ( ) . ContainKey ( [ "LoadFromUrl.cs" ] ) ;
355+ subdirContents . Should ( ) . ContainKey ( [ "PackFile.cs" ] ) ;
356+ subdirContents . Should ( ) . ContainKey ( [ "PackIndex.cs" ] ) ;
357+ subdirContents . Should ( ) . ContainKey ( [ "GitSmartHttp.cs" ] ) ;
358+ subdirContents . Should ( ) . ContainKey ( [ "Common" , "EnumerableExtensions.cs" ] ) ;
359+
360+ // Verify SHA256 hashes of file contents
361+ ComputeSha256Hex ( subdirContents [ [ "GitCore.csproj" ] ] ) . Should ( ) . Be (
362+ "892f9343f5165b461aef51aefd841a78eb1faa73a1ef678d2eadeb0f6dbe2906" ,
363+ "SHA256 of GitCore.csproj should match" ) ;
364+
365+ ComputeSha256Hex ( subdirContents [ [ "README.md" ] ] ) . Should ( ) . Be (
366+ "141f37bb8117bbb98551399270e5b12a0da4767e27bfa63366b72c668b1ccb62" ,
367+ "SHA256 of README.md should match" ) ;
368+
369+ ComputeSha256Hex ( subdirContents [ [ "Repository.cs" ] ] ) . Should ( ) . Be (
370+ "9b183d06142d2fc597d5cb935850fc1770b6be1651156c8665553a13e787bfc0" ,
371+ "SHA256 of Repository.cs should match" ) ;
372+ }
373+
374+ [ Fact ]
375+ public void Load_subdirectory_contents_from_head ( )
376+ {
377+ var gitDir = _fixture . GitDirectory ;
378+ var repoDir = _fixture . RepoDirectory ;
379+
380+ var subdirContents =
381+ LoadFromLocalFiles . LoadSubdirectoryContentsFromHead (
382+ gitDir ,
383+ [ "implement" , "GitCore" ] ) ;
384+
385+ subdirContents . Should ( ) . NotBeNull ( "Subdirectory contents should be loaded from HEAD" ) ;
386+ subdirContents . Count . Should ( ) . BeGreaterThan ( 0 , "Subdirectory should contain files" ) ;
387+
388+ // Verify well-known files exist with paths relative to the subdirectory
389+ subdirContents . Should ( ) . ContainKey ( [ "GitCore.csproj" ] ) ;
390+ subdirContents . Should ( ) . ContainKey ( [ "README.md" ] ) ;
391+
392+ // Verify SHA256 hashes of file contents match git executable output
393+ foreach ( var fileName in new [ ] { "GitCore.csproj" , "README.md" } )
394+ {
395+ var gitContent = RunGitCommandBytes ( repoDir , $ "show HEAD:implement/GitCore/{ fileName } ") ;
396+ var expectedSha256 = Convert . ToHexStringLower ( SHA256 . HashData ( gitContent ) ) ;
397+
398+ ComputeSha256Hex ( subdirContents [ [ fileName ] ] ) . Should ( ) . Be (
399+ expectedSha256 ,
400+ $ "SHA256 of { fileName } from GitCore should match git show output") ;
401+ }
402+ }
403+
404+ [ Fact ]
405+ public void Load_subdirectory_contents_matches_full_tree_filtered ( )
406+ {
407+ var gitDir = _fixture . GitDirectory ;
408+
409+ // Use commit 0166a832097feb94bd565354b31559ccb355e0be ("parse commit properties and add API for reading commits") on main
410+ var commitSha = "0166a832097feb94bd565354b31559ccb355e0be" ;
411+
412+ // Load all files, then filter to the subdirectory
413+ var allFiles = LoadFromLocalFiles . LoadTreeContentsFromCommit ( gitDir , commitSha ) ;
414+
415+ var filteredFiles =
416+ new Dictionary < IReadOnlyList < string > , ReadOnlyMemory < byte > > (
417+ comparer : GitCore . Common . EnumerableExtensions . EqualityComparer < IReadOnlyList < string > > ( ) ) ;
418+
419+ foreach ( var kvp in allFiles )
420+ {
421+ if ( kvp . Key . Count >= 3 &&
422+ kvp . Key [ 0 ] == "implement" &&
423+ kvp . Key [ 1 ] == "GitCore" )
424+ {
425+ filteredFiles [ ( IReadOnlyList < string > ) kvp . Key . Skip ( 2 ) . ToArray ( ) ] = kvp . Value ;
426+ }
427+ }
428+
429+ // Load subdirectory contents directly
430+ var subdirFiles =
431+ LoadFromLocalFiles . LoadSubdirectoryContentsFromCommit (
432+ gitDir ,
433+ commitSha ,
434+ [ "implement" , "GitCore" ] ) ;
435+
436+ // Both approaches should return the same number of files
437+ subdirFiles . Count . Should ( ) . Be (
438+ filteredFiles . Count ,
439+ "Subdirectory loading should return the same number of files as filtering the full tree" ) ;
440+
441+ // Verify each file matches
442+ foreach ( var kvp in filteredFiles )
443+ {
444+ subdirFiles . Should ( ) . ContainKey (
445+ kvp . Key ,
446+ $ "File { string . Join ( "/" , kvp . Key ) } should exist in subdirectory contents") ;
447+
448+ ComputeSha256Hex ( subdirFiles [ kvp . Key ] ) . Should ( ) . Be (
449+ ComputeSha256Hex ( kvp . Value ) ,
450+ $ "Content of { string . Join ( "/" , kvp . Key ) } should match between full tree and subdirectory loading") ;
451+ }
452+ }
453+
330454 [ Fact ]
331455 public void FindGitDirectoryUpwards_from_repository_root_finds_git_directory ( )
332456 {
@@ -352,7 +476,9 @@ public void FindGitDirectoryUpwards_from_subdirectory_finds_git_directory()
352476
353477 result . Should ( ) . NotBeNull ( "Should find .git from a subdirectory" ) ;
354478 result . Should ( ) . Be ( _fixture . GitDirectory , "Should find the correct .git directory" ) ;
355- checkedPaths . Count . Should ( ) . BeGreaterThan ( 1 ,
479+
480+ checkedPaths . Count . Should ( ) . BeGreaterThan (
481+ 1 ,
356482 "Should have checked more than one path when starting below the repo root" ) ;
357483 }
358484
@@ -396,9 +522,12 @@ public void FindGitDirectoryUpwards_empty_git_directory_is_skipped()
396522 var result = LoadFromLocalFiles . FindGitDirectoryUpwards ( tempDir , out var checkedPaths ) ;
397523
398524 // The empty .git directory should have been checked but not returned
399- checkedPaths . Should ( ) . Contain ( Path . Combine ( tempDir , ".git" ) ,
525+ checkedPaths . Should ( ) . Contain (
526+ Path . Combine ( tempDir , ".git" ) ,
400527 "Should have checked the empty .git directory" ) ;
401- result . Should ( ) . NotBe ( Path . Combine ( tempDir , ".git" ) ,
528+
529+ result . Should ( ) . NotBe (
530+ Path . Combine ( tempDir , ".git" ) ,
402531 "Should not return an empty .git directory" ) ;
403532 }
404533 finally
0 commit comments