Skip to content

Add commands for managing git tags#25

Merged
darthsharp merged 8 commits intomainfrom
feature/managingtags
Feb 13, 2026
Merged

Add commands for managing git tags#25
darthsharp merged 8 commits intomainfrom
feature/managingtags

Conversation

@darthsharp
Copy link
Copy Markdown
Contributor

No description provided.

- Introduce `TagCommandGroup` as a command group for tag-related commands.
- Implement `ListTagsCommand` with options to display tags in simple or detailed views.
- Add support for `TargetCommit` property in `IGitTag` and `GitTag` to enable sorting and extended tag details.
- Create `BoolExtensions` helper class for streamlined boolean condition handling in commands.
- Introduced `IFetchTagsCommand` interface and `FetchTagsCommand` implementation for fetching tags.
- Added CLI implementation for fetching tags under `TagGroup`.
- Updated project structure and `GitCommands` to include the fetch tags functionality.
- Add `CreateTagCommand` for creating new tags with additional options.
- Introduce `FetchTagsCommandOptions` for flexible tag fetching configuration.
- Update `FetchTagsCommand` to use `FetchTagsCommandOptions`.
- Simplify `IGitTagCollection` methods by combining similar tag creation methods.
- Remove redundant console writes across various commands.
- Update `FetchTagsCommand` to use markup for success message.
- Implement CLI command for deleting tags with options for remote deletion.
- Extend `IGitTagCollection` and `GitTagCollection` with methods for tag removal.
- Update project structure to include `TagGroup/Delete` commands.
…elated CLI option classes and add `[UsedImplicitly]` for `FetchTagsCommand`.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a new tag command group to the GitTool CLI, introducing commands to list/create/delete/fetch git tags, and extends the underlying Git abstractions to support tag management operations (including fetching tags via libgit2sharp).

Changes:

  • Add tag CLI command group with list, create, delete, and fetch subcommands.
  • Extend IGitTagCollection / GitTagCollection to support tag creation with objectish, deletion (incl. remote), and pushing by name.
  • Add a new IFetchTagsCommand implementation and wire it into IGitCommands.

Reviewed changes

