diff --git a/src/subcommand/checkout_subcommand.cpp b/src/subcommand/checkout_subcommand.cpp index ccc24f9..cab71f0 100644 --- a/src/subcommand/checkout_subcommand.cpp +++ b/src/subcommand/checkout_subcommand.cpp @@ -30,21 +30,25 @@ checkout_subcommand::checkout_subcommand(const libgit2_object&, CLI::App& app) ); } -void print_no_switch(status_list_wrapper& sl) +namespace { - std::cout << "Your local changes to the following files would be overwritten by checkout:" << std::endl; - - for (const auto* entry : sl.get_entry_list(GIT_STATUS_WT_MODIFIED)) - { - std::cout << "\t" << entry->index_to_workdir->new_file.path << std::endl; - } - for (const auto* entry : sl.get_entry_list(GIT_STATUS_WT_DELETED)) + void print_no_switch(status_list_wrapper& sl) { - std::cout << "\t" << entry->index_to_workdir->old_file.path << std::endl; - } + std::cout << "Your local changes to the following files would be overwritten by checkout:" << std::endl; + + for (const auto* entry : sl.get_entry_list(GIT_STATUS_WT_MODIFIED)) + { + std::cout << "\t" << entry->index_to_workdir->new_file.path << std::endl; + } + for (const auto* entry : sl.get_entry_list(GIT_STATUS_WT_DELETED)) + { + std::cout << "\t" << entry->index_to_workdir->old_file.path << std::endl; + } - std::cout << "Please commit your changes or stash them before you switch branches.\nAborting" << std::endl; - return; + std::cout << "Please commit your changes or stash them before you switch branches.\nAborting" + << std::endl; + return; + } } void checkout_subcommand::run() diff --git a/src/subcommand/diff_subcommand.cpp b/src/subcommand/diff_subcommand.cpp index 4527fec..434d318 100644 --- a/src/subcommand/diff_subcommand.cpp +++ b/src/subcommand/diff_subcommand.cpp @@ -71,6 +71,129 @@ diff_subcommand::diff_subcommand(const libgit2_object&, CLI::App& app) ); } +namespace +{ + int colour_printer( + [[maybe_unused]] const git_diff_delta* delta, + [[maybe_unused]] const git_diff_hunk* hunk, + const git_diff_line* line, + void* payload + ) + { + bool use_colour = *reinterpret_cast(payload); + + // Only print origin for context/addition/deletion lines + bool print_origin = (line->origin == GIT_DIFF_LINE_CONTEXT || line->origin == GIT_DIFF_LINE_ADDITION || line->origin == GIT_DIFF_LINE_DELETION); + + if (use_colour) + { + switch (line->origin) + { + case GIT_DIFF_LINE_ADDITION: + std::cout << termcolor::green; + break; + case GIT_DIFF_LINE_DELETION: + std::cout << termcolor::red; + break; + case GIT_DIFF_LINE_ADD_EOFNL: + std::cout << termcolor::green; + break; + case GIT_DIFF_LINE_DEL_EOFNL: + std::cout << termcolor::red; + break; + case GIT_DIFF_LINE_FILE_HDR: + std::cout << termcolor::bold; + break; + case GIT_DIFF_LINE_HUNK_HDR: + std::cout << termcolor::cyan; + break; + default: + break; + } + } + + if (print_origin) + { + std::cout << line->origin; + } + + std::cout << std::string_view(line->content, line->content_len); + + if (use_colour) + { + std::cout << termcolor::reset; + } + + // Print copy/rename headers ONLY after the "diff --git" line + if (line->origin == GIT_DIFF_LINE_FILE_HDR) + { + if (delta->status == GIT_DELTA_COPIED) + { + if (use_colour) + { + std::cout << termcolor::bold; + } + std::cout << "similarity index " << delta->similarity << "%\n"; + std::cout << "copy from " << delta->old_file.path << "\n"; + std::cout << "copy to " << delta->new_file.path << "\n"; + if (use_colour) + { + std::cout << termcolor::reset; + } + } + else if (delta->status == GIT_DELTA_RENAMED) + { + if (use_colour) + { + std::cout << termcolor::bold; + } + std::cout << "similarity index " << delta->similarity << "%\n"; + std::cout << "rename from " << delta->old_file.path << "\n"; + std::cout << "rename to " << delta->new_file.path << "\n"; + if (use_colour) + { + std::cout << termcolor::reset; + } + } + } + + return 0; + } + + diff_wrapper compute_diff_no_index(std::vector files, git_diff_options& diffopts) + { + if (files.size() != 2) + { + throw git_exception( + "usage: git diff --no-index [] [...]", + git2cpp_error_code::BAD_ARGUMENT + ); + } + + git_diff_options_init(&diffopts, GIT_DIFF_OPTIONS_VERSION); + + std::string file1_str = read_file(files[0]); + std::string file2_str = read_file(files[1]); + + if (file1_str.empty()) + { + throw git_exception("Cannot read file: " + files[0], git2cpp_error_code::GENERIC_ERROR); + } + if (file2_str.empty()) + { + throw git_exception("Cannot read file: " + files[1], git2cpp_error_code::GENERIC_ERROR); + } + + auto patch = patch_wrapper::patch_from_files(files[0], file1_str, files[1], file2_str, &diffopts); + auto buf = patch.to_buf(); + auto diff = diff_wrapper::diff_from_buffer(buf); + + git_buf_dispose(&buf); + + return diff; + } +} + void print_stats( const diff_wrapper& diff, bool use_colour, @@ -170,93 +293,6 @@ void print_stats( git_buf_dispose(&buf); } -static int colour_printer( - [[maybe_unused]] const git_diff_delta* delta, - [[maybe_unused]] const git_diff_hunk* hunk, - const git_diff_line* line, - void* payload -) -{ - bool use_colour = *reinterpret_cast(payload); - - // Only print origin for context/addition/deletion lines - bool print_origin = (line->origin == GIT_DIFF_LINE_CONTEXT || line->origin == GIT_DIFF_LINE_ADDITION || line->origin == GIT_DIFF_LINE_DELETION); - - if (use_colour) - { - switch (line->origin) - { - case GIT_DIFF_LINE_ADDITION: - std::cout << termcolor::green; - break; - case GIT_DIFF_LINE_DELETION: - std::cout << termcolor::red; - break; - case GIT_DIFF_LINE_ADD_EOFNL: - std::cout << termcolor::green; - break; - case GIT_DIFF_LINE_DEL_EOFNL: - std::cout << termcolor::red; - break; - case GIT_DIFF_LINE_FILE_HDR: - std::cout << termcolor::bold; - break; - case GIT_DIFF_LINE_HUNK_HDR: - std::cout << termcolor::cyan; - break; - default: - break; - } - } - - if (print_origin) - { - std::cout << line->origin; - } - - std::cout << std::string_view(line->content, line->content_len); - - if (use_colour) - { - std::cout << termcolor::reset; - } - - // Print copy/rename headers ONLY after the "diff --git" line - if (line->origin == GIT_DIFF_LINE_FILE_HDR) - { - if (delta->status == GIT_DELTA_COPIED) - { - if (use_colour) - { - std::cout << termcolor::bold; - } - std::cout << "similarity index " << delta->similarity << "%\n"; - std::cout << "copy from " << delta->old_file.path << "\n"; - std::cout << "copy to " << delta->new_file.path << "\n"; - if (use_colour) - { - std::cout << termcolor::reset; - } - } - else if (delta->status == GIT_DELTA_RENAMED) - { - if (use_colour) - { - std::cout << termcolor::bold; - } - std::cout << "similarity index " << delta->similarity << "%\n"; - std::cout << "rename from " << delta->old_file.path << "\n"; - std::cout << "rename to " << delta->new_file.path << "\n"; - if (use_colour) - { - std::cout << termcolor::reset; - } - } - } - - return 0; -} - void diff_subcommand::print_diff(diff_wrapper& diff, bool use_colour) { if (m_stat_flag || m_shortstat_flag || m_numstat_flag || m_summary_flag) @@ -307,39 +343,6 @@ void diff_subcommand::print_diff(diff_wrapper& diff, bool use_colour) diff.print(format, colour_printer, &use_colour); } -diff_wrapper compute_diff_no_index(std::vector files, git_diff_options& diffopts) -{ - if (files.size() != 2) - { - throw git_exception( - "usage: git diff --no-index [] [...]", - git2cpp_error_code::BAD_ARGUMENT - ); - } - - git_diff_options_init(&diffopts, GIT_DIFF_OPTIONS_VERSION); - - std::string file1_str = read_file(files[0]); - std::string file2_str = read_file(files[1]); - - if (file1_str.empty()) - { - throw git_exception("Cannot read file: " + files[0], git2cpp_error_code::GENERIC_ERROR); - } - if (file2_str.empty()) - { - throw git_exception("Cannot read file: " + files[1], git2cpp_error_code::GENERIC_ERROR); - } - - auto patch = patch_wrapper::patch_from_files(files[0], file1_str, files[1], file2_str, &diffopts); - auto buf = patch.to_buf(); - auto diff = diff_wrapper::diff_from_buffer(buf); - - git_buf_dispose(&buf); - - return diff; -} - void diff_subcommand::run() { git_diff_options diffopts; diff --git a/src/subcommand/log_subcommand.cpp b/src/subcommand/log_subcommand.cpp index 98e50d8..bf1840b 100644 --- a/src/subcommand/log_subcommand.cpp +++ b/src/subcommand/log_subcommand.cpp @@ -52,198 +52,201 @@ log_subcommand::log_subcommand(const libgit2_object&, CLI::App& app) ); }; -void print_time(git_time intime, std::string prefix) +namespace { - char sign, out[32]; - struct tm* intm; - int offset, hours, minutes; - time_t t; - - offset = intime.offset; - if (offset < 0) + void print_time(git_time intime, std::string prefix) { - sign = '-'; - offset = -offset; - } - else - { - sign = '+'; - } + char sign, out[32]; + struct tm* intm; + int offset, hours, minutes; + time_t t; - hours = offset / 60; - minutes = offset % 60; - - t = (time_t) intime.time + (intime.offset * 60); + offset = intime.offset; + if (offset < 0) + { + sign = '-'; + offset = -offset; + } + else + { + sign = '+'; + } - intm = gmtime(&t); - strftime(out, sizeof(out), "%a %b %e %T %Y", intm); + hours = offset / 60; + minutes = offset % 60; - std::cout << prefix << out << " " << sign << std::format("{:02d}", hours) - << std::format("{:02d}", minutes) << std::endl; -} + t = (time_t) intime.time + (intime.offset * 60); -std::vector get_tags_for_commit(repository_wrapper& repo, const git_oid& commit_oid) -{ - std::vector tags; - git_strarray tag_names = {0}; + intm = gmtime(&t); + strftime(out, sizeof(out), "%a %b %e %T %Y", intm); - if (git_tag_list(&tag_names, repo) != 0) - { - return tags; + std::cout << prefix << out << " " << sign << std::format("{:02d}", hours) + << std::format("{:02d}", minutes) << std::endl; } - for (size_t i = 0; i < tag_names.count; i++) + std::vector get_tags_for_commit(repository_wrapper& repo, const git_oid& commit_oid) { - std::string tag_name = tag_names.strings[i]; - std::string ref_name = "refs/tags/" + std::string(tag_name); + std::vector tags; + git_strarray tag_names = {0}; - reference_wrapper tag_ref = repo.find_reference(ref_name); - object_wrapper peeled = tag_ref.peel(); - - if (git_oid_equal(&peeled.oid(), &commit_oid)) + if (git_tag_list(&tag_names, repo) != 0) { - tags.push_back(std::string(tag_name)); + return tags; } - } - git_strarray_dispose(&tag_names); // TODO: refactor git_strarray_wrapper to use it here - return tags; -} - -std::vector get_branches_for_commit( - repository_wrapper& repo, - git_branch_t type, - const git_oid& commit_oid, - const std::string exclude_branch -) -{ - std::vector branches; + for (size_t i = 0; i < tag_names.count; i++) + { + std::string tag_name = tag_names.strings[i]; + std::string ref_name = "refs/tags/" + std::string(tag_name); - auto branch_iter = repo.iterate_branches(type); - while (auto branch = branch_iter.next()) - { - const git_oid* branch_target = nullptr; - git_reference* ref = branch.value(); + reference_wrapper tag_ref = repo.find_reference(ref_name); + object_wrapper peeled = tag_ref.peel(); - if (git_reference_type(ref) == GIT_REFERENCE_DIRECT) - { - branch_target = git_reference_target(ref); - } - else if (git_reference_type(ref) == GIT_REFERENCE_SYMBOLIC) - { - git_reference* resolved = nullptr; - if (git_reference_resolve(&resolved, ref) == 0) + if (git_oid_equal(&peeled.oid(), &commit_oid)) { - branch_target = git_reference_target(resolved); - git_reference_free(resolved); + tags.push_back(std::string(tag_name)); } } - if (branch_target && git_oid_equal(branch_target, &commit_oid)) + git_strarray_dispose(&tag_names); // TODO: refactor git_strarray_wrapper to use it here + return tags; + } + + std::vector get_branches_for_commit( + repository_wrapper& repo, + git_branch_t type, + const git_oid& commit_oid, + const std::string exclude_branch + ) + { + std::vector branches; + + auto branch_iter = repo.iterate_branches(type); + while (auto branch = branch_iter.next()) { - std::string branch_name(branch->name()); - if (type == GIT_BRANCH_LOCAL) + const git_oid* branch_target = nullptr; + git_reference* ref = branch.value(); + + if (git_reference_type(ref) == GIT_REFERENCE_DIRECT) + { + branch_target = git_reference_target(ref); + } + else if (git_reference_type(ref) == GIT_REFERENCE_SYMBOLIC) { - if (branch_name != exclude_branch) + git_reference* resolved = nullptr; + if (git_reference_resolve(&resolved, ref) == 0) { - branches.push_back(branch_name); + branch_target = git_reference_target(resolved); + git_reference_free(resolved); } } - else + + if (branch_target && git_oid_equal(branch_target, &commit_oid)) { - branches.push_back(branch_name); + std::string branch_name(branch->name()); + if (type == GIT_BRANCH_LOCAL) + { + if (branch_name != exclude_branch) + { + branches.push_back(branch_name); + } + } + else + { + branches.push_back(branch_name); + } } } - } - return branches; -} - -struct commit_refs -{ - std::string head_branch; - std::vector tags; - std::vector local_branches; - std::vector remote_branches; + return branches; + } - bool has_refs() const + struct commit_refs { - return !head_branch.empty() || !tags.empty() || !local_branches.empty() || !remote_branches.empty(); - } -}; + std::string head_branch; + std::vector tags; + std::vector local_branches; + std::vector remote_branches; -commit_refs get_refs_for_commit(repository_wrapper& repo, const git_oid& commit_oid) -{ - commit_refs refs; + bool has_refs() const + { + return !head_branch.empty() || !tags.empty() || !local_branches.empty() || !remote_branches.empty(); + } + }; - if (!repo.is_head_unborn()) + commit_refs get_refs_for_commit(repository_wrapper& repo, const git_oid& commit_oid) { - auto head = repo.head(); - auto head_taget = head.target(); - if (git_oid_equal(head_taget, &commit_oid)) + commit_refs refs; + + if (!repo.is_head_unborn()) { - refs.head_branch = head.short_name(); + auto head = repo.head(); + auto head_taget = head.target(); + if (git_oid_equal(head_taget, &commit_oid)) + { + refs.head_branch = head.short_name(); + } } - } - refs.tags = get_tags_for_commit(repo, commit_oid); - refs.local_branches = get_branches_for_commit(repo, GIT_BRANCH_LOCAL, commit_oid, refs.head_branch); - refs.remote_branches = get_branches_for_commit(repo, GIT_BRANCH_REMOTE, commit_oid, ""); + refs.tags = get_tags_for_commit(repo, commit_oid); + refs.local_branches = get_branches_for_commit(repo, GIT_BRANCH_LOCAL, commit_oid, refs.head_branch); + refs.remote_branches = get_branches_for_commit(repo, GIT_BRANCH_REMOTE, commit_oid, ""); - return refs; -} + return refs; + } -void print_refs(const commit_refs& refs) -{ - if (!refs.has_refs()) + void print_refs(const commit_refs& refs) { - return; - } + if (!refs.has_refs()) + { + return; + } - std::cout << termcolor::yellow; - std::cout << " ("; + std::cout << termcolor::yellow; + std::cout << " ("; - bool first = true; + bool first = true; - if (!refs.head_branch.empty()) - { - std::cout << termcolor::bold << termcolor::cyan << "HEAD" << termcolor::reset << termcolor::yellow - << " -> " << termcolor::reset << termcolor::bold << termcolor::green << refs.head_branch - << termcolor::reset << termcolor::yellow; - first = false; - } + if (!refs.head_branch.empty()) + { + std::cout << termcolor::bold << termcolor::cyan << "HEAD" << termcolor::reset << termcolor::yellow + << " -> " << termcolor::reset << termcolor::bold << termcolor::green << refs.head_branch + << termcolor::reset << termcolor::yellow; + first = false; + } - for (const auto& tag : refs.tags) - { - if (!first) + for (const auto& tag : refs.tags) { - std::cout << ", "; + if (!first) + { + std::cout << ", "; + } + std::cout << termcolor::bold << "tag: " << tag << termcolor::reset << termcolor::yellow; + first = false; } - std::cout << termcolor::bold << "tag: " << tag << termcolor::reset << termcolor::yellow; - first = false; - } - for (const auto& remote : refs.remote_branches) - { - if (!first) + for (const auto& remote : refs.remote_branches) { - std::cout << ", "; + if (!first) + { + std::cout << ", "; + } + std::cout << termcolor::bold << termcolor::red << remote << termcolor::reset << termcolor::yellow; + first = false; } - std::cout << termcolor::bold << termcolor::red << remote << termcolor::reset << termcolor::yellow; - first = false; - } - for (const auto& local : refs.local_branches) - { - if (!first) + for (const auto& local : refs.local_branches) { - std::cout << ", "; + if (!first) + { + std::cout << ", "; + } + std::cout << termcolor::bold << termcolor::green << local << termcolor::reset << termcolor::yellow; + first = false; } - std::cout << termcolor::bold << termcolor::green << local << termcolor::reset << termcolor::yellow; - first = false; - } - std::cout << ")" << termcolor::reset; + std::cout << ")" << termcolor::reset; + } } void log_subcommand::print_commit(repository_wrapper& repo, const commit_wrapper& commit) diff --git a/src/subcommand/merge_subcommand.cpp b/src/subcommand/merge_subcommand.cpp index 74f7abb..41f1a46 100644 --- a/src/subcommand/merge_subcommand.cpp +++ b/src/subcommand/merge_subcommand.cpp @@ -47,31 +47,67 @@ merge_subcommand::merge_subcommand(const libgit2_object&, CLI::App& app) ); } -annotated_commit_list_wrapper merge_subcommand::resolve_heads(const repository_wrapper& repo) +namespace { - std::vector commits_to_merge; - commits_to_merge.reserve(m_branches_to_merge.size()); - - for (const auto branch_name : m_branches_to_merge) + annotated_commit_list_wrapper + resolve_mergeheads(const repository_wrapper& repo, const std::vector& oid_list) { - std::optional commit = repo.resolve_local_ref(branch_name); - if (commit.has_value()) + std::vector commits_to_merge; + commits_to_merge.reserve(oid_list.size()); + + for (const auto& id : oid_list) { - commits_to_merge.push_back(std::move(commit).value()); + std::optional commit = repo.find_annotated_commit(id); + if (commit.has_value()) + { + commits_to_merge.push_back(std::move(commit).value()); + } } + return annotated_commit_list_wrapper(std::move(commits_to_merge)); + } + + void perform_fastforward(repository_wrapper& repo, const git_oid& target_oid, int is_unborn) + { + const git_checkout_options ff_checkout_options = GIT_CHECKOUT_OPTIONS_INIT; + + auto lambda_get_target_ref = [](auto repo, auto is_unborn) + { + if (!is_unborn) + { + return repo->head(); + } + else + { + return repo->find_reference("HEAD"); + } + }; + reference_wrapper target_ref = lambda_get_target_ref(&repo, is_unborn); + + object_wrapper target = repo.find_object(target_oid, GIT_OBJECT_COMMIT); + + repo.checkout_tree(target, ff_checkout_options); + + target_ref.write_new_ref(target_oid); + } + + // This function is used as a callback in git_repository_mergehead_foreach and therefore its type must be + // git_repository_mergehead_foreach_cb. + int populate_list(const git_oid* oid, void* payload) + { + auto* l = reinterpret_cast*>(payload); + l->push_back(*oid); + return 0; } - return annotated_commit_list_wrapper(std::move(commits_to_merge)); } -annotated_commit_list_wrapper -resolve_mergeheads(const repository_wrapper& repo, const std::vector& oid_list) +annotated_commit_list_wrapper merge_subcommand::resolve_heads(const repository_wrapper& repo) { std::vector commits_to_merge; - commits_to_merge.reserve(oid_list.size()); + commits_to_merge.reserve(m_branches_to_merge.size()); - for (const auto& id : oid_list) + for (const auto branch_name : m_branches_to_merge) { - std::optional commit = repo.find_annotated_commit(id); + std::optional commit = repo.resolve_local_ref(branch_name); if (commit.has_value()) { commits_to_merge.push_back(std::move(commit).value()); @@ -80,30 +116,6 @@ resolve_mergeheads(const repository_wrapper& repo, const std::vector& o return annotated_commit_list_wrapper(std::move(commits_to_merge)); } -void perform_fastforward(repository_wrapper& repo, const git_oid& target_oid, int is_unborn) -{ - const git_checkout_options ff_checkout_options = GIT_CHECKOUT_OPTIONS_INIT; - - auto lambda_get_target_ref = [](auto repo, auto is_unborn) - { - if (!is_unborn) - { - return repo->head(); - } - else - { - return repo->find_reference("HEAD"); - } - }; - reference_wrapper target_ref = lambda_get_target_ref(&repo, is_unborn); - - object_wrapper target = repo.find_object(target_oid, GIT_OBJECT_COMMIT); - - repo.checkout_tree(target, ff_checkout_options); - - target_ref.write_new_ref(target_oid); -} - void merge_subcommand::create_merge_commit( repository_wrapper& repo, const index_wrapper& index, @@ -148,15 +160,6 @@ void merge_subcommand::create_merge_commit( repo.state_cleanup(); } -// This function is used as a callback in git_repository_mergehead_foreach and therefore its type must be -// git_repository_mergehead_foreach_cb. -int populate_list(const git_oid* oid, void* payload) -{ - auto* l = reinterpret_cast*>(payload); - l->push_back(*oid); - return 0; -} - void merge_subcommand::run() { auto directory = get_current_git_path(); diff --git a/src/subcommand/push_subcommand.cpp b/src/subcommand/push_subcommand.cpp index 5a1243c..ea8fd4a 100644 --- a/src/subcommand/push_subcommand.cpp +++ b/src/subcommand/push_subcommand.cpp @@ -33,6 +33,89 @@ push_subcommand::push_subcommand(const libgit2_object&, CLI::App& app) ); } +namespace +{ + std::unordered_map get_remotes(repository_wrapper& repo, std::string remote_name) + { + std::vector repo_refs = repo.refs_list(); + std::unordered_map remotes_oids; + const std::string prefix = std::string("refs/remotes/") + remote_name + "/"; + for (const auto& r : repo_refs) + { + if (r.size() > prefix.size() && r.compare(0, prefix.size(), prefix) == 0) + { + // r is like "refs/remotes/origin/main" + std::string short_name = r.substr(prefix.size()); // "main" or "feature/x" + + git_oid oid = repo.ref_name_to_id(r); + remotes_oids.emplace(short_name, oid); + } + } + return remotes_oids; + } + + std::unordered_map diff_branches( + std::unordered_map remotes_before_push, + std::unordered_map remotes_after_push + ) + { + std::unordered_map new_branches; + for (const auto& br : remotes_after_push) + { + const std::string name = br.first; + const git_oid& oid = br.second; + if (!remotes_before_push.contains(name)) + { + new_branches.emplace(name, oid); + } + } + return new_branches; + } + + std::pair, std::vector> + split_refspecs(std::vector refspecs, std::unordered_map new_branches) + { + std::vector new_pushed_refspecs; + std::vector existing_refspecs; + + for (const auto refspec : refspecs) + { + if (!new_branches.contains(refspec)) + { + existing_refspecs.push_back(refspec); + } + else + { + new_pushed_refspecs.push_back(refspec); + } + } + + return std::make_pair(new_pushed_refspecs, existing_refspecs); + } + + std::pair + get_branch_names(repository_wrapper& repo, std::string remote_name, std::string refspec) + { + std::optional upstream_opt = repo.branch_upstream_name(refspec); + std::string remote_branch = refspec; + if (upstream_opt.has_value()) + { + const std::string up_name = upstream_opt.value(); + auto pos = up_name.find('/'); + if (pos != std::string::npos && pos + 1 < up_name.size()) + { + std::string up_remote = up_name.substr(0, pos); + std::string up_branch = up_name.substr(pos + 1); + if (up_remote == remote_name) + { + remote_branch = up_branch; + } + } + } + return std::make_pair(refspec, remote_branch); + } +} + void push_subcommand::fill_refspec(repository_wrapper& repo) { const std::string prefix = std::string("refs/heads/"); @@ -78,86 +161,6 @@ void push_subcommand::fill_refspec(repository_wrapper& repo) } } -std::unordered_map get_remotes(repository_wrapper& repo, std::string remote_name) -{ - std::vector repo_refs = repo.refs_list(); - std::unordered_map remotes_oids; - const std::string prefix = std::string("refs/remotes/") + remote_name + "/"; - for (const auto& r : repo_refs) - { - if (r.size() > prefix.size() && r.compare(0, prefix.size(), prefix) == 0) - { - // r is like "refs/remotes/origin/main" - std::string short_name = r.substr(prefix.size()); // "main" or "feature/x" - - git_oid oid = repo.ref_name_to_id(r); - remotes_oids.emplace(short_name, oid); - } - } - return remotes_oids; -} - -std::unordered_map diff_branches( - std::unordered_map remotes_before_push, - std::unordered_map remotes_after_push -) -{ - std::unordered_map new_branches; - for (const auto& br : remotes_after_push) - { - const std::string name = br.first; - const git_oid& oid = br.second; - if (!remotes_before_push.contains(name)) - { - new_branches.emplace(name, oid); - } - } - return new_branches; -} - -std::pair, std::vector> -split_refspecs(std::vector refspecs, std::unordered_map new_branches) -{ - std::vector new_pushed_refspecs; - std::vector existing_refspecs; - - for (const auto refspec : refspecs) - { - if (!new_branches.contains(refspec)) - { - existing_refspecs.push_back(refspec); - } - else - { - new_pushed_refspecs.push_back(refspec); - } - } - - return std::make_pair(new_pushed_refspecs, existing_refspecs); -} - -std::pair -get_branch_names(repository_wrapper& repo, std::string remote_name, std::string refspec) -{ - std::optional upstream_opt = repo.branch_upstream_name(refspec); - std::string remote_branch = refspec; - if (upstream_opt.has_value()) - { - const std::string up_name = upstream_opt.value(); - auto pos = up_name.find('/'); - if (pos != std::string::npos && pos + 1 < up_name.size()) - { - std::string up_remote = up_name.substr(0, pos); - std::string up_branch = up_name.substr(pos + 1); - if (up_remote == remote_name) - { - remote_branch = up_branch; - } - } - } - return std::make_pair(refspec, remote_branch); -} - void push_subcommand::run() { wasm_http_transport_scope transport; // Enables wasm http(s) transport. diff --git a/src/subcommand/rebase_subcommand.cpp b/src/subcommand/rebase_subcommand.cpp index 4bedcc5..8e17b7c 100644 --- a/src/subcommand/rebase_subcommand.cpp +++ b/src/subcommand/rebase_subcommand.cpp @@ -31,11 +31,14 @@ rebase_subcommand::rebase_subcommand(const libgit2_object&, CLI::App& app) ); } -void ensure_rebase_in_progress(git_repository_state_t state) +namespace { - if (state != GIT_REPOSITORY_STATE_REBASE_INTERACTIVE && state != GIT_REPOSITORY_STATE_REBASE_MERGE) + void ensure_rebase_in_progress(git_repository_state_t state) { - throw std::runtime_error("No rebase in progress"); + if (state != GIT_REPOSITORY_STATE_REBASE_INTERACTIVE && state != GIT_REPOSITORY_STATE_REBASE_MERGE) + { + throw std::runtime_error("No rebase in progress"); + } } } diff --git a/src/subcommand/tag_subcommand.cpp b/src/subcommand/tag_subcommand.cpp index 192b0d1..58c2041 100644 --- a/src/subcommand/tag_subcommand.cpp +++ b/src/subcommand/tag_subcommand.cpp @@ -27,104 +27,107 @@ tag_subcommand::tag_subcommand(const libgit2_object&, CLI::App& app) ); } -// Tag listing: Print individual message lines -void print_list_lines(const std::string& message, int num_lines) +namespace { - if (message.empty()) + // Tag listing: Print individual message lines + void print_list_lines(const std::string& message, int num_lines) { - return; - } + if (message.empty()) + { + return; + } - auto lines = split_input_at_newlines(message); + auto lines = split_input_at_newlines(message); - // header - std::cout << lines[0]; + // 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++) + // other lines + if (num_lines <= 1 || lines.size() <= 2) + { + std::cout << std::endl; + } + else { - if (i < num_lines) + for (size_t i = 1; i < lines.size(); i++) { - std::cout << "\n\t\t" << lines[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) + // Tag listing: Print an actual tag object + void print_tag(git_tag* tag, int num_lines) { - std::string msg = git_tag_message(tag); - if (!msg.empty()) + std::cout << std::left << std::setw(16) << git_tag_name(tag); + + if (num_lines) { - print_list_lines(msg, 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; } } - 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) + // Tag listing: Print a commit (target of a lightweight tag) + void print_commit(git_commit* commit, std::string name, int num_lines) { - std::string msg = git_commit_message(commit); - if (!msg.empty()) + std::cout << std::left << std::setw(16) << name; + + if (num_lines) { - print_list_lines(msg, 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; } } - 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()) + // 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) { - switch (git_object_type(obj.value())) + auto obj = repo.revparse_single(name); + + if (obj.has_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; + 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; } - } - else - { - std::cout << name << std::endl; } }