|
| 1 | +#include <iostream> |
| 2 | +#include <iomanip> |
| 3 | + |
| 4 | +#include <git2/remote.h> |
| 5 | + |
| 6 | +#include "../subcommand/fetch_subcommand.hpp" |
| 7 | +#include "../wrapper/repository_wrapper.hpp" |
| 8 | +#include "../wrapper/remote_wrapper.hpp" |
| 9 | +#include "../utils/output.hpp" |
| 10 | +#include "../utils/git_exception.hpp" |
| 11 | + |
| 12 | +namespace |
| 13 | +{ |
| 14 | + int sideband_progress(const char* str, int len, void*) |
| 15 | + { |
| 16 | + printf("remote: %.*s", len, str); |
| 17 | + fflush(stdout); |
| 18 | + return 0; |
| 19 | + } |
| 20 | + |
| 21 | + int fetch_progress(const git_indexer_progress* stats, void* payload) |
| 22 | + { |
| 23 | + static bool done = false; |
| 24 | + |
| 25 | + auto* pr = reinterpret_cast<git_indexer_progress*>(payload); |
| 26 | + *pr = *stats; |
| 27 | + |
| 28 | + if (done) |
| 29 | + { |
| 30 | + return 0; |
| 31 | + } |
| 32 | + |
| 33 | + int network_percent = pr->total_objects > 0 ? |
| 34 | + (100 * pr->received_objects / pr->total_objects) |
| 35 | + : 0; |
| 36 | + size_t mbytes = pr->received_bytes / (1024*1024); |
| 37 | + |
| 38 | + std::cout << "Receiving objects: " << std::setw(4) << network_percent |
| 39 | + << "% (" << pr->received_objects << "/" << pr->total_objects << "), " |
| 40 | + << mbytes << " MiB"; |
| 41 | + |
| 42 | + if (pr->received_objects == pr->total_objects) |
| 43 | + { |
| 44 | + std::cout << ", done." << std::endl; |
| 45 | + done = true; |
| 46 | + } |
| 47 | + else |
| 48 | + { |
| 49 | + std::cout << '\r'; |
| 50 | + } |
| 51 | + return 0; |
| 52 | + } |
| 53 | + |
| 54 | + int update_refs(const char* refname, const git_oid* a, const git_oid* b, git_refspec*, void*) |
| 55 | + { |
| 56 | + char a_str[GIT_OID_SHA1_HEXSIZE+1], b_str[GIT_OID_SHA1_HEXSIZE+1]; |
| 57 | + |
| 58 | + git_oid_fmt(b_str, b); |
| 59 | + b_str[GIT_OID_SHA1_HEXSIZE] = '\0'; |
| 60 | + |
| 61 | + if (git_oid_is_zero(a)) |
| 62 | + { |
| 63 | + printf("[new] %.20s %s\n", b_str, refname); |
| 64 | + } |
| 65 | + else |
| 66 | + { |
| 67 | + git_oid_fmt(a_str, a); |
| 68 | + a_str[GIT_OID_SHA1_HEXSIZE] = '\0'; |
| 69 | + printf("[updated] %.10s..%.10s %s\n", a_str, b_str, refname); |
| 70 | + } |
| 71 | + |
| 72 | + return 0; |
| 73 | + } |
| 74 | +} |
| 75 | + |
| 76 | +fetch_subcommand::fetch_subcommand(const libgit2_object&, CLI::App& app) |
| 77 | +{ |
| 78 | + auto* sub = app.add_subcommand("fetch", "Download objects and refs from another repository"); |
| 79 | + |
| 80 | + sub->add_option("<remote>", m_remote_name, "The remote to fetch from") |
| 81 | + ->default_val("origin"); |
| 82 | + |
| 83 | + sub->callback([this]() { this->run(); }); |
| 84 | +} |
| 85 | + |
| 86 | +void fetch_subcommand::run() |
| 87 | +{ |
| 88 | + auto directory = get_current_git_path(); |
| 89 | + auto repo = repository_wrapper::open(directory); |
| 90 | + |
| 91 | + // Find the remote (default to origin if not specified) |
| 92 | + std::string remote_name = m_remote_name.empty() ? "origin" : m_remote_name; |
| 93 | + auto remote = repo.find_remote(remote_name); |
| 94 | + |
| 95 | + git_indexer_progress pd = {0}; |
| 96 | + git_fetch_options fetch_opts = GIT_FETCH_OPTIONS_INIT; |
| 97 | + fetch_opts.callbacks.sideband_progress = sideband_progress; |
| 98 | + fetch_opts.callbacks.transfer_progress = fetch_progress; |
| 99 | + fetch_opts.callbacks.payload = &pd; |
| 100 | + fetch_opts.callbacks.update_refs = update_refs; |
| 101 | + |
| 102 | + cursor_hider ch; |
| 103 | + |
| 104 | + // Perform the fetch |
| 105 | + throw_if_error(git_remote_fetch(remote, nullptr, &fetch_opts, "fetch")); |
| 106 | + |
| 107 | + // Show statistics |
| 108 | + const git_indexer_progress* stats = git_remote_stats(remote); |
| 109 | + if (stats->local_objects > 0) |
| 110 | + { |
| 111 | + std::cout << "\rReceived " << stats->indexed_objects << "/" << stats->total_objects |
| 112 | + << " objects in " << stats->received_bytes << " bytes (used " |
| 113 | + << stats->local_objects << " local objects)" << std::endl; |
| 114 | + } |
| 115 | + else |
| 116 | + { |
| 117 | + std::cout << "\rReceived " << stats->indexed_objects << "/" << stats->total_objects |
| 118 | + << " objects in " << stats->received_bytes << " bytes" << std::endl; |
| 119 | + } |
| 120 | +} |
| 121 | + |
0 commit comments