Copilot reviewed 26 out of 26 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
source/GitTool/CreativeCoders.GitTool.Cli.GtApp/Program.cs Adjust CLI host footer output behavior.
source/GitTool/CreativeCoders.GitTool.Cli.Commands/TagGroup/TagCommandGroup.cs Introduce tag command group metadata.
source/GitTool/CreativeCoders.GitTool.Cli.Commands/TagGroup/List/ListTagsOptions.cs Add options for tag listing output format.
source/GitTool/CreativeCoders.GitTool.Cli.Commands/TagGroup/List/ListTagsCommand.cs Implement tag list command.
source/GitTool/CreativeCoders.GitTool.Cli.Commands/TagGroup/Fetch/FetchTagsOptions.cs Add options for tag fetch.
source/GitTool/CreativeCoders.GitTool.Cli.Commands/TagGroup/Fetch/FetchTagsCommand.cs Implement tag fetch command.
source/GitTool/CreativeCoders.GitTool.Cli.Commands/TagGroup/Delete/DeleteTagOptions.cs Add options for tag delete.
source/GitTool/CreativeCoders.GitTool.Cli.Commands/TagGroup/Delete/DeleteTagCommand.cs Implement tag delete command.
source/GitTool/CreativeCoders.GitTool.Cli.Commands/TagGroup/Create/CreateTagOptions.cs Add options for tag create.
source/GitTool/CreativeCoders.GitTool.Cli.Commands/TagGroup/Create/CreateTagCommand.cs Implement tag create command.
source/GitTool/CreativeCoders.GitTool.Cli.Commands/Shared/GitToolPushCommand.cs Remove trailing blank-line output.
source/GitTool/CreativeCoders.GitTool.Cli.Commands/Shared/GitToolPullCommand.cs Remove trailing blank-line output.
source/GitTool/CreativeCoders.GitTool.Cli.Commands/Shared/BoolExtensions.cs Add bool chaining helpers used by new tag commands.
source/GitTool/CreativeCoders.GitTool.Cli.Commands/ReleaseGroup/Create/CreateReleaseCommand.cs Update tag creation call to match new signature.
source/GitTool/CreativeCoders.GitTool.Cli.Commands/BranchGroup/Update/UpdateBranchesCommand.cs Remove extra empty-line output.
source/GitTool/CreativeCoders.GitTool.Cli.Commands/BranchGroup/List/ListBranchesCommand.cs Remove extra empty-line output.
source/GitTool/CreativeCoders.GitTool.Cli.Commands/BranchGroup/Info/InfoBranchesCommand.cs Remove extra empty-line output.
source/Git/CreativeCoders.Git/Tags/GitTagCollection.cs Add tag create/delete/push APIs and remote tag deletion logic.
source/Git/CreativeCoders.Git/Tags/GitTag.cs Add TargetCommit property population for tags.
source/Git/CreativeCoders.Git/GitCommands/GitCommands.cs Add factory method for fetch-tags command.
source/Git/CreativeCoders.Git/GitCommands/FetchTagsCommand.cs Implement fetch-tags command via libgit2sharp.
source/Git/CreativeCoders.Git.Abstractions/Tags/IGitTagCollection.cs Update tag collection interface for new tag operations.
source/Git/CreativeCoders.Git.Abstractions/Tags/IGitTag.cs Add TargetCommit to tag abstraction.
source/Git/CreativeCoders.Git.Abstractions/GitCommands/IGitCommands.cs Add CreateFetchTagsCommand to command factory abstraction.
source/Git/CreativeCoders.Git.Abstractions/GitCommands/IFetchTagsCommand.cs Add abstraction for fetching tags.
source/Git/CreativeCoders.Git.Abstractions/GitCommands/FetchTagsCommandOptions.cs Add options DTO for fetching tags.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +18 to 22
if (_tag.Target is Commit commit)
{
TargetCommit = GitCommit.From(commit);
}
}
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TargetCommit is only populated when the tag directly targets a Commit (lightweight tags). For annotated tags (TagAnnotation), TargetCommit stays null even though PeeledTargetCommit() can resolve the commit, which will break tag sorting / display in the new CLI commands. Consider setting TargetCommit based on the peeled target (or implement TargetCommit to return PeeledTargetCommit()), so both lightweight and annotated tags have a commit when available.

Copilot uses AI. Check for mistakes.
return new GitTag(
_libGitCaller
.Invoke(() => _repository.ApplyTag(tagName, objectish, _context.GetSignature(), message)));
DeleteTag(tag.Name.Canonical, deleteOnRemote);
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DeleteTag(IGitTag ...) passes tag.Name.Canonical into DeleteTag(string ...). Since canonical tag names include the refs/tags/ prefix, this can produce an invalid remote delete refspec (:refs/tags/refs/tags/<name>) when deleteOnRemote is true. Pass tag.Name.Friendly (or otherwise normalize to a plain tag name) to avoid duplicating the prefix.

Suggested change
DeleteTag(tag.Name.Canonical, deleteOnRemote);
DeleteTag(tag.Name.Friendly, deleteOnRemote);

Copilot uses AI. Check for mistakes.
Comment on lines +64 to +73
public void PushTag(string tagName)
{
var pushOptions = new PushOptions
{
CredentialsProvider = _context.GetCredentialsHandler()
};

_libGitCaller.Invoke(() =>
_repository.Network.Push(_repository.Network.Remotes[GitRemotes.Origin], tag.Name.Canonical, pushOptions));
_repository.Network.Push(_repository.Network.Remotes[GitRemotes.Origin], tagName, pushOptions));
}
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PushTag(string tagName) forwards tagName directly to Network.Push(...) as the refspec. Given the method name/signature, callers are likely to pass a friendly tag name (e.g. v1.2.3), not a canonical refspec (refs/tags/v1.2.3). Either normalize the input to a proper tag refspec before pushing, or rename/ document the parameter as a refspec to avoid subtle push failures.

