LCOV - code coverage report
Current view: top level - src/esys/repo/hybrid - git_hybrid.cpp (source / functions) Hit Total Coverage
Test: esysrepo_coverage.info Lines: 155 228 68.0 %
Date: 2026-08-12 14:26:50 Functions: 34 54 63.0 %

          Line data    Source code
       1             : /*!
       2             :  * \file esys/repo/hybrid/git_hybrid.cpp
       3             :  * \brief Hybrid Git backend: gitcmdline clone/fetch + libgit2 elsewhere
       4             :  *
       5             :  * \cond
       6             :  * __legal_b__
       7             :  *
       8             :  * Copyright (c) 2026 Michel Gillet
       9             :  * Distributed under the MIT License.
      10             :  * (See accompanying file LICENSE.txt or
      11             :  * copy at https://opensource.org/licenses/MIT)
      12             :  *
      13             :  * __legal_e__
      14             :  * \endcond
      15             :  *
      16             :  */
      17             : 
      18             : #include "esys/repo/esysrepo_prec.h"
      19             : #include "esys/repo/hybrid/git.h"
      20             : #include "esys/repo/gitcmdline/git.h"
      21             : #include "esys/repo/libgit2/git.h"
      22             : 
      23             : #include <chrono>
      24             : 
      25             : namespace esys::repo::hybrid
      26             : {
      27             : 
      28         147 : Git::Git()
      29         147 :     : GitBase()
      30             : {
      31         147 :     m_cmdline = std::make_shared<gitcmdline::Git>();
      32         147 :     m_libgit2 = std::make_shared<libgit2::Git>();
      33         147 : }
      34             : 
      35         147 : Git::~Git()
      36             : {
      37         147 :     if (is_open()) close();
      38         441 : }
      39             : 
      40         145 : std::shared_ptr<GitBase> Git::new_ptr()
      41             : {
      42         145 :     return std::make_shared<Git>();
      43             : }
      44             : 
      45         454 : void Git::sync_meta()
      46             : {
      47         454 :     m_cmdline->set_author(get_author());
      48         454 :     m_cmdline->set_committer(get_committer());
      49         454 :     m_cmdline->set_id(get_id());
      50         454 :     m_cmdline->set_debug(get_debug());
      51         454 :     m_cmdline->set_progress_callback(get_progress_callback());
      52             : 
      53         454 :     m_libgit2->set_author(get_author());
      54         454 :     m_libgit2->set_committer(get_committer());
      55         454 :     m_libgit2->set_id(get_id());
      56         454 :     m_libgit2->set_debug(get_debug());
      57         454 :     m_libgit2->set_progress_callback(get_progress_callback());
      58         454 : }
      59             : 
      60         157 : Result Git::refresh_libgit2()
      61             : {
      62         157 :     if (m_folder.empty()) return ESYSREPO_RESULT(ResultCode::GIT_GENERIC_ERROR, "no repository folder");
      63             : 
      64         157 :     const auto start = std::chrono::steady_clock::now();
      65             : 
      66         157 :     if (m_libgit2->is_open())
      67             :     {
      68          36 :         auto result = m_libgit2->close();
      69          36 :         if (result.error()) return ESYSREPO_RESULT(result);
      70          36 :     }
      71         157 :     auto result = m_libgit2->open(m_folder);
      72             : 
      73         314 :     m_last_refresh_ms = static_cast<uint64_t>(
      74         157 :         std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start).count());
      75             : 
      76         157 :     return result;
      77         157 : }
      78             : 
      79         169 : uint64_t Git::take_last_refresh_ms()
      80             : {
      81         169 :     const uint64_t ms = m_last_refresh_ms;
      82         169 :     m_last_refresh_ms = 0;
      83         169 :     return ms;
      84             : }
      85             : 
      86          89 : Result Git::open(const std::string &folder)
      87             : {
      88          89 :     sync_meta();
      89             : 
      90          89 :     auto result = m_cmdline->open(folder);
      91          89 :     if (result.error()) return ESYSREPO_RESULT(result);
      92             : 
      93          89 :     result = m_libgit2->open(folder);
      94          89 :     if (result.error())
      95             :     {
      96           0 :         m_cmdline->close();
      97           0 :         return ESYSREPO_RESULT(result);
      98             :     }
      99             : 
     100          89 :     m_folder = folder;
     101          89 :     open_time();
     102          89 :     return ESYSREPO_RESULT(ResultCode::OK);
     103          89 : }
     104             : 
     105         387 : bool Git::is_open()
     106             : {
     107         387 :     return m_cmdline->is_open() && m_libgit2->is_open();
     108             : }
     109             : 
     110         211 : Result Git::close()
     111             : {
     112         422 :     Result first = ESYSREPO_RESULT(ResultCode::OK);
     113             : 
     114         211 :     if (m_libgit2->is_open())
     115             :     {
     116         211 :         auto result = m_libgit2->close();
     117         211 :         if (result.error() && first.ok()) first = result;
     118         211 :     }
     119         211 :     if (m_cmdline->is_open())
     120             :     {
     121         211 :         auto result = m_cmdline->close();
     122         211 :         if (result.error() && first.ok()) first = result;
     123         211 :     }
     124             : 
     125         211 :     m_folder.clear();
     126         211 :     close_time();
     127         633 :     return first.ok() ? ESYSREPO_RESULT(ResultCode::OK) : ESYSREPO_RESULT(first);
     128         211 : }
     129             : 
     130           0 : void Git::close_on_error()
     131             : {
     132           0 :     if (is_open() || m_cmdline->is_open() || m_libgit2->is_open()) close();
     133           0 : }
     134             : 
     135         136 : Result Git::clone(const std::string &url, const std::string &path, const std::string &rev,
     136             :                   const git::CloneOptions &options)
     137             : {
     138         136 :     sync_meta();
     139             : 
     140         136 :     auto result = m_cmdline->clone(url, path, rev, options);
     141         151 :     if (result.error()) return ESYSREPO_RESULT(result);
     142             : 
     143         121 :     m_folder = path;
     144         121 :     result = refresh_libgit2();
     145         121 :     if (result.error())
     146             :     {
     147           0 :         m_cmdline->close();
     148           0 :         m_folder.clear();
     149           0 :         return ESYSREPO_RESULT(result);
     150             :     }
     151             : 
     152         121 :     open_time();
     153         121 :     return ESYSREPO_RESULT(ResultCode::OK);
     154         136 : }
     155             : 
     156          37 : Result Git::fetch(const std::string &remote)
     157             : {
     158          37 :     sync_meta();
     159             : 
     160          37 :     auto result = m_cmdline->fetch(remote);
     161          38 :     if (result.error()) return ESYSREPO_RESULT(result);
     162             : 
     163             :     // External fetch updates on-disk refs; reopen libgit2 so later ops see them.
     164          36 :     return refresh_libgit2();
     165          37 : }
     166             : 
     167           0 : Result Git::update_submodules(const std::string &path, bool recursive)
     168             : {
     169           0 :     sync_meta();
     170           0 :     return m_cmdline->update_submodules(path, recursive);
     171             : }
     172             : 
     173           0 : Result Git::init_bare(const std::string &folder_path)
     174             : {
     175           0 :     sync_meta();
     176             : 
     177           0 :     auto result = m_libgit2->init_bare(folder_path);
     178           0 :     if (result.error()) return ESYSREPO_RESULT(result);
     179             : 
     180           0 :     m_folder = folder_path;
     181           0 :     result = m_cmdline->open(folder_path);
     182           0 :     if (result.error())
     183             :     {
     184           0 :         m_libgit2->close();
     185           0 :         m_folder.clear();
     186           0 :         return ESYSREPO_RESULT(result);
     187             :     }
     188             : 
     189           0 :     open_time();
     190           0 :     return ESYSREPO_RESULT(ResultCode::OK);
     191           0 : }
     192             : 
     193           1 : Result Git::init(const std::string &folder_path)
     194             : {
     195           1 :     sync_meta();
     196             : 
     197           1 :     auto result = m_libgit2->init(folder_path);
     198           1 :     if (result.error()) return ESYSREPO_RESULT(result);
     199             : 
     200           1 :     m_folder = folder_path;
     201           1 :     result = m_cmdline->open(folder_path);
     202           1 :     if (result.error())
     203             :     {
     204           0 :         m_libgit2->close();
     205           0 :         m_folder.clear();
     206           0 :         return ESYSREPO_RESULT(result);
     207             :     }
     208             : 
     209           1 :     open_time();
     210           1 :     return ESYSREPO_RESULT(ResultCode::OK);
     211           1 : }
     212             : 
     213           1 : Result Git::get_remotes(std::vector<git::Remote> &remotes)
     214             : {
     215           1 :     sync_meta();
     216           1 :     return m_libgit2->get_remotes(remotes);
     217             : }
     218             : 
     219           6 : Result Git::get_head_branch_remote(git::Branch &branch, git::Remote &remote)
     220             : {
     221           6 :     sync_meta();
     222           6 :     return m_libgit2->get_head_branch_remote(branch, remote);
     223             : }
     224             : 
     225           1 : Result Git::add_remote(const std::string &name, const std::string &url)
     226             : {
     227           1 :     sync_meta();
     228           1 :     return m_libgit2->add_remote(name, url);
     229             : }
     230             : 
     231          47 : Result Git::get_branches(git::Branches &branches, git::BranchType branch_type)
     232             : {
     233          47 :     sync_meta();
     234          47 :     return m_libgit2->get_branches(branches, branch_type);
     235             : }
     236             : 
     237          33 : Result Git::checkout(const std::string &branch, bool force)
     238             : {
     239          33 :     sync_meta();
     240          33 :     return m_libgit2->checkout(branch, force);
     241             : }
     242             : 
     243           0 : Result Git::reset(const git::CommitHash &commit, git::ResetType type)
     244             : {
     245           0 :     sync_meta();
     246           0 :     return m_libgit2->reset(commit, type);
     247             : }
     248             : 
     249           1 : Result Git::fastforward(const git::CommitHash &commit)
     250             : {
     251           1 :     sync_meta();
     252           1 :     return m_libgit2->fastforward(commit);
     253             : }
     254             : 
     255          21 : Result Git::get_last_commit(git::CommitHash &commit)
     256             : {
     257          21 :     sync_meta();
     258          21 :     return m_libgit2->get_last_commit(commit);
     259             : }
     260             : 
     261           0 : Result Git::get_last_commit(git::Commit &commit, bool get_all_notes)
     262             : {
     263           0 :     sync_meta();
     264           0 :     return m_libgit2->get_last_commit(commit, get_all_notes);
     265             : }
     266             : 
     267           0 : Result Git::get_parent_commit(const git::CommitHash &commit, git::CommitHash &parent, int nth_parent)
     268             : {
     269           0 :     sync_meta();
     270           0 :     return m_libgit2->get_parent_commit(commit, parent, nth_parent);
     271             : }
     272             : 
     273           9 : Result Git::is_dirty(bool &dirty)
     274             : {
     275           9 :     sync_meta();
     276           9 :     return m_libgit2->is_dirty(dirty);
     277             : }
     278             : 
     279           9 : Result Git::is_detached(bool &detached)
     280             : {
     281           9 :     sync_meta();
     282           9 :     return m_libgit2->is_detached(detached);
     283             : }
     284             : 
     285          20 : Result Git::get_status(git::RepoStatus &status)
     286             : {
     287          20 :     sync_meta();
     288          20 :     return m_libgit2->get_status(status);
     289             : }
     290             : 
     291           2 : Result_t<bool> Git::is_ssh_agent_running(bool log_once)
     292             : {
     293           2 :     sync_meta();
     294           2 :     return m_libgit2->is_ssh_agent_running(log_once);
     295             : }
     296             : 
     297           2 : void Git::detect_ssh_agent(bool log_once)
     298             : {
     299           2 :     sync_meta();
     300           2 :     m_libgit2->detect_ssh_agent(log_once);
     301           2 : }
     302             : 
     303           7 : Result Git::merge_analysis(const std::vector<std::string> &refs, git::MergeAnalysisResult &merge_analysis_result,
     304             :                            std::vector<git::CommitHash> &commits)
     305             : {
     306           7 :     sync_meta();
     307           7 :     return m_libgit2->merge_analysis(refs, merge_analysis_result, commits);
     308             : }
     309             : 
     310           0 : Result Git::fetch_all_notes(const std::string &remote)
     311             : {
     312           0 :     sync_meta();
     313           0 :     return m_libgit2->fetch_all_notes(remote);
     314             : }
     315             : 
     316           7 : Result_t<bool> Git::has_branch(const std::string &name, git::BranchType branch_type)
     317             : {
     318           7 :     sync_meta();
     319           7 :     return m_libgit2->has_branch(name, branch_type);
     320             : }
     321             : 
     322          21 : Result Git::get_hash(const std::string &revision, std::string &hash, git::BranchType branch_type)
     323             : {
     324          21 :     sync_meta();
     325          21 :     return m_libgit2->get_hash(revision, hash, branch_type);
     326             : }
     327             : 
     328           0 : Result Git::walk_commits(std::shared_ptr<git::WalkCommit> walk_commit)
     329             : {
     330           0 :     sync_meta();
     331           0 :     return m_libgit2->walk_commits(walk_commit);
     332             : }
     333             : 
     334           0 : Result Git::diff(const git::CommitHash &commit_hash, std::shared_ptr<git::Diff> diff)
     335             : {
     336           0 :     sync_meta();
     337           0 :     return m_libgit2->diff(commit_hash, diff);
     338             : }
     339             : 
     340           0 : Result Git::get_ahead_behind(git::AheadBehind &ahead_behind, const git::Branch &local_branch)
     341             : {
     342           0 :     sync_meta();
     343           0 :     return m_libgit2->get_ahead_behind(ahead_behind, local_branch);
     344             : }
     345             : 
     346           3 : Result Git::get_ahead_behind(git::AheadBehind &ahead_behind, const std::string &first_ref, const std::string &second_ref)
     347             : {
     348           3 :     sync_meta();
     349           3 :     return m_libgit2->get_ahead_behind(ahead_behind, first_ref, second_ref);
     350             : }
     351             : 
     352           1 : Result Git::add_note(git::NoteId &note_id, const std::string &reference, const std::string &message, bool overwrite)
     353             : {
     354           1 :     sync_meta();
     355           1 :     return m_libgit2->add_note(note_id, reference, message, overwrite);
     356             : }
     357             : 
     358           0 : Result Git::remove_note(const git::NoteId &note_id)
     359             : {
     360           0 :     sync_meta();
     361           0 :     return m_libgit2->remove_note(note_id);
     362             : }
     363             : 
     364           0 : Result Git::remove_note(const git::CommitHash &commit_hash, const std::string &reference)
     365             : {
     366           0 :     sync_meta();
     367           0 :     return m_libgit2->remove_note(commit_hash, reference);
     368             : }
     369             : 
     370           0 : Result Git::remove_note_last_commit(const std::string &reference)
     371             : {
     372           0 :     sync_meta();
     373           0 :     return m_libgit2->remove_note_last_commit(reference);
     374             : }
     375             : 
     376           0 : Result Git::push(const std::string &remote, const std::string &src_ref, const std::string &dst_ref)
     377             : {
     378           0 :     sync_meta();
     379           0 :     return m_libgit2->push(remote, src_ref, dst_ref);
     380             : }
     381             : 
     382           0 : Result Git::push_notes(const std::string &remote, const std::string &reference)
     383             : {
     384           0 :     sync_meta();
     385           0 :     return m_libgit2->push_notes(remote, reference);
     386             : }
     387             : 
     388           0 : const std::string &Git::get_version()
     389             : {
     390           0 :     return s_get_version();
     391             : }
     392             : 
     393           0 : const std::string &Git::get_lib_name()
     394             : {
     395           0 :     return s_get_lib_name();
     396             : }
     397             : 
     398           0 : const std::string &Git::s_get_version()
     399             : {
     400           0 :     static const std::string version = "0.1.0";
     401           0 :     return version;
     402             : }
     403             : 
     404           0 : const std::string &Git::s_get_lib_name()
     405             : {
     406           0 :     static const std::string name = "hybrid";
     407           0 :     return name;
     408             : }
     409             : 
     410         336 : void Git::set_logger_if(std::shared_ptr<log::Logger_if> logger_if)
     411             : {
     412         672 :     log::User::set_logger_if(logger_if);
     413         672 :     m_cmdline->set_logger_if(logger_if);
     414         672 :     m_libgit2->set_logger_if(logger_if);
     415         336 : }
     416             : 
     417           3 : bool Git::do_is_ssh_backend_supported(git::SshBackend backend) const
     418             : {
     419           3 :     return m_libgit2->do_is_ssh_backend_supported(backend);
     420             : }
     421             : 
     422           2 : bool Git::do_is_ssh_backend_available(git::SshBackend backend, bool force)
     423             : {
     424           2 :     return m_libgit2->do_is_ssh_backend_available(backend, force);
     425             : }
     426             : 
     427           5 : Result Git::do_set_ssh_backend(git::SshBackend backend)
     428             : {
     429           5 :     return m_libgit2->do_set_ssh_backend(backend);
     430             : }
     431             : 
     432          51 : Result_t<git::SshBackend> Git::do_get_ssh_backend() const
     433             : {
     434          51 :     return m_libgit2->do_get_ssh_backend();
     435             : }
     436             : 
     437             : } // namespace esys::repo::hybrid

Generated by: LCOV version 1.14