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: 76 225 33.8 %
Date: 2026-08-01 10:43:40 Functions: 12 53 22.6 %

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

Generated by: LCOV version 1.14