Copilot uses AI. Check for mistakes.
Prune = commandOptions.Prune,
TagFetchMode = TagFetchMode.All
};

Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FetchOptions here don't set a CredentialsProvider. The repository’s existing fetch path uses gitFetchOptions.ToFetchOptions(GetCredentialsHandler()), so this new command may fail against authenticated remotes. Set fetchOptions.CredentialsProvider using the repository context’s credentials handler (and keep behavior consistent with other fetch operations).

Suggested change
fetchOptions.CredentialsProvider = _repositoryContext.GetCredentialsHandler();

Copilot uses AI. Check for mistakes.
Comment on lines +23 to +41
var remotes = _gitRepository.Remotes.ToList();

if (remotes.Count == 0)
{
_ansiConsole.WriteLine("No remote repository found.");
return Task.FromResult(CommandResult.Success);
}

var fetchTagsCommand = _gitRepository.Commands.CreateFetchTagsCommand();

foreach (var remote in remotes)
{
_ansiConsole.WriteLine($"Fetch tags from remote '{remote.Name}' ({remote.Url})...");
fetchTagsCommand.Execute(new FetchTagsCommandOptions
{
RemoteName = remote.Name,
Prune = options.Prune
});
}
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FetchTagsOptions.RemoteName is never used. The command currently fetches tags from all remotes (looping over remotes) regardless of the --remote option, so the CLI behavior doesn't match the option/help text. Use options.RemoteName to select a single remote (and report an error if not found), or remove the option if fetching all remotes is intended.

Copilot uses AI. Check for mistakes.
public string RemoteName { get; set; } = "origin";

[OptionParameter('p', "prune", HelpText = "Prune deleted remote tags")]
public bool Prune { get; set; } = true;
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prune defaults to true while also being a boolean flag option. With most CLI parsers, a flag can only turn the value on, so this makes pruning effectively always enabled and prevents users from disabling it. Consider defaulting to false (so --prune enables it) or adding an explicit --no-prune option / using a nullable bool if the parser supports it.

Suggested change
public bool Prune { get; set; } = true;
public bool Prune { get; set; } = false;

Copilot uses AI. Check for mistakes.
Comment on lines +9 to +10
[OptionValue(0, HelpText = "The name of the tag to delete")]
public string TagName { get; set; } = string.Empty;
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The positional TagName argument isn't marked as required and defaults to string.Empty. If the user runs tag delete without a name, this will attempt to delete an empty tag name (likely throwing) instead of failing fast with a clear validation error. Mark the option value as required (and avoid using an empty-string default).

Suggested change
[OptionValue(0, HelpText = "The name of the tag to delete")]
public string TagName { get; set; } = string.Empty;
[OptionValue(0, HelpText = "The name of the tag to delete", Required = true)]
public string TagName { get; set; } = null!;

Copilot uses AI. Check for mistakes.
Comment on lines +25 to +27
var tags = _gitRepository.Tags.OrderByDescending(x =>
x.TargetCommit?.Author.When ?? DateTimeOffset.MinValue);

Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This command sorts and displays tag metadata using IGitTag.TargetCommit. As implemented, TargetCommit is null for annotated tags, which will cause annotated tags to sort to the bottom and show empty date/committer info. Either use PeeledTargetCommit() here, or ensure TargetCommit resolves the peeled commit for annotated tags.

Copilot uses AI. Check for mistakes.
@darthsharp darthsharp merged commit 330738b into main Feb 13, 2026
6 checks passed
@darthsharp darthsharp deleted the feature/managingtags branch February 13, 2026 18:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants