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: 167 234 71.4 %
Date: 2026-09-26 15:38:18 Functions: 37 55 67.3 %

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

Generated by: LCOV version 1.14