44using Files . App . Dialogs ;
55using LibGit2Sharp ;
66using Microsoft . Extensions . Logging ;
7- using Sentry ;
87using System . Net . Http ;
98using System . Net . Http . Json ;
109using System . Text ;
@@ -13,6 +12,8 @@ namespace Files.App.Utils.Git
1312{
1413 internal static class GitHelpers
1514 {
15+ private static readonly StatusCenterViewModel StatusCenterViewModel = Ioc . Default . GetRequiredService < StatusCenterViewModel > ( ) ;
16+
1617 private const string GIT_RESOURCE_NAME = "Files:https://github.com" ;
1718
1819 private const string GIT_RESOURCE_USERNAME = "Personal Access Token" ;
@@ -845,5 +846,85 @@ private static bool IsAuthorizationException(Exception ex)
845846 GitOperationSemaphore . Release ( ) ;
846847 }
847848 }
849+
850+ /// <summary>
851+ /// Gets repository information from a GitHub URL.
852+ /// </summary>
853+ /// <param name="url"></param>
854+ /// <returns></returns>
855+ public static ( string RepoUrl , string RepoName ) GetRepoInfo ( string url )
856+ {
857+ // Remove protocol and normalize slashes
858+ var normalizedUrl = url . ToLower ( ) . Replace ( "https://" , "" ) . Replace ( "http://" , "" ) . Replace ( "//" , "" ) ;
859+
860+ string [ ] parts = normalizedUrl . Split ( '/' ) ;
861+
862+ // Check if the URL includes an organization or user name + repo (github.com/username/repo)
863+ if ( parts . Length >= 3 && parts [ 0 ] == "github.com" )
864+ {
865+ // Construct the repo URL from the first three parts
866+ string repoUrl = $ "https://{ parts [ 0 ] } /{ parts [ 1 ] } /{ parts [ 2 ] } ";
867+ return ( repoUrl , parts [ 2 ] ) ;
868+ }
869+
870+ return ( string . Empty , string . Empty ) ;
871+ }
872+
873+ /// <summary>
874+ /// Checks if the provided URL is a valid GitHub URL.
875+ /// </summary>
876+ /// <param name="url">The URL to validate.</param>
877+ /// <returns>True if the URL is a valid GitHub URL; otherwise, false.</returns>
878+ public static bool IsValidRepoUrl ( string url )
879+ {
880+ return ! string . IsNullOrWhiteSpace ( url ) && url . Contains ( "github.com" ) ;
881+ }
882+
883+ public static async Task CloneRepoAsync ( string repoUrl , string repoName , string targetDirectory )
884+ {
885+ var banner = StatusCenterHelper . AddCard_GitClone ( repoName . CreateEnumerable ( ) , targetDirectory . CreateEnumerable ( ) , ReturnResult . InProgress ) ;
886+ var fsProgress = new StatusCenterItemProgressModel ( banner . ProgressEventSource , enumerationCompleted : true , FileSystemStatusCode . InProgress ) ;
887+
888+ bool isSuccess = await Task . Run ( ( ) =>
889+ {
890+ try
891+ {
892+ var cloneOptions = new CloneOptions
893+ {
894+ FetchOptions =
895+ {
896+ OnTransferProgress = progress =>
897+ {
898+ banner . CancellationToken . ThrowIfCancellationRequested ( ) ;
899+ fsProgress . ItemsCount = progress . TotalObjects ;
900+ fsProgress . SetProcessedSize ( progress . ReceivedBytes ) ;
901+ fsProgress . AddProcessedItemsCount ( 1 ) ;
902+ fsProgress . Report ( ( int ) ( ( progress . ReceivedObjects / ( double ) progress . TotalObjects ) * 100 ) ) ;
903+ return true ;
904+ } ,
905+ OnProgress = _ => ! banner . CancellationToken . IsCancellationRequested
906+ } ,
907+ OnCheckoutProgress = ( path , completed , total ) =>
908+ banner . CancellationToken . ThrowIfCancellationRequested ( )
909+ } ;
910+
911+ Repository . Clone ( repoUrl , targetDirectory , cloneOptions ) ;
912+ return true ;
913+ }
914+ catch
915+ {
916+ return false ;
917+ }
918+ } , banner . CancellationToken ) ;
919+
920+ StatusCenterViewModel . RemoveItem ( banner ) ;
921+
922+ StatusCenterHelper . AddCard_GitClone (
923+ repoName . CreateEnumerable ( ) ,
924+ targetDirectory . CreateEnumerable ( ) ,
925+ isSuccess ? ReturnResult . Success :
926+ banner . CancellationToken . IsCancellationRequested ? ReturnResult . Cancelled :
927+ ReturnResult . Failed ) ;
928+ }
848929 }
849930}
0 commit comments