Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 63 additions & 8 deletions CSharp/TfsCmdlets/Cmdlets/Git/GetGitRepository.cs
Original file line number Diff line number Diff line change
@@ -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
{
Expand Down Expand Up @@ -30,13 +33,28 @@ partial class GetGitRepository
/// </summary>
[Parameter()]
public SwitchParameter IncludeParent { get; set; }

/// <summary>
/// Includes repositories in the recycle bin in the search results.
/// </summary>
[Parameter()]
public SwitchParameter IncludeRecycleBin { get; set; }
}

[CmdletController(typeof(GitRepository), Client=typeof(IGitHttpClient))]
partial class GetGitRepositoryController
{
protected override IEnumerable Run()
{
// Get recycle bin repositories once if needed
IList<GitDeletedRepository> recycleBinRepos = null;
if (IncludeRecycleBin)
{
recycleBinRepos = Client
.GetRecycleBinRepositoriesAsync(Project.Name)
.GetResult("Error getting recycle bin repositories");
}

foreach (var input in Repository)
{
var repository = input switch
Expand All @@ -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:
Expand All @@ -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:
Expand All @@ -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<GitDeletedRepository>())
{
yield return deletedRepo;
}
}
break;
}
default:
Expand Down
52 changes: 52 additions & 0 deletions Docs/Get-TfsGitRepository-RecycleBin.md
Original file line number Diff line number Diff line change
@@ -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).
76 changes: 76 additions & 0 deletions PS/_Tests/Git/GetTfsGitRepository-RecycleBin.Tests.ps1
Original file line number Diff line number Diff line change
@@ -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] <Object>]
# [-IncludeParent]
# [-IncludeRecycleBin]
# [-Project <Object>]
# [-Collection <Object>]
# [-Server <Object>] [<CommonParameters>]

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
}
}
}
29 changes: 29 additions & 0 deletions PS/_Tests/Git/GetTfsGitRepository.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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] <Object>]
# [-IncludeRecycleBin]
# [-IncludeParent]
# [-Project <Object>]
# [-Collection <Object>]
# [-Server <Object>] [<CommonParameters>]

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
}
}
}