From 3226bd0a4cf4cd4b670d6604b47199a9c3c572fa Mon Sep 17 00:00:00 2001 From: Johan Mabille Date: Wed, 30 Jul 2025 15:50:12 +0200 Subject: [PATCH] Improved clone output --- CMakeLists.txt | 1 + src/subcommand/clone_subcommand.cpp | 87 ++++++++++++++++++++++++++++- src/utils/output.hpp | 20 +++++++ 3 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 src/utils/output.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d84f74b..b2e1816 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,6 +55,7 @@ set(GIT2CPP_SRC ${GIT2CPP_SOURCE_DIR}/utils/common.hpp ${GIT2CPP_SOURCE_DIR}/utils/git_exception.cpp ${GIT2CPP_SOURCE_DIR}/utils/git_exception.hpp + ${GIT2CPP_SOURCE_DIR}/utils/output.hpp ${GIT2CPP_SOURCE_DIR}/wrapper/annotated_commit_wrapper.cpp ${GIT2CPP_SOURCE_DIR}/wrapper/annotated_commit_wrapper.hpp ${GIT2CPP_SOURCE_DIR}/wrapper/branch_wrapper.cpp diff --git a/src/subcommand/clone_subcommand.cpp b/src/subcommand/clone_subcommand.cpp index 93b5f6a..6c9b803 100644 --- a/src/subcommand/clone_subcommand.cpp +++ b/src/subcommand/clone_subcommand.cpp @@ -1,6 +1,7 @@ #include #include "../subcommand/clone_subcommand.hpp" +#include "../utils/output.hpp" #include "../wrapper/repository_wrapper.hpp" clone_subcommand::clone_subcommand(const libgit2_object&, CLI::App& app) @@ -13,9 +14,90 @@ clone_subcommand::clone_subcommand(const libgit2_object&, CLI::App& app) sub->callback([this]() { this->run(); }); } +namespace +{ + int sideband_progress(const char* str, int len, void*) + { + printf("remote: %.*s", len, str); + fflush(stdout); + return 0; + } + + int fetch_progress(const git_indexer_progress* stats, void* payload) + { + static bool done = false; + + // We need to copy stats into payload even if the fetch is done, + // because the checkout_progress callback will be called with the + // same payload and needs the data to be up do date. + auto* pr = reinterpret_cast(payload); + *pr = *stats; + + if (done) + { + return 0; + } + + int network_percent = pr->total_objects > 0 ? + (100 * pr->received_objects / pr->total_objects) + : 0; + size_t mbytes = pr->received_bytes / (1024*1024); + + std::cout << "Receiving objects: " << std::setw(4) << network_percent + << "% (" << pr->received_objects << "/" << pr->total_objects << "), " + << mbytes << " MiB"; + + if (pr->received_objects == pr->total_objects) + { + std::cout << ", done." << std::endl; + done = true; + } + else + { + std::cout << '\r'; + } + return 0; + } + + void checkout_progress(const char* path, size_t cur, size_t tot, void* payload) + { + static bool done = false; + if (done) + { + return; + } + auto* pr = reinterpret_cast(payload); + int deltas_percent = pr->total_deltas > 0 ? + (100 * pr->indexed_deltas / pr->total_deltas) + : 0; + + std::cout << "Resolving deltas: " << std::setw(4) << deltas_percent + << "% (" << pr->indexed_deltas << "/" << pr->total_deltas << ")"; + if (pr->indexed_deltas == pr->total_deltas) + { + std::cout << ", done." << std::endl; + done = true; + } + else + { + std::cout << '\r'; + } + } +} + void clone_subcommand::run() { - git_clone_options opts = GIT_CLONE_OPTIONS_INIT; + git_indexer_progress pd; + git_clone_options clone_opts = GIT_CLONE_OPTIONS_INIT; + git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT; + checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE; + checkout_opts.progress_cb = checkout_progress; + checkout_opts.progress_payload = &pd; + clone_opts.checkout_opts = checkout_opts; + clone_opts.fetch_opts.callbacks.sideband_progress = sideband_progress; + clone_opts.fetch_opts.callbacks.transfer_progress = fetch_progress; + clone_opts.fetch_opts.callbacks.payload = &pd; + std::string short_name = m_directory; if (m_directory.empty()) { @@ -27,5 +109,6 @@ void clone_subcommand::run() m_directory = get_current_git_path() + '/' + short_name; } std::cout << "Cloning into '" + short_name + "'..." << std::endl; - repository_wrapper::clone(m_repository, m_directory, opts); + cursor_hider ch; + repository_wrapper::clone(m_repository, m_directory, clone_opts); } diff --git a/src/utils/output.hpp b/src/utils/output.hpp new file mode 100644 index 0000000..173c2a5 --- /dev/null +++ b/src/utils/output.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include +#include "common.hpp" + +// Scope object to hide the cursor. This avoids +// cursor twinkling when rewritting the same line +// too frequently. +struct cursor_hider : noncopyable_nonmovable +{ + cursor_hider() + { + std::cout << "\e[?25l"; + } + + ~cursor_hider() + { + std::cout << "\e[?25h"; + } +};