@@ -9,23 +9,30 @@ namespace Azure.Sdk.Tools.Cli.Helpers
99 public interface IGitHelper
1010 {
1111 // Get the owner
12- public Task < string > GetRepoOwnerNameAsync ( string path , bool findUpstreamParent = true ) ;
13- public Task < string > GetRepoFullNameAsync ( string path , bool findUpstreamParent = true ) ;
14- public Uri GetRepoRemoteUri ( string path ) ;
15- public string GetBranchName ( string path ) ;
16- public string GetMergeBaseCommitSha ( string path , string targetBranch ) ;
17- public string DiscoverRepoRoot ( string path ) ;
18- public string GetRepoName ( string path ) ;
12+ public Task < string > GetRepoOwnerNameAsync ( string pathInRepo , bool findUpstreamParent = true ) ;
13+ public Task < string > GetRepoFullNameAsync ( string pathInRepo , bool findUpstreamParent = true ) ;
14+ public Uri GetRepoRemoteUri ( string pathInRepo ) ;
15+ public string GetBranchName ( string pathInRepo ) ;
16+ public string GetMergeBaseCommitSha ( string pathInRepo , string targetBranch ) ;
17+ public string DiscoverRepoRoot ( string pathInRepo ) ;
18+ public string GetRepoName ( string pathInRepo ) ;
1919 }
2020
2121 public class GitHelper ( IGitHubService gitHubService , ILogger < GitHelper > logger ) : IGitHelper
2222 {
2323 private readonly ILogger < GitHelper > logger = logger ;
2424 private readonly IGitHubService gitHubService = gitHubService ;
2525
26- public string GetMergeBaseCommitSha ( string path , string targetBranchName )
26+ /// <summary>
27+ /// Gets the SHA of the merge base (common ancestor) between the current branch and the target branch.
28+ /// </summary>
29+ /// <param name="pathInRepo">Any path within the git repository (file or directory)</param>
30+ /// <param name="targetBranchName">The name of the target branch to find the merge base with</param>
31+ /// <returns>The SHA of the merge base commit, or empty string if not found</returns>
32+ public string GetMergeBaseCommitSha ( string pathInRepo , string targetBranchName )
2733 {
28- using ( var repo = new Repository ( path ) )
34+ var repoRoot = DiscoverRepoRoot ( pathInRepo ) ;
35+ using ( var repo = new Repository ( repoRoot ) )
2936 {
3037 // Get the current branch
3138 Branch currentBranch = repo . Head ;
@@ -38,16 +45,29 @@ public string GetMergeBaseCommitSha(string path, string targetBranchName)
3845 }
3946 }
4047
41- public string GetBranchName ( string repoPath )
48+ /// <summary>
49+ /// Gets the friendly name of the current branch in the repository.
50+ /// </summary>
51+ /// <param name="pathInRepo">Any path within the git repository (file or directory)</param>
52+ /// <returns>The friendly name of the current branch</returns>
53+ public string GetBranchName ( string pathInRepo )
4254 {
43- using var repo = new Repository ( repoPath ) ;
55+ var repoRoot = DiscoverRepoRoot ( pathInRepo ) ;
56+ using var repo = new Repository ( repoRoot ) ;
4457 var branchName = repo . Head . FriendlyName ;
4558 return branchName ;
4659 }
4760
48- public Uri GetRepoRemoteUri ( string path )
61+ /// <summary>
62+ /// Gets the remote origin URI of the repository in HTTPS format.
63+ /// </summary>
64+ /// <param name="pathInRepo">Any path within the git repository (file or directory)</param>
65+ /// <returns>The HTTPS URI of the remote origin</returns>
66+ /// <exception cref="InvalidOperationException">Thrown when unable to determine remote URL</exception>
67+ public Uri GetRepoRemoteUri ( string pathInRepo )
4968 {
50- using var repo = new Repository ( path ) ;
69+ var repoRoot = DiscoverRepoRoot ( pathInRepo ) ;
70+ using var repo = new Repository ( repoRoot ) ;
5171 var remote = repo . Network ? . Remotes [ "origin" ] ;
5272 if ( remote != null )
5373 {
@@ -87,9 +107,16 @@ private static string ConvertSshToHttpsUrl(string gitUrl)
87107 return gitUrl ;
88108 }
89109
90- public async Task < string > GetRepoOwnerNameAsync ( string path , bool findUpstreamParent = true )
110+ /// <summary>
111+ /// Gets the owner name of the repository, optionally finding the upstream parent if the repo is a fork.
112+ /// </summary>
113+ /// <param name="pathInRepo">Any path within the git repository (file or directory)</param>
114+ /// <param name="findUpstreamParent">Whether to find the upstream parent repo if this is a fork (default: true)</param>
115+ /// <returns>The owner name of the repository or its upstream parent</returns>
116+ /// <exception cref="InvalidOperationException">Thrown when unable to determine repository owner</exception>
117+ public async Task < string > GetRepoOwnerNameAsync ( string pathInRepo , bool findUpstreamParent = true )
91118 {
92- var uri = GetRepoRemoteUri ( path ) ;
119+ var uri = GetRepoRemoteUri ( pathInRepo ) ;
93120 var segments = uri . Segments ;
94121 string repoOwner = string . Empty ;
95122 string repoName = string . Empty ;
@@ -122,52 +149,74 @@ public async Task<string> GetRepoOwnerNameAsync(string path, bool findUpstreamPa
122149 throw new InvalidOperationException ( "Unable to determine repository owner." ) ;
123150 }
124151
125- // Get the full name of repo in the format of "{owner/name}", e.g. "Azure/azure-rest-api-specs"
126- public async Task < string > GetRepoFullNameAsync ( string path , bool findUpstreamParent = true )
152+ /// <summary>
153+ /// Gets the full name of the repository in the format "{owner}/{name}", e.g. "Azure/azure-rest-api-specs".
154+ /// </summary>
155+ /// <param name="pathInRepo">Any path within the git repository (file or directory)</param>
156+ /// <param name="findUpstreamParent">Whether to find the upstream parent repo if this is a fork (default: true)</param>
157+ /// <returns>The full name of the repository in "owner/name" format</returns>
158+ /// <exception cref="ArgumentException">Thrown when pathInRepo is null or empty</exception>
159+ public async Task < string > GetRepoFullNameAsync ( string pathInRepo , bool findUpstreamParent = true )
127160 {
128- if ( ! string . IsNullOrEmpty ( path ) )
161+ if ( ! string . IsNullOrEmpty ( pathInRepo ) )
129162 {
130- var repoOwner = await GetRepoOwnerNameAsync ( path , findUpstreamParent ) ;
131- var repoName = GetRepoName ( path ) ;
163+ var repoOwner = await GetRepoOwnerNameAsync ( pathInRepo , findUpstreamParent ) ;
164+ var repoName = GetRepoName ( pathInRepo ) ;
132165 return $ "{ repoOwner } /{ repoName } ";
133166 }
134167
135- throw new ArgumentException ( "Invalid repository path." , nameof ( path ) ) ;
168+ throw new ArgumentException ( "Invalid repository path." , nameof ( pathInRepo ) ) ;
136169 }
137170
138- public string DiscoverRepoRoot ( string path )
171+ /// <summary>
172+ /// Discovers and returns the root directory path of the git repository containing the specified path.
173+ /// </summary>
174+ /// <param name="pathInRepo">Any path within the git repository (file or directory)</param>
175+ /// <returns>The absolute path to the repository root directory</returns>
176+ /// <exception cref="InvalidOperationException">Thrown when no git repository is found at or above the specified path</exception>
177+ public string DiscoverRepoRoot ( string pathInRepo )
139178 {
140- var repoPath = Repository . Discover ( path ) ;
179+ // Discover the repo root for this path
180+ var repoPath = Repository . Discover ( pathInRepo ) ;
141181 if ( string . IsNullOrEmpty ( repoPath ) )
142182 {
143- throw new InvalidOperationException ( $ "No git repository found at or above the path: { path } ") ;
183+ throw new InvalidOperationException ( $ "No git repository found at or above the path: { pathInRepo } ") ;
144184 }
145185
146186 // Repository.Discover returns the path to .git directory
147187 // The repository root is the parent directory of .git
148188 var gitDir = new DirectoryInfo ( repoPath ) ;
149- return gitDir . Parent ? . FullName ?? throw new InvalidOperationException ( "Unable to determine repository root" ) ;
189+ if ( gitDir . Parent == null || string . IsNullOrEmpty ( gitDir . Parent . FullName ) )
190+ {
191+ throw new InvalidOperationException ( "Unable to determine repository root" ) ;
192+ }
193+
194+ return gitDir . Parent . FullName ;
150195 }
151196
152- // Get the repository name from the local path
153- public string GetRepoName ( string path )
197+ /// <summary>
198+ /// Gets the repository name from the remote origin URL.
199+ /// </summary>
200+ /// <param name="pathInRepo">Any path within the git repository (file or directory)</param>
201+ /// <returns>The name of the repository (without the owner)</returns>
202+ /// <exception cref="ArgumentException">Thrown when pathInRepo is null or empty</exception>
203+ /// <exception cref="InvalidOperationException">Thrown when unable to determine repository name from remote URL</exception>
204+ public string GetRepoName ( string pathInRepo )
154205 {
155- if ( string . IsNullOrEmpty ( path ) )
206+ if ( string . IsNullOrEmpty ( pathInRepo ) )
156207 {
157- throw new ArgumentException ( "Invalid repository path." , nameof ( path ) ) ;
208+ throw new ArgumentException ( "Invalid repository path." , nameof ( pathInRepo ) ) ;
158209 }
159210
160- var repoRoot = DiscoverRepoRoot ( path ) ;
161- var uri = GetRepoRemoteUri ( repoRoot ) ;
211+ var uri = GetRepoRemoteUri ( pathInRepo ) ;
162212 var segments = uri . Segments ;
163213
164214 if ( segments . Length < 2 )
165215 {
166- throw new InvalidOperationException ( $ "Unable to parse repository owner and name from remote URL: { uri } ") ;
216+ throw new InvalidOperationException ( $ "Unable to parse repository name from remote URL: { uri } ") ;
167217 }
168218
169219 string repoName = segments [ ^ 1 ] . TrimEnd ( ".git" . ToCharArray ( ) ) ;
170-
171220 return repoName ;
172221 }
173222 }
0 commit comments