diff --git a/CSharp/TfsCmdlets/Cmdlets/Git/GetGitRepository.cs b/CSharp/TfsCmdlets/Cmdlets/Git/GetGitRepository.cs index 0e9c6e8a..f15cbc15 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Git/GetGitRepository.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Git/GetGitRepository.cs @@ -1,5 +1,8 @@ +using System; using System.Management.Automation; using Microsoft.TeamFoundation.SourceControl.WebApi; +using System.Linq; +using System.Collections.Generic; namespace TfsCmdlets.Cmdlets.Git { @@ -30,6 +33,12 @@ partial class GetGitRepository /// [Parameter()] public SwitchParameter IncludeParent { get; set; } + + /// + /// Includes repositories in the recycle bin in the search results. + /// + [Parameter()] + public SwitchParameter IncludeRecycleBin { get; set; } } [CmdletController(typeof(GitRepository), Client=typeof(IGitHttpClient))] @@ -37,6 +46,15 @@ partial class GetGitRepositoryController { protected override IEnumerable Run() { + // Get recycle bin repositories once if needed + IList recycleBinRepos = null; + if (IncludeRecycleBin) + { + recycleBinRepos = Client + .GetRecycleBinRepositoriesAsync(Project.Name) + .GetResult("Error getting recycle bin repositories"); + } + foreach (var input in Repository) { var repository = input switch @@ -59,6 +77,16 @@ string s when string.IsNullOrEmpty(s) => Project.Name, yield return Client .GetRepositoryAsync(Project.Name, guid, includeParent: IncludeParent) .GetResult($"Error getting repository with ID {guid}"); + + // Also check recycle bin for this ID + if (IncludeRecycleBin) + { + var deletedRepo = recycleBinRepos?.FirstOrDefault(r => r.Id == guid); + if (deletedRepo != null) + { + yield return deletedRepo; + } + } break; } case { } when Default: @@ -70,30 +98,48 @@ string s when string.IsNullOrEmpty(s) => Project.Name, } case string s when !s.IsWildcard(): { - GitRepository result; + GitRepository result = null; try { result = Client .GetRepositoryAsync(Project.Name, s, includeParent: IncludeParent) .GetResult($"Error getting repository '{s}'"); + yield return result; } catch { // Workaround to retrieve disabled repositories - result = Client + var activeRepo = Client .GetRepositoriesAsync(Project.Name, includeLinks: true, includeHidden: true) .GetResult($"Error getting repository(ies) '{s}'") - .First(r => r.Name.Equals(s, StringComparison.OrdinalIgnoreCase)); + .FirstOrDefault(r => r.Name.Equals(s, StringComparison.OrdinalIgnoreCase)); - if (IncludeParent) + if (activeRepo != null) { - result = Client - .GetRepositoryAsync(Project.Name, result.Id, includeParent: true) - .GetResult($"Error getting repository(ies) '{s}'"); + if (IncludeParent) + { + result = Client + .GetRepositoryAsync(Project.Name, activeRepo.Id, includeParent: true) + .GetResult($"Error getting repository(ies) '{s}'"); + yield return result; + } + else + { + yield return activeRepo; + } + } + } + + // Also check recycle bin for this name + if (IncludeRecycleBin) + { + var deletedRepo = recycleBinRepos?.FirstOrDefault(r => r.Name.Equals(s, StringComparison.OrdinalIgnoreCase)); + if (deletedRepo != null) + { + yield return deletedRepo; } } - yield return result; break; } case string s: @@ -114,6 +160,15 @@ string s when string.IsNullOrEmpty(s) => Project.Name, yield return repo; } } + + // Also check recycle bin for wildcard patterns + if (IncludeRecycleBin) + { + foreach (var deletedRepo in recycleBinRepos?.Where(r => r.Name.IsLike(s)) ?? Enumerable.Empty()) + { + yield return deletedRepo; + } + } break; } default: diff --git a/Docs/Get-TfsGitRepository-RecycleBin.md b/Docs/Get-TfsGitRepository-RecycleBin.md new file mode 100644 index 00000000..57cf6351 --- /dev/null +++ b/Docs/Get-TfsGitRepository-RecycleBin.md @@ -0,0 +1,52 @@ +# Using Get-TfsGitRepository with Recycle Bin + +The `Get-TfsGitRepository` cmdlet now supports retrieving repositories from the Recycle Bin using the `-IncludeRecycleBin` parameter. + +## Examples + +### Get all repositories including those in the recycle bin +```powershell +Get-TfsGitRepository -Repository '*' -IncludeRecycleBin -Project 'MyProject' +``` + +### Find a specific deleted repository by name +```powershell +Get-TfsGitRepository -Repository 'DeletedRepo' -IncludeRecycleBin -Project 'MyProject' +``` + +### Find deleted repositories matching a pattern +```powershell +Get-TfsGitRepository -Repository 'Old*' -IncludeRecycleBin -Project 'MyProject' +``` + +### Find a deleted repository by ID +```powershell +Get-TfsGitRepository -Repository '12345678-1234-1234-1234-123456789012' -IncludeRecycleBin -Project 'MyProject' +``` + +## Output Types + +When `-IncludeRecycleBin` is used, the cmdlet may return two types of objects: + +- **GitRepository**: Active repositories +- **GitDeletedRepository**: Deleted repositories from the recycle bin + +### GitDeletedRepository Properties + +The `GitDeletedRepository` object includes the following properties: + +- `Id`: The repository ID +- `Name`: The repository name +- `ProjectReference`: Reference to the team project +- `DeletedBy`: The user who deleted the repository +- `CreatedDate`: When the repository was originally created +- `DeletedDate`: When the repository was deleted + +## API Reference + +This feature uses the Azure DevOps REST API endpoint: +``` +GET https://dev.azure.com/{organization}/{project}/_apis/git/recycleBin/repositories?api-version=7.1 +``` + +For more information, see the [official Azure DevOps REST API documentation](https://learn.microsoft.com/en-us/rest/api/azure/devops/git/repositories/get-recycle-bin-repositories?view=azure-devops-rest-7.1). \ No newline at end of file diff --git a/PS/_Tests/Git/GetTfsGitRepository-RecycleBin.Tests.ps1 b/PS/_Tests/Git/GetTfsGitRepository-RecycleBin.Tests.ps1 new file mode 100644 index 00000000..d8c12a9a --- /dev/null +++ b/PS/_Tests/Git/GetTfsGitRepository-RecycleBin.Tests.ps1 @@ -0,0 +1,76 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context 'Get repositories including recycle bin' { + # Get-TfsGitRepository + # [[-Repository] ] + # [-IncludeParent] + # [-IncludeRecycleBin] + # [-Project ] + # [-Collection ] + # [-Server ] [] + + It 'Should accept IncludeRecycleBin parameter' { + { Get-TfsGitRepository -Repository '*' -IncludeRecycleBin -Project $tfsProject } | Should -Not -Throw + } + + It 'Should include recycle bin repositories when IncludeRecycleBin is specified' { + # This test assumes there's at least one deleted repository in the recycle bin + $allRepos = @(Get-TfsGitRepository -Repository '*' -Project $tfsProject) + $allReposWithRecycleBin = @(Get-TfsGitRepository -Repository '*' -IncludeRecycleBin -Project $tfsProject) + + $allReposWithRecycleBin.Count | Should -BeGreaterOrEqual $allRepos.Count + } + + It 'Should find specific repository in recycle bin by name' { + # This is a conceptual test - would need actual deleted repository + # $repository = Get-TfsGitRepository -Repository 'DeletedRepo' -IncludeRecycleBin -Project $tfsProject + # $repository.Name | Should -Be 'DeletedRepo' + # $repository | Should -BeOfType 'Microsoft.TeamFoundation.SourceControl.WebApi.GitDeletedRepository' + + # For now, just verify the parameter is accepted without error + { Get-TfsGitRepository -Repository 'NonExistentRepo' -IncludeRecycleBin -Project $tfsProject } | Should -Not -Throw + } + + It 'Should find specific repository in recycle bin by ID' { + # This is a conceptual test - would need actual deleted repository ID + # $repository = Get-TfsGitRepository -Repository '12345678-1234-1234-1234-123456789012' -IncludeRecycleBin -Project $tfsProject + # $repository.Id | Should -Be '12345678-1234-1234-1234-123456789012' + # $repository | Should -BeOfType 'Microsoft.TeamFoundation.SourceControl.WebApi.GitDeletedRepository' + + # For now, just verify the parameter is accepted without error + { Get-TfsGitRepository -Repository '12345678-1234-1234-1234-123456789012' -IncludeRecycleBin -Project $tfsProject } | Should -Not -Throw + } + + It 'Should support wildcards with recycle bin repositories' { + # $repositories = Get-TfsGitRepository -Repository 'Deleted*' -IncludeRecycleBin -Project $tfsProject + # $repositories | Should -BeOfType 'Microsoft.TeamFoundation.SourceControl.WebApi.GitDeletedRepository' + + # For now, just verify the parameter is accepted without error + { Get-TfsGitRepository -Repository 'Deleted*' -IncludeRecycleBin -Project $tfsProject } | Should -Not -Throw + } + + It 'Should handle multiple repository names with recycle bin' { + { Get-TfsGitRepository -Repository 'Repo1', 'Repo2' -IncludeRecycleBin -Project $tfsProject } | Should -Not -Throw + } + + It 'Should work with Default parameter and IncludeRecycleBin' { + { Get-TfsGitRepository -Default -IncludeRecycleBin -Project $tfsProject } | Should -Not -Throw + } + + It 'Should work with IncludeParent and IncludeRecycleBin together' { + { Get-TfsGitRepository -Repository '*' -IncludeParent -IncludeRecycleBin -Project $tfsProject } | Should -Not -Throw + } + + It 'Should return both active and deleted repositories' { + # This would require actual data to test properly + # $results = Get-TfsGitRepository -Repository '*' -IncludeRecycleBin -Project $tfsProject + # $results | Where-Object { $_ -is [Microsoft.TeamFoundation.SourceControl.WebApi.GitRepository] } | Should -Not -BeNullOrEmpty + # $results | Where-Object { $_ -is [Microsoft.TeamFoundation.SourceControl.WebApi.GitDeletedRepository] } | Should -Not -BeNullOrEmpty + + # For now, just verify it runs without error + { Get-TfsGitRepository -Repository '*' -IncludeRecycleBin -Project $tfsProject } | Should -Not -Throw + } + } +} \ No newline at end of file diff --git a/PS/_Tests/Git/GetTfsGitRepository.Tests.ps1 b/PS/_Tests/Git/GetTfsGitRepository.Tests.ps1 index aa8266e1..9b94c75f 100644 --- a/PS/_Tests/Git/GetTfsGitRepository.Tests.ps1 +++ b/PS/_Tests/Git/GetTfsGitRepository.Tests.ps1 @@ -61,4 +61,33 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { $repository | Should -BeOfType 'Microsoft.TeamFoundation.SourceControl.WebApi.GitRepository' } } + + Context 'Get repositories including recycle bin' { + # Get-TfsGitRepository + # [[-Repository] ] + # [-IncludeRecycleBin] + # [-IncludeParent] + # [-Project ] + # [-Collection ] + # [-Server ] [] + + It 'Should accept IncludeRecycleBin parameter' { + { Get-TfsGitRepository -Repository '*' -IncludeRecycleBin -Project $tfsProject } | Should -Not -Throw + } + + It 'Should include recycle bin repositories when IncludeRecycleBin is specified' { + $allRepos = @(Get-TfsGitRepository -Repository '*' -Project $tfsProject) + $allReposWithRecycleBin = @(Get-TfsGitRepository -Repository '*' -IncludeRecycleBin -Project $tfsProject) + + $allReposWithRecycleBin.Count | Should -BeGreaterOrEqual $allRepos.Count + } + + It 'Should support wildcards with recycle bin repositories' { + { Get-TfsGitRepository -Repository 'Test*' -IncludeRecycleBin -Project $tfsProject } | Should -Not -Throw + } + + It 'Should work with IncludeParent and IncludeRecycleBin together' { + { Get-TfsGitRepository -Repository '*' -IncludeParent -IncludeRecycleBin -Project $tfsProject } | Should -Not -Throw + } + } } \ No newline at end of file