LCOV - code coverage report
Current view: top level - src/esys/repo/exe - cmdmulti.cpp (source / functions) Hit Total Coverage
Test: esysrepo_coverage.info Lines: 88 218 40.4 %
Date: 2026-08-01 10:43:40 Functions: 6 10 60.0 %

          Line data    Source code
       1             : /*!
       2             :  * \file esys/repo/exe/cmdmulti.cpp
       3             :  * \brief
       4             :  *
       5             :  * \cond
       6             :  * __legal_b__
       7             :  *
       8             :  * Copyright (c) 2023 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/exe/cmdmulti.h"
      20             : #include "esys/repo/manifest/capture.h"
      21             : #include "esys/repo/manifest/multifilexml.h"
      22             : #include "esys/repo/manifest/repository.h"
      23             : #include "esys/repo/workspace.h"
      24             : 
      25             : #include <boost/filesystem.hpp>
      26             : 
      27             : #include <iomanip>
      28             : #include <sstream>
      29             : 
      30             : namespace esys::repo::exe
      31             : {
      32             : 
      33          17 : CmdMulti::CmdMulti()
      34          17 :     : Cmd("Multi")
      35             : {
      36          17 : }
      37             : 
      38          17 : CmdMulti::~CmdMulti() = default;
      39             : 
      40           1 : void CmdMulti::set_task(Task task)
      41             : {
      42           1 :     m_task = task;
      43           1 : }
      44             : 
      45           1 : CmdMulti::Task CmdMulti::get_task() const
      46             : {
      47           1 :     return m_task;
      48             : }
      49             : 
      50           1 : Result CmdMulti::impl_run()
      51             : {
      52           1 :     switch (get_task())
      53             :     {
      54           1 :         case Task::CAPTURE: return do_task_capture();
      55           0 :         case Task::DELETE: return do_task_delete();
      56           0 :         case Task::PUSH: return do_task_push();
      57           0 :         case Task::SHOW: return do_task_show();
      58           0 :         default: return ResultCode::GENERIC_ERROR;
      59             :     }
      60             : }
      61             : 
      62           1 : Result CmdMulti::do_task_capture()
      63             : {
      64           1 :     Workspace workspace;
      65           1 :     std::string folder_path;
      66             : 
      67           1 :     if (!get_folder().empty() && get_workspace_path().empty())
      68           0 :         folder_path = get_folder();
      69           1 :     else if (!get_workspace_path().empty())
      70           1 :         folder_path = get_workspace_path();
      71             :     else
      72           0 :         folder_path = boost::filesystem::current_path().string();
      73             : 
      74           1 :     Result result = workspace.load(folder_path);
      75           1 :     if (result.error()) return ESYSREPO_RESULT(result);
      76             : 
      77           1 :     set_workspace_path(workspace.get_path());
      78             : 
      79           1 :     Result rresult;
      80           1 :     git::Branches branches;
      81           1 :     git::AheadBehind ahead_behind;
      82           1 :     auto git_helper = new_git_helper();
      83           1 :     std::ostringstream info_oss;
      84           1 :     auto capture = std::make_shared<manifest::Capture>();
      85             : 
      86           3 :     for (auto location : workspace.get_manifest()->get_locations())
      87             :     {
      88           7 :         for (auto repo : location->get_repos())
      89             :         {
      90          12 :             info_oss.str("");
      91           6 :             info_oss << "repo '" << repo->get_path() << "'" << std::endl;
      92             : 
      93          12 :             std::string repo_full_path = workspace.get_full_path(repo);
      94           6 :             result = git_helper->open(repo_full_path, log::Level::DEBUG);
      95           6 :             GitHelper::AutoClose auto_close(git_helper.get());
      96           6 :             if (result.error())
      97             :             {
      98           0 :                 info_oss << "    failed to open the git repository";
      99           0 :                 error(info_oss.str());
     100             : 
     101           0 :                 rresult.add(ESYSREPO_RESULT(ResultCode::GIT_ERROR_OPENING_REPO, repo_full_path));
     102           0 :                 continue;
     103             :             }
     104             : 
     105           6 :             git::Branch head_branch;
     106           6 :             git::Remote head_remote;
     107           6 :             result = git_helper->get_git()->get_head_branch_remote(head_branch, head_remote);
     108           6 :             if (result.error())
     109             :             {
     110           2 :                 info_oss << "    couldn't get the information about the HEAD";
     111           2 :                 error(info_oss.str());
     112             : 
     113           2 :                 rresult.add(ESYSREPO_RESULT(result));
     114           2 :                 continue;
     115             :             }
     116             : 
     117           7 :             if ((repo->get_location_str() != head_remote.get_name()) || (repo->get_url() != head_remote.get_url()))
     118             :             {
     119           1 :                 auto item = std::make_shared<manifest::Capture::Item>();
     120             : 
     121           1 :                 item->set_path(repo->get_path());
     122           1 :                 item->set_remote_name(head_remote.get_name());
     123           1 :                 item->set_remote_url(head_remote.get_url());
     124           1 :                 item->set_revision(head_branch.get_name());
     125           2 :                 capture->add_item(item);
     126           1 :                 continue;
     127           1 :             }
     128             : 
     129           9 :             auto revision = workspace.get_manifest()->get_repo_revision(repo);
     130           3 :             result = git_helper->get_git()->get_ahead_behind(ahead_behind, head_branch.get_name(), revision);
     131           3 :             if (result.error())
     132             :             {
     133           0 :                 info_oss << "    failed to get the number of commits ahead or behind between local branch and branch "
     134           0 :                             "in manifest";
     135           0 :                 error(info_oss.str());
     136             : 
     137           0 :                 rresult.add(ESYSREPO_RESULT(result, repo->get_path()));
     138           0 :                 continue;
     139             :             }
     140             : 
     141           3 :             if ((ahead_behind.get_ahead() == 0) && (ahead_behind.get_behind() == 0))
     142             :             {
     143           3 :                 info_oss << "    HEAD is at the same position as the revision in the manifest";
     144           3 :                 info(info_oss.str());
     145           3 :                 continue;
     146             :             }
     147             : 
     148             :             // Check if the local branch is ahead or behind of the upstream
     149           0 :             result = git_helper->get_git()->get_ahead_behind(ahead_behind, head_branch);
     150           0 :             if (result.error())
     151             :             {
     152           0 :                 info_oss
     153           0 :                     << "    failed to get the number of commits ahead or behind between local and upstream branches";
     154           0 :                 error(info_oss.str());
     155           0 :                 rresult.add(ESYSREPO_RESULT(result, repo_full_path));
     156           0 :                 continue;
     157             :             }
     158             : 
     159           0 :             if (ahead_behind.get_ahead() != 0)
     160             :             {
     161           0 :                 std::ostringstream oss;
     162             : 
     163           0 :                 info_oss << "    local branch is " << ahead_behind.get_ahead()
     164           0 :                          << " commits ahead of the remote. Push your changes.";
     165           0 :                 error(info_oss.str());
     166           0 :                 continue;
     167           0 :             }
     168             : 
     169           0 :             capture->add_item(repo->get_path(), head_branch.get_name());
     170          12 :         }
     171           1 :     }
     172             : 
     173           1 :     if (rresult.error()) return ESYSREPO_RESULT(rresult);
     174             : 
     175           1 :     if (capture->get_items().empty())
     176             :     {
     177           0 :         info("Nothing to capture.");
     178           0 :         return ESYSREPO_RESULT(ResultCode::OK);
     179             :     }
     180             : 
     181           1 :     manifest::MultiFileXML file_xml;
     182           1 :     std::ostringstream oss;
     183             : 
     184           2 :     file_xml.set_capture(capture);
     185           1 :     result = file_xml.write(oss);
     186           1 :     if (result.error()) return ESYSREPO_RESULT(result);
     187             : 
     188           2 :     git_helper = new_git_helper();
     189             : 
     190           1 :     if (!get_folder().empty())
     191           0 :         folder_path = get_folder();
     192           1 :     else if (!get_workspace_path().empty())
     193           1 :         folder_path = get_workspace_path();
     194             :     else
     195           0 :         folder_path = boost::filesystem::current_path().string();
     196             : 
     197           1 :     if (folder_path.empty()) return ESYSREPO_RESULT(ResultCode::INTERNAL_ERROR);
     198             : 
     199           1 :     folder_path = find_git_root_path(folder_path);
     200             : 
     201           1 :     result = git_helper->open(folder_path, log::Level::DEBUG);
     202           1 :     if (result.error()) return ESYSREPO_RESULT(result);
     203             : 
     204           1 :     GitHelper::AutoClose auto_close(git_helper.get());
     205             : 
     206           1 :     auto person = std::make_shared<git::Person>();
     207           1 :     std::string email = "esysrepo@libesys.org";
     208           1 :     std::string name = "ESysRepo";
     209           1 :     person->set_email(email);
     210           1 :     person->set_name(name);
     211           3 :     git_helper->get_git()->set_author(person);
     212             : 
     213           1 :     git::NoteId note_id;
     214           1 :     result = git_helper->get_git()->add_note(note_id, "esysrepo", oss.str(), true);
     215           1 :     if (result.error()) return ESYSREPO_RESULT(result);
     216             : 
     217           1 :     return ESYSREPO_RESULT(ResultCode::OK);
     218           4 : }
     219             : 
     220           0 : Result CmdMulti::do_task_delete()
     221             : {
     222           0 :     std::string folder_path;
     223             : 
     224           0 :     if (!get_folder().empty())
     225           0 :         folder_path = get_folder();
     226           0 :     else if (!get_workspace_path().empty())
     227           0 :         folder_path = get_workspace_path();
     228             :     else
     229           0 :         folder_path = boost::filesystem::current_path().string();
     230             : 
     231           0 :     if (folder_path.empty()) return ESYSREPO_RESULT(ResultCode::INTERNAL_ERROR);
     232             : 
     233           0 :     folder_path = find_git_root_path(folder_path);
     234             : 
     235           0 :     auto git_helper = new_git_helper();
     236           0 :     auto result = git_helper->open(folder_path, log::Level::DEBUG);
     237           0 :     if (result.error()) return ESYSREPO_RESULT(result);
     238             : 
     239           0 :     GitHelper::AutoClose auto_close(git_helper.get());
     240             : 
     241           0 :     git::Commit last_commit;
     242             : 
     243           0 :     git_helper->get_git()->add_notes_ref("esysrepo");
     244           0 :     result = git_helper->get_git()->get_last_commit(last_commit, true);
     245           0 :     if (result.error()) return ESYSREPO_RESULT(result);
     246             : 
     247           0 :     if (last_commit.get_all_notes().empty())
     248             :     {
     249           0 :         info("No notes found to delete.");
     250           0 :         return ESYSREPO_RESULT(ResultCode::OK);
     251             :     }
     252             : 
     253           0 :     auto person = std::make_shared<git::Person>();
     254           0 :     std::string email = "esysrepo@libesys.org";
     255           0 :     std::string name = "ESysRepo";
     256           0 :     person->set_email(email);
     257           0 :     person->set_name(name);
     258           0 :     git_helper->get_git()->set_author(person);
     259             : 
     260           0 :     result = git_helper->get_git()->remove_note_last_commit("esysrepo");
     261           0 :     if (result.error()) return ESYSREPO_RESULT(result);
     262           0 :     return ESYSREPO_RESULT(ResultCode::OK);
     263           0 : }
     264             : 
     265           0 : Result CmdMulti::do_task_push()
     266             : {
     267             :     // First we need to get the working folder
     268           0 :     std::string folder_path;
     269             : 
     270           0 :     if (!get_folder().empty())
     271           0 :         folder_path = get_folder();
     272           0 :     else if (!get_workspace_path().empty())
     273           0 :         folder_path = get_workspace_path();
     274             :     else
     275           0 :         folder_path = boost::filesystem::current_path().string();
     276             : 
     277           0 :     if (folder_path.empty()) return ESYSREPO_RESULT(ResultCode::INTERNAL_ERROR);
     278             : 
     279           0 :     folder_path = find_git_root_path(folder_path);
     280             : 
     281             :     // Then load the workspace
     282           0 :     Workspace workspace;
     283             : 
     284           0 :     Result result = workspace.load(folder_path);
     285           0 :     if (result.error()) return ESYSREPO_RESULT(result);
     286             : 
     287           0 :     set_workspace_path(workspace.get_path());
     288             : 
     289           0 :     auto repo_rel_path = boost::filesystem::relative(folder_path, get_workspace_path());
     290           0 :     auto repo = workspace.get_manifest()->find_repo_by_path(repo_rel_path.generic_string());
     291             : 
     292           0 :     std::string remote_str = repo->get_location_str();
     293           0 :     if (remote_str.empty()) return ESYSREPO_RESULT(ResultCode::MANIFEST_REPOSITORY_WITHOUT_LOCATION);
     294             : 
     295           0 :     auto git_helper = new_git_helper();
     296           0 :     result = git_helper->open(folder_path, log::Level::DEBUG);
     297           0 :     if (result.error()) return ESYSREPO_RESULT(result);
     298             : 
     299           0 :     GitHelper::AutoClose auto_close(git_helper.get());
     300             : 
     301           0 :     auto person = std::make_shared<git::Person>();
     302           0 :     std::string email = "esysrepo@libesys.org";
     303           0 :     std::string name = "ESysRepo";
     304           0 :     person->set_email(email);
     305           0 :     person->set_name(name);
     306           0 :     git_helper->get_git()->set_author(person);
     307             : 
     308           0 :     result = git_helper->get_git()->push_notes(remote_str, "esysrepo");
     309           0 :     if (result.error()) return ESYSREPO_RESULT(result);
     310             : 
     311           0 :     return ResultCode::NOT_IMPLEMENTED;
     312           0 : }
     313             : 
     314           0 : Result CmdMulti::do_task_show()
     315             : {
     316           0 :     std::string folder_path;
     317             : 
     318           0 :     if (!get_folder().empty())
     319           0 :         folder_path = get_folder();
     320           0 :     else if (!get_workspace_path().empty())
     321           0 :         folder_path = get_workspace_path();
     322             :     else
     323           0 :         folder_path = boost::filesystem::current_path().string();
     324             : 
     325           0 :     if (folder_path.empty()) return ESYSREPO_RESULT(ResultCode::INTERNAL_ERROR);
     326             : 
     327           0 :     folder_path = find_git_root_path(folder_path);
     328             : 
     329           0 :     auto git_helper = new_git_helper();
     330             : 
     331           0 :     auto result = git_helper->open(folder_path, log::Level::DEBUG);
     332           0 :     if (result.error()) return ESYSREPO_RESULT(result);
     333             : 
     334           0 :     GitHelper::AutoClose auto_close(git_helper.get());
     335             : 
     336           0 :     int result_int = git_helper->get_git()->add_notes_ref("esysrepo");
     337           0 :     if (result_int < 0) return ESYSREPO_RESULT(ResultCode::GIT_RAW_INT_ERROR, result_int);
     338             : 
     339           0 :     result = git_helper->get_git()->fetch_all_notes();
     340           0 :     if (result.error()) return ESYSREPO_RESULT(result);
     341             : 
     342           0 :     git::Commit last_commit;
     343             : 
     344           0 :     result = git_helper->get_git()->get_last_commit(last_commit, true);
     345           0 :     if (result.error()) return ESYSREPO_RESULT(result);
     346             : 
     347           0 :     std::vector<std::shared_ptr<git::Note>> notes;
     348           0 :     result_int = last_commit.get_notes("esysrepo", notes);
     349           0 :     if (result_int < 0) return ESYSREPO_RESULT(ResultCode::GIT_RAW_INT_ERROR, result_int);
     350             : 
     351           0 :     std::ostringstream oss;
     352             : 
     353           0 :     oss << "Found " << notes.size() << " esysrepo notes.";
     354             : 
     355           0 :     info(oss.str());
     356             : 
     357           0 :     if (notes.empty()) return ESYSREPO_RESULT(ResultCode::OK);
     358             : 
     359           0 :     for (auto idx = 0; idx < notes.size(); ++idx)
     360             :     {
     361           0 :         oss.str("");
     362             : 
     363           0 :         oss << "[" << idx << "] :";
     364             : 
     365           0 :         result = print_esysrepo_note(oss, notes[idx]->get_message());
     366           0 :         if (result.error()) ESYSREPO_RESULT(result);
     367             : 
     368           0 :         info(oss.str());
     369             :     }
     370             : 
     371           0 :     return ESYSREPO_RESULT(ResultCode::OK);
     372           0 : }
     373             : 
     374             : } // namespace esys::repo::exe

Generated by: LCOV version 1.14