-
Notifications
You must be signed in to change notification settings - Fork 5
Add tag subcommand
#96
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
SandrineP
wants to merge
2
commits into
QuantStack:main
Choose a base branch
from
SandrineP:tag_cmd
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,237 @@ | ||
| #include <git2.h> | ||
|
|
||
| #include "../subcommand/tag_subcommand.hpp" | ||
| #include "../wrapper/commit_wrapper.hpp" | ||
| #include "../wrapper/tag_wrapper.hpp" | ||
|
|
||
| tag_subcommand::tag_subcommand(const libgit2_object&, CLI::App& app) | ||
| { | ||
| auto* sub = app.add_subcommand("tag", "Create, list, delete or verify tags"); | ||
|
|
||
| sub->add_flag("-l,--list", m_list_flag, "List tags. With optional <pattern>."); | ||
| sub->add_flag("-f,--force", m_force_flag, "Replace an existing tag with the given name (instead of failing)"); | ||
| sub->add_option("-d,--delete", m_delete, "Delete existing tags with the given names."); | ||
| sub->add_option("-n", m_num_lines, "<num> specifies how many lines from the annotation, if any, are printed when using -l. Implies --list."); | ||
| sub->add_option("-m,--message", m_message, "Tag message for annotated tags"); | ||
| sub->add_option("<tagname>", m_tag_name, "Tag name"); | ||
| sub->add_option("<commit>", m_target, "Target commit (defaults to HEAD)"); | ||
|
|
||
| sub->callback([this]() { this->run(); }); | ||
| } | ||
|
|
||
| // Tag listing: Print individual message lines | ||
| void print_list_lines(const std::string& message, int num_lines) | ||
| { | ||
| if (message.empty()) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| auto lines = split_input_at_newlines(message); | ||
|
|
||
| // header | ||
| std::cout << lines[0]; | ||
|
|
||
| // other lines | ||
| if (num_lines <= 1 || lines.size() <= 2) | ||
| { | ||
| std::cout << std::endl; | ||
| } | ||
| else | ||
| { | ||
| for (size_t i = 1; i < lines.size() ; i++) | ||
| { | ||
| if (i < num_lines) | ||
| { | ||
| std::cout << "\n\t\t" << lines[i]; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Tag listing: Print an actual tag object | ||
| void print_tag(git_tag* tag, int num_lines) | ||
| { | ||
| std::cout << std::left << std::setw(16) << git_tag_name(tag); | ||
|
|
||
| if (num_lines) | ||
| { | ||
| std::string msg = git_tag_message(tag); | ||
| if (!msg.empty()) | ||
| { | ||
| print_list_lines(msg, num_lines); | ||
| } | ||
| else | ||
| { | ||
| std::cout << std::endl; | ||
| } | ||
| } | ||
| else | ||
| { | ||
| std::cout << std::endl; | ||
| } | ||
| } | ||
|
|
||
| // Tag listing: Print a commit (target of a lightweight tag) | ||
| void print_commit(git_commit* commit, std::string name, int num_lines) | ||
| { | ||
| std::cout << std::left << std::setw(16) << name; | ||
|
|
||
| if (num_lines) | ||
| { | ||
| std::string msg = git_commit_message(commit); | ||
| if (!msg.empty()) | ||
| { | ||
| print_list_lines(msg, num_lines); | ||
| } | ||
| else | ||
| { | ||
| std::cout <<std::endl; | ||
| } | ||
| } | ||
| else | ||
| { | ||
| std::cout <<std::endl; | ||
| } | ||
| } | ||
|
|
||
| // Tag listing: Lookup tags based on ref name and dispatch to print | ||
| void each_tag(repository_wrapper& repo, const std::string& name, int num_lines) | ||
| { | ||
| auto obj = repo.revparse_single(name); | ||
|
|
||
| if (obj.has_value()) | ||
| { | ||
| switch (git_object_type(obj.value())) | ||
| { | ||
| case GIT_OBJECT_TAG: | ||
| print_tag(obj.value(), num_lines); | ||
| break; | ||
| case GIT_OBJECT_COMMIT: | ||
| print_commit(obj.value(), name, num_lines); | ||
| break; | ||
| default: | ||
| std::cout << name << std::endl; | ||
| } | ||
| } | ||
| else | ||
| { | ||
| std::cout << name << std::endl; | ||
| } | ||
| } | ||
|
|
||
| void tag_subcommand::list_tags(repository_wrapper& repo) | ||
| { | ||
| std::string pattern = m_tag_name.empty() ? "*" : m_tag_name; | ||
| auto tag_names = repo.tag_list_match(pattern); | ||
|
|
||
| for (const auto& tag_name: tag_names) | ||
| { | ||
| each_tag(repo, tag_name, m_num_lines); | ||
| } | ||
| } | ||
|
|
||
| void tag_subcommand::delete_tag(repository_wrapper& repo) | ||
| { | ||
| if (m_delete.empty()) | ||
| { | ||
| throw git_exception("Name required for tag deletion.", git2cpp_error_code::GENERIC_ERROR); | ||
| } | ||
|
|
||
| auto obj = repo.revparse_single(m_delete); | ||
| if (!obj.has_value()) | ||
| { | ||
| throw git_exception("error: tag '" + m_delete + "' not found.", git2cpp_error_code::GENERIC_ERROR); | ||
| } | ||
|
|
||
| git_buf abbrev_oid = GIT_BUF_INIT; | ||
| throw_if_error(git_object_short_id(&abbrev_oid, obj.value())); | ||
|
|
||
| std::string oid_str(abbrev_oid.ptr); | ||
| git_buf_dispose(&abbrev_oid); | ||
|
|
||
| throw_if_error(git_tag_delete(repo, m_delete.c_str())); | ||
| std::cout << "Deleted tag '" << m_delete << "' (was " << oid_str << ")" << std::endl; | ||
| } | ||
|
|
||
| std::optional<object_wrapper> tag_subcommand::get_target_obj(repository_wrapper& repo) | ||
| { | ||
| if (m_tag_name.empty()) | ||
| { | ||
| throw git_exception("Tag name required", git2cpp_error_code::GENERIC_ERROR); | ||
| } | ||
|
|
||
| std::string target = m_target.empty() ? "HEAD" : m_target; | ||
|
|
||
| auto target_obj = repo.revparse_single(target); | ||
| if (!target_obj.has_value()) | ||
| { | ||
| throw git_exception("Unable to resolve target: " + target, git2cpp_error_code::GENERIC_ERROR); | ||
| } | ||
|
|
||
| return target_obj; | ||
| } | ||
|
|
||
| void tag_subcommand::handle_error(int error) | ||
| { | ||
| if (error < 0) | ||
| { | ||
| if (error == GIT_EEXISTS) | ||
| { | ||
| throw git_exception("tag '" + m_tag_name + "' already exists", git2cpp_error_code::FILESYSTEM_ERROR); | ||
| } | ||
| throw git_exception("Unable to create annotated tag", error); | ||
| } | ||
| } | ||
|
|
||
| void tag_subcommand::create_lightweight_tag(repository_wrapper& repo) | ||
| { | ||
| auto target_obj = tag_subcommand::get_target_obj(repo); | ||
|
|
||
| git_oid oid; | ||
| size_t force = m_force_flag ? 1 : 0; | ||
| int error = git_tag_create_lightweight(&oid, repo, m_tag_name.c_str(), target_obj.value(), force); | ||
|
|
||
| handle_error(error); | ||
| } | ||
|
|
||
| void tag_subcommand::create_tag(repository_wrapper& repo) | ||
| { | ||
| auto target_obj = tag_subcommand::get_target_obj(repo); | ||
|
|
||
| auto tagger = signature_wrapper::get_default_signature_from_env(repo); | ||
|
|
||
| git_oid oid; | ||
| size_t force = m_force_flag ? 1 : 0; | ||
| int error = git_tag_create(&oid, repo, m_tag_name.c_str(), target_obj.value(), tagger.first, m_message.c_str(), force); | ||
|
|
||
| handle_error(error); | ||
| } | ||
|
|
||
| void tag_subcommand::run() | ||
| { | ||
| auto directory = get_current_git_path(); | ||
| auto repo = repository_wrapper::open(directory); | ||
|
|
||
| if (!m_delete.empty()) | ||
| { | ||
| delete_tag(repo); | ||
| } | ||
| else if (m_list_flag || (m_tag_name.empty() && m_message.empty())) | ||
| { | ||
| list_tags(repo); | ||
| } | ||
| else if (!m_message.empty()) | ||
| { | ||
| create_tag(repo); | ||
| } | ||
| else if (!m_tag_name.empty()) | ||
| { | ||
| create_lightweight_tag(repo); | ||
| } | ||
| else | ||
| { | ||
| list_tags(repo); | ||
| } | ||
|
|
||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| #pragma once | ||
|
|
||
| #include <CLI/CLI.hpp> | ||
|
|
||
| #include "../utils/common.hpp" | ||
| #include "../wrapper/repository_wrapper.hpp" | ||
|
|
||
| class tag_subcommand | ||
| { | ||
| public: | ||
|
|
||
| explicit tag_subcommand(const libgit2_object&, CLI::App& app); | ||
|
|
||
| void run(); | ||
|
|
||
| private: | ||
|
|
||
| void list_tags(repository_wrapper& repo); | ||
| void delete_tag(repository_wrapper& repo); | ||
| void create_lightweight_tag(repository_wrapper& repo); | ||
| void create_tag(repository_wrapper& repo); | ||
| std::optional<object_wrapper> get_target_obj(repository_wrapper& repo); | ||
| void handle_error(int error); | ||
|
|
||
| std::string m_delete; | ||
| std::string m_message; | ||
| std::string m_tag_name; | ||
| std::string m_target; | ||
| bool m_list_flag = false; | ||
| bool m_force_flag = false; | ||
| int m_num_lines = 0; | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function looks quite complicated for a relatively simple task. Maybe a better approach would be to first split the string at newline characters into a vector of strings and walk that vector. There is already a split-string-at-newlines function at
git2cpp/src/utils/terminal_pager.cpp
Line 199 in 4bd9b8e
which could be pulled out of that file and reused.
But that could be in a later separate PR.